@dropthis/cli 0.9.3 → 0.11.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/README.md +39 -2
- package/dist/cli.cjs +559 -30
- package/dist/cli.cjs.map +1 -1
- package/node_modules/@dropthis/node/README.md +146 -7
- package/node_modules/@dropthis/node/dist/{drops-BWA0D1IW.d.cts → drops-DEJcU6qw.d.cts} +235 -17
- package/node_modules/@dropthis/node/dist/{drops-BWA0D1IW.d.ts → drops-DEJcU6qw.d.ts} +235 -17
- package/node_modules/@dropthis/node/dist/edge.cjs +301 -41
- package/node_modules/@dropthis/node/dist/edge.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/edge.d.cts +3 -1
- package/node_modules/@dropthis/node/dist/edge.d.ts +3 -1
- package/node_modules/@dropthis/node/dist/edge.mjs +301 -41
- package/node_modules/@dropthis/node/dist/edge.mjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.cjs +310 -43
- package/node_modules/@dropthis/node/dist/index.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.d.cts +12 -7
- package/node_modules/@dropthis/node/dist/index.d.ts +12 -7
- package/node_modules/@dropthis/node/dist/index.mjs +309 -43
- package/node_modules/@dropthis/node/dist/index.mjs.map +1 -1
- package/node_modules/@dropthis/node/package.json +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -135,10 +135,10 @@ function readlineQuestion(question) {
|
|
|
135
135
|
input: process.stdin,
|
|
136
136
|
output: process.stderr
|
|
137
137
|
});
|
|
138
|
-
return new Promise((
|
|
138
|
+
return new Promise((resolve2) => {
|
|
139
139
|
rl.question(question, (answer) => {
|
|
140
140
|
rl.close();
|
|
141
|
-
|
|
141
|
+
resolve2(answer);
|
|
142
142
|
});
|
|
143
143
|
});
|
|
144
144
|
}
|
|
@@ -207,7 +207,7 @@ function nextActionForApiError(error2) {
|
|
|
207
207
|
const uploadNextAction = error2.code ? UPLOAD_NEXT_ACTIONS[error2.code] : void 0;
|
|
208
208
|
if (uploadNextAction) return uploadNextAction;
|
|
209
209
|
if (error2.code === "revision_conflict") {
|
|
210
|
-
return "
|
|
210
|
+
return "Re-read the drop with dropthis get <drop-id>, merge your changes, and retry with --if-revision set to the current revision.";
|
|
211
211
|
}
|
|
212
212
|
if (error2.statusCode === 404 || error2.code === "not_found") {
|
|
213
213
|
return "Check the drop id or slug and retry; list your drops with dropthis list.";
|
|
@@ -232,12 +232,14 @@ function exitCodeFor(code) {
|
|
|
232
232
|
if (code === "auth_error") return 3;
|
|
233
233
|
if (code === "local_input_error") return 4;
|
|
234
234
|
if (code === "network_error") return 5;
|
|
235
|
+
if (code === "verify_pending") return 6;
|
|
236
|
+
if (code === "verify_timeout") return 7;
|
|
235
237
|
return 1;
|
|
236
238
|
}
|
|
237
239
|
|
|
238
240
|
// src/program.ts
|
|
239
241
|
var import_commander = require("commander");
|
|
240
|
-
var
|
|
242
|
+
var import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
241
243
|
|
|
242
244
|
// src/auth.ts
|
|
243
245
|
async function resolveCredential(input) {
|
|
@@ -285,6 +287,9 @@ function success(msg) {
|
|
|
285
287
|
function error(msg) {
|
|
286
288
|
return `${import_picocolors.default.red(symbols.error)} ${msg}`;
|
|
287
289
|
}
|
|
290
|
+
function warn(msg) {
|
|
291
|
+
return `${import_picocolors.default.yellow(symbols.warning)} ${msg}`;
|
|
292
|
+
}
|
|
288
293
|
function hint(msg) {
|
|
289
294
|
return ` ${import_picocolors.default.dim(msg)}`;
|
|
290
295
|
}
|
|
@@ -305,6 +310,17 @@ function writeResult(deps, link, verb, data) {
|
|
|
305
310
|
const details = formatDropDetails(data);
|
|
306
311
|
if (details) deps.stdout(`${details}
|
|
307
312
|
`);
|
|
313
|
+
const warnings = data.warnings;
|
|
314
|
+
if (Array.isArray(warnings)) {
|
|
315
|
+
for (const w of warnings) {
|
|
316
|
+
if (w.code === "slug_suffixed") {
|
|
317
|
+
deps.stderr(
|
|
318
|
+
`${hint(`Vanity slug was taken \u2014 served at: ${w.detail ?? "see URL"}`)}
|
|
319
|
+
`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
308
324
|
} else {
|
|
309
325
|
deps.stdout(`${JSON.stringify({ ok: true, drop: data })}
|
|
310
326
|
`);
|
|
@@ -371,6 +387,11 @@ function writeApiError(deps, details) {
|
|
|
371
387
|
const nextAction = nextActionForApiError(details);
|
|
372
388
|
if (nextAction) deps.stderr(`${hint(nextAction)}
|
|
373
389
|
`);
|
|
390
|
+
if (details.currentRevision !== void 0)
|
|
391
|
+
deps.stderr(
|
|
392
|
+
`${hint(`Current revision: ${details.currentRevision} \u2014 retry with --if-revision ${details.currentRevision}`)}
|
|
393
|
+
`
|
|
394
|
+
);
|
|
374
395
|
if (details.requestId)
|
|
375
396
|
deps.stderr(`${import_picocolors2.default.dim(`Request ID: ${details.requestId}`)}
|
|
376
397
|
`);
|
|
@@ -605,6 +626,13 @@ var COMMAND_ANNOTATIONS = {
|
|
|
605
626
|
auth: "required",
|
|
606
627
|
examples: ["dropthis update-settings drop_abc --title 'Launch' --json"]
|
|
607
628
|
},
|
|
629
|
+
pull: {
|
|
630
|
+
auth: "required",
|
|
631
|
+
examples: [
|
|
632
|
+
"dropthis pull drop_abc -o ./site",
|
|
633
|
+
"dropthis pull https://abc123.dropthis.app"
|
|
634
|
+
]
|
|
635
|
+
},
|
|
608
636
|
get: {
|
|
609
637
|
auth: "required",
|
|
610
638
|
examples: ["dropthis get drop_abc --json"]
|
|
@@ -769,19 +797,322 @@ async function runDoctor(_input, deps) {
|
|
|
769
797
|
const authSource = credential?.source ?? "missing";
|
|
770
798
|
const storageBackend = stored?.storage ?? "none";
|
|
771
799
|
const pairs = [
|
|
772
|
-
["Version", "0.
|
|
800
|
+
["Version", "0.11.0"],
|
|
773
801
|
["Auth", authSource],
|
|
774
802
|
["Storage", storageBackend]
|
|
775
803
|
];
|
|
776
804
|
writeKv(deps, pairs, {
|
|
777
805
|
ok: true,
|
|
778
|
-
version: "0.
|
|
806
|
+
version: "0.11.0",
|
|
779
807
|
auth: { source: authSource },
|
|
780
808
|
storage: { backend: storageBackend }
|
|
781
809
|
});
|
|
782
810
|
return { exitCode: 0 };
|
|
783
811
|
}
|
|
784
812
|
|
|
813
|
+
// src/commands/domains.ts
|
|
814
|
+
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
815
|
+
function formatDnsTable(records) {
|
|
816
|
+
if (records.length === 0) return "";
|
|
817
|
+
const rows = [];
|
|
818
|
+
const colWidths = {
|
|
819
|
+
type: Math.max(4, ...records.map((r) => r.type.length)),
|
|
820
|
+
name: Math.max(4, ...records.map((r) => r.name.length)),
|
|
821
|
+
value: Math.max(5, ...records.map((r) => r.value.length)),
|
|
822
|
+
status: Math.max(6, ...records.map((r) => r.status.length))
|
|
823
|
+
};
|
|
824
|
+
const header = [
|
|
825
|
+
"Type".padEnd(colWidths.type),
|
|
826
|
+
"Name".padEnd(colWidths.name),
|
|
827
|
+
"Value".padEnd(colWidths.value),
|
|
828
|
+
"Status".padEnd(colWidths.status)
|
|
829
|
+
].join(" ");
|
|
830
|
+
rows.push(import_picocolors3.default.dim(header));
|
|
831
|
+
rows.push(import_picocolors3.default.dim("\u2500".repeat(header.length)));
|
|
832
|
+
for (const rec of records) {
|
|
833
|
+
const statusColor = rec.status === "ok" ? import_picocolors3.default.green(rec.status.padEnd(colWidths.status)) : rec.status === "mismatch" ? import_picocolors3.default.yellow(rec.status.padEnd(colWidths.status)) : import_picocolors3.default.red(rec.status.padEnd(colWidths.status));
|
|
834
|
+
rows.push(
|
|
835
|
+
[
|
|
836
|
+
rec.type.padEnd(colWidths.type),
|
|
837
|
+
rec.name.padEnd(colWidths.name),
|
|
838
|
+
rec.value.padEnd(colWidths.value),
|
|
839
|
+
statusColor
|
|
840
|
+
].join(" ")
|
|
841
|
+
);
|
|
842
|
+
if (rec.hint) {
|
|
843
|
+
rows.push(` ${import_picocolors3.default.dim(rec.hint)}`);
|
|
844
|
+
}
|
|
845
|
+
if (rec.observed) {
|
|
846
|
+
rows.push(` ${import_picocolors3.default.dim(`Observed: ${rec.observed}`)}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return rows.join("\n");
|
|
850
|
+
}
|
|
851
|
+
function printNextHints(deps, hints) {
|
|
852
|
+
if (hints.length === 0) return;
|
|
853
|
+
deps.stdout("\n");
|
|
854
|
+
for (const h of hints) {
|
|
855
|
+
deps.stdout(`${hint(h.message)}
|
|
856
|
+
`);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function computeSleepMs(records, elapsedMs, timeoutMs) {
|
|
860
|
+
const maxRetryAfter = records.reduce(
|
|
861
|
+
(max, r) => Math.max(max, r.retryAfter ?? 0),
|
|
862
|
+
0
|
|
863
|
+
);
|
|
864
|
+
const defaultSeconds = maxRetryAfter > 0 ? maxRetryAfter : 15;
|
|
865
|
+
const desiredMs = defaultSeconds * 1e3;
|
|
866
|
+
const remainingMs = timeoutMs - elapsedMs;
|
|
867
|
+
return Math.min(desiredMs, Math.max(0, remainingMs));
|
|
868
|
+
}
|
|
869
|
+
async function runDomainsConnect(input, deps) {
|
|
870
|
+
try {
|
|
871
|
+
await requireCredential({
|
|
872
|
+
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
873
|
+
env: deps.env,
|
|
874
|
+
store: deps.store
|
|
875
|
+
});
|
|
876
|
+
} catch {
|
|
877
|
+
return writeAuthError(deps);
|
|
878
|
+
}
|
|
879
|
+
const result = await deps.client.domains.connect(input);
|
|
880
|
+
if (result.error) return writeApiError(deps, result.error);
|
|
881
|
+
const domain = result.data;
|
|
882
|
+
if (deps.outputMode === "human") {
|
|
883
|
+
deps.stdout(
|
|
884
|
+
`${success(`Connected ${domain.hostname} (${domain.mode} mode)`)}
|
|
885
|
+
`
|
|
886
|
+
);
|
|
887
|
+
if (domain.dns.length > 0) {
|
|
888
|
+
deps.stdout("\nDNS records to create at your provider:\n\n");
|
|
889
|
+
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
890
|
+
`);
|
|
891
|
+
}
|
|
892
|
+
printNextHints(deps, domain.next);
|
|
893
|
+
} else {
|
|
894
|
+
writeJson(deps, { ok: true, domain });
|
|
895
|
+
}
|
|
896
|
+
return { exitCode: 0 };
|
|
897
|
+
}
|
|
898
|
+
async function runDomainsList(_input, deps) {
|
|
899
|
+
try {
|
|
900
|
+
await requireCredential({
|
|
901
|
+
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
902
|
+
env: deps.env,
|
|
903
|
+
store: deps.store
|
|
904
|
+
});
|
|
905
|
+
} catch {
|
|
906
|
+
return writeAuthError(deps);
|
|
907
|
+
}
|
|
908
|
+
const result = await deps.client.domains.list();
|
|
909
|
+
if (result.error) return writeApiError(deps, result.error);
|
|
910
|
+
const domains = result.data?.domains ?? [];
|
|
911
|
+
if (deps.outputMode === "human") {
|
|
912
|
+
if (domains.length === 0) {
|
|
913
|
+
writeEmpty(deps, "No domains connected yet.", { ok: true, domains: [] });
|
|
914
|
+
} else {
|
|
915
|
+
for (const d of domains) {
|
|
916
|
+
const defaultMark = d.default ? import_picocolors3.default.green(" (default)") : "";
|
|
917
|
+
const drop = d.dropId ? import_picocolors3.default.dim(` \u2192 ${d.dropId}`) : "";
|
|
918
|
+
deps.stdout(
|
|
919
|
+
` ${d.hostname}${defaultMark} ${import_picocolors3.default.dim(d.mode)} ${import_picocolors3.default.dim(d.status)}${drop}
|
|
920
|
+
`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
} else {
|
|
925
|
+
writeJson(deps, { ok: true, domains });
|
|
926
|
+
}
|
|
927
|
+
return { exitCode: 0 };
|
|
928
|
+
}
|
|
929
|
+
async function runDomainsStatus(idOrHostname, _input, deps) {
|
|
930
|
+
try {
|
|
931
|
+
await requireCredential({
|
|
932
|
+
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
933
|
+
env: deps.env,
|
|
934
|
+
store: deps.store
|
|
935
|
+
});
|
|
936
|
+
} catch {
|
|
937
|
+
return writeAuthError(deps);
|
|
938
|
+
}
|
|
939
|
+
const result = await deps.client.domains.get(idOrHostname);
|
|
940
|
+
if (result.error) return writeApiError(deps, result.error);
|
|
941
|
+
const domain = result.data;
|
|
942
|
+
if (deps.outputMode === "human") {
|
|
943
|
+
deps.stdout(` ${import_picocolors3.default.dim("Hostname:")} ${domain.hostname}
|
|
944
|
+
`);
|
|
945
|
+
deps.stdout(` ${import_picocolors3.default.dim("Mode: ")} ${domain.mode}
|
|
946
|
+
`);
|
|
947
|
+
deps.stdout(` ${import_picocolors3.default.dim("Status: ")} ${domain.status}
|
|
948
|
+
`);
|
|
949
|
+
if (domain.failureReason) {
|
|
950
|
+
deps.stdout(
|
|
951
|
+
` ${import_picocolors3.default.dim("Reason: ")} ${import_picocolors3.default.red(domain.failureReason)}
|
|
952
|
+
`
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
if (domain.dropId) {
|
|
956
|
+
deps.stdout(` ${import_picocolors3.default.dim("Drop: ")} ${domain.dropId}
|
|
957
|
+
`);
|
|
958
|
+
}
|
|
959
|
+
if (domain.verifiedAt) {
|
|
960
|
+
deps.stdout(` ${import_picocolors3.default.dim("Verified:")} ${domain.verifiedAt}
|
|
961
|
+
`);
|
|
962
|
+
}
|
|
963
|
+
deps.stdout(` ${import_picocolors3.default.dim("Created: ")} ${domain.createdAt}
|
|
964
|
+
`);
|
|
965
|
+
if (domain.dns.length > 0) {
|
|
966
|
+
deps.stdout("\nDNS records:\n\n");
|
|
967
|
+
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
968
|
+
`);
|
|
969
|
+
}
|
|
970
|
+
printNextHints(deps, domain.next);
|
|
971
|
+
} else {
|
|
972
|
+
writeJson(deps, { ok: true, domain });
|
|
973
|
+
}
|
|
974
|
+
return { exitCode: 0 };
|
|
975
|
+
}
|
|
976
|
+
var defaultSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
977
|
+
async function runDomainsVerify(idOrHostname, input, deps, sleep = defaultSleep) {
|
|
978
|
+
try {
|
|
979
|
+
await requireCredential({
|
|
980
|
+
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
981
|
+
env: deps.env,
|
|
982
|
+
store: deps.store
|
|
983
|
+
});
|
|
984
|
+
} catch {
|
|
985
|
+
return writeAuthError(deps);
|
|
986
|
+
}
|
|
987
|
+
const timeoutMs = (input.timeout ?? 300) * 1e3;
|
|
988
|
+
const doVerify = async (elapsedMs) => {
|
|
989
|
+
const result = await deps.client.domains.verify(idOrHostname);
|
|
990
|
+
if (result.error) return writeApiError(deps, result.error);
|
|
991
|
+
const domain = result.data;
|
|
992
|
+
if (!input.wait) {
|
|
993
|
+
if (deps.outputMode === "human") {
|
|
994
|
+
deps.stdout(
|
|
995
|
+
`${domain.status === "live" ? success(`${idOrHostname} is live`) : warn(`${idOrHostname}: ${domain.status}`)}
|
|
996
|
+
`
|
|
997
|
+
);
|
|
998
|
+
if (domain.dns.length > 0) {
|
|
999
|
+
deps.stdout("\n");
|
|
1000
|
+
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
1001
|
+
`);
|
|
1002
|
+
}
|
|
1003
|
+
printNextHints(deps, domain.next);
|
|
1004
|
+
} else {
|
|
1005
|
+
writeJson(deps, { ok: true, domain });
|
|
1006
|
+
}
|
|
1007
|
+
return {
|
|
1008
|
+
exitCode: domain.status === "live" ? 0 : domain.status === "failed" ? 1 : 6
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
if (domain.status === "live" || domain.status === "failed") {
|
|
1012
|
+
if (deps.outputMode === "human") {
|
|
1013
|
+
deps.stdout(
|
|
1014
|
+
`${domain.status === "live" ? success(`${idOrHostname} is live`) : warn(`${idOrHostname}: ${domain.status}`)}
|
|
1015
|
+
`
|
|
1016
|
+
);
|
|
1017
|
+
if (domain.dns.length > 0) {
|
|
1018
|
+
deps.stdout("\n");
|
|
1019
|
+
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
1020
|
+
`);
|
|
1021
|
+
}
|
|
1022
|
+
} else {
|
|
1023
|
+
writeJson(deps, { ok: true, domain });
|
|
1024
|
+
}
|
|
1025
|
+
return { exitCode: domain.status === "live" ? 0 : 1 };
|
|
1026
|
+
}
|
|
1027
|
+
const sleepMs = computeSleepMs(domain.dns, elapsedMs, timeoutMs);
|
|
1028
|
+
const newElapsed = elapsedMs + sleepMs;
|
|
1029
|
+
if (newElapsed >= timeoutMs) {
|
|
1030
|
+
const timeoutSecs = input.timeout ?? 300;
|
|
1031
|
+
const message = `Timed out waiting for ${idOrHostname} to go live after ${timeoutSecs}s`;
|
|
1032
|
+
if (deps.outputMode === "human") {
|
|
1033
|
+
deps.stderr(
|
|
1034
|
+
` ${import_picocolors3.default.red("Timed out waiting for")} ${idOrHostname} ${import_picocolors3.default.red("to go live")}
|
|
1035
|
+
`
|
|
1036
|
+
);
|
|
1037
|
+
} else {
|
|
1038
|
+
deps.stderr(
|
|
1039
|
+
`${JSON.stringify(errorEnvelope("verify_timeout", message))}
|
|
1040
|
+
`
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
return { exitCode: 7 };
|
|
1044
|
+
}
|
|
1045
|
+
if (deps.outputMode === "human" && domain.dns.length > 0) {
|
|
1046
|
+
deps.stdout(`${formatDnsTable(domain.dns)}
|
|
1047
|
+
`);
|
|
1048
|
+
}
|
|
1049
|
+
await sleep(sleepMs);
|
|
1050
|
+
return doVerify(newElapsed);
|
|
1051
|
+
};
|
|
1052
|
+
return doVerify(0);
|
|
1053
|
+
}
|
|
1054
|
+
async function runDomainsUpdate(idOrHostname, input, deps) {
|
|
1055
|
+
if (input.drop === void 0 && input.setDefault === void 0) {
|
|
1056
|
+
return writeError(
|
|
1057
|
+
deps,
|
|
1058
|
+
"invalid_usage",
|
|
1059
|
+
"Provide at least one option: --drop <dropId> or --default"
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
try {
|
|
1063
|
+
await requireCredential({
|
|
1064
|
+
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
1065
|
+
env: deps.env,
|
|
1066
|
+
store: deps.store
|
|
1067
|
+
});
|
|
1068
|
+
} catch {
|
|
1069
|
+
return writeAuthError(deps);
|
|
1070
|
+
}
|
|
1071
|
+
const updateInput = {};
|
|
1072
|
+
if (input.drop !== void 0) updateInput.dropId = input.drop;
|
|
1073
|
+
if (input.setDefault !== void 0) updateInput.default = input.setDefault;
|
|
1074
|
+
const result = await deps.client.domains.update(idOrHostname, updateInput);
|
|
1075
|
+
if (result.error) return writeApiError(deps, result.error);
|
|
1076
|
+
const domain = result.data;
|
|
1077
|
+
writeHumanOrJson(deps, `${success(`Updated ${domain.hostname}`)}
|
|
1078
|
+
`, {
|
|
1079
|
+
ok: true,
|
|
1080
|
+
domain
|
|
1081
|
+
});
|
|
1082
|
+
return { exitCode: 0 };
|
|
1083
|
+
}
|
|
1084
|
+
async function runDomainsRemove(idOrHostname, _input, deps) {
|
|
1085
|
+
try {
|
|
1086
|
+
await requireCredential({
|
|
1087
|
+
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
1088
|
+
env: deps.env,
|
|
1089
|
+
store: deps.store
|
|
1090
|
+
});
|
|
1091
|
+
} catch {
|
|
1092
|
+
return writeAuthError(deps);
|
|
1093
|
+
}
|
|
1094
|
+
const result = await deps.client.domains.delete(idOrHostname);
|
|
1095
|
+
if (result.error) return writeApiError(deps, result.error);
|
|
1096
|
+
const deleted = result.data;
|
|
1097
|
+
if (deps.outputMode === "human") {
|
|
1098
|
+
deps.stdout(`${success(`Removed ${deleted.hostname}`)}
|
|
1099
|
+
`);
|
|
1100
|
+
if (deleted.warning) {
|
|
1101
|
+
deps.stderr(`${warn(deleted.warning)}
|
|
1102
|
+
`);
|
|
1103
|
+
}
|
|
1104
|
+
} else {
|
|
1105
|
+
writeJson(deps, {
|
|
1106
|
+
ok: true,
|
|
1107
|
+
deleted: true,
|
|
1108
|
+
id: deleted.id,
|
|
1109
|
+
hostname: deleted.hostname,
|
|
1110
|
+
warning: deleted.warning
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
return { exitCode: 0 };
|
|
1114
|
+
}
|
|
1115
|
+
|
|
785
1116
|
// src/commands/drops.ts
|
|
786
1117
|
var prompts2 = __toESM(require("@clack/prompts"), 1);
|
|
787
1118
|
async function runDropsList(input, deps) {
|
|
@@ -1119,7 +1450,9 @@ async function parseDropOptions(raw) {
|
|
|
1119
1450
|
...raw.entry ? { entry: raw.entry } : {},
|
|
1120
1451
|
...raw.contentType ? { contentType: raw.contentType } : {},
|
|
1121
1452
|
...raw.path ? { path: raw.path } : {},
|
|
1122
|
-
...raw.idempotencyKey ? { idempotencyKey: raw.idempotencyKey } : {}
|
|
1453
|
+
...raw.idempotencyKey ? { idempotencyKey: raw.idempotencyKey } : {},
|
|
1454
|
+
...raw.domain ? { domain: raw.domain } : {},
|
|
1455
|
+
...raw.slug ? { slug: raw.slug } : {}
|
|
1123
1456
|
};
|
|
1124
1457
|
}
|
|
1125
1458
|
function parseJsonObject(value, label) {
|
|
@@ -1131,12 +1464,12 @@ function parseJsonObject(value, label) {
|
|
|
1131
1464
|
}
|
|
1132
1465
|
|
|
1133
1466
|
// src/spinner.ts
|
|
1134
|
-
var
|
|
1467
|
+
var import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
1135
1468
|
var frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1136
1469
|
function createSpinner(message, stderr) {
|
|
1137
1470
|
let i = 0;
|
|
1138
1471
|
const timer = setInterval(() => {
|
|
1139
|
-
stderr(`\r\x1B[2K${
|
|
1472
|
+
stderr(`\r\x1B[2K${import_picocolors4.default.cyan(frames[i % frames.length])} ${message}`);
|
|
1140
1473
|
i++;
|
|
1141
1474
|
}, 80);
|
|
1142
1475
|
return {
|
|
@@ -1274,6 +1607,110 @@ async function handlePublishDryRun(input, raw, deps, client) {
|
|
|
1274
1607
|
}
|
|
1275
1608
|
}
|
|
1276
1609
|
|
|
1610
|
+
// src/commands/pull.ts
|
|
1611
|
+
var import_promises2 = require("fs/promises");
|
|
1612
|
+
var import_node_path = require("path");
|
|
1613
|
+
async function runPull(target, input, deps) {
|
|
1614
|
+
try {
|
|
1615
|
+
await requireCredential({
|
|
1616
|
+
...deps.apiKey ? { apiKey: deps.apiKey } : {},
|
|
1617
|
+
env: deps.env,
|
|
1618
|
+
store: deps.store
|
|
1619
|
+
});
|
|
1620
|
+
} catch {
|
|
1621
|
+
return writeAuthError(deps);
|
|
1622
|
+
}
|
|
1623
|
+
let dropId;
|
|
1624
|
+
let defaultDirName;
|
|
1625
|
+
if (target.startsWith("drop_")) {
|
|
1626
|
+
dropId = target;
|
|
1627
|
+
defaultDirName = target;
|
|
1628
|
+
} else {
|
|
1629
|
+
const resolved = await deps.client.drops.resolve(target);
|
|
1630
|
+
if (resolved.error) return writeApiError(deps, resolved.error);
|
|
1631
|
+
if (resolved.data === null) {
|
|
1632
|
+
return writeApiError(deps, {
|
|
1633
|
+
code: "not_found",
|
|
1634
|
+
message: `No drop matching "${target}" found in your account.`,
|
|
1635
|
+
suggestion: "It may belong to another account or have been deleted. Run dropthis list to see your drops, or pass the full drop_\u2026 id."
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
dropId = resolved.data.id;
|
|
1639
|
+
defaultDirName = resolved.data.slug || resolved.data.id;
|
|
1640
|
+
}
|
|
1641
|
+
const spin = shouldSpin(deps.outputMode) ? createSpinner("Pulling content\u2026", deps.stderr) : void 0;
|
|
1642
|
+
const manifestResult = await deps.client.drops.getContent(dropId);
|
|
1643
|
+
if (manifestResult.error) {
|
|
1644
|
+
spin?.fail();
|
|
1645
|
+
return writeApiError(deps, manifestResult.error);
|
|
1646
|
+
}
|
|
1647
|
+
const manifest = manifestResult.data;
|
|
1648
|
+
const outDir = (0, import_node_path.resolve)(
|
|
1649
|
+
deps.cwd ?? process.cwd(),
|
|
1650
|
+
input.output ?? defaultDirName
|
|
1651
|
+
);
|
|
1652
|
+
let destinations;
|
|
1653
|
+
try {
|
|
1654
|
+
destinations = manifest.files.map((file) => ({
|
|
1655
|
+
path: file.path,
|
|
1656
|
+
dest: safeDestination(outDir, file.path)
|
|
1657
|
+
}));
|
|
1658
|
+
} catch (error2) {
|
|
1659
|
+
spin?.fail();
|
|
1660
|
+
const message = error2 instanceof Error ? error2.message : "Invalid manifest path.";
|
|
1661
|
+
return writeError(deps, "local_input_error", message);
|
|
1662
|
+
}
|
|
1663
|
+
try {
|
|
1664
|
+
await (0, import_promises2.mkdir)(outDir, { recursive: true });
|
|
1665
|
+
for (const { path, dest } of destinations) {
|
|
1666
|
+
const fileResult = await deps.client.drops.getContent(dropId, { path });
|
|
1667
|
+
if (fileResult.error) {
|
|
1668
|
+
spin?.fail();
|
|
1669
|
+
return writeApiError(deps, {
|
|
1670
|
+
...fileResult.error,
|
|
1671
|
+
message: `${path}: ${fileResult.error.message}`
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
const file = fileResult.data;
|
|
1675
|
+
await (0, import_promises2.mkdir)((0, import_node_path.dirname)(dest), { recursive: true });
|
|
1676
|
+
await (0, import_promises2.writeFile)(dest, file.bytes);
|
|
1677
|
+
}
|
|
1678
|
+
} catch (error2) {
|
|
1679
|
+
spin?.fail();
|
|
1680
|
+
const message = error2 instanceof Error ? error2.message : "Failed to write files.";
|
|
1681
|
+
return writeError(deps, "local_input_error", message);
|
|
1682
|
+
}
|
|
1683
|
+
spin?.stop();
|
|
1684
|
+
const count = manifest.files.length;
|
|
1685
|
+
writeHumanOrJson(
|
|
1686
|
+
deps,
|
|
1687
|
+
success(`Pulled ${count} ${count === 1 ? "file" : "files"} to ${outDir}`),
|
|
1688
|
+
{
|
|
1689
|
+
ok: true,
|
|
1690
|
+
pulled: {
|
|
1691
|
+
dropId: manifest.dropId,
|
|
1692
|
+
deploymentId: manifest.deploymentId,
|
|
1693
|
+
revision: manifest.revision,
|
|
1694
|
+
...manifest.entry !== void 0 ? { entry: manifest.entry } : {},
|
|
1695
|
+
dir: outDir,
|
|
1696
|
+
files: manifest.files
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
);
|
|
1700
|
+
return { exitCode: 0 };
|
|
1701
|
+
}
|
|
1702
|
+
function safeDestination(outDir, filePath) {
|
|
1703
|
+
if (!filePath || (0, import_node_path.isAbsolute)(filePath)) {
|
|
1704
|
+
throw new Error(`Unsafe file path in content manifest: "${filePath}"`);
|
|
1705
|
+
}
|
|
1706
|
+
const dest = (0, import_node_path.join)(outDir, filePath);
|
|
1707
|
+
const rel = (0, import_node_path.relative)(outDir, dest);
|
|
1708
|
+
if (rel.startsWith("..") || (0, import_node_path.isAbsolute)(rel)) {
|
|
1709
|
+
throw new Error(`Unsafe file path in content manifest: "${filePath}"`);
|
|
1710
|
+
}
|
|
1711
|
+
return dest;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1277
1714
|
// src/commands/update-content.ts
|
|
1278
1715
|
var NO_CONTENT_MESSAGE = "update-content requires content. Provide a file, folder, URL, or text to ship a new deployment.";
|
|
1279
1716
|
var NO_CONTENT_NEXT_ACTION = "Pass an input, e.g. dropthis update-content <dropId> ./dist. To change title, visibility, password, expiry, or metadata, use dropthis update-settings <dropId> instead.";
|
|
@@ -1691,9 +2128,9 @@ function createContext(input) {
|
|
|
1691
2128
|
}
|
|
1692
2129
|
|
|
1693
2130
|
// src/storage.ts
|
|
1694
|
-
var
|
|
2131
|
+
var import_promises3 = require("fs/promises");
|
|
1695
2132
|
var import_node_os = require("os");
|
|
1696
|
-
var
|
|
2133
|
+
var import_node_path2 = require("path");
|
|
1697
2134
|
var import_keyring = require("@napi-rs/keyring");
|
|
1698
2135
|
var MemoryCredentialStore = class {
|
|
1699
2136
|
credential = null;
|
|
@@ -1725,7 +2162,7 @@ var InsecureFileCredentialStore = class {
|
|
|
1725
2162
|
});
|
|
1726
2163
|
}
|
|
1727
2164
|
async clear() {
|
|
1728
|
-
await (0,
|
|
2165
|
+
await (0, import_promises3.rm)(this.filePath, { force: true });
|
|
1729
2166
|
}
|
|
1730
2167
|
};
|
|
1731
2168
|
var KeyringCredentialStore = class {
|
|
@@ -1752,7 +2189,7 @@ var KeyringCredentialStore = class {
|
|
|
1752
2189
|
}
|
|
1753
2190
|
async clear() {
|
|
1754
2191
|
await this.keyring.deletePassword();
|
|
1755
|
-
await (0,
|
|
2192
|
+
await (0, import_promises3.rm)(this.filePath, { force: true });
|
|
1756
2193
|
}
|
|
1757
2194
|
};
|
|
1758
2195
|
function createCredentialStore(options = {}) {
|
|
@@ -1770,14 +2207,14 @@ function defaultKeyringAdapter() {
|
|
|
1770
2207
|
};
|
|
1771
2208
|
}
|
|
1772
2209
|
function credentialPath(configDir) {
|
|
1773
|
-
return (0,
|
|
1774
|
-
configDir ?? (0,
|
|
2210
|
+
return (0, import_node_path2.join)(
|
|
2211
|
+
configDir ?? (0, import_node_path2.join)((0, import_node_os.homedir)(), ".config", "dropthis"),
|
|
1775
2212
|
"credentials.json"
|
|
1776
2213
|
);
|
|
1777
2214
|
}
|
|
1778
2215
|
async function readJsonFile(path) {
|
|
1779
2216
|
try {
|
|
1780
|
-
return JSON.parse(await (0,
|
|
2217
|
+
return JSON.parse(await (0, import_promises3.readFile)(path, "utf8"));
|
|
1781
2218
|
} catch (error2) {
|
|
1782
2219
|
if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
|
|
1783
2220
|
return null;
|
|
@@ -1786,14 +2223,14 @@ async function readJsonFile(path) {
|
|
|
1786
2223
|
}
|
|
1787
2224
|
}
|
|
1788
2225
|
async function writeCredentialFile(path, credential) {
|
|
1789
|
-
await (0,
|
|
2226
|
+
await (0, import_promises3.mkdir)((0, import_node_path2.dirname)(path), { recursive: true });
|
|
1790
2227
|
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
1791
|
-
await (0,
|
|
2228
|
+
await (0, import_promises3.writeFile)(tmpPath, `${JSON.stringify(credential, null, 2)}
|
|
1792
2229
|
`, {
|
|
1793
2230
|
mode: 384
|
|
1794
2231
|
});
|
|
1795
|
-
await (0,
|
|
1796
|
-
await (0,
|
|
2232
|
+
await (0, import_promises3.chmod)(tmpPath, 384);
|
|
2233
|
+
await (0, import_promises3.rename)(tmpPath, path);
|
|
1797
2234
|
}
|
|
1798
2235
|
|
|
1799
2236
|
// src/program.ts
|
|
@@ -1816,13 +2253,13 @@ function buildProgram(options = {}) {
|
|
|
1816
2253
|
});
|
|
1817
2254
|
program.name("dropthis").description(
|
|
1818
2255
|
[
|
|
1819
|
-
|
|
2256
|
+
import_picocolors5.default.dim("Publish anything online and get a URL back."),
|
|
1820
2257
|
"",
|
|
1821
|
-
`${
|
|
1822
|
-
` ${
|
|
1823
|
-
` ${
|
|
2258
|
+
`${import_picocolors5.default.bold("Quick start:")}`,
|
|
2259
|
+
` ${import_picocolors5.default.cyan("dropthis login")}`,
|
|
2260
|
+
` ${import_picocolors5.default.cyan("dropthis publish ./dist")}`
|
|
1824
2261
|
].join("\n")
|
|
1825
|
-
).version("0.
|
|
2262
|
+
).version("0.11.0").configureHelp({
|
|
1826
2263
|
subcommandTerm(cmd) {
|
|
1827
2264
|
const args = cmd.registeredArguments.map((a) => {
|
|
1828
2265
|
const name = a.name();
|
|
@@ -1831,10 +2268,10 @@ function buildProgram(options = {}) {
|
|
|
1831
2268
|
}).join(" ");
|
|
1832
2269
|
return args ? `${cmd.name()} ${args}` : cmd.name();
|
|
1833
2270
|
},
|
|
1834
|
-
styleTitle: (title) =>
|
|
1835
|
-
styleCommandText: (str) =>
|
|
1836
|
-
styleSubcommandText: (str) =>
|
|
1837
|
-
styleOptionText: (str) =>
|
|
2271
|
+
styleTitle: (title) => import_picocolors5.default.bold(title),
|
|
2272
|
+
styleCommandText: (str) => import_picocolors5.default.cyan(str),
|
|
2273
|
+
styleSubcommandText: (str) => import_picocolors5.default.cyan(str),
|
|
2274
|
+
styleOptionText: (str) => import_picocolors5.default.yellow(str)
|
|
1838
2275
|
});
|
|
1839
2276
|
program.option("--api-key <key>", "Override API key for this invocation");
|
|
1840
2277
|
program.option("--api-url <url>", "Override API base URL");
|
|
@@ -1852,7 +2289,13 @@ function buildProgram(options = {}) {
|
|
|
1852
2289
|
).option(
|
|
1853
2290
|
"--metadata <json>",
|
|
1854
2291
|
`Attach JSON key-value pairs, e.g. '{"source":"ci"}'`
|
|
1855
|
-
).option("--metadata-file <path>", "Read metadata JSON from a file").option("--path <name>", "Set filename when publishing from stdin").option(
|
|
2292
|
+
).option("--metadata-file <path>", "Read metadata JSON from a file").option("--path <name>", "Set filename when publishing from stdin").option(
|
|
2293
|
+
"--domain <hostname>",
|
|
2294
|
+
"Serve this drop under a connected custom domain (must be live \u2014 see: dropthis domains list). Path-mode: drop lives at https://domain/{slug}/. Dedicated: drop is at the root (409 if occupied). Omit to use the account's default path domain."
|
|
2295
|
+
).option(
|
|
2296
|
+
"--slug <vanity-slug>",
|
|
2297
|
+
"Vanity slug for path-mode custom domains (1\u201363 lowercase letters/digits/hyphens). Auto-suffixed if taken. Only valid with --domain on a path-mode domain."
|
|
2298
|
+
).option("--url", "Print only the published URL (no JSON envelope)").option(
|
|
1856
2299
|
"--dry-run",
|
|
1857
2300
|
"Show what would be published without publishing (JSON; cannot combine with --url)"
|
|
1858
2301
|
).option("--json", "Force JSON output").option(
|
|
@@ -1984,6 +2427,20 @@ function buildProgram(options = {}) {
|
|
|
1984
2427
|
process.exitCode = result.exitCode;
|
|
1985
2428
|
}
|
|
1986
2429
|
);
|
|
2430
|
+
program.command("pull <target>").description(
|
|
2431
|
+
"Download a drop's files into a local directory (accepts the drop_\u2026 id, the drop URL, or its slug).\nFetches the current deployment's file manifest and writes every file. Pull, edit, then update-content to ship the changes back to the same URL \u2014 also the rollback path."
|
|
2432
|
+
).option("-o, --output <dir>", "Output directory (default: ./<slug-or-id>)").option("--json", "Force JSON output").action(
|
|
2433
|
+
async (target, commandOptions) => {
|
|
2434
|
+
const deps = await commandDeps(
|
|
2435
|
+
program,
|
|
2436
|
+
options,
|
|
2437
|
+
store,
|
|
2438
|
+
commandOptions
|
|
2439
|
+
);
|
|
2440
|
+
const result = await runPull(target, commandOptions, deps);
|
|
2441
|
+
process.exitCode = result.exitCode;
|
|
2442
|
+
}
|
|
2443
|
+
);
|
|
1987
2444
|
program.command("get <dropId>").description("Show drop details (pass the full drop_\u2026 id)").option("--json", "Force JSON output").action(async (dropId, commandOptions) => {
|
|
1988
2445
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1989
2446
|
const result = await runDropsGet(dropId, commandOptions, deps);
|
|
@@ -2032,6 +2489,76 @@ function buildProgram(options = {}) {
|
|
|
2032
2489
|
process.exitCode = result.exitCode;
|
|
2033
2490
|
}
|
|
2034
2491
|
);
|
|
2492
|
+
const domains = program.command("domains").description(
|
|
2493
|
+
"Manage custom domains. Loop: connect \u2192 create the CNAME at your DNS provider \u2192 verify --wait \u2192 publish --domain"
|
|
2494
|
+
);
|
|
2495
|
+
domains.command("connect <hostname>").description(
|
|
2496
|
+
"Connect a custom domain. Returns DNS records to create at your provider.\nAfter adding them: dropthis domains verify <hostname> --wait"
|
|
2497
|
+
).requiredOption(
|
|
2498
|
+
"--mode <mode>",
|
|
2499
|
+
"Mount mode: path (many drops at hostname/{slug}/) or dedicated (one drop at root)"
|
|
2500
|
+
).option("--json", "Force JSON output").action(
|
|
2501
|
+
async (hostname, commandOptions) => {
|
|
2502
|
+
if (commandOptions.mode !== "path" && commandOptions.mode !== "dedicated") {
|
|
2503
|
+
process.stderr.write(`error: --mode must be "path" or "dedicated"
|
|
2504
|
+
`);
|
|
2505
|
+
process.exitCode = 2;
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2509
|
+
const result = await runDomainsConnect(
|
|
2510
|
+
{ hostname, mode: commandOptions.mode },
|
|
2511
|
+
deps
|
|
2512
|
+
);
|
|
2513
|
+
process.exitCode = result.exitCode;
|
|
2514
|
+
}
|
|
2515
|
+
);
|
|
2516
|
+
domains.command("list").description("List connected custom domains").option("--json", "Force JSON output").action(async (commandOptions) => {
|
|
2517
|
+
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2518
|
+
const result = await runDomainsList(commandOptions, deps);
|
|
2519
|
+
process.exitCode = result.exitCode;
|
|
2520
|
+
});
|
|
2521
|
+
domains.command("status <hostname>").description("Show domain details and DNS record status").option("--json", "Force JSON output").action(async (hostname, commandOptions) => {
|
|
2522
|
+
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2523
|
+
const result = await runDomainsStatus(hostname, commandOptions, deps);
|
|
2524
|
+
process.exitCode = result.exitCode;
|
|
2525
|
+
});
|
|
2526
|
+
domains.command("verify <hostname>").description(
|
|
2527
|
+
"Trigger a DNS verification check. With --wait, polls until live or failed.\nUse after adding the DNS records from: dropthis domains connect"
|
|
2528
|
+
).option("--wait", "Poll until live (or failed/timeout)").option(
|
|
2529
|
+
"--timeout <secs>",
|
|
2530
|
+
"Max seconds to wait (default: 300)",
|
|
2531
|
+
parseInteger
|
|
2532
|
+
).option("--json", "Force JSON output").action(
|
|
2533
|
+
async (hostname, commandOptions) => {
|
|
2534
|
+
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2535
|
+
const result = await runDomainsVerify(hostname, commandOptions, deps);
|
|
2536
|
+
process.exitCode = result.exitCode;
|
|
2537
|
+
}
|
|
2538
|
+
);
|
|
2539
|
+
domains.command("update <hostname>").description(
|
|
2540
|
+
"Update a domain \u2014 repoint to a different drop (dedicated mode) or set/clear as account default (path mode). At least one option required."
|
|
2541
|
+
).option("--drop <dropId>", "Repoint to a different drop (dedicated mode)").option("--default", "Set as account default publish domain (path mode)").option("--json", "Force JSON output").action(
|
|
2542
|
+
async (hostname, commandOptions) => {
|
|
2543
|
+
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2544
|
+
const result = await runDomainsUpdate(
|
|
2545
|
+
hostname,
|
|
2546
|
+
{
|
|
2547
|
+
...commandOptions.drop !== void 0 ? { drop: commandOptions.drop } : {},
|
|
2548
|
+
...commandOptions.default !== void 0 ? { setDefault: commandOptions.default } : {}
|
|
2549
|
+
},
|
|
2550
|
+
deps
|
|
2551
|
+
);
|
|
2552
|
+
process.exitCode = result.exitCode;
|
|
2553
|
+
}
|
|
2554
|
+
);
|
|
2555
|
+
domains.command("remove <hostname>").description(
|
|
2556
|
+
"Remove a custom domain. IMPORTANT: also delete the DNS CNAME at your provider to prevent DNS hijacking."
|
|
2557
|
+
).option("--json", "Force JSON output").action(async (hostname, commandOptions) => {
|
|
2558
|
+
const deps = await commandDeps(program, options, store, commandOptions);
|
|
2559
|
+
const result = await runDomainsRemove(hostname, commandOptions, deps);
|
|
2560
|
+
process.exitCode = result.exitCode;
|
|
2561
|
+
});
|
|
2035
2562
|
const apiKeys = program.command("api-keys").description("Manage API keys");
|
|
2036
2563
|
apiKeys.command("create").description("Create an API key").option("--label <label>", "API key label", "CLI").option("--json", "Force JSON output").action(async (commandOptions) => {
|
|
2037
2564
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
@@ -2270,6 +2797,8 @@ function toDropOptions(options) {
|
|
|
2270
2797
|
...options.contentType ? { contentType: options.contentType } : {},
|
|
2271
2798
|
...options.path ? { path: options.path } : {},
|
|
2272
2799
|
...options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
|
|
2800
|
+
...options.domain ? { domain: options.domain } : {},
|
|
2801
|
+
...options.slug ? { slug: options.slug } : {},
|
|
2273
2802
|
...options.json !== void 0 ? { json: options.json } : {},
|
|
2274
2803
|
...options.quiet !== void 0 ? { quiet: options.quiet } : {},
|
|
2275
2804
|
...options.url !== void 0 ? { url: options.url } : {},
|