@afora/core 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +52 -30
  2. package/dist/adapters.js +31 -55
  3. package/dist/core/application/application.d.ts +30 -0
  4. package/dist/core/application/application.js +1291 -0
  5. package/dist/core/application/exceptions.js +85 -0
  6. package/dist/core/application/migrate.js +67 -0
  7. package/dist/core/application/providers.js +108 -0
  8. package/dist/core/application/registry.js +23 -0
  9. package/dist/core/application/schema.js +398 -0
  10. package/dist/core/application/start.js +162 -0
  11. package/dist/core/application/types/application.interface.d.ts +23 -3
  12. package/dist/core/auth/jwt.js +200 -0
  13. package/dist/core/auth/types/jwt.interface.d.ts +10 -2
  14. package/dist/core/blueprint/blueprint-plugin.js +7 -0
  15. package/dist/core/blueprint/blueprint.js +146 -0
  16. package/dist/core/blueprint/callable.js +13 -0
  17. package/dist/core/blueprint/exceptions.js +20 -0
  18. package/dist/core/blueprint/is-blueprint.js +4 -0
  19. package/dist/core/blueprint/list.js +19 -0
  20. package/dist/core/blueprint/optional.js +69 -0
  21. package/dist/core/blueprint/record-field.js +14 -0
  22. package/dist/core/blueprint/slice.js +127 -0
  23. package/dist/core/blueprint/types/blueprint.interface.d.ts +16 -0
  24. package/dist/core/blueprint/variant.js +72 -0
  25. package/dist/core/command/command.js +428 -0
  26. package/dist/core/command/exceptions.js +30 -0
  27. package/dist/core/command/types/command.interface.d.ts +10 -1
  28. package/dist/core/entity/abstract-entity.d.ts +19 -0
  29. package/dist/core/entity/abstract-entity.js +181 -0
  30. package/dist/core/entity/entity.js +209 -0
  31. package/dist/core/entity/event.d.ts +39 -0
  32. package/dist/core/entity/event.js +80 -0
  33. package/dist/core/entity/exceptions.js +27 -0
  34. package/dist/core/entity/trust.js +7 -0
  35. package/dist/core/entity/version.js +13 -0
  36. package/dist/core/environment/environment.d.ts +26 -3
  37. package/dist/core/environment/environment.js +71 -0
  38. package/dist/core/event-policy/event-policy.js +76 -0
  39. package/dist/core/event-policy/exceptions.js +12 -0
  40. package/dist/core/event-policy/publish.js +98 -0
  41. package/dist/core/event-policy/publishers.js +18 -0
  42. package/dist/core/event-policy/types/event-policy.interface.d.ts +8 -2
  43. package/dist/core/exception/enums/exception-layers.js +15 -0
  44. package/dist/core/exception/exception.d.ts +15 -0
  45. package/dist/core/exception/exception.js +128 -0
  46. package/dist/core/exception/exported.js +49 -0
  47. package/dist/core/exception/guard.d.ts +20 -1
  48. package/dist/core/exception/guard.js +16 -0
  49. package/dist/core/exception/types/exception.interface.d.ts +13 -0
  50. package/dist/core/json.js +19 -0
  51. package/dist/core/logger/exceptions.js +7 -0
  52. package/dist/core/logger/logger.d.ts +14 -1
  53. package/dist/core/logger/logger.js +178 -0
  54. package/dist/core/openapi/exceptions.js +7 -0
  55. package/dist/core/openapi/openapi.js +132 -0
  56. package/dist/core/openapi/types/openapi.interface.d.ts +7 -1
  57. package/dist/core/output/access.js +66 -0
  58. package/dist/core/output/coerce.js +38 -0
  59. package/dist/core/output/exceptions.js +22 -0
  60. package/dist/{index-y280ew0k.js → core/output/http.js} +35 -80
  61. package/dist/core/output/openapi-sink.d.ts +10 -2
  62. package/dist/{index-hg1z2s2v.js → core/output/openapi-sink.js} +60 -544
  63. package/dist/core/output/output.js +192 -0
  64. package/dist/core/output/problem.js +30 -0
  65. package/dist/core/output/select.js +28 -0
  66. package/dist/core/output/stage.js +111 -0
  67. package/dist/core/output/status.js +52 -0
  68. package/dist/core/output/types/http.interface.d.ts +5 -1
  69. package/dist/core/output/types/output.interface.d.ts +24 -0
  70. package/dist/core/parse/issue.js +31 -0
  71. package/dist/core/parse/parse.d.ts +8 -1
  72. package/dist/core/parse/parse.js +323 -0
  73. package/dist/core/parse/strip.js +48 -0
  74. package/dist/core/property/abstract-property.js +37 -0
  75. package/dist/core/property/define-property.js +209 -0
  76. package/dist/core/property/enums/property-states.js +8 -0
  77. package/dist/core/property/exceptions.js +12 -0
  78. package/dist/core/property/input.js +10 -0
  79. package/dist/core/property/is-property.js +3 -0
  80. package/dist/core/property/lineage.js +8 -0
  81. package/dist/core/property/property.d.ts +27 -0
  82. package/dist/core/property/property.js +46 -0
  83. package/dist/core/property/refine.js +103 -0
  84. package/dist/core/record/abstract-record.d.ts +2 -1
  85. package/dist/core/record/abstract-record.js +307 -0
  86. package/dist/core/record/exceptions.js +49 -0
  87. package/dist/core/record/marks.js +7 -0
  88. package/dist/core/record/mask.js +60 -0
  89. package/dist/core/record/name.js +23 -0
  90. package/dist/core/record/plugin-catalog.js +115 -0
  91. package/dist/core/record/plugin.js +198 -0
  92. package/dist/core/record/record.js +117 -0
  93. package/dist/core/record/types/record.interface.d.ts +5 -0
  94. package/dist/core/repository/cursor.js +83 -0
  95. package/dist/core/repository/exceptions.js +72 -0
  96. package/dist/core/repository/in-memory.js +196 -0
  97. package/dist/core/repository/migrate.js +254 -0
  98. package/dist/core/repository/plan.js +27 -0
  99. package/dist/core/repository/provider.js +29 -0
  100. package/dist/core/repository/query.js +153 -0
  101. package/dist/core/repository/references.js +196 -0
  102. package/dist/core/repository/repository.d.ts +22 -2
  103. package/dist/core/repository/repository.js +720 -0
  104. package/dist/core/repository/state.js +138 -0
  105. package/dist/core/repository/types/query.interface.d.ts +13 -0
  106. package/dist/core/repository/types/repository.interface.d.ts +20 -8
  107. package/dist/core/repository/types/schema.interface.d.ts +8 -2
  108. package/dist/core/repository/upcast.js +179 -0
  109. package/dist/core/schema/schema.d.ts +9 -0
  110. package/dist/core/schema/schema.js +542 -0
  111. package/dist/core/standard/standard-schema.js +1 -0
  112. package/dist/core/standard/standard.js +23 -0
  113. package/dist/core/symbols/demo.d.ts +4 -0
  114. package/dist/core/symbols/demo.js +1 -0
  115. package/dist/core/symbols/kind.js +1 -0
  116. package/dist/core/symbols/meta.js +1 -0
  117. package/dist/core/symbols/state.d.ts +5 -0
  118. package/dist/core/symbols/state.js +1 -0
  119. package/dist/core/tracing/exceptions.d.ts +4 -0
  120. package/dist/core/tracing/exceptions.js +7 -0
  121. package/dist/core/tracing/tracing.d.ts +72 -0
  122. package/dist/core/tracing/tracing.js +101 -0
  123. package/dist/core/use-case/access.js +56 -0
  124. package/dist/core/use-case/decorators.js +65 -0
  125. package/dist/core/use-case/exceptions.d.ts +5 -1
  126. package/dist/core/use-case/exceptions.js +48 -0
  127. package/dist/core/use-case/plugin-catalog.js +68 -0
  128. package/dist/core/use-case/plugin.js +133 -0
  129. package/dist/core/use-case/route.js +200 -0
  130. package/dist/core/use-case/scope.d.ts +5 -0
  131. package/dist/core/use-case/scope.js +4 -0
  132. package/dist/core/use-case/steps/describe.d.ts +5 -1
  133. package/dist/core/use-case/steps/describe.js +24 -0
  134. package/dist/core/use-case/steps/references.js +105 -0
  135. package/dist/core/use-case/steps/run.js +139 -0
  136. package/dist/core/use-case/steps/types/check.interface.d.ts +27 -8
  137. package/dist/core/use-case/steps/types/steps.interface.d.ts +8 -1
  138. package/dist/core/use-case/steps/validate.js +368 -0
  139. package/dist/core/use-case/target.js +100 -0
  140. package/dist/core/use-case/types/route.interface.d.ts +25 -5
  141. package/dist/core/use-case/use-case.d.ts +77 -2
  142. package/dist/core/use-case/use-case.js +493 -0
  143. package/dist/experimental.d.ts +5 -0
  144. package/dist/experimental.js +18 -161
  145. package/dist/index.d.ts +7 -5
  146. package/dist/index.js +213 -367
  147. package/dist/std/blueprint-plugin.js +42 -0
  148. package/dist/std/blueprint.d.ts +21 -0
  149. package/dist/std/blueprint.js +3 -0
  150. package/dist/std/command.js +3 -0
  151. package/dist/std/entity.d.ts +20 -1
  152. package/dist/std/entity.js +30 -0
  153. package/dist/std/event-policies.js +3 -0
  154. package/dist/std/properties/contact.d.ts +3 -0
  155. package/dist/std/properties/contact.js +29 -0
  156. package/dist/std/properties/enum.js +26 -0
  157. package/dist/std/properties/exceptions.js +14 -0
  158. package/dist/std/properties/headless.js +45 -0
  159. package/dist/std/properties/identity.js +28 -0
  160. package/dist/std/properties/json.js +161 -0
  161. package/dist/std/properties/password.js +79 -0
  162. package/dist/std/properties/primitives.d.ts +4 -0
  163. package/dist/std/properties/primitives.js +28 -0
  164. package/dist/std/properties/time.js +55 -0
  165. package/dist/std/record-plugins.js +73 -0
  166. package/dist/std/record.d.ts +20 -1
  167. package/dist/std/record.js +3 -0
  168. package/dist/std/resource.js +39 -0
  169. package/dist/std/runtime.js +193 -0
  170. package/dist/std/stages.js +4 -0
  171. package/dist/std/use-cases/auth.js +193 -0
  172. package/dist/std/use-cases/crud.js +261 -0
  173. package/dist/std/use-cases/index.js +12 -0
  174. package/dist/std/use-cases/lifecycle.js +97 -0
  175. package/dist/std/use-cases/shared.js +232 -0
  176. package/dist/std/use-cases/types.d.ts +5 -1
  177. package/dist/std/use-cases/types.js +0 -0
  178. package/dist/testing/http-provider-contract.js +344 -0
  179. package/dist/testing/index.js +3 -1435
  180. package/dist/testing/memory-publisher.js +12 -0
  181. package/dist/testing/provider-contract.d.ts +12 -3
  182. package/dist/testing/provider-contract.js +1071 -0
  183. package/package.json +10 -3
  184. package/dist/index-3aae5jfq.js +0 -2997
  185. package/dist/index-ny3as79r.js +0 -4567
  186. package/dist/index-xegv6a1p.js +0 -4291
