@syncular/core 0.0.6-96 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blobs.d.ts +14 -7
- package/dist/blobs.d.ts.map +1 -1
- package/dist/blobs.js.map +1 -1
- package/dist/column-codecs.d.ts +3 -0
- package/dist/column-codecs.d.ts.map +1 -1
- package/dist/column-codecs.js +32 -4
- package/dist/column-codecs.js.map +1 -1
- package/dist/conflict.js.map +1 -1
- package/dist/database.js.map +1 -1
- package/dist/error-responses.d.ts +393 -0
- package/dist/error-responses.d.ts.map +1 -0
- package/dist/error-responses.js +393 -0
- package/dist/error-responses.js.map +1 -0
- package/dist/http/api-client.d.ts +11 -0
- package/dist/http/api-client.d.ts.map +1 -0
- package/dist/http/api-client.js +30 -0
- package/dist/http/api-client.js.map +1 -0
- package/dist/http/blob.d.ts +6 -0
- package/dist/http/blob.d.ts.map +1 -0
- package/dist/http/blob.js +46 -0
- package/dist/http/blob.js.map +1 -0
- package/dist/http/generated/api.d.ts +5241 -0
- package/dist/http/generated/api.d.ts.map +1 -0
- package/dist/http/generated/api.js +6 -0
- package/dist/http/generated/api.js.map +1 -0
- package/dist/http/index.d.ts +18 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +212 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/shared.d.ts +45 -0
- package/dist/http/shared.d.ts.map +1 -0
- package/dist/http/shared.js +122 -0
- package/dist/http/shared.js.map +1 -0
- package/dist/http/transport-client.d.ts +31 -0
- package/dist/http/transport-client.d.ts.map +1 -0
- package/dist/http/transport-client.js +193 -0
- package/dist/http/transport-client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/kysely-column-codecs.d.ts +17 -1
- package/dist/kysely-column-codecs.d.ts.map +1 -1
- package/dist/kysely-column-codecs.js +56 -70
- package/dist/kysely-column-codecs.js.map +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/schemas/blobs.d.ts +7 -1
- package/dist/schemas/blobs.d.ts.map +1 -1
- package/dist/schemas/blobs.js +1 -0
- package/dist/schemas/blobs.js.map +1 -1
- package/dist/schemas/common.d.ts +73 -1
- package/dist/schemas/common.d.ts.map +1 -1
- package/dist/schemas/common.js +38 -0
- package/dist/schemas/common.js.map +1 -1
- package/dist/schemas/sync.d.ts +712 -38
- package/dist/schemas/sync.d.ts.map +1 -1
- package/dist/schemas/sync.js +209 -5
- package/dist/schemas/sync.js.map +1 -1
- package/dist/scopes/index.d.ts +5 -0
- package/dist/scopes/index.d.ts.map +1 -1
- package/dist/scopes/index.js +27 -0
- package/dist/scopes/index.js.map +1 -1
- package/dist/sentry.d.ts +31 -0
- package/dist/sentry.d.ts.map +1 -0
- package/dist/sentry.js +147 -0
- package/dist/sentry.js.map +1 -0
- package/dist/snapshot-chunks.d.ts +104 -20
- package/dist/snapshot-chunks.d.ts.map +1 -1
- package/dist/snapshot-chunks.js +665 -72
- package/dist/snapshot-chunks.js.map +1 -1
- package/dist/sync-packs.d.ts +33 -0
- package/dist/sync-packs.d.ts.map +1 -0
- package/dist/sync-packs.js +821 -0
- package/dist/sync-packs.js.map +1 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +3 -2
- package/dist/telemetry.js.map +1 -1
- package/dist/transforms.js.map +1 -1
- package/dist/types.d.ts +48 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/bytes.d.ts +4 -0
- package/dist/utils/bytes.d.ts.map +1 -0
- package/dist/utils/bytes.js +66 -0
- package/dist/utils/bytes.js.map +1 -0
- package/dist/utils/compression.d.ts +11 -3
- package/dist/utils/compression.d.ts.map +1 -1
- package/dist/utils/compression.js +102 -50
- package/dist/utils/compression.js.map +1 -1
- package/dist/utils/crypto.d.ts +7 -1
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +89 -7
- package/dist/utils/crypto.js.map +1 -1
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +4 -1
- package/dist/utils/id.js.map +1 -1
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/internal-runtime.d.ts +16 -0
- package/dist/utils/internal-runtime.d.ts.map +1 -0
- package/dist/utils/internal-runtime.js +14 -0
- package/dist/utils/internal-runtime.js.map +1 -0
- package/dist/utils/object.js.map +1 -1
- package/dist/utils/realtime-change-index.d.ts +9 -0
- package/dist/utils/realtime-change-index.d.ts.map +1 -0
- package/dist/utils/realtime-change-index.js +36 -0
- package/dist/utils/realtime-change-index.js.map +1 -0
- package/dist/utils/realtime-connection-registry.d.ts +40 -0
- package/dist/utils/realtime-connection-registry.d.ts.map +1 -0
- package/dist/utils/realtime-connection-registry.js +237 -0
- package/dist/utils/realtime-connection-registry.js.map +1 -0
- package/dist/utils/table-registry.d.ts +10 -0
- package/dist/utils/table-registry.d.ts.map +1 -0
- package/dist/utils/table-registry.js +54 -0
- package/dist/utils/table-registry.js.map +1 -0
- package/dist/utils/url.d.ts +3 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/url.js +20 -0
- package/dist/utils/url.js.map +1 -0
- package/package.json +51 -6
- package/src/blobs.ts +24 -7
- package/src/column-codecs.ts +36 -8
- package/src/error-responses.ts +423 -0
- package/src/http/api-client.ts +39 -0
- package/src/http/blob.ts +95 -0
- package/src/http/generated/api.ts +5241 -0
- package/src/http/index.ts +365 -0
- package/src/http/shared.ts +220 -0
- package/src/http/transport-client.ts +314 -0
- package/src/index.ts +4 -0
- package/src/kysely-column-codecs.ts +81 -86
- package/src/schemas/blobs.ts +10 -1
- package/src/schemas/common.ts +44 -0
- package/src/schemas/sync.ts +298 -7
- package/src/scopes/index.ts +42 -0
- package/src/sentry.ts +200 -0
- package/src/snapshot-chunks.ts +921 -69
- package/src/sync-packs.ts +1096 -0
- package/src/telemetry.ts +7 -2
- package/src/types.ts +63 -8
- package/src/utils/bytes.ts +75 -0
- package/src/utils/compression.ts +147 -51
- package/src/utils/crypto.ts +120 -7
- package/src/utils/id.ts +11 -1
- package/src/utils/index.ts +5 -0
- package/src/utils/internal-runtime.ts +39 -0
- package/src/utils/realtime-change-index.ts +44 -0
- package/src/utils/realtime-connection-registry.ts +286 -0
- package/src/utils/table-registry.ts +67 -0
- package/src/utils/url.ts +29 -0
- package/src/__tests__/compression.test.ts +0 -54
- package/src/__tests__/conflict.test.ts +0 -325
- package/src/__tests__/telemetry.test.ts +0 -170
- package/src/__tests__/utils.test.ts +0 -27
package/src/schemas/sync.ts
CHANGED
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import {
|
|
12
|
+
SYNC_SCOPED_SNAPSHOT_ARTIFACT_KIND_SQLITE_V1,
|
|
13
|
+
SYNC_SCOPED_SNAPSHOT_ARTIFACT_MANIFEST_VERSION,
|
|
14
|
+
SYNC_SNAPSHOT_ARTIFACT_COMPRESSION_NONE,
|
|
12
15
|
SYNC_SNAPSHOT_CHUNK_COMPRESSION,
|
|
13
|
-
|
|
16
|
+
SYNC_SNAPSHOT_CHUNK_ENCODINGS,
|
|
14
17
|
} from '../snapshot-chunks';
|
|
15
18
|
|
|
16
19
|
// ============================================================================
|
|
@@ -37,6 +40,95 @@ export const ScopeValuesSchema = z.record(
|
|
|
37
40
|
z.union([z.string(), z.array(z.string())])
|
|
38
41
|
);
|
|
39
42
|
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Auth Lease Schemas
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
export const SYNC_AUTH_LEASE_VERSION = 1;
|
|
48
|
+
export const SYNC_AUTH_LEASE_PROTOCOL_VERSION = 1;
|
|
49
|
+
export const SYNC_AUTH_LEASE_ALG_ES256 = 'ES256';
|
|
50
|
+
export const SYNC_AUTH_LEASE_TYP = 'syncular-auth-lease+jws';
|
|
51
|
+
export const SYNC_AUTH_LEASE_CODE_MISSING = 'sync.auth_lease_missing';
|
|
52
|
+
export const SYNC_AUTH_LEASE_CODE_INVALID = 'sync.auth_lease_invalid';
|
|
53
|
+
export const SYNC_AUTH_LEASE_CODE_EXPIRED = 'sync.auth_lease_expired';
|
|
54
|
+
export const SYNC_AUTH_LEASE_CODE_SCHEMA_MISMATCH =
|
|
55
|
+
'sync.auth_lease_schema_mismatch';
|
|
56
|
+
export const SYNC_AUTH_LEASE_CODE_SCOPE_MISMATCH =
|
|
57
|
+
'sync.auth_lease_scope_mismatch';
|
|
58
|
+
export const SYNC_AUTH_LEASE_CODE_SCOPE_REVOKED =
|
|
59
|
+
'sync.auth_lease_scope_revoked';
|
|
60
|
+
export const SYNC_AUTH_LEASE_CODE_BUSINESS_REJECTED =
|
|
61
|
+
'sync.auth_lease_business_rejected';
|
|
62
|
+
|
|
63
|
+
export const SyncAuthLeaseProtectedHeaderSchema = z.object({
|
|
64
|
+
alg: z.literal(SYNC_AUTH_LEASE_ALG_ES256),
|
|
65
|
+
kid: z.string().min(1),
|
|
66
|
+
typ: z.literal(SYNC_AUTH_LEASE_TYP),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export type SyncAuthLeaseProtectedHeader = z.infer<
|
|
70
|
+
typeof SyncAuthLeaseProtectedHeaderSchema
|
|
71
|
+
>;
|
|
72
|
+
|
|
73
|
+
export const SyncAuthLeaseCapabilitiesSchema = z.object({
|
|
74
|
+
allowBlobs: z.boolean(),
|
|
75
|
+
allowCrdt: z.boolean(),
|
|
76
|
+
allowEncryptedFields: z.boolean(),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export type SyncAuthLeaseCapabilities = z.infer<
|
|
80
|
+
typeof SyncAuthLeaseCapabilitiesSchema
|
|
81
|
+
>;
|
|
82
|
+
|
|
83
|
+
export const SyncAuthLeaseScopeSchema = z.object({
|
|
84
|
+
subscriptionId: z.string().min(1),
|
|
85
|
+
table: z.string().min(1),
|
|
86
|
+
values: ScopeValuesSchema,
|
|
87
|
+
operations: z.array(SyncOpSchema).min(1),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export type SyncAuthLeaseScope = z.infer<typeof SyncAuthLeaseScopeSchema>;
|
|
91
|
+
|
|
92
|
+
export const SyncAuthLeasePayloadSchema = z.object({
|
|
93
|
+
version: z.literal(SYNC_AUTH_LEASE_VERSION),
|
|
94
|
+
leaseId: z.string().min(1),
|
|
95
|
+
issuer: z.string().min(1),
|
|
96
|
+
audience: z.string().min(1),
|
|
97
|
+
actorId: z.string().min(1),
|
|
98
|
+
subject: z.record(z.string(), z.unknown()).default({}),
|
|
99
|
+
schemaVersion: z.number().int().min(1),
|
|
100
|
+
protocolVersion: z.literal(SYNC_AUTH_LEASE_PROTOCOL_VERSION),
|
|
101
|
+
issuedAtMs: z.number().int(),
|
|
102
|
+
notBeforeMs: z.number().int(),
|
|
103
|
+
expiresAtMs: z.number().int(),
|
|
104
|
+
maxClockSkewMs: z.number().int().min(0),
|
|
105
|
+
scopes: z.array(SyncAuthLeaseScopeSchema).min(1),
|
|
106
|
+
capabilities: SyncAuthLeaseCapabilitiesSchema,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export type SyncAuthLeasePayload = z.infer<typeof SyncAuthLeasePayloadSchema>;
|
|
110
|
+
|
|
111
|
+
export const SyncAuthLeaseIssueRequestSchema = z.object({
|
|
112
|
+
schemaVersion: z.number().int().min(1),
|
|
113
|
+
ttlMs: z.number().int().positive().optional(),
|
|
114
|
+
scopes: z.array(SyncAuthLeaseScopeSchema).min(1),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export type SyncAuthLeaseIssueRequest = z.infer<
|
|
118
|
+
typeof SyncAuthLeaseIssueRequestSchema
|
|
119
|
+
>;
|
|
120
|
+
|
|
121
|
+
export const SyncAuthLeaseIssueResponseSchema = z.object({
|
|
122
|
+
ok: z.literal(true),
|
|
123
|
+
token: z.string().min(1),
|
|
124
|
+
protectedHeader: SyncAuthLeaseProtectedHeaderSchema,
|
|
125
|
+
payload: SyncAuthLeasePayloadSchema,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
export type SyncAuthLeaseIssueResponse = z.infer<
|
|
129
|
+
typeof SyncAuthLeaseIssueResponseSchema
|
|
130
|
+
>;
|
|
131
|
+
|
|
40
132
|
// ============================================================================
|
|
41
133
|
// Sync Operation Schema
|
|
42
134
|
// ============================================================================
|
|
@@ -55,15 +147,34 @@ export type SyncOperation = z.infer<typeof SyncOperationSchema>;
|
|
|
55
147
|
// Push Request/Response Schemas
|
|
56
148
|
// ============================================================================
|
|
57
149
|
|
|
150
|
+
export const SyncAuthLeaseProvenanceSchema = z.object({
|
|
151
|
+
leaseId: z.string().min(1),
|
|
152
|
+
leaseExpiresAtMs: z.number().int(),
|
|
153
|
+
leaseStatusAtEnqueue: z.string().min(1),
|
|
154
|
+
leaseScopeSummaryJson: z.string().optional(),
|
|
155
|
+
leaseToken: z.string().min(1).optional(),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
export type SyncAuthLeaseProvenance = z.infer<
|
|
159
|
+
typeof SyncAuthLeaseProvenanceSchema
|
|
160
|
+
>;
|
|
161
|
+
|
|
58
162
|
export const SyncPushRequestSchema = z.object({
|
|
59
163
|
clientId: z.string().min(1),
|
|
60
164
|
clientCommitId: z.string().min(1),
|
|
61
165
|
operations: z.array(SyncOperationSchema).min(1),
|
|
62
166
|
schemaVersion: z.number().int().min(1),
|
|
167
|
+
authLease: SyncAuthLeaseProvenanceSchema.optional(),
|
|
63
168
|
});
|
|
64
169
|
|
|
65
170
|
export type SyncPushRequest = z.infer<typeof SyncPushRequestSchema>;
|
|
66
171
|
|
|
172
|
+
export const SyncPushCommitRequestSchema = SyncPushRequestSchema.omit({
|
|
173
|
+
clientId: true,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
export type SyncPushCommitRequest = z.infer<typeof SyncPushCommitRequestSchema>;
|
|
177
|
+
|
|
67
178
|
const SyncOperationResultAppliedSchema = z.object({
|
|
68
179
|
opIndex: z.number().int(),
|
|
69
180
|
status: z.literal('applied'),
|
|
@@ -73,6 +184,7 @@ const SyncOperationResultConflictSchema = z.object({
|
|
|
73
184
|
opIndex: z.number().int(),
|
|
74
185
|
status: z.literal('conflict'),
|
|
75
186
|
message: z.string(),
|
|
187
|
+
code: z.string().optional(),
|
|
76
188
|
server_version: z.number().int(),
|
|
77
189
|
server_row: z.unknown(),
|
|
78
190
|
});
|
|
@@ -102,6 +214,28 @@ export const SyncPushResponseSchema = z.object({
|
|
|
102
214
|
|
|
103
215
|
export type SyncPushResponse = z.infer<typeof SyncPushResponseSchema>;
|
|
104
216
|
|
|
217
|
+
export const SyncPushBatchCommitResponseSchema = SyncPushResponseSchema.extend({
|
|
218
|
+
clientCommitId: z.string().min(1),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
export type SyncPushBatchCommitResponse = z.infer<
|
|
222
|
+
typeof SyncPushBatchCommitResponseSchema
|
|
223
|
+
>;
|
|
224
|
+
|
|
225
|
+
export const SyncPushBatchRequestSchema = z.object({
|
|
226
|
+
clientId: z.string().min(1),
|
|
227
|
+
commits: z.array(SyncPushCommitRequestSchema).min(1),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
export type SyncPushBatchRequest = z.infer<typeof SyncPushBatchRequestSchema>;
|
|
231
|
+
|
|
232
|
+
export const SyncPushBatchResponseSchema = z.object({
|
|
233
|
+
ok: z.literal(true),
|
|
234
|
+
commits: z.array(SyncPushBatchCommitResponseSchema),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
export type SyncPushBatchResponse = z.infer<typeof SyncPushBatchResponseSchema>;
|
|
238
|
+
|
|
105
239
|
// ============================================================================
|
|
106
240
|
// Bootstrap State Schema
|
|
107
241
|
// ============================================================================
|
|
@@ -119,6 +253,19 @@ export type SyncBootstrapState = z.infer<typeof SyncBootstrapStateSchema>;
|
|
|
119
253
|
// Pull Request/Response Schemas
|
|
120
254
|
// ============================================================================
|
|
121
255
|
|
|
256
|
+
export const SyncCrdtStateVectorHintSchema = z.object({
|
|
257
|
+
rowId: z.string().min(1),
|
|
258
|
+
field: z.string().min(1),
|
|
259
|
+
stateColumn: z.string().min(1),
|
|
260
|
+
stateVectorBase64: z.string().min(1),
|
|
261
|
+
syncMode: z.string().min(1),
|
|
262
|
+
updatedAt: z.number().int(),
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
export type SyncCrdtStateVectorHint = z.infer<
|
|
266
|
+
typeof SyncCrdtStateVectorHintSchema
|
|
267
|
+
>;
|
|
268
|
+
|
|
122
269
|
export const SyncSubscriptionRequestSchema = z.object({
|
|
123
270
|
id: z.string().min(1),
|
|
124
271
|
table: z.string().min(1),
|
|
@@ -126,18 +273,46 @@ export const SyncSubscriptionRequestSchema = z.object({
|
|
|
126
273
|
params: z.record(z.string(), z.unknown()).optional(),
|
|
127
274
|
cursor: z.number().int(),
|
|
128
275
|
bootstrapState: SyncBootstrapStateSchema.nullable().optional(),
|
|
276
|
+
verifiedRoot: z.string().optional(),
|
|
277
|
+
crdtStateVectors: z
|
|
278
|
+
.array(SyncCrdtStateVectorHintSchema)
|
|
279
|
+
.optional()
|
|
280
|
+
.default([]),
|
|
129
281
|
});
|
|
130
282
|
|
|
131
283
|
export type SyncSubscriptionRequest = z.infer<
|
|
132
284
|
typeof SyncSubscriptionRequestSchema
|
|
133
285
|
>;
|
|
134
286
|
|
|
287
|
+
export const SyncSnapshotChunkEncodingSchema = z.enum(
|
|
288
|
+
SYNC_SNAPSHOT_CHUNK_ENCODINGS
|
|
289
|
+
);
|
|
290
|
+
export const SyncScopedSnapshotArtifactKindSchema = z.literal(
|
|
291
|
+
SYNC_SCOPED_SNAPSHOT_ARTIFACT_KIND_SQLITE_V1
|
|
292
|
+
);
|
|
293
|
+
export const SyncSnapshotArtifactCompressionSchema = z.union([
|
|
294
|
+
z.literal(SYNC_SNAPSHOT_ARTIFACT_COMPRESSION_NONE),
|
|
295
|
+
z.literal(SYNC_SNAPSHOT_CHUNK_COMPRESSION),
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
export const SyncSnapshotArtifactsRequestSchema = z.object({
|
|
299
|
+
artifactKinds: z.array(SyncScopedSnapshotArtifactKindSchema).min(1),
|
|
300
|
+
compressions: z.array(SyncSnapshotArtifactCompressionSchema).optional(),
|
|
301
|
+
featureSet: z.array(z.string()).optional(),
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
export type SyncSnapshotArtifactsRequest = z.infer<
|
|
305
|
+
typeof SyncSnapshotArtifactsRequestSchema
|
|
306
|
+
>;
|
|
307
|
+
|
|
135
308
|
export const SyncPullRequestSchema = z.object({
|
|
136
309
|
clientId: z.string().min(1),
|
|
310
|
+
schemaVersion: z.number().int().min(1),
|
|
137
311
|
limitCommits: z.number().int().min(1),
|
|
138
312
|
limitSnapshotRows: z.number().int().min(1).optional(),
|
|
139
313
|
maxSnapshotPages: z.number().int().min(1).optional(),
|
|
140
314
|
dedupeRows: z.boolean().optional(),
|
|
315
|
+
snapshotArtifacts: SyncSnapshotArtifactsRequestSchema.optional(),
|
|
141
316
|
subscriptions: z.array(SyncSubscriptionRequestSchema),
|
|
142
317
|
});
|
|
143
318
|
|
|
@@ -163,24 +338,137 @@ export const SyncCommitSchema = z.object({
|
|
|
163
338
|
|
|
164
339
|
export type SyncCommit = z.infer<typeof SyncCommitSchema>;
|
|
165
340
|
|
|
341
|
+
export const SyncPullSubscriptionIntegritySchema = z.object({
|
|
342
|
+
partitionId: z.string(),
|
|
343
|
+
previousChainRoot: z.string(),
|
|
344
|
+
commitChainRoot: z.string(),
|
|
345
|
+
commitSeq: z.number().int(),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
export type SyncPullSubscriptionIntegrity = z.infer<
|
|
349
|
+
typeof SyncPullSubscriptionIntegritySchema
|
|
350
|
+
>;
|
|
351
|
+
|
|
166
352
|
export const SyncSnapshotChunkRefSchema = z.object({
|
|
167
353
|
id: z.string(),
|
|
168
354
|
byteLength: z.number().int(),
|
|
169
355
|
sha256: z.string(),
|
|
170
|
-
encoding:
|
|
356
|
+
encoding: SyncSnapshotChunkEncodingSchema,
|
|
171
357
|
compression: z.literal(SYNC_SNAPSHOT_CHUNK_COMPRESSION),
|
|
172
358
|
});
|
|
173
359
|
|
|
174
360
|
export type SyncSnapshotChunkRef = z.infer<typeof SyncSnapshotChunkRefSchema>;
|
|
175
361
|
|
|
176
|
-
export const
|
|
362
|
+
export const SyncSnapshotManifestChunkSchema = z.object({
|
|
363
|
+
id: z.string(),
|
|
364
|
+
byteLength: z.number().int(),
|
|
365
|
+
sha256: z.string(),
|
|
366
|
+
encoding: SyncSnapshotChunkEncodingSchema,
|
|
367
|
+
compression: z.literal(SYNC_SNAPSHOT_CHUNK_COMPRESSION),
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
export const SyncSnapshotManifestSchema = z.object({
|
|
371
|
+
version: z.literal(1),
|
|
372
|
+
digest: z.string(),
|
|
373
|
+
table: z.string(),
|
|
374
|
+
asOfCommitSeq: z.number().int(),
|
|
375
|
+
scopeDigest: z.string(),
|
|
376
|
+
rowCursor: z.string().nullable(),
|
|
377
|
+
rowLimit: z.number().int().min(1),
|
|
378
|
+
nextRowCursor: z.string().nullable(),
|
|
379
|
+
isFirstPage: z.boolean(),
|
|
380
|
+
isLastPage: z.boolean(),
|
|
381
|
+
chunks: z.array(SyncSnapshotManifestChunkSchema),
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
export type SyncSnapshotManifest = z.infer<typeof SyncSnapshotManifestSchema>;
|
|
385
|
+
|
|
386
|
+
export const SyncScopedSnapshotArtifactManifestSchema = z.object({
|
|
387
|
+
version: z.literal(SYNC_SCOPED_SNAPSHOT_ARTIFACT_MANIFEST_VERSION),
|
|
388
|
+
artifactKind: z.literal(SYNC_SCOPED_SNAPSHOT_ARTIFACT_KIND_SQLITE_V1),
|
|
389
|
+
digest: z.string(),
|
|
390
|
+
partitionId: z.string(),
|
|
391
|
+
subscriptionId: z.string(),
|
|
177
392
|
table: z.string(),
|
|
178
|
-
|
|
179
|
-
|
|
393
|
+
schemaVersion: z.string(),
|
|
394
|
+
asOfCommitSeq: z.number().int(),
|
|
395
|
+
scopeDigest: z.string(),
|
|
396
|
+
rowCursor: z.string().nullable(),
|
|
397
|
+
rowLimit: z.number().int().min(1),
|
|
398
|
+
rowCount: z.number().int().min(0),
|
|
399
|
+
nextRowCursor: z.string().nullable(),
|
|
180
400
|
isFirstPage: z.boolean(),
|
|
181
401
|
isLastPage: z.boolean(),
|
|
402
|
+
compression: z.union([
|
|
403
|
+
z.literal(SYNC_SNAPSHOT_ARTIFACT_COMPRESSION_NONE),
|
|
404
|
+
z.literal(SYNC_SNAPSHOT_CHUNK_COMPRESSION),
|
|
405
|
+
]),
|
|
406
|
+
byteLength: z.number().int().min(0),
|
|
407
|
+
sha256: z.string(),
|
|
408
|
+
featureSet: z.array(z.string()),
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
export type SyncScopedSnapshotArtifactManifest = z.infer<
|
|
412
|
+
typeof SyncScopedSnapshotArtifactManifestSchema
|
|
413
|
+
>;
|
|
414
|
+
|
|
415
|
+
export const SyncSnapshotArtifactRefSchema = z.object({
|
|
416
|
+
id: z.string(),
|
|
417
|
+
byteLength: z.number().int().min(0),
|
|
418
|
+
sha256: z.string(),
|
|
419
|
+
manifestDigest: z.string(),
|
|
420
|
+
artifactKind: z.literal(SYNC_SCOPED_SNAPSHOT_ARTIFACT_KIND_SQLITE_V1),
|
|
421
|
+
compression: z.union([
|
|
422
|
+
z.literal(SYNC_SNAPSHOT_ARTIFACT_COMPRESSION_NONE),
|
|
423
|
+
z.literal(SYNC_SNAPSHOT_CHUNK_COMPRESSION),
|
|
424
|
+
]),
|
|
425
|
+
rowCount: z.number().int().min(0),
|
|
426
|
+
nextRowCursor: z.string().nullable(),
|
|
427
|
+
isFirstPage: z.boolean(),
|
|
428
|
+
isLastPage: z.boolean(),
|
|
429
|
+
manifest: SyncScopedSnapshotArtifactManifestSchema,
|
|
182
430
|
});
|
|
183
431
|
|
|
432
|
+
export type SyncSnapshotArtifactRef = z.infer<
|
|
433
|
+
typeof SyncSnapshotArtifactRefSchema
|
|
434
|
+
>;
|
|
435
|
+
|
|
436
|
+
export const SyncSnapshotSchema = z
|
|
437
|
+
.object({
|
|
438
|
+
table: z.string(),
|
|
439
|
+
rows: z.array(z.unknown()),
|
|
440
|
+
chunks: z.array(SyncSnapshotChunkRefSchema).optional(),
|
|
441
|
+
manifest: SyncSnapshotManifestSchema.optional(),
|
|
442
|
+
artifacts: z.array(SyncSnapshotArtifactRefSchema).optional(),
|
|
443
|
+
isFirstPage: z.boolean(),
|
|
444
|
+
isLastPage: z.boolean(),
|
|
445
|
+
bootstrapStateAfter: SyncBootstrapStateSchema.nullable().optional(),
|
|
446
|
+
})
|
|
447
|
+
.superRefine((snapshot, ctx) => {
|
|
448
|
+
if (!snapshot.artifacts || snapshot.artifacts.length === 0) return;
|
|
449
|
+
if (snapshot.rows.length > 0) {
|
|
450
|
+
ctx.addIssue({
|
|
451
|
+
code: z.ZodIssueCode.custom,
|
|
452
|
+
path: ['rows'],
|
|
453
|
+
message: 'Snapshot artifacts cannot be mixed with inline rows',
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
if (snapshot.chunks && snapshot.chunks.length > 0) {
|
|
457
|
+
ctx.addIssue({
|
|
458
|
+
code: z.ZodIssueCode.custom,
|
|
459
|
+
path: ['chunks'],
|
|
460
|
+
message: 'Snapshot artifacts cannot be mixed with chunk refs',
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
if (snapshot.manifest) {
|
|
464
|
+
ctx.addIssue({
|
|
465
|
+
code: z.ZodIssueCode.custom,
|
|
466
|
+
path: ['manifest'],
|
|
467
|
+
message: 'Snapshot artifacts cannot be mixed with chunk manifests',
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
|
|
184
472
|
export type SyncSnapshot = z.infer<typeof SyncSnapshotSchema>;
|
|
185
473
|
|
|
186
474
|
export const SyncPullSubscriptionResponseSchema = z.object({
|
|
@@ -190,6 +478,7 @@ export const SyncPullSubscriptionResponseSchema = z.object({
|
|
|
190
478
|
bootstrap: z.boolean(),
|
|
191
479
|
bootstrapState: SyncBootstrapStateSchema.nullable().optional(),
|
|
192
480
|
nextCursor: z.number().int(),
|
|
481
|
+
integrity: SyncPullSubscriptionIntegritySchema.optional(),
|
|
193
482
|
commits: z.array(SyncCommitSchema),
|
|
194
483
|
snapshots: z.array(SyncSnapshotSchema).optional(),
|
|
195
484
|
});
|
|
@@ -211,7 +500,7 @@ export type SyncPullResponse = z.infer<typeof SyncPullResponseSchema>;
|
|
|
211
500
|
|
|
212
501
|
export const SyncCombinedRequestSchema = z.object({
|
|
213
502
|
clientId: z.string().min(1),
|
|
214
|
-
push:
|
|
503
|
+
push: SyncPushBatchRequestSchema.omit({ clientId: true }).optional(),
|
|
215
504
|
pull: SyncPullRequestSchema.omit({ clientId: true }).optional(),
|
|
216
505
|
});
|
|
217
506
|
|
|
@@ -219,7 +508,9 @@ export type SyncCombinedRequest = z.infer<typeof SyncCombinedRequestSchema>;
|
|
|
219
508
|
|
|
220
509
|
export const SyncCombinedResponseSchema = z.object({
|
|
221
510
|
ok: z.literal(true),
|
|
222
|
-
|
|
511
|
+
requiredSchemaVersion: z.number().int().min(1).optional(),
|
|
512
|
+
latestSchemaVersion: z.number().int().min(1).optional(),
|
|
513
|
+
push: SyncPushBatchResponseSchema.optional(),
|
|
223
514
|
pull: SyncPullResponseSchema.optional(),
|
|
224
515
|
});
|
|
225
516
|
|
package/src/scopes/index.ts
CHANGED
|
@@ -179,6 +179,36 @@ export function normalizeScopes(
|
|
|
179
179
|
return result;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
export function createSingleVariableScopeMetadata(
|
|
183
|
+
scopes: readonly ScopeDefinition[]
|
|
184
|
+
): {
|
|
185
|
+
scopePatterns: ScopePattern[];
|
|
186
|
+
scopeColumnsByVariable: Record<string, string>;
|
|
187
|
+
} {
|
|
188
|
+
const scopeColumnMap = normalizeScopes(scopes);
|
|
189
|
+
const scopePatterns = Object.keys(scopeColumnMap) as ScopePattern[];
|
|
190
|
+
const scopeColumnsByVariable: Record<string, string> = {};
|
|
191
|
+
|
|
192
|
+
for (const [pattern, columnName] of Object.entries(scopeColumnMap)) {
|
|
193
|
+
const vars = extractScopeVars(pattern);
|
|
194
|
+
if (vars.length !== 1) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Scope pattern "${pattern}" must contain exactly one placeholder (got ${vars.length}).`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const variableName = vars[0]!;
|
|
200
|
+
const existingColumn = scopeColumnsByVariable[variableName];
|
|
201
|
+
if (existingColumn && existingColumn !== columnName) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Scope variable "${variableName}" is mapped to multiple columns: "${existingColumn}" and "${columnName}".`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
scopeColumnsByVariable[variableName] = columnName;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { scopePatterns, scopeColumnsByVariable };
|
|
210
|
+
}
|
|
211
|
+
|
|
182
212
|
// ── Value operations (public) ────────────────────────────────────────
|
|
183
213
|
|
|
184
214
|
/**
|
|
@@ -193,3 +223,15 @@ export function extractScopeVars(pattern: ScopePattern): string[] {
|
|
|
193
223
|
if (!matches) return [];
|
|
194
224
|
return matches.map((m) => m.slice(1, -1));
|
|
195
225
|
}
|
|
226
|
+
|
|
227
|
+
export function collectScopeVars(
|
|
228
|
+
scopePatterns: readonly ScopePattern[]
|
|
229
|
+
): Set<string> {
|
|
230
|
+
const keys = new Set<string>();
|
|
231
|
+
for (const pattern of scopePatterns) {
|
|
232
|
+
for (const key of extractScopeVars(pattern)) {
|
|
233
|
+
keys.add(key);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return keys;
|
|
237
|
+
}
|
package/src/sentry.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SyncMetricOptions,
|
|
3
|
+
SyncMetrics,
|
|
4
|
+
SyncSpan,
|
|
5
|
+
SyncSpanOptions,
|
|
6
|
+
SyncTelemetry,
|
|
7
|
+
SyncTelemetryAttributes,
|
|
8
|
+
SyncTelemetryAttributeValue,
|
|
9
|
+
SyncTelemetryEvent,
|
|
10
|
+
SyncTelemetryLevel,
|
|
11
|
+
SyncTracer,
|
|
12
|
+
} from '@syncular/core';
|
|
13
|
+
|
|
14
|
+
interface SentryLoggerAdapter {
|
|
15
|
+
trace?(message: string, attributes?: Record<string, unknown>): void;
|
|
16
|
+
debug?(message: string, attributes?: Record<string, unknown>): void;
|
|
17
|
+
info?(message: string, attributes?: Record<string, unknown>): void;
|
|
18
|
+
warn?(message: string, attributes?: Record<string, unknown>): void;
|
|
19
|
+
error?(message: string, attributes?: Record<string, unknown>): void;
|
|
20
|
+
fatal?(message: string, attributes?: Record<string, unknown>): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SentrySpanAdapter {
|
|
24
|
+
setAttribute?(name: string, value: SyncTelemetryAttributeValue): void;
|
|
25
|
+
setAttributes?(attributes: SyncTelemetryAttributes): void;
|
|
26
|
+
setStatus?(status: 'ok' | 'error' | string): void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface SentryMetricsAdapter {
|
|
30
|
+
count?(name: string, value: number, options?: SyncMetricOptions): void;
|
|
31
|
+
gauge?(name: string, value: number, options?: SyncMetricOptions): void;
|
|
32
|
+
distribution?(name: string, value: number, options?: SyncMetricOptions): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SentryTelemetryAdapter {
|
|
36
|
+
logger?: SentryLoggerAdapter;
|
|
37
|
+
startSpan?<T>(
|
|
38
|
+
options: SyncSpanOptions,
|
|
39
|
+
callback: (span: SentrySpanAdapter) => T
|
|
40
|
+
): T;
|
|
41
|
+
metrics?: SentryMetricsAdapter;
|
|
42
|
+
captureException?(error: unknown): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const noopSpan: SyncSpan = {
|
|
46
|
+
setAttribute() {},
|
|
47
|
+
setAttributes() {},
|
|
48
|
+
setStatus() {},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function resolveLogLevel(event: SyncTelemetryEvent): SyncTelemetryLevel {
|
|
52
|
+
if (event.level) return event.level;
|
|
53
|
+
return event.error ? 'error' : 'info';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveLogMethod(
|
|
57
|
+
logger: SentryLoggerAdapter | undefined,
|
|
58
|
+
level: SyncTelemetryLevel
|
|
59
|
+
): ((message: string, attributes?: Record<string, unknown>) => void) | null {
|
|
60
|
+
if (!logger) return null;
|
|
61
|
+
switch (level) {
|
|
62
|
+
case 'trace':
|
|
63
|
+
return logger.trace ?? logger.debug ?? logger.info ?? null;
|
|
64
|
+
case 'debug':
|
|
65
|
+
return logger.debug ?? logger.info ?? null;
|
|
66
|
+
case 'info':
|
|
67
|
+
return logger.info ?? null;
|
|
68
|
+
case 'warn':
|
|
69
|
+
return logger.warn ?? logger.info ?? null;
|
|
70
|
+
case 'error':
|
|
71
|
+
return logger.error ?? logger.warn ?? logger.info ?? null;
|
|
72
|
+
case 'fatal':
|
|
73
|
+
return logger.fatal ?? logger.error ?? logger.warn ?? logger.info ?? null;
|
|
74
|
+
default:
|
|
75
|
+
return logger.info ?? null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function toSpan(span: SentrySpanAdapter): SyncSpan {
|
|
80
|
+
return {
|
|
81
|
+
setAttribute(name, value) {
|
|
82
|
+
span.setAttribute?.(name, value);
|
|
83
|
+
},
|
|
84
|
+
setAttributes(attributes) {
|
|
85
|
+
if (span.setAttributes) {
|
|
86
|
+
span.setAttributes(attributes);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
91
|
+
span.setAttribute?.(key, value);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
setStatus(status) {
|
|
95
|
+
span.setStatus?.(status);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function toLogAttributeValue(
|
|
101
|
+
value: unknown
|
|
102
|
+
): SyncTelemetryAttributeValue | undefined {
|
|
103
|
+
if (typeof value === 'string') return value;
|
|
104
|
+
if (typeof value === 'number') {
|
|
105
|
+
return Number.isFinite(value) ? value : undefined;
|
|
106
|
+
}
|
|
107
|
+
if (typeof value === 'boolean') return value;
|
|
108
|
+
if (typeof value === 'bigint') {
|
|
109
|
+
const asNumber = Number(value);
|
|
110
|
+
if (Number.isFinite(asNumber)) return asNumber;
|
|
111
|
+
return value.toString();
|
|
112
|
+
}
|
|
113
|
+
if (value instanceof Date) return value.toISOString();
|
|
114
|
+
if (value === null || value === undefined) return undefined;
|
|
115
|
+
|
|
116
|
+
if (Array.isArray(value) || typeof value === 'object') {
|
|
117
|
+
try {
|
|
118
|
+
return JSON.stringify(value);
|
|
119
|
+
} catch {
|
|
120
|
+
return String(value);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return String(value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function sanitizeLogAttributes(
|
|
128
|
+
attributes: Record<string, unknown>
|
|
129
|
+
): Record<string, SyncTelemetryAttributeValue> | null {
|
|
130
|
+
const sanitized: Record<string, SyncTelemetryAttributeValue> = {};
|
|
131
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
132
|
+
const normalized = toLogAttributeValue(value);
|
|
133
|
+
if (normalized !== undefined) {
|
|
134
|
+
sanitized[name] = normalized;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return Object.keys(sanitized).length > 0 ? sanitized : null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function createTracer(adapter: SentryTelemetryAdapter): SyncTracer {
|
|
142
|
+
return {
|
|
143
|
+
startSpan<T>(options: SyncSpanOptions, callback: (span: SyncSpan) => T): T {
|
|
144
|
+
if (!adapter.startSpan) return callback(noopSpan);
|
|
145
|
+
return adapter.startSpan(options, (span) => callback(toSpan(span)));
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function createMetrics(adapter: SentryTelemetryAdapter): SyncMetrics {
|
|
151
|
+
return {
|
|
152
|
+
count(name, value, options) {
|
|
153
|
+
adapter.metrics?.count?.(name, value ?? 1, options);
|
|
154
|
+
},
|
|
155
|
+
gauge(name, value, options) {
|
|
156
|
+
adapter.metrics?.gauge?.(name, value, options);
|
|
157
|
+
},
|
|
158
|
+
distribution(name, value, options) {
|
|
159
|
+
adapter.metrics?.distribution?.(name, value, options);
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Create a Syncular telemetry adapter backed by Sentry primitives.
|
|
166
|
+
*/
|
|
167
|
+
export function createSentrySyncTelemetry(
|
|
168
|
+
adapter: SentryTelemetryAdapter
|
|
169
|
+
): SyncTelemetry {
|
|
170
|
+
return {
|
|
171
|
+
log(event) {
|
|
172
|
+
const level = resolveLogLevel(event);
|
|
173
|
+
const { event: message, ...attributes } = event;
|
|
174
|
+
const logMethod = resolveLogMethod(adapter.logger, level);
|
|
175
|
+
if (!logMethod) return;
|
|
176
|
+
const sanitizedAttributes = sanitizeLogAttributes(attributes);
|
|
177
|
+
if (!sanitizedAttributes) {
|
|
178
|
+
logMethod(message);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
logMethod(message, sanitizedAttributes);
|
|
182
|
+
},
|
|
183
|
+
tracer: createTracer(adapter),
|
|
184
|
+
metrics: createMetrics(adapter),
|
|
185
|
+
captureException(error, context) {
|
|
186
|
+
adapter.captureException?.(error);
|
|
187
|
+
if (!context) return;
|
|
188
|
+
const logMethod =
|
|
189
|
+
resolveLogMethod(adapter.logger, 'error') ??
|
|
190
|
+
resolveLogMethod(adapter.logger, 'info');
|
|
191
|
+
if (!logMethod) return;
|
|
192
|
+
const sanitizedContext = sanitizeLogAttributes(context);
|
|
193
|
+
if (!sanitizedContext) {
|
|
194
|
+
logMethod('sync.exception.context');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
logMethod('sync.exception.context', sanitizedContext);
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|