@tanstack/offline-transactions 1.0.1 → 1.0.3

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 (55) hide show
  1. package/README.md +14 -14
  2. package/dist/cjs/OfflineExecutor.cjs.map +1 -1
  3. package/dist/cjs/api/OfflineAction.cjs.map +1 -1
  4. package/dist/cjs/api/OfflineTransaction.cjs.map +1 -1
  5. package/dist/cjs/connectivity/OnlineDetector.cjs.map +1 -1
  6. package/dist/cjs/coordination/BroadcastChannelLeader.cjs.map +1 -1
  7. package/dist/cjs/coordination/LeaderElection.cjs.map +1 -1
  8. package/dist/cjs/coordination/WebLocksLeader.cjs.map +1 -1
  9. package/dist/cjs/executor/KeyScheduler.cjs.map +1 -1
  10. package/dist/cjs/executor/TransactionExecutor.cjs.map +1 -1
  11. package/dist/cjs/outbox/OutboxManager.cjs.map +1 -1
  12. package/dist/cjs/outbox/TransactionSerializer.cjs.map +1 -1
  13. package/dist/cjs/retry/RetryPolicy.cjs.map +1 -1
  14. package/dist/cjs/storage/IndexedDBAdapter.cjs.map +1 -1
  15. package/dist/cjs/storage/LocalStorageAdapter.cjs.map +1 -1
  16. package/dist/cjs/storage/StorageAdapter.cjs.map +1 -1
  17. package/dist/cjs/telemetry/tracer.cjs.map +1 -1
  18. package/dist/cjs/types.cjs.map +1 -1
  19. package/dist/esm/OfflineExecutor.js.map +1 -1
  20. package/dist/esm/api/OfflineAction.js.map +1 -1
  21. package/dist/esm/api/OfflineTransaction.js.map +1 -1
  22. package/dist/esm/connectivity/OnlineDetector.js.map +1 -1
  23. package/dist/esm/coordination/BroadcastChannelLeader.js.map +1 -1
  24. package/dist/esm/coordination/LeaderElection.js.map +1 -1
  25. package/dist/esm/coordination/WebLocksLeader.js.map +1 -1
  26. package/dist/esm/executor/KeyScheduler.js.map +1 -1
  27. package/dist/esm/executor/TransactionExecutor.js.map +1 -1
  28. package/dist/esm/outbox/OutboxManager.js.map +1 -1
  29. package/dist/esm/outbox/TransactionSerializer.js.map +1 -1
  30. package/dist/esm/retry/RetryPolicy.js.map +1 -1
  31. package/dist/esm/storage/IndexedDBAdapter.js.map +1 -1
  32. package/dist/esm/storage/LocalStorageAdapter.js.map +1 -1
  33. package/dist/esm/storage/StorageAdapter.js.map +1 -1
  34. package/dist/esm/telemetry/tracer.js.map +1 -1
  35. package/dist/esm/types.js.map +1 -1
  36. package/package.json +10 -13
  37. package/src/OfflineExecutor.ts +26 -26
  38. package/src/api/OfflineAction.ts +6 -6
  39. package/src/api/OfflineTransaction.ts +6 -6
  40. package/src/connectivity/OnlineDetector.ts +2 -2
  41. package/src/coordination/BroadcastChannelLeader.ts +1 -1
  42. package/src/coordination/LeaderElection.ts +1 -1
  43. package/src/coordination/WebLocksLeader.ts +3 -3
  44. package/src/executor/KeyScheduler.ts +12 -12
  45. package/src/executor/TransactionExecutor.ts +22 -22
  46. package/src/index.ts +16 -16
  47. package/src/outbox/OutboxManager.ts +17 -17
  48. package/src/outbox/TransactionSerializer.ts +7 -7
  49. package/src/retry/NonRetriableError.ts +1 -1
  50. package/src/retry/RetryPolicy.ts +3 -3
  51. package/src/storage/IndexedDBAdapter.ts +3 -3
  52. package/src/storage/LocalStorageAdapter.ts +3 -3
  53. package/src/storage/StorageAdapter.ts +1 -1
  54. package/src/telemetry/tracer.ts +3 -3
  55. package/src/types.ts +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"StorageAdapter.js","sources":["../../../src/storage/StorageAdapter.ts"],"sourcesContent":["import type { StorageAdapter } from \"../types\"\n\nexport abstract class BaseStorageAdapter implements StorageAdapter {\n abstract get(key: string): Promise<string | null>\n abstract set(key: string, value: string): Promise<void>\n abstract delete(key: string): Promise<void>\n abstract keys(): Promise<Array<string>>\n abstract clear(): Promise<void>\n}\n\nexport { type StorageAdapter }\n"],"names":[],"mappings":"AAEO,MAAe,mBAA6C;AAMnE;"}
