@syncular/client 0.4.1 → 0.5.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/README.md +4 -4
- package/dist/apply.d.ts +5 -1
- package/dist/apply.js +6 -4
- package/dist/client.d.ts +49 -2
- package/dist/client.js +527 -259
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/invalidation.d.ts +81 -48
- package/dist/invalidation.js +130 -42
- package/dist/reactive-store.d.ts +74 -0
- package/dist/reactive-store.js +576 -0
- package/dist/schema.js +1 -0
- package/dist/state.d.ts +9 -0
- package/dist/state.js +29 -0
- package/dist/window.d.ts +6 -1
- package/dist/window.js +0 -0
- package/dist/worker-entry.js +78 -52
- package/dist/worker-host.d.ts +8 -4
- package/dist/worker-host.js +26 -6
- package/dist/worker-protocol.d.ts +12 -14
- package/package.json +3 -3
- package/src/apply.ts +18 -5
- package/src/client.ts +685 -311
- package/src/index.ts +1 -0
- package/src/invalidation.ts +216 -62
- package/src/reactive-store.ts +695 -0
- package/src/schema.ts +3 -0
- package/src/state.ts +32 -0
- package/src/window.ts +0 -0
- package/src/worker-entry.ts +83 -54
- package/src/worker-host.ts +44 -8
- package/src/worker-protocol.ts +20 -13
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export * from './multi-tab.js';
|
|
|
23
23
|
export * from './naming.js';
|
|
24
24
|
export * from './outbox.js';
|
|
25
25
|
export * from './query-guard.js';
|
|
26
|
+
export * from './reactive-store.js';
|
|
26
27
|
export * from './schema.js';
|
|
27
28
|
export * from './sql-tag.js';
|
|
28
29
|
export * from './state.js';
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export * from './multi-tab.js';
|
|
|
23
23
|
export * from './naming.js';
|
|
24
24
|
export * from './outbox.js';
|
|
25
25
|
export * from './query-guard.js';
|
|
26
|
+
export * from './reactive-store.js';
|
|
26
27
|
export * from './schema.js';
|
|
27
28
|
export * from './sql-tag.js';
|
|
28
29
|
export * from './state.js';
|
package/dist/invalidation.d.ts
CHANGED
|
@@ -1,69 +1,102 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* I1–I4). Every local mutation — `COMMIT` apply, segment apply (rows +
|
|
4
|
-
* sqlite images), optimistic overlay rebuild, revocation purge, schema-bump
|
|
5
|
-
* reset, and (future) window eviction — routes its touched keys through a
|
|
6
|
-
* single {@link Invalidation} accumulator, and the client emits exactly ONE
|
|
7
|
-
* {@link InvalidationEvent} per apply batch (never per row). Live queries
|
|
8
|
-
* subscribe via `SyncClient.onInvalidate` and re-run only when a table they
|
|
9
|
-
* depend on appears.
|
|
2
|
+
* Revisioned client-local observation events (SPEC §7.5 / RFC 0003).
|
|
10
3
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - Segments carry only a table + `scopeDigest`, NOT per-row scope keys, so
|
|
15
|
-
* a segment apply invalidates at the **table** granularity plus the
|
|
16
|
-
* subscription's requested/effective scope keys (the coarsest honest key
|
|
17
|
-
* the wire supports for bulk data).
|
|
18
|
-
* - Purge / reset / optimistic / eviction are keyed by table (and effective
|
|
19
|
-
* scope keys where a scope map is in hand).
|
|
20
|
-
*
|
|
21
|
-
* `tables` is therefore always the reliable floor; `scopeKeys` is a
|
|
22
|
-
* best-effort refinement present where the source carried it. A live query
|
|
23
|
-
* that cannot express its scope footprint keys off `tables` alone.
|
|
4
|
+
* The core records observer domains while it owns the SQLite transaction,
|
|
5
|
+
* increments the persisted local revision in that same transaction, and emits
|
|
6
|
+
* the frozen batch only after commit. Bridges forward this shape verbatim.
|
|
24
7
|
*/
|
|
25
8
|
import type { ScopeMap } from '@syncular/core';
|
|
9
|
+
import type { LeaseState, SchemaFloor } from './client.js';
|
|
26
10
|
import type { CompiledClientTable } from './schema.js';
|
|
27
|
-
|
|
28
|
-
export interface
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
readonly scopeKeys: ReadonlySet<string>;
|
|
11
|
+
export type LocalRevision = bigint;
|
|
12
|
+
export interface TableChange {
|
|
13
|
+
readonly table: string;
|
|
14
|
+
/** Undefined means honestly table-wide; an empty set is never emitted. */
|
|
15
|
+
readonly scopeKeys?: ReadonlySet<string>;
|
|
33
16
|
}
|
|
34
|
-
export
|
|
17
|
+
export interface WindowChange {
|
|
18
|
+
readonly baseKey: string;
|
|
19
|
+
readonly table: string;
|
|
20
|
+
readonly units: ReadonlySet<string>;
|
|
21
|
+
}
|
|
22
|
+
export interface SyncStatusSnapshot {
|
|
23
|
+
readonly outbox: number;
|
|
24
|
+
readonly upgrading: boolean;
|
|
25
|
+
readonly leaseState: LeaseState | undefined;
|
|
26
|
+
readonly schemaFloor: SchemaFloor | undefined;
|
|
27
|
+
readonly syncNeeded: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface ClientChangeBatch {
|
|
30
|
+
readonly revision: LocalRevision;
|
|
31
|
+
readonly tables: readonly TableChange[];
|
|
32
|
+
readonly windows: readonly WindowChange[];
|
|
33
|
+
readonly status?: SyncStatusSnapshot;
|
|
34
|
+
readonly conflictsChanged: boolean;
|
|
35
|
+
readonly rejectionsChanged: boolean;
|
|
36
|
+
}
|
|
37
|
+
export type ClientChangeListener = (batch: ClientChangeBatch) => void;
|
|
38
|
+
/** Network work created by a core command (SPEC §7.5). */
|
|
39
|
+
export type SyncIntent = {
|
|
40
|
+
readonly kind: 'none';
|
|
41
|
+
} | {
|
|
42
|
+
readonly kind: 'interactive';
|
|
43
|
+
} | {
|
|
44
|
+
readonly kind: 'background';
|
|
45
|
+
readonly delayMs: number;
|
|
46
|
+
};
|
|
47
|
+
export interface CommandEffects {
|
|
48
|
+
readonly sync: SyncIntent;
|
|
49
|
+
}
|
|
50
|
+
export interface CommandResult<T> {
|
|
51
|
+
readonly value: T;
|
|
52
|
+
readonly effects: CommandEffects;
|
|
53
|
+
}
|
|
54
|
+
export declare const NO_COMMAND_EFFECTS: CommandEffects;
|
|
35
55
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* and cheap on purpose — no allocation until something is actually touched.
|
|
56
|
+
* Per-observer-transaction accumulator. It contains no revision itself: the
|
|
57
|
+
* transaction wrapper assigns the revision only after all writes succeeded.
|
|
39
58
|
*/
|
|
40
|
-
export declare class
|
|
59
|
+
export declare class ChangeAccumulator {
|
|
41
60
|
#private;
|
|
42
|
-
/** Mark a whole table dirty
|
|
61
|
+
/** Mark a whole table dirty, discarding any weaker scope-only facts. */
|
|
43
62
|
table(name: string): void;
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
/** Associate a precise `prefix:value` key with its table. */
|
|
64
|
+
scope(table: string, key: string): void;
|
|
65
|
+
/** Record registration/completeness change for one window unit. */
|
|
66
|
+
window(baseKey: string, table: string, unit: string): void;
|
|
67
|
+
status(): void;
|
|
68
|
+
conflicts(): void;
|
|
69
|
+
rejections(): void;
|
|
70
|
+
/** Add precise keys for a requested/effective scope map. */
|
|
51
71
|
scopeMap(table: CompiledClientTable, scopes: ScopeMap): void;
|
|
52
|
-
/**
|
|
53
|
-
changeScopes(table: CompiledClientTable, scopes: Record<string, string
|
|
72
|
+
/** Add precise keys for a COMMIT change's stored scope values. */
|
|
73
|
+
changeScopes(table: CompiledClientTable, scopes: Readonly<Record<string, string>>): void;
|
|
54
74
|
get touched(): boolean;
|
|
55
|
-
|
|
56
|
-
finish(
|
|
75
|
+
get statusChanged(): boolean;
|
|
76
|
+
finish(revision: LocalRevision, status: SyncStatusSnapshot | undefined): ClientChangeBatch;
|
|
77
|
+
}
|
|
78
|
+
/** Exception-isolated synchronous change listener set. */
|
|
79
|
+
export declare class ChangeEmitter {
|
|
80
|
+
#private;
|
|
81
|
+
on(listener: ClientChangeListener): () => void;
|
|
82
|
+
emit(batch: ClientChangeBatch): void;
|
|
83
|
+
get size(): number;
|
|
57
84
|
}
|
|
58
85
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* never corrupts the emitting apply path). Reused by the worker handle so
|
|
62
|
-
* both cores expose the identical `onInvalidate` surface.
|
|
86
|
+
* Legacy invalidation shape. It is derived only from an exact core batch;
|
|
87
|
+
* missing bridge information is never treated as global.
|
|
63
88
|
*/
|
|
89
|
+
export interface InvalidationEvent {
|
|
90
|
+
readonly tables: ReadonlySet<string>;
|
|
91
|
+
readonly scopeKeys: ReadonlySet<string>;
|
|
92
|
+
}
|
|
93
|
+
export type InvalidationListener = (event: InvalidationEvent) => void;
|
|
94
|
+
export declare function invalidationFromChange(batch: ClientChangeBatch): InvalidationEvent | undefined;
|
|
64
95
|
export declare class InvalidationEmitter {
|
|
65
96
|
#private;
|
|
66
97
|
on(listener: InvalidationListener): () => void;
|
|
67
98
|
emit(event: InvalidationEvent): void;
|
|
68
99
|
get size(): number;
|
|
69
100
|
}
|
|
101
|
+
/** @deprecated Use {@link ChangeAccumulator}. */
|
|
102
|
+
export { ChangeAccumulator as Invalidation };
|
package/dist/invalidation.js
CHANGED
|
@@ -1,65 +1,151 @@
|
|
|
1
|
+
export const NO_COMMAND_EFFECTS = {
|
|
2
|
+
sync: { kind: 'none' },
|
|
3
|
+
};
|
|
1
4
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* and cheap on purpose — no allocation until something is actually touched.
|
|
5
|
+
* Per-observer-transaction accumulator. It contains no revision itself: the
|
|
6
|
+
* transaction wrapper assigns the revision only after all writes succeeded.
|
|
5
7
|
*/
|
|
6
|
-
export class
|
|
7
|
-
#tables;
|
|
8
|
-
#
|
|
9
|
-
|
|
8
|
+
export class ChangeAccumulator {
|
|
9
|
+
#tables = new Map();
|
|
10
|
+
#windows = new Map();
|
|
11
|
+
#status = false;
|
|
12
|
+
#conflicts = false;
|
|
13
|
+
#rejections = false;
|
|
14
|
+
/** Mark a whole table dirty, discarding any weaker scope-only facts. */
|
|
10
15
|
table(name) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
this.#tables.set(name, { tableWide: true, scopeKeys: undefined });
|
|
17
|
+
}
|
|
18
|
+
/** Associate a precise `prefix:value` key with its table. */
|
|
19
|
+
scope(table, key) {
|
|
20
|
+
const current = this.#tables.get(table);
|
|
21
|
+
if (current?.tableWide)
|
|
22
|
+
return;
|
|
23
|
+
if (current === undefined) {
|
|
24
|
+
this.#tables.set(table, {
|
|
25
|
+
tableWide: false,
|
|
26
|
+
scopeKeys: new Set([key]),
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
current.scopeKeys?.add(key);
|
|
31
|
+
}
|
|
32
|
+
/** Record registration/completeness change for one window unit. */
|
|
33
|
+
window(baseKey, table, unit) {
|
|
34
|
+
const current = this.#windows.get(baseKey);
|
|
35
|
+
if (current === undefined) {
|
|
36
|
+
this.#windows.set(baseKey, { table, units: new Set([unit]) });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (current.table !== table) {
|
|
40
|
+
throw new Error(`window base ${JSON.stringify(baseKey)} changed tables`);
|
|
41
|
+
}
|
|
42
|
+
current.units.add(unit);
|
|
43
|
+
}
|
|
44
|
+
status() {
|
|
45
|
+
this.#status = true;
|
|
46
|
+
}
|
|
47
|
+
conflicts() {
|
|
48
|
+
this.#conflicts = true;
|
|
49
|
+
}
|
|
50
|
+
rejections() {
|
|
51
|
+
this.#rejections = true;
|
|
52
|
+
}
|
|
53
|
+
/** Add precise keys for a requested/effective scope map. */
|
|
26
54
|
scopeMap(table, scopes) {
|
|
27
55
|
for (const [variable, values] of Object.entries(scopes)) {
|
|
28
56
|
const prefix = table.scopePrefixByVariable.get(variable);
|
|
29
57
|
if (prefix === undefined)
|
|
30
58
|
continue;
|
|
31
|
-
for (const
|
|
32
|
-
this.
|
|
59
|
+
for (const value of values)
|
|
60
|
+
this.scope(table.name, `${prefix}:${value}`);
|
|
33
61
|
}
|
|
34
62
|
}
|
|
35
|
-
/**
|
|
63
|
+
/** Add precise keys for a COMMIT change's stored scope values. */
|
|
36
64
|
changeScopes(table, scopes) {
|
|
37
65
|
for (const [variable, value] of Object.entries(scopes)) {
|
|
38
66
|
const prefix = table.scopePrefixByVariable.get(variable);
|
|
39
|
-
if (prefix !== undefined)
|
|
40
|
-
this.
|
|
67
|
+
if (prefix !== undefined) {
|
|
68
|
+
this.scope(table.name, `${prefix}:${value}`);
|
|
69
|
+
}
|
|
41
70
|
}
|
|
42
71
|
}
|
|
43
72
|
get touched() {
|
|
44
|
-
return this.#tables
|
|
73
|
+
return (this.#tables.size > 0 ||
|
|
74
|
+
this.#windows.size > 0 ||
|
|
75
|
+
this.#status ||
|
|
76
|
+
this.#conflicts ||
|
|
77
|
+
this.#rejections);
|
|
78
|
+
}
|
|
79
|
+
get statusChanged() {
|
|
80
|
+
return this.#status;
|
|
45
81
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
82
|
+
finish(revision, status) {
|
|
83
|
+
if (this.#status && status === undefined) {
|
|
84
|
+
throw new Error('status change batch requires a post-commit snapshot');
|
|
85
|
+
}
|
|
86
|
+
const tables = [];
|
|
87
|
+
for (const [table, change] of this.#tables) {
|
|
88
|
+
tables.push(change.tableWide
|
|
89
|
+
? { table }
|
|
90
|
+
: { table, scopeKeys: change.scopeKeys });
|
|
91
|
+
}
|
|
92
|
+
const windows = [];
|
|
93
|
+
for (const [baseKey, change] of this.#windows) {
|
|
94
|
+
windows.push({
|
|
95
|
+
baseKey,
|
|
96
|
+
table: change.table,
|
|
97
|
+
units: change.units,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
50
100
|
return {
|
|
51
|
-
|
|
52
|
-
|
|
101
|
+
revision,
|
|
102
|
+
tables,
|
|
103
|
+
windows,
|
|
104
|
+
...(this.#status ? { status: status } : {}),
|
|
105
|
+
conflictsChanged: this.#conflicts,
|
|
106
|
+
rejectionsChanged: this.#rejections,
|
|
53
107
|
};
|
|
54
108
|
}
|
|
55
109
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
110
|
+
/** Exception-isolated synchronous change listener set. */
|
|
111
|
+
export class ChangeEmitter {
|
|
112
|
+
#listeners = new Set();
|
|
113
|
+
on(listener) {
|
|
114
|
+
this.#listeners.add(listener);
|
|
115
|
+
return () => {
|
|
116
|
+
this.#listeners.delete(listener);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
emit(batch) {
|
|
120
|
+
for (const listener of this.#listeners) {
|
|
121
|
+
try {
|
|
122
|
+
listener(batch);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Observer code must never corrupt the committed core path.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
get size() {
|
|
130
|
+
return this.#listeners.size;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function invalidationFromChange(batch) {
|
|
134
|
+
if (batch.tables.length === 0 && batch.windows.length === 0)
|
|
135
|
+
return undefined;
|
|
136
|
+
const tables = new Set();
|
|
137
|
+
const scopeKeys = new Set();
|
|
138
|
+
for (const change of batch.tables) {
|
|
139
|
+
tables.add(change.table);
|
|
140
|
+
for (const key of change.scopeKeys ?? [])
|
|
141
|
+
scopeKeys.add(key);
|
|
142
|
+
}
|
|
143
|
+
// Window-only changes project the table for old completeness consumers.
|
|
144
|
+
// The exact API still keeps this distinct, so new table-only reads do not run.
|
|
145
|
+
for (const change of batch.windows)
|
|
146
|
+
tables.add(change.table);
|
|
147
|
+
return { tables, scopeKeys };
|
|
148
|
+
}
|
|
63
149
|
export class InvalidationEmitter {
|
|
64
150
|
#listeners = new Set();
|
|
65
151
|
on(listener) {
|
|
@@ -74,7 +160,7 @@ export class InvalidationEmitter {
|
|
|
74
160
|
listener(event);
|
|
75
161
|
}
|
|
76
162
|
catch {
|
|
77
|
-
//
|
|
163
|
+
// Compatibility observers are isolated like exact observers.
|
|
78
164
|
}
|
|
79
165
|
}
|
|
80
166
|
}
|
|
@@ -82,3 +168,5 @@ export class InvalidationEmitter {
|
|
|
82
168
|
return this.#listeners.size;
|
|
83
169
|
}
|
|
84
170
|
}
|
|
171
|
+
/** @deprecated Use {@link ChangeAccumulator}. */
|
|
172
|
+
export { ChangeAccumulator as Invalidation };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { QueryReadSpec, QuerySnapshot, WindowCoverage, WindowState } from './client.js';
|
|
2
|
+
import type { SqlValue } from './database.js';
|
|
3
|
+
import type { ClientChangeListener, SyncStatusSnapshot } from './invalidation.js';
|
|
4
|
+
import { type WindowBase } from './window.js';
|
|
5
|
+
export interface QueryDependency {
|
|
6
|
+
readonly table: string;
|
|
7
|
+
readonly scopeKeys?: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export interface ReactiveQuerySpec<Row> {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly sql: string;
|
|
12
|
+
readonly params?: readonly SqlValue[];
|
|
13
|
+
readonly dependencies: readonly QueryDependency[];
|
|
14
|
+
readonly coverage?: readonly WindowCoverage[];
|
|
15
|
+
readonly rowKey?: (row: Row) => readonly SqlValue[];
|
|
16
|
+
readonly claimCoverage?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export type LiveQueryPhase = 'loading' | 'partial' | 'ready' | 'error';
|
|
19
|
+
export interface LiveQueryResult<Row> {
|
|
20
|
+
readonly rows: readonly Row[];
|
|
21
|
+
readonly phase: LiveQueryPhase;
|
|
22
|
+
readonly revision: bigint | undefined;
|
|
23
|
+
readonly error: Error | undefined;
|
|
24
|
+
readonly isRefreshing: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ReactiveQueryClient {
|
|
27
|
+
onChange(listener: ClientChangeListener): () => void;
|
|
28
|
+
querySnapshot<Row = Record<string, SqlValue>>(spec: QueryReadSpec): QuerySnapshot<Row> | Promise<QuerySnapshot<Row>>;
|
|
29
|
+
statusSnapshot(): SyncStatusSnapshot | Promise<SyncStatusSnapshot>;
|
|
30
|
+
readonly conflicts: readonly unknown[] | (() => readonly unknown[] | Promise<readonly unknown[]>);
|
|
31
|
+
readonly rejections: readonly unknown[] | (() => readonly unknown[] | Promise<readonly unknown[]>);
|
|
32
|
+
setWindow(base: WindowBase, units: readonly string[]): void | Promise<void>;
|
|
33
|
+
windowState(base: WindowBase): WindowState | Promise<WindowState>;
|
|
34
|
+
}
|
|
35
|
+
export interface ExternalStoreEntry<T> {
|
|
36
|
+
subscribe(listener: () => void): () => void;
|
|
37
|
+
getSnapshot(): T;
|
|
38
|
+
refresh(): void;
|
|
39
|
+
}
|
|
40
|
+
export interface WindowRetention {
|
|
41
|
+
/** Resolves once this retained claim has reached the core window. */
|
|
42
|
+
readonly ready: Promise<void>;
|
|
43
|
+
/** Release only this retention owner's units from the composable union. */
|
|
44
|
+
readonly release: () => void;
|
|
45
|
+
}
|
|
46
|
+
export interface StatusStoreSnapshot {
|
|
47
|
+
readonly status: SyncStatusSnapshot | undefined;
|
|
48
|
+
readonly error: Error | undefined;
|
|
49
|
+
readonly isLoading: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ConflictStoreSnapshot<Conflict = unknown, Rejection = unknown> {
|
|
52
|
+
readonly conflicts: readonly Conflict[];
|
|
53
|
+
readonly rejections: readonly Rejection[];
|
|
54
|
+
readonly error: Error | undefined;
|
|
55
|
+
readonly isLoading: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Lossless deterministic identity for query params, bytes, and row keys. */
|
|
58
|
+
export declare function canonicalValue(value: unknown): string;
|
|
59
|
+
export declare class ReactiveClientStore {
|
|
60
|
+
#private;
|
|
61
|
+
readonly client: ReactiveQueryClient;
|
|
62
|
+
readonly status: ExternalStoreEntry<StatusStoreSnapshot>;
|
|
63
|
+
readonly conflicts: ExternalStoreEntry<ConflictStoreSnapshot>;
|
|
64
|
+
constructor(client: ReactiveQueryClient);
|
|
65
|
+
query<Row>(spec: ReactiveQuerySpec<Row>): ExternalStoreEntry<LiveQueryResult<Row>>;
|
|
66
|
+
/** Retain a composable window working set outside React. The returned
|
|
67
|
+
* handle exposes registration completion and releases only this owner. */
|
|
68
|
+
retainWindow(base: WindowBase, units: readonly string[]): WindowRetention;
|
|
69
|
+
window(base: WindowBase): ExternalStoreEntry<WindowState>;
|
|
70
|
+
setWindowClaim(owner: symbol, base: WindowBase, units: readonly string[]): Promise<void>;
|
|
71
|
+
releaseWindowClaims(owner: symbol): void;
|
|
72
|
+
start(): void;
|
|
73
|
+
dispose(): void;
|
|
74
|
+
}
|