@hozu/runtime-client 0.7.0 → 0.9.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.
@@ -44,59 +44,6 @@ var init_uploads = __esm({
44
44
  }
45
45
  });
46
46
 
47
- // packages/runtime-client/dist/dev.js
48
- var dev_exports = {};
49
- __export(dev_exports, {
50
- expose: () => expose,
51
- restore: () => restore
52
- });
53
- function restore(doc) {
54
- const store2 = storage(doc);
55
- let saved = {};
56
- try {
57
- saved = JSON.parse(store2?.getItem(KEY) ?? "{}");
58
- } catch {
59
- }
60
- store2?.removeItem(KEY);
61
- return (id, machine) => {
62
- const entry = saved[id];
63
- if (!entry || !machine || entry.machine !== JSON.stringify(machine))
64
- return void 0;
65
- return machine.states[entry.snapshot.state]?.invoke ? void 0 : entry.snapshot;
66
- };
67
- }
68
- function expose(doc, apps, machines) {
69
- const win = doc.defaultView;
70
- if (!win)
71
- return;
72
- win.__hozu = {
73
- save() {
74
- const saved = {};
75
- for (const [id, app] of apps) {
76
- const snapshot = app.snapshot();
77
- const machine = machines.get(id);
78
- if (snapshot && machine)
79
- saved[id] = { machine: JSON.stringify(machine), snapshot };
80
- }
81
- storage(doc)?.setItem(KEY, JSON.stringify(saved));
82
- }
83
- };
84
- }
85
- var KEY, storage;
86
- var init_dev = __esm({
87
- "packages/runtime-client/dist/dev.js"() {
88
- "use strict";
89
- KEY = "hozu:snapshots";
90
- storage = (doc) => {
91
- try {
92
- return doc.defaultView?.sessionStorage ?? null;
93
- } catch {
94
- return null;
95
- }
96
- };
97
- }
98
- });
99
-
100
47
  // packages/runtime-client/dist/motion.js
101
48
  var motion_exports = {};
