@cosmicdrift/kumiko-dev-server 0.369.0 → 0.370.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.
Files changed (43) hide show
  1. package/dist/build.d.ts +1 -1
  2. package/dist/cli.d.ts +3 -3
  3. package/dist/compose-stacks.d.ts +57 -3
  4. package/dist/compose-stacks.js +88 -9
  5. package/dist/host-dispatch-adapter.d.ts +7 -0
  6. package/dist/host-dispatch-adapter.js +31 -0
  7. package/dist/index.d.ts +21 -20
  8. package/dist/index.js +2 -1
  9. package/dist/init-deploy-cli.js +6 -0
  10. package/dist/managed-files/app-scripts.d.ts +13 -0
  11. package/dist/managed-files/app-scripts.js +40 -0
  12. package/dist/managed-files/check.d.ts +34 -0
  13. package/dist/managed-files/check.js +109 -0
  14. package/dist/managed-files/index.d.ts +8 -0
  15. package/dist/managed-files/index.js +8 -0
  16. package/dist/managed-files/jsonc.d.ts +1 -0
  17. package/dist/managed-files/jsonc.js +12 -0
  18. package/dist/managed-files/line-diff.d.ts +2 -0
  19. package/dist/managed-files/line-diff.js +60 -0
  20. package/dist/managed-files/marker.d.ts +17 -0
  21. package/dist/managed-files/marker.js +49 -0
  22. package/dist/managed-files/port-registry.d.ts +17 -0
  23. package/dist/managed-files/port-registry.js +31 -0
  24. package/dist/managed-files/registry.d.ts +51 -0
  25. package/dist/managed-files/registry.js +115 -0
  26. package/dist/managed-files/static-app-config.d.ts +16 -0
  27. package/dist/managed-files/static-app-config.js +99 -0
  28. package/dist/managed-files/sync.d.ts +19 -0
  29. package/dist/managed-files/sync.js +101 -0
  30. package/dist/run-dev-app.d.ts +1 -1
  31. package/dist/run-dev-app.js +7 -1
  32. package/dist/scaffold-app.d.ts +3 -3
  33. package/dist/scaffold-app.js +125 -199
  34. package/dist/scaffold-deploy.d.ts +9 -0
  35. package/dist/scaffold-deploy.js +34 -6
  36. package/dist/scaffold-feature.js +1 -0
  37. package/package.json +18 -9
  38. package/src/changes.json +19 -0
  39. package/templates/managed/cache-cleanup.yml.template +30 -0
  40. package/templates/managed/ci.yml.template +65 -0
  41. package/templates/managed/husky-pre-push.template +36 -0
  42. package/templates/managed/kumiko-schema.ts.template +12 -0
  43. package/templates/managed/release.yml.template +55 -0
package/dist/build.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { ASSETS_DIR, type BuildManifest, type BuildProdBundleOptions, type BuildResult, buildProdBundle, type ClientEntry, type ClientEntryDeclaration, discoverHtmlTemplate, formatBuildResult, injectAssetTags, readClientEntriesConfig, resolveClientEntries, } from "@cosmicdrift/kumiko-server-runtime/build-prod-bundle";
2
- export { type BuildServerBundleEntry, type BuildServerBundleOptions, type BuildServerBundleResult, buildServerBundle, discoverServerEntry, formatServerBuildResult, readExtraRuntimeExternals, } from "./build-server-bundle.js";
2
+ export { type BuildServerBundleEntry, type BuildServerBundleOptions, type BuildServerBundleResult, buildServerBundle, discoverServerEntry, formatServerBuildResult, readExtraRuntimeExternals, } from "./build-server-bundle.ts";
package/dist/cli.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { buildProdBundle, buildServerBundle, discoverServerEntry, formatBuildResult, formatServerBuildResult, readClientEntriesConfig, readExtraRuntimeExternals, resolveClientEntries, } from "./build.js";
2
- export { createCrashTracker } from "./crash-tracker.js";
3
- export { runInitDeployCli } from "./init-deploy-cli.js";
1
+ export { buildProdBundle, buildServerBundle, discoverServerEntry, formatBuildResult, formatServerBuildResult, readClientEntriesConfig, readExtraRuntimeExternals, resolveClientEntries, } from "./build.ts";
2
+ export { createCrashTracker } from "./crash-tracker.ts";
3
+ export { runInitDeployCli } from "./init-deploy-cli.ts";
@@ -1,13 +1,20 @@
1
+ import { type AccountSecurityFeatureOptions } from "@cosmicdrift/kumiko-bundled-features/account-security";
1
2
  import { type AuthMfaFeatureOptions } from "@cosmicdrift/kumiko-bundled-features/auth-mfa";
3
+ import { type BillingFoundationOptions } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
4
+ import { type CreateCapOverviewOptions } from "@cosmicdrift/kumiko-bundled-features/cap-overview";
2
5
  import { type LegalPagesOptions } from "@cosmicdrift/kumiko-bundled-features/legal-pages";
6
+ import { type SubscriptionMollieOptions } from "@cosmicdrift/kumiko-bundled-features/subscription-mollie";
7
+ import { type CreateTierEngineOptions } from "@cosmicdrift/kumiko-bundled-features/tier-engine";
3
8
  import { type UserDataRightsOptions } from "@cosmicdrift/kumiko-bundled-features/user-data-rights";
9
+ import { type WaitlistOptions } from "@cosmicdrift/kumiko-bundled-features/waitlist";
4
10
  import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
5
11
  import { type MailBrandSuffixResolver, type MailFooterResolver } from "@cosmicdrift/kumiko-renderer-mail-html";
6
12
  export type FileProviderKind = "inmemory" | "s3" | "s3-env";
