@takazudo/zfb 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +8 -7
  2. package/dist/config.d.ts +43 -9
  3. package/dist/config.js +23 -12
  4. package/dist/config.js.map +1 -1
  5. package/dist/content.js.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/island-boundary.d.ts +2 -2
  9. package/dist/island-boundary.js +4 -2
  10. package/dist/island-boundary.js.map +1 -1
  11. package/dist/island.d.ts +8 -4
  12. package/dist/island.js +1 -1
  13. package/dist/island.js.map +1 -1
  14. package/dist/jsx-types.d.ts +1 -1
  15. package/dist/jsx-types.js.map +1 -1
  16. package/dist/runtime.d.ts +7 -4
  17. package/dist/runtime.js +166 -28
  18. package/dist/runtime.js.map +1 -1
  19. package/dist/slugify.js.map +1 -1
  20. package/dist/zudo-react/description.d.ts +18 -0
  21. package/dist/zudo-react/description.js +41 -0
  22. package/dist/zudo-react/description.js.map +1 -1
  23. package/dist/zudo-react/dom-bindings.d.ts +2 -1
  24. package/dist/zudo-react/dom-bindings.js +16 -22
  25. package/dist/zudo-react/dom-bindings.js.map +1 -1
  26. package/dist/zudo-react/forms.js.map +1 -1
  27. package/dist/zudo-react/hydrate.js +27 -34
  28. package/dist/zudo-react/hydrate.js.map +1 -1
  29. package/dist/zudo-react/index.d.ts +2 -1
  30. package/dist/zudo-react/index.js.map +1 -1
  31. package/dist/zudo-react/jsx-dev-runtime.d.ts +2 -6
  32. package/dist/zudo-react/jsx-dev-runtime.js +3 -3
  33. package/dist/zudo-react/jsx-dev-runtime.js.map +1 -1
  34. package/dist/zudo-react/jsx-runtime.js +2 -2
  35. package/dist/zudo-react/jsx-runtime.js.map +1 -1
  36. package/dist/zudo-react/jsx-types.d.ts +80 -31
  37. package/dist/zudo-react/jsx-types.js.map +1 -1
  38. package/dist/zudo-react/props-transport.d.ts +2 -0
  39. package/dist/zudo-react/props-transport.js +42 -14
  40. package/dist/zudo-react/props-transport.js.map +1 -1
  41. package/dist/zudo-react/raw-html.d.ts +1 -0
  42. package/dist/zudo-react/raw-html.js +120 -0
  43. package/dist/zudo-react/raw-html.js.map +1 -0
  44. package/dist/zudo-react/reactive.js.map +1 -1
  45. package/dist/zudo-react/render-html.d.ts +11 -1
  46. package/dist/zudo-react/render-html.js +96 -71
  47. package/dist/zudo-react/render-html.js.map +1 -1
  48. package/dist/zudo-react/root.js +24 -1
  49. package/dist/zudo-react/root.js.map +1 -1
  50. package/dist/zudo-react/server.d.ts +2 -0
  51. package/dist/zudo-react/server.js.map +1 -1
  52. package/dist/zudo-react/structure.d.ts +2 -0
  53. package/dist/zudo-react/structure.js +14 -0
  54. package/dist/zudo-react/structure.js.map +1 -1
  55. package/dist/zudo-react/style.d.ts +10 -0
  56. package/dist/zudo-react/style.js +525 -0
  57. package/dist/zudo-react/style.js.map +1 -0
  58. package/dist/zudo-react/testing.d.ts +27 -0
  59. package/dist/zudo-react/testing.js +117 -0
  60. package/dist/zudo-react/testing.js.map +1 -0
  61. package/dist/zudo-react/vocabulary.d.ts +19 -0
  62. package/dist/zudo-react/vocabulary.js +155 -0
  63. package/dist/zudo-react/vocabulary.js.map +1 -0
  64. package/package.json +16 -10
