@afora/core 0.0.4 → 0.0.6

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.
Files changed (36) hide show
  1. package/README.md +2 -0
  2. package/dist/core/application/exceptions.d.ts +9 -0
  3. package/dist/core/application/providers.d.ts +46 -0
  4. package/dist/core/application/types/application.interface.d.ts +70 -15
  5. package/dist/core/blueprint/types/slice.interface.d.ts +5 -2
  6. package/dist/core/entity/abstract-entity.d.ts +1 -1
  7. package/dist/core/entity/types/entity.interface.d.ts +13 -7
  8. package/dist/core/exception/exception.d.ts +10 -1
  9. package/dist/core/output/status.d.ts +2 -2
  10. package/dist/core/property/property.d.ts +4 -4
  11. package/dist/core/property/types/optional-property.type.d.ts +2 -2
  12. package/dist/core/property/types/property-type.interface.d.ts +17 -0
  13. package/dist/core/record/abstract-record.d.ts +4 -4
  14. package/dist/core/record/exceptions.d.ts +2 -0
  15. package/dist/core/record/name.d.ts +16 -0
  16. package/dist/core/record/record.d.ts +4 -3
  17. package/dist/core/record/types/plugin.interface.d.ts +11 -0
  18. package/dist/core/record/types/record.interface.d.ts +71 -10
  19. package/dist/core/repository/cursor.d.ts +13 -0
  20. package/dist/core/repository/exceptions.d.ts +10 -0
  21. package/dist/core/repository/query.d.ts +4 -4
  22. package/dist/core/repository/repository.d.ts +1 -1
  23. package/dist/core/repository/state.d.ts +3 -2
  24. package/dist/core/repository/types/query.interface.d.ts +37 -0
  25. package/dist/core/repository/types/repository.interface.d.ts +10 -6
  26. package/dist/core/use-case/steps/types/steps.interface.d.ts +1 -1
  27. package/dist/experimental.js +3 -2
  28. package/dist/{index-hn82hm31.js → index-3nd9bjnc.js} +69 -10
  29. package/dist/{index-ewm6r4nw.js → index-gbgc40xe.js} +452 -96
  30. package/dist/index.d.ts +9 -7
  31. package/dist/index.js +16 -2
  32. package/dist/std/properties/password.d.ts +5 -1
  33. package/dist/std/record-plugins.d.ts +5 -5
  34. package/dist/testing/index.js +91 -2
  35. package/dist/testing/provider-contract.d.ts +5 -0
  36. package/package.json +1 -1
@@ -9,6 +9,8 @@ import {
9
9
  isBlueprint2,
10
10
  isRecordClass2,
11
11
  recordFieldOf2,
12
+ recordNameOf2,
13
+ declaredRecordNameOf,
12
14
  isBlueprintList2,
13
15
  UNEXPECTED_ERROR2,
14
16
  Exception2,
@@ -40,7 +42,7 @@ import {
40
42
  exports_exceptions3,
41
43
  propertyOf2,
42
44
  blueprintBuilderOf2
43
- } from "./index-hn82hm31.js";
45
+ } from "./index-3nd9bjnc.js";
44
46
 
45
47
  // src/core/environment/environment.ts
46
48
  var exports_environment = {};
