@abloatai/ablo 0.3.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 (278) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +12 -0
  4. package/README.md +230 -0
  5. package/dist/BaseSyncedStore.d.ts +709 -0
  6. package/dist/BaseSyncedStore.js +1843 -0
  7. package/dist/Database.d.ts +344 -0
  8. package/dist/Database.js +1259 -0
  9. package/dist/LazyReferenceCollection.d.ts +181 -0
  10. package/dist/LazyReferenceCollection.js +460 -0
  11. package/dist/Model.d.ts +339 -0
  12. package/dist/Model.js +715 -0
  13. package/dist/ModelRegistry.d.ts +200 -0
  14. package/dist/ModelRegistry.js +535 -0
  15. package/dist/NetworkMonitor.d.ts +27 -0
  16. package/dist/NetworkMonitor.js +73 -0
  17. package/dist/ObjectPool.d.ts +202 -0
  18. package/dist/ObjectPool.js +1106 -0
  19. package/dist/SyncClient.d.ts +489 -0
  20. package/dist/SyncClient.js +1555 -0
  21. package/dist/SyncEngineContext.d.ts +46 -0
  22. package/dist/SyncEngineContext.js +74 -0
  23. package/dist/adapters/alwaysOnline.d.ts +16 -0
  24. package/dist/adapters/alwaysOnline.js +19 -0
  25. package/dist/adapters/inMemoryStorage.d.ts +30 -0
  26. package/dist/adapters/inMemoryStorage.js +94 -0
  27. package/dist/agent/Agent.d.ts +358 -0
  28. package/dist/agent/Agent.js +500 -0
  29. package/dist/agent/index.d.ts +115 -0
  30. package/dist/agent/index.js +128 -0
  31. package/dist/agent/session.d.ts +90 -0
  32. package/dist/agent/session.js +156 -0
  33. package/dist/agent/types.d.ts +73 -0
  34. package/dist/agent/types.js +10 -0
  35. package/dist/ai-sdk/coordination-context.d.ts +51 -0
  36. package/dist/ai-sdk/coordination-context.js +107 -0
  37. package/dist/ai-sdk/index.d.ts +68 -0
  38. package/dist/ai-sdk/index.js +68 -0
  39. package/dist/ai-sdk/intent-broadcast.d.ts +77 -0
  40. package/dist/ai-sdk/intent-broadcast.js +72 -0
  41. package/dist/ai-sdk/wrap.d.ts +67 -0
  42. package/dist/ai-sdk/wrap.js +45 -0
  43. package/dist/api/index.d.ts +10 -0
  44. package/dist/api/index.js +9 -0
  45. package/dist/auth/index.d.ts +137 -0
  46. package/dist/auth/index.js +246 -0
  47. package/dist/client/Ablo.d.ts +835 -0
  48. package/dist/client/Ablo.js +1440 -0
  49. package/dist/client/ApiClient.d.ts +200 -0
  50. package/dist/client/ApiClient.js +659 -0
  51. package/dist/client/auth.d.ts +79 -0
  52. package/dist/client/auth.js +81 -0
  53. package/dist/client/createInternalComponents.d.ts +44 -0
  54. package/dist/client/createInternalComponents.js +88 -0
  55. package/dist/client/createModelProxy.d.ts +152 -0
  56. package/dist/client/createModelProxy.js +199 -0
  57. package/dist/client/identity.d.ts +63 -0
  58. package/dist/client/identity.js +156 -0
  59. package/dist/client/index.d.ts +36 -0
  60. package/dist/client/index.js +33 -0
  61. package/dist/client/persistence.d.ts +7 -0
  62. package/dist/client/persistence.js +11 -0
  63. package/dist/client/validateAbloOptions.d.ts +42 -0
  64. package/dist/client/validateAbloOptions.js +43 -0
  65. package/dist/config/index.d.ts +10 -0
  66. package/dist/config/index.js +12 -0
  67. package/dist/context.d.ts +27 -0
  68. package/dist/context.js +58 -0
  69. package/dist/core/DatabaseManager.d.ts +108 -0
  70. package/dist/core/DatabaseManager.js +361 -0
  71. package/dist/core/QueryProcessor.d.ts +77 -0
  72. package/dist/core/QueryProcessor.js +262 -0
  73. package/dist/core/QueryView.d.ts +64 -0
  74. package/dist/core/QueryView.js +219 -0
  75. package/dist/core/StoreManager.d.ts +131 -0
  76. package/dist/core/StoreManager.js +334 -0
  77. package/dist/core/ViewRegistry.d.ts +20 -0
  78. package/dist/core/ViewRegistry.js +55 -0
  79. package/dist/core/index.d.ts +34 -0
  80. package/dist/core/index.js +59 -0
  81. package/dist/core/openIDBWithTimeout.d.ts +27 -0
  82. package/dist/core/openIDBWithTimeout.js +63 -0
  83. package/dist/core/query-utils.d.ts +37 -0
  84. package/dist/core/query-utils.js +60 -0
  85. package/dist/errors.d.ts +235 -0
  86. package/dist/errors.js +243 -0
  87. package/dist/index.d.ts +41 -0
  88. package/dist/index.js +82 -0
  89. package/dist/interfaces/headless.d.ts +95 -0
  90. package/dist/interfaces/headless.js +41 -0
  91. package/dist/interfaces/index.d.ts +321 -0
  92. package/dist/interfaces/index.js +8 -0
  93. package/dist/mutators/RecordingTransaction.d.ts +36 -0
  94. package/dist/mutators/RecordingTransaction.js +216 -0
  95. package/dist/mutators/Transaction.d.ts +48 -0
  96. package/dist/mutators/Transaction.js +64 -0
  97. package/dist/mutators/UndoManager.d.ts +114 -0
  98. package/dist/mutators/UndoManager.js +143 -0
  99. package/dist/mutators/defineMutators.d.ts +55 -0
  100. package/dist/mutators/defineMutators.js +28 -0
  101. package/dist/policy/index.d.ts +19 -0
  102. package/dist/policy/index.js +18 -0
  103. package/dist/policy/types.d.ts +74 -0
  104. package/dist/policy/types.js +17 -0
  105. package/dist/principal.d.ts +44 -0
  106. package/dist/principal.js +49 -0
  107. package/dist/query/client.d.ts +43 -0
  108. package/dist/query/client.js +84 -0
  109. package/dist/query/index.d.ts +6 -0
  110. package/dist/query/index.js +5 -0
  111. package/dist/query/types.d.ts +143 -0
  112. package/dist/query/types.js +36 -0
  113. package/dist/react/AbloProvider.d.ts +205 -0
  114. package/dist/react/AbloProvider.js +398 -0
  115. package/dist/react/ClientSideSuspense.d.ts +36 -0
  116. package/dist/react/ClientSideSuspense.js +17 -0
  117. package/dist/react/DefaultFallback.d.ts +24 -0
  118. package/dist/react/DefaultFallback.js +43 -0
  119. package/dist/react/SyncGroupProvider.d.ts +19 -0
  120. package/dist/react/SyncGroupProvider.js +44 -0
  121. package/dist/react/context.d.ts +161 -0
  122. package/dist/react/context.js +35 -0
  123. package/dist/react/index.d.ts +64 -0
  124. package/dist/react/index.js +73 -0
  125. package/dist/react/internalContext.d.ts +35 -0
  126. package/dist/react/internalContext.js +3 -0
  127. package/dist/react/useAblo.d.ts +72 -0
  128. package/dist/react/useAblo.js +63 -0
  129. package/dist/react/useCurrentUserId.d.ts +21 -0
  130. package/dist/react/useCurrentUserId.js +33 -0
  131. package/dist/react/useErrorListener.d.ts +20 -0
  132. package/dist/react/useErrorListener.js +39 -0
  133. package/dist/react/useIntent.d.ts +29 -0
  134. package/dist/react/useIntent.js +42 -0
  135. package/dist/react/useMutate.d.ts +83 -0
  136. package/dist/react/useMutate.js +122 -0
  137. package/dist/react/useMutationFailureListener.d.ts +26 -0
  138. package/dist/react/useMutationFailureListener.js +38 -0
  139. package/dist/react/useMutators.d.ts +56 -0
  140. package/dist/react/useMutators.js +66 -0
  141. package/dist/react/usePresence.d.ts +32 -0
  142. package/dist/react/usePresence.js +41 -0
  143. package/dist/react/useQuery.d.ts +123 -0
  144. package/dist/react/useQuery.js +145 -0
  145. package/dist/react/useReactive.d.ts +35 -0
  146. package/dist/react/useReactive.js +111 -0
  147. package/dist/react/useReader.d.ts +69 -0
  148. package/dist/react/useReader.js +73 -0
  149. package/dist/react/useSyncStatus.d.ts +61 -0
  150. package/dist/react/useSyncStatus.js +76 -0
  151. package/dist/react/useUndoScope.d.ts +36 -0
  152. package/dist/react/useUndoScope.js +73 -0
  153. package/dist/realtime/index.d.ts +10 -0
  154. package/dist/realtime/index.js +9 -0
  155. package/dist/schema/field.d.ts +134 -0
  156. package/dist/schema/field.js +264 -0
  157. package/dist/schema/index.d.ts +29 -0
  158. package/dist/schema/index.js +38 -0
  159. package/dist/schema/model.d.ts +326 -0
  160. package/dist/schema/model.js +89 -0
  161. package/dist/schema/queries.d.ts +203 -0
  162. package/dist/schema/queries.js +145 -0
  163. package/dist/schema/relation.d.ts +172 -0
  164. package/dist/schema/relation.js +104 -0
  165. package/dist/schema/schema.d.ts +259 -0
  166. package/dist/schema/schema.js +188 -0
  167. package/dist/schema/sugar.d.ts +129 -0
  168. package/dist/schema/sugar.js +94 -0
  169. package/dist/source/index.d.ts +423 -0
  170. package/dist/source/index.js +320 -0
  171. package/dist/source/pushQueue.d.ts +112 -0
  172. package/dist/source/pushQueue.js +249 -0
  173. package/dist/stores/ObjectStore.d.ts +103 -0
  174. package/dist/stores/ObjectStore.js +371 -0
  175. package/dist/stores/ObjectStoreContract.d.ts +39 -0
  176. package/dist/stores/ObjectStoreContract.js +1 -0
  177. package/dist/stores/SyncActionStore.d.ts +101 -0
  178. package/dist/stores/SyncActionStore.js +481 -0
  179. package/dist/sync/BootstrapHelper.d.ts +127 -0
  180. package/dist/sync/BootstrapHelper.js +434 -0
  181. package/dist/sync/ConnectionManager.d.ts +136 -0
  182. package/dist/sync/ConnectionManager.js +465 -0
  183. package/dist/sync/HydrationCoordinator.d.ts +137 -0
  184. package/dist/sync/HydrationCoordinator.js +468 -0
  185. package/dist/sync/NetworkProbe.d.ts +43 -0
  186. package/dist/sync/NetworkProbe.js +113 -0
  187. package/dist/sync/OfflineFlush.d.ts +9 -0
  188. package/dist/sync/OfflineFlush.js +22 -0
  189. package/dist/sync/OfflineTransactionStore.d.ts +37 -0
  190. package/dist/sync/OfflineTransactionStore.js +263 -0
  191. package/dist/sync/SyncWebSocket.d.ts +663 -0
  192. package/dist/sync/SyncWebSocket.js +1336 -0
  193. package/dist/sync/createIntentStream.d.ts +33 -0
  194. package/dist/sync/createIntentStream.js +243 -0
  195. package/dist/sync/createPresenceStream.d.ts +46 -0
  196. package/dist/sync/createPresenceStream.js +192 -0
  197. package/dist/sync/createSnapshot.d.ts +33 -0
  198. package/dist/sync/createSnapshot.js +124 -0
  199. package/dist/sync/participants.d.ts +114 -0
  200. package/dist/sync/participants.js +336 -0
  201. package/dist/sync/schemas.d.ts +79 -0
  202. package/dist/sync/schemas.js +78 -0
  203. package/dist/testing/fixtures/bootstrap.d.ts +45 -0
  204. package/dist/testing/fixtures/bootstrap.js +53 -0
  205. package/dist/testing/fixtures/deltas.d.ts +86 -0
  206. package/dist/testing/fixtures/deltas.js +139 -0
  207. package/dist/testing/fixtures/models.d.ts +82 -0
  208. package/dist/testing/fixtures/models.js +270 -0
  209. package/dist/testing/helpers/react-wrapper.d.ts +66 -0
  210. package/dist/testing/helpers/react-wrapper.js +64 -0
  211. package/dist/testing/helpers/sync-engine-harness.d.ts +55 -0
  212. package/dist/testing/helpers/sync-engine-harness.js +70 -0
  213. package/dist/testing/helpers/wait.d.ts +25 -0
  214. package/dist/testing/helpers/wait.js +44 -0
  215. package/dist/testing/index.d.ts +21 -0
  216. package/dist/testing/index.js +32 -0
  217. package/dist/testing/mocks/MockMutationExecutor.d.ts +65 -0
  218. package/dist/testing/mocks/MockMutationExecutor.js +139 -0
  219. package/dist/testing/mocks/MockNetworkMonitor.d.ts +20 -0
  220. package/dist/testing/mocks/MockNetworkMonitor.js +46 -0
  221. package/dist/testing/mocks/MockSyncContext.d.ts +64 -0
  222. package/dist/testing/mocks/MockSyncContext.js +100 -0
  223. package/dist/testing/mocks/MockSyncStore.d.ts +88 -0
  224. package/dist/testing/mocks/MockSyncStore.js +171 -0
  225. package/dist/testing/mocks/MockWebSocket.d.ts +66 -0
  226. package/dist/testing/mocks/MockWebSocket.js +117 -0
  227. package/dist/transactions/OptimisticEchoTracker.d.ts +82 -0
  228. package/dist/transactions/OptimisticEchoTracker.js +104 -0
  229. package/dist/transactions/TransactionQueue.d.ts +499 -0
  230. package/dist/transactions/TransactionQueue.js +1895 -0
  231. package/dist/transactions/index.d.ts +16 -0
  232. package/dist/transactions/index.js +7 -0
  233. package/dist/transactions/mutation-error-handler.d.ts +5 -0
  234. package/dist/transactions/mutation-error-handler.js +39 -0
  235. package/dist/types/global.d.ts +107 -0
  236. package/dist/types/global.js +38 -0
  237. package/dist/types/index.d.ts +241 -0
  238. package/dist/types/index.js +70 -0
  239. package/dist/types/streams.d.ts +495 -0
  240. package/dist/types/streams.js +11 -0
  241. package/dist/utils/asyncIterator.d.ts +41 -0
  242. package/dist/utils/asyncIterator.js +142 -0
  243. package/dist/utils/duration.d.ts +28 -0
  244. package/dist/utils/duration.js +47 -0
  245. package/dist/utils/mobx-setup.d.ts +42 -0
  246. package/dist/utils/mobx-setup.js +381 -0
  247. package/docs/api-keys.md +24 -0
  248. package/docs/api.md +230 -0
  249. package/docs/audit.md +81 -0
  250. package/docs/capabilities.md +163 -0
  251. package/docs/client-behavior.md +202 -0
  252. package/docs/data-sources.md +214 -0
  253. package/docs/examples/agent-human.md +84 -0
  254. package/docs/examples/ai-sdk-tool.md +92 -0
  255. package/docs/examples/existing-python-backend.md +249 -0
  256. package/docs/examples/nextjs.md +88 -0
  257. package/docs/examples/server-agent.md +86 -0
  258. package/docs/guarantees.md +148 -0
  259. package/docs/index.md +97 -0
  260. package/docs/integration-guide.md +493 -0
  261. package/docs/interaction-model.md +140 -0
  262. package/docs/mcp/claude-code.md +43 -0
  263. package/docs/mcp/cursor.md +53 -0
  264. package/docs/mcp/windsurf.md +46 -0
  265. package/docs/mcp.md +59 -0
  266. package/docs/quickstart.md +152 -0
  267. package/docs/react.md +115 -0
  268. package/docs/roadmap.md +45 -0
  269. package/examples/README.md +54 -0
  270. package/examples/data-source/README.md +102 -0
  271. package/examples/data-source/ablo-driver.ts +89 -0
  272. package/examples/data-source/customer-server.ts +208 -0
  273. package/examples/data-source/run.ts +101 -0
  274. package/examples/data-source/schema.ts +25 -0
  275. package/examples/quickstart.ts +54 -0
  276. package/examples/tsconfig.json +16 -0
  277. package/llms.txt +143 -0
  278. package/package.json +147 -0
