@dropthis/cli 0.3.4 → 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/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
  }
@@ -84,11 +93,14 @@ function apiErrorEnvelope(error2) {
84
93
  ...error2.param ? { param: error2.param } : {},
85
94
  ...error2.currentRevision !== void 0 ? { current_revision: error2.currentRevision } : {},
86
95
  ...error2.requestId ? { request_id: error2.requestId } : {},
96
+ ...error2.suggestion ? { suggestion: error2.suggestion } : {},
97
+ ...error2.retryable !== void 0 && error2.retryable !== null ? { retryable: error2.retryable } : {},
87
98
  next_action: nextActionForApiError(error2)
88
99
  }
89
100
  };
90
101
  }
91
102
  function nextActionForApiError(error2) {
103
+ if (error2.suggestion) return error2.suggestion;
92
104
  const uploadNextAction = error2.code ? UPLOAD_NEXT_ACTIONS[error2.code] : void 0;
93
105
  if (uploadNextAction) return uploadNextAction;
94
106
  if (error2.code === "revision_conflict") {
@@ -212,8 +224,11 @@ function writeApiError(deps, details) {
212
224
  if (deps.outputMode === "human") {
213
225
  deps.stderr(`${error(details.message)}
214
226
  `);
215
- const nextAction = details.requestId ? `Request ID: ${details.requestId}` : void 0;
227
+ const nextAction = nextActionForApiError(details);
216
228
  if (nextAction) deps.stderr(`${hint(nextAction)}
229
+ `);
230
+ if (details.requestId)
231
+ deps.stderr(`${import_picocolors2.default.dim(`Request ID: ${details.requestId}`)}
217
232
  `);
218
233
  } else {
219
234
  deps.stderr(`${JSON.stringify(apiErrorEnvelope(details))}
@@ -251,11 +266,13 @@ function writeEmpty(deps, message, json) {
251
266
 
252
267
  // src/commands/account.ts
253
268
  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) {
269
+ try {
270
+ await requireCredential({
271
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
272
+ env: deps.env,
273
+ store: deps.store
274
+ });
275
+ } catch {
259
276
  return writeAuthError(deps);
260
277
  }
261
278
  const result = await deps.client.account.get();
@@ -273,8 +290,33 @@ async function runAccountGet(_input, deps) {
273
290
 
274
291
  // src/commands/api-keys.ts
275
292
  var prompts = __toESM(require("@clack/prompts"), 1);
293
+
294
+ // src/types.ts
295
+ function unwrapListData(data, ...fallbackKeys) {
296
+ if (data && typeof data === "object") {
297
+ if ("data" in data) {
298
+ return data.data;
299
+ }
300
+ for (const key of fallbackKeys) {
301
+ if (key in data) {
302
+ return data[key];
303
+ }
304
+ }
305
+ }
306
+ return data;
307
+ }
308
+
309
+ // src/commands/api-keys.ts
276
310
  async function runApiKeysCreate(input, deps) {
277
- if (!await hasCredential(deps)) return writeAuthError(deps);
311
+ try {
312
+ await requireCredential({
313
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
314
+ env: deps.env,
315
+ store: deps.store
316
+ });
317
+ } catch {
318
+ return writeAuthError(deps);
319
+ }
278
320
  const result = await deps.client.apiKeys.create({ label: input.label });
279
321
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
280
322
  const data = result.data;
@@ -286,10 +328,18 @@ async function runApiKeysCreate(input, deps) {
286
328
  return { exitCode: 0 };
287
329
  }
288
330
  async function runApiKeysList(_input, deps) {
289
- if (!await hasCredential(deps)) return writeAuthError(deps);
331
+ try {
332
+ await requireCredential({
333
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
334
+ env: deps.env,
335
+ store: deps.store
336
+ });
337
+ } catch {
338
+ return writeAuthError(deps);
339
+ }
290
340
  const result = await deps.client.apiKeys.list();
291
341
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
292
- const raw = listItems(result.data);
342
+ const raw = unwrapListData(result.data, "apiKeys", "api_keys");
293
343
  const items = Array.isArray(raw) ? raw : void 0;
294
344
  if (deps.outputMode === "human") {
295
345
  if (!items || items.length === 0) {
@@ -323,7 +373,15 @@ async function runApiKeysDelete(keyId, input, deps) {
323
373
  return { exitCode: 0 };
324
374
  }
325
375
  }
326
- if (!await hasCredential(deps)) return writeAuthError(deps);
376
+ try {
377
+ await requireCredential({
378
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
379
+ env: deps.env,
380
+ store: deps.store
381
+ });
382
+ } catch {
383
+ return writeAuthError(deps);
384
+ }
327
385
  const result = await deps.client.apiKeys.delete(keyId);
328
386
  if (result?.error) return writeApiErrorSimple(deps, result.error.message);
329
387
  writeHumanOrJson(deps, success(`Deleted ${keyId}`), {
@@ -333,25 +391,9 @@ async function runApiKeysDelete(keyId, input, deps) {
333
391
  });
334
392
  return { exitCode: 0 };
335
393
  }
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
394
 
353
- // src/commands/commands.ts
354
- var COMMANDS = [
395
+ // src/catalog.ts
396
+ var COMMAND_CATALOG = [
355
397
  {
356
398
  name: "publish",
357
399
  description: "Publish an input and return a Dropthis URL.",
@@ -426,12 +468,14 @@ var COMMANDS = [
426
468
  examples: ["dropthis commands --json"]
427
469
  }
428
470
  ];
471
+
472
+ // src/commands/commands.ts
429
473
  async function runCommands(_input, deps) {
430
474
  deps.stdout(
431
475
  `${JSON.stringify({
432
476
  ok: true,
433
477
  output: "JSON by default in CI, pipes, non-TTY, --json, or --quiet.",
434
- commands: COMMANDS
478
+ commands: COMMAND_CATALOG
435
479
  })}
436
480
  `
437
481
  );
@@ -440,7 +484,15 @@ async function runCommands(_input, deps) {
440
484
 
441
485
  // src/commands/deployments.ts
442
486
  async function runDeploymentsList(dropId, input, deps) {
443
- if (!await hasCredential2(deps)) return writeAuthError(deps);
487
+ try {
488
+ await requireCredential({
489
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
490
+ env: deps.env,
491
+ store: deps.store
492
+ });
493
+ } catch {
494
+ return writeAuthError(deps);
495
+ }
444
496
  const params = {
445
497
  ...input.limit !== void 0 ? { limit: input.limit } : {},
446
498
  ...input.cursor ? { cursor: input.cursor } : {}
@@ -473,7 +525,15 @@ async function runDeploymentsList(dropId, input, deps) {
473
525
  return { exitCode: 0 };
474
526
  }
475
527
  async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
476
- if (!await hasCredential2(deps)) return writeAuthError(deps);
528
+ try {
529
+ await requireCredential({
530
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
531
+ env: deps.env,
532
+ store: deps.store
533
+ });
534
+ } catch {
535
+ return writeAuthError(deps);
536
+ }
477
537
  const result = await deps.client.deployments.get(dropId, deploymentId);
478
538
  if (result.error) {
479
539
  return writeApiError(deps, result.error);
@@ -487,13 +547,6 @@ async function runDeploymentsGet(dropId, deploymentId, _input, deps) {
487
547
  writeKv(deps, pairs, { ok: true, deployment: result.data });
488
548
  return { exitCode: 0 };
489
549
  }
490
- async function hasCredential2(deps) {
491
- return await resolveCredential({
492
- ...deps.apiKey ? { apiKey: deps.apiKey } : {},
493
- env: deps.env,
494
- store: deps.store
495
- }) !== null;
496
- }
497
550
  function spreadData(data) {
498
551
  return data && typeof data === "object" ? data : { data };
499
552
  }
@@ -508,13 +561,13 @@ async function runDoctor(_input, deps) {
508
561
  const authSource = credential?.source ?? "missing";
509
562
  const storageBackend = stored?.storage ?? "none";
510
563
  const pairs = [
511
- ["Version", "0.3.4"],
564
+ ["Version", "0.4.0"],
512
565
  ["Auth", authSource],
513
566
  ["Storage", storageBackend]
514
567
  ];
515
568
  writeKv(deps, pairs, {
516
569
  ok: true,
517
- version: "0.3.4",
570
+ version: "0.4.0",
518
571
  auth: { source: authSource },
519
572
  storage: { backend: storageBackend }
520
573
  });
@@ -524,14 +577,22 @@ async function runDoctor(_input, deps) {
524
577
  // src/commands/drops.ts
525
578
  var prompts2 = __toESM(require("@clack/prompts"), 1);
526
579
  async function runDropsList(input, deps) {
527
- if (!await hasCredential3(deps)) return writeAuthError(deps);
580
+ try {
581
+ await requireCredential({
582
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
583
+ env: deps.env,
584
+ store: deps.store
585
+ });
586
+ } catch {
587
+ return writeAuthError(deps);
588
+ }
528
589
  const params = {
529
590
  ...input.limit !== void 0 ? { limit: input.limit } : {},
530
591
  ...input.cursor ? { cursor: input.cursor } : {}
531
592
  };
532
593
  const result = await deps.client.drops.list(params);
533
594
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
534
- const raw = listItems2(result.data);
595
+ const raw = unwrapListData(result.data, "drops");
535
596
  const items = Array.isArray(raw) ? raw : void 0;
536
597
  if (deps.outputMode === "human") {
537
598
  if (!items || items.length === 0) {
@@ -550,7 +611,15 @@ async function runDropsList(input, deps) {
550
611
  return { exitCode: 0 };
551
612
  }
552
613
  async function runDropsGet(dropId, _input, deps) {
553
- if (!await hasCredential3(deps)) return writeAuthError(deps);
614
+ try {
615
+ await requireCredential({
616
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
617
+ env: deps.env,
618
+ store: deps.store
619
+ });
620
+ } catch {
621
+ return writeAuthError(deps);
622
+ }
554
623
  const result = await deps.client.drops.get(dropId);
555
624
  if (result.error) return writeApiErrorSimple(deps, result.error.message);
556
625
  const data = result.data;
@@ -587,7 +656,15 @@ async function runDropsDelete(dropId, input, deps) {
587
656
  return { exitCode: 0 };
588
657
  }
589
658
  }
590
- if (!await hasCredential3(deps)) return writeAuthError(deps);
659
+ try {
660
+ await requireCredential({
661
+ ...deps.apiKey ? { apiKey: deps.apiKey } : {},
662
+ env: deps.env,
663
+ store: deps.store
664
+ });
665
+ } catch {
666
+ return writeAuthError(deps);
667
+ }
591
668
  const result = await deps.client.drops.delete(dropId);
592
669
  if (result?.error) return writeApiErrorSimple(deps, result.error.message);
593
670
  writeHumanOrJson(deps, success(`Deleted ${dropId}`), {
@@ -597,22 +674,6 @@ async function runDropsDelete(dropId, input, deps) {
597
674
  });
598
675
  return { exitCode: 0 };
599
676
  }
600
- async function hasCredential3(deps) {
601
- return await resolveCredential({
602
- ...deps.apiKey ? { apiKey: deps.apiKey } : {},
603
- env: deps.env,
604
- store: deps.store
605
- }) !== null;
606
- }
607
- function listItems2(data) {
608
- if (data && typeof data === "object" && "data" in data) {
609
- return data.data;
610
- }
611
- if (data && typeof data === "object" && "drops" in data) {
612
- return data.drops;
613
- }
614
- return data;
615
- }
616
677
 
617
678
  // src/commands/login.ts
618
679
  var prompts3 = __toESM(require("@clack/prompts"), 1);
@@ -758,9 +819,88 @@ async function runLogout(input, deps) {
758
819
  }
759
820
 
760
821
  // src/commands/publish.ts
761
- var import_node_crypto = require("crypto");
762
822
  var import_promises3 = require("fs/promises");
823
+ var import_node_path2 = require("path");
824
+
825
+ // src/drop-operations.ts
826
+ var import_node_crypto = require("crypto");
763
827
  var import_node_path = require("path");
828
+ var MAX_BUNDLE_FILE_COUNT = 200;
829
+ function isFileSystemError(error2) {
830
+ if (!(error2 instanceof Error)) return false;
831
+ const code = error2.code;
832
+ return code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELIMIT";
833
+ }
834
+ function mimeForPath(filePath) {
835
+ const ext = (0, import_node_path.extname)(filePath).toLowerCase();
836
+ const types = {
837
+ ".html": "text/html",
838
+ ".htm": "text/html",
839
+ ".css": "text/css",
840
+ ".js": "text/javascript",
841
+ ".mjs": "text/javascript",
842
+ ".json": "application/json",
843
+ ".xml": "application/xml",
844
+ ".svg": "image/svg+xml",
845
+ ".txt": "text/plain",
846
+ ".md": "text/markdown",
847
+ ".jpg": "image/jpeg",
848
+ ".jpeg": "image/jpeg",
849
+ ".png": "image/png",
850
+ ".gif": "image/gif",
851
+ ".webp": "image/webp",
852
+ ".ico": "image/x-icon",
853
+ ".avif": "image/avif",
854
+ ".pdf": "application/pdf",
855
+ ".zip": "application/zip",
856
+ ".woff": "font/woff",
857
+ ".woff2": "font/woff2",
858
+ ".ttf": "font/ttf",
859
+ ".mp4": "video/mp4",
860
+ ".webm": "video/webm"
861
+ };
862
+ return types[ext] ?? "application/octet-stream";
863
+ }
864
+ function withIdempotencyKey(options, prefix) {
865
+ return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_${prefix}_${(0, import_node_crypto.randomUUID)()}` };
866
+ }
867
+ function validateManifestFileCount(count) {
868
+ if (count > MAX_BUNDLE_FILE_COUNT) {
869
+ throw Object.assign(
870
+ new Error(
871
+ `Bundle contains ${count} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
872
+ ),
873
+ { code: "ELIMIT" }
874
+ );
875
+ }
876
+ }
877
+ function formatDryRunManifest(prepared, extra) {
878
+ const totalBytes = prepared.manifest.files.reduce(
879
+ (sum, f) => sum + f.sizeBytes,
880
+ 0
881
+ );
882
+ return {
883
+ dryRun: true,
884
+ kind: "staged",
885
+ ...extra,
886
+ manifest: {
887
+ files: prepared.manifest.files,
888
+ entry: prepared.manifest.entry ?? null
889
+ },
890
+ options: prepared.options,
891
+ ...prepared.metadata ? { metadata: prepared.metadata } : {},
892
+ totalBytes
893
+ };
894
+ }
895
+ function dryRunOptionsBody(options) {
896
+ const result = {};
897
+ if (options.title) result.title = options.title;
898
+ if (options.visibility) result.visibility = options.visibility;
899
+ if (options.password !== void 0) result.password = options.password;
900
+ if (options.noindex !== void 0) result.noindex = options.noindex;
901
+ if (options.expiresAt) result.expires_at = options.expiresAt;
902
+ return result;
903
+ }
764
904
 
765
905
  // src/options.ts
766
906
  var import_promises = require("fs/promises");
@@ -856,14 +996,14 @@ async function readJsonObjectFile(path) {
856
996
  }
857
997
 
858
998
  // src/commands/publish.ts
859
- var MAX_BUNDLE_FILE_COUNT = 200;
860
999
  async function runPublish(input, raw, deps) {
861
- const credential = await resolveCredential({
862
- ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
863
- env: deps.env,
864
- store: deps.store
865
- });
866
- if (!credential) {
1000
+ try {
1001
+ await requireCredential({
1002
+ ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
1003
+ env: deps.env,
1004
+ store: deps.store
1005
+ });
1006
+ } catch {
867
1007
  return writeAuthError(deps);
868
1008
  }
869
1009
  if (raw.dryRun) {
@@ -875,7 +1015,7 @@ async function runPublish(input, raw, deps) {
875
1015
  if (raw.fromJson && singleInput) {
876
1016
  throw new Error("Use either <input> or --from-json, not both.");
877
1017
  }
878
- const options = withPublishIdempotency(await parseDropOptions(raw));
1018
+ const options = withIdempotencyKey(await parseDropOptions(raw), "pub");
879
1019
  const result = raw.fromJson ? await deps.client.drops.createRaw(
880
1020
  await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
881
1021
  options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
@@ -931,8 +1071,10 @@ async function handlePublishDryRun(input, raw, deps) {
931
1071
  throw new Error("Client does not support dry-run.");
932
1072
  }
933
1073
  const prepared = await deps.client.prepare(input, options);
934
- const output = formatDryRunOutput(prepared);
935
- validateDryRunOutput(output);
1074
+ const output = formatDryRunManifest(prepared);
1075
+ validateManifestFileCount(
1076
+ output.manifest.files.length
1077
+ );
936
1078
  deps.stdout(`${JSON.stringify(output, null, 2)}
937
1079
  `);
938
1080
  return { exitCode: 0 };
@@ -963,18 +1105,12 @@ async function dryRunMultiFile(inputs, options, deps) {
963
1105
  );
964
1106
  }
965
1107
  files.push({
966
- path: (0, import_node_path.basename)(filePath),
1108
+ path: (0, import_node_path2.basename)(filePath),
967
1109
  contentType: mimeForPath(filePath),
968
1110
  sizeBytes: info.size
969
1111
  });
970
1112
  }
971
- if (files.length > MAX_BUNDLE_FILE_COUNT) {
972
- return writeError(
973
- deps,
974
- "local_input_error",
975
- `Bundle contains ${files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
976
- );
977
- }
1113
+ validateManifestFileCount(files.length);
978
1114
  const entry = options.entry ?? files.find((f) => f.path === "index.html")?.path ?? files[0]?.path ?? null;
979
1115
  const totalBytes = files.reduce((sum, f) => sum + f.sizeBytes, 0);
980
1116
  const output = {
@@ -988,92 +1124,16 @@ async function dryRunMultiFile(inputs, options, deps) {
988
1124
  `);
989
1125
  return { exitCode: 0 };
990
1126
  }
991
- function formatDryRunOutput(prepared) {
992
- const totalBytes = prepared.manifest.files.reduce(
993
- (sum, f) => sum + f.sizeBytes,
994
- 0
995
- );
996
- return {
997
- dryRun: true,
998
- kind: "staged",
999
- manifest: {
1000
- files: prepared.manifest.files,
1001
- entry: prepared.manifest.entry ?? null
1002
- },
1003
- options: prepared.options,
1004
- ...prepared.metadata ? { metadata: prepared.metadata } : {},
1005
- totalBytes
1006
- };
1007
- }
1008
- function validateDryRunOutput(output) {
1009
- if (output.kind !== "staged") return;
1010
- const manifest = output.manifest;
1011
- if (manifest.files.length > MAX_BUNDLE_FILE_COUNT) {
1012
- throw Object.assign(
1013
- new Error(
1014
- `Bundle contains ${manifest.files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT}.`
1015
- ),
1016
- { code: "ELIMIT" }
1017
- );
1018
- }
1019
- }
1020
- function dryRunOptionsBody(options) {
1021
- const result = {};
1022
- if (options.title) result.title = options.title;
1023
- if (options.visibility) result.visibility = options.visibility;
1024
- if (options.password !== void 0) result.password = options.password;
1025
- if (options.noindex !== void 0) result.noindex = options.noindex;
1026
- if (options.expiresAt) result.expires_at = options.expiresAt;
1027
- return result;
1028
- }
1029
- function mimeForPath(filePath) {
1030
- const ext = (0, import_node_path.extname)(filePath).toLowerCase();
1031
- const types = {
1032
- ".html": "text/html",
1033
- ".htm": "text/html",
1034
- ".css": "text/css",
1035
- ".js": "text/javascript",
1036
- ".mjs": "text/javascript",
1037
- ".json": "application/json",
1038
- ".xml": "application/xml",
1039
- ".svg": "image/svg+xml",
1040
- ".txt": "text/plain",
1041
- ".md": "text/markdown",
1042
- ".jpg": "image/jpeg",
1043
- ".jpeg": "image/jpeg",
1044
- ".png": "image/png",
1045
- ".gif": "image/gif",
1046
- ".webp": "image/webp",
1047
- ".ico": "image/x-icon",
1048
- ".avif": "image/avif",
1049
- ".pdf": "application/pdf",
1050
- ".zip": "application/zip",
1051
- ".woff": "font/woff",
1052
- ".woff2": "font/woff2",
1053
- ".ttf": "font/ttf",
1054
- ".mp4": "video/mp4",
1055
- ".webm": "video/webm"
1056
- };
1057
- return types[ext] ?? "application/octet-stream";
1058
- }
1059
- function isFileSystemError(error2) {
1060
- if (!(error2 instanceof Error)) return false;
1061
- const code = error2.code;
1062
- return code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELIMIT";
1063
- }
1064
- function withPublishIdempotency(options) {
1065
- return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_pub_${(0, import_node_crypto.randomUUID)()}` };
1066
- }
1067
1127
 
1068
1128
  // src/commands/update.ts
1069
- var import_node_crypto2 = require("crypto");
1070
1129
  async function runUpdate(target, input, raw, deps) {
1071
- const credential = await resolveCredential({
1072
- ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
1073
- env: deps.env,
1074
- store: deps.store
1075
- });
1076
- if (!credential) {
1130
+ try {
1131
+ await requireCredential({
1132
+ ...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
1133
+ env: deps.env,
1134
+ store: deps.store
1135
+ });
1136
+ } catch {
1077
1137
  return writeAuthError(deps);
1078
1138
  }
1079
1139
  if (raw.dryRun) {
@@ -1090,17 +1150,23 @@ async function runUpdate(target, input, raw, deps) {
1090
1150
  const result = raw.fromJson ? await deps.client.deployments.createRaw(
1091
1151
  target,
1092
1152
  await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson),
1093
- withUpdateIdempotency({
1094
- ...revisionOptions,
1095
- ...parsed.idempotencyKey ? { idempotencyKey: parsed.idempotencyKey } : {}
1096
- })
1153
+ withIdempotencyKey(
1154
+ {
1155
+ ...revisionOptions,
1156
+ ...parsed.idempotencyKey ? { idempotencyKey: parsed.idempotencyKey } : {}
1157
+ },
1158
+ "upd"
1159
+ )
1097
1160
  ) : input ? await deps.client.update(
1098
1161
  target,
1099
1162
  input,
1100
- withUpdateIdempotency({
1101
- ...parsed,
1102
- ...revisionOptions
1103
- })
1163
+ withIdempotencyKey(
1164
+ {
1165
+ ...parsed,
1166
+ ...revisionOptions
1167
+ },
1168
+ "upd"
1169
+ )
1104
1170
  ) : await deps.client.update(target, parsed, revisionOptions);
1105
1171
  if (result.error) {
1106
1172
  spin?.fail();
@@ -1113,8 +1179,9 @@ async function runUpdate(target, input, raw, deps) {
1113
1179
  return { exitCode: 0 };
1114
1180
  } catch (error2) {
1115
1181
  const message = error2 instanceof Error ? error2.message : "Update failed.";
1182
+ const code = isFileSystemError(error2) ? "local_input_error" : "invalid_usage";
1116
1183
  spin?.fail();
1117
- return writeError(deps, "invalid_usage", message);
1184
+ return writeError(deps, code, message);
1118
1185
  }
1119
1186
  }
1120
1187
  async function handleUpdateDryRun(target, input, raw, deps) {
@@ -1160,56 +1227,17 @@ async function handleUpdateDryRun(target, input, raw, deps) {
1160
1227
  throw new Error("Client does not support dry-run.");
1161
1228
  }
1162
1229
  const prepared = await deps.client.prepare(input, options);
1163
- const output = formatUpdateDryRun(target, prepared);
1164
- validateDryRunFileCount(output);
1230
+ const output = formatDryRunManifest(prepared, { target });
1231
+ validateManifestFileCount(prepared.manifest.files.length);
1165
1232
  deps.stdout(`${JSON.stringify(output, null, 2)}
1166
1233
  `);
1167
1234
  return { exitCode: 0 };
1168
1235
  } catch (error2) {
1169
- const code = isFileSystemError2(error2) ? "local_input_error" : "invalid_usage";
1236
+ const code = isFileSystemError(error2) ? "local_input_error" : "invalid_usage";
1170
1237
  const message = error2 instanceof Error ? error2.message : "Dry-run failed.";
1171
1238
  return writeError(deps, code, message);
1172
1239
  }
1173
1240
  }
1174
- var MAX_BUNDLE_FILE_COUNT2 = 200;
1175
- function formatUpdateDryRun(target, prepared) {
1176
- const totalBytes = prepared.manifest.files.reduce(
1177
- (sum, f) => sum + f.sizeBytes,
1178
- 0
1179
- );
1180
- return {
1181
- dryRun: true,
1182
- kind: "staged",
1183
- target,
1184
- manifest: {
1185
- files: prepared.manifest.files,
1186
- entry: prepared.manifest.entry ?? null
1187
- },
1188
- options: prepared.options,
1189
- ...prepared.metadata ? { metadata: prepared.metadata } : {},
1190
- totalBytes
1191
- };
1192
- }
1193
- function validateDryRunFileCount(output) {
1194
- if (output.kind !== "staged") return;
1195
- const manifest = output.manifest;
1196
- if (manifest.files.length > MAX_BUNDLE_FILE_COUNT2) {
1197
- throw Object.assign(
1198
- new Error(
1199
- `Bundle contains ${manifest.files.length} files, exceeding the maximum of ${MAX_BUNDLE_FILE_COUNT2}.`
1200
- ),
1201
- { code: "ELIMIT" }
1202
- );
1203
- }
1204
- }
1205
- function isFileSystemError2(error2) {
1206
- if (!(error2 instanceof Error)) return false;
1207
- const code = error2.code;
1208
- return code === "ENOENT" || code === "ENOTDIR";
1209
- }
1210
- function withUpdateIdempotency(options) {
1211
- return options.idempotencyKey ? options : { ...options, idempotencyKey: `cli_upd_${(0, import_node_crypto2.randomUUID)()}` };
1212
- }
1213
1241
  function assertDropId(target) {
1214
1242
  if (!target.startsWith("drop_")) {
1215
1243
  throw new Error(
@@ -1280,7 +1308,7 @@ function createContext(input) {
1280
1308
  // src/storage.ts
1281
1309
  var import_promises4 = require("fs/promises");
1282
1310
  var import_node_os = require("os");
1283
- var import_node_path2 = require("path");
1311
+ var import_node_path3 = require("path");
1284
1312
  var import_keyring = require("@napi-rs/keyring");
1285
1313
  var MemoryCredentialStore = class {
1286
1314
  credential = null;
@@ -1357,8 +1385,8 @@ function defaultKeyringAdapter() {
1357
1385
  };
1358
1386
  }
1359
1387
  function credentialPath(configDir) {
1360
- return (0, import_node_path2.join)(
1361
- configDir ?? (0, import_node_path2.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
1388
+ return (0, import_node_path3.join)(
1389
+ configDir ?? (0, import_node_path3.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
1362
1390
  "credentials.json"
1363
1391
  );
1364
1392
  }
@@ -1373,7 +1401,7 @@ async function readJsonFile(path) {
1373
1401
  }
1374
1402
  }
1375
1403
  async function writeCredentialFile(path, credential) {
1376
- await (0, import_promises4.mkdir)((0, import_node_path2.dirname)(path), { recursive: true });
1404
+ await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(path), { recursive: true });
1377
1405
  await (0, import_promises4.writeFile)(path, `${JSON.stringify(credential, null, 2)}
1378
1406
  `, {
1379
1407
  mode: 384
@@ -1393,7 +1421,7 @@ function buildProgram(options = {}) {
1393
1421
  ` ${import_picocolors4.default.cyan("dropthis login")}`,
1394
1422
  ` ${import_picocolors4.default.cyan("dropthis publish ./dist")}`
1395
1423
  ].join("\n")
1396
- ).version("0.3.4").configureHelp({
1424
+ ).version("0.4.0").configureHelp({
1397
1425
  subcommandTerm(cmd) {
1398
1426
  const args = cmd.registeredArguments.map((a) => {
1399
1427
  const name = a.name();
@@ -1696,15 +1724,10 @@ function globalOptions(program, commandOptions) {
1696
1724
  };
1697
1725
  }
1698
1726
  function toDropOptions(options) {
1699
- if (options.visibility && options.visibility !== "public" && options.visibility !== "unlisted") {
1700
- throw new Error(
1701
- `Invalid visibility "${options.visibility}". Use "public" or "unlisted".`
1702
- );
1703
- }
1704
1727
  return {
1705
1728
  ...options.slug ? { slug: options.slug } : {},
1706
1729
  ...options.title ? { title: options.title } : {},
1707
- ...options.visibility === "public" || options.visibility === "unlisted" ? { visibility: options.visibility } : {},
1730
+ ...options.visibility ? { visibility: options.visibility } : {},
1708
1731
  ...typeof options.password === "string" ? { password: options.password } : {},
1709
1732
  ...options.password === false || options.noPassword ? { noPassword: true } : {},
1710
1733
  ...options.noindex ? { noindex: true } : {},