@cosmicdrift/kumiko-framework 0.370.0 → 0.371.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.
|
@@ -184,7 +184,7 @@ describe("chat-channels recipe", () => {
|
|
|
184
184
|
});
|
|
185
185
|
expect(rows.map((r) => r["recipientAddress"])).toEqual(["ops"]);
|
|
186
186
|
expect(JSON.stringify(rows)).not.toContain("XXXX");
|
|
187
|
-
}
|
|
187
|
+
});
|
|
188
188
|
|
|
189
189
|
test("announce rejects a caller without the Admin role", async () => {
|
|
190
190
|
const attemptsBefore = await selectMany(db(), deliveryAttemptsTable, {
|
|
@@ -135,7 +135,10 @@ describe("KUMIKO_DRY_RUN_ENV=boot", () => {
|
|
|
135
135
|
|
|
136
136
|
test("exits 1 on an invalid value and never prints it", async () => {
|
|
137
137
|
const endpoint = "http://s3.leak-check.example.com";
|
|
138
|
-
const { stdout, stderr, exitCode } = await runBootDryRun({
|
|
138
|
+
const { stdout, stderr, exitCode } = await runBootDryRun({
|
|
139
|
+
NODE_ENV: "production",
|
|
140
|
+
S3_ENDPOINT: endpoint,
|
|
141
|
+
});
|
|
139
142
|
|
|
140
143
|
expect(exitCode).toBe(1);
|
|
141
144
|
expect(stdout).toMatch(/S3_ENDPOINT\s+file-provider-s3-env\s+optional\s+invalid/);
|
|
@@ -206,11 +209,18 @@ describe("env composition", () => {
|
|
|
206
209
|
expect(report).not.toContain("S3_BUCKET");
|
|
207
210
|
});
|
|
208
211
|
|
|
209
|
-
test("rejects a remote http S3 endpoint", () => {
|
|
212
|
+
test("rejects a remote http S3 endpoint in production", () => {
|
|
210
213
|
const composed = composeAppEnv();
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
const originalNodeEnv = process.env["NODE_ENV"];
|
|
215
|
+
process.env["NODE_ENV"] = "production";
|
|
216
|
+
try {
|
|
217
|
+
expect(() =>
|
|
218
|
+
parseEnv(composed.schema, { ...minimalValidValues, S3_ENDPOINT: "http://s3.example.com" }),
|
|
219
|
+
).toThrow(KumikoBootError);
|
|
220
|
+
} finally {
|
|
221
|
+
if (originalNodeEnv === undefined) delete process.env["NODE_ENV"];
|
|
222
|
+
else process.env["NODE_ENV"] = originalNodeEnv;
|
|
223
|
+
}
|
|
214
224
|
});
|
|
215
225
|
});
|
|
216
226
|
```
|
package/dist/env/index.js
CHANGED
|
@@ -76,6 +76,18 @@ export function classifyField(field) {
|
|
|
76
76
|
for (let i = 0; i < 8; i++) {
|
|
77
77
|
if (current instanceof z.ZodDefault)
|
|
78
78
|
return "withDefault";
|
|
79
|
+
// `.required()` on an object wraps optional fields in nonoptional(); the
|
|
80
|
+
// wrapper makes the field required, whatever the optional beneath it says.
|
|
81
|
+
if (current instanceof z.ZodNonOptional) {
|
|
82
|
+
const beneath = zodDef(current)?.innerType;
|
|
83
|
+
if (beneath instanceof z.ZodOptional) {
|
|
84
|
+
const unwrapped = zodDef(beneath)?.innerType;
|
|
85
|
+
if (unwrapped === undefined)
|
|
86
|
+
return "required";
|
|
87
|
+
current = unwrapped;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
79
91
|
if (current instanceof z.ZodOptional || current instanceof z.ZodNullable) {
|
|
80
92
|
return "optional";
|
|
81
93
|
}
|
|
@@ -324,7 +336,10 @@ export function parseEnv(schema, env, options = {}) {
|
|
|
324
336
|
const isMissing = issue.code === "invalid_type" && !(name in cleaned);
|
|
325
337
|
const kind = isMissing ? "missing" : "invalid";
|
|
326
338
|
const desc = field ? getFieldDescription(field) : undefined;
|
|
327
|
-
const
|
|
339
|
+
const issueText = issue.code === "invalid_type" && issue.expected === "nonoptional"
|
|
340
|
+
? "Invalid input: required"
|
|
341
|
+
: issue.message;
|
|
342
|
+
const message = desc ? `${issueText} — ${desc}` : issueText;
|
|
328
343
|
const suggestion = field ? buildPulumiSuggestion(name, field, options.pulumiPrefix) : undefined;
|
|
329
344
|
const source = options.sources?.[name];
|
|
330
345
|
return {
|
package/dist/schema-cli.js
CHANGED
|
@@ -20,12 +20,21 @@ import { join, resolve as resolvePath } from "node:path";
|
|
|
20
20
|
import { configureBlindIndexKey, configurePiiSubjectKms, resolveKmsWiringAsync, } from "./crypto/index.js";
|
|
21
21
|
import { assertUniqueTableNames, assertValidMigrationName, baselineMigrations, createDbConnection, diffReplayAgainstSnapshot, fetchAppliedMigrations, findCommentedDropTables, generateMigration, isRetiredFrameworkTable, loadMigrationsFromDir, loadSnapshotJson, readMigrationSqlTexts, rebuildTablesFromDiff, replayMigrationsDir, runMigrationsFromDir, tableExists, writeRebuildMarker, writeSnapshotJson, } from "./db/index.js";
|
|
22
22
|
import { validateBoot } from "./engine/boot-validator.js";
|
|
23
|
+
import { FILE_STORAGE_PROVIDER_BOOT_SENTINEL, FILE_STORAGE_PROVIDER_ENV, } from "./engine/extension-names.js";
|
|
23
24
|
import { createRegistry } from "./engine/registry.js";
|
|
24
25
|
import { createEventsTable } from "./event-store/index.js";
|
|
25
26
|
import { queueRebuildsFromMarkers, reportPendingRebuildRun, runPendingRebuilds, } from "./migrations/index.js";
|
|
26
27
|
import { createEventConsumerStateTable, createProjectionStateTable } from "./pipeline/index.js";
|
|
27
28
|
import { ensureTemporalPolyfill } from "./time/index.js";
|
|
28
29
|
const SNAPSHOT_FILENAME = ".snapshot.json";
|
|
30
|
+
// `validate` only sees the composed FEATURES. The file provider normally comes from
|
|
31
|
+
// runProdApp's `fileStorage` option, which defaults FILE_STORAGE_PROVIDER at boot, so
|
|
32
|
+
// an unset variable here is no misconfiguration. The deploy env is checked by the real boot.
|
|
33
|
+
function withFileStoragePresence(env) {
|
|
34
|
+
return env[FILE_STORAGE_PROVIDER_ENV] === undefined
|
|
35
|
+
? { ...env, [FILE_STORAGE_PROVIDER_ENV]: FILE_STORAGE_PROVIDER_BOOT_SENTINEL }
|
|
36
|
+
: { ...env };
|
|
37
|
+
}
|
|
29
38
|
// Single load-path for kumiko/schema.ts (512/3) — `generate` and `validate`
|
|
30
39
|
// both need ENTITY_METAS (and validate additionally wants FEATURES); this is
|
|
31
40
|
// the one place that enforces "ENTITY_METAS must be an array" so the two
|
|
@@ -247,7 +256,9 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
|
|
|
247
256
|
// 2. Boot validity — needs the composed feature set.
|
|
248
257
|
if (Array.isArray(mod.FEATURES)) {
|
|
249
258
|
try {
|
|
250
|
-
validateBoot(mod.FEATURES
|
|
259
|
+
validateBoot(mod.FEATURES, {
|
|
260
|
+
env: withFileStoragePresence(process.env),
|
|
261
|
+
});
|
|
251
262
|
out.log(" ✓ boot: feature configuration is valid");
|
|
252
263
|
}
|
|
253
264
|
catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.371.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>",
|
|
@@ -199,8 +199,8 @@
|
|
|
199
199
|
"./package.json": "./package.json"
|
|
200
200
|
},
|
|
201
201
|
"dependencies": {
|
|
202
|
-
"@cosmicdrift/kumiko-http": "0.
|
|
203
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
202
|
+
"@cosmicdrift/kumiko-http": "0.371.0",
|
|
203
|
+
"@cosmicdrift/kumiko-types": "0.371.0",
|
|
204
204
|
"bullmq": "^5.76.7",
|
|
205
205
|
"bun-types": "^1.3.13",
|
|
206
206
|
"hono": "^4.13.1",
|
|
@@ -216,8 +216,8 @@
|
|
|
216
216
|
"zod": "^4.4.3"
|
|
217
217
|
},
|
|
218
218
|
"devDependencies": {
|
|
219
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
220
|
-
"@cosmicdrift/kumiko-testing": "0.
|
|
219
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.371.0",
|
|
220
|
+
"@cosmicdrift/kumiko-testing": "0.371.0",
|
|
221
221
|
"bun-types": "^1.3.13",
|
|
222
222
|
"pino-pretty": "^13.1.3"
|
|
223
223
|
},
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.371.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Dry-run env table and boot check respect required overrides and the fileStorage default",
|
|
6
|
+
"detail": "The env table of KUMIKO_DRY_RUN_ENV=boot shows a variable as required when the app schema overlays required() on an optional field, and the error says required. The schema-cli boot check applies runProdApp's fileStorage default instead of demanding FILE_STORAGE_PROVIDER."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.370.0",
|
|
4
10
|
"type": "improvement",
|