@@ -0,0 +1,37 @@
1
+ export declare enum Priority {
2
+ CRITICAL = 0,
3
+ HIGH = 1,
4
+ NORMAL = 2,
5
+ LOW = 3
6
+ }
7
+ export type OfflineTransaction = {
8
+ id: string;
9
+ opName: string;
10
+ priority: Priority;
11
+ createdAt: number;
12
+ dependsOn?: string[];
13
+ request: {
14
+ gqlId?: string;
15
+ document?: string;
16
+ variables?: Record<string, any>;
17
+ headers?: Record<string, string>;
18
+ method?: string;
19
+ url?: string;
20
+ body?: any;
21
+ };
22
+ };
23
+ export declare class OfflineTransactionStore {
24
+ private db;
25
+ private key;
26
+ init(): Promise<void>;
27
+ enqueue(tx: Omit<OfflineTransaction, 'createdAt'>): Promise<string>;
28
+ listAll(): Promise<OfflineTransaction[]>;
29
+ getOptimizedSyncOrder(): Promise<OfflineTransaction[]>;
30
+ remove(id: string): Promise<void>;
31
+ clear(): Promise<void>;
32
+ flush(processor: (tx: OfflineTransaction) => Promise<void>): Promise<{
33
+ processed: number;
34
+ failed: number;
35
+ }>;
36
+ }
37
+ export declare const offlineTxStore: OfflineTransactionStore;
@@ -0,0 +1,263 @@
1
+ /*
2
+ * OfflineTransactionStore
3
+ * - IndexedDB-backed queue for GraphQL mutations and REST ops
4
+ * - AES-GCM encryption at rest (per-browser key)
5
+ * - Priority-aware ordering with simple topological sort
6
+ */
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ import { openIDBWithTimeout } from '../core/openIDBWithTimeout.js';
9
+ import { AbloConnectionError } from '../errors.js';
10
+ export var Priority;
11
+ (function (Priority) {
12
+ Priority[Priority["CRITICAL"] = 0] = "CRITICAL";
13
+ Priority[Priority["HIGH"] = 1] = "HIGH";
14
+ Priority[Priority["NORMAL"] = 2] = "NORMAL";
15
+ Priority[Priority["LOW"] = 3] = "LOW";
16
+ })(Priority || (Priority = {}));
17
+ // Minimal IndexedDB helpers. Delegates to openIDBWithTimeout so the open
18
+ // request can't silently hang when another tab holds an older version
19
+ // (`onblocked`) or when storage is in a bad state (no event fires at all).
20
+ function openDB(name, version, upgrade) {
21
+ return openIDBWithTimeout(name, version, {
22
+ onUpgrade: (request) => upgrade(request.result),
23
+ });
24
+ }
25
+ function txStore(db, mode) {
26
+ const tx = db.transaction('transactions', mode);
27
+ const store = tx.objectStore('transactions');
28
+ return { tx, store };
29
+ }
30
+ function b64(bytes) {
31
+ // Buffer.from accepts ArrayBufferLike but the @types/node overloads
32
+ // don't all line up — wrap in Uint8Array (which Buffer DOES accept
33
+ // unambiguously) so the type system stays happy without a cast.
34
+ if (typeof Buffer !== 'undefined') {
35
+ return Buffer.from(new Uint8Array(bytes)).toString('base64');
36
+ }
37
+ let binary = '';
38
+ const arr = new Uint8Array(bytes);
39
+ for (let i = 0; i < arr.byteLength; i++)
40
+ binary += String.fromCharCode(arr[i]);
41
+ return btoa(binary);
42
+ }
43
+ function b64dec(text) {
44
+ if (typeof Buffer !== 'undefined') {
45
+ const buffer = Buffer.from(text, 'base64');
46
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
47
+ }
48
+ const binary = atob(text);
49
+ const bytes = new Uint8Array(binary.length);
50
+ for (let i = 0; i < binary.length; i++)
51
+ bytes[i] = binary.charCodeAt(i);
52
+ return bytes.buffer;
53
+ }
54
+ async function getOrCreateKey() {
55
+ try {
56
+ const storageKey = 'ablo:offline:key';
57
+ let raw = localStorage.getItem(storageKey);
58
+ if (!raw) {
59
+ const seed = crypto.getRandomValues(new Uint8Array(32));
60
+ raw = b64(seed.buffer);
61
+ localStorage.setItem(storageKey, raw);
62
+ }
63
+ const keyData = b64dec(raw);
64
+ return await crypto.subtle.importKey('raw', keyData, 'AES-GCM', false, ['encrypt', 'decrypt']);
65
+ }
66
+ catch {
67
+ return null; // no crypto in environment
68
+ }
69
+ }
70
+ async function encryptJSON(key, data) {
71
+ const plaintext = new TextEncoder().encode(JSON.stringify(data));
72
+ if (!key || !crypto?.subtle) {
73
+ return { iv: '', ciphertext: b64(plaintext.buffer) };
74
+ }
75
+ const iv = crypto.getRandomValues(new Uint8Array(12));
76
+ const ct = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext);
77
+ return { iv: b64(iv.buffer), ciphertext: b64(ct) };
78
+ }
79
+ async function decryptJSON(key, record) {
80
+ if (!key || !record.iv) {
81
+ const buf = b64dec(record.ciphertext);
82
+ const text = new TextDecoder().decode(buf);
83
+ return JSON.parse(text);
84
+ }
85
+ const iv = new Uint8Array(b64dec(record.iv));
86
+ const ct = b64dec(record.ciphertext);
87
+ const pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ct);
88
+ return JSON.parse(new TextDecoder().decode(pt));
89
+ }
90
+ export class OfflineTransactionStore {
91
+ db = null;
92
+ key = null;
93
+ async init() {
94
+ if (typeof indexedDB === 'undefined')
95
+ return;
96
+ this.key = await getOrCreateKey();
97
+ this.db = await openDB('ablo-sync', 1, (db) => {
98
+ if (!db.objectStoreNames.contains('transactions')) {
99
+ const store = db.createObjectStore('transactions', { keyPath: 'id' });
100
+ store.createIndex('priority_createdAt', ['priority', 'createdAt']);
101
+ store.createIndex('createdAt', 'createdAt');
102
+ }
103
+ });
104
+ }
105
+ async enqueue(tx) {
106
+ if (!this.db)
107
+ await this.init();
108
+ if (!this.db)
109
+ throw new AbloConnectionError('IndexedDB unavailable', {
110
+ code: 'idb_unavailable',
111
+ });
112
+ const full = { ...tx, createdAt: Date.now() };
113
+ const { iv, ciphertext } = await encryptJSON(this.key, full);
114
+ const rec = {
115
+ id: full.id,
116
+ priority: full.priority,
117
+ createdAt: full.createdAt,
118
+ dependsOn: full.dependsOn,
119
+ iv,
120
+ ciphertext,
121
+ };
122
+ const { tx: itx, store } = txStore(this.db, 'readwrite');
123
+ return new Promise((resolve, reject) => {
124
+ const req = store.put(rec);
125
+ req.onsuccess = () => {
126
+ resolve(full.id);
127
+ };
128
+ req.onerror = () => {
129
+ reject(req.error);
130
+ };
131
+ });
132
+ }
133
+ async listAll() {
134
+ if (!this.db)
135
+ await this.init();
136
+ if (!this.db)
137
+ return [];
138
+ const out = [];
139
+ const { store } = txStore(this.db, 'readonly');
140
+ const req = store.getAll();
141
+ return new Promise((resolve, reject) => {
142
+ req.onsuccess = async () => {
143
+ try {
144
+ for (const rec of req.result) {
145
+ out.push(await decryptJSON(this.key, rec));
146
+ }
147
+ resolve(out);
148
+ }
149
+ catch (e) {
150
+ reject(e);
151
+ }
152
+ };
153
+ req.onerror = () => reject(req.error);
154
+ });
155
+ }
156
+ async getOptimizedSyncOrder() {
157
+ // Collect all, sort by priority asc, then topologically by dependsOn
158
+ const items = await this.listAll();
159
+ items.sort((a, b) => {
160
+ if (a.priority !== b.priority)
161
+ return a.priority - b.priority;
162
+ return a.createdAt - b.createdAt;
163
+ });
164
+ // Topological sort
165
+ const byId = new Map();
166
+ const indeg = new Map();
167
+ const graph = new Map();
168
+ for (const it of items) {
169
+ byId.set(it.id, it);
170
+ indeg.set(it.id, 0);
171
+ graph.set(it.id, []);
172
+ }
173
+ for (const it of items) {
174
+ for (const dep of it.dependsOn || []) {
175
+ if (!byId.has(dep))
176
+ continue; // ignore external
177
+ indeg.set(it.id, (indeg.get(it.id) || 0) + 1);
178
+ graph.get(dep).push(it.id);
179
+ }
180
+ }
181
+ const queue = [];
182
+ for (const [id, deg] of indeg)
183
+ if (deg === 0)
184
+ queue.push(id);
185
+ // Sort queue by priority so nodes at the same dependency level
186
+ // come out in priority order (lower number = higher priority)
187
+ const sortQueue = () => queue.sort((a, b) => {
188
+ const pa = byId.get(a)?.priority ?? 0;
189
+ const pb = byId.get(b)?.priority ?? 0;
190
+ if (pa !== pb)
191
+ return pa - pb;
192
+ return (byId.get(a)?.createdAt ?? 0) - (byId.get(b)?.createdAt ?? 0);
193
+ });
194
+ sortQueue();
195
+ const order = [];
196
+ while (queue.length) {
197
+ const id = queue.shift();
198
+ const node = byId.get(id);
199
+ if (node)
200
+ order.push(node);
201
+ for (const nxt of graph.get(id) || []) {
202
+ const d = (indeg.get(nxt) || 0) - 1;
203
+ indeg.set(nxt, d);
204
+ if (d === 0)
205
+ queue.push(nxt);
206
+ }
207
+ if (queue.length > 1)
208
+ sortQueue();
209
+ }
210
+ // Fallback: if cycles exist, append remaining by createdAt
211
+ if (order.length < items.length) {
212
+ const seen = new Set(order.map((x) => x.id));
213
+ for (const it of items)
214
+ if (!seen.has(it.id))
215
+ order.push(it);
216
+ }
217
+ return order;
218
+ }
219
+ async remove(id) {
220
+ if (!this.db)
221
+ await this.init();
222
+ if (!this.db)
223
+ return;
224
+ const { store } = txStore(this.db, 'readwrite');
225
+ await new Promise((resolve, reject) => {
226
+ const req = store.delete(id);
227
+ req.onsuccess = () => resolve();
228
+ req.onerror = () => reject(req.error);
229
+ });
230
+ }
231
+ async clear() {
232
+ if (!this.db)
233
+ await this.init();
234
+ if (!this.db)
235
+ return;
236
+ const { store } = txStore(this.db, 'readwrite');
237
+ await new Promise((resolve, reject) => {
238
+ const req = store.clear();
239
+ req.onsuccess = () => resolve();
240
+ req.onerror = () => reject(req.error);
241
+ });
242
+ }
243
+ // Flush with caller-provided network function
244
+ async flush(processor) {
245
+ const ordered = await this.getOptimizedSyncOrder();
246
+ let processed = 0;
247
+ let failed = 0;
248
+ for (const tx of ordered) {
249
+ try {
250
+ await processor(tx);
251
+ await this.remove(tx.id);
252
+ processed++;
253
+ }
254
+ catch (_err) {
255
+ // Stop on first failure to preserve order
256
+ failed++;
257
+ break;
258
+ }
259
+ }
260
+ return { processed, failed };
261
+ }
262
+ }
263
+ export const offlineTxStore = new OfflineTransactionStore();