@tanstack/offline-transactions 1.1.0 → 1.1.1

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 (46) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/OfflineExecutor.cjs +72 -80
  3. package/dist/cjs/OfflineExecutor.cjs.map +1 -1
  4. package/dist/cjs/OfflineExecutor.d.cts +6 -0
  5. package/dist/cjs/api/OfflineAction.cjs +4 -15
  6. package/dist/cjs/api/OfflineAction.cjs.map +1 -1
  7. package/dist/cjs/api/OfflineTransaction.cjs +54 -51
  8. package/dist/cjs/api/OfflineTransaction.cjs.map +1 -1
  9. package/dist/cjs/api/OfflineTransaction.d.cts +2 -0
  10. package/dist/cjs/executor/KeyScheduler.cjs +12 -38
  11. package/dist/cjs/executor/KeyScheduler.cjs.map +1 -1
  12. package/dist/cjs/executor/TransactionExecutor.cjs +106 -161
  13. package/dist/cjs/executor/TransactionExecutor.cjs.map +1 -1
  14. package/dist/cjs/outbox/OutboxManager.cjs +48 -77
  15. package/dist/cjs/outbox/OutboxManager.cjs.map +1 -1
  16. package/dist/esm/OfflineExecutor.d.ts +6 -0
  17. package/dist/esm/OfflineExecutor.js +72 -80
  18. package/dist/esm/OfflineExecutor.js.map +1 -1
  19. package/dist/esm/api/OfflineAction.js +4 -15
  20. package/dist/esm/api/OfflineAction.js.map +1 -1
  21. package/dist/esm/api/OfflineTransaction.d.ts +2 -0
  22. package/dist/esm/api/OfflineTransaction.js +54 -51
  23. package/dist/esm/api/OfflineTransaction.js.map +1 -1
  24. package/dist/esm/executor/KeyScheduler.js +12 -38
  25. package/dist/esm/executor/KeyScheduler.js.map +1 -1
  26. package/dist/esm/executor/TransactionExecutor.js +106 -161
  27. package/dist/esm/executor/TransactionExecutor.js.map +1 -1
  28. package/dist/esm/outbox/OutboxManager.js +48 -77
  29. package/dist/esm/outbox/OutboxManager.js.map +1 -1
  30. package/package.json +2 -2
  31. package/src/OfflineExecutor.ts +93 -94
  32. package/src/api/OfflineAction.ts +4 -22
  33. package/src/api/OfflineTransaction.ts +78 -66
  34. package/src/executor/KeyScheduler.ts +18 -47
  35. package/src/executor/TransactionExecutor.ts +122 -186
  36. package/src/outbox/OutboxManager.ts +54 -86
  37. package/dist/cjs/retry/NonRetriableError.d.cts +0 -1
  38. package/dist/cjs/telemetry/tracer.cjs +0 -27
  39. package/dist/cjs/telemetry/tracer.cjs.map +0 -1
  40. package/dist/cjs/telemetry/tracer.d.cts +0 -30
  41. package/dist/esm/retry/NonRetriableError.d.ts +0 -1
  42. package/dist/esm/telemetry/tracer.d.ts +0 -30
  43. package/dist/esm/telemetry/tracer.js +0 -27
  44. package/dist/esm/telemetry/tracer.js.map +0 -1
  45. package/src/retry/NonRetriableError.ts +0 -1
  46. package/src/telemetry/tracer.ts +0 -66
