@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 +30 -0
- package/dist/commands/doctor.js +3 -1
- package/dist/commands/init.d.ts +6 -1
- package/dist/commands/init.js +30 -9
- package/dist/utils/nextjs.js +48 -8
- package/dist/utils/prepare-bundle.js +9 -0
- package/package.json +2 -2
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
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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.",
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -10,10 +10,15 @@ export declare const initCommand: import("citty").CommandDef<{
|
|
|
10
10
|
default: boolean;
|
|
11
11
|
};
|
|
12
12
|
name: {
|
|
13
|
-
type: "
|
|
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;
|
package/dist/commands/init.js
CHANGED
|
@@ -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: "
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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> [
|
|
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
|
|
295
|
+
* Operator may override via the positional name argument.
|
|
275
296
|
*/
|
|
276
297
|
function defaultHostName(addr) {
|
|
277
298
|
const noScheme = addr.replace(/^https?:\/\//, "").replace(/[:/].*$/, "");
|
package/dist/utils/nextjs.js
CHANGED
|
@@ -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.
|
|
48
|
-
*
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
36
|
+
"@solcreek/sdk": "0.4.12"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@testing-library/dom": "^10.4.1",
|