@cosmicdrift/kumiko-dev-server 0.328.1 → 0.330.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/README.md +17 -0
- package/bin/kumiko-build.ts +3 -2
- package/bin/kumiko-dev.ts +1 -1
- package/bin/kumiko-init-deploy.ts +1 -1
- package/bin/kumiko-schema-check.ts +4 -1
- package/dist/build-server-bundle.d.ts +31 -0
- package/dist/build-server-bundle.js +282 -0
- package/dist/build.d.ts +2 -0
- package/dist/build.js +5 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +9 -0
- package/dist/codegen/index.d.ts +4 -0
- package/dist/codegen/index.js +4 -0
- package/dist/codegen/render.d.ts +55 -0
- package/dist/codegen/render.js +273 -0
- package/dist/codegen/run-codegen.d.ts +20 -0
- package/dist/codegen/run-codegen.js +219 -0
- package/dist/codegen/scan-events.d.ts +76 -0
- package/dist/codegen/scan-events.js +447 -0
- package/dist/codegen/watch.d.ts +33 -0
- package/dist/codegen/watch.js +112 -0
- package/dist/compose-stacks.d.ts +60 -0
- package/dist/compose-stacks.js +127 -0
- package/dist/crash-tracker.d.ts +17 -0
- package/dist/crash-tracker.js +37 -0
- package/dist/create-kumiko-server.d.ts +195 -0
- package/dist/create-kumiko-server.js +1040 -0
- package/dist/env-schema.d.ts +15 -0
- package/dist/env-schema.js +60 -0
- package/dist/few-shot-corpus.d.ts +72 -0
- package/dist/few-shot-corpus.js +262 -0
- package/dist/framework-version.d.ts +1 -0
- package/dist/framework-version.js +31 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +33 -0
- package/dist/init-deploy-cli.d.ts +10 -0
- package/dist/init-deploy-cli.js +111 -0
- package/dist/kebab.d.ts +1 -0
- package/{src/kebab.ts → dist/kebab.js} +2 -3
- package/dist/run-dev-app.d.ts +174 -0
- package/dist/run-dev-app.js +403 -0
- package/dist/scaffold-app-feature.d.ts +16 -0
- package/dist/scaffold-app-feature.js +240 -0
- package/dist/scaffold-app.d.ts +45 -0
- package/dist/scaffold-app.js +852 -0
- package/dist/scaffold-demo-tasks.d.ts +8 -0
- package/{src/scaffold-demo-tasks.ts → dist/scaffold-demo-tasks.js} +69 -98
- package/dist/scaffold-deploy.d.ts +92 -0
- package/dist/scaffold-deploy.js +254 -0
- package/dist/scaffold-feature.d.ts +26 -0
- package/dist/scaffold-feature.js +163 -0
- package/dist/schema-apply.d.ts +11 -0
- package/dist/schema-apply.js +95 -0
- package/dist/schema-check-core.d.ts +2 -0
- package/{src/schema-check-core.ts → dist/schema-check-core.js} +8 -10
- package/dist/schema-tables-auth-mode.d.ts +2 -0
- package/{src/schema-tables-auth-mode.ts → dist/schema-tables-auth-mode.js} +2 -10
- package/dist/schema-tables-minimal.d.ts +2 -0
- package/{src/schema-tables-minimal.ts → dist/schema-tables-minimal.js} +2 -11
- package/dist/setup-test-stack-from-features.d.ts +11 -0
- package/dist/setup-test-stack-from-features.js +42 -0
- package/dist/welcome-banner.d.ts +15 -0
- package/dist/welcome-banner.js +27 -0
- package/package.json +71 -24
- package/src/changes.json +6 -0
- package/src/__tests__/build-prod-bundle.integration.test.ts +0 -282
- package/src/__tests__/build-server-bundle.test.ts +0 -228
- package/src/__tests__/classify-change.test.ts +0 -87
- package/src/__tests__/compose-stacks.test.ts +0 -273
- package/src/__tests__/config-resolver-default.integration.test.ts +0 -165
- package/src/__tests__/crash-tracker.test.ts +0 -89
- package/src/__tests__/create-kumiko-server-errors.test.ts +0 -54
- package/src/__tests__/create-kumiko-server-options.test.ts +0 -33
- package/src/__tests__/create-kumiko-server.integration.test.ts +0 -1007
- package/src/__tests__/discover-format.test.ts +0 -32
- package/src/__tests__/env-schema.integration.test.ts +0 -248
- package/src/__tests__/env-schema.test.ts +0 -132
- package/src/__tests__/few-shot-corpus.test.ts +0 -316
- package/src/__tests__/framework-version.test.ts +0 -45
- package/src/__tests__/init-deploy-cli.test.ts +0 -113
- package/src/__tests__/kumiko-schema-check.test.ts +0 -56
- package/src/__tests__/merge-extra-context.test.ts +0 -67
- package/src/__tests__/resolve-stylesheet.test.ts +0 -132
- package/src/__tests__/run-dev-app-boot-validation.test.ts +0 -38
- package/src/__tests__/run-dev-app.integration.test.ts +0 -290
- package/src/__tests__/runtime-classification.test.ts +0 -26
- package/src/__tests__/saas-identity-wire.integration.test.ts +0 -427
- package/src/__tests__/scaffold-app-feature.test.ts +0 -216
- package/src/__tests__/scaffold-app.test.ts +0 -630
- package/src/__tests__/scaffold-demo-tasks.test.ts +0 -47
- package/src/__tests__/scaffold-deploy.test.ts +0 -464
- package/src/__tests__/scaffold-dev-cycle.integration.test.ts +0 -109
- package/src/__tests__/scaffold-feature.test.ts +0 -143
- package/src/__tests__/schema-apply.integration.test.ts +0 -191
- package/src/__tests__/setup-test-stack-from-features.integration.test.ts +0 -30
- package/src/__tests__/setup-test-stack-job-run-logger.integration.test.ts +0 -72
- package/src/__tests__/walkthrough.integration.test.ts +0 -140
- package/src/__tests__/welcome-banner.test.ts +0 -42
- package/src/build-server-bundle.ts +0 -346
- package/src/build.ts +0 -28
- package/src/codegen/__tests__/render-codegen.test.ts +0 -100
- package/src/codegen/__tests__/run-codegen.test.ts +0 -665
- package/src/codegen/__tests__/strict-mode-diagnostics.test.ts +0 -347
- package/src/codegen/__tests__/watch.test.ts +0 -222
- package/src/codegen/index.ts +0 -19
- package/src/codegen/render.ts +0 -305
- package/src/codegen/run-codegen.ts +0 -278
- package/src/codegen/scan-events.ts +0 -565
- package/src/codegen/watch.ts +0 -140
- package/src/compose-stacks.ts +0 -193
- package/src/crash-tracker.ts +0 -56
- package/src/create-kumiko-server.ts +0 -1397
- package/src/env-schema.ts +0 -72
- package/src/few-shot-corpus.ts +0 -370
- package/src/framework-version.ts +0 -39
- package/src/index.ts +0 -105
- package/src/init-deploy-cli.ts +0 -148
- package/src/run-dev-app.ts +0 -694
- package/src/scaffold-app-feature.ts +0 -283
- package/src/scaffold-app.ts +0 -1033
- package/src/scaffold-deploy.ts +0 -393
- package/src/scaffold-feature.ts +0 -226
- package/src/schema-apply.ts +0 -123
- package/src/setup-test-stack-from-features.ts +0 -80
- 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 {};
|