@monimesl/dot-cli 0.1.0-alpha.34 → 0.1.0-alpha.36

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/dist/cli.js CHANGED
@@ -575,7 +575,7 @@ var LocalDeviceServer = class {
575
575
  }
576
576
  #receiveLog(request, response, from) {
577
577
  readLogText(request).then((text) => {
578
- for (const line of text.split("\n")) this.#onLog(line, from);
578
+ for (const line of text.split("\n")) this.#onLog(withoutControlCharacters(line), from);
579
579
  response.writeHead(204).end();
580
580
  }).catch(() => response.writeHead(413).end());
581
581
  }
@@ -623,6 +623,9 @@ function isAuthorized(request, token) {
623
623
  function sendJson(response, body) {
624
624
  response.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(body));
625
625
  }
626
+ function withoutControlCharacters(line) {
627
+ return line.replace(/[\u0000-\u0008\u000a-\u001f\u007f-\u009f]/g, "");
628
+ }
626
629
  async function readLogText(request) {
627
630
  const chunks = [];
628
631
  let size = 0;
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 and dash.account 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 };\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 { 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(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// The first IPv4 address that isn't a loopback address.\nfunction localNetworkAddress(): string | undefined {\n for (const addresses of Object.values(networkInterfaces())) {\n for (const candidate of addresses ?? []) {\n if (candidate.family === 'IPv4' && !candidate.internal) {\n return candidate.address;\n }\n }\n }\n return undefined;\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\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 and dash.account 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 };\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 { 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// The first IPv4 address that isn't a loopback address.\nfunction localNetworkAddress(): string | undefined {\n for (const addresses of Object.values(networkInterfaces())) {\n for (const candidate of addresses ?? []) {\n if (candidate.family === 'IPv4' && !candidate.internal) {\n return candidate.address;\n }\n }\n }\n return undefined;\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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"}
@@ -4,7 +4,7 @@ import state from './state';
4
4
 
5
5
  export default definePage({
6
6
  initialState: state,
7
- configs: { navigationBar: { title: 'dash.account' } },
7
+ configs: { navigationBar: { title: 'Account' } },
8
8
  mutations: {
9
9
  setNonce: (_state, event: ValueEvent) => ({ nonce: event.detail.value }),
10
10
  },
@@ -6,7 +6,7 @@ const RECENT_MILLISECONDS = 30_000;
6
6
 
7
7
  export default definePage({
8
8
  initialState: state,
9
- configs: { navigationBar: { title: 'dash.storage.cache' } },
9
+ configs: { navigationBar: { title: 'Cache' } },
10
10
  hooks: {
11
11
  async onShow() {
12
12
  await this.actions.refresh();
@@ -4,7 +4,7 @@ import state from './state';
4
4
 
5
5
  export default definePage({
6
6
  initialState: state,
7
- configs: { navigationBar: { title: 'dash.contacts' } },
7
+ configs: { navigationBar: { title: 'Contacts' } },
8
8
  actions: {
9
9
  async choose() {
10
10
  try {
@@ -4,7 +4,7 @@ import state from './state';
4
4
 
5
5
  export default definePage({
6
6
  initialState: state,
7
- configs: { navigationBar: { title: 'dash.crypto' } },
7
+ configs: { navigationBar: { title: 'Crypto' } },
8
8
  mutations: {
9
9
  setText: (_state, event: ValueEvent) => ({ text: event.detail.value }),
10
10
  setKey: (_state, event: ValueEvent) => ({ key: event.detail.value }),
@@ -5,7 +5,7 @@ const PICTURE = 'pictures/artwork.jpg';
5
5
 
6
6
  export default definePage({
7
7
  initialState: state,
8
- configs: { navigationBar: { title: 'dash.storage.file' } },
8
+ configs: { navigationBar: { title: 'Files' } },
9
9
  hooks: {
10
10
  async onShow() {
11
11
  await this.actions.refresh();
@@ -5,7 +5,7 @@ import state from './state';
5
5
 
6
6
  export default definePage({
7
7
  initialState: state,
8
- configs: { navigationBar: { title: 'dash.media' } },
8
+ configs: { navigationBar: { title: 'Photos, files and scanning' } },
9
9
  // unlessCancelled gives undefined when the person closes the picker or scanner.
10
10
  actions: {
11
11
  async pickPhoto(_state, event: DataEvent<{ source: PhotoSource }>) {
@@ -40,7 +40,7 @@ const artistSearch = latest();
40
40
 
41
41
  export default definePage({
42
42
  initialState: state,
43
- configs: { navigationBar: { title: 'dash.network' } },
43
+ configs: { navigationBar: { title: 'Network' } },
44
44
  mutations: {
45
45
  setArtist: (_state, event: ValueEvent) => ({ artist: event.detail.value }),
46
46
  },
@@ -5,7 +5,7 @@ const NAME = 'session-name';
5
5
 
6
6
  export default definePage({
7
7
  initialState: state,
8
- configs: { navigationBar: { title: 'dash.storage.secret' } },
8
+ configs: { navigationBar: { title: 'Secrets' } },
9
9
  hooks: {
10
10
  async onShow() {
11
11
  await this.actions.refresh();
@@ -5,7 +5,7 @@ import state from './state';
5
5
 
6
6
  export default definePage({
7
7
  initialState: state,
8
- configs: { navigationBar: { title: 'dash.system' } },
8
+ configs: { navigationBar: { title: 'System' } },
9
9
  mutations: {
10
10
  clearToast: () => ({ toast: '' }),
11
11
  setNumber: (_state, event: ValueEvent) => ({ number: event.detail.value }),
@@ -4,7 +4,7 @@ import state from './state';
4
4
 
5
5
  export default definePage({
6
6
  initialState: state,
7
- configs: { navigationBar: { title: 'dash.ui' } },
7
+ configs: { navigationBar: { title: 'Dialogs and pickers' } },
8
8
  actions: {
9
9
  async greet() {
10
10
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monimesl/dot-cli",
3
- "version": "0.1.0-alpha.34",
3
+ "version": "0.1.0-alpha.36",
4
4
  "description": "The dotctl command: build mini apps and run them in the simulator",
5
5
  "type": "module",
6
6
  "repository": {
@@ -24,11 +24,11 @@
24
24
  "dependencies": {
25
25
  "typescript": "^5.9.3",
26
26
  "uqr": "^0.1.3",
27
- "@monimesl/dot-compiler": "0.1.0-alpha.34",
28
- "@monimesl/dot-core": "0.1.0-alpha.34",
29
- "@monimesl/dot-dml-parser": "0.1.0-alpha.34",
30
- "@monimesl/dot-script-parser": "0.1.0-alpha.34",
31
- "@monimesl/dot-simulator": "0.1.0-alpha.34"
27
+ "@monimesl/dot-compiler": "0.1.0-alpha.36",
28
+ "@monimesl/dot-core": "0.1.0-alpha.36",
29
+ "@monimesl/dot-dml-parser": "0.1.0-alpha.36",
30
+ "@monimesl/dot-script-parser": "0.1.0-alpha.36",
31
+ "@monimesl/dot-simulator": "0.1.0-alpha.36"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^22.20.4",