@afora/core 0.0.6 → 0.0.7
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 -1
- package/dist/core/application/types/application.interface.d.ts +11 -0
- package/dist/core/command/command.d.ts +11 -4
- package/dist/core/command/types/command.interface.d.ts +19 -0
- package/dist/core/output/status.d.ts +2 -1
- package/dist/core/property/input.d.ts +20 -0
- package/dist/core/record/plugin.d.ts +6 -2
- package/dist/core/repository/repository.d.ts +1 -1
- package/dist/core/repository/state.d.ts +9 -0
- package/dist/core/repository/types/repository.interface.d.ts +9 -2
- package/dist/core/use-case/exceptions.d.ts +36 -0
- package/dist/core/use-case/plugin-catalog.d.ts +11 -0
- package/dist/core/use-case/plugin.d.ts +25 -0
- package/dist/core/use-case/steps/types/steps.interface.d.ts +23 -6
- package/dist/core/use-case/target.d.ts +9 -0
- package/dist/core/use-case/types/plugin.interface.d.ts +185 -0
- package/dist/experimental.js +2 -2
- package/dist/{index-gbgc40xe.js → index-q6hggpzw.js} +1024 -48
- package/dist/{index-3nd9bjnc.js → index-r78dsvtc.js} +68 -57
- package/dist/index.d.ts +15 -6
- package/dist/index.js +41 -5
- package/dist/std/command.d.ts +11 -0
- package/dist/std/properties/password.d.ts +16 -1
- package/dist/std/use-cases/auth.d.ts +5 -0
- package/dist/std/use-cases/crud.d.ts +23 -0
- package/dist/std/use-cases/index.d.ts +11 -0
- package/dist/std/use-cases/lifecycle.d.ts +13 -0
- package/dist/std/use-cases/shared.d.ts +60 -0
- package/dist/std/use-cases/types.d.ts +212 -0
- package/dist/testing/index.js +10 -2
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
recordFieldOf2,
|
|
12
12
|
recordNameOf2,
|
|
13
13
|
declaredRecordNameOf,
|
|
14
|
+
listOf2,
|
|
14
15
|
isBlueprintList2,
|
|
15
16
|
UNEXPECTED_ERROR2,
|
|
16
17
|
Exception2,
|
|
@@ -29,20 +30,22 @@ import {
|
|
|
29
30
|
AbstractRecord2,
|
|
30
31
|
IdentifiedRecord,
|
|
31
32
|
isVersion,
|
|
32
|
-
|
|
33
|
+
satisfies,
|
|
33
34
|
pluginsOf2,
|
|
34
35
|
isRecordPlugin2,
|
|
36
|
+
hasPlugin2,
|
|
35
37
|
recordPluginOf2,
|
|
36
38
|
recordBuilderOf2,
|
|
37
39
|
pluginContextOf,
|
|
38
40
|
recordOf3,
|
|
39
41
|
declareRecord,
|
|
42
|
+
optionalOf2,
|
|
40
43
|
exports_exceptions2,
|
|
41
44
|
InvalidPropertyValue2,
|
|
42
45
|
exports_exceptions3,
|
|
43
46
|
propertyOf2,
|
|
44
47
|
blueprintBuilderOf2
|
|
45
|
-
} from "./index-
|
|
48
|
+
} from "./index-r78dsvtc.js";
|
|
46
49
|
|
|
47
50
|
// src/core/environment/environment.ts
|
|
48
51
|
var exports_environment = {};
|
|
@@ -94,6 +97,19 @@ __export(exports_exceptions7, {
|
|
|
94
97
|
RepositoryUniqueViolation: () => RepositoryUniqueViolation2
|
|
95
98
|
});
|
|
96
99
|
|
|
100
|
+
// src/core/use-case/exceptions.ts
|
|
101
|
+
var exports_exceptions8 = {};
|
|
102
|
+
__export(exports_exceptions8, {
|
|
103
|
+
IncompatibleUseCasePlugin: () => IncompatibleUseCasePlugin2,
|
|
104
|
+
InvalidUseCasePlugin: () => InvalidUseCasePlugin2,
|
|
105
|
+
InvalidUseCaseStep: () => InvalidUseCaseStep2,
|
|
106
|
+
UnknownUseCasePlugin: () => UnknownUseCasePlugin2,
|
|
107
|
+
UseCaseEntityConflict: () => UseCaseEntityConflict2,
|
|
108
|
+
UseCaseEntityNotFound: () => UseCaseEntityNotFound2,
|
|
109
|
+
UseCaseInvalidCredentials: () => UseCaseInvalidCredentials2,
|
|
110
|
+
UseCaseUnavailable: () => UseCaseUnavailable2
|
|
111
|
+
});
|
|
112
|
+
|
|
97
113
|
// src/core/use-case/use-case.ts
|
|
98
114
|
var exports_use_case = {};
|
|
99
115
|
__export(exports_use_case, {
|
|
@@ -106,12 +122,6 @@ __export(exports_use_case, {
|
|
|
106
122
|
useCaseOf: () => useCaseOf2
|
|
107
123
|
});
|
|
108
124
|
|
|
109
|
-
// src/core/use-case/exceptions.ts
|
|
110
|
-
var exports_exceptions8 = {};
|
|
111
|
-
__export(exports_exceptions8, {
|
|
112
|
-
InvalidUseCaseStep: () => InvalidUseCaseStep2
|
|
113
|
-
});
|
|
114
|
-
|
|
115
125
|
// src/core/command/exceptions.ts
|
|
116
126
|
var exports_exceptions9 = {};
|
|
117
127
|
__export(exports_exceptions9, {
|
|
@@ -1192,6 +1202,14 @@ var origins = new WeakMap;
|
|
|
1192
1202
|
function declarationOf(state) {
|
|
1193
1203
|
return origins.get(state) ?? state;
|
|
1194
1204
|
}
|
|
1205
|
+
var declared = new WeakMap;
|
|
1206
|
+
function declareRepository(state, repository) {
|
|
1207
|
+
declared.set(state, repository);
|
|
1208
|
+
}
|
|
1209
|
+
function relatedRepositoryOf(repository, Entity) {
|
|
1210
|
+
const related = findState(repository)?.relations.get(Entity);
|
|
1211
|
+
return related ? declared.get(declarationOf(related)) : undefined;
|
|
1212
|
+
}
|
|
1195
1213
|
function bindState(state, provider, instances, bound = new Map) {
|
|
1196
1214
|
const done = bound.get(state);
|
|
1197
1215
|
if (done)
|
|
@@ -1541,6 +1559,29 @@ var remove = action(async (self, target) => {
|
|
|
1541
1559
|
}
|
|
1542
1560
|
return entity;
|
|
1543
1561
|
});
|
|
1562
|
+
var restore = action(async (self, target) => {
|
|
1563
|
+
const { context } = self;
|
|
1564
|
+
const id = typeof target === "string" ? target : self.dataOf(target).id;
|
|
1565
|
+
const data = await self.provider.findOne(context, {
|
|
1566
|
+
...equals(self, "id", id),
|
|
1567
|
+
withDeleted: true
|
|
1568
|
+
});
|
|
1569
|
+
if (data === undefined) {
|
|
1570
|
+
throw new RepositoryEntityNotFound2({
|
|
1571
|
+
source: context.name,
|
|
1572
|
+
details: { id }
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
if (data.deletedAt === undefined)
|
|
1576
|
+
return self.hydrate(data);
|
|
1577
|
+
const { deletedAt: _deletedAt, ...alive } = data;
|
|
1578
|
+
const restored = {
|
|
1579
|
+
...alive,
|
|
1580
|
+
updatedAt: context.entity[EntityClock].now()
|
|
1581
|
+
};
|
|
1582
|
+
await self.provider.update(context, restored);
|
|
1583
|
+
return self.hydrate(restored);
|
|
1584
|
+
});
|
|
1544
1585
|
var count = action(async (self, query) => self.provider.count(self.context, queryOf(self, query)));
|
|
1545
1586
|
var NEEDS = {
|
|
1546
1587
|
findBy: ["findOne"],
|
|
@@ -1548,6 +1589,7 @@ var NEEDS = {
|
|
|
1548
1589
|
create: ["insert"],
|
|
1549
1590
|
update: ["update"],
|
|
1550
1591
|
delete: ["findOne", "remove"],
|
|
1592
|
+
restore: ["findOne", "update"],
|
|
1551
1593
|
count: ["count"],
|
|
1552
1594
|
findMany: ["findMany"],
|
|
1553
1595
|
exists: ["findOne"],
|
|
@@ -1604,6 +1646,7 @@ function repositoryOf2(settings) {
|
|
|
1604
1646
|
methods.set("create", ["create", create]);
|
|
1605
1647
|
methods.set("update", ["update", update]);
|
|
1606
1648
|
methods.set("delete", ["delete", remove]);
|
|
1649
|
+
methods.set("restore", ["restore", restore]);
|
|
1607
1650
|
methods.set("count", ["count", count]);
|
|
1608
1651
|
methods.set("findMany", ["findMany", findMany]);
|
|
1609
1652
|
methods.set("exists", ["exists", exists]);
|
|
@@ -1617,6 +1660,9 @@ function repositoryOf2(settings) {
|
|
|
1617
1660
|
if (!method)
|
|
1618
1661
|
invalid3(name, unknownAction(recordNameOf2(entity), String(name_), methods));
|
|
1619
1662
|
const [kind, implementation] = method;
|
|
1663
|
+
if (kind === "restore" && deletion !== "soft") {
|
|
1664
|
+
invalid3(name, '"restore" needs deletion to be "soft".');
|
|
1665
|
+
}
|
|
1620
1666
|
const needs = deletion === "soft" && kind === "delete" ? ["findOne", "update"] : NEEDS[kind] ?? [];
|
|
1621
1667
|
for (const need of needs) {
|
|
1622
1668
|
if (typeof provider?.[need] !== "function") {
|
|
@@ -1630,7 +1676,7 @@ function repositoryOf2(settings) {
|
|
|
1630
1676
|
});
|
|
1631
1677
|
}
|
|
1632
1678
|
Object.defineProperty(DeclaredRepository, "name", { value: name });
|
|
1633
|
-
|
|
1679
|
+
const state = {
|
|
1634
1680
|
name,
|
|
1635
1681
|
entity,
|
|
1636
1682
|
actions: Object.freeze([...new Set(actions)]),
|
|
@@ -1639,8 +1685,11 @@ function repositoryOf2(settings) {
|
|
|
1639
1685
|
explicitProvider: settings.provider !== undefined,
|
|
1640
1686
|
environment: environmentDependencies,
|
|
1641
1687
|
relations
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1688
|
+
};
|
|
1689
|
+
states.set(DeclaredRepository, state);
|
|
1690
|
+
const repository = Object.freeze(new DeclaredRepository);
|
|
1691
|
+
declareRepository(state, repository);
|
|
1692
|
+
return repository;
|
|
1644
1693
|
}
|
|
1645
1694
|
function RepositoryOf2(repository) {
|
|
1646
1695
|
if (!isRepository2(repository)) {
|
|
@@ -1652,6 +1701,54 @@ function isRepository2(value) {
|
|
|
1652
1701
|
return value instanceof AbstractRepository2;
|
|
1653
1702
|
}
|
|
1654
1703
|
|
|
1704
|
+
// src/core/use-case/exceptions.ts
|
|
1705
|
+
var InvalidUseCaseStep2 = exceptionOf2({
|
|
1706
|
+
layer: "use-case" /* UseCase */,
|
|
1707
|
+
name: "InvalidUseCaseStep",
|
|
1708
|
+
message: ({
|
|
1709
|
+
index,
|
|
1710
|
+
reason
|
|
1711
|
+
}) => `Invalid step ${index}: ${reason}`
|
|
1712
|
+
});
|
|
1713
|
+
var InvalidUseCasePlugin2 = exceptionOf2({
|
|
1714
|
+
layer: "use-case" /* UseCase */,
|
|
1715
|
+
name: "InvalidUseCasePlugin",
|
|
1716
|
+
message: ({ reason }, source) => `Invalid use case plugin "${source}": ${reason}.`
|
|
1717
|
+
});
|
|
1718
|
+
var UnknownUseCasePlugin2 = exceptionOf2({
|
|
1719
|
+
layer: "use-case" /* UseCase */,
|
|
1720
|
+
name: "UnknownUseCasePlugin",
|
|
1721
|
+
message: ({ name }) => `No use case plugin "${name}" in the catalog.`
|
|
1722
|
+
});
|
|
1723
|
+
var IncompatibleUseCasePlugin2 = exceptionOf2({
|
|
1724
|
+
layer: "use-case" /* UseCase */,
|
|
1725
|
+
name: "IncompatibleUseCasePlugin",
|
|
1726
|
+
message: ({ reason }, source) => `Incompatible use case plugin "${source}": ${reason}.`
|
|
1727
|
+
});
|
|
1728
|
+
var UseCaseUnavailable2 = exceptionOf2({
|
|
1729
|
+
layer: "use-case" /* UseCase */,
|
|
1730
|
+
name: "UseCaseUnavailable",
|
|
1731
|
+
message: ({
|
|
1732
|
+
repository,
|
|
1733
|
+
reason
|
|
1734
|
+
}, source) => `The use case "${source}" is not available for "${repository}": ${reason}.`
|
|
1735
|
+
});
|
|
1736
|
+
var UseCaseEntityNotFound2 = exceptionOf2({
|
|
1737
|
+
layer: "use-case" /* UseCase */,
|
|
1738
|
+
name: "UseCaseEntityNotFound",
|
|
1739
|
+
message: "The entity was not found."
|
|
1740
|
+
});
|
|
1741
|
+
var UseCaseEntityConflict2 = exceptionOf2({
|
|
1742
|
+
layer: "use-case" /* UseCase */,
|
|
1743
|
+
name: "UseCaseEntityConflict",
|
|
1744
|
+
message: "The entity already exists."
|
|
1745
|
+
});
|
|
1746
|
+
var UseCaseInvalidCredentials2 = exceptionOf2({
|
|
1747
|
+
layer: "use-case" /* UseCase */,
|
|
1748
|
+
name: "UseCaseInvalidCredentials",
|
|
1749
|
+
message: "Invalid credentials."
|
|
1750
|
+
});
|
|
1751
|
+
|
|
1655
1752
|
// src/core/use-case/steps/references.ts
|
|
1656
1753
|
var RESERVED_NAMES = [
|
|
1657
1754
|
"input",
|
|
@@ -1759,15 +1856,104 @@ function freezeData(value) {
|
|
|
1759
1856
|
return value;
|
|
1760
1857
|
}
|
|
1761
1858
|
|
|
1762
|
-
// src/core/
|
|
1763
|
-
var
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
}
|
|
1859
|
+
// src/core/property/input.ts
|
|
1860
|
+
var PropertyInput2 = Symbol("propertyInput");
|
|
1861
|
+
function propertyInputOf2(field) {
|
|
1862
|
+
const property = fieldOf(field);
|
|
1863
|
+
if (!isProperty2(property))
|
|
1864
|
+
return;
|
|
1865
|
+
const declared = property[PropertyInput2];
|
|
1866
|
+
return typeof declared === "object" && declared !== null && isProperty2(declared.accepts) && typeof declared.convert === "function" ? declared : undefined;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// src/core/use-case/target.ts
|
|
1870
|
+
var IDENTITY = ENTITY_IDENTITY_KEYS2;
|
|
1871
|
+
function isOptionalField(field) {
|
|
1872
|
+
if (isProperty2(field))
|
|
1873
|
+
return field.type.optional === true;
|
|
1874
|
+
const record = recordFieldOf2(field);
|
|
1875
|
+
if (record)
|
|
1876
|
+
return record.optional;
|
|
1877
|
+
return field.optional === true;
|
|
1878
|
+
}
|
|
1879
|
+
function planOf(entity, key, owners) {
|
|
1880
|
+
const field = fieldOf(entity.blueprint.properties[key]);
|
|
1881
|
+
const optional = isOptionalField(field);
|
|
1882
|
+
const record = recordFieldOf2(field);
|
|
1883
|
+
const item = isBlueprintList2(field) ? recordFieldOf2(field.item) : undefined;
|
|
1884
|
+
const related = [record?.record, item?.record].find(isEntityClass2);
|
|
1885
|
+
const kind = record ? related ? "entity" : "record" : isBlueprintList2(field) ? related ? "entity-list" : "list" : isBlueprint2(field) ? "blueprint" : "property";
|
|
1886
|
+
let input = { key, field };
|
|
1887
|
+
if (kind === "entity" && related) {
|
|
1888
|
+
const id = related.blueprint.properties.id;
|
|
1889
|
+
input = { key: `${key}Id`, field: optional ? optionalOf2(id) : id };
|
|
1890
|
+
}
|
|
1891
|
+
const declared = kind === "property" ? propertyInputOf2(field) : undefined;
|
|
1892
|
+
if (declared) {
|
|
1893
|
+
input = {
|
|
1894
|
+
key,
|
|
1895
|
+
field: optional ? optionalOf2(declared.accepts) : declared.accepts
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
return Object.freeze({
|
|
1899
|
+
key,
|
|
1900
|
+
field,
|
|
1901
|
+
identity: IDENTITY.includes(key),
|
|
1902
|
+
plugin: owners.get(key),
|
|
1903
|
+
sensitive: entity.options.sensitive.includes(key),
|
|
1904
|
+
unique: entity.options.unique.includes(key),
|
|
1905
|
+
optional,
|
|
1906
|
+
change: entity.options.change.includes(key),
|
|
1907
|
+
kind,
|
|
1908
|
+
related,
|
|
1909
|
+
input: Object.freeze(input),
|
|
1910
|
+
convert: declared ? (value) => value === undefined ? undefined : declared.convert(value) : undefined
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
function useCaseTargetOf2(repository, source = "useCaseTargetOf") {
|
|
1914
|
+
const state = findState(repository);
|
|
1915
|
+
if (!state)
|
|
1916
|
+
throw new TypeError("Expected a repository made by repositoryOf");
|
|
1917
|
+
const { entity } = state;
|
|
1918
|
+
const unavailable = (reason) => {
|
|
1919
|
+
throw new UseCaseUnavailable2({
|
|
1920
|
+
source,
|
|
1921
|
+
details: { repository: state.name, reason }
|
|
1922
|
+
});
|
|
1923
|
+
};
|
|
1924
|
+
const owners = new Map;
|
|
1925
|
+
for (const plugin of pluginsOf2(entity)) {
|
|
1926
|
+
for (const key of plugin.keys)
|
|
1927
|
+
owners.set(key, plugin.name);
|
|
1928
|
+
}
|
|
1929
|
+
const fields = Object.freeze(Object.keys(entity.blueprint.properties).map((key) => planOf(entity, key, owners)));
|
|
1930
|
+
const field = (key) => fields.find((plan) => plan.key === key) ?? unavailable(`the entity has no key "${key}"`);
|
|
1931
|
+
return Object.freeze({
|
|
1932
|
+
repository,
|
|
1933
|
+
repositoryName: state.name,
|
|
1934
|
+
entity,
|
|
1935
|
+
entityName: recordNameOf2(entity),
|
|
1936
|
+
actions: state.actions,
|
|
1937
|
+
deletion: state.deletion,
|
|
1938
|
+
fields,
|
|
1939
|
+
changeKeys: entity.options.change,
|
|
1940
|
+
uniqueKeys: entity.options.unique,
|
|
1941
|
+
field,
|
|
1942
|
+
relation(key, actions = []) {
|
|
1943
|
+
const { related } = field(key);
|
|
1944
|
+
const found = related ? relatedRepositoryOf(repository, related) : undefined;
|
|
1945
|
+
if (!found) {
|
|
1946
|
+
return unavailable(`the repository has no relation for the entity of "${key}"`);
|
|
1947
|
+
}
|
|
1948
|
+
const missing = actions.filter((action) => !found.actions.includes(action));
|
|
1949
|
+
if (missing.length > 0) {
|
|
1950
|
+
unavailable(`the repository of "${key}" lacks the action ${missing.map((action) => `"${action}"`).join(", ")}`);
|
|
1951
|
+
}
|
|
1952
|
+
return found;
|
|
1953
|
+
},
|
|
1954
|
+
unavailable
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1771
1957
|
|
|
1772
1958
|
// src/core/use-case/steps/run.ts
|
|
1773
1959
|
var FORBIDDEN_METHODS = new Set([
|
|
@@ -1801,7 +1987,6 @@ async function executeSteps(dependencies = {}) {
|
|
|
1801
1987
|
return followPath(named.get(root), path);
|
|
1802
1988
|
};
|
|
1803
1989
|
const value = (raw) => resolveValue(raw, lookup);
|
|
1804
|
-
const list = (raw) => raw === undefined ? [] : value(raw);
|
|
1805
1990
|
for (const [index, step] of steps.entries()) {
|
|
1806
1991
|
const invalid = (reason) => {
|
|
1807
1992
|
throw new InvalidUseCaseStep2({
|
|
@@ -1809,6 +1994,14 @@ async function executeSteps(dependencies = {}) {
|
|
|
1809
1994
|
details: { index, reason, code: "invalid_target" }
|
|
1810
1995
|
});
|
|
1811
1996
|
};
|
|
1997
|
+
const list = (raw) => {
|
|
1998
|
+
if (raw === undefined)
|
|
1999
|
+
return [];
|
|
2000
|
+
const resolved = value(raw);
|
|
2001
|
+
if (!Array.isArray(resolved))
|
|
2002
|
+
invalid("args must resolve to a list.");
|
|
2003
|
+
return resolved;
|
|
2004
|
+
};
|
|
1812
2005
|
const bind = (result) => {
|
|
1813
2006
|
if (typeof step.as === "string")
|
|
1814
2007
|
named.set(step.as, result);
|
|
@@ -1853,7 +2046,10 @@ async function executeSteps(dependencies = {}) {
|
|
|
1853
2046
|
if (typeof namespace !== "object" || namespace === null || typeof namespace[key] !== "function") {
|
|
1854
2047
|
invalid(`"${key}" is not a change key of the record.`);
|
|
1855
2048
|
}
|
|
1856
|
-
|
|
2049
|
+
const changed = value(step.value);
|
|
2050
|
+
if (step.ifPresent !== true || changed !== undefined) {
|
|
2051
|
+
await namespace[key]?.(changed);
|
|
2052
|
+
}
|
|
1857
2053
|
bind(record);
|
|
1858
2054
|
} else if (Object.hasOwn(step, "invoke")) {
|
|
1859
2055
|
const record = target("invoke");
|
|
@@ -1869,7 +2065,7 @@ async function executeSteps(dependencies = {}) {
|
|
|
1869
2065
|
const fn = step.fn;
|
|
1870
2066
|
bind(await fn(...list(step.args), { context: this.context }));
|
|
1871
2067
|
} else {
|
|
1872
|
-
const action = ["create", "update", "delete"].find((kind) => Object.hasOwn(step, kind));
|
|
2068
|
+
const action = ["create", "update", "delete", "restore"].find((kind) => Object.hasOwn(step, kind));
|
|
1873
2069
|
const methods = dependencies[step[action]];
|
|
1874
2070
|
bind(await methods[action].call(methods, value(step.value)));
|
|
1875
2071
|
}
|
|
@@ -1889,11 +2085,15 @@ var SHAPES = {
|
|
|
1889
2085
|
optional: ["other", "details"]
|
|
1890
2086
|
},
|
|
1891
2087
|
build: { required: ["build", "values", "as"], optional: [] },
|
|
1892
|
-
change: {
|
|
2088
|
+
change: {
|
|
2089
|
+
required: ["change", "key", "value"],
|
|
2090
|
+
optional: ["ifPresent", "as"]
|
|
2091
|
+
},
|
|
1893
2092
|
invoke: { required: ["invoke", "method"], optional: ["args", "as"] },
|
|
1894
2093
|
create: { required: ["create", "value"], optional: ["as"] },
|
|
1895
2094
|
update: { required: ["update", "value"], optional: ["as"] },
|
|
1896
2095
|
delete: { required: ["delete", "value"], optional: ["as"] },
|
|
2096
|
+
restore: { required: ["restore", "value"], optional: ["as"] },
|
|
1897
2097
|
call: { required: ["call", "input"], optional: ["as"] },
|
|
1898
2098
|
run: { required: ["run", "fn"], optional: ["args", "as"] },
|
|
1899
2099
|
return: { required: ["return"], optional: [] }
|
|
@@ -1908,7 +2108,7 @@ var ENSURE_KINDS = new Set([
|
|
|
1908
2108
|
"false"
|
|
1909
2109
|
]);
|
|
1910
2110
|
var READ_ACTION = /^(?:findBy.+|findManyBy.+|findMany|exists|findPage|findCursor|count)$/;
|
|
1911
|
-
var WRITE_ACTIONS = new Set(["create", "update", "delete"]);
|
|
2111
|
+
var WRITE_ACTIONS = new Set(["create", "update", "delete", "restore"]);
|
|
1912
2112
|
function validateSteps(declaration, steps) {
|
|
1913
2113
|
const { name } = declaration;
|
|
1914
2114
|
const fail = (index, code, reason) => {
|
|
@@ -1964,8 +2164,12 @@ function validateSteps(declaration, steps) {
|
|
|
1964
2164
|
const checkArgs = (index, args) => {
|
|
1965
2165
|
if (args === undefined)
|
|
1966
2166
|
return;
|
|
2167
|
+
if (typeof args === "string" && args.startsWith("$")) {
|
|
2168
|
+
checkReference(index, args);
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
1967
2171
|
if (!Array.isArray(args)) {
|
|
1968
|
-
fail(index, "invalid_step", "args must be a list of values.");
|
|
2172
|
+
fail(index, "invalid_step", "args must be a list of values or a reference to one.");
|
|
1969
2173
|
}
|
|
1970
2174
|
checkValue(index, args, "args");
|
|
1971
2175
|
};
|
|
@@ -2072,6 +2276,9 @@ function validateSteps(declaration, steps) {
|
|
|
2072
2276
|
if (typeof record.key !== "string" || record.key === "") {
|
|
2073
2277
|
fail(index, "invalid_step", "change.key must be a string.");
|
|
2074
2278
|
}
|
|
2279
|
+
if (record.ifPresent !== undefined && typeof record.ifPresent !== "boolean") {
|
|
2280
|
+
fail(index, "invalid_step", "change.ifPresent must be a boolean.");
|
|
2281
|
+
}
|
|
2075
2282
|
const reference = parseReference(record.change);
|
|
2076
2283
|
const known = reference && reference.path.length === 0 ? bound.get(reference.root)?.record : undefined;
|
|
2077
2284
|
if (known) {
|
|
@@ -2094,7 +2301,8 @@ function validateSteps(declaration, steps) {
|
|
|
2094
2301
|
}
|
|
2095
2302
|
case "create":
|
|
2096
2303
|
case "update":
|
|
2097
|
-
case "delete":
|
|
2304
|
+
case "delete":
|
|
2305
|
+
case "restore": {
|
|
2098
2306
|
repositoryOf(index, record[kind], kind);
|
|
2099
2307
|
checkValue(index, record.value, "value");
|
|
2100
2308
|
break;
|
|
@@ -2513,6 +2721,195 @@ function isUseCase2(value) {
|
|
|
2513
2721
|
return value instanceof AbstractUseCase2;
|
|
2514
2722
|
}
|
|
2515
2723
|
|
|
2724
|
+
// src/core/use-case/plugin.ts
|
|
2725
|
+
var UseCaseOrigin = Symbol("useCaseOrigin");
|
|
2726
|
+
var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2727
|
+
var identities = new WeakMap;
|
|
2728
|
+
var nextIdentity = 0;
|
|
2729
|
+
function keyOf(value) {
|
|
2730
|
+
if (typeof value === "function" || typeof value === "object" && value) {
|
|
2731
|
+
if (Array.isArray(value))
|
|
2732
|
+
return `[${value.map(keyOf).join(",")}]`;
|
|
2733
|
+
if (isPlain2(value)) {
|
|
2734
|
+
return `{${Object.keys(value).sort().filter((key) => value[key] !== undefined).map((key) => `${JSON.stringify(key)}:${keyOf(value[key])}`).join(",")}}`;
|
|
2735
|
+
}
|
|
2736
|
+
let identity = identities.get(value);
|
|
2737
|
+
if (identity === undefined) {
|
|
2738
|
+
identity = nextIdentity++;
|
|
2739
|
+
identities.set(value, identity);
|
|
2740
|
+
}
|
|
2741
|
+
return `#${identity}`;
|
|
2742
|
+
}
|
|
2743
|
+
return value === undefined ? "undefined" : JSON.stringify(value);
|
|
2744
|
+
}
|
|
2745
|
+
function isUseCasePlugin2(value) {
|
|
2746
|
+
return typeof value === "object" && value !== null && value[Kind2] === "use-case-plugin";
|
|
2747
|
+
}
|
|
2748
|
+
function useCaseOriginOf2(UseCase) {
|
|
2749
|
+
return typeof UseCase === "function" ? UseCase[UseCaseOrigin] : undefined;
|
|
2750
|
+
}
|
|
2751
|
+
function useCasePluginOf2(name, settings) {
|
|
2752
|
+
const source = typeof name === "string" ? name : "useCasePluginOf";
|
|
2753
|
+
const invalid = (reason) => {
|
|
2754
|
+
throw new InvalidUseCasePlugin2({ source, details: { reason } });
|
|
2755
|
+
};
|
|
2756
|
+
if (typeof name !== "string" || !NAME.test(name)) {
|
|
2757
|
+
invalid("its name must be an identifier");
|
|
2758
|
+
}
|
|
2759
|
+
if (!isPlain2(settings) || typeof settings.compile !== "function") {
|
|
2760
|
+
invalid("it needs a compile function");
|
|
2761
|
+
}
|
|
2762
|
+
const { version = "1.0.0", description, compile } = settings;
|
|
2763
|
+
if (!isVersion(version)) {
|
|
2764
|
+
invalid(`"${String(version)}" is not a semantic version`);
|
|
2765
|
+
}
|
|
2766
|
+
if (description !== undefined && typeof description !== "string") {
|
|
2767
|
+
invalid("its description must be a string");
|
|
2768
|
+
}
|
|
2769
|
+
const names = (list, what) => {
|
|
2770
|
+
if (list === undefined)
|
|
2771
|
+
return Object.freeze([]);
|
|
2772
|
+
if (!Array.isArray(list) || list.some((item) => typeof item !== "string" || item === "")) {
|
|
2773
|
+
invalid(`requires.${what} must be a list of names`);
|
|
2774
|
+
}
|
|
2775
|
+
return Object.freeze([...new Set(list)]);
|
|
2776
|
+
};
|
|
2777
|
+
if (settings.requires !== undefined && !isPlain2(settings.requires)) {
|
|
2778
|
+
invalid("requires must be an object of actions and plugins");
|
|
2779
|
+
}
|
|
2780
|
+
const requires = Object.freeze({
|
|
2781
|
+
actions: names(settings.requires?.actions, "actions"),
|
|
2782
|
+
plugins: names(settings.requires?.plugins, "plugins")
|
|
2783
|
+
});
|
|
2784
|
+
const made = new WeakMap;
|
|
2785
|
+
const make = (repository, given) => {
|
|
2786
|
+
if (!isRepository2(repository)) {
|
|
2787
|
+
return invalid("it makes a use case for a repository of repositoryOf");
|
|
2788
|
+
}
|
|
2789
|
+
if (given !== undefined && !isPlain2(given)) {
|
|
2790
|
+
return invalid("its options must be a plain object");
|
|
2791
|
+
}
|
|
2792
|
+
const options = Object.freeze({ ...given });
|
|
2793
|
+
const info = inspectRepository(repository);
|
|
2794
|
+
let classes = made.get(info.state);
|
|
2795
|
+
if (!classes) {
|
|
2796
|
+
classes = new Map;
|
|
2797
|
+
made.set(info.state, classes);
|
|
2798
|
+
}
|
|
2799
|
+
const key = keyOf(options);
|
|
2800
|
+
const known = classes.get(key);
|
|
2801
|
+
if (known)
|
|
2802
|
+
return known;
|
|
2803
|
+
const target = useCaseTargetOf2(repository, name);
|
|
2804
|
+
const missing = {
|
|
2805
|
+
actions: requires.actions.filter((action) => !info.actions.includes(action)),
|
|
2806
|
+
plugins: requires.plugins.filter((plugin) => !hasPlugin2(info.entity, plugin))
|
|
2807
|
+
};
|
|
2808
|
+
if (missing.actions.length > 0 || missing.plugins.length > 0) {
|
|
2809
|
+
const quoted = (list) => list.map((item) => `"${item}"`).join(", ");
|
|
2810
|
+
throw new UseCaseUnavailable2({
|
|
2811
|
+
source: name,
|
|
2812
|
+
details: {
|
|
2813
|
+
repository: target.repositoryName,
|
|
2814
|
+
reason: [
|
|
2815
|
+
missing.actions.length > 0 ? `the repository lacks the action ${quoted(missing.actions)}` : undefined,
|
|
2816
|
+
missing.plugins.length > 0 ? `the entity lacks the plugin ${quoted(missing.plugins)}` : undefined
|
|
2817
|
+
].filter(Boolean).join(" and "),
|
|
2818
|
+
missing
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
const UseCase = useCaseOf2(compile(target, options));
|
|
2823
|
+
Object.defineProperty(UseCase, UseCaseOrigin, {
|
|
2824
|
+
value: Object.freeze({
|
|
2825
|
+
plugin,
|
|
2826
|
+
repository,
|
|
2827
|
+
options
|
|
2828
|
+
})
|
|
2829
|
+
});
|
|
2830
|
+
classes.set(key, UseCase);
|
|
2831
|
+
return UseCase;
|
|
2832
|
+
};
|
|
2833
|
+
const plugin = Object.freeze({
|
|
2834
|
+
[Kind2]: "use-case-plugin",
|
|
2835
|
+
name,
|
|
2836
|
+
version,
|
|
2837
|
+
...description === undefined ? {} : { description },
|
|
2838
|
+
requires,
|
|
2839
|
+
make,
|
|
2840
|
+
toJSON: () => ({
|
|
2841
|
+
kind: "use-case-plugin",
|
|
2842
|
+
name,
|
|
2843
|
+
version,
|
|
2844
|
+
...description === undefined ? {} : { description },
|
|
2845
|
+
requires
|
|
2846
|
+
})
|
|
2847
|
+
});
|
|
2848
|
+
return plugin;
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
// src/core/use-case/plugin-catalog.ts
|
|
2852
|
+
function isUseCaseCatalog2(value) {
|
|
2853
|
+
return typeof value === "object" && value !== null && value[Kind2] === "use-case-catalog";
|
|
2854
|
+
}
|
|
2855
|
+
function useCaseCatalogOf2(...plugins) {
|
|
2856
|
+
const byName = {};
|
|
2857
|
+
const context = {};
|
|
2858
|
+
for (const plugin of plugins) {
|
|
2859
|
+
if (!isUseCasePlugin2(plugin)) {
|
|
2860
|
+
throw new InvalidUseCasePlugin2({
|
|
2861
|
+
source: "catalog",
|
|
2862
|
+
details: {
|
|
2863
|
+
reason: "a catalog takes plugins made by `useCasePluginOf`"
|
|
2864
|
+
}
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
if (Object.hasOwn(byName, plugin.name)) {
|
|
2868
|
+
throw new InvalidUseCasePlugin2({
|
|
2869
|
+
source: plugin.name,
|
|
2870
|
+
details: { reason: "the catalog has another plugin with its name" }
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2873
|
+
byName[plugin.name] = plugin;
|
|
2874
|
+
context[plugin.name] = plugin.make;
|
|
2875
|
+
}
|
|
2876
|
+
const find = (name) => {
|
|
2877
|
+
const plugin = Object.hasOwn(byName, name) ? byName[name] : undefined;
|
|
2878
|
+
if (!plugin) {
|
|
2879
|
+
throw new UnknownUseCasePlugin2({ source: "catalog", details: { name } });
|
|
2880
|
+
}
|
|
2881
|
+
return plugin;
|
|
2882
|
+
};
|
|
2883
|
+
return Object.freeze({
|
|
2884
|
+
[Kind2]: "use-case-catalog",
|
|
2885
|
+
plugins: Object.freeze(byName),
|
|
2886
|
+
list: Object.freeze([...plugins]),
|
|
2887
|
+
context: Object.freeze(context),
|
|
2888
|
+
resolve(reference) {
|
|
2889
|
+
const at = typeof reference === "string" ? reference.lastIndexOf("@") : -1;
|
|
2890
|
+
if (at <= 0)
|
|
2891
|
+
return find(String(reference));
|
|
2892
|
+
const plugin = find(reference.slice(0, at));
|
|
2893
|
+
const version = reference.slice(at + 1);
|
|
2894
|
+
if (!isVersion(version)) {
|
|
2895
|
+
throw new IncompatibleUseCasePlugin2({
|
|
2896
|
+
source: plugin.name,
|
|
2897
|
+
details: { reason: `"${version}" is not a semantic version` }
|
|
2898
|
+
});
|
|
2899
|
+
}
|
|
2900
|
+
if (!satisfies(plugin.version, version)) {
|
|
2901
|
+
throw new IncompatibleUseCasePlugin2({
|
|
2902
|
+
source: plugin.name,
|
|
2903
|
+
details: {
|
|
2904
|
+
reason: `version ${plugin.version} does not satisfy ${version}`
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
}
|
|
2908
|
+
return plugin;
|
|
2909
|
+
}
|
|
2910
|
+
});
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2516
2913
|
// src/core/command/exceptions.ts
|
|
2517
2914
|
var InvalidCommandDeclaration2 = exceptionOf2({
|
|
2518
2915
|
layer: "command" /* Command */,
|
|
@@ -2667,9 +3064,12 @@ function callEnvironment(source, environments, given) {
|
|
|
2667
3064
|
}
|
|
2668
3065
|
return { byEnvironment, merged: Object.freeze(merged) };
|
|
2669
3066
|
}
|
|
2670
|
-
function
|
|
2671
|
-
const { name, description, context,
|
|
3067
|
+
function declareCommand(given, generics) {
|
|
3068
|
+
const { name, description, context, onEventError } = given;
|
|
2672
3069
|
const source = typeof name === "string" ? name : "commandOf";
|
|
3070
|
+
const picked = given.useCases;
|
|
3071
|
+
const useCases = typeof picked === "function" && !isUseCaseClass2(picked) ? guard2({ layer: "command" /* Command */, source }, () => picked(generics)) : picked;
|
|
3072
|
+
const settings = given;
|
|
2673
3073
|
const invalid = (reason) => {
|
|
2674
3074
|
throw new InvalidCommandDeclaration2({ source, details: { reason } });
|
|
2675
3075
|
};
|
|
@@ -2687,7 +3087,7 @@ function commandOf2(settings) {
|
|
|
2687
3087
|
invalid("Expected context to be a non-optional blueprint.");
|
|
2688
3088
|
}
|
|
2689
3089
|
if (typeof useCases !== "object" || useCases === null || Array.isArray(useCases) || Reflect.ownKeys(useCases).length === 0) {
|
|
2690
|
-
invalid("Expected useCases to be a non-empty object of use case classes.");
|
|
3090
|
+
invalid("Expected useCases to be a non-empty object of use case classes, or a function returning one.");
|
|
2691
3091
|
}
|
|
2692
3092
|
for (const key of Reflect.ownKeys(useCases)) {
|
|
2693
3093
|
if (typeof key !== "string" || !IDENTIFIER2.test(key) || !isUseCaseClass2(useCases[key])) {
|
|
@@ -2825,6 +3225,19 @@ function commandOf2(settings) {
|
|
|
2825
3225
|
verifiers.set(command, providerOfCall);
|
|
2826
3226
|
return command;
|
|
2827
3227
|
}
|
|
3228
|
+
function commandBuilderOf2(...catalogs) {
|
|
3229
|
+
if (!catalogs.every(isUseCaseCatalog2)) {
|
|
3230
|
+
throw new InvalidCommandDeclaration2({
|
|
3231
|
+
source: "commandBuilderOf",
|
|
3232
|
+
details: {
|
|
3233
|
+
reason: "Expected catalogs made by useCaseCatalogOf."
|
|
3234
|
+
}
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3237
|
+
const generics = Object.freeze(Object.assign({}, ...catalogs.map((catalog) => catalog.context)));
|
|
3238
|
+
return (settings) => declareCommand(settings, generics);
|
|
3239
|
+
}
|
|
3240
|
+
var commandOf = commandBuilderOf2();
|
|
2828
3241
|
async function applyPolicy(command, policy, onEventError, call) {
|
|
2829
3242
|
const { operation, raised } = call;
|
|
2830
3243
|
const source = `${command.commandName}.${operation}`;
|
|
@@ -3663,7 +4076,7 @@ function isOfKind(collection, value) {
|
|
|
3663
4076
|
return isDeclaredException(value);
|
|
3664
4077
|
}
|
|
3665
4078
|
}
|
|
3666
|
-
function
|
|
4079
|
+
function keyOf2(collection, value) {
|
|
3667
4080
|
switch (collection) {
|
|
3668
4081
|
case "properties":
|
|
3669
4082
|
return fieldOf(value);
|
|
@@ -3746,11 +4159,25 @@ function resolve(settings) {
|
|
|
3746
4159
|
collection,
|
|
3747
4160
|
name,
|
|
3748
4161
|
value,
|
|
3749
|
-
key:
|
|
4162
|
+
key: keyOf2(collection, value),
|
|
3750
4163
|
from: source
|
|
3751
4164
|
});
|
|
3752
4165
|
}
|
|
3753
4166
|
}
|
|
4167
|
+
for (const entry of [...registry.commands.values()]) {
|
|
4168
|
+
for (const UseCase of Object.values(entry.value.useCases)) {
|
|
4169
|
+
if (useCaseOriginOf2(UseCase) === undefined || byKey.has(UseCase)) {
|
|
4170
|
+
continue;
|
|
4171
|
+
}
|
|
4172
|
+
add({
|
|
4173
|
+
collection: "useCases",
|
|
4174
|
+
name: UseCase.useCaseName,
|
|
4175
|
+
value: UseCase,
|
|
4176
|
+
key: UseCase,
|
|
4177
|
+
from: entry.from
|
|
4178
|
+
});
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
3754
4181
|
return registry;
|
|
3755
4182
|
}
|
|
3756
4183
|
function recordsIn(properties) {
|
|
@@ -4087,6 +4514,36 @@ function describeRegistry(name, extended, registry, provider, named, log) {
|
|
|
4087
4514
|
})
|
|
4088
4515
|
};
|
|
4089
4516
|
};
|
|
4517
|
+
const describeOption = (value) => {
|
|
4518
|
+
if (typeof value === "function") {
|
|
4519
|
+
if (isDeclaredException(value)) {
|
|
4520
|
+
return exceptionRefs.get(value) ?? { core: value.name };
|
|
4521
|
+
}
|
|
4522
|
+
return scope.entities.get(value) ?? scope.records.get(value) ?? { kind: "opaque" };
|
|
4523
|
+
}
|
|
4524
|
+
if (Array.isArray(value))
|
|
4525
|
+
return value.map(describeOption);
|
|
4526
|
+
if (typeof value === "object" && value !== null) {
|
|
4527
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined).map(([key, item]) => [key, describeOption(item)]));
|
|
4528
|
+
}
|
|
4529
|
+
return value;
|
|
4530
|
+
};
|
|
4531
|
+
const describeGeneric = (UseCase) => {
|
|
4532
|
+
const origin = useCaseOriginOf2(UseCase);
|
|
4533
|
+
if (!origin || interpretedStepsOf(UseCase) === undefined)
|
|
4534
|
+
return {};
|
|
4535
|
+
const repository = repositories.get(inspectRepository(origin.repository).state);
|
|
4536
|
+
if (repository === undefined)
|
|
4537
|
+
return {};
|
|
4538
|
+
return {
|
|
4539
|
+
generic: {
|
|
4540
|
+
name: origin.plugin.name,
|
|
4541
|
+
version: origin.plugin.version,
|
|
4542
|
+
repository,
|
|
4543
|
+
options: describeOption(origin.options)
|
|
4544
|
+
}
|
|
4545
|
+
};
|
|
4546
|
+
};
|
|
4090
4547
|
const refsOf = (blueprints) => {
|
|
4091
4548
|
const refs = {};
|
|
4092
4549
|
for (const [key, blueprint] of Object.entries(blueprints)) {
|
|
@@ -4188,7 +4645,8 @@ function describeRegistry(name, extended, registry, provider, named, log) {
|
|
|
4188
4645
|
ref: repositories.get(inspectRepository(dependency).state)
|
|
4189
4646
|
}
|
|
4190
4647
|
])),
|
|
4191
|
-
implementation: describeImplementation(UseCase)
|
|
4648
|
+
implementation: describeImplementation(UseCase),
|
|
4649
|
+
...describeGeneric(UseCase)
|
|
4192
4650
|
};
|
|
4193
4651
|
}),
|
|
4194
4652
|
commands: each("commands", (entry) => {
|
|
@@ -4387,7 +4845,7 @@ function applicationOf2(settings) {
|
|
|
4387
4845
|
const boundCommandOf = new Map;
|
|
4388
4846
|
for (const entry of registry.commands.values()) {
|
|
4389
4847
|
const declared = entry.value;
|
|
4390
|
-
const rebuilt = !rebinds ? declared :
|
|
4848
|
+
const rebuilt = !rebinds ? declared : commandOf({
|
|
4391
4849
|
name: declared.commandName,
|
|
4392
4850
|
description: declared.description,
|
|
4393
4851
|
tags: declared.tags,
|
|
@@ -4629,7 +5087,12 @@ var INVALID_INPUT = new Set([
|
|
|
4629
5087
|
var CONFIGURATION = new Set(["InvalidCommandEnvironment"]);
|
|
4630
5088
|
var NOT_FOUND = new Set([
|
|
4631
5089
|
"RepositoryEntityNotFound",
|
|
4632
|
-
"UnknownCommandOperation"
|
|
5090
|
+
"UnknownCommandOperation",
|
|
5091
|
+
"UseCaseEntityNotFound"
|
|
5092
|
+
]);
|
|
5093
|
+
var CONFLICT = new Set([
|
|
5094
|
+
"RepositoryUniqueViolation",
|
|
5095
|
+
"UseCaseEntityConflict"
|
|
4633
5096
|
]);
|
|
4634
5097
|
function raisedOf(error) {
|
|
4635
5098
|
let raised = error;
|
|
@@ -4654,8 +5117,10 @@ function statusOf2(error, operation, statuses) {
|
|
|
4654
5117
|
}
|
|
4655
5118
|
if (NOT_FOUND.has(name))
|
|
4656
5119
|
return 404;
|
|
4657
|
-
if (name
|
|
5120
|
+
if (CONFLICT.has(name))
|
|
4658
5121
|
return 409;
|
|
5122
|
+
if (name === "UseCaseInvalidCredentials")
|
|
5123
|
+
return 401;
|
|
4659
5124
|
if (operation?.errors.some((Declared) => error.is(Declared) || Declared.name === name)) {
|
|
4660
5125
|
return 422;
|
|
4661
5126
|
}
|
|
@@ -5000,17 +5465,6 @@ var select2 = transformOf2({
|
|
|
5000
5465
|
}
|
|
5001
5466
|
});
|
|
5002
5467
|
// src/core/record/plugin-catalog.ts
|
|
5003
|
-
function satisfies(available, requested) {
|
|
5004
|
-
const [major, minor, patch] = versionParts(available);
|
|
5005
|
-
const [wantedMajor, wantedMinor, wantedPatch] = versionParts(requested);
|
|
5006
|
-
if (major !== wantedMajor)
|
|
5007
|
-
return false;
|
|
5008
|
-
if (major === 0 && minor !== wantedMinor)
|
|
5009
|
-
return false;
|
|
5010
|
-
if (minor !== wantedMinor)
|
|
5011
|
-
return minor > wantedMinor;
|
|
5012
|
-
return patch >= wantedPatch;
|
|
5013
|
-
}
|
|
5014
5468
|
var LISTS2 = ["keys", "sensitive", "unique", "change", "members"];
|
|
5015
5469
|
function isDescriptor(value) {
|
|
5016
5470
|
if (typeof value !== "object" || value === null)
|
|
@@ -5467,6 +5921,19 @@ function passwordIssues2(value) {
|
|
|
5467
5921
|
}
|
|
5468
5922
|
return issues;
|
|
5469
5923
|
}
|
|
5924
|
+
var escaped = PASSWORD_SPECIAL_CHARACTERS2.replace(/[\\\]^-]/g, "\\$&");
|
|
5925
|
+
|
|
5926
|
+
class PlainPassword2 extends propertyOf2({
|
|
5927
|
+
name: "plainPassword",
|
|
5928
|
+
schema: Type5.String({
|
|
5929
|
+
minLength: PASSWORD_MIN_LENGTH2,
|
|
5930
|
+
maxLength: PASSWORD_MAX_LENGTH2,
|
|
5931
|
+
pattern: `^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[${escaped}])`
|
|
5932
|
+
}),
|
|
5933
|
+
default: "Demo@123",
|
|
5934
|
+
sensitive: true
|
|
5935
|
+
}) {
|
|
5936
|
+
}
|
|
5470
5937
|
|
|
5471
5938
|
class Password2 extends propertyOf2({
|
|
5472
5939
|
name: "password",
|
|
@@ -5474,6 +5941,10 @@ class Password2 extends propertyOf2({
|
|
|
5474
5941
|
default: DEMO_HASH,
|
|
5475
5942
|
sensitive: true
|
|
5476
5943
|
}) {
|
|
5944
|
+
static [PropertyInput2] = {
|
|
5945
|
+
accepts: PlainPassword2,
|
|
5946
|
+
convert: async (plain) => (await Password2.hash(plain)).value
|
|
5947
|
+
};
|
|
5477
5948
|
static async hash(plain) {
|
|
5478
5949
|
const issues = passwordIssues2(plain);
|
|
5479
5950
|
if (issues.length > 0) {
|
|
@@ -5601,6 +6072,511 @@ var Std2 = blueprintPluginOf2("std", {
|
|
|
5601
6072
|
|
|
5602
6073
|
// src/std/blueprint.ts
|
|
5603
6074
|
var blueprintOf2 = blueprintBuilderOf2(Std2);
|
|
6075
|
+
// src/std/use-cases/shared.ts
|
|
6076
|
+
var fields = blueprintBuilderOf2();
|
|
6077
|
+
var IDENTIFIER3 = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
6078
|
+
var capitalize2 = (key) => `${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
6079
|
+
function literal(value) {
|
|
6080
|
+
if (typeof value === "string") {
|
|
6081
|
+
return value.startsWith("$") ? { literal: value } : value;
|
|
6082
|
+
}
|
|
6083
|
+
if (Array.isArray(value))
|
|
6084
|
+
return value.map(literal);
|
|
6085
|
+
if (isPlain2(value)) {
|
|
6086
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, literal(item)]));
|
|
6087
|
+
}
|
|
6088
|
+
return value;
|
|
6089
|
+
}
|
|
6090
|
+
function checkOptions(target, options, allowed) {
|
|
6091
|
+
const known = [...COMMON, ...allowed];
|
|
6092
|
+
for (const key of Object.keys(options)) {
|
|
6093
|
+
if (!known.includes(key)) {
|
|
6094
|
+
target.unavailable(`it has no option "${key}" (options: ${known.join(", ")})`);
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
var COMMON = ["name", "description", "tags", "deprecated", "errors"];
|
|
6099
|
+
function metaOf(target, options, verb) {
|
|
6100
|
+
const { name, description, tags, deprecated } = options;
|
|
6101
|
+
if (name !== undefined && (typeof name !== "string" || name === "")) {
|
|
6102
|
+
target.unavailable("the option name must be a non-empty string");
|
|
6103
|
+
}
|
|
6104
|
+
return {
|
|
6105
|
+
name: name ?? `${verb}${target.entityName}`,
|
|
6106
|
+
...description === undefined ? {} : { description },
|
|
6107
|
+
...tags === undefined ? {} : { tags },
|
|
6108
|
+
...deprecated === undefined ? {} : { deprecated }
|
|
6109
|
+
};
|
|
6110
|
+
}
|
|
6111
|
+
function asOf(target, options) {
|
|
6112
|
+
const { as = "item" } = options;
|
|
6113
|
+
if (typeof as !== "string" || !IDENTIFIER3.test(as)) {
|
|
6114
|
+
target.unavailable("the option as must be an identifier");
|
|
6115
|
+
}
|
|
6116
|
+
return as;
|
|
6117
|
+
}
|
|
6118
|
+
function errorOf(target, options, kind, fallback) {
|
|
6119
|
+
const { errors } = options;
|
|
6120
|
+
if (errors === undefined)
|
|
6121
|
+
return fallback;
|
|
6122
|
+
if (!isPlain2(errors)) {
|
|
6123
|
+
return target.unavailable("the option errors must be an object of exception classes");
|
|
6124
|
+
}
|
|
6125
|
+
const given = errors[kind];
|
|
6126
|
+
if (given === undefined)
|
|
6127
|
+
return fallback;
|
|
6128
|
+
if (!isDeclaredException(given)) {
|
|
6129
|
+
return target.unavailable(`errors.${kind} must be a class made by exceptionOf`);
|
|
6130
|
+
}
|
|
6131
|
+
return given;
|
|
6132
|
+
}
|
|
6133
|
+
function keysOf(target, options, option) {
|
|
6134
|
+
const given = options[option];
|
|
6135
|
+
if (given === undefined)
|
|
6136
|
+
return [];
|
|
6137
|
+
if (!Array.isArray(given) || given.some((key) => typeof key !== "string")) {
|
|
6138
|
+
return target.unavailable(`the option ${option} must be a list of keys`);
|
|
6139
|
+
}
|
|
6140
|
+
return given.map((key) => target.field(key));
|
|
6141
|
+
}
|
|
6142
|
+
function valuesOf(target, options, option) {
|
|
6143
|
+
const given = options[option];
|
|
6144
|
+
if (given === undefined)
|
|
6145
|
+
return {};
|
|
6146
|
+
if (!isPlain2(given)) {
|
|
6147
|
+
return target.unavailable(`the option ${option} must be an object`);
|
|
6148
|
+
}
|
|
6149
|
+
for (const key of Object.keys(given)) {
|
|
6150
|
+
if (target.field(key).identity) {
|
|
6151
|
+
target.unavailable(`${option}.${key} is an identity key`);
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
return given;
|
|
6155
|
+
}
|
|
6156
|
+
function ownerOf(target, options) {
|
|
6157
|
+
const { owner } = options;
|
|
6158
|
+
if (owner === undefined)
|
|
6159
|
+
return;
|
|
6160
|
+
if (!isPlain2(owner) || typeof owner.context !== "string" || !IDENTIFIER3.test(owner.context) || owner.key !== undefined && typeof owner.key !== "string") {
|
|
6161
|
+
return target.unavailable("the option owner must be { key?, context }, context naming a context key");
|
|
6162
|
+
}
|
|
6163
|
+
const plan = target.field(owner.key ?? "ownerId");
|
|
6164
|
+
if (plan.identity || plan.optional) {
|
|
6165
|
+
target.unavailable(`owner.key "${plan.key}" must be a required key`);
|
|
6166
|
+
}
|
|
6167
|
+
if (plan.kind === "entity" && plan.related) {
|
|
6168
|
+
return {
|
|
6169
|
+
plan,
|
|
6170
|
+
context: owner.context,
|
|
6171
|
+
field: plan.related.blueprint.properties.id,
|
|
6172
|
+
reference: `$item.${plan.key}.id`
|
|
6173
|
+
};
|
|
6174
|
+
}
|
|
6175
|
+
if (plan.kind !== "property") {
|
|
6176
|
+
target.unavailable(`owner.key "${plan.key}" must hold an id or an entity`);
|
|
6177
|
+
}
|
|
6178
|
+
return {
|
|
6179
|
+
plan,
|
|
6180
|
+
context: owner.context,
|
|
6181
|
+
field: plan.field,
|
|
6182
|
+
reference: `$item.${plan.key}`
|
|
6183
|
+
};
|
|
6184
|
+
}
|
|
6185
|
+
function contextOf2(owner) {
|
|
6186
|
+
return owner ? { context: fields(() => ({ [owner.context]: owner.field })) } : {};
|
|
6187
|
+
}
|
|
6188
|
+
function loadSteps(owner, notFound) {
|
|
6189
|
+
return [
|
|
6190
|
+
{ find: "repository.findById", args: ["$input.id"], as: "item" },
|
|
6191
|
+
{ ensure: "exists", value: "$item", error: notFound },
|
|
6192
|
+
...owner ? [
|
|
6193
|
+
{
|
|
6194
|
+
ensure: "equals",
|
|
6195
|
+
value: owner.reference,
|
|
6196
|
+
other: `$context.${owner.context}`,
|
|
6197
|
+
error: notFound
|
|
6198
|
+
}
|
|
6199
|
+
] : []
|
|
6200
|
+
];
|
|
6201
|
+
}
|
|
6202
|
+
var idOf2 = (target) => target.field("id").field;
|
|
6203
|
+
var distinct = (...errors) => [...new Set(errors)];
|
|
6204
|
+
function entityUseCase(target, options, verb, allowed, steps) {
|
|
6205
|
+
checkOptions(target, options, ["as", "owner", ...allowed]);
|
|
6206
|
+
const owner = ownerOf(target, options);
|
|
6207
|
+
const notFound = errorOf(target, options, "notFound", UseCaseEntityNotFound2);
|
|
6208
|
+
const body = steps({ owner });
|
|
6209
|
+
return {
|
|
6210
|
+
...metaOf(target, options, verb),
|
|
6211
|
+
blueprint: fields(() => ({ id: idOf2(target) })),
|
|
6212
|
+
response: fields(() => ({ [asOf(target, options)]: target.entity })),
|
|
6213
|
+
...contextOf2(owner),
|
|
6214
|
+
dependencies: { repository: target.repository },
|
|
6215
|
+
errors: [notFound],
|
|
6216
|
+
steps: [
|
|
6217
|
+
...loadSteps(owner, notFound),
|
|
6218
|
+
...body.steps,
|
|
6219
|
+
{ return: { [asOf(target, options)]: body.result } }
|
|
6220
|
+
]
|
|
6221
|
+
};
|
|
6222
|
+
}
|
|
6223
|
+
|
|
6224
|
+
// src/std/use-cases/crud.ts
|
|
6225
|
+
function createCompilation(target, options, verb, before = [], errors = []) {
|
|
6226
|
+
checkOptions(target, options, ["as", "owner", "omit", "values"]);
|
|
6227
|
+
const owner = ownerOf(target, options);
|
|
6228
|
+
const omitted = keysOf(target, options, "omit");
|
|
6229
|
+
const constants = valuesOf(target, options, "values");
|
|
6230
|
+
const notFound = errorOf(target, options, "notFound", UseCaseEntityNotFound2);
|
|
6231
|
+
const ownerError = errorOf(target, options, "owner", notFound);
|
|
6232
|
+
const input = {};
|
|
6233
|
+
const values = {};
|
|
6234
|
+
const dependencies = {
|
|
6235
|
+
repository: target.repository
|
|
6236
|
+
};
|
|
6237
|
+
const steps = [];
|
|
6238
|
+
const used = new Set(errors);
|
|
6239
|
+
const take = (key, field) => {
|
|
6240
|
+
if (Object.hasOwn(input, key)) {
|
|
6241
|
+
target.unavailable(`two keys of the entity take the input "${key}"`);
|
|
6242
|
+
}
|
|
6243
|
+
input[key] = field;
|
|
6244
|
+
};
|
|
6245
|
+
const load = (key, reference, error) => {
|
|
6246
|
+
const dependency = `${key}Repository`;
|
|
6247
|
+
dependencies[dependency] = target.relation(key, ["findById"]);
|
|
6248
|
+
used.add(error);
|
|
6249
|
+
steps.push({
|
|
6250
|
+
find: `${dependency}.findById`,
|
|
6251
|
+
args: [reference],
|
|
6252
|
+
as: `${key}Found`
|
|
6253
|
+
}, { ensure: "exists", value: `$${key}Found`, error });
|
|
6254
|
+
return `$${key}Found`;
|
|
6255
|
+
};
|
|
6256
|
+
for (const plan of target.fields) {
|
|
6257
|
+
const { key } = plan;
|
|
6258
|
+
if (plan.identity)
|
|
6259
|
+
continue;
|
|
6260
|
+
if (Object.hasOwn(constants, key)) {
|
|
6261
|
+
values[key] = literal(constants[key]);
|
|
6262
|
+
continue;
|
|
6263
|
+
}
|
|
6264
|
+
if (owner?.plan.key === key) {
|
|
6265
|
+
values[key] = plan.kind === "entity" ? load(key, `$context.${owner.context}`, ownerError) : `$context.${owner.context}`;
|
|
6266
|
+
continue;
|
|
6267
|
+
}
|
|
6268
|
+
const unloadable = plan.kind === "entity-list" || plan.kind === "entity" && plan.optional;
|
|
6269
|
+
if (omitted.includes(plan) || unloadable) {
|
|
6270
|
+
if (!plan.optional) {
|
|
6271
|
+
target.unavailable(unloadable ? `"${key}" holds a list of entities, which the input cannot take; give it in values` : `"${key}" is required and cannot be omitted`);
|
|
6272
|
+
}
|
|
6273
|
+
continue;
|
|
6274
|
+
}
|
|
6275
|
+
take(plan.input.key, plan.input.field);
|
|
6276
|
+
const given = `$input.${plan.input.key}`;
|
|
6277
|
+
if (plan.kind === "entity") {
|
|
6278
|
+
values[key] = load(key, given, notFound);
|
|
6279
|
+
} else if (plan.convert) {
|
|
6280
|
+
steps.push({
|
|
6281
|
+
run: `convert${capitalize2(key)}`,
|
|
6282
|
+
fn: plan.convert,
|
|
6283
|
+
args: [given],
|
|
6284
|
+
as: `${key}Converted`
|
|
6285
|
+
});
|
|
6286
|
+
values[key] = `$${key}Converted`;
|
|
6287
|
+
} else {
|
|
6288
|
+
values[key] = given;
|
|
6289
|
+
}
|
|
6290
|
+
}
|
|
6291
|
+
return {
|
|
6292
|
+
...metaOf(target, options, verb),
|
|
6293
|
+
blueprint: fields(() => input),
|
|
6294
|
+
response: fields(() => ({ [asOf(target, options)]: target.entity })),
|
|
6295
|
+
...contextOf2(owner),
|
|
6296
|
+
dependencies,
|
|
6297
|
+
errors: distinct(...used),
|
|
6298
|
+
steps: [
|
|
6299
|
+
...before,
|
|
6300
|
+
...steps,
|
|
6301
|
+
{ build: target.entity, values, as: "built" },
|
|
6302
|
+
{ create: "repository", value: "$built", as: "item" },
|
|
6303
|
+
{ return: { [asOf(target, options)]: "$item" } }
|
|
6304
|
+
]
|
|
6305
|
+
};
|
|
6306
|
+
}
|
|
6307
|
+
var create2 = useCasePluginOf2("create", {
|
|
6308
|
+
description: "Builds an entity from the input (its keys, less the identity; an entity it holds by id) and stores it.",
|
|
6309
|
+
requires: { actions: ["create"] },
|
|
6310
|
+
compile: (target, options) => createCompilation(target, options, "Create")
|
|
6311
|
+
});
|
|
6312
|
+
var get = useCasePluginOf2("get", {
|
|
6313
|
+
description: "Gives the entity of an id.",
|
|
6314
|
+
requires: { actions: ["findById"] },
|
|
6315
|
+
compile: (target, options) => entityUseCase(target, options, "Get", [], () => ({
|
|
6316
|
+
steps: [],
|
|
6317
|
+
result: "$item"
|
|
6318
|
+
}))
|
|
6319
|
+
});
|
|
6320
|
+
var list = useCasePluginOf2("list", {
|
|
6321
|
+
description: "Gives a page of entities: { items, total, limit, offset }.",
|
|
6322
|
+
requires: { actions: ["findPage"] },
|
|
6323
|
+
compile: (target, options) => {
|
|
6324
|
+
checkOptions(target, options, ["owner", "orderBy"]);
|
|
6325
|
+
const owner = ownerOf(target, options);
|
|
6326
|
+
const { orderBy = [{ key: "createdAt", direction: "desc" }] } = options;
|
|
6327
|
+
if (!Array.isArray(orderBy)) {
|
|
6328
|
+
target.unavailable("the option orderBy must be a list of orders");
|
|
6329
|
+
}
|
|
6330
|
+
for (const order of orderBy) {
|
|
6331
|
+
target.field(String(order?.key));
|
|
6332
|
+
}
|
|
6333
|
+
return {
|
|
6334
|
+
...metaOf(target, options, "List"),
|
|
6335
|
+
blueprint: fields(() => ({
|
|
6336
|
+
limit: optionalOf2(Integer2),
|
|
6337
|
+
offset: optionalOf2(Integer2)
|
|
6338
|
+
})),
|
|
6339
|
+
response: fields(() => ({
|
|
6340
|
+
items: listOf2(target.entity),
|
|
6341
|
+
total: Integer2,
|
|
6342
|
+
limit: Integer2,
|
|
6343
|
+
offset: Integer2
|
|
6344
|
+
})),
|
|
6345
|
+
...contextOf2(owner),
|
|
6346
|
+
dependencies: { repository: target.repository },
|
|
6347
|
+
steps: [
|
|
6348
|
+
{
|
|
6349
|
+
find: "repository.findPage",
|
|
6350
|
+
args: [
|
|
6351
|
+
{
|
|
6352
|
+
...owner ? {
|
|
6353
|
+
where: {
|
|
6354
|
+
[owner.plan.key]: `$context.${owner.context}`
|
|
6355
|
+
}
|
|
6356
|
+
} : {},
|
|
6357
|
+
orderBy: literal(orderBy),
|
|
6358
|
+
limit: "$input.limit",
|
|
6359
|
+
offset: "$input.offset"
|
|
6360
|
+
}
|
|
6361
|
+
],
|
|
6362
|
+
as: "page"
|
|
6363
|
+
},
|
|
6364
|
+
{
|
|
6365
|
+
return: {
|
|
6366
|
+
items: "$page.items",
|
|
6367
|
+
total: "$page.total",
|
|
6368
|
+
limit: "$page.limit",
|
|
6369
|
+
offset: "$page.offset"
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
]
|
|
6373
|
+
};
|
|
6374
|
+
}
|
|
6375
|
+
});
|
|
6376
|
+
var update2 = useCasePluginOf2("update", {
|
|
6377
|
+
description: "Changes the change keys the input gives, leaving the others as they are, and stores the entity.",
|
|
6378
|
+
requires: { actions: ["findById", "update"] },
|
|
6379
|
+
compile: (target, options) => {
|
|
6380
|
+
checkOptions(target, options, ["as", "owner"]);
|
|
6381
|
+
const owner = ownerOf(target, options);
|
|
6382
|
+
const notFound = errorOf(target, options, "notFound", UseCaseEntityNotFound2);
|
|
6383
|
+
const plans = target.changeKeys.map((key) => target.field(key)).filter((plan) => plan.kind !== "entity" && plan.kind !== "entity-list");
|
|
6384
|
+
if (plans.length === 0) {
|
|
6385
|
+
target.unavailable("the entity has no change key an input can take");
|
|
6386
|
+
}
|
|
6387
|
+
if (plans.some((plan) => plan.key === "id")) {
|
|
6388
|
+
target.unavailable('"id" cannot be a change key');
|
|
6389
|
+
}
|
|
6390
|
+
const steps = [];
|
|
6391
|
+
for (const plan of plans) {
|
|
6392
|
+
const given = `$input.${plan.input.key}`;
|
|
6393
|
+
let value = given;
|
|
6394
|
+
if (plan.convert) {
|
|
6395
|
+
steps.push({
|
|
6396
|
+
run: `convert${capitalize2(plan.key)}`,
|
|
6397
|
+
fn: plan.convert,
|
|
6398
|
+
args: [given],
|
|
6399
|
+
as: `${plan.key}Converted`
|
|
6400
|
+
});
|
|
6401
|
+
value = `$${plan.key}Converted`;
|
|
6402
|
+
}
|
|
6403
|
+
steps.push({ change: "$item", key: plan.key, value, ifPresent: true });
|
|
6404
|
+
}
|
|
6405
|
+
return {
|
|
6406
|
+
...metaOf(target, options, "Update"),
|
|
6407
|
+
blueprint: fields(() => ({
|
|
6408
|
+
id: idOf2(target),
|
|
6409
|
+
...Object.fromEntries(plans.map((plan) => [plan.input.key, optionalOf2(plan.input.field)]))
|
|
6410
|
+
})),
|
|
6411
|
+
response: fields(() => ({ [asOf(target, options)]: target.entity })),
|
|
6412
|
+
...contextOf2(owner),
|
|
6413
|
+
dependencies: { repository: target.repository },
|
|
6414
|
+
errors: [notFound],
|
|
6415
|
+
steps: [
|
|
6416
|
+
...loadSteps(owner, notFound),
|
|
6417
|
+
...steps,
|
|
6418
|
+
{ update: "repository", value: "$item", as: "saved" },
|
|
6419
|
+
{ return: { [asOf(target, options)]: "$saved" } }
|
|
6420
|
+
]
|
|
6421
|
+
};
|
|
6422
|
+
}
|
|
6423
|
+
});
|
|
6424
|
+
var remove2 = useCasePluginOf2("delete", {
|
|
6425
|
+
description: "Deletes the entity of an id, hard or soft as the repository declares, and gives it.",
|
|
6426
|
+
requires: { actions: ["findById", "delete"] },
|
|
6427
|
+
compile: (target, options) => entityUseCase(target, options, "Delete", [], () => ({
|
|
6428
|
+
steps: [{ delete: "repository", value: "$item", as: "deleted" }],
|
|
6429
|
+
result: "$deleted"
|
|
6430
|
+
}))
|
|
6431
|
+
});
|
|
6432
|
+
var restore2 = useCasePluginOf2("restore", {
|
|
6433
|
+
description: "Brings back the soft-deleted entity of an id; for a repository with soft deletion and the restore action.",
|
|
6434
|
+
requires: { actions: ["restore"] },
|
|
6435
|
+
compile: (target, options) => {
|
|
6436
|
+
checkOptions(target, options, ["as"]);
|
|
6437
|
+
return {
|
|
6438
|
+
...metaOf(target, options, "Restore"),
|
|
6439
|
+
blueprint: fields(() => ({ id: idOf2(target) })),
|
|
6440
|
+
response: fields(() => ({ [asOf(target, options)]: target.entity })),
|
|
6441
|
+
dependencies: { repository: target.repository },
|
|
6442
|
+
steps: [
|
|
6443
|
+
{ restore: "repository", value: "$input.id", as: "item" },
|
|
6444
|
+
{ return: { [asOf(target, options)]: "$item" } }
|
|
6445
|
+
]
|
|
6446
|
+
};
|
|
6447
|
+
}
|
|
6448
|
+
});
|
|
6449
|
+
|
|
6450
|
+
// src/std/use-cases/auth.ts
|
|
6451
|
+
var authenticatable = (actions) => ({
|
|
6452
|
+
requires: { actions, plugins: ["authenticatable"] }
|
|
6453
|
+
});
|
|
6454
|
+
var register = useCasePluginOf2("register", {
|
|
6455
|
+
description: "Stores an authenticatable entity built from the input, its password given plain and stored as a hash, unless the email is taken.",
|
|
6456
|
+
...authenticatable(["findByEmail", "create"]),
|
|
6457
|
+
compile: (target, options) => {
|
|
6458
|
+
const conflict = errorOf(target, options, "conflict", UseCaseEntityConflict2);
|
|
6459
|
+
return createCompilation(target, options, "Register", [
|
|
6460
|
+
{
|
|
6461
|
+
find: "repository.findByEmail",
|
|
6462
|
+
args: ["$input.email"],
|
|
6463
|
+
as: "existing"
|
|
6464
|
+
},
|
|
6465
|
+
{ ensure: "absent", value: "$existing", error: conflict }
|
|
6466
|
+
], [conflict]);
|
|
6467
|
+
}
|
|
6468
|
+
});
|
|
6469
|
+
var login = useCasePluginOf2("login", {
|
|
6470
|
+
description: "Gives the authenticatable entity of an email when the password matches; issues no token.",
|
|
6471
|
+
...authenticatable(["findByEmail"]),
|
|
6472
|
+
compile: (target, options) => {
|
|
6473
|
+
checkOptions(target, options, ["as"]);
|
|
6474
|
+
const invalid = errorOf(target, options, "invalid", UseCaseInvalidCredentials2);
|
|
6475
|
+
return {
|
|
6476
|
+
...metaOf(target, options, "Login"),
|
|
6477
|
+
blueprint: fields(() => ({
|
|
6478
|
+
email: target.field("email").field,
|
|
6479
|
+
password: StringProperty2
|
|
6480
|
+
})),
|
|
6481
|
+
response: fields(() => ({ [asOf(target, options)]: target.entity })),
|
|
6482
|
+
dependencies: { repository: target.repository },
|
|
6483
|
+
errors: [invalid],
|
|
6484
|
+
steps: [
|
|
6485
|
+
{ find: "repository.findByEmail", args: ["$input.email"], as: "item" },
|
|
6486
|
+
{ ensure: "exists", value: "$item", error: invalid },
|
|
6487
|
+
{
|
|
6488
|
+
invoke: "$item",
|
|
6489
|
+
method: "verifyPassword",
|
|
6490
|
+
args: ["$input.password"],
|
|
6491
|
+
as: "matches"
|
|
6492
|
+
},
|
|
6493
|
+
{ ensure: "true", value: "$matches", error: invalid },
|
|
6494
|
+
{ return: { [asOf(target, options)]: "$item" } }
|
|
6495
|
+
]
|
|
6496
|
+
};
|
|
6497
|
+
}
|
|
6498
|
+
});
|
|
6499
|
+
|
|
6500
|
+
// src/std/use-cases/lifecycle.ts
|
|
6501
|
+
function invoking(target, options, verb, method) {
|
|
6502
|
+
const set = valuesOf(target, options, "set");
|
|
6503
|
+
const changes = Object.entries(set).map(([key, value]) => {
|
|
6504
|
+
if (!target.changeKeys.includes(key)) {
|
|
6505
|
+
target.unavailable(`set.${key} is not a change key of the entity`);
|
|
6506
|
+
}
|
|
6507
|
+
return { change: "$item", key, value: literal(value) };
|
|
6508
|
+
});
|
|
6509
|
+
return entityUseCase(target, options, verb, ["set"], () => ({
|
|
6510
|
+
steps: [
|
|
6511
|
+
...changes,
|
|
6512
|
+
{ invoke: "$item", method },
|
|
6513
|
+
{ update: "repository", value: "$item", as: "saved" }
|
|
6514
|
+
],
|
|
6515
|
+
result: "$saved"
|
|
6516
|
+
}));
|
|
6517
|
+
}
|
|
6518
|
+
var lifecycle = (plugin) => ({
|
|
6519
|
+
requires: { actions: ["findById", "update"], plugins: [plugin] }
|
|
6520
|
+
});
|
|
6521
|
+
var publish = useCasePluginOf2("publish", {
|
|
6522
|
+
description: "Publishes the publishable entity of an id and stores it.",
|
|
6523
|
+
...lifecycle("publishable"),
|
|
6524
|
+
compile: (target, options) => invoking(target, options, "Publish", "publish")
|
|
6525
|
+
});
|
|
6526
|
+
var unpublish = useCasePluginOf2("unpublish", {
|
|
6527
|
+
description: "Makes the publishable entity of an id a draft again and stores it.",
|
|
6528
|
+
...lifecycle("publishable"),
|
|
6529
|
+
compile: (target, options) => invoking(target, options, "Unpublish", "unpublish")
|
|
6530
|
+
});
|
|
6531
|
+
var archive = useCasePluginOf2("archive", {
|
|
6532
|
+
description: "Archives the archivable entity of an id and stores it.",
|
|
6533
|
+
...lifecycle("archivable"),
|
|
6534
|
+
compile: (target, options) => invoking(target, options, "Archive", "archive")
|
|
6535
|
+
});
|
|
6536
|
+
var unarchive = useCasePluginOf2("unarchive", {
|
|
6537
|
+
description: "Makes the archived entity of an id active again and stores it.",
|
|
6538
|
+
...lifecycle("archivable"),
|
|
6539
|
+
compile: (target, options) => invoking(target, options, "Unarchive", "restore")
|
|
6540
|
+
});
|
|
6541
|
+
function tagging(target, options, verb, method) {
|
|
6542
|
+
checkOptions(target, options, ["as", "owner"]);
|
|
6543
|
+
const owner = ownerOf(target, options);
|
|
6544
|
+
const notFound = errorOf(target, options, "notFound", UseCaseEntityNotFound2);
|
|
6545
|
+
const { item } = target.field("tags").field;
|
|
6546
|
+
return {
|
|
6547
|
+
...metaOf(target, options, verb),
|
|
6548
|
+
blueprint: fields(() => ({
|
|
6549
|
+
id: idOf2(target),
|
|
6550
|
+
tags: listOf2(item, { minItems: 1 })
|
|
6551
|
+
})),
|
|
6552
|
+
response: fields(() => ({ [asOf(target, options)]: target.entity })),
|
|
6553
|
+
...contextOf2(owner),
|
|
6554
|
+
dependencies: { repository: target.repository },
|
|
6555
|
+
errors: [notFound],
|
|
6556
|
+
steps: [
|
|
6557
|
+
...loadSteps(owner, notFound),
|
|
6558
|
+
{ invoke: "$item", method, args: "$input.tags" },
|
|
6559
|
+
{ update: "repository", value: "$item", as: "saved" },
|
|
6560
|
+
{ return: { [asOf(target, options)]: "$saved" } }
|
|
6561
|
+
]
|
|
6562
|
+
};
|
|
6563
|
+
}
|
|
6564
|
+
var tag = useCasePluginOf2("tag", {
|
|
6565
|
+
description: "Adds tags to the taggable entity of an id and stores it.",
|
|
6566
|
+
...lifecycle("taggable"),
|
|
6567
|
+
compile: (target, options) => tagging(target, options, "Tag", "tag")
|
|
6568
|
+
});
|
|
6569
|
+
var untag = useCasePluginOf2("untag", {
|
|
6570
|
+
description: "Removes tags from the taggable entity of an id and stores it.",
|
|
6571
|
+
...lifecycle("taggable"),
|
|
6572
|
+
compile: (target, options) => tagging(target, options, "Untag", "untag")
|
|
6573
|
+
});
|
|
6574
|
+
|
|
6575
|
+
// src/std/use-cases/index.ts
|
|
6576
|
+
var StdUseCases2 = useCaseCatalogOf2(create2, get, list, update2, remove2, restore2, publish, unpublish, archive, unarchive, tag, untag, register, login);
|
|
6577
|
+
|
|
6578
|
+
// src/std/command.ts
|
|
6579
|
+
var commandOf2 = commandBuilderOf2(StdUseCases2);
|
|
5604
6580
|
// src/std/record-plugins.ts
|
|
5605
6581
|
var now = () => new Date().toISOString();
|
|
5606
6582
|
|
|
@@ -5688,4 +6664,4 @@ var entityOf2 = entityBuilderOf2({
|
|
|
5688
6664
|
var recordOf2 = recordBuilderOf2(StdRecordPlugins2);
|
|
5689
6665
|
// src/std/stages.ts
|
|
5690
6666
|
var StdStages2 = stageCatalogOf2(select2, openapi2);
|
|
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,
|
|
6667
|
+
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, InvalidUseCasePlugin2, UnknownUseCasePlugin2, IncompatibleUseCasePlugin2, UseCaseUnavailable2, UseCaseEntityNotFound2, UseCaseEntityConflict2, UseCaseInvalidCredentials2, PropertyInput2, propertyInputOf2, useCaseTargetOf2, InvalidUseCaseDeclaration2, UseCaseAlreadyExecuted2, InvalidUseCaseDependencies2, AbstractUseCase2, useCaseOf2, isUseCaseClass2, isUseCase2, isUseCasePlugin2, useCaseOriginOf2, useCasePluginOf2, isUseCaseCatalog2, useCaseCatalogOf2, InvalidCommandDeclaration2, UnknownCommandOperation2, InvalidCommandContext2, InvalidCommandEnvironment2, CommandEventFailed2, commandBuilderOf2, 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, PlainPassword2, Password2, StringProperty2, Text2, NumberProperty2, Integer2, BooleanProperty2, DateTime2, DateProperty2, CreatedAt2, UpdatedAt2, DeletedAt2, Std2, blueprintOf2, StdUseCases2, commandOf2, Publishable2, Archivable2, Taggable2, Ownable2, Authenticatable2, StdRecordPlugins2, entityOf2, recordOf2, StdStages2 };
|