@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.
- package/README.md +2 -0
- package/dist/core/application/exceptions.d.ts +9 -0
- package/dist/core/application/providers.d.ts +46 -0
- package/dist/core/application/types/application.interface.d.ts +70 -15
- package/dist/core/blueprint/types/slice.interface.d.ts +5 -2
- package/dist/core/entity/abstract-entity.d.ts +1 -1
- package/dist/core/entity/types/entity.interface.d.ts +13 -7
- package/dist/core/exception/exception.d.ts +10 -1
- package/dist/core/output/status.d.ts +2 -2
- package/dist/core/property/property.d.ts +4 -4
- package/dist/core/property/types/optional-property.type.d.ts +2 -2
- package/dist/core/property/types/property-type.interface.d.ts +17 -0
- package/dist/core/record/abstract-record.d.ts +4 -4
- package/dist/core/record/exceptions.d.ts +2 -0
- package/dist/core/record/name.d.ts +16 -0
- package/dist/core/record/record.d.ts +4 -3
- package/dist/core/record/types/plugin.interface.d.ts +11 -0
- package/dist/core/record/types/record.interface.d.ts +71 -10
- package/dist/core/repository/cursor.d.ts +13 -0
- package/dist/core/repository/exceptions.d.ts +10 -0
- package/dist/core/repository/query.d.ts +4 -4
- package/dist/core/repository/repository.d.ts +1 -1
- package/dist/core/repository/state.d.ts +3 -2
- package/dist/core/repository/types/query.interface.d.ts +37 -0
- package/dist/core/repository/types/repository.interface.d.ts +10 -6
- package/dist/core/use-case/steps/types/steps.interface.d.ts +1 -1
- package/dist/experimental.js +3 -2
- package/dist/{index-hn82hm31.js → index-3nd9bjnc.js} +69 -10
- package/dist/{index-ewm6r4nw.js → index-gbgc40xe.js} +452 -96
- package/dist/index.d.ts +9 -7
- package/dist/index.js +16 -2
- package/dist/std/properties/password.d.ts +5 -1
- package/dist/std/record-plugins.d.ts +5 -5
- package/dist/testing/index.js +91 -2
- package/dist/testing/provider-contract.d.ts +5 -0
- 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-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
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
|
|
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
|
|
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
|
|
1310
|
+
return invalid(`Missing relation: no repository of "${recordNameOf2(target)}".`);
|
|
1202
1311
|
}
|
|
1203
1312
|
if (!exact && matches.length > 1) {
|
|
1204
|
-
return invalid(`Relation "${target
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ? {} : {
|
|
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
|
-
|
|
4241
|
+
invalid7("applicationOf", "Expected a name.");
|
|
3974
4242
|
}
|
|
3975
4243
|
const extended = settings.extends ?? [];
|
|
3976
4244
|
if (!Array.isArray(extended) || !extended.every(isApplication2)) {
|
|
3977
|
-
|
|
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
|
-
|
|
3985
|
-
|
|
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
|
|
3989
|
-
{
|
|
3990
|
-
const
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
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
|
|
3998
|
-
reference:
|
|
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
|
|
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 =
|
|
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 (
|
|
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
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4211
|
-
|
|
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
|
-
|
|
4230
|
-
await stopProvider(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 && !
|
|
4367
|
-
return `Expected ${where}.route["${id}"].method to be one of ${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4915
|
+
const name = recordNameOf2(record);
|
|
4560
4916
|
if (!/^[A-Za-z0-9._-]+$/.test(name) || name === "Problem") {
|
|
4561
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|