@afora/core 0.0.2 → 0.0.3

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.
Files changed (79) hide show
  1. package/README.md +220 -733
  2. package/dist/core/application/application.d.ts +9 -1
  3. package/dist/core/application/exceptions.d.ts +28 -0
  4. package/dist/core/application/registry.d.ts +33 -0
  5. package/dist/core/application/start.d.ts +51 -0
  6. package/dist/core/application/types/application.interface.d.ts +186 -7
  7. package/dist/core/blueprint/types/blueprint.interface.d.ts +66 -11
  8. package/dist/core/blueprint/types/slice.interface.d.ts +7 -2
  9. package/dist/core/command/command.d.ts +25 -0
  10. package/dist/core/command/exceptions.d.ts +33 -0
  11. package/dist/core/command/types/command.interface.d.ts +102 -0
  12. package/dist/core/environment/environment.d.ts +4 -1
  13. package/dist/core/exception/exception.d.ts +4 -0
  14. package/dist/core/exception/exported.d.ts +4 -0
  15. package/dist/core/json.d.ts +10 -0
  16. package/dist/core/output/exceptions.d.ts +8 -0
  17. package/dist/core/output/openapi-sink.d.ts +83 -0
  18. package/dist/core/output/output.d.ts +19 -0
  19. package/dist/core/output/problem.d.ts +15 -0
  20. package/dist/core/output/select.d.ts +14 -0
  21. package/dist/core/output/stage.d.ts +24 -0
  22. package/dist/core/output/status.d.ts +17 -0
  23. package/dist/core/output/types/output.interface.d.ts +171 -0
  24. package/dist/core/property/abstract-property.d.ts +8 -7
  25. package/dist/core/property/define-property.d.ts +8 -7
  26. package/dist/core/property/property.d.ts +26 -15
  27. package/dist/core/property/types/abstract-property-constructor.type.d.ts +8 -7
  28. package/dist/core/property/types/define-property.interface.d.ts +5 -4
  29. package/dist/core/property/types/optional-property.type.d.ts +5 -5
  30. package/dist/core/property/types/property-collection.type.d.ts +2 -2
  31. package/dist/core/property/types/property-constructor.type.d.ts +5 -1
  32. package/dist/core/property/types/property-options.interface.d.ts +17 -8
  33. package/dist/core/property/types/property-type.interface.d.ts +7 -3
  34. package/dist/core/property/types/property.interface.d.ts +5 -4
  35. package/dist/core/record/abstract-record.d.ts +12 -4
  36. package/dist/core/record/types/plugin.interface.d.ts +13 -3
  37. package/dist/core/record/types/record.interface.d.ts +20 -8
  38. package/dist/core/repository/exceptions.d.ts +4 -0
  39. package/dist/core/repository/in-memory.d.ts +3 -0
  40. package/dist/core/repository/provider.d.ts +6 -0
  41. package/dist/core/repository/query.d.ts +9 -0
  42. package/dist/core/repository/references.d.ts +4 -2
  43. package/dist/core/repository/repository.d.ts +4 -2
  44. package/dist/core/repository/state.d.ts +4 -0
  45. package/dist/core/repository/types/query.interface.d.ts +67 -0
  46. package/dist/core/repository/types/repository.interface.d.ts +13 -13
  47. package/dist/core/schema/types/schema.interface.d.ts +11 -4
  48. package/dist/core/symbols/meta.d.ts +2 -0
  49. package/dist/core/use-case/exceptions.d.ts +10 -0
  50. package/dist/core/use-case/scope.d.ts +21 -0
  51. package/dist/core/use-case/steps/describe.d.ts +13 -0
  52. package/dist/core/use-case/steps/references.d.ts +27 -0
  53. package/dist/core/use-case/steps/run.d.ts +20 -0
  54. package/dist/core/use-case/steps/types/steps.interface.d.ts +153 -0
  55. package/dist/core/use-case/steps/validate.d.ts +17 -0
  56. package/dist/core/use-case/use-case.d.ts +74 -9
  57. package/dist/experimental.d.ts +6 -0
  58. package/dist/experimental.js +123 -0
  59. package/dist/index-hn82hm31.js +1651 -0
  60. package/dist/index-sw2b9ehx.js +5019 -0
  61. package/dist/index.d.ts +30 -11
  62. package/dist/index.js +325 -4142
  63. package/dist/std/blueprint-plugin.d.ts +10 -10
  64. package/dist/std/blueprint.d.ts +10 -10
  65. package/dist/std/entity.d.ts +2 -2
  66. package/dist/std/properties/contact.d.ts +4 -4
  67. package/dist/std/properties/enum.d.ts +1 -1
  68. package/dist/std/properties/headless.d.ts +1 -1
  69. package/dist/std/properties/identity.d.ts +2 -2
  70. package/dist/std/properties/password.d.ts +1 -1
  71. package/dist/std/properties/primitives.d.ts +5 -5
  72. package/dist/std/properties/time.d.ts +2 -2
  73. package/dist/std/record-plugins.d.ts +12 -12
  74. package/dist/std/record.d.ts +1 -1
  75. package/dist/std/stages.d.ts +2 -0
  76. package/dist/testing/index.d.ts +2 -0
  77. package/dist/testing/index.js +502 -0
  78. package/dist/testing/provider-contract.d.ts +22 -0
  79. package/package.json +28 -8
@@ -8,10 +8,15 @@ import type { PropertyValue } from "./types/property-constructor.type.js";
8
8
  import type { PropertyOptions } from "./types/property-options.interface.js";
9
9
  import type { IPropertyType, UniqueMark } from "./types/property-type.interface.js";
10
10
  type SchemaValueType<SchemaType extends TSchema, Optional extends boolean = false> = Optional extends true ? TOptional<SchemaType> : SchemaType;
