@vaadin/hilla-models 25.0.5 → 25.1.0-alpha10

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.
@@ -0,0 +1,11 @@
1
+ import type { EmptyObject } from 'type-fest';
2
+ import { type NonAttributedConstraint, $name, type AnyObject, Model, type Value } from './Model.js';
3
+ export declare class ConstraintBuilder<V = unknown, const N extends string = string, A extends AnyObject = EmptyObject> {
4
+ #private;
5
+ protected [$name]: N | undefined;
6
+ constructor();
7
+ model<const M extends Model<V>>(supportedModel: M): ConstraintBuilder<Value<M>, N, A>;
8
+ name<const NN extends N>(name: NN): ConstraintBuilder<V, N & NN, A>;
9
+ attribute<AN extends string, AV>(name: AN, model: Model<AV>): ConstraintBuilder<V, N, (A extends EmptyObject ? AnyObject : A) & Readonly<undefined extends AV ? Partial<Record<AN, AV>> : Record<AN, AV>>>;
10
+ build(this: string extends N ? never : this): NonAttributedConstraint<V | undefined, N, A>;
11
+ }
@@ -0,0 +1,49 @@
1
+ import { $assertSupportedModel, $defaultValue, $name, Model, } from './Model.js';
2
+ export class ConstraintBuilder {
3
+ #supportedModel;
4
+ [$name];
5
+ #attributeDefaults;
6
+ constructor() {
7
+ this.#supportedModel = Model;
8
+ this.#attributeDefaults = {};
9
+ }
10
+ model(supportedModel) {
11
+ this.#supportedModel = supportedModel;
12
+ return this;
13
+ }
14
+ name(name) {
15
+ this[$name] = name;
16
+ return this;
17
+ }
18
+ attribute(name, model) {
19
+ this.#attributeDefaults[name] = model[$defaultValue];
20
+ return this;
21
+ }
22
+ build() {
23
+ const name = this[$name];
24
+ const attributeDefaults = this.#attributeDefaults;
25
+ const supportedModel = this.#supportedModel;
26
+ function assertSupportedModel(model) {
27
+ if (!(model instanceof supportedModel)) {
28
+ throw new Error(`The constraint "${name}" is not applicable to the model "${model[$name]}".`);
29
+ }
30
+ }
31
+ let NonAttributedConstraint = ((valueOrAttributes) => {
32
+ const attributes = {
33
+ ...attributeDefaults,
34
+ ...(typeof valueOrAttributes === 'object' && valueOrAttributes !== null
35
+ ? valueOrAttributes
36
+ : { value: valueOrAttributes }),
37
+ };
38
+ return Object.defineProperties(Object.create(NonAttributedConstraint), {
39
+ attributes: { value: attributes },
40
+ });
41
+ });
42
+ NonAttributedConstraint = Object.defineProperties(NonAttributedConstraint, {
43
+ name: { value: name },
44
+ [$assertSupportedModel]: { value: assertSupportedModel },
45
+ });
46
+ return NonAttributedConstraint;
47
+ }
48
+ }
49
+ //# sourceMappingURL=ConstraintBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConstraintBuilder.js","sourceRoot":"","sources":["src/ConstraintBuilder.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EAIrB,aAAa,EACb,KAAK,EAEL,KAAK,GAEN,MAAM,YAAY,CAAC;AAepB,MAAM,OAAO,iBAAiB;IAC5B,eAAe,CAAW;IAChB,CAAC,KAAK,CAAC,CAAgB;IACxB,kBAAkB,CAAc;IAEzC;QACE,IAAI,CAAC,eAAe,GAAG,KAA4B,CAAC;QACpD,IAAI,CAAC,kBAAkB,GAAG,EAA4B,CAAC;IACzD,CAAC;IAOD,KAAK,CAA2B,cAAiB;QAC/C,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC;QACtC,OAAO,IAAW,CAAC;IACrB,CAAC;IAQD,IAAI,CAAqB,IAAQ;QAC/B,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QACnB,OAAO,IAAW,CAAC;IACrB,CAAC;IASD,SAAS,CACP,IAAQ,EACR,KAAgB;QAMf,IAAI,CAAC,kBAA8C,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;QAClF,OAAO,IAAW,CAAC;IACrB,CAAC;IAQD,KAAK;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,MAAM,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,CAAC;QAClD,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC;QAE5C,SAAS,oBAAoB,CAAC,KAAe;YAC3C,IAAI,CAAC,CAAC,KAAK,YAAY,cAAc,CAAC,EAAE,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,qCAAqC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChG,CAAC;QACH,CAAC;QAED,IAAI,uBAAuB,GAAG,CAAC,CAAC,iBAA2B,EAAE,EAAE;YAC7D,MAAM,UAAU,GAAgB;gBAC9B,GAAG,iBAAiB;gBACpB,GAAG,CAAC,OAAO,iBAAiB,KAAK,QAAQ,IAAI,iBAAiB,KAAK,IAAI;oBACrE,CAAC,CAAC,iBAAiB;oBACnB,CAAC,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;aAClC,CAAC;YAEF,OAAO,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,EAAE;gBACrE,UAAU,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE;aAClC,CAAoC,CAAC;QACxC,CAAC,CAA8C,CAAC;QAEhD,uBAAuB,GAAG,MAAM,CAAC,gBAAgB,CAAC,uBAAuB,EAAE;YACzE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;YACrB,CAAC,qBAAqB,CAAC,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;SACzD,CAAiD,CAAC;QAEnD,OAAO,uBAAuE,CAAC;IACjF,CAAC;CACF","sourcesContent":["import type { EmptyObject } from 'type-fest';\nimport {\n $assertSupportedModel,\n type Constraint,\n type ConstraintFn,\n type NonAttributedConstraint,\n $defaultValue,\n $name,\n type AnyObject,\n Model,\n type Value,\n} from './Model.js';\n\n/**\n * A builder class for declaring constraints.\n * @internal low-level API for internal use only.\n *\n * @typeParam V - The value type of the constraint.\n * @typeParam N - The name of the constraint.\n * @typeParam A - The attributes for the constraint function, object with string\n * keys and arbitrary values. Attributes are required or optional settings for\n * a particular constraint, for example, the 'min' and 'max' settings for the\n * `Size({min, max})` constraint. When the `value` attribute is the only one,\n * the value can be given directly as the first argument of the constraint\n * function; otherwise the attribute object should be provided.\n */\nexport class ConstraintBuilder<V = unknown, const N extends string = string, A extends AnyObject = EmptyObject> {\n #supportedModel: Model<V>;\n protected [$name]: N | undefined;\n readonly #attributeDefaults: Required<A>;\n\n constructor() {\n this.#supportedModel = Model as unknown as Model<V>;\n this.#attributeDefaults = {} as unknown as Required<A>;\n }\n\n /**\n * Sets the model that the constraint is applicable to.\n *\n * @param supportedModel - The model that the constraint is applicable to.\n */\n model<const M extends Model<V>>(supportedModel: M): ConstraintBuilder<Value<M>, N, A> {\n this.#supportedModel = supportedModel;\n return this as any;\n }\n\n /**\n * Sets the name of the constraint.\n *\n * @param name - the name of the constraint.\n * @returns The current builder instance updated with the new name.\n */\n name<const NN extends N>(name: NN): ConstraintBuilder<V, N & NN, A> {\n this[$name] = name;\n return this as any;\n }\n\n /**\n * Defines a new attribute for the constraint.\n *\n * @param name - the name of the attribute.\n * @param model - the model of the attribute value.\n * @returns The current builder instance updated with the new attribute.\n */\n attribute<AN extends string, AV>(\n name: AN,\n model: Model<AV>,\n ): ConstraintBuilder<\n V,\n N,\n (A extends EmptyObject ? AnyObject : A) & Readonly<undefined extends AV ? Partial<Record<AN, AV>> : Record<AN, AV>>\n > {\n (this.#attributeDefaults as Record<string, unknown>)[name] = model[$defaultValue];\n return this as any;\n }\n\n /**\n * Builds the constraint declaration. On the typing level, it checks if all the\n * constraint parts are set correctly and raises an error if not.\n *\n * @returns The constraint declaration.\n */\n build(this: string extends N ? never : this): NonAttributedConstraint<V | undefined, N, A> {\n const name = this[$name];\n const attributeDefaults = this.#attributeDefaults;\n const supportedModel = this.#supportedModel;\n\n function assertSupportedModel(model: Model<V>): void {\n if (!(model instanceof supportedModel)) {\n throw new Error(`The constraint \"${name}\" is not applicable to the model \"${model[$name]}\".`);\n }\n }\n\n let NonAttributedConstraint = ((valueOrAttributes?: unknown) => {\n const attributes: Required<A> = {\n ...attributeDefaults,\n ...(typeof valueOrAttributes === 'object' && valueOrAttributes !== null\n ? valueOrAttributes\n : { value: valueOrAttributes }),\n };\n\n return Object.defineProperties(Object.create(NonAttributedConstraint), {\n attributes: { value: attributes },\n }) as Constraint<V | undefined, N, A>;\n }) as unknown as ConstraintFn<V | undefined, A>;\n\n NonAttributedConstraint = Object.defineProperties(NonAttributedConstraint, {\n name: { value: name },\n [$assertSupportedModel]: { value: assertSupportedModel },\n }) as NonAttributedConstraint<V | undefined, N, A>;\n\n return NonAttributedConstraint as NonAttributedConstraint<V | undefined, N, A>;\n }\n}\n"]}
package/Model.d.ts ADDED
@@ -0,0 +1,75 @@
1
+ import type { EmptyObject } from 'type-fest';
2
+ export interface JvmTypeRef {
3
+ jvmType: string;
4
+ genericArguments?: JvmTypeRef[];
5
+ }
6
+ export type AnnotationValue = AnnotationValue[] | JvmTypeRef | boolean | number | string | undefined;
7
+ export interface Annotation {
8
+ jvmType: string;
9
+ attributes?: Record<string, AnnotationValue>;
10
+ }
11
+ export interface ModelMetadata {
12
+ jvmType?: string;
13
+ annotations?: Annotation[];
14
+ }
15
+ export type Target<T = unknown> = {
16
+ readonly model?: Model<T>;
17
+ readonly value: T;
18
+ };
19
+ export declare const nothing: unique symbol;
20
+ export declare enum Enum {
21
+ }
22
+ export type AnyObject = Readonly<Record<never, never>>;
23
+ export declare const $key: unique symbol;
24
+ export declare const $name: unique symbol;
25
+ export declare const $owner: unique symbol;
26
+ export declare const $meta: unique symbol;
27
+ export declare const $optional: unique symbol;
28
+ export declare const $defaultValue: unique symbol;
29
+ export declare const $constraints: unique symbol;
30
+ export declare const $members: unique symbol;
31
+ export type Value<M extends Model> = M extends Model<infer T> ? T : never;
32
+ export type Extensions<M extends Model> = M extends Model<unknown, infer EX> ? EX : AnyObject;
33
+ export declare const $assertSupportedModel: unique symbol;
34
+ export type ConstraintFn<V = unknown, A extends AnyObject = AnyObject> = EmptyObject extends A ? (attributes?: A) => Constraint<V> : {
35
+ readonly value: never;
36
+ } extends A ? (valueOrAttributes: (A & {
37
+ readonly value: unknown;
38
+ })['value'] | A) => Constraint<V> : (attributes: A) => Constraint<V>;
39
+ export type NonAttributedConstraint<V = unknown, N extends string = string, A extends AnyObject = AnyObject> = ConstraintFn<V, A> & Readonly<{
40
+ attributes: A;
41
+ name: N;
42
+ [$assertSupportedModel](model: Model<V>): void;
43
+ }>;
44
+ export type Constraint<V = unknown, N extends string = string, A extends AnyObject = AnyObject> = Readonly<{
45
+ attributes: EmptyObject extends A ? A : Required<A>;
46
+ name: N;
47
+ [$assertSupportedModel](model: Model<V>): void;
48
+ }>;
49
+ export type Model<V = unknown, EX extends AnyObject = AnyObject> = EX & {
50
+ readonly [$key]: keyof any;
51
+ readonly [$name]: string;
52
+ readonly [$owner]: Model | Target;
53
+ readonly [$meta]?: ModelMetadata;
54
+ readonly [$optional]: boolean;
55
+ readonly [$constraints]: readonly Constraint[];
56
+ readonly [$defaultValue]: V;
57
+ readonly [Symbol.toStringTag]: string;
58
+ [Symbol.hasInstance](value: any): value is Model<V, EX>;
59
+ toString(): string;
60
+ };
61
+ export type DefaultValueProvider<V, EX extends AnyObject = AnyObject> = (model: Model<V, EX>) => V;
62
+ export declare const Model: Model;
63
+ export declare const $sourceModel: unique symbol;
64
+ export declare const $targetModel: unique symbol;
65
+ export type ModelConverter<SM extends Model = Model, TM extends Model = Model> = {
66
+ readonly [$sourceModel]: SM;
67
+ readonly [$targetModel]: TM;
68
+ };
69
+ export type SourceModel<MC extends ModelConverter> = MC[typeof $sourceModel];
70
+ export type TargetModel<MC extends ModelConverter, SM extends SourceModel<MC>> = (MC & {
71
+ readonly [$sourceModel]: SM;
72
+ })[typeof $targetModel];
73
+ export interface CompositeOf<A extends ModelConverter, B extends ModelConverter<Model, SourceModel<A>>> extends ModelConverter<SourceModel<B>> {
74
+ readonly [$targetModel]: TargetModel<A, TargetModel<B, SourceModel<this>>>;
75
+ }
@@ -10,16 +10,16 @@ export const $name = Symbol('name');
10
10
  export const $owner = Symbol('owner');
11
11
  export const $meta = Symbol('meta');
12
12
  export const $optional = Symbol('optional');
13
- export const $defaultValue = Symbol('value');
14
- export const $enum = Symbol('enumerate');
13
+ export const $defaultValue = Symbol('defaultValue');
14
+ export const $constraints = Symbol('constraints');
15
15
  export const $members = Symbol('members');
