@syncular/core 0.0.6-206 → 0.0.6-211

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.
@@ -251,7 +251,7 @@ export declare const SyncSnapshotChunkRefSchema: z.ZodObject<{
251
251
  id: z.ZodString;
252
252
  byteLength: z.ZodNumber;
253
253
  sha256: z.ZodString;
254
- encoding: z.ZodLiteral<"json-row-batch-frame-v2">;
254
+ encoding: z.ZodLiteral<"json-row-frame-v1">;
255
255
  compression: z.ZodLiteral<"gzip">;
256
256
  }, z.core.$strip>;
257
257
  export type SyncSnapshotChunkRef = z.infer<typeof SyncSnapshotChunkRefSchema>;
@@ -262,7 +262,7 @@ export declare const SyncSnapshotSchema: z.ZodObject<{
262
262
  id: z.ZodString;
263
263
  byteLength: z.ZodNumber;
264
264
  sha256: z.ZodString;
265
- encoding: z.ZodLiteral<"json-row-batch-frame-v2">;
265
+ encoding: z.ZodLiteral<"json-row-frame-v1">;
266
266
  compression: z.ZodLiteral<"gzip">;
267
267
  }, z.core.$strip>>>;
268
268
  isFirstPage: z.ZodBoolean;
@@ -307,7 +307,7 @@ export declare const SyncPullSubscriptionResponseSchema: z.ZodObject<{
307
307
  id: z.ZodString;
308
308
  byteLength: z.ZodNumber;
309
309
  sha256: z.ZodString;
310
- encoding: z.ZodLiteral<"json-row-batch-frame-v2">;
310
+ encoding: z.ZodLiteral<"json-row-frame-v1">;
311
311
  compression: z.ZodLiteral<"gzip">;
312
312
  }, z.core.$strip>>>;
313
313
  isFirstPage: z.ZodBoolean;
@@ -355,7 +355,7 @@ export declare const SyncPullResponseSchema: z.ZodObject<{
355
355
  id: z.ZodString;
356
356
  byteLength: z.ZodNumber;
357
357
  sha256: z.ZodString;
358
- encoding: z.ZodLiteral<"json-row-batch-frame-v2">;
358
+ encoding: z.ZodLiteral<"json-row-frame-v1">;
359
359
  compression: z.ZodLiteral<"gzip">;
360
360
  }, z.core.$strip>>>;
361
361
  isFirstPage: z.ZodBoolean;
@@ -474,7 +474,7 @@ export declare const SyncCombinedResponseSchema: z.ZodObject<{
474
474
  id: z.ZodString;
475
475
  byteLength: z.ZodNumber;
476
476
  sha256: z.ZodString;
477
- encoding: z.ZodLiteral<"json-row-batch-frame-v2">;
477
+ encoding: z.ZodLiteral<"json-row-frame-v1">;
478
478
  compression: z.ZodLiteral<"gzip">;
479
479
  }, z.core.$strip>>>;
480
480
  isFirstPage: z.ZodBoolean;
@@ -1,23 +1,22 @@
1
1
  /**
2
2
  * @syncular/core - Snapshot chunk encoding helpers
3
3
  */
4
- export declare const SYNC_SNAPSHOT_CHUNK_ENCODING = "json-row-batch-frame-v2";
4
+ export declare const SYNC_SNAPSHOT_CHUNK_ENCODING = "json-row-frame-v1";
5
5
  export type SyncSnapshotChunkEncoding = typeof SYNC_SNAPSHOT_CHUNK_ENCODING;
6
6
  export declare const SYNC_SNAPSHOT_CHUNK_COMPRESSION = "gzip";
7
7
  export type SyncSnapshotChunkCompression = typeof SYNC_SNAPSHOT_CHUNK_COMPRESSION;
8
- export declare const SYNC_SNAPSHOT_CHUNK_MAGIC: Uint8Array<ArrayBuffer>;
9
8
  /**
10
- * Encode rows as a single framed JSON batch without the format header.
9
+ * Encode rows as framed JSON bytes without the format header.
11
10
  */
12
11
  export declare function encodeSnapshotRowFrames(rows: readonly unknown[]): Uint8Array;
