@solcreek/cli 0.4.23 → 0.4.25

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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @solcreek/cli
2
2
 
3
+ ## 0.4.24
4
+
5
+ ### Fixes / DX
6
+
7
+ - **`creek init --db` adds a database without prompting.** Non-interactive
8
+ runs (CI, coding agents) previously skipped the "Add a database?"
9
+ question silently and produced a config without one. With `--db`, init
10
+ writes `[resources] database = true` and `[build].worker`, and scaffolds
11
+ the worker/index.ts example. When the prompt is skipped, init now says
12
+ so and points at `--db` (`--json` output gets a `databasePromptSkipped`
13
+ field and a breadcrumb).
14
+
15
+ - **`creek init <name>` sets the project name.** The positional name was
16
+ ignored and the directory basename used instead; `--name` was the only
17
+ working form.
18
+
19
+ - **`creek doctor` catches the resources-without-worker mismatch.** Two new
20
+ findings: declaring resources with no worker entry (the deploy would be a
21
+ static SPA where `/api/*` serves index.html — warn), and a worker file on
22
+ disk that no config points at, so it would never deploy (info).
23
+
24
+ - **`creek deploy` warns before shipping a static SPA that declares
25
+ resources**, naming the bindings and pointing at `[build].worker` — the
26
+ same mismatch doctor flags, surfaced even when doctor never ran.
27
+
28
+ - **The "nothing to deploy" finding now presents both fixes.** Build output
29
+ and worker entry are separate inputs; the guidance previously steered
30
+ only toward re-running the build, leaving API-route projects chasing the
31
+ wrong one.
32
+
3
33
  ## 0.4.23
4
34
 
5
35
  ### Fixes / DX
