bazis 0.97.2 → 0.97.3

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/cli/build.ts CHANGED
@@ -13,7 +13,7 @@ export interface BuildOptions {
13
13
  }
14
14
 
15
15
  /**
16
- * `bazis dev`: codegen, then run the default target's entrypoint from source.
16
+ * `bazis dev`: codegen of every target, then run the default target's entrypoint from source.
17
17
  * With `watch`, a change under src/ (except src/generated) stops the app,
18
18
  * reruns codegen and starts it again; a failed codegen waits for the next change.
19
19
  * The app runs as `development` unless BAZIS_ENV is set in the shell.
@@ -28,7 +28,7 @@ export async function runDev(cwd: string, codegen: Codegen, log: (message: strin
28
28
  const entry = await projectEntry(cwd);
29
29
  const env = devEnvironment(process.env);
30
30
  if (!options.watch) {
31
- const generated = await codegen(cwd);
31
+ const generated = await codegen(cwd, "all");
32
32
  if (generated !== 0) return generated;
33
33
  const child = Bun.spawn([bun, "run", entry], { cwd, env, stdin: "inherit", stdout: "inherit", stderr: "inherit" });
34
34
  // The app owns graceful shutdown; forward the signal instead of dying first.
@@ -55,7 +55,7 @@ export async function runDev(cwd: string, codegen: Codegen, log: (message: strin
55
55
  await running.exited;
56
56
  };
57
57
  const start = async () => {
58
- if (await codegen(cwd) !== 0) { log("[bazis] codegen failed; waiting for changes..."); return; }
58
+ if (await codegen(cwd, "all") !== 0) { log("[bazis] codegen failed; waiting for changes..."); return; }
59
59
  child = Bun.spawn([bun, "run", entry], { cwd, env, stdin: "inherit", stdout: "inherit", stderr: "inherit" });
60
60
  };
61
61
  const watcher = watch(path.join(cwd, "src"), { recursive: true }, (_event, file) => {
@@ -84,16 +84,16 @@ export async function runDev(cwd: string, codegen: Codegen, log: (message: strin
84
84
  }
85
85
  }
86
86
 
87
- /** `bazis test [args]`: codegen, then `bun test` with the given arguments. */
87
+ /** `bazis test [args]`: codegen of every target, then `bun test` with the given arguments. */
88
88
  export async function runTest(cwd: string, args: readonly string[], codegen: Codegen): Promise<number> {
89
- const generated = await codegen(cwd);
89
+ const generated = await codegen(cwd, "all");
90
90
  if (generated !== 0) return generated;
91
91
  return await Bun.spawn([await resolveBun(cwd), "test", ...args], { cwd, stdin: "inherit", stdout: "inherit", stderr: "inherit" }).exited;
92
92
  }
93
93
 
