@cosmicdrift/kumiko-server-runtime 0.329.0 → 0.330.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 +15 -0
- package/dist/boot/apply-boot-seeds.d.ts +8 -0
- package/{src/boot/apply-boot-seeds.ts → dist/boot/apply-boot-seeds.js} +2 -10
- package/dist/boot/boot-crypto.d.ts +9 -0
- package/dist/boot/boot-crypto.js +39 -0
- package/dist/boot/job-run-logger.d.ts +19 -0
- package/dist/boot/job-run-logger.js +44 -0
- package/dist/boot/pii-event-backfill-on-boot.d.ts +13 -0
- package/dist/boot/pii-event-backfill-on-boot.js +25 -0
- package/dist/build-prod-bundle.d.ts +103 -0
- package/dist/build-prod-bundle.js +751 -0
- package/dist/bun-serve-options.d.ts +32 -0
- package/dist/bun-serve-options.js +49 -0
- package/dist/compose-features.d.ts +31 -0
- package/dist/compose-features.js +124 -0
- package/dist/extra-routes-deps.d.ts +30 -0
- package/dist/extra-routes-deps.js +4 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/pii-boot-gate.d.ts +13 -0
- package/dist/pii-boot-gate.js +46 -0
- package/dist/renderer-web-fonts.d.ts +9 -0
- package/{src/renderer-web-fonts.ts → dist/renderer-web-fonts.js} +14 -18
- package/dist/resolve-tailwind-cli.d.ts +14 -0
- package/dist/resolve-tailwind-cli.js +36 -0
- package/dist/run-prod-app-boot-context.d.ts +43 -0
- package/dist/run-prod-app-boot-context.js +198 -0
- package/dist/run-prod-app-static-files.d.ts +32 -0
- package/dist/run-prod-app-static-files.js +280 -0
- package/dist/run-prod-app.d.ts +471 -0
- package/dist/run-prod-app.js +741 -0
- package/dist/run-worker-app.d.ts +92 -0
- package/dist/run-worker-app.js +220 -0
- package/dist/security-headers.d.ts +20 -0
- package/dist/security-headers.js +49 -0
- package/dist/session-boot-gate.d.ts +11 -0
- package/dist/session-boot-gate.js +22 -0
- package/dist/session-wiring.d.ts +11 -0
- package/{src/session-wiring.ts → dist/session-wiring.js} +2 -6
- package/dist/try-hono-first.d.ts +24 -0
- package/{src/try-hono-first.ts → dist/try-hono-first.js} +8 -36
- package/package.json +102 -36
- package/src/changes.json +6 -0
- package/src/__tests__/boot-extra-context.test.ts +0 -163
- package/src/__tests__/boot-probe-fixture.ts +0 -64
- package/src/__tests__/build-prod-bundle.inprocess.test.ts +0 -233
- package/src/__tests__/build-prod-bundle.test.ts +0 -724
- package/src/__tests__/cache-headers.test.ts +0 -83
- package/src/__tests__/compose-features-mfa-wiring.integration.test.ts +0 -308
- package/src/__tests__/compose-features-wiring.integration.test.ts +0 -609
- package/src/__tests__/compose-features.test.ts +0 -206
- package/src/__tests__/config-seed-boot.integration.test.ts +0 -158
- package/src/__tests__/pii-boot-gate.test.ts +0 -112
- package/src/__tests__/renderer-web-css-relocation.integration.test.ts +0 -85
- package/src/__tests__/renderer-web-shell-sentinel.test.ts +0 -35
- package/src/__tests__/renderer-web-theme-plugin.integration.test.ts +0 -77
- package/src/__tests__/require-env.test.ts +0 -34
- package/src/__tests__/resolve-auth-mail.test.ts +0 -144
- package/src/__tests__/resolve-tailwind-cli.test.ts +0 -81
- package/src/__tests__/run-prod-app-dry-run.test.ts +0 -135
- package/src/__tests__/run-prod-app-env-source.test.ts +0 -118
- package/src/__tests__/run-prod-app-kms-slots.test.ts +0 -108
- package/src/__tests__/run-prod-app-spec.test.ts +0 -167
- package/src/__tests__/run-prod-app-static-files.test.ts +0 -298
- package/src/__tests__/run-prod-app.integration.test.ts +0 -1525
- package/src/__tests__/run-worker-app-boot.test.ts +0 -183
- package/src/__tests__/run-worker-app.integration.test.ts +0 -218
- package/src/__tests__/security-headers.test.ts +0 -88
- package/src/__tests__/session-boot-gate.test.ts +0 -70
- package/src/__tests__/session-wiring.test.ts +0 -16
- package/src/__tests__/try-hono-first.test.ts +0 -108
- package/src/__tests__/websocket-route.integration.test.ts +0 -695
- package/src/boot/__tests__/job-run-logger.integration.test.ts +0 -262
- package/src/boot/__tests__/job-run-logger.test.ts +0 -26
- package/src/boot/boot-crypto.ts +0 -67
- package/src/boot/job-run-logger.ts +0 -63
- package/src/boot/pii-event-backfill-on-boot.ts +0 -43
- package/src/build-prod-bundle.ts +0 -996
- package/src/bun-serve-options.ts +0 -110
- package/src/compose-features.ts +0 -197
- package/src/extra-routes-deps.ts +0 -38
- package/src/index.ts +0 -27
- package/src/pii-boot-gate.ts +0 -80
- package/src/resolve-tailwind-cli.ts +0 -45
- package/src/run-prod-app-boot-context.ts +0 -319
- package/src/run-prod-app-static-files.ts +0 -359
- package/src/run-prod-app.ts +0 -1420
- package/src/run-worker-app.ts +0 -366
- package/src/security-headers.ts +0 -67
- package/src/session-boot-gate.ts +0 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.330.2",
|
|
4
4
|
"description": "Production server-boot runtime for Kumiko apps: connections, schema-drift-gate, seeds, lifecycle, graceful shutdown. Symmetric to kumiko-dev-server's runDevApp, without dev/scaffold/codegen tooling.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -19,78 +19,144 @@
|
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|
|
22
|
-
"types": "./
|
|
23
|
-
"default": "./
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
24
|
},
|
|
25
25
|
"./compose-features": {
|
|
26
|
-
"types": "./
|
|
27
|
-
"default": "./
|
|
26
|
+
"types": "./dist/compose-features.d.ts",
|
|
27
|
+
"default": "./dist/compose-features.js"
|
|
28
28
|
},
|
|
29
29
|
"./run-prod-app": {
|
|
30
|
-
"types": "./
|
|
31
|
-
"default": "./
|
|
30
|
+
"types": "./dist/run-prod-app.d.ts",
|
|
31
|
+
"default": "./dist/run-prod-app.js"
|
|
32
32
|
},
|
|
33
33
|
"./run-worker-app": {
|
|
34
|
-
"types": "./
|
|
35
|
-
"default": "./
|
|
34
|
+
"types": "./dist/run-worker-app.d.ts",
|
|
35
|
+
"default": "./dist/run-worker-app.js"
|
|
36
36
|
},
|
|
37
37
|
"./bun-serve-options": {
|
|
38
|
-
"types": "./
|
|
39
|
-
"default": "./
|
|
38
|
+
"types": "./dist/bun-serve-options.d.ts",
|
|
39
|
+
"default": "./dist/bun-serve-options.js"
|
|
40
40
|
},
|
|
41
41
|
"./pii-boot-gate": {
|
|
42
|
-
"types": "./
|
|
43
|
-
"default": "./
|
|
42
|
+
"types": "./dist/pii-boot-gate.d.ts",
|
|
43
|
+
"default": "./dist/pii-boot-gate.js"
|
|
44
44
|
},
|
|
45
45
|
"./session-boot-gate": {
|
|
46
|
-
"types": "./
|
|
47
|
-
"default": "./
|
|
46
|
+
"types": "./dist/session-boot-gate.d.ts",
|
|
47
|
+
"default": "./dist/session-boot-gate.js"
|
|
48
48
|
},
|
|
49
49
|
"./boot/apply-boot-seeds": {
|
|
50
|
-
"types": "./
|
|
51
|
-
"default": "./
|
|
50
|
+
"types": "./dist/boot/apply-boot-seeds.d.ts",
|
|
51
|
+
"default": "./dist/boot/apply-boot-seeds.js"
|
|
52
52
|
},
|
|
53
53
|
"./boot/boot-crypto": {
|
|
54
|
-
"types": "./
|
|
55
|
-
"default": "./
|
|
54
|
+
"types": "./dist/boot/boot-crypto.d.ts",
|
|
55
|
+
"default": "./dist/boot/boot-crypto.js"
|
|
56
56
|
},
|
|
57
57
|
"./boot/job-run-logger": {
|
|
58
|
-
"types": "./
|
|
59
|
-
"default": "./
|
|
58
|
+
"types": "./dist/boot/job-run-logger.d.ts",
|
|
59
|
+
"default": "./dist/boot/job-run-logger.js"
|
|
60
60
|
},
|
|
61
61
|
"./extra-routes-deps": {
|
|
62
|
-
"types": "./
|
|
63
|
-
"default": "./
|
|
62
|
+
"types": "./dist/extra-routes-deps.d.ts",
|
|
63
|
+
"default": "./dist/extra-routes-deps.js"
|
|
64
64
|
},
|
|
65
65
|
"./renderer-web-fonts": {
|
|
66
|
-
"types": "./
|
|
67
|
-
"default": "./
|
|
66
|
+
"types": "./dist/renderer-web-fonts.d.ts",
|
|
67
|
+
"default": "./dist/renderer-web-fonts.js"
|
|
68
68
|
},
|
|
69
69
|
"./resolve-tailwind-cli": {
|
|
70
|
-
"types": "./
|
|
71
|
-
"default": "./
|
|
70
|
+
"types": "./dist/resolve-tailwind-cli.d.ts",
|
|
71
|
+
"default": "./dist/resolve-tailwind-cli.js"
|
|
72
72
|
},
|
|
73
73
|
"./try-hono-first": {
|
|
74
|
-
"types": "./
|
|
75
|
-
"default": "./
|
|
74
|
+
"types": "./dist/try-hono-first.d.ts",
|
|
75
|
+
"default": "./dist/try-hono-first.js"
|
|
76
76
|
},
|
|
77
77
|
"./build-prod-bundle": {
|
|
78
|
-
"types": "./
|
|
79
|
-
"default": "./
|
|
78
|
+
"types": "./dist/build-prod-bundle.d.ts",
|
|
79
|
+
"default": "./dist/build-prod-bundle.js"
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
84
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
85
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
83
|
+
"@cosmicdrift/kumiko-bundled-features": "0.330.2",
|
|
84
|
+
"@cosmicdrift/kumiko-framework": "0.330.2",
|
|
85
|
+
"@cosmicdrift/kumiko-headless": "0.330.2",
|
|
86
|
+
"@cosmicdrift/kumiko-renderer-web": "0.330.2",
|
|
87
|
+
"bun-types": "^1.3.13",
|
|
88
|
+
"ioredis": "^5.10.1",
|
|
86
89
|
"temporal-polyfill": "^0.3.2"
|
|
87
90
|
},
|
|
88
91
|
"publishConfig": {
|
|
89
92
|
"registry": "https://registry.npmjs.org",
|
|
90
|
-
"access": "public"
|
|
93
|
+
"access": "public",
|
|
94
|
+
"exports": {
|
|
95
|
+
".": {
|
|
96
|
+
"types": "./dist/index.d.ts",
|
|
97
|
+
"default": "./dist/index.js"
|
|
98
|
+
},
|
|
99
|
+
"./compose-features": {
|
|
100
|
+
"types": "./dist/compose-features.d.ts",
|
|
101
|
+
"default": "./dist/compose-features.js"
|
|
102
|
+
},
|
|
103
|
+
"./run-prod-app": {
|
|
104
|
+
"types": "./dist/run-prod-app.d.ts",
|
|
105
|
+
"default": "./dist/run-prod-app.js"
|
|
106
|
+
},
|
|
107
|
+
"./run-worker-app": {
|
|
108
|
+
"types": "./dist/run-worker-app.d.ts",
|
|
109
|
+
"default": "./dist/run-worker-app.js"
|
|
110
|
+
},
|
|
111
|
+
"./bun-serve-options": {
|
|
112
|
+
"types": "./dist/bun-serve-options.d.ts",
|
|
113
|
+
"default": "./dist/bun-serve-options.js"
|
|
114
|
+
},
|
|
115
|
+
"./pii-boot-gate": {
|
|
116
|
+
"types": "./dist/pii-boot-gate.d.ts",
|
|
117
|
+
"default": "./dist/pii-boot-gate.js"
|
|
118
|
+
},
|
|
119
|
+
"./session-boot-gate": {
|
|
120
|
+
"types": "./dist/session-boot-gate.d.ts",
|
|
121
|
+
"default": "./dist/session-boot-gate.js"
|
|
122
|
+
},
|
|
123
|
+
"./boot/apply-boot-seeds": {
|
|
124
|
+
"types": "./dist/boot/apply-boot-seeds.d.ts",
|
|
125
|
+
"default": "./dist/boot/apply-boot-seeds.js"
|
|
126
|
+
},
|
|
127
|
+
"./boot/boot-crypto": {
|
|
128
|
+
"types": "./dist/boot/boot-crypto.d.ts",
|
|
129
|
+
"default": "./dist/boot/boot-crypto.js"
|
|
130
|
+
},
|
|
131
|
+
"./boot/job-run-logger": {
|
|
132
|
+
"types": "./dist/boot/job-run-logger.d.ts",
|
|
133
|
+
"default": "./dist/boot/job-run-logger.js"
|
|
134
|
+
},
|
|
135
|
+
"./extra-routes-deps": {
|
|
136
|
+
"types": "./dist/extra-routes-deps.d.ts",
|
|
137
|
+
"default": "./dist/extra-routes-deps.js"
|
|
138
|
+
},
|
|
139
|
+
"./renderer-web-fonts": {
|
|
140
|
+
"types": "./dist/renderer-web-fonts.d.ts",
|
|
141
|
+
"default": "./dist/renderer-web-fonts.js"
|
|
142
|
+
},
|
|
143
|
+
"./resolve-tailwind-cli": {
|
|
144
|
+
"types": "./dist/resolve-tailwind-cli.d.ts",
|
|
145
|
+
"default": "./dist/resolve-tailwind-cli.js"
|
|
146
|
+
},
|
|
147
|
+
"./try-hono-first": {
|
|
148
|
+
"types": "./dist/try-hono-first.d.ts",
|
|
149
|
+
"default": "./dist/try-hono-first.js"
|
|
150
|
+
},
|
|
151
|
+
"./build-prod-bundle": {
|
|
152
|
+
"types": "./dist/build-prod-bundle.d.ts",
|
|
153
|
+
"default": "./dist/build-prod-bundle.js"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
91
156
|
},
|
|
92
157
|
"files": [
|
|
93
|
-
"
|
|
158
|
+
"dist",
|
|
159
|
+
"src/changes.json",
|
|
94
160
|
"README.md",
|
|
95
161
|
"LICENSE"
|
|
96
162
|
]
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.330.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "server-runtime and dev-server declare their renderer-web dependency and their .d.ts resolve Bun and Temporal types without consumer setup",
|
|
6
|
+
"detail": "The emitted declarations that mention `Temporal` or `Bun` now carry a preserved `/// <reference types>` for `temporal-polyfill/global` and `bun-types`, so a consumer without `bun-types` in its tsconfig no longer gets unresolved `Temporal`/`Bun` errors from our declarations. `kumiko-server-runtime` and `kumiko-dev-server` declare `@cosmicdrift/kumiko-renderer-web` as a dependency (they resolve its `styles.css`; it was already installed through bundled-features) and `bun-types` as a dependency. `bundled-features`, `server-runtime`, `dev-server`, `dispatcher-live`, `headless`, `renderer`, `renderer-web` and `cli` ship the README.md their `files` list already named. `bun run check:dist` now typechecks the packed install, checks README shipping, runtime-resolved package declarations and the `styles.css` resolve."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.328.0",
|
|
4
10
|
"type": "improvement",
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { createAuditFeature } from "@cosmicdrift/kumiko-bundled-features/audit";
|
|
3
|
-
import { createDeliveryFeature } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
4
|
-
import { createSecretsFeature } from "@cosmicdrift/kumiko-bundled-features/secrets";
|
|
5
|
-
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
6
|
-
import { createRegistry, defineFeature } from "@cosmicdrift/kumiko-framework/engine";
|
|
7
|
-
import type { MasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
|
|
8
|
-
import { buildBootExtraContext } from "../run-prod-app";
|
|
9
|
-
|
|
10
|
-
// Pins runProdApp's framework-default-provider autowire (buildBootExtraContext):
|
|
11
|
-
// textContent unconditional, secrets feature-gated, KEK-env trap avoided, the
|
|
12
|
-
// money-horse regression (no secrets feature → no forced KEK → no throw), and
|
|
13
|
-
// the delivery _notifyFactory wiring that makes ctx.notify work in prod (only
|
|
14
|
-
// test-wired before — runProdApp/runDevApp call THIS, not createDeliveryTestContext).
|
|
15
|
-
|
|
16
|
-
// db is never touched at construction time — createTextContentApi /
|
|
17
|
-
// createSecretsContext only store the handle and query lazily. A bare stub
|
|
18
|
-
// keeps this a fast unit test (no Postgres) instead of a full boot.
|
|
19
|
-
const fakeDb = {} as unknown as DbConnection;
|
|
20
|
-
const registry = createRegistry([]);
|
|
21
|
-
const KEK = Buffer.alloc(32, 7).toString("base64");
|
|
22
|
-
|
|
23
|
-
const otherFeature = defineFeature("widgets", () => {});
|
|
24
|
-
|
|
25
|
-
describe("buildBootExtraContext — framework-default provider autowire", () => {
|
|
26
|
-
test("templateResolver is wired unconditionally (no secrets feature, no auth)", () => {
|
|
27
|
-
const ctx = buildBootExtraContext({
|
|
28
|
-
db: fakeDb,
|
|
29
|
-
features: [otherFeature],
|
|
30
|
-
envSource: {},
|
|
31
|
-
registry,
|
|
32
|
-
hasAuth: false,
|
|
33
|
-
});
|
|
34
|
-
expect(typeof (ctx["templateResolver"] as { findExact?: unknown }).findExact).toBe("function");
|
|
35
|
-
expect(ctx["secrets"]).toBeUndefined();
|
|
36
|
-
expect(ctx["configResolver"]).toBeUndefined();
|
|
37
|
-
expect(ctx["_notifyFactory"]).toBeUndefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("delivery feature mounted → _notifyFactory wired (ctx.notify works in prod)", () => {
|
|
41
|
-
const ctx = buildBootExtraContext({
|
|
42
|
-
db: fakeDb,
|
|
43
|
-
features: [createDeliveryFeature()],
|
|
44
|
-
envSource: {},
|
|
45
|
-
registry,
|
|
46
|
-
hasAuth: false,
|
|
47
|
-
});
|
|
48
|
-
expect(typeof ctx["_notifyFactory"]).toBe("function");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("no delivery feature → _notifyFactory not wired", () => {
|
|
52
|
-
const ctx = buildBootExtraContext({
|
|
53
|
-
db: fakeDb,
|
|
54
|
-
features: [otherFeature],
|
|
55
|
-
envSource: {},
|
|
56
|
-
registry,
|
|
57
|
-
hasAuth: false,
|
|
58
|
-
});
|
|
59
|
-
expect(ctx["_notifyFactory"]).toBeUndefined();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("secrets feature mounted + valid KEK env → ctx.secrets wired", () => {
|
|
63
|
-
const ctx = buildBootExtraContext({
|
|
64
|
-
db: fakeDb,
|
|
65
|
-
features: [createSecretsFeature()],
|
|
66
|
-
envSource: {
|
|
67
|
-
KUMIKO_SECRETS_MASTER_KEY_V1: KEK,
|
|
68
|
-
KUMIKO_SECRETS_MASTER_KEY_CURRENT_VERSION: "1",
|
|
69
|
-
},
|
|
70
|
-
registry,
|
|
71
|
-
hasAuth: false,
|
|
72
|
-
});
|
|
73
|
-
const secrets = ctx["secrets"] as {
|
|
74
|
-
get?: unknown;
|
|
75
|
-
set?: unknown;
|
|
76
|
-
has?: unknown;
|
|
77
|
-
delete?: unknown;
|
|
78
|
-
};
|
|
79
|
-
expect(typeof secrets.get).toBe("function");
|
|
80
|
-
expect(typeof secrets.set).toBe("function");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("secrets feature mounted, only V1 set (no CURRENT_VERSION) → wired (default '1')", () => {
|
|
84
|
-
const ctx = buildBootExtraContext({
|
|
85
|
-
db: fakeDb,
|
|
86
|
-
features: [createSecretsFeature()],
|
|
87
|
-
envSource: { KUMIKO_SECRETS_MASTER_KEY_V1: KEK },
|
|
88
|
-
registry,
|
|
89
|
-
hasAuth: false,
|
|
90
|
-
});
|
|
91
|
-
expect(ctx["secrets"]).toBeDefined();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("secrets feature mounted but NO KEK + no override → skipped, no throw (dev DEV_KEY path)", () => {
|
|
95
|
-
// dev wires its own DEV_KEY secrets explicitly; the auto-wire must not
|
|
96
|
-
// eagerly construct an env-provider and crash the boot when no env KEK
|
|
97
|
-
// exists. ctx.secrets stays unset so the app's explicit wiring wins.
|
|
98
|
-
let ctx: Record<string, unknown> | undefined;
|
|
99
|
-
expect(() => {
|
|
100
|
-
ctx = buildBootExtraContext({
|
|
101
|
-
db: fakeDb,
|
|
102
|
-
features: [createSecretsFeature()],
|
|
103
|
-
envSource: {},
|
|
104
|
-
registry,
|
|
105
|
-
hasAuth: false,
|
|
106
|
-
});
|
|
107
|
-
}).not.toThrow();
|
|
108
|
-
expect(ctx?.["secrets"]).toBeUndefined();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("no secrets feature → no KEK ever read, boots green (money-horse regression)", () => {
|
|
112
|
-
const ctx = buildBootExtraContext({
|
|
113
|
-
db: fakeDb,
|
|
114
|
-
features: [otherFeature],
|
|
115
|
-
envSource: {}, // no KEK — must NOT matter when secrets isn't mounted
|
|
116
|
-
registry,
|
|
117
|
-
hasAuth: false,
|
|
118
|
-
});
|
|
119
|
-
expect(ctx["secrets"]).toBeUndefined();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("masterKey override is used verbatim — no env-provider built", () => {
|
|
123
|
-
const override: MasterKeyProvider = {
|
|
124
|
-
wrapDek: async () => ({ encryptedDek: Buffer.alloc(0), kekVersion: 1 }),
|
|
125
|
-
unwrapDek: async () => Buffer.alloc(0),
|
|
126
|
-
currentVersion: () => 1,
|
|
127
|
-
isAvailable: async () => true,
|
|
128
|
-
};
|
|
129
|
-
// envSource is empty: if the override weren't honoured, createEnvMasterKeyProvider
|
|
130
|
-
// would throw "no KEK". It doesn't → the override path is taken.
|
|
131
|
-
const ctx = buildBootExtraContext({
|
|
132
|
-
db: fakeDb,
|
|
133
|
-
features: [createSecretsFeature()],
|
|
134
|
-
envSource: {},
|
|
135
|
-
registry,
|
|
136
|
-
hasAuth: false,
|
|
137
|
-
masterKey: override,
|
|
138
|
-
});
|
|
139
|
-
expect(ctx["secrets"]).toBeDefined();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test("audit feature mounted → _escapeHatchAuditSink wired", () => {
|
|
143
|
-
const ctx = buildBootExtraContext({
|
|
144
|
-
db: fakeDb,
|
|
145
|
-
features: [createAuditFeature()],
|
|
146
|
-
envSource: {},
|
|
147
|
-
registry,
|
|
148
|
-
hasAuth: false,
|
|
149
|
-
});
|
|
150
|
-
expect(typeof ctx["_escapeHatchAuditSink"]).toBe("function");
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("no audit feature → _escapeHatchAuditSink not wired", () => {
|
|
154
|
-
const ctx = buildBootExtraContext({
|
|
155
|
-
db: fakeDb,
|
|
156
|
-
features: [otherFeature],
|
|
157
|
-
envSource: {},
|
|
158
|
-
registry,
|
|
159
|
-
hasAuth: false,
|
|
160
|
-
});
|
|
161
|
-
expect(ctx["_escapeHatchAuditSink"]).toBeUndefined();
|
|
162
|
-
});
|
|
163
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Shared fixture for run-prod-app-{dry-run,env-source}.test.ts — both boot
|
|
2
|
-
// runProdApp against a minimal probe feature with process.env cleared so the
|
|
3
|
-
// test fully controls config via envSource. Table/feature name stay
|
|
4
|
-
// parametrized (never collapsed to one shared fixture): a table clash
|
|
5
|
-
// between the two test files' entities would surface as flaky cross-file
|
|
6
|
-
// DB state, not a compile error.
|
|
7
|
-
import { afterEach, beforeEach } from "bun:test";
|
|
8
|
-
import {
|
|
9
|
-
createBooleanField,
|
|
10
|
-
createEntity,
|
|
11
|
-
createTextField,
|
|
12
|
-
defineFeature,
|
|
13
|
-
type FeatureDefinition,
|
|
14
|
-
type FeatureRegistrar,
|
|
15
|
-
} from "@cosmicdrift/kumiko-framework/engine";
|
|
16
|
-
import * as z from "zod";
|
|
17
|
-
|
|
18
|
-
export function makeProbeFeature(opts: {
|
|
19
|
-
readonly name: string;
|
|
20
|
-
readonly table: string;
|
|
21
|
-
readonly extraSetup?: (r: FeatureRegistrar<string>) => void;
|
|
22
|
-
}): FeatureDefinition {
|
|
23
|
-
const probeEntity = createEntity({
|
|
24
|
-
fields: {
|
|
25
|
-
name: createTextField({ personal: false, reason: "test_fixture", required: true }),
|
|
26
|
-
active: createBooleanField({ default: true }),
|
|
27
|
-
},
|
|
28
|
-
table: opts.table,
|
|
29
|
-
});
|
|
30
|
-
return defineFeature(opts.name, (r) => {
|
|
31
|
-
r.entity("widget", probeEntity);
|
|
32
|
-
opts.extraSetup?.(r);
|
|
33
|
-
r.queryHandler({
|
|
34
|
-
name: "ping",
|
|
35
|
-
schema: z.object({}),
|
|
36
|
-
access: { roles: ["anonymous"] },
|
|
37
|
-
rateLimit: { per: "ip", limit: 60, windowSeconds: 60 },
|
|
38
|
-
handler: async () => ({ pong: true }),
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// DATABASE_URL/REDIS_URL/JWT_SECRET are required (their read throws
|
|
44
|
-
// pre-#1441-fix boot bugs); PORT is non-throwing, cleared only so ambient
|
|
45
|
-
// PORT can't mask an "envSource wins" assertion.
|
|
46
|
-
export const CLEARED_BOOT_VARS = ["DATABASE_URL", "REDIS_URL", "JWT_SECRET", "PORT"] as const;
|
|
47
|
-
|
|
48
|
-
// Registers beforeEach/afterEach for the current describe block — call this
|
|
49
|
-
// at the top of a `describe(...)` body, same as calling beforeEach directly.
|
|
50
|
-
export function withClearedBootEnv(): void {
|
|
51
|
-
const saved: Record<string, string | undefined> = {};
|
|
52
|
-
beforeEach(() => {
|
|
53
|
-
for (const k of CLEARED_BOOT_VARS) {
|
|
54
|
-
saved[k] = process.env[k];
|
|
55
|
-
delete process.env[k];
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
afterEach(() => {
|
|
59
|
-
for (const k of CLEARED_BOOT_VARS) {
|
|
60
|
-
if (saved[k] === undefined) delete process.env[k];
|
|
61
|
-
else process.env[k] = saved[k];
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
// In-process buildProdBundle — covers Bun.build + HTML render + build-info
|
|
2
|
-
// in the same process (CLI subprocess tests do not contribute to lcov).
|
|
3
|
-
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
5
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
6
|
-
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
7
|
-
import { tmpdir } from "node:os";
|
|
8
|
-
import { join, resolve } from "node:path";
|
|
9
|
-
import { buildProdBundle } from "../build-prod-bundle";
|
|
10
|
-
|
|
11
|
-
const REPO_ROOT = resolve(import.meta.dir, "../../../..");
|
|
12
|
-
|
|
13
|
-
describe("buildProdBundle in-process (Bun.build)", () => {
|
|
14
|
-
let tmp = "";
|
|
15
|
-
|
|
16
|
-
beforeEach(async () => {
|
|
17
|
-
tmp = await mkdtemp(join(tmpdir(), "kumiko-build-inproc-"));
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(async () => {
|
|
21
|
-
await rm(tmp, { recursive: true, force: true });
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
async function writeMinimalClientApp(opts?: { html?: string; client?: string }) {
|
|
25
|
-
await mkdir(join(tmp, "src"), { recursive: true });
|
|
26
|
-
await writeFile(
|
|
27
|
-
join(tmp, "src/client.ts"),
|
|
28
|
-
opts?.client ??
|
|
29
|
-
`const root = document.getElementById("root"); if (root) root.textContent = "hi";`,
|
|
30
|
-
);
|
|
31
|
-
await mkdir(join(tmp, "public"), { recursive: true });
|
|
32
|
-
await writeFile(
|
|
33
|
-
join(tmp, "public/index.html"),
|
|
34
|
-
opts?.html ??
|
|
35
|
-
`<!doctype html><html><head></head><body><div id="root"></div><script type="module" src="/client.js"></script></body></html>`,
|
|
36
|
-
);
|
|
37
|
-
await writeFile(join(tmp, "package.json"), `{"name":"inproc-fixture","private":true}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
test("client.ts → hashed bundle, manifest, build-info, injected script", async () => {
|
|
41
|
-
await writeMinimalClientApp();
|
|
42
|
-
|
|
43
|
-
const result = await buildProdBundle({ cwd: tmp, stylesheet: false });
|
|
44
|
-
|
|
45
|
-
expect(result.manifest["client.js"]).toMatch(/^\/assets\/client-[a-z0-9]+\.js$/);
|
|
46
|
-
expect(result.buildInfo?.id).toMatch(/^[0-9a-f]{12}$/);
|
|
47
|
-
expect(existsSync(join(tmp, "dist/build-info.json"))).toBe(true);
|
|
48
|
-
|
|
49
|
-
const html = await readFile(join(tmp, "dist/index.html"), "utf8");
|
|
50
|
-
expect(html).toContain(`src="${result.manifest["client.js"]}"`);
|
|
51
|
-
expect(html).toContain("__KUMIKO_BUILD__");
|
|
52
|
-
|
|
53
|
-
const assetPath = join(tmp, "dist", result.manifest["client.js"] ?? "");
|
|
54
|
-
expect(existsSync(assetPath)).toBe(true);
|
|
55
|
-
expect(await readFile(assetPath, "utf8")).toContain('"hi"');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("missing index.html → error with template snippet", async () => {
|
|
59
|
-
await mkdir(join(tmp, "src"), { recursive: true });
|
|
60
|
-
await writeFile(join(tmp, "src/client.ts"), `console.log("hi");`);
|
|
61
|
-
await writeFile(join(tmp, "package.json"), `{"name":"no-html","private":true}`);
|
|
62
|
-
|
|
63
|
-
await expect(buildProdBundle({ cwd: tmp, stylesheet: false })).rejects.toThrow(
|
|
64
|
-
/kein index\.html gefunden/,
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("index.html without /client.js placeholder → error with snippet", async () => {
|
|
69
|
-
await writeMinimalClientApp({
|
|
70
|
-
html: `<!doctype html><html><body>no script</body></html>`,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
await expect(buildProdBundle({ cwd: tmp, stylesheet: false })).rejects.toThrow(
|
|
74
|
-
/keinen Entry-Tag für \/client\.js/,
|
|
75
|
-
);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("syntax-broken client → Bun.build rejects", async () => {
|
|
79
|
-
await writeMinimalClientApp({ client: `const x = {{{` });
|
|
80
|
-
|
|
81
|
-
// Bun may throw "Bundle failed" before returning `{ success: false }`.
|
|
82
|
-
await expect(buildProdBundle({ cwd: tmp, stylesheet: false })).rejects.toThrow(
|
|
83
|
-
/Bundle failed|Bun\.build failed/,
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("stylesheet override src/styles.css → hashed styles.css in manifest", async () => {
|
|
88
|
-
// Temp under REPO_ROOT so @tailwindcss/cli + tailwindcss peer resolve
|
|
89
|
-
// (same constraint as renderer-web-css-relocation.integration.test.ts).
|
|
90
|
-
const dir = await mkdtemp(join(REPO_ROOT, ".inproc-styles-"));
|
|
91
|
-
const cwd = join(dir, "app");
|
|
92
|
-
try {
|
|
93
|
-
await mkdir(join(cwd, "src"), { recursive: true });
|
|
94
|
-
await writeFile(
|
|
95
|
-
join(cwd, "src/client.ts"),
|
|
96
|
-
`const root = document.getElementById("root"); if (root) root.textContent = "hi";`,
|
|
97
|
-
);
|
|
98
|
-
await writeFile(join(cwd, "src/styles.css"), "body { margin: 0; }\n");
|
|
99
|
-
await mkdir(join(cwd, "public"), { recursive: true });
|
|
100
|
-
await writeFile(
|
|
101
|
-
join(cwd, "public/index.html"),
|
|
102
|
-
`<!doctype html><html><head><link rel="stylesheet" href="/styles.css" /></head><body><div id="root"></div><script type="module" src="/client.js"></script></body></html>`,
|
|
103
|
-
);
|
|
104
|
-
await writeFile(join(cwd, "package.json"), `{"name":"inproc-styles","private":true}`);
|
|
105
|
-
|
|
106
|
-
const result = await buildProdBundle({ cwd, stylesheet: "src/styles.css" });
|
|
107
|
-
|
|
108
|
-
expect(result.manifest["styles.css"]).toMatch(/^\/assets\/styles-[a-z0-9]+\.css$/);
|
|
109
|
-
const cssPath = join(cwd, "dist", result.manifest["styles.css"] ?? "");
|
|
110
|
-
expect(existsSync(cssPath)).toBe(true);
|
|
111
|
-
expect(await readFile(cssPath, "utf8")).toMatch(/margin:\s*0/);
|
|
112
|
-
|
|
113
|
-
const html = await readFile(join(cwd, "dist/index.html"), "utf8");
|
|
114
|
-
expect(html).toContain(`href="${result.manifest["styles.css"]}"`);
|
|
115
|
-
expect(html).not.toContain('href="/styles.css"');
|
|
116
|
-
} finally {
|
|
117
|
-
await rm(dir, { recursive: true, force: true });
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test("renderer-web stylesheet → woff2 files land where the CSS points, no external font URL", async () => {
|
|
122
|
-
const dir = await mkdtemp(join(REPO_ROOT, ".inproc-fonts-"));
|
|
123
|
-
const cwd = join(dir, "app");
|
|
124
|
-
try {
|
|
125
|
-
await mkdir(join(cwd, "src"), { recursive: true });
|
|
126
|
-
await writeFile(
|
|
127
|
-
join(cwd, "src/client.ts"),
|
|
128
|
-
`const root = document.getElementById("root"); if (root) root.textContent = "hi";`,
|
|
129
|
-
);
|
|
130
|
-
await writeFile(
|
|
131
|
-
join(cwd, "src/styles.css"),
|
|
132
|
-
`@import "@cosmicdrift/kumiko-renderer-web/styles.css";\n`,
|
|
133
|
-
);
|
|
134
|
-
await mkdir(join(cwd, "public"), { recursive: true });
|
|
135
|
-
await writeFile(
|
|
136
|
-
join(cwd, "public/index.html"),
|
|
137
|
-
`<!doctype html><html><head><link rel="stylesheet" href="/styles.css" /></head><body><div id="root"></div><script type="module" src="/client.js"></script></body></html>`,
|
|
138
|
-
);
|
|
139
|
-
await writeFile(join(cwd, "package.json"), `{"name":"inproc-fonts","private":true}`);
|
|
140
|
-
|
|
141
|
-
const result = await buildProdBundle({ cwd, stylesheet: "src/styles.css" });
|
|
142
|
-
|
|
143
|
-
const css = await readFile(join(cwd, "dist", result.manifest["styles.css"] ?? ""), "utf8");
|
|
144
|
-
const referenced = [
|
|
145
|
-
...css.matchAll(/url\("?(\/assets\/kumiko\/fonts\/[a-z0-9-]+\.woff2)"?\)/g),
|
|
146
|
-
].map((m) => m[1] ?? "");
|
|
147
|
-
expect(referenced.length).toBeGreaterThanOrEqual(10);
|
|
148
|
-
for (const url of referenced) {
|
|
149
|
-
const file = join(cwd, "dist", url);
|
|
150
|
-
expect(existsSync(file)).toBe(true);
|
|
151
|
-
// woff2 magic number
|
|
152
|
-
expect((await readFile(file)).subarray(0, 4).toString("latin1")).toBe("wOF2");
|
|
153
|
-
}
|
|
154
|
-
expect(css).not.toMatch(/url\(\s*["']?(https?:)?\/\//);
|
|
155
|
-
expect(css).not.toContain("data:font");
|
|
156
|
-
} finally {
|
|
157
|
-
await rm(dir, { recursive: true, force: true });
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test("missing stylesheet override → tailwind rejects", async () => {
|
|
162
|
-
const dir = await mkdtemp(join(REPO_ROOT, ".inproc-styles-miss-"));
|
|
163
|
-
const cwd = join(dir, "app");
|
|
164
|
-
try {
|
|
165
|
-
await mkdir(join(cwd, "src"), { recursive: true });
|
|
166
|
-
await writeFile(join(cwd, "src/client.ts"), `console.log("hi");`);
|
|
167
|
-
await mkdir(join(cwd, "public"), { recursive: true });
|
|
168
|
-
await writeFile(
|
|
169
|
-
join(cwd, "public/index.html"),
|
|
170
|
-
`<!doctype html><html><body><div id="root"></div><script type="module" src="/client.js"></script></body></html>`,
|
|
171
|
-
);
|
|
172
|
-
await writeFile(join(cwd, "package.json"), `{"name":"inproc-styles-miss","private":true}`);
|
|
173
|
-
|
|
174
|
-
await expect(buildProdBundle({ cwd, stylesheet: "src/missing-theme.css" })).rejects.toThrow(
|
|
175
|
-
/tailwind|Bundle failed|tailwindcss/i,
|
|
176
|
-
);
|
|
177
|
-
} finally {
|
|
178
|
-
await rm(dir, { recursive: true, force: true });
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test("multi-entry client-admin + client-public via kumiko.clientEntries → two hashed bundles", async () => {
|
|
183
|
-
await mkdir(join(tmp, "src"), { recursive: true });
|
|
184
|
-
await writeFile(join(tmp, "src/client-admin.ts"), `console.log("admin");`);
|
|
185
|
-
await writeFile(join(tmp, "src/client-public.ts"), `console.log("public");`);
|
|
186
|
-
await mkdir(join(tmp, "public"), { recursive: true });
|
|
187
|
-
await writeFile(
|
|
188
|
-
join(tmp, "public/index.html"),
|
|
189
|
-
`<!doctype html><html><body><script type="module" src="/client-public.js"></script></body></html>`,
|
|
190
|
-
);
|
|
191
|
-
await writeFile(
|
|
192
|
-
join(tmp, "admin.html"),
|
|
193
|
-
`<!doctype html><html><body><script type="module" src="/client-admin.js"></script></body></html>`,
|
|
194
|
-
);
|
|
195
|
-
await writeFile(join(tmp, "package.json"), `{"name":"multi-inproc","private":true}`);
|
|
196
|
-
|
|
197
|
-
const result = await buildProdBundle({
|
|
198
|
-
cwd: tmp,
|
|
199
|
-
stylesheet: false,
|
|
200
|
-
clientEntries: [
|
|
201
|
-
{ name: "admin", sourceFile: "./src/client-admin.ts" },
|
|
202
|
-
{ name: "public", sourceFile: "./src/client-public.ts" },
|
|
203
|
-
],
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
expect(result.manifest["client-admin.js"]).toMatch(/^\/assets\/client-admin-/);
|
|
207
|
-
expect(result.manifest["client-public.js"]).toMatch(/^\/assets\/client-public-/);
|
|
208
|
-
expect(existsSync(join(tmp, "dist/admin.html"))).toBe(true);
|
|
209
|
-
expect(existsSync(join(tmp, "dist/index.html"))).toBe(true);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
test("undeclared helper next to declared entries produces no bundle for it", async () => {
|
|
213
|
-
await mkdir(join(tmp, "src"), { recursive: true });
|
|
214
|
-
await writeFile(join(tmp, "src/client-public.ts"), `console.log("public");`);
|
|
215
|
-
await writeFile(join(tmp, "src/client-helper.ts"), `export const helper = "unused";`);
|
|
216
|
-
await mkdir(join(tmp, "public"), { recursive: true });
|
|
217
|
-
await writeFile(
|
|
218
|
-
join(tmp, "public/index.html"),
|
|
219
|
-
`<!doctype html><html><body><script type="module" src="/client-public.js"></script></body></html>`,
|
|
220
|
-
);
|
|
221
|
-
await writeFile(join(tmp, "package.json"), `{"name":"undeclared-helper","private":true}`);
|
|
222
|
-
|
|
223
|
-
const result = await buildProdBundle({
|
|
224
|
-
cwd: tmp,
|
|
225
|
-
stylesheet: false,
|
|
226
|
-
clientEntries: [{ name: "public", sourceFile: "./src/client-public.ts" }],
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
expect(Object.keys(result.manifest)).toEqual(["client-public.js"]);
|
|
230
|
-
const assetFiles = readdirSync(join(tmp, "dist/assets"));
|
|
231
|
-
expect(assetFiles.some((f) => f.startsWith("client-helper-"))).toBe(false);
|
|
232
|
-
});
|
|
233
|
-
});
|