@cosmicdrift/kumiko-server-runtime 0.367.0 → 0.369.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @cosmicdrift/kumiko-server-runtime
2
2
 
3
- Production server boot for Kumiko apps: connections, schema drift gate, seeds, lifecycle and graceful shutdown. The prod counterpart to `runDevApp` in `@cosmicdrift/kumiko-dev-server`, without dev, scaffold or codegen tooling.
3
+ Production server boot for Kumiko apps: connections, schema drift gate, seeds, lifecycle and graceful shutdown. The prod counterpart to `runDevApp` in `@cosmicdrift/kumiko-dev-server`, without dev or scaffold tooling.
4
4
 
5
5
  ## Install
6
6
 
@@ -105,8 +105,14 @@ export async function bootWorkerProcess(options, envSource, profile) {
105
105
  throw new Error(`[${processName}] BOOT ABORTED — KMS health check failed (latency ${kmsHealth.latencyMs}ms)`);
106
106
  }
107
107
  }
108
- const { db, close: closeDb } = createDbConnection(databaseUrl, dbConnectionOptionsFromEnv(envSource));
109
- const { db: outsideTransactionDb, close: closeOutsideTransactionDb } = createDbConnection(databaseUrl, outsideTransactionPoolOptionsFromEnv(envSource));
108
+ const { db, close: closeDb } = createDbConnection({
109
+ url: databaseUrl,
110
+ ...dbConnectionOptionsFromEnv(envSource),
111
+ });
112
+ const { db: outsideTransactionDb, close: closeOutsideTransactionDb } = createDbConnection({
113
+ url: databaseUrl,
114
+ ...outsideTransactionPoolOptionsFromEnv(envSource),
115
+ });
110
116
  const redis = new Redis(redisUrl, { maxRetriesPerRequest: null });
