@jxsuite/protocol 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -3
- package/src/routes.ts +39 -8
- package/src/types.ts +42 -4
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ A backend is free to change the transport and path prefix; the **sub-path, metho
|
|
|
17
17
|
|
|
18
18
|
## Core vs optional
|
|
19
19
|
|
|
20
|
-
Optional routes back optional `StudioPlatform` members. Omitting one is not an error. Studio degrades exactly as each entry's `degradation` describes (the starter picker empties, the Projects catalogue hides, the Publish panel explains git-push publishing, and so on). `coreRouteNames()` / `optionalRouteNames()` split the table programmatically; a conformance test for a new backend can iterate `STUDIO_ROUTES` and assert every core route is served.
|
|
20
|
+
Optional routes back optional `StudioPlatform` members, or, like `filesRead`, back no member and let an adapter serve one it already has more cheaply (their `degradation` names the route they stand in for). Omitting one is not an error. Studio degrades exactly as each entry's `degradation` describes (the starter picker empties, the Projects catalogue hides, the Publish panel explains git-push publishing, and so on). `coreRouteNames()` / `optionalRouteNames()` split the table programmatically; a conformance test for a new backend can iterate `STUDIO_ROUTES` and assert every core route is served.
|
|
21
21
|
|
|
22
22
|
Highlights an implementer should know:
|
|
23
23
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Studio Backend Protocol package: wire types plus the canonical route
|
|
3
|
-
*
|
|
4
|
-
* guide.
|
|
2
|
+
* The Studio Backend Protocol package: wire types plus the canonical route table every Jx Studio
|
|
3
|
+
* backend implements. See README.md for the implementer guide.
|
|
5
4
|
*
|
|
6
5
|
* @license MIT
|
|
7
6
|
*/
|
|
@@ -10,3 +9,5 @@ export * from "./problem";
|
|
|
10
9
|
export * from "./problems";
|
|
11
10
|
export * from "./routes";
|
|
12
11
|
export type * from "./types";
|
|
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";
|
package/src/routes.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Studio Backend Protocol — the canonical route table. The dev server
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* back
|
|
7
|
-
*
|
|
2
|
+
* Studio Backend Protocol — the canonical route table. The dev server (@jxsuite/server) is the
|
|
3
|
+
* reference implementation; every other backend (desktop RPC bridge, cloud platforms) serves the
|
|
4
|
+
* same shapes, either at these literal paths or through an equivalent transport. Optional routes
|
|
5
|
+
* mostly back optional StudioPlatform members — Studio degrades without them, as described by each
|
|
6
|
+
* entry's `degradation`. A few back no member at all: an adapter uses them to serve members it
|
|
7
|
+
* already has more cheaply (`filesRead`), and their `degradation` is the route they stand in for.
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
@@ -28,7 +28,10 @@ export type StudioRouteMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
|
28
28
|
export interface StudioRoute {
|
|
29
29
|
path: string;
|
|
30
30
|
method: StudioRouteMethod;
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* True when a backend may omit the route: its PAL member is optional, or it has no member and an
|
|
33
|
+
* adapter falls back to another route.
|
|
34
|
+
*/
|
|
32
35
|
optional: boolean;
|
|
33
36
|
/** One-line contract summary. */
|
|
34
37
|
summary: string;
|
|
@@ -111,6 +114,31 @@ export const STUDIO_ROUTES = {
|
|
|
111
114
|
"files project-wide",
|
|
112
115
|
),
|
|
113
116
|
fileRead: route("GET", "/__studio/file", "Read a file's text content"),
|
|
117
|
+
/**
|
|
118
|
+
* Many reads in ONE round trip, for a backend whose every request is a network hop.
|
|
119
|
+
*
|
|
120
|
+
* The first optional route with no `StudioPlatform` member behind it: the PAL keeps `readFile`,
|
|
121
|
+
* and an adapter for such a backend coalesces the reads issued in one tick into this call itself
|
|
122
|
+
* (specs/desktop.md §3.1, §10.1). Callers never see it, so a backend without it costs latency and
|
|
123
|
+
* nothing else — which is why the dev server, whose reads are loopback, does not serve it.
|
|
124
|
+
*
|
|
125
|
+
* The answer is partial by design, never a 400: past `READ_FILES_MAX_PATHS` paths or the
|
|
126
|
+
* backend's byte budget, the rest come back in `omitted` for the client to ask again, and the
|
|
127
|
+
* first path is always answered so that asking again makes progress. Each answered path carries
|
|
128
|
+
* its own outcome, because one missing file is not a failed batch. It is a READ: a read-only
|
|
129
|
+
* session may call it, and whatever a single `fileRead` does on the backend's side (a cloud
|
|
130
|
+
* session fetches and stores the file it serves) this does per path too.
|
|
131
|
+
*/
|
|
132
|
+
filesRead: route(
|
|
133
|
+
"POST",
|
|
134
|
+
"/__studio/files/read",
|
|
135
|
+
"Read many files' text content {paths} → ReadFilesResult {files: [path, {content} | " +
|
|
136
|
+
"{error: {status, code?, message}}][], omitted}. At most READ_FILES_MAX_PATHS (200) paths " +
|
|
137
|
+
"and a backend byte budget per call: the excess is listed in `omitted` (never a 400) and " +
|
|
138
|
+
"the first path is always answered. Per-path errors: 404 missing/deleted, 415 binary, 413 " +
|
|
139
|
+
"too large, 502 upstream. Allowed for read-only sessions; keeps fileRead's server-side effects",
|
|
140
|
+
"Adapters fall back to one fileRead per path.",
|
|
141
|
+
),
|
|
114
142
|
fileWrite: route("PUT", "/__studio/file", "Write a file's text content"),
|
|
115
143
|
fileDelete: route("DELETE", "/__studio/file", "Delete a file"),
|
|
116
144
|
fileUpload: route(
|
|
@@ -475,7 +503,10 @@ export function coreRouteNames(): StudioRouteName[] {
|
|
|
475
503
|
);
|
|
476
504
|
}
|
|
477
505
|
|
|
478
|
-
/**
|
|
506
|
+
/**
|
|
507
|
+
* Names of the optional routes (each backs an optional StudioPlatform member, or is an adapter's
|
|
508
|
+
* optimisation with a fallback).
|
|
509
|
+
*/
|
|
479
510
|
export function optionalRouteNames(): StudioRouteName[] {
|
|
480
511
|
return (Object.keys(STUDIO_ROUTES) as StudioRouteName[]).filter(
|
|
481
512
|
(name) => STUDIO_ROUTES[name].optional,
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Studio Backend Protocol — wire types. Every shape a Studio backend serves
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* platforms) share one contract. Environment-agnostic: no DOM, no node —
|
|
2
|
+
* Studio Backend Protocol — wire types. Every shape a Studio backend serves (or a `StudioPlatform`
|
|
3
|
+
* adapter consumes) lives here so client adapters and server implementations (the dev server, the
|
|
4
|
+
* desktop RPC bridge, cloud platforms) share one contract. Environment-agnostic: no DOM, no node —
|
|
6
5
|
* importable in browsers, Bun, and Cloudflare Workers alike.
|
|
7
6
|
*
|
|
8
7
|
* @license MIT
|
|
@@ -87,6 +86,45 @@ export interface UploadResult {
|
|
|
87
86
|
size?: number;
|
|
88
87
|
}
|
|
89
88
|
|
|
89
|
+
/**
|
|
90
|
+
* The most paths one `filesRead` request may name. A backend answers at most this many and lists
|
|
91
|
+
* the rest under {@link ReadFilesResult.omitted}, so a client that sends more is not refused — it is
|
|
92
|
+
* answered in part, and asks again.
|
|
93
|
+
*
|
|
94
|
+
* The one runtime value in this file: a client sizes its batches to it, so it has to exist at
|
|
95
|
+
* runtime rather than only in a summary string.
|
|
96
|
+
*/
|
|
97
|
+
export const READ_FILES_MAX_PATHS = 200;
|
|
98
|
+
|
|
99
|
+
/** Body of `POST /__studio/files/read` (`filesRead`): the project-relative paths to read. */
|
|
100
|
+
export interface ReadFilesRequest {
|
|
101
|
+
paths: string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* One path's answer inside a {@link ReadFilesResult}: its text, or why it has none.
|
|
106
|
+
*
|
|
107
|
+
* Per path rather than per request, because one unreadable file in a batch of two hundred is not a
|
|
108
|
+
* failed batch. `status` borrows the HTTP code the single-file read would have answered (404
|
|
109
|
+
* missing or deleted, 415 binary, 413 too large, 502 upstream), so a client can treat both routes'
|
|
110
|
+
* failures alike.
|
|
111
|
+
*/
|
|
112
|
+
export type ReadFilesOutcome =
|
|
113
|
+
| { content: string }
|
|
114
|
+
| { error: { status: number; code?: string; message: string } };
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Answer of `POST /__studio/files/read`. `files` pairs each answered path with its outcome;
|
|
118
|
+
* `omitted` lists the requested paths this response did not get to (a path or byte budget ran out).
|
|
119
|
+
* Omission is never an error and never a 400: the client asks for those paths again. A backend
|
|
120
|
+
* always answers the FIRST requested path, which is what guarantees that asking again makes
|
|
121
|
+
* progress.
|
|
122
|
+
*/
|
|
123
|
+
export interface ReadFilesResult {
|
|
124
|
+
files: [path: string, outcome: ReadFilesOutcome][];
|
|
125
|
+
omitted: string[];
|
|
126
|
+
}
|
|
127
|
+
|
|
90
128
|
/**
|
|
91
129
|
* What a backend will accept as an upload, when it says.
|
|
92
130
|
*
|