@cosmicdrift/kumiko-dev-server 0.81.0 → 0.82.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.82.0",
|
|
4
4
|
"description": "Development server bootstrap for Kumiko apps. Bundles the client, mints dev-JWTs, injects the resolved AppSchema, and seeds an admin. Not for production.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"kumiko-schema-check": "./bin/kumiko-schema-check.ts"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
54
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
53
|
+
"@cosmicdrift/kumiko-bundled-features": "0.82.0",
|
|
54
|
+
"@cosmicdrift/kumiko-framework": "0.82.0",
|
|
55
55
|
"ts-morph": "^28.0.0"
|
|
56
56
|
},
|
|
57
57
|
"publishConfig": {
|
|
@@ -94,4 +94,38 @@ describe("runDevApp configResolver-default — ENV→app-override bridge", () =>
|
|
|
94
94
|
}) as ExtraObj;
|
|
95
95
|
expect(extra.configResolver).toBe(custom);
|
|
96
96
|
});
|
|
97
|
+
|
|
98
|
+
// Regression: the GDPR export download threw errors.internal in prod because
|
|
99
|
+
// boot only wired `configResolver`, never `_configAccessorFactory` — so the
|
|
100
|
+
// dispatcher left ctx.config undefined and createFileProviderForTenant threw
|
|
101
|
+
// "ctx.config is missing". The boot now mints the factory.
|
|
102
|
+
test("boot wiring mints _configAccessorFactory so handlers get ctx.config", () => {
|
|
103
|
+
const extra = mergeConfigResolverDefault(undefined, stack.registry, {}) as ExtraObj & {
|
|
104
|
+
readonly _configAccessorFactory?: unknown;
|
|
105
|
+
};
|
|
106
|
+
expect(typeof extra._configAccessorFactory).toBe("function");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("_configAccessorFactory uses the EFFECTIVE resolver (caller override wins over env-bridge)", async () => {
|
|
110
|
+
// money-horse pins `file-foundation:config:provider` = "s3-env" via an
|
|
111
|
+
// appOverride on its own configResolver; ctx.config MUST read that override,
|
|
112
|
+
// not the boot default — else the download resolves no provider.
|
|
113
|
+
const override = createConfigResolver({ appOverrides: new Map([[PAGE_SIZE, "99"]]) });
|
|
114
|
+
const extra = mergeConfigResolverDefault({ configResolver: override }, stack.registry, {
|
|
115
|
+
DEVCFG_PAGE_SIZE: "25", // env-bridge default would be 25 — the override must win
|
|
116
|
+
}) as ExtraObj & {
|
|
117
|
+
readonly _configAccessorFactory: (deps: {
|
|
118
|
+
readonly user: { readonly id: string; readonly tenantId: string };
|
|
119
|
+
readonly db: typeof stack.db;
|
|
120
|
+
}) => (key: string) => Promise<unknown>;
|
|
121
|
+
};
|
|
122
|
+
const accessor = extra._configAccessorFactory({
|
|
123
|
+
user: { id: TestUsers.systemAdmin.id, tenantId: TestUsers.systemAdmin.tenantId },
|
|
124
|
+
db: stack.db,
|
|
125
|
+
});
|
|
126
|
+
// appOverride values come through raw (string) — what matters is it's the
|
|
127
|
+
// override's "99", NOT the env-bridge default ("25"): proves the factory
|
|
128
|
+
// was built from the caller's resolver, exactly like money-horse's "s3-env".
|
|
129
|
+
expect(await accessor(PAGE_SIZE)).toBe("99");
|
|
130
|
+
});
|
|
97
131
|
});
|
package/src/run-dev-app.ts
CHANGED
|
@@ -69,6 +69,7 @@ import type {
|
|
|
69
69
|
PasswordResetSetup,
|
|
70
70
|
SignupSetup,
|
|
71
71
|
} from "./run-prod-app";
|
|
72
|
+
import { addConfigAccessorFactory } from "./run-prod-app";
|
|
72
73
|
|
|
73
74
|
export type RunDevAppAuthOptions = {
|
|
74
75
|
/** Admin user to seed at boot. Idempotent — re-runs in persistent-DB
|
|
@@ -451,9 +452,13 @@ export function mergeConfigResolverDefault(
|
|
|
451
452
|
appOverrides: buildEnvConfigOverrides(registry, envSource),
|
|
452
453
|
}),
|
|
453
454
|
};
|
|
454
|
-
|
|
455
|
+
// ctx.config wird per-Request aus _configAccessorFactory geminted (siehe
|
|
456
|
+
// addConfigAccessorFactory) — sonst bleibt ctx.config undefined und Handler
|
|
457
|
+
// die es lesen (createFileProviderForTenant) werfen. Aus dem EFFEKTIVEN
|
|
458
|
+
// Resolver (Caller-Override gewinnt) gebaut, symmetrisch zu runProdApp.
|
|
459
|
+
if (ctx === undefined) return addConfigAccessorFactory(defaults, registry);
|
|
455
460
|
if (typeof ctx === "function") {
|
|
456
|
-
return (deps) => ({ ...defaults, ...ctx(deps) });
|
|
461
|
+
return (deps) => addConfigAccessorFactory({ ...defaults, ...ctx(deps) }, registry);
|
|
457
462
|
}
|
|
458
|
-
return { ...defaults, ...ctx };
|
|
463
|
+
return addConfigAccessorFactory({ ...defaults, ...ctx }, registry);
|
|
459
464
|
}
|
package/src/run-prod-app.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
} from "@cosmicdrift/kumiko-bundled-features/auth-email-password/seeding";
|
|
39
39
|
import {
|
|
40
40
|
buildEnvConfigOverrides,
|
|
41
|
+
createConfigAccessorFactory,
|
|
41
42
|
createConfigResolver,
|
|
42
43
|
} from "@cosmicdrift/kumiko-bundled-features/config";
|
|
43
44
|
import { createSessionCallbacks } from "@cosmicdrift/kumiko-bundled-features/sessions";
|
|
@@ -47,11 +48,13 @@ import { createSseBroker, type SseBroker } from "@cosmicdrift/kumiko-framework/a
|
|
|
47
48
|
import { createDbConnection, type DbRunner } from "@cosmicdrift/kumiko-framework/db";
|
|
48
49
|
import {
|
|
49
50
|
buildAppSchema,
|
|
51
|
+
type ConfigResolver,
|
|
50
52
|
collectWriteHandlerQns,
|
|
51
53
|
createRegistry,
|
|
52
54
|
type EffectiveFeaturesResolver,
|
|
53
55
|
type FeatureDefinition,
|
|
54
56
|
findTierResolverUsage,
|
|
57
|
+
type Registry,
|
|
55
58
|
type TenantId,
|
|
56
59
|
type TierResolverPlugin,
|
|
57
60
|
validateAppCustomScreenWriteQns,
|
|
@@ -517,6 +520,25 @@ export type ProdAppHandle = {
|
|
|
517
520
|
readonly stop: () => Promise<void>;
|
|
518
521
|
};
|
|
519
522
|
|
|
523
|
+
// Mint `ctx.config` per request: the dispatcher only builds a per-user
|
|
524
|
+
// ConfigAccessor when `_configAccessorFactory` is on the AppContext
|
|
525
|
+
// (pipeline/dispatcher.ts). Without it `ctx.config` stays undefined and any
|
|
526
|
+
// handler reading it — e.g. createFileProviderForTenant for the GDPR export
|
|
527
|
+
// download — throws "ctx.config is missing". Built from the EFFECTIVE resolver
|
|
528
|
+
// so an app-supplied configResolver override (its appOverrides) is the one
|
|
529
|
+
// ctx.config reads. Shared with runDevApp (mergeConfigResolverDefault) for
|
|
530
|
+
// dev/prod parity.
|
|
531
|
+
export function addConfigAccessorFactory<T extends { readonly configResolver?: ConfigResolver }>(
|
|
532
|
+
resolved: T,
|
|
533
|
+
registry: Registry,
|
|
534
|
+
): T {
|
|
535
|
+
if (!resolved.configResolver) return resolved;
|
|
536
|
+
return {
|
|
537
|
+
...resolved,
|
|
538
|
+
_configAccessorFactory: createConfigAccessorFactory(registry, resolved.configResolver),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
520
542
|
export async function runProdApp(options: RunProdAppOptions): Promise<ProdAppHandle> {
|
|
521
543
|
// 0. Env-Schema validation + dry-run modes. Runs FIRST so:
|
|
522
544
|
// - operators can introspect env-requirements without a real boot
|
|
@@ -688,14 +710,17 @@ export async function runProdApp(options: RunProdAppOptions): Promise<ProdAppHan
|
|
|
688
710
|
typeof options.extraContext === "function"
|
|
689
711
|
? options.extraContext(deps)
|
|
690
712
|
: (options.extraContext ?? {});
|
|
691
|
-
const extraContext =
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
713
|
+
const extraContext = addConfigAccessorFactory(
|
|
714
|
+
options.auth
|
|
715
|
+
? {
|
|
716
|
+
configResolver: createConfigResolver({
|
|
717
|
+
appOverrides: buildEnvConfigOverrides(registry, envSource),
|
|
718
|
+
}),
|
|
719
|
+
...resolvedExtraContext,
|
|
720
|
+
}
|
|
721
|
+
: resolvedExtraContext,
|
|
722
|
+
registry,
|
|
723
|
+
);
|
|
699
724
|
const resolvedAnonymousAccess =
|
|
700
725
|
typeof options.anonymousAccess === "function"
|
|
701
726
|
? options.anonymousAccess(deps)
|