@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.
- package/dist/extra-routes-deps.d.ts +3 -0
- package/dist/run-prod-app-boot-context.d.ts +3 -0
- package/dist/run-prod-app-boot-context.js +2 -0
- package/dist/run-prod-app.d.ts +22 -1
- package/dist/run-prod-app.js +44 -7
- package/dist/run-worker-app.d.ts +1 -1
- package/dist/run-worker-app.js +2 -0
- package/dist/seed-dispatcher.d.ts +11 -0
- package/dist/seed-dispatcher.js +36 -0
- package/package.json +7 -7
|
@@ -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)
|
package/dist/run-prod-app.d.ts
CHANGED
|
@@ -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
|
|
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
|
package/dist/run-prod-app.js
CHANGED
|
@@ -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
|
|
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
|
|
705
|
-
|
|
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 +
|
package/dist/run-worker-app.d.ts
CHANGED
|
@@ -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
|
|
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";
|
package/dist/run-worker-app.js
CHANGED
|
@@ -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.
|
|
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.
|
|
88
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
89
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
90
|
-
"@cosmicdrift/kumiko-renderer-mail-html": "0.
|
|
91
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
92
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
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"
|