@twin.org/entity 0.0.4-next.1 → 0.0.4-next.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,8 @@ export function entity(options) {
13
13
  return (target) => {
14
14
  const entitySchema = DecoratorHelper.getSchema(target);
15
15
  entitySchema.type = target.name;
16
- entitySchema.options = options;
16
+ entitySchema.description = options?.description;
17
+ entitySchema.version = options?.version;
17
18
  DecoratorHelper.setSchema(target, entitySchema);
18
19
  };
19
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"entityDecorator.js","sourceRoot":"","sources":["../../../src/decorators/entityDecorator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,uDAAuD;AACvD,OAAO,kBAAkB,CAAC;AAC1B,OAAO,OAAO,CAAC;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,OAA8B;IACpD,OAAO,CAAC,MAAW,EAAE,EAAE;QACtB,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvD,YAAY,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QAChC,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;QAC/B,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport \"reflect-metadata\";\nimport \"tslib\";\nimport type { IEntitySchemaOptions } from \"../models/IEntitySchemaOptions.js\";\nimport { DecoratorHelper } from \"../utils/decoratorHelper.js\";\n\n/**\n * Decorator to produce schema data for entity.\n * @param options The options for the entity.\n * @returns The class decorator.\n */\nexport function entity(options?: IEntitySchemaOptions): any {\n\treturn (target: any) => {\n\t\tconst entitySchema = DecoratorHelper.getSchema(target);\n\t\tentitySchema.type = target.name;\n\t\tentitySchema.options = options;\n\t\tDecoratorHelper.setSchema(target, entitySchema);\n\t};\n}\n"]}
1
+ {"version":3,"file":"entityDecorator.js","sourceRoot":"","sources":["../../../src/decorators/entityDecorator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,uDAAuD;AACvD,OAAO,kBAAkB,CAAC;AAC1B,OAAO,OAAO,CAAC;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,OAA8B;IACpD,OAAO,CAAC,MAAW,EAAE,EAAE;QACtB,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvD,YAAY,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QAChC,YAAY,CAAC,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;QAChD,YAAY,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC;QAExC,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport \"reflect-metadata\";\nimport \"tslib\";\nimport type { IEntitySchemaOptions } from \"../models/IEntitySchemaOptions.js\";\nimport { DecoratorHelper } from \"../utils/decoratorHelper.js\";\n\n/**\n * Decorator to produce schema data for entity.\n * @param options The options for the entity.\n * @returns The class decorator.\n */\nexport function entity(options?: IEntitySchemaOptions): any {\n\treturn (target: any) => {\n\t\tconst entitySchema = DecoratorHelper.getSchema(target);\n\t\tentitySchema.type = target.name;\n\t\tentitySchema.description = options?.description;\n\t\tentitySchema.version = options?.version;\n\n\t\tDecoratorHelper.setSchema(target, entitySchema);\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IEntitySchema.js","sourceRoot":"","sources":["../../../src/models/IEntitySchema.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IEntitySchemaOptions } from \"./IEntitySchemaOptions.js\";\nimport type { IEntitySchemaProperty } from \"./IEntitySchemaProperty.js\";\n\n/**\n * Definition for an entity schema.\n */\nexport interface IEntitySchema<T = unknown> {\n\t/**\n\t * The type of the entity.\n\t */\n\ttype: string | undefined;\n\n\t/**\n\t * The options for the entity.\n\t */\n\toptions?: IEntitySchemaOptions;\n\n\t/**\n\t * The properties of the entity.\n\t */\n\tproperties?: IEntitySchemaProperty<T>[];\n}\n"]}
1
+ {"version":3,"file":"IEntitySchema.js","sourceRoot":"","sources":["../../../src/models/IEntitySchema.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IEntitySchemaOptions } from \"./IEntitySchemaOptions.js\";\nimport type { IEntitySchemaProperty } from \"./IEntitySchemaProperty.js\";\n\n/**\n * Definition for an entity schema.\n */\nexport interface IEntitySchema<T = unknown> extends IEntitySchemaOptions {\n\t/**\n\t * The type of the entity.\n\t */\n\ttype: string | undefined;\n\n\t/**\n\t * The properties of the entity.\n\t */\n\tproperties?: IEntitySchemaProperty<T>[];\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IEntitySchemaOptions.js","sourceRoot":"","sources":["../../../src/models/IEntitySchemaOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Definition for an entity schema options.\n */\nexport interface IEntitySchemaOptions {\n\t/**\n\t * Description of the object.\n\t */\n\tdescription?: string;\n}\n"]}
1
+ {"version":3,"file":"IEntitySchemaOptions.js","sourceRoot":"","sources":["../../../src/models/IEntitySchemaOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Definition for an entity schema options.\n */\nexport interface IEntitySchemaOptions {\n\t/**\n\t * Description of the object.\n\t */\n\tdescription?: string;\n\n\t/**\n\t * The schema version. Used to drive ordered migrations. Absent is treated as version 0.\n\t */\n\tversion?: number;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IEntitySchemaProperty.js","sourceRoot":"","sources":["../../../src/models/IEntitySchemaProperty.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { EntitySchemaPropertyFormat } from \"./entitySchemaPropertyFormat.js\";\nimport type { EntitySchemaPropertyType } from \"./entitySchemaPropertyType.js\";\nimport type { SortDirection } from \"./sortDirection.js\";\n\n/**\n * Definition for an entity schema property.\n */\nexport interface IEntitySchemaProperty<T = unknown> {\n\t/**\n\t * The property name from the entity.\n\t */\n\tproperty: keyof T;\n\n\t/**\n\t * The type of the property.\n\t */\n\ttype: EntitySchemaPropertyType;\n\n\t/**\n\t * The format of the property.\n\t */\n\tformat?: EntitySchemaPropertyFormat;\n\n\t/**\n\t * Is this the primary index property.\n\t */\n\tisPrimary?: boolean;\n\n\t/**\n\t * Is this a secondary index property.\n\t */\n\tisSecondary?: boolean;\n\n\t/**\n\t * Default sort direction for this field, leave empty if not sortable.\n\t */\n\tsortDirection?: SortDirection;\n\n\t/**\n\t * Is the property optional.\n\t */\n\toptional?: boolean;\n\n\t/**\n\t * The type of the item (only applies when type is `array`).\n\t */\n\titemType?: EntitySchemaPropertyType;\n\n\t/**\n\t * The type ref of the item (only applies when type is either `array` or `object`).\n\t */\n\titemTypeRef?: string;\n\n\t/**\n\t * Description of the object.\n\t */\n\tdescription?: string;\n\n\t/**\n\t * Examples of the property values.\n\t */\n\texamples?: unknown[];\n}\n"]}
1
+ {"version":3,"file":"IEntitySchemaProperty.js","sourceRoot":"","sources":["../../../src/models/IEntitySchemaProperty.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { EntitySchemaPropertyFormat } from \"./entitySchemaPropertyFormat.js\";\nimport type { EntitySchemaPropertyType } from \"./entitySchemaPropertyType.js\";\nimport type { SortDirection } from \"./sortDirection.js\";\n\n/**\n * Definition for an entity schema property.\n */\nexport interface IEntitySchemaProperty<T = unknown> {\n\t/**\n\t * The property name from the entity.\n\t */\n\tproperty: keyof T;\n\n\t/**\n\t * The type of the property.\n\t */\n\ttype: EntitySchemaPropertyType;\n\n\t/**\n\t * The format of the property.\n\t */\n\tformat?: EntitySchemaPropertyFormat;\n\n\t/**\n\t * Is this the primary index property.\n\t */\n\tisPrimary?: boolean;\n\n\t/**\n\t * Is this a secondary index property.\n\t */\n\tisSecondary?: boolean;\n\n\t/**\n\t * Default sort direction for this field, leave empty if not sortable.\n\t */\n\tsortDirection?: SortDirection;\n\n\t/**\n\t * Is the property optional.\n\t */\n\toptional?: boolean;\n\n\t/**\n\t * The type of the item (only applies when type is `array`).\n\t */\n\titemType?: EntitySchemaPropertyType;\n\n\t/**\n\t * The type ref of the item (only applies when type is either `array` or `object`).\n\t */\n\titemTypeRef?: string;\n\n\t/**\n\t * Description of the object.\n\t */\n\tdescription?: string;\n\n\t/**\n\t * Examples of the property values.\n\t */\n\texamples?: unknown[];\n\n\t/**\n\t * A default value which can be used in migrations when the property value is not provided.\n\t */\n\tdefaultValue?: unknown;\n}\n"]}
@@ -19,6 +19,27 @@ export class EntitySchemaHelper {
19
19
  static getSchema(target) {
20
20
  return DecoratorHelper.getSchema(target);
21
21
  }
22
+ /**
23
+ * Get the version of the entity schema, defaulting to 0 when absent.
24
+ * This is the single source of truth for the "absent version = v0" convention.
25
+ * When a version is present it must be a non-negative integer >= 0.
26
+ * @param entitySchema The entity schema to read the version from.
27
+ * @returns The declared version, or 0 if no version was set.
28
+ * @throws GuardError if entitySchema is undefined or version is not an integer.
29
+ * @throws GeneralError if version is present but less than 0.
30
+ */
31
+ static getVersion(entitySchema) {
32
+ Guards.object(EntitySchemaHelper.CLASS_NAME, "entitySchema", entitySchema);
33
+ if (!Is.empty(entitySchema?.version)) {
34
+ Guards.integer(EntitySchemaHelper.CLASS_NAME, "entitySchema.version", entitySchema?.version);
35
+ if (entitySchema.version < 0) {
36
+ throw new GeneralError(EntitySchemaHelper.CLASS_NAME, "versionMustBeGreaterThanOrEqualZero", {
37
+ version: entitySchema.version
38
+ });
39
+ }
40
+ }
41
+ return entitySchema.version ?? 0;
42
+ }
22
43
  /**
23
44
  * Get the primary key from the entity schema.
24
45
  * @param entitySchema The entity schema to find the primary key from.
@@ -1 +1 @@
1
- {"version":3,"file":"entitySchemaHelper.js","sourceRoot":"","sources":["../../../src/utils/entitySchemaHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { DecoratorHelper } from \"./decoratorHelper.js\";\nimport type { IEntitySchema } from \"../models/IEntitySchema.js\";\nimport type { IEntitySchemaProperty } from \"../models/IEntitySchemaProperty.js\";\nimport type { IEntitySort } from \"../models/IEntitySort.js\";\nimport type { SortDirection } from \"../models/sortDirection.js\";\n\n/**\n * Class to help with entity schema operations.\n */\nexport class EntitySchemaHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntitySchemaHelper>();\n\n\t/**\n\t * Get the schema for the specified object.\n\t * @param target The object to get the schema data for.\n\t * @returns The schema for the object if it can be found.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic static getSchema<T = unknown>(target: any): IEntitySchema<T> {\n\t\treturn DecoratorHelper.getSchema<T>(target);\n\t}\n\n\t/**\n\t * Get the primary key from the entity schema.\n\t * @param entitySchema The entity schema to find the primary key from.\n\t * @returns The key if only one was found.\n\t * @throws If no primary key was found, or more than one.\n\t */\n\tpublic static getPrimaryKey<T>(entitySchema: IEntitySchema<T>): IEntitySchemaProperty<T> {\n\t\tGuards.object<IEntitySchema<T>>(\n\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\tnameof(entitySchema),\n\t\t\tentitySchema\n\t\t);\n\n\t\tconst primaryKeys = (entitySchema.properties ?? [])?.filter(p => p.isPrimary);\n\t\tif (primaryKeys.length === 0) {\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"noIsPrimary\");\n\t\t}\n\t\tif (primaryKeys.length > 1) {\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"multipleIsPrimary\");\n\t\t}\n\t\treturn primaryKeys[0];\n\t}\n\n\t/**\n\t * Get the sort properties from the schema.\n\t * @param entitySchema The entity schema to find the primary key from.\n\t * @returns The sort keys from the schema or undefined if there are none.\n\t */\n\tpublic static getSortProperties<T>(entitySchema: IEntitySchema<T>): IEntitySort<T>[] | undefined {\n\t\tGuards.object<IEntitySchema<T>>(\n\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\tnameof(entitySchema),\n\t\t\tentitySchema\n\t\t);\n\n\t\tconst sortFields = (entitySchema.properties ?? []).filter(p => !Is.undefined(p.sortDirection));\n\n\t\treturn sortFields.length > 0\n\t\t\t? sortFields.map(\n\t\t\t\t\tp =>\n\t\t\t\t\t\t({\n\t\t\t\t\t\t\tproperty: p.property,\n\t\t\t\t\t\t\ttype: p.type,\n\t\t\t\t\t\t\tsortDirection: p.sortDirection\n\t\t\t\t\t\t}) as IEntitySort<T>\n\t\t\t\t)\n\t\t\t: undefined;\n\t}\n\n\t/**\n\t * Build sort properties from the schema and override if necessary.\n\t * @param entitySchema The entity schema to retrieve the default sort keys.\n\t * @param overrideSortKeys The override sort keys.\n\t * @returns The finalised sort keys.\n\t */\n\tpublic static buildSortProperties<T>(\n\t\tentitySchema: IEntitySchema<T>,\n\t\toverrideSortKeys?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[]\n\t): IEntitySort<T>[] | undefined {\n\t\tGuards.object(EntitySchemaHelper.CLASS_NAME, nameof(entitySchema), entitySchema);\n\n\t\tlet finalSortKeys: IEntitySort<T>[] | undefined;\n\n\t\tif (Is.arrayValue(overrideSortKeys)) {\n\t\t\tfinalSortKeys = [];\n\n\t\t\tfor (const sortKey of overrideSortKeys) {\n\t\t\t\tconst property = (entitySchema.properties ?? []).find(p => p.property === sortKey.property);\n\t\t\t\tif (property) {\n\t\t\t\t\tfinalSortKeys.push({\n\t\t\t\t\t\tproperty: sortKey.property,\n\t\t\t\t\t\tsortDirection: sortKey.sortDirection,\n\t\t\t\t\t\ttype: property.type\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfinalSortKeys = EntitySchemaHelper.getSortProperties(entitySchema);\n\t\t}\n\n\t\treturn finalSortKeys;\n\t}\n\n\t/**\n\t * Validate the entity against the schema.\n\t * @param entity The entity to validate.\n\t * @param entitySchema The schema to validate against.\n\t * @throws If the entity is invalid.\n\t */\n\tpublic static validateEntity<T>(entity: T, entitySchema: IEntitySchema<T>): void {\n\t\tGuards.object(EntitySchemaHelper.CLASS_NAME, nameof(entity), entity);\n\t\tGuards.object<IEntitySchema<T>>(\n\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\tnameof(entitySchema),\n\t\t\tentitySchema\n\t\t);\n\n\t\tconst properties = entitySchema.properties ?? [];\n\t\tif (properties.length === 0 && Is.objectValue(entity)) {\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidEntityProperties\");\n\t\t}\n\n\t\tconst allKeys = Object.keys(entity);\n\n\t\tfor (const prop of properties) {\n\t\t\tconst idx = allKeys.indexOf(prop.property as string);\n\t\t\tif (idx !== -1) {\n\t\t\t\tallKeys.splice(idx, 1);\n\t\t\t}\n\n\t\t\tconst value = entity[prop.property];\n\n\t\t\tif (Is.empty(value)) {\n\t\t\t\t// If the value is empty but the property is not optional, then it's invalid\n\t\t\t\tif (!prop.optional) {\n\t\t\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidOptional\", {\n\t\t\t\t\t\tproperty: prop.property,\n\t\t\t\t\t\ttype: prop.type\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (prop.type === \"integer\" && Is.integer(value)) {\n\t\t\t\t// If the schema expects an integer and the value is an integer, then it's valid\n\t\t\t} else if (\n\t\t\t\tprop.type === \"object\" &&\n\t\t\t\t(Is.object(value) ||\n\t\t\t\t\tIs.array(value) ||\n\t\t\t\t\tIs.string(value) ||\n\t\t\t\t\tIs.number(value) ||\n\t\t\t\t\tIs.boolean(value) ||\n\t\t\t\t\tIs.null(value))\n\t\t\t) {\n\t\t\t\t// If the schema expects an object and the value is anything that can be JSON serialised, then it's valid\n\t\t\t} else if (prop.type === \"array\" && Is.array(value)) {\n\t\t\t\t// If the schema expects an array and the value is an array, then it's valid\n\t\t\t} else if (prop.type !== typeof value) {\n\t\t\t\t// The schema type does not match the value type\n\t\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidEntityProperty\", {\n\t\t\t\t\tvalue,\n\t\t\t\t\tproperty: prop.property,\n\t\t\t\t\ttype: prop.type\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (allKeys.length > 0) {\n\t\t\t// There are keys in the entity that are not in the schema\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidEntityKeys\", {\n\t\t\t\tkeys: allKeys.join(\", \")\n\t\t\t});\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"entitySchemaHelper.js","sourceRoot":"","sources":["../../../src/utils/entitySchemaHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { DecoratorHelper } from \"./decoratorHelper.js\";\nimport type { IEntitySchema } from \"../models/IEntitySchema.js\";\nimport type { IEntitySchemaProperty } from \"../models/IEntitySchemaProperty.js\";\nimport type { IEntitySort } from \"../models/IEntitySort.js\";\nimport type { SortDirection } from \"../models/sortDirection.js\";\n\n/**\n * Class to help with entity schema operations.\n */\nexport class EntitySchemaHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntitySchemaHelper>();\n\n\t/**\n\t * Get the schema for the specified object.\n\t * @param target The object to get the schema data for.\n\t * @returns The schema for the object if it can be found.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic static getSchema<T = unknown>(target: any): IEntitySchema<T> {\n\t\treturn DecoratorHelper.getSchema<T>(target);\n\t}\n\n\t/**\n\t * Get the version of the entity schema, defaulting to 0 when absent.\n\t * This is the single source of truth for the \"absent version = v0\" convention.\n\t * When a version is present it must be a non-negative integer >= 0.\n\t * @param entitySchema The entity schema to read the version from.\n\t * @returns The declared version, or 0 if no version was set.\n\t * @throws GuardError if entitySchema is undefined or version is not an integer.\n\t * @throws GeneralError if version is present but less than 0.\n\t */\n\tpublic static getVersion(entitySchema: IEntitySchema): number {\n\t\tGuards.object<IEntitySchema>(EntitySchemaHelper.CLASS_NAME, nameof(entitySchema), entitySchema);\n\n\t\tif (!Is.empty(entitySchema?.version)) {\n\t\t\tGuards.integer(\n\t\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\t\tnameof(entitySchema?.version),\n\t\t\t\tentitySchema?.version\n\t\t\t);\n\t\t\tif (entitySchema.version < 0) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\t\t\t\"versionMustBeGreaterThanOrEqualZero\",\n\t\t\t\t\t{\n\t\t\t\t\t\tversion: entitySchema.version\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn entitySchema.version ?? 0;\n\t}\n\n\t/**\n\t * Get the primary key from the entity schema.\n\t * @param entitySchema The entity schema to find the primary key from.\n\t * @returns The key if only one was found.\n\t * @throws If no primary key was found, or more than one.\n\t */\n\tpublic static getPrimaryKey<T>(entitySchema: IEntitySchema<T>): IEntitySchemaProperty<T> {\n\t\tGuards.object<IEntitySchema<T>>(\n\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\tnameof(entitySchema),\n\t\t\tentitySchema\n\t\t);\n\n\t\tconst primaryKeys = (entitySchema.properties ?? [])?.filter(p => p.isPrimary);\n\t\tif (primaryKeys.length === 0) {\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"noIsPrimary\");\n\t\t}\n\t\tif (primaryKeys.length > 1) {\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"multipleIsPrimary\");\n\t\t}\n\t\treturn primaryKeys[0];\n\t}\n\n\t/**\n\t * Get the sort properties from the schema.\n\t * @param entitySchema The entity schema to find the primary key from.\n\t * @returns The sort keys from the schema or undefined if there are none.\n\t */\n\tpublic static getSortProperties<T>(entitySchema: IEntitySchema<T>): IEntitySort<T>[] | undefined {\n\t\tGuards.object<IEntitySchema<T>>(\n\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\tnameof(entitySchema),\n\t\t\tentitySchema\n\t\t);\n\n\t\tconst sortFields = (entitySchema.properties ?? []).filter(p => !Is.undefined(p.sortDirection));\n\n\t\treturn sortFields.length > 0\n\t\t\t? sortFields.map(\n\t\t\t\t\tp =>\n\t\t\t\t\t\t({\n\t\t\t\t\t\t\tproperty: p.property,\n\t\t\t\t\t\t\ttype: p.type,\n\t\t\t\t\t\t\tsortDirection: p.sortDirection\n\t\t\t\t\t\t}) as IEntitySort<T>\n\t\t\t\t)\n\t\t\t: undefined;\n\t}\n\n\t/**\n\t * Build sort properties from the schema and override if necessary.\n\t * @param entitySchema The entity schema to retrieve the default sort keys.\n\t * @param overrideSortKeys The override sort keys.\n\t * @returns The finalised sort keys.\n\t */\n\tpublic static buildSortProperties<T>(\n\t\tentitySchema: IEntitySchema<T>,\n\t\toverrideSortKeys?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[]\n\t): IEntitySort<T>[] | undefined {\n\t\tGuards.object(EntitySchemaHelper.CLASS_NAME, nameof(entitySchema), entitySchema);\n\n\t\tlet finalSortKeys: IEntitySort<T>[] | undefined;\n\n\t\tif (Is.arrayValue(overrideSortKeys)) {\n\t\t\tfinalSortKeys = [];\n\n\t\t\tfor (const sortKey of overrideSortKeys) {\n\t\t\t\tconst property = (entitySchema.properties ?? []).find(p => p.property === sortKey.property);\n\t\t\t\tif (property) {\n\t\t\t\t\tfinalSortKeys.push({\n\t\t\t\t\t\tproperty: sortKey.property,\n\t\t\t\t\t\tsortDirection: sortKey.sortDirection,\n\t\t\t\t\t\ttype: property.type\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfinalSortKeys = EntitySchemaHelper.getSortProperties(entitySchema);\n\t\t}\n\n\t\treturn finalSortKeys;\n\t}\n\n\t/**\n\t * Validate the entity against the schema.\n\t * @param entity The entity to validate.\n\t * @param entitySchema The schema to validate against.\n\t * @throws If the entity is invalid.\n\t */\n\tpublic static validateEntity<T>(entity: T, entitySchema: IEntitySchema<T>): void {\n\t\tGuards.object(EntitySchemaHelper.CLASS_NAME, nameof(entity), entity);\n\t\tGuards.object<IEntitySchema<T>>(\n\t\t\tEntitySchemaHelper.CLASS_NAME,\n\t\t\tnameof(entitySchema),\n\t\t\tentitySchema\n\t\t);\n\n\t\tconst properties = entitySchema.properties ?? [];\n\t\tif (properties.length === 0 && Is.objectValue(entity)) {\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidEntityProperties\");\n\t\t}\n\n\t\tconst allKeys = Object.keys(entity);\n\n\t\tfor (const prop of properties) {\n\t\t\tconst idx = allKeys.indexOf(prop.property as string);\n\t\t\tif (idx !== -1) {\n\t\t\t\tallKeys.splice(idx, 1);\n\t\t\t}\n\n\t\t\tconst value = entity[prop.property];\n\n\t\t\tif (Is.empty(value)) {\n\t\t\t\t// If the value is empty but the property is not optional, then it's invalid\n\t\t\t\tif (!prop.optional) {\n\t\t\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidOptional\", {\n\t\t\t\t\t\tproperty: prop.property,\n\t\t\t\t\t\ttype: prop.type\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (prop.type === \"integer\" && Is.integer(value)) {\n\t\t\t\t// If the schema expects an integer and the value is an integer, then it's valid\n\t\t\t} else if (\n\t\t\t\tprop.type === \"object\" &&\n\t\t\t\t(Is.object(value) ||\n\t\t\t\t\tIs.array(value) ||\n\t\t\t\t\tIs.string(value) ||\n\t\t\t\t\tIs.number(value) ||\n\t\t\t\t\tIs.boolean(value) ||\n\t\t\t\t\tIs.null(value))\n\t\t\t) {\n\t\t\t\t// If the schema expects an object and the value is anything that can be JSON serialised, then it's valid\n\t\t\t} else if (prop.type === \"array\" && Is.array(value)) {\n\t\t\t\t// If the schema expects an array and the value is an array, then it's valid\n\t\t\t} else if (prop.type !== typeof value) {\n\t\t\t\t// The schema type does not match the value type\n\t\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidEntityProperty\", {\n\t\t\t\t\tvalue,\n\t\t\t\t\tproperty: prop.property,\n\t\t\t\t\ttype: prop.type\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (allKeys.length > 0) {\n\t\t\t// There are keys in the entity that are not in the schema\n\t\t\tthrow new GeneralError(EntitySchemaHelper.CLASS_NAME, \"invalidEntityKeys\", {\n\t\t\t\tkeys: allKeys.join(\", \")\n\t\t\t});\n\t\t}\n\t}\n}\n"]}
@@ -3,15 +3,11 @@ import type { IEntitySchemaProperty } from "./IEntitySchemaProperty.js";
3
3
  /**
4
4
  * Definition for an entity schema.
5
5
  */
6
- export interface IEntitySchema<T = unknown> {
6
+ export interface IEntitySchema<T = unknown> extends IEntitySchemaOptions {
7
7
  /**
8
8
  * The type of the entity.
9
9
  */
10
10
  type: string | undefined;
11
- /**
12
- * The options for the entity.
13
- */
14
- options?: IEntitySchemaOptions;
15
11
  /**
16
12
  * The properties of the entity.
17
13
  */
@@ -6,4 +6,8 @@ export interface IEntitySchemaOptions {
6
6
  * Description of the object.
7
7
  */
8
8
  description?: string;
9
+ /**
10
+ * The schema version. Used to drive ordered migrations. Absent is treated as version 0.
11
+ */
12
+ version?: number;
9
13
  }
@@ -49,4 +49,8 @@ export interface IEntitySchemaProperty<T = unknown> {
49
49
  * Examples of the property values.
50
50
  */
51
51
  examples?: unknown[];
52
+ /**
53
+ * A default value which can be used in migrations when the property value is not provided.
54
+ */
55
+ defaultValue?: unknown;
52
56
  }
@@ -16,6 +16,16 @@ export declare class EntitySchemaHelper {
16
16
  * @returns The schema for the object if it can be found.
17
17
  */
18
18
  static getSchema<T = unknown>(target: any): IEntitySchema<T>;
19
+ /**
20
+ * Get the version of the entity schema, defaulting to 0 when absent.
21
+ * This is the single source of truth for the "absent version = v0" convention.
22
+ * When a version is present it must be a non-negative integer >= 0.
23
+ * @param entitySchema The entity schema to read the version from.
24
+ * @returns The declared version, or 0 if no version was set.
25
+ * @throws GuardError if entitySchema is undefined or version is not an integer.
26
+ * @throws GeneralError if version is present but less than 0.
27
+ */
28
+ static getVersion(entitySchema: IEntitySchema): number;
19
29
  /**
20
30
  * Get the primary key from the entity schema.
21
31
  * @param entitySchema The entity schema to find the primary key from.
package/docs/changelog.md CHANGED
@@ -1,5 +1,215 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.4-next.11](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.10...entity-v0.0.4-next.11) (2026-06-15)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **entity:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/nameof bumped from 0.0.4-next.10 to 0.0.4-next.11
16
+ * @twin.org/core bumped from 0.0.4-next.10 to 0.0.4-next.11
17
+ * devDependencies
18
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.10 to 0.0.4-next.11
19
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.10 to 0.0.4-next.11
20
+ * @twin.org/validate-locales bumped from 0.0.4-next.10 to 0.0.4-next.11
21
+
22
+ ## [0.0.4-next.10](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.9...entity-v0.0.4-next.10) (2026-06-15)
23
+
24
+
25
+ ### Features
26
+
27
+ * add context id features ([#206](https://github.com/iotaledger/twin-framework/issues/206)) ([ef0d4ee](https://github.com/iotaledger/twin-framework/commit/ef0d4ee11a4f5fc6cc6f52a4958ce905c04ee13b))
28
+ * add guards arrayEndsWith and arrayStartsWith ([95d875e](https://github.com/iotaledger/twin-framework/commit/95d875ec8ccb4713c145fdde941d4cfedcec2ed3))
29
+ * add IEntitySchemaDiff and entitySchemaDiff utility ([#282](https://github.com/iotaledger/twin-framework/issues/282)) ([9d63e94](https://github.com/iotaledger/twin-framework/commit/9d63e94021ee2ffc138004ee68cf53d08a6b17f9))
30
+ * add support for null in EntityConditions.compare ([922c4ba](https://github.com/iotaledger/twin-framework/commit/922c4ba8af578b4e7eaaf21b3c37a9d788941487))
31
+ * add version field to IEntitySchema and EntitySchemaHelper.getVersion for migration support ([#346](https://github.com/iotaledger/twin-framework/issues/346)) ([e74557e](https://github.com/iotaledger/twin-framework/commit/e74557e4ccbda5b9971f4cfcd0852ba5957cead0))
32
+ * entity schema decorators default value ([33397c2](https://github.com/iotaledger/twin-framework/commit/33397c2e24978a91257371a4c63ce7f6a7125d0c))
33
+ * entity schema diff updates ([#294](https://github.com/iotaledger/twin-framework/issues/294)) ([7a7a94d](https://github.com/iotaledger/twin-framework/commit/7a7a94d14ea5e785dd68fd6de1c5a84941721d28))
34
+ * eslint migration to flat config ([74427d7](https://github.com/iotaledger/twin-framework/commit/74427d78d342167f7850e49ab87269326355befe))
35
+ * locales validation ([#197](https://github.com/iotaledger/twin-framework/issues/197)) ([55fdadb](https://github.com/iotaledger/twin-framework/commit/55fdadb13595ce0047f787bd1d4135d429a99f12))
36
+ * relocate core packages from tools ([bcab8f3](https://github.com/iotaledger/twin-framework/commit/bcab8f3160442ea4fcaf442947462504f3d6a17d))
37
+ * support for object comparisons in entity conditions ([edae91d](https://github.com/iotaledger/twin-framework/commit/edae91d3205524080188a35e0ab04da036fa4f39))
38
+ * typescript 6 update ([1d10f31](https://github.com/iotaledger/twin-framework/commit/1d10f31e6516ec622773f45e88af82fe749b384a))
39
+ * update dependencies ([4da77ab](https://github.com/iotaledger/twin-framework/commit/4da77ab30f499e52825ac5a76f51436ceb59c26e))
40
+ * update dependencies ([f3bd015](https://github.com/iotaledger/twin-framework/commit/f3bd015efd169196b7e0335f5cab876ba6ca1d75))
41
+ * use cause instead of inner for errors ([1f4acc4](https://github.com/iotaledger/twin-framework/commit/1f4acc4d7a6b71a134d9547da9bf40de1e1e49da))
42
+
43
+
44
+ ### Bug Fixes
45
+
46
+ * ensure __decorate is defined for decorators ([103a563](https://github.com/iotaledger/twin-framework/commit/103a563ce01ebdef6240d2e590e7b026e8692684))
47
+ * update copyright year ([#260](https://github.com/iotaledger/twin-framework/issues/260)) ([c4ad930](https://github.com/iotaledger/twin-framework/commit/c4ad930fcc84ba6b5447a8074574329870b4c3f5))
48
+
49
+
50
+ ### Dependencies
51
+
52
+ * The following workspace dependencies were updated
53
+ * dependencies
54
+ * @twin.org/nameof bumped from 0.0.4-next.9 to 0.0.4-next.10
55
+ * @twin.org/core bumped from 0.0.4-next.9 to 0.0.4-next.10
56
+ * devDependencies
57
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.9 to 0.0.4-next.10
58
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.9 to 0.0.4-next.10
59
+ * @twin.org/validate-locales bumped from 0.0.4-next.9 to 0.0.4-next.10
60
+
61
+ ## [0.0.4-next.9](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.8...entity-v0.0.4-next.9) (2026-06-15)
62
+
63
+
64
+ ### Miscellaneous Chores
65
+
66
+ * **entity:** Synchronize repo versions
67
+
68
+
69
+ ### Dependencies
70
+
71
+ * The following workspace dependencies were updated
72
+ * dependencies
73
+ * @twin.org/nameof bumped from 0.0.4-next.8 to 0.0.4-next.9
74
+ * @twin.org/core bumped from 0.0.4-next.8 to 0.0.4-next.9
75
+ * devDependencies
76
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.8 to 0.0.4-next.9
77
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.8 to 0.0.4-next.9
78
+ * @twin.org/validate-locales bumped from 0.0.4-next.8 to 0.0.4-next.9
79
+
80
+ ## [0.0.4-next.8](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.7...entity-v0.0.4-next.8) (2026-06-10)
81
+
82
+
83
+ ### Miscellaneous Chores
84
+
85
+ * **entity:** Synchronize repo versions
86
+
87
+
88
+ ### Dependencies
89
+
90
+ * The following workspace dependencies were updated
91
+ * dependencies
92
+ * @twin.org/nameof bumped from 0.0.4-next.7 to 0.0.4-next.8
93
+ * @twin.org/core bumped from 0.0.4-next.7 to 0.0.4-next.8
94
+ * devDependencies
95
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.7 to 0.0.4-next.8
96
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.7 to 0.0.4-next.8
97
+ * @twin.org/validate-locales bumped from 0.0.4-next.7 to 0.0.4-next.8
98
+
99
+ ## [0.0.4-next.7](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.6...entity-v0.0.4-next.7) (2026-06-10)
100
+
101
+
102
+ ### Miscellaneous Chores
103
+
104
+ * **entity:** Synchronize repo versions
105
+
106
+
107
+ ### Dependencies
108
+
109
+ * The following workspace dependencies were updated
110
+ * dependencies
111
+ * @twin.org/nameof bumped from 0.0.4-next.6 to 0.0.4-next.7
112
+ * @twin.org/core bumped from 0.0.4-next.6 to 0.0.4-next.7
113
+ * devDependencies
114
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.6 to 0.0.4-next.7
115
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.6 to 0.0.4-next.7
116
+ * @twin.org/validate-locales bumped from 0.0.4-next.6 to 0.0.4-next.7
117
+
118
+ ## [0.0.4-next.6](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.5...entity-v0.0.4-next.6) (2026-06-05)
119
+
120
+
121
+ ### Features
122
+
123
+ * entity schema decorators default value ([33397c2](https://github.com/iotaledger/twin-framework/commit/33397c2e24978a91257371a4c63ce7f6a7125d0c))
124
+
125
+
126
+ ### Dependencies
127
+
128
+ * The following workspace dependencies were updated
129
+ * dependencies
130
+ * @twin.org/nameof bumped from 0.0.4-next.5 to 0.0.4-next.6
131
+ * @twin.org/core bumped from 0.0.4-next.5 to 0.0.4-next.6
132
+ * devDependencies
133
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.5 to 0.0.4-next.6
134
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.5 to 0.0.4-next.6
135
+ * @twin.org/validate-locales bumped from 0.0.4-next.5 to 0.0.4-next.6
136
+
137
+ ## [0.0.4-next.5](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.4...entity-v0.0.4-next.5) (2026-06-04)
138
+
139
+
140
+ ### Features
141
+
142
+ * add version field to IEntitySchema and EntitySchemaHelper.getVersion for migration support ([#346](https://github.com/iotaledger/twin-framework/issues/346)) ([e74557e](https://github.com/iotaledger/twin-framework/commit/e74557e4ccbda5b9971f4cfcd0852ba5957cead0))
143
+
144
+
145
+ ### Dependencies
146
+
147
+ * The following workspace dependencies were updated
148
+ * dependencies
149
+ * @twin.org/nameof bumped from 0.0.4-next.4 to 0.0.4-next.5
150
+ * @twin.org/core bumped from 0.0.4-next.4 to 0.0.4-next.5
151
+ * devDependencies
152
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.4 to 0.0.4-next.5
153
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.4 to 0.0.4-next.5
154
+ * @twin.org/validate-locales bumped from 0.0.4-next.4 to 0.0.4-next.5
155
+
156
+ ## [0.0.4-next.4](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.3...entity-v0.0.4-next.4) (2026-06-02)
157
+
158
+
159
+ ### Miscellaneous Chores
160
+
161
+ * **entity:** Synchronize repo versions
162
+
163
+
164
+ ### Dependencies
165
+
166
+ * The following workspace dependencies were updated
167
+ * dependencies
168
+ * @twin.org/nameof bumped from 0.0.4-next.3 to 0.0.4-next.4
169
+ * @twin.org/core bumped from 0.0.4-next.3 to 0.0.4-next.4
170
+ * devDependencies
171
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.3 to 0.0.4-next.4
172
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.3 to 0.0.4-next.4
173
+ * @twin.org/validate-locales bumped from 0.0.4-next.3 to 0.0.4-next.4
174
+
175
+ ## [0.0.4-next.3](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.2...entity-v0.0.4-next.3) (2026-05-28)
176
+
177
+
178
+ ### Miscellaneous Chores
179
+
180
+ * **entity:** Synchronize repo versions
181
+
182
+
183
+ ### Dependencies
184
+
185
+ * The following workspace dependencies were updated
186
+ * dependencies
187
+ * @twin.org/nameof bumped from 0.0.4-next.2 to 0.0.4-next.3
188
+ * @twin.org/core bumped from 0.0.4-next.2 to 0.0.4-next.3
189
+ * devDependencies
190
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.2 to 0.0.4-next.3
191
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.2 to 0.0.4-next.3
192
+ * @twin.org/validate-locales bumped from 0.0.4-next.2 to 0.0.4-next.3
193
+
194
+ ## [0.0.4-next.2](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.1...entity-v0.0.4-next.2) (2026-05-28)
195
+
196
+
197
+ ### Miscellaneous Chores
198
+
199
+ * **entity:** Synchronize repo versions
200
+
201
+
202
+ ### Dependencies
203
+
204
+ * The following workspace dependencies were updated
205
+ * dependencies
206
+ * @twin.org/nameof bumped from 0.0.4-next.1 to 0.0.4-next.2
207
+ * @twin.org/core bumped from 0.0.4-next.1 to 0.0.4-next.2
208
+ * devDependencies
209
+ * @twin.org/nameof-transformer bumped from 0.0.4-next.1 to 0.0.4-next.2
210
+ * @twin.org/nameof-vitest-plugin bumped from 0.0.4-next.1 to 0.0.4-next.2
211
+ * @twin.org/validate-locales bumped from 0.0.4-next.1 to 0.0.4-next.2
212
+
3
213
  ## [0.0.4-next.1](https://github.com/iotaledger/twin-framework/compare/entity-v0.0.4-next.0...entity-v0.0.4-next.1) (2026-05-27)
4
214
 
5
215
 
@@ -50,6 +50,38 @@ The schema for the object if it can be found.
50
50
 
51
51
  ***
52
52
 
53
+ ### getVersion() {#getversion}
54
+
55
+ > `static` **getVersion**(`entitySchema`): `number`
56
+
57
+ Get the version of the entity schema, defaulting to 0 when absent.
58
+ This is the single source of truth for the "absent version = v0" convention.
59
+ When a version is present it must be a non-negative integer >= 0.
60
+
61
+ #### Parameters
62
+
63
+ ##### entitySchema
64
+
65
+ [`IEntitySchema`](../interfaces/IEntitySchema.md)
66
+
67
+ The entity schema to read the version from.
68
+
69
+ #### Returns
70
+
71
+ `number`
72
+
73
+ The declared version, or 0 if no version was set.
74
+
75
+ #### Throws
76
+
77
+ GuardError if entitySchema is undefined or version is not an integer.
78
+
79
+ #### Throws
80
+
81
+ GeneralError if version is present but less than 0.
82
+
83
+ ***
84
+
53
85
  ### getPrimaryKey() {#getprimarykey}
54
86
 
55
87
  > `static` **getPrimaryKey**\<`T`\>(`entitySchema`): [`IEntitySchemaProperty`](../interfaces/IEntitySchemaProperty.md)\<`T`\>
@@ -2,6 +2,10 @@
2
2
 
3
3
  Definition for an entity schema.
4
4
 
5
+ ## Extends
6
+
7
+ - [`IEntitySchemaOptions`](IEntitySchemaOptions.md)
8
+
5
9
  ## Type Parameters
6
10
 
7
11
  ### T
@@ -18,16 +22,32 @@ The type of the entity.
18
22
 
19
23
  ***
20
24
 
21
- ### options? {#options}
25
+ ### properties? {#properties}
22
26
 
23
- > `optional` **options?**: [`IEntitySchemaOptions`](IEntitySchemaOptions.md)
27
+ > `optional` **properties?**: [`IEntitySchemaProperty`](IEntitySchemaProperty.md)\<`T`\>[]
24
28
 
25
- The options for the entity.
29
+ The properties of the entity.
26
30
 
27
31
  ***
28
32
 
29
- ### properties? {#properties}
33
+ ### description? {#description}
30
34
 
31
- > `optional` **properties?**: [`IEntitySchemaProperty`](IEntitySchemaProperty.md)\<`T`\>[]
35
+ > `optional` **description?**: `string`
32
36
 
33
- The properties of the entity.
37
+ Description of the object.
38
+
39
+ #### Inherited from
40
+
41
+ [`IEntitySchemaOptions`](IEntitySchemaOptions.md).[`description`](IEntitySchemaOptions.md#description)
42
+
43
+ ***
44
+
45
+ ### version? {#version}
46
+
47
+ > `optional` **version?**: `number`
48
+
49
+ The schema version. Used to drive ordered migrations. Absent is treated as version 0.
50
+
51
+ #### Inherited from
52
+
53
+ [`IEntitySchemaOptions`](IEntitySchemaOptions.md).[`version`](IEntitySchemaOptions.md#version)
@@ -2,6 +2,10 @@
2
2
 
3
3
  Definition for an entity schema options.
4
4
 
5
+ ## Extended by
6
+
7
+ - [`IEntitySchema`](IEntitySchema.md)
8
+
5
9
  ## Properties
6
10
 
7
11
  ### description? {#description}
@@ -9,3 +13,11 @@ Definition for an entity schema options.
9
13
  > `optional` **description?**: `string`
10
14
 
11
15
  Description of the object.
16
+
17
+ ***
18
+
19
+ ### version? {#version}
20
+
21
+ > `optional` **version?**: `number`
22
+
23
+ The schema version. Used to drive ordered migrations. Absent is treated as version 0.
@@ -95,3 +95,11 @@ Description of the object.
95
95
  > `optional` **examples?**: `unknown`[]
96
96
 
97
97
  Examples of the property values.
98
+
99
+ ***
100
+
101
+ ### defaultValue? {#defaultvalue}
102
+
103
+ > `optional` **defaultValue?**: `unknown`
104
+
105
+ A default value which can be used in migrations when the property value is not provided.
package/locales/en.json CHANGED
@@ -10,7 +10,8 @@
10
10
  "invalidEntityProperties": "The schema has no properties defined, but the entity has properties",
11
11
  "invalidEntityProperty": "The entity value of \"{value}\" does not match the type \"{type}\" for property \"{property}\"",
12
12
  "invalidOptional": "The entity property \"{property}\" of type \"{type}\" is not optional, but no value has been provided",
13
- "invalidEntityKeys": "The entity had additional properties that are not in the schema, \"{keys}\""
13
+ "invalidEntityKeys": "The entity had additional properties that are not in the schema, \"{keys}\"",
14
+ "versionMustBeGreaterThanOrEqualZero": "Property \"entitySchema.version\" must be an integer >= 0, but got {version}"
14
15
  }
15
16
  }
16
17
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@twin.org/entity",
3
- "version": "0.0.4-next.1",
3
+ "version": "0.0.4-next.11",
4
4
  "description": "Helpers for defining and working with entities",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/iotaledger/framework.git",
7
+ "url": "git+https://github.com/iotaledger/twin-framework.git",
8
8
  "directory": "packages/entity"
9
9
  },
10
10
  "author": "martyn.janes@iota.org",
@@ -14,8 +14,8 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/core": "0.0.4-next.1",
18
- "@twin.org/nameof": "0.0.4-next.1",
17
+ "@twin.org/core": "0.0.4-next.11",
18
+ "@twin.org/nameof": "0.0.4-next.11",
19
19
  "reflect-metadata": "0.2.2",
20
20
  "tslib": "2.8.1"
21
21
  },
@@ -43,7 +43,7 @@
43
43
  "blockchain"
44
44
  ],
45
45
  "bugs": {
46
- "url": "git+https://github.com/iotaledger/framework/issues"
46
+ "url": "git+https://github.com/iotaledger/twin-framework/issues"
47
47
  },
48
48
  "homepage": "https://twindev.org"
49
49
  }