1
+ {"version":3,"file":"StorageAdapter.js","sources":["../../../src/storage/StorageAdapter.ts"],"sourcesContent":["import type { StorageAdapter } from '../types'\n\nexport abstract class BaseStorageAdapter implements StorageAdapter {\n abstract get(key: string): Promise<string | null>\n abstract set(key: string, value: string): Promise<void>\n abstract delete(key: string): Promise<void>\n abstract keys(): Promise<Array<string>>\n abstract clear(): Promise<void>\n}\n\nexport { type StorageAdapter }\n"],"names":[],"mappings":"AAEO,MAAe,mBAA6C;AAMnE;"}
@@ -1 +1 @@
1
- {"version":3,"file":"tracer.js","sources":["../../../src/telemetry/tracer.ts"],"sourcesContent":["export interface SpanAttrs {\n [key: string]: string | number | boolean | undefined\n}\n\ninterface WithSpanOptions {\n parentContext?: any\n}\n\n// No-op span implementation\nconst noopSpan = {\n setAttribute: () => {},\n setAttributes: () => {},\n setStatus: () => {},\n recordException: () => {},\n end: () => {},\n}\n\n/**\n * Lightweight span wrapper with error handling.\n * No-op implementation - telemetry has been removed.\n *\n * By default, creates spans at the current context level (siblings).\n * Use withNestedSpan if you want parent-child relationships.\n */\nexport async function withSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: any) => Promise<T>,\n _options?: WithSpanOptions\n): Promise<T> {\n return await fn(noopSpan)\n}\n\n/**\n * Like withSpan but propagates context so child spans nest properly.\n * No-op implementation - telemetry has been removed.\n */\nexport async function withNestedSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: any) => Promise<T>,\n _options?: WithSpanOptions\n): Promise<T> {\n return await fn(noopSpan)\n}\n\n/**\n * Creates a synchronous span for non-async operations\n * No-op implementation - telemetry has been removed.\n */\nexport function withSyncSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: any) => T,\n _options?: WithSpanOptions\n): T {\n return fn(noopSpan)\n}\n\n/**\n * Get the current tracer instance\n * No-op implementation - telemetry has been removed.\n */\nexport function getTracer() {\n return null\n}\n"],"names":[],"mappings":"AASA,MAAM,WAAW;AAAA,EACf,cAAc,MAAM;AAAA,EAAC;AAAA,EACrB,eAAe,MAAM;AAAA,EAAC;AAAA,EACtB,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,iBAAiB,MAAM;AAAA,EAAC;AAAA,EACxB,KAAK,MAAM;AAAA,EAAC;AACd;AASA,eAAsB,SACpB,MACA,OACA,IACA,UACY;AACZ,SAAO,MAAM,GAAG,QAAQ;AAC1B;AAMA,eAAsB,eACpB,MACA,OACA,IACA,UACY;AACZ,SAAO,MAAM,GAAG,QAAQ;AAC1B;AAMO,SAAS,aACd,MACA,OACA,IACA,UACG;AACH,SAAO,GAAG,QAAQ;AACpB;"}
1
+ {"version":3,"file":"tracer.js","sources":["../../../src/telemetry/tracer.ts"],"sourcesContent":["export interface SpanAttrs {\n [key: string]: string | number | boolean | undefined\n}\n\ninterface WithSpanOptions {\n parentContext?: any\n}\n\n// No-op span implementation\nconst noopSpan = {\n setAttribute: () => {},\n setAttributes: () => {},\n setStatus: () => {},\n recordException: () => {},\n end: () => {},\n}\n\n/**\n * Lightweight span wrapper with error handling.\n * No-op implementation - telemetry has been removed.\n *\n * By default, creates spans at the current context level (siblings).\n * Use withNestedSpan if you want parent-child relationships.\n */\nexport async function withSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: any) => Promise<T>,\n _options?: WithSpanOptions,\n): Promise<T> {\n return await fn(noopSpan)\n}\n\n/**\n * Like withSpan but propagates context so child spans nest properly.\n * No-op implementation - telemetry has been removed.\n */\nexport async function withNestedSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: any) => Promise<T>,\n _options?: WithSpanOptions,\n): Promise<T> {\n return await fn(noopSpan)\n}\n\n/**\n * Creates a synchronous span for non-async operations\n * No-op implementation - telemetry has been removed.\n */\nexport function withSyncSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: any) => T,\n _options?: WithSpanOptions,\n): T {\n return fn(noopSpan)\n}\n\n/**\n * Get the current tracer instance\n * No-op implementation - telemetry has been removed.\n */\nexport function getTracer() {\n return null\n}\n"],"names":[],"mappings":"AASA,MAAM,WAAW;AAAA,EACf,cAAc,MAAM;AAAA,EAAC;AAAA,EACrB,eAAe,MAAM;AAAA,EAAC;AAAA,EACtB,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,iBAAiB,MAAM;AAAA,EAAC;AAAA,EACxB,KAAK,MAAM;AAAA,EAAC;AACd;AASA,eAAsB,SACpB,MACA,OACA,IACA,UACY;AACZ,SAAO,MAAM,GAAG,QAAQ;AAC1B;AAMA,eAAsB,eACpB,MACA,OACA,IACA,UACY;AACZ,SAAO,MAAM,GAAG,QAAQ;AAC1B;AAMO,SAAS,aACd,MACA,OACA,IACA,UACG;AACH,SAAO,GAAG,QAAQ;AACpB;"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import type {\n Collection,\n MutationFnParams,\n PendingMutation,\n} from \"@tanstack/db\"\n\n// Extended mutation function that includes idempotency key\nexport type OfflineMutationFnParams<\n T extends object = Record<string, unknown>,\n> = MutationFnParams<T> & {\n idempotencyKey: string\n}\n\nexport type OfflineMutationFn<T extends object = Record<string, unknown>> = (\n params: OfflineMutationFnParams<T>\n) => Promise<any>\n\n// Simplified mutation structure for serialization\nexport interface SerializedMutation {\n globalKey: string\n type: string\n modified: any\n original: any\n collectionId: string\n}\n\nexport interface SerializedError {\n name: string\n message: string\n stack?: string\n}\n\nexport interface SerializedSpanContext {\n traceId: string\n spanId: string\n traceFlags: number\n traceState?: string\n}\n\n// In-memory representation with full PendingMutation objects\nexport interface OfflineTransaction {\n id: string\n mutationFnName: string\n mutations: Array<PendingMutation>\n keys: Array<string>\n idempotencyKey: string\n createdAt: Date\n retryCount: number\n nextAttemptAt: number\n lastError?: SerializedError\n metadata?: Record<string, any>\n spanContext?: SerializedSpanContext\n version: 1\n}\n\n// Serialized representation for storage\nexport interface SerializedOfflineTransaction {\n id: string\n mutationFnName: string\n mutations: Array<SerializedMutation>\n keys: Array<string>\n idempotencyKey: string\n createdAt: Date\n retryCount: number\n nextAttemptAt: number\n lastError?: SerializedError\n metadata?: Record<string, any>\n spanContext?: SerializedSpanContext\n version: 1\n}\n\n// Storage diagnostics and mode\nexport type OfflineMode = `offline` | `online-only`\n\nexport type StorageDiagnosticCode =\n | `STORAGE_AVAILABLE`\n | `INDEXEDDB_UNAVAILABLE`\n | `LOCALSTORAGE_UNAVAILABLE`\n | `STORAGE_BLOCKED`\n | `QUOTA_EXCEEDED`\n | `UNKNOWN_ERROR`\n\nexport interface StorageDiagnostic {\n code: StorageDiagnosticCode\n mode: OfflineMode\n message: string\n error?: Error\n}\n\nexport interface OfflineConfig {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n collections: Record<string, Collection<any, any, any, any, any>>\n mutationFns: Record<string, OfflineMutationFn>\n storage?: StorageAdapter\n maxConcurrency?: number\n jitter?: boolean\n beforeRetry?: (\n transactions: Array<OfflineTransaction>\n ) => Array<OfflineTransaction>\n onUnknownMutationFn?: (name: string, tx: OfflineTransaction) => void\n onLeadershipChange?: (isLeader: boolean) => void\n onStorageFailure?: (diagnostic: StorageDiagnostic) => void\n leaderElection?: LeaderElection\n}\n\nexport interface StorageAdapter {\n get: (key: string) => Promise<string | null>\n set: (key: string, value: string) => Promise<void>\n delete: (key: string) => Promise<void>\n keys: () => Promise<Array<string>>\n clear: () => Promise<void>\n}\n\nexport interface RetryPolicy {\n calculateDelay: (retryCount: number) => number\n shouldRetry: (error: Error, retryCount: number) => boolean\n}\n\nexport interface LeaderElection {\n requestLeadership: () => Promise<boolean>\n releaseLeadership: () => void\n isLeader: () => boolean\n onLeadershipChange: (callback: (isLeader: boolean) => void) => () => void\n}\n\nexport interface OnlineDetector {\n subscribe: (callback: () => void) => () => void\n notifyOnline: () => void\n}\n\nexport interface CreateOfflineTransactionOptions {\n id?: string\n mutationFnName: string\n autoCommit?: boolean\n idempotencyKey?: string\n metadata?: Record<string, any>\n}\n\nexport interface CreateOfflineActionOptions<T> {\n mutationFnName: string\n onMutate: (variables: T) => void\n}\n\nexport class NonRetriableError extends Error {\n constructor(message: string) {\n super(message)\n this.name = `NonRetriableError`\n }\n}\n"],"names":[],"mappings":"AA+IO,MAAM,0BAA0B,MAAM;AAAA,EAC3C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;"}
1
+ {"version":3,"file":"types.js","sources":["../../src/types.ts"],"sourcesContent":["import type {\n Collection,\n MutationFnParams,\n PendingMutation,\n} from '@tanstack/db'\n\n// Extended mutation function that includes idempotency key\nexport type OfflineMutationFnParams<\n T extends object = Record<string, unknown>,\n> = MutationFnParams<T> & {\n idempotencyKey: string\n}\n\nexport type OfflineMutationFn<T extends object = Record<string, unknown>> = (\n params: OfflineMutationFnParams<T>,\n) => Promise<any>\n\n// Simplified mutation structure for serialization\nexport interface SerializedMutation {\n globalKey: string\n type: string\n modified: any\n original: any\n collectionId: string\n}\n\nexport interface SerializedError {\n name: string\n message: string\n stack?: string\n}\n\nexport interface SerializedSpanContext {\n traceId: string\n spanId: string\n traceFlags: number\n traceState?: string\n}\n\n// In-memory representation with full PendingMutation objects\nexport interface OfflineTransaction {\n id: string\n mutationFnName: string\n mutations: Array<PendingMutation>\n keys: Array<string>\n idempotencyKey: string\n createdAt: Date\n retryCount: number\n nextAttemptAt: number\n lastError?: SerializedError\n metadata?: Record<string, any>\n spanContext?: SerializedSpanContext\n version: 1\n}\n\n// Serialized representation for storage\nexport interface SerializedOfflineTransaction {\n id: string\n mutationFnName: string\n mutations: Array<SerializedMutation>\n keys: Array<string>\n idempotencyKey: string\n createdAt: Date\n retryCount: number\n nextAttemptAt: number\n lastError?: SerializedError\n metadata?: Record<string, any>\n spanContext?: SerializedSpanContext\n version: 1\n}\n\n// Storage diagnostics and mode\nexport type OfflineMode = `offline` | `online-only`\n\nexport type StorageDiagnosticCode =\n | `STORAGE_AVAILABLE`\n | `INDEXEDDB_UNAVAILABLE`\n | `LOCALSTORAGE_UNAVAILABLE`\n | `STORAGE_BLOCKED`\n | `QUOTA_EXCEEDED`\n | `UNKNOWN_ERROR`\n\nexport interface StorageDiagnostic {\n code: StorageDiagnosticCode\n mode: OfflineMode\n message: string\n error?: Error\n}\n\nexport interface OfflineConfig {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n collections: Record<string, Collection<any, any, any, any, any>>\n mutationFns: Record<string, OfflineMutationFn>\n storage?: StorageAdapter\n maxConcurrency?: number\n jitter?: boolean\n beforeRetry?: (\n transactions: Array<OfflineTransaction>,\n ) => Array<OfflineTransaction>\n onUnknownMutationFn?: (name: string, tx: OfflineTransaction) => void\n onLeadershipChange?: (isLeader: boolean) => void\n onStorageFailure?: (diagnostic: StorageDiagnostic) => void\n leaderElection?: LeaderElection\n}\n\nexport interface StorageAdapter {\n get: (key: string) => Promise<string | null>\n set: (key: string, value: string) => Promise<void>\n delete: (key: string) => Promise<void>\n keys: () => Promise<Array<string>>\n clear: () => Promise<void>\n}\n\nexport interface RetryPolicy {\n calculateDelay: (retryCount: number) => number\n shouldRetry: (error: Error, retryCount: number) => boolean\n}\n\nexport interface LeaderElection {\n requestLeadership: () => Promise<boolean>\n releaseLeadership: () => void\n isLeader: () => boolean\n onLeadershipChange: (callback: (isLeader: boolean) => void) => () => void\n}\n\nexport interface OnlineDetector {\n subscribe: (callback: () => void) => () => void\n notifyOnline: () => void\n}\n\nexport interface CreateOfflineTransactionOptions {\n id?: string\n mutationFnName: string\n autoCommit?: boolean\n idempotencyKey?: string\n metadata?: Record<string, any>\n}\n\nexport interface CreateOfflineActionOptions<T> {\n mutationFnName: string\n onMutate: (variables: T) => void\n}\n\nexport class NonRetriableError extends Error {\n constructor(message: string) {\n super(message)\n this.name = `NonRetriableError`\n }\n}\n"],"names":[],"mappings":"AA+IO,MAAM,0BAA0B,MAAM;AAAA,EAC3C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;"}
package/package.json CHANGED
@@ -1,17 +1,14 @@
1
1
  {
2
2
  "name": "@tanstack/offline-transactions",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Offline-first transaction capabilities for TanStack DB",
5
5
  "author": "TanStack",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/TanStack/db.git",
9
+ "url": "git+https://github.com/TanStack/db.git",
10
10
  "directory": "packages/offline-transactions"
11
11
  },
