@monimesl/dot-cli 0.1.0-alpha.21 → 0.1.0-alpha.24

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.
Files changed (44) hide show
  1. package/dist/cli.js +42 -25
  2. package/dist/cli.js.map +1 -1
  3. package/dist/{server-B9-F93mS.js → server-DMnWDdJ6.js} +47 -34
  4. package/dist/{server-B9-F93mS.js.map → server-DMnWDdJ6.js.map} +1 -1
  5. package/dist/templates/showcase/dot.ts +9 -1
  6. package/dist/templates/showcase/pages/basics/bars/controller.ts +13 -2
  7. package/dist/templates/showcase/pages/basics/bars/state.ts +1 -0
  8. package/dist/templates/showcase/pages/basics/bars/view.dml +8 -0
  9. package/dist/templates/showcase/pages/basics/colors/controller.ts +112 -0
  10. package/dist/templates/showcase/pages/basics/colors/state.ts +17 -0
  11. package/dist/templates/showcase/pages/basics/colors/view.dml +142 -0
  12. package/dist/templates/showcase/pages/basics/concert/controller.ts +16 -0
  13. package/dist/templates/showcase/pages/basics/concert/state.ts +7 -0
  14. package/dist/templates/showcase/pages/basics/concert/view.dml +20 -0
  15. package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +7 -1
  16. package/dist/templates/showcase/pages/basics/pick-genre/view.dml +1 -0
  17. package/dist/templates/showcase/pages/basics/share/concerts.ts +31 -0
  18. package/dist/templates/showcase/pages/basics/share/controller.ts +12 -0
  19. package/dist/templates/showcase/pages/basics/share/state.ts +5 -0
  20. package/dist/templates/showcase/pages/basics/share/view.dml +21 -0
  21. package/dist/templates/showcase/pages/components/styles/view.dml +1 -1
  22. package/dist/templates/showcase/pages/dash/account/controller.ts +23 -0
  23. package/dist/templates/showcase/pages/dash/account/state.ts +7 -0
  24. package/dist/templates/showcase/pages/dash/account/view.dml +23 -0
  25. package/dist/templates/showcase/pages/dash/cache/controller.ts +2 -2
  26. package/dist/templates/showcase/pages/dash/cache/view.dml +1 -1
  27. package/dist/templates/showcase/pages/dash/calls/controller.ts +14 -0
  28. package/dist/templates/showcase/pages/dash/calls/state.ts +3 -0
  29. package/dist/templates/showcase/pages/dash/calls/view.dml +17 -1
  30. package/dist/templates/showcase/pages/dash/crypto/controller.ts +38 -0
  31. package/dist/templates/showcase/pages/dash/crypto/state.ts +10 -0
  32. package/dist/templates/showcase/pages/dash/crypto/view.dml +48 -0
  33. package/dist/templates/showcase/pages/dash/files/controller.ts +0 -20
  34. package/dist/templates/showcase/pages/dash/files/state.ts +1 -2
  35. package/dist/templates/showcase/pages/dash/files/view.dml +4 -5
  36. package/dist/templates/showcase/pages/dash/media/controller.ts +29 -0
  37. package/dist/templates/showcase/pages/dash/media/state.ts +4 -0
  38. package/dist/templates/showcase/pages/dash/media/view.dml +20 -1
  39. package/dist/templates/showcase/pages/dash/secrets/view.dml +1 -1
  40. package/dist/templates/showcase/pages/dash/system/controller.ts +3 -3
  41. package/dist/templates/showcase/pages/dash/system/state.ts +2 -2
  42. package/dist/templates/showcase/pages/dash/system/view.dml +1 -2
  43. package/dist/templates/showcase/pages/index/state.ts +29 -4
  44. package/package.json +6 -6
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { existsSync, readFileSync, readdirSync, watch } from "node:fs";
8
8
  import { cp, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
9
9
  import { DmlParseError } from "@monimesl/dot-dml-parser";
10
10
  import { ScriptParseError, parseDotModule } from "@monimesl/dot-script-parser";
11
- import { isPageId } from "@monimesl/dot-core";
11
+ import { DEFAULT_DASH_API_VERSION, isPageId } from "@monimesl/dot-core";
12
12
  import { renderUnicodeCompact } from "uqr";
13
13
  import { createHash, createHmac, createPrivateKey, createPublicKey, randomBytes, sign, timingSafeEqual } from "node:crypto";
14
14
  import { homedir, networkInterfaces } from "node:os";
@@ -61,6 +61,9 @@ function findDotRoot(dir) {
61
61
  function resolveOutDir(root, outDir) {
62
62
  return resolve(root, outDir ?? "dist");
63
63
  }
64
+ function isOneOf(values, name) {
65
+ return values.includes(name);
66
+ }
64
67
  //#endregion
65
68
  //#region src/create/template.ts
66
69
  const TEMPLATES = ["starter", "showcase"];
@@ -69,7 +72,7 @@ const TEMPLATE_HINTS = {
69
72
  showcase: "a page for each feature, to learn from"
70
73
  };
71
74
  function isTemplate(name) {
72
- return TEMPLATES.includes(name);
75
+ return isOneOf(TEMPLATES, name);
73
76
  }
74
77
  const PAGE_FILES = {
75
78
  "view.dml": "<View>\n <Text>My new page</Text>\n</View>\n",
@@ -150,6 +153,7 @@ function dotJson({ name, dotPackage, template }) {
150
153
  };
151
154
  if (template === "showcase") {
152
155
  dot.permissions = {
156
+ account: "Show your name on your concert ticket",
153
157
  camera: "Take a photo for your concert ticket, or scan a ticket's QR code",
154
158
  photoLibrary: "Choose a photo for your concert ticket",
155
159
  files: "Attach your concert ticket",
@@ -161,6 +165,7 @@ function dotJson({ name, dotPackage, template }) {
161
165
  "httpbin.org"
162
166
  ] };
163
167
  }
168
+ dot.dash = { minApiVersion: DEFAULT_DASH_API_VERSION };
164
169
  return formatJson(dot);
165
170
  }
166
171
  const TEMPLATE_READMES = {
@@ -206,10 +211,11 @@ other host fail. Your editor can complete the other fields from the schema the f
206
211
 
207
212
  ## Which Dash opens it
208
213
 
209
- Dash opens the dot unless it was built with a newer major version of \`@monimesl/dot-runtime\` than
210
- the one Dash has. A newer minor version can add Dash calls that an older Dash doesn't have yet.
211
- There, such a call fails with a \`DashError\` whose \`code\` is \`unsupported\`, so catch it and carry
212
- on without the feature.
214
+ \`dash.minApiVersion\` in \`dot.json\` is the Dash API version the dot needs, such as \`1.0\`. Dash
215
+ opens the dot when it offers that version, or a newer minor of the same major. Each Dash call's docs
216
+ say which version it came in, after \`@since\`. To use a newer call and still open on older Dash,
217
+ keep \`minApiVersion\` and check \`this.dash.supports('contacts.pickPhone')\` before the call.
218
+ \`dotctl dev\` warns when the dot makes a newer call without checking.
213
219
 
214
220
  ## .env
215
221
 
@@ -268,7 +274,8 @@ function isInDotWorkspace(dir) {
268
274
  }
269
275
  function templateDir(template) {
270
276
  const example = join(cliRoot(), "../../examples", template);
271
- return existsSync(example) ? example : join(cliRoot(), "dist/templates", template);
277
+ if (existsSync(example)) return example;
278
+ return join(cliRoot(), "dist/templates", template);
272
279
  }
273
280
  function cliVersion() {
274
281
  return readPackageJson(join(cliRoot(), "package.json")).version;
@@ -346,7 +353,7 @@ const PACKAGE_MANAGERS = [
346
353
  "bun"
347
354
  ];
348
355
  function isPackageManager(name) {
349
- return PACKAGE_MANAGERS.includes(name);
356
+ return isOneOf(PACKAGE_MANAGERS, name);
350
357
  }
351
358
  function launchedBy(userAgent = process.env.npm_config_user_agent ?? "") {
352
359
  return PACKAGE_MANAGERS.find((name) => userAgent.startsWith(`${name}/`)) ?? "npm";
@@ -476,7 +483,7 @@ var LocalDeviceServer = class {
476
483
  this.#announce();
477
484
  }
478
485
  close() {
479
- for (const wake of this.#waiting) wake();
486
+ this.#wakeWaitingDevices();
480
487
  return new Promise((resolve) => {
481
488
  this.#server.close(() => resolve());
482
489
  this.#server.closeAllConnections();
@@ -484,6 +491,9 @@ var LocalDeviceServer = class {
484
491
  }
485
492
  #announce() {
486
493
  this.#generation += 1;
494
+ this.#wakeWaitingDevices();
495
+ }
496
+ #wakeWaitingDevices() {
487
497
  for (const wake of this.#waiting) wake();
488
498
  }
489
499
  #handle(request, response) {
@@ -548,20 +558,20 @@ var LocalDeviceServer = class {
548
558
  sendJson(response, { generation: this.#generation });
549
559
  return;
550
560
  }
551
- const wake = () => {
561
+ const stopWaiting = () => {
552
562
  clearTimeout(timer);
553
563
  this.#waiting.delete(wake);
564
+ };
565
+ const wake = () => {
566
+ stopWaiting();
554
567
  sendJson(response, { generation: this.#generation });
555
568
  };
556
569
  const timer = setTimeout(() => {
557
- this.#waiting.delete(wake);
570
+ stopWaiting();
558
571
  response.writeHead(204).end();
559
572
  }, WAIT_MS);
560
573
  this.#waiting.add(wake);
561
- response.on("close", () => {
562
- clearTimeout(timer);
563
- this.#waiting.delete(wake);
564
- });
574
+ response.on("close", stopWaiting);
565
575
  }
566
576
  #receiveLog(request, response, from) {
567
577
  readLogText(request).then((text) => {
@@ -607,7 +617,8 @@ function buildFileName(path) {
607
617
  function isAuthorized(request, token) {
608
618
  const given = Buffer.from(request.headers.authorization ?? "");
609
619
  const expected = Buffer.from(`Bearer ${token}`);
610
- return given.length === expected.length && timingSafeEqual(given, expected);
620
+ if (given.length !== expected.length) return false;
621
+ return timingSafeEqual(given, expected);
611
622
  }
612
623
  function sendJson(response, body) {
613
624
  response.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(body));
@@ -616,9 +627,10 @@ async function readLogText(request) {
616
627
  const chunks = [];
617
628
  let size = 0;
618
629
  for await (const chunk of request) {
619
- size += chunk.length;
630
+ const bytes = chunk;
631
+ size += bytes.length;
620
632
  if (size > MAX_LOG_BYTES) throw new Error(`A device sent more than ${MAX_LOG_BYTES} bytes of log at once`);
621
- chunks.push(chunk);
633
+ chunks.push(bytes);
622
634
  }
623
635
  return Buffer.concat(chunks).toString("utf8");
624
636
  }
@@ -774,7 +786,8 @@ const HIDE_CURSOR = "\x1B[?25l";
774
786
  const SHOW_CURSOR = "\x1B[?25h";
775
787
  async function select(question, choices, fallback, { input, output } = TERMINAL) {
776
788
  if (!input.isTTY) return fallback;
777
- let selectedIndex = Math.max(0, choices.findIndex((choice) => choice.value === fallback));
789
+ let selectedIndex = choices.findIndex((choice) => choice.value === fallback);
790
+ if (selectedIndex < 0) selectedIndex = 0;
778
791
  let valueWidth = 0;
779
792
  if (choices.some((choice) => choice.hint)) valueWidth = Math.max(...choices.map((choice) => choice.value.length));
780
793
  const formatChoice = (choice, index) => {
@@ -786,7 +799,8 @@ async function select(question, choices, fallback, { input, output } = TERMINAL)
786
799
  };
787
800
  let drawnLines = 0;
788
801
  const draw = (lines) => {
789
- const moveUp = drawnLines > 0 ? cursorUp(drawnLines) : "";
802
+ let moveUp = "";
803
+ if (drawnLines > 0) moveUp = cursorUp(drawnLines);
790
804
  output.write(`${moveUp}${ERASE_BELOW}${lines.join("\n")}\n`);
791
805
  drawnLines = lines.length;
792
806
  };
@@ -837,7 +851,9 @@ async function ask(question, fallback, hint, { input, output } = TERMINAL) {
837
851
  output
838
852
  });
839
853
  try {
840
- return (await readline.question(`${styleText("bold", question)}${hintText}${defaultMark}: `)).trim() || fallback;
854
+ const trimmedAnswer = (await readline.question(`${styleText("bold", question)}${hintText}${defaultMark}: `)).trim();
855
+ if (trimmedAnswer === "") return fallback;
856
+ return trimmedAnswer;
841
857
  } catch (error) {
842
858
  if (error.name === "AbortError") return;
843
859
  throw error;
@@ -938,7 +954,7 @@ async function main(argv) {
938
954
  return runBuild(options);
939
955
  }
940
956
  async function serveLanguageServer() {
941
- const { serveStdio } = await import("./server-B9-F93mS.js");
957
+ const { serveStdio } = await import("./server-DMnWDdJ6.js");
942
958
  serveStdio();
943
959
  return new Promise(() => {});
944
960
  }
@@ -1043,7 +1059,8 @@ async function createDot(dir, flags) {
1043
1059
  dotPackage,
1044
1060
  template
1045
1061
  });
1046
- const path = relative(process.cwd(), root) || ".";
1062
+ let path = relative(process.cwd(), root);
1063
+ if (path === "") path = ".";
1047
1064
  console.log(`\nCreated ${name} in ${path}`);
1048
1065
  if (workspace) console.log("It is inside the Dot repository, so it uses the workspace packages.");
1049
1066
  let packageManager;
@@ -1059,14 +1076,14 @@ async function createDot(dir, flags) {
1059
1076
  return 0;
1060
1077
  }
1061
1078
  function runCommand(command, args, cwd) {
1062
- return new Promise((resolvePromise, reject) => {
1079
+ return new Promise((resolve, reject) => {
1063
1080
  const child = spawn(command, args, {
1064
1081
  cwd,
1065
1082
  stdio: "inherit",
1066
1083
  shell: process.platform === "win32"
1067
1084
  });
1068
1085
  child.on("error", reject);
1069
- child.on("exit", (code) => resolvePromise(code ?? 1));
1086
+ child.on("exit", (exitCode) => resolve(exitCode ?? 1));
1070
1087
  });
1071
1088
  }
1072
1089
  async function askDotPackage(suggested) {