@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,127 @@
1
+ import { AbstractRecord } from "../record/abstract-record.js";
2
+ import { recordNameOf } from "../record/name.js";
3
+ import { declareRecord } from "../record/record.js";
4
+ import { blueprintFromProperties } from "./blueprint.js";
5
+ import {
6
+ InvalidBlueprintVariant,
7
+ UndeclaredBlueprintProperty
8
+ } from "./exceptions.js";
9
+ import { isBlueprint } from "./is-blueprint.js";
10
+ import { isBlueprintList, listOf } from "./list.js";
11
+ import { optionalOf } from "./optional.js";
12
+ import { isRecordClass, recordFieldOf } from "./record-field.js";
13
+ import { isBlueprintVariant } from "./variant.js";
14
+ function propertiesOfField(field) {
15
+ if (field === undefined)
16
+ return;
17
+ if (isBlueprintList(field))
18
+ return propertiesOfField(field.item);
19
+ const record = recordFieldOf(field);
20
+ if (record)
21
+ return record.record.blueprint.properties;
22
+ return isBlueprint(field) ? field.properties : undefined;
23
+ }
24
+ function maskOf(selector, against) {
25
+ if (isRecordClass(selector))
26
+ return maskOf(selector.blueprint, against);
27
+ if (isBlueprint(selector)) {
28
+ return Object.fromEntries(Object.entries(selector.properties).map(([key, field]) => [key, fieldMaskOf(field, against?.[key])]));
29
+ }
30
+ return selector;
31
+ }
32
+ function fieldMaskOf(field, other) {
33
+ if (isBlueprintList(field)) {
34
+ return fieldMaskOf(field.item, isBlueprintList(other) ? other.item : other);
35
+ }
36
+ const record = recordFieldOf(field);
37
+ if (record) {
38
+ if (record.record === recordFieldOf(other)?.record)
39
+ return true;
40
+ return maskOf(record.record, propertiesOfField(other));
41
+ }
42
+ if (isBlueprint(field)) {
43
+ return field === other ? true : maskOf(field, propertiesOfField(other));
44
+ }
45
+ return true;
46
+ }
47
+ function sliceCollection(properties, mask, path) {
48
+ const sliced = {};
49
+ for (const [key, selected] of Object.entries(mask)) {
50
+ if (!selected)
51
+ continue;
52
+ if (!Object.hasOwn(properties, key)) {
53
+ throw new UndeclaredBlueprintProperty({
54
+ source: "blueprint",
55
+ details: { key: path + key }
56
+ });
57
+ }
58
+ sliced[key] = sliceField(properties[key], selected, path + key);
59
+ }
60
+ return sliced;
61
+ }
62
+ function slicedRecordOf(Original, mask, path) {
63
+ const source = Original.blueprint;
64
+ const blueprint = blueprintFromProperties(source.plugins, sliceCollection(source.properties, mask, `${path}.`));
65
+ const kept = (keys) => keys.filter((key) => Object.hasOwn(blueprint.properties, key));
66
+ const { sensitive, unique, change } = Original.options;
67
+ const Sliced = declareRecord(AbstractRecord, blueprint, {
68
+ sensitive: kept(sensitive),
69
+ unique: kept(unique),
70
+ change: kept(change)
71
+ }, recordNameOf(Original));
72
+ Object.defineProperty(Sliced, Symbol.hasInstance, {
73
+ value(value) {
74
+ return Function.prototype[Symbol.hasInstance].call(this, value) || value instanceof Original;
75
+ }
76
+ });
77
+ return Sliced;
78
+ }
79
+ function sliceField(field, selected, path) {
80
+ if (selected === true)
81
+ return field;
82
+ if (isBlueprintList(field)) {
83
+ const list = listOf(sliceField(field.item, selected, path), {
84
+ minItems: field.minItems,
85
+ maxItems: field.maxItems
86
+ });
87
+ return field.optional ? optionalOf(list) : list;
88
+ }
89
+ const record = recordFieldOf(field);
90
+ if (record) {
91
+ const sliced = slicedRecordOf(record.record, selected, path);
92
+ return record.optional ? optionalOf(sliced) : sliced;
93
+ }
94
+ if (isBlueprint(field)) {
95
+ const sliced = blueprintFromProperties(field.plugins, sliceCollection(field.properties, selected, `${path}.`));
96
+ return field.optional ? optionalOf(sliced) : sliced;
97
+ }
98
+ if (isBlueprintVariant(field)) {
99
+ throw new InvalidBlueprintVariant({
100
+ source: "blueprint",
101
+ details: {
102
+ key: field.key,
103
+ reason: `"${path}" cannot be sliced by its keys yet, only picked whole`
104
+ }
105
+ });
106
+ }
107
+ throw new UndeclaredBlueprintProperty({
108
+ source: "blueprint",
109
+ details: { key: `${path}.${Object.keys(selected)[0] ?? ""}` }
110
+ });
111
+ }
112
+ export const sliceOf = (blueprint, selector) => blueprintFromProperties(blueprint.plugins, sliceCollection(blueprint.properties, maskOf(selector, blueprint.properties), ""));
113
+ export const sliceValue = (value, selector) => pick(value, maskOf(selector));
114
+ function pick(value, mask) {
115
+ if (Array.isArray(value))
116
+ return value.map((item) => pick(item, mask));
117
+ if (typeof value !== "object" || value === null)
118
+ return value;
119
+ const source = isRecordClass(value.constructor) ? value.toJSON() : value;
120
+ const picked = {};
121
+ for (const [key, selected] of Object.entries(mask)) {
122
+ if (!selected || !Object.hasOwn(source, key))
123
+ continue;
124
+ picked[key] = selected === true ? source[key] : pick(source[key], selected);
125
+ }
126
+ return picked;
127
+ }
@@ -10,9 +10,22 @@ import type { StandardSchemaV1 } from "../../standard/standard-schema.js";
10
10
  import type { Kind } from "../../symbols/kind.js";
