@afora/core 0.0.8 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +149 -23
  2. package/dist/adapters.d.ts +25 -0
  3. package/dist/adapters.js +32 -0
  4. package/dist/core/application/application.d.ts +30 -0
  5. package/dist/core/application/application.js +1291 -0
  6. package/dist/core/application/exceptions.d.ts +17 -0
  7. package/dist/core/application/exceptions.js +85 -0
  8. package/dist/core/application/migrate.d.ts +19 -0
  9. package/dist/core/application/migrate.js +67 -0
  10. package/dist/core/application/providers.js +108 -0
  11. package/dist/core/application/registry.js +23 -0
  12. package/dist/core/application/schema.d.ts +30 -0
  13. package/dist/core/application/schema.js +398 -0
  14. package/dist/core/application/start.js +162 -0
  15. package/dist/core/application/types/application.interface.d.ts +60 -3
  16. package/dist/core/auth/jwt.d.ts +30 -0
  17. package/dist/core/auth/jwt.js +200 -0
  18. package/dist/core/auth/types/jwt.interface.d.ts +74 -0
  19. package/dist/core/blueprint/blueprint-plugin.js +7 -0
  20. package/dist/core/blueprint/blueprint.js +146 -0
  21. package/dist/core/blueprint/callable.js +13 -0
  22. package/dist/core/blueprint/exceptions.d.ts +5 -0
  23. package/dist/core/blueprint/exceptions.js +20 -0
  24. package/dist/core/blueprint/is-blueprint.js +4 -0
  25. package/dist/core/blueprint/list.js +19 -0
  26. package/dist/core/blueprint/optional.d.ts +3 -1
  27. package/dist/core/blueprint/optional.js +69 -0
  28. package/dist/core/blueprint/record-field.js +14 -0
  29. package/dist/core/blueprint/slice.js +127 -0
  30. package/dist/core/blueprint/types/blueprint.interface.d.ts +95 -5
  31. package/dist/core/blueprint/variant.d.ts +32 -0
  32. package/dist/core/blueprint/variant.js +72 -0
  33. package/dist/core/command/command.js +428 -0
  34. package/dist/core/command/exceptions.js +30 -0
  35. package/dist/core/command/types/command.interface.d.ts +10 -1
  36. package/dist/core/entity/abstract-entity.d.ts +24 -0
  37. package/dist/core/entity/abstract-entity.js +181 -0
  38. package/dist/core/entity/entity.d.ts +1 -1
  39. package/dist/core/entity/entity.js +209 -0
  40. package/dist/core/entity/event.d.ts +39 -0
  41. package/dist/core/entity/event.js +80 -0
  42. package/dist/core/entity/exceptions.d.ts +9 -0
  43. package/dist/core/entity/exceptions.js +27 -0
  44. package/dist/core/entity/trust.d.ts +24 -0
  45. package/dist/core/entity/trust.js +7 -0
  46. package/dist/core/entity/types/entity.interface.d.ts +30 -0
  47. package/dist/core/entity/version.js +13 -0
  48. package/dist/core/environment/environment.d.ts +26 -3
  49. package/dist/core/environment/environment.js +71 -0
  50. package/dist/core/event-policy/event-policy.js +76 -0
  51. package/dist/core/event-policy/exceptions.js +12 -0
  52. package/dist/core/event-policy/publish.js +98 -0
  53. package/dist/core/event-policy/publishers.js +18 -0
  54. package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
  55. package/dist/core/exception/enums/exception-layers.js +15 -0
  56. package/dist/core/exception/exception.d.ts +15 -0
  57. package/dist/core/exception/exception.js +128 -0
  58. package/dist/core/exception/exported.js +49 -0
  59. package/dist/core/exception/guard.d.ts +20 -1
  60. package/dist/core/exception/guard.js +16 -0
  61. package/dist/core/exception/types/exception.interface.d.ts +13 -0
  62. package/dist/core/json.js +19 -0
  63. package/dist/core/logger/exceptions.js +7 -0
  64. package/dist/core/logger/logger.d.ts +14 -1
  65. package/dist/core/logger/logger.js +178 -0
  66. package/dist/core/openapi/exceptions.js +7 -0
  67. package/dist/core/openapi/openapi.js +132 -0
  68. package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
  69. package/dist/core/output/access.d.ts +16 -0
  70. package/dist/core/output/access.js +66 -0
  71. package/dist/core/output/coerce.js +38 -0
  72. package/dist/core/output/exceptions.d.ts +16 -0
  73. package/dist/core/output/exceptions.js +22 -0
  74. package/dist/core/output/http.d.ts +36 -0
  75. package/dist/core/output/http.js +351 -0
  76. package/dist/core/output/openapi-sink.d.ts +33 -4
  77. package/dist/core/output/openapi-sink.js +571 -0
  78. package/dist/core/output/output.js +192 -0
  79. package/dist/core/output/problem.js +30 -0
  80. package/dist/core/output/select.js +28 -0
  81. package/dist/core/output/stage.d.ts +7 -2
  82. package/dist/core/output/stage.js +111 -0
  83. package/dist/core/output/status.d.ts +1 -1
  84. package/dist/core/output/status.js +52 -0
  85. package/dist/core/output/types/http.interface.d.ts +134 -0
  86. package/dist/core/output/types/output.interface.d.ts +49 -5
  87. package/dist/core/parse/issue.d.ts +28 -3
  88. package/dist/core/parse/issue.js +31 -0
  89. package/dist/core/parse/parse.d.ts +16 -0
  90. package/dist/core/parse/parse.js +323 -0
  91. package/dist/core/parse/strip.js +48 -0
  92. package/dist/core/property/abstract-property.d.ts +9 -0
  93. package/dist/core/property/abstract-property.js +37 -0
  94. package/dist/core/property/define-property.d.ts +34 -6
  95. package/dist/core/property/define-property.js +209 -0
  96. package/dist/core/property/enums/property-states.js +8 -0
  97. package/dist/core/property/exceptions.js +12 -0
  98. package/dist/core/property/input.js +10 -0
  99. package/dist/core/property/is-property.js +3 -0
  100. package/dist/core/property/lineage.js +8 -0
  101. package/dist/core/property/property.d.ts +27 -0
  102. package/dist/core/property/property.js +46 -0
  103. package/dist/core/property/refine.js +103 -0
  104. package/dist/core/property/types/property-options.interface.d.ts +10 -0
  105. package/dist/core/property/types/property-type.interface.d.ts +4 -0
  106. package/dist/core/record/abstract-record.d.ts +8 -0
  107. package/dist/core/record/abstract-record.js +307 -0
  108. package/dist/core/record/exceptions.js +49 -0
  109. package/dist/core/record/marks.js +7 -0
  110. package/dist/core/record/mask.js +60 -0
  111. package/dist/core/record/name.js +23 -0
  112. package/dist/core/record/plugin-catalog.js +115 -0
  113. package/dist/core/record/plugin.js +198 -0
  114. package/dist/core/record/record.js +117 -0
  115. package/dist/core/record/types/record.interface.d.ts +17 -1
  116. package/dist/core/repository/cursor.js +83 -0
  117. package/dist/core/repository/exceptions.d.ts +13 -0
  118. package/dist/core/repository/exceptions.js +72 -0
  119. package/dist/core/repository/in-memory.d.ts +2 -2
  120. package/dist/core/repository/in-memory.js +196 -0
  121. package/dist/core/repository/migrate.d.ts +20 -0
  122. package/dist/core/repository/migrate.js +254 -0
  123. package/dist/core/repository/plan.js +27 -0
  124. package/dist/core/repository/provider.js +29 -0
  125. package/dist/core/repository/query.js +153 -0
  126. package/dist/core/repository/references.js +196 -0
  127. package/dist/core/repository/repository.d.ts +22 -2
  128. package/dist/core/repository/repository.js +720 -0
  129. package/dist/core/repository/state.d.ts +2 -0
  130. package/dist/core/repository/state.js +138 -0
  131. package/dist/core/repository/types/query.interface.d.ts +13 -0
  132. package/dist/core/repository/types/repository.interface.d.ts +50 -5
  133. package/dist/core/repository/types/schema.interface.d.ts +132 -0
  134. package/dist/core/repository/upcast.d.ts +64 -0
  135. package/dist/core/repository/upcast.js +179 -0
  136. package/dist/core/schema/schema.d.ts +37 -0
  137. package/dist/core/schema/schema.js +542 -0
  138. package/dist/core/standard/standard-schema.js +1 -0
  139. package/dist/core/standard/standard.js +23 -0
  140. package/dist/core/symbols/demo.d.ts +4 -0
  141. package/dist/core/symbols/demo.js +1 -0
  142. package/dist/core/symbols/kind.d.ts +1 -1
  143. package/dist/core/symbols/kind.js +1 -0
  144. package/dist/core/symbols/meta.js +1 -0
  145. package/dist/core/symbols/state.d.ts +5 -0
  146. package/dist/core/symbols/state.js +1 -0
  147. package/dist/core/tracing/exceptions.d.ts +4 -0
  148. package/dist/core/tracing/exceptions.js +7 -0
  149. package/dist/core/tracing/tracing.d.ts +72 -0
  150. package/dist/core/tracing/tracing.js +101 -0
  151. package/dist/core/use-case/access.d.ts +12 -0
  152. package/dist/core/use-case/access.js +56 -0
  153. package/dist/core/use-case/decorators.d.ts +40 -0
  154. package/dist/core/use-case/decorators.js +65 -0
  155. package/dist/core/use-case/exceptions.d.ts +5 -1
  156. package/dist/core/use-case/exceptions.js +48 -0
  157. package/dist/core/use-case/plugin-catalog.js +68 -0
  158. package/dist/core/use-case/plugin.js +133 -0
  159. package/dist/core/use-case/route.d.ts +18 -0
  160. package/dist/core/use-case/route.js +200 -0
  161. package/dist/core/use-case/scope.d.ts +5 -0
  162. package/dist/core/use-case/scope.js +4 -0
  163. package/dist/core/use-case/steps/describe.d.ts +5 -1
  164. package/dist/core/use-case/steps/describe.js +24 -0
  165. package/dist/core/use-case/steps/references.js +105 -0
  166. package/dist/core/use-case/steps/run.js +139 -0
  167. package/dist/core/use-case/steps/types/check.interface.d.ts +233 -0
  168. package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
  169. package/dist/core/use-case/steps/validate.js +368 -0
  170. package/dist/core/use-case/target.js +100 -0
  171. package/dist/core/use-case/types/access.interface.d.ts +39 -0
  172. package/dist/core/use-case/types/route.interface.d.ts +86 -0
  173. package/dist/core/use-case/use-case.d.ts +127 -2
  174. package/dist/core/use-case/use-case.js +493 -0
  175. package/dist/experimental.d.ts +18 -1
  176. package/dist/experimental.js +18 -123
  177. package/dist/index.d.ts +49 -51
  178. package/dist/index.js +213 -443
  179. package/dist/std/blueprint-plugin.js +42 -0
  180. package/dist/std/blueprint.d.ts +21 -0
  181. package/dist/std/blueprint.js +3 -0
  182. package/dist/std/command.js +3 -0
  183. package/dist/std/entity.d.ts +20 -1
  184. package/dist/std/entity.js +30 -0
  185. package/dist/std/event-policies.js +3 -0
  186. package/dist/std/properties/contact.d.ts +3 -0
  187. package/dist/std/properties/contact.js +29 -0
  188. package/dist/std/properties/enum.js +26 -0
  189. package/dist/std/properties/exceptions.js +14 -0
  190. package/dist/std/properties/headless.js +45 -0
  191. package/dist/std/properties/identity.js +28 -0
  192. package/dist/std/properties/json.js +161 -0
  193. package/dist/std/properties/password.d.ts +0 -2
  194. package/dist/std/properties/password.js +79 -0
  195. package/dist/std/properties/primitives.d.ts +4 -0
  196. package/dist/std/properties/primitives.js +28 -0
  197. package/dist/std/properties/time.js +55 -0
  198. package/dist/std/record-plugins.d.ts +2 -2
  199. package/dist/std/record-plugins.js +73 -0
  200. package/dist/std/record.d.ts +20 -1
  201. package/dist/std/record.js +3 -0
  202. package/dist/std/resource.d.ts +31 -0
  203. package/dist/std/resource.js +39 -0
  204. package/dist/std/runtime.d.ts +6 -0
  205. package/dist/std/runtime.js +193 -0
  206. package/dist/std/stages.js +4 -0
  207. package/dist/std/use-cases/auth.d.ts +5 -1
  208. package/dist/std/use-cases/auth.js +193 -0
  209. package/dist/std/use-cases/crud.js +261 -0
  210. package/dist/std/use-cases/index.d.ts +1 -1
  211. package/dist/std/use-cases/index.js +12 -0
  212. package/dist/std/use-cases/lifecycle.js +97 -0
  213. package/dist/std/use-cases/shared.d.ts +2 -2
  214. package/dist/std/use-cases/shared.js +232 -0
  215. package/dist/std/use-cases/types.d.ts +102 -2
  216. package/dist/std/use-cases/types.js +0 -0
  217. package/dist/testing/http-provider-contract.d.ts +32 -0
  218. package/dist/testing/http-provider-contract.js +344 -0
  219. package/dist/testing/index.d.ts +2 -0
  220. package/dist/testing/index.js +3 -944
  221. package/dist/testing/memory-publisher.js +12 -0
  222. package/dist/testing/provider-contract.d.ts +26 -1
  223. package/dist/testing/provider-contract.js +1071 -0
  224. package/package.json +23 -3
  225. package/dist/index-qxfkyd6k.js +0 -1721
  226. package/dist/index-t5qwtyj5.js +0 -8194
