diff --git a/dist/js/entity/in_memory.d.ts b/dist/js/entity/in_memory.d.ts index 8c7ef904..44075a26 100644 --- a/dist/js/entity/in_memory.d.ts +++ b/dist/js/entity/in_memory.d.ts @@ -1,4 +1,3 @@ -import { AnyObject } from "@mat3ra/esse/dist/js/esse/types"; import { JSONSchema } from "@mat3ra/esse/dist/js/esse/utils"; import { BaseInMemoryEntitySchema, EntityReferenceSchema } from "@mat3ra/esse/dist/js/types"; export declare enum ValidationErrorCode { @@ -8,7 +7,7 @@ export declare enum ValidationErrorCode { } interface ErrorDetails { error?: object | null; - json: AnyObject; + json: object; schema: JSONSchema; } export declare class EntityError extends Error { @@ -19,48 +18,52 @@ export declare class EntityError extends Error { details?: ErrorDetails; }); } -export declare class InMemoryEntity implements BaseInMemoryEntitySchema { - static create(config: object): InMemoryEntity; +type Schema = BaseInMemoryEntitySchema; +export declare class InMemoryEntity implements Schema { + static create>(config: Schema): T; static _isDeepCloneRequired: boolean; static allowJsonSchemaTypesCoercing: boolean; static readonly jsonSchema?: JSONSchema; - _json: AnyObject; - constructor(config?: object | InMemoryEntity); - prop(name: string, defaultValue: T): T; - prop(name: string): T | undefined; + _json: S; + constructor(config: NoInfer); + prop(name: K, defaultValue: S[K]): S[K]; + prop(name: K): S[K] | undefined; /** * @summary Return a required prop, throwing an error if it doesn't exist or is undefined/null */ - requiredProp(name: string): T; + requiredProp(name: K): S[K]; /** * @summary Set a prop */ - setProp(name: string, value: unknown): void; + setProp(name: keyof S, value: S[typeof name]): void; /** * @summary Remove a prop */ - unsetProp(name: string): void; + unsetProp(name: keyof S): void; /** * Updates internal JSON. Works the same as Mongo's $set operator * @see https://www.mongodb.com/docs/manual/reference/operator/update/set/#-set */ - setProps(json?: AnyObject): this; + setProps(json?: Partial): this; /** - * @summary Array of fields to exclude from resulted JSON + * @summary Array of fields to exclude from resulted JSON. + * JSON.stringify calls `toJSON(key)` with the parent property name (a string). + * Only an actual array is treated as an omit list — otherwise stringify would + * strip fields whose names match the property key (e.g. systemTeams.owner). */ - toJSON(exclude?: string[]): AnyObject; - toJSONSafe(exclude?: string[]): AnyObject; - toJSONQuick(exclude?: string[]): AnyObject; + toJSON(exclude?: (keyof S)[]): S; + toJSONSafe(exclude?: (keyof S)[]): S; + toJSONQuick(exclude?: (keyof S)[]): S; /** * @summary Clone this entity */ clone(extraContext?: object): this; - static validateData(data: AnyObject, clean?: boolean, jsonSchema?: import("json-schema").JSONSchema7 | undefined): AnyObject; + static validateData(data: object, clean?: boolean, jsonSchema?: import("json-schema").JSONSchema7 | undefined): object; /** * @summary Validate entity contents against schema */ validate(): void; - clean(config: AnyObject): AnyObject; + clean(config: S): S; isValid(): boolean; static get cls(): string; get cls(): string; @@ -71,26 +74,21 @@ export declare class InMemoryEntity implements BaseInMemoryEntitySchema { * @returns identifying data */ getAsEntityReference(byIdOnly: true): { + _id: NonNullable; + }; + getAsEntityReference(byIdOnly?: false): EntityReferenceSchema & { _id: string; + cls: string; }; - getAsEntityReference(byIdOnly?: false): Required; - /** - * @summary Pluck an entity from a collection by name. - * If no name is provided and no entity has prop isDefault, return the first entity - * @param entities the entities - * @param entity the kind of entities - * @param name the name of the entity to choose - */ - getEntityByName(entities: InMemoryEntity[], entity: string, name: string): InMemoryEntity; - get id(): string; - set id(id: string); - get _id(): string; - set _id(id: string); - get schemaVersion(): string; - set schemaVersion(schemaVersion: string); - get systemName(): string; - set systemName(systemName: string); - get slug(): string; + get id(): S["_id"]; + set id(id: S["_id"]); + get _id(): S["_id"]; + set _id(id: S["_id"]); + get schemaVersion(): S["schemaVersion"]; + set schemaVersion(schemaVersion: S["schemaVersion"]); + get systemName(): S["systemName"]; + set systemName(systemName: S["systemName"]); + get slug(): S["slug"] | undefined; get isSystemEntity(): boolean; } export type InMemoryEntityConstructor = new (...args: any[]) => T; diff --git a/dist/js/entity/in_memory.js b/dist/js/entity/in_memory.js index c3540198..ea92daaa 100644 --- a/dist/js/entity/in_memory.js +++ b/dist/js/entity/in_memory.js @@ -60,23 +60,18 @@ class InMemoryEntity { static create(config) { return new this.prototype.constructor(config); } - constructor(config = {}) { - this._json = {}; - if (config instanceof InMemoryEntity) { - this._json = config.toJSON(); - } - else { - this._json = this.constructor._isDeepCloneRequired - ? (0, clone_1.deepClone)(config) - : (0, clone_1.clone)(config); - } + // NoInfer: keep default S (or an explicit type arg) instead of inferring S from the config literal. + constructor(config) { + this._json = this.constructor._isDeepCloneRequired + ? (0, clone_1.deepClone)(config) + : (0, clone_1.clone)(config); } /** * @summary Return a prop or the default */ prop(name, defaultValue) { var _a; - // `lodash.get` gets `null` when the value is `null`, but we still want a default value in this case, hence `||` + // `lodash.get` gets `null` when the value is `null`, but we still want a default value in this case, hence `??` return (_a = (0, get_1.default)(this._json, name, defaultValue)) !== null && _a !== void 0 ? _a : defaultValue; } /** @@ -114,22 +109,31 @@ class InMemoryEntity { * @see https://www.mongodb.com/docs/manual/reference/operator/update/set/#-set */ setProps(json = {}) { - Object.entries(json).forEach(([key, value]) => this.setProp(key, value)); + Object.entries(json).forEach(([key, value]) => { + const keyType = key; + this.setProp(keyType, value); + }); return this; } /** - * @summary Array of fields to exclude from resulted JSON + * @summary Array of fields to exclude from resulted JSON. + * JSON.stringify calls `toJSON(key)` with the parent property name (a string). + * Only an actual array is treated as an omit list — otherwise stringify would + * strip fields whose names match the property key (e.g. systemTeams.owner). */ toJSON(exclude = []) { + const omitKeys = Array.isArray(exclude) ? exclude : []; return this.constructor._isDeepCloneRequired - ? this.toJSONSafe(exclude) - : this.toJSONQuick(exclude); + ? this.toJSONSafe(omitKeys) + : this.toJSONQuick(omitKeys); } toJSONSafe(exclude = []) { - return this.clean((0, clone_1.deepClone)((0, omit_1.default)(this._json, exclude))); + const omitKeys = Array.isArray(exclude) ? exclude : []; + return this.clean((0, clone_1.deepClone)((0, omit_1.default)(this._json, omitKeys))); } toJSONQuick(exclude = []) { - return this.clean((0, clone_1.clone)((0, omit_1.default)(this._json, exclude))); + const omitKeys = Array.isArray(exclude) ? exclude : []; + return this.clean((0, clone_1.clone)((0, omit_1.default)(this._json, omitKeys))); } /** * @summary Clone this entity @@ -206,7 +210,9 @@ class InMemoryEntity { return this.constructor.name; } getAsEntityReference(byIdOnly = false) { - if (!this.id) { + // Slug is usually present on entity references, but not required for all entities + // (e.g. workflows are not slugified). Only `_id` is required to form a reference. + if (!this._id) { throw new EntityError({ code: ValidationErrorCode.ENTITY_REFERENCE_ERROR, details: { @@ -216,64 +222,41 @@ class InMemoryEntity { }); } if (byIdOnly) { - return { _id: this.id }; + return { _id: this._id }; } return { - _id: this.id, - slug: this.slug, + _id: this._id, + ...(this.slug !== undefined ? { slug: this.slug } : {}), cls: this.getClsName(), }; } - /** - * @summary Pluck an entity from a collection by name. - * If no name is provided and no entity has prop isDefault, return the first entity - * @param entities the entities - * @param entity the kind of entities - * @param name the name of the entity to choose - */ - // eslint-disable-next-line class-methods-use-this - getEntityByName(entities, entity, name) { - let filtered; - if (!name) { - filtered = entities.filter((ent) => ent.prop("isDefault") === true); - if (!filtered.length) - filtered = [entities[0]]; - } - else { - filtered = entities.filter((ent) => ent.prop("name") === name); - } - if (filtered.length !== 1) { - console.log(`found ${filtered.length} entity ${entity} with name ${name} expected 1`); - } - return filtered[0]; - } // Properties from BaseInMemoryEntitySchema get id() { - return this.prop("_id", ""); + return this.prop("_id"); } set id(id) { this.setProp("_id", id); } get _id() { - return this.prop("_id", ""); + return this.prop("_id"); } set _id(id) { this.setProp("_id", id); } get schemaVersion() { - return this.prop("schemaVersion", ""); + return this.prop("schemaVersion"); } set schemaVersion(schemaVersion) { this.setProp("schemaVersion", schemaVersion); } get systemName() { - return this.prop("systemName", ""); + return this.prop("systemName"); } set systemName(systemName) { this.setProp("systemName", systemName); } get slug() { - return this.prop("slug", ""); + return this.prop("slug"); } get isSystemEntity() { return Boolean(this.systemName); diff --git a/dist/js/entity/index.d.ts b/dist/js/entity/index.d.ts index 0ae8658d..fee5d4c3 100644 --- a/dist/js/entity/index.d.ts +++ b/dist/js/entity/index.d.ts @@ -1,8 +1,7 @@ import { InMemoryEntity } from "./in_memory"; import { DefaultableInMemoryEntity, HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity, HasMetadataNamedDefaultableInMemoryEntity, NamedDefaultableInMemoryEntity, NamedInMemoryEntity } from "./other"; import { ENTITY_SET_TYPES } from "./set/enums"; -import { constructEntitySetFactoryByConfig } from "./set/factory"; import { InMemoryEntitySetMixin } from "./set/mixins"; import { orderedEntityInSetMixin, orderedEntitySetMixin } from "./set/ordered/mixins"; import * as selectorsForEntitySet from "./set/selectors"; -export { InMemoryEntity, NamedInMemoryEntity, DefaultableInMemoryEntity, NamedDefaultableInMemoryEntity, HasMetadataNamedDefaultableInMemoryEntity, HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity, ENTITY_SET_TYPES, constructEntitySetFactoryByConfig, selectorsForEntitySet, InMemoryEntitySetMixin, orderedEntitySetMixin, orderedEntityInSetMixin, }; +export { InMemoryEntity, NamedInMemoryEntity, DefaultableInMemoryEntity, NamedDefaultableInMemoryEntity, HasMetadataNamedDefaultableInMemoryEntity, HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity, ENTITY_SET_TYPES, selectorsForEntitySet, InMemoryEntitySetMixin, orderedEntitySetMixin, orderedEntityInSetMixin, }; diff --git a/dist/js/entity/index.js b/dist/js/entity/index.js index 6f2f0045..1c56aaef 100644 --- a/dist/js/entity/index.js +++ b/dist/js/entity/index.js @@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -exports.orderedEntityInSetMixin = exports.orderedEntitySetMixin = exports.InMemoryEntitySetMixin = exports.selectorsForEntitySet = exports.constructEntitySetFactoryByConfig = exports.ENTITY_SET_TYPES = exports.HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity = exports.HasMetadataNamedDefaultableInMemoryEntity = exports.NamedDefaultableInMemoryEntity = exports.DefaultableInMemoryEntity = exports.NamedInMemoryEntity = exports.InMemoryEntity = void 0; +exports.orderedEntityInSetMixin = exports.orderedEntitySetMixin = exports.InMemoryEntitySetMixin = exports.selectorsForEntitySet = exports.ENTITY_SET_TYPES = exports.HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity = exports.HasMetadataNamedDefaultableInMemoryEntity = exports.NamedDefaultableInMemoryEntity = exports.DefaultableInMemoryEntity = exports.NamedInMemoryEntity = exports.InMemoryEntity = void 0; const in_memory_1 = require("./in_memory"); Object.defineProperty(exports, "InMemoryEntity", { enumerable: true, get: function () { return in_memory_1.InMemoryEntity; } }); const other_1 = require("./other"); @@ -44,8 +44,6 @@ Object.defineProperty(exports, "NamedDefaultableInMemoryEntity", { enumerable: t Object.defineProperty(exports, "NamedInMemoryEntity", { enumerable: true, get: function () { return other_1.NamedInMemoryEntity; } }); const enums_1 = require("./set/enums"); Object.defineProperty(exports, "ENTITY_SET_TYPES", { enumerable: true, get: function () { return enums_1.ENTITY_SET_TYPES; } }); -const factory_1 = require("./set/factory"); -Object.defineProperty(exports, "constructEntitySetFactoryByConfig", { enumerable: true, get: function () { return factory_1.constructEntitySetFactoryByConfig; } }); const mixins_1 = require("./set/mixins"); Object.defineProperty(exports, "InMemoryEntitySetMixin", { enumerable: true, get: function () { return mixins_1.InMemoryEntitySetMixin; } }); const mixins_2 = require("./set/ordered/mixins"); diff --git a/dist/js/entity/mixins/BankableEntityMixin.d.ts b/dist/js/entity/mixins/BankableEntityMixin.d.ts new file mode 100644 index 00000000..97b48825 --- /dev/null +++ b/dist/js/entity/mixins/BankableEntityMixin.d.ts @@ -0,0 +1,7 @@ +import { type BankableSchemaMixin } from "../../generated/BankableSchemaMixin"; +import type { Constructor } from "../../utils/types"; +import { InMemoryEntity } from "../in_memory"; +import { type HashedEntity } from "./HashedEntityMixin"; +export type BankableEntity = BankableSchemaMixin & HashedEntity; +export type BankableInMemoryEntityConstructor = Constructor; +export declare function bankableEntityMixin(item: T): asserts item is T & BankableEntity; diff --git a/dist/js/entity/mixins/BankableEntityMixin.js b/dist/js/entity/mixins/BankableEntityMixin.js new file mode 100644 index 00000000..1e31441b --- /dev/null +++ b/dist/js/entity/mixins/BankableEntityMixin.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.bankableEntityMixin = bankableEntityMixin; +const BankableSchemaMixin_1 = require("../../generated/BankableSchemaMixin"); +const HashedEntityMixin_1 = require("./HashedEntityMixin"); +function bankableEntityMixin(item) { + (0, BankableSchemaMixin_1.bankableSchemaMixin)(item); + (0, HashedEntityMixin_1.hashedEntityMixin)(item); +} diff --git a/dist/js/entity/mixins/HasMetadataMixin.d.ts b/dist/js/entity/mixins/HasMetadataMixin.d.ts index 49273f34..e439a82d 100644 --- a/dist/js/entity/mixins/HasMetadataMixin.d.ts +++ b/dist/js/entity/mixins/HasMetadataMixin.d.ts @@ -2,9 +2,11 @@ import type { MetadataSchema } from "@mat3ra/esse/dist/js/types"; import type { Constructor } from "../../utils/types"; import { InMemoryEntity } from "../in_memory"; type Metadata = MetadataSchema["metadata"]; -export type HasMetadata = { - metadata?: T; - updateMetadata: (object: Partial) => void; +type HasMetadataSchema = { + metadata?: M; +}; +export type HasMetadata = HasMetadataSchema & { + updateMetadata: (object: M) => void; }; export type HasMetadataInMemoryEntityConstructor = Constructor>; export declare function hasMetadataMixin(item: T): asserts item is T & HasMetadata; diff --git a/dist/js/entity/mixins/HasScopeTrackMixin.d.ts b/dist/js/entity/mixins/HasScopeTrackMixin.d.ts index 8e9df8fa..b03d276f 100644 --- a/dist/js/entity/mixins/HasScopeTrackMixin.d.ts +++ b/dist/js/entity/mixins/HasScopeTrackMixin.d.ts @@ -1,8 +1,11 @@ import { InMemoryEntity } from "../in_memory"; +type ScopeTrackSchema = { + scopeTrack?: unknown[]; +}; type ScopeTrackDescriptor = { get scopeTrack(): unknown[]; set scopeTrack(array: unknown[]); }; -export declare function hasScopeTrackMixin(item: InMemoryEntity): InMemoryEntity & ScopeTrackDescriptor; +export declare function hasScopeTrackMixin(item: InMemoryEntity): InMemoryEntity & ScopeTrackDescriptor; export type HasScopeTrackInMemoryEntity = ReturnType; export {}; diff --git a/dist/js/entity/mixins/HasScopeTrackMixin.js b/dist/js/entity/mixins/HasScopeTrackMixin.js index 11b85ab2..35bd2f2a 100644 --- a/dist/js/entity/mixins/HasScopeTrackMixin.js +++ b/dist/js/entity/mixins/HasScopeTrackMixin.js @@ -5,7 +5,8 @@ function schemaMixin(item) { // @ts-expect-error const properties = { get scopeTrack() { - return this.prop("scopeTrack", []); + var _a; + return (_a = this.prop("scopeTrack")) !== null && _a !== void 0 ? _a : []; }, set scopeTrack(array) { this.setProp("scopeTrack", array); diff --git a/dist/js/entity/mixins/HashedEntityMixin.d.ts b/dist/js/entity/mixins/HashedEntityMixin.d.ts index 178b857b..253c0d82 100644 --- a/dist/js/entity/mixins/HashedEntityMixin.d.ts +++ b/dist/js/entity/mixins/HashedEntityMixin.d.ts @@ -1,6 +1,7 @@ +import { type HashedSchemaMixin } from "../../generated/HashedSchemaMixin"; import type { Constructor } from "../../utils/types"; import { InMemoryEntity } from "../in_memory"; -export type HashedEntity = { +export type HashedEntity = HashedSchemaMixin & { calculateHash(): string; getHashObject?(): object; }; diff --git a/dist/js/entity/mixins/HashedEntityMixin.js b/dist/js/entity/mixins/HashedEntityMixin.js index d365abc1..c4debfbc 100644 --- a/dist/js/entity/mixins/HashedEntityMixin.js +++ b/dist/js/entity/mixins/HashedEntityMixin.js @@ -1,8 +1,10 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.hashedEntityMixin = hashedEntityMixin; +const HashedSchemaMixin_1 = require("../../generated/HashedSchemaMixin"); const hash_1 = require("../../utils/hash"); function hashedEntityMixin(item) { + (0, HashedSchemaMixin_1.hashedSchemaMixin)(item); // @ts-expect-error const properties = { /** diff --git a/dist/js/entity/mixins/ImportantSettingsProviderMixin.d.ts b/dist/js/entity/mixins/ImportantSettingsProviderMixin.d.ts deleted file mode 100644 index 9cb5ce49..00000000 --- a/dist/js/entity/mixins/ImportantSettingsProviderMixin.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Constructor } from "../../utils/types"; -import type { InMemoryEntity } from "../in_memory"; -export interface ContextProvider { - domain?: string; -} -export type ImportantSettingsProvider = { - important: object; - setImportant(key: string, value: unknown): void; - importantSettingsProviders: ContextProvider[]; - isImportantEdited: boolean | undefined; -}; -type AbstractBase = { - contextProviders: ContextProvider[]; -}; -export type ImportantSettingsProviderInMemoryEntityConstructor = Constructor; -export declare function importantSettingsProviderMixin(item: T): asserts item is T & ImportantSettingsProvider; -export {}; diff --git a/dist/js/entity/mixins/ImportantSettingsProviderMixin.js b/dist/js/entity/mixins/ImportantSettingsProviderMixin.js deleted file mode 100644 index b4477d54..00000000 --- a/dist/js/entity/mixins/ImportantSettingsProviderMixin.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.importantSettingsProviderMixin = importantSettingsProviderMixin; -const clone_1 = require("../../utils/clone"); -function importantSettingsProviderMixin(item) { - // @ts-expect-error - const properties = { - get important() { - return (0, clone_1.deepClone)(this._json.important || {}); - }, - setImportant(key, value) { - this.setProp("important", { [key]: value }); - }, - get importantSettingsProviders() { - return this.contextProviders.filter((p) => p.domain === "important"); - }, - get isImportantEdited() { - return this.prop("important.isEdited"); - }, - set isImportantEdited(bool) { - this.setProp("important", Object.assign(this.important, { isEdited: bool })); - }, - }; - Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); -} diff --git a/dist/js/entity/mixins/ValidatableMixin.d.ts b/dist/js/entity/mixins/ValidatableMixin.d.ts new file mode 100644 index 00000000..db9792a0 --- /dev/null +++ b/dist/js/entity/mixins/ValidatableMixin.d.ts @@ -0,0 +1,30 @@ +import { JSONSchema } from "@mat3ra/esse/dist/js/esse/utils"; +import type { AbstractConstructor, Constructor } from "../../utils/types"; +import { InMemoryEntity } from "../in_memory"; +export type Validatable = { + validate(): void; + isValid(): boolean; + clean(config: T): T; +}; +export type ValidatableInMemoryStaticEntity = { + validateData(data: object, clean?: boolean, jsonSchema?: JSONSchema): object; + allowJsonSchemaTypesCoercing: boolean; + readonly jsonSchema?: JSONSchema; +}; +export type ValidatableInMemoryEntityConstructor = Constructor & ValidatableInMemoryStaticEntity; +/** + * Opt-in AJV validation for InMemoryEntity subclasses. + * + * Apply on the constructor (adds static + prototype members): + * + * ```ts + * interface MyEntity extends Validatable {} + * class MyEntity extends InMemoryEntity { + * static readonly jsonSchema = mySchema; + * } + * validatableEntityMixin(MyEntity); + * ``` + * + * Idempotent: safe to call more than once on the same class. + */ +export declare function validatableEntityMixin(Item: AbstractConstructor): void; diff --git a/dist/js/entity/mixins/ValidatableMixin.js b/dist/js/entity/mixins/ValidatableMixin.js new file mode 100644 index 00000000..9fabe9f4 --- /dev/null +++ b/dist/js/entity/mixins/ValidatableMixin.js @@ -0,0 +1,93 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validatableEntityMixin = validatableEntityMixin; +const omit_1 = __importDefault(require("lodash/omit")); +const clone_1 = require("../../utils/clone"); +const in_memory_1 = require("../in_memory"); +const validateEntityData_1 = require("../validateEntityData"); +const validatableEntityMixinApplied = Symbol("validatableEntityMixinApplied"); +function validatableEntityStaticMixin(Item) { + // @ts-expect-error — assigning mixin statics onto constructor + const staticProperties = { + allowJsonSchemaTypesCoercing: false, + validateData(data, clean, jsonSchema) { + return (0, validateEntityData_1.validateEntityData)(data, jsonSchema !== null && jsonSchema !== void 0 ? jsonSchema : this.jsonSchema, { + clean: clean !== null && clean !== void 0 ? clean : false, + coerceTypes: this.allowJsonSchemaTypesCoercing, + }); + }, + }; + Object.defineProperties(Item, Object.getOwnPropertyDescriptors(staticProperties)); +} +function validatableEntityInstanceMixin(item) { + // @ts-expect-error — assigning mixin instance methods onto prototype + const properties = { + validate() { + const ctr = this.constructor; + if (this._json) { + ctr.validateData(this._json); + } + }, + isValid() { + try { + this.validate(); + return true; + } + catch (_a) { + return false; + } + }, + clean(config) { + var _a, _b, _c; + try { + const ctr = this.constructor; + return ctr.validateData(config, true); + } + catch (err) { + if (err instanceof in_memory_1.EntityError) { + console.error({ + error: JSON.stringify((_a = err.details) === null || _a === void 0 ? void 0 : _a.error), + json: JSON.stringify((_b = err.details) === null || _b === void 0 ? void 0 : _b.json), + schema: JSON.stringify((_c = err.details) === null || _c === void 0 ? void 0 : _c.schema), + }); + } + throw err; + } + }, + toJSON(exclude = []) { + const ctr = this.constructor; + const json = ctr._isDeepCloneRequired + ? (0, clone_1.deepClone)((0, omit_1.default)(this._json, exclude)) + : (0, clone_1.clone)((0, omit_1.default)(this._json, exclude)); + return this.clean(json); + }, + }; + Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); +} +/** + * Opt-in AJV validation for InMemoryEntity subclasses. + * + * Apply on the constructor (adds static + prototype members): + * + * ```ts + * interface MyEntity extends Validatable {} + * class MyEntity extends InMemoryEntity { + * static readonly jsonSchema = mySchema; + * } + * validatableEntityMixin(MyEntity); + * ``` + * + * Idempotent: safe to call more than once on the same class. + */ +function validatableEntityMixin(Item) { + const ctor = Item; + if (ctor[validatableEntityMixinApplied]) { + return; + } + validatableEntityStaticMixin(Item); + validatableEntityInstanceMixin(Item.prototype); + ctor[validatableEntityMixinApplied] = true; +} diff --git a/dist/js/entity/set/InMemoryEntitySetBaseMixin.d.ts b/dist/js/entity/set/InMemoryEntitySetBaseMixin.d.ts index 0b937881..ff1cac00 100644 --- a/dist/js/entity/set/InMemoryEntitySetBaseMixin.d.ts +++ b/dist/js/entity/set/InMemoryEntitySetBaseMixin.d.ts @@ -1,19 +1,21 @@ -import { type EntitySetSchema, SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; +import { type BaseInMemoryEntitySchema, type EntitySetSchema, SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; import { type InMemoryEntity } from "../in_memory"; export type SystemInSet = Required; export type InSet = SystemInSet["inSet"][0]; +type EntitySetEntitySchema = BaseInMemoryEntitySchema & EntitySetSchema; export declare enum EntitySetType { ordered = "ordered", unordered = "unordered" } -declare function schemaMixin(item: E): InMemoryEntity & EntitySetSchema; type EntitySetBaseMethodsDescriptor = { toJSONForInclusionInEntity(): { _id: string; type: string; }; }; -declare function methodsMixin(item: E & EntitySetSchema): InMemoryEntity & EntitySetSchema & EntitySetBaseMethodsDescriptor; -export type InMemoryEntitySetBase = ReturnType & ReturnType; -export declare function inMemoryEntitySetBaseMixin(item: T): asserts item is T & InMemoryEntitySetBase; +/** Mixin shape only — safe to merge into schema-generic subclasses (no baked-in `_json`). */ +export type InMemoryEntitySetBaseMixin = EntitySetSchema & EntitySetBaseMethodsDescriptor; +/** Instance type with default entity-set schema (includes `InMemoryEntity<_json>`). */ +export type InMemoryEntitySetBase = InMemoryEntity & InMemoryEntitySetBaseMixin; +export declare function inMemoryEntitySetBaseMixin(item: T): asserts item is T & InMemoryEntitySetBaseMixin; export {}; diff --git a/dist/js/entity/set/InMemoryEntitySetMixin.d.ts b/dist/js/entity/set/InMemoryEntitySetMixin.d.ts index 4d16c78f..ca94a280 100644 --- a/dist/js/entity/set/InMemoryEntitySetMixin.d.ts +++ b/dist/js/entity/set/InMemoryEntitySetMixin.d.ts @@ -5,4 +5,4 @@ export type InSet = SystemInSet["inSet"][0]; export type InMemoryEntitySet = { containsEntity(entity?: SystemInSetSchema): boolean; }; -export declare function inMemoryEntitySetMixin(item: T): InMemoryEntity & InMemoryEntitySet; +export declare function inMemoryEntitySetMixin(item: T): InMemoryEntity & InMemoryEntitySet; diff --git a/dist/js/entity/set/factory.d.ts b/dist/js/entity/set/factory.d.ts deleted file mode 100644 index 400063c7..00000000 --- a/dist/js/entity/set/factory.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { AnyObject } from "@mat3ra/esse/dist/js/esse/types"; -import { EntitySetSchema } from "@mat3ra/esse/dist/js/types"; -import { InMemoryEntity } from "../in_memory"; -export declare const constructEntitySetFactoryByConfig: ({ entitySetCls }: { - entitySetCls?: typeof InMemoryEntity | undefined; -}) => (config: AnyObject, entityCls: EntitySetSchema["entityCls"]) => InMemoryEntity; diff --git a/dist/js/entity/set/factory.js b/dist/js/entity/set/factory.js deleted file mode 100644 index 7c8c6f25..00000000 --- a/dist/js/entity/set/factory.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.constructEntitySetFactoryByConfig = void 0; -const in_memory_1 = require("../in_memory"); -const constructEntitySetFactoryByConfig = ({ entitySetCls = in_memory_1.InMemoryEntity }) => (config, entityCls) => { - const Cls = entitySetCls; - return new Cls({ - ...config, - entityCls, - }); -}; -exports.constructEntitySetFactoryByConfig = constructEntitySetFactoryByConfig; diff --git a/dist/js/entity/validateEntityData.d.ts b/dist/js/entity/validateEntityData.d.ts new file mode 100644 index 00000000..cb070978 --- /dev/null +++ b/dist/js/entity/validateEntityData.d.ts @@ -0,0 +1,10 @@ +import { JSONSchema } from "@mat3ra/esse/dist/js/esse/utils"; +export type ValidateEntityDataOptions = { + clean?: boolean; + coerceTypes?: boolean; +}; +/** + * Validate (and optionally clean) entity data against a JSON schema. + * Returns `data` unchanged when no schema is provided. + */ +export declare function validateEntityData(data: object, jsonSchema: JSONSchema | undefined, options?: ValidateEntityDataOptions): object; diff --git a/dist/js/entity/validateEntityData.js b/dist/js/entity/validateEntityData.js new file mode 100644 index 00000000..90aeeac0 --- /dev/null +++ b/dist/js/entity/validateEntityData.js @@ -0,0 +1,62 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validateEntityData = validateEntityData; +const ajv = __importStar(require("@mat3ra/esse/dist/js/utils/ajv")); +const in_memory_1 = require("./in_memory"); +/** + * Validate (and optionally clean) entity data against a JSON schema. + * Returns `data` unchanged when no schema is provided. + */ +function validateEntityData(data, jsonSchema, options = {}) { + if (!jsonSchema) { + return data; + } + const { clean = false, coerceTypes = false } = options; + const result = clean + ? ajv.validateAndClean(data, jsonSchema, { coerceTypes }) + : ajv.validate(data, jsonSchema); + if (!result.isValid) { + throw new in_memory_1.EntityError({ + code: in_memory_1.ValidationErrorCode.IN_MEMORY_ENTITY_DATA_INVALID, + details: { + error: result === null || result === void 0 ? void 0 : result.errors, + json: data, + schema: jsonSchema, + }, + }); + } + return data; +} diff --git a/dist/js/generateSchemaMixin.js b/dist/js/generateSchemaMixin.js index 44a775ce..e6d168f5 100644 --- a/dist/js/generateSchemaMixin.js +++ b/dist/js/generateSchemaMixin.js @@ -82,7 +82,7 @@ function generateMixinFunction(schema, schemaName, mixinTypeName, entityTypeName // Filter out skip fields const propertyEntries = Object.entries(properties).filter(([propertyName]) => !skipFields.includes(propertyName)); let code = `import type { InMemoryEntity } from "${entityFrom}";\n`; - code += `import type { ${schemaName} } from "${from}";\n\n`; + code += `import type { BaseInMemoryEntitySchema, ${schemaName} } from "${from}";\n\n`; // Generate the mixin type - only use Omit if skipFields has values if (skipFields && skipFields.length > 0) { const skipFieldNames = skipFields.map((field) => `"${field}"`).join(" | "); @@ -91,20 +91,20 @@ function generateMixinFunction(schema, schemaName, mixinTypeName, entityTypeName else { code += `export type ${mixinTypeName} = ${schemaName};\n\n`; } - // Generate the entity type - code += `export type ${entityTypeName} = InMemoryEntity & ${mixinTypeName};\n\n`; + // Entity schema must include base fields so `_json` / `toJSON` stay compatible with InMemoryEntity + code += `export type ${entityTypeName} = InMemoryEntity;\n\n`; code += `export function ${functionName}(\n`; code += ` item: InMemoryEntity,\n`; code += `): asserts item is T & ${mixinTypeName} {\n`; code += ` // @ts-expect-error\n`; - code += ` const properties: InMemoryEntity & ${mixinTypeName} = {\n`; + code += ` const properties: InMemoryEntity<${mixinTypeName}> & ${mixinTypeName} = {\n`; for (let i = 0; i < propertyEntries.length; i++) { const [propertyName] = propertyEntries[i]; const isRequired = isRequiredProperty(propertyName, required); const methodName = isRequired ? "requiredProp" : "prop"; const typeAnnotation = generateTypeAnnotation(propertyName, schemaName); code += `get ${propertyName}() {\n`; - code += `return this.${methodName}<${typeAnnotation}>("${propertyName}");\n`; + code += `return this.${methodName}("${propertyName}");\n`; code += `},\n`; code += `set ${propertyName}(value: ${typeAnnotation}) {\n`; code += `this.setProp("${propertyName}", value);\n`; diff --git a/dist/js/generated/BankableSchemaMixin.d.ts b/dist/js/generated/BankableSchemaMixin.d.ts new file mode 100644 index 00000000..b5177b25 --- /dev/null +++ b/dist/js/generated/BankableSchemaMixin.d.ts @@ -0,0 +1,5 @@ +import type { BankableSchema, BaseInMemoryEntitySchema } from "@mat3ra/esse/dist/js/types"; +import type { InMemoryEntity } from "../entity/in_memory"; +export type BankableSchemaMixin = BankableSchema; +export type BankableInMemoryEntity = InMemoryEntity; +export declare function bankableSchemaMixin(item: InMemoryEntity): asserts item is T & BankableSchemaMixin; diff --git a/dist/js/generated/BankableSchemaMixin.js b/dist/js/generated/BankableSchemaMixin.js new file mode 100644 index 00000000..7350b8ea --- /dev/null +++ b/dist/js/generated/BankableSchemaMixin.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.bankableSchemaMixin = bankableSchemaMixin; +function bankableSchemaMixin(item) { + // @ts-expect-error + const properties = { + get exabyteId() { + return this.requiredProp("exabyteId"); + }, + set exabyteId(value) { + this.setProp("exabyteId", value); + }, + }; + Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); +} diff --git a/dist/js/generated/DefaultableSchemaMixin.d.ts b/dist/js/generated/DefaultableSchemaMixin.d.ts index e254e5c4..bf608636 100644 --- a/dist/js/generated/DefaultableSchemaMixin.d.ts +++ b/dist/js/generated/DefaultableSchemaMixin.d.ts @@ -1,5 +1,5 @@ -import type { DefaultableEntitySchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, DefaultableEntitySchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type DefaultableSchemaMixin = DefaultableEntitySchema; -export type DefaultableInMemoryEntity = InMemoryEntity & DefaultableSchemaMixin; +export type DefaultableInMemoryEntity = InMemoryEntity; export declare function defaultableSchemaMixin(item: InMemoryEntity): asserts item is T & DefaultableSchemaMixin; diff --git a/dist/js/generated/HasConsistencyChecksSchemaMixin.d.ts b/dist/js/generated/HasConsistencyChecksSchemaMixin.d.ts index 9266913b..599f9735 100644 --- a/dist/js/generated/HasConsistencyChecksSchemaMixin.d.ts +++ b/dist/js/generated/HasConsistencyChecksSchemaMixin.d.ts @@ -1,5 +1,5 @@ -import type { HasConsistencyCheckSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, HasConsistencyCheckSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type HasConsistencyChecksSchemaMixin = HasConsistencyCheckSchema; -export type HasConsistencyChecksInMemoryEntity = InMemoryEntity & HasConsistencyChecksSchemaMixin; +export type HasConsistencyChecksInMemoryEntity = InMemoryEntity; export declare function hasConsistencyChecksSchemaMixin(item: InMemoryEntity): asserts item is T & HasConsistencyChecksSchemaMixin; diff --git a/dist/js/generated/HasConsistencyChecksSchemaMixin.js b/dist/js/generated/HasConsistencyChecksSchemaMixin.js index a5f3e02c..5db5cf1e 100644 --- a/dist/js/generated/HasConsistencyChecksSchemaMixin.js +++ b/dist/js/generated/HasConsistencyChecksSchemaMixin.js @@ -5,7 +5,7 @@ function hasConsistencyChecksSchemaMixin(item) { // @ts-expect-error const properties = { get consistencyChecks() { - return this.prop("consistencyChecks", []); + return this.prop("consistencyChecks"); }, set consistencyChecks(value) { this.setProp("consistencyChecks", value); diff --git a/dist/js/generated/HasDescriptionSchemaMixin.d.ts b/dist/js/generated/HasDescriptionSchemaMixin.d.ts index 5fbec77d..bc7b4609 100644 --- a/dist/js/generated/HasDescriptionSchemaMixin.d.ts +++ b/dist/js/generated/HasDescriptionSchemaMixin.d.ts @@ -1,5 +1,5 @@ -import type { DescriptionSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, DescriptionSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type HasDescriptionSchemaMixin = DescriptionSchema; -export type HasDescriptionInMemoryEntity = InMemoryEntity & HasDescriptionSchemaMixin; +export type HasDescriptionInMemoryEntity = InMemoryEntity; export declare function hasDescriptionSchemaMixin(item: InMemoryEntity): asserts item is T & HasDescriptionSchemaMixin; diff --git a/dist/js/generated/HashedSchemaMixin.d.ts b/dist/js/generated/HashedSchemaMixin.d.ts new file mode 100644 index 00000000..355e3e7b --- /dev/null +++ b/dist/js/generated/HashedSchemaMixin.d.ts @@ -0,0 +1,5 @@ +import type { BaseInMemoryEntitySchema, HashedSchema } from "@mat3ra/esse/dist/js/types"; +import type { InMemoryEntity } from "../entity/in_memory"; +export type HashedSchemaMixin = HashedSchema; +export type HashedInMemoryEntity = InMemoryEntity; +export declare function hashedSchemaMixin(item: InMemoryEntity): asserts item is T & HashedSchemaMixin; diff --git a/dist/js/generated/HashedSchemaMixin.js b/dist/js/generated/HashedSchemaMixin.js new file mode 100644 index 00000000..eb97531b --- /dev/null +++ b/dist/js/generated/HashedSchemaMixin.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.hashedSchemaMixin = hashedSchemaMixin; +function hashedSchemaMixin(item) { + // @ts-expect-error + const properties = { + get hash() { + return this.requiredProp("hash"); + }, + set hash(value) { + this.setProp("hash", value); + }, + }; + Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); +} diff --git a/dist/js/generated/InSetSchemaMixin.d.ts b/dist/js/generated/InSetSchemaMixin.d.ts index 21c2022f..52ed1f69 100644 --- a/dist/js/generated/InSetSchemaMixin.d.ts +++ b/dist/js/generated/InSetSchemaMixin.d.ts @@ -1,5 +1,5 @@ -import type { SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type InSetSchemaMixin = SystemInSetSchema; -export type InSetInMemoryEntity = InMemoryEntity & InSetSchemaMixin; +export type InSetInMemoryEntity = InMemoryEntity; export declare function inSetSchemaMixin(item: InMemoryEntity): asserts item is T & InSetSchemaMixin; diff --git a/dist/js/generated/NamedEntitySchemaMixin.d.ts b/dist/js/generated/NamedEntitySchemaMixin.d.ts index 441041c5..b75a5d18 100644 --- a/dist/js/generated/NamedEntitySchemaMixin.d.ts +++ b/dist/js/generated/NamedEntitySchemaMixin.d.ts @@ -1,5 +1,5 @@ -import type { NameEntitySchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, NameEntitySchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type NamedEntitySchemaMixin = NameEntitySchema; -export type NamedEntityInMemoryEntity = InMemoryEntity & NamedEntitySchemaMixin; +export type NamedEntityInMemoryEntity = InMemoryEntity; export declare function namedEntitySchemaMixin(item: InMemoryEntity): asserts item is T & NamedEntitySchemaMixin; diff --git a/dist/js/generated/RuntimeItemsSchemaMixin.d.ts b/dist/js/generated/RuntimeItemsSchemaMixin.d.ts index 7de1b3d1..d56e0901 100644 --- a/dist/js/generated/RuntimeItemsSchemaMixin.d.ts +++ b/dist/js/generated/RuntimeItemsSchemaMixin.d.ts @@ -1,5 +1,5 @@ -import type { RuntimeItemsSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, RuntimeItemsSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type RuntimeItemsSchemaMixin = RuntimeItemsSchema; -export type RuntimeItemsInMemoryEntity = InMemoryEntity & RuntimeItemsSchemaMixin; +export type RuntimeItemsInMemoryEntity = InMemoryEntity; export declare function runtimeItemsSchemaMixin(item: InMemoryEntity): asserts item is T & RuntimeItemsSchemaMixin; diff --git a/dist/js/generated/TaggableSchemaMixin.d.ts b/dist/js/generated/TaggableSchemaMixin.d.ts index a2ced7e6..e1417801 100644 --- a/dist/js/generated/TaggableSchemaMixin.d.ts +++ b/dist/js/generated/TaggableSchemaMixin.d.ts @@ -1,5 +1,5 @@ -import type { EntityTagsSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, EntityTagsSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type TaggableSchemaMixin = EntityTagsSchema; -export type TaggableInMemoryEntity = InMemoryEntity & TaggableSchemaMixin; +export type TaggableInMemoryEntity = InMemoryEntity; export declare function taggableSchemaMixin(item: InMemoryEntity): asserts item is T & TaggableSchemaMixin; diff --git a/dist/js/generated/TaggableSchemaMixin.js b/dist/js/generated/TaggableSchemaMixin.js index fb57c06a..86018a67 100644 --- a/dist/js/generated/TaggableSchemaMixin.js +++ b/dist/js/generated/TaggableSchemaMixin.js @@ -5,7 +5,7 @@ function taggableSchemaMixin(item) { // @ts-expect-error const properties = { get tags() { - return this.prop("tags", []); + return this.prop("tags"); }, set tags(value) { this.setProp("tags", value); diff --git a/dist/js/index.d.ts b/dist/js/index.d.ts index d52bc1d8..4eaa4bef 100644 --- a/dist/js/index.d.ts +++ b/dist/js/index.d.ts @@ -2,10 +2,12 @@ import { ArrayWithIds, RoundedArrayWithIds } from "./ArrayWithIds"; import * as context from "./context"; import * as entity from "./entity"; import * as utils from "./utils"; +import * as validator from "./validator"; import { RoundedValueWithId, ValueWithId } from "./ValueWithId"; import { RoundedVector3D, Vector3D } from "./vector"; export { ArrayWithIds, ValueWithId, RoundedValueWithId, RoundedArrayWithIds, RoundedVector3D, Vector3D, }; -export { entity, context, utils }; +export { entity, context, utils, validator }; +export { JsonSchemaValidator, jsonSchemaValidator } from "./validator"; declare const Code: { ArrayWithIds: typeof ArrayWithIds; ValueWithId: typeof ValueWithId; @@ -16,6 +18,7 @@ declare const Code: { entity: typeof entity; context: typeof context; utils: typeof utils; + validator: typeof validator; }; export type CodeType = typeof Code; export default Code; diff --git a/dist/js/index.js b/dist/js/index.js index 846b45f8..6fbe16c2 100644 --- a/dist/js/index.js +++ b/dist/js/index.js @@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -exports.utils = exports.context = exports.entity = exports.Vector3D = exports.RoundedVector3D = exports.RoundedArrayWithIds = exports.RoundedValueWithId = exports.ValueWithId = exports.ArrayWithIds = void 0; +exports.jsonSchemaValidator = exports.JsonSchemaValidator = exports.validator = exports.utils = exports.context = exports.entity = exports.Vector3D = exports.RoundedVector3D = exports.RoundedArrayWithIds = exports.RoundedValueWithId = exports.ValueWithId = exports.ArrayWithIds = void 0; const ArrayWithIds_1 = require("./ArrayWithIds"); Object.defineProperty(exports, "ArrayWithIds", { enumerable: true, get: function () { return ArrayWithIds_1.ArrayWithIds; } }); Object.defineProperty(exports, "RoundedArrayWithIds", { enumerable: true, get: function () { return ArrayWithIds_1.RoundedArrayWithIds; } }); @@ -43,12 +43,17 @@ const entity = __importStar(require("./entity")); exports.entity = entity; const utils = __importStar(require("./utils")); exports.utils = utils; +const validator = __importStar(require("./validator")); +exports.validator = validator; const ValueWithId_1 = require("./ValueWithId"); Object.defineProperty(exports, "RoundedValueWithId", { enumerable: true, get: function () { return ValueWithId_1.RoundedValueWithId; } }); Object.defineProperty(exports, "ValueWithId", { enumerable: true, get: function () { return ValueWithId_1.ValueWithId; } }); const vector_1 = require("./vector"); Object.defineProperty(exports, "RoundedVector3D", { enumerable: true, get: function () { return vector_1.RoundedVector3D; } }); Object.defineProperty(exports, "Vector3D", { enumerable: true, get: function () { return vector_1.Vector3D; } }); +var validator_1 = require("./validator"); +Object.defineProperty(exports, "JsonSchemaValidator", { enumerable: true, get: function () { return validator_1.JsonSchemaValidator; } }); +Object.defineProperty(exports, "jsonSchemaValidator", { enumerable: true, get: function () { return validator_1.jsonSchemaValidator; } }); const Code = { ArrayWithIds: ArrayWithIds_1.ArrayWithIds, ValueWithId: ValueWithId_1.ValueWithId, @@ -59,5 +64,6 @@ const Code = { entity, context, utils, + validator, }; exports.default = Code; diff --git a/dist/js/utils/graph.d.ts b/dist/js/utils/graph.d.ts index 61811463..0c53651d 100644 --- a/dist/js/utils/graph.d.ts +++ b/dist/js/utils/graph.d.ts @@ -1,9 +1,6 @@ +import type { SubworkflowUnitSchema } from "@mat3ra/esse/dist/js/types"; import { InMemoryEntity } from "../entity/in_memory"; -export type UnitEntity = InMemoryEntity & { - head: boolean; - next?: string; - flowchartId: string; -}; +export type UnitEntity = SubworkflowUnitSchema & InMemoryEntity; /** * @summary Set the head of an array of units */ diff --git a/dist/js/validator/JsonSchemaValidator.d.ts b/dist/js/validator/JsonSchemaValidator.d.ts new file mode 100644 index 00000000..5c1bbac8 --- /dev/null +++ b/dist/js/validator/JsonSchemaValidator.d.ts @@ -0,0 +1,127 @@ +import Ajv, { ErrorObject } from "ajv"; +import type { AnyObject, ValidationSchema } from "./types"; +type ErrorResult = { + [key: string]: string | ErrorResult; +}; +export type JsonSchemaValidatorOptions = { + /** + * Delete object properties whose value is `null` before AJV (default: `true`). + * Nested objects are walked; array elements are not removed. + */ + removeNull?: boolean; + /** + * Delete object properties whose value is `""` before AJV (default: `true`). + * Keep `false` when empty strings are intentional placeholders (e.g. flowchart links). + */ + removeEmptyStrings?: boolean; +}; +export declare class JsonSchemaValidator { + private readonly ajv; + private readonly removeNull; + private readonly removeEmptyStrings; + private readonly formatCodeMap; + private readonly keywordsCodeMap; + constructor({ removeNull, removeEmptyStrings }?: JsonSchemaValidatorOptions); + /** + * Register host-specific AJV formats/keywords (e.g. web-app password/phone) on this instance. + */ + registerExtensions(register: (ajvInstance: Ajv) => void): void; + private getCompiledValidator; + /** + * Simplify AJV errors into LIVR-inspired field → code maps for API responses. + * Inspired by https://github.com/koorchik/LIVR + * + * @example of raw AJV errors: + * [ + * { + * "instancePath": "", + * "schemaPath": "#/required", + * "keyword": "required", + * "params": { + * "missingProperty": "intent" + * }, + * "message": "must have required property 'intent'" + * }, + * { + * "instancePath": "/user", + * "schemaPath": "#/properties/user/required", + * "keyword": "required", + * "params": { + * "missingProperty": "username" + * }, + * "message": "must have required property 'username'" + * }, + * { + * "instancePath": "/user", + * "schemaPath": "#/properties/user/required", + * "keyword": "required", + * "params": { + * "missingProperty": "fullname" + * }, + * "message": "must have required property 'fullname'" + * }, + * { + * "instancePath": "/user/termsAccepted", + * "schemaPath": "#/properties/user/properties/termsAccepted/type", + * "keyword": "type", + * "params": { + * "type": "boolean" + * }, + * "message": "must be boolean" + * }, + * { + * "instancePath": "/user/termsAccepted", + * "schemaPath": "#/properties/user/properties/termsAccepted/enum", + * "keyword": "enum", + * "params": { + * "allowedValues": [ + * true + * ] + * }, + * "message": "must be equal to one of the allowed values" + * }, + * { + * "instancePath": "/user/password", + * "schemaPath": "#/properties/user/properties/password/equalToField", + * "keyword": "equalToField", + * "params": {}, + * "message": "should be equal to the value of \"passwordConfirmation\"" + * }, + * { + * "instancePath": "/user/passwordConfirmation", + * "schemaPath": "#/properties/user/properties/passwordConfirmation/equalToField", + * "keyword": "equalToField", + * "params": {}, + * "message": "should be equal to the value of \"password\"" + * } + * ] + * + * @example of the same errors after transformation: + * { + * "intent": "REQUIRED", + * "user": { + * "username": "REQUIRED", + * "fullname": "REQUIRED", + * "termsAccepted": "FORMAT_ERROR", + * "password": "FIELDS_NOT_EQUAL", + * "passwordConfirmation": "FIELDS_NOT_EQUAL" + * } + * } + */ + transformErrors(errors?: ErrorObject[] | null): ErrorResult | undefined; + /** + * Validates and cleans data against the schema. + * Optionally drops null / empty-string properties (constructor options), then AJV removeAdditional. + */ + validateAndClean(data: AnyObject, jsonSchema: ValidationSchema): { + isValid: boolean | Promise; + errors: ErrorResult | undefined; + rawErrors: ErrorObject, unknown>[] | null | undefined; + validated: { + [x: string]: any; + }; + }; +} +/** Shared singleton — hosts register app formats on this instance (null + "" strip on). */ +export declare const jsonSchemaValidator: JsonSchemaValidator; +export {}; diff --git a/dist/js/validator/JsonSchemaValidator.js b/dist/js/validator/JsonSchemaValidator.js new file mode 100644 index 00000000..61826621 --- /dev/null +++ b/dist/js/validator/JsonSchemaValidator.js @@ -0,0 +1,239 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jsonSchemaValidator = exports.JsonSchemaValidator = void 0; +/** + * Standalone AJV validator for cleaning and validating JSON against JSON Schema. + * + * Why this exists (alongside esse `utils/ajv`): + * - Compose schemas with `mergeAllOf` and enforce allowlists via `additionalProperties: false` + * so callers can reject unknown / system-owned fields. + * - Optionally strip null / empty-string properties before type checks (see constructor options). + * - Map raw AJV errors into stable nested field → code objects (LIVR-style) for consumers that + * need machine-readable validation results. + * + * Distinct from `@mat3ra/esse` `utils/ajv.validateAndClean` (entity clean: null-only strip used by + * `InMemoryEntity.toJSON*`). Prefer this class/API for application validation; register host + * formats with {@link JsonSchemaValidator.registerExtensions}. + */ +const removeEmptyAndNullProperties_1 = require("@mat3ra/esse/dist/js/utils/removeEmptyAndNullProperties"); +const ajv_1 = __importDefault(require("ajv")); +const ajv_formats_1 = __importDefault(require("ajv-formats")); +const ajv_keywords_1 = __importDefault(require("ajv-keywords")); +const json_pointer_1 = __importDefault(require("json-pointer")); +const json_schema_merge_allof_1 = __importDefault(require("json-schema-merge-allof")); +const rules_1 = __importDefault(require("./rules")); +/** + * Recursively deletes empty-string object properties (mutates in place). + */ +function removeEmptyStringProperties(obj) { + if (typeof obj !== "object" || obj === null) { + return obj; + } + if (Array.isArray(obj)) { + obj.forEach((item) => removeEmptyStringProperties(item)); + return obj; + } + Object.keys(obj).forEach((key) => { + const value = obj[key]; + if (value === "") { + delete obj[key]; + } + else if (typeof value === "object") { + removeEmptyStringProperties(value); + } + }); + return obj; +} +class JsonSchemaValidator { + constructor({ removeNull = true, removeEmptyStrings = true } = {}) { + this.formatCodeMap = { + email: "WRONG_EMAIL", + "custom-email": "WRONG_EMAIL", + "custom-username": "WRONG_USERNAME", + "custom-phone": "WRONG_PHONE", + "custom-password": "WRONG_PASSWORD", + }; + this.keywordsCodeMap = { + required: "REQUIRED", + type: "FORMAT_ERROR", + equalToField: "FIELDS_NOT_EQUAL", + minLength: "TOO_SHORT", + maxLength: "TOO_LONG", + isoDate: "WRONG_DATE", + }; + this.removeNull = removeNull; + this.removeEmptyStrings = removeEmptyStrings; + this.ajv = new ajv_1.default({ + allErrors: true, + useDefaults: true, + discriminator: true, + removeAdditional: true, + coerceTypes: true, + allowUnionTypes: true, + // Esse / workflow schemas: json-schema-to-typescript-only keywords, and `default` inside + // discriminated `oneOf` branches where Ajv still treats nested rules as composite (see + // assignDefault + compositeRule). strictSchema would fail compilation for those valid schemas. + strictSchema: false, + }); + (0, ajv_formats_1.default)(this.ajv); + (0, ajv_keywords_1.default)(this.ajv); + (0, rules_1.default)(this.ajv); + } + /** + * Register host-specific AJV formats/keywords (e.g. web-app password/phone) on this instance. + */ + registerExtensions(register) { + register(this.ajv); + } + getCompiledValidator(jsonSchema) { + let validate = this.ajv.getSchema(jsonSchema.$id); + if (!validate) { + const schema = (0, json_schema_merge_allof_1.default)(jsonSchema, { ignoreAdditionalProperties: true }); + this.ajv.addSchema(schema, jsonSchema.$id); + validate = this.ajv.getSchema(jsonSchema.$id); + } + if (!validate) { + throw new Error("JSONSchemasInterface AJV validator error"); + } + return validate; + } + /** + * Simplify AJV errors into LIVR-inspired field → code maps for API responses. + * Inspired by https://github.com/koorchik/LIVR + * + * @example of raw AJV errors: + * [ + * { + * "instancePath": "", + * "schemaPath": "#/required", + * "keyword": "required", + * "params": { + * "missingProperty": "intent" + * }, + * "message": "must have required property 'intent'" + * }, + * { + * "instancePath": "/user", + * "schemaPath": "#/properties/user/required", + * "keyword": "required", + * "params": { + * "missingProperty": "username" + * }, + * "message": "must have required property 'username'" + * }, + * { + * "instancePath": "/user", + * "schemaPath": "#/properties/user/required", + * "keyword": "required", + * "params": { + * "missingProperty": "fullname" + * }, + * "message": "must have required property 'fullname'" + * }, + * { + * "instancePath": "/user/termsAccepted", + * "schemaPath": "#/properties/user/properties/termsAccepted/type", + * "keyword": "type", + * "params": { + * "type": "boolean" + * }, + * "message": "must be boolean" + * }, + * { + * "instancePath": "/user/termsAccepted", + * "schemaPath": "#/properties/user/properties/termsAccepted/enum", + * "keyword": "enum", + * "params": { + * "allowedValues": [ + * true + * ] + * }, + * "message": "must be equal to one of the allowed values" + * }, + * { + * "instancePath": "/user/password", + * "schemaPath": "#/properties/user/properties/password/equalToField", + * "keyword": "equalToField", + * "params": {}, + * "message": "should be equal to the value of \"passwordConfirmation\"" + * }, + * { + * "instancePath": "/user/passwordConfirmation", + * "schemaPath": "#/properties/user/properties/passwordConfirmation/equalToField", + * "keyword": "equalToField", + * "params": {}, + * "message": "should be equal to the value of \"password\"" + * } + * ] + * + * @example of the same errors after transformation: + * { + * "intent": "REQUIRED", + * "user": { + * "username": "REQUIRED", + * "fullname": "REQUIRED", + * "termsAccepted": "FORMAT_ERROR", + * "password": "FIELDS_NOT_EQUAL", + * "passwordConfirmation": "FIELDS_NOT_EQUAL" + * } + * } + */ + transformErrors(errors) { + if (!errors) { + return undefined; + } + const result = {}; + errors.forEach((error) => { + const code = error.keyword === "format" + ? this.formatCodeMap[error.params.format] || "FORMAT_ERROR" + : this.keywordsCodeMap[error.keyword] || "FORMAT_ERROR"; + const instancePath = error.params.missingProperty + ? `${error.instancePath}/${error.params.missingProperty}` + : error.instancePath; + json_pointer_1.default.set(result, instancePath, code); + }); + /** + * JSON.parse/JSON.stringify is required to fix <1 empty item> issue. + * @example + * pointer.set(result, "/nested/prop2/0", "ERROR_1"); + * pointer.set(result, "/nested/prop2/2", "ERROR_2"); + * + * without JSON.parse/JSON.stringify: + * result.prop2 === ["ERROR_1", <1 empty item>, "ERROR_2"] + * + * with JSON.parse/JSON.stringify: + * result.prop2 === ["ERROR_1", null, "ERROR_2"] + */ + return JSON.parse(JSON.stringify(result)); + } + /** + * Validates and cleans data against the schema. + * Optionally drops null / empty-string properties (constructor options), then AJV removeAdditional. + */ + validateAndClean(data, jsonSchema) { + const validated = { ...data }; + if (this.removeNull) { + (0, removeEmptyAndNullProperties_1.removeEmptyAndNullProperties)(validated); + } + if (this.removeEmptyStrings) { + removeEmptyStringProperties(validated); + } + const compiledValidator = this.getCompiledValidator({ + ...jsonSchema, + additionalProperties: false, + }); + const isValid = compiledValidator(validated); + return { + isValid, + errors: this.transformErrors(compiledValidator.errors || undefined), + rawErrors: compiledValidator.errors, + validated, + }; + } +} +exports.JsonSchemaValidator = JsonSchemaValidator; +/** Shared singleton — hosts register app formats on this instance (null + "" strip on). */ +exports.jsonSchemaValidator = new JsonSchemaValidator(); diff --git a/dist/js/validator/index.d.ts b/dist/js/validator/index.d.ts new file mode 100644 index 00000000..202c720a --- /dev/null +++ b/dist/js/validator/index.d.ts @@ -0,0 +1,4 @@ +export { JsonSchemaValidator, jsonSchemaValidator } from "./JsonSchemaValidator"; +export type { JsonSchemaValidatorOptions } from "./JsonSchemaValidator"; +export type { ErrorObject, SchemaObject } from "ajv"; +export type { AnyObject, ValidationSchema } from "./types"; diff --git a/dist/js/validator/index.js b/dist/js/validator/index.js new file mode 100644 index 00000000..2dc9ae91 --- /dev/null +++ b/dist/js/validator/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jsonSchemaValidator = exports.JsonSchemaValidator = void 0; +var JsonSchemaValidator_1 = require("./JsonSchemaValidator"); +Object.defineProperty(exports, "JsonSchemaValidator", { enumerable: true, get: function () { return JsonSchemaValidator_1.JsonSchemaValidator; } }); +Object.defineProperty(exports, "jsonSchemaValidator", { enumerable: true, get: function () { return JsonSchemaValidator_1.jsonSchemaValidator; } }); diff --git a/dist/js/validator/rules/equalTo.d.ts b/dist/js/validator/rules/equalTo.d.ts new file mode 100644 index 00000000..c0f7a056 --- /dev/null +++ b/dist/js/validator/rules/equalTo.d.ts @@ -0,0 +1,2 @@ +import Ajv from "ajv"; +export default function addEqualToFieldKeyword(ajv: Ajv): void; diff --git a/dist/js/validator/rules/equalTo.js b/dist/js/validator/rules/equalTo.js new file mode 100644 index 00000000..9eea62df --- /dev/null +++ b/dist/js/validator/rules/equalTo.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = addEqualToFieldKeyword; +function addEqualToFieldKeyword(ajv) { + ajv.addKeyword({ + keyword: "equalToField", + type: "string", // Applies to string fields + schemaType: "string", // The schema for this keyword should be a string (field name) + validate(schema, data, _parentSchema, dataCxt) { + const otherFieldValue = dataCxt === null || dataCxt === void 0 ? void 0 : dataCxt.parentData[schema]; + return data === otherFieldValue; + }, + errors: true, // Enable error handling + error: { + message: (cxt) => `should be equal to the value of "${cxt.schema}"`, + }, + }); +} diff --git a/dist/js/validator/rules/index.d.ts b/dist/js/validator/rules/index.d.ts new file mode 100644 index 00000000..dfbc5056 --- /dev/null +++ b/dist/js/validator/rules/index.d.ts @@ -0,0 +1,6 @@ +import type Ajv from "ajv"; +/** + * Shared AJV formats/keywords for the standalone validator. + * App-specific formats (password, phone, username, email) are registered by the host. + */ +export default function addSharedFormatsAndKeywords(ajv: Ajv): void; diff --git a/dist/js/validator/rules/index.js b/dist/js/validator/rules/index.js new file mode 100644 index 00000000..db61979c --- /dev/null +++ b/dist/js/validator/rules/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = addSharedFormatsAndKeywords; +const jsonSchemaToTypescriptKeywords_1 = __importDefault(require("@mat3ra/esse/dist/js/utils/ajvKeywords/jsonSchemaToTypescriptKeywords")); +const equalTo_1 = __importDefault(require("./equalTo")); +const isoDate_1 = __importDefault(require("./isoDate")); +/** + * Shared AJV formats/keywords for the standalone validator. + * App-specific formats (password, phone, username, email) are registered by the host. + */ +function addSharedFormatsAndKeywords(ajv) { + (0, jsonSchemaToTypescriptKeywords_1.default)(ajv); + (0, equalTo_1.default)(ajv); + (0, isoDate_1.default)(ajv); +} diff --git a/dist/js/validator/rules/isoDate.d.ts b/dist/js/validator/rules/isoDate.d.ts new file mode 100644 index 00000000..3f41672e --- /dev/null +++ b/dist/js/validator/rules/isoDate.d.ts @@ -0,0 +1,2 @@ +import Ajv from "ajv"; +export default function addIsoDateKeyword(ajv: Ajv): void; diff --git a/dist/js/validator/rules/isoDate.js b/dist/js/validator/rules/isoDate.js new file mode 100644 index 00000000..58fcabc4 --- /dev/null +++ b/dist/js/validator/rules/isoDate.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = addIsoDateKeyword; +function addIsoDateKeyword(ajv) { + ajv.addKeyword({ + keyword: "isoDate", + type: "string", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate(_schema, data, _parentSchema, dataCxt) { + if (data === null || typeof data === "undefined") { + return true; + } + if (typeof data !== "string") { + return false; + } + const isValid = !Number.isNaN(Date.parse(data)); + if (isValid && dataCxt) { + dataCxt.parentData[dataCxt.parentDataProperty] = new Date(data); + } + return isValid; + }, + errors: false, + modifying: true, + metaSchema: { + type: "boolean", + }, + }); +} diff --git a/dist/js/validator/types.d.ts b/dist/js/validator/types.d.ts new file mode 100644 index 00000000..37633a2b --- /dev/null +++ b/dist/js/validator/types.d.ts @@ -0,0 +1,5 @@ +import type { SchemaObject } from "ajv"; +export type ValidationSchema = SchemaObject & { + $id: string; +}; +export type AnyObject = Record; diff --git a/dist/js/validator/types.js b/dist/js/validator/types.js new file mode 100644 index 00000000..c8ad2e54 --- /dev/null +++ b/dist/js/validator/types.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/package-lock.json b/package-lock.json index 98966b76..5d935756 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,14 @@ "license": "Apache-2.0", "dependencies": { "@types/mathjs": "^5.0.1", + "ajv": "^8.20.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0", "crypto-js": "^4.2.0", "js-yaml": "^4.1.0", + "json-pointer": "^0.6.2", "json-schema": "^0.4.0", + "json-schema-merge-allof": "^0.8.1", "lodash": "^4.17.*", "mathjs": "^5.10.3", "mixwith": "^0.1.1", @@ -33,12 +38,14 @@ "@babel/register": "^7.25.7", "@babel/runtime-corejs3": "^7.25.7", "@exabyte-io/eslint-config": "^2025.1.15-0", - "@mat3ra/esse": "2026.5.27-0", + "@mat3ra/esse": "https://github.com/mat3ra/esse/tarball/13eb236299a2d5f3a43e75d1fe9e121deb674c0e", "@mat3ra/tsconfig": "2024.6.3-0", "@types/chai": "^4.3.20", "@types/crypto-js": "^4.2.2", "@types/js-yaml": "^4.0.9", + "@types/json-pointer": "^1.0.34", "@types/json-schema": "^7.0.15", + "@types/json-schema-merge-allof": "^0.6.5", "@types/mocha": "^10.0.8", "@types/node": "^20.16.10", "@types/underscore": "^1.11.15", @@ -104,9 +111,9 @@ } }, "node_modules/@babel/cli": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.6.tgz", - "integrity": "sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.29.7.tgz", + "integrity": "sha512-/75HwRbAYPqXv/Ax1h7Fg3IZfXgdU98jnA8H93/m/QBaPV3Hp5ICoLqzGYye1yHBCgpmXvtqgSUN8oOKX5tojQ==", "dev": true, "license": "MIT", "dependencies": { @@ -143,13 +150,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -158,9 +165,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -168,21 +175,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -208,9 +215,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz", - "integrity": "sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz", + "integrity": "sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==", "dev": true, "license": "MIT", "dependencies": { @@ -236,14 +243,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -253,27 +260,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -292,18 +299,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "engines": { @@ -323,13 +330,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-annotate-as-pure": "^7.29.7", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, @@ -380,9 +387,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -390,43 +397,43 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -436,22 +443,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -459,15 +466,15 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -477,15 +484,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -495,23 +502,23 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -519,9 +526,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -529,9 +536,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -539,29 +546,29 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -583,13 +590,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -599,14 +606,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", - "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -616,13 +623,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -632,13 +639,30 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -648,15 +672,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -666,14 +690,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", - "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/traverse": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1021,13 +1045,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", - "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1037,13 +1061,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1066,13 +1090,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1192,13 +1216,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1224,13 +1248,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1240,15 +1264,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1258,15 +1282,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1276,13 +1300,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1292,13 +1316,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1308,14 +1332,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1325,14 +1349,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1342,18 +1366,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1363,14 +1387,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1380,14 +1404,14 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1397,14 +1421,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", - "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1414,13 +1438,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1430,14 +1454,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1447,13 +1471,13 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1463,14 +1487,14 @@ } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", - "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1480,13 +1504,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", - "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1496,13 +1520,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1512,14 +1536,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1529,15 +1553,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1547,13 +1571,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", - "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1563,13 +1587,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1579,13 +1603,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1595,13 +1619,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1611,14 +1635,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1628,14 +1652,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1645,16 +1669,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", - "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.8.tgz", + "integrity": "sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.29.0" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.8" }, "engines": { "node": ">=6.9.0" @@ -1664,14 +1688,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1681,14 +1705,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1698,13 +1722,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1714,13 +1738,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1730,13 +1754,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1746,17 +1770,17 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1766,14 +1790,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1783,13 +1807,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1799,14 +1823,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1816,13 +1840,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1832,14 +1856,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1849,15 +1873,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1867,13 +1891,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1883,13 +1907,13 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz", + "integrity": "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1899,17 +1923,17 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", + "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-jsx": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1919,13 +1943,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", + "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" + "@babel/plugin-transform-react-jsx": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1935,14 +1959,14 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz", + "integrity": "sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1952,13 +1976,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.8.tgz", + "integrity": "sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1968,14 +1992,14 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", - "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1985,13 +2009,13 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2001,13 +2025,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2017,14 +2041,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz", + "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2034,13 +2058,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2050,13 +2074,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2066,13 +2090,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2082,17 +2106,17 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", - "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2102,13 +2126,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2118,14 +2142,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", - "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2135,14 +2159,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2152,14 +2176,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", - "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2169,76 +2193,77 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", - "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.28.6", - "@babel/plugin-syntax-import-attributes": "^7.28.6", + "@babel/plugin-syntax-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.29.0", - "@babel/plugin-transform-async-to-generator": "^7.28.6", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.6", - "@babel/plugin-transform-class-properties": "^7.28.6", - "@babel/plugin-transform-class-static-block": "^7.28.6", - "@babel/plugin-transform-classes": "^7.28.6", - "@babel/plugin-transform-computed-properties": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.28.6", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.6", - "@babel/plugin-transform-exponentiation-operator": "^7.28.6", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.28.6", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.0", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", - "@babel/plugin-transform-numeric-separator": "^7.28.6", - "@babel/plugin-transform-object-rest-spread": "^7.28.6", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.28.6", - "@babel/plugin-transform-optional-chaining": "^7.28.6", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.28.6", - "@babel/plugin-transform-private-property-in-object": "^7.28.6", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.29.0", - "@babel/plugin-transform-regexp-modifiers": "^7.28.6", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.28.6", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.28.6", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", + "@babel/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.15", "babel-plugin-polyfill-corejs3": "^0.14.0", @@ -2277,18 +2302,18 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.29.7.tgz", + "integrity": "sha512-C+PV1TFUPTmBQGoPBL8j2QmLpZ117YTCwxIZeJOM96GbYMFSc7/pOXU5lVykwnZxyTqQxRsvoRk6f2FktZgGHA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-transform-react-display-name": "^7.29.7", + "@babel/plugin-transform-react-jsx": "^7.29.7", + "@babel/plugin-transform-react-jsx-development": "^7.29.7", + "@babel/plugin-transform-react-pure-annotations": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2298,17 +2323,17 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", - "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2318,9 +2343,9 @@ } }, "node_modules/@babel/register": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.28.6.tgz", - "integrity": "sha512-pgcbbEl/dWQYb6L6Yew6F94rdwygfuv+vJ/tXfwIOYAfPB6TNWpXUMEtEq3YuTeHRdvMIhvz13bkT9CNaS+wqA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.29.7.tgz", + "integrity": "sha512-AMGJoWuES861riy6pcB0fphE1YXybtQnBYQMuIyPv6mKLiosfa79BKTnAOyx215c/3RJPJpdQwoHZ3earVH7AA==", "dev": true, "license": "MIT", "dependencies": { @@ -2338,9 +2363,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "dev": true, "license": "MIT", "engines": { @@ -2348,9 +2373,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz", - "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.7.tgz", + "integrity": "sha512-ppj9ouYku+RX0ljtgZd+KMO5mkM2bCqg8H2PYAFWnLsHEIKIdRojqbJ2i3eVHrisuxy7nOFCmngTDdWtUCdXUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2361,33 +2386,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -2395,14 +2420,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2501,6 +2526,23 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@eslint/eslintrc/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -2547,6 +2589,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint/eslintrc/node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -2781,29 +2830,29 @@ "license": "MIT" }, "node_modules/@mat3ra/esse": { - "version": "2026.5.27-0", - "resolved": "https://registry.npmjs.org/@mat3ra/esse/-/esse-2026.5.27-0.tgz", - "integrity": "sha512-0/z6ORG+hNK64g/5S6JOZbgSUlTJDK4weMkkK4OIqYO4W26GQWRhvtRg4ydK38kV+IUHnwNYlEl3Z0b6XOwqTg==", + "version": "0.0.0", + "resolved": "https://github.com/mat3ra/esse/tarball/13eb236299a2d5f3a43e75d1fe9e121deb674c0e", + "integrity": "sha512-vHou0Fely/PWEkenU5xDFuJd+4kaLuGfdPB+Y80PbOmZgQyNAU0PXUKOnxCLewlLJb0C0yusuSBhx87D5P12jQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@babel/cli": "^7.28.6", - "@babel/core": "^7.29.0", - "@babel/eslint-parser": "^7.28.6", + "@babel/cli": "^7.29.7", + "@babel/core": "^7.29.7", + "@babel/eslint-parser": "^7.29.7", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/preset-env": "^7.29.2", - "@babel/preset-react": "^7.28.5", - "@babel/preset-typescript": "^7.28.5", - "@babel/register": "^7.28.6", - "@babel/runtime-corejs3": "^7.29.2", - "@mat3ra/utils": "^2026.4.15-1", + "@babel/preset-env": "^7.29.7", + "@babel/preset-react": "^7.29.7", + "@babel/preset-typescript": "^7.29.7", + "@babel/register": "^7.29.7", + "@babel/runtime-corejs3": "^7.29.7", + "@mat3ra/utils": "^2026.6.11-0", "@types/chai": "^4.3.20", "@types/js-yaml": "^4.0.9", "@types/json-schema-merge-allof": "^0.6.5", "@types/mocha": "^10.0.10", "ajv": "^8.20.0", "ajv-formats": "^2.1.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "json-schema": "^0.4.0", "json-schema-deref-sync": "^0.14.0", "json-schema-merge-allof": "^0.8.1", @@ -2814,30 +2863,6 @@ "node": ">=14.0.0" } }, - "node_modules/@mat3ra/esse/node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@mat3ra/esse/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, "node_modules/@mat3ra/tsconfig": { "version": "2024.6.3-0", "resolved": "https://registry.npmjs.org/@mat3ra/tsconfig/-/tsconfig-2024.6.3-0.tgz", @@ -2848,9 +2873,9 @@ } }, "node_modules/@mat3ra/utils": { - "version": "2026.4.15-1", - "resolved": "https://registry.npmjs.org/@mat3ra/utils/-/utils-2026.4.15-1.tgz", - "integrity": "sha512-ahIUAc9QzbZ+G3rvIPW5BqAwa4pPMjDwH4k3+1SrXSYUhM67z+S4FTpcsfNn4WkAgmJ2Az9l026RvP3xZcXF9g==", + "version": "2026.6.11-0", + "resolved": "https://registry.npmjs.org/@mat3ra/utils/-/utils-2026.6.11-0.tgz", + "integrity": "sha512-teAR++MGHsPpFOjLE/8TFcqzv3i3/Is1t0rMnWoO/ZXO5ICBiJzl9Uar4uFu4yBrVDsO7+f51/sjBSkquAYx/w==", "dev": true, "license": "ISC", "dependencies": { @@ -3462,6 +3487,13 @@ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, + "node_modules/@types/json-pointer": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/@types/json-pointer/-/json-pointer-1.0.34.tgz", + "integrity": "sha512-JRnWcxzXSaLei98xgw1B7vAeBVOrkyw0+Rt9j1QoJrczE78OpHsyQC8GNbuhw+/2vxxDe58QvWnngS86CoIbRg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3473,6 +3505,7 @@ "resolved": "https://registry.npmjs.org/@types/json-schema-merge-allof/-/json-schema-merge-allof-0.6.5.tgz", "integrity": "sha512-5mS11ZUTyFNUVEMpK3uKoPb6BWL/nLgW/ln2VOiI8OOxKEYC4Gl9O3WjS5P49yqVTfkcbCAPKw3T1O4erUah5g==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "*" } @@ -3790,15 +3823,15 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -3809,7 +3842,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -3822,28 +3855,18 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -3905,7 +3928,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "devOptional": true, + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -4258,7 +4281,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8" }, @@ -4280,7 +4303,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "devOptional": true, + "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -4491,7 +4514,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "devOptional": true, + "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4729,7 +4752,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz", "integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==", - "dev": true, "dependencies": { "validate.io-array": "^1.0.3", "validate.io-function": "^1.0.2", @@ -4740,7 +4762,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz", "integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==", - "dev": true, "dependencies": { "compute-gcd": "^1.2.1", "validate.io-array": "^1.0.3", @@ -5443,21 +5464,6 @@ "eslint-plugin-import": "*" } }, - "node_modules/eslint-import-resolver-meteor": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-meteor/-/eslint-import-resolver-meteor-0.4.0.tgz", - "integrity": "sha512-BSqvgt6QZvk9EGhDGnM4azgbxyBD8b0y6FYA52WFzpWpHcZV9ys8PxM33bx8dlCy3HyopRLLsMUnlhTpZzsZmQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "object-assign": "^4.0.1", - "resolve": "^1.1.6" - }, - "peerDependencies": { - "eslint-plugin-import": ">=1.4.0" - } - }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -5753,20 +5759,6 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5878,6 +5870,23 @@ "@babel/highlight": "^7.10.4" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -5994,6 +6003,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/eslint/node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -6134,8 +6150,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.3.0", @@ -6163,7 +6178,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -6174,8 +6190,7 @@ "node_modules/fast-uri": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", - "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==", - "dev": true + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==" }, "node_modules/fastq": { "version": "1.17.1", @@ -6202,7 +6217,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "devOptional": true, + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6274,6 +6289,12 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreach": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", + "license": "MIT" + }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -6492,7 +6513,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -6851,7 +6872,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6943,7 +6964,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6988,7 +7009,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -7057,7 +7078,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.12.0" } @@ -7474,9 +7495,19 @@ "dev": true }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -7514,6 +7545,15 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-pointer": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", + "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", + "license": "MIT", + "dependencies": { + "foreach": "^2.0.4" + } + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -7523,7 +7563,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz", "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==", - "dev": true, "dependencies": { "lodash": "^4.17.4" } @@ -7551,7 +7590,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz", "integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==", - "dev": true, + "license": "MIT", "dependencies": { "compute-lcm": "^1.1.2", "json-schema-compare": "^0.2.2", @@ -7602,10 +7641,10 @@ } }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -8421,7 +8460,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9068,7 +9107,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, @@ -9200,6 +9239,7 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -9243,7 +9283,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -9392,7 +9432,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -10108,28 +10147,6 @@ "node": ">=10.0.0" } }, - "node_modules/table/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -10213,7 +10230,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -10622,6 +10639,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -10660,20 +10678,17 @@ "node_modules/validate.io-array": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", - "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", - "dev": true + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==" }, "node_modules/validate.io-function": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", - "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==", - "dev": true + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==" }, "node_modules/validate.io-integer": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz", "integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==", - "dev": true, "dependencies": { "validate.io-number": "^1.0.3" } @@ -10682,7 +10697,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz", "integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==", - "dev": true, "dependencies": { "validate.io-array": "^1.0.3", "validate.io-integer": "^1.0.4" @@ -10691,8 +10705,7 @@ "node_modules/validate.io-number": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", - "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==", - "dev": true + "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==" }, "node_modules/which": { "version": "2.0.2", diff --git a/package.json b/package.json index 4a175666..d2c6037b 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,15 @@ "license": "Apache-2.0", "homepage": "https://github.com/Exabyte-io/code.js", "dependencies": { + "@types/mathjs": "^5.0.1", + "ajv": "^8.20.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0", "crypto-js": "^4.2.0", "js-yaml": "^4.1.0", + "json-pointer": "^0.6.2", "json-schema": "^0.4.0", + "json-schema-merge-allof": "^0.8.1", "lodash": "^4.17.*", "mathjs": "^5.10.3", "mixwith": "^0.1.1", @@ -41,8 +47,7 @@ "semver": "^7.6.3", "underscore": "^1.13.7", "underscore.string": "^3.3.6", - "uuid": "8.3.2", - "@types/mathjs": "^5.0.1" + "uuid": "8.3.2" }, "devDependencies": { "@babel/cli": "^7.25.7", @@ -55,12 +60,14 @@ "@babel/register": "^7.25.7", "@babel/runtime-corejs3": "^7.25.7", "@exabyte-io/eslint-config": "^2025.1.15-0", - "@mat3ra/esse": "2026.5.27-0", + "@mat3ra/esse": "https://github.com/mat3ra/esse/tarball/13eb236299a2d5f3a43e75d1fe9e121deb674c0e", "@mat3ra/tsconfig": "2024.6.3-0", "@types/chai": "^4.3.20", "@types/crypto-js": "^4.2.2", "@types/js-yaml": "^4.0.9", + "@types/json-pointer": "^1.0.34", "@types/json-schema": "^7.0.15", + "@types/json-schema-merge-allof": "^0.6.5", "@types/mocha": "^10.0.8", "@types/node": "^20.16.10", "@types/underscore": "^1.11.15", diff --git a/scripts/generate-mixins.ts b/scripts/generate-mixins.ts index 0108f2fc..45a848f0 100644 --- a/scripts/generate-mixins.ts +++ b/scripts/generate-mixins.ts @@ -24,6 +24,8 @@ const SKIP_FIELDS: string[] = []; * Output file paths for each schema */ const OUTPUT_PATHS = { + "system/hashed": "src/js/generated/HashedSchemaMixin.ts", + "system/bankable": "src/js/generated/BankableSchemaMixin.ts", "system/defaultable": "src/js/generated/DefaultableSchemaMixin.ts", "system/has-consistency-check": "src/js/generated/HasConsistencyChecksSchemaMixin.ts", "system/description": "src/js/generated/HasDescriptionSchemaMixin.ts", diff --git a/src/js/entity/in_memory.ts b/src/js/entity/in_memory.ts index c4d3065a..400172f4 100644 --- a/src/js/entity/in_memory.ts +++ b/src/js/entity/in_memory.ts @@ -16,7 +16,7 @@ export enum ValidationErrorCode { interface ErrorDetails { error?: object | null; - json: AnyObject; + json: object; schema: JSONSchema; } @@ -32,9 +32,11 @@ export class EntityError extends Error { } } -export class InMemoryEntity implements BaseInMemoryEntitySchema { - static create(config: object) { - return new (this.prototype.constructor as typeof InMemoryEntity)(config); +type Schema = BaseInMemoryEntitySchema; + +export class InMemoryEntity implements Schema { + static create>(config: Schema): T { + return new (this.prototype.constructor as InMemoryEntityConstructor)(config); } // Override if deepClone of config is required @@ -44,35 +46,32 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { static readonly jsonSchema?: JSONSchema; - _json: AnyObject = {}; + _json: S; - constructor(config: object | InMemoryEntity = {}) { - if (config instanceof InMemoryEntity) { - this._json = config.toJSON(); - } else { - this._json = (this.constructor as typeof InMemoryEntity)._isDeepCloneRequired - ? deepClone(config) - : clone(config); - } + // NoInfer: keep default S (or an explicit type arg) instead of inferring S from the config literal. + constructor(config: NoInfer) { + this._json = (this.constructor as typeof InMemoryEntity)._isDeepCloneRequired + ? deepClone(config) + : clone(config); } - prop(name: string, defaultValue: T): T; + prop(name: K, defaultValue: S[K]): S[K]; - prop(name: string): T | undefined; + prop(name: K): S[K] | undefined; /** * @summary Return a prop or the default */ - prop(name: string, defaultValue?: T): T | undefined { - // `lodash.get` gets `null` when the value is `null`, but we still want a default value in this case, hence `||` - return (getValue(this._json, name, defaultValue) as T) ?? defaultValue; + prop(name: K, defaultValue?: S[K]): S[K] | undefined { + // `lodash.get` gets `null` when the value is `null`, but we still want a default value in this case, hence `??` + return (getValue(this._json, name, defaultValue) as S[K] | undefined) ?? defaultValue; } /** * @summary Return a required prop, throwing an error if it doesn't exist or is undefined/null */ - requiredProp(name: string): T { - const value = this.prop(name); + requiredProp(name: K): S[K] { + const value = this.prop(name); if (value === undefined || value === null) { throw new EntityError({ code: ValidationErrorCode.REQUIRED_PROPERTY_MISSING, @@ -89,7 +88,7 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { /** * @summary Set a prop */ - setProp(name: string, value: unknown) { + setProp(name: keyof S, value: S[typeof name]) { // lodash.set is required to support dot-notation in keys (e.g. "compute.cluster.fqdn") set(this._json, name, value); } @@ -97,7 +96,7 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { /** * @summary Remove a prop */ - unsetProp(name: string) { + unsetProp(name: keyof S) { delete this._json[name]; } @@ -105,26 +104,35 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { * Updates internal JSON. Works the same as Mongo's $set operator * @see https://www.mongodb.com/docs/manual/reference/operator/update/set/#-set */ - setProps(json: AnyObject = {}) { - Object.entries(json).forEach(([key, value]) => this.setProp(key, value)); + setProps(json: Partial = {}) { + Object.entries(json).forEach(([key, value]) => { + const keyType = key as keyof S; + this.setProp(keyType, value as S[typeof keyType]); + }); return this; } /** - * @summary Array of fields to exclude from resulted JSON + * @summary Array of fields to exclude from resulted JSON. + * JSON.stringify calls `toJSON(key)` with the parent property name (a string). + * Only an actual array is treated as an omit list — otherwise stringify would + * strip fields whose names match the property key (e.g. systemTeams.owner). */ - toJSON(exclude: string[] = []) { + toJSON(exclude: (keyof S)[] = []): S { + const omitKeys = Array.isArray(exclude) ? exclude : []; return (this.constructor as typeof InMemoryEntity)._isDeepCloneRequired - ? this.toJSONSafe(exclude) - : this.toJSONQuick(exclude); + ? this.toJSONSafe(omitKeys) + : this.toJSONQuick(omitKeys); } - toJSONSafe(exclude: string[] = []): AnyObject { - return this.clean(deepClone(omit(this._json, exclude))); + toJSONSafe(exclude: (keyof S)[] = []): S { + const omitKeys = Array.isArray(exclude) ? exclude : []; + return this.clean(deepClone(omit(this._json, omitKeys))); } - toJSONQuick(exclude: string[] = []): AnyObject { - return this.clean(clone(omit(this._json, exclude))); + toJSONQuick(exclude: (keyof S)[] = []): S { + const omitKeys = Array.isArray(exclude) ? exclude : []; + return this.clean(clone(omit(this._json, omitKeys))); } /** @@ -140,15 +148,15 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { }); } - static validateData(data: AnyObject, clean = false, jsonSchema = this.jsonSchema) { + static validateData(data: object, clean = false, jsonSchema = this.jsonSchema) { if (!jsonSchema) { return data; } const result = clean - ? ajv.validateAndClean(data, jsonSchema, { + ? ajv.validateAndClean(data as AnyObject, jsonSchema, { coerceTypes: this.allowJsonSchemaTypesCoercing, }) - : ajv.validate(data, jsonSchema); + : ajv.validate(data as AnyObject, jsonSchema); if (!result.isValid) { throw new EntityError({ @@ -173,9 +181,9 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { } } - clean(config: AnyObject) { + clean(config: S) { try { - return (this.constructor as typeof InMemoryEntity).validateData(config, true); + return (this.constructor as typeof InMemoryEntity).validateData(config, true) as S; } catch (err) { if (err instanceof EntityError) { console.error({ @@ -216,12 +224,14 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { * @param byIdOnly if true, return only the id * @returns identifying data */ - getAsEntityReference(byIdOnly: true): { _id: string }; + getAsEntityReference(byIdOnly: true): { _id: NonNullable }; - getAsEntityReference(byIdOnly?: false): Required; + getAsEntityReference(byIdOnly?: false): EntityReferenceSchema & { _id: string; cls: string }; getAsEntityReference(byIdOnly = false) { - if (!this.id) { + // Slug is usually present on entity references, but not required for all entities + // (e.g. workflows are not slugified). Only `_id` is required to form a reference. + if (!this._id) { throw new EntityError({ code: ValidationErrorCode.ENTITY_REFERENCE_ERROR, details: { @@ -232,74 +242,52 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { } if (byIdOnly) { - return { _id: this.id }; + return { _id: this._id }; } return { - _id: this.id, - slug: this.slug, + _id: this._id, + ...(this.slug !== undefined ? { slug: this.slug } : {}), cls: this.getClsName(), }; } - /** - * @summary Pluck an entity from a collection by name. - * If no name is provided and no entity has prop isDefault, return the first entity - * @param entities the entities - * @param entity the kind of entities - * @param name the name of the entity to choose - */ - // eslint-disable-next-line class-methods-use-this - getEntityByName(entities: InMemoryEntity[], entity: string, name: string) { - let filtered; - if (!name) { - filtered = entities.filter((ent) => ent.prop("isDefault") === true); - if (!filtered.length) filtered = [entities[0]]; - } else { - filtered = entities.filter((ent) => ent.prop("name") === name); - } - if (filtered.length !== 1) { - console.log(`found ${filtered.length} entity ${entity} with name ${name} expected 1`); - } - return filtered[0]; - } - // Properties from BaseInMemoryEntitySchema get id() { - return this.prop("_id", ""); + return this.prop("_id"); } - set id(id) { + set id(id: S["_id"]) { this.setProp("_id", id); } get _id() { - return this.prop("_id", ""); + return this.prop("_id"); } - set _id(id) { + set _id(id: S["_id"]) { this.setProp("_id", id); } get schemaVersion() { - return this.prop("schemaVersion", ""); + return this.prop("schemaVersion"); } - set schemaVersion(schemaVersion) { + set schemaVersion(schemaVersion: S["schemaVersion"]) { this.setProp("schemaVersion", schemaVersion); } get systemName() { - return this.prop("systemName", ""); + return this.prop("systemName"); } - set systemName(systemName) { + set systemName(systemName: S["systemName"]) { this.setProp("systemName", systemName); } get slug() { - return this.prop("slug", ""); + return this.prop("slug"); } get isSystemEntity() { diff --git a/src/js/entity/index.ts b/src/js/entity/index.ts index 4a5bee5e..425ab4c8 100644 --- a/src/js/entity/index.ts +++ b/src/js/entity/index.ts @@ -7,7 +7,6 @@ import { NamedInMemoryEntity, } from "./other"; import { ENTITY_SET_TYPES } from "./set/enums"; -import { constructEntitySetFactoryByConfig } from "./set/factory"; import { InMemoryEntitySetMixin } from "./set/mixins"; import { orderedEntityInSetMixin, orderedEntitySetMixin } from "./set/ordered/mixins"; import * as selectorsForEntitySet from "./set/selectors"; @@ -20,7 +19,6 @@ export { HasMetadataNamedDefaultableInMemoryEntity, HasConsistencyChecksHasMetadataNamedDefaultableInMemoryEntity, ENTITY_SET_TYPES, - constructEntitySetFactoryByConfig, selectorsForEntitySet, InMemoryEntitySetMixin, orderedEntitySetMixin, diff --git a/src/js/entity/mixins/BankableEntityMixin.ts b/src/js/entity/mixins/BankableEntityMixin.ts new file mode 100644 index 00000000..1e1e5c2b --- /dev/null +++ b/src/js/entity/mixins/BankableEntityMixin.ts @@ -0,0 +1,15 @@ +import { type BankableSchemaMixin, bankableSchemaMixin } from "../../generated/BankableSchemaMixin"; +import type { Constructor } from "../../utils/types"; +import { InMemoryEntity } from "../in_memory"; +import { type HashedEntity, hashedEntityMixin } from "./HashedEntityMixin"; + +export type BankableEntity = BankableSchemaMixin & HashedEntity; + +export type BankableInMemoryEntityConstructor = Constructor; + +export function bankableEntityMixin( + item: T, +): asserts item is T & BankableEntity { + bankableSchemaMixin(item); + hashedEntityMixin(item); +} diff --git a/src/js/entity/mixins/HasConsistencyChecksMixin.ts b/src/js/entity/mixins/HasConsistencyChecksMixin.ts index 99b8cf58..cf13558f 100644 --- a/src/js/entity/mixins/HasConsistencyChecksMixin.ts +++ b/src/js/entity/mixins/HasConsistencyChecksMixin.ts @@ -21,7 +21,7 @@ export function hasConsistencyChecksMixin( hasConsistencyChecksSchemaMixin(item); // @ts-expect-error - const properties: InMemoryEntity & HasConsistencyChecks = { + const properties: InMemoryEntity & HasConsistencyChecks = { addConsistencyChecks(array: ConsistencyCheck[]) { this.consistencyChecks = [...(this.consistencyChecks || []), ...array]; }, diff --git a/src/js/entity/mixins/HasMetadataMixin.ts b/src/js/entity/mixins/HasMetadataMixin.ts index 02cbaedc..3986ee53 100644 --- a/src/js/entity/mixins/HasMetadataMixin.ts +++ b/src/js/entity/mixins/HasMetadataMixin.ts @@ -5,9 +5,12 @@ import { InMemoryEntity } from "../in_memory"; type Metadata = MetadataSchema["metadata"]; -export type HasMetadata = { - metadata?: T; - updateMetadata: (object: Partial) => void; +type HasMetadataSchema = { + metadata?: M; +}; + +export type HasMetadata = HasMetadataSchema & { + updateMetadata: (object: M) => void; }; export type HasMetadataInMemoryEntityConstructor = Constructor< @@ -16,16 +19,16 @@ export type HasMetadataInMemoryEntityConstructor function hasMetadataPropertiesMixin( item: T, -): asserts item is T & HasMetadata { +): asserts item is T & HasMetadata { // @ts-expect-error - const properties: InMemoryEntity & HasMetadata = { + const properties: InMemoryEntity> & HasMetadata = { get metadata() { - return this.prop("metadata"); + return this.prop("metadata"); }, set metadata(value: M | undefined) { this.setProp("metadata", value); }, - updateMetadata(object: Partial) { + updateMetadata(object: M) { this.setProp("metadata", { ...this.metadata, ...object }); }, }; diff --git a/src/js/entity/mixins/HasScopeTrackMixin.ts b/src/js/entity/mixins/HasScopeTrackMixin.ts index 12e1ec54..2b19b57b 100644 --- a/src/js/entity/mixins/HasScopeTrackMixin.ts +++ b/src/js/entity/mixins/HasScopeTrackMixin.ts @@ -1,5 +1,9 @@ import { InMemoryEntity } from "../in_memory"; +type ScopeTrackSchema = { + scopeTrack?: unknown[]; +}; + type ScopeTrackDescriptor = { get scopeTrack(): unknown[]; set scopeTrack(array: unknown[]); @@ -7,9 +11,9 @@ type ScopeTrackDescriptor = { function schemaMixin(item: InMemoryEntity) { // @ts-expect-error - const properties: InMemoryEntity & ScopeTrackDescriptor = { + const properties: InMemoryEntity & ScopeTrackDescriptor = { get scopeTrack(): unknown[] { - return this.prop("scopeTrack", []); + return this.prop("scopeTrack") ?? []; }, set scopeTrack(array: unknown[]) { this.setProp("scopeTrack", array); diff --git a/src/js/entity/mixins/HashedEntityMixin.ts b/src/js/entity/mixins/HashedEntityMixin.ts index 7aed5a06..8e2eb74c 100644 --- a/src/js/entity/mixins/HashedEntityMixin.ts +++ b/src/js/entity/mixins/HashedEntityMixin.ts @@ -1,8 +1,9 @@ +import { type HashedSchemaMixin, hashedSchemaMixin } from "../../generated/HashedSchemaMixin"; import { calculateHashFromObject } from "../../utils/hash"; import type { Constructor } from "../../utils/types"; import { InMemoryEntity } from "../in_memory"; -export type HashedEntity = { +export type HashedEntity = HashedSchemaMixin & { calculateHash(): string; getHashObject?(): object; }; @@ -12,8 +13,10 @@ export type HashedInMemoryEntityConstructor = Constructor; export function hashedEntityMixin( item: T, ): asserts item is T & HashedEntity { + hashedSchemaMixin(item); + // @ts-expect-error - const properties: InMemoryEntity & HashedEntity = { + const properties: InMemoryEntity & Pick = { /** * @summary Calculates hash based on meaningful fields and unit-specific fields. Unit-specific fields are * separated into _typeSpecificHash function which can be overwritten by child classes. diff --git a/src/js/entity/mixins/ImportantSettingsProviderMixin.ts b/src/js/entity/mixins/ImportantSettingsProviderMixin.ts deleted file mode 100644 index 0ea00155..00000000 --- a/src/js/entity/mixins/ImportantSettingsProviderMixin.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { deepClone } from "../../utils/clone"; -import type { Constructor } from "../../utils/types"; -import type { InMemoryEntity } from "../in_memory"; - -export interface ContextProvider { - domain?: string; -} - -export type ImportantSettingsProvider = { - important: object; - setImportant(key: string, value: unknown): void; - importantSettingsProviders: ContextProvider[]; - isImportantEdited: boolean | undefined; -}; - -type AbstractBase = { - contextProviders: ContextProvider[]; -}; - -export type ImportantSettingsProviderInMemoryEntityConstructor = - Constructor; - -export function importantSettingsProviderMixin( - item: T, -): asserts item is T & ImportantSettingsProvider { - // @ts-expect-error - const properties: InMemoryEntity & AbstractBase & ImportantSettingsProvider = { - get important() { - return deepClone(this._json.important || {}); - }, - - setImportant(key: string, value: unknown) { - this.setProp("important", { [key]: value }); - }, - - get importantSettingsProviders() { - return this.contextProviders.filter((p) => p.domain === "important"); - }, - - get isImportantEdited() { - return this.prop("important.isEdited"); - }, - - set isImportantEdited(bool) { - this.setProp("important", Object.assign(this.important, { isEdited: bool })); - }, - }; - - Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); -} diff --git a/src/js/entity/mixins/NamedEntityMixin.ts b/src/js/entity/mixins/NamedEntityMixin.ts index e3e115b8..e6135099 100644 --- a/src/js/entity/mixins/NamedEntityMixin.ts +++ b/src/js/entity/mixins/NamedEntityMixin.ts @@ -17,7 +17,7 @@ function namedEntityPropertiesMixin( item: T, ): asserts item is T & NamedEntity { // @ts-expect-error - const properties: InMemoryEntity & NamedEntity = { + const properties: InMemoryEntity & NamedEntity = { setName(name: string) { this.setProp("name", name); }, diff --git a/src/js/entity/mixins/RuntimeItemsMixin.ts b/src/js/entity/mixins/RuntimeItemsMixin.ts index 432acb33..5976327f 100644 --- a/src/js/entity/mixins/RuntimeItemsMixin.ts +++ b/src/js/entity/mixins/RuntimeItemsMixin.ts @@ -21,7 +21,7 @@ function runtimeItemsPropertiesMixin( item: T, ): asserts item is T & RuntimeItems { // @ts-expect-error - const properties: InMemoryEntity & RuntimeItemsNameObject = { + const properties: InMemoryEntity & RuntimeItems = { get hashObjectFromRuntimeItems() { return { results: this.results, diff --git a/src/js/entity/mixins/TaggableMixin.ts b/src/js/entity/mixins/TaggableMixin.ts index 2d5effb5..ea5f0260 100644 --- a/src/js/entity/mixins/TaggableMixin.ts +++ b/src/js/entity/mixins/TaggableMixin.ts @@ -12,7 +12,7 @@ export type TaggableInMemoryEntityConstructor = Constructor; function taggablePropertiesMixin(item: T): asserts item is T & Taggable { // @ts-expect-error - const properties: InMemoryEntity & Taggable = { + const properties: InMemoryEntity & Taggable = { setTags(array: string[]) { this.tags = array.filter((value, index, self) => self.indexOf(value) === index); }, diff --git a/src/js/entity/set/InMemoryEntityInSetMixin.ts b/src/js/entity/set/InMemoryEntityInSetMixin.ts index f9cd9d17..56b21e6c 100644 --- a/src/js/entity/set/InMemoryEntityInSetMixin.ts +++ b/src/js/entity/set/InMemoryEntityInSetMixin.ts @@ -13,7 +13,7 @@ export function inMemoryEntityInSetMixin( inSetSchemaMixin(item); // @ts-expect-error - const properties: InMemoryEntity & InMemoryEntityInSet = { + const properties: InMemoryEntity & InMemoryEntityInSet = { getInSetFilteredByCls(cls: string) { return this.inSet.filter((ref) => ref.cls === cls); }, diff --git a/src/js/entity/set/InMemoryEntitySetBaseMixin.ts b/src/js/entity/set/InMemoryEntitySetBaseMixin.ts index de1c0fa9..90153bf2 100644 --- a/src/js/entity/set/InMemoryEntitySetBaseMixin.ts +++ b/src/js/entity/set/InMemoryEntitySetBaseMixin.ts @@ -1,10 +1,16 @@ -import { type EntitySetSchema, SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; +import { + type BaseInMemoryEntitySchema, + type EntitySetSchema, + SystemInSetSchema, +} from "@mat3ra/esse/dist/js/types"; import { type InMemoryEntity } from "../in_memory"; export type SystemInSet = Required; export type InSet = SystemInSet["inSet"][0]; +type EntitySetEntitySchema = BaseInMemoryEntitySchema & EntitySetSchema; + export enum EntitySetType { ordered = "ordered", unordered = "unordered", @@ -12,7 +18,7 @@ export enum EntitySetType { function schemaMixin(item: E) { // @ts-expect-error - const properties: InMemoryEntity & EntitySetSchema = { + const properties: InMemoryEntity & EntitySetSchema = { get isEntitySet() { return this.prop("isEntitySet", false); }, @@ -22,7 +28,7 @@ function schemaMixin(item: E) { }, get entityCls() { - return this.prop("entityCls"); + return this.prop("entityCls"); }, }; @@ -39,7 +45,9 @@ function methodsMixin(item: E & EntitySetSchema) { const originalCls = item.cls; // @ts-expect-error - const properties: InMemoryEntity & EntitySetSchema & EntitySetBaseMethodsDescriptor = { + const properties: InMemoryEntity & + EntitySetSchema & + EntitySetBaseMethodsDescriptor = { get cls() { return this.entityCls || originalCls; }, @@ -54,12 +62,16 @@ function methodsMixin(item: E & EntitySetSchema) { return properties; } -export type InMemoryEntitySetBase = ReturnType & - ReturnType; +/** Mixin shape only — safe to merge into schema-generic subclasses (no baked-in `_json`). */ +export type InMemoryEntitySetBaseMixin = EntitySetSchema & EntitySetBaseMethodsDescriptor; + +/** Instance type with default entity-set schema (includes `InMemoryEntity<_json>`). */ +export type InMemoryEntitySetBase = InMemoryEntity & + InMemoryEntitySetBaseMixin; export function inMemoryEntitySetBaseMixin( item: T, -): asserts item is T & InMemoryEntitySetBase { +): asserts item is T & InMemoryEntitySetBaseMixin { schemaMixin(item); methodsMixin(item as T & EntitySetSchema); } diff --git a/src/js/entity/set/factory.ts b/src/js/entity/set/factory.ts deleted file mode 100644 index f8bceb76..00000000 --- a/src/js/entity/set/factory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { AnyObject } from "@mat3ra/esse/dist/js/esse/types"; -import { EntitySetSchema } from "@mat3ra/esse/dist/js/types"; - -import { InMemoryEntity } from "../in_memory"; - -export const constructEntitySetFactoryByConfig = - ({ entitySetCls = InMemoryEntity }) => - (config: AnyObject, entityCls: EntitySetSchema["entityCls"]) => { - const Cls = entitySetCls; - return new Cls({ - ...config, - entityCls, - }); - }; diff --git a/src/js/entity/set/ordered/OrderedInMemoryEntityInSetMixin.ts b/src/js/entity/set/ordered/OrderedInMemoryEntityInSetMixin.ts index 779ef1ea..1f436bd4 100644 --- a/src/js/entity/set/ordered/OrderedInMemoryEntityInSetMixin.ts +++ b/src/js/entity/set/ordered/OrderedInMemoryEntityInSetMixin.ts @@ -1,3 +1,5 @@ +import { SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; + import { type InMemoryEntity } from "../../in_memory"; import type { InMemoryEntityInSet } from "../InMemoryEntityInSetMixin"; @@ -9,7 +11,9 @@ export function orderedEntityInSetMixin & + InMemoryEntityInSet & + OrderedInMemoryEntityInSet = { getIndexByIdInOrderedSet(setId: string): number { const setData = this.inSet.find((s) => s._id === setId); return setData?.index || 0; diff --git a/src/js/entity/set/ordered/OrderedInMemoryEntitySetMixin.ts b/src/js/entity/set/ordered/OrderedInMemoryEntitySetMixin.ts index f3056d2e..c08e999a 100644 --- a/src/js/entity/set/ordered/OrderedInMemoryEntitySetMixin.ts +++ b/src/js/entity/set/ordered/OrderedInMemoryEntitySetMixin.ts @@ -1,7 +1,11 @@ +import { type BaseInMemoryEntitySchema, type EntitySetSchema } from "@mat3ra/esse/dist/js/types"; + import { type InMemoryEntity } from "../../in_memory"; import { ENTITY_SET_TYPES } from "../enums"; import type { InMemoryEntitySetBase } from "../InMemoryEntitySetBaseMixin"; +type EntitySetEntitySchema = BaseInMemoryEntitySchema & EntitySetSchema; + export type OrderedInMemoryEntitySet = { get isOrderedSet(): boolean; }; @@ -10,7 +14,9 @@ export function orderedEntitySetMixin & + InMemoryEntitySetBase & + OrderedInMemoryEntitySet = { get isOrderedSet(): boolean { return this.entitySetType === ENTITY_SET_TYPES.ordered; }, diff --git a/src/js/generateSchemaMixin.ts b/src/js/generateSchemaMixin.ts index a7447abe..aba2e25c 100644 --- a/src/js/generateSchemaMixin.ts +++ b/src/js/generateSchemaMixin.ts @@ -104,7 +104,7 @@ function generateMixinFunction( ); let code = `import type { InMemoryEntity } from "${entityFrom}";\n`; - code += `import type { ${schemaName} } from "${from}";\n\n`; + code += `import type { BaseInMemoryEntitySchema, ${schemaName} } from "${from}";\n\n`; // Generate the mixin type - only use Omit if skipFields has values if (skipFields && skipFields.length > 0) { @@ -114,14 +114,14 @@ function generateMixinFunction( code += `export type ${mixinTypeName} = ${schemaName};\n\n`; } - // Generate the entity type - code += `export type ${entityTypeName} = InMemoryEntity & ${mixinTypeName};\n\n`; + // Entity schema must include base fields so `_json` / `toJSON` stay compatible with InMemoryEntity + code += `export type ${entityTypeName} = InMemoryEntity;\n\n`; code += `export function ${functionName}(\n`; code += ` item: InMemoryEntity,\n`; code += `): asserts item is T & ${mixinTypeName} {\n`; code += ` // @ts-expect-error\n`; - code += ` const properties: InMemoryEntity & ${mixinTypeName} = {\n`; + code += ` const properties: InMemoryEntity<${mixinTypeName}> & ${mixinTypeName} = {\n`; for (let i = 0; i < propertyEntries.length; i++) { const [propertyName] = propertyEntries[i]; @@ -130,7 +130,7 @@ function generateMixinFunction( const typeAnnotation = generateTypeAnnotation(propertyName, schemaName); code += `get ${propertyName}() {\n`; - code += `return this.${methodName}<${typeAnnotation}>("${propertyName}");\n`; + code += `return this.${methodName}("${propertyName}");\n`; code += `},\n`; code += `set ${propertyName}(value: ${typeAnnotation}) {\n`; code += `this.setProp("${propertyName}", value);\n`; diff --git a/src/js/generated/BankableSchemaMixin.ts b/src/js/generated/BankableSchemaMixin.ts new file mode 100644 index 00000000..f6d6449b --- /dev/null +++ b/src/js/generated/BankableSchemaMixin.ts @@ -0,0 +1,23 @@ +import type { BankableSchema, BaseInMemoryEntitySchema } from "@mat3ra/esse/dist/js/types"; + +import type { InMemoryEntity } from "../entity/in_memory"; + +export type BankableSchemaMixin = BankableSchema; + +export type BankableInMemoryEntity = InMemoryEntity; + +export function bankableSchemaMixin( + item: InMemoryEntity, +): asserts item is T & BankableSchemaMixin { + // @ts-expect-error + const properties: InMemoryEntity & BankableSchemaMixin = { + get exabyteId() { + return this.requiredProp("exabyteId"); + }, + set exabyteId(value: BankableSchema["exabyteId"]) { + this.setProp("exabyteId", value); + }, + }; + + Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); +} diff --git a/src/js/generated/DefaultableSchemaMixin.ts b/src/js/generated/DefaultableSchemaMixin.ts index cfde55d1..5a320e88 100644 --- a/src/js/generated/DefaultableSchemaMixin.ts +++ b/src/js/generated/DefaultableSchemaMixin.ts @@ -1,18 +1,20 @@ -import type { DefaultableEntitySchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, DefaultableEntitySchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type DefaultableSchemaMixin = DefaultableEntitySchema; -export type DefaultableInMemoryEntity = InMemoryEntity & DefaultableSchemaMixin; +export type DefaultableInMemoryEntity = InMemoryEntity< + BaseInMemoryEntitySchema & DefaultableSchemaMixin +>; export function defaultableSchemaMixin( item: InMemoryEntity, ): asserts item is T & DefaultableSchemaMixin { // @ts-expect-error - const properties: InMemoryEntity & DefaultableSchemaMixin = { + const properties: InMemoryEntity & DefaultableSchemaMixin = { get isDefault() { - return this.prop("isDefault"); + return this.prop("isDefault"); }, set isDefault(value: DefaultableEntitySchema["isDefault"]) { this.setProp("isDefault", value); diff --git a/src/js/generated/HasConsistencyChecksSchemaMixin.ts b/src/js/generated/HasConsistencyChecksSchemaMixin.ts index 34b9636b..8e7d8162 100644 --- a/src/js/generated/HasConsistencyChecksSchemaMixin.ts +++ b/src/js/generated/HasConsistencyChecksSchemaMixin.ts @@ -1,21 +1,24 @@ -import type { HasConsistencyCheckSchema } from "@mat3ra/esse/dist/js/types"; +import type { + BaseInMemoryEntitySchema, + HasConsistencyCheckSchema, +} from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type HasConsistencyChecksSchemaMixin = HasConsistencyCheckSchema; -export type HasConsistencyChecksInMemoryEntity = InMemoryEntity & HasConsistencyChecksSchemaMixin; +export type HasConsistencyChecksInMemoryEntity = InMemoryEntity< + BaseInMemoryEntitySchema & HasConsistencyChecksSchemaMixin +>; export function hasConsistencyChecksSchemaMixin( item: InMemoryEntity, ): asserts item is T & HasConsistencyChecksSchemaMixin { // @ts-expect-error - const properties: InMemoryEntity & HasConsistencyChecksSchemaMixin = { + const properties: InMemoryEntity & + HasConsistencyChecksSchemaMixin = { get consistencyChecks() { - return this.prop( - "consistencyChecks", - [], - ); + return this.prop("consistencyChecks"); }, set consistencyChecks(value: HasConsistencyCheckSchema["consistencyChecks"]) { this.setProp("consistencyChecks", value); diff --git a/src/js/generated/HasDescriptionSchemaMixin.ts b/src/js/generated/HasDescriptionSchemaMixin.ts index 6fc6983f..cb4ec708 100644 --- a/src/js/generated/HasDescriptionSchemaMixin.ts +++ b/src/js/generated/HasDescriptionSchemaMixin.ts @@ -1,24 +1,26 @@ -import type { DescriptionSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, DescriptionSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type HasDescriptionSchemaMixin = DescriptionSchema; -export type HasDescriptionInMemoryEntity = InMemoryEntity & HasDescriptionSchemaMixin; +export type HasDescriptionInMemoryEntity = InMemoryEntity< + BaseInMemoryEntitySchema & HasDescriptionSchemaMixin +>; export function hasDescriptionSchemaMixin( item: InMemoryEntity, ): asserts item is T & HasDescriptionSchemaMixin { // @ts-expect-error - const properties: InMemoryEntity & HasDescriptionSchemaMixin = { + const properties: InMemoryEntity & HasDescriptionSchemaMixin = { get description() { - return this.prop("description"); + return this.prop("description"); }, set description(value: DescriptionSchema["description"]) { this.setProp("description", value); }, get descriptionObject() { - return this.prop("descriptionObject"); + return this.prop("descriptionObject"); }, set descriptionObject(value: DescriptionSchema["descriptionObject"]) { this.setProp("descriptionObject", value); diff --git a/src/js/generated/HashedSchemaMixin.ts b/src/js/generated/HashedSchemaMixin.ts new file mode 100644 index 00000000..f1e22e51 --- /dev/null +++ b/src/js/generated/HashedSchemaMixin.ts @@ -0,0 +1,23 @@ +import type { BaseInMemoryEntitySchema, HashedSchema } from "@mat3ra/esse/dist/js/types"; + +import type { InMemoryEntity } from "../entity/in_memory"; + +export type HashedSchemaMixin = HashedSchema; + +export type HashedInMemoryEntity = InMemoryEntity; + +export function hashedSchemaMixin( + item: InMemoryEntity, +): asserts item is T & HashedSchemaMixin { + // @ts-expect-error + const properties: InMemoryEntity & HashedSchemaMixin = { + get hash() { + return this.requiredProp("hash"); + }, + set hash(value: HashedSchema["hash"]) { + this.setProp("hash", value); + }, + }; + + Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); +} diff --git a/src/js/generated/InSetSchemaMixin.ts b/src/js/generated/InSetSchemaMixin.ts index 40f37f4e..bc347587 100644 --- a/src/js/generated/InSetSchemaMixin.ts +++ b/src/js/generated/InSetSchemaMixin.ts @@ -1,18 +1,18 @@ -import type { SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, SystemInSetSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type InSetSchemaMixin = SystemInSetSchema; -export type InSetInMemoryEntity = InMemoryEntity & InSetSchemaMixin; +export type InSetInMemoryEntity = InMemoryEntity; export function inSetSchemaMixin( item: InMemoryEntity, ): asserts item is T & InSetSchemaMixin { // @ts-expect-error - const properties: InMemoryEntity & InSetSchemaMixin = { + const properties: InMemoryEntity & InSetSchemaMixin = { get inSet() { - return this.requiredProp("inSet"); + return this.requiredProp("inSet"); }, set inSet(value: SystemInSetSchema["inSet"]) { this.setProp("inSet", value); diff --git a/src/js/generated/NamedEntitySchemaMixin.ts b/src/js/generated/NamedEntitySchemaMixin.ts index 72744c2b..5730d633 100644 --- a/src/js/generated/NamedEntitySchemaMixin.ts +++ b/src/js/generated/NamedEntitySchemaMixin.ts @@ -1,18 +1,20 @@ -import type { NameEntitySchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, NameEntitySchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type NamedEntitySchemaMixin = NameEntitySchema; -export type NamedEntityInMemoryEntity = InMemoryEntity & NamedEntitySchemaMixin; +export type NamedEntityInMemoryEntity = InMemoryEntity< + BaseInMemoryEntitySchema & NamedEntitySchemaMixin +>; export function namedEntitySchemaMixin( item: InMemoryEntity, ): asserts item is T & NamedEntitySchemaMixin { // @ts-expect-error - const properties: InMemoryEntity & NamedEntitySchemaMixin = { + const properties: InMemoryEntity & NamedEntitySchemaMixin = { get name() { - return this.requiredProp("name"); + return this.requiredProp("name"); }, set name(value: NameEntitySchema["name"]) { this.setProp("name", value); diff --git a/src/js/generated/RuntimeItemsSchemaMixin.ts b/src/js/generated/RuntimeItemsSchemaMixin.ts index 4b16e484..3e5c4a95 100644 --- a/src/js/generated/RuntimeItemsSchemaMixin.ts +++ b/src/js/generated/RuntimeItemsSchemaMixin.ts @@ -1,36 +1,38 @@ -import type { RuntimeItemsSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, RuntimeItemsSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type RuntimeItemsSchemaMixin = RuntimeItemsSchema; -export type RuntimeItemsInMemoryEntity = InMemoryEntity & RuntimeItemsSchemaMixin; +export type RuntimeItemsInMemoryEntity = InMemoryEntity< + BaseInMemoryEntitySchema & RuntimeItemsSchemaMixin +>; export function runtimeItemsSchemaMixin( item: InMemoryEntity, ): asserts item is T & RuntimeItemsSchemaMixin { // @ts-expect-error - const properties: InMemoryEntity & RuntimeItemsSchemaMixin = { + const properties: InMemoryEntity & RuntimeItemsSchemaMixin = { get preProcessors() { - return this.requiredProp("preProcessors"); + return this.requiredProp("preProcessors"); }, set preProcessors(value: RuntimeItemsSchema["preProcessors"]) { this.setProp("preProcessors", value); }, get postProcessors() { - return this.requiredProp("postProcessors"); + return this.requiredProp("postProcessors"); }, set postProcessors(value: RuntimeItemsSchema["postProcessors"]) { this.setProp("postProcessors", value); }, get monitors() { - return this.requiredProp("monitors"); + return this.requiredProp("monitors"); }, set monitors(value: RuntimeItemsSchema["monitors"]) { this.setProp("monitors", value); }, get results() { - return this.requiredProp("results"); + return this.requiredProp("results"); }, set results(value: RuntimeItemsSchema["results"]) { this.setProp("results", value); diff --git a/src/js/generated/TaggableSchemaMixin.ts b/src/js/generated/TaggableSchemaMixin.ts index 2c3fc837..b0efa328 100644 --- a/src/js/generated/TaggableSchemaMixin.ts +++ b/src/js/generated/TaggableSchemaMixin.ts @@ -1,18 +1,18 @@ -import type { EntityTagsSchema } from "@mat3ra/esse/dist/js/types"; +import type { BaseInMemoryEntitySchema, EntityTagsSchema } from "@mat3ra/esse/dist/js/types"; import type { InMemoryEntity } from "../entity/in_memory"; export type TaggableSchemaMixin = EntityTagsSchema; -export type TaggableInMemoryEntity = InMemoryEntity & TaggableSchemaMixin; +export type TaggableInMemoryEntity = InMemoryEntity; export function taggableSchemaMixin( item: InMemoryEntity, ): asserts item is T & TaggableSchemaMixin { // @ts-expect-error - const properties: InMemoryEntity & TaggableSchemaMixin = { + const properties: InMemoryEntity & TaggableSchemaMixin = { get tags() { - return this.prop("tags", []); + return this.prop("tags"); }, set tags(value: EntityTagsSchema["tags"]) { this.setProp("tags", value); diff --git a/src/js/index.ts b/src/js/index.ts index 0894b9f7..bf246765 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -2,6 +2,7 @@ import { ArrayWithIds, RoundedArrayWithIds } from "./ArrayWithIds"; import * as context from "./context"; import * as entity from "./entity"; import * as utils from "./utils"; +import * as validator from "./validator"; import { RoundedValueWithId, ValueWithId } from "./ValueWithId"; import { RoundedVector3D, Vector3D } from "./vector"; @@ -13,7 +14,8 @@ export { RoundedVector3D, Vector3D, }; -export { entity, context, utils }; +export { entity, context, utils, validator }; +export { JsonSchemaValidator, jsonSchemaValidator } from "./validator"; const Code = { ArrayWithIds, @@ -25,6 +27,7 @@ const Code = { entity, context, utils, + validator, }; export type CodeType = typeof Code; diff --git a/src/js/utils/graph.ts b/src/js/utils/graph.ts index 35ffde13..5f72abae 100644 --- a/src/js/utils/graph.ts +++ b/src/js/utils/graph.ts @@ -1,10 +1,8 @@ +import type { SubworkflowUnitSchema } from "@mat3ra/esse/dist/js/types"; + import { InMemoryEntity } from "../entity/in_memory"; -export type UnitEntity = InMemoryEntity & { - head: boolean; - next?: string; - flowchartId: string; -}; +export type UnitEntity = SubworkflowUnitSchema & InMemoryEntity; /** * @summary Set the head of an array of units diff --git a/src/js/validator/JsonSchemaValidator.ts b/src/js/validator/JsonSchemaValidator.ts new file mode 100644 index 00000000..fdf74964 --- /dev/null +++ b/src/js/validator/JsonSchemaValidator.ts @@ -0,0 +1,285 @@ +/** + * Standalone AJV validator for cleaning and validating JSON against JSON Schema. + * + * Why this exists (alongside esse `utils/ajv`): + * - Compose schemas with `mergeAllOf` and enforce allowlists via `additionalProperties: false` + * so callers can reject unknown / system-owned fields. + * - Optionally strip null / empty-string properties before type checks (see constructor options). + * - Map raw AJV errors into stable nested field → code objects (LIVR-style) for consumers that + * need machine-readable validation results. + * + * Distinct from `@mat3ra/esse` `utils/ajv.validateAndClean` (entity clean: null-only strip used by + * `InMemoryEntity.toJSON*`). Prefer this class/API for application validation; register host + * formats with {@link JsonSchemaValidator.registerExtensions}. + */ +import { removeEmptyAndNullProperties } from "@mat3ra/esse/dist/js/utils/removeEmptyAndNullProperties"; +import Ajv, { ErrorObject } from "ajv"; +import { AnyValidateFunction } from "ajv/dist/core"; +import addFormats from "ajv-formats"; +import addKeywords from "ajv-keywords"; +import pointer from "json-pointer"; +import mergeAllOf from "json-schema-merge-allof"; + +import addSharedFormatsAndKeywords from "./rules"; +import type { AnyObject, ValidationSchema } from "./types"; + +type ErrorResult = { + [key: string]: string | ErrorResult; +}; + +export type JsonSchemaValidatorOptions = { + /** + * Delete object properties whose value is `null` before AJV (default: `true`). + * Nested objects are walked; array elements are not removed. + */ + removeNull?: boolean; + /** + * Delete object properties whose value is `""` before AJV (default: `true`). + * Keep `false` when empty strings are intentional placeholders (e.g. flowchart links). + */ + removeEmptyStrings?: boolean; +}; + +/** + * Recursively deletes empty-string object properties (mutates in place). + */ +function removeEmptyStringProperties(obj: AnyObject): AnyObject { + if (typeof obj !== "object" || obj === null) { + return obj; + } + + if (Array.isArray(obj)) { + obj.forEach((item) => removeEmptyStringProperties(item)); + return obj; + } + + Object.keys(obj).forEach((key) => { + const value = obj[key]; + + if (value === "") { + delete obj[key]; + } else if (typeof value === "object") { + removeEmptyStringProperties(value); + } + }); + + return obj; +} + +export class JsonSchemaValidator { + private readonly ajv: Ajv; + + private readonly removeNull: boolean; + + private readonly removeEmptyStrings: boolean; + + private readonly formatCodeMap: Record = { + email: "WRONG_EMAIL", + "custom-email": "WRONG_EMAIL", + "custom-username": "WRONG_USERNAME", + "custom-phone": "WRONG_PHONE", + "custom-password": "WRONG_PASSWORD", + }; + + private readonly keywordsCodeMap: Record = { + required: "REQUIRED", + type: "FORMAT_ERROR", + equalToField: "FIELDS_NOT_EQUAL", + minLength: "TOO_SHORT", + maxLength: "TOO_LONG", + isoDate: "WRONG_DATE", + }; + + constructor({ removeNull = true, removeEmptyStrings = true }: JsonSchemaValidatorOptions = {}) { + this.removeNull = removeNull; + this.removeEmptyStrings = removeEmptyStrings; + + this.ajv = new Ajv({ + allErrors: true, + useDefaults: true, + discriminator: true, + removeAdditional: true, + coerceTypes: true, + allowUnionTypes: true, + // Esse / workflow schemas: json-schema-to-typescript-only keywords, and `default` inside + // discriminated `oneOf` branches where Ajv still treats nested rules as composite (see + // assignDefault + compositeRule). strictSchema would fail compilation for those valid schemas. + strictSchema: false, + }); + + addFormats(this.ajv); + addKeywords(this.ajv); + addSharedFormatsAndKeywords(this.ajv); + } + + /** + * Register host-specific AJV formats/keywords (e.g. web-app password/phone) on this instance. + */ + registerExtensions(register: (ajvInstance: Ajv) => void): void { + register(this.ajv); + } + + private getCompiledValidator(jsonSchema: ValidationSchema): AnyValidateFunction { + let validate = this.ajv.getSchema(jsonSchema.$id); + + if (!validate) { + const schema = mergeAllOf(jsonSchema, { ignoreAdditionalProperties: true }); + this.ajv.addSchema(schema, jsonSchema.$id); + validate = this.ajv.getSchema(jsonSchema.$id); + } + + if (!validate) { + throw new Error("JSONSchemasInterface AJV validator error"); + } + + return validate; + } + + /** + * Simplify AJV errors into LIVR-inspired field → code maps for API responses. + * Inspired by https://github.com/koorchik/LIVR + * + * @example of raw AJV errors: + * [ + * { + * "instancePath": "", + * "schemaPath": "#/required", + * "keyword": "required", + * "params": { + * "missingProperty": "intent" + * }, + * "message": "must have required property 'intent'" + * }, + * { + * "instancePath": "/user", + * "schemaPath": "#/properties/user/required", + * "keyword": "required", + * "params": { + * "missingProperty": "username" + * }, + * "message": "must have required property 'username'" + * }, + * { + * "instancePath": "/user", + * "schemaPath": "#/properties/user/required", + * "keyword": "required", + * "params": { + * "missingProperty": "fullname" + * }, + * "message": "must have required property 'fullname'" + * }, + * { + * "instancePath": "/user/termsAccepted", + * "schemaPath": "#/properties/user/properties/termsAccepted/type", + * "keyword": "type", + * "params": { + * "type": "boolean" + * }, + * "message": "must be boolean" + * }, + * { + * "instancePath": "/user/termsAccepted", + * "schemaPath": "#/properties/user/properties/termsAccepted/enum", + * "keyword": "enum", + * "params": { + * "allowedValues": [ + * true + * ] + * }, + * "message": "must be equal to one of the allowed values" + * }, + * { + * "instancePath": "/user/password", + * "schemaPath": "#/properties/user/properties/password/equalToField", + * "keyword": "equalToField", + * "params": {}, + * "message": "should be equal to the value of \"passwordConfirmation\"" + * }, + * { + * "instancePath": "/user/passwordConfirmation", + * "schemaPath": "#/properties/user/properties/passwordConfirmation/equalToField", + * "keyword": "equalToField", + * "params": {}, + * "message": "should be equal to the value of \"password\"" + * } + * ] + * + * @example of the same errors after transformation: + * { + * "intent": "REQUIRED", + * "user": { + * "username": "REQUIRED", + * "fullname": "REQUIRED", + * "termsAccepted": "FORMAT_ERROR", + * "password": "FIELDS_NOT_EQUAL", + * "passwordConfirmation": "FIELDS_NOT_EQUAL" + * } + * } + */ + transformErrors(errors?: ErrorObject[] | null): ErrorResult | undefined { + if (!errors) { + return undefined; + } + + const result: ErrorResult = {}; + + errors.forEach((error) => { + const code = + error.keyword === "format" + ? this.formatCodeMap[error.params.format] || "FORMAT_ERROR" + : this.keywordsCodeMap[error.keyword] || "FORMAT_ERROR"; + + const instancePath = error.params.missingProperty + ? `${error.instancePath}/${error.params.missingProperty}` + : error.instancePath; + + pointer.set(result, instancePath, code); + }); + + /** + * JSON.parse/JSON.stringify is required to fix <1 empty item> issue. + * @example + * pointer.set(result, "/nested/prop2/0", "ERROR_1"); + * pointer.set(result, "/nested/prop2/2", "ERROR_2"); + * + * without JSON.parse/JSON.stringify: + * result.prop2 === ["ERROR_1", <1 empty item>, "ERROR_2"] + * + * with JSON.parse/JSON.stringify: + * result.prop2 === ["ERROR_1", null, "ERROR_2"] + */ + return JSON.parse(JSON.stringify(result)); + } + + /** + * Validates and cleans data against the schema. + * Optionally drops null / empty-string properties (constructor options), then AJV removeAdditional. + */ + validateAndClean(data: AnyObject, jsonSchema: ValidationSchema) { + const validated = { ...data }; + + if (this.removeNull) { + removeEmptyAndNullProperties(validated); + } + + if (this.removeEmptyStrings) { + removeEmptyStringProperties(validated); + } + + const compiledValidator = this.getCompiledValidator({ + ...jsonSchema, + additionalProperties: false, + }); + const isValid = compiledValidator(validated); + + return { + isValid, + errors: this.transformErrors(compiledValidator.errors || undefined), + rawErrors: compiledValidator.errors, + validated, + }; + } +} + +/** Shared singleton — hosts register app formats on this instance (null + "" strip on). */ +export const jsonSchemaValidator = new JsonSchemaValidator(); diff --git a/src/js/validator/index.ts b/src/js/validator/index.ts new file mode 100644 index 00000000..202c720a --- /dev/null +++ b/src/js/validator/index.ts @@ -0,0 +1,4 @@ +export { JsonSchemaValidator, jsonSchemaValidator } from "./JsonSchemaValidator"; +export type { JsonSchemaValidatorOptions } from "./JsonSchemaValidator"; +export type { ErrorObject, SchemaObject } from "ajv"; +export type { AnyObject, ValidationSchema } from "./types"; diff --git a/src/js/validator/rules/equalTo.ts b/src/js/validator/rules/equalTo.ts new file mode 100644 index 00000000..be5b8add --- /dev/null +++ b/src/js/validator/rules/equalTo.ts @@ -0,0 +1,17 @@ +import Ajv from "ajv"; + +export default function addEqualToFieldKeyword(ajv: Ajv) { + ajv.addKeyword({ + keyword: "equalToField", + type: "string", // Applies to string fields + schemaType: "string", // The schema for this keyword should be a string (field name) + validate(schema, data, _parentSchema, dataCxt) { + const otherFieldValue = dataCxt?.parentData[schema]; + return data === otherFieldValue; + }, + errors: true, // Enable error handling + error: { + message: (cxt) => `should be equal to the value of "${cxt.schema}"`, + }, + }); +} diff --git a/src/js/validator/rules/index.ts b/src/js/validator/rules/index.ts new file mode 100644 index 00000000..18ac7083 --- /dev/null +++ b/src/js/validator/rules/index.ts @@ -0,0 +1,15 @@ +import addJsonSchemaToTypescriptKeywords from "@mat3ra/esse/dist/js/utils/ajvKeywords/jsonSchemaToTypescriptKeywords"; +import type Ajv from "ajv"; + +import addEqualToFieldKeyword from "./equalTo"; +import addIsoDateKeyword from "./isoDate"; + +/** + * Shared AJV formats/keywords for the standalone validator. + * App-specific formats (password, phone, username, email) are registered by the host. + */ +export default function addSharedFormatsAndKeywords(ajv: Ajv): void { + addJsonSchemaToTypescriptKeywords(ajv); + addEqualToFieldKeyword(ajv); + addIsoDateKeyword(ajv); +} diff --git a/src/js/validator/rules/isoDate.ts b/src/js/validator/rules/isoDate.ts new file mode 100644 index 00000000..4d83b5d0 --- /dev/null +++ b/src/js/validator/rules/isoDate.ts @@ -0,0 +1,37 @@ +import Ajv from "ajv"; +import { AnySchemaObject, DataValidationCxt } from "ajv/dist/types"; + +export default function addIsoDateKeyword(ajv: Ajv) { + ajv.addKeyword({ + keyword: "isoDate", + type: "string", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate( + _schema: boolean, + data: unknown, + _parentSchema?: AnySchemaObject, + dataCxt?: DataValidationCxt, + ) { + if (data === null || typeof data === "undefined") { + return true; + } + + if (typeof data !== "string") { + return false; + } + + const isValid = !Number.isNaN(Date.parse(data)); + + if (isValid && dataCxt) { + dataCxt.parentData[dataCxt.parentDataProperty] = new Date(data); + } + + return isValid; + }, + errors: false, + modifying: true, + metaSchema: { + type: "boolean", + }, + }); +} diff --git a/src/js/validator/types.ts b/src/js/validator/types.ts new file mode 100644 index 00000000..6a32a506 --- /dev/null +++ b/src/js/validator/types.ts @@ -0,0 +1,6 @@ +import type { SchemaObject } from "ajv"; + +export type ValidationSchema = SchemaObject & { $id: string }; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AnyObject = Record; diff --git a/tests/js/generateSchemaMixin.tests.ts b/tests/js/generateSchemaMixin.tests.ts index 92f0e371..5ebcc22f 100644 --- a/tests/js/generateSchemaMixin.tests.ts +++ b/tests/js/generateSchemaMixin.tests.ts @@ -209,6 +209,28 @@ describe("generateSchemaMixin Tests", function () { }); }); + describe("generateShemaMixin - InMemoryEntity type alias", () => { + it("should type the entity as InMemoryEntity", () => { + const outputPath = path.join(tempDir, "BankableSchemaMixin.ts"); + const outputPaths = { + "property/holder": outputPath, + }; + + generateShemaMixin(mockSchemas, outputPaths); + + const generatedCode = fs.readFileSync(outputPath, "utf-8"); + expect(generatedCode).to.include( + "import type { BaseInMemoryEntitySchema, PropertyHolder }", + ); + expect(generatedCode).to.include( + "export type BankableInMemoryEntity = InMemoryEntity;", + ); + expect(generatedCode).to.not.include( + "export type BankableInMemoryEntity = InMemoryEntity & BankableSchemaMixin", + ); + }); + }); + describe("generateShemaMixin - Omit Type Generation", () => { it("should not use Omit when skipFields is undefined", () => { const outputPath = path.join(tempDir, "NoSkipFieldsSchemaMixin.ts"); diff --git a/tests/js/inMemoryEntitySetBase.tests.ts b/tests/js/inMemoryEntitySetBase.tests.ts new file mode 100644 index 00000000..6221d3a0 --- /dev/null +++ b/tests/js/inMemoryEntitySetBase.tests.ts @@ -0,0 +1,44 @@ +/* eslint-disable no-unused-expressions */ +import type { BaseInMemoryEntitySchema, EntitySetSchema } from "@mat3ra/esse/dist/js/types"; +import { expect } from "chai"; + +import { InMemoryEntity } from "../../src/js/entity/in_memory"; +import { + type InMemoryEntitySetBaseMixin, + inMemoryEntitySetBaseMixin, +} from "../../src/js/entity/set/InMemoryEntitySetBaseMixin"; + +type WiderEntitySetSchema = BaseInMemoryEntitySchema & + EntitySetSchema & { + owner?: string; + }; + +/** + * Mirrors web-app CoreEntitySet: schema-generic class + interface merge of the + * mixin shape (not InMemoryEntitySetBase, which bakes in a narrower `_json`). + */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface WiderEntitySet extends InMemoryEntitySetBaseMixin {} + +class WiderEntitySet extends InMemoryEntity {} + +inMemoryEntitySetBaseMixin(WiderEntitySet.prototype); + +describe("InMemoryEntitySetBaseMixin", () => { + it("merges into a schema-generic InMemoryEntity subclass without _json conflict", () => { + const entity = new WiderEntitySet({ + _id: "set-1", + isEntitySet: true, + entitySetType: "unordered", + owner: "account-1", + }); + + expect(entity.isEntitySet).to.equal(true); + expect(entity.entitySetType).to.equal("unordered"); + expect(entity._json.owner).to.equal("account-1"); + expect(entity.toJSONForInclusionInEntity()).to.deep.equal({ + _id: "set-1", + type: undefined, + }); + }); +}); diff --git a/tests/js/in_memory.tests.ts b/tests/js/in_memory.tests.ts index 6037b52b..b647dbb4 100644 --- a/tests/js/in_memory.tests.ts +++ b/tests/js/in_memory.tests.ts @@ -1,6 +1,7 @@ /* eslint-disable no-unused-expressions */ import { JSONSchema } from "@mat3ra/esse/dist/js/esse/utils"; import inMemoryEntitySchema from "@mat3ra/esse/dist/js/schema/in_memory_entity/base.json"; +import type { BaseInMemoryEntitySchema } from "@mat3ra/esse/dist/js/types"; import { expect } from "chai"; import { InMemoryEntity } from "../../src/js/entity/in_memory"; @@ -19,53 +20,100 @@ function validateEntity(entity: DerivedInMemoryEntity) { } describe("InMemoryEntity", () => { - const obj = { - a: "b", - name: "test", + const obj: BaseInMemoryEntitySchema = { + _id: "123", + schemaVersion: "2022.8.16", }; it("can be created", () => { - const empty = new InMemoryEntity(); - // eslint-disable-next-line no-unused-expressions - expect(empty).to.exist; + const empty = new InMemoryEntity({}); const entity = new InMemoryEntity(obj); - // eslint-disable-next-line no-unused-expressions + + expect(empty).to.exist; expect(entity).to.exist; }); it("prop gets props", () => { const entity = new InMemoryEntity(obj); - expect(entity.prop("a")).to.equal("b"); - expect(entity.prop("b")).to.equal(undefined); - expect(entity.prop("b", "a")).to.equal("a"); + expect(entity._id).to.equal(obj._id); + expect(entity.schemaVersion).to.equal(obj.schemaVersion); + expect(entity.systemName).to.equal(undefined); }); it("setProp sets props", () => { const entity = new InMemoryEntity(obj); - expect(entity.prop("a")).to.equal("b"); - entity.setProp("b", "c"); - expect(entity.prop("b")).to.equal("c"); - entity.setProp("a", "d"); - expect(entity.prop("a")).to.equal("d"); + entity.setProp("systemName", "b"); + expect(entity.systemName).to.equal("b"); + entity.setProp("slug", "c"); + expect(entity.slug).to.equal("c"); }); it("unsetProp unsets props", () => { - console.log("obj", obj); const entity = new InMemoryEntity(obj); - console.log("obj", obj); - expect(entity.prop("a")).to.equal("b"); - entity.unsetProp("a"); - // eslint-disable-next-line no-unused-expressions - expect(entity.prop("a")).not.to.exist; + expect(entity._id).to.equal(obj._id); + entity.unsetProp("_id"); + expect(entity._id).to.equal(undefined); + }); + + it("getAsEntityReference requires _id but not slug", () => { + const withoutSlug = new InMemoryEntity({ _id: "123" }); + expect(withoutSlug.getAsEntityReference()).to.deep.equal({ + _id: "123", + cls: "InMemoryEntity", + }); + expect(withoutSlug.getAsEntityReference(true)).to.deep.equal({ _id: "123" }); + + const withSlug = new InMemoryEntity({ _id: "123", slug: "total-energy" }); + expect(withSlug.getAsEntityReference()).to.deep.equal({ + _id: "123", + slug: "total-energy", + cls: "InMemoryEntity", + }); + + const missingId = new InMemoryEntity({}); + expect(() => missingId.getAsEntityReference()).to.throw("ENTITY_REFERENCE_ERROR"); }); it("toJSON converts to JSON", () => { - const entity = new DerivedInMemoryEntity({ _id: "123", type: "type" }); + const entity = new DerivedInMemoryEntity({ + _id: "123", + additional: "additional", + } as BaseInMemoryEntitySchema); expect(JSON.stringify(entity.toJSON())).to.be.equal( JSON.stringify({ _id: "123", schemaVersion: "2022.8.16" }), ); }); + it("toJSON ignores JSON.stringify property-key argument so fields are not stripped", () => { + // JSON.stringify calls value.toJSON(key) when serializing a nested object. + // systemTeams.owner → toJSON("owner") must not omit the entity's owner field. + const entity = new DerivedInMemoryEntity({ + _id: "123", + slug: "owner", + } as BaseInMemoryEntitySchema); + + expect(entity.toJSON("owner" as unknown as (keyof BaseInMemoryEntitySchema)[])).to.include({ + _id: "123", + slug: "owner", + }); + + const wrapped = { owner: entity }; + const parsed = JSON.parse(JSON.stringify(wrapped)); + expect(parsed.owner).to.include({ _id: "123", slug: "owner" }); + }); + + it("toJSON still omits when given an explicit exclude array", () => { + const entity = new DerivedInMemoryEntity({ + _id: "123", + slug: "owner", + } as BaseInMemoryEntitySchema); + + expect(entity.toJSON(["slug"])).to.deep.equal({ + _id: "123", + schemaVersion: "2022.8.16", + }); + }); + it("jsonSchema returns correct registered schema", async () => { expect(DerivedInMemoryEntity.jsonSchema).to.be.an("object"); expect(DerivedInMemoryEntity.jsonSchema).to.have.nested.property("properties._id"); // check mix schemas @@ -73,7 +121,10 @@ describe("InMemoryEntity", () => { it("jsonSchema validate", async () => { const validEntity = new DerivedInMemoryEntity({ _id: "123", slug: "slug" }); - const invalidEntity = new DerivedInMemoryEntity({ _id: "123", slug: ["slug"] }); + const invalidEntity = new DerivedInMemoryEntity({ + _id: "123", + slug: ["slug"], + } as unknown as BaseInMemoryEntitySchema); expect(validateEntity(validEntity)).to.be.true; expect(validateEntity(invalidEntity)).to.be.false; @@ -85,7 +136,7 @@ describe("InMemoryEntity", () => { slug: "slug", additional: "additional", }; - const cleanConfig = new DerivedInMemoryEntity().clean({ ...config }); + const cleanConfig = new DerivedInMemoryEntity({}).clean({ ...config }); expect(cleanConfig).to.be.deep.equal({ _id: "123", diff --git a/tests/js/validator.tests.ts b/tests/js/validator.tests.ts new file mode 100644 index 00000000..16ae34d6 --- /dev/null +++ b/tests/js/validator.tests.ts @@ -0,0 +1,97 @@ +import { expect } from "chai"; + +import { + JsonSchemaValidator, + jsonSchemaValidator, +} from "../../src/js/validator/JsonSchemaValidator"; + +const cleanSchema = { + $id: "test/code-validator-clean", + type: "object", + properties: { + name: { type: "string" }, + slug: { type: "string" }, + description: { type: "string" }, + }, + required: ["name"], + additionalProperties: false, +}; + +describe("JsonSchemaValidator.transformErrors", () => { + it("maps AJV required and format errors to LIVR-style codes", () => { + const transformed = jsonSchemaValidator.transformErrors([ + { + instancePath: "", + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "intent" }, + message: "must have required property 'intent'", + }, + { + instancePath: "/user/password", + schemaPath: "#/properties/user/properties/password/format", + keyword: "format", + params: { format: "custom-password" }, + message: 'must match format "custom-password"', + }, + ] as never); + + expect(transformed).to.deep.equal({ + intent: "REQUIRED", + user: { + password: "WRONG_PASSWORD", + }, + }); + }); +}); + +describe("JsonSchemaValidator.validateAndClean", () => { + it("strips nulls, empty strings, and additional properties by default", () => { + const data = { + name: "Si", + slug: null, + description: "", + owner: { _id: "x" }, + }; + + const result = jsonSchemaValidator.validateAndClean(data, cleanSchema); + + expect(result.isValid).to.equal(true); + expect(result.validated).to.deep.equal({ name: "Si" }); + }); + + it("keeps empty strings when removeEmptyStrings is false", () => { + const validator = new JsonSchemaValidator({ + removeNull: true, + removeEmptyStrings: false, + }); + const schema = { + ...cleanSchema, + $id: "test/code-validator-keep-empty", + }; + + const result = validator.validateAndClean( + { name: "Si", slug: null, description: "" }, + schema, + ); + + expect(result.isValid).to.equal(true); + expect(result.validated).to.deep.equal({ name: "Si", description: "" }); + }); + + it("leaves null keys for AJV when removeNull is false", () => { + // AJV coerceTypes may turn null → "" for string fields; the option only skips our pre-strip. + const validator = new JsonSchemaValidator({ + removeNull: false, + removeEmptyStrings: true, + }); + const schema = { + ...cleanSchema, + $id: "test/code-validator-keep-null-keys", + }; + + const result = validator.validateAndClean({ name: "Si", slug: null }, schema); + + expect(Object.prototype.hasOwnProperty.call(result.validated, "slug")).to.equal(true); + }); +});