@carno.js/live 1.8.0 → 1.8.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/LiveEngine.d.ts +14 -0
- package/dist/LiveEngine.js +73 -8
- package/dist/LivePlugin.d.ts +9 -0
- package/dist/LivePlugin.js +12 -0
- package/dist/config.d.ts +12 -1
- package/dist/config.js +1 -0
- package/dist/graph/DependencyGraph.d.ts +25 -0
- package/dist/graph/DependencyGraph.js +65 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/dist/resource/ResourceRegistry.d.ts +3 -0
- package/dist/resource/ResourceRegistry.js +6 -0
- package/dist/scope-warning.d.ts +28 -0
- package/dist/scope-warning.js +48 -0
- package/dist/shared/canonical.d.ts +24 -0
- package/dist/shared/canonical.js +209 -19
- package/package.json +2 -2
- package/src/LiveEngine.ts +806 -730
- package/src/LivePlugin.ts +276 -253
- package/src/config.ts +61 -49
- package/src/graph/DependencyGraph.ts +223 -147
- package/src/index.ts +83 -81
- package/src/resource/ResourceRegistry.ts +185 -178
- package/src/scope-warning.ts +58 -0
- package/src/shared/canonical.ts +301 -63
- package/test/canonical-equivalence.test.ts +290 -0
- package/test/dependency-graph-index.test.ts +206 -0
- package/test/recompute-concurrency.test.ts +183 -0
- package/test/scope-warning.test.ts +157 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/config.ts
CHANGED
|
@@ -1,49 +1,61 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tunables from §10.1 of the design. These are starting points to calibrate
|
|
3
|
-
* against the recompute-without-patch metric, not measured values.
|
|
4
|
-
*/
|
|
5
|
-
export interface LiveConfig {
|
|
6
|
-
/** Window in ms over which invalidations for one instance are grouped. */
|
|
7
|
-
coalesceMs: number;
|
|
8
|
-
/** Above this many row keys, one read collapses to its table key. */
|
|
9
|
-
maxKeysPerRead: number;
|
|
10
|
-
/** Ceiling on the canonicalized inputs of a single subscription. */
|
|
11
|
-
maxInputBytes: number;
|
|
12
|
-
/** Grace period before dropping an instance whose refcount hit zero. */
|
|
13
|
-
unsubGraceMs: number;
|
|
14
|
-
/** Consecutive back-pressured sends before collapsing to a snapshot. */
|
|
15
|
-
maxPendingPatches: number;
|
|
16
|
-
/**
|
|
17
|
-
fanoutQueueThreshold: number;
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Tunables from §10.1 of the design. These are starting points to calibrate
|
|
3
|
+
* against the recompute-without-patch metric, not measured values.
|
|
4
|
+
*/
|
|
5
|
+
export interface LiveConfig {
|
|
6
|
+
/** Window in ms over which invalidations for one instance are grouped. */
|
|
7
|
+
coalesceMs: number;
|
|
8
|
+
/** Above this many row keys, one read collapses to its table key. */
|
|
9
|
+
maxKeysPerRead: number;
|
|
10
|
+
/** Ceiling on the canonicalized inputs of a single subscription. */
|
|
11
|
+
maxInputBytes: number;
|
|
12
|
+
/** Grace period before dropping an instance whose refcount hit zero. */
|
|
13
|
+
unsubGraceMs: number;
|
|
14
|
+
/** Consecutive back-pressured sends before collapsing to a snapshot. */
|
|
15
|
+
maxPendingPatches: number;
|
|
16
|
+
/** Recomputes finished in one run before the loop is yielded back. */
|
|
17
|
+
fanoutQueueThreshold: number;
|
|
18
|
+
/**
|
|
19
|
+
* Recomputes allowed to run at once, across every path.
|
|
20
|
+
*
|
|
21
|
+
* Each one executes the resource's route, and so its queries. Raising it
|
|
22
|
+
* past the database pool buys no parallelism -- the driver queues the
|
|
23
|
+
* excess either way -- it only puts live queries in front of the ordinary
|
|
24
|
+
* HTTP requests competing for the same connections. The default sits well
|
|
25
|
+
* under Bun's pool default of 10; raise it with the pool, not with the
|
|
26
|
+
* fan-out.
|
|
27
|
+
*/
|
|
28
|
+
maxConcurrentRecomputes: number;
|
|
29
|
+
/** Ceiling on live instances held by a single connection. */
|
|
30
|
+
maxInstancesPerConnection: number;
|
|
31
|
+
/** Ceiling on live instances held by this process. */
|
|
32
|
+
maxInstancesPerNode: number;
|
|
33
|
+
/** Path of the SSE downstream, when the SSE transport is on. */
|
|
34
|
+
ssePath: string;
|
|
35
|
+
/** Path client messages are posted to, when the SSE transport is on. */
|
|
36
|
+
sseControlPath: string;
|
|
37
|
+
/** Comment frame interval that keeps idle-timeout proxies from reaping. */
|
|
38
|
+
sseHeartbeatMs: number;
|
|
39
|
+
/** Ceiling on concurrent SSE streams held by this process. */
|
|
40
|
+
sseMaxConnections: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const DEFAULT_LIVE_CONFIG: LiveConfig = {
|
|
44
|
+
coalesceMs: 16,
|
|
45
|
+
maxKeysPerRead: 64,
|
|
46
|
+
maxInputBytes: 8192,
|
|
47
|
+
unsubGraceMs: 5000,
|
|
48
|
+
maxPendingPatches: 32,
|
|
49
|
+
fanoutQueueThreshold: 500,
|
|
50
|
+
maxConcurrentRecomputes: 4,
|
|
51
|
+
maxInstancesPerConnection: 64,
|
|
52
|
+
maxInstancesPerNode: 50000,
|
|
53
|
+
ssePath: '/live/sse',
|
|
54
|
+
sseControlPath: '/live/control',
|
|
55
|
+
sseHeartbeatMs: 15000,
|
|
56
|
+
sseMaxConnections: 10000
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export function resolveLiveConfig(overrides: Partial<LiveConfig> = {}): LiveConfig {
|
|
60
|
+
return { ...DEFAULT_LIVE_CONFIG, ...overrides };
|
|
61
|
+
}
|
|
@@ -1,147 +1,223 @@
|
|
|
1
|
-
import { ancestorsOf, type DepKey } from './dep-key';
|
|
2
|
-
import type { Dependency, InvalidationEvent } from './types';
|
|
3
|
-
|
|
4
|
-
/** Column sets registered per instance under one key. null means wildcard. */
|
|
5
|
-
type ColumnSet = Set<string> | null;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Key ↔ instance index with ancestor resolution and column filtering.
|
|
9
|
-
*
|
|
10
|
-
* Knows nothing about WebSocket, the ORM, or resources — it is a pure data
|
|
11
|
-
* structure, which is why the hard part of invalidation is testable without
|
|
12
|
-
* a server, a database or a socket.
|
|
13
|
-
*/
|
|
14
|
-
export class DependencyGraph {
|
|
15
|
-
private readonly byKey = new Map<DepKey, Map<string, ColumnSet>>();
|
|
16
|
-
private readonly byInstance = new Map<string, Set<DepKey>>();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
1
|
+
import { ancestorsOf, type DepKey } from './dep-key';
|
|
2
|
+
import type { Dependency, InvalidationEvent } from './types';
|
|
3
|
+
|
|
4
|
+
/** Column sets registered per instance under one key. null means wildcard. */
|
|
5
|
+
type ColumnSet = Set<string> | null;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Key ↔ instance index with ancestor resolution and column filtering.
|
|
9
|
+
*
|
|
10
|
+
* Knows nothing about WebSocket, the ORM, or resources — it is a pure data
|
|
11
|
+
* structure, which is why the hard part of invalidation is testable without
|
|
12
|
+
* a server, a database or a socket.
|
|
13
|
+
*/
|
|
14
|
+
export class DependencyGraph {
|
|
15
|
+
private readonly byKey = new Map<DepKey, Map<string, ColumnSet>>();
|
|
16
|
+
private readonly byInstance = new Map<string, Set<DepKey>>();
|
|
17
|
+
/**
|
|
18
|
+
* Registered row keys grouped by the key that contains them, so a write
|
|
19
|
+
* that names a whole table finds its rows instead of being searched for.
|
|
20
|
+
*
|
|
21
|
+
* Without it, resolving a table event means walking every key in the
|
|
22
|
+
* graph: at the configured `maxInstancesPerNode` that is a five-millisecond
|
|
23
|
+
* scan, run synchronously once per event of the batch, with every HTTP
|
|
24
|
+
* request in the process waiting behind it. Table events are not the rare
|
|
25
|
+
* case either -- a write degrades to its table key unless its WHERE clause
|
|
26
|
+
* is a literal primary-key match.
|
|
27
|
+
*/
|
|
28
|
+
private readonly byParent = new Map<DepKey, Set<Map<string, ColumnSet>>>();
|
|
29
|
+
|
|
30
|
+
/** Replace every dependency held by this instance. */
|
|
31
|
+
setDependencies(instanceId: string, deps: Dependency[]): void {
|
|
32
|
+
this.remove(instanceId);
|
|
33
|
+
|
|
34
|
+
if (deps.length === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const keys = new Set<DepKey>();
|
|
39
|
+
|
|
40
|
+
for (const dep of deps) {
|
|
41
|
+
keys.add(dep.key);
|
|
42
|
+
|
|
43
|
+
let holders = this.byKey.get(dep.key);
|
|
44
|
+
if (!holders) {
|
|
45
|
+
holders = new Map<string, ColumnSet>();
|
|
46
|
+
this.byKey.set(dep.key, holders);
|
|
47
|
+
this.index(dep.key, holders);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!holders.has(instanceId)) {
|
|
51
|
+
holders.set(instanceId, dep.columns === null ? null : new Set(dep.columns));
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const existing = holders.get(instanceId)!;
|
|
56
|
+
|
|
57
|
+
if (existing === null) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (dep.columns === null) {
|
|
62
|
+
holders.set(instanceId, null);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const column of dep.columns) {
|
|
67
|
+
existing.add(column);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.byInstance.set(instanceId, keys);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Forget the instance entirely. */
|
|
75
|
+
remove(instanceId: string): void {
|
|
76
|
+
const keys = this.byInstance.get(instanceId);
|
|
77
|
+
|
|
78
|
+
if (!keys) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const key of keys) {
|
|
83
|
+
const holders = this.byKey.get(key);
|
|
84
|
+
|
|
85
|
+
if (!holders) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
holders.delete(instanceId);
|
|
90
|
+
|
|
91
|
+
if (holders.size === 0) {
|
|
92
|
+
this.byKey.delete(key);
|
|
93
|
+
this.unindex(key, holders);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.byInstance.delete(instanceId);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Instances concerned by this write.
|
|
102
|
+
*
|
|
103
|
+
* Both directions of the hierarchy matter. A row write wakes table
|
|
104
|
+
* subscribers, while a table write wakes row subscribers because a
|
|
105
|
+
* predicate write may have touched that row.
|
|
106
|
+
*/
|
|
107
|
+
resolve(event: InvalidationEvent): string[] {
|
|
108
|
+
const matched = new Set<string>();
|
|
109
|
+
|
|
110
|
+
for (const key of ancestorsOf(event.key)) {
|
|
111
|
+
this.collect(key, event.columns, matched);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// A key with a `#` is already a row: it has no descendants, and the
|
|
115
|
+
// ancestor pass above has covered its table.
|
|
116
|
+
if (!event.key.includes('#')) {
|
|
117
|
+
const rows = this.byParent.get(event.key);
|
|
118
|
+
|
|
119
|
+
if (rows) {
|
|
120
|
+
// Holder maps, not keys: the key would only be looked up again.
|
|
121
|
+
for (const holders of rows) {
|
|
122
|
+
this.collectFrom(holders, event.columns, matched);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return [...matched];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
keyCount(): number {
|
|
131
|
+
return this.byKey.size;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
instanceCount(): number {
|
|
135
|
+
return this.byInstance.size;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Keys that currently hold indexed rows. Zero when the graph is empty. */
|
|
139
|
+
parentCount(): number {
|
|
140
|
+
return this.byParent.size;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Record a row key's holders under the key that contains it.
|
|
145
|
+
*
|
|
146
|
+
* A table key has no separator and so no parent; it is reached directly.
|
|
147
|
+
* The holder map is stored rather than the key because it is what
|
|
148
|
+
* `resolve` actually needs, and because its identity is stable: it is
|
|
149
|
+
* created once in `setDependencies` and dropped only in `remove`.
|
|
150
|
+
*/
|
|
151
|
+
private index(key: DepKey, holders: Map<string, ColumnSet>): void {
|
|
152
|
+
const separator = key.indexOf('#');
|
|
153
|
+
|
|
154
|
+
if (separator === -1) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const parent = key.slice(0, separator);
|
|
159
|
+
let rows = this.byParent.get(parent);
|
|
160
|
+
|
|
161
|
+
if (!rows) {
|
|
162
|
+
rows = new Set<Map<string, ColumnSet>>();
|
|
163
|
+
this.byParent.set(parent, rows);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
rows.add(holders);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private unindex(key: DepKey, holders: Map<string, ColumnSet>): void {
|
|
170
|
+
const separator = key.indexOf('#');
|
|
171
|
+
|
|
172
|
+
if (separator === -1) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const parent = key.slice(0, separator);
|
|
177
|
+
const rows = this.byParent.get(parent);
|
|
178
|
+
|
|
179
|
+
if (!rows) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
rows.delete(holders);
|
|
184
|
+
|
|
185
|
+
if (rows.size === 0) {
|
|
186
|
+
this.byParent.delete(parent);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private collect(key: DepKey, writtenColumns: string[] | null, into: Set<string>): void {
|
|
191
|
+
const holders = this.byKey.get(key);
|
|
192
|
+
|
|
193
|
+
if (holders) {
|
|
194
|
+
this.collectFrom(holders, writtenColumns, into);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private collectFrom(
|
|
199
|
+
holders: Map<string, ColumnSet>,
|
|
200
|
+
writtenColumns: string[] | null,
|
|
201
|
+
into: Set<string>
|
|
202
|
+
): void {
|
|
203
|
+
for (const [instanceId, readColumns] of holders) {
|
|
204
|
+
if (intersects(readColumns, writtenColumns)) {
|
|
205
|
+
into.add(instanceId);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function intersects(readColumns: Set<string> | null, writtenColumns: string[] | null): boolean {
|
|
212
|
+
if (readColumns === null || writtenColumns === null) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
for (const column of writtenColumns) {
|
|
217
|
+
if (readColumns.has(column)) {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return false;
|
|
223
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,81 +1,83 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
|
-
|
|
3
|
-
// Decorator and metadata
|
|
4
|
-
export { Live } from './decorators/Live';
|
|
5
|
-
export { LIVE_META } from './metadata';
|
|
6
|
-
export type { LiveMeta, LiveOptions, LiveShared } from './metadata';
|
|
7
|
-
|
|
8
|
-
// Plugin and services
|
|
9
|
-
export { LivePlugin } from './LivePlugin';
|
|
10
|
-
export type { LivePluginOptions } from './LivePlugin';
|
|
11
|
-
export { closeLiveRuntime } from './runtime';
|
|
12
|
-
export { LiveService } from './LiveService';
|
|
13
|
-
export { LiveEngine } from './LiveEngine';
|
|
14
|
-
export type { LiveTransport, LiveStats } from './LiveEngine';
|
|
15
|
-
|
|
16
|
-
// Metrics
|
|
17
|
-
export { LiveMetrics } from './observability';
|
|
18
|
-
export type { MetricSink } from './observability';
|
|
19
|
-
|
|
20
|
-
// Configuration
|
|
21
|
-
export { DEFAULT_LIVE_CONFIG, resolveLiveConfig } from './config';
|
|
22
|
-
export type { LiveConfig } from './config';
|
|
23
|
-
|
|
24
|
-
// Scope
|
|
25
|
-
export { ConnectionScopeResolver } from './transport/scope-resolver';
|
|
26
|
-
export type { LiveHandshake, LiveScopeResolver } from './transport/scope-resolver';
|
|
27
|
-
export type { LiveExecutionContext, LiveInputs, LiveResourceExecutor, LiveScope } from './resource/types';
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export
|
|
37
|
-
export {
|
|
38
|
-
export type {
|
|
39
|
-
export {
|
|
40
|
-
export {
|
|
41
|
-
export
|
|
42
|
-
export {
|
|
43
|
-
export type {
|
|
44
|
-
export {
|
|
45
|
-
export type {
|
|
46
|
-
export {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
export
|
|
52
|
-
export {
|
|
53
|
-
export {
|
|
54
|
-
export {
|
|
55
|
-
export
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
export {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export {
|
|
69
|
-
export type {
|
|
70
|
-
export {
|
|
71
|
-
export type {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
export {
|
|
81
|
-
export {
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
|
|
3
|
+
// Decorator and metadata
|
|
4
|
+
export { Live } from './decorators/Live';
|
|
5
|
+
export { LIVE_META } from './metadata';
|
|
6
|
+
export type { LiveMeta, LiveOptions, LiveShared } from './metadata';
|
|
7
|
+
|
|
8
|
+
// Plugin and services
|
|
9
|
+
export { LivePlugin } from './LivePlugin';
|
|
10
|
+
export type { LivePluginOptions } from './LivePlugin';
|
|
11
|
+
export { closeLiveRuntime } from './runtime';
|
|
12
|
+
export { LiveService } from './LiveService';
|
|
13
|
+
export { LiveEngine } from './LiveEngine';
|
|
14
|
+
export type { LiveTransport, LiveStats } from './LiveEngine';
|
|
15
|
+
|
|
16
|
+
// Metrics
|
|
17
|
+
export { LiveMetrics } from './observability';
|
|
18
|
+
export type { MetricSink } from './observability';
|
|
19
|
+
|
|
20
|
+
// Configuration
|
|
21
|
+
export { DEFAULT_LIVE_CONFIG, resolveLiveConfig } from './config';
|
|
22
|
+
export type { LiveConfig } from './config';
|
|
23
|
+
|
|
24
|
+
// Scope
|
|
25
|
+
export { ConnectionScopeResolver } from './transport/scope-resolver';
|
|
26
|
+
export type { LiveHandshake, LiveScopeResolver } from './transport/scope-resolver';
|
|
27
|
+
export type { LiveExecutionContext, LiveInputs, LiveResourceExecutor, LiveScope } from './resource/types';
|
|
28
|
+
export { defaultScopeWarning } from './scope-warning';
|
|
29
|
+
export type { DefaultScopeWarningInput } from './scope-warning';
|
|
30
|
+
|
|
31
|
+
// Authorization
|
|
32
|
+
export { AllowAllAuthorizer, authKeysOf, isAuthKey } from './auth/authorizer';
|
|
33
|
+
export type { LiveAuthorizationRequest, LiveAuthorizer } from './auth/authorizer';
|
|
34
|
+
|
|
35
|
+
// Invalidation
|
|
36
|
+
export { InProcessBus } from './bus/InProcessBus';
|
|
37
|
+
export type { InvalidationBus, InvalidationHandler } from './bus/InvalidationBus';
|
|
38
|
+
export type { Dependency, InvalidationEvent } from './graph/types';
|
|
39
|
+
export { ancestorsOf, rowKey, tableKey } from './graph/dep-key';
|
|
40
|
+
export type { DepKey } from './graph/dep-key';
|
|
41
|
+
export { WriteDuringComputeError } from './emitters/AppEmitter';
|
|
42
|
+
export { PgNotifyBus, chunkEvents } from './bus/PgNotifyBus';
|
|
43
|
+
export type { PgNotifyBusOptions } from './bus/PgNotifyBus';
|
|
44
|
+
export { PgNotifyEmitter, eventsFromPayload } from './emitters/pg-notify-emitter';
|
|
45
|
+
export type { PgNotifyEmitterOptions, PgNotifyTable } from './emitters/pg-notify-emitter';
|
|
46
|
+
export { PgListener } from './emitters/pg-listener';
|
|
47
|
+
export type { ListenableSql, PgListenerOptions } from './emitters/pg-listener';
|
|
48
|
+
export { tableOfKey } from './graph/dep-key';
|
|
49
|
+
|
|
50
|
+
// Protocol and patches, shared with the client
|
|
51
|
+
export * from './shared/protocol';
|
|
52
|
+
export type { PatchOp, PathSegment } from './patch/types';
|
|
53
|
+
export { PatchEngine } from './patch/PatchEngine';
|
|
54
|
+
export { canonical, NonSerializableInputError } from './shared/canonical';
|
|
55
|
+
export { fnv1a64 } from './shared/hash';
|
|
56
|
+
export { normalizeLiveInputs, resourceIdOf } from './shared/descriptor';
|
|
57
|
+
export type { LiveDataOf, LiveDescriptor, LiveInputsOf } from './shared/descriptor';
|
|
58
|
+
export type { OptimisticEntry, OptimisticList } from './client/optimistic';
|
|
59
|
+
|
|
60
|
+
// Framework-free client adapter
|
|
61
|
+
export { liveStore, liveStoreOf, liveIdentity, LiveSlot } from './client/vanilla';
|
|
62
|
+
export type { LiveHandle } from './client/vanilla';
|
|
63
|
+
|
|
64
|
+
export { WebSocketTransport, LadderTransport, PollingTransport, SseClientTransport, routeIndex } from './client/transport';
|
|
65
|
+
export type { ClientTransport, TransportHandlers, EventSourceLike, RoutePath } from './client/transport';
|
|
66
|
+
|
|
67
|
+
// Transports
|
|
68
|
+
export { FanTransport } from './transport/FanTransport';
|
|
69
|
+
export type { OwnedTransport } from './transport/FanTransport';
|
|
70
|
+
export { SseTransport } from './transport/SseTransport';
|
|
71
|
+
export type { SseTransportOptions } from './transport/SseTransport';
|
|
72
|
+
export { createSseRoutes } from './transport/sse-routes';
|
|
73
|
+
export type { SseRouteOptions } from './transport/sse-routes';
|
|
74
|
+
|
|
75
|
+
// Conditional GET
|
|
76
|
+
export { LiveETagMiddleware, pathMatcher } from './http/etag';
|
|
77
|
+
export type { LivePollingGuard, LivePollingRequest, LiveRoutePath } from './http/etag';
|
|
78
|
+
|
|
79
|
+
// First paint
|
|
80
|
+
export { prefetchLive } from './resource/prefetch';
|
|
81
|
+
export type { LivePayload } from './resource/prefetch';
|
|
82
|
+
export { LiveRouteExecutionError } from './resource/route-executor';
|
|
83
|
+
export { hydrationKey, toHydrateMap, readHydrationPayload, HYDRATION_ATTRIBUTE } from './client/hydrate';
|