@syncular/core 0.30.16 → 0.32.0

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/errors.d.ts CHANGED
@@ -12,3 +12,13 @@ export declare class DecodeError extends Error {
12
12
  readonly code: string;
13
13
  constructor(code: string, message: string);
14
14
  }
15
+ /**
16
+ * A value the row codec cannot encode (SPEC.md §7.1 authoring validation,
17
+ * §6.1 push payloads). Structural failures stay plain `Error`s; this class
18
+ * marks exactly the value refusals, so a client can classify them without
19
+ * inspecting message text.
20
+ */
21
+ export declare class EncodeError extends Error {
22
+ readonly name = "EncodeError";
23
+ readonly code = "sync.invalid_request";
24
+ }
package/dist/errors.js CHANGED
@@ -15,3 +15,13 @@ export class DecodeError extends Error {
15
15
  this.code = code;
16
16
  }
17
17
  }
18
+ /**
19
+ * A value the row codec cannot encode (SPEC.md §7.1 authoring validation,
20
+ * §6.1 push payloads). Structural failures stay plain `Error`s; this class
21
+ * marks exactly the value refusals, so a client can classify them without
22
+ * inspecting message text.
23
+ */
24
+ export class EncodeError extends Error {
25
+ name = 'EncodeError';
26
+ code = 'sync.invalid_request';
27
+ }
package/dist/render.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * fields embedded as parsed JSON.
9
9
  */
10
10
  import type { SyncMessage } from './message.js';
11
- import type { RowColumn, SparseRowValue } from './row-codec.js';
11
+ import type { RowColumn, RowValue, SparseRowValue } from './row-codec.js';
12
12
  import type { RowsSegment } from './segment.js';
13
13
  export type JsonValue = null | boolean | number | string | JsonValue[] | {
14
14
  [key: string]: JsonValue;
@@ -18,6 +18,10 @@ export declare function bytesToBase64(bytes: Uint8Array): string;
18
18
  export declare function renderMessageValue(message: SyncMessage): JsonValue;
19
19
  /** `render(bytes) → json` for SSP2 messages (§11). */
20
20
  export declare function renderMessage(bytes: Uint8Array): JsonValue;
21
+ /** Render a decoded full row (§11) as JSON keyed by column name. */
22
+ export declare function renderRowValues(columns: readonly RowColumn[], values: readonly RowValue[]): {
23
+ [column: string]: JsonValue;
24
+ };
21
25
  export declare function renderRowsSegmentValue(segment: RowsSegment): JsonValue;
22
26
  /** `render(bytes) → json` for standalone SSG2 rows segments (§11 rule 8). */
23
27
  export declare function renderRowsSegment(bytes: Uint8Array): JsonValue;
package/dist/render.js CHANGED
@@ -275,6 +275,13 @@ function renderRowValue(column, value) {
275
275
  }
276
276
  return value;
277
277
  }
