@cosmicdrift/kumiko-server-runtime 0.362.0 → 0.363.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 +7 -0
- package/dist/boot/worker-boot-core.js +6 -2
- package/dist/build-prod-bundle.js +6 -9
- package/dist/compose-features.js +15 -0
- package/dist/renderer-web-fonts.js +2 -3
- package/dist/run-prod-app-boot-context.d.ts +2 -2
- package/dist/run-prod-app-boot-context.js +30 -7
- package/dist/run-prod-app-static-files.js +3 -2
- package/dist/run-prod-app.js +5 -2
- package/package.json +7 -6
- package/src/changes.json +12 -0
|
@@ -8,4 +8,11 @@ import { seedAllConfigValues } from "@cosmicdrift/kumiko-bundled-features/config
|
|
|
8
8
|
// @wrapper-known semantic-alias
|
|
9
9
|
export async function applyBootSeeds(deps) {
|
|
10
10
|
await seedAllConfigValues(deps.registry, deps.db, deps.cipher);
|
|
11
|
+
// Loaded lazily: the seeding module pulls the document sanitizer (happy-dom),
|
|
12
|
+
// which apps without notification templates must not pay for at boot.
|
|
13
|
+
const hasNotificationTemplates = Array.from(deps.registry.getAllNotifications().values()).some((notification) => notification.template !== undefined);
|
|
14
|
+
if (hasNotificationTemplates) {
|
|
15
|
+
const { seedNotificationTemplates } = await import("@cosmicdrift/kumiko-bundled-features/template-resolver/seeding");
|
|
16
|
+
await seedNotificationTemplates(deps.db, deps.registry);
|
|
17
|
+
}
|
|
11
18
|
}
|
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
import { loadJwtSecretOrKeyring } from "@cosmicdrift/kumiko-framework/api";
|
|
7
7
|
import { configureBlindIndexKey, configurePiiSubjectKms, resolvePlatformKeks, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
8
8
|
import { configureEntityFieldEncryption, createDbConnection, dbConnectionOptionsFromEnv, outsideTransactionPoolOptionsFromEnv, } from "@cosmicdrift/kumiko-framework/db";
|
|
9
|
-
import { createRegistry, findTierResolverUsage, validateBoot, } from "@cosmicdrift/kumiko-framework/engine";
|
|
9
|
+
import { createRegistry, findTierResolverUsage, isTierResolverPlugin, validateBoot, } from "@cosmicdrift/kumiko-framework/engine";
|
|
10
10
|
import { createWorkerEntrypoint, } from "@cosmicdrift/kumiko-framework/entrypoint";
|
|
11
11
|
import { assertKumikoSchemaCurrent, SchemaDriftError, } from "@cosmicdrift/kumiko-framework/migrations";
|
|
12
12
|
import { createEntityCache, createEventDedup, createIdempotencyGuard, } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
13
13
|
import { createRedisCacheSyncBus, redisClientOptionsFromEnv, } from "@cosmicdrift/kumiko-framework/redis";
|
|
14
14
|
import { warnIfNonUtcServerTimeZone } from "@cosmicdrift/kumiko-framework/time";
|
|
15
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
15
16
|
import { Redis } from "ioredis";
|
|
16
17
|
import { composeFeatures } from "../compose-features.js";
|
|
17
18
|
import { assertPiiBootInvariants } from "../pii-boot-gate.js";
|
|
@@ -23,7 +24,7 @@ import { assertWorkerMetricsOptions } from "./worker-metrics-server.js";
|
|
|
23
24
|
function isSecretsContext(value) {
|
|
24
25
|
if (typeof value !== "object" || value === null)
|
|
25
26
|
return false;
|
|
26
|
-
return ["get", "has", "set", "delete"].every((method) => typeof value
|
|
27
|
+
return ["get", "has", "set", "delete"].every((method) => typeof propertyOf(value, method) === "function");
|
|
27
28
|
}
|
|
28
29
|
export async function resolveWorkerEnvSource(options, processName) {
|
|
29
30
|
const rawEnvSource = options.envSource ?? process.env;
|
|
@@ -39,6 +40,9 @@ async function buildTierEffectiveFeatures(features, db, registry, cacheSync) {
|
|
|
39
40
|
if (!tierResolverUsage)
|
|
40
41
|
return undefined;
|
|
41
42
|
const plugin = tierResolverUsage.options;
|
|
43
|
+
if (!isTierResolverPlugin(plugin)) {
|
|
44
|
+
throw new Error("tier-resolver registration options must provide a build() factory");
|
|
45
|
+
}
|
|
42
46
|
return plugin.build({ db, registry, cacheSync });
|
|
43
47
|
}
|
|
44
48
|
async function assertWorkerSchemaCurrent(db, migrationsDir, processName) {
|
|
@@ -60,7 +60,7 @@ import { canResolveTailwindStylesheet, resolveTailwindCli } from "./resolve-tail
|
|
|
60
60
|
// (Tailwind via Bun.spawn, Client-Bundle via Bun.build, Stylesheet-
|
|
61
61
|
// Resolution via Bun.resolveSync) sind Bun-only. Pro-Funktions-Inline-
|
|
62
62
|
// Checks driften sonst — eine Konstante hier hält das konsistent.
|
|
63
|
-
const hasBun = typeof
|
|
63
|
+
const hasBun = typeof Bun !== "undefined";
|
|
64
64
|
// Default-HTML wird nur genutzt wenn der App-Author KEIN index.html liefert.
|
|
65
65
|
// Hat keine Asset-Placeholder, weil der Default-Pfad für vanilla apps
|
|
66
66
|
// (nur public/) gedacht ist — wer JS/CSS will, schreibt ein eigenes
|
|
@@ -444,9 +444,8 @@ function resolveStylesheetEntry(cwd, clientEntry, override) {
|
|
|
444
444
|
if (!hasBun)
|
|
445
445
|
return undefined;
|
|
446
446
|
try {
|
|
447
|
-
const resolved =
|
|
448
|
-
|
|
449
|
-
if (!canResolveTailwindStylesheet(resolved, { bun, cwd })) {
|
|
447
|
+
const resolved = Bun.resolveSync("@cosmicdrift/kumiko-renderer-web/styles.css", cwd);
|
|
448
|
+
if (!canResolveTailwindStylesheet(resolved, { bun: Bun, cwd })) {
|
|
450
449
|
return undefined;
|
|
451
450
|
}
|
|
452
451
|
return { path: resolved, isRendererWebFallback: true };
|
|
@@ -521,16 +520,14 @@ export async function runTailwindOnce(entry, cwd) {
|
|
|
521
520
|
if (!hasBun) {
|
|
522
521
|
throw new Error("[kumiko build] Tailwind one-shot requires Bun (Bun.spawn) — run via `bun run …` or `bun kumiko build`.");
|
|
523
522
|
}
|
|
524
|
-
const
|
|
525
|
-
.Bun;
|
|
526
|
-
const cliPath = resolveTailwindCli({ bun: bunResolver, cwd });
|
|
523
|
+
const cliPath = resolveTailwindCli({ bun: Bun, cwd });
|
|
527
524
|
if (cliPath === undefined) {
|
|
528
525
|
throw new Error("[kumiko build] @tailwindcss/cli nicht auflösbar — `bun install` im App-Root ausführen.");
|
|
529
526
|
}
|
|
530
|
-
if (!canResolveTailwindStylesheet(entry, { bun:
|
|
527
|
+
if (!canResolveTailwindStylesheet(entry, { bun: Bun, cwd })) {
|
|
531
528
|
throw new Error(`[kumiko build] tailwindcss nicht auflösbar für ${entry} — peer dependency fehlt am Stylesheet-Ort.`);
|
|
532
529
|
}
|
|
533
|
-
const { compile, optimize, Scanner } = await loadTailwindEngine(cliPath,
|
|
530
|
+
const { compile, optimize, Scanner } = await loadTailwindEngine(cliPath, Bun);
|
|
534
531
|
const input = resolve(cwd, entry);
|
|
535
532
|
if (!existsSync(input)) {
|
|
536
533
|
throw new Error(`[kumiko build] tailwind: stylesheet not found: ${input}`);
|
package/dist/compose-features.js
CHANGED
|
@@ -16,10 +16,13 @@ import { AUTH_SELF_REGISTRATION_FEATURE, createAuthEmailPasswordFeature, createA
|
|
|
16
16
|
import { AUTH_MFA_FEATURE, mfaStatusCheckerFromFeature, } from "@cosmicdrift/kumiko-bundled-features/auth-mfa";
|
|
17
17
|
import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";
|
|
18
18
|
import { createFilesTenantDataFeature } from "@cosmicdrift/kumiko-bundled-features/files-tenant-data";
|
|
19
|
+
import { createRendererFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/renderer-foundation";
|
|
20
|
+
import { createTemplateResolverFeature } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
|
|
19
21
|
import { collectAssignableAppRoles, createTenantFeature, } from "@cosmicdrift/kumiko-bundled-features/tenant";
|
|
20
22
|
import { TENANT_LIFECYCLE_FEATURE } from "@cosmicdrift/kumiko-bundled-features/tenant-lifecycle";
|
|
21
23
|
import { createUserFeature } from "@cosmicdrift/kumiko-bundled-features/user";
|
|
22
24
|
import { dedupeFeatures } from "@cosmicdrift/kumiko-framework/engine";
|
|
25
|
+
import { createRendererMailHtmlFeature } from "@cosmicdrift/kumiko-renderer-mail-html";
|
|
23
26
|
const FILES_FEATURE = "files";
|
|
24
27
|
const FILES_TENANT_DATA_FEATURE = "files-tenant-data";
|
|
25
28
|
export function composeFeatures(appFeatures, options) {
|
|
@@ -92,10 +95,22 @@ export function composeFeatures(appFeatures, options) {
|
|
|
92
95
|
!names.has(FILES_TENANT_DATA_FEATURE);
|
|
93
96
|
return [
|
|
94
97
|
...bundled,
|
|
98
|
+
...missingAuthMailStack(names),
|
|
95
99
|
...filteredApp,
|
|
96
100
|
...(needsFilesTenantData ? [createFilesTenantDataFeature()] : []),
|
|
97
101
|
];
|
|
98
102
|
}
|
|
103
|
+
// auth-email-password declares its mails as stored templates, and the boot seed
|
|
104
|
+
// fails without the template-resolver and a mail renderer. Mounting the missing
|
|
105
|
+
// ones here keeps every includeBundled app bootable (also scaffolds); features
|
|
106
|
+
// the app already mounts are skipped because createRegistry rejects duplicates.
|
|
107
|
+
function missingAuthMailStack(mountedNames) {
|
|
108
|
+
return [
|
|
109
|
+
createTemplateResolverFeature(),
|
|
110
|
+
createRendererFoundationFeature(),
|
|
111
|
+
createRendererMailHtmlFeature(),
|
|
112
|
+
].filter((feature) => !mountedNames.has(feature.name));
|
|
113
|
+
}
|
|
99
114
|
// The magic-link mail fields shared by all five flows. Conditional spreads omit
|
|
100
115
|
// undefined keys (exactOptionalPropertyTypes) and avoid the property-write that
|
|
101
116
|
// trips noPropertyAccessFromIndexSignature on the type-aliased option shapes.
|
|
@@ -8,17 +8,16 @@ export const RENDERER_WEB_FONTS_URL_PREFIX = "/assets/kumiko/fonts/";
|
|
|
8
8
|
export const RENDERER_WEB_FONTS_DIST_DIR = "assets/kumiko/fonts";
|
|
9
9
|
/** Allowlist for servable font file names; also rules out path segments. */
|
|
10
10
|
export const RENDERER_WEB_FONT_FILE_PATTERN = /^[a-z0-9-]+\.woff2$/;
|
|
11
|
-
const hasBun = typeof
|
|
11
|
+
const hasBun = typeof Bun !== "undefined";
|
|
12
12
|
/** Directory of the woff2 files next to renderer-web's styles.css, or
|
|
13
13
|
* undefined when the package can't be resolved from any `searchFrom` dir
|
|
14
14
|
* (or outside Bun, which does the package-exports resolution). */
|
|
15
15
|
export function resolveRendererWebFontsDir(searchFrom) {
|
|
16
16
|
if (!hasBun)
|
|
17
17
|
return undefined;
|
|
18
|
-
const bun = globalThis.Bun;
|
|
19
18
|
for (const from of searchFrom) {
|
|
20
19
|
try {
|
|
21
|
-
const stylesheet =
|
|
20
|
+
const stylesheet = Bun.resolveSync("@cosmicdrift/kumiko-renderer-web/styles.css", from);
|
|
22
21
|
return join(dirname(stylesheet), "fonts");
|
|
23
22
|
}
|
|
24
23
|
catch {
|
|
@@ -2,8 +2,8 @@ import { type EmailVerificationOptions, type PasswordResetOptions } from "@cosmi
|
|
|
2
2
|
import { resolveSessionStore } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
|
|
3
3
|
import type { SseBroker } from "@cosmicdrift/kumiko-framework/api";
|
|
4
4
|
import type { KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
|
|
5
|
-
import type
|
|
6
|
-
import type
|
|
5
|
+
import { type DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
6
|
+
import { type ConfigResolver, type FeatureDefinition, type Registry } from "@cosmicdrift/kumiko-framework/engine";
|
|
7
7
|
import type { Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
8
8
|
import type { MasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
|
|
9
9
|
import type { Redis } from "ioredis";
|
|
@@ -9,6 +9,9 @@ import { bindPatAutoRevokeOnPasswordChangeFromFeature, revokeAllPatTokensForUser
|
|
|
9
9
|
import { createSecretsContext, SECRETS_FEATURE_NAME, } from "@cosmicdrift/kumiko-bundled-features/secrets";
|
|
10
10
|
import { bindAutoRevokeFromFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
|
|
11
11
|
import { createTemplateResolverApi } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
|
|
12
|
+
import { TENANT_FEATURE, TenantQueries } from "@cosmicdrift/kumiko-bundled-features/tenant";
|
|
13
|
+
import { createTenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
14
|
+
import { createSystemUser, } from "@cosmicdrift/kumiko-framework/engine";
|
|
12
15
|
import { resolveBootCrypto } from "./boot/boot-crypto.js";
|
|
13
16
|
// Boot-time context helpers for runProdApp: ctx-extra-context wiring
|
|
14
17
|
// (templateResolver/delivery/secrets/config-resolver), auth-mail convenience
|
|
@@ -43,8 +46,10 @@ export function addConfigAccessorFactory(resolved, registry) {
|
|
|
43
46
|
// and every notification silently skips. sseBroker optional (email/push don't
|
|
44
47
|
// need it, in-app SSE does). Queued channels go through the delivery jobs of
|
|
45
48
|
// the calling context's job runner (3rd factory arg); without one they send
|
|
46
|
-
// inline, with `secrets` for chat-channel credentials.
|
|
49
|
+
// inline, with `secrets` for chat-channel credentials. `to: { tenant }` resolves
|
|
50
|
+
// through tenantUserIdsQuery, which the caller sets when the tenant feature is mounted.
|
|
47
51
|
function buildDeliveryNotifyFactory(opts) {
|
|
52
|
+
const configAccessorFactory = opts.configResolver && createConfigAccessorFactory(opts.registry, opts.configResolver);
|
|
48
53
|
const deliveryService = createDeliveryService({
|
|
49
54
|
db: opts.db,
|
|
50
55
|
registry: opts.registry,
|
|
@@ -54,6 +59,17 @@ function buildDeliveryNotifyFactory(opts) {
|
|
|
54
59
|
...(opts.sseBroker && { sseBroker: opts.sseBroker }),
|
|
55
60
|
...(opts.escapeHatchAuditSink && { escapeHatchAuditSink: opts.escapeHatchAuditSink }),
|
|
56
61
|
...(opts.log && { log: opts.log }),
|
|
62
|
+
...(opts.tenantUserIdsQuery && { tenantUserIdsQuery: opts.tenantUserIdsQuery }),
|
|
63
|
+
...(configAccessorFactory && {
|
|
64
|
+
tenantConfigFor: (tenantId) => {
|
|
65
|
+
const systemUser = createSystemUser(tenantId);
|
|
66
|
+
return configAccessorFactory({
|
|
67
|
+
user: { id: systemUser.id, tenantId },
|
|
68
|
+
db: createTenantDb(opts.db, tenantId),
|
|
69
|
+
...(opts.secrets && { secrets: opts.secrets }),
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
57
73
|
});
|
|
58
74
|
return createDeliveryNotifyFactory(deliveryService, {
|
|
59
75
|
deliverQueuedInline: opts.deliverQueuedInline === true,
|
|
@@ -93,11 +109,21 @@ function bootCryptoContext(crypto) {
|
|
|
93
109
|
...(crypto.configCipher && { configEncryption: crypto.configCipher }),
|
|
94
110
|
};
|
|
95
111
|
}
|
|
112
|
+
function createBootConfigResolver(registry, envSource, crypto) {
|
|
113
|
+
return createConfigResolver({
|
|
114
|
+
appOverrides: buildEnvConfigOverrides(registry, envSource),
|
|
115
|
+
...(crypto.configCipher && { cipher: crypto.configCipher }),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
96
118
|
export function buildBootExtraContext(opts) {
|
|
97
119
|
const crypto = opts.crypto ?? resolveBootCrypto(opts.envSource, opts.masterKey);
|
|
98
120
|
const hasDeliveryFeature = opts.features.some((f) => f.name === DELIVERY_FEATURE);
|
|
121
|
+
const hasTenantFeature = opts.features.some((f) => f.name === TENANT_FEATURE);
|
|
99
122
|
const escapeHatchAuditSink = resolveEscapeHatchAuditSink(opts.features, opts.db);
|
|
100
123
|
const secrets = resolveBootSecrets(opts.db, opts.features, crypto, opts.registry);
|
|
124
|
+
const configResolver = opts.hasAuth
|
|
125
|
+
? createBootConfigResolver(opts.registry, opts.envSource, crypto)
|
|
126
|
+
: undefined;
|
|
101
127
|
return {
|
|
102
128
|
templateResolver: createTemplateResolverApi(opts.db),
|
|
103
129
|
...(opts.kms && { kms: opts.kms }),
|
|
@@ -111,6 +137,8 @@ export function buildBootExtraContext(opts) {
|
|
|
111
137
|
...(opts.redis && { redis: opts.redis }),
|
|
112
138
|
...(escapeHatchAuditSink && { escapeHatchAuditSink }),
|
|
113
139
|
...(opts.log && { log: opts.log }),
|
|
140
|
+
...(hasTenantFeature && { tenantUserIdsQuery: TenantQueries.resolveUserIds }),
|
|
141
|
+
configResolver,
|
|
114
142
|
deliverQueuedInline: opts.deliverQueuedInline,
|
|
115
143
|
}),
|
|
116
144
|
}),
|
|
@@ -118,12 +146,7 @@ export function buildBootExtraContext(opts) {
|
|
|
118
146
|
// reach it via ctx — previously only test-stack wired it.
|
|
119
147
|
...bootCryptoContext(crypto),
|
|
120
148
|
...(secrets && { secrets }),
|
|
121
|
-
...(
|
|
122
|
-
configResolver: createConfigResolver({
|
|
123
|
-
appOverrides: buildEnvConfigOverrides(opts.registry, opts.envSource),
|
|
124
|
-
...(crypto.configCipher && { cipher: crypto.configCipher }),
|
|
125
|
-
}),
|
|
126
|
-
}),
|
|
149
|
+
...(configResolver && { configResolver }),
|
|
127
150
|
};
|
|
128
151
|
}
|
|
129
152
|
function buildAuthPathAppUrl(baseUrl, path) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve, sep } from "node:path";
|
|
2
2
|
import { cachedResponse, computeStrongEtag, computeWeakEtag, createClientIpResolver, } from "@cosmicdrift/kumiko-framework/api";
|
|
3
3
|
import { resolveAndInjectPageHead } from "@cosmicdrift/kumiko-headless/apex";
|
|
4
|
+
import { errorCodeOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
4
5
|
import { ASSETS_DIR } from "./build-prod-bundle.js";
|
|
5
6
|
import { BUNDLED_ASSETS_DIST_DIR } from "./bundled-assets.js";
|
|
6
7
|
import { buildRequestBoundSystemQuery, } from "./request-bound-system-query.js";
|
|
@@ -35,7 +36,7 @@ export async function readStaticFile(filePath) {
|
|
|
35
36
|
return { bytes, mime: mimeTypeFor(filePath), mtimeMs: fileStat.mtimeMs };
|
|
36
37
|
}
|
|
37
38
|
catch (err) {
|
|
38
|
-
const code = err
|
|
39
|
+
const code = errorCodeOf(err);
|
|
39
40
|
// ENOENT: no such path. EISDIR: readFile() on a directory (e.g. GET
|
|
40
41
|
// /assets where "assets" is a subfolder copied verbatim from public/).
|
|
41
42
|
// ENOTDIR: a path segment that isn't a directory is used as one (e.g.
|
|
@@ -121,7 +122,7 @@ trustedProxyHops = 0) {
|
|
|
121
122
|
if (!file)
|
|
122
123
|
return null;
|
|
123
124
|
return {
|
|
124
|
-
bytes: file.bytes.buffer
|
|
125
|
+
bytes: new Uint8Array(file.bytes).buffer,
|
|
125
126
|
mime: file.mime,
|
|
126
127
|
etag: computeWeakEtag(file.mtimeMs, file.bytes.byteLength),
|
|
127
128
|
mtimeMs: file.mtimeMs,
|
package/dist/run-prod-app.js
CHANGED
|
@@ -51,7 +51,7 @@ import { UserQueries } from "@cosmicdrift/kumiko-bundled-features/user";
|
|
|
51
51
|
import { createDefaultSseBroker, createRedisLoginRateLimiter, loadJwtSecretOrKeyring, parseTrustedProxyHopsEnv, TRUSTED_PROXY_HOPS_ENV, } from "@cosmicdrift/kumiko-framework/api";
|
|
52
52
|
import { configureBlindIndexKey, configurePiiSubjectKms, resolvePlatformKeks, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
53
53
|
import { configureEntityFieldEncryption, createDbConnection, dbConnectionOptionsFromEnv, outsideTransactionPoolOptionsFromEnv, } from "@cosmicdrift/kumiko-framework/db";
|
|
54
|
-
import { collectWriteHandlerQns, createRegistry, findTierResolverUsage, validateAppCustomScreenWriteQns, validateBoot, } from "@cosmicdrift/kumiko-framework/engine";
|
|
54
|
+
import { collectWriteHandlerQns, createRegistry, findTierResolverUsage, isTierResolverPlugin, validateAppCustomScreenWriteQns, validateBoot, } from "@cosmicdrift/kumiko-framework/engine";
|
|
55
55
|
import { createAllInOneEntrypoint, createApiEntrypoint, } from "@cosmicdrift/kumiko-framework/entrypoint";
|
|
56
56
|
import { KumikoBootError, kmsSlotsOf, parseEnv, withOptionalEnvKeys, } from "@cosmicdrift/kumiko-framework/env";
|
|
57
57
|
import { renderDryRun } from "@cosmicdrift/kumiko-framework/env/dry-run";
|
|
@@ -360,6 +360,9 @@ export async function runProdApp(options) {
|
|
|
360
360
|
const tierResolverUsage = findTierResolverUsage(features);
|
|
361
361
|
if (tierResolverUsage) {
|
|
362
362
|
const plugin = tierResolverUsage.options;
|
|
363
|
+
if (!isTierResolverPlugin(plugin)) {
|
|
364
|
+
throw new Error("tier-resolver registration options must provide a build() factory");
|
|
365
|
+
}
|
|
363
366
|
resolvedEffectiveFeatures = await plugin.build({ db, registry, cacheSync });
|
|
364
367
|
}
|
|
365
368
|
}
|
|
@@ -749,7 +752,7 @@ export async function runProdApp(options) {
|
|
|
749
752
|
// Options-Shape (inkl. idleTimeout: 0 für SSE) liegt in der
|
|
750
753
|
// exportierten buildBunServeOptions-Funktion — siehe ihren
|
|
751
754
|
// Header für die Begründung.
|
|
752
|
-
if (typeof
|
|
755
|
+
if (typeof Bun === "undefined") {
|
|
753
756
|
// Klare Fehlermeldung statt nackter ReferenceError. Trifft wenn
|
|
754
757
|
// jemand listen() unter Node/vitest aufruft ohne autoListen:false
|
|
755
758
|
// — hilft beim Debug, statt sich an "Bun is not defined" abzumühen.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.363.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,11 +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-
|
|
91
|
-
"@cosmicdrift/kumiko-
|
|
87
|
+
"@cosmicdrift/kumiko-bundled-features": "0.363.0",
|
|
88
|
+
"@cosmicdrift/kumiko-framework": "0.363.0",
|
|
89
|
+
"@cosmicdrift/kumiko-headless": "0.363.0",
|
|
90
|
+
"@cosmicdrift/kumiko-renderer-mail-html": "0.363.0",
|
|
91
|
+
"@cosmicdrift/kumiko-renderer-web": "0.363.0",
|
|
92
|
+
"@cosmicdrift/kumiko-types": "0.363.0",
|
|
92
93
|
"bun-types": "^1.3.13",
|
|
93
94
|
"ioredis": "^5.10.1",
|
|
94
95
|
"temporal-polyfill": "^0.3.2"
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "composeFeatures adds template-resolver, renderer-foundation and renderer-mail-html for auth apps",
|
|
6
|
+
"detail": "With includeBundled, the auth mails are stored templates and the boot seed needs the template-resolver and a mail renderer. composeFeatures adds whichever of template-resolver, renderer-foundation and renderer-mail-html the app does not mount itself, so runDevApp, runProdApp and scaffolds keep booting. Apps without the template tables need a schema migration (`kumiko schema generate`)."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.363.0",
|
|
10
|
+
"type": "improvement",
|
|
11
|
+
"title": "Immediate notify renders with the tenant config in runProdApp",
|
|
12
|
+
"detail": "The boot delivery notify factory passes tenantConfigFor, built from the boot config resolver, to the delivery service, so immediate notifications render the tenant name in the mail layout like the delivery.render job does."
|
|
13
|
+
},
|
|
2
14
|
{
|
|
3
15
|
"version": "0.357.0",
|
|
4
16
|
"type": "fix",
|