@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.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.
Files changed (447) hide show
  1. package/LICENSE +90 -0
  2. package/README.md +43 -2
  3. package/dist/a11y/announcer.d.ts +65 -0
  4. package/dist/a11y/announcer.js +140 -0
  5. package/dist/a11y/announcer.js.map +1 -0
  6. package/dist/a11y/contrast.d.ts +54 -0
  7. package/dist/a11y/contrast.js +93 -0
  8. package/dist/a11y/contrast.js.map +1 -0
  9. package/dist/a11y/focus.d.ts +123 -0
  10. package/dist/a11y/focus.js +282 -0
  11. package/dist/a11y/focus.js.map +1 -0
  12. package/dist/a11y/index.d.ts +21 -0
  13. package/dist/a11y/index.js +18 -0
  14. package/dist/a11y/index.js.map +1 -0
  15. package/dist/a11y/keyboard.d.ts +144 -0
  16. package/dist/a11y/keyboard.js +317 -0
  17. package/dist/a11y/keyboard.js.map +1 -0
  18. package/dist/adapters/default-image.d.ts +25 -0
  19. package/dist/adapters/default-image.js +37 -0
  20. package/dist/adapters/default-image.js.map +1 -0
  21. package/dist/adapters/index.d.ts +27 -0
  22. package/dist/adapters/index.js +28 -0
  23. package/dist/adapters/index.js.map +1 -0
  24. package/dist/adapters/next-image.d.ts +38 -0
  25. package/dist/adapters/next-image.js +124 -0
  26. package/dist/adapters/next-image.js.map +1 -0
  27. package/dist/adapters/types.d.ts +61 -0
  28. package/dist/adapters/types.js +11 -0
  29. package/dist/adapters/types.js.map +1 -0
  30. package/dist/analytics/index.d.ts +14 -0
  31. package/dist/analytics/index.js +15 -0
  32. package/dist/analytics/index.js.map +1 -0
  33. package/dist/analytics/tracker.d.ts +65 -0
  34. package/dist/analytics/tracker.js +251 -0
  35. package/dist/analytics/tracker.js.map +1 -0
  36. package/dist/analytics/types.d.ts +130 -0
  37. package/dist/analytics/types.js +11 -0
  38. package/dist/analytics/types.js.map +1 -0
  39. package/dist/auth/index.d.ts +15 -0
  40. package/dist/auth/index.js +14 -0
  41. package/dist/auth/index.js.map +1 -0
  42. package/dist/auth/provider.d.ts +132 -0
  43. package/dist/auth/provider.js +473 -0
  44. package/dist/auth/provider.js.map +1 -0
  45. package/dist/auth/types.d.ts +117 -0
  46. package/dist/auth/types.js +9 -0
  47. package/dist/auth/types.js.map +1 -0
  48. package/dist/binding/collection.d.ts +190 -0
  49. package/dist/binding/collection.js +358 -0
  50. package/dist/binding/collection.js.map +1 -0
  51. package/dist/binding/datasource.d.ts +319 -0
  52. package/dist/binding/datasource.js +556 -0
  53. package/dist/binding/datasource.js.map +1 -0
  54. package/dist/binding/engine.d.ts +65 -0
  55. package/dist/binding/engine.js +268 -0
  56. package/dist/binding/engine.js.map +1 -0
  57. package/dist/binding/expression.d.ts +150 -0
  58. package/dist/binding/expression.js +787 -0
  59. package/dist/binding/expression.js.map +1 -0
  60. package/dist/binding/index.d.ts +19 -0
  61. package/dist/binding/index.js +14 -0
  62. package/dist/binding/index.js.map +1 -0
  63. package/dist/binding/two-way.d.ts +149 -0
  64. package/dist/binding/two-way.js +462 -0
  65. package/dist/binding/two-way.js.map +1 -0
  66. package/dist/binding/types.d.ts +119 -0
  67. package/dist/binding/types.js +9 -0
  68. package/dist/binding/types.js.map +1 -0
  69. package/dist/blocks/component-types.d.ts +81 -0
  70. package/dist/blocks/component-types.js +11 -0
  71. package/dist/blocks/component-types.js.map +1 -0
  72. package/dist/blocks/composition-rules.d.ts +168 -0
  73. package/dist/blocks/composition-rules.js +391 -0
  74. package/dist/blocks/composition-rules.js.map +1 -0
  75. package/dist/blocks/hooks.d.ts +31 -0
  76. package/dist/blocks/hooks.js +42 -0
  77. package/dist/blocks/hooks.js.map +1 -0
  78. package/dist/blocks/index.d.ts +14 -0
  79. package/dist/blocks/index.js +23 -0
  80. package/dist/blocks/index.js.map +1 -0
  81. package/dist/blocks/registry.d.ts +84 -0
  82. package/dist/blocks/registry.js +136 -0
  83. package/dist/blocks/registry.js.map +1 -0
  84. package/dist/blocks/rule-engine.d.ts +131 -0
  85. package/dist/blocks/rule-engine.js +321 -0
  86. package/dist/blocks/rule-engine.js.map +1 -0
  87. package/dist/blocks/surface-event-context.d.ts +13 -0
  88. package/dist/blocks/surface-event-context.js +14 -0
  89. package/dist/blocks/surface-event-context.js.map +1 -0
  90. package/dist/blocks/surface-id-context.d.ts +13 -0
  91. package/dist/blocks/surface-id-context.js +15 -0
  92. package/dist/blocks/surface-id-context.js.map +1 -0
  93. package/dist/bus.d.ts +41 -0
  94. package/dist/bus.js +158 -0
  95. package/dist/bus.js.map +1 -0
  96. package/dist/cache/index.d.ts +9 -0
  97. package/dist/cache/index.js +10 -0
  98. package/dist/cache/index.js.map +1 -0
  99. package/dist/cache/memory.d.ts +32 -0
  100. package/dist/cache/memory.js +138 -0
  101. package/dist/cache/memory.js.map +1 -0
  102. package/dist/cache/types.d.ts +62 -0
  103. package/dist/cache/types.js +9 -0
  104. package/dist/cache/types.js.map +1 -0
  105. package/dist/capability/index.d.ts +72 -0
  106. package/dist/capability/index.js +87 -0
  107. package/dist/capability/index.js.map +1 -0
  108. package/dist/capability/kernel-capabilities.d.ts +31 -0
  109. package/dist/capability/kernel-capabilities.js +36 -0
  110. package/dist/capability/kernel-capabilities.js.map +1 -0
  111. package/dist/capability/platform-capabilities.d.ts +71 -0
  112. package/dist/capability/platform-capabilities.js +267 -0
  113. package/dist/capability/platform-capabilities.js.map +1 -0
  114. package/dist/capability/type-mapping.d.ts +70 -0
  115. package/dist/capability/type-mapping.js +107 -0
  116. package/dist/capability/type-mapping.js.map +1 -0
  117. package/dist/capability/validation.d.ts +46 -0
  118. package/dist/capability/validation.js +54 -0
  119. package/dist/capability/validation.js.map +1 -0
  120. package/dist/connectors/declared-connector.d.ts +48 -0
  121. package/dist/connectors/declared-connector.js +333 -0
  122. package/dist/connectors/declared-connector.js.map +1 -0
  123. package/dist/connectors/engine-registry.d.ts +50 -0
  124. package/dist/connectors/engine-registry.js +135 -0
  125. package/dist/connectors/engine-registry.js.map +1 -0
  126. package/dist/connectors/index.d.ts +14 -0
  127. package/dist/connectors/index.js +14 -0
  128. package/dist/connectors/index.js.map +1 -0
  129. package/dist/connectors/module-loader.d.ts +70 -0
  130. package/dist/connectors/module-loader.js +143 -0
  131. package/dist/connectors/module-loader.js.map +1 -0
  132. package/dist/connectors/types.d.ts +174 -0
  133. package/dist/connectors/types.js +23 -0
  134. package/dist/connectors/types.js.map +1 -0
  135. package/dist/context.d.ts +27 -0
  136. package/dist/context.js +38 -0
  137. package/dist/context.js.map +1 -0
  138. package/dist/data/DataCapabilityProvider.d.ts +39 -0
  139. package/dist/data/DataCapabilityProvider.js +58 -0
  140. package/dist/data/DataCapabilityProvider.js.map +1 -0
  141. package/dist/data/index.d.ts +17 -0
  142. package/dist/data/index.js +18 -0
  143. package/dist/data/index.js.map +1 -0
  144. package/dist/data/manager.d.ts +40 -0
  145. package/dist/data/manager.js +442 -0
  146. package/dist/data/manager.js.map +1 -0
  147. package/dist/data/types.d.ts +218 -0
  148. package/dist/data/types.js +2 -0
  149. package/dist/data/types.js.map +1 -0
  150. package/dist/entity/aggregate.d.ts +8 -0
  151. package/dist/entity/aggregate.js +24 -0
  152. package/dist/entity/aggregate.js.map +1 -0
  153. package/dist/entity/apply-query.d.ts +58 -0
  154. package/dist/entity/apply-query.js +264 -0
  155. package/dist/entity/apply-query.js.map +1 -0
  156. package/dist/entity/behavior.d.ts +94 -0
  157. package/dist/entity/behavior.js +78 -0
  158. package/dist/entity/behavior.js.map +1 -0
  159. package/dist/entity/connector.d.ts +141 -0
  160. package/dist/entity/connector.js +89 -0
  161. package/dist/entity/connector.js.map +1 -0
  162. package/dist/entity/connectors/index.d.ts +12 -0
  163. package/dist/entity/connectors/index.js +11 -0
  164. package/dist/entity/connectors/index.js.map +1 -0
  165. package/dist/entity/connectors/static.d.ts +51 -0
  166. package/dist/entity/connectors/static.js +245 -0
  167. package/dist/entity/connectors/static.js.map +1 -0
  168. package/dist/entity/entity-datasource.d.ts +50 -0
  169. package/dist/entity/entity-datasource.js +33 -0
  170. package/dist/entity/entity-datasource.js.map +1 -0
  171. package/dist/entity/facade.d.ts +70 -0
  172. package/dist/entity/facade.js +70 -0
  173. package/dist/entity/facade.js.map +1 -0
  174. package/dist/entity/http-connector.d.ts +123 -0
  175. package/dist/entity/http-connector.js +295 -0
  176. package/dist/entity/http-connector.js.map +1 -0
  177. package/dist/entity/index.d.ts +38 -0
  178. package/dist/entity/index.js +31 -0
  179. package/dist/entity/index.js.map +1 -0
  180. package/dist/entity/postgres-connector.d.ts +64 -0
  181. package/dist/entity/postgres-connector.js +239 -0
  182. package/dist/entity/postgres-connector.js.map +1 -0
  183. package/dist/entity/registry.d.ts +59 -0
  184. package/dist/entity/registry.js +90 -0
  185. package/dist/entity/registry.js.map +1 -0
  186. package/dist/entity/store.d.ts +66 -0
  187. package/dist/entity/store.js +349 -0
  188. package/dist/entity/store.js.map +1 -0
  189. package/dist/entity/types.d.ts +180 -0
  190. package/dist/entity/types.js +10 -0
  191. package/dist/entity/types.js.map +1 -0
  192. package/dist/execution-context.d.ts +88 -0
  193. package/dist/execution-context.js +146 -0
  194. package/dist/execution-context.js.map +1 -0
  195. package/dist/executor.d.ts +27 -0
  196. package/dist/executor.js +33 -0
  197. package/dist/executor.js.map +1 -0
  198. package/dist/forms/form-entity-bridge.d.ts +94 -0
  199. package/dist/forms/form-entity-bridge.js +96 -0
  200. package/dist/forms/form-entity-bridge.js.map +1 -0
  201. package/dist/forms/form-write-target.d.ts +34 -0
  202. package/dist/forms/form-write-target.js +81 -0
  203. package/dist/forms/form-write-target.js.map +1 -0
  204. package/dist/forms/index.d.ts +22 -0
  205. package/dist/forms/index.js +25 -0
  206. package/dist/forms/index.js.map +1 -0
  207. package/dist/forms/named-control-values.d.ts +27 -0
  208. package/dist/forms/named-control-values.js +42 -0
  209. package/dist/forms/named-control-values.js.map +1 -0
  210. package/dist/forms/processor.d.ts +55 -0
  211. package/dist/forms/processor.js +154 -0
  212. package/dist/forms/processor.js.map +1 -0
  213. package/dist/forms/types.d.ts +174 -0
  214. package/dist/forms/types.js +33 -0
  215. package/dist/forms/types.js.map +1 -0
  216. package/dist/forms/validator.d.ts +61 -0
  217. package/dist/forms/validator.js +218 -0
  218. package/dist/forms/validator.js.map +1 -0
  219. package/dist/graph/index.d.ts +10 -0
  220. package/dist/graph/index.js +9 -0
  221. package/dist/graph/index.js.map +1 -0
  222. package/dist/graph/resolver.d.ts +66 -0
  223. package/dist/graph/resolver.js +243 -0
  224. package/dist/graph/resolver.js.map +1 -0
  225. package/dist/graph/types.d.ts +125 -0
  226. package/dist/graph/types.js +9 -0
  227. package/dist/graph/types.js.map +1 -0
  228. package/dist/hooks/useInstalledAtoms.d.ts +96 -0
  229. package/dist/hooks/useInstalledAtoms.js +92 -0
  230. package/dist/hooks/useInstalledAtoms.js.map +1 -0
  231. package/dist/i18n/index.d.ts +15 -0
  232. package/dist/i18n/index.js +14 -0
  233. package/dist/i18n/index.js.map +1 -0
  234. package/dist/i18n/translator.d.ts +76 -0
  235. package/dist/i18n/translator.js +357 -0
  236. package/dist/i18n/translator.js.map +1 -0
  237. package/dist/i18n/types.d.ts +93 -0
  238. package/dist/i18n/types.js +9 -0
  239. package/dist/i18n/types.js.map +1 -0
  240. package/dist/index.d.ts +64 -0
  241. package/dist/index.js +95 -0
  242. package/dist/index.js.map +1 -0
  243. package/dist/kernel.d.ts +47 -0
  244. package/dist/kernel.js +258 -0
  245. package/dist/kernel.js.map +1 -0
  246. package/dist/perf/budget.d.ts +92 -0
  247. package/dist/perf/budget.js +122 -0
  248. package/dist/perf/budget.js.map +1 -0
  249. package/dist/perf/index.d.ts +16 -0
  250. package/dist/perf/index.js +13 -0
  251. package/dist/perf/index.js.map +1 -0
  252. package/dist/perf/render-timer.d.ts +90 -0
  253. package/dist/perf/render-timer.js +136 -0
  254. package/dist/perf/render-timer.js.map +1 -0
  255. package/dist/perf/reporter.d.ts +126 -0
  256. package/dist/perf/reporter.js +241 -0
  257. package/dist/perf/reporter.js.map +1 -0
  258. package/dist/perf/web-vitals.d.ts +111 -0
  259. package/dist/perf/web-vitals.js +268 -0
  260. package/dist/perf/web-vitals.js.map +1 -0
  261. package/dist/registry.d.ts +63 -0
  262. package/dist/registry.js +126 -0
  263. package/dist/registry.js.map +1 -0
  264. package/dist/render/BehaviorSurfaceProvider.d.ts +65 -0
  265. package/dist/render/BehaviorSurfaceProvider.js +185 -0
  266. package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
  267. package/dist/render/BlockErrorBoundary.d.ts +26 -0
  268. package/dist/render/BlockErrorBoundary.js +41 -0
  269. package/dist/render/BlockErrorBoundary.js.map +1 -0
  270. package/dist/render/DataBoundSurfaceRenderer.d.ts +78 -0
  271. package/dist/render/DataBoundSurfaceRenderer.js +998 -0
  272. package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
  273. package/dist/render/DynamicBlockWrapper.d.ts +23 -0
  274. package/dist/render/DynamicBlockWrapper.js +117 -0
  275. package/dist/render/DynamicBlockWrapper.js.map +1 -0
  276. package/dist/render/appearance-resolver.d.ts +50 -0
  277. package/dist/render/appearance-resolver.js +78 -0
  278. package/dist/render/appearance-resolver.js.map +1 -0
  279. package/dist/render/capability-gate.d.ts +69 -0
  280. package/dist/render/capability-gate.js +94 -0
  281. package/dist/render/capability-gate.js.map +1 -0
  282. package/dist/render/conditional-states-css.d.ts +41 -0
  283. package/dist/render/conditional-states-css.js +82 -0
  284. package/dist/render/conditional-states-css.js.map +1 -0
  285. package/dist/render/connections-context.d.ts +31 -0
  286. package/dist/render/connections-context.js +34 -0
  287. package/dist/render/connections-context.js.map +1 -0
  288. package/dist/render/dark-theme-css.d.ts +36 -0
  289. package/dist/render/dark-theme-css.js +67 -0
  290. package/dist/render/dark-theme-css.js.map +1 -0
  291. package/dist/render/data-binding-types.d.ts +191 -0
  292. package/dist/render/data-binding-types.js +11 -0
  293. package/dist/render/data-binding-types.js.map +1 -0
  294. package/dist/render/data-cache.d.ts +30 -0
  295. package/dist/render/data-cache.js +37 -0
  296. package/dist/render/data-cache.js.map +1 -0
  297. package/dist/render/derive-dark-theme.d.ts +17 -0
  298. package/dist/render/derive-dark-theme.js +18 -0
  299. package/dist/render/derive-dark-theme.js.map +1 -0
  300. package/dist/render/dynamic-artifact-context.d.ts +77 -0
  301. package/dist/render/dynamic-artifact-context.js +89 -0
  302. package/dist/render/dynamic-artifact-context.js.map +1 -0
  303. package/dist/render/dynamic-block-loader.d.ts +80 -0
  304. package/dist/render/dynamic-block-loader.js +197 -0
  305. package/dist/render/dynamic-block-loader.js.map +1 -0
  306. package/dist/render/endpoint.d.ts +18 -0
  307. package/dist/render/endpoint.js +109 -0
  308. package/dist/render/endpoint.js.map +1 -0
  309. package/dist/render/entity-facade-context.d.ts +34 -0
  310. package/dist/render/entity-facade-context.js +37 -0
  311. package/dist/render/entity-facade-context.js.map +1 -0
  312. package/dist/render/group-states-css.d.ts +41 -0
  313. package/dist/render/group-states-css.js +73 -0
  314. package/dist/render/group-states-css.js.map +1 -0
  315. package/dist/render/hooks/useSurfaceEvents.d.ts +8 -0
  316. package/dist/render/hooks/useSurfaceEvents.js +9 -0
  317. package/dist/render/hooks/useSurfaceEvents.js.map +1 -0
  318. package/dist/render/hooks/useSurfaceId.d.ts +8 -0
  319. package/dist/render/hooks/useSurfaceId.js +9 -0
  320. package/dist/render/hooks/useSurfaceId.js.map +1 -0
  321. package/dist/render/index.d.ts +48 -0
  322. package/dist/render/index.js +61 -0
  323. package/dist/render/index.js.map +1 -0
  324. package/dist/render/interaction-rule-engine.d.ts +12 -0
  325. package/dist/render/interaction-rule-engine.js +47 -0
  326. package/dist/render/interaction-rule-engine.js.map +1 -0
  327. package/dist/render/keyframes-css.d.ts +31 -0
  328. package/dist/render/keyframes-css.js +44 -0
  329. package/dist/render/keyframes-css.js.map +1 -0
  330. package/dist/render/query-state.d.ts +3 -0
  331. package/dist/render/query-state.js +70 -0
  332. package/dist/render/query-state.js.map +1 -0
  333. package/dist/render/renderer.d.ts +44 -0
  334. package/dist/render/renderer.js +937 -0
  335. package/dist/render/renderer.js.map +1 -0
  336. package/dist/render/resolve-expressions.d.ts +37 -0
  337. package/dist/render/resolve-expressions.js +309 -0
  338. package/dist/render/resolve-expressions.js.map +1 -0
  339. package/dist/render/responsive-style-css.d.ts +42 -0
  340. package/dist/render/responsive-style-css.js +123 -0
  341. package/dist/render/responsive-style-css.js.map +1 -0
  342. package/dist/render/responsive.d.ts +49 -0
  343. package/dist/render/responsive.js +99 -0
  344. package/dist/render/responsive.js.map +1 -0
  345. package/dist/render/scoped-css.d.ts +71 -0
  346. package/dist/render/scoped-css.js +102 -0
  347. package/dist/render/scoped-css.js.map +1 -0
  348. package/dist/render/shell-selection.d.ts +69 -0
  349. package/dist/render/shell-selection.js +117 -0
  350. package/dist/render/shell-selection.js.map +1 -0
  351. package/dist/render/states-css.d.ts +32 -0
  352. package/dist/render/states-css.js +63 -0
  353. package/dist/render/states-css.js.map +1 -0
  354. package/dist/render/surface-data-context.d.ts +18 -0
  355. package/dist/render/surface-data-context.js +21 -0
  356. package/dist/render/surface-data-context.js.map +1 -0
  357. package/dist/render/surface-event-bus.d.ts +19 -0
  358. package/dist/render/surface-event-bus.js +68 -0
  359. package/dist/render/surface-event-bus.js.map +1 -0
  360. package/dist/render/surface-event-context.d.ts +11 -0
  361. package/dist/render/surface-event-context.js +12 -0
  362. package/dist/render/surface-event-context.js.map +1 -0
  363. package/dist/render/surface-id-context.d.ts +8 -0
  364. package/dist/render/surface-id-context.js +9 -0
  365. package/dist/render/surface-id-context.js.map +1 -0
  366. package/dist/render/theme-utils.d.ts +14 -0
  367. package/dist/render/theme-utils.js +15 -0
  368. package/dist/render/theme-utils.js.map +1 -0
  369. package/dist/render/types.d.ts +282 -0
  370. package/dist/render/types.js +9 -0
  371. package/dist/render/types.js.map +1 -0
  372. package/dist/render/visibility-css.d.ts +42 -0
  373. package/dist/render/visibility-css.js +119 -0
  374. package/dist/render/visibility-css.js.map +1 -0
  375. package/dist/render/width-scope.d.ts +34 -0
  376. package/dist/render/width-scope.js +44 -0
  377. package/dist/render/width-scope.js.map +1 -0
  378. package/dist/render/write-only-sources.d.ts +33 -0
  379. package/dist/render/write-only-sources.js +99 -0
  380. package/dist/render/write-only-sources.js.map +1 -0
  381. package/dist/routing/index.d.ts +14 -0
  382. package/dist/routing/index.js +15 -0
  383. package/dist/routing/index.js.map +1 -0
  384. package/dist/routing/router.d.ts +42 -0
  385. package/dist/routing/router.js +218 -0
  386. package/dist/routing/router.js.map +1 -0
  387. package/dist/routing/types.d.ts +124 -0
  388. package/dist/routing/types.js +9 -0
  389. package/dist/routing/types.js.map +1 -0
  390. package/dist/seo/generator.d.ts +39 -0
  391. package/dist/seo/generator.js +236 -0
  392. package/dist/seo/generator.js.map +1 -0
  393. package/dist/seo/index.d.ts +14 -0
  394. package/dist/seo/index.js +15 -0
  395. package/dist/seo/index.js.map +1 -0
  396. package/dist/seo/types.d.ts +181 -0
  397. package/dist/seo/types.js +9 -0
  398. package/dist/seo/types.js.map +1 -0
  399. package/dist/state/index.d.ts +15 -0
  400. package/dist/state/index.js +16 -0
  401. package/dist/state/index.js.map +1 -0
  402. package/dist/state/manager.d.ts +33 -0
  403. package/dist/state/manager.js +81 -0
  404. package/dist/state/manager.js.map +1 -0
  405. package/dist/state/store.d.ts +40 -0
  406. package/dist/state/store.js +145 -0
  407. package/dist/state/store.js.map +1 -0
  408. package/dist/state/types.d.ts +107 -0
  409. package/dist/state/types.js +9 -0
  410. package/dist/state/types.js.map +1 -0
  411. package/dist/surface/expression.d.ts +65 -0
  412. package/dist/surface/expression.js +179 -0
  413. package/dist/surface/expression.js.map +1 -0
  414. package/dist/surface/index.d.ts +32 -0
  415. package/dist/surface/index.js +25 -0
  416. package/dist/surface/index.js.map +1 -0
  417. package/dist/surface/interpreter.d.ts +96 -0
  418. package/dist/surface/interpreter.js +231 -0
  419. package/dist/surface/interpreter.js.map +1 -0
  420. package/dist/surface/layout.d.ts +54 -0
  421. package/dist/surface/layout.js +288 -0
  422. package/dist/surface/layout.js.map +1 -0
  423. package/dist/surface/output.d.ts +90 -0
  424. package/dist/surface/output.js +104 -0
  425. package/dist/surface/output.js.map +1 -0
  426. package/dist/surface/primitives.d.ts +180 -0
  427. package/dist/surface/primitives.js +339 -0
  428. package/dist/surface/primitives.js.map +1 -0
  429. package/dist/surface/renderer.d.ts +96 -0
  430. package/dist/surface/renderer.js +190 -0
  431. package/dist/surface/renderer.js.map +1 -0
  432. package/dist/surface/schemas.d.ts +181 -0
  433. package/dist/surface/schemas.js +142 -0
  434. package/dist/surface/schemas.js.map +1 -0
  435. package/dist/surface/transforms.d.ts +144 -0
  436. package/dist/surface/transforms.js +120 -0
  437. package/dist/surface/transforms.js.map +1 -0
  438. package/dist/surface/types.d.ts +160 -0
  439. package/dist/surface/types.js +51 -0
  440. package/dist/surface/types.js.map +1 -0
  441. package/dist/surface/validation.d.ts +123 -0
  442. package/dist/surface/validation.js +147 -0
  443. package/dist/surface/validation.js.map +1 -0
  444. package/dist/validator.d.ts +55 -0
  445. package/dist/validator.js +100 -0
  446. package/dist/validator.js.map +1 -0
  447. package/package.json +106 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-vitals.js","sourceRoot":"","sources":["../../src/perf/web-vitals.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Core Web Vitals Collection\n *\n * Collects Core Web Vitals (LCP, FID, CLS, FCP, TTFB, INP) using\n * PerformanceObserver. Provides real-time metric collection with\n * rating classification based on web.dev thresholds.\n *\n * All browser APIs are guarded for SSR safety.\n *\n * @module @pwfabric/runtime/perf\n */\n\n// ============================================================\n// TYPES\n// ============================================================\n\n/** Web Vital metric names */\nexport type WebVitalName = 'LCP' | 'FID' | 'CLS' | 'FCP' | 'TTFB' | 'INP'\n\n/** Rating classification for a metric */\nexport type WebVitalRating = 'good' | 'needs-improvement' | 'poor'\n\n/** A single Web Vital measurement */\nexport interface WebVitalMetric {\n /** Metric name */\n readonly name: WebVitalName\n /** Metric value (ms for timing metrics, unitless for CLS) */\n readonly value: number\n /** Rating based on web.dev thresholds */\n readonly rating: WebVitalRating\n /** Timestamp when the metric was collected */\n readonly timestamp: number\n}\n\n/** Callback invoked when a metric is collected */\nexport type WebVitalCallback = (metric: WebVitalMetric) => void\n\n/** Aggregated report of all collected Web Vitals */\nexport interface WebVitalsReport {\n /** Largest Contentful Paint */\n readonly lcp?: WebVitalMetric | undefined\n /** First Input Delay */\n readonly fid?: WebVitalMetric | undefined\n /** Cumulative Layout Shift */\n readonly cls?: WebVitalMetric | undefined\n /** First Contentful Paint */\n readonly fcp?: WebVitalMetric | undefined\n /** Time to First Byte */\n readonly ttfb?: WebVitalMetric | undefined\n /** Interaction to Next Paint */\n readonly inp?: WebVitalMetric | undefined\n /** Timestamp when the report was generated */\n readonly collectedAt: number\n}\n\n// ============================================================\n// THRESHOLDS (from web.dev)\n// ============================================================\n\n/** Rating thresholds: [goodThreshold, poorThreshold] */\nconst THRESHOLDS: Record<WebVitalName, readonly [number, number]> = {\n LCP: [2500, 4000],\n FID: [100, 300],\n CLS: [0.1, 0.25],\n FCP: [1800, 3000],\n TTFB: [800, 1800],\n INP: [200, 500],\n} as const\n\n// ============================================================\n// UTILITY\n// ============================================================\n\n/**\n * Rate a metric value against web.dev thresholds.\n *\n * @param name - The metric name\n * @param value - The measured value\n * @returns The rating classification\n */\nexport function rateMetric(name: WebVitalName, value: number): WebVitalRating {\n const threshold = THRESHOLDS[name]\n if (!threshold) {\n return 'good'\n }\n if (value <= threshold[0]) {\n return 'good'\n }\n if (value > threshold[1]) {\n return 'poor'\n }\n return 'needs-improvement'\n}\n\n// ============================================================\n// COLLECTOR\n// ============================================================\n\n/**\n * Collects Core Web Vitals using PerformanceObserver.\n *\n * Supports LCP, FID, CLS, FCP, TTFB, and INP metrics.\n * All observers are cleaned up on dispose.\n */\nexport class WebVitalsCollector {\n private metrics = new Map<WebVitalName, WebVitalMetric>()\n private observers: PerformanceObserver[] = []\n private callbacks = new Map<WebVitalName, WebVitalCallback[]>()\n private clsValue = 0\n private clsSessionEntries: PerformanceEntry[] = []\n private clsSessionValue = 0\n\n /**\n * Start collecting all Web Vitals.\n *\n * Creates PerformanceObservers for each metric type.\n * Safe to call in SSR environments (no-ops silently).\n */\n startCollecting(): void {\n if (typeof PerformanceObserver === 'undefined') {\n return\n }\n\n this.observeLCP()\n this.observeFID()\n this.observeCLS()\n this.observeFCP()\n this.observeTTFB()\n this.observeINP()\n }\n\n /**\n * Register a callback for a specific metric.\n *\n * The callback fires each time the metric is updated.\n *\n * @param name - The metric name to observe\n * @param callback - Function to call with the metric\n */\n onMetric(name: WebVitalName, callback: WebVitalCallback): void {\n const existing = this.callbacks.get(name) ?? []\n existing.push(callback)\n this.callbacks.set(name, existing)\n }\n\n /**\n * Get the current Web Vitals report.\n *\n * @returns A snapshot of all collected metrics\n */\n getReport(): WebVitalsReport {\n return {\n lcp: this.metrics.get('LCP'),\n fid: this.metrics.get('FID'),\n cls: this.metrics.get('CLS'),\n fcp: this.metrics.get('FCP'),\n ttfb: this.metrics.get('TTFB'),\n inp: this.metrics.get('INP'),\n collectedAt: Date.now(),\n }\n }\n\n /**\n * Stop collecting metrics and disconnect all observers.\n */\n stopCollecting(): void {\n for (const observer of this.observers) {\n observer.disconnect()\n }\n this.observers = []\n }\n\n /**\n * Stop collecting and clear all state.\n */\n dispose(): void {\n this.stopCollecting()\n this.metrics.clear()\n this.callbacks.clear()\n this.clsValue = 0\n this.clsSessionEntries = []\n this.clsSessionValue = 0\n }\n\n // ----------------------------------------------------------\n // Private: metric recording\n // ----------------------------------------------------------\n\n private recordMetric(name: WebVitalName, value: number): void {\n const metric: WebVitalMetric = {\n name,\n value,\n rating: rateMetric(name, value),\n timestamp: Date.now(),\n }\n this.metrics.set(name, metric)\n\n const cbs = this.callbacks.get(name)\n if (cbs) {\n for (const cb of cbs) {\n cb(metric)\n }\n }\n }\n\n // ----------------------------------------------------------\n // Private: observer helpers\n // ----------------------------------------------------------\n\n private tryObserve(\n entryType: string,\n handler: (entries: PerformanceEntryList) => void,\n options?: { buffered?: boolean; type?: string }\n ): void {\n try {\n const observer = new PerformanceObserver((list) => {\n handler(list.getEntries())\n })\n const observeOptions: PerformanceObserverInit = {\n type: options?.type ?? entryType,\n buffered: options?.buffered ?? true,\n }\n observer.observe(observeOptions)\n this.observers.push(observer)\n } catch {\n // Entry type not supported in this browser — silently skip\n }\n }\n\n private observeLCP(): void {\n this.tryObserve('largest-contentful-paint', (entries) => {\n const last = entries[entries.length - 1]\n if (last) {\n this.recordMetric('LCP', last.startTime)\n }\n })\n }\n\n private observeFID(): void {\n this.tryObserve('first-input', (entries) => {\n const first = entries[0]\n if (first) {\n const fidEntry = first as PerformanceEventTiming\n this.recordMetric('FID', fidEntry.processingStart - fidEntry.startTime)\n }\n })\n }\n\n private observeCLS(): void {\n this.tryObserve('layout-shift', (entries) => {\n for (const entry of entries) {\n const lsEntry = entry as LayoutShiftEntry\n // Only count shifts without recent user input\n if (!lsEntry.hadRecentInput) {\n // Session window algorithm\n if (\n this.clsSessionEntries.length > 0 &&\n entry.startTime -\n (this.clsSessionEntries[this.clsSessionEntries.length - 1]?.startTime ?? 0) >\n 1000\n ) {\n // Gap > 1s → start new session window\n if (this.clsSessionValue > this.clsValue) {\n this.clsValue = this.clsSessionValue\n }\n this.clsSessionEntries = []\n this.clsSessionValue = 0\n }\n\n // Add to current session\n if (\n this.clsSessionEntries.length === 0 ||\n entry.startTime - (this.clsSessionEntries[0]?.startTime ?? 0) <= 5000\n ) {\n this.clsSessionEntries.push(entry)\n this.clsSessionValue += lsEntry.value\n } else {\n // Session exceeded 5s → start new session\n if (this.clsSessionValue > this.clsValue) {\n this.clsValue = this.clsSessionValue\n }\n this.clsSessionEntries = [entry]\n this.clsSessionValue = lsEntry.value\n }\n\n // Report the max of current session and previous max\n const currentCls = Math.max(this.clsValue, this.clsSessionValue)\n this.recordMetric('CLS', currentCls)\n }\n }\n })\n }\n\n private observeFCP(): void {\n this.tryObserve('paint', (entries) => {\n const fcp = entries.find((e) => e.name === 'first-contentful-paint')\n if (fcp) {\n this.recordMetric('FCP', fcp.startTime)\n }\n })\n }\n\n private observeTTFB(): void {\n this.tryObserve('navigation', (entries) => {\n const nav = entries[0] as PerformanceNavigationTiming | undefined\n if (nav) {\n this.recordMetric('TTFB', nav.responseStart)\n }\n })\n }\n\n private observeINP(): void {\n this.tryObserve(\n 'event',\n (entries) => {\n let maxDuration = 0\n for (const entry of entries) {\n const eventEntry = entry as PerformanceEventTiming\n if (eventEntry.duration > maxDuration) {\n maxDuration = eventEntry.duration\n }\n }\n if (maxDuration > 0) {\n const existing = this.metrics.get('INP')\n // INP tracks the worst interaction\n if (!existing || maxDuration > existing.value) {\n this.recordMetric('INP', maxDuration)\n }\n }\n },\n { type: 'event', buffered: true }\n )\n }\n}\n\n// ============================================================\n// LAYOUT SHIFT ENTRY TYPE (not in all TS libs)\n// ============================================================\n\ninterface LayoutShiftEntry extends PerformanceEntry {\n readonly hadRecentInput: boolean\n readonly value: number\n}\n\ninterface PerformanceEventTiming extends PerformanceEntry {\n readonly processingStart: number\n readonly duration: number\n}\n\n// ============================================================\n// FACTORY\n// ============================================================\n\n/**\n * Create a new WebVitalsCollector instance.\n *\n * @returns A new collector (not yet started)\n */\nexport function createWebVitalsCollector(): WebVitalsCollector {\n return new WebVitalsCollector()\n}\n"]}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Capability Registry
