@sincpro/mobile 0.4.3 → 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 (197) 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/ReceiptExporter.adapter.js +10 -2
  5. package/dist/adapters/repositories/database_table.repository.d.ts +3 -2
  6. package/dist/adapters/repositories/database_table.repository.js +1 -1
  7. package/dist/adapters/repositories/document.repository.d.ts +81 -0
  8. package/dist/adapters/repositories/document.repository.js +653 -0
  9. package/dist/adapters/repositories/domain_event.repository.d.ts +17 -17
  10. package/dist/adapters/repositories/domain_event.repository.js +25 -31
  11. package/dist/adapters/repositories/domain_event_dead_letter.repository.d.ts +10 -10
  12. package/dist/adapters/repositories/domain_event_dead_letter.repository.js +14 -13
  13. package/dist/adapters/repositories/event_row.d.ts +13 -0
  14. package/dist/adapters/repositories/event_row.js +22 -0
  15. package/dist/adapters/repositories/sqlite_document_store.d.ts +33 -0
  16. package/dist/adapters/repositories/sqlite_document_store.js +201 -0
  17. package/dist/domain/connectivity/events.d.ts +5 -5
  18. package/dist/domain/connectivity/events.js +3 -3
  19. package/dist/domain/database/criteria.d.ts +19 -0
  20. package/dist/domain/database/criteria.js +36 -0
  21. package/dist/domain/database/criteria_clock.d.ts +8 -0
  22. package/dist/domain/database/criteria_clock.js +305 -0
  23. package/dist/domain/database/criteria_sql.d.ts +61 -0
  24. package/dist/domain/database/criteria_sql.js +476 -0
  25. package/dist/domain/database/database.d.ts +1 -0
  26. package/dist/domain/database/index.d.ts +3 -1
  27. package/dist/domain/database/index.js +3 -1
  28. package/dist/domain/database/migration_plan.d.ts +14 -0
  29. package/dist/domain/database/migration_plan.js +44 -0
  30. package/dist/domain/entity/change_tracking.d.ts +21 -0
  31. package/dist/domain/entity/change_tracking.js +71 -0
  32. package/dist/domain/entity/checks.d.ts +8 -0
  33. package/dist/domain/entity/checks.js +82 -0
  34. package/dist/domain/entity/entity.d.ts +39 -32
  35. package/dist/domain/entity/entity.js +94 -105
  36. package/dist/domain/entity/entity_collection.d.ts +15 -30
  37. package/dist/domain/entity/entity_collection.js +30 -121
  38. package/dist/domain/entity/index.d.ts +5 -0
  39. package/dist/domain/entity/index.js +5 -0
  40. package/dist/domain/entity/reconcile.d.ts +7 -0
  41. package/dist/domain/entity/reconcile.js +12 -0
  42. package/dist/domain/entity/relations.d.ts +27 -0
  43. package/dist/domain/entity/relations.js +69 -0
  44. package/dist/domain/entity/remote_sync.d.ts +4 -0
  45. package/dist/domain/entity/remote_sync.js +19 -0
  46. package/dist/domain/entity/values.d.ts +6 -0
  47. package/dist/domain/entity/values.js +13 -0
  48. package/dist/domain/events/catalog.d.ts +29 -0
  49. package/dist/domain/{events.js → events/catalog.js} +7 -11
  50. package/dist/domain/events/chain.d.ts +5 -0
  51. package/dist/domain/events/chain.js +6 -0
  52. package/dist/domain/{event_sourcing → events}/domain_event.d.ts +20 -22
  53. package/dist/domain/events/domain_event.js +227 -0
  54. package/dist/domain/events/index.d.ts +8 -0
  55. package/dist/domain/events/index.js +8 -0
  56. package/dist/domain/events/integration_event.d.ts +103 -0
  57. package/dist/domain/events/integration_event.js +72 -0
  58. package/dist/domain/events/label.d.ts +2 -0
  59. package/dist/domain/events/label.js +26 -0
  60. package/dist/domain/index.d.ts +0 -1
  61. package/dist/domain/index.js +0 -1
  62. package/dist/domain/print/events.d.ts +8 -4
  63. package/dist/domain/print/events.js +9 -4
  64. package/dist/domain/repositories/document_store.d.ts +33 -0
  65. package/dist/domain/repositories/document_store.js +5 -0
  66. package/dist/domain/repositories/hooks.d.ts +23 -0
  67. package/dist/domain/repositories/hooks.js +32 -0
  68. package/dist/domain/repositories/index.d.ts +4 -0
  69. package/dist/domain/repositories/index.js +4 -0
  70. package/dist/domain/{database → repositories}/repository.d.ts +9 -3
  71. package/dist/domain/settings.d.ts +4 -1
  72. package/dist/domain/settings.js +3 -0
  73. package/dist/domain/webview/events.d.ts +2 -2
  74. package/dist/domain/webview/events.js +2 -2
  75. package/dist/entrypoints/db/migrations.js +10 -10
  76. package/dist/entrypoints/queue/QueueProcessor.d.ts +1 -1
  77. package/dist/entrypoints/queue/activateDomain.subscriber.d.ts +2 -2
  78. package/dist/entrypoints/queue/activateDomain.subscriber.js +2 -2
  79. package/dist/entrypoints/queue/newAppSettings.handler.d.ts +2 -2
  80. package/dist/entrypoints/queue/newAppSettings.handler.js +1 -1
  81. package/dist/entrypoints/queue/printImage.subscriber.d.ts +1 -1
  82. package/dist/entrypoints/queue/printImage.subscriber.js +2 -2
  83. package/dist/entrypoints/queue/processWebViewMessage.subscriber.d.ts +1 -1
  84. package/dist/entrypoints/queue/processWebViewMessage.subscriber.js +1 -1
  85. package/dist/entrypoints/ui/AppShell.js +2 -2
  86. package/dist/entrypoints/ui/common_provider.d.ts +1 -13
  87. package/dist/entrypoints/ui/common_provider.js +4 -133
  88. package/dist/entrypoints/ui/domain_switcher.d.ts +2 -2
  89. package/dist/event_driven/index.d.ts +1 -0
  90. package/dist/event_driven/index.js +1 -0
  91. package/dist/{domain/event_sourcing/event_handler.d.ts → event_driven/subscriber.d.ts} +1 -1
  92. package/dist/exceptions.d.ts +21 -0
  93. package/dist/exceptions.js +30 -0
  94. package/dist/framework/base_module.d.ts +1 -1
  95. package/dist/framework/createApp.d.ts +3 -3
  96. package/dist/framework/domain_module.d.ts +1 -1
  97. package/dist/framework/feature.d.ts +26 -0
  98. package/dist/framework/feature.js +36 -0
  99. package/dist/framework/kernel.d.ts +1 -1
  100. package/dist/framework/kernel.js +17 -8
  101. package/dist/index.d.ts +1 -0
  102. package/dist/index.js +1 -0
  103. package/dist/infrastructure/database/connector.d.ts +2 -0
  104. package/dist/infrastructure/database/connector.js +22 -0
  105. package/dist/infrastructure/database/index.d.ts +1 -0
  106. package/dist/infrastructure/database/index.js +1 -0
  107. package/dist/infrastructure/database/schema.d.ts +9 -0
  108. package/dist/infrastructure/database/schema.js +61 -0
  109. package/dist/infrastructure/database/utils.d.ts +2 -0
  110. package/dist/infrastructure/database/utils.js +12 -4
  111. package/dist/infrastructure/event_chain.d.ts +6 -0
  112. package/dist/infrastructure/event_chain.js +25 -0
  113. package/dist/infrastructure/interceptor/intercept.d.ts +7 -2
  114. package/dist/infrastructure/interceptor/intercept.js +5 -2
  115. package/dist/infrastructure/telemetry/tracing/tracing_hooks.d.ts +9 -6
  116. package/dist/infrastructure/telemetry/tracing/tracing_hooks.js +61 -35
  117. package/dist/infrastructure/ui/UIEventBus.d.ts +3 -1
  118. package/dist/infrastructure/ui/UIEventBus.js +8 -1
  119. package/dist/infrastructure/ui/activity.d.ts +37 -0
  120. package/dist/infrastructure/ui/activity.js +84 -0
  121. package/dist/infrastructure/workers/CronWorker.d.ts +6 -4
  122. package/dist/infrastructure/workers/CronWorker.js +24 -18
  123. package/dist/infrastructure/workers/EventBus.d.ts +9 -5
  124. package/dist/infrastructure/workers/EventBus.js +68 -33
  125. package/dist/services/bluetooth.service.js +16 -14
  126. package/dist/services/dead_letter_queue.service.d.ts +7 -7
  127. package/dist/services/dead_letter_queue.service.js +0 -2
  128. package/dist/services/event.service.d.ts +3 -3
  129. package/dist/services/event.service.js +1 -1
  130. package/dist/services/network.service.d.ts +1 -1
  131. package/dist/services/network.service.js +17 -15
  132. package/dist/services/printer.service.d.ts +1 -1
  133. package/dist/services/printer.service.js +50 -44
  134. package/dist/tools/utils/date.js +4 -3
  135. package/dist/tools/utils/monetary.d.ts +6 -3
  136. package/dist/tools/utils/monetary.js +33 -21
  137. package/dist/tools/utils/serializer.d.ts +1 -1
  138. package/dist/tools/utils/serializer.js +32 -35
  139. package/dist/ui/activity/DeviceNotices.d.ts +1 -0
  140. package/dist/ui/activity/DeviceNotices.js +29 -0
  141. package/dist/ui/activity/ProcessToast.d.ts +2 -0
  142. package/dist/ui/activity/ProcessToast.js +71 -0
  143. package/dist/ui/{components/molecules → activity}/ProcessToastProvider.d.ts +1 -1
  144. package/dist/ui/activity/index.d.ts +4 -0
  145. package/dist/ui/activity/index.js +4 -0
  146. package/dist/ui/activity/useActivity.d.ts +9 -0
  147. package/dist/ui/activity/useActivity.js +48 -0
  148. package/dist/ui/components/atoms/DebugBanner.d.ts +1 -1
  149. package/dist/ui/components/molecules/BluetoothDeviceSelectorModal.d.ts +1 -1
  150. package/dist/ui/components/molecules/BluetoothDeviceSelectorModal.js +8 -2
  151. package/dist/ui/components/molecules/DeadLetterErrorBlock.d.ts +1 -1
  152. package/dist/ui/components/molecules/EventTimelineItem.d.ts +3 -3
  153. package/dist/ui/components/molecules/EventTimelineItem.js +1 -1
  154. package/dist/ui/components/molecules/GeoPermissionCard.d.ts +1 -1
  155. package/dist/ui/components/molecules/TimeZoneSelector.d.ts +1 -1
  156. package/dist/ui/components/molecules/TimeZoneSelectorModal.d.ts +1 -1
  157. package/dist/ui/components/molecules/TimeZoneSelectorModal.js +5 -3
  158. package/dist/ui/components/molecules/index.d.ts +1 -2
  159. package/dist/ui/components/molecules/index.js +1 -2
  160. package/dist/ui/components/organisms/BluetoothPrinterSelector.d.ts +1 -1
  161. package/dist/ui/components/organisms/BluetoothPrinterSelector.js +3 -0
  162. package/dist/ui/components/organisms/DatabaseInfoRow.d.ts +1 -1
  163. package/dist/ui/components/organisms/DeadLetterQueueRow.d.ts +5 -4
  164. package/dist/ui/components/organisms/EventRow.d.ts +4 -4
  165. package/dist/ui/components/organisms/EventRow.js +1 -1
  166. package/dist/ui/components/organisms/InjectableWebView.d.ts +2 -2
  167. package/dist/ui/components/organisms/InjectableWebView.js +6 -3
  168. package/dist/ui/components/organisms/Receipt.d.ts +2 -1
  169. package/dist/ui/components/organisms/TableInfoInfoRow.d.ts +1 -1
  170. package/dist/ui/screens/database/database.context.d.ts +2 -2
  171. package/dist/ui/screens/database/database.context.js +9 -18
  172. package/dist/ui/screens/database/database.json_detail.d.ts +1 -1
  173. package/dist/ui/screens/database/database.list.d.ts +1 -1
  174. package/dist/ui/screens/database/database.table_rows.d.ts +1 -1
  175. package/dist/ui/screens/dead_letter_queue/dead_letter_queue.context.d.ts +7 -7
  176. package/dist/ui/screens/dead_letter_queue/dead_letter_queue.list.d.ts +1 -1
  177. package/dist/ui/screens/events/events.context.d.ts +6 -6
  178. package/dist/ui/screens/events/events.context.js +20 -25
  179. package/dist/ui/screens/events/events.list.d.ts +1 -1
  180. package/package.json +51 -40
  181. package/dist/domain/event_sourcing/domain_event.js +0 -164
  182. package/dist/domain/event_sourcing/event.d.ts +0 -25
  183. package/dist/domain/event_sourcing/event.js +0 -25
  184. package/dist/domain/event_sourcing/index.d.ts +0 -2
  185. package/dist/domain/event_sourcing/index.js +0 -2
  186. package/dist/domain/events.d.ts +0 -33
  187. package/dist/infrastructure/ui/ToastHost.d.ts +0 -6
  188. package/dist/infrastructure/ui/ToastHost.js +0 -28
  189. package/dist/infrastructure/ui/events.d.ts +0 -1
  190. package/dist/infrastructure/ui/events.js +0 -1
  191. package/dist/ui/components/molecules/ProcessToast.d.ts +0 -8
  192. package/dist/ui/components/molecules/ProcessToast.js +0 -52
  193. /package/dist/domain/{repositories.d.ts → repositories/catalog.d.ts} +0 -0
  194. /package/dist/domain/{repositories.js → repositories/catalog.js} +0 -0
  195. /package/dist/domain/{database → repositories}/repository.js +0 -0
  196. /package/dist/{domain/event_sourcing/event_handler.js → event_driven/subscriber.js} +0 -0
  197. /package/dist/ui/{components/molecules → activity}/ProcessToastProvider.js +0 -0
