@01.software/init 0.9.2 → 0.10.1

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 (114) hide show
  1. package/dist/ai-docs.d.ts +13 -0
  2. package/dist/browser-auth-CJDrpp5T.d.ts +11 -0
  3. package/dist/{chunk-UA7WNT2F.js → chunk-4LHYICUL.js} +1 -1
  4. package/dist/chunk-4LHYICUL.js.map +1 -0
  5. package/dist/{chunk-R4GGO33X.js → chunk-NJ4X7VNK.js} +1 -1
  6. package/dist/{chunk-R4GGO33X.js.map → chunk-NJ4X7VNK.js.map} +1 -1
  7. package/dist/chunk-STM4DKVZ.js +183 -0
  8. package/dist/chunk-STM4DKVZ.js.map +1 -0
  9. package/dist/{chunk-ENQSB4OF.js → chunk-WDWJ73KP.js} +40 -214
  10. package/dist/chunk-WDWJ73KP.js.map +1 -0
  11. package/dist/create-app-templates/ecommerce/AGENTS.md +88 -0
  12. package/dist/create-app-templates/ecommerce/CHANGELOG.md +48 -0
  13. package/dist/create-app-templates/ecommerce/CLAUDE.md +1 -0
  14. package/dist/create-app-templates/ecommerce/README.md +154 -0
  15. package/dist/create-app-templates/ecommerce/app/api/auth/login/route.ts +30 -0
  16. package/dist/create-app-templates/ecommerce/app/api/auth/logout/route.ts +18 -0
  17. package/dist/create-app-templates/ecommerce/app/api/auth/register/route.ts +41 -0
  18. package/dist/create-app-templates/ecommerce/app/api/cart/clear/route.ts +12 -0
  19. package/dist/create-app-templates/ecommerce/app/api/cart/items/route.ts +45 -0
  20. package/dist/create-app-templates/ecommerce/app/api/cart/route.ts +14 -0
  21. package/dist/create-app-templates/ecommerce/app/api/checkout/payment-return/route.ts +86 -0
  22. package/dist/create-app-templates/ecommerce/app/api/checkout/reconcile/route.ts +50 -0
  23. package/dist/create-app-templates/ecommerce/app/api/checkout/route.ts +41 -0
  24. package/dist/create-app-templates/ecommerce/app/cart/page.tsx +10 -0
  25. package/dist/create-app-templates/ecommerce/app/checkout/page.tsx +10 -0
  26. package/dist/create-app-templates/ecommerce/app/checkout/success/page.tsx +34 -0
  27. package/dist/create-app-templates/ecommerce/app/favicon.ico +0 -0
  28. package/dist/create-app-templates/ecommerce/app/globals.css +67 -0
  29. package/dist/create-app-templates/ecommerce/app/layout.tsx +23 -0
  30. package/dist/create-app-templates/ecommerce/app/login/page.tsx +11 -0
  31. package/dist/create-app-templates/ecommerce/app/page.tsx +5 -0
  32. package/dist/create-app-templates/ecommerce/app/products/[slug]/page.tsx +46 -0
  33. package/dist/create-app-templates/ecommerce/app/products/page.tsx +45 -0
  34. package/dist/create-app-templates/ecommerce/app/register/page.tsx +11 -0
  35. package/dist/create-app-templates/ecommerce/app/webhook/payment/route.ts +20 -0
  36. package/dist/create-app-templates/ecommerce/app-config.ts +54 -0
  37. package/dist/create-app-templates/ecommerce/components/auth/auth-form.tsx +109 -0
  38. package/dist/create-app-templates/ecommerce/components/cart/cart-content.tsx +129 -0
  39. package/dist/create-app-templates/ecommerce/components/checkout/checkout-form.tsx +307 -0
  40. package/dist/create-app-templates/ecommerce/components/checkout/checkout-reconcile.tsx +78 -0
  41. package/dist/create-app-templates/ecommerce/components/layout/account-nav.tsx +48 -0
  42. package/dist/create-app-templates/ecommerce/components/layout/account-slot.tsx +12 -0
  43. package/dist/create-app-templates/ecommerce/components/layout/cart-link.tsx +13 -0
  44. package/dist/create-app-templates/ecommerce/components/layout/page-shell.tsx +11 -0
  45. package/dist/create-app-templates/ecommerce/components/layout/site-header.tsx +22 -0
  46. package/dist/create-app-templates/ecommerce/components/product/add-to-cart.tsx +116 -0
  47. package/dist/create-app-templates/ecommerce/components/product/product-card.tsx +50 -0
  48. package/dist/create-app-templates/ecommerce/components/product/product-gallery.tsx +39 -0
  49. package/dist/create-app-templates/ecommerce/data/mock-catalog.json +173 -0
  50. package/dist/create-app-templates/ecommerce/eslint.config.mjs +18 -0
  51. package/dist/create-app-templates/ecommerce/lib/cart/cookie.ts +40 -0
  52. package/dist/create-app-templates/ecommerce/lib/cart/normalize.ts +32 -0
  53. package/dist/create-app-templates/ecommerce/lib/cart/parse-cart-request.ts +56 -0
  54. package/dist/create-app-templates/ecommerce/lib/cart/route-helpers.ts +17 -0
  55. package/dist/create-app-templates/ecommerce/lib/cart/select-provider.ts +44 -0
  56. package/dist/create-app-templates/ecommerce/lib/cart/server-cart.ts +135 -0
  57. package/dist/create-app-templates/ecommerce/lib/cart/sync-on-login.server.ts +34 -0
  58. package/dist/create-app-templates/ecommerce/lib/cart/use-cart.tsx +151 -0
  59. package/dist/create-app-templates/ecommerce/lib/checkout/checkout-errors.ts +22 -0
  60. package/dist/create-app-templates/ecommerce/lib/checkout/checkout-provider.ts +28 -0
  61. package/dist/create-app-templates/ecommerce/lib/checkout/parse-checkout-payload.ts +86 -0
  62. package/dist/create-app-templates/ecommerce/lib/checkout/start-checkout.ts +73 -0
  63. package/dist/create-app-templates/ecommerce/lib/checkout/types.ts +6 -0
  64. package/dist/create-app-templates/ecommerce/lib/commerce/adapters/mock.ts +346 -0
  65. package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software-mappers.ts +312 -0
  66. package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software.ts +930 -0
  67. package/dist/create-app-templates/ecommerce/lib/commerce/product-summary.ts +37 -0
  68. package/dist/create-app-templates/ecommerce/lib/commerce/provider.server.ts +60 -0
  69. package/dist/create-app-templates/ecommerce/lib/commerce/provider.ts +96 -0
  70. package/dist/create-app-templates/ecommerce/lib/commerce/stock.ts +37 -0
  71. package/dist/create-app-templates/ecommerce/lib/commerce/types.ts +208 -0
  72. package/dist/create-app-templates/ecommerce/lib/commerce/variant-selection.ts +23 -0
  73. package/dist/create-app-templates/ecommerce/lib/customer/auth-actions.ts +131 -0
  74. package/dist/create-app-templates/ecommerce/lib/customer/cart-sync.ts +44 -0
  75. package/dist/create-app-templates/ecommerce/lib/customer/client.server.ts +109 -0
  76. package/dist/create-app-templates/ecommerce/lib/customer/current-customer.ts +15 -0
  77. package/dist/create-app-templates/ecommerce/lib/customer/route-guard.ts +58 -0
  78. package/dist/create-app-templates/ecommerce/lib/customer/route-helpers.ts +75 -0
  79. package/dist/create-app-templates/ecommerce/lib/customer/session.ts +108 -0
  80. package/dist/create-app-templates/ecommerce/lib/format.ts +7 -0
  81. package/dist/create-app-templates/ecommerce/lib/payment/adapters/mock.ts +84 -0
  82. package/dist/create-app-templates/ecommerce/lib/payment/adapters/portone.ts +254 -0
  83. package/dist/create-app-templates/ecommerce/lib/payment/adapters/tosspayments.ts +287 -0
  84. package/dist/create-app-templates/ecommerce/lib/payment/amount-gate.ts +86 -0
  85. package/dist/create-app-templates/ecommerce/lib/payment/provider.server.ts +51 -0
  86. package/dist/create-app-templates/ecommerce/lib/payment/provider.ts +18 -0
  87. package/dist/create-app-templates/ecommerce/lib/payment/sync-order-payment.ts +96 -0
  88. package/dist/create-app-templates/ecommerce/lib/payment/types.ts +71 -0
  89. package/dist/create-app-templates/ecommerce/lib/server-only-guard.ts +20 -0
  90. package/dist/create-app-templates/ecommerce/next-env.d.ts +6 -0
  91. package/dist/create-app-templates/ecommerce/next.config.ts +16 -0
  92. package/dist/create-app-templates/ecommerce/package.json +33 -0
  93. package/dist/create-app-templates/ecommerce/postcss.config.mjs +7 -0
  94. package/dist/create-app-templates/ecommerce/tests/customer-auth.test.ts +263 -0
  95. package/dist/create-app-templates/ecommerce/tests/customer-cart.test.ts +401 -0
  96. package/dist/create-app-templates/ecommerce/tests/domain.test.ts +1632 -0
  97. package/dist/create-app-templates/ecommerce/tsconfig.json +35 -0
  98. package/dist/create-app-templates/registry.json +66 -0
  99. package/dist/create-app.d.ts +40 -0
  100. package/dist/create-app.js +652 -0
  101. package/dist/create-app.js.map +1 -0
  102. package/dist/detect-Bjxp9wcS.d.ts +13 -0
  103. package/dist/file-ops.d.ts +21 -0
  104. package/dist/file-ops.js +1 -1
  105. package/dist/index.d.ts +2 -0
  106. package/dist/index.js +4 -3
  107. package/dist/index.js.map +1 -1
  108. package/dist/init.d.ts +40 -0
  109. package/dist/init.js +4 -3
  110. package/dist/templates.d.ts +27 -0
  111. package/dist/templates.js +1 -1
  112. package/package.json +18 -3
  113. package/dist/chunk-ENQSB4OF.js.map +0 -1
  114. package/dist/chunk-UA7WNT2F.js.map +0 -1
