@ultimat3/realtime 27.5.0 → 27.6.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/README.md +1 -1
- package/package.json +5 -5
- package/src/error-titles.ts +4 -1
- package/src/first-paint-hold.ts +4 -4
- package/src/pg-array.ts +6 -104
- package/src/pg-values.ts +2 -1
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ class — stay on `.`.
|
|
|
87
87
|
| hooks | `.` | `useQuery`, `useRecord`, `useRecords` (by key, or a whole type: `RecordSelection`), `useMutation`, `useMutationQueue`, `useOutbox`, `useConnection`, `useChannel`, `usePresence`, `hasPageSocket`, `installRealtime` |
|
|
88
88
|
| channel records, no replicator | `./server` | `recordPublisher` — an app's committed rows as channel `records`, sequenced per producer ([below](#channel-records-with-no-replicator)) |
|
|
89
89
|
| channels | `.` | `channel`, `channelRef`, `ChannelHandle`, `topic`, `readPresence`, the channel frame types |
|
|
90
|
-
| first paint | `.` | `holdFirstPaint`, `FIRST_PAINT_HOLD_MS` (1 s) — what the island bootstrap awaits before `mount
|
|
90
|
+
| first paint | `.` | `holdFirstPaint`, `FIRST_PAINT_HOLD_MS` (1 s) — what the island bootstrap awaits before `mount`, for an island that reads the store or the outbox: the boot's restore and the open outbox, capped (#506). An island taking only `useChannel` / `usePresence` / `useConnection` never awaits it and is never hidden |
|
|
91
91
|
| offline | `.` | `pageOutbox`, `recordPersister`, `persistedTypes`, `openLocalStore`, `pageLocalStore`, `memoryLocalStore()` |
|
|
92
92
|
| the socket's worker | `./sync-worker` | the SharedWorker entry — no exports |
|
|
93
93
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/realtime",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.6.1",
|
|
4
4
|
"description": "Three-tier realtime: channels, live queries, local-first sync — one protocol, one mutator shape",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"test": "bun test"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@ultimat3/core": "27.
|
|
44
|
-
"@ultimat3/entity": "27.
|
|
45
|
-
"@ultimat3/policy": "27.
|
|
46
|
-
"@ultimat3/query": "27.
|
|
43
|
+
"@ultimat3/core": "27.6.1",
|
|
44
|
+
"@ultimat3/entity": "27.6.1",
|
|
45
|
+
"@ultimat3/policy": "27.6.1",
|
|
46
|
+
"@ultimat3/query": "27.6.1",
|
|
47
47
|
"nats": "2.29.3"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/src/error-titles.ts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
// rebuilds a node's refusal by code in `refusalError`, and `page-errors.ts` constructs codes from
|
|
6
6
|
// here without importing it) and by `errors.ts`, so constructing a server refusal registers too.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// From `/page`, never core's barrel: this module is in every island chunk that reaches realtime,
|
|
9
|
+
// and the barrel's own anchors are core's and schema's titles tables — 4,571 B minified of titles
|
|
10
|
+
// no realtime island reads (`page-errors-bundle.test.ts`). The registry is one module either way.
|
|
11
|
+
import { registerErrorCodes } from '@ultimat3/core/page';
|
|
9
12
|
|
|
10
13
|
/** Codes this package declares and owns. */
|
|
11
14
|
export const REALTIME_OWNED_ERROR_CODES = [
|
package/src/first-paint-hold.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// What a realtime island
|
|
2
|
-
//
|
|
3
|
-
// the
|
|
4
|
-
// an IndexedDB that hangs must cost the hold, never the island.
|
|
1
|
+
// What the bootstrap of a realtime island that READS the store or the outbox awaits before `mount`
|
|
2
|
+
// (#506, owner option 3; one that only follows a channel never imports this): the page boot's disk
|
|
3
|
+
// restore AND the outbox it opened, so the first render already carries the overlays a reload
|
|
4
|
+
// rebuilds from the queue. Capped, because an IndexedDB that hangs must cost the hold, never the island.
|
|
5
5
|
|
|
6
6
|
import { peekOutbox } from './outbox-slot';
|
|
7
7
|
import { pageRealtime } from './page-store';
|
package/src/pg-array.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// The
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// The element type behind an array type oid. The WAL names only the ARRAY type and carries the
|
|
2
|
+
// value as its TEXT literal (`{a,b}`), while `@ultimat3/entity`'s repository hands an `arrayOf()`
|
|
3
|
+
// column back as a JS array — and a live row and a repository row have to be the same object.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// The GRAMMAR of that literal is `@ultimat3/entity`'s `parsePgArray`: the repository reads the
|
|
6
|
+
// same text when a driver leaves an array unparsed, and one grammar has one reader. This file
|
|
7
|
+
// keeps the half only the WAL needs — which element decoder an oid selects.
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* `text[]` -> `text`. The wire names only the ARRAY type, so the element type behind it is a
|
|
@@ -41,101 +41,3 @@ const ELEMENT_OF = Object.freeze<Record<number, number>>({
|
|
|
41
41
|
export function arrayElementOid(typeOid: number): number | undefined {
|
|
42
42
|
return Object.hasOwn(ELEMENT_OF, typeOid) ? ELEMENT_OF[typeOid] : undefined;
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
/** Where the scan is, so every branch below advances one cursor rather than slicing the text. */
|
|
46
|
-
interface Scan {
|
|
47
|
-
readonly text: string;
|
|
48
|
-
at: number;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* One array literal -> a nested JS array, or `null` when the text is not one this grammar
|
|
53
|
-
* describes. `null` is not an error: a dimension prefix (`[0:2]={…}`), a `DateStyle` this decoder
|
|
54
|
-
* does not read, or a corrupted literal all mean the same thing to the caller — keep the text it
|
|
55
|
-
* arrived as rather than deliver an array that is missing a member.
|
|
56
|
-
*
|
|
57
|
-
* `decode` is the ELEMENT decoder. It never sees a quoted element's quotes or its backslash
|
|
58
|
-
* escapes: an unquoted `NULL` is the null value and a quoted `"NULL"` is the four-character
|
|
59
|
-
* string, which is the one distinction the quoting exists to carry.
|
|
60
|
-
*/
|
|
61
|
-
export function parsePgArray<T>(text: string, decode: (raw: string) => T): PgArray<T> | null {
|
|
62
|
-
const scan: Scan = { text, at: 0 };
|
|
63
|
-
const parsed = readArray(scan, decode);
|
|
64
|
-
// Trailing content means the literal was not what this grammar accepted, whatever it parsed.
|
|
65
|
-
return parsed === null || scan.at !== text.length ? null : parsed;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** A member is a decoded value, the null member, or — for a multidimensional array — a row of them. */
|
|
69
|
-
export type PgArray<T> = (T | null | PgArray<T>)[];
|
|
70
|
-
|
|
71
|
-
function readArray<T>(scan: Scan, decode: (raw: string) => T): PgArray<T> | null {
|
|
72
|
-
if (scan.text[scan.at] !== '{') return null;
|
|
73
|
-
scan.at += 1;
|
|
74
|
-
const out: PgArray<T> = [];
|
|
75
|
-
if (scan.text[scan.at] === '}') {
|
|
76
|
-
scan.at += 1;
|
|
77
|
-
return out;
|
|
78
|
-
}
|
|
79
|
-
for (;;) {
|
|
80
|
-
const member = readMember(scan, decode);
|
|
81
|
-
if (member === FAILED) return null;
|
|
82
|
-
out.push(member);
|
|
83
|
-
const next = scan.text[scan.at];
|
|
84
|
-
scan.at += 1;
|
|
85
|
-
if (next === '}') return out;
|
|
86
|
-
if (next !== ',') return null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** A sentinel, because `null` is a legal member and `undefined` would be a second spelling of it. */
|
|
91
|
-
const FAILED = Symbol('pg-array-failed');
|
|
92
|
-
|
|
93
|
-
function readMember<T>(
|
|
94
|
-
scan: Scan,
|
|
95
|
-
decode: (raw: string) => T,
|
|
96
|
-
): T | null | PgArray<T> | typeof FAILED {
|
|
97
|
-
const head = scan.text[scan.at];
|
|
98
|
-
if (head === '{') {
|
|
99
|
-
const nested = readArray(scan, decode);
|
|
100
|
-
return nested === null ? FAILED : nested;
|
|
101
|
-
}
|
|
102
|
-
if (head === '"') {
|
|
103
|
-
const quoted = readQuoted(scan);
|
|
104
|
-
return quoted === null ? FAILED : decode(quoted);
|
|
105
|
-
}
|
|
106
|
-
const start = scan.at;
|
|
107
|
-
while (scan.at < scan.text.length) {
|
|
108
|
-
const char = scan.text[scan.at];
|
|
109
|
-
if (char === ',' || char === '}') break;
|
|
110
|
-
// A brace or a quote inside a bare element is a literal this grammar does not describe.
|
|
111
|
-
if (char === '{' || char === '"') return FAILED;
|
|
112
|
-
scan.at += 1;
|
|
113
|
-
}
|
|
114
|
-
if (scan.at === scan.text.length) return FAILED;
|
|
115
|
-
const raw = scan.text.slice(start, scan.at);
|
|
116
|
-
// Unquoted and case-insensitive is the ONLY spelling of the null member; `"NULL"` is a string.
|
|
117
|
-
return raw.toUpperCase() === 'NULL' ? null : decode(raw);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** The text between one pair of quotes, with `\\` and `\"` unescaped. `null` if it never closes. */
|
|
121
|
-
function readQuoted(scan: Scan): string | null {
|
|
122
|
-
scan.at += 1;
|
|
123
|
-
let out = '';
|
|
124
|
-
while (scan.at < scan.text.length) {
|
|
125
|
-
const char = scan.text[scan.at];
|
|
126
|
-
if (char === '"') {
|
|
127
|
-
scan.at += 1;
|
|
128
|
-
return out;
|
|
129
|
-
}
|
|
130
|
-
if (char === '\\') {
|
|
131
|
-
const escaped = scan.text[scan.at + 1];
|
|
132
|
-
if (escaped === undefined) return null;
|
|
133
|
-
out += escaped;
|
|
134
|
-
scan.at += 2;
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
out += char ?? '';
|
|
138
|
-
scan.at += 1;
|
|
139
|
-
}
|
|
140
|
-
return null;
|
|
141
|
-
}
|
package/src/pg-values.ts
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
// in the component the first patch reached.
|
|
12
12
|
|
|
13
13
|
import { renderThrowable } from '@ultimat3/core';
|
|
14
|
+
import { parsePgArray } from '@ultimat3/entity';
|
|
14
15
|
import { ReplicationProtocolError } from './errors';
|
|
15
16
|
import type { JsonValue } from './json';
|
|
16
|
-
import { arrayElementOid
|
|
17
|
+
import { arrayElementOid } from './pg-array';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* What a row value can be between the WAL and the wire: JSON, plus the two JS shapes a repository
|