micra.js 2.3.0 → 2.3.1
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/CHANGELOG.md +43 -0
- package/README.md +43 -0
- package/dist/core/reactive.d.ts +4 -0
- package/dist/dom/directives.d.ts +2 -2
- package/dist/dom/scan.d.ts +3 -3
- package/dist/micra.cjs.js +11 -12
- package/dist/micra.cjs.js.map +2 -2
- package/dist/micra.esm.js +11 -12
- package/dist/micra.esm.js.map +2 -2
- package/dist/micra.js +11 -12
- package/dist/micra.js.map +2 -2
- package/dist/micra.min.js +2 -2
- package/dist/types.d.ts +0 -2
- package/llms-full.txt +110 -15
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/core/mount.ts +1 -1
- package/src/core/reactive.ts +6 -1
- package/src/dom/directives.ts +1 -3
- package/src/dom/each.ts +3 -5
- package/src/dom/scan.ts +3 -3
- package/src/types.ts +0 -2
- package/src/dom/query.ts +0 -50
package/dist/micra.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/utils/fetch.ts", "../src/core/registry.ts", "../src/utils/expr.ts", "../src/core/bus.ts", "../src/core/reactive.ts", "../src/dom/directives.ts", "../src/dom/events.ts", "../src/dom/scan.ts", "../src/dom/each.ts", "../src/dom/refs.ts", "../src/core/mount.ts", "../src/core/start.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Micra.js \u2014 Lightweight reactive framework for small sites and simple SaaS.\n *\n * Public surface \u2014 re-exports only.\n *\n * Features:\n * - JS expressions in directives (data-if=\"count > 0\")\n * - Keyed list diffing (data-each=\"items\" data-key=\"id\")\n * - Auto-mount via data-component (Micra.define + Micra.start)\n * - Props from SSR data-attributes (this.prop('page'))\n * - Built-in fetch helper (this.fetch('/api/...'))\n * - Global event bus (Micra.on / Micra.emit)\n * - DOM refs (data-ref=\"chart\" \u2192 this.refs.chart)\n * - Additive class toggling (data-class=\"active:isActive\")\n * - @event shorthand (@click=\"increment\")\n * - Lifecycle: onCreate, onDestroy\n * - SSR-friendly: Micra.start() is safe to call multiple times\n * - Directive cache: O(1) re-renders after first mount\n *\n * Size target: < 5.5 KB minified+gzipped\n *\n * @module Micra\n */\n\n// \u2500\u2500 Public types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport type {\n StateRecord,\n UnsubFn,\n EventHandler,\n EventPayload,\n EmitArgs,\n MicraEvents,\n FetchOptions,\n ComponentMethods,\n ComponentBuiltins,\n ComponentInstance,\n ComponentDefinition,\n} from './types'\n\n// \u2500\u2500 Errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport { FetchError } from './utils/fetch'\n\n// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport { define, defineComponent, instances, registry, debug } from './core/registry'\nexport { mount } from './core/mount'\nexport { start } from './core/start'\nexport { on, off, emit } from './core/bus'\n", "/**\n * src/utils/fetch.ts \u2014 HTTP fetch helper.\n *\n * Responsibilities:\n * - Auto-attach CSRF token from <meta name=\"csrf-token\">\n * - Serialize POST/PUT/PATCH body as JSON\n * - Serialize GET/HEAD options as query params\n * - Throw a typed FetchError on non-2xx responses\n * - Return parsed JSON or text\n *\n * LLM NOTE: This module is PURE (no DOM side effects beyond reading a meta tag).\n * It wraps the native fetch() API with SaaS-friendly defaults.\n */\n\nimport type { FetchOptions } from '../types'\n\n// \u2500\u2500 CSRF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Read CSRF token from <meta name=\"csrf-token\"> (Rails, Laravel, Django\u2026). */\nfunction getCSRF(): string | null {\n return document.querySelector('meta[name=\"csrf-token\"]')?.getAttribute('content') ?? null\n}\n\n// \u2500\u2500 Typed error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Thrown by `this.fetch()` when the server returns a non-2xx status.\n *\n * @example\n * try {\n * await this.fetch('/api/data')\n * } catch (e) {\n * if (e instanceof FetchError && e.status === 404) { ... }\n * }\n */\nexport class FetchError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly response: Response,\n ) {\n super(message)\n this.name = 'FetchError'\n }\n}\n\n// \u2500\u2500 Fetch helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Fetch wrapper with SaaS defaults.\n *\n * - GET/HEAD: extra `options` keys become URL query params\n * - POST/PUT/PATCH/DELETE: `options.body` is JSON-serialized\n * - Attaches X-CSRF-Token header automatically\n * - Returns parsed JSON if Content-Type is application/json, else text\n *\n * @example\n * // GET with params \u2192 /api/users?page=2&status=active\n * const data = await this.fetch('/api/users', { page: 2, status: 'active' })\n *\n * // POST with JSON body\n * await this.fetch('/api/invite', { method: 'POST', body: { email, role } })\n */\nexport async function micraFetch(url: string, options: FetchOptions = {}): Promise<unknown> {\n const method = ((options.method as string | undefined) ?? 'GET').toUpperCase()\n const headers: Record<string, string> = {\n Accept: 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n }\n\n const csrf = getCSRF()\n if (csrf) headers['X-CSRF-Token'] = csrf\n\n let finalUrl = url\n let body: string | undefined\n\n if (method === 'GET' || method === 'HEAD') {\n const params: Record<string, string> = {}\n for (const [k, v] of Object.entries(options)) {\n if (k !== 'method' && k !== 'headers' && k !== 'signal' && v != null)\n params[k] = String(v)\n }\n if (Object.keys(params).length)\n finalUrl += (url.includes('?') ? '&' : '?') + new URLSearchParams(params)\n } else if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json'\n body = JSON.stringify(options.body)\n }\n\n const res = await fetch(finalUrl, {\n method,\n headers,\n ...(options.signal !== undefined ? { signal: options.signal as AbortSignal } : {}),\n ...(body !== undefined ? { body } : {}),\n })\n\n if (!res.ok)\n throw new FetchError(`[Micra] fetch: ${method} ${url} \u2192 ${res.status}`, res.status, res)\n\n const ct = res.headers.get('content-type') ?? ''\n return ct.includes('application/json') ? res.json() : res.text()\n}\n", "/**\n * src/core/registry.ts \u2014 Component definition registry and instance store.\n *\n * Responsibilities:\n * - Store named component definitions (define / registry)\n * - Store live component instances keyed by root HTMLElement (instances)\n *\n * LLM NOTE: Both maps are module-level singletons (one per page load).\n * They are intentionally mutable from mount.ts and start.ts.\n */\n\nimport type {\n ComponentDefinition,\n ComponentInstance,\n ComponentMethods,\n InternalInstance,\n StateRecord,\n} from '../types'\n\n// Named definition map \u2014 populated by define()\nexport const _registry = new Map<string, ComponentDefinition>()\n\n// Live instance map \u2014 populated by mount(), cleared by instance.destroy()\nexport const _instances = new Map<HTMLElement, InternalInstance>()\n\n// \u2500\u2500 Public accessors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Register a component definition under `name`.\n *\n * Both state shape (`S`) and method set (`M`) are inferred from the literal,\n * so `this.state.X` and `this.someMethod()` are fully typed inside the\n * method bodies and lifecycle hooks.\n *\n * @example\n * define('counter', {\n * state: { count: 0 },\n * inc() { this.state.count++ }, // this.state.count: number \u2713\n * reset() { this.state.count = 0 }, // this.reset() is also typed \u2713\n * onCreate() { this.inc() }, // \u2713\n * })\n */\nexport function define<S extends StateRecord, M>(\n name: string,\n definition: ComponentDefinition<S, M>,\n): void {\n _registry.set(name, definition as unknown as ComponentDefinition)\n}\n\n/**\n * Type-helper \u2014 returns `definition` unchanged but lets TypeScript infer `S`\n * and `M` from the literal so all methods are typed with the correct `this`.\n *\n * Use this when defining a component outside a `define()` call.\n *\n * @example\n * const counter = defineComponent({\n * state: { count: 0 },\n * increment() { this.state.count++ }, // this.state: { count: number } \u2713\n * })\n * Micra.define('counter', counter)\n */\nexport function defineComponent<S extends StateRecord, M>(\n definition: ComponentDefinition<S, M>,\n): ComponentDefinition<S, M> {\n return definition\n}\n\n/**\n * Returns a read-only view of all live instances (keyed by root element).\n * Useful for DevTools / debugging.\n */\nexport function instances(): ReadonlyMap<HTMLElement, ComponentInstance> {\n return _instances as unknown as ReadonlyMap<HTMLElement, ComponentInstance>\n}\n\n/**\n * Returns a read-only view of all registered component definitions.\n * Useful for DevTools / debugging.\n */\nexport function registry(): ReadonlyMap<string, ComponentDefinition> {\n return _registry\n}\n\n/**\n * Print all live component instances to the browser console.\n * Shows component name, root element, and current state for each instance.\n *\n * @example\n * // In browser DevTools console:\n * Micra.debug()\n * // [Micra] 3 live component(s)\n * // counter $el: <div> state: { count: 5 }\n * // user-list $el: <div> state: { users: [...], loading: false }\n */\nexport function debug(): void {\n if (_instances.size === 0) {\n console.log('[Micra] No live components.')\n return\n }\n console.group(`[Micra] ${_instances.size} live component(s)`)\n for (const [el, instance] of _instances) {\n const name = el.getAttribute('data-component') ?? '(unnamed)'\n console.group(`%c${name}`, 'font-weight:bold;color:#6366f1')\n console.log('$el ', el)\n console.log('state', { ...instance.state })\n console.groupEnd()\n }\n console.groupEnd()\n}\n", "/**\n * src/utils/expr.ts \u2014 JS expression evaluator.\n *\n * Responsibilities:\n * - Compile expression strings into cached functions\n * - Evaluate them against a state object\n * - Fast-path for simple property lookups\n * - Shadow non-state identifiers so directive expressions cannot reach\n * globals like `window`, `fetch`, `constructor`, etc. A small whitelist\n * of utility globals (Math, JSON, Date, ...) remains accessible.\n *\n * LLM NOTE: This module is PURE. It does not touch the DOM or mutate state.\n *\n * Security model:\n * Directive expressions are JavaScript \u2014 they are compiled via `new Function`\n * and run with full JS capability except that bare identifiers must resolve\n * to either a state key, a component instance method, or one of\n * ALLOWED_GLOBALS. This blocks the `constructor.constructor(\"...\")()` chain\n * and accidental access to `window` / `document` / `fetch`. It does NOT\n * sandbox method calls \u2014 if a component method itself touches `window`,\n * that still works. Treat directive templates as trusted code regardless.\n */\n\nimport type { StateRecord } from '../types'\n\n// \u2500\u2500 Expression cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Compiled functions are keyed by expression string \u2014 Function() is only called\n// once per unique expression across the entire app lifetime.\n\n// LLM NOTE: exprCache is module-level (shared across all components).\n// This is intentional \u2014 most apps reuse the same expressions.\n\n// Compiled fn for complex expressions; pre-split parts for simple dot-paths.\n// Storing parts once avoids the SIMPLE_PATH regex test + split on every evalExpr call.\ntype CompiledFn = (state: object, safe: object) => unknown\ntype CachedEntry =\n | { kind: 'fn'; fn: CompiledFn }\n | { kind: 'path'; parts: string[] }\n\nconst exprCache = new Map<string, CachedEntry>()\n// Expressions whose runtime error we have already warned about. Prevents log spam\n// when the same `data-text=\"item.naame\"` typo fires every render.\nconst warnedRuntime = new Set<string>()\n\n// Simple identifier or dot-path: \"count\", \"user.name\", \"item.email\"\n// Matches: letter/$/_ followed by word chars, optionally with .property chains\nconst SIMPLE_PATH = /^[a-zA-Z_$][a-zA-Z0-9_$]*(\\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/\n\n// \u2500\u2500 Safe scope \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Globals reachable from directive expressions. Anything else (window, fetch,\n * constructor, eval, ...) is shadowed by SAFE_OUTER and resolves to undefined.\n */\nconst ALLOWED_GLOBALS = new Set<string>([\n 'Math', 'JSON', 'Date', 'String', 'Number', 'Boolean', 'Array', 'Object',\n 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'NaN', 'Infinity', 'undefined',\n])\n\n/**\n * Outer `with()` scope. Its `has` trap claims every non-whitelisted identifier\n * is \"in scope\" so the JS engine resolves the read on this Proxy (which returns\n * undefined) instead of walking up to the global object. Whitelisted names fall\n * through to globalThis.\n */\n// Sentinel parameter names used by the compiled function. SAFE_OUTER must NOT\n// shadow them, or `with($s)` would resolve to `undefined` via SAFE_OUTER.\nconst PARAM_S = '$s'\nconst PARAM_SAFE = '$safe'\n\nconst SAFE_OUTER: object = new Proxy(Object.create(null) as object, {\n has(_target, key): boolean {\n if (typeof key !== 'string') return false\n if (key === PARAM_S || key === PARAM_SAFE) return false\n return !ALLOWED_GLOBALS.has(key)\n },\n get(): undefined {\n return undefined\n },\n})\n\n/**\n * @internal Per-state safe wrappers \u2014 one per source state object. WeakMap so\n * short-lived itemStates get GC'd with their wrappers.\n */\nconst safeWrapCache = new WeakMap<object, object>()\n\n/**\n * @internal Pre-computed names that live on `Object.prototype`\n * (constructor, toString, hasOwnProperty, ...). Used by safeStateHas to detect\n * built-in keys without re-walking the chain on every call.\n */\nconst OBJ_PROTO_KEYS = new Set<string>(Object.getOwnPropertyNames(Object.prototype))\n\n/**\n * Wrap a state object so its `has` trap reports only \"real\" keys \u2014 own\n * properties or keys reachable up to (but not including) `Object.prototype`.\n * This blocks `'constructor' in state` from leaking the prototype.\n */\nfunction safeStateWrap(state: object): object {\n const cached = safeWrapCache.get(state)\n if (cached) return cached\n const wrapped = new Proxy(state, {\n has(target, key) {\n return safeStateHas(target, key)\n },\n get(target, key) {\n return Reflect.get(target, key)\n },\n })\n safeWrapCache.set(state, wrapped)\n return wrapped\n}\n\n/**\n * Return true iff `key` is reachable on `state` without walking into\n * `Object.prototype`. Works for plain objects, prototype-chained objects, and\n * Proxies with their own `has` trap.\n */\nfunction safeStateHas(state: object, key: PropertyKey): boolean {\n if (typeof key !== 'string') return false\n if (!Reflect.has(state, key)) return false\n // Identifiers that are NOT on Object.prototype are always safe \u2014 accept them\n // immediately without walking the chain.\n if (!OBJ_PROTO_KEYS.has(key)) return true\n // Built-in Object.prototype names (constructor, toString, hasOwnProperty, ...)\n // are only accepted when they have been explicitly placed on the state chain.\n let obj: object | null = state\n while (obj && obj !== Object.prototype) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) return true\n obj = Object.getPrototypeOf(obj) as object | null\n }\n return false\n}\n\n// \u2500\u2500 evalExpr \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Evaluate a JS expression string against a state object.\n *\n * Results are cached by expression string \u2014 repeated evaluations hit the cache.\n * Uses a fast-path for simple dot-paths (e.g. \"count\", \"user.name\") that avoids\n * Function() overhead.\n *\n * @example\n * evalExpr('count > 0', { count: 5 }) // \u2192 true\n * evalExpr('user.name', { user: { name: 'Alice' } }) // \u2192 'Alice'\n * evalExpr('price * qty', { price: 9.99, qty: 3 }) // \u2192 29.97\n */\nexport function evalExpr(expr: string, state: StateRecord): unknown {\n let cached = exprCache.get(expr)\n\n if (!cached) {\n // Determine once whether this is a simple dot-path and cache the result.\n if (SIMPLE_PATH.test(expr)) {\n cached = { kind: 'path', parts: expr.split('.') }\n } else {\n try {\n cached = {\n kind: 'fn',\n fn: new Function('$s', '$safe', `with($safe){with($s){return (${expr})}}`) as CompiledFn,\n }\n } catch {\n warn(`invalid expression \"${expr}\"`)\n cached = { kind: 'fn', fn: () => undefined }\n }\n }\n exprCache.set(expr, cached)\n }\n\n // Fast-path: simple property access \u2014 no Function() needed.\n // Still guarded so bare access to Object.prototype names returns undefined.\n if (cached.kind === 'path') {\n if (!safeStateHas(state, cached.parts[0]!)) return undefined\n return cached.parts.reduce<unknown>(\n (obj, key) => (obj != null ? (obj as StateRecord)[key] : undefined),\n state,\n )\n }\n\n try {\n return cached.fn(safeStateWrap(state), SAFE_OUTER)\n } catch (e) {\n if (!warnedRuntime.has(expr)) {\n warnedRuntime.add(expr)\n warn(`runtime error in \"${expr}\": ${(e as Error).message}`)\n }\n return undefined\n }\n}\n\n// \u2500\u2500 Dev warnings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** @internal Consistent warning prefix. */\nexport function warn(msg: string): void {\n console.warn(`[Micra] ${msg}`)\n}\n", "/**\n * src/core/bus.ts \u2014 Global event bus.\n *\n * Responsibilities:\n * - Publish events (emit)\n * - Subscribe and unsubscribe (on / off)\n * - Provide unsubscribe tokens for component cleanup\n *\n * LLM NOTE: The bus is a module-level singleton.\n * Component instances subscribe via `instance.on()` which auto-registers\n * the unsub token in `instance.__micraSubs` for cleanup on destroy().\n */\n\nimport type { EmitArgs, EventHandler, EventPayload, UnsubFn } from '../types'\n\n// Module-level bus state \u2014 one bus per page load.\nconst _bus = new Map<string, Set<EventHandler>>()\n\n/**\n * Subscribe to a named event. Returns an unsubscribe function.\n * Payload is typed via the `MicraEvents` interface (augmentable).\n *\n * @example\n * const unsub = on('user:login', (user) => console.log(user))\n * unsub() // stop listening\n */\nexport function on<K extends string>(\n event: K,\n handler: (payload: EventPayload<K>) => void,\n): UnsubFn {\n if (!_bus.has(event)) _bus.set(event, new Set())\n _bus.get(event)!.add(handler as EventHandler)\n return () => off(event, handler)\n}\n\n/**\n * Unsubscribe a specific handler from an event.\n */\nexport function off<K extends string>(\n event: K,\n handler: (payload: EventPayload<K>) => void,\n): void {\n const set = _bus.get(event)\n if (!set) return\n set.delete(handler as EventHandler)\n if (set.size === 0) _bus.delete(event)\n}\n\n/**\n * Publish an event to all subscribers. Errors are caught per-handler.\n * Payload is typed via the `MicraEvents` interface (augmentable).\n *\n * @example\n * emit('user:updated', { id: 1, name: 'Alice' })\n */\nexport function emit<K extends string>(event: K, ...args: EmitArgs<K>): void {\n const payload = args[0]\n _bus.get(event)?.forEach(h => {\n try { h(payload) } catch (e) { console.error(`[Micra] bus error [${event}]:`, e) }\n })\n}\n", "/**\n * src/core/reactive.ts \u2014 Reactive state proxy and batch scheduler.\n *\n * Responsibilities:\n * - Wrap a plain state object in a Proxy that notifies on writes\n * - Batch multiple synchronous mutations into a single microtask render\n *\n * LLM NOTE: Both functions are PURE constructors \u2014 they have no side effects\n * beyond setting up a Proxy / Promise chain. No DOM access here.\n */\n\nimport type { StateRecord } from '../types'\n\n/**\n * Wrap `obj` in a shallow Proxy. Any property write calls `schedule()`.\n * Arrays: replace, don't mutate \u2014 `state.items = [...state.items, x]`.\n *\n * @example\n * const raw = { count: 0 }\n * const state = createReactiveState(raw, render)\n * state.count = 5 // triggers render() in next microtask\n */\nexport function createReactiveState<S extends StateRecord>(obj: S, schedule: () => void, onKey?: (key: string) => void): S {\n return new Proxy(obj, {\n set(target, key: string, value: unknown) {\n // Cast through StateRecord \u2014 TypeScript cannot write through a generic index\n ;(target as StateRecord)[key] = value\n onKey?.(key)\n schedule()\n return true\n },\n })\n}\n\n/**\n * Return a debounce function that defers `render` to the next microtask.\n * Multiple calls within the same tick collapse to a single render.\n *\n * @example\n * const schedule = createScheduler(render)\n * schedule() // defers render\n * schedule() // no-op \u2014 already pending\n */\nexport function createScheduler(render: () => void): () => void {\n let pending = false\n return function schedule() {\n if (pending) return\n pending = true\n Promise.resolve().then(() => { pending = false; render() })\n }\n}\n", "/**\n * src/dom/directives.ts \u2014 Apply DOM directives to a component subtree.\n *\n * Responsibilities:\n * - data-text, data-html, data-if, data-show, data-bind, data-model\n * - data-class (additive class toggling)\n *\n * LLM NOTE: applyDirectives() is called on every render. It consumes a\n * pre-computed ScanIndex (built once by scan.ts and cached on the element).\n * The scan replaced 10+ querySelectorAll calls with a single TreeWalker pass.\n *\n * Important: this module does NOT handle data-each \u2014 see dom/each.ts.\n */\n\nimport type {\n CachedIfBinding,\n InternalInstance,\n ScanIndex,\n StateRecord,\n} from '../types'\nimport { evalExpr, warn } from '../utils/expr'\n\n// \u2500\u2500 Directive appliers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Each function is PURE relative to state \u2014 reads state, writes DOM.\n\nfunction applyText(el: Element, expr: string, state: StateRecord): void {\n const text = String(evalExpr(expr, state) ?? '')\n if (el.textContent !== text) el.textContent = text\n}\n\n/**\n * data-html \u2014 writes the expression value as innerHTML.\n *\n * \u26A0\uFE0F XSS WARNING: the value is rendered as raw HTML. Never bind untrusted\n * input here \u2014 use `data-text` (textContent) instead. See docs/directives.md\n * for the full security model.\n */\nfunction applyHtml(el: Element, expr: string, state: StateRecord): void {\n const html = String(evalExpr(expr, state) ?? '')\n if (el.innerHTML !== html) el.innerHTML = html\n}\n\n/**\n * data-if \u2014 true mount/unmount. When the expression is falsy, the element is\n * detached from the DOM and a Comment placeholder takes its slot. When truthy,\n * the element is re-inserted where the placeholder is.\n *\n * Side effect: when an element is detached, its `data-ref` is gone from\n * `this.refs` and its `data-model` listener still exists on the (detached)\n * node \u2014 listeners survive detach.\n *\n * Use `data-show` when you want the cheap display:none toggle instead.\n */\nfunction applyIf(binding: CachedIfBinding, state: StateRecord): void {\n const el = binding.el as HTMLElement\n const truthy = !!evalExpr(binding.expr, state)\n if (truthy) {\n // If a placeholder is currently in the DOM in the element's slot, swap back.\n const ph = binding.placeholder\n if (ph && ph.parentNode) ph.parentNode.replaceChild(el, ph)\n } else {\n // Only detach if currently attached somewhere. Standalone elements\n // (no parent \u2014 common in unit tests) are a no-op.\n const parent = el.parentNode\n if (parent) {\n if (!binding.placeholder) binding.placeholder = document.createComment('if')\n parent.replaceChild(binding.placeholder, el)\n }\n }\n}\n\n/**\n * data-show \u2014 visibility toggle via `style.display`. Element stays in the DOM.\n */\nfunction applyShow(el: Element, expr: string, state: StateRecord): void {\n const desired = evalExpr(expr, state) ? '' : 'none'\n const htmlEl = el as HTMLElement\n if (htmlEl.style.display !== desired) htmlEl.style.display = desired\n}\n\nfunction applyBind(\n el: Element,\n pairs: ReadonlyArray<readonly [string, string]>,\n state: StateRecord,\n): void {\n for (const [attr, valExpr] of pairs) {\n const val = evalExpr(valExpr, state)\n\n if (attr === 'class') {\n (el as HTMLElement).className = String(val ?? '')\n } else if (attr === 'value') {\n if (document.activeElement !== el)\n (el as HTMLInputElement).value = String(val ?? '')\n } else if (attr === 'style') {\n if (typeof val === 'object' && val !== null) {\n Object.assign((el as HTMLElement).style, val)\n } else {\n el.setAttribute('style', String(val ?? ''))\n }\n } else if (typeof val === 'boolean') {\n val ? el.setAttribute(attr, '') : el.removeAttribute(attr)\n } else {\n val == null ? el.removeAttribute(attr) : el.setAttribute(attr, String(val))\n }\n }\n}\n\n/**\n * data-class=\"active:isActive, disabled:count === 0\"\n * Toggles classes additively (does NOT replace full className like data-bind:class).\n * Pairs are pre-parsed at scan time.\n */\nfunction applyClass(\n el: Element,\n pairs: ReadonlyArray<readonly [string, string]>,\n state: StateRecord,\n): void {\n for (const [cls, valExpr] of pairs) {\n el.classList.toggle(cls, Boolean(evalExpr(valExpr, state)))\n }\n}\n\nfunction applyModel(\n el: Element,\n key: string,\n rawState: StateRecord,\n): void {\n const html = el as HTMLInputElement\n const stateVal = rawState[key]\n const desired = stateVal == null ? '' : String(stateVal)\n // Only write when out of sync. This is a no-op during live typing (the input\n // event already drove state to match el.value) but still propagates\n // programmatic resets such as `this.state.q = ''` on focused inputs.\n if (html.value !== desired) html.value = desired\n // listener is attached separately in events.ts \u2014 this only syncs the value\n}\n\n// \u2500\u2500 Main entry point \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Apply all non-each directives to a component subtree.\n *\n * Consumes a pre-computed ScanIndex. data-if runs first so subsequent\n * directives don't write into a tree that's about to be detached this tick.\n *\n * @param scan - Pre-computed scan from scan.ts (cached per element)\n * @param state - Expression state (may include item/index for each rows)\n * @param rawState - Raw (non-proxy) state for model sync\n */\nexport function applyDirectives<S extends StateRecord>(\n scan: ScanIndex,\n state: StateRecord,\n rawState: StateRecord,\n _instance: InternalInstance<S>,\n): void {\n // data-if runs first so subsequent directives don't write into a tree that's\n // about to be detached this tick.\n for (const b of scan.if) applyIf(b, state)\n for (const b of scan.text) applyText(b.el, b.expr, state)\n for (const b of scan.html) applyHtml(b.el, b.expr, state)\n for (const b of scan.show) applyShow(b.el, b.expr, state)\n for (const b of scan.bind) applyBind(b.el, b.pairs, state)\n for (const b of scan.model) applyModel(b.el, b.expr.trim(), rawState)\n for (const b of scan.class) applyClass(b.el, b.pairs, state)\n}\n\n// \u2500\u2500 Dev warning helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Validate directive usage and emit dev warnings.\n * Called once after the initial render of a component, with the already-built\n * scan so we don't walk the DOM again.\n *\n * @internal\n */\nexport function validateDirectives(scan: ScanIndex): void {\n for (const el of scan.each) {\n const tmpl = el as HTMLTemplateElement & { __micraNoKeyWarned?: true }\n if (!el.hasAttribute('data-key') && !tmpl.__micraNoKeyWarned) {\n tmpl.__micraNoKeyWarned = true\n warn(\n `data-each=\"${el.getAttribute('data-each')}\" has no data-key \u2014 ` +\n `keyed diff disabled. Add data-key=\"id\" for better performance.`,\n )\n }\n }\n\n // data-bind=\"class:...\" replaces className wholesale, which fights with\n // data-class on the same element. Warn so the developer picks one.\n for (const b of scan.bind) {\n const hasClassBind = b.pairs.some(p => p[0] === 'class')\n if (hasClassBind && b.el.hasAttribute('data-class')) {\n warn(\n `element has both data-bind=\"class:...\" and data-class \u2014 they fight ` +\n `on every render. Use one.`,\n )\n }\n }\n}\n\n// Re-export warn for use in other modules\nexport { warn }\n", "/**\n * src/dom/events.ts \u2014 DOM event binding.\n *\n * Responsibilities:\n * - Bind `data-on=\"event:method\"` listeners (once per element)\n * - Bind `@event=\"method\"` shorthand (once per element)\n * - Bind `data-model` two-way input listeners (once per element)\n *\n * LLM NOTE: Every listener attached here is also recorded in\n * instance.__micraListeners so destroy() can remove it cleanly.\n * Re-render skips already-bound elements via per-element __micra* flags.\n *\n * All three binders accept pre-computed element lists from scan.ts \u2014\n * no DOM queries here.\n */\n\nimport type {\n CachedBinding,\n InternalInstance,\n MicraElement,\n StateRecord,\n} from '../types'\nimport { warn } from '../utils/expr'\n\n/** @internal Attach a DOM listener and track it on the instance for destroy(). */\nfunction track<S extends StateRecord>(\n instance: InternalInstance<S>,\n el: Element,\n type: string,\n fn: EventListener,\n): void {\n el.addEventListener(type, fn)\n ;(instance.__micraListeners ??= []).push({ el, type, fn })\n}\n\n// \u2500\u2500 data-on \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Bind `data-on=\"event:method[,event2:method2]\"` listeners.\n * Listeners are bound once \u2014 re-render calls are no-ops for already-bound elements.\n *\n * Supports modifiers: `click.prevent`, `click.stop`, `click.self`.\n *\n * @param els - Pre-computed list of [data-on] elements from scan.ts\n *\n * @example\n * <button data-on=\"click:save\">Save</button>\n * <form data-on=\"submit.prevent:handleSubmit\">\n */\nexport function bindDataOn<S extends StateRecord>(\n els: Element[],\n instance: InternalInstance<S>,\n): void {\n for (const el of els) {\n const mEl = el as MicraElement\n if (mEl.__micraEvents) continue\n mEl.__micraEvents = true\n\n const spec = mEl.dataset['on'] ?? ''\n for (const part of spec.split(',')) {\n const [evSpec, method] = part.trim().split(':') as [string, string]\n if (!evSpec || !method) continue\n\n const [evName, ...mods] = evSpec.split('.')\n\n track(instance, el, evName!, (e: Event) => {\n if (mods.includes('prevent')) e.preventDefault()\n if (mods.includes('stop')) e.stopPropagation()\n if (mods.includes('self') && e.target !== el) return\n\n const fn = instance[method.trim()]\n if (typeof fn === 'function') (fn as (e: Event) => void).call(instance, e)\n else warn(`method \"${method.trim()}\" not found`)\n })\n }\n }\n}\n\n// \u2500\u2500 @event shorthand \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Bind `@event=\"method\"` shorthand attributes (Stimulus-style).\n * Bound once per element via `__micraAtBound` \u2014 re-renders are no-ops.\n *\n * @param els - Pre-computed list of elements with at least one @-prefixed attr\n * (from scan.ts \u2014 replaces the old `querySelectorAll('*')` walk)\n *\n * @example\n * <button @click=\"increment\">+</button>\n * <form @submit.prevent=\"handleSubmit\">\n */\nexport function bindAtEvents<S extends StateRecord>(\n els: Element[],\n instance: InternalInstance<S>,\n): void {\n for (const el of els) {\n const mEl = el as MicraElement\n if (mEl.__micraAtBound) continue\n\n let bound = false\n for (const attr of Array.from(el.attributes)) {\n if (!attr.name.startsWith('@')) continue\n const [evSpec, ...rest] = attr.name.slice(1).split('.')\n const method = attr.value.trim()\n\n track(instance, el, evSpec!, (e: Event) => {\n if (rest.includes('prevent')) e.preventDefault()\n if (rest.includes('stop')) e.stopPropagation()\n if (rest.includes('self') && e.target !== el) return\n\n const fn = instance[method]\n if (typeof fn === 'function') (fn as (e: Event) => void).call(instance, e)\n else warn(`method \"${method}\" not found`)\n })\n bound = true\n }\n if (bound) mEl.__micraAtBound = true\n }\n}\n\n// \u2500\u2500 data-model \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Two-way binding: `data-model=\"key\"` wires <input>/<select>/<textarea>\n * to `state[key]`. Binding is attached once per element.\n *\n * Numeric inputs (`type=\"number\"` / `type=\"range\"`) write numbers, not strings.\n * Checkbox inputs write booleans. Everything else writes strings.\n *\n * @param bindings - Pre-computed model bindings from scan.ts\n * (each carries { el, expr } where expr is the state key)\n *\n * @example\n * <input data-model=\"search\"> // updates state.search on every keystroke\n * <select data-model=\"sortBy\"> // updates state.sortBy on change\n */\nexport function bindModels<S extends StateRecord>(\n bindings: CachedBinding[],\n instance: InternalInstance<S>,\n): void {\n for (const { el, expr } of bindings) {\n const mEl = el as MicraElement\n if (mEl.__micraModel) continue\n mEl.__micraModel = true\n\n const key = expr.trim()\n const tag = el.tagName\n const inputEl = el as HTMLInputElement\n const inputType = inputEl.type\n\n const update = () => {\n let val: unknown\n if (tag === 'INPUT' && inputType === 'checkbox') {\n val = inputEl.checked\n } else if (tag === 'INPUT' && (inputType === 'number' || inputType === 'range')) {\n // Empty string \u2192 NaN; preserve raw empty as null so state stays \"unfilled\"\n val = inputEl.value === '' ? null : inputEl.valueAsNumber\n } else {\n val = inputEl.value\n }\n ;(instance.state as StateRecord)[key] = val\n }\n\n const evType = tag === 'SELECT' || inputType === 'radio' ? 'change' : 'input'\n track(instance, el, evType, update)\n }\n}\n", "/**\n * src/dom/scan.ts \u2014 Single-pass directive/event/ref scanner.\n *\n * Replaces 10+ querySelectorAll calls per render with ONE TreeWalker\n * traversal that classifies every directive attribute in a single visit.\n *\n * Boundaries:\n * - REJECT (skip subtree) on nested [data-component] \u2014 same semantics as\n * the old `filterOwn` helper, but applied during the walk so we don't\n * even *visit* those nodes.\n * - <template> contents are not visited (browser TreeWalker default).\n * `<template data-each>` itself IS visited and classified into scan.each;\n * its children are processed by each.ts on every render \u2014 fresh rows\n * are wrapped in a per-row element and scanned via scanComponent.\n *\n * Hot-path notes:\n * - We read `el.attributes` once and switch by suffix. No allocations per\n * non-matching attr.\n * - Pair-parsing (`data-bind`, `data-class`) happens here, once, at scan\n * time. Reused on every render.\n */\n\nimport type { CachedIfBinding, CachedPairBinding, ScanIndex } from \"../types\";\n\nfunction emptyScan(): ScanIndex {\n return {\n text: [],\n html: [],\n if: [],\n show: [],\n bind: [],\n model: [],\n class: [],\n each: [],\n on: [],\n atEvents: [],\n refs: [],\n };\n}\n\n/** @internal Parse `name:expr, name2:expr2` once at scan time. */\nfunction parsePairs(expr: string): Array<readonly [string, string]> {\n const out: Array<readonly [string, string]> = [];\n for (const part of expr.split(\",\")) {\n const colon = part.indexOf(\":\");\n if (colon === -1) continue;\n const left = part.slice(0, colon).trim();\n const right = part.slice(colon + 1).trim();\n if (!left) continue;\n out.push([left, right]);\n }\n return out;\n}\n\n/** @internal Classify every relevant attribute on one element. */\nfunction classify(el: Element, scan: ScanIndex): void {\n // <template data-each> is the only directive we permit on a <template>.\n // Other directives on a <template> would be meaningless (the content lives\n // in template.content, not template.children).\n if (el.tagName === \"TEMPLATE\") {\n if (el.hasAttribute(\"data-each\")) scan.each.push(el);\n return;\n }\n\n const attrs = el.attributes;\n let atEventSeen = false;\n\n for (let i = 0; i < attrs.length; i++) {\n const a = attrs[i]!;\n const name = a.name;\n\n // Fast path: most attribute names aren't ours. First-char check rejects\n // the common case (id, class, style, href, \u2026) without a string compare.\n const first = name.charCodeAt(0);\n\n if (first === 64 /* '@' */) {\n // @event=\"method\" or @event.modifier=\"method\"\n if (!atEventSeen) {\n scan.atEvents.push(el);\n atEventSeen = true;\n }\n continue;\n }\n\n // data-X attributes\n if (\n first === 100 /* d */ &&\n name.length >= 6 &&\n name.charCodeAt(4) === 45 /* '-' */\n ) {\n // 'data-' prefix\n const rest = name.slice(5);\n switch (rest) {\n case \"text\":\n scan.text.push({ el, expr: a.value });\n break;\n case \"html\":\n scan.html.push({ el, expr: a.value });\n break;\n case \"if\":\n scan.if.push({ el, expr: a.value } as CachedIfBinding);\n break;\n case \"show\":\n scan.show.push({ el, expr: a.value });\n break;\n case \"bind\": {\n const pairs = parsePairs(a.value);\n scan.bind.push({ el, expr: a.value, pairs } as CachedPairBinding);\n break;\n }\n case \"model\":\n scan.model.push({ el, expr: a.value });\n break;\n case \"class\": {\n const pairs = parsePairs(a.value);\n scan.class.push({ el, expr: a.value, pairs } as CachedPairBinding);\n break;\n }\n case \"on\":\n scan.on.push(el);\n break;\n case \"ref\":\n scan.refs.push(el);\n break;\n // data-key, data-each, data-component, data-* user attrs \u2014 ignored here\n }\n }\n }\n}\n\n/** @internal Shared filter: stop descending into nested components. */\nconst NESTED_COMPONENT_FILTER: NodeFilter = {\n acceptNode(node: Node): number {\n if ((node as Element).hasAttribute(\"data-component\"))\n return NodeFilter.FILTER_REJECT;\n return NodeFilter.FILTER_ACCEPT;\n },\n};\n\n/**\n * Scan an Element subtree owned by one component. Skips nested\n * [data-component] subtrees entirely. Visits the root itself.\n *\n * Cached on `el.__micraScan` after the first call \u2014 subsequent renders\n * are free.\n */\nexport function scanComponent(root: Element): ScanIndex {\n const scan = emptyScan();\n\n // Always classify root itself first \u2014 the TreeWalker's filter would\n // REJECT it if it had `data-component` (which it normally does for a\n // mounted component). The filter is for *descendants*.\n classify(root, scan);\n\n const walker = document.createTreeWalker(\n root,\n NodeFilter.SHOW_ELEMENT,\n NESTED_COMPONENT_FILTER,\n );\n\n let node: Element | null = walker.nextNode() as Element | null;\n while (node) {\n classify(node, scan);\n node = walker.nextNode() as Element | null;\n }\n\n return scan;\n}\n\n", "/**\n * src/dom/each.ts \u2014 Keyed and non-keyed list rendering (data-each).\n *\n * Responsibilities:\n * - Process `<template data-each=\"items\" data-key=\"id\">` elements\n * - Keyed diff: reuse/reorder DOM nodes by key \u2014 O(n) with a Map\n * - Non-keyed fallback: length-based positional reuse \u2014 min(old, new) rows\n * are kept as-is, the tail is removed or new rows are appended\n * - Apply directives to each row with a scoped itemState\n *\n * LLM NOTE: renderList() is called on every render cycle AFTER applyDirectives().\n * The template list comes pre-scanned from scan.ts \u2014 no DOM queries here.\n * Each row node gets its own ScanIndex cached on `node.__micraScan` so\n * re-renders of that row don't re-walk the DOM.\n * Keyed mode (data-key present) mutates the DOM in-place \u2014 nodes are\n * created once and reused. Non-keyed mode also reuses existing nodes\n * positionally: only the length delta is touched, the rest gets a fresh\n * itemState and re-applies directives.\n */\n\nimport type {\n InternalInstance,\n MicraElement,\n MicraTemplate,\n StateRecord,\n} from '../types'\nimport { evalExpr, warn } from '../utils/expr'\nimport { applyDirectives } from './directives'\nimport { bindDataOn, bindAtEvents, bindModels } from './events'\nimport { scanComponent } from './scan'\n\n/**\n * Process all `<template data-each>` elements found by the scanner.\n * Scoped itemState makes `item`, `index`, `$index` available in row expressions.\n *\n * @param templates - Pre-scanned list of <template data-each> elements\n * @param state - Expression state (proxy merging rawState + instance)\n * @param rawState - Raw (non-proxy) state \u2014 used for model binding\n * @param instance - Component instance (for event binding)\n * @param triggerKey - Which state key triggered this render (null = initial, 'MULTIPLE' = batch)\n */\nexport function renderList<S extends StateRecord>(\n templates: Element[],\n state: StateRecord,\n rawState: StateRecord,\n instance: InternalInstance<S>,\n triggerKey: string | null | 'MULTIPLE',\n): void {\n for (const tmplEl of templates) {\n if (tmplEl.tagName !== 'TEMPLATE') continue\n const tmpl = tmplEl as MicraTemplate\n\n const itemsExpr = tmpl.getAttribute('data-each')!\n const keyAttr = tmpl.getAttribute('data-key') ?? null\n const items = evalExpr(itemsExpr, state)\n\n // Ensure marker comment + internal state are initialized\n if (!tmpl.__micraMarker) {\n const m = document.createComment(`each:${itemsExpr}`)\n tmpl.after(m)\n tmpl.__micraMarker = m\n tmpl.__micraNodes = new Map()\n tmpl.__micraList = []\n }\n\n const marker = tmpl.__micraMarker\n const keyMap = tmpl.__micraNodes\n // The template (and its marker) is currently detached \u2014 likely a data-if\n // ancestor unmounted this subtree. Nothing to do until it returns.\n if (!marker.parentNode) continue\n\n // Empty / non-array: clear all rendered rows\n if (!Array.isArray(items)) {\n tmpl.__micraList.forEach(n => n.remove())\n tmpl.__micraList = []\n keyMap.clear()\n continue\n }\n\n // canSkipUnchanged: true when only this list's state key changed \u2014 rows\n // whose item reference and index are both unchanged can skip applyDirectives.\n const canSkipUnchanged = triggerKey !== null &&\n triggerKey !== 'MULTIPLE' &&\n triggerKey === itemsExpr\n\n if (keyAttr) {\n renderKeyed(tmpl, items as StateRecord[], keyAttr, marker, keyMap, state, rawState, instance, canSkipUnchanged)\n } else {\n renderNoKey(tmpl, items as StateRecord[], marker, state, rawState, instance, canSkipUnchanged)\n }\n }\n}\n\n// \u2500\u2500 Row node creation (shared by both paths) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Clone the template into a fresh row node, wrapping multi-root content in\n * `<micra-each-item style=\"display:contents\">` so the row always corresponds\n * to a single, stable DOM element. Scans, binds listeners once, and caches\n * an empty itemState prototyped from `state` (filled in by the caller).\n */\nfunction createRowNode<S extends StateRecord>(\n tmpl: MicraTemplate,\n state: StateRecord,\n instance: InternalInstance<S>,\n): MicraElement {\n const frag = tmpl.content.cloneNode(true) as DocumentFragment\n let node: MicraElement\n if (frag.childNodes.length === 1) {\n node = frag.firstElementChild as MicraElement\n } else {\n node = document.createElement('micra-each-item') as MicraElement\n node.style.display = 'contents'\n node.append(frag)\n }\n const rowScan = scanComponent(node)\n node.__micraScan = rowScan\n node._itemState = Object.create(state) as StateRecord\n bindDataOn(rowScan.on, instance)\n bindAtEvents(rowScan.atEvents, instance)\n bindModels(rowScan.model, instance)\n return node\n}\n\n// \u2500\u2500 Keyed diff \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction renderKeyed<S extends StateRecord>(\n tmpl: MicraTemplate,\n items: StateRecord[],\n keyAttr: string,\n marker: Comment,\n keyMap: Map<unknown, MicraElement>,\n state: StateRecord,\n rawState: StateRecord,\n instance: InternalInstance<S>,\n canSkipUnchanged: boolean,\n): void {\n const nextKeys = new Set<unknown>()\n const nextNodes: MicraElement[] = []\n let warnedNullKey = false\n let warnedDupKey = false\n\n for (const [index, item] of items.entries()) {\n const key = item[keyAttr]\n if (key == null && !warnedNullKey) {\n warn(`data-key=\"${keyAttr}\" is null/undefined on item at index ${index}`)\n warnedNullKey = true\n }\n if (nextKeys.has(key) && !warnedDupKey) {\n warn(`data-key=\"${keyAttr}\" has duplicate value ${JSON.stringify(key)} \u2014 rows will collide`)\n warnedDupKey = true\n }\n nextKeys.add(key)\n\n let node = keyMap.get(key) as MicraElement | undefined\n\n if (!node) {\n node = createRowNode(tmpl, state, instance)\n node.__micraKey = key\n keyMap.set(key, node)\n } else if (canSkipUnchanged && node.__micraItem === item && node.__micraIndex === index) {\n // Item reference and index are unchanged, and no other state key changed\n // this cycle \u2014 the DOM already reflects the latest values. Skip re-render.\n nextNodes.push(node)\n continue\n }\n\n node.__micraItem = item\n node.__micraIndex = index\n\n // Reuse the cached itemState, just update the per-row values.\n const itemState = node._itemState!\n itemState.item = item\n itemState.index = index\n itemState.$index = index\n\n // Use the cached scan if present (created above on first sight of this key);\n // older paths may pass a node we haven't scanned yet.\n const rowScan = node.__micraScan ?? (node.__micraScan = scanComponent(node))\n applyDirectives(rowScan, itemState, rawState, instance)\n nextNodes.push(node)\n }\n\n // Remove stale nodes\n for (const [key, node] of keyMap) {\n if (!nextKeys.has(key)) { node.remove(); keyMap.delete(key) }\n }\n\n const prevList = tmpl.__micraList\n if (prevList.length === 0) {\n // First render (or refill after a clear): every node is new and already in\n // order \u2014 batch into one fragment so the DOM takes a single insertion\n // instead of N anchor.after() calls. Skips LIS entirely.\n if (nextNodes.length) {\n const frag = document.createDocumentFragment()\n for (const node of nextNodes) frag.append(node)\n marker.after(frag)\n }\n } else {\n // Skip DOM reorder when list order is unchanged (pure JS array compare, no DOM reads).\n let orderChanged = nextNodes.length !== prevList.length\n if (!orderChanged) {\n for (let i = 0; i < nextNodes.length; i++) {\n if (nextNodes[i] !== prevList[i]) { orderChanged = true; break }\n }\n }\n if (orderChanged) reorderKeyed(nextNodes, prevList, marker)\n }\n\n tmpl.__micraList = nextNodes\n}\n\n// \u2500\u2500 Keyed list reorder (LIS) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Move DOM nodes to match `nextNodes` order using the minimum number of moves.\n *\n * Computes the Longest Increasing Subsequence of each node's position in prevList \u2014\n * nodes in the LIS keep their place. Only the others are re-inserted via anchor.after().\n *\n * Complexity: O(n log n) for LIS, O(k) DOM operations where k = nodes that moved.\n * For a 2-node swap this means 2 DOM ops instead of n.\n */\nfunction reorderKeyed(nextNodes: MicraElement[], prevList: MicraElement[], marker: Comment): void {\n const prevPos = new Map<MicraElement, number>()\n for (let i = 0; i < prevList.length; i++) prevPos.set(prevList[i]!, i)\n\n const n = nextNodes.length\n const tails: number[] = [] // patience sort: smallest tail at each LIS length\n const tailIdx: number[] = [] // index into nextNodes for each tail\n const prev: number[] = new Array(n).fill(-1)\n\n for (let i = 0; i < n; i++) {\n const p = prevPos.get(nextNodes[i]!)\n if (p === undefined) continue // new node \u2014 always moved\n let lo = 0, hi = tails.length\n while (lo < hi) { const m = (lo + hi) >> 1; tails[m]! < p ? lo = m + 1 : hi = m }\n if (lo > 0) prev[i] = tailIdx[lo - 1]!\n tails[lo] = p\n tailIdx[lo] = i\n }\n\n // Reconstruct stable (non-moving) set from LIS parent chain\n const stable = new Set<number>()\n let idx: number = tailIdx[tails.length - 1]!\n while (idx >= 0) { stable.add(idx); idx = prev[idx]! }\n\n // Move unstable nodes into position; stable (LIS) nodes serve as anchors\n let anchor: ChildNode = marker\n for (let i = 0; i < n; i++) {\n const node = nextNodes[i]!\n if (stable.has(i)) { anchor = node; continue }\n anchor.after(node)\n anchor = node\n }\n}\n\n// \u2500\u2500 Non-keyed (positional reuse) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Diff a non-keyed list by length: reuse the first min(prev, next) DOM nodes,\n * remove the tail when the list shrinks, clone fresh rows for the growth delta.\n * Multi-root template rows are wrapped in `<micra-each-item style=\"display:contents\">`\n * \u2014 same as keyed mode \u2014 so the reused list is one DOM node per row.\n */\nfunction renderNoKey<S extends StateRecord>(\n tmpl: MicraTemplate,\n items: StateRecord[],\n marker: Comment,\n state: StateRecord,\n rawState: StateRecord,\n instance: InternalInstance<S>,\n canSkipUnchanged: boolean,\n): void {\n const prevList = tmpl.__micraList\n const prevLen = prevList.length\n const nextLen = items.length\n const reuseLen = nextLen < prevLen ? nextLen : prevLen\n const nextList: MicraElement[] = new Array(nextLen)\n\n // 1. Reuse [0, reuseLen): refresh itemState, re-apply directives in place.\n for (let i = 0; i < reuseLen; i++) {\n const node = prevList[i]!\n const item = items[i]!\n if (canSkipUnchanged && node.__micraItem === item && node.__micraIndex === i) {\n nextList[i] = node\n continue\n }\n node.__micraItem = item\n node.__micraIndex = i\n const itemState = node._itemState!\n itemState.item = item\n itemState.index = i\n itemState.$index = i\n applyDirectives(node.__micraScan!, itemState, rawState, instance)\n nextList[i] = node\n }\n\n // 2. Shrink: remove tail nodes [nextLen, prevLen).\n for (let i = nextLen; i < prevLen; i++) {\n prevList[i]!.remove()\n }\n\n // 3. Grow: clone and attach fresh rows for [prevLen, nextLen).\n if (nextLen > prevLen) {\n const frag = document.createDocumentFragment()\n for (let i = prevLen; i < nextLen; i++) {\n const node = createRowNode(tmpl, state, instance)\n const item = items[i]!\n const itemState = node._itemState!\n itemState.item = item\n itemState.index = i\n itemState.$index = i\n node.__micraEach = true\n node.__micraItem = item\n node.__micraIndex = i\n applyDirectives(node.__micraScan!, itemState, rawState, instance)\n nextList[i] = node\n frag.append(node)\n }\n // Insert after the last reused node, or the marker if the list was empty.\n const anchor: ChildNode = prevLen > 0 ? nextList[prevLen - 1]! : marker\n anchor.after(frag)\n }\n\n tmpl.__micraList = nextList\n}\n", "/**\n * src/dom/refs.ts \u2014 data-ref collection.\n *\n * Responsibilities:\n * - Populate `instance.refs` from a pre-scanned list of [data-ref] elements.\n *\n * LLM NOTE: This module is PURE relative to state \u2014 it only reads DOM attributes\n * and writes to instance.refs. It does NOT trigger renders.\n */\n\nimport type { InternalInstance, MicraElement, StateRecord } from '../types'\n\n/**\n * Build `instance.refs` from the pre-scanned [data-ref] elements.\n *\n * Called once after the initial render and again on every re-render (refs may\n * point to newly created elements after an each-list update).\n *\n * @param els - List of [data-ref] elements from scan.ts\n *\n * @example\n * // HTML: <canvas data-ref=\"chart\">\n * // JS: this.refs.chart \u2192 HTMLCanvasElement\n */\nexport function collectRefs<S extends StateRecord>(\n els: Element[],\n instance: InternalInstance<S>,\n): void {\n if (!els.length) return\n instance.refs = {}\n for (const el of els) {\n const name = (el as MicraElement).dataset['ref']\n if (name) instance.refs[name] = el as HTMLElement\n }\n}\n", "/**\n * src/core/mount.ts \u2014 Mount a component definition onto a DOM element.\n *\n * Responsibilities:\n * - Create and initialize an InternalInstance\n * - Set up reactive state + batch scheduler\n * - Wire render(), destroy(), prop(), fetch(), on(), emit()\n * - Run initial render + call onCreate() in a microtask\n *\n * LLM NOTE: This is the core of the Micra runtime.\n * mount() is called by both the public Micra.mount() API and by start()\n * (which scans the DOM for [data-component] elements).\n */\n\nimport type {\n ComponentDefinition,\n ComponentInstance,\n ComponentMethods,\n EventHandler,\n EventPayload,\n InternalInstance,\n MicraElement,\n\n StateRecord,\n UnsubFn,\n} from \"../types\";\nimport { warn } from \"../utils/expr\";\nimport { micraFetch } from \"../utils/fetch\";\nimport { on as busOn, emit as busEmit } from \"../core/bus\";\nimport { createReactiveState, createScheduler } from \"../core/reactive\";\nimport { applyDirectives, validateDirectives } from \"../dom/directives\";\nimport { renderList } from \"../dom/each\";\nimport { bindDataOn, bindAtEvents, bindModels } from \"../dom/events\";\nimport { collectRefs } from \"../dom/refs\";\nimport { scanComponent } from \"../dom/scan\";\nimport { _instances } from \"../core/registry\";\n\n/**\n * Mount a component definition onto a DOM element.\n * Returns the component instance, or null if the root element is not found.\n *\n * Already-mounted elements return the existing instance.\n *\n * Both `S` (state) and `M` (methods) are inferred from the literal \u2014 the\n * returned instance is fully typed: `inst.state.X` and `inst.someMethod()`\n * are checked.\n *\n * @example\n * const instance = Micra.mount('#counter', {\n * state: { count: 0 },\n * inc() { this.state.count++ },\n * })\n * instance?.inc()\n * instance?.state.count\n */\nexport function mount<S extends StateRecord, M>(\n selector: string | HTMLElement,\n definition: ComponentDefinition<S, M>,\n): ComponentInstance<S, M> | null {\n const root =\n typeof selector === \"string\"\n ? document.querySelector<HTMLElement>(selector)\n : selector;\n\n if (!root) {\n warn(`\"${selector}\" not found`);\n return null;\n }\n\n // Already mounted \u2014 return existing instance without re-mounting\n if (_instances.has(root))\n return _instances.get(root) as unknown as ComponentInstance<S, M>;\n\n const rawState: StateRecord = { ...(definition.state ?? {}) };\n const instance = { $el: root, refs: {} } as InternalInstance<S>;\n\n // Copy user-defined methods from definition to instance\n for (const [key, val] of Object.entries(\n definition as Record<string, unknown>,\n )) {\n if (key === \"state\" || key === \"onCreate\" || key === \"onDestroy\") continue;\n if (typeof val === \"function\") instance[key] = val;\n }\n\n // \u2500\u2500 prop() \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Read data-* attributes from the root element with auto-cast.\n instance.prop = function <T>(name: string, defaultVal?: T): T | undefined {\n const val = root.dataset[name];\n if (val === undefined) return defaultVal;\n if (val === \"true\") return true as unknown as T;\n if (val === \"false\") return false as unknown as T;\n if (val !== \"\" && !isNaN(Number(val))) return Number(val) as unknown as T;\n return val as unknown as T;\n } as ComponentInstance<S>[\"prop\"];\n\n // \u2500\u2500 fetch(), emit(), on() \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n instance.fetch = micraFetch;\n instance.emit = busEmit;\n\n instance.on = <K extends string>(\n event: K,\n handler: (payload: EventPayload<K>) => void,\n ): UnsubFn => {\n const unsub = busOn(event, handler as EventHandler);\n if (!instance.__micraSubs) instance.__micraSubs = [];\n instance.__micraSubs.push(unsub);\n return unsub;\n };\n\n // \u2500\u2500 Render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let isRendering = false;\n // Track which state key triggered the current render cycle.\n // 'MULTIPLE' means more than one key was written before the microtask fired.\n let _triggerKey: string | null | \"MULTIPLE\" = null;\n const schedule = createScheduler(() => instance.render());\n instance.state = createReactiveState(rawState, schedule, (key) => {\n if (_triggerKey === null) _triggerKey = key;\n else if (_triggerKey !== key) _triggerKey = \"MULTIPLE\";\n }) as S;\n\n // Expression state: proxy that falls back to instance methods so expressions\n // like `data-text=\"formatDate(item.date)\"` can call component methods.\n //\n // Instance methods are returned BOUND to the instance \u2014 directive expressions\n // call them as bare identifiers via `with()`, which would normally lose `this`.\n // Bound copies are memoized per method name so repeated reads are cheap.\n //\n // Both traps reject Object.prototype names ('constructor', 'toString', ...) \u2014\n // accessing them via a directive expression returns undefined instead of\n // leaking the prototype.\n const boundMethods = new Map<string, Function>();\n const exprState = new Proxy(rawState, {\n get(target, key: string) {\n if (Object.prototype.hasOwnProperty.call(target, key)) return target[key];\n if (\n Object.prototype.hasOwnProperty.call(instance, key) &&\n typeof instance[key] === \"function\"\n ) {\n const cached = boundMethods.get(key);\n if (cached) return cached;\n const bound = (instance[key] as Function).bind(instance);\n boundMethods.set(key, bound);\n return bound;\n }\n return undefined;\n },\n has(target, key: string) {\n if (typeof key !== \"string\") return false;\n if (Object.prototype.hasOwnProperty.call(target, key)) return true;\n return (\n Object.prototype.hasOwnProperty.call(instance, key) &&\n typeof instance[key] === \"function\"\n );\n },\n });\n\n let warnedReentry = false;\n instance.render = function () {\n if (instance.__micraDestroyed) return;\n const triggerKey = _triggerKey;\n _triggerKey = null;\n if (isRendering) {\n if (!warnedReentry) {\n warn(\n \"render() re-entry detected \u2014 mutation inside a directive expression is ignored. Move state writes to a method.\",\n );\n warnedReentry = true;\n }\n return;\n }\n isRendering = true;\n try {\n // Single-pass scan, cached on the root for re-renders. Replaces what\n // used to be ~10 separate querySelectorAll passes per render.\n const mRoot = root as MicraElement;\n const scan =\n mRoot.__micraScan ?? (mRoot.__micraScan = scanComponent(root));\n applyDirectives(scan, exprState, rawState, instance);\n renderList(scan.each, exprState, rawState, instance, triggerKey);\n bindDataOn(scan.on, instance);\n bindAtEvents(scan.atEvents, instance);\n bindModels(scan.model, instance);\n collectRefs(scan.refs, instance);\n } finally {\n isRendering = false;\n }\n };\n\n // \u2500\u2500 Destroy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n instance.destroy = function () {\n if (instance.__micraDestroyed) return;\n instance.__micraDestroyed = true;\n\n // Remove every DOM listener attached by bindDataOn / bindAtEvents / bindModels.\n instance.__micraListeners?.forEach(({ el, type, fn }) =>\n el.removeEventListener(type, fn),\n );\n instance.__micraListeners = [];\n\n // Clear per-element flags & cached scan so a future re-mount of the same DOM works.\n const clearFlags = (el: Element) => {\n const m = el as MicraElement;\n delete m.__micraEvents;\n delete m.__micraAtBound;\n delete m.__micraModel;\n delete m.__micraScan;\n };\n clearFlags(root);\n root.querySelectorAll(\"*\").forEach(clearFlags);\n\n instance.__micraSubs?.forEach((unsub) => unsub());\n instance.__micraSubs = [];\n\n if (typeof (definition as Record<string, unknown>).onDestroy === \"function\")\n (definition.onDestroy as () => void).call(instance);\n _instances.delete(root);\n };\n\n // \u2500\u2500 Bootstrap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n _instances.set(root, instance as InternalInstance);\n instance.render();\n\n // Validate directive usage and emit dev warnings \u2014 reuses the same scan.\n const mRoot = root as MicraElement;\n if (mRoot.__micraScan) validateDirectives(mRoot.__micraScan);\n\n if (typeof (definition as Record<string, unknown>).onCreate === \"function\")\n Promise.resolve().then(() =>\n (definition.onCreate as () => void | Promise<void>).call(instance),\n );\n\n return instance as unknown as ComponentInstance<S, M>;\n}\n", "/**\n * src/core/start.ts \u2014 Auto-mount via [data-component].\n *\n * Responsibilities:\n * - Scan the DOM (or a subtree) for [data-component] elements\n * - Mount each using the registered definition\n * - Skip already-mounted elements (safe to call multiple times)\n * - Warn clearly when a component name is not registered\n *\n * LLM NOTE: start() is SSR-friendly \u2014 calling it multiple times is safe\n * because mount() checks _instances before re-mounting.\n */\n\nimport { warn } from '../utils/expr'\nimport { _registry, _instances } from './registry'\nimport { mount } from './mount'\n\n/**\n * Scan for `[data-component]` elements and auto-mount registered definitions.\n *\n * Pass a subtree root to limit the scan (e.g., after a partial SSR update):\n * `Micra.start(document.getElementById('panel'))`\n *\n * @example\n * // Mount everything on the page (called once after DOM ready)\n * Micra.start()\n *\n * // Re-mount after injecting new HTML\n * Micra.start(document.querySelector('#dynamic-section'))\n */\nexport function start(root: Document | HTMLElement = document): void {\n root.querySelectorAll<HTMLElement>('[data-component]').forEach(el => {\n if (_instances.has(el)) return // already mounted \u2014 skip\n const name = el.getAttribute('data-component')!\n const def = _registry.get(name)\n if (!def) {\n warn(`component \"${name}\" not defined. Call Micra.define('${name}', {...}) first.`)\n return\n }\n mount(el, def)\n })\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Micra.js \u2014 Lightweight reactive framework for small sites and simple SaaS.\n *\n * Public surface \u2014 re-exports only.\n *\n * Features:\n * - JS expressions in directives (data-if=\"count > 0\")\n * - Keyed list diffing (data-each=\"items\" data-key=\"id\")\n * - Auto-mount via data-component (Micra.define + Micra.start)\n * - Props from SSR data-attributes (this.prop('page'))\n * - Built-in fetch helper (this.fetch('/api/...'))\n * - Global event bus (Micra.on / Micra.emit)\n * - DOM refs (data-ref=\"chart\" \u2192 this.refs.chart)\n * - Additive class toggling (data-class=\"active:isActive\")\n * - @event shorthand (@click=\"increment\")\n * - Lifecycle: onCreate, onDestroy\n * - SSR-friendly: Micra.start() is safe to call multiple times\n * - Directive cache: O(1) re-renders after first mount\n *\n * Size target: < 5.5 KB minified+gzipped\n *\n * @module Micra\n */\n\n// \u2500\u2500 Public types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport type {\n StateRecord,\n UnsubFn,\n EventHandler,\n EventPayload,\n EmitArgs,\n MicraEvents,\n FetchOptions,\n ComponentMethods,\n ComponentBuiltins,\n ComponentInstance,\n ComponentDefinition,\n} from './types'\n\n// \u2500\u2500 Errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport { FetchError } from './utils/fetch'\n\n// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport { define, defineComponent, instances, registry, debug } from './core/registry'\nexport { mount } from './core/mount'\nexport { start } from './core/start'\nexport { on, off, emit } from './core/bus'\n", "/**\n * src/utils/fetch.ts \u2014 HTTP fetch helper.\n *\n * Responsibilities:\n * - Auto-attach CSRF token from <meta name=\"csrf-token\">\n * - Serialize POST/PUT/PATCH body as JSON\n * - Serialize GET/HEAD options as query params\n * - Throw a typed FetchError on non-2xx responses\n * - Return parsed JSON or text\n *\n * LLM NOTE: This module is PURE (no DOM side effects beyond reading a meta tag).\n * It wraps the native fetch() API with SaaS-friendly defaults.\n */\n\nimport type { FetchOptions } from '../types'\n\n// \u2500\u2500 CSRF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Read CSRF token from <meta name=\"csrf-token\"> (Rails, Laravel, Django\u2026). */\nfunction getCSRF(): string | null {\n return document.querySelector('meta[name=\"csrf-token\"]')?.getAttribute('content') ?? null\n}\n\n// \u2500\u2500 Typed error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Thrown by `this.fetch()` when the server returns a non-2xx status.\n *\n * @example\n * try {\n * await this.fetch('/api/data')\n * } catch (e) {\n * if (e instanceof FetchError && e.status === 404) { ... }\n * }\n */\nexport class FetchError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly response: Response,\n ) {\n super(message)\n this.name = 'FetchError'\n }\n}\n\n// \u2500\u2500 Fetch helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Fetch wrapper with SaaS defaults.\n *\n * - GET/HEAD: extra `options` keys become URL query params\n * - POST/PUT/PATCH/DELETE: `options.body` is JSON-serialized\n * - Attaches X-CSRF-Token header automatically\n * - Returns parsed JSON if Content-Type is application/json, else text\n *\n * @example\n * // GET with params \u2192 /api/users?page=2&status=active\n * const data = await this.fetch('/api/users', { page: 2, status: 'active' })\n *\n * // POST with JSON body\n * await this.fetch('/api/invite', { method: 'POST', body: { email, role } })\n */\nexport async function micraFetch(url: string, options: FetchOptions = {}): Promise<unknown> {\n const method = ((options.method as string | undefined) ?? 'GET').toUpperCase()\n const headers: Record<string, string> = {\n Accept: 'application/json',\n ...(options.headers as Record<string, string> | undefined),\n }\n\n const csrf = getCSRF()\n if (csrf) headers['X-CSRF-Token'] = csrf\n\n let finalUrl = url\n let body: string | undefined\n\n if (method === 'GET' || method === 'HEAD') {\n const params: Record<string, string> = {}\n for (const [k, v] of Object.entries(options)) {\n if (k !== 'method' && k !== 'headers' && k !== 'signal' && v != null)\n params[k] = String(v)\n }\n if (Object.keys(params).length)\n finalUrl += (url.includes('?') ? '&' : '?') + new URLSearchParams(params)\n } else if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json'\n body = JSON.stringify(options.body)\n }\n\n const res = await fetch(finalUrl, {\n method,\n headers,\n ...(options.signal !== undefined ? { signal: options.signal as AbortSignal } : {}),\n ...(body !== undefined ? { body } : {}),\n })\n\n if (!res.ok)\n throw new FetchError(`[Micra] fetch: ${method} ${url} \u2192 ${res.status}`, res.status, res)\n\n const ct = res.headers.get('content-type') ?? ''\n return ct.includes('application/json') ? res.json() : res.text()\n}\n", "/**\n * src/core/registry.ts \u2014 Component definition registry and instance store.\n *\n * Responsibilities:\n * - Store named component definitions (define / registry)\n * - Store live component instances keyed by root HTMLElement (instances)\n *\n * LLM NOTE: Both maps are module-level singletons (one per page load).\n * They are intentionally mutable from mount.ts and start.ts.\n */\n\nimport type {\n ComponentDefinition,\n ComponentInstance,\n ComponentMethods,\n InternalInstance,\n StateRecord,\n} from '../types'\n\n// Named definition map \u2014 populated by define()\nexport const _registry = new Map<string, ComponentDefinition>()\n\n// Live instance map \u2014 populated by mount(), cleared by instance.destroy()\nexport const _instances = new Map<HTMLElement, InternalInstance>()\n\n// \u2500\u2500 Public accessors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Register a component definition under `name`.\n *\n * Both state shape (`S`) and method set (`M`) are inferred from the literal,\n * so `this.state.X` and `this.someMethod()` are fully typed inside the\n * method bodies and lifecycle hooks.\n *\n * @example\n * define('counter', {\n * state: { count: 0 },\n * inc() { this.state.count++ }, // this.state.count: number \u2713\n * reset() { this.state.count = 0 }, // this.reset() is also typed \u2713\n * onCreate() { this.inc() }, // \u2713\n * })\n */\nexport function define<S extends StateRecord, M>(\n name: string,\n definition: ComponentDefinition<S, M>,\n): void {\n _registry.set(name, definition as unknown as ComponentDefinition)\n}\n\n/**\n * Type-helper \u2014 returns `definition` unchanged but lets TypeScript infer `S`\n * and `M` from the literal so all methods are typed with the correct `this`.\n *\n * Use this when defining a component outside a `define()` call.\n *\n * @example\n * const counter = defineComponent({\n * state: { count: 0 },\n * increment() { this.state.count++ }, // this.state: { count: number } \u2713\n * })\n * Micra.define('counter', counter)\n */\nexport function defineComponent<S extends StateRecord, M>(\n definition: ComponentDefinition<S, M>,\n): ComponentDefinition<S, M> {\n return definition\n}\n\n/**\n * Returns a read-only view of all live instances (keyed by root element).\n * Useful for DevTools / debugging.\n */\nexport function instances(): ReadonlyMap<HTMLElement, ComponentInstance> {\n return _instances as unknown as ReadonlyMap<HTMLElement, ComponentInstance>\n}\n\n/**\n * Returns a read-only view of all registered component definitions.\n * Useful for DevTools / debugging.\n */\nexport function registry(): ReadonlyMap<string, ComponentDefinition> {\n return _registry\n}\n\n/**\n * Print all live component instances to the browser console.\n * Shows component name, root element, and current state for each instance.\n *\n * @example\n * // In browser DevTools console:\n * Micra.debug()\n * // [Micra] 3 live component(s)\n * // counter $el: <div> state: { count: 5 }\n * // user-list $el: <div> state: { users: [...], loading: false }\n */\nexport function debug(): void {\n if (_instances.size === 0) {\n console.log('[Micra] No live components.')\n return\n }\n console.group(`[Micra] ${_instances.size} live component(s)`)\n for (const [el, instance] of _instances) {\n const name = el.getAttribute('data-component') ?? '(unnamed)'\n console.group(`%c${name}`, 'font-weight:bold;color:#6366f1')\n console.log('$el ', el)\n console.log('state', { ...instance.state })\n console.groupEnd()\n }\n console.groupEnd()\n}\n", "/**\n * src/utils/expr.ts \u2014 JS expression evaluator.\n *\n * Responsibilities:\n * - Compile expression strings into cached functions\n * - Evaluate them against a state object\n * - Fast-path for simple property lookups\n * - Shadow non-state identifiers so directive expressions cannot reach\n * globals like `window`, `fetch`, `constructor`, etc. A small whitelist\n * of utility globals (Math, JSON, Date, ...) remains accessible.\n *\n * LLM NOTE: This module is PURE. It does not touch the DOM or mutate state.\n *\n * Security model:\n * Directive expressions are JavaScript \u2014 they are compiled via `new Function`\n * and run with full JS capability except that bare identifiers must resolve\n * to either a state key, a component instance method, or one of\n * ALLOWED_GLOBALS. This blocks the `constructor.constructor(\"...\")()` chain\n * and accidental access to `window` / `document` / `fetch`. It does NOT\n * sandbox method calls \u2014 if a component method itself touches `window`,\n * that still works. Treat directive templates as trusted code regardless.\n */\n\nimport type { StateRecord } from '../types'\n\n// \u2500\u2500 Expression cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Compiled functions are keyed by expression string \u2014 Function() is only called\n// once per unique expression across the entire app lifetime.\n\n// LLM NOTE: exprCache is module-level (shared across all components).\n// This is intentional \u2014 most apps reuse the same expressions.\n\n// Compiled fn for complex expressions; pre-split parts for simple dot-paths.\n// Storing parts once avoids the SIMPLE_PATH regex test + split on every evalExpr call.\ntype CompiledFn = (state: object, safe: object) => unknown\ntype CachedEntry =\n | { kind: 'fn'; fn: CompiledFn }\n | { kind: 'path'; parts: string[] }\n\nconst exprCache = new Map<string, CachedEntry>()\n// Expressions whose runtime error we have already warned about. Prevents log spam\n// when the same `data-text=\"item.naame\"` typo fires every render.\nconst warnedRuntime = new Set<string>()\n\n// Simple identifier or dot-path: \"count\", \"user.name\", \"item.email\"\n// Matches: letter/$/_ followed by word chars, optionally with .property chains\nconst SIMPLE_PATH = /^[a-zA-Z_$][a-zA-Z0-9_$]*(\\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/\n\n// \u2500\u2500 Safe scope \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Globals reachable from directive expressions. Anything else (window, fetch,\n * constructor, eval, ...) is shadowed by SAFE_OUTER and resolves to undefined.\n */\nconst ALLOWED_GLOBALS = new Set<string>([\n 'Math', 'JSON', 'Date', 'String', 'Number', 'Boolean', 'Array', 'Object',\n 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'NaN', 'Infinity', 'undefined',\n])\n\n/**\n * Outer `with()` scope. Its `has` trap claims every non-whitelisted identifier\n * is \"in scope\" so the JS engine resolves the read on this Proxy (which returns\n * undefined) instead of walking up to the global object. Whitelisted names fall\n * through to globalThis.\n */\n// Sentinel parameter names used by the compiled function. SAFE_OUTER must NOT\n// shadow them, or `with($s)` would resolve to `undefined` via SAFE_OUTER.\nconst PARAM_S = '$s'\nconst PARAM_SAFE = '$safe'\n\nconst SAFE_OUTER: object = new Proxy(Object.create(null) as object, {\n has(_target, key): boolean {\n if (typeof key !== 'string') return false\n if (key === PARAM_S || key === PARAM_SAFE) return false\n return !ALLOWED_GLOBALS.has(key)\n },\n get(): undefined {\n return undefined\n },\n})\n\n/**\n * @internal Per-state safe wrappers \u2014 one per source state object. WeakMap so\n * short-lived itemStates get GC'd with their wrappers.\n */\nconst safeWrapCache = new WeakMap<object, object>()\n\n/**\n * @internal Pre-computed names that live on `Object.prototype`\n * (constructor, toString, hasOwnProperty, ...). Used by safeStateHas to detect\n * built-in keys without re-walking the chain on every call.\n */\nconst OBJ_PROTO_KEYS = new Set<string>(Object.getOwnPropertyNames(Object.prototype))\n\n/**\n * Wrap a state object so its `has` trap reports only \"real\" keys \u2014 own\n * properties or keys reachable up to (but not including) `Object.prototype`.\n * This blocks `'constructor' in state` from leaking the prototype.\n */\nfunction safeStateWrap(state: object): object {\n const cached = safeWrapCache.get(state)\n if (cached) return cached\n const wrapped = new Proxy(state, {\n has(target, key) {\n return safeStateHas(target, key)\n },\n get(target, key) {\n return Reflect.get(target, key)\n },\n })\n safeWrapCache.set(state, wrapped)\n return wrapped\n}\n\n/**\n * Return true iff `key` is reachable on `state` without walking into\n * `Object.prototype`. Works for plain objects, prototype-chained objects, and\n * Proxies with their own `has` trap.\n */\nfunction safeStateHas(state: object, key: PropertyKey): boolean {\n if (typeof key !== 'string') return false\n if (!Reflect.has(state, key)) return false\n // Identifiers that are NOT on Object.prototype are always safe \u2014 accept them\n // immediately without walking the chain.\n if (!OBJ_PROTO_KEYS.has(key)) return true\n // Built-in Object.prototype names (constructor, toString, hasOwnProperty, ...)\n // are only accepted when they have been explicitly placed on the state chain.\n let obj: object | null = state\n while (obj && obj !== Object.prototype) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) return true\n obj = Object.getPrototypeOf(obj) as object | null\n }\n return false\n}\n\n// \u2500\u2500 evalExpr \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Evaluate a JS expression string against a state object.\n *\n * Results are cached by expression string \u2014 repeated evaluations hit the cache.\n * Uses a fast-path for simple dot-paths (e.g. \"count\", \"user.name\") that avoids\n * Function() overhead.\n *\n * @example\n * evalExpr('count > 0', { count: 5 }) // \u2192 true\n * evalExpr('user.name', { user: { name: 'Alice' } }) // \u2192 'Alice'\n * evalExpr('price * qty', { price: 9.99, qty: 3 }) // \u2192 29.97\n */\nexport function evalExpr(expr: string, state: StateRecord): unknown {\n let cached = exprCache.get(expr)\n\n if (!cached) {\n // Determine once whether this is a simple dot-path and cache the result.\n if (SIMPLE_PATH.test(expr)) {\n cached = { kind: 'path', parts: expr.split('.') }\n } else {\n try {\n cached = {\n kind: 'fn',\n fn: new Function('$s', '$safe', `with($safe){with($s){return (${expr})}}`) as CompiledFn,\n }\n } catch {\n warn(`invalid expression \"${expr}\"`)\n cached = { kind: 'fn', fn: () => undefined }\n }\n }\n exprCache.set(expr, cached)\n }\n\n // Fast-path: simple property access \u2014 no Function() needed.\n // Still guarded so bare access to Object.prototype names returns undefined.\n if (cached.kind === 'path') {\n if (!safeStateHas(state, cached.parts[0]!)) return undefined\n return cached.parts.reduce<unknown>(\n (obj, key) => (obj != null ? (obj as StateRecord)[key] : undefined),\n state,\n )\n }\n\n try {\n return cached.fn(safeStateWrap(state), SAFE_OUTER)\n } catch (e) {\n if (!warnedRuntime.has(expr)) {\n warnedRuntime.add(expr)\n warn(`runtime error in \"${expr}\": ${(e as Error).message}`)\n }\n return undefined\n }\n}\n\n// \u2500\u2500 Dev warnings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** @internal Consistent warning prefix. */\nexport function warn(msg: string): void {\n console.warn(`[Micra] ${msg}`)\n}\n", "/**\n * src/core/bus.ts \u2014 Global event bus.\n *\n * Responsibilities:\n * - Publish events (emit)\n * - Subscribe and unsubscribe (on / off)\n * - Provide unsubscribe tokens for component cleanup\n *\n * LLM NOTE: The bus is a module-level singleton.\n * Component instances subscribe via `instance.on()` which auto-registers\n * the unsub token in `instance.__micraSubs` for cleanup on destroy().\n */\n\nimport type { EmitArgs, EventHandler, EventPayload, UnsubFn } from '../types'\n\n// Module-level bus state \u2014 one bus per page load.\nconst _bus = new Map<string, Set<EventHandler>>()\n\n/**\n * Subscribe to a named event. Returns an unsubscribe function.\n * Payload is typed via the `MicraEvents` interface (augmentable).\n *\n * @example\n * const unsub = on('user:login', (user) => console.log(user))\n * unsub() // stop listening\n */\nexport function on<K extends string>(\n event: K,\n handler: (payload: EventPayload<K>) => void,\n): UnsubFn {\n if (!_bus.has(event)) _bus.set(event, new Set())\n _bus.get(event)!.add(handler as EventHandler)\n return () => off(event, handler)\n}\n\n/**\n * Unsubscribe a specific handler from an event.\n */\nexport function off<K extends string>(\n event: K,\n handler: (payload: EventPayload<K>) => void,\n): void {\n const set = _bus.get(event)\n if (!set) return\n set.delete(handler as EventHandler)\n if (set.size === 0) _bus.delete(event)\n}\n\n/**\n * Publish an event to all subscribers. Errors are caught per-handler.\n * Payload is typed via the `MicraEvents` interface (augmentable).\n *\n * @example\n * emit('user:updated', { id: 1, name: 'Alice' })\n */\nexport function emit<K extends string>(event: K, ...args: EmitArgs<K>): void {\n const payload = args[0]\n _bus.get(event)?.forEach(h => {\n try { h(payload) } catch (e) { console.error(`[Micra] bus error [${event}]:`, e) }\n })\n}\n", "/**\n * src/core/reactive.ts \u2014 Reactive state proxy and batch scheduler.\n *\n * Responsibilities:\n * - Wrap a plain state object in a Proxy that notifies on writes\n * - Batch multiple synchronous mutations into a single microtask render\n *\n * LLM NOTE: Both functions are PURE constructors \u2014 they have no side effects\n * beyond setting up a Proxy / Promise chain. No DOM access here.\n */\n\nimport type { StateRecord } from '../types'\n\n/**\n * Wrap `obj` in a shallow Proxy. Any property write calls `schedule()`.\n * Arrays: replace, don't mutate \u2014 `state.items = [...state.items, x]`.\n *\n * @example\n * const raw = { count: 0 }\n * const state = createReactiveState(raw, render)\n * state.count = 5 // triggers render() in next microtask\n */\nexport function createReactiveState<S extends StateRecord>(obj: S, schedule: () => void, onKey?: (key: string) => void): S {\n return new Proxy(obj, {\n set(target, key: string, value: unknown) {\n // Cast through StateRecord \u2014 TypeScript cannot write through a generic index\n ;(target as StateRecord)[key] = value\n onKey?.(key)\n schedule()\n return true\n },\n })\n}\n\n/**\n * Return a debounce function that defers `render` to the next microtask.\n * Multiple calls within the same tick collapse to a single render.\n *\n * Uses `queueMicrotask` so each batch enqueues a single microtask instead of\n * allocating a Promise + reaction job. `flush` is hoisted out of the hot path\n * so it isn't re-created on every schedule() call.\n *\n * @example\n * const schedule = createScheduler(render)\n * schedule() // defers render\n * schedule() // no-op \u2014 already pending\n */\nexport function createScheduler(render: () => void): () => void {\n let pending = false\n const flush = () => { pending = false; render() }\n return function schedule() {\n if (pending) return\n pending = true\n queueMicrotask(flush)\n }\n}\n", "/**\n * src/dom/directives.ts \u2014 Apply DOM directives to a component subtree.\n *\n * Responsibilities:\n * - data-text, data-html, data-if, data-show, data-bind, data-model\n * - data-class (additive class toggling)\n *\n * LLM NOTE: applyDirectives() is called on every render. It consumes a\n * pre-computed ScanIndex (built once by scan.ts and cached on the element).\n * The scan replaced 10+ querySelectorAll calls with a single TreeWalker pass.\n *\n * Important: this module does NOT handle data-each \u2014 see dom/each.ts.\n */\n\nimport type {\n CachedIfBinding,\n ScanIndex,\n StateRecord,\n} from '../types'\nimport { evalExpr, warn } from '../utils/expr'\n\n// \u2500\u2500 Directive appliers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Each function is PURE relative to state \u2014 reads state, writes DOM.\n\nfunction applyText(el: Element, expr: string, state: StateRecord): void {\n const text = String(evalExpr(expr, state) ?? '')\n if (el.textContent !== text) el.textContent = text\n}\n\n/**\n * data-html \u2014 writes the expression value as innerHTML.\n *\n * \u26A0\uFE0F XSS WARNING: the value is rendered as raw HTML. Never bind untrusted\n * input here \u2014 use `data-text` (textContent) instead. See docs/directives.md\n * for the full security model.\n */\nfunction applyHtml(el: Element, expr: string, state: StateRecord): void {\n const html = String(evalExpr(expr, state) ?? '')\n if (el.innerHTML !== html) el.innerHTML = html\n}\n\n/**\n * data-if \u2014 true mount/unmount. When the expression is falsy, the element is\n * detached from the DOM and a Comment placeholder takes its slot. When truthy,\n * the element is re-inserted where the placeholder is.\n *\n * Side effect: when an element is detached, its `data-ref` is gone from\n * `this.refs` and its `data-model` listener still exists on the (detached)\n * node \u2014 listeners survive detach.\n *\n * Use `data-show` when you want the cheap display:none toggle instead.\n */\nfunction applyIf(binding: CachedIfBinding, state: StateRecord): void {\n const el = binding.el as HTMLElement\n const truthy = !!evalExpr(binding.expr, state)\n if (truthy) {\n // If a placeholder is currently in the DOM in the element's slot, swap back.\n const ph = binding.placeholder\n if (ph && ph.parentNode) ph.parentNode.replaceChild(el, ph)\n } else {\n // Only detach if currently attached somewhere. Standalone elements\n // (no parent \u2014 common in unit tests) are a no-op.\n const parent = el.parentNode\n if (parent) {\n if (!binding.placeholder) binding.placeholder = document.createComment('if')\n parent.replaceChild(binding.placeholder, el)\n }\n }\n}\n\n/**\n * data-show \u2014 visibility toggle via `style.display`. Element stays in the DOM.\n */\nfunction applyShow(el: Element, expr: string, state: StateRecord): void {\n const desired = evalExpr(expr, state) ? '' : 'none'\n const htmlEl = el as HTMLElement\n if (htmlEl.style.display !== desired) htmlEl.style.display = desired\n}\n\nfunction applyBind(\n el: Element,\n pairs: ReadonlyArray<readonly [string, string]>,\n state: StateRecord,\n): void {\n for (const [attr, valExpr] of pairs) {\n const val = evalExpr(valExpr, state)\n\n if (attr === 'class') {\n (el as HTMLElement).className = String(val ?? '')\n } else if (attr === 'value') {\n if (document.activeElement !== el)\n (el as HTMLInputElement).value = String(val ?? '')\n } else if (attr === 'style') {\n if (typeof val === 'object' && val !== null) {\n Object.assign((el as HTMLElement).style, val)\n } else {\n el.setAttribute('style', String(val ?? ''))\n }\n } else if (typeof val === 'boolean') {\n val ? el.setAttribute(attr, '') : el.removeAttribute(attr)\n } else {\n val == null ? el.removeAttribute(attr) : el.setAttribute(attr, String(val))\n }\n }\n}\n\n/**\n * data-class=\"active:isActive, disabled:count === 0\"\n * Toggles classes additively (does NOT replace full className like data-bind:class).\n * Pairs are pre-parsed at scan time.\n */\nfunction applyClass(\n el: Element,\n pairs: ReadonlyArray<readonly [string, string]>,\n state: StateRecord,\n): void {\n for (const [cls, valExpr] of pairs) {\n el.classList.toggle(cls, Boolean(evalExpr(valExpr, state)))\n }\n}\n\nfunction applyModel(\n el: Element,\n key: string,\n rawState: StateRecord,\n): void {\n const html = el as HTMLInputElement\n const stateVal = rawState[key]\n const desired = stateVal == null ? '' : String(stateVal)\n // Only write when out of sync. This is a no-op during live typing (the input\n // event already drove state to match el.value) but still propagates\n // programmatic resets such as `this.state.q = ''` on focused inputs.\n if (html.value !== desired) html.value = desired\n // listener is attached separately in events.ts \u2014 this only syncs the value\n}\n\n// \u2500\u2500 Main entry point \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Apply all non-each directives to a component subtree.\n *\n * Consumes a pre-computed ScanIndex. data-if runs first so subsequent\n * directives don't write into a tree that's about to be detached this tick.\n *\n * @param scan - Pre-computed scan from scan.ts (cached per element)\n * @param state - Expression state (may include item/index for each rows)\n * @param rawState - Raw (non-proxy) state for model sync\n */\nexport function applyDirectives(\n scan: ScanIndex,\n state: StateRecord,\n rawState: StateRecord,\n): void {\n // data-if runs first so subsequent directives don't write into a tree that's\n // about to be detached this tick.\n for (const b of scan.if) applyIf(b, state)\n for (const b of scan.text) applyText(b.el, b.expr, state)\n for (const b of scan.html) applyHtml(b.el, b.expr, state)\n for (const b of scan.show) applyShow(b.el, b.expr, state)\n for (const b of scan.bind) applyBind(b.el, b.pairs, state)\n for (const b of scan.model) applyModel(b.el, b.expr.trim(), rawState)\n for (const b of scan.class) applyClass(b.el, b.pairs, state)\n}\n\n// \u2500\u2500 Dev warning helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Validate directive usage and emit dev warnings.\n * Called once after the initial render of a component, with the already-built\n * scan so we don't walk the DOM again.\n *\n * @internal\n */\nexport function validateDirectives(scan: ScanIndex): void {\n for (const el of scan.each) {\n const tmpl = el as HTMLTemplateElement & { __micraNoKeyWarned?: true }\n if (!el.hasAttribute('data-key') && !tmpl.__micraNoKeyWarned) {\n tmpl.__micraNoKeyWarned = true\n warn(\n `data-each=\"${el.getAttribute('data-each')}\" has no data-key \u2014 ` +\n `keyed diff disabled. Add data-key=\"id\" for better performance.`,\n )\n }\n }\n\n // data-bind=\"class:...\" replaces className wholesale, which fights with\n // data-class on the same element. Warn so the developer picks one.\n for (const b of scan.bind) {\n const hasClassBind = b.pairs.some(p => p[0] === 'class')\n if (hasClassBind && b.el.hasAttribute('data-class')) {\n warn(\n `element has both data-bind=\"class:...\" and data-class \u2014 they fight ` +\n `on every render. Use one.`,\n )\n }\n }\n}\n\n// Re-export warn for use in other modules\nexport { warn }\n", "/**\n * src/dom/events.ts \u2014 DOM event binding.\n *\n * Responsibilities:\n * - Bind `data-on=\"event:method\"` listeners (once per element)\n * - Bind `@event=\"method\"` shorthand (once per element)\n * - Bind `data-model` two-way input listeners (once per element)\n *\n * LLM NOTE: Every listener attached here is also recorded in\n * instance.__micraListeners so destroy() can remove it cleanly.\n * Re-render skips already-bound elements via per-element __micra* flags.\n *\n * All three binders accept pre-computed element lists from scan.ts \u2014\n * no DOM queries here.\n */\n\nimport type {\n CachedBinding,\n InternalInstance,\n MicraElement,\n StateRecord,\n} from '../types'\nimport { warn } from '../utils/expr'\n\n/** @internal Attach a DOM listener and track it on the instance for destroy(). */\nfunction track<S extends StateRecord>(\n instance: InternalInstance<S>,\n el: Element,\n type: string,\n fn: EventListener,\n): void {\n el.addEventListener(type, fn)\n ;(instance.__micraListeners ??= []).push({ el, type, fn })\n}\n\n// \u2500\u2500 data-on \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Bind `data-on=\"event:method[,event2:method2]\"` listeners.\n * Listeners are bound once \u2014 re-render calls are no-ops for already-bound elements.\n *\n * Supports modifiers: `click.prevent`, `click.stop`, `click.self`.\n *\n * @param els - Pre-computed list of [data-on] elements from scan.ts\n *\n * @example\n * <button data-on=\"click:save\">Save</button>\n * <form data-on=\"submit.prevent:handleSubmit\">\n */\nexport function bindDataOn<S extends StateRecord>(\n els: Element[],\n instance: InternalInstance<S>,\n): void {\n for (const el of els) {\n const mEl = el as MicraElement\n if (mEl.__micraEvents) continue\n mEl.__micraEvents = true\n\n const spec = mEl.dataset['on'] ?? ''\n for (const part of spec.split(',')) {\n const [evSpec, method] = part.trim().split(':') as [string, string]\n if (!evSpec || !method) continue\n\n const [evName, ...mods] = evSpec.split('.')\n\n track(instance, el, evName!, (e: Event) => {\n if (mods.includes('prevent')) e.preventDefault()\n if (mods.includes('stop')) e.stopPropagation()\n if (mods.includes('self') && e.target !== el) return\n\n const fn = instance[method.trim()]\n if (typeof fn === 'function') (fn as (e: Event) => void).call(instance, e)\n else warn(`method \"${method.trim()}\" not found`)\n })\n }\n }\n}\n\n// \u2500\u2500 @event shorthand \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Bind `@event=\"method\"` shorthand attributes (Stimulus-style).\n * Bound once per element via `__micraAtBound` \u2014 re-renders are no-ops.\n *\n * @param els - Pre-computed list of elements with at least one @-prefixed attr\n * (from scan.ts \u2014 replaces the old `querySelectorAll('*')` walk)\n *\n * @example\n * <button @click=\"increment\">+</button>\n * <form @submit.prevent=\"handleSubmit\">\n */\nexport function bindAtEvents<S extends StateRecord>(\n els: Element[],\n instance: InternalInstance<S>,\n): void {\n for (const el of els) {\n const mEl = el as MicraElement\n if (mEl.__micraAtBound) continue\n\n let bound = false\n for (const attr of Array.from(el.attributes)) {\n if (!attr.name.startsWith('@')) continue\n const [evSpec, ...rest] = attr.name.slice(1).split('.')\n const method = attr.value.trim()\n\n track(instance, el, evSpec!, (e: Event) => {\n if (rest.includes('prevent')) e.preventDefault()\n if (rest.includes('stop')) e.stopPropagation()\n if (rest.includes('self') && e.target !== el) return\n\n const fn = instance[method]\n if (typeof fn === 'function') (fn as (e: Event) => void).call(instance, e)\n else warn(`method \"${method}\" not found`)\n })\n bound = true\n }\n if (bound) mEl.__micraAtBound = true\n }\n}\n\n// \u2500\u2500 data-model \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Two-way binding: `data-model=\"key\"` wires <input>/<select>/<textarea>\n * to `state[key]`. Binding is attached once per element.\n *\n * Numeric inputs (`type=\"number\"` / `type=\"range\"`) write numbers, not strings.\n * Checkbox inputs write booleans. Everything else writes strings.\n *\n * @param bindings - Pre-computed model bindings from scan.ts\n * (each carries { el, expr } where expr is the state key)\n *\n * @example\n * <input data-model=\"search\"> // updates state.search on every keystroke\n * <select data-model=\"sortBy\"> // updates state.sortBy on change\n */\nexport function bindModels<S extends StateRecord>(\n bindings: CachedBinding[],\n instance: InternalInstance<S>,\n): void {\n for (const { el, expr } of bindings) {\n const mEl = el as MicraElement\n if (mEl.__micraModel) continue\n mEl.__micraModel = true\n\n const key = expr.trim()\n const tag = el.tagName\n const inputEl = el as HTMLInputElement\n const inputType = inputEl.type\n\n const update = () => {\n let val: unknown\n if (tag === 'INPUT' && inputType === 'checkbox') {\n val = inputEl.checked\n } else if (tag === 'INPUT' && (inputType === 'number' || inputType === 'range')) {\n // Empty string \u2192 NaN; preserve raw empty as null so state stays \"unfilled\"\n val = inputEl.value === '' ? null : inputEl.valueAsNumber\n } else {\n val = inputEl.value\n }\n ;(instance.state as StateRecord)[key] = val\n }\n\n const evType = tag === 'SELECT' || inputType === 'radio' ? 'change' : 'input'\n track(instance, el, evType, update)\n }\n}\n", "/**\n * src/dom/scan.ts \u2014 Single-pass directive/event/ref scanner.\n *\n * Replaces 10+ querySelectorAll calls per render with ONE TreeWalker\n * traversal that classifies every directive attribute in a single visit.\n *\n * Boundaries:\n * - REJECT (skip subtree) on nested [data-component] \u2014 a parent component\n * never processes directives owned by a nested child. Applied during the\n * walk so we don't even *visit* those nodes.\n * - <template> contents are not visited (browser TreeWalker default).\n * `<template data-each>` itself IS visited and classified into scan.each;\n * its children are processed by each.ts on every render \u2014 fresh rows\n * are wrapped in a per-row element and scanned via scanComponent.\n *\n * Hot-path notes:\n * - We read `el.attributes` once and switch by suffix. No allocations per\n * non-matching attr.\n * - Pair-parsing (`data-bind`, `data-class`) happens here, once, at scan\n * time. Reused on every render.\n */\n\nimport type { CachedIfBinding, CachedPairBinding, ScanIndex } from \"../types\";\n\nfunction emptyScan(): ScanIndex {\n return {\n text: [],\n html: [],\n if: [],\n show: [],\n bind: [],\n model: [],\n class: [],\n each: [],\n on: [],\n atEvents: [],\n refs: [],\n };\n}\n\n/** @internal Parse `name:expr, name2:expr2` once at scan time. */\nfunction parsePairs(expr: string): Array<readonly [string, string]> {\n const out: Array<readonly [string, string]> = [];\n for (const part of expr.split(\",\")) {\n const colon = part.indexOf(\":\");\n if (colon === -1) continue;\n const left = part.slice(0, colon).trim();\n const right = part.slice(colon + 1).trim();\n if (!left) continue;\n out.push([left, right]);\n }\n return out;\n}\n\n/** @internal Classify every relevant attribute on one element. */\nfunction classify(el: Element, scan: ScanIndex): void {\n // <template data-each> is the only directive we permit on a <template>.\n // Other directives on a <template> would be meaningless (the content lives\n // in template.content, not template.children).\n if (el.tagName === \"TEMPLATE\") {\n if (el.hasAttribute(\"data-each\")) scan.each.push(el);\n return;\n }\n\n const attrs = el.attributes;\n let atEventSeen = false;\n\n for (let i = 0; i < attrs.length; i++) {\n const a = attrs[i]!;\n const name = a.name;\n\n // Fast path: most attribute names aren't ours. First-char check rejects\n // the common case (id, class, style, href, \u2026) without a string compare.\n const first = name.charCodeAt(0);\n\n if (first === 64 /* '@' */) {\n // @event=\"method\" or @event.modifier=\"method\"\n if (!atEventSeen) {\n scan.atEvents.push(el);\n atEventSeen = true;\n }\n continue;\n }\n\n // data-X attributes\n if (\n first === 100 /* d */ &&\n name.length >= 6 &&\n name.charCodeAt(4) === 45 /* '-' */\n ) {\n // 'data-' prefix\n const rest = name.slice(5);\n switch (rest) {\n case \"text\":\n scan.text.push({ el, expr: a.value });\n break;\n case \"html\":\n scan.html.push({ el, expr: a.value });\n break;\n case \"if\":\n scan.if.push({ el, expr: a.value } as CachedIfBinding);\n break;\n case \"show\":\n scan.show.push({ el, expr: a.value });\n break;\n case \"bind\": {\n const pairs = parsePairs(a.value);\n scan.bind.push({ el, expr: a.value, pairs } as CachedPairBinding);\n break;\n }\n case \"model\":\n scan.model.push({ el, expr: a.value });\n break;\n case \"class\": {\n const pairs = parsePairs(a.value);\n scan.class.push({ el, expr: a.value, pairs } as CachedPairBinding);\n break;\n }\n case \"on\":\n scan.on.push(el);\n break;\n case \"ref\":\n scan.refs.push(el);\n break;\n // data-key, data-each, data-component, data-* user attrs \u2014 ignored here\n }\n }\n }\n}\n\n/** @internal Shared filter: stop descending into nested components. */\nconst NESTED_COMPONENT_FILTER: NodeFilter = {\n acceptNode(node: Node): number {\n if ((node as Element).hasAttribute(\"data-component\"))\n return NodeFilter.FILTER_REJECT;\n return NodeFilter.FILTER_ACCEPT;\n },\n};\n\n/**\n * Scan an Element subtree owned by one component. Skips nested\n * [data-component] subtrees entirely. Visits the root itself.\n *\n * Cached on `el.__micraScan` after the first call \u2014 subsequent renders\n * are free.\n */\nexport function scanComponent(root: Element): ScanIndex {\n const scan = emptyScan();\n\n // Always classify root itself first \u2014 the TreeWalker's filter would\n // REJECT it if it had `data-component` (which it normally does for a\n // mounted component). The filter is for *descendants*.\n classify(root, scan);\n\n const walker = document.createTreeWalker(\n root,\n NodeFilter.SHOW_ELEMENT,\n NESTED_COMPONENT_FILTER,\n );\n\n let node: Element | null = walker.nextNode() as Element | null;\n while (node) {\n classify(node, scan);\n node = walker.nextNode() as Element | null;\n }\n\n return scan;\n}\n\n", "/**\n * src/dom/each.ts \u2014 Keyed and non-keyed list rendering (data-each).\n *\n * Responsibilities:\n * - Process `<template data-each=\"items\" data-key=\"id\">` elements\n * - Keyed diff: reuse/reorder DOM nodes by key \u2014 O(n) with a Map\n * - Non-keyed fallback: length-based positional reuse \u2014 min(old, new) rows\n * are kept as-is, the tail is removed or new rows are appended\n * - Apply directives to each row with a scoped itemState\n *\n * LLM NOTE: renderList() is called on every render cycle AFTER applyDirectives().\n * The template list comes pre-scanned from scan.ts \u2014 no DOM queries here.\n * Each row node gets its own ScanIndex cached on `node.__micraScan` so\n * re-renders of that row don't re-walk the DOM.\n * Keyed mode (data-key present) mutates the DOM in-place \u2014 nodes are\n * created once and reused. Non-keyed mode also reuses existing nodes\n * positionally: only the length delta is touched, the rest gets a fresh\n * itemState and re-applies directives.\n */\n\nimport type {\n InternalInstance,\n MicraElement,\n MicraTemplate,\n StateRecord,\n} from '../types'\nimport { evalExpr, warn } from '../utils/expr'\nimport { applyDirectives } from './directives'\nimport { bindDataOn, bindAtEvents, bindModels } from './events'\nimport { scanComponent } from './scan'\n\n/**\n * Process all `<template data-each>` elements found by the scanner.\n * Scoped itemState makes `item`, `index`, `$index` available in row expressions.\n *\n * @param templates - Pre-scanned list of <template data-each> elements\n * @param state - Expression state (proxy merging rawState + instance)\n * @param rawState - Raw (non-proxy) state \u2014 used for model binding\n * @param instance - Component instance (for event binding)\n * @param triggerKey - Which state key triggered this render (null = initial, 'MULTIPLE' = batch)\n */\nexport function renderList<S extends StateRecord>(\n templates: Element[],\n state: StateRecord,\n rawState: StateRecord,\n instance: InternalInstance<S>,\n triggerKey: string | null | 'MULTIPLE',\n): void {\n for (const tmplEl of templates) {\n if (tmplEl.tagName !== 'TEMPLATE') continue\n const tmpl = tmplEl as MicraTemplate\n\n const itemsExpr = tmpl.getAttribute('data-each')!\n const keyAttr = tmpl.getAttribute('data-key') ?? null\n const items = evalExpr(itemsExpr, state)\n\n // Ensure marker comment + internal state are initialized\n if (!tmpl.__micraMarker) {\n const m = document.createComment(`each:${itemsExpr}`)\n tmpl.after(m)\n tmpl.__micraMarker = m\n tmpl.__micraNodes = new Map()\n tmpl.__micraList = []\n }\n\n const marker = tmpl.__micraMarker\n const keyMap = tmpl.__micraNodes\n // The template (and its marker) is currently detached \u2014 likely a data-if\n // ancestor unmounted this subtree. Nothing to do until it returns.\n if (!marker.parentNode) continue\n\n // Empty / non-array: clear all rendered rows\n if (!Array.isArray(items)) {\n tmpl.__micraList.forEach(n => n.remove())\n tmpl.__micraList = []\n keyMap.clear()\n continue\n }\n\n // canSkipUnchanged: true when only this list's state key changed \u2014 rows\n // whose item reference and index are both unchanged can skip applyDirectives.\n const canSkipUnchanged = triggerKey !== null &&\n triggerKey !== 'MULTIPLE' &&\n triggerKey === itemsExpr\n\n if (keyAttr) {\n renderKeyed(tmpl, items as StateRecord[], keyAttr, marker, keyMap, state, rawState, instance, canSkipUnchanged)\n } else {\n renderNoKey(tmpl, items as StateRecord[], marker, state, rawState, instance, canSkipUnchanged)\n }\n }\n}\n\n// \u2500\u2500 Row node creation (shared by both paths) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Clone the template into a fresh row node, wrapping multi-root content in\n * `<micra-each-item style=\"display:contents\">` so the row always corresponds\n * to a single, stable DOM element. Scans, binds listeners once, and caches\n * an empty itemState prototyped from `state` (filled in by the caller).\n */\nfunction createRowNode<S extends StateRecord>(\n tmpl: MicraTemplate,\n state: StateRecord,\n instance: InternalInstance<S>,\n): MicraElement {\n const frag = tmpl.content.cloneNode(true) as DocumentFragment\n let node: MicraElement\n if (frag.childNodes.length === 1) {\n node = frag.firstElementChild as MicraElement\n } else {\n node = document.createElement('micra-each-item') as MicraElement\n node.style.display = 'contents'\n node.append(frag)\n }\n const rowScan = scanComponent(node)\n node.__micraScan = rowScan\n node._itemState = Object.create(state) as StateRecord\n bindDataOn(rowScan.on, instance)\n bindAtEvents(rowScan.atEvents, instance)\n bindModels(rowScan.model, instance)\n return node\n}\n\n// \u2500\u2500 Keyed diff \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction renderKeyed<S extends StateRecord>(\n tmpl: MicraTemplate,\n items: StateRecord[],\n keyAttr: string,\n marker: Comment,\n keyMap: Map<unknown, MicraElement>,\n state: StateRecord,\n rawState: StateRecord,\n instance: InternalInstance<S>,\n canSkipUnchanged: boolean,\n): void {\n const nextKeys = new Set<unknown>()\n const nextNodes: MicraElement[] = []\n let warnedNullKey = false\n let warnedDupKey = false\n\n for (const [index, item] of items.entries()) {\n const key = item[keyAttr]\n if (key == null && !warnedNullKey) {\n warn(`data-key=\"${keyAttr}\" is null/undefined on item at index ${index}`)\n warnedNullKey = true\n }\n if (nextKeys.has(key) && !warnedDupKey) {\n warn(`data-key=\"${keyAttr}\" has duplicate value ${JSON.stringify(key)} \u2014 rows will collide`)\n warnedDupKey = true\n }\n nextKeys.add(key)\n\n let node = keyMap.get(key) as MicraElement | undefined\n\n if (!node) {\n node = createRowNode(tmpl, state, instance)\n keyMap.set(key, node)\n } else if (canSkipUnchanged && node.__micraItem === item && node.__micraIndex === index) {\n // Item reference and index are unchanged, and no other state key changed\n // this cycle \u2014 the DOM already reflects the latest values. Skip re-render.\n nextNodes.push(node)\n continue\n }\n\n node.__micraItem = item\n node.__micraIndex = index\n\n // Reuse the cached itemState, just update the per-row values.\n const itemState = node._itemState!\n itemState.item = item\n itemState.index = index\n itemState.$index = index\n\n // Use the cached scan if present (created above on first sight of this key);\n // older paths may pass a node we haven't scanned yet.\n const rowScan = node.__micraScan ?? (node.__micraScan = scanComponent(node))\n applyDirectives(rowScan, itemState, rawState)\n nextNodes.push(node)\n }\n\n // Remove stale nodes\n for (const [key, node] of keyMap) {\n if (!nextKeys.has(key)) { node.remove(); keyMap.delete(key) }\n }\n\n const prevList = tmpl.__micraList\n if (prevList.length === 0) {\n // First render (or refill after a clear): every node is new and already in\n // order \u2014 batch into one fragment so the DOM takes a single insertion\n // instead of N anchor.after() calls. Skips LIS entirely.\n if (nextNodes.length) {\n const frag = document.createDocumentFragment()\n for (const node of nextNodes) frag.append(node)\n marker.after(frag)\n }\n } else {\n // Skip DOM reorder when list order is unchanged (pure JS array compare, no DOM reads).\n let orderChanged = nextNodes.length !== prevList.length\n if (!orderChanged) {\n for (let i = 0; i < nextNodes.length; i++) {\n if (nextNodes[i] !== prevList[i]) { orderChanged = true; break }\n }\n }\n if (orderChanged) reorderKeyed(nextNodes, prevList, marker)\n }\n\n tmpl.__micraList = nextNodes\n}\n\n// \u2500\u2500 Keyed list reorder (LIS) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Move DOM nodes to match `nextNodes` order using the minimum number of moves.\n *\n * Computes the Longest Increasing Subsequence of each node's position in prevList \u2014\n * nodes in the LIS keep their place. Only the others are re-inserted via anchor.after().\n *\n * Complexity: O(n log n) for LIS, O(k) DOM operations where k = nodes that moved.\n * For a 2-node swap this means 2 DOM ops instead of n.\n */\nfunction reorderKeyed(nextNodes: MicraElement[], prevList: MicraElement[], marker: Comment): void {\n const prevPos = new Map<MicraElement, number>()\n for (let i = 0; i < prevList.length; i++) prevPos.set(prevList[i]!, i)\n\n const n = nextNodes.length\n const tails: number[] = [] // patience sort: smallest tail at each LIS length\n const tailIdx: number[] = [] // index into nextNodes for each tail\n const prev: number[] = new Array(n).fill(-1)\n\n for (let i = 0; i < n; i++) {\n const p = prevPos.get(nextNodes[i]!)\n if (p === undefined) continue // new node \u2014 always moved\n let lo = 0, hi = tails.length\n while (lo < hi) { const m = (lo + hi) >> 1; tails[m]! < p ? lo = m + 1 : hi = m }\n if (lo > 0) prev[i] = tailIdx[lo - 1]!\n tails[lo] = p\n tailIdx[lo] = i\n }\n\n // Reconstruct stable (non-moving) set from LIS parent chain\n const stable = new Set<number>()\n let idx: number = tailIdx[tails.length - 1]!\n while (idx >= 0) { stable.add(idx); idx = prev[idx]! }\n\n // Move unstable nodes into position; stable (LIS) nodes serve as anchors\n let anchor: ChildNode = marker\n for (let i = 0; i < n; i++) {\n const node = nextNodes[i]!\n if (stable.has(i)) { anchor = node; continue }\n anchor.after(node)\n anchor = node\n }\n}\n\n// \u2500\u2500 Non-keyed (positional reuse) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Diff a non-keyed list by length: reuse the first min(prev, next) DOM nodes,\n * remove the tail when the list shrinks, clone fresh rows for the growth delta.\n * Multi-root template rows are wrapped in `<micra-each-item style=\"display:contents\">`\n * \u2014 same as keyed mode \u2014 so the reused list is one DOM node per row.\n */\nfunction renderNoKey<S extends StateRecord>(\n tmpl: MicraTemplate,\n items: StateRecord[],\n marker: Comment,\n state: StateRecord,\n rawState: StateRecord,\n instance: InternalInstance<S>,\n canSkipUnchanged: boolean,\n): void {\n const prevList = tmpl.__micraList\n const prevLen = prevList.length\n const nextLen = items.length\n const reuseLen = nextLen < prevLen ? nextLen : prevLen\n const nextList: MicraElement[] = new Array(nextLen)\n\n // 1. Reuse [0, reuseLen): refresh itemState, re-apply directives in place.\n for (let i = 0; i < reuseLen; i++) {\n const node = prevList[i]!\n const item = items[i]!\n if (canSkipUnchanged && node.__micraItem === item && node.__micraIndex === i) {\n nextList[i] = node\n continue\n }\n node.__micraItem = item\n node.__micraIndex = i\n const itemState = node._itemState!\n itemState.item = item\n itemState.index = i\n itemState.$index = i\n applyDirectives(node.__micraScan!, itemState, rawState)\n nextList[i] = node\n }\n\n // 2. Shrink: remove tail nodes [nextLen, prevLen).\n for (let i = nextLen; i < prevLen; i++) {\n prevList[i]!.remove()\n }\n\n // 3. Grow: clone and attach fresh rows for [prevLen, nextLen).\n if (nextLen > prevLen) {\n const frag = document.createDocumentFragment()\n for (let i = prevLen; i < nextLen; i++) {\n const node = createRowNode(tmpl, state, instance)\n const item = items[i]!\n const itemState = node._itemState!\n itemState.item = item\n itemState.index = i\n itemState.$index = i\n node.__micraItem = item\n node.__micraIndex = i\n applyDirectives(node.__micraScan!, itemState, rawState)\n nextList[i] = node\n frag.append(node)\n }\n // Insert after the last reused node, or the marker if the list was empty.\n const anchor: ChildNode = prevLen > 0 ? nextList[prevLen - 1]! : marker\n anchor.after(frag)\n }\n\n tmpl.__micraList = nextList\n}\n", "/**\n * src/dom/refs.ts \u2014 data-ref collection.\n *\n * Responsibilities:\n * - Populate `instance.refs` from a pre-scanned list of [data-ref] elements.\n *\n * LLM NOTE: This module is PURE relative to state \u2014 it only reads DOM attributes\n * and writes to instance.refs. It does NOT trigger renders.\n */\n\nimport type { InternalInstance, MicraElement, StateRecord } from '../types'\n\n/**\n * Build `instance.refs` from the pre-scanned [data-ref] elements.\n *\n * Called once after the initial render and again on every re-render (refs may\n * point to newly created elements after an each-list update).\n *\n * @param els - List of [data-ref] elements from scan.ts\n *\n * @example\n * // HTML: <canvas data-ref=\"chart\">\n * // JS: this.refs.chart \u2192 HTMLCanvasElement\n */\nexport function collectRefs<S extends StateRecord>(\n els: Element[],\n instance: InternalInstance<S>,\n): void {\n if (!els.length) return\n instance.refs = {}\n for (const el of els) {\n const name = (el as MicraElement).dataset['ref']\n if (name) instance.refs[name] = el as HTMLElement\n }\n}\n", "/**\n * src/core/mount.ts \u2014 Mount a component definition onto a DOM element.\n *\n * Responsibilities:\n * - Create and initialize an InternalInstance\n * - Set up reactive state + batch scheduler\n * - Wire render(), destroy(), prop(), fetch(), on(), emit()\n * - Run initial render + call onCreate() in a microtask\n *\n * LLM NOTE: This is the core of the Micra runtime.\n * mount() is called by both the public Micra.mount() API and by start()\n * (which scans the DOM for [data-component] elements).\n */\n\nimport type {\n ComponentDefinition,\n ComponentInstance,\n ComponentMethods,\n EventHandler,\n EventPayload,\n InternalInstance,\n MicraElement,\n\n StateRecord,\n UnsubFn,\n} from \"../types\";\nimport { warn } from \"../utils/expr\";\nimport { micraFetch } from \"../utils/fetch\";\nimport { on as busOn, emit as busEmit } from \"../core/bus\";\nimport { createReactiveState, createScheduler } from \"../core/reactive\";\nimport { applyDirectives, validateDirectives } from \"../dom/directives\";\nimport { renderList } from \"../dom/each\";\nimport { bindDataOn, bindAtEvents, bindModels } from \"../dom/events\";\nimport { collectRefs } from \"../dom/refs\";\nimport { scanComponent } from \"../dom/scan\";\nimport { _instances } from \"../core/registry\";\n\n/**\n * Mount a component definition onto a DOM element.\n * Returns the component instance, or null if the root element is not found.\n *\n * Already-mounted elements return the existing instance.\n *\n * Both `S` (state) and `M` (methods) are inferred from the literal \u2014 the\n * returned instance is fully typed: `inst.state.X` and `inst.someMethod()`\n * are checked.\n *\n * @example\n * const instance = Micra.mount('#counter', {\n * state: { count: 0 },\n * inc() { this.state.count++ },\n * })\n * instance?.inc()\n * instance?.state.count\n */\nexport function mount<S extends StateRecord, M>(\n selector: string | HTMLElement,\n definition: ComponentDefinition<S, M>,\n): ComponentInstance<S, M> | null {\n const root =\n typeof selector === \"string\"\n ? document.querySelector<HTMLElement>(selector)\n : selector;\n\n if (!root) {\n warn(`\"${selector}\" not found`);\n return null;\n }\n\n // Already mounted \u2014 return existing instance without re-mounting\n if (_instances.has(root))\n return _instances.get(root) as unknown as ComponentInstance<S, M>;\n\n const rawState: StateRecord = { ...(definition.state ?? {}) };\n const instance = { $el: root, refs: {} } as InternalInstance<S>;\n\n // Copy user-defined methods from definition to instance\n for (const [key, val] of Object.entries(\n definition as Record<string, unknown>,\n )) {\n if (key === \"state\" || key === \"onCreate\" || key === \"onDestroy\") continue;\n if (typeof val === \"function\") instance[key] = val;\n }\n\n // \u2500\u2500 prop() \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Read data-* attributes from the root element with auto-cast.\n instance.prop = function <T>(name: string, defaultVal?: T): T | undefined {\n const val = root.dataset[name];\n if (val === undefined) return defaultVal;\n if (val === \"true\") return true as unknown as T;\n if (val === \"false\") return false as unknown as T;\n if (val !== \"\" && !isNaN(Number(val))) return Number(val) as unknown as T;\n return val as unknown as T;\n } as ComponentInstance<S>[\"prop\"];\n\n // \u2500\u2500 fetch(), emit(), on() \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n instance.fetch = micraFetch;\n instance.emit = busEmit;\n\n instance.on = <K extends string>(\n event: K,\n handler: (payload: EventPayload<K>) => void,\n ): UnsubFn => {\n const unsub = busOn(event, handler as EventHandler);\n if (!instance.__micraSubs) instance.__micraSubs = [];\n instance.__micraSubs.push(unsub);\n return unsub;\n };\n\n // \u2500\u2500 Render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let isRendering = false;\n // Track which state key triggered the current render cycle.\n // 'MULTIPLE' means more than one key was written before the microtask fired.\n let _triggerKey: string | null | \"MULTIPLE\" = null;\n const schedule = createScheduler(() => instance.render());\n instance.state = createReactiveState(rawState, schedule, (key) => {\n if (_triggerKey === null) _triggerKey = key;\n else if (_triggerKey !== key) _triggerKey = \"MULTIPLE\";\n }) as S;\n\n // Expression state: proxy that falls back to instance methods so expressions\n // like `data-text=\"formatDate(item.date)\"` can call component methods.\n //\n // Instance methods are returned BOUND to the instance \u2014 directive expressions\n // call them as bare identifiers via `with()`, which would normally lose `this`.\n // Bound copies are memoized per method name so repeated reads are cheap.\n //\n // Both traps reject Object.prototype names ('constructor', 'toString', ...) \u2014\n // accessing them via a directive expression returns undefined instead of\n // leaking the prototype.\n const boundMethods = new Map<string, Function>();\n const exprState = new Proxy(rawState, {\n get(target, key: string) {\n if (Object.prototype.hasOwnProperty.call(target, key)) return target[key];\n if (\n Object.prototype.hasOwnProperty.call(instance, key) &&\n typeof instance[key] === \"function\"\n ) {\n const cached = boundMethods.get(key);\n if (cached) return cached;\n const bound = (instance[key] as Function).bind(instance);\n boundMethods.set(key, bound);\n return bound;\n }\n return undefined;\n },\n has(target, key: string) {\n if (typeof key !== \"string\") return false;\n if (Object.prototype.hasOwnProperty.call(target, key)) return true;\n return (\n Object.prototype.hasOwnProperty.call(instance, key) &&\n typeof instance[key] === \"function\"\n );\n },\n });\n\n let warnedReentry = false;\n instance.render = function () {\n if (instance.__micraDestroyed) return;\n const triggerKey = _triggerKey;\n _triggerKey = null;\n if (isRendering) {\n if (!warnedReentry) {\n warn(\n \"render() re-entry detected \u2014 mutation inside a directive expression is ignored. Move state writes to a method.\",\n );\n warnedReentry = true;\n }\n return;\n }\n isRendering = true;\n try {\n // Single-pass scan, cached on the root for re-renders. Replaces what\n // used to be ~10 separate querySelectorAll passes per render.\n const mRoot = root as MicraElement;\n const scan =\n mRoot.__micraScan ?? (mRoot.__micraScan = scanComponent(root));\n applyDirectives(scan, exprState, rawState);\n renderList(scan.each, exprState, rawState, instance, triggerKey);\n bindDataOn(scan.on, instance);\n bindAtEvents(scan.atEvents, instance);\n bindModels(scan.model, instance);\n collectRefs(scan.refs, instance);\n } finally {\n isRendering = false;\n }\n };\n\n // \u2500\u2500 Destroy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n instance.destroy = function () {\n if (instance.__micraDestroyed) return;\n instance.__micraDestroyed = true;\n\n // Remove every DOM listener attached by bindDataOn / bindAtEvents / bindModels.\n instance.__micraListeners?.forEach(({ el, type, fn }) =>\n el.removeEventListener(type, fn),\n );\n instance.__micraListeners = [];\n\n // Clear per-element flags & cached scan so a future re-mount of the same DOM works.\n const clearFlags = (el: Element) => {\n const m = el as MicraElement;\n delete m.__micraEvents;\n delete m.__micraAtBound;\n delete m.__micraModel;\n delete m.__micraScan;\n };\n clearFlags(root);\n root.querySelectorAll(\"*\").forEach(clearFlags);\n\n instance.__micraSubs?.forEach((unsub) => unsub());\n instance.__micraSubs = [];\n\n if (typeof (definition as Record<string, unknown>).onDestroy === \"function\")\n (definition.onDestroy as () => void).call(instance);\n _instances.delete(root);\n };\n\n // \u2500\u2500 Bootstrap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n _instances.set(root, instance as InternalInstance);\n instance.render();\n\n // Validate directive usage and emit dev warnings \u2014 reuses the same scan.\n const mRoot = root as MicraElement;\n if (mRoot.__micraScan) validateDirectives(mRoot.__micraScan);\n\n if (typeof (definition as Record<string, unknown>).onCreate === \"function\")\n Promise.resolve().then(() =>\n (definition.onCreate as () => void | Promise<void>).call(instance),\n );\n\n return instance as unknown as ComponentInstance<S, M>;\n}\n", "/**\n * src/core/start.ts \u2014 Auto-mount via [data-component].\n *\n * Responsibilities:\n * - Scan the DOM (or a subtree) for [data-component] elements\n * - Mount each using the registered definition\n * - Skip already-mounted elements (safe to call multiple times)\n * - Warn clearly when a component name is not registered\n *\n * LLM NOTE: start() is SSR-friendly \u2014 calling it multiple times is safe\n * because mount() checks _instances before re-mounting.\n */\n\nimport { warn } from '../utils/expr'\nimport { _registry, _instances } from './registry'\nimport { mount } from './mount'\n\n/**\n * Scan for `[data-component]` elements and auto-mount registered definitions.\n *\n * Pass a subtree root to limit the scan (e.g., after a partial SSR update):\n * `Micra.start(document.getElementById('panel'))`\n *\n * @example\n * // Mount everything on the page (called once after DOM ready)\n * Micra.start()\n *\n * // Re-mount after injecting new HTML\n * Micra.start(document.querySelector('#dynamic-section'))\n */\nexport function start(root: Document | HTMLElement = document): void {\n root.querySelectorAll<HTMLElement>('[data-component]').forEach(el => {\n if (_instances.has(el)) return // already mounted \u2014 skip\n const name = el.getAttribute('data-component')!\n const def = _registry.get(name)\n if (!def) {\n warn(`component \"${name}\" not defined. Call Micra.define('${name}', {...}) first.`)\n return\n }\n mount(el, def)\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["_a", "mRoot"]
|
|
7
7
|
}
|
package/dist/micra.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/* Micra.js v2.3.
|
|
2
|
-
"use strict";var Micra=(()=>{var O=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var ae=Object.prototype.hasOwnProperty;var ie=(e,t)=>{for(var r in t)O(e,r,{get:t[r],enumerable:!0})},se=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of oe(t))!ae.call(e,n)&&n!==r&&O(e,n,{get:()=>t[n],enumerable:!(o=re(t,n))||o.enumerable});return e};var ce=e=>se(O({},"__esModule",{value:!0}),e);var Te={};ie(Te,{FetchError:()=>M,debug:()=>K,define:()=>F,defineComponent:()=>j,emit:()=>T,instances:()=>H,mount:()=>P,off:()=>k,on:()=>C,registry:()=>B,start:()=>te});function le(){var e,t;return(t=(e=document.querySelector('meta[name="csrf-token"]'))==null?void 0:e.getAttribute("content"))!=null?t:null}var M=class extends Error{constructor(r,o,n){super(r);this.status=o;this.response=n;this.name="FetchError"}};async function D(e,t={}){var m,h;let r=((m=t.method)!=null?m:"GET").toUpperCase(),o={Accept:"application/json",...t.headers},n=le();n&&(o["X-CSRF-Token"]=n);let a=e,u;if(r==="GET"||r==="HEAD"){let d={};for(let[f,s]of Object.entries(t))f!=="method"&&f!=="headers"&&f!=="signal"&&s!=null&&(d[f]=String(s));Object.keys(d).length&&(a+=(e.includes("?")?"&":"?")+new URLSearchParams(d))}else t.body!==void 0&&(o["Content-Type"]="application/json",u=JSON.stringify(t.body));let c=await fetch(a,{method:r,headers:o,...t.signal!==void 0?{signal:t.signal}:{},...u!==void 0?{body:u}:{}});if(!c.ok)throw new M(`[Micra] fetch: ${r} ${e} \u2192 ${c.status}`,c.status,c);return((h=c.headers.get("content-type"))!=null?h:"").includes("application/json")?c.json():c.text()}var w=new Map,_=new Map;function F(e,t){w.set(e,t)}function j(e){return e}function H(){return _}function B(){return w}function K(){var e;if(_.size===0){console.log("[Micra] No live components.");return}console.group(`[Micra] ${_.size} live component(s)`);for(let[t,r]of _){let o=(e=t.getAttribute("data-component"))!=null?e:"(unnamed)";console.group(`%c${o}`,"font-weight:bold;color:#6366f1"),console.log("$el ",t),console.log("state",{...r.state}),console.groupEnd()}console.groupEnd()}var U=new Map,W=new Set,de=/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/,fe=new Set(["Math","JSON","Date","String","Number","Boolean","Array","Object","parseInt","parseFloat","isNaN","isFinite","NaN","Infinity","undefined"]),me="$s",ue="$safe",pe=new Proxy(Object.create(null),{has(e,t){return typeof t!="string"||t===me||t===ue?!1:!fe.has(t)},get(){}}),z=new WeakMap,he=new Set(Object.getOwnPropertyNames(Object.prototype));function ge(e){let t=z.get(e);if(t)return t;let r=new Proxy(e,{has(o,n){return J(o,n)},get(o,n){return Reflect.get(o,n)}});return z.set(e,r),r}function J(e,t){if(typeof t!="string"||!Reflect.has(e,t))return!1;if(!he.has(t))return!0;let r=e;for(;r&&r!==Object.prototype;){if(Object.prototype.hasOwnProperty.call(r,t))return!0;r=Object.getPrototypeOf(r)}return!1}function b(e,t){let r=U.get(e);if(!r){if(de.test(e))r={kind:"path",parts:e.split(".")};else try{r={kind:"fn",fn:new Function("$s","$safe",`with($safe){with($s){return (${e})}}`)}}catch{y(`invalid expression "${e}"`),r={kind:"fn",fn:()=>{}}}U.set(e,r)}if(r.kind==="path")return J(t,r.parts[0])?r.parts.reduce((o,n)=>o!=null?o[n]:void 0,t):void 0;try{return r.fn(ge(t),pe)}catch(o){W.has(e)||(W.add(e),y(`runtime error in "${e}": ${o.message}`));return}}function y(e){console.warn(`[Micra] ${e}`)}var v=new Map;function C(e,t){return v.has(e)||v.set(e,new Set),v.get(e).add(t),()=>k(e,t)}function k(e,t){let r=v.get(e);r&&(r.delete(t),r.size===0&&v.delete(e))}function T(e,...t){var o;let r=t[0];(o=v.get(e))==null||o.forEach(n=>{try{n(r)}catch(a){console.error(`[Micra] bus error [${e}]:`,a)}})}function q(e,t,r){return new Proxy(e,{set(o,n,a){return o[n]=a,r==null||r(n),t(),!0}})}function Z(e){let t=!1;return function(){t||(t=!0,Promise.resolve().then(()=>{t=!1,e()}))}}function Se(e,t,r){var n;let o=String((n=b(t,r))!=null?n:"");e.textContent!==o&&(e.textContent=o)}function Ee(e,t,r){var n;let o=String((n=b(t,r))!=null?n:"");e.innerHTML!==o&&(e.innerHTML=o)}function ye(e,t){let r=e.el;if(!!b(e.expr,t)){let n=e.placeholder;n&&n.parentNode&&n.parentNode.replaceChild(r,n)}else{let n=r.parentNode;n&&(e.placeholder||(e.placeholder=document.createComment("if")),n.replaceChild(e.placeholder,r))}}function _e(e,t,r){let o=b(t,r)?"":"none",n=e;n.style.display!==o&&(n.style.display=o)}function be(e,t,r){for(let[o,n]of t){let a=b(n,r);o==="class"?e.className=String(a!=null?a:""):o==="value"?document.activeElement!==e&&(e.value=String(a!=null?a:"")):o==="style"?typeof a=="object"&&a!==null?Object.assign(e.style,a):e.setAttribute("style",String(a!=null?a:"")):typeof a=="boolean"?a?e.setAttribute(o,""):e.removeAttribute(o):a==null?e.removeAttribute(o):e.setAttribute(o,String(a))}}function ve(e,t,r){for(let[o,n]of t)e.classList.toggle(o,!!b(n,r))}function xe(e,t,r){let o=e,n=r[t],a=n==null?"":String(n);o.value!==a&&(o.value=a)}function x(e,t,r,o){for(let n of e.if)ye(n,t);for(let n of e.text)Se(n.el,n.expr,t);for(let n of e.html)Ee(n.el,n.expr,t);for(let n of e.show)_e(n.el,n.expr,t);for(let n of e.bind)be(n.el,n.pairs,t);for(let n of e.model)xe(n.el,n.expr.trim(),r);for(let n of e.class)ve(n.el,n.pairs,t)}function G(e){for(let t of e.each){let r=t;!t.hasAttribute("data-key")&&!r.__micraNoKeyWarned&&(r.__micraNoKeyWarned=!0,y(`data-each="${t.getAttribute("data-each")}" has no data-key \u2014 keyed diff disabled. Add data-key="id" for better performance.`))}for(let t of e.bind)t.pairs.some(o=>o[0]==="class")&&t.el.hasAttribute("data-class")&&y('element has both data-bind="class:..." and data-class \u2014 they fight on every render. Use one.')}function $(e,t,r,o){var n;t.addEventListener(r,o),((n=e.__micraListeners)!=null?n:e.__micraListeners=[]).push({el:t,type:r,fn:o})}function L(e,t){var r;for(let o of e){let n=o;if(n.__micraEvents)continue;n.__micraEvents=!0;let a=(r=n.dataset.on)!=null?r:"";for(let u of a.split(",")){let[c,p]=u.trim().split(":");if(!c||!p)continue;let[m,...h]=c.split(".");$(t,o,m,d=>{if(h.includes("prevent")&&d.preventDefault(),h.includes("stop")&&d.stopPropagation(),h.includes("self")&&d.target!==o)return;let f=t[p.trim()];typeof f=="function"?f.call(t,d):y(`method "${p.trim()}" not found`)})}}}function A(e,t){for(let r of e){let o=r;if(o.__micraAtBound)continue;let n=!1;for(let a of Array.from(r.attributes)){if(!a.name.startsWith("@"))continue;let[u,...c]=a.name.slice(1).split("."),p=a.value.trim();$(t,r,u,m=>{if(c.includes("prevent")&&m.preventDefault(),c.includes("stop")&&m.stopPropagation(),c.includes("self")&&m.target!==r)return;let h=t[p];typeof h=="function"?h.call(t,m):y(`method "${p}" not found`)}),n=!0}n&&(o.__micraAtBound=!0)}}function N(e,t){for(let{el:r,expr:o}of e){let n=r;if(n.__micraModel)continue;n.__micraModel=!0;let a=o.trim(),u=r.tagName,c=r,p=c.type;$(t,r,u==="SELECT"||p==="radio"?"change":"input",()=>{let d;u==="INPUT"&&p==="checkbox"?d=c.checked:u==="INPUT"&&(p==="number"||p==="range")?d=c.value===""?null:c.valueAsNumber:d=c.value,t.state[a]=d})}}function Me(){return{text:[],html:[],if:[],show:[],bind:[],model:[],class:[],each:[],on:[],atEvents:[],refs:[]}}function V(e){let t=[];for(let r of e.split(",")){let o=r.indexOf(":");if(o===-1)continue;let n=r.slice(0,o).trim(),a=r.slice(o+1).trim();n&&t.push([n,a])}return t}function X(e,t){if(e.tagName==="TEMPLATE"){e.hasAttribute("data-each")&&t.each.push(e);return}let r=e.attributes,o=!1;for(let n=0;n<r.length;n++){let a=r[n],u=a.name,c=u.charCodeAt(0);if(c===64){o||(t.atEvents.push(e),o=!0);continue}if(c===100&&u.length>=6&&u.charCodeAt(4)===45)switch(u.slice(5)){case"text":t.text.push({el:e,expr:a.value});break;case"html":t.html.push({el:e,expr:a.value});break;case"if":t.if.push({el:e,expr:a.value});break;case"show":t.show.push({el:e,expr:a.value});break;case"bind":{let m=V(a.value);t.bind.push({el:e,expr:a.value,pairs:m});break}case"model":t.model.push({el:e,expr:a.value});break;case"class":{let m=V(a.value);t.class.push({el:e,expr:a.value,pairs:m});break}case"on":t.on.push(e);break;case"ref":t.refs.push(e);break}}}var Ie={acceptNode(e){return e.hasAttribute("data-component")?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}};function I(e){let t=Me();X(e,t);let r=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,Ie),o=r.nextNode();for(;o;)X(o,t),o=r.nextNode();return t}function Y(e,t,r,o,n){var a;for(let u of e){if(u.tagName!=="TEMPLATE")continue;let c=u,p=c.getAttribute("data-each"),m=(a=c.getAttribute("data-key"))!=null?a:null,h=b(p,t);if(!c.__micraMarker){let l=document.createComment(`each:${p}`);c.after(l),c.__micraMarker=l,c.__micraNodes=new Map,c.__micraList=[]}let d=c.__micraMarker,f=c.__micraNodes;if(!d.parentNode)continue;if(!Array.isArray(h)){c.__micraList.forEach(l=>l.remove()),c.__micraList=[],f.clear();continue}let s=n!==null&&n!=="MULTIPLE"&&n===p;m?Re(c,h,m,d,f,t,r,o,s):Ce(c,h,d,t,r,o,s)}}function Q(e,t,r){let o=e.content.cloneNode(!0),n;o.childNodes.length===1?n=o.firstElementChild:(n=document.createElement("micra-each-item"),n.style.display="contents",n.append(o));let a=I(n);return n.__micraScan=a,n._itemState=Object.create(t),L(a.on,r),A(a.atEvents,r),N(a.model,r),n}function Re(e,t,r,o,n,a,u,c,p){var l;let m=new Set,h=[],d=!1,f=!1;for(let[i,g]of t.entries()){let S=g[r];S==null&&!d&&(y(`data-key="${r}" is null/undefined on item at index ${i}`),d=!0),m.has(S)&&!f&&(y(`data-key="${r}" has duplicate value ${JSON.stringify(S)} \u2014 rows will collide`),f=!0),m.add(S);let E=n.get(S);if(!E)E=Q(e,a,c),E.__micraKey=S,n.set(S,E);else if(p&&E.__micraItem===g&&E.__micraIndex===i){h.push(E);continue}E.__micraItem=g,E.__micraIndex=i;let R=E._itemState;R.item=g,R.index=i,R.$index=i;let ne=(l=E.__micraScan)!=null?l:E.__micraScan=I(E);x(ne,R,u,c),h.push(E)}for(let[i,g]of n)m.has(i)||(g.remove(),n.delete(i));let s=e.__micraList;if(s.length===0){if(h.length){let i=document.createDocumentFragment();for(let g of h)i.append(g);o.after(i)}}else{let i=h.length!==s.length;if(!i){for(let g=0;g<h.length;g++)if(h[g]!==s[g]){i=!0;break}}i&&we(h,s,o)}e.__micraList=h}function we(e,t,r){let o=new Map;for(let d=0;d<t.length;d++)o.set(t[d],d);let n=e.length,a=[],u=[],c=new Array(n).fill(-1);for(let d=0;d<n;d++){let f=o.get(e[d]);if(f===void 0)continue;let s=0,l=a.length;for(;s<l;){let i=s+l>>1;a[i]<f?s=i+1:l=i}s>0&&(c[d]=u[s-1]),a[s]=f,u[s]=d}let p=new Set,m=u[a.length-1];for(;m>=0;)p.add(m),m=c[m];let h=r;for(let d=0;d<n;d++){let f=e[d];if(p.has(d)){h=f;continue}h.after(f),h=f}}function Ce(e,t,r,o,n,a,u){let c=e.__micraList,p=c.length,m=t.length,h=m<p?m:p,d=new Array(m);for(let f=0;f<h;f++){let s=c[f],l=t[f];if(u&&s.__micraItem===l&&s.__micraIndex===f){d[f]=s;continue}s.__micraItem=l,s.__micraIndex=f;let i=s._itemState;i.item=l,i.index=f,i.$index=f,x(s.__micraScan,i,n,a),d[f]=s}for(let f=m;f<p;f++)c[f].remove();if(m>p){let f=document.createDocumentFragment();for(let l=p;l<m;l++){let i=Q(e,o,a),g=t[l],S=i._itemState;S.item=g,S.index=l,S.$index=l,i.__micraEach=!0,i.__micraItem=g,i.__micraIndex=l,x(i.__micraScan,S,n,a),d[l]=i,f.append(i)}(p>0?d[p-1]:r).after(f)}e.__micraList=d}function ee(e,t){if(e.length){t.refs={};for(let r of e){let o=r.dataset.ref;o&&(t.refs[o]=r)}}}function P(e,t){var f;let r=typeof e=="string"?document.querySelector(e):e;if(!r)return y(`"${e}" not found`),null;if(_.has(r))return _.get(r);let o={...(f=t.state)!=null?f:{}},n={$el:r,refs:{}};for(let[s,l]of Object.entries(t))s==="state"||s==="onCreate"||s==="onDestroy"||typeof l=="function"&&(n[s]=l);n.prop=function(s,l){let i=r.dataset[s];return i===void 0?l:i==="true"?!0:i==="false"?!1:i!==""&&!isNaN(Number(i))?Number(i):i},n.fetch=D,n.emit=T,n.on=(s,l)=>{let i=C(s,l);return n.__micraSubs||(n.__micraSubs=[]),n.__micraSubs.push(i),i};let a=!1,u=null,c=Z(()=>n.render());n.state=q(o,c,s=>{u===null?u=s:u!==s&&(u="MULTIPLE")});let p=new Map,m=new Proxy(o,{get(s,l){if(Object.prototype.hasOwnProperty.call(s,l))return s[l];if(Object.prototype.hasOwnProperty.call(n,l)&&typeof n[l]=="function"){let i=p.get(l);if(i)return i;let g=n[l].bind(n);return p.set(l,g),g}},has(s,l){return typeof l!="string"?!1:Object.prototype.hasOwnProperty.call(s,l)?!0:Object.prototype.hasOwnProperty.call(n,l)&&typeof n[l]=="function"}}),h=!1;n.render=function(){var l;if(n.__micraDestroyed)return;let s=u;if(u=null,a){h||(y("render() re-entry detected \u2014 mutation inside a directive expression is ignored. Move state writes to a method."),h=!0);return}a=!0;try{let i=r,g=(l=i.__micraScan)!=null?l:i.__micraScan=I(r);x(g,m,o,n),Y(g.each,m,o,n,s),L(g.on,n),A(g.atEvents,n),N(g.model,n),ee(g.refs,n)}finally{a=!1}},n.destroy=function(){var l,i;if(n.__micraDestroyed)return;n.__micraDestroyed=!0,(l=n.__micraListeners)==null||l.forEach(({el:g,type:S,fn:E})=>g.removeEventListener(S,E)),n.__micraListeners=[];let s=g=>{let S=g;delete S.__micraEvents,delete S.__micraAtBound,delete S.__micraModel,delete S.__micraScan};s(r),r.querySelectorAll("*").forEach(s),(i=n.__micraSubs)==null||i.forEach(g=>g()),n.__micraSubs=[],typeof t.onDestroy=="function"&&t.onDestroy.call(n),_.delete(r)},_.set(r,n),n.render();let d=r;return d.__micraScan&&G(d.__micraScan),typeof t.onCreate=="function"&&Promise.resolve().then(()=>t.onCreate.call(n)),n}function te(e=document){e.querySelectorAll("[data-component]").forEach(t=>{if(_.has(t))return;let r=t.getAttribute("data-component"),o=w.get(r);if(!o){y(`component "${r}" not defined. Call Micra.define('${r}', {...}) first.`);return}P(t,o)})}return ce(Te);})();
|
|
1
|
+
/* Micra.js v2.3.1 — https://github.com/micra-js/micra — MIT */
|
|
2
|
+
"use strict";var Micra=(()=>{var k=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var ae=Object.prototype.hasOwnProperty;var ie=(e,t)=>{for(var n in t)k(e,n,{get:t[n],enumerable:!0})},se=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of oe(t))!ae.call(e,r)&&r!==n&&k(e,r,{get:()=>t[r],enumerable:!(o=re(t,r))||o.enumerable});return e};var ce=e=>se(k({},"__esModule",{value:!0}),e);var Te={};ie(Te,{FetchError:()=>M,debug:()=>K,define:()=>F,defineComponent:()=>j,emit:()=>T,instances:()=>H,mount:()=>P,off:()=>O,on:()=>C,registry:()=>B,start:()=>te});function le(){var e,t;return(t=(e=document.querySelector('meta[name="csrf-token"]'))==null?void 0:e.getAttribute("content"))!=null?t:null}var M=class extends Error{constructor(n,o,r){super(n);this.status=o;this.response=r;this.name="FetchError"}};async function D(e,t={}){var u,h;let n=((u=t.method)!=null?u:"GET").toUpperCase(),o={Accept:"application/json",...t.headers},r=le();r&&(o["X-CSRF-Token"]=r);let a=e,m;if(n==="GET"||n==="HEAD"){let d={};for(let[f,s]of Object.entries(t))f!=="method"&&f!=="headers"&&f!=="signal"&&s!=null&&(d[f]=String(s));Object.keys(d).length&&(a+=(e.includes("?")?"&":"?")+new URLSearchParams(d))}else t.body!==void 0&&(o["Content-Type"]="application/json",m=JSON.stringify(t.body));let l=await fetch(a,{method:n,headers:o,...t.signal!==void 0?{signal:t.signal}:{},...m!==void 0?{body:m}:{}});if(!l.ok)throw new M(`[Micra] fetch: ${n} ${e} \u2192 ${l.status}`,l.status,l);return((h=l.headers.get("content-type"))!=null?h:"").includes("application/json")?l.json():l.text()}var I=new Map,_=new Map;function F(e,t){I.set(e,t)}function j(e){return e}function H(){return _}function B(){return I}function K(){var e;if(_.size===0){console.log("[Micra] No live components.");return}console.group(`[Micra] ${_.size} live component(s)`);for(let[t,n]of _){let o=(e=t.getAttribute("data-component"))!=null?e:"(unnamed)";console.group(`%c${o}`,"font-weight:bold;color:#6366f1"),console.log("$el ",t),console.log("state",{...n.state}),console.groupEnd()}console.groupEnd()}var U=new Map,W=new Set,de=/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/,fe=new Set(["Math","JSON","Date","String","Number","Boolean","Array","Object","parseInt","parseFloat","isNaN","isFinite","NaN","Infinity","undefined"]),ue="$s",me="$safe",pe=new Proxy(Object.create(null),{has(e,t){return typeof t!="string"||t===ue||t===me?!1:!fe.has(t)},get(){}}),z=new WeakMap,he=new Set(Object.getOwnPropertyNames(Object.prototype));function ge(e){let t=z.get(e);if(t)return t;let n=new Proxy(e,{has(o,r){return q(o,r)},get(o,r){return Reflect.get(o,r)}});return z.set(e,n),n}function q(e,t){if(typeof t!="string"||!Reflect.has(e,t))return!1;if(!he.has(t))return!0;let n=e;for(;n&&n!==Object.prototype;){if(Object.prototype.hasOwnProperty.call(n,t))return!0;n=Object.getPrototypeOf(n)}return!1}function b(e,t){let n=U.get(e);if(!n){if(de.test(e))n={kind:"path",parts:e.split(".")};else try{n={kind:"fn",fn:new Function("$s","$safe",`with($safe){with($s){return (${e})}}`)}}catch{E(`invalid expression "${e}"`),n={kind:"fn",fn:()=>{}}}U.set(e,n)}if(n.kind==="path")return q(t,n.parts[0])?n.parts.reduce((o,r)=>o!=null?o[r]:void 0,t):void 0;try{return n.fn(ge(t),pe)}catch(o){W.has(e)||(W.add(e),E(`runtime error in "${e}": ${o.message}`));return}}function E(e){console.warn(`[Micra] ${e}`)}var v=new Map;function C(e,t){return v.has(e)||v.set(e,new Set),v.get(e).add(t),()=>O(e,t)}function O(e,t){let n=v.get(e);n&&(n.delete(t),n.size===0&&v.delete(e))}function T(e,...t){var o;let n=t[0];(o=v.get(e))==null||o.forEach(r=>{try{r(n)}catch(a){console.error(`[Micra] bus error [${e}]:`,a)}})}function J(e,t,n){return new Proxy(e,{set(o,r,a){return o[r]=a,n==null||n(r),t(),!0}})}function Z(e){let t=!1,n=()=>{t=!1,e()};return function(){t||(t=!0,queueMicrotask(n))}}function Se(e,t,n){var r;let o=String((r=b(t,n))!=null?r:"");e.textContent!==o&&(e.textContent=o)}function Ee(e,t,n){var r;let o=String((r=b(t,n))!=null?r:"");e.innerHTML!==o&&(e.innerHTML=o)}function ye(e,t){let n=e.el;if(!!b(e.expr,t)){let r=e.placeholder;r&&r.parentNode&&r.parentNode.replaceChild(n,r)}else{let r=n.parentNode;r&&(e.placeholder||(e.placeholder=document.createComment("if")),r.replaceChild(e.placeholder,n))}}function _e(e,t,n){let o=b(t,n)?"":"none",r=e;r.style.display!==o&&(r.style.display=o)}function be(e,t,n){for(let[o,r]of t){let a=b(r,n);o==="class"?e.className=String(a!=null?a:""):o==="value"?document.activeElement!==e&&(e.value=String(a!=null?a:"")):o==="style"?typeof a=="object"&&a!==null?Object.assign(e.style,a):e.setAttribute("style",String(a!=null?a:"")):typeof a=="boolean"?a?e.setAttribute(o,""):e.removeAttribute(o):a==null?e.removeAttribute(o):e.setAttribute(o,String(a))}}function ve(e,t,n){for(let[o,r]of t)e.classList.toggle(o,!!b(r,n))}function xe(e,t,n){let o=e,r=n[t],a=r==null?"":String(r);o.value!==a&&(o.value=a)}function x(e,t,n){for(let o of e.if)ye(o,t);for(let o of e.text)Se(o.el,o.expr,t);for(let o of e.html)Ee(o.el,o.expr,t);for(let o of e.show)_e(o.el,o.expr,t);for(let o of e.bind)be(o.el,o.pairs,t);for(let o of e.model)xe(o.el,o.expr.trim(),n);for(let o of e.class)ve(o.el,o.pairs,t)}function G(e){for(let t of e.each){let n=t;!t.hasAttribute("data-key")&&!n.__micraNoKeyWarned&&(n.__micraNoKeyWarned=!0,E(`data-each="${t.getAttribute("data-each")}" has no data-key \u2014 keyed diff disabled. Add data-key="id" for better performance.`))}for(let t of e.bind)t.pairs.some(o=>o[0]==="class")&&t.el.hasAttribute("data-class")&&E('element has both data-bind="class:..." and data-class \u2014 they fight on every render. Use one.')}function $(e,t,n,o){var r;t.addEventListener(n,o),((r=e.__micraListeners)!=null?r:e.__micraListeners=[]).push({el:t,type:n,fn:o})}function L(e,t){var n;for(let o of e){let r=o;if(r.__micraEvents)continue;r.__micraEvents=!0;let a=(n=r.dataset.on)!=null?n:"";for(let m of a.split(",")){let[l,p]=m.trim().split(":");if(!l||!p)continue;let[u,...h]=l.split(".");$(t,o,u,d=>{if(h.includes("prevent")&&d.preventDefault(),h.includes("stop")&&d.stopPropagation(),h.includes("self")&&d.target!==o)return;let f=t[p.trim()];typeof f=="function"?f.call(t,d):E(`method "${p.trim()}" not found`)})}}}function A(e,t){for(let n of e){let o=n;if(o.__micraAtBound)continue;let r=!1;for(let a of Array.from(n.attributes)){if(!a.name.startsWith("@"))continue;let[m,...l]=a.name.slice(1).split("."),p=a.value.trim();$(t,n,m,u=>{if(l.includes("prevent")&&u.preventDefault(),l.includes("stop")&&u.stopPropagation(),l.includes("self")&&u.target!==n)return;let h=t[p];typeof h=="function"?h.call(t,u):E(`method "${p}" not found`)}),r=!0}r&&(o.__micraAtBound=!0)}}function N(e,t){for(let{el:n,expr:o}of e){let r=n;if(r.__micraModel)continue;r.__micraModel=!0;let a=o.trim(),m=n.tagName,l=n,p=l.type;$(t,n,m==="SELECT"||p==="radio"?"change":"input",()=>{let d;m==="INPUT"&&p==="checkbox"?d=l.checked:m==="INPUT"&&(p==="number"||p==="range")?d=l.value===""?null:l.valueAsNumber:d=l.value,t.state[a]=d})}}function Me(){return{text:[],html:[],if:[],show:[],bind:[],model:[],class:[],each:[],on:[],atEvents:[],refs:[]}}function V(e){let t=[];for(let n of e.split(",")){let o=n.indexOf(":");if(o===-1)continue;let r=n.slice(0,o).trim(),a=n.slice(o+1).trim();r&&t.push([r,a])}return t}function X(e,t){if(e.tagName==="TEMPLATE"){e.hasAttribute("data-each")&&t.each.push(e);return}let n=e.attributes,o=!1;for(let r=0;r<n.length;r++){let a=n[r],m=a.name,l=m.charCodeAt(0);if(l===64){o||(t.atEvents.push(e),o=!0);continue}if(l===100&&m.length>=6&&m.charCodeAt(4)===45)switch(m.slice(5)){case"text":t.text.push({el:e,expr:a.value});break;case"html":t.html.push({el:e,expr:a.value});break;case"if":t.if.push({el:e,expr:a.value});break;case"show":t.show.push({el:e,expr:a.value});break;case"bind":{let u=V(a.value);t.bind.push({el:e,expr:a.value,pairs:u});break}case"model":t.model.push({el:e,expr:a.value});break;case"class":{let u=V(a.value);t.class.push({el:e,expr:a.value,pairs:u});break}case"on":t.on.push(e);break;case"ref":t.refs.push(e);break}}}var Re={acceptNode(e){return e.hasAttribute("data-component")?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}};function R(e){let t=Me();X(e,t);let n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,Re),o=n.nextNode();for(;o;)X(o,t),o=n.nextNode();return t}function Y(e,t,n,o,r){var a;for(let m of e){if(m.tagName!=="TEMPLATE")continue;let l=m,p=l.getAttribute("data-each"),u=(a=l.getAttribute("data-key"))!=null?a:null,h=b(p,t);if(!l.__micraMarker){let c=document.createComment(`each:${p}`);l.after(c),l.__micraMarker=c,l.__micraNodes=new Map,l.__micraList=[]}let d=l.__micraMarker,f=l.__micraNodes;if(!d.parentNode)continue;if(!Array.isArray(h)){l.__micraList.forEach(c=>c.remove()),l.__micraList=[],f.clear();continue}let s=r!==null&&r!=="MULTIPLE"&&r===p;u?we(l,h,u,d,f,t,n,o,s):Ce(l,h,d,t,n,o,s)}}function Q(e,t,n){let o=e.content.cloneNode(!0),r;o.childNodes.length===1?r=o.firstElementChild:(r=document.createElement("micra-each-item"),r.style.display="contents",r.append(o));let a=R(r);return r.__micraScan=a,r._itemState=Object.create(t),L(a.on,n),A(a.atEvents,n),N(a.model,n),r}function we(e,t,n,o,r,a,m,l,p){var c;let u=new Set,h=[],d=!1,f=!1;for(let[i,g]of t.entries()){let S=g[n];S==null&&!d&&(E(`data-key="${n}" is null/undefined on item at index ${i}`),d=!0),u.has(S)&&!f&&(E(`data-key="${n}" has duplicate value ${JSON.stringify(S)} \u2014 rows will collide`),f=!0),u.add(S);let y=r.get(S);if(!y)y=Q(e,a,l),r.set(S,y);else if(p&&y.__micraItem===g&&y.__micraIndex===i){h.push(y);continue}y.__micraItem=g,y.__micraIndex=i;let w=y._itemState;w.item=g,w.index=i,w.$index=i;let ne=(c=y.__micraScan)!=null?c:y.__micraScan=R(y);x(ne,w,m),h.push(y)}for(let[i,g]of r)u.has(i)||(g.remove(),r.delete(i));let s=e.__micraList;if(s.length===0){if(h.length){let i=document.createDocumentFragment();for(let g of h)i.append(g);o.after(i)}}else{let i=h.length!==s.length;if(!i){for(let g=0;g<h.length;g++)if(h[g]!==s[g]){i=!0;break}}i&&Ie(h,s,o)}e.__micraList=h}function Ie(e,t,n){let o=new Map;for(let d=0;d<t.length;d++)o.set(t[d],d);let r=e.length,a=[],m=[],l=new Array(r).fill(-1);for(let d=0;d<r;d++){let f=o.get(e[d]);if(f===void 0)continue;let s=0,c=a.length;for(;s<c;){let i=s+c>>1;a[i]<f?s=i+1:c=i}s>0&&(l[d]=m[s-1]),a[s]=f,m[s]=d}let p=new Set,u=m[a.length-1];for(;u>=0;)p.add(u),u=l[u];let h=n;for(let d=0;d<r;d++){let f=e[d];if(p.has(d)){h=f;continue}h.after(f),h=f}}function Ce(e,t,n,o,r,a,m){let l=e.__micraList,p=l.length,u=t.length,h=u<p?u:p,d=new Array(u);for(let f=0;f<h;f++){let s=l[f],c=t[f];if(m&&s.__micraItem===c&&s.__micraIndex===f){d[f]=s;continue}s.__micraItem=c,s.__micraIndex=f;let i=s._itemState;i.item=c,i.index=f,i.$index=f,x(s.__micraScan,i,r),d[f]=s}for(let f=u;f<p;f++)l[f].remove();if(u>p){let f=document.createDocumentFragment();for(let c=p;c<u;c++){let i=Q(e,o,a),g=t[c],S=i._itemState;S.item=g,S.index=c,S.$index=c,i.__micraItem=g,i.__micraIndex=c,x(i.__micraScan,S,r),d[c]=i,f.append(i)}(p>0?d[p-1]:n).after(f)}e.__micraList=d}function ee(e,t){if(e.length){t.refs={};for(let n of e){let o=n.dataset.ref;o&&(t.refs[o]=n)}}}function P(e,t){var f;let n=typeof e=="string"?document.querySelector(e):e;if(!n)return E(`"${e}" not found`),null;if(_.has(n))return _.get(n);let o={...(f=t.state)!=null?f:{}},r={$el:n,refs:{}};for(let[s,c]of Object.entries(t))s==="state"||s==="onCreate"||s==="onDestroy"||typeof c=="function"&&(r[s]=c);r.prop=function(s,c){let i=n.dataset[s];return i===void 0?c:i==="true"?!0:i==="false"?!1:i!==""&&!isNaN(Number(i))?Number(i):i},r.fetch=D,r.emit=T,r.on=(s,c)=>{let i=C(s,c);return r.__micraSubs||(r.__micraSubs=[]),r.__micraSubs.push(i),i};let a=!1,m=null,l=Z(()=>r.render());r.state=J(o,l,s=>{m===null?m=s:m!==s&&(m="MULTIPLE")});let p=new Map,u=new Proxy(o,{get(s,c){if(Object.prototype.hasOwnProperty.call(s,c))return s[c];if(Object.prototype.hasOwnProperty.call(r,c)&&typeof r[c]=="function"){let i=p.get(c);if(i)return i;let g=r[c].bind(r);return p.set(c,g),g}},has(s,c){return typeof c!="string"?!1:Object.prototype.hasOwnProperty.call(s,c)?!0:Object.prototype.hasOwnProperty.call(r,c)&&typeof r[c]=="function"}}),h=!1;r.render=function(){var c;if(r.__micraDestroyed)return;let s=m;if(m=null,a){h||(E("render() re-entry detected \u2014 mutation inside a directive expression is ignored. Move state writes to a method."),h=!0);return}a=!0;try{let i=n,g=(c=i.__micraScan)!=null?c:i.__micraScan=R(n);x(g,u,o),Y(g.each,u,o,r,s),L(g.on,r),A(g.atEvents,r),N(g.model,r),ee(g.refs,r)}finally{a=!1}},r.destroy=function(){var c,i;if(r.__micraDestroyed)return;r.__micraDestroyed=!0,(c=r.__micraListeners)==null||c.forEach(({el:g,type:S,fn:y})=>g.removeEventListener(S,y)),r.__micraListeners=[];let s=g=>{let S=g;delete S.__micraEvents,delete S.__micraAtBound,delete S.__micraModel,delete S.__micraScan};s(n),n.querySelectorAll("*").forEach(s),(i=r.__micraSubs)==null||i.forEach(g=>g()),r.__micraSubs=[],typeof t.onDestroy=="function"&&t.onDestroy.call(r),_.delete(n)},_.set(n,r),r.render();let d=n;return d.__micraScan&&G(d.__micraScan),typeof t.onCreate=="function"&&Promise.resolve().then(()=>t.onCreate.call(r)),r}function te(e=document){e.querySelectorAll("[data-component]").forEach(t=>{if(_.has(t))return;let n=t.getAttribute("data-component"),o=I.get(n);if(!o){E(`component "${n}" not defined. Call Micra.define('${n}', {...}) first.`);return}P(t,o)})}return ce(Te);})();
|
package/dist/types.d.ts
CHANGED
|
@@ -165,8 +165,6 @@ export interface MicraElement extends HTMLElement {
|
|
|
165
165
|
__micraModel?: true;
|
|
166
166
|
__micraEvents?: true;
|
|
167
167
|
__micraAtBound?: true;
|
|
168
|
-
__micraKey?: unknown;
|
|
169
|
-
__micraEach?: true;
|
|
170
168
|
__micraScan?: ScanIndex;
|
|
171
169
|
__micraItem?: StateRecord;
|
|
172
170
|
__micraIndex?: number;
|