@afora/core 0.0.9 → 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 +52 -30
- 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 +10 -3
- package/dist/index-3aae5jfq.js +0 -2997
- package/dist/index-ny3as79r.js +0 -4567
- package/dist/index-xegv6a1p.js +0 -4291
|
@@ -0,0 +1,1291 @@
|
|
|
1
|
+
import { fieldOf } from "../blueprint/callable.js";
|
|
2
|
+
import { isBlueprint } from "../blueprint/is-blueprint.js";
|
|
3
|
+
import { isBlueprintList } from "../blueprint/list.js";
|
|
4
|
+
import { isRecordClass, recordFieldOf } from "../blueprint/record-field.js";
|
|
5
|
+
import { isBlueprintVariant } from "../blueprint/variant.js";
|
|
6
|
+
import {
|
|
7
|
+
commandOf,
|
|
8
|
+
commandTransactionProvider,
|
|
9
|
+
isCommand,
|
|
10
|
+
verifyCommand
|
|
11
|
+
} from "../command/command.js";
|
|
12
|
+
import {
|
|
13
|
+
EntitySettings,
|
|
14
|
+
identityKeysOf,
|
|
15
|
+
isVersionedEntityClass
|
|
16
|
+
} from "../entity/abstract-entity.js";
|
|
17
|
+
import { isEntityClass } from "../entity/entity.js";
|
|
18
|
+
import { eventHasHandler, lifecycleEventsOf } from "../entity/event.js";
|
|
19
|
+
import {
|
|
20
|
+
InvalidEnvironment,
|
|
21
|
+
isEnvironment
|
|
22
|
+
} from "../environment/environment.js";
|
|
23
|
+
import {
|
|
24
|
+
eventPolicyDescriptionOf,
|
|
25
|
+
eventPolicyPublishersOf
|
|
26
|
+
} from "../event-policy/event-policy.js";
|
|
27
|
+
import {
|
|
28
|
+
ApplicationPublishersKey,
|
|
29
|
+
bindPublishers,
|
|
30
|
+
isEventPublisher
|
|
31
|
+
} from "../event-policy/publishers.js";
|
|
32
|
+
import {
|
|
33
|
+
declaredMessageOf,
|
|
34
|
+
Exception,
|
|
35
|
+
isDeclaredException
|
|
36
|
+
} from "../exception/exception.js";
|
|
37
|
+
import { isExportedException } from "../exception/exported.js";
|
|
38
|
+
import {
|
|
39
|
+
ApplicationLoggerKey,
|
|
40
|
+
applicationLogger,
|
|
41
|
+
checkLogSettings,
|
|
42
|
+
isLoggerDeclaration,
|
|
43
|
+
logProviderNameOf,
|
|
44
|
+
silentLogger
|
|
45
|
+
} from "../logger/logger.js";
|
|
46
|
+
import { plainOf } from "../openapi/openapi.js";
|
|
47
|
+
import { isOutput, outputOf } from "../output/output.js";
|
|
48
|
+
import { stageDescriptionOf } from "../output/stage.js";
|
|
49
|
+
import { isProperty } from "../property/is-property.js";
|
|
50
|
+
import { propertyLineageOf } from "../property/lineage.js";
|
|
51
|
+
import { declaredRecordNameOf, recordNameOf } from "../record/name.js";
|
|
52
|
+
import { pluginsOf } from "../record/plugin.js";
|
|
53
|
+
import { providerDescriptionOf } from "../repository/provider.js";
|
|
54
|
+
import { isRepository } from "../repository/repository.js";
|
|
55
|
+
import {
|
|
56
|
+
bindRepository,
|
|
57
|
+
inspectRepository,
|
|
58
|
+
providerOfRepository
|
|
59
|
+
} from "../repository/state.js";
|
|
60
|
+
import { rawTypeOf } from "../schema/schema.js";
|
|
61
|
+
import { Kind } from "../symbols/kind.js";
|
|
62
|
+
import {
|
|
63
|
+
ApplicationTracerKey,
|
|
64
|
+
checkTracer
|
|
65
|
+
} from "../tracing/tracing.js";
|
|
66
|
+
import { useCaseOriginOf } from "../use-case/plugin.js";
|
|
67
|
+
import { describeSteps } from "../use-case/steps/describe.js";
|
|
68
|
+
import { interpretedStepsOf } from "../use-case/steps/run.js";
|
|
69
|
+
import { isUseCaseClass } from "../use-case/use-case.js";
|
|
70
|
+
import {
|
|
71
|
+
ApplicationAlreadyStarted,
|
|
72
|
+
ApplicationConflict,
|
|
73
|
+
DuplicateApplicationDeclaration,
|
|
74
|
+
InvalidApplicationDeclaration,
|
|
75
|
+
InvalidApplicationEnvironment,
|
|
76
|
+
SchemaLockMismatch,
|
|
77
|
+
UnregisteredApplicationReference,
|
|
78
|
+
UnregisteredRepositoryDependency,
|
|
79
|
+
UnregisteredUseCaseDependency
|
|
80
|
+
} from "./exceptions.js";
|
|
81
|
+
import { isLock, migrateApplication } from "./migrate.js";
|
|
82
|
+
import {
|
|
83
|
+
isProviderSlot,
|
|
84
|
+
isProviderStorage,
|
|
85
|
+
slotEntry,
|
|
86
|
+
slotsOf
|
|
87
|
+
} from "./providers.js";
|
|
88
|
+
import { setStartedApplication, startedApplication } from "./registry.js";
|
|
89
|
+
import { schemaLockOf, schemaLockProblemsOf } from "./schema.js";
|
|
90
|
+
import {
|
|
91
|
+
checkCapabilities,
|
|
92
|
+
checkTransactions,
|
|
93
|
+
deferredEnvironments,
|
|
94
|
+
deferredProvider,
|
|
95
|
+
isDeferredProvider,
|
|
96
|
+
isLogFactory,
|
|
97
|
+
isProviderFactory,
|
|
98
|
+
startDeferred,
|
|
99
|
+
stopProvider
|
|
100
|
+
} from "./start.js";
|
|
101
|
+
const KEYS = [
|
|
102
|
+
"properties",
|
|
103
|
+
"blueprints",
|
|
104
|
+
"records",
|
|
105
|
+
"entities",
|
|
106
|
+
"environments",
|
|
107
|
+
"repositories",
|
|
108
|
+
"useCases",
|
|
109
|
+
"commands",
|
|
110
|
+
"outputs",
|
|
111
|
+
"exceptions"
|
|
112
|
+
];
|
|
113
|
+
const internals = new WeakMap;
|
|
114
|
+
const logSettings = new WeakMap;
|
|
115
|
+
const traceSettings = new WeakMap;
|
|
116
|
+
function inherited(extended, settings) {
|
|
117
|
+
for (let index = extended.length - 1;index >= 0; index--) {
|
|
118
|
+
const value = settings.get(extended[index]);
|
|
119
|
+
if (value !== undefined)
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const namedProviders = new WeakMap;
|
|
125
|
+
const defaultSlots = new WeakMap;
|
|
126
|
+
function referenceOf(slot) {
|
|
127
|
+
return { kind: "ref", key: slotEntry(slot).key };
|
|
128
|
+
}
|
|
129
|
+
const KIND_OF = {
|
|
130
|
+
properties: "property",
|
|
131
|
+
blueprints: "blueprint",
|
|
132
|
+
records: "record",
|
|
133
|
+
entities: "entity",
|
|
134
|
+
environments: "environment",
|
|
135
|
+
repositories: "repository",
|
|
136
|
+
useCases: "use case",
|
|
137
|
+
commands: "command",
|
|
138
|
+
outputs: "output",
|
|
139
|
+
exceptions: "exception"
|
|
140
|
+
};
|
|
141
|
+
export function isApplication(value) {
|
|
142
|
+
return typeof value === "object" && value !== null && value[Kind] === "application";
|
|
143
|
+
}
|
|
144
|
+
function invalid(source, reason) {
|
|
145
|
+
throw new InvalidApplicationDeclaration({ source, details: { reason } });
|
|
146
|
+
}
|
|
147
|
+
const publisherOrigins = new WeakMap;
|
|
148
|
+
function publishersOf(name, declared, extended) {
|
|
149
|
+
const merged = {};
|
|
150
|
+
const origins = {};
|
|
151
|
+
for (const other of extended) {
|
|
152
|
+
Object.assign(merged, other[ApplicationPublishersKey]);
|
|
153
|
+
Object.assign(origins, publisherOrigins.get(other));
|
|
154
|
+
}
|
|
155
|
+
if (declared !== undefined) {
|
|
156
|
+
if (typeof declared !== "object" || declared === null || Array.isArray(declared)) {
|
|
157
|
+
invalid(name, "Expected `publishers` to be an object of publishers by key.");
|
|
158
|
+
}
|
|
159
|
+
for (const [key, publisher] of Object.entries(declared)) {
|
|
160
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(key)) {
|
|
161
|
+
invalid(name, `Expected the publisher key "${key}" to be an identifier.`);
|
|
162
|
+
}
|
|
163
|
+
if (!isEventPublisher(publisher)) {
|
|
164
|
+
invalid(name, `Expected the publisher "${key}" to be an object with a publish(topic, event) method.`);
|
|
165
|
+
}
|
|
166
|
+
merged[key] = publisher;
|
|
167
|
+
origins[key] = name;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { publishers: Object.freeze(merged), origins };
|
|
171
|
+
}
|
|
172
|
+
function isOfKind(collection, value) {
|
|
173
|
+
switch (collection) {
|
|
174
|
+
case "properties":
|
|
175
|
+
return isProperty(value);
|
|
176
|
+
case "blueprints":
|
|
177
|
+
return isBlueprint(value);
|
|
178
|
+
case "records":
|
|
179
|
+
return isRecordClass(value) && !isEntityClass(value);
|
|
180
|
+
case "entities":
|
|
181
|
+
return isEntityClass(value);
|
|
182
|
+
case "environments":
|
|
183
|
+
return isEnvironment(value);
|
|
184
|
+
case "repositories":
|
|
185
|
+
return inspectRepository(value) !== undefined;
|
|
186
|
+
case "useCases":
|
|
187
|
+
return isUseCaseClass(value);
|
|
188
|
+
case "commands":
|
|
189
|
+
return isCommand(value);
|
|
190
|
+
case "outputs":
|
|
191
|
+
return isOutput(value);
|
|
192
|
+
case "exceptions":
|
|
193
|
+
return isDeclaredException(value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function keyOf(collection, value) {
|
|
197
|
+
switch (collection) {
|
|
198
|
+
case "properties":
|
|
199
|
+
return fieldOf(value);
|
|
200
|
+
case "blueprints":
|
|
201
|
+
return value.properties;
|
|
202
|
+
case "repositories":
|
|
203
|
+
return inspectRepository(value).state;
|
|
204
|
+
default:
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function emptyRegistry() {
|
|
209
|
+
return Object.fromEntries(KEYS.map((key) => [key, new Map]));
|
|
210
|
+
}
|
|
211
|
+
function resolve(settings) {
|
|
212
|
+
const registry = emptyRegistry();
|
|
213
|
+
const byKey = new Map;
|
|
214
|
+
const source = settings.name;
|
|
215
|
+
const add = (entry) => {
|
|
216
|
+
const same = registry[entry.collection].get(entry.name);
|
|
217
|
+
if (same) {
|
|
218
|
+
if (same.key === entry.key)
|
|
219
|
+
return;
|
|
220
|
+
conflict(`${KIND_OF[entry.collection]} "${entry.name}" is declared as different objects by "${same.from}" and by "${entry.from}".`, same, entry);
|
|
221
|
+
}
|
|
222
|
+
const other = byKey.get(entry.key);
|
|
223
|
+
if (other) {
|
|
224
|
+
if (other.from === entry.from) {
|
|
225
|
+
throw new DuplicateApplicationDeclaration({
|
|
226
|
+
source,
|
|
227
|
+
details: {
|
|
228
|
+
first: `${KIND_OF[other.collection]} "${other.name}"`,
|
|
229
|
+
second: `${KIND_OF[entry.collection]} "${entry.name}"`
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
conflict(`The same ${KIND_OF[entry.collection]} is ${KIND_OF[other.collection]} "${other.name}" in "${other.from}" and ${KIND_OF[entry.collection]} "${entry.name}" in "${entry.from}".`, other, entry);
|
|
234
|
+
}
|
|
235
|
+
registry[entry.collection].set(entry.name, entry);
|
|
236
|
+
byKey.set(entry.key, entry);
|
|
237
|
+
};
|
|
238
|
+
const conflict = (reason, first, second) => {
|
|
239
|
+
throw new ApplicationConflict({
|
|
240
|
+
source,
|
|
241
|
+
details: {
|
|
242
|
+
reason,
|
|
243
|
+
origins: [first.from, second.from],
|
|
244
|
+
declarations: [
|
|
245
|
+
`${KIND_OF[first.collection]} ${first.name}`,
|
|
246
|
+
`${KIND_OF[second.collection]} ${second.name}`
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
for (const other of settings.extends ?? []) {
|
|
252
|
+
const resolved = internals.get(other);
|
|
253
|
+
if (!resolved)
|
|
254
|
+
invalid(source, "Expected `extends` to hold applications.");
|
|
255
|
+
for (const key of KEYS) {
|
|
256
|
+
for (const entry of resolved[key].values())
|
|
257
|
+
add(entry);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const collection of KEYS) {
|
|
261
|
+
const declared = settings[collection];
|
|
262
|
+
if (declared === undefined)
|
|
263
|
+
continue;
|
|
264
|
+
if (typeof declared !== "object" || declared === null || Array.isArray(declared)) {
|
|
265
|
+
invalid(source, `Expected ${collection} to be an object of declarations.`);
|
|
266
|
+
}
|
|
267
|
+
for (const [name, value] of Object.entries(declared)) {
|
|
268
|
+
if (!isOfKind(collection, value)) {
|
|
269
|
+
invalid(source, `${collection}.${name} is not a ${KIND_OF[collection]} (${collection} takes ${KIND_OF[collection]} declarations).`);
|
|
270
|
+
}
|
|
271
|
+
const declaredName = collection === "useCases" ? value.useCaseName : collection === "commands" ? value.commandName : collection === "outputs" ? value.outputName : collection === "exceptions" ? value.name : collection === "records" || collection === "entities" ? declaredRecordNameOf(value) ?? name : name;
|
|
272
|
+
if (declaredName !== name) {
|
|
273
|
+
invalid(source, `${collection}.${name} must be keyed by its name, "${declaredName}".`);
|
|
274
|
+
}
|
|
275
|
+
add({
|
|
276
|
+
collection,
|
|
277
|
+
name,
|
|
278
|
+
value,
|
|
279
|
+
key: keyOf(collection, value),
|
|
280
|
+
from: source
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
for (const entry of [...registry.commands.values()]) {
|
|
285
|
+
for (const UseCase of Object.values(entry.value.useCases)) {
|
|
286
|
+
if (useCaseOriginOf(UseCase) === undefined || byKey.has(UseCase)) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
add({
|
|
290
|
+
collection: "useCases",
|
|
291
|
+
name: UseCase.useCaseName,
|
|
292
|
+
value: UseCase,
|
|
293
|
+
key: UseCase,
|
|
294
|
+
from: entry.from
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return registry;
|
|
299
|
+
}
|
|
300
|
+
function recordsIn(properties) {
|
|
301
|
+
const found = new Set;
|
|
302
|
+
const visit = (field) => {
|
|
303
|
+
if (isBlueprintList(field))
|
|
304
|
+
visit(field.item);
|
|
305
|
+
else if (isBlueprint(field))
|
|
306
|
+
walk(field.properties);
|
|
307
|
+
else if (isBlueprintVariant(field)) {
|
|
308
|
+
for (const variant of Object.values(field.variants)) {
|
|
309
|
+
visit(variant);
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
const record = recordFieldOf(field)?.record;
|
|
313
|
+
if (record)
|
|
314
|
+
found.add(record);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
const walk = (collection) => {
|
|
318
|
+
for (const field of Object.values(collection))
|
|
319
|
+
visit(field);
|
|
320
|
+
};
|
|
321
|
+
walk(properties);
|
|
322
|
+
return found;
|
|
323
|
+
}
|
|
324
|
+
function validate(source, registry) {
|
|
325
|
+
const registered = new Set([
|
|
326
|
+
...[...registry.records.values()].map((entry) => entry.key),
|
|
327
|
+
...[...registry.entities.values()].map((entry) => entry.key)
|
|
328
|
+
]);
|
|
329
|
+
const holders = [
|
|
330
|
+
["blueprints", "properties"],
|
|
331
|
+
["records", "blueprint.properties"],
|
|
332
|
+
["entities", "blueprint.properties"]
|
|
333
|
+
];
|
|
334
|
+
for (const [collection] of holders) {
|
|
335
|
+
for (const entry of registry[collection].values()) {
|
|
336
|
+
const properties = collection === "blueprints" ? entry.value.properties : entry.value.blueprint.properties;
|
|
337
|
+
for (const record of recordsIn(properties)) {
|
|
338
|
+
if (registered.has(record))
|
|
339
|
+
continue;
|
|
340
|
+
throw new UnregisteredApplicationReference({
|
|
341
|
+
source,
|
|
342
|
+
details: {
|
|
343
|
+
holder: `${KIND_OF[collection]} "${entry.name}"`,
|
|
344
|
+
reference: `${isEntityClass(record) ? "entity" : "record"} "${recordNameOf(record)}"`
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const repositories = new Set([...registry.repositories.values()].map((entry) => entry.key));
|
|
351
|
+
const environments = new Set([...registry.environments.values()].map((entry) => entry.key));
|
|
352
|
+
const unregistered = (repository, dependency) => {
|
|
353
|
+
throw new UnregisteredRepositoryDependency({
|
|
354
|
+
source,
|
|
355
|
+
details: { repository, dependency }
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
for (const entry of registry.repositories.values()) {
|
|
359
|
+
const info = inspectRepository(entry.value);
|
|
360
|
+
if (!registered.has(info.entity)) {
|
|
361
|
+
unregistered(entry.name, `entity "${info.entity.name}"`);
|
|
362
|
+
}
|
|
363
|
+
for (const relation of info.relations) {
|
|
364
|
+
if (!repositories.has(relation)) {
|
|
365
|
+
unregistered(entry.name, `the repository of "${recordNameOf(relation.entity)}"`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
for (const environment of info.environments) {
|
|
369
|
+
if (!environments.has(environment)) {
|
|
370
|
+
unregistered(entry.name, "an environment");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function validateCommands(source, registry) {
|
|
376
|
+
const useCases = new Set([...registry.useCases.values()].map((entry) => entry.key));
|
|
377
|
+
const environments = new Set([...registry.environments.values()].map((entry) => entry.key));
|
|
378
|
+
for (const entry of registry.commands.values()) {
|
|
379
|
+
const command = entry.value;
|
|
380
|
+
for (const environment of command.environments) {
|
|
381
|
+
if (environments.has(environment))
|
|
382
|
+
continue;
|
|
383
|
+
throw new UnregisteredApplicationReference({
|
|
384
|
+
source,
|
|
385
|
+
details: {
|
|
386
|
+
holder: `command "${entry.name}"`,
|
|
387
|
+
reference: "an environment",
|
|
388
|
+
command: entry.name
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
for (const UseCase of Object.values(command.useCases)) {
|
|
393
|
+
if (useCases.has(UseCase))
|
|
394
|
+
continue;
|
|
395
|
+
throw new UnregisteredApplicationReference({
|
|
396
|
+
source,
|
|
397
|
+
details: {
|
|
398
|
+
holder: `command "${entry.name}"`,
|
|
399
|
+
reference: `use case "${UseCase.useCaseName}"`,
|
|
400
|
+
command: entry.name,
|
|
401
|
+
useCase: UseCase.useCaseName
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function validateOutputs(source, registry) {
|
|
408
|
+
const commands = new Set([...registry.commands.values()].map((entry) => entry.key));
|
|
409
|
+
for (const entry of registry.outputs.values()) {
|
|
410
|
+
const output = entry.value;
|
|
411
|
+
if (!output.outputName) {
|
|
412
|
+
invalid(source, `outputs.${entry.name} has no name (an output made by pipe cannot be registered).`);
|
|
413
|
+
}
|
|
414
|
+
for (const command of output.commands) {
|
|
415
|
+
if (commands.has(command))
|
|
416
|
+
continue;
|
|
417
|
+
throw new UnregisteredApplicationReference({
|
|
418
|
+
source,
|
|
419
|
+
details: {
|
|
420
|
+
holder: `output "${entry.name}"`,
|
|
421
|
+
reference: `command "${command.commandName}"`,
|
|
422
|
+
output: entry.name,
|
|
423
|
+
command: command.commandName
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
function validateUseCases(source, registry) {
|
|
430
|
+
const registered = new Set([
|
|
431
|
+
...[...registry.records.values()].map((entry) => entry.key),
|
|
432
|
+
...[...registry.entities.values()].map((entry) => entry.key)
|
|
433
|
+
]);
|
|
434
|
+
const known = (collection) => new Set([...registry[collection].values()].map((entry) => entry.key));
|
|
435
|
+
const useCases = known("useCases");
|
|
436
|
+
const repositories = known("repositories");
|
|
437
|
+
const environments = known("environments");
|
|
438
|
+
const exceptions = known("exceptions");
|
|
439
|
+
for (const entry of registry.useCases.values()) {
|
|
440
|
+
const UseCase = entry.value;
|
|
441
|
+
for (const [key, dependency] of Object.entries(UseCase.dependencies)) {
|
|
442
|
+
const found = isLoggerDeclaration(dependency) ? true : isUseCaseClass(dependency) ? useCases.has(dependency) : isEnvironment(dependency) ? environments.has(dependency) : repositories.has(inspectRepository(dependency).state);
|
|
443
|
+
if (!found) {
|
|
444
|
+
throw new UnregisteredUseCaseDependency({
|
|
445
|
+
source,
|
|
446
|
+
details: { useCase: `"${entry.name}"`, dependency: `"${key}"` }
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
for (const [part, blueprint] of [
|
|
451
|
+
["blueprint", UseCase.blueprint],
|
|
452
|
+
["response", UseCase.response],
|
|
453
|
+
["context", UseCase.context]
|
|
454
|
+
]) {
|
|
455
|
+
if (blueprint === undefined)
|
|
456
|
+
continue;
|
|
457
|
+
for (const record of recordsIn(blueprint.properties)) {
|
|
458
|
+
if (registered.has(record))
|
|
459
|
+
continue;
|
|
460
|
+
throw new UnregisteredApplicationReference({
|
|
461
|
+
source,
|
|
462
|
+
details: {
|
|
463
|
+
holder: `the ${part} of use case "${entry.name}"`,
|
|
464
|
+
reference: `${isEntityClass(record) ? "entity" : "record"} "${recordNameOf(record)}"`
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
for (const Thrown of UseCase.errors) {
|
|
470
|
+
if (exceptions.has(Thrown) || isExportedException(Thrown))
|
|
471
|
+
continue;
|
|
472
|
+
throw new UnregisteredApplicationReference({
|
|
473
|
+
source,
|
|
474
|
+
details: {
|
|
475
|
+
holder: `use case "${entry.name}"`,
|
|
476
|
+
reference: `exception "${Thrown.name}"`
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
for (const [index, step] of (interpretedStepsOf(UseCase) ?? []).entries()) {
|
|
481
|
+
const holder = `step ${index} of use case "${entry.name}"`;
|
|
482
|
+
if (step.build !== undefined && !registered.has(step.build)) {
|
|
483
|
+
throw new UnregisteredApplicationReference({
|
|
484
|
+
source,
|
|
485
|
+
details: {
|
|
486
|
+
holder,
|
|
487
|
+
reference: `${isEntityClass(step.build) ? "entity" : "record"} "${recordNameOf(step.build)}"`
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
const Thrown = step.error;
|
|
492
|
+
if (Thrown !== undefined && !exceptions.has(Thrown) && !isExportedException(Thrown)) {
|
|
493
|
+
throw new UnregisteredApplicationReference({
|
|
494
|
+
source,
|
|
495
|
+
details: { holder, reference: `exception "${Thrown.name}"` }
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function jsonOf(value) {
|
|
502
|
+
return JSON.parse(JSON.stringify(value, (_key, item) => item instanceof RegExp ? item.source : item));
|
|
503
|
+
}
|
|
504
|
+
function referencesOf(registry, collection) {
|
|
505
|
+
return new Map([...registry[collection].values()].map((entry) => [entry.key, entry.name]));
|
|
506
|
+
}
|
|
507
|
+
function propertyReferenceOf(Property, scope) {
|
|
508
|
+
const ref = scope.properties.get(fieldOf(Property));
|
|
509
|
+
return ref === undefined ? propertyDescriptionOf(Property, scope) : { ref };
|
|
510
|
+
}
|
|
511
|
+
function lineageDescriptionOf(lineage, scope) {
|
|
512
|
+
switch (lineage.kind) {
|
|
513
|
+
case "refined":
|
|
514
|
+
return {
|
|
515
|
+
kind: "refined",
|
|
516
|
+
base: propertyReferenceOf(lineage.base, scope),
|
|
517
|
+
options: jsonOf(lineage.options)
|
|
518
|
+
};
|
|
519
|
+
case "optional":
|
|
520
|
+
return {
|
|
521
|
+
kind: "optional",
|
|
522
|
+
base: propertyReferenceOf(lineage.base, scope)
|
|
523
|
+
};
|
|
524
|
+
case "factory":
|
|
525
|
+
return {
|
|
526
|
+
kind: "factory",
|
|
527
|
+
factory: lineage.factory,
|
|
528
|
+
args: jsonOf(lineage.args)
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
function propertyDescriptionOf(Property, scope) {
|
|
533
|
+
const { type } = Property;
|
|
534
|
+
const lineage = propertyLineageOf(Property);
|
|
535
|
+
return {
|
|
536
|
+
name: type.name,
|
|
537
|
+
optional: type.optional === true,
|
|
538
|
+
sensitive: type.sensitive === true,
|
|
539
|
+
unique: type.unique === true,
|
|
540
|
+
schema: plainOf(rawTypeOf(type.schema)),
|
|
541
|
+
...lineage ? { lineage: lineageDescriptionOf(lineage, scope) } : {}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function fieldDescriptionOf(field, scope) {
|
|
545
|
+
if (isProperty(field)) {
|
|
546
|
+
const real = fieldOf(field);
|
|
547
|
+
const ref = scope.properties.get(real);
|
|
548
|
+
return ref === undefined ? { kind: "property", ...propertyDescriptionOf(real, scope) } : { kind: "property", ref, optional: real.type.optional === true };
|
|
549
|
+
}
|
|
550
|
+
if (isBlueprintList(field)) {
|
|
551
|
+
return {
|
|
552
|
+
kind: "list",
|
|
553
|
+
item: fieldDescriptionOf(field.item, scope),
|
|
554
|
+
optional: field.optional,
|
|
555
|
+
...field.minItems === undefined ? {} : { minItems: field.minItems },
|
|
556
|
+
...field.maxItems === undefined ? {} : { maxItems: field.maxItems }
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
if (isBlueprint(field)) {
|
|
560
|
+
const ref = scope.blueprints.get(field.properties);
|
|
561
|
+
return ref === undefined ? {
|
|
562
|
+
kind: "blueprint",
|
|
563
|
+
optional: field.optional,
|
|
564
|
+
fields: fieldsDescriptionOf(field.properties, scope)
|
|
565
|
+
} : { kind: "blueprint", ref, optional: field.optional };
|
|
566
|
+
}
|
|
567
|
+
if (isBlueprintVariant(field)) {
|
|
568
|
+
const variants = field.variants;
|
|
569
|
+
return {
|
|
570
|
+
kind: "variant",
|
|
571
|
+
key: field.key,
|
|
572
|
+
optional: field.optional,
|
|
573
|
+
variants: Object.fromEntries(Object.entries(variants).map(([tag, variant]) => [
|
|
574
|
+
tag,
|
|
575
|
+
fieldDescriptionOf(variant, scope)
|
|
576
|
+
]))
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
const record = recordFieldOf(field);
|
|
580
|
+
if (record) {
|
|
581
|
+
const entity = scope.entities.get(record.record);
|
|
582
|
+
const ref = entity ?? scope.records.get(record.record);
|
|
583
|
+
return {
|
|
584
|
+
kind: entity === undefined ? "record" : "entity",
|
|
585
|
+
ref: ref ?? recordNameOf(record.record),
|
|
586
|
+
optional: record.optional
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
kind: "property",
|
|
591
|
+
name: "unknown",
|
|
592
|
+
optional: false,
|
|
593
|
+
sensitive: false,
|
|
594
|
+
unique: false,
|
|
595
|
+
schema: {}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function fieldsDescriptionOf(properties, scope, skip = new Set) {
|
|
599
|
+
return Object.fromEntries(Object.entries(properties).filter(([key]) => !skip.has(key)).map(([key, field]) => [key, fieldDescriptionOf(field, scope)]));
|
|
600
|
+
}
|
|
601
|
+
function recordDescriptionOf(entry, scope) {
|
|
602
|
+
const Record = entry.value;
|
|
603
|
+
const plugins = pluginsOf(Record).map((plugin) => plugin.toJSON());
|
|
604
|
+
const skip = new Set(plugins.flatMap((plugin) => plugin.keys));
|
|
605
|
+
const settings = entry.collection === "entities" ? Record[EntitySettings] : undefined;
|
|
606
|
+
const expires = settings?.expires;
|
|
607
|
+
const upgrade = Object.keys(settings?.upgrade ?? {}).map(Number).sort((a, b) => a - b);
|
|
608
|
+
if (entry.collection === "entities") {
|
|
609
|
+
for (const key of identityKeysOf(Record))
|
|
610
|
+
skip.add(key);
|
|
611
|
+
}
|
|
612
|
+
const own = (keys, flag) => keys.filter((key) => {
|
|
613
|
+
if (skip.has(key))
|
|
614
|
+
return false;
|
|
615
|
+
const field = Record.blueprint.properties[key];
|
|
616
|
+
return !(flag && isProperty(field) && field.type[flag] === true);
|
|
617
|
+
});
|
|
618
|
+
const lifecycle = entry.collection === "entities" ? lifecycleEventsOf(Record) : {};
|
|
619
|
+
const events = Object.fromEntries(Object.entries(lifecycle).map(([cycle, list]) => [
|
|
620
|
+
cycle,
|
|
621
|
+
list.map((event) => ({
|
|
622
|
+
name: event.name,
|
|
623
|
+
handler: eventHasHandler(event)
|
|
624
|
+
}))
|
|
625
|
+
]));
|
|
626
|
+
return {
|
|
627
|
+
from: entry.from,
|
|
628
|
+
fields: fieldsDescriptionOf(Record.blueprint.properties, scope, skip),
|
|
629
|
+
options: {
|
|
630
|
+
sensitive: own(Record.options.sensitive, "sensitive"),
|
|
631
|
+
unique: own(Record.options.unique, "unique"),
|
|
632
|
+
change: own(Record.options.change)
|
|
633
|
+
},
|
|
634
|
+
plugins,
|
|
635
|
+
...Object.keys(events).length > 0 ? { events } : {},
|
|
636
|
+
...expires === undefined ? {} : { expires },
|
|
637
|
+
...entry.collection === "entities" && isVersionedEntityClass(Record) ? { versioned: true } : {},
|
|
638
|
+
...settings?.schemaVersioned ? { schemaVersion: settings.schemaVersion } : {},
|
|
639
|
+
...upgrade.length > 0 ? { upgrade } : {},
|
|
640
|
+
...settings !== undefined && settings.previousNames.length > 0 ? { previousNames: [...settings.previousNames] } : {}
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function describeRegistry(name, extended, registry, provider, named, log, publishers, traced) {
|
|
644
|
+
const scope = {
|
|
645
|
+
properties: referencesOf(registry, "properties"),
|
|
646
|
+
blueprints: referencesOf(registry, "blueprints"),
|
|
647
|
+
records: referencesOf(registry, "records"),
|
|
648
|
+
entities: referencesOf(registry, "entities")
|
|
649
|
+
};
|
|
650
|
+
const repositories = referencesOf(registry, "repositories");
|
|
651
|
+
const environmentRefs = referencesOf(registry, "environments");
|
|
652
|
+
const useCaseRefs = referencesOf(registry, "useCases");
|
|
653
|
+
const exceptionRefs = referencesOf(registry, "exceptions");
|
|
654
|
+
const describeImplementation = (UseCase) => {
|
|
655
|
+
const steps = interpretedStepsOf(UseCase);
|
|
656
|
+
return steps === undefined ? { kind: "opaque" } : {
|
|
657
|
+
kind: "steps",
|
|
658
|
+
steps: describeSteps(steps, {
|
|
659
|
+
record: (Class) => scope.entities.get(Class) ?? scope.records.get(Class),
|
|
660
|
+
error: (Class) => {
|
|
661
|
+
const ref = exceptionRefs.get(Class);
|
|
662
|
+
return ref === undefined ? { core: Class.name } : ref;
|
|
663
|
+
},
|
|
664
|
+
params: (blueprint) => fieldsDescriptionOf(blueprint.properties, scope)
|
|
665
|
+
})
|
|
666
|
+
};
|
|
667
|
+
};
|
|
668
|
+
const describeOption = (value) => {
|
|
669
|
+
if (isLoggerDeclaration(value))
|
|
670
|
+
return "Logger";
|
|
671
|
+
if (isUseCaseClass(value))
|
|
672
|
+
return useCaseRefs.get(value);
|
|
673
|
+
if (isEnvironment(value))
|
|
674
|
+
return environmentRefs.get(value);
|
|
675
|
+
if (isRepository(value)) {
|
|
676
|
+
return repositories.get(inspectRepository(value).state);
|
|
677
|
+
}
|
|
678
|
+
if (typeof value === "function") {
|
|
679
|
+
if (isDeclaredException(value)) {
|
|
680
|
+
return exceptionRefs.get(value) ?? { core: value.name };
|
|
681
|
+
}
|
|
682
|
+
return scope.entities.get(value) ?? scope.records.get(value) ?? { kind: "opaque" };
|
|
683
|
+
}
|
|
684
|
+
if (Array.isArray(value))
|
|
685
|
+
return value.map(describeOption);
|
|
686
|
+
if (typeof value === "object" && value !== null) {
|
|
687
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined).map(([key, item]) => [key, describeOption(item)]));
|
|
688
|
+
}
|
|
689
|
+
return value;
|
|
690
|
+
};
|
|
691
|
+
const describeGeneric = (UseCase) => {
|
|
692
|
+
const origin = useCaseOriginOf(UseCase);
|
|
693
|
+
if (!origin || interpretedStepsOf(UseCase) === undefined)
|
|
694
|
+
return {};
|
|
695
|
+
const repository = repositories.get(inspectRepository(origin.repository).state);
|
|
696
|
+
if (repository === undefined)
|
|
697
|
+
return {};
|
|
698
|
+
return {
|
|
699
|
+
generic: {
|
|
700
|
+
name: origin.plugin.name,
|
|
701
|
+
version: origin.plugin.version,
|
|
702
|
+
repository,
|
|
703
|
+
options: describeOption(origin.options)
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
};
|
|
707
|
+
const refsOf = (blueprints) => {
|
|
708
|
+
const refs = {};
|
|
709
|
+
for (const [key, blueprint] of Object.entries(blueprints)) {
|
|
710
|
+
const ref = blueprint === undefined ? undefined : scope.blueprints.get(blueprint.properties);
|
|
711
|
+
if (ref !== undefined)
|
|
712
|
+
refs[key] = ref;
|
|
713
|
+
}
|
|
714
|
+
return Object.keys(refs).length === 0 ? {} : { refs };
|
|
715
|
+
};
|
|
716
|
+
const each = (collection, describe) => Object.fromEntries([...registry[collection].values()].map((entry) => [
|
|
717
|
+
entry.name,
|
|
718
|
+
describe(entry)
|
|
719
|
+
]));
|
|
720
|
+
return {
|
|
721
|
+
name,
|
|
722
|
+
extends: extended.map((other) => other.name),
|
|
723
|
+
...provider === undefined ? {} : { provider },
|
|
724
|
+
...named.size === 0 ? {} : {
|
|
725
|
+
providers: Object.fromEntries([...named].map(([key, { slot, from }]) => {
|
|
726
|
+
const { value } = slotEntry(slot);
|
|
727
|
+
return [
|
|
728
|
+
key,
|
|
729
|
+
{
|
|
730
|
+
from,
|
|
731
|
+
...isProviderFactory(value) ? { kind: "opaque" } : providerDescriptionOf(value)
|
|
732
|
+
}
|
|
733
|
+
];
|
|
734
|
+
}))
|
|
735
|
+
},
|
|
736
|
+
...log === undefined ? {} : log === "opaque" ? { log: { kind: "opaque" } } : {
|
|
737
|
+
log: {
|
|
738
|
+
level: log.level,
|
|
739
|
+
providers: log.providers.map((logProvider) => {
|
|
740
|
+
const providerName = logProviderNameOf(logProvider);
|
|
741
|
+
return providerName === undefined ? { kind: "opaque" } : { kind: "log-provider", name: providerName };
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
...traced ? { trace: { kind: "opaque" } } : {},
|
|
746
|
+
...Object.keys(publishers).length === 0 ? {} : {
|
|
747
|
+
publishers: Object.fromEntries(Object.entries(publishers).map(([key, from]) => [key, { from }]))
|
|
748
|
+
},
|
|
749
|
+
properties: each("properties", (entry) => ({
|
|
750
|
+
from: entry.from,
|
|
751
|
+
...propertyDescriptionOf(entry.value, scope)
|
|
752
|
+
})),
|
|
753
|
+
blueprints: each("blueprints", (entry) => ({
|
|
754
|
+
from: entry.from,
|
|
755
|
+
fields: fieldsDescriptionOf(entry.value.properties, scope)
|
|
756
|
+
})),
|
|
757
|
+
records: each("records", (entry) => recordDescriptionOf(entry, scope)),
|
|
758
|
+
entities: each("entities", (entry) => recordDescriptionOf(entry, scope)),
|
|
759
|
+
environments: each("environments", (entry) => ({
|
|
760
|
+
from: entry.from,
|
|
761
|
+
fields: fieldsDescriptionOf(entry.value.blueprint.properties, scope)
|
|
762
|
+
})),
|
|
763
|
+
repositories: each("repositories", (entry) => {
|
|
764
|
+
const info = inspectRepository(entry.value);
|
|
765
|
+
return {
|
|
766
|
+
from: entry.from,
|
|
767
|
+
entity: scope.entities.get(info.entity),
|
|
768
|
+
actions: info.actions,
|
|
769
|
+
deletion: info.deletion,
|
|
770
|
+
indexes: info.indexes,
|
|
771
|
+
scans: info.scans,
|
|
772
|
+
relations: info.relations.map((relation) => repositories.get(relation)),
|
|
773
|
+
environments: info.environments.map((environment) => environmentRefs.get(environment)),
|
|
774
|
+
...info.provider === undefined ? {} : {
|
|
775
|
+
provider: isProviderSlot(info.provider) ? referenceOf(info.provider) : providerDescriptionOf(info.provider)
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
}),
|
|
779
|
+
useCases: each("useCases", (entry) => {
|
|
780
|
+
const UseCase = entry.value;
|
|
781
|
+
return {
|
|
782
|
+
from: entry.from,
|
|
783
|
+
name: UseCase.useCaseName,
|
|
784
|
+
...UseCase.description === undefined ? {} : { description: UseCase.description },
|
|
785
|
+
tags: UseCase.tags,
|
|
786
|
+
deprecated: UseCase.deprecated,
|
|
787
|
+
...UseCase.route === undefined ? {} : { route: UseCase.route },
|
|
788
|
+
...UseCase.access === undefined ? {} : { access: UseCase.access },
|
|
789
|
+
input: fieldsDescriptionOf(UseCase.blueprint.properties, scope),
|
|
790
|
+
response: fieldsDescriptionOf(UseCase.response.properties, scope),
|
|
791
|
+
...UseCase.context === undefined ? {} : {
|
|
792
|
+
context: fieldsDescriptionOf(UseCase.context.properties, scope)
|
|
793
|
+
},
|
|
794
|
+
...refsOf({
|
|
795
|
+
input: UseCase.blueprint,
|
|
796
|
+
response: UseCase.response,
|
|
797
|
+
context: UseCase.context
|
|
798
|
+
}),
|
|
799
|
+
errors: UseCase.errors.map((Thrown) => {
|
|
800
|
+
const ref = exceptionRefs.get(Thrown);
|
|
801
|
+
return ref === undefined ? { kind: "core", name: Thrown.name } : { kind: "exception", ref };
|
|
802
|
+
}),
|
|
803
|
+
dependencies: Object.fromEntries(Object.entries(UseCase.dependencies).map(([key, dependency]) => [
|
|
804
|
+
key,
|
|
805
|
+
isLoggerDeclaration(dependency) ? { kind: "logger", ref: "Logger" } : isUseCaseClass(dependency) ? {
|
|
806
|
+
kind: "use-case",
|
|
807
|
+
ref: useCaseRefs.get(dependency)
|
|
808
|
+
} : isEnvironment(dependency) ? {
|
|
809
|
+
kind: "environment",
|
|
810
|
+
ref: environmentRefs.get(dependency)
|
|
811
|
+
} : {
|
|
812
|
+
kind: "repository",
|
|
813
|
+
ref: repositories.get(inspectRepository(dependency).state)
|
|
814
|
+
}
|
|
815
|
+
])),
|
|
816
|
+
implementation: describeImplementation(UseCase),
|
|
817
|
+
...describeGeneric(UseCase)
|
|
818
|
+
};
|
|
819
|
+
}),
|
|
820
|
+
commands: each("commands", (entry) => {
|
|
821
|
+
const command = entry.value;
|
|
822
|
+
return {
|
|
823
|
+
from: entry.from,
|
|
824
|
+
name: command.commandName,
|
|
825
|
+
...command.description === undefined ? {} : { description: command.description },
|
|
826
|
+
tags: command.tags,
|
|
827
|
+
...command.context === undefined ? {} : {
|
|
828
|
+
context: fieldsDescriptionOf(command.context.properties, scope)
|
|
829
|
+
},
|
|
830
|
+
...refsOf({ context: command.context }),
|
|
831
|
+
operations: Object.fromEntries(Object.entries(command.useCases).map(([op, UseCase]) => [
|
|
832
|
+
op,
|
|
833
|
+
{ ref: useCaseRefs.get(UseCase) }
|
|
834
|
+
])),
|
|
835
|
+
transaction: command.transaction,
|
|
836
|
+
...command.environments.length === 0 ? {} : {
|
|
837
|
+
environments: command.environments.map((environment) => environmentRefs.get(environment))
|
|
838
|
+
},
|
|
839
|
+
events: typeof command.events === "function" ? eventPolicyDescriptionOf(command.events) ?? { kind: "opaque" } : command.events,
|
|
840
|
+
...command.onEventError === undefined ? {} : { onEventError: { kind: "opaque" } }
|
|
841
|
+
};
|
|
842
|
+
}),
|
|
843
|
+
outputs: each("outputs", (entry) => {
|
|
844
|
+
const output = entry.value;
|
|
845
|
+
return {
|
|
846
|
+
from: entry.from,
|
|
847
|
+
name: output.outputName,
|
|
848
|
+
commands: output.commands.map((command) => command.commandName),
|
|
849
|
+
statuses: output.statuses,
|
|
850
|
+
pipe: output.stages.map((stage) => stageDescriptionOf(stage))
|
|
851
|
+
};
|
|
852
|
+
}),
|
|
853
|
+
exceptions: each("exceptions", (entry) => {
|
|
854
|
+
const Declared = entry.value;
|
|
855
|
+
const message = declaredMessageOf(Declared);
|
|
856
|
+
return {
|
|
857
|
+
from: entry.from,
|
|
858
|
+
layer: Declared.layer,
|
|
859
|
+
...message === undefined ? {} : { message }
|
|
860
|
+
};
|
|
861
|
+
})
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
export function applicationOf(settings) {
|
|
865
|
+
const { name } = settings ?? {};
|
|
866
|
+
if (typeof name !== "string" || name === "") {
|
|
867
|
+
invalid("applicationOf", "Expected a name.");
|
|
868
|
+
}
|
|
869
|
+
const extended = settings.extends ?? [];
|
|
870
|
+
if (!Array.isArray(extended) || !extended.every(isApplication)) {
|
|
871
|
+
invalid(name, "Expected `extends` to be a list of applications.");
|
|
872
|
+
}
|
|
873
|
+
const registry = resolve(settings);
|
|
874
|
+
validate(name, registry);
|
|
875
|
+
validateUseCases(name, registry);
|
|
876
|
+
validateCommands(name, registry);
|
|
877
|
+
validateOutputs(name, registry);
|
|
878
|
+
if (settings.providers !== undefined && !isProviderStorage(settings.providers)) {
|
|
879
|
+
invalid(name, "Expected `providers` to be a storage made by providersOf.");
|
|
880
|
+
}
|
|
881
|
+
const named = new Map;
|
|
882
|
+
const addNamed = (entry) => {
|
|
883
|
+
const { key } = slotEntry(entry.slot);
|
|
884
|
+
const current = named.get(key);
|
|
885
|
+
if (current !== undefined && current.slot !== entry.slot) {
|
|
886
|
+
throw new ApplicationConflict({
|
|
887
|
+
source: name,
|
|
888
|
+
details: {
|
|
889
|
+
reason: `The provider "${key}" is declared by different storages, in "${current.from}" and "${entry.from}".`,
|
|
890
|
+
origins: [current.from, entry.from],
|
|
891
|
+
declarations: [`provider ${key}`, `provider ${key}`]
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
named.set(key, entry);
|
|
896
|
+
};
|
|
897
|
+
for (const other of extended) {
|
|
898
|
+
for (const entry of namedProviders.get(other)?.values() ?? []) {
|
|
899
|
+
addNamed(entry);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
for (const slot of settings.providers ? slotsOf(settings.providers) : []) {
|
|
903
|
+
const inherited = named.get(slotEntry(slot).key);
|
|
904
|
+
if (inherited?.slot === slot)
|
|
905
|
+
continue;
|
|
906
|
+
const { value } = slotEntry(slot);
|
|
907
|
+
addNamed({
|
|
908
|
+
slot,
|
|
909
|
+
from: name,
|
|
910
|
+
instance: isProviderFactory(value) ? deferredProvider(value, name) : value
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
const instances = new Map([...named.values()].map((entry) => [entry.slot, entry.instance]));
|
|
914
|
+
const instanceOf = (slot, holder) => {
|
|
915
|
+
const instance = instances.get(slot);
|
|
916
|
+
if (instance === undefined) {
|
|
917
|
+
throw new UnregisteredApplicationReference({
|
|
918
|
+
source: name,
|
|
919
|
+
details: {
|
|
920
|
+
holder,
|
|
921
|
+
reference: `provider "${slotEntry(slot).key}"`
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
return instance;
|
|
926
|
+
};
|
|
927
|
+
for (const entry of registry.repositories.values()) {
|
|
928
|
+
const declared = inspectRepository(entry.value).provider;
|
|
929
|
+
if (isProviderSlot(declared)) {
|
|
930
|
+
instanceOf(declared, `repository "${entry.name}"`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
const inheritedFrom = settings.provider === undefined ? [...extended].reverse().find((other) => other.provider) : undefined;
|
|
934
|
+
const declaredProvider = settings.provider ?? inheritedFrom?.provider;
|
|
935
|
+
if (declaredProvider !== undefined && typeof declaredProvider !== "object") {
|
|
936
|
+
invalid(name, "Expected `provider` to be a repository provider.");
|
|
937
|
+
}
|
|
938
|
+
const defaultSlot = isProviderSlot(declaredProvider) ? declaredProvider : inheritedFrom === undefined ? undefined : defaultSlots.get(inheritedFrom);
|
|
939
|
+
const provider = defaultSlot !== undefined ? instanceOf(defaultSlot, "the application") : isProviderFactory(declaredProvider) ? deferredProvider(declaredProvider, name) : declaredProvider;
|
|
940
|
+
const allProviders = [
|
|
941
|
+
...new Set([provider, ...instances.values()].filter((value) => value !== undefined))
|
|
942
|
+
];
|
|
943
|
+
{
|
|
944
|
+
const registered = new Set([...registry.environments.values()].map((entry) => entry.key));
|
|
945
|
+
const holderOf = (instance) => {
|
|
946
|
+
const entry = [...named.values()].find((candidate) => candidate.instance === instance);
|
|
947
|
+
return entry === undefined || instance === provider ? "the provider" : `provider "${slotEntry(entry.slot).key}"`;
|
|
948
|
+
};
|
|
949
|
+
for (const instance of allProviders) {
|
|
950
|
+
for (const environment of deferredEnvironments(instance)) {
|
|
951
|
+
if (registered.has(environment))
|
|
952
|
+
continue;
|
|
953
|
+
throw new UnregisteredApplicationReference({
|
|
954
|
+
source: name,
|
|
955
|
+
details: {
|
|
956
|
+
holder: holderOf(instance),
|
|
957
|
+
reference: "an environment"
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
const declaredLog = settings.log ?? inherited(extended, logSettings);
|
|
964
|
+
const log = declaredLog === undefined ? undefined : applicationLogger(name);
|
|
965
|
+
if (declaredLog !== undefined && !isLogFactory(declaredLog)) {
|
|
966
|
+
log?.set(checkLogSettings(declaredLog));
|
|
967
|
+
}
|
|
968
|
+
const logger = log?.logger;
|
|
969
|
+
const tracer = settings.trace === undefined ? inherited(extended, traceSettings) : checkTracer(settings.trace);
|
|
970
|
+
const { publishers, origins } = publishersOf(name, settings.publishers, extended);
|
|
971
|
+
const hasPublishers = Object.keys(publishers).length > 0;
|
|
972
|
+
for (const entry of registry.commands.values()) {
|
|
973
|
+
for (const key of eventPolicyPublishersOf(entry.value.events)) {
|
|
974
|
+
if (!Object.hasOwn(publishers, key)) {
|
|
975
|
+
throw new UnregisteredApplicationReference({
|
|
976
|
+
source: name,
|
|
977
|
+
details: {
|
|
978
|
+
holder: `command "${entry.name}"`,
|
|
979
|
+
reference: `publisher "${key}"`
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
const bindsProviders = provider !== undefined || instances.size > 0;
|
|
986
|
+
const rebinds = bindsProviders || logger !== undefined || tracer !== undefined || hasPublishers;
|
|
987
|
+
const collection = (key) => Object.freeze(Object.fromEntries([...registry[key].values()].map((entry) => [entry.name, entry.value])));
|
|
988
|
+
const bound = new Map;
|
|
989
|
+
const boundByState = new Map;
|
|
990
|
+
const repositories = {};
|
|
991
|
+
for (const entry of registry.repositories.values()) {
|
|
992
|
+
const repository = bindsProviders ? bindRepository(entry.value, provider, instances, {
|
|
993
|
+
application: name,
|
|
994
|
+
key: entry.name,
|
|
995
|
+
logger
|
|
996
|
+
}) : entry.value;
|
|
997
|
+
bound.set(entry.value, repository);
|
|
998
|
+
boundByState.set(entry.key, repository);
|
|
999
|
+
repositories[entry.name] = repository;
|
|
1000
|
+
}
|
|
1001
|
+
const boundUseCases = new Map;
|
|
1002
|
+
const bindUseCase = (UseCase) => {
|
|
1003
|
+
if (!rebinds)
|
|
1004
|
+
return UseCase;
|
|
1005
|
+
const done = boundUseCases.get(UseCase);
|
|
1006
|
+
if (done)
|
|
1007
|
+
return done;
|
|
1008
|
+
const Declared = UseCase;
|
|
1009
|
+
|
|
1010
|
+
class Bound extends Declared {
|
|
1011
|
+
}
|
|
1012
|
+
Object.defineProperty(Bound, "name", { value: Declared.name });
|
|
1013
|
+
boundUseCases.set(UseCase, Bound);
|
|
1014
|
+
const dependencies = Object.fromEntries(Object.entries(UseCase.dependencies).map(([key, dependency]) => [
|
|
1015
|
+
key,
|
|
1016
|
+
isUseCaseClass(dependency) ? bindUseCase(dependency) : isEnvironment(dependency) || isLoggerDeclaration(dependency) ? dependency : boundByState.get(inspectRepository(dependency).state) ?? dependency
|
|
1017
|
+
]));
|
|
1018
|
+
Object.defineProperty(Bound, "dependencies", {
|
|
1019
|
+
value: Object.freeze(dependencies)
|
|
1020
|
+
});
|
|
1021
|
+
if (logger)
|
|
1022
|
+
Object.defineProperty(Bound, ApplicationLoggerKey, { value: logger });
|
|
1023
|
+
if (tracer)
|
|
1024
|
+
Object.defineProperty(Bound, ApplicationTracerKey, { value: tracer });
|
|
1025
|
+
return Bound;
|
|
1026
|
+
};
|
|
1027
|
+
const useCases = {};
|
|
1028
|
+
const boundUseCaseOf = new Map;
|
|
1029
|
+
for (const entry of registry.useCases.values()) {
|
|
1030
|
+
const bound = bindUseCase(entry.value);
|
|
1031
|
+
boundUseCaseOf.set(entry.value, bound);
|
|
1032
|
+
useCases[entry.name] = bound;
|
|
1033
|
+
}
|
|
1034
|
+
const commands = {};
|
|
1035
|
+
const boundCommandOf = new Map;
|
|
1036
|
+
for (const entry of registry.commands.values()) {
|
|
1037
|
+
const declared = entry.value;
|
|
1038
|
+
const rebuilt = !rebinds ? declared : commandOf({
|
|
1039
|
+
name: declared.commandName,
|
|
1040
|
+
description: declared.description,
|
|
1041
|
+
tags: declared.tags,
|
|
1042
|
+
context: declared.context,
|
|
1043
|
+
useCases: Object.fromEntries(Object.entries(declared.useCases).map(([op, UseCase]) => [
|
|
1044
|
+
op,
|
|
1045
|
+
boundUseCaseOf.get(UseCase)
|
|
1046
|
+
])),
|
|
1047
|
+
transaction: declared.transaction,
|
|
1048
|
+
environmentDependencies: declared.environments,
|
|
1049
|
+
events: declared.events,
|
|
1050
|
+
onEventError: declared.onEventError
|
|
1051
|
+
});
|
|
1052
|
+
if (bindsProviders)
|
|
1053
|
+
verifyCommand(rebuilt);
|
|
1054
|
+
if (hasPublishers)
|
|
1055
|
+
bindPublishers(rebuilt, publishers);
|
|
1056
|
+
boundCommandOf.set(entry.value, rebuilt);
|
|
1057
|
+
commands[entry.name] = rebuilt;
|
|
1058
|
+
}
|
|
1059
|
+
const outputs = {};
|
|
1060
|
+
const boundOutputOf = new Map;
|
|
1061
|
+
for (const entry of registry.outputs.values()) {
|
|
1062
|
+
const declared = entry.value;
|
|
1063
|
+
const rebuilt = !rebinds ? declared : outputOf({
|
|
1064
|
+
name: declared.outputName,
|
|
1065
|
+
commands: declared.commands.map((command) => boundCommandOf.get(command)),
|
|
1066
|
+
statuses: declared.statuses,
|
|
1067
|
+
pipe: declared.stages
|
|
1068
|
+
});
|
|
1069
|
+
boundOutputOf.set(entry.value, rebuilt);
|
|
1070
|
+
outputs[entry.name] = rebuilt;
|
|
1071
|
+
}
|
|
1072
|
+
const environmentEntries = [...registry.environments.values()];
|
|
1073
|
+
const registeredEnvironments = new Set(environmentEntries.map((entry) => entry.value));
|
|
1074
|
+
const context = Object.freeze({
|
|
1075
|
+
name,
|
|
1076
|
+
env(environment) {
|
|
1077
|
+
if (!registeredEnvironments.has(environment)) {
|
|
1078
|
+
throw new UnregisteredApplicationReference({
|
|
1079
|
+
source: name,
|
|
1080
|
+
details: { holder: "the context", reference: "an environment" }
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
return environment.load();
|
|
1084
|
+
},
|
|
1085
|
+
logger: logger ?? silentLogger
|
|
1086
|
+
});
|
|
1087
|
+
let starting;
|
|
1088
|
+
let started = false;
|
|
1089
|
+
const start = async (lock) => {
|
|
1090
|
+
if (lock !== undefined) {
|
|
1091
|
+
if (!isLock(lock)) {
|
|
1092
|
+
invalid(name, "Expected lock to be a schema lock ({ lockVersion: 1, entities }).");
|
|
1093
|
+
}
|
|
1094
|
+
const problems = schemaLockProblemsOf(lock, schemaLockOf(application.describe()));
|
|
1095
|
+
if (problems.length > 0) {
|
|
1096
|
+
throw new SchemaLockMismatch({ source: name, details: { problems } });
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
const failures = {};
|
|
1100
|
+
let cause;
|
|
1101
|
+
for (const entry of environmentEntries) {
|
|
1102
|
+
try {
|
|
1103
|
+
entry.value.load();
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
if (!(error instanceof Exception) || !error.is(InvalidEnvironment)) {
|
|
1106
|
+
throw error;
|
|
1107
|
+
}
|
|
1108
|
+
cause ??= error;
|
|
1109
|
+
failures[entry.name] = [
|
|
1110
|
+
...error.details?.keys ?? []
|
|
1111
|
+
];
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (Object.keys(failures).length > 0) {
|
|
1115
|
+
throw new InvalidApplicationEnvironment({
|
|
1116
|
+
source: name,
|
|
1117
|
+
details: { environments: failures },
|
|
1118
|
+
cause
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
if (isLogFactory(declaredLog)) {
|
|
1122
|
+
log?.set(checkLogSettings(await declaredLog.create(context)));
|
|
1123
|
+
}
|
|
1124
|
+
for (const instance of allProviders) {
|
|
1125
|
+
if (!isDeferredProvider(instance))
|
|
1126
|
+
continue;
|
|
1127
|
+
const made = await startDeferred(instance, context);
|
|
1128
|
+
checkTransactions(name, made, Object.entries(commands).filter(([, command]) => command.transaction && commandTransactionProvider(command) === instance).map(([key]) => key));
|
|
1129
|
+
checkCapabilities(name, made, Object.entries(repositories).filter(([, repository]) => providerOfRepository(repository) === instance && inspectRepository(repository).actions.includes("findCursor")).map(([key]) => key));
|
|
1130
|
+
}
|
|
1131
|
+
started = true;
|
|
1132
|
+
};
|
|
1133
|
+
const application = Object.freeze({
|
|
1134
|
+
[Kind]: "application",
|
|
1135
|
+
name,
|
|
1136
|
+
extends: Object.freeze([...extended]),
|
|
1137
|
+
provider,
|
|
1138
|
+
logger: logger ?? silentLogger,
|
|
1139
|
+
context,
|
|
1140
|
+
...logger ? { [ApplicationLoggerKey]: logger } : {},
|
|
1141
|
+
...tracer ? { [ApplicationTracerKey]: tracer } : {},
|
|
1142
|
+
...hasPublishers ? { [ApplicationPublishersKey]: publishers } : {},
|
|
1143
|
+
properties: collection("properties"),
|
|
1144
|
+
blueprints: collection("blueprints"),
|
|
1145
|
+
records: collection("records"),
|
|
1146
|
+
entities: collection("entities"),
|
|
1147
|
+
environments: collection("environments"),
|
|
1148
|
+
repositories: Object.freeze(repositories),
|
|
1149
|
+
useCases: Object.freeze(useCases),
|
|
1150
|
+
commands: Object.freeze(commands),
|
|
1151
|
+
outputs: Object.freeze(outputs),
|
|
1152
|
+
exceptions: collection("exceptions"),
|
|
1153
|
+
useCase(useCase) {
|
|
1154
|
+
if (!boundUseCaseOf.has(useCase)) {
|
|
1155
|
+
invalid(name, "Expected a use case registered in the application.");
|
|
1156
|
+
}
|
|
1157
|
+
return boundUseCaseOf.get(useCase);
|
|
1158
|
+
},
|
|
1159
|
+
async run(useCase, input = {}, options) {
|
|
1160
|
+
if (!boundUseCaseOf.has(useCase)) {
|
|
1161
|
+
invalid(name, "Expected a use case registered in the application.");
|
|
1162
|
+
}
|
|
1163
|
+
const Bound = boundUseCaseOf.get(useCase);
|
|
1164
|
+
const instance = options?.context === undefined ? new Bound(input) : new Bound(input, { context: options.context });
|
|
1165
|
+
return instance.execute(options?.dependencies);
|
|
1166
|
+
},
|
|
1167
|
+
command(command) {
|
|
1168
|
+
if (!boundCommandOf.has(command)) {
|
|
1169
|
+
invalid(name, "Expected a command registered in the application.");
|
|
1170
|
+
}
|
|
1171
|
+
return boundCommandOf.get(command);
|
|
1172
|
+
},
|
|
1173
|
+
output(output) {
|
|
1174
|
+
if (!boundOutputOf.has(output)) {
|
|
1175
|
+
invalid(name, "Expected an output registered in the application.");
|
|
1176
|
+
}
|
|
1177
|
+
return boundOutputOf.get(output);
|
|
1178
|
+
},
|
|
1179
|
+
repository(repository) {
|
|
1180
|
+
if (!bound.has(repository)) {
|
|
1181
|
+
invalid(name, "Expected a repository registered in the application.");
|
|
1182
|
+
}
|
|
1183
|
+
return bound.get(repository);
|
|
1184
|
+
},
|
|
1185
|
+
extend(...others) {
|
|
1186
|
+
return applicationOf({
|
|
1187
|
+
name,
|
|
1188
|
+
extends: [application, ...others],
|
|
1189
|
+
...provider === undefined ? {} : { provider: defaultSlot ?? provider },
|
|
1190
|
+
...declaredLog === undefined ? {} : { log: declaredLog },
|
|
1191
|
+
...tracer === undefined ? {} : { trace: tracer }
|
|
1192
|
+
});
|
|
1193
|
+
},
|
|
1194
|
+
describe: () => describeRegistry(name, extended, registry, defaultSlot !== undefined ? referenceOf(defaultSlot) : provider === undefined ? undefined : providerDescriptionOf(provider), named, isLogFactory(declaredLog) ? "opaque" : log?.settings, origins, tracer !== undefined),
|
|
1195
|
+
get started() {
|
|
1196
|
+
return started;
|
|
1197
|
+
},
|
|
1198
|
+
migrate(options) {
|
|
1199
|
+
return migrateApplication({
|
|
1200
|
+
name,
|
|
1201
|
+
entities: new Map([...registry.entities.values()].map((entry) => [
|
|
1202
|
+
entry.name,
|
|
1203
|
+
entry.value
|
|
1204
|
+
])),
|
|
1205
|
+
repositories: Object.values(repositories),
|
|
1206
|
+
describe: () => application.describe()
|
|
1207
|
+
}, options);
|
|
1208
|
+
},
|
|
1209
|
+
start(options = {}) {
|
|
1210
|
+
const { replace = false, register = true, lock } = options ?? {};
|
|
1211
|
+
starting ??= (async () => {
|
|
1212
|
+
if (register) {
|
|
1213
|
+
const current = startedApplication();
|
|
1214
|
+
if (current !== undefined && current !== application) {
|
|
1215
|
+
if (!replace) {
|
|
1216
|
+
throw new ApplicationAlreadyStarted({
|
|
1217
|
+
source: name,
|
|
1218
|
+
details: { current: current.name, attempted: name }
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
await current.stop();
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
await start(lock);
|
|
1225
|
+
if (register)
|
|
1226
|
+
setStartedApplication(application);
|
|
1227
|
+
})().then(() => application, async (error) => {
|
|
1228
|
+
starting = undefined;
|
|
1229
|
+
for (const instance of allProviders) {
|
|
1230
|
+
if (isDeferredProvider(instance))
|
|
1231
|
+
await stopProvider(instance);
|
|
1232
|
+
}
|
|
1233
|
+
if (isLogFactory(declaredLog))
|
|
1234
|
+
log?.set(undefined);
|
|
1235
|
+
throw error;
|
|
1236
|
+
});
|
|
1237
|
+
return starting;
|
|
1238
|
+
},
|
|
1239
|
+
async stop() {
|
|
1240
|
+
const wasStarting = starting;
|
|
1241
|
+
starting = undefined;
|
|
1242
|
+
started = false;
|
|
1243
|
+
if (wasStarting)
|
|
1244
|
+
await wasStarting.catch(() => {
|
|
1245
|
+
return;
|
|
1246
|
+
});
|
|
1247
|
+
if (startedApplication() === application)
|
|
1248
|
+
setStartedApplication(undefined);
|
|
1249
|
+
for (const instance of allProviders)
|
|
1250
|
+
await stopProvider(instance);
|
|
1251
|
+
await log?.flush();
|
|
1252
|
+
if (isLogFactory(declaredLog))
|
|
1253
|
+
log?.set(undefined);
|
|
1254
|
+
},
|
|
1255
|
+
get(declaration) {
|
|
1256
|
+
if (isEnvironment(declaration)) {
|
|
1257
|
+
if (!registeredEnvironments.has(declaration)) {
|
|
1258
|
+
invalid(name, "Expected an environment registered in the application.");
|
|
1259
|
+
}
|
|
1260
|
+
return declaration.load();
|
|
1261
|
+
}
|
|
1262
|
+
if (isCommand(declaration))
|
|
1263
|
+
return application.command(declaration);
|
|
1264
|
+
if (isOutput(declaration))
|
|
1265
|
+
return application.output(declaration);
|
|
1266
|
+
if (isUseCaseClass(declaration))
|
|
1267
|
+
return application.useCase(declaration);
|
|
1268
|
+
if (isRepository(declaration)) {
|
|
1269
|
+
return application.repository(declaration);
|
|
1270
|
+
}
|
|
1271
|
+
if (isProviderSlot(declaration)) {
|
|
1272
|
+
const instance = instances.get(declaration);
|
|
1273
|
+
if (instance === undefined) {
|
|
1274
|
+
invalid(name, "Expected a provider of a storage registered in the application.");
|
|
1275
|
+
}
|
|
1276
|
+
return instance;
|
|
1277
|
+
}
|
|
1278
|
+
return invalid(name, "Expected a repository, use case, command, output, environment or provider registered in the application.");
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
internals.set(application, registry);
|
|
1282
|
+
namedProviders.set(application, named);
|
|
1283
|
+
if (defaultSlot !== undefined)
|
|
1284
|
+
defaultSlots.set(application, defaultSlot);
|
|
1285
|
+
if (declaredLog !== undefined)
|
|
1286
|
+
logSettings.set(application, declaredLog);
|
|
1287
|
+
if (tracer !== undefined)
|
|
1288
|
+
traceSettings.set(application, tracer);
|
|
1289
|
+
publisherOrigins.set(application, Object.freeze(origins));
|
|
1290
|
+
return application;
|
|
1291
|
+
}
|