@@ -1 +1 @@
1
- {"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../src/zudo-react/reactive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAwB,MAAM,gBAAgB,CAAC;AAW/D,IAAI,eAAe,GAAoB,IAAI,CAAC;AAC5C,IAAI,UAAU,GAAG,CAAC,CAAC;AACnB,MAAM,eAAe,GAAwB,EAAE,CAAC;AAChD,IAAI,MAAM,GAAG,CAAC,CAAC;AAEf,iEAAiE;AACjE,MAAM,IAAI,GAAG,EAAE,gDAAgD,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;AAEhG,SAAS,KAAK,CAAC,MAAc;IAC3B,IAAI,CAAC,eAAe;QAAE,OAAO;IAC7B,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACxC,eAAe,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC;AACD,SAAS,MAAM,CAAC,QAAkB;IAChC,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,YAAY;QAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChF,QAAQ,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;AAChC,CAAC;AACD,SAAS,SAAS,CAAI,EAAW;IAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC;IACjC,eAAe,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACT,eAAe,GAAG,QAAQ,CAAC;IAC7B,CAAC;AACH,CAAC;AACD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,MAAM,QAAQ;IAIQ;IAHX,cAAc,GAAG,wBAAiC,CAAC;IACnD,cAAc,GAAG,wBAAiC,CAAC;IACnD,WAAW,GAAG,IAAI,GAAG,EAAY,CAAC;IAC3C,YAAoB,OAAU;QAAV,YAAO,GAAP,OAAO,CAAG;QAC5B,IAAI,CAAC,IAAI,CAAC,gDAAgD,CAAC;YACzD,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,KAAK;QACP,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IACD,IAAI,KAAK,CAAC,KAAQ;QAChB,IAAI,UAAU,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QAC3F,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO;QAC3C,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;YAAE,QAAQ,CAAC,UAAU,EAAE,CAAC;IACtE,CAAC;CACF;AAED,MAAM,QAAQ;IAQiB;IAPpB,cAAc,GAAG,wBAAiC,CAAC;IACnD,EAAE,GAAG,YAAY,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IACjC,WAAW,GAAG,IAAI,GAAG,EAAY,CAAC;IAClC,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,CAAK;IACX,KAAK,GAAG,IAAI,CAAC;IACb,aAAa,GAAG,KAAK,CAAC;IAC9B,YAA6B,IAAa;QAAb,SAAI,GAAJ,IAAI,CAAS;IAAG,CAAC;IAC9C,IAAI,KAAK;QACP,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,sBAAsB,CAAC,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAC5F,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAChC,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IACO,QAAQ;QACd,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,QAAQ,GAAG,eAAe,CAAC;QACjC,eAAe,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,eAAe,CAAC,IAAI,CAAC,IAAyB,CAAC,CAAC;QAChD,UAAU,EAAE,CAAC;QACb,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACvB,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,UAAU,EAAE,CAAC;YACb,eAAe,CAAC,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,eAAe,GAAG,QAAQ,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,UAAU;QACR,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;YAAE,QAAQ,CAAC,UAAU,EAAE,CAAC;IACtE,CAAC;IACD,OAAO;QACL,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAChC,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;CACF;AAED,SAAS,OAAO,CAAC,MAAc;IAC7B,IAAI,MAAM,YAAY,QAAQ;QAAE,MAAM,CAAC,OAAO,EAAE,CAAC;AACnD,CAAC;AACD,SAAS,gBAAgB,CAAC,QAAkB;IAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC3C,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjB,KAAK,MAAM,MAAM,IAAI,OAAO;QAAE,OAAO,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,MAAM,CAAI,OAAU;IAClC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AACD,MAAM,UAAU,QAAQ,CAAI,IAAa;IACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AACD,MAAM,UAAU,YAAY,CAAI,KAAwB;IACtD,OAAO,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC;AAOD,MAAM,UAAU,SAAS,CACvB,QAAoB,EACpB,UAA0F,EAAE;IAE5F,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,QAAQ,GAAa;QACzB,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,cAAc,IAAI,CAAC,MAAM,EAAE,EAAE;QAC/C,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,UAAU;YACR,OAAO,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;KACF,CAAC;IACF,MAAM,GAAG,GAAQ;QACf,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,QAAQ;QAChC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,IAAI,CAAC;QACvD,GAAG,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE;QAC7B,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC;IACF,MAAM,YAAY,GAAiB;QACjC,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,GAAG;YACD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE;gBAAE,OAAO;YAC1B,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC3B,MAAM,QAAQ,GAAG,eAAe,CAAC;YACjC,eAAe,GAAG,QAAQ,CAAC;YAC3B,IAAI,CAAC;gBACH,QAAQ,EAAE,CAAC;YACb,CAAC;oBAAS,CAAC;gBACT,eAAe,GAAG,QAAQ,CAAC;YAC7B,CAAC;QACH,CAAC;QACD,OAAO;YACL,QAAQ,GAAG,IAAI,CAAC;YAChB,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC;IACF,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,eAAe,CAAC,KAA8B;IAC5D,OAAO,KAAK,YAAY,QAAQ,IAAI,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7F,CAAC","sourcesContent":["import { enqueue, type Job, type Phase } from \"./scheduler.js\";\nimport type { ReadonlySignal, Signal } from \"./reactive-types.js\";\n\ninterface Source {\n readonly subscribers: Set<Observer>;\n}\ninterface Observer {\n readonly id: string;\n readonly dependencies: Set<Source>;\n invalidate(): void;\n}\nlet currentObserver: Observer | null = null;\nlet evaluating = 0;\nconst evaluationStack: Computed<unknown>[] = [];\nlet nextId = 0;\n\n// The quoted own key is the retained runtime definition witness.\nconst core = { \"@takazudo/zfb/zudo-react/runtime-definition-v1\": true, nextId: () => ++nextId };\n\nfunction track(source: Source): void {\n if (!currentObserver) return;\n source.subscribers.add(currentObserver);\n currentObserver.dependencies.add(source);\n}\nfunction detach(observer: Observer): void {\n for (const source of observer.dependencies) source.subscribers.delete(observer);\n observer.dependencies.clear();\n}\nfunction untracked<T>(fn: () => T): T {\n const previous = currentObserver;\n currentObserver = null;\n try {\n return fn();\n } finally {\n currentObserver = previous;\n }\n}\nexport { untracked };\n\nclass Writable<T> implements Signal<T>, Source {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly $$zudoWritable = \"zudo-react.writable.v1\" as const;\n readonly subscribers = new Set<Observer>();\n constructor(private current: T) {\n if (!core[\"@takazudo/zfb/zudo-react/runtime-definition-v1\"])\n throw new Error(\"ZR_RUNTIME_DEFINITION\");\n }\n get value(): T {\n track(this);\n return this.current;\n }\n set value(value: T) {\n if (evaluating > 0) throw new Error(\"ZR_COMPUTED_WRITE: write during computed evaluation\");\n if (Object.is(value, this.current)) return;\n this.current = value;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n}\n\nclass Computed<T> implements ReadonlySignal<T>, Source, Observer {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly id = `computed:${core.nextId()}`;\n readonly subscribers = new Set<Observer>();\n readonly dependencies = new Set<Source>();\n private cached!: T;\n private dirty = true;\n private evaluatingNow = false;\n constructor(private readonly read: () => T) {}\n get value(): T {\n if (this.evaluatingNow) {\n throw new Error(\n `ZR_COMPUTED_CYCLE: ${[...evaluationStack.map((entry) => entry.id), this.id].join(\" -> \")}`,\n );\n }\n if (this.dirty) this.evaluate();\n track(this);\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n return this.cached;\n }\n private evaluate(): void {\n detachAndRelease(this);\n const previous = currentObserver;\n currentObserver = this;\n this.evaluatingNow = true;\n evaluationStack.push(this as Computed<unknown>);\n evaluating++;\n try {\n this.cached = this.read();\n this.dirty = false;\n } catch (error) {\n detachAndRelease(this);\n throw error;\n } finally {\n evaluating--;\n evaluationStack.pop();\n this.evaluatingNow = false;\n currentObserver = previous;\n }\n }\n invalidate(): void {\n if (this.dirty) return;\n this.dirty = true;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n release(): void {\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n }\n}\n\nfunction release(source: Source): void {\n if (source instanceof Computed) source.release();\n}\nfunction detachAndRelease(observer: Observer): void {\n const sources = [...observer.dependencies];\n detach(observer);\n for (const source of sources) release(source);\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n return new Writable(initial);\n}\nexport function computed<T>(read: () => T): ReadonlySignal<T> {\n return new Computed(read);\n}\nexport function readSnapshot<T>(value: ReadonlySignal<T>): T {\n return untracked(() => value.value);\n}\n\nexport interface Subscription {\n readonly id: string;\n run(): void;\n dispose(): void;\n}\nexport function subscribe(\n callback: () => void,\n options: { phase?: Phase; id?: string; active?: () => boolean; report?: Job[\"report\"] } = {},\n): Subscription {\n let disposed = false;\n const observer: Observer = {\n id: options.id ?? `subscriber:${core.nextId()}`,\n dependencies: new Set(),\n invalidate() {\n enqueue(job);\n },\n };\n const job: Job = {\n id: observer.id,\n phase: options.phase ?? \"commit\",\n active: () => !disposed && (options.active?.() ?? true),\n run: () => subscription.run(),\n ...(options.report ? { report: options.report } : {}),\n };\n const subscription: Subscription = {\n id: observer.id,\n run() {\n if (!job.active()) return;\n detachAndRelease(observer);\n const previous = currentObserver;\n currentObserver = observer;\n try {\n callback();\n } finally {\n currentObserver = previous;\n }\n },\n dispose() {\n disposed = true;\n detachAndRelease(observer);\n },\n };\n return subscription;\n}\n\n/** Internal test introspection; absent from every public entry. */\nexport function subscriberCount(value: ReadonlySignal<unknown>): number {\n return value instanceof Writable || value instanceof Computed ? value.subscribers.size : 0;\n}\n"]}
1
+ {"version":3,"file":"reactive.js","sourceRoot":"","sources":["../../src/zudo-react/reactive.ts"],"names":[],"mappings":"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","sourcesContent":["import { enqueue, type Job, type Phase } from \"./scheduler.js\";\nimport type { ReadonlySignal, Signal } from \"./reactive-types.js\";\n\ninterface Source {\n readonly subscribers: Set<Observer>;\n}\ninterface Observer {\n readonly id: string;\n readonly dependencies: Set<Source>;\n invalidate(): void;\n}\nlet currentObserver: Observer | null = null;\nlet evaluating = 0;\nconst evaluationStack: Computed<unknown>[] = [];\nlet nextId = 0;\n\n// The quoted own key is the retained runtime definition witness.\nconst core = { \"@takazudo/zfb/zudo-react/runtime-definition-v1\": true, nextId: () => ++nextId };\n\nfunction track(source: Source): void {\n if (!currentObserver) return;\n source.subscribers.add(currentObserver);\n currentObserver.dependencies.add(source);\n}\nfunction detach(observer: Observer): void {\n for (const source of observer.dependencies) source.subscribers.delete(observer);\n observer.dependencies.clear();\n}\nfunction untracked<T>(fn: () => T): T {\n const previous = currentObserver;\n currentObserver = null;\n try {\n return fn();\n } finally {\n currentObserver = previous;\n }\n}\nexport { untracked };\n\nclass Writable<T> implements Signal<T>, Source {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly $$zudoWritable = \"zudo-react.writable.v1\" as const;\n readonly subscribers = new Set<Observer>();\n constructor(private current: T) {\n if (!core[\"@takazudo/zfb/zudo-react/runtime-definition-v1\"])\n throw new Error(\"ZR_RUNTIME_DEFINITION\");\n }\n get value(): T {\n track(this);\n return this.current;\n }\n set value(value: T) {\n if (evaluating > 0) throw new Error(\"ZR_COMPUTED_WRITE: write during computed evaluation\");\n if (Object.is(value, this.current)) return;\n this.current = value;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n}\n\nclass Computed<T> implements ReadonlySignal<T>, Source, Observer {\n readonly $$zudoReactive = \"zudo-react.reactive.v1\" as const;\n readonly id = `computed:${core.nextId()}`;\n readonly subscribers = new Set<Observer>();\n readonly dependencies = new Set<Source>();\n private cached!: T;\n private dirty = true;\n private evaluatingNow = false;\n constructor(private readonly read: () => T) {}\n get value(): T {\n if (this.evaluatingNow) {\n throw new Error(\n `ZR_COMPUTED_CYCLE: ${[...evaluationStack.map((entry) => entry.id), this.id].join(\" -> \")}`,\n );\n }\n if (this.dirty) this.evaluate();\n track(this);\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n return this.cached;\n }\n private evaluate(): void {\n detachAndRelease(this);\n const previous = currentObserver;\n currentObserver = this;\n this.evaluatingNow = true;\n evaluationStack.push(this as Computed<unknown>);\n evaluating++;\n try {\n this.cached = this.read();\n this.dirty = false;\n } catch (error) {\n detachAndRelease(this);\n throw error;\n } finally {\n evaluating--;\n evaluationStack.pop();\n this.evaluatingNow = false;\n currentObserver = previous;\n }\n }\n invalidate(): void {\n if (this.dirty) return;\n this.dirty = true;\n for (const observer of [...this.subscribers]) observer.invalidate();\n }\n release(): void {\n if (this.subscribers.size === 0) {\n detachAndRelease(this);\n this.dirty = true;\n }\n }\n}\n\nfunction release(source: Source): void {\n if (source instanceof Computed) source.release();\n}\nfunction detachAndRelease(observer: Observer): void {\n const sources = [...observer.dependencies];\n detach(observer);\n for (const source of sources) release(source);\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n return new Writable(initial);\n}\nexport function computed<T>(read: () => T): ReadonlySignal<T> {\n return new Computed(read);\n}\nexport function readSnapshot<T>(value: ReadonlySignal<T>): T {\n return untracked(() => value.value);\n}\n\nexport interface Subscription {\n readonly id: string;\n run(): void;\n dispose(): void;\n}\nexport function subscribe(\n callback: () => void,\n options: { phase?: Phase; id?: string; active?: () => boolean; report?: Job[\"report\"] } = {},\n): Subscription {\n let disposed = false;\n const observer: Observer = {\n id: options.id ?? `subscriber:${core.nextId()}`,\n dependencies: new Set(),\n invalidate() {\n enqueue(job);\n },\n };\n const job: Job = {\n id: observer.id,\n phase: options.phase ?? \"commit\",\n active: () => !disposed && (options.active?.() ?? true),\n run: () => subscription.run(),\n ...(options.report ? { report: options.report } : {}),\n };\n const subscription: Subscription = {\n id: observer.id,\n run() {\n if (!job.active()) return;\n detachAndRelease(observer);\n const previous = currentObserver;\n currentObserver = observer;\n try {\n callback();\n } finally {\n currentObserver = previous;\n }\n },\n dispose() {\n disposed = true;\n detachAndRelease(observer);\n },\n };\n return subscription;\n}\n\n/** Internal test introspection; absent from every public entry. */\nexport function subscriberCount(value: ReadonlySignal<unknown>): number {\n return value instanceof Writable || value instanceof Computed ? value.subscribers.size : 0;\n}\n"]}
@@ -1,3 +1,13 @@
1
- import { type Child } from "./description.js";
1
+ import { type Child, type DescriptionSite } from "./description.js";
2
2
  import type { RenderOptions } from "./server.js";