@@ -2,6 +2,8 @@ import type { AnyBlueprint } from "../../blueprint/types/blueprint.interface.js"
2
2
  import type { AnyCommand } from "../../command/types/command.interface.js";
3
3
  import type { JsonValue } from "../../json.js";
4
4
  import type { Kind } from "../../symbols/kind.js";
5
+ import type { AccessDeclaration, Principal } from "../../use-case/types/access.interface.js";
6
+ import type { RouteDeclaration } from "../../use-case/types/route.interface.js";
5
7
  import type { AnyExceptionClass } from "../../use-case/use-case.js";
6
8
  /** The options of a stage: a JSON object. */
7
9
  export type StageOptions = {
@@ -9,9 +11,15 @@ export type StageOptions = {
9
11
  };
10
12
  /** An HTTP-style status by exception name. */
11
13
  export type OutputStatuses = Readonly<Record<string, number>>;
12
- /** The call of an operation: `context` is what the command (or use case) declares. */
14
+ /**
15
+ * The call of an operation: `context` is what the command (or use case)
16
+ * declares, and `principal` is who calls, as the sink authenticated it
17
+ * (`null` or absent for an anonymous caller). An operation with an `access`
18
+ * checks the principal before it runs.
19
+ */
13
20
  export interface OperationCallOptions {
14
21
  readonly context?: unknown;
22
+ readonly principal?: Principal | null;
15
23
  }
16
24
  /**
17
25
  * One operation of a command, as an output sees it. Frozen; transforms change
@@ -39,9 +47,16 @@ export interface OperationDescriptor {
39
47
  readonly context?: AnyBlueprint;
40
48
  /** The `errors` of the use case. */
41
49
  readonly errors: readonly AnyExceptionClass[];
50
+ /** The route the use case declares (`route` or `@Route`), if any. */
51
+ readonly route?: RouteDeclaration;
52
+ /** Who may call it, as the use case declares (`access` or `@Access`), if it does. */
53
+ readonly access?: AccessDeclaration;
42
54
  /** Set by transforms; `{}` initially. */
43
55
  readonly meta: Readonly<Record<string, unknown>>;
44
- /** `command.run(name, input, options)`. */
56
+ /**
57
+ * `command.run(name, input, { context })`, after the check of `access`:
58
+ * it rejects with `Unauthenticated` or `Forbidden` before anything runs.
59
+ */
45
60
  invoke(input: unknown, options?: OperationCallOptions): Promise<unknown>;
46
61
  }
