@cosmicdrift/kumiko-server-runtime 0.274.0 → 0.275.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.274.0",
3
+ "version": "0.275.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>",
@@ -80,8 +80,8 @@
80
80
  }
81
81
  },
82
82
  "dependencies": {
83
- "@cosmicdrift/kumiko-bundled-features": "0.274.0",
84
- "@cosmicdrift/kumiko-framework": "0.274.0",
83
+ "@cosmicdrift/kumiko-bundled-features": "0.275.0",
84
+ "@cosmicdrift/kumiko-framework": "0.275.0",
85
85
  "temporal-polyfill": "^0.3.2"
86
86
  },
87
87
  "publishConfig": {
@@ -8,6 +8,8 @@
8
8
  // dispatched ins Leere → 500.
9
9
 
10
10
  import { describe, expect, spyOn, test } from "bun:test";
11
+ import { securityBaselineFeatures } from "@cosmicdrift/kumiko-bundled-features/presets";
12
+ import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
11
13
  import { defineFeature, validateBoot } from "@cosmicdrift/kumiko-framework/engine";
12
14
  import { composeFeatures } from "../compose-features";
13
15
 
@@ -190,4 +192,15 @@ describe("composeFeatures", () => {
190
192
  if (!auth) return;
191
193
  expect(Object.keys(auth.writeHandlers)).toContain("login");
192
194
  });
195
+
196
+ test("includeBundled=false → sessions mounted via preset + standalone collapses to one entry", () => {
197
+ const sessionsInstances = securityBaselineFeatures();
198
+ const standaloneSessions = createSessionsFeature();
199
+ const features = composeFeatures([...sessionsInstances, standaloneSessions], {
200
+ includeBundled: false,
201
+ });
202
+ const sessionsEntries = features.filter((f) => f.name === "sessions");
203
+ expect(sessionsEntries).toHaveLength(1);
204
+ expect(sessionsEntries[0]).toBe(sessionsInstances[0]);
205
+ });
193
206
  });
@@ -32,7 +32,7 @@ import {
32
32
  import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";
33
33
  import { createTenantFeature } from "@cosmicdrift/kumiko-bundled-features/tenant";
34
34
  import { createUserFeature } from "@cosmicdrift/kumiko-bundled-features/user";
35
- import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
35
+ import { dedupeFeatures, type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
36
36
 
37
37
  export type ComposeFeaturesOptions = {
38
38
  /** When true, prepends config + user + tenant + auth-email-password
@@ -58,7 +58,7 @@ export function composeFeatures(
58
58
  // own createAuthEmailPasswordFeature(...) call, or login silently
59
59
  // bypasses MFA. Upgrade if this trips someone: warn here when appFeatures
60
60
  // contains AUTH_MFA_FEATURE but includeBundled is false.
61
- if (!options.includeBundled) return [...appFeatures];
61
+ if (!options.includeBundled) return dedupeFeatures(appFeatures);
62
62
 
63
63
  // Bundled foundation goes first so its instances carry the runDevApp /
64
64
  // runProdApp `authOptions` (passwordReset wiring etc.). App-features that
@@ -71,7 +71,8 @@ export function composeFeatures(
71
71
  // via APP_FEATURES) — but if it's there, the login handler needs its
72
72
  // status-checker wired in at construction time, since createAuthEmail-
73
73
  // PasswordFeature is built right here, before the caller ever sees it.
74
- const mfaFeature = appFeatures.find((f) => f.name === AUTH_MFA_FEATURE);
74
+ const dedupedAppFeatures = dedupeFeatures(appFeatures);
75
+ const mfaFeature = dedupedAppFeatures.find((f) => f.name === AUTH_MFA_FEATURE);
75
76
  const authOptions = mfaFeature
76
77
  ? { ...options.authOptions, mfaStatusChecker: mfaStatusCheckerFromFeature(mfaFeature) }
77
78
  : options.authOptions;
@@ -95,7 +96,7 @@ export function composeFeatures(
95
96
  ];
96
97
  const bundledNames = new Set(bundled.map((f) => f.name));
97
98
  const filteredApp: FeatureDefinition[] = [];
98
- for (const f of appFeatures) {
99
+ for (const f of dedupedAppFeatures) {
99
100
  if (bundledNames.has(f.name)) {
100
101
  // biome-ignore lint/suspicious/noConsole: boot-time UX warning
101
102
  console.warn(