@cosmicdrift/kumiko-dev-server 0.385.0 → 0.387.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/LICENSE CHANGED
@@ -1,57 +1,21 @@
1
- Business Source License 1.1
2
-
3
- Parameters
4
-
5
- Licensor: Marc Frost
6
-
7
- Licensed Work: @cosmicdrift/kumiko-framework
8
- The Licensed Work is © 2026 Marc Frost.
9
-
10
- Additional Use Grant:
11
- You may use the Licensed Work in production for any purpose, including
12
- commercially, EXCEPT for the Restricted Use.
13
-
14
- "Restricted Use" is defined as using the Licensed Work to provide a platform
15
- or service to third parties that allows them to host, deploy, or run their
16
- own applications built with the Licensed Work. This includes, but is not
17
- limited to: managed hosting services, software-as-a-service (SaaS) platforms,
18
- platform-as-a-service (PaaS), developer platforms, or any multi-tenant
19
- managed offering of the Licensed Work.
20
-
21
- This restriction does not apply to the Licensor, any entity controlled by,
22
- controlling, or under common control with the Licensor ("Affiliates"), or
23
- contractors acting on their behalf. The Licensor remains free to use the
24
- Licensed Work for any purpose, including for the operation of kumiko.rocks.
25
-
26
- Change Date: 2030-05-05
27
- Change License: Apache License, Version 2.0
28
-
29
-
30
- Terms
31
-
32
- The Licensor hereby grants you the right to copy, modify, create derivative works,
33
- redistribute, and make non-production use of the Licensed Work. The Licensor may
34
- make an Additional Use Grant, above, permitting limited production use.
35
-
36
- Effective on the Change Date, or the fourth anniversary of the first publicly
37
- available distribution of the Licensed Work under this License, whichever comes
38
- first, this License will convert to the Change License.
39
-
40
- This Business Source License governs use of the Licensed Work in all cases, except
41
- as to any use that is explicitly granted in the Additional Use Grant above or
42
- under the Change License after the Change Date.
43
-
44
- If your use of the Licensed Work does not comply with the requirements of this
45
- License, you must cease use of the Licensed Work immediately.
46
-
47
- All copies of the Licensed Work, and all derivative works thereof, must include
48
- this License.
49
-
50
- This License does not grant you any right, title, or interest in any trademark,
51
- logo, or branding of the Licensor, except as required to comply with this License.
52
-
53
- TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
54
- “AS IS” BASIS. LICENSOR DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
55
- WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
56
- TITLE, AND NON-INFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY DAMAGES
57
- ARISING OUT OF OR RELATED TO THIS LICENSE OR THE USE OF THE LICENSED WORK.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marc Frost
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -3,10 +3,10 @@ export type BuildServerBundleOptions = {
3
3
  readonly cwd?: string;
4
4
  /** Output-Folder relativ zu cwd. Default: "dist-server". */
5
5
  readonly outDir?: string;
6
- /** Zusätzliche Pakete die zur Laufzeit installiert sein müssen
7
- * (beyond RUNTIME_EXTERNALS). App-spezifisch — Default leer. */
6
+ /** Additional packages that must be installed at runtime
7
+ * (beyond RUNTIME_EXTERNALS). App-specific; defaults to empty. */
8
8
  readonly extraRuntimeExternals?: readonly string[];
9
- /** Zusätzliche Build-only-Externals. App-spezifisch — Default leer. */
9
+ /** Additional build-only externals. App-specific; defaults to empty. */
10
10
  readonly extraBuildOnlyExternals?: readonly string[];
11
11
  /** Additional entry points (paths relative to cwd, e.g. "bin/remote-probe.ts"),
12
12
  * each emitted as `<basename>.js` next to server.js and sharing its chunks.
@@ -22,9 +22,9 @@ export type BuildServerBundleResult = {
22
22
  readonly outDir: string;
23
23
  /** Named entry files (server.js, plus kumiko.js, worker.js, bootstrap.js and extra entries when present). */
24
24
  readonly entries: readonly BuildServerBundleEntry[];
25
- /** Geteilte Framework-Chunks (von splitting). */
25
+ /** Shared framework chunks (from splitting). */
26
26
  readonly chunks: readonly BuildServerBundleEntry[];
27
- /** Gesamt-Größe aller Outputs (entries + chunks) in Bytes. */
27
+ /** Total size of all outputs (entries + chunks) in bytes. */
28
28
  readonly totalBytes: number;
29
29
  readonly runtimeDeps: Readonly<Record<string, string>>;
30
30
  };
