@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 +3 -3
- package/src/__tests__/scaffold-app.test.ts +37 -0
- package/src/index.ts +5 -1
- package/src/scaffold-app.ts +48 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-dev-server",
|
|
3
|
-
"version": "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.
|
|
54
|
-
"@cosmicdrift/kumiko-framework": "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 {
|
|
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,
|
package/src/scaffold-app.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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:
|
|
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
|
-
"//
|
|
165
|
-
"//
|
|
166
|
-
"//
|
|
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)",
|