bazis 0.96.1 → 0.96.2
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/README.md +46 -6
- package/cli/generateProject.ts +26 -9
- package/cli/main.ts +6 -3
- package/cli/parseCli.ts +7 -6
- package/core/infra/connectors/codex/CodexAppServerClient.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,18 +7,58 @@ per-environment defaults, JWT, WebSocket, gRPC, background services, AI agents
|
|
|
7
7
|
and a CLI that scaffolds, runs, tests and compiles your app into a single
|
|
8
8
|
executable.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
no build step and no separate type package.
|
|
10
|
+
## Requirements
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
- **Bun 1.4.0 or newer. Node.js and Deno are not supported.** The package
|
|
13
|
+
ships TypeScript sources with standard decorators and no build step; Bun
|
|
14
|
+
runs them directly. Node.js refuses to load TypeScript from `node_modules`
|
|
15
|
+
(`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), and the runtime uses Bun
|
|
16
|
+
APIs throughout: `Bun.serve`, `Bun.SQL`, the built-in Redis client,
|
|
17
|
+
`bun:test`, `bun build --compile`.
|
|
18
|
+
- TypeScript 5.9 or newer as a peer dependency: the code generator reads
|
|
19
|
+
your sources through the TypeScript compiler API. Nothing is compiled to
|
|
20
|
+
JavaScript.
|
|
21
|
+
- PostgreSQL for the ORM.
|
|
22
|
+
- Qualified platforms: macOS arm64, Linux x64 and arm64.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
The package is `bazis` on npm. Install
|
|
27
|
+
it with Bun; `npm install` would download it too, but the code only runs on Bun.
|
|
28
|
+
|
|
29
|
+
**A new project** (recommended): the CLI creates the app and adds the
|
|
30
|
+
dependency `"bazis": "^<current version>"`:
|
|
14
31
|
|
|
15
32
|
```sh
|
|
16
|
-
bunx bazis new MyApp
|
|
33
|
+
bunx bazis new MyApp # creates ./my-app; nothing is copied, the package comes from npm
|
|
17
34
|
cd my-app
|
|
18
|
-
bun install
|
|
35
|
+
bun install # downloads bazis and its peer dependency typescript
|
|
19
36
|
bunx bazis dev # GET http://127.0.0.1:3000/health
|
|
20
37
|
```
|
|
21
38
|
|
|
39
|
+
**An existing Bun project:**
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
bun add bazis # or a fixed version: bun add bazis@0.96.2
|
|
43
|
+
bun add -d typescript @types/bun
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then take `tsconfig.json`, `bazis.config.json` and `src/index.ts` from a
|
|
47
|
+
project made by `bazis new` as the starting point, and run `bunx bazis codegen`.
|
|
48
|
+
|
|
49
|
+
| Task | Command |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| Show the installed version | `bun pm ls \| grep bazis` |
|
|
52
|
+
| Update within the version range | `bun update bazis` |
|
|
53
|
+
| Move to the latest release | `bun add bazis@latest` |
|
|
54
|
+
| A project that must build without npm access | `bunx bazis new MyApp --vendor` (copies the package into `vendor/bazis`) |
|
|
55
|
+
|
|
56
|
+
Every release is published from GitHub Actions with
|
|
57
|
+
[npm provenance](https://docs.npmjs.com/generating-provenance-statements): the
|
|
58
|
+
npm page shows the source commit and the workflow that built it. The release
|
|
59
|
+
tarball is also attached to each
|
|
60
|
+
[GitHub Release](https://github.com/asus97178/bazis/releases).
|
|
61
|
+
|
|
22
62
|
`bazis new` creates a project with `src/index.ts`, a root `AppModule`,
|
|
23
63
|
`bazis.config.json`, scripts, a `/health` test and an `.env.example`.
|
|
24
64
|
|
|
@@ -26,7 +66,7 @@ bunx bazis dev # GET http://127.0.0.1:3000/health
|
|
|
26
66
|
|
|
27
67
|
| Command | What it does |
|
|
28
68
|
| --- | --- |
|
|
29
|
-
| `bazis new <Name
|
|
69
|
+
| `bazis new <Name> [--vendor]` | Create a project in `./<name>` that depends on `bazis` from npm; `--vendor` copies the package into `vendor/bazis` instead |
|
|
30
70
|
| `bazis g module <Name> --empty\|--minimal\|--full` | Add a module: empty; CRUD with ORM, validation and paging; or the same with auth guards |
|
|
31
71
|
| `bazis g pack <Name> --parts a,b` | Add a composite module made of several atomic ones |
|
|
32
72
|
| `bazis codegen` | Regenerate dependency wiring into `src/generated/bazis` |
|
package/cli/generateProject.ts
CHANGED
|
@@ -8,8 +8,10 @@ export interface GenerateProjectOptions {
|
|
|
8
8
|
readonly outputPath?: string;
|
|
9
9
|
/** Local Bazis package source; defaults to this checkout's src/bazis. */
|
|
10
10
|
readonly frameworkPath?: string;
|
|
11
|
-
/** Opt into a live link to the source checkout instead of
|
|
11
|
+
/** Opt into a live link to the source checkout instead of the npm dependency. */
|
|
12
12
|
readonly linkFramework?: boolean;
|
|
13
|
+
/** Copy the package into vendor/bazis instead of depending on npm (offline projects). */
|
|
14
|
+
readonly vendor?: boolean;
|
|
13
15
|
readonly dryRun?: boolean;
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -18,8 +20,11 @@ export interface GenerateProjectResult {
|
|
|
18
20
|
/** Application scaffold files; framework sources are summarized separately. */
|
|
19
21
|
readonly files: readonly string[];
|
|
20
22
|
readonly dryRun: boolean;
|
|
21
|
-
|
|
23
|
+
/** registry: `"bazis": "^<version>"` from npm; snapshot: vendor/bazis copy; link: `file:` to a checkout. */
|
|
24
|
+
readonly frameworkMode: FrameworkMode;
|
|
22
25
|
readonly frameworkFileCount: number;
|
|
26
|
+
/** The dependency written to package.json. */
|
|
27
|
+
readonly dependency: string;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
/** Create a separate application without mutating the framework checkout. */
|
|
@@ -35,16 +40,17 @@ export async function generateProject(options: GenerateProjectOptions): Promise<
|
|
|
35
40
|
const projectDir = path.join(parent, path.basename(requested));
|
|
36
41
|
if (await exists(projectDir)) throw new Error(`Project path already exists: ${projectDir}`);
|
|
37
42
|
|
|
43
|
+
if (options.linkFramework && options.vendor) throw new Error("--link-framework and --vendor cannot be combined.");
|
|
38
44
|
const frameworkDir = await resolveFramework(options.frameworkPath);
|
|
39
45
|
if (projectDir.startsWith(`${frameworkDir}${path.sep}`)) {
|
|
40
46
|
throw new Error("Project directory must be outside the Bazis package.");
|
|
41
47
|
}
|
|
42
48
|
const dependencyPath = path.relative(projectDir, frameworkDir).replaceAll("\\", "/");
|
|
43
|
-
const frameworkMode = options.linkFramework ? "link" : "snapshot";
|
|
44
|
-
const frameworkFiles =
|
|
45
|
-
const dependency =
|
|
49
|
+
const frameworkMode: FrameworkMode = options.linkFramework ? "link" : options.vendor ? "snapshot" : "registry";
|
|
50
|
+
const frameworkFiles = frameworkMode === "snapshot" ? await collectFrameworkFiles(frameworkDir) : [];
|
|
51
|
+
const dependency = frameworkMode === "link"
|
|
46
52
|
? `file:${dependencyPath.startsWith(".") ? dependencyPath : `./${dependencyPath}`}`
|
|
47
|
-
: "file:./vendor/bazis"
|
|
53
|
+
: frameworkMode === "snapshot" ? "file:./vendor/bazis" : `^${await frameworkVersion(frameworkDir)}`;
|
|
48
54
|
const files = buildProjectFiles(name, dependency, frameworkMode);
|
|
49
55
|
if (!options.dryRun) {
|
|
50
56
|
const staged = await mkdtemp(path.join(parent, `.${name}.bazis-`));
|
|
@@ -67,7 +73,16 @@ export async function generateProject(options: GenerateProjectOptions): Promise<
|
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
return { projectDir, files: files.map(([relative]) => path.join(projectDir, relative)), dryRun: options.dryRun === true,
|
|
70
|
-
frameworkMode, frameworkFileCount: frameworkFiles.length };
|
|
76
|
+
frameworkMode, frameworkFileCount: frameworkFiles.length, dependency };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type FrameworkMode = "registry" | "snapshot" | "link";
|
|
80
|
+
|
|
81
|
+
/** The version the CLI belongs to; a new project depends on the same release line. */
|
|
82
|
+
async function frameworkVersion(directory: string): Promise<string> {
|
|
83
|
+
const version = JSON.parse(await readFile(path.join(directory, "package.json"), "utf8")).version;
|
|
84
|
+
if (typeof version !== "string" || !/^\d+\.\d+\.\d+$/.test(version)) throw new Error(`Bazis package has no release version: ${String(version)}`);
|
|
85
|
+
return version;
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
/** Package sources only: do not carry checkout state, dependencies or test fixtures. */
|
|
@@ -122,7 +137,7 @@ async function exists(file: string): Promise<boolean> {
|
|
|
122
137
|
}
|
|
123
138
|
}
|
|
124
139
|
|
|
125
|
-
function buildProjectFiles(name: string, dependency: string, frameworkMode:
|
|
140
|
+
function buildProjectFiles(name: string, dependency: string, frameworkMode: FrameworkMode): readonly (readonly [string, string])[] {
|
|
126
141
|
const manifest = {
|
|
127
142
|
name, version: "0.1.0", private: true, type: "module",
|
|
128
143
|
scripts: {
|
|
@@ -156,7 +171,9 @@ function buildProjectFiles(name: string, dependency: string, frameworkMode: "sna
|
|
|
156
171
|
["docs/architecture/MODULE_ARCHITECTURE.md", "# Application module architecture\n\n`src/index.ts` calls `runApp`; `src/app/modules/App.module.ts` composes feature modules through `imports`. The application root owns no domain logic.\n\nOne self-contained function is an atomic module. It owns its data, services, HTTP and background handlers. A composite module is only for several independent functions; its root does composition. Layers and file counts alone do not create submodules.\n\nCreate new modules only with `bunx bazis g module <Name> --empty|--minimal|--full` or `bunx bazis g pack <Name> --parts <a,b>`. Before implementing, define the responsibility and public entries, then fill in the generated `MODULE.md`: fields, errors, dependencies, exports and checks. Use the public APIs of the `bazis` package and its DI and ORM. Do not edit `src/generated/` by hand; run `bunx bazis codegen`.\n\n`--minimal` creates a sample CRUD with the ORM. To run it the application needs a database provider and a ready schema. For a first function without a database use `--empty`. Check types and the binary build after changes that affect startup.\n"],
|
|
157
172
|
["src/app/modules/App.module.ts", 'import { Module } from "bazis/core/di";\n\n@Module({ imports: [], exports: [] })\nexport class AppModule {}\n'],
|
|
158
173
|
["src/index.ts", 'import { runApp } from "bazis/core/app";\nimport { AppModule } from "./app/modules/App.module";\nimport { registerBazisGeneratedRuntime } from "./generated/bazis/runtime";\n\nawait registerBazisGeneratedRuntime();\nawait runApp(AppModule, { http: { hostname: process.env.HOST ?? "127.0.0.1", port: Number(process.env.PORT ?? 3000), health: true } });\n'],
|
|
159
|
-
["README.md", `# ${name}\n\
|
|
174
|
+
["README.md", `# ${name}\n\nA [bazis](https://www.npmjs.com/package/bazis) application. ${frameworkMode === "registry"
|
|
175
|
+
? "The framework comes from npm as the `bazis` dependency; update it with `bun update bazis`."
|
|
176
|
+
: frameworkMode === "snapshot"
|
|
160
177
|
? "The framework package is copied to `vendor/bazis`; keep it in Git and move it with the project. The framework checkout is no longer needed. Framework updates do not reach this copy automatically."
|
|
161
178
|
: "The `bazis` dependency links an external local checkout (`--link-framework`). Moving the project needs the same package and an updated path in `package.json`."}\nRead the [local architecture](docs/architecture/MODULE_ARCHITECTURE.md) before changing modules.\n\nNeeds Bun ≥ 1.4.0. Settings go to \`.env\` (example: \`.env.example\`).\n\n\`\`\`sh\nbun install\nbunx bazis dev\n# GET http://127.0.0.1:3000/health\n# If the port is busy: PORT=3100 bunx bazis dev\n\`\`\`\n\nAdd an atomic module from the project root: \`bunx bazis g module Task --empty\`.\nAfter filling in its passport and implementation, run \`bunx bazis codegen\`.\nTests: \`bunx bazis test\`. Typecheck: \`bunx bazis build\`. Binary: \`bunx bazis build --bin\`, run it with \`bun run start\`.\n\n\`--minimal\` creates a sample CRUD with paging (20 records by default, at most 100 over HTTP); it needs a database provider and a schema to run. The service returns \`PageResult\`; the HTTP controller builds JSON:API. \`DbContext.saveChanges()\` saves every change of its context. In ORM predicates combine conditions with \`.and()\` and \`.or()\`; codegen rejects \`&&\` and \`||\` between predicates.\n`],
|
|
162
179
|
];
|
package/cli/main.ts
CHANGED
|
@@ -22,7 +22,8 @@ export const USAGE = `Usage:
|
|
|
22
22
|
Generation options:
|
|
23
23
|
--path <directory> Exact project directory (new only; default: ./<name>)
|
|
24
24
|
--framework <path> Local bazis package directory (new only)
|
|
25
|
-
--
|
|
25
|
+
--vendor Copy the package into vendor/bazis instead of the npm dependency (new only)
|
|
26
|
+
--link-framework Depend on that local checkout through a file: link (new only)
|
|
26
27
|
--modules-root <path> Modules root (default: src/app/modules)
|
|
27
28
|
--app-module <path> Host module (default: {modules-root}/App.module.ts)
|
|
28
29
|
--empty Module entry and MODULE.md only
|
|
@@ -83,10 +84,12 @@ export async function runCli(argv: readonly string[], runtime: CliRuntime = defa
|
|
|
83
84
|
if (parsed.kind === "test") return await runTest(process.cwd(), parsed.args, runtime.codegen);
|
|
84
85
|
if (parsed.kind === "build") return await runBuild(process.cwd(), { bin: parsed.bin, outfile: parsed.outfile }, runtime.codegen, runtime.log);
|
|
85
86
|
if (parsed.kind === "new") {
|
|
86
|
-
const result = await generateProject({ name: parsed.name, outputPath: parsed.outputPath, frameworkPath: parsed.frameworkPath, linkFramework: parsed.linkFramework, dryRun: parsed.dryRun });
|
|
87
|
+
const result = await generateProject({ name: parsed.name, outputPath: parsed.outputPath, frameworkPath: parsed.frameworkPath, linkFramework: parsed.linkFramework, vendor: parsed.vendor, dryRun: parsed.dryRun });
|
|
87
88
|
runtime.log(`[bazis] ${result.dryRun ? "planned" : "created"} project: ${result.projectDir}`);
|
|
88
89
|
for (const file of result.files) runtime.log(` + ${file}`);
|
|
89
|
-
runtime.log(result.frameworkMode === "
|
|
90
|
+
runtime.log(result.frameworkMode === "registry"
|
|
91
|
+
? `[bazis] Framework dependency: "bazis": "${result.dependency}" from npm.`
|
|
92
|
+
: result.frameworkMode === "snapshot"
|
|
90
93
|
? `[bazis] vendor/bazis: ${result.frameworkFileCount} package files${result.dryRun ? " planned" : " copied"}. Keep this directory in version control.`
|
|
91
94
|
: "[bazis] Framework is linked to an external checkout (--link-framework).");
|
|
92
95
|
if (!result.dryRun) runtime.log(`[bazis] Next: cd ${result.projectDir} && bun install && bunx bazis dev`);
|
package/cli/parseCli.ts
CHANGED
|
@@ -18,7 +18,7 @@ export interface ParsedGenerateArgs {
|
|
|
18
18
|
|
|
19
19
|
export type ParseCliResult =
|
|
20
20
|
| { readonly kind: "help"; readonly help: boolean }
|
|
21
|
-
| { readonly kind: "new"; readonly name: string; readonly outputPath?: string; readonly frameworkPath?: string; readonly linkFramework: boolean; readonly dryRun: boolean }
|
|
21
|
+
| { readonly kind: "new"; readonly name: string; readonly outputPath?: string; readonly frameworkPath?: string; readonly linkFramework: boolean; readonly vendor: boolean; readonly dryRun: boolean }
|
|
22
22
|
| { readonly kind: "generate"; readonly args: ParsedGenerateArgs }
|
|
23
23
|
| { readonly kind: "codegen"; readonly target?: string }
|
|
24
24
|
| { readonly kind: "dev"; readonly watch: boolean }
|
|
@@ -31,7 +31,7 @@ const GENERATOR_ALIASES = new Map<string, "module" | "pack">([
|
|
|
31
31
|
["pack", "pack"], ["p", "pack"], ["module-pack", "pack"],
|
|
32
32
|
]);
|
|
33
33
|
const VALUE_OPTIONS = new Set(["--modules-root", "--app-module", "--parts", "--target", "--path", "--framework", "--outfile"]);
|
|
34
|
-
const FLAG_OPTIONS = new Set(["--no-register", "--force", "--full", "--enterprise", "--minimal", "--empty", "--dry-run", "--no-codegen", "--link-framework", "--bin", "--watch"]);
|
|
34
|
+
const FLAG_OPTIONS = new Set(["--no-register", "--force", "--full", "--enterprise", "--minimal", "--empty", "--dry-run", "--no-codegen", "--link-framework", "--vendor", "--bin", "--watch"]);
|
|
35
35
|
|
|
36
36
|
/** Parsing is pure: help and invalid input can never start generation. */
|
|
37
37
|
export function parseCliArgs(argv: readonly string[]): ParseCliResult {
|
|
@@ -64,18 +64,19 @@ export function parseCliArgs(argv: readonly string[]): ParseCliResult {
|
|
|
64
64
|
const [command, generatorToken, name] = positional;
|
|
65
65
|
const target = options.get("--target");
|
|
66
66
|
if (command === "new") {
|
|
67
|
-
if (positional.length !== 2) return error("Use: bazis new <Name> [--path <directory>] [--framework <directory>] [--link-framework] [--dry-run]");
|
|
67
|
+
if (positional.length !== 2) return error("Use: bazis new <Name> [--path <directory>] [--framework <directory>] [--vendor | --link-framework] [--dry-run]");
|
|
68
68
|
for (const option of options.keys()) {
|
|
69
|
-
if (option !== "--path" && option !== "--framework" && option !== "--link-framework" && option !== "--dry-run") {
|
|
69
|
+
if (option !== "--path" && option !== "--framework" && option !== "--link-framework" && option !== "--vendor" && option !== "--dry-run") {
|
|
70
70
|
return error(`Option ${option} is not supported by new.`);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
if (options.has("--vendor") && options.has("--link-framework")) return error("--vendor and --link-framework cannot be combined.");
|
|
73
74
|
try {
|
|
74
75
|
parseModuleName(generatorToken!);
|
|
75
76
|
} catch (cause) {
|
|
76
77
|
return error(cause instanceof Error ? cause.message : String(cause));
|
|
77
78
|
}
|
|
78
|
-
return { kind: "new", name: generatorToken!, outputPath: options.get("--path"), frameworkPath: options.get("--framework"), linkFramework: options.has("--link-framework"), dryRun: options.has("--dry-run") };
|
|
79
|
+
return { kind: "new", name: generatorToken!, outputPath: options.get("--path"), frameworkPath: options.get("--framework"), linkFramework: options.has("--link-framework"), vendor: options.has("--vendor"), dryRun: options.has("--dry-run") };
|
|
79
80
|
}
|
|
80
81
|
if (target !== undefined && !/^[a-z][a-z0-9-]*$/.test(target)) {
|
|
81
82
|
return error("Target must be a codegen target name or all.");
|
|
@@ -105,7 +106,7 @@ export function parseCliArgs(argv: readonly string[]): ParseCliResult {
|
|
|
105
106
|
return error(`Unknown command: ${command ?? "(missing)"}. Use: bazis --help`);
|
|
106
107
|
}
|
|
107
108
|
if (generatorToken === undefined) return error("Generator is required: module (m) or pack (p).");
|
|
108
|
-
if (options.has("--path") || options.has("--framework") || options.has("--link-framework")) return error("--path, --framework
|
|
109
|
+
if (options.has("--path") || options.has("--framework") || options.has("--link-framework") || options.has("--vendor")) return error("--path, --framework, --link-framework and --vendor are only supported by new.");
|
|
109
110
|
const generator = GENERATOR_ALIASES.get(generatorToken);
|
|
110
111
|
if (generator === undefined) return error(`Unknown generator: ${generatorToken}. Supported: module (m), pack (p)`);
|
|
111
112
|
if (name === undefined) return error("Module name is required.");
|
|
@@ -232,7 +232,7 @@ export class CodexAppServerClient implements CodexClient {
|
|
|
232
232
|
if (method === "account/updated") this.modelCache = undefined;
|
|
233
233
|
}, () => { this.clearLogin(); this.modelCache = undefined; });
|
|
234
234
|
const initialized = await client.request("initialize", {
|
|
235
|
-
clientInfo: { name: "bazis", title: "Bazis", version: "0.96.
|
|
235
|
+
clientInfo: { name: "bazis", title: "Bazis", version: "0.96.2" }, capabilities: { experimentalApi: true },
|
|
236
236
|
}, 15_000, signal);
|
|
237
237
|
if (resolve(text(initialized.codexHome, 4096)) !== this.paths.home) throw new CodexError("PROTOCOL_ERROR");
|
|
238
238
|
client.notify("initialized");
|
package/package.json
CHANGED