111
117
  const cacheSync = createRedisCacheSyncBus({
112
118
  redisUrl,
@@ -0,0 +1,17 @@
1
+ import { type UnsubscribeLinkConfig } from "@cosmicdrift/kumiko-bundled-features/delivery";
2
+ import { type ExtraRouteDefinition } from "@cosmicdrift/kumiko-framework/api";
3
+ export type UnsubscribeOption = {
4
+ readonly secret: string;
5
+ readonly tenantOrigin?: UnsubscribeLinkConfig["tenantOrigin"];
6
+ };
7
+ export type UnsubscribeWiring = {
8
+ readonly unsubscribeLinks: UnsubscribeLinkConfig | undefined;
9
+ readonly unsubscribeRoutes: readonly ExtraRouteDefinition[];
10
+ };
11
+ export declare function resolveUnsubscribeWiring(opts: {
12
+ readonly unsubscribe: UnsubscribeOption | undefined;
13
+ readonly publicOrigin: string | undefined;
14
+ readonly mailBaseUrl: string | undefined;
15
+ readonly envSource: Record<string, string | undefined>;
16
+ readonly warn: (message: string) => void;
17
+ }): UnsubscribeWiring;
@@ -0,0 +1,24 @@
1
+ import { createUnsubscribeRoutes, } from "@cosmicdrift/kumiko-bundled-features/delivery";
2
+ import { publicOriginOf } from "@cosmicdrift/kumiko-framework/api";
3
+ import { APP_BASE_URL_ENV } from "./resolve-auth-public-origin.js";
4
+ export function resolveUnsubscribeWiring(opts) {
5
+ const { unsubscribe } = opts;
6
+ if (unsubscribe === undefined)
7
+ return { unsubscribeLinks: undefined, unsubscribeRoutes: [] };
8
+ const baseUrl = publicOriginOf(opts.publicOrigin ?? opts.mailBaseUrl ?? opts.envSource[APP_BASE_URL_ENV] ?? "");
9
+ if (baseUrl === undefined) {
10
+ opts.warn(`unsubscribe is enabled but no valid public app URL is known (auth.publicOrigin, auth.mail.baseUrl or ${APP_BASE_URL_ENV}), so mails get no unsubscribe link.`);
11
+ }
12
+ return {
13
+ unsubscribeLinks: baseUrl === undefined
14
+ ? undefined
15
+ : {
16
+ secret: unsubscribe.secret,
17
+ baseUrl,
18
+ ...(unsubscribe.tenantOrigin !== undefined && {
19
+ tenantOrigin: unsubscribe.tenantOrigin,
20
+ }),
21
+ },
22
+ unsubscribeRoutes: createUnsubscribeRoutes({ secret: unsubscribe.secret }),
23
+ };
24
+ }
@@ -66,7 +66,7 @@ function buildDeliveryNotifyFactory(opts) {
66
66
  const systemUser = createSystemUser(tenantId);
67
67
  return configAccessorFactory({
68
68
  user: { id: systemUser.id, tenantId },
69
- db: createTenantDb(opts.db, tenantId),
69
+ db: createTenantDb({ db: opts.db, tenantId }),
70
70
  ...(opts.secrets && { secrets: opts.secrets }),
71
71
  });
72
72
  },
@@ -1,7 +1,6 @@
1
1
  /// <reference types="bun-types" preserve="true" />
2
2
  import { type AccountLockoutOptions, type AccountUnlockOptions, type AuthMailLocale, type AuthPaths, type EmailVerificationOptions, type InviteOptions, type PasswordResetOptions, type SignupOptions } from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
3
3
  import { type SeedAdminOptions } from "@cosmicdrift/kumiko-bundled-features/auth-email-password/seeding";
4
- import { type UnsubscribeLinkConfig } from "@cosmicdrift/kumiko-bundled-features/delivery";
5
4
  import { type ExtraRouteDefinition, type KumikoServeEnv, type PostAuthLandingResolver, type SseBroker } from "@cosmicdrift/kumiko-framework/api";
6
5
  import { type KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
7
6
  import { type EffectiveFeaturesResolver, type FeatureDefinition, type ValidateBootOptions } from "@cosmicdrift/kumiko-framework/engine";
@@ -14,9 +13,11 @@ import type { MasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
14
13
  import type { PageHeadMeta, PageHeadResolver, PageHeadSystemQuery } from "@cosmicdrift/kumiko-headless/apex";
15
14
  import { Redis } from "ioredis";
16
15
  import { type SystemWireDeps } from "./extra-routes-deps.js";
16
+ import { type UnsubscribeOption } from "./resolve-unsubscribe-wiring.js";
17
17
  import { type SecurityHeadersOption } from "./security-headers.js";
18
18
  import { type SeedCreateFileRef } from "./seed-dispatcher.js";
19
19
  export { buildBunServeOptions } from "./bun-serve-options.js";
20
+ export { resolveUnsubscribeWiring, type UnsubscribeOption, } from "./resolve-unsubscribe-wiring.js";
20
21
  export { addConfigAccessorFactory, buildBootExtraContext, loggerFromExtraContext, resolveAuthMail, } from "./run-prod-app-boot-context.js";
21
22
  export { staticCachePolicy } from "./run-prod-app-static-files.js";
22
23
  export declare function requireEnv(name: string, src?: Record<string, string | undefined>, context?: string): string;
@@ -409,10 +410,7 @@ export type RunProdAppOptions = {
409
410
  * a throw or a non-https value fall back to the app origin. The link carries the mail's
410
411
  * locale, so the confirmation pages speak the mail's language. An app reacts to an address
411
412
  * opt-out with an `r.job` triggered on `DeliveryHandlers.unsubscribeAddress`. */
412
- readonly unsubscribe?: {
413
- readonly secret: string;
414
- readonly tenantOrigin?: UnsubscribeLinkConfig["tenantOrigin"];
415
- };
413
+ readonly unsubscribe?: UnsubscribeOption;
416
414
  /** Hook for app-wired co-running components that need the system-write
417
415
  * dispatcher (e.g. wiring a webhook plugin's system-scoped secrets, or
418
416
  * starting a co-running supervisor) — runs after buildServer, before
@@ -43,13 +43,12 @@ import { AuthErrors, AuthHandlers, AuthQueries, } from "@cosmicdrift/kumiko-bund
43
43
  import { seedAdminGuarded, } from "@cosmicdrift/kumiko-bundled-features/auth-email-password/seeding";
44
44
  import { EXT_SESSION_STORE, EXT_TOKEN_VERIFIER, resolveAnonymousAccessFromRegistry, resolveTokenVerifier, } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
45
45
  import { AUTH_MFA_FEATURE, AuthMfaHandlers } from "@cosmicdrift/kumiko-bundled-features/auth-mfa";
46
- import { createUnsubscribeRoutes, } from "@cosmicdrift/kumiko-bundled-features/delivery";
47
46
  import { PAT_FEATURE, patRateLimitFromFeature, } from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens";
48
47
  import { SECRETS_MASTER_KEK_ENV_KEYS } from "@cosmicdrift/kumiko-bundled-features/secrets";
49
48
  import { SESSIONS_FEATURE } from "@cosmicdrift/kumiko-bundled-features/sessions";
50
49
  import { TenantQueries } from "@cosmicdrift/kumiko-bundled-features/tenant";
51
50
  import { UserQueries } from "@cosmicdrift/kumiko-bundled-features/user";
52
- import { createDefaultSseBroker, createRedisLoginRateLimiter, loadJwtSecretOrKeyring, parseTrustedProxyHopsEnv, publicOriginOf, TRUSTED_PROXY_HOPS_ENV, withFileProviderResolver, } from "@cosmicdrift/kumiko-framework/api";
51
+ import { createDefaultSseBroker, createRedisLoginRateLimiter, loadJwtSecretOrKeyring, parseTrustedProxyHopsEnv, TRUSTED_PROXY_HOPS_ENV, withFileProviderResolver, } from "@cosmicdrift/kumiko-framework/api";
53
52
  import { configureBlindIndexKey, configurePiiSubjectKms, resolvePlatformKeks, } from "@cosmicdrift/kumiko-framework/crypto";
54
53
  import { configureEntityFieldEncryption, createDbConnection, dbConnectionOptionsFromEnv, outsideTransactionPoolOptionsFromEnv, } from "@cosmicdrift/kumiko-framework/db";
55
54
  import { collectWriteHandlerQns, createRegistry, findTierResolverUsage, requireTierResolverPlugin, validateAppCustomScreenWriteQns, validateBoot, } from "@cosmicdrift/kumiko-framework/engine";
@@ -75,7 +74,8 @@ import { buildBunServeOptions, resolveDerivedMaxRequestBodySize } from "./bun-se
75
74
  import { buildComposeAuthOptions, composeFeatures } from "./compose-features.js";
76
75
  import { makeDispatchSystemWrite } from "./extra-routes-deps.js";
77
76
  import { assertPiiBootInvariants } from "./pii-boot-gate.js";
78
- import { APP_BASE_URL_ENV, resolveAuthPublicOrigin } from "./resolve-auth-public-origin.js";
77
+ import { resolveAuthPublicOrigin } from "./resolve-auth-public-origin.js";
78
+ import { resolveUnsubscribeWiring } from "./resolve-unsubscribe-wiring.js";
79
79
  import { addConfigAccessorFactory, buildBootExtraContext, buildProdSessionAuth, loggerFromExtraContext, resolveAuthMail, wireProdPatAutoRevoke, } from "./run-prod-app-boot-context.js";
80
80
  import { buildStaticFallback } from "./run-prod-app-static-files.js";
81
81
  import { withSecurityHeaders } from "./security-headers.js";
@@ -84,6 +84,7 @@ import { assertSessionBootInvariants } from "./session-boot-gate.js";
84
84
  import { shouldWireProdSessions } from "./session-wiring.js";
85
85
  import { stripNoRouteMatchHeader } from "./try-hono-first.js";
86
86
  export { buildBunServeOptions } from "./bun-serve-options.js";
87
+ export { resolveUnsubscribeWiring, } from "./resolve-unsubscribe-wiring.js";
87
88
  export { addConfigAccessorFactory, buildBootExtraContext, loggerFromExtraContext, resolveAuthMail, } from "./run-prod-app-boot-context.js";
88
89
  export { staticCachePolicy } from "./run-prod-app-static-files.js";
89
90
  // Strict env-var read. Throws with a clear hint when missing — better
@@ -291,27 +292,13 @@ export async function runProdApp(options) {
291
292
  console.warn(message);
292
293
  })
293
294
  : undefined;
294
- const unsubscribeBaseUrl = options.unsubscribe
295
- ? publicOriginOf(effectiveAuth?.publicOrigin ??
296
- effectiveAuth?.mail?.baseUrl ??
297
- envSource[APP_BASE_URL_ENV] ??
298
- "")
299
- : undefined;
300
- if (options.unsubscribe && unsubscribeBaseUrl === undefined) {
301
- createFallbackLogger("runProdApp").warn(`unsubscribe is enabled but no valid public app URL is known (auth.publicOrigin, auth.mail.baseUrl or ${APP_BASE_URL_ENV}), so mails get no unsubscribe link.`);
302
- }
303
- const unsubscribeLinks = options.unsubscribe && unsubscribeBaseUrl !== undefined
304
- ? {
305
- secret: options.unsubscribe.secret,
306
- baseUrl: unsubscribeBaseUrl,
307
- ...(options.unsubscribe.tenantOrigin !== undefined && {
308
- tenantOrigin: options.unsubscribe.tenantOrigin,
309
- }),
310
- }
311
- : undefined;
312
- const unsubscribeRoutes = options.unsubscribe
313
- ? createUnsubscribeRoutes({ secret: options.unsubscribe.secret })
314
- : [];
295
+ const { unsubscribeLinks, unsubscribeRoutes } = resolveUnsubscribeWiring({
296
+ unsubscribe: options.unsubscribe,
297
+ publicOrigin: effectiveAuth?.publicOrigin,
298
+ mailBaseUrl: effectiveAuth?.mail?.baseUrl,
299
+ envSource,
300
+ warn: (message) => createFallbackLogger("runProdApp").warn(message),
301
+ });
315
302
  // 3. Feature registry. Auth-mode auto-mixes config/user/tenant/auth-email-
316
303
  // password via composeFeatures — same source-of-truth as runDevApp
317
304
  // AND the per-app drizzle-Schema-Generator, so Migration und Runtime
@@ -370,8 +357,14 @@ export async function runProdApp(options) {
370
357
  throw new Error(`[runProdApp] BOOT ABORTED — KMS health check failed (latency ${kmsHealth.latencyMs}ms)`);
371
358
  }
372
359
  }
373
- const { db, close: closeDb } = createDbConnection(databaseUrl, dbConnectionOptionsFromEnv(envSource));
374
- const { db: outsideTransactionDb, close: closeOutsideTransactionDb } = createDbConnection(databaseUrl, outsideTransactionPoolOptionsFromEnv(envSource));
360
+ const { db, close: closeDb } = createDbConnection({
361
+ url: databaseUrl,
362
+ ...dbConnectionOptionsFromEnv(envSource),
363
+ });
364
+ const { db: outsideTransactionDb, close: closeOutsideTransactionDb } = createDbConnection({
365
+ url: databaseUrl,
366
+ ...outsideTransactionPoolOptionsFromEnv(envSource),
367
+ });
375
368
  const redis = new Redis(redisUrl, { maxRetriesPerRequest: null });
376
369
  await waitForDatabase(db, { processName: "runProdApp" });
377
370
  // Sprint-8a Tier-Composition auto-wire: scan features for a
@@ -6,7 +6,7 @@ export function makeSeedCreateFileRef(deps) {
6
6
  const { db, registry, context } = deps;
7
7
  const fileResolver = context._fileProviderResolver;
8
8
  return (tenantId, input) => createFileRefFromBytes({
9
- db: createTenantDb(db, tenantId),
9
+ db: createTenantDb({ db, tenantId }),
10
10
  files: fileResolver
11
11
  ? createFileContext(() => fileResolver(tenantId), tenantId)
12
12
  : context.files,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-server-runtime",
3
- "version": "0.367.0",
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.",
3
+ "version": "0.369.0",
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
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
7
7
  "repository": {
@@ -84,16 +84,19 @@
84
84
  }
85
85
  },
86
86
  "dependencies": {
87
- "@cosmicdrift/kumiko-bundled-features": "0.367.0",
88
- "@cosmicdrift/kumiko-framework": "0.367.0",
89
- "@cosmicdrift/kumiko-headless": "0.367.0",
90
- "@cosmicdrift/kumiko-renderer-mail-html": "0.367.0",
91
- "@cosmicdrift/kumiko-renderer-web": "0.367.0",
92
- "@cosmicdrift/kumiko-types": "0.367.0",
87
+ "@cosmicdrift/kumiko-bundled-features": "0.369.0",
88
+ "@cosmicdrift/kumiko-framework": "0.369.0",
89
+ "@cosmicdrift/kumiko-headless": "0.369.0",
90
+ "@cosmicdrift/kumiko-renderer-mail-html": "0.369.0",
91
+ "@cosmicdrift/kumiko-renderer-web": "0.369.0",
92
+ "@cosmicdrift/kumiko-types": "0.369.0",
93
93
  "bun-types": "^1.3.13",
94
94
  "ioredis": "^5.10.1",
95
95
  "temporal-polyfill": "^0.3.2"
96
96
  },
97
+ "devDependencies": {
98
+ "@cosmicdrift/kumiko-testing": "0.369.0"
99
+ },
97
100
  "publishConfig": {
98
101
  "registry": "https://registry.npmjs.org",
99
102
  "access": "public",