From 9d069dfd2e51bef2282999804677313461331fde Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Wed, 3 Jun 2026 01:13:03 +0300 Subject: [PATCH 1/5] chore: generic InMemoryEntity --- dist/js/entity/in_memory.d.ts | 62 ++++------ dist/js/entity/in_memory.js | 60 +++------ dist/js/entity/index.d.ts | 3 +- dist/js/entity/index.js | 4 +- dist/js/entity/mixins/HasMetadataMixin.d.ts | 8 +- dist/js/entity/mixins/HasScopeTrackMixin.d.ts | 5 +- dist/js/entity/mixins/HasScopeTrackMixin.js | 3 +- .../ImportantSettingsProviderMixin.d.ts | 17 --- .../mixins/ImportantSettingsProviderMixin.js | 25 ---- .../set/InMemoryEntitySetBaseMixin.d.ts | 7 +- .../js/entity/set/InMemoryEntitySetMixin.d.ts | 2 +- dist/js/entity/set/factory.d.ts | 6 - dist/js/entity/set/factory.js | 12 -- dist/js/generateSchemaMixin.js | 4 +- .../HasConsistencyChecksSchemaMixin.js | 2 +- dist/js/generated/TaggableSchemaMixin.js | 2 +- dist/js/utils/graph.d.ts | 7 +- src/js/entity/in_memory.ts | 115 +++++++----------- src/js/entity/index.ts | 2 - .../mixins/HasConsistencyChecksMixin.ts | 2 +- src/js/entity/mixins/HasMetadataMixin.ts | 17 +-- src/js/entity/mixins/HasScopeTrackMixin.ts | 8 +- .../mixins/ImportantSettingsProviderMixin.ts | 50 -------- src/js/entity/mixins/NamedEntityMixin.ts | 2 +- src/js/entity/mixins/RuntimeItemsMixin.ts | 2 +- src/js/entity/mixins/TaggableMixin.ts | 2 +- src/js/entity/set/InMemoryEntityInSetMixin.ts | 2 +- .../entity/set/InMemoryEntitySetBaseMixin.ts | 16 ++- src/js/entity/set/factory.ts | 14 --- .../OrderedInMemoryEntityInSetMixin.ts | 6 +- .../ordered/OrderedInMemoryEntitySetMixin.ts | 8 +- src/js/generateSchemaMixin.ts | 4 +- src/js/generated/DefaultableSchemaMixin.ts | 4 +- .../HasConsistencyChecksSchemaMixin.ts | 8 +- src/js/generated/HasDescriptionSchemaMixin.ts | 6 +- src/js/generated/InSetSchemaMixin.ts | 4 +- src/js/generated/NamedEntitySchemaMixin.ts | 4 +- src/js/generated/RuntimeItemsSchemaMixin.ts | 10 +- src/js/generated/TaggableSchemaMixin.ts | 4 +- src/js/utils/graph.ts | 8 +- tests/js/in_memory.tests.ts | 50 ++++---- 41 files changed, 210 insertions(+), 367 deletions(-) delete mode 100644 dist/js/entity/mixins/ImportantSettingsProviderMixin.d.ts delete mode 100644 dist/js/entity/mixins/ImportantSettingsProviderMixin.js delete mode 100644 dist/js/entity/set/factory.d.ts delete mode 100644 dist/js/entity/set/factory.js delete mode 100644 src/js/entity/mixins/ImportantSettingsProviderMixin.ts delete mode 100644 src/js/entity/set/factory.ts diff --git a/dist/js/entity/in_memory.d.ts b/dist/js/entity/in_memory.d.ts index 8c7ef904..63a9e83e 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,49 @@ 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: S); + 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 */ - 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 +71,18 @@ export declare class InMemoryEntity implements BaseInMemoryEntitySchema { * @returns identifying data */ getAsEntityReference(byIdOnly: true): { - _id: string; + _id: NonNullable; }; 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..24adab28 100644 --- a/dist/js/entity/in_memory.js +++ b/dist/js/entity/in_memory.js @@ -60,23 +60,17 @@ 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); - } + 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,7 +108,10 @@ 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; } /** @@ -206,7 +203,7 @@ class InMemoryEntity { return this.constructor.name; } getAsEntityReference(byIdOnly = false) { - if (!this.id) { + if (!this._id || !this.slug) { throw new EntityError({ code: ValidationErrorCode.ENTITY_REFERENCE_ERROR, details: { @@ -216,64 +213,41 @@ class InMemoryEntity { }); } if (byIdOnly) { - return { _id: this.id }; + return { _id: this._id }; } return { - _id: this.id, + _id: this._id, 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/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/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/set/InMemoryEntitySetBaseMixin.d.ts b/dist/js/entity/set/InMemoryEntitySetBaseMixin.d.ts index 0b937881..7eb0cf66 100644 --- a/dist/js/entity/set/InMemoryEntitySetBaseMixin.d.ts +++ b/dist/js/entity/set/InMemoryEntitySetBaseMixin.d.ts @@ -1,19 +1,20 @@ -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; +declare function schemaMixin(item: E): InMemoryEntity & EntitySetSchema; type EntitySetBaseMethodsDescriptor = { toJSONForInclusionInEntity(): { _id: string; type: string; }; }; -declare function methodsMixin(item: E & EntitySetSchema): InMemoryEntity & EntitySetSchema & EntitySetBaseMethodsDescriptor; +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; 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/generateSchemaMixin.js b/dist/js/generateSchemaMixin.js index 44a775ce..4e216135 100644 --- a/dist/js/generateSchemaMixin.js +++ b/dist/js/generateSchemaMixin.js @@ -97,14 +97,14 @@ function generateMixinFunction(schema, schemaName, mixinTypeName, entityTypeName 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/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/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/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/src/js/entity/in_memory.ts b/src/js/entity/in_memory.ts index c4d3065a..48c5c496 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,31 @@ 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); - } + constructor(config: S) { + 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 +87,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 +95,7 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { /** * @summary Remove a prop */ - unsetProp(name: string) { + unsetProp(name: keyof S) { delete this._json[name]; } @@ -105,25 +103,28 @@ 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 */ - toJSON(exclude: string[] = []) { + toJSON(exclude: (keyof S)[] = []) { return (this.constructor as typeof InMemoryEntity)._isDeepCloneRequired ? this.toJSONSafe(exclude) : this.toJSONQuick(exclude); } - toJSONSafe(exclude: string[] = []): AnyObject { + toJSONSafe(exclude: (keyof S)[] = []): S { return this.clean(deepClone(omit(this._json, exclude))); } - toJSONQuick(exclude: string[] = []): AnyObject { + toJSONQuick(exclude: (keyof S)[] = []): S { return this.clean(clone(omit(this._json, exclude))); } @@ -140,15 +141,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 +174,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 +217,12 @@ 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) { - if (!this.id) { + if (!this._id || !this.slug) { throw new EntityError({ code: ValidationErrorCode.ENTITY_REFERENCE_ERROR, details: { @@ -232,74 +233,52 @@ export class InMemoryEntity implements BaseInMemoryEntitySchema { } if (byIdOnly) { - return { _id: this.id }; + return { _id: this._id }; } return { - _id: this.id, + _id: this._id, 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]; + } as Required; } // 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/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/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..2cbcf9e3 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; }, 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..2776fdc1 100644 --- a/src/js/generateSchemaMixin.ts +++ b/src/js/generateSchemaMixin.ts @@ -121,7 +121,7 @@ function generateMixinFunction( 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/DefaultableSchemaMixin.ts b/src/js/generated/DefaultableSchemaMixin.ts index cfde55d1..e83265d4 100644 --- a/src/js/generated/DefaultableSchemaMixin.ts +++ b/src/js/generated/DefaultableSchemaMixin.ts @@ -10,9 +10,9 @@ 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..95ac06ac 100644 --- a/src/js/generated/HasConsistencyChecksSchemaMixin.ts +++ b/src/js/generated/HasConsistencyChecksSchemaMixin.ts @@ -10,12 +10,10 @@ 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..734fbbc9 100644 --- a/src/js/generated/HasDescriptionSchemaMixin.ts +++ b/src/js/generated/HasDescriptionSchemaMixin.ts @@ -10,15 +10,15 @@ 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/InSetSchemaMixin.ts b/src/js/generated/InSetSchemaMixin.ts index 40f37f4e..88c5b83f 100644 --- a/src/js/generated/InSetSchemaMixin.ts +++ b/src/js/generated/InSetSchemaMixin.ts @@ -10,9 +10,9 @@ 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..7b867577 100644 --- a/src/js/generated/NamedEntitySchemaMixin.ts +++ b/src/js/generated/NamedEntitySchemaMixin.ts @@ -10,9 +10,9 @@ 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..25e7a803 100644 --- a/src/js/generated/RuntimeItemsSchemaMixin.ts +++ b/src/js/generated/RuntimeItemsSchemaMixin.ts @@ -10,27 +10,27 @@ 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..2697a339 100644 --- a/src/js/generated/TaggableSchemaMixin.ts +++ b/src/js/generated/TaggableSchemaMixin.ts @@ -10,9 +10,9 @@ 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/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/tests/js/in_memory.tests.ts b/tests/js/in_memory.tests.ts index 6037b52b..b7fc3017 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,48 +20,46 @@ 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("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" }), ); @@ -73,7 +72,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 +87,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", From e24d82f9b67647b914fe7d0180278a9251c5cc08 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Fri, 19 Jun 2026 18:01:50 +0300 Subject: [PATCH 2/5] Add generated BankableSchemaMixin for system/bankable schema. Co-authored-by: Cursor --- dist/js/generated/BankableSchemaMixin.d.ts | 5 ++++ dist/js/generated/BankableSchemaMixin.js | 21 ++++++++++++++++ scripts/generate-mixins.ts | 1 + src/js/generated/BankableSchemaMixin.ts | 29 ++++++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 dist/js/generated/BankableSchemaMixin.d.ts create mode 100644 dist/js/generated/BankableSchemaMixin.js create mode 100644 src/js/generated/BankableSchemaMixin.ts diff --git a/dist/js/generated/BankableSchemaMixin.d.ts b/dist/js/generated/BankableSchemaMixin.d.ts new file mode 100644 index 00000000..35ae8ff5 --- /dev/null +++ b/dist/js/generated/BankableSchemaMixin.d.ts @@ -0,0 +1,5 @@ +import type { BankableSchema } from "@mat3ra/esse/dist/js/types"; +import type { InMemoryEntity } from "../entity/in_memory"; +export type BankableSchemaMixin = BankableSchema; +export type BankableInMemoryEntity = InMemoryEntity & BankableSchemaMixin; +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..9726765c --- /dev/null +++ b/dist/js/generated/BankableSchemaMixin.js @@ -0,0 +1,21 @@ +"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); + }, + get hash() { + return this.requiredProp("hash"); + }, + set hash(value) { + this.setProp("hash", value); + }, + }; + Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); +} diff --git a/scripts/generate-mixins.ts b/scripts/generate-mixins.ts index 0108f2fc..d3ac39e8 100644 --- a/scripts/generate-mixins.ts +++ b/scripts/generate-mixins.ts @@ -24,6 +24,7 @@ const SKIP_FIELDS: string[] = []; * Output file paths for each schema */ const OUTPUT_PATHS = { + "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/generated/BankableSchemaMixin.ts b/src/js/generated/BankableSchemaMixin.ts new file mode 100644 index 00000000..68f634a6 --- /dev/null +++ b/src/js/generated/BankableSchemaMixin.ts @@ -0,0 +1,29 @@ +import type { BankableSchema } from "@mat3ra/esse/dist/js/types"; + +import type { InMemoryEntity } from "../entity/in_memory"; + +export type BankableSchemaMixin = BankableSchema; + +export type BankableInMemoryEntity = InMemoryEntity & BankableSchemaMixin; + +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); + }, + get hash() { + return this.requiredProp("hash"); + }, + set hash(value: BankableSchema["hash"]) { + this.setProp("hash", value); + }, + }; + + Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); +} From 62fa688445923bcb412b362e8a41b365bdbbad29 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Fri, 19 Jun 2026 18:07:17 +0300 Subject: [PATCH 3/5] Add bankableEntityMixin combining schema fields and hash calculation. Co-authored-by: Cursor --- dist/js/entity/mixins/BankableEntityMixin.d.ts | 7 +++++++ dist/js/entity/mixins/BankableEntityMixin.js | 9 +++++++++ src/js/entity/mixins/BankableEntityMixin.ts | 15 +++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 dist/js/entity/mixins/BankableEntityMixin.d.ts create mode 100644 dist/js/entity/mixins/BankableEntityMixin.js create mode 100644 src/js/entity/mixins/BankableEntityMixin.ts 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/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); +} From caba90ad8fa85b7fbde9190031e2ae23f5f02547 Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Tue, 23 Jun 2026 16:03:34 +0300 Subject: [PATCH 4/5] chore: bankable + hashed mixins --- dist/js/entity/mixins/HashedEntityMixin.d.ts | 3 +- dist/js/entity/mixins/HashedEntityMixin.js | 2 + dist/js/generated/BankableSchemaMixin.js | 6 -- dist/js/generated/HashedSchemaMixin.d.ts | 5 ++ dist/js/generated/HashedSchemaMixin.js | 15 +++++ package-lock.json | 67 ++++++-------------- package.json | 8 +-- scripts/generate-mixins.ts | 1 + src/js/entity/mixins/HashedEntityMixin.ts | 7 +- src/js/generated/BankableSchemaMixin.ts | 6 -- src/js/generated/HashedSchemaMixin.ts | 23 +++++++ 11 files changed, 76 insertions(+), 67 deletions(-) create mode 100644 dist/js/generated/HashedSchemaMixin.d.ts create mode 100644 dist/js/generated/HashedSchemaMixin.js create mode 100644 src/js/generated/HashedSchemaMixin.ts 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/generated/BankableSchemaMixin.js b/dist/js/generated/BankableSchemaMixin.js index 9726765c..7350b8ea 100644 --- a/dist/js/generated/BankableSchemaMixin.js +++ b/dist/js/generated/BankableSchemaMixin.js @@ -10,12 +10,6 @@ function bankableSchemaMixin(item) { set exabyteId(value) { this.setProp("exabyteId", value); }, - 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/HashedSchemaMixin.d.ts b/dist/js/generated/HashedSchemaMixin.d.ts new file mode 100644 index 00000000..ad58896b --- /dev/null +++ b/dist/js/generated/HashedSchemaMixin.d.ts @@ -0,0 +1,5 @@ +import type { HashedSchema } from "@mat3ra/esse/dist/js/types"; +import type { InMemoryEntity } from "../entity/in_memory"; +export type HashedSchemaMixin = HashedSchema; +export type HashedInMemoryEntity = InMemoryEntity & HashedSchemaMixin; +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/package-lock.json b/package-lock.json index 98966b76..e48526c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "@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": "file:../esse/mat3ra-esse-0.0.0.tgz", "@mat3ra/tsconfig": "2024.6.3-0", "@types/chai": "^4.3.20", "@types/crypto-js": "^4.2.2", @@ -68,7 +68,7 @@ "node": ">=12.0.0" }, "peerDependencies": { - "@mat3ra/esse": "*" + "@mat3ra/esse": "file:../esse/mat3ra-esse-0.0.0.tgz" } }, "node_modules/@ampproject/remapping": { @@ -2781,9 +2781,9 @@ "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": "file:../esse/mat3ra-esse-0.0.0.tgz", + "integrity": "sha512-yRdNF1ECrhkiBa4SXVBiiVP2acXSN6uBZiRhopD0cVuL0vNwxHX+K3rvQ5MZdggn5zhCWJDL/YtOq0SYx98xTQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3905,7 +3905,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 +4258,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 +4280,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 +4491,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", @@ -5443,21 +5443,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 +5738,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", @@ -6202,7 +6173,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" }, @@ -6492,7 +6463,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 +6822,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 +6914,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 +6959,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 +7028,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" } @@ -8421,7 +8392,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 +9039,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" }, @@ -9243,7 +9214,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" }, @@ -10213,7 +10184,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" }, diff --git a/package.json b/package.json index 4a175666..fe3774eb 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "license": "Apache-2.0", "homepage": "https://github.com/Exabyte-io/code.js", "dependencies": { + "@types/mathjs": "^5.0.1", "crypto-js": "^4.2.0", "js-yaml": "^4.1.0", "json-schema": "^0.4.0", @@ -41,8 +42,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,7 +55,7 @@ "@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": "file:../esse/mat3ra-esse-0.0.0.tgz", "@mat3ra/tsconfig": "2024.6.3-0", "@types/chai": "^4.3.20", "@types/crypto-js": "^4.2.2", @@ -87,7 +87,7 @@ "typescript": "^5.6.0" }, "peerDependencies": { - "@mat3ra/esse": "*" + "@mat3ra/esse": "file:../esse/mat3ra-esse-0.0.0.tgz" }, "engines": { "node": ">=12.0.0" diff --git a/scripts/generate-mixins.ts b/scripts/generate-mixins.ts index d3ac39e8..45a848f0 100644 --- a/scripts/generate-mixins.ts +++ b/scripts/generate-mixins.ts @@ -24,6 +24,7 @@ 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", 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/generated/BankableSchemaMixin.ts b/src/js/generated/BankableSchemaMixin.ts index 68f634a6..07b681df 100644 --- a/src/js/generated/BankableSchemaMixin.ts +++ b/src/js/generated/BankableSchemaMixin.ts @@ -17,12 +17,6 @@ export function bankableSchemaMixin( set exabyteId(value: BankableSchema["exabyteId"]) { this.setProp("exabyteId", value); }, - get hash() { - return this.requiredProp("hash"); - }, - set hash(value: BankableSchema["hash"]) { - this.setProp("hash", value); - }, }; Object.defineProperties(item, Object.getOwnPropertyDescriptors(properties)); diff --git a/src/js/generated/HashedSchemaMixin.ts b/src/js/generated/HashedSchemaMixin.ts new file mode 100644 index 00000000..55b37073 --- /dev/null +++ b/src/js/generated/HashedSchemaMixin.ts @@ -0,0 +1,23 @@ +import type { HashedSchema } from "@mat3ra/esse/dist/js/types"; + +import type { InMemoryEntity } from "../entity/in_memory"; + +export type HashedSchemaMixin = HashedSchema; + +export type HashedInMemoryEntity = InMemoryEntity & HashedSchemaMixin; + +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)); +} From e5f03b02102f51a5792e0b8c4fedac3860aaf7ed Mon Sep 17 00:00:00 2001 From: Kostiantyn Dvornik Date: Tue, 23 Jun 2026 16:08:03 +0300 Subject: [PATCH 5/5] chore: bankable + hashed mixins --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index e48526c7..6f69aa3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "@babel/register": "^7.25.7", "@babel/runtime-corejs3": "^7.25.7", "@exabyte-io/eslint-config": "^2025.1.15-0", - "@mat3ra/esse": "file:../esse/mat3ra-esse-0.0.0.tgz", + "@mat3ra/esse": "https://github.com/mat3ra/esse/tarball/c9cb5f08236cb513f19f34a03670f80eaf9e466a", "@mat3ra/tsconfig": "2024.6.3-0", "@types/chai": "^4.3.20", "@types/crypto-js": "^4.2.2", @@ -68,7 +68,7 @@ "node": ">=12.0.0" }, "peerDependencies": { - "@mat3ra/esse": "file:../esse/mat3ra-esse-0.0.0.tgz" + "@mat3ra/esse": "*" } }, "node_modules/@ampproject/remapping": { @@ -2782,8 +2782,8 @@ }, "node_modules/@mat3ra/esse": { "version": "0.0.0", - "resolved": "file:../esse/mat3ra-esse-0.0.0.tgz", - "integrity": "sha512-yRdNF1ECrhkiBa4SXVBiiVP2acXSN6uBZiRhopD0cVuL0vNwxHX+K3rvQ5MZdggn5zhCWJDL/YtOq0SYx98xTQ==", + "resolved": "https://github.com/mat3ra/esse/tarball/c9cb5f08236cb513f19f34a03670f80eaf9e466a", + "integrity": "sha512-8sWRcnME/A8hntztfZbG1G9G6FX86HeYnUSN0DG63ItHZl+vb1iI4X+IiGHUOXnrxJC4dxHgfaa8IVqTKLbg5Q==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index fe3774eb..81ebd1e6 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@babel/register": "^7.25.7", "@babel/runtime-corejs3": "^7.25.7", "@exabyte-io/eslint-config": "^2025.1.15-0", - "@mat3ra/esse": "file:../esse/mat3ra-esse-0.0.0.tgz", + "@mat3ra/esse": "https://github.com/mat3ra/esse/tarball/c9cb5f08236cb513f19f34a03670f80eaf9e466a", "@mat3ra/tsconfig": "2024.6.3-0", "@types/chai": "^4.3.20", "@types/crypto-js": "^4.2.2", @@ -87,7 +87,7 @@ "typescript": "^5.6.0" }, "peerDependencies": { - "@mat3ra/esse": "file:../esse/mat3ra-esse-0.0.0.tgz" + "@mat3ra/esse": "*" }, "engines": { "node": ">=12.0.0"