@jxsuite/protocol 2.3.0 → 2.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/protocol",
3
- "version": "2.3.0",
3
+ "version": "2.4.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": "^2.2.0"
30
+ "@jxsuite/schema": "^2.3.0"
31
31
  }
32
32
  }
package/src/routes.ts CHANGED
@@ -83,7 +83,7 @@ export const STUDIO_ROUTES = {
83
83
  "GET",
84
84
  "/__studio/locate-directory",
85
85
  `Resolve the absolute path of a showDirectoryPicker() folder by the id in its ${LOCATION_ID_FILE}`,
86
- "The New Project Location field loses its Browse… button and is typed by hand.",
86
+ "Browse… reports that the folder could not be placed; the Location is typed by hand.",
87
87
  ),
88
88
  createProject: route(
89
89
  "POST",
@@ -119,6 +119,31 @@ export const STUDIO_ROUTES = {
119
119
  "Upload binary content to a path → UploadResult {path, size?}. `path` is the answer, not an " +
120
120
  "echo: a store that de-duplicates, suffixes, or normalizes names reports what it really wrote",
121
121
  ),
122
+ /**
123
+ * A project file's raw BYTES, as an answer rather than as a URL.
124
+ *
125
+ * `fileRead` cannot serve this and never could: it answers `readFile(abs, "utf8")`, and Bun
126
+ * replaces every byte sequence that is not valid UTF-8 with U+FFFD — so a JPEG round-trips
127
+ * through it as mojibake, silently and losslessly-looking. A caller that wants to DECODE a
128
+ * project image (Studio's image editor) needs the bytes intact.
129
+ *
130
+ * Distinct from {@link documentRaw}, which serves files at a URL a `<img src>` or a `fetch` in
131
+ * the canvas realm can name. That is the right shape for rendering and the wrong one for reading:
132
+ * on the desktop the canvas origin is a cross-origin loopback, and `server.md` §4.2 bans CORS
133
+ * outright, so nothing in the shell can read those bytes back. This route is addressed by the
134
+ * PAL, not by a document, which is what lets every backend answer it the same way.
135
+ *
136
+ * `Cache-Control: no-store`, deliberately unlike `fileRead`'s `max-age=5`: the caller is an
137
+ * editor about to overwrite the file, and five seconds of staleness is an edit applied to the
138
+ * wrong bytes.
139
+ */
140
+ fileBytes: route(
141
+ "GET",
142
+ "/__studio/file/bytes",
143
+ "A project file's raw bytes, undecoded — the read side of `fileUpload`, for a caller that must " +
144
+ "not go through UTF-8",
145
+ "Studio can only read a project file as text, so any surface that must decode one stays hidden.",
146
+ ),
122
147
  /**
123
148
  * A project file as its own BYTES, at its own URL.
124
149
  *
package/src/types.ts CHANGED
@@ -758,6 +758,13 @@ export interface AiModelsResponse {
758
758
  defaultModel?: string;
759
759
  /** Set when the upstream provider was unreachable and defaults were returned. */
760
760
  upstreamError?: number | string;
761
+ /**
762
+ * The upstream's own error message, when {@link upstreamError} is set. Populated from whatever
763
+ * envelope the upstream used — OpenAI's `{error}`, or an array-shaped one like Cloudflare's
764
+ * `{errors: [{message}]}` — so a caller can show the real reason (e.g. "No route for that URI")
765
+ * instead of a bare status code.
766
+ */
767
+ upstreamMessage?: string;
761
768
  /**
762
769
  * Why the backend holds no working credentials, when it holds none.
763
770
  *
@@ -765,9 +772,10 @@ export interface AiModelsResponse {
765
772
  * lapsed", and those are different sentences on screen — the second one explains a thing that
766
773
  * used to work. `cf_upstream_error` is the third case and deliberately arrives WITH `configured:
767
774
  * true`: Cloudflare being briefly unreachable is not a reason to send someone round an OAuth flow
768
- * that fixes nothing.
775
+ * that fixes nothing. `cf_account_required` is a live grant with no account chosen (ai.md §2.1):
776
+ * re-authorizing lands back in the same state, so it calls for the account picker instead.
769
777
  */
770
- code?: "cf_not_connected" | "cf_reconnect_required" | "cf_upstream_error";
778
+ code?: "cf_not_connected" | "cf_reconnect_required" | "cf_account_required" | "cf_upstream_error";
771
779
  }
772
780
 
773
781
  // ─── Cloudflare publish surface ──────────────────────────────────────────────