@cosmicdrift/kumiko-framework 0.76.0 → 0.77.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-framework",
3
- "version": "0.76.0",
3
+ "version": "0.77.0",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -181,7 +181,7 @@
181
181
  "zod": "^4.4.3"
182
182
  },
183
183
  "devDependencies": {
184
- "@cosmicdrift/kumiko-dispatcher-live": "0.76.0",
184
+ "@cosmicdrift/kumiko-dispatcher-live": "0.77.0",
185
185
  "bun-types": "^1.3.13",
186
186
  "pino-pretty": "^13.1.3"
187
187
  },
@@ -257,6 +257,20 @@ describe("parseEnv", () => {
257
257
  expect(boot.format()).toContain("✗ JWT_SECRET (auth-email-password, required, missing)");
258
258
  }
259
259
  });
260
+
261
+ it("error.message carries the per-var details (uncaught Bun prints message, not format())", () => {
262
+ // Without this, `bun run boot` against a misconfigured app shows only
263
+ // "Boot failed: 1 env-var problem" + the EnvError[] collapsed to
264
+ // "[Object ...]". The user can't tell which var is the problem.
265
+ const schema = z.object({ JWT_SECRET: z.string().min(32) });
266
+ try {
267
+ parseEnv(schema, {}, { sources: { JWT_SECRET: "auth-email-password" } });
268
+ } catch (err) {
269
+ const boot = err as KumikoBootError;
270
+ expect(boot.message).toContain("Boot failed: 1 env-var problem");
271
+ expect(boot.message).toContain("✗ JWT_SECRET (auth-email-password, required, missing)");
272
+ }
273
+ });
260
274
  });
261
275
 
262
276
  describe("pulumiConfigKey + camelCase", () => {
package/src/env/index.ts CHANGED
@@ -226,34 +226,36 @@ export type EnvError = {
226
226
  readonly suggestion?: string;
227
227
  };
228
228
 
229
+ function formatBootError(errors: readonly EnvError[]): string {
230
+ const lines: string[] = [
231
+ `Boot failed: ${errors.length} env-var problem${errors.length === 1 ? "" : "s"}`,
232
+ "",
233
+ ];
234
+ for (const err of errors) {
235
+ const tag = err.kind === "missing" ? "required, missing" : "invalid";
236
+ const sourceTag = err.source ? `${err.source}, ${tag}` : tag;
237
+ lines.push(` ✗ ${err.name} (${sourceTag})`);
238
+ lines.push(` ${err.message}`);
239
+ if (err.suggestion) {
240
+ lines.push(` ${err.suggestion}`);
241
+ }
242
+ }
243
+ lines.push("");
244
+ lines.push("See: kumiko-platform/docs/runbooks/standard-deploy-app.md#step-1-boot-dry-run-lokal");
245
+ return lines.join("\n");
246
+ }
247
+
229
248
  export class KumikoBootError extends Error {
230
249
  readonly errors: readonly EnvError[];
231
250
 
232
251
  constructor(errors: readonly EnvError[]) {
233
- super(`Boot failed: ${errors.length} env-var problem${errors.length === 1 ? "" : "s"}`);
252
+ super(formatBootError(errors));
234
253
  this.name = "KumikoBootError";
235
254
  this.errors = errors;
236
255
  }
237
256
 
238
257
  format(): string {
239
- const lines: string[] = [
240
- `Boot failed: ${this.errors.length} env-var problem${this.errors.length === 1 ? "" : "s"}`,
241
- "",
242
- ];
243
- for (const err of this.errors) {
244
- const tag = err.kind === "missing" ? "required, missing" : "invalid";
245
- const sourceTag = err.source ? `${err.source}, ${tag}` : tag;
246
- lines.push(` ✗ ${err.name} (${sourceTag})`);
247
- lines.push(` ${err.message}`);
248
- if (err.suggestion) {
249
- lines.push(` ${err.suggestion}`);
250
- }
251
- }
252
- lines.push("");
253
- lines.push(
254
- "See: kumiko-platform/docs/runbooks/standard-deploy-app.md#step-1-boot-dry-run-lokal",
255
- );
256
- return lines.join("\n");
258
+ return formatBootError(this.errors);
257
259
  }
258
260
  }
259
261