@bigcommerce/catalyst 1.0.0-alpha.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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/cli.js +930 -0
- package/dist/cli.js.map +1 -0
- package/package.json +53 -0
- package/templates/open-next.config.ts +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 BigCommerce
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,930 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/program.ts
|
|
4
|
+
import { Command as Command8 } from "commander";
|
|
5
|
+
import { colorize as colorize2 } from "consola/utils";
|
|
6
|
+
import { config } from "dotenv";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
|
|
9
|
+
// package.json
|
|
10
|
+
var package_default = {
|
|
11
|
+
name: "@bigcommerce/catalyst",
|
|
12
|
+
version: "1.0.0-alpha.0",
|
|
13
|
+
type: "module",
|
|
14
|
+
bin: {
|
|
15
|
+
catalyst: "dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
files: [
|
|
18
|
+
"dist",
|
|
19
|
+
"templates"
|
|
20
|
+
],
|
|
21
|
+
scripts: {
|
|
22
|
+
dev: "tsup --watch",
|
|
23
|
+
typecheck: "tsc --noEmit",
|
|
24
|
+
lint: "eslint . --max-warnings 0",
|
|
25
|
+
test: "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
build: "tsup"
|
|
29
|
+
},
|
|
30
|
+
engines: {
|
|
31
|
+
node: "^20.0.0 || ^22.0.0"
|
|
32
|
+
},
|
|
33
|
+
dependencies: {
|
|
34
|
+
"@segment/analytics-node": "^2.2.1",
|
|
35
|
+
"adm-zip": "^0.5.16",
|
|
36
|
+
commander: "^14.0.0",
|
|
37
|
+
conf: "^13.1.0",
|
|
38
|
+
consola: "^3.4.2",
|
|
39
|
+
dotenv: "^16.5.0",
|
|
40
|
+
execa: "^9.6.0",
|
|
41
|
+
"yocto-spinner": "^1.0.0",
|
|
42
|
+
zod: "^4.0.5"
|
|
43
|
+
},
|
|
44
|
+
devDependencies: {
|
|
45
|
+
"@bigcommerce/eslint-config": "^2.11.0",
|
|
46
|
+
"@bigcommerce/eslint-config-catalyst": "workspace:^",
|
|
47
|
+
"@commander-js/extra-typings": "^14.0.0",
|
|
48
|
+
"@types/adm-zip": "^0.5.7",
|
|
49
|
+
"@types/node": "^22.15.30",
|
|
50
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
51
|
+
"@vitest/ui": "^3.2.4",
|
|
52
|
+
eslint: "^8.57.1",
|
|
53
|
+
msw: "^2.9.0",
|
|
54
|
+
prettier: "^3.6.2",
|
|
55
|
+
tsup: "^8.5.0",
|
|
56
|
+
typescript: "^5.8.3",
|
|
57
|
+
vitest: "^3.2.4"
|
|
58
|
+
},
|
|
59
|
+
peerDependencies: {
|
|
60
|
+
"@opennextjs/cloudflare": "^1.8.0"
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/cli/commands/build.ts
|
|
65
|
+
import { Command, Option } from "commander";
|
|
66
|
+
import { execa } from "execa";
|
|
67
|
+
import { existsSync } from "fs";
|
|
68
|
+
import { copyFile, cp, writeFile } from "fs/promises";
|
|
69
|
+
import { join as join3 } from "path";
|
|
70
|
+
|
|
71
|
+
// src/cli/lib/get-module-cli-path.ts
|
|
72
|
+
import { dirname, join } from "path";
|
|
73
|
+
import { fileURLToPath } from "url";
|
|
74
|
+
function getModuleCliPath() {
|
|
75
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
76
|
+
const __dirname = dirname(__filename);
|
|
77
|
+
return join(__dirname, "..");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/cli/lib/logger.ts
|
|
81
|
+
import { createConsola } from "consola";
|
|
82
|
+
var consola = createConsola({
|
|
83
|
+
level: "3" ? parseInt("3", 10) : 3
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// src/cli/lib/project-config.ts
|
|
87
|
+
import Conf from "conf";
|
|
88
|
+
import { join as join2 } from "path";
|
|
89
|
+
function getProjectConfig(rootDir = process.cwd()) {
|
|
90
|
+
return new Conf({
|
|
91
|
+
cwd: join2(rootDir, ".bigcommerce"),
|
|
92
|
+
projectSuffix: "",
|
|
93
|
+
configName: "project",
|
|
94
|
+
schema: {
|
|
95
|
+
projectUuid: { type: "string", format: "uuid" },
|
|
96
|
+
framework: {
|
|
97
|
+
type: "string",
|
|
98
|
+
enum: ["catalyst", "nextjs"],
|
|
99
|
+
default: "nextjs"
|
|
100
|
+
},
|
|
101
|
+
telemetry: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
enabled: { type: "boolean" },
|
|
105
|
+
anonymousId: { type: "string" }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/cli/lib/wrangler-config.ts
|
|
113
|
+
function getWranglerConfig(projectUuid, kvNamespaceId) {
|
|
114
|
+
return {
|
|
115
|
+
$schema: "node_modules/wrangler/config-schema.json",
|
|
116
|
+
main: "../.open-next/worker.js",
|
|
117
|
+
name: `project-${projectUuid}`,
|
|
118
|
+
compatibility_date: "2025-07-15",
|
|
119
|
+
compatibility_flags: ["nodejs_compat", "global_fetch_strictly_public"],
|
|
120
|
+
observability: {
|
|
121
|
+
enabled: true,
|
|
122
|
+
head_sampling_rate: 0.05,
|
|
123
|
+
logs: {
|
|
124
|
+
enabled: true,
|
|
125
|
+
head_sampling_rate: 1,
|
|
126
|
+
invocation_logs: false
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
assets: {
|
|
130
|
+
directory: "../.open-next/assets",
|
|
131
|
+
binding: "ASSETS"
|
|
132
|
+
},
|
|
133
|
+
services: [
|
|
134
|
+
{
|
|
135
|
+
binding: "WORKER_SELF_REFERENCE",
|
|
136
|
+
service: `project-${projectUuid}`
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
kv_namespaces: [
|
|
140
|
+
{
|
|
141
|
+
binding: "NEXT_INC_CACHE_KV",
|
|
142
|
+
id: kvNamespaceId
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
durable_objects: {
|
|
146
|
+
bindings: [
|
|
147
|
+
{
|
|
148
|
+
name: "NEXT_CACHE_DO_QUEUE",
|
|
149
|
+
class_name: "DOQueueHandler"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "NEXT_TAG_CACHE_DO_SHARDED",
|
|
153
|
+
class_name: "DOShardedTagCache"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "NEXT_CACHE_DO_PURGE",
|
|
157
|
+
class_name: "BucketCachePurge"
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
migrations: [
|
|
162
|
+
{
|
|
163
|
+
tag: "v1",
|
|
164
|
+
new_sqlite_classes: ["DOQueueHandler", "DOShardedTagCache", "BucketCachePurge"]
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/cli/commands/build.ts
|
|
171
|
+
var WRANGLER_VERSION = "4.24.3";
|
|
172
|
+
var build = new Command("build").allowUnknownOption().argument(
|
|
173
|
+
"[next-build-options...]",
|
|
174
|
+
"Next.js `build` options (see: https://nextjs.org/docs/app/api-reference/cli/next#next-build-options)"
|
|
175
|
+
).addOption(
|
|
176
|
+
new Option(
|
|
177
|
+
"--project-uuid <uuid>",
|
|
178
|
+
"Project UUID to be included in the deployment configuration."
|
|
179
|
+
).env("BIGCOMMERCE_PROJECT_UUID")
|
|
180
|
+
).addOption(
|
|
181
|
+
new Option("--framework <framework>", "The framework to use for the build.").choices([
|
|
182
|
+
"nextjs",
|
|
183
|
+
"catalyst"
|
|
184
|
+
])
|
|
185
|
+
).action(async (nextBuildOptions, options) => {
|
|
186
|
+
const coreDir = process.cwd();
|
|
187
|
+
try {
|
|
188
|
+
const config2 = getProjectConfig();
|
|
189
|
+
const framework = options.framework ?? config2.get("framework");
|
|
190
|
+
if (framework === "nextjs") {
|
|
191
|
+
const nextBin = join3("node_modules", ".bin", "next");
|
|
192
|
+
if (!existsSync(nextBin)) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Next.js is not installed in ${coreDir}. Are you in a valid Next.js project?`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
await execa(nextBin, ["build", ...nextBuildOptions], {
|
|
198
|
+
stdio: "inherit",
|
|
199
|
+
cwd: coreDir
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (framework === "catalyst") {
|
|
203
|
+
const openNextOutDir = join3(coreDir, ".open-next");
|
|
204
|
+
const bigcommerceDistDir = join3(coreDir, ".bigcommerce", "dist");
|
|
205
|
+
const projectUuid = options.projectUuid ?? config2.get("projectUuid");
|
|
206
|
+
if (!projectUuid) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
"Project UUID is required. Please run `link` or provide `--project-uuid`"
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const wranglerConfig = getWranglerConfig(projectUuid, "PLACEHOLDER_KV_ID");
|
|
212
|
+
consola.start("Copying templates...");
|
|
213
|
+
await copyFile(
|
|
214
|
+
join3(getModuleCliPath(), "templates", "open-next.config.ts"),
|
|
215
|
+
join3(coreDir, ".bigcommerce", "open-next.config.ts")
|
|
216
|
+
);
|
|
217
|
+
await writeFile(
|
|
218
|
+
join3(coreDir, ".bigcommerce", "wrangler.jsonc"),
|
|
219
|
+
JSON.stringify(wranglerConfig, null, 2)
|
|
220
|
+
);
|
|
221
|
+
consola.success("Templates copied");
|
|
222
|
+
consola.start("Building project...");
|
|
223
|
+
await execa(
|
|
224
|
+
"pnpm",
|
|
225
|
+
[
|
|
226
|
+
"exec",
|
|
227
|
+
"opennextjs-cloudflare",
|
|
228
|
+
"build",
|
|
229
|
+
"--skipWranglerConfigCheck",
|
|
230
|
+
"--openNextConfigPath",
|
|
231
|
+
join3(coreDir, ".bigcommerce", "open-next.config.ts")
|
|
232
|
+
],
|
|
233
|
+
{
|
|
234
|
+
stdout: ["pipe", "inherit"],
|
|
235
|
+
cwd: coreDir
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
await execa(
|
|
239
|
+
"pnpm",
|
|
240
|
+
[
|
|
241
|
+
"dlx",
|
|
242
|
+
`wrangler@${WRANGLER_VERSION}`,
|
|
243
|
+
"deploy",
|
|
244
|
+
"--config",
|
|
245
|
+
join3(coreDir, ".bigcommerce", "wrangler.jsonc"),
|
|
246
|
+
"--keep-vars",
|
|
247
|
+
"--outdir",
|
|
248
|
+
bigcommerceDistDir,
|
|
249
|
+
"--dry-run"
|
|
250
|
+
],
|
|
251
|
+
{
|
|
252
|
+
stdout: ["pipe", "inherit"],
|
|
253
|
+
cwd: coreDir
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
consola.success("Project built");
|
|
257
|
+
await cp(join3(openNextOutDir, "assets"), join3(bigcommerceDistDir, "assets"), {
|
|
258
|
+
recursive: true,
|
|
259
|
+
force: true
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
} catch (error) {
|
|
263
|
+
consola.error(error);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// src/cli/commands/deploy.ts
|
|
269
|
+
import AdmZip from "adm-zip";
|
|
270
|
+
import { Command as Command2, Option as Option2 } from "commander";
|
|
271
|
+
import { access, readdir, readFile } from "fs/promises";
|
|
272
|
+
import { join as join4 } from "path";
|
|
273
|
+
import yoctoSpinner from "yocto-spinner";
|
|
274
|
+
import { z } from "zod";
|
|
275
|
+
|
|
276
|
+
// src/cli/lib/telemetry.ts
|
|
277
|
+
import { Analytics } from "@segment/analytics-node";
|
|
278
|
+
import { randomBytes } from "crypto";
|
|
279
|
+
var TELEMETRY_KEY_ENABLED = "telemetry.enabled";
|
|
280
|
+
var TELEMETRY_KEY_ID = `telemetry.anonymousId`;
|
|
281
|
+
var Telemetry = class {
|
|
282
|
+
constructor() {
|
|
283
|
+
this.projectName = "catalyst-cli";
|
|
284
|
+
this.projectVersion = package_default.version;
|
|
285
|
+
this.setEnabled = (_enabled) => {
|
|
286
|
+
const enabled = Boolean(_enabled);
|
|
287
|
+
this.projectConfig.set("telemetry.enabled", enabled);
|
|
288
|
+
};
|
|
289
|
+
this.CATALYST_TELEMETRY_DISABLED = process.env.CATALYST_TELEMETRY_DISABLED;
|
|
290
|
+
this.projectConfig = getProjectConfig();
|
|
291
|
+
this.sessionId = randomBytes(32).toString("hex");
|
|
292
|
+
this.analytics = new Analytics({
|
|
293
|
+
writeKey: "OCkxLj3KTc1N3ebEbnFBqbhxfE3tVb3Z"
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
async track(eventName, payload) {
|
|
297
|
+
if (!this.isEnabled()) {
|
|
298
|
+
return Promise.resolve(void 0);
|
|
299
|
+
}
|
|
300
|
+
this.analytics.track({
|
|
301
|
+
event: eventName,
|
|
302
|
+
anonymousId: this.getAnonymousId(),
|
|
303
|
+
properties: {
|
|
304
|
+
...payload,
|
|
305
|
+
sessionId: this.sessionId
|
|
306
|
+
},
|
|
307
|
+
context: {
|
|
308
|
+
app: {
|
|
309
|
+
name: this.projectName,
|
|
310
|
+
version: this.projectVersion
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async identify(storeHash) {
|
|
316
|
+
if (!this.isEnabled()) {
|
|
317
|
+
return Promise.resolve(void 0);
|
|
318
|
+
}
|
|
319
|
+
if (!storeHash) {
|
|
320
|
+
return Promise.resolve(void 0);
|
|
321
|
+
}
|
|
322
|
+
this.analytics.identify({
|
|
323
|
+
userId: storeHash,
|
|
324
|
+
anonymousId: this.getAnonymousId(),
|
|
325
|
+
context: {
|
|
326
|
+
app: {
|
|
327
|
+
name: this.projectName,
|
|
328
|
+
version: this.projectVersion
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
isEnabled() {
|
|
334
|
+
return !this.CATALYST_TELEMETRY_DISABLED && this.projectConfig.get(TELEMETRY_KEY_ENABLED, true);
|
|
335
|
+
}
|
|
336
|
+
getAnonymousId() {
|
|
337
|
+
const val = this.projectConfig.get(TELEMETRY_KEY_ID);
|
|
338
|
+
if (val) {
|
|
339
|
+
return val;
|
|
340
|
+
}
|
|
341
|
+
const generated = randomBytes(32).toString("hex");
|
|
342
|
+
this.projectConfig.set(TELEMETRY_KEY_ID, generated);
|
|
343
|
+
return generated;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// src/cli/commands/deploy.ts
|
|
348
|
+
var telemetry = new Telemetry();
|
|
349
|
+
var stepsEnum = z.enum([
|
|
350
|
+
"initializing",
|
|
351
|
+
"downloading",
|
|
352
|
+
"unzipping",
|
|
353
|
+
"processing",
|
|
354
|
+
"deploying",
|
|
355
|
+
"finalizing",
|
|
356
|
+
"complete"
|
|
357
|
+
]);
|
|
358
|
+
var STEPS = {
|
|
359
|
+
initializing: "Initializing...",
|
|
360
|
+
downloading: "Downloading...",
|
|
361
|
+
unzipping: "Unzipping...",
|
|
362
|
+
processing: "Processing...",
|
|
363
|
+
deploying: "Deploying...",
|
|
364
|
+
finalizing: "Finalizing...",
|
|
365
|
+
complete: "Complete"
|
|
366
|
+
};
|
|
367
|
+
var UploadSignatureSchema = z.object({
|
|
368
|
+
data: z.object({
|
|
369
|
+
upload_url: z.url(),
|
|
370
|
+
upload_uuid: z.string()
|
|
371
|
+
})
|
|
372
|
+
});
|
|
373
|
+
var CreateDeploymentSchema = z.object({
|
|
374
|
+
data: z.object({
|
|
375
|
+
deployment_uuid: z.uuid()
|
|
376
|
+
})
|
|
377
|
+
});
|
|
378
|
+
var DeploymentStatusSchema = z.object({
|
|
379
|
+
deployment_uuid: z.uuid(),
|
|
380
|
+
deployment_status: z.enum(["queued", "in_progress", "failed", "completed"]),
|
|
381
|
+
event: z.object({
|
|
382
|
+
step: stepsEnum,
|
|
383
|
+
progress: z.number()
|
|
384
|
+
}).nullable(),
|
|
385
|
+
error: z.object({
|
|
386
|
+
code: z.number()
|
|
387
|
+
}).optional()
|
|
388
|
+
});
|
|
389
|
+
var generateBundleZip = async () => {
|
|
390
|
+
consola.info("Generating bundle...");
|
|
391
|
+
const bigcommerceDir = join4(process.cwd(), ".bigcommerce");
|
|
392
|
+
const distDir = join4(process.cwd(), ".bigcommerce", "dist");
|
|
393
|
+
try {
|
|
394
|
+
await access(distDir);
|
|
395
|
+
} catch {
|
|
396
|
+
throw new Error(`Dist directory not found: ${distDir}`);
|
|
397
|
+
}
|
|
398
|
+
const buildDirContents = await readdir(distDir);
|
|
399
|
+
if (buildDirContents.length === 0) {
|
|
400
|
+
throw new Error(`Dist directory is empty: ${distDir}`);
|
|
401
|
+
}
|
|
402
|
+
const outputZip = join4(bigcommerceDir, "bundle.zip");
|
|
403
|
+
const zip = new AdmZip();
|
|
404
|
+
zip.addLocalFolder(distDir, "output");
|
|
405
|
+
zip.writeZip(outputZip);
|
|
406
|
+
consola.success(`Bundle created at: ${outputZip}`);
|
|
407
|
+
};
|
|
408
|
+
var generateUploadSignature = async (storeHash, accessToken, apiHost) => {
|
|
409
|
+
consola.info("Generating upload signature...");
|
|
410
|
+
const response = await fetch(
|
|
411
|
+
`https://${apiHost}/stores/${storeHash}/v3/infrastructure/deployments/uploads`,
|
|
412
|
+
{
|
|
413
|
+
method: "POST",
|
|
414
|
+
headers: {
|
|
415
|
+
"X-Auth-Token": accessToken,
|
|
416
|
+
"Content-Type": "application/json",
|
|
417
|
+
Accept: "application/json"
|
|
418
|
+
},
|
|
419
|
+
body: JSON.stringify({})
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
if (!response.ok) {
|
|
423
|
+
throw new Error(`Failed to fetch upload signature: ${response.status} ${response.statusText}`);
|
|
424
|
+
}
|
|
425
|
+
const res = await response.json();
|
|
426
|
+
const { data } = UploadSignatureSchema.parse(res);
|
|
427
|
+
consola.success("Upload signature generated.");
|
|
428
|
+
return data;
|
|
429
|
+
};
|
|
430
|
+
var uploadBundleZip = async (uploadUrl) => {
|
|
431
|
+
consola.info("Uploading bundle...");
|
|
432
|
+
const zipPath = join4(process.cwd(), ".bigcommerce", "bundle.zip");
|
|
433
|
+
const fileBuffer = await readFile(zipPath);
|
|
434
|
+
const response = await fetch(uploadUrl, {
|
|
435
|
+
method: "PUT",
|
|
436
|
+
headers: {
|
|
437
|
+
"Content-Type": "application/zip"
|
|
438
|
+
},
|
|
439
|
+
body: fileBuffer
|
|
440
|
+
});
|
|
441
|
+
if (!response.ok) {
|
|
442
|
+
throw new Error(`Failed to upload bundle: ${response.status} ${response.statusText}`);
|
|
443
|
+
}
|
|
444
|
+
consola.success("Bundle uploaded successfully.");
|
|
445
|
+
return true;
|
|
446
|
+
};
|
|
447
|
+
var parseEnvironmentVariables = (secretOption) => {
|
|
448
|
+
return secretOption?.map((envVar) => {
|
|
449
|
+
const [key, value] = envVar.split("=");
|
|
450
|
+
if (!key || !value) {
|
|
451
|
+
throw new Error(`Invalid secret format: ${envVar}. Expected format: KEY=VALUE`);
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
type: "secret",
|
|
455
|
+
key: key.trim(),
|
|
456
|
+
value: value.trim()
|
|
457
|
+
};
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
var createDeployment = async (projectUuid, uploadUuid, storeHash, accessToken, apiHost, environmentVariables) => {
|
|
461
|
+
consola.info("Creating deployment...");
|
|
462
|
+
const response = await fetch(
|
|
463
|
+
`https://${apiHost}/stores/${storeHash}/v3/infrastructure/deployments`,
|
|
464
|
+
{
|
|
465
|
+
method: "POST",
|
|
466
|
+
headers: {
|
|
467
|
+
"X-Auth-Token": accessToken,
|
|
468
|
+
"Content-Type": "application/json",
|
|
469
|
+
Accept: "application/json"
|
|
470
|
+
},
|
|
471
|
+
body: JSON.stringify({
|
|
472
|
+
project_uuid: projectUuid,
|
|
473
|
+
upload_uuid: uploadUuid,
|
|
474
|
+
environment_variables: environmentVariables
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
if (!response.ok) {
|
|
479
|
+
throw new Error(`Failed to create deployment: ${response.status} ${response.statusText}`);
|
|
480
|
+
}
|
|
481
|
+
const res = await response.json();
|
|
482
|
+
const { data } = CreateDeploymentSchema.parse(res);
|
|
483
|
+
consola.success("Deployment started...");
|
|
484
|
+
return data;
|
|
485
|
+
};
|
|
486
|
+
var getDeploymentStatus = async (deploymentUuid, storeHash, accessToken, apiHost) => {
|
|
487
|
+
consola.info("Fetching deployment status...");
|
|
488
|
+
const spinner = yoctoSpinner().start("Fetching...");
|
|
489
|
+
const response = await fetch(
|
|
490
|
+
`https://${apiHost}/stores/${storeHash}/v3/infrastructure/deployments/${deploymentUuid}/events`,
|
|
491
|
+
{
|
|
492
|
+
method: "GET",
|
|
493
|
+
headers: {
|
|
494
|
+
"X-Auth-Token": accessToken,
|
|
495
|
+
Accept: "text/event-stream",
|
|
496
|
+
Connection: "keep-alive"
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
if (!response.ok) {
|
|
501
|
+
throw new Error(`Failed to open event stream: ${response.status} ${response.statusText}`);
|
|
502
|
+
}
|
|
503
|
+
const reader = response.body?.getReader();
|
|
504
|
+
if (!reader) {
|
|
505
|
+
throw new Error("Failed to read event stream.");
|
|
506
|
+
}
|
|
507
|
+
const decoder = new TextDecoder();
|
|
508
|
+
let done = false;
|
|
509
|
+
while (!done) {
|
|
510
|
+
const { value, done: streamDone } = await reader.read();
|
|
511
|
+
let json;
|
|
512
|
+
if (value) {
|
|
513
|
+
const chunk = decoder.decode(value, { stream: true }).trim();
|
|
514
|
+
const split = chunk.split("\n\n").map((s) => s.replace("data:", "").trim()).filter(Boolean);
|
|
515
|
+
split.forEach((event) => {
|
|
516
|
+
try {
|
|
517
|
+
json = JSON.parse(event);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
consola.warn(`Failed to parse event, dropping from stream. Event: ${event}`, error);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const data = DeploymentStatusSchema.parse(json);
|
|
523
|
+
if (data.error) {
|
|
524
|
+
throw new Error(`Deployment failed with error code: ${data.error.code}`);
|
|
525
|
+
}
|
|
526
|
+
if (data.event && STEPS[data.event.step] !== spinner.text) {
|
|
527
|
+
spinner.text = STEPS[data.event.step];
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
done = streamDone;
|
|
532
|
+
}
|
|
533
|
+
spinner.success("Deployment completed successfully.");
|
|
534
|
+
};
|
|
535
|
+
var deploy = new Command2("deploy").description("Deploy your application to Cloudflare.").addOption(
|
|
536
|
+
new Option2(
|
|
537
|
+
"--store-hash <hash>",
|
|
538
|
+
"BigCommerce store hash. Can be found in the URL of your store Control Panel."
|
|
539
|
+
).env("BIGCOMMERCE_STORE_HASH").makeOptionMandatory()
|
|
540
|
+
).addOption(
|
|
541
|
+
new Option2(
|
|
542
|
+
"--access-token <token>",
|
|
543
|
+
"BigCommerce access token. Can be found after creating a store-level API account."
|
|
544
|
+
).env("BIGCOMMERCE_ACCESS_TOKEN").makeOptionMandatory()
|
|
545
|
+
).addOption(
|
|
546
|
+
new Option2("--api-host <host>", "BigCommerce API host. The default is api.bigcommerce.com.").env("BIGCOMMERCE_API_HOST").default("api.bigcommerce.com")
|
|
547
|
+
).addOption(
|
|
548
|
+
new Option2(
|
|
549
|
+
"--project-uuid <uuid>",
|
|
550
|
+
"BigCommerce intrastructure project UUID. Can be found via the BigCommerce API (GET /v3/infrastructure/projects)."
|
|
551
|
+
).env("BIGCOMMERCE_PROJECT_UUID")
|
|
552
|
+
).addOption(
|
|
553
|
+
new Option2(
|
|
554
|
+
"--secret <secrets...>",
|
|
555
|
+
"Secrets to set for the deployment. Format: SECRET_1=FOO SECRET_2=BAR"
|
|
556
|
+
)
|
|
557
|
+
).option("--dry-run", "Run the command to generate the bundle without uploading or deploying.").action(async (options) => {
|
|
558
|
+
try {
|
|
559
|
+
const config2 = getProjectConfig();
|
|
560
|
+
await telemetry.identify(options.storeHash);
|
|
561
|
+
const projectUuid = options.projectUuid ?? config2.get("projectUuid");
|
|
562
|
+
if (!projectUuid) {
|
|
563
|
+
throw new Error(
|
|
564
|
+
"Project UUID is required. Please run either `bigcommerce link` or this command again with --project-uuid <uuid>."
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
await generateBundleZip();
|
|
568
|
+
if (options.dryRun) {
|
|
569
|
+
consola.info("Dry run enabled \u2014 skipping upload and deployment steps.");
|
|
570
|
+
consola.info("Next steps (skipped):");
|
|
571
|
+
consola.info("- Generate upload signature");
|
|
572
|
+
consola.info("- Upload bundle.zip");
|
|
573
|
+
consola.info("- Create deployment");
|
|
574
|
+
process.exit(0);
|
|
575
|
+
}
|
|
576
|
+
const uploadSignature = await generateUploadSignature(
|
|
577
|
+
options.storeHash,
|
|
578
|
+
options.accessToken,
|
|
579
|
+
options.apiHost
|
|
580
|
+
);
|
|
581
|
+
await uploadBundleZip(uploadSignature.upload_url);
|
|
582
|
+
const environmentVariables = parseEnvironmentVariables(options.secret);
|
|
583
|
+
const { deployment_uuid: deploymentUuid } = await createDeployment(
|
|
584
|
+
projectUuid,
|
|
585
|
+
uploadSignature.upload_uuid,
|
|
586
|
+
options.storeHash,
|
|
587
|
+
options.accessToken,
|
|
588
|
+
options.apiHost,
|
|
589
|
+
environmentVariables
|
|
590
|
+
);
|
|
591
|
+
await getDeploymentStatus(
|
|
592
|
+
deploymentUuid,
|
|
593
|
+
options.storeHash,
|
|
594
|
+
options.accessToken,
|
|
595
|
+
options.apiHost
|
|
596
|
+
);
|
|
597
|
+
} catch (error) {
|
|
598
|
+
consola.error(error);
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// src/cli/commands/dev.ts
|
|
604
|
+
import { Command as Command3 } from "commander";
|
|
605
|
+
import { execa as execa2 } from "execa";
|
|
606
|
+
import { existsSync as existsSync2 } from "fs";
|
|
607
|
+
import { join as join5 } from "path";
|
|
608
|
+
var dev = new Command3("dev").description("Start the Catalyst development server.").helpOption(false).allowUnknownOption(true).argument(
|
|
609
|
+
"[options...]",
|
|
610
|
+
"Next.js `dev` options (see: https://nextjs.org/docs/app/api-reference/cli/next#next-dev-options)"
|
|
611
|
+
).action(async (options) => {
|
|
612
|
+
try {
|
|
613
|
+
const nextBin = join5("node_modules", ".bin", "next");
|
|
614
|
+
if (!existsSync2(nextBin)) {
|
|
615
|
+
throw new Error(
|
|
616
|
+
`Next.js is not installed in ${process.cwd()}. Are you in a valid Next.js project?`
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
await execa2(nextBin, ["dev", ...options], {
|
|
620
|
+
stdio: "inherit",
|
|
621
|
+
cwd: process.cwd()
|
|
622
|
+
});
|
|
623
|
+
} catch (error) {
|
|
624
|
+
consola.error(error instanceof Error ? error.message : error);
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
// src/cli/commands/link.ts
|
|
630
|
+
import { Command as Command4, Option as Option3 } from "commander";
|
|
631
|
+
import { z as z2 } from "zod";
|
|
632
|
+
var telemetry2 = new Telemetry();
|
|
633
|
+
var fetchProjectsSchema = z2.object({
|
|
634
|
+
data: z2.array(
|
|
635
|
+
z2.object({
|
|
636
|
+
uuid: z2.string(),
|
|
637
|
+
name: z2.string()
|
|
638
|
+
})
|
|
639
|
+
)
|
|
640
|
+
});
|
|
641
|
+
var createProjectSchema = z2.object({
|
|
642
|
+
data: z2.object({
|
|
643
|
+
uuid: z2.string(),
|
|
644
|
+
name: z2.string(),
|
|
645
|
+
date_created: z2.coerce.date(),
|
|
646
|
+
date_modified: z2.coerce.date()
|
|
647
|
+
})
|
|
648
|
+
});
|
|
649
|
+
async function fetchProjects(storeHash, accessToken, apiHost) {
|
|
650
|
+
const response = await fetch(
|
|
651
|
+
`https://${apiHost}/stores/${storeHash}/v3/infrastructure/projects`,
|
|
652
|
+
{
|
|
653
|
+
method: "GET",
|
|
654
|
+
headers: {
|
|
655
|
+
"X-Auth-Token": accessToken
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
if (response.status === 403) {
|
|
660
|
+
throw new Error(
|
|
661
|
+
"Infrastructure Projects API not enabled. If you are part of the alpha, contact support@bigcommerce.com to enable it."
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
if (!response.ok) {
|
|
665
|
+
throw new Error(`Failed to fetch projects: ${response.statusText}`);
|
|
666
|
+
}
|
|
667
|
+
const res = await response.json();
|
|
668
|
+
const { data } = fetchProjectsSchema.parse(res);
|
|
669
|
+
return data;
|
|
670
|
+
}
|
|
671
|
+
async function createProject(name, storeHash, accessToken, apiHost) {
|
|
672
|
+
const response = await fetch(
|
|
673
|
+
`https://${apiHost}/stores/${storeHash}/v3/infrastructure/projects`,
|
|
674
|
+
{
|
|
675
|
+
method: "POST",
|
|
676
|
+
headers: {
|
|
677
|
+
"X-Auth-Token": accessToken,
|
|
678
|
+
Accept: "application/json",
|
|
679
|
+
"Content-Type": "application/json"
|
|
680
|
+
},
|
|
681
|
+
body: JSON.stringify({ name })
|
|
682
|
+
}
|
|
683
|
+
);
|
|
684
|
+
if (response.status === 502) {
|
|
685
|
+
throw new Error("Failed to create project, is the name already in use?");
|
|
686
|
+
}
|
|
687
|
+
if (response.status === 403) {
|
|
688
|
+
throw new Error(
|
|
689
|
+
"Infrastructure Projects API not enabled. If you are part of the alpha, contact support@bigcommerce.com to enable it."
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if (!response.ok) {
|
|
693
|
+
throw new Error(`Failed to create project: ${response.statusText}`);
|
|
694
|
+
}
|
|
695
|
+
const res = await response.json();
|
|
696
|
+
const { data } = createProjectSchema.parse(res);
|
|
697
|
+
return data;
|
|
698
|
+
}
|
|
699
|
+
var link = new Command4("link").description(
|
|
700
|
+
"Link your local Catalyst project to a BigCommerce infrastructure project. You can provide a project UUID directly, or fetch and select from available projects using your store credentials."
|
|
701
|
+
).addOption(
|
|
702
|
+
new Option3(
|
|
703
|
+
"--store-hash <hash>",
|
|
704
|
+
"BigCommerce store hash. Can be found in the URL of your store Control Panel."
|
|
705
|
+
).env("BIGCOMMERCE_STORE_HASH")
|
|
706
|
+
).addOption(
|
|
707
|
+
new Option3(
|
|
708
|
+
"--access-token <token>",
|
|
709
|
+
"BigCommerce access token. Can be found after creating a store-level API account."
|
|
710
|
+
).env("BIGCOMMERCE_ACCESS_TOKEN")
|
|
711
|
+
).addOption(
|
|
712
|
+
new Option3("--api-host <host>", "BigCommerce API host. The default is api.bigcommerce.com.").env("BIGCOMMERCE_API_HOST").default("api.bigcommerce.com")
|
|
713
|
+
).option(
|
|
714
|
+
"--project-uuid <uuid>",
|
|
715
|
+
"BigCommerce infrastructure project UUID. Can be found via the BigCommerce API (GET /v3/infrastructure/projects). Use this to link directly without fetching projects."
|
|
716
|
+
).option(
|
|
717
|
+
"--root-dir <path>",
|
|
718
|
+
"Path to the root directory of your Catalyst project (default: current working directory).",
|
|
719
|
+
process.cwd()
|
|
720
|
+
).action(async (options) => {
|
|
721
|
+
try {
|
|
722
|
+
const config2 = getProjectConfig(options.rootDir);
|
|
723
|
+
const writeProjectConfig = (uuid) => {
|
|
724
|
+
consola.start("Writing project UUID to .bigcommerce/project.json...");
|
|
725
|
+
config2.set("projectUuid", uuid);
|
|
726
|
+
config2.set("framework", "catalyst");
|
|
727
|
+
consola.success("Project UUID written to .bigcommerce/project.json.");
|
|
728
|
+
};
|
|
729
|
+
if (options.projectUuid) {
|
|
730
|
+
writeProjectConfig(options.projectUuid);
|
|
731
|
+
process.exit(0);
|
|
732
|
+
}
|
|
733
|
+
if (options.storeHash && options.accessToken) {
|
|
734
|
+
await telemetry2.identify(options.storeHash);
|
|
735
|
+
consola.start("Fetching projects...");
|
|
736
|
+
const projects = await fetchProjects(
|
|
737
|
+
options.storeHash,
|
|
738
|
+
options.accessToken,
|
|
739
|
+
options.apiHost
|
|
740
|
+
);
|
|
741
|
+
consola.success("Projects fetched.");
|
|
742
|
+
const promptOptions = [
|
|
743
|
+
...projects.map((project) => ({
|
|
744
|
+
label: project.name,
|
|
745
|
+
value: project.uuid,
|
|
746
|
+
hint: project.uuid
|
|
747
|
+
})),
|
|
748
|
+
{
|
|
749
|
+
label: "Create a new project",
|
|
750
|
+
value: "create",
|
|
751
|
+
hint: "Create a new infrastructure project for this BigCommerce store."
|
|
752
|
+
}
|
|
753
|
+
];
|
|
754
|
+
let projectUuid = await consola.prompt(
|
|
755
|
+
"Select a project or create a new project (Press <enter> to select).",
|
|
756
|
+
{
|
|
757
|
+
type: "select",
|
|
758
|
+
options: promptOptions,
|
|
759
|
+
cancel: "reject"
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
if (projectUuid === "create") {
|
|
763
|
+
const newProjectName = await consola.prompt("Enter a name for the new project:", {
|
|
764
|
+
type: "text"
|
|
765
|
+
});
|
|
766
|
+
const data = await createProject(
|
|
767
|
+
newProjectName,
|
|
768
|
+
options.storeHash,
|
|
769
|
+
options.accessToken,
|
|
770
|
+
options.apiHost
|
|
771
|
+
);
|
|
772
|
+
projectUuid = data.uuid;
|
|
773
|
+
consola.success(`Project "${data.name}" created successfully.`);
|
|
774
|
+
}
|
|
775
|
+
writeProjectConfig(projectUuid);
|
|
776
|
+
process.exit(0);
|
|
777
|
+
}
|
|
778
|
+
consola.error("Insufficient information to link a project.");
|
|
779
|
+
consola.info("Provide a project UUID with --project-uuid, or");
|
|
780
|
+
consola.info("Provide both --store-hash and --access-token to fetch and select a project.");
|
|
781
|
+
process.exit(1);
|
|
782
|
+
} catch (error) {
|
|
783
|
+
consola.error(error instanceof Error ? error.message : error);
|
|
784
|
+
process.exit(1);
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// src/cli/commands/start.ts
|
|
789
|
+
import { Command as Command5, Option as Option4 } from "commander";
|
|
790
|
+
import { execa as execa3 } from "execa";
|
|
791
|
+
import { existsSync as existsSync3 } from "fs";
|
|
792
|
+
import { join as join6 } from "path";
|
|
793
|
+
var start = new Command5("start").description("Start your Catalyst storefront in optimized production mode.").allowUnknownOption(true).argument(
|
|
794
|
+
"[start-options...]",
|
|
795
|
+
"Pass additional options to the start command. If framework is Next.js, see https://nextjs.org/docs/api-reference/cli#start for available options."
|
|
796
|
+
).addOption(
|
|
797
|
+
new Option4("--framework <framework>", "The framework to use for the preview").choices([
|
|
798
|
+
"catalyst",
|
|
799
|
+
"nextjs"
|
|
800
|
+
])
|
|
801
|
+
).action(async (startOptions, options) => {
|
|
802
|
+
try {
|
|
803
|
+
const config2 = getProjectConfig();
|
|
804
|
+
const framework = options.framework ?? config2.get("framework");
|
|
805
|
+
if (framework === "nextjs") {
|
|
806
|
+
const nextBin = join6("node_modules", ".bin", "next");
|
|
807
|
+
if (!existsSync3(nextBin)) {
|
|
808
|
+
throw new Error(
|
|
809
|
+
`Next.js is not installed in ${process.cwd()}. Are you in a valid Next.js project?`
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
await execa3(nextBin, ["start", ...startOptions], {
|
|
813
|
+
stdio: "inherit",
|
|
814
|
+
cwd: process.cwd()
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
await execa3(
|
|
818
|
+
"pnpm",
|
|
819
|
+
[
|
|
820
|
+
"exec",
|
|
821
|
+
"opennextjs-cloudflare",
|
|
822
|
+
"preview",
|
|
823
|
+
"--config",
|
|
824
|
+
join6(".bigcommerce", "wrangler.jsonc"),
|
|
825
|
+
...startOptions
|
|
826
|
+
],
|
|
827
|
+
{
|
|
828
|
+
stdio: "inherit",
|
|
829
|
+
cwd: process.cwd()
|
|
830
|
+
}
|
|
831
|
+
);
|
|
832
|
+
} catch (error) {
|
|
833
|
+
consola.error(error instanceof Error ? error.message : error);
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
// src/cli/commands/telemetry.ts
|
|
838
|
+
import { Argument, Command as Command6, Option as Option5 } from "commander";
|
|
839
|
+
import { colorize } from "consola/utils";
|
|
840
|
+
var telemetryService = new Telemetry();
|
|
841
|
+
var isEnabled = telemetryService.isEnabled();
|
|
842
|
+
var telemetry3 = new Command6("telemetry").addArgument(new Argument("[arg]").choices(["disable", "enable", "status"])).addOption(new Option5("--enable", `Enables CLI telemetry collection.`).conflicts("disable")).option("--disable", `Disables CLI telemetry collection.`).action((arg, options) => {
|
|
843
|
+
if (options.enable || arg === "enable") {
|
|
844
|
+
telemetryService.setEnabled(true);
|
|
845
|
+
isEnabled = true;
|
|
846
|
+
consola.success("Success!\n");
|
|
847
|
+
} else if (options.disable || arg === "disable") {
|
|
848
|
+
telemetryService.setEnabled(false);
|
|
849
|
+
if (isEnabled) {
|
|
850
|
+
consola.success("Your preference has been saved to .bigcommerce/project.json");
|
|
851
|
+
} else {
|
|
852
|
+
consola.info(`Catalyst CLI telemetry collection is already disabled.`);
|
|
853
|
+
}
|
|
854
|
+
isEnabled = false;
|
|
855
|
+
} else {
|
|
856
|
+
consola.info("Catalyst CLI Telemetry\n");
|
|
857
|
+
}
|
|
858
|
+
consola.info(
|
|
859
|
+
`Status: ${colorize("bold", isEnabled ? colorize("green", "Enabled") : colorize("red", "Disabled"))}`
|
|
860
|
+
);
|
|
861
|
+
if (!isEnabled) {
|
|
862
|
+
consola.info(
|
|
863
|
+
`You have opted-out of Catalyst CLI telemetry. No data will be collected from your machine.`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// src/cli/commands/version.ts
|
|
869
|
+
import { Command as Command7 } from "commander";
|
|
870
|
+
var version = new Command7("version").description("Display detailed version information.").action(() => {
|
|
871
|
+
consola.log("Version Information:");
|
|
872
|
+
consola.log(`CLI Version: ${package_default.version}`);
|
|
873
|
+
consola.log(`Node Version: ${process.version}`);
|
|
874
|
+
consola.log(`Platform: ${process.platform} (${process.arch})`);
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
// src/cli/hooks/telemetry.ts
|
|
878
|
+
var telemetry4 = new Telemetry();
|
|
879
|
+
var allowlistArguments = ["--keep-temp-dir", "--api-host", "--project-uuid"];
|
|
880
|
+
function parseArguments(args) {
|
|
881
|
+
return args.reduce((result, arg, index, array) => {
|
|
882
|
+
if (arg.includes("=")) {
|
|
883
|
+
const [key, value] = arg.split("=");
|
|
884
|
+
if (allowlistArguments.includes(key)) {
|
|
885
|
+
return {
|
|
886
|
+
...result,
|
|
887
|
+
[key]: value
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (allowlistArguments.includes(arg)) {
|
|
892
|
+
const nextValue = array[index + 1] && !array[index + 1].startsWith("--") ? array[index + 1] : null;
|
|
893
|
+
if (nextValue && !nextValue.includes("--")) {
|
|
894
|
+
return {
|
|
895
|
+
...result,
|
|
896
|
+
[arg]: nextValue
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return result;
|
|
901
|
+
}, {});
|
|
902
|
+
}
|
|
903
|
+
var telemetryPreHook = async (command) => {
|
|
904
|
+
const [commandName, ...args] = command.args;
|
|
905
|
+
return await telemetry4.track(commandName, {
|
|
906
|
+
...parseArguments(args)
|
|
907
|
+
});
|
|
908
|
+
};
|
|
909
|
+
var telemetryPostHook = async () => {
|
|
910
|
+
await telemetry4.analytics.closeAndFlush();
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
// src/cli/program.ts
|
|
914
|
+
var program = new Command8();
|
|
915
|
+
config({
|
|
916
|
+
path: [
|
|
917
|
+
resolve(process.cwd(), ".env"),
|
|
918
|
+
resolve(process.cwd(), ".env.local"),
|
|
919
|
+
// Assumes the parent directory is the monorepo root:
|
|
920
|
+
resolve(process.cwd(), "..", ".env"),
|
|
921
|
+
resolve(process.cwd(), "..", ".env.local")
|
|
922
|
+
]
|
|
923
|
+
});
|
|
924
|
+
consola.log(colorize2("cyanBright", `\u25E2 ${package_default.name} v${package_default.version}
|
|
925
|
+
`));
|
|
926
|
+
program.name(package_default.name).version(package_default.version).description("CLI tool for Catalyst development").addCommand(version).addCommand(dev).addCommand(start).addCommand(build).addCommand(deploy).addCommand(link).addCommand(telemetry3).hook("preAction", telemetryPreHook).hook("postAction", telemetryPostHook);
|
|
927
|
+
|
|
928
|
+
// src/cli/index.ts
|
|
929
|
+
program.parse(process.argv);
|
|
930
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/program.ts","../package.json","../src/cli/commands/build.ts","../src/cli/lib/get-module-cli-path.ts","../src/cli/lib/logger.ts","../src/cli/lib/project-config.ts","../src/cli/lib/wrangler-config.ts","../src/cli/commands/deploy.ts","../src/cli/lib/telemetry.ts","../src/cli/commands/dev.ts","../src/cli/commands/link.ts","../src/cli/commands/start.ts","../src/cli/commands/telemetry.ts","../src/cli/commands/version.ts","../src/cli/hooks/telemetry.ts","../src/cli/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { colorize } from 'consola/utils';\nimport { config } from 'dotenv';\nimport { resolve } from 'node:path';\n\nimport PACKAGE_INFO from '../../package.json';\n\nimport { build } from './commands/build';\nimport { deploy } from './commands/deploy';\nimport { dev } from './commands/dev';\nimport { link } from './commands/link';\nimport { start } from './commands/start';\nimport { telemetry } from './commands/telemetry';\nimport { version } from './commands/version';\nimport { telemetryPostHook, telemetryPreHook } from './hooks/telemetry';\nimport { consola } from './lib/logger';\n\nexport const program = new Command();\n\nconfig({\n path: [\n resolve(process.cwd(), '.env'),\n resolve(process.cwd(), '.env.local'),\n // Assumes the parent directory is the monorepo root:\n resolve(process.cwd(), '..', '.env'),\n resolve(process.cwd(), '..', '.env.local'),\n ],\n});\n\nconsola.log(colorize('cyanBright', `◢ ${PACKAGE_INFO.name} v${PACKAGE_INFO.version}\\n`));\n\nprogram\n .name(PACKAGE_INFO.name)\n .version(PACKAGE_INFO.version)\n .description('CLI tool for Catalyst development')\n .addCommand(version)\n .addCommand(dev)\n .addCommand(start)\n .addCommand(build)\n .addCommand(deploy)\n .addCommand(link)\n .addCommand(telemetry)\n .hook('preAction', telemetryPreHook)\n .hook('postAction', telemetryPostHook);\n","{\n \"name\": \"@bigcommerce/catalyst\",\n \"version\": \"1.0.0-alpha.0\",\n \"type\": \"module\",\n \"bin\": {\n \"catalyst\": \"dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"templates\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"eslint . --max-warnings 0\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"build\": \"tsup\"\n },\n \"engines\": {\n \"node\": \"^20.0.0 || ^22.0.0\"\n },\n \"dependencies\": {\n \"@segment/analytics-node\": \"^2.2.1\",\n \"adm-zip\": \"^0.5.16\",\n \"commander\": \"^14.0.0\",\n \"conf\": \"^13.1.0\",\n \"consola\": \"^3.4.2\",\n \"dotenv\": \"^16.5.0\",\n \"execa\": \"^9.6.0\",\n \"yocto-spinner\": \"^1.0.0\",\n \"zod\": \"^4.0.5\"\n },\n \"devDependencies\": {\n \"@bigcommerce/eslint-config\": \"^2.11.0\",\n \"@bigcommerce/eslint-config-catalyst\": \"workspace:^\",\n \"@commander-js/extra-typings\": \"^14.0.0\",\n \"@types/adm-zip\": \"^0.5.7\",\n \"@types/node\": \"^22.15.30\",\n \"@vitest/coverage-v8\": \"^3.2.4\",\n \"@vitest/ui\": \"^3.2.4\",\n \"eslint\": \"^8.57.1\",\n \"msw\": \"^2.9.0\",\n \"prettier\": \"^3.6.2\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.8.3\",\n \"vitest\": \"^3.2.4\"\n },\n \"peerDependencies\": {\n \"@opennextjs/cloudflare\": \"^1.8.0\"\n }\n}\n","import { Command, Option } from 'commander';\nimport { execa } from 'execa';\nimport { existsSync } from 'node:fs';\nimport { copyFile, cp, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { getModuleCliPath } from '../lib/get-module-cli-path';\nimport { consola } from '../lib/logger';\nimport { getProjectConfig } from '../lib/project-config';\nimport { getWranglerConfig } from '../lib/wrangler-config';\n\nconst WRANGLER_VERSION = '4.24.3';\n\nexport const build = new Command('build')\n .allowUnknownOption()\n // The unknown options end up in program.args, not in program.opts(). Commander does not take a guess at how to interpret the unknown options.\n .argument(\n '[next-build-options...]',\n 'Next.js `build` options (see: https://nextjs.org/docs/app/api-reference/cli/next#next-build-options)',\n )\n .addOption(\n new Option(\n '--project-uuid <uuid>',\n 'Project UUID to be included in the deployment configuration.',\n ).env('BIGCOMMERCE_PROJECT_UUID'),\n )\n .addOption(\n new Option('--framework <framework>', 'The framework to use for the build.').choices([\n 'nextjs',\n 'catalyst',\n ]),\n )\n .action(async (nextBuildOptions, options) => {\n const coreDir = process.cwd();\n\n try {\n const config = getProjectConfig();\n const framework = options.framework ?? config.get('framework');\n\n if (framework === 'nextjs') {\n const nextBin = join('node_modules', '.bin', 'next');\n\n if (!existsSync(nextBin)) {\n throw new Error(\n `Next.js is not installed in ${coreDir}. Are you in a valid Next.js project?`,\n );\n }\n\n await execa(nextBin, ['build', ...nextBuildOptions], {\n stdio: 'inherit',\n cwd: coreDir,\n });\n }\n\n if (framework === 'catalyst') {\n const openNextOutDir = join(coreDir, '.open-next');\n const bigcommerceDistDir = join(coreDir, '.bigcommerce', 'dist');\n\n const projectUuid = options.projectUuid ?? config.get('projectUuid');\n\n if (!projectUuid) {\n throw new Error(\n 'Project UUID is required. Please run `link` or provide `--project-uuid`',\n );\n }\n\n const wranglerConfig = getWranglerConfig(projectUuid, 'PLACEHOLDER_KV_ID');\n\n consola.start('Copying templates...');\n\n await copyFile(\n join(getModuleCliPath(), 'templates', 'open-next.config.ts'),\n join(coreDir, '.bigcommerce', 'open-next.config.ts'),\n );\n await writeFile(\n join(coreDir, '.bigcommerce', 'wrangler.jsonc'),\n JSON.stringify(wranglerConfig, null, 2),\n );\n\n consola.success('Templates copied');\n\n consola.start('Building project...');\n\n await execa(\n 'pnpm',\n [\n 'exec',\n 'opennextjs-cloudflare',\n 'build',\n '--skipWranglerConfigCheck',\n '--openNextConfigPath',\n join(coreDir, '.bigcommerce', 'open-next.config.ts'),\n ],\n {\n stdout: ['pipe', 'inherit'],\n cwd: coreDir,\n },\n );\n\n await execa(\n 'pnpm',\n [\n 'dlx',\n `wrangler@${WRANGLER_VERSION}`,\n 'deploy',\n '--config',\n join(coreDir, '.bigcommerce', 'wrangler.jsonc'),\n '--keep-vars',\n '--outdir',\n bigcommerceDistDir,\n '--dry-run',\n ],\n {\n stdout: ['pipe', 'inherit'],\n cwd: coreDir,\n },\n );\n\n consola.success('Project built');\n\n await cp(join(openNextOutDir, 'assets'), join(bigcommerceDistDir, 'assets'), {\n recursive: true,\n force: true,\n });\n }\n } catch (error) {\n consola.error(error);\n process.exit(1);\n }\n });\n","/* eslint-disable no-underscore-dangle */\n\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport function getModuleCliPath() {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n\n return join(__dirname, '..');\n}\n","import { createConsola } from 'consola';\n\nexport const consola = createConsola({\n level: process.env.CONSOLA_LEVEL ? parseInt(process.env.CONSOLA_LEVEL, 10) : 3,\n});\n","import Conf from 'conf';\nimport { join } from 'path';\n\nexport interface ProjectConfigSchema {\n projectUuid: string;\n framework: 'catalyst' | 'nextjs';\n telemetry: {\n enabled: boolean;\n anonymousId: string;\n };\n}\n\nexport function getProjectConfig(rootDir = process.cwd()) {\n return new Conf<ProjectConfigSchema>({\n cwd: join(rootDir, '.bigcommerce'),\n projectSuffix: '',\n configName: 'project',\n schema: {\n projectUuid: { type: 'string', format: 'uuid' },\n framework: {\n type: 'string',\n enum: ['catalyst', 'nextjs'],\n default: 'nextjs',\n },\n telemetry: {\n type: 'object',\n properties: {\n enabled: { type: 'boolean' },\n anonymousId: { type: 'string' },\n },\n },\n },\n });\n}\n","export function getWranglerConfig(projectUuid: string, kvNamespaceId: string) {\n return {\n $schema: 'node_modules/wrangler/config-schema.json',\n main: '../.open-next/worker.js',\n name: `project-${projectUuid}`,\n compatibility_date: '2025-07-15',\n compatibility_flags: ['nodejs_compat', 'global_fetch_strictly_public'],\n observability: {\n enabled: true,\n head_sampling_rate: 0.05,\n logs: {\n enabled: true,\n head_sampling_rate: 1,\n invocation_logs: false,\n },\n },\n assets: {\n directory: '../.open-next/assets',\n binding: 'ASSETS',\n },\n services: [\n {\n binding: 'WORKER_SELF_REFERENCE',\n service: `project-${projectUuid}`,\n },\n ],\n kv_namespaces: [\n {\n binding: 'NEXT_INC_CACHE_KV',\n id: kvNamespaceId,\n },\n ],\n durable_objects: {\n bindings: [\n {\n name: 'NEXT_CACHE_DO_QUEUE',\n class_name: 'DOQueueHandler',\n },\n {\n name: 'NEXT_TAG_CACHE_DO_SHARDED',\n class_name: 'DOShardedTagCache',\n },\n {\n name: 'NEXT_CACHE_DO_PURGE',\n class_name: 'BucketCachePurge',\n },\n ],\n },\n migrations: [\n {\n tag: 'v1',\n new_sqlite_classes: ['DOQueueHandler', 'DOShardedTagCache', 'BucketCachePurge'],\n },\n ],\n };\n}\n","import AdmZip from 'adm-zip';\nimport { Command, Option } from 'commander';\nimport { access, readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport yoctoSpinner from 'yocto-spinner';\nimport { z } from 'zod';\n\nimport { consola } from '../lib/logger';\nimport { getProjectConfig } from '../lib/project-config';\nimport { Telemetry } from '../lib/telemetry';\n\nconst telemetry = new Telemetry();\n\nconst stepsEnum = z.enum([\n 'initializing',\n 'downloading',\n 'unzipping',\n 'processing',\n 'deploying',\n 'finalizing',\n 'complete',\n]);\n\nconst STEPS: Record<z.infer<typeof stepsEnum>, string> = {\n initializing: 'Initializing...',\n downloading: 'Downloading...',\n unzipping: 'Unzipping...',\n processing: 'Processing...',\n deploying: 'Deploying...',\n finalizing: 'Finalizing...',\n complete: 'Complete',\n};\n\nconst UploadSignatureSchema = z.object({\n data: z.object({\n upload_url: z.url(),\n upload_uuid: z.string(),\n }),\n});\n\nconst CreateDeploymentSchema = z.object({\n data: z.object({\n deployment_uuid: z.uuid(),\n }),\n});\n\nconst DeploymentStatusSchema = z.object({\n deployment_uuid: z.uuid(),\n deployment_status: z.enum(['queued', 'in_progress', 'failed', 'completed']),\n event: z\n .object({\n step: stepsEnum,\n progress: z.number(),\n })\n .nullable(),\n error: z\n .object({\n code: z.number(),\n })\n .optional(),\n});\n\nexport const generateBundleZip = async () => {\n consola.info('Generating bundle...');\n\n const bigcommerceDir = join(process.cwd(), '.bigcommerce');\n const distDir = join(process.cwd(), '.bigcommerce', 'dist');\n\n // Check if .bigcommerce/dist exists\n try {\n await access(distDir);\n } catch {\n throw new Error(`Dist directory not found: ${distDir}`);\n }\n\n // Check if .bigcommerce/dist is not empty\n const buildDirContents = await readdir(distDir);\n\n if (buildDirContents.length === 0) {\n throw new Error(`Dist directory is empty: ${distDir}`);\n }\n\n const outputZip = join(bigcommerceDir, 'bundle.zip');\n\n // Use AdmZip to create the zip\n const zip = new AdmZip();\n\n zip.addLocalFolder(distDir, 'output');\n zip.writeZip(outputZip);\n\n consola.success(`Bundle created at: ${outputZip}`);\n};\n\nexport const generateUploadSignature = async (\n storeHash: string,\n accessToken: string,\n apiHost: string,\n) => {\n consola.info('Generating upload signature...');\n\n const response = await fetch(\n `https://${apiHost}/stores/${storeHash}/v3/infrastructure/deployments/uploads`,\n {\n method: 'POST',\n headers: {\n 'X-Auth-Token': accessToken,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({}),\n },\n );\n\n if (!response.ok) {\n throw new Error(`Failed to fetch upload signature: ${response.status} ${response.statusText}`);\n }\n\n const res: unknown = await response.json();\n const { data } = UploadSignatureSchema.parse(res);\n\n consola.success('Upload signature generated.');\n\n return data;\n};\n\nexport const uploadBundleZip = async (uploadUrl: string) => {\n consola.info('Uploading bundle...');\n\n const zipPath = join(process.cwd(), '.bigcommerce', 'bundle.zip');\n\n // Read the zip file as a buffer\n const fileBuffer = await readFile(zipPath);\n\n const response = await fetch(uploadUrl, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/zip',\n },\n body: fileBuffer,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to upload bundle: ${response.status} ${response.statusText}`);\n }\n\n consola.success('Bundle uploaded successfully.');\n\n return true;\n};\n\nexport const parseEnvironmentVariables = (secretOption?: string[]) => {\n return secretOption?.map((envVar) => {\n const [key, value] = envVar.split('=');\n\n if (!key || !value) {\n throw new Error(`Invalid secret format: ${envVar}. Expected format: KEY=VALUE`);\n }\n\n return {\n type: 'secret' as const,\n key: key.trim(),\n value: value.trim(),\n };\n });\n};\n\nexport const createDeployment = async (\n projectUuid: string,\n uploadUuid: string,\n storeHash: string,\n accessToken: string,\n apiHost: string,\n environmentVariables?: Array<{ type: 'secret' | 'plain_text'; key: string; value: string }>,\n) => {\n consola.info('Creating deployment...');\n\n const response = await fetch(\n `https://${apiHost}/stores/${storeHash}/v3/infrastructure/deployments`,\n {\n method: 'POST',\n headers: {\n 'X-Auth-Token': accessToken,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({\n project_uuid: projectUuid,\n upload_uuid: uploadUuid,\n environment_variables: environmentVariables,\n }),\n },\n );\n\n if (!response.ok) {\n throw new Error(`Failed to create deployment: ${response.status} ${response.statusText}`);\n }\n\n const res: unknown = await response.json();\n const { data } = CreateDeploymentSchema.parse(res);\n\n consola.success('Deployment started...');\n\n return data;\n};\n\nexport const getDeploymentStatus = async (\n deploymentUuid: string,\n storeHash: string,\n accessToken: string,\n apiHost: string,\n) => {\n consola.info('Fetching deployment status...');\n\n const spinner = yoctoSpinner().start('Fetching...');\n\n const response = await fetch(\n `https://${apiHost}/stores/${storeHash}/v3/infrastructure/deployments/${deploymentUuid}/events`,\n {\n method: 'GET',\n headers: {\n 'X-Auth-Token': accessToken,\n Accept: 'text/event-stream',\n Connection: 'keep-alive',\n },\n },\n );\n\n if (!response.ok) {\n throw new Error(`Failed to open event stream: ${response.status} ${response.statusText}`);\n }\n\n const reader = response.body?.getReader();\n\n if (!reader) {\n throw new Error('Failed to read event stream.');\n }\n\n const decoder = new TextDecoder();\n let done = false;\n\n while (!done) {\n // eslint-disable-next-line no-await-in-loop\n const { value, done: streamDone } = await reader.read();\n let json: unknown;\n\n if (value) {\n const chunk = decoder.decode(value, { stream: true }).trim();\n const split = chunk\n .split('\\n\\n')\n .map((s) => s.replace('data:', '').trim())\n .filter(Boolean);\n\n split.forEach((event) => {\n try {\n json = JSON.parse(event);\n } catch (error) {\n consola.warn(`Failed to parse event, dropping from stream. Event: ${event}`, error);\n\n return;\n }\n\n const data = DeploymentStatusSchema.parse(json);\n\n if (data.error) {\n throw new Error(`Deployment failed with error code: ${data.error.code}`);\n }\n\n if (data.event && STEPS[data.event.step] !== spinner.text) {\n spinner.text = STEPS[data.event.step];\n }\n });\n }\n\n done = streamDone;\n }\n\n spinner.success('Deployment completed successfully.');\n};\n\nexport const deploy = new Command('deploy')\n .description('Deploy your application to Cloudflare.')\n .addOption(\n new Option(\n '--store-hash <hash>',\n 'BigCommerce store hash. Can be found in the URL of your store Control Panel.',\n )\n .env('BIGCOMMERCE_STORE_HASH')\n .makeOptionMandatory(),\n )\n .addOption(\n new Option(\n '--access-token <token>',\n 'BigCommerce access token. Can be found after creating a store-level API account.',\n )\n .env('BIGCOMMERCE_ACCESS_TOKEN')\n .makeOptionMandatory(),\n )\n .addOption(\n new Option('--api-host <host>', 'BigCommerce API host. The default is api.bigcommerce.com.')\n .env('BIGCOMMERCE_API_HOST')\n .default('api.bigcommerce.com'),\n )\n .addOption(\n new Option(\n '--project-uuid <uuid>',\n 'BigCommerce intrastructure project UUID. Can be found via the BigCommerce API (GET /v3/infrastructure/projects).',\n ).env('BIGCOMMERCE_PROJECT_UUID'),\n )\n .addOption(\n new Option(\n '--secret <secrets...>',\n 'Secrets to set for the deployment. Format: SECRET_1=FOO SECRET_2=BAR',\n ),\n )\n .option('--dry-run', 'Run the command to generate the bundle without uploading or deploying.')\n\n .action(async (options) => {\n try {\n const config = getProjectConfig();\n\n await telemetry.identify(options.storeHash);\n\n const projectUuid = options.projectUuid ?? config.get('projectUuid');\n\n if (!projectUuid) {\n throw new Error(\n 'Project UUID is required. Please run either `bigcommerce link` or this command again with --project-uuid <uuid>.',\n );\n }\n\n await generateBundleZip();\n\n if (options.dryRun) {\n consola.info('Dry run enabled — skipping upload and deployment steps.');\n consola.info('Next steps (skipped):');\n consola.info('- Generate upload signature');\n consola.info('- Upload bundle.zip');\n consola.info('- Create deployment');\n\n process.exit(0);\n }\n\n const uploadSignature = await generateUploadSignature(\n options.storeHash,\n options.accessToken,\n options.apiHost,\n );\n\n await uploadBundleZip(uploadSignature.upload_url);\n\n const environmentVariables = parseEnvironmentVariables(options.secret);\n\n const { deployment_uuid: deploymentUuid } = await createDeployment(\n projectUuid,\n uploadSignature.upload_uuid,\n options.storeHash,\n options.accessToken,\n options.apiHost,\n environmentVariables,\n );\n\n await getDeploymentStatus(\n deploymentUuid,\n options.storeHash,\n options.accessToken,\n options.apiHost,\n );\n } catch (error) {\n consola.error(error);\n process.exit(1);\n }\n });\n","import { Analytics } from '@segment/analytics-node';\nimport Conf from 'conf';\nimport { randomBytes } from 'node:crypto';\n\nimport PACKAGE_INFO from '../../../package.json';\n\nimport { getProjectConfig, ProjectConfigSchema } from './project-config';\n\nconst TELEMETRY_KEY_ENABLED = 'telemetry.enabled';\nconst TELEMETRY_KEY_ID = `telemetry.anonymousId`;\n\nexport class Telemetry {\n readonly sessionId: string;\n readonly analytics: Analytics;\n\n private projectConfig: Conf<ProjectConfigSchema>;\n private CATALYST_TELEMETRY_DISABLED: string | undefined;\n\n private readonly projectName = 'catalyst-cli';\n private readonly projectVersion = PACKAGE_INFO.version;\n\n constructor() {\n this.CATALYST_TELEMETRY_DISABLED = process.env.CATALYST_TELEMETRY_DISABLED;\n\n this.projectConfig = getProjectConfig();\n\n this.sessionId = randomBytes(32).toString('hex');\n this.analytics = new Analytics({\n writeKey: process.env.CLI_SEGMENT_WRITE_KEY ?? 'not-a-valid-segment-write-key',\n });\n }\n\n async track(eventName: string, payload: Record<string, unknown>) {\n if (!this.isEnabled()) {\n return Promise.resolve(undefined);\n }\n\n this.analytics.track({\n event: eventName,\n anonymousId: this.getAnonymousId(),\n properties: {\n ...payload,\n sessionId: this.sessionId,\n },\n context: {\n app: {\n name: this.projectName,\n version: this.projectVersion,\n },\n },\n });\n }\n\n async identify(storeHash?: string) {\n if (!this.isEnabled()) {\n return Promise.resolve(undefined);\n }\n\n if (!storeHash) {\n return Promise.resolve(undefined);\n }\n\n this.analytics.identify({\n userId: storeHash,\n anonymousId: this.getAnonymousId(),\n context: {\n app: {\n name: this.projectName,\n version: this.projectVersion,\n },\n },\n });\n }\n\n setEnabled = (_enabled: boolean) => {\n const enabled = Boolean(_enabled);\n\n this.projectConfig.set('telemetry.enabled', enabled);\n };\n\n isEnabled() {\n return (\n !this.CATALYST_TELEMETRY_DISABLED &&\n this.projectConfig.get<typeof TELEMETRY_KEY_ENABLED, boolean>(TELEMETRY_KEY_ENABLED, true)\n );\n }\n\n private getAnonymousId(): string {\n const val = this.projectConfig.get<typeof TELEMETRY_KEY_ID, string>(TELEMETRY_KEY_ID);\n\n if (val) {\n return val;\n }\n\n const generated = randomBytes(32).toString('hex');\n\n this.projectConfig.set(TELEMETRY_KEY_ID, generated);\n\n return generated;\n }\n}\n","import { Command } from 'commander';\nimport { execa } from 'execa';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { consola } from '../lib/logger';\n\nexport const dev = new Command('dev')\n .description('Start the Catalyst development server.')\n // Proxy `--help` to the underlying `next dev` command\n .helpOption(false)\n .allowUnknownOption(true)\n // The unknown options end up in program.args, not in program.opts(). Commander does not take a guess at how to interpret the unknown options.\n .argument(\n '[options...]',\n 'Next.js `dev` options (see: https://nextjs.org/docs/app/api-reference/cli/next#next-dev-options)',\n )\n .action(async (options) => {\n try {\n const nextBin = join('node_modules', '.bin', 'next');\n\n if (!existsSync(nextBin)) {\n throw new Error(\n `Next.js is not installed in ${process.cwd()}. Are you in a valid Next.js project?`,\n );\n }\n\n await execa(nextBin, ['dev', ...options], {\n stdio: 'inherit',\n cwd: process.cwd(),\n });\n } catch (error) {\n consola.error(error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n","import { Command, Option } from 'commander';\nimport { z } from 'zod';\n\nimport { consola } from '../lib/logger';\nimport { getProjectConfig } from '../lib/project-config';\nimport { Telemetry } from '../lib/telemetry';\n\nconst telemetry = new Telemetry();\n\nconst fetchProjectsSchema = z.object({\n data: z.array(\n z.object({\n uuid: z.string(),\n name: z.string(),\n }),\n ),\n});\n\nconst createProjectSchema = z.object({\n data: z.object({\n uuid: z.string(),\n name: z.string(),\n date_created: z.coerce.date(),\n date_modified: z.coerce.date(),\n }),\n});\n\nasync function fetchProjects(storeHash: string, accessToken: string, apiHost: string) {\n const response = await fetch(\n `https://${apiHost}/stores/${storeHash}/v3/infrastructure/projects`,\n {\n method: 'GET',\n headers: {\n 'X-Auth-Token': accessToken,\n },\n },\n );\n\n if (response.status === 403) {\n throw new Error(\n 'Infrastructure Projects API not enabled. If you are part of the alpha, contact support@bigcommerce.com to enable it.',\n );\n }\n\n if (!response.ok) {\n throw new Error(`Failed to fetch projects: ${response.statusText}`);\n }\n\n const res: unknown = await response.json();\n\n const { data } = fetchProjectsSchema.parse(res);\n\n return data;\n}\n\nasync function createProject(\n name: string,\n storeHash: string,\n accessToken: string,\n apiHost: string,\n) {\n const response = await fetch(\n `https://${apiHost}/stores/${storeHash}/v3/infrastructure/projects`,\n {\n method: 'POST',\n headers: {\n 'X-Auth-Token': accessToken,\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ name }),\n },\n );\n\n if (response.status === 502) {\n throw new Error('Failed to create project, is the name already in use?');\n }\n\n if (response.status === 403) {\n throw new Error(\n 'Infrastructure Projects API not enabled. If you are part of the alpha, contact support@bigcommerce.com to enable it.',\n );\n }\n\n if (!response.ok) {\n throw new Error(`Failed to create project: ${response.statusText}`);\n }\n\n const res: unknown = await response.json();\n\n const { data } = createProjectSchema.parse(res);\n\n return data;\n}\n\nexport const link = new Command('link')\n .description(\n 'Link your local Catalyst project to a BigCommerce infrastructure project. You can provide a project UUID directly, or fetch and select from available projects using your store credentials.',\n )\n .addOption(\n new Option(\n '--store-hash <hash>',\n 'BigCommerce store hash. Can be found in the URL of your store Control Panel.',\n ).env('BIGCOMMERCE_STORE_HASH'),\n )\n .addOption(\n new Option(\n '--access-token <token>',\n 'BigCommerce access token. Can be found after creating a store-level API account.',\n ).env('BIGCOMMERCE_ACCESS_TOKEN'),\n )\n .addOption(\n new Option('--api-host <host>', 'BigCommerce API host. The default is api.bigcommerce.com.')\n .env('BIGCOMMERCE_API_HOST')\n .default('api.bigcommerce.com'),\n )\n .option(\n '--project-uuid <uuid>',\n 'BigCommerce infrastructure project UUID. Can be found via the BigCommerce API (GET /v3/infrastructure/projects). Use this to link directly without fetching projects.',\n )\n .option(\n '--root-dir <path>',\n 'Path to the root directory of your Catalyst project (default: current working directory).',\n process.cwd(),\n )\n .action(async (options) => {\n try {\n const config = getProjectConfig(options.rootDir);\n\n const writeProjectConfig = (uuid: string) => {\n consola.start('Writing project UUID to .bigcommerce/project.json...');\n config.set('projectUuid', uuid);\n config.set('framework', 'catalyst');\n consola.success('Project UUID written to .bigcommerce/project.json.');\n };\n\n if (options.projectUuid) {\n writeProjectConfig(options.projectUuid);\n\n process.exit(0);\n }\n\n if (options.storeHash && options.accessToken) {\n await telemetry.identify(options.storeHash);\n\n consola.start('Fetching projects...');\n\n const projects = await fetchProjects(\n options.storeHash,\n options.accessToken,\n options.apiHost,\n );\n\n consola.success('Projects fetched.');\n\n const promptOptions = [\n ...projects.map((project) => ({\n label: project.name,\n value: project.uuid,\n hint: project.uuid,\n })),\n {\n label: 'Create a new project',\n value: 'create',\n hint: 'Create a new infrastructure project for this BigCommerce store.',\n },\n ];\n\n let projectUuid = await consola.prompt(\n 'Select a project or create a new project (Press <enter> to select).',\n {\n type: 'select',\n options: promptOptions,\n cancel: 'reject',\n },\n );\n\n if (projectUuid === 'create') {\n const newProjectName = await consola.prompt('Enter a name for the new project:', {\n type: 'text',\n });\n\n const data = await createProject(\n newProjectName,\n options.storeHash,\n options.accessToken,\n options.apiHost,\n );\n\n projectUuid = data.uuid;\n\n consola.success(`Project \"${data.name}\" created successfully.`);\n }\n\n writeProjectConfig(projectUuid);\n\n process.exit(0);\n }\n\n consola.error('Insufficient information to link a project.');\n consola.info('Provide a project UUID with --project-uuid, or');\n consola.info('Provide both --store-hash and --access-token to fetch and select a project.');\n process.exit(1);\n } catch (error) {\n consola.error(error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n","import { Command, Option } from 'commander';\nimport { execa } from 'execa';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { consola } from '../lib/logger';\nimport { getProjectConfig } from '../lib/project-config';\n\nexport const start = new Command('start')\n .description('Start your Catalyst storefront in optimized production mode.')\n .allowUnknownOption(true)\n // The unknown options end up in program.args, not in program.opts(). Commander does not take a guess at how to interpret the unknown options.\n .argument(\n '[start-options...]',\n 'Pass additional options to the start command. If framework is Next.js, see https://nextjs.org/docs/api-reference/cli#start for available options.',\n )\n .addOption(\n new Option('--framework <framework>', 'The framework to use for the preview').choices([\n 'catalyst',\n 'nextjs',\n ]),\n )\n .action(async (startOptions, options) => {\n try {\n const config = getProjectConfig();\n const framework = options.framework ?? config.get('framework');\n\n if (framework === 'nextjs') {\n const nextBin = join('node_modules', '.bin', 'next');\n\n if (!existsSync(nextBin)) {\n throw new Error(\n `Next.js is not installed in ${process.cwd()}. Are you in a valid Next.js project?`,\n );\n }\n\n await execa(nextBin, ['start', ...startOptions], {\n stdio: 'inherit',\n cwd: process.cwd(),\n });\n }\n\n await execa(\n 'pnpm',\n [\n 'exec',\n 'opennextjs-cloudflare',\n 'preview',\n '--config',\n join('.bigcommerce', 'wrangler.jsonc'),\n ...startOptions,\n ],\n {\n stdio: 'inherit',\n cwd: process.cwd(),\n },\n );\n } catch (error) {\n consola.error(error instanceof Error ? error.message : error);\n }\n });\n","import { Argument, Command, Option } from 'commander';\nimport { colorize } from 'consola/utils';\n\nimport { consola } from '../lib/logger';\nimport { Telemetry } from '../lib/telemetry';\n\nconst telemetryService = new Telemetry();\nlet isEnabled = telemetryService.isEnabled();\n\nexport const telemetry = new Command('telemetry')\n .addArgument(new Argument('[arg]').choices(['disable', 'enable', 'status']))\n .addOption(new Option('--enable', `Enables CLI telemetry collection.`).conflicts('disable'))\n .option('--disable', `Disables CLI telemetry collection.`)\n .action((arg, options) => {\n if (options.enable || arg === 'enable') {\n telemetryService.setEnabled(true);\n isEnabled = true;\n\n consola.success('Success!\\n');\n } else if (options.disable || arg === 'disable') {\n telemetryService.setEnabled(false);\n\n if (isEnabled) {\n consola.success('Your preference has been saved to .bigcommerce/project.json');\n } else {\n consola.info(`Catalyst CLI telemetry collection is already disabled.`);\n }\n\n isEnabled = false;\n } else {\n consola.info('Catalyst CLI Telemetry\\n');\n }\n\n consola.info(\n `Status: ${colorize('bold', isEnabled ? colorize('green', 'Enabled') : colorize('red', 'Disabled'))}`,\n );\n\n if (!isEnabled) {\n consola.info(\n `You have opted-out of Catalyst CLI telemetry. No data will be collected from your machine.`,\n );\n }\n });\n","import { Command } from 'commander';\n\nimport PACKAGE_INFO from '../../../package.json';\nimport { consola } from '../lib/logger';\n\nexport const version = new Command('version')\n .description('Display detailed version information.')\n .action(() => {\n consola.log('Version Information:');\n consola.log(`CLI Version: ${PACKAGE_INFO.version}`);\n consola.log(`Node Version: ${process.version}`);\n consola.log(`Platform: ${process.platform} (${process.arch})`);\n });\n","import { Command } from '@commander-js/extra-typings';\n\nimport { Telemetry } from '../lib/telemetry';\n\nconst telemetry = new Telemetry();\n\nconst allowlistArguments = ['--keep-temp-dir', '--api-host', '--project-uuid'];\n\nfunction parseArguments(args: string[]) {\n return args.reduce<Record<string, string>>((result, arg, index, array) => {\n if (arg.includes('=')) {\n const [key, value] = arg.split('=');\n\n if (allowlistArguments.includes(key)) {\n return {\n ...result,\n [key]: value,\n };\n }\n }\n\n if (allowlistArguments.includes(arg)) {\n const nextValue =\n array[index + 1] && !array[index + 1].startsWith('--') ? array[index + 1] : null;\n\n if (nextValue && !nextValue.includes('--')) {\n return {\n ...result,\n [arg]: nextValue,\n };\n }\n }\n\n return result;\n }, {});\n}\n\nexport const telemetryPreHook = async (command: Command) => {\n const [commandName, ...args] = command.args;\n\n // Return the await to get a proper stack trace.\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression\n return await telemetry.track(commandName, {\n ...parseArguments(args),\n });\n};\n\nexport const telemetryPostHook = async () => {\n await telemetry.analytics.closeAndFlush();\n};\n","#!/usr/bin/env node\nimport { program } from './program';\n\nprogram.parse(process.argv);\n"],"mappings":"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","names":["Command","colorize","join","join","config","join","Command","Option","join","join","Command","Option","config","Command","execa","existsSync","join","Command","join","existsSync","execa","Command","Option","z","telemetry","z","Command","Option","config","Command","Option","execa","existsSync","join","Command","Option","config","join","existsSync","execa","Command","Option","telemetry","Command","Option","Command","Command","telemetry","Command","colorize","telemetry"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bigcommerce/catalyst",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"catalyst": "dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"templates"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": "^20.0.0 || ^22.0.0"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@segment/analytics-node": "^2.2.1",
|
|
17
|
+
"adm-zip": "^0.5.16",
|
|
18
|
+
"commander": "^14.0.0",
|
|
19
|
+
"conf": "^13.1.0",
|
|
20
|
+
"consola": "^3.4.2",
|
|
21
|
+
"dotenv": "^16.5.0",
|
|
22
|
+
"execa": "^9.6.0",
|
|
23
|
+
"yocto-spinner": "^1.0.0",
|
|
24
|
+
"zod": "^4.0.5"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@bigcommerce/eslint-config": "^2.11.0",
|
|
28
|
+
"@commander-js/extra-typings": "^14.0.0",
|
|
29
|
+
"@types/adm-zip": "^0.5.7",
|
|
30
|
+
"@types/node": "^22.15.30",
|
|
31
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
32
|
+
"@vitest/ui": "^3.2.4",
|
|
33
|
+
"eslint": "^8.57.1",
|
|
34
|
+
"msw": "^2.9.0",
|
|
35
|
+
"prettier": "^3.6.2",
|
|
36
|
+
"tsup": "^8.5.0",
|
|
37
|
+
"typescript": "^5.8.3",
|
|
38
|
+
"vitest": "^3.2.4",
|
|
39
|
+
"@bigcommerce/eslint-config-catalyst": "^1.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@opennextjs/cloudflare": "^1.8.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"dev": "tsup --watch",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"lint": "eslint . --max-warnings 0",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"test:coverage": "vitest run --coverage",
|
|
51
|
+
"build": "tsup"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineCloudflareConfig, type OpenNextConfig } from '@opennextjs/cloudflare';
|
|
2
|
+
import { purgeCache } from '@opennextjs/cloudflare/overrides/cache-purge/index';
|
|
3
|
+
import kvIncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache';
|
|
4
|
+
import doQueue from '@opennextjs/cloudflare/overrides/queue/do-queue';
|
|
5
|
+
import queueCache from '@opennextjs/cloudflare/overrides/queue/queue-cache';
|
|
6
|
+
import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache';
|
|
7
|
+
|
|
8
|
+
const cloudflareConfig = defineCloudflareConfig({
|
|
9
|
+
incrementalCache: kvIncrementalCache,
|
|
10
|
+
queue: queueCache(doQueue, {
|
|
11
|
+
regionalCacheTtlSec: 5,
|
|
12
|
+
}),
|
|
13
|
+
routePreloadingBehavior: 'withWaitUntil',
|
|
14
|
+
tagCache: doShardedTagCache({
|
|
15
|
+
baseShardSize: 12,
|
|
16
|
+
regionalCache: true,
|
|
17
|
+
regionalCacheTtlSec: 5,
|
|
18
|
+
shardReplication: {
|
|
19
|
+
numberOfSoftReplicas: 4,
|
|
20
|
+
numberOfHardReplicas: 2,
|
|
21
|
+
regionalReplication: {
|
|
22
|
+
defaultRegion: 'enam',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
enableCacheInterception: false,
|
|
27
|
+
cachePurge: purgeCache({ type: 'durableObject' }),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const config: OpenNextConfig = {
|
|
31
|
+
buildCommand: 'node_modules/.bin/next build',
|
|
32
|
+
...cloudflareConfig,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default config;
|