@saastemly/voidcommerce 0.13.0 → 0.14.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/catalog.js +1 -1
- package/dist/cli.js +4 -4
- package/dist/{index-727eybzt.js → index-30y19qz5.js} +1 -1
- package/dist/{index-ze4z3n5q.js → index-jvsednjr.js} +38 -25
- package/dist/{index-cxse24q6.js → index-paeg95y4.js} +2 -2
- package/dist/{index-0je92d88.js → index-xyjhy6kp.js} +55 -6
- package/dist/index.js +4 -4
- package/dist/{keys-9efk9rdz.js → keys-nntc65pq.js} +3 -3
- package/dist/manifest.js +2 -2
- package/package.json +1 -1
- package/src/catalog.ts +43 -5
- package/src/generate/index.ts +5 -1
- package/src/generate/support.ts +35 -20
package/dist/catalog.js
CHANGED
package/dist/cli.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
runVoid,
|
|
22
22
|
version,
|
|
23
23
|
voidAppsIn
|
|
24
|
-
} from "./index-
|
|
24
|
+
} from "./index-jvsednjr.js";
|
|
25
25
|
import {
|
|
26
26
|
LOCAL_KEY_FILE,
|
|
27
27
|
PRIVATE_KEY_VAR,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
setVariable,
|
|
51
51
|
variableNames,
|
|
52
52
|
verifyCloudflareToken
|
|
53
|
-
} from "./index-
|
|
53
|
+
} from "./index-paeg95y4.js";
|
|
54
54
|
import {
|
|
55
55
|
LAYOUTS,
|
|
56
56
|
oneOrigin,
|
|
@@ -58,10 +58,10 @@ import {
|
|
|
58
58
|
validate,
|
|
59
59
|
writeManifest,
|
|
60
60
|
zoneOf
|
|
61
|
-
} from "./index-
|
|
61
|
+
} from "./index-30y19qz5.js";
|
|
62
62
|
import {
|
|
63
63
|
GROUPS
|
|
64
|
-
} from "./index-
|
|
64
|
+
} from "./index-xyjhy6kp.js";
|
|
65
65
|
import {
|
|
66
66
|
__require
|
|
67
67
|
} from "./index-0v6na3yp.js";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
renderEnvLocal,
|
|
12
12
|
renderEnvProduction,
|
|
13
13
|
renderEnvTs
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-paeg95y4.js";
|
|
15
15
|
import {
|
|
16
16
|
MANIFEST_FILE,
|
|
17
17
|
has,
|
|
@@ -24,10 +24,10 @@ import {
|
|
|
24
24
|
workerHosts,
|
|
25
25
|
writeManifest,
|
|
26
26
|
zone
|
|
27
|
-
} from "./index-
|
|
27
|
+
} from "./index-30y19qz5.js";
|
|
28
28
|
import {
|
|
29
29
|
CHOICES
|
|
30
|
-
} from "./index-
|
|
30
|
+
} from "./index-xyjhy6kp.js";
|
|
31
31
|
import {
|
|
32
32
|
__require
|
|
33
33
|
} from "./index-0v6na3yp.js";
|
|
@@ -1378,7 +1378,7 @@ import color from "picocolors";
|
|
|
1378
1378
|
// package.json
|
|
1379
1379
|
var package_default = {
|
|
1380
1380
|
name: "@saastemly/voidcommerce",
|
|
1381
|
-
version: "0.
|
|
1381
|
+
version: "0.14.0",
|
|
1382
1382
|
description: "Void, with a shop in it. `vc init` walks you through Better Auth, betterCommerce and every plugin; everything else passes through to `void`.",
|
|
1383
1383
|
type: "module",
|
|
1384
1384
|
license: "MIT",
|
|
@@ -2232,44 +2232,57 @@ function renderShippingTs(manifest) {
|
|
|
2232
2232
|
const parcel2go = has(manifest, "parcel2go");
|
|
2233
2233
|
const origin = ["parcel2go", "dhl-express", "shippo", "easypost", "ups", "fedex"].some((id) => has(manifest, id));
|
|
2234
2234
|
const imports = [
|
|
2235
|
+
...origin || parcel2go ? [`import { env } from "void/env";`] : [],
|
|
2235
2236
|
...origin ? [`import type { DeliveryAddress } from "@saastemly/better-commerce/providers/delivery";`] : [],
|
|
2236
2237
|
...royalMail ? [`import type { RoyalMailService } from "@saastemly/better-commerce/providers/royal-mail";`] : []
|
|
2237
2238
|
];
|
|
2238
|
-
const
|
|
2239
|
+
const header = `
|
|
2239
2240
|
/**
|
|
2240
|
-
*
|
|
2241
|
+
* Where parcels come FROM, and who the carrier account belongs to.
|
|
2242
|
+
*
|
|
2243
|
+
* These are CONFIG, not code: they vary per deploy, they are nobody's
|
|
2244
|
+
* business but this shop's, and hard-coding them here would mean a fresh
|
|
2245
|
+
* clone of this repository shipped somebody else's address. So they come
|
|
2246
|
+
* from the environment — declared in env.ts, encrypted in .env.secrets, and
|
|
2247
|
+
* set with \`vc secrets set\`.
|
|
2241
2248
|
*
|
|
2242
|
-
*
|
|
2243
|
-
*
|
|
2249
|
+
* This file's job is to read them and fail loudly when one is missing, which
|
|
2250
|
+
* is the difference between a shop that refuses to quote and a shop that
|
|
2251
|
+
* quotes against the wrong postcode and loses money on every order.
|
|
2244
2252
|
*/
|
|
2253
|
+
const must = (key: string): string => {
|
|
2254
|
+
const value = (env as Record<string, unknown>)[key];
|
|
2255
|
+
if (typeof value !== "string" || !value || value === "unset") {
|
|
2256
|
+
throw new Error(\`shipping: \${key} is not set — \\\`vc secrets set \${key}\\\`\`);
|
|
2257
|
+
}
|
|
2258
|
+
return value;
|
|
2259
|
+
};
|
|
2260
|
+
`;
|
|
2261
|
+
const originBlock = `
|
|
2262
|
+
/** The address every parcel is quoted against, and the door a courier is sent to. */
|
|
2245
2263
|
export const shipFrom: DeliveryAddress = {
|
|
2246
|
-
name: "
|
|
2247
|
-
line1: "
|
|
2248
|
-
city: "
|
|
2249
|
-
postalCode: "
|
|
2264
|
+
name: must("SHIP_FROM_NAME"),
|
|
2265
|
+
line1: must("SHIP_FROM_LINE1"),
|
|
2266
|
+
city: must("SHIP_FROM_CITY"),
|
|
2267
|
+
postalCode: must("SHIP_FROM_POSTCODE"),
|
|
2250
2268
|
country: "${manifest.shop.country}",
|
|
2251
|
-
phone: "
|
|
2269
|
+
phone: must("SHIP_FROM_PHONE"),
|
|
2252
2270
|
};
|
|
2253
2271
|
`;
|
|
2254
2272
|
const parcel2goBlock = `
|
|
2255
2273
|
/**
|
|
2256
|
-
* Who the Parcel2Go account belongs to.
|
|
2257
|
-
*
|
|
2258
|
-
* Every order carries it: Parcel2Go books a courier to come to a real person
|
|
2259
|
-
* at a real door, and needs to know whose door. This is the ACCOUNT holder,
|
|
2260
|
-
* not the buyer — the buyer's details ride on the parcel.
|
|
2261
|
-
*
|
|
2262
|
-
* TODO VERIFY: these are placeholders.
|
|
2274
|
+
* Who the Parcel2Go account belongs to — the ACCOUNT holder, not the buyer.
|
|
2275
|
+
* Parcel2Go books a courier for a named person and rejects an order without.
|
|
2263
2276
|
*/
|
|
2264
2277
|
export const parcel2goCustomer = {
|
|
2265
|
-
email: "
|
|
2266
|
-
forename: "
|
|
2267
|
-
surname: "
|
|
2278
|
+
email: must("PARCEL2GO_CUSTOMER_EMAIL"),
|
|
2279
|
+
forename: must("PARCEL2GO_CUSTOMER_FORENAME"),
|
|
2280
|
+
surname: must("PARCEL2GO_CUSTOMER_SURNAME"),
|
|
2268
2281
|
};
|
|
2269
2282
|
`;
|
|
2270
2283
|
return `${imports.join(`
|
|
2271
2284
|
`)}
|
|
2272
|
-
${origin ? originBlock : ""}${parcel2go ? parcel2goBlock : ""}${royalMail ? ROYAL_MAIL_SERVICES() : ""}`;
|
|
2285
|
+
${origin || parcel2go ? header : ""}${origin ? originBlock : ""}${parcel2go ? parcel2goBlock : ""}${royalMail ? ROYAL_MAIL_SERVICES() : ""}`;
|
|
2273
2286
|
}
|
|
2274
2287
|
function renderErpTs() {
|
|
2275
2288
|
return `import { businessCentralProvider } from "@saastemly/better-commerce/providers/business-central";
|
|
@@ -2460,7 +2473,7 @@ async function generateApi(root, dir, manifest, result, opts) {
|
|
|
2460
2473
|
await put(root, at("lib/mint.ts"), renderMintTs(), result, own);
|
|
2461
2474
|
}
|
|
2462
2475
|
if (WIRED_CARRIERS.some((id) => needsShippingConfig(id) && has(manifest, id))) {
|
|
2463
|
-
await put(root, at("lib/shipping.ts"), renderShippingTs(manifest), result,
|
|
2476
|
+
await put(root, at("lib/shipping.ts"), renderShippingTs(manifest), result, "regenerate");
|
|
2464
2477
|
}
|
|
2465
2478
|
if (has(manifest, "business-central")) {
|
|
2466
2479
|
await put(root, at("lib/erp.ts"), renderErpTs(), result, own);
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
isApex,
|
|
5
5
|
oneOrigin,
|
|
6
6
|
zone
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-30y19qz5.js";
|
|
8
8
|
import {
|
|
9
9
|
__require,
|
|
10
10
|
__toESM
|
|
@@ -389,7 +389,7 @@ async function decryptSecrets(project) {
|
|
|
389
389
|
}
|
|
390
390
|
const expected = committedPublicKey(root);
|
|
391
391
|
if (expected) {
|
|
392
|
-
const { publicKeyFor } = await import("./keys-
|
|
392
|
+
const { publicKeyFor } = await import("./keys-nntc65pq.js");
|
|
393
393
|
const derived = await publicKeyFor(privateKey);
|
|
394
394
|
if (derived && derived.toLowerCase() !== expected.toLowerCase()) {
|
|
395
395
|
return {
|
|
@@ -447,14 +447,63 @@ var CARRIERS = {
|
|
|
447
447
|
recommended: true,
|
|
448
448
|
env: [
|
|
449
449
|
{ key: "PARCEL2GO_CLIENT_ID", breaks: "no rates are quoted and no collection is ever booked", where: "parcel2go.com/myaccount/api → Client Credentials" },
|
|
450
|
-
{ key: "PARCEL2GO_CLIENT_SECRET", breaks: "the same — and Parcel2Go will not show it twice, so keep it when you make it", where: "you choose this value when creating the credentials" }
|
|
450
|
+
{ key: "PARCEL2GO_CLIENT_SECRET", breaks: "the same — and Parcel2Go will not show it twice, so keep it when you make it", where: "you choose this value when creating the credentials" },
|
|
451
|
+
{ key: "PARCEL2GO_CUSTOMER_EMAIL", breaks: "Parcel2Go rejects every order: it books a courier for a named person and will not accept one without", where: "the email on your Parcel2Go account" },
|
|
452
|
+
{ key: "PARCEL2GO_CUSTOMER_FORENAME", breaks: "the same" },
|
|
453
|
+
{ key: "PARCEL2GO_CUSTOMER_SURNAME", breaks: "the same" },
|
|
454
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
455
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
456
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
457
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
458
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" }
|
|
451
459
|
]
|
|
452
460
|
},
|
|
453
|
-
{ id: "shippo", label: "Shippo", hint: "many carriers through one API", env: [
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
461
|
+
{ id: "shippo", label: "Shippo", hint: "many carriers through one API", env: [
|
|
462
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
463
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
464
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
465
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
466
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
467
|
+
{ key: "SHIPPO_API_TOKEN", breaks: "calculated rates fall back to flat" }
|
|
468
|
+
] },
|
|
469
|
+
{ id: "easypost", label: "EasyPost", hint: "the same, US-leaning", env: [
|
|
470
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
471
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
472
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
473
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
474
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
475
|
+
{ key: "EASYPOST_API_KEY", breaks: "calculated rates fall back to flat" }
|
|
476
|
+
] },
|
|
477
|
+
{ id: "ups", label: "UPS", hint: "your own UPS account, for negotiated rates an aggregator cannot see", env: [
|
|
478
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
479
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
480
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
481
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
482
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
483
|
+
{ key: "UPS_CLIENT_ID", breaks: "UPS rates are not quoted" },
|
|
484
|
+
{ key: "UPS_CLIENT_SECRET", breaks: "the same" },
|
|
485
|
+
{ key: "UPS_ACCOUNT_NUMBER", breaks: "UPS has no account to bill the transport to" }
|
|
486
|
+
] },
|
|
487
|
+
{ id: "fedex", label: "FedEx", hint: "your own FedEx account, for negotiated rates an aggregator cannot see", env: [
|
|
488
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
489
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
490
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
491
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
492
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
493
|
+
{ key: "FEDEX_CLIENT_ID", breaks: "FedEx rates are not quoted" },
|
|
494
|
+
{ key: "FEDEX_CLIENT_SECRET", breaks: "the same" },
|
|
495
|
+
{ key: "FEDEX_ACCOUNT_NUMBER", breaks: "FedEx has no account to bill the transport to" }
|
|
496
|
+
] },
|
|
497
|
+
{ id: "dhl-express", label: "DHL Express", hint: "international express, and the one worth having for a shop that posts abroad", env: [
|
|
498
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
499
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
500
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
501
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
502
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
503
|
+
{ key: "DHL_API_KEY", breaks: "DHL rates are not quoted" },
|
|
504
|
+
{ key: "DHL_API_SECRET", breaks: "the same" },
|
|
505
|
+
{ key: "DHL_ACCOUNT_NUMBER", breaks: "DHL has no account to bill the transport to" }
|
|
506
|
+
] },
|
|
458
507
|
{ id: "usps", label: "USPS", hint: "US domestic post — the cheapest way to ship a small parcel inside the US", env: [{ key: "USPS_CLIENT_ID", breaks: "USPS rates are not quoted" }, { key: "USPS_CLIENT_SECRET", breaks: "the same" }] },
|
|
459
508
|
{
|
|
460
509
|
id: "royal-mail",
|
package/dist/index.js
CHANGED
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
routeProblem,
|
|
53
53
|
strictDependencies,
|
|
54
54
|
upsertJsonc
|
|
55
|
-
} from "./index-
|
|
55
|
+
} from "./index-jvsednjr.js";
|
|
56
56
|
import {
|
|
57
57
|
allEnvKeys,
|
|
58
58
|
envSummary,
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
renderEnvLocal,
|
|
61
61
|
renderEnvProduction,
|
|
62
62
|
renderEnvTs
|
|
63
|
-
} from "./index-
|
|
63
|
+
} from "./index-paeg95y4.js";
|
|
64
64
|
import {
|
|
65
65
|
LAYOUTS,
|
|
66
66
|
MANIFEST_FILE,
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
writeManifest,
|
|
82
82
|
zone,
|
|
83
83
|
zoneOf
|
|
84
|
-
} from "./index-
|
|
84
|
+
} from "./index-30y19qz5.js";
|
|
85
85
|
import {
|
|
86
86
|
AUTH_PLUGINS,
|
|
87
87
|
CARRIERS,
|
|
@@ -98,7 +98,7 @@ import {
|
|
|
98
98
|
SIGN_IN,
|
|
99
99
|
TAX,
|
|
100
100
|
UI
|
|
101
|
-
} from "./index-
|
|
101
|
+
} from "./index-xyjhy6kp.js";
|
|
102
102
|
import"./index-0v6na3yp.js";
|
|
103
103
|
export {
|
|
104
104
|
zoneOf,
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
localPrivateKey,
|
|
9
9
|
provisionKey,
|
|
10
10
|
publicKeyFor
|
|
11
|
-
} from "./index-
|
|
12
|
-
import"./index-
|
|
13
|
-
import"./index-
|
|
11
|
+
} from "./index-paeg95y4.js";
|
|
12
|
+
import"./index-30y19qz5.js";
|
|
13
|
+
import"./index-xyjhy6kp.js";
|
|
14
14
|
import"./index-0v6na3yp.js";
|
|
15
15
|
export {
|
|
16
16
|
publicKeyFor,
|
package/dist/manifest.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saastemly/voidcommerce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Void, with a shop in it. `vc init` walks you through Better Auth, betterCommerce and every plugin; everything else passes through to `void`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/catalog.ts
CHANGED
|
@@ -521,13 +521,51 @@ export const CARRIERS: Group = {
|
|
|
521
521
|
env: [
|
|
522
522
|
{ key: "PARCEL2GO_CLIENT_ID", breaks: "no rates are quoted and no collection is ever booked", where: "parcel2go.com/myaccount/api → Client Credentials" },
|
|
523
523
|
{ key: "PARCEL2GO_CLIENT_SECRET", breaks: "the same — and Parcel2Go will not show it twice, so keep it when you make it", where: "you choose this value when creating the credentials" },
|
|
524
|
+
{ key: "PARCEL2GO_CUSTOMER_EMAIL", breaks: "Parcel2Go rejects every order: it books a courier for a named person and will not accept one without", where: "the email on your Parcel2Go account" },
|
|
525
|
+
{ key: "PARCEL2GO_CUSTOMER_FORENAME", breaks: "the same" },
|
|
526
|
+
{ key: "PARCEL2GO_CUSTOMER_SURNAME", breaks: "the same" },
|
|
527
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
528
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
529
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
530
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
531
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
524
532
|
],
|
|
525
533
|
},
|
|
526
|
-
{ id: "shippo", label: "Shippo", hint: "many carriers through one API", env: [
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
534
|
+
{ id: "shippo", label: "Shippo", hint: "many carriers through one API", env: [
|
|
535
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
536
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
537
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
538
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
539
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
540
|
+
{ key: "SHIPPO_API_TOKEN", breaks: "calculated rates fall back to flat" }] },
|
|
541
|
+
{ id: "easypost", label: "EasyPost", hint: "the same, US-leaning", env: [
|
|
542
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
543
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
544
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
545
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
546
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
547
|
+
{ key: "EASYPOST_API_KEY", breaks: "calculated rates fall back to flat" }] },
|
|
548
|
+
{ id: "ups", label: "UPS", hint: "your own UPS account, for negotiated rates an aggregator cannot see", env: [
|
|
549
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
550
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
551
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
552
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
553
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
554
|
+
{ key: "UPS_CLIENT_ID", breaks: "UPS rates are not quoted" }, { key: "UPS_CLIENT_SECRET", breaks: "the same" }, { key: "UPS_ACCOUNT_NUMBER", breaks: "UPS has no account to bill the transport to" }] },
|
|
555
|
+
{ id: "fedex", label: "FedEx", hint: "your own FedEx account, for negotiated rates an aggregator cannot see", env: [
|
|
556
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
557
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
558
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
559
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
560
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
561
|
+
{ key: "FEDEX_CLIENT_ID", breaks: "FedEx rates are not quoted" }, { key: "FEDEX_CLIENT_SECRET", breaks: "the same" }, { key: "FEDEX_ACCOUNT_NUMBER", breaks: "FedEx has no account to bill the transport to" }] },
|
|
562
|
+
{ id: "dhl-express", label: "DHL Express", hint: "international express, and the one worth having for a shop that posts abroad", env: [
|
|
563
|
+
{ key: "SHIP_FROM_NAME", breaks: "a courier has no one to ask for at the door, and most carriers refuse an origin with no name" },
|
|
564
|
+
{ key: "SHIP_FROM_LINE1", breaks: "the courier is sent to an incomplete address" },
|
|
565
|
+
{ key: "SHIP_FROM_CITY", breaks: "the same" },
|
|
566
|
+
{ key: "SHIP_FROM_POSTCODE", breaks: "every rate is quoted against the wrong place, so every order is mispriced" },
|
|
567
|
+
{ key: "SHIP_FROM_PHONE", breaks: "a driver who cannot find the door has no one to ring" },
|
|
568
|
+
{ key: "DHL_API_KEY", breaks: "DHL rates are not quoted" }, { key: "DHL_API_SECRET", breaks: "the same" }, { key: "DHL_ACCOUNT_NUMBER", breaks: "DHL has no account to bill the transport to" }] },
|
|
531
569
|
{ id: "usps", label: "USPS", hint: "US domestic post — the cheapest way to ship a small parcel inside the US", env: [{ key: "USPS_CLIENT_ID", breaks: "USPS rates are not quoted" }, { key: "USPS_CLIENT_SECRET", breaks: "the same" }] },
|
|
532
570
|
{
|
|
533
571
|
id: "royal-mail",
|
package/src/generate/index.ts
CHANGED
|
@@ -248,7 +248,11 @@ async function generateApi(root: string, dir: string, manifest: Manifest, result
|
|
|
248
248
|
// against, or a price table. The list lives with the carriers themselves so
|
|
249
249
|
// it cannot drift from them.
|
|
250
250
|
if (WIRED_CARRIERS.some((id) => needsShippingConfig(id) && has(manifest, id))) {
|
|
251
|
-
|
|
251
|
+
// REGENERATED, not owned. It used to carry the shop's address as
|
|
252
|
+
// constants, which made it config and so nobody's to overwrite. Now it
|
|
253
|
+
// only READS that config from the environment, so it is ordinary code
|
|
254
|
+
// and should receive fixes like any other generated file.
|
|
255
|
+
await put(root, at("lib/shipping.ts"), renderShippingTs(manifest), result, "regenerate");
|
|
252
256
|
}
|
|
253
257
|
if (has(manifest, "business-central")) {
|
|
254
258
|
await put(root, at("lib/erp.ts"), renderErpTs(), result, own);
|
package/src/generate/support.ts
CHANGED
|
@@ -281,44 +281,59 @@ export function renderShippingTs(manifest: Manifest): string {
|
|
|
281
281
|
const origin = ["parcel2go", "dhl-express", "shippo", "easypost", "ups", "fedex"].some((id) => has(manifest, id));
|
|
282
282
|
|
|
283
283
|
const imports = [
|
|
284
|
+
...(origin || parcel2go ? [`import { env } from "void/env";`] : []),
|
|
284
285
|
...(origin ? [`import type { DeliveryAddress } from "@saastemly/better-commerce/providers/delivery";`] : []),
|
|
285
286
|
...(royalMail ? [`import type { RoyalMailService } from "@saastemly/better-commerce/providers/royal-mail";`] : []),
|
|
286
287
|
];
|
|
287
288
|
|
|
288
|
-
const
|
|
289
|
+
const header = `
|
|
289
290
|
/**
|
|
290
|
-
*
|
|
291
|
+
* Where parcels come FROM, and who the carrier account belongs to.
|
|
292
|
+
*
|
|
293
|
+
* These are CONFIG, not code: they vary per deploy, they are nobody's
|
|
294
|
+
* business but this shop's, and hard-coding them here would mean a fresh
|
|
295
|
+
* clone of this repository shipped somebody else's address. So they come
|
|
296
|
+
* from the environment — declared in env.ts, encrypted in .env.secrets, and
|
|
297
|
+
* set with \`vc secrets set\`.
|
|
291
298
|
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
299
|
+
* This file's job is to read them and fail loudly when one is missing, which
|
|
300
|
+
* is the difference between a shop that refuses to quote and a shop that
|
|
301
|
+
* quotes against the wrong postcode and loses money on every order.
|
|
294
302
|
*/
|
|
303
|
+
const must = (key: string): string => {
|
|
304
|
+
const value = (env as Record<string, unknown>)[key];
|
|
305
|
+
if (typeof value !== "string" || !value || value === "unset") {
|
|
306
|
+
throw new Error(\`shipping: \${key} is not set — \\\`vc secrets set \${key}\\\`\`);
|
|
307
|
+
}
|
|
308
|
+
return value;
|
|
309
|
+
};
|
|
310
|
+
`;
|
|
311
|
+
|
|
312
|
+
const originBlock = `
|
|
313
|
+
/** The address every parcel is quoted against, and the door a courier is sent to. */
|
|
295
314
|
export const shipFrom: DeliveryAddress = {
|
|
296
|
-
name: "
|
|
297
|
-
line1: "
|
|
298
|
-
city: "
|
|
299
|
-
postalCode: "
|
|
315
|
+
name: must("SHIP_FROM_NAME"),
|
|
316
|
+
line1: must("SHIP_FROM_LINE1"),
|
|
317
|
+
city: must("SHIP_FROM_CITY"),
|
|
318
|
+
postalCode: must("SHIP_FROM_POSTCODE"),
|
|
300
319
|
country: "${manifest.shop.country}",
|
|
301
|
-
phone: "
|
|
320
|
+
phone: must("SHIP_FROM_PHONE"),
|
|
302
321
|
};
|
|
303
322
|
`;
|
|
304
323
|
|
|
305
324
|
const parcel2goBlock = `
|
|
306
325
|
/**
|
|
307
|
-
* Who the Parcel2Go account belongs to.
|
|
308
|
-
*
|
|
309
|
-
* Every order carries it: Parcel2Go books a courier to come to a real person
|
|
310
|
-
* at a real door, and needs to know whose door. This is the ACCOUNT holder,
|
|
311
|
-
* not the buyer — the buyer's details ride on the parcel.
|
|
312
|
-
*
|
|
313
|
-
* TODO VERIFY: these are placeholders.
|
|
326
|
+
* Who the Parcel2Go account belongs to — the ACCOUNT holder, not the buyer.
|
|
327
|
+
* Parcel2Go books a courier for a named person and rejects an order without.
|
|
314
328
|
*/
|
|
315
329
|
export const parcel2goCustomer = {
|
|
316
|
-
email: "
|
|
317
|
-
forename: "
|
|
318
|
-
surname: "
|
|
330
|
+
email: must("PARCEL2GO_CUSTOMER_EMAIL"),
|
|
331
|
+
forename: must("PARCEL2GO_CUSTOMER_FORENAME"),
|
|
332
|
+
surname: must("PARCEL2GO_CUSTOMER_SURNAME"),
|
|
319
333
|
};
|
|
320
334
|
`;
|
|
321
|
-
|
|
335
|
+
|
|
336
|
+
return `${imports.join("\n")}\n${origin || parcel2go ? header : ""}${origin ? originBlock : ""}${parcel2go ? parcel2goBlock : ""}${royalMail ? ROYAL_MAIL_SERVICES() : ""}`;
|
|
322
337
|
}
|
|
323
338
|
|
|
324
339
|
export function renderErpTs(): string {
|