@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 +10 -0
- package/dist/errors.js +10 -0
- package/dist/render.d.ts +5 -1
- package/dist/render.js +8 -4
- package/dist/row-codec.js +8 -8
- package/package.json +1 -1
- package/src/errors.ts +11 -0
- package/src/render.ts +14 -6
- package/src/row-codec.ts +8 -8
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|