@decohere/scaffold 0.0.1-dev.125 → 0.0.1-dev.18
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 +9 -19
- package/src/add-api.action.ts +25 -0
- package/src/add-api.command.ts +31 -0
- package/src/add.command.ts +20 -0
- package/src/add.module.ts +11 -0
- package/src/cli.ts +4 -0
- package/src/create.command.ts +20 -0
- package/src/create.module.ts +11 -0
- package/src/domain/prompt.ts +50 -0
- package/{types/src/index.d.ts → src/index.ts} +0 -4
- package/src/models/compose.model.ts +205 -0
- package/src/models/config-yaml.model.ts +83 -0
- package/src/models/const.ts +20 -0
- package/src/models/dev-container.model.ts +136 -0
- package/src/models/docker-image.model.ts +343 -0
- package/src/models/env-file.model.ts +45 -0
- package/src/models/gitignore.model.ts +35 -0
- package/src/models/launch-json.model.ts +60 -0
- package/src/models/package-json.model.ts +169 -0
- package/src/models/project.model.ts +141 -0
- package/{types/src/models/source-file.d.ts → src/models/source-file.ts} +6 -4
- package/src/models/tsconfig.model.ts +29 -0
- package/src/models/vitest-config.model.ts +26 -0
- package/src/models/vscode-settings.model.ts +24 -0
- package/src/program.ts +63 -0
- package/{types/src/resolve-target-dir.d.ts → src/resolve-target-dir.ts} +4 -1
- package/src/services/scaffold.service.ts +89 -0
- package/src/version.ts +56 -0
- package/src/webapi/api-name.ts +73 -0
- package/src/webapi/cli-options.ts +56 -0
- package/{types/src/webapi/const.d.ts → src/webapi/const.ts} +21 -9
- package/src/webapi/create-api.ts +275 -0
- package/src/webapi/devcontainer/host-scripts.ts +35 -0
- package/src/webapi/features/auth.feature.ts +454 -0
- package/src/webapi/features/auth0-cli.ts +421 -0
- package/src/webapi/features/biome-format.ts +34 -0
- package/src/webapi/features/database.feature.ts +522 -0
- package/src/webapi/features/finalize.feature.ts +112 -0
- package/src/webapi/features/postman.feature.ts +287 -0
- package/src/webapi/models/api-package-json.model.ts +60 -0
- package/src/webapi/models/auth-config.model.ts +36 -0
- package/src/webapi/models/controller-source-file.model.ts +232 -0
- package/{es6/src/webapi/models/github-ci.model.js → src/webapi/models/github-ci.model.ts} +29 -27
- package/src/webapi/models/main-source-file.model.ts +208 -0
- package/src/webapi/models/production-dockerfile.model.ts +162 -0
- package/src/webapi/models/sample-files.model.ts +45 -0
- package/src/webapi/models/webapi-image.model.ts +21 -0
- package/src/webapi/postman/hello-openapi.ts +92 -0
- package/src/webapi/postman/local-view.ts +195 -0
- package/src/webapi/postman/openapi-to-postman.ts +208 -0
- package/src/webapi/workspace-readme.ts +192 -0
- package/src/webapi.action.ts +22 -0
- package/src/webapi.command.ts +41 -0
- package/tests/auth-postman.wiring.spec.ts +263 -0
- package/tests/auth0-cli.spec.ts +232 -0
- package/tests/biome-format.spec.ts +49 -0
- package/tests/cli.integration.spec.ts +658 -0
- package/tests/compose.model.spec.ts +51 -0
- package/tests/container-image.model.spec.ts +162 -0
- package/tests/dev-container.model.spec.ts +29 -0
- package/tests/devcontainer-base-image.spec.ts +32 -0
- package/tests/hello-di-inject.scaffold.spec.ts +18 -0
- package/tests/hello-route.contract.spec.ts +94 -0
- package/tests/package-json.order.spec.ts +59 -0
- package/tests/postman-from-openapi.spec.ts +197 -0
- package/tests/seed-devcontainer-templates.ts +40 -0
- package/tests/webapi/devcontainer/codebase-rag-mcp.spec.ts +96 -0
- package/LICENSE +0 -15
- package/commonjs/src/add-api.action.js +0 -37
- package/commonjs/src/add-api.command.js +0 -39
- package/commonjs/src/add.command.js +0 -22
- package/commonjs/src/add.module.js +0 -24
- package/commonjs/src/auth0-configure.action.js +0 -28
- package/commonjs/src/auth0-configure.command.js +0 -24
- package/commonjs/src/auth0-login.action.js +0 -28
- package/commonjs/src/auth0-login.command.js +0 -23
- package/commonjs/src/auth0.command.js +0 -22
- package/commonjs/src/auth0.module.js +0 -28
- package/commonjs/src/cli.js +0 -8
- package/commonjs/src/create.command.js +0 -22
- package/commonjs/src/create.module.js +0 -24
- package/commonjs/src/domain/prompt.js +0 -62
- package/commonjs/src/index.js +0 -48
- package/commonjs/src/models/compose.model.js +0 -258
- package/commonjs/src/models/config-yaml.model.js +0 -86
- package/commonjs/src/models/const.js +0 -30
- package/commonjs/src/models/dev-container.model.js +0 -172
- package/commonjs/src/models/docker-image.model.js +0 -277
- package/commonjs/src/models/env-file.model.js +0 -48
- package/commonjs/src/models/gitignore.model.js +0 -48
- package/commonjs/src/models/launch-json.model.js +0 -70
- package/commonjs/src/models/package-json.model.js +0 -156
- package/commonjs/src/models/project.model.js +0 -132
- package/commonjs/src/models/source-file.js +0 -4
- package/commonjs/src/models/tsconfig.model.js +0 -32
- package/commonjs/src/models/vitest-config.model.js +0 -33
- package/commonjs/src/models/vscode-settings.model.js +0 -29
- package/commonjs/src/program.js +0 -64
- package/commonjs/src/resolve-target-dir.js +0 -17
- package/commonjs/src/services/scaffold.service.js +0 -60
- package/commonjs/src/version.js +0 -55
- package/commonjs/src/webapi/api-name.js +0 -93
- package/commonjs/src/webapi/auth0-commands.js +0 -250
- package/commonjs/src/webapi/authorize-scopes.js +0 -79
- package/commonjs/src/webapi/cli-options.js +0 -99
- package/commonjs/src/webapi/const.js +0 -54
- package/commonjs/src/webapi/create-api.js +0 -265
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.js +0 -164
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/commonjs/src/webapi/devcontainer/host-scripts.js +0 -50
- package/commonjs/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/commonjs/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/commonjs/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/commonjs/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/commonjs/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/commonjs/src/webapi/features/auth.feature.js +0 -408
- package/commonjs/src/webapi/features/auth0-cli.js +0 -472
- package/commonjs/src/webapi/features/biome-format.js +0 -47
- package/commonjs/src/webapi/features/database.feature.js +0 -433
- package/commonjs/src/webapi/features/finalize.feature.js +0 -93
- package/commonjs/src/webapi/features/postman.feature.js +0 -322
- package/commonjs/src/webapi/models/api-package-json.model.js +0 -58
- package/commonjs/src/webapi/models/auth-config.model.js +0 -24
- package/commonjs/src/webapi/models/controller-source-file.model.js +0 -190
- package/commonjs/src/webapi/models/github-ci.model.js +0 -193
- package/commonjs/src/webapi/models/main-source-file.model.js +0 -165
- package/commonjs/src/webapi/models/production-dockerfile.model.js +0 -75
- package/commonjs/src/webapi/models/sample-files.model.js +0 -58
- package/commonjs/src/webapi/models/webapi-image.model.js +0 -25
- package/commonjs/src/webapi/postman/hello-openapi.js +0 -173
- package/commonjs/src/webapi/postman/local-view.js +0 -182
- package/commonjs/src/webapi/postman/openapi-to-postman.js +0 -238
- package/commonjs/src/webapi/workspace-readme.js +0 -109
- package/commonjs/src/webapi.action.js +0 -28
- package/commonjs/src/webapi.command.js +0 -43
- package/es6/src/add-api.action.js +0 -27
- package/es6/src/add-api.command.js +0 -29
- package/es6/src/add.command.js +0 -12
- package/es6/src/add.module.js +0 -14
- package/es6/src/auth0-configure.action.js +0 -18
- package/es6/src/auth0-configure.command.js +0 -14
- package/es6/src/auth0-login.action.js +0 -18
- package/es6/src/auth0-login.command.js +0 -13
- package/es6/src/auth0.command.js +0 -12
- package/es6/src/auth0.module.js +0 -18
- package/es6/src/cli.js +0 -4
- package/es6/src/create.command.js +0 -12
- package/es6/src/create.module.js +0 -14
- package/es6/src/domain/prompt.js +0 -41
- package/es6/src/index.js +0 -9
- package/es6/src/models/compose.model.js +0 -254
- package/es6/src/models/config-yaml.model.js +0 -74
- package/es6/src/models/const.js +0 -20
- package/es6/src/models/dev-container.model.js +0 -162
- package/es6/src/models/docker-image.model.js +0 -235
- package/es6/src/models/env-file.model.js +0 -44
- package/es6/src/models/gitignore.model.js +0 -38
- package/es6/src/models/launch-json.model.js +0 -60
- package/es6/src/models/package-json.model.js +0 -141
- package/es6/src/models/project.model.js +0 -117
- package/es6/src/models/source-file.js +0 -1
- package/es6/src/models/tsconfig.model.js +0 -22
- package/es6/src/models/vitest-config.model.js +0 -23
- package/es6/src/models/vscode-settings.model.js +0 -19
- package/es6/src/program.js +0 -50
- package/es6/src/resolve-target-dir.js +0 -7
- package/es6/src/services/scaffold.service.js +0 -50
- package/es6/src/version.js +0 -34
- package/es6/src/webapi/api-name.js +0 -57
- package/es6/src/webapi/auth0-commands.js +0 -185
- package/es6/src/webapi/authorize-scopes.js +0 -55
- package/es6/src/webapi/cli-options.js +0 -34
- package/es6/src/webapi/const.js +0 -21
- package/es6/src/webapi/create-api.js +0 -245
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.js +0 -154
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/es6/src/webapi/devcontainer/host-scripts.js +0 -29
- package/es6/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/es6/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/es6/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/es6/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/es6/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/es6/src/webapi/features/auth.feature.js +0 -357
- package/es6/src/webapi/features/auth0-cli.js +0 -406
- package/es6/src/webapi/features/biome-format.js +0 -40
- package/es6/src/webapi/features/database.feature.js +0 -423
- package/es6/src/webapi/features/finalize.feature.js +0 -83
- package/es6/src/webapi/features/postman.feature.js +0 -256
- package/es6/src/webapi/models/api-package-json.model.js +0 -53
- package/es6/src/webapi/models/auth-config.model.js +0 -20
- package/es6/src/webapi/models/controller-source-file.model.js +0 -181
- package/es6/src/webapi/models/main-source-file.model.js +0 -161
- package/es6/src/webapi/models/production-dockerfile.model.js +0 -66
- package/es6/src/webapi/models/sample-files.model.js +0 -40
- package/es6/src/webapi/models/webapi-image.model.js +0 -18
- package/es6/src/webapi/postman/hello-openapi.js +0 -166
- package/es6/src/webapi/postman/local-view.js +0 -167
- package/es6/src/webapi/postman/openapi-to-postman.js +0 -179
- package/es6/src/webapi/workspace-readme.js +0 -88
- package/es6/src/webapi.action.js +0 -18
- package/es6/src/webapi.command.js +0 -33
- package/types/src/add-api.action.d.ts +0 -13
- package/types/src/add-api.command.d.ts +0 -7
- package/types/src/add.command.d.ts +0 -6
- package/types/src/add.module.d.ts +0 -14
- package/types/src/auth0-configure.action.d.ts +0 -7
- package/types/src/auth0-configure.command.d.ts +0 -6
- package/types/src/auth0-login.action.d.ts +0 -4
- package/types/src/auth0-login.command.d.ts +0 -6
- package/types/src/auth0.command.d.ts +0 -6
- package/types/src/auth0.module.d.ts +0 -18
- package/types/src/cli.d.ts +0 -2
- package/types/src/create.command.d.ts +0 -6
- package/types/src/create.module.d.ts +0 -14
- package/types/src/domain/prompt.d.ts +0 -4
- package/types/src/models/compose.model.d.ts +0 -73
- package/types/src/models/config-yaml.model.d.ts +0 -27
- package/types/src/models/const.d.ts +0 -20
- package/types/src/models/dev-container.model.d.ts +0 -49
- package/types/src/models/docker-image.model.d.ts +0 -90
- package/types/src/models/env-file.model.d.ts +0 -16
- package/types/src/models/gitignore.model.d.ts +0 -9
- package/types/src/models/launch-json.model.d.ts +0 -7
- package/types/src/models/package-json.model.d.ts +0 -36
- package/types/src/models/project.model.d.ts +0 -60
- package/types/src/models/tsconfig.model.d.ts +0 -5
- package/types/src/models/vitest-config.model.d.ts +0 -5
- package/types/src/models/vscode-settings.model.d.ts +0 -7
- package/types/src/program.d.ts +0 -8
- package/types/src/services/scaffold.service.d.ts +0 -22
- package/types/src/version.d.ts +0 -6
- package/types/src/webapi/api-name.d.ts +0 -14
- package/types/src/webapi/auth0-commands.d.ts +0 -11
- package/types/src/webapi/authorize-scopes.d.ts +0 -7
- package/types/src/webapi/cli-options.d.ts +0 -6
- package/types/src/webapi/create-api.d.ts +0 -15
- package/types/src/webapi/devcontainer/codebase-rag-mcp.d.ts +0 -32
- package/types/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/types/src/webapi/devcontainer/host-scripts.d.ts +0 -7
- package/types/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/types/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/types/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/types/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/types/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/types/src/webapi/features/auth.feature.d.ts +0 -10
- package/types/src/webapi/features/auth0-cli.d.ts +0 -84
- package/types/src/webapi/features/biome-format.d.ts +0 -5
- package/types/src/webapi/features/database.feature.d.ts +0 -7
- package/types/src/webapi/features/finalize.feature.d.ts +0 -2
- package/types/src/webapi/features/postman.feature.d.ts +0 -16
- package/types/src/webapi/models/api-package-json.model.d.ts +0 -18
- package/types/src/webapi/models/auth-config.model.d.ts +0 -26
- package/types/src/webapi/models/controller-source-file.model.d.ts +0 -46
- package/types/src/webapi/models/github-ci.model.d.ts +0 -9
- package/types/src/webapi/models/main-source-file.model.d.ts +0 -54
- package/types/src/webapi/models/production-dockerfile.model.d.ts +0 -18
- package/types/src/webapi/models/sample-files.model.d.ts +0 -7
- package/types/src/webapi/models/webapi-image.model.d.ts +0 -12
- package/types/src/webapi/postman/hello-openapi.d.ts +0 -19
- package/types/src/webapi/postman/local-view.d.ts +0 -17
- package/types/src/webapi/postman/openapi-to-postman.d.ts +0 -62
- package/types/src/webapi/workspace-readme.d.ts +0 -18
- package/types/src/webapi.action.d.ts +0 -14
- package/types/src/webapi.command.d.ts +0 -11
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import type { KeyedModel, SourceFile, SourceFileProvider } from "./source-file.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A sub-unit of the monorepo (e.g. `projects/api`).
|
|
7
|
+
*
|
|
8
|
+
* Models and files registered here produce paths relative to `dir`. The parent
|
|
9
|
+
* `MonoRepoScaffold` calls `files()` and prepends `dir/` to every path, so
|
|
10
|
+
* models remain unaware of where they live in the repo.
|
|
11
|
+
*/
|
|
12
|
+
export class ProjectScaffold {
|
|
13
|
+
private readonly _models = new Map<string, SourceFileProvider>();
|
|
14
|
+
private readonly _files: SourceFile[] = [];
|
|
15
|
+
|
|
16
|
+
public constructor(public readonly dir: string) {}
|
|
17
|
+
|
|
18
|
+
public addModel<T extends KeyedModel>(model: T): T {
|
|
19
|
+
this._models.set(model.key, model);
|
|
20
|
+
return model;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public addFile(provider: SourceFileProvider): this {
|
|
24
|
+
this._files.push(...provider.files());
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public getModel<T extends SourceFileProvider>(key: string): T | undefined {
|
|
29
|
+
return this._models.get(key) as T | undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public getModels<T extends SourceFileProvider>(ctor: new (...args: never[]) => T): T[] {
|
|
33
|
+
return [...this._models.values()].filter((m): m is T => m instanceof ctor);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Returns all files with `dir/` prepended to each path. */
|
|
37
|
+
public files(): SourceFile[] {
|
|
38
|
+
const all = [...this._files, ...[...this._models.values()].flatMap((m) => m.files())];
|
|
39
|
+
return all.map((f) => ({ filePath: `${this.dir}/${f.filePath}`, content: f.content }));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export abstract class WorkspaceScaffold {
|
|
44
|
+
public readonly files: SourceFile[] = [];
|
|
45
|
+
public targetDir: string = process.cwd();
|
|
46
|
+
private _name: string | undefined;
|
|
47
|
+
|
|
48
|
+
/** Project name — host directory basename, not the in-container mount path. */
|
|
49
|
+
public get name(): string {
|
|
50
|
+
return this._name ?? basename(this.targetDir);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public setName(name: string): this {
|
|
54
|
+
this._name = name;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Add an ad-hoc file provider whose output is included at render time. */
|
|
59
|
+
public addFile(provider: SourceFileProvider): this {
|
|
60
|
+
this.files.push(...provider.files());
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public abstract addModel<T extends KeyedModel>(model: T): T;
|
|
65
|
+
public abstract getModel<T extends SourceFileProvider>(key: string): T | undefined;
|
|
66
|
+
public abstract getModels<T extends SourceFileProvider>(
|
|
67
|
+
ctor: new (...args: never[]) => T,
|
|
68
|
+
): T[];
|
|
69
|
+
public abstract getProject(dir: string): ProjectScaffold | undefined;
|
|
70
|
+
public abstract getProjects(): readonly ProjectScaffold[];
|
|
71
|
+
public abstract scaffold(): Promise<void> | void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class MonoRepoScaffold extends WorkspaceScaffold {
|
|
75
|
+
private readonly _models = new Map<string, SourceFileProvider>();
|
|
76
|
+
private readonly _projects: ProjectScaffold[] = [];
|
|
77
|
+
|
|
78
|
+
/** Register a named project. */
|
|
79
|
+
public addProject(project: ProjectScaffold): ProjectScaffold {
|
|
80
|
+
this._projects.push(project);
|
|
81
|
+
return project;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Retrieve a previously-registered project by its directory path. */
|
|
85
|
+
public getProject(dir: string): ProjectScaffold | undefined {
|
|
86
|
+
return this._projects.find((p) => p.dir === dir);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** All registered projects, in registration order. */
|
|
90
|
+
public getProjects(): readonly ProjectScaffold[] {
|
|
91
|
+
return this._projects;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Register a named model at workspace level. */
|
|
95
|
+
public addModel<T extends KeyedModel>(model: T): T {
|
|
96
|
+
this._models.set(model.key, model);
|
|
97
|
+
return model;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Retrieve a model by key. Searches workspace models first, then all
|
|
102
|
+
* registered projects in order.
|
|
103
|
+
*/
|
|
104
|
+
public getModel<T extends SourceFileProvider>(key: string): T | undefined {
|
|
105
|
+
const m = this._models.get(key) as T | undefined;
|
|
106
|
+
if (m !== undefined) return m;
|
|
107
|
+
for (const project of this._projects) {
|
|
108
|
+
const pm = project.getModel<T>(key);
|
|
109
|
+
if (pm !== undefined) return pm;
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Retrieve all models of a given type. Searches workspace models and all
|
|
116
|
+
* projects, so features can discover controllers across multiple projects.
|
|
117
|
+
*/
|
|
118
|
+
public getModels<T extends SourceFileProvider>(ctor: new (...args: never[]) => T): T[] {
|
|
119
|
+
return [
|
|
120
|
+
...[...this._models.values()].filter((m): m is T => m instanceof ctor),
|
|
121
|
+
...this._projects.flatMap((p) => p.getModels(ctor)),
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Render all models and projects into files, then write everything to disk. */
|
|
126
|
+
public async scaffold(): Promise<void> {
|
|
127
|
+
const allFiles = [
|
|
128
|
+
...this.files,
|
|
129
|
+
...[...this._models.values()].flatMap((m) => m.files()),
|
|
130
|
+
...this._projects.flatMap((p) => p.files()),
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
await Promise.all(
|
|
134
|
+
allFiles.map(async (file) => {
|
|
135
|
+
const fullPath = join(this.targetDir, file.filePath);
|
|
136
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
137
|
+
await writeFile(fullPath, file.content);
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export interface SourceFileProvider {
|
|
2
|
-
|
|
2
|
+
files(): SourceFile[];
|
|
3
3
|
}
|
|
4
|
+
|
|
4
5
|
/** A SourceFileProvider that knows its own registration key. */
|
|
5
6
|
export interface KeyedModel extends SourceFileProvider {
|
|
6
|
-
|
|
7
|
+
readonly key: string;
|
|
7
8
|
}
|
|
9
|
+
|
|
8
10
|
export type SourceFile = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
filePath: string;
|
|
12
|
+
content: string;
|
|
11
13
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { WELL_KNOWN_FILES } from "./const.js";
|
|
2
|
+
import type { KeyedModel, SourceFile } from "./source-file.js";
|
|
3
|
+
|
|
4
|
+
export class TsConfig implements KeyedModel {
|
|
5
|
+
public readonly key = WELL_KNOWN_FILES.TSCONFIG;
|
|
6
|
+
|
|
7
|
+
public files(): SourceFile[] {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
filePath: "tsconfig.json",
|
|
11
|
+
content: JSON.stringify(
|
|
12
|
+
{
|
|
13
|
+
compilerOptions: {
|
|
14
|
+
target: "ES2022",
|
|
15
|
+
module: "NodeNext",
|
|
16
|
+
moduleResolution: "NodeNext",
|
|
17
|
+
strict: true,
|
|
18
|
+
skipLibCheck: true,
|
|
19
|
+
experimentalDecorators: true,
|
|
20
|
+
emitDecoratorMetadata: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
null,
|
|
24
|
+
2,
|
|
25
|
+
),
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { WELL_KNOWN_FILES } from "./const.js";
|
|
2
|
+
import type { KeyedModel, SourceFile } from "./source-file.js";
|
|
3
|
+
|
|
4
|
+
export class VitestConfig implements KeyedModel {
|
|
5
|
+
public readonly key = WELL_KNOWN_FILES.VITEST_CONFIG;
|
|
6
|
+
|
|
7
|
+
public files(): SourceFile[] {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
filePath: "vitest.config.ts",
|
|
11
|
+
content: [
|
|
12
|
+
'import { forgeBuild } from "@decohere/forge/vitest";',
|
|
13
|
+
'import { defineConfig } from "vitest/config";',
|
|
14
|
+
"",
|
|
15
|
+
"export default defineConfig({",
|
|
16
|
+
'\tplugins: [forgeBuild({ logLevel: "silent" })],',
|
|
17
|
+
"\ttest: {",
|
|
18
|
+
'\t\tinclude: ["projects/**/*.{test,spec}.?(c|m)[jt]s?(x)"],',
|
|
19
|
+
"\t},",
|
|
20
|
+
"});",
|
|
21
|
+
"",
|
|
22
|
+
].join("\n"),
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { WELL_KNOWN_FILES } from "./const.js";
|
|
2
|
+
import type { KeyedModel, SourceFile } from "./source-file.js";
|
|
3
|
+
|
|
4
|
+
export class VSCodeSettings implements KeyedModel {
|
|
5
|
+
public readonly key = WELL_KNOWN_FILES.VSCODE_SETTINGS;
|
|
6
|
+
|
|
7
|
+
public constructor(private readonly title: string) {}
|
|
8
|
+
|
|
9
|
+
public files(): SourceFile[] {
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
filePath: ".vscode/settings.json",
|
|
13
|
+
content: JSON.stringify(
|
|
14
|
+
{
|
|
15
|
+
"window.title": `${this.title}\${separator}\${activeEditorShort}`,
|
|
16
|
+
"window.titleSeparator": " | ",
|
|
17
|
+
},
|
|
18
|
+
null,
|
|
19
|
+
2,
|
|
20
|
+
),
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/program.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Command, CommanderError } from "commander";
|
|
2
|
+
import { CreateCliError } from "./webapi/api-name.js";
|
|
3
|
+
import {
|
|
4
|
+
addWebapiFeatureOptions,
|
|
5
|
+
collectName,
|
|
6
|
+
runScaffoldApi,
|
|
7
|
+
toApiOptions,
|
|
8
|
+
} from "./webapi/cli-options.js";
|
|
9
|
+
|
|
10
|
+
export function createProgram(): Command {
|
|
11
|
+
const program = new Command();
|
|
12
|
+
program.exitOverride();
|
|
13
|
+
program
|
|
14
|
+
.name("decohere")
|
|
15
|
+
.description("Decohere CLI")
|
|
16
|
+
.option("-d <directory>", "Target directory (default: current directory)", ".");
|
|
17
|
+
|
|
18
|
+
const create = program.command("create").description("Create a new Decohere workspace");
|
|
19
|
+
addWebapiFeatureOptions(
|
|
20
|
+
create
|
|
21
|
+
.command("webapi")
|
|
22
|
+
.argument("[directory]", "Target directory (default: -d or current directory)")
|
|
23
|
+
.option(
|
|
24
|
+
"--name <slug>",
|
|
25
|
+
"API name under projects/ (repeatable; default: api)",
|
|
26
|
+
collectName,
|
|
27
|
+
[] as string[],
|
|
28
|
+
),
|
|
29
|
+
).action(async (directory: string | undefined, _opts: unknown, cmd: Command) => {
|
|
30
|
+
await runScaffoldApi(toApiOptions(directory, cmd.optsWithGlobals()));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
addWebapiFeatureOptions(
|
|
34
|
+
program
|
|
35
|
+
.command("add")
|
|
36
|
+
.description("Add to an existing Decohere workspace")
|
|
37
|
+
.command("api")
|
|
38
|
+
.argument("<name>", "API name under projects/"),
|
|
39
|
+
).action(async (name: string, _opts: unknown, cmd: Command) => {
|
|
40
|
+
await runScaffoldApi(
|
|
41
|
+
toApiOptions(undefined, cmd.optsWithGlobals(), { names: [name], add: true }),
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return program;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function runCli(argv: string[] = process.argv): Promise<void> {
|
|
49
|
+
try {
|
|
50
|
+
await createProgram().parseAsync(argv);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err instanceof CommanderError) {
|
|
53
|
+
process.exit(err.exitCode);
|
|
54
|
+
}
|
|
55
|
+
if (err instanceof CreateCliError) {
|
|
56
|
+
console.error(`\n ${err.message}\n`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
console.error(err);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
process.exit(process.exitCode ?? 0);
|
|
63
|
+
}
|
|
@@ -2,4 +2,7 @@
|
|
|
2
2
|
* Resolve scaffold destination: positional `webapi my-app` wins over `-d`.
|
|
3
3
|
* `npm create … webapi -d my-app` drops `-d` (npm loglevel) and forwards `my-app`.
|
|
4
4
|
*/
|
|
5
|
-
export
|
|
5
|
+
export function resolveTargetDir(positional?: string, flag?: string): string {
|
|
6
|
+
const dir = positional?.trim() || flag?.trim() || ".";
|
|
7
|
+
return dir;
|
|
8
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { WorkspaceScaffold } from "../models/project.model.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A feature has three responsibilities:
|
|
5
|
+
* 1. `getOptions` — resolve provider selection and any follow-up questions.
|
|
6
|
+
* Receives the options collected so far; returns only the keys it owns.
|
|
7
|
+
* Short-circuits if the caller already supplied those keys via CLI flags.
|
|
8
|
+
* 2. `scaffold` — mutate the workspace models and/or write files using the
|
|
9
|
+
* fully-resolved options. Called after ALL features have gathered options
|
|
10
|
+
* but BEFORE files are written to disk.
|
|
11
|
+
* 3. `finalize` (optional) — run post-write actions (e.g. install dependencies).
|
|
12
|
+
* Called after workspace.scaffold() has flushed all files to disk.
|
|
13
|
+
*/
|
|
14
|
+
export interface Feature<TOptions extends object = object> {
|
|
15
|
+
getOptions(
|
|
16
|
+
workspace: WorkspaceScaffold,
|
|
17
|
+
cliOptions: Partial<TOptions>,
|
|
18
|
+
): Promise<Partial<TOptions>>;
|
|
19
|
+
scaffold(workspace: WorkspaceScaffold, options: TOptions): Promise<void> | void;
|
|
20
|
+
finalize?(workspace: WorkspaceScaffold, options: TOptions): Promise<void> | void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function scaffold(
|
|
24
|
+
workspace: WorkspaceScaffold,
|
|
25
|
+
cliOptions: object,
|
|
26
|
+
features: Feature[],
|
|
27
|
+
) {
|
|
28
|
+
const options = await getAllOptions(workspace, cliOptions, features);
|
|
29
|
+
// Features mutate workspace models first, then workspace renders to disk.
|
|
30
|
+
await scaffoldFeatures(workspace, options, features);
|
|
31
|
+
await scaffoldWorkspace(workspace, options);
|
|
32
|
+
// Post-write phase: install deps, print final messages, etc.
|
|
33
|
+
await finalizeFeatures(workspace, options, features);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function scaffoldWorkspace(workspace: WorkspaceScaffold, _options: object) {
|
|
37
|
+
await workspace.scaffold();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Iterate features in order, calling `getOptions` on each.
|
|
42
|
+
* Each feature receives the running merged options so later features can
|
|
43
|
+
* branch on decisions made by earlier ones (e.g. database choice affects auth).
|
|
44
|
+
* Results are shallow-merged, so features only need to return what they own.
|
|
45
|
+
*/
|
|
46
|
+
async function getAllOptions(
|
|
47
|
+
workspace: WorkspaceScaffold,
|
|
48
|
+
cliOptions: object,
|
|
49
|
+
features: Feature[],
|
|
50
|
+
): Promise<object> {
|
|
51
|
+
let options = { ...cliOptions };
|
|
52
|
+
for (const feature of features) {
|
|
53
|
+
const featureOptions = await feature.getOptions(workspace, options);
|
|
54
|
+
options = { ...options, ...featureOptions };
|
|
55
|
+
}
|
|
56
|
+
return options;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Iterate features in order, calling `scaffold` with the fully-resolved options.
|
|
61
|
+
* All option-gathering is complete before any scaffolding begins.
|
|
62
|
+
*/
|
|
63
|
+
async function scaffoldFeatures(
|
|
64
|
+
workspace: WorkspaceScaffold,
|
|
65
|
+
options: object,
|
|
66
|
+
features: Feature[],
|
|
67
|
+
): Promise<void> {
|
|
68
|
+
for (const feature of features) {
|
|
69
|
+
await feature.scaffold(workspace, options as never);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Iterate features in order, calling `finalize` on each (if defined).
|
|
75
|
+
* All files are on disk by this point.
|
|
76
|
+
*/
|
|
77
|
+
async function finalizeFeatures(
|
|
78
|
+
workspace: WorkspaceScaffold,
|
|
79
|
+
options: object,
|
|
80
|
+
features: Feature[],
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
for (const feature of features) {
|
|
83
|
+
if (feature.finalize) {
|
|
84
|
+
await feature.finalize(workspace, options as never);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const ScaffoldService = { scaffold };
|
package/src/version.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const _require = createRequire(import.meta.url);
|
|
6
|
+
|
|
7
|
+
type CreatePackageMetadata = {
|
|
8
|
+
name?: string;
|
|
9
|
+
version?: string;
|
|
10
|
+
decohere?: {
|
|
11
|
+
dockerImageRepository?: string;
|
|
12
|
+
devcontainerImageRepository?: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const DEFAULT_CREATE_VERSION = "0.0.1-dev";
|
|
17
|
+
const DEFAULT_CREATE_IMAGE_REPOSITORY = "ghcr.io/typesmiths/decohere/create";
|
|
18
|
+
const DEFAULT_DEVCONTAINER_IMAGE_REPOSITORY = "ghcr.io/typesmiths/decohere/devcontainer";
|
|
19
|
+
|
|
20
|
+
function resolveCreateMetadata() {
|
|
21
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
for (let i = 0; i < 5; i++) {
|
|
23
|
+
try {
|
|
24
|
+
const pkg = _require(join(dir, "package.json")) as CreatePackageMetadata;
|
|
25
|
+
if (pkg.name === "@decohere/scaffold") {
|
|
26
|
+
return {
|
|
27
|
+
version: pkg.version ?? DEFAULT_CREATE_VERSION,
|
|
28
|
+
imageRepository:
|
|
29
|
+
pkg.decohere?.dockerImageRepository ?? DEFAULT_CREATE_IMAGE_REPOSITORY,
|
|
30
|
+
devcontainerImageRepository:
|
|
31
|
+
pkg.decohere?.devcontainerImageRepository ?? DEFAULT_DEVCONTAINER_IMAGE_REPOSITORY,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
} catch {}
|
|
35
|
+
const parent = dirname(dir);
|
|
36
|
+
if (parent === dir) break;
|
|
37
|
+
dir = parent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
version: DEFAULT_CREATE_VERSION,
|
|
42
|
+
imageRepository: DEFAULT_CREATE_IMAGE_REPOSITORY,
|
|
43
|
+
devcontainerImageRepository: DEFAULT_DEVCONTAINER_IMAGE_REPOSITORY,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const createMetadata = resolveCreateMetadata();
|
|
48
|
+
|
|
49
|
+
/** Version of @decohere/create, used to pin scaffolded @decohere/* dependencies. */
|
|
50
|
+
export const CREATE_VERSION = createMetadata.version;
|
|
51
|
+
|
|
52
|
+
/** Canonical published Docker repository for the @decohere/create builder image. */
|
|
53
|
+
export const CREATE_IMAGE_REPOSITORY = createMetadata.imageRepository;
|
|
54
|
+
|
|
55
|
+
/** Published app Dev Container base (`docker pull` updates shared layers). */
|
|
56
|
+
export const DEVCONTAINER_IMAGE_REPOSITORY = createMetadata.devcontainerImageRepository;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { DEFAULT_WEBAPI_NAME } from "./const.js";
|
|
4
|
+
|
|
5
|
+
/** Lowercase npm-safe directory slug: `billing`, `admin-api`. */
|
|
6
|
+
export const API_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
7
|
+
|
|
8
|
+
export class CreateCliError extends Error {
|
|
9
|
+
public constructor(message: string) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "CreateCliError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function assertValidApiName(name: string): void {
|
|
16
|
+
if (!API_NAME_PATTERN.test(name)) {
|
|
17
|
+
throw new CreateCliError(
|
|
18
|
+
`Invalid API name "${name}". Use a lowercase slug starting with a letter, then letters, digits, or hyphens (e.g. billing).`,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Default `["api"]` when the caller omitted `--name`. Rejects duplicates and invalid slugs. */
|
|
24
|
+
export function parseApiNames(raw: readonly string[] | undefined): string[] {
|
|
25
|
+
const names = raw != null && raw.length > 0 ? [...raw] : [DEFAULT_WEBAPI_NAME];
|
|
26
|
+
const seen = new Set<string>();
|
|
27
|
+
for (const name of names) {
|
|
28
|
+
assertValidApiName(name);
|
|
29
|
+
if (seen.has(name)) {
|
|
30
|
+
throw new CreateCliError(`Duplicate API name "${name}".`);
|
|
31
|
+
}
|
|
32
|
+
seen.add(name);
|
|
33
|
+
}
|
|
34
|
+
return names;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isDecohereWorkspace(dir: string): boolean {
|
|
38
|
+
try {
|
|
39
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
|
|
40
|
+
scripts?: Record<string, string>;
|
|
41
|
+
};
|
|
42
|
+
const build = pkg.scripts?.build;
|
|
43
|
+
return typeof build === "string" && build.includes("forge");
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Directory names under `projects/` that already have a `package.json`. */
|
|
50
|
+
export function listExistingApiNames(dir: string): string[] {
|
|
51
|
+
const projectsDir = join(dir, "projects");
|
|
52
|
+
try {
|
|
53
|
+
return readdirSync(projectsDir, { withFileTypes: true })
|
|
54
|
+
.filter(
|
|
55
|
+
(entry) =>
|
|
56
|
+
entry.isDirectory() && existsSync(join(projectsDir, entry.name, "package.json")),
|
|
57
|
+
)
|
|
58
|
+
.map((entry) => entry.name)
|
|
59
|
+
.sort();
|
|
60
|
+
} catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** `billing` → `BILLING`, `admin-api` → `ADMIN_API` — prefix for `{PREFIX}_DB_HOST`. */
|
|
66
|
+
export function apiEnvPrefix(apiName: string): string {
|
|
67
|
+
return apiName.replace(/-/g, "_").toUpperCase();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function apiNameFromProjectDir(dir: string): string {
|
|
71
|
+
const slash = dir.lastIndexOf("/");
|
|
72
|
+
return slash >= 0 ? dir.slice(slash + 1) : dir;
|
|
73
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { resolveTargetDir } from "../resolve-target-dir.js";
|
|
3
|
+
import { parseApiNames } from "./api-name.js";
|
|
4
|
+
import type { ApiOptions } from "./create-api.js";
|
|
5
|
+
import { scaffoldApi } from "./create-api.js";
|
|
6
|
+
|
|
7
|
+
export function collectName(value: string, previous: string[]): string[] {
|
|
8
|
+
previous.push(value);
|
|
9
|
+
return previous;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function addWebapiFeatureOptions(cmd: Command): Command {
|
|
13
|
+
return cmd
|
|
14
|
+
.option("--database <provider>", "Database provider: none | postgres/typeorm")
|
|
15
|
+
.option("--auth <provider>", "Auth provider: none | auth0")
|
|
16
|
+
.option("--postman <provider>", "Postman provider: none | local | api")
|
|
17
|
+
.option("-y, --yes", "Install dependencies without prompting")
|
|
18
|
+
.option("--install <mode>", "Dependency install mode: ask | yes | no", "ask");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function toApiOptions(
|
|
22
|
+
directory: string | undefined,
|
|
23
|
+
opts: Record<string, unknown>,
|
|
24
|
+
extra: Partial<ApiOptions> = {},
|
|
25
|
+
): ApiOptions {
|
|
26
|
+
const installMode = String(opts.install ?? "ask").toLowerCase();
|
|
27
|
+
const names = Array.isArray(opts.name)
|
|
28
|
+
? (opts.name as string[])
|
|
29
|
+
: typeof opts.name === "string"
|
|
30
|
+
? [opts.name]
|
|
31
|
+
: extra.names;
|
|
32
|
+
return {
|
|
33
|
+
d: resolveTargetDir(directory, typeof opts.d === "string" ? opts.d : undefined),
|
|
34
|
+
databaseProvider: opts.database as ApiOptions["databaseProvider"],
|
|
35
|
+
authProvider: opts.auth as ApiOptions["authProvider"],
|
|
36
|
+
postmanProvider: opts.postman as ApiOptions["postmanProvider"],
|
|
37
|
+
installDependencies:
|
|
38
|
+
opts.yes === true
|
|
39
|
+
? "yes"
|
|
40
|
+
: installMode === "yes" || installMode === "no"
|
|
41
|
+
? installMode
|
|
42
|
+
: undefined,
|
|
43
|
+
names,
|
|
44
|
+
...extra,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function runScaffoldApi(normalized: ApiOptions): Promise<void> {
|
|
49
|
+
parseApiNames(normalized.names);
|
|
50
|
+
const { closePrompt } = await import("../domain/prompt.js");
|
|
51
|
+
try {
|
|
52
|
+
await scaffoldApi(normalized);
|
|
53
|
+
} finally {
|
|
54
|
+
closePrompt();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
/** Default API project slug when `--name` is omitted. */
|
|
2
|
-
export
|
|
2
|
+
export const DEFAULT_WEBAPI_NAME = "api";
|
|
3
|
+
|
|
3
4
|
/** Relative path of an API project inside the monorepo. */
|
|
4
|
-
export
|
|
5
|
+
export function webapiProjectDir(name: string): string {
|
|
6
|
+
return `projects/${name}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* Forge release entry for a scaffolded API (`pack: true` → per-target outDir).
|
|
7
11
|
* Keep in sync with root `package.json` `"start"` / `"start:<name>"` and Dockerfiles.
|
|
8
12
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
export function webapiReleaseEntry(name: string): string {
|
|
14
|
+
return `dist/release/${webapiProjectDir(name)}/es6/src/main.js`;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
/** npm package name for a scaffolded API (`flux serve --project`). */
|
|
13
|
-
export
|
|
18
|
+
export function webapiPackageName(workspaceName: string, apiName: string): string {
|
|
19
|
+
return `@${workspaceName}/${apiName}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
/** Local listen port for the nth API (0-based). Production uses `PORT`. */
|
|
15
|
-
export
|
|
23
|
+
export function webapiPort(index: number): number {
|
|
24
|
+
return 3000 + index;
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
/** @deprecated Use `webapiProjectDir(name)`. Default path for the first API. */
|
|
17
|
-
export
|
|
28
|
+
export const WEBAPI_PROJECT_DIR = webapiProjectDir(DEFAULT_WEBAPI_NAME);
|
|
29
|
+
|
|
18
30
|
/** @deprecated Use `webapiReleaseEntry(name)`. Default start entry for the first API. */
|
|
19
|
-
export
|
|
31
|
+
export const WEBAPI_RELEASE_ENTRY = webapiReleaseEntry(DEFAULT_WEBAPI_NAME);
|