@cosmicdrift/kumiko-dev-server 0.348.1 → 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.
@@ -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. The db name
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;
@@ -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 part of DATABASE_URL — deliberately stricter than a full Postgres
135
- // role-name grammar (no quoting support) since it is interpolated into a
136
- // shell string, not passed through a driver's escaping.
137
- const DB_USER_RE = /^[a-zA-Z0-9_][a-zA-Z0-9_-]{0,62}$/;
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(DB_USER_RE).optional(),
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 the wrong stack network, breaking
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 resolveDeployConfig(deployConfigRaw, appName) {
171
- const config = parseDeployConfig(deployConfigRaw);
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 DB_USER_RE's length cap — validate the effective value so an
175
- // over-long appName fails loud here instead of producing a DB user
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 (!DB_USER_RE.test(dbUser)) {
178
- if (config.dbUser === undefined) {
179
- throw new Error(`scaffoldDeploy: appName "${appName}" cannot serve as the default DB user (must match ${DB_USER_RE}); set package.json#kumiko.deploy.dbUser`);
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.dbUser — effective value "${dbUser}" (defaulted from appName) does not match ${DB_USER_RE}`);
183
+ throw new Error(`scaffoldDeploy: invalid package.json#kumiko.deploy.${field} — effective value "${effective}" does not match ${DB_IDENTIFIER_RE}`);
182
184
  }
183
- return { dbUser };
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.348.1",
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.348.1",
68
- "@cosmicdrift/kumiko-framework": "0.348.1",
69
- "@cosmicdrift/kumiko-headless": "0.348.1",
70
- "@cosmicdrift/kumiko-renderer-web": "0.348.1",
71
- "@cosmicdrift/kumiko-server-runtime": "0.348.1",
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/{{appName}}"
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 \