@monimesl/dot-cli 0.1.0-alpha.10 → 0.1.0-alpha.11
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 +8 -4
- package/dist/cli.js.map +1 -1
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -112,10 +112,12 @@ function tsconfig({ workspace }) {
|
|
|
112
112
|
]
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
+
const UNREGISTERED_DOT_ID = "<monime-Internal-Id>";
|
|
115
116
|
function dotJson({ name, packageName, template }) {
|
|
116
117
|
const dot = {
|
|
118
|
+
id: UNREGISTERED_DOT_ID,
|
|
117
119
|
$schema: DOT_JSON_SCHEMA,
|
|
118
|
-
|
|
120
|
+
package: `com.example.${hostWord(packageName)}`,
|
|
119
121
|
name,
|
|
120
122
|
version: "0.1.0"
|
|
121
123
|
};
|
|
@@ -161,8 +163,10 @@ also lists the page in \`dot.ts\`. The dot opens on the first page in that list.
|
|
|
161
163
|
|
|
162
164
|
## dot.json
|
|
163
165
|
|
|
164
|
-
\`dot.json\` describes the dot to Dash. Before you publish, change its \`
|
|
165
|
-
\`com.example.…\` to a reverse domain name you own. You can't change
|
|
166
|
+
\`dot.json\` describes the dot to Dash. Before you publish, change its \`package\` from
|
|
167
|
+
\`com.example.…\` to a reverse domain name you own. You can't change it after publishing. To
|
|
168
|
+
publish, register the dot with Monime first, and replace its \`id\`, \`<monime-Internal-Id>\`, with
|
|
169
|
+
the ID Monime gives you.
|
|
166
170
|
\`network.hosts\` lists every host the dot sends requests to with \`dash.network\`. Requests to any
|
|
167
171
|
other host fail. Your editor can complete the other fields from the schema the file points to.
|
|
168
172
|
|
|
@@ -174,7 +178,7 @@ Never put a secret there. \`.env.development\` and \`.env.production\` override
|
|
|
174
178
|
\`build\`.
|
|
175
179
|
`;
|
|
176
180
|
}
|
|
177
|
-
function
|
|
181
|
+
function hostWord(packageName) {
|
|
178
182
|
return packageName.slice(0, 63).replace(/-+$/, "");
|
|
179
183
|
}
|
|
180
184
|
//#endregion
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/report.ts","../src/create/template.ts","../src/create/create.ts","../src/dot-root.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 { 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 ((error as { code?: string }).code?.startsWith('ERR_PARSE_ARGS')) {\n // Keep only the first sentence. Node goes on to explain `--`, which doesn't help here.\n const [firstSentence] = (error as Error).message.split('. ');\n return `${firstSentence}. See dotctl --help.`;\n }\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\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","export 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 (TEMPLATES as readonly string[]).includes(name);\n}\n\ninterface TemplateOptions {\n name: string;\n packageName: 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.\nconst DOT_JSON_SCHEMA = './node_modules/@monimesl/dot-runtime/dot.schema.json';\n\nconst json = (value: unknown) => `${JSON.stringify(value, null, 2)}\\n`;\n\nexport function projectFiles(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 json({\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 json({\n compilerOptions,\n include: ['dot.ts', 'pages', '.dot/pages.d.ts', '.dot/env.d.ts'],\n });\n}\n\nfunction dotJson({ name, packageName, template }: TemplateOptions): string {\n const dot: Record<string, unknown> = {\n $schema: DOT_JSON_SCHEMA,\n id: `com.example.${idWord(packageName)}`,\n name,\n version: '0.1.0',\n };\n if (template === 'showcase') {\n // The showcase's network page sends requests to these hosts.\n dot.network = { hosts: ['itunes.apple.com', '*.mzstatic.com', 'httpbin.org'] };\n }\n return json(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: layout,\ntext, images and inputs, then state, navigation, and Dash's alerts, network and files. Read a page\nto see how it works. Then delete the pages you don't need, both from \\`pages/\\` and from\n\\`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. Before you publish, change its \\`id\\` from\n\\`com.example.…\\` to a reverse domain name you own. You can't change the id after publishing.\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## .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 is already lowercase letters, digits and inner hyphens. A part of a host name\n// can be at most 63 characters, so cut it there.\nfunction idWord(packageName: string): string {\n return packageName.slice(0, 63).replace(/-+$/, '');\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 { CommandError } from '../report';\nimport type { Template } from './template';\nimport { projectFiles, TEMPLATE_FILES } from './template';\n\ninterface CreateOptions {\n name: string;\n dir: 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 async function create({\n name,\n dir,\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\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 = projectFiles({\n name,\n packageName,\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 let current = dirname(dir);\n while (true) {\n if (existsSync(join(current, 'pnpm-workspace.yaml'))) {\n const runtimeManifest = join(current, 'packages/runtime/package.json');\n if (!existsSync(runtimeManifest)) {\n return false;\n }\n return readPackageJson(runtimeManifest).name === '@monimesl/dot-runtime';\n }\n const parent = dirname(current);\n if (parent === current) {\n return false;\n }\n current = parent;\n }\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 return existsSync(example) ? example : 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 { dirname, join } from 'node:path';\n\n// The nearest folder with a dot.json, starting at `dir` and walking up.\nexport function findDotRoot(dir: string): string | undefined {\n while (true) {\n if (existsSync(join(dir, 'dot.json'))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return undefined;\n }\n dir = parent;\n }\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { isPageId } from '@monimesl/dot-core';\nimport type { DotModuleIR } from '@monimesl/dot-script-parser';\nimport { parseDotModule } from '@monimesl/dot-script-parser';\n\nimport { findDotRoot } from '../dot-root';\nimport { CommandError } from '../report';\nimport { PAGE_FILES } from './template';\n\ninterface CreatePageResult {\n readonly dir: string;\n readonly files: readonly string[];\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.\n readonly listing: 'added' | 'already' | 'manual';\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', 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 return { dir, files, listing: await listPage(join(root, 'dot.ts'), path) };\n}\n\nasync function listPage(dotFile: string, path: string): Promise<CreatePageResult['listing']> {\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 = withPage(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 withPage(source: string, lastPage: DotModuleIR['lastPage'], path: string): string {\n const quote = source[lastPage.start] === '\"' ? '\"' : \"'\";\n const page = `${quote}${path}${quote}`;\n const lineStart = source.lastIndexOf('\\n', lastPage.start - 1) + 1;\n const indent = source.slice(lineStart, lastPage.start);\n if (!/^\\s*$/.test(indent)) {\n return `${source.slice(0, lastPage.end)}, ${page}${source.slice(lastPage.end)}`;\n }\n const trailingComma = /^\\s*,/.exec(source.slice(lastPage.end));\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 `${source.slice(0, lastPage.end)},\\n${indent}${page}${source.slice(lastPage.end)}`;\n}\n\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\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 (PACKAGE_MANAGERS as readonly string[]).includes(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 = onPath,\n): PackageManager[] {\n return PACKAGE_MANAGERS.filter(isInstalled);\n}\n\nfunction onPath(name: PackageManager): boolean {\n return (\n spawnSync(name, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' })\n .status === 0\n );\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 raw = Buffer.from(createPublicKey(privateKey).export({ format: 'jwk' }).x!, 'base64url');\n return {\n publicKey: raw.toString('base64'),\n keyId: raw.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, 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\ninterface Build {\n readonly files: ReadonlyMap<string, { readonly bytes: Buffer; readonly hash: string }>;\n}\n\nconst DEVICE_SCHEME = 'dotdev';\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 createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update('dotdev-token', 'utf8')\n .digest('base64url');\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 // Base64\n readonly sig: string;\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 createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update(`dotdev-build\\n${body}`, 'utf8')\n .digest('base64url');\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 // The secret never goes over the network. Only the link carries it.\n const secret = randomBytes(16).toString('base64url');\n const token = deviceToken(secret);\n let signature: LinkSignature | null = null;\n let build: Build = { files: new Map() };\n let 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 let hotSince = 0;\n let error: string | null = null;\n const waiting = new Set<() => void>();\n const connected = new Set<string>();\n\n const announce = () => {\n generation += 1;\n for (const wake of waiting) {\n wake();\n }\n };\n\n const server = createServer((request, response) => {\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 if (signature) {\n sendJson(response, signature);\n } else {\n response.writeHead(404).end();\n }\n return;\n }\n if (!isAuthorized(request, 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 if (!connected.has(from)) {\n connected.add(from);\n onConnect(from);\n }\n const files = Object.fromEntries([...build.files].map(([name, file]) => [name, file.hash]));\n const body = JSON.stringify({ generation, hotSince, files, error });\n response\n .writeHead(200, {\n 'content-type': 'application/json',\n [BUILD_MAC_HEADER]: buildMac(secret, body),\n })\n .end(body);\n } else if (request.method === 'GET' && path.startsWith('/dev/files/')) {\n if (url.searchParams.get('generation') !== String(generation)) {\n response.writeHead(409).end();\n return;\n }\n const name = path.slice('/dev/files/'.length).split('/').map(decodeURIComponent).join('/');\n const file = build.files.get(name);\n if (!file) {\n response.writeHead(404).end();\n return;\n }\n response.writeHead(200, { 'content-type': 'application/octet-stream' }).end(file.bytes);\n } else if (request.method === 'GET' && path === '/dev/changes') {\n waitForChange(response, Number(url.searchParams.get('after')));\n } else if (request.method === 'POST' && path === '/dev/log') {\n readText(request)\n .then((text) => {\n for (const line of text.split('\\n')) {\n onLog(line, from);\n }\n response.writeHead(204).end();\n })\n .catch(() => response.writeHead(413).end());\n } else {\n response.writeHead(404).end();\n }\n });\n\n const waitForChange = (response: ServerResponse, after: number) => {\n if (generation > after) {\n sendJson(response, { generation });\n return;\n }\n const wake = () => {\n clearTimeout(timer);\n waiting.delete(wake);\n sendJson(response, { generation });\n };\n const timer = setTimeout(() => {\n waiting.delete(wake);\n response.writeHead(204).end();\n }, WAIT_MS);\n waiting.add(wake);\n response.on('close', () => {\n clearTimeout(timer);\n waiting.delete(wake);\n });\n };\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, address, () => resolve());\n });\n const { port: listening } = server.address() as AddressInfo;\n const origin = `http://${address}:${listening}`;\n if (developerKey) {\n const expires = Math.floor(Date.now() / 1000) + LINK_LIFETIME_SECONDS;\n const keyId = developerKey.keyId;\n const message = signedLinkMessage(origin, secret, expires, keyId);\n signature = { expires, keyId, sig: developerKey.sign(Buffer.from(message, 'utf8')) };\n }\n\n return {\n link: `${DEVICE_SCHEME}://connect?server=${encodeURIComponent(origin)}&secret=${secret}`,\n async publish(dir, { hot = false } = {}) {\n build = await readBuild(dir);\n error = null;\n announce();\n if (!hot) {\n hotSince = generation;\n }\n },\n fail(message) {\n error = message;\n announce();\n },\n close() {\n for (const wake of waiting) {\n wake();\n }\n return new Promise((resolve) => {\n server.close(() => resolve());\n server.closeAllConnections();\n });\n },\n };\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<Build> {\n const files = new Map<string, { bytes: Buffer; hash: string }>();\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 files.set(relative(dir, path).split(sep).join('/'), { bytes, hash });\n }\n return { files };\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 return given.length === expected.length && 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 readText(request: IncomingMessage): Promise<string> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n size += (chunk as Buffer).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(chunk as Buffer);\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, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\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\nexport async function dev({ port, device, ...options }: DevOptions): Promise<() => Promise<void>> {\n const project = await loadProject(options.root);\n const outDir = resolve(project.root, options.outDir ?? 'dist');\n const buildOptions: BuildOptions = { mode: 'development', ...options, outDir };\n\n // Returns the error to show, with paths relative to the project, or null if the build worked.\n const buildApp = async (): Promise<string | null> => {\n const started = performance.now();\n try {\n printWarnings(await build(buildOptions));\n printBuildTime(started);\n return null;\n } catch (error) {\n const message = describeError(error).replaceAll(project.root + sep, '');\n console.error(`error: ${message}`);\n return message;\n }\n };\n\n const firstError = await buildApp();\n const simulator = await startDotSimulator(project, outDir, port);\n if (firstError) {\n simulator.error(firstError);\n }\n console.log(`\\n Simulator running at: ${simulator.url}\\n`);\n\n let devices: DeviceServer | undefined;\n if (device) {\n const developerKey = await loadDeveloperKey();\n devices = 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 devices.fail(firstError);\n } else {\n await devices.publish(outDir);\n }\n printDeviceLink(devices.link);\n if (developerKey) {\n console.log(` Signed with developer key ${developerKey.keyId}\\n`);\n } else {\n console.log(`${missingDeveloperKeyHint()}\\n`);\n }\n }\n\n // Files changed since the last good build. They decide whether the next build can hot reload.\n const changed = 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 `changed` for the next one.\n const building = [...changed];\n const error = await buildApp();\n if (error) {\n simulator.error(error);\n devices?.fail(error);\n return;\n }\n for (const path of building) {\n changed.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 = building.every((path) => path.startsWith(pagesDir));\n if (hot) {\n simulator.hotReload();\n } else {\n simulator.reload();\n }\n await devices?.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 changed.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(), devices?.close()]);\n };\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 console.log(renderUnicodeCompact(link, { border: 1 }).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 return file.split(/[\\\\/]/).some((segment) => {\n if (segment === '.env' || segment.startsWith('.env.')) {\n return false;\n }\n return segment === 'node_modules' || segment.startsWith('.');\n });\n}\n","import { resolve } from 'node:path';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\n\nimport { printBuildTime, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\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 = resolve(project.root, options.outDir ?? 'dist');\n const started = performance.now();\n printWarnings(await build({ ...options, outDir, mode: 'production' }));\n printBuildTime(started);\n const simulator = await startDotSimulator(project, outDir, port);\n console.log(`\\n Simulator running at: ${simulator.url}\\n`);\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\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';\nconst cursorUp = (lines: number) => `\\x1b[${lines}A`;\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 current = Math.max(\n 0,\n choices.findIndex((choice) => choice.value === fallback),\n );\n // Pad the values so their hints line up.\n const width = choices.some((choice) => choice.hint)\n ? Math.max(...choices.map((choice) => choice.value.length))\n : 0;\n const row = (choice: Choice<Value>, index: number) => {\n const name = choice.value.padEnd(width);\n const hint = choice.hint ? styleText('dim', ` ${choice.hint}`) : '';\n const defaultMark = choice.value === fallback ? styleText('green', ' (default)') : '';\n if (index === current) {\n return `${styleText('cyan', '❯')} ${styleText(['cyan', 'bold'], name)}${hint}${defaultMark}`;\n }\n return ` ${name}${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[]) => {\n output.write(`${drawnLines ? cursorUp(drawnLines) : ''}${ERASE_BELOW}${lines.join('\\n')}\\n`);\n drawnLines = lines.length;\n };\n const drawList = () =>\n draw([\n `${styleText('bold', question)} ${styleText('dim', '↑/↓ to move, Enter to choose')}`,\n ...choices.map(row),\n ]);\n\n emitKeypressEvents(input);\n input.setRawMode(true);\n input.resume();\n output.write(HIDE_CURSOR);\n drawList();\n return new Promise((resolve) => {\n const onKeypress = (_text: string, key: { name?: string; ctrl?: boolean }) => {\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 current = (current - 1 + choices.length) % choices.length;\n drawList();\n } else if (key.name === 'down' || key.name === 'j') {\n current = (current + 1) % choices.length;\n drawList();\n } else if (key.name === 'return' || key.name === 'enter') {\n const { value } = choices[current]!;\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 return (await select(question, choices, fallback ? 'Yes' : 'No', terminal)) === 'Yes';\n}\n\n// Asks for a typed answer. An empty answer, or no terminal, gives `fallback`, shown as the default.\nexport async function ask(\n question: string,\n fallback?: string,\n { input, output }: Terminal = TERMINAL,\n): Promise<string | undefined> {\n if (!input.isTTY) {\n return fallback;\n }\n const hint = fallback ? styleText('dim', ` (default: ${fallback})`) : '';\n const readline = createInterface({ input, output });\n try {\n const answer = await readline.question(`${styleText('bold', question)}${hint}: `);\n return answer.trim() || fallback;\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","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 } from '@monimesl/dot-compiler';\nimport { DEFAULT_PORT } from '@monimesl/dot-simulator';\n\nimport { create, toDisplayName, toPackageName } from './create/create';\nimport { createPage } from './create/page';\nimport type { PackageManager } from './create/package-manager';\nimport type { Template } from './create/template';\nimport { isTemplate, TEMPLATE_HINTS, TEMPLATES } from './create/template';\nimport {\n installedManagers,\n isPackageManager,\n launchedBy,\n PACKAGE_MANAGERS,\n} from './create/package-manager';\nimport { dev } from './dev';\nimport { preview } from './preview';\nimport { ask, confirm, select } from './prompts';\nimport {\n defaultDeveloperKeyPath,\n loadDeveloperKey,\n missingDeveloperKeyHint,\n} from './device/developer-key';\nimport { describeError, printBuildTime, printWarnings } from './report';\n\nconst USAGE = `Usage:\n dotctl create [dir] [--name <name>] [--template <starter|showcase>]\n [--pm <npm|pnpm|yarn|bun>]\n Create a dot. Asks for its name and 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\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 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\n const [command, target, pagePath] = positionals;\n if (command === 'lsp' && !values.help) {\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 if (command === 'keys' && !values.help) {\n return keys(target);\n }\n if (command === 'create' && target === 'page' && !values.help) {\n return addPage(pagePath);\n }\n if (command === 'create' && !values.help) {\n return createApp(target, values.name, values.template, values.pm);\n }\n if (values.help || (command !== 'build' && command !== 'dev' && command !== 'preview')) {\n console.log(USAGE);\n return values.help ? 0 : 1;\n }\n const port = values.port === undefined ? undefined : Number(values.port);\n if (port !== undefined && !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 const stop = await dev({ ...options, port, device: values.device });\n await new Promise((resolve) => process.once('SIGINT', resolve));\n await stop();\n return 0;\n }\n\n if (command === 'preview') {\n if (values.device) {\n console.error('--device only works with dotctl dev');\n return 1;\n }\n const simulator = await preview({ ...options, port });\n await new Promise((resolve) => process.once('SIGINT', resolve));\n await simulator.close();\n return 0;\n }\n\n const started = performance.now();\n const result = await build(options);\n for (const file of result.files.filter((file) => !file.endsWith('.map'))) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n printWarnings(result);\n printBuildTime(started);\n return 0;\n}\n\nasync function keys(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(path: string | undefined): Promise<number> {\n if (path === undefined) {\n console.error('Give the page a path, such as: dotctl create page cart/checkout');\n return 1;\n }\n if (path.startsWith('/')) {\n path = path.substring(1);\n }\n if (path.endsWith('/')) {\n path = path.substring(0, path.length - 1);\n }\n const { files, listing } = await createPage(path, process.cwd());\n for (const file of files) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\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\nasync function createApp(\n dir: string | undefined,\n name: string | undefined,\n templateFlag: string | undefined,\n packageManagerFlag: string | undefined,\n): Promise<number> {\n if (templateFlag !== undefined && !isTemplate(templateFlag)) {\n console.error(`--template must be one of ${TEMPLATES.join(', ')}`);\n return 1;\n }\n if (packageManagerFlag !== undefined && !isPackageManager(packageManagerFlag)) {\n console.error(`--pm must be one of ${PACKAGE_MANAGERS.join(', ')}`);\n return 1;\n }\n name ??= await ask('Dot name', dir ? toDisplayName(dir) : 'Dot');\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 template = templateFlag ?? (await chooseTemplate());\n const { root, workspace } = await create({\n name,\n dir: dir ?? toPackageName(name),\n template,\n });\n const path = relative(process.cwd(), root) || '.';\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 = workspace ? 'pnpm' : (packageManagerFlag ?? launchedBy());\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 installed = await run(packageManager, ['install'], root);\n if (installed === 0) {\n return run(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\nfunction run(command: string, args: string[], cwd: string): Promise<number> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n child.on('error', reject);\n child.on('exit', (code) => resolvePromise(code ?? 1));\n });\n}\n\nfunction chooseTemplate(): Promise<Template> {\n console.log();\n return select(\n 'Template?',\n TEMPLATES.map((template) => ({ value: template, hint: TEMPLATE_HINTS[template] })),\n 'starter',\n );\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 const fallback = installed.includes(suggested) ? suggested : installed[0]!;\n return select(\n 'Package manager?',\n installed.map((manager) => ({ value: manager })),\n fallback,\n );\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (error: unknown) => {\n console.error(`error: ${describeError(error)}`);\n process.exit(1);\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAMA,IAAa,eAAb,cAAkC,MAAM;CACtC,OAAyB;AAC3B;AAEA,SAAgB,cAAc,OAAwB;CAMpD,IAJE,iBAAiB,gBACjB,iBAAiB,gBACjB,iBAAiB,iBACjB,iBAAiB,kBAEjB,OAAO,MAAM;CAEf,IAAK,MAA4B,MAAM,WAAW,gBAAgB,GAAG;EAEnE,MAAM,CAAC,iBAAkB,MAAgB,QAAQ,MAAM,IAAI;EAC3D,OAAO,GAAG,cAAc;CAC1B;CACA,OAAO,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW,OAAO,KAAK;AAC/E;AAEA,SAAgB,cAAc,EAAE,YAA+B;CAC7D,KAAK,MAAM,WAAW,UACpB,QAAQ,KAAK,YAAY,QAAQ,SAAS;AAE9C;AAEA,SAAgB,eAAe,WAAyB;CACtD,QAAQ,IAAI,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,SAAS,EAAE,GAAG;AACvE;;;ACnCA,MAAa,YAAY,CAAC,WAAW,UAAU;AAK/C,MAAa,iBAA2C;CACtD,SAAS;CACT,UAAU;AACZ;AAEA,SAAgB,WAAW,MAAgC;CACzD,OAAQ,UAAgC,SAAS,IAAI;AACvD;AAUA,MAAa,aAA+C;CAC1D,YAAY;CACZ,YAAY;CACZ,iBAAiB;;;;;;AAMnB;AAGA,MAAa,iBAAiB;CAAC;CAAU;CAAS;CAAW;AAAM;AAGnE,MAAM,kBAAkB;AAExB,MAAM,QAAQ,UAAmB,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE;AAEnE,SAAgB,aAAa,SAAkD;CAC7E,OAAO;EACL,gBAAgB,YAAY,OAAO;EACnC,iBAAiB,SAAS,OAAO;EACjC,cAAc;EACd,YAAY,QAAQ,OAAO;EAC3B,aAAa,OAAO,OAAO;CAC7B;AACF;AAEA,SAAS,YAAY,EAAE,aAAa,YAAY,aAAsC;CACpF,MAAM,aAAa,YAAY,uCAAuC;CACtE,OAAO,KAAK;EACV,MAAM;EACN,SAAS;EACT,MAAM;EACN,SAAS;GACP,KAAK,aAAa;GAClB,OAAO,eAAe;GACtB,SAAS,iBAAiB;GAC1B,WAAW;EACb;EACA,cAAc,EAAE,yBAAyB,WAAW;EACpD,iBAAiB;GAAE,qBAAqB;GAAY,YAAY;EAAS;CAC3E,CAAC;AACH;AAEA,SAAS,SAAS,EAAE,aAAsC;CACxD,MAAM,kBAA2C;EAC/C,QAAQ;EACR,QAAQ;EACR,kBAAkB;EAClB,KAAK,CAAC,QAAQ;EACd,OAAO,CAAC,+BAA+B;EACvC,QAAQ;EACR,QAAQ;EACR,cAAc;EACd,sBAAsB;CACxB;CACA,IAAI,WACF,gBAAgB,mBAAmB,CAAC,sBAAsB;CAE5D,OAAO,KAAK;EACV;EACA,SAAS;GAAC;GAAU;GAAS;GAAmB;EAAe;CACjE,CAAC;AACH;AAEA,SAAS,QAAQ,EAAE,MAAM,aAAa,YAAqC;CACzE,MAAM,MAA+B;EACnC,SAAS;EACT,IAAI,eAAe,OAAO,WAAW;EACrC;EACA,SAAS;CACX;CACA,IAAI,aAAa,YAEf,IAAI,UAAU,EAAE,OAAO;EAAC;EAAoB;EAAkB;CAAa,EAAE;CAE/E,OAAO,KAAK,GAAG;AACjB;AAEA,MAAM,mBAAuD;CAC3D,SAAS;;;CAGT,UAAU;;;;AAIZ;AAEA,SAAS,OAAO,EAAE,MAAM,YAAqC;CAC3D,OAAO,KAAK,KAAK;;;;;;;;;;EAUjB,iBAAiB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2B7B;AAIA,SAAS,OAAO,aAA6B;CAC3C,OAAO,YAAY,MAAM,GAAG,EAAE,CAAC,CAAC,QAAQ,OAAO,EAAE;AACnD;;;ACpIA,eAAsB,OAAO,EAC3B,MACA,KACA,WAAW,aAC4B;CACvC,MAAM,OAAO,QAAQ,GAAG;CACxB,IAAI,WAAW,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,SAAS,GACjD,MAAM,IAAI,aAAa,GAAG,IAAI,wDAAwD;CAExF,MAAM,cAAc,cAAc,IAAI;CACtC,IAAI,CAAC,aACH,MAAM,IAAI,aAAa,aAAa,KAAK,qCAAqC;CAGhF,MAAM,YAAY,iBAAiB,IAAI;CACvC,MAAM,SAAS,YAAY,QAAQ;CACnC,MAAM,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC;CACrC,KAAK,MAAM,QAAQ,gBACjB,MAAM,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAEpE,MAAM,YAAY,aAAa;EAC7B;EACA;EACA,YAAY,YAAY,gBAAgB,IAAI,WAAW;EACvD;EACA;CACF,CAAC;CACD,KAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,SAAS,GACpD,MAAM,UAAU,KAAK,MAAM,IAAI,GAAG,OAAO;CAE3C,OAAO;EAAE;EAAM;CAAU;AAC3B;AAEA,SAAgB,cAAc,MAAsB;CAClD,OAAO,KACJ,YAAY,CAAC,CACb,QAAQ,eAAe,GAAG,CAAC,CAC3B,QAAQ,YAAY,EAAE;AAC3B;AAEA,SAAgB,cAAc,KAAqB;CACjD,OAAO,SAAS,QAAQ,GAAG,CAAC,CAAC,CAC1B,MAAM,SAAS,CAAC,CAChB,OAAO,OAAO,CAAC,CACf,KAAK,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAC3D,KAAK,GAAG;AACb;AAGA,SAAgB,iBAAiB,KAAsB;CACrD,IAAI,UAAU,QAAQ,GAAG;CACzB,OAAO,MAAM;EACX,IAAI,WAAW,KAAK,SAAS,qBAAqB,CAAC,GAAG;GACpD,MAAM,kBAAkB,KAAK,SAAS,+BAA+B;GACrE,IAAI,CAAC,WAAW,eAAe,GAC7B,OAAO;GAET,OAAO,gBAAgB,eAAe,CAAC,CAAC,SAAS;EACnD;EACA,MAAM,SAAS,QAAQ,OAAO;EAC9B,IAAI,WAAW,SACb,OAAO;EAET,UAAU;CACZ;AACF;AAGA,SAAS,YAAY,UAA4B;CAC/C,MAAM,UAAU,KAAK,QAAQ,GAAG,kBAAkB,QAAQ;CAC1D,OAAO,WAAW,OAAO,IAAI,UAAU,KAAK,QAAQ,GAAG,kBAAkB,QAAQ;AACnF;AAEA,SAAS,aAAqB;CAC5B,OAAO,gBAAgB,KAAK,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC;AAC1D;AAEA,SAAS,gBAAgB,MAA2B;CAClD,OAAO,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AAC9C;AAEA,SAAS,UAAkB;CACzB,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC,CAAC,QAAQ,gCAAgC,CAAC;AACzF;;;ACxGA,SAAgB,YAAY,KAAiC;CAC3D,OAAO,MAAM;EACX,IAAI,WAAW,KAAK,KAAK,UAAU,CAAC,GAClC,OAAO;EAET,MAAM,SAAS,QAAQ,GAAG;EAC1B,IAAI,WAAW,KACb;EAEF,MAAM;CACR;AACF;;;ACMA,eAAsB,WAAW,MAAc,KAAwC;CACrF,IAAI,CAAC,SAAS,IAAI,GAChB,MAAM,IAAI,aACR,IAAI,KAAK,0GACX;CAEF,MAAM,OAAO,YAAY,QAAQ,GAAG,CAAC;CACrC,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,aACR,2FACF;CAEF,MAAM,MAAM,KAAK,MAAM,SAAS,IAAI;CACpC,IAAI,WAAW,GAAG,GAChB,MAAM,IAAI,aAAa,SAAS,KAAK,qCAAqC;CAE5E,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;CACpC,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,UAAU,GAAG;EACxD,MAAM,OAAO,KAAK,KAAK,IAAI;EAC3B,MAAM,UAAU,MAAM,OAAO;EAC7B,MAAM,KAAK,IAAI;CACjB;CACA,OAAO;EAAE;EAAK;EAAO,SAAS,MAAM,SAAS,KAAK,MAAM,QAAQ,GAAG,IAAI;CAAE;AAC3E;AAEA,eAAe,SAAS,SAAiB,MAAoD;CAC3F,IAAI,CAAC,WAAW,OAAO,GACrB,OAAO;CAET,MAAM,SAAS,MAAM,SAAS,SAAS,MAAM;CAC7C,IAAI;CACJ,IAAI;EACF,MAAM,eAAe,QAAQ,OAAO;CACtC,QAAQ;EACN,OAAO;CACT;CACA,IAAI,IAAI,MAAM,SAAS,IAAI,GACzB,OAAO;CAET,MAAM,UAAU,SAAS,QAAQ,IAAI,UAAU,IAAI;CACnD,IAAI,CAAC,UAAU,SAAS,SAAS,IAAI,GACnC,OAAO;CAET,MAAM,UAAU,SAAS,OAAO;CAChC,OAAO;AACT;AAIA,SAAS,SAAS,QAAgB,UAAmC,MAAsB;CACzF,MAAM,QAAQ,OAAO,SAAS,WAAW,OAAM,OAAM;CACrD,MAAM,OAAO,GAAG,QAAQ,OAAO;CAC/B,MAAM,YAAY,OAAO,YAAY,MAAM,SAAS,QAAQ,CAAC,IAAI;CACjE,MAAM,SAAS,OAAO,MAAM,WAAW,SAAS,KAAK;CACrD,IAAI,CAAC,QAAQ,KAAK,MAAM,GACtB,OAAO,GAAG,OAAO,MAAM,GAAG,SAAS,GAAG,EAAE,IAAI,OAAO,OAAO,MAAM,SAAS,GAAG;CAE9E,MAAM,gBAAgB,QAAQ,KAAK,OAAO,MAAM,SAAS,GAAG,CAAC;CAC7D,IAAI,eAAe;EACjB,MAAM,MAAM,SAAS,MAAM,cAAc,EAAE,CAAC;EAC5C,OAAO,GAAG,OAAO,MAAM,GAAG,GAAG,EAAE,IAAI,SAAS,KAAK,GAAG,OAAO,MAAM,GAAG;CACtE;CACA,OAAO,GAAG,OAAO,MAAM,GAAG,SAAS,GAAG,EAAE,KAAK,SAAS,OAAO,OAAO,MAAM,SAAS,GAAG;AACxF;AAEA,SAAS,UAAU,QAAgB,SAAiB,MAAuB;CACzE,IAAI;EACF,OAAO,eAAe,QAAQ,OAAO,CAAC,CAAC,MAAM,SAAS,IAAI;CAC5D,QAAQ;EACN,OAAO;CACT;AACF;;;AC3FA,MAAa,mBAAmB;CAAC;CAAO;CAAQ;CAAQ;AAAK;AAG7D,SAAgB,iBAAiB,MAAsC;CACrE,OAAQ,iBAAuC,SAAS,IAAI;AAC9D;AAEA,SAAgB,WAAW,YAAY,QAAQ,IAAI,yBAAyB,IAAoB;CAC9F,OAAO,iBAAiB,MAAM,SAAS,UAAU,WAAW,GAAG,KAAK,EAAE,CAAC,KAAK;AAC9E;AAEA,SAAgB,kBACd,cAAiD,QAC/B;CAClB,OAAO,iBAAiB,OAAO,WAAW;AAC5C;AAEA,SAAS,OAAO,MAA+B;CAC7C,OACE,UAAU,MAAM,CAAC,WAAW,GAAG;EAAE,OAAO;EAAU,OAAO,QAAQ,aAAa;CAAQ,CAAC,CAAC,CACrF,WAAW;AAElB;;;ACPA,SAAgB,0BAAkC;CAChD,OAAO,QAAQ,IAAI,qBAAqB,KAAK,QAAQ,GAAG,QAAQ,mBAAmB;AACrF;AAGA,SAAgB,wBAAwB,OAAO,wBAAwB,GAAW;CAChF,OACE,kCAAkC,KAAK;;eAEvB,QAAQ,IAAI,EAAE,8CAA8C,KAAK;AAGrF;AAGA,eAAsB,iBACpB,OAAO,wBAAwB,GACI;CACnC,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,SAAS,MAAM,MAAM;CACnC,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAC5C;EAEF,MAAM;CACR;CACA,MAAM,aAAa,iBAAiB,GAAG;CACvC,IAAI,WAAW,sBAAsB,WACnC,MAAM,IAAI,MACR,GAAG,KAAK,iFAAiF,MAC3F;CAEF,OAAO,eAAe,UAAU;AAClC;AAEA,SAAS,eAAe,YAAqC;CAC3D,MAAM,MAAM,OAAO,KAAK,gBAAgB,UAAU,CAAC,CAAC,OAAO,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,GAAI,WAAW;CAC7F,OAAO;EACL,WAAW,IAAI,SAAS,QAAQ;EAChC,OAAO,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK;EACxC,OAAO,UAAU,KAAK,MAAM,OAAO,UAAU,CAAC,CAAC,SAAS,QAAQ;CAClE;AACF;;;ACrBA,MAAM,gBAAgB;AAItB,MAAM,UAAU;AAChB,MAAM,gBAAgB;AAEtB,MAAM,wBAAwB;AAE9B,MAAa,mBAAmB;AAGhC,SAAgB,kBACd,QACA,QACA,SACA,OACQ;CACR,OAAO;EAAC;EAAkB;EAAQ;EAAQ,OAAO,OAAO;EAAG;CAAK,CAAC,CAAC,KAAK,IAAI;AAC7E;AAIA,SAAgB,YAAY,QAAwB;CAClD,OAAO,WAAW,UAAU,OAAO,KAAK,QAAQ,WAAW,CAAC,CAAC,CAC1D,OAAO,gBAAgB,MAAM,CAAC,CAC9B,OAAO,WAAW;AACvB;AAaA,SAAgB,SAAS,QAAgB,MAAsB;CAC7D,OAAO,WAAW,UAAU,OAAO,KAAK,QAAQ,WAAW,CAAC,CAAC,CAC1D,OAAO,iBAAiB,QAAQ,MAAM,CAAC,CACvC,OAAO,WAAW;AACvB;AAEA,eAAsB,kBAAkB,EACtC,UAAU,oBAAoB,GAC9B,OAAO,GACP,cAAc,CAAC,GACf,kBAAkB,CAAC,GACnB,iBACuB,CAAC,GAA0B;CAClD,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,MACR,2GACF;CAGF,MAAM,SAAS,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;CACnD,MAAM,QAAQ,YAAY,MAAM;CAChC,IAAI,YAAkC;CACtC,IAAI,QAAe,EAAE,uBAAO,IAAI,IAAI,EAAE;CACtC,IAAI,aAAa;CAGjB,IAAI,WAAW;CACf,IAAI,QAAuB;CAC3B,MAAM,0BAAU,IAAI,IAAgB;CACpC,MAAM,4BAAY,IAAI,IAAY;CAElC,MAAM,iBAAiB;EACrB,cAAc;EACd,KAAK,MAAM,QAAQ,SACjB,KAAK;CAET;CAEA,MAAM,SAAS,cAAc,SAAS,aAAa;EACjD,MAAM,OAAO,QAAQ,OAAO,iBAAiB;EAG7C,IAAI,QAAQ,WAAW,SAAS,QAAQ,QAAQ,aAAa;GAC3D,IAAI,WACF,SAAS,UAAU,SAAS;QAE5B,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI;GAE9B;EACF;EACA,IAAI,CAAC,aAAa,SAAS,KAAK,GAAG;GACjC,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI;GAC5B;EACF;EACA,MAAM,MAAM,IAAI,IAAI,QAAQ,OAAO,KAAK,eAAe;EACvD,MAAM,OAAO,IAAI;EACjB,IAAI,QAAQ,WAAW,SAAS,SAAS,cAAc;GACrD,IAAI,CAAC,UAAU,IAAI,IAAI,GAAG;IACxB,UAAU,IAAI,IAAI;IAClB,UAAU,IAAI;GAChB;GACA,MAAM,QAAQ,OAAO,YAAY,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;GAC1F,MAAM,OAAO,KAAK,UAAU;IAAE;IAAY;IAAU;IAAO;GAAM,CAAC;GAClE,SACG,UAAU,KAAK;IACd,gBAAgB;KACf,mBAAmB,SAAS,QAAQ,IAAI;GAC3C,CAAC,CAAC,CACD,IAAI,IAAI;EACb,OAAO,IAAI,QAAQ,WAAW,SAAS,KAAK,WAAW,aAAa,GAAG;GACrE,IAAI,IAAI,aAAa,IAAI,YAAY,MAAM,OAAO,UAAU,GAAG;IAC7D,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI;IAC5B;GACF;GACA,MAAM,OAAO,KAAK,MAAM,EAAoB,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;GACzF,MAAM,OAAO,MAAM,MAAM,IAAI,IAAI;GACjC,IAAI,CAAC,MAAM;IACT,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI;IAC5B;GACF;GACA,SAAS,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK;EACxF,OAAO,IAAI,QAAQ,WAAW,SAAS,SAAS,gBAC9C,cAAc,UAAU,OAAO,IAAI,aAAa,IAAI,OAAO,CAAC,CAAC;OACxD,IAAI,QAAQ,WAAW,UAAU,SAAS,YAC/C,SAAS,OAAO,CAAC,CACd,MAAM,SAAS;GACd,KAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,GAChC,MAAM,MAAM,IAAI;GAElB,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI;EAC9B,CAAC,CAAC,CACD,YAAY,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC;OAE5C,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI;CAEhC,CAAC;CAED,MAAM,iBAAiB,UAA0B,UAAkB;EACjE,IAAI,aAAa,OAAO;GACtB,SAAS,UAAU,EAAE,WAAW,CAAC;GACjC;EACF;EACA,MAAM,aAAa;GACjB,aAAa,KAAK;GAClB,QAAQ,OAAO,IAAI;GACnB,SAAS,UAAU,EAAE,WAAW,CAAC;EACnC;EACA,MAAM,QAAQ,iBAAiB;GAC7B,QAAQ,OAAO,IAAI;GACnB,SAAS,UAAU,GAAG,CAAC,CAAC,IAAI;EAC9B,GAAG,OAAO;EACV,QAAQ,IAAI,IAAI;EAChB,SAAS,GAAG,eAAe;GACzB,aAAa,KAAK;GAClB,QAAQ,OAAO,IAAI;EACrB,CAAC;CACH;CAEA,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,MAAM,eAAe,QAAQ,CAAC;CAC9C,CAAC;CACD,MAAM,EAAE,MAAM,cAAc,OAAO,QAAQ;CAC3C,MAAM,SAAS,UAAU,QAAQ,GAAG;CACpC,IAAI,cAAc;EAChB,MAAM,UAAU,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;EAChD,MAAM,QAAQ,aAAa;EAC3B,MAAM,UAAU,kBAAkB,QAAQ,QAAQ,SAAS,KAAK;EAChE,YAAY;GAAE;GAAS;GAAO,KAAK,aAAa,KAAK,OAAO,KAAK,SAAS,MAAM,CAAC;EAAE;CACrF;CAEA,OAAO;EACL,MAAM,GAAG,cAAc,oBAAoB,mBAAmB,MAAM,EAAE,UAAU;EAChF,MAAM,QAAQ,KAAK,EAAE,MAAM,UAAU,CAAC,GAAG;GACvC,QAAQ,MAAM,UAAU,GAAG;GAC3B,QAAQ;GACR,SAAS;GACT,IAAI,CAAC,KACH,WAAW;EAEf;EACA,KAAK,SAAS;GACZ,QAAQ;GACR,SAAS;EACX;EACA,QAAQ;GACN,KAAK,MAAM,QAAQ,SACjB,KAAK;GAEP,OAAO,IAAI,SAAS,YAAY;IAC9B,OAAO,YAAY,QAAQ,CAAC;IAC5B,OAAO,oBAAoB;GAC7B,CAAC;EACH;CACF;AACF;AAGA,SAAS,sBAA0C;CACjD,KAAK,MAAM,aAAa,OAAO,OAAO,kBAAkB,CAAC,GACvD,KAAK,MAAM,aAAa,aAAa,CAAC,GACpC,IAAI,UAAU,WAAW,UAAU,CAAC,UAAU,UAC5C,OAAO,UAAU;AAKzB;AAGA,eAAe,UAAU,KAA6B;CACpD,MAAM,wBAAQ,IAAI,IAA6C;CAC/D,MAAM,UAAU,MAAM,QAAQ,KAAK;EAAE,WAAW;EAAM,eAAe;CAAK,CAAC;CAC3E,KAAK,MAAM,SAAS,SAAS;EAE3B,IAAI,CAAC,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,MAAM,KAAK,MAAM,KAAK,SAAS,MAAM,GAC9E;EAEF,MAAM,OAAO,KAAK,MAAM,YAAY,MAAM,IAAI;EAC9C,MAAM,QAAQ,MAAM,SAAS,IAAI;EACjC,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,WAAW;EAClE,MAAM,IAAI,SAAS,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,GAAG;GAAE;GAAO;EAAK,CAAC;CACrE;CACA,OAAO,EAAE,MAAM;AACjB;AAEA,SAAS,aAAa,SAA0B,OAAwB;CACtE,MAAM,QAAQ,OAAO,KAAK,QAAQ,QAAQ,iBAAiB,EAAE;CAC7D,MAAM,WAAW,OAAO,KAAK,UAAU,OAAO;CAC9C,OAAO,MAAM,WAAW,SAAS,UAAU,gBAAgB,OAAO,QAAQ;AAC5E;AAEA,SAAS,SAAS,UAA0B,MAAqB;CAC/D,SAAS,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC;AAC1F;AAEA,eAAe,SAAS,SAA2C;CACjE,MAAM,SAAmB,CAAC;CAC1B,IAAI,OAAO;CACX,WAAW,MAAM,SAAS,SAAS;EACjC,QAAS,MAAiB;EAC1B,IAAI,OAAO,eACT,MAAM,IAAI,MAAM,2BAA2B,cAAc,sBAAsB;EAEjF,OAAO,KAAK,KAAe;CAC7B;CACA,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,MAAM;AAC9C;;;ACtRA,SAAgB,kBACd,SACA,QACA,MACoB;CACpB,OAAO,eAAe;EACpB,QAAQ;EACR,cAAc,iBAAiB,QAAQ,IAAI,CAAC,CAAC,MAAM;EACnD,MAAM,QAAQ,QAAQ;EACtB;EACA,YAAY,KAAK,QAAQ,MAAM,QAAQ,OAAO;CAChD,CAAC;AACH;;;ACAA,MAAM,qBAAqB;AAE3B,eAAsB,IAAI,EAAE,MAAM,QAAQ,GAAG,WAAqD;CAChG,MAAM,UAAU,MAAM,YAAY,QAAQ,IAAI;CAC9C,MAAM,SAAS,QAAQ,QAAQ,MAAM,QAAQ,UAAU,MAAM;CAC7D,MAAM,eAA6B;EAAE,MAAM;EAAe,GAAG;EAAS;CAAO;CAG7E,MAAM,WAAW,YAAoC;EACnD,MAAM,UAAU,YAAY,IAAI;EAChC,IAAI;GACF,cAAc,MAAM,MAAM,YAAY,CAAC;GACvC,eAAe,OAAO;GACtB,OAAO;EACT,SAAS,OAAO;GACd,MAAM,UAAU,cAAc,KAAK,CAAC,CAAC,WAAW,QAAQ,OAAO,KAAK,EAAE;GACtE,QAAQ,MAAM,UAAU,SAAS;GACjC,OAAO;EACT;CACF;CAEA,MAAM,aAAa,MAAM,SAAS;CAClC,MAAM,YAAY,MAAM,kBAAkB,SAAS,QAAQ,IAAI;CAC/D,IAAI,YACF,UAAU,MAAM,UAAU;CAE5B,QAAQ,IAAI,6BAA6B,UAAU,IAAI,GAAG;CAE1D,IAAI;CACJ,IAAI,QAAQ;EACV,MAAM,eAAe,MAAM,iBAAiB;EAC5C,UAAU,MAAM,kBAAkB;GAChC;GACA,YAAY,SAAS,QAAQ,IAAI,2BAA2B,MAAM;GAClE,QAAQ,SAAS,QAAQ,IAAI,UAAU,MAAM;EAC/C,CAAC;EACD,IAAI,YACF,QAAQ,KAAK,UAAU;OAEvB,MAAM,QAAQ,QAAQ,MAAM;EAE9B,gBAAgB,QAAQ,IAAI;EAC5B,IAAI,cACF,QAAQ,IAAI,+BAA+B,aAAa,MAAM,GAAG;OAEjE,QAAQ,IAAI,GAAG,wBAAwB,EAAE,GAAG;CAEhD;CAGA,MAAM,0BAAU,IAAI,IAAY;CAChC,MAAM,WAAW,KAAK,QAAQ,MAAM,SAAS,IAAI;CACjD,MAAM,WAAW,IAAI,aAAa,YAAY;EAE5C,MAAM,WAAW,CAAC,GAAG,OAAO;EAC5B,MAAM,QAAQ,MAAM,SAAS;EAC7B,IAAI,OAAO;GACT,UAAU,MAAM,KAAK;GACrB,SAAS,KAAK,KAAK;GACnB;EACF;EACA,KAAK,MAAM,QAAQ,UACjB,QAAQ,OAAO,IAAI;EAIrB,MAAM,MAAM,SAAS,OAAO,SAAS,KAAK,WAAW,QAAQ,CAAC;EAC9D,IAAI,KACF,UAAU,UAAU;OAEpB,UAAU,OAAO;EAEnB,MAAM,SAAS,QAAQ,QAAQ,EAAE,IAAI,CAAC;CACxC,CAAC;CAED,IAAI;CACJ,MAAM,UAAU,MAAM,QAAQ,MAAM,EAAE,WAAW,KAAK,IAAI,QAAQ,SAAS;EACzE,IAAI,CAAC,QAAQ,UAAU,IAAI,GACzB;EAEF,MAAM,OAAO,QAAQ,QAAQ,MAAM,IAAI;EACvC,IAAI,SAAS,UAAU,KAAK,WAAW,SAAS,GAAG,GACjD;EAEF,QAAQ,IAAI,IAAI;EAChB,aAAa,aAAa;EAC1B,gBAAgB,iBAAiB;GAC/B,QAAQ,IAAI,WAAW,SAAS,QAAQ,MAAM,IAAI,GAAG;GACrD,SAAS,QAAQ;EACnB,GAAG,kBAAkB;CACvB,CAAC;CAED,OAAO,YAAY;EACjB,aAAa,aAAa;EAC1B,QAAQ,MAAM;EACd,MAAM,QAAQ,IAAI,CAAC,UAAU,MAAM,GAAG,SAAS,MAAM,CAAC,CAAC;CACzD;AACF;AAEA,SAAS,gBAAgB,MAAoB;CAC3C,QAAQ,IACN,uIAEF;CACA,QAAQ,IAAI,qBAAqB,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,OAAO,IAAI,CAAC;CAC1E,QAAQ,IAAI,OAAO,KAAK,GAAG;AAC7B;AAGA,IAAM,eAAN,MAAmB;CACjB;CACA,YAAY;CACZ,wBAAwB;CAExB,YAAY,SAA8B;EACxC,KAAK,WAAW;CAClB;CAEA,UAAgB;EACd,IAAI,KAAK,WAAW;GAClB,KAAK,wBAAwB;GAC7B;EACF;EACA,KAAU,OAAO;CACnB;CAEA,MAAM,SAAwB;EAC5B,KAAK,YAAY;EACjB,KAAK,wBAAwB;EAC7B,IAAI;GACF,MAAM,KAAK,SAAS;EACtB,UAAU;GACR,KAAK,YAAY;EACnB;EACA,IAAI,KAAK,uBACP,KAAK,QAAQ;CAEjB;AACF;AAIA,SAAS,UAAU,MAAuB;CACxC,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,MAAM,YAAY;EAC3C,IAAI,YAAY,UAAU,QAAQ,WAAW,OAAO,GAClD,OAAO;EAET,OAAO,YAAY,kBAAkB,QAAQ,WAAW,GAAG;CAC7D,CAAC;AACH;;;AC1JA,eAAsB,QAAQ,EAAE,MAAM,GAAG,WAA+C;CACtF,MAAM,UAAU,MAAM,YAAY,QAAQ,IAAI;CAC9C,MAAM,SAAS,QAAQ,QAAQ,MAAM,QAAQ,UAAU,MAAM;CAC7D,MAAM,UAAU,YAAY,IAAI;CAChC,cAAc,MAAM,MAAM;EAAE,GAAG;EAAS;EAAQ,MAAM;CAAa,CAAC,CAAC;CACrE,eAAe,OAAO;CACtB,MAAM,YAAY,MAAM,kBAAkB,SAAS,QAAQ,IAAI;CAC/D,QAAQ,IAAI,6BAA6B,UAAU,IAAI,GAAG;CAC1D,OAAO;AACT;;;ACTA,MAAM,WAAqB;CAAE,OAAO,QAAQ;CAAO,QAAQ,QAAQ;AAAO;AAE1E,MAAM,cAAc;AACpB,MAAM,cAAc;AACpB,MAAM,cAAc;AACpB,MAAM,YAAY,UAAkB,QAAQ,MAAM;AAIlD,eAAsB,OACpB,UACA,SACA,UACA,EAAE,OAAO,WAAqB,UACd;CAChB,IAAI,CAAC,MAAM,OACT,OAAO;CAET,IAAI,UAAU,KAAK,IACjB,GACA,QAAQ,WAAW,WAAW,OAAO,UAAU,QAAQ,CACzD;CAEA,MAAM,QAAQ,QAAQ,MAAM,WAAW,OAAO,IAAI,IAC9C,KAAK,IAAI,GAAG,QAAQ,KAAK,WAAW,OAAO,MAAM,MAAM,CAAC,IACxD;CACJ,MAAM,OAAO,QAAuB,UAAkB;EACpD,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK;EACtC,MAAM,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK,OAAO,MAAM,IAAI;EAClE,MAAM,cAAc,OAAO,UAAU,WAAW,UAAU,SAAS,YAAY,IAAI;EACnF,IAAI,UAAU,SACZ,OAAO,GAAG,UAAU,QAAQ,GAAG,EAAE,GAAG,UAAU,CAAC,QAAQ,MAAM,GAAG,IAAI,IAAI,OAAO;EAEjF,OAAO,KAAK,OAAO,OAAO;CAC5B;CAIA,IAAI,aAAa;CACjB,MAAM,QAAQ,UAAoB;EAChC,OAAO,MAAM,GAAG,aAAa,SAAS,UAAU,IAAI,KAAK,cAAc,MAAM,KAAK,IAAI,EAAE,GAAG;EAC3F,aAAa,MAAM;CACrB;CACA,MAAM,iBACJ,KAAK,CACH,GAAG,UAAU,QAAQ,QAAQ,EAAE,GAAG,UAAU,OAAO,8BAA8B,KACjF,GAAG,QAAQ,IAAI,GAAG,CACpB,CAAC;CAEH,mBAAmB,KAAK;CACxB,MAAM,WAAW,IAAI;CACrB,MAAM,OAAO;CACb,OAAO,MAAM,WAAW;CACxB,SAAS;CACT,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,cAAc,OAAe,QAA2C;GAC5E,IAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;IAChC,OAAO,MAAM,GAAG,YAAY,GAAG;IAC/B,QAAQ,KAAK,GAAG;GAClB;GACA,IAAI,IAAI,SAAS,QAAQ,IAAI,SAAS,KAAK;IACzC,WAAW,UAAU,IAAI,QAAQ,UAAU,QAAQ;IACnD,SAAS;GACX,OAAO,IAAI,IAAI,SAAS,UAAU,IAAI,SAAS,KAAK;IAClD,WAAW,UAAU,KAAK,QAAQ;IAClC,SAAS;GACX,OAAO,IAAI,IAAI,SAAS,YAAY,IAAI,SAAS,SAAS;IACxD,MAAM,EAAE,UAAU,QAAQ;IAC1B,MAAM,IAAI,YAAY,UAAU;IAChC,MAAM,WAAW,KAAK;IACtB,MAAM,MAAM;IACZ,KAAK,CAAC,GAAG,UAAU,QAAQ,QAAQ,EAAE,GAAG,UAAU,QAAQ,KAAK,GAAG,CAAC;IACnE,OAAO,MAAM,WAAW;IACxB,QAAQ,KAAK;GACf;EACF;EACA,MAAM,GAAG,YAAY,UAAU;CACjC,CAAC;AACH;AAGA,eAAsB,QACpB,UACA,UACA,WAAqB,UACH;CAClB,IAAI,CAAC,SAAS,MAAM,OAClB,OAAO;CAGT,OAAQ,MAAM,OAAO,UAAU,CADd,EAAE,OAAO,MAAM,GAAG,EAAE,OAAO,KAAK,CACZ,GAAG,WAAW,QAAQ,MAAM,QAAQ,MAAO;AAClF;AAGA,eAAsB,IACpB,UACA,UACA,EAAE,OAAO,WAAqB,UACD;CAC7B,IAAI,CAAC,MAAM,OACT,OAAO;CAET,MAAM,OAAO,WAAW,UAAU,OAAO,cAAc,SAAS,EAAE,IAAI;CACtE,MAAM,WAAW,gBAAgB;EAAE;EAAO;CAAO,CAAC;CAClD,IAAI;EAEF,QAAO,MADc,SAAS,SAAS,GAAG,UAAU,QAAQ,QAAQ,IAAI,KAAK,GAAG,EAAA,CAClE,KAAK,KAAK;CAC1B,SAAS,OAAO;EAEd,IAAK,MAAgB,SAAS,cAC5B;EAEF,MAAM;CACR,UAAU;EACR,SAAS,MAAM;CACjB;AACF;;;ACtGA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;2DAuB6C,aAAa;;;;;AAMxE,eAAe,KAAK,MAAiC;CACnD,MAAM,EAAE,QAAQ,gBAAgB,UAAU;EACxC,MAAM;EACN,kBAAkB;EAClB,SAAS;GACP,WAAW,EAAE,MAAM,SAAS;GAC5B,YAAY,EAAE,MAAM,SAAS;GAC7B,MAAM,EAAE,MAAM,SAAS;GACvB,QAAQ,EAAE,MAAM,UAAU;GAC1B,MAAM,EAAE,MAAM,SAAS;GACvB,UAAU,EAAE,MAAM,SAAS;GAC3B,IAAI,EAAE,MAAM,SAAS;GACrB,MAAM;IAAE,MAAM;IAAW,OAAO;GAAI;GAEpC,OAAO,EAAE,MAAM,UAAU;EAC3B;CACF,CAAC;CAED,MAAM,CAAC,SAAS,QAAQ,YAAY;CACpC,IAAI,YAAY,SAAS,CAAC,OAAO,MAAM;EAErC,MAAM,EAAE,eAAe,MAAM,OAAO;EACpC,WAAW;EAEX,OAAO,IAAI,cAAsB,CAAC,CAAC;CACrC;CACA,IAAI,YAAY,UAAU,CAAC,OAAO,MAChC,OAAO,KAAK,MAAM;CAEpB,IAAI,YAAY,YAAY,WAAW,UAAU,CAAC,OAAO,MACvD,OAAO,QAAQ,QAAQ;CAEzB,IAAI,YAAY,YAAY,CAAC,OAAO,MAClC,OAAO,UAAU,QAAQ,OAAO,MAAM,OAAO,UAAU,OAAO,EAAE;CAElE,IAAI,OAAO,QAAS,YAAY,WAAW,YAAY,SAAS,YAAY,WAAY;EACtF,QAAQ,IAAI,KAAK;EACjB,OAAO,OAAO,OAAO,IAAI;CAC3B;CACA,MAAM,OAAO,OAAO,SAAS,KAAA,IAAY,KAAA,IAAY,OAAO,OAAO,IAAI;CACvE,IAAI,SAAS,KAAA,KAAa,CAAC,OAAO,UAAU,IAAI,GAAG;EACjD,QAAQ,MAAM,uCAAuC,OAAO,KAAK,EAAE;EACnE,OAAO;CACT;CAEA,MAAM,UAAwB,EAAE,MAAM,UAAU,IAAI;CACpD,IAAI,OAAO,eAAe,KAAA,GACxB,QAAQ,SAAS,OAAO;CAE1B,IAAI,OAAO,eAAe,KAAA,GACxB,QAAQ,aAAa,OAAO,WAAW,MAAM,GAAG;CAGlD,IAAI,YAAY,OAAO;EACrB,MAAM,OAAO,MAAM,IAAI;GAAE,GAAG;GAAS;GAAM,QAAQ,OAAO;EAAO,CAAC;EAClE,MAAM,IAAI,SAAS,YAAY,QAAQ,KAAK,UAAU,OAAO,CAAC;EAC9D,MAAM,KAAK;EACX,OAAO;CACT;CAEA,IAAI,YAAY,WAAW;EACzB,IAAI,OAAO,QAAQ;GACjB,QAAQ,MAAM,qCAAqC;GACnD,OAAO;EACT;EACA,MAAM,YAAY,MAAM,QAAQ;GAAE,GAAG;GAAS;EAAK,CAAC;EACpD,MAAM,IAAI,SAAS,YAAY,QAAQ,KAAK,UAAU,OAAO,CAAC;EAC9D,MAAM,UAAU,MAAM;EACtB,OAAO;CACT;CAEA,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,SAAS,MAAM,MAAM,OAAO;CAClC,KAAK,MAAM,QAAQ,OAAO,MAAM,QAAQ,SAAS,CAAC,KAAK,SAAS,MAAM,CAAC,GACrE,QAAQ,IAAI,KAAK,SAAS,QAAQ,IAAI,GAAG,IAAI,GAAG;CAElD,cAAc,MAAM;CACpB,eAAe,OAAO;CACtB,OAAO;AACT;AAEA,eAAe,KAAK,QAA6C;CAC/D,IAAI,WAAW,QAAQ;EACrB,QAAQ,MAAM,yBAAyB;EACvC,OAAO;CACT;CACA,MAAM,OAAO,wBAAwB;CACrC,MAAM,MAAM,MAAM,iBAAiB,IAAI;CACvC,IAAI,CAAC,KAAK;EACR,QAAQ,MAAM,wBAAwB,IAAI,CAAC;EAC3C,OAAO;CACT;CACA,QAAQ,IAAI,mBAAmB,IAAI,UAAU,kBAAkB,IAAI,MAAM,GAAG;CAC5E,OAAO;AACT;AAEA,eAAe,QAAQ,MAA2C;CAChE,IAAI,SAAS,KAAA,GAAW;EACtB,QAAQ,MAAM,iEAAiE;EAC/E,OAAO;CACT;CACA,IAAI,KAAK,WAAW,GAAG,GACrB,OAAO,KAAK,UAAU,CAAC;CAEzB,IAAI,KAAK,SAAS,GAAG,GACnB,OAAO,KAAK,UAAU,GAAG,KAAK,SAAS,CAAC;CAE1C,MAAM,EAAE,OAAO,YAAY,MAAM,WAAW,MAAM,QAAQ,IAAI,CAAC;CAC/D,KAAK,MAAM,QAAQ,OACjB,QAAQ,IAAI,KAAK,SAAS,QAAQ,IAAI,GAAG,IAAI,GAAG;CAElD,IAAI,YAAY,SACd,QAAQ,IAAI,aAAa,KAAK,aAAa;MACtC,IAAI,YAAY,UACrB,QAAQ,IACN,+EAA+E,KAAK,MACtF;CAEF,OAAO;AACT;AAEA,eAAe,UACb,KACA,MACA,cACA,oBACiB;CACjB,IAAI,iBAAiB,KAAA,KAAa,CAAC,WAAW,YAAY,GAAG;EAC3D,QAAQ,MAAM,6BAA6B,UAAU,KAAK,IAAI,GAAG;EACjE,OAAO;CACT;CACA,IAAI,uBAAuB,KAAA,KAAa,CAAC,iBAAiB,kBAAkB,GAAG;EAC7E,QAAQ,MAAM,uBAAuB,iBAAiB,KAAK,IAAI,GAAG;EAClE,OAAO;CACT;CACA,SAAS,MAAM,IAAI,YAAY,MAAM,cAAc,GAAG,IAAI,KAAK;CAC/D,IAAI,CAAC,MAAM;EACT,QAAQ,MAAM,4EAA0E;EACxF,OAAO;CACT;CACA,MAAM,WAAW,gBAAiB,MAAM,eAAe;CACvD,MAAM,EAAE,MAAM,cAAc,MAAM,OAAO;EACvC;EACA,KAAK,OAAO,cAAc,IAAI;EAC9B;CACF,CAAC;CACD,MAAM,OAAO,SAAS,QAAQ,IAAI,GAAG,IAAI,KAAK;CAC9C,QAAQ,IAAI,aAAa,KAAK,MAAM,MAAM;CAC1C,IAAI,WACF,QAAQ,IAAI,qEAAqE;CAInF,IAAI,iBAAiC,YAAY,SAAU,sBAAsB,WAAW;CAC5F,QAAQ,IAAI;CACZ,IAAI,MAAM,QAAQ,6BAA6B,IAAI,GAAG;EACpD,IAAI,CAAC,aAAa,CAAC,oBACjB,iBAAiB,MAAM,qBAAqB,cAAc;EAG5D,IAAI,MADoB,IAAI,gBAAgB,CAAC,SAAS,GAAG,IAAI,MAC3C,GAChB,OAAO,IAAI,gBAAgB,CAAC,OAAO,KAAK,GAAG,IAAI;EAEjD,QAAQ,MACN,KAAK,eAAe,kEACtB;CACF;CACA,QAAQ,IACN,mBAAmB,KAAK,MAAM,eAAe,cAAc,eAAe,WAC5E;CACA,OAAO;AACT;AAEA,SAAS,IAAI,SAAiB,MAAgB,KAA8B;CAC1E,OAAO,IAAI,SAAS,gBAAgB,WAAW;EAC7C,MAAM,QAAQ,MAAM,SAAS,MAAM;GACjC;GACA,OAAO;GACP,OAAO,QAAQ,aAAa;EAC9B,CAAC;EACD,MAAM,GAAG,SAAS,MAAM;EACxB,MAAM,GAAG,SAAS,SAAS,eAAe,QAAQ,CAAC,CAAC;CACtD,CAAC;AACH;AAEA,SAAS,iBAAoC;CAC3C,QAAQ,IAAI;CACZ,OAAO,OACL,aACA,UAAU,KAAK,cAAc;EAAE,OAAO;EAAU,MAAM,eAAe;CAAU,EAAE,GACjF,SACF;AACF;AAEA,eAAe,qBAAqB,WAAoD;CACtF,MAAM,YAAY,kBAAkB;CACpC,IAAI,UAAU,SAAS,GACrB,OAAO,UAAU,MAAM;CAEzB,MAAM,WAAW,UAAU,SAAS,SAAS,IAAI,YAAY,UAAU;CACvE,OAAO,OACL,oBACA,UAAU,KAAK,aAAa,EAAE,OAAO,QAAQ,EAAE,GAC/C,QACF;AACF;AAEA,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,MACzB,SAAS,QAAQ,KAAK,IAAI,IAC1B,UAAmB;CAClB,QAAQ,MAAM,UAAU,cAAc,KAAK,GAAG;CAC9C,QAAQ,KAAK,CAAC;AAChB,CACF"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/report.ts","../src/create/template.ts","../src/create/create.ts","../src/dot-root.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 { 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 ((error as { code?: string }).code?.startsWith('ERR_PARSE_ARGS')) {\n // Keep only the first sentence. Node goes on to explain `--`, which doesn't help here.\n const [firstSentence] = (error as Error).message.split('. ');\n return `${firstSentence}. See dotctl --help.`;\n }\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\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","export 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 (TEMPLATES as readonly string[]).includes(name);\n}\n\ninterface TemplateOptions {\n name: string;\n packageName: 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.\nconst DOT_JSON_SCHEMA = './node_modules/@monimesl/dot-runtime/dot.schema.json';\n\nconst json = (value: unknown) => `${JSON.stringify(value, null, 2)}\\n`;\n\nexport function projectFiles(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 json({\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 json({\n compilerOptions,\n include: ['dot.ts', 'pages', '.dot/pages.d.ts', '.dot/env.d.ts'],\n });\n}\n\n// Stands in for the ID Monime gives the dot when it's registered.\nexport const UNREGISTERED_DOT_ID = '<monime-Internal-Id>';\n\nfunction dotJson({ name, packageName, template }: TemplateOptions): string {\n const dot: Record<string, unknown> = {\n id: UNREGISTERED_DOT_ID,\n $schema: DOT_JSON_SCHEMA,\n package: `com.example.${hostWord(packageName)}`,\n name,\n version: '0.1.0',\n };\n if (template === 'showcase') {\n // The showcase's network page sends requests to these hosts.\n dot.network = { hosts: ['itunes.apple.com', '*.mzstatic.com', 'httpbin.org'] };\n }\n return json(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: layout,\ntext, images and inputs, then state, navigation, and Dash's alerts, network and files. Read a page\nto see how it works. Then delete the pages you don't need, both from \\`pages/\\` and from\n\\`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. Before you publish, change its \\`package\\` from\n\\`com.example.…\\` to a reverse domain name you own. You can't change it after publishing. To\npublish, register the dot with Monime first, and replace its \\`id\\`, \\`<monime-Internal-Id>\\`, with\nthe 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## .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 npm package name is already lowercase letters, digits and inner hyphens. A part of a host name\n// can be at most 63 characters, so cut it there.\nfunction hostWord(packageName: string): string {\n return packageName.slice(0, 63).replace(/-+$/, '');\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 { CommandError } from '../report';\nimport type { Template } from './template';\nimport { projectFiles, TEMPLATE_FILES } from './template';\n\ninterface CreateOptions {\n name: string;\n dir: 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 async function create({\n name,\n dir,\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\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 = projectFiles({\n name,\n packageName,\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 let current = dirname(dir);\n while (true) {\n if (existsSync(join(current, 'pnpm-workspace.yaml'))) {\n const runtimeManifest = join(current, 'packages/runtime/package.json');\n if (!existsSync(runtimeManifest)) {\n return false;\n }\n return readPackageJson(runtimeManifest).name === '@monimesl/dot-runtime';\n }\n const parent = dirname(current);\n if (parent === current) {\n return false;\n }\n current = parent;\n }\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 return existsSync(example) ? example : 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 { dirname, join } from 'node:path';\n\n// The nearest folder with a dot.json, starting at `dir` and walking up.\nexport function findDotRoot(dir: string): string | undefined {\n while (true) {\n if (existsSync(join(dir, 'dot.json'))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return undefined;\n }\n dir = parent;\n }\n}\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { isPageId } from '@monimesl/dot-core';\nimport type { DotModuleIR } from '@monimesl/dot-script-parser';\nimport { parseDotModule } from '@monimesl/dot-script-parser';\n\nimport { findDotRoot } from '../dot-root';\nimport { CommandError } from '../report';\nimport { PAGE_FILES } from './template';\n\ninterface CreatePageResult {\n readonly dir: string;\n readonly files: readonly string[];\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.\n readonly listing: 'added' | 'already' | 'manual';\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', 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 return { dir, files, listing: await listPage(join(root, 'dot.ts'), path) };\n}\n\nasync function listPage(dotFile: string, path: string): Promise<CreatePageResult['listing']> {\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 = withPage(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 withPage(source: string, lastPage: DotModuleIR['lastPage'], path: string): string {\n const quote = source[lastPage.start] === '\"' ? '\"' : \"'\";\n const page = `${quote}${path}${quote}`;\n const lineStart = source.lastIndexOf('\\n', lastPage.start - 1) + 1;\n const indent = source.slice(lineStart, lastPage.start);\n if (!/^\\s*$/.test(indent)) {\n return `${source.slice(0, lastPage.end)}, ${page}${source.slice(lastPage.end)}`;\n }\n const trailingComma = /^\\s*,/.exec(source.slice(lastPage.end));\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 `${source.slice(0, lastPage.end)},\\n${indent}${page}${source.slice(lastPage.end)}`;\n}\n\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\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 (PACKAGE_MANAGERS as readonly string[]).includes(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 = onPath,\n): PackageManager[] {\n return PACKAGE_MANAGERS.filter(isInstalled);\n}\n\nfunction onPath(name: PackageManager): boolean {\n return (\n spawnSync(name, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' })\n .status === 0\n );\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 raw = Buffer.from(createPublicKey(privateKey).export({ format: 'jwk' }).x!, 'base64url');\n return {\n publicKey: raw.toString('base64'),\n keyId: raw.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, 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\ninterface Build {\n readonly files: ReadonlyMap<string, { readonly bytes: Buffer; readonly hash: string }>;\n}\n\nconst DEVICE_SCHEME = 'dotdev';\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 createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update('dotdev-token', 'utf8')\n .digest('base64url');\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 // Base64\n readonly sig: string;\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 createHmac('sha256', Buffer.from(secret, 'base64url'))\n .update(`dotdev-build\\n${body}`, 'utf8')\n .digest('base64url');\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 // The secret never goes over the network. Only the link carries it.\n const secret = randomBytes(16).toString('base64url');\n const token = deviceToken(secret);\n let signature: LinkSignature | null = null;\n let build: Build = { files: new Map() };\n let 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 let hotSince = 0;\n let error: string | null = null;\n const waiting = new Set<() => void>();\n const connected = new Set<string>();\n\n const announce = () => {\n generation += 1;\n for (const wake of waiting) {\n wake();\n }\n };\n\n const server = createServer((request, response) => {\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 if (signature) {\n sendJson(response, signature);\n } else {\n response.writeHead(404).end();\n }\n return;\n }\n if (!isAuthorized(request, 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 if (!connected.has(from)) {\n connected.add(from);\n onConnect(from);\n }\n const files = Object.fromEntries([...build.files].map(([name, file]) => [name, file.hash]));\n const body = JSON.stringify({ generation, hotSince, files, error });\n response\n .writeHead(200, {\n 'content-type': 'application/json',\n [BUILD_MAC_HEADER]: buildMac(secret, body),\n })\n .end(body);\n } else if (request.method === 'GET' && path.startsWith('/dev/files/')) {\n if (url.searchParams.get('generation') !== String(generation)) {\n response.writeHead(409).end();\n return;\n }\n const name = path.slice('/dev/files/'.length).split('/').map(decodeURIComponent).join('/');\n const file = build.files.get(name);\n if (!file) {\n response.writeHead(404).end();\n return;\n }\n response.writeHead(200, { 'content-type': 'application/octet-stream' }).end(file.bytes);\n } else if (request.method === 'GET' && path === '/dev/changes') {\n waitForChange(response, Number(url.searchParams.get('after')));\n } else if (request.method === 'POST' && path === '/dev/log') {\n readText(request)\n .then((text) => {\n for (const line of text.split('\\n')) {\n onLog(line, from);\n }\n response.writeHead(204).end();\n })\n .catch(() => response.writeHead(413).end());\n } else {\n response.writeHead(404).end();\n }\n });\n\n const waitForChange = (response: ServerResponse, after: number) => {\n if (generation > after) {\n sendJson(response, { generation });\n return;\n }\n const wake = () => {\n clearTimeout(timer);\n waiting.delete(wake);\n sendJson(response, { generation });\n };\n const timer = setTimeout(() => {\n waiting.delete(wake);\n response.writeHead(204).end();\n }, WAIT_MS);\n waiting.add(wake);\n response.on('close', () => {\n clearTimeout(timer);\n waiting.delete(wake);\n });\n };\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, address, () => resolve());\n });\n const { port: listening } = server.address() as AddressInfo;\n const origin = `http://${address}:${listening}`;\n if (developerKey) {\n const expires = Math.floor(Date.now() / 1000) + LINK_LIFETIME_SECONDS;\n const keyId = developerKey.keyId;\n const message = signedLinkMessage(origin, secret, expires, keyId);\n signature = { expires, keyId, sig: developerKey.sign(Buffer.from(message, 'utf8')) };\n }\n\n return {\n link: `${DEVICE_SCHEME}://connect?server=${encodeURIComponent(origin)}&secret=${secret}`,\n async publish(dir, { hot = false } = {}) {\n build = await readBuild(dir);\n error = null;\n announce();\n if (!hot) {\n hotSince = generation;\n }\n },\n fail(message) {\n error = message;\n announce();\n },\n close() {\n for (const wake of waiting) {\n wake();\n }\n return new Promise((resolve) => {\n server.close(() => resolve());\n server.closeAllConnections();\n });\n },\n };\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<Build> {\n const files = new Map<string, { bytes: Buffer; hash: string }>();\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 files.set(relative(dir, path).split(sep).join('/'), { bytes, hash });\n }\n return { files };\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 return given.length === expected.length && 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 readText(request: IncomingMessage): Promise<string> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n size += (chunk as Buffer).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(chunk as Buffer);\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, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\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\nexport async function dev({ port, device, ...options }: DevOptions): Promise<() => Promise<void>> {\n const project = await loadProject(options.root);\n const outDir = resolve(project.root, options.outDir ?? 'dist');\n const buildOptions: BuildOptions = { mode: 'development', ...options, outDir };\n\n // Returns the error to show, with paths relative to the project, or null if the build worked.\n const buildApp = async (): Promise<string | null> => {\n const started = performance.now();\n try {\n printWarnings(await build(buildOptions));\n printBuildTime(started);\n return null;\n } catch (error) {\n const message = describeError(error).replaceAll(project.root + sep, '');\n console.error(`error: ${message}`);\n return message;\n }\n };\n\n const firstError = await buildApp();\n const simulator = await startDotSimulator(project, outDir, port);\n if (firstError) {\n simulator.error(firstError);\n }\n console.log(`\\n Simulator running at: ${simulator.url}\\n`);\n\n let devices: DeviceServer | undefined;\n if (device) {\n const developerKey = await loadDeveloperKey();\n devices = 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 devices.fail(firstError);\n } else {\n await devices.publish(outDir);\n }\n printDeviceLink(devices.link);\n if (developerKey) {\n console.log(` Signed with developer key ${developerKey.keyId}\\n`);\n } else {\n console.log(`${missingDeveloperKeyHint()}\\n`);\n }\n }\n\n // Files changed since the last good build. They decide whether the next build can hot reload.\n const changed = 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 `changed` for the next one.\n const building = [...changed];\n const error = await buildApp();\n if (error) {\n simulator.error(error);\n devices?.fail(error);\n return;\n }\n for (const path of building) {\n changed.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 = building.every((path) => path.startsWith(pagesDir));\n if (hot) {\n simulator.hotReload();\n } else {\n simulator.reload();\n }\n await devices?.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 changed.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(), devices?.close()]);\n };\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 console.log(renderUnicodeCompact(link, { border: 1 }).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 return file.split(/[\\\\/]/).some((segment) => {\n if (segment === '.env' || segment.startsWith('.env.')) {\n return false;\n }\n return segment === 'node_modules' || segment.startsWith('.');\n });\n}\n","import { resolve } from 'node:path';\n\nimport type { BuildOptions } from '@monimesl/dot-compiler';\nimport { build, loadProject } from '@monimesl/dot-compiler';\nimport type { Simulator } from '@monimesl/dot-simulator';\n\nimport { printBuildTime, printWarnings } from './report';\nimport { startDotSimulator } from './simulator';\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 = resolve(project.root, options.outDir ?? 'dist');\n const started = performance.now();\n printWarnings(await build({ ...options, outDir, mode: 'production' }));\n printBuildTime(started);\n const simulator = await startDotSimulator(project, outDir, port);\n console.log(`\\n Simulator running at: ${simulator.url}\\n`);\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\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';\nconst cursorUp = (lines: number) => `\\x1b[${lines}A`;\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 current = Math.max(\n 0,\n choices.findIndex((choice) => choice.value === fallback),\n );\n // Pad the values so their hints line up.\n const width = choices.some((choice) => choice.hint)\n ? Math.max(...choices.map((choice) => choice.value.length))\n : 0;\n const row = (choice: Choice<Value>, index: number) => {\n const name = choice.value.padEnd(width);\n const hint = choice.hint ? styleText('dim', ` ${choice.hint}`) : '';\n const defaultMark = choice.value === fallback ? styleText('green', ' (default)') : '';\n if (index === current) {\n return `${styleText('cyan', '❯')} ${styleText(['cyan', 'bold'], name)}${hint}${defaultMark}`;\n }\n return ` ${name}${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[]) => {\n output.write(`${drawnLines ? cursorUp(drawnLines) : ''}${ERASE_BELOW}${lines.join('\\n')}\\n`);\n drawnLines = lines.length;\n };\n const drawList = () =>\n draw([\n `${styleText('bold', question)} ${styleText('dim', '↑/↓ to move, Enter to choose')}`,\n ...choices.map(row),\n ]);\n\n emitKeypressEvents(input);\n input.setRawMode(true);\n input.resume();\n output.write(HIDE_CURSOR);\n drawList();\n return new Promise((resolve) => {\n const onKeypress = (_text: string, key: { name?: string; ctrl?: boolean }) => {\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 current = (current - 1 + choices.length) % choices.length;\n drawList();\n } else if (key.name === 'down' || key.name === 'j') {\n current = (current + 1) % choices.length;\n drawList();\n } else if (key.name === 'return' || key.name === 'enter') {\n const { value } = choices[current]!;\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 return (await select(question, choices, fallback ? 'Yes' : 'No', terminal)) === 'Yes';\n}\n\n// Asks for a typed answer. An empty answer, or no terminal, gives `fallback`, shown as the default.\nexport async function ask(\n question: string,\n fallback?: string,\n { input, output }: Terminal = TERMINAL,\n): Promise<string | undefined> {\n if (!input.isTTY) {\n return fallback;\n }\n const hint = fallback ? styleText('dim', ` (default: ${fallback})`) : '';\n const readline = createInterface({ input, output });\n try {\n const answer = await readline.question(`${styleText('bold', question)}${hint}: `);\n return answer.trim() || fallback;\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","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 } from '@monimesl/dot-compiler';\nimport { DEFAULT_PORT } from '@monimesl/dot-simulator';\n\nimport { create, toDisplayName, toPackageName } from './create/create';\nimport { createPage } from './create/page';\nimport type { PackageManager } from './create/package-manager';\nimport type { Template } from './create/template';\nimport { isTemplate, TEMPLATE_HINTS, TEMPLATES } from './create/template';\nimport {\n installedManagers,\n isPackageManager,\n launchedBy,\n PACKAGE_MANAGERS,\n} from './create/package-manager';\nimport { dev } from './dev';\nimport { preview } from './preview';\nimport { ask, confirm, select } from './prompts';\nimport {\n defaultDeveloperKeyPath,\n loadDeveloperKey,\n missingDeveloperKeyHint,\n} from './device/developer-key';\nimport { describeError, printBuildTime, printWarnings } from './report';\n\nconst USAGE = `Usage:\n dotctl create [dir] [--name <name>] [--template <starter|showcase>]\n [--pm <npm|pnpm|yarn|bun>]\n Create a dot. Asks for its name and 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\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 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\n const [command, target, pagePath] = positionals;\n if (command === 'lsp' && !values.help) {\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 if (command === 'keys' && !values.help) {\n return keys(target);\n }\n if (command === 'create' && target === 'page' && !values.help) {\n return addPage(pagePath);\n }\n if (command === 'create' && !values.help) {\n return createApp(target, values.name, values.template, values.pm);\n }\n if (values.help || (command !== 'build' && command !== 'dev' && command !== 'preview')) {\n console.log(USAGE);\n return values.help ? 0 : 1;\n }\n const port = values.port === undefined ? undefined : Number(values.port);\n if (port !== undefined && !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 const stop = await dev({ ...options, port, device: values.device });\n await new Promise((resolve) => process.once('SIGINT', resolve));\n await stop();\n return 0;\n }\n\n if (command === 'preview') {\n if (values.device) {\n console.error('--device only works with dotctl dev');\n return 1;\n }\n const simulator = await preview({ ...options, port });\n await new Promise((resolve) => process.once('SIGINT', resolve));\n await simulator.close();\n return 0;\n }\n\n const started = performance.now();\n const result = await build(options);\n for (const file of result.files.filter((file) => !file.endsWith('.map'))) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\n printWarnings(result);\n printBuildTime(started);\n return 0;\n}\n\nasync function keys(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(path: string | undefined): Promise<number> {\n if (path === undefined) {\n console.error('Give the page a path, such as: dotctl create page cart/checkout');\n return 1;\n }\n if (path.startsWith('/')) {\n path = path.substring(1);\n }\n if (path.endsWith('/')) {\n path = path.substring(0, path.length - 1);\n }\n const { files, listing } = await createPage(path, process.cwd());\n for (const file of files) {\n console.log(` ${relative(process.cwd(), file)}`);\n }\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\nasync function createApp(\n dir: string | undefined,\n name: string | undefined,\n templateFlag: string | undefined,\n packageManagerFlag: string | undefined,\n): Promise<number> {\n if (templateFlag !== undefined && !isTemplate(templateFlag)) {\n console.error(`--template must be one of ${TEMPLATES.join(', ')}`);\n return 1;\n }\n if (packageManagerFlag !== undefined && !isPackageManager(packageManagerFlag)) {\n console.error(`--pm must be one of ${PACKAGE_MANAGERS.join(', ')}`);\n return 1;\n }\n name ??= await ask('Dot name', dir ? toDisplayName(dir) : 'Dot');\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 template = templateFlag ?? (await chooseTemplate());\n const { root, workspace } = await create({\n name,\n dir: dir ?? toPackageName(name),\n template,\n });\n const path = relative(process.cwd(), root) || '.';\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 = workspace ? 'pnpm' : (packageManagerFlag ?? launchedBy());\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 installed = await run(packageManager, ['install'], root);\n if (installed === 0) {\n return run(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\nfunction run(command: string, args: string[], cwd: string): Promise<number> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n child.on('error', reject);\n child.on('exit', (code) => resolvePromise(code ?? 1));\n });\n}\n\nfunction chooseTemplate(): Promise<Template> {\n console.log();\n return select(\n 'Template?',\n TEMPLATES.map((template) => ({ value: template, hint: TEMPLATE_HINTS[template] })),\n 'starter',\n );\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 const fallback = installed.includes(suggested) ? suggested : installed[0]!;\n return select(\n 'Package manager?',\n installed.map((manager) => ({ value: manager })),\n fallback,\n );\n}\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (error: unknown) => {\n console.error(`error: ${describeError(error)}`);\n process.exit(1);\n },\n);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monimesl/dot-cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.11",
|
|
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-core": "0.1.0-alpha.
|
|
28
|
-
"@monimesl/dot-dml-parser": "0.1.0-alpha.
|
|
29
|
-
"@monimesl/dot-
|
|
30
|
-
"@monimesl/dot-
|
|
31
|
-
"@monimesl/dot-script-parser": "0.1.0-alpha.
|
|
27
|
+
"@monimesl/dot-core": "0.1.0-alpha.11",
|
|
28
|
+
"@monimesl/dot-dml-parser": "0.1.0-alpha.11",
|
|
29
|
+
"@monimesl/dot-compiler": "0.1.0-alpha.11",
|
|
30
|
+
"@monimesl/dot-simulator": "0.1.0-alpha.11",
|
|
31
|
+
"@monimesl/dot-script-parser": "0.1.0-alpha.11"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22.20.4",
|