@@ -37,7 +37,7 @@ export type KumikoBuildArgs = {
37
37
  /** Parse `kumiko-build [app-dir] [--entry <path>]...` (also `--entry=<path>`).
38
38
  * Unknown flags and a missing `--entry` value throw instead of being ignored. */
39
39
  export declare function parseKumikoBuildArgs(args: readonly string[]): KumikoBuildArgs;
40
- /** Read `package.json#kumiko.extraRuntimeExternals` for kumiko-build (#1484).
40
+ /** Read `package.json#kumiko.extraRuntimeExternals` for kumiko-build.
41
41
  * Invalid / missing shapes → empty list (CLI stays convention-driven). */
42
42
  export declare function readExtraRuntimeExternals(cwd: string): readonly string[];
43
43
  export declare function formatServerBuildResult(result: BuildServerBundleResult, durationMs: number): string;
@@ -1,20 +1,20 @@
1
- // buildServerBundle — Production-Server-Bundle für Kumiko-Apps. Pendant zu
2
- // buildProdBundle (Client). Convention-Driven Discovery liest bin/main.ts
3
- // (+ optional <repo>/bin/kumiko.ts) und produziert ein runtime-self-contained
4
- // dist-server/ aus dem ein Bun-Alpine-Container ohne Monorepo bootet.
1
+ // buildServerBundle — Production server bundle for Kumiko apps. Counterpart to
2
+ // buildProdBundle (client). Convention-driven discovery reads bin/main.ts
3
+ // (+ optional <repo>/bin/kumiko.ts) and produces a runtime-self-contained
4
+ // dist-server/ from which a Bun-Alpine container can boot without the monorepo.
5
5
  //
6
6
  // Convention:
7
7
  //
8
- // bin/main.ts → dist-server/server.js (App-Boot, ruft runProdApp)
9
- // <repo>/bin/kumiko.ts → dist-server/kumiko.js (Migrate-CLI: `schema apply`,
10
- // gefunden via walk-up bis bin/kumiko.ts)
8
+ // bin/main.ts → dist-server/server.js (app boot, calls runProdApp)
9
+ // <repo>/bin/kumiko.ts → dist-server/kumiko.js (migration CLI: `schema apply`,
10
+ // found by walking up to bin/kumiko.ts)
11
11
  // bin/worker.ts → dist-server/worker.js (optional, next to bin/main.ts:
12
12
  // background worker as its own process from the same image)
13
13
  // bin/bootstrap.ts → dist-server/bootstrap.js (optional, next to bin/main.ts:
14
14
  // one-shot runBootstrap provisioning, run as a k8s Job)
15
15
  // extraEntries[i] → dist-server/<basename>.js (opt-in, e.g. a probe script
16
16
  // `bin/remote-probe.ts`; CLI: repeatable `--entry <path>`)
17
- // dist-server/package.json → runtime-deps mit gepinnten Versionen
17
+ // dist-server/package.json → runtime dependencies with pinned versions
18
18
  //
19
19
  // All entries are built in ONE Bun.build call with `splitting`: the framework
20
20
  // lands once as shared chunk-*.js and server.js, kumiko.js, worker.js,
@@ -24,41 +24,41 @@
24
24
  // Output:
25
25
  //
26
26
  // dist-server/
27
- // server.js ← App-Boot-Entry
28
- // kumiko.js ← Migrate-CLI-Entry (wenn bin/kumiko.ts gefunden)
27
+ // server.js ← app boot entry
28
+ // kumiko.js ← migration CLI entry (if bin/kumiko.ts is found)
29
29
  // worker.js ← worker entry (if bin/worker.ts sits next to main.ts)
30
30
  // bootstrap.js ← provisioning entry (if bin/bootstrap.ts sits next to main.ts)
31
31
  // <basename>.js ← one per extraEntries path
32
- // chunk-*.js ← geteilte Framework-Chunks
33
- // package.json ← runtime-deps mit gepinnten Versionen
32
+ // chunk-*.js ← shared framework chunks
33
+ // package.json ← runtime dependencies with pinned versions
34
34
  // bun.lock ← written by writeServerLockfile (kumiko-build), not by buildServerBundle
35
35
  //
36
- // Externals — Pakete die NICHT ins Bundle gebakt werden, sondern via
37
- // `bun install` im Runtime-Container nachgeholt werden:
36
+ // Externals — packages that are NOT bundled, but installed via
37
+ // `bun install` in the runtime container:
38
38
  //
39
- // RUNTIME_EXTERNALS — wird zur Laufzeit gebraucht (native bindings,
40
- // worker-threads, dynamic-require). Landet in
39
+ // RUNTIME_EXTERNALS — needed at runtime (native bindings,
40
+ // worker threads, dynamic require). Added to
41
41
  // dist-server/package.json#dependencies.
42
42
  //
43
- // BUILD_ONLY_EXTERNALS — referenziert nur transitiv im Framework, vom
44
- // App-Code aber nie. Tree-Shake wirft sie aus dem
45
- // Bundle, der external-Marker schaltet nur das
46
- // resolution-during-build ab. NICHT in runtime-deps.
43
+ // BUILD_ONLY_EXTERNALS — referenced only transitively in the framework,
44
+ // never by app code. Tree-shaking removes them from
45
+ // the bundle; the external marker only disables
46
+ // resolution during the build. NOT in runtime dependencies.
47
47
  import { existsSync, readFileSync, statSync } from "node:fs";
48
48
  import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
49
49
  import { basename, dirname, extname, join, resolve } from "node:path";
50
50
  import { parseJsonOrThrow, parseJsonSafe } from "@cosmicdrift/kumiko-framework/utils";
51
51
  const hasBun = typeof Bun !== "undefined";
52
- // Pakete die das Bundle zur Laufzeit weiter referenziert (`from "<pkg>"` nach
53
- // Tree-Shake). Native bindings (argon2), worker-thread-Loader (bullmq,
54
- // ioredis), dynamic-require (postgres, temporal-polyfill) — alles was unter
55
- // Bun-bundling bricht. (drizzle-kit/drizzle-orm sind raus: der Migrate-Pfad
56
- // nutzt jetzt den framework-eigenen runMigrationsFromDir, kein drizzle-kit.)
57
- // meilisearch: deliberately here, not in BUILD_ONLY_EXTERNALS below — apps
52
+ // Packages that the bundle still references at runtime (`from "<pkg>"` after
53
+ // tree-shaking). Native bindings (argon2), worker-thread loaders (bullmq,
54
+ // ioredis), and dynamic require (postgres, temporal-polyfill) — anything that
55
+ // breaks under Bun bundling. (drizzle-kit/drizzle-orm are excluded: the
56
+ // migration path now uses the framework's runMigrationsFromDir, not drizzle-kit.)
57
+ // meilisearch is deliberately here, not in BUILD_ONLY_EXTERNALS below — apps
58
58
  // that import createMeilisearchAdapter (kumiko-framework/search/meilisearch)
59
59
  // reference the package at runtime (no native binding, plain HTTP client,
60
- // but a real runtime import). Found 2026-07-19: money-horse prod crash
61
- // "Cannot find package 'meilisearch'" because it used to sit in
60
+ // but a real runtime import). A production crash in money-horse,
61
+ // "Cannot find package 'meilisearch'", occurred because it used to be in
62
62
  // BUILD_ONLY_EXTERNALS instead.
63
63
  const RUNTIME_EXTERNALS = [
64
64
  "@node-rs/argon2",
@@ -71,11 +71,11 @@ const RUNTIME_EXTERNALS = [
71
71
  "pino-pretty",
72
72
  "meilisearch",
73
73
  ];
74
- // Pakete die nur im Build-Stack erscheinen (transitive Imports im Framework),
75
- // aber vom App-Code nicht referenziert werden. Ohne external-Markierung
76
- // scheitert bun build an dynamic-imports. Tree-Shake wirft sie eh aus dem
77
- // Bundle — der Marker schaltet nur das resolution-during-build ab. NICHT in
78
- // runtime-deps.
74
+ // Packages that appear only in the build stack (transitive framework imports),
75
+ // but are not referenced by app code. Without the external marker,
76
+ // bun build fails on dynamic imports. Tree-shaking removes them from the
77
+ // bundle; the marker only disables resolution during the build. NOT in
78
+ // runtime dependencies.
79
79
  const BUILD_ONLY_EXTERNALS = [
80
80
  "@planetscale/database",
81
81
  "@libsql/client",
@@ -86,9 +86,9 @@ const BUILD_ONLY_EXTERNALS = [
86
86
  // ink (kumiko-tui) hat react-devtools-core als dev-only transitive import.
87
87
  "react-devtools-core",
88
88
  ];
89
- // Bun benennt Entries nach Source-Basename (bin/main.ts → main.js). server.js
90
- // ist die Runtime-Convention; der Entry wird von nichts importiert (nur die
91
- // chunks werden referenziert, per relativem Pfad), darum ist der Rename sicher.
89
+ // Bun names entries after the source basename (bin/main.ts → main.js). server.js
90
+ // is the runtime convention; the entry is not imported by anything (only the
91
+ // chunks are referenced by relative path), so renaming it is safe.
92
92
  const ENTRY_RENAMES = { "main.js": "server.js" };
93
93
  const OPTIONAL_SIBLING_ENTRY_NAMES = ["worker", "bootstrap"];
94
94
  // Output names the built-in entries occupy ("main" is renamed to server.js).
@@ -141,8 +141,8 @@ export async function buildServerBundle(options = {}) {
141
141
  ];
142
142
  await rm(outDir, { recursive: true, force: true });
143
143
  await mkdir(outDir, { recursive: true });
144
- // Ein Bun.build-Call mit allen Entries + splitting → das Framework wird
145
- // einmal als shared chunk abgelegt statt pro Entry inlined.
144
+ // One Bun.build call with all entries plus splitting stores the framework
145
+ // once as a shared chunk instead of inlining it into every entry.
146
146
  const optionalSiblingEntries = OPTIONAL_SIBLING_ENTRY_NAMES.map((baseName) => discoverSiblingEntry(dirname(serverEntry), baseName)).filter((path) => path !== undefined);
147
147
  const entrypoints = [
148
148
  serverEntry,
@@ -164,8 +164,8 @@ export async function buildServerBundle(options = {}) {
164
164
  const logs = result.logs.map(String).join("\n");
165
165
  throw new Error(`[buildServerBundle] build FAILED:\n${logs}`);
166
166
  }
167
- // Größe von Disk lesen, nicht aus out.size — letzteres ist für gesplittete
168
- // Entry-Points unzuverlässig (meldet teils ~0 für den Entry-Stub).
167
+ // Read the size from disk rather than out.size — the latter is unreliable for
168
+ // split entry points and sometimes reports ~0 for the entry stub.
169
169
  const entries = [];
170
170
  const chunks = [];
171
171
  for (const out of result.outputs) {
@@ -209,9 +209,9 @@ function discoverSiblingEntry(dir, baseName) {
209
209
  }
210
210
  return undefined;
211
211
  }
212
- // Walking up vom App-cwd bis ein Verzeichnis mit `bin/kumiko.ts` gefunden wird.
213
- // Failsafe: nach 8 Levels aufgeben — nichts gefunden, kein Repo-Root, dann
214
- // liefert die Funktion undefined und der Caller skippt das CLI-Bundle.
212
+ // Walk up from the app cwd until a directory containing `bin/kumiko.ts` is found.
213
+ // Failsafe: stop after 8 levels. If nothing is found, there is no repo root,
214
+ // so the function returns undefined and the caller skips the CLI bundle.
215
215
  function findRepoRoot(start) {
216
216
  let cur = start;
217
217
  for (let i = 0; i < 8; i++) {
@@ -252,7 +252,7 @@ export function parseKumikoBuildArgs(args) {
252
252
  }
253
253
  return { appDir, extraEntries };
254
254
  }
255
- /** Read `package.json#kumiko.extraRuntimeExternals` for kumiko-build (#1484).
255
+ /** Read `package.json#kumiko.extraRuntimeExternals` for kumiko-build.
256
256
  * Invalid / missing shapes → empty list (CLI stays convention-driven). */
257
257
  export function readExtraRuntimeExternals(cwd) {
258
258
  const pkgJson = join(cwd, "package.json");
@@ -271,11 +271,11 @@ export function readExtraRuntimeExternals(cwd) {
271
271
  return [];
272
272
  return list.filter((x) => typeof x === "string" && x.trim().length > 0);
273
273
  }
274
- // Versionen für RUNTIME_EXTERNALS auflösen: erst aus den installierten
275
- // node_modules/@cosmicdrift/*-Packages (relativ zu cwd — funktioniert für
276
- // jede Consumer-App), dann als Zusatz aus packages/framework + bundled-
277
- // features (Workspace-Checkout des Frameworks selbst). Fallback auf "*"
278
- // wenn keine Quelle den Namen kennt.
274
+ // Resolve versions for RUNTIME_EXTERNALS: first from installed
275
+ // node_modules/@cosmicdrift/*-packages (relative to cwd, which works for
276
+ // any consumer app), then additionally from packages/framework + bundled-
277
+ // features (the framework's own workspace checkout). Fall back to "*"
278
+ // when no source knows the package name.
279
279
  async function resolveRuntimeDepsVersions(cwd, repoRoot, packages) {
280
280
  const out = {};
281
281
  const pinSources = [
@@ -286,7 +286,7 @@ async function resolveRuntimeDepsVersions(cwd, repoRoot, packages) {
286
286
  ]
287
287
  : []),
288
288
  // node_modules applied last among framework sources so installed versions
289
- // win over the repo-root fallback (#1217).
289
+ // win over the repo-root fallback.
290
290
  join(cwd, "node_modules/@cosmicdrift/kumiko-framework/package.json"),
291
291
  join(cwd, "node_modules/@cosmicdrift/kumiko-bundled-features/package.json"),
292
292
  ];
@@ -299,7 +299,7 @@ async function resolveRuntimeDepsVersions(cwd, repoRoot, packages) {
299
299
  Object.assign(allDeps, parsed.dependencies ?? {}, parsed.optionalDependencies ?? {});
300
300
  }
301
301
  // App package.json fills gaps only — pins extraRuntimeExternals without
302
- // overriding framework-native pins the app may also list (#1484 / review).
302
+ // overriding framework-native pins the app may also list.
303
303
  const appPkg = join(cwd, "package.json");
304
304
  if (existsSync(appPkg)) {
305
305
  const raw = await readFile(appPkg, "utf-8");
package/dist/build.js CHANGED
@@ -1,6 +1,6 @@
1
- // Build-Toolchain Public-API. Eigener Sub-Path-Export `@cosmicdrift/kumiko-dev-server/build`
2
- // damit der Main-Barrel kein Bun-Toolchain-Bundle in Production-Reads zieht
3
- // (z.B. wenn drizzle-kit die App-Config unter Node lädt).
1
+ // Build-toolchain public API. The dedicated subpath export `@cosmicdrift/kumiko-dev-server/build`
2
+ // keeps the main barrel from pulling the Bun toolchain bundle into production reads
3
+ // (for example, when drizzle-kit loads the app config under Node).
4
4
  export { ASSETS_DIR, buildProdBundle, discoverHtmlTemplate, formatBuildResult, injectAssetTags, readClientEntriesConfig, resolveClientEntries, } from "@cosmicdrift/kumiko-server-runtime/build-prod-bundle";
5
5
  export { buildServerBundle, discoverServerEntry, formatServerBuildResult, parseKumikoBuildArgs, readExtraRuntimeExternals, } from "./build-server-bundle.js";
6
6
  export { writeServerLockfile, } from "./write-server-lockfile.js";
@@ -37,7 +37,7 @@ export type GdprStackOptions = {
37
37
  };
38
38
  /** Optional tokenVerifier provider(s) (e.g. PAT) when `sessions` is on.
39
39
  * Session-only stacks need none — sessions registers sessionStore, which
40
- * satisfies auth-foundation's boot check (#1570). Pass providers when the
40
+ * satisfies auth-foundation's boot check. Pass providers when the
41
41
  * app also wants Bearer/PAT auth. */
42
42
  readonly providers?: readonly FeatureDefinition[];
43
43
  /** Roles allowed to use the compliance-profiles handlers; default access.admin. */
@@ -59,7 +59,7 @@ export type OpsStackOptions = {
59
59
  * on composeFeatures(includeBundled). Pass `mfa` options to mount TOTP.
60
60
  * When `sessions` is on (the default), auth-foundation is mounted alongside it —
61
61
  * the framework's registry now hard-requires it (sessions.requires("auth-foundation")).
62
- * Session-only is enough for boot (#1570); optional Bearer/PAT still needs a
62
+ * Session-only is enough for boot; optional Bearer/PAT still needs a
63
63
  * tokenVerifier provider from the caller (e.g. createPersonalAccessTokensFeature
64
64
  * ({ scopes })) — PAT scopes are app-specific, so they stay caller-owned. */
65
65
  export type IdentityStackOptions = {
@@ -1,8 +1,7 @@
1
1
  // @runtime runtime
2
2
  // Despite living in this "dev"-marked package, every preset here composes
3
3
  // @cosmicdrift/kumiko-bundled-features factories, and run-config.ts in every
4
- // app repo feeds it into the runProdApp boot path, not just runDevApp
5
- // (kumiko-framework#2337).
4
+ // app repo feeds it into the runProdApp boot path, not just runDevApp.
6
5
  //
7
6
  // Composable SaaS stack presets — explicit opt-in blocks for run-config.ts.
8
7
  // Presets export feature instances only; no implicit PAT scopes or
@@ -3,15 +3,11 @@ export type CrashTrackerOptions = {
3
3
  readonly windowMs: number;
4
4
  };
5
5
  export type CrashTracker = {
6
- /** Crash bei `now` registrieren. Returns `true` wenn der Wrapper
7
- * noch respawnen darf, `false` wenn das Limit überschritten ist.
6
+ /** Registers a crash at `now`. Returns `true` if the wrapper may still respawn and `false` if the limit has been exceeded.
8
7
  *
9
- * Der Crash wird IMMER aufgenommen, auch wenn `false` zurückkommt —
10
- * der Caller terminiert in dem Fall normalerweise eh. Reuse mit
11
- * "rejected → state unchanged"-Semantik wird nicht unterstützt. */
8
+ * The crash is always recorded, even when `false` is returned; the caller normally terminates in that case anyway. Reuse with "rejected → state unchanged" semantics is not supported. */
12
9
  readonly noteCrash: (now: number) => boolean;
13
- /** Anzahl Crashes im Fenster relativ zu `now` — prunt lazy, damit
14
- * der Aufruf auch ohne vorheriges noteCrash konsistent ist. */
10
+ /** Counts crashes in the window relative to `now`. Prunes lazily so the call is consistent even without a preceding noteCrash. */
15
11
  readonly crashCountInWindow: (now: number) => number;
16
12
  };
17
13
  export declare function createCrashTracker(options: CrashTrackerOptions): CrashTracker;
@@ -1,20 +1,11 @@
1
- // Tracks server-Crashes in einem rollenden Zeitfenster und entscheidet,
2
- // ob der dev-wrapper noch respawnen darf oder den Crash-Loop aufgeben
3
- // muss. Liegt als eigener File, damit `kumiko-dev.ts` (bin-Script ohne
4
- // Test-Setup) eine pure, injizierbare API bekommt — die Logik ist
5
- // testbar via createCrashTracker, ohne den Wrapper-Prozess fahren zu
6
- // müssen.
1
+ // Tracks server crashes in a rolling time window and decides whether the dev wrapper may respawn or must stop the crash loop.
2
+ // It lives in a separate file so `kumiko-dev.ts` (a bin script without test setup) has a pure, injectable API; the logic can be tested via createCrashTracker without running the wrapper process.
7
3
  //
8
- // Semantik: ein Crash "zählt im Fenster", wenn er innerhalb der letzten
9
- // `windowMs` ms vor `now` registriert wurde. Wenn nach Aufnahme des
10
- // neuen Crashes mehr als `maxCrashes` im Fenster liegen, gibt der
11
- // Tracker `false` zurück → Wrapper soll aufgeben.
4
+ // A crash counts as being in the window if it was registered within the last `windowMs` ms before `now`. If, after recording the new crash, more than `maxCrashes` are in the window, the tracker returns `false`, so the wrapper must stop.
12
5
  export function createCrashTracker(options) {
13
6
  const timestamps = [];
14
7
  const pruneBefore = (cutoff) => {
15
- // Crashes älter als `cutoff` raus. Gleicher Timestamp wie cutoff
16
- // bleibt drin (`<` statt `<=`) — symmetrisch zur Fenster-Definition
17
- // "innerhalb der letzten windowMs", also Endpoint inklusive.
8
+ // Removes crashes older than `cutoff`. A timestamp equal to cutoff remains (`<` rather than `<=`) to match the window definition "within the last windowMs", whose endpoint is inclusive.
18
9
  while (timestamps.length > 0) {
19
10
  const head = timestamps[0];
20
11
  if (head !== undefined && head < cutoff)
@@ -22,10 +22,9 @@ export type DevClientEntry = {
22
22
  * `kumiko.clientEntries[].name` so dev and prod produce identical
23
23
  * asset URLs (`/client-<name>.js`). */
24
24
  readonly name: string;
25
- /** Absoluter Pfad zur Browser-Entry-Datei. */
25
+ /** Absolute path to the browser entry file. */
26
26
  readonly sourceFile: string;
27
- /** Optional eigenes HTML-Template für diesen Entry. Wenn nicht gesetzt,
28
- * wird `htmlPath` (das default-Template) für alle Entries genutzt. */
27
+ /** Optional HTML template for this entry. If omitted, `htmlPath` (the default template) is used for all entries. */
29
28
  readonly htmlPath?: string;
30
29
  };
31
30
  /** Dev counterpart of `runProdApp.hostDispatch`. Not the same shape: dev names
@@ -58,8 +57,7 @@ export type DevHostDispatchResult = {
58
57
  readonly entryName?: string;
59
58
  readonly file?: string;
60
59
  };
61
- /** Picks an entry by inspecting the incoming request. Wird von
62
- * Multi-Entry-Apps gesetzt; im Single-Entry-Mode irrelevant. */
60
+ /** Picks an entry by inspecting the incoming request. Set by multi-entry apps; irrelevant in single-entry mode. */
63
61
  export type DevHostDispatch = (req: Request, deps: {
64
62
  readonly systemQuery: PageHeadSystemQuery;
65
63
  /** Always empty: dev serves no per-host CSP. */
@@ -69,27 +67,26 @@ export type CreateKumikoServerOptions = {
69
67
  /** Features whose entities, handlers, and screens get wired into the
70
68
  * dev stack. Pass every feature the app is supposed to run. */
71
69
  readonly features: readonly FeatureDefinition[];
72
- /** Absolute path to the browser entry module. The dev-server runs
70
+ /** Absolute path to the browser entry module. The dev server runs
73
71
  * `Bun.build` on it and serves the output at `/client.js`. Omit to
74
- * run a headless API-only dev-stack (rare — every sample has one).
75
- * Mutually exclusive mit `clientEntries`. */
72
+ * run a headless API-only dev stack. Mutually exclusive with
73
+ * `clientEntries`. */
76
74
  readonly clientEntry?: string;
77
- /** Multi-Entry-Mode: mehrere getrennte Bundles, jeweils unter
78
- * `/client-<name>.js`. Mutually exclusive mit `clientEntry`. Setze
79
- * `hostDispatch` mit, sonst bleibt unklar welches Template zurück-
80
- * geht. */
75
+ /** Multi-entry mode: separate bundles served at
76
+ * `/client-<name>.js`. Mutually exclusive with `clientEntry`. Set
77
+ * `hostDispatch` as well; otherwise the server cannot determine which
78
+ * template to return. */
81
79
  readonly clientEntries?: readonly DevClientEntry[];
82
- /** Multi-Entry-Mode: Routing pro Request. Inspiziert `Host` (oder
83
- * was auch immer) und liefert eine Discriminated-Union zurück
84
- * (html → entry-bundle, redirect → 30x, not-found → 404).
85
- * Symmetric zu `runProdApp.hostDispatch` damit dev/prod-Drift
86
- * beim Routing unmöglich ist. */
80
+ /** Multi-entry request routing. Inspects `Host` (or another request
81
+ * property) and returns a discriminated union
82
+ * (`html` → entry bundle, `redirect` → 30x, `not-found` → 404).
83
+ * Mirrors `runProdApp.hostDispatch` so dev and production routing
84
+ * remain aligned. */
87
85
  readonly hostDispatch?: DevHostDispatch;
88
- /** @internal — ersetzt `Bun.build` für Tests. Default ruft die echte
89
- * Bun-Toolchain. Tests unter Node injizieren einen Stub damit der
90
- * Routing-Pfad treibbar bleibt ohne Bun.build aufzurufen.
91
- * KEIN Public-API-Surface — präfixiert mit `_` damit Konsumenten
92
- * wissen dass das ein Test-Seam ist. */
86
+ /** @internal — replaces `Bun.build` in tests. The default uses the real
87
+ * Bun toolchain; Node-based tests inject a stub so the routing path can
88
+ * run without calling Bun.build.
89
+ * Not part of the public API; the `_` prefix identifies this test seam. */
93
90
  readonly _buildBundle?: (sourceFile: string) => Promise<{
94
91
  readonly js: string;
95
92
  readonly map: string;
@@ -128,41 +125,40 @@ export type CreateKumikoServerOptions = {
128
125
  * this so repeated `createKumikoServer` calls don't accumulate
129
126
  * listeners on the process. Default true (dev-server behaviour). */
130
127
  readonly installSignalHandlers?: boolean;
131
- /** Auth-Route-Config (login, tenants, switch-tenant, logout). Wenn
132
- * gesetzt wird die Auto-JWT-Mint auf GET / abgeschaltet — der
133
- * Client ist dann selbst fürs Login zuständig. Zur echten Wirkung
134
- * brauchen die dazugehörigen Features (user/tenant/auth-email-
135
- * password) via `features` drin sein. */
128
+ /** Auth route configuration (login, tenants, switch-tenant, logout). When
129
+ * set, disables automatic JWT minting on GET /; the client is responsible
130
+ * for login. The corresponding features (user/tenant/auth-email-password)
131
+ * must also be included through `features` for these routes to work. */
136
132
  readonly auth?: AuthRoutesConfig;
137
- /** Extra-AppContext-Keys (z.B. configResolver für config-feature).
138
- * Wird an setupTestStack weitergereicht. Siehe TestStackOptions
139
- * für die erlaubten Shapes (object oder factory-function). */
133
+ /** Additional AppContext keys, such as `configResolver` for the config
134
+ * feature. Forwarded to `setupTestStack`; see `TestStackOptions` for
135
+ * the accepted shapes (object or factory function). */
140
136
  readonly extraContext?: TestStackOptions["extraContext"];
141
- /** Anonymous-Access aktivieren — Requests ohne JWT werden als
142
- * Pseudo-User mit Rolle `anonymous` durchgelassen, sofern der
143
- * Handler `roles: ["anonymous"]` deklariert. Tenant-Resolution per
144
- * Header/Cookie/Default; siehe AnonymousAccessConfig. */
137
+ /** Enables anonymous access: requests without a JWT proceed as a
138
+ * pseudo-user with the `anonymous` role when the handler declares
139
+ * `roles: ["anonymous"]`. Tenant resolution uses a header, cookie, or
140
+ * default; see `AnonymousAccessConfig`. */
145
141
  readonly anonymousAccess?: TestStackOptions["anonymousAccess"];
146
- /** File-Storage-Provider — durchgereicht an setupTestStack. Wenn gesetzt,
147
- * mountet der Stack die Upload-Routes (POST/GET/DELETE /api/files) +
148
- * `ctx.files` und legt die file_refs-Tabelle an. Für Demos:
142
+ /** File-storage provider forwarded to `setupTestStack`. When set, the
143
+ * stack mounts the upload routes (POST/GET/DELETE /api/files), exposes
144
+ * `ctx.files`, and creates the `file_refs` table. Example:
149
145
  * `{ storageProvider: createInMemoryFileProvider() }`. */
150
146
  readonly files?: TestStackOptions["files"];
151
- /** Feature-toggle resolver — durchgereicht an setupTestStack. Wenn
152
- * gesetzt, konsultiert der dispatcher-feature-gate, hook-filter, MSP-
153
- * filter den callback; absent = alle features always-on. Erforderlich
154
- * für Tier-Composition (Sprint 8) wo per-Tenant unterschiedliche
155
- * features aktiv sein sollen. Die typische produktive Implementierung
156
- * ist `() => globalFeatureToggleRuntime.effectiveFeatures` post-boot
157
- * (createLateBoundHolder-pattern, weil runtime stack.db braucht). */
147
+ /** Feature-toggle resolver forwarded to `setupTestStack`. When set, the
148
+ * dispatcher feature gate, hook filter, and MSP filter consult it;
149
+ * absent means all features remain enabled. Required for per-tenant
150
+ * feature composition. The typical production implementation is
151
+ * `() => globalFeatureToggleRuntime.effectiveFeatures` after boot,
152
+ * using the createLateBoundHolder pattern because the runtime needs
153
+ * `stack.db`. */
158
154
  readonly effectiveFeatures?: TestStackOptions["effectiveFeatures"];
159
- /** Wird nach dem Aufsetzen der Entity-Tabellen aufgerufen. Hook für
160
- * non-entity-tables (unsafePushTables) und Seeding (admin user, initial
161
- * tenant, …). Muss idempotent sein — im persistent-DB-Modus läuft
162
- * es bei jedem Boot. */
155
+ /** Called after entity tables are set up. Use this hook for non-entity
156
+ * tables (`unsafePushTables`) and seeding (admin user, initial tenant,
157
+ * and so on). Must be idempotent because persistent-DB mode runs it on
158
+ * every boot. */
163
159
  readonly onAfterSetup?: (stack: TestStack) => Promise<void>;
164
160
  /** Declarative HTTP-routes outside the /api/write|query|batch pipeline —
165
- * symmetric to runProdApp.extraRoutes (kumiko-framework#3050). Mounted
161
+ * symmetric to runProdApp.extraRoutes. Mounted
166
162
  * inside buildServer (setupTestStack), before the Static/HTML-fallback,
167
163
  * so an own GET (/feed.xml, /og-image, …) wins over the dev-asset path. */
168
164
  readonly extraRoutes?: readonly ExtraRouteDefinition[];
@@ -179,8 +175,8 @@ export type CreateKumikoServerOptions = {
179
175
  /** Per-request head-metadata resolver (Open-Graph/title/description) —
180
176
  * same option, same `PageHeadResolver` signature, and same shared
181
177
  * resolve+inject call (`resolveAndInjectPageHead`) as `runProdApp`'s
182
- * `resolvePageHead`, so dev and e2e exercise the exact code prod does
183
- * (kumiko-framework#3026). Consulted for every templated HTML response
178
+ * `resolvePageHead`, so dev and e2e exercise the exact code prod does.
179
+ * Consulted for every templated HTML response
184
180
  * (the default single-entry shell and a host-dispatched `"html"` entry),
185
181
  * right before it's served. On error, `null`, or a timeout, the shell
186
182
  * ships unchanged with status 200 — never a 500. `systemQuery` inside