@teamscala/service-boot 0.23.540 → 0.23.541

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
@@ -103,6 +103,8 @@
103
103
  "./auth/sign-out.ts": "./src/auth/sign-out.ts",
104
104
  "./bind-page-handler": "./src/bind-page-handler.ts",
105
105
  "./bind-page-handler.ts": "./src/bind-page-handler.ts",
106
+ "./boot-shell": "./src/boot-shell.ts",
107
+ "./boot-shell.ts": "./src/boot-shell.ts",
106
108
  "./build-client-assets": "./src/build-client-assets.ts",
107
109
  "./build-client-assets.ts": "./src/build-client-assets.ts",
108
110
  "./build-client-assets/build-lock": "./src/build-client-assets/build-lock.ts",
@@ -305,5 +307,5 @@
305
307
  "repository": "https://github.com/teamgbg/service-boot",
306
308
  "sideEffects": false,
307
309
  "type": "module",
308
- "version": "0.23.540"
310
+ "version": "0.23.541"
309
311
  }
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * @status generated — ts-api-smoke template (scala-test-suites
3
- * templates/ts_api_smoke.pkl @1.3.0) over the exports map of @teamscala/service-boot.
4
- * ./apply-logger-config declares 1 runtime export(s) and 3 declared behaviour vector(s);
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
+ * ./apply-logger-config declares 1 runtime export(s) and 4 declared behaviour vector(s);
5
5
  * this suite asserts the import resolves, the runtime surface matches
6
6
  * exactly, and every export carries its declared kind. Regenerate from
7
- * inputs/server-service-boot-ts/api_smoke.json (scala-test-suites) — hand edits are
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
8
  * overwritten.
9
9
  */
10
10
 
11
11
  import { describe, expect, it } from "bun:test";
12
- import { mock } from "bun:test";
12
+ const { mock } = await import("bun:test");
13
13
  const __setMinLevel: string[] = [];
14
14
  const __fileSinks: Record<string, unknown>[] = [];
15
15
  mock.module("@teamscala/logger/creator", () => ({ setMinLevel: (level: string) => { __setMinLevel.push(level); } }));
@@ -30,17 +30,22 @@ describe("apply-logger-config exports-map smoke", () => {
30
30
  });
31
31
  it("a declared min level reaches the logger creator", async () => {
32
32
  __setMinLevel.length = 0;
33
- applyLoggerConfig({ minLevel: "debug" });
33
+ applyLoggerConfig({ minLevel: "debug" })
34
34
  expect(__setMinLevel).toEqual(["debug"]);
35
35
  });
36
36
  it("an undeclared min level leaves the creator untouched", async () => {
37
37
  __setMinLevel.length = 0;
38
- applyLoggerConfig({});
38
+ applyLoggerConfig({})
39
39
  expect(__setMinLevel).toEqual([]);
40
40
  });
41
41
  it("the file sink is configured from the boot row's own values", async () => {
42
42
  __fileSinks.length = 0;
43
- applyLoggerConfig({ logDir: "/var/log/scala", maxFileSize: 10, maxFiles: 3 });
44
- expect(__fileSinks).toEqual([{ logDir: "/var/log/scala", maxFileSize: 10, maxFiles: 3 }]);
43
+ applyLoggerConfig({ logDir: "/var/log/scala", maxFileSize: 10, maxFiles: 3 })
44
+ expect(__fileSinks).toEqual([{"logDir": "/var/log/scala", "maxFileSize": 10, "maxFiles": 3}]);
45
+ });
46
+ it("a row logDir carrying a host placeholder is resolved before it reaches the file sink", async () => {
47
+ __fileSinks.length = 0;
48
+ applyLoggerConfig({ logDir: "${HOME}/.local/share/oxmgr/logs" })
49
+ expect(__fileSinks).toEqual([{ logDir: `${process.env.HOME}/.local/share/oxmgr/logs`, maxFileSize: undefined, maxFiles: undefined }]);
45
50
  });
