@chidchanun/bcp 0.3.0 → 0.3.1
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 +135 -111
- package/docs/README.md +53 -78
- package/docs/api-freeze-snapshot.json +13 -2
- package/docs/api-manifest.json +20 -12
- package/docs/api-reference.md +195 -197
- package/docs/application-platform.md +141 -171
- package/docs/docs-web-manifest.json +6 -4
- package/docs/migration-0.3.md +189 -68
- package/docs/platform-contract.md +53 -37
- package/docs/platform-manifest.json +21 -4
- package/docs/releases/0.3.1.md +116 -0
- package/docs/service-container.md +333 -0
- package/package.json +7 -1
- package/packages/bundler/src/client-boundary.ts +1 -0
- package/packages/client/src/application.mjs +586 -20
- package/packages/client/src/container.mjs +573 -0
- package/packages/client/src/container.ts +24 -0
- package/packages/server/src/application.ts +116 -5
- package/packages/server/src/container.ts +970 -0
|
@@ -1,3 +1,506 @@
|
|
|
1
|
+
// packages/server/src/container.ts
|
|
2
|
+
var ServiceNotFoundError = class extends Error {
|
|
3
|
+
token;
|
|
4
|
+
constructor(token) {
|
|
5
|
+
super(
|
|
6
|
+
`BCP Container: service "${token.description}" is not registered.`
|
|
7
|
+
);
|
|
8
|
+
this.name = "ServiceNotFoundError";
|
|
9
|
+
this.token = token;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var ServiceResolutionError = class extends Error {
|
|
13
|
+
token;
|
|
14
|
+
cause;
|
|
15
|
+
constructor(token, message, cause) {
|
|
16
|
+
super(
|
|
17
|
+
`BCP Container: could not resolve "${token.description}". ${message}`
|
|
18
|
+
);
|
|
19
|
+
this.name = "ServiceResolutionError";
|
|
20
|
+
this.token = token;
|
|
21
|
+
this.cause = cause;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var ServiceDisposalError = class extends AggregateError {
|
|
25
|
+
constructor(errors) {
|
|
26
|
+
super(
|
|
27
|
+
errors,
|
|
28
|
+
"BCP Container: one or more services failed to dispose."
|
|
29
|
+
);
|
|
30
|
+
this.name = "ServiceDisposalError";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function createServiceContainer(options = {}) {
|
|
34
|
+
const root = new ScopeImpl(
|
|
35
|
+
normalizeOptionalName(
|
|
36
|
+
options.name
|
|
37
|
+
) ?? "root",
|
|
38
|
+
void 0
|
|
39
|
+
);
|
|
40
|
+
root.root = root;
|
|
41
|
+
const container = {
|
|
42
|
+
get name() {
|
|
43
|
+
return root.name;
|
|
44
|
+
},
|
|
45
|
+
get state() {
|
|
46
|
+
return root.state;
|
|
47
|
+
},
|
|
48
|
+
get parent() {
|
|
49
|
+
return void 0;
|
|
50
|
+
},
|
|
51
|
+
has(token) {
|
|
52
|
+
return root.has(token);
|
|
53
|
+
},
|
|
54
|
+
resolve(token) {
|
|
55
|
+
return root.resolve(token);
|
|
56
|
+
},
|
|
57
|
+
optional(token) {
|
|
58
|
+
return root.optional(token);
|
|
59
|
+
},
|
|
60
|
+
createScope(scopeOptions = {}) {
|
|
61
|
+
return root.createScope(
|
|
62
|
+
scopeOptions
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
graph() {
|
|
66
|
+
return root.graph();
|
|
67
|
+
},
|
|
68
|
+
dispose() {
|
|
69
|
+
return root.dispose();
|
|
70
|
+
},
|
|
71
|
+
register(provider, registerOptions = {}) {
|
|
72
|
+
root.registerProvider(
|
|
73
|
+
provider,
|
|
74
|
+
registerOptions.replace === true
|
|
75
|
+
);
|
|
76
|
+
return container;
|
|
77
|
+
},
|
|
78
|
+
registerMany(providers) {
|
|
79
|
+
for (const provider of providers) {
|
|
80
|
+
root.registerProvider(
|
|
81
|
+
provider,
|
|
82
|
+
false
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return container;
|
|
86
|
+
},
|
|
87
|
+
providers() {
|
|
88
|
+
return Array.from(
|
|
89
|
+
root.providersMap.values()
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
for (const provider of options.providers ?? []) {
|
|
94
|
+
root.registerProvider(
|
|
95
|
+
provider,
|
|
96
|
+
false
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return container;
|
|
100
|
+
}
|
|
101
|
+
var ScopeImpl = class _ScopeImpl {
|
|
102
|
+
constructor(name, parent) {
|
|
103
|
+
this.name = name;
|
|
104
|
+
this.parent = parent;
|
|
105
|
+
if (parent) {
|
|
106
|
+
this.root = parent.root;
|
|
107
|
+
parent.children.add(this);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
name;
|
|
111
|
+
parent;
|
|
112
|
+
root;
|
|
113
|
+
providersMap = /* @__PURE__ */ new Map();
|
|
114
|
+
overrideProviders = /* @__PURE__ */ new Map();
|
|
115
|
+
singletonCache = /* @__PURE__ */ new Map();
|
|
116
|
+
scopedCache = /* @__PURE__ */ new Map();
|
|
117
|
+
disposals = [];
|
|
118
|
+
children = /* @__PURE__ */ new Set();
|
|
119
|
+
state = "active";
|
|
120
|
+
disposePromise;
|
|
121
|
+
has(token) {
|
|
122
|
+
assertToken(token);
|
|
123
|
+
return this.findProvider(token) !== void 0;
|
|
124
|
+
}
|
|
125
|
+
resolve(token) {
|
|
126
|
+
return this.resolveWithPath(
|
|
127
|
+
token,
|
|
128
|
+
[]
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
async optional(token) {
|
|
132
|
+
if (!this.has(token)) {
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
return this.resolve(token);
|
|
136
|
+
}
|
|
137
|
+
createScope(options = {}) {
|
|
138
|
+
this.assertActive();
|
|
139
|
+
const scope = new _ScopeImpl(
|
|
140
|
+
normalizeOptionalName(
|
|
141
|
+
options.name
|
|
142
|
+
) ?? `${this.name}:scope-${this.children.size + 1}`,
|
|
143
|
+
this
|
|
144
|
+
);
|
|
145
|
+
for (const provider of options.overrides ?? []) {
|
|
146
|
+
scope.registerOverride(
|
|
147
|
+
provider
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return scope;
|
|
151
|
+
}
|
|
152
|
+
graph() {
|
|
153
|
+
const effective = /* @__PURE__ */ new Map();
|
|
154
|
+
let current = this;
|
|
155
|
+
while (current) {
|
|
156
|
+
for (const [id, provider] of current.overrideProviders) {
|
|
157
|
+
if (!effective.has(id)) {
|
|
158
|
+
effective.set(
|
|
159
|
+
id,
|
|
160
|
+
{
|
|
161
|
+
provider,
|
|
162
|
+
owner: current,
|
|
163
|
+
overridden: true
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
current = current.parent;
|
|
169
|
+
}
|
|
170
|
+
for (const [id, provider] of this.root.providersMap) {
|
|
171
|
+
if (!effective.has(id)) {
|
|
172
|
+
effective.set(
|
|
173
|
+
id,
|
|
174
|
+
{
|
|
175
|
+
provider,
|
|
176
|
+
owner: this.root,
|
|
177
|
+
overridden: false
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return Array.from(
|
|
183
|
+
effective.values(),
|
|
184
|
+
(record) => ({
|
|
185
|
+
token: record.provider.token,
|
|
186
|
+
description: record.provider.token.description,
|
|
187
|
+
lifetime: normalizeLifetime(
|
|
188
|
+
record.provider.lifetime
|
|
189
|
+
),
|
|
190
|
+
dependencies: (record.provider.dependencies ?? []).map(
|
|
191
|
+
(dependency) => dependency.description
|
|
192
|
+
),
|
|
193
|
+
overridden: record.overridden
|
|
194
|
+
})
|
|
195
|
+
).sort(
|
|
196
|
+
(left, right) => left.description.localeCompare(
|
|
197
|
+
right.description
|
|
198
|
+
)
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
dispose() {
|
|
202
|
+
if (this.disposePromise) {
|
|
203
|
+
return this.disposePromise;
|
|
204
|
+
}
|
|
205
|
+
if (this.state === "disposed") {
|
|
206
|
+
return Promise.resolve();
|
|
207
|
+
}
|
|
208
|
+
this.disposePromise = this.disposeInternal();
|
|
209
|
+
return this.disposePromise;
|
|
210
|
+
}
|
|
211
|
+
registerProvider(provider, replace) {
|
|
212
|
+
this.assertActive();
|
|
213
|
+
if (this.parent) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
"BCP Container: providers can only be registered on the root container. Use scope overrides for child scopes."
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
validateProvider(provider);
|
|
219
|
+
const id = provider.token.id;
|
|
220
|
+
if (this.providersMap.has(id) && !replace) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`BCP Container: service "${provider.token.description}" is already registered.`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (replace && this.hasResolved(id)) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`BCP Container: service "${provider.token.description}" cannot be replaced after it has been resolved.`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
this.providersMap.set(
|
|
231
|
+
id,
|
|
232
|
+
provider
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
registerOverride(provider) {
|
|
236
|
+
validateProvider(provider);
|
|
237
|
+
const id = provider.token.id;
|
|
238
|
+
if (this.overrideProviders.has(id)) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`BCP Container: scope override for "${provider.token.description}" is already registered.`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
this.overrideProviders.set(
|
|
244
|
+
id,
|
|
245
|
+
provider
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
async resolveWithPath(token, path) {
|
|
249
|
+
this.assertActive();
|
|
250
|
+
assertToken(token);
|
|
251
|
+
if (path.some(
|
|
252
|
+
(item) => item.id === token.id
|
|
253
|
+
)) {
|
|
254
|
+
throw new ServiceResolutionError(
|
|
255
|
+
token,
|
|
256
|
+
`Circular dependency detected: ${[
|
|
257
|
+
...path,
|
|
258
|
+
token
|
|
259
|
+
].map(
|
|
260
|
+
(item) => item.description
|
|
261
|
+
).join(" -> ")}.`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const record = this.findProvider(token);
|
|
265
|
+
if (!record) {
|
|
266
|
+
throw new ServiceNotFoundError(
|
|
267
|
+
token
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
const lifetime = normalizeLifetime(
|
|
271
|
+
record.provider.lifetime
|
|
272
|
+
);
|
|
273
|
+
const cache = lifetime === "singleton" ? record.owner.singletonCache : lifetime === "scoped" ? this.scopedCache : void 0;
|
|
274
|
+
const existing = cache?.get(token.id);
|
|
275
|
+
if (existing) {
|
|
276
|
+
return existing;
|
|
277
|
+
}
|
|
278
|
+
const resolution = this.instantiate(
|
|
279
|
+
record.provider,
|
|
280
|
+
[
|
|
281
|
+
...path,
|
|
282
|
+
token
|
|
283
|
+
],
|
|
284
|
+
lifetime,
|
|
285
|
+
record.owner
|
|
286
|
+
);
|
|
287
|
+
cache?.set(
|
|
288
|
+
token.id,
|
|
289
|
+
resolution
|
|
290
|
+
);
|
|
291
|
+
try {
|
|
292
|
+
return await resolution;
|
|
293
|
+
} catch (error) {
|
|
294
|
+
cache?.delete(token.id);
|
|
295
|
+
if (error instanceof ServiceNotFoundError || error instanceof ServiceResolutionError) {
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
throw new ServiceResolutionError(
|
|
299
|
+
token,
|
|
300
|
+
error instanceof Error ? error.message : String(error),
|
|
301
|
+
error
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async instantiate(provider, path, lifetime, providerOwner) {
|
|
306
|
+
const dependencies = await Promise.all(
|
|
307
|
+
(provider.dependencies ?? []).map(
|
|
308
|
+
(dependency) => this.resolveWithPath(
|
|
309
|
+
dependency,
|
|
310
|
+
path
|
|
311
|
+
)
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
const context = {
|
|
315
|
+
scope: this,
|
|
316
|
+
resolve: (dependency) => this.resolveWithPath(
|
|
317
|
+
dependency,
|
|
318
|
+
path
|
|
319
|
+
),
|
|
320
|
+
optional: async (dependency) => {
|
|
321
|
+
if (!this.has(dependency)) {
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
return this.resolveWithPath(
|
|
325
|
+
dependency,
|
|
326
|
+
path
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const value = await provider.factory(
|
|
331
|
+
context,
|
|
332
|
+
dependencies
|
|
333
|
+
);
|
|
334
|
+
if (provider.dispose) {
|
|
335
|
+
const disposalOwner = lifetime === "singleton" ? providerOwner : this;
|
|
336
|
+
disposalOwner.disposals.push({
|
|
337
|
+
provider,
|
|
338
|
+
value
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return value;
|
|
342
|
+
}
|
|
343
|
+
findProvider(token) {
|
|
344
|
+
let current = this;
|
|
345
|
+
while (current) {
|
|
346
|
+
const override = current.overrideProviders.get(
|
|
347
|
+
token.id
|
|
348
|
+
);
|
|
349
|
+
if (override) {
|
|
350
|
+
return {
|
|
351
|
+
provider: override,
|
|
352
|
+
owner: current,
|
|
353
|
+
overridden: true
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
current = current.parent;
|
|
357
|
+
}
|
|
358
|
+
const provider = this.root.providersMap.get(
|
|
359
|
+
token.id
|
|
360
|
+
);
|
|
361
|
+
if (!provider) {
|
|
362
|
+
return void 0;
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
provider,
|
|
366
|
+
owner: this.root,
|
|
367
|
+
overridden: false
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
async disposeInternal() {
|
|
371
|
+
this.state = "disposing";
|
|
372
|
+
const errors = [];
|
|
373
|
+
for (const child of Array.from(
|
|
374
|
+
this.children
|
|
375
|
+
).reverse()) {
|
|
376
|
+
try {
|
|
377
|
+
await child.dispose();
|
|
378
|
+
} catch (error) {
|
|
379
|
+
errors.push(error);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
for (const record of [...this.disposals].reverse()) {
|
|
383
|
+
if (!record.provider.dispose) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
await record.provider.dispose(
|
|
388
|
+
record.value
|
|
389
|
+
);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
errors.push(error);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
this.disposals.length = 0;
|
|
395
|
+
this.singletonCache.clear();
|
|
396
|
+
this.scopedCache.clear();
|
|
397
|
+
this.children.clear();
|
|
398
|
+
this.parent?.children.delete(this);
|
|
399
|
+
this.state = "disposed";
|
|
400
|
+
if (errors.length > 0) {
|
|
401
|
+
throw new ServiceDisposalError(
|
|
402
|
+
errors
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
hasResolved(id) {
|
|
407
|
+
if (this.singletonCache.has(id) || this.scopedCache.has(id)) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
for (const child of this.children) {
|
|
411
|
+
if (child.hasResolved(id)) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
assertActive() {
|
|
418
|
+
if (this.state !== "active") {
|
|
419
|
+
throw new Error(
|
|
420
|
+
`BCP Container: scope "${this.name}" is ${this.state}.`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
function validateProvider(provider) {
|
|
426
|
+
if (!provider || typeof provider !== "object") {
|
|
427
|
+
throw new TypeError(
|
|
428
|
+
"BCP Container: provider must be an object."
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
assertToken(provider.token);
|
|
432
|
+
if (typeof provider.factory !== "function") {
|
|
433
|
+
throw new TypeError(
|
|
434
|
+
`BCP Container: provider "${provider.token.description}" must define factory().`
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
normalizeLifetime(
|
|
438
|
+
provider.lifetime
|
|
439
|
+
);
|
|
440
|
+
assertDependencies(
|
|
441
|
+
provider.dependencies ?? []
|
|
442
|
+
);
|
|
443
|
+
if (provider.dispose !== void 0 && typeof provider.dispose !== "function") {
|
|
444
|
+
throw new TypeError(
|
|
445
|
+
`BCP Container: provider "${provider.token.description}" dispose must be a function.`
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function assertToken(token) {
|
|
450
|
+
if (!token || typeof token !== "object" || typeof token.id !== "symbol" || typeof token.description !== "string" || token.description.trim() === "") {
|
|
451
|
+
throw new TypeError(
|
|
452
|
+
"BCP Container: invalid service token. Use createServiceToken()."
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function assertDependencies(dependencies) {
|
|
457
|
+
if (!Array.isArray(dependencies)) {
|
|
458
|
+
throw new TypeError(
|
|
459
|
+
"BCP Container: provider dependencies must be an array."
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
for (const dependency of dependencies) {
|
|
463
|
+
assertToken(dependency);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function normalizeLifetime(lifetime) {
|
|
467
|
+
const normalized = lifetime ?? "singleton";
|
|
468
|
+
if (normalized !== "singleton" && normalized !== "scoped" && normalized !== "transient") {
|
|
469
|
+
throw new TypeError(
|
|
470
|
+
"BCP Container: service lifetime must be singleton, scoped or transient."
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
return normalized;
|
|
474
|
+
}
|
|
475
|
+
function normalizeName(value, label) {
|
|
476
|
+
if (typeof value !== "string") {
|
|
477
|
+
throw new TypeError(
|
|
478
|
+
`BCP Container: ${label} must be a string.`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
const normalized = value.trim();
|
|
482
|
+
if (!normalized) {
|
|
483
|
+
throw new TypeError(
|
|
484
|
+
`BCP Container: ${label} cannot be empty.`
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
if (normalized.length > 256 || /[\r\n]/.test(normalized)) {
|
|
488
|
+
throw new TypeError(
|
|
489
|
+
`BCP Container: ${label} must be at most 256 characters without line breaks.`
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
return normalized;
|
|
493
|
+
}
|
|
494
|
+
function normalizeOptionalName(value) {
|
|
495
|
+
if (value === void 0) {
|
|
496
|
+
return void 0;
|
|
497
|
+
}
|
|
498
|
+
return normalizeName(
|
|
499
|
+
value,
|
|
500
|
+
"scope name"
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
1
504
|
// packages/server/src/deployment.ts
|
|
2
505
|
import {
|
|
3
506
|
randomUUID
|
|
@@ -73,7 +576,7 @@ function createDeploymentRuntime(options) {
|
|
|
73
576
|
"runtime start timestamp"
|
|
74
577
|
);
|
|
75
578
|
const metadata = {
|
|
76
|
-
serviceName:
|
|
579
|
+
serviceName: normalizeName2(
|
|
77
580
|
options.serviceName,
|
|
78
581
|
"serviceName"
|
|
79
582
|
),
|
|
@@ -84,7 +587,7 @@ function createDeploymentRuntime(options) {
|
|
|
84
587
|
options.version
|
|
85
588
|
)
|
|
86
589
|
} : {},
|
|
87
|
-
deploymentId:
|
|
590
|
+
deploymentId: normalizeName2(
|
|
88
591
|
options.deploymentId ?? environment.BCP_DEPLOYMENT_ID ?? idFactory(),
|
|
89
592
|
"deploymentId"
|
|
90
593
|
),
|
|
@@ -132,7 +635,7 @@ function createDeploymentRuntime(options) {
|
|
|
132
635
|
"BCP Deployment: resources can only be registered before runtime start."
|
|
133
636
|
);
|
|
134
637
|
}
|
|
135
|
-
const name =
|
|
638
|
+
const name = normalizeName2(
|
|
136
639
|
resource.name,
|
|
137
640
|
"resource name"
|
|
138
641
|
);
|
|
@@ -496,7 +999,7 @@ function normalizeSignals(signals) {
|
|
|
496
999
|
new Set(signals)
|
|
497
1000
|
);
|
|
498
1001
|
}
|
|
499
|
-
function
|
|
1002
|
+
function normalizeName2(value, field) {
|
|
500
1003
|
const normalized = String(value ?? "").trim();
|
|
501
1004
|
if (!normalized) {
|
|
502
1005
|
throw new TypeError(
|
|
@@ -606,7 +1109,7 @@ function defineModule(module) {
|
|
|
606
1109
|
"BCP Plugins: module must be an object."
|
|
607
1110
|
);
|
|
608
1111
|
}
|
|
609
|
-
|
|
1112
|
+
normalizeName3(
|
|
610
1113
|
module.name,
|
|
611
1114
|
"module name"
|
|
612
1115
|
);
|
|
@@ -676,7 +1179,7 @@ function createPluginHookBus() {
|
|
|
676
1179
|
const hooks = /* @__PURE__ */ new Map();
|
|
677
1180
|
const bus = {
|
|
678
1181
|
on(name, handler) {
|
|
679
|
-
const normalized =
|
|
1182
|
+
const normalized = normalizeName3(
|
|
680
1183
|
name,
|
|
681
1184
|
"hook name"
|
|
682
1185
|
);
|
|
@@ -706,7 +1209,7 @@ function createPluginHookBus() {
|
|
|
706
1209
|
};
|
|
707
1210
|
},
|
|
708
1211
|
async emit(name, payload) {
|
|
709
|
-
const normalized =
|
|
1212
|
+
const normalized = normalizeName3(
|
|
710
1213
|
name,
|
|
711
1214
|
"hook name"
|
|
712
1215
|
);
|
|
@@ -722,7 +1225,7 @@ function createPluginHookBus() {
|
|
|
722
1225
|
},
|
|
723
1226
|
listenerCount(name) {
|
|
724
1227
|
return hooks.get(
|
|
725
|
-
|
|
1228
|
+
normalizeName3(
|
|
726
1229
|
name,
|
|
727
1230
|
"hook name"
|
|
728
1231
|
)
|
|
@@ -734,7 +1237,7 @@ function createPluginHookBus() {
|
|
|
734
1237
|
return;
|
|
735
1238
|
}
|
|
736
1239
|
hooks.delete(
|
|
737
|
-
|
|
1240
|
+
normalizeName3(
|
|
738
1241
|
name,
|
|
739
1242
|
"hook name"
|
|
740
1243
|
)
|
|
@@ -967,7 +1470,7 @@ function createPluginHost(options = {}) {
|
|
|
967
1470
|
},
|
|
968
1471
|
plugin(name) {
|
|
969
1472
|
const entry = entries.get(
|
|
970
|
-
|
|
1473
|
+
normalizeName3(
|
|
971
1474
|
name,
|
|
972
1475
|
"plugin name"
|
|
973
1476
|
)
|
|
@@ -1001,7 +1504,7 @@ function createPluginHost(options = {}) {
|
|
|
1001
1504
|
validatePluginDefinition(
|
|
1002
1505
|
definition
|
|
1003
1506
|
);
|
|
1004
|
-
const name =
|
|
1507
|
+
const name = normalizeName3(
|
|
1005
1508
|
definition.name,
|
|
1006
1509
|
"plugin name"
|
|
1007
1510
|
);
|
|
@@ -1218,7 +1721,7 @@ function validatePluginDefinition(definition) {
|
|
|
1218
1721
|
"BCP Plugins: plugin definition must be an object."
|
|
1219
1722
|
);
|
|
1220
1723
|
}
|
|
1221
|
-
const name =
|
|
1724
|
+
const name = normalizeName3(
|
|
1222
1725
|
definition.name,
|
|
1223
1726
|
"plugin name"
|
|
1224
1727
|
);
|
|
@@ -1261,7 +1764,7 @@ function normalizeDependencyList(value, plugin, field) {
|
|
|
1261
1764
|
);
|
|
1262
1765
|
}
|
|
1263
1766
|
const normalized = value.map(
|
|
1264
|
-
(item) =>
|
|
1767
|
+
(item) => normalizeName3(
|
|
1265
1768
|
item,
|
|
1266
1769
|
`${field} dependency`
|
|
1267
1770
|
)
|
|
@@ -1322,7 +1825,7 @@ function isPluginModule(value) {
|
|
|
1322
1825
|
)
|
|
1323
1826
|
);
|
|
1324
1827
|
}
|
|
1325
|
-
function
|
|
1828
|
+
function normalizeName3(value, field) {
|
|
1326
1829
|
const text = String(value ?? "").trim();
|
|
1327
1830
|
if (!text) {
|
|
1328
1831
|
throw new TypeError(
|
|
@@ -1340,14 +1843,14 @@ function normalizeOptionalVersion(value) {
|
|
|
1340
1843
|
if (value === void 0) {
|
|
1341
1844
|
return void 0;
|
|
1342
1845
|
}
|
|
1343
|
-
return
|
|
1846
|
+
return normalizeName3(
|
|
1344
1847
|
value,
|
|
1345
1848
|
"plugin version"
|
|
1346
1849
|
);
|
|
1347
1850
|
}
|
|
1348
1851
|
function assertServiceKey(key) {
|
|
1349
1852
|
if (typeof key === "string") {
|
|
1350
|
-
|
|
1853
|
+
normalizeName3(
|
|
1351
1854
|
key,
|
|
1352
1855
|
"service key"
|
|
1353
1856
|
);
|
|
@@ -1399,7 +1902,7 @@ function defineApp(definition) {
|
|
|
1399
1902
|
}
|
|
1400
1903
|
function createApp(definition) {
|
|
1401
1904
|
defineApp(definition);
|
|
1402
|
-
const name =
|
|
1905
|
+
const name = normalizeName4(
|
|
1403
1906
|
definition.name,
|
|
1404
1907
|
"application name"
|
|
1405
1908
|
);
|
|
@@ -1410,6 +1913,10 @@ function createApp(definition) {
|
|
|
1410
1913
|
definition.schema,
|
|
1411
1914
|
definition.config
|
|
1412
1915
|
);
|
|
1916
|
+
const container = createServiceContainer({
|
|
1917
|
+
name: `${name}:container`,
|
|
1918
|
+
providers: definition.providers
|
|
1919
|
+
});
|
|
1413
1920
|
const plugins = createPluginHost({
|
|
1414
1921
|
plugins: definition.plugins,
|
|
1415
1922
|
modules: definition.modules,
|
|
@@ -1436,6 +1943,7 @@ function createApp(definition) {
|
|
|
1436
1943
|
version
|
|
1437
1944
|
} : {},
|
|
1438
1945
|
config,
|
|
1946
|
+
container,
|
|
1439
1947
|
services: plugins.services,
|
|
1440
1948
|
hooks: plugins.hooks,
|
|
1441
1949
|
plugins,
|
|
@@ -1447,6 +1955,29 @@ function createApp(definition) {
|
|
|
1447
1955
|
return state;
|
|
1448
1956
|
}
|
|
1449
1957
|
};
|
|
1958
|
+
deployment.addResource({
|
|
1959
|
+
name: "bcp:container",
|
|
1960
|
+
ready() {
|
|
1961
|
+
return {
|
|
1962
|
+
ok: container.state === "active",
|
|
1963
|
+
detail: `Service container is ${container.state}.`
|
|
1964
|
+
};
|
|
1965
|
+
},
|
|
1966
|
+
async stop() {
|
|
1967
|
+
await container.dispose();
|
|
1968
|
+
},
|
|
1969
|
+
diagnostics() {
|
|
1970
|
+
return {
|
|
1971
|
+
state: container.state,
|
|
1972
|
+
providers: container.graph().map(
|
|
1973
|
+
(node) => ({
|
|
1974
|
+
token: node.description,
|
|
1975
|
+
lifetime: node.lifetime
|
|
1976
|
+
})
|
|
1977
|
+
)
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
});
|
|
1450
1981
|
deployment.addResource({
|
|
1451
1982
|
name: "bcp:plugins",
|
|
1452
1983
|
async start() {
|
|
@@ -1478,6 +2009,7 @@ function createApp(definition) {
|
|
|
1478
2009
|
} : {},
|
|
1479
2010
|
config,
|
|
1480
2011
|
context,
|
|
2012
|
+
container,
|
|
1481
2013
|
services: plugins.services,
|
|
1482
2014
|
hooks: plugins.hooks,
|
|
1483
2015
|
plugins,
|
|
@@ -1499,6 +2031,19 @@ function createApp(definition) {
|
|
|
1499
2031
|
);
|
|
1500
2032
|
return app;
|
|
1501
2033
|
},
|
|
2034
|
+
register(provider, options = {}) {
|
|
2035
|
+
assertMutable();
|
|
2036
|
+
container.register(
|
|
2037
|
+
provider,
|
|
2038
|
+
options
|
|
2039
|
+
);
|
|
2040
|
+
return app;
|
|
2041
|
+
},
|
|
2042
|
+
createScope(options = {}) {
|
|
2043
|
+
return container.createScope(
|
|
2044
|
+
options
|
|
2045
|
+
);
|
|
2046
|
+
},
|
|
1502
2047
|
addResource(resource) {
|
|
1503
2048
|
assertMutable();
|
|
1504
2049
|
deployment.addResource(
|
|
@@ -1675,6 +2220,13 @@ function createApp(definition) {
|
|
|
1675
2220
|
errors.push(error);
|
|
1676
2221
|
}
|
|
1677
2222
|
}
|
|
2223
|
+
if (container.state !== "disposed") {
|
|
2224
|
+
try {
|
|
2225
|
+
await container.dispose();
|
|
2226
|
+
} catch (error) {
|
|
2227
|
+
errors.push(error);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
1678
2230
|
try {
|
|
1679
2231
|
await disposeApplication();
|
|
1680
2232
|
} catch (error) {
|
|
@@ -1722,6 +2274,9 @@ function createApp(definition) {
|
|
|
1722
2274
|
services: plugins.services.keys().map(
|
|
1723
2275
|
formatServiceKey2
|
|
1724
2276
|
),
|
|
2277
|
+
containerProviders: container.graph().map(
|
|
2278
|
+
(node) => node.description
|
|
2279
|
+
),
|
|
1725
2280
|
pluginCount: plugins.plugins().length
|
|
1726
2281
|
};
|
|
1727
2282
|
}
|
|
@@ -1741,6 +2296,12 @@ function createApp(definition) {
|
|
|
1741
2296
|
await plugins.close();
|
|
1742
2297
|
} catch {
|
|
1743
2298
|
}
|
|
2299
|
+
if (container.state !== "disposed") {
|
|
2300
|
+
try {
|
|
2301
|
+
await container.dispose();
|
|
2302
|
+
} catch {
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
1744
2305
|
try {
|
|
1745
2306
|
await disposeApplication();
|
|
1746
2307
|
} catch {
|
|
@@ -1772,7 +2333,7 @@ function createApp(definition) {
|
|
|
1772
2333
|
function assertMutable() {
|
|
1773
2334
|
if (state !== "created") {
|
|
1774
2335
|
throw new Error(
|
|
1775
|
-
"BCP Application: plugins, services and resources must be registered before start()."
|
|
2336
|
+
"BCP Application: plugins, services, providers and resources must be registered before start()."
|
|
1776
2337
|
);
|
|
1777
2338
|
}
|
|
1778
2339
|
}
|
|
@@ -1797,13 +2358,18 @@ function validateDefinition(definition) {
|
|
|
1797
2358
|
"BCP Application: definition must be an object."
|
|
1798
2359
|
);
|
|
1799
2360
|
}
|
|
1800
|
-
|
|
2361
|
+
normalizeName4(
|
|
1801
2362
|
definition.name,
|
|
1802
2363
|
"application name"
|
|
1803
2364
|
);
|
|
1804
2365
|
normalizeOptionalText2(
|
|
1805
2366
|
definition.version
|
|
1806
2367
|
);
|
|
2368
|
+
if (definition.providers !== void 0 && !Array.isArray(definition.providers)) {
|
|
2369
|
+
throw new TypeError(
|
|
2370
|
+
"BCP Application: providers must be an array."
|
|
2371
|
+
);
|
|
2372
|
+
}
|
|
1807
2373
|
if (definition.plugins !== void 0 && !Array.isArray(definition.plugins)) {
|
|
1808
2374
|
throw new TypeError(
|
|
1809
2375
|
"BCP Application: plugins must be an array."
|
|
@@ -1832,7 +2398,7 @@ function validateDefinition(definition) {
|
|
|
1832
2398
|
}
|
|
1833
2399
|
}
|
|
1834
2400
|
}
|
|
1835
|
-
function
|
|
2401
|
+
function normalizeName4(value, label) {
|
|
1836
2402
|
if (typeof value !== "string") {
|
|
1837
2403
|
throw new TypeError(
|
|
1838
2404
|
`BCP Application: ${label} must be a string.`
|