drop2run 0.8.0 → 0.9.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 +20 -0
- package/dist/index.js +141 -4
- package/dist/{package-BycuI1VJ.js → package-LdzYr7IV.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,9 @@ drop2run pause [site] Take a site off the air, keeping it
|
|
|
29
29
|
drop2run resume [site] Put a paused site back on the air
|
|
30
30
|
drop2run rollback <deployId> [--site X] Put an earlier version back live
|
|
31
31
|
drop2run rm <site> --yes Delete a site and everything on it
|
|
32
|
+
drop2run comments [site] Read the comments left on a site's pages
|
|
33
|
+
drop2run comments reply <thread> <text> Answer a thread as the owner [--resolve]
|
|
34
|
+
drop2run comments resolve|reopen <thread> Resolve a thread, or open it again
|
|
32
35
|
drop2run token list List your access tokens
|
|
33
36
|
drop2run whoami Check the token and whose it is
|
|
34
37
|
drop2run where Show which token source is in use
|
|
@@ -37,6 +40,23 @@ drop2run --version Print the version
|
|
|
37
40
|
|
|
38
41
|
`--json` on any command prints machine-readable output instead of text.
|
|
39
42
|
|
|
43
|
+
## Comments
|
|
44
|
+
|
|
45
|
+
On a plan that includes comments, people viewing a site can pin comments to its pages. `drop2run comments`
|
|
46
|
+
reads them — each thread's id, page, the words it points at, and every comment — with `--status
|
|
47
|
+
open|resolved|all` and `--path /page` to narrow it. The owner of the account can answer from here:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
drop2run comments calm-cedar # open threads
|
|
51
|
+
drop2run deploy dist # publish the fix first
|
|
52
|
+
drop2run comments reply 01JTHREAD... "Fixed — please check" --site calm-cedar
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The reply shows on the page under your name, as one typed there would, and the people in the thread are
|
|
56
|
+
emailed.
|
|
57
|
+
It leaves the thread open unless `--resolve` is given, so the person who raised it can check the fix and
|
|
58
|
+
resolve it themselves. `comments resolve` and `comments reopen` change a thread's state on its own.
|
|
59
|
+
|
|
40
60
|
## Which site a command acts on
|
|
41
61
|
|
|
42
62
|
`--subdomain` creates one under the name you give; otherwise `--site`, then `drop2run.json`, then a new
|
package/dist/index.js
CHANGED
|
@@ -344,6 +344,33 @@ const ROOT_FOLDER = "root";
|
|
|
344
344
|
async function resolveFolderTarget(credentials, folder) {
|
|
345
345
|
return folder.trim().toLowerCase() === ROOT_FOLDER ? ROOT_FOLDER : await resolveFolder(credentials, folder);
|
|
346
346
|
}
|
|
347
|
+
async function listComments(credentials, siteId, query = {}) {
|
|
348
|
+
const search = new URLSearchParams();
|
|
349
|
+
if (query.status !== void 0) search.set("status", query.status);
|
|
350
|
+
if (query.path !== void 0) search.set("path", query.path);
|
|
351
|
+
if (query.since !== void 0) search.set("since", String(query.since));
|
|
352
|
+
if (query.page !== void 0) search.set("page", String(query.page));
|
|
353
|
+
const encoded = search.toString();
|
|
354
|
+
const suffix = encoded === "" ? "" : `?${encoded}`;
|
|
355
|
+
return await call(
|
|
356
|
+
credentials,
|
|
357
|
+
`sites/${encodeURIComponent(siteId)}/feedback/threads${suffix}`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
async function replyToComment(credentials, siteId, threadId, body, resolve2 = false) {
|
|
361
|
+
return await call(
|
|
362
|
+
credentials,
|
|
363
|
+
`sites/${encodeURIComponent(siteId)}/feedback/threads/${encodeURIComponent(threadId)}/comments`,
|
|
364
|
+
{ method: "POST", body: JSON.stringify({ body, resolve: resolve2 }) }
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
async function setCommentResolved(credentials, siteId, threadId, resolved) {
|
|
368
|
+
return await call(
|
|
369
|
+
credentials,
|
|
370
|
+
`sites/${encodeURIComponent(siteId)}/feedback/threads/${encodeURIComponent(threadId)}/${resolved ? "resolve" : "reopen"}`,
|
|
371
|
+
{ method: "POST" }
|
|
372
|
+
);
|
|
373
|
+
}
|
|
347
374
|
const VERSIONS_LISTED = 10;
|
|
348
375
|
function servingModeOf$1(site) {
|
|
349
376
|
if (site.spaMode) return "spa";
|
|
@@ -2651,6 +2678,96 @@ async function tokens() {
|
|
|
2651
2678
|
return failure(error instanceof Error ? error.message : String(error));
|
|
2652
2679
|
}
|
|
2653
2680
|
}
|
|
2681
|
+
const COMMENTS_USAGE = [
|
|
2682
|
+
" drop2run comments [site] [--status open|resolved|all] [--path /page]",
|
|
2683
|
+
" drop2run comments reply <thread> <text> [--resolve] [--site X]",
|
|
2684
|
+
" drop2run comments resolve|reopen <thread> [--site X]"
|
|
2685
|
+
].join("\n");
|
|
2686
|
+
async function comments(subcommand, rest, site, options) {
|
|
2687
|
+
const found = credentialsOr();
|
|
2688
|
+
if ("result" in found) return found.result;
|
|
2689
|
+
const acting = subcommand === "reply" || subcommand === "resolve" || subcommand === "reopen";
|
|
2690
|
+
if (!acting) {
|
|
2691
|
+
const status = options.status ?? "open";
|
|
2692
|
+
if (status !== "open" && status !== "resolved" && status !== "all") {
|
|
2693
|
+
return failure(
|
|
2694
|
+
`\`--status\` is open, resolved or all, not "${status}".
|
|
2695
|
+
|
|
2696
|
+
${COMMENTS_USAGE}`
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2699
|
+
const target2 = siteOr(subcommand ?? site, "drop2run comments <subdomain>");
|
|
2700
|
+
if ("result" in target2) return target2.result;
|
|
2701
|
+
try {
|
|
2702
|
+
const summary = await findSite(found.credentials, target2.named);
|
|
2703
|
+
const read = await listComments(found.credentials, summary.siteId, {
|
|
2704
|
+
status,
|
|
2705
|
+
path: options.path
|
|
2706
|
+
});
|
|
2707
|
+
return {
|
|
2708
|
+
text: describeComments(read.threads, status, summary.subdomain, read.more),
|
|
2709
|
+
json: read,
|
|
2710
|
+
code: 0
|
|
2711
|
+
};
|
|
2712
|
+
} catch (error) {
|
|
2713
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
const [thread, ...words] = rest;
|
|
2717
|
+
if (thread === void 0)
|
|
2718
|
+
return failure(`Name the thread, from \`drop2run comments\`.
|
|
2719
|
+
|
|
2720
|
+
${COMMENTS_USAGE}`);
|
|
2721
|
+
const body = words.join(" ").trim();
|
|
2722
|
+
if (subcommand === "reply" && body === "") {
|
|
2723
|
+
return failure(`Write the reply after the thread id.
|
|
2724
|
+
|
|
2725
|
+
${COMMENTS_USAGE}`);
|
|
2726
|
+
}
|
|
2727
|
+
const target = siteOr(site, `drop2run comments ${subcommand} <thread> --site <subdomain>`);
|
|
2728
|
+
if ("result" in target) return target.result;
|
|
2729
|
+
try {
|
|
2730
|
+
const summary = await findSite(found.credentials, target.named);
|
|
2731
|
+
const answered = subcommand === "reply" ? await replyToComment(
|
|
2732
|
+
found.credentials,
|
|
2733
|
+
summary.siteId,
|
|
2734
|
+
thread,
|
|
2735
|
+
body,
|
|
2736
|
+
options.resolve === true
|
|
2737
|
+
) : await setCommentResolved(
|
|
2738
|
+
found.credentials,
|
|
2739
|
+
summary.siteId,
|
|
2740
|
+
thread,
|
|
2741
|
+
subcommand === "resolve"
|
|
2742
|
+
);
|
|
2743
|
+
const done = subcommand === "reply" ? `Replied on ${answered.path}${answered.resolvedAt === null ? "" : " and resolved the thread"}.` : `${subcommand === "resolve" ? "Resolved" : "Reopened"} the thread on ${answered.path}.`;
|
|
2744
|
+
return { text: `${done}
|
|
2745
|
+
${answered.url}`, json: answered, code: 0 };
|
|
2746
|
+
} catch (error) {
|
|
2747
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
function describeComments(threads, status, subdomain, more) {
|
|
2751
|
+
const which = status === "all" ? "" : `${status} `;
|
|
2752
|
+
if (threads.length === 0) return `No ${which}comment threads on ${subdomain}.`;
|
|
2753
|
+
const heading = `${threads.length} ${which}comment ${threads.length === 1 ? "thread" : "threads"} on ${subdomain}` + (more ? " (more follow; `--json` shows the cursor)." : ".");
|
|
2754
|
+
const blocks = threads.map((thread) => {
|
|
2755
|
+
const where2 = thread.place.quote ?? (thread.place.snippet || null);
|
|
2756
|
+
const flags = [
|
|
2757
|
+
...thread.resolvedAt === null ? [] : ["resolved"],
|
|
2758
|
+
...thread.outdated ? ["earlier version"] : []
|
|
2759
|
+
];
|
|
2760
|
+
return [
|
|
2761
|
+
`${thread.id} ${thread.path}${where2 === null ? "" : ` "${where2}"`}` + (flags.length === 0 ? "" : ` (${flags.join(", ")})`),
|
|
2762
|
+
...thread.comments.map((comment) => {
|
|
2763
|
+
const who = comment.author.owner ? `${comment.author.name} (owner)` : comment.author.name;
|
|
2764
|
+
return ` ${who}: ${comment.body.replace(/\s*\n\s*/g, " ")}`;
|
|
2765
|
+
}),
|
|
2766
|
+
` ${thread.url}`
|
|
2767
|
+
].join("\n");
|
|
2768
|
+
});
|
|
2769
|
+
return [heading, ...blocks].join("\n\n");
|
|
2770
|
+
}
|
|
2654
2771
|
function day(iso) {
|
|
2655
2772
|
const parsed = new Date(iso);
|
|
2656
2773
|
return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString().slice(0, 10);
|
|
@@ -2691,6 +2808,9 @@ const HELP = `drop2run — publish a static site from the command line
|
|
|
2691
2808
|
drop2run resume [site] Put a paused site back on the air
|
|
2692
2809
|
drop2run rollback <deployId> [--site X] Put an earlier version back live
|
|
2693
2810
|
drop2run rm <site> --yes Delete a site and everything on it
|
|
2811
|
+
drop2run comments [site] Read the comments left on a site's pages
|
|
2812
|
+
drop2run comments reply <thread> <text> Answer a thread as the owner [--resolve]
|
|
2813
|
+
drop2run comments resolve|reopen <thread> Resolve a thread, or open it again
|
|
2694
2814
|
drop2run token list List your access tokens
|
|
2695
2815
|
drop2run whoami Check the token and whose it is
|
|
2696
2816
|
drop2run where Show which token source is in use
|
|
@@ -2706,6 +2826,16 @@ Flags
|
|
|
2706
2826
|
--yes Confirm a deletion, which cannot be undone
|
|
2707
2827
|
--then X What \`set expires\` does when the date arrives: pause
|
|
2708
2828
|
(the default, keeps the files) or delete (needs --yes)
|
|
2829
|
+
--status X Which comment threads: open (the default), resolved or all
|
|
2830
|
+
--path X Only the comments on one page, such as /pricing
|
|
2831
|
+
--resolve Resolve the thread with the reply (\`comments reply\` only)
|
|
2832
|
+
|
|
2833
|
+
Comments
|
|
2834
|
+
\`comments reply\`, \`resolve\` and \`reopen\` act on --site, else the site in
|
|
2835
|
+
drop2run.json, and only the account's owner can use them. Deploy the fix
|
|
2836
|
+
before replying that it is fixed. A reply leaves the thread open unless
|
|
2837
|
+
--resolve is given, so the person who raised it can check the fix and
|
|
2838
|
+
resolve it themselves.
|
|
2709
2839
|
|
|
2710
2840
|
Settings
|
|
2711
2841
|
set name <text> What the dashboard calls the site; put a
|
|
@@ -2749,7 +2879,7 @@ function flagValue(args, flag) {
|
|
|
2749
2879
|
const value = args[at + 1];
|
|
2750
2880
|
return value === void 0 || value.startsWith("-") ? void 0 : value;
|
|
2751
2881
|
}
|
|
2752
|
-
const VALUE_FLAGS = ["--site", "--subdomain", "--folder", "--then"];
|
|
2882
|
+
const VALUE_FLAGS = ["--site", "--subdomain", "--folder", "--then", "--status", "--path"];
|
|
2753
2883
|
function positionals(argv) {
|
|
2754
2884
|
const found = [];
|
|
2755
2885
|
for (let at = 0; at < argv.length; at++) {
|
|
@@ -2798,7 +2928,7 @@ async function run(commandLine) {
|
|
|
2798
2928
|
const positional = [...positionals(argv), ...literal];
|
|
2799
2929
|
const [command, ...rest] = positional;
|
|
2800
2930
|
if (argv.includes("--version")) {
|
|
2801
|
-
const { version } = await import("./package-
|
|
2931
|
+
const { version } = await import("./package-LdzYr7IV.js").then(
|
|
2802
2932
|
(module) => module.default
|
|
2803
2933
|
);
|
|
2804
2934
|
return { text: version, json: { version }, code: 0 };
|
|
@@ -2828,11 +2958,16 @@ async function run(commandLine) {
|
|
|
2828
2958
|
argv.includes("--device"),
|
|
2829
2959
|
argv.includes("--yes"),
|
|
2830
2960
|
{ subdomain, folder },
|
|
2831
|
-
then
|
|
2961
|
+
then,
|
|
2962
|
+
{
|
|
2963
|
+
status: flagValue(argv, "--status"),
|
|
2964
|
+
path: flagValue(argv, "--path"),
|
|
2965
|
+
resolve: argv.includes("--resolve")
|
|
2966
|
+
}
|
|
2832
2967
|
);
|
|
2833
2968
|
return json ? { ...result, text: JSON.stringify(result.json, null, 2) } : result;
|
|
2834
2969
|
}
|
|
2835
|
-
async function dispatch(command, rest, site, json, device, confirmed, newSite = {}, then = void 0) {
|
|
2970
|
+
async function dispatch(command, rest, site, json, device, confirmed, newSite = {}, then = void 0, commentOptions = {}) {
|
|
2836
2971
|
switch (command) {
|
|
2837
2972
|
case "login": {
|
|
2838
2973
|
const print = json ? () => {
|
|
@@ -2871,6 +3006,8 @@ async function dispatch(command, rest, site, json, device, confirmed, newSite =
|
|
|
2871
3006
|
return await rollback(rest[0], site);
|
|
2872
3007
|
case "rm":
|
|
2873
3008
|
return await remove(rest[0] ?? site, confirmed);
|
|
3009
|
+
case "comments":
|
|
3010
|
+
return await comments(rest[0], rest.slice(1), site, commentOptions);
|
|
2874
3011
|
case "token":
|
|
2875
3012
|
case "tokens":
|
|
2876
3013
|
return rest[0] === void 0 || rest[0] === "list" ? await tokens() : {
|