12
- "publishConfig": {
13
- "access": "public"
14
- },
15
12
  "keywords": [
16
13
  "tanstack",
17
14
  "database",
@@ -21,7 +18,9 @@
21
18
  "sync"
22
19
  ],
23
20
  "type": "module",
24
- "sideEffects": false,
21
+ "main": "dist/cjs/index.cjs",
22
+ "module": "dist/esm/index.js",
23
+ "types": "dist/esm/index.d.ts",
25
24
  "exports": {
26
25
  ".": {
27
26
  "import": {
@@ -35,20 +34,18 @@
35
34
  },
36
35
  "./package.json": "./package.json"
37
36
  },
38
- "main": "dist/cjs/index.cjs",
39
- "module": "dist/esm/index.js",
40
- "types": "dist/esm/index.d.ts",
37
+ "sideEffects": false,
41
38
  "files": [
42
39
  "dist",
43
40
  "src"
44
41
  ],
45
42
  "dependencies": {
46
- "@tanstack/db": "0.5.11"
43
+ "@tanstack/db": "0.5.13"
47
44
  },
48
45
  "devDependencies": {
49
- "@types/node": "^20.0.0",
50
- "eslint": "^8.57.1",
51
- "typescript": "^5.5.4",
46
+ "@types/node": "^24.6.2",
47
+ "eslint": "^9.39.1",
48
+ "typescript": "^5.9.2",
52
49
  "vitest": "^3.2.4"
53
50
  },
54
51
  "scripts": {
@@ -1,28 +1,28 @@
1
1
  // Storage adapters
2
- import { createOptimisticAction, createTransaction } from "@tanstack/db"
3
- import { IndexedDBAdapter } from "./storage/IndexedDBAdapter"
4
- import { LocalStorageAdapter } from "./storage/LocalStorageAdapter"
2
+ import { createOptimisticAction, createTransaction } from '@tanstack/db'
3
+ import { IndexedDBAdapter } from './storage/IndexedDBAdapter'
4
+ import { LocalStorageAdapter } from './storage/LocalStorageAdapter'
5
5
 
6
6
  // Core components
7
- import { OutboxManager } from "./outbox/OutboxManager"
8
- import { KeyScheduler } from "./executor/KeyScheduler"
9
- import { TransactionExecutor } from "./executor/TransactionExecutor"
7
+ import { OutboxManager } from './outbox/OutboxManager'
8
+ import { KeyScheduler } from './executor/KeyScheduler'
9
+ import { TransactionExecutor } from './executor/TransactionExecutor'
10
10
 
11
11
  // Coordination
12
- import { WebLocksLeader } from "./coordination/WebLocksLeader"
13
- import { BroadcastChannelLeader } from "./coordination/BroadcastChannelLeader"
12
+ import { WebLocksLeader } from './coordination/WebLocksLeader'
13
+ import { BroadcastChannelLeader } from './coordination/BroadcastChannelLeader'
14
14
 
15
15
  // Connectivity
16
- import { DefaultOnlineDetector } from "./connectivity/OnlineDetector"
16
+ import { DefaultOnlineDetector } from './connectivity/OnlineDetector'
17
17
 
18
18
  // API
19
- import { OfflineTransaction as OfflineTransactionAPI } from "./api/OfflineTransaction"
20
- import { createOfflineAction } from "./api/OfflineAction"
19
+ import { OfflineTransaction as OfflineTransactionAPI } from './api/OfflineTransaction'
20
+ import { createOfflineAction } from './api/OfflineAction'
21
21
 
22
22
  // TanStack DB primitives
23
23
 
24
24
  // Replay
25
- import { withNestedSpan, withSpan } from "./telemetry/tracer"
25
+ import { withNestedSpan, withSpan } from './telemetry/tracer'
26
26
  import type {
27
27
  CreateOfflineActionOptions,
28
28
  CreateOfflineTransactionOptions,
@@ -32,8 +32,8 @@ import type {
32
32
  OfflineTransaction,
33
33
  StorageAdapter,
34
34
  StorageDiagnostic,
35
- } from "./types"
36
- import type { Transaction } from "@tanstack/db"
35
+ } from './types'
36
+ import type { Transaction } from '@tanstack/db'
37
37
 
38
38
  export class OfflineExecutor {
39
39
  private config: OfflineConfig
@@ -210,7 +210,7 @@ export class OfflineExecutor {
210
210
  if (isLeader) {
211
211
  this.loadAndReplayTransactions()
212
212
  }
213
- }
213
+ },
214
214
  )
215
215
  }