3
+ export interface RenderDiagnostic {
4
+ readonly code: string;
5
+ readonly path: string;
6
+ readonly component: string;
7
+ readonly spelling?: {
8
+ readonly name: string;
9
+ readonly suggestion?: string;
10
+ };
11
+ readonly site?: DescriptionSite;
12
+ }
3
13
  export declare function renderHtml(node: Child, options: RenderOptions): string;
@@ -1,21 +1,15 @@
1
- import { Fragment, isDescription } from "./description.js";
1
+ import { serializeStyle } from "./style.js";
2
+ import { Fragment, descriptionSite, isDescription, } from "./description.js";
2
3
  import { escapeAttribute, escapeText } from "./escape.js";
4
+ import { rawHtmlReserved } from "./raw-html.js";
3
5
  import { readSnapshot } from "./reactive.js";
4
6
  import { createScope, withScope } from "./scope.js";
5
- import { serializeProps } from "./props-transport.js";
7
+ import { emptyChildren, normalizeProps } from "./props-transport.js";
6
8
  import { islandRootType } from "./island-root-type.js";
7
- import { Show, For, showProps, forProps, keyed, keyPayload, view } from "./structure.js";
9
+ import { isShowType, isForType, showProps, forProps, keyed, keyPayload, view, } from "./structure.js";
10
+ import { attributeError, attributeText, booleanAttrs, commonAttrs, dialectSuggestion, emptyIframeChildren, formProps, htmlAttrs, htmlTags, isDialectProp, reactiveModelSuggestion, svgAttrs, svgTags, voidTags, } from "./vocabulary.js";
8
11
  const words = (value) => new Set(value.split(" "));
