@saastemly/voidcommerce 0.21.0 → 0.22.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.d.ts +13 -0
- package/dist/cli.js +2 -2
- package/dist/{index-pghh8nnq.js → index-2kqz80nx.js} +2 -2
- package/dist/{index-940vpkwa.js → index-t1ggktg8.js} +16 -9
- package/dist/index.js +2 -2
- package/dist/{keys-mewxnkwg.js → keys-1h4zpvd7.js} +1 -1
- package/package.json +1 -1
- package/src/catalog.ts +13 -0
- package/src/deploy/secrets.ts +15 -5
- package/src/generate/env.ts +10 -4
package/dist/catalog.d.ts
CHANGED
|
@@ -24,6 +24,19 @@ export interface EnvKey {
|
|
|
24
24
|
dev?: string | undefined;
|
|
25
25
|
/** Where the real value comes from. */
|
|
26
26
|
where?: string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Needed to DEPLOY the shop, not to run it.
|
|
29
|
+
*
|
|
30
|
+
* The Cloudflare credentials are the case. They belong in `.env.secrets`
|
|
31
|
+
* so a push carries them, and preflight must insist on them — but the
|
|
32
|
+
* worker has no business seeing one. Left unmarked they ended up in three
|
|
33
|
+
* wrong places at once: `env.ts`, so the worker refused to boot without a
|
|
34
|
+
* deploy token; `.env`, where wrangler read `CLOUDFLARE_ACCOUNT_ID=unset`
|
|
35
|
+
* and addressed `accounts/unset/...`; and `--secrets-file`, which would
|
|
36
|
+
* have uploaded the deploy credential INTO the worker for any code
|
|
37
|
+
* running there to read.
|
|
38
|
+
*/
|
|
39
|
+
deployOnly?: boolean | undefined;
|
|
27
40
|
}
|
|
28
41
|
export interface Choice {
|
|
29
42
|
id: string;
|
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
version,
|
|
26
26
|
voidAppsIn,
|
|
27
27
|
wrangler
|
|
28
|
-
} from "./index-
|
|
28
|
+
} from "./index-2kqz80nx.js";
|
|
29
29
|
import {
|
|
30
30
|
LOCAL_KEY_FILE,
|
|
31
31
|
PRIVATE_KEY_VAR,
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
run,
|
|
41
41
|
secretValue,
|
|
42
42
|
secretsCommand
|
|
43
|
-
} from "./index-
|
|
43
|
+
} from "./index-t1ggktg8.js";
|
|
44
44
|
import {
|
|
45
45
|
LAYOUTS,
|
|
46
46
|
oneOrigin,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
renderEnvTs,
|
|
14
14
|
secretValue,
|
|
15
15
|
unsetSecretNames
|
|
16
|
-
} from "./index-
|
|
16
|
+
} from "./index-t1ggktg8.js";
|
|
17
17
|
import {
|
|
18
18
|
MANIFEST_FILE,
|
|
19
19
|
has,
|
|
@@ -1383,7 +1383,7 @@ import color from "picocolors";
|
|
|
1383
1383
|
// package.json
|
|
1384
1384
|
var package_default = {
|
|
1385
1385
|
name: "@saastemly/voidcommerce",
|
|
1386
|
-
version: "0.
|
|
1386
|
+
version: "0.22.0",
|
|
1387
1387
|
description: "Void, with a shop in it. `vc init` walks you through Better Auth, betterCommerce and every plugin; everything else passes through to `void`.",
|
|
1388
1388
|
type: "module",
|
|
1389
1389
|
license: "MIT",
|
|
@@ -118,12 +118,14 @@ var BASE = [
|
|
|
118
118
|
{
|
|
119
119
|
key: "CLOUDFLARE_API_TOKEN",
|
|
120
120
|
breaks: "nothing deploys: no worker, no database, no migrations",
|
|
121
|
-
where: "dash.cloudflare.com/profile/api-tokens → Create Token → Custom token"
|
|
121
|
+
where: "dash.cloudflare.com/profile/api-tokens → Create Token → Custom token",
|
|
122
|
+
deployOnly: true
|
|
122
123
|
},
|
|
123
124
|
{
|
|
124
125
|
key: "CLOUDFLARE_ACCOUNT_ID",
|
|
125
126
|
breaks: "wrangler cannot tell which account to deploy into, and refuses rather than guessing",
|
|
126
|
-
where: "the Cloudflare dashboard sidebar, or
|
|
127
|
+
where: "the Cloudflare dashboard sidebar, or read from the token on first deploy",
|
|
128
|
+
deployOnly: true
|
|
127
129
|
},
|
|
128
130
|
{
|
|
129
131
|
key: "SHOP_DOMAIN",
|
|
@@ -174,7 +176,7 @@ function allEnvKeys(manifest) {
|
|
|
174
176
|
}
|
|
175
177
|
var NUMERIC = new Set(["SHIPPING_DOMESTIC_MINOR", "SHIPPING_FREE_FROM_MINOR"]);
|
|
176
178
|
function renderEnvTs(manifest) {
|
|
177
|
-
const keys = allEnvKeys(manifest);
|
|
179
|
+
const keys = allEnvKeys(manifest).filter((key) => !key.deployOnly);
|
|
178
180
|
const lines = keys.map((key) => {
|
|
179
181
|
const helper = NUMERIC.has(key.key) ? "number()" : "string()";
|
|
180
182
|
const doc = [` /** ${key.breaks}${key.where ? ` — from: ${key.where}` : ""} */`];
|
|
@@ -203,7 +205,7 @@ ${lines.join(`
|
|
|
203
205
|
`;
|
|
204
206
|
}
|
|
205
207
|
function renderEnvExample(manifest) {
|
|
206
|
-
const keys = allEnvKeys(manifest);
|
|
208
|
+
const keys = allEnvKeys(manifest).filter((key) => !key.deployOnly);
|
|
207
209
|
return [
|
|
208
210
|
"# Every key is required. Copy to .env for local development.",
|
|
209
211
|
"# `unset` is the one value that reads as absent — for a credential you do not have yet.",
|
|
@@ -215,7 +217,7 @@ function renderEnvExample(manifest) {
|
|
|
215
217
|
}
|
|
216
218
|
var MANIFEST_OWNED_ENV = new Set(["SHOP_DOMAIN", "SHOP_ZONE", "EMAIL_FROM", "GITHUB_PAGES_HOST"]);
|
|
217
219
|
function renderEnvLocal(manifest) {
|
|
218
|
-
const keys = allEnvKeys(manifest);
|
|
220
|
+
const keys = allEnvKeys(manifest).filter((key) => !key.deployOnly);
|
|
219
221
|
const local = {
|
|
220
222
|
SHOP_DOMAIN: `${manifest.shop.domain.split(".")[0]}.test`,
|
|
221
223
|
GITHUB_PAGES_HOST: manifest.shop.pagesHost ?? "",
|
|
@@ -370,7 +372,7 @@ async function decryptSecrets(project) {
|
|
|
370
372
|
const root = project.root;
|
|
371
373
|
if (!existsSync2(join2(root, SECRETS_FILE)))
|
|
372
374
|
return { error: `${SECRETS_FILE} does not exist — \`vc secrets init\` writes one` };
|
|
373
|
-
const { localPrivateKey } = await import("./keys-
|
|
375
|
+
const { localPrivateKey } = await import("./keys-1h4zpvd7.js");
|
|
374
376
|
const privateKey = process.env[PRIVATE_KEY_VAR] || localPrivateKey(root) || "";
|
|
375
377
|
if (!privateKey) {
|
|
376
378
|
return {
|
|
@@ -382,7 +384,7 @@ async function decryptSecrets(project) {
|
|
|
382
384
|
}
|
|
383
385
|
const expected = committedPublicKey(root);
|
|
384
386
|
if (expected) {
|
|
385
|
-
const { publicKeyFor } = await import("./keys-
|
|
387
|
+
const { publicKeyFor } = await import("./keys-1h4zpvd7.js");
|
|
386
388
|
const derived = await publicKeyFor(privateKey);
|
|
387
389
|
if (derived && derived.toLowerCase() !== expected.toLowerCase()) {
|
|
388
390
|
return {
|
|
@@ -402,8 +404,13 @@ async function decryptSecrets(project) {
|
|
|
402
404
|
if (result.code !== 0)
|
|
403
405
|
return { error: `dotenvx could not decrypt ${SECRETS_FILE}: ${result.out.trim().split(`
|
|
404
406
|
`).slice(-2).join(" ")}` };
|
|
407
|
+
const deployOnly = new Set(allEnvKeys(project.manifest).filter((key) => key.deployOnly).map((key) => key.key));
|
|
405
408
|
const lines = result.out.split(`
|
|
406
|
-
`).filter((line) =>
|
|
409
|
+
`).filter((line) => {
|
|
410
|
+
if (!/^\s*[A-Z][A-Z0-9_]*\s*=/.test(line) || line.trimStart().startsWith("DOTENV_"))
|
|
411
|
+
return false;
|
|
412
|
+
return !deployOnly.has(line.slice(0, line.indexOf("=")).trim());
|
|
413
|
+
});
|
|
407
414
|
const names = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
|
|
408
415
|
const path = join2(tmpdir(), `vc-secrets-${process.pid}-${Date.now()}.env`);
|
|
409
416
|
writeFileSync(path, `${lines.join(`
|
|
@@ -725,7 +732,7 @@ vc: no ${SECRETS_FILE} here — \`vc secrets --init\` starts a fresh one.
|
|
|
725
732
|
`);
|
|
726
733
|
return 1;
|
|
727
734
|
}
|
|
728
|
-
const { localPrivateKey, publicKeyFor, generateKeypair } = await import("./keys-
|
|
735
|
+
const { localPrivateKey, publicKeyFor, generateKeypair } = await import("./keys-1h4zpvd7.js");
|
|
729
736
|
const current = committedPublicKey(root);
|
|
730
737
|
const mine = process.env[PRIVATE_KEY_VAR] || localPrivateKey(root);
|
|
731
738
|
if (mine && current && (await publicKeyFor(mine))?.toLowerCase() === current.toLowerCase()) {
|
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-2kqz80nx.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-t1ggktg8.js";
|
|
64
64
|
import {
|
|
65
65
|
LAYOUTS,
|
|
66
66
|
MANIFEST_FILE,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saastemly/voidcommerce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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
|
@@ -25,6 +25,19 @@ export interface EnvKey {
|
|
|
25
25
|
dev?: string | undefined;
|
|
26
26
|
/** Where the real value comes from. */
|
|
27
27
|
where?: string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Needed to DEPLOY the shop, not to run it.
|
|
30
|
+
*
|
|
31
|
+
* The Cloudflare credentials are the case. They belong in `.env.secrets`
|
|
32
|
+
* so a push carries them, and preflight must insist on them — but the
|
|
33
|
+
* worker has no business seeing one. Left unmarked they ended up in three
|
|
34
|
+
* wrong places at once: `env.ts`, so the worker refused to boot without a
|
|
35
|
+
* deploy token; `.env`, where wrangler read `CLOUDFLARE_ACCOUNT_ID=unset`
|
|
36
|
+
* and addressed `accounts/unset/...`; and `--secrets-file`, which would
|
|
37
|
+
* have uploaded the deploy credential INTO the worker for any code
|
|
38
|
+
* running there to read.
|
|
39
|
+
*/
|
|
40
|
+
deployOnly?: boolean | undefined;
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
export interface Choice {
|
package/src/deploy/secrets.ts
CHANGED
|
@@ -242,11 +242,21 @@ export async function decryptSecrets(project: Project): Promise<DecryptedSecrets
|
|
|
242
242
|
});
|
|
243
243
|
if (result.code !== 0) return { error: `dotenvx could not decrypt ${SECRETS_FILE}: ${result.out.trim().split("\n").slice(-2).join(" ")}` };
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Keep only the secrets the WORKER needs.
|
|
247
|
+
*
|
|
248
|
+
* dotenvx echoes its own public key back, which has no business being a
|
|
249
|
+
* worker secret. And the deploy-only keys are the important exclusion:
|
|
250
|
+
* `--secrets-file` uploads what it is given, so leaving CLOUDFLARE_API_TOKEN
|
|
251
|
+
* in here would put the credential that can deploy, delete and re-create
|
|
252
|
+
* this shop INSIDE the shop, readable by any code running in the worker.
|
|
253
|
+
* A compromised dependency would have found it there.
|
|
254
|
+
*/
|
|
255
|
+
const deployOnly = new Set(allEnvKeys(project.manifest).filter((key) => key.deployOnly).map((key) => key.key));
|
|
256
|
+
const lines = result.out.split("\n").filter((line) => {
|
|
257
|
+
if (!/^\s*[A-Z][A-Z0-9_]*\s*=/.test(line) || line.trimStart().startsWith("DOTENV_")) return false;
|
|
258
|
+
return !deployOnly.has(line.slice(0, line.indexOf("=")).trim());
|
|
259
|
+
});
|
|
250
260
|
const names = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
|
|
251
261
|
|
|
252
262
|
const path = join(tmpdir(), `vc-secrets-${process.pid}-${Date.now()}.env`);
|
package/src/generate/env.ts
CHANGED
|
@@ -29,11 +29,13 @@ const BASE: EnvKey[] = [
|
|
|
29
29
|
key: "CLOUDFLARE_API_TOKEN",
|
|
30
30
|
breaks: "nothing deploys: no worker, no database, no migrations",
|
|
31
31
|
where: "dash.cloudflare.com/profile/api-tokens → Create Token → Custom token",
|
|
32
|
+
deployOnly: true,
|
|
32
33
|
},
|
|
33
34
|
{
|
|
34
35
|
key: "CLOUDFLARE_ACCOUNT_ID",
|
|
35
36
|
breaks: "wrangler cannot tell which account to deploy into, and refuses rather than guessing",
|
|
36
|
-
where: "the Cloudflare dashboard sidebar, or
|
|
37
|
+
where: "the Cloudflare dashboard sidebar, or read from the token on first deploy",
|
|
38
|
+
deployOnly: true,
|
|
37
39
|
},
|
|
38
40
|
{
|
|
39
41
|
key: "SHOP_DOMAIN",
|
|
@@ -90,7 +92,9 @@ export function allEnvKeys(manifest: Manifest): EnvKey[] {
|
|
|
90
92
|
const NUMERIC = new Set(["SHIPPING_DOMESTIC_MINOR", "SHIPPING_FREE_FROM_MINOR"]);
|
|
91
93
|
|
|
92
94
|
export function renderEnvTs(manifest: Manifest): string {
|
|
93
|
-
|
|
95
|
+
// The worker validates what IT needs. A deploy credential here would make
|
|
96
|
+
// the shop refuse to boot without a token it never uses.
|
|
97
|
+
const keys = allEnvKeys(manifest).filter((key) => !key.deployOnly);
|
|
94
98
|
const lines = keys.map((key) => {
|
|
95
99
|
const helper = NUMERIC.has(key.key) ? "number()" : "string()";
|
|
96
100
|
const doc = [` /** ${key.breaks}${key.where ? ` — from: ${key.where}` : ""} */`];
|
|
@@ -117,7 +121,7 @@ ${lines.join("\n")}
|
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
export function renderEnvExample(manifest: Manifest): string {
|
|
120
|
-
const keys = allEnvKeys(manifest);
|
|
124
|
+
const keys = allEnvKeys(manifest).filter((key) => !key.deployOnly);
|
|
121
125
|
return [
|
|
122
126
|
"# Every key is required. Copy to .env for local development.",
|
|
123
127
|
"# `unset` is the one value that reads as absent — for a credential you do not have yet.",
|
|
@@ -139,7 +143,9 @@ export function renderEnvExample(manifest: Manifest): string {
|
|
|
139
143
|
export const MANIFEST_OWNED_ENV = new Set(["SHOP_DOMAIN", "SHOP_ZONE", "EMAIL_FROM", "GITHUB_PAGES_HOST"]);
|
|
140
144
|
|
|
141
145
|
export function renderEnvLocal(manifest: Manifest): string {
|
|
142
|
-
|
|
146
|
+
// Not in .env either: wrangler READS .env, so a deploy-only key sitting
|
|
147
|
+
// there as `unset` becomes a literal account id — `accounts/unset/...`.
|
|
148
|
+
const keys = allEnvKeys(manifest).filter((key) => !key.deployOnly);
|
|
143
149
|
const local: Record<string, string> = {
|
|
144
150
|
SHOP_DOMAIN: `${manifest.shop.domain.split(".")[0]}.test`,
|
|
145
151
|
GITHUB_PAGES_HOST: manifest.shop.pagesHost ?? "",
|