216
216
 
@@ -221,7 +221,7 @@ export class OfflineExecutor {
221
221
  this.executor.executeAll().catch((error) => {
222
222
  console.warn(
223
223
  `Failed to execute transactions on connectivity change:`,
224
- error
224
+ error,
225
225
  )
226
226
  })
227
227
  }
@@ -258,7 +258,7 @@ export class OfflineExecutor {
258
258
  this.scheduler,
259
259
  this.outbox,
260
260
  this.config,
261
- this
261
+ this,
262
262
  )
263
263
  this.leaderElection = this.createLeaderElection()
264
264
 
@@ -279,7 +279,7 @@ export class OfflineExecutor {
279
279
  console.warn(`Failed to initialize offline executor:`, error)
280
280
  span.setAttribute(`result`, `failed`)
281
281
  this.initReject(
282
- error instanceof Error ? error : new Error(String(error))
282
+ error instanceof Error ? error : new Error(String(error)),
283
283
  )
284
284
  }
285
285
  })
@@ -303,7 +303,7 @@ export class OfflineExecutor {
303
303
  }
304
304
 
305
305
  createOfflineTransaction(
306
- options: CreateOfflineTransactionOptions
306
+ options: CreateOfflineTransactionOptions,
307
307
  ): Transaction | OfflineTransactionAPI {
308
308
  const mutationFn = this.config.mutationFns[options.mutationFnName]
309
309
 
@@ -331,7 +331,7 @@ export class OfflineExecutor {
331
331
  options,
332
332
  mutationFn,
333
333
  this.persistTransaction.bind(this),
334
- this
334
+ this,
335
335
  )
336
336
  }