@@ -1,21 +1,15 @@
1
- import { DomainEvent, EEventStatus } from "../../domain/event_sourcing";
1
+ import { EEventStatus } from "../../domain/events";
2
2
  import { ECommonRepository } from "../../domain/repositories";
3
3
  import { DATABASE_TABLES } from "../../entrypoints/db/migrations";
4
4
  import { DBCursor } from "../../infrastructure/database";
5
5
  import { loggerRepositories } from "../../infrastructure/logger";
6
+ import { readEvent } from "./event_row";
7
+ const STORED_COLUMNS = "data, status, attempts, error_message, acknowledged_at, failed_at, source_id, sequence, requires_network";
6
8
  class DomainEventRepositoryImpl {
7
9
  name = ECommonRepository.DOMAIN_EVENT;
8
10
  table = DATABASE_TABLES.DOMAIN_EVENTS;
9
11
  async hasPendingDuplicate(event) {
10
- if (event.correlationId) {
11
- const existing = await DBCursor.getFirstAsync(`SELECT uuid FROM ${this.table}
12
- WHERE name = ? AND correlation_id = ? AND status = ?
13
- LIMIT 1`, event.name, event.correlationId, EEventStatus.PENDING);
14
- return existing !== null;
15
- }
16
- const existing = await DBCursor.getFirstAsync(`SELECT uuid FROM ${this.table}
17
- WHERE name = ? AND status = ? AND correlation_id IS NULL
18
- LIMIT 1`, event.name, EEventStatus.PENDING);
12
+ const existing = await DBCursor.getFirstAsync(`SELECT uuid FROM ${this.table} WHERE uuid = ? AND status = ? LIMIT 1`, event.uuid, EEventStatus.PENDING);
19
13
  return existing !== null;
20
14
  }
21
15
  async save(event) {
@@ -37,64 +31,64 @@ class DomainEventRepositoryImpl {
37
31
  }
38
32
  }
39
33
  async findById(uuid) {
40
- const row = await DBCursor.getFirstAsync(`SELECT data FROM ${this.table} WHERE uuid = ?`, uuid);
41
- return row ? DomainEvent.fromJSON(row.data) : null;
34
+ const row = await DBCursor.getFirstAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table} WHERE uuid = ?`, uuid);
35
+ return row ? readEvent(row) : null;
42
36
  }
43
37
  async findByIds(uuids) {
44
38
  if (uuids.length === 0)
45
39
  return [];
46
40
  const placeholders = uuids.map(() => "?").join(",");
47
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table} WHERE uuid IN (${placeholders}) ORDER BY sequence ASC`, ...uuids);
48
- return rows.map((row) => DomainEvent.fromJSON(row.data));
41
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table} WHERE uuid IN (${placeholders}) ORDER BY sequence ASC`, ...uuids);
42
+ return rows.map((row) => readEvent(row));
49
43
  }
50
44
  async findNextPending(internetConnected) {
51
45
  let row;
52
46
  if (internetConnected) {
53
- row = await DBCursor.getFirstAsync(`SELECT uuid, data FROM ${this.table}
47
+ row = await DBCursor.getFirstAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
54
48
  WHERE status = ?