9
- const voidTags = words("area base br col embed hr img input link meta param source track wbr");
10
- const booleanAttrs = words("hidden inert readonly autofocus required disabled checked selected multiple open controls muted loop autoplay novalidate formnovalidate allowfullscreen");
11
- const commonAttrs = words("id class title lang dir slot role style hidden inert contenteditable draggable spellcheck tabindex accesskey translate onclick onload onerror");
12
- const htmlAttrs = words("href target rel download src alt width height type name value placeholder for charset datetime readonly autofocus required disabled checked selected multiple open controls muted loop autoplay novalidate formnovalidate maxlength minlength min max step pattern autocomplete accept accept-charset http-equiv content media method action enctype rows cols colspan rowspan scope cite poster loading decoding sizes srcset crossorigin referrerpolicy sandbox allow allowfullscreen");
13
- const svgAttrs = words("width height viewBox preserveAspectRatio gradientUnits gradientTransform markerWidth markerHeight refX refY xlink:href xml:lang stroke-width fill-rule clip-rule stroke-linecap stroke-linejoin stop-color stop-opacity fill stroke d x y x1 x2 y1 y2 cx cy r rx ry points transform opacity offset");
14
- const svgTags = words("svg g path circle ellipse rect line polyline polygon text tspan defs symbol use clipPath mask linearGradient radialGradient stop title desc foreignObject");
15
- const htmlTags = words("html head body title base link meta style script div span p a br hr main header footer nav section article aside h1 h2 h3 h4 h5 h6 ul ol li dl dt dd blockquote pre code strong em b i small mark time figure figcaption img picture source video audio track canvas form label input button textarea select option optgroup fieldset legend output progress meter datalist table caption thead tbody tfoot tr th td col colgroup details summary dialog template slot iframe noscript address abbr bdi bdo cite data del dfn ins kbd map area object param q rp rt ruby s samp sub sup u var wbr embed xmp noembed noframes plaintext");
16
- const dialect = words("className htmlFor charSet dateTime tabIndex readOnly strokeWidth dangerouslySetInnerHTML");
17
12
  const reserved = words("key ref children rawHtml");