337
337
 
@@ -364,14 +364,14 @@ export class OfflineExecutor {
364
364
  options,
365
365
  mutationFn,
366
366
  this.persistTransaction.bind(this),
367
- this
367
+ this,
368
368
  )
369
369
  return action(variables)
370
370
  }
371
371
  }
372
372
 
373
373
  private async persistTransaction(
374
- transaction: OfflineTransaction
374
+ transaction: OfflineTransaction,
375
375
  ): Promise<void> {
376
376
  // Wait for initialization to complete
377
377
  await this.initPromise
@@ -379,8 +379,8 @@ export class OfflineExecutor {
379
379
  return withNestedSpan(
380
380
  `executor.persistTransaction`,
381
381
  {
382
- "transaction.id": transaction.id,
383
- "transaction.mutationFnName": transaction.mutationFnName,
382
+ 'transaction.id': transaction.id,
383
+ 'transaction.mutationFnName': transaction.mutationFnName,
384
384
  },
385
385
  async (span) => {
386
386
  if (!this.isOfflineEnabled || !this.outbox || !this.executor) {
@@ -396,12 +396,12 @@ export class OfflineExecutor {
396
396
  } catch (error) {
397
397
  console.error(
398
398
  `Failed to persist offline transaction ${transaction.id}:`,
399
- error
399
+ error,
400
400
  )
401
401
  span.setAttribute(`result`, `failed`)
402
402
  throw error
403
403
  }
404
- }
404
+ },
405
405
  )
406
406
  }
407
407
 
@@ -1,11 +1,11 @@
1
- import { OnMutateMustBeSynchronousError } from "@tanstack/db"
2
- import { OfflineTransaction } from "./OfflineTransaction"
3
- import type { Transaction } from "@tanstack/db"
1
+ import { OnMutateMustBeSynchronousError } from '@tanstack/db'
2
+ import { OfflineTransaction } from './OfflineTransaction'
3
+ import type { Transaction } from '@tanstack/db'
4
4
  import type {
5
5
  CreateOfflineActionOptions,
6
6
  OfflineMutationFn,
7
7
  OfflineTransaction as OfflineTransactionType,
8
- } from "../types"
8
+ } from '../types'
9
9
 