55
49
  ORDER BY created_at ASC
56
50
  LIMIT 1`, EEventStatus.PENDING);
57
51
  }
58
52
  else {
59
- row = await DBCursor.getFirstAsync(`SELECT uuid, data FROM ${this.table}
53
+ row = await DBCursor.getFirstAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
60
54
  WHERE status = ? AND requires_network = 0
61
55
  ORDER BY created_at ASC
62
56
  LIMIT 1`, EEventStatus.PENDING);
63
57
  }
64
58
  if (!row)
65
59
  return null;
66
- return DomainEvent.fromJSON(row.data);
60
+ return readEvent(row);
67
61
  }
68
62
  async findByStatus(status) {
69
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
63
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
70
64
  WHERE status = ?
71
65
  ORDER BY created_at ASC`, status);
72
- return rows.map((row) => DomainEvent.fromJSON(row.data));
66
+ return rows.map((row) => readEvent(row));
73
67
  }
74
68
  async findByStatuses(statuses) {
75
69
  const placeholders = statuses.map(() => "?").join(",");
76
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
70
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
77
71
  WHERE status IN (${placeholders})
78
72
  ORDER BY created_at DESC`, ...statuses);
79
- return rows.map((row) => DomainEvent.fromJSON(row.data));
73
+ return rows.map((row) => readEvent(row));
80
74
  }
81
75
  async findByCorrelationId(correlationId) {
82
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
76
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
83
77
  WHERE correlation_id = ?
84
78
  ORDER BY sequence ASC`, correlationId);
