@jxsuite/protocol 0.3.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/package.json +2 -2
- package/src/routes.ts +109 -3
- package/src/types.ts +227 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The Studio Backend Protocol — wire types and the canonical route table every Jx Studio backend implements",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,6 +25,6 @@
|
|
|
25
25
|
"test:coverage": "bun test --isolate --coverage"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@jxsuite/schema": "^
|
|
28
|
+
"@jxsuite/schema": "^1.1.0"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/src/routes.ts
CHANGED
|
@@ -75,9 +75,19 @@ export const STUDIO_ROUTES = {
|
|
|
75
75
|
"Starter templates (StarterInfo[])",
|
|
76
76
|
"The New Project picker offers only blank/templates.",
|
|
77
77
|
),
|
|
78
|
+
importSite: route(
|
|
79
|
+
"POST",
|
|
80
|
+
"/__studio/import-site",
|
|
81
|
+
"Clone a live website into a project; streams NDJSON progress",
|
|
82
|
+
"The New Project Import tab is unavailable.",
|
|
83
|
+
),
|
|
78
84
|
|
|
79
85
|
// ─── Filesystem ───────────────────────────────────────────────────────────
|
|
80
|
-
files: route(
|
|
86
|
+
files: route(
|
|
87
|
+
"GET",
|
|
88
|
+
"/__studio/files",
|
|
89
|
+
"List a directory (DirEntry[]); with ?glob=<pattern>, search matching files project-wide",
|
|
90
|
+
),
|
|
81
91
|
fileRead: route("GET", "/__studio/file", "Read a file's text content"),
|
|
82
92
|
fileWrite: route("PUT", "/__studio/file", "Write a file's text content"),
|
|
83
93
|
fileDelete: route("DELETE", "/__studio/file", "Delete a file"),
|
|
@@ -106,8 +116,20 @@ export const STUDIO_ROUTES = {
|
|
|
106
116
|
formats: route(
|
|
107
117
|
"GET",
|
|
108
118
|
"/__studio/formats",
|
|
109
|
-
"The project's format registry entries"
|
|
110
|
-
|
|
119
|
+
"The project's format registry entries plus a sibling `extensions` array — per enabled " +
|
|
120
|
+
"extension its manifest identity and project-section contributions (ExtensionsInfo[]; " +
|
|
121
|
+
"backs listFormats and listExtensions).",
|
|
122
|
+
"Only .json documents open (backs listFormats); descriptor-contributed settings sections " +
|
|
123
|
+
"do not appear.",
|
|
124
|
+
),
|
|
125
|
+
projectSchemas: route(
|
|
126
|
+
"GET",
|
|
127
|
+
"/__studio/project-schemas",
|
|
128
|
+
"The project's generated entry documents (project.schema.json / document.schema.json), " +
|
|
129
|
+
"PRE-BUNDLED into self-contained schemas — {project, document} (backs fetchProjectSchemas). " +
|
|
130
|
+
"Regenerated on demand when missing or older than project.json.",
|
|
131
|
+
"The JSON editor falls back to the bundled core schemas (extension sections get no " +
|
|
132
|
+
"editor validation/completion).",
|
|
111
133
|
),
|
|
112
134
|
format: route(
|
|
113
135
|
"POST",
|
|
@@ -121,6 +143,24 @@ export const STUDIO_ROUTES = {
|
|
|
121
143
|
"Extract a $studio schema from a class source",
|
|
122
144
|
"Plugin property panels fall back to generic JSON editing.",
|
|
123
145
|
),
|
|
146
|
+
codeFormat: route(
|
|
147
|
+
"POST",
|
|
148
|
+
"/__studio/code/format",
|
|
149
|
+
"Format posted source {code, path?} → {code, errors}",
|
|
150
|
+
"Code editors skip format-on-open/save (codeService returns null).",
|
|
151
|
+
),
|
|
152
|
+
codeMinify: route(
|
|
153
|
+
"POST",
|
|
154
|
+
"/__studio/code/minify",
|
|
155
|
+
"Minify posted source {code} → {code}",
|
|
156
|
+
"Compiled-output minification is skipped.",
|
|
157
|
+
),
|
|
158
|
+
codeLint: route(
|
|
159
|
+
"POST",
|
|
160
|
+
"/__studio/code/lint",
|
|
161
|
+
"Lint posted source {code, path?} → {diagnostics}",
|
|
162
|
+
"Code editors show no lint markers.",
|
|
163
|
+
),
|
|
124
164
|
|
|
125
165
|
// ─── Packages ─────────────────────────────────────────────────────────────
|
|
126
166
|
packages: route("GET", "/__studio/packages", "List dependencies (PackageInfo[])"),
|
|
@@ -181,6 +221,72 @@ export const STUDIO_ROUTES = {
|
|
|
181
221
|
"Studio falls back to a direct GitHub API call with the user's token.",
|
|
182
222
|
),
|
|
183
223
|
|
|
224
|
+
// ─── Data surface (connector domain owner console) ───────────────────────
|
|
225
|
+
// These routes intentionally bypass table permission rules — the backend boundary
|
|
226
|
+
// (dev-server loopback/token, cloud collaboration permission) is the gate
|
|
227
|
+
// (specs/extensions.md §13). Secret VALUES never ride them; env-var NAMES only.
|
|
228
|
+
dataConnections: route(
|
|
229
|
+
"GET",
|
|
230
|
+
"/__studio/data/connections",
|
|
231
|
+
"Connector connections with configured/missingSecrets/isDefault state, reachable table " +
|
|
232
|
+
"names, and registry-descriptor provider metadata (DataConnectionsResponse; backs " +
|
|
233
|
+
"dataConnections)",
|
|
234
|
+
"The connections settings section shows no status, and data-domain actions stay hidden.",
|
|
235
|
+
),
|
|
236
|
+
dataConnectionTest: route(
|
|
237
|
+
"POST",
|
|
238
|
+
"/__studio/data/connections/test",
|
|
239
|
+
"Probe a connection {connection} → DataConnectionTestResult (backs dataConnectionTest)",
|
|
240
|
+
"The Test Connection action is hidden.",
|
|
241
|
+
),
|
|
242
|
+
dataPush: route(
|
|
243
|
+
"POST",
|
|
244
|
+
"/__studio/data/push",
|
|
245
|
+
"Additive schema push {connection?, dryRun?} → DataPushResult (plan of DataPushStep[], " +
|
|
246
|
+
"applied, warnings/errors; backs dataPush)",
|
|
247
|
+
"The Push Schema action is hidden; schemas deploy via `jx db push` instead.",
|
|
248
|
+
),
|
|
249
|
+
dataRows: route(
|
|
250
|
+
"GET",
|
|
251
|
+
"/__studio/data/rows",
|
|
252
|
+
"Page a table (DataRowsQuery params) → DataRowsResult {rows, total, columns} (backs dataRows)",
|
|
253
|
+
"The data grid is unavailable.",
|
|
254
|
+
),
|
|
255
|
+
dataInsertRow: route(
|
|
256
|
+
"POST",
|
|
257
|
+
"/__studio/data/rows",
|
|
258
|
+
"Insert a row {table, connection?, values} → {row} (backs dataInsertRow)",
|
|
259
|
+
"The data grid hides its add-row footer.",
|
|
260
|
+
),
|
|
261
|
+
dataUpdateRow: route(
|
|
262
|
+
"PUT",
|
|
263
|
+
"/__studio/data/rows",
|
|
264
|
+
"Update a row keyed on its primary key {table, connection?, pk, set} → {row} (backs " +
|
|
265
|
+
"dataUpdateRow)",
|
|
266
|
+
"Data grid cells are read-only.",
|
|
267
|
+
),
|
|
268
|
+
dataDeleteRow: route(
|
|
269
|
+
"DELETE",
|
|
270
|
+
"/__studio/data/rows",
|
|
271
|
+
"Delete a row (?table=&pk=&connection=) → {ok} (backs dataDeleteRow)",
|
|
272
|
+
"The data grid hides row deletion.",
|
|
273
|
+
),
|
|
274
|
+
|
|
275
|
+
// ─── Secrets (names only) ─────────────────────────────────────────────────
|
|
276
|
+
secretsList: route(
|
|
277
|
+
"GET",
|
|
278
|
+
"/__studio/secrets",
|
|
279
|
+
"Configured secret env-var NAMES, never values (SecretsListResponse; backs listSecrets)",
|
|
280
|
+
"Secret fields cannot show set/unset state.",
|
|
281
|
+
),
|
|
282
|
+
secretsSet: route(
|
|
283
|
+
"PUT",
|
|
284
|
+
"/__studio/secrets",
|
|
285
|
+
"Write/remove secrets in the backend store (.dev.vars locally) {set?, remove?} → names " +
|
|
286
|
+
"(backs setSecrets)",
|
|
287
|
+
"The secret form control renders disabled; secrets are edited in .dev.vars by hand.",
|
|
288
|
+
),
|
|
289
|
+
|
|
184
290
|
// ─── AI proxy ─────────────────────────────────────────────────────────────
|
|
185
291
|
aiChat: route(
|
|
186
292
|
"POST",
|
package/src/types.ts
CHANGED
|
@@ -112,6 +112,233 @@ export interface ComponentMeta {
|
|
|
112
112
|
hasElements?: boolean;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// ─── Extensions (specs/extensions.md §9/§9.1) ────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/** The `project` admission block of a section-owning extension class. */
|
|
118
|
+
export interface ExtensionProjectBlock {
|
|
119
|
+
/** The project.json top-level property the class owns. */
|
|
120
|
+
key: string;
|
|
121
|
+
title?: string;
|
|
122
|
+
description?: string;
|
|
123
|
+
referenceable?: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** One project-section contribution of an extension class, as served on the formats route. */
|
|
127
|
+
export interface ExtensionContributionInfo {
|
|
128
|
+
/** The $prototype-visible class name declaring the `project` block. */
|
|
129
|
+
className: string;
|
|
130
|
+
project: ExtensionProjectBlock;
|
|
131
|
+
/** The class descriptor's `$studio` block (settings-section vocabulary), when declared. */
|
|
132
|
+
studio?: Record<string, unknown> | null;
|
|
133
|
+
/**
|
|
134
|
+
* The section's value schema — `properties[<key>]` of the extension's shipped project fragment —
|
|
135
|
+
* resolved backend-side; null when the extension ships no fragment (or it lacks the key).
|
|
136
|
+
*/
|
|
137
|
+
entrySchema?: Record<string, unknown> | null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* One enabled extension package on the wire — the `extensions` sibling of the formats route's
|
|
142
|
+
* `formats` array (additive; the `formats` shape is unchanged).
|
|
143
|
+
*/
|
|
144
|
+
export interface ExtensionsInfo {
|
|
145
|
+
/** The project.json `extensions` entry that produced this extension. */
|
|
146
|
+
specifier: string;
|
|
147
|
+
/** The manifest's package name. */
|
|
148
|
+
name: string;
|
|
149
|
+
title?: string;
|
|
150
|
+
description?: string;
|
|
151
|
+
/** Project-section contributions, in class declaration order. */
|
|
152
|
+
contributions: ExtensionContributionInfo[];
|
|
153
|
+
/**
|
|
154
|
+
* Every manifest class of the extension with its backend-resolved descriptor path, in declaration
|
|
155
|
+
* order. Optional (additive): older backends omit it, and consumers needing a class `$src` fall
|
|
156
|
+
* back to their historical literals. Plain state classes (no admission blocks) carry `state:
|
|
157
|
+
* true` plus their `$studio.stateDefaults` hint, seeding studio-created defs.
|
|
158
|
+
*/
|
|
159
|
+
classes?: {
|
|
160
|
+
name: string;
|
|
161
|
+
path: string;
|
|
162
|
+
state?: boolean;
|
|
163
|
+
stateDefaults?: Record<string, unknown>;
|
|
164
|
+
}[];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Response of the project-schemas route: the project's generated entry documents
|
|
169
|
+
* (project.schema.json / document.schema.json), PRE-BUNDLED into self-contained compound schemas so
|
|
170
|
+
* editors can register them without resolving relative `$ref`s.
|
|
171
|
+
*/
|
|
172
|
+
export interface ProjectSchemasResponse {
|
|
173
|
+
project?: Record<string, unknown> | null;
|
|
174
|
+
document?: Record<string, unknown> | null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ─── Data surface (connector domain; specs/extensions.md §13) ───────────────
|
|
178
|
+
/* The /__studio/data/* routes are the project owner's admin console over the connector
|
|
179
|
+
extension's connections and tables. They intentionally bypass table permission rules —
|
|
180
|
+
the backend's own boundary (dev-server loopback/token, cloud collaboration permission)
|
|
181
|
+
is the gate. Secret VALUES never ride these shapes; env-var NAMES only. */
|
|
182
|
+
|
|
183
|
+
/** Connector-provider metadata resolved from the extension registry's class descriptors. */
|
|
184
|
+
export interface DataConnectorInfo {
|
|
185
|
+
/** Backing-service identifier (`d1`, `supabase`, `sqlite`, ...). */
|
|
186
|
+
provider: string;
|
|
187
|
+
/** SQL dialect family (`"sqlite"` | `"postgres"`). */
|
|
188
|
+
kind?: string;
|
|
189
|
+
/** Dev-server stand-in provider (specs/extensions.md §12), when the class declares one. */
|
|
190
|
+
local?: string;
|
|
191
|
+
/** The descriptor's human description, when present. */
|
|
192
|
+
description?: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** One project connection with its configuration state (identifiers and env NAMES only). */
|
|
196
|
+
export interface DataConnectionInfo {
|
|
197
|
+
/** The `connections` section key. */
|
|
198
|
+
name: string;
|
|
199
|
+
provider: string;
|
|
200
|
+
/** The connection's project.json value — identifiers and env-var names, never secrets. */
|
|
201
|
+
settings: Record<string, unknown>;
|
|
202
|
+
/** True when every env-var name the connection references resolves in the backend env. */
|
|
203
|
+
configured: boolean;
|
|
204
|
+
/** Referenced env-var NAMES with no value in the backend environment. */
|
|
205
|
+
missingSecrets: string[];
|
|
206
|
+
/** True for the first-declared connection (the console's default pick). */
|
|
207
|
+
isDefault: boolean;
|
|
208
|
+
/**
|
|
209
|
+
* Table names reachable on this connection: tables declared in project.json plus tables
|
|
210
|
+
* discovered by introspection (e.g. auth system tables). Best-effort — introspection is skipped
|
|
211
|
+
* when the connection is missing secrets or unreachable.
|
|
212
|
+
*/
|
|
213
|
+
tables: string[];
|
|
214
|
+
/** Provider metadata from the registry descriptor, when the provider class is resolvable. */
|
|
215
|
+
connector?: DataConnectorInfo | null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Response of the data-connections route. */
|
|
219
|
+
export interface DataConnectionsResponse {
|
|
220
|
+
connections: DataConnectionInfo[];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Result of the connection-test route (mirrors the connector's testConnection capability). */
|
|
224
|
+
export interface DataConnectionTestResult {
|
|
225
|
+
ok: boolean;
|
|
226
|
+
error?: string;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* A push-plan step kind. Open string union: connector DDL kinds today, and future push-step
|
|
231
|
+
* contributors (e.g. `"auth"` migration steps) extend it without a protocol bump.
|
|
232
|
+
*/
|
|
233
|
+
export type DataPushStepKind =
|
|
234
|
+
| "createTable"
|
|
235
|
+
| "addColumn"
|
|
236
|
+
| "index"
|
|
237
|
+
| "junction"
|
|
238
|
+
| "auth"
|
|
239
|
+
| (string & Record<never, never>);
|
|
240
|
+
|
|
241
|
+
/** One step of a schema-push plan. */
|
|
242
|
+
export interface DataPushStep {
|
|
243
|
+
kind: DataPushStepKind;
|
|
244
|
+
/** Affected table, when derivable from the statement. */
|
|
245
|
+
table?: string;
|
|
246
|
+
/** One-line human description of the step. */
|
|
247
|
+
summary: string;
|
|
248
|
+
/** The compiled SQL statement, when the step is SQL-backed. */
|
|
249
|
+
sql?: string;
|
|
250
|
+
/** The connection the step applies to. */
|
|
251
|
+
connection?: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Request body of the data-push route. */
|
|
255
|
+
export interface DataPushRequest {
|
|
256
|
+
/** Restrict the push to one connection; omitted = every declared connection. */
|
|
257
|
+
connection?: string;
|
|
258
|
+
/** Compile the plan without executing it. */
|
|
259
|
+
dryRun?: boolean;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Result of the data-push route. */
|
|
263
|
+
export interface DataPushResult {
|
|
264
|
+
plan: DataPushStep[];
|
|
265
|
+
/** True when the plan was executed (never on dryRun or after an error). */
|
|
266
|
+
applied: boolean;
|
|
267
|
+
/** Drift/orphan warnings from the additive-only sync. */
|
|
268
|
+
warnings?: string[];
|
|
269
|
+
errors?: string[];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Query of the data-rows route (wire form: query params). */
|
|
273
|
+
export interface DataRowsQuery {
|
|
274
|
+
table: string;
|
|
275
|
+
/** Connection to resolve undeclared (system) tables against; default = first declared. */
|
|
276
|
+
connection?: string;
|
|
277
|
+
/** Page size; default 50. */
|
|
278
|
+
limit?: number;
|
|
279
|
+
offset?: number;
|
|
280
|
+
/** Column to order by (must exist on the table). */
|
|
281
|
+
orderBy?: string;
|
|
282
|
+
dir?: "asc" | "desc";
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Column metadata from backend introspection. */
|
|
286
|
+
export interface DataColumnMeta {
|
|
287
|
+
name: string;
|
|
288
|
+
type: string;
|
|
289
|
+
pk?: boolean;
|
|
290
|
+
nullable?: boolean;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Result of the data-rows route. */
|
|
294
|
+
export interface DataRowsResult {
|
|
295
|
+
rows: Record<string, unknown>[];
|
|
296
|
+
/** Total row count of the table (for pagination). */
|
|
297
|
+
total: number;
|
|
298
|
+
columns: DataColumnMeta[];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Body of the row-insert route. */
|
|
302
|
+
export interface DataRowInsert {
|
|
303
|
+
table: string;
|
|
304
|
+
connection?: string;
|
|
305
|
+
values: Record<string, unknown>;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Body of the row-update route (keyed on the introspected primary key). */
|
|
309
|
+
export interface DataRowUpdate {
|
|
310
|
+
table: string;
|
|
311
|
+
connection?: string;
|
|
312
|
+
pk: string | number;
|
|
313
|
+
set: Record<string, unknown>;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Parameters of the row-delete route. */
|
|
317
|
+
export interface DataRowDelete {
|
|
318
|
+
table: string;
|
|
319
|
+
connection?: string;
|
|
320
|
+
pk: string | number;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ─── Secrets (names only; specs/extensions.md §13) ──────────────────────────
|
|
324
|
+
|
|
325
|
+
/** Response of the secrets-list route — env-var NAMES only, never values. */
|
|
326
|
+
export interface SecretsListResponse {
|
|
327
|
+
names: string[];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Request body of the secrets-set route (values flow in, never back out). */
|
|
331
|
+
export interface SecretsSetRequest {
|
|
332
|
+
set?: Record<string, string>;
|
|
333
|
+
remove?: string[];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Response of the secrets-set route — the resulting NAMES, never values. */
|
|
337
|
+
export interface SecretsSetResponse {
|
|
338
|
+
ok: boolean;
|
|
339
|
+
names: string[];
|
|
340
|
+
}
|
|
341
|
+
|
|
115
342
|
// ─── Packages ────────────────────────────────────────────────────────────────
|
|
116
343
|
|
|
117
344
|
export interface PackageInfo {
|