slexkit 0.2.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.
Files changed (221) hide show
  1. package/AGENTS.slexkit.md +29 -0
  2. package/CHANGELOG.md +90 -0
  3. package/LICENSE +21 -0
  4. package/README.md +165 -0
  5. package/README.zh-CN.md +165 -0
  6. package/dist/ai/llms-authoring.txt +44 -0
  7. package/dist/ai/llms-components.txt +669 -0
  8. package/dist/ai/llms-full.txt +6586 -0
  9. package/dist/ai/llms-runtime.txt +1475 -0
  10. package/dist/ai/llms-toolhost.txt +295 -0
  11. package/dist/ai/llms.txt +69 -0
  12. package/dist/ai/slexkit-ai-manifest.json +2922 -0
  13. package/dist/base.css +621 -0
  14. package/dist/chunks/accordion-5f0nvjjm.js +376 -0
  15. package/dist/chunks/accordion-830dw78f.js +221 -0
  16. package/dist/chunks/accordion-cfjyxw93.js +630 -0
  17. package/dist/chunks/accordion-cw5r75jm.js +424 -0
  18. package/dist/chunks/accordion-ehnhpeca.js +492 -0
  19. package/dist/chunks/accordion-hzyrngd6.js +2377 -0
  20. package/dist/chunks/accordion-nw12ytps.js +6823 -0
  21. package/dist/components/accordion.js +163 -0
  22. package/dist/components/badge.js +80 -0
  23. package/dist/components/button.css +114 -0
  24. package/dist/components/button.js +16 -0
  25. package/dist/components/callout.js +154 -0
  26. package/dist/components/card.js +95 -0
  27. package/dist/components/checkbox.js +114 -0
  28. package/dist/components/choice.css +165 -0
  29. package/dist/components/code-block.js +264 -0
  30. package/dist/components/collapsible.js +111 -0
  31. package/dist/components/column.js +49 -0
  32. package/dist/components/content.css +474 -0
  33. package/dist/components/disclosure.css +162 -0
  34. package/dist/components/display.css +259 -0
  35. package/dist/components/divider.js +98 -0
  36. package/dist/components/feedback.css +219 -0
  37. package/dist/components/grid.js +67 -0
  38. package/dist/components/index.js +13364 -0
  39. package/dist/components/input.css +1247 -0
  40. package/dist/components/input.js +384 -0
  41. package/dist/components/link.js +77 -0
  42. package/dist/components/progress.js +111 -0
  43. package/dist/components/radio-group.js +189 -0
  44. package/dist/components/row.js +200 -0
  45. package/dist/components/section.js +161 -0
  46. package/dist/components/select.css +260 -0
  47. package/dist/components/select.js +16 -0
  48. package/dist/components/slider.css +125 -0
  49. package/dist/components/slider.js +175 -0
  50. package/dist/components/specs.js +1090 -0
  51. package/dist/components/stat.js +178 -0
  52. package/dist/components/submit.css +9 -0
  53. package/dist/components/submit.js +77 -0
  54. package/dist/components/switch.css +114 -0
  55. package/dist/components/switch.js +114 -0
  56. package/dist/components/table.js +157 -0
  57. package/dist/components/tabs.css +192 -0
  58. package/dist/components/tabs.js +17 -0
  59. package/dist/components/text-input.css +245 -0
  60. package/dist/components/text.js +50 -0
  61. package/dist/components/toast.js +240 -0
  62. package/dist/components/tooling.css +1009 -0
  63. package/dist/components/tooling.js +48951 -0
  64. package/dist/runtime.cjs +3728 -0
  65. package/dist/runtime.js +3686 -0
  66. package/dist/slexkit.cjs +18539 -0
  67. package/dist/slexkit.css +4776 -0
  68. package/dist/slexkit.js +18497 -0
  69. package/dist/tooling.js +59141 -0
  70. package/dist/types/components/accordion.d.ts +2 -0
  71. package/dist/types/components/badge.d.ts +2 -0
  72. package/dist/types/components/button.d.ts +2 -0
  73. package/dist/types/components/callout.d.ts +2 -0
  74. package/dist/types/components/card.d.ts +2 -0
  75. package/dist/types/components/checkbox.d.ts +2 -0
  76. package/dist/types/components/code-block.d.ts +2 -0
  77. package/dist/types/components/collapsible.d.ts +2 -0
  78. package/dist/types/components/column.d.ts +2 -0
  79. package/dist/types/components/divider.d.ts +2 -0
  80. package/dist/types/components/entries/accordion.d.ts +3 -0
  81. package/dist/types/components/entries/badge.d.ts +3 -0
  82. package/dist/types/components/entries/button.d.ts +3 -0
  83. package/dist/types/components/entries/callout.d.ts +3 -0
  84. package/dist/types/components/entries/card.d.ts +3 -0
  85. package/dist/types/components/entries/checkbox.d.ts +3 -0
  86. package/dist/types/components/entries/code-block.d.ts +3 -0
  87. package/dist/types/components/entries/collapsible.d.ts +3 -0
  88. package/dist/types/components/entries/column.d.ts +3 -0
  89. package/dist/types/components/entries/divider.d.ts +3 -0
  90. package/dist/types/components/entries/grid.d.ts +3 -0
  91. package/dist/types/components/entries/input.d.ts +3 -0
  92. package/dist/types/components/entries/link.d.ts +3 -0
  93. package/dist/types/components/entries/progress.d.ts +3 -0
  94. package/dist/types/components/entries/radio-group.d.ts +3 -0
  95. package/dist/types/components/entries/row.d.ts +3 -0
  96. package/dist/types/components/entries/section.d.ts +3 -0
  97. package/dist/types/components/entries/select.d.ts +3 -0
  98. package/dist/types/components/entries/slider.d.ts +3 -0
  99. package/dist/types/components/entries/specs.d.ts +1 -0
  100. package/dist/types/components/entries/stat.d.ts +3 -0
  101. package/dist/types/components/entries/submit.d.ts +3 -0
  102. package/dist/types/components/entries/switch.d.ts +3 -0
  103. package/dist/types/components/entries/table.d.ts +3 -0
  104. package/dist/types/components/entries/tabs.d.ts +3 -0
  105. package/dist/types/components/entries/text.d.ts +3 -0
  106. package/dist/types/components/entries/toast.d.ts +3 -0
  107. package/dist/types/components/entries/tooling.d.ts +1 -0
  108. package/dist/types/components/grid.d.ts +2 -0
  109. package/dist/types/components/index.d.ts +6 -0
  110. package/dist/types/components/input.d.ts +2 -0
  111. package/dist/types/components/link.d.ts +2 -0
  112. package/dist/types/components/progress.d.ts +2 -0
  113. package/dist/types/components/radio-group.d.ts +2 -0
  114. package/dist/types/components/row.d.ts +2 -0
  115. package/dist/types/components/section.d.ts +2 -0
  116. package/dist/types/components/select.d.ts +2 -0
  117. package/dist/types/components/slider.d.ts +2 -0
  118. package/dist/types/components/spec-helpers.d.ts +23 -0
  119. package/dist/types/components/spec-registry.d.ts +12 -0
  120. package/dist/types/components/spec-schema.d.ts +74 -0
  121. package/dist/types/components/specs.d.ts +2 -0
  122. package/dist/types/components/stat.d.ts +2 -0
  123. package/dist/types/components/submit.d.ts +2 -0
  124. package/dist/types/components/svelte/adapter.d.ts +3 -0
  125. package/dist/types/components/svelte/bindProps.d.ts +2 -0
  126. package/dist/types/components/svelte/helpers.d.ts +33 -0
  127. package/dist/types/components/svelte/layout/balancedTiles.d.ts +14 -0
  128. package/dist/types/components/svelte/types.d.ts +12 -0
  129. package/dist/types/components/switch.d.ts +2 -0
  130. package/dist/types/components/table.d.ts +2 -0
  131. package/dist/types/components/tabs.d.ts +2 -0
  132. package/dist/types/components/text.d.ts +2 -0
  133. package/dist/types/components/toast.d.ts +2 -0
  134. package/dist/types/components/tooling.d.ts +2 -0
  135. package/dist/types/components-svelte.d.ts +5 -0
  136. package/dist/types/engine/component-scope.d.ts +14 -0
  137. package/dist/types/engine/component-state.d.ts +9 -0
  138. package/dist/types/engine/diagnostics.d.ts +24 -0
  139. package/dist/types/engine/engineering.d.ts +11 -0
  140. package/dist/types/engine/eval.d.ts +5 -0
  141. package/dist/types/engine/index.d.ts +26 -0
  142. package/dist/types/engine/markdown-runtime.d.ts +33 -0
  143. package/dist/types/engine/merge.d.ts +1 -0
  144. package/dist/types/engine/reactive.d.ts +11 -0
  145. package/dist/types/engine/registry.d.ts +4 -0
  146. package/dist/types/engine/renderer.d.ts +6 -0
  147. package/dist/types/engine/sandbox-runner.d.ts +2 -0
  148. package/dist/types/engine/secure-runtime.d.ts +214 -0
  149. package/dist/types/engine/store.d.ts +12 -0
  150. package/dist/types/engine/types.d.ts +58 -0
  151. package/dist/types/icons/manager.d.ts +17 -0
  152. package/dist/types/icons/phosphor.d.ts +45 -0
  153. package/dist/types/index.d.ts +61 -0
  154. package/dist/types/runtime.d.ts +32 -0
  155. package/dist/types/toolhost/index.d.ts +78 -0
  156. package/dist/types/tooling-umd.d.ts +47 -0
  157. package/dist/types/version.d.ts +8 -0
  158. package/dist/umd/slexkit.tooling.umd.js +66553 -0
  159. package/dist/umd/slexkit.umd.js +18552 -0
  160. package/package.json +136 -0
  161. package/scripts/cli.mjs +47 -0
  162. package/skills/slexkit/SKILL.md +27 -0
  163. package/skills/slexkit-author/SKILL.md +50 -0
  164. package/skills/slexkit-host-integration/SKILL.md +33 -0
  165. package/skills/slexkit-secure-runtime/SKILL.md +31 -0
  166. package/skills/slexkit-toolhost/SKILL.md +38 -0
  167. package/skills/slexkit-update/SKILL.md +23 -0
  168. package/src/components/svelte/InlineIcon.svelte +66 -0
  169. package/src/components/svelte/adapter.ts +76 -0
  170. package/src/components/svelte/bindProps.ts +9 -0
  171. package/src/components/svelte/content/Badge.svelte +19 -0
  172. package/src/components/svelte/content/Callout.svelte +57 -0
  173. package/src/components/svelte/content/CodeBlock.svelte +130 -0
  174. package/src/components/svelte/content/Divider.svelte +21 -0
  175. package/src/components/svelte/content/Link.svelte +21 -0
  176. package/src/components/svelte/content/Section.svelte +24 -0
  177. package/src/components/svelte/content/Table.svelte +44 -0
  178. package/src/components/svelte/disclosure/Accordion.svelte +100 -0
  179. package/src/components/svelte/disclosure/Collapsible.svelte +45 -0
  180. package/src/components/svelte/display/Stat.svelte +102 -0
  181. package/src/components/svelte/display/Text.svelte +11 -0
  182. package/src/components/svelte/feedback/Progress.svelte +34 -0
  183. package/src/components/svelte/feedback/Toast.svelte +105 -0
  184. package/src/components/svelte/helpers.ts +148 -0
  185. package/src/components/svelte/input/Button.svelte +78 -0
  186. package/src/components/svelte/input/Checkbox.svelte +52 -0
  187. package/src/components/svelte/input/Input.svelte +202 -0
  188. package/src/components/svelte/input/RadioGroup.svelte +71 -0
  189. package/src/components/svelte/input/Select.svelte +220 -0
  190. package/src/components/svelte/input/Slider.svelte +96 -0
  191. package/src/components/svelte/input/Submit.svelte +32 -0
  192. package/src/components/svelte/input/Switch.svelte +53 -0
  193. package/src/components/svelte/input/Tabs.svelte +188 -0
  194. package/src/components/svelte/layout/Card.svelte +17 -0
  195. package/src/components/svelte/layout/Column.svelte +15 -0
  196. package/src/components/svelte/layout/Grid.svelte +26 -0
  197. package/src/components/svelte/layout/Row.svelte +105 -0
  198. package/src/components/svelte/layout/balancedTiles.ts +85 -0
  199. package/src/components/svelte/tooling/CodeMirror.svelte +91 -0
  200. package/src/components/svelte/tooling/Playground.svelte +765 -0
  201. package/src/components/svelte/tooling/PlaygroundMarkdown.svelte +26 -0
  202. package/src/components/svelte/tooling/PlaygroundSlexCode.svelte +76 -0
  203. package/src/components/svelte/types.ts +17 -0
  204. package/src/styles/animation.css +98 -0
  205. package/src/styles/components/button.css +114 -0
  206. package/src/styles/components/choice.css +165 -0
  207. package/src/styles/components/select.css +260 -0
  208. package/src/styles/components/slider.css +125 -0
  209. package/src/styles/components/submit.css +9 -0
  210. package/src/styles/components/switch.css +114 -0
  211. package/src/styles/components/tabs.css +192 -0
  212. package/src/styles/components/text-input.css +245 -0
  213. package/src/styles/content.css +474 -0
  214. package/src/styles/disclosure.css +162 -0
  215. package/src/styles/display.css +259 -0
  216. package/src/styles/entry.css +34 -0
  217. package/src/styles/feedback.css +219 -0
  218. package/src/styles/input.css +8 -0
  219. package/src/styles/layout.css +365 -0
  220. package/src/styles/theme.css +31 -0
  221. package/src/styles/tooling.css +1009 -0
