@ponharu/pkgflare 0.0.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/cli.js ADDED
@@ -0,0 +1,528 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { randomBytes } from "crypto";
5
+ import { open } from "fs/promises";
6
+ import { relative as relative2, resolve as resolve3 } from "path";
7
+
8
+ // src/cli/deploy.ts
9
+ import { copyFile, mkdir, readFile, readdir, writeFile } from "fs/promises";
10
+ import { dirname as dirname2, join as join2, relative, resolve as resolve2 } from "path";
11
+ import { fileURLToPath } from "url";
12
+
13
+ // src/cli/config-loader.ts
14
+ import { access } from "fs/promises";
15
+ import { resolve } from "path";
16
+ import { pathToFileURL } from "url";
17
+ import { createJiti } from "jiti";
18
+
19
+ // src/config.ts
20
+ var workerNamePattern = /^[a-z](?:[a-z0-9-]{0,52}[a-z0-9])?$/;
21
+ var accountIdPattern = /^[a-f0-9]{32}$/;
22
+ var scopePattern = /^@[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;
23
+ var bindingPattern = /^[A-Z][A-Z0-9_]*$/;
24
+ var hostnamePattern = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
25
+ var reservedBindings = /* @__PURE__ */ new Set(["PKGFLARE_DB", "PKGFLARE_BUCKET", "PKGFLARE_CONFIG"]);
26
+ function isObject(value) {
27
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28
+ }
29
+ function normalizeConfig(value) {
30
+ if (!isObject(value)) throw new Error("configuration must be an object");
31
+ const { name, scopes: scopeValues, accountId, hostname, auth } = value;
32
+ if (typeof name !== "string" || !workerNamePattern.test(name)) {
33
+ throw new Error("name must be a lowercase Cloudflare resource name of at most 54 characters");
34
+ }
35
+ if (accountId !== void 0 && (typeof accountId !== "string" || !accountIdPattern.test(accountId))) {
36
+ throw new Error("accountId must be a 32-character lowercase hexadecimal Cloudflare account ID");
37
+ }
38
+ if (!Array.isArray(scopeValues) || scopeValues.length === 0 || scopeValues.some((scope) => typeof scope !== "string")) {
39
+ throw new Error("at least one package scope is required");
40
+ }
41
+ const scopes = [...new Set(scopeValues)];
42
+ for (const scope of scopes) {
43
+ if (!scopePattern.test(scope)) {
44
+ throw new Error(`invalid package scope: ${scope}`);
45
+ }
46
+ }
47
+ if (hostname !== void 0 && (typeof hostname !== "string" || !hostnamePattern.test(hostname))) {
48
+ throw new Error("hostname must be a valid DNS hostname without a scheme or path");
49
+ }
50
+ if (!isObject(auth) || auth.provider !== "secrets") {
51
+ throw new Error("unsupported authentication provider");
52
+ }
53
+ if (!Array.isArray(auth.tokens) || auth.tokens.length === 0) {
54
+ throw new Error("at least one authentication token binding is required");
55
+ }
56
+ const seenBindings = /* @__PURE__ */ new Set();
57
+ const tokens = auth.tokens.map((tokenValue) => {
58
+ if (!isObject(tokenValue) || typeof tokenValue.binding !== "string") {
59
+ throw new Error("authentication token binding must be an object with a binding name");
60
+ }
61
+ const { binding, permissions: permissionValues } = tokenValue;
62
+ if (!bindingPattern.test(binding)) {
63
+ throw new Error(`invalid secret binding: ${binding}`);
64
+ }
65
+ if (reservedBindings.has(binding)) {
66
+ throw new Error(`secret binding is reserved by pkgflare: ${binding}`);
67
+ }
68
+ if (seenBindings.has(binding)) {
69
+ throw new Error(`duplicate secret binding: ${binding}`);
70
+ }
71
+ seenBindings.add(binding);
72
+ if (!Array.isArray(permissionValues) || permissionValues.length === 0 || permissionValues.some((permission) => permission !== "read" && permission !== "publish")) {
73
+ throw new Error(`invalid permissions for ${binding}`);
74
+ }
75
+ const permissions = [...new Set(permissionValues)];
76
+ return { binding, permissions };
77
+ });
78
+ return {
79
+ name,
80
+ scopes,
81
+ ...accountId === void 0 ? {} : { accountId },
82
+ ...hostname === void 0 ? {} : { hostname },
83
+ auth: { provider: "secrets", tokens }
84
+ };
85
+ }
86
+
87
+ // src/cli/config-loader.ts
88
+ var defaultConfigFile = "pkgflare.config.ts";
89
+ async function loadConfig(cwd, configFile = defaultConfigFile) {
90
+ const path = resolve(cwd, configFile);
91
+ try {
92
+ await access(path);
93
+ } catch {
94
+ throw new Error(`configuration file not found: ${configFile}`);
95
+ }
96
+ const jiti = createJiti(pathToFileURL(import.meta.url).href, {
97
+ interopDefault: true,
98
+ moduleCache: false
99
+ });
100
+ const imported = await jiti.import(path, { default: true });
101
+ return normalizeConfig(imported);
102
+ }
103
+
104
+ // src/cli/runner.ts
105
+ import { spawn } from "child_process";
106
+ import { createRequire } from "module";
107
+ import { dirname, join } from "path";
108
+ function wranglerEntry() {
109
+ const require2 = createRequire(import.meta.url);
110
+ return join(dirname(require2.resolve("wrangler/package.json")), "bin", "wrangler.js");
111
+ }
112
+ function createWranglerRunner(cwd) {
113
+ return async (arguments_, options = {}) => {
114
+ const result = await new Promise((resolve4, reject) => {
115
+ const child = spawn(process.execPath, [wranglerEntry(), ...arguments_], {
116
+ cwd,
117
+ env: process.env,
118
+ shell: false,
119
+ stdio: ["inherit", "pipe", "pipe"]
120
+ });
121
+ let stdout = "";
122
+ let stderr = "";
123
+ child.stdout.setEncoding("utf8");
124
+ child.stderr.setEncoding("utf8");
125
+ child.stdout.on("data", (chunk) => {
126
+ stdout += chunk;
127
+ if (!options.silent) process.stdout.write(chunk);
128
+ });
129
+ child.stderr.on("data", (chunk) => {
130
+ stderr += chunk;
131
+ if (!options.silent) process.stderr.write(chunk);
132
+ });
133
+ child.once("error", reject);
134
+ child.once("close", (code) => resolve4({ exitCode: code ?? 1, stdout, stderr }));
135
+ });
136
+ if (result.exitCode !== 0 && !options.allowFailure) {
137
+ const operation = arguments_.slice(0, 3).join(" ");
138
+ throw new Error(
139
+ `Wrangler command ${JSON.stringify(operation)} exited with status ${String(result.exitCode)}`
140
+ );
141
+ }
142
+ return result;
143
+ };
144
+ }
145
+
146
+ // src/cli/deploy.ts
147
+ var stateDirectoryName = ".pkgflare";
148
+ var stateFileName = "wrangler.json";
149
+ var ownershipFileName = "ownership.json";
150
+ var d1Binding = "PKGFLARE_DB";
151
+ var r2Binding = "PKGFLARE_BUCKET";
152
+ function packageRoot() {
153
+ return dirname2(fileURLToPath(import.meta.url));
154
+ }
155
+ function stateFor(config, accountId, previous) {
156
+ if (previous?.account_id !== void 0 && previous.account_id !== accountId) {
157
+ throw new Error("authenticated Cloudflare account does not match the deployment state");
158
+ }
159
+ const state = {
160
+ $schema: "https://raw.githubusercontent.com/cloudflare/workers-sdk/main/packages/wrangler/config-schema.json",
161
+ account_id: accountId,
162
+ name: config.name,
163
+ main: "runtime/worker.js",
164
+ compatibility_date: "2026-08-01",
165
+ workers_dev: config.hostname === void 0,
166
+ vars: { PKGFLARE_CONFIG: JSON.stringify(config) },
167
+ d1_databases: previous?.d1_databases ?? [],
168
+ r2_buckets: previous?.r2_buckets ?? []
169
+ };
170
+ if (config.hostname !== void 0) {
171
+ state.routes = [{ pattern: config.hostname, custom_domain: true }];
172
+ }
173
+ return state;
174
+ }
175
+ async function readState(path, config) {
176
+ let contents;
177
+ try {
178
+ contents = await readFile(path, "utf8");
179
+ } catch (error) {
180
+ if (error.code === "ENOENT") return void 0;
181
+ throw error;
182
+ }
183
+ const state = JSON.parse(contents);
184
+ if (state.name !== config.name) {
185
+ throw new Error(
186
+ `existing deployment state belongs to ${state.name}; remove ${stateDirectoryName} only after verifying the remote resources`
187
+ );
188
+ }
189
+ return state;
190
+ }
191
+ async function writeState(path, state) {
192
+ await writeFile(path, `${JSON.stringify(state, null, 2)}
193
+ `, "utf8");
194
+ }
195
+ async function readWorkerOwnership(path, workerName, accountId) {
196
+ let contents;
197
+ try {
198
+ contents = await readFile(path, "utf8");
199
+ } catch (error) {
200
+ if (error.code === "ENOENT") return false;
201
+ throw error;
202
+ }
203
+ const ownership = JSON.parse(contents);
204
+ if (ownership.worker_name !== workerName || ownership.account_id !== accountId) {
205
+ throw new Error("deployment ownership state does not match the configured Worker and account");
206
+ }
207
+ return true;
208
+ }
209
+ async function writeWorkerOwnership(path, workerName, accountId) {
210
+ const ownership = { account_id: accountId, worker_name: workerName };
211
+ await writeFile(path, `${JSON.stringify(ownership, null, 2)}
212
+ `, "utf8");
213
+ }
214
+ async function copyRuntimeFiles(stateDirectory, root) {
215
+ const runtimeDirectory = join2(stateDirectory, "runtime");
216
+ const migrationsDirectory = join2(stateDirectory, "migrations");
217
+ await Promise.all([
218
+ mkdir(runtimeDirectory, { recursive: true }),
219
+ mkdir(migrationsDirectory, { recursive: true })
220
+ ]);
221
+ const sourceMigrations = resolve2(root, "..", "migrations");
222
+ const migrationFiles = (await readdir(sourceMigrations, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name);
223
+ migrationFiles.sort();
224
+ if (migrationFiles.length === 0) throw new Error("pkgflare package contains no D1 migrations");
225
+ await Promise.all([
226
+ copyFile(join2(root, "worker.js"), join2(runtimeDirectory, "worker.js")),
227
+ copyFile(join2(root, "worker.js.map"), join2(runtimeDirectory, "worker.js.map")),
228
+ ...migrationFiles.map(
229
+ (file) => copyFile(join2(sourceMigrations, file), join2(migrationsDirectory, file))
230
+ )
231
+ ]);
232
+ await writeFile(join2(stateDirectory, ".gitignore"), "runtime/\nmigrations/\n", {
233
+ flag: "wx"
234
+ }).catch((error) => {
235
+ if (error.code !== "EEXIST") throw error;
236
+ });
237
+ }
238
+ function configArguments(stateFile, cwd) {
239
+ return ["--config", relative(cwd, stateFile)];
240
+ }
241
+ async function recoverD1(runner, databaseName, configArgs) {
242
+ const result = await runner(["d1", "list", "--json", ...configArgs], { silent: true });
243
+ const entries = JSON.parse(result.stdout);
244
+ const matches = entries.filter(
245
+ (entry) => entry.name === databaseName && typeof entry.uuid === "string"
246
+ );
247
+ if (matches.length > 1) throw new Error(`multiple D1 databases are named ${databaseName}`);
248
+ const match = matches[0];
249
+ return match === void 0 ? null : {
250
+ binding: d1Binding,
251
+ database_name: databaseName,
252
+ database_id: match.uuid,
253
+ migrations_dir: "migrations"
254
+ };
255
+ }
256
+ async function recoverR2(runner, bucketName, configArgs) {
257
+ const result = await runner(["r2", "bucket", "info", bucketName, "--json", ...configArgs], {
258
+ silent: true,
259
+ allowFailure: true
260
+ });
261
+ if (result.exitCode === 0) return { binding: r2Binding, bucket_name: bucketName };
262
+ if (/not found|does not exist|10006/i.test(result.stderr)) return null;
263
+ throw new Error(`unable to inspect the ${bucketName} R2 bucket with the current credentials`);
264
+ }
265
+ function accountsFromWhoami(value) {
266
+ if (typeof value !== "object" || value === null)
267
+ throw new Error("Wrangler returned invalid account information");
268
+ const accounts = value.accounts;
269
+ if (!Array.isArray(accounts)) throw new Error("Wrangler returned invalid account information");
270
+ return accounts.flatMap((account) => {
271
+ if (typeof account !== "object" || account === null || !("id" in account) || typeof account.id !== "string") {
272
+ return [];
273
+ }
274
+ return [
275
+ {
276
+ id: account.id,
277
+ ...typeof account.name === "string" ? { name: account.name } : {}
278
+ }
279
+ ];
280
+ });
281
+ }
282
+ async function resolveAccountId(runner, configuredAccountId) {
283
+ if (configuredAccountId !== void 0) return configuredAccountId;
284
+ const result = await runner(["whoami", "--json"], { silent: true });
285
+ const accounts = accountsFromWhoami(JSON.parse(result.stdout));
286
+ if (accounts.length !== 1) {
287
+ throw new Error("accountId is required when Wrangler credentials can access multiple accounts");
288
+ }
289
+ const account = accounts[0];
290
+ if (account === void 0)
291
+ throw new Error("Wrangler credentials do not provide a Cloudflare account");
292
+ return account.id;
293
+ }
294
+ async function workerExists(runner, workerName, configArgs) {
295
+ const result = await runner(["versions", "list", "--name", workerName, "--json", ...configArgs], {
296
+ silent: true,
297
+ allowFailure: true
298
+ });
299
+ if (result.exitCode === 0) return true;
300
+ if (/not found|does not exist|10007/i.test(result.stderr)) return false;
301
+ throw new Error(`unable to inspect the ${workerName} Worker with the current credentials`);
302
+ }
303
+ async function verifyWorkerOwnership(workerName, configArgs, runner, adoptExisting) {
304
+ const exists = await workerExists(runner, workerName, configArgs);
305
+ if (exists && !adoptExisting) {
306
+ throw new Error(
307
+ `the ${workerName} Worker already exists; verify it, then rerun with --adopt-existing`
308
+ );
309
+ }
310
+ return exists;
311
+ }
312
+ async function ensureResources(state, stateFile, cwd, runner, adoptExisting) {
313
+ const d1Name = `${state.name}-metadata`;
314
+ const r2Name = `${state.name}-packages`;
315
+ const configArgs = configArguments(stateFile, cwd);
316
+ const configuredD1 = state.d1_databases.filter((database) => database.binding === d1Binding);
317
+ const configuredR2 = state.r2_buckets.filter((bucket) => bucket.binding === r2Binding);
318
+ const selectedD1 = configuredD1[0];
319
+ const selectedR2 = configuredR2[0];
320
+ if (configuredD1.length > 1 || selectedD1 !== void 0 && selectedD1.database_name !== d1Name) {
321
+ throw new Error(`${d1Binding} must reference the ${d1Name} D1 database`);
322
+ }
323
+ if (configuredR2.length > 1 || selectedR2 !== void 0 && selectedR2.bucket_name !== r2Name) {
324
+ throw new Error(`${r2Binding} must reference the ${r2Name} R2 bucket`);
325
+ }
326
+ if (configuredD1.length === 0) {
327
+ const recovered = await recoverD1(runner, d1Name, configArgs);
328
+ if (recovered !== null) {
329
+ if (!adoptExisting) {
330
+ throw new Error(
331
+ `the ${d1Name} D1 database already exists; verify it, then rerun with --adopt-existing`
332
+ );
333
+ }
334
+ state.d1_databases.push(recovered);
335
+ await writeState(stateFile, state);
336
+ } else {
337
+ await runner([
338
+ "d1",
339
+ "create",
340
+ d1Name,
341
+ "--binding",
342
+ d1Binding,
343
+ "--update-config",
344
+ ...configArgs
345
+ ]);
346
+ state = JSON.parse(await readFile(stateFile, "utf8"));
347
+ }
348
+ }
349
+ if (configuredR2.length === 0) {
350
+ const recovered = await recoverR2(runner, r2Name, configArgs);
351
+ if (recovered !== null) {
352
+ if (!adoptExisting) {
353
+ throw new Error(
354
+ `the ${r2Name} R2 bucket already exists; verify it, then rerun with --adopt-existing`
355
+ );
356
+ }
357
+ state.r2_buckets.push(recovered);
358
+ await writeState(stateFile, state);
359
+ } else {
360
+ await runner([
361
+ "r2",
362
+ "bucket",
363
+ "create",
364
+ r2Name,
365
+ "--binding",
366
+ r2Binding,
367
+ "--update-config",
368
+ ...configArgs
369
+ ]);
370
+ state = JSON.parse(await readFile(stateFile, "utf8"));
371
+ }
372
+ }
373
+ state.d1_databases = state.d1_databases.map(
374
+ (database) => database.binding === d1Binding ? { ...database, migrations_dir: "migrations" } : database
375
+ );
376
+ await writeState(stateFile, state);
377
+ return state;
378
+ }
379
+ function deploymentUrl(output, config) {
380
+ if (config.hostname !== void 0) return `https://${config.hostname}`;
381
+ return output.match(/https:\/\/[^\s]+\.workers\.dev\b/)?.[0] ?? null;
382
+ }
383
+ async function deploy(options) {
384
+ const cwd = resolve2(options.cwd);
385
+ const config = await loadConfig(cwd, options.configFile);
386
+ const stateDirectory = join2(cwd, stateDirectoryName);
387
+ const stateFile = join2(stateDirectory, stateFileName);
388
+ const ownershipFile = join2(stateDirectory, ownershipFileName);
389
+ await mkdir(stateDirectory, { recursive: true });
390
+ const previous = await readState(stateFile, config);
391
+ const runner = options.runner ?? createWranglerRunner(cwd);
392
+ const environmentAccountId = process.env.CLOUDFLARE_ACCOUNT_ID;
393
+ const configuredAccountId = config.accountId ?? previous?.account_id;
394
+ if (environmentAccountId !== void 0 && configuredAccountId !== void 0 && environmentAccountId !== configuredAccountId) {
395
+ throw new Error("CLOUDFLARE_ACCOUNT_ID does not match the configured deployment account");
396
+ }
397
+ const accountId = await resolveAccountId(runner, configuredAccountId ?? environmentAccountId);
398
+ const ownsWorker = await readWorkerOwnership(ownershipFile, config.name, accountId);
399
+ let state = stateFor(config, accountId, previous);
400
+ await writeState(stateFile, state);
401
+ await copyRuntimeFiles(stateDirectory, options.assetsDirectory ?? packageRoot());
402
+ state = await ensureResources(state, stateFile, cwd, runner, options.adoptExisting ?? false);
403
+ const configArgs = configArguments(stateFile, cwd);
404
+ await runner(["d1", "migrations", "apply", d1Binding, "--remote", ...configArgs]);
405
+ const adoptedWorker = ownsWorker ? false : await verifyWorkerOwnership(state.name, configArgs, runner, options.adoptExisting ?? false);
406
+ if (adoptedWorker) await writeWorkerOwnership(ownershipFile, state.name, accountId);
407
+ const deployArguments = ["deploy", "--no-bundle", ...configArgs];
408
+ if (options.secretsFile !== void 0) {
409
+ deployArguments.push("--secrets-file", options.secretsFile);
410
+ }
411
+ const deployed = await runner(deployArguments);
412
+ if (!ownsWorker && !adoptedWorker)
413
+ await writeWorkerOwnership(ownershipFile, state.name, accountId);
414
+ return {
415
+ registryUrl: deploymentUrl(deployed.stdout, config),
416
+ stateFile,
417
+ secretBindings: config.auth.tokens.map((token) => token.binding),
418
+ scopes: config.scopes
419
+ };
420
+ }
421
+
422
+ // src/cli.ts
423
+ function valueAfter(arguments_, flag) {
424
+ const index = arguments_.indexOf(flag);
425
+ if (index === -1) return void 0;
426
+ const value = arguments_[index + 1];
427
+ if (value === void 0 || value.startsWith("--")) throw new Error(`${flag} requires a value`);
428
+ return value;
429
+ }
430
+ function help() {
431
+ process.stdout.write(
432
+ `pkgflare
433
+
434
+ Usage:
435
+ pkgflare deploy [--config <path>] [--secrets-file <path>] [--adopt-existing]
436
+ pkgflare init
437
+ pkgflare token generate
438
+ `
439
+ );
440
+ }
441
+ async function init(cwd) {
442
+ const path = resolve3(cwd, "pkgflare.config.ts");
443
+ const file = await open(path, "wx").catch((error) => {
444
+ if (error.code === "EEXIST")
445
+ throw new Error("pkgflare.config.ts already exists");
446
+ throw error;
447
+ });
448
+ await file.writeFile(
449
+ `import { defineConfig } from "@ponharu/pkgflare";
450
+
451
+ export default defineConfig({
452
+ name: "acme-registry",
453
+ scopes: ["@acme"],
454
+ auth: {
455
+ provider: "secrets",
456
+ tokens: [
457
+ { binding: "PKGFLARE_READ_TOKEN", permissions: ["read"] },
458
+ { binding: "PKGFLARE_PUBLISH_TOKEN", permissions: ["publish"] },
459
+ ],
460
+ },
461
+ });
462
+ `
463
+ );
464
+ await file.close();
465
+ process.stdout.write("Created pkgflare.config.ts\n");
466
+ }
467
+ async function main(arguments_) {
468
+ const [command, subcommand] = arguments_;
469
+ if (command === void 0 || command === "--help" || command === "-h") {
470
+ help();
471
+ return;
472
+ }
473
+ if (command === "deploy") {
474
+ const configFile = valueAfter(arguments_, "--config");
475
+ const secretsFile = valueAfter(arguments_, "--secrets-file");
476
+ const adoptExisting = arguments_.includes("--adopt-existing");
477
+ const result = await deploy({
478
+ cwd: process.cwd(),
479
+ ...configFile === void 0 ? {} : { configFile },
480
+ ...secretsFile === void 0 ? {} : { secretsFile },
481
+ adoptExisting
482
+ });
483
+ const statePath = relative2(process.cwd(), result.stateFile);
484
+ process.stdout.write(`
485
+ Deployment state: ${statePath}
486
+ `);
487
+ if (result.registryUrl !== null) {
488
+ process.stdout.write(`Registry: ${result.registryUrl}
489
+ `);
490
+ }
491
+ process.stdout.write("\nRegister each token secret if it was not supplied during deploy:\n");
492
+ for (const binding of result.secretBindings) {
493
+ process.stdout.write(` npx wrangler secret put ${binding} --config ${statePath}
494
+ `);
495
+ }
496
+ if (result.registryUrl !== null) {
497
+ const host = new URL(result.registryUrl).host;
498
+ process.stdout.write("\n.npmrc:\n");
499
+ for (const scope of result.scopes) {
500
+ process.stdout.write(` ${scope}:registry=${result.registryUrl}
501
+ `);
502
+ }
503
+ process.stdout.write(` //${host}/:_authToken=\${NPM_TOKEN}
504
+ `);
505
+ }
506
+ return;
507
+ }
508
+ if (command === "init") {
509
+ await init(process.cwd());
510
+ return;
511
+ }
512
+ if (command === "token" && subcommand === "generate") {
513
+ let binary = "";
514
+ for (const byte of randomBytes(32)) binary += String.fromCharCode(byte);
515
+ const token = btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
516
+ process.stdout.write(`${token}
517
+ `);
518
+ return;
519
+ }
520
+ throw new Error(`unknown command: ${arguments_.join(" ")}`);
521
+ }
522
+ main(process.argv.slice(2)).catch((error) => {
523
+ const message = error instanceof Error ? error.message : String(error);
524
+ process.stderr.write(`pkgflare: ${message}
525
+ `);
526
+ process.exitCode = 1;
527
+ });
528
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/cli/deploy.ts","../src/cli/config-loader.ts","../src/config.ts","../src/cli/runner.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { open } from \"node:fs/promises\";\nimport { relative, resolve } from \"node:path\";\nimport { deploy } from \"./cli/deploy.js\";\n\nfunction valueAfter(arguments_: readonly string[], flag: string): string | undefined {\n const index = arguments_.indexOf(flag);\n if (index === -1) return undefined;\n const value = arguments_[index + 1];\n if (value === undefined || value.startsWith(\"--\")) throw new Error(`${flag} requires a value`);\n return value;\n}\n\nfunction help(): void {\n process.stdout.write(\n `pkgflare\\n\\nUsage:\\n pkgflare deploy [--config <path>] [--secrets-file <path>] [--adopt-existing]\\n pkgflare init\\n pkgflare token generate\\n`,\n );\n}\n\nasync function init(cwd: string): Promise<void> {\n const path = resolve(cwd, \"pkgflare.config.ts\");\n const file = await open(path, \"wx\").catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\")\n throw new Error(\"pkgflare.config.ts already exists\");\n throw error;\n });\n await file.writeFile(\n `import { defineConfig } from \"@ponharu/pkgflare\";\\n\\nexport default defineConfig({\\n name: \"acme-registry\",\\n scopes: [\"@acme\"],\\n auth: {\\n provider: \"secrets\",\\n tokens: [\\n { binding: \"PKGFLARE_READ_TOKEN\", permissions: [\"read\"] },\\n { binding: \"PKGFLARE_PUBLISH_TOKEN\", permissions: [\"publish\"] },\\n ],\\n },\\n});\\n`,\n );\n await file.close();\n process.stdout.write(\"Created pkgflare.config.ts\\n\");\n}\n\nasync function main(arguments_: string[]): Promise<void> {\n const [command, subcommand] = arguments_;\n if (command === undefined || command === \"--help\" || command === \"-h\") {\n help();\n return;\n }\n if (command === \"deploy\") {\n const configFile = valueAfter(arguments_, \"--config\");\n const secretsFile = valueAfter(arguments_, \"--secrets-file\");\n const adoptExisting = arguments_.includes(\"--adopt-existing\");\n const result = await deploy({\n cwd: process.cwd(),\n ...(configFile === undefined ? {} : { configFile }),\n ...(secretsFile === undefined ? {} : { secretsFile }),\n adoptExisting,\n });\n const statePath = relative(process.cwd(), result.stateFile);\n process.stdout.write(`\\nDeployment state: ${statePath}\\n`);\n if (result.registryUrl !== null) {\n process.stdout.write(`Registry: ${result.registryUrl}\\n`);\n }\n process.stdout.write(\"\\nRegister each token secret if it was not supplied during deploy:\\n\");\n for (const binding of result.secretBindings) {\n process.stdout.write(` npx wrangler secret put ${binding} --config ${statePath}\\n`);\n }\n if (result.registryUrl !== null) {\n const host = new URL(result.registryUrl).host;\n process.stdout.write(\"\\n.npmrc:\\n\");\n for (const scope of result.scopes) {\n process.stdout.write(` ${scope}:registry=${result.registryUrl}\\n`);\n }\n process.stdout.write(` //${host}/:_authToken=\\${NPM_TOKEN}\\n`);\n }\n return;\n }\n if (command === \"init\") {\n await init(process.cwd());\n return;\n }\n if (command === \"token\" && subcommand === \"generate\") {\n let binary = \"\";\n for (const byte of randomBytes(32)) binary += String.fromCharCode(byte);\n const token = btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n process.stdout.write(`${token}\\n`);\n return;\n }\n throw new Error(`unknown command: ${arguments_.join(\" \")}`);\n}\n\nmain(process.argv.slice(2)).catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`pkgflare: ${message}\\n`);\n process.exitCode = 1;\n});\n","import { copyFile, mkdir, readFile, readdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { NormalizedConfig } from \"../config.js\";\nimport { loadConfig } from \"./config-loader.js\";\nimport { createWranglerRunner, type WranglerRunner } from \"./runner.js\";\n\nconst stateDirectoryName = \".pkgflare\";\nconst stateFileName = \"wrangler.json\";\nconst ownershipFileName = \"ownership.json\";\nconst d1Binding = \"PKGFLARE_DB\";\nconst r2Binding = \"PKGFLARE_BUCKET\";\n\ninterface D1Binding {\n binding: string;\n database_name: string;\n database_id: string;\n migrations_dir?: string;\n}\n\ninterface R2Binding {\n binding: string;\n bucket_name: string;\n}\n\ninterface WranglerState {\n $schema: string;\n account_id?: string;\n name: string;\n main: string;\n compatibility_date: string;\n workers_dev: boolean;\n routes?: Array<{ pattern: string; custom_domain: true }>;\n vars: { PKGFLARE_CONFIG: string };\n d1_databases: D1Binding[];\n r2_buckets: R2Binding[];\n}\n\ninterface D1ListEntry {\n name?: unknown;\n uuid?: unknown;\n}\n\ninterface WhoamiResult {\n loggedIn?: unknown;\n accounts?: unknown;\n}\n\ninterface AccountEntry {\n id: string;\n name?: string;\n}\n\ninterface OwnershipState {\n account_id: string;\n worker_name: string;\n}\n\nexport interface DeployOptions {\n cwd: string;\n configFile?: string;\n secretsFile?: string;\n adoptExisting?: boolean;\n runner?: WranglerRunner;\n assetsDirectory?: string;\n}\n\nexport interface DeployResult {\n registryUrl: string | null;\n stateFile: string;\n secretBindings: string[];\n scopes: string[];\n}\n\nfunction packageRoot(): string {\n return dirname(fileURLToPath(import.meta.url));\n}\n\nfunction stateFor(\n config: NormalizedConfig,\n accountId: string,\n previous?: WranglerState,\n): WranglerState {\n if (previous?.account_id !== undefined && previous.account_id !== accountId) {\n throw new Error(\"authenticated Cloudflare account does not match the deployment state\");\n }\n const state: WranglerState = {\n $schema:\n \"https://raw.githubusercontent.com/cloudflare/workers-sdk/main/packages/wrangler/config-schema.json\",\n account_id: accountId,\n name: config.name,\n main: \"runtime/worker.js\",\n compatibility_date: \"2026-08-01\",\n workers_dev: config.hostname === undefined,\n vars: { PKGFLARE_CONFIG: JSON.stringify(config) },\n d1_databases: previous?.d1_databases ?? [],\n r2_buckets: previous?.r2_buckets ?? [],\n };\n if (config.hostname !== undefined) {\n state.routes = [{ pattern: config.hostname, custom_domain: true }];\n }\n return state;\n}\n\nasync function readState(\n path: string,\n config: NormalizedConfig,\n): Promise<WranglerState | undefined> {\n let contents: string;\n try {\n contents = await readFile(path, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw error;\n }\n const state = JSON.parse(contents) as WranglerState;\n if (state.name !== config.name) {\n throw new Error(\n `existing deployment state belongs to ${state.name}; remove ${stateDirectoryName} only after verifying the remote resources`,\n );\n }\n return state;\n}\n\nasync function writeState(path: string, state: WranglerState): Promise<void> {\n await writeFile(path, `${JSON.stringify(state, null, 2)}\\n`, \"utf8\");\n}\n\nasync function readWorkerOwnership(\n path: string,\n workerName: string,\n accountId: string,\n): Promise<boolean> {\n let contents: string;\n try {\n contents = await readFile(path, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n const ownership = JSON.parse(contents) as Partial<OwnershipState>;\n if (ownership.worker_name !== workerName || ownership.account_id !== accountId) {\n throw new Error(\"deployment ownership state does not match the configured Worker and account\");\n }\n return true;\n}\n\nasync function writeWorkerOwnership(\n path: string,\n workerName: string,\n accountId: string,\n): Promise<void> {\n const ownership: OwnershipState = { account_id: accountId, worker_name: workerName };\n await writeFile(path, `${JSON.stringify(ownership, null, 2)}\\n`, \"utf8\");\n}\n\nasync function copyRuntimeFiles(stateDirectory: string, root: string): Promise<void> {\n const runtimeDirectory = join(stateDirectory, \"runtime\");\n const migrationsDirectory = join(stateDirectory, \"migrations\");\n await Promise.all([\n mkdir(runtimeDirectory, { recursive: true }),\n mkdir(migrationsDirectory, { recursive: true }),\n ]);\n const sourceMigrations = resolve(root, \"..\", \"migrations\");\n const migrationFiles = (await readdir(sourceMigrations, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".sql\"))\n .map((entry) => entry.name);\n // oxlint-disable-next-line unicorn/no-array-sort -- this array has just been created locally\n migrationFiles.sort();\n if (migrationFiles.length === 0) throw new Error(\"pkgflare package contains no D1 migrations\");\n await Promise.all([\n copyFile(join(root, \"worker.js\"), join(runtimeDirectory, \"worker.js\")),\n copyFile(join(root, \"worker.js.map\"), join(runtimeDirectory, \"worker.js.map\")),\n ...migrationFiles.map((file) =>\n copyFile(join(sourceMigrations, file), join(migrationsDirectory, file)),\n ),\n ]);\n await writeFile(join(stateDirectory, \".gitignore\"), \"runtime/\\nmigrations/\\n\", {\n flag: \"wx\",\n }).catch((error: unknown) => {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n });\n}\n\nfunction configArguments(stateFile: string, cwd: string): string[] {\n return [\"--config\", relative(cwd, stateFile)];\n}\n\nasync function recoverD1(\n runner: WranglerRunner,\n databaseName: string,\n configArgs: readonly string[],\n): Promise<D1Binding | null> {\n const result = await runner([\"d1\", \"list\", \"--json\", ...configArgs], { silent: true });\n const entries = JSON.parse(result.stdout) as D1ListEntry[];\n const matches = entries.filter(\n (entry) => entry.name === databaseName && typeof entry.uuid === \"string\",\n );\n if (matches.length > 1) throw new Error(`multiple D1 databases are named ${databaseName}`);\n const match = matches[0];\n return match === undefined\n ? null\n : {\n binding: d1Binding,\n database_name: databaseName,\n database_id: match.uuid as string,\n migrations_dir: \"migrations\",\n };\n}\n\nasync function recoverR2(\n runner: WranglerRunner,\n bucketName: string,\n configArgs: readonly string[],\n): Promise<R2Binding | null> {\n const result = await runner([\"r2\", \"bucket\", \"info\", bucketName, \"--json\", ...configArgs], {\n silent: true,\n allowFailure: true,\n });\n if (result.exitCode === 0) return { binding: r2Binding, bucket_name: bucketName };\n if (/not found|does not exist|10006/i.test(result.stderr)) return null;\n throw new Error(`unable to inspect the ${bucketName} R2 bucket with the current credentials`);\n}\n\nfunction accountsFromWhoami(value: unknown): AccountEntry[] {\n if (typeof value !== \"object\" || value === null)\n throw new Error(\"Wrangler returned invalid account information\");\n const accounts = (value as WhoamiResult).accounts;\n if (!Array.isArray(accounts)) throw new Error(\"Wrangler returned invalid account information\");\n return accounts.flatMap((account) => {\n if (\n typeof account !== \"object\" ||\n account === null ||\n !(\"id\" in account) ||\n typeof account.id !== \"string\"\n ) {\n return [];\n }\n return [\n {\n id: account.id,\n ...(typeof (account as { name?: unknown }).name === \"string\"\n ? { name: (account as { name: string }).name }\n : {}),\n },\n ];\n });\n}\n\nasync function resolveAccountId(\n runner: WranglerRunner,\n configuredAccountId: string | undefined,\n): Promise<string> {\n if (configuredAccountId !== undefined) return configuredAccountId;\n const result = await runner([\"whoami\", \"--json\"], { silent: true });\n const accounts = accountsFromWhoami(JSON.parse(result.stdout) as unknown);\n if (accounts.length !== 1) {\n throw new Error(\"accountId is required when Wrangler credentials can access multiple accounts\");\n }\n const account = accounts[0];\n if (account === undefined)\n throw new Error(\"Wrangler credentials do not provide a Cloudflare account\");\n return account.id;\n}\n\nasync function workerExists(\n runner: WranglerRunner,\n workerName: string,\n configArgs: readonly string[],\n): Promise<boolean> {\n const result = await runner([\"versions\", \"list\", \"--name\", workerName, \"--json\", ...configArgs], {\n silent: true,\n allowFailure: true,\n });\n if (result.exitCode === 0) return true;\n if (/not found|does not exist|10007/i.test(result.stderr)) return false;\n throw new Error(`unable to inspect the ${workerName} Worker with the current credentials`);\n}\n\nasync function verifyWorkerOwnership(\n workerName: string,\n configArgs: readonly string[],\n runner: WranglerRunner,\n adoptExisting: boolean,\n): Promise<boolean> {\n const exists = await workerExists(runner, workerName, configArgs);\n if (exists && !adoptExisting) {\n throw new Error(\n `the ${workerName} Worker already exists; verify it, then rerun with --adopt-existing`,\n );\n }\n return exists;\n}\n\nasync function ensureResources(\n state: WranglerState,\n stateFile: string,\n cwd: string,\n runner: WranglerRunner,\n adoptExisting: boolean,\n): Promise<WranglerState> {\n const d1Name = `${state.name}-metadata`;\n const r2Name = `${state.name}-packages`;\n const configArgs = configArguments(stateFile, cwd);\n const configuredD1 = state.d1_databases.filter((database) => database.binding === d1Binding);\n const configuredR2 = state.r2_buckets.filter((bucket) => bucket.binding === r2Binding);\n const selectedD1 = configuredD1[0];\n const selectedR2 = configuredR2[0];\n if (\n configuredD1.length > 1 ||\n (selectedD1 !== undefined && selectedD1.database_name !== d1Name)\n ) {\n throw new Error(`${d1Binding} must reference the ${d1Name} D1 database`);\n }\n if (configuredR2.length > 1 || (selectedR2 !== undefined && selectedR2.bucket_name !== r2Name)) {\n throw new Error(`${r2Binding} must reference the ${r2Name} R2 bucket`);\n }\n\n if (configuredD1.length === 0) {\n const recovered = await recoverD1(runner, d1Name, configArgs);\n if (recovered !== null) {\n if (!adoptExisting) {\n throw new Error(\n `the ${d1Name} D1 database already exists; verify it, then rerun with --adopt-existing`,\n );\n }\n state.d1_databases.push(recovered);\n await writeState(stateFile, state);\n } else {\n await runner([\n \"d1\",\n \"create\",\n d1Name,\n \"--binding\",\n d1Binding,\n \"--update-config\",\n ...configArgs,\n ]);\n state = JSON.parse(await readFile(stateFile, \"utf8\")) as WranglerState;\n }\n }\n\n if (configuredR2.length === 0) {\n const recovered = await recoverR2(runner, r2Name, configArgs);\n if (recovered !== null) {\n if (!adoptExisting) {\n throw new Error(\n `the ${r2Name} R2 bucket already exists; verify it, then rerun with --adopt-existing`,\n );\n }\n state.r2_buckets.push(recovered);\n await writeState(stateFile, state);\n } else {\n await runner([\n \"r2\",\n \"bucket\",\n \"create\",\n r2Name,\n \"--binding\",\n r2Binding,\n \"--update-config\",\n ...configArgs,\n ]);\n state = JSON.parse(await readFile(stateFile, \"utf8\")) as WranglerState;\n }\n }\n\n state.d1_databases = state.d1_databases.map((database) =>\n database.binding === d1Binding ? { ...database, migrations_dir: \"migrations\" } : database,\n );\n await writeState(stateFile, state);\n return state;\n}\n\nfunction deploymentUrl(output: string, config: NormalizedConfig): string | null {\n if (config.hostname !== undefined) return `https://${config.hostname}`;\n return output.match(/https:\\/\\/[^\\s]+\\.workers\\.dev\\b/)?.[0] ?? null;\n}\n\nexport async function deploy(options: DeployOptions): Promise<DeployResult> {\n const cwd = resolve(options.cwd);\n const config = await loadConfig(cwd, options.configFile);\n const stateDirectory = join(cwd, stateDirectoryName);\n const stateFile = join(stateDirectory, stateFileName);\n const ownershipFile = join(stateDirectory, ownershipFileName);\n await mkdir(stateDirectory, { recursive: true });\n\n const previous = await readState(stateFile, config);\n const runner = options.runner ?? createWranglerRunner(cwd);\n const environmentAccountId = process.env.CLOUDFLARE_ACCOUNT_ID;\n const configuredAccountId = config.accountId ?? previous?.account_id;\n if (\n environmentAccountId !== undefined &&\n configuredAccountId !== undefined &&\n environmentAccountId !== configuredAccountId\n ) {\n throw new Error(\"CLOUDFLARE_ACCOUNT_ID does not match the configured deployment account\");\n }\n const accountId = await resolveAccountId(runner, configuredAccountId ?? environmentAccountId);\n const ownsWorker = await readWorkerOwnership(ownershipFile, config.name, accountId);\n let state = stateFor(config, accountId, previous);\n await writeState(stateFile, state);\n await copyRuntimeFiles(stateDirectory, options.assetsDirectory ?? packageRoot());\n\n state = await ensureResources(state, stateFile, cwd, runner, options.adoptExisting ?? false);\n const configArgs = configArguments(stateFile, cwd);\n await runner([\"d1\", \"migrations\", \"apply\", d1Binding, \"--remote\", ...configArgs]);\n\n const adoptedWorker = ownsWorker\n ? false\n : await verifyWorkerOwnership(state.name, configArgs, runner, options.adoptExisting ?? false);\n if (adoptedWorker) await writeWorkerOwnership(ownershipFile, state.name, accountId);\n\n const deployArguments = [\"deploy\", \"--no-bundle\", ...configArgs];\n if (options.secretsFile !== undefined) {\n deployArguments.push(\"--secrets-file\", options.secretsFile);\n }\n const deployed = await runner(deployArguments);\n if (!ownsWorker && !adoptedWorker)\n await writeWorkerOwnership(ownershipFile, state.name, accountId);\n return {\n registryUrl: deploymentUrl(deployed.stdout, config),\n stateFile,\n secretBindings: config.auth.tokens.map((token) => token.binding),\n scopes: config.scopes,\n };\n}\n","import { access } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { createJiti } from \"jiti\";\nimport { normalizeConfig, type NormalizedConfig } from \"../config.js\";\n\nexport const defaultConfigFile = \"pkgflare.config.ts\";\n\nexport async function loadConfig(\n cwd: string,\n configFile = defaultConfigFile,\n): Promise<NormalizedConfig> {\n const path = resolve(cwd, configFile);\n try {\n await access(path);\n } catch {\n throw new Error(`configuration file not found: ${configFile}`);\n }\n\n const jiti = createJiti(pathToFileURL(import.meta.url).href, {\n interopDefault: true,\n moduleCache: false,\n });\n const imported = await jiti.import(path, { default: true });\n return normalizeConfig(imported);\n}\n","export type Permission = \"read\" | \"publish\";\n\nexport interface SecretTokenConfig {\n binding: string;\n permissions: readonly Permission[];\n}\n\nexport interface SecretsAuthConfig {\n provider: \"secrets\";\n tokens: readonly SecretTokenConfig[];\n}\n\nexport interface PkgflareConfig {\n name: string;\n scopes: readonly string[];\n accountId?: string;\n hostname?: string;\n auth: SecretsAuthConfig;\n}\n\nexport interface NormalizedConfig {\n name: string;\n scopes: string[];\n accountId?: string;\n hostname?: string;\n auth: {\n provider: \"secrets\";\n tokens: Array<{\n binding: string;\n permissions: Permission[];\n }>;\n };\n}\n\nconst workerNamePattern = /^[a-z](?:[a-z0-9-]{0,52}[a-z0-9])?$/;\nconst accountIdPattern = /^[a-f0-9]{32}$/;\nconst scopePattern = /^@[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;\nconst bindingPattern = /^[A-Z][A-Z0-9_]*$/;\nconst hostnamePattern =\n /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/;\nconst reservedBindings = new Set([\"PKGFLARE_DB\", \"PKGFLARE_BUCKET\", \"PKGFLARE_CONFIG\"]);\n\nexport function defineConfig(config: PkgflareConfig): PkgflareConfig {\n return config;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function normalizeConfig(value: unknown): NormalizedConfig {\n if (!isObject(value)) throw new Error(\"configuration must be an object\");\n const { name, scopes: scopeValues, accountId, hostname, auth } = value;\n if (typeof name !== \"string\" || !workerNamePattern.test(name)) {\n throw new Error(\"name must be a lowercase Cloudflare resource name of at most 54 characters\");\n }\n\n if (\n accountId !== undefined &&\n (typeof accountId !== \"string\" || !accountIdPattern.test(accountId))\n ) {\n throw new Error(\"accountId must be a 32-character lowercase hexadecimal Cloudflare account ID\");\n }\n\n if (\n !Array.isArray(scopeValues) ||\n scopeValues.length === 0 ||\n scopeValues.some((scope) => typeof scope !== \"string\")\n ) {\n throw new Error(\"at least one package scope is required\");\n }\n\n const scopes = [...new Set(scopeValues as string[])];\n for (const scope of scopes) {\n if (!scopePattern.test(scope)) {\n throw new Error(`invalid package scope: ${scope}`);\n }\n }\n\n if (hostname !== undefined && (typeof hostname !== \"string\" || !hostnamePattern.test(hostname))) {\n throw new Error(\"hostname must be a valid DNS hostname without a scheme or path\");\n }\n\n if (!isObject(auth) || auth.provider !== \"secrets\") {\n throw new Error(\"unsupported authentication provider\");\n }\n\n if (!Array.isArray(auth.tokens) || auth.tokens.length === 0) {\n throw new Error(\"at least one authentication token binding is required\");\n }\n\n const seenBindings = new Set<string>();\n const tokens = auth.tokens.map((tokenValue) => {\n if (!isObject(tokenValue) || typeof tokenValue.binding !== \"string\") {\n throw new Error(\"authentication token binding must be an object with a binding name\");\n }\n const { binding, permissions: permissionValues } = tokenValue;\n if (!bindingPattern.test(binding)) {\n throw new Error(`invalid secret binding: ${binding}`);\n }\n if (reservedBindings.has(binding)) {\n throw new Error(`secret binding is reserved by pkgflare: ${binding}`);\n }\n if (seenBindings.has(binding)) {\n throw new Error(`duplicate secret binding: ${binding}`);\n }\n seenBindings.add(binding);\n\n if (\n !Array.isArray(permissionValues) ||\n permissionValues.length === 0 ||\n permissionValues.some((permission) => permission !== \"read\" && permission !== \"publish\")\n ) {\n throw new Error(`invalid permissions for ${binding}`);\n }\n const permissions = [...new Set(permissionValues)] as Permission[];\n\n return { binding, permissions };\n });\n\n return {\n name,\n scopes,\n ...(accountId === undefined ? {} : { accountId }),\n ...(hostname === undefined ? {} : { hostname }),\n auth: { provider: \"secrets\", tokens },\n };\n}\n","import { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\n\nexport interface CommandResult {\n exitCode: number;\n stdout: string;\n stderr: string;\n}\n\nexport type WranglerRunner = (\n arguments_: readonly string[],\n options?: { allowFailure?: boolean; silent?: boolean },\n) => Promise<CommandResult>;\n\nfunction wranglerEntry(): string {\n const require = createRequire(import.meta.url);\n return join(dirname(require.resolve(\"wrangler/package.json\")), \"bin\", \"wrangler.js\");\n}\n\nexport function createWranglerRunner(cwd: string): WranglerRunner {\n return async (arguments_, options = {}) => {\n const result = await new Promise<CommandResult>((resolve, reject) => {\n const child = spawn(process.execPath, [wranglerEntry(), ...arguments_], {\n cwd,\n env: process.env,\n shell: false,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.setEncoding(\"utf8\");\n child.stderr.setEncoding(\"utf8\");\n child.stdout.on(\"data\", (chunk: string) => {\n stdout += chunk;\n if (!options.silent) process.stdout.write(chunk);\n });\n child.stderr.on(\"data\", (chunk: string) => {\n stderr += chunk;\n if (!options.silent) process.stderr.write(chunk);\n });\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve({ exitCode: code ?? 1, stdout, stderr }));\n });\n\n if (result.exitCode !== 0 && !options.allowFailure) {\n const operation = arguments_.slice(0, 3).join(\" \");\n throw new Error(\n `Wrangler command ${JSON.stringify(operation)} exited with status ${String(result.exitCode)}`,\n );\n }\n return result;\n };\n}\n"],"mappings":"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","names":["relative","resolve","dirname","join","resolve","require","resolve","dirname","join","resolve","resolve","relative"]}
@@ -0,0 +1,19 @@
1
+ type Permission = "read" | "publish";
2
+ interface SecretTokenConfig {
3
+ binding: string;
4
+ permissions: readonly Permission[];
5
+ }
6
+ interface SecretsAuthConfig {
7
+ provider: "secrets";
8
+ tokens: readonly SecretTokenConfig[];
9
+ }
10
+ interface PkgflareConfig {
11
+ name: string;
12
+ scopes: readonly string[];
13
+ accountId?: string;
14
+ hostname?: string;
15
+ auth: SecretsAuthConfig;
16
+ }
17
+ declare function defineConfig(config: PkgflareConfig): PkgflareConfig;
18
+
19
+ export { type Permission, type PkgflareConfig, type SecretTokenConfig, type SecretsAuthConfig, defineConfig };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // src/config.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+ export {
6
+ defineConfig
7
+ };
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["export type Permission = \"read\" | \"publish\";\n\nexport interface SecretTokenConfig {\n binding: string;\n permissions: readonly Permission[];\n}\n\nexport interface SecretsAuthConfig {\n provider: \"secrets\";\n tokens: readonly SecretTokenConfig[];\n}\n\nexport interface PkgflareConfig {\n name: string;\n scopes: readonly string[];\n accountId?: string;\n hostname?: string;\n auth: SecretsAuthConfig;\n}\n\nexport interface NormalizedConfig {\n name: string;\n scopes: string[];\n accountId?: string;\n hostname?: string;\n auth: {\n provider: \"secrets\";\n tokens: Array<{\n binding: string;\n permissions: Permission[];\n }>;\n };\n}\n\nconst workerNamePattern = /^[a-z](?:[a-z0-9-]{0,52}[a-z0-9])?$/;\nconst accountIdPattern = /^[a-f0-9]{32}$/;\nconst scopePattern = /^@[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;\nconst bindingPattern = /^[A-Z][A-Z0-9_]*$/;\nconst hostnamePattern =\n /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/;\nconst reservedBindings = new Set([\"PKGFLARE_DB\", \"PKGFLARE_BUCKET\", \"PKGFLARE_CONFIG\"]);\n\nexport function defineConfig(config: PkgflareConfig): PkgflareConfig {\n return config;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function normalizeConfig(value: unknown): NormalizedConfig {\n if (!isObject(value)) throw new Error(\"configuration must be an object\");\n const { name, scopes: scopeValues, accountId, hostname, auth } = value;\n if (typeof name !== \"string\" || !workerNamePattern.test(name)) {\n throw new Error(\"name must be a lowercase Cloudflare resource name of at most 54 characters\");\n }\n\n if (\n accountId !== undefined &&\n (typeof accountId !== \"string\" || !accountIdPattern.test(accountId))\n ) {\n throw new Error(\"accountId must be a 32-character lowercase hexadecimal Cloudflare account ID\");\n }\n\n if (\n !Array.isArray(scopeValues) ||\n scopeValues.length === 0 ||\n scopeValues.some((scope) => typeof scope !== \"string\")\n ) {\n throw new Error(\"at least one package scope is required\");\n }\n\n const scopes = [...new Set(scopeValues as string[])];\n for (const scope of scopes) {\n if (!scopePattern.test(scope)) {\n throw new Error(`invalid package scope: ${scope}`);\n }\n }\n\n if (hostname !== undefined && (typeof hostname !== \"string\" || !hostnamePattern.test(hostname))) {\n throw new Error(\"hostname must be a valid DNS hostname without a scheme or path\");\n }\n\n if (!isObject(auth) || auth.provider !== \"secrets\") {\n throw new Error(\"unsupported authentication provider\");\n }\n\n if (!Array.isArray(auth.tokens) || auth.tokens.length === 0) {\n throw new Error(\"at least one authentication token binding is required\");\n }\n\n const seenBindings = new Set<string>();\n const tokens = auth.tokens.map((tokenValue) => {\n if (!isObject(tokenValue) || typeof tokenValue.binding !== \"string\") {\n throw new Error(\"authentication token binding must be an object with a binding name\");\n }\n const { binding, permissions: permissionValues } = tokenValue;\n if (!bindingPattern.test(binding)) {\n throw new Error(`invalid secret binding: ${binding}`);\n }\n if (reservedBindings.has(binding)) {\n throw new Error(`secret binding is reserved by pkgflare: ${binding}`);\n }\n if (seenBindings.has(binding)) {\n throw new Error(`duplicate secret binding: ${binding}`);\n }\n seenBindings.add(binding);\n\n if (\n !Array.isArray(permissionValues) ||\n permissionValues.length === 0 ||\n permissionValues.some((permission) => permission !== \"read\" && permission !== \"publish\")\n ) {\n throw new Error(`invalid permissions for ${binding}`);\n }\n const permissions = [...new Set(permissionValues)] as Permission[];\n\n return { binding, permissions };\n });\n\n return {\n name,\n scopes,\n ...(accountId === undefined ? {} : { accountId }),\n ...(hostname === undefined ? {} : { hostname }),\n auth: { provider: \"secrets\", tokens },\n };\n}\n"],"mappings":";AA0CO,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;","names":[]}