13
12
  /**
14
13
  * Encode rows as framed JSON bytes with a format header.
15
14
  *
16
15
  * Format:
17
- * - 4-byte magic header ("SBF2")
16
+ * - 4-byte magic header ("SRF1")
18
17
  * - repeated frames of:
19
18
  * - 4-byte big-endian payload byte length
20
- * - UTF-8 JSON array payload
19
+ * - UTF-8 JSON payload
21
20
  */
22
21
  export declare function encodeSnapshotRows(rows: readonly unknown[]): Uint8Array;
23
22
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-chunks.d.ts","sourceRoot":"","sources":["../src/snapshot-chunks.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AACtE,MAAM,MAAM,yBAAyB,GAAG,OAAO,4BAA4B,CAAC;AAE5E,eAAO,MAAM,+BAA+B,SAAS,CAAC;AACtD,MAAM,MAAM,4BAA4B,GACtC,OAAO,+BAA+B,CAAC;AAEzC,eAAO,MAAM,yBAAyB,yBAEpC,CAAC;AAUH;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,CAiB5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,CASvE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,EAAE,CAuC/D"}
1
+ {"version":3,"file":"snapshot-chunks.d.ts","sourceRoot":"","sources":["../src/snapshot-chunks.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,4BAA4B,sBAAsB,CAAC;AAChE,MAAM,MAAM,yBAAyB,GAAG,OAAO,4BAA4B,CAAC;AAE5E,eAAO,MAAM,+BAA+B,SAAS,CAAC;AACtD,MAAM,MAAM,4BAA4B,GACtC,OAAO,+BAA+B,CAAC;AAazC;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,CA0B5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,CASvE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,EAAE,CAmC/D"}
@@ -1,61 +1,68 @@
1
1
  /**
2
2
  * @syncular/core - Snapshot chunk encoding helpers
3
3
  */
4
- export const SYNC_SNAPSHOT_CHUNK_ENCODING = 'json-row-batch-frame-v2';
4
+ export const SYNC_SNAPSHOT_CHUNK_ENCODING = 'json-row-frame-v1';
5
5
  export const SYNC_SNAPSHOT_CHUNK_COMPRESSION = 'gzip';
6
- export const SYNC_SNAPSHOT_CHUNK_MAGIC = new Uint8Array([
7
- 0x53, 0x42, 0x46, 0x32,
8
- ]); // "SBF2"
6
+ const SNAPSHOT_ROW_FRAME_MAGIC = new Uint8Array([0x53, 0x52, 0x46, 0x31]); // "SRF1"
9
7
  const FRAME_LENGTH_BYTES = 4;
10
8
  const MAX_FRAME_BYTE_LENGTH = 0xffff_ffff;
11
9
  const snapshotRowFrameEncoder = new TextEncoder();
12
10
  const snapshotRowFrameDecoder = new TextDecoder();
13
- function normalizeRowBatchJson(rows) {
14
- return JSON.stringify(rows);
11
+ function normalizeRowJson(row) {
12
+ const serialized = JSON.stringify(row);
13
+ return serialized === undefined ? 'null' : serialized;
15
14
  }
16
15
  /**
17
- * Encode rows as a single framed JSON batch without the format header.
16
+ * Encode rows as framed JSON bytes without the format header.
18
17
  */
19
18
  export function encodeSnapshotRowFrames(rows) {
20
- if (rows.length === 0) {
21
- return new Uint8Array();
22
- }
23
- const payload = snapshotRowFrameEncoder.encode(normalizeRowBatchJson(rows));
24
- if (payload.length > MAX_FRAME_BYTE_LENGTH) {
25
- throw new Error(`Snapshot row batch payload exceeds ${MAX_FRAME_BYTE_LENGTH} bytes`);
19
+ const payloads = [];
20
+ let totalByteLength = 0;
21
+ for (const row of rows) {
22
+ const payload = snapshotRowFrameEncoder.encode(normalizeRowJson(row));
23
+ if (payload.length > MAX_FRAME_BYTE_LENGTH) {
24
+ throw new Error(`Snapshot row payload exceeds ${MAX_FRAME_BYTE_LENGTH} bytes`);
25
+ }
26
+ payloads.push(payload);
27
+ totalByteLength += FRAME_LENGTH_BYTES + payload.length;
26
28
  }
27
- const encoded = new Uint8Array(FRAME_LENGTH_BYTES + payload.length);
29
+ const encoded = new Uint8Array(totalByteLength);
28
30
  const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.length);
29
- view.setUint32(0, payload.length, false);
30
- encoded.set(payload, FRAME_LENGTH_BYTES);
31
+ let offset = 0;
32
+ for (const payload of payloads) {
33
+ view.setUint32(offset, payload.length, false);
34
+ offset += FRAME_LENGTH_BYTES;
35
+ encoded.set(payload, offset);
36
+ offset += payload.length;
37
+ }
31
38
  return encoded;
32
39
  }
33
40
  /**
34
41
  * Encode rows as framed JSON bytes with a format header.
35
42
  *
36
43
  * Format:
37
- * - 4-byte magic header ("SBF2")
44
+ * - 4-byte magic header ("SRF1")
38
45
  * - repeated frames of:
39
46
  * - 4-byte big-endian payload byte length
40
- * - UTF-8 JSON array payload
47
+ * - UTF-8 JSON payload
41
48
  */
42
49
  export function encodeSnapshotRows(rows) {
43
50
  const framedRows = encodeSnapshotRowFrames(rows);
44
- const totalByteLength = SYNC_SNAPSHOT_CHUNK_MAGIC.length + framedRows.length;
51
+ const totalByteLength = SNAPSHOT_ROW_FRAME_MAGIC.length + framedRows.length;
45
52
  const encoded = new Uint8Array(totalByteLength);
46
- encoded.set(SYNC_SNAPSHOT_CHUNK_MAGIC, 0);
47
- encoded.set(framedRows, SYNC_SNAPSHOT_CHUNK_MAGIC.length);
53
+ encoded.set(SNAPSHOT_ROW_FRAME_MAGIC, 0);
54
+ encoded.set(framedRows, SNAPSHOT_ROW_FRAME_MAGIC.length);
48
55
  return encoded;
49
56
  }
50
57
  /**
51
58
  * Decode framed JSON bytes into rows.
52
59
  */
53
60
  export function decodeSnapshotRows(bytes) {
54
- if (bytes.length < SYNC_SNAPSHOT_CHUNK_MAGIC.length) {
61
+ if (bytes.length < SNAPSHOT_ROW_FRAME_MAGIC.length) {
55
62
  throw new Error('Snapshot chunk payload is too small');
56
63
  }
57
- for (let index = 0; index < SYNC_SNAPSHOT_CHUNK_MAGIC.length; index += 1) {
58
- const expected = SYNC_SNAPSHOT_CHUNK_MAGIC[index];
64
+ for (let index = 0; index < SNAPSHOT_ROW_FRAME_MAGIC.length; index += 1) {
65
+ const expected = SNAPSHOT_ROW_FRAME_MAGIC[index];
59
66
  const actual = bytes[index];
60
67
  if (actual !== expected) {
61
68
  throw new Error('Unexpected snapshot chunk format');
@@ -63,7 +70,7 @@ export function decodeSnapshotRows(bytes) {
63
70
  }
64
71
  const rows = [];
65
72
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.length);
66
- let offset = SYNC_SNAPSHOT_CHUNK_MAGIC.length;
73
+ let offset = SNAPSHOT_ROW_FRAME_MAGIC.length;
67
74
  while (offset < bytes.length) {
68
75
  if (offset + FRAME_LENGTH_BYTES > bytes.length) {
69
76
  throw new Error('Snapshot chunk payload ended mid-frame header');
@@ -75,11 +82,7 @@ export function decodeSnapshotRows(bytes) {
75
82
  }
76
83
  const payload = bytes.subarray(offset, offset + payloadLength);
77
84
  offset += payloadLength;
78
- const parsed = JSON.parse(snapshotRowFrameDecoder.decode(payload));
79
- if (!Array.isArray(parsed)) {
80
- throw new Error('Snapshot chunk frame payload must be a JSON array');
81
- }
82
- rows.push(...parsed);
85
+ rows.push(JSON.parse(snapshotRowFrameDecoder.decode(payload)));
83
86
  }
84
87
  return rows;
85
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-chunks.js","sourceRoot":"","sources":["../src/snapshot-chunks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snapshot-chunks.js","sourceRoot":"","sources":["../src/snapshot-chunks.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/core",
3
- "version": "0.0.6-206",
3
+ "version": "0.0.6-211",
4
4
  "description": "Core protocol types and shared utilities for the Syncular sync framework",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -2,44 +2,52 @@
2
2
  * @syncular/core - Snapshot chunk encoding helpers
3
3
  */
4
4
 
5
- export const SYNC_SNAPSHOT_CHUNK_ENCODING = 'json-row-batch-frame-v2';
5
+ export const SYNC_SNAPSHOT_CHUNK_ENCODING = 'json-row-frame-v1';
6
6
  export type SyncSnapshotChunkEncoding = typeof SYNC_SNAPSHOT_CHUNK_ENCODING;
7
7
 
8
8
  export const SYNC_SNAPSHOT_CHUNK_COMPRESSION = 'gzip';
9
9
  export type SyncSnapshotChunkCompression =
10
10
  typeof SYNC_SNAPSHOT_CHUNK_COMPRESSION;
11
11
 
12
- export const SYNC_SNAPSHOT_CHUNK_MAGIC = new Uint8Array([
13
- 0x53, 0x42, 0x46, 0x32,
14
- ]); // "SBF2"
12
+ const SNAPSHOT_ROW_FRAME_MAGIC = new Uint8Array([0x53, 0x52, 0x46, 0x31]); // "SRF1"
15
13
  const FRAME_LENGTH_BYTES = 4;
16
14
  const MAX_FRAME_BYTE_LENGTH = 0xffff_ffff;
17
15
  const snapshotRowFrameEncoder = new TextEncoder();
18
16
  const snapshotRowFrameDecoder = new TextDecoder();
19
17
 
20
- function normalizeRowBatchJson(rows: readonly unknown[]): string {
21
- return JSON.stringify(rows);
18
+ function normalizeRowJson(row: unknown): string {
19
+ const serialized = JSON.stringify(row);
20
+ return serialized === undefined ? 'null' : serialized;
22
21
  }
23
22
 
24
23
  /**
25
- * Encode rows as a single framed JSON batch without the format header.
24
+ * Encode rows as framed JSON bytes without the format header.
26
25
  */
27
26
  export function encodeSnapshotRowFrames(rows: readonly unknown[]): Uint8Array {
28
- if (rows.length === 0) {
29
- return new Uint8Array();
27
+ const payloads: Uint8Array[] = [];
28
+ let totalByteLength = 0;
29
+
30
+ for (const row of rows) {
31
+ const payload = snapshotRowFrameEncoder.encode(normalizeRowJson(row));
32
+ if (payload.length > MAX_FRAME_BYTE_LENGTH) {
33
+ throw new Error(
34
+ `Snapshot row payload exceeds ${MAX_FRAME_BYTE_LENGTH} bytes`
35
+ );
36
+ }
37
+ payloads.push(payload);
38
+ totalByteLength += FRAME_LENGTH_BYTES + payload.length;
30
39
  }
31
40
 
32
- const payload = snapshotRowFrameEncoder.encode(normalizeRowBatchJson(rows));
33
- if (payload.length > MAX_FRAME_BYTE_LENGTH) {
34
- throw new Error(
35
- `Snapshot row batch payload exceeds ${MAX_FRAME_BYTE_LENGTH} bytes`
36
- );
41
+ const encoded = new Uint8Array(totalByteLength);
42
+ const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.length);
43
+ let offset = 0;
44
+ for (const payload of payloads) {
45
+ view.setUint32(offset, payload.length, false);
46
+ offset += FRAME_LENGTH_BYTES;
47
+ encoded.set(payload, offset);
48
+ offset += payload.length;
37
49
  }
38
50
 
39
- const encoded = new Uint8Array(FRAME_LENGTH_BYTES + payload.length);
40
- const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.length);
41
- view.setUint32(0, payload.length, false);
42
- encoded.set(payload, FRAME_LENGTH_BYTES);
43
51
  return encoded;
44
52
  }
45
53
 
@@ -47,18 +55,18 @@ export function encodeSnapshotRowFrames(rows: readonly unknown[]): Uint8Array {
47
55
  * Encode rows as framed JSON bytes with a format header.
48
56
  *
49
57
  * Format:
50
- * - 4-byte magic header ("SBF2")
58
+ * - 4-byte magic header ("SRF1")
51
59
  * - repeated frames of:
52
60
  * - 4-byte big-endian payload byte length
53
- * - UTF-8 JSON array payload
61
+ * - UTF-8 JSON payload
54
62
  */
55
63
  export function encodeSnapshotRows(rows: readonly unknown[]): Uint8Array {
56
64
  const framedRows = encodeSnapshotRowFrames(rows);
57
- const totalByteLength = SYNC_SNAPSHOT_CHUNK_MAGIC.length + framedRows.length;
65
+ const totalByteLength = SNAPSHOT_ROW_FRAME_MAGIC.length + framedRows.length;
58
66
 
59
67
  const encoded = new Uint8Array(totalByteLength);
60
- encoded.set(SYNC_SNAPSHOT_CHUNK_MAGIC, 0);
61
- encoded.set(framedRows, SYNC_SNAPSHOT_CHUNK_MAGIC.length);
68
+ encoded.set(SNAPSHOT_ROW_FRAME_MAGIC, 0);
69
+ encoded.set(framedRows, SNAPSHOT_ROW_FRAME_MAGIC.length);
62
70
 
63
71
  return encoded;
64
72
  }
@@ -67,12 +75,12 @@ export function encodeSnapshotRows(rows: readonly unknown[]): Uint8Array {
67
75
  * Decode framed JSON bytes into rows.
68
76
  */
69
77
  export function decodeSnapshotRows(bytes: Uint8Array): unknown[] {
70
- if (bytes.length < SYNC_SNAPSHOT_CHUNK_MAGIC.length) {
78
+ if (bytes.length < SNAPSHOT_ROW_FRAME_MAGIC.length) {
71
79
  throw new Error('Snapshot chunk payload is too small');
72
80
  }
73
81
 
74
- for (let index = 0; index < SYNC_SNAPSHOT_CHUNK_MAGIC.length; index += 1) {
75
- const expected = SYNC_SNAPSHOT_CHUNK_MAGIC[index];
82
+ for (let index = 0; index < SNAPSHOT_ROW_FRAME_MAGIC.length; index += 1) {
83
+ const expected = SNAPSHOT_ROW_FRAME_MAGIC[index];
76
84
  const actual = bytes[index];
77
85
  if (actual !== expected) {
78
86
  throw new Error('Unexpected snapshot chunk format');
@@ -81,7 +89,7 @@ export function decodeSnapshotRows(bytes: Uint8Array): unknown[] {
81
89
 
82
90
  const rows: unknown[] = [];
83
91
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.length);
84
- let offset = SYNC_SNAPSHOT_CHUNK_MAGIC.length;
92
+ let offset = SNAPSHOT_ROW_FRAME_MAGIC.length;
85
93
 
86
94
  while (offset < bytes.length) {
87
95
  if (offset + FRAME_LENGTH_BYTES > bytes.length) {
@@ -97,11 +105,7 @@ export function decodeSnapshotRows(bytes: Uint8Array): unknown[] {
97
105
 
98
106
  const payload = bytes.subarray(offset, offset + payloadLength);
99
107
  offset += payloadLength;
100
- const parsed = JSON.parse(snapshotRowFrameDecoder.decode(payload));
101
- if (!Array.isArray(parsed)) {
102
- throw new Error('Snapshot chunk frame payload must be a JSON array');
103
- }
104
- rows.push(...parsed);
108
+ rows.push(JSON.parse(snapshotRowFrameDecoder.decode(payload)));
105
109
  }
106
110
 
107
111
  return rows;