94
- /** `bazis build [--bin]`: codegen and typecheck; with `bin`, also compile. */
94
+ /** `bazis build [--bin]`: codegen of every target and typecheck; with `bin`, also compile. */
95
95
  export async function runBuild(cwd: string, options: BuildOptions, codegen: Codegen, log: (message: string) => void): Promise<number> {
96
- const generated = await codegen(cwd);
96
+ const generated = await codegen(cwd, "all");
97
97
  if (generated !== 0) return generated;
98
98
  const bun = await resolveBun(cwd);
99
99
  const tsc = path.join(cwd, "node_modules/typescript/bin/tsc");
package/cli/main.ts CHANGED
@@ -115,7 +115,7 @@ export async function runCli(argv: readonly string[], runtime: CliRuntime = defa
115
115
  runtime.log(`[bazis] codegen skipped: ${!args.codegen ? "--no-codegen" : "module is not connected by this command"}.`);
116
116
  return 0;
117
117
  }
118
- runtime.log("[bazis] running di:generate...");
118
+ runtime.log("[bazis] running codegen...");
119
119
  const exitCode = await runtime.codegen(process.cwd(), args.target);
120
120
  if (exitCode !== 0) runtime.error("[bazis] codegen failed; scaffold files were kept. Fix the error and run bazis codegen.");
121
121
  return exitCode;
@@ -1,4 +1,4 @@
1
- // This file is auto-generated by `bun run di:generate`.
1
+ // This file is auto-generated by `bazis codegen`.
2
2
  // Do not edit manually.
3
3
 
4
4
  import type { Class } from "../../di";
@@ -746,7 +746,7 @@ function resolveAndValidateResponseSchema(
746
746
  diagnostics.push(uiDiagnosticV1(
747
747
  "error",
748
748
  "UIV1_RESPONSE_MODEL_NOT_REGISTERED",
749
- `${profileName} response class is not bound to a generated OpenAPI schema. Run bun run di:generate.`,
749
+ `${profileName} response class is not bound to a generated OpenAPI schema. Run \`bazis codegen\`.`,
750
750
  { profile: profileName, path: "response" },
751
751
  ));
752
752
  return actual;
@@ -9,7 +9,7 @@ export class ClassDependenciesMismatchError extends DiError {
9
9
  declaredDeps: number,
10
10
  ) {
11
11
  super(
12
- `Class provider "${tokenToDebugName(token)}" requires at least ${requiredConstructorParams} constructor deps, but only ${declaredDeps} declared.`,
12
+ `Class provider "${tokenToDebugName(token)}" requires at least ${requiredConstructorParams} constructor deps, but only ${declaredDeps} declared. Constructor dependencies are wired by codegen: if the constructor was added or changed after the last run, run \`bazis codegen\` (bazis dev, bazis test and bazis build run it automatically); otherwise pass the deps explicitly.`,
13
13
  );
14
14
  }
15
15
  }
@@ -70,7 +70,7 @@ export class GraphValidator {
70
70
  const shape = this.context.classShape(registration.provider);
71
71
  if (shape && shape.required > shape.declared) {
72
72
  issues.add(
73
- `Class provider "${tokenToDebugName(registration.token)}" requires at least ${shape.required} constructor deps, but only ${shape.declared} declared.`,
73
+ `Class provider "${tokenToDebugName(registration.token)}" requires at least ${shape.required} constructor deps, but only ${shape.declared} declared. Constructor dependencies are wired by codegen: if the constructor was added or changed after the last run, run \`bazis codegen\` (bazis dev, bazis test and bazis build run it automatically); otherwise pass the deps explicitly.`,
74
74
  );
75
75
  }
76
76
 
@@ -37,6 +37,16 @@ export function frameworkVersion(): string | undefined {
37
37
  * (or with another framework version): new routes and dependencies would be
38
38
  * silently missing. A compiled binary carries no sources, so it is skipped.
39
39
  */
40
+ let staleGeneratedSources = false;
41
+
42
+ /**
43
+ * True when the loaded generated code was produced from older sources (or
44
+ * another bazis version) than the ones running now.
45
+ */
46
+ export function generatedSourcesAreStale(): boolean {
47
+ return staleGeneratedSources;
48
+ }
49
+
40
50
  export function warnIfGeneratedSourcesChanged(
41
51
  generatedDir: string,
42
52
  fingerprint: GeneratedSourceFingerprint,
@@ -49,6 +59,7 @@ export function warnIfGeneratedSourcesChanged(
49
59
  const version = frameworkVersion();
50
60
  if (version !== undefined && version !== fingerprint.framework) changes.push(`bazis ${fingerprint.framework} -> ${version}`);
51
61
  if (changes.length === 0) return false;
62
+ staleGeneratedSources = true;
52
63
  warn([
53
64
  "",
54
65
  `[bazis] WARNING: generated code is out of date (${changes.join("; ")}).`,
@@ -24,7 +24,7 @@ export function registerGeneratedBindings(
24
24
  }
25
25
 
26
26
  /**
27
- * Binding conventions inferred by codegen (`bun run di:generate`) from the
27
+ * Binding conventions inferred by codegen (`bazis codegen`) from the
28
28
  * controller method signature, the counterpart of DI auto deps:
29
29
  *
30
30
  * - a parameter named like a route `:name` -> from the route;
@@ -72,7 +72,7 @@ function toBinding(spec: GeneratedBindingSpec, controllerName: string, methodNam
72
72
  if (ctor === undefined) {
73
73
  throw new HttpSetupError(
74
74
  `${controllerName}.${methodName}: list model "${spec.model}" is not registered. ` +
75
- `Make sure the class extends ListRequest and its module is imported, then run \`bun run di:generate\`.`,
75
+ `Make sure the class extends ListRequest and its module is imported, then run \`bazis codegen\`.`,
76
76
  );
77
77
  }
78
78
  if (ctor === AMBIGUOUS_LIST_MODEL) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Format of the generated binding conventions (`bun run di:generate`).
2
+ * Format of the generated binding conventions (`bazis codegen`).
3
3
  *
4
4
  * Plain data without class references: the body model is given **by name** and
5
5
  * resolved at startup through the class index of the generated target.
@@ -48,7 +48,7 @@ export interface ModelBindingOptions {
48
48
  *
49
49
  * The model class must have a parameterless constructor. Class-typed nested
50
50
  * DTOs (including arrays) are recursively hydrated from metadata emitted by
51
- * `bun run di:generate`; initialized nested DTO fields remain supported as a
51
+ * `bazis codegen`; initialized nested DTO fields remain supported as a
52
52
  * runtime fallback.
53
53
  */
54
54
  export function bindModel<T extends object>(model: Class<T>, data: unknown, validator?: ModelValidator, options: ModelBindingOptions = {}): T {
@@ -13,7 +13,7 @@ export const AMBIGUOUS_REQUEST_MODEL: unique symbol = Symbol("ambiguous-request-
13
13
  export type RequestModelClass = new () => object;
14
14
 
15
15
  /**
16
- * Runtime hydration metadata emitted by `bun run di:generate` for a class-
16
+ * Runtime hydration metadata emitted by `bazis codegen` for a class-
17
17
  * typed request-model field. Standard TC39 decorators deliberately do not
18
18
  * expose design types, so this tiny registry is the dependency-free bridge
19
19
  * from TypeScript source types to the HTTP binder.
@@ -1,6 +1,7 @@
1
1
  import type { Class, Token } from "../../di";
2
2
  import { bindArguments } from "../Binding/ParameterBinder";
3
3
  import { resolveGeneratedBindings } from "../Binding/autoBindings";
4
+ import { generatedSourcesAreStale } from "../../generatedFingerprint";
4
5
  import { controllerMetaOf, type ActionMeta, type ControllerMeta } from "../Decorators/metadata";
5
6
  import { HttpSetupError } from "../Errors/HttpError";
6
7
  import type { HttpContext } from "../HttpContext/HttpContext";
@@ -108,6 +109,7 @@ export class RouterBuilder {
108
109
  const filters: readonly ActionFilterHooks[] = [...meta.filters, ...action.filters];
109
110
  const catches = meta.catches;
110
111
  const bindings = resolveGeneratedBindings(controllerClass, methodName);
112
+ assertBindingsFresh(controllerClass, methodName, bindings);
111
113
  const responseDefaults = { httpCode: action.httpCode, produces: action.produces };
112
114
  const bindingDefaults = { consumes: action.consumes };
113
115
  const token = controllerClass as unknown as Token<object>;
@@ -182,3 +184,28 @@ export class RouterBuilder {
182
184
  return { boundary, terminal };
183
185
  }
184
186
  }
187
+
188
+ /**
189
+ * Without generated bindings a method receives only the HttpContext. When the
190
+ * generated code is older than the sources, a method that declares parameters
191
+ * but has no bindings was most likely added or changed after the last codegen:
192
+ * its arguments would silently get wrong values, so the server refuses to start.
193
+ */
194
+ function assertBindingsFresh(
195
+ controllerClass: Class<object>,
196
+ methodName: string | symbol,
197
+ bindings: readonly unknown[] | undefined,
198
+ ): void {
199
+ if (bindings !== undefined || !generatedSourcesAreStale()) {
200
+ return;
201
+ }
202
+ const method = (controllerClass.prototype as Record<string | symbol, unknown>)[methodName];
203
+ if (typeof method !== "function" || method.length === 0) {
204
+ return;
205
+ }
206
+ throw new HttpSetupError(
207
+ `${controllerClass.name}.${String(methodName)} has parameters but no generated argument bindings: ` +
208
+ "the generated code is older than this controller. Run `bazis codegen` " +
209
+ "(bazis dev, bazis test and bazis build run it automatically).",
210
+ );
211
+ }
@@ -116,7 +116,7 @@ export interface HttpModuleOptions {
116
116
  readonly health?: HealthEndpointOptions | boolean;
117
117
  /**
118
118
  * Built-in generated API docs. No user-facing Swagger decorators are needed:
119
- * `bun run di:generate` derives request models and schemas from controllers.
119
+ * `bazis codegen` derives request models and schemas from controllers.
120
120
  *
121
121
  * Default: enabled in debug environments, disabled in production. `true`
122
122
  * serves `/docs` and `/docs/openapi.json`; pass an object to change paths or
@@ -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.97.2" }, capabilities: { experimentalApi: true },
235
+ clientInfo: { name: "bazis", title: "Bazis", version: "0.97.3" }, 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");
@@ -879,7 +879,7 @@ function renderAgentCatalog(context: AgentCodegenContext): RenderedAgentCatalog
879
879
  const validPrompts = filterImportablePromptEntries(context.prompts, imports, context);
880
880
 
881
881
  const lines: string[] = [];
882
- lines.push("// This file is auto-generated by `bun run di:generate`.");
882
+ lines.push("// This file is auto-generated by `bazis codegen`.");
883
883
  lines.push("// Do not edit manually.");
884
884
  lines.push("");
885
885
  if (context.frameworkImports === "public") {
@@ -545,7 +545,7 @@ function failOnFatalErrors(): void {
545
545
  function renderGeneratedRuntime(targetId: string): string {
546
546
  if (activeGeneratedDir !== APP_GENERATED_DIR) {
547
547
  return [
548
- "// This file is auto-generated by `bun run di:generate`.",
548
+ "// This file is auto-generated by `bazis codegen`.",
549
549
  "// Do not edit manually.",
550
550
  "",
551
551
  'import { registerBazisGeneratedTargetDescriptor } from "bazis/core/generatedRuntime";',
@@ -565,7 +565,7 @@ function renderGeneratedRuntime(targetId: string): string {
565
565
  ].join("\n");
566
566
  }
567
567
  return [
568
- "// This file is auto-generated by `bun run di:generate`.",
568
+ "// This file is auto-generated by `bazis codegen`.",
569
569
  "// Do not edit manually.",
570
570
  "",
571
571
  "let registered: Promise<void> | undefined;",
@@ -615,7 +615,7 @@ function renderTargetDeps(): string {
615
615
  if (dependency.target && !references.has(dependency.target)) references.set(dependency.target, `DependencyClass_${references.size}`);
616
616
  }
617
617
  }
618
- const lines = ["// This file is auto-generated by `bun run di:generate`.", "// Do not edit manually."];
618
+ const lines = ["// This file is auto-generated by `bazis codegen`.", "// Do not edit manually."];
619
619
  if (entries.some(([, deps]) => deps.some(dep => dep.target && dep.lazy))) lines.push('import { lazyDependency } from "bazis/core/di";');
620
620
  for (const [declaration, reference] of references) {
621
621
  const name = dependencyClassExportName(declaration);
@@ -668,7 +668,7 @@ function renderTargetBindings(): string {
668
668
  // bindable parameters as well as ordinary binding-bearing controllers.
669
669
  const entries = [...new Set([...Object.keys(httpBindings), ...Object.keys(openApiOperations)])]
670
670
  .sort((a, b) => a.localeCompare(b));
671
- const lines = ["// This file is auto-generated by `bun run di:generate`.", "// Do not edit manually."];
671
+ const lines = ["// This file is auto-generated by `bazis codegen`.", "// Do not edit manually."];
672
672
  for (let index = 0; index < entries.length; index += 1) {
673
673
  const name = entries[index] as string;
674
674
  const file = httpControllerFiles[name] ?? classFilesByName.get(name);
@@ -1677,7 +1677,7 @@ function sourceLocation(node: ts.Node): string {
1677
1677
 
1678
1678
  function renderListModels(imports: readonly RequestModelImport[]): string {
1679
1679
  const lines: string[] = [];
1680
- lines.push("// This file is auto-generated by `bun run di:generate`.");
1680
+ lines.push("// This file is auto-generated by `bazis codegen`.");
1681
1681
  lines.push("// Do not edit manually.");
1682
1682
  lines.push("//");
1683
1683
  lines.push("// Target-local immutable descriptor; runtime publication is staged by runtime.ts.");
@@ -1717,7 +1717,7 @@ function renderRequestModels(
1717
1717
  hydration: RequestModelHydration,
1718
1718
  ): string {
1719
1719
  const lines: string[] = [];
1720
- lines.push("// This file is auto-generated by `bun run di:generate`.");
1720
+ lines.push("// This file is auto-generated by `bazis codegen`.");
1721
1721
  lines.push("// Do not edit manually.");
1722
1722
  lines.push("//");
1723
1723
  lines.push("// Registers convention body models (class-typed action params) so they are");
@@ -1780,7 +1780,7 @@ function renderOpenApiMetadata(
1780
1780
  }
1781
1781
 
1782
1782
  const lines: string[] = [];
1783
- lines.push("// This file is auto-generated by `bun run di:generate`.");
1783
+ lines.push("// This file is auto-generated by `bazis codegen`.");
1784
1784
  lines.push("// Do not edit manually.");
1785
1785
  lines.push("");
1786
1786
  lines.push(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bazis",
3
- "version": "0.97.2",
3
+ "version": "0.97.3",
4
4
  "description": "Modular backend framework for Bun in TypeScript: DI wired by codegen, HTTP, a PostgreSQL ORM, JWT, WebSocket, agents and a CLI.",
5
5
  "license": "MIT",
6
6
  "author": "Alexander Alenin",