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 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-BycuI1VJ.js").then(
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() : {
@@ -1,5 +1,5 @@
1
1
  const name = "drop2run";
2
- const version = "0.8.0";
2
+ const version = "0.9.0";
3
3
  const description = "Publish a page, a folder of documents or a built site to Drop2Run from the command line.";
4
4
  const license = "MIT";
5
5
  const type = "module";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drop2run",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Publish a page, a folder of documents or a built site to Drop2Run from the command line.",
5
5
  "license": "MIT",
6
6
  "type": "module",