@sincpro/mobile 0.4.4 → 1.0.0

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 (165) hide show
  1. package/README.md +3 -3
  2. package/dist/adapters/JsonSerializer.adapter.d.ts +1 -1
  3. package/dist/adapters/JsonSerializer.adapter.js +2 -2
  4. package/dist/adapters/repositories/database_table.repository.d.ts +3 -2
  5. package/dist/adapters/repositories/database_table.repository.js +1 -1
  6. package/dist/adapters/repositories/document.repository.d.ts +81 -0
  7. package/dist/adapters/repositories/document.repository.js +653 -0
  8. package/dist/adapters/repositories/domain_event.repository.d.ts +17 -17
  9. package/dist/adapters/repositories/domain_event.repository.js +25 -31
  10. package/dist/adapters/repositories/domain_event_dead_letter.repository.d.ts +10 -10
  11. package/dist/adapters/repositories/domain_event_dead_letter.repository.js +14 -13
  12. package/dist/adapters/repositories/event_row.d.ts +13 -0
  13. package/dist/adapters/repositories/event_row.js +22 -0
  14. package/dist/adapters/repositories/sqlite_document_store.d.ts +33 -0
  15. package/dist/adapters/repositories/sqlite_document_store.js +201 -0
  16. package/dist/domain/connectivity/events.d.ts +5 -5
  17. package/dist/domain/connectivity/events.js +3 -3
  18. package/dist/domain/database/criteria.d.ts +19 -0
  19. package/dist/domain/database/criteria.js +36 -0
  20. package/dist/domain/database/criteria_clock.d.ts +8 -0
  21. package/dist/domain/database/criteria_clock.js +305 -0
  22. package/dist/domain/database/criteria_sql.d.ts +61 -0
  23. package/dist/domain/database/criteria_sql.js +476 -0
  24. package/dist/domain/database/database.d.ts +1 -0
  25. package/dist/domain/database/index.d.ts +3 -1
  26. package/dist/domain/database/index.js +3 -1
  27. package/dist/domain/database/migration_plan.d.ts +14 -0
  28. package/dist/domain/database/migration_plan.js +44 -0
  29. package/dist/domain/entity/change_tracking.d.ts +21 -0
  30. package/dist/domain/entity/change_tracking.js +71 -0
  31. package/dist/domain/entity/checks.d.ts +8 -0
  32. package/dist/domain/entity/checks.js +82 -0
  33. package/dist/domain/entity/entity.d.ts +39 -32
  34. package/dist/domain/entity/entity.js +94 -105
  35. package/dist/domain/entity/entity_collection.d.ts +15 -30
  36. package/dist/domain/entity/entity_collection.js +30 -121
  37. package/dist/domain/entity/index.d.ts +5 -0
  38. package/dist/domain/entity/index.js +5 -0
  39. package/dist/domain/entity/reconcile.d.ts +7 -0
  40. package/dist/domain/entity/reconcile.js +12 -0
  41. package/dist/domain/entity/relations.d.ts +27 -0
  42. package/dist/domain/entity/relations.js +69 -0
  43. package/dist/domain/entity/remote_sync.d.ts +4 -0
  44. package/dist/domain/entity/remote_sync.js +19 -0
  45. package/dist/domain/entity/values.d.ts +6 -0
  46. package/dist/domain/entity/values.js +13 -0
  47. package/dist/domain/events/catalog.d.ts +29 -0
  48. package/dist/domain/{events.js → events/catalog.js} +7 -11
  49. package/dist/domain/events/chain.d.ts +5 -0
  50. package/dist/domain/events/chain.js +6 -0
  51. package/dist/domain/{event_sourcing → events}/domain_event.d.ts +20 -22
  52. package/dist/domain/events/domain_event.js +227 -0
  53. package/dist/domain/events/index.d.ts +8 -0
  54. package/dist/domain/events/index.js +8 -0
  55. package/dist/domain/events/integration_event.d.ts +103 -0
  56. package/dist/domain/events/integration_event.js +72 -0
  57. package/dist/domain/events/label.d.ts +2 -0
  58. package/dist/domain/events/label.js +26 -0
  59. package/dist/domain/index.d.ts +0 -1
  60. package/dist/domain/index.js +0 -1
  61. package/dist/domain/print/events.d.ts +8 -4
  62. package/dist/domain/print/events.js +9 -4
  63. package/dist/domain/repositories/document_store.d.ts +33 -0
  64. package/dist/domain/repositories/document_store.js +5 -0
  65. package/dist/domain/repositories/hooks.d.ts +23 -0
  66. package/dist/domain/repositories/hooks.js +32 -0
  67. package/dist/domain/repositories/index.d.ts +4 -0
  68. package/dist/domain/repositories/index.js +4 -0
  69. package/dist/domain/{database → repositories}/repository.d.ts +9 -3
  70. package/dist/domain/settings.d.ts +4 -1
  71. package/dist/domain/settings.js +3 -0
  72. package/dist/domain/webview/events.d.ts +2 -2
  73. package/dist/domain/webview/events.js +2 -2
  74. package/dist/entrypoints/db/migrations.js +10 -10
  75. package/dist/entrypoints/queue/QueueProcessor.d.ts +1 -1
  76. package/dist/entrypoints/queue/activateDomain.subscriber.d.ts +2 -2
  77. package/dist/entrypoints/queue/activateDomain.subscriber.js +2 -2
  78. package/dist/entrypoints/queue/newAppSettings.handler.d.ts +2 -2
  79. package/dist/entrypoints/queue/newAppSettings.handler.js +1 -1
  80. package/dist/entrypoints/queue/printImage.subscriber.d.ts +1 -1
  81. package/dist/entrypoints/queue/printImage.subscriber.js +2 -2
  82. package/dist/entrypoints/queue/processWebViewMessage.subscriber.d.ts +1 -1
  83. package/dist/entrypoints/queue/processWebViewMessage.subscriber.js +1 -1
  84. package/dist/entrypoints/ui/AppShell.js +2 -2
  85. package/dist/entrypoints/ui/common_provider.d.ts +0 -12
  86. package/dist/entrypoints/ui/common_provider.js +2 -131
  87. package/dist/event_driven/index.d.ts +1 -0
  88. package/dist/event_driven/index.js +1 -0
  89. package/dist/{domain/event_sourcing/event_handler.d.ts → event_driven/subscriber.d.ts} +1 -1
  90. package/dist/exceptions.d.ts +21 -0
  91. package/dist/exceptions.js +30 -0
  92. package/dist/framework/base_module.d.ts +1 -1
  93. package/dist/framework/createApp.d.ts +3 -3
  94. package/dist/framework/domain_module.d.ts +1 -1
  95. package/dist/framework/kernel.d.ts +1 -1
  96. package/dist/framework/kernel.js +17 -8
  97. package/dist/infrastructure/database/connector.d.ts +2 -0
  98. package/dist/infrastructure/database/connector.js +22 -0
  99. package/dist/infrastructure/database/index.d.ts +1 -0
  100. package/dist/infrastructure/database/index.js +1 -0
  101. package/dist/infrastructure/database/schema.d.ts +9 -0
  102. package/dist/infrastructure/database/schema.js +61 -0
  103. package/dist/infrastructure/database/utils.d.ts +2 -0
  104. package/dist/infrastructure/database/utils.js +12 -4
  105. package/dist/infrastructure/event_chain.d.ts +6 -0
  106. package/dist/infrastructure/event_chain.js +25 -0
  107. package/dist/infrastructure/telemetry/tracing/tracing_hooks.js +8 -5
  108. package/dist/infrastructure/ui/UIEventBus.d.ts +3 -1
  109. package/dist/infrastructure/ui/UIEventBus.js +8 -1
  110. package/dist/infrastructure/ui/activity.d.ts +37 -0
  111. package/dist/infrastructure/ui/activity.js +84 -0
  112. package/dist/infrastructure/workers/CronWorker.d.ts +6 -4
  113. package/dist/infrastructure/workers/CronWorker.js +24 -18
  114. package/dist/infrastructure/workers/EventBus.d.ts +9 -5
  115. package/dist/infrastructure/workers/EventBus.js +68 -33
  116. package/dist/services/bluetooth.service.js +16 -14
  117. package/dist/services/dead_letter_queue.service.d.ts +7 -7
  118. package/dist/services/dead_letter_queue.service.js +0 -2
  119. package/dist/services/event.service.d.ts +3 -3
  120. package/dist/services/event.service.js +1 -1
  121. package/dist/services/network.service.d.ts +1 -1
  122. package/dist/services/network.service.js +17 -15
  123. package/dist/services/printer.service.d.ts +1 -1
  124. package/dist/services/printer.service.js +50 -44
  125. package/dist/tools/utils/date.js +4 -3
  126. package/dist/tools/utils/monetary.d.ts +6 -3
  127. package/dist/tools/utils/monetary.js +33 -21
  128. package/dist/tools/utils/serializer.d.ts +1 -1
  129. package/dist/tools/utils/serializer.js +32 -35
  130. package/dist/ui/activity/DeviceNotices.d.ts +1 -0
  131. package/dist/ui/activity/DeviceNotices.js +29 -0
  132. package/dist/ui/activity/ProcessToast.d.ts +2 -0
  133. package/dist/ui/activity/ProcessToast.js +71 -0
  134. package/dist/ui/activity/index.d.ts +4 -0
  135. package/dist/ui/activity/index.js +4 -0
  136. package/dist/ui/activity/useActivity.d.ts +9 -0
  137. package/dist/ui/activity/useActivity.js +48 -0
  138. package/dist/ui/components/molecules/EventTimelineItem.d.ts +2 -2
  139. package/dist/ui/components/molecules/EventTimelineItem.js +1 -1
  140. package/dist/ui/components/molecules/index.d.ts +1 -2
  141. package/dist/ui/components/molecules/index.js +1 -2
  142. package/dist/ui/components/organisms/DeadLetterQueueRow.d.ts +3 -3
  143. package/dist/ui/components/organisms/EventRow.d.ts +3 -3
  144. package/dist/ui/components/organisms/EventRow.js +1 -1
  145. package/dist/ui/screens/dead_letter_queue/dead_letter_queue.context.d.ts +5 -5
  146. package/dist/ui/screens/events/events.context.d.ts +4 -4
  147. package/package.json +13 -8
  148. package/dist/domain/event_sourcing/domain_event.js +0 -164
  149. package/dist/domain/event_sourcing/event.d.ts +0 -25
  150. package/dist/domain/event_sourcing/event.js +0 -25
  151. package/dist/domain/event_sourcing/index.d.ts +0 -2
  152. package/dist/domain/event_sourcing/index.js +0 -2
  153. package/dist/domain/events.d.ts +0 -33
  154. package/dist/infrastructure/ui/ToastHost.d.ts +0 -6
  155. package/dist/infrastructure/ui/ToastHost.js +0 -28
  156. package/dist/infrastructure/ui/events.d.ts +0 -1
  157. package/dist/infrastructure/ui/events.js +0 -1
  158. package/dist/ui/components/molecules/ProcessToast.d.ts +0 -8
  159. package/dist/ui/components/molecules/ProcessToast.js +0 -52
  160. /package/dist/domain/{repositories.d.ts → repositories/catalog.d.ts} +0 -0
  161. /package/dist/domain/{repositories.js → repositories/catalog.js} +0 -0
  162. /package/dist/domain/{database → repositories}/repository.js +0 -0
  163. /package/dist/{domain/event_sourcing/event_handler.js → event_driven/subscriber.js} +0 -0
  164. /package/dist/ui/{components/molecules → activity}/ProcessToastProvider.d.ts +0 -0
  165. /package/dist/ui/{components/molecules → activity}/ProcessToastProvider.js +0 -0
