@saastemly/voidcommerce 0.3.0 → 0.4.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 +2 -1
- package/dist/cli.js +54 -3
- package/dist/deploy/index.d.ts +9 -0
- package/dist/deploy/keys.d.ts +34 -0
- package/dist/deploy/secrets.d.ts +15 -9
- package/dist/index-0v6na3yp.js +35 -0
- package/dist/{index-z4qazajn.js → index-844b3qn9.js} +1 -4
- package/dist/index-mpr7gm6k.js +4597 -0
- package/dist/{index-zh3tmj08.js → index-pz6m2hkm.js} +1 -1
- package/dist/{index-84wq4qp8.js → index-yzezvy5h.js} +273 -110
- package/dist/index.js +4 -3
- package/dist/manifest.js +3 -2
- package/package.json +2 -1
- package/src/cli.ts +4 -1
- package/src/deploy/index.ts +54 -1
- package/src/deploy/keys.ts +212 -0
- package/src/deploy/secrets.ts +43 -21
- package/src/dotenvx-primitives.d.ts +10 -0
- package/src/generate/index.ts +21 -0
- package/src/generate/strict.ts +2 -0
package/dist/catalog.js
CHANGED
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,9 @@ import {
|
|
|
16
16
|
importHelp,
|
|
17
17
|
initHelp,
|
|
18
18
|
isVoidApp,
|
|
19
|
+
keysCommand,
|
|
19
20
|
line,
|
|
21
|
+
plaintextSecretNames,
|
|
20
22
|
preflight,
|
|
21
23
|
printPreflight,
|
|
22
24
|
row,
|
|
@@ -25,16 +27,17 @@ import {
|
|
|
25
27
|
secretsCommand,
|
|
26
28
|
version,
|
|
27
29
|
voidAppsIn
|
|
28
|
-
} from "./index-
|
|
30
|
+
} from "./index-yzezvy5h.js";
|
|
29
31
|
import {
|
|
30
32
|
LAYOUTS,
|
|
31
33
|
readManifest,
|
|
32
34
|
validate,
|
|
33
35
|
zoneOf
|
|
34
|
-
} from "./index-
|
|
36
|
+
} from "./index-pz6m2hkm.js";
|
|
35
37
|
import {
|
|
36
38
|
GROUPS
|
|
37
|
-
} from "./index-
|
|
39
|
+
} from "./index-844b3qn9.js";
|
|
40
|
+
import"./index-0v6na3yp.js";
|
|
38
41
|
|
|
39
42
|
// src/deploy/index.ts
|
|
40
43
|
import color from "picocolors";
|
|
@@ -145,6 +148,52 @@ async function secretsHelp() {
|
|
|
145
148
|
], width));
|
|
146
149
|
return 0;
|
|
147
150
|
}
|
|
151
|
+
async function keysCliCommand(args) {
|
|
152
|
+
const project = await findProject();
|
|
153
|
+
if (!project) {
|
|
154
|
+
console.error("vc: no voidcommerce.json here.");
|
|
155
|
+
return 1;
|
|
156
|
+
}
|
|
157
|
+
return keysCommand(project.manifest, project.root, args);
|
|
158
|
+
}
|
|
159
|
+
async function keysHelp() {
|
|
160
|
+
const width = 80;
|
|
161
|
+
console.log(box("vc keys", [
|
|
162
|
+
line("The key that encrypts this repository's secrets is DERIVED from", width),
|
|
163
|
+
line("CLOUDFLARE_API_TOKEN, salted with the account id. Nothing is stored.", width),
|
|
164
|
+
line("", width),
|
|
165
|
+
...row("vc keys", "what this token derives, and whether it matches the repository", width, 2),
|
|
166
|
+
...row("vc keys --rotate", "re-encrypt under a new token, while the old one still works", width, 2),
|
|
167
|
+
line("", width),
|
|
168
|
+
line("Whoever can deploy the worker can read its secrets, and nobody else can.", width),
|
|
169
|
+
line("The price: rotating the token orphans every encrypted value, a second", width),
|
|
170
|
+
line("admin's token derives a different key, and the build must use the same", width),
|
|
171
|
+
line("token the values were encrypted under. `vc keys` checks the derived", width),
|
|
172
|
+
line("public key against the committed one, so a mismatch is a clear refusal", width),
|
|
173
|
+
line("rather than a decryption error nobody can place.", width)
|
|
174
|
+
], width));
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
async function guardCommand() {
|
|
178
|
+
const project = await findProject();
|
|
179
|
+
if (!project)
|
|
180
|
+
return 0;
|
|
181
|
+
const bare = plaintextSecretNames(project.root);
|
|
182
|
+
if (bare.length === 0)
|
|
183
|
+
return 0;
|
|
184
|
+
console.error(`
|
|
185
|
+
${color.red("✗ refusing the commit")}: ${bare.length} value${bare.length === 1 ? "" : "s"} in ${SECRETS_FILE} ${bare.length === 1 ? "is" : "are"} not encrypted.
|
|
186
|
+
|
|
187
|
+
` + ` ${bare.join(`
|
|
188
|
+
`)}
|
|
189
|
+
|
|
190
|
+
` + ` bunx dotenvx encrypt -f ${SECRETS_FILE}
|
|
191
|
+
|
|
192
|
+
` + color.dim(` Committing a secret in the clear cannot be undone by a later commit;
|
|
193
|
+
the value stays in the history and must be treated as burned.
|
|
194
|
+
`));
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
148
197
|
|
|
149
198
|
// src/init.ts
|
|
150
199
|
import * as p2 from "@clack/prompts";
|
|
@@ -537,6 +586,8 @@ var EXTENDED = {
|
|
|
537
586
|
deploy: { run: deployCommand, help: deployHelp },
|
|
538
587
|
preflight: { run: preflightCommand, help: preflightHelp },
|
|
539
588
|
secrets: { run: secretsCliCommand, help: secretsHelp },
|
|
589
|
+
keys: { run: keysCliCommand, help: keysHelp },
|
|
590
|
+
guard: { run: guardCommand, help: async () => guardCommand() },
|
|
540
591
|
dev: { run: appScript("dev"), help: appScriptHelp("dev") },
|
|
541
592
|
build: { run: appScript("build"), help: appScriptHelp("build") },
|
|
542
593
|
preview: { run: appScript("preview"), help: appScriptHelp("preview") }
|
package/dist/deploy/index.d.ts
CHANGED
|
@@ -17,3 +17,12 @@ export declare function preflightHelp(): Promise<number>;
|
|
|
17
17
|
/** `vc secrets` — what the repository declares, and whether it is encrypted. */
|
|
18
18
|
export declare function secretsCliCommand(args: string[]): Promise<number>;
|
|
19
19
|
export declare function secretsHelp(): Promise<number>;
|
|
20
|
+
/** `vc keys` — what the token derives, and the warning that goes with it. */
|
|
21
|
+
export declare function keysCliCommand(args: string[]): Promise<number>;
|
|
22
|
+
export declare function keysHelp(): Promise<number>;
|
|
23
|
+
/**
|
|
24
|
+
* The pre-commit gate: refuse a commit that would put a secret in the clear.
|
|
25
|
+
*
|
|
26
|
+
* Exit code is the whole interface — a hook cares about nothing else.
|
|
27
|
+
*/
|
|
28
|
+
export declare function guardCommand(): Promise<number>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Manifest } from "../manifest";
|
|
2
|
+
/** Where the token comes from, in the order wrangler itself reads them. */
|
|
3
|
+
export declare function apiToken(): string | null;
|
|
4
|
+
/**
|
|
5
|
+
* The private key this token and account derive.
|
|
6
|
+
*
|
|
7
|
+
* The account id is the salt rather than a constant, so the same token used
|
|
8
|
+
* against two accounts does not produce one key — tokens are often scoped to
|
|
9
|
+
* several accounts, and a shared key across them would be a wider blast
|
|
10
|
+
* radius than anyone asked for.
|
|
11
|
+
*/
|
|
12
|
+
export declare function derivePrivateKey(token: string, accountId: string): string;
|
|
13
|
+
/** The public key for a private one, via dotenvx's own primitive. */
|
|
14
|
+
export declare function publicKeyFor(privateKey: string): Promise<string | null>;
|
|
15
|
+
/** The public key the repository was actually encrypted under. */
|
|
16
|
+
export declare function committedPublicKey(root: string): string | null;
|
|
17
|
+
export type KeyState = {
|
|
18
|
+
ok: true;
|
|
19
|
+
privateKey: string;
|
|
20
|
+
publicKey: string;
|
|
21
|
+
fresh: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
ok: false;
|
|
24
|
+
reason: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The key for this repository, or a refusal that says exactly what is wrong.
|
|
28
|
+
*
|
|
29
|
+
* `fresh` means the repository has no secrets yet, so there is nothing to
|
|
30
|
+
* check the derivation against and any token is as good as another.
|
|
31
|
+
*/
|
|
32
|
+
export declare function keyFor(manifest: Manifest, root: string): Promise<KeyState>;
|
|
33
|
+
/** `vc keys` — what the derivation says, without printing anything secret. */
|
|
34
|
+
export declare function keysCommand(manifest: Manifest, root: string, args: string[]): Promise<number>;
|
package/dist/deploy/secrets.d.ts
CHANGED
|
@@ -13,8 +13,9 @@ import type { Project } from "../project";
|
|
|
13
13
|
* dotenvx closes it. `.env.secrets` holds ciphertext and a public key, and
|
|
14
14
|
* it is COMMITTED: the key names are readable, the values are not, and a
|
|
15
15
|
* diff shows when a secret changed without showing what it changed to. The
|
|
16
|
-
* private key is
|
|
17
|
-
*
|
|
16
|
+
* private key is not stored at all — it is derived from the Cloudflare API
|
|
17
|
+
* token, so whoever can deploy the worker can read its secrets and nobody
|
|
18
|
+
* else can. See `./keys.ts`, which also spells out what that costs.
|
|
18
19
|
*
|
|
19
20
|
* ── Why the values still become worker SECRETS ───────────────────────────
|
|
20
21
|
*
|
|
@@ -27,14 +28,19 @@ import type { Project } from "../project";
|
|
|
27
28
|
* applies ADDITIVELY, so a secret this file does not name is left alone
|
|
28
29
|
* rather than deleted.
|
|
29
30
|
*
|
|
30
|
-
* ── The
|
|
31
|
+
* ── The tradeoffs, stated ────────────────────────────────────────────────
|
|
31
32
|
*
|
|
32
|
-
* Ciphertext in git is permanent.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
33
|
+
* Ciphertext in git is permanent. A key that ever leaks reads every secret
|
|
34
|
+
* in the history, including ones that were rotated — which is not true of
|
|
35
|
+
* `wrangler secret put`, where a rotation genuinely retires the old value.
|
|
36
|
+
*
|
|
37
|
+
* And because the key is derived, rotating the API token orphans the lot.
|
|
38
|
+
* `keys.ts` guards that with a public-key check, so the failure is a clear
|
|
39
|
+
* refusal rather than a decryption error nobody can place — but the guard
|
|
40
|
+
* cannot recover values, only explain them.
|
|
41
|
+
*
|
|
42
|
+
* Both are the price of a shop that rebuilds from a checkout, and both
|
|
43
|
+
* should be decisions rather than surprises.
|
|
38
44
|
*/
|
|
39
45
|
export declare const SECRETS_FILE = ".env.secrets";
|
|
40
46
|
export declare const PRIVATE_KEY_VAR = "DOTENV_PRIVATE_KEY_SECRETS";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
34
|
+
|
|
35
|
+
export { __toESM, __commonJS, __require };
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
-
|
|
4
1
|
// src/catalog.ts
|
|
5
2
|
var SIGN_IN = {
|
|
6
3
|
id: "signIn",
|
|
@@ -586,4 +583,4 @@ var GROUPS = [
|
|
|
586
583
|
];
|
|
587
584
|
var CHOICES = new Map(GROUPS.flatMap((group) => group.choices.map((choice) => [choice.id, choice])));
|
|
588
585
|
|
|
589
|
-
export {
|
|
586
|
+
export { SIGN_IN, AUTH_PLUGINS, COMMERCE_CATALOG, COMMERCE_SALES, COMMERCE_OPERATIONS, PAYMENT, TAX, CARRIERS, ERP, EMAIL, DNS, CONTENT, UI, GROUPS, CHOICES };
|