@by-association-only/cli 3.4.0 → 4.0.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/dist/index.cjs ADDED
@@ -0,0 +1,340 @@
1
+ var import_node_module = require("node:module");
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+
19
+ // packages/cli/src/commands/create.ts
20
+ var fs = __toESM(require("node:fs"));
21
+ var import_prompts2 = require("@clack/prompts");
22
+ var path = __toESM(require("node:path"));
23
+ var import_octokit = require("octokit");
24
+
25
+ // packages/cli/src/utils.ts
26
+ var PACKAGE_RUNNER_COMMAND = "bunx";
27
+ var GITHUB_INFO = {
28
+ organization: "baoagency",
29
+ templateRepository: "unisian"
30
+ };
31
+
32
+ // packages/cli/src/wrangler.ts
33
+ var import_prompts = require("@clack/prompts");
34
+
35
+ // packages/cli/src/constants.ts
36
+ var APP_REFERENCE_NAME = "Unisian";
37
+
38
+ // packages/cli/src/wrangler.ts
39
+ var import_promises = require("node:fs/promises");
40
+ async function runWranglerCommand(command, cwd) {
41
+ const child = Bun.spawn([PACKAGE_RUNNER_COMMAND, "wrangler@latest", ...command], {
42
+ stdin: "pipe",
43
+ stderr: "pipe",
44
+ cwd,
45
+ env: {
46
+ ...process.env,
47
+ CLOUDFLARE_ACCOUNT_ID: "824c12820335788e8daf77dba7e7891e"
48
+ }
49
+ });
50
+ const stdout = await new Response(child.stdout).text();
51
+ const stderr = await new Response(child.stderr).text();
52
+ const code = await child.exited;
53
+ return new Promise((resolve, reject) => {
54
+ if (code === 0) {
55
+ resolve({
56
+ code,
57
+ stdout,
58
+ stderr
59
+ });
60
+ }
61
+ reject({
62
+ code,
63
+ stdout,
64
+ stderr
65
+ });
66
+ });
67
+ }
68
+ async function ensureWranglerAuthenticated() {
69
+ try {
70
+ const result = await runWranglerCommand(["whoami"]);
71
+ return !result.stdout.includes("You are not authenticated");
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ async function wranglerLogin() {
77
+ const child = Bun.spawn([PACKAGE_RUNNER_COMMAND, "wrangler", "login"], { stdin: "inherit" });
78
+ const code = await child.exited;
79
+ return await new Promise((resolve, reject) => {
80
+ if (code === 0) {
81
+ resolve();
82
+ }
83
+ reject();
84
+ });
85
+ }
86
+ async function ensureWranglerSessionExists(s) {
87
+ s.message("Checking to make sure you have the Wrangler CLI installed and authenticated...");
88
+ if (!await ensureWranglerAuthenticated()) {
89
+ s.stop("Hmm. Looks like you're not logged in yet.");
90
+ const wantsToLogIn = await import_prompts.confirm({
91
+ message: `You need to be logged into wrangler to setup a ${APP_REFERENCE_NAME} app. Log in now?`
92
+ });
93
+ if (import_prompts.isCancel(wantsToLogIn) || !wantsToLogIn) {
94
+ import_prompts.cancel(`You need to be logged into wrangler to be able to setup a ${APP_REFERENCE_NAME} app.`);
95
+ process.exit(0);
96
+ }
97
+ await wranglerLogin();
98
+ }
99
+ }
100
+ async function writeWranglerConfig(location, config) {
101
+ return await import_promises.writeFile(location, JSON.stringify(config, null, 2));
102
+ }
103
+ async function createD1Database(name, cwd) {
104
+ try {
105
+ await runWranglerCommand(["d1", "create", name, "--binding=DB", "--update-config=true"], cwd);
106
+ return {
107
+ success: true,
108
+ message: `✅ D1 Database: ${name} created!`
109
+ };
110
+ } catch (error) {
111
+ return {
112
+ success: false,
113
+ message: `❌ D1 Database: ${error.stderr || error.stdout || error.message}`
114
+ };
115
+ }
116
+ }
117
+ async function createKVNamespace(name, cwd) {
118
+ try {
119
+ await runWranglerCommand(["kv", "namespace", "create", name, "--binding=BUCKET", "--update-config=true"], cwd);
120
+ return {
121
+ success: true,
122
+ message: `✅ KV Namespace: ${name} created!`
123
+ };
124
+ } catch (error) {
125
+ return {
126
+ success: false,
127
+ message: `❌ KV Namespace: ${error.stderr || error.stdout || error.message}`
128
+ };
129
+ }
130
+ }
131
+ async function createR2Bucket(name, cwd) {
132
+ try {
133
+ await runWranglerCommand(["r2", "bucket", "create", name, "--binding=BUCKET", "--update-config=true"], cwd);
134
+ return {
135
+ success: true,
136
+ message: `✅ KV Namespace: ${name} created!`
137
+ };
138
+ } catch (error) {
139
+ return {
140
+ success: false,
141
+ message: `❌ KV Namespace: ${error.stderr || error.stdout || error.message}`
142
+ };
143
+ }
144
+ }
145
+
146
+ // packages/cli/src/commands/create.ts
147
+ var import_picocolors = __toESM(require("picocolors"));
148
+ async function create() {
149
+ const initialContext = await initializeContext();
150
+ const context = await validateEnvironment(initialContext);
151
+ await createGithubRepository(context);
152
+ await cloneGithubRepository(context);
153
+ await provisionCloudflareResources(context);
154
+ await cleanup(context);
155
+ import_prompts2.note([
156
+ `1. cd ${context.localPath}`,
157
+ "2. shopify app config link",
158
+ "3. bun run dev"
159
+ ].join(`
160
+ `), `${import_picocolors.default.bgYellow(import_picocolors.default.black("Next steps."))}`);
161
+ import_prompts2.outro(`Go and build something ${import_picocolors.default.underline(import_picocolors.default.italic(import_picocolors.default.yellow("stupendous")))}.`);
162
+ }
163
+ async function initializeContext() {
164
+ import_prompts2.intro(`Setting up a new BAO ${APP_REFERENCE_NAME} project`);
165
+ const appName = await import_prompts2.text({
166
+ message: "What is the name of your project?",
167
+ placeholder: `my-${APP_REFERENCE_NAME.toLowerCase()}-project`,
168
+ validate: (value) => {
169
+ if (!value.match(/^[a-z0-9-]+$/)) {
170
+ return `Invalid app name: ${value}. App names can only contain lowercase letters, numbers, and dashes.`;
171
+ }
172
+ }
173
+ });
174
+ const repoName = `${String(appName)}-app`;
175
+ const localPath = `./${repoName}`;
176
+ if (fs.existsSync(localPath)) {
177
+ import_prompts2.log.error(`Unable to continue as ${localPath} already exists`);
178
+ process.exit(0);
179
+ }
180
+ const s = import_prompts2.spinner();
181
+ return {
182
+ appName: String(appName),
183
+ repoName,
184
+ localPath,
185
+ spinner: s
186
+ };
187
+ }
188
+ async function validateEnvironment(initialContext) {
189
+ initialContext.spinner.start("Welcome! Checking to make sure your environment is set up correctly.");
190
+ const hasEnvironmentAccessToken = typeof process.env.GITHUB_TOKEN !== "undefined" && process.env.GITHUB_TOKEN.trim() !== "";
191
+ if (!hasEnvironmentAccessToken) {
192
+ import_prompts2.log.warning(`Unable to find a GITHUB_TOKEN environment variable. Please go to https://github.com/settings/tokens/new and create an access token to be able to use the CLI.`);
193
+ }
194
+ const githubAccessToken = process.env.GITHUB_TOKEN || await import_prompts2.text({
195
+ message: "Access token"
196
+ });
197
+ try {
198
+ await testGithubAccessTokenIsValid(String(githubAccessToken));
199
+ } catch (error) {
200
+ import_prompts2.log.error(`Invalid access token: ${error.message}`);
201
+ process.exit(0);
202
+ }
203
+ await ensureWranglerSessionExists(initialContext.spinner);
204
+ initialContext.spinner.stop("Validated environment");
205
+ import_prompts2.note(`Before using R2, Queues, and Durable objects,
206
+ ` + `make sure you've enabled them in the Cloudflare Dashboard.
207
+ ` + `https://dash.cloudflare.com/
208
+ ` + "Otherwise, the following commands might fail! \uD83D\uDE2C", "\uD83D\uDC4B Heads-up:");
209
+ return {
210
+ ...initialContext,
211
+ githubPersonalAccessToken: String(githubAccessToken),
212
+ cloudflare: {}
213
+ };
214
+ }
215
+ async function testGithubAccessTokenIsValid(accessToken) {
216
+ const octokit = new import_octokit.Octokit({
217
+ auth: accessToken
218
+ });
219
+ const { data: { login } } = await octokit.rest.users.getAuthenticated();
220
+ return login;
221
+ }
222
+ async function createGithubRepository(context) {
223
+ context.spinner.start(`Creating GitHub Repository ${context.repoName}`);
224
+ const octokit = new import_octokit.Octokit({
225
+ auth: context.githubPersonalAccessToken
226
+ });
227
+ try {
228
+ const appRepo = await octokit.rest.repos.createUsingTemplate({
229
+ template_owner: GITHUB_INFO.organization,
230
+ template_repo: GITHUB_INFO.templateRepository,
231
+ owner: GITHUB_INFO.organization,
232
+ name: context.repoName,
233
+ private: true
234
+ });
235
+ context.repoHttpUrl = appRepo.data.html_url;
236
+ context.repoSshUrl = appRepo.data.ssh_url;
237
+ } catch (error) {
238
+ import_prompts2.log.error(`Unable to create Github Repository ${context.repoName}`);
239
+ import_prompts2.log.error(error);
240
+ process.exit(0);
241
+ }
242
+ context.spinner.stop("Github repository created! ⭐");
243
+ }
244
+ async function cloneGithubRepository(context) {
245
+ if (typeof context.repoSshUrl === "undefined") {
246
+ import_prompts2.log.error(`Unable to clone Github repository ${context.repoName} as can't find the .git URL`);
247
+ process.exit(0);
248
+ }
249
+ context.spinner.start(`Cloning ${context.repoName} from GitHub`);
250
+ await new Promise((resolve) => setTimeout(resolve, 5000));
251
+ const child = Bun.spawn(["git", "clone", context.repoSshUrl, context.localPath], {
252
+ stderr: "ignore",
253
+ stdout: "ignore"
254
+ });
255
+ await child.exited;
256
+ if (fs.existsSync(context.localPath)) {
257
+ context.spinner.stop(`Successfully cloned repository to ${context.localPath}`);
258
+ return;
259
+ }
260
+ context.spinner.stop();
261
+ import_prompts2.log.error(`Unable to clone repository`);
262
+ process.exit(0);
263
+ }
264
+ async function provisionCloudflareResources(context) {
265
+ import_prompts2.note(["Creating the following Cloudflare resources:", "- KV", "- D1", "- R2 Bucket"].join(`
266
+ `));
267
+ context.spinner.start(`Creating Cloudflare resources`);
268
+ const wranglerConfig = {
269
+ $schema: "node_modules/wrangler/config-schema.json",
270
+ name: context.appName,
271
+ compatibility_date: "2025-09-21",
272
+ compatibility_flags: ["nodejs_compat"],
273
+ main: "@tanstack/react-start/server-entry",
274
+ dev: {
275
+ ip: "0.0.0.0",
276
+ port: 8647
277
+ },
278
+ upload_source_maps: true,
279
+ placement: {
280
+ mode: "smart"
281
+ },
282
+ observability: {
283
+ enabled: true
284
+ }
285
+ };
286
+ await writeWranglerConfig(`${context.localPath}/wrangler.json`, wranglerConfig);
287
+ const results = [];
288
+ context.spinner.message(`Creating KV namespace`);
289
+ results.push(await createKVNamespace(context.appName, context.localPath));
290
+ context.spinner.message(`Creating D1 database`);
291
+ results.push(await createD1Database(context.appName, context.localPath));
292
+ context.spinner.message(`Creating R2 bucket`);
293
+ results.push(await createR2Bucket(context.appName, context.localPath));
294
+ for (const result of results) {
295
+ if (!result.success) {
296
+ import_prompts2.log.error(result.message);
297
+ process.exit(0);
298
+ }
299
+ }
300
+ context.spinner.stop("Cloudflare resources created and wrangler.json updated");
301
+ const allResults = results.map((r) => r.message);
302
+ import_prompts2.note(allResults.join(`
303
+ `), "Here's what we did:");
304
+ }
305
+ async function cleanup(context) {
306
+ context.spinner.start("Removing unnecessary files from the template");
307
+ import_prompts2.note("Removing default shopify.app.toml", "\uD83E\uDDF9 Cleaning up");
308
+ fs.unlinkSync(path.join(context.localPath, "shopify.app.toml"));
309
+ import_prompts2.note("Removing default shopify.app.production.toml", "\uD83E\uDDF9 Cleaning up");
310
+ fs.unlinkSync(path.join(context.localPath, "shopify.app.production.toml"));
311
+ context.spinner.message("Installing packages");
312
+ const packageInstall = Bun.spawn(["bun", "install"], {
313
+ stderr: "ignore",
314
+ stdout: "ignore",
315
+ cwd: context.localPath
316
+ });
317
+ await packageInstall.exited;
318
+ context.spinner.message("Finalising app setup");
319
+ const gitAdd = Bun.spawn(["git", "add", "."], {
320
+ stderr: "ignore",
321
+ stdout: "ignore",
322
+ cwd: context.localPath
323
+ });
324
+ await gitAdd.exited;
325
+ const gitCommit = Bun.spawn(["git", "commit", "-m", "App setup"], {
326
+ stderr: "ignore",
327
+ stdout: "ignore",
328
+ cwd: context.localPath
329
+ });
330
+ await gitCommit.exited;
331
+ context.spinner.stop();
332
+ }
333
+
334
+ // packages/cli/src/index.ts
335
+ (async () => {
336
+ await create();
337
+ })();
338
+
339
+ //# debugId=EB8FF545C7B695BE64756E2164756E21
340
+ //# sourceMappingURL=index.js.map