@cosmicdrift/kumiko-dev-server 0.329.0 → 0.330.2

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.
Files changed (125) hide show
  1. package/README.md +17 -0
  2. package/bin/kumiko-build.ts +3 -2
  3. package/bin/kumiko-dev.ts +1 -1
  4. package/bin/kumiko-init-deploy.ts +1 -1
  5. package/bin/kumiko-schema-check.ts +4 -1
  6. package/dist/build-server-bundle.d.ts +31 -0
  7. package/dist/build-server-bundle.js +282 -0
  8. package/dist/build.d.ts +2 -0
  9. package/dist/build.js +5 -0
  10. package/dist/cli.d.ts +5 -0
  11. package/dist/cli.js +9 -0
  12. package/dist/codegen/index.d.ts +4 -0
  13. package/dist/codegen/index.js +4 -0
  14. package/dist/codegen/render.d.ts +55 -0
  15. package/dist/codegen/render.js +273 -0
  16. package/dist/codegen/run-codegen.d.ts +20 -0
  17. package/dist/codegen/run-codegen.js +219 -0
  18. package/dist/codegen/scan-events.d.ts +76 -0
  19. package/dist/codegen/scan-events.js +447 -0
  20. package/dist/codegen/watch.d.ts +33 -0
  21. package/dist/codegen/watch.js +112 -0
  22. package/dist/compose-stacks.d.ts +60 -0
  23. package/dist/compose-stacks.js +127 -0
  24. package/dist/crash-tracker.d.ts +17 -0
  25. package/dist/crash-tracker.js +37 -0
  26. package/dist/create-kumiko-server.d.ts +195 -0
  27. package/dist/create-kumiko-server.js +1040 -0
  28. package/dist/env-schema.d.ts +15 -0
  29. package/dist/env-schema.js +60 -0
  30. package/dist/few-shot-corpus.d.ts +72 -0
  31. package/dist/few-shot-corpus.js +262 -0
  32. package/dist/framework-version.d.ts +1 -0
  33. package/dist/framework-version.js +31 -0
  34. package/dist/index.d.ts +22 -0
  35. package/dist/index.js +33 -0
  36. package/dist/init-deploy-cli.d.ts +10 -0
  37. package/dist/init-deploy-cli.js +111 -0
  38. package/dist/kebab.d.ts +1 -0
  39. package/{src/kebab.ts → dist/kebab.js} +2 -3
  40. package/dist/run-dev-app.d.ts +174 -0
  41. package/dist/run-dev-app.js +403 -0
  42. package/dist/scaffold-app-feature.d.ts +16 -0
  43. package/dist/scaffold-app-feature.js +240 -0
  44. package/dist/scaffold-app.d.ts +45 -0
  45. package/dist/scaffold-app.js +852 -0
  46. package/dist/scaffold-demo-tasks.d.ts +8 -0
  47. package/{src/scaffold-demo-tasks.ts → dist/scaffold-demo-tasks.js} +69 -98
  48. package/dist/scaffold-deploy.d.ts +92 -0
  49. package/dist/scaffold-deploy.js +254 -0
  50. package/dist/scaffold-feature.d.ts +26 -0
  51. package/dist/scaffold-feature.js +163 -0
  52. package/dist/schema-apply.d.ts +11 -0
  53. package/dist/schema-apply.js +95 -0
  54. package/dist/schema-check-core.d.ts +2 -0
  55. package/{src/schema-check-core.ts → dist/schema-check-core.js} +8 -10
  56. package/dist/schema-tables-auth-mode.d.ts +2 -0
  57. package/{src/schema-tables-auth-mode.ts → dist/schema-tables-auth-mode.js} +2 -10
  58. package/dist/schema-tables-minimal.d.ts +2 -0
  59. package/{src/schema-tables-minimal.ts → dist/schema-tables-minimal.js} +2 -11
  60. package/dist/setup-test-stack-from-features.d.ts +11 -0
  61. package/dist/setup-test-stack-from-features.js +42 -0
  62. package/dist/welcome-banner.d.ts +15 -0
  63. package/dist/welcome-banner.js +27 -0
  64. package/package.json +71 -24
  65. package/src/changes.json +6 -0
  66. package/src/__tests__/build-prod-bundle.integration.test.ts +0 -282
  67. package/src/__tests__/build-server-bundle.test.ts +0 -228
  68. package/src/__tests__/classify-change.test.ts +0 -87
  69. package/src/__tests__/compose-stacks.test.ts +0 -273
  70. package/src/__tests__/config-resolver-default.integration.test.ts +0 -165
  71. package/src/__tests__/crash-tracker.test.ts +0 -89
  72. package/src/__tests__/create-kumiko-server-errors.test.ts +0 -54
  73. package/src/__tests__/create-kumiko-server-options.test.ts +0 -33
  74. package/src/__tests__/create-kumiko-server.integration.test.ts +0 -1007
  75. package/src/__tests__/discover-format.test.ts +0 -32
  76. package/src/__tests__/env-schema.integration.test.ts +0 -248
  77. package/src/__tests__/env-schema.test.ts +0 -132
  78. package/src/__tests__/few-shot-corpus.test.ts +0 -316
  79. package/src/__tests__/framework-version.test.ts +0 -45
  80. package/src/__tests__/init-deploy-cli.test.ts +0 -113
  81. package/src/__tests__/kumiko-schema-check.test.ts +0 -56
  82. package/src/__tests__/merge-extra-context.test.ts +0 -67
  83. package/src/__tests__/resolve-stylesheet.test.ts +0 -132
  84. package/src/__tests__/run-dev-app-boot-validation.test.ts +0 -38
  85. package/src/__tests__/run-dev-app.integration.test.ts +0 -290
  86. package/src/__tests__/runtime-classification.test.ts +0 -26
  87. package/src/__tests__/saas-identity-wire.integration.test.ts +0 -427
  88. package/src/__tests__/scaffold-app-feature.test.ts +0 -216
  89. package/src/__tests__/scaffold-app.test.ts +0 -630
  90. package/src/__tests__/scaffold-demo-tasks.test.ts +0 -47
  91. package/src/__tests__/scaffold-deploy.test.ts +0 -464
  92. package/src/__tests__/scaffold-dev-cycle.integration.test.ts +0 -109
  93. package/src/__tests__/scaffold-feature.test.ts +0 -143
  94. package/src/__tests__/schema-apply.integration.test.ts +0 -191
  95. package/src/__tests__/setup-test-stack-from-features.integration.test.ts +0 -30
  96. package/src/__tests__/setup-test-stack-job-run-logger.integration.test.ts +0 -72
  97. package/src/__tests__/walkthrough.integration.test.ts +0 -140
  98. package/src/__tests__/welcome-banner.test.ts +0 -42
  99. package/src/build-server-bundle.ts +0 -346
  100. package/src/build.ts +0 -28
  101. package/src/codegen/__tests__/render-codegen.test.ts +0 -100
  102. package/src/codegen/__tests__/run-codegen.test.ts +0 -665
  103. package/src/codegen/__tests__/strict-mode-diagnostics.test.ts +0 -347
  104. package/src/codegen/__tests__/watch.test.ts +0 -222
  105. package/src/codegen/index.ts +0 -19
  106. package/src/codegen/render.ts +0 -305
  107. package/src/codegen/run-codegen.ts +0 -278
  108. package/src/codegen/scan-events.ts +0 -565
  109. package/src/codegen/watch.ts +0 -140
  110. package/src/compose-stacks.ts +0 -193
  111. package/src/crash-tracker.ts +0 -56
  112. package/src/create-kumiko-server.ts +0 -1397
  113. package/src/env-schema.ts +0 -72
  114. package/src/few-shot-corpus.ts +0 -370
  115. package/src/framework-version.ts +0 -39
  116. package/src/index.ts +0 -105
  117. package/src/init-deploy-cli.ts +0 -148
  118. package/src/run-dev-app.ts +0 -694
  119. package/src/scaffold-app-feature.ts +0 -283
  120. package/src/scaffold-app.ts +0 -1033
  121. package/src/scaffold-deploy.ts +0 -393
  122. package/src/scaffold-feature.ts +0 -226
  123. package/src/schema-apply.ts +0 -123
  124. package/src/setup-test-stack-from-features.ts +0 -80
  125. package/src/welcome-banner.ts +0 -43
