@cosmicdrift/kumiko-dev-server 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/kumiko-schema-check.ts +7 -0
- package/package.json +12 -7
- package/src/__tests__/build-prod-bundle.integration.test.ts +1 -1
- package/src/__tests__/build-server-bundle.test.ts +62 -0
- package/src/__tests__/compose-stacks.test.ts +271 -0
- package/src/__tests__/create-kumiko-server-errors.test.ts +54 -0
- package/src/__tests__/create-kumiko-server-options.test.ts +33 -0
- package/src/__tests__/create-kumiko-server.integration.test.ts +128 -1
- package/src/__tests__/discover-format.test.ts +1 -1
- package/src/__tests__/env-schema.integration.test.ts +1 -1
- package/src/__tests__/few-shot-corpus.test.ts +16 -11
- package/src/__tests__/merge-extra-context.test.ts +56 -0
- package/src/__tests__/resolve-stylesheet.test.ts +16 -0
- package/src/__tests__/saas-identity-wire.integration.test.ts +430 -0
- package/src/__tests__/scaffold-app-feature.test.ts +59 -6
- package/src/__tests__/scaffold-app.test.ts +34 -2
- package/src/__tests__/schema-apply.integration.test.ts +5 -2
- package/src/__tests__/setup-test-stack-from-features.integration.test.ts +30 -0
- package/src/__tests__/walkthrough.integration.test.ts +22 -6
- package/src/build-server-bundle.ts +33 -15
- package/src/build.ts +1 -2
- package/src/codegen/__tests__/render-codegen.test.ts +2 -1
- package/src/codegen/__tests__/run-codegen.test.ts +2 -2
- package/src/codegen/__tests__/strict-mode-diagnostics.test.ts +6 -1
- package/src/codegen/__tests__/watch.test.ts +1 -2
- package/src/codegen/render.ts +6 -1
- package/src/codegen/scan-events.ts +7 -5
- package/src/codegen/watch.ts +7 -4
- package/src/compose-stacks.ts +184 -0
- package/src/create-kumiko-server.ts +28 -5
- package/src/few-shot-corpus.ts +4 -3
- package/src/index.ts +30 -12
- package/src/run-dev-app.ts +195 -61
- package/src/scaffold-app-feature.ts +118 -15
- package/src/scaffold-app.ts +151 -79
- package/src/scaffold-demo-tasks.ts +233 -0
- package/src/schema-apply.ts +15 -2
- package/src/schema-check-core.ts +1 -1
- package/src/setup-test-stack-from-features.ts +61 -0
- package/src/welcome-banner.ts +1 -4
- package/src/__tests__/boot-extra-context.test.ts +0 -140
- package/src/__tests__/build-prod-bundle.test.ts +0 -311
- package/src/__tests__/cache-headers.test.ts +0 -83
- package/src/__tests__/compose-features-wiring.integration.test.ts +0 -382
- package/src/__tests__/compose-features.test.ts +0 -129
- package/src/__tests__/config-seed-boot.integration.test.ts +0 -158
- package/src/__tests__/inject-schema.test.ts +0 -62
- package/src/__tests__/renderer-web-css-relocation.integration.test.ts +0 -85
- package/src/__tests__/renderer-web-shell-sentinel.test.ts +0 -35
- package/src/__tests__/require-env.test.ts +0 -29
- package/src/__tests__/resolve-auth-mail.test.ts +0 -69
- package/src/__tests__/resolve-tailwind-cli.test.ts +0 -81
- package/src/__tests__/run-prod-app-env-source.test.ts +0 -157
- package/src/__tests__/run-prod-app-spec.test.ts +0 -57
- package/src/__tests__/run-prod-app.integration.test.ts +0 -840
- package/src/__tests__/session-wiring.test.ts +0 -51
- package/src/__tests__/try-hono-first.test.ts +0 -63
- package/src/boot/apply-boot-seeds.ts +0 -18
- package/src/build-prod-bundle.ts +0 -697
- package/src/compose-features.ts +0 -145
- package/src/extra-routes-deps.ts +0 -47
- package/src/inject-schema.ts +0 -24
- package/src/resolve-tailwind-cli.ts +0 -45
- package/src/run-prod-app.ts +0 -1492
- package/src/session-wiring.ts +0 -29
- package/src/try-hono-first.ts +0 -46
package/src/compose-features.ts
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// composeFeatures — single source of truth für die Feature-Liste die
|
|
2
|
-
// Boot UND Schema-Generator sehen.
|
|
3
|
-
//
|
|
4
|
-
// Sowohl runDevApp als auch runProdApp mischen im auth-mode dieselben
|
|
5
|
-
// vier Bundled-Features dazu (config + user + tenant + auth-email-pw).
|
|
6
|
-
// Damit der drizzle-Schema-Generator pro App genau dieselbe Feature-
|
|
7
|
-
// Liste sieht wie die Runtime, leben die Komposition hier — beide
|
|
8
|
-
// Bootstrap-Wrapper UND der per-app drizzle/generate.ts rufen sie auf.
|
|
9
|
-
//
|
|
10
|
-
// Reihenfolge: Infrastruktur-Features (config/user/tenant) zuerst, dann
|
|
11
|
-
// auth-email-password, dann die App-Features. Spätere Features dürfen
|
|
12
|
-
// auf Frühere referenzieren (z.B. authClaims-Hooks an user/tenant).
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
type AuthEmailPasswordOptions,
|
|
16
|
-
type AuthMailLocale,
|
|
17
|
-
createAuthEmailPasswordFeature,
|
|
18
|
-
type EmailVerificationOptions,
|
|
19
|
-
type InviteOptions,
|
|
20
|
-
type PasswordResetOptions,
|
|
21
|
-
type SignupOptions,
|
|
22
|
-
} from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
|
|
23
|
-
import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";
|
|
24
|
-
import { createTenantFeature } from "@cosmicdrift/kumiko-bundled-features/tenant";
|
|
25
|
-
import { createUserFeature } from "@cosmicdrift/kumiko-bundled-features/user";
|
|
26
|
-
import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
27
|
-
|
|
28
|
-
export type ComposeFeaturesOptions = {
|
|
29
|
-
/** When true, prepends config + user + tenant + auth-email-password
|
|
30
|
-
* before the app features. Mirror of "auth-mode" in run{Dev,Prod}App. */
|
|
31
|
-
readonly includeBundled: boolean;
|
|
32
|
-
/** Optional auth-feature-options durchgereicht an
|
|
33
|
-
* createAuthEmailPasswordFeature. Wenn passwordReset / emailVerification
|
|
34
|
-
* hier gesetzt sind, registriert das Feature die request-/confirm-
|
|
35
|
-
* Handler — sonst NICHT (500 wenn die routes via auth-routes.ts
|
|
36
|
-
* gemounted sind aber kein Handler dispatcht). Hand-in-hand mit dem
|
|
37
|
-
* passwordReset-Block in RunProdAppAuthOptions / RunDevAppAuthOptions. */
|
|
38
|
-
readonly authOptions?: AuthEmailPasswordOptions;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export function composeFeatures(
|
|
42
|
-
appFeatures: readonly FeatureDefinition[],
|
|
43
|
-
options: ComposeFeaturesOptions,
|
|
44
|
-
): FeatureDefinition[] {
|
|
45
|
-
if (!options.includeBundled) return [...appFeatures];
|
|
46
|
-
|
|
47
|
-
// Bundled foundation goes first so its instances carry the runDevApp /
|
|
48
|
-
// runProdApp `authOptions` (passwordReset wiring etc.). App-features that
|
|
49
|
-
// ALSO declare one of these names — e.g. the create-kumiko-app picker
|
|
50
|
-
// hands back `createAuthEmailPasswordFeature()` because the user ticked
|
|
51
|
-
// it — would otherwise crash createRegistry with "Duplicate feature".
|
|
52
|
-
// Drop the app-side duplicates and warn so the user can clean run-config.
|
|
53
|
-
const bundled = [
|
|
54
|
-
createConfigFeature(),
|
|
55
|
-
createUserFeature(),
|
|
56
|
-
createTenantFeature(),
|
|
57
|
-
createAuthEmailPasswordFeature(options.authOptions ?? {}),
|
|
58
|
-
];
|
|
59
|
-
const bundledNames = new Set(bundled.map((f) => f.name));
|
|
60
|
-
const filteredApp: FeatureDefinition[] = [];
|
|
61
|
-
for (const f of appFeatures) {
|
|
62
|
-
if (bundledNames.has(f.name)) {
|
|
63
|
-
// biome-ignore lint/suspicious/noConsole: boot-time UX warning
|
|
64
|
-
console.warn(
|
|
65
|
-
`[composeFeatures] "${f.name}" already auto-mounted via includeBundled — dropping the explicit copy from APP_FEATURES. Remove it from run-config.ts to silence this warning.`,
|
|
66
|
-
);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
filteredApp.push(f);
|
|
70
|
-
}
|
|
71
|
-
return [...bundled, ...filteredApp];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Shape eines beliebigen run{Prod,Dev}App-Auth-Blocks der eine
|
|
75
|
-
* PasswordReset/EmailVerification-Konfiguration tragen kann. Die
|
|
76
|
-
* Wrapper-API (PasswordResetSetup) extends die Feature-API
|
|
77
|
-
* (PasswordResetOptions), darum reicht ein structural-typed
|
|
78
|
-
* Lookup auf den auth-only-Subset. Erlaubt buildComposeAuthOptions
|
|
79
|
-
* mit RunProd- UND RunDev-AuthOptions zu callen ohne den Helper
|
|
80
|
-
* doppelt zu bauen. */
|
|
81
|
-
export type AuthOptionsCarrier = {
|
|
82
|
-
readonly passwordReset?: PasswordResetOptions;
|
|
83
|
-
readonly emailVerification?: EmailVerificationOptions;
|
|
84
|
-
readonly signup?: SignupOptions;
|
|
85
|
-
readonly invite?: InviteOptions;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/** Baut den authOptions-Block für composeFeatures aus einem
|
|
89
|
-
* Wrapper-Auth-Block. Reicht NUR die feature-side-Felder
|
|
90
|
-
* (hmacSecret, tokenTtlMinutes, mode) durch — die mail-side
|
|
91
|
-
* (sendResetEmail/appResetUrl) gehört in die auth-routes-config
|
|
92
|
-
* und wird vom Wrapper separat verdrahtet.
|
|
93
|
-
*
|
|
94
|
-
* Returnt undefined wenn weder passwordReset noch emailVerification
|
|
95
|
-
* gesetzt sind (composeFeatures default-deny: KEINE handler in der
|
|
96
|
-
* Registry registriert, /api/auth/request-password-reset etc. bleiben
|
|
97
|
-
* 401/404). */
|
|
98
|
-
type MailFlowFields = {
|
|
99
|
-
readonly appUrl: string;
|
|
100
|
-
readonly tokenTtlMinutes?: number;
|
|
101
|
-
readonly appName?: string;
|
|
102
|
-
readonly locale?: AuthMailLocale;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// The magic-link mail fields shared by all four flows. Conditional spreads omit
|
|
106
|
-
// undefined keys (exactOptionalPropertyTypes) and avoid the property-write that
|
|
107
|
-
// trips noPropertyAccessFromIndexSignature on the type-aliased option shapes.
|
|
108
|
-
// reset/verify layer hmacSecret (+ mode) on top.
|
|
109
|
-
function pickMailFields(src: MailFlowFields): MailFlowFields {
|
|
110
|
-
return {
|
|
111
|
-
appUrl: src.appUrl,
|
|
112
|
-
...(src.tokenTtlMinutes !== undefined && { tokenTtlMinutes: src.tokenTtlMinutes }),
|
|
113
|
-
...(src.appName !== undefined && { appName: src.appName }),
|
|
114
|
-
...(src.locale !== undefined && { locale: src.locale }),
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function buildComposeAuthOptions(
|
|
119
|
-
auth: AuthOptionsCarrier | undefined,
|
|
120
|
-
): AuthEmailPasswordOptions | undefined {
|
|
121
|
-
if (!auth) return undefined;
|
|
122
|
-
const opts: { -readonly [K in keyof AuthEmailPasswordOptions]: AuthEmailPasswordOptions[K] } = {};
|
|
123
|
-
if (auth.passwordReset) {
|
|
124
|
-
opts.passwordReset = {
|
|
125
|
-
hmacSecret: auth.passwordReset.hmacSecret,
|
|
126
|
-
...pickMailFields(auth.passwordReset),
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
if (auth.emailVerification) {
|
|
130
|
-
opts.emailVerification = {
|
|
131
|
-
hmacSecret: auth.emailVerification.hmacSecret,
|
|
132
|
-
...pickMailFields(auth.emailVerification),
|
|
133
|
-
...(auth.emailVerification.mode !== undefined && { mode: auth.emailVerification.mode }),
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
if (auth.signup) {
|
|
137
|
-
opts.signup = pickMailFields(auth.signup);
|
|
138
|
-
}
|
|
139
|
-
if (auth.invite) {
|
|
140
|
-
opts.invite = pickMailFields(auth.invite);
|
|
141
|
-
}
|
|
142
|
-
return opts.passwordReset || opts.emailVerification || opts.signup || opts.invite
|
|
143
|
-
? opts
|
|
144
|
-
: undefined;
|
|
145
|
-
}
|
package/src/extra-routes-deps.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { ROLES } from "@cosmicdrift/kumiko-framework/auth";
|
|
2
|
-
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import {
|
|
4
|
-
createSystemUser,
|
|
5
|
-
type Registry,
|
|
6
|
-
type SessionUser,
|
|
7
|
-
type TenantId,
|
|
8
|
-
type WriteResult,
|
|
9
|
-
} from "@cosmicdrift/kumiko-framework/engine";
|
|
10
|
-
import type Redis from "ioredis";
|
|
11
|
-
|
|
12
|
-
/** Deps für `extraRoutes` — geteilt zwischen runProdApp (prod) und
|
|
13
|
-
* createKumikoServer (dev), damit die beiden Pfade nicht driften.
|
|
14
|
-
* Naming: `deps` statt `ctx` weil im Framework `ctx` der HandlerContext
|
|
15
|
-
* mit user/tenant/registry ist — hier ist der Scope absichtlich kleiner
|
|
16
|
-
* (Routes laufen außerhalb der Auth/Tenant-Pipeline). */
|
|
17
|
-
export type ExtraRoutesSystemDeps = {
|
|
18
|
-
readonly db: DbConnection;
|
|
19
|
-
readonly redis: Redis;
|
|
20
|
-
/** Feature-registry — z.B. für Plugin-Lookups via
|
|
21
|
-
* `registry.getExtensionUsages("subscriptionProvider")`. */
|
|
22
|
-
readonly registry: Registry;
|
|
23
|
-
/** Schreibt durch den /api/*-Command-Dispatcher (gleiche Idempotency/
|
|
24
|
-
* Job-Hooks) — aber als auto-konstruierter SystemAdmin des Ziel-
|
|
25
|
-
* Tenants, OHNE Access-Check der Route. Privilege-Scope: SystemAdmin
|
|
26
|
-
* ist die höchste nicht-tenant-scoped Rolle — der Call erreicht JEDEN
|
|
27
|
-
* SystemAdmin-gegateten Handler auf jedem Tenant; das Rollen-Set ist
|
|
28
|
-
* nicht konfigurierbar. Nur für Pfade, die ihre Authentizität selbst
|
|
29
|
-
* beweisen (Provider-Webhook-Signaturen,
|
|
30
|
-
* createSubscriptionWebhookHandler et al.). */
|
|
31
|
-
readonly dispatchSystemWrite: (args: {
|
|
32
|
-
readonly handlerQn: string;
|
|
33
|
-
readonly payload: unknown;
|
|
34
|
-
readonly tenantId: TenantId;
|
|
35
|
-
}) => Promise<WriteResult>;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
type SystemWriteDispatcher = {
|
|
39
|
-
readonly write: (handlerQn: string, payload: unknown, user: SessionUser) => Promise<WriteResult>;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export function makeDispatchSystemWrite(
|
|
43
|
-
dispatcher: SystemWriteDispatcher,
|
|
44
|
-
): ExtraRoutesSystemDeps["dispatchSystemWrite"] {
|
|
45
|
-
return ({ handlerQn, payload, tenantId }) =>
|
|
46
|
-
dispatcher.write(handlerQn, payload, createSystemUser(tenantId, [ROLES.SystemAdmin]));
|
|
47
|
-
}
|
package/src/inject-schema.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// Injiziert das Server-aufgelöste AppSchema in das HTML-Template damit
|
|
2
|
-
// createKumikoApp() es synchron unter `window.__KUMIKO_SCHEMA__` vorfindet.
|
|
3
|
-
// JSON ist valides JS — direkt eingebettet, der Browser parsed das
|
|
4
|
-
// Object-Literal nativ.
|
|
5
|
-
//
|
|
6
|
-
// Geteilt zwischen dev-server (Schema kommt frisch aus dem laufenden
|
|
7
|
-
// Prozess) und prod-server (Schema wird beim Boot einmal berechnet und
|
|
8
|
-
// in die statisch ausgelieferte index.html injiziert). Beide Pfade
|
|
9
|
-
// nutzen dieselbe Tag-Form damit `createKumikoApp` den Lookup nicht je
|
|
10
|
-
// nach Kontext anders machen muss.
|
|
11
|
-
//
|
|
12
|
-
// Idempotenz: wenn das HTML schon einen __KUMIKO_SCHEMA__-Marker hat,
|
|
13
|
-
// wird nicht doppelt injected — verhindert dass repeated index.html-
|
|
14
|
-
// Reads (prod) oder bereits-vorbereitete templates (custom CI-builds)
|
|
15
|
-
// stacking-Tags produzieren.
|
|
16
|
-
|
|
17
|
-
export function injectSchema(html: string, schemaJson: string): string {
|
|
18
|
-
if (html.includes("__KUMIKO_SCHEMA__")) return html;
|
|
19
|
-
const tag = `<script>window.__KUMIKO_SCHEMA__=${schemaJson};</script>`;
|
|
20
|
-
if (html.includes('<script src="/client.js"')) {
|
|
21
|
-
return html.replace('<script src="/client.js"', `${tag}<script src="/client.js"`);
|
|
22
|
-
}
|
|
23
|
-
return html.includes("</body>") ? html.replace("</body>", `${tag}</body>`) : html + tag;
|
|
24
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// Resolved den lokal installierten @tailwindcss/cli-Bin auf seinen
|
|
2
|
-
// absoluten Dateipfad. Wir vermeiden `bunx`, weil das auch bei lokal
|
|
3
|
-
// installiertem Package noch das Registry-Manifest fragt und ohne Netz
|
|
4
|
-
// mit `FailedToOpenSocket` stirbt.
|
|
5
|
-
//
|
|
6
|
-
// Ausgelagert aus create-kumiko-server.ts, damit unter vitest/Node
|
|
7
|
-
// testbar (Bun-Branch + Resolve-Fail-Branch) ohne den Server zu
|
|
8
|
-
// booten.
|
|
9
|
-
|
|
10
|
-
import { dirname, resolve } from "node:path";
|
|
11
|
-
|
|
12
|
-
type BunResolver = { resolveSync: (id: string, from: string) => string };
|
|
13
|
-
|
|
14
|
-
export type ResolveTailwindCliDeps = {
|
|
15
|
-
readonly bun?: BunResolver;
|
|
16
|
-
readonly cwd: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export function resolveTailwindCli(deps: ResolveTailwindCliDeps): string | undefined {
|
|
20
|
-
if (deps.bun === undefined) return undefined;
|
|
21
|
-
try {
|
|
22
|
-
const pkgJsonPath = deps.bun.resolveSync("@tailwindcss/cli/package.json", deps.cwd);
|
|
23
|
-
// bin: { tailwindcss: "./dist/index.mjs" } → absoluter Pfad
|
|
24
|
-
return resolve(pkgJsonPath, "..", "dist", "index.mjs");
|
|
25
|
-
} catch {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Tailwind v4 resolves `@import "tailwindcss"` relative to the entry
|
|
31
|
-
* CSS file. Skip renderer-web fallback (or fail build) when peer deps
|
|
32
|
-
* aren't resolvable from that directory — e.g. Bun cache copies of
|
|
33
|
-
* `@cosmicdrift/kumiko-renderer-web` outside a hoisted node_modules tree. */
|
|
34
|
-
export function canResolveTailwindStylesheet(
|
|
35
|
-
entryCss: string,
|
|
36
|
-
deps: ResolveTailwindCliDeps,
|
|
37
|
-
): boolean {
|
|
38
|
-
if (deps.bun === undefined) return false;
|
|
39
|
-
try {
|
|
40
|
-
deps.bun.resolveSync("tailwindcss", dirname(entryCss));
|
|
41
|
-
return true;
|
|
42
|
-
} catch {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|