11
- type PropertyOfArgs<SchemaType extends TSchema, Optional extends boolean = false, Unique extends boolean = false> = {
11
+ /**
12
+ * What `propertyOf` takes. `SchemaType` is inferred from `schema` alone: the
13
+ * value is `NoInfer`, since inferring through `Static` makes TypeScript
14
+ * measure `Static` itself, which costs about a second on every check.
15
+ */
16
+ type PropertyOfArgs<SchemaType extends TSchema, Optional extends boolean = false, Unique extends boolean = false, Value = Static<SchemaType>> = {
12
17
  name: string;
13
18
  schema: SchemaType;
14
- default?: Static<SchemaType>;
19
+ default?: NoInfer<Value>;
15
20
  optional?: Optional;
16
21
  /** A record masks its value in `toJSON`, at any depth, as `Password`. */
17
22
  sensitive?: boolean;
@@ -20,27 +25,33 @@ type PropertyOfArgs<SchemaType extends TSchema, Optional extends boolean = false
20
25
  * do not check it, repositories (or the database) enforce it.
21
26
  */
22
27
  unique?: Unique;
23
- preHandle?: (value: Static<SchemaType>) => Static<SchemaType>;
24
- afterHandle?: (value: Static<SchemaType>) => Static<SchemaType>;
28
+ preHandle?: (value: NoInfer<Value>) => NoInfer<Value>;
29
+ afterHandle?: (value: NoInfer<Value>) => NoInfer<Value>;
25
30
  };
26
- /** The instance type of a class declared with `propertyOf`. */
27
- export declare class DeclaredProperty<SchemaType extends TSchema> extends AbstractProperty<SchemaType> {
28
- protected createDefinition(): AbstractDefineProperty<SchemaType>;
31
+ /**
32
+ * The instance type of a class declared with `propertyOf`; `Value` is the
33
+ * value of its schema, as in `ISchema`.
34
+ */
35
+ export declare class DeclaredProperty<SchemaType extends TSchema, Value = Static<SchemaType>> extends AbstractProperty<SchemaType, Value> {
36
+ protected createDefinition(): AbstractDefineProperty<SchemaType, Value>;
29
37
  }
30
- /** A class declared with `propertyOf`; extend it to add behavior, as `Id` does. */
31
- export interface PropertyClass<SchemaType extends TSchema> {
32
- new (value: PropertyValue<SchemaType>): DeclaredProperty<SchemaType>;
33
- readonly type: IPropertyType<SchemaType>;
38
+ /**
39
+ * A class declared with `propertyOf`; extend it to add behavior, as `Id` does.
40
+ * `Value` is the value of its schema, as in `ISchema`.
41
+ */
42
+ export interface PropertyClass<SchemaType extends TSchema, Value = Static<SchemaType>> {
43
+ new (value: PropertyValue<SchemaType, Value>): DeclaredProperty<SchemaType, Value>;
44
+ readonly type: IPropertyType<SchemaType, Value>;
34
45
  /** The value handled by the property, or every issue that rejects `value`; never throws. */
35
- safeParse(value: unknown): ParseResult<PropertyValue<SchemaType>>;
46
+ safeParse(value: unknown): ParseResult<PropertyValue<SchemaType, Value>>;
36
47
  /** The Standard Schema of the class: `validate` gives the handled value. */
37
- readonly "~standard": StandardSchemaV1.Props<PropertyValue<SchemaType>, PropertyValue<SchemaType>>;
48
+ readonly "~standard": StandardSchemaV1.Props<PropertyValue<SchemaType, Value>, PropertyValue<SchemaType, Value>>;
38
49
  /**
39
50
  * A subclass narrowed and documented by `options`, e.g.
40
51
  * `Integer.with({ min: 1, description: "Age in years" })`.
41
52
  */
42
- with<Self extends AnyPropertyConstructor>(this: Self, options: PropertyOptions<SchemaType>): Self;
53
+ with<Self extends AnyPropertyConstructor>(this: Self, options: PropertyOptions<SchemaType, Value>): Self;
43
54
  demo<Self extends AbstractProperty<any>>(this: new (value: never) => Self): Self;
44
55
  }
45
- export declare function propertyOf<SchemaType extends TSchema, Optional extends boolean = false, const Unique extends boolean = false>({ name, schema, optional, sensitive, unique, default: _default, preHandle: onPreHandle, afterHandle: onAfterHandle, }: PropertyOfArgs<SchemaType, Optional, Unique>): PropertyClass<SchemaValueType<SchemaType, Optional>> & UniqueMark<Unique>;
56
+ export declare function propertyOf<SchemaType extends TSchema, Optional extends boolean = false, const Unique extends boolean = false>({ name, schema, optional, sensitive, unique, default: _default, preHandle: onPreHandle, afterHandle: onAfterHandle, }: PropertyOfArgs<SchemaType, Optional, Unique>): PropertyClass<SchemaValueType<SchemaType, Optional>, Static<SchemaType>> & UniqueMark<Unique>;
46
57
  export {};
@@ -1,13 +1,14 @@
1
- import type { TSchema } from "typebox";
1
+ import type { Static, TSchema } from "typebox";
2
2
  import type { ParseResult } from "../../parse/types/parse.interface.js";
3
3
  import type { StandardSchemaV1 } from "../../standard/standard-schema.js";
4
4
  import type { AbstractProperty } from "../abstract-property.js";
5
5
  import type { PropertyValue } from "./property-constructor.type.js";
6
6
  import type { IPropertyType } from "./property-type.interface.js";
7
- export type AbstractPropertyConstructor<SchemaType extends TSchema> = {
8
- new (value: PropertyValue<SchemaType>): AbstractProperty<SchemaType>;
9
- readonly type: IPropertyType<SchemaType>;
10
- demo(): AbstractProperty<SchemaType>;
11
- safeParse(value: unknown): ParseResult<PropertyValue<SchemaType>>;
12
- readonly "~standard": StandardSchemaV1.Props<PropertyValue<SchemaType>, PropertyValue<SchemaType>>;
7
+ /** A property class; `Value` is the value of its schema, as in `ISchema`. */
8
+ export type AbstractPropertyConstructor<SchemaType extends TSchema, Value = Static<SchemaType>> = {
9
+ new (value: PropertyValue<SchemaType, Value>): AbstractProperty<SchemaType, Value>;
10
+ readonly type: IPropertyType<SchemaType, Value>;
11
+ demo(): AbstractProperty<SchemaType, Value>;
12
+ safeParse(value: unknown): ParseResult<PropertyValue<SchemaType, Value>>;
13
+ readonly "~standard": StandardSchemaV1.Props<PropertyValue<SchemaType, Value>, PropertyValue<SchemaType, Value>>;
13
14
  };
@@ -3,10 +3,11 @@ import type { ParseResult } from "../../parse/types/parse.interface.js";
3
3
  import { State } from "../../symbols/state.js";
4
4
  import type { PropertyStates } from "../enums/property-states.js";
5
5
  import type { IPropertyType } from "./property-type.interface.js";
6
- export interface IDefineProperty<SchemaType extends TSchema> {
6
+ /** How a property handles its value; `Value` is the value of its schema, as in `ISchema`. */
7
+ export interface IDefineProperty<SchemaType extends TSchema, Value = Static<SchemaType>> {
7
8
  [State]: PropertyStates;
8
- get type(): IPropertyType<SchemaType>;
9
- make(value: Static<SchemaType>): Static<SchemaType>;
9
+ get type(): IPropertyType<SchemaType, Value>;
10
+ make(value: Value): Value;
10
11
  /** Like `make`, but returns the issues of a rejected value instead of throwing. */
11
- parse(value: unknown): ParseResult<Static<SchemaType>>;
12
+ parse(value: unknown): ParseResult<Value>;
12
13
  }
@@ -11,10 +11,10 @@ import type { IPropertyType, UniqueTypeOf } from "./property-type.interface.js";
11
11
  * own methods and statics (e.g. `Password#verify`, `Id.generate`) are kept.
12
12
  */
13
13
  export type OptionalProperty<Property extends AnyPropertyConstructor> = Property extends {
14
- readonly type: IPropertyType<infer SchemaType>;
14
+ readonly type: IPropertyType<infer SchemaType, infer Value>;
15
15
  } ? SchemaType extends TOptional<TSchema> ? StoredField<Property> : Omit<Property, "type" | "prototype" | "safeParse" | "~standard" | CallableKey> & {
16
- new (value: PropertyValue<TOptional<SchemaType>>): AbstractProperty<TOptional<SchemaType>> & Omit<InstanceType<Property>, keyof AbstractProperty<TSchema>>;
17
- readonly type: IPropertyType<TOptional<SchemaType>> & UniqueTypeOf<Property>;
18
- safeParse(value: unknown): ParseResult<PropertyValue<TOptional<SchemaType>>>;
19
- readonly "~standard": StandardSchemaV1.Props<PropertyValue<TOptional<SchemaType>>, PropertyValue<TOptional<SchemaType>>>;
16
+ new (value: PropertyValue<TOptional<SchemaType>, Value>): AbstractProperty<TOptional<SchemaType>, Value> & Omit<InstanceType<Property>, keyof AbstractProperty<TSchema>>;
17
+ readonly type: IPropertyType<TOptional<SchemaType>, Value> & UniqueTypeOf<Property>;
18
+ safeParse(value: unknown): ParseResult<PropertyValue<TOptional<SchemaType>, Value>>;
19
+ readonly "~standard": StandardSchemaV1.Props<PropertyValue<TOptional<SchemaType>, Value>, PropertyValue<TOptional<SchemaType>, Value>>;
20
20
  } : never;
@@ -7,8 +7,8 @@ import type { IPropertyType } from "./property-type.interface.js";
7
7
  * to `unknown`, which a constructor taking a narrower value would reject.
8
8
  */
9
9
  export type AnyPropertyConstructor = {
10
- new (value: any): AbstractProperty<any>;
11
- readonly type: IPropertyType<any>;
10
+ new (value: any): AbstractProperty<any, any>;
11
+ readonly type: IPropertyType<any, any>;
12
12
  safeParse(value: unknown): ParseResult<any>;
13
13
  readonly "~standard": StandardSchemaV1.Props<any, any>;
14
14
  };
@@ -1,2 +1,6 @@
1
1
  import type { Static, TOptional, TSchema } from "typebox";
2
- export type PropertyValue<SchemaType extends TSchema> = SchemaType extends TOptional<TSchema> ? Static<SchemaType> | undefined : Static<SchemaType>;
2
+ /**
3
+ * The value a property holds: the value of its schema, or `undefined` too
4
+ * when it is optional. `Value` is the value of the schema, as in `ISchema`.
5
+ */
6
+ export type PropertyValue<SchemaType extends TSchema, Value = Static<SchemaType>> = SchemaType extends TOptional<TSchema> ? Value | undefined : Value;
@@ -46,7 +46,7 @@ export interface StringConstraints {
46
46
  * What `Property.with` and `ctx.<property>(options)` take: the metadata of any
47
47
  * property, plus the constraints of its kind of schema, for strings and numbers.
48
48
  */
49
- export type PropertyOptions<SchemaType extends TSchema> = PropertyMetadata<NonNullable<Static<SchemaType>>> & (SchemaType extends TString ? StringConstraints : SchemaType extends TNumber | TInteger ? NumberConstraints : unknown);
49
+ export type PropertyOptions<SchemaType extends TSchema, Value = Static<SchemaType>> = PropertyMetadata<NonNullable<Value>> & (SchemaType extends TString ? StringConstraints : SchemaType extends TNumber | TInteger ? NumberConstraints : unknown);
50
50
  /**
51
51
  * Brands a `CallableProperty` with the property it stands for.
52
52
  *
@@ -60,18 +60,27 @@ export declare const Callable: unique symbol;
60
60
  export type CallableProperty<Property extends {
61
61
  readonly type: IPropertyType<TSchema>;
62
62
  }> = Property & {
63
- (options?: PropertyOptions<SchemaOfProperty<Property>>): Property;
63
+ (options?: OptionsOfProperty<Property>): Property;
64
64
  readonly [Callable]: Property;
65
65
  };
66
- /** The schema of a property class. */
67
- type SchemaOfProperty<Property> = Property extends {
68
- readonly type: IPropertyType<infer SchemaType>;
69
- } ? SchemaType : TSchema;
70
- /** `Field`, or the property it stands for when it is a `CallableProperty`, as a blueprint stores it. */
66
+ /**
67
+ * The `PropertyOptions` of a property class, from its schema and the value
68
+ * its type carries, so `Static` is not measured again.
69
+ */
70
+ type OptionsOfProperty<Property> = Property extends {
71
+ readonly type: IPropertyType<infer SchemaType, infer Value>;
72
+ } ? PropertyOptions<SchemaType, Value> : PropertyOptions<TSchema>;
73
+ /**
74
+ * `Field`, or the property it stands for when it is a `CallableProperty`, as a blueprint stores it.
75
+ * @internal
76
+ */
71
77
  export type StoredField<Field> = Field extends {
72
78
  readonly [Callable]: infer Property;
73
79
  } ? Property : Field;
74
- /** `StoredField` of every field of a collection. */
80
+ /**
81
+ * `StoredField` of every field of a collection.
82
+ * @internal
83
+ */
75
84
  export type StoredFields<Fields> = {
76
85
  [Key in keyof Fields]: StoredField<Fields[Key]>;
77
86
  };
@@ -1,11 +1,15 @@
1
1
  import type { Static, TSchema } from "typebox";
2
2
  import type { ISchema } from "../../schema/types/schema.interface.js";
3
3
  import { Demo } from "../../symbols/demo.js";
4
- export interface IPropertyType<SchemaType extends TSchema> {
4
+ /**
5
+ * What a property declares about itself. `Value` is the value of its schema,
6
+ * computed where `SchemaType` is known, as in `ISchema`.
7
+ */
8
+ export interface IPropertyType<SchemaType extends TSchema, Value = Static<SchemaType>> {
5
9
  /** Value used by `demo()`; it is the property's `default`, when there is one. */
6
- readonly [Demo]: Static<SchemaType> | undefined;
10
+ readonly [Demo]: Value | undefined;
7
11
  readonly name: string;
8
- readonly schema: ISchema<SchemaType>;
12
+ readonly schema: ISchema<SchemaType, Value>;
9
13
  readonly optional?: boolean;
10
14
  /** Whether a record masks its value in `toJSON`, at any depth. */
11
15
  readonly sensitive?: boolean;
@@ -1,7 +1,8 @@
1
- import type { TSchema } from "typebox";
1
+ import type { Static, TSchema } from "typebox";
2
2
  import type { AbstractDefineProperty } from "../define-property.js";
3
3
  import type { PropertyValue } from "./property-constructor.type.js";
4
- export interface IProperty<SchemaType extends TSchema> {
5
- readonly definition: AbstractDefineProperty<SchemaType>;
6
- readonly value: PropertyValue<SchemaType>;
4
+ /** A property instance; `Value` is the value of its schema, as in `ISchema`. */
5
+ export interface IProperty<SchemaType extends TSchema, Value = Static<SchemaType>> {
6
+ readonly definition: AbstractDefineProperty<SchemaType, Value>;
7
+ readonly value: PropertyValue<SchemaType, Value>;
7
8
  }
@@ -1,7 +1,13 @@
1
1
  import type { BlueprintData, BlueprintField, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
2
2
  import type { ParseResult } from "../parse/types/parse.interface.js";
3
3
  import type { StandardSchemaV1 } from "../standard/standard-schema.js";
4
+ import { Meta } from "../symbols/meta.js";
4
5
  import type { IRecord, RecordChange, RecordEffects, RecordSettings } from "./types/record.interface.js";
6
+ /** The statics of a class made by `recordOf`, that its records read. */
7
+ interface RecordDeclaration<Properties extends IBlueprintCollection> {
8
+ readonly blueprint: IBlueprint<Properties>;
9
+ readonly options: RecordSettings<Properties>;
10
+ }
5
11
  /**
6
12
  * The names of the record's own members, and of those every object inherits
7
13
  * (`toString`, `valueOf`, `__proto__`...), which no key of a blueprint can take.
@@ -32,8 +38,10 @@ export declare function recordFieldDataOf(field: BlueprintField | undefined, val
32
38
  * and the one it replaced.
33
39
  */
34
40
  export declare function storeRecordValue(record: {
35
- readonly blueprint: {
36
- readonly properties: IBlueprintCollection;
41
+ readonly [Meta]: {
42
+ readonly blueprint: {
43
+ readonly properties: IBlueprintCollection;
44
+ };
37
45
  };
38
46
  }, key: string, value: unknown): {
39
47
  readonly next: unknown;
@@ -47,8 +55,7 @@ export declare const RecordPluginHooks: unique symbol;
47
55
  */
48
56
  export declare function runRecordEffects<Self extends object>(record: Self, key: PropertyKey, next: unknown, previous: unknown): Self;
49
57
  export declare abstract class AbstractRecord<Properties extends IBlueprintCollection> implements IRecord<Properties> {
50
- readonly blueprint: IBlueprint<Properties>;
51
- readonly options: RecordSettings<Properties>;
58
+ get [Meta](): RecordDeclaration<Properties>;
52
59
  constructor(value: BlueprintInput<Properties>);
53
60
  /**
54
61
  * An instance of the class for `value`, each key handled, or every issue
@@ -82,3 +89,4 @@ export declare abstract class AbstractRecord<Properties extends IBlueprintCollec
82
89
  */
83
90
  protected onSet(): RecordEffects<IRecord<Properties>>;
84
91
  }
92
+ export {};
@@ -90,13 +90,17 @@ export interface IRecordPluginCatalog<Plugins extends Readonly<globalThis.Record
90
90
  /** `resolve` for each of `references`, e.g. `JSON.parse(JSON.stringify(Post.plugins))`. */
91
91
  resolveAll(references: readonly RecordPluginReference[]): readonly AnyRecordPlugin[];
92
92
  }
93
- /** The plugins of a catalog made of `Plugins`, by name. */
93
+ /**
94
+ * The plugins of a catalog made of `Plugins`, by name.
95
+ * @internal
96
+ */
94
97
  export type RecordPluginsByName<Plugins extends readonly AnyRecordPlugin[]> = {
95
98
  readonly [Plugin in Plugins[number] as Plugin["name"]]: Plugin;
96
99
  };
97
100
  /**
98
101
  * What `options.plugins` picks from: the plugins of every catalog, by name; a
99
102
  * later catalog replaces a plugin of an earlier one with the same name.
103
+ * @internal
100
104
  */
101
105
  export type RecordPluginContext<Catalogs extends readonly IRecordPluginCatalog[]> = Catalogs extends readonly [
102
106
  infer Head extends IRecordPluginCatalog,
@@ -111,7 +115,10 @@ type PluginProperties<Plugin> = Plugin extends {
111
115
  readonly blueprint: IBlueprint<infer Properties, boolean>;
112
116
  };
113
117
  } ? Properties : never;
114
- /** The fields of a record, and those of each of its plugins. */
118
+ /**
119
+ * The fields of a record, and those of each of its plugins.
120
+ * @internal
121
+ */
115
122
  export type WithPluginProperties<Properties extends IBlueprintCollection, Plugins extends readonly AnyRecordPlugin[]> = [Plugins[number]] extends [never] ? Properties : Properties & UnionToIntersection<PluginProperties<Plugins[number]>> extends infer Merged ? {
116
123
  readonly [Key in keyof Merged]: Merged[Key];
117
124
  } : never;
@@ -119,7 +126,10 @@ export type WithPluginProperties<Properties extends IBlueprintCollection, Plugin
119
126
  type PluginInstance<Plugin> = Plugin extends {
120
127
  readonly record: abstract new (value: never) => infer Instance;
121
128
  } ? Instance : never;
122
- /** What the plugins add to the records they are applied to: their keys and methods. */
129
+ /**
130
+ * What the plugins add to the records they are applied to: their keys and methods.
131
+ * @internal
132
+ */
123
133
  export type PluginMembers<Plugins extends readonly AnyRecordPlugin[]> = [
124
134
  Plugins[number]
125
135
  ] extends [never] ? unknown : UnionToIntersection<PluginInstance<Plugins[number]>>;
@@ -1,14 +1,25 @@
1
- import type { BlueprintData, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection, RecordAccepts } from "../../blueprint/types/blueprint.interface.js";
1
+ import type { AnyBlueprint, BlueprintData, BlueprintInput, BlueprintValue, IBlueprint, IBlueprintCollection, RecordAccepts } from "../../blueprint/types/blueprint.interface.js";
2
2
  import type { ParseResult } from "../../parse/types/parse.interface.js";
3
3
  import type { StandardSchemaV1 } from "../../standard/standard-schema.js";
4
4
  import type { Kind } from "../../symbols/kind.js";
5
+ import type { Meta } from "../../symbols/meta.js";
5
6
  import type { AbstractRecord } from "../abstract-record.js";
6
7
  import type { AnyRecordPlugin, RecordPluginsOption, RecordUniqueKeys } from "./plugin.interface.js";
7
8
  /** The value of a record, read from its blueprint. */
8
- export type RecordValue<Record extends AnyRecordShape> = Record["blueprint"] extends IBlueprint<infer Properties> ? BlueprintValue<Properties> : never;
9
- /** Anything with a blueprint, as records are. */
9
+ export type RecordValue<Record extends AnyRecordShape> = Record extends {
10
+ readonly [Meta]: {
11
+ readonly blueprint: IBlueprint<infer Properties>;
12
+ };
13
+ } ? BlueprintValue<Properties> : Record extends {
14
+ readonly blueprint: IBlueprint<infer Properties>;
15
+ } ? BlueprintValue<Properties> : never;
16
+ /** A record instance's metadata, or a record class's statics. */
10
17
  type AnyRecordShape = {
11
- readonly blueprint: IBlueprint<any>;
18
+ readonly blueprint: AnyBlueprint;
19
+ } | {
20
+ readonly [Meta]: {
21
+ readonly blueprint: AnyBlueprint;
22
+ };
12
23
  };
13
24
  /** A side effect of a change to a key: its new value and the one it replaced. */
14
25
  export type RecordEffect<Value> = (value: Value, previous: Value) => void;
@@ -81,10 +92,11 @@ type RecordChangeMethods<Self, Properties extends IBlueprintCollection, Key exte
81
92
  readonly [K in Key]: (value: K extends keyof BlueprintValue<Properties> ? BlueprintValue<Properties>[K] : never) => Self;
82
93
  };
83
94
  export interface IRecord<Properties extends IBlueprintCollection> {
84
- /** The blueprint whose shape the record implements. */
85
- readonly blueprint: IBlueprint<Properties>;
86
- /** Which keys are sensitive, unique or have a `change.<key>` method. */
87
- readonly options: RecordSettings<Properties>;
95
+ /** Declaration metadata, separate from domain keys. */
96
+ readonly [Meta]: {
97
+ readonly blueprint: IBlueprint<Properties>;
98
+ readonly options: RecordSettings<Properties>;
99
+ };
88
100
  /** The value with the sensitive keys, and every `sensitive` property, masked. */
89
101
  toJSON(): Readonly<Partial<BlueprintData<Properties>>>;
90
102
  }
@@ -1,3 +1,7 @@
1
+ /** A query contains unsupported keys, ordering or pagination. */
2
+ export declare const InvalidRepositoryQuery: import("../../index.js").DeclaredExceptionClass<{
3
+ readonly reason: string;
4
+ }>;
1
5
  /** `repositoryOf` got something other than an entity, actions and a provider. */
2
6
  export declare const InvalidRepositoryDeclaration: import("../../index.js").DeclaredExceptionClass<{
3
7
  readonly reason: string;
@@ -3,5 +3,8 @@ import type { IRepositoryProvider } from "./types/repository.interface.js";
3
3
  * A provider that keeps the values in memory, one table per entity class,
4
4
  * enforcing its unique keys. Each `repositoryOf` without a provider gets its
5
5
  * own; repositories given the same one share the tables of their entities.
6
+ * Its `transaction` works on isolated snapshots: commits on success, discards
7
+ * on rejection, runs one at a time and makes other operations wait; nested
8
+ * transactions are savepoints.
6
9
  */
7
10
  export declare function inMemoryProvider(): IRepositoryProvider;
@@ -0,0 +1,6 @@
1
+ import type { ProviderDescription, ProviderOptions, ProviderSettings } from "./types/query.interface.js";
2
+ import type { IRepositoryProvider } from "./types/repository.interface.js";
3
+ /** Creates a provider once and attaches an immutable, serializable declaration out of band. */
4
+ export declare function providerOf<Options extends ProviderOptions = ProviderOptions, Provider extends IRepositoryProvider = IRepositoryProvider>(settings: ProviderSettings<Options, Provider>): Provider;
5
+ /** Describes a named provider; an anonymous one needs an explicit import on extraction. */
6
+ export declare function providerDescriptionOf(provider: IRepositoryProvider): ProviderDescription;
@@ -0,0 +1,9 @@
1
+ import type { RepositoryQuery } from "./types/query.interface.js";
2
+ import type { RepositoryContext, RepositoryData } from "./types/repository.interface.js";
3
+ /** Validates query structure and optionally normalizes domain values for storage. */
4
+ export declare function prepareQuery(context: RepositoryContext, input?: unknown, map?: (key: string, value: unknown) => unknown): RepositoryQuery;
5
+ /** Structural JSON/data equality without depending on object key insertion order. */
6
+ export declare function sameValue(a: unknown, b: unknown): boolean;
7
+ export declare function matchesQuery(data: RepositoryData, query: RepositoryQuery): boolean;
8
+ /** The rows of `query` after ordering, offset and limit. */
9
+ export declare function selectRows(rows: Iterable<RepositoryData>, query: RepositoryQuery): RepositoryData[];
@@ -1,3 +1,4 @@
1
+ import { type ExecutionScope } from "../use-case/scope.js";
1
2
  import { type RepositoryState } from "./state.js";
2
3
  import type { AnyRepositoryEntity, RepositoryData } from "./types/repository.interface.js";
3
4
  /** The relations of a repository of `entity` named `name`, one for each entity class it holds. */
@@ -11,6 +12,7 @@ export type ReferenceSession = Map<RepositoryState, Map<string, object>>;
11
12
  * entity they hold replaced by the entity, loaded in one batch for each
12
13
  * relation. An entity loaded twice in `session` is the same instance. Throws
13
14
  * `InvalidRepositoryData`, caused by `RepositoryEntityNotFound`, for an id
14
- * with no entity.
15
+ * with no entity. The providers of the `scope` of the call, if any, replace
16
+ * those of the relations.
15
17
  */
16
- export declare function resolveReferences(state: RepositoryState, datas: readonly RepositoryData[], session: ReferenceSession): Promise<readonly RepositoryData[]>;
18
+ export declare function resolveReferences(state: RepositoryState, datas: readonly RepositoryData[], session: ReferenceSession, scope?: ExecutionScope): Promise<readonly RepositoryData[]>;
@@ -7,6 +7,8 @@ export interface RepositorySettings<Entity extends AnyRepositoryEntity, Actions
7
7
  readonly entity: Entity;
8
8
  /** Its methods, e.g. `["findById", "findByEmail", "findManyByName", "create"]`. */
9
9
  readonly actions: Actions;
10
+ /** Physically remove rows (default), or retain them with `deletedAt`. */
11
+ readonly deletion?: "hard" | "soft";
10
12
  /** How it reads and writes; a new `inMemoryProvider()` by default. */
11
13
  readonly provider?: IRepositoryProvider<MergedEnvironment<Environment>>;
12
14
  /** The environments the provider gets, merged, as `context.env`. */
@@ -49,8 +51,8 @@ export declare abstract class AbstractRepository<Entity extends AnyRepositoryEnt
49
51
  * A repository of `entity` with the methods of `actions`: `findBy<Key>` for a
50
52
  * unique key (`id`, `options.unique`, a `unique` property), giving the entity
51
53
  * or `undefined`; `findManyBy<Key>` for any other key, giving a list;
52
- * `create`, `update`, `delete`
53
- * and `count`. Every method is async and reads and writes through `provider`
54
+ * `create`, `update`, `delete`, `count`, and the queries `findMany`,
55
+ * `exists` and `findPage`. Every method is async and reads and writes through `provider`
54
56
  * (a new `inMemoryProvider()` by default), which gets the variables of
55
57
  * `environmentDependencies` as `context.env`. The repository can be a
56
58
  * dependency of a use case, and `RepositoryOf` extends it with a class.
@@ -5,6 +5,7 @@ export interface RepositoryState {
5
5
  readonly name: string;
6
6
  readonly entity: AnyRepositoryEntity;
7
7
  readonly actions: readonly string[];
8
+ readonly deletion: "hard" | "soft";
8
9
  readonly provider: IRepositoryProvider;
9
10
  /** Whether `settings.provider` was given, and not the default one. */
10
11
  readonly explicitProvider: boolean;
@@ -24,7 +25,10 @@ export declare function contextOf(state: RepositoryState): RepositoryContext;
24
25
  export interface RepositoryInspection {
25
26
  readonly entity: AnyRepositoryEntity;
26
27
  readonly actions: readonly string[];
28
+ readonly deletion: "hard" | "soft";
27
29
  readonly explicitProvider: boolean;
30
+ /** The provider, only when the repository declared it. */
31
+ readonly provider: IRepositoryProvider | undefined;
28
32
  readonly environments: readonly object[];
29
33
  /** The repository state of each related entity class; compare with `inspect(...).state`. */
30
34
  readonly relations: readonly object[];
@@ -0,0 +1,67 @@
1
+ import type { EntityInstanceOf, EntityKeyOf, EntityValueOf, IRepositoryProvider, QueryValueOf } from "./repository.interface.js";
2
+ type QueryKey<Entity> = unknown extends Entity ? string : EntityKeyOf<Entity>;
3
+ type QueryValue<Entity, Key extends string> = unknown extends Entity ? unknown : Key extends keyof EntityValueOf<Entity> ? QueryValueOf<EntityValueOf<Entity>[Key]> : unknown;
4
+ /** Equality, or membership in a list. Conditions on different keys are ANDed. */
5
+ export type RepositoryWhere<Entity = unknown> = {
6
+ readonly [Key in QueryKey<Entity>]?: QueryValue<Entity, Key> | {
7
+ readonly in: readonly QueryValue<Entity, Key>[];
8
+ };
9
+ };
10
+ /** Ordered lexicographically; ties retain provider order. Missing values sort first. */
11
+ export interface RepositoryOrder<Key extends string = string> {
12
+ readonly key: Key;
13
+ readonly direction?: "asc" | "desc";
14
+ }
15
+ /** The v2 query shared by providers and typed repository reads. */
16
+ export interface RepositoryQuery<Entity = unknown> {
17
+ readonly where?: RepositoryWhere<Entity>;
18
+ readonly orderBy?: readonly RepositoryOrder<QueryKey<Entity>>[];
19
+ /** Nonnegative integer; zero returns an empty result. */
20
+ readonly limit?: number;
21
+ /** Nonnegative integer, applied before limit. */
22
+ readonly offset?: number;
23
+ /** Include destroyed rows; repository defaults depend on its deletion policy. */
24
+ readonly withDeleted?: boolean;
25
+ }
26
+ /** A page and the unpaginated number of matching live (or explicitly included) rows. */
27
+ export interface RepositoryPage<Entity> {
28
+ readonly items: readonly EntityInstanceOf<Entity>[];
29
+ readonly total: number;
30
+ readonly limit: number;
31
+ readonly offset: number;
32
+ }
33
+ export interface RepositoryFindMany<Entity> {
34
+ findMany(query?: RepositoryQuery<Entity>): Promise<EntityInstanceOf<Entity>[]>;
35
+ }
36
+ export interface RepositoryExists<Entity> {
37
+ exists(query?: RepositoryQuery<Entity>): Promise<boolean>;
38
+ }
39
+ export interface RepositoryFindPage<Entity> {
40
+ /** Defaults to limit 20 and offset 0. */
41
+ findPage(query?: RepositoryQuery<Entity>): Promise<RepositoryPage<Entity>>;
42
+ }
43
+ /** JSON values retained in provider descriptors, including explicit import references. */
44
+ export type ProviderOption = null | boolean | number | string | readonly ProviderOption[] | {
45
+ readonly [key: string]: ProviderOption;
46
+ };
47
+ export type ProviderOptions = Readonly<Record<string, ProviderOption>>;
48
+ /** A named provider can be reconstructed; opaque providers require an import mapping. */
49
+ export type ProviderDescription = {
50
+ readonly kind: "provider";
51
+ readonly name: string;
52
+ readonly options: ProviderOptions;
53
+ } | {
54
+ readonly kind: "opaque";
55
+ };
56
+ /** Equality and `in` are mandatory; additional operators are advertised here. */
57
+ export interface ProviderCapabilities {
58
+ readonly operators?: readonly string[];
59
+ readonly transactions?: boolean;
60
+ }
61
+ /** Declaration input for a named provider, implemented by providerOf in T10. */
62
+ export interface ProviderSettings<Options extends ProviderOptions = ProviderOptions, Provider extends IRepositoryProvider = IRepositoryProvider> {
63
+ readonly name: string;
64
+ readonly options?: Options;
65
+ readonly create: (options: Options) => Provider;
66
+ }
67
+ export {};
@@ -3,6 +3,7 @@ import type { AbstractEntity, EntityUniqueKeys } from "../../entity/abstract-ent
3
3
  import type { AnyEntityClass } from "../../entity/event.js";
4
4
  import type { RecordSettings } from "../../record/types/record.interface.js";
5
5
  import type { AbstractRepository } from "../repository.js";
6
+ import type { ProviderCapabilities, RepositoryExists, RepositoryFindMany, RepositoryFindPage, RepositoryQuery } from "./query.interface.js";
6
7
  /** Any class made by `entityOf`, or extending one, that a repository holds. */
7
8
  export type AnyRepositoryEntity = AnyRecordClass & AnyEntityClass & {
8
9
  readonly [EntityUniqueKeys]: readonly string[];
@@ -42,8 +43,8 @@ export interface RepositoryDelete<Entity> {
42
43
  delete(entity: EntityInstanceOf<Entity> | string): Promise<EntityInstanceOf<Entity>>;
43
44
  }
44
45
  /** `count`: how many entities it holds. */
45
- export interface RepositoryCount {
46
- count(): Promise<number>;
46
+ export interface RepositoryCount<Entity = unknown> {
47
+ count(query?: RepositoryQuery<Entity>): Promise<number>;
47
48
  }
48
49
  /** The `findBy<Key>` of `Actions`, one for each unique key: the entity, or `undefined`. */
49
50
  export type RepositoryFindBy<Entity, Actions extends string = string> = {
@@ -64,10 +65,11 @@ export type RepositoryFindManyBy<Entity, Actions extends string = string> = {
64
65
  /**
65
66
  * The methods of `Actions` for `Entity`. Declared as methods, not properties,
66
67
  * so that a class extending a repository can override them.
68
+ * @internal
67
69
  */
68
- export type RepositoryMethods<Entity, Actions extends string = string> = RepositoryFindBy<Entity, Actions> & RepositoryFindManyBy<Entity, Actions> & ("create" extends Actions ? RepositoryCreate<Entity> : unknown) & ("update" extends Actions ? RepositoryUpdate<Entity> : unknown) & ("delete" extends Actions ? RepositoryDelete<Entity> : unknown) & ("count" extends Actions ? RepositoryCount : unknown);
70
+ export type RepositoryMethods<Entity, Actions extends string = string> = RepositoryFindBy<Entity, Actions> & RepositoryFindManyBy<Entity, Actions> & ("create" extends Actions ? RepositoryCreate<Entity> : unknown) & ("update" extends Actions ? RepositoryUpdate<Entity> : unknown) & ("delete" extends Actions ? RepositoryDelete<Entity> : unknown) & ("count" extends Actions ? RepositoryCount<Entity> : unknown) & ("findMany" extends Actions ? RepositoryFindMany<Entity> : unknown) & ("exists" extends Actions ? RepositoryExists<Entity> : unknown) & ("findPage" extends Actions ? RepositoryFindPage<Entity> : unknown);
69
71
  /** A method a repository of `Entity` can have, e.g. `"findByEmail"`. */
70
- export type RepositoryAction<Entity> = keyof RepositoryFindBy<Entity> | keyof RepositoryFindManyBy<Entity> | "create" | "update" | "delete" | "count";
72
+ export type RepositoryAction<Entity> = keyof RepositoryFindBy<Entity> | keyof RepositoryFindManyBy<Entity> | "create" | "update" | "delete" | "count" | "findMany" | "exists" | "findPage";
71
73
  /**
72
74
  * A repository of `Entity` with the methods of `Actions`, whose provider gets
73
75
  * `Environment` as `context.env`, e.g.
@@ -96,17 +98,15 @@ export interface RepositoryContext<Environment = unknown> {
96
98
  * `RepositoryEntityNotFound` for an unknown id.
97
99
  */
98
100
  export interface IRepositoryProvider<Environment = unknown> {
99
- findOne(context: RepositoryContext<Environment>, key: string, value: unknown): Promise<RepositoryData | undefined>;
100
- findMany(context: RepositoryContext<Environment>, key: string, value: unknown): Promise<readonly RepositoryData[]>;
101
+ findOne(context: RepositoryContext<Environment>, query?: RepositoryQuery): Promise<RepositoryData | undefined>;
102
+ findMany(context: RepositoryContext<Environment>, query?: RepositoryQuery): Promise<readonly RepositoryData[]>;
101
103
  insert(context: RepositoryContext<Environment>, data: RepositoryData): Promise<void>;
102
104
  update(context: RepositoryContext<Environment>, data: RepositoryData): Promise<void>;
103
105
  remove(context: RepositoryContext<Environment>, id: string): Promise<void>;
104
- count(context: RepositoryContext<Environment>): Promise<number>;
105
- /**
106
- * Optional: every value whose `key` is one of `values`, in a single call.
107
- * A repository uses it to load the entities another one holds by id, and
108
- * calls `findOne` for each id when it is missing.
109
- */
110
- findIn?(context: RepositoryContext<Environment>, key: string, values: readonly unknown[]): Promise<readonly RepositoryData[]>;
106
+ count(context: RepositoryContext<Environment>, query?: RepositoryQuery): Promise<number>;
107
+ readonly capabilities?: ProviderCapabilities;
108
+ transaction?<Result>(run: (provider: IRepositoryProvider<Environment>) => Promise<Result>): Promise<Result>;
109
+ /** Releases what the provider holds (connections, files); `app.stop()` calls it. */
110
+ close?(): void | Promise<void>;
111
111
  }
112
112
  export {};
@@ -1,8 +1,15 @@
1
1
  import type { Static, TSchema } from "typebox";
2
2
  import type { Validator } from "typebox/compile";
3
- export type ISchemaType<SchemaType extends TSchema> = Validator<Record<string, TSchema>, SchemaType>;
4
- export interface ISchema<SchemaType extends TSchema> {
5
- match(value: unknown): value is Static<SchemaType>;
3
+ /**
4
+ * The compiled validator of a schema. `Value` is the schema's value, computed
5
+ * once where `SchemaType` is known: TypeScript then relates two of them by
6
+ * their values, without measuring `Static` itself, which costs about a second
7
+ * on every check.
8
+ */
9
+ export type ISchemaType<SchemaType extends TSchema, Value = Static<SchemaType>> = Validator<Record<string, TSchema>, SchemaType, Value, Value>;
10
+ /** A schema; `Value` is its value, as in `ISchemaType`. */
11
+ export interface ISchema<SchemaType extends TSchema, Value = Static<SchemaType>> {
12
+ match(value: unknown): value is Value;
6
13
  /**
7
14
  * Whether `value` has the structure of the schema (its types), ignoring
8
15
  * constraints such as `minLength`, `format`, `pattern` or `const`.
@@ -10,5 +17,5 @@ export interface ISchema<SchemaType extends TSchema> {
10
17
  matchShape(value: unknown): boolean;
11
18
  errors(value: unknown): string[];
12
19
  toString(): string;
13
- get type(): ISchemaType<SchemaType>;
20
+ get type(): ISchemaType<SchemaType, Value>;
14
21
  }
@@ -0,0 +1,2 @@
1
+ /** Declaration metadata, separate from the string keys of a record's domain. */
2
+ export declare const Meta: unique symbol;