@cosmicdrift/kumiko-server-runtime 0.198.0 → 0.199.1

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.198.0",
3
+ "version": "0.199.1",
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>",
@@ -80,8 +80,8 @@
80
80
  }
81
81
  },
82
82
  "dependencies": {
83
- "@cosmicdrift/kumiko-bundled-features": "0.198.0",
84
- "@cosmicdrift/kumiko-framework": "0.198.0",
83
+ "@cosmicdrift/kumiko-bundled-features": "0.199.1",
84
+ "@cosmicdrift/kumiko-framework": "0.199.1",
85
85
  "temporal-polyfill": "^0.3.2"
86
86
  },
87
87
  "publishConfig": {
@@ -532,6 +532,49 @@ describe("runProdApp", () => {
532
532
  const adminBody = await adminRes.text();
533
533
  expect(adminBody).toContain("ADMIN");
534
534
  expect(adminBody).toContain("__KUMIKO_SCHEMA__");
535
+
536
+ // #2062: this boot()'s widgetFeature never wires extraContext.searchAdapter,
537
+ // so run-prod-app's `searchAdapterMissing = !(extraContext).searchAdapter`
538
+ // must actually flip true and flow into the injected schema — proving
539
+ // the cast-boundary read isn't silently always-false.
540
+ const schemaMatch = adminBody.match(/window\.__KUMIKO_SCHEMA__=(\{.*?\});<\/script>/s);
541
+ expect(schemaMatch).not.toBeNull();
542
+ const injectedSchema = JSON.parse(schemaMatch?.[1] ?? "{}");
543
+ // Guards against a regex miss silently degrading to `?? "{}"` — an empty
544
+ // features array would make `.every(...)` below vacuously true too.
545
+ expect(injectedSchema.features.length).toBeGreaterThan(0);
546
+ const allFeaturesMissingAdapter = injectedSchema.features.every(
547
+ (f: { searchAdapterMissing?: boolean }) => f.searchAdapterMissing === true,
548
+ );
549
+ expect(allFeaturesMissingAdapter).toBe(true);
550
+ });
551
+
552
+ test("extraContext.searchAdapter wired → injected schema omits searchAdapterMissing (#2062)", async () => {
553
+ const tmpStaticDir = await createTempStaticDir({
554
+ "index.html": "<html><body>ADMIN</body><script src=/client.js></script></html>",
555
+ });
556
+
557
+ const handle = await boot(undefined, {
558
+ staticDir: tmpStaticDir,
559
+ // Presence-only probe — run-prod-app's cast-boundary read only checks
560
+ // truthiness, so a minimal stand-in is enough without pulling in the
561
+ // real SearchAdapter shape.
562
+ extraContext: { searchAdapter: { configure: async () => {}, search: async () => [] } },
563
+ hostDispatch: () => ({ kind: "html", file: "index.html", injectSchema: true }),
564
+ });
565
+
566
+ const res = await handle.fetch(new Request("http://demo.example.test/"));
567
+ const body = await res.text();
568
+ const schemaMatch = body.match(/window\.__KUMIKO_SCHEMA__=(\{.*?\});<\/script>/s);
569
+ expect(schemaMatch).not.toBeNull();
570
+ const injectedSchema = JSON.parse(schemaMatch?.[1] ?? "{}");
571
+ // Guards against a regex miss silently degrading to `?? "{}"` — an empty
572
+ // features array would make `.some(...)` below vacuously false too.
573
+ expect(injectedSchema.features.length).toBeGreaterThan(0);
574
+ const anyFeatureMissingAdapter = injectedSchema.features.some(
575
+ (f: { searchAdapterMissing?: boolean }) => f.searchAdapterMissing === true,
576
+ );
577
+ expect(anyFeatureMissingAdapter).toBe(false);
535
578
  });
536
579
 
537
580
  test("hostDispatch: redirect-Modus", async () => {
@@ -930,6 +930,11 @@ export async function runProdApp(options: RunProdAppOptions): Promise<ProdAppHan
930
930
  { ...autoExtraContext, ...resolvedExtraContext },
931
931
  registry,
932
932
  );
933
+ // @cast-boundary engine-bridge — searchAdapter is an optional context-extension
934
+ // (SharedContextFields.searchAdapter), invisible to extraContext's loose
935
+ // Record<string, unknown>-based typing. Presence check only, no need to
936
+ // pull in the SearchAdapter type here.
937
+ const searchAdapterMissing = !(extraContext as { searchAdapter?: unknown }).searchAdapter;
933
938
  const baseAnonymousAccess =
934
939
  typeof options.anonymousAccess === "function"
935
940
  ? options.anonymousAccess(deps)
@@ -1148,7 +1153,7 @@ export async function runProdApp(options: RunProdAppOptions): Promise<ProdAppHan
1148
1153
  // pro Tenant, Auth-Rolle gated Screens), muss die Injection pro
1149
1154
  // Request rendern — staticDir-Fallback einen render(req)-Hook bekommen
1150
1155
  // statt eines fixed JSON-Strings. Heute: registry-static, also OK.
1151
- const appSchemaJson = JSON.stringify(buildAppSchema(registry));
1156
+ const appSchemaJson = JSON.stringify(buildAppSchema(registry, { searchAdapterMissing }));
1152
1157
 
1153
1158
  // App-eigene HTTP-Routes mounten — VOR den Seeds (symmetrisch zum
1154
1159
  // dev-server, siehe create-kumiko-server.ts). Ein Seed, der über den