278
+ /** Render a decoded full row (§11) as JSON keyed by column name. */
279
+ export function renderRowValues(columns, values) {
280
+ return Object.fromEntries(columns.map((column, i) => [
281
+ column.name,
282
+ renderRowValue(column, values[i] ?? null),
283
+ ]));
284
+ }
278
285
  function renderColumnTable(columns) {
279
286
  return columns.map((column) => ({
280
287
  name: column.name,
@@ -291,10 +298,7 @@ export function renderRowsSegmentValue(segment) {
291
298
  columns: renderColumnTable(segment.columns),
292
299
  blocks: segment.blocks.map((block) => block.map((row) => ({
293
300
  serverVersion: row.serverVersion,
294
- values: Object.fromEntries(segment.columns.map((column, i) => [
295
- column.name,
296
- renderRowValue(column, row.values[i] ?? null),
297
- ])),
301
+ values: renderRowValues(segment.columns, row.values),
298
302
  }))),
299
303
  };
300
304
  }
package/dist/row-codec.js CHANGED
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { parseBlobRef } from './blob-ref.js';
16
16
  import { ByteReader, ByteWriter } from './bytes.js';
17
- import { DecodeError } from './errors.js';
17
+ import { DecodeError, EncodeError } from './errors.js';
18
18
  /** Wire tags, unchanged from v1's binary-table-v1 assignment (SPEC.md §2.4). */
19
19
  const TYPE_TO_TAG = {
20
20
  string: 1,
@@ -43,25 +43,25 @@ function writeValue(writer, column, value) {
43
43
  case 'json':
44
44
  case 'blob_ref':
45
45
  if (typeof value !== 'string') {
46
- throw new Error(`column ${column.name} (${column.type}) requires a string value`);
46
+ throw new EncodeError(`column ${column.name} (${column.type}) requires a string value`);
47
47
  }
48
48
  writer.str(value);
49
49
  return;
50
50
  case 'integer':
51
51
  if (typeof value !== 'number' || !Number.isSafeInteger(value)) {
52
- throw new Error(`column ${column.name} (integer) requires a safe integer value`);
52
+ throw new EncodeError(`column ${column.name} (integer) requires a safe integer value`);
53
53
  }
54
54
  writer.i64(value);
55
55
  return;
56
56
  case 'float':
57
57
  if (typeof value !== 'number') {
58
- throw new Error(`column ${column.name} (float) requires a number value`);
58
+ throw new EncodeError(`column ${column.name} (float) requires a number value`);
59
59
  }
60
60
  writer.f64(value);
61
61
  return;
62
62
  case 'boolean':
63
63
  if (typeof value !== 'boolean') {
64
- throw new Error(`column ${column.name} (boolean) requires a boolean value`);
64
+ throw new EncodeError(`column ${column.name} (boolean) requires a boolean value`);
65
65
  }
66
66
  writer.bool(value);
67
67
  return;
@@ -71,7 +71,7 @@ function writeValue(writer, column, value) {
71
71
  // opaque CRDT bytes (§5.10), no structural validation. `crdtType`
72
72
  // selects the server merger and never touches the codec.
73
73
  if (!(value instanceof Uint8Array)) {
74
- throw new Error(`column ${column.name} (${column.type}) requires a Uint8Array value`);
74
+ throw new EncodeError(`column ${column.name} (${column.type}) requires a Uint8Array value`);
75
75
  }
76
76
  writer.bytes(value);
77
77
  return;
@@ -152,7 +152,7 @@ export function encodeSparseRow(columns, primaryKeyIndex, values) {
152
152
  continue;
153
153
  if (value === null) {
154
154
  if (!column.nullable) {
155
- throw new Error(`column ${column.name} is not nullable`);
155
+ throw new EncodeError(`column ${column.name} is not nullable`);
156
156
  }
157
157
  nulls[present >> 3] = (nulls[present >> 3] ?? 0) | (1 << (present & 7));
158
158
  }
@@ -240,7 +240,7 @@ export function writeRow(writer, columns, values) {
240
240
  continue;
241
241
  if (values[i] === null) {
242
242
  if (!column.nullable) {
243
- throw new Error(`column ${column.name} is not nullable`);
243
+ throw new EncodeError(`column ${column.name} is not nullable`);
244
244
  }
245
245
  bitmap[i >> 3] = (bitmap[i >> 3] ?? 0) | (1 << (i & 7));
246
246
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/core",
3
- "version": "0.30.16",
3
+ "version": "0.32.0",
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",
package/src/errors.ts CHANGED
@@ -16,3 +16,14 @@ export class DecodeError extends Error {
16
16
  this.code = code;
17
17
  }
18
18
  }
19
+
20
+ /**
21
+ * A value the row codec cannot encode (SPEC.md §7.1 authoring validation,
22
+ * §6.1 push payloads). Structural failures stay plain `Error`s; this class
23
+ * marks exactly the value refusals, so a client can classify them without
24
+ * inspecting message text.
25
+ */
26
+ export class EncodeError extends Error {
27
+ override readonly name = 'EncodeError';
28
+ readonly code = 'sync.invalid_request';
29
+ }
package/src/render.ts CHANGED
@@ -322,6 +322,19 @@ function renderRowValue(column: RowColumn, value: RowValue): JsonValue {
322
322
  return value;
323
323
  }
324
324
 
325
+ /** Render a decoded full row (§11) as JSON keyed by column name. */
326
+ export function renderRowValues(
327
+ columns: readonly RowColumn[],
328
+ values: readonly RowValue[],
329
+ ): { [column: string]: JsonValue } {
330
+ return Object.fromEntries(
331
+ columns.map((column, i) => [
332
+ column.name,
333
+ renderRowValue(column, values[i] ?? null),
334
+ ]),
335
+ );
336
+ }
337
+
325
338
  function renderColumnTable(columns: readonly RowColumn[]): JsonValue {
326
339
  return columns.map((column) => ({
327
340
  name: column.name,
@@ -340,12 +353,7 @@ export function renderRowsSegmentValue(segment: RowsSegment): JsonValue {
340
353
  blocks: segment.blocks.map((block) =>
341
354
  block.map((row) => ({
342
355
  serverVersion: row.serverVersion,
343
- values: Object.fromEntries(
344
- segment.columns.map((column, i) => [
345
- column.name,
346
- renderRowValue(column, row.values[i] ?? null),
347
- ]),
348
- ),
356
+ values: renderRowValues(segment.columns, row.values),
349
357
  })),
350
358
  ),
351
359
  };
package/src/row-codec.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { parseBlobRef } from './blob-ref';
16
16
  import { ByteReader, ByteWriter } from './bytes';
17
- import { DecodeError } from './errors';
17
+ import { DecodeError, EncodeError } from './errors';
18
18
 
19
19
  export type ColumnType =
20
20
  | 'string'
@@ -98,7 +98,7 @@ function writeValue(
98
98
  case 'json':
99
99
  case 'blob_ref':
100
100
  if (typeof value !== 'string') {
101
- throw new Error(
101
+ throw new EncodeError(
102
102
  `column ${column.name} (${column.type}) requires a string value`,
103
103
  );
104
104
  }
@@ -106,7 +106,7 @@ function writeValue(
106
106
  return;
107
107
  case 'integer':
108
108
  if (typeof value !== 'number' || !Number.isSafeInteger(value)) {
109
- throw new Error(
109
+ throw new EncodeError(
110
110
  `column ${column.name} (integer) requires a safe integer value`,
111
111
  );
112
112
  }
@@ -114,7 +114,7 @@ function writeValue(
114
114
  return;
115
115
  case 'float':
116
116
  if (typeof value !== 'number') {
117
- throw new Error(
117
+ throw new EncodeError(
118
118
  `column ${column.name} (float) requires a number value`,
119
119
  );
120
120
  }
@@ -122,7 +122,7 @@ function writeValue(
122
122
  return;
123
123
  case 'boolean':
124
124
  if (typeof value !== 'boolean') {
125
- throw new Error(
125
+ throw new EncodeError(
126
126
  `column ${column.name} (boolean) requires a boolean value`,
127
127
  );
128
128
  }
@@ -134,7 +134,7 @@ function writeValue(
134
134
  // opaque CRDT bytes (§5.10), no structural validation. `crdtType`
135
135
  // selects the server merger and never touches the codec.
136
136
  if (!(value instanceof Uint8Array)) {
137
- throw new Error(
137
+ throw new EncodeError(
138
138
  `column ${column.name} (${column.type}) requires a Uint8Array value`,
139
139
  );
140
140
  }
@@ -237,7 +237,7 @@ export function encodeSparseRow(
237
237
  if (column === undefined || value === undefined) continue;
238
238
  if (value === null) {
239
239
  if (!column.nullable) {
240
- throw new Error(`column ${column.name} is not nullable`);
240
+ throw new EncodeError(`column ${column.name} is not nullable`);
241
241
  }
242
242
  nulls[present >> 3] = (nulls[present >> 3] ?? 0) | (1 << (present & 7));
243
243
  }
@@ -350,7 +350,7 @@ export function writeRow(
350
350
  if (column === undefined) continue;
351
351
  if (values[i] === null) {
352
352
  if (!column.nullable) {
353
- throw new Error(`column ${column.name} is not nullable`);
353
+ throw new EncodeError(`column ${column.name} is not nullable`);
354
354
  }
355
355
  bitmap[i >> 3] = (bitmap[i >> 3] ?? 0) | (1 << (i & 7));
356
356
  }