@@ -1,4 +1,3 @@
1
- import { withSpan } from '../telemetry/tracer'
2
1
  import {
3
2
  MissingTemporalConstructorError,
4
3
  TransactionSerializer,
@@ -49,45 +48,26 @@ export class OutboxManager {
49
48
  }
50
49
 
51
50
  async add(transaction: OfflineTransaction): Promise<void> {
52
- return withSpan(
53
- `outbox.add`,
54
- {
55
- 'transaction.id': transaction.id,
56
- 'transaction.mutationFnName': transaction.mutationFnName,
57
- 'transaction.keyCount': transaction.keys.length,
58
- },
59
- async () => {
60
- const key = this.getStorageKey(transaction.id)
61
- const serialized = this.serializer.serialize(transaction)
62
- await this.storage.set(key, serialized)
63
- },
64
- )
51
+ const key = this.getStorageKey(transaction.id)
52
+ const serialized = this.serializer.serialize(transaction)
53
+ await this.storage.set(key, serialized)
65
54
  }
66
55
 
67
56
  async get(id: string): Promise<OfflineTransaction | null> {
68
- return withSpan(`outbox.get`, { 'transaction.id': id }, async (span) => {
69
- const key = this.getStorageKey(id)
70
- const data = await this.storage.get(key)
71
-
72
- if (!data) {
73
- span.setAttribute(`result`, `not_found`)
74
- return null
75
- }
57
+ const key = this.getStorageKey(id)
58
+ const data = await this.storage.get(key)
59
+ if (!data) return null
76
60
 
77
- try {
78
- const transaction = this.serializer.deserialize(data)
79
- span.setAttribute(`result`, `found`)
80
- return transaction
81
- } catch (error) {
82
- if (error instanceof MissingTemporalConstructorError) {
83
- error.message = `transaction ${id}: ${error.message}`
84
- throw error
85
- }
86
- console.warn(`Failed to deserialize transaction ${id}:`, error)
87
- span.setAttribute(`result`, `deserialize_error`)
88
- return null
61
+ try {
62
+ return this.serializer.deserialize(data)
63
+ } catch (error) {
64
+ if (error instanceof MissingTemporalConstructorError) {
65
+ error.message = `transaction ${id}: ${error.message}`
66
+ throw error
89
67
  }
90
- })
68
+ console.warn(`Failed to deserialize transaction ${id}:`, error)
69
+ return null
70
+ }
91
71
  }
92
72
 
93
73
  async getAll(): Promise<Array<OfflineTransaction>> {
@@ -100,44 +80,40 @@ export class OutboxManager {
100
80
  const read = { revision: this.removalRevision }
101
81
  this.activeReads.add(read)
102
82
  try {
103
- return await withSpan(`outbox.getAll`, {}, async (span) => {
104
- const keys = await this.storage.keys()
105
- const transactionKeys = keys.filter((key) =>
106
- key.startsWith(this.keyPrefix),
107
- )
108
-
109
- span.setAttribute(`transactionCount`, transactionKeys.length)
110
-
111
- const transactions: Array<OfflineTransaction> = []
112
-
113
- for (const key of transactionKeys) {
114
- const data = await this.storage.get(key)
115
- if (data) {
116
- try {
117
- const transaction = this.serializer.deserialize(data)
118
- transactions.push(transaction)
119
- } catch (error) {
120
- if (error instanceof MissingTemporalConstructorError) {
121
- error.message = `transaction ${key.slice(this.keyPrefix.length)}: ${error.message}`
122
- throw error
123
- }
124
- console.warn(
125
- `Failed to deserialize transaction from key ${key}:`,
126
- error,
127
- )
83
+ const keys = await this.storage.keys()
84
+ const transactionKeys = keys.filter((key) =>
85
+ key.startsWith(this.keyPrefix),
86
+ )
87
+
88
+ const transactions: Array<OfflineTransaction> = []
89
+
90
+ for (const key of transactionKeys) {
91
+ const data = await this.storage.get(key)
92
+ if (data) {
93
+ try {
94
+ const transaction = this.serializer.deserialize(data)
95
+ transactions.push(transaction)
96
+ } catch (error) {
97
+ if (error instanceof MissingTemporalConstructorError) {
98
+ error.message = `transaction ${key.slice(this.keyPrefix.length)}: ${error.message}`
99
+ throw error
128
100
  }
101
+ console.warn(
102
+ `Failed to deserialize transaction from key ${key}:`,
103
+ error,
104
+ )
129
105
  }
130
106
  }
107
+ }
131
108
 
132
- const currentTransactions = transactions
133
- .filter(
134
- ({ id }) => (this.removedAtRevision.get(id) ?? 0) <= read.revision,
135
- )
136
- .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
137
- // Keep the read registered until replay admission completes. A durable
138
- // removal cannot land between filtering and this synchronous consumer.
139
- return consume(currentTransactions)
140
- })
109
+ const currentTransactions = transactions
110
+ .filter(
111
+ ({ id }) => (this.removedAtRevision.get(id) ?? 0) <= read.revision,
112
+ )
113
+ .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
114
+ // Keep the read registered until replay admission completes. A durable
115
+ // removal cannot land between filtering and this synchronous consumer.
116
+ return await consume(currentTransactions)
141
117
  } finally {
142
118
  this.activeReads.delete(read)
143
119
  this.pruneRemovalHistory()
@@ -157,29 +133,21 @@ export class OutboxManager {
157
133
  id: string,
158
134
  updates: Partial<OfflineTransaction>,
159
135
  ): Promise<void> {
160
- return withSpan(`outbox.update`, { 'transaction.id': id }, async () => {
161
- const existing = await this.get(id)
162
- if (!existing) {
163
- throw new OutboxTransactionNotFoundError(`Transaction ${id} not found`)
164
- }
165
-
166
- const updated = { ...existing, ...updates }
167
- await this.add(updated)
168
- })
136
+ const existing = await this.get(id)
137
+ if (!existing) {
138
+ throw new OutboxTransactionNotFoundError(`Transaction ${id} not found`)
139
+ }
140
+ await this.add({ ...existing, ...updates })
169
141
  }
170
142
 
171
143
  async remove(id: string): Promise<void> {
172
- return withSpan(`outbox.remove`, { 'transaction.id': id }, async () => {
173
- const key = this.getStorageKey(id)
174
- await this.storage.delete(key)
175
- this.recordRemovals([id])
176
- })
144
+ const key = this.getStorageKey(id)
145
+ await this.storage.delete(key)
146
+ this.recordRemovals([id])
177
147
  }
178
148
 
179
149
  async removeMany(ids: Array<string>): Promise<void> {
180
- return withSpan(`outbox.removeMany`, { count: ids.length }, async () => {
181
- await Promise.all(ids.map((id) => this.remove(id)))
182
- })
150
+ await Promise.all(ids.map((id) => this.remove(id)))
183
151
  }
184
152
 
185
153
  async clear(): Promise<void> {
@@ -1 +0,0 @@
1
- export { NonRetriableError } from '../types.cjs';
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const noopSpan = {
4
- setAttribute: () => {
5
- },
6
- setAttributes: () => {
7
- },
8
- setStatus: () => {
9
- },
10
- recordException: () => {
11
- },
12
- end: () => {
13
- }
14
- };
15
- async function withSpan(name, attrs, fn, _options) {
16
- return await fn(noopSpan);
17
- }
18
- async function withNestedSpan(name, attrs, fn, _options) {
19
- return await fn(noopSpan);
20
- }
21
- function withSyncSpan(name, attrs, fn, _options) {
22
- return fn(noopSpan);
23
- }
24
- exports.withNestedSpan = withNestedSpan;
25
- exports.withSpan = withSpan;
26
- exports.withSyncSpan = withSyncSpan;
27
- //# sourceMappingURL=tracer.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tracer.cjs","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,30 +0,0 @@
1
- export interface SpanAttrs {
2
- [key: string]: string | number | boolean | undefined;
3
- }
4
- interface WithSpanOptions {
5
- parentContext?: any;
6
- }
7
- /**
8
- * Lightweight span wrapper with error handling.
9
- * No-op implementation - telemetry has been removed.
10
- *
11
- * By default, creates spans at the current context level (siblings).
12
- * Use withNestedSpan if you want parent-child relationships.
13
- */
14
- export declare function withSpan<T>(name: string, attrs: SpanAttrs, fn: (span: any) => Promise<T>, _options?: WithSpanOptions): Promise<T>;
15
- /**
16
- * Like withSpan but propagates context so child spans nest properly.
17
- * No-op implementation - telemetry has been removed.
18
- */
19
- export declare function withNestedSpan<T>(name: string, attrs: SpanAttrs, fn: (span: any) => Promise<T>, _options?: WithSpanOptions): Promise<T>;
20
- /**
21
- * Creates a synchronous span for non-async operations
22
- * No-op implementation - telemetry has been removed.
23
- */
24
- export declare function withSyncSpan<T>(name: string, attrs: SpanAttrs, fn: (span: any) => T, _options?: WithSpanOptions): T;
25
- /**
26
- * Get the current tracer instance
27
- * No-op implementation - telemetry has been removed.
28
- */
29
- export declare function getTracer(): null;
30
- export {};
@@ -1 +0,0 @@
1
- export { NonRetriableError } from '../types.js';
@@ -1,30 +0,0 @@
1
- export interface SpanAttrs {
2
- [key: string]: string | number | boolean | undefined;
3
- }
4
- interface WithSpanOptions {
5
- parentContext?: any;
6
- }
7
- /**
8
- * Lightweight span wrapper with error handling.
9
- * No-op implementation - telemetry has been removed.
10
- *
11
- * By default, creates spans at the current context level (siblings).
12
- * Use withNestedSpan if you want parent-child relationships.
13
- */
14
- export declare function withSpan<T>(name: string, attrs: SpanAttrs, fn: (span: any) => Promise<T>, _options?: WithSpanOptions): Promise<T>;
15
- /**
16
- * Like withSpan but propagates context so child spans nest properly.
17
- * No-op implementation - telemetry has been removed.
18
- */
19
- export declare function withNestedSpan<T>(name: string, attrs: SpanAttrs, fn: (span: any) => Promise<T>, _options?: WithSpanOptions): Promise<T>;
20
- /**
21
- * Creates a synchronous span for non-async operations
22
- * No-op implementation - telemetry has been removed.
23
- */
24
- export declare function withSyncSpan<T>(name: string, attrs: SpanAttrs, fn: (span: any) => T, _options?: WithSpanOptions): T;
25
- /**
26
- * Get the current tracer instance
27
- * No-op implementation - telemetry has been removed.
28
- */
29
- export declare function getTracer(): null;
30
- export {};
@@ -1,27 +0,0 @@
1
- const noopSpan = {
2
- setAttribute: () => {
3
- },
4
- setAttributes: () => {
5
- },
6
- setStatus: () => {
7
- },
8
- recordException: () => {
9
- },
10
- end: () => {
11
- }
12
- };
13
- async function withSpan(name, attrs, fn, _options) {
14
- return await fn(noopSpan);
15
- }
16
- async function withNestedSpan(name, attrs, fn, _options) {
17
- return await fn(noopSpan);
18
- }
19
- function withSyncSpan(name, attrs, fn, _options) {
20
- return fn(noopSpan);
21
- }
22
- export {
23
- withNestedSpan,
24
- withSpan,
25
- withSyncSpan
26
- };
27
- //# sourceMappingURL=tracer.js.map
@@ -1 +0,0 @@
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 +0,0 @@
1
- export { NonRetriableError } from '../types'
@@ -1,66 +0,0 @@
1
- export interface SpanAttrs {
2
- [key: string]: string | number | boolean | undefined
3
- }
4
-
5
- interface WithSpanOptions {
6
- parentContext?: any
7
- }
8
-
9
- // No-op span implementation
10
- const noopSpan = {
11
- setAttribute: () => {},
12
- setAttributes: () => {},
13
- setStatus: () => {},
14
- recordException: () => {},
15
- end: () => {},
16
- }
17
-
18
- /**
19
- * Lightweight span wrapper with error handling.
20
- * No-op implementation - telemetry has been removed.
21
- *
22
- * By default, creates spans at the current context level (siblings).
23
- * Use withNestedSpan if you want parent-child relationships.
24
- */
25
- export async function withSpan<T>(
26
- name: string,
27
- attrs: SpanAttrs,
28
- fn: (span: any) => Promise<T>,
29
- _options?: WithSpanOptions,
30
- ): Promise<T> {
31
- return await fn(noopSpan)
32
- }
33
-
34
- /**
35
- * Like withSpan but propagates context so child spans nest properly.
36
- * No-op implementation - telemetry has been removed.
37
- */
38
- export async function withNestedSpan<T>(
39
- name: string,
40
- attrs: SpanAttrs,
41
- fn: (span: any) => Promise<T>,
42
- _options?: WithSpanOptions,
43
- ): Promise<T> {
44
- return await fn(noopSpan)
45
- }
46
-
47
- /**
48
- * Creates a synchronous span for non-async operations
49
- * No-op implementation - telemetry has been removed.
50
- */
51
- export function withSyncSpan<T>(
52
- name: string,
53
- attrs: SpanAttrs,
54
- fn: (span: any) => T,
55
- _options?: WithSpanOptions,
56
- ): T {
57
- return fn(noopSpan)
58
- }
59
-
60
- /**
61
- * Get the current tracer instance
62
- * No-op implementation - telemetry has been removed.
63
- */
64
- export function getTracer() {
65
- return null
66
- }