18
- const forms = words("modelValue modelChecked defaultValue defaultChecked");
19
13
  const sensitive = words("noscript xmp iframe noembed noframes plaintext");
20
14
  const tableChildren = {
21
15
  table: words("caption colgroup thead tbody tfoot"),
@@ -25,8 +19,19 @@ const tableChildren = {
25
19
  tr: words("td th"),
26
20
  colgroup: words("col"),
27
21
  };
28
- function fail(code, context, detail) {
29
- throw new TypeError(`${code}: ${detail} at ${context.path} in ${context.identity?.component ?? "static render"}`);
22
+ function fail(code, context, detail, spelling) {
23
+ const component = context.identity?.component ?? "static render";
24
+ const site = context.node && descriptionSite(context.node);
25
+ const diagnostic = {
26
+ code,
27
+ path: context.path,
28
+ component,
29
+ ...(spelling && { spelling }),
30
+ ...(site && { site }),
31
+ };
32
+ throw Object.assign(new TypeError(`${code}: ${detail} at ${context.path} in ${component}`), {
33
+ diagnostic,
34
+ });
30
35
  }
31
36
  function reactive(value) {
32
37
  return (typeof value === "object" &&
@@ -70,34 +75,26 @@ function renderText(value, context) {
70
75
  fail("ZR_CHILD", context, `invalid scalar ${typeof value}`);
71
76
  }
72
77
  function style(value, context) {
73
- if (typeof value === "string")
74
- return value;
75
- if (value === null ||
76
- typeof value !== "object" ||
77
- Array.isArray(value) ||
78
- Object.getPrototypeOf(value) !== Object.prototype)
79
- fail("ZR_STYLE", context, "style must be a string or flat object");
80
- let result = "";
81
- for (const [name, entry] of Object.entries(value)) {
82
- if (!/^(--[a-zA-Z0-9_-]+|[a-z][a-z0-9-]*)$/.test(name) || /[A-Z]/.test(name))
83
- fail("ZR_STYLE", context, `invalid property ${name}`);
84
- if (entry == null)
85
- continue;
86
- if (typeof entry !== "string" && !(typeof entry === "number" && Number.isFinite(entry)))
87
- fail("ZR_STYLE", context, `invalid value for ${name}`);
88
- result += `${name}:${entry};`;
78
+ try {
79
+ return serializeStyle(value);
80
+ }
81
+ catch (error) {
82
+ if (error instanceof TypeError)
83
+ fail("ZR_STYLE", context, error.message.replace(/^ZR_STYLE: /, ""));
84
+ throw error;
89
85
  }
90
- return result;
91
86
  }
92
87
  function attributes(tag, props, context, namespace, custom) {
93
88
  let output = "";
94
89
  for (const [name, original] of Object.entries(props)) {
95
90
  if (reserved.has(name))
96
91
  continue;
97
- if (forms.has(name))
92
+ if (formProps.has(name))
98
93
  continue;
99
- if (dialect.has(name) || /^on[A-Z]/.test(name))
100
- fail("ZR_PROP_DIALECT", context, `${tag}.${name}`);
94
+ if (isDialectProp(name, namespace, custom)) {
95
+ const suggestion = dialectSuggestion(name, namespace, custom);
96
+ fail("ZR_PROP_DIALECT", context, `${tag}.${name}${suggestion ? ` (use \`${suggestion}\` instead of \`${name}\`)` : ""}`, suggestion ? { name, suggestion } : { name });
97
+ }
101
98
  if (name.startsWith("on:")) {
102
99
  if (!/^on:[A-Za-z][A-Za-z0-9_-]*(?::capture)?$/.test(name))
103
100
  fail("ZR_LISTENER", context, `${tag}.${name}`);
@@ -118,37 +115,32 @@ function attributes(tag, props, context, namespace, custom) {
118
115
  !/^on[a-z]+$/.test(name))
119
116
  fail("ZR_ATTRIBUTE", context, `${tag}.${name}`);
120
117
  const value = read(original);
121
- if ((name === "value" || name === "checked") && reactive(original))
122
- fail("ZR_MODEL_UNSUPPORTED", context, `${tag}.${name} requires modelValue/modelChecked`);
118
+ if ((name === "value" || name === "checked") && reactive(original)) {
119
+ const suggestion = reactiveModelSuggestion(tag, name, String(read(props.type) ?? "text"), custom);
120
+ fail("ZR_MODEL_UNSUPPORTED", context, `${tag}.${name} requires ${suggestion ?? "a static value"}`);
121
+ }
122
+ if (/^on[a-z]/.test(name) && typeof value === "function")
123
+ fail("ZR_PROP_DIALECT", context, `${tag}.${name} must use on:${name.slice(2)}`, {
124
+ name,
125
+ suggestion: `on:${name.slice(2)}`,
126
+ });
127
+ const error = attributeError(name, value, custom);
128
+ if (error)
129
+ fail("ZR_ATTRIBUTE", context, `${tag}.${name} ${error}`);
123
130
  if (value == null)
124
131
  continue;
125
- if (/^on[a-z]/.test(name) && typeof value === "function")
126
- fail("ZR_PROP_DIALECT", context, `${tag}.${name} must use on:${name.slice(2)}`);
127
- if (custom && typeof value !== "string")
128
- fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a string`);
129
- if (name === "style")
130
- output += ` style="${escapeAttribute(style(value, context))}"`;
131
- else if (booleanAttrs.has(name)) {
132
- if (typeof value !== "boolean")
133
- fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a boolean`);
134
- if (value)
135
- output += ` ${name}`;
136
- }
137
- else {
138
- if (typeof value !== "string" &&
139
- typeof value !== "boolean" &&
140
- !(typeof value === "number" && Number.isFinite(value)))
141
- fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a scalar`);
142
- if (typeof value === "boolean" && !name.startsWith("aria-") && !name.startsWith("data-"))
143
- fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a string`);
144
- if (/^on[a-z]/.test(name) && typeof value !== "string")
145
- fail("ZR_ATTRIBUTE", context, `${tag}.${name} requires a string`);
146
- output += ` ${name}="${escapeAttribute(String(value))}"`;
147
- }
132
+ const text = name === "style" ? style(value, context) : attributeText(name, value);
133
+ if (text === null)
134
+ continue;
135
+ output +=
136
+ booleanAttrs.has(name) && value === true ? ` ${name}` : ` ${name}="${escapeAttribute(text)}"`;
148
137
  }
149
138
  return output;
150
139
  }
151
140
  function restricted(value, context, namespace, parent) {
141
+ return tracked(value, context, () => restrictedValue(value, context, namespace, parent));
142
+ }
143
+ function restrictedValue(value, context, namespace, parent) {
152
144
  if (value == null || typeof value === "boolean")
153
145
  return "";
154
146
  if (Array.isArray(value))
@@ -173,7 +165,7 @@ function element(description, context, namespace, parent) {
173
165
  const childNamespace = tag === "foreignObject" ? "html" : elementNamespace;
174
166
  if (elementNamespace === "svg" ? !svgTags.has(tag) : !custom && !htmlTags.has(tag))
175
167
  fail("ZR_TAG", context, tag);
176
- if (context.identity && (tag === "template" || sensitive.has(tag)))
168
+ if (context.identity && (tag === "template" || (tag !== "iframe" && sensitive.has(tag))))
177
169
  fail("ZR_PARSER_CONTEXT", context, tag);
178
170
  if (parent in tableChildren && !tableChildren[parent]?.has(tag))
179
171
  fail("ZR_PARSER_CONTEXT", context, `${parent} > ${tag}`);
@@ -188,6 +180,12 @@ function element(description, context, namespace, parent) {
188
180
  : `form:${context.nextFormId++}`;
189
181
  const source = description.props;
190
182
  const props = { ...source };
183
+ if (tag === "iframe") {
184
+ if (Object.hasOwn(props, "rawHtml"))
185
+ fail("ZR_RAW_HTML", context, "iframe forbids rawHtml");
186
+ if (!emptyIframeChildren(props.children))
187
+ fail("ZR_PARSER_CONTEXT", context, "iframe requires empty children");
188
+ }
191
189
  const hasValue = source.modelValue !== undefined;
192
190
  const hasChecked = source.modelChecked !== undefined;
193
191
  if ((hasValue && hasChecked) ||
@@ -315,7 +313,7 @@ function element(description, context, namespace, parent) {
315
313
  const payload = read(raw);
316
314
  if (typeof payload !== "string")
317
315
  fail("ZR_RAW_HTML", context, `${tag} requires string rawHtml`);
318
- if (/<!--\/?zr:1:|data-zfb-island(?:-skip-ssr)?\s*=/.test(payload))
316
+ if (rawHtmlReserved(payload, tag))
319
317
  fail("ZR_RAW_HTML", context, "reserved boundary in rawHtml");
320
318
  if ((tag === "script" && /<\/script/i.test(payload)) ||
321
319
  (tag === "style" && /<\/style/i.test(payload)))
@@ -328,6 +326,9 @@ function element(description, context, namespace, parent) {
328
326
  fail("ZR_MODEL_VALUE", context, "textarea requires string");
329
327
  content = `${value.startsWith("\n") ? "\n" : ""}${escapeText(value)}`;
330
328
  }
329
+ else if (tag === "iframe") {
330
+ // The shell has no owned children; srcdoc belongs to the browser's foreign document.
331
+ }
331
332
  else if (tag === "script" || tag === "style") {
332
333
  if (hasChildren)
333
334
  fail("ZR_RAW_HTML", context, `${tag} requires rawHtml`);
@@ -363,10 +364,27 @@ function element(description, context, namespace, parent) {
363
364
  }
364
365
  else
365
366
  content = children(props.children, context, childNamespace, tag);
367
+ if (tag === "pre" && raw === undefined && (content.startsWith("\n") || content.startsWith("\r")))
368
+ content = `\n${content}`;
366
369
  context.formId = previousFormId;
367
370
  return `<${tag}${attrs}>${voidTags.has(tag) ? "" : `${content}</${tag}>`}`;
368
371
  }
372
+ // A failure is attributed only to the value being rendered: a description's own site,
373
+ // or no site at all for scalars, promises and other non-description children.
374
+ function tracked(value, context, fn) {
375
+ const prior = context.node;
376
+ context.node = isDescription(value) ? value : undefined;
377
+ try {
378
+ return fn();
379
+ }
380
+ finally {
381
+ context.node = prior;
382
+ }
383
+ }
369
384
  function render(value, context, namespace, parent) {
385
+ return tracked(value, context, () => renderValue(value, context, namespace, parent));
386
+ }
387
+ function renderValue(value, context, namespace, parent) {
370
388
  if (value == null || typeof value === "boolean")
371
389
  return "";
372
390
  if (Array.isArray(value))
@@ -383,7 +401,7 @@ function render(value, context, namespace, parent) {
383
401
  return island(value, context, namespace, parent);
384
402
  if (value.type === Fragment)
385
403
  return region(context, "f", () => children(value.props.children, context, namespace, parent));
386
- if (value.type === Show) {
404
+ if (isShowType(value.type)) {
387
405
  if (parent in tableChildren ||
388
406
  ["select", "optgroup", "option", "title", "textarea", "script", "style"].includes(parent))
389
407
  fail("ZR_PARSER_CONTEXT", context, parent);
@@ -410,7 +428,7 @@ function render(value, context, namespace, parent) {
410
428
  }
411
429
  });
412
430
  }
413
- if (value.type === For) {
431
+ if (isForType(value.type)) {
414
432
  if (parent in tableChildren ||
415
433
  ["select", "optgroup", "option", "title", "textarea", "script", "style"].includes(parent))
416
434
  fail("ZR_PARSER_CONTEXT", context, parent);
@@ -455,9 +473,6 @@ function render(value, context, namespace, parent) {
455
473
  context.scope = prior;
456
474
  }
457
475
  }
458
- function empty(value) {
459
- return (value == null || typeof value === "boolean" || (Array.isArray(value) && value.every(empty)));
460
- }
461
476
  function island(description, context, namespace, parent) {
462
477
  if (context.boundary)
463
478
  fail("ZR_NESTED_ISLAND", context, "nested island");
@@ -472,13 +487,13 @@ function island(description, context, namespace, parent) {
472
487
  const component = child.type.displayName ?? child.type.name;
473
488
  if (component !== options.identity.component)
474
489
  fail("ZR_ISLAND_IDENTITY", context, `expected ${options.identity.component}, got ${component}`);
475
- if (Object.hasOwn(child.props, "children") && !empty(child.props.children))
490
+ if (Object.hasOwn(child.props, "children") && !emptyChildren(child.props.children))
476
491
  fail("ZR_ISLAND_CHILD", context, "nonempty children");
477
- const props = { ...child.props };
478
- delete props.children;
492
+ let props;
479
493
  let payload;
480
494
  try {
481
- payload = serializeProps(props);
495
+ props = normalizeProps(child.props);
496
+ payload = JSON.stringify(props);
482
497
  }
483
498
  catch (error) {
484
499
  throw new TypeError(`ZR_ISLAND_PROPS ${component}: ${error instanceof Error ? error.message : String(error)}`);
@@ -488,8 +503,18 @@ function island(description, context, namespace, parent) {
488
503
  (when === "media" && !options.media?.trim()) ||
489
504
  (when !== "media" && options.media !== undefined))
490
505
  fail("ZR_ISLAND_WHEN", context, when);
506
+ if (options.persist !== undefined &&
507
+ (typeof options.persist !== "string" ||
508
+ !options.persist.trim() ||
509
+ options.persist !== options.persist.trim() ||
510
+ /[\u0000-\u001f\u007f]/.test(options.persist)))
511
+ fail("ZR_ISLAND_PERSIST", context, "persist must be a nonempty string without surrounding whitespace or control characters");
512
+ if (options.persistProps !== undefined && typeof options.persistProps !== "boolean")
513
+ fail("ZR_ISLAND_PERSIST", context, "persistProps must be a boolean");
514
+ if (options.persistProps !== undefined && options.persist === undefined)
515
+ fail("ZR_ISLAND_PERSIST", context, "persistProps requires persist");
491
516
  const marker = options.skipSsr ? "data-zfb-island-skip-ssr" : "data-zfb-island";
492
- const attrs = ` ${marker}="${escapeAttribute(options.identity.component)}" data-when="${when}"${when === "media" ? ` data-media="${escapeAttribute(options.media)}"` : ""} data-zfb-transport="json/1" data-zfb-protocol="zudo-react/1" data-zfb-build="${escapeAttribute(options.identity.build)}" data-props="${escapeAttribute(payload)}"`;
517
+ const attrs = ` ${marker}="${escapeAttribute(options.identity.component)}" data-when="${when}"${when === "media" ? ` data-media="${escapeAttribute(options.media)}"` : ""} data-zfb-transport="json/1" data-zfb-protocol="zudo-react/1" data-zfb-build="${escapeAttribute(options.identity.build)}" data-props="${escapeAttribute(payload)}"${options.persist === undefined ? "" : ` data-zfb-transition-persist="${escapeAttribute(options.persist)}"`}${options.persistProps === true ? ' data-zfb-transition-persist-props="true"' : ""}`;
493
518
  const local = {
494
519
  radioGroups: new Map(),
495
520
  radioNames: new Map(),