@trainstar/synchro-react-native 0.1.2 → 0.3.2

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 (64) hide show
  1. package/README.md +103 -24
  2. package/SynchroReactNative.podspec +3 -2
  3. package/android/build.gradle +8 -3
  4. package/android/src/main/kotlin/com/trainstar/synchro/rn/SynchroModule.kt +1106 -238
  5. package/ios/SynchroModule.mm +178 -51
  6. package/ios/SynchroModule.swift +1382 -295
  7. package/lib/module/NativeSynchro.js +5 -0
  8. package/lib/module/NativeSynchro.js.map +1 -1
  9. package/lib/module/SynchroClient.js +637 -65
  10. package/lib/module/SynchroClient.js.map +1 -1
  11. package/lib/module/digest.js +82 -0
  12. package/lib/module/digest.js.map +1 -0
  13. package/lib/module/errors.js +165 -22
  14. package/lib/module/errors.js.map +1 -1
  15. package/lib/module/hooks/useQuery.js +37 -3
  16. package/lib/module/hooks/useQuery.js.map +1 -1
  17. package/lib/module/hooks/useSyncStatus.js +51 -6
  18. package/lib/module/hooks/useSyncStatus.js.map +1 -1
  19. package/lib/module/index.js +2 -1
  20. package/lib/module/index.js.map +1 -1
  21. package/lib/module/inspection.js +121 -0
  22. package/lib/module/inspection.js.map +1 -0
  23. package/lib/module/sqliteValues.js +87 -0
  24. package/lib/module/sqliteValues.js.map +1 -0
  25. package/lib/module/types.js +10 -0
  26. package/lib/module/types.js.map +1 -1
  27. package/lib/typescript/example/e2e/corpus-harness.d.ts +34 -0
  28. package/lib/typescript/example/e2e/corpus-harness.d.ts.map +1 -0
  29. package/lib/typescript/example/src/conformance/runner.d.ts +125 -0
  30. package/lib/typescript/example/src/conformance/runner.d.ts.map +1 -0
  31. package/lib/typescript/example/src/conformance/types.d.ts +71 -0
  32. package/lib/typescript/example/src/conformance/types.d.ts.map +1 -0
  33. package/lib/typescript/example/src/timeouts.d.ts +2 -0
  34. package/lib/typescript/example/src/timeouts.d.ts.map +1 -0
  35. package/lib/typescript/src/NativeSynchro.d.ts +59 -17
  36. package/lib/typescript/src/NativeSynchro.d.ts.map +1 -1
  37. package/lib/typescript/src/SynchroClient.d.ts +27 -10
  38. package/lib/typescript/src/SynchroClient.d.ts.map +1 -1
  39. package/lib/typescript/src/digest.d.ts +3 -0
  40. package/lib/typescript/src/digest.d.ts.map +1 -0
  41. package/lib/typescript/src/errors.d.ts +37 -9
  42. package/lib/typescript/src/errors.d.ts.map +1 -1
  43. package/lib/typescript/src/hooks/useQuery.d.ts +2 -2
  44. package/lib/typescript/src/hooks/useQuery.d.ts.map +1 -1
  45. package/lib/typescript/src/hooks/useSyncStatus.d.ts.map +1 -1
  46. package/lib/typescript/src/index.d.ts +4 -3
  47. package/lib/typescript/src/index.d.ts.map +1 -1
  48. package/lib/typescript/src/inspection.d.ts +43 -0
  49. package/lib/typescript/src/inspection.d.ts.map +1 -0
  50. package/lib/typescript/src/sqliteValues.d.ts +8 -0
  51. package/lib/typescript/src/sqliteValues.d.ts.map +1 -0
  52. package/lib/typescript/src/types.d.ts +203 -11
  53. package/lib/typescript/src/types.d.ts.map +1 -1
  54. package/package.json +20 -8
  55. package/src/NativeSynchro.ts +93 -26
  56. package/src/SynchroClient.ts +900 -120
  57. package/src/digest.ts +93 -0
  58. package/src/errors.ts +273 -33
  59. package/src/hooks/useQuery.ts +59 -16
  60. package/src/hooks/useSyncStatus.ts +40 -7
  61. package/src/index.ts +44 -3
  62. package/src/inspection.ts +226 -0
  63. package/src/sqliteValues.ts +105 -0
  64. package/src/types.ts +318 -16
