@hyperfixi/intent-element 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @hyperfixi/intent-element
2
+
3
+ The `<lse-intent>` custom element for [hyperfixi](https://github.com/codetalcott/hyperfixi) — a declarative way to run **LSE protocol JSON** (LokaScript Explicit Syntax) from HTML. The element validates the JSON against the LSE schema, then executes it through the hyperfixi runtime.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @hyperfixi/intent-element
9
+ ```
10
+
11
+ `@hyperfixi/core` is an optional peer dependency: validation works without it, but executing an intent needs the runtime (`window.hyperfixi.evalLSENode`).
12
+
13
+ ## Usage
14
+
15
+ The browser build auto-registers `<lse-intent>`:
16
+
17
+ ```html
18
+ <script src="hyperfixi.js"></script>
19
+ <script src="intent-element.iife.global.js"></script>
20
+
21
+ <!-- Inline LSE JSON -->
22
+ <lse-intent trigger="click">
23
+ <script type="application/lse+json">
24
+ {
25
+ "action": "toggle",
26
+ "roles": { "patient": { "type": "selector", "value": ".active" } }
27
+ }
28
+ </script>
29
+ <button>Toggle</button>
30
+ </lse-intent>
31
+
32
+ <!-- …or fetch the intent from a URL -->
33
+ <lse-intent src="/intents/toggle.json"></lse-intent>
34
+ ```
35
+
36
+ The intent is read from a child `<script type="application/lse+json">` or fetched via the `src` attribute. The optional `trigger` attribute names the DOM event that runs the intent (default: run on connect).
37
+
38
+ ## Why a custom element
39
+
40
+ - **Declarative.** Behavior travels as data — LSE JSON — not imperative code. It can be authored, stored, and served like any other content.
41
+ - **Validated.** The JSON is checked against the LSE schema (via the zero-dependency `@lokascript/intent` package) before anything executes; malformed intents fail loudly instead of silently misbehaving.
42
+ - **Sandboxed.** Execution runs through a bounded sandbox with a timeout, so a runaway intent can't lock the page.
43
+ - **Composable.** Because the element accepts `src="/intents/foo.json"`, intents can live in static files or come from an API.
44
+
45
+ ## API exports
46
+
47
+ For programmatic use (e.g. registering the element yourself, or custom schemas):
48
+
49
+ - `LSEIntentElement` — the `HTMLElement` subclass (already defined as `lse-intent` by the browser build).
50
+ - `intentRegistry` — the LSE command-schema registry.
51
+ - Type: `SandboxResult`.
52
+
53
+ The IIFE build exposes these on `window.HyperFixiIntentElement`.
54
+
55
+ ## License
56
+
57
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,489 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+
23
+ // src/schema-registry.ts
24
+ var IntentSchemaRegistry, intentRegistry;
25
+ var init_schema_registry = __esm({
26
+ "src/schema-registry.ts"() {
27
+ "use strict";
28
+ IntentSchemaRegistry = class {
29
+ constructor() {
30
+ this.schemas = /* @__PURE__ */ new Map();
31
+ }
32
+ /** Register a command schema. Overwrites any existing schema for the same action. */
33
+ register(schema) {
34
+ this.schemas.set(schema.action, schema);
35
+ }
36
+ /** Register multiple schemas at once. */
37
+ registerAll(schemas) {
38
+ for (const schema of schemas) this.register(schema);
39
+ }
40
+ /** Look up a schema by action name. Returns undefined if not registered. */
41
+ get(action) {
42
+ return this.schemas.get(action);
43
+ }
44
+ /** Check whether a schema is registered for the given action. */
45
+ has(action) {
46
+ return this.schemas.has(action);
47
+ }
48
+ /** Remove a schema. */
49
+ unregister(action) {
50
+ this.schemas.delete(action);
51
+ }
52
+ /** Clear all registered schemas. */
53
+ clear() {
54
+ this.schemas.clear();
55
+ }
56
+ /** Number of registered schemas. */
57
+ get size() {
58
+ return this.schemas.size;
59
+ }
60
+ /**
61
+ * Fetch and register schemas from a JSON endpoint.
62
+ * The endpoint must return an array of CommandSchema objects.
63
+ * Throws if the response is not OK, not an array, or contains malformed schemas.
64
+ */
65
+ async loadFrom(url) {
66
+ const response = await fetch(url);
67
+ if (!response.ok) {
68
+ throw new Error(
69
+ `Failed to load schemas from ${url}: ${response.status} ${response.statusText}`
70
+ );
71
+ }
72
+ const body = await response.json();
73
+ if (!Array.isArray(body)) {
74
+ throw new Error(`Expected array of schemas from ${url}, got ${typeof body}`);
75
+ }
76
+ for (let i = 0; i < body.length; i++) {
77
+ const s = body[i];
78
+ if (typeof s?.action !== "string" || !Array.isArray(s?.roles)) {
79
+ throw new Error(
80
+ `Schema at index ${i} from ${url} is missing required fields "action" (string) and "roles" (array)`
81
+ );
82
+ }
83
+ }
84
+ this.registerAll(body);
85
+ }
86
+ };
87
+ intentRegistry = new IntentSchemaRegistry();
88
+ }
89
+ });
90
+
91
+ // src/sandbox.ts
92
+ async function sandboxed(fn, timeoutMs = DEFAULT_TIMEOUT_MS) {
93
+ let timedOut = false;
94
+ let timeoutHandle;
95
+ const timeoutPromise = new Promise((_, reject) => {
96
+ timeoutHandle = setTimeout(() => {
97
+ timedOut = true;
98
+ reject(new Error(`LSE execution timed out after ${timeoutMs}ms`));
99
+ }, timeoutMs);
100
+ });
101
+ try {
102
+ const result = await Promise.race([fn(), timeoutPromise]);
103
+ clearTimeout(timeoutHandle);
104
+ return { ok: true, result };
105
+ } catch (err) {
106
+ clearTimeout(timeoutHandle);
107
+ const error = err instanceof Error ? err : new Error(String(err));
108
+ return { ok: false, error, timedOut };
109
+ }
110
+ }
111
+ var DEFAULT_TIMEOUT_MS;
112
+ var init_sandbox = __esm({
113
+ "src/sandbox.ts"() {
114
+ "use strict";
115
+ DEFAULT_TIMEOUT_MS = 5e3;
116
+ }
117
+ });
118
+
119
+ // src/lse-intent.ts
120
+ var lse_intent_exports = {};
121
+ __export(lse_intent_exports, {
122
+ LSEIntentElement: () => LSEIntentElement
123
+ });
124
+ function getRuntime() {
125
+ const w = globalThis;
126
+ const api = w["hyperfixi"];
127
+ if (api && typeof api["evalLSENode"] === "function") {
128
+ return api;
129
+ }
130
+ return null;
131
+ }
132
+ function parseTriggerValue(value) {
133
+ const v = value.trim().toLowerCase();
134
+ if (v === "" || v === "load") return { kind: "load" };
135
+ if (v === "manual") return { kind: "manual" };
136
+ if (v === "intersect") return { kind: "intersect" };
137
+ if (v === "submit") return { kind: "submit" };
138
+ return { kind: "event", eventName: v };
139
+ }
140
+ var import_intent, LSEIntentElement;
141
+ var init_lse_intent = __esm({
142
+ "src/lse-intent.ts"() {
143
+ "use strict";
144
+ import_intent = require("@lokascript/intent");
145
+ init_schema_registry();
146
+ init_sandbox();
147
+ LSEIntentElement = class extends HTMLElement {
148
+ constructor() {
149
+ super(...arguments);
150
+ this._node = null;
151
+ this._diagnostics = [];
152
+ this._abortController = null;
153
+ this._triggerCleanup = null;
154
+ this._initInFlight = false;
155
+ this._initPending = false;
156
+ }
157
+ // ── Lifecycle ──────────────────────────────────────────────────────────────
158
+ connectedCallback() {
159
+ void this._initialize();
160
+ }
161
+ disconnectedCallback() {
162
+ this._abortController?.abort();
163
+ this._abortController = null;
164
+ this._tearDownTrigger();
165
+ }
166
+ attributeChangedCallback(name, _old, _new) {
167
+ if (name !== "src" && name !== "disabled" && name !== "trigger") return;
168
+ if (!this.isConnected) return;
169
+ void this._initialize();
170
+ }
171
+ // ── Public accessors ───────────────────────────────────────────────────────
172
+ /** The parsed SemanticNode, or null if not yet loaded / invalid. */
173
+ get node() {
174
+ return this._node;
175
+ }
176
+ /** Diagnostics from the last validation attempt. Returns a snapshot copy. */
177
+ get diagnostics() {
178
+ return [...this._diagnostics];
179
+ }
180
+ /**
181
+ * Re-run preparation and execution explicitly. Useful after updating the
182
+ * inline JSON, and required when `trigger="manual"`. Always executes
183
+ * regardless of trigger mode.
184
+ */
185
+ async refresh() {
186
+ const prepared = await this._prepare();
187
+ if (!prepared) return;
188
+ await this._execute(prepared.node);
189
+ }
190
+ // ── Private: initialization and trigger dispatch ───────────────────────────
191
+ /**
192
+ * Serialize concurrent `_initialize` calls. If one is already running,
193
+ * mark a re-init as pending and return. The in-flight init will re-run
194
+ * once after it finishes so the final attribute/JSON state is reflected.
195
+ */
196
+ async _initialize() {
197
+ if (this._initInFlight) {
198
+ this._initPending = true;
199
+ return;
200
+ }
201
+ this._initInFlight = true;
202
+ try {
203
+ await this._doInitialize();
204
+ while (this._initPending) {
205
+ this._initPending = false;
206
+ await this._doInitialize();
207
+ }
208
+ } finally {
209
+ this._initInFlight = false;
210
+ }
211
+ }
212
+ async _doInitialize() {
213
+ this._tearDownTrigger();
214
+ if (this.hasAttribute("disabled")) return;
215
+ const prepared = await this._prepare();
216
+ if (!prepared) return;
217
+ if (!this.isConnected) return;
218
+ const spec = this._resolveTriggerSpec(prepared.raw);
219
+ switch (spec.kind) {
220
+ case "load":
221
+ await this._execute(prepared.node);
222
+ return;
223
+ case "manual":
224
+ return;
225
+ case "intersect":
226
+ this._wireIntersect(prepared.node);
227
+ return;
228
+ case "submit":
229
+ this._wireSubmit(prepared.node);
230
+ return;
231
+ case "event":
232
+ this._wireEvent(prepared.node, spec.eventName);
233
+ return;
234
+ }
235
+ }
236
+ _resolveTriggerSpec(raw) {
237
+ const attr = this.getAttribute("trigger");
238
+ if (attr !== null) {
239
+ return parseTriggerValue(attr);
240
+ }
241
+ const triggerObj = raw["trigger"];
242
+ if (triggerObj && typeof triggerObj === "object" && !Array.isArray(triggerObj)) {
243
+ const eventName = triggerObj["event"];
244
+ if (typeof eventName === "string" && eventName.length > 0) {
245
+ return parseTriggerValue(eventName);
246
+ }
247
+ }
248
+ return { kind: "load" };
249
+ }
250
+ // ── Private: trigger wiring ────────────────────────────────────────────────
251
+ _tearDownTrigger() {
252
+ if (this._triggerCleanup) {
253
+ this._triggerCleanup();
254
+ this._triggerCleanup = null;
255
+ }
256
+ }
257
+ _wireEvent(node, eventName) {
258
+ const handler = (_ev) => {
259
+ void this._execute(node);
260
+ };
261
+ this.addEventListener(eventName, handler);
262
+ this._triggerCleanup = () => this.removeEventListener(eventName, handler);
263
+ }
264
+ _wireSubmit(node) {
265
+ const form = this.closest("form");
266
+ if (!form) {
267
+ this._diagnostics.push({
268
+ severity: "warning",
269
+ code: "NO_ANCESTOR_FORM",
270
+ message: 'trigger="submit" requires an ancestor <form>; none found.'
271
+ });
272
+ return;
273
+ }
274
+ const handler = (ev) => {
275
+ ev.preventDefault();
276
+ void this._execute(node);
277
+ };
278
+ form.addEventListener("submit", handler);
279
+ this._triggerCleanup = () => form.removeEventListener("submit", handler);
280
+ }
281
+ _wireIntersect(node) {
282
+ if (typeof IntersectionObserver === "undefined") {
283
+ this._diagnostics.push({
284
+ severity: "warning",
285
+ code: "NO_INTERSECTION_OBSERVER",
286
+ message: 'trigger="intersect" requires IntersectionObserver; not available in this environment.'
287
+ });
288
+ return;
289
+ }
290
+ const observer = new IntersectionObserver((entries) => {
291
+ for (const entry of entries) {
292
+ if (entry.isIntersecting) {
293
+ observer.disconnect();
294
+ void this._execute(node);
295
+ return;
296
+ }
297
+ }
298
+ });
299
+ observer.observe(this);
300
+ this._triggerCleanup = () => observer.disconnect();
301
+ }
302
+ // ── Private: prepare + execute ─────────────────────────────────────────────
303
+ /**
304
+ * Read the JSON, validate, deserialize, and emit `lse:validated`. Returns
305
+ * the prepared node and raw JSON on success, or null on failure (in which
306
+ * case `lse:error` has already been emitted).
307
+ */
308
+ async _prepare() {
309
+ const raw = await this._readJSON();
310
+ if (raw === null) return null;
311
+ const wireDiags = (0, import_intent.validateProtocolJSON)(raw);
312
+ const hasErrors = wireDiags.some((d) => d.severity === "error");
313
+ if (hasErrors) {
314
+ this._diagnostics = wireDiags;
315
+ this._node = null;
316
+ this._showError();
317
+ this._emit("lse:error", { diagnostics: wireDiags, error: null });
318
+ return null;
319
+ }
320
+ let node;
321
+ try {
322
+ node = (0, import_intent.fromProtocolJSON)(raw);
323
+ } catch (err) {
324
+ const error = err instanceof Error ? err : new Error(String(err));
325
+ this._diagnostics = [
326
+ { severity: "error", code: "DESERIALIZE_ERROR", message: error.message }
327
+ ];
328
+ this._node = null;
329
+ this._showError();
330
+ this._emit("lse:error", { diagnostics: this._diagnostics, error });
331
+ return null;
332
+ }
333
+ const schema = intentRegistry.get(node.action);
334
+ const schemaDiags = schema ? this._validateSchema(node, schema) : [];
335
+ this._node = node;
336
+ this._diagnostics = [...wireDiags, ...schemaDiags];
337
+ this._emit("lse:validated", { node, diagnostics: [...this._diagnostics] });
338
+ return { node, raw };
339
+ }
340
+ /**
341
+ * Execute a prepared node via the hyperfixi runtime. Emits `lse:executed`
342
+ * on success or `lse:error` on failure. Safe to call multiple times.
343
+ *
344
+ * **Event-handler unwrap.** If the prepared node is an event-handler (from
345
+ * either verbose wire format or compact `trigger` sugar), the element's
346
+ * own `trigger` attribute has already wired the DOM event listener — the
347
+ * wire-format event metadata is redundant at this point. We unwrap the
348
+ * event-handler and execute each body command directly, rather than passing
349
+ * the event-handler node to `evalLSENode` (which would attempt to re-wire
350
+ * a listener at runtime, effectively discarding the body).
351
+ */
352
+ async _execute(node) {
353
+ const runtime = getRuntime();
354
+ if (!runtime) {
355
+ this._diagnostics.push({
356
+ severity: "warning",
357
+ code: "NO_RUNTIME",
358
+ message: "hyperfixi runtime not found. Load hyperfixi.js before intent-element.iife.js to enable execution."
359
+ });
360
+ return;
361
+ }
362
+ const executables = node.kind === "event-handler" ? [...node.body ?? []] : [node];
363
+ const rawTimeout = this.getAttribute("timeout");
364
+ const timeoutMs = rawTimeout !== null ? parseInt(rawTimeout, 10) || 5e3 : 5e3;
365
+ const results = [];
366
+ for (const cmd of executables) {
367
+ const result = await sandboxed(() => runtime.evalLSENode(cmd, this), timeoutMs);
368
+ if (result.ok) {
369
+ results.push(result.result);
370
+ } else {
371
+ this._diagnostics.push({
372
+ severity: "error",
373
+ code: result.timedOut ? "EXECUTION_TIMEOUT" : "EXECUTION_ERROR",
374
+ message: result.error?.message ?? "Unknown execution error"
375
+ });
376
+ this._showError();
377
+ this._emit("lse:error", { diagnostics: [...this._diagnostics], error: result.error });
378
+ return;
379
+ }
380
+ }
381
+ this._emit("lse:executed", {
382
+ node,
383
+ // For single-body cases keep the legacy `result` shape (first/only value);
384
+ // for multi-body cases expose the full array via `results` too.
385
+ result: results.length === 1 ? results[0] : results,
386
+ results
387
+ });
388
+ }
389
+ async _readJSON() {
390
+ const src = this.getAttribute("src");
391
+ if (src) {
392
+ this._abortController?.abort();
393
+ this._abortController = new AbortController();
394
+ try {
395
+ const response = await fetch(src, { signal: this._abortController.signal });
396
+ if (!response.ok) {
397
+ this._emitFetchError(src, response.status);
398
+ return null;
399
+ }
400
+ return await response.json();
401
+ } catch (err) {
402
+ if (err instanceof Error && err.name === "AbortError") return null;
403
+ this._emitFetchError(src, 0, err instanceof Error ? err : void 0);
404
+ return null;
405
+ }
406
+ }
407
+ const script = this.querySelector('script[type="application/lse+json"]');
408
+ if (script) {
409
+ try {
410
+ return JSON.parse(script.textContent ?? "");
411
+ } catch {
412
+ const diag = {
413
+ severity: "error",
414
+ code: "INVALID_JSON",
415
+ message: "Inline JSON is not valid JSON"
416
+ };
417
+ this._diagnostics = [diag];
418
+ this._showError();
419
+ this._emit("lse:error", { diagnostics: [diag], error: null });
420
+ return null;
421
+ }
422
+ }
423
+ return null;
424
+ }
425
+ _validateSchema(node, schema) {
426
+ const diags = [];
427
+ for (const roleSpec of schema.roles) {
428
+ const value = node.roles.get(roleSpec.role);
429
+ if (roleSpec.required && !value) {
430
+ diags.push({
431
+ severity: "error",
432
+ code: "MISSING_REQUIRED_ROLE",
433
+ message: `Required role "${roleSpec.role}" is missing for command "${node.action}"`
434
+ });
435
+ continue;
436
+ }
437
+ if (value && roleSpec.expectedTypes && roleSpec.expectedTypes.length > 0) {
438
+ if (!roleSpec.expectedTypes.includes(value.type)) {
439
+ diags.push({
440
+ severity: "warning",
441
+ code: "UNEXPECTED_ROLE_TYPE",
442
+ message: `Role "${roleSpec.role}" has type "${value.type}" but expected one of: ${roleSpec.expectedTypes.join(", ")}`
443
+ });
444
+ }
445
+ }
446
+ }
447
+ return diags;
448
+ }
449
+ _showError() {
450
+ const errorSlot = this.querySelector('[slot="error"]');
451
+ if (errorSlot instanceof HTMLElement) {
452
+ errorSlot.style.display = "";
453
+ }
454
+ }
455
+ _emit(type, detail) {
456
+ this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));
457
+ }
458
+ _emitFetchError(src, status, error) {
459
+ const message = status ? `Failed to fetch ${src}: HTTP ${status}` : `Failed to fetch ${src}${error ? ": " + error.message : ""}`;
460
+ const diag = { severity: "error", code: "FETCH_ERROR", message };
461
+ this._diagnostics = [diag];
462
+ this._showError();
463
+ this._emit("lse:error", { diagnostics: [diag], error: error ?? null });
464
+ }
465
+ };
466
+ LSEIntentElement.observedAttributes = ["src", "disabled", "timeout", "trigger"];
467
+ }
468
+ });
469
+
470
+ // src/index.ts
471
+ var index_exports = {};
472
+ __export(index_exports, {
473
+ LSEIntentElement: () => LSEIntentElement,
474
+ intentRegistry: () => intentRegistry
475
+ });
476
+ module.exports = __toCommonJS(index_exports);
477
+ init_lse_intent();
478
+ init_schema_registry();
479
+ if (typeof customElements !== "undefined" && !customElements.get("lse-intent")) {
480
+ Promise.resolve().then(() => (init_lse_intent(), lse_intent_exports)).then(({ LSEIntentElement: LSEIntentElement2 }) => {
481
+ customElements.define("lse-intent", LSEIntentElement2);
482
+ });
483
+ }
484
+ // Annotate the CommonJS export names for ESM import in node:
485
+ 0 && (module.exports = {
486
+ LSEIntentElement,
487
+ intentRegistry
488
+ });
489
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema-registry.ts","../src/sandbox.ts","../src/lse-intent.ts","../src/index.ts"],"sourcesContent":["/**\n * Client-side schema registry for <lse-intent>.\n *\n * Schemas registered here are used to validate protocol JSON before execution.\n * Register once per page; the registry is a singleton per module.\n */\n\nimport type { CommandSchema } from '@lokascript/intent';\n\nclass IntentSchemaRegistry {\n private schemas = new Map<string, CommandSchema>();\n\n /** Register a command schema. Overwrites any existing schema for the same action. */\n register(schema: CommandSchema): void {\n this.schemas.set(schema.action, schema);\n }\n\n /** Register multiple schemas at once. */\n registerAll(schemas: CommandSchema[]): void {\n for (const schema of schemas) this.register(schema);\n }\n\n /** Look up a schema by action name. Returns undefined if not registered. */\n get(action: string): CommandSchema | undefined {\n return this.schemas.get(action);\n }\n\n /** Check whether a schema is registered for the given action. */\n has(action: string): boolean {\n return this.schemas.has(action);\n }\n\n /** Remove a schema. */\n unregister(action: string): void {\n this.schemas.delete(action);\n }\n\n /** Clear all registered schemas. */\n clear(): void {\n this.schemas.clear();\n }\n\n /** Number of registered schemas. */\n get size(): number {\n return this.schemas.size;\n }\n\n /**\n * Fetch and register schemas from a JSON endpoint.\n * The endpoint must return an array of CommandSchema objects.\n * Throws if the response is not OK, not an array, or contains malformed schemas.\n */\n async loadFrom(url: string): Promise<void> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to load schemas from ${url}: ${response.status} ${response.statusText}`\n );\n }\n const body: unknown = await response.json();\n if (!Array.isArray(body)) {\n throw new Error(`Expected array of schemas from ${url}, got ${typeof body}`);\n }\n for (let i = 0; i < body.length; i++) {\n const s = body[i] as Record<string, unknown>;\n if (typeof s?.action !== 'string' || !Array.isArray(s?.roles)) {\n throw new Error(\n `Schema at index ${i} from ${url} is missing required fields \"action\" (string) and \"roles\" (array)`\n );\n }\n }\n this.registerAll(body as CommandSchema[]);\n }\n}\n\n/** Singleton registry — shared across all <lse-intent> elements on the page. */\nexport const intentRegistry = new IntentSchemaRegistry();\n","/**\n * Sandboxed execution wrapper for <lse-intent>.\n *\n * Wraps the hyperfixi runtime call with:\n * - try/catch for execution errors\n * - configurable timeout (default 5s)\n * - structured error reporting\n */\n\nexport interface SandboxResult {\n ok: boolean;\n result?: unknown;\n error?: Error;\n timedOut?: boolean;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5000;\n\n/**\n * Execute `fn` with a timeout. If it exceeds `timeoutMs`, the promise\n * rejects with an error whose `timedOut` flag is set.\n */\nexport async function sandboxed(\n fn: () => Promise<unknown>,\n timeoutMs = DEFAULT_TIMEOUT_MS\n): Promise<SandboxResult> {\n let timedOut = false;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n timedOut = true;\n reject(new Error(`LSE execution timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([fn(), timeoutPromise]);\n clearTimeout(timeoutHandle);\n return { ok: true, result };\n } catch (err) {\n clearTimeout(timeoutHandle);\n const error = err instanceof Error ? err : new Error(String(err));\n return { ok: false, error, timedOut };\n }\n}\n","/**\n * <lse-intent> Custom Element\n *\n * Accepts LSE protocol JSON via an inline <script type=\"application/lse+json\">\n * child (or a `src` attribute), validates it, and executes it via the hyperfixi\n * runtime according to a declarative trigger model. Degrades gracefully when\n * the runtime is unavailable.\n *\n * ## Trigger modes\n *\n * The `trigger` attribute controls when validation + execution fires:\n *\n * - `load` (default) — fire immediately on `connectedCallback`\n * - `click` — fire on click anywhere inside the element\n * - `submit` — fire on submit of the closest ancestor `<form>`; preventDefault is called synchronously\n * - `intersect` — fire when the element scrolls into view (IntersectionObserver; one-shot)\n * - `manual` — never fire automatically; caller must invoke `.refresh()`\n * - (any other value) — treated as a DOM event name, wired via addEventListener on the element\n *\n * If no `trigger` attribute is set, the element inspects the JSON wire-format\n * `trigger.event` sugar field (protocol/spec/wire-format.md lines 441–474) and\n * uses it as the event name. If neither is present, defaults to `load`.\n *\n * @example\n * ```html\n * <!-- Load on connect (default) -->\n * <lse-intent>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}}}\n * </script>\n * </lse-intent>\n *\n * <!-- Fire on click -->\n * <lse-intent trigger=\"click\">\n * <button slot=\"trigger\">Toggle</button>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}}}\n * </script>\n * </lse-intent>\n *\n * <!-- Auto-wire from JSON trigger.event sugar -->\n * <lse-intent>\n * <button>Toggle</button>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}},\"trigger\":{\"event\":\"click\"}}\n * </script>\n * </lse-intent>\n *\n * <!-- Manual (caller drives refresh) -->\n * <lse-intent trigger=\"manual\" id=\"my-intent\">...</lse-intent>\n * <script>document.getElementById('my-intent').refresh()</script>\n *\n * <!-- Fetch from URL, fire on intersect -->\n * <lse-intent src=\"/intents/toggle-sidebar.json\" trigger=\"intersect\"></lse-intent>\n * ```\n *\n * Events dispatched on the element:\n * - `lse:validated` — after schema check, before execution (detail: { node, diagnostics })\n * - `lse:executed` — after successful execution (detail: { node, result })\n * - `lse:error` — on validation or execution failure (detail: { diagnostics, error })\n */\n\nimport { fromProtocolJSON, validateProtocolJSON } from '@lokascript/intent';\nimport type { SemanticNode, IRDiagnostic, CommandSchema, SemanticValue } from '@lokascript/intent';\nimport { intentRegistry } from './schema-registry.js';\nimport { sandboxed } from './sandbox.js';\n\n// ---------------------------------------------------------------------------\n// Runtime bridge — resolved lazily so the element works without @hyperfixi/core\n// ---------------------------------------------------------------------------\n\ninterface HyperfiziRuntime {\n evalLSENode(node: SemanticNode, element?: Element): Promise<unknown>;\n}\n\nfunction getRuntime(): HyperfiziRuntime | null {\n const w = globalThis as Record<string, unknown>;\n const api = w['hyperfixi'];\n if (api && typeof (api as Record<string, unknown>)['evalLSENode'] === 'function') {\n return api as HyperfiziRuntime;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Trigger resolution\n// ---------------------------------------------------------------------------\n\ntype TriggerSpec =\n | { kind: 'load' }\n | { kind: 'manual' }\n | { kind: 'intersect' }\n | { kind: 'submit' }\n | { kind: 'event'; eventName: string };\n\nfunction parseTriggerValue(value: string): TriggerSpec {\n const v = value.trim().toLowerCase();\n if (v === '' || v === 'load') return { kind: 'load' };\n if (v === 'manual') return { kind: 'manual' };\n if (v === 'intersect') return { kind: 'intersect' };\n if (v === 'submit') return { kind: 'submit' };\n return { kind: 'event', eventName: v };\n}\n\n// ---------------------------------------------------------------------------\n// <lse-intent>\n// ---------------------------------------------------------------------------\n\ninterface PreparedNode {\n node: SemanticNode;\n raw: Record<string, unknown>;\n}\n\nexport class LSEIntentElement extends HTMLElement {\n static observedAttributes = ['src', 'disabled', 'timeout', 'trigger'];\n\n private _node: SemanticNode | null = null;\n private _diagnostics: IRDiagnostic[] = [];\n private _abortController: AbortController | null = null;\n private _triggerCleanup: (() => void) | null = null;\n private _initInFlight = false;\n private _initPending = false;\n\n // ── Lifecycle ──────────────────────────────────────────────────────────────\n\n connectedCallback(): void {\n void this._initialize();\n }\n\n disconnectedCallback(): void {\n this._abortController?.abort();\n this._abortController = null;\n this._tearDownTrigger();\n }\n\n attributeChangedCallback(name: string, _old: string | null, _new: string | null): void {\n if (name !== 'src' && name !== 'disabled' && name !== 'trigger') return;\n // Only react to attribute changes when connected. Before connect, the\n // connectedCallback will run _initialize once with the final attribute\n // state. This avoids races between setAttribute() calls that happen\n // before the element is appended to the document.\n if (!this.isConnected) return;\n void this._initialize();\n }\n\n // ── Public accessors ───────────────────────────────────────────────────────\n\n /** The parsed SemanticNode, or null if not yet loaded / invalid. */\n get node(): SemanticNode | null {\n return this._node;\n }\n\n /** Diagnostics from the last validation attempt. Returns a snapshot copy. */\n get diagnostics(): readonly IRDiagnostic[] {\n return [...this._diagnostics];\n }\n\n /**\n * Re-run preparation and execution explicitly. Useful after updating the\n * inline JSON, and required when `trigger=\"manual\"`. Always executes\n * regardless of trigger mode.\n */\n async refresh(): Promise<void> {\n const prepared = await this._prepare();\n if (!prepared) return;\n await this._execute(prepared.node);\n }\n\n // ── Private: initialization and trigger dispatch ───────────────────────────\n\n /**\n * Serialize concurrent `_initialize` calls. If one is already running,\n * mark a re-init as pending and return. The in-flight init will re-run\n * once after it finishes so the final attribute/JSON state is reflected.\n */\n private async _initialize(): Promise<void> {\n if (this._initInFlight) {\n this._initPending = true;\n return;\n }\n this._initInFlight = true;\n try {\n await this._doInitialize();\n while (this._initPending) {\n this._initPending = false;\n await this._doInitialize();\n }\n } finally {\n this._initInFlight = false;\n }\n }\n\n private async _doInitialize(): Promise<void> {\n // Always tear down any existing trigger wiring before re-initializing.\n this._tearDownTrigger();\n\n if (this.hasAttribute('disabled')) return;\n\n const prepared = await this._prepare();\n if (!prepared) return;\n\n // The element may have been disconnected while _prepare() was awaiting\n // a fetch or JSON parse. If so, don't wire triggers — disconnectedCallback\n // already ran cleanup.\n if (!this.isConnected) return;\n\n const spec = this._resolveTriggerSpec(prepared.raw);\n\n switch (spec.kind) {\n case 'load':\n await this._execute(prepared.node);\n return;\n case 'manual':\n return;\n case 'intersect':\n this._wireIntersect(prepared.node);\n return;\n case 'submit':\n this._wireSubmit(prepared.node);\n return;\n case 'event':\n this._wireEvent(prepared.node, spec.eventName);\n return;\n }\n }\n\n private _resolveTriggerSpec(raw: Record<string, unknown>): TriggerSpec {\n // 1. Explicit trigger attribute wins.\n const attr = this.getAttribute('trigger');\n if (attr !== null) {\n return parseTriggerValue(attr);\n }\n\n // 2. Wire-format trigger.event sugar in the JSON.\n const triggerObj = raw['trigger'];\n if (triggerObj && typeof triggerObj === 'object' && !Array.isArray(triggerObj)) {\n const eventName = (triggerObj as Record<string, unknown>)['event'];\n if (typeof eventName === 'string' && eventName.length > 0) {\n return parseTriggerValue(eventName);\n }\n }\n\n // 3. Default.\n return { kind: 'load' };\n }\n\n // ── Private: trigger wiring ────────────────────────────────────────────────\n\n private _tearDownTrigger(): void {\n if (this._triggerCleanup) {\n this._triggerCleanup();\n this._triggerCleanup = null;\n }\n }\n\n private _wireEvent(node: SemanticNode, eventName: string): void {\n const handler = (_ev: Event): void => {\n void this._execute(node);\n };\n this.addEventListener(eventName, handler);\n this._triggerCleanup = () => this.removeEventListener(eventName, handler);\n }\n\n private _wireSubmit(node: SemanticNode): void {\n const form = this.closest('form');\n if (!form) {\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_ANCESTOR_FORM',\n message: 'trigger=\"submit\" requires an ancestor <form>; none found.',\n });\n return;\n }\n const handler = (ev: Event): void => {\n // preventDefault synchronously so the form does not navigate while\n // execution is in flight. If execution fails, the diagnostics event\n // still fires but the form stays on the page.\n ev.preventDefault();\n void this._execute(node);\n };\n form.addEventListener('submit', handler);\n this._triggerCleanup = () => form.removeEventListener('submit', handler);\n }\n\n private _wireIntersect(node: SemanticNode): void {\n if (typeof IntersectionObserver === 'undefined') {\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_INTERSECTION_OBSERVER',\n message:\n 'trigger=\"intersect\" requires IntersectionObserver; not available in this environment.',\n });\n return;\n }\n const observer = new IntersectionObserver(entries => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n // One-shot: disconnect after first intersection.\n observer.disconnect();\n void this._execute(node);\n return;\n }\n }\n });\n observer.observe(this);\n this._triggerCleanup = () => observer.disconnect();\n }\n\n // ── Private: prepare + execute ─────────────────────────────────────────────\n\n /**\n * Read the JSON, validate, deserialize, and emit `lse:validated`. Returns\n * the prepared node and raw JSON on success, or null on failure (in which\n * case `lse:error` has already been emitted).\n */\n private async _prepare(): Promise<PreparedNode | null> {\n const raw = await this._readJSON();\n if (raw === null) return null;\n\n // Validate wire format\n const wireDiags = validateProtocolJSON(raw);\n const hasErrors = wireDiags.some(d => d.severity === 'error');\n\n if (hasErrors) {\n this._diagnostics = wireDiags;\n this._node = null;\n this._showError();\n this._emit('lse:error', { diagnostics: wireDiags, error: null });\n return null;\n }\n\n // Deserialize to SemanticNode\n let node: SemanticNode;\n try {\n node = fromProtocolJSON(raw as unknown as Parameters<typeof fromProtocolJSON>[0]);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this._diagnostics = [\n { severity: 'error', code: 'DESERIALIZE_ERROR', message: error.message },\n ];\n this._node = null;\n this._showError();\n this._emit('lse:error', { diagnostics: this._diagnostics, error });\n return null;\n }\n\n // Optional schema validation\n const schema = intentRegistry.get(node.action);\n const schemaDiags: IRDiagnostic[] = schema ? this._validateSchema(node, schema) : [];\n\n this._node = node;\n this._diagnostics = [...wireDiags, ...schemaDiags];\n this._emit('lse:validated', { node, diagnostics: [...this._diagnostics] });\n\n return { node, raw };\n }\n\n /**\n * Execute a prepared node via the hyperfixi runtime. Emits `lse:executed`\n * on success or `lse:error` on failure. Safe to call multiple times.\n *\n * **Event-handler unwrap.** If the prepared node is an event-handler (from\n * either verbose wire format or compact `trigger` sugar), the element's\n * own `trigger` attribute has already wired the DOM event listener — the\n * wire-format event metadata is redundant at this point. We unwrap the\n * event-handler and execute each body command directly, rather than passing\n * the event-handler node to `evalLSENode` (which would attempt to re-wire\n * a listener at runtime, effectively discarding the body).\n */\n private async _execute(node: SemanticNode): Promise<void> {\n const runtime = getRuntime();\n if (!runtime) {\n // Runtime not loaded — emit a warning but don't error.\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_RUNTIME',\n message:\n 'hyperfixi runtime not found. Load hyperfixi.js before intent-element.iife.js to enable execution.',\n });\n return;\n }\n\n // Unwrap event-handler nodes into their body commands. See the comment\n // above and examples/llm-native-todo-demo/README.md for the motivation.\n const executables: SemanticNode[] =\n node.kind === 'event-handler'\n ? [...((node as { body?: SemanticNode[] }).body ?? [])]\n : [node];\n\n const rawTimeout = this.getAttribute('timeout');\n const timeoutMs = rawTimeout !== null ? parseInt(rawTimeout, 10) || 5000 : 5000;\n\n // Execute body commands sequentially. A single failure stops the sequence\n // and emits `lse:error`; all successful commands before the failure have\n // already mutated the DOM.\n const results: unknown[] = [];\n for (const cmd of executables) {\n const result = await sandboxed(() => runtime.evalLSENode(cmd, this), timeoutMs);\n if (result.ok) {\n results.push(result.result);\n } else {\n this._diagnostics.push({\n severity: 'error',\n code: result.timedOut ? 'EXECUTION_TIMEOUT' : 'EXECUTION_ERROR',\n message: result.error?.message ?? 'Unknown execution error',\n });\n this._showError();\n this._emit('lse:error', { diagnostics: [...this._diagnostics], error: result.error });\n return;\n }\n }\n\n this._emit('lse:executed', {\n node,\n // For single-body cases keep the legacy `result` shape (first/only value);\n // for multi-body cases expose the full array via `results` too.\n result: results.length === 1 ? results[0] : results,\n results,\n });\n }\n\n private async _readJSON(): Promise<Record<string, unknown> | null> {\n // 1. src attribute — fetch remote JSON\n const src = this.getAttribute('src');\n if (src) {\n this._abortController?.abort();\n this._abortController = new AbortController();\n try {\n const response = await fetch(src, { signal: this._abortController.signal });\n if (!response.ok) {\n this._emitFetchError(src, response.status);\n return null;\n }\n return (await response.json()) as Record<string, unknown>;\n } catch (err) {\n // AbortError means the element was disconnected — silently ignore\n if (err instanceof Error && err.name === 'AbortError') return null;\n this._emitFetchError(src, 0, err instanceof Error ? err : undefined);\n return null;\n }\n }\n\n // 2. Inline <script type=\"application/lse+json\"> child\n const script = this.querySelector('script[type=\"application/lse+json\"]');\n if (script) {\n try {\n return JSON.parse(script.textContent ?? '') as Record<string, unknown>;\n } catch {\n const diag: IRDiagnostic = {\n severity: 'error',\n code: 'INVALID_JSON',\n message: 'Inline JSON is not valid JSON',\n };\n this._diagnostics = [diag];\n this._showError();\n this._emit('lse:error', { diagnostics: [diag], error: null });\n return null;\n }\n }\n\n return null;\n }\n\n private _validateSchema(node: SemanticNode, schema: CommandSchema): IRDiagnostic[] {\n const diags: IRDiagnostic[] = [];\n for (const roleSpec of schema.roles) {\n const value = node.roles.get(roleSpec.role) as SemanticValue | undefined;\n\n if (roleSpec.required && !value) {\n diags.push({\n severity: 'error',\n code: 'MISSING_REQUIRED_ROLE',\n message: `Required role \"${roleSpec.role}\" is missing for command \"${node.action}\"`,\n });\n continue;\n }\n\n if (value && roleSpec.expectedTypes && roleSpec.expectedTypes.length > 0) {\n if (\n !roleSpec.expectedTypes.includes(value.type as (typeof roleSpec.expectedTypes)[number])\n ) {\n diags.push({\n severity: 'warning',\n code: 'UNEXPECTED_ROLE_TYPE',\n message: `Role \"${roleSpec.role}\" has type \"${value.type}\" but expected one of: ${roleSpec.expectedTypes.join(', ')}`,\n });\n }\n }\n }\n return diags;\n }\n\n private _showError(): void {\n const errorSlot = this.querySelector('[slot=\"error\"]');\n if (errorSlot instanceof HTMLElement) {\n errorSlot.style.display = '';\n }\n }\n\n private _emit(type: string, detail: Record<string, unknown>): void {\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));\n }\n\n private _emitFetchError(src: string, status: number, error?: Error): void {\n const message = status\n ? `Failed to fetch ${src}: HTTP ${status}`\n : `Failed to fetch ${src}${error ? ': ' + error.message : ''}`;\n const diag: IRDiagnostic = { severity: 'error', code: 'FETCH_ERROR', message };\n this._diagnostics = [diag];\n this._showError();\n this._emit('lse:error', { diagnostics: [diag], error: error ?? null });\n }\n}\n","/**\n * @hyperfixi/intent-element\n *\n * Browser custom element that validates LSE protocol JSON and executes it\n * via the hyperfixi runtime. Zero-dependency validation via @lokascript/intent;\n * execution delegated to window.hyperfixi.evalLSENode (peer dep).\n *\n * Auto-registers <lse-intent> when loaded as a browser script.\n *\n * @example\n * ```html\n * <script src=\"hyperfixi.js\"></script>\n * <script src=\"intent-element.iife.global.js\"></script>\n *\n * <lse-intent>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}},\"trigger\":{\"event\":\"click\"}}\n * </script>\n * <button slot=\"trigger\">Toggle sidebar</button>\n * </lse-intent>\n * ```\n */\n\nexport { LSEIntentElement } from './lse-intent.js';\nexport { intentRegistry } from './schema-registry.js';\nexport type { SandboxResult } from './sandbox.js';\n\n// Auto-register the custom element when loaded in a browser context\nif (typeof customElements !== 'undefined' && !customElements.get('lse-intent')) {\n // Dynamic import to avoid circular reference from the export above\n import('./lse-intent.js').then(({ LSEIntentElement }) => {\n customElements.define('lse-intent', LSEIntentElement);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,IASM,sBAmEO;AA5Eb;AAAA;AAAA;AASA,IAAM,uBAAN,MAA2B;AAAA,MAA3B;AACE,aAAQ,UAAU,oBAAI,IAA2B;AAAA;AAAA;AAAA,MAGjD,SAAS,QAA6B;AACpC,aAAK,QAAQ,IAAI,OAAO,QAAQ,MAAM;AAAA,MACxC;AAAA;AAAA,MAGA,YAAY,SAAgC;AAC1C,mBAAW,UAAU,QAAS,MAAK,SAAS,MAAM;AAAA,MACpD;AAAA;AAAA,MAGA,IAAI,QAA2C;AAC7C,eAAO,KAAK,QAAQ,IAAI,MAAM;AAAA,MAChC;AAAA;AAAA,MAGA,IAAI,QAAyB;AAC3B,eAAO,KAAK,QAAQ,IAAI,MAAM;AAAA,MAChC;AAAA;AAAA,MAGA,WAAW,QAAsB;AAC/B,aAAK,QAAQ,OAAO,MAAM;AAAA,MAC5B;AAAA;AAAA,MAGA,QAAc;AACZ,aAAK,QAAQ,MAAM;AAAA,MACrB;AAAA;AAAA,MAGA,IAAI,OAAe;AACjB,eAAO,KAAK,QAAQ;AAAA,MACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,MAAM,SAAS,KAA4B;AACzC,cAAM,WAAW,MAAM,MAAM,GAAG;AAChC,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,IAAI;AAAA,YACR,+BAA+B,GAAG,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,UAC/E;AAAA,QACF;AACA,cAAM,OAAgB,MAAM,SAAS,KAAK;AAC1C,YAAI,CAAC,MAAM,QAAQ,IAAI,GAAG;AACxB,gBAAM,IAAI,MAAM,kCAAkC,GAAG,SAAS,OAAO,IAAI,EAAE;AAAA,QAC7E;AACA,iBAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,gBAAM,IAAI,KAAK,CAAC;AAChB,cAAI,OAAO,GAAG,WAAW,YAAY,CAAC,MAAM,QAAQ,GAAG,KAAK,GAAG;AAC7D,kBAAM,IAAI;AAAA,cACR,mBAAmB,CAAC,SAAS,GAAG;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AACA,aAAK,YAAY,IAAuB;AAAA,MAC1C;AAAA,IACF;AAGO,IAAM,iBAAiB,IAAI,qBAAqB;AAAA;AAAA;;;ACtDvD,eAAsB,UACpB,IACA,YAAY,oBACY;AACxB,MAAI,WAAW;AACf,MAAI;AAEJ,QAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD,oBAAgB,WAAW,MAAM;AAC/B,iBAAW;AACX,aAAO,IAAI,MAAM,iCAAiC,SAAS,IAAI,CAAC;AAAA,IAClE,GAAG,SAAS;AAAA,EACd,CAAC;AAED,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,GAAG,GAAG,cAAc,CAAC;AACxD,iBAAa,aAAa;AAC1B,WAAO,EAAE,IAAI,MAAM,OAAO;AAAA,EAC5B,SAAS,KAAK;AACZ,iBAAa,aAAa;AAC1B,UAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,WAAO,EAAE,IAAI,OAAO,OAAO,SAAS;AAAA,EACtC;AACF;AA7CA,IAgBM;AAhBN;AAAA;AAAA;AAgBA,IAAM,qBAAqB;AAAA;AAAA;;;AChB3B;AAAA;AAAA;AAAA;AA2EA,SAAS,aAAsC;AAC7C,QAAM,IAAI;AACV,QAAM,MAAM,EAAE,WAAW;AACzB,MAAI,OAAO,OAAQ,IAAgC,aAAa,MAAM,YAAY;AAChF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAaA,SAAS,kBAAkB,OAA4B;AACrD,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,MAAI,MAAM,MAAM,MAAM,OAAQ,QAAO,EAAE,MAAM,OAAO;AACpD,MAAI,MAAM,SAAU,QAAO,EAAE,MAAM,SAAS;AAC5C,MAAI,MAAM,YAAa,QAAO,EAAE,MAAM,YAAY;AAClD,MAAI,MAAM,SAAU,QAAO,EAAE,MAAM,SAAS;AAC5C,SAAO,EAAE,MAAM,SAAS,WAAW,EAAE;AACvC;AAtGA,IA8DA,eAmDa;AAjHb;AAAA;AAAA;AA8DA,oBAAuD;AAEvD;AACA;AAgDO,IAAM,mBAAN,cAA+B,YAAY;AAAA,MAA3C;AAAA;AAGL,aAAQ,QAA6B;AACrC,aAAQ,eAA+B,CAAC;AACxC,aAAQ,mBAA2C;AACnD,aAAQ,kBAAuC;AAC/C,aAAQ,gBAAgB;AACxB,aAAQ,eAAe;AAAA;AAAA;AAAA,MAIvB,oBAA0B;AACxB,aAAK,KAAK,YAAY;AAAA,MACxB;AAAA,MAEA,uBAA6B;AAC3B,aAAK,kBAAkB,MAAM;AAC7B,aAAK,mBAAmB;AACxB,aAAK,iBAAiB;AAAA,MACxB;AAAA,MAEA,yBAAyB,MAAc,MAAqB,MAA2B;AACrF,YAAI,SAAS,SAAS,SAAS,cAAc,SAAS,UAAW;AAKjE,YAAI,CAAC,KAAK,YAAa;AACvB,aAAK,KAAK,YAAY;AAAA,MACxB;AAAA;AAAA;AAAA,MAKA,IAAI,OAA4B;AAC9B,eAAO,KAAK;AAAA,MACd;AAAA;AAAA,MAGA,IAAI,cAAuC;AACzC,eAAO,CAAC,GAAG,KAAK,YAAY;AAAA,MAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,MAAM,UAAyB;AAC7B,cAAM,WAAW,MAAM,KAAK,SAAS;AACrC,YAAI,CAAC,SAAU;AACf,cAAM,KAAK,SAAS,SAAS,IAAI;AAAA,MACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA,MAAc,cAA6B;AACzC,YAAI,KAAK,eAAe;AACtB,eAAK,eAAe;AACpB;AAAA,QACF;AACA,aAAK,gBAAgB;AACrB,YAAI;AACF,gBAAM,KAAK,cAAc;AACzB,iBAAO,KAAK,cAAc;AACxB,iBAAK,eAAe;AACpB,kBAAM,KAAK,cAAc;AAAA,UAC3B;AAAA,QACF,UAAE;AACA,eAAK,gBAAgB;AAAA,QACvB;AAAA,MACF;AAAA,MAEA,MAAc,gBAA+B;AAE3C,aAAK,iBAAiB;AAEtB,YAAI,KAAK,aAAa,UAAU,EAAG;AAEnC,cAAM,WAAW,MAAM,KAAK,SAAS;AACrC,YAAI,CAAC,SAAU;AAKf,YAAI,CAAC,KAAK,YAAa;AAEvB,cAAM,OAAO,KAAK,oBAAoB,SAAS,GAAG;AAElD,gBAAQ,KAAK,MAAM;AAAA,UACjB,KAAK;AACH,kBAAM,KAAK,SAAS,SAAS,IAAI;AACjC;AAAA,UACF,KAAK;AACH;AAAA,UACF,KAAK;AACH,iBAAK,eAAe,SAAS,IAAI;AACjC;AAAA,UACF,KAAK;AACH,iBAAK,YAAY,SAAS,IAAI;AAC9B;AAAA,UACF,KAAK;AACH,iBAAK,WAAW,SAAS,MAAM,KAAK,SAAS;AAC7C;AAAA,QACJ;AAAA,MACF;AAAA,MAEQ,oBAAoB,KAA2C;AAErE,cAAM,OAAO,KAAK,aAAa,SAAS;AACxC,YAAI,SAAS,MAAM;AACjB,iBAAO,kBAAkB,IAAI;AAAA,QAC/B;AAGA,cAAM,aAAa,IAAI,SAAS;AAChC,YAAI,cAAc,OAAO,eAAe,YAAY,CAAC,MAAM,QAAQ,UAAU,GAAG;AAC9E,gBAAM,YAAa,WAAuC,OAAO;AACjE,cAAI,OAAO,cAAc,YAAY,UAAU,SAAS,GAAG;AACzD,mBAAO,kBAAkB,SAAS;AAAA,UACpC;AAAA,QACF;AAGA,eAAO,EAAE,MAAM,OAAO;AAAA,MACxB;AAAA;AAAA,MAIQ,mBAAyB;AAC/B,YAAI,KAAK,iBAAiB;AACxB,eAAK,gBAAgB;AACrB,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF;AAAA,MAEQ,WAAW,MAAoB,WAAyB;AAC9D,cAAM,UAAU,CAAC,QAAqB;AACpC,eAAK,KAAK,SAAS,IAAI;AAAA,QACzB;AACA,aAAK,iBAAiB,WAAW,OAAO;AACxC,aAAK,kBAAkB,MAAM,KAAK,oBAAoB,WAAW,OAAO;AAAA,MAC1E;AAAA,MAEQ,YAAY,MAA0B;AAC5C,cAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,YAAI,CAAC,MAAM;AACT,eAAK,aAAa,KAAK;AAAA,YACrB,UAAU;AAAA,YACV,MAAM;AAAA,YACN,SAAS;AAAA,UACX,CAAC;AACD;AAAA,QACF;AACA,cAAM,UAAU,CAAC,OAAoB;AAInC,aAAG,eAAe;AAClB,eAAK,KAAK,SAAS,IAAI;AAAA,QACzB;AACA,aAAK,iBAAiB,UAAU,OAAO;AACvC,aAAK,kBAAkB,MAAM,KAAK,oBAAoB,UAAU,OAAO;AAAA,MACzE;AAAA,MAEQ,eAAe,MAA0B;AAC/C,YAAI,OAAO,yBAAyB,aAAa;AAC/C,eAAK,aAAa,KAAK;AAAA,YACrB,UAAU;AAAA,YACV,MAAM;AAAA,YACN,SACE;AAAA,UACJ,CAAC;AACD;AAAA,QACF;AACA,cAAM,WAAW,IAAI,qBAAqB,aAAW;AACnD,qBAAW,SAAS,SAAS;AAC3B,gBAAI,MAAM,gBAAgB;AAExB,uBAAS,WAAW;AACpB,mBAAK,KAAK,SAAS,IAAI;AACvB;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AACD,iBAAS,QAAQ,IAAI;AACrB,aAAK,kBAAkB,MAAM,SAAS,WAAW;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASA,MAAc,WAAyC;AACrD,cAAM,MAAM,MAAM,KAAK,UAAU;AACjC,YAAI,QAAQ,KAAM,QAAO;AAGzB,cAAM,gBAAY,oCAAqB,GAAG;AAC1C,cAAM,YAAY,UAAU,KAAK,OAAK,EAAE,aAAa,OAAO;AAE5D,YAAI,WAAW;AACb,eAAK,eAAe;AACpB,eAAK,QAAQ;AACb,eAAK,WAAW;AAChB,eAAK,MAAM,aAAa,EAAE,aAAa,WAAW,OAAO,KAAK,CAAC;AAC/D,iBAAO;AAAA,QACT;AAGA,YAAI;AACJ,YAAI;AACF,qBAAO,gCAAiB,GAAwD;AAAA,QAClF,SAAS,KAAK;AACZ,gBAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,eAAK,eAAe;AAAA,YAClB,EAAE,UAAU,SAAS,MAAM,qBAAqB,SAAS,MAAM,QAAQ;AAAA,UACzE;AACA,eAAK,QAAQ;AACb,eAAK,WAAW;AAChB,eAAK,MAAM,aAAa,EAAE,aAAa,KAAK,cAAc,MAAM,CAAC;AACjE,iBAAO;AAAA,QACT;AAGA,cAAM,SAAS,eAAe,IAAI,KAAK,MAAM;AAC7C,cAAM,cAA8B,SAAS,KAAK,gBAAgB,MAAM,MAAM,IAAI,CAAC;AAEnF,aAAK,QAAQ;AACb,aAAK,eAAe,CAAC,GAAG,WAAW,GAAG,WAAW;AACjD,aAAK,MAAM,iBAAiB,EAAE,MAAM,aAAa,CAAC,GAAG,KAAK,YAAY,EAAE,CAAC;AAEzE,eAAO,EAAE,MAAM,IAAI;AAAA,MACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAcA,MAAc,SAAS,MAAmC;AACxD,cAAM,UAAU,WAAW;AAC3B,YAAI,CAAC,SAAS;AAEZ,eAAK,aAAa,KAAK;AAAA,YACrB,UAAU;AAAA,YACV,MAAM;AAAA,YACN,SACE;AAAA,UACJ,CAAC;AACD;AAAA,QACF;AAIA,cAAM,cACJ,KAAK,SAAS,kBACV,CAAC,GAAK,KAAmC,QAAQ,CAAC,CAAE,IACpD,CAAC,IAAI;AAEX,cAAM,aAAa,KAAK,aAAa,SAAS;AAC9C,cAAM,YAAY,eAAe,OAAO,SAAS,YAAY,EAAE,KAAK,MAAO;AAK3E,cAAM,UAAqB,CAAC;AAC5B,mBAAW,OAAO,aAAa;AAC7B,gBAAM,SAAS,MAAM,UAAU,MAAM,QAAQ,YAAY,KAAK,IAAI,GAAG,SAAS;AAC9E,cAAI,OAAO,IAAI;AACb,oBAAQ,KAAK,OAAO,MAAM;AAAA,UAC5B,OAAO;AACL,iBAAK,aAAa,KAAK;AAAA,cACrB,UAAU;AAAA,cACV,MAAM,OAAO,WAAW,sBAAsB;AAAA,cAC9C,SAAS,OAAO,OAAO,WAAW;AAAA,YACpC,CAAC;AACD,iBAAK,WAAW;AAChB,iBAAK,MAAM,aAAa,EAAE,aAAa,CAAC,GAAG,KAAK,YAAY,GAAG,OAAO,OAAO,MAAM,CAAC;AACpF;AAAA,UACF;AAAA,QACF;AAEA,aAAK,MAAM,gBAAgB;AAAA,UACzB;AAAA;AAAA;AAAA,UAGA,QAAQ,QAAQ,WAAW,IAAI,QAAQ,CAAC,IAAI;AAAA,UAC5C;AAAA,QACF,CAAC;AAAA,MACH;AAAA,MAEA,MAAc,YAAqD;AAEjE,cAAM,MAAM,KAAK,aAAa,KAAK;AACnC,YAAI,KAAK;AACP,eAAK,kBAAkB,MAAM;AAC7B,eAAK,mBAAmB,IAAI,gBAAgB;AAC5C,cAAI;AACF,kBAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,KAAK,iBAAiB,OAAO,CAAC;AAC1E,gBAAI,CAAC,SAAS,IAAI;AAChB,mBAAK,gBAAgB,KAAK,SAAS,MAAM;AACzC,qBAAO;AAAA,YACT;AACA,mBAAQ,MAAM,SAAS,KAAK;AAAA,UAC9B,SAAS,KAAK;AAEZ,gBAAI,eAAe,SAAS,IAAI,SAAS,aAAc,QAAO;AAC9D,iBAAK,gBAAgB,KAAK,GAAG,eAAe,QAAQ,MAAM,MAAS;AACnE,mBAAO;AAAA,UACT;AAAA,QACF;AAGA,cAAM,SAAS,KAAK,cAAc,qCAAqC;AACvE,YAAI,QAAQ;AACV,cAAI;AACF,mBAAO,KAAK,MAAM,OAAO,eAAe,EAAE;AAAA,UAC5C,QAAQ;AACN,kBAAM,OAAqB;AAAA,cACzB,UAAU;AAAA,cACV,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AACA,iBAAK,eAAe,CAAC,IAAI;AACzB,iBAAK,WAAW;AAChB,iBAAK,MAAM,aAAa,EAAE,aAAa,CAAC,IAAI,GAAG,OAAO,KAAK,CAAC;AAC5D,mBAAO;AAAA,UACT;AAAA,QACF;AAEA,eAAO;AAAA,MACT;AAAA,MAEQ,gBAAgB,MAAoB,QAAuC;AACjF,cAAM,QAAwB,CAAC;AAC/B,mBAAW,YAAY,OAAO,OAAO;AACnC,gBAAM,QAAQ,KAAK,MAAM,IAAI,SAAS,IAAI;AAE1C,cAAI,SAAS,YAAY,CAAC,OAAO;AAC/B,kBAAM,KAAK;AAAA,cACT,UAAU;AAAA,cACV,MAAM;AAAA,cACN,SAAS,kBAAkB,SAAS,IAAI,6BAA6B,KAAK,MAAM;AAAA,YAClF,CAAC;AACD;AAAA,UACF;AAEA,cAAI,SAAS,SAAS,iBAAiB,SAAS,cAAc,SAAS,GAAG;AACxE,gBACE,CAAC,SAAS,cAAc,SAAS,MAAM,IAA+C,GACtF;AACA,oBAAM,KAAK;AAAA,gBACT,UAAU;AAAA,gBACV,MAAM;AAAA,gBACN,SAAS,SAAS,SAAS,IAAI,eAAe,MAAM,IAAI,0BAA0B,SAAS,cAAc,KAAK,IAAI,CAAC;AAAA,cACrH,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MAEQ,aAAmB;AACzB,cAAM,YAAY,KAAK,cAAc,gBAAgB;AACrD,YAAI,qBAAqB,aAAa;AACpC,oBAAU,MAAM,UAAU;AAAA,QAC5B;AAAA,MACF;AAAA,MAEQ,MAAM,MAAc,QAAuC;AACjE,aAAK,cAAc,IAAI,YAAY,MAAM,EAAE,QAAQ,SAAS,MAAM,UAAU,KAAK,CAAC,CAAC;AAAA,MACrF;AAAA,MAEQ,gBAAgB,KAAa,QAAgB,OAAqB;AACxE,cAAM,UAAU,SACZ,mBAAmB,GAAG,UAAU,MAAM,KACtC,mBAAmB,GAAG,GAAG,QAAQ,OAAO,MAAM,UAAU,EAAE;AAC9D,cAAM,OAAqB,EAAE,UAAU,SAAS,MAAM,eAAe,QAAQ;AAC7E,aAAK,eAAe,CAAC,IAAI;AACzB,aAAK,WAAW;AAChB,aAAK,MAAM,aAAa,EAAE,aAAa,CAAC,IAAI,GAAG,OAAO,SAAS,KAAK,CAAC;AAAA,MACvE;AAAA,IACF;AA9YE,IADW,iBACJ,qBAAqB,CAAC,OAAO,YAAY,WAAW,SAAS;AAAA;AAAA;;;AClHtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBA;AACA;AAIA,IAAI,OAAO,mBAAmB,eAAe,CAAC,eAAe,IAAI,YAAY,GAAG;AAE9E,wEAA0B,KAAK,CAAC,EAAE,kBAAAA,kBAAiB,MAAM;AACvD,mBAAe,OAAO,cAAcA,iBAAgB;AAAA,EACtD,CAAC;AACH;","names":["LSEIntentElement"]}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @hyperfixi/intent-element
3
+ *
4
+ * Browser custom element that validates LSE protocol JSON and executes it
5
+ * via the hyperfixi runtime. Zero-dependency validation via @lokascript/intent;
6
+ * execution delegated to window.hyperfixi.evalLSENode (peer dep).
7
+ *
8
+ * Auto-registers <lse-intent> when loaded as a browser script.
9
+ *
10
+ * @example
11
+ * ```html
12
+ * <script src="hyperfixi.js"></script>
13
+ * <script src="intent-element.iife.global.js"></script>
14
+ *
15
+ * <lse-intent>
16
+ * <script type="application/lse+json">
17
+ * {"action":"toggle","roles":{"patient":{"type":"selector","value":".active"}},"trigger":{"event":"click"}}
18
+ * </script>
19
+ * <button slot="trigger">Toggle sidebar</button>
20
+ * </lse-intent>
21
+ * ```
22
+ */
23
+ export { LSEIntentElement } from './lse-intent.js';
24
+ export { intentRegistry } from './schema-registry.js';
25
+ export type { SandboxResult } from './sandbox.js';
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}