@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.
- package/README.md +149 -23
- package/dist/adapters.d.ts +25 -0
- package/dist/adapters.js +32 -0
- package/dist/core/application/application.d.ts +30 -0
- package/dist/core/application/application.js +1291 -0
- package/dist/core/application/exceptions.d.ts +17 -0
- package/dist/core/application/exceptions.js +85 -0
- package/dist/core/application/migrate.d.ts +19 -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.d.ts +30 -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 +60 -3
- package/dist/core/auth/jwt.d.ts +30 -0
- package/dist/core/auth/jwt.js +200 -0
- package/dist/core/auth/types/jwt.interface.d.ts +74 -0
- 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.d.ts +5 -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.d.ts +3 -1
- 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 +95 -5
- package/dist/core/blueprint/variant.d.ts +32 -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 +24 -0
- package/dist/core/entity/abstract-entity.js +181 -0
- package/dist/core/entity/entity.d.ts +1 -1
- 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.d.ts +9 -0
- package/dist/core/entity/exceptions.js +27 -0
- package/dist/core/entity/trust.d.ts +24 -0
- package/dist/core/entity/trust.js +7 -0
- package/dist/core/entity/types/entity.interface.d.ts +30 -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.d.ts +16 -0
- package/dist/core/output/access.js +66 -0
- package/dist/core/output/coerce.js +38 -0
- package/dist/core/output/exceptions.d.ts +16 -0
- package/dist/core/output/exceptions.js +22 -0
- package/dist/core/output/http.d.ts +36 -0
- package/dist/core/output/http.js +351 -0
- package/dist/core/output/openapi-sink.d.ts +33 -4
- package/dist/core/output/openapi-sink.js +571 -0
- 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.d.ts +7 -2
- package/dist/core/output/stage.js +111 -0
- package/dist/core/output/status.d.ts +1 -1
- package/dist/core/output/status.js +52 -0
- package/dist/core/output/types/http.interface.d.ts +134 -0
- package/dist/core/output/types/output.interface.d.ts +49 -5
- package/dist/core/parse/issue.d.ts +28 -3
- package/dist/core/parse/issue.js +31 -0
- package/dist/core/parse/parse.d.ts +16 -0
- package/dist/core/parse/parse.js +323 -0
- package/dist/core/parse/strip.js +48 -0
- package/dist/core/property/abstract-property.d.ts +9 -0
- package/dist/core/property/abstract-property.js +37 -0
- package/dist/core/property/define-property.d.ts +34 -6
- 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/property/types/property-options.interface.d.ts +10 -0
- package/dist/core/property/types/property-type.interface.d.ts +4 -0
- package/dist/core/record/abstract-record.d.ts +8 -0
- 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 +17 -1
- package/dist/core/repository/cursor.js +83 -0
- package/dist/core/repository/exceptions.d.ts +13 -0
- package/dist/core/repository/exceptions.js +72 -0
- package/dist/core/repository/in-memory.d.ts +2 -2
- package/dist/core/repository/in-memory.js +196 -0
- package/dist/core/repository/migrate.d.ts +20 -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.d.ts +2 -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 +50 -5
- package/dist/core/repository/types/schema.interface.d.ts +132 -0
- package/dist/core/repository/upcast.d.ts +64 -0
- package/dist/core/repository/upcast.js +179 -0
- package/dist/core/schema/schema.d.ts +37 -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.d.ts +1 -1
- 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.d.ts +12 -0
- package/dist/core/use-case/access.js +56 -0
- package/dist/core/use-case/decorators.d.ts +40 -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.d.ts +18 -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 +233 -0
- 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/access.interface.d.ts +39 -0
- package/dist/core/use-case/types/route.interface.d.ts +86 -0
- package/dist/core/use-case/use-case.d.ts +127 -2
- package/dist/core/use-case/use-case.js +493 -0
- package/dist/experimental.d.ts +18 -1
- package/dist/experimental.js +18 -123
- package/dist/index.d.ts +49 -51
- package/dist/index.js +213 -443
- 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.d.ts +0 -2
- 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.d.ts +2 -2
- 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.d.ts +31 -0
- package/dist/std/resource.js +39 -0
- package/dist/std/runtime.d.ts +6 -0
- package/dist/std/runtime.js +193 -0
- package/dist/std/stages.js +4 -0
- package/dist/std/use-cases/auth.d.ts +5 -1
- package/dist/std/use-cases/auth.js +193 -0
- package/dist/std/use-cases/crud.js +261 -0
- package/dist/std/use-cases/index.d.ts +1 -1
- package/dist/std/use-cases/index.js +12 -0
- package/dist/std/use-cases/lifecycle.js +97 -0
- package/dist/std/use-cases/shared.d.ts +2 -2
- package/dist/std/use-cases/shared.js +232 -0
- package/dist/std/use-cases/types.d.ts +102 -2
- package/dist/std/use-cases/types.js +0 -0
- package/dist/testing/http-provider-contract.d.ts +32 -0
- package/dist/testing/http-provider-contract.js +344 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +3 -944
- package/dist/testing/memory-publisher.js +12 -0
- package/dist/testing/provider-contract.d.ts +26 -1
- package/dist/testing/provider-contract.js +1071 -0
- package/package.json +23 -3
- package/dist/index-qxfkyd6k.js +0 -1721
- 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,
|
|
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>> :
|
|
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>[] :
|
|
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] :
|
|
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
|
+
}
|