11
11
  import type { listOf } from "../list.js";
12
12
  import type { optionalOf } from "../optional.js";
13
+ import type { variantOf } from "../variant.js";
13
14
  import type { IBlueprintFactories, IBlueprintPlugin } from "./blueprint-plugin.interface.js";
14
15
  /** Any blueprint, whatever its properties, optional or not. */
15
16
  export type AnyBlueprint = IBlueprint<any, boolean>;
17
+ /**
18
+ * Any variant field, whatever its key and its variants, optional or not.
19
+ * Written out, not as `IBlueprintVariant` of anything: every field of every
20
+ * blueprint is compared with it, and this way nothing is worked out for that.
21
+ */
22
+ export interface AnyBlueprintVariant {
23
+ readonly [Kind]: "variant";
24
+ readonly key: string;
25
+ readonly variants: any;
26
+ readonly optional: boolean;
27
+ readonly "~standard": StandardSchemaV1.Props<any, any>;
28
+ }
16
29
  /** Any list, whatever its item, optional or not. */
17
30
  export type AnyBlueprintList = IBlueprintList<any, boolean>;
18
31
  /**
@@ -40,9 +53,10 @@ export interface OptionalRecord<Record extends AnyRecordClass> {
40
53
  export type AnyOptionalRecord = OptionalRecord<AnyRecordClass>;
41
54
  /**
42
55
  * What a blueprint key holds: a property, a blueprint for a nested object, a
43
- * record, or a list of any of them.
56
+ * record, a list of any of them, or one of several blueprints told apart by a
57
+ * key.
44
58
  */
45
- export type BlueprintField = AnyPropertyConstructor | AnyBlueprint | AnyBlueprintList | AnyRecordClass | AnyOptionalRecord;
59
+ export type BlueprintField = AnyPropertyConstructor | AnyBlueprint | AnyBlueprintList | AnyRecordClass | AnyOptionalRecord | AnyBlueprintVariant;
46
60
  /** Fields by key, e.g. `{ name: Name, address: Address }`. */