@@ -169,7 +169,9 @@ function s(n) {
169
169
  // CK_FIX_HINTS). Keep all three in sync when adding a new CK-* rule.
170
170
  const CK_FIX_HINTS = {
171
171
  "CK-NO-CONFIG": "Run `creek init` to scaffold a creek.toml, or cd to a directory that contains creek.toml / wrangler.* / package.json / index.html.",
172
- "CK-NOTHING-TO-DEPLOY": "Run the project's build command so there's output in [build].output, or set [build].command in creek.toml if the project needs one.",
172
+ "CK-NOTHING-TO-DEPLOY": "Run the project's build command so there's output in [build].output, or set [build].command in creek.toml if the project needs one. If the project has server code / API routes, also set [build].worker — the build alone never declares it.",
173
+ "CK-RESOURCES-NO-WORKER": "Resources are declared but no worker entry — the deploy is a static SPA and /api/* serves index.html. Set [build].worker in creek.toml (e.g. worker = \"worker/index.ts\"), or remove [resources] if the site is purely static.",
174
+ "CK-WORKER-UNDECLARED": "A worker-shaped file exists on disk but no worker entry is declared, so it will not be deployed. Point [build].worker in creek.toml at it, or delete the file if unused.",
173
175
  "CK-DB-DUAL-DRIVER-SPLIT": "Consolidate the split db.local.ts + db.prod.ts files. Share schema.ts and routes.ts; keep only thin boot files (server/local.ts for dev, server/worker.ts for prod) that differ in driver setup. See examples/vite-react-drizzle.",
174
176
  "CK-SYNC-SQLITE": "better-sqlite3 is synchronous and won't run on Workers. Migrate to an async ORM with a D1 adapter — Drizzle or Kysely are the drop-in paths.",
175
177
  "CK-PRISMA-SQLITE": "Prisma's SQLite datasource isn't supported on Cloudflare Workers. Switch to Drizzle or Kysely with a D1 adapter.",
@@ -10,10 +10,15 @@ export declare const initCommand: import("citty").CommandDef<{
10
10
  default: boolean;
11
11
  };
12
12
  name: {
13
- type: "string";
13
+ type: "positional";
14
14
  description: string;
15
15
  required: false;
16
16
  };
17
+ db: {
18
+ type: "boolean";
19
+ description: string;
20
+ default: false;
21
+ };
17
22
  adopt: {
18
23
  type: "string";
19
24
  description: string;
@@ -11,14 +11,19 @@ import { ensureGitignoreEntries } from "../utils/gitignore.js";
11
11
  export const initCommand = defineCommand({
12
12
  meta: {
13
13
  name: "init",
14
- description: "Initialize a new Creek project — writes creek.toml (project name, build command/output, detected framework) and, if you add a database, a worker/index.ts example. Or register a self-host creekd via --adopt / --hostkey-fingerprint.",
14
+ description: "Initialize a new Creek project — writes creek.toml (project name, build command/output, detected framework) and, if you add a database (interactive prompt or --db), a worker/index.ts example. Or register a self-host creekd via --adopt / --hostkey-fingerprint.",
15
15
  },
16
16
  args: {
17
17
  name: {
18
- type: "string",
18
+ type: "positional",
19
19
  description: "Project name (project init) OR host short-name (self-host init)",
20
20
  required: false,
21
21
  },
22
+ db: {
23
+ type: "boolean",
24
+ description: "Add a database without prompting — writes [resources] database = true and scaffolds worker/index.ts. Required to get the database path in non-interactive runs (the prompt is skipped there).",
25
+ default: false,
26
+ },
22
27
  adopt: {
23
28
  type: "string",
24
29
  description: "TOFU-pin a creekd host at <addr> (Path B). Fetches GET /v1/hostkey, prompts to verify, writes ~/.creek/hosts.json.",
@@ -62,10 +67,20 @@ export const initCommand = defineCommand({
62
67
  }
63
68
  const defaultName = basename(cwd).toLowerCase().replace(/[^a-z0-9-]/g, "-");
64
69
  const name = args.name ?? defaultName;
65
- // Ask about database
66
- let useDb = false;
67
- if (!jsonMode && !shouldAutoConfirm(args)) {
68
- useDb = await consola.prompt("Add a database?", { type: "confirm" });
70
+ // Ask about database. --db answers without prompting; otherwise the
71
+ // prompt only fires in interactive runs. In non-interactive runs
72
+ // (agents, CI — jsonMode or auto-confirm) the question is skipped,
73
+ // and we say so: silently defaulting to "no database" is how users
74
+ // end up hand-editing creek.toml and missing [build].worker.
75
+ let useDb = args.db === true;
76
+ let dbPromptSkipped = false;
77
+ if (!useDb) {
78
+ if (!jsonMode && !shouldAutoConfirm(args)) {
79
+ useDb = await consola.prompt("Add a database?", { type: "confirm" });
80
+ }
81
+ else {
82
+ dbPromptSkipped = true;
83
+ }
69
84
  }
70
85
  const config = {
71
86
  project: {
@@ -127,13 +142,19 @@ export default app;
127
142
  }
128
143
  }
129
144
  if (jsonMode) {
130
- jsonOutput({ ok: true, name, framework: framework ?? null, database: useDb, path: configPath }, 0, [
145
+ jsonOutput({ ok: true, name, framework: framework ?? null, database: useDb, databasePromptSkipped: dbPromptSkipped, path: configPath }, 0, [
146
+ ...(dbPromptSkipped
147
+ ? [{ command: "creek init --db", description: "Re-run with a database — writes [resources] and [build].worker, scaffolds worker/index.ts" }]
148
+ : []),
131
149
  { command: "creek deploy", description: "Deploy the project" },
132
150
  { command: "creek dev", description: "Start local development server" },
133
151
  ]);
134
152
  }
135
153
  consola.success(`Created creek.toml for "${name}"`);
136
154
  if (!jsonMode) {
155
+ if (dbPromptSkipped) {
156
+ consola.info("Skipped the database prompt (non-interactive). Re-run with `creek init --db` to add one — it writes [resources] and [build].worker and scaffolds worker/index.ts.");
157
+ }
137
158
  console.log("");
138
159
  consola.info(" Next steps:");
139
160
  consola.info(" creek deploy Deploy to production");
@@ -146,7 +167,7 @@ export default app;
146
167
  *
147
168
  * Two paths:
148
169
  *
149
- * Path B: --adopt=<addr> [--name <name>]
170
+ * Path B: --adopt=<addr> [<name>]
150
171
  * Fetches GET /v1/hostkey, recomputes the fingerprint from the
151
172
  * returned publicKey, prompts the operator to verify against
152
173
  * the provider console / paper bundle, and writes the pinned
@@ -271,7 +292,7 @@ async function initHostAdopt(addr, pastedFingerprint, hostName, jsonMode, autoCo
271
292
  * Derive a short host name from the adopt address. e.g.
272
293
  * --adopt=5.75.231.44:9080 → "h-5-75-231-44"
273
294
  * --adopt=my.host.dev → "h-my-host-dev"
274
- * Operator may override via --name.
295
+ * Operator may override via the positional name argument.
275
296
  */
276
297
  function defaultHostName(addr) {
277
298
  const noScheme = addr.replace(/^https?:\/\//, "").replace(/[:/].*$/, "");
@@ -39,15 +39,40 @@ function semverGte(version, target) {
39
39
  return aMin > bMin;
40
40
  return aPat >= bPat;
41
41
  }
42
+ /**
43
+ * Read the installed adapter version from the package.json above a resolved
44
+ * adapter entry path (.../adapter-creek/dist/index.js).
45
+ */
46
+ function adapterVersionAt(entryPath) {
47
+ let dir = dirname(entryPath);
48
+ for (let i = 0; i < 3; i++) {
49
+ const pkgPath = join(dir, "package.json");
50
+ if (existsSync(pkgPath)) {
51
+ try {
52
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
53
+ if (pkg.name === ADAPTER_PKG)
54
+ return pkg.version ?? null;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ dir = dirname(dir);
61
+ }
62
+ return null;
63
+ }
42
64
  /**
43
65
  * Resolve @solcreek/adapter-creek from any reachable location.
44
66
  *
45
67
  * Tries, in order: the CLI's own install (monorepo workspace / global
46
68
  * install alongside the adapter), the project's own node_modules, then the
47
- * lazy-installed copy under .creek/node_modules. Returns the adapter entry
48
- * path (for NEXT_ADAPTER_PATH), or null if not installed anywhere.
69
+ * lazy-installed copy under .creek/node_modules. Copies older than
70
+ * `minVersion` are skipped adapter < 0.2.1 cannot resolve its cache
71
+ * handler from the .creek lazy install, so a stale cached copy must not
72
+ * shadow a fixed one. Returns the adapter entry path (for
73
+ * NEXT_ADAPTER_PATH), or null if no acceptable copy is installed.
49
74
  */
50
- function resolveAdapterPath(cwd) {
75
+ function resolveAdapterPath(cwd, minVersion) {
51
76
  const bases = [import.meta.url];
52
77
  if (cwd) {
53
78
  // createRequire walks node_modules up from the base file's directory;
@@ -57,7 +82,13 @@ function resolveAdapterPath(cwd) {
57
82
  }
58
83
  for (const base of bases) {
59
84
  try {
60
- return createRequire(base).resolve("@solcreek/adapter-creek");
85
+ const entry = createRequire(base).resolve(ADAPTER_PKG);
86
+ if (minVersion) {
87
+ const version = adapterVersionAt(entry);
88
+ if (!version || !semverGte(version, minVersion))
89
+ continue;
90
+ }
91
+ return entry;
61
92
  }
62
93
  catch {
63
94
  // try next base
@@ -148,7 +179,11 @@ const CREEK_DIR = ".creek";
148
179
  const OPENNEXT_PKG = "@opennextjs/cloudflare";
149
180
  const OPENNEXT_VERSION = "^1.18.0";
150
181
  const ADAPTER_PKG = "@solcreek/adapter-creek";
151
- const ADAPTER_VERSION = "^0.2.0";
182
+ const ADAPTER_VERSION = "^0.2.1";
183
+ // Adapter < 0.2.1 resolves its cache handler against paths that don't exist
184
+ // under the .creek lazy install, failing every Next.js build with webpack
185
+ // "Module not found". Installs below this are re-installed, not reused.
186
+ const ADAPTER_MIN_VERSION = "0.2.1";
152
187
  /**
153
188
  * Merge a dependency into .creek/package.json without clobbering deps that
154
189
  * a previous install (adapter or opennext) may have already written.
@@ -199,15 +234,20 @@ function installCreekDep(creekDir, pkg, version) {
199
234
  * CLI dependency that every `npx creek` user would pay for.
200
235
  */
201
236
  function ensureAdapter(cwd) {
202
- const existing = resolveAdapterPath(cwd);
237
+ const existing = resolveAdapterPath(cwd, ADAPTER_MIN_VERSION);
203
238
  if (existing)
204
239
  return existing;
205
- consola.start(` Installing ${ADAPTER_PKG} (one-time setup)...`);
240
+ // Distinguish "not installed" from "only stale copies" for the message;
241
+ // either way the fix is the same install into .creek.
242
+ const stale = resolveAdapterPath(cwd) !== null;
243
+ consola.start(stale
244
+ ? ` Updating ${ADAPTER_PKG} to >= ${ADAPTER_MIN_VERSION} (older versions cannot build)...`
245
+ : ` Installing ${ADAPTER_PKG} (one-time setup)...`);
206
246
  if (!installCreekDep(join(cwd, CREEK_DIR), ADAPTER_PKG, ADAPTER_VERSION)) {
207
247
  consola.warn(` Could not install ${ADAPTER_PKG}`);
208
248
  return null;
209
249
  }
210
- const resolved = resolveAdapterPath(cwd);
250
+ const resolved = resolveAdapterPath(cwd, ADAPTER_MIN_VERSION);
211
251
  if (resolved)
212
252
  consola.success(` ${ADAPTER_PKG} installed`);
213
253
  return resolved;
@@ -245,6 +245,15 @@ export async function prepareDeployBundle(input) {
245
245
  const effectiveEntrypoint = astroAdapter
246
246
  ? "entry.mjs"
247
247
  : (plan.worker.entry ?? null);
248
+ // Resources declared but the deploy carries no server code: the
249
+ // bindings get provisioned with nothing able to read them, and
250
+ // /api/* serves static assets (unknown paths fall back to
251
+ // index.html). Same condition as doctor's CK-RESOURCES-NO-WORKER —
252
+ // repeated here because most deploys never run `creek doctor` first.
253
+ const resourceBindings = resolved.bindings.filter((b) => b.type === "d1" || b.type === "r2" || b.type === "kv" || b.type === "ai");
254
+ if (effectiveRenderMode === "spa" && resourceBindings.length > 0) {
255
+ consola.warn(` Resources declared (${resourceBindings.map((b) => `env.${b.name}`).join(", ")}) but no worker entry — deploying as a static SPA. /api/* will serve index.html, not server code. If that's unintended, set [build].worker in creek.toml (details: creek doctor).`);
256
+ }
248
257
  return {
249
258
  plan,
250
259
  framework,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solcreek/cli",
3
- "version": "0.4.23",
3
+ "version": "0.4.25",
4
4
  "description": "CLI for the Creek deployment platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,7 +33,7 @@
33
33
  "esbuild": "^0.25.0",
34
34
  "smol-toml": "^1.3.1",
35
35
  "ws": "^8.20.0",
36
- "@solcreek/sdk": "0.4.11"
36
+ "@solcreek/sdk": "0.4.12"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@testing-library/dom": "^10.4.1",