46
51
  });
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @status generated — ts-api-smoke template (scala-test-suites
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
+ * ./boot-shell declares 1 runtime export(s) and 3 declared behaviour vector(s);
5
+ * this suite asserts the import resolves, the runtime surface matches
6
+ * exactly, and every export carries its declared kind. Regenerate from
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
+ * overwritten.
9
+ */
10
+
11
+ import { describe, expect, it } from "bun:test";
12
+ const { Hono } = await import("hono");
13
+ const { swapApp } = await import("./mutable-app-ref");
14
+ const { __resetReadinessGateForTest, setReady } = await import("./readiness-gate");
15
+ const { __resetPhase2StateForTest, markFullAppDegraded } = await import("./two-phase-readiness");
16
+
17
+ const mod = await import("./boot-shell");
18
+
19
+ const { createBootShellApp } = mod;
20
+
21
+ describe("boot-shell exports-map smoke", () => {
22
+ it("the runtime export set is exactly the declared surface", () => {
23
+ expect(Object.keys(mod).sort()).toEqual([
24
+ "createBootShellApp",
25
+ ]);
26
+ });
27
+ it("createBootShellApp is a function", () => {
28
+ expect(typeof mod["createBootShellApp"]).toBe("function");
29
+ });
30
+ it("answers 503 starting on every path while the mount chain runs", async () => {
31
+ __resetReadinessGateForTest();
32
+ __resetPhase2StateForTest();
33
+ const shell = createBootShellApp("boot-shell-test"); const health = await shell.request("/health"); const page = await shell.request("/any/unmounted/path")
34
+ expect(health.status).toBe(503);
35
+ expect(await health.json()).toEqual({ status: "starting" });
36
+ expect(page.status).toBe(503);
37
+ expect(await page.json()).toEqual({ status: "starting" });
38
+ });
39
+ it("delegates to the swapped-in minimal app once ready", async () => {
40
+ __resetReadinessGateForTest();
41
+ __resetPhase2StateForTest();
42
+ swapApp(new Hono().get("/delegated", () => new Response("mounted")), "minimal");
43
+ setReady();
44
+ const res = await createBootShellApp("boot-shell-test").request("/delegated")
45
+ expect(res.status).toBe(200);
46
+ expect(await res.text()).toBe("mounted");
47
+ });
48
+ it("health reports degraded once the boot finished with a failed client build", async () => {
49
+ __resetReadinessGateForTest();
50
+ __resetPhase2StateForTest();
51
+ setReady();
52
+ markFullAppDegraded("client build is not servable: boot-shell-test");
53
+ const res = await createBootShellApp("boot-shell-test").request("/health")
54
+ expect(res.status).toBe(200);
55
+ expect(await res.json()).toEqual({ status: "degraded", reason: "client build is not servable: boot-shell-test" });
56
+ });
57
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @system service-boot
3
+ * @status handwritten
4
+ */
5
+
6
+ import type { Hono } from "hono";
7
+ import { Hono } from "hono";
8
+ import { createRequestErrorHandler } from "./hono-error-handler.ts";
9
+ import { getApp } from "./mutable-app-ref.ts";
10
+ import { readinessGate } from "./readiness-gate.ts";
11
+ import { healthHandler } from "./two-phase-readiness.ts";
12
+
13
+ export function createBootShellApp(serviceSlug: string): Hono {
14
+ const app = new Hono();
15
+ app.onError(createRequestErrorHandler(`service-boot:boot-shell:${serviceSlug}`));
16
+ app.use("*", readinessGate);
17
+ app.get("/health", healthHandler);
18
+ app.all("*", (c) => getApp().fetch(c.req.raw, c.env));
19
+ return app;
20
+ }
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @status generated — ts-api-smoke template (scala-test-suites
3
- * templates/ts_api_smoke.pkl @1.3.0) over the exports map of @teamscala/service-boot.
4
- * ./error-page-logo declares 2 runtime export(s) and 6 declared behaviour vector(s);
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
+ * ./error-page-logo declares 2 runtime export(s) and 10 declared behaviour vector(s);
5
5
  * this suite asserts the import resolves, the runtime surface matches
6
6
  * exactly, and every export carries its declared kind. Regenerate from
7
- * inputs/server-service-boot-ts/api_smoke.json (scala-test-suites) — hand edits are
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
8
  * overwritten.
9
9
  */
10
10
 
@@ -29,36 +29,56 @@ describe("error-page-logo exports-map smoke", () => {
29
29
  it("inlineLogoAsDataUri is a function", () => {
30
30
  expect(typeof mod["inlineLogoAsDataUri"]).toBe("function");
31
31
  });
32
- it("inlines a stored logo as a data URI, fetching the bucket key after the /api/assets/ prefix", async () => {
32
+ it("inlines a stored logo as a data URI, fetching the bucket key after the /api/assets/ prefix [toBeTrue]", async () => {
33
33
  __requested.length = 0;
34
34
  __storageReached = false;
35
- const got = await inlineLogoAsDataUri("/api/assets/c503629a/logos/cropped.png", async () => ({ file(key) { __storageReached = true; __requested.push(key); return { stat: async () => ({}), arrayBuffer: async () => new Uint8Array([137, 80, 78, 71]).buffer }; } }));
36
- expect(__storageReached).toBeTrue();
35
+ const got = await inlineLogoAsDataUri("/api/assets/c503629a/logos/cropped.png", async () => ({ file(key) { __storageReached = true; __requested.push(key); return { stat: async () => ({}), arrayBuffer: async () => new Uint8Array([137, 80, 78, 71]).buffer }; } }))
36
+ expect(__storageReached).toEqual(true);
37
+ });
38
+ it("inlines a stored logo as a data URI, fetching the bucket key after the /api/assets/ prefix [toEqual]", async () => {
39
+ __requested.length = 0;
40
+ __storageReached = false;
41
+ const got = await inlineLogoAsDataUri("/api/assets/c503629a/logos/cropped.png", async () => ({ file(key) { __storageReached = true; __requested.push(key); return { stat: async () => ({}), arrayBuffer: async () => new Uint8Array([137, 80, 78, 71]).buffer }; } }))
37
42
  expect(__requested).toEqual(["c503629a/logos/cropped.png"]);
38
- expect(got).toBe("data:image/png;base64,iVBORw==");
39
43
  });
40
- it("a stored value without the /api/assets/ prefix is refused before storage is touched", async () => {
44
+ it("inlines a stored logo as a data URI, fetching the bucket key after the /api/assets/ prefix [toBe]", async () => {
45
+ __requested.length = 0;
46
+ __storageReached = false;
47
+ const got = await inlineLogoAsDataUri("/api/assets/c503629a/logos/cropped.png", async () => ({ file(key) { __storageReached = true; __requested.push(key); return { stat: async () => ({}), arrayBuffer: async () => new Uint8Array([137, 80, 78, 71]).buffer }; } }))
48
+ expect(got).toEqual("data:image/png;base64,iVBORw==");
49
+ });
50
+ it("a stored value without the /api/assets/ prefix is refused before storage is touched [toBeFalse]", async () => {
41
51
  __requested.length = 0;
42
52
  __storageReached = false;
43
- const got = await inlineLogoAsDataUri("https://cdn.example.com/logo.png", async () => { __storageReached = true; return null; });
44
- expect(__storageReached).toBeFalse();
53
+ const got = await inlineLogoAsDataUri("https://cdn.example.com/logo.png", async () => { __storageReached = true; return null; })
54
+ expect(__storageReached).toEqual(false);
55
+ });
56
+ it("a stored value without the /api/assets/ prefix is refused before storage is touched [toEqual]", async () => {
57
+ __requested.length = 0;
58
+ __storageReached = false;
59
+ const got = await inlineLogoAsDataUri("https://cdn.example.com/logo.png", async () => { __storageReached = true; return null; })
45
60
  expect(__requested).toEqual([]);
61
+ });
62
+ it("a stored value without the /api/assets/ prefix is refused before storage is touched [toBeNull]", async () => {
63
+ __requested.length = 0;
64
+ __storageReached = false;
65
+ const got = await inlineLogoAsDataUri("https://cdn.example.com/logo.png", async () => { __storageReached = true; return null; })
46
66
  expect(got).toBeNull();
47
67
  });
48
68
  it("an unavailable storage client returns null", async () => {
49
- const got = await inlineLogoAsDataUri("/api/assets/org/logos/cropped.png", async () => null);
69
+ const got = await inlineLogoAsDataUri("/api/assets/org/logos/cropped.png", async () => null)
50
70
  expect(got).toBeNull();
51
71
  });
52
72
  it("an oversized logo falls back to null instead of bloating the HTML", async () => {
53
- const got = await inlineLogoAsDataUri("/api/assets/org/logos/huge.png", async () => ({ file() { return { stat: async () => ({}), arrayBuffer: async () => new ArrayBuffer(100 * 1024 + 1) }; } }));
73
+ const got = await inlineLogoAsDataUri("/api/assets/org/logos/huge.png", async () => ({ file() { return { stat: async () => ({}), arrayBuffer: async () => new ArrayBuffer(100 * 1024 + 1) }; } }))
54
74
  expect(got).toBeNull();
55
75
  });
56
76
  it("an empty logo file falls back to null", async () => {
57
- const got = await inlineLogoAsDataUri("/api/assets/org/logos/empty.png", async () => ({ file() { return { stat: async () => ({}), arrayBuffer: async () => new ArrayBuffer(0) }; } }));
77
+ const got = await inlineLogoAsDataUri("/api/assets/org/logos/empty.png", async () => ({ file() { return { stat: async () => ({}), arrayBuffer: async () => new ArrayBuffer(0) }; } }))
58
78
  expect(got).toBeNull();
59
79
  });
60
80
  it("the stored mime survives inlining — a .jpg key inlines as image/jpeg", async () => {
61
- const got = await inlineLogoAsDataUri("/api/assets/org/logos/wordmark.jpg", async () => ({ file() { return { stat: async () => ({}), arrayBuffer: async () => new Uint8Array([255, 216, 255]).buffer }; } }));
62
- expect(got).toBe("data:image/jpeg;base64,/9j/");
81
+ const got = await inlineLogoAsDataUri("/api/assets/org/logos/wordmark.jpg", async () => ({ file() { return { stat: async () => ({}), arrayBuffer: async () => new Uint8Array([255, 216, 255]).buffer }; } }))
82
+ expect(got).toEqual("data:image/jpeg;base64,/9j/");
63
83
  });
64
84
  });
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @status generated — ts_function_contract_runner template (scala-test-suites
3
- * templates/ts_function_contract_runner.pkl @1.16.0) over @teamscala/service-boot's
3
+ * templates/ts_function_contract_runner.pkl @1.16.1) over @teamscala/service-boot's
4
4
  * test_vector rows: one `it` per function_call row (args -> value or
5
5
  * expect_error) and one per fs_flow row (ordered steps in a temp
6
6
  * workspace). This runner carries NO facts: every case is the registry
@@ -8,6 +8,7 @@
8
8
  * test-vector-rows projection row (scala-tools codegen run --repo
9
9
  * <consumer>); hand edits are overwritten.
10
10
  */
11
+
11
12
  function arithmetic(source: string, value: unknown, slug: string): number {
12
13
  let pos = 0;
13
14
  const text = source;
@@ -955,13 +956,13 @@ function assertRenderSpec(slug: string, container: HTMLElement, spec: Record<str
955
956
  }
956
957
 
957
958
  async function renderRow(row: Vector): Promise<void> {
959
+ if (!globalThis.document) {
960
+ const { GlobalRegistrator } = await import("@happy-dom/global-registrator");
961
+ await GlobalRegistrator.register({ url: "http://localhost" });
962
+ }
958
963
  const restore = applyEnv(row.env);
959
964
  try {
960
965
  await withStubs(row, async () => {
961
- if (!globalThis.document) {
962
- const { GlobalRegistrator } = await import("@happy-dom/global-registrator");
963
- await GlobalRegistrator.register({ url: "http://localhost" });
964
- }
965
966
  (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
966
967
  const React = await import("react");
967
968
  const { createRoot } = await import("react-dom/client");
@@ -1010,13 +1011,13 @@ async function renderRow(row: Vector): Promise<void> {
1010
1011
  }
1011
1012
  }
1012
1013
  async function hookRow(row: Vector): Promise<void> {
1014
+ if (!globalThis.document) {
1015
+ const { GlobalRegistrator } = await import("@happy-dom/global-registrator");
1016
+ await GlobalRegistrator.register({ url: "http://localhost" });
1017
+ }
1013
1018
  const restore = applyEnv(row.env);
1014
1019
  try {
1015
1020
  await withStubs(row, async () => {
1016
- if (!globalThis.document) {
1017
- const { GlobalRegistrator } = await import("@happy-dom/global-registrator");
1018
- await GlobalRegistrator.register({ url: "http://localhost" });
1019
- }
1020
1021
  (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
1021
1022
  const React = await import("react");
1022
1023
  const { createRoot } = await import("react-dom/client");
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @status generated — ts-api-smoke template (scala-test-suites
3
- * templates/ts_api_smoke.pkl @1.3.0) over the exports map of @teamscala/service-boot.
4
- * ./page-server-loader-graph declares 1 runtime export(s) and 1 declared behaviour vector(s);
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
+ * ./page-server-loader-graph declares 1 runtime export(s) and 2 declared behaviour vector(s);
5
5
  * this suite asserts the import resolves, the runtime surface matches
6
6
  * exactly, and every export carries its declared kind. Regenerate from
7
- * inputs/server-service-boot-ts/api_smoke.json (scala-test-suites) — hand edits are
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
8
  * overwritten.
9
9
  */
10
10
 
@@ -23,9 +23,13 @@ describe("page-server-loader-graph exports-map smoke", () => {
23
23
  it("resolveFromLoaderConfig is a function", () => {
24
24
  expect(typeof mod["resolveFromLoaderConfig"]).toBe("function");
25
25
  });
26
- it("the SSR binding is a resolver PRODUCT of the engine factory applied to the page-server client — never the factory itself", async () => {
27
- const { createLoaderConfigResolver } = await import("@teamscala/loader-config/loader-config/resolve-from-loader-config");
28
- expect(typeof createLoaderConfigResolver).toBe("function");
26
+ it("the SSR binding is a resolver PRODUCT of the engine factory applied to the page-server client — never the factory itself [toBe]", async () => {
27
+ const { createLoaderConfigResolver } = await import("@teamscala/loader-config/loader-config/resolve-from-loader-config")
28
+ const got = (typeof createLoaderConfigResolver);
29
+ expect(got).toEqual("function");
30
+ });
31
+ it("the SSR binding is a resolver PRODUCT of the engine factory applied to the page-server client — never the factory itself [toBe]", async () => {
32
+ const { createLoaderConfigResolver } = await import("@teamscala/loader-config/loader-config/resolve-from-loader-config")
29
33
  expect(resolveFromLoaderConfig).not.toBe(createLoaderConfigResolver);
30
34
  });
31
35
  });
@@ -12,6 +12,10 @@ export function setReady(): void {
12
12
  _ready = true;
13
13
  }
14
14
 
15
+ export function __resetReadinessGateForTest(): void {
16
+ _ready = false;
17
+ }
18
+
15
19
  export async function readinessGate(c: Context, next: Next): Promise<Response | void> {
16
20
  try {
17
21
  if (_ready) return next();
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @status generated — ts-api-smoke template (scala-test-suites
3
- * templates/ts_api_smoke.pkl @1.3.0) over the exports map of @teamscala/service-boot.
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
4
  * ./meta-title declares 3 runtime export(s) and 4 declared behaviour vector(s);
5
5
  * this suite asserts the import resolves, the runtime surface matches
6
6
  * exactly, and every export carries its declared kind. Regenerate from
7
- * inputs/server-service-boot-ts/api_smoke.json (scala-test-suites) — hand edits are
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
8
  * overwritten.
9
9
  */
10
10
 
@@ -32,19 +32,19 @@ describe("meta-title exports-map smoke", () => {
32
32
  expect(typeof mod["resolveEntityTitle"]).toBe("function");
33
33
  });
34
34
  it("an absent meta context resolves no meta at all", async () => {
35
- const got = buildMetaFromContext(undefined, { page: { name: "Quiz" } });
35
+ const got = buildMetaFromContext(undefined, { page: { name: "Quiz" } })
36
36
  expect(got).toEqual({});
37
37
  });
38
38
  it("the page's own meta_title wins, from loaderData first", async () => {
39
- const got = resolvePageMetaTitle({ loaderData: { page: { meta_title: "Sign in" } }, page: { meta_title: "Landing" } });
40
- expect(got).toBe("Sign in");
39
+ const got = resolvePageMetaTitle({ loaderData: { page: { meta_title: "Sign in" } }, page: { meta_title: "Landing" } })
40
+ expect(got).toEqual("Sign in");
41
41
  });
42
42
  it("with no loaderData page, the resolved page's meta_title answers", async () => {
43
- const got = resolvePageMetaTitle({ loaderData: {}, page: { meta_title: "Landing" } });
44
- expect(got).toBe("Landing");
43
+ const got = resolvePageMetaTitle({ loaderData: {}, page: { meta_title: "Landing" } })
44
+ expect(got).toEqual("Landing");
45
45
  });
46
46
  it("neither page carries a title, so no title resolves", async () => {
47
- const got = resolvePageMetaTitle({ loaderData: {} });
47
+ const got = resolvePageMetaTitle({ loaderData: {} })
48
48
  expect(got).toBeUndefined();
49
49
  });
50
50
  });
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * @status generated — ts-api-smoke template (scala-test-suites
3
- * templates/ts_api_smoke.pkl @1.3.0) over the exports map of @teamscala/service-boot.
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
4
  * ./resolve-brand-favicon declares 2 runtime export(s) and 5 declared behaviour vector(s);
5
5
  * this suite asserts the import resolves, the runtime surface matches
6
6
  * exactly, and every export carries its declared kind. Regenerate from
7
- * inputs/server-service-boot-ts/api_smoke.json (scala-test-suites) — hand edits are
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
8
  * overwritten.
9
9
  */
10
10
 
11
11
  import { describe, expect, it } from "bun:test";
12
- import { mock } from "bun:test";
12
+ const { mock } = await import("bun:test");
13
13
  const __realAssetProxy = await import("./asset-proxy");
14
14
  let __probeCalls = 0;
15
15
  let __probeBehaviour: "ok" | "notok" | "throw" = "ok";
@@ -34,30 +34,32 @@ describe("resolve-brand-favicon exports-map smoke", () => {
34
34
  });
35
35
  it("no brand org resolves null without probing the asset store", async () => {
36
36
  __probeCalls = 0;
37
- const got = await resolveBrandFaviconPath(undefined);
37
+ const got = await resolveBrandFaviconPath(undefined)
38
38
  expect(got).toBeNull();
39
39
  expect(__probeCalls).toBe(0);
40
40
  });
41
- it("a reachable favicon resolves the encoded convention path and caches it", async () => {
41
+ it("a reachable favicon resolves the encoded convention path and probes the asset store once", async () => {
42
42
  __probeBehaviour = "ok"; __probeCalls = 0;
43
- const got = await resolveBrandFaviconPath("brand-org-probe-1");
43
+ const got = await resolveBrandFaviconPath("brand-org-probe-1")
44
44
  expect(got).toBe("/api/assets/brand-org-probe-1%2Flogos%2Ffavicon.png");
45
45
  expect(__probeCalls).toBe(1);
46
46
  });
47
- it("the second resolution for the same org serves from cache", async () => {
47
+ it("the second resolution for the same org serves from cache without probing again", async () => {
48
+ __probeBehaviour = "ok"; __probeCalls = 0;
49
+ await resolveBrandFaviconPath("brand-org-probe-4");
48
50
  __probeCalls = 0;
49
- const got = await resolveBrandFaviconPath("brand-org-probe-1");
50
- expect(got).toBe("/api/assets/brand-org-probe-1%2Flogos%2Ffavicon.png");
51
+ const got = await resolveBrandFaviconPath("brand-org-probe-4")
52
+ expect(got).toBe("/api/assets/brand-org-probe-4%2Flogos%2Ffavicon.png");
51
53
  expect(__probeCalls).toBe(0);
52
54
  });
53
55
  it("a missing favicon at the convention key resolves null", async () => {
54
56
  __probeBehaviour = "notok"; __probeCalls = 0;
55
- const got = await resolveBrandFaviconPath("brand-org-probe-2");
57
+ const got = await resolveBrandFaviconPath("brand-org-probe-2")
56
58
  expect(got).toBeNull();
57
59
  });
58
60
  it("an asset-store failure degrades to the generic fallback (null), never a throw", async () => {
59
61
  __probeBehaviour = "throw"; __probeCalls = 0;
60
- const got = await resolveBrandFaviconPath("brand-org-probe-3");
62
+ const got = await resolveBrandFaviconPath("brand-org-probe-3")
61
63
  expect(got).toBeNull();
62
64
  });
63
65
  });
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @status generated — ts-api-smoke template (scala-test-suites
3
- * templates/ts_api_smoke.pkl @1.3.0) over the exports map of @teamscala/service-boot.
4
- * ./resolve-theme-mode declares 1 runtime export(s) + 1 type(s) and 5 declared behaviour vector(s);
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
+ * ./resolve-theme-mode declares 1 runtime export(s) + 1 type(s) and 10 declared behaviour vector(s);
5
5
  * this suite asserts the import resolves, the runtime surface matches
6
6
  * exactly, and every export carries its declared kind. Regenerate from
7
- * inputs/server-service-boot-ts/api_smoke.json (scala-test-suites) — hand edits are
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
8
  * overwritten.
9
9
  */
10
10
 
@@ -23,29 +23,54 @@ describe("resolve-theme-mode exports-map smoke", () => {
23
23
  it("resolveThemeMode is a function", () => {
24
24
  expect(typeof mod["resolveThemeMode"]).toBe("function");
25
25
  });
26
- it("the ?theme= URL override wins and marks the render embedded", async () => {
27
- const got = resolveThemeMode({ url: "https://x.local/p?theme=dark" });
28
- expect(got.theme).toBe("dark");
29
- expect(got.embedded).toBe(true);
30
- });
31
- it("an explicit override behaves exactly like the URL form", async () => {
32
- const got = resolveThemeMode({ override: "dark" });
33
- expect(got.theme).toBe("dark");
34
- expect(got.embedded).toBe(true);
35
- });
36
- it("a non-light/dark override is not an override", async () => {
37
- const got = resolveThemeMode({ override: "purple", candidates: ["dark"] });
38
- expect(got.theme).toBe("dark");
39
- expect(got.embedded).toBe(false);
40
- });
41
- it("candidates are consulted in priority order", async () => {
42
- const got = resolveThemeMode({ candidates: [null, "light", "dark"] });
43
- expect(got.theme).toBe("light");
44
- expect(got.embedded).toBe(false);
45
- });
46
- it("nothing resolvable lands on light, never a crash", async () => {
47
- const got = resolveThemeMode({});
48
- expect(got.theme).toBe("light");
49
- expect(got.embedded).toBe(false);
26
+ it("the ?theme= URL override wins and marks the render embedded [toBe]", async () => {
27
+ const got = resolveThemeMode({ url: "https://x.local/p?theme=dark" })
28
+ const got2 = (got.theme);
29
+ expect(got2).toEqual("dark");
30
+ });
31
+ it("the ?theme= URL override wins and marks the render embedded [toBe]", async () => {
32
+ const got = resolveThemeMode({ url: "https://x.local/p?theme=dark" })
33
+ const got2 = (got.embedded);
34
+ expect(got2).toEqual(true);
35
+ });
36
+ it("an explicit override behaves exactly like the URL form [toBe]", async () => {
37
+ const got = resolveThemeMode({ override: "dark" })
38
+ const got2 = (got.theme);
39
+ expect(got2).toEqual("dark");
40
+ });
41
+ it("an explicit override behaves exactly like the URL form [toBe]", async () => {
42
+ const got = resolveThemeMode({ override: "dark" })
43
+ const got2 = (got.embedded);
44
+ expect(got2).toEqual(true);
45
+ });
46
+ it("a non-light/dark override is not an override [toBe]", async () => {
47
+ const got = resolveThemeMode({ override: "purple", candidates: ["dark"] })
48
+ const got2 = (got.theme);
49
+ expect(got2).toEqual("dark");
50
+ });
51
+ it("a non-light/dark override is not an override [toBe]", async () => {
52
+ const got = resolveThemeMode({ override: "purple", candidates: ["dark"] })
53
+ const got2 = (got.embedded);
54
+ expect(got2).toEqual(false);
55
+ });
56
+ it("candidates are consulted in priority order [toBe]", async () => {
57
+ const got = resolveThemeMode({ candidates: [null, "light", "dark"] })
58
+ const got2 = (got.theme);
59
+ expect(got2).toEqual("light");
60
+ });
61
+ it("candidates are consulted in priority order [toBe]", async () => {
62
+ const got = resolveThemeMode({ candidates: [null, "light", "dark"] })
63
+ const got2 = (got.embedded);
64
+ expect(got2).toEqual(false);
65
+ });
66
+ it("nothing resolvable lands on light, never a crash [toBe]", async () => {
67
+ const got = resolveThemeMode({})
68
+ const got2 = (got.theme);
69
+ expect(got2).toEqual("light");
70
+ });
71
+ it("nothing resolvable lands on light, never a crash [toBe]", async () => {
72
+ const got = resolveThemeMode({})
73
+ const got2 = (got.embedded);
74
+ expect(got2).toEqual(false);
50
75
  });
51
76
  });
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @status generated — ts-api-smoke template (scala-test-suites
3
- * templates/ts_api_smoke.pkl @1.3.0) over the exports map of @teamscala/service-boot.
4
- * ./fetch-org-brand declares 1 runtime export(s) and 4 declared behaviour vector(s);
3
+ * templates/ts_api_smoke.pkl @1.5.0) over the exports map of @teamscala/service-boot.
4
+ * ./fetch-org-brand declares 1 runtime export(s) and 6 declared behaviour vector(s);
5
5
  * this suite asserts the import resolves, the runtime surface matches
6
6
  * exactly, and every export carries its declared kind. Regenerate from
7
- * inputs/server-service-boot-ts/api_smoke.json (scala-test-suites) — hand edits are
7
+ * inputs/bun-server-service-boot/api_smoke.json (scala-test-suites) — hand edits are
8
8
  * overwritten.
9
9
  */
10
10
 
@@ -23,26 +23,37 @@ describe("fetch-org-brand (page-org-first wrapper) exports-map smoke", () => {
23
23
  it("fetchOrgBrandWithFallback is a function", () => {
24
24
  expect(typeof mod["fetchOrgBrandWithFallback"]).toBe("function");
25
25
  });
26
- it("a page org with a brand row renders THAT brand, no fallback consulted", async () => {
26
+ it("a page org with a brand row renders THAT brand, no fallback consulted [toBeNull]", async () => {
27
27
  const client = { organisation_profile: { findFirst: async ({ select, where }) => (where.id === "org-branded" ? { company_name: "Launcha", brand_primary_color: "#123456" } : null) } };
28
- const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-branded" }, client, params: {} } as never);
28
+ const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-branded" }, client, params: {} } as never)
29
29
  expect(got).not.toBeNull();
30
- expect((got as { companyName: string }).companyName).toBe("Launcha");
30
+ expect(got).not.toBeUndefined();
31
+ });
32
+ it("a page org with a brand row renders THAT brand, no fallback consulted [toBe]", async () => {
33
+ const client = { organisation_profile: { findFirst: async ({ select, where }) => (where.id === "org-branded" ? { company_name: "Launcha", brand_primary_color: "#123456" } : null) } };
34
+ const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-branded" }, client, params: {} } as never)
35
+ const got2 = ((got as { companyName: string }).companyName);
36
+ expect(got2).toEqual("Launcha");
31
37
  });
32
38
  it("a page org with no brand falls back to the website owning-org", async () => {
33
39
  const client = { organisation_profile: { findFirst: async ({ where }) => (where.id === "org-fallback" ? { company_name: "Website Owner" } : null) } };
34
- const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-page", fallbackOrgId: "org-fallback" }, client, params: {} } as never);
35
- expect((got as { companyName: string }).companyName).toBe("Website Owner");
40
+ const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-page", fallbackOrgId: "org-fallback" }, client, params: {} } as never)
41
+ const got2 = ((got as { companyName: string }).companyName);
42
+ expect(got2).toEqual("Website Owner");
36
43
  });
37
- it("a fallback identical to the page org is not consulted twice", async () => {
44
+ it("a fallback identical to the page org is not consulted twice [toBeNull]", async () => {
38
45
  let calls = 0; const client = { organisation_profile: { findFirst: async () => { calls++; return null; } } };
39
- const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-x", fallbackOrgId: "org-x" }, client, params: {} } as never);
46
+ const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-x", fallbackOrgId: "org-x" }, client, params: {} } as never)
40
47
  expect(got).toBeNull();
41
- expect(calls).toBe(2);
48
+ });
49
+ it("a fallback identical to the page org is not consulted twice [toBe]", async () => {
50
+ let calls = 0; const client = { organisation_profile: { findFirst: async () => { calls++; return null; } } };
51
+ const got = await fetchOrgBrandWithFallback({ input: { orgId: "org-x", fallbackOrgId: "org-x" }, client, params: {} } as never)
52
+ expect(calls).toEqual(2);
42
53
  });
43
54
  it("no orgId anywhere resolves null and names it", async () => {
44
55
  const client = { organisation_profile: { findFirst: async () => { throw new Error("must not be called"); } } };
45
- const got = await fetchOrgBrandWithFallback({ input: {}, client, params: {} } as never);
56
+ const got = await fetchOrgBrandWithFallback({ input: {}, client, params: {} } as never)
46
57
  expect(got).toBeNull();
47
58
  });
48
59
  });