10
10
  function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
11
11
  return (
@@ -19,7 +19,7 @@ export function createOfflineAction<T>(
19
19
  options: CreateOfflineActionOptions<T>,
20
20
  mutationFn: OfflineMutationFn,
21
21
  persistTransaction: (tx: OfflineTransactionType) => Promise<void>,
22
- executor: any
22
+ executor: any,
23
23
  ): (variables: T) => Transaction {
24
24
  const { mutationFnName, onMutate } = options
25
25
  console.log(`createOfflineAction 2`, options)
@@ -32,7 +32,7 @@ export function createOfflineAction<T>(
32
32
  },
33
33
  mutationFn,
34
34
  persistTransaction,
35
- executor
35
+ executor,
36
36
  )
37
37
 
38
38
  const transaction = offlineTransaction.mutate(() => {
@@ -1,11 +1,11 @@
1
- import { createTransaction } from "@tanstack/db"
2
- import { NonRetriableError } from "../types"
3
- import type { PendingMutation, Transaction } from "@tanstack/db"
1
+ import { createTransaction } from '@tanstack/db'
2
+ import { NonRetriableError } from '../types'
3
+ import type { PendingMutation, Transaction } from '@tanstack/db'
4
4
  import type {
5
5
  CreateOfflineTransactionOptions,
6
6
  OfflineMutationFn,
7
7
  OfflineTransaction as OfflineTransactionType,
8
- } from "../types"
8
+ } from '../types'
9
9
 
10
10
  export class OfflineTransaction {
11
11
  private offlineId: string
@@ -21,7 +21,7 @@ export class OfflineTransaction {
21
21
  options: CreateOfflineTransactionOptions,
22
22
  mutationFn: OfflineMutationFn,
23
23
  persistTransaction: (tx: OfflineTransactionType) => Promise<void>,
24
- executor: any
24
+ executor: any,
25
25
  ) {
26
26
  this.offlineId = crypto.randomUUID()
27
27
  this.mutationFnName = options.mutationFnName
@@ -54,7 +54,7 @@ export class OfflineTransaction {
54
54
  }
55
55
 
56
56
  const completionPromise = this.executor.waitForTransactionCompletion(
57
- this.offlineId
57
+ this.offlineId,
58
58
  )
59
59
 
60
60
  try {
@@ -1,4 +1,4 @@
1
- import type { OnlineDetector } from "../types"
1
+ import type { OnlineDetector } from '../types'
2
2
 
3
3
  export class DefaultOnlineDetector implements OnlineDetector {
4
4
  private listeners: Set<() => void> = new Set()
@@ -32,7 +32,7 @@ export class DefaultOnlineDetector implements OnlineDetector {
32
32
  window.removeEventListener(`online`, this.handleOnline)
33
33
  document.removeEventListener(
34
34
  `visibilitychange`,
35
- this.handleVisibilityChange
35
+ this.handleVisibilityChange,
36
36
  )
37
37
  }
38
38
  }
@@ -1,4 +1,4 @@
1
- import { BaseLeaderElection } from "./LeaderElection"
1
+ import { BaseLeaderElection } from './LeaderElection'
2
2
 
3
3
  interface LeaderMessage {
4
4
  type: `heartbeat` | `election` | `leadership-claim`
@@ -1,4 +1,4 @@
1
- import type { LeaderElection } from "../types"
1
+ import type { LeaderElection } from '../types'
2
2
 
3
3
  export abstract class BaseLeaderElection implements LeaderElection {
4
4
  protected isLeaderState = false
@@ -1,4 +1,4 @@
1
- import { BaseLeaderElection } from "./LeaderElection"
1
+ import { BaseLeaderElection } from './LeaderElection'
2
2
 
3
3
  export class WebLocksLeader extends BaseLeaderElection {
4
4
  private lockName: string
@@ -28,7 +28,7 @@ export class WebLocksLeader extends BaseLeaderElection {
28
28
  },
29
29
  (lock) => {
30
30
  return lock !== null
31
- }
31
+ },
32
32
  )
33
33
 
34
34
  if (!available) {
@@ -52,7 +52,7 @@ export class WebLocksLeader extends BaseLeaderElection {
52
52
  }
53
53
  })
54
54
  }
55
- }
55
+ },
56
56
  )
57
57
 
58
58
  return true
@@ -1,5 +1,5 @@
1
- import { withSyncSpan } from "../telemetry/tracer"
2
- import type { OfflineTransaction } from "../types"
1
+ import { withSyncSpan } from '../telemetry/tracer'
2
+ import type { OfflineTransaction } from '../types'
3
3
 
4
4
  export class KeyScheduler {
5
5
  private pendingTransactions: Array<OfflineTransaction> = []
@@ -9,16 +9,16 @@ export class KeyScheduler {
9
9
  withSyncSpan(
10
10
  `scheduler.schedule`,
11
11
  {
12
- "transaction.id": transaction.id,
12
+ 'transaction.id': transaction.id,
13
13
  queueLength: this.pendingTransactions.length,
14
14
  },
15
15
  () => {
16
16
  this.pendingTransactions.push(transaction)
17
17
  // Sort by creation time to maintain FIFO order
18
18
  this.pendingTransactions.sort(
19
- (a, b) => a.createdAt.getTime() - b.createdAt.getTime()
19
+ (a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
20
20
  )
21
- }
21
+ },
22
22
  )
23
23
  }
24
24
 
@@ -35,7 +35,7 @@ export class KeyScheduler {
35
35
 
36
36
  // Find the first transaction that's ready to run
37
37
  const readyTransaction = this.pendingTransactions.find((tx) =>
38
- this.isReadyToRun(tx)
38
+ this.isReadyToRun(tx),
39
39
  )
40
40
 
41
41
  if (readyTransaction) {
@@ -46,7 +46,7 @@ export class KeyScheduler {
46
46
  }
47
47
 
48
48
  return readyTransaction ? [readyTransaction] : []
49
- }
49
+ },
50
50
  )
51
51
  }
