@cosmicdrift/kumiko-dev-server 0.196.1 → 0.197.1
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-dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.197.1",
|
|
4
4
|
"description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding, codegen. Not shipped into production node_modules — see @cosmicdrift/kumiko-server-runtime for the prod boot path.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"kumiko-schema-check": "./bin/kumiko-schema-check.ts"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
58
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
59
|
-
"@cosmicdrift/kumiko-server-runtime": "0.
|
|
57
|
+
"@cosmicdrift/kumiko-bundled-features": "0.197.1",
|
|
58
|
+
"@cosmicdrift/kumiko-framework": "0.197.1",
|
|
59
|
+
"@cosmicdrift/kumiko-server-runtime": "0.197.1",
|
|
60
60
|
"ts-morph": "^28.0.0"
|
|
61
61
|
},
|
|
62
62
|
"publishConfig": {
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
// run-prod-app forwarding pair. Without it a typo or wrong spread-key on the dev
|
|
6
6
|
// path would silently drop the fail-closed guard and dev/prod would diverge.
|
|
7
7
|
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
9
|
+
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
10
|
+
import { createPersonalAccessTokensFeature } from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens";
|
|
11
|
+
import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
|
|
9
12
|
import {
|
|
10
13
|
requireTemplateResolver,
|
|
11
14
|
TEXT_BLOCK_KIND,
|
|
@@ -16,8 +19,10 @@ import {
|
|
|
16
19
|
createTextField,
|
|
17
20
|
defineFeature,
|
|
18
21
|
defineQueryHandler,
|
|
22
|
+
type TenantId,
|
|
19
23
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
20
24
|
import { TestUsers } from "@cosmicdrift/kumiko-framework/stack";
|
|
25
|
+
import * as jose from "jose";
|
|
21
26
|
import { z } from "zod";
|
|
22
27
|
import type { KumikoServerHandle } from "../create-kumiko-server";
|
|
23
28
|
import { runDevApp } from "../run-dev-app";
|
|
@@ -174,3 +179,66 @@ describe("runDevApp — extraContext merge order: app values win over boot defau
|
|
|
174
179
|
expect(res?.title).toBe("from caller extraContext");
|
|
175
180
|
});
|
|
176
181
|
});
|
|
182
|
+
|
|
183
|
+
// runDevApp had no equivalent of runProdApp's session boot gate (#1262/#1275):
|
|
184
|
+
// a forgotten sessions mount left session-list silently empty instead of
|
|
185
|
+
// saying anything (#2027). Unlike prod, dev warns rather than aborts —
|
|
186
|
+
// mirrors assertPiiBootInvariants' dev/prod split.
|
|
187
|
+
describe("runDevApp — session boot gate (#2027)", () => {
|
|
188
|
+
const ADMIN = {
|
|
189
|
+
email: "session-gate-dev@example.eu",
|
|
190
|
+
password: "test-pw-strong-1234",
|
|
191
|
+
displayName: "Admin",
|
|
192
|
+
emailVerified: true,
|
|
193
|
+
memberships: [
|
|
194
|
+
{
|
|
195
|
+
tenantId: "00000000-0000-4000-8000-000000000002" as TenantId,
|
|
196
|
+
tenantKey: "session-gate-dev",
|
|
197
|
+
tenantName: "Session Gate Dev",
|
|
198
|
+
roles: ["Admin"],
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
test("auth mounted, sessions feature missing → warns but still boots (dev doesn't abort)", async () => {
|
|
204
|
+
const warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
205
|
+
try {
|
|
206
|
+
handle = await runDevApp({
|
|
207
|
+
features: [validFeature()],
|
|
208
|
+
port: 0,
|
|
209
|
+
auth: { admin: ADMIN },
|
|
210
|
+
});
|
|
211
|
+
expect(handle).toBeDefined();
|
|
212
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("sessionStore"));
|
|
213
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("[runDevApp]"));
|
|
214
|
+
} finally {
|
|
215
|
+
warnSpy.mockRestore();
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("auth mounted, sessions feature mounted → login actually creates a session (sid on the JWT)", async () => {
|
|
220
|
+
// The bug this issue reports: a mounted sessions feature whose sessionCreator
|
|
221
|
+
// never reached the auth config, so login never got a `sid` and
|
|
222
|
+
// store_user_sessions never got a row — session-list stayed empty with no
|
|
223
|
+
// error anywhere. A boot that doesn't throw proves nothing about that; only
|
|
224
|
+
// a real login + decoded JWT does.
|
|
225
|
+
handle = await runDevApp({
|
|
226
|
+
features: [
|
|
227
|
+
authFoundationFeature,
|
|
228
|
+
createPersonalAccessTokensFeature({ scopes: {} }),
|
|
229
|
+
createSessionsFeature(),
|
|
230
|
+
],
|
|
231
|
+
port: 0,
|
|
232
|
+
auth: { admin: ADMIN },
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const login = await handle.stack.http.raw("POST", "/api/auth/login", {
|
|
236
|
+
email: ADMIN.email,
|
|
237
|
+
password: ADMIN.password,
|
|
238
|
+
});
|
|
239
|
+
expect(login.status).toBe(200);
|
|
240
|
+
const body = (await login.json()) as { token?: string };
|
|
241
|
+
expect(body.token).toBeTypeOf("string");
|
|
242
|
+
expect(typeof jose.decodeJwt(body.token!).jti).toBe("string");
|
|
243
|
+
});
|
|
244
|
+
});
|
package/src/run-dev-app.ts
CHANGED
|
@@ -78,6 +78,7 @@ import {
|
|
|
78
78
|
composeFeatures,
|
|
79
79
|
} from "@cosmicdrift/kumiko-server-runtime/compose-features";
|
|
80
80
|
import { assertPiiBootInvariants } from "@cosmicdrift/kumiko-server-runtime/pii-boot-gate";
|
|
81
|
+
import { assertSessionBootInvariants } from "@cosmicdrift/kumiko-server-runtime/session-boot-gate";
|
|
81
82
|
import { watchAndRegenerate } from "./codegen";
|
|
82
83
|
import {
|
|
83
84
|
type CreateKumikoServerOptions,
|
|
@@ -371,13 +372,21 @@ export async function runDevApp(options: RunDevAppOptions): Promise<KumikoServer
|
|
|
371
372
|
blindIndexKey: options.blindIndexKey,
|
|
372
373
|
mode: "dev",
|
|
373
374
|
});
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
375
|
+
// Throwaway registry, built once and reused below — mirrors runProdApp's
|
|
376
|
+
// `createRegistry(features)` right after validateBoot (#2027). Symmetric
|
|
377
|
+
// sessionStoreProviderMounted check to runProdApp's (registry.getExtension-
|
|
378
|
+
// Usages, not a raw features.some(...) scan) so dev and prod never ask the
|
|
379
|
+
// same question two different ways again.
|
|
380
|
+
const registry = effectiveAuth ? createRegistry(features) : undefined;
|
|
381
|
+
const sessionStoreProviderMounted =
|
|
382
|
+
registry !== undefined && registry.getExtensionUsages(EXT_SESSION_STORE).length > 0;
|
|
383
|
+
assertSessionBootInvariants({
|
|
384
|
+
hasAuth: Boolean(effectiveAuth),
|
|
385
|
+
sessionStoreProviderMounted,
|
|
386
|
+
mode: "dev",
|
|
387
|
+
});
|
|
388
|
+
const cfgExtra = registry
|
|
389
|
+
? mergeConfigResolverDefault(options.extraContext, registry, envSource, bootCrypto.configCipher)
|
|
381
390
|
: options.extraContext;
|
|
382
391
|
// Auto-wire templateResolver (immer) + secrets (feature-gated), symmetrisch zu
|
|
383
392
|
// runProdApp. Anders als prod existiert die db hier erst im Factory-deps
|
|
@@ -444,10 +453,9 @@ export async function runDevApp(options: RunDevAppOptions): Promise<KumikoServer
|
|
|
444
453
|
: {};
|
|
445
454
|
|
|
446
455
|
// Parity with runProdApp (#1372 review): gate on sessionStore provider, not
|
|
447
|
-
// the sessions feature name — a custom provider must also wire auth
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
);
|
|
456
|
+
// the sessions feature name — a custom provider must also wire auth
|
|
457
|
+
// callbacks. sessionStoreProviderMounted computed above via the registry
|
|
458
|
+
// (same check as runProdApp, #2027) — not recomputed here.
|
|
451
459
|
const sessionAuthFragment =
|
|
452
460
|
effectiveAuth && sessionStoreProviderMounted
|
|
453
461
|
? {
|