102
49
  __export(motion_exports, {
@@ -190,65 +137,28 @@ var init_motion = __esm({
190
137
  }
191
138
  });
192
139
 
193
- // packages/runtime-client/dist/visible.js
194
- var visible_exports = {};
195
- __export(visible_exports, {
196
- watch: () => watch
140
+ // packages/runtime-client/dist/component.js
141
+ var component_exports = {};
142
+ __export(component_exports, {
143
+ mountComponent: () => mountComponent
197
144
  });
198
- function watch(doc) {
199
- const win = doc.defaultView;
200
- if (!win?.IntersectionObserver || watched.has(doc))
201
- return;
202
- watched.add(doc);
203
- const seen = new IntersectionObserver((entries) => {
204
- for (const e of entries)
205
- if (e.isIntersecting)
206
- e.target.dispatchEvent(new win.Event("visible"));
207
- });
208
- const scan = (root) => {
209
- if (root instanceof win.Element && root.hasAttribute("data-hozu-visible"))
210
- seen.observe(root);
211
- for (const el of root.querySelectorAll("[data-hozu-visible]"))
212
- seen.observe(el);
213
- };
214
- scan(doc);
215
- new win.MutationObserver((records) => {
216
- for (const r of records)
217
- for (const n of r.addedNodes)
218
- if (n.nodeType === 1)
219
- scan(n);
220
- }).observe(doc.body, { childList: true, subtree: true });
221
- }
222
- var watched;
223
- var init_visible = __esm({
224
- "packages/runtime-client/dist/visible.js"() {
225
- "use strict";
226
- watched = /* @__PURE__ */ new WeakSet();
227
- }
228
- });
229
-
230
- // packages/runtime-client/dist/widget.js
231
- var widget_exports = {};
232
- __export(widget_exports, {
233
- mountWidget: () => mountWidget
234
- });
235
- function mountWidget(h) {
145
+ function mountComponent(h) {
236
146
  const controller = new AbortController();
237
- h.el.setAttribute("data-hozu-widget", h.name);
238
- h.el.setAttribute("data-hozu-widget-state", "loading");
147
+ h.el.setAttribute("data-hozu-component", h.name);
148
+ h.el.setAttribute("data-hozu-component-state", "loading");
239
149
  let props = h.props();
240
150
  let instance;
241
151
  const fail = (error) => {
242
152
  instance = void 0;
243
- h.el.setAttribute("data-hozu-widget-state", "failed");
244
- h.doc.defaultView?.console.error(`Widget ${h.name} failed`, error);
153
+ h.el.setAttribute("data-hozu-component-state", "failed");
154
+ h.doc.defaultView?.console.error(`Component ${h.name} failed`, error);
245
155
  };
246
156
  const start = () => void h.load(h.ref.url).then((setup) => {
247
157
  if (controller.signal.aborted)
248
158
  return;
249
159
  try {
250
160
  instance = setup({ el: h.el, props, emit: h.emit, signal: controller.signal }) ?? void 0;
251
- h.el.setAttribute("data-hozu-widget-state", "mounted");
161
+ h.el.setAttribute("data-hozu-component-state", "mounted");
252
162
  } catch (error) {
253
163
  fail(error);
254
164
  }
@@ -286,232 +196,119 @@ function mountWidget(h) {
286
196
  instance?.destroy?.();
287
197
  });
288
198
  }
289
- var init_widget = __esm({
290
- "packages/runtime-client/dist/widget.js"() {
199
+ var init_component = __esm({
200
+ "packages/runtime-client/dist/component.js"() {
291
201
  "use strict";
292
202
  }
293
203
  });
294
204
 
295
- // packages/runtime-client/dist/live.js
296
- var live_exports = {};
297
- __export(live_exports, {
298
- liveStream: () => liveStream
205
+ // packages/runtime-client/dist/visible.js
206
+ var visible_exports = {};
207
+ __export(visible_exports, {
208
+ watch: () => watch
299
209
  });
300
- var liveStream;
301
- var init_live = __esm({
302
- "packages/runtime-client/dist/live.js"() {
210
+ function watch(doc) {
211
+ const win = doc.defaultView;
212
+ if (!win?.IntersectionObserver || watched.has(doc))
213
+ return;
214
+ watched.add(doc);
215
+ const seen = new IntersectionObserver((entries) => {
216
+ for (const e of entries)
217
+ if (e.isIntersecting)
218
+ e.target.dispatchEvent(new win.Event("visible"));
219
+ });
220
+ const scan = (root) => {
221
+ if (root instanceof win.Element && root.hasAttribute("data-hozu-visible"))
222
+ seen.observe(root);
223
+ for (const el of root.querySelectorAll("[data-hozu-visible]"))
224
+ seen.observe(el);
225
+ };
226
+ scan(doc);
227
+ new win.MutationObserver((records) => {
228
+ for (const r of records)
229
+ for (const n of r.addedNodes)
230
+ if (n.nodeType === 1)
231
+ scan(n);
232
+ }).observe(doc.body, { childList: true, subtree: true });
233
+ }
234
+ var watched;
235
+ var init_visible = __esm({
236
+ "packages/runtime-client/dist/visible.js"() {
303
237
  "use strict";
304
- liveStream = (doc) => (onTags) => {
305
- const Source = doc.defaultView?.EventSource;
306
- if (!Source)
307
- return;
308
- new Source(new URL("live", import.meta.url)).addEventListener("message", (e) => onTags(JSON.parse(e.data)));
309
- };
238
+ watched = /* @__PURE__ */ new WeakSet();
310
239
  }
311
240
  });
312
241
 
313
- // packages/runtime-client/dist/navigate.js
314
- var navigate_exports = {};
315
- __export(navigate_exports, {
316
- islandsIn: () => islandsIn,
317
- ranges: () => ranges,
318
- routeOf: () => routeOf,
319
- soft: () => soft,
320
- swap: () => swap,
321
- track: () => track
242
+ // packages/runtime-client/dist/dev.js
243
+ var dev_exports = {};
244
+ __export(dev_exports, {
245
+ expose: () => expose,
246
+ restore: () => restore
322
247
  });
323
- function routeOf(routes, pathname) {
324
- let best = null;
325
- let fewest = Infinity;
326
- for (const [id, path] of Object.entries(routes)) {
327
- const { pattern, keys } = patternOf(path);
328
- if (keys < fewest && pattern.test(pathname)) {
329
- best = id;
330
- fewest = keys;
331
- }
332
- }
333
- return best;
334
- }
335
- function islandsIn(root) {
336
- const found = [];
337
- const walker = (root.ownerDocument ?? root).createTreeWalker(root, 128);
338
- let view = null;
339
- while (walker.nextNode()) {
340
- const c = walker.currentNode;
341
- if (c.data === "i")
342
- found.push({ marker: c, view });
343
- else if (c.data.startsWith("v:"))
344
- view = c.data.slice(2);
345
- else if (c.data === "/v")
346
- view = null;
347
- }
348
- return found;
349
- }
350
- function track(state, session, payload, found) {
351
- session.islands(payload).forEach((island, i) => {
352
- const view = found[i]?.view;
353
- if (!view)
354
- return;
355
- const owners = state.views.get(view) ?? /* @__PURE__ */ new Set();
356
- owners.add(island.feature);
357
- state.views.set(view, owners);
358
- });
359
- state.soft = payload.soft ?? {};
360
- Object.assign(state.routes, payload.routes);
361
- }
362
- function ranges(body) {
363
- const out = /* @__PURE__ */ new Map();
364
- let current = null;
365
- for (const n of [...body.childNodes]) {
366
- if (n.nodeType === 8 && n.data.startsWith("v:")) {
367
- current = [n];
368
- out.set(n.data.slice(2), current);
369
- } else if (current) {
370
- current.push(n);
371
- if (n.nodeType === 8 && n.data === "/v")
372
- current = null;
373
- }
374
- }
375
- return out;
376
- }
377
- async function swap(session, state, next, kept) {
378
- const { doc, apps } = session;
379
- const { views } = state;
380
- const script = next.getElementById("hozu-payload");
381
- const fresh = ranges(next.body);
382
- const live = ranges(doc.body);
383
- if (!script?.textContent || kept.some((v) => !fresh.has(v) || !live.has(v)))
384
- return false;
385
- const payload = JSON.parse(script.textContent);
386
- const keep = new Set(kept);
387
- const found = islandsIn(next.body);
388
- const needed = /* @__PURE__ */ new Set();
389
- for (const v of keep)
390
- for (const f of views.get(v) ?? [])
391
- needed.add(f);
392
- session.islands(payload).forEach((island, i) => {
393
- const view = found[i]?.view;
394
- if (view === void 0 || view === null || !keep.has(view))
395
- needed.add(island.feature);
396
- });
397
- for (const [id, app] of apps)
398
- if (!needed.has(id)) {
399
- app.destroy();
400
- apps.delete(id);
401
- }
402
- for (const v of [...views.keys()])
403
- if (!keep.has(v))
404
- views.delete(v);
405
- const anchor = doc.getElementById("hozu-payload");
406
- let cursor = 0;
407
- const order = [...live.keys()].filter((v) => keep.has(v));
408
- for (const [ref, nodes] of fresh) {
409
- if (keep.has(ref)) {
410
- cursor++;
411
- continue;
412
- }
413
- const before = order[cursor] ? live.get(order[cursor])[0] : anchor;
414
- for (const n of nodes)
415
- doc.body.insertBefore(n, before);
248
+ function restore(doc) {
249
+ const store2 = storage(doc);
250
+ let saved = {};
251
+ try {
252
+ saved = JSON.parse(store2?.getItem(KEY) ?? "{}");
253
+ } catch {
416
254
  }
417
- for (const [ref, nodes] of live)
418
- if (!keep.has(ref))
419
- for (const n of nodes)
420
- n.remove();
421
- if (anchor)
422
- anchor.textContent = script.textContent;
423
- for (const el of doc.head.querySelectorAll(HEAD))
424
- el.remove();
425
- for (const el of next.head.querySelectorAll(HEAD))
426
- doc.head.append(el);
427
- doc.documentElement.lang = next.documentElement.lang;
428
- await session.mount(payload, found.map((f) => f.view !== null && keep.has(f.view) ? void 0 : f.marker));
429
- track(state, session, payload, found);
430
- return true;
255
+ store2?.removeItem(KEY);
256
+ return (id, machine) => {
257
+ const entry = saved[id];
258
+ if (!entry || !machine || entry.machine !== JSON.stringify(machine))
259
+ return void 0;
260
+ return machine.states[entry.snapshot.state]?.invoke ? void 0 : entry.snapshot;
261
+ };
431
262
  }
432
- function soft(session) {
433
- const { doc } = session;
263
+ function expose(doc, apps, machines) {
434
264
  const win = doc.defaultView;
435
- const script = doc.getElementById("hozu-payload");
436
- if (!win?.navigation || !script?.textContent)
265
+ if (!win)
437
266
  return;
438
- const state = { views: /* @__PURE__ */ new Map(), soft: {}, routes: {} };
439
- track(state, session, JSON.parse(script.textContent), islandsIn(doc.body));
440
- let announcer = null;
441
- let bypass = false;
442
- const announce = (title) => {
443
- if (!announcer) {
444
- announcer = doc.createElement("div");
445
- announcer.setAttribute("aria-live", "polite");
446
- announcer.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap";
447
- doc.body.append(announcer);
267
+ win.__hozu = {
268
+ save() {
269
+ const saved = {};
270
+ for (const [id, app] of apps) {
271
+ const snapshot = app.snapshot();
272
+ const machine = machines.get(id);
273
+ if (snapshot && machine)
274
+ saved[id] = { machine: JSON.stringify(machine), snapshot };
275
+ }
276
+ storage(doc)?.setItem(KEY, JSON.stringify(saved));
448
277
  }
449
- announcer.textContent = title;
450
278
  };
451
- win.navigation.addEventListener("navigate", (event) => {
452
- const e = event;
453
- if (bypass || !e.canIntercept || e.hashChange || e.downloadRequest !== null || e.formData)
454
- return;
455
- if (e.navigationType === "reload")
456
- return;
457
- const url = new URL(e.destination.url);
458
- if (url.origin !== win.location.origin)
459
- return;
460
- const target = routeOf(state.routes, url.pathname);
461
- const kept = target ? state.soft[target] : void 0;
462
- if (!kept)
463
- return;
464
- const hard = () => {
465
- bypass = true;
466
- win.location.replace(url.href);
467
- };
468
- e.intercept({
469
- handler: async () => {
470
- const root = doc.documentElement;
471
- root.setAttribute("data-hozu-navigating", "");
472
- try {
473
- const response = await win.fetch(url.href, { headers: { accept: "text/html" } });
474
- const type = response.headers.get("content-type") ?? "";
475
- if (response.status !== 200 || response.redirected || !type.includes("text/html"))
476
- return hard();
477
- const next = new win.DOMParser().parseFromString(await response.text(), "text/html");
478
- let ok = true;
479
- const run = async () => {
480
- ok = await swap(session, state, next, kept);
481
- };
482
- const transition2 = doc.startViewTransition;
483
- if (transition2)
484
- await transition2.call(doc, run).updateCallbackDone;
485
- else
486
- await run();
487
- if (!ok)
488
- return hard();
489
- announce(doc.title);
490
- } catch {
491
- hard();
492
- } finally {
493
- root.removeAttribute("data-hozu-navigating");
494
- }
495
- }
496
- });
497
- });
498
279
  }
499
- var HEAD, groups, patternOf;
500
- var init_navigate = __esm({
501
- "packages/runtime-client/dist/navigate.js"() {
280
+ var KEY, storage;
281
+ var init_dev = __esm({
282
+ "packages/runtime-client/dist/dev.js"() {
502
283
  "use strict";
503
- HEAD = 'title,meta[name=description],meta[name=robots],link[rel=canonical],meta[property],script[type="application/ld+json"]';
504
- groups = { "?": "(?:/[^/]+)?", "+": "(?:/[^/]+)+", "*": "(?:/[^/]+)*" };
505
- patternOf = (path) => {
506
- let keys = 0;
507
- const source = path.split(/(\/:[A-Za-z]\w*[?*+]?)/).map((part, i) => {
508
- if (i % 2 === 0)
509
- return part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
510
- const mod = part.at(-1);
511
- keys += mod === "+" || mod === "*" ? 1.01 : 1;
512
- return groups[mod] ?? "/[^/]+";
513
- }).join("");
514
- return { pattern: new RegExp(`^${source.replace(/\/$/, "")}/?$`), keys };
284
+ KEY = "hozu:snapshots";
285
+ storage = (doc) => {
286
+ try {
287
+ return doc.defaultView?.sessionStorage ?? null;
288
+ } catch {
289
+ return null;
290
+ }
291
+ };
292
+ }
293
+ });
294
+
295
+ // packages/runtime-client/dist/live.js
296
+ var live_exports = {};
297
+ __export(live_exports, {
298
+ liveStream: () => liveStream
299
+ });
300
+ var liveStream;
301
+ var init_live = __esm({
302
+ "packages/runtime-client/dist/live.js"() {
303
+ "use strict";
304
+ liveStream = (doc) => (onTags, tags) => {
305
+ const Source = doc.defaultView?.EventSource;
306
+ if (!Source)
307
+ return;
308
+ const url = new URL("live", import.meta.url);
309
+ for (const t of new Set(tags))
310
+ url.searchParams.append("tag", t);
311
+ new Source(url).addEventListener("message", (e) => onTags(JSON.parse(e.data)));
515
312
  };
516
313
  }
517
314
  });
@@ -520,6 +317,8 @@ var init_navigate = __esm({
520
317
  function getIn(value, path) {
521
318
  let current = value;
522
319
  for (const key of path) {
320
+ if (typeof current === "string" && key === "length")
321
+ return current.length;
523
322
  if (current === null || typeof current !== "object")
524
323
  return null;
525
324
  current = current[key];
@@ -584,8 +383,8 @@ var CompileError = class extends Error {
584
383
  name = "CompileError";
585
384
  };
586
385
  function compileValue(v, fns) {
587
- if ("literal" in v) {
588
- const literal = v.literal;
386
+ if ("literal" in v || "formRef" in v) {
387
+ const literal = "literal" in v ? v.literal : v.formRef;
589
388
  return () => literal;
590
389
  }
591
390
  if ("object" in v) {
@@ -614,16 +413,22 @@ function compileValue(v, fns) {
614
413
  const test = guard(v.test, fns);
615
414
  return (env) => test(env);
616
415
  }
617
- const { path } = v;
618
- if (v.ref === "binding") {
619
- const depth = v.depth;
416
+ if ("endpoint" in v) {
417
+ const id = v.endpoint;
418
+ const input = compileValue(v.input ?? { literal: null }, fns);
419
+ return (env) => pathOf(env.routes?.[id] ?? "", null, input(env));
420
+ }
421
+ const r = v;
422
+ const { path } = r;
423
+ if (r.ref === "binding") {
424
+ const depth = r.depth;
620
425
  return path.length ? (env) => getIn(env.bindings?.[depth], path) : (env) => env.bindings?.[depth] ?? null;
621
426
  }
622
- if (v.ref === "dom") {
427
+ if (r.ref === "dom") {
623
428
  const [field, ...rest] = path;
624
- return (env) => env.dom && field ? getIn(env.dom(field), rest) : null;
429
+ return (env) => env.dom && field ? getIn(env.dom(field), rest) ?? (field === "formAll" ? [] : null) : null;
625
430
  }
626
- const ref = v.ref;
431
+ const ref = r.ref;
627
432
  if (path.length === 0)
628
433
  return (env) => env[ref] ?? null;
629
434
  return (env) => getIn(env[ref], path);
@@ -686,7 +491,7 @@ function assign(a, fns) {
686
491
  const list = getIn(ctx, path);
687
492
  const match = v({ ...env, context: ctx });
688
493
  const items = Array.isArray(list) ? list : [];
689
- return setIn(ctx, path, items.filter((item) => !equal(item !== null && typeof item === "object" && !Array.isArray(item) ? item[key] : null, match)));
494
+ return setIn(ctx, path, items.filter((item) => !equal(key === null ? item : item !== null && typeof item === "object" && !Array.isArray(item) ? item[key] : null, match)));
690
495
  };
691
496
  }
692
497
  }
@@ -819,6 +624,27 @@ function messageOf(data, error) {
819
624
  return error;
820
625
  }
821
626
 
627
+ // packages/core/dist/ir/forms.js
628
+ function formEntries(entries, submitter) {
629
+ const first = /* @__PURE__ */ Object.create(null);
630
+ const all = /* @__PURE__ */ Object.create(null);
631
+ const add = (k, v) => {
632
+ const s = typeof v === "string" ? v : v.name;
633
+ const list = all[k];
634
+ if (list)
635
+ list.push(s);
636
+ else {
637
+ all[k] = [s];
638
+ first[k] = s;
639
+ }
640
+ };
641
+ for (const [k, v] of entries)
642
+ add(k, v);
643
+ if (submitter)
644
+ add(...submitter);
645
+ return { first, all };
646
+ }
647
+
822
648
  // packages/runtime-client/dist/dom.js
823
649
  var SVG_NS = "http://www.w3.org/2000/svg";
824
650
  var stringBooleans = /* @__PURE__ */ new Set(["contenteditable", "draggable", "spellcheck"]);
@@ -861,13 +687,12 @@ var domField = (e) => (field) => {
861
687
  return num(t?.valueAsNumber);
862
688
  case "files":
863
689
  return fileList(t?.files ?? e.dataTransfer?.files);
864
- case "form": {
865
- const out = {};
690
+ case "form":
691
+ case "formAll": {
866
692
  const form = e.target;
867
693
  const win = form.ownerDocument.defaultView;
868
- for (const [k, v] of new (win?.FormData ?? FormData)(form))
869
- out[k] = typeof v === "string" ? v : v.name;
870
- return out;
694
+ const d = formEntries(new (win?.FormData ?? FormData)(form, e.submitter));
695
+ return field === "form" ? d.first : d.all;
871
696
  }
872
697
  case "open":
873
698
  return t?.open === true;
@@ -956,7 +781,7 @@ function clear(start, end) {
956
781
  function createApp(doc, options) {
957
782
  const { machine, fns = {}, params = null, search = null, routes = {} } = options;
958
783
  const { data: payload } = store(options.payload);
959
- const ranges2 = [];
784
+ const ranges = [];
960
785
  const timers = /* @__PURE__ */ new Set();
961
786
  let first = machine ? options.snapshot ? null : init(machine) : null;
962
787
  let snapshot = options.snapshot ?? first?.snapshot ?? null;
@@ -1121,8 +946,8 @@ function createApp(doc, options) {
1121
946
  case "each":
1122
947
  each(node, scope, c, block, ns);
1123
948
  return;
1124
- case "widget":
1125
- widget(node, scope, c, block);
949
+ case "component":
950
+ component(node, scope, c, block);
1126
951
  return;
1127
952
  case "if": {
1128
953
  const test = () => value({ test: node.test }, scope) === true;
@@ -1205,8 +1030,9 @@ function createApp(doc, options) {
1205
1030
  style.setProperty(name, text(x));
1206
1031
  });
1207
1032
  };
1208
- const widget = (node, scope, c, block) => {
1209
- const ref = options.widgets?.[node.widget];
1033
+ const component = (node, scope, c, block) => {
1034
+ const name = node.use.component;
1035
+ const ref = options.components?.[name];
1210
1036
  const tag = ref?.tag ?? "div";
1211
1037
  let el;
1212
1038
  const claimed = c.claim && c.next?.nodeType === 1 && c.next.localName === tag;
@@ -1220,23 +1046,23 @@ function createApp(doc, options) {
1220
1046
  c.parent.insertBefore(el, c.next);
1221
1047
  }
1222
1048
  styling(el, node, scope, block);
1223
- if (!claimed || ref?.wraps) {
1049
+ if (!claimed || node.children.length) {
1224
1050
  const inner = { parent: el, next: claimed ? el.firstChild : null, claim: claimed };
1225
1051
  for (const child of node.children)
1226
1052
  render(child, scope, inner, block, null);
1227
1053
  }
1228
1054
  if (!ref)
1229
- console.error(`Hozu: widget ${node.widget} has no client code (bundleWidgets)`);
1230
- if (!ref || !options.loadWidget || !options.mountWidget)
1055
+ console.error(`Hozu: component ${name} has no client code (bundleComponents)`);
1056
+ if (!ref || !options.loadComponent || !options.mountComponent)
1231
1057
  return;
1232
- options.mountWidget({
1058
+ options.mountComponent({
1233
1059
  el,
1234
1060
  ref,
1235
- name: node.widget,
1061
+ name,
1236
1062
  doc,
1237
1063
  props: () => value(node.props, scope),
1238
- emit: (name, detail) => {
1239
- const send = node.on[name];
1064
+ emit: (name2, detail) => {
1065
+ const send = node.on[name2];
1240
1066
  if (send)
1241
1067
  dispatch({
1242
1068
  type: "event",
@@ -1244,7 +1070,7 @@ function createApp(doc, options) {
1244
1070
  payload: value(send.payload, scope, (f) => f === "detail" ? detail : null)
1245
1071
  });
1246
1072
  },
1247
- load: options.loadWidget,
1073
+ load: options.loadComponent,
1248
1074
  watch: (update) => block.push(update),
1249
1075
  own: (stop) => mounted.add({ el, stop }),
1250
1076
  same: equal
@@ -1402,7 +1228,7 @@ function createApp(doc, options) {
1402
1228
  const block = [];
1403
1229
  const [a, b] = span(c, () => render(node, scope, c, block, ns));
1404
1230
  if (a && b)
1405
- ranges2.push([a, b]);
1231
+ ranges.push([a, b]);
1406
1232
  const connected = a?.isConnected === true;
1407
1233
  const update = () => {
1408
1234
  if (connected && !a.isConnected)
@@ -1433,10 +1259,10 @@ function createApp(doc, options) {
1433
1259
  m.stop();
1434
1260
  mounted.clear();
1435
1261
  root.length = 0;
1436
- for (const [a, b] of ranges2)
1262
+ for (const [a, b] of ranges)
1437
1263
  for (const n of range(a, b))
1438
1264
  n.remove();
1439
- ranges2.length = 0;
1265
+ ranges.length = 0;
1440
1266
  }
1441
1267
  };
1442
1268
  }
@@ -1474,7 +1300,7 @@ var fetchQuery = async (query, input) => {
1474
1300
  });
1475
1301
  return await response.json();
1476
1302
  };
1477
- var importWidget = async (url) => (await import(__rewriteRelativeImportExtension(
1303
+ var importComponent = async (url) => (await import(__rewriteRelativeImportExtension(
1478
1304
  /* @vite-ignore */
1479
1305
  url
1480
1306
  ))).default;
@@ -1482,17 +1308,19 @@ var importFns = async (url) => (await import(__rewriteRelativeImportExtension(
1482
1308
  /* @vite-ignore */
1483
1309
  url
1484
1310
  ))).fns;
1485
- async function hydrate(doc, { transport = fetchTransport, query = fetchQuery, live, loadFns = importFns, loadWidget = importWidget } = {}) {
1311
+ async function hydrate(doc, { transport = fetchTransport, query = fetchQuery, live, loadFns = importFns, loadComponent = importComponent } = {}) {
1486
1312
  const apps = /* @__PURE__ */ new Map();
1487
1313
  const script = doc.getElementById("hozu-payload");
1488
1314
  if (!script?.textContent)
1489
1315
  return apps;
1490
- const shared = { data: /* @__PURE__ */ new Map(), versions: /* @__PURE__ */ new Map() };
1491
- const fns = {};
1492
- const widgets = {};
1493
- const routes = {};
1494
- const liveKeys = /* @__PURE__ */ new Map();
1495
- const loaded = {};
1316
+ const payload = JSON.parse(script.textContent);
1317
+ const shared = { data: new Map(payload.data), versions: /* @__PURE__ */ new Map() };
1318
+ const fns = payload.fns ? await loadFns(payload.fns) : {};
1319
+ const { components, routes } = payload;
1320
+ const motion = payload.motion ? await Promise.resolve().then(() => (init_motion(), motion_exports)) : void 0;
1321
+ const mountComponent2 = Object.keys(components).length ? (await Promise.resolve().then(() => (init_component(), component_exports))).mountComponent : void 0;
1322
+ if (payload.visible)
1323
+ void Promise.resolve().then(() => (init_visible(), visible_exports)).then((m) => m.watch(doc));
1496
1324
  const inflight = /* @__PURE__ */ new Map();
1497
1325
  const onQuery = (q, input) => {
1498
1326
  const key = q + JSON.stringify(input);
@@ -1503,106 +1331,78 @@ async function hydrate(doc, { transport = fetchTransport, query = fetchQuery, li
1503
1331
  }
1504
1332
  return pending;
1505
1333
  };
1334
+ const liveKeys = Object.entries(payload.live ?? {});
1335
+ const onTags = async (tags) => {
1336
+ if (busy)
1337
+ return void queued.push(...tags);
1338
+ const stale = liveKeys.filter(([, l]) => l.tags.some((t) => tags.includes(t)));
1339
+ for (const [key, l] of stale)
1340
+ shared.data.set(key, await query(l.query, l.input));
1341
+ if (stale.length)
1342
+ for (const app of apps.values())
1343
+ app.sync();
1344
+ };
1345
+ let busy = 0;
1346
+ let queued = [];
1506
1347
  const onInvoke = async (effect, input) => {
1507
- const { result, refreshed } = await transport(effect, input, [...shared.data.keys()]);
1348
+ busy++;
1349
+ const { result, refreshed, session } = await transport(effect, input, [...shared.data.keys()]).finally(() => busy--);
1350
+ if (session) {
1351
+ shared.data.clear();
1352
+ queued = [];
1353
+ }
1508
1354
  for (const [key, value] of refreshed) {
1509
1355
  shared.data.set(key, value);
1510
1356
  shared.versions.set(key, (shared.versions.get(key) ?? 0) + 1);
1511
1357
  }
1512
- if (refreshed.length)
1358
+ if (refreshed.length || session)
1513
1359
  for (const app of apps.values())
1514
1360
  app.sync();
1361
+ if (!busy && queued.length)
1362
+ onTags(queued.splice(0));
1515
1363
  return result;
1516
1364
  };
1517
1365
  const dev = true ? { restore: (await Promise.resolve().then(() => (init_dev(), dev_exports))).restore(doc), machines: /* @__PURE__ */ new Map() } : null;
1518
- const session = {
1519
- doc,
1520
- apps,
1521
- islands: islandsOf,
1522
- async mount(payload2, markers2) {
1523
- if (!loaded.motion && payload2.motion)
1524
- loaded.motion = await Promise.resolve().then(() => (init_motion(), motion_exports));
1525
- if (payload2.visible)
1526
- void Promise.resolve().then(() => (init_visible(), visible_exports)).then((m) => m.watch(doc));
1527
- if (!loaded.mountWidget && Object.keys(payload2.widgets).length)
1528
- loaded.mountWidget = (await Promise.resolve().then(() => (init_widget(), widget_exports))).mountWidget;
1529
- if (!loaded.fns && payload2.fns) {
1530
- Object.assign(fns, await loadFns(payload2.fns));
1531
- loaded.fns = true;
1532
- }
1533
- Object.assign(widgets, payload2.widgets);
1534
- Object.assign(routes, payload2.routes);
1535
- for (const [key, value] of payload2.data) {
1536
- if (shared.data.has(key))
1537
- shared.versions.set(key, (shared.versions.get(key) ?? 0) + 1);
1538
- shared.data.set(key, value);
1539
- }
1540
- const created = [];
1541
- for (const [id, machine] of Object.entries(payload2.features)) {
1542
- if (apps.has(id))
1543
- continue;
1544
- const snapshot = dev?.restore(id, machine) || payload2.snapshots?.[id];
1545
- if (true)
1546
- dev?.machines.set(id, machine);
1547
- const app = createApp(doc, {
1548
- machine: machine ? compileMachine({ id, machine }, fns, routes) : null,
1549
- payload: shared,
1550
- params: payload2.params,
1551
- search: payload2.search,
1552
- ...snapshot ? { snapshot } : {},
1553
- fns,
1554
- widgets,
1555
- routes,
1556
- get motion() {
1557
- return loaded.motion;
1558
- },
1559
- get mountWidget() {
1560
- return loaded.mountWidget;
1561
- },
1562
- loadWidget,
1563
- onQuery,
1564
- onInvoke,
1565
- onNavigate: (url) => doc.defaultView?.location.assign(url)
1566
- });
1567
- apps.set(id, app);
1568
- created.push(app);
1569
- }
1570
- islandsOf(payload2).forEach((island, i) => {
1571
- const at = markers2[i];
1572
- const node = payload2.nodes[island.node];
1573
- if (at?.parentNode && node)
1574
- apps.get(island.feature)?.attach(at.parentNode, at.nextSibling, node, island.scope, true);
1575
- });
1576
- for (const app of apps.values())
1577
- app.sync();
1578
- for (const app of created)
1579
- app.start();
1580
- for (const [key, l] of Object.entries(payload2.live ?? {}))
1581
- liveKeys.set(key, l);
1582
- if (liveKeys.size && !loaded.live) {
1583
- loaded.live = true;
1584
- (live ?? (await Promise.resolve().then(() => (init_live(), live_exports))).liveStream(doc))(async (tags) => {
1585
- const stale = [...liveKeys].filter(([, l]) => l.tags.some((t) => tags.includes(t)));
1586
- for (const [key, l] of stale)
1587
- shared.data.set(key, await query(l.query, l.input));
1588
- if (stale.length)
1589
- for (const app of apps.values())
1590
- app.sync();
1591
- });
1592
- }
1593
- }
1594
- };
1595
- const payload = JSON.parse(script.textContent);
1366
+ for (const [id, machine] of Object.entries(payload.features)) {
1367
+ const snapshot = dev?.restore(id, machine) || payload.snapshots?.[id];
1368
+ if (true)
1369
+ dev?.machines.set(id, machine);
1370
+ apps.set(id, createApp(doc, {
1371
+ machine: machine ? compileMachine({ id, machine }, fns, routes) : null,
1372
+ payload: shared,
1373
+ params: payload.params,
1374
+ search: payload.search,
1375
+ ...snapshot ? { snapshot } : {},
1376
+ fns,
1377
+ components,
1378
+ routes,
1379
+ motion,
1380
+ mountComponent: mountComponent2,
1381
+ loadComponent,
1382
+ onQuery,
1383
+ onInvoke,
1384
+ onNavigate: (url) => doc.defaultView?.location.assign(url)
1385
+ }));
1386
+ }
1596
1387
  const markers = [];
1597
1388
  const walker = doc.createTreeWalker(doc.body ?? doc, 128);
1598
1389
  while (walker.nextNode())
1599
1390
  if (walker.currentNode.data === "i")
1600
1391
  markers.push(walker.currentNode);
1601
- await session.mount(payload, markers);
1392
+ islandsOf(payload).forEach((island, i) => {
1393
+ const at = markers[i];
1394
+ const node = payload.nodes[island.node];
1395
+ if (at?.parentNode && node)
1396
+ apps.get(island.feature)?.attach(at.parentNode, at.nextSibling, node, island.scope, true);
1397
+ });
1398
+ for (const app of apps.values())
1399
+ app.sync();
1400
+ for (const app of apps.values())
1401
+ app.start();
1402
+ if (liveKeys.length)
1403
+ (live ?? (await Promise.resolve().then(() => (init_live(), live_exports))).liveStream(doc))(onTags, liveKeys.flatMap(([, l]) => l.tags));
1602
1404
  if (dev)
1603
1405
  (await Promise.resolve().then(() => (init_dev(), dev_exports))).expose(doc, apps, dev.machines);
1604
- if (payload.soft)
1605
- void Promise.resolve().then(() => (init_navigate(), navigate_exports)).then((m) => m.soft(session));
1606
1406
  doc.documentElement.setAttribute("data-hozu-ready", "");
1607
1407
  return apps;
1608
1408
  }