frontpage-host 0.1.20 → 0.1.22
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/AGENTS.md +3 -2
- package/dist/bin.js +4 -1
- package/dist/commands/modules.js +13 -1
- package/dist/commands/people.js +29 -1
- package/dist/commands/routines.d.ts +1 -0
- package/dist/commands/routines.js +156 -0
- package/dist/commands/shop.js +150 -47
- package/dist/completions/generate.js +12 -11
- package/dist/http/client.js +1 -1
- package/dist/modules/commands.d.ts +3 -2
- package/dist/modules/commands.js +21 -89
- package/dist/modules/oauth.d.ts +2 -2
- package/dist/modules/oauth.js +7 -101
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +62 -27
- package/dist/routines-parse.d.ts +7 -0
- package/dist/routines-parse.js +78 -0
- package/llms.txt +3 -2
- package/package.json +1 -1
package/dist/commands/shop.js
CHANGED
|
@@ -2,9 +2,11 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { resolveSlug } from "../site.js";
|
|
3
3
|
import { CliHttpError, EXIT_OK, EXIT_USAGE, v1Get, v1Patch, v1Post, v1Put, } from "../http/client.js";
|
|
4
4
|
import { assertNoUnknownFlags, hasFlag, takeFlag } from "../argv.js";
|
|
5
|
-
import { emitErr, emitOk, emitUsage, isJson } from "../runtime.js";
|
|
5
|
+
import { emitErr, emitOk, emitUsage, isJson, requireYesOrTty } from "../runtime.js";
|
|
6
|
+
import { openBrowser, resolveOpenUrl } from "../modules/oauth.js";
|
|
6
7
|
import { SHOP_ID_RE, VARIANT_ID_RE } from "./shop-ids.js";
|
|
7
|
-
const USAGE = "Usage: frontpage
|
|
8
|
+
const USAGE = "Usage: frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect|import … [--site <slug>] [--json]";
|
|
9
|
+
const CONNECT_FINISH = "Open the Frontpage URL while logged in, finish Stripe in the browser, then re-run status. This CLI cannot complete OAuth.";
|
|
8
10
|
const CENTS_HINT = "$49.99 is 4999";
|
|
9
11
|
function takeAll(work, name) {
|
|
10
12
|
const out = [];
|
|
@@ -20,10 +22,10 @@ function requireShopId(work, label = "Item id", opts) {
|
|
|
20
22
|
const positional = work.find((a) => !a.startsWith("-"));
|
|
21
23
|
const raw = fromFlag ?? (positional ? work.splice(work.indexOf(positional), 1)[0] : undefined);
|
|
22
24
|
if (!raw) {
|
|
23
|
-
throw new CliHttpError(`${label} is required. Run: frontpage help
|
|
25
|
+
throw new CliHttpError(`${label} is required. Run: frontpage help store`, EXIT_USAGE, 0, "bad_request");
|
|
24
26
|
}
|
|
25
27
|
if (!SHOP_ID_RE.test(raw)) {
|
|
26
|
-
throw new CliHttpError("Item
|
|
28
|
+
throw new CliHttpError("Item id must be kebab-case, like camp-hat (2–40 letters, numbers, or hyphens).", EXIT_USAGE, 0, "bad_request");
|
|
27
29
|
}
|
|
28
30
|
return raw;
|
|
29
31
|
}
|
|
@@ -34,7 +36,7 @@ function requireVariantId(raw, required = true) {
|
|
|
34
36
|
throw new CliHttpError("Variant id is required. Use --variant m or --id m.", EXIT_USAGE, 0, "bad_request");
|
|
35
37
|
}
|
|
36
38
|
if (!VARIANT_ID_RE.test(raw)) {
|
|
37
|
-
throw new CliHttpError("
|
|
39
|
+
throw new CliHttpError("Version id must be kebab-case, like s, m, or camp-hat-m.", EXIT_USAGE, 0, "bad_request");
|
|
38
40
|
}
|
|
39
41
|
return raw;
|
|
40
42
|
}
|
|
@@ -43,7 +45,7 @@ function requireLedgerId(work, kind) {
|
|
|
43
45
|
const positional = work.find((a) => !a.startsWith("-"));
|
|
44
46
|
const raw = fromFlag ?? (positional ? work.splice(work.indexOf(positional), 1)[0] : undefined);
|
|
45
47
|
if (!raw || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(raw)) {
|
|
46
|
-
throw new CliHttpError(`${kind}
|
|
48
|
+
throw new CliHttpError(`${kind} id must be a UUID. Run: frontpage store ${kind.toLowerCase()}s list`, EXIT_USAGE, 0, "bad_request");
|
|
47
49
|
}
|
|
48
50
|
return raw;
|
|
49
51
|
}
|
|
@@ -144,7 +146,7 @@ function printItem(row) {
|
|
|
144
146
|
const bits = [
|
|
145
147
|
row.checkout ?? "",
|
|
146
148
|
row.amount ?? "",
|
|
147
|
-
prices.length ? prices.join(",") : "",
|
|
149
|
+
prices.length ? `${prices.join(",")} cents` : "",
|
|
148
150
|
stocks.length ? `stock ${stocks.join(",")}` : "",
|
|
149
151
|
row.active === false ? "inactive" : "active",
|
|
150
152
|
row.categories?.length ? row.categories.join(",") : "",
|
|
@@ -170,7 +172,7 @@ export async function runShop(args) {
|
|
|
170
172
|
const listWork = sub === "list" ? work : rest.slice(1);
|
|
171
173
|
const archived = hasFlag(listWork, "--archived");
|
|
172
174
|
const category = takeFlag(listWork, "--category");
|
|
173
|
-
assertNoUnknownFlags(listWork, "
|
|
175
|
+
assertNoUnknownFlags(listWork, "store");
|
|
174
176
|
const qs = new URLSearchParams();
|
|
175
177
|
if (archived)
|
|
176
178
|
qs.set("archived", "1");
|
|
@@ -184,7 +186,7 @@ export async function runShop(args) {
|
|
|
184
186
|
}
|
|
185
187
|
const rows = data.items ?? [];
|
|
186
188
|
if (rows.length === 0) {
|
|
187
|
-
console.log(
|
|
189
|
+
console.log(category ? "No products in that category." : "No products in this store.");
|
|
188
190
|
return EXIT_OK;
|
|
189
191
|
}
|
|
190
192
|
for (const row of rows)
|
|
@@ -193,7 +195,7 @@ export async function runShop(args) {
|
|
|
193
195
|
}
|
|
194
196
|
if (sub === "get") {
|
|
195
197
|
const itemId = requireShopId(work);
|
|
196
|
-
assertNoUnknownFlags(work, "
|
|
198
|
+
assertNoUnknownFlags(work, "store");
|
|
197
199
|
const data = await v1Get(`${base}/items/${encodeURIComponent(itemId)}`);
|
|
198
200
|
if (isJson()) {
|
|
199
201
|
emitOk("shop", data);
|
|
@@ -205,7 +207,7 @@ export async function runShop(args) {
|
|
|
205
207
|
const stock = v.stock == null ? "unlimited" : String(v.stock);
|
|
206
208
|
const off = v.active === false ? " inactive" : "";
|
|
207
209
|
const archived = v.archivedAt ? " archived" : "";
|
|
208
|
-
console.log(` ${v.variantId} ${v.priceCents}${sale} stock ${stock}${off}${archived}`);
|
|
210
|
+
console.log(` ${v.variantId} ${v.priceCents} cents${sale} stock ${stock}${off}${archived}`);
|
|
209
211
|
}
|
|
210
212
|
return EXIT_OK;
|
|
211
213
|
}
|
|
@@ -224,12 +226,11 @@ export async function runShop(args) {
|
|
|
224
226
|
const categories = parseCsv(takeFlag(work, "--categories"));
|
|
225
227
|
const priceCents = parseCentsFlag(takeFlag(work, "--price-cents"), "priceCents");
|
|
226
228
|
const discountCents = parseCentsFlag(takeFlag(work, "--discount-cents"), "discountPriceCents");
|
|
227
|
-
const shippingCents = parseCentsFlag(takeFlag(work, "--shipping-cents"), "shippingCents");
|
|
228
229
|
const stockRaw = takeFlag(work, "--stock");
|
|
229
230
|
const variantId = takeFlag(work, "--variant-id") ?? takeFlag(work, "--variant");
|
|
230
231
|
const options = parseOptions(takeAll(work, "--option"));
|
|
231
232
|
const images = parseUrls(work);
|
|
232
|
-
assertNoUnknownFlags(work, "
|
|
233
|
+
assertNoUnknownFlags(work, "store");
|
|
233
234
|
const body = file ? await readJsonFile(file) : {};
|
|
234
235
|
if (itemId)
|
|
235
236
|
body.itemId = itemId;
|
|
@@ -251,8 +252,6 @@ export async function runShop(args) {
|
|
|
251
252
|
body.categories = categories;
|
|
252
253
|
if (images.length)
|
|
253
254
|
body.images = images;
|
|
254
|
-
if (body.checkout == null)
|
|
255
|
-
body.checkout = "cart";
|
|
256
255
|
if (body.amount == null)
|
|
257
256
|
body.amount = "fixed";
|
|
258
257
|
if (!Array.isArray(body.variants) && priceCents !== undefined) {
|
|
@@ -263,13 +262,12 @@ export async function runShop(args) {
|
|
|
263
262
|
...(options ? { options } : {}),
|
|
264
263
|
priceCents,
|
|
265
264
|
...(discountCents !== undefined ? { discountPriceCents: discountCents } : {}),
|
|
266
|
-
...(shippingCents !== undefined ? { shippingCents } : {}),
|
|
267
265
|
...(stock !== undefined ? { stock } : {}),
|
|
268
266
|
},
|
|
269
267
|
];
|
|
270
268
|
}
|
|
271
269
|
if (typeof body.itemId !== "string" || !SHOP_ID_RE.test(body.itemId)) {
|
|
272
|
-
throw new CliHttpError("Usage: frontpage
|
|
270
|
+
throw new CliHttpError("Item id must be kebab-case, like camp-hat (2–40 letters, numbers, or hyphens). Usage: frontpage store items create camp-hat --title \"Camp hat\" --price-cents 4999", EXIT_USAGE, 0, "bad_request");
|
|
273
271
|
}
|
|
274
272
|
const data = await v1Post(`${base}/items`, body);
|
|
275
273
|
if (isJson()) {
|
|
@@ -290,7 +288,7 @@ export async function runShop(args) {
|
|
|
290
288
|
const active = parseBoolFlag(takeFlag(work, "--active"), "active");
|
|
291
289
|
const categories = parseCsv(takeFlag(work, "--categories"));
|
|
292
290
|
const images = parseUrls(work);
|
|
293
|
-
assertNoUnknownFlags(work, "
|
|
291
|
+
assertNoUnknownFlags(work, "store");
|
|
294
292
|
const body = {};
|
|
295
293
|
if (title !== undefined)
|
|
296
294
|
body.title = title;
|
|
@@ -323,7 +321,7 @@ export async function runShop(args) {
|
|
|
323
321
|
}
|
|
324
322
|
if (sub === "archive") {
|
|
325
323
|
const itemId = requireShopId(work);
|
|
326
|
-
assertNoUnknownFlags(work, "
|
|
324
|
+
assertNoUnknownFlags(work, "store");
|
|
327
325
|
const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/archive`, {});
|
|
328
326
|
if (isJson()) {
|
|
329
327
|
emitOk("shop", data);
|
|
@@ -336,7 +334,7 @@ export async function runShop(args) {
|
|
|
336
334
|
const itemId = requireShopId(work);
|
|
337
335
|
const off = hasFlag(work, "--off");
|
|
338
336
|
const active = off ? false : parseBoolFlag(takeFlag(work, "--active"), "active");
|
|
339
|
-
assertNoUnknownFlags(work, "
|
|
337
|
+
assertNoUnknownFlags(work, "store");
|
|
340
338
|
const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/activate`, { active: active ?? true });
|
|
341
339
|
if (isJson()) {
|
|
342
340
|
emitOk("shop", data);
|
|
@@ -348,7 +346,7 @@ export async function runShop(args) {
|
|
|
348
346
|
if (sub === "images") {
|
|
349
347
|
const itemId = requireShopId(work);
|
|
350
348
|
const urls = parseUrls(work);
|
|
351
|
-
assertNoUnknownFlags(work, "
|
|
349
|
+
assertNoUnknownFlags(work, "store");
|
|
352
350
|
const data = await v1Put(`${base}/items/${encodeURIComponent(itemId)}/images`, { images: urls });
|
|
353
351
|
if (isJson()) {
|
|
354
352
|
emitOk("shop", data);
|
|
@@ -363,7 +361,7 @@ export async function runShop(args) {
|
|
|
363
361
|
const addRaw = takeFlag(work, "--add");
|
|
364
362
|
const removeRaw = takeFlag(work, "--remove");
|
|
365
363
|
const setRaw = takeFlag(work, "--set");
|
|
366
|
-
assertNoUnknownFlags(work, "
|
|
364
|
+
assertNoUnknownFlags(work, "store");
|
|
367
365
|
const present = [addRaw, removeRaw, setRaw].filter((v) => v !== undefined).length;
|
|
368
366
|
if (present !== 1) {
|
|
369
367
|
throw new CliHttpError("Pass either --add 5, --remove 5, or --set 20. --add / --remove become signed delta.", EXIT_USAGE, 0, "bad_request");
|
|
@@ -414,13 +412,12 @@ export async function runShop(args) {
|
|
|
414
412
|
const variantId = requireVariantId(takeFlag(work, "--id") ?? takeFlag(work, "--variant"));
|
|
415
413
|
const priceCents = parseCentsFlag(takeFlag(work, "--price-cents"), "priceCents");
|
|
416
414
|
const discountCents = parseCentsFlag(takeFlag(work, "--discount-cents"), "discountPriceCents");
|
|
417
|
-
const shippingCents = parseCentsFlag(takeFlag(work, "--shipping-cents"), "shippingCents");
|
|
418
415
|
const stockRaw = takeFlag(work, "--stock");
|
|
419
416
|
const options = parseOptions(takeAll(work, "--option"));
|
|
420
417
|
const active = parseBoolFlag(takeFlag(work, "--active"), "active");
|
|
421
|
-
assertNoUnknownFlags(work, "
|
|
418
|
+
assertNoUnknownFlags(work, "store");
|
|
422
419
|
if (priceCents === undefined) {
|
|
423
|
-
throw new CliHttpError("Usage: frontpage
|
|
420
|
+
throw new CliHttpError("Usage: frontpage store variants add <itemId> --id m --price-cents 4999", EXIT_USAGE, 0, "bad_request");
|
|
424
421
|
}
|
|
425
422
|
const stock = stockRaw === undefined ? undefined : stockRaw === "unlimited" ? null : parseCountFlag(stockRaw, "stock");
|
|
426
423
|
const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/variants`, {
|
|
@@ -428,7 +425,6 @@ export async function runShop(args) {
|
|
|
428
425
|
options,
|
|
429
426
|
priceCents,
|
|
430
427
|
discountPriceCents: discountCents,
|
|
431
|
-
shippingCents,
|
|
432
428
|
stock,
|
|
433
429
|
active,
|
|
434
430
|
});
|
|
@@ -444,18 +440,15 @@ export async function runShop(args) {
|
|
|
444
440
|
const variantId = requireVariantId(takeFlag(work, "--id") ?? takeFlag(work, "--variant"));
|
|
445
441
|
const priceCents = parseCentsFlag(takeFlag(work, "--price-cents"), "priceCents");
|
|
446
442
|
const discountCents = parseCentsFlag(takeFlag(work, "--discount-cents"), "discountPriceCents");
|
|
447
|
-
const shippingCents = parseCentsFlag(takeFlag(work, "--shipping-cents"), "shippingCents");
|
|
448
443
|
const stockRaw = takeFlag(work, "--stock");
|
|
449
444
|
const options = parseOptions(takeAll(work, "--option"));
|
|
450
445
|
const active = parseBoolFlag(takeFlag(work, "--active"), "active");
|
|
451
|
-
assertNoUnknownFlags(work, "
|
|
446
|
+
assertNoUnknownFlags(work, "store");
|
|
452
447
|
const body = {};
|
|
453
448
|
if (priceCents !== undefined)
|
|
454
449
|
body.priceCents = priceCents;
|
|
455
450
|
if (discountCents !== undefined)
|
|
456
451
|
body.discountPriceCents = discountCents;
|
|
457
|
-
if (shippingCents !== undefined)
|
|
458
|
-
body.shippingCents = shippingCents;
|
|
459
452
|
if (stockRaw !== undefined)
|
|
460
453
|
body.stock = stockRaw === "unlimited" ? null : parseCountFlag(stockRaw, "stock");
|
|
461
454
|
if (options)
|
|
@@ -476,7 +469,7 @@ export async function runShop(args) {
|
|
|
476
469
|
if (sub === "archive") {
|
|
477
470
|
const itemId = requireShopId(work, "Item id", { allowIdFlag: false });
|
|
478
471
|
const variantId = requireVariantId(takeFlag(work, "--id") ?? takeFlag(work, "--variant"));
|
|
479
|
-
assertNoUnknownFlags(work, "
|
|
472
|
+
assertNoUnknownFlags(work, "store");
|
|
480
473
|
const data = await v1Post(`${base}/items/${encodeURIComponent(itemId)}/variants/${encodeURIComponent(variantId)}/archive`, {});
|
|
481
474
|
if (isJson()) {
|
|
482
475
|
emitOk("shop", data);
|
|
@@ -493,7 +486,7 @@ export async function runShop(args) {
|
|
|
493
486
|
if (!sub || sub === "list" || sub.startsWith("-")) {
|
|
494
487
|
const listWork = sub === "list" ? work : rest.slice(1);
|
|
495
488
|
const archived = hasFlag(listWork, "--archived");
|
|
496
|
-
assertNoUnknownFlags(listWork, "
|
|
489
|
+
assertNoUnknownFlags(listWork, "store");
|
|
497
490
|
const qs = archived ? "?archived=1" : "";
|
|
498
491
|
const data = await v1Get(`${base}/categories${qs}`);
|
|
499
492
|
if (isJson()) {
|
|
@@ -515,9 +508,9 @@ export async function runShop(args) {
|
|
|
515
508
|
if (slug && work.includes(slug))
|
|
516
509
|
work.splice(work.indexOf(slug), 1);
|
|
517
510
|
const label = takeFlag(work, "--label") ?? takeFlag(work, "--title");
|
|
518
|
-
assertNoUnknownFlags(work, "
|
|
511
|
+
assertNoUnknownFlags(work, "store");
|
|
519
512
|
if (!slug || !SHOP_ID_RE.test(slug) || !label) {
|
|
520
|
-
throw new CliHttpError("Usage: frontpage
|
|
513
|
+
throw new CliHttpError("Usage: frontpage store categories create hats --label Hats", EXIT_USAGE, 0, "bad_request");
|
|
521
514
|
}
|
|
522
515
|
const data = await v1Post(`${base}/categories`, {
|
|
523
516
|
slug,
|
|
@@ -533,9 +526,9 @@ export async function runShop(args) {
|
|
|
533
526
|
if (sub === "rename") {
|
|
534
527
|
const slug = requireShopId(work, "Category slug");
|
|
535
528
|
const label = takeFlag(work, "--label") ?? takeFlag(work, "--title");
|
|
536
|
-
assertNoUnknownFlags(work, "
|
|
529
|
+
assertNoUnknownFlags(work, "store");
|
|
537
530
|
if (!label) {
|
|
538
|
-
throw new CliHttpError("Usage: frontpage
|
|
531
|
+
throw new CliHttpError("Usage: frontpage store categories rename hats --label Hats", EXIT_USAGE, 0, "bad_request");
|
|
539
532
|
}
|
|
540
533
|
const data = await v1Patch(`${base}/categories/${encodeURIComponent(slug)}`, { label });
|
|
541
534
|
if (isJson()) {
|
|
@@ -547,7 +540,7 @@ export async function runShop(args) {
|
|
|
547
540
|
}
|
|
548
541
|
if (sub === "archive") {
|
|
549
542
|
const slug = requireShopId(work, "Category slug");
|
|
550
|
-
assertNoUnknownFlags(work, "
|
|
543
|
+
assertNoUnknownFlags(work, "store");
|
|
551
544
|
const data = await v1Post(`${base}/categories/${encodeURIComponent(slug)}/archive`, {});
|
|
552
545
|
if (isJson()) {
|
|
553
546
|
emitOk("shop", data);
|
|
@@ -562,7 +555,7 @@ export async function runShop(args) {
|
|
|
562
555
|
const sub = rest[1] ?? "get";
|
|
563
556
|
const work = rest.slice(2);
|
|
564
557
|
if (sub === "get") {
|
|
565
|
-
assertNoUnknownFlags(work, "
|
|
558
|
+
assertNoUnknownFlags(work, "store");
|
|
566
559
|
const data = await v1Get(`${base}/settings`);
|
|
567
560
|
if (isJson()) {
|
|
568
561
|
emitOk("shop", data);
|
|
@@ -571,19 +564,31 @@ export async function runShop(args) {
|
|
|
571
564
|
const s = data.settings ?? {};
|
|
572
565
|
const fee = (s.checkoutFee ?? {});
|
|
573
566
|
const fulfill = (s.fulfillment ?? {});
|
|
574
|
-
const
|
|
575
|
-
|
|
567
|
+
const methods = [
|
|
568
|
+
fulfill.pickupEnabled ? "pickup" : null,
|
|
569
|
+
fulfill.shippingEnabled ? "delivery" : null,
|
|
570
|
+
].filter(Boolean);
|
|
571
|
+
const fulfillText = methods.length > 0 ? methods.join("+") : fulfill.shipping ?? "(no fulfillment)";
|
|
572
|
+
const checkoutText = s.storeCheckout === "instant" ? "pay-now" : "cart";
|
|
573
|
+
const feeText = fee.enabled ? `service-fee ${fee.cents ?? 0}` : "service-fee off";
|
|
574
|
+
console.log(`${s.currency ?? "(no currency)"} ${fulfillText} ${checkoutText} ${feeText}`);
|
|
576
575
|
return EXIT_OK;
|
|
577
576
|
}
|
|
578
577
|
if (sub === "set-fulfillment") {
|
|
579
578
|
const mode = takeFlag(work, "--mode") ?? takeFlag(work, "--shipping");
|
|
579
|
+
const pickupOn = takeBoolish(work, "--pickup", "pickupEnabled");
|
|
580
|
+
const deliveryOn = takeBoolish(work, "--delivery", "shippingEnabled");
|
|
581
|
+
const pickupLocationName = takeFlag(work, "--pickup-location");
|
|
582
|
+
const pickupAddress = takeFlag(work, "--pickup-address");
|
|
583
|
+
const pickupHours = takeFlag(work, "--pickup-hours");
|
|
584
|
+
const pickupInstructions = takeFlag(work, "--pickup-instructions");
|
|
580
585
|
const flatCents = parseCentsFlag(takeFlag(work, "--flat-cents"), "flatRateCents");
|
|
581
586
|
const ratesRaw = takeFlag(work, "--country-rates");
|
|
582
587
|
const restOn = takeBoolish(work, "--rest-of-world", "restOfWorldEnabled");
|
|
583
588
|
const restCents = parseCentsFlag(takeFlag(work, "--rest-cents"), "restOfWorldCents");
|
|
584
589
|
const freeOn = takeBoolish(work, "--free-over", "freeShippingOverEnabled");
|
|
585
590
|
const freeCents = parseCentsFlag(takeFlag(work, "--free-over-cents"), "freeShippingOverCents");
|
|
586
|
-
assertNoUnknownFlags(work, "
|
|
591
|
+
assertNoUnknownFlags(work, "store");
|
|
587
592
|
let countryRates;
|
|
588
593
|
if (ratesRaw) {
|
|
589
594
|
try {
|
|
@@ -595,6 +600,12 @@ export async function runShop(args) {
|
|
|
595
600
|
}
|
|
596
601
|
const data = await v1Post(`${base}/settings/fulfillment`, {
|
|
597
602
|
shipping: mode,
|
|
603
|
+
...(pickupOn !== undefined ? { pickupEnabled: pickupOn } : {}),
|
|
604
|
+
...(deliveryOn !== undefined ? { shippingEnabled: deliveryOn } : {}),
|
|
605
|
+
...(pickupLocationName !== undefined ? { pickupLocationName } : {}),
|
|
606
|
+
...(pickupAddress !== undefined ? { pickupAddress } : {}),
|
|
607
|
+
...(pickupHours !== undefined ? { pickupHours } : {}),
|
|
608
|
+
...(pickupInstructions !== undefined ? { pickupInstructions } : {}),
|
|
598
609
|
flatRateCents: flatCents,
|
|
599
610
|
countryRates,
|
|
600
611
|
restOfWorldEnabled: restOn,
|
|
@@ -610,9 +621,9 @@ export async function runShop(args) {
|
|
|
610
621
|
}
|
|
611
622
|
if (sub === "set-currency") {
|
|
612
623
|
const currency = takeFlag(work, "--currency");
|
|
613
|
-
assertNoUnknownFlags(work, "
|
|
624
|
+
assertNoUnknownFlags(work, "store");
|
|
614
625
|
if (!currency) {
|
|
615
|
-
throw new CliHttpError("Usage: frontpage
|
|
626
|
+
throw new CliHttpError("Usage: frontpage store settings set-currency --currency usd (any Stripe presentment code such as usd or jpy)", EXIT_USAGE, 0, "bad_request");
|
|
616
627
|
}
|
|
617
628
|
const data = await v1Post(`${base}/settings/currency`, { currency });
|
|
618
629
|
if (isJson())
|
|
@@ -621,10 +632,23 @@ export async function runShop(args) {
|
|
|
621
632
|
console.log("Currency saved.");
|
|
622
633
|
return EXIT_OK;
|
|
623
634
|
}
|
|
635
|
+
if (sub === "set-checkout") {
|
|
636
|
+
const checkout = takeFlag(work, "--mode") ?? takeFlag(work, "--checkout");
|
|
637
|
+
assertNoUnknownFlags(work, "store");
|
|
638
|
+
if (checkout !== "cart" && checkout !== "instant") {
|
|
639
|
+
throw new CliHttpError("Usage: frontpage store settings set-checkout --mode cart|instant", EXIT_USAGE, 0, "bad_request");
|
|
640
|
+
}
|
|
641
|
+
const data = await v1Post(`${base}/settings/checkout`, { checkout });
|
|
642
|
+
if (isJson())
|
|
643
|
+
emitOk("shop", data);
|
|
644
|
+
else
|
|
645
|
+
console.log(checkout === "instant" ? "Checkout is Pay now." : "Checkout is add to cart.");
|
|
646
|
+
return EXIT_OK;
|
|
647
|
+
}
|
|
624
648
|
if (sub === "set-checkout-fee") {
|
|
625
649
|
const off = hasFlag(work, "--off");
|
|
626
650
|
const cents = parseCentsFlag(takeFlag(work, "--cents"), "checkoutFeeCents");
|
|
627
|
-
assertNoUnknownFlags(work, "
|
|
651
|
+
assertNoUnknownFlags(work, "store");
|
|
628
652
|
const data = await v1Post(`${base}/settings/checkout-fee`, {
|
|
629
653
|
enabled: off ? false : true,
|
|
630
654
|
cents,
|
|
@@ -632,17 +656,96 @@ export async function runShop(args) {
|
|
|
632
656
|
if (isJson())
|
|
633
657
|
emitOk("shop", data);
|
|
634
658
|
else
|
|
635
|
-
console.log(off ? "
|
|
659
|
+
console.log(off ? "Service fee off." : "Service fee saved.");
|
|
636
660
|
return EXIT_OK;
|
|
637
661
|
}
|
|
638
662
|
return emitUsage("shop", USAGE);
|
|
639
663
|
}
|
|
664
|
+
if (group === "connect") {
|
|
665
|
+
const work = rest.slice(1);
|
|
666
|
+
const noOpen = hasFlag(work, "--no-open");
|
|
667
|
+
assertNoUnknownFlags(work, "store");
|
|
668
|
+
const data = await v1Post(`${base}/connect`, { action: "connect" });
|
|
669
|
+
const already = data.alreadyConnected === true;
|
|
670
|
+
if (isJson()) {
|
|
671
|
+
emitOk("shop", data);
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
const url = resolveOpenUrl(data.connectUrl);
|
|
675
|
+
if (url)
|
|
676
|
+
console.log(url);
|
|
677
|
+
console.log(typeof data.message === "string" ? data.message : already ? "Already connected." : CONNECT_FINISH);
|
|
678
|
+
if (typeof data.warning === "string")
|
|
679
|
+
console.log(data.warning);
|
|
680
|
+
}
|
|
681
|
+
const url = resolveOpenUrl(data.connectUrl);
|
|
682
|
+
if (url && !noOpen && !already)
|
|
683
|
+
openBrowser(url);
|
|
684
|
+
return EXIT_OK;
|
|
685
|
+
}
|
|
686
|
+
if (group === "status") {
|
|
687
|
+
const work = rest.slice(1);
|
|
688
|
+
assertNoUnknownFlags(work, "store");
|
|
689
|
+
const data = await v1Get(`${base}/connect`);
|
|
690
|
+
if (isJson()) {
|
|
691
|
+
emitOk("shop", data);
|
|
692
|
+
return EXIT_OK;
|
|
693
|
+
}
|
|
694
|
+
const connection = (data.connection ?? {});
|
|
695
|
+
const display = connection.display ?? {};
|
|
696
|
+
console.log(`connected ${connection.ready ? "yes" : "no"}`);
|
|
697
|
+
console.log(`charges ${display.chargesEnabled == null ? "—" : display.chargesEnabled ? "yes" : "no"}`);
|
|
698
|
+
console.log(`account ${display.accountPresent ? "present" : "none"}`);
|
|
699
|
+
if (typeof data.sharedStripe === "string")
|
|
700
|
+
console.log(`note ${data.sharedStripe}`);
|
|
701
|
+
if (typeof data.connectUrl === "string")
|
|
702
|
+
console.log(`connect ${data.connectUrl}`);
|
|
703
|
+
return EXIT_OK;
|
|
704
|
+
}
|
|
705
|
+
if (group === "disconnect") {
|
|
706
|
+
const work = rest.slice(1);
|
|
707
|
+
assertNoUnknownFlags(work, "store");
|
|
708
|
+
const refuse = requireYesOrTty("disconnect the store (shared Stripe account with payments)");
|
|
709
|
+
if (refuse)
|
|
710
|
+
return emitUsage("shop", refuse);
|
|
711
|
+
const data = await v1Post(`${base}/connect`, { action: "disconnect" });
|
|
712
|
+
if (isJson()) {
|
|
713
|
+
emitOk("shop", data);
|
|
714
|
+
return EXIT_OK;
|
|
715
|
+
}
|
|
716
|
+
console.log(typeof data.message === "string" ? data.message : "Disconnected.");
|
|
717
|
+
return EXIT_OK;
|
|
718
|
+
}
|
|
719
|
+
if (group === "import") {
|
|
720
|
+
const work = rest.slice(1);
|
|
721
|
+
assertNoUnknownFlags(work, "store");
|
|
722
|
+
const data = await v1Post(`${base}/import`, {});
|
|
723
|
+
if (isJson()) {
|
|
724
|
+
emitOk("shop", data);
|
|
725
|
+
return EXIT_OK;
|
|
726
|
+
}
|
|
727
|
+
const imported = data.imported ?? [];
|
|
728
|
+
const skipped = data.skipped ?? [];
|
|
729
|
+
const failed = data.failed ?? [];
|
|
730
|
+
if (imported.length === 0 && skipped.length === 0 && failed.length === 0) {
|
|
731
|
+
console.log("No src/content/products files to import.");
|
|
732
|
+
return EXIT_OK;
|
|
733
|
+
}
|
|
734
|
+
for (const row of imported)
|
|
735
|
+
console.log(`imported ${row.itemId} ${row.title}`);
|
|
736
|
+
for (const row of skipped)
|
|
737
|
+
console.log(`skipped ${row.itemId} already in Store`);
|
|
738
|
+
for (const row of failed)
|
|
739
|
+
console.log(`failed ${row.path} ${row.reason}`);
|
|
740
|
+
console.log(`${imported.length} imported ${skipped.length} skipped ${failed.length} failed`);
|
|
741
|
+
return EXIT_OK;
|
|
742
|
+
}
|
|
640
743
|
if (group === "orders" || group === "payments") {
|
|
641
744
|
const noun = group;
|
|
642
745
|
const sub = rest[1] ?? "list";
|
|
643
746
|
const work = rest.slice(2);
|
|
644
747
|
if (sub === "list") {
|
|
645
|
-
assertNoUnknownFlags(work, "
|
|
748
|
+
assertNoUnknownFlags(work, "store");
|
|
646
749
|
const data = await v1Get(`${base}/${noun}`);
|
|
647
750
|
if (isJson()) {
|
|
648
751
|
emitOk("shop", data);
|
|
@@ -660,7 +763,7 @@ export async function runShop(args) {
|
|
|
660
763
|
}
|
|
661
764
|
if (sub === "get") {
|
|
662
765
|
const id = requireLedgerId(work, noun === "orders" ? "Order" : "Payment");
|
|
663
|
-
assertNoUnknownFlags(work, "
|
|
766
|
+
assertNoUnknownFlags(work, "store");
|
|
664
767
|
const data = await v1Get(`${base}/${noun}/${encodeURIComponent(id)}`);
|
|
665
768
|
if (isJson()) {
|
|
666
769
|
emitOk("shop", data);
|
|
@@ -26,6 +26,8 @@ _frontpage_completions() {
|
|
|
26
26
|
articles) COMPREPLY=( $(compgen -W "list content create-section create-article set-draft" -- "\${cur}") ) ;;
|
|
27
27
|
analytics) COMPREPLY=( $(compgen -W "dashboard summary query funnel timeseries compare sql" -- "\${cur}") ) ;;
|
|
28
28
|
people) COMPREPLY=( $(compgen -W "list get add set note rm-note follow-up hide unhide" -- "\${cur}") ) ;;
|
|
29
|
+
routines) COMPREPLY=( $(compgen -W "list get add set pause resume rm run runs" -- "\${cur}") ) ;;
|
|
30
|
+
store) COMPREPLY=( $(compgen -W "items variants categories settings orders payments connect status disconnect import" -- "\${cur}") ) ;;
|
|
29
31
|
modules)
|
|
30
32
|
if [[ \${COMP_CWORD} -ge 3 && \${COMP_WORDS[2]} == google_reviews ]]; then
|
|
31
33
|
COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config connect resolve status list-reviews hide show set-stars set-sort set-weight reset-weights write-review-url refresh disconnect" -- "\${cur}") )
|
|
@@ -33,12 +35,10 @@ _frontpage_completions() {
|
|
|
33
35
|
COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config get set destinations connect-destination remove-destination disconnect-destination attach-destination detach-destination request-destination-setup" -- "\${cur}") )
|
|
34
36
|
elif [[ \${COMP_CWORD} -ge 3 && (\${COMP_WORDS[2]} == callbar || \${COMP_WORDS[2]} == booking || \${COMP_WORDS[2]} == signups) ]]; then
|
|
35
37
|
COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config get set" -- "\${cur}") )
|
|
36
|
-
elif [[ \${COMP_CWORD} -ge 3 &&
|
|
38
|
+
elif [[ \${COMP_CWORD} -ge 3 && \${COMP_WORDS[2]} == payments ]]; then
|
|
37
39
|
COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config status connect disconnect" -- "\${cur}") )
|
|
38
|
-
elif [[ \${COMP_CWORD} -ge 3 && \${COMP_WORDS[2]} == ordering ]]; then
|
|
39
|
-
COMPREPLY=( $(compgen -W "docs request-setup enable disable archive config status connect disconnect sync" -- "\${cur}") )
|
|
40
40
|
else
|
|
41
|
-
COMPREPLY=( $(compgen -W "list docs request-setup enable disable archive config callbar forms booking signups payments
|
|
41
|
+
COMPREPLY=( $(compgen -W "list docs request-setup enable disable archive config callbar forms booking signups payments articles google_reviews" -- "\${cur}") )
|
|
42
42
|
fi
|
|
43
43
|
;;
|
|
44
44
|
extensions) COMPREPLY=( $(compgen -W "list docs enable disable config" -- "\${cur}") ) ;;
|
|
@@ -71,6 +71,8 @@ _frontpage() {
|
|
|
71
71
|
seo|settings) _values 'subcommand' get set ;;
|
|
72
72
|
analytics) _values 'subcommand' dashboard summary query funnel timeseries compare sql ;;
|
|
73
73
|
people) _values 'subcommand' list get add set note rm-note follow-up hide unhide ;;
|
|
74
|
+
routines) _values 'subcommand' list get add set pause resume rm run runs ;;
|
|
75
|
+
store) _values 'subcommand' items variants categories settings orders payments connect status disconnect import ;;
|
|
74
76
|
modules)
|
|
75
77
|
if [[ $words[2] == google_reviews ]]; then
|
|
76
78
|
_values 'subcommand' docs request-setup enable disable archive config connect resolve status list-reviews hide show set-stars set-sort set-weight reset-weights write-review-url refresh disconnect
|
|
@@ -78,12 +80,10 @@ _frontpage() {
|
|
|
78
80
|
_values 'subcommand' docs request-setup enable disable archive config get set destinations connect-destination remove-destination disconnect-destination attach-destination detach-destination request-destination-setup
|
|
79
81
|
elif [[ $words[2] == callbar || $words[2] == booking || $words[2] == signups ]]; then
|
|
80
82
|
_values 'subcommand' docs request-setup enable disable archive config get set
|
|
81
|
-
elif [[ $words[2] == payments
|
|
83
|
+
elif [[ $words[2] == payments ]]; then
|
|
82
84
|
_values 'subcommand' docs request-setup enable disable archive config status connect disconnect
|
|
83
|
-
elif [[ $words[2] == ordering ]]; then
|
|
84
|
-
_values 'subcommand' docs request-setup enable disable archive config status connect disconnect sync
|
|
85
85
|
else
|
|
86
|
-
_values 'subcommand' list docs request-setup enable disable archive config callbar forms booking signups payments
|
|
86
|
+
_values 'subcommand' list docs request-setup enable disable archive config callbar forms booking signups payments articles google_reviews
|
|
87
87
|
fi
|
|
88
88
|
;;
|
|
89
89
|
extensions) _values 'subcommand' list docs enable disable config ;;
|
|
@@ -109,12 +109,13 @@ export function generateFishCompletion() {
|
|
|
109
109
|
`complete -c frontpage -n "__fish_seen_subcommand_from seo settings" -a "get set"`,
|
|
110
110
|
`complete -c frontpage -n "__fish_seen_subcommand_from analytics" -a "dashboard summary query funnel timeseries compare sql"`,
|
|
111
111
|
`complete -c frontpage -n "__fish_seen_subcommand_from people" -a "list get add set note rm-note follow-up hide unhide"`,
|
|
112
|
-
`complete -c frontpage -n "__fish_seen_subcommand_from
|
|
112
|
+
`complete -c frontpage -n "__fish_seen_subcommand_from routines" -a "list get add set pause resume rm run runs"`,
|
|
113
|
+
`complete -c frontpage -n "__fish_seen_subcommand_from store" -a "items variants categories settings orders payments connect status disconnect import"`,
|
|
114
|
+
`complete -c frontpage -n "__fish_seen_subcommand_from modules" -a "list docs request-setup enable disable archive config callbar forms booking signups payments articles google_reviews"`,
|
|
113
115
|
`complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from google_reviews" -a "connect resolve status list-reviews hide show set-stars set-sort set-weight reset-weights write-review-url refresh disconnect"`,
|
|
114
116
|
`complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from forms" -a "get set destinations connect-destination remove-destination disconnect-destination attach-destination detach-destination request-destination-setup"`,
|
|
115
117
|
`complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from callbar booking signups" -a "get set"`,
|
|
116
|
-
`complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from payments
|
|
117
|
-
`complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from ordering" -a "status connect disconnect sync"`,
|
|
118
|
+
`complete -c frontpage -n "__fish_seen_subcommand_from modules; and __fish_seen_subcommand_from payments" -a "status connect disconnect"`,
|
|
118
119
|
`complete -c frontpage -n "__fish_seen_subcommand_from extensions" -a "list docs enable disable config"`,
|
|
119
120
|
`complete -c frontpage -l site -d "Site subdomain slug"`,
|
|
120
121
|
];
|
package/dist/http/client.js
CHANGED
|
@@ -51,7 +51,7 @@ export function httpErrorFromV1(status, parsed, fallbackMessage) {
|
|
|
51
51
|
if (lower === "not found." || lower === "not found") {
|
|
52
52
|
return new CliHttpError("API not available (is PUBLIC_CLI_ENABLED on for this environment?).", EXIT_UNEXPECTED, status, code, requestId);
|
|
53
53
|
}
|
|
54
|
-
return new CliHttpError(message,
|
|
54
|
+
return new CliHttpError(message, EXIT_USAGE, status, code, requestId);
|
|
55
55
|
}
|
|
56
56
|
return new CliHttpError(message, EXIT_UNEXPECTED, status, code, requestId);
|
|
57
57
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* §CLI-MOD-FW — per-key CLI registry (usage printer + help --json).
|
|
3
3
|
* Instagram is not documented here. Google reviews extras ship in Phase 1.
|
|
4
4
|
*/
|
|
5
|
-
export declare const DOCUMENTED_MODULE_KEYS: readonly ["callbar", "forms", "booking", "signups", "
|
|
5
|
+
export declare const DOCUMENTED_MODULE_KEYS: readonly ["callbar", "forms", "booking", "signups", "articles", "google_reviews"];
|
|
6
6
|
export type DocumentedModuleKey = (typeof DOCUMENTED_MODULE_KEYS)[number];
|
|
7
7
|
export declare const GENERIC_MODULE_VERBS: readonly ["list", "ls", "docs", "doc", "request-setup", "request_setup", "enable", "disable", "archive", "config"];
|
|
8
8
|
export type ModuleCommandDef = {
|
|
@@ -19,10 +19,11 @@ export declare const MODULE_COMMANDS: readonly ModuleCommandDef[];
|
|
|
19
19
|
export declare const GOOGLE_REVIEWS_CLI_ACTIONS: readonly ["connect", "resolve", "status", "list-reviews", "hide", "show", "set-stars", "set-sort", "set-weight", "reset-weights", "write-review-url", "refresh", "disconnect"];
|
|
20
20
|
export declare const TYPED_CONFIG_CLI_ACTIONS: readonly ["get", "set"];
|
|
21
21
|
export declare const STRIPE_OAUTH_CLI_ACTIONS: readonly ["status", "connect", "disconnect"];
|
|
22
|
-
export declare const ORDERING_OAUTH_CLI_ACTIONS: readonly ["status", "connect", "disconnect", "sync"];
|
|
23
22
|
export declare function isCliModuleAction(key: DocumentedModuleKey, action: string): boolean;
|
|
24
23
|
export declare function isDocumentedModuleKey(raw: string): raw is DocumentedModuleKey;
|
|
25
24
|
export declare function isGenericModuleVerb(raw: string): boolean;
|
|
26
25
|
export declare function moduleCommand(key: DocumentedModuleKey): ModuleCommandDef;
|
|
26
|
+
export declare function formatShopIsNotAModule(): string;
|
|
27
|
+
export declare function formatPaymentsIsNotAModule(): string;
|
|
27
28
|
export declare function formatModuleUsage(key: DocumentedModuleKey): string;
|
|
28
29
|
export declare function formatModulesIndex(): string;
|