85
- return rows.map((row) => DomainEvent.fromJSON(row.data));
79
+ return rows.map((row) => readEvent(row));
86
80
  }
87
81
  async findByAggregateId(aggregateId) {
88
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
82
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
89
83
  WHERE aggregate_id = ?
90
84
  ORDER BY created_at ASC`, aggregateId);
91
- return rows.map((row) => DomainEvent.fromJSON(row.data));
85
+ return rows.map((row) => readEvent(row));
92
86
  }
93
87
  async findByName(name) {
94
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
88
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
95
89
  WHERE name = ?
96
90
  ORDER BY created_at ASC`, name);
97
- return rows.map((row) => DomainEvent.fromJSON(row.data));
91
+ return rows.map((row) => readEvent(row));
98
92
  }
99
93
  async findPending() {
100
94
  return this.findByStatus(EEventStatus.PENDING);
@@ -124,15 +118,15 @@ class DomainEventRepositoryImpl {
124
118
  return result?.count || 0;
125
119
  }
126
120
  findByIdSync(uuid) {
127
- const row = DBCursor.getFirstSync(`SELECT data FROM ${this.table} WHERE uuid = ?`, uuid);
128
- return row ? DomainEvent.fromJSON(row.data) : null;
121
+ const row = DBCursor.getFirstSync(`SELECT ${STORED_COLUMNS} FROM ${this.table} WHERE uuid = ?`, uuid);
122
+ return row ? readEvent(row) : null;
129
123
  }
130
124
  findByIdsSync(uuids) {
131
125
  if (uuids.length === 0)
132
126
  return [];
133
127
  const placeholder = uuids.map(() => "?").join(",");
134
- const rows = DBCursor.getAllSync(`SELECT data FROM ${this.table} WHERE uuid IN (${placeholder}) ORDER BY created_at ASC`, ...uuids);
135
- return rows.map((row) => DomainEvent.fromJSON(row.data));
128
+ const rows = DBCursor.getAllSync(`SELECT ${STORED_COLUMNS} FROM ${this.table} WHERE uuid IN (${placeholder}) ORDER BY created_at ASC`, ...uuids);
129
+ return rows.map((row) => readEvent(row));
136
130
  }
137
131
  findByRemoteIdSync(remoteId) {
138
132
  // DomainEvents don't have remote IDs, return null
@@ -1,21 +1,21 @@
1
- import { DomainEvent } from "../../domain/event_sourcing";
1
+ import { 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 DomainEventDeadLetterRepositoryImpl {
5
5
  readonly name = ECommonRepository.DOMAIN_EVENT_DEAD_LETTER;
6
6
  readonly table = DATABASE_TABLES.DOMAIN_EVENTS_DEAD_LETTER;
7
- save(event: DomainEvent, errorMessage?: string): Promise<void>;
8
- findById(uuid: string): Promise<DomainEvent | null>;
9
- findAll(): Promise<DomainEvent[]>;
10
- findByName(name: string): Promise<DomainEvent[]>;
11
- findByCorrelationId(correlationId: string): Promise<DomainEvent[]>;
12
- findByAggregateId(aggregateId: string): Promise<DomainEvent[]>;
13
- findRecent(limit?: number): Promise<DomainEvent[]>;
7
+ save(event: IntegrationEvent, errorMessage?: string): Promise<void>;
8
+ findById(uuid: string): Promise<IntegrationEvent | null>;
9
+ findAll(): Promise<IntegrationEvent[]>;
10
+ findByName(name: string): Promise<IntegrationEvent[]>;
11
+ findByCorrelationId(correlationId: string): Promise<IntegrationEvent[]>;
12
+ findByAggregateId(aggregateId: string): Promise<IntegrationEvent[]>;
13
+ findRecent(limit?: number): Promise<IntegrationEvent[]>;
14
14
  remove(uuid: string): Promise<void>;
15
15
  removeByCorrelationId(correlationId: string): Promise<void>;
16
16
  clearAll(): Promise<void>;
17
- retry(uuid: string): Promise<DomainEvent | null>;
18
- retryByCorrelationId(correlationId: string): Promise<DomainEvent[]>;
17
+ retry(uuid: string): Promise<IntegrationEvent | null>;
18
+ retryByCorrelationId(correlationId: string): Promise<IntegrationEvent[]>;
19
19
  count(): Promise<number>;
20
20
  countByName(name: string): Promise<number>;
21
21
  }
@@ -1,8 +1,9 @@
1
- import { DomainEvent } from "../../domain/event_sourcing";
2
1
  import { ECommonRepository } from "../../domain/repositories";
3
2
  import { DATABASE_TABLES } from "../../entrypoints/db/migrations";
4
3
  import { DBCursor } from "../../infrastructure/database";
5
4
  import { loggerRepositories } from "../../infrastructure/logger";
5
+ import { readEvent } from "./event_row";
6
+ const STORED_COLUMNS = "data, attempts, error_message, failed_at, source_id, sequence, requires_network, 'FAILED' as status";
6
7
  class DomainEventDeadLetterRepositoryImpl {
7
8
  name = ECommonRepository.DOMAIN_EVENT_DEAD_LETTER;
8
9
  table = DATABASE_TABLES.DOMAIN_EVENTS_DEAD_LETTER;
@@ -13,36 +14,36 @@ class DomainEventDeadLetterRepositoryImpl {
13
14
  loggerRepositories.debug(`DomainEvent moved to dead letter: ${event.name} [${event.uuid}]`);
14
15
  }
15
16
  async findById(uuid) {
16
- const row = await DBCursor.getFirstAsync(`SELECT data FROM ${this.table} WHERE uuid = ?`, uuid);
17
- return row ? DomainEvent.fromJSON(row.data) : null;
17
+ const row = await DBCursor.getFirstAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table} WHERE uuid = ?`, uuid);
18
+ return row ? readEvent(row) : null;
18
19
  }
19
20
  async findAll() {
20
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table} ORDER BY failed_at DESC`);
21
- return rows.map((row) => DomainEvent.fromJSON(row.data));
21
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table} ORDER BY failed_at DESC`);
22
+ return rows.map((row) => readEvent(row));
22
23
  }
23
24
  async findByName(name) {
24
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
25
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
25
26
  WHERE name = ?
26
27
  ORDER BY failed_at DESC`, name);
27
- return rows.map((row) => DomainEvent.fromJSON(row.data));
28
+ return rows.map((row) => readEvent(row));
28
29
  }
