@jxsuite/protocol 2.6.1 → 4.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/README.md +5 -5
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/problem.ts +70 -20
- package/src/problems.ts +28 -15
- package/src/routes.ts +24 -13
- package/src/types.ts +88 -58
package/README.md
CHANGED
|
@@ -7,11 +7,11 @@ The **Studio Backend Protocol**: the wire types and canonical route table that e
|
|
|
7
7
|
|
|
8
8
|
## Who implements this
|
|
9
9
|
|
|
10
|
-
| Backend
|
|
11
|
-
|
|
|
12
|
-
| `@jxsuite/server` (dev server)
|
|
13
|
-
| Desktop (
|
|
14
|
-
| Cloud platforms
|
|
10
|
+
| Backend | Transport |
|
|
11
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| `@jxsuite/server` (dev server) | The literal `/__studio/*` HTTP routes (the reference implementation) |
|
|
13
|
+
| Desktop (Electron / Chromium) | Typed RPC / WebSocket JSON-RPC dispatching into the same handlers |
|
|
14
|
+
| Cloud platforms | HTTP behind a session gateway (e.g. `/api/v1/p/:owner/:repo/:branch/studio/*`), same sub-paths and shapes |
|
|
15
15
|
|
|
16
16
|
A backend is free to change the transport and path prefix; the **sub-path, method, and body shapes are the contract**. `STUDIO_PROTOCOL_VERSION` bumps when any shape changes incompatibly.
|
|
17
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/protocol",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.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": {
|
|
@@ -27,6 +27,6 @@
|
|
|
27
27
|
"test:coverage": "bun test --isolate --coverage"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@jxsuite/schema": "^
|
|
30
|
+
"@jxsuite/schema": "^4.0.0"
|
|
31
31
|
}
|
|
32
32
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,4 +10,4 @@ export * from "./problems";
|
|
|
10
10
|
export * from "./routes";
|
|
11
11
|
export type * from "./types";
|
|
12
12
|
// The one runtime value types.ts carries: the batch-read limit a backend and a client both size to.
|
|
13
|
-
export { READ_FILES_MAX_PATHS } from "./types";
|
|
13
|
+
export { IMPORT_MAX_BREAKPOINTS, IMPORT_MAX_PAGES, READ_FILES_MAX_PATHS } from "./types";
|
package/src/problem.ts
CHANGED
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* decisions follow from that and are worth stating, because both look arbitrary until they are
|
|
11
11
|
* explained:
|
|
12
12
|
*
|
|
13
|
-
* - **`type` is absolute.** RFC 9457 permits a relative reference, resolved against the request
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* - **`type` is absolute.** RFC 9457 permits a relative reference, resolved against the request URL —
|
|
14
|
+
* which on a dev server is `http://127.0.0.1:3000/problems/…` and serves nothing. An absolute
|
|
15
|
+
* `https://jxsuite.com/problems/…` means the same thing everywhere, which is the whole point of
|
|
16
|
+
* identifying a type by URI.
|
|
17
17
|
* - **`instance` is never emitted.** It identifies the specific occurrence, and Jx has no
|
|
18
18
|
* per-occurrence resource to point at. A field whose only possible value is a fabricated URI is
|
|
19
19
|
* noise that looks like information.
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* @license MIT
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import type { JsonValue } from "@jxsuite/schema/types";
|
|
25
|
+
|
|
24
26
|
/** The media type a problem response carries (RFC 9457 §3). */
|
|
25
27
|
export const PROBLEM_MEDIA_TYPE = "application/problem+json";
|
|
26
28
|
|
|
@@ -54,13 +56,58 @@ export interface ProblemDetails {
|
|
|
54
56
|
* @deprecated Read `detail`. This field is removed once every client has migrated.
|
|
55
57
|
*/
|
|
56
58
|
error?: string;
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Extension members (RFC 9457 §3.2) — anything the type documents. A problem is a JSON document,
|
|
61
|
+
* so every member is a JSON value.
|
|
62
|
+
*/
|
|
63
|
+
[extension: string]: JsonValue | undefined;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
/**
|
|
67
|
+
* The members of a failure body {@link problemDetail} consults, in the order it consults them.
|
|
68
|
+
*
|
|
69
|
+
* Each is untrusted: the body arrived from a backend of unknown vintage, so a member may hold any
|
|
70
|
+
* JSON value, and only a non-blank string counts as an answer.
|
|
71
|
+
*/
|
|
72
|
+
export interface FailureBodyText {
|
|
73
|
+
detail?: JsonValue;
|
|
74
|
+
error?: JsonValue;
|
|
75
|
+
title?: JsonValue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A parsed failure body, whatever shape it turned out to be: a bare text body, any other JSON
|
|
80
|
+
* value, or a typed body carrying (or lacking) the {@link FailureBodyText} members.
|
|
81
|
+
*/
|
|
82
|
+
export type FailureBody = JsonValue | FailureBodyText | undefined;
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* The two narrowings the readers below make on wire data. They are local rather than imported from
|
|
86
|
+
* `@jxsuite/schema/guards` because this module is part of the AI gateway's worker graph, which
|
|
87
|
+
* `packages/ai/tests/worker-safety.test.ts` freezes to the protocol's two problem modules.
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/** True when a wire value is a string — blank or not; the caller decides what blank means. */
|
|
91
|
+
function isText(value: FailureBody): value is string {
|
|
92
|
+
return typeof value === "string";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* True when a failure body is an object, whose text members may or may not be present; false for
|
|
97
|
+
* null and every primitive. An array passes and simply carries none of the members.
|
|
98
|
+
*/
|
|
99
|
+
function isFailureBodyText(body: FailureBody): body is FailureBodyText {
|
|
100
|
+
return typeof body === "object" && body !== null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The slug identifying a Jx problem type, i.e. the last segment of its `type` URI.
|
|
105
|
+
*
|
|
106
|
+
* @param {JsonValue | undefined} type - A body's `type` member, as parsed off the wire
|
|
107
|
+
* @returns {string | null}
|
|
108
|
+
*/
|
|
109
|
+
export function problemSlug(type: JsonValue | undefined): string | null {
|
|
110
|
+
if (!isText(type) || !type.startsWith(PROBLEM_TYPE_BASE)) {
|
|
64
111
|
return null;
|
|
65
112
|
}
|
|
66
113
|
const slug = type.slice(PROBLEM_TYPE_BASE.length);
|
|
@@ -72,8 +119,12 @@ export function isProblemDetails(value: unknown): value is ProblemDetails {
|
|
|
72
119
|
if (!value || typeof value !== "object") {
|
|
73
120
|
return false;
|
|
74
121
|
}
|
|
75
|
-
|
|
76
|
-
|
|
122
|
+
return (
|
|
123
|
+
"type" in value &&
|
|
124
|
+
typeof value.type === "string" &&
|
|
125
|
+
"status" in value &&
|
|
126
|
+
typeof value.status === "number"
|
|
127
|
+
);
|
|
77
128
|
}
|
|
78
129
|
|
|
79
130
|
/**
|
|
@@ -87,19 +138,18 @@ export function isProblemDetails(value: unknown): value is ProblemDetails {
|
|
|
87
138
|
* generic one: "Failed to list connections" beats "Request failed (500)". Use {@link problemMessage}
|
|
88
139
|
* when there is no such context.
|
|
89
140
|
*
|
|
90
|
-
* @param {
|
|
141
|
+
* @param {FailureBody} body - A parsed response body, whatever shape it turned out to be
|
|
91
142
|
* @returns {string | null}
|
|
92
143
|
*/
|
|
93
|
-
export function problemDetail(body:
|
|
94
|
-
if (
|
|
144
|
+
export function problemDetail(body: FailureBody): string | null {
|
|
145
|
+
if (isText(body)) {
|
|
95
146
|
return body.trim() === "" ? null : body;
|
|
96
147
|
}
|
|
97
|
-
if (!body
|
|
148
|
+
if (!isFailureBodyText(body)) {
|
|
98
149
|
return null;
|
|
99
150
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
151
|
+
for (const value of [body.detail, body.error, body.title]) {
|
|
152
|
+
if (isText(value) && value.trim() !== "") {
|
|
103
153
|
return value;
|
|
104
154
|
}
|
|
105
155
|
}
|
|
@@ -114,11 +164,11 @@ export function problemDetail(body: unknown): string | null {
|
|
|
114
164
|
* is most-specific-first, because a `title` describes the type rather than the occurrence and is
|
|
115
165
|
* the less useful of the two whenever a `detail` exists.
|
|
116
166
|
*
|
|
117
|
-
* @param {
|
|
167
|
+
* @param {FailureBody} body - A parsed response body, whatever shape it turned out to be
|
|
118
168
|
* @param {number} [status] - The response status, used only when the body says nothing
|
|
119
169
|
* @returns {string}
|
|
120
170
|
*/
|
|
121
|
-
export function problemMessage(body:
|
|
171
|
+
export function problemMessage(body: FailureBody, status?: number): string {
|
|
122
172
|
return (
|
|
123
173
|
problemDetail(body) ?? (status === undefined ? "Request failed" : `Request failed (${status})`)
|
|
124
174
|
);
|
package/src/problems.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @license MIT
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import type { JsonObject, JsonValue } from "@jxsuite/schema/types";
|
|
16
17
|
import { PROBLEM_TYPE_BASE } from "./problem.ts";
|
|
17
18
|
import type { ProblemDetails } from "./problem.ts";
|
|
18
19
|
|
|
@@ -33,12 +34,13 @@ const problem = (
|
|
|
33
34
|
status: number,
|
|
34
35
|
title: string,
|
|
35
36
|
extensions?: readonly string[],
|
|
36
|
-
): ProblemType =>
|
|
37
|
-
status,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
): ProblemType => {
|
|
38
|
+
const declared: ProblemType = { status, title, type: `${PROBLEM_TYPE_BASE}${slug}` };
|
|
39
|
+
if (extensions !== undefined) {
|
|
40
|
+
declared.extensions = extensions;
|
|
41
|
+
}
|
|
42
|
+
return declared;
|
|
43
|
+
};
|
|
42
44
|
|
|
43
45
|
/**
|
|
44
46
|
* Every problem type a Jx backend may answer with, keyed by a stable name.
|
|
@@ -112,28 +114,39 @@ export type ProblemTypeName = keyof typeof PROBLEM_TYPES;
|
|
|
112
114
|
*
|
|
113
115
|
* @param {ProblemTypeName} name - A declared type
|
|
114
116
|
* @param {string} [detail] - What happened THIS time
|
|
115
|
-
* @param {
|
|
117
|
+
* @param {JsonObject} [extensions] - Extension members this type documents
|
|
116
118
|
* @returns {ProblemDetails}
|
|
117
119
|
*/
|
|
118
120
|
export function problemDetails(
|
|
119
121
|
name: ProblemTypeName,
|
|
120
122
|
detail?: string,
|
|
121
|
-
extensions:
|
|
123
|
+
extensions: JsonObject = {},
|
|
122
124
|
): ProblemDetails {
|
|
123
125
|
const declared = PROBLEM_TYPES[name];
|
|
124
|
-
|
|
126
|
+
const body: ProblemDetails = {
|
|
125
127
|
...extensions,
|
|
126
128
|
status: declared.status,
|
|
127
129
|
title: declared.title,
|
|
128
130
|
type: declared.type,
|
|
129
|
-
...(detail === undefined || detail === "" ? {} : { detail, error: detail }),
|
|
130
131
|
};
|
|
132
|
+
if (detail !== undefined && detail !== "") {
|
|
133
|
+
/*
|
|
134
|
+
* The one place the deprecated `error` alias is written (see ProblemDetails.error). Its
|
|
135
|
+
* deprecation addresses readers; the writer owes it to every unmigrated client for one release,
|
|
136
|
+
* and deleting the alias is deleting `error: detail` here.
|
|
137
|
+
*/
|
|
138
|
+
Object.assign(body, { detail, error: detail });
|
|
139
|
+
}
|
|
140
|
+
return body;
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
/**
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
143
|
+
/**
|
|
144
|
+
* The declared type a `type` URI names, or null when it is not one of ours.
|
|
145
|
+
*
|
|
146
|
+
* @param {JsonValue | undefined} type - A body's `type` member, as parsed off the wire
|
|
147
|
+
* @returns {ProblemType | null}
|
|
148
|
+
*/
|
|
149
|
+
export function problemTypeByUri(type: JsonValue | undefined): ProblemType | null {
|
|
150
|
+
// Every declared `type` is a string, so a non-string member matches nothing and answers null.
|
|
138
151
|
return Object.values(PROBLEM_TYPES).find((declared) => declared.type === type) ?? null;
|
|
139
152
|
}
|
package/src/routes.ts
CHANGED
|
@@ -44,13 +44,18 @@ const route = (
|
|
|
44
44
|
path: string,
|
|
45
45
|
summary: string,
|
|
46
46
|
degradation?: string,
|
|
47
|
-
): StudioRoute =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
47
|
+
): StudioRoute => {
|
|
48
|
+
const declared: StudioRoute = {
|
|
49
|
+
method,
|
|
50
|
+
path,
|
|
51
|
+
optional: degradation !== undefined,
|
|
52
|
+
summary,
|
|
53
|
+
};
|
|
54
|
+
if (degradation !== undefined) {
|
|
55
|
+
declared.degradation = degradation;
|
|
56
|
+
}
|
|
57
|
+
return declared;
|
|
58
|
+
};
|
|
54
59
|
|
|
55
60
|
/**
|
|
56
61
|
* Every protocol route, keyed by a stable name. Paths are the dev server's literal `/__studio/*`
|
|
@@ -496,11 +501,19 @@ export const STUDIO_ROUTES = {
|
|
|
496
501
|
/** A stable route name in {@link STUDIO_ROUTES}. */
|
|
497
502
|
export type StudioRouteName = keyof typeof STUDIO_ROUTES;
|
|
498
503
|
|
|
504
|
+
/** Every route name, in declaration order. */
|
|
505
|
+
function routeNames(): StudioRouteName[] {
|
|
506
|
+
/*
|
|
507
|
+
* SAFETY: StudioRouteName is derived from this very literal (`keyof typeof STUDIO_ROUTES`), its
|
|
508
|
+
* members are readonly under `as const`, and nothing in the protocol adds keys at runtime, so its
|
|
509
|
+
* own enumerable string keys are exactly the StudioRouteName members.
|
|
510
|
+
*/
|
|
511
|
+
return Object.keys(STUDIO_ROUTES) as StudioRouteName[];
|
|
512
|
+
}
|
|
513
|
+
|
|
499
514
|
/** Names of every route a minimal (core) backend must implement. */
|
|
500
515
|
export function coreRouteNames(): StudioRouteName[] {
|
|
501
|
-
return (
|
|
502
|
-
(name) => !STUDIO_ROUTES[name].optional,
|
|
503
|
-
);
|
|
516
|
+
return routeNames().filter((name) => !STUDIO_ROUTES[name].optional);
|
|
504
517
|
}
|
|
505
518
|
|
|
506
519
|
/**
|
|
@@ -508,7 +521,5 @@ export function coreRouteNames(): StudioRouteName[] {
|
|
|
508
521
|
* optimisation with a fallback).
|
|
509
522
|
*/
|
|
510
523
|
export function optionalRouteNames(): StudioRouteName[] {
|
|
511
|
-
return (
|
|
512
|
-
(name) => STUDIO_ROUTES[name].optional,
|
|
513
|
-
);
|
|
524
|
+
return routeNames().filter((name) => STUDIO_ROUTES[name].optional);
|
|
514
525
|
}
|
package/src/types.ts
CHANGED
|
@@ -7,7 +7,12 @@
|
|
|
7
7
|
* @license MIT
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type { ProjectBlock, StudioHints } from "@jxsuite/schema/format-registry";
|
|
11
|
+
import type {
|
|
12
|
+
JsonObject,
|
|
13
|
+
JsonValue as SchemaJsonValue,
|
|
14
|
+
JxMutableNode,
|
|
15
|
+
} from "@jxsuite/schema/types";
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* A JSON document value, or `undefined` to signal property removal in the mutators. Re-uses the
|
|
@@ -30,7 +35,7 @@ export interface ErrorBody {
|
|
|
30
35
|
error: string;
|
|
31
36
|
/** Machine-readable discriminator (e.g. "remote_moved", "cf_not_connected"). */
|
|
32
37
|
code?: string;
|
|
33
|
-
detail?:
|
|
38
|
+
detail?: SchemaJsonValue;
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
// ─── Filesystem ──────────────────────────────────────────────────────────────
|
|
@@ -95,11 +100,12 @@ export interface UploadResult {
|
|
|
95
100
|
|
|
96
101
|
/**
|
|
97
102
|
* The most paths one `filesRead` request may name. A backend answers at most this many and lists
|
|
98
|
-
* the rest under {@link ReadFilesResult.omitted}, so a client that sends more is not refused — it
|
|
99
|
-
* answered in part, and asks again.
|
|
103
|
+
* the rest under {@link ReadFilesResult.omitted}, so a client that sends more is not refused — it
|
|
104
|
+
* is answered in part, and asks again.
|
|
100
105
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
106
|
+
* One of this file's three runtime values (with {@link IMPORT_MAX_PAGES} and
|
|
107
|
+
* {@link IMPORT_MAX_BREAKPOINTS}): a client sizes its batches to it, so it has to exist at runtime
|
|
108
|
+
* rather than only in a summary string.
|
|
103
109
|
*/
|
|
104
110
|
export const READ_FILES_MAX_PATHS = 200;
|
|
105
111
|
|
|
@@ -154,7 +160,7 @@ export interface AssetCapabilities {
|
|
|
154
160
|
export interface ReferenceHit {
|
|
155
161
|
/**
|
|
156
162
|
* Which key carried it: `$ref`, `$layout`, `$src`, `attr`, `imports`, `$elements`, `url`,
|
|
157
|
-
* `tagName`.
|
|
163
|
+
* `source`, `copy`, `tagName`.
|
|
158
164
|
*/
|
|
159
165
|
refType: string;
|
|
160
166
|
/** The reference exactly as authored (`"./Card.json"`), or `"<my-card>"` for a tag instance. */
|
|
@@ -195,8 +201,13 @@ export interface ReferencesResult {
|
|
|
195
201
|
// ─── Git ─────────────────────────────────────────────────────────────────────
|
|
196
202
|
|
|
197
203
|
export interface GitFileStatus {
|
|
204
|
+
/**
|
|
205
|
+
* One porcelain letter — `M`, `A`, `D`, `R`, `U` — never a word and never the raw two-column
|
|
206
|
+
* code.
|
|
207
|
+
*/
|
|
198
208
|
status: string;
|
|
199
209
|
path: string;
|
|
210
|
+
/** Whether this change is in the index. Absent reads as unstaged. */
|
|
200
211
|
staged?: boolean;
|
|
201
212
|
}
|
|
202
213
|
|
|
@@ -212,6 +223,12 @@ export interface GitStatusResult {
|
|
|
212
223
|
export interface GitBranchesResult {
|
|
213
224
|
current: string;
|
|
214
225
|
branches: string[];
|
|
226
|
+
/**
|
|
227
|
+
* The repository's default branch (the one a pull request merges into), when the host knows it. A
|
|
228
|
+
* host whose branches are sessions reports it so a review is never committed to the live branch;
|
|
229
|
+
* a local repository may omit it.
|
|
230
|
+
*/
|
|
231
|
+
defaultBranch?: string;
|
|
215
232
|
}
|
|
216
233
|
|
|
217
234
|
export interface GitLogEntry {
|
|
@@ -239,34 +256,32 @@ export interface ComponentMeta {
|
|
|
239
256
|
tagName: string;
|
|
240
257
|
$id?: string | null;
|
|
241
258
|
path: string;
|
|
242
|
-
props?: { name: string; type?: string; default?: JsonValue; [k: string]:
|
|
259
|
+
props?: { name: string; type?: string; default?: JsonValue; [k: string]: JsonValue }[];
|
|
243
260
|
slots?: ComponentSlotMeta[];
|
|
244
261
|
hasElements?: boolean;
|
|
262
|
+
/** The custom-element tags the component's own tree renders (`componentMetaFrom`'s `uses`). */
|
|
263
|
+
uses?: string[];
|
|
245
264
|
}
|
|
246
265
|
|
|
247
266
|
// ─── Extensions (specs/extensions.md §9/§9.1) ────────────────────────────────
|
|
248
267
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
key: string;
|
|
253
|
-
title?: string;
|
|
254
|
-
description?: string;
|
|
255
|
-
referenceable?: boolean;
|
|
256
|
-
}
|
|
268
|
+
/* The `project` admission block of a section-owning extension class: the descriptor's own block,
|
|
269
|
+
declared once in @jxsuite/schema and served here unchanged. */
|
|
270
|
+
export type { ProjectBlock as ExtensionProjectBlock } from "@jxsuite/schema/format-registry";
|
|
257
271
|
|
|
258
272
|
/** One project-section contribution of an extension class, as served on the formats route. */
|
|
259
273
|
export interface ExtensionContributionInfo {
|
|
260
274
|
/** The $prototype-visible class name declaring the `project` block. */
|
|
261
275
|
className: string;
|
|
262
|
-
project:
|
|
276
|
+
project: ProjectBlock;
|
|
263
277
|
/** The class descriptor's `$studio` block (settings-section vocabulary), when declared. */
|
|
264
|
-
studio?:
|
|
278
|
+
studio?: StudioHints | null;
|
|
265
279
|
/**
|
|
266
|
-
* The section's value schema — `properties[<key>]` of the extension's shipped project fragment
|
|
267
|
-
*
|
|
280
|
+
* The section's value schema — `properties[<key>]` of the extension's shipped project fragment,
|
|
281
|
+
* which JSON Schema lets be a boolean as well as an object — resolved backend-side; null when the
|
|
282
|
+
* extension ships no fragment (or it lacks the key).
|
|
268
283
|
*/
|
|
269
|
-
entrySchema?:
|
|
284
|
+
entrySchema?: SchemaJsonValue;
|
|
270
285
|
}
|
|
271
286
|
|
|
272
287
|
/**
|
|
@@ -292,7 +307,7 @@ export interface ExtensionsInfo {
|
|
|
292
307
|
name: string;
|
|
293
308
|
path: string;
|
|
294
309
|
state?: boolean;
|
|
295
|
-
stateDefaults?:
|
|
310
|
+
stateDefaults?: JsonObject;
|
|
296
311
|
}[];
|
|
297
312
|
}
|
|
298
313
|
|
|
@@ -369,8 +384,8 @@ export interface ExtensionCatalogEntry {
|
|
|
369
384
|
* editors can register them without resolving relative `$ref`s.
|
|
370
385
|
*/
|
|
371
386
|
export interface ProjectSchemasResponse {
|
|
372
|
-
project?:
|
|
373
|
-
document?:
|
|
387
|
+
project?: JsonObject | null;
|
|
388
|
+
document?: JsonObject | null;
|
|
374
389
|
}
|
|
375
390
|
|
|
376
391
|
// ─── Data surface (connector domain; specs/extensions.md §13) ───────────────
|
|
@@ -397,7 +412,7 @@ export interface DataConnectionInfo {
|
|
|
397
412
|
name: string;
|
|
398
413
|
provider: string;
|
|
399
414
|
/** The connection's project.json value — identifiers and env-var names, never secrets. */
|
|
400
|
-
settings:
|
|
415
|
+
settings: JsonObject;
|
|
401
416
|
/** True when every env-var name the connection references resolves in the backend env. */
|
|
402
417
|
configured: boolean;
|
|
403
418
|
/** Referenced env-var NAMES with no value in the backend environment. */
|
|
@@ -419,11 +434,9 @@ export interface DataConnectionsResponse {
|
|
|
419
434
|
connections: DataConnectionInfo[];
|
|
420
435
|
}
|
|
421
436
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
error?: string;
|
|
426
|
-
}
|
|
437
|
+
/* Result of the connection-test route: the connector `testConnection` capability's own result,
|
|
438
|
+
declared once in @jxsuite/schema and relayed unchanged. */
|
|
439
|
+
export type { TestConnectionResult as DataConnectionTestResult } from "@jxsuite/schema/format-registry";
|
|
427
440
|
|
|
428
441
|
/**
|
|
429
442
|
* A push-plan step kind. Open string union: connector DDL kinds today, and future push-step
|
|
@@ -450,13 +463,9 @@ export interface DataPushStep {
|
|
|
450
463
|
connection?: string;
|
|
451
464
|
}
|
|
452
465
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
connection?: string;
|
|
457
|
-
/** Compile the plan without executing it. */
|
|
458
|
-
dryRun?: boolean;
|
|
459
|
-
}
|
|
466
|
+
/* Request body of the data-push route: the same request `jx db push` takes, declared once in
|
|
467
|
+
@jxsuite/schema. */
|
|
468
|
+
export type { DataPushRequest } from "@jxsuite/schema/format-registry";
|
|
460
469
|
|
|
461
470
|
/** Result of the data-push route. */
|
|
462
471
|
export interface DataPushResult {
|
|
@@ -491,7 +500,7 @@ export interface DataColumnMeta {
|
|
|
491
500
|
|
|
492
501
|
/** Result of the data-rows route. */
|
|
493
502
|
export interface DataRowsResult {
|
|
494
|
-
rows:
|
|
503
|
+
rows: JsonObject[];
|
|
495
504
|
/** Total row count of the table (for pagination). */
|
|
496
505
|
total: number;
|
|
497
506
|
columns: DataColumnMeta[];
|
|
@@ -501,7 +510,7 @@ export interface DataRowsResult {
|
|
|
501
510
|
export interface DataRowInsert {
|
|
502
511
|
table: string;
|
|
503
512
|
connection?: string;
|
|
504
|
-
values:
|
|
513
|
+
values: JsonObject;
|
|
505
514
|
}
|
|
506
515
|
|
|
507
516
|
/** Body of the row-update route (keyed on the introspected primary key). */
|
|
@@ -509,7 +518,7 @@ export interface DataRowUpdate {
|
|
|
509
518
|
table: string;
|
|
510
519
|
connection?: string;
|
|
511
520
|
pk: string | number;
|
|
512
|
-
set:
|
|
521
|
+
set: JsonObject;
|
|
513
522
|
}
|
|
514
523
|
|
|
515
524
|
/** Parameters of the row-delete route. */
|
|
@@ -526,36 +535,35 @@ export interface SecretsListResponse {
|
|
|
526
535
|
names: string[];
|
|
527
536
|
}
|
|
528
537
|
|
|
529
|
-
/** Request body of the secrets-set route (values flow in, never back out). */
|
|
530
|
-
export interface SecretsSetRequest {
|
|
531
|
-
set?: Record<string, string>;
|
|
532
|
-
remove?: string[];
|
|
533
|
-
}
|
|
534
|
-
|
|
535
538
|
/** Response of the secrets-set route — the resulting NAMES, never values. */
|
|
536
539
|
export interface SecretsSetResponse {
|
|
537
540
|
ok: boolean;
|
|
538
541
|
names: string[];
|
|
539
542
|
}
|
|
540
543
|
|
|
541
|
-
// ───
|
|
544
|
+
// ─── Key-value patches (secrets and user settings) ──────────────────────────
|
|
542
545
|
|
|
543
546
|
/**
|
|
544
|
-
* A set of changes to the
|
|
547
|
+
* A set of changes to a string-valued key store: the request body of the secrets-set route (values
|
|
548
|
+
* flow in, never back out; specs/extensions.md §13), and the patch the desktop launchers apply to
|
|
549
|
+
* the user-level settings store (specs/desktop.md §3.1).
|
|
545
550
|
*
|
|
546
|
-
* A patch rather than the whole map
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
+
* A patch rather than the whole map: a key named by neither `set` nor `remove` must be left exactly
|
|
552
|
+
* as it was found. Writing the whole settings map let any window overwrite the shared store with
|
|
553
|
+
* its own view of it — and on the chromium launcher every window is a separate process with a
|
|
554
|
+
* separate browser profile, so a window holding no settings could clear the credentials another had
|
|
555
|
+
* just stored.
|
|
551
556
|
*/
|
|
552
|
-
export interface
|
|
557
|
+
export interface KeyValuePatch {
|
|
553
558
|
/** Keys to store, with their new values. */
|
|
554
559
|
set?: Record<string, string>;
|
|
555
560
|
/** Keys to forget. */
|
|
556
561
|
remove?: string[];
|
|
557
562
|
}
|
|
558
563
|
|
|
564
|
+
/** The former published names of {@link KeyValuePatch}, kept for existing importers. */
|
|
565
|
+
export type { KeyValuePatch as SecretsSetRequest, KeyValuePatch as SettingsPatch };
|
|
566
|
+
|
|
559
567
|
// ─── Packages ────────────────────────────────────────────────────────────────
|
|
560
568
|
|
|
561
569
|
export interface PackageInfo {
|
|
@@ -602,22 +610,32 @@ export interface AppInfo {
|
|
|
602
610
|
|
|
603
611
|
export interface CodeServiceResult {
|
|
604
612
|
code?: string;
|
|
605
|
-
diagnostics?:
|
|
606
|
-
[key: string]:
|
|
613
|
+
diagnostics?: SchemaJsonValue[];
|
|
614
|
+
[key: string]: JsonValue;
|
|
607
615
|
}
|
|
608
616
|
|
|
609
617
|
// ─── Projects & starters ─────────────────────────────────────────────────────
|
|
610
618
|
|
|
611
|
-
/**
|
|
619
|
+
/**
|
|
620
|
+
* A starter template surfaced in the New Project picker: one entry of `@jxsuite/starters`'
|
|
621
|
+
* `registry.json`, everything the picker needs to render a template card.
|
|
622
|
+
*/
|
|
612
623
|
export interface StarterInfo {
|
|
624
|
+
/** Stable id; also the directory name under `sites/` and the value threaded as `starter`. */
|
|
613
625
|
id: string;
|
|
626
|
+
/** Human-facing display name, e.g. "Bistro & Café". */
|
|
614
627
|
name: string;
|
|
628
|
+
/** Industry archetype label, e.g. "Restaurant & Food". */
|
|
615
629
|
industry: string;
|
|
630
|
+
/** One-line summary shown under the name. */
|
|
616
631
|
tagline: string;
|
|
632
|
+
/** Longer description for the showcase / tooltip. */
|
|
617
633
|
description: string;
|
|
634
|
+
/** Short bullets naming the Jx features the starter demonstrates. */
|
|
618
635
|
features: string[];
|
|
636
|
+
/** Accent colour (hex), mirroring the site's primary token; used for card theming. */
|
|
619
637
|
accent: string;
|
|
620
|
-
/**
|
|
638
|
+
/** Small preview image as a self-contained `data:` URI so the picker needs no extra plumbing. */
|
|
621
639
|
thumbnail: string;
|
|
622
640
|
}
|
|
623
641
|
|
|
@@ -682,6 +700,18 @@ export interface ImportReadyEvent {
|
|
|
682
700
|
root: string;
|
|
683
701
|
}
|
|
684
702
|
|
|
703
|
+
/**
|
|
704
|
+
* The most pages one site import may capture. A backend clamps {@link ImportSiteOptions.maxPages} to
|
|
705
|
+
* it, and a client that builds the options clamps to the same bound before it asks.
|
|
706
|
+
*/
|
|
707
|
+
export const IMPORT_MAX_PAGES = 100;
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* The most breakpoints one site import may be asked to keep; past it, a "limit" policy is not
|
|
711
|
+
* limiting. `@jxsuite/import`'s planner clamps to it, and a client offers no larger count.
|
|
712
|
+
*/
|
|
713
|
+
export const IMPORT_MAX_BREAKPOINTS = 12;
|
|
714
|
+
|
|
685
715
|
/** Options for the import-site pipeline (StudioPlatform.importSite). */
|
|
686
716
|
export interface ImportSiteOptions {
|
|
687
717
|
/** The live site to clone; must be http(s). */
|