@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.
- package/README.md +62 -189
- package/dist/adapters.js +31 -55
- package/dist/core/application/application.d.ts +30 -0
- package/dist/core/application/application.js +1291 -0
- package/dist/core/application/exceptions.js +85 -0
- package/dist/core/application/migrate.js +67 -0
- package/dist/core/application/providers.js +108 -0
- package/dist/core/application/registry.js +23 -0
- package/dist/core/application/schema.js +398 -0
- package/dist/core/application/start.js +162 -0
- package/dist/core/application/types/application.interface.d.ts +23 -3
- package/dist/core/auth/jwt.js +200 -0
- package/dist/core/auth/types/jwt.interface.d.ts +10 -2
- package/dist/core/blueprint/blueprint-plugin.js +7 -0
- package/dist/core/blueprint/blueprint.js +146 -0
- package/dist/core/blueprint/callable.js +13 -0
- package/dist/core/blueprint/exceptions.js +20 -0
- package/dist/core/blueprint/is-blueprint.js +4 -0
- package/dist/core/blueprint/list.js +19 -0
- package/dist/core/blueprint/optional.js +69 -0
- package/dist/core/blueprint/record-field.js +14 -0
- package/dist/core/blueprint/slice.js +127 -0
- package/dist/core/blueprint/types/blueprint.interface.d.ts +16 -0
- package/dist/core/blueprint/variant.js +72 -0
- package/dist/core/command/command.js +428 -0
- package/dist/core/command/exceptions.js +30 -0
- package/dist/core/command/types/command.interface.d.ts +10 -1
- package/dist/core/entity/abstract-entity.d.ts +19 -0
- package/dist/core/entity/abstract-entity.js +181 -0
- package/dist/core/entity/entity.js +209 -0
- package/dist/core/entity/event.d.ts +39 -0
- package/dist/core/entity/event.js +80 -0
- package/dist/core/entity/exceptions.js +27 -0
- package/dist/core/entity/trust.js +7 -0
- package/dist/core/entity/version.js +13 -0
- package/dist/core/environment/environment.d.ts +26 -3
- package/dist/core/environment/environment.js +71 -0
- package/dist/core/event-policy/event-policy.js +76 -0
- package/dist/core/event-policy/exceptions.js +12 -0
- package/dist/core/event-policy/publish.js +98 -0
- package/dist/core/event-policy/publishers.js +18 -0
- package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
- package/dist/core/exception/enums/exception-layers.js +15 -0
- package/dist/core/exception/exception.d.ts +15 -0
- package/dist/core/exception/exception.js +128 -0
- package/dist/core/exception/exported.js +49 -0
- package/dist/core/exception/guard.d.ts +20 -1
- package/dist/core/exception/guard.js +16 -0
- package/dist/core/exception/types/exception.interface.d.ts +13 -0
- package/dist/core/json.js +19 -0
- package/dist/core/logger/exceptions.js +7 -0
- package/dist/core/logger/logger.d.ts +14 -1
- package/dist/core/logger/logger.js +178 -0
- package/dist/core/openapi/exceptions.js +7 -0
- package/dist/core/openapi/openapi.js +132 -0
- package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
- package/dist/core/output/access.js +66 -0
- package/dist/core/output/coerce.js +38 -0
- package/dist/core/output/exceptions.js +22 -0
- package/dist/{index-y280ew0k.js → core/output/http.js} +35 -80
- package/dist/core/output/openapi-sink.d.ts +10 -2
- package/dist/{index-hg1z2s2v.js → core/output/openapi-sink.js} +60 -544
- package/dist/core/output/output.js +192 -0
- package/dist/core/output/problem.js +30 -0
- package/dist/core/output/select.js +28 -0
- package/dist/core/output/stage.js +111 -0
- package/dist/core/output/status.js +52 -0
- package/dist/core/output/types/http.interface.d.ts +5 -1
- package/dist/core/output/types/output.interface.d.ts +24 -0
- package/dist/core/parse/issue.js +31 -0
- package/dist/core/parse/parse.d.ts +8 -1
- package/dist/core/parse/parse.js +323 -0
- package/dist/core/parse/strip.js +48 -0
- package/dist/core/property/abstract-property.js +37 -0
- package/dist/core/property/define-property.js +209 -0
- package/dist/core/property/enums/property-states.js +8 -0
- package/dist/core/property/exceptions.js +12 -0
- package/dist/core/property/input.js +10 -0
- package/dist/core/property/is-property.js +3 -0
- package/dist/core/property/lineage.js +8 -0
- package/dist/core/property/property.d.ts +27 -0
- package/dist/core/property/property.js +46 -0
- package/dist/core/property/refine.js +103 -0
- package/dist/core/record/abstract-record.d.ts +2 -1
- package/dist/core/record/abstract-record.js +307 -0
- package/dist/core/record/exceptions.js +49 -0
- package/dist/core/record/marks.js +7 -0
- package/dist/core/record/mask.js +60 -0
- package/dist/core/record/name.js +23 -0
- package/dist/core/record/plugin-catalog.js +115 -0
- package/dist/core/record/plugin.js +198 -0
- package/dist/core/record/record.js +117 -0
- package/dist/core/record/types/record.interface.d.ts +5 -0
- package/dist/core/repository/cursor.js +83 -0
- package/dist/core/repository/exceptions.js +72 -0
- package/dist/core/repository/in-memory.js +196 -0
- package/dist/core/repository/migrate.js +254 -0
- package/dist/core/repository/plan.js +27 -0
- package/dist/core/repository/provider.js +29 -0
- package/dist/core/repository/query.js +153 -0
- package/dist/core/repository/references.js +196 -0
- package/dist/core/repository/repository.d.ts +22 -2
- package/dist/core/repository/repository.js +720 -0
- package/dist/core/repository/state.js +138 -0
- package/dist/core/repository/types/query.interface.d.ts +13 -0
- package/dist/core/repository/types/repository.interface.d.ts +20 -8
- package/dist/core/repository/types/schema.interface.d.ts +8 -2
- package/dist/core/repository/upcast.js +179 -0
- package/dist/core/schema/schema.d.ts +9 -0
- package/dist/core/schema/schema.js +542 -0
- package/dist/core/standard/standard-schema.js +1 -0
- package/dist/core/standard/standard.js +23 -0
- package/dist/core/symbols/demo.d.ts +4 -0
- package/dist/core/symbols/demo.js +1 -0
- package/dist/core/symbols/kind.js +1 -0
- package/dist/core/symbols/meta.js +1 -0
- package/dist/core/symbols/state.d.ts +5 -0
- package/dist/core/symbols/state.js +1 -0
- package/dist/core/tracing/exceptions.d.ts +4 -0
- package/dist/core/tracing/exceptions.js +7 -0
- package/dist/core/tracing/tracing.d.ts +72 -0
- package/dist/core/tracing/tracing.js +101 -0
- package/dist/core/use-case/access.js +56 -0
- package/dist/core/use-case/decorators.js +65 -0
- package/dist/core/use-case/exceptions.d.ts +5 -1
- package/dist/core/use-case/exceptions.js +48 -0
- package/dist/core/use-case/plugin-catalog.js +68 -0
- package/dist/core/use-case/plugin.js +133 -0
- package/dist/core/use-case/route.js +200 -0
- package/dist/core/use-case/scope.d.ts +5 -0
- package/dist/core/use-case/scope.js +4 -0
- package/dist/core/use-case/steps/describe.d.ts +5 -1
- package/dist/core/use-case/steps/describe.js +24 -0
- package/dist/core/use-case/steps/references.js +105 -0
- package/dist/core/use-case/steps/run.js +139 -0
- package/dist/core/use-case/steps/types/check.interface.d.ts +27 -8
- package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
- package/dist/core/use-case/steps/validate.js +368 -0
- package/dist/core/use-case/target.js +100 -0
- package/dist/core/use-case/types/route.interface.d.ts +25 -5
- package/dist/core/use-case/use-case.d.ts +77 -2
- package/dist/core/use-case/use-case.js +493 -0
- package/dist/experimental.d.ts +5 -0
- package/dist/experimental.js +18 -161
- package/dist/index.d.ts +7 -5
- package/dist/index.js +213 -367
- package/dist/std/blueprint-plugin.js +42 -0
- package/dist/std/blueprint.d.ts +21 -0
- package/dist/std/blueprint.js +3 -0
- package/dist/std/command.js +3 -0
- package/dist/std/entity.d.ts +20 -1
- package/dist/std/entity.js +30 -0
- package/dist/std/event-policies.js +3 -0
- package/dist/std/properties/contact.d.ts +3 -0
- package/dist/std/properties/contact.js +29 -0
- package/dist/std/properties/enum.js +26 -0
- package/dist/std/properties/exceptions.js +14 -0
- package/dist/std/properties/headless.js +45 -0
- package/dist/std/properties/identity.js +28 -0
- package/dist/std/properties/json.js +161 -0
- package/dist/std/properties/password.js +79 -0
- package/dist/std/properties/primitives.d.ts +4 -0
- package/dist/std/properties/primitives.js +28 -0
- package/dist/std/properties/time.js +55 -0
- package/dist/std/record-plugins.js +73 -0
- package/dist/std/record.d.ts +20 -1
- package/dist/std/record.js +3 -0
- package/dist/std/resource.js +39 -0
- package/dist/std/runtime.js +193 -0
- package/dist/std/stages.js +4 -0
- package/dist/std/use-cases/auth.js +193 -0
- package/dist/std/use-cases/crud.js +261 -0
- package/dist/std/use-cases/index.js +12 -0
- package/dist/std/use-cases/lifecycle.js +97 -0
- package/dist/std/use-cases/shared.js +232 -0
- package/dist/std/use-cases/types.d.ts +5 -1
- package/dist/std/use-cases/types.js +0 -0
- package/dist/testing/http-provider-contract.js +344 -0
- package/dist/testing/index.js +3 -1435
- package/dist/testing/memory-publisher.js +12 -0
- package/dist/testing/provider-contract.d.ts +12 -3
- package/dist/testing/provider-contract.js +1071 -0
- package/package.json +17 -5
- package/dist/index-3aae5jfq.js +0 -2997
- package/dist/index-ny3as79r.js +0 -4567
- package/dist/index-xegv6a1p.js +0 -4291
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { recordPropertyLineage } from "../property/lineage.js";
|
|
3
|
+
import { derivePropertyOf } from "../property/refine.js";
|
|
4
|
+
import { rawTypeOf, schemaOf } from "../schema/schema.js";
|
|
5
|
+
import { withStandard } from "../standard/standard.js";
|
|
6
|
+
import { Kind } from "../symbols/kind.js";
|
|
7
|
+
import { fieldOf } from "./callable.js";
|
|
8
|
+
import { isBlueprint } from "./is-blueprint.js";
|
|
9
|
+
import { isBlueprintList, standardListOf } from "./list.js";
|
|
10
|
+
import { isOptionalRecord, isRecordClass } from "./record-field.js";
|
|
11
|
+
import { isBlueprintVariant, standardVariantOf } from "./variant.js";
|
|
12
|
+
const cache = new WeakMap;
|
|
13
|
+
export function optionalOf(given) {
|
|
14
|
+
const field = fieldOf(given);
|
|
15
|
+
const cached = cache.get(field);
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
if (isOptionalRecord(field))
|
|
19
|
+
return field;
|
|
20
|
+
if (isRecordClass(field)) {
|
|
21
|
+
const optional = Object.freeze(withStandard({
|
|
22
|
+
[Kind]: "record",
|
|
23
|
+
record: field,
|
|
24
|
+
optional: true
|
|
25
|
+
}, (value) => value === undefined ? { value } : field.safeParse(value)));
|
|
26
|
+
cache.set(field, optional);
|
|
27
|
+
return optional;
|
|
28
|
+
}
|
|
29
|
+
if (isBlueprint(field)) {
|
|
30
|
+
if (field.optional)
|
|
31
|
+
return field;
|
|
32
|
+
const optional = {
|
|
33
|
+
...field,
|
|
34
|
+
optional: true,
|
|
35
|
+
safeParse: (value) => value === undefined ? { value } : field.safeParse(value)
|
|
36
|
+
};
|
|
37
|
+
Object.freeze(withStandard(optional, (value) => optional.safeParse(value)));
|
|
38
|
+
cache.set(field, optional);
|
|
39
|
+
return optional;
|
|
40
|
+
}
|
|
41
|
+
if (isBlueprintList(field)) {
|
|
42
|
+
if (field.optional)
|
|
43
|
+
return field;
|
|
44
|
+
const optional = standardListOf({ ...field, optional: true });
|
|
45
|
+
cache.set(field, optional);
|
|
46
|
+
return optional;
|
|
47
|
+
}
|
|
48
|
+
if (isBlueprintVariant(field)) {
|
|
49
|
+
if (field.optional)
|
|
50
|
+
return field;
|
|
51
|
+
const optional = standardVariantOf({ ...field, optional: true });
|
|
52
|
+
cache.set(field, optional);
|
|
53
|
+
return optional;
|
|
54
|
+
}
|
|
55
|
+
const Property = field;
|
|
56
|
+
if (Property.type.optional)
|
|
57
|
+
return Property;
|
|
58
|
+
const base = Property.type;
|
|
59
|
+
const SchemaConstructor = schemaOf(base.name, Type.Optional(rawTypeOf(base.schema)));
|
|
60
|
+
const type = {
|
|
61
|
+
...base,
|
|
62
|
+
schema: new SchemaConstructor,
|
|
63
|
+
optional: true
|
|
64
|
+
};
|
|
65
|
+
const Optional = derivePropertyOf(Property, type);
|
|
66
|
+
recordPropertyLineage(Optional, { kind: "optional", base: Property });
|
|
67
|
+
cache.set(Property, Optional);
|
|
68
|
+
return Optional;
|
|
69
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Kind } from "../symbols/kind.js";
|
|
2
|
+
export function isRecordClass(value) {
|
|
3
|
+
return typeof value === "function" && value[Kind] === "record";
|
|
4
|
+
}
|
|
5
|
+
export function isOptionalRecord(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && value[Kind] === "record";
|
|
7
|
+
}
|
|
8
|
+
export function recordFieldOf(field) {
|
|
9
|
+
if (isRecordClass(field))
|
|
10
|
+
return { record: field, optional: false };
|
|
11
|
+
if (isOptionalRecord(field))
|
|
12
|
+
return field;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -277,7 +277,19 @@ export type OptionalProperties<Properties> = {
|
|
|
277
277
|
readonly variants: infer Variants extends BlueprintVariants;
|
|
278
278
|
} ? IBlueprintVariant<Name, Variants, true> : never;
|
|
279
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
|
+
*/
|
|
280
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
|
+
*/
|
|
281
293
|
export interface IBlueprint<Properties extends IBlueprintCollection, Optional extends boolean = false> {
|
|
282
294
|
readonly [Kind]: "blueprint";
|
|
283
295
|
/** The plugins it was built from, its joined blueprints' included. */
|
|
@@ -323,6 +335,10 @@ export interface BlueprintBuilder<Base extends readonly BlueprintSource[]> {
|
|
|
323
335
|
}
|
|
324
336
|
/** `Blueprint` as a field that accepts `undefined`. */
|
|
325
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
|
+
*/
|
|
326
342
|
export interface BlueprintListOptions {
|
|
327
343
|
readonly minItems?: number;
|
|
328
344
|
readonly maxItems?: number;
|
|
@@ -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
|
+
}
|