@cosmicdrift/kumiko-server-runtime 0.365.1 → 0.367.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.
@@ -1,5 +1,6 @@
1
1
  import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
2
2
  import type { Registry, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
3
+ import type { MetricsHandle } from "@cosmicdrift/kumiko-framework/observability";
3
4
  import type { Redis } from "ioredis";
4
5
  export type { SystemDispatchArgs } from "@cosmicdrift/kumiko-framework/api";
5
6
  export { makeDispatchSystemQuery, makeDispatchSystemWrite, } from "@cosmicdrift/kumiko-framework/api";
@@ -15,6 +16,8 @@ export type SystemWireDeps = {
15
16
  /** Feature-registry — e.g. for plugin-lookups via
16
17
  * `registry.getExtensionUsages("subscriptionProvider")`. */
17
18
  readonly registry: Registry;
19
+ /** Same contract as `ctx.metricsFor`: unregistered metric names no-op. */
20
+ readonly metricsFor: (featureName: string) => MetricsHandle;
18
21
  /** Writes through the /api/*-command-dispatcher (same idempotency/job
19
22
  * hooks) as an auto-constructed SystemAdmin of the target tenant,
20
23
  * WITHOUT the route's own access-check. Privilege-scope: SystemAdmin is
@@ -1,5 +1,6 @@
1
1
  import { type EmailVerificationOptions, type PasswordResetOptions } from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
2
2
  import { resolveSessionStore } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
3
+ import { type UnsubscribeLinkConfig } from "@cosmicdrift/kumiko-bundled-features/delivery";
3
4
  import type { SseBroker } from "@cosmicdrift/kumiko-framework/api";
4
5
  import type { KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
5
6
  import { type DbConnection } from "@cosmicdrift/kumiko-framework/db";
@@ -30,6 +31,8 @@ export declare function buildBootExtraContext(opts: {
30
31
  readonly deliverQueuedInline?: boolean;
31
32
  /** Receives redacted delivery failures; without it they go to the console. */
32
33
  readonly log?: Logger;
34
+ /** Signs `unsubscribe: true` notifications' mail links; see runProdApp `unsubscribe`. */
35
+ readonly unsubscribeLinks?: UnsubscribeLinkConfig;
33
36
  }): Record<string, unknown>;
34
37
  /** Die Auth-Felder die resolveAuthMail liest/normalisiert — beide
35
38
  * App-Auth-Typen (prod + dev) erfüllen das strukturell. */
