@cosmicdrift/kumiko-framework 0.122.3 → 0.122.5

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-framework",
3
- "version": "0.122.3",
3
+ "version": "0.122.5",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -189,7 +189,7 @@
189
189
  "zod": "^4.4.3"
190
190
  },
191
191
  "devDependencies": {
192
- "@cosmicdrift/kumiko-dispatcher-live": "0.122.3",
192
+ "@cosmicdrift/kumiko-dispatcher-live": "0.122.5",
193
193
  "bun-types": "^1.3.13",
194
194
  "pino-pretty": "^13.1.3"
195
195
  },
@@ -0,0 +1,48 @@
1
+ import { afterEach, describe, expect, spyOn, test } from "bun:test";
2
+ import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { runSchemaCli, type SchemaCliOut } from "../schema-cli";
6
+ import * as timeModule from "../time";
7
+
8
+ function captureOut(): { out: SchemaCliOut; log: string[]; err: string[] } {
9
+ const log: string[] = [];
10
+ const err: string[] = [];
11
+ return { out: { log: (l) => log.push(l), err: (l) => err.push(l) }, log, err };
12
+ }
13
+
14
+ describe("runSchemaCli — Temporal polyfill", () => {
15
+ let appCwd: string;
16
+
17
+ afterEach(() => {
18
+ if (appCwd) rmSync(appCwd, { recursive: true, force: true });
19
+ });
20
+
21
+ test("calls ensureTemporalPolyfill on every invocation", async () => {
22
+ // runProdApp/runDevApp call ensureTemporalPolyfill() at boot; the
23
+ // standalone CLI (migrate-db initContainer, `bun kumiko.js schema apply`)
24
+ // never goes through that boot path. Without this, a projection rebuild's
25
+ // tz/timestamp coercion throws "Temporal is not defined" on any runtime
26
+ // that lacks native Temporal — deterministically, since the crashed
27
+ // process still records the migration as applied and the rebuild is
28
+ // never retried on the next run.
29
+ //
30
+ // Asserting on globalThis.Temporal directly doesn't work here: the test
31
+ // harness's own preload (test-setup/base.preload.ts) already calls
32
+ // ensureTemporalPolyfill() once per process, and its idempotency cache
33
+ // (a module-level flag, not re-derived from globalThis) short-circuits
34
+ // any later call regardless of what a test does to globalThis.Temporal in
35
+ // between. Spying on the imported binding sidesteps that cache entirely.
36
+ appCwd = mkdtempSync(join(tmpdir(), "kumiko-schema-cli-temporal-"));
37
+ mkdirSync(join(appCwd, "kumiko"), { recursive: true });
38
+ const spy = spyOn(timeModule, "ensureTemporalPolyfill");
39
+ try {
40
+ const cap = captureOut();
41
+ const code = await runSchemaCli([], appCwd, cap.out);
42
+ expect(code).toBe(0);
43
+ expect(spy).toHaveBeenCalled();
44
+ } finally {
45
+ spy.mockRestore();
46
+ }
47
+ });
48
+ });
package/src/schema-cli.ts CHANGED
@@ -36,6 +36,7 @@ import {
36
36
  createProjectionStateTable,
37
37
  rebuildProjection,
38
38
  } from "./pipeline";
39
+ import { ensureTemporalPolyfill } from "./time";
39
40
 
40
41
  export type SchemaCliOut = {
41
42
  readonly log: (line: string) => void;
@@ -126,6 +127,13 @@ export async function runSchemaCli(
126
127
  out: SchemaCliOut,
127
128
  options: RunSchemaCliOptions = {},
128
129
  ): Promise<number> {
130
+ // runProdApp/runDevApp install this at boot; the standalone CLI (the
131
+ // migrate-db initContainer, `bun kumiko.js schema apply`) never goes through
132
+ // that boot path, so a projection rebuild's tz/timestamp coercion throws
133
+ // "Temporal is not defined" — deterministically, on every rebuild-marker
134
+ // migration, since the crashed process still records the migration as
135
+ // applied and the rebuild is never retried.
136
+ await ensureTemporalPolyfill();
129
137
  const sub = argv[0];
130
138
  const schemaFile = resolvePath(appCwd, "kumiko/schema.ts");
131
139
  const migrationsDir = resolvePath(appCwd, "kumiko/migrations");