@01.software/init 0.9.2 → 0.10.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/ai-docs.d.ts +13 -0
- package/dist/ai-docs.js +0 -0
- package/dist/browser-auth-CJDrpp5T.d.ts +11 -0
- package/dist/{chunk-UA7WNT2F.js → chunk-4LHYICUL.js} +1 -1
- package/dist/chunk-4LHYICUL.js.map +1 -0
- package/dist/{chunk-R4GGO33X.js → chunk-NJ4X7VNK.js} +1 -1
- package/dist/{chunk-R4GGO33X.js.map → chunk-NJ4X7VNK.js.map} +1 -1
- package/dist/chunk-Q6MSORYN.js +0 -0
- package/dist/chunk-STM4DKVZ.js +183 -0
- package/dist/chunk-STM4DKVZ.js.map +1 -0
- package/dist/{chunk-ENQSB4OF.js → chunk-WDWJ73KP.js} +40 -214
- package/dist/chunk-WDWJ73KP.js.map +1 -0
- package/dist/create-app-templates/ecommerce/AGENTS.md +88 -0
- package/dist/create-app-templates/ecommerce/CHANGELOG.md +30 -0
- package/dist/create-app-templates/ecommerce/CLAUDE.md +1 -0
- package/dist/create-app-templates/ecommerce/README.md +139 -0
- package/dist/create-app-templates/ecommerce/app/api/auth/login/route.ts +30 -0
- package/dist/create-app-templates/ecommerce/app/api/auth/logout/route.ts +18 -0
- package/dist/create-app-templates/ecommerce/app/api/auth/register/route.ts +41 -0
- package/dist/create-app-templates/ecommerce/app/api/cart/clear/route.ts +12 -0
- package/dist/create-app-templates/ecommerce/app/api/cart/items/route.ts +45 -0
- package/dist/create-app-templates/ecommerce/app/api/cart/route.ts +14 -0
- package/dist/create-app-templates/ecommerce/app/api/checkout/payment-return/route.ts +86 -0
- package/dist/create-app-templates/ecommerce/app/api/checkout/reconcile/route.ts +50 -0
- package/dist/create-app-templates/ecommerce/app/api/checkout/route.ts +41 -0
- package/dist/create-app-templates/ecommerce/app/cart/page.tsx +10 -0
- package/dist/create-app-templates/ecommerce/app/checkout/page.tsx +10 -0
- package/dist/create-app-templates/ecommerce/app/checkout/success/page.tsx +34 -0
- package/dist/create-app-templates/ecommerce/app/favicon.ico +0 -0
- package/dist/create-app-templates/ecommerce/app/globals.css +67 -0
- package/dist/create-app-templates/ecommerce/app/layout.tsx +23 -0
- package/dist/create-app-templates/ecommerce/app/login/page.tsx +11 -0
- package/dist/create-app-templates/ecommerce/app/page.tsx +5 -0
- package/dist/create-app-templates/ecommerce/app/products/[slug]/page.tsx +46 -0
- package/dist/create-app-templates/ecommerce/app/products/page.tsx +45 -0
- package/dist/create-app-templates/ecommerce/app/register/page.tsx +11 -0
- package/dist/create-app-templates/ecommerce/app/webhook/payment/route.ts +20 -0
- package/dist/create-app-templates/ecommerce/app-config.ts +54 -0
- package/dist/create-app-templates/ecommerce/components/auth/auth-form.tsx +109 -0
- package/dist/create-app-templates/ecommerce/components/cart/cart-content.tsx +119 -0
- package/dist/create-app-templates/ecommerce/components/checkout/checkout-form.tsx +267 -0
- package/dist/create-app-templates/ecommerce/components/checkout/checkout-reconcile.tsx +78 -0
- package/dist/create-app-templates/ecommerce/components/layout/account-nav.tsx +48 -0
- package/dist/create-app-templates/ecommerce/components/layout/account-slot.tsx +12 -0
- package/dist/create-app-templates/ecommerce/components/layout/cart-link.tsx +13 -0
- package/dist/create-app-templates/ecommerce/components/layout/page-shell.tsx +11 -0
- package/dist/create-app-templates/ecommerce/components/layout/site-header.tsx +22 -0
- package/dist/create-app-templates/ecommerce/components/product/add-to-cart.tsx +116 -0
- package/dist/create-app-templates/ecommerce/components/product/product-card.tsx +50 -0
- package/dist/create-app-templates/ecommerce/components/product/product-gallery.tsx +39 -0
- package/dist/create-app-templates/ecommerce/data/mock-catalog.json +173 -0
- package/dist/create-app-templates/ecommerce/eslint.config.mjs +18 -0
- package/dist/create-app-templates/ecommerce/lib/cart/cookie.ts +40 -0
- package/dist/create-app-templates/ecommerce/lib/cart/normalize.ts +32 -0
- package/dist/create-app-templates/ecommerce/lib/cart/parse-cart-request.ts +56 -0
- package/dist/create-app-templates/ecommerce/lib/cart/route-helpers.ts +17 -0
- package/dist/create-app-templates/ecommerce/lib/cart/select-provider.ts +44 -0
- package/dist/create-app-templates/ecommerce/lib/cart/server-cart.ts +96 -0
- package/dist/create-app-templates/ecommerce/lib/cart/sync-on-login.server.ts +34 -0
- package/dist/create-app-templates/ecommerce/lib/cart/use-cart.tsx +151 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/checkout-errors.ts +22 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/checkout-provider.ts +28 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/parse-checkout-payload.ts +76 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/start-checkout.ts +63 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/types.ts +3 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/adapters/mock.ts +336 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software-mappers.ts +312 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software.ts +913 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/product-summary.ts +37 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/provider.server.ts +60 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/provider.ts +96 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/stock.ts +37 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/types.ts +206 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/variant-selection.ts +23 -0
- package/dist/create-app-templates/ecommerce/lib/customer/auth-actions.ts +131 -0
- package/dist/create-app-templates/ecommerce/lib/customer/cart-sync.ts +44 -0
- package/dist/create-app-templates/ecommerce/lib/customer/client.server.ts +109 -0
- package/dist/create-app-templates/ecommerce/lib/customer/current-customer.ts +15 -0
- package/dist/create-app-templates/ecommerce/lib/customer/route-guard.ts +58 -0
- package/dist/create-app-templates/ecommerce/lib/customer/route-helpers.ts +75 -0
- package/dist/create-app-templates/ecommerce/lib/customer/session.ts +108 -0
- package/dist/create-app-templates/ecommerce/lib/format.ts +7 -0
- package/dist/create-app-templates/ecommerce/lib/payment/adapters/mock.ts +84 -0
- package/dist/create-app-templates/ecommerce/lib/payment/adapters/portone.ts +254 -0
- package/dist/create-app-templates/ecommerce/lib/payment/adapters/tosspayments.ts +287 -0
- package/dist/create-app-templates/ecommerce/lib/payment/amount-gate.ts +86 -0
- package/dist/create-app-templates/ecommerce/lib/payment/provider.server.ts +51 -0
- package/dist/create-app-templates/ecommerce/lib/payment/provider.ts +18 -0
- package/dist/create-app-templates/ecommerce/lib/payment/sync-order-payment.ts +96 -0
- package/dist/create-app-templates/ecommerce/lib/payment/types.ts +71 -0
- package/dist/create-app-templates/ecommerce/lib/server-only-guard.ts +20 -0
- package/dist/create-app-templates/ecommerce/next-env.d.ts +6 -0
- package/dist/create-app-templates/ecommerce/next.config.ts +16 -0
- package/dist/create-app-templates/ecommerce/package.json +33 -0
- package/dist/create-app-templates/ecommerce/postcss.config.mjs +7 -0
- package/dist/create-app-templates/ecommerce/tests/customer-auth.test.ts +263 -0
- package/dist/create-app-templates/ecommerce/tests/customer-cart.test.ts +392 -0
- package/dist/create-app-templates/ecommerce/tests/domain.test.ts +1537 -0
- package/dist/create-app-templates/ecommerce/tsconfig.json +35 -0
- package/dist/create-app-templates/registry.json +66 -0
- package/dist/create-app.d.ts +40 -0
- package/dist/create-app.js +652 -0
- package/dist/create-app.js.map +1 -0
- package/dist/detect-Bjxp9wcS.d.ts +13 -0
- package/dist/file-ops.d.ts +21 -0
- package/dist/file-ops.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +40 -0
- package/dist/init.js +4 -3
- package/dist/templates.d.ts +27 -0
- package/dist/templates.js +1 -1
- package/package.json +31 -15
- package/dist/chunk-ENQSB4OF.js.map +0 -1
- 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
|