@@ -0,0 +1,653 @@
1
+ import { compileCriteria, } from "../../domain/database/criteria_sql";
2
+ import { isChangeTracked } from "../../domain/entity/change_tracking";
3
+ import { assertChecks } from "../../domain/entity/checks";
4
+ import { ERemoteState, RemoteEntity } from "../../domain/entity/entity";
5
+ import { EntityCollection } from "../../domain/entity/entity_collection";
6
+ import { reconcile } from "../../domain/entity/reconcile";
7
+ import { heldEntities, relationPersists, relationRemoves, relations, relationWrites, } from "../../domain/entity/relations";
8
+ import { applyAcknowledgement, takeAcceptedWrite, } from "../../domain/entity/remote_sync";
9
+ import { StoreMode, } from "../../domain/repositories/document_store";
10
+ import { fireHooks, fireSearch, } from "../../domain/repositories/hooks";
11
+ import { DomainAppError, StaleAggregate } from "../../exceptions";
12
+ import { EventBus } from "../../infrastructure/workers";
13
+ import { DomainEventRepository } from "./domain_event.repository";
14
+ import { SqliteDocumentStore } from "./sqlite_document_store";
15
+ const registry = new Map();
16
+ const hookRepos = new Set();
17
+ export class DocumentRepository {
18
+ type;
19
+ store;
20
+ options;
21
+ inflight = new Map();
22
+ constructor(type, store, options = {}) {
23
+ this.type = type;
24
+ this.store = store;
25
+ this.options = options;
26
+ if (registry.has(type)) {
27
+ throw new DomainAppError(`document repository already registered for ${type.name}`);
28
+ }
29
+ registry.set(type, this);
30
+ }
31
+ static reset() {
32
+ registry.clear();
33
+ hookRepos.clear();
34
+ }
35
+ async save(entity, options) {
36
+ await this.commit(options, async (session) => {
37
+ for (const item of toItems(entity))
38
+ await this.persistIn(item, "save", session);
39
+ });
40
+ }
41
+ async upsert(entity) {
42
+ await this.commit(undefined, async (session) => {
43
+ for (const item of toItems(entity))
44
+ await this.persistIn(item, "upsert", session);
45
+ });
46
+ }
47
+ async remove(entity) {
48
+ const items = Array.isArray(entity) ? entity : [entity];
49
+ await this.commit({ deliver: false }, async (session) => {
50
+ for (const item of items)
51
+ await this.deleteIn(item, session);
52
+ });
53
+ }
54
+ async removeById(uuid) {
55
+ await this.commit({ deliver: false }, async (session) => {
56
+ await this.deleteById(session, uuid);
57
+ });
58
+ }
59
+ async publish(events) {
60
+ this.assertCanWrite();
61
+ const outbox = this.outbox();
62
+ await this.store.transaction(async () => {
63
+ for (const event of events)
64
+ await outbox.save(event);
65
+ });
66
+ outbox.schedule();
67
+ }
68
+ kick() {
69
+ this.options.outbox?.schedule();
70
+ }
71
+ async findById(uuid) {
72
+ return this.loadById(uuid);
73
+ }
74
+ findByIdSync(uuid) {
75
+ return this.loadByIdSync(uuid);
76
+ }
77
+ async findByIds(uuids) {
78
+ const found = [];
79
+ for (const uuid of uuids) {
80
+ const one = await this.loadById(uuid);
81
+ if (one)
82
+ found.push(one);
83
+ }
84
+ await this.afterSearch(found);
85
+ return new EntityCollection(found);
86
+ }
87
+ findByIdsSync(uuids) {
88
+ const found = [];
89
+ for (const uuid of uuids) {
90
+ const one = this.loadByIdSync(uuid);
91
+ if (one)
92
+ found.push(one);
93
+ }
94
+ return new EntityCollection(found);
95
+ }
96
+ async findAll() {
97
+ const found = await this.readAll();
98
+ await this.afterSearch(found);
99
+ return new EntityCollection(found);
100
+ }
101
+ async findByCriteria(criteria) {
102
+ const rows = await this.store.selectMatching(compileCriteria(criteria, this.sqlTarget()));
103
+ const found = [];
104
+ for (const row of rows)
105
+ found.push(await this.hydrate(row));
106
+ await this.afterSearch(found);
107
+ return new EntityCollection(found);
108
+ }
109
+ async findByRemoteId(remoteId) {
110
+ const found = await this.findByColumn("remoteId", remoteId);
111
+ return found[0] ?? null;
112
+ }
113
+ findByRemoteIdSync(remoteId) {
114
+ const select = this.store.selectBySync;
115
+ if (!select || !this.store.hasColumn("remoteId")) {
116
+ throw new DomainAppError("this document store cannot query remoteId synchronously");
117
+ }
118
+ const row = select.call(this.store, "remoteId", remoteId)[0];
119
+ return row ? this.hydrateSync(row) : null;
120
+ }
121
+ async findByRemoteIds(remoteIds) {
122
+ const found = [];
123
+ for (const remoteId of remoteIds)
124
+ found.push(...(await this.findByColumn("remoteId", remoteId)));
125
+ await this.afterSearch(found);
126
+ return new EntityCollection(found);
127
+ }
128
+ async findByRemoteState(state) {
129
+ const states = Array.isArray(state) ? state : [state];
130
+ const found = [];
131
+ for (const one of states)
132
+ found.push(...(await this.findByColumn("remoteState", one)));
133
+ await this.afterSearch(found);
134
+ return new EntityCollection(found);
135
+ }
136
+ async findDirty() {
137
+ if (!this.store.hasColumn("ackedVersion")) {
138
+ const found = (await this.readAll()).filter((entity) => entity instanceof RemoteEntity && entity.isDirty);
139
+ await this.afterSearch(found);
140
+ return new EntityCollection(found);
141
+ }
142
+ return this.findByCriteria({
143
+ selection: {
144
+ field: "version",
145
+ operator: "!=",
146
+ value: { $: "field", name: "ackedVersion" },
147
+ },
148
+ });
149
+ }
150
+ async acknowledge(entity, remoteId) {
151
+ if (!(entity instanceof RemoteEntity)) {
152
+ throw new DomainAppError("only a remote document can be acknowledged");
153
+ }
154
+ if (entity.version === 0) {
155
+ throw new DomainAppError("acknowledge a document after it has been saved");
156
+ }
157
+ const existing = await this.store.read(entity.uuid);
158
+ if (!existing || storedVersion(existing) !== entity.version) {
159
+ throw new StaleAggregate(`${entity.constructor.name} ${entity.uuid} is at version ${existing ? storedVersion(existing) : "none"}, not ${entity.version}`);
160
+ }
161
+ applyAcknowledgement(entity, remoteId);
162
+ const row = {
163
+ uuid: entity.uuid,
164
+ version: entity.version,
165
+ data: documentData(entity),
166
+ columns: this.columnValues(entity),
167
+ };
168
+ const written = await this.store.update(row, entity.version);
169
+ if (!written) {
170
+ throw new StaleAggregate(`${entity.constructor.name} ${entity.uuid} changed while it was acknowledged`);
171
+ }
172
+ if (isChangeTracked(entity))
173
+ entity.takeSnapshot();
174
+ }
175
+ async pull(local, incoming) {
176
+ if (!(incoming instanceof RemoteEntity) || (local && !(local instanceof RemoteEntity))) {
177
+ throw new DomainAppError("only a remote document can be pulled");
178
+ }
179
+ const decision = reconcile(local, incoming);
180
+ if (decision.write !== "keep")
181
+ await this.save(decision.entity);
182
+ return decision.entity;
183
+ }
184
+ async commit(options, work) {
185
+ this.assertCanWrite();
186
+ let session = null;
187
+ try {
188
+ await this.store.transaction(async () => {
189
+ session = this.openSession(options);
190
+ await work(session);
191
+ });
192
+ this.schedule(session);
193
+ }
194
+ catch (error) {
195
+ if (session)
196
+ restoreSession(session);
197
+ throw error;
198
+ }
199
+ }
200
+ assertCanWrite() {
201
+ const self = this;
202
+ if (hookRepos.has(self) || (hookRepos.size > 0 && this.store.inTransaction())) {
203
+ throw new DomainAppError("a hook cannot write through the same repository");
204
+ }
205
+ }
206
+ async persistIn(entity, kind, session) {
207
+ if (session.saving.has(entity.uuid))
208
+ return;
209
+ session.saving.add(entity.uuid);
210
+ remember(session, entity);
211
+ const hooks = this.hooksOf(entity);
212
+ const creating = entity.isNew;
213
+ if (kind === "save") {
214
+ await this.runHooks(hooks, "before_save", entity);
215
+ await this.runHooks(hooks, creating ? "before_create" : "before_update", entity);
216
+ assertChecks(entity);
217
+ await this.cascade(entity, kind, session);
218
+ if (isChangeTracked(entity))
219
+ entity.recordChanges();
220
+ }
221
+ else {
222
+ await this.runHooks(hooks, "before_save", entity);
223
+ await this.cascade(entity, kind, session);
224
+ }
225
+ await this.writeRow(entity, kind === "upsert");
226
+ await this.flush(entity, session);
227
+ entity.noteHeld();
228
+ if (isChangeTracked(entity))
229
+ entity.takeSnapshot();
230
+ if (kind === "save") {
231
+ await this.runHooks(hooks, creating ? "after_create" : "after_update", entity);
232
+ }
233
+ await this.runHooks(hooks, "after_save", entity);
234
+ }
235
+ async cascade(entity, kind, session) {
236
+ for (const relation of relations(entity)) {
237
+ const current = heldEntities(fieldOf(entity, relation.field));
238
+ const writes = relationWrites(relation);
239
+ const removes = relationRemoves(relation);
240
+ if (!writes && !removes)
241
+ continue;
242
+ const repo = this.repositoryFor(relation.type);
243
+ if (removes) {
244
+ const seen = new Set(current.map((child) => child.uuid));
245
+ for (const previous of entity.heldIds(relation.field)) {
246
+ if (!seen.has(previous))
247
+ await repo.deleteById(session, previous);
248
+ }
249
+ }
250
+ if (!writes)
251
+ continue;
252
+ for (const child of current) {
253
+ if (relationPersists(relation, child.isNew)) {
254
+ await repo.persistIn(child, kind, session);
255
+ }
256
+ }
257
+ }
258
+ }
259
+ async deleteIn(entity, session) {
260
+ if (session.saving.has(entity.uuid))
261
+ return;
262
+ session.saving.add(entity.uuid);
263
+ remember(session, entity);
264
+ const hooks = this.hooksOf(entity);
265
+ await this.runHooks(hooks, "before_remove", entity);
266
+ for (const relation of relations(entity)) {
267
+ if (!relationRemoves(relation))
268
+ continue;
269
+ const repo = this.repositoryFor(relation.type);
270
+ for (const child of heldEntities(fieldOf(entity, relation.field))) {
271
+ await repo.deleteIn(child, session);
272
+ }
273
+ }
274
+ await this.store.remove(entity.uuid);
275
+ await this.runHooks(hooks, "after_remove", entity);
276
+ }
277
+ async deleteById(session, uuid) {
278
+ const found = await this.loadById(uuid);
279
+ if (found)
280
+ await this.deleteIn(found, session);
281
+ }
282
+ async writeRow(entity, upsert) {
283
+ const existing = await this.store.read(entity.uuid);
284
+ const previous = existing ? storedVersion(existing) : null;
285
+ const expected = previous === null ? 0 : previous;
286
+ if (!upsert && expected !== entity.version) {
287
+ throw new StaleAggregate(`${entity.constructor.name} ${entity.uuid} is at version ${previous ?? "none"}, not ${entity.version}`);
288
+ }
289
+ entity.version = (previous ?? 0) + 1;
290
+ if (entity instanceof RemoteEntity) {
291
+ if (takeAcceptedWrite(entity))
292
+ entity.ackedVersion = entity.version;
293
+ entity.remoteState = entity.isDirty ? ERemoteState.PENDING : ERemoteState.SYNCED;
294
+ }
295
+ entity.updatedAt = new Date().toISOString();
296
+ if (!entity.createdAt)
297
+ entity.createdAt = entity.updatedAt;
298
+ const row = {
299
+ uuid: entity.uuid,
300
+ version: entity.version,
301
+ data: documentData(entity),
302
+ columns: this.columnValues(entity),
303
+ };
304
+ if (upsert) {
305
+ await this.store.upsert(row);
306
+ return;
307
+ }
308
+ if (previous === null) {
309
+ try {
310
+ await this.store.insert(row);
311
+ }
312
+ catch (error) {
313
+ const raced = await this.store.read(entity.uuid);
314
+ if (raced) {
315
+ throw new StaleAggregate(`${entity.constructor.name} ${entity.uuid} is at version ${storedVersion(raced)}, not 0`);
316
+ }
317
+ throw error;
318
+ }
319
+ return;
320
+ }
321
+ const written = await this.store.update(row, expected);
322
+ if (!written) {
323
+ const raced = await this.store.read(entity.uuid);
324
+ const current = raced ? storedVersion(raced) : "none";
325
+ throw new StaleAggregate(`${entity.constructor.name} ${entity.uuid} is at version ${current}, not ${expected}`);
326
+ }
327
+ }
328
+ columnValues(entity) {
329
+ const columns = { ...(this.options.columns?.(entity) ?? {}) };
330
+ if (!(entity instanceof RemoteEntity))
331
+ return columns;
332
+ if (this.store.hasColumn("remoteId") && columns.remoteId === undefined) {
333
+ columns.remoteId = entity.remoteId ?? null;
334
+ }
335
+ if (this.store.hasColumn("remoteState") && columns.remoteState === undefined) {
336
+ columns.remoteState = entity.remoteState;
337
+ }
338
+ if (this.store.hasColumn("ackedVersion") && columns.ackedVersion === undefined) {
339
+ columns.ackedVersion = entity.ackedVersion;
340
+ }
341
+ return columns;
342
+ }
343
+ async flush(entity, session) {
344
+ const events = entity.releaseDeliverable();
345
+ if (events.length === 0)
346
+ return;
347
+ const outbox = this.options.outbox;
348
+ if (!outbox) {
349
+ if ((this.options.mode ?? StoreMode.LOCAL) === StoreMode.RELAY) {
350
+ throw new DomainAppError("a relay repository needs an outbox");
351
+ }
352
+ entity.restoreRecorded(events);
353
+ return;
354
+ }
355
+ for (const event of events)
356
+ await outbox.save(event);
357
+ if (session.deliver)
358
+ session.outboxes.add(outbox);
359
+ }
360
+ async loadById(uuid) {
361
+ const pending = this.inflight.get(uuid);
362
+ if (pending)
363
+ return pending;
364
+ const row = await this.store.read(uuid);
365
+ return row ? this.hydrate(row) : null;
366
+ }
367
+ loadByIdSync(uuid) {
368
+ const pending = this.inflight.get(uuid);
369
+ if (pending)
370
+ return pending;
371
+ const readSync = this.store.readSync;
372
+ if (!readSync) {
373
+ throw new DomainAppError("this document store cannot read synchronously");
374
+ }
375
+ const row = readSync.call(this.store, uuid);
376
+ return row ? this.hydrateSync(row) : null;
377
+ }
378
+ async hydrate(row) {
379
+ const pending = this.inflight.get(row.uuid);
380
+ if (pending)
381
+ return pending;
382
+ const entity = this.materialize(row);
383
+ this.inflight.set(entity.uuid, entity);
384
+ try {
385
+ await this.attachRelations(entity);
386
+ entity.noteHeld();
387
+ await this.runHooks(this.hooksOf(entity), "after_read", entity);
388
+ if (isChangeTracked(entity))
389
+ entity.takeSnapshot();
390
+ return entity;
391
+ }
392
+ finally {
393
+ this.inflight.delete(entity.uuid);
394
+ }
395
+ }
396
+ hydrateSync(row) {
397
+ const pending = this.inflight.get(row.uuid);
398
+ if (pending)
399
+ return pending;
400
+ const entity = this.materialize(row);
401
+ this.inflight.set(entity.uuid, entity);
402
+ try {
403
+ this.attachRelationsSync(entity);
404
+ entity.noteHeld();
405
+ if (isChangeTracked(entity))
406
+ entity.takeSnapshot();
407
+ return entity;
408
+ }
409
+ finally {
410
+ this.inflight.delete(entity.uuid);
411
+ }
412
+ }
413
+ materialize(row) {
414
+ const parsed = JSON.parse(row.data);
415
+ const entity = this.type.fromJSON(parsed);
416
+ if (typeof row.version === "number" && !Number.isNaN(row.version)) {
417
+ entity.version = row.version;
418
+ }
419
+ else if (typeof parsed.version !== "number") {
420
+ entity.version = 1;
421
+ }
422
+ if (entity instanceof RemoteEntity) {
423
+ const storedAck = row.columns.ackedVersion;
424
+ if (typeof storedAck === "number" && !Number.isNaN(storedAck)) {
425
+ entity.ackedVersion = storedAck;
426
+ }
427
+ else if (entity.ackedVersion === 0 &&
428
+ entity.remoteState === ERemoteState.SYNCED &&
429
+ entity.version > 0) {
430
+ entity.ackedVersion = entity.version;
431
+ }
432
+ }
433
+ return entity;
434
+ }
435
+ async attachRelations(entity) {
436
+ const record = entity;
437
+ for (const relation of relations(entity)) {
438
+ record[relation.field] = await this.resolveRelation(relation, record[relation.field]);
439
+ }
440
+ }
441
+ attachRelationsSync(entity) {
442
+ const record = entity;
443
+ for (const relation of relations(entity)) {
444
+ record[relation.field] = this.resolveRelationSync(relation, record[relation.field]);
445
+ }
446
+ }
447
+ async resolveRelation(relation, value) {
448
+ if (Array.isArray(value)) {
449
+ const resolved = [];
450
+ for (const item of value) {
451
+ const one = await this.resolveOne(relation, item);
452
+ if (one != null)
453
+ resolved.push(one);
454
+ }
455
+ return resolved;
456
+ }
457
+ return this.resolveOne(relation, value);
458
+ }
459
+ resolveRelationSync(relation, value) {
460
+ if (Array.isArray(value)) {
461
+ const resolved = [];
462
+ for (const item of value) {
463
+ const one = this.resolveOneSync(relation, item);
464
+ if (one != null)
465
+ resolved.push(one);
466
+ }
467
+ return resolved;
468
+ }
469
+ return this.resolveOneSync(relation, value);
470
+ }
471
+ async resolveOne(relation, value) {
472
+ if (typeof value === "string") {
473
+ return this.repositoryFor(relation.type).loadById(value);
474
+ }
475
+ if (value && typeof value === "object")
476
+ return entityFromJSON(relation, value);
477
+ return value ?? null;
478
+ }
479
+ resolveOneSync(relation, value) {
480
+ if (typeof value === "string") {
481
+ return this.repositoryFor(relation.type).loadByIdSync(value);
482
+ }
483
+ if (value && typeof value === "object")
484
+ return entityFromJSON(relation, value);
485
+ return value ?? null;
486
+ }
487
+ async findByColumn(column, value) {
488
+ if (this.store.hasColumn(column)) {
489
+ const rows = await this.store.selectBy(column, value);
490
+ const found = [];
491
+ for (const row of rows)
492
+ found.push(await this.hydrate(row));
493
+ return found;
494
+ }
495
+ const found = [];
496
+ for (const item of await this.readAll()) {
497
+ if (item[column] === value)
498
+ found.push(item);
499
+ }
500
+ return found;
501
+ }
502
+ async readAll() {
503
+ const rows = await this.store.list();
504
+ const found = [];
505
+ for (const row of rows)
506
+ found.push(await this.hydrate(row));
507
+ return found;
508
+ }
509
+ async afterSearch(records) {
510
+ await fireSearch(this.options.hooks?.ofType(this.type) ?? [], records);
511
+ }
512
+ async runHooks(hooks, moment, entity) {
513
+ if (hooks.length === 0)
514
+ return;
515
+ const self = this;
516
+ hookRepos.add(self);
517
+ try {
518
+ await fireHooks(hooks, moment, entity);
519
+ }
520
+ finally {
521
+ hookRepos.delete(self);
522
+ }
523
+ }
524
+ hooksOf(entity) {
525
+ return this.options.hooks?.of(entity) ?? [];
526
+ }
527
+ sqlTarget() {
528
+ return {
529
+ table: this.store.table,
530
+ column: (field) => field === "uuid" || field === "version" || this.store.hasColumn(field),
531
+ relation: (name) => {
532
+ const declared = relations(Object.create(this.type.prototype)).find((item) => item.field === name);
533
+ if (!declared)
534
+ return undefined;
535
+ return this.repositoryFor(declared.type).sqlTarget();
536
+ },
537
+ };
538
+ }
539
+ repositoryFor(type) {
540
+ const found = registry.get(type);
541
+ if (!found)
542
+ throw new DomainAppError(`no document repository for ${type.name}`);
543
+ return found;
544
+ }
545
+ openSession(options) {
546
+ const mode = this.options.mode ?? StoreMode.LOCAL;
547
+ return {
548
+ deliver: options?.deliver ?? mode === StoreMode.RELAY,
549
+ outboxes: new Set(),
550
+ saving: new Set(),
551
+ remembered: new Map(),
552
+ };
553
+ }
554
+ schedule(session) {
555
+ if (!session?.deliver)
556
+ return;
557
+ for (const outbox of session.outboxes)
558
+ outbox.schedule();
559
+ }
560
+ outbox() {
561
+ if (!this.options.outbox)
562
+ throw new DomainAppError("publish needs an outbox");
563
+ return this.options.outbox;
564
+ }
565
+ }
566
+ function remember(session, entity) {
567
+ if (session.remembered.has(entity.uuid))
568
+ return;
569
+ session.remembered.set(entity.uuid, {
570
+ entity,
571
+ version: entity.version,
572
+ updatedAt: entity.updatedAt,
573
+ createdAt: entity.createdAt,
574
+ events: [...entity.recordedEvents()],
575
+ eventIds: [...entity.eventIds],
576
+ });
577
+ }
578
+ function restoreSession(active) {
579
+ for (const remembered of active.remembered.values()) {
580
+ remembered.entity.version = remembered.version;
581
+ remembered.entity.updatedAt = remembered.updatedAt;
582
+ remembered.entity.createdAt = remembered.createdAt;
583
+ remembered.entity.eventIds = [...remembered.eventIds];
584
+ remembered.entity.replaceRecorded(remembered.events);
585
+ }
586
+ }
587
+ function storedVersion(row) {
588
+ if (typeof row.version === "number" && !Number.isNaN(row.version))
589
+ return row.version;
590
+ const parsed = JSON.parse(row.data);
591
+ return typeof parsed.version === "number" ? parsed.version : 1;
592
+ }
593
+ function documentData(entity) {
594
+ const parsed = JSON.parse(entity.asJSON());
595
+ const record = entity;
596
+ for (const relation of relations(entity)) {
597
+ const value = record[relation.field];
598
+ if (Array.isArray(value)) {
599
+ parsed[relation.field] = heldEntities(value).map((child) => child.uuid);
600
+ continue;
601
+ }
602
+ const held = heldEntities(value);
603
+ if (held.length === 1)
604
+ parsed[relation.field] = held[0].uuid;
605
+ }
606
+ return JSON.stringify(parsed);
607
+ }
608
+ function fieldOf(entity, field) {
609
+ return entity[field];
610
+ }
611
+ function entityFromJSON(relation, value) {
612
+ const type = relation.type;
613
+ return type.fromJSON(value);
614
+ }
615
+ function toItems(entity) {
616
+ if (Array.isArray(entity))
617
+ return entity;
618
+ if (entity instanceof EntityCollection)
619
+ return entity.toArray();
620
+ return [entity];
621
+ }
622
+ function extendsRemote(type) {
623
+ return type === RemoteEntity || type.prototype instanceof RemoteEntity;
624
+ }
625
+ export function sqliteDocuments(type, table, spec = {}) {
626
+ const columns = { ...(spec.columns ?? {}) };
627
+ if (extendsRemote(type)) {
628
+ columns.remoteId ??= (entity) => entity.remoteId ?? null;
629
+ columns.remoteState ??= (entity) => entity.remoteState ?? null;
630
+ columns.ackedVersion ??= (entity) => entity.ackedVersion ?? 0;
631
+ }
632
+ const names = Object.keys(columns);
633
+ const integers = names.filter((name) => name === "remoteId" || name === "ackedVersion");
634
+ const readColumns = (entity) => {
635
+ const row = {};
636
+ for (const name of names)
637
+ row[name] = columns[name](entity);
638
+ return row;
639
+ };
640
+ const mode = spec.mode ?? StoreMode.LOCAL;
641
+ return new DocumentRepository(type, new SqliteDocumentStore(table, names, integers), {
642
+ mode,
643
+ hooks: spec.hooks,
644
+ outbox: spec.outbox ?? (mode === StoreMode.RELAY ? sqliteOutbox() : undefined),
645
+ columns: names.length > 0 ? readColumns : undefined,
646
+ });
647
+ }
648
+ function sqliteOutbox() {
649
+ return {
650
+ save: (event) => DomainEventRepository.save(event),
651
+ schedule: () => EventBus.schedule(),
652
+ };
653
+ }
@@ -1,32 +1,32 @@
1
- import { DomainEvent, EEventStatus } from "../../domain/event_sourcing";
1
+ import { EEventStatus, type IntegrationEvent } from "../../domain/events";
2
2
  import { ECommonRepository } from "../../domain/repositories";