29
30
  async findByCorrelationId(correlationId) {
30
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
31
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
31
32
  WHERE correlation_id = ?
32
33
  ORDER BY sequence ASC`, correlationId);
33
- return rows.map((row) => DomainEvent.fromJSON(row.data));
34
+ return rows.map((row) => readEvent(row));
34
35
  }
35
36
  async findByAggregateId(aggregateId) {
36
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
37
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
37
38
  WHERE aggregate_id = ?
38
39
  ORDER BY failed_at DESC`, aggregateId);
39
- return rows.map((row) => DomainEvent.fromJSON(row.data));
40
+ return rows.map((row) => readEvent(row));
40
41
  }
41
42
  async findRecent(limit = 50) {
42
- const rows = await DBCursor.getAllAsync(`SELECT data FROM ${this.table}
43
+ const rows = await DBCursor.getAllAsync(`SELECT ${STORED_COLUMNS} FROM ${this.table}
43
44
  ORDER BY failed_at DESC
44
45
  LIMIT ?`, limit);
45
- return rows.map((row) => DomainEvent.fromJSON(row.data));
46
+ return rows.map((row) => readEvent(row));
46
47
  }
47
48
  async remove(uuid) {
48
49
  await DBCursor.mutateDatabase(`DELETE FROM ${this.table} WHERE uuid = ?`, uuid);
@@ -0,0 +1,13 @@
1
+ import { IntegrationEvent } from "../../domain/events/integration_event";
2
+ export type EventRow = {
3
+ data: string;
4
+ status?: string | null;
5
+ attempts?: number | null;
6
+ error_message?: string | null;
7
+ acknowledged_at?: string | null;
8
+ failed_at?: string | null;
9
+ source_id?: string | null;
10
+ sequence?: number | null;
11
+ requires_network?: number | null;
12
+ };
13
+ export declare function readEvent(row: EventRow): IntegrationEvent;
@@ -0,0 +1,22 @@
1
+ import { IntegrationEvent } from "../../domain/events/integration_event";
2
+ export function readEvent(row) {
3
+ const event = IntegrationEvent.fromJSON(row.data);
4
+ if (row.status)
5
+ event.status = row.status;
6
+ if (row.attempts != null)
7
+ event.attempts = row.attempts;
8
+ if (row.error_message !== undefined)
9
+ event.errorMessage = row.error_message;
10
+ if (row.acknowledged_at !== undefined)
11
+ event.acknowledgedAt = row.acknowledged_at;
12
+ if (row.failed_at !== undefined)
13
+ event.failedAt = row.failed_at;
14
+ if (row.source_id)
15
+ event.sourceId = row.source_id;
16
+ if (row.sequence != null)
17
+ event.sequence = row.sequence;
18
+ if (row.requires_network != null) {
19
+ Object.assign(event, { requiresNetwork: row.requires_network === 1 });
20
+ }
21
+ return event;
22
+ }
@@ -0,0 +1,33 @@
1
+ import { type CompiledCriteria } from "../../domain/database/criteria_sql";
2
+ import { type ColumnValue, type DocumentRow, type DocumentStore } from "../../domain/repositories/document_store";
3
+ export declare class SqliteDocumentStore implements DocumentStore {
4
+ readonly table: string;
5
+ private readonly columnNames;
6
+ private readonly integerColumns;
7
+ private ready;
8
+ private syncReady;
9
+ constructor(table: string, columnNames?: readonly string[], integerColumns?: readonly string[]);
10
+ read(uuid: string): Promise<DocumentRow | null>;
11
+ readSync(uuid: string): DocumentRow | null;
12
+ insert(row: DocumentRow): Promise<void>;
13
+ update(row: DocumentRow, expectedVersion: number): Promise<boolean>;
14
+ upsert(row: DocumentRow): Promise<void>;
15
+ remove(uuid: string): Promise<void>;
16
+ list(): Promise<DocumentRow[]>;
17
+ selectMatching(compiled: CompiledCriteria): Promise<DocumentRow[]>;
18
+ hasColumn(name: string): boolean;
19
+ selectBy(column: string, value: ColumnValue): Promise<DocumentRow[]>;
20
+ selectBySync(column: string, value: ColumnValue): DocumentRow[];
21
+ inTransaction(): boolean;
22
+ transaction<T>(work: () => Promise<T>): Promise<T>;
23
+ private names;
24
+ private selected;
25
+ private values;
26
+ private definition;
27
+ private columnDefinition;
28
+ private ensure;
29
+ private wanted;
30
+ private create;
31
+ private ensureSync;
32
+ private assertColumn;
33
+ }
@@ -0,0 +1,201 @@
1
+ import { missingColumns, } from "../../domain/database/migration_plan";
2
+ import { DBCursor } from "../../infrastructure/database";
3
+ import { addColumnStatement, tableColumns, tableColumnsSync, } from "../../infrastructure/database/schema";
4
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
+ export class SqliteDocumentStore {
6
+ table;
7
+ columnNames;
8
+ integerColumns;
9
+ ready = null;
10
+ syncReady = false;
11
+ constructor(table, columnNames = [], integerColumns = []) {
12
+ this.table = table;
13
+ this.columnNames = columnNames;
14
+ assertIdentifier(table);
15
+ for (const name of columnNames)
16
+ assertIdentifier(name);
17
+ this.integerColumns = new Set(integerColumns);
18
+ }
19
+ async read(uuid) {
20
+ await this.ensure();
21
+ const row = await DBCursor.getFirstAsync(`SELECT ${this.selected()} FROM ${this.table} WHERE uuid = ?`, uuid);
22
+ return row ? toDocument(row, this.columnNames) : null;
23
+ }
24
+ readSync(uuid) {
25
+ this.ensureSync();
26
+ const row = DBCursor.getFirstSync(`SELECT ${this.selected()} FROM ${this.table} WHERE uuid = ?`, uuid);
27
+ return row ? toDocument(row, this.columnNames) : null;
28
+ }
29
+ async insert(row) {
30
+ await this.ensure();
31
+ const names = this.names();
32
+ await DBCursor.mutateDatabase(`INSERT INTO ${this.table} (${names.join(", ")}) VALUES (${placeholders(names.length)})`, ...this.values(row));
33
+ }
34
+ async update(row, expectedVersion) {
35
+ await this.ensure();
36
+ const assignments = ["version", "data", ...this.columnNames]
37
+ .map((name) => `${name} = ?`)
38
+ .join(", ");
39
+ const result = await DBCursor.mutateDatabase(`UPDATE ${this.table} SET ${assignments} WHERE uuid = ? AND version = ?`, row.version, row.data, ...this.columnNames.map((name) => row.columns[name] ?? null), row.uuid, expectedVersion);
40
+ return result.changes === 1;
41
+ }
42
+ async upsert(row) {
43
+ await this.ensure();
44
+ const names = this.names();
45
+ const assignments = ["version", "data", ...this.columnNames]
46
+ .map((name) => `${name} = excluded.${name}`)
47
+ .join(", ");
48
+ await DBCursor.mutateDatabase(`INSERT INTO ${this.table} (${names.join(", ")}) VALUES (${placeholders(names.length)}) ON CONFLICT(uuid) DO UPDATE SET ${assignments}`, ...this.values(row));
49
+ }
50
+ async remove(uuid) {
51
+ await this.ensure();
52
+ await DBCursor.mutateDatabase(`DELETE FROM ${this.table} WHERE uuid = ?`, uuid);
53
+ }
54
+ async list() {
55
+ await this.ensure();
56
+ const rows = await DBCursor.getAllAsync(`SELECT ${this.selected()} FROM ${this.table}`);
57
+ return rows.map((row) => toDocument(row, this.columnNames));
58
+ }
59
+ async selectMatching(compiled) {
60
+ await this.ensure();
61
+ const params = [...compiled.params];
62
+ let sql = `SELECT ${this.selected()} FROM ${this.table}`;
63
+ if (compiled.whereSql)
64
+ sql += ` WHERE ${compiled.whereSql}`;
65
+ if (compiled.orderSql)
66
+ sql += ` ORDER BY ${compiled.orderSql}`;
67
+ if (compiled.limit !== undefined) {
68
+ sql += " LIMIT ?";
69
+ params.push(compiled.limit);
70
+ }
71
+ else if (compiled.offset > 0) {
72
+ sql += " LIMIT -1";
73
+ }
74
+ if (compiled.offset > 0) {
75
+ sql += " OFFSET ?";
76
+ params.push(compiled.offset);
77
+ }
78
+ const rows = await DBCursor.getAllAsync(sql, ...params);
79
+ return rows.map((row) => toDocument(row, this.columnNames));
80
+ }
81
+ hasColumn(name) {
82
+ return this.columnNames.includes(name);
83
+ }
84
+ async selectBy(column, value) {
85
+ this.assertColumn(column);
86
+ await this.ensure();
87
+ const rows = await DBCursor.getAllAsync(`SELECT ${this.selected()} FROM ${this.table} WHERE ${column} = ?`, value);
88
+ return rows.map((row) => toDocument(row, this.columnNames));
89
+ }
90
+ selectBySync(column, value) {
91
+ this.assertColumn(column);
92
+ this.ensureSync();
93
+ const rows = DBCursor.getAllSync(`SELECT ${this.selected()} FROM ${this.table} WHERE ${column} = ?`, value);
94
+ return rows.map((row) => toDocument(row, this.columnNames));
95
+ }
96
+ inTransaction() {
97
+ return DBCursor.inTransaction();
98
+ }
99
+ transaction(work) {
100
+ return DBCursor.transaction(work);
101
+ }
102
+ names() {
103
+ return ["uuid", "version", "data", ...this.columnNames];
104
+ }
105
+ selected() {
106
+ return this.names().join(", ");
107
+ }
108
+ values(row) {
109
+ return [
110
+ row.uuid,
111
+ row.version,
112
+ row.data,
113
+ ...this.columnNames.map((name) => row.columns[name] ?? null),
114
+ ];
115
+ }
116
+ definition() {
117
+ const extras = this.columnNames
118
+ .map((name) => `${name} ${this.columnDefinition(name)}`)
119
+ .join(", ");
120
+ const tail = extras ? `, ${extras}` : "";
121
+ return `CREATE TABLE IF NOT EXISTS ${this.table} (uuid TEXT PRIMARY KEY NOT NULL, version INTEGER NOT NULL, data TEXT NOT NULL${tail})`;
122
+ }
123
+ columnDefinition(name) {
124
+ if (name === "ackedVersion")
125
+ return "INTEGER NOT NULL DEFAULT 0";
126
+ return this.integerColumns.has(name) ? "INTEGER" : "TEXT";
127
+ }
128
+ ensure() {
129
+ if (!this.ready) {
130
+ this.ready = this.create().catch((error) => {
131
+ this.ready = null;
132
+ throw error;
133
+ });
134
+ }
135
+ return this.ready;
136
+ }
137
+ wanted() {
138
+ return [
139
+ { name: "version", definition: "INTEGER NOT NULL DEFAULT 1" },
140
+ { name: "data", definition: "TEXT NOT NULL DEFAULT '{}'" },
141
+ ...this.columnNames.map((name) => ({
142
+ name,
143
+ definition: this.columnDefinition(name),
144
+ })),
145
+ ];
146
+ }
147
+ async create() {
148
+ const existing = await tableColumns(this.table);
149
+ if (existing.length === 0) {
150
+ await DBCursor.execAsync(this.definition());
151
+ }
152
+ else {
153
+ for (const column of missingColumns(existing, this.wanted())) {
154
+ await DBCursor.execAsync(addColumnStatement(this.table, column.name, column.definition));
155
+ }
156
+ }
157
+ for (const name of this.columnNames) {
158
+ await DBCursor.execAsync(`CREATE INDEX IF NOT EXISTS idx_${this.table}_${name} ON ${this.table} (${name})`);
159
+ }
160
+ }
161
+ ensureSync() {
162
+ if (this.syncReady)
163
+ return;
164
+ const existing = tableColumnsSync(this.table);
165
+ if (existing.length === 0) {
166
+ DBCursor.mutateDatabaseSync(this.definition());
167
+ }
168
+ else {
169
+ for (const column of missingColumns(existing, this.wanted())) {
170
+ DBCursor.mutateDatabaseSync(addColumnStatement(this.table, column.name, column.definition));
171
+ }
172
+ }
173
+ for (const name of this.columnNames) {
174
+ DBCursor.mutateDatabaseSync(`CREATE INDEX IF NOT EXISTS idx_${this.table}_${name} ON ${this.table} (${name})`);
175
+ }
176
+ this.syncReady = true;
177
+ }
178
+ assertColumn(name) {
179
+ if (!this.hasColumn(name))
180
+ throw new Error(`unknown column: ${name}`);
181
+ }
182
+ }
183
+ function placeholders(count) {
184
+ return Array.from({ length: count }, () => "?").join(", ");
185
+ }
186
+ function toDocument(row, columnNames) {
187
+ const columns = {};
188
+ for (const name of columnNames)
189
+ columns[name] = row[name] ?? null;
190
+ return {
191
+ uuid: String(row.uuid),
192
+ version: Number(row.version),
193
+ data: String(row.data),
194
+ columns,
195
+ };
196
+ }
197
+ function assertIdentifier(name) {
198
+ if (!IDENTIFIER.test(name)) {
199
+ throw new Error(`invalid SQL identifier: ${name}`);
200
+ }
201
+ }
@@ -1,17 +1,17 @@
1
- import { DomainEvent } from "../../domain/event_sourcing/domain_event";
1
+ import { DomainEvent } from "../../domain/events/domain_event";
2
2
  export declare class InternetIsDownEvent extends DomainEvent {
3
- static readonly name = "common.network.v2.internet_is_down";
3
+ static readonly name = "common.network.down";
4
4
  static readonly label = "Internet desconectado";
5
5
  static readonly requiresNetwork = false;
6
- readonly name = "common.network.v2.internet_is_down";
6
+ readonly name = "common.network.down";
7
7
  readonly label = "Internet desconectado";
8
8
  readonly requiresNetwork = false;
9
9
  }
10
10
  export declare class InternetIsUpEvent extends DomainEvent {
11
- static readonly name = "common.network.v2.internet_is_up";
11
+ static readonly name = "common.network.up";
12
12
  static readonly label = "Internet conectado";
13
13
  static readonly requiresNetwork = true;
14
- readonly name = "common.network.v2.internet_is_up";
14
+ readonly name = "common.network.up";
15
15
  readonly label = "Internet conectado";
16
16
  readonly requiresNetwork = true;
17
17
  }
@@ -1,6 +1,6 @@
1
- import { DomainEvent } from "../../domain/event_sourcing/domain_event";
1
+ import { DomainEvent } from "../../domain/events/domain_event";
2
2
  export class InternetIsDownEvent extends DomainEvent {
3
- static name = "common.network.v2.internet_is_down";
3
+ static name = "common.network.down";
4
4
  static label = "Internet desconectado";
5
5
  static requiresNetwork = false;
6
6
  name = InternetIsDownEvent.name;
@@ -8,7 +8,7 @@ export class InternetIsDownEvent extends DomainEvent {
8
8
  requiresNetwork = InternetIsDownEvent.requiresNetwork;
9
9
  }
10
10
  export class InternetIsUpEvent extends DomainEvent {
11
- static name = "common.network.v2.internet_is_up";
11
+ static name = "common.network.up";
12
12
  static label = "Internet conectado";
13
13
  static requiresNetwork = true;
14
14
  name = InternetIsUpEvent.name;
@@ -0,0 +1,19 @@
1
+ import { type Criteria, CriteriaBuilder, type Expression } from "@sincpro/criteria";
2
+ /**
3
+ * What the document store accepts.
4
+ *
5
+ * A `Criteria` carries the filter in `selection`. An expression is that filter
6
+ * alone. A `CriteriaBuilder` is read from `.value`.
7
+ */
8
+ export type DocumentCriteria<T> = Criteria<T> | CriteriaBuilder<T> | Expression<T>;
9
+ /**
10
+ * The rows of a document collection that answer a criteria.
11
+ *
12
+ * The filter is `selection`. It is evaluated by `@sincpro/criteria`, the same
13
+ * engine the server uses, over the entities already in memory. `order` sorts
14
+ * those rows. `pagination` cuts a page only when the criteria asks for one:
15
+ * a filter with no page returns every match. A grouping is not a list of
16
+ * documents, so it is refused.
17
+ */
18
+ export declare function criteriaOf<T>(asked: DocumentCriteria<T>): Criteria<T>;
19
+ export declare function rowsMatching<T>(rows: readonly T[], asked: DocumentCriteria<T>): T[];
@@ -0,0 +1,36 @@
1
+ import { CriteriaBuilder, InvalidCriteria, } from "@sincpro/criteria";
2
+ import { filtered, paginate, sortRows } from "@sincpro/criteria/engine";
3
+ /**
4
+ * The rows of a document collection that answer a criteria.
5
+ *
6
+ * The filter is `selection`. It is evaluated by `@sincpro/criteria`, the same
7
+ * engine the server uses, over the entities already in memory. `order` sorts
8
+ * those rows. `pagination` cuts a page only when the criteria asks for one:
9
+ * a filter with no page returns every match. A grouping is not a list of
10
+ * documents, so it is refused.
11
+ */
12
+ export function criteriaOf(asked) {
13
+ if (asked instanceof CriteriaBuilder)
14
+ return asked.value;
15
+ if (isExpression(asked))
16
+ return { selection: asked };
17
+ return asked;
18
+ }
19
+ export function rowsMatching(rows, asked) {
20
+ const criteria = criteriaOf(asked);
21
+ if (criteria.grouping !== undefined) {
22
+ throw new InvalidCriteria("a document collection answers a selection, not a grouping");
23
+ }
24
+ const matched = filtered(rows, criteria.selection);
25
+ const ordered = criteria.order?.length ? sortRows(matched, criteria.order) : [...matched];
26
+ if (criteria.pagination === undefined)
27
+ return ordered;
28
+ return paginate(ordered, criteria, criteria.order ?? []).rows;
29
+ }
30
+ function isExpression(asked) {
31
+ return ("field" in asked ||
32
+ "all" in asked ||
33
+ "any" in asked ||
34
+ "negate" in asked ||
35
+ "through" in asked);
36
+ }
@@ -0,0 +1,8 @@
1
+ import { type Value } from "@sincpro/criteria";
2
+ export interface CriteriaClock {
3
+ now: Date;
4
+ timeZone: string;
5
+ }
6
+ export declare function deviceClock(now?: Date): CriteriaClock;
7
+ export declare function readCriteriaValue(value: Value, clock: CriteriaClock, field?: string): Value;
8
+ export declare function periodBounds(value: Value, clock: CriteriaClock): [string, string];