@monimesl/dot-cli 1.0.0-beta.2 → 1.0.0-beta.3
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 +22 -9
- package/dist/cli.js +13 -11
- package/dist/cli.js.map +1 -1
- package/dist/templates/showcase/.env +2 -2
- package/dist/templates/showcase/dot.ts +2 -1
- package/dist/templates/showcase/pages/basics/bars/controller.ts +2 -2
- package/dist/templates/showcase/pages/basics/bars/view.dml +2 -1
- package/dist/templates/showcase/pages/basics/bindings/state.ts +2 -2
- package/dist/templates/showcase/pages/basics/bindings/view.dml +2 -2
- package/dist/templates/showcase/pages/basics/colors/controller.ts +1 -1
- package/dist/templates/showcase/pages/basics/concert/view.dml +1 -1
- package/dist/templates/showcase/pages/basics/control-flow/controller.ts +1 -1
- package/dist/templates/showcase/pages/basics/control-flow/view.dml +1 -1
- package/dist/templates/showcase/pages/basics/events/controller.ts +1 -1
- package/dist/templates/showcase/pages/basics/events/view.dml +1 -1
- package/dist/templates/showcase/pages/basics/hooks/controller.ts +2 -1
- package/dist/templates/showcase/pages/basics/hooks/view.dml +2 -2
- package/dist/templates/showcase/pages/basics/navigation/controller.ts +1 -0
- package/dist/templates/showcase/pages/basics/navigation/view.dml +1 -1
- package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +3 -3
- package/dist/templates/showcase/pages/basics/share/view.dml +2 -2
- package/dist/templates/showcase/pages/basics/state/controller.ts +9 -0
- package/dist/templates/showcase/pages/basics/state/state.ts +3 -0
- package/dist/templates/showcase/pages/components/carousel/view.dml +2 -2
- package/dist/templates/showcase/pages/components/feedback/controller.ts +2 -1
- package/dist/templates/showcase/pages/components/feedback/state.ts +2 -2
- package/dist/templates/showcase/pages/components/feedback/view.dml +2 -2
- package/dist/templates/showcase/pages/components/forms/controller.ts +2 -2
- package/dist/templates/showcase/pages/components/forms/state.ts +1 -1
- package/dist/templates/showcase/pages/components/forms/view.dml +5 -5
- package/dist/templates/showcase/pages/components/icons/controller.ts +1 -0
- package/dist/templates/showcase/pages/components/icons/state.ts +1 -1
- package/dist/templates/showcase/pages/components/images/controller.ts +1 -1
- package/dist/templates/showcase/pages/components/images/state.ts +1 -1
- package/dist/templates/showcase/pages/components/images/view.dml +4 -4
- package/dist/templates/showcase/pages/components/layout/controller.ts +3 -1
- package/dist/templates/showcase/pages/components/layout/view.dml +5 -5
- package/dist/templates/showcase/pages/components/lists/view.dml +8 -8
- package/dist/templates/showcase/pages/components/money/controller.ts +1 -1
- package/dist/templates/showcase/pages/components/money/view.dml +2 -2
- package/dist/templates/showcase/pages/components/reading/state.ts +2 -2
- package/dist/templates/showcase/pages/components/sheets/controller.ts +1 -1
- package/dist/templates/showcase/pages/components/sheets/view.dml +1 -1
- package/dist/templates/showcase/pages/components/styles/view.dml +3 -3
- package/dist/templates/showcase/pages/components/tabs/state.ts +2 -2
- package/dist/templates/showcase/pages/components/tabs/view.dml +2 -2
- package/dist/templates/showcase/pages/components/text/view.dml +3 -3
- package/dist/templates/showcase/pages/dash/account/controller.ts +3 -0
- package/dist/templates/showcase/pages/dash/account/view.dml +2 -2
- package/dist/templates/showcase/pages/dash/cache/controller.ts +3 -0
- package/dist/templates/showcase/pages/dash/calls/controller.ts +3 -2
- package/dist/templates/showcase/pages/dash/contacts/controller.ts +1 -0
- package/dist/templates/showcase/pages/dash/crypto/controller.ts +2 -0
- package/dist/templates/showcase/pages/dash/crypto/view.dml +1 -1
- package/dist/templates/showcase/pages/dash/files/controller.ts +6 -1
- package/dist/templates/showcase/pages/dash/files/view.dml +1 -1
- package/dist/templates/showcase/pages/dash/media/controller.ts +7 -1
- package/dist/templates/showcase/pages/dash/media/state.ts +2 -0
- package/dist/templates/showcase/pages/dash/network/controller.ts +5 -1
- package/dist/templates/showcase/pages/dash/network/view.dml +1 -1
- package/dist/templates/showcase/pages/dash/payments/cases.ts +3 -3
- package/dist/templates/showcase/pages/dash/payments/controller.ts +6 -0
- package/dist/templates/showcase/pages/dash/payments/view.dml +1 -1
- package/dist/templates/showcase/pages/dash/secrets/controller.ts +3 -0
- package/dist/templates/showcase/pages/dash/system/controller.ts +5 -0
- package/dist/templates/showcase/pages/dash/system/view.dml +4 -4
- package/dist/templates/showcase/pages/dash/ui/controller.ts +6 -1
- package/dist/templates/showcase/pages/index/controller.ts +1 -0
- package/dist/templates/showcase/pages/index/state.ts +3 -3
- package/dist/templates/starter/.env +2 -2
- package/dist/templates/starter/dot.ts +1 -0
- package/dist/templates/starter/pages/index/controller.ts +2 -0
- package/dist/templates/starter/pages/index/state.ts +2 -0
- package/dist/templates/starter/pages/index/view.dml +2 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @monimesl/dot-cli
|
|
2
2
|
|
|
3
|
-
The `dotctl` command. It creates dots, builds them and runs them in the simulator
|
|
4
|
-
|
|
3
|
+
The `dotctl` command. It creates dots, builds them and runs them in the simulator and on phones.
|
|
4
|
+
Later it will also publish them.
|
|
5
5
|
|
|
6
6
|
```sh
|
|
7
7
|
dotctl create [dir] # create a dot (asks for its name, package name and template)
|
|
8
8
|
dotctl create page <path> # add a page to the dot you are in
|
|
9
|
-
dotctl build [root] # build the dot
|
|
9
|
+
dotctl build [root] # build the dot for Dash into dist/, with the ZIP Dash downloads
|
|
10
10
|
dotctl dev [root] # run the dot in the browser simulator and reload it on each change
|
|
11
11
|
dotctl preview [root] # build as `build` does, then run that build in the browser simulator
|
|
12
12
|
dotctl keys show # print this computer's developer key: its public key and key ID
|
|
@@ -18,7 +18,7 @@ dotctl lsp # start the .dml language server (editors run this)
|
|
|
18
18
|
| `--out-dir <dir>` | `build`, `dev`, `preview` | Output directory (default: `<root>/dist`) |
|
|
19
19
|
| `--conditions <list>` | `build`, `dev`, `preview` | Extra export conditions, such as `@monimesl/dot-source` |
|
|
20
20
|
| `--port <port>` | `dev`, `preview` | Simulator port (default: `3680`, or the next free one) |
|
|
21
|
-
| `--device` | `dev` | Also serve the dot to
|
|
21
|
+
| `--device` | `dev` | Also serve the dot to phones on your network, through a QR code |
|
|
22
22
|
| `--name <name>` | `create` | The dot's name, instead of asking |
|
|
23
23
|
| `--package <name>` | `create` | The dot's package name, such as `com.mysalone.thing`, instead of asking |
|
|
24
24
|
| `--template <name>` | `create` | `starter` (one page to build on, the default) or `showcase` (all features) |
|
|
@@ -28,10 +28,10 @@ dotctl lsp # start the .dml language server (editors run this)
|
|
|
28
28
|
|
|
29
29
|
Creates a self-contained project for a new dot. It asks for:
|
|
30
30
|
|
|
31
|
-
- **The name.** The default
|
|
32
|
-
kebab case.
|
|
31
|
+
- **The name.** The default comes from `dir`, such as `Plasas Kitchen` for `plasas-kitchen`, or is
|
|
32
|
+
`Dot`. The project goes in `dir`, or a folder named after the dot in kebab case.
|
|
33
33
|
- **The package name.** A reverse domain name you own, such as `com.mysalone.thing`. It can't change
|
|
34
|
-
after the dot is published. The default is `com.example.<the
|
|
34
|
+
after the dot is published. The default is `com.example.<the name in kebab case>`.
|
|
35
35
|
- **The template.** `starter` is one page to build on. `showcase` has a page for each feature.
|
|
36
36
|
|
|
37
37
|
The dot depends on `@monimesl/dot-runtime` and `@monimesl/dot-cli`. Inside the Dot repository it
|
|
@@ -42,6 +42,17 @@ Then it offers to install the dependencies and start the simulator. If more than
|
|
|
42
42
|
manager is installed (npm, pnpm, yarn or bun), it asks which to use. The default is the one that
|
|
43
43
|
ran `dotctl`, or npm.
|
|
44
44
|
|
|
45
|
+
## `dotctl build`
|
|
46
|
+
|
|
47
|
+
Builds the dot, minified, into `dist/`:
|
|
48
|
+
|
|
49
|
+
- `ui/` and `logic/` hold the two bundles. `ui/` also has the dot's `statics/` images.
|
|
50
|
+
- `manifest.json` tells Dash how to run the build.
|
|
51
|
+
- `<package>-<version>.zip` holds all of it. It's the one file Dash downloads.
|
|
52
|
+
|
|
53
|
+
The build fails if `network.hosts` in `dot.json` lists a local address, such as `localhost`. Dash
|
|
54
|
+
refuses those in a published dot. `dotctl dev` allows them.
|
|
55
|
+
|
|
45
56
|
## `dotctl dev`
|
|
46
57
|
|
|
47
58
|
Builds the dot, opens it in the browser simulator, and builds it again each time you save:
|
|
@@ -52,8 +63,10 @@ Builds the dot, opens it in the browser simulator, and builds it again each time
|
|
|
52
63
|
pages it had open.
|
|
53
64
|
- If a build fails, the simulator shows the error and keeps running the last good build.
|
|
54
65
|
|
|
55
|
-
With `--device`, it also serves each build to
|
|
56
|
-
|
|
66
|
+
With `--device`, it also serves each build to phones on your local network. It prints a QR code.
|
|
67
|
+
Scan it with Live Test in Dash, or with a Dot example app, and the phone follows the dot in the
|
|
68
|
+
same way. To scan it in Dash, turn on Enable Dot Debugging in Developer Settings, then tap Live
|
|
69
|
+
Test on the Dots tab. Dash needs a [developer key](#developer-keys). See also
|
|
57
70
|
[the example apps](../hosts/native/README.md#the-example-apps).
|
|
58
71
|
|
|
59
72
|
## `dotctl preview`
|
package/dist/cli.js
CHANGED
|
@@ -234,7 +234,7 @@ function formatJson(value) {
|
|
|
234
234
|
}
|
|
235
235
|
//#endregion
|
|
236
236
|
//#region src/create/create.ts
|
|
237
|
-
const DOT_PACKAGE_RULE = "Use lowercase words joined by dots, such as com.mysalone.thing
|
|
237
|
+
const DOT_PACKAGE_RULE = "Use lowercase words joined by dots, such as com.mysalone.thing";
|
|
238
238
|
async function create({ name, dir, dotPackage, template = "starter" }) {
|
|
239
239
|
const root = resolve(dir);
|
|
240
240
|
if (existsSync(root) && readdirSync(root).length > 0) throw new CommandError(`${dir} already exists and is not empty. Choose another folder`);
|
|
@@ -387,7 +387,7 @@ async function loadDeveloperKey(path = defaultDeveloperKeyPath()) {
|
|
|
387
387
|
throw error;
|
|
388
388
|
}
|
|
389
389
|
const privateKey = createPrivateKey(pem);
|
|
390
|
-
if (privateKey.asymmetricKeyType !== "ed25519") throw new
|
|
390
|
+
if (privateKey.asymmetricKeyType !== "ed25519") throw new CommandError(`${path} isn't an Ed25519 key. Make one with: openssl genpkey -algorithm ed25519 -out ${path}`);
|
|
391
391
|
return toDeveloperKey(privateKey);
|
|
392
392
|
}
|
|
393
393
|
function toDeveloperKey(privateKey) {
|
|
@@ -423,7 +423,7 @@ function buildMac(secret, body) {
|
|
|
423
423
|
return hmacWithSecret(secret, `dotdev-build\n${body}`);
|
|
424
424
|
}
|
|
425
425
|
async function startDeviceServer({ address = localNetworkAddress(), port = 0, onLog = () => {}, onConnect = () => {}, developerKey } = {}) {
|
|
426
|
-
if (address === void 0) throw new
|
|
426
|
+
if (address === void 0) throw new CommandError("This computer has no local network address, so phones can't reach it. Connect to Wi-Fi and try again");
|
|
427
427
|
const deviceServer = new LocalDeviceServer(onLog, onConnect);
|
|
428
428
|
await deviceServer.listen(port, address, developerKey);
|
|
429
429
|
return deviceServer;
|
|
@@ -886,14 +886,15 @@ const USAGE = `Usage:
|
|
|
886
886
|
page for each feature
|
|
887
887
|
dotctl create page <path> Add a page to the dot you are in. Writes
|
|
888
888
|
pages/<path>/ with view.dml, state.ts and controller.ts
|
|
889
|
-
dotctl build [root] [options] Build the minified bundles
|
|
889
|
+
dotctl build [root] [options] Build the dot for Dash: minified bundles in dist/, and
|
|
890
|
+
the ZIP that Dash downloads
|
|
890
891
|
dotctl dev [root] [options] Run the dot in the browser simulator and rebuild on
|
|
891
892
|
every change
|
|
892
893
|
dotctl preview [root] [options] Build as \`build\` does, then run that build in the
|
|
893
|
-
browser simulator, exactly as
|
|
894
|
+
browser simulator, exactly as Dash gets it
|
|
894
895
|
dotctl keys show Show this computer's developer key: its public key
|
|
895
896
|
and key ID. The key signs the links that
|
|
896
|
-
\`dev --device\` shows, so Dash
|
|
897
|
+
\`dev --device\` shows, so Dash opens them
|
|
897
898
|
dotctl lsp [--stdio] Start the .dml language server for editors. It talks
|
|
898
899
|
over stdin and stdout
|
|
899
900
|
|
|
@@ -902,9 +903,9 @@ Options:
|
|
|
902
903
|
--conditions <list> Extra export conditions, separated by commas
|
|
903
904
|
--port <port> Simulator port for \`dev\` and \`preview\`. If it is taken, the
|
|
904
905
|
next free port is used (default: ${DEFAULT_PORT})
|
|
905
|
-
--device With \`dev\`, also serve the dot to
|
|
906
|
-
|
|
907
|
-
reload after each build
|
|
906
|
+
--device With \`dev\`, also serve the dot to phones on your local network,
|
|
907
|
+
through a link and QR code. Open it with Live Test in Dash, or
|
|
908
|
+
with a Dot example app. Phones reload after each build
|
|
908
909
|
-h, --help Show this message`;
|
|
909
910
|
async function main(argv) {
|
|
910
911
|
const { values, positionals } = parseArgs({
|
|
@@ -946,6 +947,7 @@ async function main(argv) {
|
|
|
946
947
|
case "dev":
|
|
947
948
|
case "preview": break;
|
|
948
949
|
default:
|
|
950
|
+
if (command !== void 0) console.error(`There is no "${command}" command. See the commands below.\n`);
|
|
949
951
|
console.log(USAGE);
|
|
950
952
|
return 1;
|
|
951
953
|
}
|
|
@@ -981,7 +983,7 @@ async function runDev(options, port, device) {
|
|
|
981
983
|
}
|
|
982
984
|
async function runPreview(options, port, device) {
|
|
983
985
|
if (device) {
|
|
984
|
-
console.error("--device only works with dotctl dev");
|
|
986
|
+
console.error("--device only works with dotctl dev. Run dotctl dev --device instead");
|
|
985
987
|
return 1;
|
|
986
988
|
}
|
|
987
989
|
const simulator = await preview({
|
|
@@ -1007,7 +1009,7 @@ function waitForCtrlC() {
|
|
|
1007
1009
|
}
|
|
1008
1010
|
async function showKeys(action) {
|
|
1009
1011
|
if (action !== "show") {
|
|
1010
|
-
console.error("
|
|
1012
|
+
console.error("Run dotctl keys show to see this computer's developer key");
|
|
1011
1013
|
return 1;
|
|
1012
1014
|
}
|
|
1013
1015
|
const path = defaultDeveloperKeyPath();
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/report.ts","../src/utils.ts","../src/create/template.ts","../src/create/create.ts","../src/create/page.ts","../src/create/package-manager.ts","../src/device/developer-key.ts","../src/device/server.ts","../src/simulator.ts","../src/dev.ts","../src/preview.ts","../src/prompts.ts","../src/cli.ts"],"sourcesContent":["import { relative } from 'node:path';\n\nimport { DmlParseError } from '@monimesl/dot-dml-parser';\nimport type { BuildResult } from '@monimesl/dot-compiler';\nimport { CompileError } from '@monimesl/dot-compiler';\nimport { ScriptParseError } from '@monimesl/dot-script-parser';\n\n// A mistake in how a command was used. Its message is enough, so no stack trace is shown.\nexport class CommandError extends Error {\n override readonly name = 'CommandError';\n}\n\nexport function describeError(error: unknown): string {\n const known =\n error instanceof CommandError ||\n error instanceof CompileError ||\n error instanceof DmlParseError ||\n error instanceof ScriptParseError;\n if (known) {\n return error.message;\n }\n if (isArgumentError(error)) {\n // Keep only the first sentence. Node goes on to explain `--`, which doesn't help here.\n const [firstSentence] = error.message.split('. ');\n return `${firstSentence}. See dotctl --help.`;\n }\n if (error instanceof Error) {\n return error.stack ?? error.message;\n }\n return String(error);\n}\n\n// Node's parseArgs throws these for an unknown flag or a missing value.\nfunction isArgumentError(error: unknown): error is Error {\n if (!(error instanceof Error) || !('code' in error)) {\n return false;\n }\n return typeof error.code === 'string' && error.code.startsWith('ERR_PARSE_ARGS');\n}\n\n// Prints each file relative to the current folder.\nexport function printFiles(files: readonly string[]): void {\n for (const file of files) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n}\n\nexport function printWarnings({ warnings }: BuildResult): void {\n for (const warning of warnings) {\n console.warn(`warning: ${warning.message}`);\n }\n}\n\nexport function printBuildTime(startedAt: number): void {\n console.log(`Built in ${Math.round(performance.now() - startedAt)}ms`);\n}\n\nexport function printSimulatorUrl(url: string): void {\n console.log(`\\n Simulator running at: ${url}\\n`);\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\n\nimport { DOT_JSON_FILE } from '@monimesl/dot-compiler';\n\n// The nearest folder that has a file named `fileName`, starting at `dir` and walking up.\nexport function findFolderWith(fileName: string, dir: string): string | undefined {\n let current = dir;\n while (true) {\n if (existsSync(join(current, fileName))) {\n return current;\n }\n const parent = dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n}\n\nexport function findDotRoot(dir: string): string | undefined {\n return findFolderWith(DOT_JSON_FILE, dir);\n}\n\n// Where `dev` and `preview` write the build: `--out-dir` if given, else dist/ in the dot.\nexport function resolveOutDir(root: string, outDir: string | undefined): string {\n return resolve(root, outDir ?? 'dist');\n}\n\nexport function isOneOf<Value extends string>(\n values: readonly Value[],\n name: string,\n): name is Value {\n return (values as readonly string[]).includes(name);\n}\n","import { DEFAULT_DASH_API_VERSION } from '@monimesl/dot-core';\n\nimport { isOneOf } from '../utils';\n\nexport const TEMPLATES = ['starter', 'showcase'] as const;\n\nexport type Template = (typeof TEMPLATES)[number];\n\n// Shown next to each template when `dotctl create` asks which one to use.\nexport const TEMPLATE_HINTS: Record<Template, string> = {\n starter: 'one page to build on',\n showcase: 'a page for each feature, to learn from',\n};\n\nexport function isTemplate(name: string): name is Template {\n return isOneOf(TEMPLATES, name);\n}\n\ninterface TemplateOptions {\n name: string;\n // The npm package name, such as `my-dot`.\n packageName: string;\n // The dot's own package name, a reverse domain name such as `com.mysalone.thing`.\n dotPackage: string;\n dotVersion: string;\n workspace: boolean;\n template: Template;\n}\n\nexport const PAGE_FILES: Readonly<Record<string, string>> = {\n 'view.dml': '<View>\\n\\t<Text>My new page</Text>\\n</View>\\n',\n 'state.ts': 'export default {};\\n',\n 'controller.ts': `import state from './state';\n\nexport default definePage({\n initialState: state,\n});\n`,\n};\n\n// The files `dotctl create` copies from the template's example. It generates the rest.\nexport const TEMPLATE_FILES = ['dot.ts', 'pages', 'statics', '.env'] as const;\n\n// Editors that follow `$schema` can check dot.json and complete its fields. unpkg serves the schema\n// from the runtime's latest release on npm, and picks up a new release within minutes.\nconst DOT_JSON_SCHEMA = 'https://unpkg.com/@monimesl/dot-runtime/dot.schema.json';\n\n// Stands in for the ID Monime gives the dot when it's registered.\nexport const UNREGISTERED_DOT_ID = '<monime-Internal-Id>';\n\n// The files `dotctl create` writes itself, by path.\nexport function generatedFiles(options: TemplateOptions): Record<string, string> {\n return {\n 'package.json': packageJson(options),\n 'tsconfig.json': tsconfig(options),\n '.gitignore': 'node_modules/\\ndist/\\n# Types generated by dotctl\\n.dot/\\n',\n 'dot.json': dotJson(options),\n 'README.md': readme(options),\n };\n}\n\nfunction packageJson({ packageName, dotVersion, workspace }: TemplateOptions): string {\n const conditions = workspace ? ' --conditions @monimesl/dot-source' : '';\n return formatJson({\n name: packageName,\n private: true,\n type: 'module',\n scripts: {\n dev: `dotctl dev${conditions}`,\n build: `dotctl build${conditions}`,\n preview: `dotctl preview${conditions}`,\n typecheck: 'tsc --noEmit',\n },\n dependencies: { '@monimesl/dot-runtime': dotVersion },\n devDependencies: { '@monimesl/dot-cli': dotVersion, typescript: '^5.9.3' },\n });\n}\n\nfunction tsconfig({ workspace }: TemplateOptions): string {\n const compilerOptions: Record<string, unknown> = {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['@monimesl/dot-runtime/globals'],\n strict: true,\n noEmit: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n };\n if (workspace) {\n compilerOptions.customConditions = ['@monimesl/dot-source'];\n }\n return formatJson({\n compilerOptions,\n include: ['dot.ts', 'pages', '.dot/pages.d.ts', '.dot/env.d.ts'],\n });\n}\n\nfunction dotJson({ name, dotPackage, template }: TemplateOptions): string {\n const dot: Record<string, unknown> = {\n id: UNREGISTERED_DOT_ID,\n $schema: DOT_JSON_SCHEMA,\n package: dotPackage,\n name,\n version: '0.1.0',\n };\n if (template === 'showcase') {\n // The dash.media, dash.storage.file, dash.contacts, dash.account and dash.payments pages ask for these.\n dot.permissions = {\n account: 'Show your name on your concert ticket',\n camera: \"Take a photo for your concert ticket, or scan a ticket's QR code\",\n photoLibrary: 'Choose a photo for your concert ticket',\n files: 'Attach your concert ticket',\n contacts: 'Choose a friend to send a ticket to',\n payments: 'Pay for concert tickets',\n };\n // The showcase's network page sends requests to these hosts.\n dot.network = { hosts: ['itunes.apple.com', '*.mzstatic.com', 'httpbin.org'] };\n }\n dot.dash = { minApiVersion: DEFAULT_DASH_API_VERSION };\n return formatJson(dot);\n}\n\nconst TEMPLATE_READMES: Readonly<Record<Template, string>> = {\n starter: `It starts with one page, \\`index\\`. It shows a greeting from \\`.env\\` and a counter you\ncan tap. Change it, and add pages as the dot grows. To see how each feature works, make a showcase\ndot with \\`npx dotctl create my-tour --template showcase\\`.`,\n showcase: `It starts as a showcase. The \\`index\\` page links to a page for each feature, in three\nfolders: \\`pages/basics/\\` for how a page works, \\`pages/components/\\` for what a view can show,\nand \\`pages/dash/\\` for the phone's features. Read a page to see how it works. Then delete the\npages you don't need, both from \\`pages/\\` and from \\`dot.ts\\`.`,\n};\n\nfunction readme({ name, template }: TemplateOptions): string {\n return `# ${name}\n\nA dot: a mini app that runs inside Dash.\n\n\\`\\`\\`sh\nnpm install\nnpm run dev # run it in the simulator; it reloads on every change\nnpm run build # build it for Dash\n\\`\\`\\`\n\n${TEMPLATE_READMES[template]}\n\n## Pages\n\nEach page is a folder under \\`pages/\\` with three files:\n\n- \\`view.dml\\`: what the page shows. It has one root \\`<View>\\`, with everything else inside it.\n- \\`state.ts\\`: the data the page starts with.\n- \\`controller.ts\\`: how the data changes.\n\n\\`npx dotctl create page <path>\\` adds a page, such as \\`cart/checkout\\`, with these three files. It\nalso lists the page in \\`dot.ts\\`. The dot opens on the first page in that list.\n\n## dot.json\n\n\\`dot.json\\` describes the dot to Dash. Its \\`package\\` is a reverse domain name you own, such as\n\\`com.mysalone.thing\\`. You can't change it after publishing. To publish, register the dot with\nMonime first, and replace its \\`id\\`, \\`${UNREGISTERED_DOT_ID}\\`, with the ID Monime gives you.\n\\`network.hosts\\` lists every host the dot sends requests to with \\`dash.network\\`. Requests to any\nother host fail. Your editor can complete the other fields from the schema the file points to.\n\n## Which Dash opens it\n\n\\`dash.minApiVersion\\` in \\`dot.json\\` is the Dash API version the dot needs, such as \\`1.0\\`. Dash\nopens the dot when it offers that version, or a newer minor of the same major. Each Dash call's docs\nsay which version it came in, after \\`@since\\`. To use a newer call and still open on older Dash,\nkeep \\`minApiVersion\\` and check \\`this.dash.supports('contacts.pickPhone')\\` before the call.\n\\`dotctl dev\\` warns when the dot makes a newer call without checking.\n\n## .env\n\n\\`.env\\` holds public configuration. Code in the dot reads each variable from \\`env\\`, such as\n\\`env.GREETING\\`. Every variable is built into the dot, so anyone who has the dot can read it.\nNever put a secret there. \\`.env.development\\` and \\`.env.production\\` override it for \\`dev\\` and\n\\`build\\`.\n`;\n}\n\n// The package name to suggest, such as `com.example.my-dot` for the npm package `my-dot`. The npm\n// name is already lowercase letters, digits and inner hyphens. A part of a host name can be at most\n// 63 characters, so it's cut there.\nexport function suggestedDotPackage(packageName: string): string {\n return `com.example.${packageName.slice(0, 63).replace(/-+$/, '')}`;\n}\n\nfunction formatJson(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { cp, mkdir, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { isDotPackage } from '@monimesl/dot-compiler';\n\nimport { CommandError } from '../report';\nimport { findFolderWith } from '../utils';\nimport type { Template } from './template';\nimport { generatedFiles, suggestedDotPackage, TEMPLATE_FILES } from './template';\n\ninterface CreateOptions {\n name: string;\n dir: string;\n // A reverse domain name, such as `com.mysalone.thing`. Defaults to `com.example.<npm name>`.\n dotPackage?: string;\n template?: Template;\n}\n\ninterface CreateResult {\n readonly root: string;\n readonly workspace: boolean;\n}\n\ninterface PackageJson {\n name: string;\n version: string;\n}\n\nexport const DOT_PACKAGE_RULE = 'Use lowercase words joined by dots, such as com.mysalone.thing.';\n\nexport async function create({\n name,\n dir,\n dotPackage,\n template = 'starter',\n}: CreateOptions): Promise<CreateResult> {\n const root = resolve(dir);\n if (existsSync(root) && readdirSync(root).length > 0) {\n throw new CommandError(`${dir} already exists and is not empty. Choose another folder`);\n }\n const packageName = toPackageName(name);\n if (!packageName) {\n throw new CommandError(`The name \"${name}\" needs at least one letter or digit`);\n }\n dotPackage ??= suggestedDotPackage(packageName);\n if (!isDotPackage(dotPackage)) {\n throw new CommandError(`\"${dotPackage}\" isn't a package name. ${DOT_PACKAGE_RULE}`);\n }\n\n const workspace = isInDotWorkspace(root);\n const source = templateDir(template);\n await mkdir(root, { recursive: true });\n for (const path of TEMPLATE_FILES) {\n await cp(join(source, path), join(root, path), { recursive: true });\n }\n const generated = generatedFiles({\n name,\n packageName,\n dotPackage,\n dotVersion: workspace ? 'workspace:*' : `^${cliVersion()}`,\n workspace,\n template,\n });\n for (const [path, content] of Object.entries(generated)) {\n await writeFile(join(root, path), content);\n }\n return { root, workspace };\n}\n\nexport function toPackageName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nexport function toDisplayName(dir: string): string {\n return basename(resolve(dir))\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n// Whether the nearest pnpm workspace above `dir` is the Dot repository.\nexport function isInDotWorkspace(dir: string): boolean {\n const workspaceRoot = findFolderWith('pnpm-workspace.yaml', dirname(dir));\n if (workspaceRoot === undefined) {\n return false;\n }\n const runtimeManifest = join(workspaceRoot, 'packages/runtime/package.json');\n if (!existsSync(runtimeManifest)) {\n return false;\n }\n return readPackageJson(runtimeManifest).name === '@monimesl/dot-runtime';\n}\n\n// Inside the repository, templates come from examples/. The published CLI ships its own copy.\nfunction templateDir(template: Template): string {\n const example = join(cliRoot(), '../../examples', template);\n if (existsSync(example)) {\n return example;\n }\n return join(cliRoot(), 'dist/templates', template);\n}\n\nfunction cliVersion(): string {\n return readPackageJson(join(cliRoot(), 'package.json')).version;\n}\n\nfunction readPackageJson(path: string): PackageJson {\n return JSON.parse(readFileSync(path, 'utf8')) as PackageJson;\n}\n\nfunction cliRoot(): string {\n return dirname(createRequire(import.meta.url).resolve('@monimesl/dot-cli/package.json'));\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { DOT_FILE, PAGES_DIR } from '@monimesl/dot-compiler';\nimport { isPageId } from '@monimesl/dot-core';\nimport type { DotModuleIR } from '@monimesl/dot-script-parser';\nimport { parseDotModule } from '@monimesl/dot-script-parser';\n\nimport { CommandError } from '../report';\nimport { findDotRoot } from '../utils';\nimport { PAGE_FILES } from './template';\n\n// Whether the page is now in `defineDot({ pages })` in dot.ts. `manual` means dot.ts couldn't be\n// updated, so the developer must add the page by hand.\ntype PageListing = 'added' | 'already' | 'manual';\n\ninterface CreatePageResult {\n readonly dir: string;\n readonly files: readonly string[];\n readonly listing: PageListing;\n}\n\n// Adds a page folder under pages/ in the dot that contains `cwd`, and lists it last in dot.ts.\nexport async function createPage(path: string, cwd: string): Promise<CreatePageResult> {\n if (!isPageId(path)) {\n throw new CommandError(\n `\"${path}\" is not a page path. Use letters, digits, \"-\" and \"_\", with \"/\" between folders, such as \"cart/checkout\"`,\n );\n }\n const root = findDotRoot(resolve(cwd));\n if (root === undefined) {\n throw new CommandError(\n 'There is no dot.json in this folder or any folder above it. Run this command inside a dot',\n );\n }\n const dir = join(root, PAGES_DIR, path);\n if (existsSync(dir)) {\n throw new CommandError(`pages/${path} already exists. Choose another path`);\n }\n await mkdir(dir, { recursive: true });\n const files: string[] = [];\n for (const [name, content] of Object.entries(PAGE_FILES)) {\n const file = join(dir, name);\n await writeFile(file, content);\n files.push(file);\n }\n const listing = await addToDotPages(join(root, DOT_FILE), path);\n return { dir, files, listing };\n}\n\nasync function addToDotPages(dotFile: string, path: string): Promise<PageListing> {\n if (!existsSync(dotFile)) {\n return 'manual';\n }\n const source = await readFile(dotFile, 'utf8');\n let dot: DotModuleIR;\n try {\n dot = parseDotModule(source, dotFile);\n } catch {\n return 'manual';\n }\n if (dot.pages.includes(path)) {\n return 'already';\n }\n const updated = insertPage(source, dot.lastPage, path);\n if (!listsPage(updated, dotFile, path)) {\n return 'manual';\n }\n await writeFile(dotFile, updated);\n return 'added';\n}\n\n// Inserts `path` after the last page, matching the list's style: same quotes, and on its own line\n// if each page has one.\nfunction insertPage(source: string, lastPage: DotModuleIR['lastPage'], path: string): string {\n const quote = source[lastPage.start] === '\"' ? '\"' : \"'\";\n const page = `${quote}${path}${quote}`;\n const before = source.slice(0, lastPage.end);\n const after = source.slice(lastPage.end);\n\n const lineStart = source.lastIndexOf('\\n', lastPage.start - 1) + 1;\n const indent = source.slice(lineStart, lastPage.start);\n const lastPageHasOwnLine = /^\\s*$/.test(indent);\n if (!lastPageHasOwnLine) {\n return `${before}, ${page}${after}`;\n }\n const trailingComma = /^\\s*,/.exec(after);\n if (trailingComma) {\n const end = lastPage.end + trailingComma[0].length;\n return `${source.slice(0, end)}\\n${indent}${page},${source.slice(end)}`;\n }\n return `${before},\\n${indent}${page}${after}`;\n}\n\n// Whether `source`, the edited dot.ts, still parses and lists `path`.\nfunction listsPage(source: string, dotFile: string, path: string): boolean {\n try {\n return parseDotModule(source, dotFile).pages.includes(path);\n } catch {\n return false;\n }\n}\n","import { spawnSync } from 'node:child_process';\n\nimport { isOneOf } from '../utils';\n\nexport const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'] as const;\nexport type PackageManager = (typeof PACKAGE_MANAGERS)[number];\n\nexport function isPackageManager(name: string): name is PackageManager {\n return isOneOf(PACKAGE_MANAGERS, name);\n}\n\nexport function launchedBy(userAgent = process.env.npm_config_user_agent ?? ''): PackageManager {\n return PACKAGE_MANAGERS.find((name) => userAgent.startsWith(`${name}/`)) ?? 'npm';\n}\n\nexport function installedManagers(\n isInstalled: (name: PackageManager) => boolean = isOnPath,\n): PackageManager[] {\n return PACKAGE_MANAGERS.filter(isInstalled);\n}\n\nfunction isOnPath(name: PackageManager): boolean {\n const result = spawnSync(name, ['--version'], {\n stdio: 'ignore',\n shell: process.platform === 'win32',\n });\n return result.status === 0;\n}\n","import { createPrivateKey, createPublicKey, sign } from 'node:crypto';\nimport type { KeyObject } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n// The Ed25519 key this computer signs dotdev links with. The developer adds its public key to their\n// Monime account. Before Dash opens a link, Monime checks its signature.\nexport interface DeveloperKey {\n // The raw 32-byte public key in base64. This is what the developer adds to their account.\n readonly publicKey: string;\n // The first 8 bytes of the public key in lowercase hex. A link names its key by this ID.\n readonly keyId: string;\n // Returns the signature in base64.\n sign(bytes: Buffer): string;\n}\n\nexport function defaultDeveloperKeyPath(): string {\n return process.env.DOT_DEVELOPER_KEY ?? join(homedir(), '.dot', 'developer-key.pem');\n}\n\n// Explains how to make a key. dotctl reads the key but never creates it.\nexport function missingDeveloperKeyHint(path = defaultDeveloperKeyPath()): string {\n return (\n ` There is no developer key at ${path}. Dash only opens signed links.\\n` +\n \" Make one with OpenSSL 3. macOS's built-in openssl can't, so first run brew install openssl:\\n\\n\" +\n ` mkdir -p ${dirname(path)} && openssl genpkey -algorithm ed25519 -out ${path}\\n\\n` +\n ' Then run `dotctl keys show` and add the public key it prints to your Monime account.'\n );\n}\n\n// Returns undefined when there is no key file yet.\nexport async function loadDeveloperKey(\n path = defaultDeveloperKeyPath(),\n): Promise<DeveloperKey | undefined> {\n let pem: string;\n try {\n pem = await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return undefined;\n }\n throw error;\n }\n const privateKey = createPrivateKey(pem);\n if (privateKey.asymmetricKeyType !== 'ed25519') {\n throw new Error(\n `${path} is not an Ed25519 key. Make one with: openssl genpkey -algorithm ed25519 -out ${path}`,\n );\n }\n return toDeveloperKey(privateKey);\n}\n\nfunction toDeveloperKey(privateKey: KeyObject): DeveloperKey {\n const publicKeyJwk = createPublicKey(privateKey).export({ format: 'jwk' });\n const rawPublicKey = Buffer.from(publicKeyJwk.x!, 'base64url');\n return {\n publicKey: rawPublicKey.toString('base64'),\n keyId: rawPublicKey.subarray(0, 8).toString('hex'),\n sign: (bytes) => sign(null, bytes, privateKey).toString('base64'),\n };\n}\n","import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { readdir, readFile } from 'node:fs/promises';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport type { NetworkInterfaceInfo } from 'node:os';\nimport { networkInterfaces } from 'node:os';\nimport { join, relative, sep } from 'node:path';\n\nimport type { DeveloperKey } from './developer-key';\n\ninterface DeviceServerOptions {\n // The address devices reach the server on. Defaults to the computer's local network address.\n address?: string | undefined;\n // Defaults to any free port. The link tells devices which one.\n port?: number | undefined;\n // Called for each line a device sends, such as its dot's console output.\n onLog?: (line: string, from: string) => void;\n onConnect?: (from: string) => void;\n // Signs the link. Hosts such as Dash only open links signed by a known developer.\n developerKey?: DeveloperKey | undefined;\n}\n\n// Serves a dot's builds to the example host apps on phones, which reload on each new build.\nexport interface DeviceServer {\n // The link a device opens: `dotdev://connect?server=…&secret=…`. It is kept short so the QR\n // code stays small. A signed link's signature is served at `GET /dev/link` instead.\n readonly link: string;\n // Serves the build output in `dir` as the newest build. A `hot` build changed only pages, so\n // devices can reload it in place and keep each open page's state.\n publish(dir: string, options?: { hot?: boolean }): Promise<void>;\n // Tells devices the newest build failed. They keep running the last good one.\n fail(message: string): void;\n close(): Promise<void>;\n}\n\n// A signed link's signature, served at `GET /dev/link`. It isn't secret, so it stays out of the QR\n// code.\nexport interface LinkSignature {\n readonly expires: number;\n readonly keyId: string;\n // In base64.\n readonly sig: string;\n}\n\ninterface BuildFile {\n readonly bytes: Buffer;\n readonly hash: string;\n}\n\nconst DEVICE_SCHEME = 'dotdev';\nconst FILES_PATH = '/dev/files/';\n\n// A waiting request ends after this and the device asks again, before the network drops the idle\n// connection.\nconst WAIT_MS = 25_000;\nconst MAX_LOG_BYTES = 1024 * 1024;\n// How long a signed link works. A photo of an old QR code soon becomes useless.\nconst LINK_LIFETIME_SECONDS = 12 * 60 * 60;\n\nexport const BUILD_MAC_HEADER = 'dot-build-mac';\n\n// The text a signed link's `sig` signs, one field per line.\nexport function signedLinkMessage(\n server: string,\n secret: string,\n expires: number,\n keyId: string,\n): string {\n return ['dotdev-connect', server, secret, String(expires), keyId].join('\\n');\n}\n\n// The bearer token a device sends. It is derived from the link's secret, so the link doesn't need\n// to carry it. Someone who sees the token on the network learns nothing about the secret.\nexport function deviceToken(secret: string): string {\n return hmacWithSecret(secret, 'dotdev-token');\n}\n\n// The HMAC-SHA256 of a build listing, keyed with the link's secret. A device only accepts listings\n// from someone who has the link, and each file must match its hash in the listing.\nexport function buildMac(secret: string, body: string): string {\n return hmacWithSecret(secret, `dotdev-build\\n${body}`);\n}\n\nexport async function startDeviceServer({\n address = localNetworkAddress(),\n port = 0,\n onLog = () => {},\n onConnect = () => {},\n developerKey,\n}: DeviceServerOptions = {}): Promise<DeviceServer> {\n if (address === undefined) {\n throw new Error(\n \"This computer has no local network address, so devices can't reach it. Connect to a network and try again\",\n );\n }\n const deviceServer = new LocalDeviceServer(onLog, onConnect);\n await deviceServer.listen(port, address, developerKey);\n return deviceServer;\n}\n\nclass LocalDeviceServer implements DeviceServer {\n readonly #onLog: (line: string, from: string) => void;\n readonly #onConnect: (from: string) => void;\n // The secret never goes over the network. Only the link carries it.\n readonly #secret = randomBytes(16).toString('base64url');\n readonly #token = deviceToken(this.#secret);\n readonly #server: Server;\n readonly #waiting = new Set<() => void>();\n readonly #connected = new Set<string>();\n #link = '';\n #signature: LinkSignature | null = null;\n #files: ReadonlyMap<string, BuildFile> = new Map();\n #generation = 0;\n // The oldest generation from which only hot builds lead to the newest. A device on this\n // generation or a later one can hot reload to the newest build.\n #hotSince = 0;\n #error: string | null = null;\n\n constructor(onLog: (line: string, from: string) => void, onConnect: (from: string) => void) {\n this.#onLog = onLog;\n this.#onConnect = onConnect;\n this.#server = createServer((request, response) => {\n this.#handle(request, response);\n });\n }\n\n get link(): string {\n return this.#link;\n }\n\n async listen(\n port: number,\n address: string,\n developerKey: DeveloperKey | undefined,\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.#server.once('error', reject);\n this.#server.listen(port, address, () => resolve());\n });\n const { port: listeningPort } = this.#server.address() as AddressInfo;\n const origin = `http://${address}:${listeningPort}`;\n if (developerKey) {\n const expires = Math.floor(Date.now() / 1000) + LINK_LIFETIME_SECONDS;\n const keyId = developerKey.keyId;\n const message = signedLinkMessage(origin, this.#secret, expires, keyId);\n const sig = developerKey.sign(Buffer.from(message, 'utf8'));\n this.#signature = { expires, keyId, sig };\n }\n const server = encodeURIComponent(origin);\n this.#link = `${DEVICE_SCHEME}://connect?server=${server}&secret=${this.#secret}`;\n }\n\n async publish(dir: string, { hot = false }: { hot?: boolean } = {}): Promise<void> {\n this.#files = await readBuild(dir);\n this.#error = null;\n this.#announce();\n if (!hot) {\n this.#hotSince = this.#generation;\n }\n }\n\n fail(message: string): void {\n this.#error = message;\n this.#announce();\n }\n\n close(): Promise<void> {\n this.#wakeWaitingDevices();\n return new Promise((resolve) => {\n this.#server.close(() => resolve());\n this.#server.closeAllConnections();\n });\n }\n\n #announce(): void {\n this.#generation += 1;\n this.#wakeWaitingDevices();\n }\n\n #wakeWaitingDevices(): void {\n for (const wake of this.#waiting) {\n wake();\n }\n }\n\n #handle(request: IncomingMessage, response: ServerResponse): void {\n const from = request.socket.remoteAddress ?? 'a device';\n // Served without the token check: a device needs the signature to check the link before using\n // it, and the signature isn't secret.\n if (request.method === 'GET' && request.url === '/dev/link') {\n this.#sendSignature(response);\n return;\n }\n if (!isAuthorized(request, this.#token)) {\n response.writeHead(401).end();\n return;\n }\n const url = new URL(request.url ?? '/', 'http://device');\n const path = url.pathname;\n if (request.method === 'GET' && path === '/dev/build') {\n this.#sendBuild(response, from);\n } else if (request.method === 'GET' && path.startsWith(FILES_PATH)) {\n this.#sendFile(response, path, url.searchParams.get('generation'));\n } else if (request.method === 'GET' && path === '/dev/changes') {\n this.#waitForChange(response, Number(url.searchParams.get('after')));\n } else if (request.method === 'POST' && path === '/dev/log') {\n this.#receiveLog(request, response, from);\n } else {\n response.writeHead(404).end();\n }\n }\n\n #sendSignature(response: ServerResponse): void {\n if (this.#signature) {\n sendJson(response, this.#signature);\n } else {\n response.writeHead(404).end();\n }\n }\n\n #sendBuild(response: ServerResponse, from: string): void {\n if (!this.#connected.has(from)) {\n this.#connected.add(from);\n this.#onConnect(from);\n }\n const hashes: Record<string, string> = {};\n for (const [name, file] of this.#files) {\n hashes[name] = file.hash;\n }\n const body = JSON.stringify({\n generation: this.#generation,\n hotSince: this.#hotSince,\n files: hashes,\n error: this.#error,\n });\n response\n .writeHead(200, {\n 'content-type': 'application/json',\n [BUILD_MAC_HEADER]: buildMac(this.#secret, body),\n })\n .end(body);\n }\n\n #sendFile(response: ServerResponse, path: string, generation: string | null): void {\n // A file from an older build is refused, so a device never mixes two builds.\n if (generation !== String(this.#generation)) {\n response.writeHead(409).end();\n return;\n }\n const fileName = buildFileName(path);\n if (fileName === undefined) {\n response.writeHead(400).end();\n return;\n }\n const file = this.#files.get(fileName);\n if (!file) {\n response.writeHead(404).end();\n return;\n }\n response.writeHead(200, { 'content-type': 'application/octet-stream' }).end(file.bytes);\n }\n\n // Answers once there is a build newer than `after`, or with no content after WAIT_MS.\n #waitForChange(response: ServerResponse, after: number): void {\n if (this.#generation > after) {\n sendJson(response, { generation: this.#generation });\n return;\n }\n const stopWaiting = (): void => {\n clearTimeout(timer);\n this.#waiting.delete(wake);\n };\n const wake = (): void => {\n stopWaiting();\n sendJson(response, { generation: this.#generation });\n };\n const timer = setTimeout(() => {\n stopWaiting();\n response.writeHead(204).end();\n }, WAIT_MS);\n this.#waiting.add(wake);\n response.on('close', stopWaiting);\n }\n\n #receiveLog(request: IncomingMessage, response: ServerResponse, from: string): void {\n readLogText(request)\n .then((text) => {\n for (const line of text.split('\\n')) {\n this.#onLog(withoutControlCharacters(line), from);\n }\n response.writeHead(204).end();\n })\n .catch(() => response.writeHead(413).end());\n }\n}\n\nfunction hmacWithSecret(secret: string, text: string): string {\n const key = Buffer.from(secret, 'base64url');\n return createHmac('sha256', key).update(text, 'utf8').digest('base64url');\n}\n\n// Interfaces a phone on the same Wi-Fi can't reach: bridges, VPNs, virtual machines and containers.\nconst UNREACHABLE_INTERFACE =\n /^(bridge|utun|tun|tap|ipsec|ppp|awdl|llw|vmnet|vboxnet|docker|br-|veth|virbr|zt|tailscale|wg)/i;\n\n// The computer's IPv4 address on its local network. An address a phone can't reach, such as a\n// Thunderbolt Bridge's, is only used when there's no other.\nexport function localNetworkAddress(\n interfaces: NodeJS.Dict<NetworkInterfaceInfo[]> = networkInterfaces(),\n): string | undefined {\n let fallback: string | undefined;\n for (const [name, addresses] of Object.entries(interfaces)) {\n for (const candidate of addresses ?? []) {\n if (candidate.family !== 'IPv4' || candidate.internal) {\n continue;\n }\n // 169.254 addresses are self-assigned, given when a network has no router to hand one out.\n const selfAssigned = candidate.address.startsWith('169.254.');\n if (!selfAssigned && !UNREACHABLE_INTERFACE.test(name)) {\n return candidate.address;\n }\n fallback ??= candidate.address;\n }\n }\n return fallback;\n}\n\n// Reads the build output into memory, so a device downloading it never gets files from two builds.\nasync function readBuild(dir: string): Promise<Map<string, BuildFile>> {\n const files = new Map<string, BuildFile>();\n const entries = await readdir(dir, { recursive: true, withFileTypes: true });\n for (const entry of entries) {\n // Devices don't need source maps or the archives `dotctl build` writes.\n if (!entry.isFile() || entry.name.endsWith('.map') || entry.name.endsWith('.zip')) {\n continue;\n }\n const path = join(entry.parentPath, entry.name);\n const bytes = await readFile(path);\n const hash = createHash('sha256').update(bytes).digest('base64url');\n const name = relative(dir, path).split(sep).join('/');\n files.set(name, { bytes, hash });\n }\n return files;\n}\n\n// `/dev/files/ui/dot.js` asks for `ui/dot.js`. Each part of the name is URL-encoded, and a badly\n// encoded part, such as a lone `%`, gives undefined.\nfunction buildFileName(path: string): string | undefined {\n const encodedParts = path.slice(FILES_PATH.length).split('/');\n try {\n return encodedParts.map((part) => decodeURIComponent(part)).join('/');\n } catch {\n return undefined;\n }\n}\n\nfunction isAuthorized(request: IncomingMessage, token: string): boolean {\n const given = Buffer.from(request.headers.authorization ?? '');\n const expected = Buffer.from(`Bearer ${token}`);\n // timingSafeEqual throws on buffers of different lengths.\n if (given.length !== expected.length) {\n return false;\n }\n return timingSafeEqual(given, expected);\n}\n\nfunction sendJson(response: ServerResponse, body: unknown): void {\n response.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(body));\n}\n\n// A dot's console.log reaches the terminal, so it must not carry ANSI or OSC escape codes.\nfunction withoutControlCharacters(line: string): string {\n // eslint-disable-next-line no-control-regex\n return line.replace(/[\\u0000-\\u0008\\u000a-\\u001f\\u007f-\\u009f]/g, '');\n}\n\nasync function readLogText(request: IncomingMessage): Promise<string> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const bytes = chunk as Buffer;\n size += bytes.length;\n if (size > MAX_LOG_BYTES) {\n throw new Error(`A device sent more than ${MAX_LOG_BYTES} bytes of log at once`);\n }\n chunks.push(bytes);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n","import { join } from 'node:path';\n\nimport type { Project } from '@monimesl/dot-compiler';\nimport { installedRuntime } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\nimport { startSimulator } from '@monimesl/dot-simulator';\n\n// `dev` and `preview` share .dot/files, so a preview sees the files the dot saved during `dev`.\nexport function startDotSimulator(\n project: Project,\n outDir: string,\n port: number | undefined,\n): Promise<Simulator> {\n return startSimulator({\n dotDir: outDir,\n reactHostDir: installedRuntime(project.root).react.dir,\n name: project.dotJson.name,\n port,\n storageDir: join(project.root, '.dot', 'files'),\n });\n}\n","import { watch } from 'node:fs';\nimport { join, relative, resolve, sep } from 'node:path';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject, PAGES_DIR } from '@monimesl/dot-compiler';\nimport { renderUnicodeCompact } from 'uqr';\n\nimport { loadDeveloperKey, missingDeveloperKeyHint } from './device/developer-key';\nimport type { DeviceServer } from './device/server';\nimport { startDeviceServer } from './device/server';\nimport { describeError, printBuildTime, printSimulatorUrl, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\nimport { resolveOutDir } from './utils';\n\ninterface DevOptions extends BuildOptions {\n port?: number | undefined;\n // Also serve the dot to the example host apps on phones, through a link and a QR code.\n device?: boolean | undefined;\n}\n\n// Editors often save a file in several writes. Waiting this long turns them into one rebuild.\nconst CHANGE_DEBOUNCE_MS = 50;\n\n// Builds, serves and watches the dot. Returns a function that stops it all.\nexport async function dev({ port, device, ...options }: DevOptions): Promise<() => Promise<void>> {\n const project = await loadProject(options.root);\n const outDir = resolveOutDir(project.root, options.outDir);\n const buildOptions: BuildOptions = { mode: 'development', ...options, outDir };\n\n const firstError = await buildDot(buildOptions, project.root);\n const simulator = await startDotSimulator(project, outDir, port);\n if (firstError) {\n simulator.error(firstError);\n }\n printSimulatorUrl(simulator.url);\n\n let deviceServer: DeviceServer | undefined;\n if (device) {\n deviceServer = await startDevices(outDir, firstError);\n }\n\n // Files changed since the last good build. They decide whether the next build can hot reload.\n const changedFiles = new Set<string>();\n const pagesDir = join(project.root, PAGES_DIR) + sep;\n const rebuilds = new RebuildQueue(async () => {\n // Files that change during this build stay in `changedFiles` for the next one.\n const filesInThisBuild = [...changedFiles];\n const error = await buildDot(buildOptions, project.root);\n if (error) {\n simulator.error(error);\n deviceServer?.fail(error);\n return;\n }\n for (const path of filesInThisBuild) {\n changedFiles.delete(path);\n }\n // Page changes hot reload in place. Any other change, such as dot.ts, .env or statics/,\n // restarts the dot on the pages it had open.\n const hot = filesInThisBuild.every((path) => path.startsWith(pagesDir));\n if (hot) {\n simulator.hotReload();\n } else {\n simulator.reload();\n }\n await deviceServer?.publish(outDir, { hot });\n });\n\n let debounceTimer: NodeJS.Timeout | undefined;\n const watcher = watch(project.root, { recursive: true }, (_event, file) => {\n if (!file || isIgnored(file)) {\n return;\n }\n const path = resolve(project.root, file);\n if (path === outDir || path.startsWith(outDir + sep)) {\n return;\n }\n changedFiles.add(path);\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n console.log(`Changed ${relative(project.root, path)}`);\n rebuilds.request();\n }, CHANGE_DEBOUNCE_MS);\n });\n\n return async () => {\n clearTimeout(debounceTimer);\n watcher.close();\n await Promise.all([simulator.close(), deviceServer?.close()]);\n };\n}\n\n// Returns the error to show, with paths relative to the dot, or null if the build worked.\nasync function buildDot(buildOptions: BuildOptions, root: string): Promise<string | null> {\n const startedAt = performance.now();\n try {\n printWarnings(await build(buildOptions));\n printBuildTime(startedAt);\n return null;\n } catch (error) {\n const message = describeError(error).replaceAll(root + sep, '');\n console.error(`error: ${message}`);\n return message;\n }\n}\n\nasync function startDevices(outDir: string, firstError: string | null): Promise<DeviceServer> {\n const developerKey = await loadDeveloperKey();\n const deviceServer = await startDeviceServer({\n developerKey,\n onConnect: (from) => console.log(`A device connected from ${from}`),\n onLog: (line) => console.log(`device ${line}`),\n });\n if (firstError) {\n deviceServer.fail(firstError);\n } else {\n await deviceServer.publish(outDir);\n }\n printDeviceLink(deviceServer.link);\n if (developerKey) {\n console.log(` Signed with developer key ${developerKey.keyId}\\n`);\n } else {\n console.log(`${missingDeveloperKeyHint()}\\n`);\n }\n return deviceServer;\n}\n\nfunction printDeviceLink(link: string): void {\n console.log(\n ' To open it in Dash, turn on Enable Dot Debugging in Developer Settings.\\n' +\n ' Then tap Live Test on the Dots tab and scan this code.\\n',\n );\n const qrCode = renderUnicodeCompact(link, { border: 1 });\n console.log(qrCode.replace(/^/gm, ' '));\n console.log(`\\n ${link}\\n`);\n}\n\n// Runs one rebuild at a time. Changes during a build lead to exactly one more build after it.\nclass RebuildQueue {\n readonly #rebuild: () => Promise<void>;\n #building = false;\n #changedWhileBuilding = false;\n\n constructor(rebuild: () => Promise<void>) {\n this.#rebuild = rebuild;\n }\n\n request(): void {\n if (this.#building) {\n this.#changedWhileBuilding = true;\n return;\n }\n void this.#build();\n }\n\n async #build(): Promise<void> {\n this.#building = true;\n this.#changedWhileBuilding = false;\n try {\n await this.#rebuild();\n } finally {\n this.#building = false;\n }\n if (this.#changedWhileBuilding) {\n this.request();\n }\n }\n}\n\n// Skips node_modules and hidden folders such as .git or .dot, but not .env files: the build reads\n// them.\nfunction isIgnored(file: string): boolean {\n const segments = file.split(/[\\\\/]/);\n return segments.some((segment) => {\n if (segment === '.env' || segment.startsWith('.env.')) {\n return false;\n }\n return segment === 'node_modules' || segment.startsWith('.');\n });\n}\n","import type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\n\nimport { printBuildTime, printSimulatorUrl, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\nimport { resolveOutDir } from './utils';\n\ninterface PreviewOptions extends BuildOptions {\n port?: number | undefined;\n}\n\n// Runs the `dotctl build` output in the simulator: minified, with only the components its pages\n// use. It doesn't rebuild on change, because a new component in a page wouldn't be in the build.\nexport async function preview({ port, ...options }: PreviewOptions): Promise<Simulator> {\n const project = await loadProject(options.root);\n const outDir = resolveOutDir(project.root, options.outDir);\n const startedAt = performance.now();\n const result = await build({ ...options, outDir, mode: 'production' });\n printWarnings(result);\n printBuildTime(startedAt);\n const simulator = await startDotSimulator(project, outDir, port);\n printSimulatorUrl(simulator.url);\n return simulator;\n}\n","import { emitKeypressEvents } from 'node:readline';\nimport { createInterface } from 'node:readline/promises';\nimport { styleText } from 'node:util';\n\nexport interface Choice<Value extends string> {\n value: Value;\n // Shown dimmed after the value, such as a short description of a template.\n hint?: string;\n}\n\ninterface Terminal {\n input: NodeJS.ReadStream;\n output: NodeJS.WriteStream;\n}\n\ninterface Keypress {\n name?: string;\n ctrl?: boolean;\n}\n\nconst TERMINAL: Terminal = { input: process.stdin, output: process.stdout };\n\nconst ERASE_BELOW = '\\x1b[0J';\nconst HIDE_CURSOR = '\\x1b[?25l';\nconst SHOW_CURSOR = '\\x1b[?25h';\n\n// Lets the person pick one of `choices` with the arrow keys and Enter. The list starts on\n// `fallback`, marked as the default. Without a terminal, it returns `fallback` without asking.\nexport async function select<Value extends string>(\n question: string,\n choices: readonly Choice<Value>[],\n fallback: Value,\n { input, output }: Terminal = TERMINAL,\n): Promise<Value> {\n if (!input.isTTY) {\n return fallback;\n }\n let selectedIndex = choices.findIndex((choice) => choice.value === fallback);\n if (selectedIndex < 0) {\n selectedIndex = 0;\n }\n // Pad the values so their hints line up.\n let valueWidth = 0;\n if (choices.some((choice) => choice.hint)) {\n valueWidth = Math.max(...choices.map((choice) => choice.value.length));\n }\n const formatChoice = (choice: Choice<Value>, index: number): string => {\n const value = choice.value.padEnd(valueWidth);\n const hint = choice.hint ? styleText('dim', ` ${choice.hint}`) : '';\n const defaultMark = choice.value === fallback ? styleText('green', ' (default)') : '';\n if (index === selectedIndex) {\n const pointer = styleText('cyan', '❯');\n return `${pointer} ${styleText(['cyan', 'bold'], value)}${hint}${defaultMark}`;\n }\n return ` ${value}${hint}${defaultMark}`;\n };\n\n // Each draw replaces the one before. First the question and its list, then the question and the\n // answer.\n let drawnLines = 0;\n const draw = (lines: string[]): void => {\n let moveUp = '';\n if (drawnLines > 0) {\n moveUp = cursorUp(drawnLines);\n }\n output.write(`${moveUp}${ERASE_BELOW}${lines.join('\\n')}\\n`);\n drawnLines = lines.length;\n };\n const drawQuestionAndChoices = (): void => {\n const keysHint = styleText('dim', '↑/↓ to move, Enter to choose');\n draw([`${styleText('bold', question)} ${keysHint}`, ...choices.map(formatChoice)]);\n };\n\n emitKeypressEvents(input);\n input.setRawMode(true);\n input.resume();\n output.write(HIDE_CURSOR);\n drawQuestionAndChoices();\n return new Promise((resolve) => {\n const onKeypress = (_text: string, key: Keypress): void => {\n if (key.ctrl && key.name === 'c') {\n output.write(`${SHOW_CURSOR}\\n`);\n process.exit(130);\n }\n if (key.name === 'up' || key.name === 'k') {\n selectedIndex = (selectedIndex - 1 + choices.length) % choices.length;\n drawQuestionAndChoices();\n } else if (key.name === 'down' || key.name === 'j') {\n selectedIndex = (selectedIndex + 1) % choices.length;\n drawQuestionAndChoices();\n } else if (key.name === 'return' || key.name === 'enter') {\n const { value } = choices[selectedIndex]!;\n input.off('keypress', onKeypress);\n input.setRawMode(false);\n input.pause();\n draw([`${styleText('bold', question)} ${styleText('cyan', value)}`]);\n output.write(SHOW_CURSOR);\n resolve(value);\n }\n };\n input.on('keypress', onKeypress);\n });\n}\n\n// A yes or no question, shown as a list. Without a terminal, the answer is always no.\nexport async function confirm(\n question: string,\n fallback: boolean,\n terminal: Terminal = TERMINAL,\n): Promise<boolean> {\n if (!terminal.input.isTTY) {\n return false;\n }\n const choices = [{ value: 'Yes' }, { value: 'No' }] as const;\n const answer = await select(question, choices, fallback ? 'Yes' : 'No', terminal);\n return answer === 'Yes';\n}\n\n// Asks for a typed answer, with an optional `hint` shown dimmed after the question. An empty answer,\n// or no terminal, gives `fallback`, shown as the default.\nexport async function ask(\n question: string,\n fallback?: string,\n hint?: string,\n { input, output }: Terminal = TERMINAL,\n): Promise<string | undefined> {\n if (!input.isTTY) {\n return fallback;\n }\n const hintText = hint ? styleText('dim', `, ${hint}`) : '';\n const defaultMark = fallback ? styleText('dim', ` (default: ${fallback})`) : '';\n const readline = createInterface({ input, output });\n try {\n const answer = await readline.question(\n `${styleText('bold', question)}${hintText}${defaultMark}: `,\n );\n const trimmedAnswer = answer.trim();\n if (trimmedAnswer === '') {\n return fallback;\n }\n return trimmedAnswer;\n } catch (error) {\n // The person pressed Ctrl+C or Ctrl+D.\n if ((error as Error).name === 'AbortError') {\n return undefined;\n }\n throw error;\n } finally {\n readline.close();\n }\n}\n\nfunction cursorUp(lines: number): string {\n return `\\x1b[${lines}A`;\n}\n","import { spawn } from 'node:child_process';\nimport { relative } from 'node:path';\nimport { parseArgs } from 'node:util';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, isDotPackage } from '@monimesl/dot-compiler';\nimport { DEFAULT_PORT } from '@monimesl/dot-simulator';\n\nimport { create, DOT_PACKAGE_RULE, toDisplayName, toPackageName } from './create/create';\nimport { createPage } from './create/page';\nimport type { PackageManager } from './create/package-manager';\nimport {\n installedManagers,\n isPackageManager,\n launchedBy,\n PACKAGE_MANAGERS,\n} from './create/package-manager';\nimport type { Template } from './create/template';\nimport { isTemplate, suggestedDotPackage, TEMPLATE_HINTS, TEMPLATES } from './create/template';\nimport { dev } from './dev';\nimport {\n defaultDeveloperKeyPath,\n loadDeveloperKey,\n missingDeveloperKeyHint,\n} from './device/developer-key';\nimport { preview } from './preview';\nimport { ask, confirm, select } from './prompts';\nimport { describeError, printBuildTime, printFiles, printWarnings } from './report';\n\nconst USAGE = `Usage:\n dotctl create [dir] [--name <name>] [--package <com.mysalone.thing>]\n [--template <starter|showcase>] [--pm <npm|pnpm|yarn|bun>]\n Create a dot. Asks for its name, its package name\n (a reverse domain name you own) and its template:\n starter is one page to build on, showcase has a\n page for each feature\n dotctl create page <path> Add a page to the dot you are in. Writes\n pages/<path>/ with view.dml, state.ts and controller.ts\n dotctl build [root] [options] Build the minified bundles the host app runs\n dotctl dev [root] [options] Run the dot in the browser simulator and rebuild on\n every change\n dotctl preview [root] [options] Build as \\`build\\` does, then run that build in the\n browser simulator, exactly as the host app gets it\n dotctl keys show Show this computer's developer key: its public key\n and key ID. The key signs the links that\n \\`dev --device\\` shows, so Dash will open them\n dotctl lsp [--stdio] Start the .dml language server for editors. It talks\n over stdin and stdout\n\nOptions:\n --out-dir <dir> Output directory (default: <root>/dist)\n --conditions <list> Extra export conditions, separated by commas\n --port <port> Simulator port for \\`dev\\` and \\`preview\\`. If it is taken, the\n next free port is used (default: ${DEFAULT_PORT})\n --device With \\`dev\\`, also serve the dot to the Dot example apps on phones\n on your local network, through a link and QR code. The apps\n reload after each build\n -h, --help Show this message`;\n\n// The flags of `dotctl create`, as typed.\ninterface CreateFlags {\n readonly name: string | undefined;\n readonly dotPackage: string | undefined;\n readonly template: string | undefined;\n readonly packageManager: string | undefined;\n}\n\nasync function main(argv: string[]): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n 'out-dir': { type: 'string' },\n conditions: { type: 'string' },\n port: { type: 'string' },\n device: { type: 'boolean' },\n name: { type: 'string' },\n package: { type: 'string' },\n template: { type: 'string' },\n pm: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n // Editors pass --stdio. The server always uses stdin and stdout, so the flag changes nothing.\n stdio: { type: 'boolean' },\n },\n });\n const [command, target, pagePath] = positionals;\n if (values.help) {\n console.log(USAGE);\n return 0;\n }\n\n switch (command) {\n case 'lsp':\n return serveLanguageServer();\n case 'keys':\n return showKeys(target);\n case 'create':\n if (target === 'page') {\n return addPage(pagePath);\n }\n return createDot(target, {\n name: values.name,\n dotPackage: values.package,\n template: values.template,\n packageManager: values.pm,\n });\n case 'build':\n case 'dev':\n case 'preview':\n break;\n default:\n console.log(USAGE);\n return 1;\n }\n\n let port: number | undefined;\n if (values.port !== undefined) {\n port = Number(values.port);\n if (!Number.isInteger(port)) {\n console.error(`--port must be a whole number, not \"${values.port}\"`);\n return 1;\n }\n }\n const options: BuildOptions = { root: target ?? '.' };\n if (values['out-dir'] !== undefined) {\n options.outDir = values['out-dir'];\n }\n if (values.conditions !== undefined) {\n options.conditions = values.conditions.split(',');\n }\n\n if (command === 'dev') {\n return runDev(options, port, values.device);\n }\n if (command === 'preview') {\n return runPreview(options, port, values.device);\n }\n return runBuild(options);\n}\n\nasync function serveLanguageServer(): Promise<number> {\n // Imported lazily so the other commands start faster.\n const { serveStdio } = await import('./lsp/server');\n serveStdio();\n // Never resolves: the connection exits the process when the editor asks it to.\n return new Promise<number>(() => {});\n}\n\nasync function runDev(\n options: BuildOptions,\n port: number | undefined,\n device: boolean | undefined,\n): Promise<number> {\n const stop = await dev({ ...options, port, device });\n await waitForCtrlC();\n await stop();\n return 0;\n}\n\nasync function runPreview(\n options: BuildOptions,\n port: number | undefined,\n device: boolean | undefined,\n): Promise<number> {\n if (device) {\n console.error('--device only works with dotctl dev');\n return 1;\n }\n const simulator = await preview({ ...options, port });\n await waitForCtrlC();\n await simulator.close();\n return 0;\n}\n\nasync function runBuild(options: BuildOptions): Promise<number> {\n const startedAt = performance.now();\n const result = await build(options);\n printFiles(result.files.filter((file) => !file.endsWith('.map')));\n printWarnings(result);\n printBuildTime(startedAt);\n return 0;\n}\n\nfunction waitForCtrlC(): Promise<void> {\n return new Promise((resolve) => {\n process.once('SIGINT', () => resolve());\n });\n}\n\nasync function showKeys(action: string | undefined): Promise<number> {\n if (action !== 'show') {\n console.error('Usage: dotctl keys show');\n return 1;\n }\n const path = defaultDeveloperKeyPath();\n const key = await loadDeveloperKey(path);\n if (!key) {\n console.error(missingDeveloperKeyHint(path));\n return 1;\n }\n console.log(`\\n Public key: ${key.publicKey}\\n Key ID: ${key.keyId}\\n`);\n return 0;\n}\n\nasync function addPage(typedPath: string | undefined): Promise<number> {\n if (typedPath === undefined) {\n console.error('Give the page a path, such as: dotctl create page cart/checkout');\n return 1;\n }\n const path = trimSlashes(typedPath);\n const { files, listing } = await createPage(path, process.cwd());\n printFiles(files);\n if (listing === 'added') {\n console.log(`\\nListed '${path}' in dot.ts.`);\n } else if (listing === 'manual') {\n console.log(\n `\\nAdd it to the pages in dot.ts so it gets built: defineDot({ pages: [..., '${path}'] })`,\n );\n }\n return 0;\n}\n\n// Removes one leading and one trailing slash, so `/cart/checkout/` is `cart/checkout`.\nfunction trimSlashes(path: string): string {\n let trimmed = path;\n if (trimmed.startsWith('/')) {\n trimmed = trimmed.substring(1);\n }\n if (trimmed.endsWith('/')) {\n trimmed = trimmed.substring(0, trimmed.length - 1);\n }\n return trimmed;\n}\n\nasync function createDot(dir: string | undefined, flags: CreateFlags): Promise<number> {\n const templateFlag = flags.template;\n if (templateFlag !== undefined && !isTemplate(templateFlag)) {\n console.error(`--template must be one of ${TEMPLATES.join(', ')}`);\n return 1;\n }\n const packageManagerFlag = flags.packageManager;\n if (packageManagerFlag !== undefined && !isPackageManager(packageManagerFlag)) {\n console.error(`--pm must be one of ${PACKAGE_MANAGERS.join(', ')}`);\n return 1;\n }\n let name = flags.name;\n if (name === undefined) {\n const suggestedName = dir ? toDisplayName(dir) : 'Dot';\n name = await ask('Dot name', suggestedName);\n }\n if (!name) {\n console.error('The dot needs a name. Pass one with: dotctl create [dir] --name \"My Dot\"');\n return 1;\n }\n const dotPackageFlag = flags.dotPackage;\n if (dotPackageFlag !== undefined && !isDotPackage(dotPackageFlag)) {\n console.error(`--package \"${dotPackageFlag}\" isn't a package name. ${DOT_PACKAGE_RULE}`);\n return 1;\n }\n let dotPackage = dotPackageFlag;\n if (dotPackage === undefined) {\n dotPackage = await askDotPackage(suggestedDotPackage(toPackageName(name)));\n }\n if (!dotPackage) {\n return 1;\n }\n let template = templateFlag;\n if (template === undefined) {\n template = await chooseTemplate();\n }\n\n const { root, workspace } = await create({\n name,\n dir: dir ?? toPackageName(name),\n dotPackage,\n template,\n });\n let path = relative(process.cwd(), root);\n if (path === '') {\n path = '.';\n }\n console.log(`\\nCreated ${name} in ${path}`);\n if (workspace) {\n console.log('It is inside the Dot repository, so it uses the workspace packages.');\n }\n\n // A command can't change its shell's folder, so it offers to install and start the dot from here.\n let packageManager: PackageManager;\n if (workspace) {\n packageManager = 'pnpm';\n } else {\n packageManager = packageManagerFlag ?? launchedBy();\n }\n console.log();\n if (await confirm('Install and start it now?', true)) {\n if (!workspace && !packageManagerFlag) {\n packageManager = await choosePackageManager(packageManager);\n }\n const installExitCode = await runCommand(packageManager, ['install'], root);\n if (installExitCode === 0) {\n return runCommand(packageManager, ['run', 'dev'], root);\n }\n console.error(\n `\\n${packageManager} install failed. Fix the problem above, then run the steps below.`,\n );\n }\n console.log(\n `\\nNext:\\n\\n cd ${path}\\n ${packageManager} install\\n ${packageManager} run dev\\n`,\n );\n return 0;\n}\n\n// Runs a command in `cwd`, showing its output, and returns its exit code.\nfunction runCommand(command: string, args: string[], cwd: string): Promise<number> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n child.on('error', reject);\n // A child killed by a signal has no exit code. Count that as a failure.\n child.on('exit', (exitCode) => resolve(exitCode ?? 1));\n });\n}\n\n// Asks again until the answer is a package name. Undefined when the person stops with Ctrl+C.\nasync function askDotPackage(suggested: string): Promise<string | undefined> {\n while (true) {\n const answer = await ask('Package name', suggested, 'a reverse domain name you own');\n if (answer === undefined || isDotPackage(answer)) {\n return answer;\n }\n console.error(`\"${answer}\" isn't a package name. ${DOT_PACKAGE_RULE}`);\n }\n}\n\nfunction chooseTemplate(): Promise<Template> {\n console.log();\n const choices = TEMPLATES.map((template) => ({\n value: template,\n hint: TEMPLATE_HINTS[template],\n }));\n return select('Template?', choices, 'starter');\n}\n\nasync function choosePackageManager(suggested: PackageManager): Promise<PackageManager> {\n const installed = installedManagers();\n if (installed.length < 2) {\n return installed[0] ?? suggested;\n }\n let fallback = installed[0]!;\n if (installed.includes(suggested)) {\n fallback = suggested;\n }\n const choices = installed.map((manager) => ({ value: manager }));\n return select('Package manager?', choices, fallback);\n}\n\nmain(process.argv.slice(2)).then(\n (exitCode) => process.exit(exitCode),\n (error: unknown) => {\n console.error(`error: ${describeError(error)}`);\n process.exit(1);\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/report.ts","../src/utils.ts","../src/create/template.ts","../src/create/create.ts","../src/create/page.ts","../src/create/package-manager.ts","../src/device/developer-key.ts","../src/device/server.ts","../src/simulator.ts","../src/dev.ts","../src/preview.ts","../src/prompts.ts","../src/cli.ts"],"sourcesContent":["import { relative } from 'node:path';\n\nimport { DmlParseError } from '@monimesl/dot-dml-parser';\nimport type { BuildResult } from '@monimesl/dot-compiler';\nimport { CompileError } from '@monimesl/dot-compiler';\nimport { ScriptParseError } from '@monimesl/dot-script-parser';\n\n// A mistake in how a command was used. Its message is enough, so no stack trace is shown.\nexport class CommandError extends Error {\n override readonly name = 'CommandError';\n}\n\nexport function describeError(error: unknown): string {\n const known =\n error instanceof CommandError ||\n error instanceof CompileError ||\n error instanceof DmlParseError ||\n error instanceof ScriptParseError;\n if (known) {\n return error.message;\n }\n if (isArgumentError(error)) {\n // Keep only the first sentence. Node goes on to explain `--`, which doesn't help here.\n const [firstSentence] = error.message.split('. ');\n return `${firstSentence}. See dotctl --help.`;\n }\n if (error instanceof Error) {\n return error.stack ?? error.message;\n }\n return String(error);\n}\n\n// Node's parseArgs throws these for an unknown flag or a missing value.\nfunction isArgumentError(error: unknown): error is Error {\n if (!(error instanceof Error) || !('code' in error)) {\n return false;\n }\n return typeof error.code === 'string' && error.code.startsWith('ERR_PARSE_ARGS');\n}\n\n// Prints each file relative to the current folder.\nexport function printFiles(files: readonly string[]): void {\n for (const file of files) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n}\n\nexport function printWarnings({ warnings }: BuildResult): void {\n for (const warning of warnings) {\n console.warn(`warning: ${warning.message}`);\n }\n}\n\nexport function printBuildTime(startedAt: number): void {\n console.log(`Built in ${Math.round(performance.now() - startedAt)}ms`);\n}\n\nexport function printSimulatorUrl(url: string): void {\n console.log(`\\n Simulator running at: ${url}\\n`);\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\n\nimport { DOT_JSON_FILE } from '@monimesl/dot-compiler';\n\n// The nearest folder that has a file named `fileName`, starting at `dir` and walking up.\nexport function findFolderWith(fileName: string, dir: string): string | undefined {\n let current = dir;\n while (true) {\n if (existsSync(join(current, fileName))) {\n return current;\n }\n const parent = dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n}\n\nexport function findDotRoot(dir: string): string | undefined {\n return findFolderWith(DOT_JSON_FILE, dir);\n}\n\n// Where `dev` and `preview` write the build: `--out-dir` if given, else dist/ in the dot.\nexport function resolveOutDir(root: string, outDir: string | undefined): string {\n return resolve(root, outDir ?? 'dist');\n}\n\nexport function isOneOf<Value extends string>(\n values: readonly Value[],\n name: string,\n): name is Value {\n return (values as readonly string[]).includes(name);\n}\n","import { DEFAULT_DASH_API_VERSION } from '@monimesl/dot-core';\n\nimport { isOneOf } from '../utils';\n\nexport const TEMPLATES = ['starter', 'showcase'] as const;\n\nexport type Template = (typeof TEMPLATES)[number];\n\n// Shown next to each template when `dotctl create` asks which one to use.\nexport const TEMPLATE_HINTS: Record<Template, string> = {\n starter: 'one page to build on',\n showcase: 'a page for each feature, to learn from',\n};\n\nexport function isTemplate(name: string): name is Template {\n return isOneOf(TEMPLATES, name);\n}\n\ninterface TemplateOptions {\n name: string;\n // The npm package name, such as `my-dot`.\n packageName: string;\n // The dot's own package name, a reverse domain name such as `com.mysalone.thing`.\n dotPackage: string;\n dotVersion: string;\n workspace: boolean;\n template: Template;\n}\n\nexport const PAGE_FILES: Readonly<Record<string, string>> = {\n 'view.dml': '<View>\\n\\t<Text>My new page</Text>\\n</View>\\n',\n 'state.ts': 'export default {};\\n',\n 'controller.ts': `import state from './state';\n\nexport default definePage({\n initialState: state,\n});\n`,\n};\n\n// The files `dotctl create` copies from the template's example. It generates the rest.\nexport const TEMPLATE_FILES = ['dot.ts', 'pages', 'statics', '.env'] as const;\n\n// Editors that follow `$schema` can check dot.json and complete its fields. unpkg serves the schema\n// from the runtime's latest release on npm, and picks up a new release within minutes.\nconst DOT_JSON_SCHEMA = 'https://unpkg.com/@monimesl/dot-runtime/dot.schema.json';\n\n// Stands in for the ID Monime gives the dot when it's registered.\nexport const UNREGISTERED_DOT_ID = '<monime-Internal-Id>';\n\n// The files `dotctl create` writes itself, by path.\nexport function generatedFiles(options: TemplateOptions): Record<string, string> {\n return {\n 'package.json': packageJson(options),\n 'tsconfig.json': tsconfig(options),\n '.gitignore': 'node_modules/\\ndist/\\n# Types generated by dotctl\\n.dot/\\n',\n 'dot.json': dotJson(options),\n 'README.md': readme(options),\n };\n}\n\nfunction packageJson({ packageName, dotVersion, workspace }: TemplateOptions): string {\n const conditions = workspace ? ' --conditions @monimesl/dot-source' : '';\n return formatJson({\n name: packageName,\n private: true,\n type: 'module',\n scripts: {\n dev: `dotctl dev${conditions}`,\n build: `dotctl build${conditions}`,\n preview: `dotctl preview${conditions}`,\n typecheck: 'tsc --noEmit',\n },\n dependencies: { '@monimesl/dot-runtime': dotVersion },\n devDependencies: { '@monimesl/dot-cli': dotVersion, typescript: '^5.9.3' },\n });\n}\n\nfunction tsconfig({ workspace }: TemplateOptions): string {\n const compilerOptions: Record<string, unknown> = {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['@monimesl/dot-runtime/globals'],\n strict: true,\n noEmit: true,\n skipLibCheck: true,\n verbatimModuleSyntax: true,\n };\n if (workspace) {\n compilerOptions.customConditions = ['@monimesl/dot-source'];\n }\n return formatJson({\n compilerOptions,\n include: ['dot.ts', 'pages', '.dot/pages.d.ts', '.dot/env.d.ts'],\n });\n}\n\nfunction dotJson({ name, dotPackage, template }: TemplateOptions): string {\n const dot: Record<string, unknown> = {\n id: UNREGISTERED_DOT_ID,\n $schema: DOT_JSON_SCHEMA,\n package: dotPackage,\n name,\n version: '0.1.0',\n };\n if (template === 'showcase') {\n // The dash.media, dash.storage.file, dash.contacts, dash.account and dash.payments pages ask for these.\n dot.permissions = {\n account: 'Show your name on your concert ticket',\n camera: \"Take a photo for your concert ticket, or scan a ticket's QR code\",\n photoLibrary: 'Choose a photo for your concert ticket',\n files: 'Attach your concert ticket',\n contacts: 'Choose a friend to send a ticket to',\n payments: 'Pay for concert tickets',\n };\n // The showcase's network page sends requests to these hosts.\n dot.network = { hosts: ['itunes.apple.com', '*.mzstatic.com', 'httpbin.org'] };\n }\n dot.dash = { minApiVersion: DEFAULT_DASH_API_VERSION };\n return formatJson(dot);\n}\n\nconst TEMPLATE_READMES: Readonly<Record<Template, string>> = {\n starter: `It starts with one page, \\`index\\`. It shows a greeting from \\`.env\\` and a counter you\ncan tap. Change it, and add pages as the dot grows. To see how each feature works, make a showcase\ndot with \\`npx dotctl create my-tour --template showcase\\`.`,\n showcase: `It starts as a showcase. The \\`index\\` page links to a page for each feature, in three\nfolders: \\`pages/basics/\\` for how a page works, \\`pages/components/\\` for what a view can show,\nand \\`pages/dash/\\` for the phone's features. Read a page to see how it works. Then delete the\npages you don't need, both from \\`pages/\\` and from \\`dot.ts\\`.`,\n};\n\nfunction readme({ name, template }: TemplateOptions): string {\n return `# ${name}\n\nA dot: a mini app that runs inside Dash.\n\n\\`\\`\\`sh\nnpm install\nnpm run dev # run it in the simulator; it reloads on every change\nnpm run build # build it for Dash\n\\`\\`\\`\n\n${TEMPLATE_READMES[template]}\n\n## Pages\n\nEach page is a folder under \\`pages/\\` with three files:\n\n- \\`view.dml\\`: what the page shows. It has one root \\`<View>\\`, with everything else inside it.\n- \\`state.ts\\`: the data the page starts with.\n- \\`controller.ts\\`: how the data changes.\n\n\\`npx dotctl create page <path>\\` adds a page, such as \\`cart/checkout\\`, with these three files. It\nalso lists the page in \\`dot.ts\\`. The dot opens on the first page in that list.\n\n## dot.json\n\n\\`dot.json\\` describes the dot to Dash. Its \\`package\\` is a reverse domain name you own, such as\n\\`com.mysalone.thing\\`. You can't change it after publishing. To publish, register the dot with\nMonime first, and replace its \\`id\\`, \\`${UNREGISTERED_DOT_ID}\\`, with the ID Monime gives you.\n\\`network.hosts\\` lists every host the dot sends requests to with \\`dash.network\\`. Requests to any\nother host fail. Your editor can complete the other fields from the schema the file points to.\n\n## Which Dash opens it\n\n\\`dash.minApiVersion\\` in \\`dot.json\\` is the Dash API version the dot needs, such as \\`1.0\\`. Dash\nopens the dot when it offers that version, or a newer minor of the same major. Each Dash call's docs\nsay which version it came in, after \\`@since\\`. To use a newer call and still open on older Dash,\nkeep \\`minApiVersion\\` and check \\`this.dash.supports('contacts.pickPhone')\\` before the call.\n\\`dotctl dev\\` warns when the dot makes a newer call without checking.\n\n## .env\n\n\\`.env\\` holds public configuration. Code in the dot reads each variable from \\`env\\`, such as\n\\`env.GREETING\\`. Every variable is built into the dot, so anyone who has the dot can read it.\nNever put a secret there. \\`.env.development\\` and \\`.env.production\\` override it for \\`dev\\` and\n\\`build\\`.\n`;\n}\n\n// The package name to suggest, such as `com.example.my-dot` for the npm package `my-dot`. The npm\n// name is already lowercase letters, digits and inner hyphens. A part of a host name can be at most\n// 63 characters, so it's cut there.\nexport function suggestedDotPackage(packageName: string): string {\n return `com.example.${packageName.slice(0, 63).replace(/-+$/, '')}`;\n}\n\nfunction formatJson(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { cp, mkdir, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { basename, dirname, join, resolve } from 'node:path';\n\nimport { isDotPackage } from '@monimesl/dot-compiler';\n\nimport { CommandError } from '../report';\nimport { findFolderWith } from '../utils';\nimport type { Template } from './template';\nimport { generatedFiles, suggestedDotPackage, TEMPLATE_FILES } from './template';\n\ninterface CreateOptions {\n name: string;\n dir: string;\n // A reverse domain name, such as `com.mysalone.thing`. Defaults to `com.example.<npm name>`.\n dotPackage?: string;\n template?: Template;\n}\n\ninterface CreateResult {\n readonly root: string;\n readonly workspace: boolean;\n}\n\ninterface PackageJson {\n name: string;\n version: string;\n}\n\nexport const DOT_PACKAGE_RULE = 'Use lowercase words joined by dots, such as com.mysalone.thing';\n\nexport async function create({\n name,\n dir,\n dotPackage,\n template = 'starter',\n}: CreateOptions): Promise<CreateResult> {\n const root = resolve(dir);\n if (existsSync(root) && readdirSync(root).length > 0) {\n throw new CommandError(`${dir} already exists and is not empty. Choose another folder`);\n }\n const packageName = toPackageName(name);\n if (!packageName) {\n throw new CommandError(`The name \"${name}\" needs at least one letter or digit`);\n }\n dotPackage ??= suggestedDotPackage(packageName);\n if (!isDotPackage(dotPackage)) {\n throw new CommandError(`\"${dotPackage}\" isn't a package name. ${DOT_PACKAGE_RULE}`);\n }\n\n const workspace = isInDotWorkspace(root);\n const source = templateDir(template);\n await mkdir(root, { recursive: true });\n for (const path of TEMPLATE_FILES) {\n await cp(join(source, path), join(root, path), { recursive: true });\n }\n const generated = generatedFiles({\n name,\n packageName,\n dotPackage,\n dotVersion: workspace ? 'workspace:*' : `^${cliVersion()}`,\n workspace,\n template,\n });\n for (const [path, content] of Object.entries(generated)) {\n await writeFile(join(root, path), content);\n }\n return { root, workspace };\n}\n\nexport function toPackageName(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nexport function toDisplayName(dir: string): string {\n return basename(resolve(dir))\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n// Whether the nearest pnpm workspace above `dir` is the Dot repository.\nexport function isInDotWorkspace(dir: string): boolean {\n const workspaceRoot = findFolderWith('pnpm-workspace.yaml', dirname(dir));\n if (workspaceRoot === undefined) {\n return false;\n }\n const runtimeManifest = join(workspaceRoot, 'packages/runtime/package.json');\n if (!existsSync(runtimeManifest)) {\n return false;\n }\n return readPackageJson(runtimeManifest).name === '@monimesl/dot-runtime';\n}\n\n// Inside the repository, templates come from examples/. The published CLI ships its own copy.\nfunction templateDir(template: Template): string {\n const example = join(cliRoot(), '../../examples', template);\n if (existsSync(example)) {\n return example;\n }\n return join(cliRoot(), 'dist/templates', template);\n}\n\nfunction cliVersion(): string {\n return readPackageJson(join(cliRoot(), 'package.json')).version;\n}\n\nfunction readPackageJson(path: string): PackageJson {\n return JSON.parse(readFileSync(path, 'utf8')) as PackageJson;\n}\n\nfunction cliRoot(): string {\n return dirname(createRequire(import.meta.url).resolve('@monimesl/dot-cli/package.json'));\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { DOT_FILE, PAGES_DIR } from '@monimesl/dot-compiler';\nimport { isPageId } from '@monimesl/dot-core';\nimport type { DotModuleIR } from '@monimesl/dot-script-parser';\nimport { parseDotModule } from '@monimesl/dot-script-parser';\n\nimport { CommandError } from '../report';\nimport { findDotRoot } from '../utils';\nimport { PAGE_FILES } from './template';\n\n// Whether the page is now in `defineDot({ pages })` in dot.ts. `manual` means dot.ts couldn't be\n// updated, so the developer must add the page by hand.\ntype PageListing = 'added' | 'already' | 'manual';\n\ninterface CreatePageResult {\n readonly dir: string;\n readonly files: readonly string[];\n readonly listing: PageListing;\n}\n\n// Adds a page folder under pages/ in the dot that contains `cwd`, and lists it last in dot.ts.\nexport async function createPage(path: string, cwd: string): Promise<CreatePageResult> {\n if (!isPageId(path)) {\n throw new CommandError(\n `\"${path}\" is not a page path. Use letters, digits, \"-\" and \"_\", with \"/\" between folders, such as \"cart/checkout\"`,\n );\n }\n const root = findDotRoot(resolve(cwd));\n if (root === undefined) {\n throw new CommandError(\n 'There is no dot.json in this folder or any folder above it. Run this command inside a dot',\n );\n }\n const dir = join(root, PAGES_DIR, path);\n if (existsSync(dir)) {\n throw new CommandError(`pages/${path} already exists. Choose another path`);\n }\n await mkdir(dir, { recursive: true });\n const files: string[] = [];\n for (const [name, content] of Object.entries(PAGE_FILES)) {\n const file = join(dir, name);\n await writeFile(file, content);\n files.push(file);\n }\n const listing = await addToDotPages(join(root, DOT_FILE), path);\n return { dir, files, listing };\n}\n\nasync function addToDotPages(dotFile: string, path: string): Promise<PageListing> {\n if (!existsSync(dotFile)) {\n return 'manual';\n }\n const source = await readFile(dotFile, 'utf8');\n let dot: DotModuleIR;\n try {\n dot = parseDotModule(source, dotFile);\n } catch {\n return 'manual';\n }\n if (dot.pages.includes(path)) {\n return 'already';\n }\n const updated = insertPage(source, dot.lastPage, path);\n if (!listsPage(updated, dotFile, path)) {\n return 'manual';\n }\n await writeFile(dotFile, updated);\n return 'added';\n}\n\n// Inserts `path` after the last page, matching the list's style: same quotes, and on its own line\n// if each page has one.\nfunction insertPage(source: string, lastPage: DotModuleIR['lastPage'], path: string): string {\n const quote = source[lastPage.start] === '\"' ? '\"' : \"'\";\n const page = `${quote}${path}${quote}`;\n const before = source.slice(0, lastPage.end);\n const after = source.slice(lastPage.end);\n\n const lineStart = source.lastIndexOf('\\n', lastPage.start - 1) + 1;\n const indent = source.slice(lineStart, lastPage.start);\n const lastPageHasOwnLine = /^\\s*$/.test(indent);\n if (!lastPageHasOwnLine) {\n return `${before}, ${page}${after}`;\n }\n const trailingComma = /^\\s*,/.exec(after);\n if (trailingComma) {\n const end = lastPage.end + trailingComma[0].length;\n return `${source.slice(0, end)}\\n${indent}${page},${source.slice(end)}`;\n }\n return `${before},\\n${indent}${page}${after}`;\n}\n\n// Whether `source`, the edited dot.ts, still parses and lists `path`.\nfunction listsPage(source: string, dotFile: string, path: string): boolean {\n try {\n return parseDotModule(source, dotFile).pages.includes(path);\n } catch {\n return false;\n }\n}\n","import { spawnSync } from 'node:child_process';\n\nimport { isOneOf } from '../utils';\n\nexport const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'] as const;\nexport type PackageManager = (typeof PACKAGE_MANAGERS)[number];\n\nexport function isPackageManager(name: string): name is PackageManager {\n return isOneOf(PACKAGE_MANAGERS, name);\n}\n\nexport function launchedBy(userAgent = process.env.npm_config_user_agent ?? ''): PackageManager {\n return PACKAGE_MANAGERS.find((name) => userAgent.startsWith(`${name}/`)) ?? 'npm';\n}\n\nexport function installedManagers(\n isInstalled: (name: PackageManager) => boolean = isOnPath,\n): PackageManager[] {\n return PACKAGE_MANAGERS.filter(isInstalled);\n}\n\nfunction isOnPath(name: PackageManager): boolean {\n const result = spawnSync(name, ['--version'], {\n stdio: 'ignore',\n shell: process.platform === 'win32',\n });\n return result.status === 0;\n}\n","import { createPrivateKey, createPublicKey, sign } from 'node:crypto';\nimport type { KeyObject } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nimport { CommandError } from '../report';\n\n// The Ed25519 key this computer signs dotdev links with. The developer adds its public key to their\n// Monime account. Before Dash opens a link, Monime checks its signature.\nexport interface DeveloperKey {\n // The raw 32-byte public key in base64. This is what the developer adds to their account.\n readonly publicKey: string;\n // The first 8 bytes of the public key in lowercase hex. A link names its key by this ID.\n readonly keyId: string;\n // Returns the signature in base64.\n sign(bytes: Buffer): string;\n}\n\nexport function defaultDeveloperKeyPath(): string {\n return process.env.DOT_DEVELOPER_KEY ?? join(homedir(), '.dot', 'developer-key.pem');\n}\n\n// Explains how to make a key. dotctl reads the key but never creates it.\nexport function missingDeveloperKeyHint(path = defaultDeveloperKeyPath()): string {\n return (\n ` There is no developer key at ${path}. Dash only opens signed links.\\n` +\n \" Make one with OpenSSL 3. macOS's built-in openssl can't, so first run brew install openssl:\\n\\n\" +\n ` mkdir -p ${dirname(path)} && openssl genpkey -algorithm ed25519 -out ${path}\\n\\n` +\n ' Then run `dotctl keys show` and add the public key it prints to your Monime account.'\n );\n}\n\n// Returns undefined when there is no key file yet.\nexport async function loadDeveloperKey(\n path = defaultDeveloperKeyPath(),\n): Promise<DeveloperKey | undefined> {\n let pem: string;\n try {\n pem = await readFile(path, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return undefined;\n }\n throw error;\n }\n const privateKey = createPrivateKey(pem);\n if (privateKey.asymmetricKeyType !== 'ed25519') {\n throw new CommandError(\n `${path} isn't an Ed25519 key. Make one with: openssl genpkey -algorithm ed25519 -out ${path}`,\n );\n }\n return toDeveloperKey(privateKey);\n}\n\nfunction toDeveloperKey(privateKey: KeyObject): DeveloperKey {\n const publicKeyJwk = createPublicKey(privateKey).export({ format: 'jwk' });\n const rawPublicKey = Buffer.from(publicKeyJwk.x!, 'base64url');\n return {\n publicKey: rawPublicKey.toString('base64'),\n keyId: rawPublicKey.subarray(0, 8).toString('hex'),\n sign: (bytes) => sign(null, bytes, privateKey).toString('base64'),\n };\n}\n","import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\nimport { readdir, readFile } from 'node:fs/promises';\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http';\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport type { NetworkInterfaceInfo } from 'node:os';\nimport { networkInterfaces } from 'node:os';\nimport { join, relative, sep } from 'node:path';\n\nimport { CommandError } from '../report';\nimport type { DeveloperKey } from './developer-key';\n\ninterface DeviceServerOptions {\n // The address devices reach the server on. Defaults to the computer's local network address.\n address?: string | undefined;\n // Defaults to any free port. The link tells devices which one.\n port?: number | undefined;\n // Called for each line a device sends, such as its dot's console output.\n onLog?: (line: string, from: string) => void;\n onConnect?: (from: string) => void;\n // Signs the link. Hosts such as Dash only open links signed by a known developer.\n developerKey?: DeveloperKey | undefined;\n}\n\n// Serves a dot's builds to phones, for Dash's Live Test and the example apps. Phones reload on\n// each new build.\nexport interface DeviceServer {\n // The link a device opens: `dotdev://connect?server=…&secret=…`. It is kept short so the QR\n // code stays small. A signed link's signature is served at `GET /dev/link` instead.\n readonly link: string;\n // Serves the build output in `dir` as the newest build. A `hot` build changed only pages, so\n // devices can reload it in place and keep each open page's state.\n publish(dir: string, options?: { hot?: boolean }): Promise<void>;\n // Tells devices the newest build failed. They keep running the last good one.\n fail(message: string): void;\n close(): Promise<void>;\n}\n\n// A signed link's signature, served at `GET /dev/link`. It isn't secret, so it stays out of the QR\n// code.\nexport interface LinkSignature {\n readonly expires: number;\n readonly keyId: string;\n // In base64.\n readonly sig: string;\n}\n\ninterface BuildFile {\n readonly bytes: Buffer;\n readonly hash: string;\n}\n\nconst DEVICE_SCHEME = 'dotdev';\nconst FILES_PATH = '/dev/files/';\n\n// A waiting request ends after this and the device asks again, before the network drops the idle\n// connection.\nconst WAIT_MS = 25_000;\nconst MAX_LOG_BYTES = 1024 * 1024;\n// How long a signed link works. A photo of an old QR code soon becomes useless.\nconst LINK_LIFETIME_SECONDS = 12 * 60 * 60;\n\nexport const BUILD_MAC_HEADER = 'dot-build-mac';\n\n// The text a signed link's `sig` signs, one field per line.\nexport function signedLinkMessage(\n server: string,\n secret: string,\n expires: number,\n keyId: string,\n): string {\n return ['dotdev-connect', server, secret, String(expires), keyId].join('\\n');\n}\n\n// The bearer token a device sends. It is derived from the link's secret, so the link doesn't need\n// to carry it. Someone who sees the token on the network learns nothing about the secret.\nexport function deviceToken(secret: string): string {\n return hmacWithSecret(secret, 'dotdev-token');\n}\n\n// The HMAC-SHA256 of a build listing, keyed with the link's secret. A device only accepts listings\n// from someone who has the link, and each file must match its hash in the listing.\nexport function buildMac(secret: string, body: string): string {\n return hmacWithSecret(secret, `dotdev-build\\n${body}`);\n}\n\nexport async function startDeviceServer({\n address = localNetworkAddress(),\n port = 0,\n onLog = () => {},\n onConnect = () => {},\n developerKey,\n}: DeviceServerOptions = {}): Promise<DeviceServer> {\n if (address === undefined) {\n throw new CommandError(\n \"This computer has no local network address, so phones can't reach it. Connect to Wi-Fi and try again\",\n );\n }\n const deviceServer = new LocalDeviceServer(onLog, onConnect);\n await deviceServer.listen(port, address, developerKey);\n return deviceServer;\n}\n\nclass LocalDeviceServer implements DeviceServer {\n readonly #onLog: (line: string, from: string) => void;\n readonly #onConnect: (from: string) => void;\n // The secret never goes over the network. Only the link carries it.\n readonly #secret = randomBytes(16).toString('base64url');\n readonly #token = deviceToken(this.#secret);\n readonly #server: Server;\n readonly #waiting = new Set<() => void>();\n readonly #connected = new Set<string>();\n #link = '';\n #signature: LinkSignature | null = null;\n #files: ReadonlyMap<string, BuildFile> = new Map();\n #generation = 0;\n // The oldest generation from which only hot builds lead to the newest. A device on this\n // generation or a later one can hot reload to the newest build.\n #hotSince = 0;\n #error: string | null = null;\n\n constructor(onLog: (line: string, from: string) => void, onConnect: (from: string) => void) {\n this.#onLog = onLog;\n this.#onConnect = onConnect;\n this.#server = createServer((request, response) => {\n this.#handle(request, response);\n });\n }\n\n get link(): string {\n return this.#link;\n }\n\n async listen(\n port: number,\n address: string,\n developerKey: DeveloperKey | undefined,\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n this.#server.once('error', reject);\n this.#server.listen(port, address, () => resolve());\n });\n const { port: listeningPort } = this.#server.address() as AddressInfo;\n const origin = `http://${address}:${listeningPort}`;\n if (developerKey) {\n const expires = Math.floor(Date.now() / 1000) + LINK_LIFETIME_SECONDS;\n const keyId = developerKey.keyId;\n const message = signedLinkMessage(origin, this.#secret, expires, keyId);\n const sig = developerKey.sign(Buffer.from(message, 'utf8'));\n this.#signature = { expires, keyId, sig };\n }\n const server = encodeURIComponent(origin);\n this.#link = `${DEVICE_SCHEME}://connect?server=${server}&secret=${this.#secret}`;\n }\n\n async publish(dir: string, { hot = false }: { hot?: boolean } = {}): Promise<void> {\n this.#files = await readBuild(dir);\n this.#error = null;\n this.#announce();\n if (!hot) {\n this.#hotSince = this.#generation;\n }\n }\n\n fail(message: string): void {\n this.#error = message;\n this.#announce();\n }\n\n close(): Promise<void> {\n this.#wakeWaitingDevices();\n return new Promise((resolve) => {\n this.#server.close(() => resolve());\n this.#server.closeAllConnections();\n });\n }\n\n #announce(): void {\n this.#generation += 1;\n this.#wakeWaitingDevices();\n }\n\n #wakeWaitingDevices(): void {\n for (const wake of this.#waiting) {\n wake();\n }\n }\n\n #handle(request: IncomingMessage, response: ServerResponse): void {\n const from = request.socket.remoteAddress ?? 'a device';\n // Served without the token check: a device needs the signature to check the link before using\n // it, and the signature isn't secret.\n if (request.method === 'GET' && request.url === '/dev/link') {\n this.#sendSignature(response);\n return;\n }\n if (!isAuthorized(request, this.#token)) {\n response.writeHead(401).end();\n return;\n }\n const url = new URL(request.url ?? '/', 'http://device');\n const path = url.pathname;\n if (request.method === 'GET' && path === '/dev/build') {\n this.#sendBuild(response, from);\n } else if (request.method === 'GET' && path.startsWith(FILES_PATH)) {\n this.#sendFile(response, path, url.searchParams.get('generation'));\n } else if (request.method === 'GET' && path === '/dev/changes') {\n this.#waitForChange(response, Number(url.searchParams.get('after')));\n } else if (request.method === 'POST' && path === '/dev/log') {\n this.#receiveLog(request, response, from);\n } else {\n response.writeHead(404).end();\n }\n }\n\n #sendSignature(response: ServerResponse): void {\n if (this.#signature) {\n sendJson(response, this.#signature);\n } else {\n response.writeHead(404).end();\n }\n }\n\n #sendBuild(response: ServerResponse, from: string): void {\n if (!this.#connected.has(from)) {\n this.#connected.add(from);\n this.#onConnect(from);\n }\n const hashes: Record<string, string> = {};\n for (const [name, file] of this.#files) {\n hashes[name] = file.hash;\n }\n const body = JSON.stringify({\n generation: this.#generation,\n hotSince: this.#hotSince,\n files: hashes,\n error: this.#error,\n });\n response\n .writeHead(200, {\n 'content-type': 'application/json',\n [BUILD_MAC_HEADER]: buildMac(this.#secret, body),\n })\n .end(body);\n }\n\n #sendFile(response: ServerResponse, path: string, generation: string | null): void {\n // A file from an older build is refused, so a device never mixes two builds.\n if (generation !== String(this.#generation)) {\n response.writeHead(409).end();\n return;\n }\n const fileName = buildFileName(path);\n if (fileName === undefined) {\n response.writeHead(400).end();\n return;\n }\n const file = this.#files.get(fileName);\n if (!file) {\n response.writeHead(404).end();\n return;\n }\n response.writeHead(200, { 'content-type': 'application/octet-stream' }).end(file.bytes);\n }\n\n // Answers once there is a build newer than `after`, or with no content after WAIT_MS.\n #waitForChange(response: ServerResponse, after: number): void {\n if (this.#generation > after) {\n sendJson(response, { generation: this.#generation });\n return;\n }\n const stopWaiting = (): void => {\n clearTimeout(timer);\n this.#waiting.delete(wake);\n };\n const wake = (): void => {\n stopWaiting();\n sendJson(response, { generation: this.#generation });\n };\n const timer = setTimeout(() => {\n stopWaiting();\n response.writeHead(204).end();\n }, WAIT_MS);\n this.#waiting.add(wake);\n response.on('close', stopWaiting);\n }\n\n #receiveLog(request: IncomingMessage, response: ServerResponse, from: string): void {\n readLogText(request)\n .then((text) => {\n for (const line of text.split('\\n')) {\n this.#onLog(withoutControlCharacters(line), from);\n }\n response.writeHead(204).end();\n })\n .catch(() => response.writeHead(413).end());\n }\n}\n\nfunction hmacWithSecret(secret: string, text: string): string {\n const key = Buffer.from(secret, 'base64url');\n return createHmac('sha256', key).update(text, 'utf8').digest('base64url');\n}\n\n// Interfaces a phone on the same Wi-Fi can't reach: bridges, VPNs, virtual machines and containers.\nconst UNREACHABLE_INTERFACE =\n /^(bridge|utun|tun|tap|ipsec|ppp|awdl|llw|vmnet|vboxnet|docker|br-|veth|virbr|zt|tailscale|wg)/i;\n\n// The computer's IPv4 address on its local network. An address a phone can't reach, such as a\n// Thunderbolt Bridge's, is only used when there's no other.\nexport function localNetworkAddress(\n interfaces: NodeJS.Dict<NetworkInterfaceInfo[]> = networkInterfaces(),\n): string | undefined {\n let fallback: string | undefined;\n for (const [name, addresses] of Object.entries(interfaces)) {\n for (const candidate of addresses ?? []) {\n if (candidate.family !== 'IPv4' || candidate.internal) {\n continue;\n }\n // 169.254 addresses are self-assigned, given when a network has no router to hand one out.\n const selfAssigned = candidate.address.startsWith('169.254.');\n if (!selfAssigned && !UNREACHABLE_INTERFACE.test(name)) {\n return candidate.address;\n }\n fallback ??= candidate.address;\n }\n }\n return fallback;\n}\n\n// Reads the build output into memory, so a device downloading it never gets files from two builds.\nasync function readBuild(dir: string): Promise<Map<string, BuildFile>> {\n const files = new Map<string, BuildFile>();\n const entries = await readdir(dir, { recursive: true, withFileTypes: true });\n for (const entry of entries) {\n // Devices don't need source maps or the archives `dotctl build` writes.\n if (!entry.isFile() || entry.name.endsWith('.map') || entry.name.endsWith('.zip')) {\n continue;\n }\n const path = join(entry.parentPath, entry.name);\n const bytes = await readFile(path);\n const hash = createHash('sha256').update(bytes).digest('base64url');\n const name = relative(dir, path).split(sep).join('/');\n files.set(name, { bytes, hash });\n }\n return files;\n}\n\n// `/dev/files/ui/dot.js` asks for `ui/dot.js`. Each part of the name is URL-encoded, and a badly\n// encoded part, such as a lone `%`, gives undefined.\nfunction buildFileName(path: string): string | undefined {\n const encodedParts = path.slice(FILES_PATH.length).split('/');\n try {\n return encodedParts.map((part) => decodeURIComponent(part)).join('/');\n } catch {\n return undefined;\n }\n}\n\nfunction isAuthorized(request: IncomingMessage, token: string): boolean {\n const given = Buffer.from(request.headers.authorization ?? '');\n const expected = Buffer.from(`Bearer ${token}`);\n // timingSafeEqual throws on buffers of different lengths.\n if (given.length !== expected.length) {\n return false;\n }\n return timingSafeEqual(given, expected);\n}\n\nfunction sendJson(response: ServerResponse, body: unknown): void {\n response.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(body));\n}\n\n// A dot's console.log reaches the terminal, so it must not carry ANSI or OSC escape codes.\nfunction withoutControlCharacters(line: string): string {\n // eslint-disable-next-line no-control-regex\n return line.replace(/[\\u0000-\\u0008\\u000a-\\u001f\\u007f-\\u009f]/g, '');\n}\n\nasync function readLogText(request: IncomingMessage): Promise<string> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const bytes = chunk as Buffer;\n size += bytes.length;\n if (size > MAX_LOG_BYTES) {\n throw new Error(`A device sent more than ${MAX_LOG_BYTES} bytes of log at once`);\n }\n chunks.push(bytes);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n","import { join } from 'node:path';\n\nimport type { Project } from '@monimesl/dot-compiler';\nimport { installedRuntime } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\nimport { startSimulator } from '@monimesl/dot-simulator';\n\n// `dev` and `preview` share .dot/files, so a preview sees the files the dot saved during `dev`.\nexport function startDotSimulator(\n project: Project,\n outDir: string,\n port: number | undefined,\n): Promise<Simulator> {\n return startSimulator({\n dotDir: outDir,\n reactHostDir: installedRuntime(project.root).react.dir,\n name: project.dotJson.name,\n port,\n storageDir: join(project.root, '.dot', 'files'),\n });\n}\n","import { watch } from 'node:fs';\nimport { join, relative, resolve, sep } from 'node:path';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject, PAGES_DIR } from '@monimesl/dot-compiler';\nimport { renderUnicodeCompact } from 'uqr';\n\nimport { loadDeveloperKey, missingDeveloperKeyHint } from './device/developer-key';\nimport type { DeviceServer } from './device/server';\nimport { startDeviceServer } from './device/server';\nimport { describeError, printBuildTime, printSimulatorUrl, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\nimport { resolveOutDir } from './utils';\n\ninterface DevOptions extends BuildOptions {\n port?: number | undefined;\n // Also serve the dot to phones, for Dash's Live Test and the example apps.\n device?: boolean | undefined;\n}\n\n// Editors often save a file in several writes. Waiting this long turns them into one rebuild.\nconst CHANGE_DEBOUNCE_MS = 50;\n\n// Builds, serves and watches the dot. Returns a function that stops it all.\nexport async function dev({ port, device, ...options }: DevOptions): Promise<() => Promise<void>> {\n const project = await loadProject(options.root);\n const outDir = resolveOutDir(project.root, options.outDir);\n const buildOptions: BuildOptions = { mode: 'development', ...options, outDir };\n\n const firstError = await buildDot(buildOptions, project.root);\n const simulator = await startDotSimulator(project, outDir, port);\n if (firstError) {\n simulator.error(firstError);\n }\n printSimulatorUrl(simulator.url);\n\n let deviceServer: DeviceServer | undefined;\n if (device) {\n deviceServer = await startDevices(outDir, firstError);\n }\n\n // Files changed since the last good build. They decide whether the next build can hot reload.\n const changedFiles = new Set<string>();\n const pagesDir = join(project.root, PAGES_DIR) + sep;\n const rebuilds = new RebuildQueue(async () => {\n // Files that change during this build stay in `changedFiles` for the next one.\n const filesInThisBuild = [...changedFiles];\n const error = await buildDot(buildOptions, project.root);\n if (error) {\n simulator.error(error);\n deviceServer?.fail(error);\n return;\n }\n for (const path of filesInThisBuild) {\n changedFiles.delete(path);\n }\n // Page changes hot reload in place. Any other change, such as dot.ts, .env or statics/,\n // restarts the dot on the pages it had open.\n const hot = filesInThisBuild.every((path) => path.startsWith(pagesDir));\n if (hot) {\n simulator.hotReload();\n } else {\n simulator.reload();\n }\n await deviceServer?.publish(outDir, { hot });\n });\n\n let debounceTimer: NodeJS.Timeout | undefined;\n const watcher = watch(project.root, { recursive: true }, (_event, file) => {\n if (!file || isIgnored(file)) {\n return;\n }\n const path = resolve(project.root, file);\n if (path === outDir || path.startsWith(outDir + sep)) {\n return;\n }\n changedFiles.add(path);\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n console.log(`Changed ${relative(project.root, path)}`);\n rebuilds.request();\n }, CHANGE_DEBOUNCE_MS);\n });\n\n return async () => {\n clearTimeout(debounceTimer);\n watcher.close();\n await Promise.all([simulator.close(), deviceServer?.close()]);\n };\n}\n\n// Returns the error to show, with paths relative to the dot, or null if the build worked.\nasync function buildDot(buildOptions: BuildOptions, root: string): Promise<string | null> {\n const startedAt = performance.now();\n try {\n printWarnings(await build(buildOptions));\n printBuildTime(startedAt);\n return null;\n } catch (error) {\n const message = describeError(error).replaceAll(root + sep, '');\n console.error(`error: ${message}`);\n return message;\n }\n}\n\nasync function startDevices(outDir: string, firstError: string | null): Promise<DeviceServer> {\n const developerKey = await loadDeveloperKey();\n const deviceServer = await startDeviceServer({\n developerKey,\n onConnect: (from) => console.log(`A device connected from ${from}`),\n onLog: (line) => console.log(`device ${line}`),\n });\n if (firstError) {\n deviceServer.fail(firstError);\n } else {\n await deviceServer.publish(outDir);\n }\n printDeviceLink(deviceServer.link);\n if (developerKey) {\n console.log(` Signed with developer key ${developerKey.keyId}\\n`);\n } else {\n console.log(`${missingDeveloperKeyHint()}\\n`);\n }\n return deviceServer;\n}\n\nfunction printDeviceLink(link: string): void {\n console.log(\n ' To open it in Dash, turn on Enable Dot Debugging in Developer Settings.\\n' +\n ' Then tap Live Test on the Dots tab and scan this code.\\n',\n );\n const qrCode = renderUnicodeCompact(link, { border: 1 });\n console.log(qrCode.replace(/^/gm, ' '));\n console.log(`\\n ${link}\\n`);\n}\n\n// Runs one rebuild at a time. Changes during a build lead to exactly one more build after it.\nclass RebuildQueue {\n readonly #rebuild: () => Promise<void>;\n #building = false;\n #changedWhileBuilding = false;\n\n constructor(rebuild: () => Promise<void>) {\n this.#rebuild = rebuild;\n }\n\n request(): void {\n if (this.#building) {\n this.#changedWhileBuilding = true;\n return;\n }\n void this.#build();\n }\n\n async #build(): Promise<void> {\n this.#building = true;\n this.#changedWhileBuilding = false;\n try {\n await this.#rebuild();\n } finally {\n this.#building = false;\n }\n if (this.#changedWhileBuilding) {\n this.request();\n }\n }\n}\n\n// Skips node_modules and hidden folders such as .git or .dot, but not .env files, which the build\n// reads.\nfunction isIgnored(file: string): boolean {\n const segments = file.split(/[\\\\/]/);\n return segments.some((segment) => {\n if (segment === '.env' || segment.startsWith('.env.')) {\n return false;\n }\n return segment === 'node_modules' || segment.startsWith('.');\n });\n}\n","import type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\n\nimport { printBuildTime, printSimulatorUrl, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\nimport { resolveOutDir } from './utils';\n\ninterface PreviewOptions extends BuildOptions {\n port?: number | undefined;\n}\n\n// Runs the `dotctl build` output in the simulator: minified, with only the components its pages\n// use. It doesn't rebuild on change, because a new component in a page wouldn't be in the build.\nexport async function preview({ port, ...options }: PreviewOptions): Promise<Simulator> {\n const project = await loadProject(options.root);\n const outDir = resolveOutDir(project.root, options.outDir);\n const startedAt = performance.now();\n const result = await build({ ...options, outDir, mode: 'production' });\n printWarnings(result);\n printBuildTime(startedAt);\n const simulator = await startDotSimulator(project, outDir, port);\n printSimulatorUrl(simulator.url);\n return simulator;\n}\n","import { emitKeypressEvents } from 'node:readline';\nimport { createInterface } from 'node:readline/promises';\nimport { styleText } from 'node:util';\n\nexport interface Choice<Value extends string> {\n value: Value;\n // Shown dimmed after the value, such as a short description of a template.\n hint?: string;\n}\n\ninterface Terminal {\n input: NodeJS.ReadStream;\n output: NodeJS.WriteStream;\n}\n\ninterface Keypress {\n name?: string;\n ctrl?: boolean;\n}\n\nconst TERMINAL: Terminal = { input: process.stdin, output: process.stdout };\n\nconst ERASE_BELOW = '\\x1b[0J';\nconst HIDE_CURSOR = '\\x1b[?25l';\nconst SHOW_CURSOR = '\\x1b[?25h';\n\n// Lets the person pick one of `choices` with the arrow keys and Enter. The list starts on\n// `fallback`, marked as the default. Without a terminal, it returns `fallback` without asking.\nexport async function select<Value extends string>(\n question: string,\n choices: readonly Choice<Value>[],\n fallback: Value,\n { input, output }: Terminal = TERMINAL,\n): Promise<Value> {\n if (!input.isTTY) {\n return fallback;\n }\n let selectedIndex = choices.findIndex((choice) => choice.value === fallback);\n if (selectedIndex < 0) {\n selectedIndex = 0;\n }\n // Pad the values so their hints line up.\n let valueWidth = 0;\n if (choices.some((choice) => choice.hint)) {\n valueWidth = Math.max(...choices.map((choice) => choice.value.length));\n }\n const formatChoice = (choice: Choice<Value>, index: number): string => {\n const value = choice.value.padEnd(valueWidth);\n const hint = choice.hint ? styleText('dim', ` ${choice.hint}`) : '';\n const defaultMark = choice.value === fallback ? styleText('green', ' (default)') : '';\n if (index === selectedIndex) {\n const pointer = styleText('cyan', '❯');\n return `${pointer} ${styleText(['cyan', 'bold'], value)}${hint}${defaultMark}`;\n }\n return ` ${value}${hint}${defaultMark}`;\n };\n\n // Each draw replaces the one before. First the question and its list, then the question and the\n // answer.\n let drawnLines = 0;\n const draw = (lines: string[]): void => {\n let moveUp = '';\n if (drawnLines > 0) {\n moveUp = cursorUp(drawnLines);\n }\n output.write(`${moveUp}${ERASE_BELOW}${lines.join('\\n')}\\n`);\n drawnLines = lines.length;\n };\n const drawQuestionAndChoices = (): void => {\n const keysHint = styleText('dim', '↑/↓ to move, Enter to choose');\n draw([`${styleText('bold', question)} ${keysHint}`, ...choices.map(formatChoice)]);\n };\n\n emitKeypressEvents(input);\n input.setRawMode(true);\n input.resume();\n output.write(HIDE_CURSOR);\n drawQuestionAndChoices();\n return new Promise((resolve) => {\n const onKeypress = (_text: string, key: Keypress): void => {\n if (key.ctrl && key.name === 'c') {\n output.write(`${SHOW_CURSOR}\\n`);\n process.exit(130);\n }\n if (key.name === 'up' || key.name === 'k') {\n selectedIndex = (selectedIndex - 1 + choices.length) % choices.length;\n drawQuestionAndChoices();\n } else if (key.name === 'down' || key.name === 'j') {\n selectedIndex = (selectedIndex + 1) % choices.length;\n drawQuestionAndChoices();\n } else if (key.name === 'return' || key.name === 'enter') {\n const { value } = choices[selectedIndex]!;\n input.off('keypress', onKeypress);\n input.setRawMode(false);\n input.pause();\n draw([`${styleText('bold', question)} ${styleText('cyan', value)}`]);\n output.write(SHOW_CURSOR);\n resolve(value);\n }\n };\n input.on('keypress', onKeypress);\n });\n}\n\n// A yes or no question, shown as a list. Without a terminal, the answer is always no.\nexport async function confirm(\n question: string,\n fallback: boolean,\n terminal: Terminal = TERMINAL,\n): Promise<boolean> {\n if (!terminal.input.isTTY) {\n return false;\n }\n const choices = [{ value: 'Yes' }, { value: 'No' }] as const;\n const answer = await select(question, choices, fallback ? 'Yes' : 'No', terminal);\n return answer === 'Yes';\n}\n\n// Asks for a typed answer, with an optional `hint` shown dimmed after the question. An empty answer,\n// or no terminal, gives `fallback`, shown as the default.\nexport async function ask(\n question: string,\n fallback?: string,\n hint?: string,\n { input, output }: Terminal = TERMINAL,\n): Promise<string | undefined> {\n if (!input.isTTY) {\n return fallback;\n }\n const hintText = hint ? styleText('dim', `, ${hint}`) : '';\n const defaultMark = fallback ? styleText('dim', ` (default: ${fallback})`) : '';\n const readline = createInterface({ input, output });\n try {\n const answer = await readline.question(\n `${styleText('bold', question)}${hintText}${defaultMark}: `,\n );\n const trimmedAnswer = answer.trim();\n if (trimmedAnswer === '') {\n return fallback;\n }\n return trimmedAnswer;\n } catch (error) {\n // The person pressed Ctrl+C or Ctrl+D.\n if ((error as Error).name === 'AbortError') {\n return undefined;\n }\n throw error;\n } finally {\n readline.close();\n }\n}\n\nfunction cursorUp(lines: number): string {\n return `\\x1b[${lines}A`;\n}\n","import { spawn } from 'node:child_process';\nimport { relative } from 'node:path';\nimport { parseArgs } from 'node:util';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, isDotPackage } from '@monimesl/dot-compiler';\nimport { DEFAULT_PORT } from '@monimesl/dot-simulator';\n\nimport { create, DOT_PACKAGE_RULE, toDisplayName, toPackageName } from './create/create';\nimport { createPage } from './create/page';\nimport type { PackageManager } from './create/package-manager';\nimport {\n installedManagers,\n isPackageManager,\n launchedBy,\n PACKAGE_MANAGERS,\n} from './create/package-manager';\nimport type { Template } from './create/template';\nimport { isTemplate, suggestedDotPackage, TEMPLATE_HINTS, TEMPLATES } from './create/template';\nimport { dev } from './dev';\nimport {\n defaultDeveloperKeyPath,\n loadDeveloperKey,\n missingDeveloperKeyHint,\n} from './device/developer-key';\nimport { preview } from './preview';\nimport { ask, confirm, select } from './prompts';\nimport { describeError, printBuildTime, printFiles, printWarnings } from './report';\n\nconst USAGE = `Usage:\n dotctl create [dir] [--name <name>] [--package <com.mysalone.thing>]\n [--template <starter|showcase>] [--pm <npm|pnpm|yarn|bun>]\n Create a dot. Asks for its name, its package name\n (a reverse domain name you own) and its template:\n starter is one page to build on, showcase has a\n page for each feature\n dotctl create page <path> Add a page to the dot you are in. Writes\n pages/<path>/ with view.dml, state.ts and controller.ts\n dotctl build [root] [options] Build the dot for Dash: minified bundles in dist/, and\n the ZIP that Dash downloads\n dotctl dev [root] [options] Run the dot in the browser simulator and rebuild on\n every change\n dotctl preview [root] [options] Build as \\`build\\` does, then run that build in the\n browser simulator, exactly as Dash gets it\n dotctl keys show Show this computer's developer key: its public key\n and key ID. The key signs the links that\n \\`dev --device\\` shows, so Dash opens them\n dotctl lsp [--stdio] Start the .dml language server for editors. It talks\n over stdin and stdout\n\nOptions:\n --out-dir <dir> Output directory (default: <root>/dist)\n --conditions <list> Extra export conditions, separated by commas\n --port <port> Simulator port for \\`dev\\` and \\`preview\\`. If it is taken, the\n next free port is used (default: ${DEFAULT_PORT})\n --device With \\`dev\\`, also serve the dot to phones on your local network,\n through a link and QR code. Open it with Live Test in Dash, or\n with a Dot example app. Phones reload after each build\n -h, --help Show this message`;\n\n// The flags of `dotctl create`, as typed.\ninterface CreateFlags {\n readonly name: string | undefined;\n readonly dotPackage: string | undefined;\n readonly template: string | undefined;\n readonly packageManager: string | undefined;\n}\n\nasync function main(argv: string[]): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n 'out-dir': { type: 'string' },\n conditions: { type: 'string' },\n port: { type: 'string' },\n device: { type: 'boolean' },\n name: { type: 'string' },\n package: { type: 'string' },\n template: { type: 'string' },\n pm: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n // Editors pass --stdio. The server always uses stdin and stdout, so the flag changes nothing.\n stdio: { type: 'boolean' },\n },\n });\n const [command, target, pagePath] = positionals;\n if (values.help) {\n console.log(USAGE);\n return 0;\n }\n\n switch (command) {\n case 'lsp':\n return serveLanguageServer();\n case 'keys':\n return showKeys(target);\n case 'create':\n if (target === 'page') {\n return addPage(pagePath);\n }\n return createDot(target, {\n name: values.name,\n dotPackage: values.package,\n template: values.template,\n packageManager: values.pm,\n });\n case 'build':\n case 'dev':\n case 'preview':\n break;\n default:\n if (command !== undefined) {\n console.error(`There is no \"${command}\" command. See the commands below.\\n`);\n }\n console.log(USAGE);\n return 1;\n }\n\n let port: number | undefined;\n if (values.port !== undefined) {\n port = Number(values.port);\n if (!Number.isInteger(port)) {\n console.error(`--port must be a whole number, not \"${values.port}\"`);\n return 1;\n }\n }\n const options: BuildOptions = { root: target ?? '.' };\n if (values['out-dir'] !== undefined) {\n options.outDir = values['out-dir'];\n }\n if (values.conditions !== undefined) {\n options.conditions = values.conditions.split(',');\n }\n\n if (command === 'dev') {\n return runDev(options, port, values.device);\n }\n if (command === 'preview') {\n return runPreview(options, port, values.device);\n }\n return runBuild(options);\n}\n\nasync function serveLanguageServer(): Promise<number> {\n // Imported lazily so the other commands start faster.\n const { serveStdio } = await import('./lsp/server');\n serveStdio();\n // Never resolves: the connection exits the process when the editor asks it to.\n return new Promise<number>(() => {});\n}\n\nasync function runDev(\n options: BuildOptions,\n port: number | undefined,\n device: boolean | undefined,\n): Promise<number> {\n const stop = await dev({ ...options, port, device });\n await waitForCtrlC();\n await stop();\n return 0;\n}\n\nasync function runPreview(\n options: BuildOptions,\n port: number | undefined,\n device: boolean | undefined,\n): Promise<number> {\n if (device) {\n console.error('--device only works with dotctl dev. Run dotctl dev --device instead');\n return 1;\n }\n const simulator = await preview({ ...options, port });\n await waitForCtrlC();\n await simulator.close();\n return 0;\n}\n\nasync function runBuild(options: BuildOptions): Promise<number> {\n const startedAt = performance.now();\n const result = await build(options);\n printFiles(result.files.filter((file) => !file.endsWith('.map')));\n printWarnings(result);\n printBuildTime(startedAt);\n return 0;\n}\n\nfunction waitForCtrlC(): Promise<void> {\n return new Promise((resolve) => {\n process.once('SIGINT', () => resolve());\n });\n}\n\nasync function showKeys(action: string | undefined): Promise<number> {\n if (action !== 'show') {\n console.error(\"Run dotctl keys show to see this computer's developer key\");\n return 1;\n }\n const path = defaultDeveloperKeyPath();\n const key = await loadDeveloperKey(path);\n if (!key) {\n console.error(missingDeveloperKeyHint(path));\n return 1;\n }\n console.log(`\\n Public key: ${key.publicKey}\\n Key ID: ${key.keyId}\\n`);\n return 0;\n}\n\nasync function addPage(typedPath: string | undefined): Promise<number> {\n if (typedPath === undefined) {\n console.error('Give the page a path, such as: dotctl create page cart/checkout');\n return 1;\n }\n const path = trimSlashes(typedPath);\n const { files, listing } = await createPage(path, process.cwd());\n printFiles(files);\n if (listing === 'added') {\n console.log(`\\nListed '${path}' in dot.ts.`);\n } else if (listing === 'manual') {\n console.log(\n `\\nAdd it to the pages in dot.ts so it gets built: defineDot({ pages: [..., '${path}'] })`,\n );\n }\n return 0;\n}\n\n// Removes one leading and one trailing slash, so `/cart/checkout/` is `cart/checkout`.\nfunction trimSlashes(path: string): string {\n let trimmed = path;\n if (trimmed.startsWith('/')) {\n trimmed = trimmed.substring(1);\n }\n if (trimmed.endsWith('/')) {\n trimmed = trimmed.substring(0, trimmed.length - 1);\n }\n return trimmed;\n}\n\nasync function createDot(dir: string | undefined, flags: CreateFlags): Promise<number> {\n const templateFlag = flags.template;\n if (templateFlag !== undefined && !isTemplate(templateFlag)) {\n console.error(`--template must be one of ${TEMPLATES.join(', ')}`);\n return 1;\n }\n const packageManagerFlag = flags.packageManager;\n if (packageManagerFlag !== undefined && !isPackageManager(packageManagerFlag)) {\n console.error(`--pm must be one of ${PACKAGE_MANAGERS.join(', ')}`);\n return 1;\n }\n let name = flags.name;\n if (name === undefined) {\n const suggestedName = dir ? toDisplayName(dir) : 'Dot';\n name = await ask('Dot name', suggestedName);\n }\n if (!name) {\n console.error('The dot needs a name. Pass one with: dotctl create [dir] --name \"My Dot\"');\n return 1;\n }\n const dotPackageFlag = flags.dotPackage;\n if (dotPackageFlag !== undefined && !isDotPackage(dotPackageFlag)) {\n console.error(`--package \"${dotPackageFlag}\" isn't a package name. ${DOT_PACKAGE_RULE}`);\n return 1;\n }\n let dotPackage = dotPackageFlag;\n if (dotPackage === undefined) {\n dotPackage = await askDotPackage(suggestedDotPackage(toPackageName(name)));\n }\n if (!dotPackage) {\n return 1;\n }\n let template = templateFlag;\n if (template === undefined) {\n template = await chooseTemplate();\n }\n\n const { root, workspace } = await create({\n name,\n dir: dir ?? toPackageName(name),\n dotPackage,\n template,\n });\n let path = relative(process.cwd(), root);\n if (path === '') {\n path = '.';\n }\n console.log(`\\nCreated ${name} in ${path}`);\n if (workspace) {\n console.log('It is inside the Dot repository, so it uses the workspace packages.');\n }\n\n // A command can't change its shell's folder, so it offers to install and start the dot from here.\n let packageManager: PackageManager;\n if (workspace) {\n packageManager = 'pnpm';\n } else {\n packageManager = packageManagerFlag ?? launchedBy();\n }\n console.log();\n if (await confirm('Install and start it now?', true)) {\n if (!workspace && !packageManagerFlag) {\n packageManager = await choosePackageManager(packageManager);\n }\n const installExitCode = await runCommand(packageManager, ['install'], root);\n if (installExitCode === 0) {\n return runCommand(packageManager, ['run', 'dev'], root);\n }\n console.error(\n `\\n${packageManager} install failed. Fix the problem above, then run the steps below.`,\n );\n }\n console.log(\n `\\nNext:\\n\\n cd ${path}\\n ${packageManager} install\\n ${packageManager} run dev\\n`,\n );\n return 0;\n}\n\n// Runs a command in `cwd`, showing its output, and returns its exit code.\nfunction runCommand(command: string, args: string[], cwd: string): Promise<number> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n child.on('error', reject);\n // A child killed by a signal has no exit code. Count that as a failure.\n child.on('exit', (exitCode) => resolve(exitCode ?? 1));\n });\n}\n\n// Asks again until the answer is a package name. Undefined when the person stops with Ctrl+C.\nasync function askDotPackage(suggested: string): Promise<string | undefined> {\n while (true) {\n const answer = await ask('Package name', suggested, 'a reverse domain name you own');\n if (answer === undefined || isDotPackage(answer)) {\n return answer;\n }\n console.error(`\"${answer}\" isn't a package name. ${DOT_PACKAGE_RULE}`);\n }\n}\n\nfunction chooseTemplate(): Promise<Template> {\n console.log();\n const choices = TEMPLATES.map((template) => ({\n value: template,\n hint: TEMPLATE_HINTS[template],\n }));\n return select('Template?', choices, 'starter');\n}\n\nasync function choosePackageManager(suggested: PackageManager): Promise<PackageManager> {\n const installed = installedManagers();\n if (installed.length < 2) {\n return installed[0] ?? suggested;\n }\n let fallback = installed[0]!;\n if (installed.includes(suggested)) {\n fallback = suggested;\n }\n const choices = installed.map((manager) => ({ value: manager }));\n return select('Package manager?', choices, fallback);\n}\n\nmain(process.argv.slice(2)).then(\n (exitCode) => process.exit(exitCode),\n (error: unknown) => {\n console.error(`error: ${describeError(error)}`);\n process.exit(1);\n },\n);\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
# Public
|
|
2
|
-
#
|
|
1
|
+
# Public settings for the dot. Code reads each one from `env`, such as `env.GREETING`.
|
|
2
|
+
# They're built into the dot, so anyone who has it can read them. Never put a secret here.
|
|
3
3
|
GREETING=Welcome to Salone Sounds. This message comes from the dot's .env file.
|