@dropthis/cli 0.3.3 → 0.3.5

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.cjs CHANGED
@@ -44,6 +44,15 @@ async function resolveCredential(input) {
44
44
  ...stored.email ? { email: stored.email } : {}
45
45
  };
46
46
  }
47
+ async function requireCredential(input) {
48
+ const credential = await resolveCredential(input);
49
+ if (!credential) {
50
+ throw Object.assign(new Error("No API key found."), {
51
+ code: "auth_error"
52
+ });
53
+ }
54
+ return credential;
55
+ }
47
56
  function maskKey(apiKey) {
48
57
  return apiKey.length <= 8 ? "sk_..." : `${apiKey.slice(0, 3)}...${apiKey.slice(-4)}`;
49
58
  }
@@ -251,11 +260,13 @@ function writeEmpty(deps, message, json) {
251
260
 
252
261
  // src/commands/account.ts
253
262
  async function runAccountGet(_input, deps) {
254
- if (await resolveCredential({
255
- ...deps.apiKey ? { apiKey: deps.apiKey } : {},
256
- env: deps.env,
257
- store: deps.store
258
- }) === null) {
263
+ try {
264
+ await requireCredential({
265
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
266
+ env: deps.env,
267
+ store: deps.store
268
+ });
269
+ } catch {
259
270
  return writeAuthError(deps);
260
271
  }
261
272
  const result = await deps.client.account.get();
@@ -273,8 +284,33 @@ async function runAccountGet(_input, deps) {
273
284
 
274
285
  // src/commands/api-keys.ts
275
286
  var prompts = __toESM(require("@clack/prompts"), 1);
287
+
288
+ // src/types.ts
289
+ function unwrapListData(data, ...fallbackKeys) {
290
+ if (data && typeof data === "object") {
291
+ if ("data" in data) {
292
+ return data.data;
293
+ }
294
+ for (const key of fallbackKeys) {
295
+ if (key in data) {
296
+ return data[key];
297
+ }
298
+ }
299
+ }
300
+ return data;
301
+ }
302
+
303
+ // src/commands/api-keys.ts
276
304
  async function runApiKeysCreate(input, deps) {
277
- if (!await hasCredential(deps)) return writeAuthError(deps);
305
+ try {
306
+ await requireCredential({
307
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
308
+ env: deps.env,
309
+ store: deps.store
310
+ });
311
+ } catch {
312
+ return writeAuthError(deps);
313
+ }
278
314
  const result = await deps.client.apiKeys.create({ label: input.label });
279
315
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
280
316
  const data = result.data;
@@ -286,10 +322,18 @@ async function runApiKeysCreate(input, deps) {
286
322
  return { exitCode: 0 };
287
323
  }
288
324
  async function runApiKeysList(_input, deps) {
289
- if (!await hasCredential(deps)) return writeAuthError(deps);
325
+ try {
326
+ await requireCredential({
327
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
328
+ env: deps.env,
329
+ store: deps.store
330
+ });
331
+ } catch {
332
+ return writeAuthError(deps);
333
+ }
290
334
  const result = await deps.client.apiKeys.list();
291
335
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
292
- const raw = listItems(result.data);
336
+ const raw = unwrapListData(result.data, "apiKeys", "api_keys");
293
337
  const items = Array.isArray(raw) ? raw : void 0;
294
338
  if (deps.outputMode === "human") {
295
339
  if (!items || items.length === 0) {
@@ -323,7 +367,15 @@ async function runApiKeysDelete(keyId, input, deps) {
323
367
  return { exitCode: 0 };
324
368
  }
325
369
  }
326
- if (!await hasCredential(deps)) return writeAuthError(deps);
370
+ try {
371
+ await requireCredential({
372
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
373
+ env: deps.env,
374
+ store: deps.store
375
+ });
376
+ } catch {
377
+ return writeAuthError(deps);
378
+ }
327
379
  const result = await deps.client.apiKeys.delete(keyId);
328
380
  if (result?.error) return writeApiErrorSimple(deps, result.error.message);
329
381
  writeHumanOrJson(deps, success(`Deleted ${keyId}`), {
@@ -333,25 +385,9 @@ async function runApiKeysDelete(keyId, input, deps) {
333
385
  });
334
386
  return { exitCode: 0 };
335
387
  }
336
- async function hasCredential(deps) {
337
- return await resolveCredential({
338
- ...deps.apiKey ? { apiKey: deps.apiKey } : {},
339
- env: deps.env,
340
- store: deps.store
341
- }) !== null;
342
- }
343
- function listItems(data) {
344
- if (data && typeof data === "object" && "data" in data) {
345
- return data.data;
346
- }
347
- if (data && typeof data === "object" && "apiKeys" in data) {
348
- return data.apiKeys;
349
- }
350
- return data;
351
- }
352
388
 
353
- // src/commands/commands.ts
354
- var COMMANDS = [
389
+ // src/catalog.ts
390
+ var COMMAND_CATALOG = [
355
391
  {
356
392
  name: "publish",
357
393
  description: "Publish an input and return a Dropthis URL.",
@@ -426,70 +462,31 @@ var COMMANDS = [
426
462
  examples: ["dropthis commands --json"]
427
463
  }
428
464
  ];
465
+
466
+ // src/commands/commands.ts
429
467
  async function runCommands(_input, deps) {
430
468
  deps.stdout(
431
469
  `${JSON.stringify({
432
470
  ok: true,
433
471
  output: "JSON by default in CI, pipes, non-TTY, --json, or --quiet.",
434
- commands: COMMANDS
472
+ commands: COMMAND_CATALOG
435
473
  })}
436
474
  `
437
475
  );
438
476
  return { exitCode: 0 };
439
477
  }
440
478
 
441
- // src/commands/completions.ts
442
- function generateCompletions(program) {
443
- const name = program.name();
444
- const cmds = program.commands.map((cmd) => ({
445
- name: cmd.name(),
446
- subs: cmd.commands.map((sub) => sub.name()),
447
- opts: cmd.options.map((o) => o.long).filter(Boolean)
448
- }));
449
- const topNames = cmds.map((c) => c.name).join(" ");
450
- const topOpts = program.options.map((o) => o.long).filter(Boolean).join(" ");
451
- const cases = cmds.filter((c) => c.subs.length > 0).map(
452
- (c) => ` ${c.name})
453
- COMPREPLY=($(compgen -W "${c.subs.join(" ")}" -- "$cur"))
454
- ;;`
455
- ).join("\n");
456
- return `
457
- # ${name} shell completions \u2014 add to .bashrc/.zshrc:
458
- # eval "$(${name} completions)"
459
-
460
- if type complete &>/dev/null; then
461
- _${name}_completions() {
462
- local cur prev cmds
463
- cur="\${COMP_WORDS[COMP_CWORD]}"
464
- prev="\${COMP_WORDS[COMP_CWORD-1]}"
465
- cmds="${topNames}"
466
-
467
- if [[ \${COMP_CWORD} -eq 1 ]]; then
468
- COMPREPLY=($(compgen -W "$cmds ${topOpts}" -- "$cur"))
469
- return
470
- fi
471
-
472
- case "\${COMP_WORDS[1]}" in
473
- ${cases}
474
- *)
475
- COMPREPLY=($(compgen -f -- "$cur"))
476
- ;;
477
- esac
478
- }
479
- complete -F _${name}_completions ${name}
480
-
481
- elif type compctl &>/dev/null; then
482
- _${name}_completions() {
483
- reply=(${topNames})
484
- }
485
- compctl -K _${name}_completions + -f ${name}
486
- fi
487
- `.trimStart();
488
- }
489
-
490
479
  // src/commands/deployments.ts
491
480
  async function runDeploymentsList(dropId, input, deps) {
492
- if (!await hasCredential2(deps)) return writeAuthError(deps);
481
+ try {
482
+ await requireCredential({
483
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
484
+ env: deps.env,
485
+ store: deps.store
486
+ });
487
+ } catch {
488
+ return writeAuthError(deps);
489
+ }
493
490
  const params = {
494
491
  ...input.limit !== void 0 ? { limit: input.limit } : {},
495
492
  ...input.cursor ? { cursor: input.cursor } : {}
@@ -522,7 +519,15 @@ async function runDeploymentsList(dropId, input, deps) {
522
519
  return { exitCode: 0 };
523
520
  }
524
521
  async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
525
- if (!await hasCredential2(deps)) return writeAuthError(deps);
522
+ try {
523
+ await requireCredential({
524
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
525
+ env: deps.env,
526
+ store: deps.store
527
+ });
528
+ } catch {
529
+ return writeAuthError(deps);
530
+ }
526
531
  const result = await deps.client.deployments.get(dropId, deploymentId);
527
532
  if (result.error) {
528
533
  return writeApiError(deps, result.error);
@@ -536,13 +541,6 @@ async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
536
541
  writeKv(deps, pairs, { ok: true, deployment: result.data });
537
542
  return { exitCode: 0 };
538
543
  }
539
- async function hasCredential2(deps) {
540
- return await resolveCredential({
541
- ...deps.apiKey ? { apiKey: deps.apiKey } : {},
542
- env: deps.env,
543
- store: deps.store
544
- }) !== null;
545
- }
546
544
  function spreadData(data) {
547
545
  return data && typeof data === "object" ? data : { data };
548
546
  }
@@ -557,13 +555,13 @@ async function runDoctor(_input, deps) {
557
555
  const authSource = credential?.source ?? "missing";
558
556
  const storageBackend = stored?.storage ?? "none";
559
557
  const pairs = [
560
- ["Version", "0.3.3"],
558
+ ["Version", "0.3.5"],
561
559
  ["Auth", authSource],
562
560
  ["Storage", storageBackend]
563
561
  ];
564
562
  writeKv(deps, pairs, {
565
563
  ok: true,
566
- version: "0.3.3",
564
+ version: "0.3.5",
567
565
  auth: { source: authSource },
568
566
  storage: { backend: storageBackend }
569
567
  });
@@ -573,14 +571,22 @@ async function runDoctor(_input, deps) {
573
571
  // src/commands/drops.ts
574
572
  var prompts2 = __toESM(require("@clack/prompts"), 1);
575
573
  async function runDropsList(input, deps) {
576
- if (!await hasCredential3(deps)) return writeAuthError(deps);
574
+ try {
575
+ await requireCredential({
576
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
577
+ env: deps.env,
578
+ store: deps.store
579
+ });
580
+ } catch {
581
+ return writeAuthError(deps);
582
+ }
577
583
  const params = {
578
584
  ...input.limit !== void 0 ? { limit: input.limit } : {},
579
585
  ...input.cursor ? { cursor: input.cursor } : {}
580
586
  };
581
587
  const result = await deps.client.drops.list(params);
582
588
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
583
- const raw = listItems2(result.data);
589
+ const raw = unwrapListData(result.data, "drops");
584
590
  const items = Array.isArray(raw) ? raw : void 0;
585
591
  if (deps.outputMode === "human") {
586
592
  if (!items || items.length === 0) {
@@ -599,7 +605,15 @@ async function runDropsList(input, deps) {
599
605
  return { exitCode: 0 };
600
606
  }
601
607
  async function runDropsGet(dropId, _input, deps) {
602
- if (!await hasCredential3(deps)) return writeAuthError(deps);
608
+ try {
609
+ await requireCredential({
610
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
611
+ env: deps.env,
612
+ store: deps.store
613
+ });
614
+ } catch {
615
+ return writeAuthError(deps);
616
+ }
603
617
  const result = await deps.client.drops.get(dropId);
604
618
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
605
619
  const data = result.data;
@@ -636,7 +650,15 @@ async function runDropsDelete(dropId, input, deps) {
636
650
  return { exitCode: 0 };
637
651
  }
638
652
  }
639
- if (!await hasCredential3(deps)) return writeAuthError(deps);
653
+ try {
654
+ await requireCredential({
655
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
656
+ env: deps.env,
657
+ store: deps.store
658
+ });
659
+ } catch {
660
+ return writeAuthError(deps);
661
+ }
640
662
  const result = await deps.client.drops.delete(dropId);
641
663
  if (result?.error) return writeApiErrorSimple(deps, result.error.message);
642
664
  writeHumanOrJson(deps, success(`Deleted ${dropId}`), {
@@ -646,22 +668,6 @@ async function runDropsDelete(dropId, input, deps) {
646
668
  });
647
669
  return { exitCode: 0 };
648
670
  }
649
- async function hasCredential3(deps) {
650
- return await resolveCredential({
651
- ...deps.apiKey ? { apiKey: deps.apiKey } : {},
652
- env: deps.env,
653
- store: deps.store
654
- }) !== null;
655
- }
656
- function listItems2(data) {
657
- if (data && typeof data === "object" && "data" in data) {
658
- return data.data;
659
- }
660
- if (data && typeof data === "object" && "drops" in data) {
661
- return data.drops;
662
- }
663
- return data;
664
- }
665
671
 
666
672
  // src/commands/login.ts
667
673
  var prompts3 = __toESM(require("@clack/prompts"), 1);
@@ -807,9 +813,88 @@ async function runLogout(input, deps) {
807
813
  }
808
814
 
809
815
  // src/commands/publish.ts
810
- var import_node_crypto = require("crypto");
811
816
  var import_promises3 = require("fs/promises");
817
+ var import_node_path2 = require("path");
818
+
819
+ // src/drop-operations.ts
820
+ var import_node_crypto = require("crypto");
812
821
  var import_node_path = require("path");
822
+ var MAX_BUNDLE_FILE_COUNT = 200;
823
+ function isFileSystemError(error2) {
824
+ if (!(error2 instanceof Error)) return false;
825
+ const code = error2.code;
826
+ return code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELIMIT";
827
+ }
828
+ function mimeForPath(filePath) {
829
+ const ext = (0, import_node_path.extname)(filePath).toLowerCase();
830
+ const types = {
831
+ ".html": "text/html",
832
+ ".htm": "text/html",
833
+ ".css": "text/css",
834
+ ".js": "text/javascript",
835
+ ".mjs": "text/javascript",
836
+ ".json": "application/json",
837
+ ".xml": "application/xml",
838
+ ".svg": "image/svg+xml",
839
+ ".txt": "text/plain",
840
+ ".md": "text/markdown",
841
+ ".jpg": "image/jpeg",
842
+ ".jpeg": "image/jpeg",
843
+ ".png": "image/png",
844
+ ".gif": "image/gif",
845
+ ".webp": "image/webp",
846
+ ".ico": "image/x-icon",
847
+ ".avif": "image/avif",
848
+ ".pdf": "application/pdf",
849
+ ".zip": "application/zip",
850
+ ".woff": "font/woff",
851
+ ".woff2": "font/woff2",
852
+ ".ttf": "font/ttf",
853
+ ".mp4": "video/mp4",
854
+ ".webm": "video/webm"
855
+ };
856
+ return types[ext] ?? "application/octet-stream";
857
+ }
858
+ function withIdempotencyKey(options, prefix) {
859
+ return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_${prefix}_${(0, import_node_crypto.randomUUID)()}` };
860
+ }
861
+ function validateManifestFileCount(count) {
862
+ if (count > MAX_BUNDLE_FILE_COUNT) {
863
+ throw Object.assign(
864
+ new Error(
865
+ `Bundle contains ${count} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
866
+ ),
867
+ { code: "ELIMIT" }
868
+ );
869
+ }
870
+ }
871
+ function formatDryRunManifest(prepared, extra) {
872
+ const totalBytes = prepared.manifest.files.reduce(
873
+ (sum, f) => sum + f.sizeBytes,
874
+ 0
875
+ );
876
+ return {
877
+ dryRun: true,
878
+ kind: "staged",
879
+ ...extra,
880
+ manifest: {
881
+ files: prepared.manifest.files,
882
+ entry: prepared.manifest.entry ?? null
883
+ },
884
+ options: prepared.options,
885
+ ...prepared.metadata ? { metadata: prepared.metadata } : {},
886
+ totalBytes
887
+ };
888
+ }
889
+ function dryRunOptionsBody(options) {
890
+ const result = {};
891
+ if (options.title) result.title = options.title;
892
+ if (options.visibility) result.visibility = options.visibility;
893
+ if (options.password !== void 0) result.password = options.password;
894
+ if (options.noindex !== void 0) result.noindex = options.noindex;
895
+ if (options.expiresAt) result.expires_at = options.expiresAt;
896
+ return result;
897
+ }
813
898
 
814
899
  // src/options.ts
815
900
  var import_promises = require("fs/promises");
@@ -905,14 +990,14 @@ async function readJsonObjectFile(path) {
905
990
  }
906
991
 
907
992
  // src/commands/publish.ts
908
- var MAX_BUNDLE_FILE_COUNT = 200;
909
993
  async function runPublish(input, raw, deps) {
910
- const credential = await resolveCredential({
911
- ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
912
- env: deps.env,
913
- store: deps.store
914
- });
915
- if (!credential) {
994
+ try {
995
+ await requireCredential({
996
+ ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
997
+ env: deps.env,
998
+ store: deps.store
999
+ });
1000
+ } catch {
916
1001
  return writeAuthError(deps);
917
1002
  }
918
1003
  if (raw.dryRun) {
@@ -924,7 +1009,7 @@ async function runPublish(input, raw, deps) {
924
1009
  if (raw.fromJson && singleInput) {
925
1010
  throw new Error("Use either <input> or --from-json, not both.");
926
1011
  }
927
- const options = withPublishIdempotency(await parseDropOptions(raw));
1012
+ const options = withIdempotencyKey(await parseDropOptions(raw), "pub");
928
1013
  const result = raw.fromJson ? await deps.client.drops.createRaw(
929
1014
  await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
930
1015
  options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
@@ -980,8 +1065,10 @@ async function handlePublishDryRun(input, raw, deps) {
980
1065
  throw new Error("Client does not support dry-run.");
981
1066
  }
982
1067
  const prepared = await deps.client.prepare(input, options);
983
- const output = formatDryRunOutput(prepared);
984
- validateDryRunOutput(output);
1068
+ const output = formatDryRunManifest(prepared);
1069
+ validateManifestFileCount(
1070
+ output.manifest.files.length
1071
+ );
985
1072
  deps.stdout(`${JSON.stringify(output, null, 2)}
986
1073
  `);
987
1074
  return { exitCode: 0 };
@@ -1012,18 +1099,12 @@ async function dryRunMultiFile(inputs, options, deps) {
1012
1099
  );
1013
1100
  }
1014
1101
  files.push({
1015
- path: (0, import_node_path.basename)(filePath),
1102
+ path: (0, import_node_path2.basename)(filePath),
1016
1103
  contentType: mimeForPath(filePath),
1017
1104
  sizeBytes: info.size
1018
1105
  });
1019
1106
  }
1020
- if (files.length > MAX_BUNDLE_FILE_COUNT) {
1021
- return writeError(
1022
- deps,
1023
- "local_input_error",
1024
- `Bundle contains ${files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
1025
- );
1026
- }
1107
+ validateManifestFileCount(files.length);
1027
1108
  const entry = options.entry ?? files.find((f) => f.path === "index.html")?.path ?? files[0]?.path ?? null;
1028
1109
  const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0);
1029
1110
  const output = {
@@ -1037,92 +1118,16 @@ async function dryRunMultiFile(inputs, options, deps) {
1037
1118
  `);
1038
1119
  return { exitCode: 0 };
1039
1120
  }
1040
- function formatDryRunOutput(prepared) {
1041
- const totalBytes = prepared.manifest.files.reduce(
1042
- (sum, f) => sum + f.sizeBytes,
1043
- 0
1044
- );
1045
- return {
1046
- dryRun: true,
1047
- kind: "staged",
1048
- manifest: {
1049
- files: prepared.manifest.files,
1050
- entry: prepared.manifest.entry ?? null
1051
- },
1052
- options: prepared.options,
1053
- ...prepared.metadata ? { metadata: prepared.metadata } : {},
1054
- totalBytes
1055
- };
1056
- }
1057
- function validateDryRunOutput(output) {
1058
- if (output.kind !== "staged") return;
1059
- const manifest = output.manifest;
1060
- if (manifest.files.length > MAX_BUNDLE_FILE_COUNT) {
1061
- throw Object.assign(
1062
- new Error(
1063
- `Bundle contains ${manifest.files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
1064
- ),
1065
- { code: "ELIMIT" }
1066
- );
1067
- }
1068
- }
1069
- function dryRunOptionsBody(options) {
1070
- const result = {};
1071
- if (options.title) result.title = options.title;
1072
- if (options.visibility) result.visibility = options.visibility;
1073
- if (options.password !== void 0) result.password = options.password;
1074
- if (options.noindex !== void 0) result.noindex = options.noindex;
1075
- if (options.expiresAt) result.expires_at = options.expiresAt;
1076
- return result;
1077
- }
1078
- function mimeForPath(filePath) {
1079
- const ext = (0, import_node_path.extname)(filePath).toLowerCase();
1080
- const types = {
1081
- ".html": "text/html",
1082
- ".htm": "text/html",
1083
- ".css": "text/css",
1084
- ".js": "text/javascript",
1085
- ".mjs": "text/javascript",
1086
- ".json": "application/json",
1087
- ".xml": "application/xml",
1088
- ".svg": "image/svg+xml",
1089
- ".txt": "text/plain",
1090
- ".md": "text/markdown",
1091
- ".jpg": "image/jpeg",
1092
- ".jpeg": "image/jpeg",
1093
- ".png": "image/png",
1094
- ".gif": "image/gif",
1095
- ".webp": "image/webp",
1096
- ".ico": "image/x-icon",
1097
- ".avif": "image/avif",
1098
- ".pdf": "application/pdf",
1099
- ".zip": "application/zip",
1100
- ".woff": "font/woff",
1101
- ".woff2": "font/woff2",
1102
- ".ttf": "font/ttf",
1103
- ".mp4": "video/mp4",
1104
- ".webm": "video/webm"
1105
- };
1106
- return types[ext] ?? "application/octet-stream";
1107
- }
1108
- function isFileSystemError(error2) {
1109
- if (!(error2 instanceof Error)) return false;
1110
- const code = error2.code;
1111
- return code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELIMIT";
1112
- }
1113
- function withPublishIdempotency(options) {
1114
- return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_pub_${(0, import_node_crypto.randomUUID)()}` };
1115
- }
1116
1121
 
1117
1122
  // src/commands/update.ts
1118
- var import_node_crypto2 = require("crypto");
1119
1123
  async function runUpdate(target, input, raw, deps) {
1120
- const credential = await resolveCredential({
1121
- ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
1122
- env: deps.env,
1123
- store: deps.store
1124
- });
1125
- if (!credential) {
1124
+ try {
1125
+ await requireCredential({
1126
+ ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
1127
+ env: deps.env,
1128
+ store: deps.store
1129
+ });
1130
+ } catch {
1126
1131
  return writeAuthError(deps);
1127
1132
  }
1128
1133
  if (raw.dryRun) {
@@ -1139,17 +1144,23 @@ async function runUpdate(target, input, raw, deps) {
1139
1144
  const result = raw.fromJson ? await deps.client.deployments.createRaw(
1140
1145
  target,
1141
1146
  await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
1142
- withUpdateIdempotency({
1143
- ...revisionOptions,
1144
- ...parsed.idempotencyKey ? { idempotencyKey: parsed.idempotencyKey } : {}
1145
- })
1147
+ withIdempotencyKey(
1148
+ {
1149
+ ...revisionOptions,
1150
+ ...parsed.idempotencyKey ? { idempotencyKey: parsed.idempotencyKey } : {}
1151
+ },
1152
+ "upd"
1153
+ )
1146
1154
  ) : input ? await deps.client.update(
1147
1155
  target,
1148
1156
  input,
1149
- withUpdateIdempotency({
1150
- ...parsed,
1151
- ...revisionOptions
1152
- })
1157
+ withIdempotencyKey(
1158
+ {
1159
+ ...parsed,
1160
+ ...revisionOptions
1161
+ },
1162
+ "upd"
1163
+ )
1153
1164
  ) : await deps.client.update(target, parsed, revisionOptions);
1154
1165
  if (result.error) {
1155
1166
  spin?.fail();
@@ -1162,8 +1173,9 @@ async function runUpdate(target, input, raw, deps) {
1162
1173
  return { exitCode: 0 };
1163
1174
  } catch (error2) {
1164
1175
  const message = error2 instanceof Error ? error2.message : "Update failed.";
1176
+ const code = isFileSystemError(error2) ? "local_input_error" : "invalid_usage";
1165
1177
  spin?.fail();
1166
- return writeError(deps, "invalid_usage", message);
1178
+ return writeError(deps, code, message);
1167
1179
  }
1168
1180
  }
1169
1181
  async function handleUpdateDryRun(target, input, raw, deps) {
@@ -1209,56 +1221,17 @@ async function handleUpdateDryRun(target, input, raw, deps) {
1209
1221
  throw new Error("Client does not support dry-run.");
1210
1222
  }
1211
1223
  const prepared = await deps.client.prepare(input, options);
1212
- const output = formatUpdateDryRun(target, prepared);
1213
- validateDryRunFileCount(output);
1224
+ const output = formatDryRunManifest(prepared, { target });
1225
+ validateManifestFileCount(prepared.manifest.files.length);
1214
1226
  deps.stdout(`${JSON.stringify(output, null, 2)}
1215
1227
  `);
1216
1228
  return { exitCode: 0 };
1217
1229
  } catch (error2) {
1218
- const code = isFileSystemError2(error2) ? "local_input_error" : "invalid_usage";
1230
+ const code = isFileSystemError(error2) ? "local_input_error" : "invalid_usage";
1219
1231
  const message = error2 instanceof Error ? error2.message : "Dry-run failed.";
1220
1232
  return writeError(deps, code, message);
1221
1233
  }
1222
1234
  }
1223
- var MAX_BUNDLE_FILE_COUNT2 = 200;
1224
- function formatUpdateDryRun(target, prepared) {
1225
- const totalBytes = prepared.manifest.files.reduce(
1226
- (sum, f) => sum + f.sizeBytes,
1227
- 0
1228
- );
1229
- return {
1230
- dryRun: true,
1231
- kind: "staged",
1232
- target,
1233
- manifest: {
1234
- files: prepared.manifest.files,
1235
- entry: prepared.manifest.entry ?? null
1236
- },
1237
- options: prepared.options,
1238
- ...prepared.metadata ? { metadata: prepared.metadata } : {},
1239
- totalBytes
1240
- };
1241
- }
1242
- function validateDryRunFileCount(output) {
1243
- if (output.kind !== "staged") return;
1244
- const manifest = output.manifest;
1245
- if (manifest.files.length > MAX_BUNDLE_FILE_COUNT2) {
1246
- throw Object.assign(
1247
- new Error(
1248
- `Bundle contains ${manifest.files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT2}.`
1249
- ),
1250
- { code: "ELIMIT" }
1251
- );
1252
- }
1253
- }
1254
- function isFileSystemError2(error2) {
1255
- if (!(error2 instanceof Error)) return false;
1256
- const code = error2.code;
1257
- return code === "ENOENT" || code === "ENOTDIR";
1258
- }
1259
- function withUpdateIdempotency(options) {
1260
- return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_upd_${(0, import_node_crypto2.randomUUID)()}` };
1261
- }
1262
1235
  function assertDropId(target) {
1263
1236
  if (!target.startsWith("drop_")) {
1264
1237
  throw new Error(
@@ -1329,7 +1302,7 @@ function createContext(input) {
1329
1302
  // src/storage.ts
1330
1303
  var import_promises4 = require("fs/promises");
1331
1304
  var import_node_os = require("os");
1332
- var import_node_path2 = require("path");
1305
+ var import_node_path3 = require("path");
1333
1306
  var import_keyring = require("@napi-rs/keyring");
1334
1307
  var MemoryCredentialStore = class {
1335
1308
  credential = null;
@@ -1406,8 +1379,8 @@ function defaultKeyringAdapter() {
1406
1379
  };
1407
1380
  }
1408
1381
  function credentialPath(configDir) {
1409
- return (0, import_node_path2.join)(
1410
- configDir ?? (0, import_node_path2.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
1382
+ return (0, import_node_path3.join)(
1383
+ configDir ?? (0, import_node_path3.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
1411
1384
  "credentials.json"
1412
1385
  );
1413
1386
  }
@@ -1422,7 +1395,7 @@ async function readJsonFile(path) {
1422
1395
  }
1423
1396
  }
1424
1397
  async function writeCredentialFile(path, credential) {
1425
- await (0, import_promises4.mkdir)((0, import_node_path2.dirname)(path), { recursive: true });
1398
+ await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(path), { recursive: true });
1426
1399
  await (0, import_promises4.writeFile)(path, `${JSON.stringify(credential, null, 2)}
1427
1400
  `, {
1428
1401
  mode: 384
@@ -1442,7 +1415,7 @@ function buildProgram(options = {}) {
1442
1415
  ` ${import_picocolors4.default.cyan("dropthis login")}`,
1443
1416
  ` ${import_picocolors4.default.cyan("dropthis publish ./dist")}`
1444
1417
  ].join("\n")
1445
- ).version("0.3.3").configureHelp({
1418
+ ).version("0.3.5").configureHelp({
1446
1419
  subcommandTerm(cmd) {
1447
1420
  const args = cmd.registeredArguments.map((a) => {
1448
1421
  const name = a.name();
@@ -1689,9 +1662,6 @@ function buildProgram(options = {}) {
1689
1662
  const result = await runAccountGet(commandOptions, deps);
1690
1663
  process.exitCode = result.exitCode;
1691
1664
  });
1692
- program.command("completions").description("Output shell completions for bash/zsh").action(() => {
1693
- process.stdout.write(generateCompletions(program));
1694
- });
1695
1665
  program.command("doctor").description("Report CLI diagnostics").option("--json", "Force JSON output").action(async (commandOptions) => {
1696
1666
  const deps = await commandDeps(
1697
1667
  program,
@@ -1748,15 +1718,10 @@ function globalOptions(program, commandOptions) {
1748
1718
  };
1749
1719
  }
1750
1720
  function toDropOptions(options) {
1751
- if (options.visibility && options.visibility !== "public" && options.visibility !== "unlisted") {
1752
- throw new Error(
1753
- `Invalid visibility "${options.visibility}". Use "public" or "unlisted".`
1754
- );
1755
- }
1756
1721
  return {
1757
1722
  ...options.slug ? { slug: options.slug } : {},
1758
1723
  ...options.title ? { title: options.title } : {},
1759
- ...options.visibility === "public" || options.visibility === "unlisted" ? { visibility: options.visibility } : {},
1724
+ ...options.visibility ? { visibility: options.visibility } : {},
1760
1725
  ...typeof options.password === "string" ? { password: options.password } : {},
1761
1726
  ...options.password === false || options.noPassword ? { noPassword: true } : {},
1762
1727
  ...options.noindex ? { noindex: true } : {},