@cosmicdrift/kumiko-server-runtime 0.388.0 → 0.390.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/boot/apply-boot-seeds.js +11 -8
- package/dist/boot/job-run-logger.js +1 -1
- package/dist/boot/shared-boot-steps.d.ts +8 -5
- package/dist/boot/shared-boot-steps.js +26 -16
- package/dist/boot/stream-tenant-backfill-on-boot.d.ts +27 -0
- package/dist/boot/stream-tenant-backfill-on-boot.js +161 -0
- package/dist/boot/wait-for-database.d.ts +2 -0
- package/dist/boot/wait-for-database.js +4 -3
- package/dist/boot/warn-row-level-security-off.d.ts +3 -0
- package/dist/boot/warn-row-level-security-off.js +6 -0
- package/dist/boot/worker-boot-core.d.ts +2 -1
- package/dist/boot/worker-boot-core.js +28 -5
- package/dist/boot/worker-metrics-server.js +2 -0
- package/dist/boot-options/demo-mode.d.ts +3 -3
- package/dist/boot-options/demo-mode.js +4 -1
- package/dist/boot-options/resolve-boot-options.d.ts +1 -0
- package/dist/boot-options/resolve-boot-options.js +2 -2
- package/dist/boot-options/seed-shortcuts.js +3 -1
- package/dist/boot-options/stack-boot-requirements.d.ts +1 -1
- package/dist/boot-options/stack-boot-requirements.js +1 -13
- package/dist/build-prod-bundle.js +13 -13
- package/dist/bundled-lists-table.d.ts +2 -0
- package/dist/bundled-lists-table.js +56 -0
- package/dist/compose-auth-options.d.ts +16 -0
- package/dist/compose-auth-options.js +49 -0
- package/dist/compose-features.d.ts +5 -17
- package/dist/compose-features.js +13 -53
- package/dist/extra-routes-deps.d.ts +5 -1
- package/dist/extra-routes-deps.js +1 -0
- package/dist/files-for-tenant.d.ts +3 -0
- package/dist/files-for-tenant.js +10 -0
- package/dist/renderer-web-fonts.js +3 -9
- package/dist/resolve-installed-package.d.ts +10 -0
- package/dist/resolve-installed-package.js +32 -0
- package/dist/resolve-tailwind-cli.d.ts +3 -8
- package/dist/resolve-tailwind-cli.js +8 -25
- package/dist/run-prod-app-boot-context.d.ts +2 -0
- package/dist/run-prod-app-boot-context.js +11 -9
- package/dist/run-prod-app-config.d.ts +2 -0
- package/dist/run-prod-app-config.js +3 -1
- package/dist/run-prod-app-connections.d.ts +2 -1
- package/dist/run-prod-app-connections.js +11 -7
- package/dist/run-prod-app-entrypoint.d.ts +1 -1
- package/dist/run-prod-app-entrypoint.js +6 -2
- package/dist/run-prod-app-handle.js +4 -1
- package/dist/run-prod-app-request-context.js +9 -2
- package/dist/run-prod-app-seeds.js +39 -24
- package/dist/run-prod-app-static-files.js +9 -1
- package/dist/run-prod-app.d.ts +19 -4
- package/dist/run-prod-app.js +7 -3
- package/dist/run-worker-app.d.ts +22 -4
- package/dist/run-worker-app.js +22 -4
- package/dist/seed-dispatcher.js +3 -1
- package/package.json +19 -8
- package/src/changes.json +92 -0
|
@@ -2,6 +2,7 @@ import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
|
2
2
|
import type { Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
3
3
|
import { createRedisCacheSyncBus } from "@cosmicdrift/kumiko-framework/redis";
|
|
4
4
|
import { Redis } from "ioredis";
|
|
5
|
+
import { type DatabaseWaitClock } from "./boot/wait-for-database.ts";
|
|
5
6
|
import type { RunProdAppOptions } from "./run-prod-app.ts";
|
|
6
7
|
import type { ProdBootConfig, RegisterBootAbortCleanup } from "./run-prod-app-config.ts";
|
|
7
8
|
export type ProdConnections = {
|
|
@@ -12,5 +13,5 @@ export type ProdConnections = {
|
|
|
12
13
|
readonly redis: Redis;
|
|
13
14
|
readonly cacheSync: ReturnType<typeof createRedisCacheSyncBus>;
|
|
14
15
|
};
|
|
15
|
-
export declare function connectProdDependencies(config: ProdBootConfig, log: Logger, registerAbortCleanup: RegisterBootAbortCleanup): Promise<ProdConnections>;
|
|
16
|
+
export declare function connectProdDependencies(config: ProdBootConfig, log: Logger, registerAbortCleanup: RegisterBootAbortCleanup, databaseWait?: DatabaseWaitClock): Promise<ProdConnections>;
|
|
16
17
|
export declare function assertProdSchemaCurrent(db: DbConnection, migrations: RunProdAppOptions["migrations"], log: Logger): Promise<void>;
|
|
@@ -2,27 +2,31 @@ import { assertKumikoSchemaCurrent, SchemaDriftError, } from "@cosmicdrift/kumik
|
|
|
2
2
|
import { createRedisCacheSyncBus, logRedisConnectionState, redisClientOptionsFromEnv, } from "@cosmicdrift/kumiko-framework/redis";
|
|
3
3
|
import { Redis } from "ioredis";
|
|
4
4
|
import { openDatabasePools, waitForKmsHealthy } from "./boot/shared-boot-steps.js";
|
|
5
|
-
import { WAIT_FOR_DATABASE_DEADLINE_MS, waitForDatabase } from "./boot/wait-for-database.js";
|
|
5
|
+
import { WAIT_FOR_DATABASE_DEADLINE_MS, waitForDatabase, } from "./boot/wait-for-database.js";
|
|
6
6
|
// Postgres + Redis. The Redis client is shared by idempotency, event-dedup,
|
|
7
7
|
// entity-cache, rate-limit; failing to construct here surfaces the misconfig
|
|
8
8
|
// immediately. `new Redis(...)` connects eagerly, so this must stay AFTER the
|
|
9
9
|
// boot-mode exit.
|
|
10
|
-
export async function connectProdDependencies(config, log, registerAbortCleanup) {
|
|
11
|
-
const { bootKms, databaseUrl, redisUrl, envSource } = config;
|
|
10
|
+
export async function connectProdDependencies(config, log, registerAbortCleanup, databaseWait) {
|
|
11
|
+
const { bootKms, databaseUrl, redisUrl, envSource, rowLevelSecurity } = config;
|
|
12
12
|
// KMS health gate: an app configured for crypto-shredding must not boot
|
|
13
13
|
// against an unreachable key store — a silent start would answer every
|
|
14
14
|
// PII read with 503. Runs BEFORE connections so an abort leaks nothing.
|
|
15
|
-
const databaseWaitDeadline = Date.now() + WAIT_FOR_DATABASE_DEADLINE_MS;
|
|
15
|
+
const databaseWaitDeadline = (databaseWait?.now ?? Date.now)() + WAIT_FOR_DATABASE_DEADLINE_MS;
|
|
16
16
|
if (bootKms.kms) {
|
|
17
|
-
await waitForKmsHealthy(bootKms.kms, "runProdApp", databaseWaitDeadline);
|
|
17
|
+
await waitForKmsHealthy(bootKms.kms, "runProdApp", databaseWaitDeadline, databaseWait);
|
|
18
18
|
}
|
|
19
|
-
const pools = openDatabasePools(databaseUrl, envSource);
|
|
19
|
+
const pools = openDatabasePools(databaseUrl, envSource, rowLevelSecurity);
|
|
20
20
|
const redis = new Redis(redisUrl, { maxRetriesPerRequest: null });
|
|
21
21
|
registerAbortCleanup(pools.closeDb);
|
|
22
22
|
registerAbortCleanup(pools.closeOutsideTransactionDb);
|
|
23
23
|
registerAbortCleanup(() => redis.disconnect());
|
|
24
24
|
logRedisConnectionState(redis, log);
|
|
25
|
-
await waitForDatabase(pools.db, {
|
|
25
|
+
await waitForDatabase(pools.db, {
|
|
26
|
+
...databaseWait,
|
|
27
|
+
processName: "runProdApp",
|
|
28
|
+
deadline: databaseWaitDeadline,
|
|
29
|
+
});
|
|
26
30
|
// One bus per process: the tier resolver, the dispatcher and the file-provider
|
|
27
31
|
// resolver all subscribe to it (invalidation across the API pods).
|
|
28
32
|
const cacheSync = createRedisCacheSyncBus({
|
|
@@ -10,5 +10,5 @@ export declare function buildProdEntrypointOptions(options: RunProdAppOptions, c
|
|
|
10
10
|
export type ProdEntrypointOptions = ReturnType<typeof buildProdEntrypointOptions>;
|
|
11
11
|
export declare function createProdEntrypoint(options: RunProdAppOptions, config: ProdBootConfig, composed: ProdComposedFeatures, connections: ProdConnections, entrypointOptions: ProdEntrypointOptions): ProdEntrypoint;
|
|
12
12
|
export declare function registerProdShutdownHooks(entrypoint: ProdEntrypoint, cacheSync: ProdConnections["cacheSync"], ownedRedisSseBroker: ProdRequestContext["ownedRedisSseBroker"]): void;
|
|
13
|
-
export declare function runProdWireHook(options: RunProdAppOptions, composed: ProdComposedFeatures, connections: ProdConnections, entrypoint: ProdEntrypoint): Promise<void>;
|
|
13
|
+
export declare function runProdWireHook(options: RunProdAppOptions, config: ProdBootConfig, composed: ProdComposedFeatures, connections: ProdConnections, entrypoint: ProdEntrypoint): Promise<void>;
|
|
14
14
|
export declare function startProdBackgroundWork(entrypoint: ProdEntrypoint, config: ProdBootConfig, composed: ProdComposedFeatures, connections: ProdConnections): Promise<void>;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createAllInOneEntrypoint, createApiEntrypoint, } from "@cosmicdrift/kumiko-framework/entrypoint";
|
|
2
|
+
import { appHookScopedRunner } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
2
3
|
import { createMetricsForFactory } from "@cosmicdrift/kumiko-framework/observability";
|
|
3
4
|
import { jobRunLoggerCallbacks } from "./boot/job-run-logger.js";
|
|
4
5
|
import { startPiiEventBackfillOnBoot } from "./boot/pii-event-backfill-on-boot.js";
|
|
5
6
|
import { recordAppStartedOnBoot } from "./boot/record-app-started-on-boot.js";
|
|
6
7
|
import { makeDispatchSystemQuery, makeDispatchSystemWrite } from "./extra-routes-deps.js";
|
|
8
|
+
import { makeFilesForTenant } from "./files-for-tenant.js";
|
|
7
9
|
import { buildProdAuthRoutes } from "./run-prod-app-auth.js";
|
|
8
10
|
function collectProdExtraRoutes(options, config, composed) {
|
|
9
11
|
const bootExtraRoutes = config.bootOptionWiring.extraRoutes;
|
|
@@ -26,6 +28,7 @@ export function buildProdEntrypointOptions(options, config, composed, connection
|
|
|
26
28
|
registry,
|
|
27
29
|
...requestContext.extraContext,
|
|
28
30
|
},
|
|
31
|
+
rowLevelSecurity: config.rowLevelSecurity,
|
|
29
32
|
sseBroker: requestContext.sseBroker,
|
|
30
33
|
jwtSecret: config.jwtSecretOrKeyring,
|
|
31
34
|
...(jwtIssuer && { jwtIssuer }),
|
|
@@ -118,14 +121,15 @@ export function registerProdShutdownHooks(entrypoint, cacheSync, ownedRedisSseBr
|
|
|
118
121
|
// runs. `wire` is the remaining escape-hatch for app-wired co-running
|
|
119
122
|
// components that need the system-write dispatcher but don't declare a
|
|
120
123
|
// route.
|
|
121
|
-
export async function runProdWireHook(options, composed, connections, entrypoint) {
|
|
124
|
+
export async function runProdWireHook(options, config, composed, connections, entrypoint) {
|
|
122
125
|
const { wire } = options;
|
|
123
126
|
if (wire) {
|
|
124
127
|
const { bootCrypto } = composed;
|
|
125
128
|
await wire({
|
|
126
|
-
db: connections.db,
|
|
129
|
+
db: appHookScopedRunner(connections.db, config.rowLevelSecurity),
|
|
127
130
|
redis: connections.redis,
|
|
128
131
|
registry: composed.registry,
|
|
132
|
+
filesForTenant: makeFilesForTenant(entrypoint.context),
|
|
129
133
|
...(bootCrypto.masterKeyProvider && { masterKeyProvider: bootCrypto.masterKeyProvider }),
|
|
130
134
|
...(bootCrypto.configCipher && { configEncryption: bootCrypto.configCipher }),
|
|
131
135
|
metricsFor: createMetricsForFactory(entrypoint.observability.meter),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertNoActiveRowLevelSecurityScope } from "@cosmicdrift/kumiko-framework/db";
|
|
1
2
|
import { flushEscapeHatchAudits } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
2
3
|
import { installShutdownSignals } from "./boot-options/process-signals.js";
|
|
3
4
|
import { buildBunServeOptions, resolveDerivedMaxRequestBodySize } from "./bun-serve-options.js";
|
|
@@ -45,7 +46,8 @@ export function createProdAppHandle(input) {
|
|
|
45
46
|
const { options, config, composed, connections, entrypoint, log } = input;
|
|
46
47
|
const { fetchHandler, cspScriptHashes } = input.fetch;
|
|
47
48
|
// Upgrade rejections (401/403/426...) are ordinary responses and get the same headers as fetchHandler.
|
|
48
|
-
|
|
49
|
+
// Same read-only demo gate as fetchHandler: upgrade requests are GETs, but the socket then writes.
|
|
50
|
+
const securedUpgradeFetch = withSecurityHeaders(config.bootOptionWiring.wrapFetch(async (req, env) => stripNoRouteMatchHeader(await entrypoint.app.fetch(req, env))), options.securityHeaders, cspScriptHashes);
|
|
49
51
|
const webSocketUpgradeFetch = async (req, env) => securedUpgradeFetch(req, env);
|
|
50
52
|
let uninstallShutdownSignals;
|
|
51
53
|
let uninstallFatalHandlers;
|
|
@@ -64,6 +66,7 @@ export function createProdAppHandle(input) {
|
|
|
64
66
|
throw new Error("[runProdApp] listen() requires Bun runtime (Bun.serve). " +
|
|
65
67
|
"Under Node/vitest pass `autoListen: false` and call the returned `fetch()` directly.");
|
|
66
68
|
}
|
|
69
|
+
assertNoActiveRowLevelSecurityScope("prod app server");
|
|
67
70
|
handle.server = Bun.serve(buildBunServeOptions(listenPort, fetchHandler, options.maxRequestBodySize ?? resolveDerivedMaxRequestBodySize(composed.registry), { upgradeFetch: webSocketUpgradeFetch }));
|
|
68
71
|
// SIGTERM/SIGINT — graceful shutdown. Only registered when we
|
|
69
72
|
// actually own a Bun-server, otherwise the test process picks up
|
|
@@ -2,6 +2,7 @@ import { resolveAnonymousAccessFromRegistry } from "@cosmicdrift/kumiko-bundled-
|
|
|
2
2
|
import { createDefaultSseBroker } from "@cosmicdrift/kumiko-framework/api";
|
|
3
3
|
import { configureBlindIndexKey, configurePiiSubjectKms, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
4
4
|
import { configureEntityFieldEncryption } from "@cosmicdrift/kumiko-framework/db";
|
|
5
|
+
import { appHookScopedRunner } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
5
6
|
import { createFallbackLogger, setProcessLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
6
7
|
import { createEntityCache, createEventDedup, createIdempotencyGuard, } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
7
8
|
import { addConfigAccessorFactory, buildBootExtraContext, loggerFromExtraContext, } from "./run-prod-app-boot-context.js";
|
|
@@ -47,7 +48,13 @@ function buildProdExtraContext(options, config, composed, connections, deps) {
|
|
|
47
48
|
...(options.defaultLocale !== undefined && { defaultLocale: options.defaultLocale }),
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
|
-
async function resolveProdAnonymousAccess(options,
|
|
51
|
+
async function resolveProdAnonymousAccess(options, requestDeps, config) {
|
|
52
|
+
// extraContext services run inside requests and keep the ambient tenant scope; only this
|
|
53
|
+
// boot-time factory and the registry resolvers get the system scope.
|
|
54
|
+
const deps = {
|
|
55
|
+
...requestDeps,
|
|
56
|
+
db: appHookScopedRunner(requestDeps.db, config.rowLevelSecurity),
|
|
57
|
+
};
|
|
51
58
|
const baseAnonymousAccess = typeof options.anonymousAccess === "function"
|
|
52
59
|
? options.anonymousAccess(deps)
|
|
53
60
|
: options.anonymousAccess;
|
|
@@ -90,7 +97,7 @@ export async function buildProdRequestContext(options, config, composed, connect
|
|
|
90
97
|
...(bootCrypto.configCipher && { configEncryption: bootCrypto.configCipher }),
|
|
91
98
|
};
|
|
92
99
|
const extraContext = buildProdExtraContext(options, config, composed, connections, deps);
|
|
93
|
-
const resolvedAnonymousAccess = await resolveProdAnonymousAccess(options, deps);
|
|
100
|
+
const resolvedAnonymousAccess = await resolveProdAnonymousAccess(options, deps, config);
|
|
94
101
|
return {
|
|
95
102
|
idempotency,
|
|
96
103
|
eventDedup,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { withFileProviderResolver } from "@cosmicdrift/kumiko-framework/api";
|
|
2
2
|
import { createEsOperationsTable, createSeedMigrationContext, runPendingSeedMigrations, } from "@cosmicdrift/kumiko-framework/es-ops";
|
|
3
|
+
import { runInSystemRowLevelSecurityScope } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
3
4
|
import { createDispatcher, createUnlimitedRateLimitResolver, } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
4
5
|
import { applyStandardBootSeeds } from "./boot/shared-boot-steps.js";
|
|
6
|
+
import { runStreamTenantBackfillOnBoot } from "./boot/stream-tenant-backfill-on-boot.js";
|
|
5
7
|
import { makeSeedCreateFileRef, withSeedErrorContext, } from "./seed-dispatcher.js";
|
|
6
8
|
// Seeds are bootstrap writes, not tenant traffic: they always get the unlimited resolver,
|
|
7
9
|
// even when the app has a real one, so a handler's rateLimit can neither fail nor drain them.
|
|
@@ -58,29 +60,42 @@ async function runSeedMigrations(run, seedsDir, seedDispatcher) {
|
|
|
58
60
|
// preconditions. Config-seeds rely on a deterministic
|
|
59
61
|
// aggregate-id so re-boot becomes a version_conflict skip.
|
|
60
62
|
export async function runProdBootSeeds(run, entrypoint, dispatcherOptions) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
return runInSystemRowLevelSecurityScope("boot seeds write rows for arbitrary tenants", async () => {
|
|
64
|
+
const { options, config, composed, connections } = run;
|
|
65
|
+
// First: custom seeds update tenants by version and would crash against unmigrated streams.
|
|
66
|
+
await runStreamTenantBackfillOnBoot({
|
|
67
|
+
db: connections.db,
|
|
68
|
+
registry: composed.registry,
|
|
69
|
+
extraContext: run.requestContext.extraContext,
|
|
70
|
+
processName: "runProdApp",
|
|
71
|
+
...(options.search && { search: options.search }),
|
|
72
|
+
...(options.databaseWait && { databaseWait: options.databaseWait }),
|
|
73
|
+
});
|
|
74
|
+
await applyStandardBootSeeds({
|
|
75
|
+
db: connections.db,
|
|
76
|
+
registry: composed.registry,
|
|
77
|
+
admin: composed.effectiveAuth?.admin,
|
|
78
|
+
shortcutSeeds: config.bootOptionWiring.shortcutSeeds,
|
|
79
|
+
cipher: composed.bootCrypto.configCipher,
|
|
80
|
+
processName: "runProdApp",
|
|
81
|
+
...(options.databaseWait && { databaseWait: options.databaseWait }),
|
|
82
|
+
});
|
|
83
|
+
const hasSeeds = config.bootOptionWiring.customSeeds.length > 0 || options.seedsDir !== undefined;
|
|
84
|
+
const seedContext = hasSeeds ? buildSeedContext(run) : undefined;
|
|
85
|
+
const seedDispatcher = seedContext
|
|
86
|
+
? withSeedErrorContext(createDispatcher(composed.registry, seedContext, {
|
|
87
|
+
...dispatcherOptions,
|
|
88
|
+
lifecycle: entrypoint.lifecycleHooks,
|
|
89
|
+
...(entrypoint.mode === "all-in-one" && { jobRunner: entrypoint.jobRunner }),
|
|
90
|
+
}))
|
|
91
|
+
: undefined;
|
|
92
|
+
if (seedDispatcher && seedContext) {
|
|
93
|
+
await runCustomSeeds(run, seedContext, seedDispatcher);
|
|
94
|
+
}
|
|
95
|
+
if (seedDispatcher &&
|
|
96
|
+
options.seedsDir !== undefined &&
|
|
97
|
+
config.envSource["KUMIKO_SKIP_ES_OPS"] !== "1") {
|
|
98
|
+
await runSeedMigrations(run, options.seedsDir, seedDispatcher);
|
|
99
|
+
}
|
|
68
100
|
});
|
|
69
|
-
const hasSeeds = config.bootOptionWiring.customSeeds.length > 0 || options.seedsDir !== undefined;
|
|
70
|
-
const seedContext = hasSeeds ? buildSeedContext(run) : undefined;
|
|
71
|
-
const seedDispatcher = seedContext
|
|
72
|
-
? withSeedErrorContext(createDispatcher(composed.registry, seedContext, {
|
|
73
|
-
...dispatcherOptions,
|
|
74
|
-
lifecycle: entrypoint.lifecycleHooks,
|
|
75
|
-
...(entrypoint.mode === "all-in-one" && { jobRunner: entrypoint.jobRunner }),
|
|
76
|
-
}))
|
|
77
|
-
: undefined;
|
|
78
|
-
if (seedDispatcher && seedContext) {
|
|
79
|
-
await runCustomSeeds(run, seedContext, seedDispatcher);
|
|
80
|
-
}
|
|
81
|
-
if (seedDispatcher &&
|
|
82
|
-
options.seedsDir !== undefined &&
|
|
83
|
-
config.envSource["KUMIKO_SKIP_ES_OPS"] !== "1") {
|
|
84
|
-
await runSeedMigrations(run, options.seedsDir, seedDispatcher);
|
|
85
|
-
}
|
|
86
101
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { join, resolve, sep } from "node:path";
|
|
2
3
|
import { cachedResponse, computeStrongEtag, computeWeakEtag, createClientIpResolver, } from "@cosmicdrift/kumiko-framework/api";
|
|
3
4
|
import { rankPreferredLocales } from "@cosmicdrift/kumiko-framework/i18n";
|
|
4
5
|
import { resolveAndInjectPageHead } from "@cosmicdrift/kumiko-renderer/headless/apex";
|
|
5
6
|
import { errorCodeOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
6
|
-
import { ASSETS_DIR } from "./build-prod-bundle.js";
|
|
7
|
+
import { ASSETS_DIR, collectBuildScriptHashes } from "./build-prod-bundle.js";
|
|
7
8
|
import { BUNDLED_ASSETS_DIST_DIR } from "./bundled-assets.js";
|
|
8
9
|
import { CSP_HASHES_FILE, parseCspHashes } from "./csp-hashes.js";
|
|
9
10
|
import { buildRequestBoundSystemQuery, } from "./request-bound-system-query.js";
|
|
@@ -67,7 +68,14 @@ function usesDefaultCsp(securityHeaders) {
|
|
|
67
68
|
export async function resolveHostDispatchScriptHashes(staticDir, securityHeaders, defaultCspHashes) {
|
|
68
69
|
return usesDefaultCsp(securityHeaders) ? defaultCspHashes : readBuildScriptHashes(staticDir);
|
|
69
70
|
}
|
|
71
|
+
// Apps also write HTML into staticDir at boot (rendered landing pages), after kumiko build wrote
|
|
72
|
+
// the file; staticDir is server-owned, so hashing what is on disk never trusts request data.
|
|
70
73
|
export async function readBuildScriptHashes(staticDir) {
|
|
74
|
+
const fromFile = await readBuildHashesFile(staticDir);
|
|
75
|
+
const onDisk = existsSync(staticDir) ? collectBuildScriptHashes(staticDir).scriptSrc : [];
|
|
76
|
+
return [...new Set([...fromFile, ...onDisk])].sort();
|
|
77
|
+
}
|
|
78
|
+
async function readBuildHashesFile(staticDir) {
|
|
71
79
|
const file = await readStaticFile(join(staticDir, CSP_HASHES_FILE));
|
|
72
80
|
const result = file ? parseCspHashes(new TextDecoder().decode(file.bytes)) : undefined;
|
|
73
81
|
if (result?.kind === "found")
|
package/dist/run-prod-app.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { AccountLockoutOptions, AccountUnlockOptions, AuthMailLocale, AuthP
|
|
|
3
3
|
import type { SeedAdminOptions } from "@cosmicdrift/kumiko-bundled-features/auth-email-password/seeding";
|
|
4
4
|
import type { ExtraRouteDefinition, KumikoServeEnv, PostAuthLandingResolver, SseBroker } from "@cosmicdrift/kumiko-framework/api";
|
|
5
5
|
import type { KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
|
|
6
|
-
import
|
|
6
|
+
import { type RowLevelSecurityMode } from "@cosmicdrift/kumiko-framework/db";
|
|
7
|
+
import type { BundledListsOptions, FeatureDefinition, ValidateBootOptions } from "@cosmicdrift/kumiko-framework/engine";
|
|
7
8
|
import type { AllInOneEntrypoint, ApiEntrypoint } from "@cosmicdrift/kumiko-framework/entrypoint";
|
|
8
9
|
import type { ComposedEnvSchema, KumikoBootError } from "@cosmicdrift/kumiko-framework/env";
|
|
9
10
|
import type { ErrorDocsConfig } from "@cosmicdrift/kumiko-framework/errors";
|
|
@@ -12,6 +13,8 @@ import type { ObservabilityProvider } from "@cosmicdrift/kumiko-framework/observ
|
|
|
12
13
|
import type { MasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
|
|
13
14
|
import type { PageHeadMeta, PageHeadResolver, PageHeadSystemQuery } from "@cosmicdrift/kumiko-renderer/headless/apex";
|
|
14
15
|
import type { Redis } from "ioredis";
|
|
16
|
+
import type { SearchBackendDeclaration } from "./boot/stream-tenant-backfill-on-boot.ts";
|
|
17
|
+
import type { DatabaseWaitClock } from "./boot/wait-for-database.ts";
|
|
15
18
|
import type { BillingBootOption } from "./boot-options/billing-option.ts";
|
|
16
19
|
import type { DemoModeOption } from "./boot-options/demo-mode.ts";
|
|
17
20
|
import type { FileStorageBootOption } from "./boot-options/file-storage-option.ts";
|
|
@@ -29,7 +32,7 @@ export { parseRunMode } from "./boot/env-gate.ts";
|
|
|
29
32
|
export { buildBunServeOptions } from "./bun-serve-options.ts";
|
|
30
33
|
export { requireEnv } from "./require-env.ts";
|
|
31
34
|
export { resolveUnsubscribeWiring, type UnsubscribeOption, } from "./resolve-unsubscribe-wiring.ts";
|
|
32
|
-
export { addConfigAccessorFactory, buildBootExtraContext, type ConfigOverridesInput, loggerFromExtraContext, resolveAuthMail, } from "./run-prod-app-boot-context.ts";
|
|
35
|
+
export { addConfigAccessorFactory, buildBootExtraContext, buildConfigOverrides, type ConfigOverridesInput, loggerFromExtraContext, resolveAuthMail, } from "./run-prod-app-boot-context.ts";
|
|
33
36
|
export { staticCachePolicy } from "./run-prod-app-static-files.ts";
|
|
34
37
|
/** Wrapper API for the password-reset flow.
|
|
35
38
|
*
|
|
@@ -263,6 +266,15 @@ export type RunProdAppOptions = {
|
|
|
263
266
|
readonly features: readonly FeatureDefinition[];
|
|
264
267
|
/** Opt-in boot-validator warnings — see ValidateBootOptions. */
|
|
265
268
|
readonly validateBootOptions?: ValidateBootOptions;
|
|
269
|
+
/** One table block for every list of the bundled features, see ComposeFeaturesOptions.bundledLists. */
|
|
270
|
+
readonly bundledLists?: BundledListsOptions;
|
|
271
|
+
/** Row level security mode of every connection; pass `appConfig.rowLevelSecurity`. Default "on". */
|
|
272
|
+
readonly rowLevelSecurity?: RowLevelSecurityMode;
|
|
273
|
+
/** `"none"`: the app has no search backend in any process (no `searchAdapter` in `extraContext`),
|
|
274
|
+
* so no search docs can be orphaned and the stream-tenant backfill runs without a reindex. Unset:
|
|
275
|
+
* a process without a search adapter defers the backfill of searchable entities to one that has
|
|
276
|
+
* it. Combining `"none"` with a `searchAdapter` fails the boot. Must match the worker process. */
|
|
277
|
+
readonly search?: SearchBackendDeclaration;
|
|
266
278
|
/** Listen-Port. Default 3000 (or $PORT). */
|
|
267
279
|
readonly port?: number;
|
|
268
280
|
/** Ceiling Bun.serve buffers a request body to before any app-level
|
|
@@ -339,16 +351,19 @@ export type RunProdAppOptions = {
|
|
|
339
351
|
readonly defaultLocale?: string;
|
|
340
352
|
readonly extraContext?: ExtraContextOption;
|
|
341
353
|
/** MasterKeyProvider replacing the env KEK (`KUMIKO_SECRETS_MASTER_KEY_V<n>`)
|
|
342
|
-
* for `ctx.secrets`, encrypted config keys and `encrypted: true` entity
|
|
354
|
+
* for `ctx.services.secrets`, encrypted config keys and `encrypted: true` entity
|
|
343
355
|
* fields. Default: `createEnvMasterKeyProvider`. Use it for KMS backends
|
|
344
356
|
* (AWS/GCP/Azure); with it set, `KUMIKO_SECRETS_MASTER_KEY_V1` is not
|
|
345
357
|
* required in the env. */
|
|
346
358
|
readonly masterKey?: MasterKeyProvider;
|
|
347
359
|
/** Subject-key adapter for crypto-shredding (GDPR Art. 17). When set, it is
|
|
348
|
-
* exposed to feature code as `ctx.kms`, and boot checks its health so the app
|
|
360
|
+
* exposed to feature code as `ctx.services.kms`, and boot checks its health so the app
|
|
349
361
|
* cannot start against an unreachable KMS. Without an adapter,
|
|
350
362
|
* crypto-shredding remains disabled. */
|
|
351
363
|
readonly kms?: KmsAdapter;
|
|
364
|
+
/** Replaces the real clock, sleep and log sink of the boot backoff for the database, KMS and
|
|
365
|
+
* seed waits; tests use it to run the retry path without real waiting. */
|
|
366
|
+
readonly databaseWait?: DatabaseWaitClock;
|
|
352
367
|
/** 32-byte base64 key for blind-index HMACs on `lookupable` fields
|
|
353
368
|
* (env: KUMIKO_BLIND_INDEX_KEY, `openssl rand -base64 32`). It is separate
|
|
354
369
|
* from PLATFORM_KEK and not part of the KmsAdapter contract. It is required
|
package/dist/run-prod-app.js
CHANGED
|
@@ -39,9 +39,11 @@
|
|
|
39
39
|
// same JWT_SECRET.
|
|
40
40
|
// PORT=3000
|
|
41
41
|
// KUMIKO_INSTANCE_ID=<stable per replica>
|
|
42
|
+
import { assertRowLevelSecurityBootState, knownTablesOf, } from "@cosmicdrift/kumiko-framework/db";
|
|
42
43
|
import { assertSeedModeNotInProduction } from "@cosmicdrift/kumiko-framework/event-store";
|
|
43
44
|
import { runEnvGate } from "./boot/env-gate.js";
|
|
44
45
|
import { buildEffectiveFeaturesResolver } from "./boot/shared-boot-steps.js";
|
|
46
|
+
import { warnIfRowLevelSecurityOff } from "./boot/warn-row-level-security-off.js";
|
|
45
47
|
import { installProcessLogger, uninstallProcessLogger } from "./process-logger.js";
|
|
46
48
|
import { wireProdAuth } from "./run-prod-app-auth.js";
|
|
47
49
|
import { composeProdFeatures, resolveProdBootConfig, runWithBootAbortCleanups, } from "./run-prod-app-config.js";
|
|
@@ -54,7 +56,7 @@ export { parseRunMode } from "./boot/env-gate.js";
|
|
|
54
56
|
export { buildBunServeOptions } from "./bun-serve-options.js";
|
|
55
57
|
export { requireEnv } from "./require-env.js";
|
|
56
58
|
export { resolveUnsubscribeWiring, } from "./resolve-unsubscribe-wiring.js";
|
|
57
|
-
export { addConfigAccessorFactory, buildBootExtraContext, loggerFromExtraContext, resolveAuthMail, } from "./run-prod-app-boot-context.js";
|
|
59
|
+
export { addConfigAccessorFactory, buildBootExtraContext, buildConfigOverrides, loggerFromExtraContext, resolveAuthMail, } from "./run-prod-app-boot-context.js";
|
|
58
60
|
export { staticCachePolicy } from "./run-prod-app-static-files.js";
|
|
59
61
|
// Returned from runProdApp when KUMIKO_DRY_RUN_ENV is set AND envSource
|
|
60
62
|
// was passed (= test-mode). The handle is intentionally inert — listen()
|
|
@@ -119,7 +121,7 @@ async function bootProdApp(options, registerAbortCleanup) {
|
|
|
119
121
|
}
|
|
120
122
|
return makeDryRunHandle();
|
|
121
123
|
}
|
|
122
|
-
const connections = await connectProdDependencies(config, log, registerAbortCleanup);
|
|
124
|
+
const connections = await connectProdDependencies(config, log, registerAbortCleanup, options.databaseWait);
|
|
123
125
|
// Tier-composition auto-wire: if a feature carries a tenantTierResolver extension, the
|
|
124
126
|
// resolver is built here, before the dispatcher exists. The app author only mounts
|
|
125
127
|
// `createTierEngineFeature(opts)`; the framework does the wiring.
|
|
@@ -131,12 +133,14 @@ async function bootProdApp(options, registerAbortCleanup) {
|
|
|
131
133
|
cacheSync: connections.cacheSync,
|
|
132
134
|
});
|
|
133
135
|
await assertProdSchemaCurrent(connections.db, options.migrations, log);
|
|
136
|
+
await assertRowLevelSecurityBootState(connections.db, config.rowLevelSecurity, knownTablesOf(features));
|
|
137
|
+
warnIfRowLevelSecurityOff(config.rowLevelSecurity, log);
|
|
134
138
|
const requestContext = await buildProdRequestContext(options, config, composed, connections);
|
|
135
139
|
const authWiring = await wireProdAuth(config, composed, connections);
|
|
136
140
|
const entrypointOptions = buildProdEntrypointOptions(options, config, composed, connections, requestContext, authWiring, effectiveFeatures);
|
|
137
141
|
const entrypoint = createProdEntrypoint(options, config, composed, connections, entrypointOptions);
|
|
138
142
|
registerProdShutdownHooks(entrypoint, connections.cacheSync, requestContext.ownedRedisSseBroker);
|
|
139
|
-
await runProdWireHook(options, composed, connections, entrypoint);
|
|
143
|
+
await runProdWireHook(options, config, composed, connections, entrypoint);
|
|
140
144
|
await runProdBootSeeds({ options, config, composed, connections, requestContext }, entrypoint, entrypointOptions.dispatcherOptions);
|
|
141
145
|
await startProdBackgroundWork(entrypoint, config, composed, connections);
|
|
142
146
|
const fetch = await buildProdFetchHandler(options, config, entrypoint);
|
package/dist/run-worker-app.d.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import type { ServerOptions } from "@cosmicdrift/kumiko-framework/api";
|
|
2
2
|
import type { KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
|
|
3
|
-
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
4
|
-
import type { FeatureDefinition, Registry, ValidateBootOptions } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import type { DbConnection, RowLevelSecurityMode } from "@cosmicdrift/kumiko-framework/db";
|
|
4
|
+
import type { BundledListsOptions, FeatureDefinition, Registry, ValidateBootOptions } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
5
|
import type { WorkerEntrypoint } from "@cosmicdrift/kumiko-framework/entrypoint";
|
|
6
6
|
import type { ComposedEnvSchema, KumikoBootError } from "@cosmicdrift/kumiko-framework/env";
|
|
7
7
|
import type { JobRunnerOptions } from "@cosmicdrift/kumiko-framework/jobs";
|
|
8
8
|
import { type ObservabilityProvider } from "@cosmicdrift/kumiko-framework/observability";
|
|
9
9
|
import type { MasterKeyProvider, SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
10
10
|
import type { Redis } from "ioredis";
|
|
11
|
+
import { type SearchBackendDeclaration } from "./boot/stream-tenant-backfill-on-boot.ts";
|
|
12
|
+
import type { DatabaseWaitClock } from "./boot/wait-for-database.ts";
|
|
11
13
|
import { type WorkerMetricsOptions, type WorkerMetricsServer } from "./boot/worker-metrics-server.ts";
|
|
12
14
|
import type { BillingBootOption } from "./boot-options/billing-option.ts";
|
|
13
15
|
import type { FileStorageBootOption } from "./boot-options/file-storage-option.ts";
|
|
14
16
|
import type { KmsMode } from "./boot-options/kms-mode.ts";
|
|
15
17
|
import type { MailBootOption } from "./boot-options/mail-option.ts";
|
|
16
18
|
import { type SystemWireDeps } from "./extra-routes-deps.ts";
|
|
19
|
+
import type { ConfigOverridesInput } from "./run-prod-app-boot-context.ts";
|
|
17
20
|
export type WorkerContextOption = Record<string, unknown> | ((deps: WorkerDeps) => Record<string, unknown>);
|
|
18
21
|
export type WorkerDeps = {
|
|
19
22
|
readonly db: DbConnection;
|
|
@@ -27,18 +30,27 @@ export type WorkerDeps = {
|
|
|
27
30
|
* `lifecycle` for `registerShutdownHook`. */
|
|
28
31
|
export type WorkerWireDeps = SystemWireDeps & {
|
|
29
32
|
readonly lifecycle: WorkerEntrypoint["lifecycle"];
|
|
30
|
-
/** The worker's ctx.secrets — what `createInboundMailSupervisor` needs as
|
|
33
|
+
/** The worker's ctx.services.secrets — what `createInboundMailSupervisor` needs as
|
|
31
34
|
* `providerCtx.secrets` (the IMAP provider reads credentials through it,
|
|
32
35
|
* with `auditUserId` set to a system identity for the audit trail). Undefined
|
|
33
36
|
* when the secrets feature is not mounted or no master key is available. */
|
|
34
37
|
readonly secrets?: SecretsContext;
|
|
35
38
|
};
|
|
36
39
|
export type RunWorkerAppOptions = {
|
|
40
|
+
/** Row level security mode of every connection; pass appConfig.rowLevelSecurity. Default "on". */
|
|
41
|
+
readonly rowLevelSecurity?: RowLevelSecurityMode;
|
|
42
|
+
/** `"none"`: the app has no search backend in any process (no `searchAdapter` in `extraContext`),
|
|
43
|
+
* so no search docs can be orphaned and the stream-tenant backfill runs without a reindex. Unset:
|
|
44
|
+
* a process without a search adapter defers the backfill of searchable entities to one that has
|
|
45
|
+
* it. Combining `"none"` with a `searchAdapter` fails the boot. Must match the API process. */
|
|
46
|
+
readonly search?: SearchBackendDeclaration;
|
|
37
47
|
/** App-specific features — same array as in the API/all-in-one process,
|
|
38
48
|
* so the registry + schema stay identical across processes. */
|
|
39
49
|
readonly features: readonly FeatureDefinition[];
|
|
40
50
|
/** Opt-in boot-validator warnings — see ValidateBootOptions. */
|
|
41
51
|
readonly validateBootOptions?: ValidateBootOptions;
|
|
52
|
+
/** One table block for every list of the bundled features, see ComposeFeaturesOptions.bundledLists. Must match the API process. */
|
|
53
|
+
readonly bundledLists?: BundledListsOptions;
|
|
42
54
|
/** Mount the auto-mixed config/user/tenant/auth-email-password features —
|
|
43
55
|
* MUST match the API process's `includeBundled` value, otherwise API
|
|
44
56
|
* and worker run with a diverging registry topology. Also controls
|
|
@@ -54,7 +66,7 @@ export type RunWorkerAppOptions = {
|
|
|
54
66
|
* RunProdAppOptions["extraContext"], without sseBroker (the worker
|
|
55
67
|
* has none — see entrypoint/index.ts's documented SSE limitation). */
|
|
56
68
|
readonly extraContext?: WorkerContextOption;
|
|
57
|
-
/** MasterKeyProvider replacing the env KEK for ctx.secrets, encrypted
|
|
69
|
+
/** MasterKeyProvider replacing the env KEK for ctx.services.secrets, encrypted
|
|
58
70
|
* config keys and `encrypted: true` entity fields; with it set,
|
|
59
71
|
* `KUMIKO_SECRETS_MASTER_KEY_V1` is not required (see
|
|
60
72
|
* RunProdAppOptions["masterKey"]). */
|
|
@@ -62,6 +74,12 @@ export type RunWorkerAppOptions = {
|
|
|
62
74
|
/** Subject-key adapter for crypto-shredding — boot checks health()
|
|
63
75
|
* before any connection (see RunProdAppOptions["kms"]). */
|
|
64
76
|
readonly kms?: KmsAdapter;
|
|
77
|
+
/** App-level config values, same semantics and merge as RunProdAppOptions["appOverrides"];
|
|
78
|
+
* the worker renders the mail layouts and must resolve the same branding as the API. */
|
|
79
|
+
readonly appOverrides?: ConfigOverridesInput;
|
|
80
|
+
/** Replaces the real clock, sleep and log sink of the boot backoff for the database and
|
|
81
|
+
* KMS waits; tests use it to run the retry path without real waiting. */
|
|
82
|
+
readonly databaseWait?: DatabaseWaitClock;
|
|
65
83
|
/** Blind-index key for lookupable fields (see
|
|
66
84
|
* RunProdAppOptions["blindIndexKey"]). */
|
|
67
85
|
readonly blindIndexKey?: string;
|
package/dist/run-worker-app.js
CHANGED
|
@@ -15,18 +15,21 @@
|
|
|
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 { appHookScopedRunner, DEFAULT_ROW_LEVEL_SECURITY_MODE, } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
18
19
|
import { scrubLogText } from "@cosmicdrift/kumiko-framework/logging";
|
|
19
20
|
import { createMetricsForFactory, } from "@cosmicdrift/kumiko-framework/observability";
|
|
20
21
|
import { logRedisConnectionState } from "@cosmicdrift/kumiko-framework/redis";
|
|
21
22
|
import { runEnvGate } from "./boot/env-gate.js";
|
|
22
23
|
import { startPiiEventBackfillOnBoot } from "./boot/pii-event-backfill-on-boot.js";
|
|
23
24
|
import { recordAppStartedOnBoot } from "./boot/record-app-started-on-boot.js";
|
|
25
|
+
import { runStreamTenantBackfillOnBoot, } from "./boot/stream-tenant-backfill-on-boot.js";
|
|
24
26
|
import { bootWorkerProcess, resolveWorkerEnvSource, } from "./boot/worker-boot-core.js";
|
|
25
27
|
import { startWorkerMetricsServer, } from "./boot/worker-metrics-server.js";
|
|
26
28
|
import { resolveBootOptionWiring } from "./boot-options/resolve-boot-options.js";
|
|
27
29
|
import { assertStackBootRequirements } from "./boot-options/stack-boot-requirements.js";
|
|
28
30
|
import { makeDispatchSystemQuery, makeDispatchSystemWrite, } from "./extra-routes-deps.js";
|
|
29
31
|
import { installFatalErrorHandlers } from "./fatal-error-handlers.js";
|
|
32
|
+
import { makeFilesForTenant } from "./files-for-tenant.js";
|
|
30
33
|
import { installProcessLogger, uninstallProcessLogger } from "./process-logger.js";
|
|
31
34
|
function makeBootModeHandle() {
|
|
32
35
|
return {
|
|
@@ -48,6 +51,22 @@ async function resolveGatedEnvSource(options) {
|
|
|
48
51
|
function assertWorkerStackRequirements(options) {
|
|
49
52
|
assertStackBootRequirements({ features: options.features, ...(options.featureToggles && { featureToggles: true }) }, ["featureToggles"]);
|
|
50
53
|
}
|
|
54
|
+
function wireHookDb(db, options) {
|
|
55
|
+
return appHookScopedRunner(db, options.rowLevelSecurity ?? DEFAULT_ROW_LEVEL_SECURITY_MODE);
|
|
56
|
+
}
|
|
57
|
+
// Runs before the metrics port opens and before start() consumes jobs: a worker must not process
|
|
58
|
+
// events of a feature whose streams still sit on the wrong tenant.
|
|
59
|
+
async function backfillThenStartMetrics(boot, options) {
|
|
60
|
+
await runStreamTenantBackfillOnBoot({
|
|
61
|
+
db: boot.db,
|
|
62
|
+
registry: boot.registry,
|
|
63
|
+
extraContext: boot.extraContext,
|
|
64
|
+
processName: "runWorkerApp",
|
|
65
|
+
...(options.search && { search: options.search }),
|
|
66
|
+
...(options.databaseWait && { databaseWait: options.databaseWait }),
|
|
67
|
+
});
|
|
68
|
+
return options.metrics ? startWorkerMetricsServer(options.metrics, boot.entrypoint) : undefined;
|
|
69
|
+
}
|
|
51
70
|
export async function runWorkerApp(options) {
|
|
52
71
|
assertSeedModeNotInProduction();
|
|
53
72
|
const resolvedEnvSource = await resolveGatedEnvSource(options);
|
|
@@ -91,9 +110,7 @@ export async function runWorkerApp(options) {
|
|
|
91
110
|
// hook, so a failing start() does not leak the listener.
|
|
92
111
|
let metricsServer;
|
|
93
112
|
try {
|
|
94
|
-
metricsServer = options
|
|
95
|
-
? startWorkerMetricsServer(options.metrics, entrypoint)
|
|
96
|
-
: undefined;
|
|
113
|
+
metricsServer = await backfillThenStartMetrics(boot, options);
|
|
97
114
|
await entrypoint.start();
|
|
98
115
|
}
|
|
99
116
|
catch (error) {
|
|
@@ -124,9 +141,10 @@ export async function runWorkerApp(options) {
|
|
|
124
141
|
});
|
|
125
142
|
if (options.wireComponents) {
|
|
126
143
|
await options.wireComponents({
|
|
127
|
-
db,
|
|
144
|
+
db: wireHookDb(db, options),
|
|
128
145
|
redis,
|
|
129
146
|
registry,
|
|
147
|
+
filesForTenant: makeFilesForTenant(entrypoint.context),
|
|
130
148
|
metricsFor: createMetricsForFactory(entrypoint.observability.meter),
|
|
131
149
|
dispatchSystemWrite: makeDispatchSystemWrite(entrypoint.dispatcher),
|
|
132
150
|
dispatchSystemQuery: makeDispatchSystemQuery(entrypoint.dispatcher),
|
package/dist/seed-dispatcher.js
CHANGED
|
@@ -7,7 +7,9 @@ export function makeSeedCreateFileRef(deps) {
|
|
|
7
7
|
const fileResolver = context._fileProviderResolver;
|
|
8
8
|
return (tenantId, input) => createFileRefFromBytes({
|
|
9
9
|
db: createTenantDb({ db, tenantId }),
|
|
10
|
-
|
|
10
|
+
services: {
|
|
11
|
+
files: fileResolver ? createTenantFileContext(fileResolver, tenantId) : context.files,
|
|
12
|
+
},
|
|
11
13
|
registry,
|
|
12
14
|
user: createSystemUser(tenantId),
|
|
13
15
|
}, input);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.390.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
5
|
"license": "MIT",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"types": "./dist/renderer-web-fonts.d.ts",
|
|
71
71
|
"default": "./dist/renderer-web-fonts.js"
|
|
72
72
|
},
|
|
73
|
+
"./resolve-installed-package": {
|
|
74
|
+
"types": "./dist/resolve-installed-package.d.ts",
|
|
75
|
+
"default": "./dist/resolve-installed-package.js"
|
|
76
|
+
},
|
|
73
77
|
"./resolve-tailwind-cli": {
|
|
74
78
|
"types": "./dist/resolve-tailwind-cli.d.ts",
|
|
75
79
|
"default": "./dist/resolve-tailwind-cli.js"
|
|
@@ -92,18 +96,21 @@
|
|
|
92
96
|
}
|
|
93
97
|
},
|
|
94
98
|
"dependencies": {
|
|
95
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
96
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
97
|
-
"@cosmicdrift/kumiko-renderer": "0.
|
|
98
|
-
"@cosmicdrift/kumiko-renderer-mail-html": "0.
|
|
99
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
100
|
-
"bun-types": "^1.3.13",
|
|
99
|
+
"@cosmicdrift/kumiko-bundled-features": "0.390.0",
|
|
100
|
+
"@cosmicdrift/kumiko-framework": "0.390.0",
|
|
101
|
+
"@cosmicdrift/kumiko-renderer": "0.390.0",
|
|
102
|
+
"@cosmicdrift/kumiko-renderer-mail-html": "0.390.0",
|
|
103
|
+
"@cosmicdrift/kumiko-types": "0.390.0",
|
|
101
104
|
"ioredis": "^5.10.1",
|
|
102
105
|
"temporal-polyfill": "^0.3.2",
|
|
103
106
|
"zod": "^4.4.3"
|
|
104
107
|
},
|
|
108
|
+
"peerDependencies": {
|
|
109
|
+
"bun-types": "^1.4.4"
|
|
110
|
+
},
|
|
105
111
|
"devDependencies": {
|
|
106
|
-
"@cosmicdrift/kumiko-testing": "0.
|
|
112
|
+
"@cosmicdrift/kumiko-testing": "0.390.0",
|
|
113
|
+
"bun-types": "^1.4.4"
|
|
107
114
|
},
|
|
108
115
|
"publishConfig": {
|
|
109
116
|
"registry": "https://registry.npmjs.org",
|
|
@@ -161,6 +168,10 @@
|
|
|
161
168
|
"types": "./dist/renderer-web-fonts.d.ts",
|
|
162
169
|
"default": "./dist/renderer-web-fonts.js"
|
|
163
170
|
},
|
|
171
|
+
"./resolve-installed-package": {
|
|
172
|
+
"types": "./dist/resolve-installed-package.d.ts",
|
|
173
|
+
"default": "./dist/resolve-installed-package.js"
|
|
174
|
+
},
|
|
164
175
|
"./resolve-tailwind-cli": {
|
|
165
176
|
"types": "./dist/resolve-tailwind-cli.d.ts",
|
|
166
177
|
"default": "./dist/resolve-tailwind-cli.js"
|