47
62
  /** What `operationOf` can change. */
@@ -85,6 +100,11 @@ export type StageDescription = {
85
100
  } | {
86
101
  readonly kind: "opaque";
87
102
  };
103
+ /**
104
+ * What `transformOf` takes: the `name` of the stage, the `apply` function that
105
+ * changes each operation (or removes it) and an optional `validate` of its
106
+ * options.
107
+ */
88
108
  export interface TransformSettings<Options extends object> {
89
109
  readonly name: string;
90
110
  /** Returns the operation, a changed copy, or `null` to remove it. */
@@ -92,15 +112,30 @@ export interface TransformSettings<Options extends object> {
92
112
  /** Why `options` cannot be used, or `undefined`. Checked when the stage is made. */
93
113
  readonly validate?: (options: Options) => string | undefined;
94
114
  }
95
- export interface SinkSettings<Result, Options extends object> {
115
+ /**
116
+ * What `sinkOf` takes: the `name` of the stage, the `build` function that turns
117
+ * the operations into the result of the output (a server, a document...) and an
118
+ * optional `validate` of its options.
119
+ */
120
+ export interface SinkSettings<Result, Options extends object, Hooks = never> {
96
121
  readonly name: string;
97
- readonly build: (operations: readonly OperationDescriptor[], options: Options, context: StageContext) => Result;
122
+ /**
123
+ * `hooks` is the second argument the stage was made with: what cannot be
124
+ * JSON (functions, an instance of a server). It is not part of the
125
+ * description of the stage.
126
+ */
127
+ readonly build: (operations: readonly OperationDescriptor[], options: Options, context: StageContext, hooks: Hooks | undefined) => Result;
98
128
  /** Why `options` cannot be used, or `undefined`. Checked when the stage is made. */
99
129
  readonly validate?: (options: Options) => string | undefined;
100
130
  }
101
131
  /** Makes a stage from JSON options. */
102
132
  export type TransformFactory<Options extends object = StageOptions> = (options?: Options) => TransformStage<Options>;
103
- export type SinkFactory<Result = unknown, Options extends object = StageOptions> = (options?: Options) => SinkStage<Result, Options>;
133
+ /**
134
+ * Makes a sink stage from JSON `options` and, second, `hooks`: what cannot be
135
+ * JSON (functions, an instance of a server), which the description of the stage
136
+ * leaves out. What `sinkOf` returns.
137
+ */
138
+ export type SinkFactory<Result = unknown, Options extends object = StageOptions, Hooks = never> = (options?: Options, hooks?: Hooks) => SinkStage<Result, Options>;
104
139
  /** Any stage factory. */
105
140
  export type StageFactory = TransformFactory<any> | SinkFactory<any, any>;
106
141
  /** Stage factories by name. */
@@ -123,6 +158,11 @@ export interface OutputApplication {
123
158
  }
124
159
  /** What an output takes its commands from. */
125
160
  export type OutputSource = AnyCommand | OutputApplication;
161
+ /**
162
+ * What `outputOf` takes: the `name` of the output, the `commands` it exposes
163
+ * (commands, applications or a mix), the `statuses` by exception name and the
164
+ * `pipe` of stages that transform its operations and end in a sink.
165
+ */
126
166
  export interface OutputSettings<Pipe extends readonly Stage[]> {
127
167
  readonly name: string;
128
168
  /** The commands the output exposes: commands, applications (all their commands) or a mix. */
@@ -164,6 +204,10 @@ export interface Problem {
164
204
  /** Only with `debug`. */
165
205
  readonly trace?: string;
166
206
  }
207
+ /**
208
+ * What `problemOf` takes: the HTTP `status` of the problem and `debug`, which
209
+ * adds the `trace` and the real `detail` of a 5xx.
210
+ */
167
211
  export interface ProblemOptions {
168
212
  readonly status: number;
169
213
  readonly debug?: boolean;
@@ -1,6 +1,31 @@
1
1
  import type { Exception } from "../exception/exception.js";
2
- import type { Issue } from "./types/parse.interface.js";
3
- /** `issue` with a `cause` that is built the first time it is read, and kept. */
4
- export declare function withLazyCause(issue: Issue, build: () => Exception): Issue;
2
+ import type { Issue, IssueCode } from "./types/parse.interface.js";
3
+ /**
4
+ * What a step gives back, in place of a value, for a value it rejects; the
5
+ * issues are then found where the step says, so a valid value costs no result
6
+ * object.
7
+ * @internal
8
+ */
9
+ export declare const REJECTED: unique symbol;
10
+ /** Who knows the exception an issue stands for, and builds it only when asked. */
11
+ export interface IssueOrigin {
12
+ exception(): Exception;
13
+ }
14
+ /**
15
+ * An issue whose `cause` is built the first time it is read, by its origin,
16
+ * which keeps it: every issue of one origin has the same cause. Its own keys
17
+ * are those of any issue, `cause` is not one of them.
18
+ */
19
+ export declare class LazyIssue implements Issue {
20
+ #private;
21
+ readonly message: string;
22
+ readonly path: readonly (string | number)[];
23
+ readonly code: IssueCode;
24
+ readonly source: string;
25
+ constructor(message: string, path: readonly (string | number)[], code: IssueCode, source: string, origin: IssueOrigin);
26
+ get cause(): Exception;
27
+ /** A copy of the issue at `path`, with the same cause. */
28
+ at(path: readonly (string | number)[]): LazyIssue;
29
+ }
5
30
  /** A copy of `issue` at `path`, with the same `cause`, built lazily or not. */
6
31
  export declare function movedIssue(issue: Issue, path: readonly (string | number)[]): Issue;
@@ -0,0 +1,31 @@
1
+ export const REJECTED = Symbol("rejected");
2
+
3
+ export class LazyIssue {
4
+ message;
5
+ path;
6
+ code;
7
+ source;
8
+ #origin;
9
+ constructor(message, path, code, source, origin) {
10
+ this.message = message;
11
+ this.path = path;
12
+ this.code = code;
13
+ this.source = source;
14
+ this.#origin = origin;
15
+ }
16
+ get cause() {
17
+ return this.#origin.exception();
18
+ }
19
+ at(path) {
20
+ return new LazyIssue(this.message, path, this.code, this.source, this.#origin);
21
+ }
22
+ }
23
+ export function movedIssue(issue, path) {
24
+ if (issue instanceof LazyIssue)
25
+ return issue.at(path);
26
+ const moved = { ...issue, path };
27
+ const cause = Object.getOwnPropertyDescriptor(issue, "cause");
28
+ if (cause && !cause.enumerable)
29
+ Object.defineProperty(moved, "cause", cause);
30
+ return moved;
31
+ }
@@ -18,6 +18,22 @@ export declare function isPlainObject(value: unknown): value is Record<string, u
18
18
  * is parsing it, e.g. a record name.
19
19
  */
20
20
  export declare function parseCollection(properties: IBlueprintCollection, value: unknown, path: Path, source: string): ParseResult<Readonly<Record<string, unknown>>>;
21
+ /**
22
+ * Makes, once the rest of a collection is known to be valid, the values the
23
+ * caller left out for it (`undefined` in `handled`); it returns the keys it
24
+ * made that still have to be checked.
25
+ * @internal
26
+ */
27
+ export type Complete = (handled: Record<string, unknown>) => readonly string[] | undefined;
28
+ /**
29
+ * `parseCollection` at the root, as a function of the value alone: what it
30
+ * needs of `properties` is looked up on the first value and kept, instead of
31
+ * on every one. For what parses many values of one shape, a blueprint or a
32
+ * record class. The values of the keys in `vouched` are kept as they are,
33
+ * unchecked: for what the caller made itself and knows to be valid.
34
+ * @internal
35
+ */
36
+ export declare function collectionParserOf(properties: IBlueprintCollection, source: string): (value: unknown, vouched?: readonly string[], complete?: Complete) => ParseResult<Readonly<Record<string, unknown>>>;
21
37
  /**
22
38
  * `value` handled by `field`, or every issue found: a property runs its
23
39
  * handlers, a blueprint each of its fields, a list each of its items, and a
@@ -0,0 +1,323 @@
1
+ import { isBlueprint } from "../blueprint/is-blueprint.js";
2
+ import { isBlueprintList } from "../blueprint/list.js";
3
+ import { recordFieldOf } from "../blueprint/record-field.js";
4
+ import { isBlueprintVariant } from "../blueprint/variant.js";
5
+ import {
6
+ AbstractProperty,
7
+ parsingDefinitionOf
8
+ } from "../property/abstract-property.js";
9
+ import { parserOf, takeRejectionIssues } from "../property/define-property.js";
10
+ import { isProperty } from "../property/is-property.js";
11
+ import { recordNameOf } from "../record/name.js";
12
+ import { movedIssue, REJECTED } from "./issue.js";
13
+ const entriesOf = new WeakMap;
14
+ export function fieldsOf(properties) {
15
+ let entries = entriesOf.get(properties);
16
+ if (!entries) {
17
+ entries = Object.entries(properties);
18
+ entriesOf.set(properties, entries);
19
+ }
20
+ return entries;
21
+ }
22
+ export function isPlainObject(value) {
23
+ if (typeof value !== "object" || value === null)
24
+ return false;
25
+ const prototype = Object.getPrototypeOf(value);
26
+ return prototype === Object.prototype || prototype === null;
27
+ }
28
+ const PROPERTY = 0;
29
+ const LIST = 1;
30
+ const BLUEPRINT = 2;
31
+ const RECORD = 3;
32
+ const UNDECLARED_FIELD = 4;
33
+ const VARIANT = 5;
34
+ function stepOf(field) {
35
+ const record = recordFieldOf(field);
36
+ const kind = isBlueprintList(field) ? LIST : isBlueprint(field) ? BLUEPRINT : isBlueprintVariant(field) ? VARIANT : record ? RECORD : isProperty(field) ? PROPERTY : UNDECLARED_FIELD;
37
+ return {
38
+ kind,
39
+ property: kind === PROPERTY ? field : undefined,
40
+ parser: undefined,
41
+ list: kind === LIST ? field : undefined,
42
+ item: undefined,
43
+ blueprint: kind === BLUEPRINT ? field : undefined,
44
+ collection: undefined,
45
+ record: kind === RECORD ? record?.record : undefined,
46
+ optionalRecord: kind === RECORD && record?.optional === true,
47
+ variant: kind === VARIANT ? field : undefined,
48
+ variants: undefined
49
+ };
50
+ }
51
+ const UNDECLARED = {
52
+ kind: UNDECLARED_FIELD,
53
+ property: undefined,
54
+ parser: undefined,
55
+ list: undefined,
56
+ item: undefined,
57
+ blueprint: undefined,
58
+ collection: undefined,
59
+ record: undefined,
60
+ optionalRecord: false,
61
+ variant: undefined,
62
+ variants: undefined
63
+ };
64
+ const steps = new WeakMap;
65
+ function keptStepOf(field) {
66
+ if (typeof field !== "object" && typeof field !== "function" || field === null) {
67
+ return UNDECLARED;
68
+ }
69
+ let step = steps.get(field);
70
+ if (!step) {
71
+ step = stepOf(field);
72
+ steps.set(field, step);
73
+ }
74
+ return step;
75
+ }
76
+ const collections = new WeakMap;
77
+ function collectionOf(properties) {
78
+ let collection = collections.get(properties);
79
+ if (!collection) {
80
+ const fields = fieldsOf(properties);
81
+ collection = {
82
+ properties,
83
+ names: fields.map(([name]) => name),
84
+ steps: fields.map(([, field]) => keptStepOf(field))
85
+ };
86
+ collections.set(properties, collection);
87
+ }
88
+ return collection;
89
+ }
90
+ function runOf(base, source) {
91
+ return { source, base, keys: [], issues: undefined };
92
+ }
93
+ function pathOf(run, depth) {
94
+ const { base, keys } = run;
95
+ const path = base.length === 0 ? [] : [...base];
96
+ for (let index = 0;index < depth; index++) {
97
+ path.push(keys[index]);
98
+ }
99
+ return path;
100
+ }
101
+ function reject(run, code, message, path) {
102
+ run.issues ??= [];
103
+ run.issues.push({ code, message, path, source: run.source });
104
+ return REJECTED;
105
+ }
106
+ function resultOf(run, handled) {
107
+ return handled === REJECTED ? { issues: run.issues ?? [] } : { value: handled };
108
+ }
109
+ export function parseCollection(properties, value, path, source) {
110
+ const run = runOf(path, source);
111
+ return resultOf(run, collectionAt(collectionOf(properties), value, run, 0));
112
+ }
113
+ const ROOT = Object.freeze([]);
114
+ export function collectionParserOf(properties, source) {
115
+ let collection;
116
+ return (value, vouched, complete) => {
117
+ collection ??= collectionOf(properties);
118
+ const run = runOf(ROOT, source);
119
+ return resultOf(run, collectionAt(collection, value, run, 0, vouched, undefined, complete));
120
+ };
121
+ }
122
+ function collectionAt(collection, value, run, depth, vouched, tag, complete) {
123
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
124
+ return reject(run, "invalid_type", "must be an object", pathOf(run, depth));
125
+ }
126
+ const { properties, names, steps } = collection;
127
+ let rejected = false;
128
+ const keys = Object.keys(value);
129
+ for (let index = 0;index < keys.length; index++) {
130
+ const name = keys[index];
131
+ if (name === names[index] || name === tag || Object.hasOwn(properties, name)) {
132
+ continue;
133
+ }
134
+ rejected = true;
135
+ reject(run, "undeclared_key", "is not declared", [
136
+ ...pathOf(run, depth),
137
+ name
138
+ ]);
139
+ }
140
+ const handled = tag === undefined ? {} : { [tag]: value[tag] };
141
+ const first = vouched?.[0];
142
+ const second = vouched?.[1];
143
+ const third = vouched?.[2];
144
+ for (let index = 0;index < names.length; index++) {
145
+ const name = names[index];
146
+ if (vouched !== undefined && (name === first || name === second || name === third || vouched.length > 3 && vouched.includes(name))) {
147
+ handled[name] = value[name];
148
+ continue;
149
+ }
150
+ run.keys[depth] = name;
151
+ const item = fieldAt(steps[index], value[name], run, depth + 1);
152
+ if (item === REJECTED)
153
+ rejected = true;
154
+ else if (item !== undefined)
155
+ handled[name] = item;
156
+ }
157
+ if (!rejected && complete !== undefined) {
158
+ for (const name of complete(handled) ?? []) {
159
+ run.keys[depth] = name;
160
+ const item = fieldAt(steps[names.indexOf(name)], handled[name], run, depth + 1);
161
+ if (item === REJECTED)
162
+ rejected = true;
163
+ else if (item !== undefined)
164
+ handled[name] = item;
165
+ }
166
+ }
167
+ return rejected ? REJECTED : Object.freeze(handled);
168
+ }
169
+ export function parseField(field, value, path, source) {
170
+ const run = runOf(path, source);
171
+ return resultOf(run, fieldAt(keptStepOf(field), value, run, 0));
172
+ }
173
+ const DECLARED_SAFE_PARSE = AbstractProperty.safeParse;
174
+ function fieldAt(step, value, run, depth) {
175
+ switch (step.kind) {
176
+ case PROPERTY:
177
+ return propertyAt(step, value, run, depth);
178
+ case LIST: {
179
+ const list = step.list;
180
+ if (value === undefined) {
181
+ return list.optional ? value : required(run, depth);
182
+ }
183
+ if (!Array.isArray(value)) {
184
+ return reject(run, "invalid_type", "must be an array", pathOf(run, depth));
185
+ }
186
+ const { minItems, maxItems } = list;
187
+ let rejected = false;
188
+ if (minItems !== undefined && value.length < minItems || maxItems !== undefined && value.length > maxItems) {
189
+ rejected = true;
190
+ const path = pathOf(run, depth);
191
+ if (minItems !== undefined && value.length < minItems) {
192
+ reject(run, "too_few_items", `must have at least ${minItems} items`, path);
193
+ }
194
+ if (maxItems !== undefined && value.length > maxItems) {
195
+ reject(run, "too_many_items", `must have at most ${maxItems} items`, path);
196
+ }
197
+ }
198
+ step.item ??= keptStepOf(list.item);
199
+ const { item } = step;
200
+ const items = [];
201
+ for (let index = 0;index < value.length; index++) {
202
+ run.keys[depth] = index;
203
+ const handled = fieldAt(item, value[index], run, depth + 1);
204
+ if (handled === REJECTED)
205
+ rejected = true;
206
+ else
207
+ items.push(handled);
208
+ }
209
+ return rejected ? REJECTED : Object.freeze(items);
210
+ }
211
+ case BLUEPRINT: {
212
+ const blueprint = step.blueprint;
213
+ if (value === undefined) {
214
+ return blueprint.optional ? value : required(run, depth);
215
+ }
216
+ step.collection ??= collectionOf(blueprint.properties);
217
+ return collectionAt(step.collection, value, run, depth);
218
+ }
219
+ case VARIANT: {
220
+ const variant = step.variant;
221
+ if (value === undefined) {
222
+ return variant.optional ? value : required(run, depth);
223
+ }
224
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
225
+ return reject(run, "invalid_type", "must be an object", pathOf(run, depth));
226
+ }
227
+ let { variants } = step;
228
+ if (variants === undefined) {
229
+ variants = new Map;
230
+ const declared = variant.variants;
231
+ for (const tag of Object.keys(declared)) {
232
+ const one = declared[tag];
233
+ variants.set(tag, isBlueprint(one) ? collectionOf(one.properties) : one);
234
+ }
235
+ step.variants = variants;
236
+ }
237
+ if (!isPlainObject(value)) {
238
+ for (const one of variants.values()) {
239
+ if (typeof one === "function" && value instanceof one)
240
+ return value;
241
+ }
242
+ }
243
+ const { key } = variant;
244
+ const tag = value[key];
245
+ const named = typeof tag === "string" ? variants.get(tag) : undefined;
246
+ if (named === undefined) {
247
+ return reject(run, "invalid_value", "must be equal to one of the allowed values", [...pathOf(run, depth), key]);
248
+ }
249
+ if (typeof named !== "function") {
250
+ return collectionAt(named, value, run, depth, undefined, key);
251
+ }
252
+ const { [key]: _name, ...input } = value;
253
+ const result = named.safeParse(input);
254
+ if (!result.issues)
255
+ return result.value;
256
+ const path = pathOf(run, depth);
257
+ run.issues ??= [];
258
+ for (const issue of result.issues) {
259
+ run.issues.push(movedIssue(issue, [...path, ...issue.path]));
260
+ }
261
+ return REJECTED;
262
+ }
263
+ case RECORD: {
264
+ const Record = step.record;
265
+ if (value === undefined) {
266
+ return step.optionalRecord ? value : required(run, depth);
267
+ }
268
+ if (value instanceof Record)
269
+ return value;
270
+ if (!isPlainObject(value)) {
271
+ return reject(run, "not_record", `must be an instance of record "${recordNameOf(Record)}"`, pathOf(run, depth));
272
+ }
273
+ const result = Record.safeParse(value);
274
+ if (!result.issues)
275
+ return result.value;
276
+ const path = pathOf(run, depth);
277
+ run.issues ??= [];
278
+ for (const issue of result.issues) {
279
+ run.issues.push(movedIssue(issue, [...path, ...issue.path]));
280
+ }
281
+ return REJECTED;
282
+ }
283
+ default:
284
+ return reject(run, "undeclared_key", "is not declared", pathOf(run, depth));
285
+ }
286
+ }
287
+ function required(run, depth) {
288
+ return reject(run, "required", "is required", pathOf(run, depth));
289
+ }
290
+ function propertyAt(step, value, run, depth) {
291
+ const property = step.property;
292
+ if (property.safeParse === DECLARED_SAFE_PARSE) {
293
+ let { parser } = step;
294
+ if (parser === undefined) {
295
+ parser = parserOf(parsingDefinitionOf(property)) ?? null;
296
+ step.parser = parser;
297
+ }
298
+ if (parser !== null) {
299
+ const handled = parser(value);
300
+ if (handled !== REJECTED)
301
+ return handled;
302
+ run.issues ??= [];
303
+ takeRejectionIssues(pathOf(run, depth), run.issues);
304
+ return REJECTED;
305
+ }
306
+ }
307
+ const result = property.safeParse(value);
308
+ if (!result.issues)
309
+ return result.value;
310
+ const path = pathOf(run, depth);
311
+ run.issues ??= [];
312
+ if (result.issues.some((issue) => Object.isFrozen(issue))) {
313
+ for (const issue of result.issues) {
314
+ run.issues.push(movedIssue(issue, [...path, ...issue.path]));
315
+ }
316
+ } else {
317
+ for (const issue of result.issues) {
318
+ issue.path = [...path, ...issue.path];
319
+ run.issues.push(issue);
320
+ }
321
+ }
322
+ return REJECTED;
323
+ }
@@ -0,0 +1,48 @@
1
+ import { isBlueprint } from "../blueprint/is-blueprint.js";
2
+ import { isBlueprintList } from "../blueprint/list.js";
3
+ import { recordFieldOf } from "../blueprint/record-field.js";
4
+ import { isBlueprintVariant, variantOfValue } from "../blueprint/variant.js";
5
+ import { fieldsOf, isPlainObject } from "./parse.js";
6
+ function collectionOf(properties, value) {
7
+ if (!isPlainObject(value))
8
+ return value;
9
+ const kept = {};
10
+ for (const [name, field] of fieldsOf(properties)) {
11
+ if (!Object.hasOwn(value, name))
12
+ continue;
13
+ const item = fieldOf(field, value[name]);
14
+ if (item === undefined)
15
+ continue;
16
+ Object.defineProperty(kept, name, {
17
+ value: item,
18
+ enumerable: true,
19
+ writable: true,
20
+ configurable: true
21
+ });
22
+ }
23
+ return kept;
24
+ }
25
+ function fieldOf(field, value) {
26
+ if (isBlueprintList(field)) {
27
+ return Array.isArray(value) ? value.map((item) => fieldOf(field.item, item)) : value;
28
+ }
29
+ if (isBlueprint(field))
30
+ return collectionOf(field.properties, value);
31
+ if (isBlueprintVariant(field)) {
32
+ const variant = variantOfValue(field, value);
33
+ if (!variant || !isPlainObject(value))
34
+ return value;
35
+ return {
36
+ [field.key]: value[field.key],
37
+ ...collectionOf(variant.properties, value)
38
+ };
39
+ }
40
+ const record = recordFieldOf(field);
41
+ if (record) {
42
+ return collectionOf(record.record.blueprint.properties, value);
43
+ }
44
+ return value;
45
+ }
46
+ export function stripUndeclared(shape, value) {
47
+ return fieldOf(shape, value);
48
+ }
@@ -9,6 +9,15 @@ import type { AnyPropertyConstructor } from "./types/property-collection.type.js
9
9
  import type { PropertyValue } from "./types/property-constructor.type.js";
10
10
  import type { PropertyOptions } from "./types/property-options.interface.js";
11
11
  import type { IPropertyType } from "./types/property-type.interface.js";
12
+ /**
13
+ * The definition `Class` parses with. It does not depend on any member the
14
+ * constructor sets, and `parse` leaves nothing on it that the next one reads,
15
+ * so every `safeParse` shares one.
16
+ * @internal
17
+ */
18
+ export declare function parsingDefinitionOf(Class: {
19
+ prototype: object;
20
+ }): AbstractDefineProperty<TSchema>;
12
21
  /** A property instance; `Value` is the value of its schema, as in `ISchema`. */
13
22
  export declare abstract class AbstractProperty<SchemaType extends TSchema, Value = Static<SchemaType>> implements IProperty<SchemaType, Value> {
14
23
  get [State](): PropertyStates;
@@ -0,0 +1,37 @@
1
+ import { standardOfClass } from "../standard/standard.js";
2
+ import { Demo } from "../symbols/demo.js";
3
+ import { State } from "../symbols/state.js";
4
+ import { refinePropertyOf } from "./refine.js";
5
+ const parsingDefinitions = new WeakMap;
6
+ export function parsingDefinitionOf(Class) {
7
+ let definition = parsingDefinitions.get(Class);
8
+ if (!definition) {
9
+ definition = Object.create(Class.prototype).createDefinition();
10
+ parsingDefinitions.set(Class, definition);
11
+ }
12
+ return definition;
13
+ }
14
+
15
+ export class AbstractProperty {
16
+ get [State]() {
17
+ return this.definition[State];
18
+ }
19
+ definition;
20
+ value;
21
+ constructor(value) {
22
+ this.definition = this.createDefinition();
23
+ this.value = this.definition.make(value === Demo ? this.definition.type[Demo] : value);
24
+ }
25
+ static demo() {
26
+ return new this(Demo);
27
+ }
28
+ static safeParse(value) {
29
+ return parsingDefinitionOf(this).parse(value);
30
+ }
31
+ static with(options) {
32
+ return refinePropertyOf(this, options);
33
+ }
34
+ static get "~standard"() {
35
+ return standardOfClass(this);
36
+ }
37
+ }