@@ -0,0 +1,127 @@
1
+ // @runtime runtime
2
+ // Despite living in this "dev"-marked package, every preset here composes
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).
6
+ //
7
+ // Composable SaaS stack presets — explicit opt-in blocks for run-config.ts.
8
+ // Presets export feature instances only; no implicit PAT scopes, anonymousAccess,
9
+ // or tier maps (those stay in bin/server.ts / app run-config).
10
+ import { createAuditFeature } from "@cosmicdrift/kumiko-bundled-features/audit";
11
+ import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
12
+ import { createAuthMfaFeature, } from "@cosmicdrift/kumiko-bundled-features/auth-mfa";
13
+ import { createComplianceProfilesFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles";
14
+ import { createDataRetentionFeature } from "@cosmicdrift/kumiko-bundled-features/data-retention";
15
+ import { createDeliveryFeature } from "@cosmicdrift/kumiko-bundled-features/delivery";
16
+ import { fileFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/file-foundation";
17
+ import { fileProviderInMemoryFeature } from "@cosmicdrift/kumiko-bundled-features/file-provider-inmemory";
18
+ import { fileProviderS3Feature } from "@cosmicdrift/kumiko-bundled-features/file-provider-s3";
19
+ import { fileProviderS3EnvFeature } from "@cosmicdrift/kumiko-bundled-features/file-provider-s3-env";
20
+ import { createFilesFeature } from "@cosmicdrift/kumiko-bundled-features/files";
21
+ import { createJobsFeature } from "@cosmicdrift/kumiko-bundled-features/jobs";
22
+ import { createLegalPagesFeature, } from "@cosmicdrift/kumiko-bundled-features/legal-pages";
23
+ import { mailFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/mail-foundation";
24
+ import { mailTransportInMemoryFeature } from "@cosmicdrift/kumiko-bundled-features/mail-transport-inmemory";
25
+ import { mailTransportSmtpFeature } from "@cosmicdrift/kumiko-bundled-features/mail-transport-smtp";
26
+ import { createRateLimitingFeature } from "@cosmicdrift/kumiko-bundled-features/rate-limiting";
27
+ import { createRendererFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/renderer-foundation";
28
+ import { createRendererSimpleFeature } from "@cosmicdrift/kumiko-bundled-features/renderer-simple";
29
+ import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
30
+ import { createTemplateResolverFeature } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
31
+ import { createTenantLifecycleFeature } from "@cosmicdrift/kumiko-bundled-features/tenant-lifecycle";
32
+ import { createUserDataRightsFeature, } from "@cosmicdrift/kumiko-bundled-features/user-data-rights";
33
+ import { createUserDataRightsDefaultsFeature } from "@cosmicdrift/kumiko-bundled-features/user-data-rights-defaults";
34
+ export function stackFeatureNames(features) {
35
+ return features.map((f) => f.name);
36
+ }
37
+ export function composePagesStack(options = {}) {
38
+ // Pages content lives in template-resolver (kind text-block), which the
39
+ // renderer stack already mounts — an app composing pages WITHOUT the renderer
40
+ // stack has to add createTemplateResolverFeature() itself, or legal-pages'
41
+ // r.requires fails the boot. Mounting it here instead would collide with
42
+ // composeRendererStack (createRegistry rejects duplicate features).
43
+ return [
44
+ createLegalPagesFeature(options.wrapLayout !== undefined ? { wrapLayout: options.wrapLayout } : {}),
45
+ ];
46
+ }
47
+ export function composeRendererStack() {
48
+ return [
49
+ createTemplateResolverFeature(),
50
+ createRendererFoundationFeature(),
51
+ createRendererSimpleFeature(),
52
+ ];
53
+ }
54
+ export function composeMailStack(options) {
55
+ const out = [mailFoundationFeature];
56
+ for (const transport of options.transports) {
57
+ if (transport === "inmemory")
58
+ out.push(mailTransportInMemoryFeature);
59
+ if (transport === "smtp")
60
+ out.push(mailTransportSmtpFeature);
61
+ }
62
+ return out;
63
+ }
64
+ export function composeFileStack(options) {
65
+ const out = [fileFoundationFeature];
66
+ for (const provider of options.providers) {
67
+ if (provider === "inmemory")
68
+ out.push(fileProviderInMemoryFeature);
69
+ if (provider === "s3")
70
+ out.push(fileProviderS3Feature);
71
+ if (provider === "s3-env")
72
+ out.push(fileProviderS3EnvFeature);
73
+ }
74
+ if (options.includeFilesFeature ?? true)
75
+ out.push(createFilesFeature());
76
+ return out;
77
+ }
78
+ /** Retention + compliance (+ optional lifecycle/sessions). UDR/files mount separately — order varies per app. */
79
+ export function composeGdprStack(options = {}) {
80
+ const order = options.order ?? "retention-first";
81
+ const retention = createDataRetentionFeature();
82
+ const compliance = createComplianceProfilesFeature();
83
+ const out = order === "compliance-first" ? [compliance, retention] : [retention, compliance];
84
+ if (options.tenantLifecycle)
85
+ out.push(createTenantLifecycleFeature());
86
+ if (options.sessions) {
87
+ out.push(authFoundationFeature, createSessionsFeature());
88
+ if (options.providers)
89
+ out.push(...options.providers);
90
+ }
91
+ return out;
92
+ }
93
+ export function composeUserDataRightsStack(options = {}) {
94
+ const includeDefaults = options.includeDefaults ?? true;
95
+ const out = [createUserDataRightsFeature(options.userDataRights ?? {})];
96
+ if (includeDefaults)
97
+ out.push(createUserDataRightsDefaultsFeature());
98
+ return out;
99
+ }
100
+ export function composeOpsStack(options = {}) {
101
+ const delivery = options.delivery ?? true;
102
+ const audit = options.audit ?? true;
103
+ const jobs = options.jobs ?? true;
104
+ const rateLimiting = options.rateLimiting ?? false;
105
+ const out = [];
106
+ if (delivery)
107
+ out.push(createDeliveryFeature());
108
+ if (audit)
109
+ out.push(createAuditFeature());
110
+ if (jobs)
111
+ out.push(createJobsFeature());
112
+ if (rateLimiting)
113
+ out.push(createRateLimitingFeature());
114
+ return out;
115
+ }
116
+ /** Identity opt-ins: sessions by default; auth-mfa when `mfa` options given.
117
+ * Do not also pass `sessions: true` to composeGdprStack — duplicate feature. */
118
+ export function composeIdentityStack(options = {}) {
119
+ const sessions = options.sessions ?? true;
120
+ const out = [];
121
+ if (sessions) {
122
+ out.push(authFoundationFeature, createSessionsFeature());
123
+ }
124
+ if (options.mfa !== undefined)
125
+ out.push(createAuthMfaFeature(options.mfa));
126
+ return out;
127
+ }
@@ -0,0 +1,17 @@
1
+ export type CrashTrackerOptions = {
2
+ readonly maxCrashes: number;
3
+ readonly windowMs: number;
4
+ };
5
+ export type CrashTracker = {
6
+ /** Crash bei `now` registrieren. Returns `true` wenn der Wrapper
7
+ * noch respawnen darf, `false` wenn das Limit überschritten ist.
8
+ *
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. */
12
+ 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. */
15
+ readonly crashCountInWindow: (now: number) => number;
16
+ };
17
+ export declare function createCrashTracker(options: CrashTrackerOptions): CrashTracker;
@@ -0,0 +1,37 @@
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.
7
+ //
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.
12
+ export function createCrashTracker(options) {
13
+ const timestamps = [];
14
+ 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.
18
+ while (timestamps.length > 0) {
19
+ const head = timestamps[0];
20
+ if (head !== undefined && head < cutoff)
21
+ timestamps.shift();
22
+ else
23
+ break;
24
+ }
25
+ };
26
+ return {
27
+ noteCrash: (now) => {
28
+ pruneBefore(now - options.windowMs);
29
+ timestamps.push(now);
30
+ return timestamps.length <= options.maxCrashes;
31
+ },
32
+ crashCountInWindow: (now) => {
33
+ pruneBefore(now - options.windowMs);
34
+ return timestamps.length;
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,195 @@
1
+ /// <reference types="bun-types" preserve="true" />
2
+ import { type AuthRoutesConfig, type ExtraRouteDefinition } from "@cosmicdrift/kumiko-framework/api";
3
+ import { type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
4
+ import { type TestStack, type TestStackOptions } from "@cosmicdrift/kumiko-framework/stack";
5
+ import { type PageHeadResolver, type PageHeadSystemQuery } from "@cosmicdrift/kumiko-headless/apex";
6
+ import { type SystemWireDeps } from "@cosmicdrift/kumiko-server-runtime/extra-routes-deps";
7
+ type BunServer = typeof Bun extends undefined ? unknown : ReturnType<typeof Bun.serve>;
8
+ /** Multi-entry mode for apps that ship several separate bundles (e.g.
9
+ * publicstatus: `admin.<base>` loads the admin UI, otherwise the public
10
+ * page).
11
+ *
12
+ * kumiko-build reads the same entry list from package.json
13
+ * `kumiko.clientEntries` and serves `/client-<name>.js` per HTTP — the
14
+ * dev server and the prod build stay in sync via that shared declaration,
15
+ * not a filename convention. Mutually exclusive with `clientEntry`.
16
+ * Multi-entry apps MUST also set `hostDispatch` — otherwise the server
17
+ * has no way to know which HTML to serve. */
18
+ export type DevClientEntry = {
19
+ /** Logical name. Should match the corresponding package.json
20
+ * `kumiko.clientEntries[].name` so dev and prod produce identical
21
+ * asset URLs (`/client-<name>.js`). */
22
+ readonly name: string;
23
+ /** Absoluter Pfad zur Browser-Entry-Datei. */
24
+ readonly sourceFile: string;
25
+ /** Optional eigenes HTML-Template für diesen Entry. Wenn nicht gesetzt,
26
+ * wird `htmlPath` (das default-Template) für alle Entries genutzt. */
27
+ readonly htmlPath?: string;
28
+ };
29
+ /** Discriminated union, identical in shape to `runProdApp.hostDispatch`, so
30
+ * dev/prod routing mirrors 1:1 — an apex 404 in prod is an apex 404 in dev
31
+ * (with an `/etc/hosts` entry for the affected domain). HTML never carries
32
+ * a schema, in dev or prod — createKumikoApp fetches it itself from the
33
+ * authenticated GET /api/schema. */
34
+ export type DevHostDispatchResult = {
35
+ readonly kind: "html";
36
+ readonly entryName: string;
37
+ /** @deprecated Ignored — HTML never carries the schema; createKumikoApp
38
+ * fetches it from the authenticated GET /api/schema. */
39
+ readonly injectSchema?: boolean;
40
+ } | {
41
+ /** Static HTML: serves a file verbatim, no bundle injection. Mirrors
42
+ * prod's `{ kind: "html", file: ... }` for marketing/apex pages that
43
+ * don't need React. Path relative to the server CWD. */
44
+ readonly kind: "static-html";
45
+ readonly file: string;
46
+ } | {
47
+ readonly kind: "redirect";
48
+ readonly to: string;
49
+ readonly status?: 301 | 302;
50
+ } | {
51
+ readonly kind: "not-found";
52
+ };
53
+ /** Picks an entry by inspecting the incoming request. Wird von
54
+ * Multi-Entry-Apps gesetzt; im Single-Entry-Mode irrelevant. */
55
+ export type DevHostDispatch = (req: Request, deps: {
56
+ readonly systemQuery: PageHeadSystemQuery;
57
+ }) => DevHostDispatchResult | Promise<DevHostDispatchResult>;
58
+ export type CreateKumikoServerOptions = {
59
+ /** Features whose entities, handlers, and screens get wired into the
60
+ * dev stack. Pass every feature the app is supposed to run. */
61
+ readonly features: readonly FeatureDefinition[];
62
+ /** Absolute path to the browser entry module. The dev-server runs
63
+ * `Bun.build` on it and serves the output at `/client.js`. Omit to
64
+ * run a headless API-only dev-stack (rare — every sample has one).
65
+ * Mutually exclusive mit `clientEntries`. */
66
+ readonly clientEntry?: string;
67
+ /** Multi-Entry-Mode: mehrere getrennte Bundles, jeweils unter
68
+ * `/client-<name>.js`. Mutually exclusive mit `clientEntry`. Setze
69
+ * `hostDispatch` mit, sonst bleibt unklar welches Template zurück-
70
+ * geht. */
71
+ readonly clientEntries?: readonly DevClientEntry[];
72
+ /** Multi-Entry-Mode: Routing pro Request. Inspiziert `Host` (oder
73
+ * was auch immer) und liefert eine Discriminated-Union zurück
74
+ * (html → entry-bundle, redirect → 30x, not-found → 404).
75
+ * Symmetric zu `runProdApp.hostDispatch` damit dev/prod-Drift
76
+ * beim Routing unmöglich ist. */
77
+ readonly hostDispatch?: DevHostDispatch;
78
+ /** @internal — ersetzt `Bun.build` für Tests. Default ruft die echte
79
+ * Bun-Toolchain. Tests unter Node injizieren einen Stub damit der
80
+ * Routing-Pfad treibbar bleibt ohne Bun.build aufzurufen.
81
+ * KEIN Public-API-Surface — präfixiert mit `_` damit Konsumenten
82
+ * wissen dass das ein Test-Seam ist. */
83
+ readonly _buildBundle?: (sourceFile: string) => Promise<{
84
+ readonly js: string;
85
+ readonly map: string;
86
+ }>;
87
+ /** Absolute path to the CSS entry (typically styles.css with
88
+ * @import "tailwindcss"). The dev-server builds it with the Tailwind
89
+ * CLI and serves the result at /styles.css; `stylesheetWatch` decides
90
+ * whether the CLI keeps running with `--watch` afterwards.
91
+ *
92
+ * When `undefined` and a client entry is set, the app's own
93
+ * src/styles.css wins, otherwise `@cosmicdrift/kumiko-renderer-web/styles.css`
94
+ * resolves via package exports, so no sample hardcodes a monorepo-relative path.
95
+ *
96
+ * `stylesheet: false` disables the CSS pipeline. */
97
+ readonly stylesheet?: string | false;
98
+ /** `true`: the Tailwind CLI keeps running with `--watch` after the
99
+ * initial build. `false`: CSS is built once at boot, no watcher.
100
+ * When `undefined`, env `KUMIKO_DEV_STYLESHEET_WATCH=0` means `false`,
101
+ * anything else `true`. `defineAppE2eConfig` sets that env: Tailwind v4
102
+ * subscribes to the whole app cwd without a gitignore filter, so every
103
+ * Playwright artifact write under test-results/ would trigger a rebuild. */
104
+ readonly stylesheetWatch?: boolean;
105
+ /** Optional HTML template served at `GET /`. The dev-server injects
106
+ * a `<script type="module" src="/client.js">` and a reload-listener
107
+ * snippet into `</body>` if those aren't already there. Defaults to a
108
+ * minimal empty-body document — enough to boot the client. */
109
+ readonly htmlPath?: string;
110
+ /** Port to listen on. Default 4173. Overridable via `PORT` env. */
111
+ readonly port?: number;
112
+ /** Extra directories to watch for reload triggers. The entry's
113
+ * directory is watched automatically. */
114
+ readonly watchDirs?: readonly string[];
115
+ /** When false, no SIGINT/SIGTERM handlers are installed. Tests set
116
+ * this so repeated `createKumikoServer` calls don't accumulate
117
+ * listeners on the process. Default true (dev-server behaviour). */
118
+ readonly installSignalHandlers?: boolean;
119
+ /** Auth-Route-Config (login, tenants, switch-tenant, logout). Wenn
120
+ * gesetzt wird die Auto-JWT-Mint auf GET / abgeschaltet — der
121
+ * Client ist dann selbst fürs Login zuständig. Zur echten Wirkung
122
+ * brauchen die dazugehörigen Features (user/tenant/auth-email-
123
+ * password) via `features` drin sein. */
124
+ readonly auth?: AuthRoutesConfig;
125
+ /** Extra-AppContext-Keys (z.B. configResolver für config-feature).
126
+ * Wird an setupTestStack weitergereicht. Siehe TestStackOptions
127
+ * für die erlaubten Shapes (object oder factory-function). */
128
+ readonly extraContext?: TestStackOptions["extraContext"];
129
+ /** Anonymous-Access aktivieren — Requests ohne JWT werden als
130
+ * Pseudo-User mit Rolle `anonymous` durchgelassen, sofern der
131
+ * Handler `roles: ["anonymous"]` deklariert. Tenant-Resolution per
132
+ * Header/Cookie/Default; siehe AnonymousAccessConfig. */
133
+ readonly anonymousAccess?: TestStackOptions["anonymousAccess"];
134
+ /** File-Storage-Provider — durchgereicht an setupTestStack. Wenn gesetzt,
135
+ * mountet der Stack die Upload-Routes (POST/GET/DELETE /api/files) +
136
+ * `ctx.files` und legt die file_refs-Tabelle an. Für Demos:
137
+ * `{ storageProvider: createInMemoryFileProvider() }`. */
138
+ readonly files?: TestStackOptions["files"];
139
+ /** Feature-toggle resolver — durchgereicht an setupTestStack. Wenn
140
+ * gesetzt, konsultiert der dispatcher-feature-gate, hook-filter, MSP-
141
+ * filter den callback; absent = alle features always-on. Erforderlich
142
+ * für Tier-Composition (Sprint 8) wo per-Tenant unterschiedliche
143
+ * features aktiv sein sollen. Die typische produktive Implementierung
144
+ * ist `() => globalFeatureToggleRuntime.effectiveFeatures` post-boot
145
+ * (createLateBoundHolder-pattern, weil runtime stack.db braucht). */
146
+ readonly effectiveFeatures?: TestStackOptions["effectiveFeatures"];
147
+ /** Wird nach dem Aufsetzen der Entity-Tabellen aufgerufen. Hook für
148
+ * non-entity-tables (unsafePushTables) und Seeding (admin user, initial
149
+ * tenant, …). Muss idempotent sein — im persistent-DB-Modus läuft
150
+ * es bei jedem Boot. */
151
+ readonly onAfterSetup?: (stack: TestStack) => Promise<void>;
152
+ /** Declarative HTTP-routes outside the /api/write|query|batch pipeline —
153
+ * symmetric to runProdApp.extraRoutes (kumiko-framework#3050). Mounted
154
+ * inside buildServer (setupTestStack), before the Static/HTML-fallback,
155
+ * so an own GET (/feed.xml, /og-image, …) wins over the dev-asset path. */
156
+ readonly extraRoutes?: readonly ExtraRouteDefinition[];
157
+ /** Forwarded to setupTestStack → buildServer's top-level
158
+ * `ServerOptions.trustedProxyHops`. Dev usually runs unproxied, so this
159
+ * is normally left unset (default 0). */
160
+ readonly trustedProxyHops?: number;
161
+ /** Hook for app-wired co-running components that need the system-write
162
+ * dispatcher — runs after buildServer, before onAfterSetup (seeds), with
163
+ * NO `app` (routes are declared via `extraRoutes`, not wired here). */
164
+ readonly wire?: (deps: SystemWireDeps) => void | Promise<void>;
165
+ /** Per-request head-metadata resolver (Open-Graph/title/description) —
166
+ * same option, same `PageHeadResolver` signature, and same shared
167
+ * resolve+inject call (`resolveAndInjectPageHead`) as `runProdApp`'s
168
+ * `resolvePageHead`, so dev and e2e exercise the exact code prod does
169
+ * (kumiko-framework#3026). Consulted for every templated HTML response
170
+ * (the default single-entry shell and a host-dispatched `"html"` entry),
171
+ * right before it's served. On error, `null`, or a timeout, the shell
172
+ * ships unchanged with status 200 — never a 500. `systemQuery` inside
173
+ * the resolver runs as the anonymous role, same as prod. */
174
+ readonly resolvePageHead?: PageHeadResolver;
175
+ };
176
+ export type KumikoServerHandle = {
177
+ /** The fetch handler that routes a Request through the dev-server
178
+ * layer (HTML, /client.js, /_reload, SSE) and falls back to the
179
+ * underlying Kumiko stack. Tests call this directly to exercise
180
+ * the routing without going through real sockets. */
181
+ readonly fetch: (req: Request) => Promise<Response>;
182
+ /** Bun.serve instance. `undefined` when running outside Bun (e.g.
183
+ * in vitest under Node) — the handle still works via `.fetch`. */
184
+ readonly server: BunServer | undefined;
185
+ readonly stack: TestStack;
186
+ /** Stops the server and tears down the stack (DB + redis). */
187
+ readonly stop: () => Promise<void>;
188
+ };
189
+ export declare const STYLESHEET_WATCH_ENV = "KUMIKO_DEV_STYLESHEET_WATCH";
190
+ export declare const PROD_BUNDLES_ENV = "KUMIKO_DEV_PROD_BUNDLES";
191
+ export declare function classifyChange(filename: string): "restart" | "hot-reload" | "ignore";
192
+ export declare function resolveStylesheet(options: CreateKumikoServerOptions): string | undefined;
193
+ export declare function resolveStylesheetWatch(options: Pick<CreateKumikoServerOptions, "stylesheetWatch">, env?: Readonly<Record<string, string | undefined>>): boolean;
194
+ export declare function createKumikoServer(options: CreateKumikoServerOptions): Promise<KumikoServerHandle>;
195
+ export {};