@cosmicdrift/kumiko-dev-server 0.73.0 → 0.75.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-dev-server",
3
- "version": "0.73.0",
3
+ "version": "0.75.0",
4
4
  "description": "Development server bootstrap for Kumiko apps. Bundles the client, mints dev-JWTs, injects the resolved AppSchema, and seeds an admin. Not for production.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -50,8 +50,8 @@
50
50
  "kumiko-schema-check": "./bin/kumiko-schema-check.ts"
51
51
  },
52
52
  "dependencies": {
53
- "@cosmicdrift/kumiko-bundled-features": "0.73.0",
54
- "@cosmicdrift/kumiko-framework": "0.73.0",
53
+ "@cosmicdrift/kumiko-bundled-features": "0.75.0",
54
+ "@cosmicdrift/kumiko-framework": "0.75.0",
55
55
  "ts-morph": "^28.0.0"
56
56
  },
57
57
  "publishConfig": {
@@ -119,6 +119,43 @@ describe("scaffoldApp", () => {
119
119
  expect(() => scaffoldApp({ name: "existing", destination: dest })).toThrow(/already exists/);
120
120
  });
121
121
 
122
+ test("features-param: custom selection lands in run-config.ts imports + APP_FEATURES", () => {
123
+ const dest = join(tmp, "custom-features");
124
+ scaffoldApp({
125
+ name: "custom-features",
126
+ destination: dest,
127
+ features: [
128
+ {
129
+ name: "tenant",
130
+ importPath: "@cosmicdrift/kumiko-bundled-features/tenant",
131
+ exportName: "createTenantFeature",
132
+ callExpression: "createTenantFeature()",
133
+ },
134
+ {
135
+ name: "billing-foundation",
136
+ importPath: "@cosmicdrift/kumiko-bundled-features/billing-foundation",
137
+ exportName: "billingFoundationFeature",
138
+ callExpression: "billingFoundationFeature",
139
+ },
140
+ ],
141
+ });
142
+ const cfg = readFileSync(join(dest, "src/run-config.ts"), "utf-8");
143
+ expect(cfg).toContain('from "@cosmicdrift/kumiko-bundled-features/tenant"');
144
+ expect(cfg).toContain('from "@cosmicdrift/kumiko-bundled-features/billing-foundation"');
145
+ expect(cfg).toContain("createTenantFeature()");
146
+ expect(cfg).toContain("billingFoundationFeature");
147
+ expect(cfg).not.toContain("createSecretsFeature");
148
+ expect(cfg).not.toContain("createSessionsFeature");
149
+ });
150
+
151
+ test("features-param: empty array falls back to foundation (backwards-compat)", () => {
152
+ const dest = join(tmp, "empty-features");
153
+ scaffoldApp({ name: "empty-features", destination: dest, features: [] });
154
+ const cfg = readFileSync(join(dest, "src/run-config.ts"), "utf-8");
155
+ expect(cfg).toContain("createSecretsFeature()");
156
+ expect(cfg).toContain("createSessionsFeature()");
157
+ });
158
+
122
159
  test("deterministic tenantId for same name (reproducible boots)", () => {
123
160
  const a = join(tmp, "a");
124
161
  const b = join(tmp, "b");
package/src/index.ts CHANGED
@@ -54,7 +54,11 @@ export type {
54
54
  SignupSetup,
55
55
  } from "./run-prod-app";
56
56
  export { runProdApp } from "./run-prod-app";
57
- export type { ScaffoldAppOptions, ScaffoldAppResult } from "./scaffold-app";
57
+ export type {
58
+ ScaffoldAppOptions,
59
+ ScaffoldAppResult,
60
+ ScaffoldFeatureEntry,
61
+ } from "./scaffold-app";
58
62
  export { scaffoldApp } from "./scaffold-app";
59
63
  export type {
60
64
  ScaffoldAppFeatureOptions,
@@ -16,6 +16,18 @@ import { join, resolve } from "node:path";
16
16
  import { IndentationText, Project, VariableDeclarationKind } from "ts-morph";
17
17
  import { isKebabSegment } from "./kebab";
18
18
 
19
+ // Single bundled-feature entry the scaffolder mounts into run-config.ts.
20
+ // importPath is the from-spec ("@cosmicdrift/kumiko-bundled-features/files"),
21
+ // exportName the named import, callExpression the form that lands in the
22
+ // APP_FEATURES array literal — typically `${exportName}()` for factory-style
23
+ // features and just `${exportName}` for object-style ones (e.g. billingFoundationFeature).
24
+ export type ScaffoldFeatureEntry = {
25
+ readonly name: string;
26
+ readonly importPath: string;
27
+ readonly exportName: string;
28
+ readonly callExpression: string;
29
+ };
30
+
19
31
  export type ScaffoldAppOptions = {
20
32
  /** kebab-case app name (e.g. "my-shop"). Becomes package-name + folder. */
21
33
  readonly name: string;
@@ -28,6 +40,10 @@ export type ScaffoldAppOptions = {
28
40
  readonly cwd?: string;
29
41
  /** npm-version-pin for @cosmicdrift/* deps. Default "*" for latest. */
30
42
  readonly frameworkVersion?: string;
43
+ /** Bundled-features to mount in run-config.ts. Default: secrets + sessions
44
+ * (the historical foundation). create-kumiko-app passes the picker output
45
+ * here so the generated APP_FEATURES reflects the user's selection. */
46
+ readonly features?: ReadonlyArray<ScaffoldFeatureEntry>;
31
47
  };
32
48
 
33
49
  export type ScaffoldAppResult = {
@@ -58,7 +74,7 @@ export function scaffoldApp(options: ScaffoldAppOptions): ScaffoldAppResult {
58
74
  write(join(destination, "tsconfig.json"), renderTsconfig());
59
75
  files.push("tsconfig.json");
60
76
 
61
- write(join(destination, "src", "run-config.ts"), renderRunConfig());
77
+ write(join(destination, "src", "run-config.ts"), renderRunConfig(options.features));
62
78
  files.push("src/run-config.ts");
63
79
 
64
80
  write(join(destination, "bin", "main.ts"), renderMain(options.name));
@@ -133,26 +149,44 @@ function newTsProject(): Project {
133
149
  });
134
150
  }
135
151
 
136
- function renderRunConfig(): string {
152
+ const FOUNDATION_FEATURES: ReadonlyArray<ScaffoldFeatureEntry> = [
153
+ {
154
+ name: "secrets",
155
+ importPath: "@cosmicdrift/kumiko-bundled-features/secrets",
156
+ exportName: "createSecretsFeature",
157
+ callExpression: "createSecretsFeature()",
158
+ },
159
+ {
160
+ name: "sessions",
161
+ importPath: "@cosmicdrift/kumiko-bundled-features/sessions",
162
+ exportName: "createSessionsFeature",
163
+ callExpression: "createSessionsFeature()",
164
+ },
165
+ ];
166
+
167
+ function renderRunConfig(features?: ReadonlyArray<ScaffoldFeatureEntry>): string {
137
168
  const project = newTsProject();
138
169
  const sf = project.createSourceFile("run-config.ts", "");
139
170
 
140
- sf.addImportDeclaration({
141
- moduleSpecifier: "@cosmicdrift/kumiko-bundled-features/secrets",
142
- namedImports: ["createSecretsFeature"],
143
- });
144
- sf.addImportDeclaration({
145
- moduleSpecifier: "@cosmicdrift/kumiko-bundled-features/sessions",
146
- namedImports: ["createSessionsFeature"],
147
- });
171
+ const effective = features && features.length > 0 ? features : FOUNDATION_FEATURES;
172
+ const grouped = new Map<string, string[]>();
173
+ for (const entry of effective) {
174
+ const existing = grouped.get(entry.importPath) ?? [];
175
+ if (!existing.includes(entry.exportName)) existing.push(entry.exportName);
176
+ grouped.set(entry.importPath, existing);
177
+ }
178
+ for (const [importPath, namedImports] of grouped) {
179
+ sf.addImportDeclaration({ moduleSpecifier: importPath, namedImports });
180
+ }
148
181
 
182
+ const callList = effective.map((f) => f.callExpression).join(", ");
149
183
  sf.addVariableStatement({
150
184
  declarationKind: VariableDeclarationKind.Const,
151
185
  isExported: true,
152
186
  declarations: [
153
187
  {
154
188
  name: "APP_FEATURES",
155
- initializer: "[createSecretsFeature(), createSessionsFeature()] as const",
189
+ initializer: `[${callList}] as const`,
156
190
  },
157
191
  ],
158
192
  });
@@ -161,9 +195,9 @@ function renderRunConfig(): string {
161
195
  0,
162
196
  [
163
197
  "// Single source of truth für die Feature-Komposition deiner App.",
164
- "// Bundled-Foundation: secrets + sessions. config/user/tenant/auth-email-password",
165
- "// werden via composeFeatures(includeBundled:true) automatisch ergänzt",
166
- "// wenn runProdApp mit `auth: {…}` aufgerufen wird (siehe bin/main.ts).",
198
+ "// config/user/tenant/auth-email-password werden via",
199
+ "// composeFeatures(includeBundled:true) automatisch ergänzt wenn",
200
+ "// runProdApp mit `auth: {…}` aufgerufen wird (siehe bin/main.ts).",
167
201
  "//",
168
202
  "// Neue features hinzufügen:",
169
203
  "// - bunx @cosmicdrift/kumiko-cli add feature <name> (DX-2, automatisch)",