@syncular/server 0.31.0 → 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/README.md +124 -74
- package/dist/admin.d.ts +394 -50
- package/dist/admin.js +714 -82
- package/dist/d1-storage.d.ts +6 -5
- package/dist/d1-storage.js +128 -19
- package/dist/events-ring.d.ts +12 -2
- package/dist/events-ring.js +27 -2
- package/dist/events.d.ts +21 -3
- package/dist/handler.js +19 -6
- package/dist/postgres-storage.d.ts +6 -5
- package/dist/postgres-storage.js +102 -18
- package/dist/realtime.d.ts +15 -0
- package/dist/realtime.js +24 -0
- package/dist/sqlite-storage.d.ts +6 -5
- package/dist/sqlite-storage.js +115 -18
- package/dist/storage.d.ts +58 -7
- package/package.json +2 -2
- package/src/admin.ts +1215 -120
- package/src/d1-storage.ts +187 -23
- package/src/events-ring.ts +31 -2
- package/src/events.ts +27 -4
- package/src/handler.ts +20 -6
- package/src/postgres-storage.ts +173 -28
- package/src/realtime.ts +35 -1
- package/src/sqlite-storage.ts +181 -26
- package/src/storage.ts +72 -9
package/dist/admin.d.ts
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `SyncularAdmin` is the operator-facing
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* surface. This host surface is mirrored in the server README.
|
|
2
|
+
* `SyncularAdmin` is the operator-facing surface over the server core: a
|
|
3
|
+
* partition-scoped, JSON-able query layer over `ServerStorage`, the optional
|
|
4
|
+
* segment/blob store stats, and an in-memory event ring, plus three explicit
|
|
5
|
+
* operator actions (retry a dead-lettered reaction, prune the commit log,
|
|
6
|
+
* revoke a client's lease). No separate UI package, no framework, and no
|
|
7
|
+
* wire-protocol surface. This host surface is mirrored in the server README.
|
|
9
8
|
*
|
|
10
|
-
* Nothing here is on the sync hot path.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* getting a silently-empty console.
|
|
9
|
+
* Nothing here is on the sync hot path. The additive optional storage/store
|
|
10
|
+
* methods it depends on are documented as such and this module fails loud (a
|
|
11
|
+
* thrown `Error`) when a backend lacks one, so a host wiring an unsupported
|
|
12
|
+
* store learns immediately rather than getting a silently-empty console.
|
|
15
13
|
*/
|
|
14
|
+
import { type ColumnType, type JsonValue, type PushOperationResult, type ScopeMap } from '@syncular/core';
|
|
16
15
|
import type { BlobStore, BlobStoreStats } from './blob-store.js';
|
|
17
|
-
import { type SyncServerConfig } from './context.js';
|
|
18
|
-
import type
|
|
16
|
+
import { type ResolveScopes, type SyncServerConfig } from './context.js';
|
|
17
|
+
import { type SyncularServerEvent, type SyncularServerEvents } from './events.js';
|
|
19
18
|
import type { RingBufferEvents, RingEventQuery } from './events-ring.js';
|
|
20
19
|
import type { LeaseRecord, LeaseStore } from './lease-store.js';
|
|
20
|
+
import { RealtimeHub } from './realtime.js';
|
|
21
21
|
import { type RetentionPolicy } from './prune.js';
|
|
22
22
|
import { type ServerSchema } from './schema.js';
|
|
23
23
|
import type { SegmentStore, SegmentStoreStats } from './segment-store.js';
|
|
24
|
-
import type { CheckpointDeclaration, CommitMetadata, ReactionStatus, ScopeCommitActivity, ServerStorage, StoredReaction } from './storage.js';
|
|
24
|
+
import type { CheckpointDeclaration, CommitMetadata, ReactionStatus, StoredPushResult, RowChangeRecord, ScopeCommitActivity, ServerStorage, StoredReaction } from './storage.js';
|
|
25
25
|
/** A connected/known client as the console sees it (§4.5, §8.1). */
|
|
26
26
|
export interface AdminClient {
|
|
27
27
|
readonly clientId: string;
|
|
28
28
|
readonly actorId: string;
|
|
29
|
+
/** SSP2 wire version last accepted from this client. */
|
|
30
|
+
readonly wireVersion: number;
|
|
31
|
+
/** Highest codec schema version among its subscriptions, when recorded. */
|
|
32
|
+
readonly schemaVersion?: number;
|
|
29
33
|
readonly cursor: number;
|
|
30
34
|
/**
|
|
31
35
|
* Commits the client has not pulled yet: `maxCommitSeq − max(cursor, 0)`.
|
|
@@ -40,6 +44,24 @@ export interface AdminClient {
|
|
|
40
44
|
}[];
|
|
41
45
|
/** True when the cursor record was touched within the active window. */
|
|
42
46
|
readonly active: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* True while the client holds an open realtime socket on this process.
|
|
49
|
+
* Absent when the admin has no realtime hub.
|
|
50
|
+
*/
|
|
51
|
+
readonly connected?: boolean;
|
|
52
|
+
/** When the client's oldest open socket opened. */
|
|
53
|
+
readonly connectedSinceMs?: number;
|
|
54
|
+
}
|
|
55
|
+
export interface AdminListClientsOptions {
|
|
56
|
+
/** Default 200. */
|
|
57
|
+
readonly limit?: number;
|
|
58
|
+
/** `seen` (default): most recently seen first. `lag`: largest lag first. */
|
|
59
|
+
readonly sort?: 'seen' | 'lag';
|
|
60
|
+
}
|
|
61
|
+
export interface AdminClientPage {
|
|
62
|
+
readonly clients: readonly AdminClient[];
|
|
63
|
+
/** Known clients in the partition before `limit`. */
|
|
64
|
+
readonly total: number;
|
|
43
65
|
}
|
|
44
66
|
/** One client's drill-down: record + lease + its slice of the event tail. */
|
|
45
67
|
export interface AdminClientDetail {
|
|
@@ -52,12 +74,27 @@ export interface AdminClientDetail {
|
|
|
52
74
|
/** Recent ring events carrying this clientId (newest first). */
|
|
53
75
|
readonly events: readonly SyncularServerEvent[];
|
|
54
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* How much of a requested window the ring still holds. `complete` is false
|
|
79
|
+
* when the ring dropped events inside the window; the aggregates then cover
|
|
80
|
+
* only `fromMs` onward.
|
|
81
|
+
*/
|
|
82
|
+
export interface AdminEventCoverage {
|
|
83
|
+
readonly complete: boolean;
|
|
84
|
+
readonly fromMs: number;
|
|
85
|
+
}
|
|
86
|
+
export interface AdminPushCounts {
|
|
87
|
+
readonly applied: number;
|
|
88
|
+
readonly rejected: number;
|
|
89
|
+
readonly conflicted: number;
|
|
90
|
+
}
|
|
55
91
|
/** Ring-derived request/push aggregates over a trailing window. */
|
|
56
92
|
export interface AdminMetrics {
|
|
57
93
|
readonly partition: string;
|
|
58
94
|
readonly windowMs: number;
|
|
59
95
|
/** Wall-clock the aggregation ran at (window end). */
|
|
60
96
|
readonly atMs: number;
|
|
97
|
+
readonly coverage: AdminEventCoverage;
|
|
61
98
|
readonly requests: {
|
|
62
99
|
readonly count: number;
|
|
63
100
|
readonly perMinute: number;
|
|
@@ -67,21 +104,58 @@ export interface AdminMetrics {
|
|
|
67
104
|
readonly p50Ms: number;
|
|
68
105
|
readonly p95Ms: number;
|
|
69
106
|
};
|
|
70
|
-
readonly pushes:
|
|
71
|
-
readonly applied: number;
|
|
72
|
-
readonly rejected: number;
|
|
73
|
-
readonly conflicted: number;
|
|
74
|
-
};
|
|
107
|
+
readonly pushes: AdminPushCounts;
|
|
75
108
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
109
|
+
* The window split into `counts.length` equal buckets, oldest first: the
|
|
110
|
+
* request count, failed requests, p95 duration (`null` for an empty
|
|
111
|
+
* bucket), and push outcomes per bucket.
|
|
78
112
|
*/
|
|
79
113
|
readonly buckets: {
|
|
80
114
|
readonly widthMs: number;
|
|
81
115
|
readonly counts: readonly number[];
|
|
82
116
|
readonly errors: readonly number[];
|
|
117
|
+
readonly p95Ms: readonly (number | null)[];
|
|
118
|
+
readonly pushes: readonly AdminPushCounts[];
|
|
83
119
|
};
|
|
84
120
|
}
|
|
121
|
+
/** Failures sharing one source and code within the window. */
|
|
122
|
+
export interface AdminErrorGroup {
|
|
123
|
+
readonly source: 'request' | 'push' | 'reaction' | 'scopes';
|
|
124
|
+
/** The §10.2 code; `scopes.resolve_failed` for a throwing resolver. */
|
|
125
|
+
readonly code: string;
|
|
126
|
+
readonly count: number;
|
|
127
|
+
/** Distinct clients among the events that carry a clientId. */
|
|
128
|
+
readonly clients: number;
|
|
129
|
+
/** Distinct actors among the events that carry an actorId. */
|
|
130
|
+
readonly actors: number;
|
|
131
|
+
readonly lastAtMs: number;
|
|
132
|
+
/** Up to five client ids, most recent first. */
|
|
133
|
+
readonly sampleClientIds: readonly string[];
|
|
134
|
+
/** Up to five rows the failing pushes targeted, most recent first. */
|
|
135
|
+
readonly sampleRows: readonly {
|
|
136
|
+
readonly table: string;
|
|
137
|
+
readonly rowId: string;
|
|
138
|
+
}[];
|
|
139
|
+
}
|
|
140
|
+
/** One client's push failures within the window. */
|
|
141
|
+
export interface AdminClientErrors {
|
|
142
|
+
readonly clientId: string;
|
|
143
|
+
readonly actorId: string;
|
|
144
|
+
readonly count: number;
|
|
145
|
+
readonly lastCode: string;
|
|
146
|
+
readonly lastAtMs: number;
|
|
147
|
+
}
|
|
148
|
+
/** "What is failing": ring failures grouped by code and by client. */
|
|
149
|
+
export interface AdminErrorSummary {
|
|
150
|
+
readonly partition: string;
|
|
151
|
+
readonly windowMs: number;
|
|
152
|
+
readonly atMs: number;
|
|
153
|
+
readonly coverage: AdminEventCoverage;
|
|
154
|
+
/** Largest count first. */
|
|
155
|
+
readonly byCode: readonly AdminErrorGroup[];
|
|
156
|
+
/** Every client with a request or push failure, largest count first. */
|
|
157
|
+
readonly byClient: readonly AdminClientErrors[];
|
|
158
|
+
}
|
|
85
159
|
/** One partition's row in the fleet view (`listPartitions` + horizon math). */
|
|
86
160
|
export interface AdminPartitionOverview {
|
|
87
161
|
readonly partition: string;
|
|
@@ -93,14 +167,175 @@ export interface AdminPartitionOverview {
|
|
|
93
167
|
readonly activeClients: number;
|
|
94
168
|
readonly recommendation: 'up-to-date' | 'prune-recommended';
|
|
95
169
|
}
|
|
170
|
+
export interface AdminPartitionPage {
|
|
171
|
+
readonly partitions: readonly AdminPartitionOverview[];
|
|
172
|
+
/** Every registered partition before `offset`/`limit`. */
|
|
173
|
+
readonly total: number;
|
|
174
|
+
}
|
|
96
175
|
export interface AdminRowInspection {
|
|
97
176
|
readonly table: string;
|
|
98
177
|
readonly rowId: string;
|
|
99
178
|
readonly exists: boolean;
|
|
100
179
|
readonly serverVersion?: number;
|
|
101
180
|
readonly scopes?: Record<string, string>;
|
|
102
|
-
/**
|
|
103
|
-
readonly
|
|
181
|
+
/** Present iff `exists`: the row decoded and rendered per §11. */
|
|
182
|
+
readonly values?: Record<string, JsonValue>;
|
|
183
|
+
/** The retained changes to this row, newest first. */
|
|
184
|
+
readonly history: readonly RowChangeRecord[];
|
|
185
|
+
}
|
|
186
|
+
/** One change in a commit drill-down. */
|
|
187
|
+
export interface AdminCommitChange {
|
|
188
|
+
readonly table: string;
|
|
189
|
+
readonly rowId: string;
|
|
190
|
+
readonly op: 'upsert' | 'delete';
|
|
191
|
+
readonly rowVersion?: number;
|
|
192
|
+
readonly scopes: Record<string, string>;
|
|
193
|
+
/** The upserted row rendered per §11; absent for deletes. */
|
|
194
|
+
readonly values?: Record<string, JsonValue>;
|
|
195
|
+
/**
|
|
196
|
+
* True when the stored payload does not decode against the current
|
|
197
|
+
* schema (written under an older column set).
|
|
198
|
+
*/
|
|
199
|
+
readonly undecodable?: boolean;
|
|
200
|
+
}
|
|
201
|
+
export interface AdminCommitDetail extends CommitMetadata {
|
|
202
|
+
readonly changes: readonly AdminCommitChange[];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The trace verdict, evaluated in this order:
|
|
206
|
+
* `client-unknown`: no client record for the id.
|
|
207
|
+
* `row-missing`: no row and no retained change to it.
|
|
208
|
+
* `no-subscription`: the client subscribes to nothing on the table.
|
|
209
|
+
* `row-deleted`: the row's newest retained change is a delete the client
|
|
210
|
+
* has pulled.
|
|
211
|
+
* `scope-mismatch`: no subscription requests the row's stored scopes.
|
|
212
|
+
* `scope-denied`: a subscription requests them, but the actor's allowed
|
|
213
|
+
* scopes (the host `resolveScopes`) exclude the row.
|
|
214
|
+
* `pending-pull`: the client's cursor is below the row's newest change.
|
|
215
|
+
* `delivered`: the client has pulled the row's newest change.
|
|
216
|
+
*/
|
|
217
|
+
export type AdminTraceVerdict = 'client-unknown' | 'row-missing' | 'no-subscription' | 'row-deleted' | 'scope-mismatch' | 'scope-denied' | 'pending-pull' | 'delivered';
|
|
218
|
+
/** One of the client's subscriptions on the traced table. */
|
|
219
|
+
export interface AdminTraceSubscription {
|
|
220
|
+
readonly id: string;
|
|
221
|
+
readonly scopes: Record<string, readonly string[]>;
|
|
222
|
+
/** The row's stored scopes satisfy the requested scopes. */
|
|
223
|
+
readonly requestedMatch: boolean;
|
|
224
|
+
/** Each requested variable the row's stored value fails. */
|
|
225
|
+
readonly mismatches: readonly {
|
|
226
|
+
readonly variable: string;
|
|
227
|
+
readonly rowValue: string | null;
|
|
228
|
+
readonly requested: readonly string[];
|
|
229
|
+
}[];
|
|
230
|
+
/**
|
|
231
|
+
* The row also satisfies requested ∩ allowed (§3.2). Absent when the
|
|
232
|
+
* admin has no `resolveScopes` or the resolver reported an outage.
|
|
233
|
+
*/
|
|
234
|
+
readonly effectiveMatch?: boolean;
|
|
235
|
+
}
|
|
236
|
+
/** "Why does client X (not) see row Y": one read across row and client. */
|
|
237
|
+
export interface AdminRowTrace {
|
|
238
|
+
readonly verdict: AdminTraceVerdict;
|
|
239
|
+
readonly table: string;
|
|
240
|
+
readonly rowId: string;
|
|
241
|
+
readonly clientId: string;
|
|
242
|
+
readonly row: AdminRowInspection;
|
|
243
|
+
/** The row's newest retained change. */
|
|
244
|
+
readonly lastChange?: RowChangeRecord;
|
|
245
|
+
/** The partition horizon: changes at or below it are pruned. */
|
|
246
|
+
readonly horizonSeq: number;
|
|
247
|
+
readonly client?: AdminClient;
|
|
248
|
+
/**
|
|
249
|
+
* The actor's allowed scopes from the host `resolveScopes`, or `outage`
|
|
250
|
+
* when it returned the §7.3.3 outage sentinel. Absent without a resolver.
|
|
251
|
+
*/
|
|
252
|
+
readonly allowed?: ScopeMap | 'outage';
|
|
253
|
+
readonly subscriptions: readonly AdminTraceSubscription[];
|
|
254
|
+
/** The client's retained push rejections and conflicts, newest first. */
|
|
255
|
+
readonly rejections: readonly SyncularServerEvent[];
|
|
256
|
+
}
|
|
257
|
+
/** One client's verdict in a row's delivery matrix. */
|
|
258
|
+
export interface AdminRowDeliveryClient {
|
|
259
|
+
readonly client: AdminClient;
|
|
260
|
+
readonly verdict: AdminTraceVerdict;
|
|
261
|
+
}
|
|
262
|
+
/** Who has one row and who does not: every known client's verdict. */
|
|
263
|
+
export interface AdminRowDelivery {
|
|
264
|
+
readonly table: string;
|
|
265
|
+
readonly rowId: string;
|
|
266
|
+
readonly row: AdminRowInspection;
|
|
267
|
+
readonly lastChange?: RowChangeRecord;
|
|
268
|
+
readonly horizonSeq: number;
|
|
269
|
+
/** Every known client, sorted by verdict then most recently seen. */
|
|
270
|
+
readonly clients: readonly AdminRowDeliveryClient[];
|
|
271
|
+
}
|
|
272
|
+
/** One actor and its devices (clients). */
|
|
273
|
+
export interface AdminActor {
|
|
274
|
+
readonly actorId: string;
|
|
275
|
+
readonly clients: readonly AdminClient[];
|
|
276
|
+
/** Devices with an open socket; absent without a realtime hub. */
|
|
277
|
+
readonly connected?: number;
|
|
278
|
+
readonly lastSeenMs: number;
|
|
279
|
+
readonly maxLag: number;
|
|
280
|
+
/** The actor's failures in the last 5 minutes, as `errors().byClient` counts them. */
|
|
281
|
+
readonly recentFailures: number;
|
|
282
|
+
}
|
|
283
|
+
/** A page of decoded rows from one table. */
|
|
284
|
+
export interface AdminRowPage {
|
|
285
|
+
readonly table: string;
|
|
286
|
+
readonly rows: readonly {
|
|
287
|
+
readonly rowId: string;
|
|
288
|
+
readonly serverVersion: number;
|
|
289
|
+
readonly scopes: Record<string, string>;
|
|
290
|
+
readonly values: Record<string, JsonValue>;
|
|
291
|
+
}[];
|
|
292
|
+
/** Pass as `afterRowId` for the next page; absent on the last page. */
|
|
293
|
+
readonly nextAfterRowId?: string;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* One per-operation result of a stored write, JSON-safe: a conflict's
|
|
297
|
+
* `serverRow` and `conflictColumns` bytes are standard base64 (§11).
|
|
298
|
+
*/
|
|
299
|
+
export type AdminWriteResult = Exclude<PushOperationResult, {
|
|
300
|
+
status: 'conflict';
|
|
301
|
+
}> | (Omit<Extract<PushOperationResult, {
|
|
302
|
+
status: 'conflict';
|
|
303
|
+
}>, 'serverRow' | 'conflictColumns'> & {
|
|
304
|
+
readonly serverRow: string;
|
|
305
|
+
readonly conflictColumns: string;
|
|
306
|
+
});
|
|
307
|
+
/** One stored outcome of a client write (§2.3 idempotency record). */
|
|
308
|
+
export interface AdminWriteOutcome {
|
|
309
|
+
readonly clientId: string;
|
|
310
|
+
readonly status: StoredPushResult['status'];
|
|
311
|
+
readonly commitSeq?: number;
|
|
312
|
+
readonly recordedAtMs?: number;
|
|
313
|
+
readonly results: readonly AdminWriteResult[];
|
|
314
|
+
}
|
|
315
|
+
/** What happened to the write a client logged as `clientCommitId`. */
|
|
316
|
+
export interface AdminWriteLookup {
|
|
317
|
+
readonly clientCommitId: string;
|
|
318
|
+
/** One outcome per client that sent this clientCommitId. */
|
|
319
|
+
readonly outcomes: readonly AdminWriteOutcome[];
|
|
320
|
+
/** Retained ring events carrying this clientCommitId, newest first. */
|
|
321
|
+
readonly events: readonly SyncularServerEvent[];
|
|
322
|
+
}
|
|
323
|
+
/** Everything an id pasted into the console search matches. */
|
|
324
|
+
export interface AdminSearchResult {
|
|
325
|
+
readonly query: string;
|
|
326
|
+
/** Clients whose id contains the query, or whose actor equals it. */
|
|
327
|
+
readonly clients: readonly AdminClient[];
|
|
328
|
+
/** Actors whose id contains the query. */
|
|
329
|
+
readonly actors: readonly string[];
|
|
330
|
+
/** Rows whose id equals the query, one per table that holds it. */
|
|
331
|
+
readonly rows: readonly {
|
|
332
|
+
readonly table: string;
|
|
333
|
+
readonly rowId: string;
|
|
334
|
+
}[];
|
|
335
|
+
/** The commit whose seq equals the query. */
|
|
336
|
+
readonly commit?: CommitMetadata;
|
|
337
|
+
/** Outcomes of writes whose clientCommitId equals the query. */
|
|
338
|
+
readonly writes: readonly AdminWriteOutcome[];
|
|
104
339
|
}
|
|
105
340
|
export interface AdminHorizonStatus {
|
|
106
341
|
readonly partition: string;
|
|
@@ -114,10 +349,28 @@ export interface AdminHorizonStatus {
|
|
|
114
349
|
readonly recommendedHorizonSeq: number;
|
|
115
350
|
readonly recommendation: 'up-to-date' | 'prune-recommended';
|
|
116
351
|
}
|
|
352
|
+
export interface AdminPruneResult {
|
|
353
|
+
readonly previousHorizonSeq: number;
|
|
354
|
+
readonly horizonSeq: number;
|
|
355
|
+
}
|
|
356
|
+
/** One schema table as the console's pickers need it. */
|
|
357
|
+
export interface AdminTableInfo {
|
|
358
|
+
readonly name: string;
|
|
359
|
+
readonly columns: readonly {
|
|
360
|
+
readonly name: string;
|
|
361
|
+
readonly type: ColumnType;
|
|
362
|
+
readonly nullable: boolean;
|
|
363
|
+
readonly primaryKey: boolean;
|
|
364
|
+
}[];
|
|
365
|
+
}
|
|
117
366
|
export interface AdminListCommitsOptions {
|
|
118
367
|
readonly afterSeq?: number;
|
|
368
|
+
/** Exclusive upper bound: pass the oldest seq of a page for the next. */
|
|
369
|
+
readonly beforeSeq?: number;
|
|
119
370
|
readonly limit?: number;
|
|
120
371
|
readonly table?: string;
|
|
372
|
+
/** Attach each commit's first N changes as `sample`. */
|
|
373
|
+
readonly changeSample?: number;
|
|
121
374
|
}
|
|
122
375
|
export interface AdminScopeActivityOptions {
|
|
123
376
|
readonly limit?: number;
|
|
@@ -134,57 +387,124 @@ export interface AdminStats {
|
|
|
134
387
|
export interface SyncularAdminOptions {
|
|
135
388
|
readonly storage: ServerStorage;
|
|
136
389
|
/**
|
|
137
|
-
* The server schema.
|
|
138
|
-
*
|
|
139
|
-
* a storage instance that has not served a sync request yet.
|
|
390
|
+
* The server schema. Row reads (`inspectRow`, `traceRow`, `commitDetail`)
|
|
391
|
+
* require it: it decodes payloads and ensures the relational row tables
|
|
392
|
+
* exist on a storage instance that has not served a sync request yet.
|
|
140
393
|
*/
|
|
141
394
|
readonly schema?: ServerSchema;
|
|
142
395
|
/** RFC 0007: declarations `inspectRow` carries into `ensureSchema`. */
|
|
143
396
|
readonly checkpoints?: readonly CheckpointDeclaration[];
|
|
144
397
|
/** The event ring feeding the event tail. Absent ⇒ `events()` is empty. */
|
|
145
398
|
readonly ring?: RingBufferEvents;
|
|
399
|
+
/** Receives `prune.completed` and `lease.revoked` from operator actions. */
|
|
400
|
+
readonly events?: SyncularServerEvents;
|
|
401
|
+
/** The host resolver: `traceRow` checks the actor's allowed scopes. */
|
|
402
|
+
readonly resolveScopes?: ResolveScopes;
|
|
403
|
+
/** The realtime hub: which clients hold an open socket right now. */
|
|
404
|
+
readonly realtime?: RealtimeHub;
|
|
146
405
|
readonly segments?: SegmentStore;
|
|
147
406
|
readonly blobs?: BlobStore;
|
|
148
|
-
/** The §7.3 lease store —
|
|
407
|
+
/** The §7.3 lease store — the client drill-down and `revokeLease`. */
|
|
149
408
|
readonly leases?: LeaseStore;
|
|
150
|
-
/** Retention policy for horizon recommendation
|
|
409
|
+
/** Retention policy for horizon recommendation and prune (§4.6). */
|
|
151
410
|
readonly retention?: Partial<RetentionPolicy>;
|
|
152
411
|
/** Epoch-ms clock (defaults to `Date.now`) — active-window math. */
|
|
153
412
|
readonly clock?: () => number;
|
|
154
413
|
}
|
|
155
|
-
/** The
|
|
414
|
+
/** The console query surface plus its operator actions. One per process. */
|
|
156
415
|
export declare class SyncularAdmin {
|
|
157
416
|
#private;
|
|
158
417
|
constructor(options: SyncularAdminOptions);
|
|
159
418
|
/**
|
|
160
|
-
* Build an admin over a `SyncServerConfig`, reusing its storage
|
|
161
|
-
*
|
|
162
|
-
* composed into the config's `events` by
|
|
419
|
+
* Build an admin over a `SyncServerConfig`, reusing its storage, schema,
|
|
420
|
+
* resolver, events sink, stores, leases and clock. Pass the ring
|
|
421
|
+
* separately (it is an events sink, composed into the config's `events` by
|
|
422
|
+
* the host — see `composeEvents`).
|
|
163
423
|
*/
|
|
164
424
|
static fromConfig(config: SyncServerConfig, extra?: {
|
|
165
425
|
ring?: RingBufferEvents;
|
|
166
426
|
retention?: Partial<RetentionPolicy>;
|
|
167
427
|
}): SyncularAdmin;
|
|
168
|
-
/** The
|
|
169
|
-
|
|
428
|
+
/** The server schema version; `undefined` without a schema. */
|
|
429
|
+
get schemaVersion(): number | undefined;
|
|
430
|
+
/** The schema tables and columns, for row and commit pickers. */
|
|
431
|
+
tables(): AdminTableInfo[];
|
|
432
|
+
/** The known clients for a partition (cursor, lag, versions, subs). */
|
|
433
|
+
listClients(partition: string, options?: AdminListClientsOptions): Promise<AdminClientPage>;
|
|
170
434
|
/**
|
|
171
435
|
* One client's drill-down: its record (with lag), its §7.3 lease when a
|
|
172
|
-
* lease store is wired, and its recent slice of the
|
|
173
|
-
* "why is this client stale" in a single read.
|
|
436
|
+
* lease store is wired, its realtime state, and its recent slice of the
|
|
437
|
+
* event tail. Answers "why is this client stale" in a single read.
|
|
174
438
|
*/
|
|
175
439
|
clientDetail(partition: string, clientId: string, options?: {
|
|
176
440
|
readonly eventLimit?: number;
|
|
177
441
|
}): Promise<AdminClientDetail>;
|
|
178
442
|
/** Commit-log metadata (no payloads), newest first. */
|
|
179
443
|
listCommits(partition: string, options?: AdminListCommitsOptions): Promise<CommitMetadata[]>;
|
|
444
|
+
/**
|
|
445
|
+
* One retained commit with every change rendered per §11. Throws
|
|
446
|
+
* `sync.not_found` when the commit does not exist or was pruned.
|
|
447
|
+
*/
|
|
448
|
+
commitDetail(partition: string, commitSeq: number): Promise<AdminCommitDetail>;
|
|
180
449
|
/** Pending, leased, completed, and dead-lettered durable reactions. */
|
|
181
450
|
listReactions(partition: string, options?: AdminListReactionsOptions): Promise<StoredReaction[]>;
|
|
182
451
|
/**
|
|
183
|
-
* Inspect a single row:
|
|
184
|
-
*
|
|
185
|
-
*
|
|
452
|
+
* Inspect a single row: server_version, stored scopes, its values decoded
|
|
453
|
+
* per §11, and its retained change history (newest first). Throws
|
|
454
|
+
* `sync.unknown_table` for a table outside the schema.
|
|
455
|
+
*/
|
|
456
|
+
inspectRow(partition: string, table: string, rowId: string, options?: {
|
|
457
|
+
readonly historyLimit?: number;
|
|
458
|
+
}): Promise<AdminRowInspection>;
|
|
459
|
+
/**
|
|
460
|
+
* Trace whether one client receives one row, combining the row's stored
|
|
461
|
+
* scopes and newest change with the client's subscriptions, cursor, the
|
|
462
|
+
* actor's allowed scopes, and its retained push failures. See
|
|
463
|
+
* `AdminTraceVerdict` for the verdict order.
|
|
464
|
+
*/
|
|
465
|
+
traceRow(partition: string, query: {
|
|
466
|
+
readonly table: string;
|
|
467
|
+
readonly rowId: string;
|
|
468
|
+
readonly clientId: string;
|
|
469
|
+
}): Promise<AdminRowTrace>;
|
|
470
|
+
/**
|
|
471
|
+
* Who has one row and who does not: the trace verdict for every known
|
|
472
|
+
* client in the partition, resolving each actor's allowed scopes once.
|
|
186
473
|
*/
|
|
187
|
-
|
|
474
|
+
rowDelivery(partition: string, table: string, rowId: string): Promise<AdminRowDelivery>;
|
|
475
|
+
/**
|
|
476
|
+
* The partition's actors with their devices (clients), open sockets,
|
|
477
|
+
* worst lag, and failures in the last 5 minutes; most recently seen
|
|
478
|
+
* first.
|
|
479
|
+
*/
|
|
480
|
+
actors(partition: string): Promise<AdminActor[]>;
|
|
481
|
+
/** Row counts per schema table for the partition. */
|
|
482
|
+
tableCounts(partition: string): Promise<Record<string, number>>;
|
|
483
|
+
/**
|
|
484
|
+
* One page of a table's rows decoded per §11, ordered by rowId. With a
|
|
485
|
+
* `scope`, only rows whose stored scope matches, read through the scope
|
|
486
|
+
* index.
|
|
487
|
+
*/
|
|
488
|
+
listRows(partition: string, table: string, options?: {
|
|
489
|
+
readonly afterRowId?: string;
|
|
490
|
+
readonly limit?: number;
|
|
491
|
+
readonly scope?: {
|
|
492
|
+
readonly variable: string;
|
|
493
|
+
readonly value: string;
|
|
494
|
+
};
|
|
495
|
+
}): Promise<AdminRowPage>;
|
|
496
|
+
/**
|
|
497
|
+
* What happened to a write: every stored idempotency outcome for the
|
|
498
|
+
* clientCommitId (applied with its commitSeq, or rejected with the
|
|
499
|
+
* per-operation results), plus the retained ring events that carry it.
|
|
500
|
+
*/
|
|
501
|
+
findWrite(partition: string, clientCommitId: string): Promise<AdminWriteLookup>;
|
|
502
|
+
/**
|
|
503
|
+
* Resolve an id pasted from a bug report: clients and actors containing
|
|
504
|
+
* it, rows with that rowId in any table, the commit with that seq, and
|
|
505
|
+
* writes with that clientCommitId.
|
|
506
|
+
*/
|
|
507
|
+
search(partition: string, query: string): Promise<AdminSearchResult>;
|
|
188
508
|
/**
|
|
189
509
|
* Recent commits touching one scope key (`variable:value`, e.g.
|
|
190
510
|
* `project:p1`) — routed through the change-scope index, never a scan.
|
|
@@ -201,10 +521,8 @@ export declare class SyncularAdmin {
|
|
|
201
521
|
horizonStatus(partition: string): Promise<AdminHorizonStatus>;
|
|
202
522
|
/** Segment + blob store counters where the stores expose them. */
|
|
203
523
|
stats(partition: string): Promise<AdminStats>;
|
|
204
|
-
/**
|
|
205
|
-
|
|
206
|
-
/** Blob store counters for a partition (undefined when unsupported/unset). */
|
|
207
|
-
blobStats(partition: string): Promise<BlobStoreStats | undefined>;
|
|
524
|
+
/** True when a realtime hub is wired: clients report `connected`. */
|
|
525
|
+
get hasRealtime(): boolean;
|
|
208
526
|
/** True when this admin has an event ring wired (the event tail is live). */
|
|
209
527
|
get hasEventStream(): boolean;
|
|
210
528
|
/** The event tail from the ring buffer (newest first). Empty when unwired. */
|
|
@@ -218,19 +536,45 @@ export declare class SyncularAdmin {
|
|
|
218
536
|
/**
|
|
219
537
|
* Request/push health over a trailing window, derived entirely from the
|
|
220
538
|
* ring (zero new server state): rates, error share, duration percentiles,
|
|
221
|
-
* and per-bucket
|
|
222
|
-
*
|
|
539
|
+
* and per-bucket series. Only events carrying this `partition` count.
|
|
540
|
+
* `coverage` reports when the ring dropped events inside the window.
|
|
223
541
|
*/
|
|
224
542
|
metrics(partition: string, options?: {
|
|
225
543
|
readonly windowMs?: number;
|
|
226
544
|
readonly buckets?: number;
|
|
227
545
|
}): AdminMetrics;
|
|
546
|
+
/**
|
|
547
|
+
* Failures over a trailing window, grouped by source and code and by
|
|
548
|
+
* client: failed requests, push rejections and conflicts, dead-lettered
|
|
549
|
+
* reactions, and throwing scope resolvers. Derived from the ring.
|
|
550
|
+
*/
|
|
551
|
+
errors(partition: string, options?: {
|
|
552
|
+
readonly windowMs?: number;
|
|
553
|
+
}): AdminErrorSummary;
|
|
228
554
|
/** Every authenticated partition in the storage-backed registry. */
|
|
229
555
|
listPartitions(): Promise<string[]>;
|
|
230
556
|
/**
|
|
231
557
|
* The fleet view: one row per known partition — retained backlog, client
|
|
232
|
-
* counts, prune recommendation
|
|
233
|
-
*
|
|
558
|
+
* counts, prune recommendation — paged by `offset`/`limit` (default 100)
|
|
559
|
+
* because each row costs several reads. A cross-partition read (every
|
|
560
|
+
* other method is partition-scoped); host authorization should account
|
|
561
|
+
* for it.
|
|
562
|
+
*/
|
|
563
|
+
partitionsOverview(options?: {
|
|
564
|
+
readonly limit?: number;
|
|
565
|
+
readonly offset?: number;
|
|
566
|
+
}): Promise<AdminPartitionPage>;
|
|
567
|
+
/**
|
|
568
|
+
* Operator action: requeue a dead-lettered reaction. Throws
|
|
569
|
+
* `sync.invalid_request` when the key names no dead-lettered reaction.
|
|
570
|
+
*/
|
|
571
|
+
retryReaction(partition: string, idempotencyKey: string): Promise<StoredReaction>;
|
|
572
|
+
/** Operator action: one §4.6 prune pass now, emitting `prune.completed`. */
|
|
573
|
+
prune(partition: string): Promise<AdminPruneResult>;
|
|
574
|
+
/**
|
|
575
|
+
* Operator action: revoke the client's §7.3 lease (§7.3.4), emitting
|
|
576
|
+
* `lease.revoked`. Throws `sync.invalid_request` when the client holds no
|
|
577
|
+
* lease.
|
|
234
578
|
*/
|
|
235
|
-
|
|
579
|
+
revokeLease(partition: string, clientId: string): Promise<LeaseRecord>;
|
|
236
580
|
}
|