@cosmicdrift/kumiko-server-runtime 0.385.0 → 0.387.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/LICENSE +21 -57
- package/dist/boot/boot-crypto.js +1 -2
- package/dist/boot/worker-boot-core.js +2 -2
- package/dist/build-prod-bundle.d.ts +16 -15
- package/dist/build-prod-bundle.js +106 -108
- package/dist/extra-routes-deps.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/pii-boot-gate.js +1 -1
- package/dist/resolve-tailwind-cli.js +5 -7
- package/dist/run-prod-app-boot-context.d.ts +2 -2
- package/dist/run-prod-app-boot-context.js +21 -22
- package/dist/run-prod-app-static-files.js +43 -50
- package/dist/run-prod-app.d.ts +73 -85
- package/dist/run-prod-app.js +97 -114
- package/dist/run-worker-app.js +5 -5
- package/dist/session-boot-gate.d.ts +1 -1
- package/dist/session-boot-gate.js +1 -1
- package/dist/session-wiring.d.ts +1 -1
- package/dist/session-wiring.js +1 -1
- package/dist/try-hono-first.js +1 -1
- package/package.json +8 -8
package/dist/run-prod-app.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
/// <reference types="bun-types" preserve="true" />
|
|
2
|
-
// runProdApp — production-grade
|
|
2
|
+
// runProdApp — production-grade bootstrap wrapper for Kumiko apps.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
// - DATABASE_URL / REDIS_URL / JWT_SECRET
|
|
6
|
-
//
|
|
7
|
-
// - Hard
|
|
8
|
-
//
|
|
9
|
-
// Boot
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// -
|
|
13
|
-
//
|
|
14
|
-
// - HTTP
|
|
15
|
-
// - Auth
|
|
16
|
-
//
|
|
4
|
+
// Symmetric with runDevApp, but:
|
|
5
|
+
// - DATABASE_URL / REDIS_URL / JWT_SECRET come from env (fail-fast at boot;
|
|
6
|
+
// no ephemeral test databases)
|
|
7
|
+
// - Hard schema-drift gate: checks kumiko/migrations against
|
|
8
|
+
// _kumiko_migrations and verifies every expected table with tableExists.
|
|
9
|
+
// Boot never runs CREATE TABLE automatically; migrations are applied by
|
|
10
|
+
// CI (`bun kumiko schema apply`) and boot only validates them. This avoids
|
|
11
|
+
// races in multi-replica deployments and keeps schema state reviewable.
|
|
12
|
+
// - Idempotent seeds run only when the database is empty, using an
|
|
13
|
+
// `isDbEmpty` probe per seed. Reboots after the first seed are no-ops.
|
|
14
|
+
// - HTTP server via Bun.serve with graceful SIGTERM/SIGINT → drain().
|
|
15
|
+
// - Auth routes and bundled features are mixed automatically when `auth:` is
|
|
16
|
+
// set, using the same logic as runDevApp.
|
|
17
17
|
//
|
|
18
|
-
// App
|
|
18
|
+
// App authors write:
|
|
19
19
|
// await runProdApp({ features, envSchema, auth, anonymousAccess, seeds });
|
|
20
20
|
//
|
|
21
|
-
// Container/Coolify
|
|
21
|
+
// Container/Coolify sets:
|
|
22
22
|
// DATABASE_URL=postgresql://...
|
|
23
23
|
// REDIS_URL=redis://...
|
|
24
|
-
// JWT_SECRET=<random-32+> (always required — also signs
|
|
25
|
-
//
|
|
26
|
-
// — additionally, for zero-downtime rotation of the session-JWT
|
|
27
|
-
//
|
|
24
|
+
// JWT_SECRET=<random-32+> (always required — also signs password-reset and
|
|
25
|
+
// email-verification HMAC tokens, a separate non-rotating token family)
|
|
26
|
+
// — additionally, for zero-downtime rotation of the session-JWT signing
|
|
27
|
+
// key specifically:
|
|
28
28
|
// JWT_SECRET_V1=<random-32+> (repeat _V2, _V3, ... per rotation)
|
|
29
29
|
// JWT_SECRET_CURRENT_VERSION=1 (which V<n> signs new tokens; the others
|
|
30
30
|
// still verify in-flight tokens until they expire)
|
|
31
|
-
//
|
|
32
|
-
// plain JWT_SECRET
|
|
33
|
-
// verify-only legacy key (loadJwtSecretOrKeyring), so sessions signed
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
31
|
+
// When adopting rotation for the first time (with no JWT_SECRET_V<n> set),
|
|
32
|
+
// the plain JWT_SECRET remains set and is carried into the keyring as a
|
|
33
|
+
// verify-only legacy key (loadJwtSecretOrKeyring), so sessions signed before
|
|
34
|
+
// cutover continue to verify without a mass logout. This legacy key never
|
|
35
|
+
// expires automatically; boot logs a warning while it is present. Once the
|
|
36
|
+
// maximum token TTL has elapsed since cutover, unset JWT_SECRET to retire
|
|
37
|
+
// it. First check whether the app relies on auth.mail's convenience default
|
|
38
|
+
// for passwordReset/emailVerification hmacSecret, which falls back to this
|
|
39
|
+
// same JWT_SECRET.
|
|
40
40
|
// PORT=3000
|
|
41
41
|
// KUMIKO_INSTANCE_ID=<stable per replica>
|
|
42
42
|
import { AuthErrors, AuthHandlers, AuthQueries, } from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
|
|
@@ -219,7 +219,7 @@ export async function runProdApp(options) {
|
|
|
219
219
|
}
|
|
220
220
|
const jwtIssuer = readEnv("JWT_ISSUER", envSource);
|
|
221
221
|
const instanceId = readEnv("KUMIKO_INSTANCE_ID", envSource);
|
|
222
|
-
//
|
|
222
|
+
// options.auth.trustedProxyHops wins; falls back
|
|
223
223
|
// to the env var so ops can close the XFF-spoofing hole per-deployment
|
|
224
224
|
// without a code change (mirrors instanceId's env-first pattern above).
|
|
225
225
|
const trustedProxyHops = resolveTrustedProxyHops({
|
|
@@ -237,9 +237,9 @@ export async function runProdApp(options) {
|
|
|
237
237
|
const resolvedRateLimit = resolveRateLimit(options.rateLimit);
|
|
238
238
|
const port = options.port ?? Number.parseInt(envSource["PORT"] ?? "3000", 10);
|
|
239
239
|
log.info(`booting Kumiko stack on port ${port}`);
|
|
240
|
-
// auth.mail
|
|
241
|
-
//
|
|
242
|
-
//
|
|
240
|
+
// auth.mail is expanded into the explicit flow fields before feeding both
|
|
241
|
+
// the feature side (buildComposeAuthOptions) and the routes fragment below.
|
|
242
|
+
// From here on, always read effectiveAuth instead of options.auth.
|
|
243
243
|
const effectiveAuth = options.auth
|
|
244
244
|
? resolveAuthMail(options.auth, jwtSecret, envSource, {
|
|
245
245
|
mailConfigured: bootOptionWiring.mailConfigured,
|
|
@@ -257,11 +257,10 @@ export async function runProdApp(options) {
|
|
|
257
257
|
envSource,
|
|
258
258
|
warn: (message) => createFallbackLogger("runProdApp").warn(message),
|
|
259
259
|
});
|
|
260
|
-
// 3. Feature registry. Auth
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
// can validate wiring and exit without opening a Postgres/Redis socket.
|
|
260
|
+
// 3. Feature registry. Auth mode automatically mixes config/user/tenant/
|
|
261
|
+
// auth-email-password via composeFeatures, using the same feature list as
|
|
262
|
+
// runDevApp and the per-app schema generator. Build it before any connection
|
|
263
|
+
// so boot mode can validate wiring without opening Postgres or Redis sockets.
|
|
265
264
|
const composeAuthOptions = buildComposeAuthOptions(effectiveAuth);
|
|
266
265
|
const features = composeFeatures([...options.features, ...bootOptionWiring.features], {
|
|
267
266
|
includeBundled: !!effectiveAuth,
|
|
@@ -330,7 +329,7 @@ export async function runProdApp(options) {
|
|
|
330
329
|
const redis = new Redis(redisUrl, { maxRetriesPerRequest: null });
|
|
331
330
|
logRedisConnectionState(redis, log);
|
|
332
331
|
await waitForDatabase(db, { processName: "runProdApp" });
|
|
333
|
-
//
|
|
332
|
+
// Tier-Composition auto-wire: scan features for a
|
|
334
333
|
// tenantTierResolver-extension. If found, build the resolver here (db +
|
|
335
334
|
// registry are available) before the dispatcher is constructed. App-Author sees
|
|
336
335
|
// nothing — `createTierEngineFeature(opts)` mounts + framework auto-wires.
|
|
@@ -358,14 +357,13 @@ export async function runProdApp(options) {
|
|
|
358
357
|
});
|
|
359
358
|
resolvedEffectiveFeatures = bound.resolver;
|
|
360
359
|
}
|
|
361
|
-
// 5. Schema-
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
// `migrations: false` für custom Schema-Setups.
|
|
360
|
+
// 5. Schema-drift gate (without drizzle, using kumiko/migrations). `kumiko
|
|
361
|
+
// schema apply` runs as a deploy step before container rollout. Boot only
|
|
362
|
+
// verifies that every kumiko/migrations/*.sql file is applied in
|
|
363
|
+
// _kumiko_migrations with an unchanged checksum and that every table in
|
|
364
|
+
// kumiko/migrations/.snapshot.json exists. Drift aborts boot rather than being auto-healed, avoiding
|
|
365
|
+
// races between container replicas. `migrations: false` opts out for custom
|
|
366
|
+
// schema setups.
|
|
369
367
|
if (options.migrations !== false) {
|
|
370
368
|
const migrationsDir = options.migrations?.dir ?? "./kumiko/migrations";
|
|
371
369
|
log.info(`checking schema drift (${migrationsDir})`);
|
|
@@ -383,27 +381,20 @@ export async function runProdApp(options) {
|
|
|
383
381
|
const idempotency = createIdempotencyGuard(redis, { ttlSeconds: 60 });
|
|
384
382
|
const eventDedup = createEventDedup(redis, { ttlSeconds: 60 });
|
|
385
383
|
const entityCache = createEntityCache(redis, { ttlSeconds: 60 });
|
|
386
|
-
// 7.
|
|
387
|
-
//
|
|
388
|
-
// for SIGTERM.
|
|
384
|
+
// 7. When no lifecycle is supplied, createApiEntrypoint builds and owns it;
|
|
385
|
+
// the handle exposes it for SIGTERM.
|
|
389
386
|
//
|
|
390
|
-
// extraContext
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
// exactly one broker. createDefaultSseBroker (not createSseBroker
|
|
402
|
-
// directly) is what makes that broker Redis-backed under REDIS_URL —
|
|
403
|
-
// an unconditional in-memory broker here would silently win over
|
|
404
|
-
// buildServer's own REDIS_URL default (ServerOptions.sseBroker always
|
|
405
|
-
// wins), which is exactly how fw#2630's cross-replica fanout fix shipped
|
|
406
|
-
// without ever taking effect in production.
|
|
387
|
+
// extraContext and anonymousAccess accept either an object or a factory that
|
|
388
|
+
// receives {db, redis, registry}; factories are useful when boot-time database
|
|
389
|
+
// access is needed, such as a subdomain-to-tenant resolver.
|
|
390
|
+
//
|
|
391
|
+
// Build the SSE broker here so extraContext factories can close over it, and
|
|
392
|
+
// pass the same broker into createApiEntrypoint (sseBroker option) so
|
|
393
|
+
// server-internal broadcasts and app-specific publishes share one broker. Use
|
|
394
|
+
// createDefaultSseBroker so REDIS_URL selects a Redis-backed broker; an
|
|
395
|
+
// unconditional in-memory broker would silently override buildServer's
|
|
396
|
+
// Redis-backed default (ServerOptions.sseBroker always wins) and prevent
|
|
397
|
+
// cross-replica fanout.
|
|
407
398
|
const { sseBroker, ownedRedisSseBroker } = createDefaultSseBroker(envSource);
|
|
408
399
|
const deps = {
|
|
409
400
|
db,
|
|
@@ -417,20 +408,19 @@ export async function runProdApp(options) {
|
|
|
417
408
|
const resolvedExtraContext = typeof options.extraContext === "function"
|
|
418
409
|
? options.extraContext(deps)
|
|
419
410
|
: (options.extraContext ?? {});
|
|
420
|
-
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
423
|
-
//
|
|
411
|
+
// Install framework defaults first; app values in resolvedExtraContext always
|
|
412
|
+
// win (for example, an app-specific configResolver).
|
|
413
|
+
// The app-wide cipher is resolved lazily, so entities without encrypted
|
|
414
|
+
// fields never access it.
|
|
424
415
|
configureEntityFieldEncryption(bootCrypto.entityFieldCipher);
|
|
425
|
-
// Subject-key KMS for pii-annotated fields (crypto-shredding
|
|
416
|
+
// Subject-key KMS for pii-annotated fields (crypto-shredding).
|
|
426
417
|
// No adapter only gets this far when assertPiiBootInvariants above let it
|
|
427
418
|
// through: no PII carriers, or an explicit allowPlaintextPii opt-out. The
|
|
428
419
|
// engine then stays off and those fields are stored in plaintext.
|
|
429
420
|
configurePiiSubjectKms(bootKms.kms);
|
|
430
|
-
// Blind-
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
// silent-broken-auth.
|
|
421
|
+
// Blind-index key boot gate: with KMS enabled, lookupable fields are stored as
|
|
422
|
+
// ciphertext. Without the key, equality lookups such as login by email cannot
|
|
423
|
+
// work, so fail fast rather than allowing silently broken authentication.
|
|
434
424
|
configureBlindIndexKey(bootKms.blindIndexKey);
|
|
435
425
|
const bootLogger = loggerFromExtraContext(resolvedExtraContext);
|
|
436
426
|
if (bootLogger)
|
|
@@ -456,21 +446,19 @@ export async function runProdApp(options) {
|
|
|
456
446
|
const baseAnonymousAccess = typeof options.anonymousAccess === "function"
|
|
457
447
|
? options.anonymousAccess(deps)
|
|
458
448
|
: options.anonymousAccess;
|
|
459
|
-
//
|
|
449
|
+
// tenantResolver / tenantExists come from auth-foundation providers.
|
|
460
450
|
const resolvedAnonymousAccess = await resolveAnonymousAccessFromRegistry(baseAnonymousAccess, {
|
|
461
451
|
db,
|
|
462
452
|
registry,
|
|
463
453
|
warn: (message) => createFallbackLogger("runProdApp").warn(message),
|
|
464
454
|
});
|
|
465
|
-
// Sessions
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
//
|
|
472
|
-
// über die auth-routes.
|
|
473
|
-
// Secure-by-default (#1372): sessionStore provider → resolveSessionStore.
|
|
455
|
+
// Sessions are wired directly because db has already been created; unlike
|
|
456
|
+
// runDevApp, no late binding is needed. Once a sessionChecker is wired, JWTs
|
|
457
|
+
// without sid are rejected, so production sessions cannot be bypassed.
|
|
458
|
+
// sessionMassRevoker (the fourth callback from createSessionCallbacks) is not
|
|
459
|
+
// part of AuthRoutesConfig; bindAutoRevokeFromFeature hands it to the sessions
|
|
460
|
+
// feature so password change and reset revoke all sessions. Secure by default: the sessionStore provider
|
|
461
|
+
// goes through resolveSessionStore.
|
|
474
462
|
const mfaFeature = features.find((f) => f.name === AUTH_MFA_FEATURE);
|
|
475
463
|
if (mfaFeature)
|
|
476
464
|
bindMfaTokenSecretsFromFeature(mfaFeature)?.(jwtSecret, envSource);
|
|
@@ -551,7 +539,7 @@ export async function runProdApp(options) {
|
|
|
551
539
|
// Redis-backed, not the in-memory default createAuthRoutes falls
|
|
552
540
|
// back to — an in-process limiter only rate-limits within a single
|
|
553
541
|
// replica, so a multi-instance prod deployment would silently give
|
|
554
|
-
// each replica its own bucket
|
|
542
|
+
// each replica its own bucket. Redis is required infra
|
|
555
543
|
// here already (REDIS_URL), so this is free.
|
|
556
544
|
loginRateLimit: createRedisLoginRateLimiter(redis),
|
|
557
545
|
...(effectiveAuth.cookieDomain !== undefined && {
|
|
@@ -621,17 +609,15 @@ export async function runProdApp(options) {
|
|
|
621
609
|
}),
|
|
622
610
|
...(resolvedAnonymousAccess && { anonymousAccess: resolvedAnonymousAccess }),
|
|
623
611
|
};
|
|
624
|
-
//
|
|
625
|
-
//
|
|
626
|
-
//
|
|
627
|
-
//
|
|
628
|
-
//
|
|
629
|
-
// runSingleInstance:false
|
|
630
|
-
// worker
|
|
631
|
-
//
|
|
632
|
-
//
|
|
633
|
-
// false honorieren (api-only, kein lokaler Dispatcher), damit der explizite
|
|
634
|
-
// Flag Vorrang behält aber Bestands-Caller nicht brechen.
|
|
612
|
+
// Deployment topology. By default, createAllInOneEntrypoint runs HTTP, both
|
|
613
|
+
// job lanes (two runners, each scheduling only its own lane's crons, so
|
|
614
|
+
// nothing fires twice) and the event dispatcher inline. That way worker-lane
|
|
615
|
+
// crons (run-export-jobs defaults to runIn:"worker") and MSPs run without a
|
|
616
|
+
// separate worker process, the gap the data export used to stall on.
|
|
617
|
+
// runSingleInstance:false means API-only: a dedicated worker must then take
|
|
618
|
+
// over the worker lane and MSPs, while API-lane jobs keep running locally.
|
|
619
|
+
// The legacy eventDispatcher.disabled maps to runSingleInstance:false so
|
|
620
|
+
// existing callers keep working; the explicit flag takes precedence.
|
|
635
621
|
const runSingleInstance = options.runSingleInstance ?? options.eventDispatcher?.disabled !== true;
|
|
636
622
|
const hasJobs = registry.getAllJobs().size > 0;
|
|
637
623
|
const queueNamePrefix = options.jobs?.queueNamePrefix;
|
|
@@ -670,7 +656,7 @@ export async function runProdApp(options) {
|
|
|
670
656
|
// entrypoint's. Registered after createApiEntrypoint/createAllInOneEntrypoint
|
|
671
657
|
// (which already registered the eventDispatcher/jobRunner hooks above),
|
|
672
658
|
// so it drains LIFO before those, matching the close-before-teardown
|
|
673
|
-
// order
|
|
659
|
+
// order established for buildServer's own hook.
|
|
674
660
|
entrypoint.lifecycle.registerShutdownHook("prodAppCacheSync", async () => {
|
|
675
661
|
await cacheSync.close();
|
|
676
662
|
});
|
|
@@ -684,7 +670,7 @@ export async function runProdApp(options) {
|
|
|
684
670
|
// createApiEntrypoint/createAllInOneEntrypoint, before this function ever
|
|
685
671
|
// runs. `wire` is the remaining escape-hatch for app-wired co-running
|
|
686
672
|
// components that need the system-write dispatcher but don't declare a
|
|
687
|
-
// route
|
|
673
|
+
// route.
|
|
688
674
|
if (options.wire) {
|
|
689
675
|
await options.wire({
|
|
690
676
|
db,
|
|
@@ -746,18 +732,17 @@ export async function runProdApp(options) {
|
|
|
746
732
|
});
|
|
747
733
|
}
|
|
748
734
|
}
|
|
749
|
-
// ES
|
|
750
|
-
//
|
|
751
|
-
//
|
|
752
|
-
//
|
|
753
|
-
// Membership-Roles-Change nach initialer Seed-Erstellung).
|
|
735
|
+
// ES operations and seed migrations run after applyBootSeeds and the existing
|
|
736
|
+
// seeds array. Declarative seeds insert missing records; seed migrations
|
|
737
|
+
// diff and update state that existing seeds cannot revise, such as membership
|
|
738
|
+
// roles changed after initial seed creation.
|
|
754
739
|
if (seedDispatcher && options.seedsDir !== undefined && envSource["KUMIKO_SKIP_ES_OPS"] !== "1") {
|
|
755
740
|
await createEsOperationsTable(db);
|
|
756
741
|
await runPendingSeedMigrations({
|
|
757
742
|
db,
|
|
758
743
|
seedsDir: options.seedsDir,
|
|
759
744
|
appliedBy: "boot",
|
|
760
|
-
registry, //
|
|
745
|
+
registry, // The dry-run validator catches handler QN typos before writing.
|
|
761
746
|
// @wrapper-known semantic-alias
|
|
762
747
|
createContext: (dbRunner) => createSeedMigrationContext({ dispatcher: seedDispatcher, dbRunner }),
|
|
763
748
|
});
|
|
@@ -800,15 +785,13 @@ export async function runProdApp(options) {
|
|
|
800
785
|
fetch: fetchHandler,
|
|
801
786
|
webSocketUpgradeFetch,
|
|
802
787
|
listen: async (listenPort = port) => {
|
|
803
|
-
// Bun.serve
|
|
804
|
-
//
|
|
805
|
-
//
|
|
806
|
-
// exportierten buildBunServeOptions-Funktion — siehe ihren
|
|
807
|
-
// Header für die Begründung.
|
|
788
|
+
// Bun.serve provides production HTTP. Tests run under Node, so they call the
|
|
789
|
+
// fetch handler directly instead. buildBunServeOptions centralizes the
|
|
790
|
+
// options shape, including `idleTimeout: 0` for SSE.
|
|
808
791
|
if (typeof Bun === "undefined") {
|
|
809
|
-
//
|
|
810
|
-
//
|
|
811
|
-
//
|
|
792
|
+
// Throw a clear error instead of a raw ReferenceError when listen() is called
|
|
793
|
+
// under Node/vitest without autoListen:false, making the runtime
|
|
794
|
+
// requirement explicit.
|
|
812
795
|
throw new Error("[runProdApp] listen() requires Bun runtime (Bun.serve). " +
|
|
813
796
|
"Under Node/vitest pass `autoListen: false` and call the returned `fetch()` directly.");
|
|
814
797
|
}
|
package/dist/run-worker-app.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
// Hono app, no auth routes, no SSE broker, no seeds. Shares the boot core
|
|
4
4
|
// with runProdApp (env fail-fast, Temporal polyfill, composeFeatures/
|
|
5
5
|
// registry, PII invariants, KMS health gate, connections, schema-drift
|
|
6
|
-
// gate, boot-crypto, extraContext)
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
6
|
+
// gate, boot-crypto, extraContext). Before this function, every app
|
|
7
|
+
// deploying a worker rebuilt this boot by hand, and every deviation was
|
|
8
|
+
// silent: without `ensureTemporalPolyfill`, every job in the worker fails
|
|
9
|
+
// with "Temporal is not defined" in a retry loop, with no boot-time signal
|
|
10
|
+
// that anything is wrong.
|
|
11
11
|
//
|
|
12
12
|
// App-author writes:
|
|
13
13
|
// await runWorkerApp({ features, wireComponents: async (deps) => {...} });
|
|
@@ -5,7 +5,7 @@ export type SessionBootGateOptions = {
|
|
|
5
5
|
* silently skip server-side revocation and any session-list UI stays
|
|
6
6
|
* empty); dev only warns — a forgotten sessions mount in a local sample
|
|
7
7
|
* app shouldn't crash the dev-server boot, but it must not stay silent
|
|
8
|
-
* either
|
|
8
|
+
* either. */
|
|
9
9
|
readonly mode: "prod" | "dev";
|
|
10
10
|
};
|
|
11
11
|
export declare function assertSessionBootInvariants(opts: SessionBootGateOptions): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Catch a forgotten sessions mount at boot instead of silently degrading
|
|
2
|
-
// into stateless JWTs
|
|
2
|
+
// into stateless JWTs. Mount createSessionsFeature() for revocable
|
|
3
3
|
// sessions; there is no auth.sessions opt-out anymore.
|
|
4
4
|
export function assertSessionBootInvariants(opts) {
|
|
5
5
|
// skip: no auth mounted — nothing to gate.
|
package/dist/session-wiring.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* runProdApp session-wiring decision (extracted pure so it is testable without a
|
|
3
3
|
* full prod boot).
|
|
4
4
|
*
|
|
5
|
-
* Secure-by-default
|
|
5
|
+
* Secure-by-default: mounting a sessionStore provider (sessions feature)
|
|
6
6
|
* turns server-side session revocation ON automatically. There is no
|
|
7
7
|
* `auth.sessions` opt-in/opt-out — mount sessions for revocable JWTs, omit it
|
|
8
8
|
* for intentional stateless JWTs (boot-gate warns / aborts unless acknowledged
|
package/dist/session-wiring.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* runProdApp session-wiring decision (extracted pure so it is testable without a
|
|
3
3
|
* full prod boot).
|
|
4
4
|
*
|
|
5
|
-
* Secure-by-default
|
|
5
|
+
* Secure-by-default: mounting a sessionStore provider (sessions feature)
|
|
6
6
|
* turns server-side session revocation ON automatically. There is no
|
|
7
7
|
* `auth.sessions` opt-in/opt-out — mount sessions for revocable JWTs, omit it
|
|
8
8
|
* for intentional stateless JWTs (boot-gate warns / aborts unless acknowledged
|
package/dist/try-hono-first.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
//
|
|
15
15
|
// The status code alone used to be the only signal ("404 = no match") —
|
|
16
16
|
// that masked any matched route's deliberate 404 (e.g. default-deny reads)
|
|
17
|
-
// as the SPA shell with status 200
|
|
17
|
+
// as the SPA shell with status 200. buildServer
|
|
18
18
|
// (framework/api/server.ts) registers app.notFound() LAST on the app object
|
|
19
19
|
// and marks exactly the "no handler found" case with
|
|
20
20
|
// NO_ROUTE_MATCH_HEADER_NAME — a handler that builds its own
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.387.0",
|
|
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 tooling.",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -88,18 +88,18 @@
|
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
90
|
"dependencies": {
|
|
91
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
92
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
93
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
94
|
-
"@cosmicdrift/kumiko-renderer-mail-html": "0.
|
|
95
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
91
|
+
"@cosmicdrift/kumiko-bundled-features": "0.387.0",
|
|
92
|
+
"@cosmicdrift/kumiko-framework": "0.387.0",
|
|
93
|
+
"@cosmicdrift/kumiko-renderer": "0.387.0",
|
|
94
|
+
"@cosmicdrift/kumiko-renderer-mail-html": "0.387.0",
|
|
95
|
+
"@cosmicdrift/kumiko-types": "0.387.0",
|
|
96
96
|
"bun-types": "^1.3.13",
|
|
97
97
|
"ioredis": "^5.10.1",
|
|
98
98
|
"temporal-polyfill": "^0.3.2",
|
|
99
99
|
"zod": "^4.4.3"
|
|
100
100
|
},
|
|
101
101
|
"devDependencies": {
|
|
102
|
-
"@cosmicdrift/kumiko-testing": "0.
|
|
102
|
+
"@cosmicdrift/kumiko-testing": "0.387.0"
|
|
103
103
|
},
|
|
104
104
|
"publishConfig": {
|
|
105
105
|
"registry": "https://registry.npmjs.org",
|