47
61
  export type IBlueprintCollection = Record<string, BlueprintField>;
48
62
  /** What a blueprint picks its fields from: a plugin, or another blueprint. */
@@ -66,7 +80,17 @@ type FieldSchema<Field, Mode extends FieldMode> = 0 extends 1 & Field ? TSchema
66
80
  } ? Optional extends true ? TOptional<CollectionSchema<Properties, Mode>> : CollectionSchema<Properties, Mode> : Field extends {
67
81
  readonly item: infer Item;
68
82
  readonly optional: infer Optional;
69
- } ? Optional extends true ? TOptional<TArray<FieldSchema<Item, Mode>>> : TArray<FieldSchema<Item, Mode>> : never;
83
+ } ? Optional extends true ? TOptional<TArray<FieldSchema<Item, Mode>>> : TArray<FieldSchema<Item, Mode>> : VariantSchema<Field, Mode>;
84
+ /**
85
+ * The schema of a variant field: its value as it is, since a union of objects
86
+ * is only read for whether the key is optional. Apart from `FieldSchema`, so
87
+ * that a field that is not a variant costs one comparison there.
88
+ */
89
+ type VariantSchema<Field, Mode extends FieldMode> = Field extends {
90
+ readonly key: infer Key extends string;
91
+ readonly variants: infer Variants;
92
+ readonly optional: infer Optional;
93
+ } ? Optional extends true ? TOptional<TUnsafe<VariantValue<Key, Variants, Mode>>> : TUnsafe<VariantValue<Key, Variants, Mode>> : never;
70
94
  /** Declares the records a field of a record class takes besides its own. */
71
95
  export declare const RecordAccepts: unique symbol;
72
96
  /** The values besides its instances and plain input that a field of `Record` takes. */
@@ -102,7 +126,31 @@ type FieldValue<Field, Mode extends FieldMode> = 0 extends 1 & Field ? Static<TS
102
126
  readonly properties: infer Properties extends IBlueprintCollection;
103
127
  } ? CollectionValue<Properties, Mode> : Field extends {
104
128
  readonly item: infer Item;
105
- } ? FieldValue<Item, Mode>[] : never;
129
+ } ? FieldValue<Item, Mode>[] : Field extends {
130
+ readonly key: infer Key extends string;
131
+ readonly variants: infer Variants;
132
+ } ? VariantValue<Key, Variants, Mode> : never;
133
+ /**
134
+ * The value of a variant field: for each variant, its own value with `Key`
135
+ * set to its name, so that `value[Key]` tells which one it is. Anything for a
136
+ * field of any variants (as `AnyBlueprintVariant`'s).
137
+ */
138
+ type VariantValue<Key extends string, Variants, Mode extends FieldMode> = 0 extends 1 & Variants ? any : {
139
+ [Tag in keyof Variants & string]: VariantMember<Key, Tag, Variants[Tag], Mode>;
140
+ }[keyof Variants & string];
141
+ /**
142
+ * The value of one variant named `Tag`. A blueprint gives its value with the
143
+ * key. A record class gives an instance, which has no key and is told apart
144
+ * by its class; what it takes is an instance or its plain input with the key,
145
+ * and its plain data has the key.
146
+ */
147
+ type VariantMember<Key extends string, Tag, Variant, Mode extends FieldMode> = Variant extends AnyRecordClass ? Mode extends "value" ? InstanceType<Variant> : Mode extends "input" ? InstanceType<Variant> | Evaluate<{
148
+ readonly [Name in Key]: Tag;
149
+ } & RecordInput<Variant>> : Evaluate<{
150
+ readonly [Name in Key]: Tag;
151
+ } & RecordValueOf<Variant, "data">> : Evaluate<{
152
+ readonly [Name in Key]: Tag;
153
+ } & FieldValue<Variant, Mode>>;
106
154
  /** The value of a record field, as `RecordSchema` describes it. */