package/src/types.ts CHANGED
@@ -1,5 +1,21 @@
1
1
  export type Row = Record<string, unknown>;
2
2
 
3
+ /** A lossless SQLite BLOB value at the React Native boundary. */
4
+ export interface SQLiteBytes {
5
+ readonly type: 'bytes';
6
+ readonly base64: string;
7
+ }
8
+
9
+ /** A lossless SQLite INTEGER value outside JavaScript's safe integer range. */
10
+ export interface SQLiteInt64 {
11
+ readonly type: 'int64';
12
+ readonly value: string;
13
+ }
14
+
15
+ export type SQLiteTaggedValue = SQLiteBytes | SQLiteInt64;
16
+
17
+ export type SQLiteBindValue = null | string | number | boolean | SQLiteTaggedValue;
18
+
3
19
  export interface ExecResult {
4
20
  rowsAffected: number;
5
21
  }
@@ -10,7 +26,7 @@ export interface BatchResult {
10
26
 
11
27
  export interface SQLStatement {
12
28
  sql: string;
13
- params?: unknown[];
29
+ params?: readonly SQLiteBindValue[];
14
30
  }
15
31
 
16
32
  export interface ColumnDef {
@@ -26,26 +42,311 @@ export interface TableOptions {
26
42
  withoutRowid?: boolean;
27
43
  }
28
44
 
29
- export interface Transaction {
30
- query(sql: string, params?: unknown[]): Promise<Row[]>;
31
- queryOne(sql: string, params?: unknown[]): Promise<Row | null>;
32
- execute(sql: string, params?: unknown[]): Promise<ExecResult>;
45
+ export interface ReadTransaction {
46
+ query(sql: string, params?: readonly SQLiteBindValue[]): Promise<Row[]>;
47
+ queryOne(sql: string, params?: readonly SQLiteBindValue[]): Promise<Row | null>;
48
+ }
49
+
50
+ export interface Transaction extends ReadTransaction {
51
+ execute(sql: string, params?: readonly SQLiteBindValue[]): Promise<ExecResult>;
52
+ }
53
+
54
+ export const SYNC_STATUS_TYPES = [
55
+ 'uninitialized',
56
+ 'local_ready',
57
+ 'connecting',
58
+ 'schema_applying',
59
+ 'ready',
60
+ 'pushing',
61
+ 'pulling',
62
+ 'rebuilding',
63
+ 'backoff',
64
+ 'error',
65
+ 'stopped',
66
+ ] as const;
67
+
68
+ export type SyncStatusType = (typeof SYNC_STATUS_TYPES)[number];
69
+
70
+ export const SYNC_OPERATION_KINDS = [
71
+ 'opening',
72
+ 'connecting',
73
+ 'schema',
74
+ 'pushing',
75
+ 'pulling',
76
+ 'rebuilding',
77
+ 'lifecycle',
78
+ 'database',
79
+ ] as const;
80
+
81
+ export type SyncOperationKind = (typeof SYNC_OPERATION_KINDS)[number];
82
+
83
+ export const SYNC_RECOVERY_ACTIONS = ['retry', 'schema_reset', 'none'] as const;
84
+
85
+ export type SyncRecoveryAction = (typeof SYNC_RECOVERY_ACTIONS)[number];
86
+
87
+ export const SYNC_FAILURE_CODES = [
88
+ 'auth_required',
89
+ 'client_retired',
90
+ 'idempotency_conflict',
91
+ 'invalid_request',
92
+ 'invalid_response',
93
+ 'invalid_schema_reference',
94
+ 'invalid_state_transition',
95
+ 'local_database',
96
+ 'schema_application_failed',
97
+ 'sync_integrity_failure',
98
+ 'unsupported_schema',
99
+ 'upgrade_required',
100
+ 'schema_mismatch',
101
+ 'server_error',
102
+ 'network_error',
103
+ 'database_error',
104
+ 'local_failure',
105
+ ] as const;
106
+
107
+ export type SyncFailureCode = (typeof SYNC_FAILURE_CODES)[number];
108
+
109
+ export const PROTOCOL_ERROR_CODES = [
110
+ 'invalid_request',
111
+ 'invalid_schema_reference',
112
+ 'upgrade_required',
113
+ 'auth_required',
114
+ 'idempotency_conflict',
115
+ 'client_retired',
116
+ 'client_generation_expired',
117
+ 'rebuild_restart_required',
118
+ 'schema_mismatch',
119
+ 'retry_later',
120
+ 'sync_integrity_failure',
121
+ 'capture_pending',
122
+ 'temporary_unavailable',
123
+ ] as const;
124
+
125
+ export type ProtocolErrorCode = (typeof PROTOCOL_ERROR_CODES)[number];
126
+
127
+ export interface SyncFailure {
128
+ operation: SyncOperationKind;
129
+ code: SyncFailureCode;
130
+ retryable: boolean;
131
+ message: string;
132
+ recoveryAction: SyncRecoveryAction;
133
+ metadata: Record<string, string>;
33
134
  }
34
135
 
35
- export type CheckpointMode = 'passive' | 'full' | 'restart' | 'truncate';
136
+ type DetailFreeSyncStatus = {
137
+ [K in Exclude<SyncStatusType, 'backoff' | 'error'>]: {
138
+ status: K;
139
+ retryAt: null;
140
+ operation: null;
141
+ failure: null;
142
+ };
143
+ }[Exclude<SyncStatusType, 'backoff' | 'error'>];
144
+
145
+ export type SyncStatus =
146
+ | DetailFreeSyncStatus
147
+ | {
148
+ status: 'backoff';
149
+ retryAt: Date;
150
+ operation: SyncOperationKind;
151
+ failure: null;
152
+ }
153
+ | {
154
+ status: 'error';
155
+ retryAt: null;
156
+ operation: null;
157
+ failure: SyncFailure;
158
+ };
36
159
 
37
- export type SyncStatusType =
38
- | 'idle'
39
- | 'connecting'
40
- | 'syncing'
41
- | 'error'
42
- | 'stopped';
160
+ export type JSONValue =
161
+ | null
162
+ | boolean
163
+ | number
164
+ | string
165
+ | JSONValue[]
166
+ | { [key: string]: JSONValue };
43
167
 
44
- export interface SyncStatus {
45
- status: SyncStatusType;
46
- retryAt: Date | null;
168
+ export type MutationOperation = 'insert' | 'upsert' | 'update' | 'delete';
169
+
170
+ export type LocalMutationStatus =
171
+ | 'pending'
172
+ | 'sealed'
173
+ | 'superseded_before_send'
174
+ | 'cancelled_before_send'
175
+ | 'blocked_by_predecessor'
176
+ | 'server_rejected'
177
+ | 'exceeds_push_limit';
178
+
179
+ export type MutationStatus = 'applied' | 'conflict' | 'rejected_terminal';
180
+
181
+ export type MutationRejectionCode =
182
+ | 'version_conflict'
183
+ | 'row_already_exists'
184
+ | 'row_deleted'
185
+ | 'row_not_found'
186
+ | 'schema_incompatible'
187
+ | 'policy_rejected'
188
+ | 'validation_failed'
189
+ | 'table_not_synced';
190
+
191
+ export interface SchemaRef {
192
+ version: number;
193
+ hash: string;
47
194
  }
48
195
 
196
+ export interface AuthoredMutationField {
197
+ fieldID: string;
198
+ logicalType: string;
199
+ value: JSONValue;
200
+ }
201
+
202
+ export interface PendingMutationInspection {
203
+ mutationID: string;
204
+ localOrder: number;
205
+ tableID: string;
206
+ tableName: string;
207
+ recordID: string;
208
+ primaryKeyFieldID: string;
209
+ primaryKeyLogicalType: string;
210
+ operation: MutationOperation;
211
+ authoredSchema: SchemaRef;
212
+ baseVersion: string | null;
213
+ clientVersion: string;
214
+ status: LocalMutationStatus;
215
+ sourceKind: string;
216
+ dependsOnMutationID: string | null;
217
+ normalizedMutationID: string | null;
218
+ sealedBatchID: string | null;
219
+ sealedOrdinal: number | null;
220
+ authoredFields: AuthoredMutationField[];
221
+ }
222
+
223
+ export interface RejectedMutationInspection {
224
+ mutationID: string;
225
+ tableName: string;
226
+ recordID: string;
227
+ status: MutationStatus;
228
+ code: MutationRejectionCode;
229
+ message: string | null;
230
+ serverRowJSON: string | null;
231
+ serverVersion: string | null;
232
+ mutationJSON: string;
233
+ rejectionJSON: string;
234
+ createdAt: string;
235
+ updatedAt: string;
236
+ }
237
+
238
+ export interface ScopeStateInspection {
239
+ scopeID: string;
240
+ cursor: string | null;
241
+ checksum: string | null;
242
+ localChecksum: string;
243
+ generation: number;
244
+ }
245
+
246
+ export interface ScopeRowInspection {
247
+ scopeID: string;
248
+ tableName: string;
249
+ recordID: string;
250
+ checksum: string;
251
+ generation: number;
252
+ }
253
+
254
+ export interface RebuildAttemptInspection {
255
+ scopeID: string;
256
+ rebuildID: string;
257
+ clientGeneration: number;
258
+ schemaVersion: number;
259
+ schemaHash: string;
260
+ generation: number;
261
+ cursor: string | null;
262
+ pageLimit: number;
263
+ }
264
+
265
+ export interface ClientStateInspection {
266
+ schema: SchemaRef | null;
267
+ scopeStates: ScopeStateInspection[];
268
+ scopeRows: ScopeRowInspection[];
269
+ rebuildAttempts: RebuildAttemptInspection[];
270
+ applicationRowCount: number;
271
+ mutationLedgerCount: number;
272
+ mutationOutcomeCount: number;
273
+ sealedBatchCount: number;
274
+ rejectedMutationCount: number;
275
+ scopeStateCount: number;
276
+ scopeRowCount: number;
277
+ provenanceCount: number;
278
+ rowMetadataCount: number;
279
+ rebuildAttemptCount: number;
280
+ rebuildReceiptCount: number;
281
+ provenanceMaintenanceWorkCursor: string;
282
+ }
283
+
284
+ export type TransportOperationClass =
285
+ | 'connect'
286
+ | 'pull'
287
+ | 'push'
288
+ | 'checkpoint'
289
+ | 'schemas'
290
+ | 'rebuild'
291
+ | 'other';
292
+
293
+ export interface TransportObservation {
294
+ sequence: number;
295
+ operationClass: TransportOperationClass;
296
+ statusCode: number;
297
+ durationNanoseconds: number;
298
+ cursorFingerprints?: string[];
299
+ cursorFingerprintsComplete?: boolean;
300
+ requestFacts?: Record<string, JSONValue>;
301
+ rebuildResponseFacts?: Record<string, JSONValue>;
302
+ pullResponseFacts?: Record<string, JSONValue>;
303
+ }
304
+
305
+ export interface TransportObservationSnapshot {
306
+ observations: TransportObservation[];
307
+ overflowed: boolean;
308
+ sequenceCheckpoint: number;
309
+ }
310
+
311
+ export type SchemaAction = 'none' | 'replace' | 'rebuild_local' | 'unsupported';
312
+
313
+ export interface SyncSchemaEvent {
314
+ source: SchemaRef;
315
+ target: SchemaRef;
316
+ action: SchemaAction;
317
+ }
318
+
319
+ export interface SyncMutationEvent {
320
+ mutationID: string;
321
+ tableID: string;
322
+ status: MutationStatus;
323
+ rejectionCode: MutationRejectionCode | null;
324
+ }
325
+
326
+ export interface SyncRebuildEvent {
327
+ scopeID: string;
328
+ rebuildID: string;
329
+ }
330
+
331
+ export type SyncEvent =
332
+ | {
333
+ type: 'state_changed';
334
+ from: SyncStatusType;
335
+ to: SyncStatusType;
336
+ }
337
+ | ({ type: 'backoff' } & {
338
+ operation: SyncOperationKind;
339
+ attempt: number;
340
+ retryAt: Date;
341
+ })
342
+ | ({ type: 'schema_applying' | 'schema_applied' } & SyncSchemaEvent)
343
+ | ({ type: 'mutation_accepted' | 'mutation_rejected' } & SyncMutationEvent)
344
+ | ({ type: 'rebuild_requested' | 'rebuild_completed' } & SyncRebuildEvent)
345
+ | {
346
+ type: 'failure';
347
+ failure: SyncFailure;
348
+ };
349
+
49
350
  export interface ConflictEvent {
50
351
  table: string;
51
352
  recordID: string;
@@ -65,7 +366,8 @@ export interface SynchroConfig {
65
366
  maxRetryAttempts?: number;
66
367
  pullPageSize?: number;
67
368
  pushBatchSize?: number;
68
- snapshotPageSize?: number;
369
+ seedDatabasePath?: string;
69
370
  }
70
371
 
71
372
  export type Unsubscribe = () => void;
373
+ export type AsyncUnsubscribe = () => Promise<void>;