@afora/core 0.0.3 → 0.0.5

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.
@@ -10,6 +10,7 @@ import {
10
10
  isRecordClass2,
11
11
  recordFieldOf2,
12
12
  isBlueprintList2,
13
+ UNEXPECTED_ERROR2,
13
14
  Exception2,
14
15
  isDeclaredException,
15
16
  declaredMessageOf,
@@ -50,17 +51,38 @@ __export(exports_environment, {
50
51
  loadEnvironments: () => loadEnvironments
51
52
  });
52
53
 
53
- // src/core/entity/exceptions.ts
54
+ // src/core/application/exceptions.ts
54
55
  var exports_exceptions4 = {};
55
56
  __export(exports_exceptions4, {
57
+ ApplicationAlreadyStarted: () => ApplicationAlreadyStarted2,
58
+ ApplicationConflict: () => ApplicationConflict2,
59
+ ApplicationNotStarted: () => ApplicationNotStarted2,
60
+ DuplicateApplicationDeclaration: () => DuplicateApplicationDeclaration2,
61
+ InvalidApplicationDeclaration: () => InvalidApplicationDeclaration2,
62
+ InvalidApplicationEnvironment: () => InvalidApplicationEnvironment2,
63
+ ProviderNotBound: () => ProviderNotBound2,
64
+ UnregisteredApplicationReference: () => UnregisteredApplicationReference2,
65
+ UnregisteredRepositoryDependency: () => UnregisteredRepositoryDependency2,
66
+ UnregisteredUseCaseDependency: () => UnregisteredUseCaseDependency2
67
+ });
68
+
69
+ // src/core/logger/exceptions.ts
70
+ var exports_exceptions5 = {};
71
+ __export(exports_exceptions5, {
72
+ InvalidLogDeclaration: () => InvalidLogDeclaration2
73
+ });
74
+
75
+ // src/core/entity/exceptions.ts
76
+ var exports_exceptions6 = {};
77
+ __export(exports_exceptions6, {
56
78
  DeletedEntity: () => DeletedEntity2,
57
79
  ImmutableEntityKey: () => ImmutableEntityKey2,
58
80
  ReservedEntityKey: () => ReservedEntityKey2
59
81
  });
60
82
 
61
83
  // src/core/repository/exceptions.ts
62
- var exports_exceptions5 = {};
63
- __export(exports_exceptions5, {
84
+ var exports_exceptions7 = {};
85
+ __export(exports_exceptions7, {
64
86
  InvalidRepositoryData: () => InvalidRepositoryData2,
65
87
  InvalidRepositoryDeclaration: () => InvalidRepositoryDeclaration2,
66
88
  InvalidRepositoryEntity: () => InvalidRepositoryEntity2,
@@ -82,14 +104,14 @@ __export(exports_use_case, {
82
104
  });
83
105
 
84
106
  // src/core/use-case/exceptions.ts
85
- var exports_exceptions6 = {};
86
- __export(exports_exceptions6, {
107
+ var exports_exceptions8 = {};
108
+ __export(exports_exceptions8, {
87
109
  InvalidUseCaseStep: () => InvalidUseCaseStep2
88
110
  });
89
111
 
90
112
  // src/core/command/exceptions.ts
91
- var exports_exceptions7 = {};
92
- __export(exports_exceptions7, {
113
+ var exports_exceptions9 = {};
114
+ __export(exports_exceptions9, {
93
115
  CommandEventFailed: () => CommandEventFailed2,
94
116
  InvalidCommandContext: () => InvalidCommandContext2,
95
117
  InvalidCommandDeclaration: () => InvalidCommandDeclaration2,
@@ -97,29 +119,15 @@ __export(exports_exceptions7, {
97
119
  UnknownCommandOperation: () => UnknownCommandOperation2
98
120
  });
99
121
 
100
- // src/core/application/exceptions.ts
101
- var exports_exceptions8 = {};
102
- __export(exports_exceptions8, {
103
- ApplicationAlreadyStarted: () => ApplicationAlreadyStarted2,
104
- ApplicationConflict: () => ApplicationConflict2,
105
- ApplicationNotStarted: () => ApplicationNotStarted2,
106
- DuplicateApplicationDeclaration: () => DuplicateApplicationDeclaration2,
107
- InvalidApplicationDeclaration: () => InvalidApplicationDeclaration2,
108
- InvalidApplicationEnvironment: () => InvalidApplicationEnvironment2,
109
- UnregisteredApplicationReference: () => UnregisteredApplicationReference2,
110
- UnregisteredRepositoryDependency: () => UnregisteredRepositoryDependency2,
111
- UnregisteredUseCaseDependency: () => UnregisteredUseCaseDependency2
112
- });
113
-
114
122
  // src/core/openapi/exceptions.ts
115
- var exports_exceptions9 = {};
116
- __export(exports_exceptions9, {
123
+ var exports_exceptions10 = {};
124
+ __export(exports_exceptions10, {
117
125
  InvalidOpenAPIModel: () => InvalidOpenAPIModel2
118
126
  });
119
127
 
120
128
  // src/core/output/exceptions.ts
121
- var exports_exceptions10 = {};
122
- __export(exports_exceptions10, {
129
+ var exports_exceptions11 = {};
130
+ __export(exports_exceptions11, {
123
131
  DuplicateOperation: () => DuplicateOperation2,
124
132
  InvalidOutputStage: () => InvalidOutputStage2
125
133
  });
@@ -209,6 +217,288 @@ function guard2(scope, run, resolve) {
209
217
  }
210
218
  }
211
219
 
220
+ // src/core/application/exceptions.ts
221
+ var InvalidApplicationDeclaration2 = exceptionOf2({
222
+ layer: "application" /* Application */,
223
+ name: "InvalidApplicationDeclaration",
224
+ message: ({ reason }) => reason
225
+ });
226
+ var DuplicateApplicationDeclaration2 = exceptionOf2({
227
+ layer: "application" /* Application */,
228
+ name: "DuplicateApplicationDeclaration",
229
+ message: ({
230
+ first,
231
+ second
232
+ }) => `The same object is registered as ${first} and as ${second}.`
233
+ });
234
+ var ApplicationConflict2 = exceptionOf2({
235
+ layer: "application" /* Application */,
236
+ name: "ApplicationConflict",
237
+ message: ({
238
+ reason
239
+ }) => reason
240
+ });
241
+ var UnregisteredApplicationReference2 = exceptionOf2({
242
+ layer: "application" /* Application */,
243
+ name: "UnregisteredApplicationReference",
244
+ message: ({
245
+ holder,
246
+ reference
247
+ }) => `${holder} holds ${reference}, which is not registered in the application.`
248
+ });
249
+ var UnregisteredRepositoryDependency2 = exceptionOf2({
250
+ layer: "application" /* Application */,
251
+ name: "UnregisteredRepositoryDependency",
252
+ message: ({
253
+ repository,
254
+ dependency
255
+ }) => `Repository ${repository} depends on ${dependency}, which is not registered in the application.`
256
+ });
257
+ var UnregisteredUseCaseDependency2 = exceptionOf2({
258
+ layer: "application" /* Application */,
259
+ name: "UnregisteredUseCaseDependency",
260
+ message: ({
261
+ useCase,
262
+ dependency
263
+ }) => `Use case ${useCase} depends on ${dependency}, which is not registered in the application.`
264
+ });
265
+ var ApplicationNotStarted2 = exceptionOf2({
266
+ layer: "application" /* Application */,
267
+ name: "ApplicationNotStarted",
268
+ message: ({
269
+ application
270
+ }) => application === undefined ? "No application is started: call await app.start() first." : `The application "${application}" is not started: call await app.start() first.`
271
+ });
272
+ var ProviderNotBound2 = exceptionOf2({
273
+ layer: "application" /* Application */,
274
+ name: "ProviderNotBound",
275
+ message: ({
276
+ provider
277
+ }) => `The provider "${provider}" is made by an application: register its storage in applicationOf({ providers }) and call the repository through the application.`
278
+ });
279
+ var ApplicationAlreadyStarted2 = exceptionOf2({
280
+ layer: "application" /* Application */,
281
+ name: "ApplicationAlreadyStarted",
282
+ message: ({
283
+ current,
284
+ attempted
285
+ }) => `The application "${current}" is already started, so "${attempted}" cannot be: stop it, or call start({ replace: true }).`
286
+ });
287
+ var InvalidApplicationEnvironment2 = exceptionOf2({
288
+ layer: "environment" /* Environment */,
289
+ name: "InvalidApplicationEnvironment",
290
+ message: ({
291
+ environments
292
+ }) => `Invalid environment variables: ${Object.entries(environments).map(([name, keys]) => `${name} (${keys.join(", ")})`).join("; ")}.`
293
+ });
294
+
295
+ // src/core/application/registry.ts
296
+ var SLOT = Symbol.for("afora.application");
297
+ function slot() {
298
+ const global = globalThis;
299
+ global[SLOT] ??= { current: undefined };
300
+ return global[SLOT];
301
+ }
302
+ function useApplication2() {
303
+ const { current } = slot();
304
+ if (current === undefined) {
305
+ throw new ApplicationNotStarted2({ source: "useApplication", details: {} });
306
+ }
307
+ return current;
308
+ }
309
+ function resetApplication2() {
310
+ slot().current = undefined;
311
+ }
312
+ function startedApplication() {
313
+ return slot().current;
314
+ }
315
+ function setStartedApplication(application) {
316
+ slot().current = application;
317
+ }
318
+
319
+ // src/core/logger/exceptions.ts
320
+ var InvalidLogDeclaration2 = exceptionOf2({
321
+ layer: "application" /* Application */,
322
+ name: "InvalidLogDeclaration",
323
+ message: ({ reason }) => reason
324
+ });
325
+
326
+ // src/core/logger/logger.ts
327
+ var LogLevels2 = Object.freeze([
328
+ "debug",
329
+ "info",
330
+ "warn",
331
+ "error"
332
+ ]);
333
+ var names = new WeakMap;
334
+ var invalid = (reason) => {
335
+ throw new InvalidLogDeclaration2({ source: "log", details: { reason } });
336
+ };
337
+ function logProviderOf2(settings) {
338
+ if (!settings || typeof settings.write !== "function") {
339
+ invalid("Expected a log provider with a write function.");
340
+ }
341
+ if (settings.flush !== undefined && typeof settings.flush !== "function") {
342
+ invalid("Expected flush to be a function.");
343
+ }
344
+ if (settings.name !== undefined && (typeof settings.name !== "string" || settings.name.trim() === "")) {
345
+ invalid("Expected the name of a log provider to be a non-empty string.");
346
+ }
347
+ const provider = Object.freeze({
348
+ write: settings.write,
349
+ ...settings.flush ? { flush: settings.flush } : {}
350
+ });
351
+ if (settings.name)
352
+ names.set(provider, settings.name);
353
+ return provider;
354
+ }
355
+ function logProviderNameOf(provider) {
356
+ return names.get(provider);
357
+ }
358
+ var fieldsText = (fields) => Object.keys(fields).length > 0 ? ` ${JSON.stringify(fields)}` : "";
359
+ function consoleLogProvider2() {
360
+ return logProviderOf2({
361
+ name: "console",
362
+ write: ({ at, level, event, fields, application }) => {
363
+ const line = `${at} ${level.toUpperCase()} [${application}] ${event}${fieldsText(fields)}`;
364
+ (level === "debug" ? console.debug : level === "info" ? console.info : level === "warn" ? console.warn : console.error)(line);
365
+ }
366
+ });
367
+ }
368
+ function jsonLogProvider2() {
369
+ return logProviderOf2({
370
+ name: "json",
371
+ write: (entry) => {
372
+ console.log(JSON.stringify({
373
+ at: entry.at,
374
+ level: entry.level,
375
+ application: entry.application,
376
+ event: entry.event,
377
+ ...entry.fields
378
+ }));
379
+ }
380
+ });
381
+ }
382
+ function memoryLogProvider2() {
383
+ const entries = [];
384
+ const provider = Object.freeze({
385
+ write: (entry) => {
386
+ entries.push(entry);
387
+ },
388
+ entries,
389
+ clear: () => {
390
+ entries.length = 0;
391
+ }
392
+ });
393
+ names.set(provider, "memory");
394
+ return provider;
395
+ }
396
+ function checkLogSettings(settings) {
397
+ if (typeof settings !== "object" || settings === null) {
398
+ return invalid("Expected `log` to be { level?, providers }.");
399
+ }
400
+ const { level = "info", providers } = settings;
401
+ if (!LogLevels2.includes(level)) {
402
+ invalid(`Expected the log level to be one of ${LogLevels2.join(", ")}.`);
403
+ }
404
+ if (!Array.isArray(providers) || providers.some((provider) => typeof provider !== "object" || provider === null || typeof provider.write !== "function")) {
405
+ invalid("Expected `log.providers` to be a list of log providers.");
406
+ }
407
+ return Object.freeze({ level, providers: Object.freeze([...providers]) });
408
+ }
409
+ var silentLogger = Object.freeze({
410
+ debug: () => {},
411
+ info: () => {},
412
+ warn: () => {},
413
+ error: () => {}
414
+ });
415
+ var rank = (level) => LogLevels2.indexOf(level);
416
+ function applicationLogger(application) {
417
+ let current;
418
+ const write = (level) => (event, fields = {}) => {
419
+ if (!current || rank(level) < rank(current.level))
420
+ return;
421
+ const entry = Object.freeze({
422
+ at: new Date().toISOString(),
423
+ level,
424
+ event,
425
+ fields,
426
+ application
427
+ });
428
+ for (const provider of current.providers) {
429
+ try {
430
+ const written = provider.write(entry);
431
+ if (written && typeof written.catch === "function") {
432
+ written.catch(() => {});
433
+ }
434
+ } catch {}
435
+ }
436
+ };
437
+ const logger = Object.freeze({
438
+ debug: write("debug"),
439
+ info: write("info"),
440
+ warn: write("warn"),
441
+ error: write("error")
442
+ });
443
+ return {
444
+ logger,
445
+ get settings() {
446
+ return current;
447
+ },
448
+ set(settings) {
449
+ current = settings;
450
+ },
451
+ async flush() {
452
+ await Promise.all((current?.providers ?? []).map((provider) => Promise.resolve().then(() => provider.flush?.()).catch(() => {})));
453
+ }
454
+ };
455
+ }
456
+ var Logger2 = Object.freeze({
457
+ [Kind2]: "logger"
458
+ });
459
+ function isLoggerDeclaration(value) {
460
+ return value === Logger2;
461
+ }
462
+ function isLogger(value) {
463
+ return typeof value === "object" && value !== null && ["debug", "info", "warn", "error"].every((level) => typeof value[level] === "function");
464
+ }
465
+ var ApplicationLoggerKey = Symbol("afora.application.logger");
466
+ function resolveLogger(scoped, owner) {
467
+ return scoped ?? owner?.[ApplicationLoggerKey] ?? startedApplication()?.[ApplicationLoggerKey];
468
+ }
469
+ function clock() {
470
+ return globalThis.performance?.now() ?? Date.now();
471
+ }
472
+ function elapsed(since) {
473
+ return Math.round(clock() - since);
474
+ }
475
+ var logged = new WeakSet;
476
+ function logFailure(logger, event, fields, error) {
477
+ if (!logger)
478
+ return;
479
+ const exception = error instanceof Exception2 ? error : undefined;
480
+ const key = exception?.origin ?? error;
481
+ if (typeof key === "object" && key !== null) {
482
+ if (logged.has(key))
483
+ return;
484
+ logged.add(key);
485
+ }
486
+ const unexpected = !exception || exception.origin.name === UNEXPECTED_ERROR2;
487
+ try {
488
+ logger[unexpected ? "error" : "warn"](event, {
489
+ ...fields,
490
+ exception: exception ? exception.toJSON() : { message: error instanceof Error ? error.message : String(error) }
491
+ });
492
+ } catch {}
493
+ }
494
+ function writeLog(logger, level, event, fields) {
495
+ if (!logger)
496
+ return;
497
+ try {
498
+ logger[level](event, fields);
499
+ } catch {}
500
+ }
501
+
212
502
  // src/core/entity/exceptions.ts
213
503
  var ReservedEntityKey2 = exceptionOf2({
214
504
  layer: "entity" /* Entity */,
@@ -291,8 +581,10 @@ function eventOf2(name, entityClass, handler) {
291
581
  return Object.freeze({
292
582
  name,
293
583
  entity,
294
- async handle() {
295
- await handler(entity);
584
+ async handle(context) {
585
+ await handler(entity, {
586
+ logger: context?.logger ?? resolveLogger(undefined, undefined) ?? silentLogger
587
+ });
296
588
  }
297
589
  });
298
590
  });
@@ -473,7 +765,7 @@ function frozenJson(value, subject, fail, seen = new Set) {
473
765
  // src/core/repository/provider.ts
474
766
  var descriptions = new WeakMap;
475
767
  var opaque = Object.freeze({ kind: "opaque" });
476
- function invalid(reason) {
768
+ function invalid2(reason) {
477
769
  throw new InvalidRepositoryDeclaration2({
478
770
  source: "providerOf",
479
771
  details: { reason }
@@ -481,13 +773,13 @@ function invalid(reason) {
481
773
  }
482
774
  function providerOf2(settings) {
483
775
  if (!settings || typeof settings.name !== "string" || settings.name.trim().length === 0 || typeof settings.create !== "function")
484
- invalid("Expected a provider name and create function.");
485
- const options = frozenJson(settings.options ?? {}, "Provider options", invalid);
776
+ invalid2("Expected a provider name and create function.");
777
+ const options = frozenJson(settings.options ?? {}, "Provider options", invalid2);
486
778
  if (options === null || typeof options !== "object" || Array.isArray(options))
487
- invalid("Provider options must be an object.");
779
+ invalid2("Provider options must be an object.");
488
780
  const provider = settings.create(options);
489
781
  if (!provider || typeof provider !== "object" || ["findOne", "findMany", "insert", "update", "remove", "count"].some((key) => typeof provider[key] !== "function"))
490
- invalid("Expected a repository provider from create.");
782
+ invalid2("Expected a repository provider from create.");
491
783
  descriptions.set(provider, Object.freeze({
492
784
  kind: "provider",
493
785
  name: settings.name,
@@ -576,11 +868,11 @@ function matchesQuery(data, query) {
576
868
  function codePoints(text) {
577
869
  return Array.from(text, (char) => char.codePointAt(0));
578
870
  }
579
- function rank(value) {
871
+ function rank2(value) {
580
872
  return value instanceof Date ? 1 : { number: 0, bigint: 0, string: 2, boolean: 3 }[typeof value] ?? 4;
581
873
  }
582
874
  function compareScalars(a, b) {
583
- const [left, right] = [rank(a), rank(b)];
875
+ const [left, right] = [rank2(a), rank2(b)];
584
876
  if (left !== right)
585
877
  return left - right;
586
878
  if (typeof a === "string" && typeof b === "string") {
@@ -799,31 +1091,31 @@ var origins = new WeakMap;
799
1091
  function declarationOf(state) {
800
1092
  return origins.get(state) ?? state;
801
1093
  }
802
- function bindState(state, provider, bound = new Map) {
1094
+ function bindState(state, provider, instances, bound = new Map) {
803
1095
  const done = bound.get(state);
804
1096
  if (done)
805
1097
  return done;
806
1098
  const copy = {
807
1099
  ...state,
808
- provider: state.explicitProvider ? state.provider : provider,
1100
+ provider: state.explicitProvider ? instances?.get(state.provider) ?? state.provider : provider ?? state.provider,
809
1101
  context: undefined,
810
1102
  relations: new Map([...state.relations].map(([target, related]) => [
811
1103
  target,
812
- bindState(related, provider, bound)
1104
+ bindState(related, provider, instances, bound)
813
1105
  ]))
814
1106
  };
815
1107
  origins.set(copy, declarationOf(state));
816
1108
  bound.set(state, copy);
817
1109
  return copy;
818
1110
  }
819
- function bindRepository(repository, provider) {
1111
+ function bindRepository(repository, provider, instances) {
820
1112
  const state = stateOf(repository);
821
1113
  const Declared = repository.constructor;
822
1114
 
823
1115
  class Bound extends Declared {
824
1116
  }
825
1117
  Object.defineProperty(Bound, "name", { value: Declared.name });
826
- states.set(Bound, bindState(state, provider));
1118
+ states.set(Bound, bindState(state, provider, instances));
827
1119
  return Object.freeze(new Bound);
828
1120
  }
829
1121
 
@@ -1138,25 +1430,25 @@ function unknownAction(entity, action, methods) {
1138
1430
  }
1139
1431
  return `"${action}" is not an action of ${entity}.`;
1140
1432
  }
1141
- function invalid2(source, reason) {
1433
+ function invalid3(source, reason) {
1142
1434
  throw new InvalidRepositoryDeclaration2({ source, details: { reason } });
1143
1435
  }
1144
1436
  function repositoryOf2(settings) {
1145
1437
  const { entity, actions, environmentDependencies } = settings ?? {};
1146
1438
  if (!isEntityClass2(entity) || !(EntityUniqueKeys2 in entity)) {
1147
- invalid2("repositoryOf", "Expected an entity class made by entityOf.");
1439
+ invalid3("repositoryOf", "Expected an entity class made by entityOf.");
1148
1440
  }
1149
1441
  const name = `${entity.name}Repository`;
1150
1442
  const deletion = settings.deletion ?? "hard";
1151
1443
  if (deletion !== "hard" && deletion !== "soft") {
1152
- invalid2(name, 'Expected deletion to be "hard" or "soft".');
1444
+ invalid3(name, 'Expected deletion to be "hard" or "soft".');
1153
1445
  }
1154
1446
  const provider = settings.provider ?? inMemoryProvider2();
1155
1447
  if (!Array.isArray(actions) || actions.length === 0) {
1156
- invalid2(name, "Expected a list of actions.");
1448
+ invalid3(name, "Expected a list of actions.");
1157
1449
  }
1158
1450
  if (environmentDependencies !== undefined && !isEnvironment2(environmentDependencies) && !(Array.isArray(environmentDependencies) && environmentDependencies.every(isEnvironment2))) {
1159
- invalid2(name, "Expected environments made by environmentOf.");
1451
+ invalid3(name, "Expected environments made by environmentOf.");
1160
1452
  }
1161
1453
  const relations = relationsOf(name, entity, settings.relations);
1162
1454
  const unique = entity[EntityUniqueKeys2];
@@ -1182,12 +1474,12 @@ function repositoryOf2(settings) {
1182
1474
  for (const name_ of new Set(actions)) {
1183
1475
  const method = typeof name_ === "string" ? methods.get(name_) : undefined;
1184
1476
  if (!method)
1185
- invalid2(name, unknownAction(entity.name, String(name_), methods));
1477
+ invalid3(name, unknownAction(entity.name, String(name_), methods));
1186
1478
  const [kind, implementation] = method;
1187
1479
  const needs = deletion === "soft" && kind === "delete" ? ["findOne", "update"] : NEEDS[kind] ?? [];
1188
1480
  for (const need of needs) {
1189
1481
  if (typeof provider?.[need] !== "function") {
1190
- invalid2(name, `The provider has no "${need}" for "${name_}".`);
1482
+ invalid3(name, `The provider has no "${need}" for "${name_}".`);
1191
1483
  }
1192
1484
  }
1193
1485
  Object.defineProperty(DeclaredRepository.prototype, name_, {
@@ -1211,7 +1503,7 @@ function repositoryOf2(settings) {
1211
1503
  }
1212
1504
  function RepositoryOf2(repository) {
1213
1505
  if (!isRepository2(repository)) {
1214
- invalid2("RepositoryOf", "Expected a repository made by repositoryOf.");
1506
+ invalid3("RepositoryOf", "Expected a repository made by repositoryOf.");
1215
1507
  }
1216
1508
  return repository.constructor;
1217
1509
  }
@@ -1719,6 +2011,9 @@ var InvalidUseCaseDependencies2 = exceptionOf2({
1719
2011
  name: "InvalidUseCaseDependencies",
1720
2012
  message: "Dependency overrides must contain only declared names: functions for use cases, objects of methods for repositories, and valid variables for environments."
1721
2013
  });
2014
+ function loggerOfCall(state) {
2015
+ return resolveLogger(state.scope?.logger, state.owner);
2016
+ }
1722
2017
  var states2 = new WeakMap;
1723
2018
  function invalidValue(issues, source, phase) {
1724
2019
  throw new Exception2({
@@ -1773,6 +2068,7 @@ function methodNamesOf(repository) {
1773
2068
  }
1774
2069
  function bindRepository2(repository, overrides, state, source) {
1775
2070
  const Entity = repository.entity;
2071
+ const repositoryName = Entity.name;
1776
2072
  const bound = Object.create(null);
1777
2073
  const view = new Proxy(Object.create(repository), {
1778
2074
  get: (_target, key, receiver) => {
@@ -1793,7 +2089,14 @@ function bindRepository2(repository, overrides, state, source) {
1793
2089
  bound[name] = async (...args) => {
1794
2090
  if (!state.active)
1795
2091
  throw new InvalidUseCaseDependencies2({ source });
2092
+ const started = clock();
1796
2093
  const result = await method.apply(receiver, args);
2094
+ writeLog(loggerOfCall(state), "debug", "repository", {
2095
+ repository: repositoryName,
2096
+ method: name,
2097
+ useCase: source,
2098
+ ms: elapsed(started)
2099
+ });
1797
2100
  for (const arg of args)
1798
2101
  pullEntities(arg, Entity, state.events);
1799
2102
  pullEntities(result, Entity, state.events);
@@ -1828,6 +2131,8 @@ function narrowedContext(Child, parent) {
1828
2131
  return picked;
1829
2132
  }
1830
2133
  function isOverrideOf(dependency, value) {
2134
+ if (isLoggerDeclaration(dependency))
2135
+ return isLogger(value);
1831
2136
  if (isRepository2(dependency) || isEnvironment2(dependency)) {
1832
2137
  return typeof value === "object" && value !== null && !Array.isArray(value);
1833
2138
  }
@@ -1873,7 +2178,8 @@ class AbstractUseCase2 {
1873
2178
  started: false,
1874
2179
  active: false,
1875
2180
  events: [],
1876
- dependencies: Object.create(null)
2181
+ dependencies: Object.create(null),
2182
+ owner: this.constructor
1877
2183
  };
1878
2184
  states2.set(this, state);
1879
2185
  for (const [key, dependency] of Object.entries(declaration.dependencies)) {
@@ -1881,6 +2187,13 @@ class AbstractUseCase2 {
1881
2187
  state.dependencies[key] = bindRepository2(dependency, undefined, state, source);
1882
2188
  continue;
1883
2189
  }
2190
+ if (isLoggerDeclaration(dependency)) {
2191
+ Object.defineProperty(state.dependencies, key, {
2192
+ get: () => loggerOfCall(state) ?? silentLogger,
2193
+ enumerable: true
2194
+ });
2195
+ continue;
2196
+ }
1884
2197
  if (isEnvironment2(dependency)) {
1885
2198
  Object.defineProperty(state.dependencies, key, {
1886
2199
  get: () => state.scope?.environments?.get(dependency) ?? dependency.load(),
@@ -1925,6 +2238,10 @@ class AbstractUseCase2 {
1925
2238
  dependencies[key] = bindRepository2(dependency, substitute, state, source);
1926
2239
  continue;
1927
2240
  }
2241
+ if (isLoggerDeclaration(dependency)) {
2242
+ dependencies[key] = substitute;
2243
+ continue;
2244
+ }
1928
2245
  if (isEnvironment2(dependency)) {
1929
2246
  const parsed = dependency.blueprint.safeParse(substitute);
1930
2247
  if (parsed.issues)
@@ -1949,13 +2266,22 @@ class AbstractUseCase2 {
1949
2266
  }
1950
2267
  state.resolved = Object.freeze(dependencies);
1951
2268
  state.active = true;
2269
+ const started = clock();
1952
2270
  try {
1953
2271
  const result = await guard2({ layer: "use-case" /* UseCase */, source }, () => implementation.call(this, state.resolved));
1954
2272
  const parsed = declaration.response.safeParse(result);
1955
2273
  if (parsed.issues)
1956
2274
  invalidValue(parsed.issues, source, "response");
1957
2275
  collectEvents(declaration.response, parsed.value, state.events);
2276
+ writeLog(loggerOfCall(state), "debug", "use-case", {
2277
+ useCase: source,
2278
+ outcome: "ok",
2279
+ ms: elapsed(started)
2280
+ });
1958
2281
  return parsed.value;
2282
+ } catch (error) {
2283
+ logFailure(loggerOfCall(state), "use-case-failed", { useCase: source, ms: elapsed(started) }, error);
2284
+ throw error;
1959
2285
  } finally {
1960
2286
  state.active = false;
1961
2287
  state.resolved = undefined;
@@ -1981,7 +2307,7 @@ function useCaseOf2(settings) {
1981
2307
  throw new InvalidUseCaseDeclaration2({
1982
2308
  source: typeof name === "string" ? name : "useCaseOf",
1983
2309
  details: {
1984
- reason: "Expected a name, input and response blueprints, and UseCase classes, repositories or environments as dependencies."
2310
+ reason: "Expected a name, input and response blueprints, and UseCase classes, repositories, environments or Logger as dependencies."
1985
2311
  }
1986
2312
  });
1987
2313
  }
@@ -2037,7 +2363,7 @@ function useCaseOf2(settings) {
2037
2363
  return DeclaredUseCase;
2038
2364
  }
2039
2365
  function isDependency(value) {
2040
- return isUseCaseClass2(value) || isRepository2(value) || isEnvironment2(value);
2366
+ return isUseCaseClass2(value) || isRepository2(value) || isEnvironment2(value) || isLoggerDeclaration(value);
2041
2367
  }
2042
2368
  function isUseCaseClass2(value) {
2043
2369
  return typeof value === "function" && value.prototype instanceof AbstractUseCase2 && value[Kind2] === "use-case" && typeof value.prototype.execute === "function";
@@ -2085,7 +2411,7 @@ function commandTransactionProvider(command) {
2085
2411
  return verifiers.get(command)?.();
2086
2412
  }
2087
2413
  var IDENTIFIER2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
2088
- function providersOf(useCases, declared) {
2414
+ function repositoryProvidersOf(useCases, declared) {
2089
2415
  const providers = new Map;
2090
2416
  const seen = new Set;
2091
2417
  const add = (state) => {
@@ -2119,7 +2445,7 @@ function providersOf(useCases, declared) {
2119
2445
  return providers;
2120
2446
  }
2121
2447
  function transactionProviderOf(name, useCases, declared = false) {
2122
- const providers = providersOf(useCases, declared);
2448
+ const providers = repositoryProvidersOf(useCases, declared);
2123
2449
  const invalid = (reason, repositories) => {
2124
2450
  throw new InvalidCommandDeclaration2({
2125
2451
  source: name,
@@ -2283,9 +2609,16 @@ function commandOf2(settings) {
2283
2609
  given = narrowed(UseCase, callContext);
2284
2610
  }
2285
2611
  const overrides = options?.env === undefined ? undefined : callEnvironment(where, environments, options.env);
2286
- const withEnvironments = (scope) => overrides === undefined || overrides.byEnvironment.size === 0 ? scope : {
2287
- providers: scope?.providers ?? new Map,
2288
- environments: overrides.byEnvironment
2612
+ const logger = resolveLogger(undefined, UseCase);
2613
+ const withEnvironments = (scope) => {
2614
+ const environmentsOfCall = overrides === undefined || overrides.byEnvironment.size === 0 ? scope?.environments : overrides.byEnvironment;
2615
+ if (!environmentsOfCall && !logger)
2616
+ return scope;
2617
+ return {
2618
+ providers: scope?.providers ?? new Map,
2619
+ ...environmentsOfCall ? { environments: environmentsOfCall } : {},
2620
+ ...logger ? { logger } : {}
2621
+ };
2289
2622
  };
2290
2623
  const run = async (declared) => {
2291
2624
  const scope = withEnvironments(declared);
@@ -2294,11 +2627,20 @@ function commandOf2(settings) {
2294
2627
  return { value, events: useCase.pullEvents() };
2295
2628
  };
2296
2629
  const provider = providerOfCall();
2297
- const outcome = await guard2({ layer: "command" /* Command */, source: where }, () => provider?.transaction ? provider.transaction((scoped) => run({ providers: new Map([[provider, scoped]]) })) : run(undefined));
2630
+ const started = clock();
2631
+ const outcome = await guard2({ layer: "command" /* Command */, source: where }, () => provider?.transaction ? provider.transaction((scoped) => run({ providers: new Map([[provider, scoped]]) })) : run(undefined)).catch((error) => {
2632
+ logFailure(logger, "command-failed", { command: where, ms: elapsed(started) }, error);
2633
+ throw error;
2634
+ });
2635
+ writeLog(logger, "debug", "command", {
2636
+ command: where,
2637
+ ms: elapsed(started)
2638
+ });
2298
2639
  return {
2299
2640
  ...outcome,
2300
2641
  context: callContext,
2301
- env: () => overrides?.merged ?? loadEnvironments(environments)
2642
+ env: () => overrides?.merged ?? loadEnvironments(environments),
2643
+ logger: logger ?? silentLogger
2302
2644
  };
2303
2645
  };
2304
2646
  const command = {
@@ -2325,13 +2667,15 @@ function commandOf2(settings) {
2325
2667
  value,
2326
2668
  events: raised,
2327
2669
  context: callContext,
2328
- env
2670
+ env,
2671
+ logger
2329
2672
  } = await perform(op, input, options);
2330
2673
  await applyPolicy(command, events, onEventError, {
2331
2674
  operation: op,
2332
2675
  raised,
2333
2676
  context: callContext,
2334
- env
2677
+ env,
2678
+ logger
2335
2679
  });
2336
2680
  return value;
2337
2681
  }
@@ -2351,7 +2695,8 @@ async function applyPolicy(command, policy, onEventError, call) {
2351
2695
  context: call.context,
2352
2696
  get env() {
2353
2697
  return call.env();
2354
- }
2698
+ },
2699
+ logger: call.logger
2355
2700
  };
2356
2701
  if (typeof policy === "function") {
2357
2702
  try {
@@ -2367,7 +2712,7 @@ async function applyPolicy(command, policy, onEventError, call) {
2367
2712
  }
2368
2713
  for (const [index, event] of raised.entries()) {
2369
2714
  try {
2370
- await event.handle();
2715
+ await event.handle({ logger: call.logger });
2371
2716
  } catch (cause) {
2372
2717
  if (!onEventError) {
2373
2718
  throw new CommandEventFailed2({
@@ -2402,74 +2747,6 @@ function isCommand2(value) {
2402
2747
  return typeof value === "object" && value !== null && value[Kind2] === "command";
2403
2748
  }
2404
2749
 
2405
- // src/core/application/exceptions.ts
2406
- var InvalidApplicationDeclaration2 = exceptionOf2({
2407
- layer: "application" /* Application */,
2408
- name: "InvalidApplicationDeclaration",
2409
- message: ({ reason }) => reason
2410
- });
2411
- var DuplicateApplicationDeclaration2 = exceptionOf2({
2412
- layer: "application" /* Application */,
2413
- name: "DuplicateApplicationDeclaration",
2414
- message: ({
2415
- first,
2416
- second
2417
- }) => `The same object is registered as ${first} and as ${second}.`
2418
- });
2419
- var ApplicationConflict2 = exceptionOf2({
2420
- layer: "application" /* Application */,
2421
- name: "ApplicationConflict",
2422
- message: ({
2423
- reason
2424
- }) => reason
2425
- });
2426
- var UnregisteredApplicationReference2 = exceptionOf2({
2427
- layer: "application" /* Application */,
2428
- name: "UnregisteredApplicationReference",
2429
- message: ({
2430
- holder,
2431
- reference
2432
- }) => `${holder} holds ${reference}, which is not registered in the application.`
2433
- });
2434
- var UnregisteredRepositoryDependency2 = exceptionOf2({
2435
- layer: "application" /* Application */,
2436
- name: "UnregisteredRepositoryDependency",
2437
- message: ({
2438
- repository,
2439
- dependency
2440
- }) => `Repository ${repository} depends on ${dependency}, which is not registered in the application.`
2441
- });
2442
- var UnregisteredUseCaseDependency2 = exceptionOf2({
2443
- layer: "application" /* Application */,
2444
- name: "UnregisteredUseCaseDependency",
2445
- message: ({
2446
- useCase,
2447
- dependency
2448
- }) => `Use case ${useCase} depends on ${dependency}, which is not registered in the application.`
2449
- });
2450
- var ApplicationNotStarted2 = exceptionOf2({
2451
- layer: "application" /* Application */,
2452
- name: "ApplicationNotStarted",
2453
- message: ({
2454
- application
2455
- }) => application === undefined ? "No application is started: call await app.start() first." : `The application "${application}" is not started: call await app.start() first.`
2456
- });
2457
- var ApplicationAlreadyStarted2 = exceptionOf2({
2458
- layer: "application" /* Application */,
2459
- name: "ApplicationAlreadyStarted",
2460
- message: ({
2461
- current,
2462
- attempted
2463
- }) => `The application "${current}" is already started, so "${attempted}" cannot be: stop it, or call start({ replace: true }).`
2464
- });
2465
- var InvalidApplicationEnvironment2 = exceptionOf2({
2466
- layer: "environment" /* Environment */,
2467
- name: "InvalidApplicationEnvironment",
2468
- message: ({
2469
- environments
2470
- }) => `Invalid environment variables: ${Object.entries(environments).map(([name, keys]) => `${name} (${keys.join(", ")})`).join("; ")}.`
2471
- });
2472
-
2473
2750
  // src/core/openapi/exceptions.ts
2474
2751
  var InvalidOpenAPIModel2 = exceptionOf2({
2475
2752
  layer: "output" /* Output */,
@@ -2502,18 +2779,19 @@ function isExportedException(Declared) {
2502
2779
  if (!collected) {
2503
2780
  collected = true;
2504
2781
  for (const module of [
2505
- exports_exceptions8,
2506
- exports_exceptions2,
2507
- exports_exceptions7,
2508
2782
  exports_exceptions4,
2509
- exports_environment,
2783
+ exports_exceptions2,
2510
2784
  exports_exceptions9,
2785
+ exports_exceptions6,
2786
+ exports_environment,
2787
+ exports_exceptions5,
2511
2788
  exports_exceptions10,
2789
+ exports_exceptions11,
2512
2790
  exports_exceptions3,
2513
2791
  exports_exceptions,
2514
- exports_exceptions5,
2792
+ exports_exceptions7,
2515
2793
  exports_use_case,
2516
- exports_exceptions6
2794
+ exports_exceptions8
2517
2795
  ]) {
2518
2796
  markExported(...Object.values(module));
2519
2797
  }
@@ -2635,28 +2913,28 @@ function toOpenAPI2(modelOrModels, { refPrefix = "#/components/schemas/" } = {})
2635
2913
  var descriptions2 = new WeakMap;
2636
2914
  var factories = new WeakMap;
2637
2915
  var opaque2 = Object.freeze({ kind: "opaque" });
2638
- function invalid3(source, reason) {
2916
+ function invalid4(source, reason) {
2639
2917
  throw new InvalidOutputStage2({ source, details: { reason } });
2640
2918
  }
2641
2919
  function optionsOf(name, given, validate) {
2642
- const options = frozenJson(given ?? {}, "Stage options", (reason) => invalid3(name, reason));
2920
+ const options = frozenJson(given ?? {}, "Stage options", (reason) => invalid4(name, reason));
2643
2921
  if (options === null || typeof options !== "object" || Array.isArray(options)) {
2644
- invalid3(name, "Stage options must be an object.");
2922
+ invalid4(name, "Stage options must be an object.");
2645
2923
  }
2646
2924
  const reason = validate?.(options);
2647
2925
  if (reason !== undefined)
2648
- invalid3(name, reason);
2926
+ invalid4(name, reason);
2649
2927
  return options;
2650
2928
  }
2651
2929
  function checkName(source, name) {
2652
2930
  if (typeof name !== "string" || !name.trim()) {
2653
- invalid3(source, "Expected the stage name to be a non-empty string.");
2931
+ invalid4(source, "Expected the stage name to be a non-empty string.");
2654
2932
  }
2655
2933
  }
2656
2934
  function transformOf2(settings) {
2657
2935
  checkName("transformOf", settings?.name);
2658
2936
  if (typeof settings.apply !== "function") {
2659
- invalid3(settings.name, "Expected apply to be a function.");
2937
+ invalid4(settings.name, "Expected apply to be a function.");
2660
2938
  }
2661
2939
  const { name, apply, validate } = settings;
2662
2940
  const factory = (given) => {
@@ -2680,7 +2958,7 @@ function transformOf2(settings) {
2680
2958
  function sinkOf2(settings) {
2681
2959
  checkName("sinkOf", settings?.name);
2682
2960
  if (typeof settings.build !== "function") {
2683
- invalid3(settings.name, "Expected build to be a function.");
2961
+ invalid4(settings.name, "Expected build to be a function.");
2684
2962
  }
2685
2963
  const { name, build, validate } = settings;
2686
2964
  const factory = (given) => {
@@ -2718,10 +2996,10 @@ function stageCatalogOf2(...stages) {
2718
2996
  for (const factory of stages) {
2719
2997
  const known = factories.get(factory);
2720
2998
  if (!known) {
2721
- invalid3("catalog", "A catalog takes factories made by `transformOf` or `sinkOf`.");
2999
+ invalid4("catalog", "A catalog takes factories made by `transformOf` or `sinkOf`.");
2722
3000
  }
2723
3001
  if (Object.hasOwn(byName, known.name)) {
2724
- invalid3(known.name, "The catalog has another stage with its name.");
3002
+ invalid4(known.name, "The catalog has another stage with its name.");
2725
3003
  }
2726
3004
  byName[known.name] = factory;
2727
3005
  }
@@ -2732,7 +3010,7 @@ function stageCatalogOf2(...stages) {
2732
3010
  resolve(name, options) {
2733
3011
  const factory = Object.hasOwn(byName, name) ? byName[name] : undefined;
2734
3012
  if (!factory)
2735
- invalid3("catalog", `Unknown stage "${name}".`);
3013
+ invalid4("catalog", `Unknown stage "${name}".`);
2736
3014
  return factory(options);
2737
3015
  }
2738
3016
  });
@@ -2742,38 +3020,38 @@ function stageCatalogOf2(...stages) {
2742
3020
  function isApplicationLike(value) {
2743
3021
  return typeof value === "object" && value !== null && value[Kind2] === "application";
2744
3022
  }
2745
- function invalid4(source, reason) {
3023
+ function invalid5(source, reason) {
2746
3024
  throw new InvalidOutputStage2({ source, details: { reason } });
2747
3025
  }
2748
3026
  var PATCHABLE = ["meta", "tags", "description", "deprecated", "invoke"];
2749
3027
  function operationOf2(operation, patch) {
2750
3028
  const source = operation?.id ?? "operationOf";
2751
3029
  if (operation?.kind !== "operation" || operation.version !== 1) {
2752
- invalid4(source, "Expected an operation descriptor.");
3030
+ invalid5(source, "Expected an operation descriptor.");
2753
3031
  }
2754
3032
  if (typeof patch !== "object" || patch === null || Array.isArray(patch)) {
2755
- invalid4(source, "Expected the patch to be an object.");
3033
+ invalid5(source, "Expected the patch to be an object.");
2756
3034
  }
2757
3035
  for (const key of Reflect.ownKeys(patch)) {
2758
3036
  if (typeof key !== "string" || !PATCHABLE.includes(key)) {
2759
- invalid4(source, `"${String(key)}" cannot be patched; only ${PATCHABLE.join(", ")} can.`);
3037
+ invalid5(source, `"${String(key)}" cannot be patched; only ${PATCHABLE.join(", ")} can.`);
2760
3038
  }
2761
3039
  }
2762
3040
  const { meta, tags, description, deprecated, invoke } = patch;
2763
3041
  if ("meta" in patch && (typeof meta !== "object" || meta === null || Array.isArray(meta))) {
2764
- invalid4(source, "Expected meta to be an object.");
3042
+ invalid5(source, "Expected meta to be an object.");
2765
3043
  }
2766
3044
  if ("tags" in patch && (!Array.isArray(tags) || tags.some((tag) => typeof tag !== "string"))) {
2767
- invalid4(source, "Expected tags to be a list of strings.");
3045
+ invalid5(source, "Expected tags to be a list of strings.");
2768
3046
  }
2769
3047
  if ("description" in patch && description !== undefined && typeof description !== "string") {
2770
- invalid4(source, "Expected description to be a string.");
3048
+ invalid5(source, "Expected description to be a string.");
2771
3049
  }
2772
3050
  if ("deprecated" in patch && typeof deprecated !== "boolean") {
2773
- invalid4(source, "Expected deprecated to be a boolean.");
3051
+ invalid5(source, "Expected deprecated to be a boolean.");
2774
3052
  }
2775
3053
  if ("invoke" in patch && typeof invoke !== "function") {
2776
- invalid4(source, "Expected invoke to be a function.");
3054
+ invalid5(source, "Expected invoke to be a function.");
2777
3055
  }
2778
3056
  return Object.freeze({
2779
3057
  ...operation,
@@ -2814,25 +3092,25 @@ function statusesOf(name, statuses) {
2814
3092
  if (statuses === undefined)
2815
3093
  return Object.freeze({});
2816
3094
  if (typeof statuses !== "object" || statuses === null || Array.isArray(statuses)) {
2817
- invalid4(name, "Expected statuses to be a map of exception names to statuses.");
3095
+ invalid5(name, "Expected statuses to be a map of exception names to statuses.");
2818
3096
  }
2819
3097
  for (const [exception, status] of Object.entries(statuses)) {
2820
3098
  if (!exception || typeof status !== "number" || !Number.isInteger(status) || status < 400 || status > 599) {
2821
- invalid4(name, `Expected the status of "${exception}" to be an integer between 400 and 599.`);
3099
+ invalid5(name, `Expected the status of "${exception}" to be an integer between 400 and 599.`);
2822
3100
  }
2823
3101
  }
2824
3102
  return Object.freeze({ ...statuses });
2825
3103
  }
2826
3104
  function checkPipe(name, pipe) {
2827
3105
  if (!Array.isArray(pipe))
2828
- invalid4(name, "Expected pipe to be a list of stages.");
3106
+ invalid5(name, "Expected pipe to be a list of stages.");
2829
3107
  const stages = pipe;
2830
3108
  stages.forEach((stage, index) => {
2831
3109
  if (!isStage(stage)) {
2832
- invalid4(name, `Expected pipe[${index}] to be a transform or sink stage.`);
3110
+ invalid5(name, `Expected pipe[${index}] to be a transform or sink stage.`);
2833
3111
  }
2834
3112
  if (stage.kind === "sink" && index !== stages.length - 1) {
2835
- invalid4(name, `The sink at pipe[${index}] must be the last stage.`);
3113
+ invalid5(name, `The sink at pipe[${index}] must be the last stage.`);
2836
3114
  }
2837
3115
  });
2838
3116
  return Object.freeze([...stages]);
@@ -2848,7 +3126,7 @@ function transformed(name, operations, transforms, context) {
2848
3126
  if (result === null)
2849
3127
  continue;
2850
3128
  if (typeof result !== "object" || result?.kind !== "operation" || result.id !== operation.id) {
2851
- invalid4(name, `A transform must return the operation "${operation.id}" or null.`);
3129
+ invalid5(name, `A transform must return the operation "${operation.id}" or null.`);
2852
3130
  }
2853
3131
  next.push(result);
2854
3132
  }
@@ -2873,7 +3151,7 @@ function make(name, commands, operations, stages, statuses) {
2873
3151
  return built.value;
2874
3152
  const sink = stages.at(-1);
2875
3153
  if (sink?.kind !== "sink") {
2876
- invalid4(name || "output", "The pipe has no sink to build.");
3154
+ invalid5(name || "output", "The pipe has no sink to build.");
2877
3155
  }
2878
3156
  built = { value: sink.build(operations, context) };
2879
3157
  return built.value;
@@ -2882,7 +3160,7 @@ function make(name, commands, operations, stages, statuses) {
2882
3160
  const next = checkPipe(name || "output", more);
2883
3161
  const last = next.at(-1);
2884
3162
  if (stages.at(-1)?.kind === "sink") {
2885
- invalid4(name || "output", "Cannot add stages after the sink.");
3163
+ invalid5(name || "output", "Cannot add stages after the sink.");
2886
3164
  }
2887
3165
  const ops = transformed(name, operations, next, context);
2888
3166
  if (last?.kind === "sink") {
@@ -2896,11 +3174,11 @@ function make(name, commands, operations, stages, statuses) {
2896
3174
  function outputOf2(settings) {
2897
3175
  const { name, commands } = settings ?? {};
2898
3176
  if (typeof name !== "string" || !name.trim()) {
2899
- invalid4("outputOf", "Expected name to be a non-empty string.");
3177
+ invalid5("outputOf", "Expected name to be a non-empty string.");
2900
3178
  }
2901
3179
  const sources = Array.isArray(commands) ? commands : [commands];
2902
3180
  if (!Array.isArray(commands) && isCommand2(commands) || sources.some((entry) => !isCommand2(entry) && !isApplicationLike(entry))) {
2903
- invalid4(name, "Expected commands to be a list of commands made by commandOf or applications.");
3181
+ invalid5(name, "Expected commands to be a list of commands made by commandOf or applications.");
2904
3182
  }
2905
3183
  const flat = [
2906
3184
  ...new Set(sources.flatMap((entry) => isCommand2(entry) ? [entry] : Object.values(entry.commands)))
@@ -2944,32 +3222,16 @@ function describeSteps(steps, names) {
2944
3222
  });
2945
3223
  }
2946
3224
 
2947
- // src/core/application/registry.ts
2948
- var SLOT = Symbol.for("afora.application");
2949
- function slot() {
2950
- const global = globalThis;
2951
- global[SLOT] ??= { current: undefined };
2952
- return global[SLOT];
2953
- }
2954
- function useApplication2() {
2955
- const { current } = slot();
2956
- if (current === undefined) {
2957
- throw new ApplicationNotStarted2({ source: "useApplication", details: {} });
2958
- }
2959
- return current;
2960
- }
2961
- function resetApplication2() {
2962
- slot().current = undefined;
2963
- }
2964
- function startedApplication() {
2965
- return slot().current;
2966
- }
2967
- function setStartedApplication(application) {
2968
- slot().current = application;
2969
- }
2970
-
2971
3225
  // src/core/application/start.ts
2972
3226
  function providerFrom2(environmentDependencies, create) {
3227
+ if (typeof environmentDependencies === "function" && create === undefined) {
3228
+ return Object.freeze({
3229
+ [Kind2]: "provider-factory",
3230
+ environments: Object.freeze([]),
3231
+ create: environmentDependencies,
3232
+ withContext: true
3233
+ });
3234
+ }
2973
3235
  const list = isEnvironment2(environmentDependencies) ? [environmentDependencies] : Array.isArray(environmentDependencies) ? [...environmentDependencies] : [null];
2974
3236
  if (!list.every(isEnvironment2) || new Set(list).size !== list.length || typeof create !== "function") {
2975
3237
  throw new InvalidApplicationDeclaration2({
@@ -2982,9 +3244,22 @@ function providerFrom2(environmentDependencies, create) {
2982
3244
  return Object.freeze({
2983
3245
  [Kind2]: "provider-factory",
2984
3246
  environments: Object.freeze(list),
2985
- create
3247
+ create,
3248
+ withContext: false
2986
3249
  });
2987
3250
  }
3251
+ function logFrom2(create) {
3252
+ if (typeof create !== "function") {
3253
+ throw new InvalidApplicationDeclaration2({
3254
+ source: "logFrom",
3255
+ details: { reason: "Expected logFrom to take a create function." }
3256
+ });
3257
+ }
3258
+ return Object.freeze({ [Kind2]: "log-factory", create });
3259
+ }
3260
+ function isLogFactory2(value) {
3261
+ return typeof value === "object" && value !== null && value[Kind2] === "log-factory";
3262
+ }
2988
3263
  function isProviderFactory2(value) {
2989
3264
  return typeof value === "object" && value !== null && value[Kind2] === "provider-factory";
2990
3265
  }
@@ -3041,12 +3316,12 @@ function isDeferredProvider(provider) {
3041
3316
  function deferredEnvironments(provider) {
3042
3317
  return (typeof provider === "object" && provider !== null ? deferred.get(provider)?.factory.environments : undefined) ?? [];
3043
3318
  }
3044
- function startDeferred(provider) {
3319
+ function startDeferred(provider, context) {
3045
3320
  const state = deferred.get(provider);
3046
3321
  if (!state)
3047
3322
  return;
3048
3323
  state.starting ??= (async () => {
3049
- const made = await state.factory.create(loadEnvironments(state.factory.environments));
3324
+ const made = await state.factory.create(state.factory.withContext ? context : loadEnvironments(state.factory.environments));
3050
3325
  if (typeof made !== "object" || made === null) {
3051
3326
  state.starting = undefined;
3052
3327
  throw new InvalidApplicationDeclaration2({
@@ -3085,6 +3360,106 @@ function checkTransactions(source, provider, commands) {
3085
3360
  }
3086
3361
  }
3087
3362
 
3363
+ // src/core/application/providers.ts
3364
+ var slots = new WeakMap;
3365
+ var storages = new WeakMap;
3366
+ var METHODS2 = [
3367
+ "findOne",
3368
+ "findMany",
3369
+ "insert",
3370
+ "update",
3371
+ "remove",
3372
+ "count"
3373
+ ];
3374
+ var KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3375
+ var RESERVED = new Set(["inMemory", "__proto__", "constructor", "prototype"]);
3376
+ function invalid6(reason) {
3377
+ throw new InvalidApplicationDeclaration2({
3378
+ source: "providersOf",
3379
+ details: { reason }
3380
+ });
3381
+ }
3382
+ function isProviderObject(value) {
3383
+ return typeof value === "object" && value !== null && !isProviderFactory2(value) && METHODS2.some((method) => typeof value[method] === "function");
3384
+ }
3385
+ function slotOf(key, storage, value) {
3386
+ const slot = {
3387
+ [Kind2]: "provider-slot",
3388
+ key
3389
+ };
3390
+ if (isProviderFactory2(value)) {
3391
+ const unbound = () => {
3392
+ throw new ProviderNotBound2({ source: key, details: { provider: key } });
3393
+ };
3394
+ for (const method of METHODS2)
3395
+ slot[method] = unbound;
3396
+ slot.transaction = unbound;
3397
+ Object.defineProperty(slot, "capabilities", {
3398
+ get: () => {
3399
+ return;
3400
+ },
3401
+ enumerable: true
3402
+ });
3403
+ } else {
3404
+ const target = value;
3405
+ for (const method of [...METHODS2, "transaction", "close"]) {
3406
+ if (typeof target[method] === "function") {
3407
+ slot[method] = (...args) => target[method]?.(...args);
3408
+ }
3409
+ }
3410
+ Object.defineProperty(slot, "capabilities", {
3411
+ get: () => value.capabilities,
3412
+ enumerable: true
3413
+ });
3414
+ }
3415
+ Object.freeze(slot);
3416
+ slots.set(slot, { key, storage, value });
3417
+ return slot;
3418
+ }
3419
+ function providersOf2(entries) {
3420
+ if (typeof entries !== "object" || entries === null || Array.isArray(entries) || Object.keys(entries).length === 0) {
3421
+ invalid6("Expected an object of providers by key.");
3422
+ }
3423
+ const storage = Object.create(null);
3424
+ storage[Kind2] = "provider-storage";
3425
+ const seen = new Map;
3426
+ const list = [];
3427
+ for (const [key, value] of Object.entries(entries)) {
3428
+ if (!KEY.test(key) || RESERVED.has(key)) {
3429
+ invalid6(key === "inMemory" ? `"inMemory" is the built-in provider; choose another key.` : `Expected "${key}" to be an identifier that is not reserved.`);
3430
+ }
3431
+ if (isProviderSlot2(value)) {
3432
+ invalid6(`"${key}" holds a provider of another storage; a storage holds providers, not slots.`);
3433
+ }
3434
+ if (!isProviderFactory2(value) && !isProviderObject(value)) {
3435
+ invalid6(`Expected "${key}" to be a repository provider or a factory of providerFrom.`);
3436
+ }
3437
+ const other = seen.get(value);
3438
+ if (other !== undefined) {
3439
+ invalid6(`"${other}" and "${key}" hold the same provider.`);
3440
+ }
3441
+ seen.set(value, key);
3442
+ const slot = slotOf(key, storage, value);
3443
+ storage[key] = slot;
3444
+ list.push(slot);
3445
+ }
3446
+ Object.freeze(storage);
3447
+ storages.set(storage, Object.freeze(list));
3448
+ return storage;
3449
+ }
3450
+ function isProviderSlot2(value) {
3451
+ return typeof value === "object" && value !== null && slots.has(value);
3452
+ }
3453
+ function isProviderStorage2(value) {
3454
+ return typeof value === "object" && value !== null && storages.has(value);
3455
+ }
3456
+ function slotsOf(storage) {
3457
+ return storages.get(storage) ?? [];
3458
+ }
3459
+ function slotEntry(slot) {
3460
+ return slots.get(slot);
3461
+ }
3462
+
3088
3463
  // src/core/application/application.ts
3089
3464
  var KEYS = [
3090
3465
  "properties",
@@ -3099,6 +3474,12 @@ var KEYS = [
3099
3474
  "exceptions"
3100
3475
  ];
3101
3476
  var internals = new WeakMap;
3477
+ var logSettings = new WeakMap;
3478
+ var namedProviders = new WeakMap;
3479
+ var defaultSlots = new WeakMap;
3480
+ function referenceOf(slot) {
3481
+ return { kind: "ref", key: slotEntry(slot).key };
3482
+ }
3102
3483
  var KIND_OF = {
3103
3484
  properties: "property",
3104
3485
  blueprints: "blueprint",
@@ -3114,7 +3495,7 @@ var KIND_OF = {
3114
3495
  function isApplication2(value) {
3115
3496
  return typeof value === "object" && value !== null && value[Kind2] === "application";
3116
3497
  }
3117
- function invalid5(source, reason) {
3498
+ function invalid7(source, reason) {
3118
3499
  throw new InvalidApplicationDeclaration2({ source, details: { reason } });
3119
3500
  }
3120
3501
  function isOfKind(collection, value) {
@@ -3199,7 +3580,7 @@ function resolve(settings) {
3199
3580
  for (const other of settings.extends ?? []) {
3200
3581
  const resolved = internals.get(other);
3201
3582
  if (!resolved)
3202
- invalid5(source, "Expected `extends` to hold applications.");
3583
+ invalid7(source, "Expected `extends` to hold applications.");
3203
3584
  for (const key of KEYS) {
3204
3585
  for (const entry of resolved[key].values())
3205
3586
  add(entry);
@@ -3210,15 +3591,15 @@ function resolve(settings) {
3210
3591
  if (declared === undefined)
3211
3592
  continue;
3212
3593
  if (typeof declared !== "object" || declared === null || Array.isArray(declared)) {
3213
- invalid5(source, `Expected ${collection} to be an object of declarations.`);
3594
+ invalid7(source, `Expected ${collection} to be an object of declarations.`);
3214
3595
  }
3215
3596
  for (const [name, value] of Object.entries(declared)) {
3216
3597
  if (!isOfKind(collection, value)) {
3217
- invalid5(source, `${collection}.${name} is not a ${KIND_OF[collection]} (${collection} takes ${KIND_OF[collection]} declarations).`);
3598
+ invalid7(source, `${collection}.${name} is not a ${KIND_OF[collection]} (${collection} takes ${KIND_OF[collection]} declarations).`);
3218
3599
  }
3219
3600
  const declaredName = collection === "useCases" ? value.useCaseName : collection === "commands" ? value.commandName : collection === "outputs" ? value.outputName : collection === "exceptions" ? value.name : name;
3220
3601
  if (declaredName !== name) {
3221
- invalid5(source, `${collection}.${name} must be keyed by its name, "${declaredName}".`);
3602
+ invalid7(source, `${collection}.${name} must be keyed by its name, "${declaredName}".`);
3222
3603
  }
3223
3604
  add({
3224
3605
  collection,
@@ -3339,7 +3720,7 @@ function validateOutputs(source, registry) {
3339
3720
  for (const entry of registry.outputs.values()) {
3340
3721
  const output = entry.value;
3341
3722
  if (!output.outputName) {
3342
- invalid5(source, `outputs.${entry.name} has no name (an output made by pipe cannot be registered).`);
3723
+ invalid7(source, `outputs.${entry.name} has no name (an output made by pipe cannot be registered).`);
3343
3724
  }
3344
3725
  for (const command of output.commands) {
3345
3726
  if (commands.has(command))
@@ -3369,7 +3750,7 @@ function validateUseCases(source, registry) {
3369
3750
  for (const entry of registry.useCases.values()) {
3370
3751
  const UseCase = entry.value;
3371
3752
  for (const [key, dependency] of Object.entries(UseCase.dependencies)) {
3372
- const found = isUseCaseClass2(dependency) ? useCases.has(dependency) : isEnvironment2(dependency) ? environments.has(dependency) : repositories.has(inspectRepository(dependency).state);
3753
+ const found = isLoggerDeclaration(dependency) ? true : isUseCaseClass2(dependency) ? useCases.has(dependency) : isEnvironment2(dependency) ? environments.has(dependency) : repositories.has(inspectRepository(dependency).state);
3373
3754
  if (!found) {
3374
3755
  throw new UnregisteredUseCaseDependency2({
3375
3756
  source,
@@ -3541,7 +3922,7 @@ function recordDescriptionOf(entry, scope) {
3541
3922
  plugins
3542
3923
  };
3543
3924
  }
3544
- function describeRegistry(name, extended, registry, provider) {
3925
+ function describeRegistry(name, extended, registry, provider, named, log) {
3545
3926
  const scope = {
3546
3927
  properties: referencesOf(registry, "properties"),
3547
3928
  blueprints: referencesOf(registry, "blueprints"),
@@ -3581,7 +3962,28 @@ function describeRegistry(name, extended, registry, provider) {
3581
3962
  return {
3582
3963
  name,
3583
3964
  extends: extended.map((other) => other.name),
3584
- ...provider === undefined ? {} : { provider: providerDescriptionOf2(provider) },
3965
+ ...provider === undefined ? {} : { provider },
3966
+ ...named.size === 0 ? {} : {
3967
+ providers: Object.fromEntries([...named].map(([key, { slot, from }]) => {
3968
+ const { value } = slotEntry(slot);
3969
+ return [
3970
+ key,
3971
+ {
3972
+ from,
3973
+ ...isProviderFactory2(value) ? { kind: "opaque" } : providerDescriptionOf2(value)
3974
+ }
3975
+ ];
3976
+ }))
3977
+ },
3978
+ ...log === undefined ? {} : log === "opaque" ? { log: { kind: "opaque" } } : {
3979
+ log: {
3980
+ level: log.level,
3981
+ providers: log.providers.map((logProvider) => {
3982
+ const providerName = logProviderNameOf(logProvider);
3983
+ return providerName === undefined ? { kind: "opaque" } : { kind: "log-provider", name: providerName };
3984
+ })
3985
+ }
3986
+ },
3585
3987
  properties: each("properties", (entry) => ({
3586
3988
  from: entry.from,
3587
3989
  ...propertyDescriptionOf(entry.value, scope)
@@ -3605,7 +4007,9 @@ function describeRegistry(name, extended, registry, provider) {
3605
4007
  deletion: info.deletion,
3606
4008
  relations: info.relations.map((relation) => repositories.get(relation)),
3607
4009
  environments: info.environments.map((environment) => environmentRefs.get(environment)),
3608
- ...info.provider === undefined ? {} : { provider: providerDescriptionOf2(info.provider) }
4010
+ ...info.provider === undefined ? {} : {
4011
+ provider: isProviderSlot2(info.provider) ? referenceOf(info.provider) : providerDescriptionOf2(info.provider)
4012
+ }
3609
4013
  };
3610
4014
  }),
3611
4015
  useCases: each("useCases", (entry) => {
@@ -3632,7 +4036,10 @@ function describeRegistry(name, extended, registry, provider) {
3632
4036
  }),
3633
4037
  dependencies: Object.fromEntries(Object.entries(UseCase.dependencies).map(([key, dependency]) => [
3634
4038
  key,
3635
- isUseCaseClass2(dependency) ? { kind: "use-case", ref: useCaseRefs.get(dependency) } : isEnvironment2(dependency) ? {
4039
+ isLoggerDeclaration(dependency) ? { kind: "logger", ref: "Logger" } : isUseCaseClass2(dependency) ? {
4040
+ kind: "use-case",
4041
+ ref: useCaseRefs.get(dependency)
4042
+ } : isEnvironment2(dependency) ? {
3636
4043
  kind: "environment",
3637
4044
  ref: environmentRefs.get(dependency)
3638
4045
  } : {
@@ -3690,49 +4097,123 @@ function describeRegistry(name, extended, registry, provider) {
3690
4097
  function applicationOf2(settings) {
3691
4098
  const { name } = settings ?? {};
3692
4099
  if (typeof name !== "string" || name === "") {
3693
- invalid5("applicationOf", "Expected a name.");
4100
+ invalid7("applicationOf", "Expected a name.");
3694
4101
  }
3695
4102
  const extended = settings.extends ?? [];
3696
4103
  if (!Array.isArray(extended) || !extended.every(isApplication2)) {
3697
- invalid5(name, "Expected `extends` to be a list of applications.");
4104
+ invalid7(name, "Expected `extends` to be a list of applications.");
3698
4105
  }
3699
4106
  const registry = resolve(settings);
3700
4107
  validate(name, registry);
3701
4108
  validateUseCases(name, registry);
3702
4109
  validateCommands(name, registry);
3703
4110
  validateOutputs(name, registry);
3704
- const declaredProvider = settings.provider ?? [...extended].reverse().find((other) => other.provider)?.provider;
3705
- if (declaredProvider !== undefined && typeof declaredProvider !== "object") {
3706
- invalid5(name, "Expected `provider` to be a repository provider.");
4111
+ if (settings.providers !== undefined && !isProviderStorage2(settings.providers)) {
4112
+ invalid7(name, "Expected `providers` to be a storage made by providersOf.");
3707
4113
  }
3708
- const provider = isProviderFactory2(declaredProvider) ? deferredProvider(declaredProvider, name) : declaredProvider;
3709
- {
3710
- const registered = new Set([...registry.environments.values()].map((entry) => entry.key));
3711
- for (const environment of deferredEnvironments(provider)) {
3712
- if (registered.has(environment))
3713
- continue;
4114
+ const named = new Map;
4115
+ const addNamed = (entry) => {
4116
+ const { key } = slotEntry(entry.slot);
4117
+ const current = named.get(key);
4118
+ if (current !== undefined && current.slot !== entry.slot) {
4119
+ throw new ApplicationConflict2({
4120
+ source: name,
4121
+ details: {
4122
+ reason: `The provider "${key}" is declared by different storages, in "${current.from}" and "${entry.from}".`,
4123
+ origins: [current.from, entry.from],
4124
+ declarations: [`provider ${key}`, `provider ${key}`]
4125
+ }
4126
+ });
4127
+ }
4128
+ named.set(key, entry);
4129
+ };
4130
+ for (const other of extended) {
4131
+ for (const entry of namedProviders.get(other)?.values() ?? []) {
4132
+ addNamed(entry);
4133
+ }
4134
+ }
4135
+ for (const slot of settings.providers ? slotsOf(settings.providers) : []) {
4136
+ const inherited = named.get(slotEntry(slot).key);
4137
+ if (inherited?.slot === slot)
4138
+ continue;
4139
+ const { value } = slotEntry(slot);
4140
+ addNamed({
4141
+ slot,
4142
+ from: name,
4143
+ instance: isProviderFactory2(value) ? deferredProvider(value, name) : value
4144
+ });
4145
+ }
4146
+ const instances = new Map([...named.values()].map((entry) => [entry.slot, entry.instance]));
4147
+ const instanceOf = (slot, holder) => {
4148
+ const instance = instances.get(slot);
4149
+ if (instance === undefined) {
3714
4150
  throw new UnregisteredApplicationReference2({
3715
4151
  source: name,
3716
4152
  details: {
3717
- holder: "the provider",
3718
- reference: "an environment"
4153
+ holder,
4154
+ reference: `provider "${slotEntry(slot).key}"`
3719
4155
  }
3720
4156
  });
3721
4157
  }
4158
+ return instance;
4159
+ };
4160
+ for (const entry of registry.repositories.values()) {
4161
+ const declared = inspectRepository(entry.value).provider;
4162
+ if (isProviderSlot2(declared)) {
4163
+ instanceOf(declared, `repository "${entry.name}"`);
4164
+ }
3722
4165
  }
4166
+ const inheritedFrom = settings.provider === undefined ? [...extended].reverse().find((other) => other.provider) : undefined;
4167
+ const declaredProvider = settings.provider ?? inheritedFrom?.provider;
4168
+ if (declaredProvider !== undefined && typeof declaredProvider !== "object") {
4169
+ invalid7(name, "Expected `provider` to be a repository provider.");
4170
+ }
4171
+ const defaultSlot = isProviderSlot2(declaredProvider) ? declaredProvider : inheritedFrom === undefined ? undefined : defaultSlots.get(inheritedFrom);
4172
+ const provider = defaultSlot !== undefined ? instanceOf(defaultSlot, "the application") : isProviderFactory2(declaredProvider) ? deferredProvider(declaredProvider, name) : declaredProvider;
4173
+ const allProviders = [
4174
+ ...new Set([provider, ...instances.values()].filter((value) => value !== undefined))
4175
+ ];
4176
+ {
4177
+ const registered = new Set([...registry.environments.values()].map((entry) => entry.key));
4178
+ const holderOf = (instance) => {
4179
+ const entry = [...named.values()].find((candidate) => candidate.instance === instance);
4180
+ return entry === undefined || instance === provider ? "the provider" : `provider "${slotEntry(entry.slot).key}"`;
4181
+ };
4182
+ for (const instance of allProviders) {
4183
+ for (const environment of deferredEnvironments(instance)) {
4184
+ if (registered.has(environment))
4185
+ continue;
4186
+ throw new UnregisteredApplicationReference2({
4187
+ source: name,
4188
+ details: {
4189
+ holder: holderOf(instance),
4190
+ reference: "an environment"
4191
+ }
4192
+ });
4193
+ }
4194
+ }
4195
+ }
4196
+ const declaredLog = settings.log ?? [...extended].reverse().map((other) => logSettings.get(other)).find((log) => log !== undefined);
4197
+ const log = declaredLog === undefined ? undefined : applicationLogger(name);
4198
+ if (declaredLog !== undefined && !isLogFactory2(declaredLog)) {
4199
+ log?.set(checkLogSettings(declaredLog));
4200
+ }
4201
+ const logger = log?.logger;
4202
+ const bindsProviders = provider !== undefined || instances.size > 0;
4203
+ const rebinds = bindsProviders || logger !== undefined;
3723
4204
  const collection = (key) => Object.freeze(Object.fromEntries([...registry[key].values()].map((entry) => [entry.name, entry.value])));
3724
4205
  const bound = new Map;
3725
4206
  const boundByState = new Map;
3726
4207
  const repositories = {};
3727
4208
  for (const entry of registry.repositories.values()) {
3728
- const repository = provider === undefined ? entry.value : bindRepository(entry.value, provider);
4209
+ const repository = bindsProviders ? bindRepository(entry.value, provider, instances) : entry.value;
3729
4210
  bound.set(entry.value, repository);
3730
4211
  boundByState.set(entry.key, repository);
3731
4212
  repositories[entry.name] = repository;
3732
4213
  }
3733
4214
  const boundUseCases = new Map;
3734
4215
  const bindUseCase = (UseCase) => {
3735
- if (provider === undefined)
4216
+ if (!rebinds)
3736
4217
  return UseCase;
3737
4218
  const done = boundUseCases.get(UseCase);
3738
4219
  if (done)
@@ -3745,11 +4226,13 @@ function applicationOf2(settings) {
3745
4226
  boundUseCases.set(UseCase, Bound);
3746
4227
  const dependencies = Object.fromEntries(Object.entries(UseCase.dependencies).map(([key, dependency]) => [
3747
4228
  key,
3748
- isUseCaseClass2(dependency) ? bindUseCase(dependency) : isEnvironment2(dependency) ? dependency : boundByState.get(inspectRepository(dependency).state) ?? dependency
4229
+ isUseCaseClass2(dependency) ? bindUseCase(dependency) : isEnvironment2(dependency) || isLoggerDeclaration(dependency) ? dependency : boundByState.get(inspectRepository(dependency).state) ?? dependency
3749
4230
  ]));
3750
4231
  Object.defineProperty(Bound, "dependencies", {
3751
4232
  value: Object.freeze(dependencies)
3752
4233
  });
4234
+ if (logger)
4235
+ Object.defineProperty(Bound, ApplicationLoggerKey, { value: logger });
3753
4236
  return Bound;
3754
4237
  };
3755
4238
  const useCases = {};
@@ -3763,7 +4246,7 @@ function applicationOf2(settings) {
3763
4246
  const boundCommandOf = new Map;
3764
4247
  for (const entry of registry.commands.values()) {
3765
4248
  const declared = entry.value;
3766
- const rebuilt = provider === undefined ? declared : commandOf2({
4249
+ const rebuilt = !rebinds ? declared : commandOf2({
3767
4250
  name: declared.commandName,
3768
4251
  description: declared.description,
3769
4252
  tags: declared.tags,
@@ -3777,7 +4260,7 @@ function applicationOf2(settings) {
3777
4260
  events: declared.events,
3778
4261
  onEventError: declared.onEventError
3779
4262
  });
3780
- if (provider !== undefined)
4263
+ if (bindsProviders)
3781
4264
  verifyCommand(rebuilt);
3782
4265
  boundCommandOf.set(entry.value, rebuilt);
3783
4266
  commands[entry.name] = rebuilt;
@@ -3786,7 +4269,7 @@ function applicationOf2(settings) {
3786
4269
  const boundOutputOf = new Map;
3787
4270
  for (const entry of registry.outputs.values()) {
3788
4271
  const declared = entry.value;
3789
- const rebuilt = provider === undefined ? declared : outputOf2({
4272
+ const rebuilt = !rebinds ? declared : outputOf2({
3790
4273
  name: declared.outputName,
3791
4274
  commands: declared.commands.map((command) => boundCommandOf.get(command)),
3792
4275
  statuses: declared.statuses,
@@ -3797,6 +4280,19 @@ function applicationOf2(settings) {
3797
4280
  }
3798
4281
  const environmentEntries = [...registry.environments.values()];
3799
4282
  const registeredEnvironments = new Set(environmentEntries.map((entry) => entry.value));
4283
+ const context = Object.freeze({
4284
+ name,
4285
+ env(environment) {
4286
+ if (!registeredEnvironments.has(environment)) {
4287
+ throw new UnregisteredApplicationReference2({
4288
+ source: name,
4289
+ details: { holder: "the context", reference: "an environment" }
4290
+ });
4291
+ }
4292
+ return environment.load();
4293
+ },
4294
+ logger: logger ?? silentLogger
4295
+ });
3800
4296
  let starting;
3801
4297
  let started = false;
3802
4298
  const start = async () => {
@@ -3822,9 +4318,14 @@ function applicationOf2(settings) {
3822
4318
  cause
3823
4319
  });
3824
4320
  }
3825
- if (provider !== undefined && isDeferredProvider(provider)) {
3826
- const made = await startDeferred(provider);
3827
- checkTransactions(name, made, Object.entries(commands).filter(([, command]) => command.transaction && commandTransactionProvider(command) === provider).map(([key]) => key));
4321
+ if (isLogFactory2(declaredLog)) {
4322
+ log?.set(checkLogSettings(await declaredLog.create(context)));
4323
+ }
4324
+ for (const instance of allProviders) {
4325
+ if (!isDeferredProvider(instance))
4326
+ continue;
4327
+ const made = await startDeferred(instance, context);
4328
+ checkTransactions(name, made, Object.entries(commands).filter(([, command]) => command.transaction && commandTransactionProvider(command) === instance).map(([key]) => key));
3828
4329
  }
3829
4330
  started = true;
3830
4331
  };
@@ -3833,6 +4334,9 @@ function applicationOf2(settings) {
3833
4334
  name,
3834
4335
  extends: Object.freeze([...extended]),
3835
4336
  provider,
4337
+ logger: logger ?? silentLogger,
4338
+ context,
4339
+ ...logger ? { [ApplicationLoggerKey]: logger } : {},
3836
4340
  properties: collection("properties"),
3837
4341
  blueprints: collection("blueprints"),
3838
4342
  records: collection("records"),
@@ -3845,25 +4349,25 @@ function applicationOf2(settings) {
3845
4349
  exceptions: collection("exceptions"),
3846
4350
  useCase(useCase) {
3847
4351
  if (!boundUseCaseOf.has(useCase)) {
3848
- invalid5(name, "Expected a use case registered in the application.");
4352
+ invalid7(name, "Expected a use case registered in the application.");
3849
4353
  }
3850
4354
  return boundUseCaseOf.get(useCase);
3851
4355
  },
3852
4356
  command(command) {
3853
4357
  if (!boundCommandOf.has(command)) {
3854
- invalid5(name, "Expected a command registered in the application.");
4358
+ invalid7(name, "Expected a command registered in the application.");
3855
4359
  }
3856
4360
  return boundCommandOf.get(command);
3857
4361
  },
3858
4362
  output(output) {
3859
4363
  if (!boundOutputOf.has(output)) {
3860
- invalid5(name, "Expected an output registered in the application.");
4364
+ invalid7(name, "Expected an output registered in the application.");
3861
4365
  }
3862
4366
  return boundOutputOf.get(output);
3863
4367
  },
3864
4368
  repository(repository) {
3865
4369
  if (!bound.has(repository)) {
3866
- invalid5(name, "Expected a repository registered in the application.");
4370
+ invalid7(name, "Expected a repository registered in the application.");
3867
4371
  }
3868
4372
  return bound.get(repository);
3869
4373
  },
@@ -3871,10 +4375,11 @@ function applicationOf2(settings) {
3871
4375
  return applicationOf2({
3872
4376
  name,
3873
4377
  extends: [application, ...others],
3874
- ...provider === undefined ? {} : { provider }
4378
+ ...provider === undefined ? {} : { provider: defaultSlot ?? provider },
4379
+ ...declaredLog === undefined ? {} : { log: declaredLog }
3875
4380
  });
3876
4381
  },
3877
- describe: () => describeRegistry(name, extended, registry, provider),
4382
+ describe: () => describeRegistry(name, extended, registry, defaultSlot !== undefined ? referenceOf(defaultSlot) : provider === undefined ? undefined : providerDescriptionOf2(provider), named, isLogFactory2(declaredLog) ? "opaque" : log?.settings),
3878
4383
  get started() {
3879
4384
  return started;
3880
4385
  },
@@ -3898,9 +4403,12 @@ function applicationOf2(settings) {
3898
4403
  setStartedApplication(application);
3899
4404
  })().then(() => application, async (error) => {
3900
4405
  starting = undefined;
3901
- if (provider !== undefined && isDeferredProvider(provider)) {
3902
- await stopProvider(provider);
4406
+ for (const instance of allProviders) {
4407
+ if (isDeferredProvider(instance))
4408
+ await stopProvider(instance);
3903
4409
  }
4410
+ if (isLogFactory2(declaredLog))
4411
+ log?.set(undefined);
3904
4412
  throw error;
3905
4413
  });
3906
4414
  return starting;
@@ -3915,13 +4423,16 @@ function applicationOf2(settings) {
3915
4423
  });
3916
4424
  if (startedApplication() === application)
3917
4425
  setStartedApplication(undefined);
3918
- if (provider !== undefined)
3919
- await stopProvider(provider);
4426
+ for (const instance of allProviders)
4427
+ await stopProvider(instance);
4428
+ await log?.flush();
4429
+ if (isLogFactory2(declaredLog))
4430
+ log?.set(undefined);
3920
4431
  },
3921
4432
  get(declaration) {
3922
4433
  if (isEnvironment2(declaration)) {
3923
4434
  if (!registeredEnvironments.has(declaration)) {
3924
- invalid5(name, "Expected an environment registered in the application.");
4435
+ invalid7(name, "Expected an environment registered in the application.");
3925
4436
  }
3926
4437
  return declaration.load();
3927
4438
  }
@@ -3934,10 +4445,22 @@ function applicationOf2(settings) {
3934
4445
  if (isRepository2(declaration)) {
3935
4446
  return application.repository(declaration);
3936
4447
  }
3937
- return invalid5(name, "Expected a repository, use case, command, output or environment registered in the application.");
4448
+ if (isProviderSlot2(declaration)) {
4449
+ const instance = instances.get(declaration);
4450
+ if (instance === undefined) {
4451
+ invalid7(name, "Expected a provider of a storage registered in the application.");
4452
+ }
4453
+ return instance;
4454
+ }
4455
+ return invalid7(name, "Expected a repository, use case, command, output, environment or provider registered in the application.");
3938
4456
  }
3939
4457
  });
3940
4458
  internals.set(application, registry);
4459
+ namedProviders.set(application, named);
4460
+ if (defaultSlot !== undefined)
4461
+ defaultSlots.set(application, defaultSlot);
4462
+ if (declaredLog !== undefined)
4463
+ logSettings.set(application, declaredLog);
3941
4464
  return application;
3942
4465
  }
3943
4466
  // src/core/blueprint/blueprint-plugin.ts
@@ -4021,7 +4544,7 @@ function outputValueOf2(value) {
4021
4544
  }
4022
4545
 
4023
4546
  // src/core/output/openapi-sink.ts
4024
- var METHODS2 = ["get", "post", "put", "patch", "delete"];
4547
+ var METHODS3 = ["get", "post", "put", "patch", "delete"];
4025
4548
  function isObject(value) {
4026
4549
  return typeof value === "object" && value !== null && !Array.isArray(value);
4027
4550
  }
@@ -4047,8 +4570,8 @@ function routeOptionsIssue2(options, where) {
4047
4570
  if (!isObject(entry) || onlyKeys(entry, ["method", "path"], "route")) {
4048
4571
  return `Expected ${where}.route["${id}"] to be { method?, path? }.`;
4049
4572
  }
4050
- if (entry.method !== undefined && !METHODS2.includes(entry.method)) {
4051
- return `Expected ${where}.route["${id}"].method to be one of ${METHODS2.join(", ")}.`;
4573
+ if (entry.method !== undefined && !METHODS3.includes(entry.method)) {
4574
+ return `Expected ${where}.route["${id}"].method to be one of ${METHODS3.join(", ")}.`;
4052
4575
  }
4053
4576
  if (entry.path !== undefined && !isPath(entry.path)) {
4054
4577
  return `Expected ${where}.route["${id}"].path to start with "/" and have no parameters or query.`;
@@ -4086,7 +4609,7 @@ function validate2(options) {
4086
4609
  }
4087
4610
  return routeOptionsIssue2({ basePath, route }, "openapi");
4088
4611
  }
4089
- function invalid6(reason, source = "openapi") {
4612
+ function invalid8(reason, source = "openapi") {
4090
4613
  throw new InvalidOutputStage2({ source, details: { reason } });
4091
4614
  }
4092
4615
  function kebab(text) {
@@ -4098,7 +4621,7 @@ function operationRoutes2(operations, options, source = "openapi") {
4098
4621
  const ids = new Set(operations.map(({ id }) => id));
4099
4622
  for (const id of Object.keys(route)) {
4100
4623
  if (!ids.has(id)) {
4101
- invalid6(`${source}.route names the unknown operation "${id}".`, source);
4624
+ invalid8(`${source}.route names the unknown operation "${id}".`, source);
4102
4625
  }
4103
4626
  }
4104
4627
  const routes = new Map;
@@ -4109,7 +4632,7 @@ function operationRoutes2(operations, options, source = "openapi") {
4109
4632
  const path = base + (chosen.path ?? `/${kebab(operation.command)}/${kebab(operation.name)}`);
4110
4633
  const key = `${method} ${path}`;
4111
4634
  if (taken.has(key)) {
4112
- invalid6(`Two operations are routed to ${method.toUpperCase()} ${path}.`, source);
4635
+ invalid8(`Two operations are routed to ${method.toUpperCase()} ${path}.`, source);
4113
4636
  }
4114
4637
  taken.add(key);
4115
4638
  routes.set(operation.id, { method, path });
@@ -4215,7 +4738,7 @@ function parametersOf(operation, schema) {
4215
4738
  const field = operation.input.properties[name];
4216
4739
  const type = property.type;
4217
4740
  if (!isProperty2(field) || type === "object" || type === "array" || Array.isArray(type) && type.some((t) => t === "object" || t === "array")) {
4218
- invalid6(`${operation.id} uses a ${"get/delete"} route, whose input becomes query parameters, but "${name}" is not a scalar; route it with method "post".`);
4741
+ invalid8(`${operation.id} uses a ${"get/delete"} route, whose input becomes query parameters, but "${name}" is not a scalar; route it with method "post".`);
4219
4742
  }
4220
4743
  const { description, ...rest } = property;
4221
4744
  return {
@@ -4242,10 +4765,10 @@ var openapi2 = sinkOf2({
4242
4765
  for (const record of records) {
4243
4766
  const name = record.name;
4244
4767
  if (!/^[A-Za-z0-9._-]+$/.test(name) || name === "Problem") {
4245
- invalid6(`The record class "${name}" cannot be a component name; give the class a name that is not "Problem".`);
4768
+ invalid8(`The record class "${name}" cannot be a component name; give the class a name that is not "Problem".`);
4246
4769
  }
4247
4770
  if (classes.has(name)) {
4248
- invalid6(`Two record classes are named "${name}"; rename one.`);
4771
+ invalid8(`Two record classes are named "${name}"; rename one.`);
4249
4772
  }
4250
4773
  classes.set(name, record);
4251
4774
  models[name] = record;
@@ -4265,7 +4788,7 @@ var openapi2 = sinkOf2({
4265
4788
  const { method, path } = routes.get(operation.id);
4266
4789
  const operationId = operation.id.replaceAll(".", "_");
4267
4790
  if (operationIds.has(operationId)) {
4268
- invalid6(`The operationId "${operationId}" is produced twice.`);
4791
+ invalid8(`The operationId "${operationId}" is produced twice.`);
4269
4792
  }
4270
4793
  operationIds.add(operationId);
4271
4794
  const slot = paths[path] ?? {};
@@ -5016,4 +5539,4 @@ var entityOf2 = entityBuilderOf2({
5016
5539
  var recordOf2 = recordBuilderOf2(StdRecordPlugins2);
5017
5540
  // src/std/stages.ts
5018
5541
  var StdStages2 = stageCatalogOf2(select2, openapi2);
5019
- export { InvalidEnvironment2, environmentOf2, isEnvironment2, guard2, ReservedEntityKey2, ImmutableEntityKey2, DeletedEntity2, entityBuilderOf2, isEntityClass2, isEntity2, eventOf2, ENTITY_IDENTITY_KEYS2, RESERVED_ENTITY_KEYS2, EntityUniqueKeys2, AbstractEntity2, InvalidRepositoryQuery2, InvalidRepositoryDeclaration2, InvalidRepositoryEntity2, InvalidRepositoryData2, RepositoryUniqueViolation2, RepositoryEntityNotFound2, providerOf2, providerDescriptionOf2, inMemoryProvider2, AbstractRepository2, repositoryOf2, RepositoryOf2, isRepository2, InvalidUseCaseStep2, InvalidUseCaseDeclaration2, UseCaseAlreadyExecuted2, InvalidUseCaseDependencies2, AbstractUseCase2, useCaseOf2, isUseCaseClass2, isUseCase2, InvalidCommandDeclaration2, UnknownCommandOperation2, InvalidCommandContext2, InvalidCommandEnvironment2, CommandEventFailed2, commandOf2, isCommand2, InvalidApplicationDeclaration2, DuplicateApplicationDeclaration2, ApplicationConflict2, UnregisteredApplicationReference2, UnregisteredRepositoryDependency2, UnregisteredUseCaseDependency2, ApplicationNotStarted2, ApplicationAlreadyStarted2, InvalidApplicationEnvironment2, InvalidOpenAPIModel2, InvalidOutputStage2, DuplicateOperation2, toOpenAPI2, transformOf2, sinkOf2, stageDescriptionOf2, stageKindOf2, stageCatalogOf2, operationOf2, outputOf2, isOutput2, useApplication2, resetApplication2, providerFrom2, isProviderFactory2, isApplication2, applicationOf2, blueprintPluginOf2, statusOf2, PROBLEM_MEDIA_TYPE2, problemOf2, outputValueOf2, routeOptionsIssue2, operationRoutes2, openapi2, select2, recordPluginCatalogOf2, Email2, Phone2, Url2, Ip2, enumOf2, SchemaProperty2, PasswordHasherRequired2, SyncVerifyUnsupported2, configure2, resetRuntime2, Id2, Slug2, PASSWORD_MIN_LENGTH2, PASSWORD_MAX_LENGTH2, PASSWORD_SPECIAL_CHARACTERS2, isPasswordHash2, passwordIssues2, Password2, StringProperty2, Text2, NumberProperty2, Integer2, BooleanProperty2, DateTime2, DateProperty2, CreatedAt2, UpdatedAt2, DeletedAt2, Std2, blueprintOf2, Publishable2, Archivable2, Taggable2, Ownable2, Authenticatable2, StdRecordPlugins2, entityOf2, recordOf2, StdStages2 };
5542
+ export { InvalidEnvironment2, environmentOf2, isEnvironment2, guard2, InvalidApplicationDeclaration2, DuplicateApplicationDeclaration2, ApplicationConflict2, UnregisteredApplicationReference2, UnregisteredRepositoryDependency2, UnregisteredUseCaseDependency2, ApplicationNotStarted2, ProviderNotBound2, ApplicationAlreadyStarted2, InvalidApplicationEnvironment2, useApplication2, resetApplication2, InvalidLogDeclaration2, LogLevels2, logProviderOf2, consoleLogProvider2, jsonLogProvider2, memoryLogProvider2, Logger2, ReservedEntityKey2, ImmutableEntityKey2, DeletedEntity2, entityBuilderOf2, isEntityClass2, isEntity2, eventOf2, ENTITY_IDENTITY_KEYS2, RESERVED_ENTITY_KEYS2, EntityUniqueKeys2, AbstractEntity2, InvalidRepositoryQuery2, InvalidRepositoryDeclaration2, InvalidRepositoryEntity2, InvalidRepositoryData2, RepositoryUniqueViolation2, RepositoryEntityNotFound2, providerOf2, providerDescriptionOf2, inMemoryProvider2, AbstractRepository2, repositoryOf2, RepositoryOf2, isRepository2, InvalidUseCaseStep2, InvalidUseCaseDeclaration2, UseCaseAlreadyExecuted2, InvalidUseCaseDependencies2, AbstractUseCase2, useCaseOf2, isUseCaseClass2, isUseCase2, InvalidCommandDeclaration2, UnknownCommandOperation2, InvalidCommandContext2, InvalidCommandEnvironment2, CommandEventFailed2, commandOf2, isCommand2, InvalidOpenAPIModel2, InvalidOutputStage2, DuplicateOperation2, toOpenAPI2, transformOf2, sinkOf2, stageDescriptionOf2, stageKindOf2, stageCatalogOf2, operationOf2, outputOf2, isOutput2, providerFrom2, logFrom2, isLogFactory2, isProviderFactory2, providersOf2, isProviderSlot2, isProviderStorage2, isApplication2, applicationOf2, blueprintPluginOf2, statusOf2, PROBLEM_MEDIA_TYPE2, problemOf2, outputValueOf2, routeOptionsIssue2, operationRoutes2, openapi2, select2, recordPluginCatalogOf2, Email2, Phone2, Url2, Ip2, enumOf2, SchemaProperty2, PasswordHasherRequired2, SyncVerifyUnsupported2, configure2, resetRuntime2, Id2, Slug2, PASSWORD_MIN_LENGTH2, PASSWORD_MAX_LENGTH2, PASSWORD_SPECIAL_CHARACTERS2, isPasswordHash2, passwordIssues2, Password2, StringProperty2, Text2, NumberProperty2, Integer2, BooleanProperty2, DateTime2, DateProperty2, CreatedAt2, UpdatedAt2, DeletedAt2, Std2, blueprintOf2, Publishable2, Archivable2, Taggable2, Ownable2, Authenticatable2, StdRecordPlugins2, entityOf2, recordOf2, StdStages2 };