@@ -0,0 +1,652 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ startBrowserAuth
4
+ } from "./chunk-STM4DKVZ.js";
5
+ import {
6
+ writeEnvFile
7
+ } from "./chunk-4LHYICUL.js";
8
+
9
+ // src/create-app.ts
10
+ import { execSync } from "child_process";
11
+ import fs2 from "fs";
12
+ import path2 from "path";
13
+ import { fileURLToPath } from "url";
14
+ import pc from "picocolors";
15
+ import prompts from "prompts";
16
+
17
+ // src/template-registry.ts
18
+ import fs from "fs";
19
+ import path from "path";
20
+ var REGISTRY_FILE_NAME = "registry.json";
21
+ function readTemplateRegistry(registryDir) {
22
+ const file = path.join(registryDir, REGISTRY_FILE_NAME);
23
+ const registry = JSON.parse(fs.readFileSync(file, "utf-8"));
24
+ if (!Array.isArray(registry.templates) || registry.templates.length === 0) {
25
+ throw new Error(`Template registry has no templates: ${file}`);
26
+ }
27
+ if (!registry.sdk?.package) {
28
+ throw new Error(`Template registry is missing sdk.package: ${file}`);
29
+ }
30
+ for (const template of registry.templates) {
31
+ if (!template.paymentProviders?.length) {
32
+ throw new Error(
33
+ `Template "${template.name}" has no payment providers in ${file}`
34
+ );
35
+ }
36
+ if (!findPaymentProvider(template, template.defaultPaymentProvider)) {
37
+ throw new Error(
38
+ `Template "${template.name}" defaultPaymentProvider "${template.defaultPaymentProvider}" is not a registered provider in ${file}`
39
+ );
40
+ }
41
+ for (const feature of template.features ?? []) {
42
+ if (!feature.id || !feature.marker || !Array.isArray(feature.paths)) {
43
+ throw new Error(
44
+ `Template "${template.name}" has a malformed feature (needs id, marker, paths) in ${file}`
45
+ );
46
+ }
47
+ }
48
+ }
49
+ return registry;
50
+ }
51
+ function findTemplate(registry, name) {
52
+ return registry.templates.find((template) => template.name === name);
53
+ }
54
+ function findPaymentProvider(template, id) {
55
+ return template.paymentProviders.find((provider) => provider.id === id);
56
+ }
57
+ function findFeature(template, id) {
58
+ return template.features?.find((feature) => feature.id === id);
59
+ }
60
+
61
+ // src/create-app.ts
62
+ var PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
63
+ var PACKAGE_MANAGER_VALUES = new Set(PACKAGE_MANAGERS);
64
+ var AUTH_VALUES = /* @__PURE__ */ new Set(["browser", "manual", "skip"]);
65
+ async function createApp(rawOptions = {}, deps = {}) {
66
+ const options = await resolveOptions(rawOptions);
67
+ const projectDir = path2.resolve(options.projectDir);
68
+ const sourceDir = path2.join(options.templateRoot, options.template.dir);
69
+ if (!fs2.existsSync(sourceDir)) {
70
+ throw new Error(`Template not found: ${options.template.name}`);
71
+ }
72
+ assertTargetAvailable(projectDir);
73
+ fs2.mkdirSync(projectDir, { recursive: true });
74
+ copyTemplate(sourceDir, projectDir);
75
+ rewritePackageName(projectDir);
76
+ applyPaymentProvider(projectDir, options);
77
+ applyCustomerAccounts(projectDir, options);
78
+ writeAppConfig(projectDir, options);
79
+ writeEnvExample(projectDir, options);
80
+ let tenantName;
81
+ if (options.auth === "browser") {
82
+ const creds = await (deps.startBrowserAuth ?? startBrowserAuth)();
83
+ writeSdkEnv(projectDir, creds.publishableKey, creds.secretKey);
84
+ tenantName = creds.tenantName;
85
+ } else if (options.auth === "manual") {
86
+ writeSdkEnv(projectDir, options.publishableKey, options.secretKey);
87
+ }
88
+ let installed = false;
89
+ if (options.install) {
90
+ const cmd = installCommand(options.packageManager);
91
+ const runInstall = deps.runInstall ?? ((installCmd, cwd) => execSync(installCmd, { cwd, stdio: "inherit" }));
92
+ runInstall(cmd, projectDir);
93
+ installed = true;
94
+ }
95
+ return {
96
+ projectDir,
97
+ template: options.template.name,
98
+ paymentProvider: options.provider.id,
99
+ packageManager: options.packageManager,
100
+ installed,
101
+ tenantName
102
+ };
103
+ }
104
+ async function resolveOptions(raw) {
105
+ const onCancel = () => {
106
+ throw new Error("cancelled");
107
+ };
108
+ const templateRoot = raw.templateRoot ?? defaultTemplateRoot();
109
+ const registry = readTemplateRegistry(templateRoot);
110
+ let projectDir = raw.projectDir;
111
+ if (!projectDir) {
112
+ const { name } = await prompts(
113
+ {
114
+ type: "text",
115
+ name: "name",
116
+ message: "Project directory",
117
+ initial: "my-01-store"
118
+ },
119
+ { onCancel }
120
+ );
121
+ projectDir = name;
122
+ }
123
+ const template = await resolveTemplate(registry, raw.template, onCancel);
124
+ const provider = await resolveProvider(
125
+ template,
126
+ raw.paymentProvider,
127
+ onCancel
128
+ );
129
+ const customerAccounts = await resolveCustomerAccounts(
130
+ template,
131
+ raw.customerAccounts,
132
+ onCancel
133
+ );
134
+ let packageManager = raw.packageManager;
135
+ if (!packageManager) {
136
+ const { pm } = await prompts(
137
+ {
138
+ type: "select",
139
+ name: "pm",
140
+ message: "Package manager",
141
+ choices: PACKAGE_MANAGERS.map((value) => ({ title: value, value })),
142
+ initial: 0
143
+ },
144
+ { onCancel }
145
+ );
146
+ packageManager = pm;
147
+ }
148
+ let auth = raw.auth;
149
+ if (!auth) {
150
+ const { mode } = await prompts(
151
+ {
152
+ type: "select",
153
+ name: "mode",
154
+ message: "Connect to a 01.software tenant now?",
155
+ choices: [
156
+ { title: "Browser login (recommended)", value: "browser" },
157
+ { title: "Enter keys manually", value: "manual" },
158
+ { title: "Skip and use demo data", value: "skip" }
159
+ ],
160
+ initial: 0
161
+ },
162
+ { onCancel }
163
+ );
164
+ auth = mode;
165
+ }
166
+ let publishableKey = raw.publishableKey ?? "";
167
+ let secretKey = raw.secretKey ?? "";
168
+ if (auth === "manual" && (!publishableKey || !secretKey)) {
169
+ const keys = await prompts(
170
+ [
171
+ {
172
+ type: "text",
173
+ name: "publishableKey",
174
+ message: "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY",
175
+ initial: publishableKey
176
+ },
177
+ {
178
+ type: "password",
179
+ name: "secretKey",
180
+ message: "SOFTWARE_SECRET_KEY",
181
+ initial: secretKey
182
+ }
183
+ ],
184
+ { onCancel }
185
+ );
186
+ publishableKey = keys.publishableKey ?? "";
187
+ secretKey = keys.secretKey ?? "";
188
+ }
189
+ if (!projectDir?.trim()) {
190
+ throw new Error("Project directory is required.");
191
+ }
192
+ projectDir = projectDir.trim();
193
+ if (!packageManager) {
194
+ throw new Error("Package manager is required.");
195
+ }
196
+ if (!auth) {
197
+ throw new Error("Auth mode is required.");
198
+ }
199
+ if (auth === "manual" && (!publishableKey.trim() || !secretKey.trim())) {
200
+ throw new Error(
201
+ "Manual auth requires NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY and SOFTWARE_SECRET_KEY. Use --auth skip for demo data."
202
+ );
203
+ }
204
+ return {
205
+ projectDir,
206
+ templateRoot,
207
+ registry,
208
+ template,
209
+ provider,
210
+ customerAccounts,
211
+ packageManager,
212
+ auth,
213
+ publishableKey,
214
+ secretKey,
215
+ install: raw.install ?? true
216
+ };
217
+ }
218
+ async function resolveTemplate(registry, requested, onCancel) {
219
+ if (requested) {
220
+ const template2 = findTemplate(registry, requested);
221
+ if (!template2) {
222
+ throw new Error(`Invalid template: ${requested}`);
223
+ }
224
+ return template2;
225
+ }
226
+ if (registry.templates.length === 1) {
227
+ return registry.templates[0];
228
+ }
229
+ const { selected } = await prompts(
230
+ {
231
+ type: "select",
232
+ name: "selected",
233
+ message: "Choose a starter",
234
+ choices: registry.templates.map((template2) => ({
235
+ title: template2.title,
236
+ value: template2.name
237
+ })),
238
+ initial: 0
239
+ },
240
+ { onCancel }
241
+ );
242
+ const template = findTemplate(registry, selected);
243
+ if (!template) {
244
+ throw new Error("Template is required.");
245
+ }
246
+ return template;
247
+ }
248
+ async function resolveProvider(template, requested, onCancel) {
249
+ if (requested) {
250
+ const provider2 = findPaymentProvider(template, requested);
251
+ if (!provider2) {
252
+ throw new Error(`Invalid payment provider: ${requested}`);
253
+ }
254
+ return provider2;
255
+ }
256
+ if (template.paymentProviders.length === 1) {
257
+ return template.paymentProviders[0];
258
+ }
259
+ const { selected } = await prompts(
260
+ {
261
+ type: "select",
262
+ name: "selected",
263
+ message: "Choose a payment provider",
264
+ choices: template.paymentProviders.map((provider2) => ({
265
+ title: provider2.label,
266
+ value: provider2.id
267
+ })),
268
+ initial: defaultProviderIndex(template)
269
+ },
270
+ { onCancel }
271
+ );
272
+ const provider = findPaymentProvider(template, selected);
273
+ if (!provider) {
274
+ throw new Error("Payment provider is required.");
275
+ }
276
+ return provider;
277
+ }
278
+ async function resolveCustomerAccounts(template, requested, onCancel) {
279
+ const feature = findFeature(template, "customer-accounts");
280
+ if (!feature) return false;
281
+ if (requested !== void 0) return requested;
282
+ if (!process.stdin.isTTY) return feature.defaultEnabled ?? false;
283
+ const { include } = await prompts(
284
+ {
285
+ type: "confirm",
286
+ name: "include",
287
+ message: `Include ${feature.label}? Requires a starter+ plan; guest checkout works without it.`,
288
+ initial: feature.defaultEnabled ?? false
289
+ },
290
+ { onCancel }
291
+ );
292
+ return Boolean(include);
293
+ }
294
+ function defaultProviderIndex(template) {
295
+ const index = template.paymentProviders.findIndex(
296
+ (provider) => provider.id === template.defaultPaymentProvider
297
+ );
298
+ return index === -1 ? 0 : index;
299
+ }
300
+ function defaultTemplateRoot() {
301
+ return path2.join(path2.dirname(fileURLToPath(import.meta.url)), "create-app-templates");
302
+ }
303
+ function assertTargetAvailable(projectDir) {
304
+ if (!fs2.existsSync(projectDir)) return;
305
+ const entries = fs2.readdirSync(projectDir).filter((entry) => entry !== ".DS_Store");
306
+ if (entries.length > 0) {
307
+ throw new Error(`Target directory is not empty: ${projectDir}`);
308
+ }
309
+ }
310
+ function copyTemplate(sourceDir, targetDir) {
311
+ const ignored = /* @__PURE__ */ new Set([
312
+ "node_modules",
313
+ ".next",
314
+ ".turbo",
315
+ "dist",
316
+ ".DS_Store",
317
+ ".mock-orders.json",
318
+ ".mock-payments.json"
319
+ ]);
320
+ for (const entry of fs2.readdirSync(sourceDir, { withFileTypes: true })) {
321
+ if (ignored.has(entry.name)) continue;
322
+ const source = path2.join(sourceDir, entry.name);
323
+ const target = path2.join(targetDir, entry.name);
324
+ if (entry.isDirectory()) {
325
+ fs2.mkdirSync(target, { recursive: true });
326
+ copyTemplate(source, target);
327
+ } else if (entry.isFile()) {
328
+ fs2.mkdirSync(path2.dirname(target), { recursive: true });
329
+ fs2.copyFileSync(source, target);
330
+ }
331
+ }
332
+ }
333
+ function rewritePackageName(projectDir) {
334
+ const packagePath = path2.join(projectDir, "package.json");
335
+ const packageJson = JSON.parse(fs2.readFileSync(packagePath, "utf-8"));
336
+ packageJson.name = packageNameFromDir(projectDir);
337
+ packageJson.version = "0.1.0";
338
+ fs2.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}
339
+ `);
340
+ }
341
+ function applyPaymentProvider(projectDir, options) {
342
+ const others = options.template.paymentProviders.filter(
343
+ (provider) => provider.id !== options.provider.id
344
+ );
345
+ for (const provider of others) {
346
+ fs2.rmSync(path2.join(projectDir, provider.adapterFile), { force: true });
347
+ }
348
+ stripProviderMarkers(
349
+ projectDir,
350
+ others.map((provider) => provider.id)
351
+ );
352
+ pruneDependencies(
353
+ projectDir,
354
+ others.flatMap((provider) => provider.dependencies),
355
+ options.registry.sdk.package
356
+ );
357
+ }
358
+ function stripProviderMarkers(projectDir, otherProviderIds) {
359
+ for (const file of walkSourceFiles(projectDir)) {
360
+ const content = fs2.readFileSync(file, "utf-8");
361
+ if (!content.includes("scaffold:provider:")) continue;
362
+ let next = content;
363
+ for (const id of otherProviderIds) {
364
+ next = stripMarkerBlocks(next, id);
365
+ }
366
+ next = removeMarkerLines(next);
367
+ if (next !== content) fs2.writeFileSync(file, next);
368
+ }
369
+ }
370
+ function applyCustomerAccounts(projectDir, options) {
371
+ const feature = findFeature(options.template, "customer-accounts");
372
+ if (!feature) return;
373
+ if (!options.customerAccounts) {
374
+ for (const rel of feature.paths) {
375
+ fs2.rmSync(path2.join(projectDir, rel), { recursive: true, force: true });
376
+ }
377
+ }
378
+ const tagPrefix = `scaffold:${feature.marker}:`;
379
+ const linePattern = new RegExp(`scaffold:${feature.marker}:(?:start|end)\\b`);
380
+ const transform = options.customerAccounts ? (content) => removeMarkerLinesFor(content, linePattern) : (content) => stripMarkerBlocksByTag(content, feature.marker);
381
+ const files = walkSourceFiles(projectDir);
382
+ const agentsMd = path2.join(projectDir, "AGENTS.md");
383
+ if (fs2.existsSync(agentsMd)) files.push(agentsMd);
384
+ for (const file of files) {
385
+ const content = fs2.readFileSync(file, "utf-8");
386
+ if (!content.includes(tagPrefix)) continue;
387
+ const next = transform(content);
388
+ if (next !== content) fs2.writeFileSync(file, next);
389
+ }
390
+ }
391
+ var MARKER_LINE = /scaffold:provider:[\w-]+:(?:start|end)\b/;
392
+ function removeMarkerLinesFor(content, pattern) {
393
+ return content.split("\n").filter((line) => !pattern.test(line)).join("\n");
394
+ }
395
+ function removeMarkerLines(content) {
396
+ return removeMarkerLinesFor(content, MARKER_LINE);
397
+ }
398
+ function stripMarkerBlocks(content, providerId) {
399
+ return stripMarkerBlocksByTag(content, `provider:${providerId}`);
400
+ }
401
+ function stripMarkerBlocksByTag(content, tag) {
402
+ const startMarker = `scaffold:${tag}:start`;
403
+ const endMarker = `scaffold:${tag}:end`;
404
+ const lines = content.split("\n");
405
+ const result = [];
406
+ let skipping = false;
407
+ for (const line of lines) {
408
+ if (!skipping && line.includes(startMarker)) {
409
+ skipping = true;
410
+ continue;
411
+ }
412
+ if (skipping) {
413
+ if (line.includes(endMarker)) skipping = false;
414
+ continue;
415
+ }
416
+ result.push(line);
417
+ }
418
+ if (skipping) {
419
+ throw new Error(
420
+ `Unterminated scaffold marker: missing "${endMarker}" to close "${startMarker}"`
421
+ );
422
+ }
423
+ return result.join("\n");
424
+ }
425
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
426
+ var WALK_IGNORED = /* @__PURE__ */ new Set(["node_modules", ".next", ".turbo", "dist", ".git"]);
427
+ function walkSourceFiles(dir) {
428
+ const files = [];
429
+ for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
430
+ if (WALK_IGNORED.has(entry.name)) continue;
431
+ const full = path2.join(dir, entry.name);
432
+ if (entry.isDirectory()) {
433
+ files.push(...walkSourceFiles(full));
434
+ } else if (entry.isFile() && SOURCE_EXTENSIONS.has(path2.extname(entry.name))) {
435
+ files.push(full);
436
+ }
437
+ }
438
+ return files;
439
+ }
440
+ function pruneDependencies(projectDir, depsToRemove, sdkPackage) {
441
+ const packagePath = path2.join(projectDir, "package.json");
442
+ const packageJson = JSON.parse(fs2.readFileSync(packagePath, "utf-8"));
443
+ const dependencies = packageJson.dependencies;
444
+ if (dependencies) {
445
+ for (const dep of depsToRemove) delete dependencies[dep];
446
+ const sdkRange = dependencies[sdkPackage];
447
+ if (sdkRange) dependencies[sdkPackage] = sdkRange.replace(/^[\^~]/, "");
448
+ }
449
+ fs2.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}
450
+ `);
451
+ }
452
+ function writeAppConfig(projectDir, options) {
453
+ const brand = humanize(path2.basename(projectDir));
454
+ const slug = packageNameFromDir(projectDir);
455
+ const cartKey = `${slug}-cart`;
456
+ const customerKey = `${slug}-customer`;
457
+ const providerId = options.provider.id;
458
+ const label = options.provider.label;
459
+ const customerKeyField = options.customerAccounts ? `
460
+ /** Cookie name holding the HttpOnly customer session JWT. */
461
+ customerKey: string;` : "";
462
+ const customerKeyValue = options.customerAccounts ? `
463
+ customerKey: ${JSON.stringify(customerKey)},` : "";
464
+ const content = `/**
465
+ * App configuration generated by create-01-software-app.
466
+ *
467
+ * Generic source files read their per-app values from here so they stay
468
+ * consistent across the app. Edit freely \u2014 this is your project's config.
469
+ */
470
+ export type PaymentProviderId = ${JSON.stringify(providerId)};
471
+
472
+ export interface AppConfig {
473
+ /** Brand name shown in the storefront chrome. */
474
+ brand: string;
475
+ /** Document title for the app. */
476
+ title: string;
477
+ /** Cookie name holding the HttpOnly guest-cart ownership token. */
478
+ cartKey: string;${customerKeyField}
479
+ /** Payment provider this app was scaffolded for. */
480
+ paymentProvider: PaymentProviderId;
481
+ }
482
+
483
+ export const appConfig: AppConfig = {
484
+ brand: ${JSON.stringify(brand)},
485
+ title: ${JSON.stringify(brand)},
486
+ cartKey: ${JSON.stringify(cartKey)},${customerKeyValue}
487
+ paymentProvider: ${JSON.stringify(providerId)},
488
+ };
489
+
490
+ export const PAYMENT_PROVIDER_LABELS: Record<PaymentProviderId, string> = {
491
+ ${JSON.stringify(providerId)}: ${JSON.stringify(label)},
492
+ };
493
+
494
+ export function paymentProviderLabel(
495
+ provider: PaymentProviderId = appConfig.paymentProvider,
496
+ ): string {
497
+ return PAYMENT_PROVIDER_LABELS[provider];
498
+ }
499
+ `;
500
+ fs2.writeFileSync(path2.join(projectDir, "app-config.ts"), content);
501
+ }
502
+ function humanize(name) {
503
+ const words = name.replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1));
504
+ return words.join(" ") || "My 01 Store";
505
+ }
506
+ function packageNameFromDir(projectDir) {
507
+ const name = path2.basename(projectDir);
508
+ const normalized = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
509
+ return normalized || "my-01-store";
510
+ }
511
+ function writeSdkEnv(projectDir, publishableKey, secretKey) {
512
+ writeEnvFile(
513
+ path2.join(projectDir, ".env.local"),
514
+ [
515
+ "# 01.software",
516
+ `NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY=${publishableKey}`,
517
+ `SOFTWARE_SECRET_KEY=${secretKey}`,
518
+ ""
519
+ ].join("\n")
520
+ );
521
+ }
522
+ function writeEnvExample(projectDir, options) {
523
+ const lines = ["# 01.software"];
524
+ for (const entry of options.registry.sdk.env) {
525
+ lines.push(`${entry.key}=${entry.value ?? ""}`);
526
+ }
527
+ lines.push("", `# ${options.provider.label}`);
528
+ for (const entry of options.provider.env) {
529
+ lines.push(`${entry.key}=${entry.value ?? ""}`);
530
+ }
531
+ lines.push("");
532
+ fs2.writeFileSync(path2.join(projectDir, ".env.local.example"), `${lines.join("\n")}
533
+ `);
534
+ }
535
+ function installCommand(packageManager) {
536
+ if (packageManager === "yarn") return "yarn install";
537
+ if (packageManager === "bun") return "bun install";
538
+ return `${packageManager} install`;
539
+ }
540
+ function parseArgs(argv) {
541
+ const options = {};
542
+ const rest = [];
543
+ for (let index = 0; index < argv.length; index += 1) {
544
+ const arg = argv[index];
545
+ if (arg === "--help" || arg === "-h") {
546
+ options.help = true;
547
+ } else if (arg === "--template") {
548
+ options.template = argv[++index];
549
+ } else if (arg === "--adapter" || arg === "--provider") {
550
+ options.paymentProvider = argv[++index];
551
+ } else if (arg === "--customer-accounts") {
552
+ options.customerAccounts = true;
553
+ } else if (arg === "--no-customer-accounts") {
554
+ options.customerAccounts = false;
555
+ } else if (arg === "--package-manager") {
556
+ const value = argv[++index];
557
+ if (!isPackageManager(value)) {
558
+ throw new Error(`Invalid package manager: ${value}`);
559
+ }
560
+ options.packageManager = value;
561
+ } else if (arg === "--auth") {
562
+ const value = argv[++index];
563
+ if (!isAuthMode(value)) {
564
+ throw new Error(`Invalid auth mode: ${value}`);
565
+ }
566
+ options.auth = value;
567
+ } else if (arg === "--publishable-key") {
568
+ options.publishableKey = argv[++index];
569
+ } else if (arg === "--secret-key") {
570
+ options.secretKey = argv[++index];
571
+ } else if (arg === "--no-install") {
572
+ options.install = false;
573
+ } else if (arg.startsWith("--")) {
574
+ throw new Error(`Unknown option: ${arg}`);
575
+ } else {
576
+ rest.push(arg);
577
+ }
578
+ }
579
+ if (rest[0]) options.projectDir = rest[0];
580
+ return options;
581
+ }
582
+ function isPackageManager(value) {
583
+ return PACKAGE_MANAGER_VALUES.has(value);
584
+ }
585
+ function isAuthMode(value) {
586
+ return AUTH_VALUES.has(value);
587
+ }
588
+ function printHelp() {
589
+ console.log(`
590
+ create-01-software-app
591
+
592
+ Usage:
593
+ create-01-software-app [project-dir] [options]
594
+
595
+ Options:
596
+ --template <name> template name from templates/registry.json
597
+ --adapter <id> payment provider id (alias: --provider)
598
+ --customer-accounts include customer accounts (login/register; starter+)
599
+ --no-customer-accounts guest checkout only (default)
600
+ --package-manager pnpm|npm|yarn|bun
601
+ --auth browser|manual|skip
602
+ --publishable-key <key>
603
+ --secret-key <key>
604
+ --no-install
605
+ `);
606
+ }
607
+ async function runCreateAppCli(argv = process.argv.slice(2)) {
608
+ const options = parseArgs(argv);
609
+ if (options.help) {
610
+ printHelp();
611
+ return;
612
+ }
613
+ console.log();
614
+ console.log(pc.bold(" create-01-software-app"));
615
+ console.log(pc.dim(" Create a greenfield 01.software commerce app"));
616
+ console.log();
617
+ const result = await createApp(options);
618
+ console.log();
619
+ console.log(pc.green(" Created"), path2.relative(process.cwd(), result.projectDir) || ".");
620
+ if (result.tenantName) console.log(pc.dim(` Tenant: ${result.tenantName}`));
621
+ console.log();
622
+ console.log(" Next steps:");
623
+ console.log(pc.cyan(` cd ${path2.relative(process.cwd(), result.projectDir) || "."}`));
624
+ if (!result.installed) console.log(pc.cyan(` ${installCommand(result.packageManager)}`));
625
+ console.log(pc.cyan(` ${result.packageManager === "npm" ? "npm run" : result.packageManager} dev`));
626
+ console.log();
627
+ }
628
+ var isDirectRun = process.argv[1] ? isEntrypoint(process.argv[1]) : false;
629
+ function isEntrypoint(argvPath) {
630
+ try {
631
+ return fs2.realpathSync(argvPath) === fileURLToPath(import.meta.url);
632
+ } catch {
633
+ return path2.resolve(argvPath) === fileURLToPath(import.meta.url);
634
+ }
635
+ }
636
+ if (isDirectRun) {
637
+ runCreateAppCli().catch((error) => {
638
+ if (error instanceof Error && error.message === "cancelled") {
639
+ console.log(pc.yellow(" Cancelled."));
640
+ process.exit(0);
641
+ }
642
+ console.error(pc.red(" Error:"), error instanceof Error ? error.message : String(error));
643
+ process.exit(1);
644
+ });
645
+ }
646
+ export {
647
+ createApp,
648
+ removeMarkerLines,
649
+ runCreateAppCli,
650
+ stripMarkerBlocks
651
+ };
652
+ //# sourceMappingURL=create-app.js.map