52
52
 
@@ -69,7 +69,7 @@ export class KeyScheduler {
69
69
 
70
70
  private removeTransaction(transaction: OfflineTransaction): void {
71
71
  const index = this.pendingTransactions.findIndex(
72
- (tx) => tx.id === transaction.id
72
+ (tx) => tx.id === transaction.id,
73
73
  )
74
74
  if (index >= 0) {
75
75
  this.pendingTransactions.splice(index, 1)
@@ -78,13 +78,13 @@ export class KeyScheduler {
78
78
 
79
79
  updateTransaction(transaction: OfflineTransaction): void {
80
80
  const index = this.pendingTransactions.findIndex(
81
- (tx) => tx.id === transaction.id
81
+ (tx) => tx.id === transaction.id,
82
82
  )
83
83
  if (index >= 0) {
84
84
  this.pendingTransactions[index] = transaction
85
85
  // Re-sort to maintain FIFO order after update
86
86
  this.pendingTransactions.sort(
87
- (a, b) => a.createdAt.getTime() - b.createdAt.getTime()
87
+ (a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
88
88
  )
89
89
  }
90
90
  }
@@ -109,7 +109,7 @@ export class KeyScheduler {
109
109
  updateTransactions(updatedTransactions: Array<OfflineTransaction>): void {
110
110
  for (const updatedTx of updatedTransactions) {
111
111
  const index = this.pendingTransactions.findIndex(
112
- (tx) => tx.id === updatedTx.id
112
+ (tx) => tx.id === updatedTx.id,
113
113
  )
114
114
  if (index >= 0) {
115
115
  this.pendingTransactions[index] = updatedTx
@@ -117,7 +117,7 @@ export class KeyScheduler {
117
117
  }
118
118
  // Re-sort to maintain FIFO order after updates
119
119
  this.pendingTransactions.sort(
120
- (a, b) => a.createdAt.getTime() - b.createdAt.getTime()
120
+ (a, b) => a.createdAt.getTime() - b.createdAt.getTime(),
121
121
  )
122
122
  }
123
123
  }
@@ -1,9 +1,9 @@
1
- import { DefaultRetryPolicy } from "../retry/RetryPolicy"
2
- import { NonRetriableError } from "../types"
3
- import { withNestedSpan } from "../telemetry/tracer"
4
- import type { KeyScheduler } from "./KeyScheduler"
5
- import type { OutboxManager } from "../outbox/OutboxManager"
6
- import type { OfflineConfig, OfflineTransaction } from "../types"
1
+ import { DefaultRetryPolicy } from '../retry/RetryPolicy'
2
+ import { NonRetriableError } from '../types'
3
+ import { withNestedSpan } from '../telemetry/tracer'
4
+ import type { KeyScheduler } from './KeyScheduler'
5
+ import type { OutboxManager } from '../outbox/OutboxManager'
6
+ import type { OfflineConfig, OfflineTransaction } from '../types'
7
7
 
8
8
  const HANDLED_EXECUTION_ERROR = Symbol(`HandledExecutionError`)
9
9
 
@@ -21,7 +21,7 @@ export class TransactionExecutor {
21
21
  scheduler: KeyScheduler,
22
22
  outbox: OutboxManager,
23
23
  config: OfflineConfig,
24
- offlineExecutor: any
24
+ offlineExecutor: any,
25
25
  ) {
26
26
  this.scheduler = scheduler
27
27
  this.outbox = outbox
@@ -62,7 +62,7 @@ export class TransactionExecutor {
62
62
  }
63
63
 
64
64
  const executions = batch.map((transaction) =>
65
- this.executeTransaction(transaction)
65
+ this.executeTransaction(transaction),
66
66
  )
67
67
  await Promise.allSettled(executions)
68
68
  }
@@ -72,16 +72,16 @@ export class TransactionExecutor {
72
72
  }
73
73
 
74
74
  private async executeTransaction(
75
- transaction: OfflineTransaction
75
+ transaction: OfflineTransaction,
76
76
  ): Promise<void> {
77
77
  try {
78
78
  await withNestedSpan(
79
79
  `transaction.execute`,
80
80
  {
81
- "transaction.id": transaction.id,
82
- "transaction.mutationFnName": transaction.mutationFnName,
83
- "transaction.retryCount": transaction.retryCount,
84
- "transaction.keyCount": transaction.keys.length,
81
+ 'transaction.id': transaction.id,
82
+ 'transaction.mutationFnName': transaction.mutationFnName,
83
+ 'transaction.retryCount': transaction.retryCount,
84
+ 'transaction.keyCount': transaction.keys.length,
85
85
  },
86
86
  async (span) => {
87
87
  this.scheduler.markStarted(transaction)
@@ -108,7 +108,7 @@ export class TransactionExecutor {
108
108
  ;(err as any)[HANDLED_EXECUTION_ERROR] = true
109
109
  throw err
110
110
  }
111
- }
111
+ },
112
112
  )
113
113
  } catch (error) {
114
114
  if (
@@ -151,19 +151,19 @@ export class TransactionExecutor {
151
151
 
152
152
  private async handleError(
153
153
  transaction: OfflineTransaction,
154
- error: Error
154
+ error: Error,
155
155
  ): Promise<void> {
156
156
  return withNestedSpan(
157
157
  `transaction.handleError`,
158
158
  {
159
- "transaction.id": transaction.id,
160
- "error.name": error.name,
161
- "error.message": error.message,
159
+ 'transaction.id': transaction.id,
160
+ 'error.name': error.name,
161
+ 'error.message': error.message,
162
162
  },
163
163
  async (span) => {
164
164
  const shouldRetry = this.retryPolicy.shouldRetry(
165
165
  error,
166
- transaction.retryCount
166
+ transaction.retryCount,
167
167
  )
168
168
 
169
169
  span.setAttribute(`shouldRetry`, shouldRetry)
@@ -173,7 +173,7 @@ export class TransactionExecutor {
173
173
  await this.outbox.remove(transaction.id)
174
174
  console.warn(
175
175
  `Transaction ${transaction.id} failed permanently:`,
176
- error
176
+ error,
177
177
  )
178
178
 
179
179
  span.setAttribute(`result`, `permanent_failure`)
@@ -211,7 +211,7 @@ export class TransactionExecutor {
211
211
 
212
212
  // Schedule retry timer
213
213
  this.scheduleNextRetry()
214
- }
214
+ },
215
215
  )
216
216
  }
217
217
 
@@ -234,7 +234,7 @@ export class TransactionExecutor {
234
234
  this.scheduleNextRetry()
235
235
 
236
236
  const removedTransactions = transactions.filter(
237
- (tx) => !filteredTransactions.some((filtered) => filtered.id === tx.id)
237
+ (tx) => !filteredTransactions.some((filtered) => filtered.id === tx.id),
238
238
  )
239
239
 
240
240
  if (removedTransactions.length > 0) {
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Main API
2
- export { OfflineExecutor, startOfflineExecutor } from "./OfflineExecutor"
2
+ export { OfflineExecutor, startOfflineExecutor } from './OfflineExecutor'
3
3
 
4
4
  // Types
5
5
  export type {
@@ -16,35 +16,35 @@ export type {
16
16
  CreateOfflineActionOptions,
17
17
  SerializedError,
18
18
  SerializedMutation,
19
- } from "./types"
19
+ } from './types'
20
20
 
21
- export { NonRetriableError } from "./types"
21
+ export { NonRetriableError } from './types'
22
22
 
23
23
  // Storage adapters
24
- export { IndexedDBAdapter } from "./storage/IndexedDBAdapter"
25
- export { LocalStorageAdapter } from "./storage/LocalStorageAdapter"
24
+ export { IndexedDBAdapter } from './storage/IndexedDBAdapter'
25
+ export { LocalStorageAdapter } from './storage/LocalStorageAdapter'
26
26
 
27
27
  // Retry policies
28
- export { DefaultRetryPolicy } from "./retry/RetryPolicy"
29
- export { BackoffCalculator } from "./retry/BackoffCalculator"
28
+ export { DefaultRetryPolicy } from './retry/RetryPolicy'
29
+ export { BackoffCalculator } from './retry/BackoffCalculator'
30
30
 
31
31
  // Coordination
32
- export { WebLocksLeader } from "./coordination/WebLocksLeader"
33
- export { BroadcastChannelLeader } from "./coordination/BroadcastChannelLeader"
32
+ export { WebLocksLeader } from './coordination/WebLocksLeader'
33
+ export { BroadcastChannelLeader } from './coordination/BroadcastChannelLeader'
34
34
 
35
35
  // Connectivity
36
- export { DefaultOnlineDetector } from "./connectivity/OnlineDetector"
36
+ export { DefaultOnlineDetector } from './connectivity/OnlineDetector'
37
37
 
38
38
  // API components
39
- export { OfflineTransaction as OfflineTransactionAPI } from "./api/OfflineTransaction"
40
- export { createOfflineAction } from "./api/OfflineAction"
39
+ export { OfflineTransaction as OfflineTransactionAPI } from './api/OfflineTransaction'
40
+ export { createOfflineAction } from './api/OfflineAction'
41
41
 
42
42
  // Outbox management
43
- export { OutboxManager } from "./outbox/OutboxManager"
44
- export { TransactionSerializer } from "./outbox/TransactionSerializer"
43
+ export { OutboxManager } from './outbox/OutboxManager'
44
+ export { TransactionSerializer } from './outbox/TransactionSerializer'
45
45
 
46
46
  // Execution engine
47
- export { KeyScheduler } from "./executor/KeyScheduler"
48
- export { TransactionExecutor } from "./executor/TransactionExecutor"
47
+ export { KeyScheduler } from './executor/KeyScheduler'
48
+ export { TransactionExecutor } from './executor/TransactionExecutor'
49
49
 
50
50
  // Replay