3
3
  import { DATABASE_TABLES } from "../../entrypoints/db/migrations";
4
4
  declare class DomainEventRepositoryImpl {
5
5
  readonly name = ECommonRepository.DOMAIN_EVENT;
6
6
  readonly table = DATABASE_TABLES.DOMAIN_EVENTS;
7
7
  private hasPendingDuplicate;
8
- save(event: DomainEvent): Promise<void>;
9
- saveMany(events: DomainEvent[]): Promise<void>;
10
- findById(uuid: string): Promise<DomainEvent | null>;
11
- findByIds(uuids: string[]): Promise<DomainEvent[]>;
12
- findNextPending(internetConnected: boolean): Promise<DomainEvent | null>;
13
- findByStatus(status: EEventStatus): Promise<DomainEvent[]>;
14
- findByStatuses(statuses: EEventStatus[]): Promise<DomainEvent[]>;
15
- findByCorrelationId(correlationId: string): Promise<DomainEvent[]>;
16
- findByAggregateId(aggregateId: string): Promise<DomainEvent[]>;
17
- findByName(name: string): Promise<DomainEvent[]>;
18
- findPending(): Promise<DomainEvent[]>;
19
- findFailed(): Promise<DomainEvent[]>;
8
+ save(event: IntegrationEvent): Promise<void>;
9
+ saveMany(events: IntegrationEvent[]): Promise<void>;
10
+ findById(uuid: string): Promise<IntegrationEvent | null>;
11
+ findByIds(uuids: string[]): Promise<IntegrationEvent[]>;
12
+ findNextPending(internetConnected: boolean): Promise<IntegrationEvent | null>;
13
+ findByStatus(status: EEventStatus): Promise<IntegrationEvent[]>;
14
+ findByStatuses(statuses: EEventStatus[]): Promise<IntegrationEvent[]>;
15
+ findByCorrelationId(correlationId: string): Promise<IntegrationEvent[]>;
16
+ findByAggregateId(aggregateId: string): Promise<IntegrationEvent[]>;
17
+ findByName(name: string): Promise<IntegrationEvent[]>;
18
+ findPending(): Promise<IntegrationEvent[]>;
19
+ findFailed(): Promise<IntegrationEvent[]>;
20
20
  remove(uuid: string): Promise<void>;
21
21
  removeByCorrelationId(correlationId: string): Promise<void>;
22
22
  removeAcknowledged(): Promise<void>;
23
23
  clearAll(): Promise<void>;
24
24
  count(): Promise<number>;
25
25
  countByStatus(status: EEventStatus): Promise<number>;
26
- findByIdSync(uuid: string): DomainEvent | null;
27
- findByIdsSync(uuids: string[]): DomainEvent[];
28
- findByRemoteIdSync(remoteId: number | string): DomainEvent | null;
29
- findByRemoteIdsSync(remoteIds: (number | string)[]): DomainEvent[];
26
+ findByIdSync(uuid: string): IntegrationEvent | null;
27
+ findByIdsSync(uuids: string[]): IntegrationEvent[];
28
+ findByRemoteIdSync(remoteId: number | string): IntegrationEvent | null;
29
+ findByRemoteIdsSync(remoteIds: (number | string)[]): IntegrationEvent[];
30
30
  }
31
31
  export declare const DomainEventRepository: DomainEventRepositoryImpl;
32
32
  export {};