@@ -60,6 +62,7 @@ __export(exports_exceptions4, {
60
62
  DuplicateApplicationDeclaration: () => DuplicateApplicationDeclaration2,
61
63
  InvalidApplicationDeclaration: () => InvalidApplicationDeclaration2,
62
64
  InvalidApplicationEnvironment: () => InvalidApplicationEnvironment2,
65
+ ProviderNotBound: () => ProviderNotBound2,
63
66
  UnregisteredApplicationReference: () => UnregisteredApplicationReference2,
64
67
  UnregisteredRepositoryDependency: () => UnregisteredRepositoryDependency2,
65
68
  UnregisteredUseCaseDependency: () => UnregisteredUseCaseDependency2
@@ -82,6 +85,7 @@ __export(exports_exceptions6, {
82
85
  // src/core/repository/exceptions.ts
83
86
  var exports_exceptions7 = {};
84
87
  __export(exports_exceptions7, {
88
+ InvalidRepositoryCursor: () => InvalidRepositoryCursor2,
85
89
  InvalidRepositoryData: () => InvalidRepositoryData2,
86
90
  InvalidRepositoryDeclaration: () => InvalidRepositoryDeclaration2,
87
91
  InvalidRepositoryEntity: () => InvalidRepositoryEntity2,
@@ -268,6 +272,13 @@ var ApplicationNotStarted2 = exceptionOf2({
268
272
  application
269
273
  }) => application === undefined ? "No application is started: call await app.start() first." : `The application "${application}" is not started: call await app.start() first.`
270
274
  });
275
+ var ProviderNotBound2 = exceptionOf2({
276
+ layer: "application" /* Application */,
277
+ name: "ProviderNotBound",
278
+ message: ({
279
+ provider
280
+ }) => `The provider "${provider}" is made by an application: register its storage in applicationOf({ providers }) and call the repository through the application.`
281
+ });
271
282
  var ApplicationAlreadyStarted2 = exceptionOf2({
272
283
  layer: "application" /* Application */,
273
284
  name: "ApplicationAlreadyStarted",
@@ -632,7 +643,7 @@ function clockOf(entity) {
632
643
  function assertAlive(entity) {
633
644
  if (entity.isDeleted) {
634
645
  throw new DeletedEntity2({
635
- source: entity.constructor.name,
646
+ source: recordNameOf2(entity.constructor),
636
647
  details: { id: identityOf(entity).id }
637
648
  });
638
649
  }
@@ -682,7 +693,7 @@ class AbstractEntity2 extends AbstractRecord2 {
682
693
  const name = String(key);
683
694
  if (ENTITY_IDENTITY_KEYS2.includes(name)) {
684
695
  throw new ImmutableEntityKey2({
685
- source: this.constructor.name,
696
+ source: recordNameOf2(this.constructor),
686
697
  details: { key: name }
687
698
  });
688
699
  }
@@ -707,6 +718,13 @@ var InvalidRepositoryQuery2 = exceptionOf2({
707
718
  name: "InvalidRepositoryQuery",
708
719
  message: ({ reason }) => reason
709
720
  });
721
+ var InvalidRepositoryCursor2 = exceptionOf2({
722
+ layer: "repository" /* Repository */,
723
+ name: "InvalidRepositoryCursor",
724
+ message: ({
725
+ reason
726
+ }) => reason
727
+ });
710
728
  var InvalidRepositoryDeclaration2 = exceptionOf2({
711
729
  layer: "repository" /* Repository */,
712
730
  name: "InvalidRepositoryDeclaration",
@@ -733,6 +751,90 @@ var RepositoryEntityNotFound2 = exceptionOf2({
733
751
  message: ({ id }, source) => `"${source}" holds no entity of id "${id}".`
734
752
  });
735
753
 
754
+ // src/core/repository/cursor.ts
755
+ var VERSION = 1;
756
+ var signatureOf = (orders) => orders.map(({ key, direction = "asc" }) => `${key}:${direction}`);
757
+ function pack(value) {
758
+ if (value === undefined || value === null)
759
+ return null;
760
+ if (value instanceof Date)
761
+ return { d: value.toISOString() };
762
+ if (typeof value === "bigint")
763
+ return { b: value.toString() };
764
+ return value;
765
+ }
766
+ function unpack(value) {
767
+ if (value === null)
768
+ return;
769
+ if (["string", "number", "boolean"].includes(typeof value))
770
+ return value;
771
+ if (typeof value === "object") {
772
+ const { d, b } = value;
773
+ const keys = Object.keys(value);
774
+ if (keys.length === 1 && typeof d === "string") {
775
+ const date = new Date(d);
776
+ if (!Number.isNaN(date.getTime()))
777
+ return date;
778
+ }
779
+ if (keys.length === 1 && typeof b === "string" && /^-?\d+$/.test(b))
780
+ return BigInt(b);
781
+ }
782
+ throw new SyntaxError("value");
783
+ }
784
+ function encodeText(text) {
785
+ let binary = "";
786
+ for (const byte of new TextEncoder().encode(text))
787
+ binary += String.fromCharCode(byte);
788
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
789
+ }
790
+ function decodeText(encoded) {
791
+ if (!/^[A-Za-z0-9_-]+$/.test(encoded))
792
+ throw new SyntaxError("alphabet");
793
+ const binary = atob(encoded.replaceAll("-", "+").replaceAll("_", "/"));
794
+ return new TextDecoder("utf-8", { fatal: true }).decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));
795
+ }
796
+ function encodeCursor(orders, row) {
797
+ const payload = {
798
+ v: VERSION,
799
+ k: signatureOf(orders),
800
+ a: orders.map(({ key }) => pack(row[key]))
801
+ };
802
+ return encodeText(JSON.stringify(payload));
803
+ }
804
+ function decodeCursor(source, orders, cursor) {
805
+ const fail = (reason) => {
806
+ throw new InvalidRepositoryCursor2({
807
+ source,
808
+ details: {
809
+ reason,
810
+ issues: [
811
+ { code: "invalid_value", path: ["after"], message: reason, source }
812
+ ]
813
+ }
814
+ });
815
+ };
816
+ if (typeof cursor !== "string" || cursor === "")
817
+ return fail("Expected a cursor given by findCursor.");
818
+ let payload;
819
+ let values;
820
+ try {
821
+ payload = JSON.parse(decodeText(cursor));
822
+ if (typeof payload !== "object" || payload === null)
823
+ throw new SyntaxError;
824
+ if (!Array.isArray(payload.k) || !Array.isArray(payload.a))
825
+ throw new SyntaxError;
826
+ values = payload.a.map(unpack);
827
+ } catch {
828
+ return fail("The cursor is not one given by findCursor.");
829
+ }
830
+ if (payload.v !== VERSION)
831
+ return fail("The cursor was made by another version.");
832
+ const expected = signatureOf(orders);
833
+ if (payload.k?.length !== expected.length || values.length !== expected.length || expected.some((key, index) => payload.k?.[index] !== key))
834
+ return fail("The cursor was made for another orderBy.");
835
+ return values;
836
+ }
837
+
736
838
  // src/core/json.ts
737
839
  function frozenJson(value, subject, fail, seen = new Set) {
738
840
  if (value === null || typeof value === "string" || typeof value === "boolean")
@@ -790,7 +892,8 @@ function plain(value) {
790
892
  function isMembership(value) {
791
893
  return plain(value) && Object.keys(value).length === 1 && Array.isArray(value.in);
792
894
  }
793
- function prepareQuery(context, input = {}, map = (_key, value) => value) {
895
+ var QUERY_OPTIONS = ["where", "orderBy", "limit", "offset", "withDeleted"];
896
+ function prepareQuery(context, input = {}, map = (_key, value) => value, options = QUERY_OPTIONS) {
794
897
  const fail = (reason) => {
795
898
  throw new InvalidRepositoryQuery2({
796
899
  source: context.name,
@@ -800,7 +903,7 @@ function prepareQuery(context, input = {}, map = (_key, value) => value) {
800
903
  if (!plain(input))
801
904
  return fail("Expected a query object.");
802
905
  for (const key of Object.keys(input))
803
- if (!["where", "orderBy", "limit", "offset", "withDeleted"].includes(key))
906
+ if (!options.includes(key))
804
907
  fail(`Unknown query option "${key}".`);
805
908
  const properties = context.entity.blueprint.properties;
806
909
  const keyOf = (key) => {
@@ -882,26 +985,32 @@ function compareScalars(a, b) {
882
985
  ];
883
986
  return x < y ? -1 : x > y ? 1 : 0;
884
987
  }
988
+ function compareRows(orders, a, b) {
989
+ for (const { key, direction } of orders) {
990
+ const [x, y] = [a[key], b[key]];
991
+ const [missingX, missingY] = [
992
+ x === undefined || x === null,
993
+ y === undefined || y === null
994
+ ];
995
+ let result = missingX || missingY ? Number(!missingX) - Number(!missingY) : compareScalars(x, y);
996
+ if (direction === "desc")
997
+ result = -result;
998
+ if (result !== 0)
999
+ return result;
1000
+ }
1001
+ return 0;
1002
+ }
885
1003
  function orderRows(rows, query) {
886
1004
  const orders = query.orderBy ?? [];
887
- return [...rows].sort((a, b) => {
888
- for (const { key, direction } of orders) {
889
- const [x, y] = [a[key], b[key]];
890
- const [missingX, missingY] = [
891
- x === undefined || x === null,
892
- y === undefined || y === null
893
- ];
894
- let result = missingX || missingY ? Number(!missingX) - Number(!missingY) : compareScalars(x, y);
895
- if (direction === "desc")
896
- result = -result;
897
- if (result !== 0)
898
- return result;
899
- }
900
- return 0;
901
- });
1005
+ return [...rows].sort((a, b) => compareRows(orders, a, b));
1006
+ }
1007
+ function rowsAfter(rows, orders, after) {
1008
+ const position = Object.fromEntries(orders.map(({ key }, index) => [key, after[index]]));
1009
+ return rows.filter((row) => compareRows(orders, row, position) > 0);
902
1010
  }
903
1011
  function selectRows(rows, query) {
904
- const matched = orderRows([...rows].filter((data) => matchesQuery(data, query)), query);
1012
+ const ordered = orderRows([...rows].filter((data) => matchesQuery(data, query)), query);
1013
+ const matched = query.after === undefined ? ordered : rowsAfter(ordered, query.orderBy ?? [], query.after);
905
1014
  const start = query.offset ?? 0;
906
1015
  return matched.slice(start, query.limit === undefined ? undefined : start + query.limit);
907
1016
  }
@@ -953,7 +1062,7 @@ function scopeOf(initial, root) {
953
1062
  }
954
1063
  };
955
1064
  const provider = {
956
- capabilities: Object.freeze({ transactions: true }),
1065
+ capabilities: Object.freeze({ transactions: true, keyset: true }),
957
1066
  findOne: (context, query = {}) => whenIdle(() => {
958
1067
  const [first] = selectRows(tableOf(context).values(), {
959
1068
  ...query,
@@ -1083,31 +1192,31 @@ var origins = new WeakMap;
1083
1192
  function declarationOf(state) {
1084
1193
  return origins.get(state) ?? state;
1085
1194
  }
1086
- function bindState(state, provider, bound = new Map) {
1195
+ function bindState(state, provider, instances, bound = new Map) {
1087
1196
  const done = bound.get(state);
1088
1197
  if (done)
1089
1198
  return done;
1090
1199
  const copy = {
1091
1200
  ...state,
1092
- provider: state.explicitProvider ? state.provider : provider,
1201
+ provider: state.explicitProvider ? instances?.get(state.provider) ?? state.provider : provider ?? state.provider,
1093
1202
  context: undefined,
1094
1203
  relations: new Map([...state.relations].map(([target, related]) => [
1095
1204
  target,
1096
- bindState(related, provider, bound)
1205
+ bindState(related, provider, instances, bound)
1097
1206
  ]))
1098
1207
  };
1099
1208
  origins.set(copy, declarationOf(state));
1100
1209
  bound.set(state, copy);
1101
1210
  return copy;
1102
1211
  }
1103
- function bindRepository(repository, provider) {
1212
+ function bindRepository(repository, provider, instances) {
1104
1213
  const state = stateOf(repository);
1105
1214
  const Declared = repository.constructor;
1106
1215
 
1107
1216
  class Bound extends Declared {
1108
1217
  }
1109
1218
  Object.defineProperty(Bound, "name", { value: Declared.name });
1110
- states.set(Bound, bindState(state, provider));
1219
+ states.set(Bound, bindState(state, provider, instances));
1111
1220
  return Object.freeze(new Bound);
1112
1221
  }
1113
1222
 
@@ -1185,7 +1294,7 @@ function relationsOf(name, entity, relations) {
1185
1294
  if (!state)
1186
1295
  return invalid("Expected relations made by repositoryOf.");
1187
1296
  if (related.some((other) => other.entity === state.entity)) {
1188
- return invalid(`Relation "${state.entity.name}" is duplicated.`);
1297
+ return invalid(`Relation "${recordNameOf2(state.entity)}" is duplicated.`);
1189
1298
  }
1190
1299
  if (typeof state.provider.findMany !== "function") {
1191
1300
  return invalid(`The provider of "${state.name}" has no "findMany" to load it.`);
@@ -1198,10 +1307,10 @@ function relationsOf(name, entity, relations) {
1198
1307
  const matches = related.filter((state) => state.entity === target || state.entity.prototype instanceof target);
1199
1308
  const exact = matches.find((state) => state.entity === target);
1200
1309
  if (!exact && matches.length === 0) {
1201
- return invalid(`Missing relation: no repository of "${target.name}".`);
1310
+ return invalid(`Missing relation: no repository of "${recordNameOf2(target)}".`);
1202
1311
  }
1203
1312
  if (!exact && matches.length > 1) {
1204
- return invalid(`Relation "${target.name}" matches more than one repository.`);
1313
+ return invalid(`Relation "${recordNameOf2(target)}" matches more than one repository.`);
1205
1314
  }
1206
1315
  const state = exact ?? matches[0];
1207
1316
  map.set(target, state);
@@ -1209,7 +1318,7 @@ function relationsOf(name, entity, relations) {
1209
1318
  }
1210
1319
  for (const state of related) {
1211
1320
  if (!used.has(state)) {
1212
- return invalid(`Relation "${state.entity.name}" is not held by ${entity.name}.`);
1321
+ return invalid(`Relation "${recordNameOf2(state.entity)}" is not held by ${recordNameOf2(entity)}.`);
1213
1322
  }
1214
1323
  }
1215
1324
  return map;
@@ -1360,6 +1469,44 @@ var findPage = action(async (self, query) => {
1360
1469
  ]);
1361
1470
  return { items: await self.hydrateMany(rows), total, limit, offset };
1362
1471
  });
1472
+ var CURSOR_OPTIONS = ["where", "orderBy", "limit", "withDeleted", "after"];
1473
+ var findCursor = action(async (self, query = {}) => {
1474
+ const { context } = self;
1475
+ const fail = (reason) => {
1476
+ throw new InvalidRepositoryQuery2({
1477
+ source: context.name,
1478
+ details: { reason }
1479
+ });
1480
+ };
1481
+ const prepared = prepareQuery(context, query, (key, value) => queryValueOf(context, key, value), CURSOR_OPTIONS);
1482
+ const { after } = query;
1483
+ const { where, limit = 20 } = prepared;
1484
+ if (limit < 1)
1485
+ fail("Expected a positive integer for limit.");
1486
+ const withDeleted = prepared.withDeleted ?? stateOf(self).deletion === "hard";
1487
+ const declared = prepared.orderBy ?? [];
1488
+ const orderBy = declared.some(({ key }) => key === "id") ? declared : [...declared, { key: "id", direction: "asc" }];
1489
+ for (const { key } of orderBy) {
1490
+ if (context.entity.options.sensitive.includes(key))
1491
+ fail(`"${key}" is sensitive and cannot order a cursor.`);
1492
+ }
1493
+ if (self.provider.capabilities?.keyset !== true)
1494
+ fail("The provider does not read by cursor (capabilities.keyset).");
1495
+ const rows = await self.provider.findMany(context, {
1496
+ where,
1497
+ orderBy,
1498
+ withDeleted,
1499
+ limit: limit + 1,
1500
+ ...after === undefined ? {} : { after: decodeCursor(context.name, orderBy, after) }
1501
+ });
1502
+ const page = rows.slice(0, limit);
1503
+ const last = page.at(-1);
1504
+ return {
1505
+ items: await self.hydrateMany(page),
1506
+ limit,
1507
+ next: rows.length > limit && last !== undefined ? encodeCursor(orderBy, last) : undefined
1508
+ };
1509
+ });
1363
1510
  var create = action(async (self, input) => {
1364
1511
  const { entity: Entity } = self.context;
1365
1512
  const entity = input instanceof Entity ? input : new Entity(input);
@@ -1404,7 +1551,8 @@ var NEEDS = {
1404
1551
  count: ["count"],
1405
1552
  findMany: ["findMany"],
1406
1553
  exists: ["findOne"],
1407
- findPage: ["findMany", "count"]
1554
+ findPage: ["findMany", "count"],
1555
+ findCursor: ["findMany"]
1408
1556
  };
1409
1557
  function capitalize(key) {
1410
1558
  return `${key.charAt(0).toUpperCase()}${key.slice(1)}`;
@@ -1430,7 +1578,7 @@ function repositoryOf2(settings) {
1430
1578
  if (!isEntityClass2(entity) || !(EntityUniqueKeys2 in entity)) {
1431
1579
  invalid3("repositoryOf", "Expected an entity class made by entityOf.");
1432
1580
  }
1433
- const name = `${entity.name}Repository`;
1581
+ const name = `${recordNameOf2(entity)}Repository`;
1434
1582
  const deletion = settings.deletion ?? "hard";
1435
1583
  if (deletion !== "hard" && deletion !== "soft") {
1436
1584
  invalid3(name, 'Expected deletion to be "hard" or "soft".');
@@ -1460,13 +1608,14 @@ function repositoryOf2(settings) {
1460
1608
  methods.set("findMany", ["findMany", findMany]);
1461
1609
  methods.set("exists", ["exists", exists]);
1462
1610
  methods.set("findPage", ["findPage", findPage]);
1611
+ methods.set("findCursor", ["findCursor", findCursor]);
1463
1612
 
1464
1613
  class DeclaredRepository extends AbstractRepository2 {
1465
1614
  }
1466
1615
  for (const name_ of new Set(actions)) {
1467
1616
  const method = typeof name_ === "string" ? methods.get(name_) : undefined;
1468
1617
  if (!method)
1469
- invalid3(name, unknownAction(entity.name, String(name_), methods));
1618
+ invalid3(name, unknownAction(recordNameOf2(entity), String(name_), methods));
1470
1619
  const [kind, implementation] = method;
1471
1620
  const needs = deletion === "soft" && kind === "delete" ? ["findOne", "update"] : NEEDS[kind] ?? [];
1472
1621
  for (const need of needs) {
@@ -1758,7 +1907,7 @@ var ENSURE_KINDS = new Set([
1758
1907
  "true",
1759
1908
  "false"
1760
1909
  ]);
1761
- var READ_ACTION = /^(?:findBy.+|findManyBy.+|findMany|exists|findPage|count)$/;
1910
+ var READ_ACTION = /^(?:findBy.+|findManyBy.+|findMany|exists|findPage|findCursor|count)$/;
1762
1911
  var WRITE_ACTIONS = new Set(["create", "update", "delete"]);
1763
1912
  function validateSteps(declaration, steps) {
1764
1913
  const { name } = declaration;
@@ -2060,7 +2209,7 @@ function methodNamesOf(repository) {
2060
2209
  }
2061
2210
  function bindRepository2(repository, overrides, state, source) {
2062
2211
  const Entity = repository.entity;
2063
- const repositoryName = Entity.name;
2212
+ const repositoryName = recordNameOf2(Entity);
2064
2213
  const bound = Object.create(null);
2065
2214
  const view = new Proxy(Object.create(repository), {
2066
2215
  get: (_target, key, receiver) => {
@@ -2403,7 +2552,7 @@ function commandTransactionProvider(command) {
2403
2552
  return verifiers.get(command)?.();
2404
2553
  }
2405
2554
  var IDENTIFIER2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
2406
- function providersOf(useCases, declared) {
2555
+ function repositoryProvidersOf(useCases, declared) {
2407
2556
  const providers = new Map;
2408
2557
  const seen = new Set;
2409
2558
  const add = (state) => {
@@ -2437,7 +2586,7 @@ function providersOf(useCases, declared) {
2437
2586
  return providers;
2438
2587
  }
2439
2588
  function transactionProviderOf(name, useCases, declared = false) {
2440
- const providers = providersOf(useCases, declared);
2589
+ const providers = repositoryProvidersOf(useCases, declared);
2441
2590
  const invalid = (reason, repositories) => {
2442
2591
  throw new InvalidCommandDeclaration2({
2443
2592
  source: name,
@@ -3352,6 +3501,106 @@ function checkTransactions(source, provider, commands) {
3352
3501
  }
3353
3502
  }
3354
3503
 
3504
+ // src/core/application/providers.ts
3505
+ var slots = new WeakMap;
3506
+ var storages = new WeakMap;
3507
+ var METHODS2 = [
3508
+ "findOne",
3509
+ "findMany",
3510
+ "insert",
3511
+ "update",
3512
+ "remove",
3513
+ "count"
3514
+ ];
3515
+ var KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3516
+ var RESERVED = new Set(["inMemory", "__proto__", "constructor", "prototype"]);
3517
+ function invalid6(reason) {
3518
+ throw new InvalidApplicationDeclaration2({
3519
+ source: "providersOf",
3520
+ details: { reason }
3521
+ });
3522
+ }
3523
+ function isProviderObject(value) {
3524
+ return typeof value === "object" && value !== null && !isProviderFactory2(value) && METHODS2.some((method) => typeof value[method] === "function");
3525
+ }
3526
+ function slotOf(key, storage, value) {
3527
+ const slot = {
3528
+ [Kind2]: "provider-slot",
3529
+ key
3530
+ };
3531
+ if (isProviderFactory2(value)) {
3532
+ const unbound = () => {
3533
+ throw new ProviderNotBound2({ source: key, details: { provider: key } });
3534
+ };
3535
+ for (const method of METHODS2)
3536
+ slot[method] = unbound;
3537
+ slot.transaction = unbound;
3538
+ Object.defineProperty(slot, "capabilities", {
3539
+ get: () => {
3540
+ return;
3541
+ },
3542
+ enumerable: true
3543
+ });
3544
+ } else {
3545
+ const target = value;
3546
+ for (const method of [...METHODS2, "transaction", "close"]) {
3547
+ if (typeof target[method] === "function") {
3548
+ slot[method] = (...args) => target[method]?.(...args);
3549
+ }
3550
+ }
3551
+ Object.defineProperty(slot, "capabilities", {
3552
+ get: () => value.capabilities,
3553
+ enumerable: true
3554
+ });
3555
+ }
3556
+ Object.freeze(slot);
3557
+ slots.set(slot, { key, storage, value });
3558
+ return slot;
3559
+ }
3560
+ function providersOf2(entries) {
3561
+ if (typeof entries !== "object" || entries === null || Array.isArray(entries) || Object.keys(entries).length === 0) {
3562
+ invalid6("Expected an object of providers by key.");
3563
+ }
3564
+ const storage = Object.create(null);
3565
+ storage[Kind2] = "provider-storage";
3566
+ const seen = new Map;
3567
+ const list = [];
3568
+ for (const [key, value] of Object.entries(entries)) {
3569
+ if (!KEY.test(key) || RESERVED.has(key)) {
3570
+ invalid6(key === "inMemory" ? `"inMemory" is the built-in provider; choose another key.` : `Expected "${key}" to be an identifier that is not reserved.`);
3571
+ }
3572
+ if (isProviderSlot2(value)) {
3573
+ invalid6(`"${key}" holds a provider of another storage; a storage holds providers, not slots.`);
3574
+ }
3575
+ if (!isProviderFactory2(value) && !isProviderObject(value)) {
3576
+ invalid6(`Expected "${key}" to be a repository provider or a factory of providerFrom.`);
3577
+ }
3578
+ const other = seen.get(value);
3579
+ if (other !== undefined) {
3580
+ invalid6(`"${other}" and "${key}" hold the same provider.`);
3581
+ }
3582
+ seen.set(value, key);
3583
+ const slot = slotOf(key, storage, value);
3584
+ storage[key] = slot;
3585
+ list.push(slot);
3586
+ }
3587
+ Object.freeze(storage);
3588
+ storages.set(storage, Object.freeze(list));
3589
+ return storage;
3590
+ }
3591
+ function isProviderSlot2(value) {
3592
+ return typeof value === "object" && value !== null && slots.has(value);
3593
+ }
3594
+ function isProviderStorage2(value) {
3595
+ return typeof value === "object" && value !== null && storages.has(value);
3596
+ }
3597
+ function slotsOf(storage) {
3598
+ return storages.get(storage) ?? [];
3599
+ }
3600
+ function slotEntry(slot) {
3601
+ return slots.get(slot);
3602
+ }
3603
+
3355
3604
  // src/core/application/application.ts
3356
3605
  var KEYS = [
3357
3606
  "properties",
@@ -3367,6 +3616,11 @@ var KEYS = [
3367
3616
  ];
3368
3617
  var internals = new WeakMap;
3369
3618
  var logSettings = new WeakMap;
3619
+ var namedProviders = new WeakMap;
3620
+ var defaultSlots = new WeakMap;
3621
+ function referenceOf(slot) {
3622
+ return { kind: "ref", key: slotEntry(slot).key };
3623
+ }
3370
3624
  var KIND_OF = {
3371
3625
  properties: "property",
3372
3626
  blueprints: "blueprint",
@@ -3382,7 +3636,7 @@ var KIND_OF = {
3382
3636
  function isApplication2(value) {
3383
3637
  return typeof value === "object" && value !== null && value[Kind2] === "application";
3384
3638
  }
3385
- function invalid6(source, reason) {
3639
+ function invalid7(source, reason) {
3386
3640
  throw new InvalidApplicationDeclaration2({ source, details: { reason } });
3387
3641
  }
3388
3642
  function isOfKind(collection, value) {
@@ -3467,7 +3721,7 @@ function resolve(settings) {
3467
3721
  for (const other of settings.extends ?? []) {
3468
3722
  const resolved = internals.get(other);
3469
3723
  if (!resolved)
3470
- invalid6(source, "Expected `extends` to hold applications.");
3724
+ invalid7(source, "Expected `extends` to hold applications.");
3471
3725
  for (const key of KEYS) {
3472
3726
  for (const entry of resolved[key].values())
3473
3727
  add(entry);
@@ -3478,15 +3732,15 @@ function resolve(settings) {
3478
3732
  if (declared === undefined)
3479
3733
  continue;
3480
3734
  if (typeof declared !== "object" || declared === null || Array.isArray(declared)) {
3481
- invalid6(source, `Expected ${collection} to be an object of declarations.`);
3735
+ invalid7(source, `Expected ${collection} to be an object of declarations.`);
3482
3736
  }
3483
3737
  for (const [name, value] of Object.entries(declared)) {
3484
3738
  if (!isOfKind(collection, value)) {
3485
- invalid6(source, `${collection}.${name} is not a ${KIND_OF[collection]} (${collection} takes ${KIND_OF[collection]} declarations).`);
3739
+ invalid7(source, `${collection}.${name} is not a ${KIND_OF[collection]} (${collection} takes ${KIND_OF[collection]} declarations).`);
3486
3740
  }
3487
- const declaredName = collection === "useCases" ? value.useCaseName : collection === "commands" ? value.commandName : collection === "outputs" ? value.outputName : collection === "exceptions" ? value.name : name;
3741
+ const declaredName = collection === "useCases" ? value.useCaseName : collection === "commands" ? value.commandName : collection === "outputs" ? value.outputName : collection === "exceptions" ? value.name : collection === "records" || collection === "entities" ? declaredRecordNameOf(value) ?? name : name;
3488
3742
  if (declaredName !== name) {
3489
- invalid6(source, `${collection}.${name} must be keyed by its name, "${declaredName}".`);
3743
+ invalid7(source, `${collection}.${name} must be keyed by its name, "${declaredName}".`);
3490
3744
  }
3491
3745
  add({
3492
3746
  collection,
@@ -3539,7 +3793,7 @@ function validate(source, registry) {
3539
3793
  source,
3540
3794
  details: {
3541
3795
  holder: `${KIND_OF[collection]} "${entry.name}"`,
3542
- reference: `${isEntityClass2(record) ? "entity" : "record"} "${record.name}"`
3796
+ reference: `${isEntityClass2(record) ? "entity" : "record"} "${recordNameOf2(record)}"`
3543
3797
  }
3544
3798
  });
3545
3799
  }
@@ -3560,7 +3814,7 @@ function validate(source, registry) {
3560
3814
  }
3561
3815
  for (const relation of info.relations) {
3562
3816
  if (!repositories.has(relation)) {
3563
- unregistered(entry.name, `the repository of "${relation.entity.name}"`);
3817
+ unregistered(entry.name, `the repository of "${recordNameOf2(relation.entity)}"`);
3564
3818
  }
3565
3819
  }
3566
3820
  for (const environment of info.environments) {
@@ -3607,7 +3861,7 @@ function validateOutputs(source, registry) {
3607
3861
  for (const entry of registry.outputs.values()) {
3608
3862
  const output = entry.value;
3609
3863
  if (!output.outputName) {
3610
- invalid6(source, `outputs.${entry.name} has no name (an output made by pipe cannot be registered).`);
3864
+ invalid7(source, `outputs.${entry.name} has no name (an output made by pipe cannot be registered).`);
3611
3865
  }
3612
3866
  for (const command of output.commands) {
3613
3867
  if (commands.has(command))
@@ -3659,7 +3913,7 @@ function validateUseCases(source, registry) {
3659
3913
  source,
3660
3914
  details: {
3661
3915
  holder: `the ${part} of use case "${entry.name}"`,
3662
- reference: `${isEntityClass2(record) ? "entity" : "record"} "${record.name}"`
3916
+ reference: `${isEntityClass2(record) ? "entity" : "record"} "${recordNameOf2(record)}"`
3663
3917
  }
3664
3918
  });
3665
3919
  }
@@ -3682,7 +3936,7 @@ function validateUseCases(source, registry) {
3682
3936
  source,
3683
3937
  details: {
3684
3938
  holder,
3685
- reference: `${isEntityClass2(step.build) ? "entity" : "record"} "${step.build.name}"`
3939
+ reference: `${isEntityClass2(step.build) ? "entity" : "record"} "${recordNameOf2(step.build)}"`
3686
3940
  }
3687
3941
  });
3688
3942
  }
@@ -3768,7 +4022,7 @@ function fieldDescriptionOf(field, scope) {
3768
4022
  const ref = entity ?? scope.records.get(record.record);
3769
4023
  return {
3770
4024
  kind: entity === undefined ? "record" : "entity",
3771
- ref: ref ?? record.record.name,
4025
+ ref: ref ?? recordNameOf2(record.record),
3772
4026
  optional: record.optional
3773
4027
  };
3774
4028
  }
@@ -3809,7 +4063,7 @@ function recordDescriptionOf(entry, scope) {
3809
4063
  plugins
3810
4064
  };
3811
4065
  }
3812
- function describeRegistry(name, extended, registry, provider, log) {
4066
+ function describeRegistry(name, extended, registry, provider, named, log) {
3813
4067
  const scope = {
3814
4068
  properties: referencesOf(registry, "properties"),
3815
4069
  blueprints: referencesOf(registry, "blueprints"),
@@ -3849,7 +4103,19 @@ function describeRegistry(name, extended, registry, provider, log) {
3849
4103
  return {
3850
4104
  name,
3851
4105
  extends: extended.map((other) => other.name),
3852
- ...provider === undefined ? {} : { provider: providerDescriptionOf2(provider) },
4106
+ ...provider === undefined ? {} : { provider },
4107
+ ...named.size === 0 ? {} : {
4108
+ providers: Object.fromEntries([...named].map(([key, { slot, from }]) => {
4109
+ const { value } = slotEntry(slot);
4110
+ return [
4111
+ key,
4112
+ {
4113
+ from,
4114
+ ...isProviderFactory2(value) ? { kind: "opaque" } : providerDescriptionOf2(value)
4115
+ }
4116
+ ];
4117
+ }))
4118
+ },
3853
4119
  ...log === undefined ? {} : log === "opaque" ? { log: { kind: "opaque" } } : {
3854
4120
  log: {
3855
4121
  level: log.level,
@@ -3882,7 +4148,9 @@ function describeRegistry(name, extended, registry, provider, log) {
3882
4148
  deletion: info.deletion,
3883
4149
  relations: info.relations.map((relation) => repositories.get(relation)),
3884
4150
  environments: info.environments.map((environment) => environmentRefs.get(environment)),
3885
- ...info.provider === undefined ? {} : { provider: providerDescriptionOf2(info.provider) }
4151
+ ...info.provider === undefined ? {} : {
4152
+ provider: isProviderSlot2(info.provider) ? referenceOf(info.provider) : providerDescriptionOf2(info.provider)
4153
+ }
3886
4154
  };
3887
4155
  }),
3888
4156
  useCases: each("useCases", (entry) => {
@@ -3970,35 +4238,101 @@ function describeRegistry(name, extended, registry, provider, log) {
3970
4238
  function applicationOf2(settings) {
3971
4239
  const { name } = settings ?? {};
3972
4240
  if (typeof name !== "string" || name === "") {
3973
- invalid6("applicationOf", "Expected a name.");
4241
+ invalid7("applicationOf", "Expected a name.");
3974
4242
  }
3975
4243
  const extended = settings.extends ?? [];
3976
4244
  if (!Array.isArray(extended) || !extended.every(isApplication2)) {
3977
- invalid6(name, "Expected `extends` to be a list of applications.");
4245
+ invalid7(name, "Expected `extends` to be a list of applications.");
3978
4246
  }
3979
4247
  const registry = resolve(settings);
3980
4248
  validate(name, registry);
3981
4249
  validateUseCases(name, registry);
3982
4250
  validateCommands(name, registry);
3983
4251
  validateOutputs(name, registry);
3984
- const declaredProvider = settings.provider ?? [...extended].reverse().find((other) => other.provider)?.provider;
3985
- if (declaredProvider !== undefined && typeof declaredProvider !== "object") {
3986
- invalid6(name, "Expected `provider` to be a repository provider.");
4252
+ if (settings.providers !== undefined && !isProviderStorage2(settings.providers)) {
4253
+ invalid7(name, "Expected `providers` to be a storage made by providersOf.");
3987
4254
  }
3988
- const provider = isProviderFactory2(declaredProvider) ? deferredProvider(declaredProvider, name) : declaredProvider;
3989
- {
3990
- const registered = new Set([...registry.environments.values()].map((entry) => entry.key));
3991
- for (const environment of deferredEnvironments(provider)) {
3992
- if (registered.has(environment))
3993
- continue;
4255
+ const named = new Map;
4256
+ const addNamed = (entry) => {
4257
+ const { key } = slotEntry(entry.slot);
4258
+ const current = named.get(key);
4259
+ if (current !== undefined && current.slot !== entry.slot) {
4260
+ throw new ApplicationConflict2({
4261
+ source: name,
4262
+ details: {
4263
+ reason: `The provider "${key}" is declared by different storages, in "${current.from}" and "${entry.from}".`,
4264
+ origins: [current.from, entry.from],
4265
+ declarations: [`provider ${key}`, `provider ${key}`]
4266
+ }
4267
+ });
4268
+ }
4269
+ named.set(key, entry);
4270
+ };
4271
+ for (const other of extended) {
4272
+ for (const entry of namedProviders.get(other)?.values() ?? []) {
4273
+ addNamed(entry);
4274
+ }
4275
+ }
4276
+ for (const slot of settings.providers ? slotsOf(settings.providers) : []) {
4277
+ const inherited = named.get(slotEntry(slot).key);
4278
+ if (inherited?.slot === slot)
4279
+ continue;
4280
+ const { value } = slotEntry(slot);
4281
+ addNamed({
4282
+ slot,
4283
+ from: name,
4284
+ instance: isProviderFactory2(value) ? deferredProvider(value, name) : value
4285
+ });
4286
+ }
4287
+ const instances = new Map([...named.values()].map((entry) => [entry.slot, entry.instance]));
4288
+ const instanceOf = (slot, holder) => {
4289
+ const instance = instances.get(slot);
4290
+ if (instance === undefined) {
3994
4291
  throw new UnregisteredApplicationReference2({
3995
4292
  source: name,
3996
4293
  details: {
3997
- holder: "the provider",
3998
- reference: "an environment"
4294
+ holder,
4295
+ reference: `provider "${slotEntry(slot).key}"`
3999
4296
  }
4000
4297
  });
4001
4298
  }
4299
+ return instance;
4300
+ };
4301
+ for (const entry of registry.repositories.values()) {
4302
+ const declared = inspectRepository(entry.value).provider;
4303
+ if (isProviderSlot2(declared)) {
4304
+ instanceOf(declared, `repository "${entry.name}"`);
4305
+ }
4306
+ }
4307
+ const inheritedFrom = settings.provider === undefined ? [...extended].reverse().find((other) => other.provider) : undefined;
4308
+ const declaredProvider = settings.provider ?? inheritedFrom?.provider;
4309
+ if (declaredProvider !== undefined && typeof declaredProvider !== "object") {
4310
+ invalid7(name, "Expected `provider` to be a repository provider.");
4311
+ }
4312
+ const defaultSlot = isProviderSlot2(declaredProvider) ? declaredProvider : inheritedFrom === undefined ? undefined : defaultSlots.get(inheritedFrom);
4313
+ const provider = defaultSlot !== undefined ? instanceOf(defaultSlot, "the application") : isProviderFactory2(declaredProvider) ? deferredProvider(declaredProvider, name) : declaredProvider;
4314
+ const allProviders = [
4315
+ ...new Set([provider, ...instances.values()].filter((value) => value !== undefined))
4316
+ ];
4317
+ {
4318
+ const registered = new Set([...registry.environments.values()].map((entry) => entry.key));
4319
+ const holderOf = (instance) => {
4320
+ const entry = [...named.values()].find((candidate) => candidate.instance === instance);
4321
+ return entry === undefined || instance === provider ? "the provider" : `provider "${slotEntry(entry.slot).key}"`;
4322
+ };
4323
+ for (const instance of allProviders) {
4324
+ for (const environment of deferredEnvironments(instance)) {
4325
+ if (registered.has(environment))
4326
+ continue;
4327
+ throw new UnregisteredApplicationReference2({
4328
+ source: name,
4329
+ details: {
4330
+ holder: holderOf(instance),
4331
+ reference: "an environment"
4332
+ }
4333
+ });
4334
+ }
4335
+ }
4002
4336
  }
4003
4337
  const declaredLog = settings.log ?? [...extended].reverse().map((other) => logSettings.get(other)).find((log) => log !== undefined);
4004
4338
  const log = declaredLog === undefined ? undefined : applicationLogger(name);
@@ -4006,13 +4340,14 @@ function applicationOf2(settings) {
4006
4340
  log?.set(checkLogSettings(declaredLog));
4007
4341
  }
4008
4342
  const logger = log?.logger;
4009
- const rebinds = provider !== undefined || logger !== undefined;
4343
+ const bindsProviders = provider !== undefined || instances.size > 0;
4344
+ const rebinds = bindsProviders || logger !== undefined;
4010
4345
  const collection = (key) => Object.freeze(Object.fromEntries([...registry[key].values()].map((entry) => [entry.name, entry.value])));
4011
4346
  const bound = new Map;
4012
4347
  const boundByState = new Map;
4013
4348
  const repositories = {};
4014
4349
  for (const entry of registry.repositories.values()) {
4015
- const repository = provider === undefined ? entry.value : bindRepository(entry.value, provider);
4350
+ const repository = bindsProviders ? bindRepository(entry.value, provider, instances) : entry.value;
4016
4351
  bound.set(entry.value, repository);
4017
4352
  boundByState.set(entry.key, repository);
4018
4353
  repositories[entry.name] = repository;
@@ -4066,7 +4401,7 @@ function applicationOf2(settings) {
4066
4401
  events: declared.events,
4067
4402
  onEventError: declared.onEventError
4068
4403
  });
4069
- if (provider !== undefined)
4404
+ if (bindsProviders)
4070
4405
  verifyCommand(rebuilt);
4071
4406
  boundCommandOf.set(entry.value, rebuilt);
4072
4407
  commands[entry.name] = rebuilt;
@@ -4127,9 +4462,11 @@ function applicationOf2(settings) {
4127
4462
  if (isLogFactory2(declaredLog)) {
4128
4463
  log?.set(checkLogSettings(await declaredLog.create(context)));
4129
4464
  }
4130
- if (provider !== undefined && isDeferredProvider(provider)) {
4131
- const made = await startDeferred(provider, context);
4132
- checkTransactions(name, made, Object.entries(commands).filter(([, command]) => command.transaction && commandTransactionProvider(command) === provider).map(([key]) => key));
4465
+ for (const instance of allProviders) {
4466
+ if (!isDeferredProvider(instance))
4467
+ continue;
4468
+ const made = await startDeferred(instance, context);
4469
+ checkTransactions(name, made, Object.entries(commands).filter(([, command]) => command.transaction && commandTransactionProvider(command) === instance).map(([key]) => key));
4133
4470
  }
4134
4471
  started = true;
4135
4472
  };
@@ -4153,25 +4490,33 @@ function applicationOf2(settings) {
4153
4490
  exceptions: collection("exceptions"),
4154
4491
  useCase(useCase) {
4155
4492
  if (!boundUseCaseOf.has(useCase)) {
4156
- invalid6(name, "Expected a use case registered in the application.");
4493
+ invalid7(name, "Expected a use case registered in the application.");
4157
4494
  }
4158
4495
  return boundUseCaseOf.get(useCase);
4159
4496
  },
4497
+ async run(useCase, input = {}, options) {
4498
+ if (!boundUseCaseOf.has(useCase)) {
4499
+ invalid7(name, "Expected a use case registered in the application.");
4500
+ }
4501
+ const Bound = boundUseCaseOf.get(useCase);
4502
+ const instance = options?.context === undefined ? new Bound(input) : new Bound(input, { context: options.context });
4503
+ return instance.execute(options?.dependencies);
4504
+ },
4160
4505
  command(command) {
4161
4506
  if (!boundCommandOf.has(command)) {
4162
- invalid6(name, "Expected a command registered in the application.");
4507
+ invalid7(name, "Expected a command registered in the application.");
4163
4508
  }
4164
4509
  return boundCommandOf.get(command);
4165
4510
  },
4166
4511
  output(output) {
4167
4512
  if (!boundOutputOf.has(output)) {
4168
- invalid6(name, "Expected an output registered in the application.");
4513
+ invalid7(name, "Expected an output registered in the application.");
4169
4514
  }
4170
4515
  return boundOutputOf.get(output);
4171
4516
  },
4172
4517
  repository(repository) {
4173
4518
  if (!bound.has(repository)) {
4174
- invalid6(name, "Expected a repository registered in the application.");
4519
+ invalid7(name, "Expected a repository registered in the application.");
4175
4520
  }
4176
4521
  return bound.get(repository);
4177
4522
  },
@@ -4179,11 +4524,11 @@ function applicationOf2(settings) {
4179
4524
  return applicationOf2({
4180
4525
  name,
4181
4526
  extends: [application, ...others],
4182
- ...provider === undefined ? {} : { provider },
4527
+ ...provider === undefined ? {} : { provider: defaultSlot ?? provider },
4183
4528
  ...declaredLog === undefined ? {} : { log: declaredLog }
4184
4529
  });
4185
4530
  },
4186
- describe: () => describeRegistry(name, extended, registry, provider, isLogFactory2(declaredLog) ? "opaque" : log?.settings),
4531
+ describe: () => describeRegistry(name, extended, registry, defaultSlot !== undefined ? referenceOf(defaultSlot) : provider === undefined ? undefined : providerDescriptionOf2(provider), named, isLogFactory2(declaredLog) ? "opaque" : log?.settings),
4187
4532
  get started() {
4188
4533
  return started;
4189
4534
  },
@@ -4207,8 +4552,9 @@ function applicationOf2(settings) {
4207
4552
  setStartedApplication(application);
4208
4553
  })().then(() => application, async (error) => {
4209
4554
  starting = undefined;
4210
- if (provider !== undefined && isDeferredProvider(provider)) {
4211
- await stopProvider(provider);
4555
+ for (const instance of allProviders) {
4556
+ if (isDeferredProvider(instance))
4557
+ await stopProvider(instance);
4212
4558
  }
4213
4559
  if (isLogFactory2(declaredLog))
4214
4560
  log?.set(undefined);
@@ -4226,8 +4572,8 @@ function applicationOf2(settings) {
4226
4572
  });
4227
4573
  if (startedApplication() === application)
4228
4574
  setStartedApplication(undefined);
4229
- if (provider !== undefined)
4230
- await stopProvider(provider);
4575
+ for (const instance of allProviders)
4576
+ await stopProvider(instance);
4231
4577
  await log?.flush();
4232
4578
  if (isLogFactory2(declaredLog))
4233
4579
  log?.set(undefined);
@@ -4235,7 +4581,7 @@ function applicationOf2(settings) {
4235
4581
  get(declaration) {
4236
4582
  if (isEnvironment2(declaration)) {
4237
4583
  if (!registeredEnvironments.has(declaration)) {
4238
- invalid6(name, "Expected an environment registered in the application.");
4584
+ invalid7(name, "Expected an environment registered in the application.");
4239
4585
  }
4240
4586
  return declaration.load();
4241
4587
  }
@@ -4248,10 +4594,20 @@ function applicationOf2(settings) {
4248
4594
  if (isRepository2(declaration)) {
4249
4595
  return application.repository(declaration);
4250
4596
  }
4251
- return invalid6(name, "Expected a repository, use case, command, output or environment registered in the application.");
4597
+ if (isProviderSlot2(declaration)) {
4598
+ const instance = instances.get(declaration);
4599
+ if (instance === undefined) {
4600
+ invalid7(name, "Expected a provider of a storage registered in the application.");
4601
+ }
4602
+ return instance;
4603
+ }
4604
+ return invalid7(name, "Expected a repository, use case, command, output, environment or provider registered in the application.");
4252
4605
  }
4253
4606
  });
4254
4607
  internals.set(application, registry);
4608
+ namedProviders.set(application, named);
4609
+ if (defaultSlot !== undefined)
4610
+ defaultSlots.set(application, defaultSlot);
4255
4611
  if (declaredLog !== undefined)
4256
4612
  logSettings.set(application, declaredLog);
4257
4613
  return application;
@@ -4337,7 +4693,7 @@ function outputValueOf2(value) {
4337
4693
  }
4338
4694
 
4339
4695
  // src/core/output/openapi-sink.ts
4340
- var METHODS2 = ["get", "post", "put", "patch", "delete"];
4696
+ var METHODS3 = ["get", "post", "put", "patch", "delete"];
4341
4697
  function isObject(value) {
4342
4698
  return typeof value === "object" && value !== null && !Array.isArray(value);
4343
4699
  }
@@ -4363,8 +4719,8 @@ function routeOptionsIssue2(options, where) {
4363
4719
  if (!isObject(entry) || onlyKeys(entry, ["method", "path"], "route")) {
4364
4720
  return `Expected ${where}.route["${id}"] to be { method?, path? }.`;
4365
4721
  }
4366
- if (entry.method !== undefined && !METHODS2.includes(entry.method)) {
4367
- return `Expected ${where}.route["${id}"].method to be one of ${METHODS2.join(", ")}.`;
4722
+ if (entry.method !== undefined && !METHODS3.includes(entry.method)) {
4723
+ return `Expected ${where}.route["${id}"].method to be one of ${METHODS3.join(", ")}.`;
4368
4724
  }
4369
4725
  if (entry.path !== undefined && !isPath(entry.path)) {
4370
4726
  return `Expected ${where}.route["${id}"].path to start with "/" and have no parameters or query.`;
@@ -4402,7 +4758,7 @@ function validate2(options) {
4402
4758
  }
4403
4759
  return routeOptionsIssue2({ basePath, route }, "openapi");
4404
4760
  }
4405
- function invalid7(reason, source = "openapi") {
4761
+ function invalid8(reason, source = "openapi") {
4406
4762
  throw new InvalidOutputStage2({ source, details: { reason } });
4407
4763
  }
4408
4764
  function kebab(text) {
@@ -4414,7 +4770,7 @@ function operationRoutes2(operations, options, source = "openapi") {
4414
4770
  const ids = new Set(operations.map(({ id }) => id));
4415
4771
  for (const id of Object.keys(route)) {
4416
4772
  if (!ids.has(id)) {
4417
- invalid7(`${source}.route names the unknown operation "${id}".`, source);
4773
+ invalid8(`${source}.route names the unknown operation "${id}".`, source);
4418
4774
  }
4419
4775
  }
4420
4776
  const routes = new Map;
@@ -4425,7 +4781,7 @@ function operationRoutes2(operations, options, source = "openapi") {
4425
4781
  const path = base + (chosen.path ?? `/${kebab(operation.command)}/${kebab(operation.name)}`);
4426
4782
  const key = `${method} ${path}`;
4427
4783
  if (taken.has(key)) {
4428
- invalid7(`Two operations are routed to ${method.toUpperCase()} ${path}.`, source);
4784
+ invalid8(`Two operations are routed to ${method.toUpperCase()} ${path}.`, source);
4429
4785
  }
4430
4786
  taken.add(key);
4431
4787
  routes.set(operation.id, { method, path });
@@ -4531,7 +4887,7 @@ function parametersOf(operation, schema) {
4531
4887
  const field = operation.input.properties[name];
4532
4888
  const type = property.type;
4533
4889
  if (!isProperty2(field) || type === "object" || type === "array" || Array.isArray(type) && type.some((t) => t === "object" || t === "array")) {
4534
- invalid7(`${operation.id} uses a ${"get/delete"} route, whose input becomes query parameters, but "${name}" is not a scalar; route it with method "post".`);
4890
+ invalid8(`${operation.id} uses a ${"get/delete"} route, whose input becomes query parameters, but "${name}" is not a scalar; route it with method "post".`);
4535
4891
  }
4536
4892
  const { description, ...rest } = property;
4537
4893
  return {
@@ -4556,12 +4912,12 @@ var openapi2 = sinkOf2({
4556
4912
  const models = {};
4557
4913
  const classes = new Map;
4558
4914
  for (const record of records) {
4559
- const name = record.name;
4915
+ const name = recordNameOf2(record);
4560
4916
  if (!/^[A-Za-z0-9._-]+$/.test(name) || name === "Problem") {
4561
- invalid7(`The record class "${name}" cannot be a component name; give the class a name that is not "Problem".`);
4917
+ invalid8(`The record class "${name}" cannot be a component name; give the class a name that is not "Problem".`);
4562
4918
  }
4563
4919
  if (classes.has(name)) {
4564
- invalid7(`Two record classes are named "${name}"; rename one.`);
4920
+ invalid8(`Two record classes are named "${name}"; rename one.`);
4565
4921
  }
4566
4922
  classes.set(name, record);
4567
4923
  models[name] = record;
@@ -4581,7 +4937,7 @@ var openapi2 = sinkOf2({
4581
4937
  const { method, path } = routes.get(operation.id);
4582
4938
  const operationId = operation.id.replaceAll(".", "_");
4583
4939
  if (operationIds.has(operationId)) {
4584
- invalid7(`The operationId "${operationId}" is produced twice.`);
4940
+ invalid8(`The operationId "${operationId}" is produced twice.`);
4585
4941
  }
4586
4942
  operationIds.add(operationId);
4587
4943
  const slot = paths[path] ?? {};
@@ -5332,4 +5688,4 @@ var entityOf2 = entityBuilderOf2({
5332
5688
  var recordOf2 = recordBuilderOf2(StdRecordPlugins2);
5333
5689
  // src/std/stages.ts
5334
5690
  var StdStages2 = stageCatalogOf2(select2, openapi2);
5335
- export { InvalidEnvironment2, environmentOf2, isEnvironment2, guard2, InvalidApplicationDeclaration2, DuplicateApplicationDeclaration2, ApplicationConflict2, UnregisteredApplicationReference2, UnregisteredRepositoryDependency2, UnregisteredUseCaseDependency2, ApplicationNotStarted2, 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, 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 };
5691
+ 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, InvalidRepositoryCursor2, 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 };