@@ -0,0 +1,3686 @@
1
+ // src/engine/reactive.ts
2
+ var ITERATE_KEY = Symbol("iterate");
3
+ var REVISION_KEY = Symbol("revision");
4
+ var activeEffect = null;
5
+ var activeScope = null;
6
+ var batchDepth = 0;
7
+ var pendingEffects = new Set;
8
+ var targetDeps = new WeakMap;
9
+ var proxyCache = new WeakMap;
10
+ var rawTargets = new WeakMap;
11
+ var rootTargets = new WeakMap;
12
+
13
+ class ReactiveScope {
14
+ cleanups = [];
15
+ effects = new Set;
16
+ disposed = false;
17
+ dispose() {
18
+ if (this.disposed)
19
+ return;
20
+ this.disposed = true;
21
+ for (const effect of Array.from(this.effects))
22
+ effect.stop();
23
+ this.effects.clear();
24
+ for (let i = this.cleanups.length - 1;i >= 0; i -= 1) {
25
+ this.cleanups[i]();
26
+ }
27
+ this.cleanups.length = 0;
28
+ }
29
+ }
30
+
31
+ class ReactiveEffect {
32
+ fn;
33
+ scope;
34
+ deps = [];
35
+ cleanups = [];
36
+ stopped = false;
37
+ running = false;
38
+ constructor(fn, scope) {
39
+ this.fn = fn;
40
+ this.scope = scope;
41
+ if (scope)
42
+ scope.effects.add(this);
43
+ }
44
+ run() {
45
+ if (this.stopped || this.running)
46
+ return;
47
+ this.running = true;
48
+ this.cleanupDeps();
49
+ this.runCleanups();
50
+ const previousEffect = activeEffect;
51
+ const previousScope = activeScope;
52
+ activeEffect = this;
53
+ if (this.scope)
54
+ activeScope = this.scope;
55
+ try {
56
+ this.fn();
57
+ } finally {
58
+ activeEffect = previousEffect;
59
+ activeScope = previousScope;
60
+ this.running = false;
61
+ }
62
+ }
63
+ stop() {
64
+ if (this.stopped)
65
+ return;
66
+ this.stopped = true;
67
+ this.cleanupDeps();
68
+ this.runCleanups();
69
+ this.scope?.effects.delete(this);
70
+ }
71
+ cleanupDeps() {
72
+ for (const dep of this.deps)
73
+ dep.delete(this);
74
+ this.deps.length = 0;
75
+ }
76
+ runCleanups() {
77
+ for (let i = this.cleanups.length - 1;i >= 0; i -= 1) {
78
+ this.cleanups[i]();
79
+ }
80
+ this.cleanups.length = 0;
81
+ }
82
+ }
83
+ function getDep(target, key) {
84
+ let deps = targetDeps.get(target);
85
+ if (!deps) {
86
+ deps = new Map;
87
+ targetDeps.set(target, deps);
88
+ }
89
+ let dep = deps.get(key);
90
+ if (!dep) {
91
+ dep = new Set;
92
+ deps.set(key, dep);
93
+ }
94
+ return dep;
95
+ }
96
+ function track(target, key) {
97
+ if (!activeEffect)
98
+ return;
99
+ const dep = getDep(target, key);
100
+ if (dep.has(activeEffect))
101
+ return;
102
+ dep.add(activeEffect);
103
+ activeEffect.deps.push(dep);
104
+ }
105
+ function trigger(target, key) {
106
+ const deps = targetDeps.get(target);
107
+ if (!deps)
108
+ return;
109
+ const effects = new Set;
110
+ const add = (dep) => {
111
+ if (!dep)
112
+ return;
113
+ for (const effect of dep)
114
+ effects.add(effect);
115
+ };
116
+ add(deps.get(key));
117
+ if (key !== REVISION_KEY)
118
+ add(deps.get(ITERATE_KEY));
119
+ for (const effect of effects)
120
+ schedule(effect);
121
+ }
122
+ function schedule(effect) {
123
+ if (effect.stopped)
124
+ return;
125
+ if (batchDepth > 0) {
126
+ pendingEffects.add(effect);
127
+ return;
128
+ }
129
+ effect.run();
130
+ }
131
+ function flush() {
132
+ while (pendingEffects.size > 0) {
133
+ const effects = Array.from(pendingEffects);
134
+ pendingEffects.clear();
135
+ for (const effect of effects)
136
+ effect.run();
137
+ }
138
+ }
139
+ function notifyRoot(target) {
140
+ const root = rootTargets.get(target) ?? target;
141
+ trigger(root, REVISION_KEY);
142
+ }
143
+ function toReactive(value, root) {
144
+ const cached = proxyCache.get(value);
145
+ if (cached)
146
+ return cached;
147
+ const proxy = new Proxy(value, {
148
+ get(target, key, receiver) {
149
+ if (key === "__slexkitRaw")
150
+ return target;
151
+ if (key === "__slexkitRoot")
152
+ return root;
153
+ track(target, key);
154
+ const result = Reflect.get(target, key, receiver);
155
+ if (result && typeof result === "object") {
156
+ return toReactive(result, root);
157
+ }
158
+ return result;
159
+ },
160
+ set(target, key, next, receiver) {
161
+ const previous = Reflect.get(target, key, receiver);
162
+ const hadKey = Object.prototype.hasOwnProperty.call(target, key);
163
+ const ok = Reflect.set(target, key, next, receiver);
164
+ if (ok && (!Object.is(previous, next) || !hadKey)) {
165
+ trigger(target, key);
166
+ if (!hadKey)
167
+ trigger(target, ITERATE_KEY);
168
+ notifyRoot(target);
169
+ }
170
+ return ok;
171
+ },
172
+ deleteProperty(target, key) {
173
+ const hadKey = Object.prototype.hasOwnProperty.call(target, key);
174
+ const ok = Reflect.deleteProperty(target, key);
175
+ if (ok && hadKey) {
176
+ trigger(target, key);
177
+ trigger(target, ITERATE_KEY);
178
+ notifyRoot(target);
179
+ }
180
+ return ok;
181
+ },
182
+ ownKeys(target) {
183
+ track(target, ITERATE_KEY);
184
+ return Reflect.ownKeys(target);
185
+ }
186
+ });
187
+ proxyCache.set(value, proxy);
188
+ rawTargets.set(proxy, value);
189
+ rootTargets.set(value, root);
190
+ rootTargets.set(proxy, root);
191
+ return proxy;
192
+ }
193
+ function createReactiveState(value) {
194
+ return toReactive(value, value);
195
+ }
196
+ function asReactiveValue(value, owner) {
197
+ if (!value || typeof value !== "object")
198
+ return value;
199
+ if (rawTargets.has(value) || rootTargets.has(value))
200
+ return value;
201
+ if (!owner || typeof owner !== "object")
202
+ return value;
203
+ const root = rootTargets.get(owner) ?? rawTargets.get(owner);
204
+ if (!root)
205
+ return value;
206
+ return toReactive(value, root);
207
+ }
208
+ function trackReactiveValue(value) {
209
+ if (!value || typeof value !== "object")
210
+ return;
211
+ const root = rootTargets.get(value) ?? rawTargets.get(value);
212
+ if (root)
213
+ track(root, REVISION_KEY);
214
+ }
215
+ function createRoot(fn) {
216
+ const scope = new ReactiveScope;
217
+ const previousScope = activeScope;
218
+ const previousEffect = activeEffect;
219
+ activeScope = scope;
220
+ activeEffect = null;
221
+ try {
222
+ return fn(() => scope.dispose());
223
+ } finally {
224
+ activeEffect = previousEffect;
225
+ activeScope = previousScope;
226
+ }
227
+ }
228
+ function createEffect(fn) {
229
+ const effect = new ReactiveEffect(fn, activeScope);
230
+ effect.run();
231
+ return () => effect.stop();
232
+ }
233
+ function createMemo(fn) {
234
+ const source = {};
235
+ let value;
236
+ createEffect(() => {
237
+ value = fn();
238
+ trigger(source, "value");
239
+ });
240
+ return () => {
241
+ track(source, "value");
242
+ return value;
243
+ };
244
+ }
245
+ function createSignal(initial) {
246
+ const source = {};
247
+ let value = initial;
248
+ return [
249
+ () => {
250
+ track(source, "value");
251
+ return value;
252
+ },
253
+ (next) => {
254
+ if (Object.is(value, next))
255
+ return;
256
+ value = next;
257
+ trigger(source, "value");
258
+ }
259
+ ];
260
+ }
261
+ function onCleanup(cleanup) {
262
+ if (activeEffect) {
263
+ activeEffect.cleanups.push(cleanup);
264
+ return;
265
+ }
266
+ if (activeScope) {
267
+ activeScope.cleanups.push(cleanup);
268
+ }
269
+ }
270
+ function batch(fn) {
271
+ batchDepth += 1;
272
+ try {
273
+ return fn();
274
+ } finally {
275
+ batchDepth -= 1;
276
+ if (batchDepth === 0)
277
+ flush();
278
+ }
279
+ }
280
+
281
+ // src/engine/store.ts
282
+ var _stores = new Map;
283
+ function getStore(ns) {
284
+ if (!_stores.has(ns)) {
285
+ _stores.set(ns, {
286
+ g: createReactiveState({}),
287
+ components: createReactiveState({}),
288
+ componentTypes: createReactiveState({}),
289
+ layouts: [],
290
+ roots: new Map,
291
+ cleanups: new Map
292
+ });
293
+ }
294
+ return _stores.get(ns);
295
+ }
296
+ function peekStore(ns) {
297
+ return _stores.get(ns);
298
+ }
299
+ function deleteStore(ns) {
300
+ return _stores.delete(ns);
301
+ }
302
+
303
+ // src/engine/merge.ts
304
+ function deepMerge(target, source) {
305
+ for (const key in source) {
306
+ if (!Object.prototype.hasOwnProperty.call(source, key))
307
+ continue;
308
+ const srcVal = source[key];
309
+ if (typeof srcVal === "function") {
310
+ target[key] = srcVal;
311
+ } else if (srcVal !== null && typeof srcVal === "object" && !Array.isArray(srcVal)) {
312
+ if (!(key in target) || typeof target[key] !== "object") {
313
+ target[key] = {};
314
+ }
315
+ deepMerge(target[key], srcVal);
316
+ } else {
317
+ target[key] = srcVal;
318
+ }
319
+ }
320
+ return target;
321
+ }
322
+
323
+ // src/engine/registry.ts
324
+ var _registry = new Map;
325
+ function register(type, renderFn, options = {}) {
326
+ _registry.set(type, {
327
+ renderFn,
328
+ options: {
329
+ state: options.state ?? "none"
330
+ }
331
+ });
332
+ }
333
+ function getRenderer(type) {
334
+ return _registry.get(type)?.renderFn;
335
+ }
336
+ function getComponentStateMode(type) {
337
+ return _registry.get(type)?.options.state ?? "none";
338
+ }
339
+
340
+ // src/engine/eval.ts
341
+ var lastValues = new Map;
342
+ var SKIP_EAGER_TRACK = Symbol.for("slexkit.skipEagerTrack");
343
+ var EAGER_TRACK_TARGET = Symbol.for("slexkit.eagerTrackTarget");
344
+ function clearEvalCache(ns) {
345
+ const prefix = `${ns}:`;
346
+ for (const key of Array.from(lastValues.keys())) {
347
+ if (key.startsWith(prefix))
348
+ lastValues.delete(key);
349
+ }
350
+ }
351
+ function evalRead(expr, context, ns, path) {
352
+ try {
353
+ for (const value of Object.values(context)) {
354
+ if (value && (typeof value === "object" || typeof value === "function") && value[SKIP_EAGER_TRACK]) {
355
+ continue;
356
+ }
357
+ const target = value && (typeof value === "object" || typeof value === "function") ? value[EAGER_TRACK_TARGET] ?? value : value;
358
+ trackReactiveValue(target);
359
+ }
360
+ const revision = context.$revision;
361
+ if (typeof revision === "function")
362
+ revision();
363
+ const keys = Object.keys(context);
364
+ const vals = Object.values(context);
365
+ const fn = new Function(...keys, `"use strict"; return (${expr});`);
366
+ const result = fn(...vals);
367
+ if (path !== undefined)
368
+ lastValues.set(`${ns ?? "?"}:${path}:${expr}`, result);
369
+ return result;
370
+ } catch (e) {
371
+ const cacheKey = path !== undefined ? `${ns ?? "?"}:${path}:${expr}` : expr;
372
+ const last = lastValues.get(cacheKey);
373
+ console.warn(`[SlexKit][${ns || "?"}] $eval error at ${path || "?"}:`, expr, e.message);
374
+ if (last !== undefined)
375
+ return last;
376
+ return;
377
+ }
378
+ }
379
+ function execWrite(stmt, context, ns, path) {
380
+ try {
381
+ const keys = Object.keys(context);
382
+ const vals = Object.values(context);
383
+ const fn = new Function(...keys, `"use strict"; { ${stmt} }`);
384
+ batch(() => fn(...vals));
385
+ } catch (e) {
386
+ console.warn(`[SlexKit][${ns || "?"}] @exec error at ${path || "?"}:`, stmt, e.message);
387
+ }
388
+ }
389
+
390
+ // src/engine/component-scope.ts
391
+ var componentDisposers = new WeakMap;
392
+ var flushDom;
393
+ function configureComponentScope(options) {
394
+ flushDom = options.flush;
395
+ }
396
+ function createComponentAccessor(read) {
397
+ const subscribers = new Set;
398
+ let current = read();
399
+ const accessor = () => current;
400
+ accessor.subscribe = (run) => {
401
+ subscribers.add(run);
402
+ run(current);
403
+ const stop = createEffect(() => {
404
+ current = read();
405
+ for (const subscriber of subscribers)
406
+ subscriber(current);
407
+ flushDom?.();
408
+ });
409
+ return () => {
410
+ subscribers.delete(run);
411
+ stop();
412
+ };
413
+ };
414
+ return accessor;
415
+ }
416
+ function renderComponent(render) {
417
+ return render() ?? null;
418
+ }
419
+ function attachComponentDisposer(el, dispose) {
420
+ componentDisposers.set(el, dispose);
421
+ }
422
+ function disposeComponent(el) {
423
+ const dispose = componentDisposers.get(el);
424
+ if (!dispose)
425
+ return;
426
+ componentDisposers.delete(el);
427
+ dispose();
428
+ }
429
+
430
+ // src/engine/engineering.ts
431
+ var NUMBER_RE = /^[+-]?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+))(?:[eE][+-]?\d+)?/;
432
+ var PREFIX_FACTORS = {
433
+ p: 0.000000000001,
434
+ n: 0.000000001,
435
+ u: 0.000001,
436
+ "µ": 0.000001,
437
+ m: 0.001,
438
+ k: 1000,
439
+ K: 1000,
440
+ M: 1e6,
441
+ meg: 1e6,
442
+ G: 1e9,
443
+ T: 1000000000000
444
+ };
445
+ var PREFIXES = ["meg", "p", "n", "u", "µ", "m", "k", "K", "M", "G", "T"];
446
+ function invalid(raw, error) {
447
+ return {
448
+ raw,
449
+ number: null,
450
+ valid: false,
451
+ prefix: "",
452
+ unit: "",
453
+ normalized: "",
454
+ error
455
+ };
456
+ }
457
+ function parseSuffix(suffix) {
458
+ const compact = suffix.trim();
459
+ if (!compact)
460
+ return { prefix: "", unit: "" };
461
+ for (const prefix of PREFIXES) {
462
+ if (prefix === "meg") {
463
+ if (compact.toLowerCase().startsWith("meg")) {
464
+ return { prefix: "meg", unit: compact.slice(3).trim() };
465
+ }
466
+ continue;
467
+ }
468
+ if (compact.startsWith(prefix)) {
469
+ return { prefix, unit: compact.slice(prefix.length).trim() };
470
+ }
471
+ }
472
+ return { prefix: "", unit: compact };
473
+ }
474
+ function parseEngineeringNumber(input) {
475
+ const raw = input == null ? "" : String(input);
476
+ const source = raw.trim();
477
+ if (!source) {
478
+ return {
479
+ raw,
480
+ number: null,
481
+ valid: false,
482
+ prefix: "",
483
+ unit: "",
484
+ normalized: "",
485
+ error: "empty"
486
+ };
487
+ }
488
+ const numberMatch = source.match(NUMBER_RE);
489
+ if (!numberMatch)
490
+ return invalid(raw, "invalid_number");
491
+ const numberText = numberMatch[0];
492
+ const rest = source.slice(numberText.length).trim();
493
+ const base = Number(numberText);
494
+ if (!Number.isFinite(base))
495
+ return invalid(raw, "invalid_number");
496
+ const { prefix, unit } = parseSuffix(rest);
497
+ const multiplier = prefix ? PREFIX_FACTORS[prefix] : 1;
498
+ if (!Number.isFinite(multiplier))
499
+ return invalid(raw, "invalid_prefix");
500
+ const number = base * multiplier;
501
+ if (!Number.isFinite(number))
502
+ return invalid(raw, "out_of_range");
503
+ return {
504
+ raw,
505
+ number,
506
+ valid: true,
507
+ prefix,
508
+ unit,
509
+ normalized: `${number}${unit ? ` ${unit}` : ""}`
510
+ };
511
+ }
512
+ function isEngineeringNumberResult(value) {
513
+ return !!value && typeof value === "object" && "raw" in value && "number" in value && "valid" in value && "prefix" in value && "unit" in value && "normalized" in value;
514
+ }
515
+
516
+ // src/engine/component-state.ts
517
+ var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
518
+ var componentStateProxies = new WeakMap;
519
+ function isReadableValue(value) {
520
+ return !!value && (typeof value === "object" || typeof value === "function") && typeof value.subscribe === "function";
521
+ }
522
+ function rawRecord(value) {
523
+ return value.__slexkitRaw ?? value;
524
+ }
525
+ function componentPropName(key) {
526
+ return key.startsWith("$") ? key.slice(1) : key;
527
+ }
528
+ function isEventProp(key, value) {
529
+ return key.startsWith("on") && typeof value === "function";
530
+ }
531
+ function isWritableComponent(type) {
532
+ const mode = getComponentStateMode(type);
533
+ return mode === "value" || mode === "checked" || mode === "enabled";
534
+ }
535
+ function isStatefulComponent(type) {
536
+ return getComponentStateMode(type) !== "none";
537
+ }
538
+ function isInputStateProp(type, propName) {
539
+ const mode = getComponentStateMode(type);
540
+ if (mode === "value")
541
+ return propName === "value";
542
+ if (mode === "checked")
543
+ return propName === "value" || propName === "checked";
544
+ if (mode === "enabled")
545
+ return propName === "enabled";
546
+ return false;
547
+ }
548
+ function assignEngineeringState(state, value) {
549
+ const result = isEngineeringNumberResult(value) ? value : parseEngineeringNumber(value);
550
+ state.value = result.raw;
551
+ state.number = result.number;
552
+ state.valid = result.valid;
553
+ state.prefix = result.prefix;
554
+ state.unit = result.unit;
555
+ state.normalized = result.normalized;
556
+ if (result.error)
557
+ state.error = result.error;
558
+ else
559
+ delete state.error;
560
+ }
561
+ function clearEngineeringState(state) {
562
+ delete state.number;
563
+ delete state.valid;
564
+ delete state.prefix;
565
+ delete state.unit;
566
+ delete state.normalized;
567
+ delete state.error;
568
+ }
569
+ function assignInputType(state, inputType) {
570
+ const previousType = state.type;
571
+ state.type = inputType;
572
+ if (inputType === "engineering" && "value" in state) {
573
+ assignEngineeringState(state, state.value);
574
+ } else if (previousType === "engineering" && inputType !== "engineering") {
575
+ clearEngineeringState(state);
576
+ }
577
+ }
578
+ function assignComponentProp(state, type, propName, value, force) {
579
+ if (!force && propName in state)
580
+ return;
581
+ if (type === "input" && propName === "value" && state.type === "engineering") {
582
+ assignEngineeringState(state, value);
583
+ return;
584
+ }
585
+ if (getComponentStateMode(type) === "checked" && (propName === "checked" || propName === "value")) {
586
+ const checked = !!value;
587
+ state.checked = checked;
588
+ state.value = checked;
589
+ return;
590
+ }
591
+ if (getComponentStateMode(type) === "enabled" && propName === "enabled") {
592
+ state.enabled = !!value;
593
+ return;
594
+ }
595
+ state[propName] = value;
596
+ }
597
+ function publicComponentState(name, state, componentTypes) {
598
+ let cached = componentStateProxies.get(state);
599
+ if (!cached) {
600
+ cached = new Proxy(state, {
601
+ get(target, key, receiver) {
602
+ if (key === SKIP_EAGER_TRACK)
603
+ return true;
604
+ return Reflect.get(target, key, receiver);
605
+ },
606
+ set(target, key, value, receiver) {
607
+ const currentType = componentTypes[name] ?? "";
608
+ if (isWritableComponent(currentType)) {
609
+ if (typeof key === "string") {
610
+ assignComponentProp(target, currentType, key, value, true);
611
+ return true;
612
+ }
613
+ return Reflect.set(target, key, value, receiver);
614
+ }
615
+ console.warn(`[SlexKit] Component state '${name}' is read-only. Use dynamic $ props to update output components.`);
616
+ return true;
617
+ },
618
+ deleteProperty(target, key) {
619
+ if (isWritableComponent(componentTypes[name] ?? "")) {
620
+ return Reflect.deleteProperty(target, key);
621
+ }
622
+ console.warn(`[SlexKit] Component state '${name}' is read-only. Use dynamic $ props to update output components.`);
623
+ return true;
624
+ }
625
+ });
626
+ componentStateProxies.set(state, cached);
627
+ }
628
+ return cached;
629
+ }
630
+ function createGProxy(g, components, componentTypes) {
631
+ return new Proxy(g, {
632
+ get(target, key, receiver) {
633
+ if (key === EAGER_TRACK_TARGET)
634
+ return target;
635
+ if (typeof key === "string" && !(key in target) && key in components) {
636
+ return publicComponentState(key, components[key], componentTypes);
637
+ }
638
+ return Reflect.get(target, key, receiver);
639
+ },
640
+ set(target, key, value, receiver) {
641
+ return Reflect.set(target, key, value, receiver);
642
+ },
643
+ has(target, key) {
644
+ return key in target || key in components;
645
+ }
646
+ });
647
+ }
648
+ function ensureComponentState(name, type, components, componentTypes) {
649
+ if (!components[name])
650
+ components[name] = {};
651
+ componentTypes[name] = type;
652
+ return components[name];
653
+ }
654
+ function syncReadableComponentProp(type, state, propName, value) {
655
+ if (isWritableComponent(type)) {
656
+ const unsubscribe2 = value.subscribe((next) => {
657
+ assignComponentProp(state, type, propName, next, true);
658
+ });
659
+ onCleanup(unsubscribe2);
660
+ return;
661
+ }
662
+ let unsubscribe;
663
+ let cancelled = false;
664
+ queueMicrotask(() => {
665
+ if (cancelled)
666
+ return;
667
+ unsubscribe = value.subscribe((next) => {
668
+ assignComponentProp(state, type, propName, next, true);
669
+ });
670
+ });
671
+ onCleanup(() => {
672
+ cancelled = true;
673
+ unsubscribe?.();
674
+ });
675
+ }
676
+ function syncComponentProps(type, name, props, components, componentTypes) {
677
+ if (!name || !isStatefulComponent(type))
678
+ return;
679
+ const state = ensureComponentState(name, type, components, componentTypes);
680
+ if (type === "input" && typeof props.type === "string") {
681
+ assignInputType(state, props.type);
682
+ }
683
+ for (const [key, value] of Object.entries(props)) {
684
+ if (isEventProp(key, value))
685
+ continue;
686
+ const propName = componentPropName(key);
687
+ const inputStateProp = isInputStateProp(type, propName);
688
+ if (isReadableValue(value)) {
689
+ syncReadableComponentProp(type, state, propName, value);
690
+ } else {
691
+ assignComponentProp(state, type, propName, value, !inputStateProp);
692
+ }
693
+ }
694
+ return state;
695
+ }
696
+ function bindInputStateProps(type, state, props) {
697
+ if (!state)
698
+ return;
699
+ const mode = getComponentStateMode(type);
700
+ if (mode === "value") {
701
+ props.value = createComponentAccessor(() => state.value);
702
+ } else if (mode === "checked") {
703
+ const checked = createComponentAccessor(() => !!(state.checked ?? state.value));
704
+ props.checked = checked;
705
+ props.value = checked;
706
+ } else if (mode === "enabled") {
707
+ props.enabled = createComponentAccessor(() => !!state.enabled);
708
+ }
709
+ }
710
+ function applyComponentEventState(type, name, data, components, componentTypes) {
711
+ if (!name || !isWritableComponent(type))
712
+ return;
713
+ const state = ensureComponentState(name, type, components, componentTypes);
714
+ const mode = getComponentStateMode(type);
715
+ if (mode === "checked") {
716
+ const checked = !!data;
717
+ state.checked = checked;
718
+ state.value = checked;
719
+ } else if (mode === "enabled") {
720
+ state.enabled = !!data;
721
+ } else if (type === "input" && state.type === "engineering") {
722
+ assignEngineeringState(state, data);
723
+ } else {
724
+ state.value = data;
725
+ }
726
+ }
727
+ function seedStaticComponentState(type, state, props) {
728
+ if (type === "input" && typeof props.type === "string") {
729
+ assignInputType(state, props.type);
730
+ }
731
+ for (const [key, value] of Object.entries(props)) {
732
+ if (key.startsWith("$") || isEventProp(key, value))
733
+ continue;
734
+ const propName = componentPropName(key);
735
+ if (isInputStateProp(type, propName)) {
736
+ assignComponentProp(state, type, propName, value, false);
737
+ }
738
+ }
739
+ }
740
+ function warnDuplicateState(ns, name, currentType, currentPath, previous) {
741
+ console.warn(`[SlexKit][${ns}] Component state '${name}' is declared more than once at ${previous.path} and ${currentPath}; state is shared by namespace and component name.`);
742
+ if (previous.type !== currentType) {
743
+ console.warn(`[SlexKit][${ns}] Component state '${name}' is used by multiple component types (${previous.type}, ${currentType}); the latest rendered type controls write behavior.`);
744
+ }
745
+ }
746
+ function warnForState(ns, name, path) {
747
+ console.warn(`[SlexKit][${ns}] Component state '${name}' is used with $for at ${path}; repeated items share one namespace-level instance state.`);
748
+ }
749
+ function prepareComponentStatesInner(layout, components, componentTypes, ns, seen, parentPath = "") {
750
+ if (!layout || typeof layout !== "object")
751
+ return;
752
+ for (const [key, val] of Object.entries(layout)) {
753
+ if (!key.includes(":") || typeof val !== "object" || val === null)
754
+ continue;
755
+ const [type, name] = key.split(":");
756
+ const props = val;
757
+ const path = parentPath ? `${parentPath}.${key}` : key;
758
+ if (name && isStatefulComponent(type)) {
759
+ const previous = seen.get(name);
760
+ if (previous)
761
+ warnDuplicateState(ns, name, type, path, previous);
762
+ else
763
+ seen.set(name, { type, path });
764
+ if (props.$for && isWritableComponent(type))
765
+ warnForState(ns, name, path);
766
+ const state = ensureComponentState(name, type, components, componentTypes);
767
+ seedStaticComponentState(type, state, props);
768
+ }
769
+ prepareComponentStatesInner(props, components, componentTypes, ns, seen, path);
770
+ }
771
+ }
772
+ function prepareComponentStates(layout, components, componentTypes, ns) {
773
+ prepareComponentStatesInner(layout, components, componentTypes, ns, new Map);
774
+ }
775
+ function buildComponentEvalContext(g, components, componentTypes, api, forCtx) {
776
+ const ctx = { g: createGProxy(g, components, componentTypes) };
777
+ for (const name of Object.keys(rawRecord(components))) {
778
+ if (IDENTIFIER.test(name)) {
779
+ ctx[name] = publicComponentState(name, components[name], componentTypes);
780
+ }
781
+ }
782
+ if (api)
783
+ ctx.api = api;
784
+ if (forCtx) {
785
+ for (const k of Object.keys(forCtx)) {
786
+ Object.defineProperty(ctx, k, {
787
+ get: () => {
788
+ const current = forCtx[k];
789
+ return k === "$index" && typeof current === "function" ? current() : current;
790
+ },
791
+ enumerable: true
792
+ });
793
+ }
794
+ }
795
+ return ctx;
796
+ }
797
+
798
+ // src/engine/renderer.ts
799
+ var FALLBACK_CSS = "background:var(--muted);border:1px solid var(--border);border-radius:calc(var(--radius) - 2px);padding:0.5rem;text-align:center;font-size:0.75rem;color:var(--muted-foreground)";
800
+ var defaultRenderOptions = {
801
+ dir: "ltr",
802
+ labels: {}
803
+ };
804
+ function hasComponentKey(obj) {
805
+ return Object.keys(obj).some((k) => k.includes(":"));
806
+ }
807
+ function separatePropsAndChildren(obj, props, children) {
808
+ for (const [k, v] of Object.entries(obj)) {
809
+ if (k === "$if" || k === "$for" || k === "$key")
810
+ continue;
811
+ if (k.includes(":") || typeof v === "object" && v !== null && Object.keys(v).length > 0 && hasComponentKey(v)) {
812
+ children[k] = v;
813
+ } else {
814
+ props[k] = v;
815
+ }
816
+ }
817
+ }
818
+ function callHook(g, name, type) {
819
+ const key = name ? `${type}_${name}` : `${type}_`;
820
+ const fn = g[key];
821
+ if (typeof fn === "function") {
822
+ fn.call(g);
823
+ }
824
+ }
825
+ function applyEnterAnimation(el, props) {
826
+ const enterFn = props.$enter;
827
+ if (typeof enterFn !== "function")
828
+ return;
829
+ const animClass = enterFn();
830
+ if (animClass && typeof animClass === "string") {
831
+ el.classList.add(animClass);
832
+ el.addEventListener("animationend", () => el.classList.remove(animClass), { once: true });
833
+ }
834
+ }
835
+ function applyLeaveAnimation(el, props, callback) {
836
+ const leaveFn = props.$leave;
837
+ if (typeof leaveFn !== "function") {
838
+ callback();
839
+ return;
840
+ }
841
+ const animClass = leaveFn();
842
+ if (animClass && typeof animClass === "string") {
843
+ el.classList.add(animClass);
844
+ el.addEventListener("animationend", () => {
845
+ callback();
846
+ }, { once: true });
847
+ } else {
848
+ callback();
849
+ }
850
+ }
851
+ function renderWithFallback(renderer, props, name, ctx, fullKey, ns) {
852
+ try {
853
+ return renderComponent(() => renderer(props, name, ctx));
854
+ } catch (e) {
855
+ console.warn(`[SlexKit][${ns}] Render error at ${fullKey}:`, e.message);
856
+ const fb = (ctx.document || document).createElement("div");
857
+ fb.className = "slex-render-error";
858
+ fb.title = fullKey;
859
+ fb.setAttribute("style", FALLBACK_CSS);
860
+ return fb;
861
+ }
862
+ }
863
+ function resolveDynamicProps(props, g, components, componentTypes, api, forCtx, ns, fullKey) {
864
+ const evalCtx = buildComponentEvalContext(g, components, componentTypes, api, forCtx);
865
+ for (const [k, v] of Object.entries(props)) {
866
+ if (k.startsWith("$") && !k.startsWith("$if") && !k.startsWith("$for") && !k.startsWith("$key") && typeof v === "string") {
867
+ const path = `${fullKey}:${k}`;
868
+ const memo = createMemo(() => evalRead(v, evalCtx, ns, path));
869
+ props[k] = createComponentAccessor(memo);
870
+ } else if (k.startsWith("on") && typeof v === "string") {
871
+ const stmt = v;
872
+ const path = `${fullKey}:${k}`;
873
+ props[k] = ($event) => execWrite(stmt, { ...evalCtx, $event: $event ?? null }, ns, path);
874
+ }
875
+ }
876
+ }
877
+ function resolveKeyValue(item, itemIndex, $keyProp) {
878
+ if ($keyProp) {
879
+ if ($keyProp === "$value") {
880
+ return item;
881
+ }
882
+ if ($keyProp === "id" || $keyProp !== "id" && item && typeof item === "object") {
883
+ return item?.[$keyProp];
884
+ }
885
+ }
886
+ if (item && typeof item === "object" && "id" in item) {
887
+ return item.id;
888
+ }
889
+ const primitiveTypes = ["string", "number", "boolean"];
890
+ if (primitiveTypes.includes(typeof item)) {
891
+ console.warn("[SlexKit] $for with primitive array items but no $key specified. Use '$key: $value' for primitive arrays. Falling back to index.");
892
+ return itemIndex;
893
+ }
894
+ console.warn("[SlexKit] $for array item has no 'id' property and no $key specified. Falling back to index.");
895
+ return itemIndex;
896
+ }
897
+ function trackForCollection(value) {
898
+ if (!Array.isArray(value))
899
+ return value;
900
+ Reflect.get(value, "length");
901
+ for (const item of value) {
902
+ if (item && typeof item === "object") {
903
+ for (const key of Object.keys(item)) {
904
+ Reflect.get(item, key);
905
+ }
906
+ }
907
+ }
908
+ return value;
909
+ }
910
+ function renderIfNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options) {
911
+ const [type, name] = fullKey.split(":");
912
+ const renderer = getRenderer(type);
913
+ if (!renderer)
914
+ return;
915
+ const evalCtx = buildComponentEvalContext(g, components, componentTypes, api, forCtx);
916
+ const show = createMemo(() => evalRead(props.$if, evalCtx, ns, `${fullKey}:$if`));
917
+ let current = null;
918
+ const leavingInstances = new Set;
919
+ let mounting = false;
920
+ let emit;
921
+ const disposeInstance = (instance) => {
922
+ if (instance.disposed)
923
+ return;
924
+ instance.disposed = true;
925
+ leavingInstances.delete(instance);
926
+ if (current === instance)
927
+ current = null;
928
+ disposeComponent(instance.el);
929
+ instance.dispose();
930
+ callHook(g, name, "onUnmount");
931
+ instance.el.remove();
932
+ };
933
+ createEffect(() => {
934
+ if (show()) {
935
+ if (!current && !mounting) {
936
+ mounting = true;
937
+ let innerProps;
938
+ let innerChildren;
939
+ let currentEl;
940
+ const dispose = createRoot((_dispose) => {
941
+ innerProps = {};
942
+ innerChildren = {};
943
+ separatePropsAndChildren(props, innerProps, innerChildren);
944
+ resolveDynamicProps(innerProps, g, components, componentTypes, api, forCtx, ns, fullKey);
945
+ const componentState = syncComponentProps(type, name, innerProps, components, componentTypes);
946
+ bindInputStateProps(type, componentState, innerProps);
947
+ emit = (event, data) => {
948
+ if (event === "change")
949
+ applyComponentEventState(type, name, data, components, componentTypes);
950
+ const h = innerProps[`on${event}`];
951
+ if (typeof h === "function")
952
+ h(data);
953
+ };
954
+ currentEl = renderWithFallback(renderer, innerProps, name, {
955
+ g,
956
+ api,
957
+ dir: options.dir,
958
+ labels: options.labels,
959
+ document: container.ownerDocument || document,
960
+ forCtx,
961
+ children: innerChildren,
962
+ id: name || null,
963
+ emit,
964
+ renderTree: (layout, _container, childForCtx) => renderTree(layout, _container, g, components, componentTypes, childForCtx ?? forCtx, ns, api, options, false)
965
+ }, fullKey, ns);
966
+ return _dispose;
967
+ });
968
+ mounting = false;
969
+ if (currentEl) {
970
+ container.appendChild(currentEl);
971
+ applyEnterAnimation(currentEl, innerProps);
972
+ callHook(g, name, "onMount");
973
+ current = {
974
+ el: currentEl,
975
+ props: innerProps,
976
+ dispose,
977
+ disposed: false
978
+ };
979
+ } else {
980
+ dispose();
981
+ }
982
+ }
983
+ } else {
984
+ if (current) {
985
+ const instance = current;
986
+ current = null;
987
+ leavingInstances.add(instance);
988
+ applyLeaveAnimation(instance.el, instance.props, () => {
989
+ disposeInstance(instance);
990
+ });
991
+ }
992
+ }
993
+ });
994
+ onCleanup(() => {
995
+ if (current)
996
+ disposeInstance(current);
997
+ for (const instance of Array.from(leavingInstances))
998
+ disposeInstance(instance);
999
+ });
1000
+ }
1001
+ function renderAndMountSlot(item, index, keyVal, indexSignal, revisionSignal, renderer, type, name, props, container, g, components, componentTypes, api, forCtx, ns, fullKey, options) {
1002
+ indexSignal[1](index);
1003
+ const reactiveItem = asReactiveValue(item, g);
1004
+ const innerForCtx = {
1005
+ ...forCtx,
1006
+ $item: reactiveItem,
1007
+ $index: indexSignal[0],
1008
+ $key: keyVal,
1009
+ $revision: revisionSignal[0]
1010
+ };
1011
+ if (name) {
1012
+ innerForCtx[name] = reactiveItem;
1013
+ }
1014
+ let innerProps;
1015
+ let innerChildren;
1016
+ let el;
1017
+ let fEmit;
1018
+ const dispose = createRoot((_dispose) => {
1019
+ innerProps = {};
1020
+ innerChildren = {};
1021
+ separatePropsAndChildren(props, innerProps, innerChildren);
1022
+ resolveDynamicProps(innerProps, g, components, componentTypes, api, innerForCtx, ns, fullKey);
1023
+ const componentState = syncComponentProps(type, name, innerProps, components, componentTypes);
1024
+ bindInputStateProps(type, componentState, innerProps);
1025
+ fEmit = (event, data) => {
1026
+ if (event === "change")
1027
+ applyComponentEventState(type, name, data, components, componentTypes);
1028
+ const h = innerProps[`on${event}`];
1029
+ if (typeof h === "function")
1030
+ h(data);
1031
+ };
1032
+ el = renderWithFallback(renderer, innerProps, name, {
1033
+ g,
1034
+ api,
1035
+ dir: options.dir,
1036
+ labels: options.labels,
1037
+ document: container.ownerDocument || document,
1038
+ forCtx: innerForCtx,
1039
+ children: innerChildren,
1040
+ id: name || null,
1041
+ emit: fEmit,
1042
+ renderTree: (layout, _container, childForCtx) => renderTree(layout, _container, g, components, componentTypes, childForCtx ?? innerForCtx, ns, api, options, false)
1043
+ }, `${fullKey}[${index}]`, ns);
1044
+ return _dispose;
1045
+ });
1046
+ return {
1047
+ key: keyVal,
1048
+ el,
1049
+ forCtx: innerForCtx,
1050
+ index,
1051
+ item: reactiveItem,
1052
+ props: innerProps,
1053
+ children: innerChildren,
1054
+ indexSignal,
1055
+ revision: 0,
1056
+ revisionSignal,
1057
+ dispose
1058
+ };
1059
+ }
1060
+ function renderForNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options) {
1061
+ const [type, name] = fullKey.split(":");
1062
+ const renderer = getRenderer(type);
1063
+ if (!renderer)
1064
+ return;
1065
+ const forWrapper = (container.ownerDocument || document).createElement("div");
1066
+ forWrapper.className = "slexkit-for-wrapper";
1067
+ container.appendChild(forWrapper);
1068
+ const evalCtx = buildComponentEvalContext(g, components, componentTypes, api, forCtx);
1069
+ const items = createMemo(() => trackForCollection(evalRead(props.$for, evalCtx, ns, `${fullKey}:$for`)));
1070
+ const $keyProp = props.$key;
1071
+ const slotMap = new Map;
1072
+ const leavingSlots = new Set;
1073
+ const disposedSlots = new WeakSet;
1074
+ const disposeSlot = (slot) => {
1075
+ if (disposedSlots.has(slot))
1076
+ return;
1077
+ disposedSlots.add(slot);
1078
+ leavingSlots.delete(slot);
1079
+ callHook(g, name, "onUnmount");
1080
+ disposeComponent(slot.el);
1081
+ slot.el.remove();
1082
+ if (slot.dispose)
1083
+ slot.dispose();
1084
+ };
1085
+ createEffect(() => {
1086
+ const arr = items();
1087
+ if (!Array.isArray(arr)) {
1088
+ if (slotMap.size > 0) {
1089
+ for (const [, slot] of slotMap) {
1090
+ disposeSlot(slot);
1091
+ }
1092
+ slotMap.clear();
1093
+ }
1094
+ return;
1095
+ }
1096
+ const newKeys = arr.map((item, i) => resolveKeyValue(item, i, $keyProp));
1097
+ const newKeySet = new Set(newKeys);
1098
+ const deletedSlots = [];
1099
+ for (const [oldKey, slot] of slotMap) {
1100
+ if (!newKeySet.has(oldKey)) {
1101
+ deletedSlots.push(slot);
1102
+ slotMap.delete(oldKey);
1103
+ }
1104
+ }
1105
+ for (const slot of deletedSlots) {
1106
+ container.appendChild(slot.el);
1107
+ leavingSlots.add(slot);
1108
+ applyLeaveAnimation(slot.el, slot.props, () => {
1109
+ disposeSlot(slot);
1110
+ });
1111
+ }
1112
+ arr.forEach((item, index) => {
1113
+ item = asReactiveValue(item, g);
1114
+ const keyVal = newKeys[index];
1115
+ let slot = slotMap.get(keyVal);
1116
+ if (slot) {
1117
+ const indexChanged = slot.index !== index;
1118
+ const itemChanged = slot.item !== item;
1119
+ slot.forCtx.$item = item;
1120
+ slot.forCtx.$key = keyVal;
1121
+ if (name)
1122
+ slot.forCtx[name] = item;
1123
+ slot.index = index;
1124
+ slot.item = item;
1125
+ if (slot.revisionSignal) {
1126
+ slot.revision = (slot.revision ?? 0) + 1;
1127
+ slot.revisionSignal[1](slot.revision);
1128
+ }
1129
+ if (indexChanged || itemChanged) {
1130
+ if (indexChanged && slot.indexSignal) {
1131
+ slot.indexSignal[1](index);
1132
+ }
1133
+ callHook(g, name, "onUpdate");
1134
+ }
1135
+ } else {
1136
+ const indexSignal = createSignal(index);
1137
+ const revisionSignal = createSignal(0);
1138
+ slot = renderAndMountSlot(item, index, keyVal, indexSignal, revisionSignal, renderer, type, name, props, container, g, components, componentTypes, api, forCtx, ns, fullKey, options);
1139
+ if (slot.el) {
1140
+ applyEnterAnimation(slot.el, slot.props);
1141
+ callHook(g, name, "onMount");
1142
+ }
1143
+ slotMap.set(keyVal, slot);
1144
+ }
1145
+ const refChild = forWrapper.children[index];
1146
+ if (slot.el && refChild !== slot.el) {
1147
+ forWrapper.insertBefore(slot.el, refChild ?? null);
1148
+ }
1149
+ });
1150
+ while (forWrapper.children.length > arr.length) {
1151
+ forWrapper.lastChild.remove();
1152
+ }
1153
+ });
1154
+ onCleanup(() => {
1155
+ for (const slot of Array.from(slotMap.values()))
1156
+ disposeSlot(slot);
1157
+ slotMap.clear();
1158
+ for (const slot of Array.from(leavingSlots))
1159
+ disposeSlot(slot);
1160
+ forWrapper.remove();
1161
+ });
1162
+ }
1163
+ function renderNormalNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options) {
1164
+ const [type, name] = fullKey.split(":");
1165
+ const renderer = getRenderer(type);
1166
+ if (!renderer)
1167
+ return;
1168
+ const nodeProps = {};
1169
+ const nodeChildren = {};
1170
+ separatePropsAndChildren(props, nodeProps, nodeChildren);
1171
+ resolveDynamicProps(nodeProps, g, components, componentTypes, api, forCtx, ns, fullKey);
1172
+ const componentState = syncComponentProps(type, name, nodeProps, components, componentTypes);
1173
+ bindInputStateProps(type, componentState, nodeProps);
1174
+ const nEmit = (event, data) => {
1175
+ if (event === "change")
1176
+ applyComponentEventState(type, name, data, components, componentTypes);
1177
+ const h = nodeProps[`on${event}`];
1178
+ if (typeof h === "function")
1179
+ h(data);
1180
+ };
1181
+ const el = renderWithFallback(renderer, nodeProps, name, {
1182
+ g,
1183
+ api,
1184
+ dir: options.dir,
1185
+ labels: options.labels,
1186
+ document: container.ownerDocument || document,
1187
+ forCtx,
1188
+ children: nodeChildren,
1189
+ id: name || null,
1190
+ emit: nEmit,
1191
+ renderTree: (layout, _container, childForCtx) => renderTree(layout, _container, g, components, componentTypes, childForCtx ?? forCtx, ns, api, options, false)
1192
+ }, fullKey, ns);
1193
+ if (el) {
1194
+ container.appendChild(el);
1195
+ applyEnterAnimation(el, nodeProps);
1196
+ callHook(g, name, "onMount");
1197
+ onCleanup(() => callHook(g, name, "onUnmount"));
1198
+ onCleanup(() => disposeComponent(el));
1199
+ }
1200
+ }
1201
+ function renderNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options) {
1202
+ if (!fullKey || !fullKey.includes(":"))
1203
+ return;
1204
+ if (props.$if) {
1205
+ renderIfNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options);
1206
+ } else if (props.$for) {
1207
+ renderForNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options);
1208
+ } else {
1209
+ renderNormalNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options);
1210
+ }
1211
+ }
1212
+ function renderTree(layout, container, g, components, componentTypes, forCtx, ns = "?", api, options = defaultRenderOptions, prepare = true) {
1213
+ if (!layout || typeof layout !== "object")
1214
+ return;
1215
+ if (prepare)
1216
+ prepareComponentStates(layout, components, componentTypes, ns);
1217
+ for (const [key, val] of Object.entries(layout)) {
1218
+ if (!key.includes(":"))
1219
+ continue;
1220
+ if (typeof val === "object" && val !== null) {
1221
+ renderNode(key, val, container, g, components, componentTypes, api, forCtx, ns, options);
1222
+ }
1223
+ }
1224
+ }
1225
+
1226
+ // src/engine/diagnostics.ts
1227
+ class SlexKitSyntaxError extends SyntaxError {
1228
+ diagnostic;
1229
+ constructor(diagnostic) {
1230
+ super(formatSlexKitDiagnostic(diagnostic));
1231
+ this.name = "SlexKitSyntaxError";
1232
+ this.diagnostic = diagnostic;
1233
+ }
1234
+ }
1235
+ function excerpt(source, line, column) {
1236
+ const lines = source.split(`
1237
+ `);
1238
+ const start = Math.max(1, line - 2);
1239
+ const end = Math.min(lines.length, line + 2);
1240
+ const width = String(end).length;
1241
+ const rows = [];
1242
+ for (let current = start;current <= end; current += 1) {
1243
+ const marker = current === line ? ">" : " ";
1244
+ rows.push(`${marker} ${String(current).padStart(width, " ")} | ${lines[current - 1] ?? ""}`);
1245
+ if (current === line) {
1246
+ rows.push(` ${" ".repeat(width)} | ${" ".repeat(Math.max(0, column - 1))}^`);
1247
+ }
1248
+ }
1249
+ return rows.join(`
1250
+ `);
1251
+ }
1252
+ function lineColumnAt(source, index) {
1253
+ const prefix = source.slice(0, Math.max(0, index));
1254
+ const lines = prefix.split(`
1255
+ `);
1256
+ return {
1257
+ line: lines.length,
1258
+ column: lines[lines.length - 1].length + 1
1259
+ };
1260
+ }
1261
+ function stackLine(error) {
1262
+ if (!(error instanceof Error) || !error.stack)
1263
+ return null;
1264
+ const match = error.stack.match(/<parse>\s+\(:(\d+)\)/);
1265
+ if (!match)
1266
+ return null;
1267
+ const parsed = Number(match[1]);
1268
+ return Number.isFinite(parsed) ? Math.max(1, parsed - 2) : null;
1269
+ }
1270
+ function scanSource(source) {
1271
+ const chars = [];
1272
+ let quote = "";
1273
+ let escaped = false;
1274
+ let line = 1;
1275
+ let column = 0;
1276
+ for (let index = 0;index < source.length; index += 1) {
1277
+ const char = source[index];
1278
+ const next = source[index + 1];
1279
+ column += 1;
1280
+ if (char === `
1281
+ `) {
1282
+ line += 1;
1283
+ column = 0;
1284
+ if (quote === "//")
1285
+ quote = "";
1286
+ continue;
1287
+ }
1288
+ if (quote) {
1289
+ if (quote === "/*" && char === "*" && next === "/") {
1290
+ quote = "";
1291
+ index += 1;
1292
+ column += 1;
1293
+ } else if (quote !== "/*" && quote !== "//" && !escaped && char === quote) {
1294
+ quote = "";
1295
+ }
1296
+ escaped = !escaped && quote !== "/*" && quote !== "//" && char === "\\";
1297
+ if (char !== "\\")
1298
+ escaped = false;
1299
+ continue;
1300
+ }
1301
+ if (char === "/" && next === "/") {
1302
+ quote = "//";
1303
+ index += 1;
1304
+ column += 1;
1305
+ continue;
1306
+ }
1307
+ if (char === "/" && next === "*") {
1308
+ quote = "/*";
1309
+ index += 1;
1310
+ column += 1;
1311
+ continue;
1312
+ }
1313
+ if (char === '"' || char === "'" || char === "`") {
1314
+ quote = char;
1315
+ escaped = false;
1316
+ continue;
1317
+ }
1318
+ chars.push({ char, index, line, column });
1319
+ }
1320
+ return chars;
1321
+ }
1322
+ function tokenDiagnostic(source, message) {
1323
+ const chars = scanSource(source);
1324
+ const unexpectedString = message.match(/Unexpected string(?: literal)?(?: "([^"]+)")?/);
1325
+ if (unexpectedString) {
1326
+ const literal = unexpectedString[1];
1327
+ if (literal) {
1328
+ const quoted = [`"${literal}"`, `'${literal}'`, `\`${literal}\``];
1329
+ const index = quoted.map((candidate) => source.indexOf(candidate)).filter((candidate) => candidate >= 0).sort((a, b) => a - b)[0];
1330
+ if (index !== undefined)
1331
+ return lineColumnAt(source, index);
1332
+ }
1333
+ const suspiciousString = source.match(/[}\])"']\s*(['"`])/);
1334
+ if (suspiciousString?.index !== undefined) {
1335
+ const quoteIndex = source.indexOf(suspiciousString[1], suspiciousString.index + 1);
1336
+ if (quoteIndex >= 0)
1337
+ return lineColumnAt(source, quoteIndex);
1338
+ }
1339
+ }
1340
+ if (message.includes("Unexpected token ':'")) {
1341
+ const doubleColon = chars.find((item, index) => item.char === ":" && chars[index - 1]?.char === ":");
1342
+ if (doubleColon)
1343
+ return doubleColon;
1344
+ const suspiciousColon = chars.find((item, index) => {
1345
+ if (item.char !== ":")
1346
+ return false;
1347
+ const prev = chars[index - 1]?.char;
1348
+ return prev === '"' || prev === "'" || prev === "}" || prev === "]" || prev === ")";
1349
+ });
1350
+ if (suspiciousColon)
1351
+ return suspiciousColon;
1352
+ }
1353
+ return null;
1354
+ }
1355
+ function delimiterDiagnostic(source) {
1356
+ const stack = [];
1357
+ const pairs = { "}": "{", "]": "[", ")": "(" };
1358
+ for (const item of scanSource(source)) {
1359
+ if (item.char === "{" || item.char === "[" || item.char === "(") {
1360
+ stack.push(item);
1361
+ continue;
1362
+ }
1363
+ if (item.char === "}" || item.char === "]" || item.char === ")") {
1364
+ const expected = pairs[item.char];
1365
+ const opener2 = stack.pop();
1366
+ if (!opener2 || opener2.char !== expected) {
1367
+ return {
1368
+ line: item.line,
1369
+ column: item.column,
1370
+ detail: `Unexpected closing delimiter ${item.char}.`
1371
+ };
1372
+ }
1373
+ }
1374
+ }
1375
+ const opener = stack.at(-1);
1376
+ if (!opener)
1377
+ return null;
1378
+ const end = lineColumnAt(source, source.length);
1379
+ return {
1380
+ line: end.line,
1381
+ column: end.column,
1382
+ detail: `Expected closing delimiter for ${opener.char} opened at line ${opener.line}, column ${opener.column}.`
1383
+ };
1384
+ }
1385
+ function locateSyntaxError(source, error) {
1386
+ const rawMessage = error instanceof Error ? error.message : String(error);
1387
+ const delimiter = delimiterDiagnostic(source);
1388
+ const token = tokenDiagnostic(source, rawMessage);
1389
+ const stack = stackLine(error);
1390
+ const position = token ?? (delimiter && rawMessage.includes("Expected") ? delimiter : null) ?? (stack ? { line: stack, column: 1 } : null) ?? delimiter ?? { line: 1, column: 1 };
1391
+ const detail = token ? "The parser failed at this token." : delimiter?.detail;
1392
+ return {
1393
+ message: rawMessage,
1394
+ line: position.line,
1395
+ column: position.column,
1396
+ detail,
1397
+ excerpt: excerpt(source, position.line, position.column)
1398
+ };
1399
+ }
1400
+ function readStringLiteral(source, start) {
1401
+ const quote = source[start];
1402
+ if (quote !== '"' && quote !== "'" && quote !== "`")
1403
+ return null;
1404
+ let escaped = false;
1405
+ for (let index = start + 1;index < source.length; index += 1) {
1406
+ const char = source[index];
1407
+ if (!escaped && char === quote) {
1408
+ return { end: index + 1, value: source.slice(start, index + 1) };
1409
+ }
1410
+ escaped = !escaped && char === "\\";
1411
+ if (char !== "\\")
1412
+ escaped = false;
1413
+ }
1414
+ return null;
1415
+ }
1416
+ function skipWhitespace(source, index) {
1417
+ let cursor = index;
1418
+ while (cursor < source.length && /\s/.test(source[cursor]))
1419
+ cursor += 1;
1420
+ return cursor;
1421
+ }
1422
+ function findExpressionEnd(source, start) {
1423
+ const stack = [];
1424
+ let quote = "";
1425
+ let escaped = false;
1426
+ for (let index = start;index < source.length; index += 1) {
1427
+ const char = source[index];
1428
+ const next = source[index + 1];
1429
+ if (quote) {
1430
+ if (quote === "//" && char === `
1431
+ `) {
1432
+ quote = "";
1433
+ } else if (quote === "/*" && char === "*" && next === "/") {
1434
+ quote = "";
1435
+ index += 1;
1436
+ } else if (quote !== "//" && quote !== "/*" && !escaped && char === quote) {
1437
+ quote = "";
1438
+ }
1439
+ escaped = !escaped && quote !== "//" && quote !== "/*" && char === "\\";
1440
+ if (char !== "\\")
1441
+ escaped = false;
1442
+ continue;
1443
+ }
1444
+ if (char === "/" && next === "/") {
1445
+ quote = "//";
1446
+ index += 1;
1447
+ continue;
1448
+ }
1449
+ if (char === "/" && next === "*") {
1450
+ quote = "/*";
1451
+ index += 1;
1452
+ continue;
1453
+ }
1454
+ if (char === '"' || char === "'" || char === "`") {
1455
+ quote = char;
1456
+ escaped = false;
1457
+ continue;
1458
+ }
1459
+ if (char === "{" || char === "[" || char === "(") {
1460
+ stack.push(char);
1461
+ continue;
1462
+ }
1463
+ if (char === "}" || char === "]" || char === ")") {
1464
+ if (stack.length === 0)
1465
+ return index;
1466
+ stack.pop();
1467
+ continue;
1468
+ }
1469
+ if (char === "," && stack.length === 0)
1470
+ return index;
1471
+ }
1472
+ return source.length;
1473
+ }
1474
+ function isSingleStringLiteral(source) {
1475
+ const start = skipWhitespace(source, 0);
1476
+ const literal = readStringLiteral(source, start);
1477
+ return Boolean(literal && skipWhitespace(source, literal.end) === source.length);
1478
+ }
1479
+ function transformDynamicPropExpressions(source) {
1480
+ const edits = [];
1481
+ const keyPattern = /(^|[,{]\s*)(\$[A-Za-z_$][\w$]*)\s*:/g;
1482
+ let match;
1483
+ while (match = keyPattern.exec(source)) {
1484
+ const colon = source.indexOf(":", match.index + match[1].length);
1485
+ const valueStart = skipWhitespace(source, colon + 1);
1486
+ const valueEnd = findExpressionEnd(source, valueStart);
1487
+ const rawValue = source.slice(valueStart, valueEnd);
1488
+ if (!rawValue.trim() || isSingleStringLiteral(rawValue))
1489
+ continue;
1490
+ edits.push({
1491
+ start: valueStart,
1492
+ end: valueEnd,
1493
+ value: JSON.stringify(rawValue.trim())
1494
+ });
1495
+ }
1496
+ if (edits.length === 0)
1497
+ return source;
1498
+ let transformed = source;
1499
+ for (let index = edits.length - 1;index >= 0; index -= 1) {
1500
+ const edit = edits[index];
1501
+ transformed = `${transformed.slice(0, edit.start)}${edit.value}${transformed.slice(edit.end)}`;
1502
+ }
1503
+ return transformed;
1504
+ }
1505
+ function formatSlexKitDiagnostic(diagnostic) {
1506
+ const detail = diagnostic.detail ? `
1507
+ ${diagnostic.detail}` : "";
1508
+ return `${diagnostic.message} at line ${diagnostic.line}, column ${diagnostic.column}.${detail}
1509
+ ${diagnostic.excerpt}`;
1510
+ }
1511
+ function diagnoseSlexKitSource(source, error) {
1512
+ return locateSyntaxError(source, error);
1513
+ }
1514
+ function parseSlexSource(source) {
1515
+ try {
1516
+ const parseSource = transformDynamicPropExpressions(source);
1517
+ return {
1518
+ ok: true,
1519
+ value: new Function(`"use strict";
1520
+ return (
1521
+ ${parseSource}
1522
+ );`)()
1523
+ };
1524
+ } catch (error) {
1525
+ const diagnostic = diagnoseSlexKitSource(source, error);
1526
+ return {
1527
+ ok: false,
1528
+ diagnostic,
1529
+ error: new SlexKitSyntaxError(diagnostic)
1530
+ };
1531
+ }
1532
+ }
1533
+ var parseSlexKitDsl = parseSlexSource;
1534
+
1535
+ // src/version.ts
1536
+ var SLEXKIT_VERSION = "0.2.0";
1537
+ var SLEX_PROTOCOL_VERSION = "0.1";
1538
+ var SLEXKIT_COMPONENTS_VERSION = SLEXKIT_VERSION;
1539
+ function getSlexKitInfo() {
1540
+ return {
1541
+ version: SLEXKIT_VERSION,
1542
+ protocolVersion: SLEX_PROTOCOL_VERSION,
1543
+ componentsVersion: SLEXKIT_COMPONENTS_VERSION
1544
+ };
1545
+ }
1546
+
1547
+ // src/engine/secure-runtime.ts
1548
+ class SlexKitRuntimeError extends Error {
1549
+ kind;
1550
+ code;
1551
+ elapsedMs;
1552
+ constructor(kind, code, message, elapsedMs) {
1553
+ super(message);
1554
+ this.name = "SlexKitRuntimeError";
1555
+ this.kind = kind;
1556
+ this.code = code;
1557
+ this.elapsedMs = elapsedMs;
1558
+ }
1559
+ }
1560
+ function runtimeNow(adapter) {
1561
+ if (adapter?.now)
1562
+ return adapter.now();
1563
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
1564
+ return performance.now();
1565
+ }
1566
+ return Date.now();
1567
+ }
1568
+ function currentOrigin() {
1569
+ if (typeof location !== "undefined" && location.origin)
1570
+ return location.origin;
1571
+ if (typeof window !== "undefined" && window.location?.origin)
1572
+ return window.location.origin;
1573
+ return "http://localhost";
1574
+ }
1575
+ function resolveUrl(url) {
1576
+ try {
1577
+ return new URL(url, currentOrigin());
1578
+ } catch {
1579
+ throw new SlexKitRuntimeError("policy", "invalid_url", "Invalid request URL.");
1580
+ }
1581
+ }
1582
+ function originAllowed(origin, patterns) {
1583
+ let parsedOrigin;
1584
+ try {
1585
+ parsedOrigin = new URL(origin);
1586
+ } catch {
1587
+ return false;
1588
+ }
1589
+ for (const pattern of patterns) {
1590
+ const normalized = pattern.trim();
1591
+ if (normalized === "*" || normalized === origin)
1592
+ return true;
1593
+ if (normalized.endsWith("://*")) {
1594
+ const protocol = normalized.slice(0, -4);
1595
+ if (parsedOrigin.protocol === `${protocol}:`)
1596
+ return true;
1597
+ }
1598
+ const wildcard = normalized.match(/^([a-z][a-z0-9+.-]*):\/\/\*\.(.+)$/i);
1599
+ if (wildcard && parsedOrigin.protocol === `${wildcard[1]}:`) {
1600
+ const suffix = wildcard[2].toLowerCase();
1601
+ const host = parsedOrigin.hostname.toLowerCase();
1602
+ if (host.endsWith(`.${suffix}`))
1603
+ return true;
1604
+ }
1605
+ }
1606
+ return false;
1607
+ }
1608
+ function needsJsonBody(method, body) {
1609
+ if (method !== "POST" || body === undefined)
1610
+ return false;
1611
+ return !(typeof body === "string" || body instanceof ArrayBuffer || ArrayBuffer.isView(body));
1612
+ }
1613
+ function bodySize(body) {
1614
+ if (body === undefined || body === null)
1615
+ return 0;
1616
+ if (typeof body === "string")
1617
+ return body.length;
1618
+ if (body instanceof ArrayBuffer)
1619
+ return body.byteLength;
1620
+ if (ArrayBuffer.isView(body))
1621
+ return body.byteLength;
1622
+ return JSON.stringify(body).length;
1623
+ }
1624
+ var DEFAULT_ALLOWED_HEADERS = ["accept", "content-type"];
1625
+ var BLOCKED_HEADERS = [
1626
+ "authorization",
1627
+ "cookie",
1628
+ "proxy-authorization",
1629
+ "sec-fetch-dest",
1630
+ "sec-fetch-mode",
1631
+ "sec-fetch-site",
1632
+ "sec-fetch-user",
1633
+ "set-cookie"
1634
+ ];
1635
+ function normalizeHeaderName(name) {
1636
+ return name.trim().toLowerCase();
1637
+ }
1638
+ function assertHeaders(headers, allowHeaders) {
1639
+ if (!headers)
1640
+ return;
1641
+ const allowed = new Set((allowHeaders ?? DEFAULT_ALLOWED_HEADERS).map(normalizeHeaderName));
1642
+ for (const [name, value] of Object.entries(headers)) {
1643
+ const normalized = normalizeHeaderName(name);
1644
+ if (!normalized || typeof value !== "string") {
1645
+ throw new SlexKitRuntimeError("policy", "header_blocked", "Request header is not allowed.");
1646
+ }
1647
+ if (BLOCKED_HEADERS.includes(normalized) || !allowed.has(normalized)) {
1648
+ throw new SlexKitRuntimeError("policy", "header_blocked", `Request header ${name} is not allowed.`);
1649
+ }
1650
+ }
1651
+ }
1652
+ function responseSize(result) {
1653
+ if (typeof result.text === "string")
1654
+ return result.text.length;
1655
+ if (result.data !== undefined) {
1656
+ try {
1657
+ return JSON.stringify(result.data).length;
1658
+ } catch {
1659
+ return 0;
1660
+ }
1661
+ }
1662
+ return 0;
1663
+ }
1664
+ function contentTypeAllowed(contentType, patterns) {
1665
+ const normalized = contentType.split(";")[0].trim().toLowerCase();
1666
+ if (!normalized)
1667
+ return false;
1668
+ return patterns.some((pattern) => {
1669
+ const allowed = pattern.trim().toLowerCase();
1670
+ if (allowed === "*" || allowed === normalized)
1671
+ return true;
1672
+ if (allowed.endsWith("/*")) {
1673
+ return normalized.startsWith(`${allowed.slice(0, -1)}`);
1674
+ }
1675
+ return false;
1676
+ });
1677
+ }
1678
+ function responseHeader(headers, name) {
1679
+ const target = name.toLowerCase();
1680
+ for (const [key, value] of Object.entries(headers)) {
1681
+ if (key.toLowerCase() === target)
1682
+ return value;
1683
+ }
1684
+ return;
1685
+ }
1686
+ function assertNetworkResult(result, policy) {
1687
+ if (!policy)
1688
+ return;
1689
+ if (policy.maxResponseBytes !== undefined && responseSize(result) > policy.maxResponseBytes) {
1690
+ throw new SlexKitRuntimeError("policy", "response_too_large", "Response body exceeds the runtime policy limit.", result.elapsedMs);
1691
+ }
1692
+ if (policy.allowContentTypes?.length) {
1693
+ const contentType = responseHeader(result.headers, "content-type");
1694
+ if (!contentType || !contentTypeAllowed(contentType, policy.allowContentTypes)) {
1695
+ throw new SlexKitRuntimeError("policy", "content_type_blocked", `Response content-type ${contentType || "unknown"} is not allowed.`, result.elapsedMs);
1696
+ }
1697
+ }
1698
+ }
1699
+ function canvasPolicy(policy) {
1700
+ const canvas = policy.canvas;
1701
+ if (!canvas?.enabled) {
1702
+ throw new SlexKitRuntimeError("policy", "canvas_disabled", "Canvas access is disabled.");
1703
+ }
1704
+ return canvas;
1705
+ }
1706
+ function assertCanvasDimensions(policy, width, height) {
1707
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
1708
+ throw new SlexKitRuntimeError("policy", "canvas_size_invalid", "Canvas size must be positive finite numbers.");
1709
+ }
1710
+ const pixels = Math.ceil(width) * Math.ceil(height);
1711
+ if (policy.maxPixels !== undefined && pixels > policy.maxPixels) {
1712
+ throw new SlexKitRuntimeError("policy", "canvas_too_large", "Canvas size exceeds the runtime policy limit.");
1713
+ }
1714
+ }
1715
+ function assertCanvasContext(policy, contextId) {
1716
+ if (policy.allowedContexts?.length && !policy.allowedContexts.includes(contextId)) {
1717
+ throw new SlexKitRuntimeError("policy", "canvas_context_blocked", `Canvas context ${contextId} is not allowed.`);
1718
+ }
1719
+ }
1720
+ function emitNetworkLog(adapter, event) {
1721
+ try {
1722
+ adapter?.onNetworkLog?.(event);
1723
+ } catch {}
1724
+ }
1725
+ function emitRuntimeError(adapter, event) {
1726
+ try {
1727
+ adapter?.onRuntimeError?.(event);
1728
+ } catch {}
1729
+ }
1730
+ function timerHost(adapter) {
1731
+ const globalTimer = globalThis;
1732
+ return {
1733
+ setTimeout: adapter?.setTimeout ?? ((fn, ms) => Number(globalTimer.setTimeout(fn, ms))),
1734
+ clearTimeout: adapter?.clearTimeout ?? ((id) => globalTimer.clearTimeout(id)),
1735
+ setInterval: adapter?.setInterval ?? ((fn, ms) => Number(globalTimer.setInterval(fn, ms))),
1736
+ clearInterval: adapter?.clearInterval ?? ((id) => globalTimer.clearInterval(id))
1737
+ };
1738
+ }
1739
+ function rafHost(adapter) {
1740
+ const globalRaf = globalThis;
1741
+ return {
1742
+ requestAnimationFrame: adapter?.requestAnimationFrame ?? ((fn) => {
1743
+ if (typeof globalRaf.requestAnimationFrame === "function") {
1744
+ return Number(globalRaf.requestAnimationFrame(fn));
1745
+ }
1746
+ return Number(globalThis.setTimeout(() => fn(runtimeNow(adapter)), 16));
1747
+ }),
1748
+ cancelAnimationFrame: adapter?.cancelAnimationFrame ?? ((id) => {
1749
+ if (typeof globalRaf.cancelAnimationFrame === "function") {
1750
+ globalRaf.cancelAnimationFrame(id);
1751
+ } else {
1752
+ globalThis.clearTimeout(id);
1753
+ }
1754
+ })
1755
+ };
1756
+ }
1757
+ async function readResponseText(response, maxResponseBytes) {
1758
+ if (maxResponseBytes === undefined || !response.body?.getReader) {
1759
+ const text2 = await response.text();
1760
+ if (maxResponseBytes !== undefined && text2.length > maxResponseBytes) {
1761
+ throw new SlexKitRuntimeError("policy", "response_too_large", "Response body exceeds the runtime policy limit.");
1762
+ }
1763
+ return text2;
1764
+ }
1765
+ const reader = response.body.getReader();
1766
+ const decoder = new TextDecoder;
1767
+ let size = 0;
1768
+ let text = "";
1769
+ while (true) {
1770
+ const { done, value } = await reader.read();
1771
+ if (done)
1772
+ break;
1773
+ size += value.byteLength;
1774
+ if (size > maxResponseBytes) {
1775
+ await reader.cancel();
1776
+ throw new SlexKitRuntimeError("policy", "response_too_large", "Response body exceeds the runtime policy limit.");
1777
+ }
1778
+ text += decoder.decode(value, { stream: true });
1779
+ }
1780
+ text += decoder.decode();
1781
+ return text;
1782
+ }
1783
+ async function defaultFetch(request, adapter, options = {}) {
1784
+ if (adapter?.fetch)
1785
+ return adapter.fetch(request);
1786
+ if (typeof fetch !== "function") {
1787
+ throw new SlexKitRuntimeError("network", "fetch_unavailable", "Host fetch is unavailable.");
1788
+ }
1789
+ const start = runtimeNow(adapter);
1790
+ const controller = typeof AbortController !== "undefined" ? new AbortController : undefined;
1791
+ const releaseController = controller ? options.onController?.(controller) : undefined;
1792
+ const timeout = controller ? globalThis.setTimeout(() => controller.abort(), request.timeoutMs) : undefined;
1793
+ try {
1794
+ const init = {
1795
+ method: request.method,
1796
+ headers: request.headers,
1797
+ credentials: request.credentials,
1798
+ signal: controller?.signal
1799
+ };
1800
+ if (request.method === "POST" && request.body !== undefined) {
1801
+ if (typeof request.body === "string" || request.body instanceof ArrayBuffer || ArrayBuffer.isView(request.body)) {
1802
+ init.body = request.body;
1803
+ } else {
1804
+ init.body = JSON.stringify(request.body);
1805
+ init.headers = request.headers;
1806
+ }
1807
+ }
1808
+ const response = await fetch(request.url, init);
1809
+ const text = await readResponseText(response, options.maxResponseBytes);
1810
+ let data = undefined;
1811
+ try {
1812
+ data = text ? JSON.parse(text) : undefined;
1813
+ } catch {
1814
+ data = undefined;
1815
+ }
1816
+ const headers = {};
1817
+ response.headers.forEach((value, key) => {
1818
+ headers[key] = value;
1819
+ });
1820
+ return {
1821
+ ok: response.ok,
1822
+ status: response.status,
1823
+ statusText: response.statusText,
1824
+ url: response.url,
1825
+ headers,
1826
+ text,
1827
+ data,
1828
+ elapsedMs: runtimeNow(adapter) - start
1829
+ };
1830
+ } catch (error) {
1831
+ const elapsedMs = runtimeNow(adapter) - start;
1832
+ if (error instanceof Error && error.name === "AbortError") {
1833
+ throw new SlexKitRuntimeError("timeout", "timeout", "Request timed out.", elapsedMs);
1834
+ }
1835
+ throw new SlexKitRuntimeError("network", "network_error", errorMessage(error), elapsedMs);
1836
+ } finally {
1837
+ if (timeout !== undefined)
1838
+ globalThis.clearTimeout(timeout);
1839
+ releaseController?.();
1840
+ }
1841
+ }
1842
+ function errorMessage(error) {
1843
+ if (error instanceof Error)
1844
+ return error.message;
1845
+ if (error && typeof error === "object" && "message" in error) {
1846
+ return String(error.message);
1847
+ }
1848
+ return String(error ?? "Unknown error.");
1849
+ }
1850
+ function serializeRuntimeError(error) {
1851
+ if (error instanceof SlexKitRuntimeError) {
1852
+ return {
1853
+ name: error.name,
1854
+ message: error.message,
1855
+ kind: error.kind,
1856
+ code: error.code,
1857
+ elapsedMs: error.elapsedMs
1858
+ };
1859
+ }
1860
+ if (error instanceof Error) {
1861
+ return {
1862
+ name: error.name,
1863
+ message: error.message
1864
+ };
1865
+ }
1866
+ return {
1867
+ name: "Error",
1868
+ message: errorMessage(error)
1869
+ };
1870
+ }
1871
+ function deserializeRuntimeError(error) {
1872
+ if (error.name === "SlexKitRuntimeError" && error.kind && error.code) {
1873
+ return new SlexKitRuntimeError(error.kind, error.code, error.message, error.elapsedMs);
1874
+ }
1875
+ const next = new Error(error.message);
1876
+ next.name = error.name || "Error";
1877
+ return next;
1878
+ }
1879
+ function createSecureRuntime(policy, hostAdapter) {
1880
+ const disposers = new Set;
1881
+ const timeoutIds = new Set;
1882
+ const intervalIds = new Set;
1883
+ const rafIds = new Set;
1884
+ const abortControllers = new Set;
1885
+ const ownedCanvases = new Set;
1886
+ const countedCanvases = new Set;
1887
+ let disposed = false;
1888
+ let nextSyntheticId = 1;
1889
+ const syntheticTimers = new Map;
1890
+ const timers = timerHost(hostAdapter);
1891
+ const raf = rafHost(hostAdapter);
1892
+ function assertNotDisposed() {
1893
+ if (disposed) {
1894
+ throw new SlexKitRuntimeError("policy", "runtime_disposed", "Runtime has been disposed.");
1895
+ }
1896
+ }
1897
+ function assertNetwork(method, url, options = {}) {
1898
+ const network = policy.network;
1899
+ if (!network?.enabled) {
1900
+ throw new SlexKitRuntimeError("policy", "network_disabled", "Network access is disabled.");
1901
+ }
1902
+ if (!network.methods.includes(method)) {
1903
+ throw new SlexKitRuntimeError("policy", "method_blocked", `Network method ${method} is not allowed.`);
1904
+ }
1905
+ const parsed = resolveUrl(url);
1906
+ if (!originAllowed(parsed.origin, network.allowOrigins)) {
1907
+ throw new SlexKitRuntimeError("policy", "origin_blocked", `Network origin ${parsed.origin} is not allowed.`);
1908
+ }
1909
+ if (method === "POST" && bodySize(options.body) > network.maxBodyBytes) {
1910
+ throw new SlexKitRuntimeError("policy", "body_too_large", "Request body exceeds the runtime policy limit.");
1911
+ }
1912
+ const headers = needsJsonBody(method, options.body) ? { "content-type": "application/json", ...options.headers } : options.headers;
1913
+ assertHeaders(headers, network.allowHeaders);
1914
+ const timeoutMs = Math.min(options.timeoutMs ?? network.timeoutMs, network.timeoutMs);
1915
+ const credentials = options.credentials ?? network.credentials;
1916
+ if (credentials !== network.credentials) {
1917
+ throw new SlexKitRuntimeError("policy", "credentials_blocked", "Request credentials mode is not allowed.");
1918
+ }
1919
+ return {
1920
+ method,
1921
+ url: parsed.href,
1922
+ headers,
1923
+ body: options.body,
1924
+ credentials,
1925
+ timeoutMs
1926
+ };
1927
+ }
1928
+ function assertTimer(ms) {
1929
+ const timer = policy.timer;
1930
+ if (!timer?.enabled) {
1931
+ throw new SlexKitRuntimeError("policy", "timer_disabled", "Timer access is disabled.");
1932
+ }
1933
+ if (!Number.isFinite(ms) || ms < timer.minIntervalMs) {
1934
+ throw new SlexKitRuntimeError("policy", "timer_interval_blocked", "Timer interval is below policy limit.");
1935
+ }
1936
+ if (timeoutIds.size + intervalIds.size >= timer.maxTimers) {
1937
+ throw new SlexKitRuntimeError("policy", "timer_limit", "Timer limit exceeded.");
1938
+ }
1939
+ }
1940
+ function normalizeTimerId(raw) {
1941
+ if (Number.isFinite(raw) && raw > 0)
1942
+ return raw;
1943
+ const synthetic = nextSyntheticId++;
1944
+ syntheticTimers.set(synthetic, raw);
1945
+ return synthetic;
1946
+ }
1947
+ function assertCanvas(width, height) {
1948
+ assertNotDisposed();
1949
+ const canvas = canvasPolicy(policy);
1950
+ assertCanvasDimensions(canvas, width, height);
1951
+ if (canvas.maxCanvases !== undefined && countedCanvases.size >= canvas.maxCanvases) {
1952
+ throw new SlexKitRuntimeError("policy", "canvas_limit", "Canvas limit exceeded.");
1953
+ }
1954
+ return canvas;
1955
+ }
1956
+ function countCanvas(canvas, policy2) {
1957
+ if (countedCanvases.has(canvas))
1958
+ return;
1959
+ if (policy2.maxCanvases !== undefined && countedCanvases.size >= policy2.maxCanvases) {
1960
+ throw new SlexKitRuntimeError("policy", "canvas_limit", "Canvas limit exceeded.");
1961
+ }
1962
+ countedCanvases.add(canvas);
1963
+ }
1964
+ function reportCallbackError(phase, error) {
1965
+ emitRuntimeError(hostAdapter, {
1966
+ phase,
1967
+ error: serializeRuntimeError(error)
1968
+ });
1969
+ }
1970
+ const api = {
1971
+ now: () => runtimeNow(hostAdapter),
1972
+ get: (url, options = {}) => api.fetch(url, { ...options, method: "GET" }),
1973
+ post: (url, body, options = {}) => api.fetch(url, { ...options, method: "POST", body }),
1974
+ fetch: async (url, options = {}) => {
1975
+ assertNotDisposed();
1976
+ const method = options.method ?? "GET";
1977
+ const request = assertNetwork(method, url, options);
1978
+ const started = runtimeNow(hostAdapter);
1979
+ emitNetworkLog(hostAdapter, { phase: "request", request });
1980
+ try {
1981
+ const result = await defaultFetch(request, hostAdapter, {
1982
+ maxResponseBytes: policy.network?.maxResponseBytes,
1983
+ onController(controller) {
1984
+ abortControllers.add(controller);
1985
+ return () => abortControllers.delete(controller);
1986
+ }
1987
+ });
1988
+ assertNetworkResult(result, policy.network);
1989
+ emitNetworkLog(hostAdapter, {
1990
+ phase: "response",
1991
+ request,
1992
+ result,
1993
+ elapsedMs: runtimeNow(hostAdapter) - started
1994
+ });
1995
+ return result;
1996
+ } catch (error) {
1997
+ const serialized = serializeRuntimeError(error);
1998
+ emitNetworkLog(hostAdapter, {
1999
+ phase: "error",
2000
+ request,
2001
+ error: serialized,
2002
+ elapsedMs: runtimeNow(hostAdapter) - started
2003
+ });
2004
+ throw error;
2005
+ }
2006
+ },
2007
+ setTimeout: (fn, ms) => {
2008
+ assertNotDisposed();
2009
+ assertTimer(ms);
2010
+ let id = 0;
2011
+ const raw = timers.setTimeout(() => {
2012
+ timeoutIds.delete(id);
2013
+ syntheticTimers.delete(id);
2014
+ try {
2015
+ fn();
2016
+ } catch (error) {
2017
+ reportCallbackError("timer", error);
2018
+ throw error;
2019
+ }
2020
+ }, ms);
2021
+ id = normalizeTimerId(raw);
2022
+ timeoutIds.add(id);
2023
+ return id;
2024
+ },
2025
+ clearTimeout: (id) => {
2026
+ const raw = syntheticTimers.get(id) ?? id;
2027
+ timers.clearTimeout(raw);
2028
+ timeoutIds.delete(id);
2029
+ syntheticTimers.delete(id);
2030
+ },
2031
+ setInterval: (fn, ms) => {
2032
+ assertNotDisposed();
2033
+ assertTimer(ms);
2034
+ const id = normalizeTimerId(timers.setInterval(() => {
2035
+ try {
2036
+ fn();
2037
+ } catch (error) {
2038
+ reportCallbackError("interval", error);
2039
+ throw error;
2040
+ }
2041
+ }, ms));
2042
+ intervalIds.add(id);
2043
+ return id;
2044
+ },
2045
+ clearInterval: (id) => {
2046
+ const raw = syntheticTimers.get(id) ?? id;
2047
+ timers.clearInterval(raw);
2048
+ intervalIds.delete(id);
2049
+ syntheticTimers.delete(id);
2050
+ },
2051
+ raf: (fn) => {
2052
+ assertNotDisposed();
2053
+ if (!policy.animation?.enabled) {
2054
+ throw new SlexKitRuntimeError("policy", "animation_disabled", "Animation access is disabled.");
2055
+ }
2056
+ const id = raf.requestAnimationFrame((time) => {
2057
+ rafIds.delete(id);
2058
+ try {
2059
+ fn(time);
2060
+ } catch (error) {
2061
+ reportCallbackError("raf", error);
2062
+ throw error;
2063
+ }
2064
+ });
2065
+ rafIds.add(id);
2066
+ return id;
2067
+ },
2068
+ cancelRaf: (id) => {
2069
+ raf.cancelAnimationFrame(id);
2070
+ rafIds.delete(id);
2071
+ },
2072
+ createCanvas: (width, height) => {
2073
+ assertCanvas(width, height);
2074
+ if (typeof document === "undefined" || typeof document.createElement !== "function") {
2075
+ throw new SlexKitRuntimeError("policy", "canvas_unavailable", "Canvas is unavailable in this runtime.");
2076
+ }
2077
+ const canvas = document.createElement("canvas");
2078
+ canvas.width = Math.ceil(width);
2079
+ canvas.height = Math.ceil(height);
2080
+ ownedCanvases.add(canvas);
2081
+ countedCanvases.add(canvas);
2082
+ return canvas;
2083
+ },
2084
+ getCanvasContext: (canvas, contextId = "2d", options) => {
2085
+ assertNotDisposed();
2086
+ const canvasAccess = canvasPolicy(policy);
2087
+ assertCanvasDimensions(canvasAccess, canvas.width, canvas.height);
2088
+ assertCanvasContext(canvasAccess, contextId);
2089
+ countCanvas(canvas, canvasAccess);
2090
+ const context = canvas.getContext(contextId, options);
2091
+ if (!context) {
2092
+ throw new SlexKitRuntimeError("policy", "canvas_context_unavailable", `Canvas context ${contextId} is unavailable.`);
2093
+ }
2094
+ return context;
2095
+ },
2096
+ onDispose: (fn) => {
2097
+ disposers.add(fn);
2098
+ },
2099
+ isTimeoutError: (error) => error instanceof SlexKitRuntimeError && error.kind === "timeout",
2100
+ isNetworkError: (error) => error instanceof SlexKitRuntimeError && error.kind === "network",
2101
+ isPolicyError: (error) => error instanceof SlexKitRuntimeError && error.kind === "policy",
2102
+ errorMessage
2103
+ };
2104
+ return {
2105
+ api,
2106
+ dispose: () => {
2107
+ if (disposed)
2108
+ return;
2109
+ disposed = true;
2110
+ for (const id of Array.from(timeoutIds))
2111
+ api.clearTimeout(id);
2112
+ for (const id of Array.from(intervalIds))
2113
+ api.clearInterval(id);
2114
+ for (const id of Array.from(rafIds))
2115
+ api.cancelRaf(id);
2116
+ for (const controller of Array.from(abortControllers))
2117
+ controller.abort();
2118
+ abortControllers.clear();
2119
+ for (const canvas of Array.from(ownedCanvases)) {
2120
+ if (canvas.isConnected)
2121
+ canvas.remove();
2122
+ }
2123
+ ownedCanvases.clear();
2124
+ countedCanvases.clear();
2125
+ for (const disposer of Array.from(disposers)) {
2126
+ try {
2127
+ disposer();
2128
+ } catch (error) {
2129
+ reportCallbackError("dispose", error);
2130
+ }
2131
+ }
2132
+ disposers.clear();
2133
+ }
2134
+ };
2135
+ }
2136
+ // src/engine/markdown-runtime.ts
2137
+ var DEFAULT_POLICY = {};
2138
+ function isRecord(value) {
2139
+ return !!value && typeof value === "object" && !Array.isArray(value);
2140
+ }
2141
+ function isRenderableSource(value) {
2142
+ if (!isRecord(value))
2143
+ return false;
2144
+ if ("layout" in value)
2145
+ return isRecord(value.layout) && Object.keys(value.layout).length > 0;
2146
+ if ("namespace" in value || "g" in value)
2147
+ return false;
2148
+ return Object.keys(value).some((key) => key.includes(":"));
2149
+ }
2150
+ function bareLayoutFromSource(value) {
2151
+ const { slex: _slex, namespace: _namespace, g: _g, layout: _layout, ...layout } = value;
2152
+ return layout;
2153
+ }
2154
+ function isStateOnlySource(value) {
2155
+ return isRecord(value) && !isRenderableSource(value) && (("slex" in value) || ("namespace" in value) || ("g" in value));
2156
+ }
2157
+ function isSlexExpressionEnvelope(value) {
2158
+ return isRecord(value) && (("slex" in value) || ("namespace" in value) || ("g" in value) || ("layout" in value));
2159
+ }
2160
+ function parseTrustedSource(source) {
2161
+ if (typeof source !== "string")
2162
+ return source;
2163
+ const parsed = parseSlexSource(source);
2164
+ return parsed.ok ? parsed.value : source;
2165
+ }
2166
+ function scopedTrustedSource(source, artifactId) {
2167
+ if (!artifactId || !isSlexExpressionEnvelope(source)) {
2168
+ return source;
2169
+ }
2170
+ if (!("layout" in source) && isRenderableSource(source)) {
2171
+ return {
2172
+ slex: typeof source.slex === "string" ? source.slex : undefined,
2173
+ namespace: `${artifactId}::${String(source.namespace || "default")}`,
2174
+ g: isRecord(source.g) ? source.g : {},
2175
+ layout: bareLayoutFromSource(source)
2176
+ };
2177
+ }
2178
+ return {
2179
+ ...source,
2180
+ namespace: `${artifactId}::${String(source.namespace || "default")}`
2181
+ };
2182
+ }
2183
+ function namespaceFromSource(source) {
2184
+ if (!isSlexExpressionEnvelope(source))
2185
+ return;
2186
+ return String(source.namespace || "default");
2187
+ }
2188
+ function compareDocumentOrder(a, b) {
2189
+ if (a === b)
2190
+ return 0;
2191
+ const position = a.compareDocumentPosition(b);
2192
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING)
2193
+ return -1;
2194
+ if (position & Node.DOCUMENT_POSITION_PRECEDING)
2195
+ return 1;
2196
+ return 0;
2197
+ }
2198
+ function createSlexKitMarkdownRuntimeHost(initialOptions = {}) {
2199
+ let options = {
2200
+ mode: "trusted",
2201
+ secureFrame: true,
2202
+ policy: DEFAULT_POLICY,
2203
+ ...initialOptions
2204
+ };
2205
+ const cleanups = new Map;
2206
+ const artifactContainers = new Map;
2207
+ const artifactSources = new Map;
2208
+ const secureArtifactAnchors = new Map;
2209
+ const secureArtifactCleanups = new Map;
2210
+ const trustedArtifactNamespaces = new Map;
2211
+ function rememberTrustedArtifactNamespace(artifactId, source) {
2212
+ if (!artifactId)
2213
+ return;
2214
+ const namespace = namespaceFromSource(source);
2215
+ if (!namespace)
2216
+ return;
2217
+ let namespaces = trustedArtifactNamespaces.get(artifactId);
2218
+ if (!namespaces) {
2219
+ namespaces = new Set;
2220
+ trustedArtifactNamespaces.set(artifactId, namespaces);
2221
+ }
2222
+ namespaces.add(`${artifactId}::${namespace}`);
2223
+ }
2224
+ function disposeTrustedArtifactNamespaces(artifactId) {
2225
+ const namespaces = trustedArtifactNamespaces.get(artifactId);
2226
+ if (!namespaces)
2227
+ return;
2228
+ for (const namespace of namespaces)
2229
+ disposeNamespace(namespace);
2230
+ trustedArtifactNamespaces.delete(artifactId);
2231
+ }
2232
+ function composeSecureArtifactSource(artifactId) {
2233
+ const sources = artifactSources.get(artifactId);
2234
+ if (!sources)
2235
+ return "";
2236
+ const orderedSources = Array.from(sources.entries()).sort(([a], [b]) => compareDocumentOrder(a, b)).map(([, source]) => source);
2237
+ const serializedSources = JSON.stringify(orderedSources.map((source) => ({
2238
+ kind: typeof source === "string" ? "script" : "json",
2239
+ source: encodeURIComponent(typeof source === "string" ? source : JSON.stringify(source))
2240
+ })));
2241
+ const artifactPrefix = JSON.stringify(`${artifactId}::`);
2242
+ return `(() => {
2243
+ const __sources = ${serializedSources}.map((entry) => ({
2244
+ kind: entry.kind,
2245
+ source: decodeURIComponent(entry.source),
2246
+ }));
2247
+ const __isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
2248
+ const __merge = (target, source) => {
2249
+ if (!__isRecord(source)) return target;
2250
+ for (const [key, value] of Object.entries(source)) {
2251
+ if (__isRecord(value) && __isRecord(target[key])) __merge(target[key], value);
2252
+ else target[key] = value;
2253
+ }
2254
+ return target;
2255
+ };
2256
+ const __isRenderableTree = (value) => __isRecord(value) && Object.keys(value).some((key) => key.includes(":"));
2257
+ const __artifactPrefix = ${artifactPrefix};
2258
+ const __target = { slex: "0.1", namespace: __artifactPrefix + "default", g: {}, layout: {} };
2259
+ const __layouts = [];
2260
+ for (const __entry of __sources) {
2261
+ const __script = __entry.kind === "json" ? JSON.parse(__entry.source) : (0, eval)(__entry.source);
2262
+ if (!__isRecord(__script)) continue;
2263
+ if ("slex" in __script) __target.slex = String(__script.slex || "0.1");
2264
+ if ("namespace" in __script) __target.namespace = __artifactPrefix + String(__script.namespace || "default");
2265
+ if (__isRecord(__script.g)) __merge(__target.g, __script.g);
2266
+ if (__isRecord(__script.layout) && Object.keys(__script.layout).length > 0) __layouts.push(__script.layout);
2267
+ else if (!("namespace" in __script) && !("g" in __script) && !("layout" in __script) && __isRenderableTree(__script)) {
2268
+ const { slex: __slex, ...__layout } = __script;
2269
+ __layouts.push(__layout);
2270
+ }
2271
+ }
2272
+ if (__layouts.length === 1) {
2273
+ __target.layout = { "column:block_0": Object.assign({}, __layouts[0], { id: "slexkit-slot-slot_0" }) };
2274
+ } else if (__layouts.length > 1) {
2275
+ __target.layout = {
2276
+ "column:artifact": Object.fromEntries(
2277
+ __layouts.map((layout, index) => ["column:block_" + index, Object.assign({}, layout, { id: "slexkit-slot-slot_" + index })]),
2278
+ ),
2279
+ };
2280
+ }
2281
+ return __target;
2282
+ })()`;
2283
+ }
2284
+ function addArtifactContainer(artifactId, container) {
2285
+ let containers = artifactContainers.get(artifactId);
2286
+ if (!containers) {
2287
+ containers = new Set;
2288
+ artifactContainers.set(artifactId, containers);
2289
+ }
2290
+ containers.add(container);
2291
+ }
2292
+ function addArtifactSource(block) {
2293
+ const artifactId = block.artifactId;
2294
+ let sources = artifactSources.get(artifactId);
2295
+ if (!sources) {
2296
+ sources = new Map;
2297
+ artifactSources.set(artifactId, sources);
2298
+ }
2299
+ sources.set(block.container, block.source);
2300
+ addArtifactContainer(artifactId, block.container);
2301
+ return artifactId;
2302
+ }
2303
+ function clearSecureSlot(container) {
2304
+ delete container.dataset.slexkitSecureArtifactSlot;
2305
+ container.replaceChildren();
2306
+ }
2307
+ function selectSecureArtifactAnchor(artifactId) {
2308
+ const containers = artifactContainers.get(artifactId);
2309
+ if (!containers?.size)
2310
+ return;
2311
+ return Array.from(containers).sort(compareDocumentOrder)[0];
2312
+ }
2313
+ function remountSecureArtifact(artifactId, theme) {
2314
+ const anchor = secureArtifactAnchors.get(artifactId);
2315
+ if (!anchor)
2316
+ return;
2317
+ secureArtifactCleanups.get(artifactId)?.();
2318
+ secureArtifactCleanups.delete(artifactId);
2319
+ clearSecureSlot(anchor);
2320
+ const containers = artifactContainers.get(artifactId) ?? new Set;
2321
+ const orderedContainers = Array.from(containers).sort(compareDocumentOrder);
2322
+ for (const container of containers) {
2323
+ if (container === anchor)
2324
+ continue;
2325
+ clearSecureSlot(container);
2326
+ container.dataset.slexkitSecureArtifactSlot = "true";
2327
+ }
2328
+ const cleanup = mountSecureArtifact(composeSecureArtifactSource(artifactId), anchor, {
2329
+ theme,
2330
+ dir: options.dir,
2331
+ labels: options.labels,
2332
+ policy: options.policy ?? DEFAULT_POLICY,
2333
+ hostAdapter: options.hostAdapter,
2334
+ frame: options.secureFrame ?? true,
2335
+ artifactSlots: orderedContainers.map((container, index) => ({
2336
+ id: `slot_${index}`,
2337
+ container
2338
+ }))
2339
+ });
2340
+ secureArtifactCleanups.set(artifactId, cleanup);
2341
+ }
2342
+ function mountSecureBlock(block) {
2343
+ if (!block.artifactId) {
2344
+ const cleanup2 = mountSecureArtifact(block.source, block.container, {
2345
+ theme: block.theme ?? options.theme,
2346
+ dir: block.dir ?? options.dir,
2347
+ labels: block.labels ?? options.labels,
2348
+ policy: options.policy ?? DEFAULT_POLICY,
2349
+ hostAdapter: options.hostAdapter,
2350
+ frame: options.secureFrame ?? true
2351
+ });
2352
+ return remember(block, cleanup2);
2353
+ }
2354
+ const artifactId = addArtifactSource(block);
2355
+ const anchor = selectSecureArtifactAnchor(artifactId);
2356
+ if (anchor)
2357
+ secureArtifactAnchors.set(artifactId, anchor);
2358
+ remountSecureArtifact(artifactId, block.theme ?? options.theme);
2359
+ const cleanup = () => {
2360
+ artifactSources.get(artifactId)?.delete(block.container);
2361
+ artifactContainers.get(artifactId)?.delete(block.container);
2362
+ cleanups.delete(block.container);
2363
+ clearSecureSlot(block.container);
2364
+ const sources = artifactSources.get(artifactId);
2365
+ const containers = artifactContainers.get(artifactId);
2366
+ if (!sources?.size || !containers?.size) {
2367
+ secureArtifactCleanups.get(artifactId)?.();
2368
+ secureArtifactCleanups.delete(artifactId);
2369
+ secureArtifactAnchors.delete(artifactId);
2370
+ artifactSources.delete(artifactId);
2371
+ artifactContainers.delete(artifactId);
2372
+ return;
2373
+ }
2374
+ if (secureArtifactAnchors.get(artifactId) === block.container) {
2375
+ const nextAnchor = selectSecureArtifactAnchor(artifactId);
2376
+ if (nextAnchor)
2377
+ secureArtifactAnchors.set(artifactId, nextAnchor);
2378
+ }
2379
+ remountSecureArtifact(artifactId, block.theme ?? options.theme);
2380
+ };
2381
+ cleanups.set(block.container, cleanup);
2382
+ return cleanup;
2383
+ }
2384
+ function remember(block, cleanup) {
2385
+ cleanups.set(block.container, cleanup);
2386
+ if (block.artifactId) {
2387
+ addArtifactContainer(block.artifactId, block.container);
2388
+ }
2389
+ return () => {
2390
+ cleanup();
2391
+ cleanups.delete(block.container);
2392
+ if (!block.artifactId)
2393
+ return;
2394
+ const containers = artifactContainers.get(block.artifactId);
2395
+ containers?.delete(block.container);
2396
+ if (containers?.size === 0)
2397
+ artifactContainers.delete(block.artifactId);
2398
+ };
2399
+ }
2400
+ function disposeBlock(container) {
2401
+ const cleanup = cleanups.get(container);
2402
+ if (!cleanup)
2403
+ return;
2404
+ cleanup();
2405
+ cleanups.delete(container);
2406
+ for (const [artifactId, containers] of artifactContainers) {
2407
+ containers.delete(container);
2408
+ artifactSources.get(artifactId)?.delete(container);
2409
+ if (secureArtifactAnchors.get(artifactId) === container) {
2410
+ const nextAnchor = selectSecureArtifactAnchor(artifactId);
2411
+ if (nextAnchor)
2412
+ secureArtifactAnchors.set(artifactId, nextAnchor);
2413
+ else
2414
+ secureArtifactAnchors.delete(artifactId);
2415
+ }
2416
+ if (containers.size === 0)
2417
+ artifactContainers.delete(artifactId);
2418
+ if (artifactSources.get(artifactId)?.size === 0)
2419
+ artifactSources.delete(artifactId);
2420
+ }
2421
+ }
2422
+ return {
2423
+ configure(nextOptions) {
2424
+ options = { ...options, ...nextOptions };
2425
+ },
2426
+ getMode() {
2427
+ return options.mode ?? "trusted";
2428
+ },
2429
+ mountBlock(block) {
2430
+ disposeBlock(block.container);
2431
+ block.container.replaceChildren();
2432
+ const mode = options.mode ?? "trusted";
2433
+ if (mode === "secure")
2434
+ return mountSecureBlock(block);
2435
+ const parsedSource = parseTrustedSource(block.source);
2436
+ rememberTrustedArtifactNamespace(block.artifactId, parsedSource);
2437
+ const trustedSource = scopedTrustedSource(parsedSource, block.artifactId);
2438
+ const stateOnly = block.stateOnly ?? isStateOnlySource(trustedSource);
2439
+ if (stateOnly) {
2440
+ const ok = ingest(trustedSource);
2441
+ if (!ok)
2442
+ throw new Error("Failed to parse Slex state block.");
2443
+ return remember(block, () => {});
2444
+ }
2445
+ const theme = block.theme ?? options.theme;
2446
+ const cleanup = mount(trustedSource, block.container, {
2447
+ theme,
2448
+ dir: block.dir ?? options.dir,
2449
+ labels: block.labels ?? options.labels
2450
+ });
2451
+ return remember(block, cleanup);
2452
+ },
2453
+ disposeBlock,
2454
+ disposeArtifact(artifactId) {
2455
+ const containers = Array.from(artifactContainers.get(artifactId) ?? []);
2456
+ for (const container of containers)
2457
+ disposeBlock(container);
2458
+ secureArtifactCleanups.get(artifactId)?.();
2459
+ secureArtifactCleanups.delete(artifactId);
2460
+ secureArtifactAnchors.delete(artifactId);
2461
+ artifactContainers.delete(artifactId);
2462
+ artifactSources.delete(artifactId);
2463
+ disposeTrustedArtifactNamespaces(artifactId);
2464
+ },
2465
+ disposeAll() {
2466
+ for (const container of Array.from(cleanups.keys()))
2467
+ disposeBlock(container);
2468
+ for (const cleanup of Array.from(secureArtifactCleanups.values()))
2469
+ cleanup();
2470
+ secureArtifactCleanups.clear();
2471
+ secureArtifactAnchors.clear();
2472
+ artifactContainers.clear();
2473
+ artifactSources.clear();
2474
+ for (const artifactId of Array.from(trustedArtifactNamespaces.keys())) {
2475
+ disposeTrustedArtifactNamespaces(artifactId);
2476
+ }
2477
+ }
2478
+ };
2479
+ }
2480
+ var globalMarkdownRuntimeHost;
2481
+ function installSlexKitMarkdownRuntimeHost(options = {}) {
2482
+ globalMarkdownRuntimeHost = createSlexKitMarkdownRuntimeHost(options);
2483
+ return globalMarkdownRuntimeHost;
2484
+ }
2485
+ function getSlexKitMarkdownRuntimeHost() {
2486
+ if (!globalMarkdownRuntimeHost) {
2487
+ globalMarkdownRuntimeHost = createSlexKitMarkdownRuntimeHost();
2488
+ }
2489
+ return globalMarkdownRuntimeHost;
2490
+ }
2491
+
2492
+ // src/engine/index.ts
2493
+ var defaultRuntimeUrl = typeof document !== "undefined" && typeof document.currentScript === "object" && document.currentScript && "src" in document.currentScript ? String(document.currentScript.src || "") : undefined;
2494
+ function setSlexKitRuntimeUrl(url) {
2495
+ defaultRuntimeUrl = url || undefined;
2496
+ }
2497
+ function getSlexKitRuntimeUrl() {
2498
+ return defaultRuntimeUrl;
2499
+ }
2500
+ function normalizeExpression(input) {
2501
+ if (!input || typeof input !== "object")
2502
+ return input;
2503
+ const hasProtocolMarker = "slex" in input;
2504
+ const hasEnvelopeField = "namespace" in input || "g" in input || "layout" in input;
2505
+ if (hasEnvelopeField)
2506
+ return input;
2507
+ const keys = Object.keys(input);
2508
+ if (keys.some((key) => key.includes(":"))) {
2509
+ const { slex, ...layout } = input;
2510
+ return {
2511
+ ...hasProtocolMarker ? { slex } : {},
2512
+ namespace: "default",
2513
+ g: {},
2514
+ layout
2515
+ };
2516
+ }
2517
+ return input;
2518
+ }
2519
+ function warnUnsupportedProtocol(expression) {
2520
+ if (expression.slex === undefined || expression.slex === SLEX_PROTOCOL_VERSION)
2521
+ return;
2522
+ console.warn(`[SlexKit] Slex protocol marker '${String(expression.slex)}' does not match supported protocol '${SLEX_PROTOCOL_VERSION}'.`);
2523
+ }
2524
+ function parseSourceOrExpression(input) {
2525
+ if (typeof input !== "string")
2526
+ return normalizeExpression(input);
2527
+ const parsed = parseSlexSource(input);
2528
+ if (!parsed.ok) {
2529
+ console.warn("[SlexKit] Failed to parse Slex source:", parsed.error.message);
2530
+ return;
2531
+ }
2532
+ return normalizeExpression(parsed.value);
2533
+ }
2534
+ function applyExpression(expression) {
2535
+ warnUnsupportedProtocol(expression);
2536
+ const ns = expression.namespace || "default";
2537
+ const store = getStore(ns);
2538
+ if (expression.g) {
2539
+ try {
2540
+ deepMerge(store.g, expression.g);
2541
+ } catch (e) {
2542
+ console.warn(`[SlexKit][${ns}] Merge error, replacing entire g:`, e.message);
2543
+ for (const k of Object.keys(store.g)) {
2544
+ delete store.g[k];
2545
+ }
2546
+ Object.assign(store.g, expression.g);
2547
+ }
2548
+ }
2549
+ if (expression.layout) {
2550
+ store.layouts = [expression.layout];
2551
+ }
2552
+ }
2553
+ function hasThemeToken(el, token) {
2554
+ return getComputedStyle(el).getPropertyValue(token).trim().length > 0;
2555
+ }
2556
+ function resolveThemeMode(container, requested = "auto") {
2557
+ if (requested !== "auto")
2558
+ return requested;
2559
+ if (container.closest(".slexkit-theme-uno, .slexkit-theme-flowbite"))
2560
+ return "uno";
2561
+ return hasThemeToken(container, "--primary") && hasThemeToken(container, "--background") ? "host-shadcn" : "uno";
2562
+ }
2563
+ function resolveColorMode(container) {
2564
+ const scoped = container.closest(".dark, .light, [data-theme='dark'], [data-theme='light']");
2565
+ if (scoped?.classList.contains("dark") || scoped?.dataset.theme === "dark")
2566
+ return "dark";
2567
+ if (scoped?.classList.contains("light") || scoped?.dataset.theme === "light")
2568
+ return "light";
2569
+ const doc = container.ownerDocument || document;
2570
+ const docEl = doc.documentElement;
2571
+ if (docEl.classList.contains("dark") || docEl.dataset.theme === "dark")
2572
+ return "dark";
2573
+ if (docEl.classList.contains("light") || docEl.dataset.theme === "light")
2574
+ return "light";
2575
+ const scheme = getComputedStyle(docEl).colorScheme || getComputedStyle(container).colorScheme;
2576
+ if (scheme.split(/\s+/).includes("dark"))
2577
+ return "dark";
2578
+ if (scheme.split(/\s+/).includes("light"))
2579
+ return "light";
2580
+ return;
2581
+ }
2582
+ function applyDocumentColorMode(doc, mode) {
2583
+ if (!mode)
2584
+ return;
2585
+ doc.documentElement.classList.toggle("dark", mode === "dark");
2586
+ doc.documentElement.classList.toggle("light", mode === "light");
2587
+ doc.documentElement.dataset.theme = mode;
2588
+ }
2589
+ function resolveDirection(container, requested = "auto") {
2590
+ if (requested === "ltr" || requested === "rtl")
2591
+ return requested;
2592
+ const inherited = container.closest("[dir]")?.dir || container.ownerDocument?.documentElement?.dir;
2593
+ return inherited === "rtl" ? "rtl" : "ltr";
2594
+ }
2595
+ function mount(input, container, options = {}) {
2596
+ const expression = parseSourceOrExpression(input);
2597
+ if (!expression)
2598
+ return () => {};
2599
+ const ns = expression.namespace || "default";
2600
+ const store = getStore(ns);
2601
+ const ownerDocument = container.ownerDocument || document;
2602
+ applyExpression(expression);
2603
+ const oldCleanup = store.cleanups.get(container);
2604
+ if (oldCleanup)
2605
+ oldCleanup();
2606
+ const root = ownerDocument.createElement("div");
2607
+ const theme = resolveThemeMode(container, options.theme);
2608
+ const dir = resolveDirection(container, options.dir);
2609
+ root.className = `slexkit-root slexkit-theme-${theme}`;
2610
+ root.dir = dir;
2611
+ root.dataset.namespace = ns;
2612
+ root.dataset.theme = theme;
2613
+ root.dataset.dir = dir;
2614
+ container.appendChild(root);
2615
+ store.roots.set(container, root);
2616
+ const cleanup = createRoot((dispose) => {
2617
+ const layoutRoot = ownerDocument.createElement("div");
2618
+ layoutRoot.className = "slex-layout";
2619
+ root.appendChild(layoutRoot);
2620
+ renderTree(expression.layout ?? {}, layoutRoot, store.g, store.components, store.componentTypes, undefined, ns, options.api, {
2621
+ dir,
2622
+ labels: options.labels ?? {}
2623
+ });
2624
+ return () => {
2625
+ dispose();
2626
+ root.remove();
2627
+ store.roots.delete(container);
2628
+ store.cleanups.delete(container);
2629
+ };
2630
+ });
2631
+ store.cleanups.set(container, cleanup);
2632
+ return cleanup;
2633
+ }
2634
+ function disposeNamespace(namespace) {
2635
+ const ns = namespace || "default";
2636
+ const store = peekStore(ns);
2637
+ if (store) {
2638
+ for (const cleanup of Array.from(store.cleanups.values())) {
2639
+ try {
2640
+ cleanup();
2641
+ } catch (e) {
2642
+ console.warn(`[SlexKit][${ns}] Cleanup error while disposing namespace:`, e.message);
2643
+ }
2644
+ }
2645
+ for (const root of Array.from(store.roots.values())) {
2646
+ root.remove();
2647
+ }
2648
+ store.roots.clear();
2649
+ store.cleanups.clear();
2650
+ deleteStore(ns);
2651
+ }
2652
+ clearEvalCache(ns);
2653
+ }
2654
+ function mountSecureArtifact(input, container, options) {
2655
+ container.dataset.slexkitSecureRuntime = "true";
2656
+ let frameTarget;
2657
+ try {
2658
+ frameTarget = createSecureFrameTarget(input, container, options);
2659
+ } catch (error) {
2660
+ delete container.dataset.slexkitSecureRuntime;
2661
+ throw error;
2662
+ }
2663
+ if (frameTarget?.sandboxed) {
2664
+ return () => {
2665
+ frameTarget.dispose();
2666
+ delete container.dataset.slexkitSecureRuntime;
2667
+ };
2668
+ }
2669
+ if (!options.unsafeInlineExecution) {
2670
+ delete container.dataset.slexkitSecureRuntime;
2671
+ throw new Error("mountSecureArtifact requires a sandbox frame runtime URL. Use unsafeInlineExecution only for trusted inline execution.");
2672
+ }
2673
+ const runtime = createSecureRuntime(options.policy, options.hostAdapter);
2674
+ const target = frameTarget?.target ?? container;
2675
+ const cleanup = mount(input, target, {
2676
+ theme: options.theme,
2677
+ dir: options.dir,
2678
+ labels: options.labels,
2679
+ api: runtime.api
2680
+ });
2681
+ return () => {
2682
+ cleanup();
2683
+ runtime.dispose();
2684
+ frameTarget?.dispose();
2685
+ delete container.dataset.slexkitSecureRuntime;
2686
+ };
2687
+ }
2688
+ function createSecureFrameTarget(input, container, mountOptions) {
2689
+ const frame = mountOptions.frame;
2690
+ if (!frame)
2691
+ return null;
2692
+ const ownerDocument = container.ownerDocument || document;
2693
+ const options = typeof frame === "object" ? frame : {};
2694
+ const iframe = ownerDocument.createElement("iframe");
2695
+ iframe.className = options.className ?? "slexkit-secure-frame";
2696
+ iframe.title = options.title ?? "SlexKit secure artifact";
2697
+ iframe.setAttribute("data-slexkit-secure-frame", "true");
2698
+ iframe.setAttribute("referrerpolicy", "no-referrer");
2699
+ const runtimeUrl = options.runtimeUrl ?? options.runnerUrl ?? defaultRuntimeUrl;
2700
+ if (runtimeUrl) {
2701
+ const resolvedRuntimeUrl = resolveRuntimeUrl(runtimeUrl);
2702
+ assertSandboxCloneable(input);
2703
+ iframe.setAttribute("sandbox", secureSandboxAttribute(options));
2704
+ container.replaceChildren(iframe);
2705
+ const bridge = createSandboxBridge(input, container, iframe, mountOptions);
2706
+ iframe.srcdoc = secureRunnerSrcdoc(resolvedRuntimeUrl);
2707
+ return bridge;
2708
+ }
2709
+ if (frame) {
2710
+ iframe.remove();
2711
+ throw new Error("SlexKit secure frame requires runtimeUrl or setSlexKitRuntimeUrl().");
2712
+ }
2713
+ if (options.sandbox)
2714
+ iframe.setAttribute("sandbox", options.sandbox);
2715
+ container.replaceChildren(iframe);
2716
+ const frameDocument = iframe.contentDocument;
2717
+ if (!frameDocument?.body) {
2718
+ iframe.remove();
2719
+ throw new Error("Unable to create SlexKit secure frame document.");
2720
+ }
2721
+ frameDocument.open();
2722
+ frameDocument.write('<!doctype html><html><head></head><body><div id="slexkit-secure-root"></div></body></html>');
2723
+ frameDocument.close();
2724
+ applyDocumentColorMode(frameDocument, resolveColorMode(container));
2725
+ const target = frameDocument.getElementById("slexkit-secure-root");
2726
+ if (!target) {
2727
+ iframe.remove();
2728
+ throw new Error("Unable to create SlexKit secure frame root.");
2729
+ }
2730
+ return {
2731
+ target,
2732
+ dispose: () => iframe.remove()
2733
+ };
2734
+ }
2735
+ function secureSandboxAttribute(options) {
2736
+ const sandbox = options.sandbox ?? "allow-scripts";
2737
+ const tokens = sandbox.split(/\s+/).filter(Boolean);
2738
+ if (tokens.includes("allow-same-origin") && !options.unsafeAllowSameOrigin) {
2739
+ throw new Error("SlexKit secure frames cannot use allow-same-origin unless unsafeAllowSameOrigin is explicitly enabled.");
2740
+ }
2741
+ if (!tokens.includes("allow-scripts")) {
2742
+ throw new Error("SlexKit secure frames require allow-scripts to run the runtime.");
2743
+ }
2744
+ return tokens.join(" ");
2745
+ }
2746
+ function resolveRuntimeUrl(runtimeUrl) {
2747
+ try {
2748
+ const base = typeof document !== "undefined" ? document.baseURI : undefined;
2749
+ return new URL(runtimeUrl, base).href;
2750
+ } catch {
2751
+ return runtimeUrl;
2752
+ }
2753
+ }
2754
+ function cspSourceForRuntime(runtimeUrl) {
2755
+ try {
2756
+ const parsed = new URL(runtimeUrl);
2757
+ if (parsed.protocol === "blob:")
2758
+ return "blob:";
2759
+ if (parsed.protocol === "data:")
2760
+ return "data:";
2761
+ return parsed.origin;
2762
+ } catch {
2763
+ return "'self'";
2764
+ }
2765
+ }
2766
+ function escapeHtmlAttribute(value) {
2767
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2768
+ }
2769
+ function secureRunnerSrcdoc(runtimeUrl) {
2770
+ const nonce = randomToken(12);
2771
+ const csp = [
2772
+ "default-src 'none'",
2773
+ `script-src 'nonce-${nonce}' 'unsafe-eval' ${cspSourceForRuntime(runtimeUrl)}`,
2774
+ "connect-src 'none'",
2775
+ "img-src data: blob:",
2776
+ "style-src 'unsafe-inline'",
2777
+ "font-src data:",
2778
+ "form-action 'none'",
2779
+ "base-uri 'none'"
2780
+ ].join("; ");
2781
+ const style = "html,body{margin:0;min-height:100%;overflow:hidden;}#slexkit-secure-root{min-height:100%;}";
2782
+ return `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${escapeHtmlAttribute(csp)}"><style>${style}</style></head><body><div id="slexkit-secure-root"></div><script type="module" nonce="${nonce}">import { startSlexKitSandboxRunner } from ${JSON.stringify(runtimeUrl)}; startSlexKitSandboxRunner();</script></body></html>`;
2783
+ }
2784
+ function secureFrameLoadTimeout(options) {
2785
+ const frame = options.frame;
2786
+ if (frame && typeof frame === "object" && typeof frame.loadTimeoutMs === "number") {
2787
+ return Math.max(0, frame.loadTimeoutMs);
2788
+ }
2789
+ return 8000;
2790
+ }
2791
+ function secureFrameMaxUnresponsiveMs(options) {
2792
+ const value = options.policy.execution?.maxUnresponsiveMs;
2793
+ return typeof value === "number" ? Math.max(0, value) : 0;
2794
+ }
2795
+ function secureFrameLoadError(runtimeUrl, phase, detail) {
2796
+ const phaseMessage = phase === "ready" ? "The sandbox did not report that the runtime module loaded." : "The sandbox loaded but did not confirm artifact mount.";
2797
+ const suffix = detail ? `
2798
+ Sandbox error: ${detail}` : "";
2799
+ return new Error(`SlexKit secure runtime failed to load.
2800
+ ${phaseMessage}
2801
+ Runtime URL: ${runtimeUrl}
2802
+ ` + `Check that this URL serves slexkit.runtime.js as an ES module with:
2803
+ ` + `Access-Control-Allow-Origin: *
2804
+ Content-Type: text/javascript
2805
+ ` + "Do not fix this by adding allow-same-origin to the sandbox frame." + suffix);
2806
+ }
2807
+ function secureFrameHeartbeatError(runtimeUrl, elapsedMs) {
2808
+ return new Error(`SlexKit secure runtime stopped responding.
2809
+ ` + `No sandbox heartbeat was received for ${Math.round(elapsedMs)} ms.
2810
+ ` + `Runtime URL: ${runtimeUrl}
2811
+ ` + "The sandbox frame was terminated to avoid keeping unresponsive agent code alive.");
2812
+ }
2813
+ function showSecureFrameError(container, iframe, error) {
2814
+ container.dataset.slexkitSecureStatus = "error";
2815
+ let alert = container.querySelector(".slexkit-secure-error");
2816
+ if (!alert) {
2817
+ alert = (container.ownerDocument || document).createElement("div");
2818
+ alert.className = "slexkit-secure-error";
2819
+ alert.setAttribute("role", "alert");
2820
+ iframe.after(alert);
2821
+ }
2822
+ alert.textContent = error.message;
2823
+ console.error(error.message);
2824
+ }
2825
+ function clearSecureFrameError(container) {
2826
+ delete container.dataset.slexkitSecureStatus;
2827
+ container.querySelector(".slexkit-secure-error")?.remove();
2828
+ }
2829
+ function assertSandboxCloneable(input) {
2830
+ if (typeof input === "string")
2831
+ return;
2832
+ const seen = new Set;
2833
+ const visit = (value) => {
2834
+ if (typeof value === "function") {
2835
+ throw new Error("Sandbox runner input cannot contain functions. Pass Slex source to execute functions inside the sandbox realm.");
2836
+ }
2837
+ if (!value || typeof value !== "object")
2838
+ return;
2839
+ if (seen.has(value))
2840
+ return;
2841
+ seen.add(value);
2842
+ for (const item of Object.values(value))
2843
+ visit(item);
2844
+ };
2845
+ visit(input);
2846
+ }
2847
+ function createSandboxBridge(input, container, iframe, options) {
2848
+ const id = `secure_${randomToken(8)}`;
2849
+ const token = randomToken(24);
2850
+ const bridgeRuntime = createSecureRuntime(options.policy, options.hostAdapter);
2851
+ const runtimeUrl = typeof options.frame === "object" ? resolveRuntimeUrl(options.frame.runtimeUrl ?? options.frame.runnerUrl ?? defaultRuntimeUrl ?? "") : resolveRuntimeUrl(defaultRuntimeUrl ?? "");
2852
+ const loadTimeoutMs = secureFrameLoadTimeout(options);
2853
+ const artifactSlots = normalizeArtifactSlots(options.artifactSlots);
2854
+ let disposed = false;
2855
+ let mounted = false;
2856
+ let ready = false;
2857
+ let mountAcknowledged = false;
2858
+ let loadTimer;
2859
+ let heartbeatTimer;
2860
+ let slotSyncFrame;
2861
+ let slotSyncTimer;
2862
+ let lastHeartbeat = 0;
2863
+ const maxUnresponsiveMs = secureFrameMaxUnresponsiveMs(options);
2864
+ const ownerWindow = iframe.ownerDocument.defaultView ?? window;
2865
+ const slotStyleSnapshots = new Map;
2866
+ let adjustedAnchorPosition = false;
2867
+ const clearLoadTimer = () => {
2868
+ if (loadTimer === undefined)
2869
+ return;
2870
+ window.clearTimeout(loadTimer);
2871
+ loadTimer = undefined;
2872
+ };
2873
+ const clearHeartbeatTimer = () => {
2874
+ if (heartbeatTimer === undefined)
2875
+ return;
2876
+ window.clearInterval(heartbeatTimer);
2877
+ heartbeatTimer = undefined;
2878
+ };
2879
+ const failLoad = (phase, detail) => {
2880
+ if (disposed || mountAcknowledged)
2881
+ return;
2882
+ clearLoadTimer();
2883
+ showSecureFrameError(container, iframe, secureFrameLoadError(runtimeUrl, phase, detail));
2884
+ terminateFrame({ keepDiagnostic: true });
2885
+ };
2886
+ const failHeartbeat = () => {
2887
+ if (disposed || !mountAcknowledged || maxUnresponsiveMs <= 0)
2888
+ return;
2889
+ const elapsed = Date.now() - lastHeartbeat;
2890
+ if (elapsed < maxUnresponsiveMs)
2891
+ return;
2892
+ clearHeartbeatTimer();
2893
+ showSecureFrameError(container, iframe, secureFrameHeartbeatError(runtimeUrl, elapsed));
2894
+ terminateFrame({ keepDiagnostic: true });
2895
+ };
2896
+ const startHeartbeatWatchdog = () => {
2897
+ if (maxUnresponsiveMs <= 0 || heartbeatTimer !== undefined)
2898
+ return;
2899
+ lastHeartbeat = Date.now();
2900
+ heartbeatTimer = window.setInterval(failHeartbeat, Math.max(10, Math.min(1000, maxUnresponsiveMs / 2)));
2901
+ };
2902
+ const clearSlotSyncTimer = () => {
2903
+ if (slotSyncFrame !== undefined) {
2904
+ ownerWindow.cancelAnimationFrame(slotSyncFrame);
2905
+ slotSyncFrame = undefined;
2906
+ }
2907
+ if (slotSyncTimer !== undefined) {
2908
+ ownerWindow.clearTimeout(slotSyncTimer);
2909
+ slotSyncTimer = undefined;
2910
+ }
2911
+ };
2912
+ const syncArtifactSlotsNow = () => {
2913
+ slotSyncFrame = undefined;
2914
+ slotSyncTimer = undefined;
2915
+ if (!artifactSlots.length || disposed)
2916
+ return;
2917
+ const frameRect = iframe.getBoundingClientRect();
2918
+ const slots = artifactSlots.map((slot) => {
2919
+ const rect = slot.container.getBoundingClientRect();
2920
+ return {
2921
+ id: slot.id,
2922
+ left: rect.left - frameRect.left,
2923
+ top: rect.top - frameRect.top,
2924
+ width: rect.width,
2925
+ height: rect.height
2926
+ };
2927
+ });
2928
+ if (artifactSlots.length > 1) {
2929
+ const maxBottom = Math.max(1, ...slots.map((slot) => slot.top + slot.height));
2930
+ iframe.style.height = `${Math.ceil(maxBottom)}px`;
2931
+ }
2932
+ iframe.contentWindow?.postMessage({
2933
+ channel: "slexkit-secure",
2934
+ type: "slots",
2935
+ id,
2936
+ token,
2937
+ slots
2938
+ }, "*");
2939
+ };
2940
+ const requestArtifactSlotSync = () => {
2941
+ if (!artifactSlots.length || disposed || slotSyncFrame !== undefined || slotSyncTimer !== undefined)
2942
+ return;
2943
+ if (typeof ownerWindow.requestAnimationFrame === "function") {
2944
+ slotSyncFrame = ownerWindow.requestAnimationFrame(syncArtifactSlotsNow);
2945
+ } else {
2946
+ slotSyncTimer = ownerWindow.setTimeout(syncArtifactSlotsNow, 0);
2947
+ }
2948
+ };
2949
+ const slotResizeObserver = artifactSlots.length && typeof ResizeObserver !== "undefined" ? new ResizeObserver(requestArtifactSlotSync) : undefined;
2950
+ if (artifactSlots.length > 1) {
2951
+ slotStyleSnapshots.set(container, {
2952
+ minHeight: container.style.minHeight,
2953
+ position: container.style.position,
2954
+ secureSlotId: container.dataset.slexkitSecureArtifactSlotId
2955
+ });
2956
+ iframe.style.position = "absolute";
2957
+ iframe.style.inset = "0 auto auto 0";
2958
+ iframe.style.width = "100%";
2959
+ iframe.style.minHeight = "1px";
2960
+ iframe.style.border = "0";
2961
+ iframe.style.background = "transparent";
2962
+ iframe.style.zIndex = "1";
2963
+ iframe.setAttribute("allowtransparency", "true");
2964
+ const computedAnchorPosition = ownerWindow.getComputedStyle(container).position;
2965
+ if (!computedAnchorPosition || computedAnchorPosition === "static") {
2966
+ container.style.position = "relative";
2967
+ adjustedAnchorPosition = true;
2968
+ }
2969
+ }
2970
+ for (const slot of artifactSlots) {
2971
+ if (!slotStyleSnapshots.has(slot.container)) {
2972
+ slotStyleSnapshots.set(slot.container, {
2973
+ minHeight: slot.container.style.minHeight,
2974
+ position: slot.container.style.position,
2975
+ secureSlotId: slot.container.dataset.slexkitSecureArtifactSlotId
2976
+ });
2977
+ }
2978
+ slot.container.dataset.slexkitSecureArtifactSlotId = slot.id;
2979
+ slotResizeObserver?.observe(slot.container);
2980
+ }
2981
+ if (artifactSlots.length) {
2982
+ ownerWindow.addEventListener("resize", requestArtifactSlotSync);
2983
+ ownerWindow.addEventListener("scroll", requestArtifactSlotSync, true);
2984
+ }
2985
+ if (loadTimeoutMs > 0) {
2986
+ loadTimer = window.setTimeout(() => {
2987
+ failLoad(ready ? "mounted" : "ready");
2988
+ }, loadTimeoutMs);
2989
+ }
2990
+ const postMount = () => {
2991
+ if (mounted)
2992
+ return;
2993
+ mounted = true;
2994
+ iframe.contentWindow?.postMessage({
2995
+ channel: "slexkit-secure",
2996
+ type: "mount",
2997
+ id,
2998
+ token,
2999
+ input,
3000
+ policy: options.policy,
3001
+ theme: options.theme,
3002
+ colorMode: resolveColorMode(container),
3003
+ dir: options.dir,
3004
+ labels: options.labels
3005
+ }, "*");
3006
+ };
3007
+ const onMessage = (event) => {
3008
+ if (disposed || event.source !== iframe.contentWindow)
3009
+ return;
3010
+ const data = event.data;
3011
+ if (!data || data.channel !== "slexkit-secure")
3012
+ return;
3013
+ if (data.type === "ready") {
3014
+ ready = true;
3015
+ postMount();
3016
+ return;
3017
+ }
3018
+ if (data.type === "mounted" && data.id === id && data.token === token) {
3019
+ mountAcknowledged = true;
3020
+ lastHeartbeat = Date.now();
3021
+ clearLoadTimer();
3022
+ clearSecureFrameError(container);
3023
+ startHeartbeatWatchdog();
3024
+ syncArtifactSlotsNow();
3025
+ return;
3026
+ }
3027
+ if (data.type === "heartbeat" && data.id === id && data.token === token) {
3028
+ lastHeartbeat = Date.now();
3029
+ return;
3030
+ }
3031
+ if (data.type === "slot-size" && data.id === id && data.token === token && typeof data.slotId === "string") {
3032
+ const slot = artifactSlots.find((item) => item.id === data.slotId);
3033
+ if (slot && typeof data.height === "number" && Number.isFinite(data.height)) {
3034
+ slot.container.style.minHeight = `${Math.max(0, Math.ceil(data.height))}px`;
3035
+ requestArtifactSlotSync();
3036
+ }
3037
+ return;
3038
+ }
3039
+ if (data.type === "error" && (!data.id || data.id === id) && (!data.token || data.token === token)) {
3040
+ const error = data;
3041
+ failLoad(ready ? "mounted" : "ready", typeof error.error?.message === "string" ? error.error.message : undefined);
3042
+ return;
3043
+ }
3044
+ if (data.type !== "fetch" || data.id !== id || data.token !== token || typeof data.requestId !== "string")
3045
+ return;
3046
+ const request = validateSandboxFetchRequest(data.request);
3047
+ if (!request)
3048
+ return;
3049
+ bridgeRuntime.api.fetch(request.url, request).then((result) => {
3050
+ iframe.contentWindow?.postMessage({
3051
+ channel: "slexkit-secure",
3052
+ type: "fetch-result",
3053
+ id,
3054
+ token,
3055
+ requestId: data.requestId,
3056
+ result
3057
+ }, "*");
3058
+ }).catch((error) => {
3059
+ iframe.contentWindow?.postMessage({
3060
+ channel: "slexkit-secure",
3061
+ type: "fetch-result",
3062
+ id,
3063
+ token,
3064
+ requestId: data.requestId,
3065
+ error: serializeRuntimeError(error)
3066
+ }, "*");
3067
+ });
3068
+ };
3069
+ window.addEventListener("message", onMessage);
3070
+ const terminateFrame = ({ keepDiagnostic = false } = {}) => {
3071
+ if (disposed)
3072
+ return;
3073
+ disposed = true;
3074
+ iframe.contentWindow?.postMessage({
3075
+ channel: "slexkit-secure",
3076
+ type: "dispose",
3077
+ id,
3078
+ token
3079
+ }, "*");
3080
+ clearLoadTimer();
3081
+ clearHeartbeatTimer();
3082
+ clearSlotSyncTimer();
3083
+ slotResizeObserver?.disconnect();
3084
+ if (artifactSlots.length) {
3085
+ ownerWindow.removeEventListener("resize", requestArtifactSlotSync);
3086
+ ownerWindow.removeEventListener("scroll", requestArtifactSlotSync, true);
3087
+ }
3088
+ for (const slot of artifactSlots) {
3089
+ const snapshot = slotStyleSnapshots.get(slot.container);
3090
+ if (snapshot) {
3091
+ if (snapshot.secureSlotId === undefined)
3092
+ delete slot.container.dataset.slexkitSecureArtifactSlotId;
3093
+ else
3094
+ slot.container.dataset.slexkitSecureArtifactSlotId = snapshot.secureSlotId;
3095
+ slot.container.style.minHeight = snapshot.minHeight;
3096
+ } else {
3097
+ delete slot.container.dataset.slexkitSecureArtifactSlotId;
3098
+ slot.container.style.removeProperty("min-height");
3099
+ }
3100
+ }
3101
+ if (adjustedAnchorPosition)
3102
+ container.style.position = slotStyleSnapshots.get(container)?.position ?? "";
3103
+ window.removeEventListener("message", onMessage);
3104
+ bridgeRuntime.dispose();
3105
+ iframe.remove();
3106
+ if (!keepDiagnostic)
3107
+ clearSecureFrameError(container);
3108
+ if (!container.hasChildNodes())
3109
+ container.replaceChildren();
3110
+ };
3111
+ return {
3112
+ sandboxed: true,
3113
+ dispose: () => terminateFrame()
3114
+ };
3115
+ }
3116
+ function normalizeArtifactSlots(slots) {
3117
+ if (!slots?.length)
3118
+ return [];
3119
+ const seen = new Set;
3120
+ const normalized = [];
3121
+ for (const slot of slots) {
3122
+ const id = String(slot.id || "").trim();
3123
+ if (!id || seen.has(id) || !slot.container)
3124
+ continue;
3125
+ seen.add(id);
3126
+ normalized.push({ id, container: slot.container });
3127
+ }
3128
+ return normalized;
3129
+ }
3130
+ function randomToken(bytes) {
3131
+ const cryptoApi = globalThis.crypto;
3132
+ if (cryptoApi?.getRandomValues) {
3133
+ const values = new Uint8Array(bytes);
3134
+ cryptoApi.getRandomValues(values);
3135
+ return Array.from(values, (value) => value.toString(16).padStart(2, "0")).join("");
3136
+ }
3137
+ return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
3138
+ }
3139
+ function validateSandboxFetchRequest(value) {
3140
+ if (!value || typeof value !== "object")
3141
+ return null;
3142
+ const request = value;
3143
+ if (typeof request.url !== "string")
3144
+ return null;
3145
+ if (request.method !== "GET" && request.method !== "POST")
3146
+ return null;
3147
+ if (request.credentials !== "omit" && request.credentials !== "same-origin" && request.credentials !== "include")
3148
+ return null;
3149
+ if (typeof request.timeoutMs !== "number" || !Number.isFinite(request.timeoutMs) || request.timeoutMs <= 0)
3150
+ return null;
3151
+ if (request.headers !== undefined && !isStringRecord(request.headers))
3152
+ return null;
3153
+ return {
3154
+ url: request.url,
3155
+ method: request.method,
3156
+ headers: request.headers,
3157
+ body: request.body,
3158
+ credentials: request.credentials,
3159
+ timeoutMs: request.timeoutMs
3160
+ };
3161
+ }
3162
+ function isStringRecord(value) {
3163
+ if (!value || typeof value !== "object" || Array.isArray(value))
3164
+ return false;
3165
+ return Object.values(value).every((item) => typeof item === "string");
3166
+ }
3167
+ function ingest(input) {
3168
+ const expression = parseSourceOrExpression(input);
3169
+ if (!expression)
3170
+ return false;
3171
+ applyExpression(expression);
3172
+ return true;
3173
+ }
3174
+ function copyText(text) {
3175
+ if (navigator.clipboard?.writeText) {
3176
+ navigator.clipboard.writeText(text);
3177
+ return;
3178
+ }
3179
+ const textarea = document.createElement("textarea");
3180
+ textarea.value = text;
3181
+ textarea.setAttribute("readonly", "");
3182
+ textarea.style.position = "fixed";
3183
+ textarea.style.opacity = "0";
3184
+ document.body.appendChild(textarea);
3185
+ textarea.select();
3186
+ document.execCommand("copy");
3187
+ textarea.remove();
3188
+ }
3189
+ function mountBootPreview(source, host, options) {
3190
+ host.replaceChildren();
3191
+ const preview = document.createElement("div");
3192
+ preview.className = "slexkit-preview";
3193
+ host.appendChild(preview);
3194
+ return mount(source, preview, options);
3195
+ }
3196
+ function addSourceControls(pre, host, remount) {
3197
+ const toolbar = document.createElement("div");
3198
+ toolbar.className = "slexkit-source-toolbar";
3199
+ const copyButton = document.createElement("button");
3200
+ copyButton.type = "button";
3201
+ copyButton.className = "slexkit-source-button";
3202
+ copyButton.textContent = "Copy source";
3203
+ copyButton.addEventListener("click", () => copyText(pre.textContent || ""));
3204
+ const viewButton = document.createElement("button");
3205
+ viewButton.type = "button";
3206
+ viewButton.className = "slexkit-source-button";
3207
+ viewButton.textContent = "Hide source";
3208
+ viewButton.addEventListener("click", () => {
3209
+ pre.hidden = !pre.hidden;
3210
+ viewButton.textContent = pre.hidden ? "View source" : "Hide source";
3211
+ });
3212
+ const renderButton = document.createElement("button");
3213
+ renderButton.type = "button";
3214
+ renderButton.className = "slexkit-source-button";
3215
+ renderButton.textContent = "Re-render";
3216
+ renderButton.addEventListener("click", remount);
3217
+ toolbar.append(copyButton, viewButton, renderButton);
3218
+ host.parentNode?.insertBefore(toolbar, host);
3219
+ }
3220
+ function boot(options = {}) {
3221
+ const selector = options.selector || "pre code.language-slex";
3222
+ document.querySelectorAll(selector).forEach((el) => {
3223
+ if (el.dataset.slexkitBooted === "true")
3224
+ return;
3225
+ el.dataset.slexkitBooted = "true";
3226
+ const source = el.textContent || "";
3227
+ const pre = el.closest("pre");
3228
+ const host = document.createElement("div");
3229
+ host.className = "slexkit-card";
3230
+ if (pre)
3231
+ pre.parentNode.insertBefore(host, pre.nextSibling);
3232
+ else
3233
+ el.parentNode.insertBefore(host, el.nextSibling);
3234
+ let cleanup = mountBootPreview(source, host, { theme: options.theme, dir: options.dir, labels: options.labels });
3235
+ const remount = () => {
3236
+ cleanup();
3237
+ cleanup = mountBootPreview(el.textContent || "", host, { theme: options.theme, dir: options.dir, labels: options.labels });
3238
+ };
3239
+ if (pre && options.sourceControls !== false) {
3240
+ addSourceControls(pre, host, remount);
3241
+ }
3242
+ });
3243
+ }
3244
+
3245
+ // src/engine/sandbox-runner.ts
3246
+ var schedulingSnapshot = {
3247
+ setTimeout: typeof globalThis.setTimeout === "function" ? globalThis.setTimeout.bind(globalThis) : undefined,
3248
+ clearTimeout: typeof globalThis.clearTimeout === "function" ? globalThis.clearTimeout.bind(globalThis) : undefined,
3249
+ setInterval: typeof globalThis.setInterval === "function" ? globalThis.setInterval.bind(globalThis) : undefined,
3250
+ clearInterval: typeof globalThis.clearInterval === "function" ? globalThis.clearInterval.bind(globalThis) : undefined,
3251
+ requestAnimationFrame: typeof globalThis.requestAnimationFrame === "function" ? globalThis.requestAnimationFrame.bind(globalThis) : undefined,
3252
+ cancelAnimationFrame: typeof globalThis.cancelAnimationFrame === "function" ? globalThis.cancelAnimationFrame.bind(globalThis) : undefined
3253
+ };
3254
+ function isHostMessage(value) {
3255
+ return !!value && typeof value === "object" && value.channel === "slexkit-secure" && typeof value.type === "string";
3256
+ }
3257
+ function frameRoot() {
3258
+ let root = document.getElementById("slexkit-secure-root");
3259
+ if (!root) {
3260
+ root = document.createElement("div");
3261
+ root.id = "slexkit-secure-root";
3262
+ document.body.appendChild(root);
3263
+ }
3264
+ return root;
3265
+ }
3266
+ function applyColorMode(mode) {
3267
+ if (mode !== "dark" && mode !== "light")
3268
+ return;
3269
+ document.documentElement.classList.toggle("dark", mode === "dark");
3270
+ document.documentElement.classList.toggle("light", mode === "light");
3271
+ document.documentElement.dataset.theme = mode;
3272
+ }
3273
+ function post(message) {
3274
+ window.parent?.postMessage(message, "*");
3275
+ }
3276
+ function createBridgeAdapter(id, token, pending) {
3277
+ let nextRequest = 1;
3278
+ return {
3279
+ fetch(request) {
3280
+ const requestId = `${Date.now()}_${nextRequest++}`;
3281
+ post({
3282
+ channel: "slexkit-secure",
3283
+ type: "fetch",
3284
+ id,
3285
+ token,
3286
+ requestId,
3287
+ request
3288
+ });
3289
+ return new Promise((resolve, reject) => {
3290
+ pending.set(requestId, { resolve, reject });
3291
+ });
3292
+ },
3293
+ setTimeout(fn, ms) {
3294
+ if (!schedulingSnapshot.setTimeout) {
3295
+ throw new SlexKitRuntimeError("policy", "timer_unavailable", "Native timer host is unavailable.");
3296
+ }
3297
+ return schedulingSnapshot.setTimeout(fn, ms);
3298
+ },
3299
+ clearTimeout(id2) {
3300
+ schedulingSnapshot.clearTimeout?.(id2);
3301
+ },
3302
+ setInterval(fn, ms) {
3303
+ if (!schedulingSnapshot.setInterval) {
3304
+ throw new SlexKitRuntimeError("policy", "timer_unavailable", "Native interval host is unavailable.");
3305
+ }
3306
+ return schedulingSnapshot.setInterval(fn, ms);
3307
+ },
3308
+ clearInterval(id2) {
3309
+ schedulingSnapshot.clearInterval?.(id2);
3310
+ },
3311
+ requestAnimationFrame(fn) {
3312
+ if (!schedulingSnapshot.requestAnimationFrame) {
3313
+ throw new SlexKitRuntimeError("policy", "animation_unavailable", "Native animation frame host is unavailable.");
3314
+ }
3315
+ return schedulingSnapshot.requestAnimationFrame(fn);
3316
+ },
3317
+ cancelAnimationFrame(id2) {
3318
+ schedulingSnapshot.cancelAnimationFrame?.(id2);
3319
+ }
3320
+ };
3321
+ }
3322
+ function blockedNetworkError() {
3323
+ return new Error("Native network APIs are disabled inside the SlexKit sandbox. Use api.get(), api.post(), or api.fetch().");
3324
+ }
3325
+ function defineBlockedGlobal(name, value) {
3326
+ try {
3327
+ Object.defineProperty(globalThis, name, {
3328
+ configurable: true,
3329
+ value
3330
+ });
3331
+ } catch {}
3332
+ }
3333
+ function hardenNetworkGlobals() {
3334
+ defineBlockedGlobal("fetch", () => Promise.reject(blockedNetworkError()));
3335
+ defineBlockedGlobal("XMLHttpRequest", class {
3336
+ constructor() {
3337
+ throw blockedNetworkError();
3338
+ }
3339
+ });
3340
+ defineBlockedGlobal("WebSocket", class {
3341
+ constructor() {
3342
+ throw blockedNetworkError();
3343
+ }
3344
+ });
3345
+ defineBlockedGlobal("EventSource", class {
3346
+ constructor() {
3347
+ throw blockedNetworkError();
3348
+ }
3349
+ });
3350
+ defineBlockedGlobal("Worker", class {
3351
+ constructor() {
3352
+ throw blockedNetworkError();
3353
+ }
3354
+ });
3355
+ defineBlockedGlobal("SharedWorker", class {
3356
+ constructor() {
3357
+ throw blockedNetworkError();
3358
+ }
3359
+ });
3360
+ try {
3361
+ Object.defineProperty(navigator, "sendBeacon", {
3362
+ configurable: true,
3363
+ value: () => false
3364
+ });
3365
+ } catch {}
3366
+ }
3367
+ function blockedSchedulingError(name) {
3368
+ return new SlexKitRuntimeError("policy", "native_scheduling_disabled", `Native ${name} is disabled inside the SlexKit sandbox. Use api.${name}().`);
3369
+ }
3370
+ function hardenSchedulingGlobals() {
3371
+ defineBlockedGlobal("setTimeout", () => {
3372
+ throw blockedSchedulingError("setTimeout");
3373
+ });
3374
+ defineBlockedGlobal("setInterval", () => {
3375
+ throw blockedSchedulingError("setInterval");
3376
+ });
3377
+ defineBlockedGlobal("requestAnimationFrame", () => {
3378
+ throw blockedSchedulingError("raf");
3379
+ });
3380
+ defineBlockedGlobal("clearTimeout", () => {
3381
+ return;
3382
+ });
3383
+ defineBlockedGlobal("clearInterval", () => {
3384
+ return;
3385
+ });
3386
+ defineBlockedGlobal("cancelAnimationFrame", () => {
3387
+ return;
3388
+ });
3389
+ }
3390
+ var canvasSnapshot = {
3391
+ getContext: typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement.prototype.getContext : undefined,
3392
+ offscreenCanvas: typeof OffscreenCanvas !== "undefined" ? OffscreenCanvas : undefined
3393
+ };
3394
+ function canvasPolicyError(code, message) {
3395
+ return new SlexKitRuntimeError("policy", code, message);
3396
+ }
3397
+ function assertSandboxCanvasPolicy(policy, width, height, contextId) {
3398
+ if (!policy.enabled) {
3399
+ throw canvasPolicyError("canvas_disabled", "Canvas access is disabled.");
3400
+ }
3401
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
3402
+ throw canvasPolicyError("canvas_size_invalid", "Canvas size must be positive finite numbers.");
3403
+ }
3404
+ const pixels = Math.ceil(width) * Math.ceil(height);
3405
+ if (policy.maxPixels !== undefined && pixels > policy.maxPixels) {
3406
+ throw canvasPolicyError("canvas_too_large", "Canvas size exceeds the runtime policy limit.");
3407
+ }
3408
+ if (contextId && policy.allowedContexts?.length && !policy.allowedContexts.includes(contextId)) {
3409
+ throw canvasPolicyError("canvas_context_blocked", `Canvas context ${contextId} is not allowed.`);
3410
+ }
3411
+ }
3412
+ function hardenCanvasGlobals(policy) {
3413
+ if (typeof HTMLCanvasElement !== "undefined" && canvasSnapshot.getContext) {
3414
+ const contextCanvases = new WeakSet;
3415
+ let contextCanvasCount = 0;
3416
+ const canvasPolicy2 = policy.canvas;
3417
+ HTMLCanvasElement.prototype.getContext = function getContext(contextId, options) {
3418
+ if (!canvasPolicy2?.enabled) {
3419
+ throw canvasPolicyError("canvas_disabled", "Canvas access is disabled.");
3420
+ }
3421
+ assertSandboxCanvasPolicy(canvasPolicy2, this.width, this.height, contextId);
3422
+ if (!contextCanvases.has(this)) {
3423
+ if (canvasPolicy2.maxCanvases !== undefined && contextCanvasCount >= canvasPolicy2.maxCanvases) {
3424
+ throw canvasPolicyError("canvas_limit", "Canvas limit exceeded.");
3425
+ }
3426
+ contextCanvases.add(this);
3427
+ contextCanvasCount += 1;
3428
+ }
3429
+ return canvasSnapshot.getContext.call(this, contextId, options);
3430
+ };
3431
+ }
3432
+ if (canvasSnapshot.offscreenCanvas) {
3433
+ const OriginalOffscreenCanvas = canvasSnapshot.offscreenCanvas;
3434
+ defineBlockedGlobal("OffscreenCanvas", class extends OriginalOffscreenCanvas {
3435
+ constructor(width, height) {
3436
+ const canvasPolicy2 = policy.canvas;
3437
+ if (!canvasPolicy2?.enabled) {
3438
+ throw canvasPolicyError("canvas_disabled", "Canvas access is disabled.");
3439
+ }
3440
+ assertSandboxCanvasPolicy(canvasPolicy2, width, height);
3441
+ super(width, height);
3442
+ }
3443
+ });
3444
+ }
3445
+ }
3446
+ function startSlexKitSandboxRunner() {
3447
+ hardenNetworkGlobals();
3448
+ hardenSchedulingGlobals();
3449
+ const pendingFetches = new Map;
3450
+ let cleanup;
3451
+ let runtimeDispose;
3452
+ let activeId;
3453
+ let activeToken;
3454
+ let heartbeatTimer;
3455
+ let slotResizeObserver;
3456
+ function clearHeartbeat() {
3457
+ if (heartbeatTimer === undefined)
3458
+ return;
3459
+ schedulingSnapshot.clearInterval?.(heartbeatTimer);
3460
+ heartbeatTimer = undefined;
3461
+ }
3462
+ function startHeartbeat(policy, id, token) {
3463
+ clearHeartbeat();
3464
+ const intervalMs = Math.max(100, policy.execution?.heartbeatIntervalMs ?? 1000);
3465
+ if (!schedulingSnapshot.setInterval)
3466
+ return;
3467
+ heartbeatTimer = schedulingSnapshot.setInterval(() => {
3468
+ post({
3469
+ channel: "slexkit-secure",
3470
+ type: "heartbeat",
3471
+ id,
3472
+ token
3473
+ });
3474
+ }, intervalMs);
3475
+ }
3476
+ function dispose(id) {
3477
+ if (id && activeId && id !== activeId)
3478
+ return;
3479
+ clearHeartbeat();
3480
+ slotResizeObserver?.disconnect();
3481
+ slotResizeObserver = undefined;
3482
+ cleanup?.();
3483
+ runtimeDispose?.();
3484
+ for (const pending of pendingFetches.values()) {
3485
+ pending.reject(new Error("SlexKit sandbox runtime was disposed."));
3486
+ }
3487
+ pendingFetches.clear();
3488
+ cleanup = undefined;
3489
+ runtimeDispose = undefined;
3490
+ activeId = undefined;
3491
+ activeToken = undefined;
3492
+ const root = frameRoot();
3493
+ root.style.removeProperty("position");
3494
+ root.style.removeProperty("min-height");
3495
+ root.replaceChildren();
3496
+ }
3497
+ function slotElement(slotId) {
3498
+ return document.getElementById(`slexkit-slot-${slotId}`);
3499
+ }
3500
+ function reportSlotSize(slotId, element) {
3501
+ if (!activeId || !activeToken)
3502
+ return;
3503
+ const height = Math.max(element.scrollHeight, element.getBoundingClientRect().height);
3504
+ post({
3505
+ channel: "slexkit-secure",
3506
+ type: "slot-size",
3507
+ id: activeId,
3508
+ token: activeToken,
3509
+ slotId,
3510
+ height
3511
+ });
3512
+ }
3513
+ function applySlotRects(slots) {
3514
+ const root = frameRoot();
3515
+ const multiSlot = slots.length > 1;
3516
+ if (multiSlot) {
3517
+ const maxBottom = Math.max(1, ...slots.map((slot) => slot.top + slot.height));
3518
+ root.style.position = "relative";
3519
+ root.style.minHeight = `${Math.ceil(maxBottom)}px`;
3520
+ } else {
3521
+ root.style.removeProperty("position");
3522
+ root.style.removeProperty("min-height");
3523
+ }
3524
+ slotResizeObserver?.disconnect();
3525
+ slotResizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver((entries) => {
3526
+ for (const entry of entries) {
3527
+ const element = entry.target;
3528
+ const slotId = element.dataset.slexkitSlotId;
3529
+ if (slotId)
3530
+ reportSlotSize(slotId, element);
3531
+ }
3532
+ }) : undefined;
3533
+ for (const slot of slots) {
3534
+ const element = slotElement(slot.id);
3535
+ if (!element)
3536
+ continue;
3537
+ element.dataset.slexkitSlotId = slot.id;
3538
+ element.style.boxSizing = "border-box";
3539
+ if (multiSlot) {
3540
+ element.style.position = "absolute";
3541
+ element.style.left = `${slot.left}px`;
3542
+ element.style.top = `${slot.top}px`;
3543
+ } else {
3544
+ element.style.removeProperty("position");
3545
+ element.style.removeProperty("left");
3546
+ element.style.removeProperty("top");
3547
+ }
3548
+ if (slot.width > 0)
3549
+ element.style.width = `${slot.width}px`;
3550
+ slotResizeObserver?.observe(element);
3551
+ reportSlotSize(slot.id, element);
3552
+ }
3553
+ }
3554
+ async function mountArtifact(message) {
3555
+ dispose();
3556
+ activeId = message.id;
3557
+ activeToken = message.token;
3558
+ hardenCanvasGlobals(message.policy);
3559
+ applyColorMode(message.colorMode);
3560
+ const runtime = createSecureRuntime(message.policy, createBridgeAdapter(message.id, message.token, pendingFetches));
3561
+ runtimeDispose = runtime.dispose;
3562
+ cleanup = mount(message.input, frameRoot(), {
3563
+ theme: message.theme,
3564
+ dir: message.dir,
3565
+ labels: message.labels,
3566
+ api: runtime.api
3567
+ });
3568
+ startHeartbeat(message.policy, message.id, message.token);
3569
+ post({
3570
+ channel: "slexkit-secure",
3571
+ type: "mounted",
3572
+ id: message.id,
3573
+ token: message.token
3574
+ });
3575
+ }
3576
+ window.addEventListener("message", (event) => {
3577
+ if (event.source !== window.parent || !isHostMessage(event.data))
3578
+ return;
3579
+ const message = event.data;
3580
+ try {
3581
+ if (message.type === "mount") {
3582
+ mountArtifact(message).catch((error) => {
3583
+ post({
3584
+ channel: "slexkit-secure",
3585
+ type: "error",
3586
+ id: message.id,
3587
+ token: message.token,
3588
+ error: serializeRuntimeError(error)
3589
+ });
3590
+ });
3591
+ } else if (message.type === "dispose") {
3592
+ if (activeToken && message.token !== activeToken)
3593
+ return;
3594
+ dispose(message.id);
3595
+ post({
3596
+ channel: "slexkit-secure",
3597
+ type: "disposed",
3598
+ id: message.id,
3599
+ token: message.token
3600
+ });
3601
+ } else if (message.type === "fetch-result") {
3602
+ const response = message;
3603
+ if (activeToken && response.token !== activeToken)
3604
+ return;
3605
+ const pending = pendingFetches.get(response.requestId);
3606
+ if (!pending)
3607
+ return;
3608
+ pendingFetches.delete(response.requestId);
3609
+ if (response.error)
3610
+ pending.reject(deserializeRuntimeError(response.error));
3611
+ else
3612
+ pending.resolve(response.result);
3613
+ } else if (message.type === "slots") {
3614
+ if (activeToken && message.token !== activeToken)
3615
+ return;
3616
+ applySlotRects(message.slots);
3617
+ }
3618
+ } catch (error) {
3619
+ post({
3620
+ channel: "slexkit-secure",
3621
+ type: "error",
3622
+ id: "id" in message ? message.id : undefined,
3623
+ token: activeToken,
3624
+ error: serializeRuntimeError(error)
3625
+ });
3626
+ }
3627
+ });
3628
+ post({
3629
+ channel: "slexkit-secure",
3630
+ type: "ready"
3631
+ });
3632
+ }
3633
+
3634
+ // src/runtime.ts
3635
+ setSlexKitRuntimeUrl(import.meta.url);
3636
+ var mountApi = mount;
3637
+ var ingestApi = ingest;
3638
+ var bootApi = boot;
3639
+ var disposeNamespaceApi = disposeNamespace;
3640
+ var registerApi = register;
3641
+ var getRendererApi = getRenderer;
3642
+ var diagnoseSlexKitSourceApi = diagnoseSlexKitSource;
3643
+ var SlexKitSyntaxErrorApi = SlexKitSyntaxError;
3644
+ var formatSlexKitDiagnosticApi = formatSlexKitDiagnostic;
3645
+ var mountSecureArtifactApi = mountSecureArtifact;
3646
+ var parseSlexSourceApi = parseSlexSource;
3647
+ var parseSlexKitDslApi = parseSlexKitDsl;
3648
+ var createSecureRuntimeApi = createSecureRuntime;
3649
+ var SlexKitRuntimeErrorApi = SlexKitRuntimeError;
3650
+ var getSlexKitRuntimeUrlApi = getSlexKitRuntimeUrl;
3651
+ var setSlexKitRuntimeUrlApi = setSlexKitRuntimeUrl;
3652
+ var createSlexKitMarkdownRuntimeHostApi = createSlexKitMarkdownRuntimeHost;
3653
+ var getSlexKitMarkdownRuntimeHostApi = getSlexKitMarkdownRuntimeHost;
3654
+ var installSlexKitMarkdownRuntimeHostApi = installSlexKitMarkdownRuntimeHost;
3655
+ var attachComponentDisposerApi = attachComponentDisposer;
3656
+ var configureComponentScopeApi = configureComponentScope;
3657
+ var startSlexKitSandboxRunnerApi = startSlexKitSandboxRunner;
3658
+ var getSlexKitInfoApi = getSlexKitInfo;
3659
+ export {
3660
+ startSlexKitSandboxRunnerApi as startSlexKitSandboxRunner,
3661
+ setSlexKitRuntimeUrlApi as setSlexKitRuntimeUrl,
3662
+ registerApi as register,
3663
+ parseSlexSourceApi as parseSlexSource,
3664
+ parseSlexKitDslApi as parseSlexKitDsl,
3665
+ mountSecureArtifactApi as mountSecureArtifact,
3666
+ mountApi as mount,
3667
+ installSlexKitMarkdownRuntimeHostApi as installSlexKitMarkdownRuntimeHost,
3668
+ ingestApi as ingest,
3669
+ getSlexKitRuntimeUrlApi as getSlexKitRuntimeUrl,
3670
+ getSlexKitMarkdownRuntimeHostApi as getSlexKitMarkdownRuntimeHost,
3671
+ getSlexKitInfoApi as getSlexKitInfo,
3672
+ getRendererApi as getRenderer,
3673
+ formatSlexKitDiagnosticApi as formatSlexKitDiagnostic,
3674
+ disposeNamespaceApi as disposeNamespace,
3675
+ diagnoseSlexKitSourceApi as diagnoseSlexKitSource,
3676
+ createSlexKitMarkdownRuntimeHostApi as createSlexKitMarkdownRuntimeHost,
3677
+ createSecureRuntimeApi as createSecureRuntime,
3678
+ configureComponentScopeApi as configureComponentScope,
3679
+ bootApi as boot,
3680
+ attachComponentDisposerApi as attachComponentDisposer,
3681
+ SlexKitSyntaxErrorApi as SlexKitSyntaxError,
3682
+ SlexKitRuntimeErrorApi as SlexKitRuntimeError,
3683
+ SLEX_PROTOCOL_VERSION,
3684
+ SLEXKIT_VERSION,
3685
+ SLEXKIT_COMPONENTS_VERSION
3686
+ };