@@ -0,0 +1,196 @@
1
+ import { isBlueprint } from "../blueprint/is-blueprint.js";
2
+ import { isBlueprintList } from "../blueprint/list.js";
3
+ import { recordFieldOf } from "../blueprint/record-field.js";
4
+ import {
5
+ blueprintOfVariant,
6
+ isBlueprintVariant,
7
+ namedVariantOf
8
+ } from "../blueprint/variant.js";
9
+ import { isEntityClass } from "../entity/entity.js";
10
+ import { recordNameOf } from "../record/name.js";
11
+ import { scopedProvider } from "../use-case/scope.js";
12
+ import {
13
+ InvalidRepositoryData,
14
+ InvalidRepositoryDeclaration,
15
+ RepositoryEntityNotFound
16
+ } from "./exceptions.js";
17
+ import { contextOf, findState } from "./state.js";
18
+ import { upcastRows } from "./upcast.js";
19
+ function isPlain(value) {
20
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21
+ }
22
+ function recordClassOf(field) {
23
+ return recordFieldOf(field)?.record;
24
+ }
25
+ function entityClassesOf(properties) {
26
+ const found = new Set;
27
+ function visit(field) {
28
+ if (isBlueprintList(field)) {
29
+ visit(field.item);
30
+ } else if (isBlueprint(field)) {
31
+ walk(field.properties);
32
+ } else if (isBlueprintVariant(field)) {
33
+ for (const variant of Object.values(field.variants)) {
34
+ visit(variant);
35
+ }
36
+ } else {
37
+ const record = recordClassOf(field);
38
+ if (!record)
39
+ return;
40
+ if (isEntityClass(record))
41
+ found.add(record);
42
+ else
43
+ walk(record.blueprint.properties);
44
+ }
45
+ }
46
+ function walk(properties) {
47
+ for (const field of Object.values(properties))
48
+ visit(field);
49
+ }
50
+ walk(properties);
51
+ return found;
52
+ }
53
+ function mapReferences(properties, data, visit) {
54
+ function mapField(field, value) {
55
+ if (value === undefined || value === null)
56
+ return value;
57
+ if (isBlueprintList(field)) {
58
+ return Array.isArray(value) ? Object.freeze(value.map((item) => mapField(field.item, item))) : value;
59
+ }
60
+ if (isBlueprint(field)) {
61
+ return isPlain(value) ? mapCollection(field.properties, value) : value;
62
+ }
63
+ if (isBlueprintVariant(field)) {
64
+ const variant = namedVariantOf(field, value);
65
+ if (!variant || !isPlain(value))
66
+ return value;
67
+ if (isEntityClass(variant)) {
68
+ const mapped = visit(variant, value.id);
69
+ return mapped === value.id ? value : mapped;
70
+ }
71
+ return mapCollection(blueprintOfVariant(variant).properties, value);
72
+ }
73
+ const record = recordClassOf(field);
74
+ if (!record)
75
+ return value;
76
+ if (isEntityClass(record))
77
+ return visit(record, value);
78
+ return isPlain(value) ? mapCollection(record.blueprint.properties, value) : value;
79
+ }
80
+ function mapCollection(properties, value) {
81
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [
82
+ key,
83
+ mapField(properties[key], item)
84
+ ])));
85
+ }
86
+ return mapCollection(properties, data);
87
+ }
88
+ export function relationsOf(name, entity, relations) {
89
+ const invalid = (reason) => {
90
+ throw new InvalidRepositoryDeclaration({
91
+ source: name,
92
+ details: { reason }
93
+ });
94
+ };
95
+ if (relations === undefined)
96
+ relations = [];
97
+ if (!Array.isArray(relations)) {
98
+ return invalid("Expected relations to be a list of repositories.");
99
+ }
100
+ const related = [];
101
+ for (const relation of relations) {
102
+ const state = findState(relation);
103
+ if (!state)
104
+ return invalid("Expected relations made by repositoryOf.");
105
+ if (related.some((other) => other.entity === state.entity)) {
106
+ return invalid(`Relation "${recordNameOf(state.entity)}" is duplicated.`);
107
+ }
108
+ if (typeof state.provider.findMany !== "function") {
109
+ return invalid(`The provider of "${state.name}" has no "findMany" to load it.`);
110
+ }
111
+ related.push(state);
112
+ }
113
+ const used = new Set;
114
+ const map = new Map;
115
+ for (const target of entityClassesOf(entity.blueprint.properties)) {
116
+ const matches = related.filter((state) => state.entity === target || state.entity.prototype instanceof target);
117
+ const exact = matches.find((state) => state.entity === target);
118
+ if (!exact && matches.length === 0) {
119
+ return invalid(`Missing relation: no repository of "${recordNameOf(target)}".`);
120
+ }
121
+ if (!exact && matches.length > 1) {
122
+ return invalid(`Relation "${recordNameOf(target)}" matches more than one repository.`);
123
+ }
124
+ const state = exact ?? matches[0];
125
+ map.set(target, state);
126
+ used.add(state);
127
+ }
128
+ for (const state of related) {
129
+ if (!used.has(state)) {
130
+ return invalid(`Relation "${recordNameOf(state.entity)}" is not held by ${recordNameOf(entity)}.`);
131
+ }
132
+ }
133
+ return map;
134
+ }
135
+ export function parseEntity(state, data) {
136
+ const parsed = state.entity.safeParse(data);
137
+ if (parsed.issues) {
138
+ throw new InvalidRepositoryData({
139
+ source: state.name,
140
+ cause: parsed.issues.find((issue) => issue.cause)?.cause
141
+ });
142
+ }
143
+ return parsed.value;
144
+ }
145
+ function loadByIds(state, ids, scope) {
146
+ return scopedProvider(scope, state.provider).findMany(contextOf(state), {
147
+ where: { id: { in: ids } },
148
+ withDeleted: true,
149
+ withExpired: true
150
+ });
151
+ }
152
+ export async function resolveReferences(state, datas, session, scope) {
153
+ if (state.relations.size === 0)
154
+ return datas;
155
+ const { properties } = state.entity.blueprint;
156
+ const wanted = new Map;
157
+ for (const data of datas) {
158
+ mapReferences(properties, data, (target, value) => {
159
+ const relation = state.relations.get(target);
160
+ if (relation && typeof value === "string") {
161
+ const ids = wanted.get(relation) ?? new Set;
162
+ wanted.set(relation, ids.add(value));
163
+ }
164
+ return value;
165
+ });
166
+ }
167
+ for (const [relation, ids] of wanted) {
168
+ const loaded = session.get(relation) ?? new Map;
169
+ session.set(relation, loaded);
170
+ const missing = [...ids].filter((id) => !loaded.has(id));
171
+ if (missing.length === 0)
172
+ continue;
173
+ const found = upcastRows(relation, await loadByIds(relation, missing, scope));
174
+ const resolved = await resolveReferences(relation, found, session, scope);
175
+ for (const data of resolved) {
176
+ const id = data.id;
177
+ if (!loaded.has(id))
178
+ loaded.set(id, parseEntity(relation, data));
179
+ }
180
+ for (const id of missing) {
181
+ if (loaded.has(id))
182
+ continue;
183
+ throw new InvalidRepositoryData({
184
+ source: state.name,
185
+ cause: new RepositoryEntityNotFound({
186
+ source: relation.name,
187
+ details: { id }
188
+ })
189
+ });
190
+ }
191
+ }
192
+ return datas.map((data) => mapReferences(properties, data, (target, value) => {
193
+ const relation = state.relations.get(target);
194
+ return relation && typeof value === "string" ? session.get(relation)?.get(value) ?? value : value;
195
+ }));
196
+ }
@@ -80,9 +80,29 @@ export declare abstract class AbstractRepository<Entity extends AnyRepositoryEnt
80
80
  * `create`, `update`, `delete`, `restore` (soft deletion only), `increment`, `count`, and the queries `findMany`,
