@cosmicdrift/kumiko-server-runtime 0.197.0 → 0.198.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": "@cosmicdrift/kumiko-server-runtime",
3
- "version": "0.197.0",
3
+ "version": "0.198.0",
4
4
  "description": "Production server-boot runtime for Kumiko apps: connections, schema-drift-gate, seeds, lifecycle, graceful shutdown. Symmetric to kumiko-dev-server's runDevApp, without dev/scaffold/codegen tooling.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -42,6 +42,10 @@
42
42
  "types": "./src/pii-boot-gate.ts",
43
43
  "default": "./src/pii-boot-gate.ts"
44
44
  },
45
+ "./session-boot-gate": {
46
+ "types": "./src/session-boot-gate.ts",
47
+ "default": "./src/session-boot-gate.ts"
48
+ },
45
49
  "./boot/apply-boot-seeds": {
46
50
  "types": "./src/boot/apply-boot-seeds.ts",
47
51
  "default": "./src/boot/apply-boot-seeds.ts"
@@ -76,8 +80,8 @@
76
80
  }
77
81
  },
78
82
  "dependencies": {
79
- "@cosmicdrift/kumiko-bundled-features": "0.197.0",
80
- "@cosmicdrift/kumiko-framework": "0.197.0",
83
+ "@cosmicdrift/kumiko-bundled-features": "0.198.0",
84
+ "@cosmicdrift/kumiko-framework": "0.198.0",
81
85
  "temporal-polyfill": "^0.3.2"
82
86
  },
83
87
  "publishConfig": {
@@ -1,22 +1,70 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, spyOn, test } from "bun:test";
2
2
  import { assertSessionBootInvariants } from "../session-boot-gate";
3
3
 
4
4
  describe("assertSessionBootInvariants (#1372)", () => {
5
5
  test("no auth → no throw", () => {
6
6
  expect(() =>
7
- assertSessionBootInvariants({ hasAuth: false, sessionStoreProviderMounted: false }),
7
+ assertSessionBootInvariants({
8
+ hasAuth: false,
9
+ sessionStoreProviderMounted: false,
10
+ mode: "prod",
11
+ }),
8
12
  ).not.toThrow();
9
13
  });
10
14
 
11
15
  test("auth + sessionStore → no throw", () => {
12
16
  expect(() =>
13
- assertSessionBootInvariants({ hasAuth: true, sessionStoreProviderMounted: true }),
17
+ assertSessionBootInvariants({
18
+ hasAuth: true,
19
+ sessionStoreProviderMounted: true,
20
+ mode: "prod",
21
+ }),
14
22
  ).not.toThrow();
15
23
  });
16
24
 
17
- test("auth without sessionStore → throws", () => {
25
+ test("prod: auth without sessionStore → BOOT ABORTED throw", () => {
18
26
  expect(() =>
19
- assertSessionBootInvariants({ hasAuth: true, sessionStoreProviderMounted: false }),
27
+ assertSessionBootInvariants({
28
+ hasAuth: true,
29
+ sessionStoreProviderMounted: false,
30
+ mode: "prod",
31
+ }),
20
32
  ).toThrow(/BOOT ABORTED/);
21
33
  });
22
34
  });
35
+
36
+ // dev/prod split (#2027) — dev-server had no equivalent gate at all before
37
+ // this: a forgotten sessions mount silently left session-list empty with no
38
+ // signal. Mirrors assertPiiBootInvariants' dev/prod split (warn vs abort).
39
+ describe("assertSessionBootInvariants — dev mode (#2027)", () => {
40
+ test("dev: auth without sessionStore → warns, does NOT throw", () => {
41
+ const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
42
+ try {
43
+ expect(() =>
44
+ assertSessionBootInvariants({
45
+ hasAuth: true,
46
+ sessionStoreProviderMounted: false,
47
+ mode: "dev",
48
+ }),
49
+ ).not.toThrow();
50
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("sessionStore"));
51
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("[runDevApp]"));
52
+ } finally {
53
+ warnSpy.mockRestore();
54
+ }
55
+ });
56
+
57
+ test("dev: auth + sessionStore → no throw, no warn", () => {
58
+ const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
59
+ try {
60
+ assertSessionBootInvariants({
61
+ hasAuth: true,
62
+ sessionStoreProviderMounted: true,
63
+ mode: "dev",
64
+ });
65
+ expect(warnSpy).not.toHaveBeenCalled();
66
+ } finally {
67
+ warnSpy.mockRestore();
68
+ }
69
+ });
70
+ });
@@ -799,6 +799,7 @@ export async function runProdApp(options: RunProdAppOptions): Promise<ProdAppHan
799
799
  assertSessionBootInvariants({
800
800
  hasAuth: Boolean(effectiveAuth),
801
801
  sessionStoreProviderMounted: registry.getExtensionUsages(EXT_SESSION_STORE).length > 0,
802
+ mode: "prod",
802
803
  });
803
804
 
804
805
  // C1 boot-mode exit: validators ran + registry built; no DB/Redis client
@@ -1,6 +1,12 @@
1
1
  export type SessionBootGateOptions = {
2
2
  readonly hasAuth: boolean;
3
3
  readonly sessionStoreProviderMounted: boolean;
4
+ /** prod fails hard on a missing sessionStore provider (stateless JWTs would
5
+ * silently skip server-side revocation and any session-list UI stays
6
+ * empty); dev only warns — a forgotten sessions mount in a local sample
7
+ * app shouldn't crash the dev-server boot, but it must not stay silent
8
+ * either (#2027 — dev previously had no gate here at all). */
9
+ readonly mode: "prod" | "dev";
4
10
  };
5
11
 
6
12
  // Catch a forgotten sessions mount at boot instead of silently degrading
@@ -12,9 +18,17 @@ export function assertSessionBootInvariants(opts: SessionBootGateOptions): void
12
18
  // skip: sessionStore provider is wired (sessions feature).
13
19
  if (opts.sessionStoreProviderMounted) return;
14
20
 
15
- throw new Error(
16
- "[runProdApp] BOOT ABORTED — auth is mounted but no sessionStore provider is registered. " +
17
- "JWTs would be stateless (no server-side revocation). Mount createSessionsFeature() " +
18
- "(@cosmicdrift/kumiko-bundled-features/sessions) alongside auth-foundation.",
19
- );
21
+ const tag = opts.mode === "prod" ? "runProdApp" : "runDevApp";
22
+ const message =
23
+ "auth is mounted but no sessionStore provider is registered. JWTs would be stateless " +
24
+ "(no server-side revocation) and any session-list screen stays empty. Mount " +
25
+ "createSessionsFeature() (@cosmicdrift/kumiko-bundled-features/sessions) alongside auth-foundation.";
26
+
27
+ if (opts.mode === "dev") {
28
+ // biome-ignore lint/suspicious/noConsole: boot-time ops warning, no logger configured this early
29
+ console.warn(`[${tag}] ${message}`);
30
+ // skip: dev mode, warning already logged above — never abort dev boot.
31
+ return;
32
+ }
33
+ throw new Error(`[${tag}] BOOT ABORTED — ${message}`);
20
34
  }