@tanstack/offline-transactions 1.0.54 → 1.0.58
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.
- package/README.md +137 -23
- package/dist/cjs/OfflineExecutor.cjs +28 -11
- package/dist/cjs/OfflineExecutor.cjs.map +1 -1
- package/dist/cjs/OfflineExecutor.d.cts +1 -0
- package/dist/cjs/api/OfflineTransaction.cjs +4 -1
- package/dist/cjs/api/OfflineTransaction.cjs.map +1 -1
- package/dist/cjs/connectivity/OnlineDetector.cjs +1 -1
- package/dist/cjs/connectivity/OnlineDetector.cjs.map +1 -1
- package/dist/cjs/connectivity/ReactNativeOnlineDetector.cjs +1 -7
- package/dist/cjs/connectivity/ReactNativeOnlineDetector.cjs.map +1 -1
- package/dist/cjs/executor/KeyScheduler.cjs +31 -26
- package/dist/cjs/executor/KeyScheduler.cjs.map +1 -1
- package/dist/cjs/executor/KeyScheduler.d.cts +5 -3
- package/dist/cjs/executor/TransactionExecutor.cjs +72 -35
- package/dist/cjs/executor/TransactionExecutor.cjs.map +1 -1
- package/dist/cjs/executor/TransactionExecutor.d.cts +3 -2
- package/dist/cjs/outbox/OutboxManager.cjs +66 -24
- package/dist/cjs/outbox/OutboxManager.cjs.map +1 -1
- package/dist/cjs/outbox/OutboxManager.d.cts +6 -0
- package/dist/cjs/outbox/TransactionSerializer.cjs +159 -20
- package/dist/cjs/outbox/TransactionSerializer.cjs.map +1 -1
- package/dist/cjs/outbox/TransactionSerializer.d.cts +2 -0
- package/dist/cjs/storage/IndexedDBAdapter.cjs +16 -12
- package/dist/cjs/storage/IndexedDBAdapter.cjs.map +1 -1
- package/dist/cjs/storage/IndexedDBAdapter.d.cts +1 -0
- package/dist/cjs/types.cjs.map +1 -1
- package/dist/cjs/types.d.cts +3 -0
- package/dist/esm/OfflineExecutor.d.ts +1 -0
- package/dist/esm/OfflineExecutor.js +28 -11
- package/dist/esm/OfflineExecutor.js.map +1 -1
- package/dist/esm/api/OfflineTransaction.js +4 -1
- package/dist/esm/api/OfflineTransaction.js.map +1 -1
- package/dist/esm/connectivity/OnlineDetector.js +1 -1
- package/dist/esm/connectivity/OnlineDetector.js.map +1 -1
- package/dist/esm/connectivity/ReactNativeOnlineDetector.js +1 -7
- package/dist/esm/connectivity/ReactNativeOnlineDetector.js.map +1 -1
- package/dist/esm/executor/KeyScheduler.d.ts +5 -3
- package/dist/esm/executor/KeyScheduler.js +31 -26
- package/dist/esm/executor/KeyScheduler.js.map +1 -1
- package/dist/esm/executor/TransactionExecutor.d.ts +3 -2
- package/dist/esm/executor/TransactionExecutor.js +72 -35
- package/dist/esm/executor/TransactionExecutor.js.map +1 -1
- package/dist/esm/outbox/OutboxManager.d.ts +6 -0
- package/dist/esm/outbox/OutboxManager.js +67 -25
- package/dist/esm/outbox/OutboxManager.js.map +1 -1
- package/dist/esm/outbox/TransactionSerializer.d.ts +2 -0
- package/dist/esm/outbox/TransactionSerializer.js +159 -20
- package/dist/esm/outbox/TransactionSerializer.js.map +1 -1
- package/dist/esm/storage/IndexedDBAdapter.d.ts +1 -0
- package/dist/esm/storage/IndexedDBAdapter.js +16 -12
- package/dist/esm/storage/IndexedDBAdapter.js.map +1 -1
- package/dist/esm/types.d.ts +3 -0
- package/dist/esm/types.js.map +1 -1
- package/package.json +2 -2
- package/src/OfflineExecutor.ts +34 -15
- package/src/api/OfflineTransaction.ts +7 -2
- package/src/connectivity/OnlineDetector.ts +6 -1
- package/src/connectivity/ReactNativeOnlineDetector.ts +1 -11
- package/src/executor/KeyScheduler.ts +39 -28
- package/src/executor/TransactionExecutor.ts +88 -46
- package/src/outbox/OutboxManager.ts +82 -28
- package/src/outbox/TransactionSerializer.ts +236 -22
- package/src/storage/IndexedDBAdapter.ts +23 -12
- package/src/types.ts +3 -0
|
@@ -6,6 +6,94 @@ import type {
|
|
|
6
6
|
} from '../types'
|
|
7
7
|
import type { Collection, PendingMutation } from '@tanstack/db'
|
|
8
8
|
|
|
9
|
+
const temporalConstructorNames = [
|
|
10
|
+
`Duration`,
|
|
11
|
+
`Instant`,
|
|
12
|
+
`PlainDate`,
|
|
13
|
+
`PlainDateTime`,
|
|
14
|
+
`PlainMonthDay`,
|
|
15
|
+
`PlainTime`,
|
|
16
|
+
`PlainYearMonth`,
|
|
17
|
+
`ZonedDateTime`,
|
|
18
|
+
] as const
|
|
19
|
+
|
|
20
|
+
type TemporalConstructorName = (typeof temporalConstructorNames)[number]
|
|
21
|
+
type TemporalConstructor = {
|
|
22
|
+
from: (value: string) => unknown
|
|
23
|
+
prototype?: { toString?: () => string }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getTemporalConstructorName(
|
|
27
|
+
type: unknown,
|
|
28
|
+
): TemporalConstructorName | undefined {
|
|
29
|
+
if (typeof type !== `string` || !type.startsWith(`Temporal.`)) return
|
|
30
|
+
const constructorName = type.slice(
|
|
31
|
+
`Temporal.`.length,
|
|
32
|
+
) as TemporalConstructorName
|
|
33
|
+
return temporalConstructorNames.includes(constructorName)
|
|
34
|
+
? constructorName
|
|
35
|
+
: undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function requireTemporalConstructor(
|
|
39
|
+
name: TemporalConstructorName,
|
|
40
|
+
): TemporalConstructor {
|
|
41
|
+
const constructor = (
|
|
42
|
+
globalThis as {
|
|
43
|
+
Temporal?: Partial<Record<TemporalConstructorName, TemporalConstructor>>
|
|
44
|
+
}
|
|
45
|
+
).Temporal?.[name]
|
|
46
|
+
if (typeof constructor?.from !== `function`)
|
|
47
|
+
throw new MissingTemporalConstructorError(
|
|
48
|
+
`Missing global Temporal.${name} constructor`,
|
|
49
|
+
)
|
|
50
|
+
return constructor
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function serializeTemporalValue(
|
|
54
|
+
value: object,
|
|
55
|
+
name: TemporalConstructorName,
|
|
56
|
+
): string | undefined {
|
|
57
|
+
const constructor = requireTemporalConstructor(name)
|
|
58
|
+
const toString = constructor.prototype?.toString
|
|
59
|
+
if (typeof toString !== `function` || toString === Object.prototype.toString)
|
|
60
|
+
return
|
|
61
|
+
try {
|
|
62
|
+
const serialized = toString.call(value)
|
|
63
|
+
return typeof serialized === `string` ? serialized : undefined
|
|
64
|
+
} catch {
|
|
65
|
+
// Temporal prototype methods brand-check their receiver. A matching
|
|
66
|
+
// Symbol.toStringTag without the corresponding internal slots is user data.
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function mayBeNativeScalar(value: object): boolean {
|
|
72
|
+
const prototype = Object.getPrototypeOf(value)
|
|
73
|
+
return (
|
|
74
|
+
prototype !== null &&
|
|
75
|
+
prototype !== Object.prototype &&
|
|
76
|
+
!Array.isArray(value)
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class MissingTemporalConstructorError extends Error {}
|
|
81
|
+
|
|
82
|
+
function setDataProperty(
|
|
83
|
+
object: Record<string, unknown>,
|
|
84
|
+
key: string,
|
|
85
|
+
value: unknown,
|
|
86
|
+
): void {
|
|
87
|
+
if (key !== `__proto__`) object[key] = value
|
|
88
|
+
else
|
|
89
|
+
Object.defineProperty(object, key, {
|
|
90
|
+
value,
|
|
91
|
+
enumerable: true,
|
|
92
|
+
configurable: true,
|
|
93
|
+
writable: true,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
9
97
|
export class TransactionSerializer {
|
|
10
98
|
private collections: Record<string, Collection<any, any, any, any, any>>
|
|
11
99
|
private collectionIdToKey: Map<string, string>
|
|
@@ -24,7 +112,9 @@ export class TransactionSerializer {
|
|
|
24
112
|
serialize(transaction: OfflineTransaction): string {
|
|
25
113
|
const serialized: SerializedOfflineTransaction = {
|
|
26
114
|
...transaction,
|
|
115
|
+
valueEncoding: 3,
|
|
27
116
|
createdAt: transaction.createdAt.toISOString(),
|
|
117
|
+
metadata: this.serializeValue(transaction.metadata, `metadata`),
|
|
28
118
|
mutations: transaction.mutations.map((mutation) =>
|
|
29
119
|
this.serializeMutation(mutation),
|
|
30
120
|
),
|
|
@@ -33,9 +123,23 @@ export class TransactionSerializer {
|
|
|
33
123
|
}
|
|
34
124
|
|
|
35
125
|
deserialize(data: string): OfflineTransaction {
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
const
|
|
126
|
+
// Old records retain their Date-marker meaning. New records also escape
|
|
127
|
+
// marker-shaped user objects; the encoding tag is not application metadata.
|
|
128
|
+
const {
|
|
129
|
+
valueEncoding,
|
|
130
|
+
...parsed
|
|
131
|
+
}: Omit<SerializedOfflineTransaction, `valueEncoding`> & {
|
|
132
|
+
valueEncoding?: unknown
|
|
133
|
+
} = JSON.parse(data)
|
|
134
|
+
if (
|
|
135
|
+
valueEncoding !== undefined &&
|
|
136
|
+
valueEncoding !== 2 &&
|
|
137
|
+
valueEncoding !== 3
|
|
138
|
+
) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Unsupported transaction value encoding: ${valueEncoding}`,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
39
143
|
|
|
40
144
|
const createdAt = new Date(parsed.createdAt)
|
|
41
145
|
if (isNaN(createdAt.getTime())) {
|
|
@@ -47,8 +151,12 @@ export class TransactionSerializer {
|
|
|
47
151
|
return {
|
|
48
152
|
...parsed,
|
|
49
153
|
createdAt,
|
|
154
|
+
metadata:
|
|
155
|
+
valueEncoding === 3
|
|
156
|
+
? this.deserializeValue(parsed.metadata, valueEncoding)
|
|
157
|
+
: parsed.metadata,
|
|
50
158
|
mutations: parsed.mutations.map((mutationData) =>
|
|
51
|
-
this.deserializeMutation(mutationData),
|
|
159
|
+
this.deserializeMutation(mutationData, valueEncoding),
|
|
52
160
|
),
|
|
53
161
|
}
|
|
54
162
|
}
|
|
@@ -71,13 +179,16 @@ export class TransactionSerializer {
|
|
|
71
179
|
}
|
|
72
180
|
}
|
|
73
181
|
|
|
74
|
-
private deserializeMutation(
|
|
182
|
+
private deserializeMutation(
|
|
183
|
+
data: SerializedMutation,
|
|
184
|
+
valueEncoding: 2 | 3 | undefined,
|
|
185
|
+
): PendingMutation {
|
|
75
186
|
const collection = this.collections[data.collectionId]
|
|
76
187
|
if (!collection) {
|
|
77
188
|
throw new Error(`Collection with id ${data.collectionId} not found`)
|
|
78
189
|
}
|
|
79
190
|
|
|
80
|
-
const modified = this.deserializeValue(data.modified)
|
|
191
|
+
const modified = this.deserializeValue(data.modified, valueEncoding)
|
|
81
192
|
|
|
82
193
|
// Extract the key from the modified data using the collection's getKey function
|
|
83
194
|
// This is needed for optimistic state restoration to work correctly
|
|
@@ -89,8 +200,8 @@ export class TransactionSerializer {
|
|
|
89
200
|
globalKey: data.globalKey,
|
|
90
201
|
type: data.type as any,
|
|
91
202
|
modified,
|
|
92
|
-
original: this.deserializeValue(data.original),
|
|
93
|
-
changes: this.deserializeValue(data.changes) ?? {},
|
|
203
|
+
original: this.deserializeValue(data.original, valueEncoding),
|
|
204
|
+
changes: this.deserializeValue(data.changes, valueEncoding) ?? {},
|
|
94
205
|
collection,
|
|
95
206
|
// These fields would need to be reconstructed by the executor
|
|
96
207
|
mutationId: ``, // Will be regenerated
|
|
@@ -103,29 +214,104 @@ export class TransactionSerializer {
|
|
|
103
214
|
} as PendingMutation
|
|
104
215
|
}
|
|
105
216
|
|
|
106
|
-
private serializeValue(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
217
|
+
private serializeValue(
|
|
218
|
+
value: any,
|
|
219
|
+
jsonKey?: string | false,
|
|
220
|
+
ancestors = new WeakSet<object>(),
|
|
221
|
+
): any {
|
|
222
|
+
if (value === null || typeof value !== `object`) return value
|
|
110
223
|
|
|
111
|
-
if (value instanceof Date) {
|
|
224
|
+
if (jsonKey !== false && value instanceof Date) {
|
|
112
225
|
return { __type: `Date`, value: value.toISOString() }
|
|
113
226
|
}
|
|
114
227
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
228
|
+
const temporalConstructorName =
|
|
229
|
+
jsonKey !== false && mayBeNativeScalar(value)
|
|
230
|
+
? getTemporalConstructorName(value[Symbol.toStringTag])
|
|
231
|
+
: undefined
|
|
232
|
+
if (temporalConstructorName) {
|
|
233
|
+
const temporalValue = serializeTemporalValue(
|
|
234
|
+
value,
|
|
235
|
+
temporalConstructorName,
|
|
236
|
+
)
|
|
237
|
+
if (temporalValue !== undefined)
|
|
238
|
+
return {
|
|
239
|
+
__type: `Temporal`,
|
|
240
|
+
type: `Temporal.${temporalConstructorName}`,
|
|
241
|
+
value: temporalValue,
|
|
120
242
|
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (ancestors.has(value))
|
|
246
|
+
throw new TypeError(`Converting circular structure to JSON`)
|
|
247
|
+
|
|
248
|
+
const toJSON = typeof jsonKey === `string` && value.toJSON
|
|
249
|
+
if (typeof toJSON === `function`) {
|
|
250
|
+
const replacement = toJSON.call(value, jsonKey)
|
|
251
|
+
if (replacement === value) {
|
|
252
|
+
return this.serializeValue(replacement, false, ancestors)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
ancestors.add(value)
|
|
256
|
+
try {
|
|
257
|
+
return this.serializeValue(replacement, false, ancestors)
|
|
258
|
+
} finally {
|
|
259
|
+
ancestors.delete(value)
|
|
121
260
|
}
|
|
122
|
-
|
|
261
|
+
}
|
|
262
|
+
if (
|
|
263
|
+
jsonKey !== undefined &&
|
|
264
|
+
(value instanceof Boolean ||
|
|
265
|
+
value instanceof BigInt ||
|
|
266
|
+
value instanceof Number ||
|
|
267
|
+
value instanceof String)
|
|
268
|
+
) {
|
|
269
|
+
return value.valueOf()
|
|
123
270
|
}
|
|
124
271
|
|
|
125
|
-
|
|
272
|
+
ancestors.add(value)
|
|
273
|
+
|
|
274
|
+
const isArray = Array.isArray(value)
|
|
275
|
+
const result: any = isArray ? [] : {}
|
|
276
|
+
try {
|
|
277
|
+
if (isArray) {
|
|
278
|
+
const length = value.length
|
|
279
|
+
for (let index = 0; index < length; index++) {
|
|
280
|
+
const key = String(index)
|
|
281
|
+
setDataProperty(
|
|
282
|
+
result,
|
|
283
|
+
key,
|
|
284
|
+
this.serializeValue(
|
|
285
|
+
value[index],
|
|
286
|
+
jsonKey === undefined ? undefined : key,
|
|
287
|
+
ancestors,
|
|
288
|
+
),
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
for (const key of Object.keys(value)) {
|
|
293
|
+
setDataProperty(
|
|
294
|
+
result,
|
|
295
|
+
key,
|
|
296
|
+
this.serializeValue(
|
|
297
|
+
value[key],
|
|
298
|
+
jsonKey === undefined ? undefined : key,
|
|
299
|
+
ancestors,
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} finally {
|
|
305
|
+
ancestors.delete(value)
|
|
306
|
+
}
|
|
307
|
+
if (jsonKey === false && typeof result.toJSON === `function`)
|
|
308
|
+
delete result.toJSON
|
|
309
|
+
return !isArray && Object.prototype.hasOwnProperty.call(value, `__type`)
|
|
310
|
+
? { __type: `Object`, value: result }
|
|
311
|
+
: result
|
|
126
312
|
}
|
|
127
313
|
|
|
128
|
-
private deserializeValue(value: any): any {
|
|
314
|
+
private deserializeValue(value: any, valueEncoding: 2 | 3 | undefined): any {
|
|
129
315
|
if (value === null || value === undefined) {
|
|
130
316
|
return value
|
|
131
317
|
}
|
|
@@ -143,11 +329,39 @@ export class TransactionSerializer {
|
|
|
143
329
|
return date
|
|
144
330
|
}
|
|
145
331
|
|
|
332
|
+
if (
|
|
333
|
+
valueEncoding === 3 &&
|
|
334
|
+
typeof value === `object` &&
|
|
335
|
+
value.__type === `Temporal`
|
|
336
|
+
) {
|
|
337
|
+
const constructorName = getTemporalConstructorName(value.type)
|
|
338
|
+
if (!constructorName)
|
|
339
|
+
throw new Error(`Corrupted Temporal marker: invalid type field`)
|
|
340
|
+
if (typeof value.value !== `string`)
|
|
341
|
+
throw new Error(`Corrupted Temporal marker: missing value field`)
|
|
342
|
+
return requireTemporalConstructor(constructorName).from(value.value)
|
|
343
|
+
}
|
|
344
|
+
|
|
146
345
|
if (typeof value === `object`) {
|
|
346
|
+
// Unwrap once, then decode only the fields: the object's own __type is data.
|
|
347
|
+
if (valueEncoding !== undefined && value.__type === `Object`) {
|
|
348
|
+
if (
|
|
349
|
+
value.value === null ||
|
|
350
|
+
typeof value.value !== `object` ||
|
|
351
|
+
Array.isArray(value.value)
|
|
352
|
+
) {
|
|
353
|
+
throw new Error(`Corrupted Object marker: expected an object value`)
|
|
354
|
+
}
|
|
355
|
+
value = value.value
|
|
356
|
+
}
|
|
147
357
|
const result: any = Array.isArray(value) ? [] : {}
|
|
148
358
|
for (const key in value) {
|
|
149
359
|
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
150
|
-
|
|
360
|
+
setDataProperty(
|
|
361
|
+
result,
|
|
362
|
+
key,
|
|
363
|
+
this.deserializeValue(value[key], valueEncoding),
|
|
364
|
+
)
|
|
151
365
|
}
|
|
152
366
|
}
|
|
153
367
|
return result
|
|
@@ -89,6 +89,23 @@ export class IndexedDBAdapter extends BaseStorageAdapter {
|
|
|
89
89
|
return transaction.objectStore(this.storeName)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
private waitForWrite(
|
|
93
|
+
store: IDBObjectStore,
|
|
94
|
+
write: () => void,
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
const transaction = store.transaction
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
// A successful request can still be rolled back by its transaction.
|
|
99
|
+
transaction.oncomplete = () => resolve()
|
|
100
|
+
transaction.onabort = () =>
|
|
101
|
+
reject(
|
|
102
|
+
transaction.error ??
|
|
103
|
+
new DOMException(`IndexedDB transaction aborted`, `AbortError`),
|
|
104
|
+
)
|
|
105
|
+
write()
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
92
109
|
async get(key: string): Promise<string | null> {
|
|
93
110
|
try {
|
|
94
111
|
const store = await this.getStore(`readonly`)
|
|
@@ -106,10 +123,8 @@ export class IndexedDBAdapter extends BaseStorageAdapter {
|
|
|
106
123
|
async set(key: string, value: string): Promise<void> {
|
|
107
124
|
try {
|
|
108
125
|
const store = await this.getStore(`readwrite`)
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
request.onerror = () => reject(request.error)
|
|
112
|
-
request.onsuccess = () => resolve()
|
|
126
|
+
return this.waitForWrite(store, () => {
|
|
127
|
+
store.put(value, key)
|
|
113
128
|
})
|
|
114
129
|
} catch (error) {
|
|
115
130
|
if (
|
|
@@ -127,10 +142,8 @@ export class IndexedDBAdapter extends BaseStorageAdapter {
|
|
|
127
142
|
async delete(key: string): Promise<void> {
|
|
128
143
|
try {
|
|
129
144
|
const store = await this.getStore(`readwrite`)
|
|
130
|
-
return
|
|
131
|
-
|
|
132
|
-
request.onerror = () => reject(request.error)
|
|
133
|
-
request.onsuccess = () => resolve()
|
|
145
|
+
return this.waitForWrite(store, () => {
|
|
146
|
+
store.delete(key)
|
|
134
147
|
})
|
|
135
148
|
} catch (error) {
|
|
136
149
|
console.warn(`IndexedDB delete failed:`, error)
|
|
@@ -154,10 +167,8 @@ export class IndexedDBAdapter extends BaseStorageAdapter {
|
|
|
154
167
|
async clear(): Promise<void> {
|
|
155
168
|
try {
|
|
156
169
|
const store = await this.getStore(`readwrite`)
|
|
157
|
-
return
|
|
158
|
-
|
|
159
|
-
request.onerror = () => reject(request.error)
|
|
160
|
-
request.onsuccess = () => resolve()
|
|
170
|
+
return this.waitForWrite(store, () => {
|
|
171
|
+
store.clear()
|
|
161
172
|
})
|
|
162
173
|
} catch (error) {
|
|
163
174
|
console.warn(`IndexedDB clear failed:`, error)
|
package/src/types.ts
CHANGED
|
@@ -57,6 +57,8 @@ export interface OfflineTransaction {
|
|
|
57
57
|
|
|
58
58
|
// Serialized representation for storage
|
|
59
59
|
export interface SerializedOfflineTransaction {
|
|
60
|
+
/** Absent for the original Date-marker format. */
|
|
61
|
+
valueEncoding?: 2 | 3
|
|
60
62
|
id: string
|
|
61
63
|
mutationFnName: string
|
|
62
64
|
mutations: Array<SerializedMutation>
|
|
@@ -131,6 +133,7 @@ export interface LeaderElection {
|
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
export interface TransactionSignaler {
|
|
136
|
+
readonly isOfflineEnabled: boolean
|
|
134
137
|
resolveTransaction: (transactionId: string, result: any) => void
|
|
135
138
|
rejectTransaction: (transactionId: string, error: Error) => void
|
|
136
139
|
registerRestorationTransaction: (
|