creek 0.3.0 → 0.3.2

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.
Files changed (49) hide show
  1. package/bin.js +2 -0
  2. package/hono.d.ts +1 -0
  3. package/hono.js +1 -0
  4. package/index.d.ts +1 -0
  5. package/index.js +1 -0
  6. package/package.json +30 -35
  7. package/react.d.ts +1 -0
  8. package/react.js +1 -0
  9. package/README.md +0 -184
  10. package/dist/commands/claim.d.ts +0 -18
  11. package/dist/commands/claim.js +0 -93
  12. package/dist/commands/deploy.d.ts +0 -38
  13. package/dist/commands/deploy.js +0 -807
  14. package/dist/commands/deployments.d.ts +0 -18
  15. package/dist/commands/deployments.js +0 -84
  16. package/dist/commands/env.d.ts +0 -2
  17. package/dist/commands/env.js +0 -104
  18. package/dist/commands/init.d.ts +0 -18
  19. package/dist/commands/init.js +0 -69
  20. package/dist/commands/login.d.ts +0 -23
  21. package/dist/commands/login.js +0 -120
  22. package/dist/commands/projects.d.ts +0 -13
  23. package/dist/commands/projects.js +0 -38
  24. package/dist/commands/status.d.ts +0 -18
  25. package/dist/commands/status.js +0 -115
  26. package/dist/commands/whoami.d.ts +0 -13
  27. package/dist/commands/whoami.js +0 -43
  28. package/dist/index.d.ts +0 -3
  29. package/dist/index.js +0 -36
  30. package/dist/utils/auth-server.d.ts +0 -22
  31. package/dist/utils/auth-server.js +0 -91
  32. package/dist/utils/bundle.d.ts +0 -6
  33. package/dist/utils/bundle.js +0 -39
  34. package/dist/utils/config.d.ts +0 -12
  35. package/dist/utils/config.js +0 -37
  36. package/dist/utils/git-clone.d.ts +0 -44
  37. package/dist/utils/git-clone.js +0 -193
  38. package/dist/utils/nextjs.d.ts +0 -48
  39. package/dist/utils/nextjs.js +0 -368
  40. package/dist/utils/output.d.ts +0 -38
  41. package/dist/utils/output.js +0 -45
  42. package/dist/utils/repo-url.d.ts +0 -48
  43. package/dist/utils/repo-url.js +0 -201
  44. package/dist/utils/sandbox.d.ts +0 -50
  45. package/dist/utils/sandbox.js +0 -69
  46. package/dist/utils/ssr-bundle.d.ts +0 -6
  47. package/dist/utils/ssr-bundle.js +0 -48
  48. package/dist/utils/tos.d.ts +0 -28
  49. package/dist/utils/tos.js +0 -95
