@afora/core 0.0.9 → 0.0.12

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 (186) hide show
  1. package/README.md +62 -189
  2. package/dist/adapters.js +31 -55
  3. package/dist/core/application/application.d.ts +30 -0
  4. package/dist/core/application/application.js +1291 -0
  5. package/dist/core/application/exceptions.js +85 -0
  6. package/dist/core/application/migrate.js +67 -0
  7. package/dist/core/application/providers.js +108 -0
  8. package/dist/core/application/registry.js +23 -0
  9. package/dist/core/application/schema.js +398 -0
  10. package/dist/core/application/start.js +162 -0
  11. package/dist/core/application/types/application.interface.d.ts +23 -3
  12. package/dist/core/auth/jwt.js +200 -0
  13. package/dist/core/auth/types/jwt.interface.d.ts +10 -2
  14. package/dist/core/blueprint/blueprint-plugin.js +7 -0
  15. package/dist/core/blueprint/blueprint.js +146 -0
  16. package/dist/core/blueprint/callable.js +13 -0
  17. package/dist/core/blueprint/exceptions.js +20 -0
  18. package/dist/core/blueprint/is-blueprint.js +4 -0
  19. package/dist/core/blueprint/list.js +19 -0
  20. package/dist/core/blueprint/optional.js +69 -0
  21. package/dist/core/blueprint/record-field.js +14 -0
  22. package/dist/core/blueprint/slice.js +127 -0
  23. package/dist/core/blueprint/types/blueprint.interface.d.ts +16 -0
  24. package/dist/core/blueprint/variant.js +72 -0
  25. package/dist/core/command/command.js +428 -0
  26. package/dist/core/command/exceptions.js +30 -0
  27. package/dist/core/command/types/command.interface.d.ts +10 -1
  28. package/dist/core/entity/abstract-entity.d.ts +19 -0
  29. package/dist/core/entity/abstract-entity.js +181 -0
  30. package/dist/core/entity/entity.js +209 -0
  31. package/dist/core/entity/event.d.ts +39 -0
  32. package/dist/core/entity/event.js +80 -0
  33. package/dist/core/entity/exceptions.js +27 -0
  34. package/dist/core/entity/trust.js +7 -0
  35. package/dist/core/entity/version.js +13 -0
  36. package/dist/core/environment/environment.d.ts +26 -3
  37. package/dist/core/environment/environment.js +71 -0
  38. package/dist/core/event-policy/event-policy.js +76 -0
  39. package/dist/core/event-policy/exceptions.js +12 -0
  40. package/dist/core/event-policy/publish.js +98 -0
  41. package/dist/core/event-policy/publishers.js +18 -0
  42. package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
  43. package/dist/core/exception/enums/exception-layers.js +15 -0
  44. package/dist/core/exception/exception.d.ts +15 -0
  45. package/dist/core/exception/exception.js +128 -0
  46. package/dist/core/exception/exported.js +49 -0
  47. package/dist/core/exception/guard.d.ts +20 -1
  48. package/dist/core/exception/guard.js +16 -0
  49. package/dist/core/exception/types/exception.interface.d.ts +13 -0
  50. package/dist/core/json.js +19 -0
  51. package/dist/core/logger/exceptions.js +7 -0
  52. package/dist/core/logger/logger.d.ts +14 -1
  53. package/dist/core/logger/logger.js +178 -0
  54. package/dist/core/openapi/exceptions.js +7 -0
  55. package/dist/core/openapi/openapi.js +132 -0
  56. package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
  57. package/dist/core/output/access.js +66 -0
  58. package/dist/core/output/coerce.js +38 -0
  59. package/dist/core/output/exceptions.js +22 -0
  60. package/dist/{index-y280ew0k.js → core/output/http.js} +35 -80
  61. package/dist/core/output/openapi-sink.d.ts +10 -2
  62. package/dist/{index-hg1z2s2v.js → core/output/openapi-sink.js} +60 -544
  63. package/dist/core/output/output.js +192 -0
  64. package/dist/core/output/problem.js +30 -0
  65. package/dist/core/output/select.js +28 -0
  66. package/dist/core/output/stage.js +111 -0
  67. package/dist/core/output/status.js +52 -0
  68. package/dist/core/output/types/http.interface.d.ts +5 -1
  69. package/dist/core/output/types/output.interface.d.ts +24 -0
  70. package/dist/core/parse/issue.js +31 -0
  71. package/dist/core/parse/parse.d.ts +8 -1
  72. package/dist/core/parse/parse.js +323 -0
  73. package/dist/core/parse/strip.js +48 -0
  74. package/dist/core/property/abstract-property.js +37 -0
  75. package/dist/core/property/define-property.js +209 -0
  76. package/dist/core/property/enums/property-states.js +8 -0
  77. package/dist/core/property/exceptions.js +12 -0
  78. package/dist/core/property/input.js +10 -0
  79. package/dist/core/property/is-property.js +3 -0
  80. package/dist/core/property/lineage.js +8 -0
  81. package/dist/core/property/property.d.ts +27 -0
  82. package/dist/core/property/property.js +46 -0
  83. package/dist/core/property/refine.js +103 -0
  84. package/dist/core/record/abstract-record.d.ts +2 -1
  85. package/dist/core/record/abstract-record.js +307 -0
  86. package/dist/core/record/exceptions.js +49 -0
  87. package/dist/core/record/marks.js +7 -0
  88. package/dist/core/record/mask.js +60 -0
  89. package/dist/core/record/name.js +23 -0
  90. package/dist/core/record/plugin-catalog.js +115 -0
  91. package/dist/core/record/plugin.js +198 -0
  92. package/dist/core/record/record.js +117 -0
  93. package/dist/core/record/types/record.interface.d.ts +5 -0
  94. package/dist/core/repository/cursor.js +83 -0
  95. package/dist/core/repository/exceptions.js +72 -0
  96. package/dist/core/repository/in-memory.js +196 -0
  97. package/dist/core/repository/migrate.js +254 -0
  98. package/dist/core/repository/plan.js +27 -0
  99. package/dist/core/repository/provider.js +29 -0
  100. package/dist/core/repository/query.js +153 -0
  101. package/dist/core/repository/references.js +196 -0
  102. package/dist/core/repository/repository.d.ts +22 -2
  103. package/dist/core/repository/repository.js +720 -0
  104. package/dist/core/repository/state.js +138 -0
  105. package/dist/core/repository/types/query.interface.d.ts +13 -0
  106. package/dist/core/repository/types/repository.interface.d.ts +20 -8
  107. package/dist/core/repository/types/schema.interface.d.ts +8 -2
  108. package/dist/core/repository/upcast.js +179 -0
  109. package/dist/core/schema/schema.d.ts +9 -0
  110. package/dist/core/schema/schema.js +542 -0
  111. package/dist/core/standard/standard-schema.js +1 -0
  112. package/dist/core/standard/standard.js +23 -0
  113. package/dist/core/symbols/demo.d.ts +4 -0
  114. package/dist/core/symbols/demo.js +1 -0
  115. package/dist/core/symbols/kind.js +1 -0
  116. package/dist/core/symbols/meta.js +1 -0
  117. package/dist/core/symbols/state.d.ts +5 -0
  118. package/dist/core/symbols/state.js +1 -0
  119. package/dist/core/tracing/exceptions.d.ts +4 -0
  120. package/dist/core/tracing/exceptions.js +7 -0
  121. package/dist/core/tracing/tracing.d.ts +72 -0
  122. package/dist/core/tracing/tracing.js +101 -0
  123. package/dist/core/use-case/access.js +56 -0
  124. package/dist/core/use-case/decorators.js +65 -0
  125. package/dist/core/use-case/exceptions.d.ts +5 -1
  126. package/dist/core/use-case/exceptions.js +48 -0
  127. package/dist/core/use-case/plugin-catalog.js +68 -0
  128. package/dist/core/use-case/plugin.js +133 -0
  129. package/dist/core/use-case/route.js +200 -0
  130. package/dist/core/use-case/scope.d.ts +5 -0
  131. package/dist/core/use-case/scope.js +4 -0
  132. package/dist/core/use-case/steps/describe.d.ts +5 -1
  133. package/dist/core/use-case/steps/describe.js +24 -0
  134. package/dist/core/use-case/steps/references.js +105 -0
  135. package/dist/core/use-case/steps/run.js +139 -0
  136. package/dist/core/use-case/steps/types/check.interface.d.ts +27 -8
  137. package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
  138. package/dist/core/use-case/steps/validate.js +368 -0
  139. package/dist/core/use-case/target.js +100 -0
  140. package/dist/core/use-case/types/route.interface.d.ts +25 -5
  141. package/dist/core/use-case/use-case.d.ts +77 -2
  142. package/dist/core/use-case/use-case.js +493 -0
  143. package/dist/experimental.d.ts +5 -0
  144. package/dist/experimental.js +18 -161
  145. package/dist/index.d.ts +7 -5
  146. package/dist/index.js +213 -367
  147. package/dist/std/blueprint-plugin.js +42 -0
  148. package/dist/std/blueprint.d.ts +21 -0
  149. package/dist/std/blueprint.js +3 -0
  150. package/dist/std/command.js +3 -0
  151. package/dist/std/entity.d.ts +20 -1
  152. package/dist/std/entity.js +30 -0
  153. package/dist/std/event-policies.js +3 -0
  154. package/dist/std/properties/contact.d.ts +3 -0
  155. package/dist/std/properties/contact.js +29 -0
  156. package/dist/std/properties/enum.js +26 -0
  157. package/dist/std/properties/exceptions.js +14 -0
  158. package/dist/std/properties/headless.js +45 -0
  159. package/dist/std/properties/identity.js +28 -0
  160. package/dist/std/properties/json.js +161 -0
  161. package/dist/std/properties/password.js +79 -0
  162. package/dist/std/properties/primitives.d.ts +4 -0
  163. package/dist/std/properties/primitives.js +28 -0
  164. package/dist/std/properties/time.js +55 -0
  165. package/dist/std/record-plugins.js +73 -0
  166. package/dist/std/record.d.ts +20 -1
  167. package/dist/std/record.js +3 -0
  168. package/dist/std/resource.js +39 -0
  169. package/dist/std/runtime.js +193 -0
  170. package/dist/std/stages.js +4 -0
  171. package/dist/std/use-cases/auth.js +193 -0
  172. package/dist/std/use-cases/crud.js +261 -0
  173. package/dist/std/use-cases/index.js +12 -0
  174. package/dist/std/use-cases/lifecycle.js +97 -0
  175. package/dist/std/use-cases/shared.js +232 -0
  176. package/dist/std/use-cases/types.d.ts +5 -1
  177. package/dist/std/use-cases/types.js +0 -0
  178. package/dist/testing/http-provider-contract.js +344 -0
  179. package/dist/testing/index.js +3 -1435
  180. package/dist/testing/memory-publisher.js +12 -0
  181. package/dist/testing/provider-contract.d.ts +12 -3
  182. package/dist/testing/provider-contract.js +1071 -0
  183. package/package.json +17 -5
  184. package/dist/index-3aae5jfq.js +0 -2997
  185. package/dist/index-ny3as79r.js +0 -4567
  186. package/dist/index-xegv6a1p.js +0 -4291