81
81
  * `exists`, `findPage` and `findCursor`, and `forEachPage`, which walks the
82
82
  * pages of a cursor query, resumably. Every method is async and reads and writes through `provider`
83
- * (a new `inMemoryProvider()` by default), which gets the variables of
83
+ * (a new {@link inMemoryProvider} by default), which gets the variables of
84
84
  * `environmentDependencies` as `context.env`. The repository can be a
85
- * dependency of a use case, and `RepositoryOf` extends it with a class.
85
+ * dependency of a use case, and {@link RepositoryOf} extends it with a class.
86
+ *
87
+ * @throws {@link InvalidRepositoryDeclaration} when `entity` is not an entity class made by `entityOf`, or a setting (`actions`, `deletion`, `scans`, `indexes`...) is not valid.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * import { blueprintOf, entityOf, repositoryOf } from "@afora/core";
92
+ *
93
+ * class User extends entityOf(
94
+ * blueprintOf((ctx) => ({ name: ctx.string, email: ctx.email })),
95
+ * { unique: ["email"] },
96
+ * ) {}
97
+ *
98
+ * const Users = repositoryOf({
99
+ * entity: User,
100
+ * actions: ["findByEmail", "create"],
101
+ * });
102
+ *
103
+ * await Users.create({ name: "Ana", email: "ana@example.com" });
104
+ * const ana = await Users.findByEmail("ana@example.com"); // User | undefined
105
+ * ```
86
106
  */
87
107
  export declare function repositoryOf<Entity extends AnyRepositoryEntity, const Actions extends readonly RepositoryAction<Entity>[], const Environment extends EnvironmentDependencies | undefined = undefined, const Capabilities extends ProviderCapabilities = {
88
108
  readonly keyset: true;