@velajs/studio-protocol 2.0.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/index.d.ts +1285 -0
- package/dist/index.js +775 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1285 @@
|
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Studio error wire shape + the closed catalog of Studio-specific error codes.
|
|
4
|
+
*
|
|
5
|
+
* `AdminErrorBody` is a STRUCTURAL MIRROR of `@velajs/errors` `WireErrorObject`
|
|
6
|
+
* (`errors/src/to-error-body.ts`), enriched with the `title` and `status` the
|
|
7
|
+
* UI renders directly off the error envelope. The server enriches a
|
|
8
|
+
* `WireErrorObject` (from `toErrorBody`) into this shape; a drift guard asserting
|
|
9
|
+
* field compatibility lives in the server package.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The client-bound error body carried by a failed {@link AdminRpcResponse}.
|
|
13
|
+
*
|
|
14
|
+
* Shared fields (`code`, `message`, `hint`, `docsUrl`, `details`) mirror
|
|
15
|
+
* `WireErrorObject`; `title` and `status` are the Studio-facing enrichment.
|
|
16
|
+
*/
|
|
17
|
+
export interface AdminErrorBody {
|
|
18
|
+
/** Stable, dot/underscore-namespaced machine code (e.g. `STUDIO_DISABLED`). */
|
|
19
|
+
code: string;
|
|
20
|
+
/** Human-readable, catalog-sourced summary of the error class. */
|
|
21
|
+
title: string;
|
|
22
|
+
/** HTTP status this error maps to. */
|
|
23
|
+
status: number;
|
|
24
|
+
/** Redaction-safe message (unbranded errors carry only their status title). */
|
|
25
|
+
message: string;
|
|
26
|
+
/** Optional actionable remediation hint. */
|
|
27
|
+
hint?: string;
|
|
28
|
+
/** Optional link to documentation for this error class. */
|
|
29
|
+
docsUrl?: string;
|
|
30
|
+
/** Optional wire-encoded structured data (mirrors `WireErrorObject.details`). */
|
|
31
|
+
details?: unknown;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The structured payload carried on {@link AdminErrorBody.details} of a
|
|
35
|
+
* `STUDIO_CONFIRM_REQUIRED` (428) error — the server's challenge for a
|
|
36
|
+
* destructive op. The client re-sends the identical op args plus
|
|
37
|
+
* {@link StudioConfirmChallenge.confirmToken} to complete the op.
|
|
38
|
+
*
|
|
39
|
+
* The token is single-use and payload-bound (see the server confirm signer);
|
|
40
|
+
* `summary` is a human line the op handler supplies for the confirm dialog.
|
|
41
|
+
* This is the canonical wire shape for the challenge — producers (server,
|
|
42
|
+
* test fixtures) and consumers (UI decode) MUST use this type rather than
|
|
43
|
+
* re-declaring it, so the two ends cannot drift.
|
|
44
|
+
*/
|
|
45
|
+
export interface StudioConfirmChallenge {
|
|
46
|
+
/** Single-use, op+payload-bound token to echo back on the confirmed retry. */
|
|
47
|
+
confirmToken: string;
|
|
48
|
+
/** Epoch-ms after which the token is rejected. */
|
|
49
|
+
expiresAt: number;
|
|
50
|
+
/** Human-readable description of what the confirmed op will do. */
|
|
51
|
+
summary: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The closed set of Studio-specific error codes. `const` array + derived union
|
|
55
|
+
* so the compile-time type and any runtime membership check can never drift.
|
|
56
|
+
* Parenthetical statuses document the intended HTTP mapping (the wire carries
|
|
57
|
+
* the concrete status on {@link AdminErrorBody}).
|
|
58
|
+
*/
|
|
59
|
+
export declare const STUDIO_ERROR_CODES: readonly [
|
|
60
|
+
/** Studio surface disabled (no token configured). */
|
|
61
|
+
'STUDIO_DISABLED',
|
|
62
|
+
/** Missing or invalid bearer token (401). */
|
|
63
|
+
'STUDIO_UNAUTHORIZED',
|
|
64
|
+
/** Dispatch target op is not registered (404). */
|
|
65
|
+
'STUDIO_UNKNOWN_OP',
|
|
66
|
+
/** Op recognized but forbidden for this principal/config (403). */
|
|
67
|
+
'STUDIO_OP_FORBIDDEN',
|
|
68
|
+
/** Ephemeral WS sub-token failed verification (401). */
|
|
69
|
+
'STUDIO_SUB_TOKEN_INVALID',
|
|
70
|
+
/** Per-IP token bucket exhausted (429). */
|
|
71
|
+
'STUDIO_RATE_LIMITED',
|
|
72
|
+
/** A confirmation token is required for this destructive op (428). */
|
|
73
|
+
'STUDIO_CONFIRM_REQUIRED',
|
|
74
|
+
/** Referenced data model is unknown (404). */
|
|
75
|
+
'STUDIO_UNKNOWN_MODEL',
|
|
76
|
+
/** Data editing is disabled (read-only Studio) (403). */
|
|
77
|
+
'DATA_EDIT_DISABLED',
|
|
78
|
+
/** Time travel is unavailable in this environment (409). */
|
|
79
|
+
'TIMETRAVEL_UNAVAILABLE',
|
|
80
|
+
/** Snapshot schema is incompatible with the current model (409). */
|
|
81
|
+
'TIMETRAVEL_SCHEMA_MISMATCH',
|
|
82
|
+
/** The backing feature/package is present but not configured (404). */
|
|
83
|
+
'FEATURE_UNCONFIGURED'];
|
|
84
|
+
/** Union of every Studio-specific error code. */
|
|
85
|
+
export type StudioErrorCode = (typeof STUDIO_ERROR_CODES)[number];
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/envelope.d.ts
|
|
88
|
+
/** A dispatch request. `args` shape is the op's `req` type from the op catalog. */
|
|
89
|
+
export interface AdminRpcRequest<A = unknown> {
|
|
90
|
+
args?: A;
|
|
91
|
+
}
|
|
92
|
+
/** Success metadata attached to every ok response. */
|
|
93
|
+
export interface AdminResponseMeta {
|
|
94
|
+
/** Server-measured handling time in milliseconds. */
|
|
95
|
+
ms: number;
|
|
96
|
+
op: string;
|
|
97
|
+
mode: 'read' | 'write';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The dispatch response, discriminated on `ok`. Narrowing on `ok === true`
|
|
101
|
+
* exposes `data` + `meta`; `ok === false` exposes `error` + `status`.
|
|
102
|
+
*/
|
|
103
|
+
export type AdminRpcResponse<R = unknown> = {
|
|
104
|
+
ok: true;
|
|
105
|
+
op: string;
|
|
106
|
+
data: R;
|
|
107
|
+
meta: AdminResponseMeta;
|
|
108
|
+
} | {
|
|
109
|
+
ok: false;
|
|
110
|
+
op: string;
|
|
111
|
+
error: AdminErrorBody;
|
|
112
|
+
status: number;
|
|
113
|
+
};
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/data.d.ts
|
|
116
|
+
/**
|
|
117
|
+
* Data-browser wire contract. Model discovery + row access shapes.
|
|
118
|
+
*
|
|
119
|
+
* `StudioFilterOperator` is a compatible subset of `@velajs/crud`'s
|
|
120
|
+
* `FilterOperator` (`crud/packages/core/src/adapter/query-types.ts`), and
|
|
121
|
+
* `StudioPageInfo` is a structural mirror of crud's `PageInfo` (snake_case
|
|
122
|
+
* preserved). Drift guards against the real crud package land in the server
|
|
123
|
+
* package; see the report for mirror source paths.
|
|
124
|
+
*/
|
|
125
|
+
/**
|
|
126
|
+
* Grid filter operators — a subset of crud's `FILTER_OPERATORS`, chosen so every
|
|
127
|
+
* member is assignment-compatible with crud's `FilterOperator`. `const` array +
|
|
128
|
+
* derived union so compile-time type and runtime membership can't drift.
|
|
129
|
+
*/
|
|
130
|
+
export declare const STUDIO_FILTER_OPERATORS: readonly ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'like', 'ilike', 'null', 'between'];
|
|
131
|
+
/** Union of every supported grid filter operator. */
|
|
132
|
+
export type StudioFilterOperator = (typeof STUDIO_FILTER_OPERATORS)[number];
|
|
133
|
+
/** One grid filter clause. */
|
|
134
|
+
export interface StudioGridFilter {
|
|
135
|
+
field: string;
|
|
136
|
+
operator: StudioFilterOperator;
|
|
137
|
+
value: unknown;
|
|
138
|
+
}
|
|
139
|
+
/** A single column in a model, derived from the model schema. */
|
|
140
|
+
export interface StudioColumn {
|
|
141
|
+
name: string;
|
|
142
|
+
type: 'string' | 'number' | 'boolean' | 'date' | 'json' | 'unknown';
|
|
143
|
+
pk: boolean;
|
|
144
|
+
nullable: boolean;
|
|
145
|
+
unique: boolean;
|
|
146
|
+
/** Foreign-key reference, from `model.relations` foreignKey. */
|
|
147
|
+
fk?: {
|
|
148
|
+
table: string;
|
|
149
|
+
relation: string;
|
|
150
|
+
};
|
|
151
|
+
/** Managed field: timestamps / soft-delete / tenant. */
|
|
152
|
+
managed: boolean;
|
|
153
|
+
}
|
|
154
|
+
/** Lightweight model listing entry. */
|
|
155
|
+
export interface StudioModelInfo {
|
|
156
|
+
name: string;
|
|
157
|
+
table: string;
|
|
158
|
+
label: string;
|
|
159
|
+
/** Adapter capability names available for this model (e.g. `search`, `aggregate`). */
|
|
160
|
+
capabilities: string[];
|
|
161
|
+
}
|
|
162
|
+
/** Full descriptor for a single model. */
|
|
163
|
+
export interface StudioModelDescriptor {
|
|
164
|
+
name: string;
|
|
165
|
+
table: string;
|
|
166
|
+
primaryKeys: string[];
|
|
167
|
+
columns: StudioColumn[];
|
|
168
|
+
relations: Array<{
|
|
169
|
+
name: string;
|
|
170
|
+
type: 'hasOne' | 'hasMany' | 'belongsTo';
|
|
171
|
+
target: string;
|
|
172
|
+
foreignKey: string;
|
|
173
|
+
cascade?: string;
|
|
174
|
+
}>;
|
|
175
|
+
flags: {
|
|
176
|
+
softDelete: boolean;
|
|
177
|
+
multiTenant: boolean;
|
|
178
|
+
versioning: boolean;
|
|
179
|
+
audit: boolean;
|
|
180
|
+
};
|
|
181
|
+
supports: {
|
|
182
|
+
bulkWrites: boolean;
|
|
183
|
+
facets: boolean;
|
|
184
|
+
search: boolean;
|
|
185
|
+
cascade: boolean;
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/** Request to list rows of a model. */
|
|
189
|
+
export interface ListRowsRequest {
|
|
190
|
+
model: string;
|
|
191
|
+
filters?: StudioGridFilter[];
|
|
192
|
+
sort?: {
|
|
193
|
+
field: string;
|
|
194
|
+
order: 'asc' | 'desc';
|
|
195
|
+
};
|
|
196
|
+
page?: number;
|
|
197
|
+
perPage?: number;
|
|
198
|
+
cursor?: string;
|
|
199
|
+
search?: string;
|
|
200
|
+
withDeleted?: boolean;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Pagination metadata — structural mirror of `@velajs/crud` `PageInfo`
|
|
204
|
+
* (snake_case field names preserved for faithful mirroring).
|
|
205
|
+
*/
|
|
206
|
+
export interface StudioPageInfo {
|
|
207
|
+
page: number;
|
|
208
|
+
per_page: number;
|
|
209
|
+
total_count?: number;
|
|
210
|
+
total_pages?: number;
|
|
211
|
+
has_next_page: boolean;
|
|
212
|
+
has_prev_page: boolean;
|
|
213
|
+
/** Next-page cursor (keyset pagination). Walks are next-only. */
|
|
214
|
+
next_cursor?: string;
|
|
215
|
+
}
|
|
216
|
+
/** A page of untyped rows plus pagination metadata. */
|
|
217
|
+
export interface StudioRowPage {
|
|
218
|
+
rows: Array<Record<string, unknown>>;
|
|
219
|
+
info: StudioPageInfo;
|
|
220
|
+
}
|
|
221
|
+
/** Request to write (create or patch) a single row. */
|
|
222
|
+
export interface WriteRowRequest {
|
|
223
|
+
model: string;
|
|
224
|
+
/** Present for an update; absent for a create. */
|
|
225
|
+
id?: string;
|
|
226
|
+
patch: Record<string, unknown>;
|
|
227
|
+
confirmToken?: string;
|
|
228
|
+
}
|
|
229
|
+
/** Request to delete rows (soft or hard). Destructive: `confirmToken` required. */
|
|
230
|
+
export interface DeleteRowsRequest {
|
|
231
|
+
model: string;
|
|
232
|
+
ids: string[];
|
|
233
|
+
mode: 'soft' | 'hard';
|
|
234
|
+
confirmToken: string;
|
|
235
|
+
}
|
|
236
|
+
/** Request to clear an entire table. Destructive: `confirmToken` required. */
|
|
237
|
+
export interface ClearTableRequest {
|
|
238
|
+
model: string;
|
|
239
|
+
confirmToken: string;
|
|
240
|
+
}
|
|
241
|
+
/** Request faceted counts for a field. */
|
|
242
|
+
export interface FacetsRequest {
|
|
243
|
+
model: string;
|
|
244
|
+
field: string;
|
|
245
|
+
filters?: StudioGridFilter[];
|
|
246
|
+
limit?: number;
|
|
247
|
+
}
|
|
248
|
+
/** Faceted count buckets. */
|
|
249
|
+
export interface FacetsResponse {
|
|
250
|
+
buckets: Array<{
|
|
251
|
+
value: unknown;
|
|
252
|
+
count: number;
|
|
253
|
+
}>;
|
|
254
|
+
}
|
|
255
|
+
/** Request a cascade-delete preview for the given rows. */
|
|
256
|
+
export interface CascadePreviewRequest {
|
|
257
|
+
model: string;
|
|
258
|
+
ids: string[];
|
|
259
|
+
}
|
|
260
|
+
/** The relations a cascade delete would touch. */
|
|
261
|
+
export interface CascadePreviewResponse {
|
|
262
|
+
relations: Array<{
|
|
263
|
+
relation: string;
|
|
264
|
+
target: string;
|
|
265
|
+
action: string;
|
|
266
|
+
affected: number;
|
|
267
|
+
}>;
|
|
268
|
+
}
|
|
269
|
+
/** Request to generate synthetic rows. */
|
|
270
|
+
export interface GenerateRowsRequest {
|
|
271
|
+
model: string;
|
|
272
|
+
count: number;
|
|
273
|
+
overrides?: Record<string, unknown>;
|
|
274
|
+
}
|
|
275
|
+
/** Result of a generate-rows write. */
|
|
276
|
+
export interface GenerateRowsResponse {
|
|
277
|
+
inserted: number;
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/app.d.ts
|
|
281
|
+
/**
|
|
282
|
+
* Application-introspection wire contract (the `app.*` and `api.authorizeTryIt` ops).
|
|
283
|
+
*
|
|
284
|
+
* `RouteRow` mirrors `@velajs/cli` `introspect.ts` `RouteRow`, and `ModuleNode`
|
|
285
|
+
* mirrors vela's `ModuleDescription` (`vela/src/container/types.ts`) field-for-
|
|
286
|
+
* field. `EntrypointRow` intentionally carries `meta?: unknown` (the wire shape)
|
|
287
|
+
* rather than the CLI's serialized `meta: string`. See the report for sources.
|
|
288
|
+
*/
|
|
289
|
+
/** One row of the app route table. Mirrors `@velajs/cli` `RouteRow`. */
|
|
290
|
+
export interface RouteRow {
|
|
291
|
+
method: string;
|
|
292
|
+
path: string;
|
|
293
|
+
/** `Controller#handler`, or `(mounted)` for routes vela did not compose itself. */
|
|
294
|
+
handler: string;
|
|
295
|
+
source: 'controller' | 'mounted';
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* One module instance in the loaded graph. Structural mirror of vela's
|
|
299
|
+
* `ModuleDescription` (`Container.getModuleDescriptions()`).
|
|
300
|
+
*/
|
|
301
|
+
export interface ModuleNode {
|
|
302
|
+
moduleId: string;
|
|
303
|
+
/** moduleIds this instance imports. */
|
|
304
|
+
imports: string[];
|
|
305
|
+
isGlobal: boolean;
|
|
306
|
+
lazy: boolean;
|
|
307
|
+
/** Token labels registered in this instance's bucket (registration order). */
|
|
308
|
+
providers: string[];
|
|
309
|
+
/** Token labels this instance exports. */
|
|
310
|
+
exports: string[];
|
|
311
|
+
}
|
|
312
|
+
/** One entrypoint entry (queue/cron/etc.). Wire shape: `meta` is optional/unknown. */
|
|
313
|
+
export interface EntrypointRow {
|
|
314
|
+
kind: string;
|
|
315
|
+
target: string;
|
|
316
|
+
meta?: unknown;
|
|
317
|
+
}
|
|
318
|
+
/** An API "try it" request proxied against the app. */
|
|
319
|
+
export interface TryItRequest {
|
|
320
|
+
method: string;
|
|
321
|
+
path: string;
|
|
322
|
+
query?: Record<string, string>;
|
|
323
|
+
headers?: Record<string, string>;
|
|
324
|
+
body?: unknown;
|
|
325
|
+
}
|
|
326
|
+
/** The response captured from a "try it" request. */
|
|
327
|
+
export interface TryItResponse {
|
|
328
|
+
status: number;
|
|
329
|
+
headers: Record<string, string>;
|
|
330
|
+
body: unknown;
|
|
331
|
+
}
|
|
332
|
+
/** Validate JSON from the local API explorer before any request can be sent. */
|
|
333
|
+
export declare function parseTryItRequest(value: unknown): TryItRequest;
|
|
334
|
+
export declare function parseTryItResponse(value: unknown): TryItResponse;
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/panels.d.ts
|
|
337
|
+
/**
|
|
338
|
+
* Read-panel row types for the package-backed op sets (auth / queue / schedule /
|
|
339
|
+
* flags / logs / live / presence / audit / transfer / overview).
|
|
340
|
+
*
|
|
341
|
+
* Kept MINIMAL and honest: only the fields the panels actually need. Field names
|
|
342
|
+
* are chosen to match the real backing services later ops call (`@velajs/auth`,
|
|
343
|
+
* `@velajs/feature-flags`, `vela/src/queue`, `vela/src/schedule*`) so they are
|
|
344
|
+
* not fantasy; the server package narrows them to concrete service reads.
|
|
345
|
+
*/
|
|
346
|
+
/** A user row for the auth users panel. */
|
|
347
|
+
export interface AuthUserRow {
|
|
348
|
+
id: string;
|
|
349
|
+
email: string;
|
|
350
|
+
name?: string;
|
|
351
|
+
emailVerified: boolean;
|
|
352
|
+
image?: string;
|
|
353
|
+
role?: string;
|
|
354
|
+
banned?: boolean;
|
|
355
|
+
createdAt: number;
|
|
356
|
+
}
|
|
357
|
+
/** A session row for the auth sessions panel. */
|
|
358
|
+
export interface AuthSessionRow {
|
|
359
|
+
id: string;
|
|
360
|
+
userId: string;
|
|
361
|
+
createdAt: number;
|
|
362
|
+
expiresAt: number;
|
|
363
|
+
ipAddress?: string;
|
|
364
|
+
userAgent?: string;
|
|
365
|
+
}
|
|
366
|
+
/** An organization row (better-auth organization plugin). */
|
|
367
|
+
export interface AuthOrgRow {
|
|
368
|
+
id: string;
|
|
369
|
+
name: string;
|
|
370
|
+
slug?: string;
|
|
371
|
+
memberCount?: number;
|
|
372
|
+
createdAt: number;
|
|
373
|
+
}
|
|
374
|
+
/** The detail view for a single user. */
|
|
375
|
+
export interface AuthUserDetail {
|
|
376
|
+
user: AuthUserRow;
|
|
377
|
+
sessions: AuthSessionRow[];
|
|
378
|
+
organizations: AuthOrgRow[];
|
|
379
|
+
}
|
|
380
|
+
/** A queue row for the queues panel. */
|
|
381
|
+
export interface QueueRow {
|
|
382
|
+
name: string;
|
|
383
|
+
kind: string;
|
|
384
|
+
depth?: number;
|
|
385
|
+
}
|
|
386
|
+
/** A live queue-depth reading. */
|
|
387
|
+
export interface QueueDepthRow {
|
|
388
|
+
name: string;
|
|
389
|
+
depth: number;
|
|
390
|
+
inFlight?: number;
|
|
391
|
+
}
|
|
392
|
+
/** A dead-letter-queue entry. */
|
|
393
|
+
export interface DlqEntryRow {
|
|
394
|
+
id: string;
|
|
395
|
+
queue: string;
|
|
396
|
+
failedAt: number;
|
|
397
|
+
attempts: number;
|
|
398
|
+
error?: string;
|
|
399
|
+
payload?: unknown;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* A scheduled job row. Mirrors vela's `ScheduleJobRef` (`kind` + `expression?` /
|
|
403
|
+
* `ms?`). `ScheduleJobRef` carries no name or run timestamps, so `name`,
|
|
404
|
+
* `lastRun`, and `nextRun` are all server-synthesized around the driver read.
|
|
405
|
+
*/
|
|
406
|
+
export interface ScheduleJobRow {
|
|
407
|
+
/** Server-synthesized display name (derived from the job's `methodName`). */
|
|
408
|
+
name: string;
|
|
409
|
+
kind: 'cron' | 'interval';
|
|
410
|
+
/** Cron expression (`kind: 'cron'`). */
|
|
411
|
+
expression?: string;
|
|
412
|
+
/** Interval period in ms (`kind: 'interval'`). */
|
|
413
|
+
ms?: number;
|
|
414
|
+
/** Server-synthesized: last fire time (epoch ms). */
|
|
415
|
+
lastRun?: number;
|
|
416
|
+
/** Server-synthesized: next scheduled fire time (epoch ms). */
|
|
417
|
+
nextRun?: number;
|
|
418
|
+
}
|
|
419
|
+
/** A declared cron trigger. */
|
|
420
|
+
export interface CronTriggerRow {
|
|
421
|
+
name: string;
|
|
422
|
+
cron: string;
|
|
423
|
+
nextRun?: number;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* A value a feature flag can resolve to. A local structural mirror of
|
|
427
|
+
* `@velajs/feature-flags`' `FlagValue` (`boolean | string | number | object`) —
|
|
428
|
+
* mirrored, never imported, so the wire contract is independent of that package.
|
|
429
|
+
*/
|
|
430
|
+
export type FlagValue = boolean | string | number | object;
|
|
431
|
+
/**
|
|
432
|
+
* Why a flag evaluation returned the value it did. A local structural mirror of
|
|
433
|
+
* `@velajs/feature-flags`' `FlagEvaluationReason` union (its members, exactly).
|
|
434
|
+
*/
|
|
435
|
+
export type FlagEvaluationReason = 'STATIC' | 'DEFAULT' | 'ERROR';
|
|
436
|
+
/**
|
|
437
|
+
* A feature-flag row. A flag resolves to a {@link FlagValue}, not a boolean —
|
|
438
|
+
* the driver has no `enabled`/`description` fields to back those, so the row
|
|
439
|
+
* carries only the key and its resolved value.
|
|
440
|
+
*/
|
|
441
|
+
export interface FlagRow {
|
|
442
|
+
key: string;
|
|
443
|
+
value: FlagValue;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* The result of evaluating a flag against a context. Mirrors
|
|
447
|
+
* `@velajs/feature-flags`' `FlagEvaluationDetails`: `flagKey`, the resolved
|
|
448
|
+
* {@link FlagValue}, a required {@link FlagEvaluationReason}, and an optional
|
|
449
|
+
* `errorMessage` present only on `reason: 'ERROR'`.
|
|
450
|
+
*/
|
|
451
|
+
export interface FlagEvaluation {
|
|
452
|
+
flagKey: string;
|
|
453
|
+
value: FlagValue;
|
|
454
|
+
reason: FlagEvaluationReason;
|
|
455
|
+
errorMessage?: string;
|
|
456
|
+
}
|
|
457
|
+
/** A captured application log line. */
|
|
458
|
+
export interface AdminLogEntry {
|
|
459
|
+
ts: number;
|
|
460
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
461
|
+
msg: string;
|
|
462
|
+
source?: string;
|
|
463
|
+
fields?: Record<string, unknown>;
|
|
464
|
+
}
|
|
465
|
+
/** An active live subscription. */
|
|
466
|
+
export interface LiveSubscriptionRow {
|
|
467
|
+
id: string;
|
|
468
|
+
room: string;
|
|
469
|
+
tags: string[];
|
|
470
|
+
connectedAt: number;
|
|
471
|
+
clientId?: string;
|
|
472
|
+
}
|
|
473
|
+
/** A presence room occupancy row. */
|
|
474
|
+
export interface PresenceRoomRow {
|
|
475
|
+
room: string;
|
|
476
|
+
count: number;
|
|
477
|
+
members?: string[];
|
|
478
|
+
}
|
|
479
|
+
/** One recorded admin audit row (mirrors the server's `AdminAuditEntry`). */
|
|
480
|
+
export interface AdminAuditEntry {
|
|
481
|
+
ts: number;
|
|
482
|
+
op: string;
|
|
483
|
+
mode: 'read' | 'write';
|
|
484
|
+
subject: string;
|
|
485
|
+
status: number;
|
|
486
|
+
ms: number;
|
|
487
|
+
ip: string | null;
|
|
488
|
+
detail?: {
|
|
489
|
+
target?: string;
|
|
490
|
+
summary?: string;
|
|
491
|
+
extra?: Record<string, unknown>;
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
/** The result of a transfer export (a downloadable NDJSON URL). */
|
|
495
|
+
export interface TransferExportResult {
|
|
496
|
+
exportUrl: string;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* A transfer import request. Destructive (bulk ingest): `confirmToken` required,
|
|
500
|
+
* so it satisfies the destructive-op contract.
|
|
501
|
+
*/
|
|
502
|
+
export interface TransferImportRequest {
|
|
503
|
+
model: string;
|
|
504
|
+
ndjson: string;
|
|
505
|
+
confirmToken: string;
|
|
506
|
+
}
|
|
507
|
+
/** The result of a transfer import. */
|
|
508
|
+
export interface TransferImportResult {
|
|
509
|
+
imported: number;
|
|
510
|
+
errors: Array<{
|
|
511
|
+
line: number;
|
|
512
|
+
message: string;
|
|
513
|
+
}>;
|
|
514
|
+
}
|
|
515
|
+
/** A minimal health + counts summary for the overview panel. */
|
|
516
|
+
export interface OverviewSummary {
|
|
517
|
+
status: 'ok' | 'degraded';
|
|
518
|
+
uptimeMs?: number;
|
|
519
|
+
counts: {
|
|
520
|
+
models?: number;
|
|
521
|
+
routes?: number;
|
|
522
|
+
modules?: number;
|
|
523
|
+
queues?: number;
|
|
524
|
+
scheduledJobs?: number;
|
|
525
|
+
flags?: number;
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/time-travel.d.ts
|
|
530
|
+
/**
|
|
531
|
+
* Time-travel wire contract — adopted verbatim (in concept) from the
|
|
532
|
+
* `timetravel.contracts` design slice. Pure types only: the `TimeTravelPort`
|
|
533
|
+
* interface lives here, but its DI token is declared in the server package.
|
|
534
|
+
*
|
|
535
|
+
* Marks are opaque (a CF DO bookmark string, or a portable manifest id). The
|
|
536
|
+
* lifecycle is preview -> confirmToken -> arm -> undo; unavailable environments
|
|
537
|
+
* surface `TIMETRAVEL_UNAVAILABLE` (409).
|
|
538
|
+
*/
|
|
539
|
+
/** How precisely a port can address a point in time. */
|
|
540
|
+
export type TimeTravelGranularity = 'bookmark' | 'snapshot' | 'snapshot+cdc';
|
|
541
|
+
/**
|
|
542
|
+
* Capability negotiation shape the UI reads for defaults-shown / hide-on-disabled
|
|
543
|
+
* affordances. Both adapters expose their full power here rather than collapsing
|
|
544
|
+
* to a lowest common denominator.
|
|
545
|
+
*/
|
|
546
|
+
export interface TimeTravelCapabilities {
|
|
547
|
+
/** `getMarkForTime` supported. */
|
|
548
|
+
markByTime: boolean;
|
|
549
|
+
/** `listMarks` supported (portable: yes; CF DO: no). */
|
|
550
|
+
list: boolean;
|
|
551
|
+
/** `armRestore` returns an undo mark. */
|
|
552
|
+
undo: boolean;
|
|
553
|
+
/** Restore applies to the live store. */
|
|
554
|
+
inPlace: boolean;
|
|
555
|
+
/** CF DO: applies only on restart/abort. */
|
|
556
|
+
restartRequired: boolean;
|
|
557
|
+
/** A snapshot can be downloaded off-platform. */
|
|
558
|
+
portableExport: boolean;
|
|
559
|
+
/** `createSnapshot` supported. */
|
|
560
|
+
createOnDemand: boolean;
|
|
561
|
+
/** Addressing granularity of this port. */
|
|
562
|
+
granularity: TimeTravelGranularity;
|
|
563
|
+
/** Human, honest note on what a restore actually covers. */
|
|
564
|
+
scopeNote: string;
|
|
565
|
+
}
|
|
566
|
+
/** Selects the dataset / log-scope to operate on. */
|
|
567
|
+
export interface TimeTravelScope {
|
|
568
|
+
/** Portable: a named table-group (`'default'` = all managed). CF: the DO room/name. */
|
|
569
|
+
dataset?: string;
|
|
570
|
+
}
|
|
571
|
+
/** An opaque, addressable point in time. */
|
|
572
|
+
export interface TimeTravelMark {
|
|
573
|
+
/** Opaque id: a CF bookmark string or a portable manifest id. */
|
|
574
|
+
id: string;
|
|
575
|
+
kind: 'bookmark' | 'snapshot';
|
|
576
|
+
/** Epoch-ms when known. */
|
|
577
|
+
time?: number;
|
|
578
|
+
label?: string;
|
|
579
|
+
/** Portable snapshots only. */
|
|
580
|
+
schemaHash?: string;
|
|
581
|
+
sizeBytes?: number;
|
|
582
|
+
/** Portable: the tables captured by this mark. */
|
|
583
|
+
tables?: string[];
|
|
584
|
+
}
|
|
585
|
+
/** A page of marks (cursor pagination; next-only). */
|
|
586
|
+
export interface TimeTravelMarkPage {
|
|
587
|
+
marks: TimeTravelMark[];
|
|
588
|
+
nextCursor?: string;
|
|
589
|
+
}
|
|
590
|
+
/** Addresses a restore target — an explicit mark id or a point in time. */
|
|
591
|
+
export interface RestoreTarget {
|
|
592
|
+
/** Explicit mark id (e.g. an undo mark) — wins over `time`. */
|
|
593
|
+
bookmark?: string;
|
|
594
|
+
/** Epoch-ms or ISO string. */
|
|
595
|
+
time?: number | string;
|
|
596
|
+
}
|
|
597
|
+
/** The result of a restore preview; `confirmToken` must be echoed to `armRestore`. */
|
|
598
|
+
export interface RestorePreview {
|
|
599
|
+
target: TimeTravelMark;
|
|
600
|
+
affectedTables: Array<{
|
|
601
|
+
table: string;
|
|
602
|
+
approxRows?: number;
|
|
603
|
+
}>;
|
|
604
|
+
/** `manifest.schemaHash === current`. */
|
|
605
|
+
schemaCompatible: boolean;
|
|
606
|
+
incompatibleTables: string[];
|
|
607
|
+
undoAvailable: boolean;
|
|
608
|
+
restartRequired: boolean;
|
|
609
|
+
/** Single-use token bound to (mark, tables, schemaHash); echo to `armRestore`. */
|
|
610
|
+
confirmToken: string;
|
|
611
|
+
/** Epoch-ms after which the `confirmToken` is rejected. */
|
|
612
|
+
expiresAt: number;
|
|
613
|
+
}
|
|
614
|
+
/** A confirmed restore request. */
|
|
615
|
+
export interface RestoreRequest extends RestoreTarget {
|
|
616
|
+
scope?: TimeTravelScope;
|
|
617
|
+
/** CF: also `ctx.abort()` to apply now. */
|
|
618
|
+
restart?: boolean;
|
|
619
|
+
/** Override a schema mismatch (dev-host only). */
|
|
620
|
+
force?: boolean;
|
|
621
|
+
/** The token echoed from {@link RestorePreview}. */
|
|
622
|
+
confirmToken: string;
|
|
623
|
+
}
|
|
624
|
+
/** The outcome of an armed restore. */
|
|
625
|
+
export interface RestoreOutcome {
|
|
626
|
+
/** Target mark id. */
|
|
627
|
+
restoredTo: string;
|
|
628
|
+
/** Restore to this mark to undo. */
|
|
629
|
+
undoMark?: TimeTravelMark;
|
|
630
|
+
/** `false` when armed-for-next-restart (CF, `restart: false`). */
|
|
631
|
+
applied: boolean;
|
|
632
|
+
restartRequested: boolean;
|
|
633
|
+
}
|
|
634
|
+
/** Snapshot retention policy for pruning. */
|
|
635
|
+
export interface RetentionPolicy {
|
|
636
|
+
keepLast?: number;
|
|
637
|
+
maxAgeMs?: number;
|
|
638
|
+
}
|
|
639
|
+
/** A portable snapshot manifest (adapter B). */
|
|
640
|
+
export interface SnapshotManifest {
|
|
641
|
+
id: string;
|
|
642
|
+
createdAt: number;
|
|
643
|
+
label?: string;
|
|
644
|
+
tables: Array<{
|
|
645
|
+
table: string;
|
|
646
|
+
rows: number;
|
|
647
|
+
schemaHash: string;
|
|
648
|
+
ndjsonKey: string;
|
|
649
|
+
}>;
|
|
650
|
+
/** Set when a change source captured a window. */
|
|
651
|
+
changeLog?: {
|
|
652
|
+
fromTs: number;
|
|
653
|
+
toTs: number;
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
/** A single CDC record kind. */
|
|
657
|
+
export type StudioChangeKind = 'insert' | 'update' | 'delete';
|
|
658
|
+
/** A single committed change (CDC replay input). */
|
|
659
|
+
export interface StudioChange {
|
|
660
|
+
ts: number;
|
|
661
|
+
table: string;
|
|
662
|
+
kind: StudioChangeKind;
|
|
663
|
+
key: Record<string, unknown>;
|
|
664
|
+
before?: Record<string, unknown>;
|
|
665
|
+
after?: Record<string, unknown>;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* The time-travel seam. Two adapters implement it: the portable NDJSON
|
|
669
|
+
* snapshot/replay adapter and the CF-native DO PITR bookmark adapter. Declared
|
|
670
|
+
* here as pure types; the `TIME_TRAVEL_PORT` DI token lives in the server package.
|
|
671
|
+
*/
|
|
672
|
+
export interface TimeTravelPort {
|
|
673
|
+
/** `'cf-do-pitr'` | `'portable-snapshot'`. */
|
|
674
|
+
readonly id: string;
|
|
675
|
+
capabilities(scope?: TimeTravelScope): TimeTravelCapabilities;
|
|
676
|
+
getCurrentMark(scope?: TimeTravelScope): Promise<TimeTravelMark>;
|
|
677
|
+
getMarkForTime?(time: number | string, scope?: TimeTravelScope): Promise<TimeTravelMark | null>;
|
|
678
|
+
listMarks?(scope?: TimeTravelScope, opts?: {
|
|
679
|
+
limit?: number;
|
|
680
|
+
before?: string;
|
|
681
|
+
}): Promise<TimeTravelMarkPage>;
|
|
682
|
+
preview(target: RestoreTarget, scope?: TimeTravelScope): Promise<RestorePreview>;
|
|
683
|
+
armRestore(req: RestoreRequest): Promise<RestoreOutcome>;
|
|
684
|
+
createSnapshot?(opts?: {
|
|
685
|
+
scope?: TimeTravelScope;
|
|
686
|
+
label?: string;
|
|
687
|
+
}): Promise<TimeTravelMark>;
|
|
688
|
+
exportSnapshot?(markId: string): Promise<ReadableStream<Uint8Array>>;
|
|
689
|
+
prune?(retention: RetentionPolicy, scope?: TimeTravelScope): Promise<{
|
|
690
|
+
pruned: number;
|
|
691
|
+
}>;
|
|
692
|
+
}
|
|
693
|
+
//#endregion
|
|
694
|
+
//#region src/ops.d.ts
|
|
695
|
+
/** A request that carries no arguments. */
|
|
696
|
+
export type EmptyArgs = Record<string, never>;
|
|
697
|
+
/**
|
|
698
|
+
* The closed catalog of every Studio op, keyed by op name. Each entry declares
|
|
699
|
+
* its `req` (dispatch args) and `res` (result) type.
|
|
700
|
+
*/
|
|
701
|
+
export interface StudioRpcMap {
|
|
702
|
+
'studio.capabilities': {
|
|
703
|
+
req: EmptyArgs;
|
|
704
|
+
res: StudioCapabilities;
|
|
705
|
+
};
|
|
706
|
+
'app.routes': {
|
|
707
|
+
req: EmptyArgs;
|
|
708
|
+
res: RouteRow[];
|
|
709
|
+
};
|
|
710
|
+
'app.modules': {
|
|
711
|
+
req: EmptyArgs;
|
|
712
|
+
res: ModuleNode[];
|
|
713
|
+
};
|
|
714
|
+
'app.entrypoints': {
|
|
715
|
+
req: EmptyArgs;
|
|
716
|
+
res: EntrypointRow[];
|
|
717
|
+
};
|
|
718
|
+
'app.openapi': {
|
|
719
|
+
req: EmptyArgs;
|
|
720
|
+
res: unknown;
|
|
721
|
+
};
|
|
722
|
+
'api.authorizeTryIt': {
|
|
723
|
+
req: TryItRequest;
|
|
724
|
+
res: {
|
|
725
|
+
authorized: true;
|
|
726
|
+
};
|
|
727
|
+
};
|
|
728
|
+
'data.listModels': {
|
|
729
|
+
req: EmptyArgs;
|
|
730
|
+
res: StudioModelInfo[];
|
|
731
|
+
};
|
|
732
|
+
'data.describeModel': {
|
|
733
|
+
req: {
|
|
734
|
+
model: string;
|
|
735
|
+
};
|
|
736
|
+
res: StudioModelDescriptor;
|
|
737
|
+
};
|
|
738
|
+
'data.listRows': {
|
|
739
|
+
req: ListRowsRequest;
|
|
740
|
+
res: StudioRowPage;
|
|
741
|
+
};
|
|
742
|
+
'data.readRow': {
|
|
743
|
+
req: {
|
|
744
|
+
model: string;
|
|
745
|
+
id: string;
|
|
746
|
+
};
|
|
747
|
+
res: Record<string, unknown> | null;
|
|
748
|
+
};
|
|
749
|
+
'data.facets': {
|
|
750
|
+
req: FacetsRequest;
|
|
751
|
+
res: FacetsResponse;
|
|
752
|
+
};
|
|
753
|
+
'data.cascadePreview': {
|
|
754
|
+
req: CascadePreviewRequest;
|
|
755
|
+
res: CascadePreviewResponse;
|
|
756
|
+
};
|
|
757
|
+
'data.writeRow': {
|
|
758
|
+
req: WriteRowRequest;
|
|
759
|
+
res: Record<string, unknown>;
|
|
760
|
+
};
|
|
761
|
+
'data.deleteRows': {
|
|
762
|
+
req: DeleteRowsRequest;
|
|
763
|
+
res: {
|
|
764
|
+
deleted: number;
|
|
765
|
+
};
|
|
766
|
+
};
|
|
767
|
+
'data.clearTable': {
|
|
768
|
+
req: ClearTableRequest;
|
|
769
|
+
res: {
|
|
770
|
+
deleted: number;
|
|
771
|
+
};
|
|
772
|
+
};
|
|
773
|
+
'data.generateRows': {
|
|
774
|
+
req: GenerateRowsRequest;
|
|
775
|
+
res: GenerateRowsResponse;
|
|
776
|
+
};
|
|
777
|
+
'timeTravel.capabilities': {
|
|
778
|
+
req: {
|
|
779
|
+
scope?: TimeTravelScope;
|
|
780
|
+
};
|
|
781
|
+
res: TimeTravelCapabilities;
|
|
782
|
+
};
|
|
783
|
+
'timeTravel.currentMark': {
|
|
784
|
+
req: {
|
|
785
|
+
scope?: TimeTravelScope;
|
|
786
|
+
};
|
|
787
|
+
res: TimeTravelMark;
|
|
788
|
+
};
|
|
789
|
+
'timeTravel.markForTime': {
|
|
790
|
+
req: {
|
|
791
|
+
time: number | string;
|
|
792
|
+
scope?: TimeTravelScope;
|
|
793
|
+
};
|
|
794
|
+
res: TimeTravelMark | null;
|
|
795
|
+
};
|
|
796
|
+
'timeTravel.listMarks': {
|
|
797
|
+
req: {
|
|
798
|
+
scope?: TimeTravelScope;
|
|
799
|
+
limit?: number;
|
|
800
|
+
before?: string;
|
|
801
|
+
};
|
|
802
|
+
res: TimeTravelMarkPage;
|
|
803
|
+
};
|
|
804
|
+
'timeTravel.preview': {
|
|
805
|
+
req: {
|
|
806
|
+
target: RestoreTarget;
|
|
807
|
+
scope?: TimeTravelScope;
|
|
808
|
+
};
|
|
809
|
+
res: RestorePreview;
|
|
810
|
+
};
|
|
811
|
+
'timeTravel.armRestore': {
|
|
812
|
+
req: RestoreRequest;
|
|
813
|
+
res: RestoreOutcome;
|
|
814
|
+
};
|
|
815
|
+
'timeTravel.undo': {
|
|
816
|
+
req: {
|
|
817
|
+
undoMark: string;
|
|
818
|
+
scope?: TimeTravelScope;
|
|
819
|
+
confirmToken: string;
|
|
820
|
+
};
|
|
821
|
+
res: RestoreOutcome;
|
|
822
|
+
};
|
|
823
|
+
'timeTravel.createSnapshot': {
|
|
824
|
+
req: {
|
|
825
|
+
scope?: TimeTravelScope;
|
|
826
|
+
label?: string;
|
|
827
|
+
};
|
|
828
|
+
res: TimeTravelMark;
|
|
829
|
+
};
|
|
830
|
+
'timeTravel.prune': {
|
|
831
|
+
req: {
|
|
832
|
+
retention: RetentionPolicy;
|
|
833
|
+
scope?: TimeTravelScope;
|
|
834
|
+
confirmToken: string;
|
|
835
|
+
};
|
|
836
|
+
res: {
|
|
837
|
+
pruned: number;
|
|
838
|
+
};
|
|
839
|
+
};
|
|
840
|
+
'transfer.export': {
|
|
841
|
+
req: {
|
|
842
|
+
model?: string;
|
|
843
|
+
};
|
|
844
|
+
res: TransferExportResult;
|
|
845
|
+
};
|
|
846
|
+
'transfer.import': {
|
|
847
|
+
req: TransferImportRequest;
|
|
848
|
+
res: TransferImportResult;
|
|
849
|
+
};
|
|
850
|
+
'auth.users': {
|
|
851
|
+
req: {
|
|
852
|
+
q?: string;
|
|
853
|
+
cursor?: string;
|
|
854
|
+
};
|
|
855
|
+
res: {
|
|
856
|
+
rows: AuthUserRow[];
|
|
857
|
+
nextCursor?: string;
|
|
858
|
+
};
|
|
859
|
+
};
|
|
860
|
+
'auth.userDetail': {
|
|
861
|
+
req: {
|
|
862
|
+
id: string;
|
|
863
|
+
};
|
|
864
|
+
res: AuthUserDetail;
|
|
865
|
+
};
|
|
866
|
+
'auth.sessions': {
|
|
867
|
+
req: {
|
|
868
|
+
userId?: string;
|
|
869
|
+
};
|
|
870
|
+
res: AuthSessionRow[];
|
|
871
|
+
};
|
|
872
|
+
'auth.revokeSession': {
|
|
873
|
+
req: {
|
|
874
|
+
sessionId: string;
|
|
875
|
+
};
|
|
876
|
+
res: {
|
|
877
|
+
ok: true;
|
|
878
|
+
};
|
|
879
|
+
};
|
|
880
|
+
'auth.organizations': {
|
|
881
|
+
req: EmptyArgs;
|
|
882
|
+
res: AuthOrgRow[];
|
|
883
|
+
};
|
|
884
|
+
'queue.list': {
|
|
885
|
+
req: EmptyArgs;
|
|
886
|
+
res: QueueRow[];
|
|
887
|
+
};
|
|
888
|
+
'queue.depths': {
|
|
889
|
+
req: EmptyArgs;
|
|
890
|
+
res: QueueDepthRow[];
|
|
891
|
+
};
|
|
892
|
+
'queue.dlq': {
|
|
893
|
+
req: {
|
|
894
|
+
queue: string;
|
|
895
|
+
};
|
|
896
|
+
res: DlqEntryRow[];
|
|
897
|
+
};
|
|
898
|
+
'queue.send': {
|
|
899
|
+
req: {
|
|
900
|
+
queue: string;
|
|
901
|
+
payload: unknown;
|
|
902
|
+
};
|
|
903
|
+
res: {
|
|
904
|
+
id: string;
|
|
905
|
+
};
|
|
906
|
+
};
|
|
907
|
+
'queue.replay': {
|
|
908
|
+
req: {
|
|
909
|
+
queue: string;
|
|
910
|
+
ids: string[];
|
|
911
|
+
};
|
|
912
|
+
res: {
|
|
913
|
+
replayed: number;
|
|
914
|
+
};
|
|
915
|
+
};
|
|
916
|
+
'schedule.jobs': {
|
|
917
|
+
req: EmptyArgs;
|
|
918
|
+
res: ScheduleJobRow[];
|
|
919
|
+
};
|
|
920
|
+
'schedule.triggers': {
|
|
921
|
+
req: EmptyArgs;
|
|
922
|
+
res: CronTriggerRow[];
|
|
923
|
+
};
|
|
924
|
+
'schedule.runNow': {
|
|
925
|
+
req: {
|
|
926
|
+
id: string;
|
|
927
|
+
};
|
|
928
|
+
res: {
|
|
929
|
+
ok: true;
|
|
930
|
+
};
|
|
931
|
+
};
|
|
932
|
+
'flags.list': {
|
|
933
|
+
req: EmptyArgs;
|
|
934
|
+
res: FlagRow[];
|
|
935
|
+
};
|
|
936
|
+
'flags.evaluate': {
|
|
937
|
+
req: {
|
|
938
|
+
key: string;
|
|
939
|
+
context?: Record<string, unknown>;
|
|
940
|
+
};
|
|
941
|
+
res: FlagEvaluation;
|
|
942
|
+
};
|
|
943
|
+
'logs.tail': {
|
|
944
|
+
req: {
|
|
945
|
+
level?: AdminLogEntry['level'];
|
|
946
|
+
limit?: number;
|
|
947
|
+
};
|
|
948
|
+
res: AdminLogEntry[];
|
|
949
|
+
};
|
|
950
|
+
'live.subscriptions': {
|
|
951
|
+
req: EmptyArgs;
|
|
952
|
+
res: LiveSubscriptionRow[];
|
|
953
|
+
};
|
|
954
|
+
'presence.rooms': {
|
|
955
|
+
req: EmptyArgs;
|
|
956
|
+
res: PresenceRoomRow[];
|
|
957
|
+
};
|
|
958
|
+
'audit.tail': {
|
|
959
|
+
req: {
|
|
960
|
+
limit?: number;
|
|
961
|
+
};
|
|
962
|
+
res: AdminAuditEntry[];
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
/** The union of every op name. */
|
|
966
|
+
export type StudioOp = keyof StudioRpcMap;
|
|
967
|
+
/** The `req` type for an op. */
|
|
968
|
+
export type StudioOpReq<Op extends StudioOp> = StudioRpcMap[Op]['req'];
|
|
969
|
+
/** The `res` type for an op. */
|
|
970
|
+
export type StudioOpRes<Op extends StudioOp> = StudioRpcMap[Op]['res'];
|
|
971
|
+
/**
|
|
972
|
+
* Runtime classification of a single op. Single source for dispatch
|
|
973
|
+
* (read/write), capability negotiation (`feature`), write-gating (`gate`), and
|
|
974
|
+
* destructive-confirm handling (`destructive`).
|
|
975
|
+
*/
|
|
976
|
+
export interface StudioOpMeta {
|
|
977
|
+
mode: 'read' | 'write';
|
|
978
|
+
feature: StudioFeatureKey;
|
|
979
|
+
gate?: keyof StudioWriteGates;
|
|
980
|
+
destructive?: true;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Op meta for every op. `as const satisfies` enforces total key coverage while
|
|
984
|
+
* preserving literal types (so `destructive` narrows exactly), which drives the
|
|
985
|
+
* type-level destructive-confirm guard in the tests.
|
|
986
|
+
*/
|
|
987
|
+
export declare const STUDIO_OP_META: {
|
|
988
|
+
readonly 'studio.capabilities': {
|
|
989
|
+
readonly mode: 'read';
|
|
990
|
+
readonly feature: 'app';
|
|
991
|
+
};
|
|
992
|
+
readonly 'app.routes': {
|
|
993
|
+
readonly mode: 'read';
|
|
994
|
+
readonly feature: 'app';
|
|
995
|
+
};
|
|
996
|
+
readonly 'app.modules': {
|
|
997
|
+
readonly mode: 'read';
|
|
998
|
+
readonly feature: 'app';
|
|
999
|
+
};
|
|
1000
|
+
readonly 'app.entrypoints': {
|
|
1001
|
+
readonly mode: 'read';
|
|
1002
|
+
readonly feature: 'app';
|
|
1003
|
+
};
|
|
1004
|
+
readonly 'app.openapi': {
|
|
1005
|
+
readonly mode: 'read';
|
|
1006
|
+
readonly feature: 'openapi';
|
|
1007
|
+
};
|
|
1008
|
+
/** Authorizes a host HTTP request; server gates and audits this operational action. */
|
|
1009
|
+
readonly 'api.authorizeTryIt': {
|
|
1010
|
+
readonly mode: 'write';
|
|
1011
|
+
readonly feature: 'openapi';
|
|
1012
|
+
readonly gate: 'opsEditable';
|
|
1013
|
+
};
|
|
1014
|
+
readonly 'data.listModels': {
|
|
1015
|
+
readonly mode: 'read';
|
|
1016
|
+
readonly feature: 'data';
|
|
1017
|
+
};
|
|
1018
|
+
readonly 'data.describeModel': {
|
|
1019
|
+
readonly mode: 'read';
|
|
1020
|
+
readonly feature: 'data';
|
|
1021
|
+
};
|
|
1022
|
+
readonly 'data.listRows': {
|
|
1023
|
+
readonly mode: 'read';
|
|
1024
|
+
readonly feature: 'data';
|
|
1025
|
+
};
|
|
1026
|
+
readonly 'data.readRow': {
|
|
1027
|
+
readonly mode: 'read';
|
|
1028
|
+
readonly feature: 'data';
|
|
1029
|
+
};
|
|
1030
|
+
readonly 'data.facets': {
|
|
1031
|
+
readonly mode: 'read';
|
|
1032
|
+
readonly feature: 'data';
|
|
1033
|
+
};
|
|
1034
|
+
readonly 'data.cascadePreview': {
|
|
1035
|
+
readonly mode: 'read';
|
|
1036
|
+
readonly feature: 'data';
|
|
1037
|
+
};
|
|
1038
|
+
readonly 'data.writeRow': {
|
|
1039
|
+
readonly mode: 'write';
|
|
1040
|
+
readonly feature: 'data';
|
|
1041
|
+
readonly gate: 'dataEditable';
|
|
1042
|
+
};
|
|
1043
|
+
readonly 'data.deleteRows': {
|
|
1044
|
+
readonly mode: 'write';
|
|
1045
|
+
readonly feature: 'data';
|
|
1046
|
+
readonly gate: 'dataEditable';
|
|
1047
|
+
readonly destructive: true;
|
|
1048
|
+
};
|
|
1049
|
+
readonly 'data.clearTable': {
|
|
1050
|
+
readonly mode: 'write';
|
|
1051
|
+
readonly feature: 'data';
|
|
1052
|
+
readonly gate: 'dataEditable';
|
|
1053
|
+
readonly destructive: true;
|
|
1054
|
+
};
|
|
1055
|
+
readonly 'data.generateRows': {
|
|
1056
|
+
readonly mode: 'write';
|
|
1057
|
+
readonly feature: 'data';
|
|
1058
|
+
readonly gate: 'dataEditable';
|
|
1059
|
+
};
|
|
1060
|
+
readonly 'timeTravel.capabilities': {
|
|
1061
|
+
readonly mode: 'read';
|
|
1062
|
+
readonly feature: 'timeTravel';
|
|
1063
|
+
};
|
|
1064
|
+
readonly 'timeTravel.currentMark': {
|
|
1065
|
+
readonly mode: 'read';
|
|
1066
|
+
readonly feature: 'timeTravel';
|
|
1067
|
+
};
|
|
1068
|
+
readonly 'timeTravel.markForTime': {
|
|
1069
|
+
readonly mode: 'read';
|
|
1070
|
+
readonly feature: 'timeTravel';
|
|
1071
|
+
};
|
|
1072
|
+
readonly 'timeTravel.listMarks': {
|
|
1073
|
+
readonly mode: 'read';
|
|
1074
|
+
readonly feature: 'timeTravel';
|
|
1075
|
+
};
|
|
1076
|
+
readonly 'timeTravel.preview': {
|
|
1077
|
+
readonly mode: 'read';
|
|
1078
|
+
readonly feature: 'timeTravel';
|
|
1079
|
+
};
|
|
1080
|
+
readonly 'timeTravel.armRestore': {
|
|
1081
|
+
readonly mode: 'write';
|
|
1082
|
+
readonly feature: 'timeTravel';
|
|
1083
|
+
readonly gate: 'timeTravelRestore';
|
|
1084
|
+
readonly destructive: true;
|
|
1085
|
+
};
|
|
1086
|
+
readonly 'timeTravel.undo': {
|
|
1087
|
+
readonly mode: 'write';
|
|
1088
|
+
readonly feature: 'timeTravel';
|
|
1089
|
+
readonly gate: 'timeTravelRestore';
|
|
1090
|
+
readonly destructive: true;
|
|
1091
|
+
};
|
|
1092
|
+
readonly 'timeTravel.createSnapshot': {
|
|
1093
|
+
readonly mode: 'write';
|
|
1094
|
+
readonly feature: 'timeTravel';
|
|
1095
|
+
};
|
|
1096
|
+
readonly 'timeTravel.prune': {
|
|
1097
|
+
readonly mode: 'write';
|
|
1098
|
+
readonly feature: 'timeTravel';
|
|
1099
|
+
readonly gate: 'timeTravelRestore';
|
|
1100
|
+
readonly destructive: true;
|
|
1101
|
+
};
|
|
1102
|
+
readonly 'transfer.export': {
|
|
1103
|
+
readonly mode: 'read';
|
|
1104
|
+
readonly feature: 'transfer';
|
|
1105
|
+
};
|
|
1106
|
+
readonly 'transfer.import': {
|
|
1107
|
+
readonly mode: 'write';
|
|
1108
|
+
readonly feature: 'transfer';
|
|
1109
|
+
readonly gate: 'transferImport';
|
|
1110
|
+
readonly destructive: true;
|
|
1111
|
+
};
|
|
1112
|
+
readonly 'auth.users': {
|
|
1113
|
+
readonly mode: 'read';
|
|
1114
|
+
readonly feature: 'auth';
|
|
1115
|
+
};
|
|
1116
|
+
readonly 'auth.userDetail': {
|
|
1117
|
+
readonly mode: 'read';
|
|
1118
|
+
readonly feature: 'auth';
|
|
1119
|
+
};
|
|
1120
|
+
readonly 'auth.sessions': {
|
|
1121
|
+
readonly mode: 'read';
|
|
1122
|
+
readonly feature: 'auth';
|
|
1123
|
+
};
|
|
1124
|
+
readonly 'auth.revokeSession': {
|
|
1125
|
+
readonly mode: 'write';
|
|
1126
|
+
readonly feature: 'auth';
|
|
1127
|
+
readonly gate: 'opsEditable';
|
|
1128
|
+
};
|
|
1129
|
+
readonly 'auth.organizations': {
|
|
1130
|
+
readonly mode: 'read';
|
|
1131
|
+
readonly feature: 'authOrganizations';
|
|
1132
|
+
};
|
|
1133
|
+
readonly 'queue.list': {
|
|
1134
|
+
readonly mode: 'read';
|
|
1135
|
+
readonly feature: 'queue';
|
|
1136
|
+
};
|
|
1137
|
+
readonly 'queue.depths': {
|
|
1138
|
+
readonly mode: 'read';
|
|
1139
|
+
readonly feature: 'queue';
|
|
1140
|
+
};
|
|
1141
|
+
readonly 'queue.dlq': {
|
|
1142
|
+
readonly mode: 'read';
|
|
1143
|
+
readonly feature: 'queue';
|
|
1144
|
+
};
|
|
1145
|
+
readonly 'queue.send': {
|
|
1146
|
+
readonly mode: 'write';
|
|
1147
|
+
readonly feature: 'queue';
|
|
1148
|
+
readonly gate: 'opsEditable';
|
|
1149
|
+
};
|
|
1150
|
+
readonly 'queue.replay': {
|
|
1151
|
+
readonly mode: 'write';
|
|
1152
|
+
readonly feature: 'queue';
|
|
1153
|
+
readonly gate: 'opsEditable';
|
|
1154
|
+
};
|
|
1155
|
+
readonly 'schedule.jobs': {
|
|
1156
|
+
readonly mode: 'read';
|
|
1157
|
+
readonly feature: 'schedule';
|
|
1158
|
+
};
|
|
1159
|
+
readonly 'schedule.triggers': {
|
|
1160
|
+
readonly mode: 'read';
|
|
1161
|
+
readonly feature: 'schedule';
|
|
1162
|
+
};
|
|
1163
|
+
readonly 'schedule.runNow': {
|
|
1164
|
+
readonly mode: 'write';
|
|
1165
|
+
readonly feature: 'schedule';
|
|
1166
|
+
readonly gate: 'opsEditable';
|
|
1167
|
+
};
|
|
1168
|
+
readonly 'flags.list': {
|
|
1169
|
+
readonly mode: 'read';
|
|
1170
|
+
readonly feature: 'flags';
|
|
1171
|
+
};
|
|
1172
|
+
readonly 'flags.evaluate': {
|
|
1173
|
+
readonly mode: 'read';
|
|
1174
|
+
readonly feature: 'flags';
|
|
1175
|
+
};
|
|
1176
|
+
readonly 'logs.tail': {
|
|
1177
|
+
readonly mode: 'read';
|
|
1178
|
+
readonly feature: 'logs';
|
|
1179
|
+
};
|
|
1180
|
+
readonly 'live.subscriptions': {
|
|
1181
|
+
readonly mode: 'read';
|
|
1182
|
+
readonly feature: 'live';
|
|
1183
|
+
};
|
|
1184
|
+
readonly 'presence.rooms': {
|
|
1185
|
+
readonly mode: 'read';
|
|
1186
|
+
readonly feature: 'presence';
|
|
1187
|
+
};
|
|
1188
|
+
readonly 'audit.tail': {
|
|
1189
|
+
readonly mode: 'read';
|
|
1190
|
+
readonly feature: 'audit';
|
|
1191
|
+
};
|
|
1192
|
+
};
|
|
1193
|
+
/**
|
|
1194
|
+
* Every op name as a runtime const array. Kept as a literal tuple (via
|
|
1195
|
+
* `as const satisfies`) so the exhaustiveness drift guard can compare its
|
|
1196
|
+
* element union against `keyof StudioRpcMap` in both directions.
|
|
1197
|
+
*/
|
|
1198
|
+
export declare const STUDIO_OPS: readonly ["studio.capabilities", "app.routes", "app.modules", "app.entrypoints", "app.openapi", "api.authorizeTryIt", "data.listModels", "data.describeModel", "data.listRows", "data.readRow", "data.facets", "data.cascadePreview", "data.writeRow", "data.deleteRows", "data.clearTable", "data.generateRows", "timeTravel.capabilities", "timeTravel.currentMark", "timeTravel.markForTime", "timeTravel.listMarks", "timeTravel.preview", "timeTravel.armRestore", "timeTravel.undo", "timeTravel.createSnapshot", "timeTravel.prune", "transfer.export", "transfer.import", "auth.users", "auth.userDetail", "auth.sessions", "auth.revokeSession", "auth.organizations", "queue.list", "queue.depths", "queue.dlq", "queue.send", "queue.replay", "schedule.jobs", "schedule.triggers", "schedule.runNow", "flags.list", "flags.evaluate", "logs.tail", "live.subscriptions", "presence.rooms", "audit.tail"];
|
|
1199
|
+
/**
|
|
1200
|
+
* The subset of ops flagged destructive (they carry a `confirmToken`). Derived
|
|
1201
|
+
* from the op union so it can't drift from {@link STUDIO_OP_META}.
|
|
1202
|
+
*/
|
|
1203
|
+
export type DestructiveStudioOp = { [K in StudioOp]: (typeof STUDIO_OP_META)[K] extends {
|
|
1204
|
+
destructive: true;
|
|
1205
|
+
} ? K : never; }[StudioOp];
|
|
1206
|
+
//#endregion
|
|
1207
|
+
//#region src/capabilities.d.ts
|
|
1208
|
+
/**
|
|
1209
|
+
* The closed set of feature keys the UI navigates by. `const` array + derived
|
|
1210
|
+
* union so a feature panel can light up iff the app actually wired that package.
|
|
1211
|
+
*/
|
|
1212
|
+
export declare const STUDIO_FEATURE_KEYS: readonly ['app', 'openapi', 'data', 'timeTravel', 'transfer', 'auth', 'authOrganizations', 'queue', 'schedule', 'flags', 'logs', 'audit', 'live', 'presence'];
|
|
1213
|
+
/** Union of every Studio feature key. */
|
|
1214
|
+
export type StudioFeatureKey = (typeof STUDIO_FEATURE_KEYS)[number];
|
|
1215
|
+
/**
|
|
1216
|
+
* The derived, wire-facing write gates. Default all `false` -> read-only Studio.
|
|
1217
|
+
* Op dispatch gates a write op against the gate named in its meta.
|
|
1218
|
+
*/
|
|
1219
|
+
export interface StudioWriteGates {
|
|
1220
|
+
/** Data-row create/update/delete/generate is permitted. */
|
|
1221
|
+
dataEditable: boolean;
|
|
1222
|
+
/** Schema-level edits are permitted. */
|
|
1223
|
+
schemaEditable: boolean;
|
|
1224
|
+
/** Operational actions (queue send/replay, schedule run-now, session revoke). */
|
|
1225
|
+
opsEditable: boolean;
|
|
1226
|
+
/** Reads/writes may run as a supplied identity. */
|
|
1227
|
+
runAsIdentity: boolean;
|
|
1228
|
+
/** Time-travel restore/undo/prune is permitted. */
|
|
1229
|
+
timeTravelRestore: boolean;
|
|
1230
|
+
/** Transfer import (bulk NDJSON ingest) is permitted. */
|
|
1231
|
+
transferImport: boolean;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* The full capability descriptor returned by `studio.capabilities`.
|
|
1235
|
+
* `timeTravel` is `null` when no {@link TimeTravelPort} is bound.
|
|
1236
|
+
*/
|
|
1237
|
+
export interface StudioCapabilities {
|
|
1238
|
+
/** Registered operations with a usable implementation; write permission is separate. */
|
|
1239
|
+
operations: StudioOp[];
|
|
1240
|
+
features: Record<StudioFeatureKey, boolean>;
|
|
1241
|
+
writes: StudioWriteGates;
|
|
1242
|
+
timeTravel: TimeTravelCapabilities | null;
|
|
1243
|
+
}
|
|
1244
|
+
//#endregion
|
|
1245
|
+
//#region src/http.d.ts
|
|
1246
|
+
/**
|
|
1247
|
+
* Transport constants shared by the server module, the UI, and the dev host.
|
|
1248
|
+
* String literals + one integer version marker; zero runtime dependencies.
|
|
1249
|
+
*/
|
|
1250
|
+
/** Default reserved prefix the admin surface mounts under. */
|
|
1251
|
+
export declare const STUDIO_DEFAULT_PATH = "/_vela/admin";
|
|
1252
|
+
/** Unauthenticated health probe suffix (`GET {prefix}/health`). */
|
|
1253
|
+
export declare const STUDIO_HEALTH_SUFFIX = "/health";
|
|
1254
|
+
/** RPC dispatch suffix (`POST {prefix}/rpc/:op`). */
|
|
1255
|
+
export declare const STUDIO_RPC_SUFFIX = "/rpc/";
|
|
1256
|
+
/** Ephemeral WS sub-token mint suffix (`POST {prefix}/ws-token`). */
|
|
1257
|
+
export declare const STUDIO_WS_TOKEN_SUFFIX = "/ws-token";
|
|
1258
|
+
/** Snapshot/transfer export suffix (`GET {prefix}/export`). */
|
|
1259
|
+
export declare const STUDIO_EXPORT_SUFFIX = "/export";
|
|
1260
|
+
/** The header carrying the master bearer token (`Authorization: Bearer <token>`). */
|
|
1261
|
+
export declare const STUDIO_TOKEN_HEADER = "authorization";
|
|
1262
|
+
/** The wire protocol version. Bumped only on a breaking envelope change. */
|
|
1263
|
+
export declare const STUDIO_PROTOCOL_VERSION = 2;
|
|
1264
|
+
//#endregion
|
|
1265
|
+
//#region src/connection.d.ts
|
|
1266
|
+
/** Browser configuration emitted by the loopback host. Never contains the master token. */
|
|
1267
|
+
export interface StudioConnection {
|
|
1268
|
+
protocolVersion: 2;
|
|
1269
|
+
routerBasePath: string;
|
|
1270
|
+
adminBasePath: string;
|
|
1271
|
+
apiRequestPath: string;
|
|
1272
|
+
sessionToken: string;
|
|
1273
|
+
}
|
|
1274
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
1275
|
+
export declare function parseStudioConnection(value: unknown): StudioConnection;
|
|
1276
|
+
//#endregion
|
|
1277
|
+
//#region src/responses.d.ts
|
|
1278
|
+
/** Every operation must supply a concrete validator for its declared output. */
|
|
1279
|
+
export declare const STUDIO_RESPONSE_PARSERS: { readonly [Op in StudioOp]: (value: unknown) => StudioOpRes<Op>; };
|
|
1280
|
+
/** The result type comes only from the selected operation and its validator. */
|
|
1281
|
+
export declare function parseStudioResponse<Op extends StudioOp>(op: Op, value: unknown): StudioOpRes<Op>;
|
|
1282
|
+
/** Validate the envelope, operation identity, and operation-specific payload. */
|
|
1283
|
+
export declare function parseStudioRpcResponse<Op extends StudioOp>(op: Op, value: unknown): AdminRpcResponse<StudioOpRes<Op>>;
|
|
1284
|
+
//#endregion
|
|
1285
|
+
//# sourceMappingURL=index.d.ts.map
|