@@ -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
+ }
@@ -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
+ }
@@ -0,0 +1,209 @@
1
+ import { Exception } from "../exception/exception.js";
2
+ import { LazyIssue, REJECTED } from "../parse/issue.js";
3
+ import {
4
+ checksOfSchema,
5
+ describeError,
6
+ errorsOfSchema
7
+ } from "../schema/schema.js";
8
+ import { State } from "../symbols/state.js";
9
+ import { PropertyStates } from "./enums/property-states.js";
10
+ import { InvalidPropertyValue, PropertyRequired } from "./exceptions.js";
11
+ const MISMATCH = Symbol("mismatch");
12
+
13
+ class Failure {
14
+ source;
15
+ raised;
16
+ errors;
17
+ cause;
18
+ constructor(source, raised, errors) {
19
+ this.source = source;
20
+ this.raised = raised;
21
+ this.errors = errors;
22
+ this.cause = raised;
23
+ }
24
+ exception() {
25
+ if (this.cause === undefined) {
26
+ const { source, errors } = this;
27
+ this.cause = errors ? new InvalidPropertyValue({
28
+ source,
29
+ details: { errors: errors.map(describeError) }
30
+ }) : new PropertyRequired({ source });
31
+ }
32
+ return this.cause;
33
+ }
34
+ }
35
+ let rejection;
36
+ function reject(failure) {
37
+ rejection = failure;
38
+ return REJECTED;
39
+ }
40
+ function segmentsOf(pointer) {
41
+ return pointer.slice(1).split("/").map((segment) => {
42
+ const key = segment.replaceAll("~1", "/").replaceAll("~0", "~");
43
+ return /^(0|[1-9]\d*)$/.test(key) ? Number(key) : key;
44
+ });
45
+ }
46
+ function issueOf(failure, message, path, code) {
47
+ return new LazyIssue(message, path, code, failure.source, failure);
48
+ }
49
+ export function takeRejectionIssues(path, issues) {
50
+ const failure = rejection;
51
+ rejection = undefined;
52
+ const { raised, errors, source } = failure;
53
+ if (raised) {
54
+ issues.push({
55
+ message: raised.message,
56
+ path,
57
+ code: "invalid_value",
58
+ source,
59
+ cause: raised
60
+ });
61
+ } else if (!errors) {
62
+ issues.push(issueOf(failure, "is required", path, "required"));
63
+ } else if (errors.length === 0) {
64
+ issues.push(issueOf(failure, "is invalid", path, "invalid_value"));
65
+ } else {
66
+ for (const { message, instancePath } of errors) {
67
+ issues.push(issueOf(failure, message, instancePath === "" ? path : [...path, ...segmentsOf(instancePath)], "invalid_value"));
68
+ }
69
+ }
70
+ }
71
+
72
+ export class AbstractDefineProperty {
73
+ [State] = PropertyStates.Declared;
74
+ make(value) {
75
+ const handled = this.attempt(value);
76
+ if (handled !== REJECTED)
77
+ return handled;
78
+ const failure = rejection;
79
+ rejection = undefined;
80
+ throw failure.exception();
81
+ }
82
+ parse(value) {
83
+ const handled = this.attemptWithoutThrowing(value);
84
+ if (handled !== REJECTED)
85
+ return { value: handled };
86
+ const issues = [];
87
+ takeRejectionIssues([], issues);
88
+ return { issues };
89
+ }
90
+ preHandle(value) {
91
+ return value;
92
+ }
93
+ afterHandle(value) {
94
+ return value;
95
+ }
96
+ handle(value) {
97
+ return this.type.schema.match(value) ? value : MISMATCH;
98
+ }
99
+ attemptWithoutThrowing(value) {
100
+ try {
101
+ return this.attempt(value);
102
+ } catch (error) {
103
+ return this.raise(error instanceof Exception ? error : this.invalidValue(error));
104
+ }
105
+ }
106
+ attempt(value) {
107
+ const { name, optional, schema } = this.type;
108
+ this[State] = PropertyStates.Declared;
109
+ if (value === undefined) {
110
+ if (!optional)
111
+ return reject(new Failure(name, undefined, undefined));
112
+ this[State] = PropertyStates.Done;
113
+ return value;
114
+ }
115
+ const preHandles = this.preHandle !== PASS.preHandle;
116
+ const handles = this.handle !== PASS.handle;
117
+ if ((preHandles || handles) && !schema.matchShape(value)) {
118
+ return this.mismatch(value);
119
+ }
120
+ try {
121
+ if (preHandles) {
122
+ this[State] = PropertyStates.PreHandling;
123
+ value = this.runHandler(this.preHandle, value);
124
+ }
125
+ this[State] = PropertyStates.Handling;
126
+ if (handles) {
127
+ const handled = this.handle(value);
128
+ if (handled === MISMATCH)
129
+ return this.mismatch(value);
130
+ value = handled;
131
+ } else if (!schema.match(value)) {
132
+ if (!preHandles && !schema.matchShape(value)) {
133
+ this[State] = PropertyStates.Declared;
134
+ }
135
+ return this.mismatch(value);
136
+ }
137
+ if (this.afterHandle !== PASS.afterHandle) {
138
+ this[State] = PropertyStates.AfterHandling;
139
+ value = this.runHandler(this.afterHandle, value);
140
+ }
141
+ } catch (error) {
142
+ if (error instanceof Exception)
143
+ return this.raise(error);
144
+ throw error;
145
+ }
146
+ this[State] = PropertyStates.Done;
147
+ return value;
148
+ }
149
+ runHandler(handler, value) {
150
+ try {
151
+ return handler.call(this, value);
152
+ } catch (error) {
153
+ if (error instanceof Exception)
154
+ throw error;
155
+ throw this.invalidValue(error);
156
+ }
157
+ }
158
+ invalidValue(error) {
159
+ return new InvalidPropertyValue({
160
+ source: this.type.name,
161
+ details: {
162
+ errors: [error instanceof Error ? error.message : String(error)]
163
+ },
164
+ cause: error
165
+ });
166
+ }
167
+ raise(exception) {
168
+ return reject(new Failure(this.type.name, exception, undefined));
169
+ }
170
+ mismatch(value) {
171
+ const { name, schema } = this.type;
172
+ return reject(new Failure(name, undefined, errorsOfSchema(schema, value)));
173
+ }
174
+ }
175
+ const PASS = AbstractDefineProperty.prototype;
176
+ const declared = new WeakSet;
177
+ export function declarePipeline(prototype) {
178
+ declared.add(prototype);
179
+ }
180
+ export function parserOf(definition) {
181
+ const pipeline = definition;
182
+ if (pipeline.parse !== AbstractDefineProperty.prototype.parse) {
183
+ return;
184
+ }
185
+ if (!declared.has(Object.getPrototypeOf(definition)) || pipeline.handle !== PASS.handle || Object.hasOwn(definition, "preHandle") || Object.hasOwn(definition, "afterHandle")) {
186
+ return (value) => pipeline.attemptWithoutThrowing(value);
187
+ }
188
+ const { name, optional, schema } = pipeline.type;
189
+ const { match, matchShape } = checksOfSchema(schema);
190
+ const preHandle = pipeline.preHandle === PASS.preHandle ? undefined : pipeline.preHandle;
191
+ const afterHandle = pipeline.afterHandle === PASS.afterHandle ? undefined : pipeline.afterHandle;
192
+ return (value) => {
193
+ if (value === undefined) {
194
+ return optional ? value : reject(new Failure(name, undefined, undefined));
195
+ }
196
+ try {
197
+ if (preHandle !== undefined) {
198
+ if (!matchShape(value))
199
+ return pipeline.mismatch(value);
200
+ value = preHandle.call(pipeline, value);
201
+ }
202
+ if (!match(value))
203
+ return pipeline.mismatch(value);
204
+ return afterHandle === undefined ? value : afterHandle.call(pipeline, value);
205
+ } catch (error) {
206
+ return pipeline.raise(error instanceof Exception ? error : pipeline.invalidValue(error));
207
+ }
208
+ };
209
+ }
@@ -0,0 +1,8 @@
1
+ export var PropertyStates;
2
+ ((PropertyStates) => {
3
+ PropertyStates[PropertyStates["Declared"] = 0] = "Declared";
4
+ PropertyStates[PropertyStates["PreHandling"] = 1] = "PreHandling";
5
+ PropertyStates[PropertyStates["Handling"] = 2] = "Handling";
6
+ PropertyStates[PropertyStates["AfterHandling"] = 3] = "AfterHandling";
7
+ PropertyStates[PropertyStates["Done"] = 4] = "Done";
8
+ })(PropertyStates ||= {});
@@ -0,0 +1,12 @@
1
+ import { ExceptionLayers } from "../exception/enums/exception-layers.js";
2
+ import { exceptionOf } from "../exception/exception.js";
3
+ export const PropertyRequired = exceptionOf({
4
+ layer: ExceptionLayers.Property,
5
+ name: "PropertyRequired",
6
+ message: (_, source) => `Property "${source}" is required.`
7
+ });
8
+ export const InvalidPropertyValue = exceptionOf({
9
+ layer: ExceptionLayers.Property,
10
+ name: "InvalidPropertyValue",
11
+ message: ({ errors }, source) => `Invalid value for property "${source}": ${errors.join("; ")}`
12
+ });
@@ -0,0 +1,10 @@
1
+ import { fieldOf } from "../blueprint/callable.js";
2
+ import { isProperty } from "./is-property.js";
3
+ export const PropertyInput = Symbol("propertyInput");
4
+ export function propertyInputOf(field) {
5
+ const property = fieldOf(field);
6
+ if (!isProperty(property))
7
+ return;
8
+ const declared = property[PropertyInput];
9
+ return typeof declared === "object" && declared !== null && isProperty(declared.accepts) && typeof declared.convert === "function" ? declared : undefined;
10
+ }
@@ -0,0 +1,3 @@
1
+ export function isProperty(value) {
2
+ return typeof value === "function" && typeof value.type === "object";
3
+ }
@@ -0,0 +1,8 @@
1
+ const lineages = new WeakMap;
2
+ export function recordPropertyLineage(Property, lineage) {
3
+ if (!lineages.has(Property))
4
+ lineages.set(Property, lineage);
5
+ }
6
+ export function propertyLineageOf(Property) {
7
+ return typeof Property === "function" ? lineages.get(Property) : undefined;
8
+ }
@@ -53,5 +53,32 @@ export interface PropertyClass<SchemaType extends TSchema, Value = Static<Schema
53
53
  with<Self extends AnyPropertyConstructor>(this: Self, options: PropertyOptions<SchemaType, Value>): Self;
