@trainstar/synchro-react-native 0.1.2 → 0.3.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.
- package/README.md +103 -24
- package/SynchroReactNative.podspec +3 -2
- package/android/build.gradle +8 -3
- package/android/src/main/kotlin/com/trainstar/synchro/rn/SynchroModule.kt +1105 -238
- package/ios/SynchroModule.mm +178 -51
- package/ios/SynchroModule.swift +1382 -295
- package/lib/module/NativeSynchro.js +5 -0
- package/lib/module/NativeSynchro.js.map +1 -1
- package/lib/module/SynchroClient.js +637 -65
- package/lib/module/SynchroClient.js.map +1 -1
- package/lib/module/digest.js +82 -0
- package/lib/module/digest.js.map +1 -0
- package/lib/module/errors.js +165 -22
- package/lib/module/errors.js.map +1 -1
- package/lib/module/hooks/useQuery.js +37 -3
- package/lib/module/hooks/useQuery.js.map +1 -1
- package/lib/module/hooks/useSyncStatus.js +51 -6
- package/lib/module/hooks/useSyncStatus.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/inspection.js +121 -0
- package/lib/module/inspection.js.map +1 -0
- package/lib/module/sqliteValues.js +87 -0
- package/lib/module/sqliteValues.js.map +1 -0
- package/lib/module/types.js +10 -0
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/example/e2e/corpus-harness.d.ts +34 -0
- package/lib/typescript/example/e2e/corpus-harness.d.ts.map +1 -0
- package/lib/typescript/example/src/conformance/runner.d.ts +125 -0
- package/lib/typescript/example/src/conformance/runner.d.ts.map +1 -0
- package/lib/typescript/example/src/conformance/types.d.ts +71 -0
- package/lib/typescript/example/src/conformance/types.d.ts.map +1 -0
- package/lib/typescript/src/NativeSynchro.d.ts +59 -17
- package/lib/typescript/src/NativeSynchro.d.ts.map +1 -1
- package/lib/typescript/src/SynchroClient.d.ts +27 -10
- package/lib/typescript/src/SynchroClient.d.ts.map +1 -1
- package/lib/typescript/src/digest.d.ts +3 -0
- package/lib/typescript/src/digest.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +37 -9
- package/lib/typescript/src/errors.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useQuery.d.ts +2 -2
- package/lib/typescript/src/hooks/useQuery.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useSyncStatus.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/inspection.d.ts +43 -0
- package/lib/typescript/src/inspection.d.ts.map +1 -0
- package/lib/typescript/src/sqliteValues.d.ts +8 -0
- package/lib/typescript/src/sqliteValues.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +203 -11
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +20 -8
- package/src/NativeSynchro.ts +93 -26
- package/src/SynchroClient.ts +899 -120
- package/src/digest.ts +93 -0
- package/src/errors.ts +273 -33
- package/src/hooks/useQuery.ts +59 -16
- package/src/hooks/useSyncStatus.ts +40 -7
- package/src/index.ts +44 -3
- package/src/inspection.ts +226 -0
- package/src/sqliteValues.ts +105 -0
- package/src/types.ts +317 -16
|
@@ -2,27 +2,505 @@
|
|
|
2
2
|
|
|
3
3
|
import { Platform } from 'react-native';
|
|
4
4
|
import NativeSynchro from "./NativeSynchro.js";
|
|
5
|
-
import { mapNativeError } from "./errors.js";
|
|
5
|
+
import { InvalidResponseError, SynchroError, mapNativeError, parseSyncFailure } from "./errors.js";
|
|
6
|
+
import { assertValidSQLiteBindParams } from "./sqliteValues.js";
|
|
7
|
+
import { SYNC_OPERATION_KINDS, SYNC_STATUS_TYPES } from "./types.js";
|
|
6
8
|
let observerCounter = 0;
|
|
9
|
+
let activeNativeOwner = null;
|
|
10
|
+
const inspectionOwners = new WeakMap();
|
|
11
|
+
const inspectionCapacities = new WeakMap();
|
|
12
|
+
const inspectionRequireNewDatabases = new WeakMap();
|
|
7
13
|
function nextObserverID() {
|
|
8
14
|
return `obs_${++observerCounter}_${Date.now()}`;
|
|
9
15
|
}
|
|
16
|
+
function nullableRow(row) {
|
|
17
|
+
return row == null ? null : row;
|
|
18
|
+
}
|
|
19
|
+
const SYNC_EVENT_TYPES = ['state_changed', 'backoff', 'schema_applying', 'schema_applied', 'mutation_accepted', 'mutation_rejected', 'rebuild_requested', 'rebuild_completed', 'failure'];
|
|
20
|
+
const SCHEMA_ACTIONS = ['none', 'replace', 'rebuild_local', 'unsupported'];
|
|
21
|
+
const MUTATION_STATUSES = ['applied', 'conflict', 'rejected_terminal'];
|
|
22
|
+
const MUTATION_REJECTION_CODES = ['version_conflict', 'row_already_exists', 'row_deleted', 'row_not_found', 'schema_incompatible', 'policy_rejected', 'validation_failed', 'table_not_synced'];
|
|
23
|
+
const MUTATION_OPERATIONS = ['insert', 'upsert', 'update', 'delete'];
|
|
24
|
+
const LOCAL_MUTATION_STATUSES = ['pending', 'sealed', 'superseded_before_send', 'cancelled_before_send', 'blocked_by_predecessor', 'server_rejected'];
|
|
25
|
+
const TRANSPORT_OPERATION_CLASSES = ['connect', 'pull', 'push', 'checkpoint', 'schemas', 'rebuild', 'other'];
|
|
26
|
+
const LEGAL_SYNC_STATUS_TRANSITIONS = {
|
|
27
|
+
uninitialized: ['local_ready', 'error', 'stopped'],
|
|
28
|
+
local_ready: ['connecting', 'error', 'stopped'],
|
|
29
|
+
connecting: ['schema_applying', 'ready', 'backoff', 'error', 'stopped'],
|
|
30
|
+
schema_applying: ['ready', 'rebuilding', 'error', 'stopped'],
|
|
31
|
+
ready: ['connecting', 'pushing', 'pulling', 'rebuilding', 'error', 'stopped'],
|
|
32
|
+
pushing: ['pushing', 'ready', 'pulling', 'connecting', 'backoff', 'error', 'stopped'],
|
|
33
|
+
pulling: ['pulling', 'ready', 'rebuilding', 'connecting', 'backoff', 'error', 'stopped'],
|
|
34
|
+
rebuilding: ['rebuilding', 'ready', 'connecting', 'backoff', 'error', 'stopped'],
|
|
35
|
+
backoff: ['connecting', 'pushing', 'pulling', 'rebuilding', 'error', 'stopped'],
|
|
36
|
+
error: ['local_ready', 'stopped'],
|
|
37
|
+
stopped: ['local_ready']
|
|
38
|
+
};
|
|
39
|
+
function isRecord(value) {
|
|
40
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
function requiredString(value, name) {
|
|
43
|
+
if (typeof value !== 'string') {
|
|
44
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
function nullableString(value, name) {
|
|
49
|
+
if (value == null) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return requiredString(value, name);
|
|
53
|
+
}
|
|
54
|
+
function requiredSafeInteger(value, name) {
|
|
55
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value)) {
|
|
56
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
function requiredNonnegativeSafeInteger(value, name) {
|
|
61
|
+
const integer = requiredSafeInteger(value, name);
|
|
62
|
+
if (integer < 0) {
|
|
63
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
64
|
+
}
|
|
65
|
+
return integer;
|
|
66
|
+
}
|
|
67
|
+
function nullableSafeInteger(value, name) {
|
|
68
|
+
return value == null ? null : requiredSafeInteger(value, name);
|
|
69
|
+
}
|
|
70
|
+
function parseDate(value, name) {
|
|
71
|
+
const stringValue = nullableString(value, name);
|
|
72
|
+
if (stringValue === null) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const date = new Date(stringValue);
|
|
76
|
+
if (Number.isNaN(date.getTime())) {
|
|
77
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
78
|
+
}
|
|
79
|
+
return date;
|
|
80
|
+
}
|
|
81
|
+
function parseStatus(value) {
|
|
82
|
+
if (!isRecord(value) || typeof value.status !== 'string') {
|
|
83
|
+
throw new InvalidResponseError('Native bridge returned an invalid sync status');
|
|
84
|
+
}
|
|
85
|
+
if (!SYNC_STATUS_TYPES.includes(value.status)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const status = value.status;
|
|
89
|
+
const retryAt = parseDate(value.retryAt, 'retryAt');
|
|
90
|
+
const operationValue = nullableString(value.operation, 'status operation');
|
|
91
|
+
const operation = operationValue === null ? null : SYNC_OPERATION_KINDS.includes(operationValue) ? operationValue : (() => {
|
|
92
|
+
throw new InvalidResponseError('Native bridge returned an invalid status operation');
|
|
93
|
+
})();
|
|
94
|
+
const failure = value.failure == null ? null : parseSyncFailure(value.failure);
|
|
95
|
+
if (status === 'backoff') {
|
|
96
|
+
if (retryAt === null || operation === null || failure !== null) {
|
|
97
|
+
throw new InvalidResponseError('Native bridge returned invalid backoff status details');
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
status,
|
|
101
|
+
retryAt,
|
|
102
|
+
operation,
|
|
103
|
+
failure: null
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (status === 'error') {
|
|
107
|
+
if (retryAt !== null || operation !== null || failure === null) {
|
|
108
|
+
throw new InvalidResponseError('Native bridge returned invalid error status details');
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
status,
|
|
112
|
+
retryAt: null,
|
|
113
|
+
operation: null,
|
|
114
|
+
failure
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (retryAt !== null || operation !== null || failure !== null) {
|
|
118
|
+
throw new InvalidResponseError('Native bridge returned invalid state detail fields');
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
status,
|
|
122
|
+
retryAt: null,
|
|
123
|
+
operation: null,
|
|
124
|
+
failure: null
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function parseSchemaRef(value, name) {
|
|
128
|
+
if (!isRecord(value) || typeof value.version !== 'number' || !Number.isSafeInteger(value.version)) {
|
|
129
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
version: value.version,
|
|
133
|
+
hash: requiredString(value.hash, `${name} hash`)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function parseMutationStatus(value) {
|
|
137
|
+
if (!MUTATION_STATUSES.includes(value)) {
|
|
138
|
+
throw new InvalidResponseError('Native bridge returned an invalid mutation status');
|
|
139
|
+
}
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
function parseRejectionCode(value) {
|
|
143
|
+
if (value == null) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
if (!MUTATION_REJECTION_CODES.includes(value)) {
|
|
147
|
+
throw new InvalidResponseError('Native bridge returned an invalid mutation rejection code');
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
function parseSchemaAction(value) {
|
|
152
|
+
if (!SCHEMA_ACTIONS.includes(value)) {
|
|
153
|
+
throw new InvalidResponseError('Native bridge returned an invalid schema action');
|
|
154
|
+
}
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
function parseSyncEvent(value) {
|
|
158
|
+
if (typeof value.type !== 'string') {
|
|
159
|
+
throw new InvalidResponseError('Native bridge returned an invalid sync event');
|
|
160
|
+
}
|
|
161
|
+
if (!SYNC_EVENT_TYPES.includes(value.type)) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
switch (value.type) {
|
|
165
|
+
case 'state_changed':
|
|
166
|
+
{
|
|
167
|
+
if (typeof value.from !== 'string' || typeof value.to !== 'string') {
|
|
168
|
+
throw new InvalidResponseError('Native bridge returned an invalid lifecycle event');
|
|
169
|
+
}
|
|
170
|
+
if (!SYNC_STATUS_TYPES.includes(value.from) || !SYNC_STATUS_TYPES.includes(value.to)) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
const from = value.from;
|
|
174
|
+
const to = value.to;
|
|
175
|
+
if (!LEGAL_SYNC_STATUS_TRANSITIONS[from].includes(to)) {
|
|
176
|
+
throw new InvalidResponseError('Native bridge returned an invalid lifecycle event');
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
type: 'state_changed',
|
|
180
|
+
from,
|
|
181
|
+
to
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case 'backoff':
|
|
185
|
+
{
|
|
186
|
+
const attempt = value.attempt;
|
|
187
|
+
if (typeof attempt !== 'number' || !Number.isSafeInteger(attempt) || attempt < 0) {
|
|
188
|
+
throw new InvalidResponseError('Native bridge returned an invalid backoff attempt');
|
|
189
|
+
}
|
|
190
|
+
const retryAt = parseDate(value.retryAt, 'backoff retryAt');
|
|
191
|
+
if (retryAt === null) {
|
|
192
|
+
throw new InvalidResponseError('Native bridge returned a missing backoff retryAt');
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
type: 'backoff',
|
|
196
|
+
operation: SYNC_OPERATION_KINDS.includes(value.operation) ? value.operation : (() => {
|
|
197
|
+
throw new InvalidResponseError('Native bridge returned an invalid backoff operation');
|
|
198
|
+
})(),
|
|
199
|
+
attempt,
|
|
200
|
+
retryAt
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
case 'schema_applying':
|
|
204
|
+
case 'schema_applied':
|
|
205
|
+
return {
|
|
206
|
+
type: value.type,
|
|
207
|
+
source: parseSchemaRef(value.source, 'schema source'),
|
|
208
|
+
target: parseSchemaRef(value.target, 'schema target'),
|
|
209
|
+
action: parseSchemaAction(value.action)
|
|
210
|
+
};
|
|
211
|
+
case 'mutation_accepted':
|
|
212
|
+
case 'mutation_rejected':
|
|
213
|
+
{
|
|
214
|
+
const mutationID = requiredString(value.mutationID, 'mutation ID');
|
|
215
|
+
return {
|
|
216
|
+
type: value.type,
|
|
217
|
+
mutationID,
|
|
218
|
+
tableID: requiredString(value.tableID, 'mutation table ID'),
|
|
219
|
+
status: parseMutationStatus(value.mutationStatus),
|
|
220
|
+
rejectionCode: parseRejectionCode(value.rejectionCode)
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
case 'rebuild_requested':
|
|
224
|
+
case 'rebuild_completed':
|
|
225
|
+
return {
|
|
226
|
+
type: value.type,
|
|
227
|
+
scopeID: requiredString(value.scopeID, 'rebuild scope ID'),
|
|
228
|
+
rebuildID: requiredString(value.rebuildID, 'rebuild ID')
|
|
229
|
+
};
|
|
230
|
+
case 'failure':
|
|
231
|
+
{
|
|
232
|
+
return {
|
|
233
|
+
type: 'failure',
|
|
234
|
+
failure: parseSyncFailure(value.failure)
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function parseNativeDTO(json) {
|
|
240
|
+
try {
|
|
241
|
+
return JSON.parse(json);
|
|
242
|
+
} catch {
|
|
243
|
+
throw new InvalidResponseError('Native bridge returned malformed JSON');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function parseNativeArray(json, name) {
|
|
247
|
+
const value = parseNativeDTO(json);
|
|
248
|
+
if (!Array.isArray(value)) {
|
|
249
|
+
throw new InvalidResponseError(`Native bridge returned invalid ${name}`);
|
|
250
|
+
}
|
|
251
|
+
return value;
|
|
252
|
+
}
|
|
253
|
+
function parsePendingMutationInspection(value, index) {
|
|
254
|
+
const name = `pending mutation at index ${index}`;
|
|
255
|
+
if (!isRecord(value)) {
|
|
256
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
257
|
+
}
|
|
258
|
+
if (!MUTATION_OPERATIONS.includes(value.operation)) {
|
|
259
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name} operation`);
|
|
260
|
+
}
|
|
261
|
+
if (!LOCAL_MUTATION_STATUSES.includes(value.status)) {
|
|
262
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name} status`);
|
|
263
|
+
}
|
|
264
|
+
if (!Array.isArray(value.authoredFields)) {
|
|
265
|
+
throw new InvalidResponseError(`Native bridge returned invalid ${name} fields`);
|
|
266
|
+
}
|
|
267
|
+
const authoredFields = value.authoredFields.map((field, fieldIndex) => {
|
|
268
|
+
if (!isRecord(field) || !Object.prototype.hasOwnProperty.call(field, 'value')) {
|
|
269
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name} field at index ${fieldIndex}`);
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
fieldID: requiredString(field.fieldID, `${name} field ID`),
|
|
273
|
+
logicalType: requiredString(field.logicalType, `${name} field logical type`),
|
|
274
|
+
value: field.value
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
mutationID: requiredString(value.mutationID, `${name} ID`),
|
|
279
|
+
localOrder: requiredSafeInteger(value.localOrder, `${name} local order`),
|
|
280
|
+
tableID: requiredString(value.tableID, `${name} table ID`),
|
|
281
|
+
tableName: requiredString(value.tableName, `${name} table name`),
|
|
282
|
+
recordID: requiredString(value.recordID, `${name} record ID`),
|
|
283
|
+
primaryKeyFieldID: requiredString(value.primaryKeyFieldID, `${name} primary-key field ID`),
|
|
284
|
+
primaryKeyLogicalType: requiredString(value.primaryKeyLogicalType, `${name} primary-key logical type`),
|
|
285
|
+
operation: value.operation,
|
|
286
|
+
authoredSchema: parseSchemaRef(value.authoredSchema, `${name} authored schema`),
|
|
287
|
+
baseVersion: nullableString(value.baseVersion, `${name} base version`),
|
|
288
|
+
clientVersion: requiredString(value.clientVersion, `${name} client version`),
|
|
289
|
+
status: value.status,
|
|
290
|
+
sourceKind: requiredString(value.sourceKind, `${name} source kind`),
|
|
291
|
+
dependsOnMutationID: nullableString(value.dependsOnMutationID, `${name} predecessor mutation ID`),
|
|
292
|
+
normalizedMutationID: nullableString(value.normalizedMutationID, `${name} normalized mutation ID`),
|
|
293
|
+
sealedBatchID: nullableString(value.sealedBatchID, `${name} sealed batch ID`),
|
|
294
|
+
sealedOrdinal: nullableSafeInteger(value.sealedOrdinal, `${name} sealed ordinal`),
|
|
295
|
+
authoredFields
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function parseRejectedMutationInspection(value, index) {
|
|
299
|
+
const name = `rejected mutation at index ${index}`;
|
|
300
|
+
if (!isRecord(value)) {
|
|
301
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
302
|
+
}
|
|
303
|
+
const code = parseRejectionCode(value.code);
|
|
304
|
+
if (code === null) {
|
|
305
|
+
throw new InvalidResponseError(`Native bridge returned a missing ${name} code`);
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
mutationID: requiredString(value.mutationID, `${name} ID`),
|
|
309
|
+
tableName: requiredString(value.tableName, `${name} table name`),
|
|
310
|
+
recordID: requiredString(value.recordID, `${name} record ID`),
|
|
311
|
+
status: parseMutationStatus(value.status),
|
|
312
|
+
code,
|
|
313
|
+
message: nullableString(value.message, `${name} message`),
|
|
314
|
+
serverRowJSON: nullableString(value.serverRowJSON, `${name} server row JSON`),
|
|
315
|
+
serverVersion: nullableString(value.serverVersion, `${name} server version`),
|
|
316
|
+
mutationJSON: requiredString(value.mutationJSON, `${name} mutation JSON`),
|
|
317
|
+
rejectionJSON: requiredString(value.rejectionJSON, `${name} rejection JSON`),
|
|
318
|
+
createdAt: requiredString(value.createdAt, `${name} creation time`),
|
|
319
|
+
updatedAt: requiredString(value.updatedAt, `${name} update time`)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function parseScopeStateInspection(value, index) {
|
|
323
|
+
const name = `scope state at index ${index}`;
|
|
324
|
+
if (!isRecord(value)) {
|
|
325
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
scopeID: requiredString(value.scope_id, `${name} ID`),
|
|
329
|
+
cursor: nullableString(value.cursor, `${name} cursor`),
|
|
330
|
+
checksum: nullableString(value.checksum, `${name} checksum`),
|
|
331
|
+
localChecksum: requiredString(value.local_checksum, `${name} local checksum`),
|
|
332
|
+
generation: requiredSafeInteger(value.generation, `${name} generation`)
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function parseScopeRowInspection(value, index) {
|
|
336
|
+
const name = `scope row at index ${index}`;
|
|
337
|
+
if (!isRecord(value)) {
|
|
338
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
scopeID: requiredString(value.scope_id, `${name} scope ID`),
|
|
342
|
+
tableName: requiredString(value.table_name, `${name} table name`),
|
|
343
|
+
recordID: requiredString(value.record_id, `${name} record ID`),
|
|
344
|
+
checksum: requiredString(value.checksum, `${name} checksum`),
|
|
345
|
+
generation: requiredSafeInteger(value.generation, `${name} generation`)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function parseRebuildAttemptInspection(value, index) {
|
|
349
|
+
const name = `rebuild attempt at index ${index}`;
|
|
350
|
+
if (!isRecord(value)) {
|
|
351
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
scopeID: requiredString(value.scope_id, `${name} scope ID`),
|
|
355
|
+
rebuildID: requiredString(value.rebuild_id, `${name} rebuild ID`),
|
|
356
|
+
clientGeneration: requiredSafeInteger(value.client_generation, `${name} client generation`),
|
|
357
|
+
schemaVersion: requiredSafeInteger(value.schema_version, `${name} schema version`),
|
|
358
|
+
schemaHash: requiredString(value.schema_hash, `${name} schema hash`),
|
|
359
|
+
generation: requiredSafeInteger(value.generation, `${name} generation`),
|
|
360
|
+
cursor: nullableString(value.cursor, `${name} cursor`),
|
|
361
|
+
pageLimit: requiredSafeInteger(value.page_limit, `${name} page limit`)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
export function parseClientStateInspection(value) {
|
|
365
|
+
if (!isRecord(value)) {
|
|
366
|
+
throw new InvalidResponseError('Native bridge returned invalid client state inspection');
|
|
367
|
+
}
|
|
368
|
+
const schema = value.schema == null ? null : parseSchemaRef(value.schema, 'client schema');
|
|
369
|
+
if (!Array.isArray(value.scope_states) || !Array.isArray(value.scope_rows) || !Array.isArray(value.rebuild_attempts)) {
|
|
370
|
+
throw new InvalidResponseError('Native bridge returned incomplete client state inspection');
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
schema,
|
|
374
|
+
scopeStates: value.scope_states.map(parseScopeStateInspection),
|
|
375
|
+
scopeRows: value.scope_rows.map(parseScopeRowInspection),
|
|
376
|
+
rebuildAttempts: value.rebuild_attempts.map(parseRebuildAttemptInspection),
|
|
377
|
+
applicationRowCount: requiredNonnegativeSafeInteger(value.application_row_count, 'application row count'),
|
|
378
|
+
mutationLedgerCount: requiredNonnegativeSafeInteger(value.mutation_ledger_count, 'mutation ledger count'),
|
|
379
|
+
mutationOutcomeCount: requiredNonnegativeSafeInteger(value.mutation_outcome_count, 'mutation outcome count'),
|
|
380
|
+
sealedBatchCount: requiredNonnegativeSafeInteger(value.sealed_batch_count, 'sealed batch count'),
|
|
381
|
+
rejectedMutationCount: requiredNonnegativeSafeInteger(value.rejected_mutation_count, 'rejected mutation count'),
|
|
382
|
+
scopeStateCount: requiredNonnegativeSafeInteger(value.scope_state_count, 'scope state count'),
|
|
383
|
+
scopeRowCount: requiredNonnegativeSafeInteger(value.scope_row_count, 'scope row count'),
|
|
384
|
+
provenanceCount: requiredNonnegativeSafeInteger(value.provenance_count, 'provenance count'),
|
|
385
|
+
rowMetadataCount: requiredNonnegativeSafeInteger(value.row_metadata_count, 'row metadata count'),
|
|
386
|
+
rebuildAttemptCount: requiredNonnegativeSafeInteger(value.rebuild_attempt_count, 'rebuild attempt count'),
|
|
387
|
+
rebuildReceiptCount: requiredNonnegativeSafeInteger(value.rebuild_receipt_count, 'rebuild receipt count'),
|
|
388
|
+
provenanceMaintenanceWorkCursor: requiredNonnegativeInt64String(value.provenance_maintenance_work_cursor, 'provenance maintenance work cursor')
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function requiredNonnegativeInt64String(value, name) {
|
|
392
|
+
const maximum = '9223372036854775807';
|
|
393
|
+
if (typeof value !== 'string' || !/^(0|[1-9][0-9]*)$/.test(value) || value.length > maximum.length || value.length === maximum.length && value > maximum) {
|
|
394
|
+
throw new InvalidResponseError(`Native bridge returned invalid ${name}`);
|
|
395
|
+
}
|
|
396
|
+
return value;
|
|
397
|
+
}
|
|
398
|
+
function optionalTransportFacts(value, name) {
|
|
399
|
+
if (value == null) {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
if (!isRecord(value)) {
|
|
403
|
+
throw new InvalidResponseError(`Native bridge returned invalid ${name}`);
|
|
404
|
+
}
|
|
405
|
+
return value;
|
|
406
|
+
}
|
|
407
|
+
function optionalTransportRequestFacts(value, name) {
|
|
408
|
+
const facts = optionalTransportFacts(value, name);
|
|
409
|
+
const mutationCount = facts?.mutation_count;
|
|
410
|
+
if (facts !== undefined && Object.prototype.hasOwnProperty.call(facts, 'mutation_count') && (typeof mutationCount !== 'number' || !Number.isSafeInteger(mutationCount) || mutationCount < 0)) {
|
|
411
|
+
throw new InvalidResponseError(`Native bridge returned invalid ${name} mutation count`);
|
|
412
|
+
}
|
|
413
|
+
return facts;
|
|
414
|
+
}
|
|
415
|
+
function parseTransportObservation(value, index) {
|
|
416
|
+
const name = `transport observation at index ${index}`;
|
|
417
|
+
if (!isRecord(value) || !TRANSPORT_OPERATION_CLASSES.includes(value.operation_class)) {
|
|
418
|
+
throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
|
|
419
|
+
}
|
|
420
|
+
const cursorFingerprints = value.cursor_fingerprints;
|
|
421
|
+
if (cursorFingerprints !== undefined && (!Array.isArray(cursorFingerprints) || !cursorFingerprints.every(item => typeof item === 'string'))) {
|
|
422
|
+
throw new InvalidResponseError(`Native bridge returned invalid ${name} cursor fingerprints`);
|
|
423
|
+
}
|
|
424
|
+
const cursorFingerprintsComplete = value.cursor_fingerprints_complete;
|
|
425
|
+
if (cursorFingerprintsComplete !== undefined && typeof cursorFingerprintsComplete !== 'boolean') {
|
|
426
|
+
throw new InvalidResponseError(`Native bridge returned invalid ${name} cursor completeness`);
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
sequence: requiredSafeInteger(value.sequence, `${name} sequence`),
|
|
430
|
+
operationClass: value.operation_class,
|
|
431
|
+
statusCode: requiredSafeInteger(value.status_code, `${name} status code`),
|
|
432
|
+
durationNanoseconds: requiredSafeInteger(value.duration_nanoseconds, `${name} duration`),
|
|
433
|
+
...(cursorFingerprints === undefined ? {} : {
|
|
434
|
+
cursorFingerprints: [...cursorFingerprints]
|
|
435
|
+
}),
|
|
436
|
+
...(cursorFingerprintsComplete === undefined ? {} : {
|
|
437
|
+
cursorFingerprintsComplete
|
|
438
|
+
}),
|
|
439
|
+
...(value.request_facts == null ? {} : {
|
|
440
|
+
requestFacts: optionalTransportRequestFacts(value.request_facts, `${name} request facts`)
|
|
441
|
+
}),
|
|
442
|
+
...(value.rebuild_response_facts == null ? {} : {
|
|
443
|
+
rebuildResponseFacts: optionalTransportFacts(value.rebuild_response_facts, `${name} rebuild response facts`)
|
|
444
|
+
}),
|
|
445
|
+
...(value.pull_response_facts == null ? {} : {
|
|
446
|
+
pullResponseFacts: optionalTransportFacts(value.pull_response_facts, `${name} pull response facts`)
|
|
447
|
+
})
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
export function parseTransportObservationSnapshot(value) {
|
|
451
|
+
if (!isRecord(value) || !Array.isArray(value.observations) || typeof value.overflowed !== 'boolean') {
|
|
452
|
+
throw new InvalidResponseError('Native bridge returned invalid transport observations');
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
observations: value.observations.map(parseTransportObservation),
|
|
456
|
+
overflowed: value.overflowed,
|
|
457
|
+
sequenceCheckpoint: requiredSafeInteger(value.sequence_checkpoint, 'transport sequence checkpoint')
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function checkedParams(params) {
|
|
461
|
+
return assertValidSQLiteBindParams(params);
|
|
462
|
+
}
|
|
10
463
|
export class SynchroClient {
|
|
11
|
-
|
|
464
|
+
nativeOwner = {};
|
|
12
465
|
authSubscription = null;
|
|
13
466
|
constructor(config) {
|
|
14
467
|
this.config = config;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
468
|
+
inspectionOwners.set(this, this.nativeOwner);
|
|
469
|
+
}
|
|
470
|
+
get native() {
|
|
471
|
+
if (activeNativeOwner !== null && activeNativeOwner !== this.nativeOwner) {
|
|
472
|
+
throw new SynchroError('CLIENT_ALREADY_ACTIVE', 'Another SynchroClient owns the native runtime');
|
|
473
|
+
}
|
|
474
|
+
return NativeSynchro;
|
|
475
|
+
}
|
|
476
|
+
installAuthSubscription() {
|
|
477
|
+
this.authSubscription?.remove();
|
|
478
|
+
const owner = this.nativeOwner;
|
|
479
|
+
this.authSubscription = NativeSynchro.onAuthRequest(event => {
|
|
18
480
|
this.config.authProvider().then(token => {
|
|
19
|
-
|
|
481
|
+
if (activeNativeOwner === owner) {
|
|
482
|
+
NativeSynchro.resolveAuthRequest(event.requestID, token);
|
|
483
|
+
}
|
|
20
484
|
}).catch(err => {
|
|
21
|
-
|
|
485
|
+
if (activeNativeOwner === owner) {
|
|
486
|
+
NativeSynchro.rejectAuthRequest(event.requestID, err instanceof Error ? err.message : String(err));
|
|
487
|
+
}
|
|
22
488
|
});
|
|
23
489
|
});
|
|
24
490
|
}
|
|
491
|
+
releaseNativeOwnership() {
|
|
492
|
+
this.authSubscription?.remove();
|
|
493
|
+
this.authSubscription = null;
|
|
494
|
+
if (activeNativeOwner === this.nativeOwner) {
|
|
495
|
+
activeNativeOwner = null;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
25
498
|
async initialize() {
|
|
499
|
+
if (activeNativeOwner !== null && activeNativeOwner !== this.nativeOwner) {
|
|
500
|
+
throw new SynchroError('CLIENT_ALREADY_ACTIVE', 'Another SynchroClient owns the native runtime');
|
|
501
|
+
}
|
|
502
|
+
activeNativeOwner = this.nativeOwner;
|
|
503
|
+
this.installAuthSubscription();
|
|
26
504
|
try {
|
|
27
505
|
await this.native.initialize({
|
|
28
506
|
dbPath: this.config.dbPath,
|
|
@@ -35,34 +513,42 @@ export class SynchroClient {
|
|
|
35
513
|
maxRetryAttempts: this.config.maxRetryAttempts ?? 5,
|
|
36
514
|
pullPageSize: this.config.pullPageSize ?? 100,
|
|
37
515
|
pushBatchSize: this.config.pushBatchSize ?? 100,
|
|
38
|
-
|
|
516
|
+
seedDatabasePath: this.config.seedDatabasePath,
|
|
517
|
+
transportObservationCapacity: inspectionCapacities.get(this) ?? 0,
|
|
518
|
+
requireNewDatabase: inspectionRequireNewDatabases.get(this) ?? false
|
|
39
519
|
});
|
|
40
520
|
} catch (error) {
|
|
521
|
+
this.releaseNativeOwnership();
|
|
41
522
|
throw mapNativeError(error);
|
|
42
523
|
}
|
|
43
524
|
}
|
|
44
525
|
|
|
45
|
-
//
|
|
526
|
+
// Core SQL
|
|
46
527
|
|
|
47
528
|
async query(sql, params) {
|
|
48
529
|
try {
|
|
49
|
-
|
|
50
|
-
return JSON.parse(json);
|
|
530
|
+
return [...(await this.native.query(sql, checkedParams(params ?? [])))];
|
|
51
531
|
} catch (error) {
|
|
52
532
|
throw mapNativeError(error);
|
|
53
533
|
}
|
|
54
534
|
}
|
|
55
535
|
async queryOne(sql, params) {
|
|
56
536
|
try {
|
|
57
|
-
|
|
58
|
-
return json ? JSON.parse(json) : null;
|
|
537
|
+
return nullableRow(await this.native.queryOne(sql, checkedParams(params ?? [])));
|
|
59
538
|
} catch (error) {
|
|
60
539
|
throw mapNativeError(error);
|
|
61
540
|
}
|
|
62
541
|
}
|
|
63
542
|
async execute(sql, params) {
|
|
64
543
|
try {
|
|
65
|
-
return await this.native.execute(sql,
|
|
544
|
+
return await this.native.execute(sql, checkedParams(params ?? []));
|
|
545
|
+
} catch (error) {
|
|
546
|
+
throw mapNativeError(error);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
async executeAuthoredWrite(tableName, operation, columnNames, sql, values) {
|
|
550
|
+
try {
|
|
551
|
+
return await this.native.executeAuthoredWrite(tableName, operation, columnNames, sql, checkedParams(values ?? []));
|
|
66
552
|
} catch (error) {
|
|
67
553
|
throw mapNativeError(error);
|
|
68
554
|
}
|
|
@@ -71,15 +557,15 @@ export class SynchroClient {
|
|
|
71
557
|
try {
|
|
72
558
|
const payload = statements.map(s => ({
|
|
73
559
|
sql: s.sql,
|
|
74
|
-
params: s.params ?? []
|
|
560
|
+
params: checkedParams(s.params ?? [])
|
|
75
561
|
}));
|
|
76
|
-
return await this.native.executeBatch(
|
|
562
|
+
return await this.native.executeBatch(payload);
|
|
77
563
|
} catch (error) {
|
|
78
564
|
throw mapNativeError(error);
|
|
79
565
|
}
|
|
80
566
|
}
|
|
81
567
|
|
|
82
|
-
//
|
|
568
|
+
// Transactions
|
|
83
569
|
|
|
84
570
|
async writeTransaction(callback) {
|
|
85
571
|
let txID;
|
|
@@ -91,15 +577,13 @@ export class SynchroClient {
|
|
|
91
577
|
try {
|
|
92
578
|
const tx = {
|
|
93
579
|
query: async (sql, params) => {
|
|
94
|
-
|
|
95
|
-
return JSON.parse(json);
|
|
580
|
+
return [...(await this.native.txQuery(txID, sql, checkedParams(params ?? [])))];
|
|
96
581
|
},
|
|
97
582
|
queryOne: async (sql, params) => {
|
|
98
|
-
|
|
99
|
-
return json ? JSON.parse(json) : null;
|
|
583
|
+
return nullableRow(await this.native.txQueryOne(txID, sql, checkedParams(params ?? [])));
|
|
100
584
|
},
|
|
101
585
|
execute: async (sql, params) => {
|
|
102
|
-
return await this.native.txExecute(txID, sql,
|
|
586
|
+
return await this.native.txExecute(txID, sql, checkedParams(params ?? []));
|
|
103
587
|
}
|
|
104
588
|
};
|
|
105
589
|
const result = await callback(tx);
|
|
@@ -124,15 +608,10 @@ export class SynchroClient {
|
|
|
124
608
|
try {
|
|
125
609
|
const tx = {
|
|
126
610
|
query: async (sql, params) => {
|
|
127
|
-
|
|
128
|
-
return JSON.parse(json);
|
|
611
|
+
return [...(await this.native.txQuery(txID, sql, checkedParams(params ?? [])))];
|
|
129
612
|
},
|
|
130
613
|
queryOne: async (sql, params) => {
|
|
131
|
-
|
|
132
|
-
return json ? JSON.parse(json) : null;
|
|
133
|
-
},
|
|
134
|
-
execute: async (sql, params) => {
|
|
135
|
-
return await this.native.txExecute(txID, sql, JSON.stringify(params ?? []));
|
|
614
|
+
return nullableRow(await this.native.txQueryOne(txID, sql, checkedParams(params ?? [])));
|
|
136
615
|
}
|
|
137
616
|
};
|
|
138
617
|
const result = await callback(tx);
|
|
@@ -148,7 +627,7 @@ export class SynchroClient {
|
|
|
148
627
|
}
|
|
149
628
|
}
|
|
150
629
|
|
|
151
|
-
//
|
|
630
|
+
// Schema, local-only
|
|
152
631
|
|
|
153
632
|
async createTable(name, columns, options) {
|
|
154
633
|
try {
|
|
@@ -172,50 +651,62 @@ export class SynchroClient {
|
|
|
172
651
|
}
|
|
173
652
|
}
|
|
174
653
|
|
|
175
|
-
//
|
|
654
|
+
// Observation
|
|
176
655
|
|
|
177
|
-
onChange(tables, callback) {
|
|
656
|
+
async onChange(tables, callback) {
|
|
178
657
|
const observerID = nextObserverID();
|
|
179
658
|
const subscription = this.native.onChange(event => {
|
|
180
659
|
if (event.observerID === observerID) {
|
|
181
660
|
callback();
|
|
182
661
|
}
|
|
183
662
|
});
|
|
184
|
-
|
|
185
|
-
|
|
663
|
+
try {
|
|
664
|
+
await this.native.addChangeObserver(observerID, tables);
|
|
665
|
+
} catch (error) {
|
|
666
|
+
subscription.remove();
|
|
667
|
+
throw mapNativeError(error);
|
|
668
|
+
}
|
|
669
|
+
return async () => {
|
|
186
670
|
subscription.remove();
|
|
187
|
-
|
|
671
|
+
try {
|
|
672
|
+
await NativeSynchro.removeObserver(observerID);
|
|
673
|
+
} catch (error) {
|
|
674
|
+
throw mapNativeError(error);
|
|
675
|
+
}
|
|
188
676
|
};
|
|
189
677
|
}
|
|
190
|
-
watch(sql, params, tables, callback) {
|
|
678
|
+
async watch(sql, params, tables, callback) {
|
|
191
679
|
const observerID = nextObserverID();
|
|
192
680
|
const subscription = this.native.onQueryResult(event => {
|
|
193
681
|
if (event.observerID === observerID) {
|
|
194
|
-
callback(
|
|
682
|
+
callback([...event.rows]);
|
|
195
683
|
}
|
|
196
684
|
});
|
|
197
|
-
this.native.addQueryObserver(observerID, sql, JSON.stringify(params ?? []), tables);
|
|
198
|
-
return () => {
|
|
199
|
-
subscription.remove();
|
|
200
|
-
this.native.removeObserver(observerID);
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// -- WAL --
|
|
205
|
-
|
|
206
|
-
async checkpoint(mode = 'passive') {
|
|
207
685
|
try {
|
|
208
|
-
await this.native.
|
|
686
|
+
await this.native.addQueryObserver(observerID, sql, checkedParams(params ?? []), tables);
|
|
209
687
|
} catch (error) {
|
|
688
|
+
subscription.remove();
|
|
210
689
|
throw mapNativeError(error);
|
|
211
690
|
}
|
|
691
|
+
return async () => {
|
|
692
|
+
subscription.remove();
|
|
693
|
+
try {
|
|
694
|
+
await NativeSynchro.removeObserver(observerID);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
throw mapNativeError(error);
|
|
697
|
+
}
|
|
698
|
+
};
|
|
212
699
|
}
|
|
213
700
|
|
|
214
|
-
//
|
|
701
|
+
// Lifecycle
|
|
215
702
|
|
|
216
703
|
async close() {
|
|
704
|
+
if (activeNativeOwner !== null && activeNativeOwner !== this.nativeOwner) {
|
|
705
|
+
throw new SynchroError('CLIENT_ALREADY_ACTIVE', 'Another SynchroClient owns the native runtime');
|
|
706
|
+
}
|
|
217
707
|
try {
|
|
218
|
-
await
|
|
708
|
+
await NativeSynchro.close();
|
|
709
|
+
this.releaseNativeOwnership();
|
|
219
710
|
} catch (error) {
|
|
220
711
|
throw mapNativeError(error);
|
|
221
712
|
}
|
|
@@ -234,8 +725,48 @@ export class SynchroClient {
|
|
|
234
725
|
throw mapNativeError(error);
|
|
235
726
|
}
|
|
236
727
|
}
|
|
728
|
+
async getSyncStatus() {
|
|
729
|
+
try {
|
|
730
|
+
const status = parseNativeDTO(await this.native.getSyncStatus());
|
|
731
|
+
const parsed = parseStatus(status);
|
|
732
|
+
if (parsed === null) {
|
|
733
|
+
throw new InvalidResponseError('Native bridge returned an unknown sync status');
|
|
734
|
+
}
|
|
735
|
+
return parsed;
|
|
736
|
+
} catch (error) {
|
|
737
|
+
throw mapNativeError(error);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
async inspectPendingMutations() {
|
|
741
|
+
try {
|
|
742
|
+
return parseNativeArray(await this.native.inspectPendingMutations(), 'pending mutation inspection').map(parsePendingMutationInspection);
|
|
743
|
+
} catch (error) {
|
|
744
|
+
throw mapNativeError(error);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
async inspectRetainedMutations() {
|
|
748
|
+
try {
|
|
749
|
+
return parseNativeArray(await this.native.inspectRetainedMutations(), 'retained mutation inspection').map(parsePendingMutationInspection);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
throw mapNativeError(error);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
async inspectRejectedMutations() {
|
|
755
|
+
try {
|
|
756
|
+
return parseNativeArray(await this.native.inspectRejectedMutations(), 'rejected mutation inspection').map(parseRejectedMutationInspection);
|
|
757
|
+
} catch (error) {
|
|
758
|
+
throw mapNativeError(error);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
async clearRejectedMutations() {
|
|
762
|
+
try {
|
|
763
|
+
await this.native.clearRejectedMutations();
|
|
764
|
+
} catch (error) {
|
|
765
|
+
throw mapNativeError(error);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
237
768
|
|
|
238
|
-
//
|
|
769
|
+
// Sync
|
|
239
770
|
|
|
240
771
|
async start() {
|
|
241
772
|
try {
|
|
@@ -251,6 +782,34 @@ export class SynchroClient {
|
|
|
251
782
|
throw mapNativeError(error);
|
|
252
783
|
}
|
|
253
784
|
}
|
|
785
|
+
async enterBackground() {
|
|
786
|
+
try {
|
|
787
|
+
await this.native.enterBackground();
|
|
788
|
+
} catch (error) {
|
|
789
|
+
throw mapNativeError(error);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
async enterForeground() {
|
|
793
|
+
try {
|
|
794
|
+
await this.native.enterForeground();
|
|
795
|
+
} catch (error) {
|
|
796
|
+
throw mapNativeError(error);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
async retryAfterError() {
|
|
800
|
+
try {
|
|
801
|
+
await this.native.retryAfterError();
|
|
802
|
+
} catch (error) {
|
|
803
|
+
throw mapNativeError(error);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
async resetSchemaAndStart() {
|
|
807
|
+
try {
|
|
808
|
+
await this.native.resetSchemaAndStart();
|
|
809
|
+
} catch (error) {
|
|
810
|
+
throw mapNativeError(error);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
254
813
|
async syncNow() {
|
|
255
814
|
try {
|
|
256
815
|
await this.native.syncNow();
|
|
@@ -259,14 +818,19 @@ export class SynchroClient {
|
|
|
259
818
|
}
|
|
260
819
|
}
|
|
261
820
|
|
|
262
|
-
//
|
|
821
|
+
// Status and events
|
|
263
822
|
|
|
264
823
|
onStatusChange(callback) {
|
|
265
824
|
const subscription = this.native.onStatusChange(event => {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
825
|
+
const status = parseStatus(event);
|
|
826
|
+
if (status !== null) callback(status);
|
|
827
|
+
});
|
|
828
|
+
return () => subscription.remove();
|
|
829
|
+
}
|
|
830
|
+
onSyncEvent(callback) {
|
|
831
|
+
const subscription = this.native.onSyncEvent(event => {
|
|
832
|
+
const parsed = parseSyncEvent(event);
|
|
833
|
+
if (parsed !== null) callback(parsed);
|
|
270
834
|
});
|
|
271
835
|
return () => subscription.remove();
|
|
272
836
|
}
|
|
@@ -281,15 +845,23 @@ export class SynchroClient {
|
|
|
281
845
|
});
|
|
282
846
|
return () => subscription.remove();
|
|
283
847
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
848
|
+
}
|
|
849
|
+
export function configureInspection(client, transportObservationCapacity, requireNewDatabase) {
|
|
850
|
+
const owner = inspectionOwners.get(client);
|
|
851
|
+
if (owner !== undefined && activeNativeOwner === owner && transportObservationCapacity === 0 && !requireNewDatabase) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
if (owner === undefined || activeNativeOwner === owner || typeof requireNewDatabase !== 'boolean' || !Number.isSafeInteger(transportObservationCapacity) || transportObservationCapacity < 0 || transportObservationCapacity > 512) {
|
|
855
|
+
throw new InvalidResponseError('Inspection configuration is invalid');
|
|
856
|
+
}
|
|
857
|
+
inspectionCapacities.set(client, transportObservationCapacity);
|
|
858
|
+
inspectionRequireNewDatabases.set(client, requireNewDatabase);
|
|
859
|
+
}
|
|
860
|
+
export function nativeForInspection(client) {
|
|
861
|
+
const owner = inspectionOwners.get(client);
|
|
862
|
+
if (owner === undefined || activeNativeOwner !== owner) {
|
|
863
|
+
throw new SynchroError('NOT_CONNECTED', 'SynchroClient is not initialized');
|
|
293
864
|
}
|
|
865
|
+
return NativeSynchro;
|
|
294
866
|
}
|
|
295
867
|
//# sourceMappingURL=SynchroClient.js.map
|