@@ -60,6 +60,7 @@ function buildDeliveryNotifyFactory(opts) {
60
60
  ...(opts.escapeHatchAuditSink && { escapeHatchAuditSink: opts.escapeHatchAuditSink }),
61
61
  ...(opts.log && { log: opts.log }),
62
62
  ...(opts.tenantUserIdsQuery && { tenantUserIdsQuery: opts.tenantUserIdsQuery }),
63
+ ...(opts.unsubscribeLinks && { unsubscribeLinks: opts.unsubscribeLinks }),
63
64
  ...(configAccessorFactory && {
64
65
  tenantConfigFor: (tenantId) => {
65
66
  const systemUser = createSystemUser(tenantId);
@@ -147,6 +148,7 @@ export function buildBootExtraContext(opts) {
147
148
  ...(hasTenantFeature && { tenantUserIdsQuery: TenantQueries.resolveUserIds }),
148
149
  configResolver: bootConfigResolver,
149
150
  deliverQueuedInline: opts.deliverQueuedInline,
151
+ unsubscribeLinks: opts.unsubscribeLinks,
150
152
  }),
151
153
  }),
152
154
  // Top-level provider so feature jobs (secrets rotate, config reencrypt)
@@ -1,6 +1,7 @@
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";
4
5
  import { type ExtraRouteDefinition, type KumikoServeEnv, type PostAuthLandingResolver, type SseBroker } from "@cosmicdrift/kumiko-framework/api";
5
6
  import { type KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
6
7
  import { type EffectiveFeaturesResolver, type FeatureDefinition, type ValidateBootOptions } from "@cosmicdrift/kumiko-framework/engine";
@@ -8,12 +9,13 @@ import { type AllInOneEntrypoint, type ApiEntrypoint } from "@cosmicdrift/kumiko
8
9
  import { type ComposedEnvSchema, KumikoBootError } from "@cosmicdrift/kumiko-framework/env";
9
10
  import type { ErrorDocsConfig } from "@cosmicdrift/kumiko-framework/errors";
10
11
  import type { JobRunnerOptions } from "@cosmicdrift/kumiko-framework/jobs";
11
- import type { ObservabilityOptions, ObservabilityProvider } from "@cosmicdrift/kumiko-framework/observability";
12
+ import { type ObservabilityOptions, type ObservabilityProvider } from "@cosmicdrift/kumiko-framework/observability";
12
13
  import type { MasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
13
14
  import type { PageHeadMeta, PageHeadResolver, PageHeadSystemQuery } from "@cosmicdrift/kumiko-headless/apex";
14
15
  import { Redis } from "ioredis";
15
16
  import { type SystemWireDeps } from "./extra-routes-deps.js";
16
17
  import { type SecurityHeadersOption } from "./security-headers.js";
18
+ import { type SeedCreateFileRef } from "./seed-dispatcher.js";
17
19
  export { buildBunServeOptions } from "./bun-serve-options.js";
18
20
  export { addConfigAccessorFactory, buildBootExtraContext, loggerFromExtraContext, resolveAuthMail, } from "./run-prod-app-boot-context.js";
19
21
  export { staticCachePolicy } from "./run-prod-app-static-files.js";
@@ -175,6 +177,9 @@ export type ProdSeedFn = (deps: {
175
177
  db: import("@cosmicdrift/kumiko-framework/db").DbConnection;
176
178
  registry: import("@cosmicdrift/kumiko-framework/engine").Registry;
177
179
  dispatcher: import("@cosmicdrift/kumiko-framework/pipeline").Dispatcher;
180
+ /** Stores bytes as a fileRef of `tenantId` (same validation and storage
181
+ * layout as POST /files) without a helper write handler. */
182
+ createFileRef: SeedCreateFileRef;
178
183
  }) => Promise<void>;
179
184
  /** Boot-Time-Deps die `extraContext` + `anonymousAccess` Factories als
180
185
  * Argument bekommen. Closure dann in der returned Config (z.B. ein
@@ -392,6 +397,22 @@ export type RunProdAppOptions = {
392
397
  * inside buildServer, right after the r.httpRoute loop, before seeds
393
398
  * (kumiko-framework#3050). */
394
399
  readonly extraRoutes?: readonly ExtraRouteDefinition[];
400
+ /** Framework-managed unsubscribe links. Mounts `createUnsubscribeRoutes({ secret })` and makes
401
+ * delivery sign `data.unsubscribeUrl` (path `DELIVERY_UNSUBSCRIBE_PATH`) for every email
402
+ * notification declared with `unsubscribe: true`, for user and `route` recipients; a link the
403
+ * caller put into `data.unsubscribeUrl` wins. `secret` must be dedicated (≥32 chars), not the
404
+ * session JWT secret. Links point at the origin of `auth.publicOrigin`, then `auth.mail.baseUrl`,
405
+ * then the `APP_BASE_URL` env; with none of them no link is added (boot warning). Address
406
+ * recipients need a blind-index key, otherwise they get no link. Do not also mount
407
+ * `createUnsubscribeRoutes` via `extraRoutes`. `tenantOrigin` points a tenant's links at
408
+ * its own subdomain or custom domain (the host must serve this app's `/api` routes); null,
409
+ * a throw or a non-https value fall back to the app origin. The link carries the mail's
410
+ * locale, so the confirmation pages speak the mail's language. An app reacts to an address
411
+ * opt-out with an `r.job` triggered on `DeliveryHandlers.unsubscribeAddress`. */
412
+ readonly unsubscribe?: {
413
+ readonly secret: string;
414
+ readonly tenantOrigin?: UnsubscribeLinkConfig["tenantOrigin"];
415
+ };
395
416
  /** Hook for app-wired co-running components that need the system-write
396
417
  * dispatcher (e.g. wiring a webhook plugin's system-scoped secrets, or
397
418
  * starting a co-running supervisor) — runs after buildServer, before
@@ -43,12 +43,13 @@ 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";
46
47
  import { PAT_FEATURE, patRateLimitFromFeature, } from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens";
47
48
  import { SECRETS_MASTER_KEK_ENV_KEYS } from "@cosmicdrift/kumiko-bundled-features/secrets";
48
49
  import { SESSIONS_FEATURE } from "@cosmicdrift/kumiko-bundled-features/sessions";
49
50
  import { TenantQueries } from "@cosmicdrift/kumiko-bundled-features/tenant";
50
51
  import { UserQueries } from "@cosmicdrift/kumiko-bundled-features/user";
51
- import { createDefaultSseBroker, createRedisLoginRateLimiter, loadJwtSecretOrKeyring, parseTrustedProxyHopsEnv, TRUSTED_PROXY_HOPS_ENV, withFileProviderResolver, } from "@cosmicdrift/kumiko-framework/api";
52
+ import { createDefaultSseBroker, createRedisLoginRateLimiter, loadJwtSecretOrKeyring, parseTrustedProxyHopsEnv, publicOriginOf, TRUSTED_PROXY_HOPS_ENV, withFileProviderResolver, } from "@cosmicdrift/kumiko-framework/api";
52
53
  import { configureBlindIndexKey, configurePiiSubjectKms, resolvePlatformKeks, } from "@cosmicdrift/kumiko-framework/crypto";
53
54
  import { configureEntityFieldEncryption, createDbConnection, dbConnectionOptionsFromEnv, outsideTransactionPoolOptionsFromEnv, } from "@cosmicdrift/kumiko-framework/db";
54
55
  import { collectWriteHandlerQns, createRegistry, findTierResolverUsage, requireTierResolverPlugin, validateAppCustomScreenWriteQns, validateBoot, } from "@cosmicdrift/kumiko-framework/engine";
@@ -57,7 +58,9 @@ import { KumikoBootError, kmsSlotsOf, parseEnv, withOptionalEnvKeys, } from "@co
57
58
  import { renderDryRun } from "@cosmicdrift/kumiko-framework/env/dry-run";
58
59
  import { createEsOperationsTable, createSeedMigrationContext, runPendingSeedMigrations, } from "@cosmicdrift/kumiko-framework/es-ops";
59
60
  import { assertSeedModeNotInProduction } from "@cosmicdrift/kumiko-framework/event-store";
61
+ import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
60
62
  import { assertKumikoSchemaCurrent, SchemaDriftError, } from "@cosmicdrift/kumiko-framework/migrations";
63
+ import { createMetricsForFactory, } from "@cosmicdrift/kumiko-framework/observability";
61
64
  import { createDispatcher, createEntityCache, createEventDedup, createIdempotencyGuard, flushEscapeHatchAudits, } from "@cosmicdrift/kumiko-framework/pipeline";
62
65
  import { createRedisCacheSyncBus, redisClientOptionsFromEnv, } from "@cosmicdrift/kumiko-framework/redis";
63
66
  import { warnIfNonUtcServerTimeZone } from "@cosmicdrift/kumiko-framework/time";
@@ -72,10 +75,11 @@ import { buildBunServeOptions, resolveDerivedMaxRequestBodySize } from "./bun-se
72
75
  import { buildComposeAuthOptions, composeFeatures } from "./compose-features.js";
73
76
  import { makeDispatchSystemWrite } from "./extra-routes-deps.js";
74
77
  import { assertPiiBootInvariants } from "./pii-boot-gate.js";
75
- import { resolveAuthPublicOrigin } from "./resolve-auth-public-origin.js";
78
+ import { APP_BASE_URL_ENV, resolveAuthPublicOrigin } from "./resolve-auth-public-origin.js";
76
79
  import { addConfigAccessorFactory, buildBootExtraContext, buildProdSessionAuth, loggerFromExtraContext, resolveAuthMail, wireProdPatAutoRevoke, } from "./run-prod-app-boot-context.js";
77
80
  import { buildStaticFallback } from "./run-prod-app-static-files.js";
78
81
  import { withSecurityHeaders } from "./security-headers.js";
82
+ import { makeSeedCreateFileRef, withSeedErrorContext, } from "./seed-dispatcher.js";
79
83
  import { assertSessionBootInvariants } from "./session-boot-gate.js";
80
84
  import { shouldWireProdSessions } from "./session-wiring.js";
81
85
  import { stripNoRouteMatchHeader } from "./try-hono-first.js";
@@ -287,6 +291,27 @@ export async function runProdApp(options) {
287
291
  console.warn(message);
288
292
  })
289
293
  : 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
+ : [];
290
315
  // 3. Feature registry. Auth-mode auto-mixes config/user/tenant/auth-email-
291
316
  // password via composeFeatures — same source-of-truth as runDevApp
292
317
  // AND the per-app drizzle-Schema-Generator, so Migration und Runtime
@@ -451,6 +476,7 @@ export async function runProdApp(options) {
451
476
  crypto: bootCrypto,
452
477
  ...(options.kms && { kms: options.kms }),
453
478
  ...(bootLogger && { log: bootLogger }),
479
+ ...(unsubscribeLinks && { unsubscribeLinks }),
454
480
  });
455
481
  const extraContext = {
456
482
  ...addConfigAccessorFactory({ ...autoExtraContext, ...resolvedExtraContext }, registry),
@@ -533,7 +559,9 @@ export async function runProdApp(options) {
533
559
  ...(options.rateLimit && { rateLimit: options.rateLimit }),
534
560
  ...(trustedProxyHops !== undefined && { trustedProxyHops }),
535
561
  ...(options.errorDocs && { errorDocs: options.errorDocs }),
536
- ...(options.extraRoutes && { extraRoutes: options.extraRoutes }),
562
+ ...((options.extraRoutes || options.unsubscribe) && {
563
+ extraRoutes: [...(options.extraRoutes ?? []), ...unsubscribeRoutes],
564
+ }),
537
565
  ...(effectiveAuth && {
538
566
  auth: {
539
567
  membershipQuery: TenantQueries.memberships,
@@ -685,6 +713,7 @@ export async function runProdApp(options) {
685
713
  db,
686
714
  redis,
687
715
  registry,
716
+ metricsFor: createMetricsForFactory(entrypoint.observability.meter),
688
717
  dispatchSystemWrite: makeDispatchSystemWrite(entrypoint.dispatcher),
689
718
  });
690
719
  }
@@ -701,12 +730,20 @@ export async function runProdApp(options) {
701
730
  db,
702
731
  ...(bootCrypto.configCipher && { cipher: bootCrypto.configCipher }),
703
732
  });
704
- const seedDispatcher = (options.seeds?.length ?? 0) > 0 || options.seedsDir !== undefined
705
- ? createDispatcher(registry, withFileProviderResolver(registry, { db, redis, entityCache, registry, ...extraContext }, cacheSync))
733
+ const hasSeeds = (options.seeds?.length ?? 0) > 0 || options.seedsDir !== undefined;
734
+ const seedContext = hasSeeds
735
+ ? withFileProviderResolver(registry, { db, redis, entityCache, registry, ...extraContext }, cacheSync)
736
+ : undefined;
737
+ const seedDispatcher = seedContext
738
+ ? withSeedErrorContext(createDispatcher(registry, seedContext, {
739
+ ...baseEntrypointOptions.dispatcherOptions,
740
+ ...(entrypoint.mode === "all-in-one" && { jobRunner: entrypoint.jobRunner }),
741
+ }))
706
742
  : undefined;
707
- if (seedDispatcher) {
743
+ if (seedDispatcher && seedContext) {
744
+ const createFileRef = makeSeedCreateFileRef({ db, registry, context: seedContext });
708
745
  for (const seed of options.seeds ?? []) {
709
- await seed({ db, registry, dispatcher: seedDispatcher });
746
+ await seed({ db, registry, dispatcher: seedDispatcher, createFileRef });
710
747
  }
711
748
  }
712
749
  // ES-Operations / Seed-Migrations (Phase 1). Läuft NACH applyBootSeeds +
@@ -4,7 +4,7 @@ import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
4
4
  import type { EffectiveFeaturesResolver, FeatureDefinition, Registry, ValidateBootOptions } from "@cosmicdrift/kumiko-framework/engine";
5
5
  import type { WorkerEntrypoint } from "@cosmicdrift/kumiko-framework/entrypoint";
6
6
  import type { JobRunnerOptions } from "@cosmicdrift/kumiko-framework/jobs";
7
- import type { ObservabilityOptions, ObservabilityProvider } from "@cosmicdrift/kumiko-framework/observability";
7
+ import { type ObservabilityOptions, type ObservabilityProvider } from "@cosmicdrift/kumiko-framework/observability";
8
8
  import type { MasterKeyProvider, SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
9
9
  import type { Redis } from "ioredis";
10
10
  import { type WorkerMetricsOptions, type WorkerMetricsServer } from "./boot/worker-metrics-server.js";
@@ -15,6 +15,7 @@
15
15
  // Container/Coolify sets the same env vars as runProdApp:
16
16
  // DATABASE_URL, REDIS_URL, JWT_SECRET — PORT is not needed.
17
17
  import { assertSeedModeNotInProduction } from "@cosmicdrift/kumiko-framework/event-store";
18
+ import { createMetricsForFactory, } from "@cosmicdrift/kumiko-framework/observability";
18
19
  import { startPiiEventBackfillOnBoot } from "./boot/pii-event-backfill-on-boot.js";
19
20
  import { recordAppStartedOnBoot } from "./boot/record-app-started-on-boot.js";
20
21
  import { bootWorkerProcess, resolveWorkerEnvSource } from "./boot/worker-boot-core.js";
@@ -70,6 +71,7 @@ export async function runWorkerApp(options) {
70
71
  db,
71
72
  redis,
72
73
  registry,
74
+ metricsFor: createMetricsForFactory(entrypoint.observability.meter),
73
75
  dispatchSystemWrite: makeDispatchSystemWrite(entrypoint.dispatcher),
74
76
  lifecycle: entrypoint.lifecycle,
75
77
  ...(boot.secrets && { secrets: boot.secrets }),
@@ -0,0 +1,11 @@
1
+ import { type DbConnection } from "@cosmicdrift/kumiko-framework/db";
2
+ import { type AppContext, type Registry, type TenantId, type WriteResult } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { type CreatedFileRef, type CreateFileRefInput } from "@cosmicdrift/kumiko-framework/files";
4
+ import type { Dispatcher } from "@cosmicdrift/kumiko-framework/pipeline";
5
+ export type SeedCreateFileRef = (tenantId: TenantId, input: CreateFileRefInput) => Promise<WriteResult<CreatedFileRef>>;
6
+ export declare function makeSeedCreateFileRef(deps: {
7
+ readonly db: DbConnection;
8
+ readonly registry: Registry;
9
+ readonly context: AppContext;
10
+ }): SeedCreateFileRef;
11
+ export declare function withSeedErrorContext<D extends Pick<Dispatcher, "write">>(dispatcher: D): D;
@@ -0,0 +1,36 @@
1
+ import { createTenantDb } from "@cosmicdrift/kumiko-framework/db";
2
+ import { createSystemUser, } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { reraiseAsKumikoError } from "@cosmicdrift/kumiko-framework/errors";
4
+ import { createFileContext, createFileRefFromBytes, } from "@cosmicdrift/kumiko-framework/files";
5
+ export function makeSeedCreateFileRef(deps) {
6
+ const { db, registry, context } = deps;
7
+ const fileResolver = context._fileProviderResolver;
8
+ return (tenantId, input) => createFileRefFromBytes({
9
+ db: createTenantDb(db, tenantId),
10
+ files: fileResolver
11
+ ? createFileContext(() => fileResolver(tenantId), tenantId)
12
+ : context.files,
13
+ registry,
14
+ user: createSystemUser(tenantId),
15
+ }, input);
16
+ }
17
+ // The dispatcher masks a thrown handler error as "internal error" and keeps the
18
+ // cause only on a side channel; a seed has no HTTP client to protect, so the
19
+ // handler name and cause message go into the message the seed caller sees.
20
+ export function withSeedErrorContext(dispatcher) {
21
+ return {
22
+ ...dispatcher,
23
+ async write(type, payload, user, requestId) {
24
+ const result = await dispatcher.write(type, payload, user, requestId);
25
+ if (result.isSuccess || result.error.code !== "internal_error")
26
+ return result;
27
+ const cause = reraiseAsKumikoError(result.error).cause;
28
+ const causeMessage = cause instanceof Error ? cause.message : undefined;
29
+ const detail = causeMessage ? `${type}: ${causeMessage}` : type;
30
+ return {
31
+ isSuccess: false,
32
+ error: { ...result.error, message: `${result.error.message} (${detail})` },
33
+ };
34
+ },
35
+ };
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-server-runtime",
3
- "version": "0.365.1",
3
+ "version": "0.367.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/codegen tooling.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -84,12 +84,12 @@
84
84
  }
85
85
  },
86
86
  "dependencies": {
87
- "@cosmicdrift/kumiko-bundled-features": "0.365.1",
88
- "@cosmicdrift/kumiko-framework": "0.365.1",
89
- "@cosmicdrift/kumiko-headless": "0.365.1",
90
- "@cosmicdrift/kumiko-renderer-mail-html": "0.365.1",
91
- "@cosmicdrift/kumiko-renderer-web": "0.365.1",
92
- "@cosmicdrift/kumiko-types": "0.365.1",
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",
93
93
  "bun-types": "^1.3.13",
94
94
  "ioredis": "^5.10.1",
95
95
  "temporal-polyfill": "^0.3.2"