54
54
  demo<Self extends AbstractProperty<any>>(this: new (value: never) => Self): Self;
55
55
  }
56
+ /**
57
+ * Declares a property: a class that holds one value, run through
58
+ * `preHandle`, the validation against the TypeBox `schema` and
59
+ * `afterHandle`. `optional`, `sensitive` and `unique` flag it for the
60
+ * blueprints and records that hold it, and `default` is its `demo()` value.
61
+ * Extend the class to add behavior, or narrow it with `.with(options)`. A
62
+ * value its schema rejects makes the constructor throw
63
+ * {@link InvalidPropertyValue}, and `undefined` for a required one
64
+ * {@link PropertyRequired}.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * import { propertyOf } from "@afora/core";
69
+ * import { Type } from "typebox";
70
+ *
71
+ * const Name = propertyOf({
72
+ * name: "name",
73
+ * schema: Type.String({ minLength: 3 }),
74
+ * preHandle: (value) => value.trim(),
75
+ * default: "Jane", // used by Name.demo()
76
+ * });
77
+ *
78
+ * new Name(" Alice ").value; // "Alice"
79
+ * Name.demo().value; // "Jane"
80
+ * // new Name("a") throws InvalidPropertyValue
81
+ * ```
82
+ */
56
83
  export declare function propertyOf<SchemaType extends TSchema, Optional extends boolean = false, const Unique extends boolean = false, const Sensitive extends boolean = false>({ name, schema, optional, sensitive, unique, default: _default, preHandle: onPreHandle, afterHandle: onAfterHandle, }: PropertyOfArgs<SchemaType, Optional, Unique, Sensitive>): PropertyClass<SchemaValueType<SchemaType, Optional>, Static<SchemaType>> & UniqueMark<Unique> & SensitiveMark<Sensitive>;
57
84
  export {};