@takazudo/zfb 4.2.1 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/runtime.js +27 -7
- package/dist/runtime.js.map +1 -1
- package/dist/slugify.d.ts +1 -68
- package/dist/slugify.js +1 -196
- package/dist/slugify.js.map +1 -1
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -74,8 +74,8 @@ surface. Today it covers:
|
|
|
74
74
|
- `clientScript(name)` — SSR helper that returns the stable URL for a
|
|
75
75
|
named client-script asset.
|
|
76
76
|
- `slugify(input)` and `SlugAllocator` — exported from the root barrel and
|
|
77
|
-
the `zfb/slugify` subpath for heading-id parity with the Rust content
|
|
78
|
-
pipeline.
|
|
77
|
+
the `zfb/slugify` compatibility subpath for heading-id parity with the Rust content
|
|
78
|
+
pipeline. For a lightweight direct import, use `@takazudo/zfb-slugify`.
|
|
79
79
|
|
|
80
80
|
The package uses the owned `@takazudo/zfb/zudo-react` runtime for island
|
|
81
81
|
rendering and hydration. Consumers do not install a separate JSX runtime.
|
package/dist/runtime.js
CHANGED
|
@@ -210,6 +210,9 @@ const ISLAND_REMOUNT_ATTR = "data-zfb-island-remount";
|
|
|
210
210
|
* - bundle re-import: dispose the old symbol handle before render mode replaces it.
|
|
211
211
|
*/
|
|
212
212
|
export const ISLAND_MOUNTED_ATTR = "data-zfb-island-mounted";
|
|
213
|
+
// Retained SSR DOM may already have incoming props while its first mount is
|
|
214
|
+
// deferred. Keep the render operation across swaps, retries, and dev re-imports.
|
|
215
|
+
const ISLAND_PENDING_RENDER_ATTR = "data-zfb-island-pending-render";
|
|
213
216
|
const COMPOSITION_KEY = Symbol.for("@takazudo/zfb/zudo-react/composition-v1");
|
|
214
217
|
const TRACKER_KEY = Symbol.for("@takazudo/zfb/zudo-react/composition-tracker-v1");
|
|
215
218
|
// A fresh bundle instance replaces roots installed by a previous instance.
|
|
@@ -237,6 +240,9 @@ function reportIslandError(element, phase, error) {
|
|
|
237
240
|
}
|
|
238
241
|
}
|
|
239
242
|
function disposeIsland(element, phase) {
|
|
243
|
+
pendingCancels.get(element)?.();
|
|
244
|
+
pendingCancels.delete(element);
|
|
245
|
+
element.removeAttribute(ISLAND_PENDING_RENDER_ATTR);
|
|
240
246
|
const handle = rootHandle(element);
|
|
241
247
|
try {
|
|
242
248
|
handle?.dispose();
|
|
@@ -320,8 +326,10 @@ export function mountIslands(manifest) {
|
|
|
320
326
|
const name = el.getAttribute("data-zfb-island");
|
|
321
327
|
if (!name)
|
|
322
328
|
continue;
|
|
329
|
+
const hadLiveRoot = !!rootHandle(el);
|
|
330
|
+
const flagged = !hadLiveRoot && !replacing && clearMountedForRemount(el);
|
|
323
331
|
warnIfNestedIsland(el, name);
|
|
324
|
-
scheduleMount(manifest, el, name, replacing ? "render" : "hydrate", { force: replacing });
|
|
332
|
+
scheduleMount(manifest, el, name, replacing || flagged || el.hasAttribute(ISLAND_PENDING_RENDER_ATTR) ? "render" : "hydrate", { force: hadLiveRoot || replacing });
|
|
325
333
|
}
|
|
326
334
|
const skipSsrIslands = document.querySelectorAll("[data-zfb-island-skip-ssr]");
|
|
327
335
|
for (const el of Array.from(skipSsrIslands)) {
|
|
@@ -329,8 +337,11 @@ export function mountIslands(manifest) {
|
|
|
329
337
|
const name = el.getAttribute("data-zfb-island-skip-ssr");
|
|
330
338
|
if (!name)
|
|
331
339
|
continue;
|
|
340
|
+
const hadLiveRoot = !!rootHandle(el);
|
|
341
|
+
if (!hadLiveRoot && !replacing)
|
|
342
|
+
clearMountedForRemount(el);
|
|
332
343
|
warnIfNestedIsland(el, name);
|
|
333
|
-
scheduleMount(manifest, el, name, "render", { force: replacing });
|
|
344
|
+
scheduleMount(manifest, el, name, "render", { force: hadLiveRoot || replacing });
|
|
334
345
|
}
|
|
335
346
|
}
|
|
336
347
|
/**
|
|
@@ -357,9 +368,10 @@ export function mountNewIslands() {
|
|
|
357
368
|
if (!name)
|
|
358
369
|
continue;
|
|
359
370
|
// The router marks changed persisted identity/props on the surviving node.
|
|
360
|
-
const
|
|
371
|
+
const hadLiveRoot = !!rootHandle(el);
|
|
372
|
+
const flagged = clearMountedForRemount(el);
|
|
361
373
|
warnIfNestedIsland(el, name);
|
|
362
|
-
scheduleMount(manifest, el, name,
|
|
374
|
+
scheduleMount(manifest, el, name, replacing || flagged || el.hasAttribute(ISLAND_PENDING_RENDER_ATTR) ? "render" : "hydrate", { force: hadLiveRoot || replacing });
|
|
363
375
|
}
|
|
364
376
|
const skipSsrIslands = document.querySelectorAll("[data-zfb-island-skip-ssr]");
|
|
365
377
|
for (const el of Array.from(skipSsrIslands)) {
|
|
@@ -368,8 +380,9 @@ export function mountNewIslands() {
|
|
|
368
380
|
if (!name)
|
|
369
381
|
continue;
|
|
370
382
|
warnIfNestedIsland(el, name);
|
|
371
|
-
const
|
|
372
|
-
|
|
383
|
+
const hadLiveRoot = !!rootHandle(el);
|
|
384
|
+
clearMountedForRemount(el);
|
|
385
|
+
scheduleMount(manifest, el, name, "render", { force: hadLiveRoot || replacing });
|
|
373
386
|
}
|
|
374
387
|
}
|
|
375
388
|
/** Consume a persisted root's remount flag and dispose its previous resources. */
|
|
@@ -379,14 +392,17 @@ function clearMountedForRemount(el) {
|
|
|
379
392
|
el.removeAttribute(ISLAND_REMOUNT_ATTR);
|
|
380
393
|
if (rootHandle(el))
|
|
381
394
|
disposeIsland(el, "remount disposal");
|
|
382
|
-
else
|
|
395
|
+
else {
|
|
383
396
|
el.removeAttribute(ISLAND_MOUNTED_ATTR);
|
|
397
|
+
el.setAttribute(ISLAND_PENDING_RENDER_ATTR, "");
|
|
398
|
+
}
|
|
384
399
|
return true;
|
|
385
400
|
}
|
|
386
401
|
function prepareMount(el) {
|
|
387
402
|
let replacing = false;
|
|
388
403
|
if (rootHandle(el) && !owned.has(el)) {
|
|
389
404
|
disposeIsland(el, "dev replacement disposal");
|
|
405
|
+
el.removeAttribute(ISLAND_REMOUNT_ATTR);
|
|
390
406
|
replacing = true;
|
|
391
407
|
}
|
|
392
408
|
if (!rootHandle(el))
|
|
@@ -444,6 +460,9 @@ function warnIfNestedIsland(el, componentName) {
|
|
|
444
460
|
function scheduleMount(manifest, element, componentName, mode, options = {}) {
|
|
445
461
|
if (rootHandle(element))
|
|
446
462
|
return;
|
|
463
|
+
// A later scan may change hydrate into render after a retained props update.
|
|
464
|
+
pendingCancels.get(element)?.();
|
|
465
|
+
pendingCancels.delete(element);
|
|
447
466
|
const entry = manifest[componentName];
|
|
448
467
|
if (entry == null) {
|
|
449
468
|
if (typeof process !== "undefined" && process.env && process.env["NODE_ENV"] !== "production") {
|
|
@@ -499,6 +518,7 @@ function fireInlineMount(element, mod, mode, options = {}) {
|
|
|
499
518
|
setRootHandle(element, handle);
|
|
500
519
|
owned.add(element);
|
|
501
520
|
element.setAttribute(ISLAND_MOUNTED_ATTR, "");
|
|
521
|
+
element.removeAttribute(ISLAND_PENDING_RENDER_ATTR);
|
|
502
522
|
}
|
|
503
523
|
catch (error) {
|
|
504
524
|
element.removeAttribute(ISLAND_MOUNTED_ATTR);
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["// Hydration scheduling helper consumed by the hydration runtime (Sub 3).\n//\n// Sub 3 owns the hydration runtime that walks the DOM, finds elements\n// marked with `data-zfb-island`, and dispatches each one through this\n// helper to decide *when* to fire the actual hydrate() call. The helper\n// itself does not know how to hydrate — it only schedules the supplied\n// `fire` callback.\n//\n// The branching matches the `When` union exactly:\n// \"visible\" → IntersectionObserver, threshold 0.0, hydrate on first\n// intersection, then disconnect.\n// \"idle\" → requestIdleCallback if available, otherwise setTimeout(0).\n// \"media\" → matchMedia(target's data-media), hydrate when the query\n// first matches (now or on a later change event), then\n// remove the listener.\n// \"load\" → immediate, synchronous fire.\n//\n// Anything else is treated as \"load\" (with a console.warn in development).\n// The helper is environment-tolerant: callers can run it in jsdom /\n// happy-dom or bare Node, and the absence of `IntersectionObserver` /\n// `requestIdleCallback` / `matchMedia` is handled gracefully.\n\nimport { resolveWhen, type When } from \"./types.js\";\nimport { ROOT_KEY, type RootHandle } from \"./zudo-react/root.js\";\nimport { parseProps as parseOwnedProps } from \"./zudo-react/props-transport.js\";\n\n/**\n * Subset of the global object that this module touches. Cast once at the\n * module top so individual scheduler functions don't repeat the inline\n * widening.\n */\ntype SchedulerGlobal = typeof globalThis & {\n requestIdleCallback?: (\n cb: (deadline: { didTimeout: boolean; timeRemaining: () => number }) => void,\n options?: { timeout?: number },\n ) => number;\n cancelIdleCallback?: (handle: number) => void;\n IntersectionObserver?: typeof IntersectionObserver;\n matchMedia?: typeof matchMedia;\n};\n\nconst g = globalThis as SchedulerGlobal;\n\n/**\n * Internal variant of `scheduleHydrate` that also reports whether the fire\n * callback was invoked synchronously. Unexported — call sites in this module\n * use this to decide whether to register a `pendingCancels` entry.\n */\nfunction scheduleHydrateInternal(\n target: Element,\n when: When | string | undefined,\n fire: () => void,\n): { fired: boolean; cancel: () => void } {\n const resolved = resolveWhen(when);\n\n if (resolved === \"load\") {\n fire();\n return { fired: true, cancel: noop };\n }\n\n if (resolved === \"idle\") {\n return { fired: false, cancel: scheduleIdle(fire) };\n }\n\n if (resolved === \"media\") {\n return scheduleMedia(target, fire);\n }\n\n // \"visible\"\n return scheduleVisible(target, fire);\n}\n\n/**\n * Schedule a hydration `fire` callback for `target` according to `when`.\n *\n * Returns a `cancel` function that aborts the scheduling if it has not\n * fired yet. After firing, calling `cancel` is a no-op. If the helper\n * cannot find the relevant browser API (e.g. running in pure Node with no\n * polyfill), it falls back to firing synchronously so server-side smoke\n * tests still observe the call.\n */\nexport function scheduleHydrate(\n target: Element,\n when: When | string | undefined,\n fire: () => void,\n): () => void {\n return scheduleHydrateInternal(target, when, fire).cancel;\n}\n\nfunction noop(): void {\n // intentionally empty\n}\n\n/**\n * Build a one-shot gate around `fn`. The returned `run` invokes `fn`\n * exactly once provided `cancel` has not been called first; `cancel`\n * marks the gate as cancelled (later `run` invocations become no-ops)\n * and reports whether the gate had already fired.\n */\nfunction oneShot(fn: () => void): {\n run: () => void;\n cancel: () => boolean;\n} {\n let fired = false;\n let cancelled = false;\n return {\n run(): void {\n if (cancelled || fired) return;\n fired = true;\n fn();\n },\n cancel(): boolean {\n if (fired) return true;\n cancelled = true;\n return false;\n },\n };\n}\n\nfunction scheduleIdle(fire: () => void): () => void {\n const gate = oneShot(fire);\n\n if (typeof g.requestIdleCallback === \"function\") {\n const handle = g.requestIdleCallback(gate.run);\n return () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n if (typeof g.cancelIdleCallback === \"function\") g.cancelIdleCallback(handle);\n };\n }\n\n const handle = setTimeout(gate.run, 0);\n return () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n clearTimeout(handle);\n };\n}\n\nfunction scheduleVisible(\n target: Element,\n fire: () => void,\n): { fired: boolean; cancel: () => void } {\n const Observer = g.IntersectionObserver;\n\n // No IntersectionObserver (e.g. very old browsers, bare Node) — fail\n // open and hydrate immediately so the island is at least functional.\n if (typeof Observer !== \"function\") {\n fire();\n return { fired: true, cancel: noop };\n }\n\n const gate = oneShot(fire);\n const observer = new Observer(\n (entries, obs) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n obs.disconnect();\n gate.run();\n return;\n }\n }\n },\n { threshold: 0 },\n );\n\n observer.observe(target);\n\n return {\n fired: false,\n cancel: () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n observer.disconnect();\n },\n };\n}\n\nfunction scheduleMedia(target: Element, fire: () => void): { fired: boolean; cancel: () => void } {\n const query = target.getAttribute(\"data-media\");\n\n // No matchMedia API (e.g. bare Node, very old browser) or missing/empty\n // query — fail open and hydrate immediately so the island is at least\n // functional.\n if (typeof g.matchMedia !== \"function\" || !query) {\n fire();\n return { fired: true, cancel: noop };\n }\n\n const mql = g.matchMedia(query);\n\n // Already matches — fire synchronously (no pending listener needed).\n if (mql.matches) {\n fire();\n return { fired: true, cancel: noop };\n }\n\n const gate = oneShot(fire);\n\n let removeListener = noop;\n\n // Listen for the first change event where the query matches.\n // We do NOT use `{once:true}` because we must ignore un-match events\n // (e.g. viewport widens back above breakpoint) and only fire on the\n // first match event — `{once:true}` would consume any change, including\n // un-match changes.\n const handler = (e: MediaQueryListEvent): void => {\n if (!e.matches) return; // ignore un-match changes\n removeListener();\n gate.run();\n };\n\n // Modern browsers expose the EventTarget API on MediaQueryList; older\n // Safari (<14) only has the deprecated addListener/removeListener pair\n // and throws on addEventListener. Prefer modern, fall back to legacy,\n // and fail open when neither exists (mirrors the missing-matchMedia case).\n if (typeof mql.addEventListener === \"function\") {\n mql.addEventListener(\"change\", handler);\n removeListener = () => mql.removeEventListener(\"change\", handler);\n } else if (typeof mql.addListener === \"function\") {\n mql.addListener(handler);\n removeListener = () => mql.removeListener(handler);\n } else {\n fire();\n return { fired: true, cancel: noop };\n }\n\n return {\n fired: false,\n cancel: () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n removeListener();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// mountIslands — DOM walk + dynamic-import dispatcher.\n//\n// `mountIslands` is the entry point the generated `islands-runtime-<hash>.js`\n// bundle calls at script load time. It walks the DOM for the two island\n// markers emitted by the server-side hydration step and the `<Island>`\n// JSX wrapper:\n//\n// 1. `[data-zfb-island]` — adopt server-rendered owned DOM.\n// 2. `[data-zfb-island-skip-ssr]` — mount over the fallback when scheduled.\n//\n// The generated shared bundle supplies strict identity and a RootHandle.\n// ---------------------------------------------------------------------------\n\n/**\n * The mount function supplied by a shared-bundle island module.\n *\n * `mode === \"hydrate\"` is used for SSR'd islands, `\"render\"` for\n * SSR-skip islands.\n */\ntype IslandMount = (\n props: Record<string, unknown>,\n element: Element,\n mode: \"hydrate\" | \"render\",\n) => RootHandle | null;\n\ninterface IslandModule {\n identity: { component: string; build: string };\n mount: IslandMount;\n}\n\n/**\n * Map of `componentName → island descriptor` baked into the runtime entry.\n *\n * Each manifest value is an inline module with `mount` or `default`.\n * The shared bundle imports island sources at build time.\n */\nexport type IslandManifestValue = IslandModule;\nexport type IslandManifest = Readonly<Record<string, IslandManifestValue>>;\n\n// data-zfb-transition-persist marker attribute — the client-router's persist\n// contract. Mirrored from client-router/swap-functions.ts: that package owns the\n// body swap (lifting persisted nodes into the incoming body), this package owns\n// island mount/unmount. Both must agree on the literal string. See the port\n// spec at packages/zfb-runtime/docs/client-router/port-spec.md §12.3.\nconst PERSIST_ATTR = \"data-zfb-transition-persist\";\n\n// Cross-package \"needs-remount\" flag set by client-router/swap-functions.ts on a\n// persisted island whose props changed across a body swap. Mirrored literal (same\n// cross-package contract as PERSIST_ATTR above — both packages must agree on the\n// string). Consumed by clearMountedForRemount(). See #1389.\nconst ISLAND_REMOUNT_ATTR = \"data-zfb-island-remount\";\n\n/**\n * Public observation marker; the symbol handle is the live-root guard.\n *\n * State table:\n * - initial/deferred/missing entry/failed mount: marker and handle absent;\n * - successful mount: both present;\n * - discarded root: disposal leaves DOM for the body swap, then clears both;\n * - unchanged island inside a retained persist boundary: handle and marker survive;\n * - changed island inside a retained boundary: metadata and remount flag are set,\n * then the post-swap scan disposes once and mounts in render mode;\n * - removed island inside a retained boundary: dispose and detach before swap;\n * - bundle re-import: dispose the old symbol handle before render mode replaces it.\n */\nexport const ISLAND_MOUNTED_ATTR = \"data-zfb-island-mounted\";\nconst COMPOSITION_KEY = Symbol.for(\"@takazudo/zfb/zudo-react/composition-v1\");\nconst TRACKER_KEY = Symbol.for(\"@takazudo/zfb/zudo-react/composition-tracker-v1\");\n\n// A fresh bundle instance replaces roots installed by a previous instance.\n// Ordinary repeat scans in the same instance keep their live handles.\nconst owned = new WeakSet<Element>();\nfunction rootHandle(element: Element): RootHandle | undefined {\n return (element as unknown as Record<symbol, RootHandle | undefined>)[ROOT_KEY];\n}\nfunction setRootHandle(element: Element, handle: RootHandle | undefined): void {\n const target = element as unknown as Record<symbol, RootHandle | undefined>;\n if (handle) target[ROOT_KEY] = handle;\n else delete target[ROOT_KEY];\n}\nfunction reportIslandError(element: Element, phase: string, error: unknown): void {\n const name =\n element.getAttribute(\"data-zfb-island\") ??\n element.getAttribute(\"data-zfb-island-skip-ssr\") ??\n \"unknown\";\n try {\n console.error(`[zfb] island \"${name}\" ${phase} failed`, error);\n } catch {\n // A broken reporter must not stop other roots.\n }\n}\nfunction disposeIsland(element: Element, phase: string): void {\n const handle = rootHandle(element);\n try {\n handle?.dispose();\n } catch (error) {\n reportIslandError(element, phase, error);\n } finally {\n if (rootHandle(element) === handle) setRootHandle(element, undefined);\n element.removeAttribute(ISLAND_MOUNTED_ATTR);\n }\n}\nfunction installCompositionTracker(doc: Document): void {\n const target = doc as unknown as Record<symbol, (() => void) | undefined>;\n if (target[TRACKER_KEY]) return;\n const set = (event: Event, value: \"active\" | \"idle\") => {\n if (event.target instanceof Element) {\n (event.target as unknown as Record<symbol, string>)[COMPOSITION_KEY] = value;\n }\n };\n const start = (event: Event) => set(event, \"active\");\n const end = (event: Event) => set(event, \"idle\");\n const input = (event: Event) => {\n if ((event as InputEvent).isComposing) set(event, \"active\");\n };\n doc.addEventListener(\"compositionstart\", start, true);\n doc.addEventListener(\"compositionend\", end, true);\n doc.addEventListener(\"input\", input, true);\n doc.addEventListener(\"blur\", end, true);\n target[TRACKER_KEY] = () => {\n doc.removeEventListener(\"compositionstart\", start, true);\n doc.removeEventListener(\"compositionend\", end, true);\n doc.removeEventListener(\"input\", input, true);\n doc.removeEventListener(\"blur\", end, true);\n delete target[TRACKER_KEY];\n };\n}\n\n// Elements for which the nested-island self-wrap warning has already been\n// emitted. Guards against repeated warn spam across re-walks (e.g. SPA swaps).\nconst warnedNested = new WeakSet<Element>();\n// Module-level captured manifest — set by the first `mountIslands` call and reused by\n// `mountNewIslands()` so the client-router does not need to know the manifest directly.\n// Named technical cause (W1B §12.1): the router lives in @takazudo/zfb-runtime; the\n// islands manifest lives in @takazudo/zfb. Passing the manifest through the swap event\n// would require widening the event API or threading manifest into router options. The\n// captured-manifest pattern keeps the package boundary clean.\nlet capturedManifest: IslandManifest | null = null;\n\n// Map of element → cancel-function for deferred-hydration islands\n// (data-when=\"idle\"|\"visible\"|\"media\").\n// Populated in scheduleMount; consulted on `zfb:before-swap` so deferred fires do not run\n// against orphan elements after a body swap. (W1B §12.5)\nconst pendingCancels = new Map<Element, () => void>();\n\n/**\n * Walk the DOM and mount every `[data-zfb-island]` / `[data-zfb-island-skip-ssr]`\n * element using `manifest`.\n *\n * No-op when `document` is undefined (SSR, edge runtime). Safe to call\n * multiple times: each element is mounted at most once thanks to the\n * symbol-handle guard.\n *\n * The manifest is captured at module level so `mountNewIslands()` can re-use\n * it after an SPA body swap without needing the caller to re-supply it.\n */\nexport function mountIslands(manifest: IslandManifest): void {\n if (typeof document === \"undefined\") return;\n\n // Capture the manifest for post-swap re-walks via mountNewIslands().\n capturedManifest = manifest;\n installCompositionTracker(document);\n\n const ssrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island]\");\n for (const el of Array.from(ssrIslands)) {\n const replacing = prepareMount(el);\n // Skip the empty-skeleton case left behind when the server-side\n // rewriter has not run yet (data-zfb-island=\"\" with no component\n // name). The hydration emit step is expected to fill this in\n // before the page reaches the browser; if it didn't, we cannot\n // dispatch.\n const name = el.getAttribute(\"data-zfb-island\");\n if (!name) continue;\n warnIfNestedIsland(el, name);\n scheduleMount(manifest, el, name, replacing ? \"render\" : \"hydrate\", { force: replacing });\n }\n\n const skipSsrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island-skip-ssr]\");\n for (const el of Array.from(skipSsrIslands)) {\n const replacing = prepareMount(el);\n const name = el.getAttribute(\"data-zfb-island-skip-ssr\");\n if (!name) continue;\n warnIfNestedIsland(el, name);\n scheduleMount(manifest, el, name, \"render\", { force: replacing });\n }\n}\n\n/**\n * Re-walk the current document body and mount any new island markers introduced\n * by an SPA body swap. Uses the manifest captured by the previous `mountIslands`\n * call — no manifest arg required.\n *\n * The caller (client-router `router.ts`) invokes this after `swap()` + `runScripts()`\n * and before dispatching `zfb:page-load`, per W1B §12.2 contract.\n *\n * No-op when called before `mountIslands` (capturedManifest is null) or when\n * `document` is undefined.\n */\nexport function mountNewIslands(): void {\n if (typeof document === \"undefined\") return;\n if (capturedManifest === null) return;\n\n const manifest = capturedManifest;\n\n const ssrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island]\");\n for (const el of Array.from(ssrIslands)) {\n const replacing = prepareMount(el);\n const name = el.getAttribute(\"data-zfb-island\");\n if (!name) continue;\n // The router marks changed persisted identity/props on the surviving node.\n const forceRemount = clearMountedForRemount(el) || replacing;\n warnIfNestedIsland(el, name);\n scheduleMount(manifest, el, name, forceRemount ? \"render\" : \"hydrate\", { force: forceRemount });\n }\n\n const skipSsrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island-skip-ssr]\");\n for (const el of Array.from(skipSsrIslands)) {\n const replacing = prepareMount(el);\n const name = el.getAttribute(\"data-zfb-island-skip-ssr\");\n if (!name) continue;\n warnIfNestedIsland(el, name);\n const forceRemount = clearMountedForRemount(el) || replacing;\n scheduleMount(manifest, el, name, \"render\", { force: forceRemount });\n }\n}\n\n/** Consume a persisted root's remount flag and dispose its previous resources. */\nfunction clearMountedForRemount(el: Element): boolean {\n if (!el.hasAttribute(ISLAND_REMOUNT_ATTR)) return false;\n el.removeAttribute(ISLAND_REMOUNT_ATTR);\n if (rootHandle(el)) disposeIsland(el, \"remount disposal\");\n else el.removeAttribute(ISLAND_MOUNTED_ATTR);\n return true;\n}\n\nfunction prepareMount(el: Element): boolean {\n let replacing = false;\n if (rootHandle(el) && !owned.has(el)) {\n disposeIsland(el, \"dev replacement disposal\");\n replacing = true;\n }\n if (!rootHandle(el)) el.removeAttribute(ISLAND_MOUNTED_ATTR);\n return replacing;\n}\n\n/**\n * Cancel deferred-hydration callbacks for all islands in the old body before a\n * swap. Prevents idle / visibility callbacks from running against orphan elements\n * after `swapBodyElement` removes them from the live document. (W1B §12.5)\n *\n * Call this on `zfb:before-swap` (or equivalently, in the router's swap sequence\n * before `swap()` mutates the DOM). Fire-and-forget; safe to call if nothing is\n * pending.\n */\nexport function cancelPendingIslands(): void {\n for (const [el, cancel] of pendingCancels) {\n cancel();\n pendingCancels.delete(el);\n }\n}\n\n/**\n * Warn (once per element, dev-only) when an island marker element is found\n * nested inside another island marker. Self-wrapping an island — emitting a\n * `data-zfb-island` or `data-zfb-island-skip-ssr` container *inside* another\n * island component's render output — mis-hydrates because the runtime will\n * try to mount both the outer and inner islands independently. The outer\n * island's framework instance owns the inner DOM, so a second `hydrate()` /\n * `render()` call against the inner element races with the outer render and\n * produces undefined behaviour.\n *\n * The fix is to author the inner component bare (no `<Island>` in its own\n * render output) and apply the `<Island when=\"...\">` wrapper at the call site.\n */\nfunction warnIfNestedIsland(el: Element, componentName: string): void {\n if (typeof process === \"undefined\" || !process.env || process.env[\"NODE_ENV\"] === \"production\") {\n return;\n }\n if (warnedNested.has(el)) return;\n const parent = el.parentElement;\n if (!parent || typeof parent.closest !== \"function\") return;\n const ancestor = parent.closest(\"[data-zfb-island],[data-zfb-island-skip-ssr]\");\n if (!ancestor) return;\n warnedNested.add(el);\n // eslint-disable-next-line no-console\n console.warn(\n `[zfb] Island \"${componentName}\" is nested inside another island marker. ` +\n `Self-wrapping an island mis-hydrates: the outer framework instance owns ` +\n `the inner DOM, causing a conflicting mount. ` +\n `Fix: author \"${componentName}\" bare (remove <Island> from its own render output) ` +\n `and apply <Island when=\"...\"> at the call site instead.`,\n );\n}\n\nfunction scheduleMount(\n manifest: IslandManifest,\n element: Element,\n componentName: string,\n mode: \"hydrate\" | \"render\",\n options: { force?: boolean } = {},\n): void {\n if (rootHandle(element)) return;\n\n const entry = manifest[componentName];\n if (entry == null) {\n if (typeof process !== \"undefined\" && process.env && process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\n `[zfb] no island manifest entry for component \"${componentName}\" — ` +\n `the runtime manifest is out of sync with the rendered HTML.`,\n );\n }\n return;\n }\n\n fireInlineMount(element, entry, mode, options);\n}\n\n/**\n * Run the mount step for the inline-module manifest shape used by the\n * shared-bundle path. The module is already in memory (it was imported\n * into the bundle at build time), so there is no async window to\n * coordinate around — we just call `mount` / `default` directly,\n * gated by `data-when` semantics.\n */\nfunction fireInlineMount(\n element: Element,\n mod: IslandModule,\n mode: \"hydrate\" | \"render\",\n options: { force?: boolean } = {},\n): void {\n const fn = mod.mount;\n if (typeof fn !== \"function\") {\n if (typeof process !== \"undefined\" && process.env && process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\"[zfb] owned island manifest entry did not export mount()\");\n }\n return;\n }\n\n const fire = (): void => {\n // Re-check the guard in case `fire` is invoked from a deferred\n // scheduler (rIC/rAF/visibility) after a sibling caller already\n // mounted this element.\n if (rootHandle(element)) return;\n // When the deferred fire actually runs, the cancel handle is no longer\n // needed — remove it so pendingCancels doesn't hold stale entries.\n pendingCancels.delete(element);\n // Stale-mount race guard for deferred inline mounts: skip if the element\n // was detached (e.g. body swap) while the idle/visible callback was queued.\n if (!element.isConnected) return;\n // Lazy props parse: read and parse data-props only at mount time.\n // For deferred strategies (media, visible, idle) this avoids JSON.parse\n // work at boot time for islands that may never hydrate.\n try {\n const props = readProps(element, mod.identity);\n const result = fn(props, element, mode);\n if (result === null) return;\n if (!result || typeof result.dispose !== \"function\" || typeof result.unmount !== \"function\") {\n throw new TypeError(\n `ZR_ROOT_HANDLE: island ${mod.identity.component} returned no root handle`,\n );\n }\n const handle: RootHandle = result;\n setRootHandle(element, handle);\n owned.add(element);\n element.setAttribute(ISLAND_MOUNTED_ATTR, \"\");\n } catch (error) {\n element.removeAttribute(ISLAND_MOUNTED_ATTR);\n reportIslandError(element, \"mount\", error);\n }\n };\n\n if (options.force) {\n fire();\n return;\n }\n\n const when = element.getAttribute(\"data-when\") ?? undefined;\n const { fired, cancel } = scheduleHydrateInternal(element, when, fire);\n // Track deferred-hydration cancel handle so cancelPendingIslands() can abort\n // idle / visibility callbacks before a body swap. (W1B §12.5)\n // Only register when the scheduler did NOT fire synchronously — a synchronous\n // fire means the island is already handling its mount and there is no\n // deferred callback to cancel. Registering noop after a sync fire would leave\n // a stale pendingCancels entry for an already-handled element. (#743)\n if (when && when !== \"load\" && !fired) {\n pendingCancels.set(element, cancel);\n }\n}\n\n/**\n * Snapshot persistence and island pairings before changing any old-body node.\n * The router lifts only matched persist nodes whose incoming targets are not\n * nested inside another matched target. Their descendants survive that lift.\n */\nexport function unmountIslands(\n root: ParentNode = document.body,\n incomingBody?: ParentNode | null,\n): void {\n const islandSelector = \"[data-zfb-island],[data-zfb-island-skip-ssr]\";\n const oldIslands = Array.from(root.querySelectorAll<HTMLElement>(islandSelector));\n if (!incomingBody) {\n for (const island of oldIslands) disposeIsland(island, \"disposal\");\n return;\n }\n\n const persistSelector = `[${PERSIST_ATTR}]`;\n const oldPersist = Array.from(root.querySelectorAll(persistSelector));\n const incomingPersist = Array.from(incomingBody.querySelectorAll(persistSelector));\n const incomingById = new Map<string, Element>();\n for (const element of incomingPersist) {\n const id = element.getAttribute(PERSIST_ATTR);\n if (id !== null && !incomingById.has(id)) incomingById.set(id, element);\n }\n\n const targets = new Map<Element, Element>();\n for (const element of oldPersist) {\n const id = element.getAttribute(PERSIST_ATTR);\n const target = id === null ? undefined : incomingById.get(id);\n if (target) targets.set(element, target);\n }\n const matchedTargets = new Set(targets.values());\n const lifted = new Set<Element>();\n for (const [element, target] of targets) {\n let ancestor = target.parentElement;\n while (ancestor && !matchedTargets.has(ancestor)) ancestor = ancestor.parentElement;\n if (!ancestor) lifted.add(element);\n }\n const retained = new Set<Element>(lifted);\n for (const element of oldPersist) {\n if (!targets.has(element)) continue;\n let ancestor = element.parentElement;\n while (ancestor && !lifted.has(ancestor)) ancestor = ancestor.parentElement;\n if (ancestor) retained.add(element);\n }\n\n const boundaryById = new Map<string, Element>();\n for (const element of oldPersist) {\n if (!retained.has(element)) continue;\n const id = element.getAttribute(PERSIST_ATTR);\n if (id !== null && !boundaryById.has(id)) boundaryById.set(id, element);\n }\n const oldByBoundary = new Map<Element, Element[]>();\n const incomingByBoundary = new Map<Element, Element[]>();\n const withoutBoundary: Element[] = [];\n for (const island of oldIslands) {\n let ancestor: Element | null = island;\n while (ancestor && !retained.has(ancestor)) ancestor = ancestor.parentElement;\n if (!ancestor) {\n withoutBoundary.push(island);\n continue;\n }\n const group = oldByBoundary.get(ancestor) ?? [];\n group.push(island);\n oldByBoundary.set(ancestor, group);\n }\n for (const island of incomingBody.querySelectorAll(islandSelector)) {\n let ancestor: Element | null = island;\n let boundary: Element | undefined;\n while (ancestor && !boundary) {\n const id = ancestor.getAttribute(PERSIST_ATTR);\n if (id !== null) boundary = boundaryById.get(id);\n ancestor = ancestor.parentElement;\n }\n if (!boundary) continue;\n const group = incomingByBoundary.get(boundary) ?? [];\n group.push(island);\n incomingByBoundary.set(boundary, group);\n }\n\n const pairs: Array<{ old: Element; incoming: Element }> = [];\n const removed: Element[] = [];\n for (const [boundary, oldGroup] of oldByBoundary) {\n const incomingGroup = incomingByBoundary.get(boundary) ?? [];\n const pairedOld = new Set<Element>();\n const pairedIncoming = new Set<Element>();\n // A persisted island root always maps to its own target, even when the\n // target changes component or ceases to be an island.\n if (oldGroup.includes(boundary)) {\n pairedOld.add(boundary);\n const target = targets.get(boundary);\n if (target && incomingGroup.includes(target)) {\n pairs.push({ old: boundary, incoming: target });\n pairedIncoming.add(target);\n } else {\n removed.push(boundary);\n }\n }\n for (const old of oldGroup) {\n if (pairedOld.has(old)) continue;\n const name = islandName(old);\n const incoming = incomingGroup.find(\n (candidate) => !pairedIncoming.has(candidate) && islandName(candidate) === name,\n );\n if (!incoming) continue;\n pairs.push({ old, incoming });\n pairedOld.add(old);\n pairedIncoming.add(incoming);\n }\n const remainingIncoming = incomingGroup.filter((island) => !pairedIncoming.has(island));\n let next = 0;\n for (const old of oldGroup) {\n if (pairedOld.has(old)) continue;\n const incoming = remainingIncoming[next++];\n if (incoming) pairs.push({ old, incoming });\n else removed.push(old);\n }\n }\n\n // Apply only after all boundaries and pairings have been computed. An old\n // descendant removed from a lifted wrapper must be detached as well as\n // disposed, or the post-swap walk could hydrate its mutated DOM.\n for (const island of withoutBoundary) disposeIsland(island, \"disposal\");\n for (const { old, incoming } of pairs) {\n const copyProps =\n !old.hasAttribute(\"data-zfb-transition-persist-props\") ||\n old.getAttribute(\"data-zfb-transition-persist-props\") === \"false\";\n const identityChanged = ISLAND_IDENTITY_ATTRS.some(\n (attribute) => old.getAttribute(attribute) !== incoming.getAttribute(attribute),\n );\n if (\n !old.hasAttribute(ISLAND_REMOUNT_ATTR) &&\n !identityChanged &&\n (!copyProps || old.getAttribute(\"data-props\") === incoming.getAttribute(\"data-props\"))\n )\n continue;\n for (const attribute of ISLAND_IDENTITY_ATTRS) copyAttribute(incoming, old, attribute);\n if (copyProps) copyAttribute(incoming, old, \"data-props\");\n old.setAttribute(ISLAND_REMOUNT_ATTR, \"\");\n }\n for (const island of removed) {\n disposeIsland(island, \"disposal\");\n island.remove();\n }\n}\n\nconst ISLAND_IDENTITY_ATTRS = [\n \"data-zfb-island\",\n \"data-zfb-island-skip-ssr\",\n \"data-zfb-transport\",\n \"data-zfb-protocol\",\n \"data-zfb-build\",\n] as const;\n\nfunction islandName(element: Element): string | null {\n return (\n element.getAttribute(\"data-zfb-island\") ?? element.getAttribute(\"data-zfb-island-skip-ssr\")\n );\n}\n\nfunction copyAttribute(from: Element, to: Element, attribute: string): void {\n const value = from.getAttribute(attribute);\n if (value === null) to.removeAttribute(attribute);\n else to.setAttribute(attribute, value);\n}\n\nfunction readProps(\n element: Element,\n identity: { component: string; build: string },\n): Record<string, unknown> {\n const raw = element.getAttribute(\"data-props\");\n const hasHydrate = element.hasAttribute(\"data-zfb-island\");\n const hasSkipSsr = element.hasAttribute(\"data-zfb-island-skip-ssr\");\n const component =\n element.getAttribute(\"data-zfb-island\") ?? element.getAttribute(\"data-zfb-island-skip-ssr\");\n if (\n hasHydrate === hasSkipSsr ||\n component !== identity.component ||\n element.getAttribute(\"data-zfb-transport\") !== \"json/1\" ||\n element.getAttribute(\"data-zfb-protocol\") !== \"zudo-react/1\" ||\n element.getAttribute(\"data-zfb-build\") !== identity.build ||\n raw === null\n ) {\n throw new TypeError(`ZR_IDENTITY: island ${identity.component} has invalid transport identity`);\n }\n if (element.querySelector(\"[data-zfb-island],[data-zfb-island-skip-ssr]\")) {\n throw new TypeError(`ZR_NESTED_ISLAND: island ${identity.component} contains another island`);\n }\n try {\n return parseOwnedProps(raw);\n } catch (error) {\n throw new TypeError(`ZR_PROPS: island ${identity.component}: ${String(error)}`);\n }\n}\n\n/**\n * Test-only seam. Returns whether the given element has an entry in the\n * module-private `pendingCancels` Map. Used to assert that a synchronous\n * scheduler fire does not leave a stale entry behind. (#743)\n */\nexport function __hasPendingCancelForTests(element: Element): boolean {\n return pendingCancels.has(element);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["// Hydration scheduling helper consumed by the hydration runtime (Sub 3).\n//\n// Sub 3 owns the hydration runtime that walks the DOM, finds elements\n// marked with `data-zfb-island`, and dispatches each one through this\n// helper to decide *when* to fire the actual hydrate() call. The helper\n// itself does not know how to hydrate — it only schedules the supplied\n// `fire` callback.\n//\n// The branching matches the `When` union exactly:\n// \"visible\" → IntersectionObserver, threshold 0.0, hydrate on first\n// intersection, then disconnect.\n// \"idle\" → requestIdleCallback if available, otherwise setTimeout(0).\n// \"media\" → matchMedia(target's data-media), hydrate when the query\n// first matches (now or on a later change event), then\n// remove the listener.\n// \"load\" → immediate, synchronous fire.\n//\n// Anything else is treated as \"load\" (with a console.warn in development).\n// The helper is environment-tolerant: callers can run it in jsdom /\n// happy-dom or bare Node, and the absence of `IntersectionObserver` /\n// `requestIdleCallback` / `matchMedia` is handled gracefully.\n\nimport { resolveWhen, type When } from \"./types.js\";\nimport { ROOT_KEY, type RootHandle } from \"./zudo-react/root.js\";\nimport { parseProps as parseOwnedProps } from \"./zudo-react/props-transport.js\";\n\n/**\n * Subset of the global object that this module touches. Cast once at the\n * module top so individual scheduler functions don't repeat the inline\n * widening.\n */\ntype SchedulerGlobal = typeof globalThis & {\n requestIdleCallback?: (\n cb: (deadline: { didTimeout: boolean; timeRemaining: () => number }) => void,\n options?: { timeout?: number },\n ) => number;\n cancelIdleCallback?: (handle: number) => void;\n IntersectionObserver?: typeof IntersectionObserver;\n matchMedia?: typeof matchMedia;\n};\n\nconst g = globalThis as SchedulerGlobal;\n\n/**\n * Internal variant of `scheduleHydrate` that also reports whether the fire\n * callback was invoked synchronously. Unexported — call sites in this module\n * use this to decide whether to register a `pendingCancels` entry.\n */\nfunction scheduleHydrateInternal(\n target: Element,\n when: When | string | undefined,\n fire: () => void,\n): { fired: boolean; cancel: () => void } {\n const resolved = resolveWhen(when);\n\n if (resolved === \"load\") {\n fire();\n return { fired: true, cancel: noop };\n }\n\n if (resolved === \"idle\") {\n return { fired: false, cancel: scheduleIdle(fire) };\n }\n\n if (resolved === \"media\") {\n return scheduleMedia(target, fire);\n }\n\n // \"visible\"\n return scheduleVisible(target, fire);\n}\n\n/**\n * Schedule a hydration `fire` callback for `target` according to `when`.\n *\n * Returns a `cancel` function that aborts the scheduling if it has not\n * fired yet. After firing, calling `cancel` is a no-op. If the helper\n * cannot find the relevant browser API (e.g. running in pure Node with no\n * polyfill), it falls back to firing synchronously so server-side smoke\n * tests still observe the call.\n */\nexport function scheduleHydrate(\n target: Element,\n when: When | string | undefined,\n fire: () => void,\n): () => void {\n return scheduleHydrateInternal(target, when, fire).cancel;\n}\n\nfunction noop(): void {\n // intentionally empty\n}\n\n/**\n * Build a one-shot gate around `fn`. The returned `run` invokes `fn`\n * exactly once provided `cancel` has not been called first; `cancel`\n * marks the gate as cancelled (later `run` invocations become no-ops)\n * and reports whether the gate had already fired.\n */\nfunction oneShot(fn: () => void): {\n run: () => void;\n cancel: () => boolean;\n} {\n let fired = false;\n let cancelled = false;\n return {\n run(): void {\n if (cancelled || fired) return;\n fired = true;\n fn();\n },\n cancel(): boolean {\n if (fired) return true;\n cancelled = true;\n return false;\n },\n };\n}\n\nfunction scheduleIdle(fire: () => void): () => void {\n const gate = oneShot(fire);\n\n if (typeof g.requestIdleCallback === \"function\") {\n const handle = g.requestIdleCallback(gate.run);\n return () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n if (typeof g.cancelIdleCallback === \"function\") g.cancelIdleCallback(handle);\n };\n }\n\n const handle = setTimeout(gate.run, 0);\n return () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n clearTimeout(handle);\n };\n}\n\nfunction scheduleVisible(\n target: Element,\n fire: () => void,\n): { fired: boolean; cancel: () => void } {\n const Observer = g.IntersectionObserver;\n\n // No IntersectionObserver (e.g. very old browsers, bare Node) — fail\n // open and hydrate immediately so the island is at least functional.\n if (typeof Observer !== \"function\") {\n fire();\n return { fired: true, cancel: noop };\n }\n\n const gate = oneShot(fire);\n const observer = new Observer(\n (entries, obs) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n obs.disconnect();\n gate.run();\n return;\n }\n }\n },\n { threshold: 0 },\n );\n\n observer.observe(target);\n\n return {\n fired: false,\n cancel: () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n observer.disconnect();\n },\n };\n}\n\nfunction scheduleMedia(target: Element, fire: () => void): { fired: boolean; cancel: () => void } {\n const query = target.getAttribute(\"data-media\");\n\n // No matchMedia API (e.g. bare Node, very old browser) or missing/empty\n // query — fail open and hydrate immediately so the island is at least\n // functional.\n if (typeof g.matchMedia !== \"function\" || !query) {\n fire();\n return { fired: true, cancel: noop };\n }\n\n const mql = g.matchMedia(query);\n\n // Already matches — fire synchronously (no pending listener needed).\n if (mql.matches) {\n fire();\n return { fired: true, cancel: noop };\n }\n\n const gate = oneShot(fire);\n\n let removeListener = noop;\n\n // Listen for the first change event where the query matches.\n // We do NOT use `{once:true}` because we must ignore un-match events\n // (e.g. viewport widens back above breakpoint) and only fire on the\n // first match event — `{once:true}` would consume any change, including\n // un-match changes.\n const handler = (e: MediaQueryListEvent): void => {\n if (!e.matches) return; // ignore un-match changes\n removeListener();\n gate.run();\n };\n\n // Modern browsers expose the EventTarget API on MediaQueryList; older\n // Safari (<14) only has the deprecated addListener/removeListener pair\n // and throws on addEventListener. Prefer modern, fall back to legacy,\n // and fail open when neither exists (mirrors the missing-matchMedia case).\n if (typeof mql.addEventListener === \"function\") {\n mql.addEventListener(\"change\", handler);\n removeListener = () => mql.removeEventListener(\"change\", handler);\n } else if (typeof mql.addListener === \"function\") {\n mql.addListener(handler);\n removeListener = () => mql.removeListener(handler);\n } else {\n fire();\n return { fired: true, cancel: noop };\n }\n\n return {\n fired: false,\n cancel: () => {\n const alreadyFired = gate.cancel();\n if (alreadyFired) return;\n removeListener();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// mountIslands — DOM walk + dynamic-import dispatcher.\n//\n// `mountIslands` is the entry point the generated `islands-runtime-<hash>.js`\n// bundle calls at script load time. It walks the DOM for the two island\n// markers emitted by the server-side hydration step and the `<Island>`\n// JSX wrapper:\n//\n// 1. `[data-zfb-island]` — adopt server-rendered owned DOM.\n// 2. `[data-zfb-island-skip-ssr]` — mount over the fallback when scheduled.\n//\n// The generated shared bundle supplies strict identity and a RootHandle.\n// ---------------------------------------------------------------------------\n\n/**\n * The mount function supplied by a shared-bundle island module.\n *\n * `mode === \"hydrate\"` is used for SSR'd islands, `\"render\"` for\n * SSR-skip islands.\n */\ntype IslandMount = (\n props: Record<string, unknown>,\n element: Element,\n mode: \"hydrate\" | \"render\",\n) => RootHandle | null;\n\ninterface IslandModule {\n identity: { component: string; build: string };\n mount: IslandMount;\n}\n\n/**\n * Map of `componentName → island descriptor` baked into the runtime entry.\n *\n * Each manifest value is an inline module with `mount` or `default`.\n * The shared bundle imports island sources at build time.\n */\nexport type IslandManifestValue = IslandModule;\nexport type IslandManifest = Readonly<Record<string, IslandManifestValue>>;\n\n// data-zfb-transition-persist marker attribute — the client-router's persist\n// contract. Mirrored from client-router/swap-functions.ts: that package owns the\n// body swap (lifting persisted nodes into the incoming body), this package owns\n// island mount/unmount. Both must agree on the literal string. See the port\n// spec at packages/zfb-runtime/docs/client-router/port-spec.md §12.3.\nconst PERSIST_ATTR = \"data-zfb-transition-persist\";\n\n// Cross-package \"needs-remount\" flag set by client-router/swap-functions.ts on a\n// persisted island whose props changed across a body swap. Mirrored literal (same\n// cross-package contract as PERSIST_ATTR above — both packages must agree on the\n// string). Consumed by clearMountedForRemount(). See #1389.\nconst ISLAND_REMOUNT_ATTR = \"data-zfb-island-remount\";\n\n/**\n * Public observation marker; the symbol handle is the live-root guard.\n *\n * State table:\n * - initial/deferred/missing entry/failed mount: marker and handle absent;\n * - successful mount: both present;\n * - discarded root: disposal leaves DOM for the body swap, then clears both;\n * - unchanged island inside a retained persist boundary: handle and marker survive;\n * - changed island inside a retained boundary: metadata and remount flag are set,\n * then the post-swap scan disposes once and mounts in render mode;\n * - removed island inside a retained boundary: dispose and detach before swap;\n * - bundle re-import: dispose the old symbol handle before render mode replaces it.\n */\nexport const ISLAND_MOUNTED_ATTR = \"data-zfb-island-mounted\";\n// Retained SSR DOM may already have incoming props while its first mount is\n// deferred. Keep the render operation across swaps, retries, and dev re-imports.\nconst ISLAND_PENDING_RENDER_ATTR = \"data-zfb-island-pending-render\";\nconst COMPOSITION_KEY = Symbol.for(\"@takazudo/zfb/zudo-react/composition-v1\");\nconst TRACKER_KEY = Symbol.for(\"@takazudo/zfb/zudo-react/composition-tracker-v1\");\n\n// A fresh bundle instance replaces roots installed by a previous instance.\n// Ordinary repeat scans in the same instance keep their live handles.\nconst owned = new WeakSet<Element>();\nfunction rootHandle(element: Element): RootHandle | undefined {\n return (element as unknown as Record<symbol, RootHandle | undefined>)[ROOT_KEY];\n}\nfunction setRootHandle(element: Element, handle: RootHandle | undefined): void {\n const target = element as unknown as Record<symbol, RootHandle | undefined>;\n if (handle) target[ROOT_KEY] = handle;\n else delete target[ROOT_KEY];\n}\nfunction reportIslandError(element: Element, phase: string, error: unknown): void {\n const name =\n element.getAttribute(\"data-zfb-island\") ??\n element.getAttribute(\"data-zfb-island-skip-ssr\") ??\n \"unknown\";\n try {\n console.error(`[zfb] island \"${name}\" ${phase} failed`, error);\n } catch {\n // A broken reporter must not stop other roots.\n }\n}\nfunction disposeIsland(element: Element, phase: string): void {\n pendingCancels.get(element)?.();\n pendingCancels.delete(element);\n element.removeAttribute(ISLAND_PENDING_RENDER_ATTR);\n const handle = rootHandle(element);\n try {\n handle?.dispose();\n } catch (error) {\n reportIslandError(element, phase, error);\n } finally {\n if (rootHandle(element) === handle) setRootHandle(element, undefined);\n element.removeAttribute(ISLAND_MOUNTED_ATTR);\n }\n}\nfunction installCompositionTracker(doc: Document): void {\n const target = doc as unknown as Record<symbol, (() => void) | undefined>;\n if (target[TRACKER_KEY]) return;\n const set = (event: Event, value: \"active\" | \"idle\") => {\n if (event.target instanceof Element) {\n (event.target as unknown as Record<symbol, string>)[COMPOSITION_KEY] = value;\n }\n };\n const start = (event: Event) => set(event, \"active\");\n const end = (event: Event) => set(event, \"idle\");\n const input = (event: Event) => {\n if ((event as InputEvent).isComposing) set(event, \"active\");\n };\n doc.addEventListener(\"compositionstart\", start, true);\n doc.addEventListener(\"compositionend\", end, true);\n doc.addEventListener(\"input\", input, true);\n doc.addEventListener(\"blur\", end, true);\n target[TRACKER_KEY] = () => {\n doc.removeEventListener(\"compositionstart\", start, true);\n doc.removeEventListener(\"compositionend\", end, true);\n doc.removeEventListener(\"input\", input, true);\n doc.removeEventListener(\"blur\", end, true);\n delete target[TRACKER_KEY];\n };\n}\n\n// Elements for which the nested-island self-wrap warning has already been\n// emitted. Guards against repeated warn spam across re-walks (e.g. SPA swaps).\nconst warnedNested = new WeakSet<Element>();\n// Module-level captured manifest — set by the first `mountIslands` call and reused by\n// `mountNewIslands()` so the client-router does not need to know the manifest directly.\n// Named technical cause (W1B §12.1): the router lives in @takazudo/zfb-runtime; the\n// islands manifest lives in @takazudo/zfb. Passing the manifest through the swap event\n// would require widening the event API or threading manifest into router options. The\n// captured-manifest pattern keeps the package boundary clean.\nlet capturedManifest: IslandManifest | null = null;\n\n// Map of element → cancel-function for deferred-hydration islands\n// (data-when=\"idle\"|\"visible\"|\"media\").\n// Populated in scheduleMount; consulted on `zfb:before-swap` so deferred fires do not run\n// against orphan elements after a body swap. (W1B §12.5)\nconst pendingCancels = new Map<Element, () => void>();\n\n/**\n * Walk the DOM and mount every `[data-zfb-island]` / `[data-zfb-island-skip-ssr]`\n * element using `manifest`.\n *\n * No-op when `document` is undefined (SSR, edge runtime). Safe to call\n * multiple times: each element is mounted at most once thanks to the\n * symbol-handle guard.\n *\n * The manifest is captured at module level so `mountNewIslands()` can re-use\n * it after an SPA body swap without needing the caller to re-supply it.\n */\nexport function mountIslands(manifest: IslandManifest): void {\n if (typeof document === \"undefined\") return;\n\n // Capture the manifest for post-swap re-walks via mountNewIslands().\n capturedManifest = manifest;\n installCompositionTracker(document);\n\n const ssrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island]\");\n for (const el of Array.from(ssrIslands)) {\n const replacing = prepareMount(el);\n // Skip the empty-skeleton case left behind when the server-side\n // rewriter has not run yet (data-zfb-island=\"\" with no component\n // name). The hydration emit step is expected to fill this in\n // before the page reaches the browser; if it didn't, we cannot\n // dispatch.\n const name = el.getAttribute(\"data-zfb-island\");\n if (!name) continue;\n const hadLiveRoot = !!rootHandle(el);\n const flagged = !hadLiveRoot && !replacing && clearMountedForRemount(el);\n warnIfNestedIsland(el, name);\n scheduleMount(\n manifest,\n el,\n name,\n replacing || flagged || el.hasAttribute(ISLAND_PENDING_RENDER_ATTR) ? \"render\" : \"hydrate\",\n { force: hadLiveRoot || replacing },\n );\n }\n\n const skipSsrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island-skip-ssr]\");\n for (const el of Array.from(skipSsrIslands)) {\n const replacing = prepareMount(el);\n const name = el.getAttribute(\"data-zfb-island-skip-ssr\");\n if (!name) continue;\n const hadLiveRoot = !!rootHandle(el);\n if (!hadLiveRoot && !replacing) clearMountedForRemount(el);\n warnIfNestedIsland(el, name);\n scheduleMount(manifest, el, name, \"render\", { force: hadLiveRoot || replacing });\n }\n}\n\n/**\n * Re-walk the current document body and mount any new island markers introduced\n * by an SPA body swap. Uses the manifest captured by the previous `mountIslands`\n * call — no manifest arg required.\n *\n * The caller (client-router `router.ts`) invokes this after `swap()` + `runScripts()`\n * and before dispatching `zfb:page-load`, per W1B §12.2 contract.\n *\n * No-op when called before `mountIslands` (capturedManifest is null) or when\n * `document` is undefined.\n */\nexport function mountNewIslands(): void {\n if (typeof document === \"undefined\") return;\n if (capturedManifest === null) return;\n\n const manifest = capturedManifest;\n\n const ssrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island]\");\n for (const el of Array.from(ssrIslands)) {\n const replacing = prepareMount(el);\n const name = el.getAttribute(\"data-zfb-island\");\n if (!name) continue;\n // The router marks changed persisted identity/props on the surviving node.\n const hadLiveRoot = !!rootHandle(el);\n const flagged = clearMountedForRemount(el);\n warnIfNestedIsland(el, name);\n scheduleMount(\n manifest,\n el,\n name,\n replacing || flagged || el.hasAttribute(ISLAND_PENDING_RENDER_ATTR) ? \"render\" : \"hydrate\",\n { force: hadLiveRoot || replacing },\n );\n }\n\n const skipSsrIslands = document.querySelectorAll<HTMLElement>(\"[data-zfb-island-skip-ssr]\");\n for (const el of Array.from(skipSsrIslands)) {\n const replacing = prepareMount(el);\n const name = el.getAttribute(\"data-zfb-island-skip-ssr\");\n if (!name) continue;\n warnIfNestedIsland(el, name);\n const hadLiveRoot = !!rootHandle(el);\n clearMountedForRemount(el);\n scheduleMount(manifest, el, name, \"render\", { force: hadLiveRoot || replacing });\n }\n}\n\n/** Consume a persisted root's remount flag and dispose its previous resources. */\nfunction clearMountedForRemount(el: Element): boolean {\n if (!el.hasAttribute(ISLAND_REMOUNT_ATTR)) return false;\n el.removeAttribute(ISLAND_REMOUNT_ATTR);\n if (rootHandle(el)) disposeIsland(el, \"remount disposal\");\n else {\n el.removeAttribute(ISLAND_MOUNTED_ATTR);\n el.setAttribute(ISLAND_PENDING_RENDER_ATTR, \"\");\n }\n return true;\n}\n\nfunction prepareMount(el: Element): boolean {\n let replacing = false;\n if (rootHandle(el) && !owned.has(el)) {\n disposeIsland(el, \"dev replacement disposal\");\n el.removeAttribute(ISLAND_REMOUNT_ATTR);\n replacing = true;\n }\n if (!rootHandle(el)) el.removeAttribute(ISLAND_MOUNTED_ATTR);\n return replacing;\n}\n\n/**\n * Cancel deferred-hydration callbacks for all islands in the old body before a\n * swap. Prevents idle / visibility callbacks from running against orphan elements\n * after `swapBodyElement` removes them from the live document. (W1B §12.5)\n *\n * Call this on `zfb:before-swap` (or equivalently, in the router's swap sequence\n * before `swap()` mutates the DOM). Fire-and-forget; safe to call if nothing is\n * pending.\n */\nexport function cancelPendingIslands(): void {\n for (const [el, cancel] of pendingCancels) {\n cancel();\n pendingCancels.delete(el);\n }\n}\n\n/**\n * Warn (once per element, dev-only) when an island marker element is found\n * nested inside another island marker. Self-wrapping an island — emitting a\n * `data-zfb-island` or `data-zfb-island-skip-ssr` container *inside* another\n * island component's render output — mis-hydrates because the runtime will\n * try to mount both the outer and inner islands independently. The outer\n * island's framework instance owns the inner DOM, so a second `hydrate()` /\n * `render()` call against the inner element races with the outer render and\n * produces undefined behaviour.\n *\n * The fix is to author the inner component bare (no `<Island>` in its own\n * render output) and apply the `<Island when=\"...\">` wrapper at the call site.\n */\nfunction warnIfNestedIsland(el: Element, componentName: string): void {\n if (typeof process === \"undefined\" || !process.env || process.env[\"NODE_ENV\"] === \"production\") {\n return;\n }\n if (warnedNested.has(el)) return;\n const parent = el.parentElement;\n if (!parent || typeof parent.closest !== \"function\") return;\n const ancestor = parent.closest(\"[data-zfb-island],[data-zfb-island-skip-ssr]\");\n if (!ancestor) return;\n warnedNested.add(el);\n // eslint-disable-next-line no-console\n console.warn(\n `[zfb] Island \"${componentName}\" is nested inside another island marker. ` +\n `Self-wrapping an island mis-hydrates: the outer framework instance owns ` +\n `the inner DOM, causing a conflicting mount. ` +\n `Fix: author \"${componentName}\" bare (remove <Island> from its own render output) ` +\n `and apply <Island when=\"...\"> at the call site instead.`,\n );\n}\n\nfunction scheduleMount(\n manifest: IslandManifest,\n element: Element,\n componentName: string,\n mode: \"hydrate\" | \"render\",\n options: { force?: boolean } = {},\n): void {\n if (rootHandle(element)) return;\n\n // A later scan may change hydrate into render after a retained props update.\n pendingCancels.get(element)?.();\n pendingCancels.delete(element);\n\n const entry = manifest[componentName];\n if (entry == null) {\n if (typeof process !== \"undefined\" && process.env && process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\n `[zfb] no island manifest entry for component \"${componentName}\" — ` +\n `the runtime manifest is out of sync with the rendered HTML.`,\n );\n }\n return;\n }\n\n fireInlineMount(element, entry, mode, options);\n}\n\n/**\n * Run the mount step for the inline-module manifest shape used by the\n * shared-bundle path. The module is already in memory (it was imported\n * into the bundle at build time), so there is no async window to\n * coordinate around — we just call `mount` / `default` directly,\n * gated by `data-when` semantics.\n */\nfunction fireInlineMount(\n element: Element,\n mod: IslandModule,\n mode: \"hydrate\" | \"render\",\n options: { force?: boolean } = {},\n): void {\n const fn = mod.mount;\n if (typeof fn !== \"function\") {\n if (typeof process !== \"undefined\" && process.env && process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\"[zfb] owned island manifest entry did not export mount()\");\n }\n return;\n }\n\n const fire = (): void => {\n // Re-check the guard in case `fire` is invoked from a deferred\n // scheduler (rIC/rAF/visibility) after a sibling caller already\n // mounted this element.\n if (rootHandle(element)) return;\n // When the deferred fire actually runs, the cancel handle is no longer\n // needed — remove it so pendingCancels doesn't hold stale entries.\n pendingCancels.delete(element);\n // Stale-mount race guard for deferred inline mounts: skip if the element\n // was detached (e.g. body swap) while the idle/visible callback was queued.\n if (!element.isConnected) return;\n // Lazy props parse: read and parse data-props only at mount time.\n // For deferred strategies (media, visible, idle) this avoids JSON.parse\n // work at boot time for islands that may never hydrate.\n try {\n const props = readProps(element, mod.identity);\n const result = fn(props, element, mode);\n if (result === null) return;\n if (!result || typeof result.dispose !== \"function\" || typeof result.unmount !== \"function\") {\n throw new TypeError(\n `ZR_ROOT_HANDLE: island ${mod.identity.component} returned no root handle`,\n );\n }\n const handle: RootHandle = result;\n setRootHandle(element, handle);\n owned.add(element);\n element.setAttribute(ISLAND_MOUNTED_ATTR, \"\");\n element.removeAttribute(ISLAND_PENDING_RENDER_ATTR);\n } catch (error) {\n element.removeAttribute(ISLAND_MOUNTED_ATTR);\n reportIslandError(element, \"mount\", error);\n }\n };\n\n if (options.force) {\n fire();\n return;\n }\n\n const when = element.getAttribute(\"data-when\") ?? undefined;\n const { fired, cancel } = scheduleHydrateInternal(element, when, fire);\n // Track deferred-hydration cancel handle so cancelPendingIslands() can abort\n // idle / visibility callbacks before a body swap. (W1B §12.5)\n // Only register when the scheduler did NOT fire synchronously — a synchronous\n // fire means the island is already handling its mount and there is no\n // deferred callback to cancel. Registering noop after a sync fire would leave\n // a stale pendingCancels entry for an already-handled element. (#743)\n if (when && when !== \"load\" && !fired) {\n pendingCancels.set(element, cancel);\n }\n}\n\n/**\n * Snapshot persistence and island pairings before changing any old-body node.\n * The router lifts only matched persist nodes whose incoming targets are not\n * nested inside another matched target. Their descendants survive that lift.\n */\nexport function unmountIslands(\n root: ParentNode = document.body,\n incomingBody?: ParentNode | null,\n): void {\n const islandSelector = \"[data-zfb-island],[data-zfb-island-skip-ssr]\";\n const oldIslands = Array.from(root.querySelectorAll<HTMLElement>(islandSelector));\n if (!incomingBody) {\n for (const island of oldIslands) disposeIsland(island, \"disposal\");\n return;\n }\n\n const persistSelector = `[${PERSIST_ATTR}]`;\n const oldPersist = Array.from(root.querySelectorAll(persistSelector));\n const incomingPersist = Array.from(incomingBody.querySelectorAll(persistSelector));\n const incomingById = new Map<string, Element>();\n for (const element of incomingPersist) {\n const id = element.getAttribute(PERSIST_ATTR);\n if (id !== null && !incomingById.has(id)) incomingById.set(id, element);\n }\n\n const targets = new Map<Element, Element>();\n for (const element of oldPersist) {\n const id = element.getAttribute(PERSIST_ATTR);\n const target = id === null ? undefined : incomingById.get(id);\n if (target) targets.set(element, target);\n }\n const matchedTargets = new Set(targets.values());\n const lifted = new Set<Element>();\n for (const [element, target] of targets) {\n let ancestor = target.parentElement;\n while (ancestor && !matchedTargets.has(ancestor)) ancestor = ancestor.parentElement;\n if (!ancestor) lifted.add(element);\n }\n const retained = new Set<Element>(lifted);\n for (const element of oldPersist) {\n if (!targets.has(element)) continue;\n let ancestor = element.parentElement;\n while (ancestor && !lifted.has(ancestor)) ancestor = ancestor.parentElement;\n if (ancestor) retained.add(element);\n }\n\n const boundaryById = new Map<string, Element>();\n for (const element of oldPersist) {\n if (!retained.has(element)) continue;\n const id = element.getAttribute(PERSIST_ATTR);\n if (id !== null && !boundaryById.has(id)) boundaryById.set(id, element);\n }\n const oldByBoundary = new Map<Element, Element[]>();\n const incomingByBoundary = new Map<Element, Element[]>();\n const withoutBoundary: Element[] = [];\n for (const island of oldIslands) {\n let ancestor: Element | null = island;\n while (ancestor && !retained.has(ancestor)) ancestor = ancestor.parentElement;\n if (!ancestor) {\n withoutBoundary.push(island);\n continue;\n }\n const group = oldByBoundary.get(ancestor) ?? [];\n group.push(island);\n oldByBoundary.set(ancestor, group);\n }\n for (const island of incomingBody.querySelectorAll(islandSelector)) {\n let ancestor: Element | null = island;\n let boundary: Element | undefined;\n while (ancestor && !boundary) {\n const id = ancestor.getAttribute(PERSIST_ATTR);\n if (id !== null) boundary = boundaryById.get(id);\n ancestor = ancestor.parentElement;\n }\n if (!boundary) continue;\n const group = incomingByBoundary.get(boundary) ?? [];\n group.push(island);\n incomingByBoundary.set(boundary, group);\n }\n\n const pairs: Array<{ old: Element; incoming: Element }> = [];\n const removed: Element[] = [];\n for (const [boundary, oldGroup] of oldByBoundary) {\n const incomingGroup = incomingByBoundary.get(boundary) ?? [];\n const pairedOld = new Set<Element>();\n const pairedIncoming = new Set<Element>();\n // A persisted island root always maps to its own target, even when the\n // target changes component or ceases to be an island.\n if (oldGroup.includes(boundary)) {\n pairedOld.add(boundary);\n const target = targets.get(boundary);\n if (target && incomingGroup.includes(target)) {\n pairs.push({ old: boundary, incoming: target });\n pairedIncoming.add(target);\n } else {\n removed.push(boundary);\n }\n }\n for (const old of oldGroup) {\n if (pairedOld.has(old)) continue;\n const name = islandName(old);\n const incoming = incomingGroup.find(\n (candidate) => !pairedIncoming.has(candidate) && islandName(candidate) === name,\n );\n if (!incoming) continue;\n pairs.push({ old, incoming });\n pairedOld.add(old);\n pairedIncoming.add(incoming);\n }\n const remainingIncoming = incomingGroup.filter((island) => !pairedIncoming.has(island));\n let next = 0;\n for (const old of oldGroup) {\n if (pairedOld.has(old)) continue;\n const incoming = remainingIncoming[next++];\n if (incoming) pairs.push({ old, incoming });\n else removed.push(old);\n }\n }\n\n // Apply only after all boundaries and pairings have been computed. An old\n // descendant removed from a lifted wrapper must be detached as well as\n // disposed, or the post-swap walk could hydrate its mutated DOM.\n for (const island of withoutBoundary) disposeIsland(island, \"disposal\");\n for (const { old, incoming } of pairs) {\n const copyProps =\n !old.hasAttribute(\"data-zfb-transition-persist-props\") ||\n old.getAttribute(\"data-zfb-transition-persist-props\") === \"false\";\n const identityChanged = ISLAND_IDENTITY_ATTRS.some(\n (attribute) => old.getAttribute(attribute) !== incoming.getAttribute(attribute),\n );\n if (\n !old.hasAttribute(ISLAND_REMOUNT_ATTR) &&\n !identityChanged &&\n (!copyProps || old.getAttribute(\"data-props\") === incoming.getAttribute(\"data-props\"))\n )\n continue;\n for (const attribute of ISLAND_IDENTITY_ATTRS) copyAttribute(incoming, old, attribute);\n if (copyProps) copyAttribute(incoming, old, \"data-props\");\n old.setAttribute(ISLAND_REMOUNT_ATTR, \"\");\n }\n for (const island of removed) {\n disposeIsland(island, \"disposal\");\n island.remove();\n }\n}\n\nconst ISLAND_IDENTITY_ATTRS = [\n \"data-zfb-island\",\n \"data-zfb-island-skip-ssr\",\n \"data-zfb-transport\",\n \"data-zfb-protocol\",\n \"data-zfb-build\",\n] as const;\n\nfunction islandName(element: Element): string | null {\n return (\n element.getAttribute(\"data-zfb-island\") ?? element.getAttribute(\"data-zfb-island-skip-ssr\")\n );\n}\n\nfunction copyAttribute(from: Element, to: Element, attribute: string): void {\n const value = from.getAttribute(attribute);\n if (value === null) to.removeAttribute(attribute);\n else to.setAttribute(attribute, value);\n}\n\nfunction readProps(\n element: Element,\n identity: { component: string; build: string },\n): Record<string, unknown> {\n const raw = element.getAttribute(\"data-props\");\n const hasHydrate = element.hasAttribute(\"data-zfb-island\");\n const hasSkipSsr = element.hasAttribute(\"data-zfb-island-skip-ssr\");\n const component =\n element.getAttribute(\"data-zfb-island\") ?? element.getAttribute(\"data-zfb-island-skip-ssr\");\n if (\n hasHydrate === hasSkipSsr ||\n component !== identity.component ||\n element.getAttribute(\"data-zfb-transport\") !== \"json/1\" ||\n element.getAttribute(\"data-zfb-protocol\") !== \"zudo-react/1\" ||\n element.getAttribute(\"data-zfb-build\") !== identity.build ||\n raw === null\n ) {\n throw new TypeError(`ZR_IDENTITY: island ${identity.component} has invalid transport identity`);\n }\n if (element.querySelector(\"[data-zfb-island],[data-zfb-island-skip-ssr]\")) {\n throw new TypeError(`ZR_NESTED_ISLAND: island ${identity.component} contains another island`);\n }\n try {\n return parseOwnedProps(raw);\n } catch (error) {\n throw new TypeError(`ZR_PROPS: island ${identity.component}: ${String(error)}`);\n }\n}\n\n/**\n * Test-only seam. Returns whether the given element has an entry in the\n * module-private `pendingCancels` Map. Used to assert that a synchronous\n * scheduler fire does not leave a stale entry behind. (#743)\n */\nexport function __hasPendingCancelForTests(element: Element): boolean {\n return pendingCancels.has(element);\n}\n"]}
|
package/dist/slugify.d.ts
CHANGED
|
@@ -1,68 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Heading-anchor slug utilities — a pure-TypeScript port of the Rust
|
|
3
|
-
* `slugify` / `next_slug` / `SlugAllocator` in
|
|
4
|
-
* `crates/zfb-content/src/plugins/heading_links.rs`.
|
|
5
|
-
*
|
|
6
|
-
* These functions produce the `id` attribute values that appear on
|
|
7
|
-
* `<h2>`–`<h6>` elements in the rendered HTML. They are intentionally
|
|
8
|
-
* **not** compatible with npm `github-slugger`: stripped punctuation
|
|
9
|
-
* collapses to a `-` separator (`"a,b"` → `"a-b"`), and leading/trailing
|
|
10
|
-
* whitespace / stripped chars emit nothing rather than a stray dash.
|
|
11
|
-
*
|
|
12
|
-
* ⚠️ These functions produce heading-anchor slugs (e.g. `"hello-world"`
|
|
13
|
-
* for `<h2 id="hello-world">`). They are **different** from
|
|
14
|
-
* `_relPathToSlug` in `content.ts`, which converts a file-system relative
|
|
15
|
-
* path into a URL slug for `CollectionEntry.slug`.
|
|
16
|
-
*
|
|
17
|
-
* Parity is enforced by the shared fixture at
|
|
18
|
-
* `crates/zfb-content/tests/fixtures/slugify-parity.json`, consumed by
|
|
19
|
-
* both a Rust integration test and the `slugify.test.ts` vitest suite.
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* Produce a heading-anchor slug from `input`, matching the Rust
|
|
23
|
-
* `slugify()` in `crates/zfb-content/src/plugins/heading_links.rs`.
|
|
24
|
-
*
|
|
25
|
-
* Algorithm (per Unicode code point):
|
|
26
|
-
* - whitespace or stripped punctuation → emit ONE `-` unless the last
|
|
27
|
-
* emitted character was already a `-` (leading separators are skipped
|
|
28
|
-
* because `lastDash` starts `true`).
|
|
29
|
-
* - anything else → push `ch.toLowerCase()` (per-code-point, not
|
|
30
|
-
* whole-string, to avoid Greek final-sigma context collapsing).
|
|
31
|
-
* - finally pop ONE trailing `-` if present.
|
|
32
|
-
*
|
|
33
|
-
* Divergence from npm github-slugger (intentional — parity target is
|
|
34
|
-
* the Rust function, not npm):
|
|
35
|
-
* - `"a,b"` → `"a-b"` (stripped punctuation emits a dash, not nothing)
|
|
36
|
-
* - `" --weird-- "` → `"--weird--"` (existing dashes/underscores pass
|
|
37
|
-
* through unchanged)
|
|
38
|
-
*/
|
|
39
|
-
export declare function slugify(input: string): string;
|
|
40
|
-
/**
|
|
41
|
-
* Strategy-aware slug allocator for a single document.
|
|
42
|
-
*
|
|
43
|
-
* Mirrors Rust `SlugAllocator` in heading_links.rs:70-118.
|
|
44
|
-
*
|
|
45
|
-
* @param strategy `"flat"` (default) — per-document dedup counter, mirrors
|
|
46
|
-
* github-slugger numbering. `"hierarchical"` — each slug is prefixed with
|
|
47
|
-
* its ancestor chain and deduped on the full candidate path.
|
|
48
|
-
*
|
|
49
|
-
* Depth contract: callers pass `depth` values 2–6 (h2–h6). h1 is never
|
|
50
|
-
* allocated — it is handled by the page layout, not the markdown body.
|
|
51
|
-
*/
|
|
52
|
-
export declare class SlugAllocator {
|
|
53
|
-
private readonly strategy;
|
|
54
|
-
private seen;
|
|
55
|
-
/** Hierarchical ancestor stack of `[depth, finalId]` pairs. Unused in flat mode. */
|
|
56
|
-
private stack;
|
|
57
|
-
constructor(strategy?: "flat" | "hierarchical");
|
|
58
|
-
/**
|
|
59
|
-
* Allocate the slug for a heading of `depth` (2–6) whose slugified text
|
|
60
|
-
* is `base`. Returns `""` for an empty `base` without mutating state.
|
|
61
|
-
*/
|
|
62
|
-
allocate(depth: number, base: string): string;
|
|
63
|
-
/**
|
|
64
|
-
* Clear all per-document state (dedup counters AND the hierarchical
|
|
65
|
-
* ancestor stack). Call between documents to avoid slug counter leakage.
|
|
66
|
-
*/
|
|
67
|
-
reset(): void;
|
|
68
|
-
}
|
|
1
|
+
export { slugify, SlugAllocator } from "@takazudo/zfb-slugify";
|
package/dist/slugify.js
CHANGED
|
@@ -1,197 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
* Heading-anchor slug utilities — a pure-TypeScript port of the Rust
|
|
3
|
-
* `slugify` / `next_slug` / `SlugAllocator` in
|
|
4
|
-
* `crates/zfb-content/src/plugins/heading_links.rs`.
|
|
5
|
-
*
|
|
6
|
-
* These functions produce the `id` attribute values that appear on
|
|
7
|
-
* `<h2>`–`<h6>` elements in the rendered HTML. They are intentionally
|
|
8
|
-
* **not** compatible with npm `github-slugger`: stripped punctuation
|
|
9
|
-
* collapses to a `-` separator (`"a,b"` → `"a-b"`), and leading/trailing
|
|
10
|
-
* whitespace / stripped chars emit nothing rather than a stray dash.
|
|
11
|
-
*
|
|
12
|
-
* ⚠️ These functions produce heading-anchor slugs (e.g. `"hello-world"`
|
|
13
|
-
* for `<h2 id="hello-world">`). They are **different** from
|
|
14
|
-
* `_relPathToSlug` in `content.ts`, which converts a file-system relative
|
|
15
|
-
* path into a URL slug for `CollectionEntry.slug`.
|
|
16
|
-
*
|
|
17
|
-
* Parity is enforced by the shared fixture at
|
|
18
|
-
* `crates/zfb-content/tests/fixtures/slugify-parity.json`, consumed by
|
|
19
|
-
* both a Rust integration test and the `slugify.test.ts` vitest suite.
|
|
20
|
-
*/
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// Helpers — mirrors the Rust `is_stripped` predicate
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
/** Unicode General Category Cc (control characters). */
|
|
25
|
-
function isCc(ch) {
|
|
26
|
-
// \p{Cc} = general category "Control" (C0 + C1 blocks: U+0000–U+001F, U+007F–U+009F)
|
|
27
|
-
return /^\p{Cc}$/u.test(ch);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Returns true when the character should be stripped (maps to a dash
|
|
31
|
-
* separator in the output). Mirrors Rust's `is_stripped`:
|
|
32
|
-
* - Unicode Cc control characters
|
|
33
|
-
* - Explicit ASCII punctuation set: `! " # $ % & ' ( ) * + , . / : ; < = > ? @ [ \ ] ^ ` { | } ~`
|
|
34
|
-
*/
|
|
35
|
-
function isStripped(ch) {
|
|
36
|
-
if (isCc(ch))
|
|
37
|
-
return true;
|
|
38
|
-
return (ch === "!" ||
|
|
39
|
-
ch === '"' ||
|
|
40
|
-
ch === "#" ||
|
|
41
|
-
ch === "$" ||
|
|
42
|
-
ch === "%" ||
|
|
43
|
-
ch === "&" ||
|
|
44
|
-
ch === "'" ||
|
|
45
|
-
ch === "(" ||
|
|
46
|
-
ch === ")" ||
|
|
47
|
-
ch === "*" ||
|
|
48
|
-
ch === "+" ||
|
|
49
|
-
ch === "," ||
|
|
50
|
-
ch === "." ||
|
|
51
|
-
ch === "/" ||
|
|
52
|
-
ch === ":" ||
|
|
53
|
-
ch === ";" ||
|
|
54
|
-
ch === "<" ||
|
|
55
|
-
ch === "=" ||
|
|
56
|
-
ch === ">" ||
|
|
57
|
-
ch === "?" ||
|
|
58
|
-
ch === "@" ||
|
|
59
|
-
ch === "[" ||
|
|
60
|
-
ch === "\\" ||
|
|
61
|
-
ch === "]" ||
|
|
62
|
-
ch === "^" ||
|
|
63
|
-
ch === "`" ||
|
|
64
|
-
ch === "{" ||
|
|
65
|
-
ch === "|" ||
|
|
66
|
-
ch === "}" ||
|
|
67
|
-
ch === "~");
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Returns true when `ch` is a Unicode whitespace character.
|
|
71
|
-
*
|
|
72
|
-
* Mirrors Rust `char::is_whitespace` which is `\p{White_Space}/u` — NOT
|
|
73
|
-
* the JS `\s` class (`\s` wrongly matches U+FEFF BYTE ORDER MARK and
|
|
74
|
-
* misses U+0085 NEXT LINE).
|
|
75
|
-
*/
|
|
76
|
-
function isWhitespace(ch) {
|
|
77
|
-
return /^\p{White_Space}$/u.test(ch);
|
|
78
|
-
}
|
|
79
|
-
// ---------------------------------------------------------------------------
|
|
80
|
-
// slugify
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
/**
|
|
83
|
-
* Produce a heading-anchor slug from `input`, matching the Rust
|
|
84
|
-
* `slugify()` in `crates/zfb-content/src/plugins/heading_links.rs`.
|
|
85
|
-
*
|
|
86
|
-
* Algorithm (per Unicode code point):
|
|
87
|
-
* - whitespace or stripped punctuation → emit ONE `-` unless the last
|
|
88
|
-
* emitted character was already a `-` (leading separators are skipped
|
|
89
|
-
* because `lastDash` starts `true`).
|
|
90
|
-
* - anything else → push `ch.toLowerCase()` (per-code-point, not
|
|
91
|
-
* whole-string, to avoid Greek final-sigma context collapsing).
|
|
92
|
-
* - finally pop ONE trailing `-` if present.
|
|
93
|
-
*
|
|
94
|
-
* Divergence from npm github-slugger (intentional — parity target is
|
|
95
|
-
* the Rust function, not npm):
|
|
96
|
-
* - `"a,b"` → `"a-b"` (stripped punctuation emits a dash, not nothing)
|
|
97
|
-
* - `" --weird-- "` → `"--weird--"` (existing dashes/underscores pass
|
|
98
|
-
* through unchanged)
|
|
99
|
-
*/
|
|
100
|
-
export function slugify(input) {
|
|
101
|
-
let out = "";
|
|
102
|
-
let lastDash = true;
|
|
103
|
-
for (const ch of input) {
|
|
104
|
-
if (isWhitespace(ch) || isStripped(ch)) {
|
|
105
|
-
if (!lastDash) {
|
|
106
|
-
out += "-";
|
|
107
|
-
lastDash = true;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
// Per-code-point lowercase — avoids Greek final-sigma context:
|
|
112
|
-
// "ΦΟΣ" → "φοσ" not "φος".
|
|
113
|
-
out += ch.toLowerCase();
|
|
114
|
-
lastDash = false;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (out.endsWith("-")) {
|
|
118
|
-
out = out.slice(0, -1);
|
|
119
|
-
}
|
|
120
|
-
return out;
|
|
121
|
-
}
|
|
122
|
-
// ---------------------------------------------------------------------------
|
|
123
|
-
// next_slug
|
|
124
|
-
// ---------------------------------------------------------------------------
|
|
125
|
-
/**
|
|
126
|
-
* Allocate the next deduplicated slug for `base` against a per-document
|
|
127
|
-
* counter map.
|
|
128
|
-
*
|
|
129
|
-
* Mirrors Rust `next_slug`: first occurrence returns `base`, second
|
|
130
|
-
* returns `base-1`, third `base-2`, … Empty `base` short-circuits to
|
|
131
|
-
* `""` without mutating the map.
|
|
132
|
-
*/
|
|
133
|
-
function nextSlug(seen, base) {
|
|
134
|
-
if (base === "")
|
|
135
|
-
return "";
|
|
136
|
-
const count = seen.get(base) ?? 0;
|
|
137
|
-
const slug = count === 0 ? base : `${base}-${count}`;
|
|
138
|
-
seen.set(base, count + 1);
|
|
139
|
-
return slug;
|
|
140
|
-
}
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
// SlugAllocator
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
/**
|
|
145
|
-
* Strategy-aware slug allocator for a single document.
|
|
146
|
-
*
|
|
147
|
-
* Mirrors Rust `SlugAllocator` in heading_links.rs:70-118.
|
|
148
|
-
*
|
|
149
|
-
* @param strategy `"flat"` (default) — per-document dedup counter, mirrors
|
|
150
|
-
* github-slugger numbering. `"hierarchical"` — each slug is prefixed with
|
|
151
|
-
* its ancestor chain and deduped on the full candidate path.
|
|
152
|
-
*
|
|
153
|
-
* Depth contract: callers pass `depth` values 2–6 (h2–h6). h1 is never
|
|
154
|
-
* allocated — it is handled by the page layout, not the markdown body.
|
|
155
|
-
*/
|
|
156
|
-
export class SlugAllocator {
|
|
157
|
-
strategy;
|
|
158
|
-
seen;
|
|
159
|
-
/** Hierarchical ancestor stack of `[depth, finalId]` pairs. Unused in flat mode. */
|
|
160
|
-
stack;
|
|
161
|
-
constructor(strategy = "flat") {
|
|
162
|
-
this.strategy = strategy;
|
|
163
|
-
this.seen = new Map();
|
|
164
|
-
this.stack = [];
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Allocate the slug for a heading of `depth` (2–6) whose slugified text
|
|
168
|
-
* is `base`. Returns `""` for an empty `base` without mutating state.
|
|
169
|
-
*/
|
|
170
|
-
allocate(depth, base) {
|
|
171
|
-
if (this.strategy === "flat") {
|
|
172
|
-
return nextSlug(this.seen, base);
|
|
173
|
-
}
|
|
174
|
-
// Hierarchical mode
|
|
175
|
-
if (base === "")
|
|
176
|
-
return "";
|
|
177
|
-
// Pop ancestors whose depth >= this heading's depth
|
|
178
|
-
let top = this.stack.at(-1);
|
|
179
|
-
while (top !== undefined && top[0] >= depth) {
|
|
180
|
-
this.stack.pop();
|
|
181
|
-
top = this.stack.at(-1);
|
|
182
|
-
}
|
|
183
|
-
const candidate = top !== undefined ? `${top[1]}-${base}` : base;
|
|
184
|
-
const slug = nextSlug(this.seen, candidate);
|
|
185
|
-
this.stack.push([depth, slug]);
|
|
186
|
-
return slug;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Clear all per-document state (dedup counters AND the hierarchical
|
|
190
|
-
* ancestor stack). Call between documents to avoid slug counter leakage.
|
|
191
|
-
*/
|
|
192
|
-
reset() {
|
|
193
|
-
this.seen.clear();
|
|
194
|
-
this.stack = [];
|
|
195
|
-
}
|
|
196
|
-
}
|
|
1
|
+
export { slugify, SlugAllocator } from "@takazudo/zfb-slugify";
|
|
197
2
|
//# sourceMappingURL=slugify.js.map
|
package/dist/slugify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slugify.js","sourceRoot":"","sources":["../src/slugify.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Heading-anchor slug utilities — a pure-TypeScript port of the Rust\n * `slugify` / `next_slug` / `SlugAllocator` in\n * `crates/zfb-content/src/plugins/heading_links.rs`.\n *\n * These functions produce the `id` attribute values that appear on\n * `<h2>`–`<h6>` elements in the rendered HTML. They are intentionally\n * **not** compatible with npm `github-slugger`: stripped punctuation\n * collapses to a `-` separator (`\"a,b\"` → `\"a-b\"`), and leading/trailing\n * whitespace / stripped chars emit nothing rather than a stray dash.\n *\n * ⚠️ These functions produce heading-anchor slugs (e.g. `\"hello-world\"`\n * for `<h2 id=\"hello-world\">`). They are **different** from\n * `_relPathToSlug` in `content.ts`, which converts a file-system relative\n * path into a URL slug for `CollectionEntry.slug`.\n *\n * Parity is enforced by the shared fixture at\n * `crates/zfb-content/tests/fixtures/slugify-parity.json`, consumed by\n * both a Rust integration test and the `slugify.test.ts` vitest suite.\n */\n\n// ---------------------------------------------------------------------------\n// Helpers — mirrors the Rust `is_stripped` predicate\n// ---------------------------------------------------------------------------\n\n/** Unicode General Category Cc (control characters). */\nfunction isCc(ch: string): boolean {\n // \\p{Cc} = general category \"Control\" (C0 + C1 blocks: U+0000–U+001F, U+007F–U+009F)\n return /^\\p{Cc}$/u.test(ch);\n}\n\n/**\n * Returns true when the character should be stripped (maps to a dash\n * separator in the output). Mirrors Rust's `is_stripped`:\n * - Unicode Cc control characters\n * - Explicit ASCII punctuation set: `! \" # $ % & ' ( ) * + , . / : ; < = > ? @ [ \\ ] ^ ` { | } ~`\n */\nfunction isStripped(ch: string): boolean {\n if (isCc(ch)) return true;\n return (\n ch === \"!\" ||\n ch === '\"' ||\n ch === \"#\" ||\n ch === \"$\" ||\n ch === \"%\" ||\n ch === \"&\" ||\n ch === \"'\" ||\n ch === \"(\" ||\n ch === \")\" ||\n ch === \"*\" ||\n ch === \"+\" ||\n ch === \",\" ||\n ch === \".\" ||\n ch === \"/\" ||\n ch === \":\" ||\n ch === \";\" ||\n ch === \"<\" ||\n ch === \"=\" ||\n ch === \">\" ||\n ch === \"?\" ||\n ch === \"@\" ||\n ch === \"[\" ||\n ch === \"\\\\\" ||\n ch === \"]\" ||\n ch === \"^\" ||\n ch === \"`\" ||\n ch === \"{\" ||\n ch === \"|\" ||\n ch === \"}\" ||\n ch === \"~\"\n );\n}\n\n/**\n * Returns true when `ch` is a Unicode whitespace character.\n *\n * Mirrors Rust `char::is_whitespace` which is `\\p{White_Space}/u` — NOT\n * the JS `\\s` class (`\\s` wrongly matches U+FEFF BYTE ORDER MARK and\n * misses U+0085 NEXT LINE).\n */\nfunction isWhitespace(ch: string): boolean {\n return /^\\p{White_Space}$/u.test(ch);\n}\n\n// ---------------------------------------------------------------------------\n// slugify\n// ---------------------------------------------------------------------------\n\n/**\n * Produce a heading-anchor slug from `input`, matching the Rust\n * `slugify()` in `crates/zfb-content/src/plugins/heading_links.rs`.\n *\n * Algorithm (per Unicode code point):\n * - whitespace or stripped punctuation → emit ONE `-` unless the last\n * emitted character was already a `-` (leading separators are skipped\n * because `lastDash` starts `true`).\n * - anything else → push `ch.toLowerCase()` (per-code-point, not\n * whole-string, to avoid Greek final-sigma context collapsing).\n * - finally pop ONE trailing `-` if present.\n *\n * Divergence from npm github-slugger (intentional — parity target is\n * the Rust function, not npm):\n * - `\"a,b\"` → `\"a-b\"` (stripped punctuation emits a dash, not nothing)\n * - `\" --weird-- \"` → `\"--weird--\"` (existing dashes/underscores pass\n * through unchanged)\n */\nexport function slugify(input: string): string {\n let out = \"\";\n let lastDash = true;\n\n for (const ch of input) {\n if (isWhitespace(ch) || isStripped(ch)) {\n if (!lastDash) {\n out += \"-\";\n lastDash = true;\n }\n } else {\n // Per-code-point lowercase — avoids Greek final-sigma context:\n // \"ΦΟΣ\" → \"φοσ\" not \"φος\".\n out += ch.toLowerCase();\n lastDash = false;\n }\n }\n\n if (out.endsWith(\"-\")) {\n out = out.slice(0, -1);\n }\n\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// next_slug\n// ---------------------------------------------------------------------------\n\n/**\n * Allocate the next deduplicated slug for `base` against a per-document\n * counter map.\n *\n * Mirrors Rust `next_slug`: first occurrence returns `base`, second\n * returns `base-1`, third `base-2`, … Empty `base` short-circuits to\n * `\"\"` without mutating the map.\n */\nfunction nextSlug(seen: Map<string, number>, base: string): string {\n if (base === \"\") return \"\";\n const count = seen.get(base) ?? 0;\n const slug = count === 0 ? base : `${base}-${count}`;\n seen.set(base, count + 1);\n return slug;\n}\n\n// ---------------------------------------------------------------------------\n// SlugAllocator\n// ---------------------------------------------------------------------------\n\n/**\n * Strategy-aware slug allocator for a single document.\n *\n * Mirrors Rust `SlugAllocator` in heading_links.rs:70-118.\n *\n * @param strategy `\"flat\"` (default) — per-document dedup counter, mirrors\n * github-slugger numbering. `\"hierarchical\"` — each slug is prefixed with\n * its ancestor chain and deduped on the full candidate path.\n *\n * Depth contract: callers pass `depth` values 2–6 (h2–h6). h1 is never\n * allocated — it is handled by the page layout, not the markdown body.\n */\nexport class SlugAllocator {\n private readonly strategy: \"flat\" | \"hierarchical\";\n private seen: Map<string, number>;\n /** Hierarchical ancestor stack of `[depth, finalId]` pairs. Unused in flat mode. */\n private stack: Array<[number, string]>;\n\n constructor(strategy: \"flat\" | \"hierarchical\" = \"flat\") {\n this.strategy = strategy;\n this.seen = new Map();\n this.stack = [];\n }\n\n /**\n * Allocate the slug for a heading of `depth` (2–6) whose slugified text\n * is `base`. Returns `\"\"` for an empty `base` without mutating state.\n */\n allocate(depth: number, base: string): string {\n if (this.strategy === \"flat\") {\n return nextSlug(this.seen, base);\n }\n\n // Hierarchical mode\n if (base === \"\") return \"\";\n\n // Pop ancestors whose depth >= this heading's depth\n let top = this.stack.at(-1);\n while (top !== undefined && top[0] >= depth) {\n this.stack.pop();\n top = this.stack.at(-1);\n }\n\n const candidate = top !== undefined ? `${top[1]}-${base}` : base;\n const slug = nextSlug(this.seen, candidate);\n this.stack.push([depth, slug]);\n return slug;\n }\n\n /**\n * Clear all per-document state (dedup counters AND the hierarchical\n * ancestor stack). Call between documents to avoid slug counter leakage.\n */\n reset(): void {\n this.seen.clear();\n this.stack = [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"slugify.js","sourceRoot":"","sources":["../src/slugify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["export { slugify, SlugAllocator } from \"@takazudo/zfb-slugify\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takazudo/zfb",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Content-site engine on the web's Request/Response model — prerender to static HTML, serve on Cloudflare Workers, or run as a local content server inside your app. One Rust binary. SDK with islands, content collections, pagination, and config helpers.",
|
|
@@ -95,12 +95,15 @@
|
|
|
95
95
|
"CHANGELOG.md",
|
|
96
96
|
"LICENSE"
|
|
97
97
|
],
|
|
98
|
+
"dependencies": {
|
|
99
|
+
"@takazudo/zfb-slugify": "4.3.1"
|
|
100
|
+
},
|
|
98
101
|
"optionalDependencies": {
|
|
99
|
-
"@takazudo/zfb-darwin-arm64": "4.
|
|
100
|
-
"@takazudo/zfb-darwin-x64": "4.
|
|
101
|
-
"@takazudo/zfb-linux-arm64-gnu": "4.
|
|
102
|
-
"@takazudo/zfb-linux-x64-gnu": "4.
|
|
103
|
-
"@takazudo/zfb-win32-x64-msvc": "4.
|
|
102
|
+
"@takazudo/zfb-darwin-arm64": "4.3.1",
|
|
103
|
+
"@takazudo/zfb-darwin-x64": "4.3.1",
|
|
104
|
+
"@takazudo/zfb-linux-arm64-gnu": "4.3.1",
|
|
105
|
+
"@takazudo/zfb-linux-x64-gnu": "4.3.1",
|
|
106
|
+
"@takazudo/zfb-win32-x64-msvc": "4.3.1"
|
|
104
107
|
},
|
|
105
108
|
"publishConfig": {
|
|
106
109
|
"access": "public"
|