7
13
  export type MailTransportKind = "inmemory" | "smtp";
8
14
  export type GdprStackOrder = "retention-first" | "compliance-first";
9
15
  export type FileStackOptions = {
10
- readonly providers: readonly FileProviderKind[];
16
+ /** Without providers the stack needs runProdApp's `fileStorage` option. */
17
+ readonly providers?: readonly FileProviderKind[];
11
18
  readonly includeFilesFeature?: boolean;
12
19
  };
13
20
  export type MailStackOptions = {
@@ -20,6 +27,11 @@ export type GdprStackOptions = {
20
27
  readonly order?: GdprStackOrder;
21
28
  readonly sessions?: boolean;
22
29
  readonly tenantLifecycle?: boolean;
30
+ /** Adds sessions, user-data-rights and user-profile in the order the
31
+ * self-service privacy center needs; user-data-rights needs `mail`. */
32
+ readonly selfService?: {
33
+ readonly userDataRights?: UserDataRightsOptions;
34
+ };
23
35
  /** Optional tokenVerifier provider(s) (e.g. PAT) when `sessions` is on.
24
36
  * Session-only stacks need none — sessions registers sessionStore, which
25
37
  * satisfies auth-foundation's boot check (#1570). Pass providers when the
@@ -56,12 +68,54 @@ export type RendererStackOptions = {
56
68
  readonly mailBrandSuffix?: MailBrandSuffixResolver;
57
69
  };
58
70
  export declare function composeRendererStack(options?: RendererStackOptions): FeatureDefinition[];
71
+ /** Tenant mail transports reach users through channel-email, so the stack needs `mail`. */
59
72
  export declare function composeMailStack(options: MailStackOptions): FeatureDefinition[];
60
- export declare function composeFileStack(options: FileStackOptions): FeatureDefinition[];
61
- /** Retention + compliance (+ optional lifecycle/sessions). UDR/files mount separately — order varies per app. */
73
+ export declare function composeFileStack(options?: FileStackOptions): FeatureDefinition[];
74
+ /** Retention + compliance (+ optional lifecycle/sessions/self-service). Needs an
75
+ * explicit KMS decision (`kmsMode`, or the low-level `kms` / `allowPlaintextPii`).
76
+ * `selfService` mounts user-profile, which requires user-data-rights-defaults and
77
+ * auth-email-password: composeUserDataRightsStack mounts the defaults, the login comes from the `auth` option. */
62
78
  export declare function composeGdprStack(options?: GdprStackOptions): FeatureDefinition[];
63
79
  export declare function composeUserDataRightsStack(options?: UserDataRightsStackOptions): FeatureDefinition[];
64
80
  export declare function composeOpsStack(options?: OpsStackOptions): FeatureDefinition[];
65
81
  /** Identity opt-ins: sessions by default; auth-mfa when `mfa` options given.
66
82
  * Do not also pass `sessions: true` to composeGdprStack — duplicate feature. */
67
83
  export declare function composeIdentityStack(options?: IdentityStackOptions): FeatureDefinition[];
84
+ export type AuthStackOptions = {
85
+ readonly sessions?: boolean;
86
+ readonly mfa?: AuthMfaFeatureOptions;
87
+ /** Self-service screen for MFA and own sessions; mounted when `mfa` and `sessions` are on. */
88
+ readonly accountSecurity?: AccountSecurityFeatureOptions;
89
+ };
90
+ /** auth-foundation + sessions, optional auth-mfa and account-security.
91
+ * auth-email-password stays with runProdApp's `auth` option, which also wires
92
+ * the MFA status checker into the login handler. */
93
+ export declare function composeAuthStack(options?: AuthStackOptions): FeatureDefinition[];
94
+ export type SaasStackBilling = {
95
+ readonly billing: "stripe";
96
+ } | {
97
+ readonly billing: "mollie";
98
+ readonly mollie: SubscriptionMollieOptions;
99
+ };
100
+ export type SaasStackOptions<TCaps extends Readonly<Record<string, unknown>> = Readonly<Record<string, unknown>>> = SaasStackBilling & {
101
+ readonly tiers: CreateTierEngineOptions<TCaps>;
102
+ readonly billingFoundation?: BillingFoundationOptions;
103
+ /** Mounts cap-counter and cap-overview. */
104
+ readonly caps?: CreateCapOverviewOptions;
105
+ /** Mounts waitlist; it needs auth-email-password and user-data-rights. */
106
+ readonly waitlist?: WaitlistOptions;
107
+ };
108
+ /** billing-foundation + tier-engine (+ caps, waitlist). `"stripe"` leaves
109
+ * subscription-stripe and its webhook route to runProdApp's `billing` option;
110
+ * `"mollie"` mounts subscription-mollie, whose webhook route the app adds.
111
+ * billing-foundation requires tenant-lifecycle and compliance-profiles, which
112
+ * composeGdprStack({ tenantLifecycle: true }) mounts. */
113
+ export declare function composeSaasStack<TCaps extends Readonly<Record<string, unknown>>>(options: SaasStackOptions<TCaps>): FeatureDefinition[];
114
+ export type SecurityStackOptions = {
115
+ /** Default true; pass false when another stack already mounts sessions with other options. */
116
+ readonly includeSessions?: boolean;
117
+ };
118
+ /** sessions, crypto-shredding, rate-limiting, audit: the security baseline the
119
+ * production boot warning checks. Relies on runProdApp's default L1/L2 rate
120
+ * limits and an explicit KMS decision. */
121
+ export declare function composeSecurityStack(options?: SecurityStackOptions): FeatureDefinition[];
@@ -5,12 +5,18 @@
5
5
  // (kumiko-framework#2337).
6
6
  //
7
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).
8
+ // Presets export feature instances only; no implicit PAT scopes or
9
+ // anonymousAccess. A stack that needs a runProdApp boot option tags the
10
+ // feature that depends on it with `bootRequires`; runProdApp names the gap.
11
+ import { createAccountSecurityFeature, } from "@cosmicdrift/kumiko-bundled-features/account-security";
10
12
  import { createAuditFeature } from "@cosmicdrift/kumiko-bundled-features/audit";
11
13
  import { createAuthFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
12
14
  import { createAuthMfaFeature, } from "@cosmicdrift/kumiko-bundled-features/auth-mfa";
15
+ import { createBillingFoundationFeature, } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
16
+ import { createCapCounterFeature } from "@cosmicdrift/kumiko-bundled-features/cap-counter";
17
+ import { createCapOverviewFeature, } from "@cosmicdrift/kumiko-bundled-features/cap-overview";
13
18
  import { createComplianceProfilesFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles";
19
+ import { createCryptoShreddingFeature } from "@cosmicdrift/kumiko-bundled-features/crypto-shredding";
14
20
  import { createDataRetentionFeature } from "@cosmicdrift/kumiko-bundled-features/data-retention";
15
21
  import { createDeliveryFeature } from "@cosmicdrift/kumiko-bundled-features/delivery";
16
22
  import { createFileFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/file-foundation";
@@ -25,11 +31,18 @@ import { createMailTransportSmtpFeature } from "@cosmicdrift/kumiko-bundled-feat
25
31
  import { createRateLimitingFeature } from "@cosmicdrift/kumiko-bundled-features/rate-limiting";
26
32
  import { createRendererFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/renderer-foundation";
27
33
  import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
34
+ import { createSubscriptionMollieFeature, } from "@cosmicdrift/kumiko-bundled-features/subscription-mollie";
28
35
  import { createTemplateResolverFeature } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
29
36
  import { createTenantLifecycleFeature } from "@cosmicdrift/kumiko-bundled-features/tenant-lifecycle";
37
+ import { createTierEngineFeature, } from "@cosmicdrift/kumiko-bundled-features/tier-engine";
30
38
  import { createUserDataRightsFeature, } from "@cosmicdrift/kumiko-bundled-features/user-data-rights";
31
39
  import { createUserDataRightsDefaultsFeature } from "@cosmicdrift/kumiko-bundled-features/user-data-rights-defaults";
40
+ import { createUserProfileFeature } from "@cosmicdrift/kumiko-bundled-features/user-profile";
41
+ import { createWaitlistFeature, } from "@cosmicdrift/kumiko-bundled-features/waitlist";
32
42
  import { createRendererMailHtmlFeature, } from "@cosmicdrift/kumiko-renderer-mail-html";
43
+ function withBootRequires(feature, stack, options) {
44
+ return { ...feature, bootRequires: { stack, options } };
45
+ }
33
46
  export function stackFeatureNames(features) {
34
47
  return features.map((f) => f.name);
35
48
  }
@@ -53,8 +66,11 @@ export function composeRendererStack(options = {}) {
53
66
  }),
54
67
  ];
55
68
  }
69
+ /** Tenant mail transports reach users through channel-email, so the stack needs `mail`. */
56
70
  export function composeMailStack(options) {
57
- const out = [createMailFoundationFeature()];
71
+ const out = [
72
+ withBootRequires(createMailFoundationFeature(), "composeMailStack", ["mail"]),
73
+ ];
58
74
  for (const transport of options.transports) {
59
75
  if (transport === "inmemory")
60
76
  out.push(createMailTransportInMemoryFeature());
@@ -63,9 +79,15 @@ export function composeMailStack(options) {
63
79
  }
64
80
  return out;
65
81
  }
66
- export function composeFileStack(options) {
67
- const out = [createFileFoundationFeature()];
68
- for (const provider of options.providers) {
82
+ export function composeFileStack(options = {}) {
83
+ const providers = options.providers ?? [];
84
+ const foundation = createFileFoundationFeature();
85
+ const out = [
86
+ providers.length === 0
87
+ ? withBootRequires(foundation, "composeFileStack", ["fileStorage"])
88
+ : foundation,
89
+ ];
90
+ for (const provider of providers) {
69
91
  if (provider === "inmemory")
70
92
  out.push(createFileProviderInMemoryFeature());
71
93
  if (provider === "s3")
@@ -77,10 +99,13 @@ export function composeFileStack(options) {
77
99
  out.push(createFilesFeature());
78
100
  return out;
79
101
  }
80
- /** Retention + compliance (+ optional lifecycle/sessions). UDR/files mount separately — order varies per app. */
102
+ /** Retention + compliance (+ optional lifecycle/sessions/self-service). Needs an
103
+ * explicit KMS decision (`kmsMode`, or the low-level `kms` / `allowPlaintextPii`).
104
+ * `selfService` mounts user-profile, which requires user-data-rights-defaults and
105
+ * auth-email-password: composeUserDataRightsStack mounts the defaults, the login comes from the `auth` option. */
81
106
  export function composeGdprStack(options = {}) {
82
107
  const order = options.order ?? "retention-first";
83
- const retention = createDataRetentionFeature();
108
+ const retention = withBootRequires(createDataRetentionFeature(), "composeGdprStack", ["kmsMode"]);
84
109
  const compliance = createComplianceProfilesFeature();
85
110
  const out = order === "compliance-first" ? [compliance, retention] : [retention, compliance];
86
111
  if (options.tenantLifecycle)
@@ -90,11 +115,19 @@ export function composeGdprStack(options = {}) {
90
115
  if (options.providers)
91
116
  out.push(...options.providers);
92
117
  }
118
+ if (options.selfService) {
119
+ if (!options.sessions)
120
+ out.push(createSessionsFeature());
121
+ out.push(withBootRequires(createUserDataRightsFeature(options.selfService.userDataRights ?? {}), "composeGdprStack", ["mail"]), createUserProfileFeature());
122
+ }
93
123
  return out;
94
124
  }
95
125
  export function composeUserDataRightsStack(options = {}) {
96
126
  const includeDefaults = options.includeDefaults ?? true;
97
- const out = [createUserDataRightsFeature(options.userDataRights ?? {})];
127
+ // Export-ready and deletion mails go through delivery + channel-email.
128
+ const out = [
129
+ withBootRequires(createUserDataRightsFeature(options.userDataRights ?? {}), "composeUserDataRightsStack", ["mail"]),
130
+ ];
98
131
  if (includeDefaults)
99
132
  out.push(createUserDataRightsDefaultsFeature());
100
133
  return out;
@@ -127,3 +160,49 @@ export function composeIdentityStack(options = {}) {
127
160
  out.push(createAuthMfaFeature(options.mfa));
128
161
  return out;
129
162
  }
163
+ /** auth-foundation + sessions, optional auth-mfa and account-security.
164
+ * auth-email-password stays with runProdApp's `auth` option, which also wires
165
+ * the MFA status checker into the login handler. */
166
+ export function composeAuthStack(options = {}) {
167
+ const sessions = options.sessions ?? true;
168
+ const out = composeIdentityStack({
169
+ sessions,
170
+ ...(options.mfa !== undefined && { mfa: options.mfa }),
171
+ });
172
+ if (options.mfa !== undefined && sessions) {
173
+ out.push(createAccountSecurityFeature(options.accountSecurity ?? {}));
174
+ }
175
+ return out;
176
+ }
177
+ /** billing-foundation + tier-engine (+ caps, waitlist). `"stripe"` leaves
178
+ * subscription-stripe and its webhook route to runProdApp's `billing` option;
179
+ * `"mollie"` mounts subscription-mollie, whose webhook route the app adds.
180
+ * billing-foundation requires tenant-lifecycle and compliance-profiles, which
181
+ * composeGdprStack({ tenantLifecycle: true }) mounts. */
182
+ export function composeSaasStack(options) {
183
+ const foundation = createBillingFoundationFeature(options.billingFoundation ?? {});
184
+ const out = [
185
+ options.billing === "stripe"
186
+ ? withBootRequires(foundation, "composeSaasStack", ["billing"])
187
+ : foundation,
188
+ createTierEngineFeature(options.tiers),
189
+ ];
190
+ if (options.billing === "mollie")
191
+ out.push(createSubscriptionMollieFeature(options.mollie));
192
+ if (options.caps)
193
+ out.push(createCapCounterFeature(), createCapOverviewFeature(options.caps));
194
+ if (options.waitlist)
195
+ out.push(createWaitlistFeature(options.waitlist));
196
+ return out;
197
+ }
198
+ /** sessions, crypto-shredding, rate-limiting, audit: the security baseline the
199
+ * production boot warning checks. Relies on runProdApp's default L1/L2 rate
200
+ * limits and an explicit KMS decision. */
201
+ export function composeSecurityStack(options = {}) {
202
+ return [
203
+ ...(options.includeSessions === false ? [] : [createSessionsFeature()]),
204
+ withBootRequires(createCryptoShreddingFeature(), "composeSecurityStack", ["kmsMode"]),
205
+ withBootRequires(createRateLimitingFeature(), "composeSecurityStack", ["rateLimit"]),
206
+ createAuditFeature(),
207
+ ];
208
+ }
@@ -0,0 +1,7 @@
1
+ import type { HostRoutingDispatchFn } from "@cosmicdrift/kumiko-bundled-features/host-routing";
2
+ import type { DevHostDispatch } from "./create-kumiko-server.ts";
3
+ export type ToDevHostDispatchOptions = {
4
+ readonly entries?: Readonly<Record<string, string>>;
5
+ readonly staticDir: string;
6
+ };
7
+ export declare function toDevHostDispatch(dispatch: HostRoutingDispatchFn, options: ToDevHostDispatchOptions): DevHostDispatch;
@@ -0,0 +1,31 @@
1
+ import { join } from "node:path";
2
+ // Runs the prod host-routing decision in the dev server, so dev and prod
3
+ // cannot route differently. Per-host CSP is a prod concern and dropped here.
4
+ export function toDevHostDispatch(dispatch, options) {
5
+ const entries = options.entries ?? {};
6
+ const toDev = (result) => {
7
+ switch (result.kind) {
8
+ case "redirect":
9
+ return result;
10
+ case "html": {
11
+ const entryName = entries[result.file];
12
+ return entryName === undefined
13
+ ? { kind: "static-html", file: join(options.staticDir, result.file) }
14
+ : { kind: "html", entryName };
15
+ }
16
+ case "not-found": {
17
+ if (result.file === undefined)
18
+ return { kind: "not-found" };
19
+ const entryName = entries[result.file];
20
+ return entryName === undefined
21
+ ? { kind: "not-found", file: join(options.staticDir, result.file) }
22
+ : { kind: "not-found", entryName };
23
+ }
24
+ }
25
+ };
26
+ return async (req, deps) => {
27
+ const url = new URL(req.url);
28
+ const host = req.headers.get("host") ?? url.host;
29
+ return toDev(await dispatch({ host, path: url.pathname, search: url.search }, deps));
30
+ };
31
+ }
package/dist/index.d.ts CHANGED
@@ -1,21 +1,22 @@
1
1
  export { type ComposeFeaturesOptions, composeFeatures, } from "@cosmicdrift/kumiko-server-runtime/compose-features";
2
- export { composeFileStack, composeGdprStack, composeIdentityStack, composeMailStack, composeOpsStack, composePagesStack, composeRendererStack, composeUserDataRightsStack, type FileProviderKind, type FileStackOptions, type GdprStackOptions, type GdprStackOrder, type IdentityStackOptions, type MailStackOptions, type MailTransportKind, type OpsStackOptions, type PagesStackOptions, stackFeatureNames, type UserDataRightsStackOptions, } from "./compose-stacks.js";
3
- export { type CreateKumikoServerOptions, createKumikoServer, type KumikoServerHandle, PROD_BUNDLES_ENV, resolveStylesheet, STYLESHEET_WATCH_ENV, } from "./create-kumiko-server.js";
4
- export { type FrameworkCoreEnv, frameworkCoreEnvSchema } from "./env-schema.js";
5
- export type { AuthoringStyle, BuildFewShotCorpusOptions, CorpusWarning, FewShotCorpus, FewShotEntry, } from "./few-shot-corpus.js";
6
- export { buildFewShotCorpus, pathToId } from "./few-shot-corpus.js";
7
- export { resolveFrameworkVersion } from "./framework-version.js";
8
- export type { CliOutput, RunInitDeployCliOptions } from "./init-deploy-cli.js";
9
- export { runInitDeployCli } from "./init-deploy-cli.js";
10
- export type { RunDevAppAuthOptions, RunDevAppOptions, SeedFn } from "./run-dev-app.js";
11
- export { runDevApp } from "./run-dev-app.js";
12
- export type { ScaffoldAppOptions, ScaffoldAppResult, ScaffoldFeatureEntry, } from "./scaffold-app.js";
13
- export { SCAFFOLD_BIOME_VERSION, scaffoldApp } from "./scaffold-app.js";
14
- export type { ScaffoldAppFeatureOptions, ScaffoldAppFeatureResult, } from "./scaffold-app-feature.js";
15
- export { runConfigPathForApp, scaffoldAppFeature } from "./scaffold-app-feature.js";
16
- export type { CheckDeployDriftResult, DeployDriftEntry, RenderDeployFilesOptions, RenderDeployFilesResult, RenderedDeployFile, ScaffoldDeployDetected, ScaffoldDeployOptions, ScaffoldDeployResult, ScaffoldedFile, } from "./scaffold-deploy.js";
17
- export { checkDeployDrift, renderDeployFiles, scaffoldDeploy } from "./scaffold-deploy.js";
18
- export type { ScaffoldFeatureOptions, ScaffoldFeatureResult } from "./scaffold-feature.js";
19
- export { scaffoldFeature } from "./scaffold-feature.js";
20
- export { type SetupTestStackFromFeaturesOptions, setupTestStackFromFeatures, type TestStackPreset, } from "./setup-test-stack-from-features.js";
21
- export { renderWelcomeBanner, type WelcomeBannerInput } from "./welcome-banner.js";
2
+ export { type AuthStackOptions, composeAuthStack, composeFileStack, composeGdprStack, composeIdentityStack, composeMailStack, composeOpsStack, composePagesStack, composeRendererStack, composeSaasStack, composeSecurityStack, composeUserDataRightsStack, type FileProviderKind, type FileStackOptions, type GdprStackOptions, type GdprStackOrder, type IdentityStackOptions, type MailStackOptions, type MailTransportKind, type OpsStackOptions, type PagesStackOptions, type SaasStackBilling, type SaasStackOptions, type SecurityStackOptions, stackFeatureNames, type UserDataRightsStackOptions, } from "./compose-stacks.ts";
3
+ export { type CreateKumikoServerOptions, createKumikoServer, type KumikoServerHandle, PROD_BUNDLES_ENV, resolveStylesheet, STYLESHEET_WATCH_ENV, } from "./create-kumiko-server.ts";
4
+ export { type FrameworkCoreEnv, frameworkCoreEnvSchema } from "./env-schema.ts";
5
+ export type { AuthoringStyle, BuildFewShotCorpusOptions, CorpusWarning, FewShotCorpus, FewShotEntry, } from "./few-shot-corpus.ts";
6
+ export { buildFewShotCorpus, pathToId } from "./few-shot-corpus.ts";
7
+ export { resolveFrameworkVersion } from "./framework-version.ts";
8
+ export { type ToDevHostDispatchOptions, toDevHostDispatch } from "./host-dispatch-adapter.ts";
9
+ export type { CliOutput, RunInitDeployCliOptions } from "./init-deploy-cli.ts";
10
+ export { runInitDeployCli } from "./init-deploy-cli.ts";
11
+ export type { RunDevAppAuthOptions, RunDevAppOptions, SeedFn } from "./run-dev-app.ts";
12
+ export { runDevApp } from "./run-dev-app.ts";
13
+ export type { ScaffoldAppOptions, ScaffoldAppResult, ScaffoldFeatureEntry, } from "./scaffold-app.ts";
14
+ export { SCAFFOLD_BIOME_VERSION, scaffoldApp } from "./scaffold-app.ts";
15
+ export type { ScaffoldAppFeatureOptions, ScaffoldAppFeatureResult, } from "./scaffold-app-feature.ts";
16
+ export { runConfigPathForApp, scaffoldAppFeature } from "./scaffold-app-feature.ts";
17
+ export type { CheckDeployDriftResult, DeployDriftEntry, RenderDeployFilesOptions, RenderDeployFilesResult, RenderedDeployFile, ScaffoldDeployDetected, ScaffoldDeployOptions, ScaffoldDeployResult, ScaffoldedFile, } from "./scaffold-deploy.ts";
18
+ export { checkDeployDrift, renderDeployFiles, scaffoldDeploy } from "./scaffold-deploy.ts";
19
+ export type { ScaffoldFeatureOptions, ScaffoldFeatureResult } from "./scaffold-feature.ts";
20
+ export { scaffoldFeature } from "./scaffold-feature.ts";
21
+ export { type SetupTestStackFromFeaturesOptions, setupTestStackFromFeatures, type TestStackPreset, } from "./setup-test-stack-from-features.ts";
22
+ export { renderWelcomeBanner, type WelcomeBannerInput } from "./welcome-banner.ts";
package/dist/index.js CHANGED
@@ -17,11 +17,12 @@ export { composeFeatures, } from "@cosmicdrift/kumiko-server-runtime/compose-fea
17
17
  // im Sub-Path-Export `@cosmicdrift/kumiko-dev-server/build`. Damit zieht der Main-Barrel
18
18
  // kein Bun-Toolchain-Bundle mehr in Production-Reads (z.B. wenn drizzle-kit
19
19
  // die App-Config unter Node lädt).
20
- export { composeFileStack, composeGdprStack, composeIdentityStack, composeMailStack, composeOpsStack, composePagesStack, composeRendererStack, composeUserDataRightsStack, stackFeatureNames, } from "./compose-stacks.js";
20
+ export { composeAuthStack, composeFileStack, composeGdprStack, composeIdentityStack, composeMailStack, composeOpsStack, composePagesStack, composeRendererStack, composeSaasStack, composeSecurityStack, composeUserDataRightsStack, stackFeatureNames, } from "./compose-stacks.js";
21
21
  export { createKumikoServer, PROD_BUNDLES_ENV, resolveStylesheet, STYLESHEET_WATCH_ENV, } from "./create-kumiko-server.js";
22
22
  export { frameworkCoreEnvSchema } from "./env-schema.js";
23
23
  export { buildFewShotCorpus, pathToId } from "./few-shot-corpus.js";
24
24
  export { resolveFrameworkVersion } from "./framework-version.js";
25
+ export { toDevHostDispatch } from "./host-dispatch-adapter.js";
25
26
  export { runInitDeployCli } from "./init-deploy-cli.js";
26
27
  export { runDevApp } from "./run-dev-app.js";
27
28
  export { SCAFFOLD_BIOME_VERSION, scaffoldApp } from "./scaffold-app.js";
@@ -8,8 +8,13 @@ import { getFlag, getNumberFlag, getStringFlag, parseArgs, } from "@cosmicdrift/
8
8
  import * as z from "zod";
9
9
  import { checkDeployDrift, scaffoldDeploy } from "./scaffold-deploy.js";
10
10
  const USAGE = "Usage: kumiko-init-deploy --app <name> [--port <n>] [--github-org <org>] [--out <dir>] [--force | --check]";
11
+ // Kept for one release so existing pipelines keep running; deploy/ is now a
12
+ // managed-files group that `kumiko new app` writes and `kumiko sync-managed` updates.
13
+ const DEPRECATION_NOTICE = " ! kumiko-init-deploy is deprecated and will be removed in the next release: deploy/ files are managed files now. Use `kumiko check managed` and `kumiko sync-managed --write`.";
11
14
  const HELP = `${USAGE}
12
15
 
16
+ ${DEPRECATION_NOTICE}
17
+
13
18
  --app <name> app name (default: package.json "name" without its @scope/)
14
19
  --port <n> port the container exposes
15
20
  --github-org <org> GitHub org for the image reference
@@ -87,6 +92,7 @@ export async function runInitDeployCli({ argv, cwd, out, }) {
87
92
  out.log(HELP);
88
93
  return 0;
89
94
  }
95
+ out.err(DEPRECATION_NOTICE);
90
96
  const args = parseArgs(argv);
91
97
  const force = getFlag(args, "force");
92
98
  const check = getFlag(args, "check");
@@ -0,0 +1,13 @@
1
+ export declare const STANDARD_APP_SCRIPTS: readonly ["dev", "build", "start", "typecheck", "lint", "check", "test", "test:unit", "test:integration", "test:integration:ci", "test:dom", "test:real", "e2e", "e2e:real", "screenshots", "prepare"];
2
+ export type StandardAppScript = (typeof STANDARD_APP_SCRIPTS)[number];
3
+ /** Extras every app may carry; they are listed but expected. */
4
+ export declare const KNOWN_EXTRA_APP_SCRIPTS: readonly ["test:verbose", "test:bunfig", "test:lib-coverage", "shots"];
5
+ export type AppScriptsReport = {
6
+ readonly missing: readonly StandardAppScript[];
7
+ readonly knownExtras: readonly string[];
8
+ readonly appExtras: readonly string[];
9
+ };
10
+ export declare function checkAppScripts(scripts: Readonly<Record<string, string>>, options: {
11
+ readonly hasUi: boolean;
12
+ }): AppScriptsReport;
13
+ export declare const PREPARE_SCRIPT = "[ \"$HUSKY\" = \"0\" ] || git config core.hooksPath .husky 2>/dev/null || true";
@@ -0,0 +1,40 @@
1
+ // The package.json scripts every app exposes, so agents and CI call the same
2
+ // names in every repo.
3
+ export const STANDARD_APP_SCRIPTS = [
4
+ "dev",
5
+ "build",
6
+ "start",
7
+ "typecheck",
8
+ "lint",
9
+ "check",
10
+ "test",
11
+ "test:unit",
12
+ "test:integration",
13
+ "test:integration:ci",
14
+ "test:dom",
15
+ "test:real",
16
+ "e2e",
17
+ "e2e:real",
18
+ "screenshots",
19
+ "prepare",
20
+ ];
21
+ /** Extras every app may carry; they are listed but expected. */
22
+ export const KNOWN_EXTRA_APP_SCRIPTS = [
23
+ "test:verbose",
24
+ "test:bunfig",
25
+ "test:lib-coverage",
26
+ "shots",
27
+ ];
28
+ export function checkAppScripts(scripts, options) {
29
+ const required = STANDARD_APP_SCRIPTS.filter((name) => name !== "test:dom" || options.hasUi);
30
+ const standard = new Set(STANDARD_APP_SCRIPTS);
31
+ const known = new Set(KNOWN_EXTRA_APP_SCRIPTS);
32
+ const extras = Object.keys(scripts).filter((name) => !standard.has(name));
33
+ return {
34
+ missing: required.filter((name) => scripts[name] === undefined),
35
+ knownExtras: extras.filter((name) => known.has(name)),
36
+ appExtras: extras.filter((name) => !known.has(name)),
37
+ };
38
+ }
39
+ // The hooks path line every app already carries; HUSKY=0 (CI) skips it.
40
+ export const PREPARE_SCRIPT = '[ "$HUSKY" = "0" ] || git config core.hooksPath .husky 2>/dev/null || true';
@@ -0,0 +1,34 @@
1
+ import { type ManagedAppFacts, type ManagedFile } from "./registry.ts";
2
+ export type ManagedFileState = {
3
+ readonly state: "ok";
4
+ } | {
5
+ readonly state: "missing";
6
+ }
7
+ /** `-` lines: template, `+` lines: repo. */
8
+ | {
9
+ readonly state: "differs";
10
+ readonly diff: string;
11
+ }
12
+ /** Exists without the marker; `diff` is set when the content also differs. */
13
+ | {
14
+ readonly state: "unmarked";
15
+ readonly diff?: string;
16
+ } | {
17
+ readonly state: "local";
18
+ readonly reason: string;
19
+ } | {
20
+ readonly state: "invalid";
21
+ readonly message: string;
22
+ };
23
+ export type ManagedFileReport = ManagedFileState & {
24
+ readonly path: string;
25
+ readonly kind: ManagedFile["kind"];
26
+ };
27
+ export type ManagedCheckResult = {
28
+ /** kumiko.config.ts calls defineAppConfig: drift fails the check from then on. */
29
+ readonly adopted: boolean;
30
+ readonly files: readonly ManagedFileReport[];
31
+ };
32
+ export declare function checkManagedFiles(facts: ManagedAppFacts): ManagedCheckResult;
33
+ /** States that fail `kumiko check managed` once the app has adopted kumiko.config.ts. */
34
+ export declare function isManagedDrift(report: ManagedFileReport): boolean;
@@ -0,0 +1,109 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { mergeBunfig } from "@cosmicdrift/kumiko-testing/bunfig";
4
+ import { parseJsonc } from "./jsonc.js";
5
+ import { renderLineDiff } from "./line-diff.js";
6
+ import { readManagedMarker, stripManagedMarker } from "./marker.js";
7
+ import { BIOME_PRESET, listManagedFiles, renderManagedBunfig, TSCONFIG_PRESET, } from "./registry.js";
8
+ function readIfExists(path) {
9
+ return existsSync(path) ? readFileSync(path, "utf-8") : undefined;
10
+ }
11
+ function compareBodies(expected, actual) {
12
+ return expected === actual ? undefined : renderLineDiff(expected, actual);
13
+ }
14
+ function checkTemplate(file, existing) {
15
+ const marker = readManagedMarker(existing);
16
+ if (marker.kind === "invalid")
17
+ return { state: "invalid", message: marker.message };
18
+ if (marker.kind === "local")
19
+ return { state: "local", reason: marker.reason };
20
+ const diff = compareBodies(stripManagedMarker(file.content), stripManagedMarker(existing));
21
+ if (marker.kind === "unmarked")
22
+ return diff === undefined ? { state: "unmarked" } : { state: "unmarked", diff };
23
+ return diff === undefined ? { state: "ok" } : { state: "differs", diff };
24
+ }
25
+ function checkBunfig(file, existing) {
26
+ const marker = readManagedMarker(existing);
27
+ if (marker.kind === "invalid")
28
+ return { state: "invalid", message: marker.message };
29
+ if (marker.kind === "local")
30
+ return { state: "local", reason: marker.reason };
31
+ const merged = mergeBunfig(renderManagedBunfig(file, existing), existing);
32
+ if (!merged.ok) {
33
+ const keys = merged.unknownKeys.map(({ section, key }) => `[${section}] ${key}`).join(", ");
34
+ return { state: "invalid", message: `keys the generator does not own: ${keys}` };
35
+ }
36
+ const diff = compareBodies(merged.content, existing);
37
+ if (marker.kind === "unmarked")
38
+ return diff === undefined ? { state: "unmarked" } : { state: "unmarked", diff };
39
+ return diff === undefined ? { state: "ok" } : { state: "differs", diff };
40
+ }
41
+ function extendsList(value) {
42
+ if (typeof value !== "object" || value === null || !("extends" in value))
43
+ return [];
44
+ const extendsValue = value.extends;
45
+ return Array.isArray(extendsValue) ? extendsValue : [extendsValue];
46
+ }
47
+ function checkStructural(file, facts, existing) {
48
+ if (file.path === "kumiko.config.ts") {
49
+ switch (facts.config.kind) {
50
+ case "defined":
51
+ return { state: "ok" };
52
+ case "legacy":
53
+ return {
54
+ state: "invalid",
55
+ message: "default-export defineAppConfig({ features, client, ports }) from @cosmicdrift/kumiko-framework/config",
56
+ };
57
+ case "invalid":
58
+ return { state: "invalid", message: facts.config.message };
59
+ case "missing":
60
+ return { state: "missing" };
61
+ }
62
+ }
63
+ const preset = file.path === "biome.json" ? BIOME_PRESET : TSCONFIG_PRESET;
64
+ let parsed;
65
+ try {
66
+ parsed = parseJsonc(existing, file.path);
67
+ }
68
+ catch (error) {
69
+ return {
70
+ state: "invalid",
71
+ message: `cannot parse: ${error instanceof Error ? error.message : String(error)}`,
72
+ };
73
+ }
74
+ return extendsList(parsed).includes(preset)
75
+ ? { state: "ok" }
76
+ : { state: "invalid", message: `must extend "${preset}"; keep app overrides below extends` };
77
+ }
78
+ function checkFile(file, facts) {
79
+ if (file.kind === "structural" && file.path === "kumiko.config.ts") {
80
+ return checkStructural(file, facts, "");
81
+ }
82
+ const existing = readIfExists(join(facts.appRoot, file.path));
83
+ if (existing === undefined)
84
+ return { state: "missing" };
85
+ switch (file.kind) {
86
+ case "template":
87
+ return checkTemplate(file, existing);
88
+ case "bunfig":
89
+ return checkBunfig(file, existing);
90
+ case "structural":
91
+ return checkStructural(file, facts, existing);
92
+ case "seed":
93
+ return { state: "ok" };
94
+ }
95
+ }
96
+ export function checkManagedFiles(facts) {
97
+ return {
98
+ adopted: facts.config.kind === "defined",
99
+ files: listManagedFiles(facts).map((file) => ({
100
+ path: file.path,
101
+ kind: file.kind,
102
+ ...checkFile(file, facts),
103
+ })),
104
+ };
105
+ }
106
+ /** States that fail `kumiko check managed` once the app has adopted kumiko.config.ts. */
107
+ export function isManagedDrift(report) {
108
+ return report.state !== "ok" && report.state !== "local";
109
+ }
@@ -0,0 +1,8 @@
1
+ export { listAppPorts } from "@cosmicdrift/kumiko-framework/config";
2
+ export { type AppScriptsReport, checkAppScripts, KNOWN_EXTRA_APP_SCRIPTS, PREPARE_SCRIPT, STANDARD_APP_SCRIPTS, type StandardAppScript, } from "./app-scripts.ts";
3
+ export { checkManagedFiles, isManagedDrift, type ManagedCheckResult, type ManagedFileReport, type ManagedFileState, } from "./check.ts";
4
+ export { addManagedMarker, MANAGED_MARKER, readManagedMarker, stripManagedMarker, } from "./marker.ts";
5
+ export { findPortCollisions, type PortClaim, type PortCollision, type ScannedApp, scanWorkspaceApps, } from "./port-registry.ts";
6
+ export { BIOME_PRESET, listManagedFiles, type ManagedAppFacts, type ManagedAppInput, type ManagedFile, type ManagedTemplateFile, renderBiomePresetJson, renderManagedBunfig, renderManagedTemplates, renderTsconfigPresetJson, resolveManagedAppFacts, TSCONFIG_PRESET, } from "./registry.ts";
7
+ export { parseStaticAppConfig, readStaticAppConfig, type StaticAppConfig, } from "./static-app-config.ts";
8
+ export { type SyncAction, type SyncManagedOptions, syncManagedFiles } from "./sync.ts";
@@ -0,0 +1,8 @@
1
+ export { listAppPorts } from "@cosmicdrift/kumiko-framework/config";
2
+ export { checkAppScripts, KNOWN_EXTRA_APP_SCRIPTS, PREPARE_SCRIPT, STANDARD_APP_SCRIPTS, } from "./app-scripts.js";
3
+ export { checkManagedFiles, isManagedDrift, } from "./check.js";
4
+ export { addManagedMarker, MANAGED_MARKER, readManagedMarker, stripManagedMarker, } from "./marker.js";
5
+ export { findPortCollisions, scanWorkspaceApps, } from "./port-registry.js";
6
+ export { BIOME_PRESET, listManagedFiles, renderBiomePresetJson, renderManagedBunfig, renderManagedTemplates, renderTsconfigPresetJson, resolveManagedAppFacts, TSCONFIG_PRESET, } from "./registry.js";
7
+ export { parseStaticAppConfig, readStaticAppConfig, } from "./static-app-config.js";
8
+ export { syncManagedFiles } from "./sync.js";
@@ -0,0 +1 @@
1
+ export declare function parseJsonc(source: string, context: string): unknown;
@@ -0,0 +1,12 @@
1
+ import { parseJsonOrThrow } from "@cosmicdrift/kumiko-framework/utils";
2
+ // Strings come first in each alternation, so comment and comma patterns
3
+ // inside string literals are matched as part of the string and kept.
4
+ const STRING_OR_COMMENT = /"(?:\\.|[^"\\])*"|\/\/[^\n]*|\/\*[\s\S]*?\*\//g;
5
+ const STRING_OR_TRAILING_COMMA = /"(?:\\.|[^"\\])*"|,(\s*[}\]])/g;
6
+ // biome.json and tsconfig.json accept comments and trailing commas; strip both
7
+ // (outside strings) so they parse as JSON.
8
+ export function parseJsonc(source, context) {
9
+ const withoutComments = source.replace(STRING_OR_COMMENT, (match) => match.startsWith('"') ? match : "");
10
+ const withoutTrailingCommas = withoutComments.replace(STRING_OR_TRAILING_COMMA, (match, afterComma) => afterComma ?? match);
11
+ return parseJsonOrThrow(withoutTrailingCommas, context);
12
+ }
@@ -0,0 +1,2 @@
1
+ /** `-` lines are what the template expects, `+` lines what the repo has. */
2
+ export declare function renderLineDiff(expected: string, actual: string): string;