107
155
  type RecordValueOf<Record extends AnyRecordClass, Mode extends FieldMode> = Mode extends "value" ? InstanceType<Record> : Mode extends "input" ? InstanceType<Record> | RecordInput<Record> | AcceptedBy<Record> : Record["blueprint"] extends IBlueprint<infer Properties, boolean> ? CollectionValue<Properties, "data"> : never;
108
156
  type ReadonlyOptionalKeys<Schemas> = {
@@ -196,6 +244,7 @@ export interface BlueprintHelpers {
196
244
  readonly listOf: typeof listOf;
197
245
  readonly optionalOf: typeof optionalOf;
198
246
  readonly propertyOf: typeof propertyOf;
247
+ readonly variantOf: typeof variantOf;
199
248
  }
200
249
  /**
201
250
  * What a blueprint's definition picks from: every source field, the field
@@ -223,9 +272,24 @@ export type CallableProperties<Fields> = {
223
272
  * @internal
224
273
  */
225
274
  export type OptionalProperties<Properties> = {
226
- readonly [Key in keyof Properties]: Properties[Key] extends AnyPropertyConstructor ? OptionalProperty<Properties[Key]> : Properties[Key] extends AnyBlueprint ? OptionalBlueprint<Properties[Key]> : Properties[Key] extends AnyBlueprintList ? OptionalBlueprintList<Properties[Key]> : Properties[Key] extends AnyRecordClass ? OptionalRecord<Properties[Key]> : Properties[Key] extends AnyOptionalRecord ? Properties[Key] : never;
275
+ readonly [Key in keyof Properties]: Properties[Key] extends AnyPropertyConstructor ? OptionalProperty<Properties[Key]> : Properties[Key] extends AnyBlueprint ? OptionalBlueprint<Properties[Key]> : Properties[Key] extends AnyBlueprintList ? OptionalBlueprintList<Properties[Key]> : Properties[Key] extends AnyRecordClass ? OptionalRecord<Properties[Key]> : Properties[Key] extends AnyOptionalRecord ? Properties[Key] : Properties[Key] extends {
276
+ readonly key: infer Name extends string;
277
+ readonly variants: infer Variants extends BlueprintVariants;
278
+ } ? IBlueprintVariant<Name, Variants, true> : never;
227
279
  };
280
+ /**
281
+ * The function {@link blueprintOf} takes: it gets the context `ctx` of the
282
+ * sources (the factories of their plugins, `listOf`, `optionalOf`, `propertyOf`
283
+ * and `variantOf`) and gives the fields of the blueprint.
284
+ */
228
285
  export type BlueprintDefinition<Sources extends readonly BlueprintSource[], Properties extends IBlueprintCollection> = (ctx: BlueprintContext<Sources>) => Properties;
286
+ /**
287
+ * A blueprint: an object shape of named fields, made by {@link blueprintOf}. It
288
+ * parses a value with `safeParse` or its Standard Schema (`~standard`), joins
289
+ * others with `join`, and is a field of another blueprint or the base of a
290
+ * record (see {@link recordOf}). `Optional` is `true` for one that, as a field,
291
+ * accepts `undefined`.
292
+ */
229
293
  export interface IBlueprint<Properties extends IBlueprintCollection, Optional extends boolean = false> {
230
294
  readonly [Kind]: "blueprint";
231
295
  /** The plugins it was built from, its joined blueprints' included. */
@@ -271,6 +335,10 @@ export interface BlueprintBuilder<Base extends readonly BlueprintSource[]> {
271
335
  }
272
336
  /** `Blueprint` as a field that accepts `undefined`. */
273
337
  export type OptionalBlueprint<Blueprint extends AnyBlueprint> = Blueprint extends IBlueprint<infer Properties, boolean> ? IBlueprint<Properties, true> : never;
338
+ /**
339
+ * The bounds of a list field, as `ctx.listOf(item, { minItems, maxItems })`
340
+ * takes them: the fewest and the most items it accepts.
341
+ */
274
342
  export interface BlueprintListOptions {
275
343
  readonly minItems?: number;
276
344
  readonly maxItems?: number;
@@ -288,6 +356,28 @@ export interface IBlueprintList<Item extends BlueprintField, Optional extends bo
288
356
  readonly "~standard": StandardSchemaV1.Props<ListShape<Item, "input", Optional>, ListShape<Item, "value", Optional>>;
289
357
  }
290
358
  type ListShape<Item, Mode extends FieldMode, Optional extends boolean> = Optional extends true ? FieldValue<Item, Mode>[] | undefined : FieldValue<Item, Mode>[];
359
+ /** The variants of a variant field, by the value its key has for each: blueprints, or record classes. */
360
+ export type BlueprintVariants = Readonly<Record<string, AnyBlueprint | AnyRecordClass>>;
361
+ /**
362
+ * A field holding one of the objects of `Variants`, told apart by the value
363
+ * of `Key`, which is the name of the variant.
364
+ */
365
+ export interface IBlueprintVariant<Key extends string, Variants extends BlueprintVariants, Optional extends boolean = false> {
366
+ readonly [Kind]: "variant";
367
+ /** The key that tells the variants apart; no variant declares it. */
368
+ readonly key: Key;
369
+ readonly variants: Variants;
370
+ /** Whether, as a field of a blueprint, it accepts `undefined`. */
371
+ readonly optional: Optional;
372
+ /**
373
+ * The Standard Schema of the field: `validate` gives the frozen value of
374
+ * the variant the key names, or the issues that reject the value.
375
+ */
376
+ readonly "~standard": StandardSchemaV1.Props<VariantShape<Key, Variants, "input", Optional>, VariantShape<Key, Variants, "value", Optional>>;
377
+ }
378
+ type VariantShape<Key extends string, Variants, Mode extends FieldMode, Optional extends boolean> = Optional extends true ? VariantValue<Key, Variants, Mode> | undefined : VariantValue<Key, Variants, Mode>;
379
+ /** `Variant` as a field that accepts `undefined`. */
380
+ export type OptionalBlueprintVariant<Variant extends AnyBlueprintVariant> = Variant extends IBlueprintVariant<infer Key, infer Variants, boolean> ? IBlueprintVariant<Key, Variants, true> : never;
291
381
  /** `List` as a field that accepts `undefined`. */
292
382
  export type OptionalBlueprintList<List extends AnyBlueprintList> = List extends IBlueprintList<infer Item, boolean> ? IBlueprintList<Item, true> : never;
293
383
  export {};
@@ -0,0 +1,32 @@
1
+ import type { AnyBlueprint, AnyBlueprintVariant, BlueprintVariants, IBlueprintVariant } from "./types/blueprint.interface.js";
2
+ /**
3
+ * A field holding one of several objects, told apart by the value of `key`:
4
+ * `variantOf("type", { click: Click, signup: Signup })` takes
5
+ * `{ type: "click", ...a Click }` or `{ type: "signup", ...a Signup }`. Each
6
+ * variant is a blueprint, which does not declare `key`: the field adds it,
7
+ * with the name of the variant for its value. A variant can also be a record
8
+ * class (an entity too): the value is then an instance of it, told apart by
9
+ * its class, and the key is only in what is given, in `toJSON` and in what is
10
+ * stored.
11
+ */
12
+ export declare function variantOf<const Key extends string, const Variants extends BlueprintVariants>(key: Key, variants: Variants): IBlueprintVariant<Key, Variants>;
13
+ /** `variant`, frozen, with the `~standard` that handles a value as `variant` does. */
14
+ export declare function standardVariantOf<Variant extends object>(variant: Variant): Variant;
15
+ type Variant = BlueprintVariants[string];
16
+ /** The blueprint of a variant: itself, or that of the record class it is. */
17
+ export declare function blueprintOfVariant(variant: Variant): AnyBlueprint;
18
+ /**
19
+ * The variant that the plain `value` names with the key of `field`, if it
20
+ * names one: a blueprint or a record class.
21
+ */
22
+ export declare function namedVariantOf(field: AnyBlueprintVariant, value: unknown): Variant | undefined;
23
+ /** The blueprint of the variant that the plain `value` names with the key of `field`, if it names one. */
24
+ export declare function variantOfValue(field: AnyBlueprintVariant, value: unknown): AnyBlueprint | undefined;
25
+ /**
26
+ * The name of the variant `instance` is, a record of one of the classes of
27
+ * `field`; `undefined` when it is of none.
28
+ */
29
+ export declare function variantNameOf(field: AnyBlueprintVariant, instance: unknown): string | undefined;
30
+ /** Whether `value` is a field made by `variantOf`. */
31
+ export declare function isBlueprintVariant(value: unknown): value is AnyBlueprintVariant;
32
+ export {};
@@ -0,0 +1,72 @@
1
+ import { parseField } from "../parse/parse.js";
2
+ import { withStandard } from "../standard/standard.js";
3
+ import { Kind } from "../symbols/kind.js";
4
+ import { InvalidBlueprintVariant } from "./exceptions.js";
5
+ import { isBlueprint } from "./is-blueprint.js";
6
+ import { isRecordClass } from "./record-field.js";
7
+ export function variantOf(key, variants) {
8
+ const fail = (reason) => {
9
+ throw new InvalidBlueprintVariant({
10
+ source: "blueprint",
11
+ details: { key: String(key), reason }
12
+ });
13
+ };
14
+ if (typeof key !== "string" || key === "") {
15
+ fail("its key must be a string that is not empty");
16
+ }
17
+ const tags = Object.keys(variants);
18
+ if (tags.length === 0)
19
+ fail("it needs at least one variant");
20
+ const classes = new Set;
21
+ for (const tag of tags) {
22
+ const variant = variants[tag];
23
+ if (!isBlueprint(variant) && !isRecordClass(variant)) {
24
+ fail(`the variant "${tag}" is not a blueprint nor a record class`);
25
+ }
26
+ if (classes.has(variant)) {
27
+ fail(`the variant "${tag}" is a class another variant already is`);
28
+ }
29
+ if (isRecordClass(variant))
30
+ classes.add(variant);
31
+ if (Object.hasOwn(blueprintOfVariant(variant).properties, key)) {
32
+ fail(`the variant "${tag}" declares "${key}", which the field adds`);
33
+ }
34
+ }
35
+ return standardVariantOf({
36
+ [Kind]: "variant",
37
+ key,
38
+ variants: Object.freeze({ ...variants }),
39
+ optional: false
40
+ });
41
+ }
42
+ export function standardVariantOf(variant) {
43
+ return Object.freeze(withStandard(variant, (value) => parseField(variant, value, [], "variant")));
44
+ }
45
+ export function blueprintOfVariant(variant) {
46
+ return isRecordClass(variant) ? variant.blueprint : variant;
47
+ }
48
+ export function namedVariantOf(field, value) {
49
+ if (typeof value !== "object" || value === null)
50
+ return;
51
+ const tag = value[field.key];
52
+ const variants = field.variants;
53
+ return typeof tag === "string" && Object.hasOwn(variants, tag) ? variants[tag] : undefined;
54
+ }
55
+ export function variantOfValue(field, value) {
56
+ const variant = namedVariantOf(field, value);
57
+ return variant && blueprintOfVariant(variant);
58
+ }
59
+ export function variantNameOf(field, instance) {
60
+ if (typeof instance !== "object" || instance === null)
61
+ return;
62
+ const variants = field.variants;
63
+ for (const tag of Object.keys(variants)) {
64
+ const variant = variants[tag];
65
+ if (isRecordClass(variant) && instance instanceof variant)
66
+ return tag;
67
+ }
68
+ return;
69
+ }
70
+ export function isBlueprintVariant(value) {
71
+ return typeof value === "object" && value !== null && value[Kind] === "variant";
72
+ }