@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,398 @@
|
|
|
1
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2
|
+
function sorted(value) {
|
|
3
|
+
if (Array.isArray(value))
|
|
4
|
+
return value.map(sorted);
|
|
5
|
+
if (!isObject(value))
|
|
6
|
+
return value;
|
|
7
|
+
return Object.fromEntries(Object.keys(value).filter((key) => !key.startsWith("~") && value[key] !== undefined).sort().map((key) => [key, sorted(value[key])]));
|
|
8
|
+
}
|
|
9
|
+
function evolutionOf(property, description, found = {}, depth = 0) {
|
|
10
|
+
if (property === undefined || depth > 64)
|
|
11
|
+
return found;
|
|
12
|
+
const resolved = "ref" in property ? description.properties[property.ref] : property;
|
|
13
|
+
const lineage = resolved?.lineage;
|
|
14
|
+
if (lineage === undefined || lineage.kind === "factory")
|
|
15
|
+
return found;
|
|
16
|
+
if (lineage.kind === "refined") {
|
|
17
|
+
const { fill, renamedFrom } = lineage.options;
|
|
18
|
+
if (!("fill" in found) && fill !== undefined)
|
|
19
|
+
found.fill = fill;
|
|
20
|
+
if (found.renamedFrom === undefined && typeof renamedFrom === "string") {
|
|
21
|
+
found.renamedFrom = renamedFrom;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return evolutionOf(lineage.base, description, found, depth + 1);
|
|
25
|
+
}
|
|
26
|
+
function fieldsOf(fields, description) {
|
|
27
|
+
if (fields === undefined)
|
|
28
|
+
return;
|
|
29
|
+
return Object.fromEntries(Object.keys(fields).sort().map((key) => [
|
|
30
|
+
key,
|
|
31
|
+
lockFieldOf(fields[key], description)
|
|
32
|
+
]));
|
|
33
|
+
}
|
|
34
|
+
function lockFieldOf(field, description) {
|
|
35
|
+
switch (field.kind) {
|
|
36
|
+
case "property": {
|
|
37
|
+
const property = "ref" in field ? description.properties[field.ref] : field;
|
|
38
|
+
const evolution = evolutionOf("ref" in field ? { ref: field.ref } : field, description);
|
|
39
|
+
return {
|
|
40
|
+
schema: sorted(property?.schema ?? {}),
|
|
41
|
+
optional: field.optional,
|
|
42
|
+
...property?.unique ? { unique: true } : {},
|
|
43
|
+
..."fill" in evolution ? { fill: sorted(evolution.fill) } : {},
|
|
44
|
+
...evolution.renamedFrom === undefined ? {} : { renamedFrom: evolution.renamedFrom }
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
case "list": {
|
|
48
|
+
const item = lockFieldOf(field.item, description);
|
|
49
|
+
return {
|
|
50
|
+
schema: sorted({
|
|
51
|
+
type: "array",
|
|
52
|
+
items: item.schema,
|
|
53
|
+
minItems: field.minItems,
|
|
54
|
+
maxItems: field.maxItems
|
|
55
|
+
}),
|
|
56
|
+
optional: field.optional,
|
|
57
|
+
...item.fields === undefined ? {} : { fields: item.fields }
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
case "blueprint": {
|
|
61
|
+
const fields = "ref" in field ? description.blueprints[field.ref]?.fields : field.fields;
|
|
62
|
+
return {
|
|
63
|
+
schema: { type: "object" },
|
|
64
|
+
optional: field.optional,
|
|
65
|
+
fields: fieldsOf(fields, description) ?? {}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
case "record": {
|
|
69
|
+
const fields = description.records[field.ref]?.fields;
|
|
70
|
+
return {
|
|
71
|
+
schema: { type: "object" },
|
|
72
|
+
optional: field.optional,
|
|
73
|
+
fields: fieldsOf(fields, description) ?? {}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
case "variant":
|
|
77
|
+
return {
|
|
78
|
+
schema: { discriminator: field.key, type: "object" },
|
|
79
|
+
optional: field.optional,
|
|
80
|
+
fields: Object.fromEntries(Object.keys(field.variants).sort().map((tag) => [
|
|
81
|
+
tag,
|
|
82
|
+
{
|
|
83
|
+
...lockFieldOf(field.variants[tag], description),
|
|
84
|
+
optional: true
|
|
85
|
+
}
|
|
86
|
+
]))
|
|
87
|
+
};
|
|
88
|
+
case "entity":
|
|
89
|
+
return {
|
|
90
|
+
schema: { entity: field.ref, type: "string" },
|
|
91
|
+
optional: field.optional
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function lockEntityOf(entity, description) {
|
|
96
|
+
const declared = fieldsOf(entity.fields, description) ?? {};
|
|
97
|
+
const unique = new Set(entity.options.unique.filter((key) => key !== "id"));
|
|
98
|
+
for (const [key, field] of Object.entries(declared)) {
|
|
99
|
+
if (field.unique)
|
|
100
|
+
unique.add(key);
|
|
101
|
+
}
|
|
102
|
+
const fields = Object.fromEntries(Object.entries(declared).map(([key, field]) => [
|
|
103
|
+
key,
|
|
104
|
+
unique.has(key) ? { ...field, unique: true } : field
|
|
105
|
+
]));
|
|
106
|
+
return {
|
|
107
|
+
schemaVersion: entity.schemaVersion ?? 1,
|
|
108
|
+
previousNames: [...entity.previousNames ?? []].sort(),
|
|
109
|
+
unique: [...unique].sort(),
|
|
110
|
+
fields,
|
|
111
|
+
...entity.upgrade === undefined || entity.upgrade.length === 0 ? {} : { upgrade: [...entity.upgrade].sort((a, b) => a - b) }
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function schemaLockOf(description) {
|
|
115
|
+
return {
|
|
116
|
+
lockVersion: 1,
|
|
117
|
+
entities: sorted(Object.fromEntries(Object.keys(description.entities).map((name) => [
|
|
118
|
+
name,
|
|
119
|
+
lockEntityOf(description.entities[name], description)
|
|
120
|
+
])))
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const METADATA = new Set([
|
|
124
|
+
"title",
|
|
125
|
+
"description",
|
|
126
|
+
"examples",
|
|
127
|
+
"default",
|
|
128
|
+
"deprecated",
|
|
129
|
+
"readOnly",
|
|
130
|
+
"writeOnly",
|
|
131
|
+
"$comment"
|
|
132
|
+
]);
|
|
133
|
+
const UPPER = new Set([
|
|
134
|
+
"maximum",
|
|
135
|
+
"exclusiveMaximum",
|
|
136
|
+
"maxLength",
|
|
137
|
+
"maxItems",
|
|
138
|
+
"maxProperties"
|
|
139
|
+
]);
|
|
140
|
+
const LOWER = new Set([
|
|
141
|
+
"minimum",
|
|
142
|
+
"exclusiveMinimum",
|
|
143
|
+
"minLength",
|
|
144
|
+
"minItems",
|
|
145
|
+
"minProperties"
|
|
146
|
+
]);
|
|
147
|
+
const NARROWING = new Set([
|
|
148
|
+
"pattern",
|
|
149
|
+
"format",
|
|
150
|
+
"multipleOf",
|
|
151
|
+
"allOf",
|
|
152
|
+
"const"
|
|
153
|
+
]);
|
|
154
|
+
const same = (a, b) => JSON.stringify(sorted(a)) === JSON.stringify(sorted(b));
|
|
155
|
+
function literalsOf(schema) {
|
|
156
|
+
if (Array.isArray(schema.enum)) {
|
|
157
|
+
return schema.enum.map((value) => JSON.stringify(value));
|
|
158
|
+
}
|
|
159
|
+
if (Array.isArray(schema.anyOf) && schema.anyOf.every((item) => isObject(item) && ("const" in item))) {
|
|
160
|
+
return schema.anyOf.map((item) => JSON.stringify(item.const));
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
function compareSchemas(previous, next) {
|
|
165
|
+
if (same(previous, next))
|
|
166
|
+
return "same";
|
|
167
|
+
const strip = (schema) => Object.fromEntries(Object.entries(schema).filter(([key]) => !METADATA.has(key)));
|
|
168
|
+
const [a, b] = [strip(previous), strip(next)];
|
|
169
|
+
if (same(a, b))
|
|
170
|
+
return "metadata";
|
|
171
|
+
if (!same(a.type, b.type) || !same(a.entity, b.entity))
|
|
172
|
+
return "type-changed";
|
|
173
|
+
const [before, after] = [literalsOf(a), literalsOf(b)];
|
|
174
|
+
let loosened = false;
|
|
175
|
+
let tightened = false;
|
|
176
|
+
if (before !== undefined || after !== undefined) {
|
|
177
|
+
if (before === undefined || after === undefined)
|
|
178
|
+
return "type-changed";
|
|
179
|
+
const kept = before.every((value) => after.includes(value));
|
|
180
|
+
const gained = after.some((value) => !before.includes(value));
|
|
181
|
+
if (!kept)
|
|
182
|
+
tightened = true;
|
|
183
|
+
if (gained)
|
|
184
|
+
loosened = true;
|
|
185
|
+
}
|
|
186
|
+
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
187
|
+
for (const key of keys) {
|
|
188
|
+
if (["type", "entity", "enum", "anyOf"].includes(key))
|
|
189
|
+
continue;
|
|
190
|
+
if (same(a[key], b[key]))
|
|
191
|
+
continue;
|
|
192
|
+
const [x, y] = [a[key], b[key]];
|
|
193
|
+
if (key === "items") {
|
|
194
|
+
const items = compareSchemas(isObject(x) ? x : {}, isObject(y) ? y : {});
|
|
195
|
+
if (items === "type-changed")
|
|
196
|
+
return "type-changed";
|
|
197
|
+
if (items === "tightened")
|
|
198
|
+
tightened = true;
|
|
199
|
+
if (items === "loosened")
|
|
200
|
+
loosened = true;
|
|
201
|
+
} else if (UPPER.has(key) || LOWER.has(key)) {
|
|
202
|
+
if (x === undefined)
|
|
203
|
+
tightened = true;
|
|
204
|
+
else if (y === undefined)
|
|
205
|
+
loosened = true;
|
|
206
|
+
else if (typeof x !== "number" || typeof y !== "number")
|
|
207
|
+
return "type-changed";
|
|
208
|
+
else if (UPPER.has(key) ? y > x : y < x)
|
|
209
|
+
loosened = true;
|
|
210
|
+
else
|
|
211
|
+
tightened = true;
|
|
212
|
+
} else if (NARROWING.has(key)) {
|
|
213
|
+
if (y === undefined)
|
|
214
|
+
loosened = true;
|
|
215
|
+
else
|
|
216
|
+
tightened = true;
|
|
217
|
+
} else {
|
|
218
|
+
return "type-changed";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (tightened)
|
|
222
|
+
return "tightened";
|
|
223
|
+
return loosened ? "loosened" : "metadata";
|
|
224
|
+
}
|
|
225
|
+
function push(context, change, klass, satisfied, extra = {}) {
|
|
226
|
+
context.changes.push({
|
|
227
|
+
entity: context.entity,
|
|
228
|
+
...extra.key === undefined ? {} : { key: extra.key },
|
|
229
|
+
change,
|
|
230
|
+
class: klass,
|
|
231
|
+
satisfied,
|
|
232
|
+
destructive: extra.destructive ?? false,
|
|
233
|
+
...extra.from === undefined ? {} : { from: extra.from },
|
|
234
|
+
...extra.detail === undefined ? {} : { detail: extra.detail }
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const STEP = "an upgrade step (bump schemaVersion and add upgrade[n])";
|
|
238
|
+
function diffFields(context, previous, next, path) {
|
|
239
|
+
const join = (key) => path ? `${path}.${key}` : key;
|
|
240
|
+
const renamedFrom = new Map;
|
|
241
|
+
for (const [key, field] of Object.entries(next)) {
|
|
242
|
+
const from = field.renamedFrom;
|
|
243
|
+
if (from !== undefined && !(key in previous) && from in previous && !(from in next)) {
|
|
244
|
+
renamedFrom.set(key, from);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const moved = new Set(renamedFrom.values());
|
|
248
|
+
const keys = [...new Set([...Object.keys(previous), ...Object.keys(next)])].filter((key) => !moved.has(key)).sort();
|
|
249
|
+
for (const key of keys) {
|
|
250
|
+
const was = previous[key];
|
|
251
|
+
const is = next[key];
|
|
252
|
+
if (is === undefined) {
|
|
253
|
+
push(context, "removed", "automatic", true, {
|
|
254
|
+
key: join(key),
|
|
255
|
+
destructive: true,
|
|
256
|
+
detail: "Left out on read; a backfill removes it only with destructive: true. Rename it with renamedFrom to keep the data."
|
|
257
|
+
});
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const from = renamedFrom.get(key);
|
|
261
|
+
if (from !== undefined) {
|
|
262
|
+
push(context, "renamed", "declared", true, {
|
|
263
|
+
key: join(key),
|
|
264
|
+
from: join(from)
|
|
265
|
+
});
|
|
266
|
+
diffField(context, previous[from], is, join(key));
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (was === undefined) {
|
|
270
|
+
if (is.optional) {
|
|
271
|
+
push(context, "added-optional", "automatic", true, { key: join(key) });
|
|
272
|
+
} else {
|
|
273
|
+
const satisfied = "fill" in is || context.stepped;
|
|
274
|
+
push(context, "added-required", "declared", satisfied, {
|
|
275
|
+
key: join(key),
|
|
276
|
+
...satisfied ? {} : {
|
|
277
|
+
detail: `Needs a fill (.with({ fill })) or ${STEP} that sets it.`
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
diffField(context, was, is, join(key));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function diffField(context, was, is, key) {
|
|
287
|
+
const compared = compareSchemas(was.schema, is.schema);
|
|
288
|
+
if (compared === "type-changed") {
|
|
289
|
+
push(context, "type-changed", "declared", context.stepped, {
|
|
290
|
+
key,
|
|
291
|
+
...context.stepped ? {} : { detail: `Needs ${STEP} that converts the stored values.` }
|
|
292
|
+
});
|
|
293
|
+
} else if (compared === "tightened") {
|
|
294
|
+
const satisfied = context.stepped || "fill" in is;
|
|
295
|
+
push(context, "tightened", "declared", satisfied, {
|
|
296
|
+
key,
|
|
297
|
+
detail: satisfied ? "Run app.migrate({ mode: 'check' }) to list the rows that violate it." : `Needs ${STEP} or a fill; app.migrate({ mode: 'check' }) lists the rows that violate it.`
|
|
298
|
+
});
|
|
299
|
+
} else if (compared === "loosened") {
|
|
300
|
+
push(context, "loosened", "automatic", true, { key });
|
|
301
|
+
} else if (compared === "metadata") {
|
|
302
|
+
push(context, "metadata", "automatic", true, { key });
|
|
303
|
+
}
|
|
304
|
+
if (was.optional && !is.optional) {
|
|
305
|
+
const satisfied = context.stepped || "fill" in is;
|
|
306
|
+
push(context, "made-required", "declared", satisfied, {
|
|
307
|
+
key,
|
|
308
|
+
...satisfied ? {} : {
|
|
309
|
+
detail: `Needs a fill (.with({ fill })) or ${STEP} for the rows without it.`
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
} else if (!was.optional && is.optional) {
|
|
313
|
+
push(context, "made-optional", "automatic", true, { key });
|
|
314
|
+
}
|
|
315
|
+
if (!was.unique && is.unique) {
|
|
316
|
+
push(context, "unique-added", "check", true, {
|
|
317
|
+
key,
|
|
318
|
+
detail: "Run app.migrate({ mode: 'check' }) and look for duplicates before it is enforced."
|
|
319
|
+
});
|
|
320
|
+
} else if (was.unique && !is.unique) {
|
|
321
|
+
push(context, "unique-removed", "automatic", true, { key });
|
|
322
|
+
}
|
|
323
|
+
if (was.fields !== undefined || is.fields !== undefined) {
|
|
324
|
+
diffFields(context, was.fields ?? {}, is.fields ?? {}, key);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function diffEntity(entity, from, to, changes) {
|
|
328
|
+
const stepped = to.schemaVersion > from.schemaVersion && (to.upgrade ?? []).some((version) => version > from.schemaVersion && version <= to.schemaVersion);
|
|
329
|
+
const context = { entity, from, to, stepped, changes };
|
|
330
|
+
diffFields(context, from.fields, to.fields, "");
|
|
331
|
+
}
|
|
332
|
+
export function diffSchemas(lock, current) {
|
|
333
|
+
const changes = [];
|
|
334
|
+
const claimed = new Set;
|
|
335
|
+
for (const name of Object.keys(current.entities).sort()) {
|
|
336
|
+
const to = current.entities[name];
|
|
337
|
+
let from = lock.entities[name];
|
|
338
|
+
if (from === undefined) {
|
|
339
|
+
const previous = to.previousNames.find((old) => lock.entities[old] !== undefined && !current.entities[old]);
|
|
340
|
+
if (previous === undefined) {
|
|
341
|
+
changes.push({
|
|
342
|
+
entity: name,
|
|
343
|
+
change: "entity-added",
|
|
344
|
+
class: "automatic",
|
|
345
|
+
satisfied: true,
|
|
346
|
+
destructive: false
|
|
347
|
+
});
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
claimed.add(previous);
|
|
351
|
+
from = lock.entities[previous];
|
|
352
|
+
changes.push({
|
|
353
|
+
entity: name,
|
|
354
|
+
change: "entity-renamed",
|
|
355
|
+
class: "declared",
|
|
356
|
+
satisfied: true,
|
|
357
|
+
destructive: true,
|
|
358
|
+
from: previous,
|
|
359
|
+
detail: "app.migrate moves its rows; it removes them from the previous name only with destructive: true."
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
diffEntity(name, from, to, changes);
|
|
363
|
+
}
|
|
364
|
+
for (const name of Object.keys(lock.entities).sort()) {
|
|
365
|
+
if (current.entities[name] !== undefined || claimed.has(name))
|
|
366
|
+
continue;
|
|
367
|
+
changes.push({
|
|
368
|
+
entity: name,
|
|
369
|
+
change: "entity-removed",
|
|
370
|
+
class: "declared",
|
|
371
|
+
satisfied: false,
|
|
372
|
+
destructive: true,
|
|
373
|
+
detail: `No entity is named "${name}": list it in the previousNames of the entity that replaces it, or remove it from the lock if it is gone.`
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return changes;
|
|
377
|
+
}
|
|
378
|
+
export function schemaLockProblemsOf(lock, current) {
|
|
379
|
+
const problems = [];
|
|
380
|
+
const renamed = new Map;
|
|
381
|
+
for (const [name, entity] of Object.entries(current.entities)) {
|
|
382
|
+
for (const old of entity.previousNames) {
|
|
383
|
+
if (!renamed.has(old))
|
|
384
|
+
renamed.set(old, name);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
for (const name of Object.keys(lock.entities).sort()) {
|
|
388
|
+
const locked = lock.entities[name];
|
|
389
|
+
const now = current.entities[name] === undefined ? renamed.get(name) : name;
|
|
390
|
+
const entity = now === undefined ? undefined : current.entities[now];
|
|
391
|
+
if (entity === undefined) {
|
|
392
|
+
problems.push(`The lock holds the entity "${name}", which no entity is named or lists in previousNames.`);
|
|
393
|
+
} else if (entity.schemaVersion < locked.schemaVersion) {
|
|
394
|
+
problems.push(`The schemaVersion of "${now}" (${entity.schemaVersion}) is lower than the lock's (${locked.schemaVersion}).`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return problems;
|
|
398
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { InvalidCommandDeclaration } from "../command/exceptions.js";
|
|
2
|
+
import {
|
|
3
|
+
isEnvironment,
|
|
4
|
+
loadEnvironments
|
|
5
|
+
} from "../environment/environment.js";
|
|
6
|
+
import { Kind } from "../symbols/kind.js";
|
|
7
|
+
import {
|
|
8
|
+
ApplicationNotStarted,
|
|
9
|
+
InvalidApplicationDeclaration
|
|
10
|
+
} from "./exceptions.js";
|
|
11
|
+
export function providerFrom(environmentDependencies, create) {
|
|
12
|
+
if (typeof environmentDependencies === "function" && create === undefined) {
|
|
13
|
+
return Object.freeze({
|
|
14
|
+
[Kind]: "provider-factory",
|
|
15
|
+
environments: Object.freeze([]),
|
|
16
|
+
create: environmentDependencies,
|
|
17
|
+
withContext: true
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const list = isEnvironment(environmentDependencies) ? [environmentDependencies] : Array.isArray(environmentDependencies) ? [...environmentDependencies] : [null];
|
|
21
|
+
if (!list.every(isEnvironment) || new Set(list).size !== list.length || typeof create !== "function") {
|
|
22
|
+
throw new InvalidApplicationDeclaration({
|
|
23
|
+
source: "providerFrom",
|
|
24
|
+
details: {
|
|
25
|
+
reason: "Expected distinct environments made by environmentOf and a create function."
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
[Kind]: "provider-factory",
|
|
31
|
+
environments: Object.freeze(list),
|
|
32
|
+
create,
|
|
33
|
+
withContext: false
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function logFrom(create) {
|
|
37
|
+
if (typeof create !== "function") {
|
|
38
|
+
throw new InvalidApplicationDeclaration({
|
|
39
|
+
source: "logFrom",
|
|
40
|
+
details: { reason: "Expected logFrom to take a create function." }
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return Object.freeze({ [Kind]: "log-factory", create });
|
|
44
|
+
}
|
|
45
|
+
export function isLogFactory(value) {
|
|
46
|
+
return typeof value === "object" && value !== null && value[Kind] === "log-factory";
|
|
47
|
+
}
|
|
48
|
+
export function isProviderFactory(value) {
|
|
49
|
+
return typeof value === "object" && value !== null && value[Kind] === "provider-factory";
|
|
50
|
+
}
|
|
51
|
+
const deferred = new WeakMap;
|
|
52
|
+
const METHODS = [
|
|
53
|
+
"findOne",
|
|
54
|
+
"findMany",
|
|
55
|
+
"insert",
|
|
56
|
+
"update",
|
|
57
|
+
"remove",
|
|
58
|
+
"count"
|
|
59
|
+
];
|
|
60
|
+
export function deferredProvider(factory, application) {
|
|
61
|
+
const state = {
|
|
62
|
+
factory,
|
|
63
|
+
application,
|
|
64
|
+
provider: undefined,
|
|
65
|
+
starting: undefined
|
|
66
|
+
};
|
|
67
|
+
const real = (method) => {
|
|
68
|
+
const { provider } = state;
|
|
69
|
+
if (!provider) {
|
|
70
|
+
throw new ApplicationNotStarted({
|
|
71
|
+
source: application,
|
|
72
|
+
details: { application }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (typeof provider[method] !== "function") {
|
|
76
|
+
throw new InvalidApplicationDeclaration({
|
|
77
|
+
source: application,
|
|
78
|
+
details: {
|
|
79
|
+
reason: `The provider made on start has no "${method}".`
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return provider;
|
|
84
|
+
};
|
|
85
|
+
const proxy = {};
|
|
86
|
+
for (const method of [...METHODS, "increment"]) {
|
|
87
|
+
proxy[method] = (...args) => real(method)[method]?.(...args);
|
|
88
|
+
}
|
|
89
|
+
proxy.transaction = (run) => real("transaction").transaction(run);
|
|
90
|
+
proxy.migrate = async (...args) => {
|
|
91
|
+
const { provider } = state;
|
|
92
|
+
if (!provider)
|
|
93
|
+
real("migrate");
|
|
94
|
+
return provider?.migrate?.(...args);
|
|
95
|
+
};
|
|
96
|
+
Object.defineProperty(proxy, "capabilities", {
|
|
97
|
+
get: () => state.provider?.capabilities,
|
|
98
|
+
enumerable: true
|
|
99
|
+
});
|
|
100
|
+
Object.freeze(proxy);
|
|
101
|
+
deferred.set(proxy, state);
|
|
102
|
+
return proxy;
|
|
103
|
+
}
|
|
104
|
+
export function isDeferredProvider(provider) {
|
|
105
|
+
return typeof provider === "object" && provider !== null && deferred.has(provider);
|
|
106
|
+
}
|
|
107
|
+
export function deferredEnvironments(provider) {
|
|
108
|
+
return (typeof provider === "object" && provider !== null ? deferred.get(provider)?.factory.environments : undefined) ?? [];
|
|
109
|
+
}
|
|
110
|
+
export function startDeferred(provider, context) {
|
|
111
|
+
const state = deferred.get(provider);
|
|
112
|
+
if (!state)
|
|
113
|
+
return;
|
|
114
|
+
state.starting ??= (async () => {
|
|
115
|
+
const made = await state.factory.create(state.factory.withContext ? context : loadEnvironments(state.factory.environments));
|
|
116
|
+
if (typeof made !== "object" || made === null) {
|
|
117
|
+
state.starting = undefined;
|
|
118
|
+
throw new InvalidApplicationDeclaration({
|
|
119
|
+
source: state.application,
|
|
120
|
+
details: {
|
|
121
|
+
reason: "Expected providerFrom's create to give a repository provider."
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
state.provider = made;
|
|
126
|
+
return made;
|
|
127
|
+
})();
|
|
128
|
+
return state.starting;
|
|
129
|
+
}
|
|
130
|
+
export async function stopProvider(provider) {
|
|
131
|
+
const state = deferred.get(provider);
|
|
132
|
+
if (!state) {
|
|
133
|
+
await provider.close?.();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const made = state.provider;
|
|
137
|
+
state.provider = undefined;
|
|
138
|
+
state.starting = undefined;
|
|
139
|
+
await made?.close?.();
|
|
140
|
+
}
|
|
141
|
+
export function checkTransactions(source, provider, commands) {
|
|
142
|
+
if (commands.length === 0)
|
|
143
|
+
return;
|
|
144
|
+
if (typeof provider.transaction !== "function" || provider.capabilities?.transactions === false) {
|
|
145
|
+
throw new InvalidCommandDeclaration({
|
|
146
|
+
source,
|
|
147
|
+
details: {
|
|
148
|
+
reason: `The provider made on start does not support transactions, which ${commands.join(", ")} declare.`
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export function checkCapabilities(source, provider, repositories) {
|
|
154
|
+
if (repositories.length === 0 || provider.capabilities?.keyset === true)
|
|
155
|
+
return;
|
|
156
|
+
throw new InvalidApplicationDeclaration({
|
|
157
|
+
source,
|
|
158
|
+
details: {
|
|
159
|
+
reason: `The provider made on start does not read by cursor (capabilities.keyset), which the findCursor of ${repositories.join(", ")} needs.`
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
@@ -9,8 +9,12 @@ import type { AnyOutput, StageDescription } from "../../output/types/output.inte
|
|
|
9
9
|
import type { RecordPluginDescriptor } from "../../record/types/plugin.interface.js";
|
|
10
10
|
import type { ProviderDescription, RepositoryIndex, RepositoryScans } from "../../repository/types/query.interface.js";
|
|
11
11
|
import type { AnyRepository, IRepositoryProvider } from "../../repository/types/repository.interface.js";
|
|
12
|
+
import type { SchemaLock, SchemaMigrationOptions, SchemaMigrationReport } from "../../repository/types/schema.interface.js";
|
|
12
13
|
import type { Kind } from "../../symbols/kind.js";
|
|
14
|
+
import type { ITracer } from "../../tracing/tracing.js";
|
|
13
15
|
import type { UseCaseStepDescription } from "../../use-case/steps/types/steps.interface.js";
|
|
16
|
+
import type { AccessDeclaration } from "../../use-case/types/access.interface.js";
|
|
17
|
+
import type { RouteDeclaration } from "../../use-case/types/route.interface.js";
|
|
14
18
|
import type { AnyExceptionClass, AnyUseCaseClass, UseCaseInput, UseCaseResponse } from "../../use-case/use-case.js";
|
|
15
19
|
import type { ProviderSlot, ProviderStorage } from "../providers.js";
|
|
16
20
|
import type { LogFactory, ProviderFactory } from "../start.js";
|
|
@@ -98,6 +102,14 @@ export interface ApplicationSettings<Name extends string, Extended extends reado
|
|
|
98
102
|
* it, the one of the last extended application that has one; none by default.
|
|
99
103
|
*/
|
|
100
104
|
readonly log?: LogSettings | LogFactory;
|
|
105
|
+
/**
|
|
106
|
+
* What traces it: a span per command operation, use case and repository
|
|
107
|
+
* call made through a use case, nested as the calls are, with names and
|
|
108
|
+
* outcomes and never a value (see `docs/tracing.md`). `otelTracer()` of
|
|
109
|
+
* `@afora/otel` gives one for OpenTelemetry. Without it, the one of the
|
|
110
|
+
* last extended application that has one; none by default.
|
|
111
|
+
*/
|
|
112
|
+
readonly trace?: ITracer;
|
|
101
113
|
/**
|
|
102
114
|
* Where event policies such as `publishEvents` publish, by key: a
|
|
103
115
|
* WebSocket hub, a queue, a broker. Commands of the application find them
|
|
@@ -206,6 +218,17 @@ export interface IApplication<Name extends string, Collections extends Applicati
|
|
|
206
218
|
* started application of the process.
|
|
207
219
|
*/
|
|
208
220
|
stop(): Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Brings the stored rows of its entities up to their current shape,
|
|
223
|
+
* walking each provider directly: in `check` it only reports, in `apply`
|
|
224
|
+
* it gives each provider the plan of its entity (`provider.migrate`, the
|
|
225
|
+
* changes from `lock`), then rewrites the rows below the current
|
|
226
|
+
* `schemaVersion` or with a `fill` or `renamedFrom` pending, and moves the
|
|
227
|
+
* rows stored under `previousNames`. Keys no longer declared, and the
|
|
228
|
+
* rows of the previous names, are only removed with `destructive: true`.
|
|
229
|
+
* Idempotent and resumable.
|
|
230
|
+
*/
|
|
231
|
+
migrate(options: SchemaMigrationOptions): Promise<SchemaMigrationReport>;
|
|
209
232
|
/** The variables of an environment registered in the application. */
|
|
210
233
|
get<Environment extends AnyEnvironment>(environment: Environment): EnvironmentValue<Environment>;
|
|
211
234
|
/** The bound version of a command registered in the application. */
|
|
@@ -225,12 +248,25 @@ export interface IApplication<Name extends string, Collections extends Applicati
|
|
|
225
248
|
export interface ApplicationStartOptions {
|
|
226
249
|
/**
|
|
227
250
|
* Become the started application of the process (what `useApplication()`
|
|
228
|
-
* gives).
|
|
229
|
-
*
|
|
251
|
+
* gives). Another one already started throws `ApplicationAlreadyStarted`.
|
|
252
|
+
*
|
|
253
|
+
* @default true
|
|
230
254
|
*/
|
|
231
255
|
readonly register?: boolean;
|
|
232
|
-
/**
|
|
256
|
+
/**
|
|
257
|
+
* Stop the started application of the process and take its place, e.g. on hot
|
|
258
|
+
* reload.
|
|
259
|
+
*
|
|
260
|
+
* @default false
|
|
261
|
+
*/
|
|
233
262
|
readonly replace?: boolean;
|
|
263
|
+
/**
|
|
264
|
+
* The committed schema lock (`afora.lock.json`): start throws
|
|
265
|
+
* `SchemaLockMismatch` when an entity of the lock is no longer named, and
|
|
266
|
+
* no entity lists it in `previousNames`, or when the `schemaVersion` of
|
|
267
|
+
* an entity is lower than the lock's.
|
|
268
|
+
*/
|
|
269
|
+
readonly lock?: SchemaLock;
|
|
234
270
|
}
|
|
235
271
|
/** Any application. */
|
|
236
272
|
export type AnyApplication = IApplication<string, any>;
|
|
@@ -290,7 +326,14 @@ export type FieldDescription = {
|
|
|
290
326
|
readonly kind: "record" | "entity";
|
|
291
327
|
readonly ref: string;
|
|
292
328
|
readonly optional: boolean;
|
|
329
|
+
} | {
|
|
330
|
+
/** One of several blueprints, told apart by the value of `key`, the name of the variant. */
|
|
331
|
+
readonly kind: "variant";
|
|
332
|
+
readonly key: string;
|
|
333
|
+
readonly optional: boolean;
|
|
334
|
+
readonly variants: Readonly<Record<string, FieldDescription>>;
|
|
293
335
|
};
|
|
336
|
+
/** The fields of a blueprint, record or entity in a description, by name. */
|
|
294
337
|
export type FieldsDescription = Readonly<Record<string, FieldDescription>>;
|
|
295
338
|
/** A record or an entity: its fields (an entity's identity and its plugins' keys left out), options and plugins. */
|
|
296
339
|
export interface RecordDescription {
|
|
@@ -318,6 +361,12 @@ export interface RecordDescription {
|
|
|
318
361
|
* `fields`) or `{ at: key }`. Present only when it has one.
|
|
319
362
|
*/
|
|
320
363
|
readonly expires?: EntityExpires;
|
|
364
|
+
/** The `schemaVersion` of an entity; present only when declared. */
|
|
365
|
+
readonly schemaVersion?: number;
|
|
366
|
+
/** The versions the `upgrade` of an entity has a step to, ascending; present only when it has some. */
|
|
367
|
+
readonly upgrade?: readonly number[];
|
|
368
|
+
/** The `previousNames` of an entity; present only when it has some. */
|
|
369
|
+
readonly previousNames?: readonly string[];
|
|
321
370
|
}
|
|
322
371
|
/** What a use case depends on, by the name it has in the application. */
|
|
323
372
|
export interface UseCaseDependencyDescription {
|
|
@@ -333,6 +382,10 @@ export interface UseCaseDescription {
|
|
|
333
382
|
readonly description?: string;
|
|
334
383
|
readonly tags: readonly string[];
|
|
335
384
|
readonly deprecated: boolean;
|
|
385
|
+
/** Where it is served over HTTP, from the `route` setting or `@Route`. */
|
|
386
|
+
readonly route?: RouteDeclaration;
|
|
387
|
+
/** Who may call it, from the `access` setting or `@Access`. */
|
|
388
|
+
readonly access?: AccessDeclaration;
|
|
336
389
|
readonly input: FieldsDescription;
|
|
337
390
|
readonly response: FieldsDescription;
|
|
338
391
|
readonly context?: FieldsDescription;
|
|
@@ -448,6 +501,10 @@ export interface ApplicationDescription {
|
|
|
448
501
|
readonly log?: LogDescription | {
|
|
449
502
|
readonly kind: "opaque";
|
|
450
503
|
};
|
|
504
|
+
/** Present, and opaque, only when it has a `trace` (its own or inherited). */
|
|
505
|
+
readonly trace?: {
|
|
506
|
+
readonly kind: "opaque";
|
|
507
|
+
};
|
|
451
508
|
/** Its publishers (inherited ones included), by key, with the application that declared each; present only when it has some. */
|
|
452
509
|
readonly publishers?: Readonly<Record<string, {
|
|
453
510
|
readonly from: string;
|