@@ -1,807 +0,0 @@
1
- import { defineCommand } from "citty";
2
- import consola from "consola";
3
- import { existsSync, readFileSync, readdirSync, statSync, rmSync } from "node:fs";
4
- import { join, resolve } from "node:path";
5
- import { execSync, execFileSync } from "node:child_process";
6
- import { CreekClient, CreekAuthError, isSSRFramework, getSSRServerEntry, getClientAssetsDir, getDefaultBuildOutput, detectFramework, resolveConfig, formatDetectionSummary, resolvedConfigToResources, resolvedConfigToBindingRequirements, ConfigNotFoundError, getSSRServerDir, collectServerFiles, isPreBundledFramework, detectNextjsMode, detectMonorepo, } from "@solcreek/sdk";
7
- import { getToken, getApiUrl } from "../utils/config.js";
8
- import { collectAssets } from "../utils/bundle.js";
9
- import { bundleSSRServer } from "../utils/ssr-bundle.js";
10
- import { sandboxDeploy, pollSandboxStatus, printSandboxSuccess } from "../utils/sandbox.js";
11
- import { isTTY, jsonOutput, resolveJsonMode, globalArgs, shouldAutoConfirm } from "../utils/output.js";
12
- import { ensureTosAccepted } from "../utils/tos.js";
13
- import { buildNextjs, patchBundledWorker, hasAdapterOutput } from "../utils/nextjs.js";
14
- import { isRepoUrl, parseRepoUrl, validateRepoUrl, validateSubpath, RepoUrlError } from "../utils/repo-url.js";
15
- import { checkGitInstalled, cloneRepo, detectPackageManager, installDependencies, cleanupDir as cleanupCloneDir, GitCloneError } from "../utils/git-clone.js";
16
- function section(name) {
17
- consola.log(`\n \x1b[2m[${name}]\x1b[0m`);
18
- }
19
- function assetSummary(fileList) {
20
- const byExt = {};
21
- for (const f of fileList) {
22
- const ext = f.includes(".") ? `.${f.split(".").pop()}` : "(other)";
23
- byExt[ext] = (byExt[ext] ?? 0) + 1;
24
- }
25
- const parts = Object.entries(byExt)
26
- .sort((a, b) => b[1] - a[1])
27
- .slice(0, 4)
28
- .map(([ext, n]) => `${n} ${ext}`);
29
- return parts.join(", ");
30
- }
31
- export const deployCommand = defineCommand({
32
- meta: {
33
- name: "deploy",
34
- description: "Deploy the current project to Creek",
35
- },
36
- args: {
37
- dir: {
38
- type: "positional",
39
- description: "Directory to deploy (default: current directory)",
40
- required: false,
41
- },
42
- "skip-build": {
43
- type: "boolean",
44
- description: "Skip the build step",
45
- default: false,
46
- },
47
- demo: {
48
- type: "boolean",
49
- description: "Deploy a sample site to see Creek in action",
50
- default: false,
51
- },
52
- ...globalArgs,
53
- template: {
54
- type: "string",
55
- description: "Deploy a template (e.g., react-dashboard, astro-landing)",
56
- required: false,
57
- },
58
- path: {
59
- type: "string",
60
- description: "Subdirectory within repo to deploy (for monorepos)",
61
- required: false,
62
- },
63
- },
64
- async run({ args }) {
65
- const jsonMode = resolveJsonMode(args);
66
- // --- Demo deploy (zero-friction showcase) ---
67
- if (args.demo) {
68
- return await deployDemo(jsonMode);
69
- }
70
- // --- Ensure ToS accepted (all non-demo paths) ---
71
- const autoConfirm = shouldAutoConfirm(args);
72
- const tos = await ensureTosAccepted(autoConfirm);
73
- // --- Template deploy ---
74
- if (args.template) {
75
- return await deployTemplate(args.template);
76
- }
77
- // --- Repo URL deploy (creek deploy https://github.com/user/repo) ---
78
- if (args.dir && isRepoUrl(args.dir)) {
79
- return await deployRepoUrl(args.dir, {
80
- path: args.path ?? null,
81
- skipBuild: args["skip-build"],
82
- json: jsonMode,
83
- });
84
- }
85
- // --- Resolve target directory ---
86
- const cwd = args.dir ? resolve(args.dir) : process.cwd();
87
- const token = getToken();
88
- // --- Explicit directory (creek deploy ./dist) ---
89
- if (args.dir) {
90
- if (!existsSync(cwd)) {
91
- if (jsonMode)
92
- jsonOutput({ error: "not_found", message: `Directory not found: ${args.dir}` }, 1);
93
- consola.error(`Directory not found: ${args.dir}`);
94
- process.exit(1);
95
- }
96
- return await deployDirectory(cwd, jsonMode, tos);
97
- }
98
- // --- Try auto-detection (creek.toml → wrangler.* → package.json → index.html) ---
99
- let resolved = null;
100
- try {
101
- resolved = resolveConfig(cwd);
102
- }
103
- catch (err) {
104
- if (!(err instanceof ConfigNotFoundError))
105
- throw err;
106
- }
107
- if (resolved) {
108
- if (!jsonMode) {
109
- consola.info(` Detected: ${formatDetectionSummary(resolved)}`);
110
- for (const ub of resolved.unsupportedBindings) {
111
- consola.warn(` Binding '${ub.name}' (${ub.type}) is not yet supported — will be skipped`);
112
- }
113
- }
114
- if (token) {
115
- return await deployAuthenticated(cwd, resolved, token, args["skip-build"], jsonMode);
116
- }
117
- return await deploySandbox(cwd, args["skip-build"], jsonMode, resolved, tos);
118
- }
119
- // --- Auto-detect build output dirs ---
120
- for (const dir of ["dist", "build", "out", ".output/public"]) {
121
- const fullPath = resolve(cwd, dir);
122
- if (existsSync(fullPath)) {
123
- if (!jsonMode)
124
- consola.info(`Found build output: ${dir}/`);
125
- return await deployDirectory(fullPath, jsonMode, tos);
126
- }
127
- }
128
- // --- Nothing found → guide the user ---
129
- if (jsonMode)
130
- jsonOutput({ error: "no_project", message: "No project found in this directory" }, 1);
131
- consola.info("No project found in this directory.\n");
132
- consola.info(" creek deploy ./dist Deploy a build output directory");
133
- consola.info(" creek deploy --demo Deploy a sample site (see Creek in 5 seconds)");
134
- consola.info("");
135
- consola.info("Or create a project first:");
136
- consola.info(" npm create vite@latest my-app && cd my-app && npx creek deploy");
137
- process.exit(1);
138
- },
139
- });
140
- async function deployRepoUrl(input, options) {
141
- const { path: subpath, skipBuild, json: jsonMode } = options;
142
- try {
143
- // 1. Parse and validate URL
144
- const parsed = parseRepoUrl(input);
145
- validateRepoUrl(parsed);
146
- if (subpath)
147
- validateSubpath(subpath);
148
- // 2. Check git is available
149
- checkGitInstalled();
150
- // 3. Clone
151
- section("Clone");
152
- consola.start(` Cloning ${parsed.displayUrl}...`);
153
- const { tmpDir, workDir, sizeMb } = cloneRepo(parsed, { subpath });
154
- consola.success(` Cloned (${sizeMb.toFixed(1)} MB)`);
155
- try {
156
- // 4. Resolve config
157
- section("Detect");
158
- let resolved;
159
- try {
160
- resolved = resolveConfig(workDir);
161
- }
162
- catch (err) {
163
- if (err instanceof ConfigNotFoundError) {
164
- consola.error(`No supported project found in ${parsed.displayUrl}.`);
165
- consola.info("");
166
- consola.info(" If this is a monorepo, specify a subdirectory:");
167
- consola.info(` creek deploy ${input} --path packages/app`);
168
- consola.info("");
169
- consola.info(" Creek looks for: creek.toml, wrangler.*, package.json, or index.html");
170
- process.exit(1);
171
- }
172
- throw err;
173
- }
174
- consola.info(` Detected: ${formatDetectionSummary(resolved)}`);
175
- for (const ub of resolved.unsupportedBindings) {
176
- consola.warn(` Binding '${ub.name}' (${ub.type}) is not yet supported — will be skipped`);
177
- }
178
- // 5. Install dependencies (if package.json exists)
179
- if (existsSync(join(workDir, "package.json"))) {
180
- section("Install");
181
- const pm = detectPackageManager(workDir);
182
- consola.start(` Installing dependencies (${pm})...`);
183
- installDependencies(workDir, pm);
184
- consola.success(` Dependencies installed`);
185
- }
186
- // 6. Route to authenticated or sandbox deploy
187
- const token = getToken();
188
- if (token) {
189
- return await deployAuthenticated(workDir, resolved, token, skipBuild, jsonMode);
190
- }
191
- return await deploySandbox(workDir, skipBuild, jsonMode, resolved);
192
- }
193
- finally {
194
- cleanupCloneDir(tmpDir);
195
- }
196
- }
197
- catch (err) {
198
- if (err instanceof RepoUrlError || err instanceof GitCloneError) {
199
- if (jsonMode)
200
- jsonOutput({ error: "repo_deploy_failed", message: err.message }, 1);
201
- consola.error(err.message);
202
- process.exit(1);
203
- }
204
- throw err;
205
- }
206
- }
207
- // ============================================================================
208
- // Demo deploy — pre-built page, zero dependencies, instant
209
- // ============================================================================
210
- const DEMO_HTML = `<!DOCTYPE html>
211
- <html lang="en">
212
- <head>
213
- <meta charset="utf-8">
214
- <meta name="viewport" content="width=device-width, initial-scale=1">
215
- <title>Creek Deploy Demo</title>
216
- <style>
217
- *{margin:0;padding:0;box-sizing:border-box}
218
- body{font-family:system-ui,-apple-system,sans-serif;background:#0a0a0a;color:#f5f5f5;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center}
219
- .card{max-width:480px;text-align:center;padding:3rem 2rem}
220
- h1{font-size:2rem;font-weight:700;letter-spacing:-0.03em;margin-bottom:0.5rem}
221
- .accent{background:linear-gradient(135deg,#38bdf8,#818cf8);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
222
- p{color:#888;line-height:1.6;margin-top:1rem}
223
- .meta{margin-top:2rem;font-size:0.85rem;color:#555}
224
- .cta{display:inline-block;margin-top:1.5rem;background:linear-gradient(135deg,#2563eb,#3b82f6);color:#fff;padding:10px 24px;border-radius:8px;text-decoration:none;font-weight:600;font-size:0.9rem;transition:opacity 0.15s}
225
- .cta:hover{opacity:0.9}
226
- code{background:#1a1a2e;padding:2px 8px;border-radius:4px;font-size:0.85rem;color:#a5b4fc}
227
- .pulse{width:12px;height:12px;background:#22c55e;border-radius:50%;display:inline-block;margin-right:8px;animation:pulse 2s infinite}
228
- @keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}
229
- </style>
230
- </head>
231
- <body>
232
- <div class="card">
233
- <h1><span class="accent">Creek</span> is live.</h1>
234
- <p>This site was deployed to the edge in seconds — no build step, no config, no account.</p>
235
- <p style="margin-top:1.5rem"><span class="pulse"></span>Running on Cloudflare's global network</p>
236
- <p class="meta">Now try it with your own project:</p>
237
- <p style="margin-top:0.5rem"><code>cd your-project && npx creek deploy</code></p>
238
- <a href="https://creek.dev" class="cta">Learn more about Creek</a>
239
- </div>
240
- <script>document.title="Creek Deploy Demo — "+new Date().toLocaleTimeString()</script>
241
- </body>
242
- </html>`;
243
- async function deployDemo(jsonMode) {
244
- if (!jsonMode)
245
- consola.start("Deploying demo site...");
246
- try {
247
- const result = await sandboxDeploy({
248
- assets: { "index.html": Buffer.from(DEMO_HTML).toString("base64") },
249
- source: "cli-demo",
250
- });
251
- if (!jsonMode)
252
- consola.start("Waiting for deployment...");
253
- const status = await pollSandboxStatus(result.statusUrl);
254
- if (jsonMode) {
255
- jsonOutput({
256
- ok: true,
257
- sandboxId: result.sandboxId,
258
- url: status.previewUrl,
259
- deployDurationMs: status.deployDurationMs,
260
- expiresAt: result.expiresAt,
261
- mode: "demo",
262
- });
263
- }
264
- const duration = status.deployDurationMs
265
- ? `in ${(status.deployDurationMs / 1000).toFixed(1)}s`
266
- : "in seconds";
267
- consola.success(`Live ${duration} → ${status.previewUrl}`);
268
- consola.info("");
269
- consola.info("That's Creek. Now deploy your own project:");
270
- consola.info(" cd your-project && npx creek deploy");
271
- }
272
- catch (err) {
273
- const message = err instanceof Error ? err.message : "Demo deploy failed";
274
- if (jsonMode)
275
- jsonOutput({ ok: false, error: "deploy_failed", message }, 1);
276
- consola.error(message);
277
- process.exit(1);
278
- }
279
- }
280
- // ============================================================================
281
- // Directory deploy — deploy pre-built static files directly
282
- // ============================================================================
283
- async function deployDirectory(dir, jsonMode, tos) {
284
- if (!jsonMode)
285
- section("Upload");
286
- const { assets, fileList } = collectAssets(dir);
287
- if (fileList.length === 0) {
288
- if (jsonMode)
289
- jsonOutput({ ok: false, error: "no_files", message: `No files found in ${dir}` }, 1);
290
- consola.error(`No files found in ${dir}\n`);
291
- consola.info(" creek deploy ./dist Deploy a build output directory");
292
- consola.info(" creek deploy --demo Deploy a sample site (see Creek in 5 seconds)");
293
- process.exit(1);
294
- }
295
- if (!jsonMode) {
296
- consola.info(` ${fileList.length} files (${assetSummary(fileList)})`);
297
- consola.info(" Mode: sandbox (60 min preview)");
298
- section("Deploy");
299
- consola.start(" Deploying to edge...");
300
- }
301
- try {
302
- const result = await sandboxDeploy({ assets, source: "cli" }, { tos });
303
- const status = await pollSandboxStatus(result.statusUrl);
304
- if (jsonMode) {
305
- jsonOutput({
306
- ok: true,
307
- sandboxId: result.sandboxId,
308
- url: status.previewUrl,
309
- deployDurationMs: status.deployDurationMs,
310
- expiresAt: result.expiresAt,
311
- assetCount: fileList.length,
312
- mode: "sandbox",
313
- });
314
- }
315
- printSandboxSuccess(status.previewUrl, result.expiresAt, result.sandboxId);
316
- }
317
- catch (err) {
318
- const message = err instanceof Error ? err.message : "Deploy failed";
319
- if (jsonMode)
320
- jsonOutput({ ok: false, error: "deploy_failed", message }, 1);
321
- consola.error(message);
322
- process.exit(1);
323
- }
324
- }
325
- // ============================================================================
326
- // Sandbox deploy — auto-detect framework, build, deploy
327
- // ============================================================================
328
- async function deploySandbox(cwd, skipBuild, jsonMode = false, resolved, tos) {
329
- const framework = resolved?.framework ?? detectFramework(JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8")));
330
- // Detect Next.js mode (static vs opennext SSR)
331
- const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
332
- const nextjsMode = framework === "nextjs" ? detectNextjsMode(pkg, cwd) : null;
333
- const monorepo = detectMonorepo(cwd);
334
- section("Detect");
335
- consola.info(` Framework: ${framework ?? "static site"}`);
336
- if (nextjsMode)
337
- consola.info(` Next.js mode: ${nextjsMode}${monorepo.isMonorepo ? " (monorepo)" : ""}`);
338
- consola.info(" Mode: sandbox (60 min preview)");
339
- // Build
340
- if (!skipBuild) {
341
- section("Build");
342
- if (nextjsMode === "opennext") {
343
- // Next.js SSR on CF Workers: adapter (>= 16.2) or legacy opennext
344
- try {
345
- buildNextjs(cwd, monorepo.isMonorepo);
346
- }
347
- catch {
348
- consola.error("Next.js build failed");
349
- process.exit(1);
350
- }
351
- consola.success(" Build complete");
352
- }
353
- else {
354
- const buildCommand = resolved?.buildCommand || "npm run build";
355
- if (!buildCommand) {
356
- consola.error("No build script found in package.json.");
357
- consola.info("Add a 'build' script or use --skip-build if already built.");
358
- process.exit(1);
359
- }
360
- consola.start(` ${buildCommand}`);
361
- try {
362
- execSync(buildCommand, { cwd, stdio: "inherit" });
363
- }
364
- catch {
365
- consola.error("Build failed");
366
- consola.info("");
367
- consola.info(" Common fixes:");
368
- consola.info(" npm install (missing dependencies?)");
369
- consola.info(" Check for TypeScript errors");
370
- process.exit(1);
371
- }
372
- consola.success(" Build complete");
373
- }
374
- }
375
- const useAdapterOutput = framework === "nextjs" && hasAdapterOutput(cwd);
376
- const outputDir = useAdapterOutput
377
- ? resolve(cwd, ".creek/adapter-output")
378
- : resolve(cwd, resolved?.buildOutput ?? getDefaultBuildOutput(framework));
379
- if (!existsSync(outputDir)) {
380
- consola.error(`Build output not found: ${outputDir}`);
381
- if (framework) {
382
- consola.info(`Expected output for ${framework}: ${getDefaultBuildOutput(framework)}`);
383
- }
384
- process.exit(1);
385
- }
386
- // Collect assets
387
- const isSSR = isSSRFramework(framework);
388
- const renderMode = isSSR ? "ssr" : "spa";
389
- let clientAssetsDir;
390
- if (useAdapterOutput) {
391
- clientAssetsDir = resolve(outputDir, "assets");
392
- }
393
- else {
394
- clientAssetsDir = outputDir;
395
- if (isSSR && framework) {
396
- const subdir = getClientAssetsDir(framework);
397
- if (subdir)
398
- clientAssetsDir = resolve(outputDir, subdir);
399
- }
400
- }
401
- section("Upload");
402
- const { assets: clientAssets, fileList } = collectAssets(clientAssetsDir);
403
- consola.info(` ${fileList.length} assets (${assetSummary(fileList)})`);
404
- let serverFiles;
405
- if (isSSR && framework) {
406
- const serverEntry = getSSRServerEntry(framework);
407
- if (serverEntry) {
408
- const serverEntryPath = resolve(outputDir, serverEntry);
409
- if (existsSync(serverEntryPath)) {
410
- consola.start(" Bundling SSR server...");
411
- const bundled = await bundleSSRServer(serverEntryPath);
412
- serverFiles = { "server.js": Buffer.from(bundled).toString("base64") };
413
- consola.success(` SSR bundled (${Math.round(bundled.length / 1024)}KB)`);
414
- }
415
- }
416
- }
417
- // Deploy to sandbox
418
- if (!jsonMode) {
419
- section("Deploy");
420
- consola.start(" Deploying to edge...");
421
- }
422
- try {
423
- const result = await sandboxDeploy({
424
- assets: clientAssets,
425
- serverFiles,
426
- framework: framework ?? undefined,
427
- source: "cli",
428
- }, { tos });
429
- const status = await pollSandboxStatus(result.statusUrl);
430
- if (jsonMode) {
431
- jsonOutput({
432
- ok: true,
433
- sandboxId: result.sandboxId,
434
- url: status.previewUrl,
435
- deployDurationMs: status.deployDurationMs,
436
- expiresAt: result.expiresAt,
437
- framework: framework ?? null,
438
- assetCount: fileList.length,
439
- mode: "sandbox",
440
- });
441
- }
442
- printSandboxSuccess(status.previewUrl, result.expiresAt, result.sandboxId);
443
- }
444
- catch (err) {
445
- const message = err instanceof Error ? err.message : "Sandbox deploy failed";
446
- if (jsonMode)
447
- jsonOutput({ ok: false, error: "deploy_failed", message }, 1);
448
- consola.error(message);
449
- process.exit(1);
450
- }
451
- }
452
- // ============================================================================
453
- // Template deploy — clone + build + deploy to sandbox
454
- // ============================================================================
455
- async function deployTemplate(templateId) {
456
- // Validate template ID — alphanumeric, hyphens, underscores only (no path traversal)
457
- if (!/^[a-zA-Z0-9_-]+$/.test(templateId)) {
458
- consola.error("Invalid template name. Use only letters, numbers, hyphens, and underscores.");
459
- process.exit(1);
460
- }
461
- consola.info(`Deploying template: ${templateId}`);
462
- // Clone template to temp dir
463
- const tmpDir = join(process.env.TMPDIR ?? "/tmp", `creek-template-${Date.now()}`);
464
- const repoUrl = "https://github.com/solcreek/templates";
465
- consola.start("Cloning template...");
466
- try {
467
- execFileSync("git", [
468
- "clone", "--depth", "1", "--filter=blob:none", "--sparse", repoUrl, tmpDir,
469
- ], { stdio: "pipe" });
470
- execFileSync("git", [
471
- "sparse-checkout", "set", templateId,
472
- ], { cwd: tmpDir, stdio: "pipe" });
473
- }
474
- catch {
475
- consola.error(`Template '${templateId}' not found.`);
476
- consola.info("Available templates: creek deploy --template");
477
- cleanupDir(tmpDir);
478
- process.exit(1);
479
- }
480
- const templateDir = join(tmpDir, templateId);
481
- // Verify resolved path is still within tmpDir (prevent path traversal)
482
- if (!resolve(templateDir).startsWith(resolve(tmpDir))) {
483
- consola.error("Invalid template path.");
484
- cleanupDir(tmpDir);
485
- process.exit(1);
486
- }
487
- if (!existsSync(templateDir)) {
488
- consola.error(`Template directory not found: ${templateId}`);
489
- cleanupDir(tmpDir);
490
- process.exit(1);
491
- }
492
- consola.start("Installing dependencies...");
493
- try {
494
- execFileSync("npm", ["install"], { cwd: templateDir, stdio: "pipe" });
495
- }
496
- catch {
497
- consola.error("Failed to install dependencies");
498
- cleanupDir(tmpDir);
499
- process.exit(1);
500
- }
501
- // Deploy as sandbox (reuse sandbox flow)
502
- await deploySandbox(templateDir, false);
503
- // Cleanup
504
- cleanupDir(tmpDir);
505
- }
506
- function cleanupDir(dir) {
507
- try {
508
- rmSync(dir, { recursive: true, force: true });
509
- }
510
- catch {
511
- // ignore
512
- }
513
- }
514
- // ============================================================================
515
- // Authenticated deploy — existing flow
516
- // ============================================================================
517
- async function deployAuthenticated(cwd, resolved, token, skipBuild, jsonMode = false) {
518
- try {
519
- const client = new CreekClient(getApiUrl(), token);
520
- section("Auth");
521
- const session = await client.getSession();
522
- if (!session?.user) {
523
- consola.error("Token is invalid or expired. Run `creek login` to re-authenticate.");
524
- process.exit(1);
525
- }
526
- consola.info(` Deploying as ${session.user.email}`);
527
- consola.info(` Project: ${resolved.projectName}`);
528
- // Ensure project exists — confirm before auto-creating
529
- let project;
530
- try {
531
- project = await client.getProject(resolved.projectName);
532
- }
533
- catch {
534
- if (!jsonMode && isTTY) {
535
- const confirm = await consola.prompt(`Project "${resolved.projectName}" does not exist. Create it?`, { type: "confirm" });
536
- if (!confirm) {
537
- consola.info("Deploy cancelled.");
538
- process.exit(0);
539
- }
540
- }
541
- const res = await client.createProject({
542
- slug: resolved.projectName,
543
- framework: resolved.framework ?? undefined,
544
- });
545
- project = res.project;
546
- if (!jsonMode)
547
- consola.success(` Created project: ${project.slug}`);
548
- }
549
- // Determine deploy mode
550
- const framework = resolved.framework;
551
- const isSSR = isSSRFramework(framework);
552
- const isWorker = !framework && !!resolved.workerEntry;
553
- const renderMode = isWorker ? "worker" : (isSSR ? "ssr" : "spa");
554
- // Detect Next.js mode for special build handling
555
- const pkg = existsSync(join(cwd, "package.json"))
556
- ? JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"))
557
- : {};
558
- const nextjsMode = framework === "nextjs" ? detectNextjsMode(pkg, cwd) : null;
559
- const monorepo = framework === "nextjs" ? detectMonorepo(cwd) : { isMonorepo: false, root: null };
560
- if (nextjsMode) {
561
- if (!jsonMode)
562
- consola.info(` Next.js mode: ${nextjsMode}${monorepo.isMonorepo ? " (monorepo)" : ""}`);
563
- }
564
- // Build (skip for pure Workers with no build command)
565
- if (!skipBuild && resolved.buildCommand) {
566
- section("Build");
567
- if (nextjsMode === "opennext") {
568
- try {
569
- buildNextjs(cwd, monorepo.isMonorepo);
570
- }
571
- catch {
572
- consola.error("Next.js build failed");
573
- process.exit(1);
574
- }
575
- consola.success(" Build complete");
576
- }
577
- else {
578
- const buildCmd = resolved.buildCommand;
579
- if (buildCmd.length > 500) {
580
- consola.error("Invalid build command (too long)");
581
- process.exit(1);
582
- }
583
- consola.start(` ${buildCmd}`);
584
- try {
585
- execSync(buildCmd, { cwd, stdio: "inherit" });
586
- }
587
- catch {
588
- consola.error("Build failed");
589
- process.exit(1);
590
- }
591
- consola.success(" Build complete");
592
- }
593
- }
594
- // Collect client assets (skip for pure Workers — no client assets)
595
- let clientAssets = {};
596
- let fileList = [];
597
- if (!isWorker) {
598
- let clientAssetsDir;
599
- // Adapter output takes precedence for Next.js
600
- if (framework === "nextjs" && hasAdapterOutput(cwd)) {
601
- clientAssetsDir = resolve(cwd, ".creek/adapter-output/assets");
602
- }
603
- else {
604
- const outputDir = resolve(cwd, resolved.buildOutput);
605
- if (!existsSync(outputDir)) {
606
- consola.error(`Build output directory not found: ${resolved.buildOutput}`);
607
- process.exit(1);
608
- }
609
- clientAssetsDir = outputDir;
610
- if (isSSR && framework) {
611
- const clientSubdir = getClientAssetsDir(framework);
612
- if (clientSubdir) {
613
- clientAssetsDir = resolve(outputDir, clientSubdir);
614
- }
615
- }
616
- }
617
- section("Upload");
618
- ({ assets: clientAssets, fileList } = collectAssets(clientAssetsDir));
619
- consola.info(` ${fileList.length} assets (${assetSummary(fileList)})`);
620
- }
621
- // Bundle server/worker files
622
- let serverFiles;
623
- if (isSSR && framework) {
624
- if (framework === "nextjs" && hasAdapterOutput(cwd)) {
625
- // Adapter path: read pre-bundled output from .creek/adapter-output/
626
- const adapterServerDir = resolve(cwd, ".creek/adapter-output/server");
627
- consola.start(" Collecting adapter output...");
628
- const collected = {};
629
- if (existsSync(adapterServerDir)) {
630
- for (const f of readdirSync(adapterServerDir)) {
631
- const fp = join(adapterServerDir, f);
632
- if (!statSync(fp).isFile())
633
- continue;
634
- if (f.endsWith(".map"))
635
- continue;
636
- collected[f] = readFileSync(fp);
637
- }
638
- }
639
- const fileCount = Object.keys(collected).length;
640
- serverFiles = Object.fromEntries(Object.entries(collected).map(([p, buf]) => [p, buf.toString("base64")]));
641
- consola.success(` Worker bundled: ${fileCount} files (${Math.round(Object.values(collected).reduce((s, b) => s + b.length, 0) / 1024)}KB)`);
642
- }
643
- else if (framework === "nextjs") {
644
- // Legacy path: use wrangler to produce a single bundled worker
645
- const bundleDir = resolve(cwd, ".creek/bundled");
646
- consola.start(" Bundling Next.js worker (legacy)...");
647
- execSync(`npx wrangler deploy --dry-run --outdir "${bundleDir}"`, { cwd, stdio: "pipe" });
648
- patchBundledWorker(bundleDir, resolve(cwd, ".open-next"));
649
- const collected = {};
650
- if (existsSync(bundleDir)) {
651
- for (const f of readdirSync(bundleDir)) {
652
- const fp = join(bundleDir, f);
653
- if (!statSync(fp).isFile())
654
- continue;
655
- if (f.endsWith(".map") || f === "README.md")
656
- continue;
657
- collected[f] = readFileSync(fp);
658
- }
659
- }
660
- const fileCount = Object.keys(collected).length;
661
- serverFiles = Object.fromEntries(Object.entries(collected).map(([p, buf]) => [p, buf.toString("base64")]));
662
- consola.success(` Worker bundled: ${fileCount} files (${Math.round(Object.values(collected).reduce((s, b) => s + b.length, 0) / 1024)}KB)`);
663
- }
664
- else if (isPreBundledFramework(framework)) {
665
- // Other pre-bundled SSR frameworks (Nuxt, SvelteKit, etc.)
666
- const serverDirRel = getSSRServerDir(framework);
667
- if (serverDirRel) {
668
- const serverDir = resolve(cwd, serverDirRel);
669
- if (existsSync(serverDir)) {
670
- consola.start(" Collecting SSR server files...");
671
- const collected = collectServerFiles(serverDir);
672
- const fileCount = Object.keys(collected).length;
673
- serverFiles = Object.fromEntries(Object.entries(collected).map(([p, buf]) => [p, buf.toString("base64")]));
674
- consola.success(` SSR server: ${fileCount} files`);
675
- }
676
- }
677
- }
678
- else {
679
- // Non-pre-bundled SSR: esbuild single-file bundle (fallback)
680
- const outputDir = resolve(cwd, resolved.buildOutput);
681
- const serverEntry = getSSRServerEntry(framework);
682
- if (serverEntry) {
683
- const serverEntryPath = resolve(outputDir, serverEntry);
684
- if (existsSync(serverEntryPath)) {
685
- consola.start(" Bundling SSR server...");
686
- const bundled = await bundleSSRServer(serverEntryPath);
687
- serverFiles = {
688
- "server.js": Buffer.from(bundled).toString("base64"),
689
- };
690
- consola.success(` SSR bundled (${Math.round(bundled.length / 1024)}KB)`);
691
- }
692
- }
693
- }
694
- }
695
- else if (isWorker && resolved.workerEntry) {
696
- // Pure Worker: bundle the worker entry point
697
- const workerEntryPath = resolve(cwd, resolved.workerEntry);
698
- if (existsSync(workerEntryPath)) {
699
- section("Bundle");
700
- consola.start(" Bundling worker...");
701
- const bundled = await bundleSSRServer(workerEntryPath);
702
- serverFiles = {
703
- "worker.js": Buffer.from(bundled).toString("base64"),
704
- };
705
- consola.success(` Worker bundled (${Math.round(bundled.length / 1024)}KB)`);
706
- }
707
- else {
708
- consola.error(`Worker entry not found: ${resolved.workerEntry}`);
709
- process.exit(1);
710
- }
711
- }
712
- section("Deploy");
713
- consola.start(" Creating deployment...");
714
- const { deployment } = await client.createDeployment(project.id);
715
- consola.start(" Uploading bundle...");
716
- const bundle = {
717
- manifest: {
718
- assets: isWorker ? [] : fileList,
719
- hasWorker: isSSR || isWorker,
720
- entrypoint: resolved.workerEntry,
721
- renderMode,
722
- framework: framework ?? undefined,
723
- },
724
- workerScript: null,
725
- assets: isWorker ? {} : clientAssets,
726
- serverFiles,
727
- // Backward compat: boolean flags
728
- resources: resolvedConfigToResources(resolved),
729
- // New: binding declarations with user-defined names
730
- bindings: resolvedConfigToBindingRequirements(resolved),
731
- // Pass through wrangler vars and compat settings
732
- ...(Object.keys(resolved.vars).length > 0 ? { vars: resolved.vars } : {}),
733
- ...(resolved.compatibilityDate ? { compatibilityDate: resolved.compatibilityDate } : {}),
734
- ...(resolved.compatibilityFlags.length > 0 ? { compatibilityFlags: resolved.compatibilityFlags } : {}),
735
- };
736
- await client.uploadDeploymentBundle(project.id, deployment.id, bundle);
737
- // Poll for async deploy progress
738
- const POLL_INTERVAL = 1000;
739
- const POLL_TIMEOUT = 120_000;
740
- const TERMINAL = new Set(["active", "failed", "cancelled"]);
741
- const STEP_LABELS = {
742
- queued: " Waiting...",
743
- uploading: " Uploading...",
744
- provisioning: " Provisioning resources...",
745
- deploying: " Deploying to edge...",
746
- };
747
- let lastStatus = "";
748
- const start = Date.now();
749
- while (Date.now() - start < POLL_TIMEOUT) {
750
- const res = await client.getDeploymentStatus(project.id, deployment.id);
751
- const { status, failed_step, error_message } = res.deployment;
752
- if (status !== lastStatus) {
753
- if (lastStatus && STEP_LABELS[lastStatus]) {
754
- consola.success(STEP_LABELS[lastStatus].replace("...", ""));
755
- }
756
- if (!TERMINAL.has(status) && STEP_LABELS[status]) {
757
- consola.start(STEP_LABELS[status]);
758
- }
759
- lastStatus = status;
760
- }
761
- if (status === "active") {
762
- if (jsonMode) {
763
- jsonOutput({
764
- ok: true,
765
- url: res.url ?? res.previewUrl,
766
- previewUrl: res.previewUrl,
767
- deploymentId: deployment.id,
768
- project: project.slug,
769
- mode: "production",
770
- });
771
- }
772
- consola.success(` Deployed! ${res.url ?? res.previewUrl}`);
773
- if (res.url && res.previewUrl) {
774
- consola.info(` Preview: ${res.previewUrl}`);
775
- }
776
- return;
777
- }
778
- if (status === "failed") {
779
- const step = failed_step ? ` at ${failed_step}` : "";
780
- const msg = error_message ?? "Unknown error";
781
- if (jsonMode)
782
- jsonOutput({ ok: false, error: "deploy_failed", message: msg, failedStep: failed_step }, 1);
783
- consola.error(`Deploy failed${step}: ${msg}`);
784
- process.exit(1);
785
- }
786
- if (status === "cancelled") {
787
- if (jsonMode)
788
- jsonOutput({ ok: false, error: "cancelled", message: "Deploy was cancelled" }, 1);
789
- consola.warn("Deploy was cancelled");
790
- process.exit(1);
791
- }
792
- await new Promise((r) => setTimeout(r, POLL_INTERVAL));
793
- }
794
- if (jsonMode)
795
- jsonOutput({ ok: false, error: "timeout", message: "Deploy timed out after 2 minutes" }, 1);
796
- consola.error("Deploy timed out after 2 minutes");
797
- process.exit(1);
798
- }
799
- catch (err) {
800
- if (err instanceof CreekAuthError) {
801
- consola.error("Authentication failed. Run `creek login` to re-authenticate.");
802
- process.exit(1);
803
- }
804
- throw err;
805
- }
806
- }
807
- //# sourceMappingURL=deploy.js.map