16
- export const $itemModel = Symbol('itemModel');
16
+ export const $assertSupportedModel = Symbol('assertSupportedModel');
17
17
  export const Model = Object.create(null, {
18
18
  [$key]: {
19
19
  value: 'model',
20
20
  },
21
21
  [$name]: {
22
- value: 'Model',
22
+ value: 'unknown',
23
23
  },
24
24
  [$owner]: {
25
25
  value: detachedTarget,
@@ -29,6 +29,9 @@ export const Model = Object.create(null, {
29
29
  value: false,
30
30
  },
31
31
  [$defaultValue]: {},
32
+ [$constraints]: {
33
+ value: [],
34
+ },
32
35
  [Symbol.toStringTag]: {
33
36
  get() {
34
37
  return this[$name];
@@ -45,4 +48,4 @@ export const Model = Object.create(null, {
45
48
  },
46
49
  },
47
50
  });
48
- //# sourceMappingURL=model.js.map
51
+ //# sourceMappingURL=Model.js.map
package/Model.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Model.js","sourceRoot":"","sources":["src/Model.ts"],"names":[],"mappings":"AA+BA,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAEzC,MAAM,cAAc,GAAW,MAAM,CAAC,MAAM,CAC1C;IACE,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY;CAC7B,EACD;IACE,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;IAC3B,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;CAC1B,CACF,CAAC;AAUF,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;AAKlC,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAKpC,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAKtC,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAKpC,MAAM,CAAC,MAAM,SAAS,GAAkB,MAAM,CAAC,UAAU,CAAC,CAAC;AAK3D,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAKpD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;AAKlD,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAiB1C,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AA4GpE,MAAM,CAAC,MAAM,KAAK,GAAU,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;IAC9C,CAAC,IAAI,CAAC,EAAE;QACN,KAAK,EAAE,OAAO;KACf;IACD,CAAC,KAAK,CAAC,EAAE;QACP,KAAK,EAAE,SAAS;KACjB;IACD,CAAC,MAAM,CAAC,EAAE;QACR,KAAK,EAAE,cAAc;KACtB;IACD,CAAC,KAAK,CAAC,EAAE,EAAE;IACX,CAAC,SAAS,CAAC,EAAE;QACX,KAAK,EAAE,KAAK;KACb;IACD,CAAC,aAAa,CAAC,EAAE,EAAE;IACnB,CAAC,YAAY,CAAC,EAAE;QACd,KAAK,EAAE,EAAE;KACV;IACD,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;QACpB,GAAG;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;KACF;IACD,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;QACpB,KAAK,CAAc,CAAU;YAC3B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5G,CAAC;KACF;IACD,QAAQ,EAAE;QACR,KAAK;YAEH,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzG,CAAC;KACF;CACF,CAAC,CAAC","sourcesContent":["import type { EmptyObject } from 'type-fest';\n\nexport interface JvmTypeRef {\n jvmType: string;\n genericArguments?: JvmTypeRef[];\n}\n\nexport type AnnotationValue = AnnotationValue[] | JvmTypeRef | boolean | number | string | undefined;\n\nexport interface Annotation {\n jvmType: string;\n attributes?: Record<string, AnnotationValue>;\n}\n\n/**\n * The metadata of a model.\n */\nexport interface ModelMetadata {\n jvmType?: string;\n annotations?: Annotation[];\n}\n\n/**\n * The target to which a model is attached. It could be a Binder instance, a\n * Signal or another object. However, it could never be another model.\n */\nexport type Target<T = unknown> = {\n readonly model?: Model<T>;\n readonly value: T;\n};\n\nexport const nothing = Symbol('nothing');\n\nconst detachedTarget: Target = Object.create(\n {\n toString: () => ':detached:',\n },\n {\n model: { value: undefined },\n value: { value: nothing },\n },\n);\n\nexport declare enum Enum {}\n\nexport type AnyObject = Readonly<Record<never, never>>; // {}\n\n/* eslint-disable tsdoc/syntax */\n/**\n * The symbol that represents the {@link Model[$key]} property.\n */\nexport const $key = Symbol('key');\n\n/**\n * The symbol that represents the {@link Model[$name]} property.\n */\nexport const $name = Symbol('name');\n\n/**\n * The symbol that represents the {@link Model[$owner]} property.\n */\nexport const $owner = Symbol('owner');\n\n/**\n * The symbol that represents the {@link Model[$meta]} property.\n */\nexport const $meta = Symbol('meta');\n\n/**\n * The symbol that represents the {@link Model[$optional]} property.\n */\nexport const $optional: unique symbol = Symbol('optional');\n\n/**\n * The symbol that represents the {@link Model[$defaultValue]} property.\n */\nexport const $defaultValue = Symbol('defaultValue');\n\n/**\n * The symbol that represents the constraints property of the {@link Model} type.\n */\nexport const $constraints = Symbol('constraints');\n\n/**\n * The symbol that represents the {@link UnionModel[$members]} property.\n */\nexport const $members = Symbol('members');\n\n/* eslint-enable tsdoc/syntax */\n\n/**\n * Extracts the value type the model represents.\n */\nexport type Value<M extends Model> = M extends Model<infer T> ? T : never;\n\n/**\n * Extracts the list of extra properties of the model.\n */\nexport type Extensions<M extends Model> = M extends Model<unknown, infer EX> ? EX : AnyObject;\n\n/**\n * The symbol that represents the {@link Constraint} method asserting supported model.\n */\nexport const $assertSupportedModel = Symbol('assertSupportedModel');\n\n/**\n * The constraint function type.\n */\nexport type ConstraintFn<V = unknown, A extends AnyObject = AnyObject> = EmptyObject extends A\n ? (attributes?: A) => Constraint<V>\n : { readonly value: never } extends A\n ? (valueOrAttributes: (A & { readonly value: unknown })['value'] | A) => Constraint<V>\n : (attributes: A) => Constraint<V>;\n\n/**\n * The constraint type that doesn't necessarily have attributes specified.\n *\n * @typeParam V - The type of the data described by the model.\n * @typeParam N - The name of the constraint.\n * @typeParam A - The attributes of the constraint.\n */\nexport type NonAttributedConstraint<\n V = unknown,\n N extends string = string,\n A extends AnyObject = AnyObject,\n> = ConstraintFn<V, A> &\n Readonly<{\n attributes: A;\n name: N;\n [$assertSupportedModel](model: Model<V>): void;\n }>;\n\n/**\n * The constraint type with specified attributes.\n *\n * @typeParam V - The type of the data described by the model.\n * @typeParam N - The name of the constraint.\n * @typeParam A - The attributes of the constraint.\n */\nexport type Constraint<V = unknown, N extends string = string, A extends AnyObject = AnyObject> = Readonly<{\n attributes: EmptyObject extends A ? A : Required<A>;\n name: N;\n [$assertSupportedModel](model: Model<V>): void;\n}>;\n\n/**\n * A model that represents a specific type of data.\n *\n * @typeParam V - The type of the data described by the model.\n * @typeParam EX - The extra properties of the model. It could be either a model\n * that represents a property of the object the current model describe, or a\n * model-specific metadata. It's recommended to use a symbol as a key for the\n * metadata property to avoid the potential naming conflicts with the described\n * object properties.\n *\n * @remarks\n * Since we know the full model definition only on this step, the `R` type\n * parameter is essential to describe a model with self-reference properties.\n */\nexport type Model<V = unknown, EX extends AnyObject = AnyObject> = EX & {\n /**\n * The key of the model in the owner model.\n */\n readonly [$key]: keyof any;\n\n /**\n * The name of the model. For attached models, the name will be prefixed\n * with the `@` symbol.\n */\n readonly [$name]: string;\n\n /**\n * The owner model of the model. For detached models, the owner will always\n * be a specific global object `detachedTarget`.\n */\n readonly [$owner]: Model | Target;\n\n /**\n * The metadata of the model.\n */\n readonly [$meta]?: ModelMetadata;\n\n /**\n * Whether the model is optional. It describes if the data described by\n * this model is nullable.\n */\n readonly [$optional]: boolean;\n\n /**\n * The list of validation constraints for the model.\n */\n readonly [$constraints]: readonly Constraint[];\n\n /**\n * The default value of the model.\n */\n readonly [$defaultValue]: V;\n readonly [Symbol.toStringTag]: string;\n [Symbol.hasInstance](value: any): value is Model<V, EX>;\n toString(): string;\n};\n\n/**\n * A function that provides a default value for a model.\n *\n * @typeParam V - The type of the data described by the model.\n * @typeParam EX - The extra properties of the model.\n * @typeParam R - The keys of the self-referencing properties of the model.\n */\nexport type DefaultValueProvider<V, EX extends AnyObject = AnyObject> = (model: Model<V, EX>) => V;\n\nexport const Model: Model = Object.create(null, {\n [$key]: {\n value: 'model',\n },\n [$name]: {\n value: 'unknown',\n },\n [$owner]: {\n value: detachedTarget,\n },\n [$meta]: {},\n [$optional]: {\n value: false,\n },\n [$defaultValue]: {},\n [$constraints]: {\n value: [],\n },\n [Symbol.toStringTag]: {\n get(this: Model) {\n return this[$name];\n },\n },\n [Symbol.hasInstance]: {\n value(this: Model, o: unknown) {\n return typeof o === 'object' && o != null && (this === o || Object.prototype.isPrototypeOf.call(this, o));\n },\n },\n toString: {\n value(this: Model) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return `[${String(this[$owner])} / ${String(this[$key])}${this[$optional] ? '?' : ''}] ${this[$name]}`;\n },\n },\n});\n\n// The following symbols are used to define HKT signatures for model converter\n// functions, enabling type inference for functional composition for them.\n// Inspired by: https://stackoverflow.com/a/69247453\n\n/**\n * The symbol marking source (parameter) type for model converter HKT\n * signatures.\n */\nexport declare const $sourceModel: unique symbol;\n\n/**\n * The symbol marking target (return) type for model converter HKT\n * signatures.\n */\nexport declare const $targetModel: unique symbol;\n\n/**\n * Base HKT signature for model converter functions. Corresponds to a function\n * such as.\n */\nexport type ModelConverter<SM extends Model = Model, TM extends Model = Model> = {\n readonly [$sourceModel]: SM;\n readonly [$targetModel]: TM;\n};\n\n/**\n * Gets the source type of the given model converter HKT signature.\n */\nexport type SourceModel<MC extends ModelConverter> = MC[typeof $sourceModel];\n\n/**\n * Gets the target type of the given model converter HKT signature for the given\n * source type.\n */\nexport type TargetModel<MC extends ModelConverter, SM extends SourceModel<MC>> = (MC & {\n readonly [$sourceModel]: SM;\n})[typeof $targetModel];\n\n/**\n * Makes functional composition of two given model converter HKT signatures,\n * given A(x) and B(x) returns A(B(x)) HKT signature.\n */\nexport interface CompositeOf<A extends ModelConverter, B extends ModelConverter<Model, SourceModel<A>>>\n extends ModelConverter<SourceModel<B>> {\n readonly [$targetModel]: TargetModel<A, TargetModel<B, SourceModel<this>>>;\n}\n"]}
@@ -0,0 +1,22 @@
1
+ export declare const Null: import("./Model.js").NonAttributedConstraint<unknown, "Null", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
2
+ export declare const NotNull: import("./Model.js").NonAttributedConstraint<unknown, "NotNull", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
3
+ export declare const AssertTrue: import("./Model.js").NonAttributedConstraint<boolean | undefined, "AssertTrue", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
4
+ export declare const AssertFalse: import("./Model.js").NonAttributedConstraint<boolean | undefined, "AssertFalse", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
5
+ export declare const Min: import("./Model.js").NonAttributedConstraint<number | undefined, "Min", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>> & Readonly<Record<"value", number>>>;
6
+ export declare const Max: import("./Model.js").NonAttributedConstraint<number | undefined, "Max", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>> & Readonly<Record<"value", number>>>;
7
+ export declare const DecimalMin: import("./Model.js").NonAttributedConstraint<string | number | undefined, "DecimalMin", Readonly<Record<never, never>> & Readonly<Record<"value", number>>>;
8
+ export declare const DecimalMax: import("./Model.js").NonAttributedConstraint<string | number | undefined, "DecimalMax", import("type-fest").EmptyObject>;
9
+ export declare const Negative: import("./Model.js").NonAttributedConstraint<number | undefined, "Negative", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
10
+ export declare const NegativeOrZero: import("./Model.js").NonAttributedConstraint<number | undefined, "NegativeOrZero", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
11
+ export declare const Positive: import("./Model.js").NonAttributedConstraint<number | undefined, "Positive", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
12
+ export declare const PositiveOrZero: import("./Model.js").NonAttributedConstraint<number | undefined, "PositiveOrZero", Readonly<Record<never, never>> & Readonly<Partial<Record<"message", string | undefined>>>>;
13
+ export declare const Size: import("./Model.js").NonAttributedConstraint<string | unknown[] | undefined, "Size", Readonly<Record<never, never>> & Readonly<Partial<Record<"min", number | undefined>>> & Readonly<Partial<Record<"max", number | undefined>>>>;
14
+ export declare const Digits: import("./Model.js").NonAttributedConstraint<string | number | undefined, "Digits", Readonly<Record<never, never>> & Readonly<Record<"integer", number>> & Readonly<Record<"fraction", number>>>;
15
+ export declare const Past: import("./Model.js").NonAttributedConstraint<string | undefined, "Past", import("type-fest").EmptyObject>;
16
+ export declare const PastOrPresent: import("./Model.js").NonAttributedConstraint<string | undefined, "PastOrPresent", import("type-fest").EmptyObject>;
17
+ export declare const Future: import("./Model.js").NonAttributedConstraint<string | undefined, "Future", import("type-fest").EmptyObject>;
18
+ export declare const FutureOrPresent: import("./Model.js").NonAttributedConstraint<string | undefined, "FutureOrPresent", import("type-fest").EmptyObject>;
19
+ export declare const Pattern: import("./Model.js").NonAttributedConstraint<string | undefined, "Pattern", Readonly<Record<never, never>> & Readonly<Record<"regexp", string>>>;
20
+ export declare const NotEmpty: import("./Model.js").NonAttributedConstraint<string | Record<string, unknown> | unknown[] | undefined, "NotEmpty", import("type-fest").EmptyObject>;
21
+ export declare const NotBlank: import("./Model.js").NonAttributedConstraint<string | undefined, "NotBlank", import("type-fest").EmptyObject>;
22
+ export declare const Email: import("./Model.js").NonAttributedConstraint<string | undefined, "Email", import("type-fest").EmptyObject>;
package/constraints.js ADDED
@@ -0,0 +1,90 @@
1
+ import { ConstraintBuilder } from './ConstraintBuilder.js';
2
+ import * as m from './m.js';
3
+ import { Model } from './Model.js';
4
+ import { ArrayModel, BooleanModel, NumberModel, RecordModel, StringModel } from './models.js';
5
+ export const Null = new ConstraintBuilder()
6
+ .model(Model)
7
+ .attribute('message', m.optional(StringModel))
8
+ .name('Null')
9
+ .build();
10
+ export const NotNull = new ConstraintBuilder()
11
+ .model(Model)
12
+ .attribute('message', m.optional(StringModel))
13
+ .name('NotNull')
14
+ .build();
15
+ export const AssertTrue = new ConstraintBuilder()
16
+ .model(BooleanModel)
17
+ .attribute('message', m.optional(StringModel))
18
+ .name('AssertTrue')
19
+ .build();
20
+ export const AssertFalse = new ConstraintBuilder()
21
+ .model(BooleanModel)
22
+ .attribute('message', m.optional(StringModel))
23
+ .name('AssertFalse')
24
+ .build();
25
+ export const Min = new ConstraintBuilder()
26
+ .model(NumberModel)
27
+ .attribute('message', m.optional(StringModel))
28
+ .name('Min')
29
+ .attribute('value', NumberModel)
30
+ .build();
31
+ export const Max = new ConstraintBuilder()
32
+ .model(NumberModel)
33
+ .attribute('message', m.optional(StringModel))
34
+ .name('Max')
35
+ .attribute('value', NumberModel)
36
+ .build();
37
+ export const DecimalMin = new ConstraintBuilder()
38
+ .model(m.union(NumberModel, StringModel))
39
+ .name('DecimalMin')
40
+ .attribute('value', NumberModel)
41
+ .build();
42
+ export const DecimalMax = new ConstraintBuilder().model(m.union(NumberModel, StringModel)).name('DecimalMax').build();
43
+ export const Negative = new ConstraintBuilder()
44
+ .model(NumberModel)
45
+ .attribute('message', m.optional(StringModel))
46
+ .name('Negative')
47
+ .build();
48
+ export const NegativeOrZero = new ConstraintBuilder()
49
+ .model(NumberModel)
50
+ .attribute('message', m.optional(StringModel))
51
+ .name('NegativeOrZero')
52
+ .build();
53
+ export const Positive = new ConstraintBuilder()
54
+ .model(NumberModel)
55
+ .attribute('message', m.optional(StringModel))
56
+ .name('Positive')
57
+ .build();
58
+ export const PositiveOrZero = new ConstraintBuilder()
59
+ .model(NumberModel)
60
+ .attribute('message', m.optional(StringModel))
61
+ .name('PositiveOrZero')
62
+ .build();
63
+ export const Size = new ConstraintBuilder()
64
+ .model(m.union(StringModel, ArrayModel))
65
+ .name('Size')
66
+ .attribute('min', m.optional(m.withDefaultValue(NumberModel, 0)))
67
+ .attribute('max', m.optional(m.withDefaultValue(NumberModel, Number.MAX_SAFE_INTEGER)))
68
+ .build();
69
+ export const Digits = new ConstraintBuilder()
70
+ .model(m.union(StringModel, NumberModel))
71
+ .name('Digits')
72
+ .attribute('integer', NumberModel)
73
+ .attribute('fraction', NumberModel)
74
+ .build();
75
+ export const Past = new ConstraintBuilder().model(StringModel).name('Past').build();
76
+ export const PastOrPresent = new ConstraintBuilder().model(StringModel).name('PastOrPresent').build();
77
+ export const Future = new ConstraintBuilder().model(StringModel).name('Future').build();
78
+ export const FutureOrPresent = new ConstraintBuilder().model(StringModel).name('FutureOrPresent').build();
79
+ export const Pattern = new ConstraintBuilder()
80
+ .model(StringModel)
81
+ .name('Pattern')
82
+ .attribute('regexp', StringModel)
83
+ .build();
84
+ export const NotEmpty = new ConstraintBuilder()
85
+ .model(m.union(StringModel, ArrayModel, RecordModel))
86
+ .name('NotEmpty')
87
+ .build();
88
+ export const NotBlank = new ConstraintBuilder().model(StringModel).name('NotBlank').build();
89
+ export const Email = new ConstraintBuilder().model(StringModel).name('Email').build();
90
+ //# sourceMappingURL=constraints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints.js","sourceRoot":"","sources":["src/constraints.ts"],"names":[],"mappings":"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","sourcesContent":["import { ConstraintBuilder } from './ConstraintBuilder.js';\nimport * as m from './m.js';\nimport { Model } from './Model.js';\nimport { ArrayModel, BooleanModel, NumberModel, RecordModel, StringModel } from './models.js';\n\n/**\n * The constrained model value must be `undefined`.\n */\nexport const Null = new ConstraintBuilder()\n .model(Model)\n .attribute('message', m.optional(StringModel))\n .name('Null')\n .build();\n\n/**\n * The constrained model value must not be `undefined`.\n */\nexport const NotNull = new ConstraintBuilder()\n .model(Model)\n .attribute('message', m.optional(StringModel))\n .name('NotNull')\n .build();\n\n/**\n * The constrained model value must be `true`.\n *\n * `undefined` value is considered valid.\n */\nexport const AssertTrue = new ConstraintBuilder()\n .model(BooleanModel)\n .attribute('message', m.optional(StringModel))\n .name('AssertTrue')\n .build();\n\n/**\n * The constrained model value must be `false`.\n *\n * `undefined` value is considered valid.\n */\nexport const AssertFalse = new ConstraintBuilder()\n .model(BooleanModel)\n .attribute('message', m.optional(StringModel))\n .name('AssertFalse')\n .build();\n\n/**\n * The constrained model value must be higher than or equal to the specified value.\n *\n * `undefined` value is considered valid.\n */\nexport const Min = new ConstraintBuilder()\n .model(NumberModel)\n .attribute('message', m.optional(StringModel))\n .name('Min')\n .attribute('value', NumberModel)\n .build();\n\n/**\n * The constrained model value must be lower than or equal to the specified value.\n *\n * `undefined` value is considered valid.\n */\nexport const Max = new ConstraintBuilder()\n .model(NumberModel)\n .attribute('message', m.optional(StringModel))\n .name('Max')\n .attribute('value', NumberModel)\n .build();\n\n/**\n * The constrained model value must be a number that is greater than or equal to the specified value.\n *\n * `undefined` value is considered valid.\n */\nexport const DecimalMin = new ConstraintBuilder()\n .model(m.union(NumberModel, StringModel))\n .name('DecimalMin')\n .attribute('value', NumberModel)\n .build();\n\n/**\n * The constrained model value must be a number that is lower than or equal to the specified value.\n *\n * `undefined` value is considered valid.\n */\nexport const DecimalMax = new ConstraintBuilder().model(m.union(NumberModel, StringModel)).name('DecimalMax').build();\n\n/**\n * The constrained model value must be strictly below zero.\n *\n * `undefined` value is considered valid.\n */\nexport const Negative = new ConstraintBuilder()\n .model(NumberModel)\n .attribute('message', m.optional(StringModel))\n .name('Negative')\n .build();\n\n/**\n * The constrained model value must be below zero or equal to zero.\n *\n * `undefined` value is considered valid.\n */\nexport const NegativeOrZero = new ConstraintBuilder()\n .model(NumberModel)\n .attribute('message', m.optional(StringModel))\n .name('NegativeOrZero')\n .build();\n\n/**\n * The constrained model value must be strictly above zero.\n *\n * `undefined` value is considered valid.\n */\nexport const Positive = new ConstraintBuilder()\n .model(NumberModel)\n .attribute('message', m.optional(StringModel))\n .name('Positive')\n .build();\n\n/**\n * The constrained model value must be above zero or equal to zero.\n *\n * `undefined` value is considered valid.\n */\nexport const PositiveOrZero = new ConstraintBuilder()\n .model(NumberModel)\n .attribute('message', m.optional(StringModel))\n .name('PositiveOrZero')\n .build();\n\n/**\n * The length of the constrained value must be between the specified boundaries.\n *\n * `undefined` value is considered valid.\n */\nexport const Size = new ConstraintBuilder()\n .model(m.union(StringModel, ArrayModel))\n .name('Size')\n .attribute('min', m.optional(m.withDefaultValue(NumberModel, 0)))\n .attribute('max', m.optional(m.withDefaultValue(NumberModel, Number.MAX_SAFE_INTEGER)))\n .build();\n\n/**\n * The constrained value must be a number within the specified range.\n *\n * `undefined` value is considered valid.\n */\nexport const Digits = new ConstraintBuilder()\n .model(m.union(StringModel, NumberModel))\n .name('Digits')\n .attribute('integer', NumberModel)\n .attribute('fraction', NumberModel)\n .build();\n\n/**\n * The constrained value must be a date, time, or timestamp in the past.\n *\n * `undefined` value is considered valid.\n */\nexport const Past = new ConstraintBuilder().model(StringModel).name('Past').build();\n\n/**\n * The constrained value must be a date, time, or timestamp in the past or present.\n *\n * `undefined` value is considered valid.\n */\nexport const PastOrPresent = new ConstraintBuilder().model(StringModel).name('PastOrPresent').build();\n\n/**\n * The constrained value must be a date, time, or timestamp in the future.\n *\n * `undefined` value is considered valid.\n */\nexport const Future = new ConstraintBuilder().model(StringModel).name('Future').build();\n\n/**\n * The constrained value must be a date, time, or timestamp in the future or present.\n *\n * `undefined` value is considered valid.\n */\nexport const FutureOrPresent = new ConstraintBuilder().model(StringModel).name('FutureOrPresent').build();\n\n/**\n * The constrained value must match the specified regular expression.\n *\n * `undefined` value is considered valid.\n */\nexport const Pattern = new ConstraintBuilder()\n .model(StringModel)\n .name('Pattern')\n .attribute('regexp', StringModel)\n .build();\n\n/**\n * The constrained value must not be an `undefined` or empty.\n *\n * `undefined` value is considered valid.\n */\nexport const NotEmpty = new ConstraintBuilder()\n .model(m.union(StringModel, ArrayModel, RecordModel))\n .name('NotEmpty')\n .build();\n\n/**\n * The constrained value must not be an `undefined` and must contain at least one non-whitespace character.\n *\n * `undefined` value is considered valid.\n */\nexport const NotBlank = new ConstraintBuilder().model(StringModel).name('NotBlank').build();\n\n/**\n * The constrained value must be a valid email address.\n *\n * `undefined` value is considered valid.\n */\nexport const Email = new ConstraintBuilder().model(StringModel).name('Email').build();\n"]}
@@ -0,0 +1,33 @@
1
+ import { $assertSupportedModel, type $targetModel, type Constraint, type CompositeOf, type SourceModel, type TargetModel, type Model, type ModelConverter, type ModelMetadata, type Value } from './Model.js';
2
+ import { ArrayModel, type OptionalModel } from './models.js';
3
+ export type ModelConverterFn<SM extends Model = Model, TM extends Model = Model> = (model: SM, ...extraArgs: readonly any[]) => TM;
4
+ export type ModelConverterCallable<MC extends ModelConverter, F extends ModelConverterFn<SourceModel<MC>, TargetModel<MC, SourceModel<MC>>>> = F extends (model: Model, ...extraArgs: infer E) => Model ? <const IMC extends ModelConverter>(modelConverter: IMC, ...extraArgs: E) => CompositeOf<MC, IMC> : never;
5
+ export interface IdentityOf extends ModelConverter {
6
+ readonly [$targetModel]: SourceModel<this>;
7
+ }
8
+ declare function selfImpl<const M extends Model>(model: M): M;
9
+ export type ModelSelf = IdentityOf & typeof selfImpl;
10
+ declare const _self: ModelSelf;
11
+ export { _self as self };
12
+ export interface OptionalOf extends ModelConverter {
13
+ readonly [$targetModel]: OptionalModel<SourceModel<this>>;
14
+ }
15
+ declare function optionalImpl<const M extends Model>(model: M): OptionalModel<M>;
16
+ export declare const optional: OptionalOf & typeof optionalImpl & (<const IMC extends ModelConverter>(modelConverter: IMC) => CompositeOf<OptionalOf, IMC>);
17
+ export interface ArrayOf extends ModelConverter {
18
+ readonly [$targetModel]: ArrayModel<SourceModel<this>>;
19
+ }
20
+ declare function arrayImpl<const M extends Model>(model: M): ArrayModel<M>;
21
+ export declare const array: ArrayOf & typeof arrayImpl & (<const IMC extends ModelConverter>(modelConverter: IMC) => CompositeOf<ArrayOf, IMC>);
22
+ declare function constrainedImpl<const M extends Model>(this: void, model: M, constraint: Constraint<Value<M>>, ...moreConstraints: ReadonlyArray<Constraint<Value<M>>>): M;
23
+ export declare const constrained: IdentityOf & typeof constrainedImpl & (<const IMC extends ModelConverter>(modelConverter: IMC, constraint: Readonly<{
24
+ attributes: Readonly<Record<never, never>>;
25
+ name: string;
26
+ [$assertSupportedModel](model: Model<unknown, Readonly<Record<never, never>>>): void;
27
+ }>, ...moreConstraints: Readonly<{
28
+ attributes: Readonly<Record<never, never>>;
29
+ name: string;
30
+ [$assertSupportedModel](model: Model<unknown, Readonly<Record<never, never>>>): void;
31
+ }>[]) => CompositeOf<IdentityOf, IMC>);
32
+ declare function metaImpl<const M extends Model>(this: void, model: M, metadata: ModelMetadata): M;
33
+ export declare const meta: IdentityOf & typeof metaImpl & (<const IMC extends ModelConverter>(modelConverter: IMC, metadata: ModelMetadata) => CompositeOf<IdentityOf, IMC>);
package/converters.js ADDED
@@ -0,0 +1,44 @@
1
+ import { $assertSupportedModel, $constraints, $name, $optional, } from './Model.js';
2
+ import { CoreModelBuilder } from './modelBuilders.js';
3
+ import { $itemModel, ArrayModel } from './models.js';
4
+ function createModelConverter(modelConverterFn) {
5
+ return ((modelOrConverter, ...extraArgs) => {
6
+ if (typeof modelOrConverter === 'function') {
7
+ return (model) => modelConverterFn(modelOrConverter(model), ...extraArgs);
8
+ }
9
+ return modelConverterFn(modelOrConverter, ...extraArgs);
10
+ });
11
+ }
12
+ function selfImpl(model) {
13
+ return model;
14
+ }
15
+ const _self = selfImpl;
16
+ export { _self as self };
17
+ function optionalImpl(model) {
18
+ return new CoreModelBuilder(model).name(model[$name]).define($optional, { value: true }).build();
19
+ }
20
+ export const optional = createModelConverter(optionalImpl);
21
+ function arrayImpl(model) {
22
+ return new CoreModelBuilder(ArrayModel, () => [])
23
+ .name(`Array<${model[$name]}>`)
24
+ .define($itemModel, { value: model })
25
+ .build();
26
+ }
27
+ export const array = createModelConverter(arrayImpl);
28
+ function constrainedImpl(model, constraint, ...moreConstraints) {
29
+ const previousConstraints = model[$constraints];
30
+ const newConstraints = [constraint, ...moreConstraints];
31
+ for (const newConstraint of newConstraints) {
32
+ newConstraint[$assertSupportedModel](model);
33
+ }
34
+ return new CoreModelBuilder(model)
35
+ .name(model[$name])
36
+ .define($constraints, { value: [...previousConstraints, ...newConstraints] })
37
+ .build();
38
+ }
39
+ export const constrained = createModelConverter(constrainedImpl);
40
+ function metaImpl(model, metadata) {
41
+ return new CoreModelBuilder(model).name(model[$name]).meta(metadata).build();
42
+ }
43
+ export const meta = createModelConverter(metaImpl);
44
+ //# sourceMappingURL=converters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"converters.js","sourceRoot":"","sources":["src/converters.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,KAAK,EACL,SAAS,GAUV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAsB,MAAM,aAAa,CAAC;AAiCzE,SAAS,oBAAoB,CAG3B,gBAAmB;IACnB,OAAO,CAAC,CAAC,gBAA0C,EAAE,GAAG,SAAyB,EAAE,EAAE;QACnF,IAAI,OAAO,gBAAgB,KAAK,UAAU,EAAE,CAAC;YAC3C,OAAO,CAAC,KAAY,EAAE,EAAE,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC;QACnF,CAAC;QAED,OAAO,gBAAgB,CAAC,gBAAgB,EAAE,GAAG,SAAS,CAAC,CAAC;IAC1D,CAAC,CAAQ,CAAC;AACZ,CAAC;AASD,SAAS,QAAQ,CAAwB,KAAQ;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC;AAQD,MAAM,KAAK,GAAG,QAAqB,CAAC;AACpC,OAAO,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC;AASzB,SAAS,YAAY,CAAwB,KAAQ;IACnD,OAAO,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAsB,CAAC;AACvH,CAAC;AAOD,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAkC,YAAY,CAAC,CAAC;AAS5F,SAAS,SAAS,CAAwB,KAAQ;IAChD,OAAO,IAAI,gBAAgB,CAAkB,UAAU,EAAE,GAAoB,EAAE,CAAC,EAAE,CAAC;SAChF,IAAI,CAAC,SAAS,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;SAC9B,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;SACpC,KAAK,EAAmB,CAAC;AAC9B,CAAC;AAMD,MAAM,CAAC,MAAM,KAAK,GAAG,oBAAoB,CAA4B,SAAS,CAAC,CAAC;AAEhF,SAAS,eAAe,CAEtB,KAAQ,EACR,UAAgC,EAChC,GAAG,eAAoD;IAEvD,MAAM,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,GAAG,eAAe,CAAC,CAAC;IACxD,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE,CAAC;QAC3C,aAAa,CAAC,qBAAqB,CAAC,CAAC,KAAwB,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,gBAAgB,CAAC,KAAK,CAAC;SAC/B,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAClB,MAAM,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,mBAAmB,EAAE,GAAG,cAAc,CAAC,EAAE,CAAC;SAC5E,KAAK,EAAS,CAAC;AACpB,CAAC;AAQD,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAqC,eAAe,CAAC,CAAC;AAErG,SAAS,QAAQ,CAAoC,KAAQ,EAAE,QAAuB;IACpF,OAAO,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAO,CAAC;AACpF,CAAC;AAOD,MAAM,CAAC,MAAM,IAAI,GAAG,oBAAoB,CAA8B,QAAQ,CAAC,CAAC","sourcesContent":["import {\n $assertSupportedModel,\n $constraints,\n $name,\n $optional,\n type $targetModel,\n type Constraint,\n type CompositeOf,\n type SourceModel,\n type TargetModel,\n type Model,\n type ModelConverter,\n type ModelMetadata,\n type Value,\n} from './Model.js';\nimport { CoreModelBuilder } from './modelBuilders.js';\nimport { $itemModel, ArrayModel, type OptionalModel } from './models.js';\n\n/**\n * Function that converts the given model.\n *\n * @param model - the model to convert\n * @returns derived model\n */\nexport type ModelConverterFn<SM extends Model = Model, TM extends Model = Model> = (\n model: SM,\n ...extraArgs: readonly any[]\n) => TM;\n\n/**\n * Universal converter function type for models and model converter functions.\n */\nexport type ModelConverterCallable<\n MC extends ModelConverter,\n F extends ModelConverterFn<SourceModel<MC>, TargetModel<MC, SourceModel<MC>>>,\n> = F extends (model: Model, ...extraArgs: infer E) => Model\n ? <const IMC extends ModelConverter>(modelConverter: IMC, ...extraArgs: E) => CompositeOf<MC, IMC>\n : never;\n\n/**\n * Applies HKT signature and adds model converter callback support for the given\n * simple model converter function implementation. These additions provide\n * support for self-referencing properties in object model, i. g.,\n * `.property(m.optional(m.self))`.\n *\n * @param modelConverterFn - model function implementation\n * @returns model converter that also supports other converters as arguments,\n * for which it returns a composite model converter.\n */\nfunction createModelConverter<\n const MC extends ModelConverter,\n const F extends ModelConverterFn<SourceModel<MC>, TargetModel<MC, SourceModel<MC>>>,\n>(modelConverterFn: F): MC & F & ModelConverterCallable<MC, F> {\n return ((modelOrConverter: Model | ModelConverterFn, ...extraArgs: readonly any[]) => {\n if (typeof modelOrConverter === 'function') {\n return (model: Model) => modelConverterFn(modelOrConverter(model), ...extraArgs);\n }\n\n return modelConverterFn(modelOrConverter, ...extraArgs);\n }) as any;\n}\n\n/**\n * HKT signature for model converters that return models of the same data type\n * as the given model.\n */\nexport interface IdentityOf extends ModelConverter {\n readonly [$targetModel]: SourceModel<this>;\n}\nfunction selfImpl<const M extends Model>(model: M): M {\n return model;\n}\nexport type ModelSelf = IdentityOf & typeof selfImpl;\n\n/**\n * The model converter identity function that returns the given model.\n *\n * @param model - The model to return.\n */\nconst _self = selfImpl as ModelSelf;\nexport { _self as self };\n\n/**\n * HKT signature for optional model converter, which returns optional model\n * of the given one.\n */\nexport interface OptionalOf extends ModelConverter {\n readonly [$targetModel]: OptionalModel<SourceModel<this>>;\n}\nfunction optionalImpl<const M extends Model>(model: M) {\n return new CoreModelBuilder(model).name(model[$name]).define($optional, { value: true }).build() as OptionalModel<M>;\n}\n\n/**\n * Creates a new model that represents an optional value.\n *\n * @param base - The base model to extend.\n */\nexport const optional = createModelConverter<OptionalOf, typeof optionalImpl>(optionalImpl);\n\n/**\n * HKT signature for array model converte, which returns array model with\n * items of the given model.\n */\nexport interface ArrayOf extends ModelConverter {\n readonly [$targetModel]: ArrayModel<SourceModel<this>>;\n}\nfunction arrayImpl<const M extends Model>(model: M) {\n return new CoreModelBuilder<Array<Value<M>>>(ArrayModel, (): Array<Value<M>> => [])\n .name(`Array<${model[$name]}>`)\n .define($itemModel, { value: model })\n .build() as ArrayModel<M>;\n}\n/**\n * Creates a new model that represents an array of items.\n *\n * @param itemModel - The model of the items in the array.\n */\nexport const array = createModelConverter<ArrayOf, typeof arrayImpl>(arrayImpl);\n\nfunction constrainedImpl<const M extends Model>(\n this: void,\n model: M,\n constraint: Constraint<Value<M>>,\n ...moreConstraints: ReadonlyArray<Constraint<Value<M>>>\n): M {\n const previousConstraints = model[$constraints];\n const newConstraints = [constraint, ...moreConstraints];\n for (const newConstraint of newConstraints) {\n newConstraint[$assertSupportedModel](model as Model<Value<M>>);\n }\n return new CoreModelBuilder(model)\n .name(model[$name])\n .define($constraints, { value: [...previousConstraints, ...newConstraints] })\n .build() as any;\n}\n/**\n * Applies the constraints to the given model.\n *\n * @param model - The model to apply the constraints to.\n * @param constraint - The constraint to apply.\n * @param moreConstraints - Additional constraints to apply.\n */\nexport const constrained = createModelConverter<IdentityOf, typeof constrainedImpl>(constrainedImpl);\n\nfunction metaImpl<const M extends Model>(this: void, model: M, metadata: ModelMetadata): M {\n return new CoreModelBuilder(model).name(model[$name]).meta(metadata).build() as M;\n}\n/**\n * Defines the metadata for the given model.\n *\n * @param model - The model to define metadata for.\n * @param metadata - The metadata to define.\n */\nexport const meta = createModelConverter<IdentityOf, typeof metaImpl>(metaImpl);\n"]}
package/index.d.ts CHANGED
@@ -1,25 +1,7 @@
1
- import type { EmptyObject } from 'type-fest';
2
- import { ObjectModelBuilder } from './builders.js';
3
- import { ArrayModel, EnumModel, type UnionModel } from './core.js';
4
- import { type AnyObject, type Target, type Enum, type Extensions, Model, type References, type Value } from './model.js';
5
- export * from './model.js';
6
- export * from './core.js';
7
- export type * from './builders.js';
8
- declare const m: {
9
- attach<M extends Model>(model: M, target: Target<Value<M>>): M;
10
- extend<M extends Model<AnyObject>>(base: M): ObjectModelBuilder<Value<M>, Value<M>, Extensions<M>, {
11
- named: false;
12
- selfRefKeys: References<M>;
13
- }>;
14
- optional<M extends Model>(base: M): M;
15
- array<M extends Model>(itemModel: M): ArrayModel<M>;
16
- object<T extends AnyObject>(name: string): ObjectModelBuilder<T, EmptyObject, EmptyObject, {
17
- named: true;
18
- selfRefKeys: never;
19
- }>;
20
- enum<T extends typeof Enum>(obj: T, name: string): EnumModel<T>;
21
- union<MM extends Model[]>(...members: MM): UnionModel<MM>;
22
- items<V extends Model>(model: ArrayModel<V>): Generator<V, undefined, void>;
23
- value<T>(model: Model<T>): T;
24
- };
1
+ import * as m from './m.js';
2
+ export * from './Model.js';
3
+ export * from './models.js';
4
+ export type * from './modelBuilders.js';
5
+ export type * from './converters.js';
6
+ export * from './constraints.js';
25
7
  export default m;
package/index.js CHANGED
@@ -1,72 +1,6 @@
1
- import { CoreModelBuilder, ObjectModelBuilder } from './builders.js';
2
- import { ArrayModel, EnumModel, ObjectModel } from './core.js';
3
- import { $defaultValue, $enum, $itemModel, $key, $members, $name, $optional, $owner, Model, nothing, } from './model.js';
4
- export * from './model.js';
5
- export * from './core.js';
6
- const { defineProperty } = Object;
7
- const arrayItemModels = new WeakMap();
8
- function getRawValue(model) {
9
- if (model[$owner] instanceof Model) {
10
- const parentValue = getRawValue(model[$owner]);
11
- return parentValue === nothing ? nothing : parentValue[model[$key]];
12
- }
13
- return model[$owner].value;
14
- }
15
- const m = {
16
- attach(model, target) {
17
- const _model = new CoreModelBuilder(model)
18
- .name(`@${model[$name]}`)
19
- .build();
20
- defineProperty(_model, $owner, { value: target });
21
- defineProperty(target, 'model', { enumerable: true, configurable: true, value: model });
22
- return _model;
23
- },
24
- extend(base) {
25
- return new ObjectModelBuilder(base);
26
- },
27
- optional(base) {
28
- return new CoreModelBuilder(base)
29
- .define($optional, { value: true })
30
- .build();
31
- },
32
- array(itemModel) {
33
- return new CoreModelBuilder(ArrayModel)
34
- .name(`Array<${itemModel[$name]}>`)
35
- .define($itemModel, { value: itemModel })
36
- .build();
37
- },
38
- object(name) {
39
- return new ObjectModelBuilder(ObjectModel).name(name);
40
- },
41
- enum(obj, name) {
42
- return new CoreModelBuilder(EnumModel).define($enum, { value: obj }).name(name).build();
43
- },
44
- union(...members) {
45
- return new CoreModelBuilder(Model, () => members[0][$defaultValue])
46
- .name(members.map((model) => model[$name]).join(' | '))
47
- .define($members, { value: members })
48
- .build();
49
- },
50
- *items(model) {
51
- const list = arrayItemModels.get(model) ?? [];
52
- arrayItemModels.set(model, list);
53
- const value = m.value(model);
54
- list.length = value.length;
55
- for (let i = 0; i < value.length; i++) {
56
- if (!list[i]) {
57
- list[i] = new CoreModelBuilder(model[$itemModel], () => value[i])
58
- .name(`${model[$itemModel][$name]}[${i}]`)
59
- .define($key, { value: i })
60
- .define($owner, { value: model })
61
- .build();
62
- }
63
- yield list[i];
64
- }
65
- },
66
- value(model) {
67
- const value = getRawValue(model);
68
- return value === nothing ? model[$defaultValue] : value;
69
- },
70
- };
1
+ import * as m from './m.js';
2
+ export * from './Model.js';
3
+ export * from './models.js';
4
+ export * from './constraints.js';
71
5
  export default m;
72
6
  //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EmptyObject } from 'type-fest';\nimport { CoreModelBuilder, ObjectModelBuilder } from './builders.js';\nimport { ArrayModel, EnumModel, ObjectModel, type UnionModel } from './core.js';\nimport {\n $defaultValue,\n $enum,\n $itemModel,\n $key,\n $members,\n $name,\n $optional,\n $owner,\n type AnyObject,\n type Target,\n type Enum,\n type Extensions,\n Model,\n nothing,\n type References,\n type Value,\n} from './model.js';\n\nexport * from './model.js';\nexport * from './core.js';\nexport type * from './builders.js';\n\nconst { defineProperty } = Object;\n\nconst arrayItemModels = new WeakMap<ArrayModel, Model[]>();\n\nfunction getRawValue<T>(model: Model<T>): T | typeof nothing {\n if (model[$owner] instanceof Model) {\n // If the current model is a property of another model, the owner is\n // definitely an object. So we just return the part of the value of\n // the owner.\n const parentValue = getRawValue(model[$owner] as Model<Record<keyof any, T>>);\n return parentValue === nothing ? nothing : parentValue[model[$key]];\n }\n\n // Otherwise, the owner is a Target, so we can return the full value.\n return (model[$owner] as Target<T>).value;\n}\n\n/**\n * The utility object that provides methods to create and manipulate models.\n */\nconst m = {\n /**\n * Attaches the given model to the target.\n *\n * @param model - The model to attach.\n * @param target - The target to attach the model to. It could be a Binder\n * instance, a Signal, or another object. However, it could never be another\n * model.\n */\n attach<M extends Model>(model: M, target: Target<Value<M>>): M {\n const _model = new CoreModelBuilder<Value<M>, Extensions<M>, { named: false; selfRefKeys: References<M> }>(model)\n .name(`@${model[$name]}`)\n .build();\n defineProperty(_model, $owner, { value: target });\n defineProperty(target, 'model', { enumerable: true, configurable: true, value: model });\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return _model as any;\n },\n\n /**\n * Creates a new model that extends the given base model.\n *\n * @param base - The base model to extend.\n */\n extend<M extends Model<AnyObject>>(\n base: M,\n ): ObjectModelBuilder<Value<M>, Value<M>, Extensions<M>, { named: false; selfRefKeys: References<M> }> {\n return new ObjectModelBuilder(base);\n },\n\n /**\n * Creates a new model that represents an optional value.\n *\n * @param base - The base model to extend.\n */\n optional<M extends Model>(base: M): M {\n return new CoreModelBuilder<Value<M>, Extensions<M>, { named: true; selfRefKeys: References<M> }>(base)\n .define($optional, { value: true })\n .build() as M;\n },\n\n /**\n * Creates a new model that represents an array of items.\n *\n * @param itemModel - The model of the items in the array.\n */\n array<M extends Model>(itemModel: M): ArrayModel<M> {\n return new CoreModelBuilder<Array<Value<M>>>(ArrayModel)\n .name(`Array<${itemModel[$name]}>`)\n .define($itemModel, { value: itemModel })\n .build();\n },\n\n /**\n * Creates a new model that represents an object.\n *\n * @param name - The name of the object.\n */\n object<T extends AnyObject>(\n name: string,\n ): ObjectModelBuilder<T, EmptyObject, EmptyObject, { named: true; selfRefKeys: never }> {\n return new ObjectModelBuilder(ObjectModel).name(name) as any;\n },\n\n /**\n * Creates a new model that represents an enum.\n *\n * @param obj - The enum object to represent.\n * @param name - The name of the model.\n */\n enum<T extends typeof Enum>(obj: T, name: string): EnumModel<T> {\n return new CoreModelBuilder<T[keyof T]>(EnumModel).define($enum, { value: obj }).name(name).build();\n },\n\n /**\n * Creates a new model that represents a union of the values of the given\n * models.\n *\n * @param members - The models to create the union from.\n */\n union<MM extends Model[]>(...members: MM): UnionModel<MM> {\n return new CoreModelBuilder(Model, () => members[0][$defaultValue] as Value<MM[number]>)\n .name(members.map((model) => model[$name]).join(' | '))\n .define($members, { value: members })\n .build();\n },\n\n /**\n * Iterates over the given array model yielding an item model for each item\n * the model value has.\n *\n * @param model - The array model to iterate over.\n */\n *items<V extends Model>(model: ArrayModel<V>): Generator<V, undefined, void> {\n const list = arrayItemModels.get(model) ?? [];\n arrayItemModels.set(model, list);\n const value = m.value(model);\n\n list.length = value.length;\n\n for (let i = 0; i < value.length; i++) {\n if (!list[i]) {\n list[i] = new CoreModelBuilder(model[$itemModel], () => value[i])\n .name(`${model[$itemModel][$name]}[${i}]`)\n .define($key, { value: i })\n .define($owner, { value: model })\n .build();\n }\n\n yield list[i] as V;\n }\n },\n\n /**\n * Provides the value the given model represents. For attached models it will\n * be the owner value or its part, for detached models it will be the default\n * value of the model.\n *\n * @param model - The model to get the value from.\n */\n value<T>(model: Model<T>): T {\n const value = getRawValue(model);\n\n // If the value is `nothing`, we return the default value of the model.\n return value === nothing ? model[$defaultValue] : value;\n },\n};\n\nexport default m;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAI5B,cAAc,kBAAkB,CAAC;AAEjC,eAAe,CAAC,CAAC","sourcesContent":["import * as m from './m.js';\n\nexport * from './Model.js';\nexport * from './models.js';\nexport type * from './modelBuilders.js';\nexport type * from './converters.js';\n\nexport * from './constraints.js';\n\nexport default m;\n"]}
package/m.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { EmptyObject } from 'type-fest';
2
+ import { type Constraint, type NonAttributedConstraint, Model, type Target, type Value, type Extensions, type AnyObject, type Enum } from './Model.js';
3
+ import { ObjectModelBuilder } from './modelBuilders.js';
4
+ import { type ArrayModel, EnumModel, type UnionModel, type OptionalModel } from './models.js';
5
+ export { self, optional, array, constrained, meta } from './converters.js';
6
+ export declare function attach<M extends Model>(this: void, model: M, targetProvider: () => Target<Value<M>>): M;
7
+ export declare function extend<M extends Model<AnyObject>>(this: void, base: M): ObjectModelBuilder<Value<M>, Value<M>, Extensions<M>>;
8
+ export declare function object<T extends AnyObject>(this: void, name: string): ObjectModelBuilder<T, AnyObject, AnyObject, {
9
+ named: true;
10
+ }>;
11
+ declare function _enum<T extends typeof Enum>(this: void, obj: T, name: string): EnumModel<T>;
12
+ export { _enum as enum };
13
+ export declare function union<MM extends Model[]>(this: void, ...members: MM): UnionModel<MM>;
14
+ export declare function getValue<T>(this: void, model: Model<T>): T;
15
+ export declare function items<V extends Model>(this: void, model: ArrayModel<V> | OptionalModel<ArrayModel<V>>): Generator<V, undefined, void>;
16
+ export declare function withDefaultValue<const M extends Model>(this: void, model: M, defaultValue: Value<M>): M;
17
+ export declare function isConstraint<V, N extends string = string, A extends AnyObject = EmptyObject>(this: void, arg: unknown, constraintType: NonAttributedConstraint<V, N, A>): arg is Constraint<V, N, A>;
package/m.js ADDED
@@ -0,0 +1,76 @@
1
+ import { Model, $owner, nothing, $key, $name, $defaultValue, $members, } from './Model.js';
2
+ import { CoreModelBuilder, ObjectModelBuilder } from './modelBuilders.js';
3
+ import { EnumModel, ObjectModel, $itemModel, $enum, } from './models.js';
4
+ const { defineProperty } = Object;
5
+ const arrayItemModels = new WeakMap();
6
+ function getRawValue(model) {
7
+ if (model[$owner] instanceof Model) {
8
+ const parentValue = getRawValue(model[$owner]);
9
+ return parentValue === nothing ? nothing : parentValue[model[$key]];
10
+ }
11
+ return model[$owner].value;
12
+ }
13
+ export { self, optional, array, constrained, meta } from './converters.js';
14
+ export function attach(model, targetProvider) {
15
+ const _model = new CoreModelBuilder(model)
16
+ .name(`@${model[$name]}`)
17
+ .build();
18
+ defineProperty(_model, $owner, { get: targetProvider });
19
+ return _model;
20
+ }
21
+ export function extend(base) {
22
+ return new ObjectModelBuilder(base);
23
+ }
24
+ export function object(name) {
25
+ return new ObjectModelBuilder(ObjectModel).name(name);
26
+ }
27
+ function _enum(obj, name) {
28
+ return new CoreModelBuilder(EnumModel).define($enum, { value: obj }).name(name).build();
29
+ }
30
+ export { _enum as enum };
31
+ export function union(...members) {
32
+ return new CoreModelBuilder(Model, () => members[0][$defaultValue])
33
+ .name(members.map((model) => model[$name]).join(' | '))
34
+ .define($members, { value: members })
35
+ .define(Symbol.hasInstance, {
36
+ value: (v) => members.some((member) => v instanceof member),
37
+ })
38
+ .build();
39
+ }
40
+ export function getValue(model) {
41
+ const v = getRawValue(model);
42
+ return v === nothing ? model[$defaultValue] : v;
43
+ }
44
+ export function* items(model) {
45
+ const list = arrayItemModels.get(model) ?? [];
46
+ arrayItemModels.set(model, list);
47
+ const v = getValue(model) ?? [];
48
+ list.length = v.length;
49
+ for (let i = 0; i < v.length; i++) {
50
+ if (!list[i]) {
51
+ list[i] = new CoreModelBuilder(model[$itemModel], () => v[i])
52
+ .name(`${model[$itemModel][$name]}[${i}]`)
53
+ .define($key, { value: i })
54
+ .define($owner, { value: model })
55
+ .build();
56
+ }
57
+ yield list[i];
58
+ }
59
+ }
60
+ export function withDefaultValue(model, defaultValue) {
61
+ return new CoreModelBuilder(model, () => defaultValue).name(model[$name]).build();
62
+ }
63
+ export function isConstraint(arg, constraintType) {
64
+ let p = arg;
65
+ do {
66
+ if (p === constraintType) {
67
+ return true;
68
+ }
69
+ if (typeof p !== 'object') {
70
+ return false;
71
+ }
72
+ p = Object.getPrototypeOf(p);
73
+ } while (p !== undefined && p !== null);
74
+ return false;
75
+ }
76
+ //# sourceMappingURL=m.js.map
package/m.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"m.js","sourceRoot":"","sources":["src/m.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EmptyObject } from 'type-fest';\nimport {\n type Constraint,\n type NonAttributedConstraint,\n Model,\n $owner,\n nothing,\n $key,\n type Target,\n type Value,\n type Extensions,\n $name,\n type AnyObject,\n type Enum,\n $defaultValue,\n $members,\n} from './Model.js';\nimport { CoreModelBuilder, ObjectModelBuilder } from './modelBuilders.js';\nimport {\n type ArrayModel,\n EnumModel,\n ObjectModel,\n type UnionModel,\n $itemModel,\n type OptionalModel,\n $enum,\n} from './models.js';\n\nconst { defineProperty } = Object;\n\nconst arrayItemModels = new WeakMap<ArrayModel | OptionalModel<ArrayModel>, Model[]>();\n\nfunction getRawValue<T>(model: Model<T>): T | typeof nothing {\n if (model[$owner] instanceof Model) {\n // If the current model is a property of another model, the owner is\n // definitely an object. So we just return the part of the value of\n // the owner.\n const parentValue = getRawValue(model[$owner] as Model<Record<keyof any, T>>);\n return parentValue === nothing ? nothing : parentValue[model[$key]];\n }\n\n // Otherwise, the owner is a Target, so we can return the full value.\n return (model[$owner] as Target<T>).value;\n}\n\nexport { self, optional, array, constrained, meta } from './converters.js';\n\n/**\n * Attaches the given model to the target.\n *\n * @param model - The model to attach.\n * @param targetProvider - The target to attach the model to. It could be a Binder\n * instance, a Signal, or another object. However, it could never be another\n * model.\n */\nexport function attach<M extends Model>(this: void, model: M, targetProvider: () => Target<Value<M>>): M {\n const _model = new CoreModelBuilder<Value<M>, Extensions<M>, { named: false }>(model)\n .name(`@${model[$name]}`)\n .build();\n defineProperty(_model, $owner, { get: targetProvider });\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return _model as any;\n}\n\n/**\n * Creates a new model that extends the given base model.\n *\n * @param base - The base model to extend.\n */\nexport function extend<M extends Model<AnyObject>>(\n this: void,\n base: M,\n): ObjectModelBuilder<Value<M>, Value<M>, Extensions<M>> {\n return new ObjectModelBuilder(base);\n}\n\n/**\n * Creates a new model that represents an object.\n *\n * @param name - The name of the object.\n */\nexport function object<T extends AnyObject>(\n this: void,\n name: string,\n): ObjectModelBuilder<T, AnyObject, AnyObject, { named: true }> {\n return new ObjectModelBuilder(ObjectModel).name(name) as any;\n}\n\n/**\n * Creates a new model that represents an enum.\n *\n * @param obj - The enum object to represent.\n * @param name - The name of the model.\n */\nfunction _enum<T extends typeof Enum>(this: void, obj: T, name: string): EnumModel<T> {\n return new CoreModelBuilder<T[keyof T]>(EnumModel).define($enum, { value: obj }).name(name).build();\n}\nexport { _enum as enum };\n\n/**\n * Creates a new model that represents a union of the values of the given\n * models.\n *\n * @param members - The models to create the union from.\n */\nexport function union<MM extends Model[]>(this: void, ...members: MM): UnionModel<MM> {\n return new CoreModelBuilder(Model, () => members[0][$defaultValue] as Value<MM[number]>)\n .name(members.map((model) => model[$name]).join(' | '))\n .define($members, { value: members })\n .define(Symbol.hasInstance, {\n value: (v: unknown): v is Value<MM[number]> => members.some((member) => v instanceof member),\n })\n .build();\n}\n\n/**\n * Provides the value the given model represents. For attached models it will\n * be the owner value or its part, for detached models it will be the default\n * value of the model.\n *\n * @param model - The model to get the value from.\n */\nexport function getValue<T>(this: void, model: Model<T>): T {\n const v = getRawValue(model);\n\n // If the value is `nothing`, we return the default value of the model.\n return v === nothing ? model[$defaultValue] : v;\n}\n\n/**\n * Iterates over the given array model yielding an item model for each item\n * the model value has.\n *\n * @param model - The array model to iterate over.\n */\nexport function* items<V extends Model>(\n this: void,\n model: ArrayModel<V> | OptionalModel<ArrayModel<V>>,\n): Generator<V, undefined, void> {\n const list = arrayItemModels.get(model) ?? [];\n arrayItemModels.set(model, list);\n const v = getValue(model) ?? [];\n\n list.length = v.length;\n\n for (let i = 0; i < v.length; i++) {\n if (!list[i]) {\n list[i] = new CoreModelBuilder(model[$itemModel], () => v[i])\n .name(`${model[$itemModel][$name]}[${i}]`)\n .define($key, { value: i })\n .define($owner, { value: model })\n .build();\n }\n\n yield list[i] as V;\n }\n}\n\n/**\n * Replaces the default value of the given model with the given value.\n *\n * @param model - The model to replace the default value of.\n * @param defaultValue - The new default value.\n */\nexport function withDefaultValue<const M extends Model>(this: void, model: M, defaultValue: Value<M>): M {\n return new CoreModelBuilder(model, () => defaultValue).name(model[$name]).build() as M;\n}\n\n/**\n * Checks if the given value is a constraint of the given type.\n *\n * @param arg - The argument to check.\n * @param constraintType - The constraint to check against.\n */\nexport function isConstraint<V, N extends string = string, A extends AnyObject = EmptyObject>(\n this: void,\n arg: unknown,\n constraintType: NonAttributedConstraint<V, N, A>,\n): arg is Constraint<V, N, A> {\n let p: unknown = arg;\n do {\n if (p === constraintType) {\n return true;\n }\n if (typeof p !== 'object') {\n return false;\n }\n p = Object.getPrototypeOf(p);\n } while (p !== undefined && p !== null);\n return false;\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import { type AnyObject, type DefaultValueProvider, type TargetModel, type Model, type ModelConverter, type ModelMetadata } from './Model.js';
2
+ import type { ObjectModel } from './models.js';
3
+ declare const $model: unique symbol;
4
+ export type Flags = {
5
+ named: boolean;
6
+ };
7
+ export declare class CoreModelBuilder<V, EX extends AnyObject = AnyObject, F extends Flags = {
8
+ named: false;
9
+ }> {
10
+ protected readonly [$model]: Model<V, EX>;
11
+ constructor(base: Model, defaultValueProvider?: (model: Model<V, EX>) => V);
12
+ meta(value: ModelMetadata): this;
13
+ define<const DK extends symbol, const DV>(key: DK, value: TypedPropertyDescriptor<DV>): CoreModelBuilder<V, {
14
+ readonly [key in keyof EX | DK]: key extends DK ? DV : key extends keyof EX ? EX[key] : never;
15
+ }, F>;
16
+ defaultValueProvider(defaultValueProvider: DefaultValueProvider<V, EX>): this;
17
+ name(name: string): CoreModelBuilder<V, EX, {
18
+ named: true;
19
+ }>;
20
+ build(this: F['named'] extends true ? this : never): AnyObject extends EX ? Model<V> : Model<V, EX>;
21
+ }
22
+ export declare class ObjectModelBuilder<V extends AnyObject, CV extends AnyObject = AnyObject, EX extends AnyObject = AnyObject, F extends Flags = {
23
+ named: false;
24
+ }> extends CoreModelBuilder<V, {
25
+ readonly [K in keyof EX]: EX[K] extends ModelConverter ? TargetModel<EX[K], ObjectModel<V, EX>> : EX[K];
26
+ }, F> {
27
+ constructor(base: Model);
28
+ object<NV extends V>(this: F['named'] extends false ? this : never, name: string): ObjectModelBuilder<NV, CV, EX, {
29
+ named: true;
30
+ }>;
31
+ ['define']: <const DK extends symbol, DV>(key: DK, value: TypedPropertyDescriptor<DV>) => ObjectModelBuilder<V, CV, {
32
+ readonly [key in keyof EX | DK]: key extends DK ? DV : key extends keyof EX ? EX[key] : never;
33
+ }, F>;
34
+ ['meta']: (value: ModelMetadata) => this;
35
+ property<const PK extends string & keyof V, const M extends Model<V[PK]>>(key: PK, model: M): ObjectModelBuilder<V, {
36
+ readonly [key in keyof CV | PK]: key extends PK ? V[PK] : key extends keyof CV ? CV[key] : never;
37
+ }, {
38
+ readonly [key in keyof EX | PK]: key extends PK ? M : key extends keyof EX ? EX[key] : never;
39
+ }, F>;
40
+ property<const PK extends string & keyof V, const MC extends ModelConverter>(key: PK, model: MC): ObjectModelBuilder<V, {
41
+ readonly [key in keyof CV | PK]: key extends PK ? V[PK] : key extends keyof CV ? CV[key] : never;
42
+ }, {
43
+ readonly [key in keyof EX | PK]: key extends PK ? MC : key extends keyof EX ? EX[key] : never;
44
+ }, F>;
45
+ build: (this: F['named'] extends true ? (CV extends V ? this : never) : never) => ObjectModel<V, EX>;
46
+ }
47
+ export {};
@@ -1,4 +1,4 @@
1
- import { $defaultValue, $key, $meta, $name, $owner, } from './model.js';
1
+ import { $defaultValue, $key, $meta, $name, $optional, $owner, } from './Model.js';
2
2
  const { create, defineProperty } = Object;
3
3
  const $model = Symbol('model');
4
4
  export class CoreModelBuilder {
@@ -36,12 +36,10 @@ const propertyRegistry = new WeakMap();
36
36
  export class ObjectModelBuilder extends CoreModelBuilder {
37
37
  constructor(base) {
38
38
  super(base, (m) => {
39
- const result = create(null);
39
+ const result = {};
40
40
  for (const key in m) {
41
- defineProperty(result, key, {
42
- enumerable: true,
43
- get: () => m[key][$defaultValue],
44
- });
41
+ const keyModel = m[key];
42
+ result[key] = keyModel[$optional] ? undefined : keyModel[$defaultValue];
45
43
  }
46
44
  return result;
47
45
  });
@@ -49,7 +47,7 @@ export class ObjectModelBuilder extends CoreModelBuilder {
49
47
  object(name) {
50
48
  return this.name(name);
51
49
  }
52
- property(key, model, options) {
50
+ property(key, model) {
53
51
  defineProperty(this[$model], key, {
54
52
  enumerable: true,
55
53
  get() {
@@ -60,7 +58,6 @@ export class ObjectModelBuilder extends CoreModelBuilder {
60
58
  props[key] ??= new CoreModelBuilder(typeof model === 'function' ? model(this) : model)
61
59
  .define($key, { value: key })
62
60
  .define($owner, { value: this })
63
- .define($meta, { value: options?.meta })
64
61
  .build();
65
62
  return props[key];
66
63
  },
@@ -68,4 +65,4 @@ export class ObjectModelBuilder extends CoreModelBuilder {
68
65
  return this;
69
66
  }
70
67
  }
71
- //# sourceMappingURL=builders.js.map
68
+ //# sourceMappingURL=modelBuilders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelBuilders.js","sourceRoot":"","sources":["src/modelBuilders.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/consistent-indexed-object-style */\nimport {\n $defaultValue,\n $key,\n $meta,\n $name,\n $optional,\n $owner,\n type AnyObject,\n type DefaultValueProvider,\n type TargetModel,\n type Model,\n type ModelConverter,\n type ModelMetadata,\n} from './Model.js';\nimport type { ObjectModel } from './models.js';\n\nconst { create, defineProperty } = Object;\n\nconst $model = Symbol('model');\n\n/**\n * The flags for the model constructor that allow to determine specific characteristics of the model.\n */\nexport type Flags = {\n /**\n * Defines if the model is named.\n */\n named: boolean;\n};\n\n/**\n * A builder class for creating all basic models.\n * @internal low-level API for internal use only.\n *\n * @typeParam V - The final value type of the model.\n * @typeParam EX - The extra properties of the model.\n * @typeParam F - The flags for the model constructor that allow to determine\n * specific characteristics of the model.\n */\nexport class CoreModelBuilder<V, EX extends AnyObject = AnyObject, F extends Flags = { named: false }> {\n protected readonly [$model]: Model<V, EX>;\n\n /**\n * @param base - The base model to extend.\n * @param defaultValueProvider - The function that provides the default value\n * for the model.\n */\n constructor(base: Model, defaultValueProvider?: (model: Model<V, EX>) => V) {\n this[$model] = create(base);\n\n if (defaultValueProvider) {\n this.defaultValueProvider(defaultValueProvider);\n }\n }\n\n /**\n * Appends metadata to the model.\n *\n * @param value - The metadata to append.\n * @returns The current builder instance.\n */\n meta(value: ModelMetadata): this {\n this.define($meta, { value });\n return this;\n }\n\n /**\n * Defines a new property on the model. The property serves the purposes of\n * storing the extra data for specific types of models.\n *\n * @remarks\n * The key of the property should be a symbol to avoid conflicts with\n * properties defined via\n * {@link ObjectModelBuilder}.\n *\n * @param key - The key of the property.\n * @param value - The descriptor of the property.\n * @returns The current builder instance.\n */\n define<const DK extends symbol, const DV>(\n key: DK,\n value: TypedPropertyDescriptor<DV>,\n ): CoreModelBuilder<\n V,\n {\n readonly [key in keyof EX | DK]: key extends DK ? DV : key extends keyof EX ? EX[key] : never;\n },\n F\n > {\n defineProperty(this[$model], key, value);\n return this as any;\n }\n\n /**\n * Sets the default value provider for the model. This is an alternative way\n * to provide the default value for the model if for some reason using the\n * constructor parameter is undesired.\n *\n * @param defaultValueProvider - The function that provides the default value\n * for the model.\n * @returns The current builder instance.\n */\n defaultValueProvider(defaultValueProvider: DefaultValueProvider<V, EX>): this {\n this.define($defaultValue, {\n get(this: Model<V, EX>) {\n return defaultValueProvider(this);\n },\n });\n return this;\n }\n\n /**\n * Sets the name of the model. The name is used for debugging purposes and is\n * displayed in the string representation. Setting the name is required;\n * otherwise, the {@link CoreModelBuilder.build} method won't be available.\n *\n * @param name - The name of the model.\n * @returns The current builder instance.\n */\n name(name: string): CoreModelBuilder<V, EX, { named: true }> {\n return this.define($name, { value: name }) as any;\n }\n\n /**\n * Builds the model. On the typing level, it checks if all the model parts are\n * set correctly and raises an error if not.\n *\n * @returns The model.\n */\n build(this: F['named'] extends true ? this : never): AnyObject extends EX ? Model<V> : Model<V, EX> {\n return this[$model];\n }\n}\n\n/**\n * A registry for the property models of the object model. Since the property\n * registration is lazy, we cannot store the property models directly on the\n * object model, so the registry plays a role of a private storage for them.\n *\n * @internal\n */\nconst propertyRegistry = new WeakMap<Model, Record<string, Model>>();\n\n/**\n * A builder class for creating object models.\n * @internal low-level API for internal use only.\n *\n * @typeParam V - The final value type of the model.\n * @typeParam CV - The current value type of the model. It changes as the model\n * is being built and defines if the\n * {@link ObjectModelBuilder.build} method can be called.\n * @typeParam EX - The extra properties of the model.\n * @typeParam F - The flags for the model constructor that allow to determine\n * specific characteristics of the model.\n */\nexport class ObjectModelBuilder<\n V extends AnyObject,\n CV extends AnyObject = AnyObject,\n EX extends AnyObject = AnyObject,\n F extends Flags = { named: false },\n> extends CoreModelBuilder<\n V,\n {\n readonly [K in keyof EX]: EX[K] extends ModelConverter ? TargetModel<EX[K], ObjectModel<V, EX>> : EX[K];\n },\n F\n> {\n constructor(base: Model) {\n super(base, (m) => {\n const result: Record<string, unknown> = {};\n\n // eslint-disable-next-line no-restricted-syntax\n for (const key in m) {\n const keyModel = m[key as keyof Model<V, EX>] as Model;\n result[key] = keyModel[$optional] ? undefined : keyModel[$defaultValue];\n // defineProperty(result, key, {\n // enumerable: true,\n // value: keyModel[$optional] ? undefined : keyModel[$defaultValue],\n // });\n }\n\n return result as V;\n });\n }\n\n /**\n * The method that should follow the {@link m.extend} method. It allows to\n * declare the extension for the model and properly name it.\n *\n * @param name - The name of the model.\n */\n object<NV extends V>(\n this: F['named'] extends false ? this : never,\n name: string,\n ): ObjectModelBuilder<NV, CV, EX, { named: true }> {\n return this.name(name) as any;\n }\n\n /**\n * {@inheritDoc CoreModelBuilder.define}\n */\n // @ts-ignore: TypeScript has difficulties with the override type here\n declare ['define']: <const DK extends symbol, DV>(\n key: DK,\n value: TypedPropertyDescriptor<DV>,\n ) => ObjectModelBuilder<\n V,\n CV,\n {\n readonly [key in keyof EX | DK]: key extends DK ? DV : key extends keyof EX ? EX[key] : never;\n },\n F\n >;\n\n /**\n * {@inheritDoc CoreModelBuilder.meta}\n */\n declare ['meta']: (value: ModelMetadata) => this;\n\n /**\n * Defines a new model property on the model. Unlike the\n * {@link ObjectModelBuilder.define}, this property is public and allows the\n * user to interact with the model data structure. It also updates the current\n * value type of the model to make it closer to the final value type.\n *\n * @param key - The key of the property.\n * @param model - The model of the property value. You can also provide a\n * function that produces the model based on the current model.\n *\n * @returns The current builder instance updated with the new property type.\n */\n property<const PK extends string & keyof V, const M extends Model<V[PK]>>(\n key: PK,\n model: M,\n ): ObjectModelBuilder<\n V,\n {\n readonly [key in keyof CV | PK]: key extends PK ? V[PK] : key extends keyof CV ? CV[key] : never;\n },\n {\n readonly [key in keyof EX | PK]: key extends PK ? M : key extends keyof EX ? EX[key] : never;\n },\n F\n >;\n property<const PK extends string & keyof V, const MC extends ModelConverter>(\n key: PK,\n model: MC,\n ): ObjectModelBuilder<\n V,\n {\n readonly [key in keyof CV | PK]: key extends PK ? V[PK] : key extends keyof CV ? CV[key] : never;\n },\n {\n readonly [key in keyof EX | PK]: key extends PK ? MC : key extends keyof EX ? EX[key] : never;\n },\n F\n >;\n property<PK extends string & keyof V, M extends Model<V[PK]>>(\n key: PK,\n model: M | ((model: Model<V, EX>) => M),\n ): unknown {\n defineProperty(this[$model], key, {\n enumerable: true,\n get(this: Model<V, EX & { readonly [key in PK]: M }>) {\n if (!propertyRegistry.has(this)) {\n propertyRegistry.set(this, {});\n }\n\n const props = propertyRegistry.get(this)!;\n\n props[key] ??= new CoreModelBuilder<V[PK], object, { named: true }>(\n typeof model === 'function' ? model(this) : model,\n )\n .define($key, { value: key })\n .define($owner, { value: this })\n .build();\n\n return props[key];\n },\n });\n\n return this;\n }\n\n /**\n * {@inheritDoc CoreModelBuilder.build}\n */\n declare build: (this: F['named'] extends true ? (CV extends V ? this : never) : never) => ObjectModel<V, EX>;\n}\n"]}
package/models.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import { type $defaultValue, type $members, type AnyObject, type Enum, type Extensions, type TargetModel, Model, type ModelConverter, type Value } from './Model.js';
2
+ export type PrimitiveModel<V = unknown> = Model<V>;
3
+ export declare const PrimitiveModel: Model<unknown>;
4
+ export type StringModel = PrimitiveModel<string>;
5
+ export declare const StringModel: Model<string>;
6
+ export type NumberModel = PrimitiveModel<number>;
7
+ export declare const NumberModel: Model<number>;
8
+ export type BooleanModel = PrimitiveModel<boolean>;
9
+ export declare const BooleanModel: Model<boolean>;
10
+ export declare const $itemModel: unique symbol;
11
+ export type ArrayModel<M extends Model = Model> = Model<Array<Value<M>>, {
12
+ readonly [$itemModel]: M;
13
+ }>;
14
+ export declare const ArrayModel: ArrayModel;
15
+ export type ObjectModel<V, EX extends AnyObject = AnyObject> = Model<V, {
16
+ readonly [K in keyof EX]: EX[K] extends ModelConverter ? TargetModel<EX[K], ObjectModel<V, EX>> : EX[K];
17
+ }>;
18
+ export declare const ObjectModel: Model<Readonly<Record<never, never>>>;
19
+ export type RecordModel<K extends string, V> = Model<Record<K, V>>;
20
+ export declare const RecordModel: Model<Record<string, unknown>>;
21
+ export declare const $enum: unique symbol;
22
+ export type EnumModel<T extends typeof Enum> = Model<T[keyof T], {
23
+ readonly [$enum]: T;
24
+ }>;
25
+ export declare const EnumModel: EnumModel<typeof Enum>;
26
+ export type UnionModel<MM extends Model[]> = Model<Value<MM[number]>, {
27
+ readonly [$members]: MM;
28
+ }>;
29
+ export type OptionalModel<M extends Model> = Model<M[typeof $defaultValue] | undefined, Extensions<M> & {
30
+ readonly $optional: true;
31
+ }>;
@@ -1,17 +1,22 @@
1
- import { CoreModelBuilder } from './builders.js';
2
- import { $enum, $itemModel, Model } from './model.js';
1
+ import { Model, } from './Model.js';
2
+ import { CoreModelBuilder } from './modelBuilders.js';
3
3
  export const PrimitiveModel = new CoreModelBuilder(Model, () => undefined).name('primitive').build();
4
4
  export const StringModel = new CoreModelBuilder(PrimitiveModel, () => '').name('string').build();
5
- export const NumberModel = new CoreModelBuilder(PrimitiveModel, () => 0).name('number').build();
5
+ export const NumberModel = new CoreModelBuilder(PrimitiveModel, () => NaN).name('number').build();
6
6
  export const BooleanModel = new CoreModelBuilder(PrimitiveModel, () => false).name('boolean').build();
7
+ export const $itemModel = Symbol('itemModel');
7
8
  export const ArrayModel = new CoreModelBuilder(Model, () => [])
8
9
  .name('Array')
9
10
  .define($itemModel, { value: Model })
10
11
  .build();
11
12
  export const ObjectModel = new CoreModelBuilder(Model, () => ({})).name('Object').build();
13
+ export const RecordModel = new CoreModelBuilder(ObjectModel, () => ({}))
14
+ .name('Record')
15
+ .build();
16
+ export const $enum = Symbol('enumerate');
12
17
  export const EnumModel = new CoreModelBuilder(Model)
13
18
  .name('Enum')
14
19
  .define($enum, { value: {} })
15
20
  .defaultValueProvider((self) => Object.values(self[$enum])[0])
16
21
  .build();
17
- //# sourceMappingURL=core.js.map
22
+ //# sourceMappingURL=models.js.map
package/models.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["src/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,GAGN,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAMtD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,GAAY,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;AAM9G,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;AAMjG,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;AAMlG,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;AAKtG,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAW9C,MAAM,CAAC,MAAM,UAAU,GAAe,IAAI,gBAAgB,CAAC,KAAK,EAAE,GAAc,EAAE,CAAC,EAAE,CAAC;KACnF,IAAI,CAAC,OAAO,CAAC;KACb,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KACpC,KAAK,EAAE,CAAC;AAWX,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,GAAc,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;AAQrG,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,WAAW,EAAE,GAA4B,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;KAC9F,IAAI,CAAC,QAAQ,CAAC;KACd,KAAK,EAAE,CAAC;AAKX,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAYzC,MAAM,CAAC,MAAM,SAAS,GAA2B,IAAI,gBAAgB,CAAmC,KAAK,CAAC;KAC3G,IAAI,CAAC,MAAM,CAAC;KAEZ,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAiB,EAAE,CAAC;KAC3C,oBAAoB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC7D,KAAK,EAAE,CAAC","sourcesContent":["import {\n type $defaultValue,\n type $members,\n type AnyObject,\n type Enum,\n type Extensions,\n type TargetModel,\n Model,\n type ModelConverter,\n type Value,\n} from './Model.js';\nimport { CoreModelBuilder } from './modelBuilders.js';\n\n/**\n * The model of a primitive value, like `string`, `number` or `boolean`.\n */\nexport type PrimitiveModel<V = unknown> = Model<V>;\nexport const PrimitiveModel = new CoreModelBuilder(Model, (): unknown => undefined).name('primitive').build();\n\n/**\n * The model of a string value.\n */\nexport type StringModel = PrimitiveModel<string>;\nexport const StringModel = new CoreModelBuilder(PrimitiveModel, () => '').name('string').build();\n\n/**\n * The model of a number value.\n */\nexport type NumberModel = PrimitiveModel<number>;\nexport const NumberModel = new CoreModelBuilder(PrimitiveModel, () => NaN).name('number').build();\n\n/**\n * The model of a boolean value.\n */\nexport type BooleanModel = PrimitiveModel<boolean>;\nexport const BooleanModel = new CoreModelBuilder(PrimitiveModel, () => false).name('boolean').build();\n\n/**\n * The symbol that represents the ArrayModel item property.\n */\nexport const $itemModel = Symbol('itemModel');\n\n/**\n * The model of array data.\n */\nexport type ArrayModel<M extends Model = Model> = Model<\n Array<Value<M>>,\n {\n readonly [$itemModel]: M;\n }\n>;\nexport const ArrayModel: ArrayModel = new CoreModelBuilder(Model, (): unknown[] => [])\n .name('Array')\n .define($itemModel, { value: Model })\n .build();\n\n/**\n * The model of object data.\n */\nexport type ObjectModel<V, EX extends AnyObject = AnyObject> = Model<\n V,\n {\n readonly [K in keyof EX]: EX[K] extends ModelConverter ? TargetModel<EX[K], ObjectModel<V, EX>> : EX[K];\n }\n>;\nexport const ObjectModel = new CoreModelBuilder(Model, (): AnyObject => ({})).name('Object').build();\n\n/**\n * The model of a `Record<string, V>` data, which is a special case\n * of `ObjectModel<Record<string, V>>` that is used to represent an arbitrary\n * object with string keys, such as a Java `Map<String, Object>`.\n */\nexport type RecordModel<K extends string, V> = Model<Record<K, V>>;\nexport const RecordModel = new CoreModelBuilder(ObjectModel, (): Record<string, unknown> => ({}))\n .name('Record')\n .build();\n\n/**\n * The symbol that represents the `EnumModel[$enumerate]` property.\n */\nexport const $enum = Symbol('enumerate');\n\n/**\n * The model of enum data.\n */\nexport type EnumModel<T extends typeof Enum> = Model<\n T[keyof T],\n {\n readonly [$enum]: T;\n }\n>;\n\nexport const EnumModel: EnumModel<typeof Enum> = new CoreModelBuilder<(typeof Enum)[keyof typeof Enum]>(Model)\n .name('Enum')\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n .define($enum, { value: {} as typeof Enum })\n .defaultValueProvider((self) => Object.values(self[$enum])[0])\n .build();\n\n/**\n * The model of a union data.\n */\nexport type UnionModel<MM extends Model[]> = Model<Value<MM[number]>, { readonly [$members]: MM }>;\n\n/**\n * The model of an optional type.\n */\nexport type OptionalModel<M extends Model> = Model<\n M[typeof $defaultValue] | undefined,\n Extensions<M> & {\n readonly $optional: true;\n }\n>;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/hilla-models",
3
- "version": "25.0.5",
3
+ "version": "25.1.0-alpha10",
4
4
  "description": "Generative form models for Hilla",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -42,9 +42,7 @@
42
42
  "publishConfig": {
43
43
  "access": "public"
44
44
  },
45
- "dependencies": {
46
- "@vaadin/hilla-lit-form": "25.0.5"
47
- },
45
+ "dependencies": {},
48
46
  "peerDependencies": {
49
47
  "react": "18 || 19",
50
48
  "react-dom": "18 || 19"
package/builders.d.ts DELETED
@@ -1,44 +0,0 @@
1
- import type { EmptyObject } from 'type-fest';
2
- import type { ObjectModel } from './core';
3
- import { type AnyObject, type DefaultValueProvider, type Model, type ModelMetadata } from './model.js';
4
- export type ModelBuilderPropertyOptions = Readonly<{
5
- meta?: ModelMetadata;
6
- }>;
7
- declare const $model: unique symbol;
8
- export type Flags = {
9
- named: boolean;
10
- selfRefKeys: keyof any;
11
- };
12
- export declare class CoreModelBuilder<V, EX extends AnyObject = EmptyObject, F extends Flags = {
13
- named: false;
14
- selfRefKeys: never;
15
- }> {
16
- protected readonly [$model]: Model<V, EX>;
17
- constructor(base: Model, defaultValueProvider?: (model: Model<V, EX>) => V);
18
- meta(value: ModelMetadata): this;
19
- define<DK extends symbol, DV>(key: DK, value: TypedPropertyDescriptor<DV>): CoreModelBuilder<V, EX & Readonly<Record<DK, DV>>, F>;
20
- defaultValueProvider(defaultValueProvider: DefaultValueProvider<V, EX>): this;
21
- name(name: string): CoreModelBuilder<V, EX, {
22
- named: true;
23
- selfRefKeys: F['selfRefKeys'];
24
- }>;
25
- build(this: F['named'] extends true ? this : never): Model<V, EX>;
26
- }
27
- export declare class ObjectModelBuilder<V extends AnyObject, CV extends AnyObject = EmptyObject, EX extends AnyObject = EmptyObject, F extends Flags = {
28
- named: false;
29
- selfRefKeys: never;
30
- }> extends CoreModelBuilder<V, EX, F> {
31
- constructor(base: Model);
32
- object<NV extends AnyObject>(this: F['named'] extends false ? this : never, name: string): ObjectModelBuilder<NV & V, CV, EX, {
33
- named: true;
34
- selfRefKeys: F['selfRefKeys'];
35
- }>;
36
- ['define']: <DK extends symbol, DV>(key: DK, value: TypedPropertyDescriptor<DV>) => ObjectModelBuilder<V, CV, EX & Readonly<Record<DK, DV>>, F>;
37
- ['meta']: (value: ModelMetadata) => this;
38
- property<PK extends string & keyof V, EXK extends AnyObject = EmptyObject>(key: PK, model: Model<V[PK], EXK> | ((model: Model<V, EX & Readonly<Record<PK, Model<V[PK], EXK>>>>) => Model<V[PK], EXK>), options?: ModelBuilderPropertyOptions): Extract<V[PK], V> extends never ? ObjectModelBuilder<V, CV & Readonly<Record<PK, V[PK]>>, EX & Readonly<Record<PK, Model<V[PK], EXK>>>, F> : ObjectModelBuilder<V, CV & Readonly<Record<PK, V[PK]>>, EX, {
39
- named: F['named'];
40
- selfRefKeys: F['selfRefKeys'] | PK;
41
- }>;
42
- build: (this: F['named'] extends true ? (CV extends V ? this : never) : never) => ObjectModel<V, EX, F['selfRefKeys']>;
43
- }
44
- export {};
package/builders.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"builders.js","sourceRoot":"","sources":["src/builders.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EmptyObject } from 'type-fest';\nimport type { ObjectModel } from './core';\nimport {\n $defaultValue,\n $key,\n $meta,\n $name,\n $owner,\n type AnyObject,\n type DefaultValueProvider,\n type Model,\n type ModelMetadata,\n} from './model.js';\n\nconst { create, defineProperty } = Object;\n\n/**\n * The options for creating the model property.\n */\nexport type ModelBuilderPropertyOptions = Readonly<{\n meta?: ModelMetadata;\n}>;\n\nconst $model = Symbol('model');\n\n/**\n * The flags for the model constructor that allow to determine specific characteristics of the model.\n */\nexport type Flags = {\n /**\n * Defines if the model is named.\n */\n named: boolean;\n\n /**\n * The keys of the self-referencing properties.\n *\n * @remarks\n * The problem of self-reference models is that they cannot have intermediate\n * type because during the property definition, the model itself is in the\n * middle of construction. That's why we define the specific type-only flag\n * that allows us to know which model properties are self-referenced. We can\n * safely set it in the end of building using this flag.\n */\n selfRefKeys: keyof any;\n};\n\n/**\n * A builder class for creating all basic models.\n *\n * @typeParam V - The final value type of the model.\n * @typeParam EX - The extra properties of the model.\n * @typeParam F - The flags for the model constructor that allow to determine\n * specific characteristics of the model.\n */\nexport class CoreModelBuilder<\n V,\n EX extends AnyObject = EmptyObject,\n F extends Flags = { named: false; selfRefKeys: never },\n> {\n protected readonly [$model]: Model<V, EX>;\n\n /**\n * @param base - The base model to extend.\n * @param defaultValueProvider - The function that provides the default value\n * for the model.\n */\n constructor(base: Model, defaultValueProvider?: (model: Model<V, EX>) => V) {\n this[$model] = create(base);\n\n if (defaultValueProvider) {\n this.defaultValueProvider(defaultValueProvider);\n }\n }\n\n /**\n * Appends metadata to the model.\n *\n * @param value - The metadata to append.\n * @returns The current builder instance.\n */\n meta(value: ModelMetadata): this {\n this.define($meta, { value });\n return this;\n }\n\n /**\n * Defines a new property on the model. The property serves the purposes of\n * storing the extra data for specific types of models.\n *\n * @remarks\n * The key of the property should be a symbol to avoid conflicts with\n * properties defined via\n * {@link ObjectModelBuilder}.\n *\n * @param key - The key of the property.\n * @param value - The descriptor of the property.\n * @returns The current builder instance.\n */\n define<DK extends symbol, DV>(\n key: DK,\n value: TypedPropertyDescriptor<DV>,\n ): CoreModelBuilder<V, EX & Readonly<Record<DK, DV>>, F> {\n defineProperty(this[$model], key, value);\n return this as any;\n }\n\n /**\n * Sets the default value provider for the model. This is an alternative way\n * to provide the default value for the model if for some reason using the\n * constructor parameter is undesired.\n *\n * @param defaultValueProvider - The function that provides the default value\n * for the model.\n * @returns The current builder instance.\n */\n defaultValueProvider(defaultValueProvider: DefaultValueProvider<V, EX>): this {\n this.define($defaultValue, {\n get(this: Model<V, EX>) {\n return defaultValueProvider(this);\n },\n });\n return this;\n }\n\n /**\n * Sets the name of the model. The name is used for debugging purposes and is\n * displayed in the string representation. Setting the name is required;\n * otherwise, the {@link CoreModelBuilder.build} method won't be available.\n *\n * @param name - The name of the model.\n * @returns The current builder instance.\n */\n name(name: string): CoreModelBuilder<V, EX, { named: true; selfRefKeys: F['selfRefKeys'] }> {\n return this.define($name, { value: name }) as any;\n }\n\n /**\n * Builds the model. On the typing level, it checks if all the model parts are\n * set correctly, and raises an error if not.\n *\n * @returns The model.\n */\n build(this: F['named'] extends true ? this : never): Model<V, EX> {\n return this[$model];\n }\n}\n\n/**\n * A registry for the property models of the object model. Since the property\n * registration is lazy, we cannot store the property models directly on the\n * object model, so the registry plays a role of a private storage for them.\n *\n * @internal\n */\nconst propertyRegistry = new WeakMap<Model, Record<string, Model>>();\n\n/**\n * A builder class for creating object models.\n *\n * @typeParam V - The final value type of the model.\n * @typeParam CV - The current value type of the model. It changes as the model\n * is being built and defines if the\n * {@link ObjectModelBuilder.build} method can be called.\n * @typeParam EX - The extra properties of the model.\n * @typeParam F - The flags for the model constructor that allow to determine\n * specific characteristics of the model.\n */\nexport class ObjectModelBuilder<\n V extends AnyObject,\n CV extends AnyObject = EmptyObject,\n EX extends AnyObject = EmptyObject,\n F extends Flags = { named: false; selfRefKeys: never },\n> extends CoreModelBuilder<V, EX, F> {\n constructor(base: Model) {\n super(base, (m) => {\n const result = create(null);\n\n // eslint-disable-next-line no-restricted-syntax\n for (const key in m) {\n defineProperty(result, key, {\n enumerable: true,\n get: () => (m[key as keyof Model<V, EX>] as Model)[$defaultValue],\n });\n }\n\n return result;\n });\n }\n\n /**\n * The method that should follow the {@link m.extend} method. It allows to\n * declare the extension for the model and properly name it.\n *\n * @param name - The name of the model.\n */\n object<NV extends AnyObject>(\n this: F['named'] extends false ? this : never,\n name: string,\n ): ObjectModelBuilder<NV & V, CV, EX, { named: true; selfRefKeys: F['selfRefKeys'] }> {\n return this.name(name) as any;\n }\n\n /**\n * {@inheritDoc CoreModelBuilder.define}\n */\n declare ['define']: <DK extends symbol, DV>(\n key: DK,\n value: TypedPropertyDescriptor<DV>,\n ) => ObjectModelBuilder<V, CV, EX & Readonly<Record<DK, DV>>, F>;\n\n /**\n * {@inheritDoc CoreModelBuilder.meta}\n */\n declare ['meta']: (value: ModelMetadata) => this;\n\n /**\n * Defines a new model property on the model. Unlike the\n * {@link ObjectModelBuilder.define}, this property is public and allows the\n * user to interact with the model data structure. It also updates the current\n * value type of the model to make it closer to the final value type.\n *\n * @param key - The key of the property.\n * @param model - The model of the property value. You can also provide a\n * function that produces the model based on the current model.\n * @param options - Additional options for the property.\n *\n * @returns The current builder instance updated with the new property type.\n * In case there is a self-referencing property, the {@link Flags.selfRefKeys}\n * flag for the specific property is set.\n */\n property<PK extends string & keyof V, EXK extends AnyObject = EmptyObject>(\n key: PK,\n model: Model<V[PK], EXK> | ((model: Model<V, EX & Readonly<Record<PK, Model<V[PK], EXK>>>>) => Model<V[PK], EXK>),\n options?: ModelBuilderPropertyOptions,\n ): // It is a workaround for the self-referencing models.\n // If the type of the model property is not the model itself,\n Extract<V[PK], V> extends never\n ? // Then we simply extend the model with the property, and update the\n // current value type of the model.\n ObjectModelBuilder<V, CV & Readonly<Record<PK, V[PK]>>, EX & Readonly<Record<PK, Model<V[PK], EXK>>>, F>\n : // Otherwise, we set a flag of the model that it contains a self-reference\n // property.\n ObjectModelBuilder<\n V,\n CV & Readonly<Record<PK, V[PK]>>,\n EX,\n {\n // Just inheriting the current flag.\n named: F['named'];\n // Adding the property name to all existing self-referencing\n // properties.\n // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n selfRefKeys: F['selfRefKeys'] | PK;\n }\n > {\n defineProperty(this[$model], key, {\n enumerable: true,\n get(this: Model<V, EX & Readonly<Record<PK, Model<V[PK], EXK>>>>) {\n if (!propertyRegistry.has(this)) {\n propertyRegistry.set(this, {});\n }\n\n const props = propertyRegistry.get(this)!;\n\n props[key] ??= new CoreModelBuilder<V[PK], EXK, { named: true; selfRefKeys: never }>(\n typeof model === 'function' ? model(this) : model,\n )\n .define($key, { value: key })\n .define($owner, { value: this })\n .define($meta, { value: options?.meta })\n .build();\n\n return props[key];\n },\n });\n\n return this as any;\n }\n\n /**\n * {@inheritDoc CoreModelBuilder.build}\n */\n declare build: (\n this: F['named'] extends true ? (CV extends V ? this : never) : never,\n ) => ObjectModel<V, EX, F['selfRefKeys']>;\n}\n"]}
package/core.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import type { EmptyObject } from 'type-fest';
2
- import { $enum, $itemModel, type $members, type AnyObject, type Enum, Model, type Value } from './model.js';
3
- export type PrimitiveModel<V = unknown> = Model<V>;
4
- export declare const PrimitiveModel: Model<unknown, EmptyObject>;
5
- export type StringModel = PrimitiveModel<string>;
6
- export declare const StringModel: Model<string, EmptyObject>;
7
- export type NumberModel = PrimitiveModel<number>;
8
- export declare const NumberModel: Model<number, EmptyObject>;
9
- export type BooleanModel = PrimitiveModel<boolean>;
10
- export declare const BooleanModel: Model<boolean, EmptyObject>;
11
- export type ArrayModel<M extends Model = Model> = Model<Array<Value<M>>, Readonly<{
12
- [$itemModel]: M;
13
- }>>;
14
- export declare const ArrayModel: Model<unknown[], EmptyObject & Readonly<Record<typeof $itemModel, Model<unknown, EmptyObject, never>>>>;
15
- export type ObjectModel<V, EX extends AnyObject = EmptyObject, R extends keyof any = never> = Model<V, EX, R>;
16
- export declare const ObjectModel: Model<Readonly<Record<never, never>>, EmptyObject>;
17
- export type EnumModel<T extends typeof Enum> = Model<T[keyof T], Readonly<{
18
- [$enum]: T;
19
- }>>;
20
- export declare const EnumModel: Model<string, EmptyObject & Readonly<Record<typeof $enum, typeof Enum>>>;
21
- export type UnionModel<MM extends Model[]> = Model<Value<MM[number]>, Readonly<{
22
- [$members]: MM;
23
- }>>;
package/core.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["src/core.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,UAAU,EAA4C,KAAK,EAAc,MAAM,YAAY,CAAC;AAM5G,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,GAAY,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;AAM9G,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;AAMjG,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;AAMhG,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC;AAYtG,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,GAAc,EAAE,CAAC,EAAE,CAAC;KACvE,IAAI,CAAC,OAAO,CAAC;KACb,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KACpC,KAAK,EAAE,CAAC;AAOX,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,GAAc,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;AAYrG,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,gBAAgB,CAAmC,KAAK,CAAC;KACnF,IAAI,CAAC,MAAM,CAAC;KAEZ,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAiB,EAAE,CAAC;KAC3C,oBAAoB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC7D,KAAK,EAAE,CAAC","sourcesContent":["import type { EmptyObject } from 'type-fest';\nimport { CoreModelBuilder } from './builders.js';\nimport { $enum, $itemModel, type $members, type AnyObject, type Enum, Model, type Value } from './model.js';\n\n/**\n * The model of a primitive value, like `string`, `number` or `boolean`.\n */\nexport type PrimitiveModel<V = unknown> = Model<V>;\nexport const PrimitiveModel = new CoreModelBuilder(Model, (): unknown => undefined).name('primitive').build();\n\n/**\n * The model of a string value.\n */\nexport type StringModel = PrimitiveModel<string>;\nexport const StringModel = new CoreModelBuilder(PrimitiveModel, () => '').name('string').build();\n\n/**\n * The model of a number value.\n */\nexport type NumberModel = PrimitiveModel<number>;\nexport const NumberModel = new CoreModelBuilder(PrimitiveModel, () => 0).name('number').build();\n\n/**\n * The model of a boolean value.\n */\nexport type BooleanModel = PrimitiveModel<boolean>;\nexport const BooleanModel = new CoreModelBuilder(PrimitiveModel, () => false).name('boolean').build();\n\n/**\n * The model of an array data.\n */\nexport type ArrayModel<M extends Model = Model> = Model<\n Array<Value<M>>,\n Readonly<{\n [$itemModel]: M;\n }>\n>;\n\nexport const ArrayModel = new CoreModelBuilder(Model, (): unknown[] => [])\n .name('Array')\n .define($itemModel, { value: Model })\n .build();\n\n/**\n * The model of an object data.\n */\nexport type ObjectModel<V, EX extends AnyObject = EmptyObject, R extends keyof any = never> = Model<V, EX, R>;\n\nexport const ObjectModel = new CoreModelBuilder(Model, (): AnyObject => ({})).name('Object').build();\n\n/**\n * The model of an enum data.\n */\nexport type EnumModel<T extends typeof Enum> = Model<\n T[keyof T],\n Readonly<{\n [$enum]: T;\n }>\n>;\n\nexport const EnumModel = new CoreModelBuilder<(typeof Enum)[keyof typeof Enum]>(Model)\n .name('Enum')\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n .define($enum, { value: {} as typeof Enum })\n .defaultValueProvider((self) => Object.values(self[$enum])[0])\n .build();\n\n/**\n * The model of a union data.\n */\nexport type UnionModel<MM extends Model[]> = Model<Value<MM[number]>, Readonly<{ [$members]: MM }>>;\n"]}
package/model.d.ts DELETED
@@ -1,49 +0,0 @@
1
- import type { EmptyObject } from 'type-fest';
2
- export interface JvmTypeRef {
3
- jvmType: string;
4
- genericArguments?: JvmTypeRef[];
5
- }
6
- export type AnnotationValue = AnnotationValue[] | JvmTypeRef | boolean | number | string | undefined;
7
- export interface Annotation {
8
- jvmType: string;
9
- arguments: Record<string, AnnotationValue>;
10
- }
11
- export interface ModelMetadata {
12
- jvmType?: string;
13
- annotations?: Annotation[];
14
- }
15
- export type Target<T = unknown> = Readonly<{
16
- model?: Model<T>;
17
- value: T;
18
- }>;
19
- export declare const nothing: unique symbol;
20
- export declare enum Enum {
21
- }
22
- export type AnyObject = Readonly<Record<never, never>>;
23
- export declare const $key: unique symbol;
24
- export declare const $name: unique symbol;
25
- export declare const $owner: unique symbol;
26
- export declare const $meta: unique symbol;
27
- export declare const $optional: unique symbol;
28
- export declare const $defaultValue: unique symbol;
29
- export declare const $enum: unique symbol;
30
- export declare const $members: unique symbol;
31
- export declare const $itemModel: unique symbol;
32
- export type Value<M extends Model> = M extends Model<infer T> ? T : never;
33
- export type Extensions<M extends Model> = M extends Model<unknown, infer EX> ? EX : EmptyObject;
34
- export type References<M extends Model> = M extends Model<unknown, AnyObject, infer R> ? R : never;
35
- export type Model<V = unknown, EX extends AnyObject = EmptyObject, R extends keyof any = never> = EX & Readonly<{
36
- [P in R]: Model<V, EX, R>;
37
- }> & Readonly<{
38
- [$key]: keyof any;
39
- [$name]: string;
40
- [$owner]: Model | Target;
41
- [$meta]?: ModelMetadata;
42
- [$optional]: boolean;
43
- [$defaultValue]: V;
44
- [Symbol.toStringTag]: string;
45
- [Symbol.hasInstance](value: any): value is Model<V, EX, R>;
46
- toString(): string;
47
- }>;
48
- export type DefaultValueProvider<V, EX extends AnyObject = EmptyObject, R extends keyof any = never> = (model: Model<V, EX, R>) => V;
49
- export declare const Model: Model;
package/model.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["src/model.ts"],"names":[],"mappings":"AA+BA,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAEzC,MAAM,cAAc,GAAW,MAAM,CAAC,MAAM,CAC1C;IACE,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY;CAC7B,EACD;IACE,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;IAC3B,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;CAC1B,CACF,CAAC;AAUF,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;AAKlC,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAKpC,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAKtC,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAKpC,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAK5C,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAK7C,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAKzC,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAK1C,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAuF9C,MAAM,CAAC,MAAM,KAAK,GAAU,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;IAC9C,CAAC,IAAI,CAAC,EAAE;QACN,KAAK,EAAE,OAAO;KACf;IACD,CAAC,KAAK,CAAC,EAAE;QACP,KAAK,EAAE,OAAO;KACf;IACD,CAAC,MAAM,CAAC,EAAE;QACR,KAAK,EAAE,cAAc;KACtB;IACD,CAAC,KAAK,CAAC,EAAE,EAAE;IACX,CAAC,SAAS,CAAC,EAAE;QACX,KAAK,EAAE,KAAK;KACb;IACD,CAAC,aAAa,CAAC,EAAE,EAAE;IACnB,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;QACpB,GAAG;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;KACF;IACD,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;QACpB,KAAK,CAAc,CAAU;YAC3B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5G,CAAC;KACF;IACD,QAAQ,EAAE;QACR,KAAK;YAEH,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzG,CAAC;KACF;CACF,CAAC,CAAC","sourcesContent":["import type { EmptyObject } from 'type-fest';\n\nexport interface JvmTypeRef {\n jvmType: string;\n genericArguments?: JvmTypeRef[];\n}\n\nexport type AnnotationValue = AnnotationValue[] | JvmTypeRef | boolean | number | string | undefined;\n\nexport interface Annotation {\n jvmType: string;\n arguments: Record<string, AnnotationValue>;\n}\n\n/**\n * The metadata of a model.\n */\nexport interface ModelMetadata {\n jvmType?: string;\n annotations?: Annotation[];\n}\n\n/**\n * The target to which a model is attached. It could be a Binder instance, a\n * Signal or another object. However, it could never be another model.\n */\nexport type Target<T = unknown> = Readonly<{\n model?: Model<T>;\n value: T;\n}>;\n\nexport const nothing = Symbol('nothing');\n\nconst detachedTarget: Target = Object.create(\n {\n toString: () => ':detached:',\n },\n {\n model: { value: undefined },\n value: { value: nothing },\n },\n);\n\nexport declare enum Enum {}\n\nexport type AnyObject = Readonly<Record<never, never>>; // {}\n\n/* eslint-disable tsdoc/syntax */\n/**\n * The symbol that represents the {@link Model[$key]} property.\n */\nexport const $key = Symbol('key');\n\n/**\n * The symbol that represents the {@link Model[$name]} property.\n */\nexport const $name = Symbol('name');\n\n/**\n * The symbol that represents the {@link Model[$owner]} property.\n */\nexport const $owner = Symbol('owner');\n\n/**\n * The symbol that represents the {@link Model[$meta]} property.\n */\nexport const $meta = Symbol('meta');\n\n/**\n * The symbol that represents the {@link Model[$optional]} property.\n */\nexport const $optional = Symbol('optional');\n\n/**\n * The symbol that represents the {@link Model[$value]} property.\n */\nexport const $defaultValue = Symbol('value');\n\n/**\n * The symbol that represents the {@link EnumModel[$enumerate]} property.\n */\nexport const $enum = Symbol('enumerate');\n\n/**\n * The symbol that represents the {@link UnionModel[$members]} property.\n */\nexport const $members = Symbol('members');\n\n/**\n * The symbol that represents the {@link ArrayModel[$itemModel]} property.\n */\nexport const $itemModel = Symbol('itemModel');\n\n/* eslint-enable tsdoc/syntax */\n\n/**\n * Extracts the value type the model represents.\n */\nexport type Value<M extends Model> = M extends Model<infer T> ? T : never;\n\n/**\n * Extracts the list of extra properties of the model.\n */\nexport type Extensions<M extends Model> = M extends Model<unknown, infer EX> ? EX : EmptyObject;\n\n/**\n * Extracts the list of self-referencing properties of the model.\n */\nexport type References<M extends Model> = M extends Model<unknown, AnyObject, infer R> ? R : never;\n\n/**\n * A model that represents a specific type of data.\n *\n * @typeParam V - The type of the data described by the model.\n * @typeParam EX - The extra properties of the model. It could be either a model\n * that represents a property of the object the current model describe, or a\n * model-specific metadata. It's recommended to use a symbol as a key for the\n * metadata property to avoid the potential naming conflicts with the described\n * object properties.\n * @typeParam R - The keys of the self-referencing properties of the model.\n *\n * @remarks\n * Since we know the full model definition only on this step, the `R` type\n * parameter is essential to describe a model with self-reference properties.\n */\nexport type Model<V = unknown, EX extends AnyObject = EmptyObject, R extends keyof any = never> = EX &\n Readonly<{\n [P in R]: Model<V, EX, R>;\n }> &\n Readonly<{\n /**\n * The key of the model in the owner model.\n */\n [$key]: keyof any;\n\n /**\n * The name of the model. For attached models, the name will be prefixed\n * with the `@` symbol.\n */\n [$name]: string;\n\n /**\n * The owner model of the model. For detached models, the owner will always\n * be a specific global object `detachedTarget`.\n */\n [$owner]: Model | Target;\n\n /**\n * The metadata of the model.\n */\n [$meta]?: ModelMetadata;\n\n /**\n * Whether the model is optional. It describes if the data described by\n * this model is nullable.\n */\n [$optional]: boolean;\n\n /**\n * The default value of the model.\n */\n [$defaultValue]: V;\n [Symbol.toStringTag]: string;\n [Symbol.hasInstance](value: any): value is Model<V, EX, R>;\n toString(): string;\n }>;\n\n/**\n * A function that provides a default value for a model.\n *\n * @typeParam V - The type of the data described by the model.\n * @typeParam EX - The extra properties of the model.\n * @typeParam R - The keys of the self-referencing properties of the model.\n */\nexport type DefaultValueProvider<V, EX extends AnyObject = EmptyObject, R extends keyof any = never> = (\n model: Model<V, EX, R>,\n) => V;\n\nexport const Model: Model = Object.create(null, {\n [$key]: {\n value: 'model',\n },\n [$name]: {\n value: 'Model',\n },\n [$owner]: {\n value: detachedTarget,\n },\n [$meta]: {},\n [$optional]: {\n value: false,\n },\n [$defaultValue]: {},\n [Symbol.toStringTag]: {\n get(this: Model) {\n return this[$name];\n },\n },\n [Symbol.hasInstance]: {\n value(this: Model, o: unknown) {\n return typeof o === 'object' && o != null && (this === o || Object.prototype.isPrototypeOf.call(this, o));\n },\n },\n toString: {\n value(this: Model) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return `[${String(this[$owner])} / ${String(this[$key])}${this[$optional] ? '?' : ''}] ${this[$name]}`;\n },\n },\n});\n"]}