@syncular/core 0.31.0 → 0.32.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/crypto.js +2 -2
- package/dist/message.js +3 -0
- package/dist/realtime.js +4 -1
- package/dist/render.d.ts +5 -1
- package/dist/render.js +10 -4
- package/package.json +1 -1
- package/src/crypto.ts +4 -2
- package/src/message.ts +3 -0
- package/src/realtime.ts +4 -1
- package/src/render.ts +15 -6
package/dist/crypto.js
CHANGED
|
@@ -111,12 +111,12 @@ export function deserializePlain(declaredType, bytes) {
|
|
|
111
111
|
catch {
|
|
112
112
|
throw new DecryptError(`decrypted ${declaredType} value is not valid UTF-8`);
|
|
113
113
|
}
|
|
114
|
-
if (declaredType === 'json') {
|
|
114
|
+
if (declaredType === 'json' || declaredType === 'blob_ref') {
|
|
115
115
|
try {
|
|
116
116
|
JSON.parse(text);
|
|
117
117
|
}
|
|
118
118
|
catch {
|
|
119
|
-
throw new DecryptError(
|
|
119
|
+
throw new DecryptError(`decrypted ${declaredType} value does not parse`);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
return text;
|
package/dist/message.js
CHANGED
|
@@ -551,6 +551,7 @@ function validateResponseSequence(frames) {
|
|
|
551
551
|
inSubscription = true;
|
|
552
552
|
sawSubscription = true;
|
|
553
553
|
sawBody = true;
|
|
554
|
+
lastPushResult = undefined;
|
|
554
555
|
subscriptionHasCommits = false;
|
|
555
556
|
subscriptionHasSegments = false;
|
|
556
557
|
break;
|
|
@@ -579,9 +580,11 @@ function validateResponseSequence(frames) {
|
|
|
579
580
|
case 'ERROR':
|
|
580
581
|
errorSeen = true;
|
|
581
582
|
sawBody = true;
|
|
583
|
+
lastPushResult = undefined;
|
|
582
584
|
break;
|
|
583
585
|
case 'UNKNOWN':
|
|
584
586
|
sawBody = true;
|
|
587
|
+
lastPushResult = undefined;
|
|
585
588
|
break;
|
|
586
589
|
}
|
|
587
590
|
}
|
package/dist/realtime.js
CHANGED
|
@@ -140,7 +140,10 @@ export function parseRealtimeServerEvent(text) {
|
|
|
140
140
|
if ('error' in data) {
|
|
141
141
|
// The publisher-directed error variant (`presence.too_large` /
|
|
142
142
|
// `presence.forbidden`, §8.6.2) — a string code, no `kind`/`doc`.
|
|
143
|
-
requireString(data.error, 'presence.data.error');
|
|
143
|
+
const error = requireString(data.error, 'presence.data.error');
|
|
144
|
+
if (error !== 'presence.too_large' && error !== 'presence.forbidden') {
|
|
145
|
+
malformed(`unknown presence error code ${JSON.stringify(error)}`);
|
|
146
|
+
}
|
|
144
147
|
}
|
|
145
148
|
else if ('kind' in data) {
|
|
146
149
|
validatePresenceKind(data.kind);
|
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
|
@@ -268,6 +268,8 @@ function renderRowValue(column, value) {
|
|
|
268
268
|
return null;
|
|
269
269
|
if (value instanceof Uint8Array)
|
|
270
270
|
return bytesToBase64(value);
|
|
271
|
+
if (typeof value === 'number')
|
|
272
|
+
return Number.isFinite(value) ? value : null;
|
|
271
273
|
if ((column.type === 'json' || column.type === 'blob_ref') &&
|
|
272
274
|
typeof value === 'string') {
|
|
273
275
|
// §11: json and blob_ref (tag 7) render as embedded parsed JSON.
|
|
@@ -275,6 +277,13 @@ function renderRowValue(column, value) {
|
|
|
275
277
|
}
|
|
276
278
|
return value;
|
|
277
279
|
}
|
|
280
|
+
/** Render a decoded full row (§11) as JSON keyed by column name. */
|
|
281
|
+
export function renderRowValues(columns, values) {
|
|
282
|
+
return Object.fromEntries(columns.map((column, i) => [
|
|
283
|
+
column.name,
|
|
284
|
+
renderRowValue(column, values[i] ?? null),
|
|
285
|
+
]));
|
|
286
|
+
}
|
|
278
287
|
function renderColumnTable(columns) {
|
|
279
288
|
return columns.map((column) => ({
|
|
280
289
|
name: column.name,
|
|
@@ -291,10 +300,7 @@ export function renderRowsSegmentValue(segment) {
|
|
|
291
300
|
columns: renderColumnTable(segment.columns),
|
|
292
301
|
blocks: segment.blocks.map((block) => block.map((row) => ({
|
|
293
302
|
serverVersion: row.serverVersion,
|
|
294
|
-
values:
|
|
295
|
-
column.name,
|
|
296
|
-
renderRowValue(column, row.values[i] ?? null),
|
|
297
|
-
])),
|
|
303
|
+
values: renderRowValues(segment.columns, row.values),
|
|
298
304
|
}))),
|
|
299
305
|
};
|
|
300
306
|
}
|
package/package.json
CHANGED
package/src/crypto.ts
CHANGED
|
@@ -148,11 +148,13 @@ export function deserializePlain(
|
|
|
148
148
|
`decrypted ${declaredType} value is not valid UTF-8`,
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
|
-
if (declaredType === 'json') {
|
|
151
|
+
if (declaredType === 'json' || declaredType === 'blob_ref') {
|
|
152
152
|
try {
|
|
153
153
|
JSON.parse(text);
|
|
154
154
|
} catch {
|
|
155
|
-
throw new DecryptError(
|
|
155
|
+
throw new DecryptError(
|
|
156
|
+
`decrypted ${declaredType} value does not parse`,
|
|
157
|
+
);
|
|
156
158
|
}
|
|
157
159
|
}
|
|
158
160
|
return text;
|
package/src/message.ts
CHANGED
|
@@ -824,6 +824,7 @@ function validateResponseSequence(frames: readonly ResponseFrame[]): void {
|
|
|
824
824
|
inSubscription = true;
|
|
825
825
|
sawSubscription = true;
|
|
826
826
|
sawBody = true;
|
|
827
|
+
lastPushResult = undefined;
|
|
827
828
|
subscriptionHasCommits = false;
|
|
828
829
|
subscriptionHasSegments = false;
|
|
829
830
|
break;
|
|
@@ -853,9 +854,11 @@ function validateResponseSequence(frames: readonly ResponseFrame[]): void {
|
|
|
853
854
|
case 'ERROR':
|
|
854
855
|
errorSeen = true;
|
|
855
856
|
sawBody = true;
|
|
857
|
+
lastPushResult = undefined;
|
|
856
858
|
break;
|
|
857
859
|
case 'UNKNOWN':
|
|
858
860
|
sawBody = true;
|
|
861
|
+
lastPushResult = undefined;
|
|
859
862
|
break;
|
|
860
863
|
}
|
|
861
864
|
}
|
package/src/realtime.ts
CHANGED
|
@@ -257,7 +257,10 @@ export function parseRealtimeServerEvent(text: string): ParsedRealtimeEvent {
|
|
|
257
257
|
if ('error' in data) {
|
|
258
258
|
// The publisher-directed error variant (`presence.too_large` /
|
|
259
259
|
// `presence.forbidden`, §8.6.2) — a string code, no `kind`/`doc`.
|
|
260
|
-
requireString(data.error, 'presence.data.error');
|
|
260
|
+
const error = requireString(data.error, 'presence.data.error');
|
|
261
|
+
if (error !== 'presence.too_large' && error !== 'presence.forbidden') {
|
|
262
|
+
malformed(`unknown presence error code ${JSON.stringify(error)}`);
|
|
263
|
+
}
|
|
261
264
|
} else if ('kind' in data) {
|
|
262
265
|
validatePresenceKind(data.kind);
|
|
263
266
|
requireString(data.actorId, 'presence.data.actorId');
|
package/src/render.ts
CHANGED
|
@@ -312,6 +312,7 @@ export function renderMessage(bytes: Uint8Array): JsonValue {
|
|
|
312
312
|
function renderRowValue(column: RowColumn, value: RowValue): JsonValue {
|
|
313
313
|
if (value === null) return null;
|
|
314
314
|
if (value instanceof Uint8Array) return bytesToBase64(value);
|
|
315
|
+
if (typeof value === 'number') return Number.isFinite(value) ? value : null;
|
|
315
316
|
if (
|
|
316
317
|
(column.type === 'json' || column.type === 'blob_ref') &&
|
|
317
318
|
typeof value === 'string'
|
|
@@ -322,6 +323,19 @@ function renderRowValue(column: RowColumn, value: RowValue): JsonValue {
|
|
|
322
323
|
return value;
|
|
323
324
|
}
|
|
324
325
|
|
|
326
|
+
/** Render a decoded full row (§11) as JSON keyed by column name. */
|
|
327
|
+
export function renderRowValues(
|
|
328
|
+
columns: readonly RowColumn[],
|
|
329
|
+
values: readonly RowValue[],
|
|
330
|
+
): { [column: string]: JsonValue } {
|
|
331
|
+
return Object.fromEntries(
|
|
332
|
+
columns.map((column, i) => [
|
|
333
|
+
column.name,
|
|
334
|
+
renderRowValue(column, values[i] ?? null),
|
|
335
|
+
]),
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
325
339
|
function renderColumnTable(columns: readonly RowColumn[]): JsonValue {
|
|
326
340
|
return columns.map((column) => ({
|
|
327
341
|
name: column.name,
|
|
@@ -340,12 +354,7 @@ export function renderRowsSegmentValue(segment: RowsSegment): JsonValue {
|
|
|
340
354
|
blocks: segment.blocks.map((block) =>
|
|
341
355
|
block.map((row) => ({
|
|
342
356
|
serverVersion: row.serverVersion,
|
|
343
|
-
values:
|
|
344
|
-
segment.columns.map((column, i) => [
|
|
345
|
-
column.name,
|
|
346
|
-
renderRowValue(column, row.values[i] ?? null),
|
|
347
|
-
]),
|
|
348
|
-
),
|
|
357
|
+
values: renderRowValues(segment.columns, row.values),
|
|
349
358
|
})),
|
|
350
359
|
),
|
|
351
360
|
};
|