@cosmicdrift/kumiko-framework 0.294.1 → 0.295.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-framework",
3
- "version": "0.294.1",
3
+ "version": "0.295.0",
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>",
@@ -198,8 +198,8 @@
198
198
  "./package.json": "./package.json"
199
199
  },
200
200
  "dependencies": {
201
- "@cosmicdrift/kumiko-http": "0.294.1",
202
- "@cosmicdrift/kumiko-types": "0.294.1",
201
+ "@cosmicdrift/kumiko-http": "0.295.0",
202
+ "@cosmicdrift/kumiko-types": "0.295.0",
203
203
  "bullmq": "^5.76.7",
204
204
  "bun-types": "^1.3.13",
205
205
  "hono": "^4.13.1",
@@ -215,7 +215,7 @@
215
215
  "zod": "^4.4.3"
216
216
  },
217
217
  "devDependencies": {
218
- "@cosmicdrift/kumiko-dispatcher-live": "0.294.1",
218
+ "@cosmicdrift/kumiko-dispatcher-live": "0.295.0",
219
219
  "bun-types": "^1.3.13",
220
220
  "pino-pretty": "^13.1.3"
221
221
  },
@@ -28,7 +28,7 @@ describe("runSchemaCli — Temporal polyfill", () => {
28
28
  // never retried on the next run.
29
29
  //
30
30
  // Asserting on globalThis.Temporal directly doesn't work here: the test
31
- // harness's own preload (test-setup/base.preload.ts) already calls
31
+ // harness's own preload (@cosmicdrift/kumiko-testing/preload/temporal) already calls
32
32
  // ensureTemporalPolyfill() once per process, and its idempotency cache
33
33
  // (a module-level flag, not re-derived from globalThis) short-circuits
34
34
  // any later call regardless of what a test does to globalThis.Temporal in
@@ -42,7 +42,7 @@ let indexPrefix: string;
42
42
  describe.skipIf(!MEILI_UP)("meilisearch adapter (live)", () => {
43
43
  beforeAll(async () => {
44
44
  client = new Meilisearch({ host: MEILI_URL, apiKey: MEILI_KEY });
45
- indexPrefix = `test_${uuid().slice(-6)}_`;
45
+ indexPrefix = `test_${uuid()}_`;
46
46
  adapter = createMeilisearchAdapter({
47
47
  url: MEILI_URL,
48
48
  apiKey: MEILI_KEY,
@@ -110,10 +110,9 @@ describe.skipIf(!MEILI_UP)("meilisearch adapter (live)", () => {
110
110
  });
111
111
 
112
112
  afterAll(async () => {
113
- // Clean up all test indices
114
113
  const indices = await client.getIndexes();
115
114
  for (const idx of indices.results) {
116
- if (idx.uid.startsWith("test_")) {
115
+ if (idx.uid.startsWith(indexPrefix)) {
117
116
  try {
118
117
  await client.index(idx.uid).delete().waitTask();
119
118
  } catch {
package/src/stack/db.ts CHANGED
@@ -12,7 +12,7 @@ function requireEnv(name: string): string {
12
12
  const value = process.env[name];
13
13
  if (!value) {
14
14
  throw new Error(
15
- `Missing required env var: ${name}. Copy .env.example to .env and fill in values.`,
15
+ `Missing required env var: ${name}. Copy .env.example to .env and fill in values. Tests also need the test services (Postgres, Redis) running, e.g. "docker compose up -d".`,
16
16
  );
17
17
  }
18
18
  return value;
@@ -0,0 +1,54 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { requireRealProviders } from "../real-providers";
3
+
4
+ const CI = "CI";
5
+ const FLAG = "KUMIKO_REAL_PROVIDERS";
6
+ const API_KEY = "ANTHROPIC_API_KEY";
7
+ const TOUCHED_ENV = [CI, FLAG, API_KEY];
8
+
9
+ describe("requireRealProviders", () => {
10
+ const saved = new Map<string, string | undefined>();
11
+
12
+ beforeEach(() => {
13
+ for (const name of TOUCHED_ENV) {
14
+ saved.set(name, process.env[name]);
15
+ delete process.env[name];
16
+ }
17
+ });
18
+
19
+ afterEach(() => {
20
+ for (const [name, value] of saved) {
21
+ if (value === undefined) delete process.env[name];
22
+ else process.env[name] = value;
23
+ }
24
+ });
25
+
26
+ test("throws when the flag is missing, even with an API key set", () => {
27
+ process.env[API_KEY] = "sk-test";
28
+ expect(() => requireRealProviders()).toThrow(/KUMIKO_REAL_PROVIDERS=1/);
29
+ });
30
+
31
+ test("throws when the flag has any value other than 1", () => {
32
+ process.env[FLAG] = "true";
33
+ expect(() => requireRealProviders()).toThrow(/KUMIKO_REAL_PROVIDERS=1/);
34
+ });
35
+
36
+ test("throws in CI even with the flag set", () => {
37
+ process.env[CI] = "true";
38
+ process.env[FLAG] = "1";
39
+ expect(() => requireRealProviders()).toThrow(/never run in CI/);
40
+ });
41
+
42
+ test("does not throw with the flag set outside CI", () => {
43
+ process.env[FLAG] = "1";
44
+ expect(() => requireRealProviders()).not.toThrow();
45
+ });
46
+
47
+ test("treats CI=false and CI=0 as not CI", () => {
48
+ process.env[FLAG] = "1";
49
+ process.env[CI] = "false";
50
+ expect(() => requireRealProviders()).not.toThrow();
51
+ process.env[CI] = "0";
52
+ expect(() => requireRealProviders()).not.toThrow();
53
+ });
54
+ });
@@ -44,6 +44,7 @@ export {
44
44
  createRecordingProvider,
45
45
  type RecordingProvider,
46
46
  } from "./observability-recorder";
47
+ export { requireRealProviders } from "./real-providers";
47
48
  export { deleteRows, seedRow, seedRows, updateRows } from "./seed";
48
49
  export {
49
50
  sharedItemEntity,
@@ -0,0 +1,20 @@
1
+ const REAL_PROVIDERS_FLAG = "KUMIKO_REAL_PROVIDERS";
2
+ const CI_FLAG = "CI";
3
+
4
+ function isCi(): boolean {
5
+ const ci = process.env[CI_FLAG];
6
+ return ci !== undefined && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
7
+ }
8
+
9
+ export function requireRealProviders(): void {
10
+ if (isCi()) {
11
+ throw new Error(
12
+ `Real-provider tests never run in CI (CI is set). Run them locally with ${REAL_PROVIDERS_FLAG}=1 via the test:real / e2e:real script.`,
13
+ );
14
+ }
15
+ if (process.env[REAL_PROVIDERS_FLAG] !== "1") {
16
+ throw new Error(
17
+ `Real-provider test needs ${REAL_PROVIDERS_FLAG}=1 (an API key alone never enables it). Run it via the test:real / e2e:real script.`,
18
+ );
19
+ }
20
+ }