From 375d00d3d5c7ba24c2c8399b7f0f2a730f3b59d2 Mon Sep 17 00:00:00 2001 From: HarlonWang <81813780@qq.com> Date: Wed, 7 Oct 2026 11:51:40 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=AD=97=E8=8A=82=E7=A0=81?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8E=E7=BC=80=20.bin=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=20.qjsb=EF=BC=88tinyui=200.9.0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 与 tiny-ui/tinyui#29 同步:manifest 派生的页面文件路径由 `.bin` 改为 `.qjsb`,上传校验与发布前核对随之。已存储的旧版本按路径原样投递不受影响; 部署后只有 tinyui-cli 0.9 起能发新包,旧 CLI 的 .bin 上传会被判为缺文件。 Co-Authored-By: Claude Fable 5.1 --- README.md | 2 +- README.zh-CN.md | 2 +- src/ids.ts | 2 +- src/manifest.ts | 4 ++-- test/helpers.ts | 4 ++-- test/publish.test.ts | 24 ++++++++++++------------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1e16b22..2b27ec2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Two kinds of `GET`, relative to `https:////`: | Path | Content | Cache | |---|---|---| | `//current.json` | mutable pointer: `{ version, rollout, signature }` | `no-store` | -| `///…` | immutable content: `manifest.json`, `pages/**/*.bin` (the runtime ships inside the App, not in packages) | `immutable` | +| `///…` | immutable content: `manifest.json`, `pages/**/*.qjsb` (the runtime ships inside the App, not in packages) | `immutable` | `` is the positive integer a host App declares for what it promises to pages: it goes up when host components or capabilities change, when pages may use a newer TinyUI runtime, or when the JS engine changes. It is not the TinyUI version: upgrading TinyUI alone leaves it as is, and the manifest's `engine` / `tinyui` are checked on their own; it plays the role of Expo's `runtimeVersion` but is always a positive integer that counts host changes, never an App version such as `1.2.0`; see the TinyUI repo's `docs/updates.md`. diff --git a/README.zh-CN.md b/README.zh-CN.md index b5b76e7..ff970b7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -13,7 +13,7 @@ | 路径 | 内容 | 缓存 | |---|---|---| | `//current.json` | 可变指针:`{ version, rollout, signature }` | `no-store` | -| `///…` | 不可变内容:`manifest.json`、`pages/**/*.bin`(运行时随 App,不在包里) | `immutable` | +| `///…` | 不可变内容:`manifest.json`、`pages/**/*.qjsb`(运行时随 App,不在包里) | `immutable` | `` 是宿主 App 为"它对页面承诺了什么"声明的正整数:宿主组件、宿主能力增删或改动,页面要用更新的 TinyUI 运行时,或者换了 JS 引擎,都加 1。它不是 TinyUI 版本:单纯升级 TinyUI 不动它,manifest 的 `engine` / `tinyui` 另行校验,作用同 Expo 的 `runtimeVersion`,但取值只能是正整数、数的是宿主的变化,不是 `1.2.0` 这类 App 版本号,见 TinyUI 仓的 `docs/updates.md`。 diff --git a/src/ids.ts b/src/ids.ts index 5c53be1..f49089c 100644 --- a/src/ids.ts +++ b/src/ids.ts @@ -13,7 +13,7 @@ export function isSegment(value: string): boolean { return /^[A-Za-z0-9._-]+$/.test(value) && value !== "." && value !== ".."; } -/** A file path inside a version directory: `pages/home.bin`, `manifest.json`. */ +/** A file path inside a version directory: `pages/home.qjsb`, `manifest.json`. */ export function isObjectPath(value: string): boolean { const segments = value.split("/"); return segments.length > 0 && segments.every(isSegment); diff --git a/src/manifest.ts b/src/manifest.ts index fb8e0af..bbf0bae 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -18,7 +18,7 @@ export interface Manifest { /** Every file of the version besides `manifest.json`, with its key into `hashes`. */ export function payload(manifest: Manifest): { path: string; hashKey: string }[] { return [ - ...manifest.pages.map((module) => ({ path: `${manifest.files[module]}.bin`, hashKey: module })), + ...manifest.pages.map((module) => ({ path: `${manifest.files[module]}.qjsb`, hashKey: module })), ...Object.values(manifest.i18n?.files ?? {}).map((path) => ({ path, hashKey: path })), ]; } @@ -82,7 +82,7 @@ export function parseManifest(text: string): Manifest { if (keys.join("\n") !== [...expected[key]].sort().join("\n")) throw new Error(`manifest.json ${key} does not cover exactly the modules in pages${key === "hashes" ? " and the i18n files" : ""}`); } for (const path of Object.values(manifest.files)) { - if (!isObjectPath(path + ".bin")) throw new Error(`manifest.json files entry ${path} is not a file path`); + if (!isObjectPath(path + ".qjsb")) throw new Error(`manifest.json files entry ${path} is not a file path`); } for (const path of strings) { if (!isObjectPath(path)) throw new Error(`manifest.json i18n file ${path} is not a file path`); diff --git a/test/helpers.ts b/test/helpers.ts index 22cfd26..4c1e99c 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -34,11 +34,11 @@ export async function signedPackage(options: { const version = options.version ?? "20260922T100000Z-abcdef1"; const key = options.key ?? (await newKeyPair()); const strings = Object.fromEntries(Object.entries(options.i18n ?? {}).map(([language, text]) => [`i18n/${language}.json`, text])); - const contents = { ...(options.files ?? { "pages/home.bin": `HOME-${version}` }), ...strings }; + const contents = { ...(options.files ?? { "pages/home.qjsb": `HOME-${version}` }), ...strings }; const files = Object.fromEntries(Object.entries(contents).map(([p, text]) => [p, new TextEncoder().encode(text)])); const modules: Record = { [`${name}/home`]: "pages/home" }; const hashes: Record = {}; - for (const [module, path] of Object.entries(modules)) hashes[module] = await sha256Hex(files[`${path}.bin`]!); + for (const [module, path] of Object.entries(modules)) hashes[module] = await sha256Hex(files[`${path}.qjsb`]!); for (const path of Object.keys(strings)) hashes[path] = await sha256Hex(files[path]!); const manifest: Record = { pages: [`${name}/home`], diff --git a/test/publish.test.ts b/test/publish.test.ts index f60edd1..714bcfa 100644 --- a/test/publish.test.ts +++ b/test/publish.test.ts @@ -22,10 +22,10 @@ describe("publishing and delivery", () => { expect(manifest.status).toBe(200); expect(manifest.headers.get("cache-control")).toBe("public, max-age=31536000, immutable"); expect(new Uint8Array(await manifest.arrayBuffer())).toEqual(p.manifest); - const home = await request(`/${app}/production/${pkg}/1/${p.version}/pages/home.bin`); + const home = await request(`/${app}/production/${pkg}/1/${p.version}/pages/home.qjsb`); expect(new TextDecoder().decode(await home.arrayBuffer())).toBe(`HOME-${p.version}`); // the channel is a pointer, not a place: another channel reads the same bytes even before it points there - expect((await request(`/${app}/staging/${pkg}/1/${p.version}/pages/home.bin`)).status).toBe(200); + expect((await request(`/${app}/staging/${pkg}/1/${p.version}/pages/home.qjsb`)).status).toBe(200); expect((await request(`/${app}/staging/${pkg}/1/current.json`)).status).toBe(404); }); @@ -39,9 +39,9 @@ describe("publishing and delivery", () => { await request(`/${app}/${pkg}/1/${p.version}/manifest.json`, { method: "PUT", token, body: p.manifest as BodyInit }); const missing = await publishPointer(app, "production", pkg, "1", p, token); expect(missing.status).toBe(409); - expect(await missing.text()).toContain("pages/home.bin is not uploaded yet"); + expect(await missing.text()).toContain("pages/home.qjsb is not uploaded yet"); - const tampered = await signedPackage({ files: { "pages/home.bin": "HOME-x" }, key: { publicKey: p.publicKey, privateKey: p.privateKey } }); + const tampered = await signedPackage({ files: { "pages/home.qjsb": "HOME-x" }, key: { publicKey: p.publicKey, privateKey: p.privateKey } }); for (const [path, bytes] of Object.entries(tampered.files)) { await request(`/${app}/${pkg}/1/${p.version}/${path}`, { method: "PUT", token, body: bytes as BodyInit }); } @@ -54,7 +54,7 @@ describe("publishing and delivery", () => { it("publishes a package with strings only once they are uploaded and match, and serves them", async () => { const p = await signedPackage({ i18n: { en: '{"hi":"Hi"}', zh: '{"hi":"你好"}' } }); const { app, pkg, token } = await setup({ publicKey: p.publicKey }); - await request(`/${app}/${pkg}/1/${p.version}/pages/home.bin`, { method: "PUT", token, body: p.files["pages/home.bin"] as BodyInit }); + await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: p.files["pages/home.qjsb"] as BodyInit }); await request(`/${app}/${pkg}/1/${p.version}/manifest.json`, { method: "PUT", token, body: p.manifest as BodyInit }); const missing = await publishPointer(app, "production", pkg, "1", p, token); expect(missing.status).toBe(409); @@ -88,14 +88,14 @@ describe("publishing and delivery", () => { it("keeps a version immutable: the same bytes again are fine, different ones are refused", async () => { const p = await signedPackage(); const { app, pkg, token } = await setup({ publicKey: p.publicKey }); - const first = await request(`/${app}/${pkg}/1/${p.version}/pages/home.bin`, { method: "PUT", token, body: p.files["pages/home.bin"] as BodyInit }); + const first = await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: p.files["pages/home.qjsb"] as BodyInit }); expect(first.status).toBe(201); - const again = await request(`/${app}/${pkg}/1/${p.version}/pages/home.bin`, { method: "PUT", token, body: p.files["pages/home.bin"] as BodyInit }); + const again = await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: p.files["pages/home.qjsb"] as BodyInit }); expect(again.status).toBe(200); expect(await again.json()).toMatchObject({ existing: true }); - const other = await request(`/${app}/${pkg}/1/${p.version}/pages/home.bin`, { method: "PUT", token, body: "something else" }); + const other = await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: "something else" }); expect(other.status).toBe(409); - const tooBig = await request(`/${app}/${pkg}/1/${p.version}/pages/big.bin`, { method: "PUT", token, body: new Uint8Array(1048577) as BodyInit }); + const tooBig = await request(`/${app}/${pkg}/1/${p.version}/pages/big.qjsb`, { method: "PUT", token, body: new Uint8Array(1048577) as BodyInit }); expect(tooBig.status).toBe(413); }); @@ -246,7 +246,7 @@ describe("publishing and delivery", () => { "", ].join("\n"); const { app, pkg, token } = await setup({ publicKey }); - for (const [path, text] of Object.entries({ "pages/home.bin": "HOME2" })) { + for (const [path, text] of Object.entries({ "pages/home.qjsb": "HOME2" })) { expect((await request(`/${app}/${pkg}/1/${version}/${path}`, { method: "PUT", token, body: text })).status).toBe(201); } expect((await request(`/${app}/${pkg}/1/${version}/manifest.json`, { method: "PUT", token, body: manifest })).status).toBe(201); @@ -263,7 +263,7 @@ describe("publishing and delivery", () => { const { app, pkg, token } = await setup({ publicKey: v1.publicKey }); // two different bodies race for one path: exactly one wins, the other is told so - const race = await Promise.all(["one", "two"].map((body) => request(`/${app}/${pkg}/1/v1/pages/race.bin`, { method: "PUT", token, body }))); + const race = await Promise.all(["one", "two"].map((body) => request(`/${app}/${pkg}/1/v1/pages/race.qjsb`, { method: "PUT", token, body }))); expect(race.map((r) => r.status).sort()).toEqual([201, 409]); await uploadContent(app, pkg, "1", v1, token); @@ -280,7 +280,7 @@ describe("publishing and delivery", () => { const p = await signedPackage(); const { app, pkg, token } = await setup({ publicKey: p.publicKey }); expect((await request(`/${app}/${pkg}/1/../manifest.json`, { method: "PUT", token, body: "x" })).status).not.toBe(201); - expect((await request(`/${app}/${pkg}/1/v1/..%2Fescape.bin`, { method: "PUT", token, body: "x" })).status).toBe(404); + expect((await request(`/${app}/${pkg}/1/v1/..%2Fescape.qjsb`, { method: "PUT", token, body: "x" })).status).toBe(404); expect((await request(`/${app}/Prod/${pkg}/1/current.json`)).status).toBe(404); // `apps` is reserved: never served as an app expect((await request(`/apps/production/${pkg}/1/current.json`)).status).toBe(404); From cfd8f9cb4dcd94a5c83d6d59018382ab2180f9a8 Mon Sep 17 00:00:00 2001 From: HarlonWang <81813780@qq.com> Date: Wed, 7 Oct 2026 12:15:33 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=AD=97=E8=8A=82=E7=A0=81=E5=90=8E?= =?UTF-8?q?=E7=BC=80=E5=AE=9A=E4=B8=BA=20.jsb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 --- README.md | 2 +- README.zh-CN.md | 2 +- src/ids.ts | 2 +- src/manifest.ts | 4 ++-- test/helpers.ts | 4 ++-- test/publish.test.ts | 24 ++++++++++++------------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 2b27ec2..acdf38c 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Two kinds of `GET`, relative to `https:////`: | Path | Content | Cache | |---|---|---| | `//current.json` | mutable pointer: `{ version, rollout, signature }` | `no-store` | -| `///…` | immutable content: `manifest.json`, `pages/**/*.qjsb` (the runtime ships inside the App, not in packages) | `immutable` | +| `///…` | immutable content: `manifest.json`, `pages/**/*.jsb` (the runtime ships inside the App, not in packages) | `immutable` | `` is the positive integer a host App declares for what it promises to pages: it goes up when host components or capabilities change, when pages may use a newer TinyUI runtime, or when the JS engine changes. It is not the TinyUI version: upgrading TinyUI alone leaves it as is, and the manifest's `engine` / `tinyui` are checked on their own; it plays the role of Expo's `runtimeVersion` but is always a positive integer that counts host changes, never an App version such as `1.2.0`; see the TinyUI repo's `docs/updates.md`. diff --git a/README.zh-CN.md b/README.zh-CN.md index ff970b7..0ac97a2 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -13,7 +13,7 @@ | 路径 | 内容 | 缓存 | |---|---|---| | `//current.json` | 可变指针:`{ version, rollout, signature }` | `no-store` | -| `///…` | 不可变内容:`manifest.json`、`pages/**/*.qjsb`(运行时随 App,不在包里) | `immutable` | +| `///…` | 不可变内容:`manifest.json`、`pages/**/*.jsb`(运行时随 App,不在包里) | `immutable` | `` 是宿主 App 为"它对页面承诺了什么"声明的正整数:宿主组件、宿主能力增删或改动,页面要用更新的 TinyUI 运行时,或者换了 JS 引擎,都加 1。它不是 TinyUI 版本:单纯升级 TinyUI 不动它,manifest 的 `engine` / `tinyui` 另行校验,作用同 Expo 的 `runtimeVersion`,但取值只能是正整数、数的是宿主的变化,不是 `1.2.0` 这类 App 版本号,见 TinyUI 仓的 `docs/updates.md`。 diff --git a/src/ids.ts b/src/ids.ts index f49089c..83a2d98 100644 --- a/src/ids.ts +++ b/src/ids.ts @@ -13,7 +13,7 @@ export function isSegment(value: string): boolean { return /^[A-Za-z0-9._-]+$/.test(value) && value !== "." && value !== ".."; } -/** A file path inside a version directory: `pages/home.qjsb`, `manifest.json`. */ +/** A file path inside a version directory: `pages/home.jsb`, `manifest.json`. */ export function isObjectPath(value: string): boolean { const segments = value.split("/"); return segments.length > 0 && segments.every(isSegment); diff --git a/src/manifest.ts b/src/manifest.ts index bbf0bae..7415547 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -18,7 +18,7 @@ export interface Manifest { /** Every file of the version besides `manifest.json`, with its key into `hashes`. */ export function payload(manifest: Manifest): { path: string; hashKey: string }[] { return [ - ...manifest.pages.map((module) => ({ path: `${manifest.files[module]}.qjsb`, hashKey: module })), + ...manifest.pages.map((module) => ({ path: `${manifest.files[module]}.jsb`, hashKey: module })), ...Object.values(manifest.i18n?.files ?? {}).map((path) => ({ path, hashKey: path })), ]; } @@ -82,7 +82,7 @@ export function parseManifest(text: string): Manifest { if (keys.join("\n") !== [...expected[key]].sort().join("\n")) throw new Error(`manifest.json ${key} does not cover exactly the modules in pages${key === "hashes" ? " and the i18n files" : ""}`); } for (const path of Object.values(manifest.files)) { - if (!isObjectPath(path + ".qjsb")) throw new Error(`manifest.json files entry ${path} is not a file path`); + if (!isObjectPath(path + ".jsb")) throw new Error(`manifest.json files entry ${path} is not a file path`); } for (const path of strings) { if (!isObjectPath(path)) throw new Error(`manifest.json i18n file ${path} is not a file path`); diff --git a/test/helpers.ts b/test/helpers.ts index 4c1e99c..70a2e3c 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -34,11 +34,11 @@ export async function signedPackage(options: { const version = options.version ?? "20260922T100000Z-abcdef1"; const key = options.key ?? (await newKeyPair()); const strings = Object.fromEntries(Object.entries(options.i18n ?? {}).map(([language, text]) => [`i18n/${language}.json`, text])); - const contents = { ...(options.files ?? { "pages/home.qjsb": `HOME-${version}` }), ...strings }; + const contents = { ...(options.files ?? { "pages/home.jsb": `HOME-${version}` }), ...strings }; const files = Object.fromEntries(Object.entries(contents).map(([p, text]) => [p, new TextEncoder().encode(text)])); const modules: Record = { [`${name}/home`]: "pages/home" }; const hashes: Record = {}; - for (const [module, path] of Object.entries(modules)) hashes[module] = await sha256Hex(files[`${path}.qjsb`]!); + for (const [module, path] of Object.entries(modules)) hashes[module] = await sha256Hex(files[`${path}.jsb`]!); for (const path of Object.keys(strings)) hashes[path] = await sha256Hex(files[path]!); const manifest: Record = { pages: [`${name}/home`], diff --git a/test/publish.test.ts b/test/publish.test.ts index 714bcfa..26e382d 100644 --- a/test/publish.test.ts +++ b/test/publish.test.ts @@ -22,10 +22,10 @@ describe("publishing and delivery", () => { expect(manifest.status).toBe(200); expect(manifest.headers.get("cache-control")).toBe("public, max-age=31536000, immutable"); expect(new Uint8Array(await manifest.arrayBuffer())).toEqual(p.manifest); - const home = await request(`/${app}/production/${pkg}/1/${p.version}/pages/home.qjsb`); + const home = await request(`/${app}/production/${pkg}/1/${p.version}/pages/home.jsb`); expect(new TextDecoder().decode(await home.arrayBuffer())).toBe(`HOME-${p.version}`); // the channel is a pointer, not a place: another channel reads the same bytes even before it points there - expect((await request(`/${app}/staging/${pkg}/1/${p.version}/pages/home.qjsb`)).status).toBe(200); + expect((await request(`/${app}/staging/${pkg}/1/${p.version}/pages/home.jsb`)).status).toBe(200); expect((await request(`/${app}/staging/${pkg}/1/current.json`)).status).toBe(404); }); @@ -39,9 +39,9 @@ describe("publishing and delivery", () => { await request(`/${app}/${pkg}/1/${p.version}/manifest.json`, { method: "PUT", token, body: p.manifest as BodyInit }); const missing = await publishPointer(app, "production", pkg, "1", p, token); expect(missing.status).toBe(409); - expect(await missing.text()).toContain("pages/home.qjsb is not uploaded yet"); + expect(await missing.text()).toContain("pages/home.jsb is not uploaded yet"); - const tampered = await signedPackage({ files: { "pages/home.qjsb": "HOME-x" }, key: { publicKey: p.publicKey, privateKey: p.privateKey } }); + const tampered = await signedPackage({ files: { "pages/home.jsb": "HOME-x" }, key: { publicKey: p.publicKey, privateKey: p.privateKey } }); for (const [path, bytes] of Object.entries(tampered.files)) { await request(`/${app}/${pkg}/1/${p.version}/${path}`, { method: "PUT", token, body: bytes as BodyInit }); } @@ -54,7 +54,7 @@ describe("publishing and delivery", () => { it("publishes a package with strings only once they are uploaded and match, and serves them", async () => { const p = await signedPackage({ i18n: { en: '{"hi":"Hi"}', zh: '{"hi":"你好"}' } }); const { app, pkg, token } = await setup({ publicKey: p.publicKey }); - await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: p.files["pages/home.qjsb"] as BodyInit }); + await request(`/${app}/${pkg}/1/${p.version}/pages/home.jsb`, { method: "PUT", token, body: p.files["pages/home.jsb"] as BodyInit }); await request(`/${app}/${pkg}/1/${p.version}/manifest.json`, { method: "PUT", token, body: p.manifest as BodyInit }); const missing = await publishPointer(app, "production", pkg, "1", p, token); expect(missing.status).toBe(409); @@ -88,14 +88,14 @@ describe("publishing and delivery", () => { it("keeps a version immutable: the same bytes again are fine, different ones are refused", async () => { const p = await signedPackage(); const { app, pkg, token } = await setup({ publicKey: p.publicKey }); - const first = await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: p.files["pages/home.qjsb"] as BodyInit }); + const first = await request(`/${app}/${pkg}/1/${p.version}/pages/home.jsb`, { method: "PUT", token, body: p.files["pages/home.jsb"] as BodyInit }); expect(first.status).toBe(201); - const again = await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: p.files["pages/home.qjsb"] as BodyInit }); + const again = await request(`/${app}/${pkg}/1/${p.version}/pages/home.jsb`, { method: "PUT", token, body: p.files["pages/home.jsb"] as BodyInit }); expect(again.status).toBe(200); expect(await again.json()).toMatchObject({ existing: true }); - const other = await request(`/${app}/${pkg}/1/${p.version}/pages/home.qjsb`, { method: "PUT", token, body: "something else" }); + const other = await request(`/${app}/${pkg}/1/${p.version}/pages/home.jsb`, { method: "PUT", token, body: "something else" }); expect(other.status).toBe(409); - const tooBig = await request(`/${app}/${pkg}/1/${p.version}/pages/big.qjsb`, { method: "PUT", token, body: new Uint8Array(1048577) as BodyInit }); + const tooBig = await request(`/${app}/${pkg}/1/${p.version}/pages/big.jsb`, { method: "PUT", token, body: new Uint8Array(1048577) as BodyInit }); expect(tooBig.status).toBe(413); }); @@ -246,7 +246,7 @@ describe("publishing and delivery", () => { "", ].join("\n"); const { app, pkg, token } = await setup({ publicKey }); - for (const [path, text] of Object.entries({ "pages/home.qjsb": "HOME2" })) { + for (const [path, text] of Object.entries({ "pages/home.jsb": "HOME2" })) { expect((await request(`/${app}/${pkg}/1/${version}/${path}`, { method: "PUT", token, body: text })).status).toBe(201); } expect((await request(`/${app}/${pkg}/1/${version}/manifest.json`, { method: "PUT", token, body: manifest })).status).toBe(201); @@ -263,7 +263,7 @@ describe("publishing and delivery", () => { const { app, pkg, token } = await setup({ publicKey: v1.publicKey }); // two different bodies race for one path: exactly one wins, the other is told so - const race = await Promise.all(["one", "two"].map((body) => request(`/${app}/${pkg}/1/v1/pages/race.qjsb`, { method: "PUT", token, body }))); + const race = await Promise.all(["one", "two"].map((body) => request(`/${app}/${pkg}/1/v1/pages/race.jsb`, { method: "PUT", token, body }))); expect(race.map((r) => r.status).sort()).toEqual([201, 409]); await uploadContent(app, pkg, "1", v1, token); @@ -280,7 +280,7 @@ describe("publishing and delivery", () => { const p = await signedPackage(); const { app, pkg, token } = await setup({ publicKey: p.publicKey }); expect((await request(`/${app}/${pkg}/1/../manifest.json`, { method: "PUT", token, body: "x" })).status).not.toBe(201); - expect((await request(`/${app}/${pkg}/1/v1/..%2Fescape.qjsb`, { method: "PUT", token, body: "x" })).status).toBe(404); + expect((await request(`/${app}/${pkg}/1/v1/..%2Fescape.jsb`, { method: "PUT", token, body: "x" })).status).toBe(404); expect((await request(`/${app}/Prod/${pkg}/1/current.json`)).status).toBe(404); // `apps` is reserved: never served as an app expect((await request(`/apps/production/${pkg}/1/current.json`)).status).toBe(404);