@cosmicdrift/kumiko-dev-server 0.348.0 → 0.349.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/bin/kumiko-upgrade.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// Shipped, app-facing upgrade CLI: `kumiko-upgrade [--from <version>] [--json] [--verbose]`.
|
|
3
|
+
// `--apply` runs the codemods and moves the marker to the installed version in one run;
|
|
4
|
+
// breaking changes without a codemod stay open in the marker until
|
|
5
|
+
// `--resolve <id|version> --reason "<text>" [--not-applicable]` marks them done.
|
|
3
6
|
//
|
|
4
7
|
// Self-contained — delegates to the framework's runUpgradeCli core, so apps
|
|
5
8
|
// check for breaking changes without the full dev `kumiko` CLI. Run from the
|
|
@@ -38,9 +38,11 @@ export type ScaffoldDeployDetected = {
|
|
|
38
38
|
* `bun install` in the manifests-first install variant. */
|
|
39
39
|
readonly registryConfigFiles: readonly RegistryConfigFile[];
|
|
40
40
|
/** DB user for migrate-step.sh's DATABASE_URL — from
|
|
41
|
-
* `package.json#kumiko.deploy.dbUser`, default = appName.
|
|
42
|
-
* always stays appName regardless of this value. */
|
|
41
|
+
* `package.json#kumiko.deploy.dbUser`, default = appName. */
|
|
43
42
|
readonly dbUser: string;
|
|
43
|
+
/** DB name for migrate-step.sh's DATABASE_URL — from
|
|
44
|
+
* `package.json#kumiko.deploy.dbName`, default = appName. */
|
|
45
|
+
readonly dbName: string;
|
|
44
46
|
};
|
|
45
47
|
export type ScaffoldedFile = {
|
|
46
48
|
readonly path: string;
|
package/dist/scaffold-deploy.js
CHANGED
|
@@ -54,6 +54,7 @@ export function renderDeployFiles(options) {
|
|
|
54
54
|
githubOrg,
|
|
55
55
|
installManifests,
|
|
56
56
|
dbUser: detected.dbUser,
|
|
57
|
+
dbName: detected.dbName,
|
|
57
58
|
};
|
|
58
59
|
const flags = {
|
|
59
60
|
hasSeeds: detected.hasSeeds,
|
|
@@ -61,6 +62,7 @@ export function renderDeployFiles(options) {
|
|
|
61
62
|
installFromFullTree: detected.installFromFullTree,
|
|
62
63
|
installFromManifests: !detected.installFromFullTree,
|
|
63
64
|
customDbUser: detected.dbUser !== options.appName,
|
|
65
|
+
customDbName: detected.dbName !== options.appName,
|
|
64
66
|
};
|
|
65
67
|
const dir = templatesDir();
|
|
66
68
|
const files = TEMPLATE_FILES.map(({ template, output }) => ({
|
|
@@ -131,13 +133,14 @@ function extractDeployConfigRaw(raw) {
|
|
|
131
133
|
return kumiko["deploy"];
|
|
132
134
|
}
|
|
133
135
|
const LOCAL_DEP_SPEC_RE = /^(file:|workspace:|link:)/;
|
|
134
|
-
// The user
|
|
135
|
-
//
|
|
136
|
-
// shell string, not passed through a driver's escaping.
|
|
137
|
-
const
|
|
136
|
+
// The user and database parts of DATABASE_URL — deliberately stricter than a
|
|
137
|
+
// full Postgres identifier grammar (no quoting support) since they are
|
|
138
|
+
// interpolated into a shell string, not passed through a driver's escaping.
|
|
139
|
+
const DB_IDENTIFIER_RE = /^[a-zA-Z0-9_][a-zA-Z0-9_-]{0,62}$/;
|
|
138
140
|
const kumikoDeployConfigSchema = z
|
|
139
141
|
.object({
|
|
140
|
-
dbUser: z.string().regex(
|
|
142
|
+
dbUser: z.string().regex(DB_IDENTIFIER_RE).optional(),
|
|
143
|
+
dbName: z.string().regex(DB_IDENTIFIER_RE).optional(),
|
|
141
144
|
// "directory" is the only behaviour left; the key stays valid so existing
|
|
142
145
|
// package.json files keep scaffolding.
|
|
143
146
|
stackNetwork: z
|
|
@@ -149,8 +152,8 @@ const kumikoDeployConfigSchema = z
|
|
|
149
152
|
})
|
|
150
153
|
.strict();
|
|
151
154
|
/** `package.json#kumiko.deploy` must be well-formed — silently falling back
|
|
152
|
-
* to defaults on an invalid `dbUser`/`stackNetwork` would render a migrate
|
|
153
|
-
* step that talks to the wrong DB user or
|
|
155
|
+
* to defaults on an invalid `dbUser`/`dbName`/`stackNetwork` would render a migrate
|
|
156
|
+
* step that talks to the wrong DB user, DB or stack network, breaking
|
|
154
157
|
* prod migrations without anyone noticing. Unlike detectOptionalSurfaces'
|
|
155
158
|
* malformed-JSON fallback, this throws (fail loud) and names the field. */
|
|
156
159
|
function parseDeployConfig(raw) {
|
|
@@ -167,20 +170,26 @@ function parseDeployConfig(raw) {
|
|
|
167
170
|
: ["kumiko", "deploy", ...issue.path.map(String)].join(".");
|
|
168
171
|
throw new Error(`scaffoldDeploy: invalid package.json#${location} — ${issue?.message ?? "validation failed"}`);
|
|
169
172
|
}
|
|
170
|
-
function
|
|
171
|
-
const
|
|
172
|
-
const dbUser = config.dbUser ?? appName;
|
|
173
|
+
function resolveDbIdentifier(configured, appName, field, role) {
|
|
174
|
+
const effective = configured ?? appName;
|
|
173
175
|
// The default (appName) already passed isKebabSegment's charset check but
|
|
174
|
-
// not
|
|
175
|
-
// over-long appName fails loud here instead of producing a DB
|
|
176
|
+
// not DB_IDENTIFIER_RE's length cap — validate the effective value so an
|
|
177
|
+
// over-long appName fails loud here instead of producing a DB identifier
|
|
176
178
|
// Postgres itself would reject at migrate-time.
|
|
177
|
-
if (!
|
|
178
|
-
if (
|
|
179
|
-
throw new Error(`scaffoldDeploy: appName "${appName}" cannot serve as the default
|
|
179
|
+
if (!DB_IDENTIFIER_RE.test(effective)) {
|
|
180
|
+
if (configured === undefined) {
|
|
181
|
+
throw new Error(`scaffoldDeploy: appName "${appName}" cannot serve as the default ${role} (must match ${DB_IDENTIFIER_RE}); set package.json#kumiko.deploy.${field}`);
|
|
180
182
|
}
|
|
181
|
-
throw new Error(`scaffoldDeploy: invalid package.json#kumiko.deploy
|
|
183
|
+
throw new Error(`scaffoldDeploy: invalid package.json#kumiko.deploy.${field} — effective value "${effective}" does not match ${DB_IDENTIFIER_RE}`);
|
|
182
184
|
}
|
|
183
|
-
return
|
|
185
|
+
return effective;
|
|
186
|
+
}
|
|
187
|
+
function resolveDeployConfig(deployConfigRaw, appName) {
|
|
188
|
+
const config = parseDeployConfig(deployConfigRaw);
|
|
189
|
+
return {
|
|
190
|
+
dbUser: resolveDbIdentifier(config.dbUser, appName, "dbUser", "DB user"),
|
|
191
|
+
dbName: resolveDbIdentifier(config.dbName, appName, "dbName", "DB name"),
|
|
192
|
+
};
|
|
184
193
|
}
|
|
185
194
|
function detectOptionalSurfaces(sourceDir, appName) {
|
|
186
195
|
const hasSeeds = existsSync(join(sourceDir, "seeds"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.349.0",
|
|
4
4
|
"description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding, codegen. Its compose-stacks/env-schema subpaths are consumed at prod boot too — see @cosmicdrift/kumiko-server-runtime for the prod runner itself.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
"kumiko-upgrade": "./bin/kumiko-upgrade.ts"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
68
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
69
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
70
|
-
"@cosmicdrift/kumiko-renderer-web": "0.
|
|
71
|
-
"@cosmicdrift/kumiko-server-runtime": "0.
|
|
67
|
+
"@cosmicdrift/kumiko-bundled-features": "0.349.0",
|
|
68
|
+
"@cosmicdrift/kumiko-framework": "0.349.0",
|
|
69
|
+
"@cosmicdrift/kumiko-headless": "0.349.0",
|
|
70
|
+
"@cosmicdrift/kumiko-renderer-web": "0.349.0",
|
|
71
|
+
"@cosmicdrift/kumiko-server-runtime": "0.349.0",
|
|
72
72
|
"bun-types": "^1.3.13",
|
|
73
73
|
"ts-morph": "^28.0.0",
|
|
74
74
|
"zod": "^4.4.3"
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.349.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "init-deploy reads kumiko.deploy.dbName for the migrate step's DATABASE_URL",
|
|
6
|
+
"detail": "init-deploy: `package.json#kumiko.deploy.dbName`\n`migrate-step.sh` builds `DATABASE_URL` with `kumiko.deploy.dbName` as the database (default: appName), validated like `dbUser`. Apps without the key render byte-identical output."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.347.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -51,13 +51,16 @@ urlencode() {
|
|
|
51
51
|
{{#customDbUser}}
|
|
52
52
|
# The db user is overridden via package.json#kumiko.deploy.dbUser.
|
|
53
53
|
{{/customDbUser}}
|
|
54
|
+
{{#customDbName}}
|
|
55
|
+
# The db name is overridden via package.json#kumiko.deploy.dbName.
|
|
56
|
+
{{/customDbName}}
|
|
54
57
|
# Image tag pinned to :latest — swap to :${BUILD_SHA} for atomic deploys.
|
|
55
58
|
#
|
|
56
59
|
# Compose DATABASE_URL into the shell env and pass it to the container by
|
|
57
60
|
# NAME (`-e DATABASE_URL`, no value) so the expanded password never appears
|
|
58
61
|
# in `docker run`'s argv — otherwise it would be visible in `ps auxe` for
|
|
59
62
|
# the duration of the migrate run.
|
|
60
|
-
export DATABASE_URL="postgresql://{{dbUser}}:$(urlencode "$DB_PASSWORD")@db:5432/{{
|
|
63
|
+
export DATABASE_URL="postgresql://{{dbUser}}:$(urlencode "$DB_PASSWORD")@db:5432/{{dbName}}"
|
|
61
64
|
docker run --rm \
|
|
62
65
|
--network "$STACK_NETWORK" \
|
|
63
66
|
-e DATABASE_URL \
|