3
+ *
4
+ * Registry for managing capability registration and lookup
5
+ *
6
+ * @module @pwfabric/runtime/registry
7
+ */
8
+ import type { CapabilityId, Capability, RegistrationResult, Resolution } from '@pwfabric/contracts';
9
+ /**
10
+ * Capability registry for managing registered capabilities
11
+ */
12
+ export interface CapabilityRegistry {
13
+ /**
14
+ * Register a capability
15
+ */
16
+ register<C extends Capability>(capability: C): RegistrationResult<C>;
17
+ /**
18
+ * Unregister a capability
19
+ */
20
+ unregister(id: CapabilityId): boolean;
21
+ /**
22
+ * Resolve a capability by ID
23
+ */
24
+ resolve<C extends Capability>(id: CapabilityId): Resolution<C>;
25
+ /**
26
+ * Check if a capability is registered
27
+ */
28
+ has(id: CapabilityId): boolean;
29
+ /**
30
+ * Get all registered capability IDs
31
+ */
32
+ list(): readonly CapabilityId[];
33
+ /**
34
+ * Get the count of registered capabilities
35
+ */
36
+ size(): number;
37
+ /**
38
+ * Clear all registered capabilities
39
+ */
40
+ clear(): void;
41
+ }
42
+ /**
43
+ * In-memory capability registry
44
+ */
45
+ export declare class InMemoryCapabilityRegistry implements CapabilityRegistry {
46
+ private readonly capabilities;
47
+ private readonly dependencyGraph;
48
+ register<C extends Capability>(capability: C): RegistrationResult<C>;
49
+ unregister(id: CapabilityId): boolean;
50
+ resolve<C extends Capability>(id: CapabilityId): Resolution<C>;
51
+ has(id: CapabilityId): boolean;
52
+ list(): readonly CapabilityId[];
53
+ size(): number;
54
+ clear(): void;
55
+ /**
56
+ * Detect circular dependencies using DFS
57
+ */
58
+ private detectCircularDependency;
59
+ }
60
+ /**
61
+ * Create a new capability registry
62
+ */
63
+ export declare function createRegistry(): CapabilityRegistry;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Capability Registry
3
+ *
4
+ * Registry for managing capability registration and lookup
5
+ *
6
+ * @module @pwfabric/runtime/registry
7
+ */
8
+ // ============================================================
9
+ // IN-MEMORY REGISTRY IMPLEMENTATION
10
+ // ============================================================
11
+ /**
12
+ * In-memory capability registry
13
+ */
14
+ export class InMemoryCapabilityRegistry {
15
+ capabilities = new Map();
16
+ dependencyGraph = new Map();
17
+ register(capability) {
18
+ const { id, dependencies } = capability;
19
+ // Check if already registered
20
+ if (this.capabilities.has(id)) {
21
+ const error = {
22
+ type: 'already_registered',
23
+ id,
24
+ };
25
+ return { success: false, error };
26
+ }
27
+ // Validate dependencies exist
28
+ for (const depId of dependencies) {
29
+ if (!this.capabilities.has(depId)) {
30
+ const error = {
31
+ type: 'missing_dependency',
32
+ id,
33
+ missing: depId,
34
+ };
35
+ return { success: false, error };
36
+ }
37
+ }
38
+ // Check for circular dependencies
39
+ const cycle = this.detectCircularDependency(id, dependencies);
40
+ if (cycle) {
41
+ const error = {
42
+ type: 'circular_dependency',
43
+ cycle,
44
+ };
45
+ return { success: false, error };
46
+ }
47
+ // Register the capability
48
+ this.capabilities.set(id, capability);
49
+ this.dependencyGraph.set(id, new Set(dependencies));
50
+ return { success: true, capability };
51
+ }
52
+ unregister(id) {
53
+ if (!this.capabilities.has(id)) {
54
+ return false;
55
+ }
56
+ // Check if other capabilities depend on this one
57
+ for (const [, deps] of this.dependencyGraph) {
58
+ if (deps.has(id)) {
59
+ // Cannot unregister - something depends on it
60
+ return false;
61
+ }
62
+ }
63
+ this.capabilities.delete(id);
64
+ this.dependencyGraph.delete(id);
65
+ return true;
66
+ }
67
+ resolve(id) {
68
+ const capability = this.capabilities.get(id);
69
+ if (!capability) {
70
+ const reason = {
71
+ type: 'not_found',
72
+ id,
73
+ };
74
+ return { found: false, reason };
75
+ }
76
+ return { found: true, capability: capability };
77
+ }
78
+ has(id) {
79
+ return this.capabilities.has(id);
80
+ }
81
+ list() {
82
+ return Array.from(this.capabilities.keys());
83
+ }
84
+ size() {
85
+ return this.capabilities.size;
86
+ }
87
+ clear() {
88
+ this.capabilities.clear();
89
+ this.dependencyGraph.clear();
90
+ }
91
+ /**
92
+ * Detect circular dependencies using DFS
93
+ */
94
+ detectCircularDependency(newId, newDeps) {
95
+ const visited = new Set();
96
+ const path = [];
97
+ const dfs = (id) => {
98
+ if (path.includes(id)) {
99
+ // Found a cycle
100
+ const cycleStart = path.indexOf(id);
101
+ return [...path.slice(cycleStart), id];
102
+ }
103
+ if (visited.has(id)) {
104
+ return null;
105
+ }
106
+ visited.add(id);
107
+ path.push(id);
108
+ const deps = id === newId ? newDeps : Array.from(this.dependencyGraph.get(id) || []);
109
+ for (const depId of deps) {
110
+ const cycle = dfs(depId);
111
+ if (cycle)
112
+ return cycle;
113
+ }
114
+ path.pop();
115
+ return null;
116
+ };
117
+ return dfs(newId);
118
+ }
119
+ }
120
+ /**
121
+ * Create a new capability registry
122
+ */
123
+ export function createRegistry() {
124
+ return new InMemoryCapabilityRegistry();
125
+ }
126
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability Registry\n *\n * Registry for managing capability registration and lookup\n *\n * @module @pwfabric/runtime/registry\n */\n\nimport type {\n CapabilityId,\n Capability,\n RegistrationResult,\n RegistrationError,\n Resolution,\n ResolutionFailure,\n} from '@pwfabric/contracts'\n\n// ============================================================\n// REGISTRY INTERFACE\n// ============================================================\n\n/**\n * Capability registry for managing registered capabilities\n */\nexport interface CapabilityRegistry {\n /**\n * Register a capability\n */\n register<C extends Capability>(capability: C): RegistrationResult<C>\n\n /**\n * Unregister a capability\n */\n unregister(id: CapabilityId): boolean\n\n /**\n * Resolve a capability by ID\n */\n resolve<C extends Capability>(id: CapabilityId): Resolution<C>\n\n /**\n * Check if a capability is registered\n */\n has(id: CapabilityId): boolean\n\n /**\n * Get all registered capability IDs\n */\n list(): readonly CapabilityId[]\n\n /**\n * Get the count of registered capabilities\n */\n size(): number\n\n /**\n * Clear all registered capabilities\n */\n clear(): void\n}\n\n// ============================================================\n// IN-MEMORY REGISTRY IMPLEMENTATION\n// ============================================================\n\n/**\n * In-memory capability registry\n */\nexport class InMemoryCapabilityRegistry implements CapabilityRegistry {\n private readonly capabilities = new Map<CapabilityId, Capability>()\n private readonly dependencyGraph = new Map<CapabilityId, Set<CapabilityId>>()\n\n register<C extends Capability>(capability: C): RegistrationResult<C> {\n const { id, dependencies } = capability\n\n // Check if already registered\n if (this.capabilities.has(id)) {\n const error: RegistrationError = {\n type: 'already_registered',\n id,\n }\n return { success: false, error }\n }\n\n // Validate dependencies exist\n for (const depId of dependencies) {\n if (!this.capabilities.has(depId)) {\n const error: RegistrationError = {\n type: 'missing_dependency',\n id,\n missing: depId,\n }\n return { success: false, error }\n }\n }\n\n // Check for circular dependencies\n const cycle = this.detectCircularDependency(id, dependencies)\n if (cycle) {\n const error: RegistrationError = {\n type: 'circular_dependency',\n cycle,\n }\n return { success: false, error }\n }\n\n // Register the capability\n this.capabilities.set(id, capability)\n this.dependencyGraph.set(id, new Set(dependencies))\n\n return { success: true, capability }\n }\n\n unregister(id: CapabilityId): boolean {\n if (!this.capabilities.has(id)) {\n return false\n }\n\n // Check if other capabilities depend on this one\n for (const [, deps] of this.dependencyGraph) {\n if (deps.has(id)) {\n // Cannot unregister - something depends on it\n return false\n }\n }\n\n this.capabilities.delete(id)\n this.dependencyGraph.delete(id)\n return true\n }\n\n resolve<C extends Capability>(id: CapabilityId): Resolution<C> {\n const capability = this.capabilities.get(id)\n\n if (!capability) {\n const reason: ResolutionFailure = {\n type: 'not_found',\n id,\n }\n return { found: false, reason }\n }\n\n return { found: true, capability: capability as C }\n }\n\n has(id: CapabilityId): boolean {\n return this.capabilities.has(id)\n }\n\n list(): readonly CapabilityId[] {\n return Array.from(this.capabilities.keys())\n }\n\n size(): number {\n return this.capabilities.size\n }\n\n clear(): void {\n this.capabilities.clear()\n this.dependencyGraph.clear()\n }\n\n /**\n * Detect circular dependencies using DFS\n */\n private detectCircularDependency(\n newId: CapabilityId,\n newDeps: readonly CapabilityId[]\n ): readonly CapabilityId[] | null {\n const visited = new Set<CapabilityId>()\n const path: CapabilityId[] = []\n\n const dfs = (id: CapabilityId): readonly CapabilityId[] | null => {\n if (path.includes(id)) {\n // Found a cycle\n const cycleStart = path.indexOf(id)\n return [...path.slice(cycleStart), id]\n }\n\n if (visited.has(id)) {\n return null\n }\n\n visited.add(id)\n path.push(id)\n\n const deps = id === newId ? newDeps : Array.from(this.dependencyGraph.get(id) || [])\n for (const depId of deps) {\n const cycle = dfs(depId)\n if (cycle) return cycle\n }\n\n path.pop()\n return null\n }\n\n return dfs(newId)\n }\n}\n\n/**\n * Create a new capability registry\n */\nexport function createRegistry(): CapabilityRegistry {\n return new InMemoryCapabilityRegistry()\n}\n"]}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * BehaviorSurfaceProvider
3
+ *
4
+ * React provider that wraps a surface render session with a
5
+ * stable `SurfaceEventBus` instance and the real `surfaceId`.
6
+ *
7
+ * When `entityFormBindings` are supplied and an EntityFacade
8
+ * is available via EntityFacadeContext, automatically attaches form→entity
9
+ * bridges on mount and tears them down on unmount.
10
+ *
11
+ * All block children receive both values via context so they can emit events
12
+ * that carry a meaningful surface ID without prop-drilling.
13
+ *
14
+ * Lifecycle:
15
+ * - Emits `surface:load` on mount
16
+ * - Emits `surface:unload` on unmount
17
+ * - Re-creates the bus (and re-emits lifecycle events) when `surfaceId` changes
18
+ *
19
+ * @module @pwfabric/runtime/render/BehaviorSurfaceProvider
20
+ */
21
+ import * as React from 'react';
22
+ import type { SurfaceEventDescriptor } from '@pwfabric/contracts';
23
+ import type { SurfaceDataSourceDefinition } from './data-binding-types.js';
24
+ import type { FormEntityBridgeConfig } from '../forms/form-entity-bridge.js';
25
+ import type { Block } from '@pwfabric/core';
26
+ export interface BehaviorSurfaceProviderProps {
27
+ /** The ID of the surface being rendered */
28
+ readonly surfaceId: string;
29
+ /** Block render tree */
30
+ readonly children: React.ReactNode;
31
+ /**
32
+ * Declarative form→entity bridge configurations extracted
33
+ * from `surface.capabilities` (platform/entities config.formBindings).
34
+ * When provided and EntityFacade is available, bridges are auto-attached on
35
+ * mount and torn down on unmount.
36
+ */
37
+ readonly entityFormBindings?: readonly FormEntityBridgeConfig[];
38
+ /**
39
+ * The read sources, so a form's `sourceId` can be resolved to a destination.
40
+ * A source that names an entity type writes through the facade; anything
41
+ * else is an address and writes over HTTP.
42
+ */
43
+ readonly dataSources?: readonly SurfaceDataSourceDefinition[];
44
+ /**
45
+ * The surface renderer's single event handler. Subscribed to the bus via the
46
+ * global `*` wildcard so EVERY block-emitted event (with its full payload,
47
+ * incl. a declarative `action`) reaches the action dispatcher + host
48
+ * notification — the canonical path replacing the retired legacy
49
+ * `context.onEvent` shim that blocks used to call directly.
50
+ */
51
+ readonly onEvent?: (event: SurfaceEventDescriptor) => void;
52
+ /**
53
+ * The rendered (resolved) block tree, so a form's submission can be
54
+ * completed with the named controls inside it that `FormData` cannot see —
55
+ * read from page state by their `stateKey`.
56
+ */
57
+ readonly blocks?: readonly Block[];
58
+ }
59
+ /**
60
+ * Wraps a surface render tree with a scoped `SurfaceEventBus` and `surfaceId`.
61
+ *
62
+ * Place this as the outermost wrapper inside `SurfaceRenderer` so every block
63
+ * in the tree can call `useSurfaceEvents()` and `useSurfaceId()`.
64
+ */
65
+ export declare function BehaviorSurfaceProvider({ surfaceId, children, entityFormBindings, dataSources, onEvent, blocks, }: BehaviorSurfaceProviderProps): React.ReactElement;
@@ -0,0 +1,185 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * BehaviorSurfaceProvider
4
+ *
5
+ * React provider that wraps a surface render session with a
6
+ * stable `SurfaceEventBus` instance and the real `surfaceId`.
7
+ *
8
+ * When `entityFormBindings` are supplied and an EntityFacade
9
+ * is available via EntityFacadeContext, automatically attaches form→entity
10
+ * bridges on mount and tears them down on unmount.
11
+ *
12
+ * All block children receive both values via context so they can emit events
13
+ * that carry a meaningful surface ID without prop-drilling.
14
+ *
15
+ * Lifecycle:
16
+ * - Emits `surface:load` on mount
17
+ * - Emits `surface:unload` on unmount
18
+ * - Re-creates the bus (and re-emits lifecycle events) when `surfaceId` changes
19
+ *
20
+ * @module @pwfabric/runtime/render/BehaviorSurfaceProvider
21
+ */
22
+ import * as React from 'react';
23
+ import { SURFACE_EVENTS } from '@pwfabric/contracts';
24
+ import { SurfaceEventContext } from './surface-event-context.js';
25
+ import { SurfaceIdContext } from './surface-id-context.js';
26
+ import { createSurfaceEventBus } from './surface-event-bus.js';
27
+ import { useEntityFacade } from './entity-facade-context.js';
28
+ import { useOptionalDataService } from '../data/DataCapabilityProvider.js';
29
+ import { useSurfaceData } from './surface-data-context.js';
30
+ import { attachFormEntityBridge } from '../forms/form-entity-bridge.js';
31
+ import { resolveFormWriteTarget, toFormWriteError } from '../forms/form-write-target.js';
32
+ import { completeNamedControlValues } from '../forms/named-control-values.js';
33
+ /**
34
+ * Wraps a surface render tree with a scoped `SurfaceEventBus` and `surfaceId`.
35
+ *
36
+ * Place this as the outermost wrapper inside `SurfaceRenderer` so every block
37
+ * in the tree can call `useSurfaceEvents()` and `useSurfaceId()`.
38
+ */
39
+ export function BehaviorSurfaceProvider({ surfaceId, children, entityFormBindings, dataSources, onEvent, blocks, }) {
40
+ // Canonical event path: the renderer's handler is subscribed to the global `*`
41
+ // wildcard. Every block emits through the bus (useSurfaceEvents) → this handler
42
+ // dispatches declarative actions (setState/refresh) + forwards to the host.
43
+ // A ref keeps the subscription stable while always calling the latest handler.
44
+ const onEventRef = React.useRef(onEvent);
45
+ onEventRef.current = onEvent;
46
+ // Re-create the bus when surfaceId changes so sibling surfaces stay isolated.
47
+ //
48
+ // The `*` subscription is attached HERE, at construction, rather than in an
49
+ // effect. `bus.emit` is synchronous and unbuffered: an event emitted while
50
+ // `handlers.get('*')` is empty is dropped and never replayed. React runs
51
+ // effects CHILD-FIRST, so a subscription owned by this provider would attach
52
+ // only after every block's own mount effect had already run — meaning any
53
+ // block that emits from its mount effect had its event silently swallowed,
54
+ // deterministically. The same gap opened, more narrowly, for interactions
55
+ // landing between a commit and its passive-effect flush, which is what made
56
+ // `dispatch-actions.dom` / `entity-list-reactive-repro.dom` flaky.
57
+ //
58
+ // Subscribing during render is safe here because the bus is constructed in
59
+ // this same factory: nothing outside observes it before we return, so there
60
+ // is no external mutation and re-running the factory yields an independent
61
+ // bus. No explicit teardown is needed either — the handler is reachable only
62
+ // through the bus, so a discarded bus takes its listener with it.
63
+ const bus = React.useMemo(() => {
64
+ const created = createSurfaceEventBus();
65
+ created.on('*', (event) => onEventRef.current?.(event));
66
+ return created;
67
+ }, [surfaceId]);
68
+ React.useEffect(() => {
69
+ bus.emit({ type: SURFACE_EVENTS.SURFACE_LOAD, surfaceId, timestamp: Date.now() });
70
+ return () => {
71
+ bus.emit({ type: SURFACE_EVENTS.SURFACE_UNLOAD, surfaceId, timestamp: Date.now() });
72
+ };
73
+ }, [bus, surfaceId]);
74
+ // auto-wire form→entity bridges when EntityFacade is available.
75
+ //
76
+ // Attached during render, for the same reason the `*` handler above is: a
77
+ // bridge that subscribes from an effect is not listening yet while the blocks
78
+ // it serves are already interactive, and `bus.emit` drops what nobody is
79
+ // listening for. A form submitted in that window lost its create outright —
80
+ // the residual flake in `entity-list-reactive-repro.dom` after the `*` fix,
81
+ // where the wildcard handler saw `form:submit` but the bridge never did.
82
+ //
83
+ // Previous bridges are torn down before new ones attach, so re-running this
84
+ // (deps change, or a StrictMode double render) never stacks duplicates.
85
+ const entityFacade = useEntityFacade();
86
+ // OPTIONAL, because this provider also mounts on the behaviours-only path
87
+ // where no data machinery exists. A form pointed at a rest source there has
88
+ // nowhere to write, and says so, rather than the whole surface failing to
89
+ // render.
90
+ const dataService = useOptionalDataService();
91
+ // What the page has bound RIGHT NOW, read at write time through a ref so a
92
+ // fresh fetch never re-attaches the bridges. Null outside the data-bound
93
+ // path, where no record can have loaded and every write is a create.
94
+ const surfaceData = useSurfaceData();
95
+ const surfaceDataRef = React.useRef(surfaceData);
96
+ surfaceDataRef.current = surfaceData;
97
+ /**
98
+ * THE ONE PLACE THAT KNOWS WHAT A SOURCE ID MEANS.
99
+ *
100
+ * The bridge does not, deliberately — that is what lets one declaration
101
+ * serve our own store and a third party's endpoint without the form knowing
102
+ * which it is talking to.
103
+ *
104
+ * THE VERB FOLLOWS THE RECORD. A binding that says `PATCH` can only update
105
+ * a record the source has loaded; with none loaded the honest write is a
106
+ * create. Otherwise a Profile form would PATCH the collection path, the API
107
+ * would answer 404 "Route not found", and the visitor would read "check the
108
+ * highlighted fields".
109
+ */
110
+ const writer = React.useMemo(() => {
111
+ const byId = new Map((dataSources ?? []).map((s) => [s.id, s]));
112
+ return {
113
+ async write(sourceId, values, method) {
114
+ const source = byId.get(sourceId);
115
+ const target = resolveFormWriteTarget(method, surfaceDataRef.current?.[sourceId]);
116
+ if (source?.kind === 'entity' && source.entityType && entityFacade) {
117
+ const entityType = source.entityType;
118
+ return target.recordId
119
+ ? entityFacade.store.update(entityType, target.recordId, values)
120
+ : entityFacade.store.create(entityType, values);
121
+ }
122
+ if (!source) {
123
+ return {
124
+ success: false,
125
+ error: {
126
+ type: 'not_found',
127
+ message: `This form writes to "${sourceId}", which this page does not declare.`,
128
+ },
129
+ };
130
+ }
131
+ if (!dataService) {
132
+ return {
133
+ success: false,
134
+ error: {
135
+ type: 'connector',
136
+ message: `This form writes to "${sourceId}", but this page has no data layer to write through.`,
137
+ },
138
+ };
139
+ }
140
+ const res = await dataService.fetchData(sourceId, {
141
+ method: target.method,
142
+ body: values,
143
+ ...(target.recordId !== undefined ? { recordId: target.recordId } : {}),
144
+ });
145
+ if (!res.success)
146
+ return { success: false, error: toFormWriteError(res.error) };
147
+ const written = res.data;
148
+ const id = written && typeof written === 'object' ? written['id'] : undefined;
149
+ return {
150
+ success: true,
151
+ data: (written ?? {}),
152
+ id: (typeof id === 'string' || typeof id === 'number' ? String(id) : target.recordId ?? sourceId),
153
+ };
154
+ },
155
+ };
156
+ }, [dataSources, entityFacade, dataService]);
157
+ // Read at submit time through a ref, like the bound data, so a re-render
158
+ // never re-attaches the bridges.
159
+ const blocksRef = React.useRef(blocks);
160
+ blocksRef.current = blocks;
161
+ const completeValues = React.useCallback((formId, values) => {
162
+ const tree = blocksRef.current;
163
+ const state = surfaceDataRef.current?.['state'];
164
+ if (!tree || !state || typeof state !== 'object')
165
+ return values;
166
+ return completeNamedControlValues(tree, formId, values, state);
167
+ }, []);
168
+ const bridgeUnsubsRef = React.useRef([]);
169
+ React.useMemo(() => {
170
+ bridgeUnsubsRef.current.forEach((u) => u());
171
+ bridgeUnsubsRef.current = entityFormBindings?.length
172
+ ? entityFormBindings.map((cfg) => attachFormEntityBridge(bus, writer, cfg, completeValues))
173
+ : [];
174
+ }, [writer, bus, entityFormBindings, completeValues]);
175
+ // Detach on unmount. Keyed on nothing so it runs once per mount; the memo
176
+ // above owns teardown for every other transition.
177
+ React.useEffect(() => {
178
+ return () => {
179
+ bridgeUnsubsRef.current.forEach((u) => u());
180
+ bridgeUnsubsRef.current = [];
181
+ };
182
+ }, []);
183
+ return (_jsx(SurfaceEventContext.Provider, { value: bus, children: _jsx(SurfaceIdContext.Provider, { value: surfaceId, children: children }) }));
184
+ }
185
+ //# sourceMappingURL=BehaviorSurfaceProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BehaviorSurfaceProvider.js","sourceRoot":"","sources":["../../src/render/BehaviorSurfaceProvider.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * BehaviorSurfaceProvider\n *\n * React provider that wraps a surface render session with a\n * stable `SurfaceEventBus` instance and the real `surfaceId`.\n *\n * When `entityFormBindings` are supplied and an EntityFacade\n * is available via EntityFacadeContext, automatically attaches form→entity\n * bridges on mount and tears them down on unmount.\n *\n * All block children receive both values via context so they can emit events\n * that carry a meaningful surface ID without prop-drilling.\n *\n * Lifecycle:\n * - Emits `surface:load` on mount\n * - Emits `surface:unload` on unmount\n * - Re-creates the bus (and re-emits lifecycle events) when `surfaceId` changes\n *\n * @module @pwfabric/runtime/render/BehaviorSurfaceProvider\n */\n\nimport * as React from 'react'\nimport { SURFACE_EVENTS } from '@pwfabric/contracts'\nimport type { SurfaceEventDescriptor } from '@pwfabric/contracts'\nimport { SurfaceEventContext } from './surface-event-context.js'\nimport { SurfaceIdContext } from './surface-id-context.js'\nimport { createSurfaceEventBus } from './surface-event-bus.js'\nimport { useEntityFacade } from './entity-facade-context.js'\nimport { useOptionalDataService } from '../data/DataCapabilityProvider.js'\nimport { useSurfaceData } from './surface-data-context.js'\nimport { attachFormEntityBridge } from '../forms/form-entity-bridge.js'\nimport type { FormWriter } from '../forms/form-entity-bridge.js'\nimport { resolveFormWriteTarget, toFormWriteError } from '../forms/form-write-target.js'\nimport type { SurfaceDataSourceDefinition } from './data-binding-types.js'\nimport type { FormEntityBridgeConfig } from '../forms/form-entity-bridge.js'\nimport { completeNamedControlValues } from '../forms/named-control-values.js'\nimport type { Block } from '@pwfabric/core'\n\nexport interface BehaviorSurfaceProviderProps {\n /** The ID of the surface being rendered */\n readonly surfaceId: string\n /** Block render tree */\n readonly children: React.ReactNode\n /**\n * Declarative form→entity bridge configurations extracted\n * from `surface.capabilities` (platform/entities config.formBindings).\n * When provided and EntityFacade is available, bridges are auto-attached on\n * mount and torn down on unmount.\n */\n readonly entityFormBindings?: readonly FormEntityBridgeConfig[]\n /**\n * The read sources, so a form's `sourceId` can be resolved to a destination.\n * A source that names an entity type writes through the facade; anything\n * else is an address and writes over HTTP.\n */\n readonly dataSources?: readonly SurfaceDataSourceDefinition[]\n /**\n * The surface renderer's single event handler. Subscribed to the bus via the\n * global `*` wildcard so EVERY block-emitted event (with its full payload,\n * incl. a declarative `action`) reaches the action dispatcher + host\n * notification — the canonical path replacing the retired legacy\n * `context.onEvent` shim that blocks used to call directly.\n */\n readonly onEvent?: (event: SurfaceEventDescriptor) => void\n /**\n * The rendered (resolved) block tree, so a form's submission can be\n * completed with the named controls inside it that `FormData` cannot see —\n * read from page state by their `stateKey`.\n */\n readonly blocks?: readonly Block[]\n}\n\n/**\n * Wraps a surface render tree with a scoped `SurfaceEventBus` and `surfaceId`.\n *\n * Place this as the outermost wrapper inside `SurfaceRenderer` so every block\n * in the tree can call `useSurfaceEvents()` and `useSurfaceId()`.\n */\nexport function BehaviorSurfaceProvider({\n surfaceId,\n children,\n entityFormBindings,\n dataSources,\n onEvent,\n blocks,\n}: BehaviorSurfaceProviderProps): React.ReactElement {\n // Canonical event path: the renderer's handler is subscribed to the global `*`\n // wildcard. Every block emits through the bus (useSurfaceEvents) → this handler\n // dispatches declarative actions (setState/refresh) + forwards to the host.\n // A ref keeps the subscription stable while always calling the latest handler.\n const onEventRef = React.useRef(onEvent)\n onEventRef.current = onEvent\n\n // Re-create the bus when surfaceId changes so sibling surfaces stay isolated.\n //\n // The `*` subscription is attached HERE, at construction, rather than in an\n // effect. `bus.emit` is synchronous and unbuffered: an event emitted while\n // `handlers.get('*')` is empty is dropped and never replayed. React runs\n // effects CHILD-FIRST, so a subscription owned by this provider would attach\n // only after every block's own mount effect had already run — meaning any\n // block that emits from its mount effect had its event silently swallowed,\n // deterministically. The same gap opened, more narrowly, for interactions\n // landing between a commit and its passive-effect flush, which is what made\n // `dispatch-actions.dom` / `entity-list-reactive-repro.dom` flaky.\n //\n // Subscribing during render is safe here because the bus is constructed in\n // this same factory: nothing outside observes it before we return, so there\n // is no external mutation and re-running the factory yields an independent\n // bus. No explicit teardown is needed either — the handler is reachable only\n // through the bus, so a discarded bus takes its listener with it.\n const bus = React.useMemo(() => {\n const created = createSurfaceEventBus()\n created.on('*', (event) => onEventRef.current?.(event))\n return created\n }, [surfaceId])\n\n React.useEffect(() => {\n bus.emit({ type: SURFACE_EVENTS.SURFACE_LOAD, surfaceId, timestamp: Date.now() })\n return () => {\n bus.emit({ type: SURFACE_EVENTS.SURFACE_UNLOAD, surfaceId, timestamp: Date.now() })\n }\n }, [bus, surfaceId])\n\n // auto-wire form→entity bridges when EntityFacade is available.\n //\n // Attached during render, for the same reason the `*` handler above is: a\n // bridge that subscribes from an effect is not listening yet while the blocks\n // it serves are already interactive, and `bus.emit` drops what nobody is\n // listening for. A form submitted in that window lost its create outright —\n // the residual flake in `entity-list-reactive-repro.dom` after the `*` fix,\n // where the wildcard handler saw `form:submit` but the bridge never did.\n //\n // Previous bridges are torn down before new ones attach, so re-running this\n // (deps change, or a StrictMode double render) never stacks duplicates.\n const entityFacade = useEntityFacade()\n // OPTIONAL, because this provider also mounts on the behaviours-only path\n // where no data machinery exists. A form pointed at a rest source there has\n // nowhere to write, and says so, rather than the whole surface failing to\n // render.\n const dataService = useOptionalDataService()\n\n // What the page has bound RIGHT NOW, read at write time through a ref so a\n // fresh fetch never re-attaches the bridges. Null outside the data-bound\n // path, where no record can have loaded and every write is a create.\n const surfaceData = useSurfaceData()\n const surfaceDataRef = React.useRef(surfaceData)\n surfaceDataRef.current = surfaceData\n\n /**\n * THE ONE PLACE THAT KNOWS WHAT A SOURCE ID MEANS.\n *\n * The bridge does not, deliberately — that is what lets one declaration\n * serve our own store and a third party's endpoint without the form knowing\n * which it is talking to.\n *\n * THE VERB FOLLOWS THE RECORD. A binding that says `PATCH` can only update\n * a record the source has loaded; with none loaded the honest write is a\n * create. Otherwise a Profile form would PATCH the collection path, the API\n * would answer 404 \"Route not found\", and the visitor would read \"check the\n * highlighted fields\".\n */\n const writer = React.useMemo<FormWriter>(() => {\n const byId = new Map((dataSources ?? []).map((s) => [s.id, s]))\n return {\n async write(sourceId, values, method) {\n const source = byId.get(sourceId)\n const target = resolveFormWriteTarget(method, surfaceDataRef.current?.[sourceId])\n if (source?.kind === 'entity' && source.entityType && entityFacade) {\n const entityType = source.entityType as never\n return target.recordId\n ? entityFacade.store.update(entityType, target.recordId as never, values)\n : entityFacade.store.create(entityType, values)\n }\n if (!source) {\n return {\n success: false,\n error: {\n type: 'not_found',\n message: `This form writes to \"${sourceId}\", which this page does not declare.`,\n },\n }\n }\n if (!dataService) {\n return {\n success: false,\n error: {\n type: 'connector',\n message: `This form writes to \"${sourceId}\", but this page has no data layer to write through.`,\n },\n }\n }\n const res = await dataService.fetchData(sourceId, {\n method: target.method,\n body: values,\n ...(target.recordId !== undefined ? { recordId: target.recordId } : {}),\n })\n if (!res.success) return { success: false, error: toFormWriteError(res.error) }\n const written = res.data as Record<string, unknown> | null\n const id = written && typeof written === 'object' ? written['id'] : undefined\n return {\n success: true,\n data: (written ?? {}) as never,\n id: (typeof id === 'string' || typeof id === 'number' ? String(id) : target.recordId ?? sourceId) as never,\n }\n },\n }\n }, [dataSources, entityFacade, dataService])\n\n // Read at submit time through a ref, like the bound data, so a re-render\n // never re-attaches the bridges.\n const blocksRef = React.useRef(blocks)\n blocksRef.current = blocks\n const completeValues = React.useCallback(\n (formId: string, values: Record<string, unknown>): Record<string, unknown> => {\n const tree = blocksRef.current\n const state = surfaceDataRef.current?.['state']\n if (!tree || !state || typeof state !== 'object') return values\n return completeNamedControlValues(tree, formId, values, state as Record<string, unknown>)\n },\n []\n )\n\n const bridgeUnsubsRef = React.useRef<readonly (() => void)[]>([])\n React.useMemo(() => {\n bridgeUnsubsRef.current.forEach((u) => u())\n bridgeUnsubsRef.current = entityFormBindings?.length\n ? entityFormBindings.map((cfg) => attachFormEntityBridge(bus, writer, cfg, completeValues))\n : []\n }, [writer, bus, entityFormBindings, completeValues])\n\n // Detach on unmount. Keyed on nothing so it runs once per mount; the memo\n // above owns teardown for every other transition.\n React.useEffect(() => {\n return () => {\n bridgeUnsubsRef.current.forEach((u) => u())\n bridgeUnsubsRef.current = []\n }\n }, [])\n\n return (\n <SurfaceEventContext.Provider value={bus}>\n <SurfaceIdContext.Provider value={surfaceId}>{children}</SurfaceIdContext.Provider>\n </SurfaceEventContext.Provider>\n )\n}\n"]}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Block Error Boundary
3
+ *
4
+ * React class component that isolates block-level render crashes.
5
+ * If a single block throws during render, only that block shows
6
+ * a fallback UI — the rest of the surface stays intact.
7
+ *
8
+ * @module @pwfabric/runtime/render/BlockErrorBoundary
9
+ */
10
+ import * as React from 'react';
11
+ interface BlockErrorBoundaryProps {
12
+ readonly blockId: string;
13
+ readonly blockType: string;
14
+ readonly children: React.ReactNode;
15
+ }
16
+ interface BlockErrorBoundaryState {
17
+ readonly hasError: boolean;
18
+ readonly error: Error | null;
19
+ }
20
+ export declare class BlockErrorBoundary extends React.Component<BlockErrorBoundaryProps, BlockErrorBoundaryState> {
21
+ constructor(props: BlockErrorBoundaryProps);
22
+ static getDerivedStateFromError(error: Error): BlockErrorBoundaryState;
23
+ componentDidCatch(error: Error, info: React.ErrorInfo): void;
24
+ render(): React.ReactNode;
25
+ }
26
+ export {};
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Block Error Boundary
4
+ *
5
+ * React class component that isolates block-level render crashes.
6
+ * If a single block throws during render, only that block shows
7
+ * a fallback UI — the rest of the surface stays intact.
8
+ *
9
+ * @module @pwfabric/runtime/render/BlockErrorBoundary
10
+ */
11
+ import * as React from 'react';
12
+ export class BlockErrorBoundary extends React.Component {
13
+ constructor(props) {
14
+ super(props);
15
+ this.state = { hasError: false, error: null };
16
+ }
17
+ static getDerivedStateFromError(error) {
18
+ return { hasError: true, error };
19
+ }
20
+ componentDidCatch(error, info) {
21
+ console.error(`[PWFabric] Block render error: ${this.props.blockType}#${this.props.blockId}`, error, info.componentStack);
22
+ }
23
+ render() {
24
+ if (this.state.hasError) {
25
+ // Quiet-shape fallback: no visible copy — the earlier card leaked the
26
+ // raw exception message ("Cannot read properties of undefined…") to end
27
+ // users in a hardcoded light-red dev box. The error tint + dashed
28
+ // border signal "this section failed"; the technical message stays in
29
+ // the title tooltip and in the componentDidCatch console.error above,
30
+ // and data-block-error keeps it queryable for tests/tooling.
31
+ return (_jsx("div", { style: {
32
+ border: '2px dashed var(--pw-color-error, #ef4444)',
33
+ borderRadius: '8px',
34
+ backgroundColor: 'color-mix(in srgb, var(--pw-color-error, #ef4444) 8%, transparent)',
35
+ minHeight: '32px',
36
+ }, "data-block-error": this.props.blockId, "data-block-error-type": this.props.blockType, title: this.state.error?.message ?? 'Unknown error' }));
37
+ }
38
+ return this.props.children;
39
+ }
40
+ }
41
+ //# sourceMappingURL=BlockErrorBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockErrorBoundary.js","sourceRoot":"","sources":["../../src/render/BlockErrorBoundary.tsx"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,MAAM,OAAO,kBAAmB,SAAQ,KAAK,CAAC,SAG7C;IACC,YAAY,KAA8B;QACxC,KAAK,CAAC,KAAK,CAAC,CAAA;QACZ,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IAC/C,CAAC;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IAClC,CAAC;IAEQ,iBAAiB,CAAC,KAAY,EAAE,IAAqB;QAC5D,OAAO,CAAC,KAAK,CACX,kCAAkC,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAC9E,KAAK,EACL,IAAI,CAAC,cAAc,CACpB,CAAA;IACH,CAAC;IAEQ,MAAM;QACb,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACxB,sEAAsE;YACtE,wEAAwE;YACxE,kEAAkE;YAClE,sEAAsE;YACtE,sEAAsE;YACtE,6DAA6D;YAC7D,OAAO,CACL,cACE,KAAK,EAAE;oBACL,MAAM,EAAE,2CAA2C;oBACnD,YAAY,EAAE,KAAK;oBACnB,eAAe,EAAE,oEAAoE;oBACrF,SAAS,EAAE,MAAM;iBAClB,sBACiB,IAAI,CAAC,KAAK,CAAC,OAAO,2BACb,IAAI,CAAC,KAAK,CAAC,SAAS,EAC3C,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,eAAe,GACnD,CACH,CAAA;QACH,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;IAC5B,CAAC;CACF","sourcesContent":["/**\n * Block Error Boundary\n *\n * React class component that isolates block-level render crashes.\n * If a single block throws during render, only that block shows\n * a fallback UI — the rest of the surface stays intact.\n *\n * @module @pwfabric/runtime/render/BlockErrorBoundary\n */\n\nimport * as React from 'react'\n\ninterface BlockErrorBoundaryProps {\n readonly blockId: string\n readonly blockType: string\n readonly children: React.ReactNode\n}\n\ninterface BlockErrorBoundaryState {\n readonly hasError: boolean\n readonly error: Error | null\n}\n\nexport class BlockErrorBoundary extends React.Component<\n BlockErrorBoundaryProps,\n BlockErrorBoundaryState\n> {\n constructor(props: BlockErrorBoundaryProps) {\n super(props)\n this.state = { hasError: false, error: null }\n }\n\n static getDerivedStateFromError(error: Error): BlockErrorBoundaryState {\n return { hasError: true, error }\n }\n\n override componentDidCatch(error: Error, info: React.ErrorInfo): void {\n console.error(\n `[PWFabric] Block render error: ${this.props.blockType}#${this.props.blockId}`,\n error,\n info.componentStack\n )\n }\n\n override render(): React.ReactNode {\n if (this.state.hasError) {\n // Quiet-shape fallback: no visible copy — the earlier card leaked the\n // raw exception message (\"Cannot read properties of undefined…\") to end\n // users in a hardcoded light-red dev box. The error tint + dashed\n // border signal \"this section failed\"; the technical message stays in\n // the title tooltip and in the componentDidCatch console.error above,\n // and data-block-error keeps it queryable for tests/tooling.\n return (\n <div\n style={{\n border: '2px dashed var(--pw-color-error, #ef4444)',\n borderRadius: '8px',\n backgroundColor: 'color-mix(in srgb, var(--pw-color-error, #ef4444) 8%, transparent)',\n minHeight: '32px',\n }}\n data-block-error={this.props.blockId}\n data-block-error-type={this.props.blockType}\n title={this.state.error?.message ?? 'Unknown error'}\n />\n )\n }\n\n return this.props.children\n }\n}\n"]}