@cosmicdrift/kumiko-dev-server 0.378.0 → 0.380.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/bin/kumiko-dev.ts CHANGED
@@ -54,7 +54,9 @@ const crashTracker = createCrashTracker({
54
54
  const spawnServer = (): void => {
55
55
  const child = spawn(bunPath, ["run", entry, ...passthroughArgs], {
56
56
  stdio: "inherit",
57
- env: process.env,
57
+ // Error responses carry cause details only under an explicit development/test NODE_ENV,
58
+ // so a local dev run without one would hide them.
59
+ env: { ...process.env, NODE_ENV: process.env["NODE_ENV"] ?? "development" },
58
60
  });
59
61
 
60
62
  child.on("exit", (code, signal) => {
@@ -37,7 +37,7 @@ export type DevHostDispatchResult = {
37
37
  readonly kind: "html";
38
38
  readonly entryName: string;
39
39
  /** @deprecated Ignored — HTML never carries the schema; createKumikoApp
40
- * fetches it from the authenticated GET /api/schema. */
40
+ * fetches it from the authenticated GET /api/schema. Removed in 0.390.0. */
41
41
  readonly injectSchema?: boolean;
42
42
  } | {
43
43
  /** Static HTML: serves a file verbatim, no bundle injection. Mirrors
@@ -28,8 +28,8 @@ export type ManagedCheckResult = {
28
28
  /** kumiko.config.ts calls defineAppConfig: drift fails the check from then on. */
29
29
  readonly adopted: boolean;
30
30
  readonly files: readonly ManagedFileReport[];
31
- /** Findings that do not fail the check. */
32
- readonly warnings: readonly string[];
31
+ /** Findings that fail the check on their own. */
32
+ readonly errors: readonly string[];
33
33
  };
34
34
  export declare function checkManagedFiles(facts: ManagedAppFacts): ManagedCheckResult;
35
35
  /** States that fail `kumiko check managed` once the app has adopted kumiko.config.ts. */
@@ -5,7 +5,7 @@ import { stripAppJobs } from "./app-jobs.js";
5
5
  import { parseJsonc } from "./jsonc.js";
6
6
  import { renderLineDiff } from "./line-diff.js";
7
7
  import { readManagedMarker, stripManagedMarker } from "./marker.js";
8
- import { BIOME_PRESET, listManagedFiles, listManagedWarnings, renderManagedBunfig, TSCONFIG_PRESET, } from "./registry.js";
8
+ import { BIOME_PRESET, listManagedErrors, listManagedFiles, renderManagedBunfig, TSCONFIG_PRESET, } from "./registry.js";
9
9
  function readIfExists(path) {
10
10
  return existsSync(path) ? readFileSync(path, "utf-8") : undefined;
11
11
  }
@@ -102,7 +102,7 @@ export function checkManagedFiles(facts) {
102
102
  kind: file.kind,
103
103
  ...checkFile(file, facts),
104
104
  })),
105
- warnings: listManagedWarnings(facts),
105
+ errors: listManagedErrors(facts),
106
106
  };
107
107
  }
108
108
  /** States that fail `kumiko check managed` once the app has adopted kumiko.config.ts. */
@@ -2,5 +2,6 @@ export { findPortCollisions, listAppPorts, type PortClaim, type PortCollision, p
2
2
  export { type AppScriptsReport, checkAppScripts, KNOWN_EXTRA_APP_SCRIPTS, PREPARE_SCRIPT, STANDARD_APP_SCRIPTS, type StandardAppScript, } from "./app-scripts.ts";
3
3
  export { checkManagedFiles, isManagedDrift, type ManagedCheckResult, type ManagedFileReport, type ManagedFileState, } from "./check.ts";
4
4
  export { addManagedMarker, MANAGED_MARKER, readManagedMarker, stripManagedMarker, } from "./marker.ts";
5
- export { BIOME_PRESET, listManagedFiles, listManagedWarnings, type ManagedAppFacts, type ManagedAppInput, type ManagedFile, type ManagedTemplateFile, renderBiomePresetJson, renderManagedBunfig, renderManagedTemplates, renderTsconfigPresetJson, resolveManagedAppFacts, TSCONFIG_PRESET, } from "./registry.ts";
5
+ export { findKumikoPinErrors } from "./pins.ts";
6
+ export { BIOME_PRESET, CURRENT_INFRA_REF, listManagedErrors, listManagedFiles, type ManagedAppFacts, type ManagedAppInput, type ManagedFile, type ManagedTemplateFile, renderBiomePresetJson, renderManagedBunfig, renderManagedTemplates, renderTsconfigPresetJson, resolveManagedAppFacts, TSCONFIG_PRESET, } from "./registry.ts";
6
7
  export { type SyncAction, type SyncManagedOptions, syncManagedFiles } from "./sync.ts";
@@ -2,5 +2,6 @@ export { findPortCollisions, listAppPorts, parseStaticAppConfig, readStaticAppCo
2
2
  export { checkAppScripts, KNOWN_EXTRA_APP_SCRIPTS, PREPARE_SCRIPT, STANDARD_APP_SCRIPTS, } from "./app-scripts.js";
3
3
  export { checkManagedFiles, isManagedDrift, } from "./check.js";
4
4
  export { addManagedMarker, MANAGED_MARKER, readManagedMarker, stripManagedMarker, } from "./marker.js";
5
- export { BIOME_PRESET, listManagedFiles, listManagedWarnings, renderBiomePresetJson, renderManagedBunfig, renderManagedTemplates, renderTsconfigPresetJson, resolveManagedAppFacts, TSCONFIG_PRESET, } from "./registry.js";
5
+ export { findKumikoPinErrors } from "./pins.js";
6
+ export { BIOME_PRESET, CURRENT_INFRA_REF, listManagedErrors, listManagedFiles, renderBiomePresetJson, renderManagedBunfig, renderManagedTemplates, renderTsconfigPresetJson, resolveManagedAppFacts, TSCONFIG_PRESET, } from "./registry.js";
6
7
  export { syncManagedFiles } from "./sync.js";
@@ -0,0 +1,2 @@
1
+ /** Errors for Kumiko dependencies of an app: every one exact, and one version per scope. */
2
+ export declare function findKumikoPinErrors(packageJson: unknown): readonly string[];
@@ -0,0 +1,43 @@
1
+ import { isPlainObject, propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
2
+ // peerDependencies stay out: libraries declare a lower bound (>=x.y.z) on purpose.
3
+ const DEPENDENCY_BLOCKS = ["dependencies", "devDependencies", "optionalDependencies"];
4
+ const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
5
+ function pinScopeOf(name) {
6
+ if (name === "create-kumiko-app" || name.startsWith("@cosmicdrift/kumiko-")) {
7
+ return "@cosmicdrift";
8
+ }
9
+ if (name.startsWith("@cosmicdriftgamestudio/kumiko-"))
10
+ return "@cosmicdriftgamestudio";
11
+ return undefined;
12
+ }
13
+ function dependencyEntries(block) {
14
+ if (!isPlainObject(block))
15
+ return [];
16
+ return Object.entries(block).flatMap(([name, spec]) => typeof spec === "string" ? [[name, spec]] : []);
17
+ }
18
+ /** Errors for Kumiko dependencies of an app: every one exact, and one version per scope. */
19
+ export function findKumikoPinErrors(packageJson) {
20
+ const errors = [];
21
+ const versionsByScope = new Map();
22
+ for (const blockName of DEPENDENCY_BLOCKS) {
23
+ const block = propertyOf(packageJson, blockName);
24
+ for (const [name, spec] of dependencyEntries(block)) {
25
+ const scope = pinScopeOf(name);
26
+ if (scope === undefined || spec.startsWith("workspace:"))
27
+ continue;
28
+ if (!EXACT_VERSION.test(spec)) {
29
+ errors.push(`package.json#${blockName}: ${name}@${spec} is not an exact version. Pin it without ^ or ~ (kumiko-upgrade --apply rewrites the pins).`);
30
+ continue;
31
+ }
32
+ const versions = versionsByScope.get(scope) ?? new Set();
33
+ versions.add(spec);
34
+ versionsByScope.set(scope, versions);
35
+ }
36
+ }
37
+ for (const [scope, versions] of versionsByScope) {
38
+ if (versions.size > 1) {
39
+ errors.push(`package.json: ${scope} packages are pinned to ${versions.size} versions (${[...versions].sort().join(", ")}). Use one version per scope.`);
40
+ }
41
+ }
42
+ return errors;
43
+ }
@@ -18,6 +18,8 @@ export type ManagedAppFacts = {
18
18
  };
19
19
  /** Ref used in the workflow templates while `kumiko.deploy.infraRef` is unset. */
20
20
  export declare const DEFAULT_INFRA_REF = "main";
21
+ /** Infra tag a new app is scaffolded with; Renovate keeps it current via the reusables-v* rule. */
22
+ export declare const CURRENT_INFRA_REF = "reusables-v1.2.0";
21
23
  /** Byte-compared after the marker lines are stripped. */
22
24
  export type ManagedTemplateFile = {
23
25
  readonly kind: "template";
@@ -44,8 +46,8 @@ export type ManagedSeedFile = {
44
46
  };
45
47
  export type ManagedFile = ManagedTemplateFile | ManagedBunfigFile | ManagedStructuralFile | ManagedSeedFile;
46
48
  export declare function resolveManagedAppFacts(input: ManagedAppInput): ManagedAppFacts;
47
- /** Findings that do not fail `kumiko check managed`. */
48
- export declare function listManagedWarnings(facts: ManagedAppFacts): readonly string[];
49
+ /** Findings that fail `kumiko check managed` whether or not the app adopted kumiko.config.ts. */
50
+ export declare function listManagedErrors(facts: ManagedAppFacts): readonly string[];
49
51
  export declare function renderManagedTemplates(facts: ManagedAppFacts): readonly ManagedTemplateFile[];
50
52
  export declare function managedBunfigFiles(facts: ManagedAppFacts): readonly ManagedBunfigFile[];
51
53
  /** Options are the app's choice; read them from the file so the check only flags real drift. */
@@ -10,10 +10,13 @@ import { BUNFIG_FILES, renderBunfig } from "@cosmicdrift/kumiko-testing/bunfig";
10
10
  import { isKebabSegment } from "../kebab.js";
11
11
  import { readReleaseTarget, render, renderDeployFiles, } from "../scaffold-deploy.js";
12
12
  import { addManagedMarker } from "./marker.js";
13
+ import { findKumikoPinErrors } from "./pins.js";
13
14
  export const BIOME_PRESET = "@cosmicdrift/kumiko-framework/biome";
14
15
  export const TSCONFIG_PRESET = "@cosmicdrift/kumiko-framework/tsconfig.app.json";
15
16
  /** Ref used in the workflow templates while `kumiko.deploy.infraRef` is unset. */
16
17
  export const DEFAULT_INFRA_REF = "main";
18
+ /** Infra tag a new app is scaffolded with; Renovate keeps it current via the reusables-v* rule. */
19
+ export const CURRENT_INFRA_REF = "reusables-v1.2.0";
17
20
  function templatesDir() {
18
21
  return join(dirname(fileURLToPath(import.meta.url)), "..", "..", "templates", "managed");
19
22
  }
@@ -44,14 +47,20 @@ export function resolveManagedAppFacts(input) {
44
47
  deploy: readReleaseTarget(input.appRoot, appName),
45
48
  };
46
49
  }
47
- /** Findings that do not fail `kumiko check managed`. */
48
- export function listManagedWarnings(facts) {
50
+ function readPackageJsonOrEmpty(appRoot) {
51
+ const path = join(appRoot, "package.json");
52
+ return existsSync(path) ? parseJsonOrThrow(readFileSync(path, "utf-8"), path) : {};
53
+ }
54
+ /** Findings that fail `kumiko check managed` whether or not the app adopted kumiko.config.ts. */
55
+ export function listManagedErrors(facts) {
56
+ const errors = [];
49
57
  if (facts.deploy.visibility === "private" && facts.deploy.infraRef === undefined) {
50
- return [
51
- `package.json#kumiko.deploy.infraRef is not set: ci.yml and release.yml call the infra workflows at @${DEFAULT_INFRA_REF}. Set it to an infra tag; a missing value will fail this check in a later release.`,
52
- ];
58
+ errors.push("package.json#kumiko.deploy.infraRef is not set. Set kumiko.deploy.infraRef to an infra tag (reusables-vX.Y.Z); ci.yml and release.yml would otherwise call the infra workflows at @" +
59
+ DEFAULT_INFRA_REF +
60
+ ".");
53
61
  }
54
- return [];
62
+ errors.push(...findKumikoPinErrors(readPackageJsonOrEmpty(facts.appRoot)));
63
+ return errors;
55
64
  }
56
65
  function marked(path, body, style, executable = false) {
57
66
  return { kind: "template", path, content: addManagedMarker(body, style), executable };
@@ -187,7 +187,13 @@ export async function runDevApp(options) {
187
187
  configurePiiSubjectKms(piiKms.kms);
188
188
  configureBlindIndexKey(piiKms.blindIndexKey);
189
189
  const releaseEphemeralPiiKms = captureEphemeralDevPiiKms(piiKms);
190
+ // The auth features bind database-backed revokers onto their (possibly shared) instances;
191
+ // stop() swaps them for inert ones so a later stack in the same process never reaches this
192
+ // app's closed connection.
193
+ const resetLateBindings = [];
190
194
  const releasePiiKms = async () => {
195
+ for (const reset of resetLateBindings)
196
+ reset();
191
197
  releaseEphemeralPiiKms();
192
198
  await wiring.kms.close();
193
199
  };
@@ -419,13 +425,13 @@ export async function runDevApp(options) {
419
425
  });
420
426
  const sessionsFeature = features.find((f) => f.name === SESSIONS_FEATURE);
421
427
  if (sessionsFeature) {
422
- bindAutoRevokeFromFeature(sessionsFeature)?.(store.massRevoker);
428
+ bindReleasable(bindAutoRevokeFromFeature(sessionsFeature), store.massRevoker, INERT_REVOKER, resetLateBindings);
423
429
  }
424
430
  if (mfaFeature) {
425
- bindMfaRevokeAllOtherSessionsFromFeature(mfaFeature)?.(store.revokeAllOthers);
431
+ bindReleasable(bindMfaRevokeAllOtherSessionsFromFeature(mfaFeature), store.revokeAllOthers, INERT_REVOKER, resetLateBindings);
426
432
  }
427
433
  if (mfaFeature && patFeature) {
428
- bindRevokeAllPatTokensFromFeature(mfaFeature)?.((userId) => revokeAllPatTokensForUser(stack.db, userId));
434
+ bindReleasable(bindRevokeAllPatTokensFromFeature(mfaFeature), (userId) => revokeAllPatTokensForUser(stack.db, userId), INERT_REVOKER, resetLateBindings);
429
435
  }
430
436
  sessionStore = store;
431
437
  }
@@ -433,7 +439,7 @@ export async function runDevApp(options) {
433
439
  tokenVerifier = (rawToken) => resolveTokenVerifier({ db: stack.db, registry: stack.registry }, rawToken);
434
440
  // Password-change → PAT-revoke: independent of sessions/MFA being
435
441
  // mounted, so wired unconditionally here whenever PAT itself is.
436
- bindPatAutoRevokeOnPasswordChangeFromFeature(patFeature)?.((userId) => revokeAllPatTokensForUser(stack.db, userId));
442
+ bindReleasable(bindPatAutoRevokeOnPasswordChangeFromFeature(patFeature), (userId) => revokeAllPatTokensForUser(stack.db, userId), INERT_REVOKER, resetLateBindings);
437
443
  }
438
444
  if (effectiveAuth) {
439
445
  await seedAdminGuarded(stack.db, effectiveAuth.admin);
@@ -487,6 +493,14 @@ export async function runDevApp(options) {
487
493
  },
488
494
  };
489
495
  }
496
+ const INERT_REVOKER = async () => 0;
497
+ // Binds a late-bound revoker and records how to swap it for an inert one when the app stops.
498
+ function bindReleasable(bind, revoker, inert, resets) {
499
+ if (bind) {
500
+ bind(revoker);
501
+ resets.push(() => bind(inert));
502
+ }
503
+ }
490
504
  // Exported for the wiring-contract test (config-resolver-default.integration):
491
505
  // pins that the dev configResolver-default carries the ENV→app-override bridge
492
506
  // and that a caller-supplied configResolver still overrides it. Not re-exported
@@ -149,8 +149,7 @@ function renderSchemaIndex(name) {
149
149
  }
150
150
  function renderWebIndex(name) {
151
151
  const camel = kebabToCamel(name);
152
- return `// @runtime client
153
- // Client-Seite des Features: translations, components (custom-Screens),
152
+ return `// Client-Seite des Features: translations, components (custom-Screens),
154
153
  // columnRenderers. In src/app/client.tsx bei
155
154
  // createKumikoApp({ clientFeatures: [...] }) einhängen — das passiert
156
155
  // NICHT automatisch. Screens: eine Datei pro Screen in diesem Ordner.
@@ -37,8 +37,7 @@ export type ScaffoldAppOptions = {
37
37
  * @cosmicdrift/kumiko-testing/scaffold. Without it the app gets the managed
38
38
  * bunfigs but no example tests and fewer standard scripts. */
39
39
  readonly testSetup?: ScaffoldTestSetupFactory;
40
- /** Infra tag written to `package.json#kumiko.deploy.infraRef`. Unset: the managed
41
- * workflows call infra at `main` and `kumiko check managed` warns. */
40
+ /** Infra tag written to `package.json#kumiko.deploy.infraRef`. Default: CURRENT_INFRA_REF. */
42
41
  readonly infraRef?: string;
43
42
  };
44
43
  export type ScaffoldAppResult = {
@@ -23,7 +23,7 @@ import { APP_CONFIG_FILE } from "@cosmicdrift/kumiko-framework/config";
23
23
  import { collectTableMetas, generateMigration, writeSnapshotJson, } from "@cosmicdrift/kumiko-framework/db";
24
24
  import { composeFeatures } from "@cosmicdrift/kumiko-server-runtime/compose-features";
25
25
  import { isKebabSegment } from "./kebab.js";
26
- import { PREPARE_SCRIPT, resolveManagedAppFacts, syncManagedFiles } from "./managed-files/index.js";
26
+ import { CURRENT_INFRA_REF, PREPARE_SCRIPT, resolveManagedAppFacts, syncManagedFiles, } from "./managed-files/index.js";
27
27
  import { renderAgentsMd, renderClaudeMd, renderMountedFeatureList } from "./scaffold-agent-docs.js";
28
28
  import { createDemoTasksFeature, renderDemoSeedFile, renderDemoTasksFeatureFile, renderDemoTasksI18n, renderDemoTasksIndex, renderDemoTasksWebIndex, } from "./scaffold-demo-tasks.js";
29
29
  // Exact pin (no caret): the generated files are formatted to this Biome's output,
@@ -51,7 +51,7 @@ export async function scaffoldApp(options) {
51
51
  mkdirSync(join(destination, "src"), { recursive: true });
52
52
  mkdirSync(join(destination, "kumiko"), { recursive: true });
53
53
  const files = [];
54
- write(join(destination, "package.json"), renderPackageJson(options.name, version, testSetup, options.infraRef));
54
+ write(join(destination, "package.json"), renderPackageJson(options.name, version, testSetup, options.infraRef ?? CURRENT_INFRA_REF));
55
55
  files.push("package.json");
56
56
  write(join(destination, "kumiko.json"), renderKumikoManifest());
57
57
  files.push("kumiko.json");
@@ -143,7 +143,7 @@ function renderPackageJson(name, version, testSetup, infraRef) {
143
143
  version: "0.1.0",
144
144
  private: true,
145
145
  type: "module",
146
- ...(infraRef === undefined ? {} : { kumiko: { deploy: { infraRef } } }),
146
+ kumiko: { deploy: { infraRef } },
147
147
  scripts: appScripts(testSetup),
148
148
  dependencies: {
149
149
  "@cosmicdrift/kumiko-bundled-features": version,
@@ -30,7 +30,7 @@ export function mergeExtraContext(base, presets) {
30
30
  Object.assign(merged, addConfigAccessorFactory({ configResolver }, deps.registry));
31
31
  }
32
32
  if (presets.includes("template-resolver")) {
33
- merged["templateResolver"] = createTemplateResolverApi(deps.db);
33
+ merged["templateResolver"] = createTemplateResolverApi(deps.db, deps.registry);
34
34
  }
35
35
  return merged;
36
36
  };
package/dist/testing.d.ts CHANGED
@@ -6,6 +6,9 @@ import { type TestStack } from "@cosmicdrift/kumiko-testing/stack";
6
6
  import { type SetupTestStackFromFeaturesOptions } from "./setup-test-stack-from-features.ts";
7
7
  export type SetupAppTestStackOptions = SetupTestStackFromFeaturesOptions & {
8
8
  readonly registryTables?: boolean;
9
+ /** Same as `RunDevAppOptions["featureToggles"]`: mounts `feature-toggles` (do not list it in `features`)
10
+ * and layers its operator switches over the dispatcher's feature gate. Replaces `effectiveFeatures`. */
11
+ readonly featureToggles?: boolean;
9
12
  };
10
13
  export declare function setupAppTestStack(features: readonly FeatureDefinition[], options?: SetupAppTestStackOptions): Promise<TestStack>;
11
14
  export declare function seedTenant(stack: TestStack, options?: SeedTenantOptions): Promise<InProcessSeededTenant>;
package/dist/testing.js CHANGED
@@ -4,21 +4,43 @@ import { hashPassword } from "@cosmicdrift/kumiko-bundled-features/auth-email-pa
4
4
  import { createMailTransportInMemoryFeature, getInbox, } from "@cosmicdrift/kumiko-bundled-features/mail-transport-inmemory";
5
5
  import { TenantHandlers, TenantQueries } from "@cosmicdrift/kumiko-bundled-features/tenant";
6
6
  import { UserHandlers } from "@cosmicdrift/kumiko-bundled-features/user";
7
+ import { resolveFeatureTogglesOption } from "@cosmicdrift/kumiko-server-runtime";
7
8
  import { persistTenantRowsWith, seedTenantWith, } from "@cosmicdrift/kumiko-testing";
8
9
  import { createE2eSeedRoutes as createE2eSeedRoutesWith, } from "@cosmicdrift/kumiko-testing/e2e/seed-route";
9
10
  import { pushEntityProjectionTables } from "@cosmicdrift/kumiko-testing/stack";
10
11
  import { setupTestStackFromFeatures, } from "./setup-test-stack-from-features.js";
11
12
  export async function setupAppTestStack(features, options = {}) {
12
- const { registryTables = true, includeBundled = true, ...stackOptions } = options;
13
- const stack = await setupTestStackFromFeatures(features, {
13
+ const { registryTables = true, includeBundled = true, featureToggles = false, ...stackOptions } = options;
14
+ if (featureToggles && stackOptions.effectiveFeatures !== undefined) {
15
+ throw new Error("setupAppTestStack: featureToggles replaces effectiveFeatures, set only one of them.");
16
+ }
17
+ const toggles = featureToggles ? resolveFeatureTogglesOption() : undefined;
18
+ const toggleResolver = { current: undefined };
19
+ const stack = await setupTestStackFromFeatures(toggles === undefined ? features : [...features, toggles.feature], {
14
20
  ...stackOptions,
15
21
  includeBundled,
16
22
  entityTables: registryTables,
23
+ ...(toggles !== undefined && {
24
+ effectiveFeatures: (tenantId) => {
25
+ if (toggleResolver.current === undefined) {
26
+ throw new Error("feature-toggles: resolver not bound yet (boot order).");
27
+ }
28
+ return toggleResolver.current(tenantId);
29
+ },
30
+ }),
17
31
  });
18
- if (!registryTables)
19
- return stack;
20
32
  try {
21
- await pushEntityProjectionTables(stack, stack.registry);
33
+ if (registryTables)
34
+ await pushEntityProjectionTables(stack, stack.registry);
35
+ if (toggles !== undefined) {
36
+ const bound = await toggles.bind({
37
+ db: stack.db,
38
+ registry: stack.registry,
39
+ cacheSync: stack.cacheSync,
40
+ tierResolver: undefined,
41
+ });
42
+ toggleResolver.current = bound.resolver;
43
+ }
22
44
  }
23
45
  catch (error) {
24
46
  await stack.cleanup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-dev-server",
3
- "version": "0.378.0",
3
+ "version": "0.380.0",
4
4
  "description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding. Its compose-stacks/env-schema subpaths are consumed at prod boot too — see @cosmicdrift/kumiko-server-runtime for the prod runner itself.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -77,14 +77,14 @@
77
77
  "kumiko-upgrade": "./bin/kumiko-upgrade.ts"
78
78
  },
79
79
  "dependencies": {
80
- "@cosmicdrift/kumiko-bundled-features": "0.378.0",
81
- "@cosmicdrift/kumiko-framework": "0.378.0",
82
- "@cosmicdrift/kumiko-renderer": "0.378.0",
83
- "@cosmicdrift/kumiko-renderer-mail-html": "0.378.0",
84
- "@cosmicdrift/kumiko-guards": "0.378.0",
85
- "@cosmicdrift/kumiko-server-runtime": "0.378.0",
86
- "@cosmicdrift/kumiko-testing": "0.378.0",
87
- "@cosmicdrift/kumiko-types": "0.378.0",
80
+ "@cosmicdrift/kumiko-bundled-features": "0.380.0",
81
+ "@cosmicdrift/kumiko-framework": "0.380.0",
82
+ "@cosmicdrift/kumiko-renderer": "0.380.0",
83
+ "@cosmicdrift/kumiko-renderer-mail-html": "0.380.0",
84
+ "@cosmicdrift/kumiko-guards": "0.380.0",
85
+ "@cosmicdrift/kumiko-server-runtime": "0.380.0",
86
+ "@cosmicdrift/kumiko-testing": "0.380.0",
87
+ "@cosmicdrift/kumiko-types": "0.380.0",
88
88
  "bun-types": "^1.3.13",
89
89
  "ts-morph": "^28.0.0",
90
90
  "zod": "^4.4.3"
package/src/changes.json CHANGED
@@ -1,4 +1,23 @@
1
1
  [
2
+ {
3
+ "version": "0.379.0",
4
+ "type": "fix",
5
+ "title": "runDevApp stop releases the late-bound revokers",
6
+ "detail": "stop() swaps the session and PAT revokers that boot bound onto the auth-mfa, sessions and personal-access-tokens feature instances for inert ones, and those features now accept a new binding on a second boot instead of keeping the first. A later app or test stack on the same feature instances no longer reaches the closed database connection."
7
+ },
8
+ {
9
+ "version": "0.379.0",
10
+ "type": "improvement",
11
+ "title": "setupAppTestStack featureToggles option",
12
+ "detail": "featureToggles true mounts the feature-toggles feature and binds it, so a disabled feature is unreachable in the test stack. It cannot be combined with effectiveFeatures."
13
+ },
14
+ {
15
+ "version": "0.379.0",
16
+ "type": "breaking",
17
+ "title": "kumiko check managed rejects loose Kumiko pins and a missing infraRef",
18
+ "detail": "In an app, all @cosmicdrift/kumiko-*, create-kumiko-app and @cosmicdriftgamestudio/kumiko-* dependencies must be exact versions (workspace specs are allowed) on one version per scope. A private app without kumiko.deploy.infraRef is an error instead of a warning. In @cosmicdrift/kumiko-dev-server, listManagedWarnings is now listManagedErrors and ManagedCheckResult.warnings is now errors.",
19
+ "migration": "Pin the Kumiko dependencies without ^ or ~ and to one version per scope. Set kumiko.deploy.infraRef to an infra tag (reusables-vX.Y.Z) in package.json. Programmatic callers: rename listManagedWarnings to listManagedErrors and ManagedCheckResult.warnings to errors."
20
+ },
2
21
  {
3
22
  "version": "0.378.0",
4
23
  "type": "improvement",