@jxsuite/protocol 0.1.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 +65 -0
- package/package.json +30 -0
- package/src/index.ts +10 -0
- package/src/routes.ts +206 -0
- package/src/types.ts +253 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @jxsuite/protocol
|
|
2
|
+
|
|
3
|
+
The **Studio Backend Protocol**: the wire types and canonical route table that
|
|
4
|
+
every Jx Studio backend implements. Jx Studio is backend-agnostic — the editor
|
|
5
|
+
talks to whatever registered a `StudioPlatform` adapter (see
|
|
6
|
+
`@jxsuite/studio/platform`), and every adapter ultimately speaks the contract
|
|
7
|
+
defined here. This package is the single source of truth for that contract:
|
|
8
|
+
|
|
9
|
+
- `@jxsuite/protocol/types` — the request/response shapes (`DirEntry`,
|
|
10
|
+
`GitStatusResult`, `StarterInfo`, `AiModelsResponse`, `CfConnection`, …).
|
|
11
|
+
Environment-agnostic: no DOM, no node — importable in browsers, Bun, and
|
|
12
|
+
Cloudflare Workers alike.
|
|
13
|
+
- `@jxsuite/protocol/routes` — `STUDIO_ROUTES`, the canonical endpoint table
|
|
14
|
+
with methods, one-line contract summaries, and per-route optionality.
|
|
15
|
+
|
|
16
|
+
## Who implements this
|
|
17
|
+
|
|
18
|
+
| Backend | Transport |
|
|
19
|
+
| ------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `@jxsuite/server` (dev server) | The literal `/__studio/*` HTTP routes — the reference implementation |
|
|
21
|
+
| Desktop (Electrobun / Chromium) | Typed RPC / WebSocket JSON-RPC dispatching into the same handlers |
|
|
22
|
+
| Cloud platforms | HTTP behind a session gateway (e.g. `/api/v1/p/:owner/:repo/:branch/studio/*`), same sub-paths and shapes |
|
|
23
|
+
|
|
24
|
+
A backend is free to change the transport and path prefix; the **sub-path,
|
|
25
|
+
method, and body shapes are the contract**. `STUDIO_PROTOCOL_VERSION` bumps
|
|
26
|
+
when any shape changes incompatibly.
|
|
27
|
+
|
|
28
|
+
## Core vs optional
|
|
29
|
+
|
|
30
|
+
Optional routes back optional `StudioPlatform` members. Omitting one is not an
|
|
31
|
+
error — Studio degrades exactly as each entry's `degradation` describes (the
|
|
32
|
+
starter picker empties, the Projects catalogue hides, the Publish panel
|
|
33
|
+
explains git-push publishing, and so on). `coreRouteNames()` /
|
|
34
|
+
`optionalRouteNames()` split the table programmatically; a conformance test
|
|
35
|
+
for a new backend can iterate `STUDIO_ROUTES` and assert every core route is
|
|
36
|
+
served.
|
|
37
|
+
|
|
38
|
+
Highlights an implementer should know:
|
|
39
|
+
|
|
40
|
+
- **`git/commit`** commits the staged files if any are staged, otherwise all
|
|
41
|
+
dirty files. Cloud backends may make commit+push atomic and treat
|
|
42
|
+
`git/push` as a sync check (`ahead` stays 0).
|
|
43
|
+
- **`git/pull`** returns `409 { code: "pull_conflict", conflicts: [paths] }`
|
|
44
|
+
when local dirty files overlap remote changes.
|
|
45
|
+
- **`format`** dispatches `{ format, action: "parse" | "serialize", source? |
|
|
46
|
+
doc?, options? }` through the project's format registry; without it only
|
|
47
|
+
`.json` documents open.
|
|
48
|
+
- **`ai/chat`** accepts `{ messages, tools, systemPrompt, model }` and streams
|
|
49
|
+
the normalized `StreamEvent` SSE defined by `@jxsuite/ai/streaming-client`.
|
|
50
|
+
`ai/models` returns `AiModelsResponse`; report `configured: true` when the
|
|
51
|
+
backend holds credentials (Studio then unlocks the assistant without a
|
|
52
|
+
locally stored key) and `managed: true` when the platform brokers them.
|
|
53
|
+
- **`cf/proxy`** is an allowlisted Cloudflare API passthrough (accounts and
|
|
54
|
+
Pages projects/deployments only). Credentials are injected by the backend —
|
|
55
|
+
a header-borne user token on the dev server, a vaulted OAuth token on cloud
|
|
56
|
+
platforms — and must never be persisted by stateless implementations.
|
|
57
|
+
- **Errors** are `{ error, code?, detail? }` (`ErrorBody`) with a meaningful
|
|
58
|
+
HTTP status; `code` is the machine-readable discriminator Studio switches
|
|
59
|
+
on (e.g. `remote_moved`, `cf_not_connected`, `needs_installation_access`).
|
|
60
|
+
|
|
61
|
+
## Versioning
|
|
62
|
+
|
|
63
|
+
The types are published as TypeScript source (like every `@jxsuite` package)
|
|
64
|
+
and follow the monorepo's release train. Within the monorepo, packages depend
|
|
65
|
+
on it via `workspace:^`.
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jxsuite/protocol",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Studio Backend Protocol — wire types and the canonical route table every Jx Studio backend implements",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jxsuite/jx.git",
|
|
9
|
+
"directory": "packages/protocol"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./src/index.ts",
|
|
17
|
+
"./types": "./src/types.ts",
|
|
18
|
+
"./routes": "./src/routes.ts"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"provenance": false
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "bun test --isolate",
|
|
25
|
+
"test:coverage": "bun test --isolate --coverage"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@jxsuite/schema": "workspace:^"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.ts
ADDED
package/src/routes.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio Backend Protocol — the canonical route table. The dev server
|
|
3
|
+
* (@jxsuite/server) is the reference implementation; every other backend
|
|
4
|
+
* (desktop RPC bridge, cloud platforms) serves the same shapes, either at
|
|
5
|
+
* these literal paths or through an equivalent transport. Optional routes
|
|
6
|
+
* back optional StudioPlatform members — Studio degrades without them, as
|
|
7
|
+
* described by each entry's `degradation`.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Bumped when a route's request/response shape changes incompatibly. */
|
|
13
|
+
export const STUDIO_PROTOCOL_VERSION = 1;
|
|
14
|
+
|
|
15
|
+
export type StudioRouteMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
16
|
+
|
|
17
|
+
export interface StudioRoute {
|
|
18
|
+
path: string;
|
|
19
|
+
method: StudioRouteMethod;
|
|
20
|
+
/** True when a backend may omit the route (its PAL member is optional). */
|
|
21
|
+
optional: boolean;
|
|
22
|
+
/** One-line contract summary. */
|
|
23
|
+
summary: string;
|
|
24
|
+
/** What Studio does when an optional route is absent. */
|
|
25
|
+
degradation?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const route = (
|
|
29
|
+
method: StudioRouteMethod,
|
|
30
|
+
path: string,
|
|
31
|
+
summary: string,
|
|
32
|
+
degradation?: string,
|
|
33
|
+
): StudioRoute => ({
|
|
34
|
+
method,
|
|
35
|
+
path,
|
|
36
|
+
optional: degradation !== undefined,
|
|
37
|
+
summary,
|
|
38
|
+
...(degradation === undefined ? {} : { degradation }),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Every protocol route, keyed by a stable name. Paths are the dev server's literal `/__studio/*`
|
|
43
|
+
* endpoints; transport-mapped backends (RPC, gateway prefixes) preserve the sub-path and shapes.
|
|
44
|
+
*/
|
|
45
|
+
export const STUDIO_ROUTES = {
|
|
46
|
+
// ─── Session / project ────────────────────────────────────────────────────
|
|
47
|
+
activate: route("POST", "/__studio/activate", "Bind the backend to a project root"),
|
|
48
|
+
project: route("GET", "/__studio/project", "Root project metadata {name, root}"),
|
|
49
|
+
projectInfo: route(
|
|
50
|
+
"GET",
|
|
51
|
+
"/__studio/project-info",
|
|
52
|
+
"Probe a directory: {isSiteProject, projectConfig?, directories?}",
|
|
53
|
+
),
|
|
54
|
+
resolveSite: route(
|
|
55
|
+
"GET",
|
|
56
|
+
"/__studio/resolve-site",
|
|
57
|
+
"Resolve the owning site of a file path {sitePath, projectConfig?, fileRelPath?}",
|
|
58
|
+
),
|
|
59
|
+
sites: route(
|
|
60
|
+
"GET",
|
|
61
|
+
"/__studio/sites",
|
|
62
|
+
"Enumerate site projects [{config, path}] (backs listProjects)",
|
|
63
|
+
"The welcome screen's Projects catalogue stays hidden.",
|
|
64
|
+
),
|
|
65
|
+
findProject: route(
|
|
66
|
+
"GET",
|
|
67
|
+
"/__studio/find-project",
|
|
68
|
+
"Locate a project directory by name outside the root",
|
|
69
|
+
"openProject falls back to config-matching only.",
|
|
70
|
+
),
|
|
71
|
+
createProject: route("POST", "/__studio/create-project", "Scaffold a project → {root, config}"),
|
|
72
|
+
starters: route(
|
|
73
|
+
"GET",
|
|
74
|
+
"/__studio/starters",
|
|
75
|
+
"Starter templates (StarterInfo[])",
|
|
76
|
+
"The New Project picker offers only blank/templates.",
|
|
77
|
+
),
|
|
78
|
+
|
|
79
|
+
// ─── Filesystem ───────────────────────────────────────────────────────────
|
|
80
|
+
files: route("GET", "/__studio/files", "List a directory (DirEntry[])"),
|
|
81
|
+
fileRead: route("GET", "/__studio/file", "Read a file's text content"),
|
|
82
|
+
fileWrite: route("PUT", "/__studio/file", "Write a file's text content"),
|
|
83
|
+
fileDelete: route("DELETE", "/__studio/file", "Delete a file"),
|
|
84
|
+
fileUpload: route("POST", "/__studio/file/upload", "Upload binary content to a path"),
|
|
85
|
+
fileRename: route("POST", "/__studio/file/rename", "Rename/move (+ refactor report)"),
|
|
86
|
+
locate: route("POST", "/__studio/locate", "Find a file by name → {path | null}"),
|
|
87
|
+
|
|
88
|
+
// ─── Documents / components / formats ─────────────────────────────────────
|
|
89
|
+
components: route("GET", "/__studio/components", "Discover components (ComponentMeta[])"),
|
|
90
|
+
cem: route(
|
|
91
|
+
"GET",
|
|
92
|
+
"/__studio/cem",
|
|
93
|
+
"Custom-elements manifest of an npm dependency",
|
|
94
|
+
"Dependency components lose prop/slot metadata.",
|
|
95
|
+
),
|
|
96
|
+
formats: route(
|
|
97
|
+
"GET",
|
|
98
|
+
"/__studio/formats",
|
|
99
|
+
"The project's format registry entries",
|
|
100
|
+
"Only .json documents open (backs listFormats).",
|
|
101
|
+
),
|
|
102
|
+
format: route(
|
|
103
|
+
"POST",
|
|
104
|
+
"/__studio/format",
|
|
105
|
+
"Dispatch a format capability {format, action: parse|serialize, source?|doc?}",
|
|
106
|
+
"Non-JSON documents cannot be parsed/serialized (backs formatAction).",
|
|
107
|
+
),
|
|
108
|
+
pluginSchema: route(
|
|
109
|
+
"GET",
|
|
110
|
+
"/__studio/plugin-schema",
|
|
111
|
+
"Extract a $studio schema from a class source",
|
|
112
|
+
"Plugin property panels fall back to generic JSON editing.",
|
|
113
|
+
),
|
|
114
|
+
|
|
115
|
+
// ─── Packages ─────────────────────────────────────────────────────────────
|
|
116
|
+
packages: route("GET", "/__studio/packages", "List dependencies (PackageInfo[])"),
|
|
117
|
+
packagesAdd: route("POST", "/__studio/packages/add", "Add a dependency"),
|
|
118
|
+
packagesRemove: route("POST", "/__studio/packages/remove", "Remove a dependency"),
|
|
119
|
+
packagesInstall: route(
|
|
120
|
+
"POST",
|
|
121
|
+
"/__studio/packages/install",
|
|
122
|
+
"Run the package manager install",
|
|
123
|
+
"Install/reinstall affordances are hidden; manifest-only edits still work.",
|
|
124
|
+
),
|
|
125
|
+
packagesNeedsInstall: route(
|
|
126
|
+
"GET",
|
|
127
|
+
"/__studio/packages/needs-install",
|
|
128
|
+
"Whether node_modules is stale",
|
|
129
|
+
"The install-on-open prompt never shows.",
|
|
130
|
+
),
|
|
131
|
+
packagesOutdated: route(
|
|
132
|
+
"GET",
|
|
133
|
+
"/__studio/packages/outdated",
|
|
134
|
+
"Dependencies with newer versions (OutdatedInfo[])",
|
|
135
|
+
"The update affordances are hidden.",
|
|
136
|
+
),
|
|
137
|
+
packagesSetVersions: route(
|
|
138
|
+
"POST",
|
|
139
|
+
"/__studio/packages/set-versions",
|
|
140
|
+
"Rewrite dependency ranges and install",
|
|
141
|
+
"Bulk version updates are hidden.",
|
|
142
|
+
),
|
|
143
|
+
|
|
144
|
+
// ─── Git ──────────────────────────────────────────────────────────────────
|
|
145
|
+
gitStatus: route("GET", "/__studio/git/status", "Working-tree status (GitStatusResult)"),
|
|
146
|
+
gitBranches: route("GET", "/__studio/git/branches", "Branch list (GitBranchesResult)"),
|
|
147
|
+
gitLog: route("GET", "/__studio/git/log", "Recent commits (GitLogEntry[])"),
|
|
148
|
+
gitStage: route("POST", "/__studio/git/stage", "Stage files"),
|
|
149
|
+
gitUnstage: route("POST", "/__studio/git/unstage", "Unstage files"),
|
|
150
|
+
gitCommit: route("POST", "/__studio/git/commit", "Commit staged (else all dirty) files"),
|
|
151
|
+
gitPush: route("POST", "/__studio/git/push", "Push (cloud: sync check — commits land on push)"),
|
|
152
|
+
gitPull: route("POST", "/__studio/git/pull", "Pull/fast-forward; 409 {conflicts} on overlap"),
|
|
153
|
+
gitFetch: route("POST", "/__studio/git/fetch", "Refresh remote tracking state"),
|
|
154
|
+
gitCheckout: route("POST", "/__studio/git/checkout", "Switch branches"),
|
|
155
|
+
gitCreateBranch: route("POST", "/__studio/git/create-branch", "Create a branch"),
|
|
156
|
+
gitDiff: route("GET", "/__studio/git/diff", "Unified diff of dirty files (or one path)"),
|
|
157
|
+
gitShow: route("GET", "/__studio/git/show", "File content at a ref"),
|
|
158
|
+
gitDiscard: route("POST", "/__studio/git/discard", "Discard working changes"),
|
|
159
|
+
gitInit: route("POST", "/__studio/git/init", "Initialize a repository"),
|
|
160
|
+
gitAddRemote: route("POST", "/__studio/git/add-remote", "Add a remote"),
|
|
161
|
+
gitClone: route(
|
|
162
|
+
"POST",
|
|
163
|
+
"/__studio/git/clone",
|
|
164
|
+
"Clone a repository",
|
|
165
|
+
"The welcome screen hides Clone Git Repository.",
|
|
166
|
+
),
|
|
167
|
+
gitPr: route(
|
|
168
|
+
"POST",
|
|
169
|
+
"/__studio/git/pr",
|
|
170
|
+
"Open a pull request → PullRequestInfo",
|
|
171
|
+
"Studio falls back to a direct GitHub API call with the user's token.",
|
|
172
|
+
),
|
|
173
|
+
|
|
174
|
+
// ─── AI proxy ─────────────────────────────────────────────────────────────
|
|
175
|
+
aiChat: route(
|
|
176
|
+
"POST",
|
|
177
|
+
"/__studio/ai/chat",
|
|
178
|
+
"StreamEvent SSE chat proxy {messages, tools, systemPrompt, model}",
|
|
179
|
+
),
|
|
180
|
+
aiModels: route("GET", "/__studio/ai/models", "Model catalogue (AiModelsResponse)"),
|
|
181
|
+
|
|
182
|
+
// ─── Cloudflare publish surface ───────────────────────────────────────────
|
|
183
|
+
cfProxy: route(
|
|
184
|
+
"POST",
|
|
185
|
+
"/__studio/cf/proxy",
|
|
186
|
+
"Allowlisted Cloudflare API passthrough {path, method?, body?} (backs cfApi)",
|
|
187
|
+
"The Publish panel explains the git-push publishing path instead.",
|
|
188
|
+
),
|
|
189
|
+
} as const satisfies Record<string, StudioRoute>;
|
|
190
|
+
|
|
191
|
+
/** A stable route name in {@link STUDIO_ROUTES}. */
|
|
192
|
+
export type StudioRouteName = keyof typeof STUDIO_ROUTES;
|
|
193
|
+
|
|
194
|
+
/** Names of every route a minimal (core) backend must implement. */
|
|
195
|
+
export function coreRouteNames(): StudioRouteName[] {
|
|
196
|
+
return (Object.keys(STUDIO_ROUTES) as StudioRouteName[]).filter(
|
|
197
|
+
(name) => !STUDIO_ROUTES[name].optional,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Names of the optional routes (each backs an optional StudioPlatform member). */
|
|
202
|
+
export function optionalRouteNames(): StudioRouteName[] {
|
|
203
|
+
return (Object.keys(STUDIO_ROUTES) as StudioRouteName[]).filter(
|
|
204
|
+
(name) => STUDIO_ROUTES[name].optional,
|
|
205
|
+
);
|
|
206
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio Backend Protocol — wire types. Every shape a Studio backend serves
|
|
3
|
+
* (or a `StudioPlatform` adapter consumes) lives here so client adapters and
|
|
4
|
+
* server implementations (the dev server, the desktop RPC bridge, cloud
|
|
5
|
+
* platforms) share one contract. Environment-agnostic: no DOM, no node —
|
|
6
|
+
* importable in browsers, Bun, and Cloudflare Workers alike.
|
|
7
|
+
*
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { JsonValue as SchemaJsonValue, JxMutableNode } from "@jxsuite/schema/types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A JSON document value, or `undefined` to signal property removal in the mutators. Re-uses the
|
|
15
|
+
* schema's precise recursive JSON model.
|
|
16
|
+
*/
|
|
17
|
+
export type JsonValue = SchemaJsonValue | undefined;
|
|
18
|
+
|
|
19
|
+
// ─── Errors ──────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
/** Message-level failure body every protocol route may return. */
|
|
22
|
+
export interface ErrorBody {
|
|
23
|
+
error: string;
|
|
24
|
+
/** Machine-readable discriminator (e.g. "remote_moved", "cf_not_connected"). */
|
|
25
|
+
code?: string;
|
|
26
|
+
detail?: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── Filesystem ──────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
export interface DirEntry {
|
|
32
|
+
name: string;
|
|
33
|
+
path: string;
|
|
34
|
+
type: "file" | "directory";
|
|
35
|
+
size?: number;
|
|
36
|
+
modified?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A filesystem change pushed from the backend (project-relative, forward-slashed path). */
|
|
40
|
+
export interface FsEvent {
|
|
41
|
+
type: "add" | "change" | "unlink" | "addDir" | "unlinkDir";
|
|
42
|
+
path: string;
|
|
43
|
+
isDir: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Result of a rename, including the references rewritten across the project (refactor report). */
|
|
47
|
+
export interface RenameResult {
|
|
48
|
+
ok: boolean;
|
|
49
|
+
from: string;
|
|
50
|
+
to: string;
|
|
51
|
+
isDir?: boolean;
|
|
52
|
+
references?: {
|
|
53
|
+
filesChanged: number;
|
|
54
|
+
refsUpdated: number;
|
|
55
|
+
files: { path: string; count: number }[];
|
|
56
|
+
};
|
|
57
|
+
errors?: { path: string; error: string }[];
|
|
58
|
+
tag?: { from: string; to: string; filesChanged: number; refsUpdated: number };
|
|
59
|
+
tagSkipped?: string;
|
|
60
|
+
error?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Git ─────────────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
export interface GitFileStatus {
|
|
66
|
+
status: string;
|
|
67
|
+
path: string;
|
|
68
|
+
staged?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface GitStatusResult {
|
|
72
|
+
branch: string;
|
|
73
|
+
files: GitFileStatus[];
|
|
74
|
+
ahead: number;
|
|
75
|
+
behind: number;
|
|
76
|
+
isRepo: boolean;
|
|
77
|
+
remotes: string[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface GitBranchesResult {
|
|
81
|
+
current: string;
|
|
82
|
+
branches: string[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface GitLogEntry {
|
|
86
|
+
hash: string;
|
|
87
|
+
message: string;
|
|
88
|
+
author: string;
|
|
89
|
+
date: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Response of the pull-request route (see StudioPlatform.createPullRequest). */
|
|
93
|
+
export interface PullRequestInfo {
|
|
94
|
+
url: string;
|
|
95
|
+
number: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─── Components ──────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
export interface ComponentSlotMeta {
|
|
101
|
+
name: string;
|
|
102
|
+
description?: string;
|
|
103
|
+
fallback?: (JxMutableNode | string)[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface ComponentMeta {
|
|
107
|
+
tagName: string;
|
|
108
|
+
$id?: string | null;
|
|
109
|
+
path: string;
|
|
110
|
+
props?: { name: string; type?: string; default?: JsonValue; [k: string]: unknown }[];
|
|
111
|
+
slots?: ComponentSlotMeta[];
|
|
112
|
+
hasElements?: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── Packages ────────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
export interface PackageInfo {
|
|
118
|
+
name: string;
|
|
119
|
+
version: string;
|
|
120
|
+
/** True when the dependency lives in `devDependencies` rather than `dependencies`. */
|
|
121
|
+
dev?: boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** A dependency with a newer version available, as reported by `bun outdated` / the npm registry. */
|
|
125
|
+
export interface OutdatedInfo {
|
|
126
|
+
name: string;
|
|
127
|
+
/** The version range pinned in package.json (e.g. "^0.19.0"). */
|
|
128
|
+
current: string;
|
|
129
|
+
/** The newest published version. */
|
|
130
|
+
latest: string;
|
|
131
|
+
/** The newest version satisfying the current range, if known. */
|
|
132
|
+
wanted?: string;
|
|
133
|
+
dev?: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Result of a package mutation that runs `bun install` (install / set-versions). */
|
|
137
|
+
export interface PackageOpResult {
|
|
138
|
+
ok: boolean;
|
|
139
|
+
/** Combined stdout/stderr from the bun invocation, surfaced to the user on failure. */
|
|
140
|
+
log?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── App / code services ─────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/** Desktop app/build info surfaced in the About screen. */
|
|
146
|
+
export interface AppInfo {
|
|
147
|
+
version: string;
|
|
148
|
+
channel: string;
|
|
149
|
+
hash: string;
|
|
150
|
+
/** Human-readable update status (e.g. "Up to date", "Update available"), if known. */
|
|
151
|
+
updateStatus?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface CodeServiceResult {
|
|
155
|
+
code?: string;
|
|
156
|
+
diagnostics?: unknown[];
|
|
157
|
+
[key: string]: unknown;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Projects & starters ─────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
/** A starter template surfaced in the New Project picker (mirrors @jxsuite/starters StarterMeta). */
|
|
163
|
+
export interface StarterInfo {
|
|
164
|
+
id: string;
|
|
165
|
+
name: string;
|
|
166
|
+
industry: string;
|
|
167
|
+
tagline: string;
|
|
168
|
+
description: string;
|
|
169
|
+
features: string[];
|
|
170
|
+
accent: string;
|
|
171
|
+
/** Preview image as a self-contained `data:` URI. */
|
|
172
|
+
thumbnail: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** A recently-opened project, keyed by its re-openable `root` (platform-specific). */
|
|
176
|
+
export interface RecentProjectEntry {
|
|
177
|
+
name: string;
|
|
178
|
+
root: string;
|
|
179
|
+
timestamp: number;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** One entry in the platform's project catalogue (see StudioPlatform.listProjects). */
|
|
183
|
+
export interface ProjectListEntry {
|
|
184
|
+
/** Display name (project.json name, repository name, ...). */
|
|
185
|
+
name: string;
|
|
186
|
+
/** Re-openable root key (server-relative path, owner/repo, absolute path). */
|
|
187
|
+
root: string;
|
|
188
|
+
/** Optional one-line descriptor shown under the name (path, permission, ...). */
|
|
189
|
+
description?: string | undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ─── Site import ─────────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
/** A progress line from the AI-guided site import (mirrors @jxsuite/import ImportProgressEvent). */
|
|
195
|
+
export interface ImportProgressEvent {
|
|
196
|
+
phase: string;
|
|
197
|
+
message: string;
|
|
198
|
+
current?: number;
|
|
199
|
+
total?: number;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Options for the import-site pipeline (StudioPlatform.importSite). */
|
|
203
|
+
export interface ImportSiteOptions {
|
|
204
|
+
/** The live site to clone; must be http(s). */
|
|
205
|
+
url: string;
|
|
206
|
+
/** Display name for the new project. */
|
|
207
|
+
name: string;
|
|
208
|
+
/** Destination directory (platform-interpreted: project-relative on the dev server). */
|
|
209
|
+
directory: string;
|
|
210
|
+
/** Max crawl depth; 0 = single page. */
|
|
211
|
+
depth: number;
|
|
212
|
+
/** Max pages to capture. */
|
|
213
|
+
maxPages: number;
|
|
214
|
+
/** Refine component/prop names with the LLM (requires a key). */
|
|
215
|
+
aiComponents: boolean;
|
|
216
|
+
/** OpenAI-compatible credentials, from the user's AI settings. */
|
|
217
|
+
apiKey?: string;
|
|
218
|
+
baseUrl?: string;
|
|
219
|
+
model?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ─── AI proxy ────────────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
/** One entry of the AI proxy's model catalogue. */
|
|
225
|
+
export interface AiModelInfo {
|
|
226
|
+
id: string;
|
|
227
|
+
name?: string;
|
|
228
|
+
contextWindow?: number;
|
|
229
|
+
/** Whether the model supports tool/function calling (agentic editing). */
|
|
230
|
+
toolSupport?: boolean;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Response of the AI proxy's models route (the chat route's sibling). */
|
|
234
|
+
export interface AiModelsResponse {
|
|
235
|
+
models: AiModelInfo[];
|
|
236
|
+
/** True when the backend holds working credentials (env key, managed platform). */
|
|
237
|
+
configured: boolean;
|
|
238
|
+
/** True when the platform manages AI credentials itself (e.g. cloud Workers AI). */
|
|
239
|
+
managed?: boolean;
|
|
240
|
+
/** The backend's preferred model id, when it declares one. */
|
|
241
|
+
defaultModel?: string;
|
|
242
|
+
/** Set when the upstream provider was unreachable and defaults were returned. */
|
|
243
|
+
upstreamError?: number | string;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ─── Cloudflare publish surface ──────────────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
/** Cloudflare connection state (see StudioPlatform.cfConnection). */
|
|
249
|
+
export interface CfConnection {
|
|
250
|
+
connected: boolean;
|
|
251
|
+
accountId?: string | undefined;
|
|
252
|
+
accountName?: string | undefined;
|
|
253
|
+
}
|