@tanstack/offline-transactions 0.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 (138) hide show
  1. package/README.md +219 -0
  2. package/dist/cjs/OfflineExecutor.cjs +266 -0
  3. package/dist/cjs/OfflineExecutor.cjs.map +1 -0
  4. package/dist/cjs/OfflineExecutor.d.cts +39 -0
  5. package/dist/cjs/api/OfflineAction.cjs +47 -0
  6. package/dist/cjs/api/OfflineAction.cjs.map +1 -0
  7. package/dist/cjs/api/OfflineAction.d.cts +3 -0
  8. package/dist/cjs/api/OfflineTransaction.cjs +96 -0
  9. package/dist/cjs/api/OfflineTransaction.cjs.map +1 -0
  10. package/dist/cjs/api/OfflineTransaction.d.cts +18 -0
  11. package/dist/cjs/connectivity/OnlineDetector.cjs +73 -0
  12. package/dist/cjs/connectivity/OnlineDetector.cjs.map +1 -0
  13. package/dist/cjs/connectivity/OnlineDetector.d.cts +15 -0
  14. package/dist/cjs/coordination/BroadcastChannelLeader.cjs +146 -0
  15. package/dist/cjs/coordination/BroadcastChannelLeader.cjs.map +1 -0
  16. package/dist/cjs/coordination/BroadcastChannelLeader.d.cts +26 -0
  17. package/dist/cjs/coordination/LeaderElection.cjs +31 -0
  18. package/dist/cjs/coordination/LeaderElection.cjs.map +1 -0
  19. package/dist/cjs/coordination/LeaderElection.d.cts +10 -0
  20. package/dist/cjs/coordination/WebLocksLeader.cjs +71 -0
  21. package/dist/cjs/coordination/WebLocksLeader.cjs.map +1 -0
  22. package/dist/cjs/coordination/WebLocksLeader.d.cts +10 -0
  23. package/dist/cjs/executor/KeyScheduler.cjs +106 -0
  24. package/dist/cjs/executor/KeyScheduler.cjs.map +1 -0
  25. package/dist/cjs/executor/KeyScheduler.d.cts +18 -0
  26. package/dist/cjs/executor/TransactionExecutor.cjs +236 -0
  27. package/dist/cjs/executor/TransactionExecutor.cjs.map +1 -0
  28. package/dist/cjs/executor/TransactionExecutor.d.cts +28 -0
  29. package/dist/cjs/index.cjs +34 -0
  30. package/dist/cjs/index.cjs.map +1 -0
  31. package/dist/cjs/index.d.cts +16 -0
  32. package/dist/cjs/outbox/OutboxManager.cjs +114 -0
  33. package/dist/cjs/outbox/OutboxManager.cjs.map +1 -0
  34. package/dist/cjs/outbox/OutboxManager.d.cts +18 -0
  35. package/dist/cjs/outbox/TransactionSerializer.cjs +135 -0
  36. package/dist/cjs/outbox/TransactionSerializer.cjs.map +1 -0
  37. package/dist/cjs/outbox/TransactionSerializer.d.cts +15 -0
  38. package/dist/cjs/retry/BackoffCalculator.cjs +14 -0
  39. package/dist/cjs/retry/BackoffCalculator.cjs.map +1 -0
  40. package/dist/cjs/retry/BackoffCalculator.d.cts +5 -0
  41. package/dist/cjs/retry/NonRetriableError.d.cts +1 -0
  42. package/dist/cjs/retry/RetryPolicy.cjs +33 -0
  43. package/dist/cjs/retry/RetryPolicy.cjs.map +1 -0
  44. package/dist/cjs/retry/RetryPolicy.d.cts +8 -0
  45. package/dist/cjs/storage/IndexedDBAdapter.cjs +104 -0
  46. package/dist/cjs/storage/IndexedDBAdapter.cjs.map +1 -0
  47. package/dist/cjs/storage/IndexedDBAdapter.d.cts +14 -0
  48. package/dist/cjs/storage/LocalStorageAdapter.cjs +71 -0
  49. package/dist/cjs/storage/LocalStorageAdapter.cjs.map +1 -0
  50. package/dist/cjs/storage/LocalStorageAdapter.d.cts +11 -0
  51. package/dist/cjs/storage/StorageAdapter.cjs +6 -0
  52. package/dist/cjs/storage/StorageAdapter.cjs.map +1 -0
  53. package/dist/cjs/storage/StorageAdapter.d.cts +9 -0
  54. package/dist/cjs/telemetry/tracer.cjs +91 -0
  55. package/dist/cjs/telemetry/tracer.cjs.map +1 -0
  56. package/dist/cjs/telemetry/tracer.d.cts +29 -0
  57. package/dist/cjs/types.cjs +10 -0
  58. package/dist/cjs/types.cjs.map +1 -0
  59. package/dist/cjs/types.d.cts +101 -0
  60. package/dist/esm/OfflineExecutor.d.ts +39 -0
  61. package/dist/esm/OfflineExecutor.js +266 -0
  62. package/dist/esm/OfflineExecutor.js.map +1 -0
  63. package/dist/esm/api/OfflineAction.d.ts +3 -0
  64. package/dist/esm/api/OfflineAction.js +47 -0
  65. package/dist/esm/api/OfflineAction.js.map +1 -0
  66. package/dist/esm/api/OfflineTransaction.d.ts +18 -0
  67. package/dist/esm/api/OfflineTransaction.js +96 -0
  68. package/dist/esm/api/OfflineTransaction.js.map +1 -0
  69. package/dist/esm/connectivity/OnlineDetector.d.ts +15 -0
  70. package/dist/esm/connectivity/OnlineDetector.js +73 -0
  71. package/dist/esm/connectivity/OnlineDetector.js.map +1 -0
  72. package/dist/esm/coordination/BroadcastChannelLeader.d.ts +26 -0
  73. package/dist/esm/coordination/BroadcastChannelLeader.js +146 -0
  74. package/dist/esm/coordination/BroadcastChannelLeader.js.map +1 -0
  75. package/dist/esm/coordination/LeaderElection.d.ts +10 -0
  76. package/dist/esm/coordination/LeaderElection.js +31 -0
  77. package/dist/esm/coordination/LeaderElection.js.map +1 -0
  78. package/dist/esm/coordination/WebLocksLeader.d.ts +10 -0
  79. package/dist/esm/coordination/WebLocksLeader.js +71 -0
  80. package/dist/esm/coordination/WebLocksLeader.js.map +1 -0
  81. package/dist/esm/executor/KeyScheduler.d.ts +18 -0
  82. package/dist/esm/executor/KeyScheduler.js +106 -0
  83. package/dist/esm/executor/KeyScheduler.js.map +1 -0
  84. package/dist/esm/executor/TransactionExecutor.d.ts +28 -0
  85. package/dist/esm/executor/TransactionExecutor.js +236 -0
  86. package/dist/esm/executor/TransactionExecutor.js.map +1 -0
  87. package/dist/esm/index.d.ts +16 -0
  88. package/dist/esm/index.js +34 -0
  89. package/dist/esm/index.js.map +1 -0
  90. package/dist/esm/outbox/OutboxManager.d.ts +18 -0
  91. package/dist/esm/outbox/OutboxManager.js +114 -0
  92. package/dist/esm/outbox/OutboxManager.js.map +1 -0
  93. package/dist/esm/outbox/TransactionSerializer.d.ts +15 -0
  94. package/dist/esm/outbox/TransactionSerializer.js +135 -0
  95. package/dist/esm/outbox/TransactionSerializer.js.map +1 -0
  96. package/dist/esm/retry/BackoffCalculator.d.ts +5 -0
  97. package/dist/esm/retry/BackoffCalculator.js +14 -0
  98. package/dist/esm/retry/BackoffCalculator.js.map +1 -0
  99. package/dist/esm/retry/NonRetriableError.d.ts +1 -0
  100. package/dist/esm/retry/RetryPolicy.d.ts +8 -0
  101. package/dist/esm/retry/RetryPolicy.js +33 -0
  102. package/dist/esm/retry/RetryPolicy.js.map +1 -0
  103. package/dist/esm/storage/IndexedDBAdapter.d.ts +14 -0
  104. package/dist/esm/storage/IndexedDBAdapter.js +104 -0
  105. package/dist/esm/storage/IndexedDBAdapter.js.map +1 -0
  106. package/dist/esm/storage/LocalStorageAdapter.d.ts +11 -0
  107. package/dist/esm/storage/LocalStorageAdapter.js +71 -0
  108. package/dist/esm/storage/LocalStorageAdapter.js.map +1 -0
  109. package/dist/esm/storage/StorageAdapter.d.ts +9 -0
  110. package/dist/esm/storage/StorageAdapter.js +6 -0
  111. package/dist/esm/storage/StorageAdapter.js.map +1 -0
  112. package/dist/esm/telemetry/tracer.d.ts +29 -0
  113. package/dist/esm/telemetry/tracer.js +91 -0
  114. package/dist/esm/telemetry/tracer.js.map +1 -0
  115. package/dist/esm/types.d.ts +101 -0
  116. package/dist/esm/types.js +10 -0
  117. package/dist/esm/types.js.map +1 -0
  118. package/package.json +66 -0
  119. package/src/OfflineExecutor.ts +360 -0
  120. package/src/api/OfflineAction.ts +68 -0
  121. package/src/api/OfflineTransaction.ts +134 -0
  122. package/src/connectivity/OnlineDetector.ts +87 -0
  123. package/src/coordination/BroadcastChannelLeader.ts +181 -0
  124. package/src/coordination/LeaderElection.ts +35 -0
  125. package/src/coordination/WebLocksLeader.ts +82 -0
  126. package/src/executor/KeyScheduler.ts +123 -0
  127. package/src/executor/TransactionExecutor.ts +330 -0
  128. package/src/index.ts +47 -0
  129. package/src/outbox/OutboxManager.ts +141 -0
  130. package/src/outbox/TransactionSerializer.ts +163 -0
  131. package/src/retry/BackoffCalculator.ts +13 -0
  132. package/src/retry/NonRetriableError.ts +1 -0
  133. package/src/retry/RetryPolicy.ts +41 -0
  134. package/src/storage/IndexedDBAdapter.ts +119 -0
  135. package/src/storage/LocalStorageAdapter.ts +79 -0
  136. package/src/storage/StorageAdapter.ts +11 -0
  137. package/src/telemetry/tracer.ts +156 -0
  138. package/src/types.ts +133 -0
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class TransactionSerializer {
4
+ constructor(collections) {
5
+ this.collections = collections;
6
+ this.collectionIdToKey = /* @__PURE__ */ new Map();
7
+ for (const [key, collection] of Object.entries(collections)) {
8
+ this.collectionIdToKey.set(collection.id, key);
9
+ }
10
+ }
11
+ serialize(transaction) {
12
+ const serialized = {
13
+ ...transaction,
14
+ createdAt: transaction.createdAt,
15
+ mutations: transaction.mutations.map(
16
+ (mutation) => this.serializeMutation(mutation)
17
+ )
18
+ };
19
+ return JSON.stringify(serialized, (key, value) => {
20
+ if (value instanceof Date) {
21
+ return value.toISOString();
22
+ }
23
+ return value;
24
+ });
25
+ }
26
+ deserialize(data) {
27
+ const parsed = JSON.parse(
28
+ data,
29
+ (key, value) => {
30
+ if (typeof value === `string` && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
31
+ return new Date(value);
32
+ }
33
+ return value;
34
+ }
35
+ );
36
+ return {
37
+ ...parsed,
38
+ mutations: parsed.mutations.map(
39
+ (mutationData) => this.deserializeMutation(mutationData)
40
+ )
41
+ };
42
+ }
43
+ serializeMutation(mutation) {
44
+ const registryKey = this.collectionIdToKey.get(mutation.collection.id);
45
+ if (!registryKey) {
46
+ throw new Error(
47
+ `Collection with id ${mutation.collection.id} not found in registry`
48
+ );
49
+ }
50
+ return {
51
+ globalKey: mutation.globalKey,
52
+ type: mutation.type,
53
+ modified: this.serializeValue(mutation.modified),
54
+ original: this.serializeValue(mutation.original),
55
+ collectionId: registryKey
56
+ // Store registry key instead of collection.id
57
+ };
58
+ }
59
+ deserializeMutation(data) {
60
+ const collection = this.collections[data.collectionId];
61
+ if (!collection) {
62
+ throw new Error(`Collection with id ${data.collectionId} not found`);
63
+ }
64
+ return {
65
+ globalKey: data.globalKey,
66
+ type: data.type,
67
+ modified: this.deserializeValue(data.modified),
68
+ original: this.deserializeValue(data.original),
69
+ collection,
70
+ // These fields would need to be reconstructed by the executor
71
+ mutationId: ``,
72
+ // Will be regenerated
73
+ key: null,
74
+ // Will be extracted from the data
75
+ changes: {},
76
+ // Will be recalculated
77
+ metadata: void 0,
78
+ syncMetadata: {},
79
+ optimistic: true,
80
+ createdAt: /* @__PURE__ */ new Date(),
81
+ updatedAt: /* @__PURE__ */ new Date()
82
+ };
83
+ }
84
+ serializeValue(value) {
85
+ if (value === null || value === void 0) {
86
+ return value;
87
+ }
88
+ if (value instanceof Date) {
89
+ return { __type: `Date`, value: value.toISOString() };
90
+ }
91
+ if (typeof value === `object`) {
92
+ const result = Array.isArray(value) ? [] : {};
93
+ for (const key in value) {
94
+ if (value.hasOwnProperty(key)) {
95
+ result[key] = this.serializeValue(value[key]);
96
+ }
97
+ }
98
+ return result;
99
+ }
100
+ return value;
101
+ }
102
+ deserializeValue(value) {
103
+ if (value === null || value === void 0) {
104
+ return value;
105
+ }
106
+ if (typeof value === `object` && value.__type === `Date`) {
107
+ return new Date(value.value);
108
+ }
109
+ if (typeof value === `object`) {
110
+ const result = Array.isArray(value) ? [] : {};
111
+ for (const key in value) {
112
+ if (value.hasOwnProperty(key)) {
113
+ result[key] = this.deserializeValue(value[key]);
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+ return value;
119
+ }
120
+ serializeError(error) {
121
+ return {
122
+ name: error.name,
123
+ message: error.message,
124
+ stack: error.stack
125
+ };
126
+ }
127
+ deserializeError(data) {
128
+ const error = new Error(data.message);
129
+ error.name = data.name;
130
+ error.stack = data.stack;
131
+ return error;
132
+ }
133
+ }
134
+ exports.TransactionSerializer = TransactionSerializer;
135
+ //# sourceMappingURL=TransactionSerializer.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TransactionSerializer.cjs","sources":["../../../src/outbox/TransactionSerializer.ts"],"sourcesContent":["import type {\n OfflineTransaction,\n SerializedError,\n SerializedMutation,\n SerializedOfflineTransaction,\n} from \"../types\"\nimport type { Collection, PendingMutation } from \"@tanstack/db\"\n\nexport class TransactionSerializer {\n private collections: Record<string, Collection>\n private collectionIdToKey: Map<string, string>\n\n constructor(collections: Record<string, Collection>) {\n this.collections = collections\n // Create reverse lookup from collection.id to registry key\n this.collectionIdToKey = new Map()\n for (const [key, collection] of Object.entries(collections)) {\n this.collectionIdToKey.set(collection.id, key)\n }\n }\n\n serialize(transaction: OfflineTransaction): string {\n const serialized: SerializedOfflineTransaction = {\n ...transaction,\n createdAt: transaction.createdAt,\n mutations: transaction.mutations.map((mutation) =>\n this.serializeMutation(mutation)\n ),\n }\n // Convert the whole object to JSON, handling dates\n return JSON.stringify(serialized, (key, value) => {\n if (value instanceof Date) {\n return value.toISOString()\n }\n return value\n })\n }\n\n deserialize(data: string): OfflineTransaction {\n const parsed: SerializedOfflineTransaction = JSON.parse(\n data,\n (key, value) => {\n // Parse ISO date strings back to Date objects\n if (\n typeof value === `string` &&\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(value)\n ) {\n return new Date(value)\n }\n return value\n }\n )\n\n return {\n ...parsed,\n mutations: parsed.mutations.map((mutationData) =>\n this.deserializeMutation(mutationData)\n ),\n }\n }\n\n private serializeMutation(mutation: PendingMutation): SerializedMutation {\n const registryKey = this.collectionIdToKey.get(mutation.collection.id)\n if (!registryKey) {\n throw new Error(\n `Collection with id ${mutation.collection.id} not found in registry`\n )\n }\n\n return {\n globalKey: mutation.globalKey,\n type: mutation.type,\n modified: this.serializeValue(mutation.modified),\n original: this.serializeValue(mutation.original),\n collectionId: registryKey, // Store registry key instead of collection.id\n }\n }\n\n private deserializeMutation(data: SerializedMutation): PendingMutation {\n const collection = this.collections[data.collectionId]\n if (!collection) {\n throw new Error(`Collection with id ${data.collectionId} not found`)\n }\n\n // Create a partial PendingMutation - we can't fully reconstruct it but\n // we provide what we can. The executor will need to handle the rest.\n return {\n globalKey: data.globalKey,\n type: data.type as any,\n modified: this.deserializeValue(data.modified),\n original: this.deserializeValue(data.original),\n collection,\n // These fields would need to be reconstructed by the executor\n mutationId: ``, // Will be regenerated\n key: null, // Will be extracted from the data\n changes: {}, // Will be recalculated\n metadata: undefined,\n syncMetadata: {},\n optimistic: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n } as PendingMutation\n }\n\n private serializeValue(value: any): any {\n if (value === null || value === undefined) {\n return value\n }\n\n if (value instanceof Date) {\n return { __type: `Date`, value: value.toISOString() }\n }\n\n if (typeof value === `object`) {\n const result: any = Array.isArray(value) ? [] : {}\n for (const key in value) {\n if (value.hasOwnProperty(key)) {\n result[key] = this.serializeValue(value[key])\n }\n }\n return result\n }\n\n return value\n }\n\n private deserializeValue(value: any): any {\n if (value === null || value === undefined) {\n return value\n }\n\n if (typeof value === `object` && value.__type === `Date`) {\n return new Date(value.value)\n }\n\n if (typeof value === `object`) {\n const result: any = Array.isArray(value) ? [] : {}\n for (const key in value) {\n if (value.hasOwnProperty(key)) {\n result[key] = this.deserializeValue(value[key])\n }\n }\n return result\n }\n\n return value\n }\n\n serializeError(error: Error): SerializedError {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n }\n\n deserializeError(data: SerializedError): Error {\n const error = new Error(data.message)\n error.name = data.name\n error.stack = data.stack\n return error\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { OfflineTransaction, SerializedError } from '../types.cjs';
2
+ import { Collection } from '@tanstack/db';
3
+ export declare class TransactionSerializer {
4
+ private collections;
5
+ private collectionIdToKey;
6
+ constructor(collections: Record<string, Collection>);
7
+ serialize(transaction: OfflineTransaction): string;
8
+ deserialize(data: string): OfflineTransaction;
9
+ private serializeMutation;
10
+ private deserializeMutation;
11
+ private serializeValue;
12
+ private deserializeValue;
13
+ serializeError(error: Error): SerializedError;
14
+ deserializeError(data: SerializedError): Error;
15
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class BackoffCalculator {
4
+ constructor(jitter = true) {
5
+ this.jitter = jitter;
6
+ }
7
+ calculate(retryCount) {
8
+ const baseDelay = Math.min(1e3 * Math.pow(2, retryCount), 6e4);
9
+ const jitterMultiplier = this.jitter ? Math.random() * 0.3 : 0;
10
+ return Math.floor(baseDelay * (1 + jitterMultiplier));
11
+ }
12
+ }
13
+ exports.BackoffCalculator = BackoffCalculator;
14
+ //# sourceMappingURL=BackoffCalculator.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackoffCalculator.cjs","sources":["../../../src/retry/BackoffCalculator.ts"],"sourcesContent":["export class BackoffCalculator {\n private jitter: boolean\n\n constructor(jitter = true) {\n this.jitter = jitter\n }\n\n calculate(retryCount: number): number {\n const baseDelay = Math.min(1000 * Math.pow(2, retryCount), 60000)\n const jitterMultiplier = this.jitter ? Math.random() * 0.3 : 0\n return Math.floor(baseDelay * (1 + jitterMultiplier))\n }\n}\n"],"names":[],"mappings":";;AAAO,MAAM,kBAAkB;AAAA,EAG7B,YAAY,SAAS,MAAM;AACzB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,UAAU,YAA4B;AACpC,UAAM,YAAY,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,UAAU,GAAG,GAAK;AAChE,UAAM,mBAAmB,KAAK,SAAS,KAAK,OAAA,IAAW,MAAM;AAC7D,WAAO,KAAK,MAAM,aAAa,IAAI,iBAAiB;AAAA,EACtD;AACF;;"}
@@ -0,0 +1,5 @@
1
+ export declare class BackoffCalculator {
2
+ private jitter;
3
+ constructor(jitter?: boolean);
4
+ calculate(retryCount: number): number;
5
+ }
@@ -0,0 +1 @@
1
+ export { NonRetriableError } from '../types.cjs';
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const types = require("../types.cjs");
4
+ const BackoffCalculator = require("./BackoffCalculator.cjs");
5
+ class DefaultRetryPolicy {
6
+ constructor(maxRetries = 10, jitter = true) {
7
+ this.backoffCalculator = new BackoffCalculator.BackoffCalculator(jitter);
8
+ this.maxRetries = maxRetries;
9
+ }
10
+ calculateDelay(retryCount) {
11
+ return this.backoffCalculator.calculate(retryCount);
12
+ }
13
+ shouldRetry(error, retryCount) {
14
+ if (retryCount >= this.maxRetries) {
15
+ return false;
16
+ }
17
+ if (error instanceof types.NonRetriableError) {
18
+ return false;
19
+ }
20
+ if (error.name === `AbortError`) {
21
+ return false;
22
+ }
23
+ if (error.message.includes(`401`) || error.message.includes(`403`)) {
24
+ return false;
25
+ }
26
+ if (error.message.includes(`422`) || error.message.includes(`400`)) {
27
+ return false;
28
+ }
29
+ return true;
30
+ }
31
+ }
32
+ exports.DefaultRetryPolicy = DefaultRetryPolicy;
33
+ //# sourceMappingURL=RetryPolicy.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RetryPolicy.cjs","sources":["../../../src/retry/RetryPolicy.ts"],"sourcesContent":["import { NonRetriableError } from \"../types\"\nimport { BackoffCalculator } from \"./BackoffCalculator\"\nimport type { RetryPolicy } from \"../types\"\n\nexport class DefaultRetryPolicy implements RetryPolicy {\n private backoffCalculator: BackoffCalculator\n private maxRetries: number\n\n constructor(maxRetries = 10, jitter = true) {\n this.backoffCalculator = new BackoffCalculator(jitter)\n this.maxRetries = maxRetries\n }\n\n calculateDelay(retryCount: number): number {\n return this.backoffCalculator.calculate(retryCount)\n }\n\n shouldRetry(error: Error, retryCount: number): boolean {\n if (retryCount >= this.maxRetries) {\n return false\n }\n\n if (error instanceof NonRetriableError) {\n return false\n }\n\n if (error.name === `AbortError`) {\n return false\n }\n\n if (error.message.includes(`401`) || error.message.includes(`403`)) {\n return false\n }\n\n if (error.message.includes(`422`) || error.message.includes(`400`)) {\n return false\n }\n\n return true\n }\n}\n"],"names":["BackoffCalculator","NonRetriableError"],"mappings":";;;;AAIO,MAAM,mBAA0C;AAAA,EAIrD,YAAY,aAAa,IAAI,SAAS,MAAM;AAC1C,SAAK,oBAAoB,IAAIA,kBAAAA,kBAAkB,MAAM;AACrD,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,eAAe,YAA4B;AACzC,WAAO,KAAK,kBAAkB,UAAU,UAAU;AAAA,EACpD;AAAA,EAEA,YAAY,OAAc,YAA6B;AACrD,QAAI,cAAc,KAAK,YAAY;AACjC,aAAO;AAAA,IACT;AAEA,QAAI,iBAAiBC,MAAAA,mBAAmB;AACtC,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,SAAS,cAAc;AAC/B,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,QAAQ,SAAS,KAAK,KAAK,MAAM,QAAQ,SAAS,KAAK,GAAG;AAClE,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,QAAQ,SAAS,KAAK,KAAK,MAAM,QAAQ,SAAS,KAAK,GAAG;AAClE,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;;"}
@@ -0,0 +1,8 @@
1
+ import { RetryPolicy } from '../types.cjs';
2
+ export declare class DefaultRetryPolicy implements RetryPolicy {
3
+ private backoffCalculator;
4
+ private maxRetries;
5
+ constructor(maxRetries?: number, jitter?: boolean);
6
+ calculateDelay(retryCount: number): number;
7
+ shouldRetry(error: Error, retryCount: number): boolean;
8
+ }
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const StorageAdapter = require("./StorageAdapter.cjs");
4
+ class IndexedDBAdapter extends StorageAdapter.BaseStorageAdapter {
5
+ constructor(dbName = `offline-transactions`, storeName = `transactions`) {
6
+ super();
7
+ this.db = null;
8
+ this.dbName = dbName;
9
+ this.storeName = storeName;
10
+ }
11
+ async openDB() {
12
+ if (this.db) {
13
+ return this.db;
14
+ }
15
+ return new Promise((resolve, reject) => {
16
+ const request = indexedDB.open(this.dbName, 1);
17
+ request.onerror = () => reject(request.error);
18
+ request.onsuccess = () => {
19
+ this.db = request.result;
20
+ resolve(this.db);
21
+ };
22
+ request.onupgradeneeded = (event) => {
23
+ const db = event.target.result;
24
+ if (!db.objectStoreNames.contains(this.storeName)) {
25
+ db.createObjectStore(this.storeName);
26
+ }
27
+ };
28
+ });
29
+ }
30
+ async getStore(mode = `readonly`) {
31
+ const db = await this.openDB();
32
+ const transaction = db.transaction([this.storeName], mode);
33
+ return transaction.objectStore(this.storeName);
34
+ }
35
+ async get(key) {
36
+ try {
37
+ const store = await this.getStore(`readonly`);
38
+ return new Promise((resolve, reject) => {
39
+ const request = store.get(key);
40
+ request.onerror = () => reject(request.error);
41
+ request.onsuccess = () => resolve(request.result ?? null);
42
+ });
43
+ } catch (error) {
44
+ console.warn(`IndexedDB get failed:`, error);
45
+ return null;
46
+ }
47
+ }
48
+ async set(key, value) {
49
+ try {
50
+ const store = await this.getStore(`readwrite`);
51
+ return new Promise((resolve, reject) => {
52
+ const request = store.put(value, key);
53
+ request.onerror = () => reject(request.error);
54
+ request.onsuccess = () => resolve();
55
+ });
56
+ } catch (error) {
57
+ if (error instanceof DOMException && error.name === `QuotaExceededError`) {
58
+ throw new Error(
59
+ `Storage quota exceeded. Consider clearing old transactions.`
60
+ );
61
+ }
62
+ throw error;
63
+ }
64
+ }
65
+ async delete(key) {
66
+ try {
67
+ const store = await this.getStore(`readwrite`);
68
+ return new Promise((resolve, reject) => {
69
+ const request = store.delete(key);
70
+ request.onerror = () => reject(request.error);
71
+ request.onsuccess = () => resolve();
72
+ });
73
+ } catch (error) {
74
+ console.warn(`IndexedDB delete failed:`, error);
75
+ }
76
+ }
77
+ async keys() {
78
+ try {
79
+ const store = await this.getStore(`readonly`);
80
+ return new Promise((resolve, reject) => {
81
+ const request = store.getAllKeys();
82
+ request.onerror = () => reject(request.error);
83
+ request.onsuccess = () => resolve(request.result);
84
+ });
85
+ } catch (error) {
86
+ console.warn(`IndexedDB keys failed:`, error);
87
+ return [];
88
+ }
89
+ }
90
+ async clear() {
91
+ try {
92
+ const store = await this.getStore(`readwrite`);
93
+ return new Promise((resolve, reject) => {
94
+ const request = store.clear();
95
+ request.onerror = () => reject(request.error);
96
+ request.onsuccess = () => resolve();
97
+ });
98
+ } catch (error) {
99
+ console.warn(`IndexedDB clear failed:`, error);
100
+ }
101
+ }
102
+ }
103
+ exports.IndexedDBAdapter = IndexedDBAdapter;
104
+ //# sourceMappingURL=IndexedDBAdapter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IndexedDBAdapter.cjs","sources":["../../../src/storage/IndexedDBAdapter.ts"],"sourcesContent":["import { BaseStorageAdapter } from \"./StorageAdapter\"\n\nexport class IndexedDBAdapter extends BaseStorageAdapter {\n private dbName: string\n private storeName: string\n private db: IDBDatabase | null = null\n\n constructor(dbName = `offline-transactions`, storeName = `transactions`) {\n super()\n this.dbName = dbName\n this.storeName = storeName\n }\n\n private async openDB(): Promise<IDBDatabase> {\n if (this.db) {\n return this.db\n }\n\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, 1)\n\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n this.db = request.result\n resolve(this.db)\n }\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result\n if (!db.objectStoreNames.contains(this.storeName)) {\n db.createObjectStore(this.storeName)\n }\n }\n })\n }\n\n private async getStore(\n mode: IDBTransactionMode = `readonly`\n ): Promise<IDBObjectStore> {\n const db = await this.openDB()\n const transaction = db.transaction([this.storeName], mode)\n return transaction.objectStore(this.storeName)\n }\n\n async get(key: string): Promise<string | null> {\n try {\n const store = await this.getStore(`readonly`)\n return new Promise((resolve, reject) => {\n const request = store.get(key)\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(request.result ?? null)\n })\n } catch (error) {\n console.warn(`IndexedDB get failed:`, error)\n return null\n }\n }\n\n async set(key: string, value: string): Promise<void> {\n try {\n const store = await this.getStore(`readwrite`)\n return new Promise((resolve, reject) => {\n const request = store.put(value, key)\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve()\n })\n } catch (error) {\n if (\n error instanceof DOMException &&\n error.name === `QuotaExceededError`\n ) {\n throw new Error(\n `Storage quota exceeded. Consider clearing old transactions.`\n )\n }\n throw error\n }\n }\n\n async delete(key: string): Promise<void> {\n try {\n const store = await this.getStore(`readwrite`)\n return new Promise((resolve, reject) => {\n const request = store.delete(key)\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve()\n })\n } catch (error) {\n console.warn(`IndexedDB delete failed:`, error)\n }\n }\n\n async keys(): Promise<Array<string>> {\n try {\n const store = await this.getStore(`readonly`)\n return new Promise((resolve, reject) => {\n const request = store.getAllKeys()\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(request.result as Array<string>)\n })\n } catch (error) {\n console.warn(`IndexedDB keys failed:`, error)\n return []\n }\n }\n\n async clear(): Promise<void> {\n try {\n const store = await this.getStore(`readwrite`)\n return new Promise((resolve, reject) => {\n const request = store.clear()\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve()\n })\n } catch (error) {\n console.warn(`IndexedDB clear failed:`, error)\n }\n }\n}\n"],"names":["BaseStorageAdapter"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { BaseStorageAdapter } from './StorageAdapter.cjs';
2
+ export declare class IndexedDBAdapter extends BaseStorageAdapter {
3
+ private dbName;
4
+ private storeName;
5
+ private db;
6
+ constructor(dbName?: string, storeName?: string);
7
+ private openDB;
8
+ private getStore;
9
+ get(key: string): Promise<string | null>;
10
+ set(key: string, value: string): Promise<void>;
11
+ delete(key: string): Promise<void>;
12
+ keys(): Promise<Array<string>>;
13
+ clear(): Promise<void>;
14
+ }
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const StorageAdapter = require("./StorageAdapter.cjs");
4
+ class LocalStorageAdapter extends StorageAdapter.BaseStorageAdapter {
5
+ constructor(prefix = `offline-tx:`) {
6
+ super();
7
+ this.prefix = prefix;
8
+ }
9
+ getKey(key) {
10
+ return `${this.prefix}${key}`;
11
+ }
12
+ get(key) {
13
+ try {
14
+ return Promise.resolve(localStorage.getItem(this.getKey(key)));
15
+ } catch (error) {
16
+ console.warn(`localStorage get failed:`, error);
17
+ return Promise.resolve(null);
18
+ }
19
+ }
20
+ set(key, value) {
21
+ try {
22
+ localStorage.setItem(this.getKey(key), value);
23
+ return Promise.resolve();
24
+ } catch (error) {
25
+ if (error instanceof DOMException && error.name === `QuotaExceededError`) {
26
+ return Promise.reject(
27
+ new Error(
28
+ `Storage quota exceeded. Consider clearing old transactions.`
29
+ )
30
+ );
31
+ }
32
+ return Promise.reject(error);
33
+ }
34
+ }
35
+ delete(key) {
36
+ try {
37
+ localStorage.removeItem(this.getKey(key));
38
+ return Promise.resolve();
39
+ } catch (error) {
40
+ console.warn(`localStorage delete failed:`, error);
41
+ return Promise.resolve();
42
+ }
43
+ }
44
+ keys() {
45
+ try {
46
+ const keys = [];
47
+ for (let i = 0; i < localStorage.length; i++) {
48
+ const key = localStorage.key(i);
49
+ if (key && key.startsWith(this.prefix)) {
50
+ keys.push(key.slice(this.prefix.length));
51
+ }
52
+ }
53
+ return Promise.resolve(keys);
54
+ } catch (error) {
55
+ console.warn(`localStorage keys failed:`, error);
56
+ return Promise.resolve([]);
57
+ }
58
+ }
59
+ async clear() {
60
+ try {
61
+ const keys = await this.keys();
62
+ for (const key of keys) {
63
+ localStorage.removeItem(this.getKey(key));
64
+ }
65
+ } catch (error) {
66
+ console.warn(`localStorage clear failed:`, error);
67
+ }
68
+ }
69
+ }
70
+ exports.LocalStorageAdapter = LocalStorageAdapter;
71
+ //# sourceMappingURL=LocalStorageAdapter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalStorageAdapter.cjs","sources":["../../../src/storage/LocalStorageAdapter.ts"],"sourcesContent":["import { BaseStorageAdapter } from \"./StorageAdapter\"\n\nexport class LocalStorageAdapter extends BaseStorageAdapter {\n private prefix: string\n\n constructor(prefix = `offline-tx:`) {\n super()\n this.prefix = prefix\n }\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`\n }\n\n get(key: string): Promise<string | null> {\n try {\n return Promise.resolve(localStorage.getItem(this.getKey(key)))\n } catch (error) {\n console.warn(`localStorage get failed:`, error)\n return Promise.resolve(null)\n }\n }\n\n set(key: string, value: string): Promise<void> {\n try {\n localStorage.setItem(this.getKey(key), value)\n return Promise.resolve()\n } catch (error) {\n if (\n error instanceof DOMException &&\n error.name === `QuotaExceededError`\n ) {\n return Promise.reject(\n new Error(\n `Storage quota exceeded. Consider clearing old transactions.`\n )\n )\n }\n return Promise.reject(error)\n }\n }\n\n delete(key: string): Promise<void> {\n try {\n localStorage.removeItem(this.getKey(key))\n return Promise.resolve()\n } catch (error) {\n console.warn(`localStorage delete failed:`, error)\n return Promise.resolve()\n }\n }\n\n keys(): Promise<Array<string>> {\n try {\n const keys: Array<string> = []\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i)\n if (key && key.startsWith(this.prefix)) {\n keys.push(key.slice(this.prefix.length))\n }\n }\n return Promise.resolve(keys)\n } catch (error) {\n console.warn(`localStorage keys failed:`, error)\n return Promise.resolve([])\n }\n }\n\n async clear(): Promise<void> {\n try {\n const keys = await this.keys()\n for (const key of keys) {\n localStorage.removeItem(this.getKey(key))\n }\n } catch (error) {\n console.warn(`localStorage clear failed:`, error)\n }\n }\n}\n"],"names":["BaseStorageAdapter"],"mappings":";;;AAEO,MAAM,4BAA4BA,eAAAA,mBAAmB;AAAA,EAG1D,YAAY,SAAS,eAAe;AAClC,UAAA;AACA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEQ,OAAO,KAAqB;AAClC,WAAO,GAAG,KAAK,MAAM,GAAG,GAAG;AAAA,EAC7B;AAAA,EAEA,IAAI,KAAqC;AACvC,QAAI;AACF,aAAO,QAAQ,QAAQ,aAAa,QAAQ,KAAK,OAAO,GAAG,CAAC,CAAC;AAAA,IAC/D,SAAS,OAAO;AACd,cAAQ,KAAK,4BAA4B,KAAK;AAC9C,aAAO,QAAQ,QAAQ,IAAI;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,IAAI,KAAa,OAA8B;AAC7C,QAAI;AACF,mBAAa,QAAQ,KAAK,OAAO,GAAG,GAAG,KAAK;AAC5C,aAAO,QAAQ,QAAA;AAAA,IACjB,SAAS,OAAO;AACd,UACE,iBAAiB,gBACjB,MAAM,SAAS,sBACf;AACA,eAAO,QAAQ;AAAA,UACb,IAAI;AAAA,YACF;AAAA,UAAA;AAAA,QACF;AAAA,MAEJ;AACA,aAAO,QAAQ,OAAO,KAAK;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,OAAO,KAA4B;AACjC,QAAI;AACF,mBAAa,WAAW,KAAK,OAAO,GAAG,CAAC;AACxC,aAAO,QAAQ,QAAA;AAAA,IACjB,SAAS,OAAO;AACd,cAAQ,KAAK,+BAA+B,KAAK;AACjD,aAAO,QAAQ,QAAA;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,OAA+B;AAC7B,QAAI;AACF,YAAM,OAAsB,CAAA;AAC5B,eAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,cAAM,MAAM,aAAa,IAAI,CAAC;AAC9B,YAAI,OAAO,IAAI,WAAW,KAAK,MAAM,GAAG;AACtC,eAAK,KAAK,IAAI,MAAM,KAAK,OAAO,MAAM,CAAC;AAAA,QACzC;AAAA,MACF;AACA,aAAO,QAAQ,QAAQ,IAAI;AAAA,IAC7B,SAAS,OAAO;AACd,cAAQ,KAAK,6BAA6B,KAAK;AAC/C,aAAO,QAAQ,QAAQ,EAAE;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,KAAA;AACxB,iBAAW,OAAO,MAAM;AACtB,qBAAa,WAAW,KAAK,OAAO,GAAG,CAAC;AAAA,MAC1C;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,8BAA8B,KAAK;AAAA,IAClD;AAAA,EACF;AACF;;"}
@@ -0,0 +1,11 @@
1
+ import { BaseStorageAdapter } from './StorageAdapter.cjs';
2
+ export declare class LocalStorageAdapter extends BaseStorageAdapter {
3
+ private prefix;
4
+ constructor(prefix?: string);
5
+ private getKey;
6
+ get(key: string): Promise<string | null>;
7
+ set(key: string, value: string): Promise<void>;
8
+ delete(key: string): Promise<void>;
9
+ keys(): Promise<Array<string>>;
10
+ clear(): Promise<void>;
11
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class BaseStorageAdapter {
4
+ }
5
+ exports.BaseStorageAdapter = BaseStorageAdapter;
6
+ //# sourceMappingURL=StorageAdapter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageAdapter.cjs","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;;"}
@@ -0,0 +1,9 @@
1
+ import { StorageAdapter } from '../types.cjs';
2
+ export declare abstract class BaseStorageAdapter implements StorageAdapter {
3
+ abstract get(key: string): Promise<string | null>;
4
+ abstract set(key: string, value: string): Promise<void>;
5
+ abstract delete(key: string): Promise<void>;
6
+ abstract keys(): Promise<Array<string>>;
7
+ abstract clear(): Promise<void>;
8
+ }
9
+ export { type StorageAdapter };
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const api = require("@opentelemetry/api");
4
+ const TRACER = api.trace.getTracer("@tanstack/offline-transactions", "0.0.1");
5
+ function getParentContext(options) {
6
+ if (options?.parentContext) {
7
+ const parentSpan = api.trace.wrapSpanContext(options.parentContext);
8
+ return api.trace.setSpan(api.context.active(), parentSpan);
9
+ }
10
+ return api.context.active();
11
+ }
12
+ async function withSpan(name, attrs, fn, options) {
13
+ const parentCtx = getParentContext(options);
14
+ const span = TRACER.startSpan(name, void 0, parentCtx);
15
+ const filteredAttrs = {};
16
+ for (const [key, value] of Object.entries(attrs)) {
17
+ if (value !== void 0) {
18
+ filteredAttrs[key] = value;
19
+ }
20
+ }
21
+ span.setAttributes(filteredAttrs);
22
+ try {
23
+ const result = await fn(span);
24
+ span.setStatus({ code: api.SpanStatusCode.OK });
25
+ return result;
26
+ } catch (error) {
27
+ span.setStatus({
28
+ code: api.SpanStatusCode.ERROR,
29
+ message: error instanceof Error ? error.message : String(error)
30
+ });
31
+ span.recordException(error);
32
+ throw error;
33
+ } finally {
34
+ span.end();
35
+ }
36
+ }
37
+ async function withNestedSpan(name, attrs, fn, options) {
38
+ const parentCtx = getParentContext(options);
39
+ const span = TRACER.startSpan(name, void 0, parentCtx);
40
+ const filteredAttrs = {};
41
+ for (const [key, value] of Object.entries(attrs)) {
42
+ if (value !== void 0) {
43
+ filteredAttrs[key] = value;
44
+ }
45
+ }
46
+ span.setAttributes(filteredAttrs);
47
+ const ctx = api.trace.setSpan(parentCtx, span);
48
+ try {
49
+ const result = await api.context.with(ctx, () => fn(span));
50
+ span.setStatus({ code: api.SpanStatusCode.OK });
51
+ return result;
52
+ } catch (error) {
53
+ span.setStatus({
54
+ code: api.SpanStatusCode.ERROR,
55
+ message: error instanceof Error ? error.message : String(error)
56
+ });
57
+ span.recordException(error);
58
+ throw error;
59
+ } finally {
60
+ span.end();
61
+ }
62
+ }
63
+ function withSyncSpan(name, attrs, fn, options) {
64
+ const parentCtx = getParentContext(options);
65
+ const span = TRACER.startSpan(name, void 0, parentCtx);
66
+ const filteredAttrs = {};
67
+ for (const [key, value] of Object.entries(attrs)) {
68
+ if (value !== void 0) {
69
+ filteredAttrs[key] = value;
70
+ }
71
+ }
72
+ span.setAttributes(filteredAttrs);
73
+ try {
74
+ const result = fn(span);
75
+ span.setStatus({ code: api.SpanStatusCode.OK });
76
+ return result;
77
+ } catch (error) {
78
+ span.setStatus({
79
+ code: api.SpanStatusCode.ERROR,
80
+ message: error instanceof Error ? error.message : String(error)
81
+ });
82
+ span.recordException(error);
83
+ throw error;
84
+ } finally {
85
+ span.end();
86
+ }
87
+ }
88
+ exports.withNestedSpan = withNestedSpan;
89
+ exports.withSpan = withSpan;
90
+ exports.withSyncSpan = withSyncSpan;
91
+ //# sourceMappingURL=tracer.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracer.cjs","sources":["../../../src/telemetry/tracer.ts"],"sourcesContent":["import {\n trace,\n type Span,\n SpanStatusCode,\n context,\n type SpanContext,\n} from \"@opentelemetry/api\"\n\nconst TRACER = trace.getTracer(\"@tanstack/offline-transactions\", \"0.0.1\")\n\nexport interface SpanAttrs {\n [key: string]: string | number | boolean | undefined\n}\n\ninterface WithSpanOptions {\n parentContext?: SpanContext\n}\n\nfunction getParentContext(options?: WithSpanOptions) {\n if (options?.parentContext) {\n const parentSpan = trace.wrapSpanContext(options.parentContext)\n return trace.setSpan(context.active(), parentSpan)\n }\n\n return context.active()\n}\n\n/**\n * Lightweight span wrapper with error handling.\n * Uses OpenTelemetry API which is no-op when tracing is disabled.\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: Span) => Promise<T>,\n options?: WithSpanOptions\n): Promise<T> {\n const parentCtx = getParentContext(options)\n const span = TRACER.startSpan(name, undefined, parentCtx)\n\n // Filter out undefined attributes\n const filteredAttrs: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined) {\n filteredAttrs[key] = value\n }\n }\n\n span.setAttributes(filteredAttrs)\n\n try {\n const result = await fn(span)\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n })\n span.recordException(error as Error)\n throw error\n } finally {\n span.end()\n }\n}\n\n/**\n * Like withSpan but propagates context so child spans nest properly.\n * Use this when you want operations inside fn to be child spans.\n */\nexport async function withNestedSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: Span) => Promise<T>,\n options?: WithSpanOptions\n): Promise<T> {\n const parentCtx = getParentContext(options)\n const span = TRACER.startSpan(name, undefined, parentCtx)\n\n // Filter out undefined attributes\n const filteredAttrs: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined) {\n filteredAttrs[key] = value\n }\n }\n\n span.setAttributes(filteredAttrs)\n\n // Set the span as active context so child spans nest properly\n const ctx = trace.setSpan(parentCtx, span)\n\n try {\n // Execute the function within the span's context\n const result = await context.with(ctx, () => fn(span))\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n })\n span.recordException(error as Error)\n throw error\n } finally {\n span.end()\n }\n}\n\n/**\n * Creates a synchronous span for non-async operations\n */\nexport function withSyncSpan<T>(\n name: string,\n attrs: SpanAttrs,\n fn: (span: Span) => T,\n options?: WithSpanOptions\n): T {\n const parentCtx = getParentContext(options)\n const span = TRACER.startSpan(name, undefined, parentCtx)\n\n // Filter out undefined attributes\n const filteredAttrs: Record<string, string | number | boolean> = {}\n for (const [key, value] of Object.entries(attrs)) {\n if (value !== undefined) {\n filteredAttrs[key] = value\n }\n }\n\n span.setAttributes(filteredAttrs)\n\n try {\n const result = fn(span)\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (error) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : String(error),\n })\n span.recordException(error as Error)\n throw error\n } finally {\n span.end()\n }\n}\n\n/**\n * Get the current tracer instance\n */\nexport function getTracer() {\n return TRACER\n}\n"],"names":["trace","context","SpanStatusCode"],"mappings":";;;AAQA,MAAM,SAASA,IAAAA,MAAM,UAAU,kCAAkC,OAAO;AAUxE,SAAS,iBAAiB,SAA2B;AACnD,MAAI,SAAS,eAAe;AAC1B,UAAM,aAAaA,IAAAA,MAAM,gBAAgB,QAAQ,aAAa;AAC9D,WAAOA,IAAAA,MAAM,QAAQC,IAAAA,QAAQ,OAAA,GAAU,UAAU;AAAA,EACnD;AAEA,SAAOA,IAAAA,QAAQ,OAAA;AACjB;AASA,eAAsB,SACpB,MACA,OACA,IACA,SACY;AACZ,QAAM,YAAY,iBAAiB,OAAO;AAC1C,QAAM,OAAO,OAAO,UAAU,MAAM,QAAW,SAAS;AAGxD,QAAM,gBAA2D,CAAA;AACjE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,QAAW;AACvB,oBAAc,GAAG,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,OAAK,cAAc,aAAa;AAEhC,MAAI;AACF,UAAM,SAAS,MAAM,GAAG,IAAI;AAC5B,SAAK,UAAU,EAAE,MAAMC,IAAAA,eAAe,IAAI;AAC1C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,SAAK,UAAU;AAAA,MACb,MAAMA,IAAAA,eAAe;AAAA,MACrB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAAA,CAC/D;AACD,SAAK,gBAAgB,KAAc;AACnC,UAAM;AAAA,EACR,UAAA;AACE,SAAK,IAAA;AAAA,EACP;AACF;AAMA,eAAsB,eACpB,MACA,OACA,IACA,SACY;AACZ,QAAM,YAAY,iBAAiB,OAAO;AAC1C,QAAM,OAAO,OAAO,UAAU,MAAM,QAAW,SAAS;AAGxD,QAAM,gBAA2D,CAAA;AACjE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,QAAW;AACvB,oBAAc,GAAG,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,OAAK,cAAc,aAAa;AAGhC,QAAM,MAAMF,IAAAA,MAAM,QAAQ,WAAW,IAAI;AAEzC,MAAI;AAEF,UAAM,SAAS,MAAMC,YAAQ,KAAK,KAAK,MAAM,GAAG,IAAI,CAAC;AACrD,SAAK,UAAU,EAAE,MAAMC,IAAAA,eAAe,IAAI;AAC1C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,SAAK,UAAU;AAAA,MACb,MAAMA,IAAAA,eAAe;AAAA,MACrB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAAA,CAC/D;AACD,SAAK,gBAAgB,KAAc;AACnC,UAAM;AAAA,EACR,UAAA;AACE,SAAK,IAAA;AAAA,EACP;AACF;AAKO,SAAS,aACd,MACA,OACA,IACA,SACG;AACH,QAAM,YAAY,iBAAiB,OAAO;AAC1C,QAAM,OAAO,OAAO,UAAU,MAAM,QAAW,SAAS;AAGxD,QAAM,gBAA2D,CAAA;AACjE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,QAAW;AACvB,oBAAc,GAAG,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,OAAK,cAAc,aAAa;AAEhC,MAAI;AACF,UAAM,SAAS,GAAG,IAAI;AACtB,SAAK,UAAU,EAAE,MAAMA,IAAAA,eAAe,IAAI;AAC1C,WAAO;AAAA,EACT,SAAS,OAAO;AACd,SAAK,UAAU;AAAA,MACb,MAAMA,IAAAA,eAAe;AAAA,MACrB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAAA,CAC/D;AACD,SAAK,gBAAgB,KAAc;AACnC,UAAM;AAAA,EACR,UAAA;AACE,SAAK,IAAA;AAAA,EACP;AACF;;;;"}
@@ -0,0 +1,29 @@
1
+ import { Span, SpanContext } from '@opentelemetry/api';
2
+ export interface SpanAttrs {
3
+ [key: string]: string | number | boolean | undefined;
4
+ }
5
+ interface WithSpanOptions {
6
+ parentContext?: SpanContext;
7
+ }
8
+ /**
9
+ * Lightweight span wrapper with error handling.
10
+ * Uses OpenTelemetry API which is no-op when tracing is disabled.
11
+ *
12
+ * By default, creates spans at the current context level (siblings).
13
+ * Use withNestedSpan if you want parent-child relationships.
14
+ */
15
+ export declare function withSpan<T>(name: string, attrs: SpanAttrs, fn: (span: Span) => Promise<T>, options?: WithSpanOptions): Promise<T>;
16
+ /**
17
+ * Like withSpan but propagates context so child spans nest properly.
18
+ * Use this when you want operations inside fn to be child spans.
19
+ */
20
+ export declare function withNestedSpan<T>(name: string, attrs: SpanAttrs, fn: (span: Span) => Promise<T>, options?: WithSpanOptions): Promise<T>;
21
+ /**
22
+ * Creates a synchronous span for non-async operations
23
+ */
24
+ export declare function withSyncSpan<T>(name: string, attrs: SpanAttrs, fn: (span: Span) => T, options?: WithSpanOptions): T;
25
+ /**
26
+ * Get the current tracer instance
27
+ */
28
+ export declare function getTracer(): import('@opentelemetry/api').Tracer;
29
+ export {};
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ class NonRetriableError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = `NonRetriableError`;
7
+ }
8
+ }
9
+ exports.NonRetriableError = NonRetriableError;
10
+ //# sourceMappingURL=types.cjs.map