@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
@@ -0,0 +1,307 @@
1
+ import { isBlueprint } from "../blueprint/is-blueprint.js";
2
+ import { isBlueprintList } from "../blueprint/list.js";
3
+ import {
4
+ isBlueprintVariant,
5
+ variantNameOf,
6
+ variantOfValue
7
+ } from "../blueprint/variant.js";
8
+ import { ExceptionLayers } from "../exception/enums/exception-layers.js";
9
+ import { Exception } from "../exception/exception.js";
10
+ import { collectionParserOf, parseField } from "../parse/parse.js";
11
+ import { standardOfClass } from "../standard/standard.js";
12
+ import { Meta } from "../symbols/meta.js";
13
+ import {
14
+ InvalidRecordValue,
15
+ ReservedRecordKey,
16
+ UndeclaredRecordProperty
17
+ } from "./exceptions.js";
18
+ import { maskCollection } from "./mask.js";
19
+ import { recordNameOf } from "./name.js";
20
+ const values = new WeakMap;
21
+ const metadata = new WeakMap;
22
+ export const RESERVED_RECORD_KEYS = Object.freeze([
23
+ "onSet",
24
+ "onSetAll",
25
+ "set",
26
+ "toJSON",
27
+ "toString",
28
+ "toLocaleString",
29
+ "valueOf",
30
+ "hasOwnProperty",
31
+ "propertyIsEnumerable",
32
+ "isPrototypeOf",
33
+ "__defineGetter__",
34
+ "__defineSetter__",
35
+ "__lookupGetter__",
36
+ "__lookupSetter__",
37
+ "__proto__",
38
+ "constructor"
39
+ ]);
40
+ export function exposeRecordKeys(prototype, keys) {
41
+ for (const key of keys) {
42
+ Object.defineProperty(prototype, key, {
43
+ get() {
44
+ return values.get(this)?.[key];
45
+ },
46
+ enumerable: true,
47
+ configurable: true
48
+ });
49
+ }
50
+ }
51
+ const checked = new WeakSet;
52
+ function checkMembers(record, properties) {
53
+ const Class = record.constructor;
54
+ if (checked.has(Class))
55
+ return;
56
+ const keys = Object.keys(properties);
57
+ for (let prototype = Object.getPrototypeOf(record);prototype !== null && !Object.hasOwn(prototype.constructor, "blueprint"); prototype = Object.getPrototypeOf(prototype)) {
58
+ const key = Object.getOwnPropertyNames(prototype).find((name) => keys.includes(name));
59
+ if (key !== undefined) {
60
+ throw new ReservedRecordKey({
61
+ source: recordNameOf(Class),
62
+ details: { key }
63
+ });
64
+ }
65
+ }
66
+ checked.add(Class);
67
+ }
68
+ let vouched;
69
+ let vouchedComplete;
70
+ export function vouchFor(keys, complete) {
71
+ vouched = keys;
72
+ vouchedComplete = complete;
73
+ }
74
+ const constructions = new WeakMap;
75
+ function constructionOf(record) {
76
+ const Class = record.constructor;
77
+ let construction = constructions.get(Class);
78
+ if (!construction) {
79
+ const { properties } = record[Meta].blueprint;
80
+ checkMembers(record, properties);
81
+ const source = recordNameOf(Class);
82
+ construction = { source, parse: collectionParserOf(properties, source) };
83
+ constructions.set(Class, construction);
84
+ }
85
+ return construction;
86
+ }
87
+ function withIssues(details, issues) {
88
+ return Object.defineProperty(details, "issues", {
89
+ value: Object.freeze([...issues]),
90
+ enumerable: false
91
+ });
92
+ }
93
+ function recordMessageOf(cause, key, source) {
94
+ if (key === "" || cause.layer !== ExceptionLayers.Property) {
95
+ return cause.message;
96
+ }
97
+ if (cause.name === "PropertyRequired") {
98
+ return `Property "${key}" of record "${source}" is required.`;
99
+ }
100
+ const errors = cause.details?.errors;
101
+ if (cause.name === "InvalidPropertyValue" && Array.isArray(errors)) {
102
+ return `Invalid value for "${key}" of record "${source}": ${errors.join("; ")}`;
103
+ }
104
+ return cause.message;
105
+ }
106
+ function exceptionOfIssues(issues, source) {
107
+ const [first] = issues;
108
+ const key = first.path.join(".");
109
+ if (first.cause) {
110
+ return first.cause.propagate({
111
+ layer: ExceptionLayers.Record,
112
+ source,
113
+ message: recordMessageOf(first.cause, key, source),
114
+ details: withIssues({ key }, issues)
115
+ });
116
+ }
117
+ if (first.code === "undeclared_key") {
118
+ return new UndeclaredRecordProperty({
119
+ source,
120
+ details: withIssues({ key }, issues)
121
+ });
122
+ }
123
+ return new InvalidRecordValue({
124
+ source,
125
+ details: withIssues({ key, errors: [first.message] }, issues)
126
+ });
127
+ }
128
+ function issuesOfError(error, source) {
129
+ const issues = error?.details?.issues;
130
+ if (error instanceof Exception && issues)
131
+ return issues;
132
+ const cause = Exception.from(error, {
133
+ layer: ExceptionLayers.Record,
134
+ source
135
+ });
136
+ return [
137
+ {
138
+ code: "invalid_value",
139
+ message: cause.message,
140
+ path: [],
141
+ source,
142
+ cause
143
+ }
144
+ ];
145
+ }
146
+ let request;
147
+ function exceptionOfRejection(rejection) {
148
+ rejection.exception ??= exceptionOfIssues(rejection.issues, rejection.source);
149
+ return rejection.exception;
150
+ }
151
+ const REJECTION_HANDLER = {
152
+ get: (rejection, key) => Reflect.get(exceptionOfRejection(rejection), key),
153
+ set: (rejection, key, value) => Reflect.set(exceptionOfRejection(rejection), key, value),
154
+ has: (rejection, key) => Reflect.has(exceptionOfRejection(rejection), key),
155
+ getPrototypeOf: (rejection) => Reflect.getPrototypeOf(exceptionOfRejection(rejection)),
156
+ ownKeys: (rejection) => Reflect.ownKeys(exceptionOfRejection(rejection)),
157
+ getOwnPropertyDescriptor: (rejection, key) => {
158
+ const descriptor = Reflect.getOwnPropertyDescriptor(exceptionOfRejection(rejection), key);
159
+ return descriptor && { ...descriptor, configurable: true };
160
+ }
161
+ };
162
+ function parseRecord(Class, value) {
163
+ if (value instanceof Class)
164
+ return { value };
165
+ const outer = request;
166
+ const own = { Class };
167
+ request = own;
168
+ try {
169
+ return { value: new Class(value) };
170
+ } catch (error) {
171
+ if (own.issues !== undefined && own.rejection === error) {
172
+ return { issues: own.issues };
173
+ }
174
+ return { issues: issuesOfError(error, recordNameOf(Class)) };
175
+ } finally {
176
+ request = outer;
177
+ }
178
+ }
179
+ function recordValue(record) {
180
+ return values.get(record);
181
+ }
182
+ export function recordDataOf(record, options) {
183
+ const { properties } = record[Meta].blueprint;
184
+ return dataOfCollection(properties, values.get(record), options);
185
+ }
186
+ function dataOfCollection(properties, value, options) {
187
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [
188
+ key,
189
+ recordFieldDataOf(properties[key], item, options)
190
+ ])));
191
+ }
192
+ export function recordFieldDataOf(field, value, options) {
193
+ if (value === undefined || value === null)
194
+ return value;
195
+ if (isBlueprintVariant(field) && value instanceof AbstractRecord) {
196
+ const name = variantNameOf(field, value);
197
+ if (name !== undefined) {
198
+ const reference = options?.reference?.(value);
199
+ return Object.freeze(reference === undefined ? { [field.key]: name, ...recordDataOf(value, options) } : { [field.key]: name, id: reference });
200
+ }
201
+ }
202
+ if (value instanceof AbstractRecord) {
203
+ return options?.reference?.(value) ?? recordDataOf(value, options);
204
+ }
205
+ if (isBlueprintList(field) && Array.isArray(value)) {
206
+ return Object.freeze(value.map((item) => recordFieldDataOf(field.item, item, options)));
207
+ }
208
+ if (isBlueprint(field) && typeof value === "object") {
209
+ return dataOfCollection(field.properties, value, options);
210
+ }
211
+ if (isBlueprintVariant(field)) {
212
+ const variant = variantOfValue(field, value);
213
+ return variant ? dataOfCollection(variant.properties, value, options) : value;
214
+ }
215
+ return value;
216
+ }
217
+ export function storeRecordValue(record, key, value) {
218
+ const source = recordNameOf(record.constructor);
219
+ const { properties } = record[Meta].blueprint;
220
+ if (!Object.hasOwn(properties, key)) {
221
+ throw exceptionOfIssues([
222
+ {
223
+ code: "undeclared_key",
224
+ message: "is not declared",
225
+ path: [key],
226
+ source
227
+ }
228
+ ], source);
229
+ }
230
+ const current = values.get(record);
231
+ const previous = current[key];
232
+ const result = parseField(properties[key], value, [key], source);
233
+ if (result.issues)
234
+ throw exceptionOfIssues(result.issues, source);
235
+ const next = result.value;
236
+ const { [key]: _, ...rest } = current;
237
+ values.set(record, Object.freeze(next === undefined ? rest : { ...rest, [key]: next }));
238
+ return { next, previous };
239
+ }
240
+ export const RecordPluginHooks = Symbol("recordPluginHooks");
241
+ export function runRecordEffects(record, key, next, previous) {
242
+ const pluginHooks = record.constructor[RecordPluginHooks];
243
+ if (pluginHooks !== undefined && pluginHooks.length > 0) {
244
+ const name = String(key);
245
+ for (const hook of pluginHooks) {
246
+ if (!hook.keys.includes(name))
247
+ continue;
248
+ hook.onSetAll?.call(record, { key, value: next, previous });
249
+ hook.onSet?.call(record)[name]?.call(record, next, previous);
250
+ }
251
+ }
252
+ const hooks = record;
253
+ hooks.onSetAll({ key, value: next, previous });
254
+ hooks.onSet()[key]?.(next, previous);
255
+ return record;
256
+ }
257
+
258
+ export class AbstractRecord {
259
+ get [Meta]() {
260
+ const Class = this.constructor;
261
+ let declaration = metadata.get(Class);
262
+ if (!declaration) {
263
+ const { blueprint, options } = Class;
264
+ declaration = Object.freeze({ blueprint, options });
265
+ metadata.set(Class, declaration);
266
+ }
267
+ return declaration;
268
+ }
269
+ constructor(value) {
270
+ let asked;
271
+ if (request !== undefined && request.Class === this.constructor) {
272
+ asked = request;
273
+ request = undefined;
274
+ }
275
+ const made = vouched;
276
+ const complete = vouchedComplete;
277
+ vouched = undefined;
278
+ vouchedComplete = undefined;
279
+ const { source, parse } = constructionOf(this);
280
+ const result = parse(value, made, complete);
281
+ if (result.issues) {
282
+ if (asked === undefined)
283
+ throw exceptionOfIssues(result.issues, source);
284
+ asked.issues = result.issues;
285
+ asked.rejection = new Proxy({ issues: asked.issues, source }, REJECTION_HANDLER);
286
+ throw asked.rejection;
287
+ }
288
+ values.set(this, result.value);
289
+ }
290
+ static safeParse(value) {
291
+ return parseRecord(this, value);
292
+ }
293
+ static get "~standard"() {
294
+ return standardOfClass(this);
295
+ }
296
+ toJSON() {
297
+ return maskCollection(this[Meta].blueprint.properties, recordValue(this), this[Meta].options.sensitive);
298
+ }
299
+ set(key, value) {
300
+ const { next, previous } = storeRecordValue(this, String(key), value);
301
+ return runRecordEffects(this, key, next, previous);
302
+ }
303
+ onSetAll(_change) {}
304
+ onSet() {
305
+ return {};
306
+ }
307
+ }
@@ -0,0 +1,49 @@
1
+ import { ExceptionLayers } from "../exception/enums/exception-layers.js";
2
+ import { exceptionOf } from "../exception/exception.js";
3
+ export const InvalidRecordValue = exceptionOf({
4
+ layer: ExceptionLayers.Record,
5
+ name: "InvalidRecordValue",
6
+ message: ({
7
+ key,
8
+ errors
9
+ }, source) => `Invalid value for ${key ? `"${key}" of ` : ""}record "${source}": ${errors.join("; ")}`
10
+ });
11
+ export const InvalidRecordDeclaration = exceptionOf({
12
+ layer: ExceptionLayers.Record,
13
+ name: "InvalidRecordDeclaration",
14
+ message: (reason, source) => `Invalid declaration of record "${source}": ${reason}`
15
+ });
16
+ export const UndeclaredRecordProperty = exceptionOf({
17
+ layer: ExceptionLayers.Record,
18
+ name: "UndeclaredRecordProperty",
19
+ message: ({ key }, source) => `Property "${key}" is not declared in record "${source}".`
20
+ });
21
+ export const ReservedRecordKey = exceptionOf({
22
+ layer: ExceptionLayers.Record,
23
+ name: "ReservedRecordKey",
24
+ message: ({ key }, source) => `"${key}" is a member of record "${source}" and cannot be a key of its blueprint.`
25
+ });
26
+ export const InvalidRecordPlugin = exceptionOf({
27
+ layer: ExceptionLayers.Record,
28
+ name: "InvalidRecordPlugin",
29
+ message: ({ reason }, source) => `Invalid record plugin "${source}": ${reason}.`
30
+ });
31
+ export const RecordPluginConflict = exceptionOf({
32
+ layer: ExceptionLayers.Record,
33
+ name: "RecordPluginConflict",
34
+ message: ({
35
+ plugin,
36
+ key,
37
+ owner
38
+ }, source) => `Plugin "${plugin}" of record "${source}" adds "${key}", which ${owner} already has.`
39
+ });
40
+ export const UnknownRecordPlugin = exceptionOf({
41
+ layer: ExceptionLayers.Record,
42
+ name: "UnknownRecordPlugin",
43
+ message: ({ name }) => `No record plugin named "${name}" in the catalog.`
44
+ });
45
+ export const IncompatibleRecordPlugin = exceptionOf({
46
+ layer: ExceptionLayers.Record,
47
+ name: "IncompatibleRecordPlugin",
48
+ message: ({ reason }, source) => `Record plugin "${source}" does not satisfy the reference: ${reason}.`
49
+ });
@@ -0,0 +1,7 @@
1
+ import { isBlueprintList } from "../blueprint/list.js";
2
+ import { isProperty } from "../property/is-property.js";
3
+ export function isMarked(field, mark, { lists = false } = {}) {
4
+ if (lists && isBlueprintList(field))
5
+ return isMarked(field.item, mark, { lists });
6
+ return isProperty(field) && field.type[mark] === true;
7
+ }
@@ -0,0 +1,60 @@
1
+ import { isBlueprint } from "../blueprint/is-blueprint.js";
2
+ import { isBlueprintList } from "../blueprint/list.js";
3
+ import { isRecordClass, recordFieldOf } from "../blueprint/record-field.js";
4
+ import {
5
+ isBlueprintVariant,
6
+ variantNameOf,
7
+ variantOfValue
8
+ } from "../blueprint/variant.js";
9
+ import { isMarked } from "./marks.js";
10
+ export const MASK = "********";
11
+ function maskAll(value) {
12
+ if (value === undefined || value === null)
13
+ return value;
14
+ if (Array.isArray(value))
15
+ return Object.freeze(value.map(maskAll));
16
+ if (typeof value === "object" && isRecordClass(value.constructor)) {
17
+ return maskAll(value.toJSON());
18
+ }
19
+ if (typeof value === "object") {
20
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, maskAll(item)])));
21
+ }
22
+ return MASK;
23
+ }
24
+ function maskField(field, value, sensitive) {
25
+ if (value === undefined)
26
+ return value;
27
+ if (isBlueprintVariant(field)) {
28
+ const name = variantNameOf(field, value);
29
+ if (name !== undefined) {
30
+ const json = Object.freeze({
31
+ [field.key]: name,
32
+ ...value.toJSON()
33
+ });
34
+ return sensitive ? maskAll(json) : json;
35
+ }
36
+ }
37
+ if (recordFieldOf(field)) {
38
+ const json = value.toJSON();
39
+ return sensitive ? maskAll(json) : json;
40
+ }
41
+ if (sensitive || isMarked(field, "sensitive"))
42
+ return maskAll(value);
43
+ if (isBlueprintList(field) && Array.isArray(value)) {
44
+ return Object.freeze(value.map((item) => maskField(field.item, item, false)));
45
+ }
46
+ if (isBlueprint(field) && typeof value === "object" && value !== null) {
47
+ return maskCollection(field.properties, value);
48
+ }
49
+ if (isBlueprintVariant(field)) {
50
+ const variant = variantOfValue(field, value);
51
+ return variant ? maskCollection(variant.properties, value) : value;
52
+ }
53
+ return value;
54
+ }
55
+ export function maskCollection(properties, value, sensitive = []) {
56
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [
57
+ key,
58
+ maskField(properties[key], item, sensitive.includes(key))
59
+ ])));
60
+ }
@@ -0,0 +1,23 @@
1
+ const RECORD_NAME = /^[A-Za-z_$][\w$]*$/;
2
+ export function isRecordName(name) {
3
+ return typeof name === "string" && RECORD_NAME.test(name);
4
+ }
5
+ export function recordNameOf(Class) {
6
+ const { recordName, name } = Class;
7
+ return typeof recordName === "string" ? recordName : String(name);
8
+ }
9
+ const declaredNames = new WeakMap;
10
+ export function declareRecordName(Class, name) {
11
+ declaredNames.set(Class, name);
12
+ }
13
+ export function declaredRecordNameOf(Class) {
14
+ for (let current = Class;typeof current === "function"; current = Object.getPrototypeOf(current)) {
15
+ const own = Object.getOwnPropertyDescriptor(current, "recordName")?.value;
16
+ if (typeof own === "string")
17
+ return own;
18
+ const declared = declaredNames.get(current);
19
+ if (declared !== undefined)
20
+ return declared;
21
+ }
22
+ return;
23
+ }
@@ -0,0 +1,115 @@
1
+ import { Kind } from "../symbols/kind.js";
2
+ import {
3
+ IncompatibleRecordPlugin,
4
+ InvalidRecordPlugin,
5
+ UnknownRecordPlugin
6
+ } from "./exceptions.js";
7
+ import { isRecordPlugin, isVersion, satisfies } from "./plugin.js";
8
+ const LISTS = ["keys", "sensitive", "unique", "change", "members"];
9
+ function isDescriptor(value) {
10
+ if (typeof value !== "object" || value === null)
11
+ return false;
12
+ const descriptor = value;
13
+ const isList = (list) => Array.isArray(list) && list.every((item) => typeof item === "string");
14
+ return descriptor.kind === "record-plugin" && typeof descriptor.name === "string" && isVersion(descriptor.version) && LISTS.every((list) => isList(descriptor[list])) && Array.isArray(descriptor.requires) && descriptor.requires.every((required) => typeof required === "object" && required !== null && typeof required.name === "string" && isVersion(required.version));
15
+ }
16
+ function mismatchOf(plugin, descriptor) {
17
+ if (!satisfies(plugin.version, descriptor.version)) {
18
+ return `version ${plugin.version} does not satisfy ${descriptor.version}`;
19
+ }
20
+ const current = {
21
+ keys: plugin.keys,
22
+ sensitive: plugin.options.sensitive,
23
+ unique: plugin.options.unique,
24
+ change: plugin.options.change,
25
+ members: plugin.members
26
+ };
27
+ for (const list of LISTS) {
28
+ const missing = descriptor[list].find((item) => !current[list].includes(item));
29
+ if (missing !== undefined)
30
+ return `"${missing}" is not in its ${list}`;
31
+ }
32
+ for (const required of descriptor.requires) {
33
+ const found = plugin.requires.find(({ name }) => name === required.name);
34
+ if (!found)
35
+ return `it does not require "${required.name}"`;
36
+ if (!satisfies(found.version, required.version)) {
37
+ return `its "${required.name}" ${found.version} does not satisfy ${required.version}`;
38
+ }
39
+ }
40
+ return;
41
+ }
42
+ export function recordPluginCatalogOf(...plugins) {
43
+ const byName = {};
44
+ for (const plugin of plugins) {
45
+ if (!isRecordPlugin(plugin)) {
46
+ throw new InvalidRecordPlugin({
47
+ source: "catalog",
48
+ details: {
49
+ reason: "a catalog takes plugins made by `recordPluginOf`"
50
+ }
51
+ });
52
+ }
53
+ if (Object.hasOwn(byName, plugin.name)) {
54
+ throw new InvalidRecordPlugin({
55
+ source: plugin.name,
56
+ details: { reason: "the catalog has another plugin with its name" }
57
+ });
58
+ }
59
+ byName[plugin.name] = plugin;
60
+ }
61
+ const find = (name) => {
62
+ const plugin = Object.hasOwn(byName, name) ? byName[name] : undefined;
63
+ if (!plugin) {
64
+ throw new UnknownRecordPlugin({ source: "catalog", details: { name } });
65
+ }
66
+ return plugin;
67
+ };
68
+ const resolve = (reference) => {
69
+ if (typeof reference === "string") {
70
+ const at = reference.lastIndexOf("@");
71
+ if (at <= 0)
72
+ return find(reference);
73
+ const plugin = find(reference.slice(0, at));
74
+ const version = reference.slice(at + 1);
75
+ if (!isVersion(version)) {
76
+ throw new IncompatibleRecordPlugin({
77
+ source: plugin.name,
78
+ details: { reason: `"${version}" is not a semantic version` }
79
+ });
80
+ }
81
+ if (!satisfies(plugin.version, version)) {
82
+ throw new IncompatibleRecordPlugin({
83
+ source: plugin.name,
84
+ details: {
85
+ reason: `version ${plugin.version} does not satisfy ${version}`
86
+ }
87
+ });
88
+ }
89
+ return plugin;
90
+ }
91
+ if (!isDescriptor(reference)) {
92
+ const name = reference?.name;
93
+ throw new IncompatibleRecordPlugin({
94
+ source: typeof name === "string" ? name : "unknown",
95
+ details: { reason: "it is not a record plugin descriptor" }
96
+ });
97
+ }
98
+ const plugin = find(reference.name);
99
+ const reason = mismatchOf(plugin, reference);
100
+ if (reason !== undefined) {
101
+ throw new IncompatibleRecordPlugin({
102
+ source: plugin.name,
103
+ details: { reason }
104
+ });
105
+ }
106
+ return plugin;
107
+ };
108
+ return Object.freeze({
109
+ [Kind]: "record-plugin-catalog",
110
+ plugins: Object.freeze(byName),
111
+ list: Object.freeze([...plugins]),
112
+ resolve,
113
+ resolveAll: (references) => Object.freeze(references.map(resolve))
114
+ });
115
+ }