@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.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 (603) hide show
  1. package/LICENSE +90 -0
  2. package/README.md +13 -2
  3. package/dist/__tests__/dom-test-setup.d.ts +21 -0
  4. package/dist/__tests__/dom-test-setup.d.ts.map +1 -0
  5. package/dist/__tests__/dom-test-setup.js +36 -0
  6. package/dist/__tests__/dom-test-setup.js.map +1 -0
  7. package/dist/a11y/announcer.d.ts +66 -0
  8. package/dist/a11y/announcer.d.ts.map +1 -0
  9. package/dist/a11y/announcer.js +140 -0
  10. package/dist/a11y/announcer.js.map +1 -0
  11. package/dist/a11y/contrast.d.ts +55 -0
  12. package/dist/a11y/contrast.d.ts.map +1 -0
  13. package/dist/a11y/contrast.js +93 -0
  14. package/dist/a11y/contrast.js.map +1 -0
  15. package/dist/a11y/focus.d.ts +124 -0
  16. package/dist/a11y/focus.d.ts.map +1 -0
  17. package/dist/a11y/focus.js +282 -0
  18. package/dist/a11y/focus.js.map +1 -0
  19. package/dist/a11y/index.d.ts +22 -0
  20. package/dist/a11y/index.d.ts.map +1 -0
  21. package/dist/a11y/index.js +18 -0
  22. package/dist/a11y/index.js.map +1 -0
  23. package/dist/a11y/keyboard.d.ts +145 -0
  24. package/dist/a11y/keyboard.d.ts.map +1 -0
  25. package/dist/a11y/keyboard.js +317 -0
  26. package/dist/a11y/keyboard.js.map +1 -0
  27. package/dist/adapters/default-image.d.ts +26 -0
  28. package/dist/adapters/default-image.d.ts.map +1 -0
  29. package/dist/adapters/default-image.js +37 -0
  30. package/dist/adapters/default-image.js.map +1 -0
  31. package/dist/adapters/index.d.ts +28 -0
  32. package/dist/adapters/index.d.ts.map +1 -0
  33. package/dist/adapters/index.js +28 -0
  34. package/dist/adapters/index.js.map +1 -0
  35. package/dist/adapters/next-image.d.ts +39 -0
  36. package/dist/adapters/next-image.d.ts.map +1 -0
  37. package/dist/adapters/next-image.js +124 -0
  38. package/dist/adapters/next-image.js.map +1 -0
  39. package/dist/adapters/types.d.ts +62 -0
  40. package/dist/adapters/types.d.ts.map +1 -0
  41. package/dist/adapters/types.js +11 -0
  42. package/dist/adapters/types.js.map +1 -0
  43. package/dist/analytics/index.d.ts +15 -0
  44. package/dist/analytics/index.d.ts.map +1 -0
  45. package/dist/analytics/index.js +15 -0
  46. package/dist/analytics/index.js.map +1 -0
  47. package/dist/analytics/tracker.d.ts +66 -0
  48. package/dist/analytics/tracker.d.ts.map +1 -0
  49. package/dist/analytics/tracker.js +251 -0
  50. package/dist/analytics/tracker.js.map +1 -0
  51. package/dist/analytics/types.d.ts +131 -0
  52. package/dist/analytics/types.d.ts.map +1 -0
  53. package/dist/analytics/types.js +11 -0
  54. package/dist/analytics/types.js.map +1 -0
  55. package/dist/auth/index.d.ts +16 -0
  56. package/dist/auth/index.d.ts.map +1 -0
  57. package/dist/auth/index.js +14 -0
  58. package/dist/auth/index.js.map +1 -0
  59. package/dist/auth/provider.d.ts +133 -0
  60. package/dist/auth/provider.d.ts.map +1 -0
  61. package/dist/auth/provider.js +473 -0
  62. package/dist/auth/provider.js.map +1 -0
  63. package/dist/auth/types.d.ts +118 -0
  64. package/dist/auth/types.d.ts.map +1 -0
  65. package/dist/auth/types.js +9 -0
  66. package/dist/auth/types.js.map +1 -0
  67. package/dist/binding/collection.d.ts +191 -0
  68. package/dist/binding/collection.d.ts.map +1 -0
  69. package/dist/binding/collection.js +358 -0
  70. package/dist/binding/collection.js.map +1 -0
  71. package/dist/binding/datasource.d.ts +320 -0
  72. package/dist/binding/datasource.d.ts.map +1 -0
  73. package/dist/binding/datasource.js +556 -0
  74. package/dist/binding/datasource.js.map +1 -0
  75. package/dist/binding/engine.d.ts +66 -0
  76. package/dist/binding/engine.d.ts.map +1 -0
  77. package/dist/binding/engine.js +268 -0
  78. package/dist/binding/engine.js.map +1 -0
  79. package/dist/binding/expression.d.ts +151 -0
  80. package/dist/binding/expression.d.ts.map +1 -0
  81. package/dist/binding/expression.js +787 -0
  82. package/dist/binding/expression.js.map +1 -0
  83. package/dist/binding/index.d.ts +20 -0
  84. package/dist/binding/index.d.ts.map +1 -0
  85. package/dist/binding/index.js +14 -0
  86. package/dist/binding/index.js.map +1 -0
  87. package/dist/binding/two-way.d.ts +150 -0
  88. package/dist/binding/two-way.d.ts.map +1 -0
  89. package/dist/binding/two-way.js +462 -0
  90. package/dist/binding/two-way.js.map +1 -0
  91. package/dist/binding/types.d.ts +120 -0
  92. package/dist/binding/types.d.ts.map +1 -0
  93. package/dist/binding/types.js +9 -0
  94. package/dist/binding/types.js.map +1 -0
  95. package/dist/blocks/__test-fixtures__/block-fixtures.d.ts +40 -0
  96. package/dist/blocks/__test-fixtures__/block-fixtures.d.ts.map +1 -0
  97. package/dist/blocks/__test-fixtures__/block-fixtures.js +506 -0
  98. package/dist/blocks/__test-fixtures__/block-fixtures.js.map +1 -0
  99. package/dist/blocks/component-types.d.ts +82 -0
  100. package/dist/blocks/component-types.d.ts.map +1 -0
  101. package/dist/blocks/component-types.js +11 -0
  102. package/dist/blocks/component-types.js.map +1 -0
  103. package/dist/blocks/composition-rules.d.ts +169 -0
  104. package/dist/blocks/composition-rules.d.ts.map +1 -0
  105. package/dist/blocks/composition-rules.js +391 -0
  106. package/dist/blocks/composition-rules.js.map +1 -0
  107. package/dist/blocks/hooks.d.ts +32 -0
  108. package/dist/blocks/hooks.d.ts.map +1 -0
  109. package/dist/blocks/hooks.js +42 -0
  110. package/dist/blocks/hooks.js.map +1 -0
  111. package/dist/blocks/index.d.ts +16 -0
  112. package/dist/blocks/index.d.ts.map +1 -0
  113. package/dist/blocks/index.js +24 -0
  114. package/dist/blocks/index.js.map +1 -0
  115. package/dist/blocks/registry.d.ts +85 -0
  116. package/dist/blocks/registry.d.ts.map +1 -0
  117. package/dist/blocks/registry.js +136 -0
  118. package/dist/blocks/registry.js.map +1 -0
  119. package/dist/blocks/rule-engine.d.ts +132 -0
  120. package/dist/blocks/rule-engine.d.ts.map +1 -0
  121. package/dist/blocks/rule-engine.js +321 -0
  122. package/dist/blocks/rule-engine.js.map +1 -0
  123. package/dist/blocks/surface-event-context.d.ts +14 -0
  124. package/dist/blocks/surface-event-context.d.ts.map +1 -0
  125. package/dist/blocks/surface-event-context.js +14 -0
  126. package/dist/blocks/surface-event-context.js.map +1 -0
  127. package/dist/blocks/surface-id-context.d.ts +14 -0
  128. package/dist/blocks/surface-id-context.d.ts.map +1 -0
  129. package/dist/blocks/surface-id-context.js +15 -0
  130. package/dist/blocks/surface-id-context.js.map +1 -0
  131. package/dist/bus.d.ts +42 -0
  132. package/dist/bus.d.ts.map +1 -0
  133. package/dist/bus.js +158 -0
  134. package/dist/bus.js.map +1 -0
  135. package/dist/cache/index.d.ts +10 -0
  136. package/dist/cache/index.d.ts.map +1 -0
  137. package/dist/cache/index.js +10 -0
  138. package/dist/cache/index.js.map +1 -0
  139. package/dist/cache/memory.d.ts +33 -0
  140. package/dist/cache/memory.d.ts.map +1 -0
  141. package/dist/cache/memory.js +138 -0
  142. package/dist/cache/memory.js.map +1 -0
  143. package/dist/cache/types.d.ts +63 -0
  144. package/dist/cache/types.d.ts.map +1 -0
  145. package/dist/cache/types.js +9 -0
  146. package/dist/cache/types.js.map +1 -0
  147. package/dist/capability/index.d.ts +73 -0
  148. package/dist/capability/index.d.ts.map +1 -0
  149. package/dist/capability/index.js +87 -0
  150. package/dist/capability/index.js.map +1 -0
  151. package/dist/capability/kernel-capabilities.d.ts +32 -0
  152. package/dist/capability/kernel-capabilities.d.ts.map +1 -0
  153. package/dist/capability/kernel-capabilities.js +36 -0
  154. package/dist/capability/kernel-capabilities.js.map +1 -0
  155. package/dist/capability/platform-capabilities.d.ts +72 -0
  156. package/dist/capability/platform-capabilities.d.ts.map +1 -0
  157. package/dist/capability/platform-capabilities.js +267 -0
  158. package/dist/capability/platform-capabilities.js.map +1 -0
  159. package/dist/capability/type-mapping.d.ts +71 -0
  160. package/dist/capability/type-mapping.d.ts.map +1 -0
  161. package/dist/capability/type-mapping.js +107 -0
  162. package/dist/capability/type-mapping.js.map +1 -0
  163. package/dist/capability/validation.d.ts +47 -0
  164. package/dist/capability/validation.d.ts.map +1 -0
  165. package/dist/capability/validation.js +54 -0
  166. package/dist/capability/validation.js.map +1 -0
  167. package/dist/connectors/declared-connector.d.ts +49 -0
  168. package/dist/connectors/declared-connector.d.ts.map +1 -0
  169. package/dist/connectors/declared-connector.js +333 -0
  170. package/dist/connectors/declared-connector.js.map +1 -0
  171. package/dist/connectors/engine-registry.d.ts +51 -0
  172. package/dist/connectors/engine-registry.d.ts.map +1 -0
  173. package/dist/connectors/engine-registry.js +135 -0
  174. package/dist/connectors/engine-registry.js.map +1 -0
  175. package/dist/connectors/index.d.ts +15 -0
  176. package/dist/connectors/index.d.ts.map +1 -0
  177. package/dist/connectors/index.js +14 -0
  178. package/dist/connectors/index.js.map +1 -0
  179. package/dist/connectors/module-loader.d.ts +71 -0
  180. package/dist/connectors/module-loader.d.ts.map +1 -0
  181. package/dist/connectors/module-loader.js +143 -0
  182. package/dist/connectors/module-loader.js.map +1 -0
  183. package/dist/connectors/types.d.ts +175 -0
  184. package/dist/connectors/types.d.ts.map +1 -0
  185. package/dist/connectors/types.js +23 -0
  186. package/dist/connectors/types.js.map +1 -0
  187. package/dist/context.d.ts +28 -0
  188. package/dist/context.d.ts.map +1 -0
  189. package/dist/context.js +38 -0
  190. package/dist/context.js.map +1 -0
  191. package/dist/data/DataCapabilityProvider.d.ts +40 -0
  192. package/dist/data/DataCapabilityProvider.d.ts.map +1 -0
  193. package/dist/data/DataCapabilityProvider.js +58 -0
  194. package/dist/data/DataCapabilityProvider.js.map +1 -0
  195. package/dist/data/index.d.ts +18 -0
  196. package/dist/data/index.d.ts.map +1 -0
  197. package/dist/data/index.js +18 -0
  198. package/dist/data/index.js.map +1 -0
  199. package/dist/data/manager.d.ts +41 -0
  200. package/dist/data/manager.d.ts.map +1 -0
  201. package/dist/data/manager.js +442 -0
  202. package/dist/data/manager.js.map +1 -0
  203. package/dist/data/types.d.ts +219 -0
  204. package/dist/data/types.d.ts.map +1 -0
  205. package/dist/data/types.js +2 -0
  206. package/dist/data/types.js.map +1 -0
  207. package/dist/entity/aggregate.d.ts +9 -0
  208. package/dist/entity/aggregate.d.ts.map +1 -0
  209. package/dist/entity/aggregate.js +24 -0
  210. package/dist/entity/aggregate.js.map +1 -0
  211. package/dist/entity/apply-query.d.ts +59 -0
  212. package/dist/entity/apply-query.d.ts.map +1 -0
  213. package/dist/entity/apply-query.js +264 -0
  214. package/dist/entity/apply-query.js.map +1 -0
  215. package/dist/entity/behavior.d.ts +95 -0
  216. package/dist/entity/behavior.d.ts.map +1 -0
  217. package/dist/entity/behavior.js +78 -0
  218. package/dist/entity/behavior.js.map +1 -0
  219. package/dist/entity/connector.d.ts +142 -0
  220. package/dist/entity/connector.d.ts.map +1 -0
  221. package/dist/entity/connector.js +89 -0
  222. package/dist/entity/connector.js.map +1 -0
  223. package/dist/entity/connectors/index.d.ts +13 -0
  224. package/dist/entity/connectors/index.d.ts.map +1 -0
  225. package/dist/entity/connectors/index.js +11 -0
  226. package/dist/entity/connectors/index.js.map +1 -0
  227. package/dist/entity/connectors/static.d.ts +52 -0
  228. package/dist/entity/connectors/static.d.ts.map +1 -0
  229. package/dist/entity/connectors/static.js +245 -0
  230. package/dist/entity/connectors/static.js.map +1 -0
  231. package/dist/entity/entity-datasource.d.ts +51 -0
  232. package/dist/entity/entity-datasource.d.ts.map +1 -0
  233. package/dist/entity/entity-datasource.js +33 -0
  234. package/dist/entity/entity-datasource.js.map +1 -0
  235. package/dist/entity/facade.d.ts +71 -0
  236. package/dist/entity/facade.d.ts.map +1 -0
  237. package/dist/entity/facade.js +70 -0
  238. package/dist/entity/facade.js.map +1 -0
  239. package/dist/entity/http-connector.d.ts +124 -0
  240. package/dist/entity/http-connector.d.ts.map +1 -0
  241. package/dist/entity/http-connector.js +295 -0
  242. package/dist/entity/http-connector.js.map +1 -0
  243. package/dist/entity/index.d.ts +39 -0
  244. package/dist/entity/index.d.ts.map +1 -0
  245. package/dist/entity/index.js +31 -0
  246. package/dist/entity/index.js.map +1 -0
  247. package/dist/entity/postgres-connector.d.ts +65 -0
  248. package/dist/entity/postgres-connector.d.ts.map +1 -0
  249. package/dist/entity/postgres-connector.js +239 -0
  250. package/dist/entity/postgres-connector.js.map +1 -0
  251. package/dist/entity/registry.d.ts +60 -0
  252. package/dist/entity/registry.d.ts.map +1 -0
  253. package/dist/entity/registry.js +90 -0
  254. package/dist/entity/registry.js.map +1 -0
  255. package/dist/entity/store.d.ts +67 -0
  256. package/dist/entity/store.d.ts.map +1 -0
  257. package/dist/entity/store.js +349 -0
  258. package/dist/entity/store.js.map +1 -0
  259. package/dist/entity/types.d.ts +181 -0
  260. package/dist/entity/types.d.ts.map +1 -0
  261. package/dist/entity/types.js +10 -0
  262. package/dist/entity/types.js.map +1 -0
  263. package/dist/execution-context.d.ts +89 -0
  264. package/dist/execution-context.d.ts.map +1 -0
  265. package/dist/execution-context.js +146 -0
  266. package/dist/execution-context.js.map +1 -0
  267. package/dist/executor.d.ts +28 -0
  268. package/dist/executor.d.ts.map +1 -0
  269. package/dist/executor.js +33 -0
  270. package/dist/executor.js.map +1 -0
  271. package/dist/forms/form-entity-bridge.d.ts +95 -0
  272. package/dist/forms/form-entity-bridge.d.ts.map +1 -0
  273. package/dist/forms/form-entity-bridge.js +96 -0
  274. package/dist/forms/form-entity-bridge.js.map +1 -0
  275. package/dist/forms/form-write-target.d.ts +35 -0
  276. package/dist/forms/form-write-target.d.ts.map +1 -0
  277. package/dist/forms/form-write-target.js +81 -0
  278. package/dist/forms/form-write-target.js.map +1 -0
  279. package/dist/forms/index.d.ts +23 -0
  280. package/dist/forms/index.d.ts.map +1 -0
  281. package/dist/forms/index.js +25 -0
  282. package/dist/forms/index.js.map +1 -0
  283. package/dist/forms/named-control-values.d.ts +28 -0
  284. package/dist/forms/named-control-values.d.ts.map +1 -0
  285. package/dist/forms/named-control-values.js +42 -0
  286. package/dist/forms/named-control-values.js.map +1 -0
  287. package/dist/forms/processor.d.ts +56 -0
  288. package/dist/forms/processor.d.ts.map +1 -0
  289. package/dist/forms/processor.js +154 -0
  290. package/dist/forms/processor.js.map +1 -0
  291. package/dist/forms/types.d.ts +175 -0
  292. package/dist/forms/types.d.ts.map +1 -0
  293. package/dist/forms/types.js +33 -0
  294. package/dist/forms/types.js.map +1 -0
  295. package/dist/forms/validator.d.ts +62 -0
  296. package/dist/forms/validator.d.ts.map +1 -0
  297. package/dist/forms/validator.js +218 -0
  298. package/dist/forms/validator.js.map +1 -0
  299. package/dist/graph/index.d.ts +11 -0
  300. package/dist/graph/index.d.ts.map +1 -0
  301. package/dist/graph/index.js +9 -0
  302. package/dist/graph/index.js.map +1 -0
  303. package/dist/graph/resolver.d.ts +67 -0
  304. package/dist/graph/resolver.d.ts.map +1 -0
  305. package/dist/graph/resolver.js +243 -0
  306. package/dist/graph/resolver.js.map +1 -0
  307. package/dist/graph/types.d.ts +126 -0
  308. package/dist/graph/types.d.ts.map +1 -0
  309. package/dist/graph/types.js +9 -0
  310. package/dist/graph/types.js.map +1 -0
  311. package/dist/hooks/useInstalledAtoms.d.ts +97 -0
  312. package/dist/hooks/useInstalledAtoms.d.ts.map +1 -0
  313. package/dist/hooks/useInstalledAtoms.js +92 -0
  314. package/dist/hooks/useInstalledAtoms.js.map +1 -0
  315. package/dist/i18n/index.d.ts +16 -0
  316. package/dist/i18n/index.d.ts.map +1 -0
  317. package/dist/i18n/index.js +14 -0
  318. package/dist/i18n/index.js.map +1 -0
  319. package/dist/i18n/translator.d.ts +77 -0
  320. package/dist/i18n/translator.d.ts.map +1 -0
  321. package/dist/i18n/translator.js +357 -0
  322. package/dist/i18n/translator.js.map +1 -0
  323. package/dist/i18n/types.d.ts +94 -0
  324. package/dist/i18n/types.d.ts.map +1 -0
  325. package/dist/i18n/types.js +9 -0
  326. package/dist/i18n/types.js.map +1 -0
  327. package/dist/index.d.ts +65 -0
  328. package/dist/index.d.ts.map +1 -0
  329. package/dist/index.js +95 -0
  330. package/dist/index.js.map +1 -0
  331. package/dist/kernel.d.ts +48 -0
  332. package/dist/kernel.d.ts.map +1 -0
  333. package/dist/kernel.js +258 -0
  334. package/dist/kernel.js.map +1 -0
  335. package/dist/perf/budget.d.ts +93 -0
  336. package/dist/perf/budget.d.ts.map +1 -0
  337. package/dist/perf/budget.js +122 -0
  338. package/dist/perf/budget.js.map +1 -0
  339. package/dist/perf/index.d.ts +17 -0
  340. package/dist/perf/index.d.ts.map +1 -0
  341. package/dist/perf/index.js +13 -0
  342. package/dist/perf/index.js.map +1 -0
  343. package/dist/perf/render-timer.d.ts +91 -0
  344. package/dist/perf/render-timer.d.ts.map +1 -0
  345. package/dist/perf/render-timer.js +136 -0
  346. package/dist/perf/render-timer.js.map +1 -0
  347. package/dist/perf/reporter.d.ts +127 -0
  348. package/dist/perf/reporter.d.ts.map +1 -0
  349. package/dist/perf/reporter.js +241 -0
  350. package/dist/perf/reporter.js.map +1 -0
  351. package/dist/perf/web-vitals.d.ts +112 -0
  352. package/dist/perf/web-vitals.d.ts.map +1 -0
  353. package/dist/perf/web-vitals.js +268 -0
  354. package/dist/perf/web-vitals.js.map +1 -0
  355. package/dist/registry.d.ts +64 -0
  356. package/dist/registry.d.ts.map +1 -0
  357. package/dist/registry.js +126 -0
  358. package/dist/registry.js.map +1 -0
  359. package/dist/render/BehaviorSurfaceProvider.d.ts +66 -0
  360. package/dist/render/BehaviorSurfaceProvider.d.ts.map +1 -0
  361. package/dist/render/BehaviorSurfaceProvider.js +185 -0
  362. package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
  363. package/dist/render/BlockErrorBoundary.d.ts +27 -0
  364. package/dist/render/BlockErrorBoundary.d.ts.map +1 -0
  365. package/dist/render/BlockErrorBoundary.js +41 -0
  366. package/dist/render/BlockErrorBoundary.js.map +1 -0
  367. package/dist/render/DataBoundSurfaceRenderer.d.ts +79 -0
  368. package/dist/render/DataBoundSurfaceRenderer.d.ts.map +1 -0
  369. package/dist/render/DataBoundSurfaceRenderer.js +998 -0
  370. package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
  371. package/dist/render/DynamicBlockWrapper.d.ts +24 -0
  372. package/dist/render/DynamicBlockWrapper.d.ts.map +1 -0
  373. package/dist/render/DynamicBlockWrapper.js +117 -0
  374. package/dist/render/DynamicBlockWrapper.js.map +1 -0
  375. package/dist/render/appearance-resolver.d.ts +51 -0
  376. package/dist/render/appearance-resolver.d.ts.map +1 -0
  377. package/dist/render/appearance-resolver.js +78 -0
  378. package/dist/render/appearance-resolver.js.map +1 -0
  379. package/dist/render/capability-gate.d.ts +70 -0
  380. package/dist/render/capability-gate.d.ts.map +1 -0
  381. package/dist/render/capability-gate.js +94 -0
  382. package/dist/render/capability-gate.js.map +1 -0
  383. package/dist/render/conditional-states-css.d.ts +42 -0
  384. package/dist/render/conditional-states-css.d.ts.map +1 -0
  385. package/dist/render/conditional-states-css.js +82 -0
  386. package/dist/render/conditional-states-css.js.map +1 -0
  387. package/dist/render/connections-context.d.ts +32 -0
  388. package/dist/render/connections-context.d.ts.map +1 -0
  389. package/dist/render/connections-context.js +34 -0
  390. package/dist/render/connections-context.js.map +1 -0
  391. package/dist/render/dark-theme-css.d.ts +37 -0
  392. package/dist/render/dark-theme-css.d.ts.map +1 -0
  393. package/dist/render/dark-theme-css.js +67 -0
  394. package/dist/render/dark-theme-css.js.map +1 -0
  395. package/dist/render/data-binding-types.d.ts +192 -0
  396. package/dist/render/data-binding-types.d.ts.map +1 -0
  397. package/dist/render/data-binding-types.js +11 -0
  398. package/dist/render/data-binding-types.js.map +1 -0
  399. package/dist/render/data-cache.d.ts +31 -0
  400. package/dist/render/data-cache.d.ts.map +1 -0
  401. package/dist/render/data-cache.js +37 -0
  402. package/dist/render/data-cache.js.map +1 -0
  403. package/dist/render/derive-dark-theme.d.ts +18 -0
  404. package/dist/render/derive-dark-theme.d.ts.map +1 -0
  405. package/dist/render/derive-dark-theme.js +18 -0
  406. package/dist/render/derive-dark-theme.js.map +1 -0
  407. package/dist/render/dynamic-artifact-context.d.ts +78 -0
  408. package/dist/render/dynamic-artifact-context.d.ts.map +1 -0
  409. package/dist/render/dynamic-artifact-context.js +89 -0
  410. package/dist/render/dynamic-artifact-context.js.map +1 -0
  411. package/dist/render/dynamic-block-loader.d.ts +81 -0
  412. package/dist/render/dynamic-block-loader.d.ts.map +1 -0
  413. package/dist/render/dynamic-block-loader.js +197 -0
  414. package/dist/render/dynamic-block-loader.js.map +1 -0
  415. package/dist/render/endpoint.d.ts +19 -0
  416. package/dist/render/endpoint.d.ts.map +1 -0
  417. package/dist/render/endpoint.js +109 -0
  418. package/dist/render/endpoint.js.map +1 -0
  419. package/dist/render/entity-facade-context.d.ts +35 -0
  420. package/dist/render/entity-facade-context.d.ts.map +1 -0
  421. package/dist/render/entity-facade-context.js +37 -0
  422. package/dist/render/entity-facade-context.js.map +1 -0
  423. package/dist/render/group-states-css.d.ts +42 -0
  424. package/dist/render/group-states-css.d.ts.map +1 -0
  425. package/dist/render/group-states-css.js +73 -0
  426. package/dist/render/group-states-css.js.map +1 -0
  427. package/dist/render/hooks/useSurfaceEvents.d.ts +9 -0
  428. package/dist/render/hooks/useSurfaceEvents.d.ts.map +1 -0
  429. package/dist/render/hooks/useSurfaceEvents.js +9 -0
  430. package/dist/render/hooks/useSurfaceEvents.js.map +1 -0
  431. package/dist/render/hooks/useSurfaceId.d.ts +9 -0
  432. package/dist/render/hooks/useSurfaceId.d.ts.map +1 -0
  433. package/dist/render/hooks/useSurfaceId.js +9 -0
  434. package/dist/render/hooks/useSurfaceId.js.map +1 -0
  435. package/dist/render/index.d.ts +49 -0
  436. package/dist/render/index.d.ts.map +1 -0
  437. package/dist/render/index.js +61 -0
  438. package/dist/render/index.js.map +1 -0
  439. package/dist/render/interaction-rule-engine.d.ts +13 -0
  440. package/dist/render/interaction-rule-engine.d.ts.map +1 -0
  441. package/dist/render/interaction-rule-engine.js +47 -0
  442. package/dist/render/interaction-rule-engine.js.map +1 -0
  443. package/dist/render/keyframes-css.d.ts +32 -0
  444. package/dist/render/keyframes-css.d.ts.map +1 -0
  445. package/dist/render/keyframes-css.js +44 -0
  446. package/dist/render/keyframes-css.js.map +1 -0
  447. package/dist/render/query-state.d.ts +4 -0
  448. package/dist/render/query-state.d.ts.map +1 -0
  449. package/dist/render/query-state.js +70 -0
  450. package/dist/render/query-state.js.map +1 -0
  451. package/dist/render/renderer.d.ts +45 -0
  452. package/dist/render/renderer.d.ts.map +1 -0
  453. package/dist/render/renderer.js +937 -0
  454. package/dist/render/renderer.js.map +1 -0
  455. package/dist/render/resolve-expressions.d.ts +38 -0
  456. package/dist/render/resolve-expressions.d.ts.map +1 -0
  457. package/dist/render/resolve-expressions.js +309 -0
  458. package/dist/render/resolve-expressions.js.map +1 -0
  459. package/dist/render/responsive-style-css.d.ts +43 -0
  460. package/dist/render/responsive-style-css.d.ts.map +1 -0
  461. package/dist/render/responsive-style-css.js +123 -0
  462. package/dist/render/responsive-style-css.js.map +1 -0
  463. package/dist/render/responsive.d.ts +50 -0
  464. package/dist/render/responsive.d.ts.map +1 -0
  465. package/dist/render/responsive.js +99 -0
  466. package/dist/render/responsive.js.map +1 -0
  467. package/dist/render/scoped-css.d.ts +72 -0
  468. package/dist/render/scoped-css.d.ts.map +1 -0
  469. package/dist/render/scoped-css.js +102 -0
  470. package/dist/render/scoped-css.js.map +1 -0
  471. package/dist/render/shell-selection.d.ts +70 -0
  472. package/dist/render/shell-selection.d.ts.map +1 -0
  473. package/dist/render/shell-selection.js +117 -0
  474. package/dist/render/shell-selection.js.map +1 -0
  475. package/dist/render/states-css.d.ts +33 -0
  476. package/dist/render/states-css.d.ts.map +1 -0
  477. package/dist/render/states-css.js +63 -0
  478. package/dist/render/states-css.js.map +1 -0
  479. package/dist/render/surface-data-context.d.ts +19 -0
  480. package/dist/render/surface-data-context.d.ts.map +1 -0
  481. package/dist/render/surface-data-context.js +21 -0
  482. package/dist/render/surface-data-context.js.map +1 -0
  483. package/dist/render/surface-event-bus.d.ts +20 -0
  484. package/dist/render/surface-event-bus.d.ts.map +1 -0
  485. package/dist/render/surface-event-bus.js +68 -0
  486. package/dist/render/surface-event-bus.js.map +1 -0
  487. package/dist/render/surface-event-context.d.ts +12 -0
  488. package/dist/render/surface-event-context.d.ts.map +1 -0
  489. package/dist/render/surface-event-context.js +12 -0
  490. package/dist/render/surface-event-context.js.map +1 -0
  491. package/dist/render/surface-id-context.d.ts +9 -0
  492. package/dist/render/surface-id-context.d.ts.map +1 -0
  493. package/dist/render/surface-id-context.js +9 -0
  494. package/dist/render/surface-id-context.js.map +1 -0
  495. package/dist/render/theme-utils.d.ts +15 -0
  496. package/dist/render/theme-utils.d.ts.map +1 -0
  497. package/dist/render/theme-utils.js +15 -0
  498. package/dist/render/theme-utils.js.map +1 -0
  499. package/dist/render/types.d.ts +283 -0
  500. package/dist/render/types.d.ts.map +1 -0
  501. package/dist/render/types.js +9 -0
  502. package/dist/render/types.js.map +1 -0
  503. package/dist/render/visibility-css.d.ts +43 -0
  504. package/dist/render/visibility-css.d.ts.map +1 -0
  505. package/dist/render/visibility-css.js +119 -0
  506. package/dist/render/visibility-css.js.map +1 -0
  507. package/dist/render/width-scope.d.ts +35 -0
  508. package/dist/render/width-scope.d.ts.map +1 -0
  509. package/dist/render/width-scope.js +44 -0
  510. package/dist/render/width-scope.js.map +1 -0
  511. package/dist/render/write-only-sources.d.ts +34 -0
  512. package/dist/render/write-only-sources.d.ts.map +1 -0
  513. package/dist/render/write-only-sources.js +99 -0
  514. package/dist/render/write-only-sources.js.map +1 -0
  515. package/dist/routing/index.d.ts +15 -0
  516. package/dist/routing/index.d.ts.map +1 -0
  517. package/dist/routing/index.js +15 -0
  518. package/dist/routing/index.js.map +1 -0
  519. package/dist/routing/router.d.ts +43 -0
  520. package/dist/routing/router.d.ts.map +1 -0
  521. package/dist/routing/router.js +218 -0
  522. package/dist/routing/router.js.map +1 -0
  523. package/dist/routing/types.d.ts +125 -0
  524. package/dist/routing/types.d.ts.map +1 -0
  525. package/dist/routing/types.js +9 -0
  526. package/dist/routing/types.js.map +1 -0
  527. package/dist/seo/generator.d.ts +40 -0
  528. package/dist/seo/generator.d.ts.map +1 -0
  529. package/dist/seo/generator.js +236 -0
  530. package/dist/seo/generator.js.map +1 -0
  531. package/dist/seo/index.d.ts +15 -0
  532. package/dist/seo/index.d.ts.map +1 -0
  533. package/dist/seo/index.js +15 -0
  534. package/dist/seo/index.js.map +1 -0
  535. package/dist/seo/types.d.ts +182 -0
  536. package/dist/seo/types.d.ts.map +1 -0
  537. package/dist/seo/types.js +9 -0
  538. package/dist/seo/types.js.map +1 -0
  539. package/dist/state/index.d.ts +16 -0
  540. package/dist/state/index.d.ts.map +1 -0
  541. package/dist/state/index.js +16 -0
  542. package/dist/state/index.js.map +1 -0
  543. package/dist/state/manager.d.ts +34 -0
  544. package/dist/state/manager.d.ts.map +1 -0
  545. package/dist/state/manager.js +81 -0
  546. package/dist/state/manager.js.map +1 -0
  547. package/dist/state/store.d.ts +41 -0
  548. package/dist/state/store.d.ts.map +1 -0
  549. package/dist/state/store.js +145 -0
  550. package/dist/state/store.js.map +1 -0
  551. package/dist/state/types.d.ts +108 -0
  552. package/dist/state/types.d.ts.map +1 -0
  553. package/dist/state/types.js +9 -0
  554. package/dist/state/types.js.map +1 -0
  555. package/dist/surface/expression.d.ts +66 -0
  556. package/dist/surface/expression.d.ts.map +1 -0
  557. package/dist/surface/expression.js +179 -0
  558. package/dist/surface/expression.js.map +1 -0
  559. package/dist/surface/index.d.ts +33 -0
  560. package/dist/surface/index.d.ts.map +1 -0
  561. package/dist/surface/index.js +25 -0
  562. package/dist/surface/index.js.map +1 -0
  563. package/dist/surface/interpreter.d.ts +97 -0
  564. package/dist/surface/interpreter.d.ts.map +1 -0
  565. package/dist/surface/interpreter.js +231 -0
  566. package/dist/surface/interpreter.js.map +1 -0
  567. package/dist/surface/layout.d.ts +55 -0
  568. package/dist/surface/layout.d.ts.map +1 -0
  569. package/dist/surface/layout.js +288 -0
  570. package/dist/surface/layout.js.map +1 -0
  571. package/dist/surface/output.d.ts +91 -0
  572. package/dist/surface/output.d.ts.map +1 -0
  573. package/dist/surface/output.js +104 -0
  574. package/dist/surface/output.js.map +1 -0
  575. package/dist/surface/primitives.d.ts +181 -0
  576. package/dist/surface/primitives.d.ts.map +1 -0
  577. package/dist/surface/primitives.js +339 -0
  578. package/dist/surface/primitives.js.map +1 -0
  579. package/dist/surface/renderer.d.ts +97 -0
  580. package/dist/surface/renderer.d.ts.map +1 -0
  581. package/dist/surface/renderer.js +190 -0
  582. package/dist/surface/renderer.js.map +1 -0
  583. package/dist/surface/schemas.d.ts +182 -0
  584. package/dist/surface/schemas.d.ts.map +1 -0
  585. package/dist/surface/schemas.js +142 -0
  586. package/dist/surface/schemas.js.map +1 -0
  587. package/dist/surface/transforms.d.ts +145 -0
  588. package/dist/surface/transforms.d.ts.map +1 -0
  589. package/dist/surface/transforms.js +120 -0
  590. package/dist/surface/transforms.js.map +1 -0
  591. package/dist/surface/types.d.ts +161 -0
  592. package/dist/surface/types.d.ts.map +1 -0
  593. package/dist/surface/types.js +51 -0
  594. package/dist/surface/types.js.map +1 -0
  595. package/dist/surface/validation.d.ts +124 -0
  596. package/dist/surface/validation.d.ts.map +1 -0
  597. package/dist/surface/validation.js +147 -0
  598. package/dist/surface/validation.js.map +1 -0
  599. package/dist/validator.d.ts +56 -0
  600. package/dist/validator.d.ts.map +1 -0
  601. package/dist/validator.js +100 -0
  602. package/dist/validator.js.map +1 -0
  603. package/package.json +105 -3
@@ -0,0 +1,998 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Data-Bound Surface Renderer
4
+ *
5
+ * Wrapper component that resolves dataSources, state, and {{expression}} props
6
+ * before passing concrete values to the existing SurfaceRenderer.
7
+ *
8
+ * Architecture (post-refactor):
9
+ * Surface JSON (dataSources + state + {{expr}} in props)
10
+ * -> DataBoundSurfaceRenderer
11
+ * -> DataCapabilityProvider (creates DataService instance)
12
+ * -> DataBoundInner
13
+ * -> useSurfaceData: maps SurfaceDataSourceDefinition → DataSourceDefinition,
14
+ * registers with DataService, fetches via DataService, manages React state
15
+ * -> SurfaceRenderer (unchanged)
16
+ * -> Block components (unchanged)
17
+ *
18
+ * Backward compatible: if no dataSources, passes through with zero overhead.
19
+ *
20
+ * @module @pwfabric/runtime/render/DataBoundSurfaceRenderer
21
+ */
22
+ import * as React from 'react';
23
+ import { SurfaceRenderer } from './renderer.js';
24
+ import { resolveBlockExpressions } from './resolve-expressions.js';
25
+ import { endpointMissingPathValue, resolveEndpoint } from './endpoint.js';
26
+ import { resolveQueryState } from './query-state.js';
27
+ import { dataSourceCacheKey } from './data-cache.js';
28
+ import { DataCapabilityProvider, useDataService } from '../data/DataCapabilityProvider.js';
29
+ import { createEntityDataSource } from '../entity/entity-datasource.js';
30
+ import { aggregateThrough } from '../entity/aggregate.js';
31
+ import { SurfaceDataContext } from './surface-data-context.js';
32
+ import { PLATFORM_PERSISTENCE_CAPABILITY_ID } from '@pwfabric/contracts';
33
+ import { isCapability } from '../capability/type-mapping.js';
34
+ import { writeOnlySourceIds } from './write-only-sources.js';
35
+ // ============================================================
36
+ // extract dataSources + initialState from persistence config
37
+ // ============================================================
38
+ /**
39
+ * consolidated these two seeds under the platform/persistence
40
+ * capability config. DataBoundSurfaceRenderer is the sole consumer.
41
+ */
42
+ function extractPersistenceSeeds(surface) {
43
+ const binding = surface.capabilities?.find((c) => isCapability(c, PLATFORM_PERSISTENCE_CAPABILITY_ID));
44
+ const cfg = binding?.config;
45
+ return {
46
+ dataSources: cfg?.dataSources,
47
+ initialState: cfg?.initialState,
48
+ // Declared behaviors (shipped by a pattern / authored in JSON). The
49
+ // renderer merges these with host-provided behaviors (the host's store) so a pack
50
+ // can carry its own controls without the host knowing about them.
51
+ behaviors: cfg?.behaviors,
52
+ };
53
+ }
54
+ /**
55
+ * A source that could not be served but declares a `fallback`: the caller
56
+ * binds `data` AND records `message` under `__errors.<id>`, so the surface
57
+ * renders honestly (sample data + error flag) instead of empty or crashed.
58
+ */
59
+ class SourceFallback extends Error {
60
+ data;
61
+ constructor(message, data) {
62
+ super(message);
63
+ this.data = data;
64
+ this.name = 'SourceFallback';
65
+ }
66
+ }
67
+ /**
68
+ * A surface whose ONLY dynamic behaviour is writing (platform/entities
69
+ * formBindings, no persistence dataSources) still needs the full data-bound
70
+ * path: the form→entity bridge dispatches statusKeys through setState and the
71
+ * success/error UI hangs off `visibleWhen: "{{state.x}}"` — both live in
72
+ * DataBoundInner's state machinery. Before this check, such a surface fell
73
+ * into the zero-overhead passthrough where visibleWhen was never evaluated
74
+ * and, worse, submits were silently lost (proven live against a real
75
+ * SharePoint list: two submits on a formBindings-only page never reached the
76
+ * connector; adding any read source made the identical form work).
77
+ */
78
+ function hasEntityFormBindings(surface) {
79
+ const entityCap = surface.capabilities?.find((c) => isCapability(c, 'platform/entities'));
80
+ const formBindings = entityCap?.config
81
+ ?.formBindings;
82
+ return Array.isArray(formBindings) && formBindings.length > 0;
83
+ }
84
+ /**
85
+ * The actions a block's `action` prop names, in order.
86
+ *
87
+ * One action or a list of them (`[setState step, setState stepError '']`) —
88
+ * a wizard's "Next" has to move the step AND clear the previous step's error
89
+ * in one click. Anything that is not a surface action (a form's `action` is
90
+ * its submit URL string) names none.
91
+ */
92
+ export function blockActionsOf(value) {
93
+ if (Array.isArray(value))
94
+ return value.filter(isSurfaceAction);
95
+ return isSurfaceAction(value) ? [value] : [];
96
+ }
97
+ function blockTreeHasAction(blocks) {
98
+ if (!blocks)
99
+ return false;
100
+ for (const block of blocks) {
101
+ if (blockActionsOf(block.props?.action).length > 0)
102
+ return true;
103
+ if (blockTreeHasAction(block.children))
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+ /**
109
+ * Does any block carry a condition or a repeat the renderer has to evaluate?
110
+ *
111
+ * `visibleWhen`/`if` and `repeat` are resolved ONLY on the data-bound path.
112
+ * A static page — no source, no state, no action — takes this path too: on
113
+ * the passthrough a condition would never be read, so the shell's empty
114
+ * brand logo would draw as "⊟ <name> logo", and a thank-you gated on
115
+ * `{{state.submitted}}` would show before anything was submitted.
116
+ * An empty condition string is no condition, as in resolveBlock.
117
+ */
118
+ function blockTreeHasCondition(blocks) {
119
+ if (!blocks)
120
+ return false;
121
+ for (const block of blocks) {
122
+ const b = block;
123
+ for (const expr of [b.visibleWhen, b.if, b.repeat]) {
124
+ if (typeof expr === 'string' && expr.trim())
125
+ return true;
126
+ }
127
+ if (blockTreeHasCondition(block.children))
128
+ return true;
129
+ }
130
+ return false;
131
+ }
132
+ /**
133
+ * Does this surface need the data-bound path — state, expression resolution,
134
+ * the action dispatcher — or can it go straight to SurfaceRenderer?
135
+ *
136
+ * ONE ANSWER FOR EVERY HOST. The renderer's own passthrough check and embed's
137
+ * `needsDataBoundRenderer` used to disagree: embed counted `initialState`, the
138
+ * renderer did not. So a wizard page carrying nothing but `initialState` and
139
+ * buttons with `action` props reached the renderer, fell into the passthrough,
140
+ * and there `visibleWhen` was never evaluated, `repeat` never expanded and no
141
+ * `action` was ever dispatched — the buttons were dead. A block `action` alone
142
+ * needs the path too: it is dispatched nowhere else. So does a block
143
+ * `visibleWhen`/`if`/`repeat` (see blockTreeHasCondition).
144
+ *
145
+ * Host-provided behaviors are a prop, not part of the surface, and are
146
+ * checked by the renderer on top of this.
147
+ */
148
+ export function surfaceNeedsDataBinding(surface) {
149
+ const { dataSources, initialState, behaviors } = extractPersistenceSeeds(surface);
150
+ return (Boolean(dataSources && dataSources.length > 0) ||
151
+ initialState !== undefined ||
152
+ Boolean(behaviors && behaviors.length > 0) ||
153
+ hasEntityFormBindings(surface) ||
154
+ blockTreeHasAction(surface.blocks) ||
155
+ blockTreeHasCondition(surface.blocks));
156
+ }
157
+ // ============================================================
158
+ // SURFACE → RUNTIME SOURCE MAPPING
159
+ // ============================================================
160
+ /**
161
+ * Maps a JSON-safe SurfaceDataSourceDefinition to a runtime DataSourceDefinition.
162
+ * Returns null for 'state' kind (handled locally via React state, not DataService).
163
+ */
164
+ function mapSurfaceSourceToDefinition(source, currentState) {
165
+ switch (source.kind) {
166
+ case 'static':
167
+ return {
168
+ id: source.id,
169
+ kind: 'static',
170
+ staticData: source.staticData,
171
+ };
172
+ case 'state':
173
+ // State sources read from React state, not managed by DataService
174
+ return null;
175
+ case 'rest': {
176
+ if (!source.endpoint)
177
+ return null;
178
+ // `{{state.x}}` placeholders resolve against current state, each value
179
+ // URL-encoded in a mixed endpoint — see resolveEndpoint for why.
180
+ const endpoint = resolveEndpoint(source.endpoint, currentState);
181
+ return {
182
+ id: source.id,
183
+ kind: 'rest',
184
+ endpoint,
185
+ method: source.method ?? 'GET',
186
+ ...(source.headers ? { headers: source.headers } : {}),
187
+ // carry the identity the source names. This branch
188
+ // copied endpoint/method/headers and stopped, so a rest source could
189
+ // declare an identity the fetch would never see.
190
+ ...(source.authProviderRef ? { authProviderRef: source.authProviderRef } : {}),
191
+ // Same omission class as the line above: the source says where its
192
+ // list is and the definition has to carry it, or `fetchRest` hands the
193
+ // binding context a whole JSON body.
194
+ ...(source.returns !== undefined ? { returns: source.returns } : {}),
195
+ // The entity branch resolves `{{state.x}}` inside the query and hands
196
+ // it to the facade; a rest source declared the same query and lost it
197
+ // here. Resolved the same way, applied by the data service after the
198
+ // fetch.
199
+ ...(source.query
200
+ ? { query: resolveQueryState(source.query, currentState) }
201
+ : {}),
202
+ };
203
+ }
204
+ default:
205
+ return null;
206
+ }
207
+ }
208
+ /** JSON of a source DEFINITION, computed once per definition object. */
209
+ const definitionJsonCache = new WeakMap();
210
+ let unserialisable = 0;
211
+ function safeJson(value) {
212
+ try {
213
+ return JSON.stringify(value) ?? 'undefined';
214
+ }
215
+ catch {
216
+ // A value that cannot be serialised can never be proven unchanged.
217
+ return `#unserialisable-${++unserialisable}`;
218
+ }
219
+ }
220
+ function definitionJson(source) {
221
+ let json = definitionJsonCache.get(source);
222
+ if (json === undefined) {
223
+ json = safeJson(source);
224
+ definitionJsonCache.set(source, json);
225
+ }
226
+ return json;
227
+ }
228
+ /** A REST source whose required path value is empty is not fetched. */
229
+ function isIdleSource(source, state) {
230
+ return source.kind === 'rest' && Boolean(source.endpoint) && endpointMissingPathValue(source.endpoint, state);
231
+ }
232
+ /**
233
+ * EVERYTHING THAT DECIDES WHAT A SOURCE FETCHES, AS ONE STRING.
234
+ *
235
+ * The definition itself plus whatever it reads from state, RESOLVED: the REST
236
+ * address, the query with its `{{state.*}}` placeholders filled in, a state
237
+ * source's value. A source is refetched when — and only when — this changes.
238
+ *
239
+ * Refetching every source on every `setState` would make a search box writing
240
+ * `state.searchDraft` on each keystroke refetch the product list AND the
241
+ * cart, flashing both loading states, and a menu toggle refetch the whole
242
+ * page. Dependency tracking on the resolved request is exact where
243
+ * key-name extraction is not: `{{state.a ? state.b : state.c}}` depends on `c`
244
+ * only while `a` is false, and the resolved value knows that.
245
+ */
246
+ function requestKeyOf(source, state) {
247
+ const def = definitionJson(source);
248
+ switch (source.kind) {
249
+ case 'rest': {
250
+ if (!source.endpoint)
251
+ return def;
252
+ if (isIdleSource(source, state))
253
+ return `${def}|idle`;
254
+ const query = source.query ? safeJson(resolveQueryState(source.query, state)) : '';
255
+ return `${def}|${resolveEndpoint(source.endpoint, state)}|${query}`;
256
+ }
257
+ case 'entity': {
258
+ const query = source.query ? safeJson(resolveQueryState(source.query, state)) : '';
259
+ // The resolved aggregate too: a counter whose filter reads
260
+ // `{{state.status}}` must ask again when the status changes.
261
+ const aggregate = source.aggregate ? safeJson(resolveQueryState(source.aggregate, state)) : '';
262
+ return `${def}|${query}|${aggregate}`;
263
+ }
264
+ case 'state':
265
+ return `${def}|${safeJson(state[source.id])}`;
266
+ default:
267
+ return def;
268
+ }
269
+ }
270
+ function useSurfaceData(dataSources, initialState, interactionMode = 'live', entityFacade, connections, dataCache,
271
+ /** Sources only a form writes to (see write-only-sources): settled, never fetched. */
272
+ writeOnly) {
273
+ const dataService = useDataService();
274
+ const writeOnlyRef = React.useRef(writeOnly);
275
+ writeOnlyRef.current = writeOnly;
276
+ const dataCacheRef = React.useRef(dataCache);
277
+ dataCacheRef.current = dataCache;
278
+ // State store
279
+ const [surfaceState, setSurfaceState] = React.useState(() => ({
280
+ ...initialState,
281
+ }));
282
+ // Source data store — seeded from the host's cache when it has yesterday's
283
+ // rows for a source, so the first frame is not an empty table. The seed is
284
+ // marked loading: the fetch that follows is what makes it today's.
285
+ const [sourceData, setSourceData] = React.useState(() => {
286
+ if (!dataCache || !dataSources)
287
+ return {};
288
+ const seeded = {};
289
+ for (const source of dataSources) {
290
+ const key = dataSourceCacheKey(source, initialState ?? {});
291
+ if (!key)
292
+ continue;
293
+ const hit = dataCache.get(key);
294
+ if (hit !== undefined) {
295
+ seeded[source.id] = { data: hit, loading: true, error: null, key: requestKeyOf(source, initialState ?? {}) };
296
+ }
297
+ }
298
+ return seeded;
299
+ });
300
+ // Track whether initial fetch is complete. A page whose every fetched
301
+ // source came out of the cache counts as loaded: the gate has nothing to
302
+ // wait for that the visitor has not already seen.
303
+ const [initialLoaded, setInitialLoaded] = React.useState(() => {
304
+ if (!dataCache || !dataSources)
305
+ return false;
306
+ const fetched = dataSources.filter((s) => dataSourceCacheKey(s, initialState ?? {}) !== undefined);
307
+ return fetched.length > 0 && fetched.every((s) => dataCache.get(dataSourceCacheKey(s, initialState ?? {})) !== undefined);
308
+ });
309
+ // Stable ref to current state for use in fetch callbacks
310
+ const stateRef = React.useRef(surfaceState);
311
+ stateRef.current = surfaceState;
312
+ // Stable ref to sources
313
+ const sourcesRef = React.useRef(dataSources);
314
+ sourcesRef.current = dataSources;
315
+ // Stable ref to the entity facade (kind:'entity' sources query through it)
316
+ const entityFacadeRef = React.useRef(entityFacade);
317
+ entityFacadeRef.current = entityFacade;
318
+ // Stable ref to the connections context (kind:'entity'
319
+ // sources with a `connector` route through the engine registry)
320
+ const connectionsRef = React.useRef(connections);
321
+ connectionsRef.current = connections;
322
+ /** The request each source was last fetched (or marked idle) for. */
323
+ const requestKeysRef = React.useRef({});
324
+ /** The in-flight fetch of each source — a newer one aborts it. */
325
+ const controllersRef = React.useRef(new Map());
326
+ // ---- setState action ----
327
+ // Writing the value a key already holds changes nothing, so it renders
328
+ // nothing and fetches nothing.
329
+ const setStateValue = React.useCallback((key, value) => {
330
+ setSurfaceState((prev) => {
331
+ if (prev[key] === value)
332
+ return prev;
333
+ return { ...prev, [key]: value };
334
+ });
335
+ }, []);
336
+ // ---- (Re-)register a source's definition for the current state ----
337
+ // Registered even when the source is idle, so a form or a behavior writing
338
+ // to it through the data service still finds it.
339
+ const registerSource = React.useCallback((source, state) => {
340
+ if (dataService.hasSource(source.id))
341
+ dataService.unregisterSource(source.id);
342
+ const definition = mapSurfaceSourceToDefinition(source, state);
343
+ if (definition)
344
+ dataService.registerSource(definition);
345
+ }, [dataService]);
346
+ // ---- Fetch a single source ----
347
+ const fetchSource = React.useCallback(async (source, state, signal) => {
348
+ const failed = (err, status = null) => ({
349
+ kind: 'error',
350
+ message: err instanceof Error ? err.message : String(err),
351
+ status,
352
+ // fallback declared → bind it; the error stays visible next to it
353
+ ...(err instanceof SourceFallback ? { fallback: { data: err.data } } : {}),
354
+ });
355
+ // State sources read from React state directly
356
+ if (source.kind === 'state') {
357
+ return { kind: 'ok', data: state[source.id], status: null, meta: null };
358
+ }
359
+ // Entity sources list records via the platform/entities facade.
360
+ // Returns the items array so {{<id>}} binds directly to the list (e.g. a
361
+ // table's `data`). Refreshed by refreshAll after a form→entity create.
362
+ if (source.kind === 'entity') {
363
+ if (!source.entityType)
364
+ return { kind: 'ok', data: [], status: null, meta: null };
365
+ // A source that names a `connector` routes through the
366
+ // host-provided engine registry when that connection is registered
367
+ // there. Registered-but-failed surfaces the reason as an honest
368
+ // per-source error (`__errors.<id>`), never a crash; an unregistered
369
+ // connection (or no registry at all) falls through to today's
370
+ // default facade path.
371
+ // `{{state.x}}` inside the query resolves against CURRENT state, so
372
+ // a pagination block writing `state.page` or a segmented-control
373
+ // writing `state.severity` reaches `query.pagination` / `query.filter`.
374
+ // Before this, only REST endpoints saw state; connector queries went raw.
375
+ const query = source.query ? resolveQueryState(source.query, state) : undefined;
376
+ // An AGGREGATE source asks for one number, never the rows. Its
377
+ // filter resolves against state like `query` does, and the answer
378
+ // binds `{ fn, field?, value, count }` (`{{<id>.value}}`), exempt
379
+ // from the list wrap below. A counter on an intake collection is
380
+ // allowed what a list of it is not, and a public one no longer hands
381
+ // every row to a stranger so the page can count them.
382
+ const aggregate = source.aggregate
383
+ ? resolveQueryState(source.aggregate, state)
384
+ : undefined;
385
+ const entityType = source.entityType;
386
+ // Any failure below binds `fallback` (when declared) and still reports
387
+ // the error — see SourceFallback.
388
+ const fail = (message) => {
389
+ if (source.fallback !== undefined)
390
+ throw new SourceFallback(message, source.fallback);
391
+ throw new Error(message);
392
+ };
393
+ try {
394
+ const engines = connectionsRef.current?.engines;
395
+ if (source.connector && engines) {
396
+ const status = engines.getStatus(source.connector);
397
+ if (status.state !== 'unregistered') {
398
+ const engine = await engines.resolve(source.connector);
399
+ if (engine) {
400
+ if (aggregate) {
401
+ const agg = await aggregateThrough(engine, entityType, aggregate);
402
+ if (agg.success)
403
+ return { kind: 'ok', data: agg.result, status: null, meta: null };
404
+ return fail(agg.error);
405
+ }
406
+ const res = await engine.query(entityType, (query ?? {}));
407
+ if (res.success)
408
+ return { kind: 'ok', data: res.result.items, status: null, meta: null };
409
+ return fail(res.error);
410
+ }
411
+ const failedStatus = engines.getStatus(source.connector);
412
+ return fail(failedStatus.state === 'failed'
413
+ ? failedStatus.reason
414
+ : `Connector engine '${source.connector}' unavailable`);
415
+ }
416
+ // Declared connector, nothing registered: a pattern shipped without
417
+ // its connection. With a fallback this is the "sample data" path;
418
+ // without one, today's facade route stays.
419
+ if (source.fallback !== undefined) {
420
+ return fail(`Connector '${source.connector}' is not connected`);
421
+ }
422
+ }
423
+ const facade = entityFacadeRef.current;
424
+ if (!facade) {
425
+ return source.fallback !== undefined
426
+ ? fail('No entity facade available')
427
+ : {
428
+ kind: 'ok',
429
+ data: aggregate
430
+ ? { fn: aggregate.fn, ...(aggregate.field ? { field: aggregate.field } : {}), value: null, count: 0 }
431
+ : [],
432
+ status: null,
433
+ meta: null,
434
+ };
435
+ }
436
+ if (aggregate) {
437
+ if (typeof facade.resolveAggregate === 'function') {
438
+ // A count that cannot be served is an error, not a 0: bind
439
+ // `fallback` and report it, same as a failed list.
440
+ try {
441
+ const agg = await facade.resolveAggregate(entityType, aggregate);
442
+ return { kind: 'ok', data: agg, status: null, meta: null };
443
+ }
444
+ catch (err) {
445
+ return fail(err instanceof Error ? err.message : String(err));
446
+ }
447
+ }
448
+ // A facade from before `resolveAggregate` lists; the rows it
449
+ // returns are reduced here, same as a list-only engine.
450
+ const viaList = await aggregateThrough({ query: async (t, q) => ({ success: true, result: await facade.resolveBinding(t, q) }) }, entityType, aggregate);
451
+ if (!viaList.success)
452
+ return fail(viaList.error);
453
+ return { kind: 'ok', data: viaList.result, status: null, meta: null };
454
+ }
455
+ const eds = createEntityDataSource(facade, {
456
+ id: source.id,
457
+ entityType: source.entityType,
458
+ ...(query ? { query } : {}),
459
+ });
460
+ try {
461
+ const result = await eds.fetch();
462
+ return { kind: 'ok', data: result.items, status: null, meta: null };
463
+ }
464
+ catch (err) {
465
+ return fail(err instanceof Error ? err.message : String(err));
466
+ }
467
+ }
468
+ catch (err) {
469
+ return failed(err);
470
+ }
471
+ }
472
+ // Everything else goes through the DataService, registered for THIS state.
473
+ registerSource(source, state);
474
+ const result = await dataService.fetchData(source.id, {
475
+ signal,
476
+ ...(source.headers ? { headers: source.headers } : {}),
477
+ });
478
+ if (result.success) {
479
+ return {
480
+ kind: 'ok',
481
+ data: result.data,
482
+ status: result.metadata.status ?? null,
483
+ meta: result.metadata.envelope ?? null,
484
+ };
485
+ }
486
+ return failed(new Error(result.error.message), result.error.status ?? null);
487
+ }, [dataService, registerSource]);
488
+ // ---- Fetch a set of sources for the current state ----
489
+ const fetchSources = React.useCallback(async (sources) => {
490
+ const state = stateRef.current;
491
+ const idle = [];
492
+ const writeOnlySettled = [];
493
+ const jobs = [];
494
+ for (const source of sources) {
495
+ controllersRef.current.get(source.id)?.abort();
496
+ controllersRef.current.delete(source.id);
497
+ const key = requestKeyOf(source, state);
498
+ requestKeysRef.current[source.id] = key;
499
+ if (isIdleSource(source, state)) {
500
+ idle.push(source);
501
+ registerSource(source, state);
502
+ continue;
503
+ }
504
+ // A form's write target nothing reads: no list request — not on
505
+ // mount, not after a submit's refreshAll. Still registered, so the
506
+ // form's write through the data service finds it.
507
+ if (writeOnlyRef.current?.has(source.id) === true) {
508
+ writeOnlySettled.push(source);
509
+ registerSource(source, state);
510
+ continue;
511
+ }
512
+ const controller = new AbortController();
513
+ controllersRef.current.set(source.id, controller);
514
+ jobs.push({ source, controller, key });
515
+ }
516
+ // Idle sources settle at once: no data, not loading, no error. The data
517
+ // of whatever was shown before belongs to another request, so it goes.
518
+ // Fetched sources show their loading flag and keep their data until the
519
+ // answer arrives (a filter change does not blank the list meanwhile).
520
+ setSourceData((prev) => {
521
+ const next = { ...prev };
522
+ for (const source of idle) {
523
+ next[source.id] = { data: null, loading: false, error: null, status: null, meta: null, idle: true, key: requestKeysRef.current[source.id] };
524
+ }
525
+ for (const source of writeOnlySettled) {
526
+ next[source.id] = { data: null, loading: false, error: null, status: null, meta: null, key: requestKeysRef.current[source.id] };
527
+ }
528
+ for (const { source } of jobs) {
529
+ const p = prev[source.id];
530
+ next[source.id] = {
531
+ data: p?.data ?? null,
532
+ loading: true,
533
+ error: null,
534
+ status: p?.status ?? null,
535
+ meta: p?.meta ?? null,
536
+ ...(p?.key !== undefined ? { key: p.key } : {}),
537
+ };
538
+ }
539
+ return next;
540
+ });
541
+ await Promise.all(jobs.map(async ({ source, controller, key }) => {
542
+ let outcome;
543
+ try {
544
+ outcome = await fetchSource(source, state, controller.signal);
545
+ }
546
+ catch (err) {
547
+ outcome = { kind: 'error', message: err instanceof Error ? err.message : String(err), status: null };
548
+ }
549
+ if (controller.signal.aborted)
550
+ return;
551
+ controllersRef.current.delete(source.id);
552
+ setSourceData((prev) => {
553
+ const next = { ...prev };
554
+ if (outcome.kind === 'ok') {
555
+ next[source.id] = { data: outcome.data, loading: false, error: null, status: outcome.status, meta: outcome.meta, key };
556
+ }
557
+ else {
558
+ const p = prev[source.id];
559
+ // ON ERROR, DATA FROM ANOTHER REQUEST IS CLEARED: a search
560
+ // for cart 999 must not leave cart 1 on screen under the error.
561
+ // The SAME request failing again (a refresh, a poll) keeps its
562
+ // last good answer — that answer is still the right record.
563
+ const keep = p?.key === key;
564
+ next[source.id] = {
565
+ data: outcome.fallback ? outcome.fallback.data : keep ? (p?.data ?? null) : null,
566
+ loading: false,
567
+ error: outcome.message,
568
+ status: outcome.status,
569
+ meta: keep ? (p?.meta ?? null) : null,
570
+ ...(keep ? { key } : {}),
571
+ };
572
+ }
573
+ return next;
574
+ });
575
+ if (outcome.kind === 'ok') {
576
+ const cacheKey = dataSourceCacheKey(source, state);
577
+ if (cacheKey)
578
+ dataCacheRef.current?.set(cacheKey, outcome.data);
579
+ }
580
+ }));
581
+ // A pass whose every fetch was superseded settles nothing; the pass
582
+ // that superseded it will.
583
+ if (jobs.length > 0 && jobs.every((j) => j.controller.signal.aborted))
584
+ return;
585
+ setInitialLoaded(true);
586
+ }, [fetchSource, registerSource]);
587
+ // ---- Fetch every source (refreshAll) ----
588
+ const fetchAllSources = React.useCallback(async () => {
589
+ const sources = sourcesRef.current;
590
+ if (!sources || sources.length === 0) {
591
+ setInitialLoaded(true);
592
+ return;
593
+ }
594
+ await fetchSources(sources);
595
+ }, [fetchSources]);
596
+ // ---- Refresh a single source ----
597
+ const refreshSource = React.useCallback(async (sourceId) => {
598
+ const source = sourcesRef.current?.find((s) => s.id === sourceId);
599
+ if (!source)
600
+ return;
601
+ await fetchSources([source]);
602
+ }, [fetchSources]);
603
+ // ---- Initial fetch + refetch of the sources whose request changed ----
604
+ React.useEffect(() => {
605
+ const sources = dataSources ?? [];
606
+ const present = new Set(sources.map((s) => s.id));
607
+ for (const id of Object.keys(requestKeysRef.current)) {
608
+ if (present.has(id))
609
+ continue;
610
+ delete requestKeysRef.current[id];
611
+ controllersRef.current.get(id)?.abort();
612
+ controllersRef.current.delete(id);
613
+ if (dataService.hasSource(id))
614
+ dataService.unregisterSource(id);
615
+ }
616
+ if (sources.length === 0) {
617
+ setInitialLoaded(true);
618
+ return;
619
+ }
620
+ const changed = sources.filter((s) => requestKeyOf(s, surfaceState) !== requestKeysRef.current[s.id]);
621
+ if (changed.length > 0)
622
+ void fetchSources(changed);
623
+ }, [dataSources, surfaceState, fetchSources, dataService]);
624
+ // ---- Unmount: abort what is in flight, forget what was fetched ----
625
+ // Forgetting matters under StrictMode's mount → unmount → mount: the second
626
+ // mount must fetch again, not believe the aborted first pass succeeded.
627
+ React.useEffect(() => {
628
+ const controllers = controllersRef.current;
629
+ return () => {
630
+ for (const controller of controllers.values())
631
+ controller.abort();
632
+ controllers.clear();
633
+ requestKeysRef.current = {};
634
+ for (const source of sourcesRef.current ?? []) {
635
+ if (dataService.hasSource(source.id))
636
+ dataService.unregisterSource(source.id);
637
+ }
638
+ };
639
+ }, [dataService]);
640
+ // ---- Auto-refresh intervals ----
641
+ React.useEffect(() => {
642
+ // Inert (editor/preview): never start auto-refresh timers.
643
+ if (!dataSources || interactionMode === 'inert')
644
+ return;
645
+ const intervals = [];
646
+ for (const source of dataSources) {
647
+ if (source.refreshInterval && source.refreshInterval > 0) {
648
+ const interval = setInterval(() => {
649
+ void refreshSource(source.id);
650
+ }, source.refreshInterval);
651
+ intervals.push(interval);
652
+ }
653
+ }
654
+ return () => {
655
+ for (const interval of intervals) {
656
+ clearInterval(interval);
657
+ }
658
+ };
659
+ }, [dataSources, refreshSource, interactionMode]);
660
+ // ---- Build expression context ----
661
+ const expressionData = React.useMemo(() => {
662
+ const ctx = { state: surfaceState };
663
+ // Per-source loading flags for skeleton UIs: `{{__loading.<sourceId>}}` /
664
+ // `{{__anyLoading}}`. A declared source with no sourceData entry yet has
665
+ // not even started its first fetch (the fetch effect runs after first
666
+ // paint) — report it as loading so a skeleton shows from frame one
667
+ // instead of flashing the block's empty state. `state`-kind sources read
668
+ // synchronously from React state and are never "loading".
669
+ const loadingMap = {};
670
+ let anyLoading = false;
671
+ // Per-source error messages — `{{__errors.<sourceId>}}` — so a block can
672
+ // distinguish "fetch failed" from "fetched, genuinely empty" instead of
673
+ // showing a false empty-state. The value is the error MESSAGE string
674
+ // (truthy check + tooltip content in one binding); null when healthy.
675
+ const errorMap = {};
676
+ let anyError = false;
677
+ // `{{__status.<id>}}` — the HTTP status of the last answer (200, 404, …);
678
+ // null for a network failure, an idle source or a non-HTTP kind.
679
+ // `{{__notFound.<id>}}` — status 404, so "no such order" and "connection
680
+ // error" can be told apart. `{{__idle.<id>}}` — a REST source
681
+ // not fetched because a required path value is empty.
682
+ // `{{__meta.<id>}}` — the envelope `returns` unwrapped the list from
683
+ // (`total`, `skip`, `limit` …); null when there is none.
684
+ const statusMap = {};
685
+ const notFoundMap = {};
686
+ const idleMap = {};
687
+ const metaMap = {};
688
+ for (const source of dataSources ?? []) {
689
+ const entry = sourceData[source.id];
690
+ const loading = source.kind === 'state' || writeOnly?.has(source.id) === true ? false : entry ? entry.loading : true;
691
+ loadingMap[source.id] = loading;
692
+ if (loading)
693
+ anyLoading = true;
694
+ const error = entry?.error ?? null;
695
+ errorMap[source.id] = error;
696
+ if (error)
697
+ anyError = true;
698
+ const status = entry?.status ?? null;
699
+ statusMap[source.id] = status;
700
+ notFoundMap[source.id] = status === 404;
701
+ idleMap[source.id] = entry?.idle === true;
702
+ metaMap[source.id] = entry?.meta ?? null;
703
+ }
704
+ /**
705
+ * A LIST SOURCE ALWAYS BINDS AS A LIST.
706
+ *
707
+ * The entity path returns `result.items` — an array. The rest path
708
+ * returned the whole JSON body, or, since `returns` landed, the plucked
709
+ * value. So the same block bound to `{{data.Product}}` received an array
710
+ * from our store and an object from anyone else's, and swapping a model
711
+ * from platform to a connection quietly changed what every bound block
712
+ * got. One shape.
713
+ *
714
+ * `static` and `state` are the documented exceptions and stay untouched:
715
+ * a state source is routinely a scalar, and wrapping it would break every
716
+ * shipped surface that reads one.
717
+ */
718
+ const LIST_KINDS = new Set(['rest', 'entity', 'graphql']);
719
+ const kindById = new Map((dataSources ?? []).map((s) => [s.id, s.kind]));
720
+ // An AGGREGATE source is one number, not a list of one: wrapping
721
+ // `{ value }` into `[{ value }]` would still read through the single-
722
+ // element fallthrough, but `.length` would lie and `count` would hide.
723
+ // Entity-only: that is the one branch that reads `aggregate`; a rest
724
+ // source carrying the key still fetches a list and binds as one.
725
+ const aggregateIds = new Set((dataSources ?? []).filter((s) => s.kind === 'entity' && s.aggregate !== undefined).map((s) => s.id));
726
+ for (const [key, value] of Object.entries(sourceData)) {
727
+ const listy = LIST_KINDS.has(kindById.get(key) ?? '') && !aggregateIds.has(key);
728
+ ctx[key] =
729
+ listy && value.data != null && !Array.isArray(value.data) ? [value.data] : value.data;
730
+ }
731
+ ctx['__loading'] = loadingMap;
732
+ ctx['__anyLoading'] = anyLoading;
733
+ ctx['__errors'] = errorMap;
734
+ ctx['__anyError'] = anyError;
735
+ ctx['__status'] = statusMap;
736
+ ctx['__notFound'] = notFoundMap;
737
+ ctx['__idle'] = idleMap;
738
+ ctx['__meta'] = metaMap;
739
+ return ctx;
740
+ }, [surfaceState, sourceData, dataSources, writeOnly]);
741
+ return {
742
+ expressionData,
743
+ surfaceState,
744
+ initialLoaded,
745
+ setStateValue,
746
+ refreshSource,
747
+ fetchAllSources,
748
+ };
749
+ }
750
+ // ============================================================
751
+ // canonicalizeEventType
752
+ // ============================================================
753
+ /**
754
+ * Normalize a stored behavior trigger string to canonical colon form.
755
+ * - Already has `:` → return lowercased/trimmed as-is
756
+ * - Has `.` → replace first `.` with `:`
757
+ * - Otherwise → return trimmed lowercased (best-effort; won't match well-formed events)
758
+ */
759
+ export function canonicalizeEventType(raw) {
760
+ const s = raw.trim().toLowerCase();
761
+ if (s.includes(':'))
762
+ return s;
763
+ if (s.includes('.'))
764
+ return s.replace('.', ':');
765
+ return s;
766
+ }
767
+ // ============================================================
768
+ // isSurfaceAction
769
+ // ============================================================
770
+ export function isSurfaceAction(value) {
771
+ if (!value || typeof value !== 'object')
772
+ return false;
773
+ const obj = value;
774
+ return (obj.type === 'setState' ||
775
+ obj.type === 'refresh' ||
776
+ obj.type === 'refreshAll' ||
777
+ obj.type === 'showBlock' ||
778
+ obj.type === 'hideBlock' ||
779
+ obj.type === 'navigate');
780
+ }
781
+ /**
782
+ * Guard the `navigate` action against XSS: only http(s) absolute URLs and
783
+ * relative/anchor URLs may be navigated to. A scheme like `javascript:`,
784
+ * `data:`, or `vbscript:` is rejected so a stored behavior cannot execute
785
+ * script in a published-surface visitor's context. Control characters are
786
+ * stripped first to defeat `java\tscript:`-style scheme smuggling.
787
+ */
788
+ export function isSafeNavigateUrl(url) {
789
+ if (typeof url !== 'string')
790
+ return false;
791
+ const stripped = url.replace(/[^!-~]/g, '');
792
+ if (stripped === '')
793
+ return false;
794
+ const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(stripped);
795
+ if (!scheme)
796
+ return true; // relative path / query / hash / protocol-relative
797
+ const s = (scheme[1] ?? '').toLowerCase();
798
+ return s === 'http' || s === 'https';
799
+ }
800
+ // ============================================================
801
+ // DataBoundSurfaceRenderer COMPONENT
802
+ // ============================================================
803
+ /**
804
+ * Data-bound surface renderer.
805
+ *
806
+ * If nothing on the surface is dynamic (see surfaceNeedsDataBinding), renders
807
+ * SurfaceRenderer directly (zero overhead).
808
+ * Otherwise, wraps with DataCapabilityProvider for DataService context, then
809
+ * fetches data, manages state, resolves expressions, and intercepts events.
810
+ */
811
+ export function DataBoundSurfaceRenderer(props) {
812
+ const { surface, onEvent, behaviors: hostBehaviors, onNavigateAction, dataServiceConfig, ...rest } = props;
813
+ const { blocks, appearance, capabilities } = surface;
814
+ // dataSources + initialState + declared behaviors live under platform/persistence config
815
+ const { dataSources, initialState, behaviors: declaredBehaviors } = extractPersistenceSeeds(surface);
816
+ // Declared (JSON) first, host (its store) after — same id in both: host wins.
817
+ const behaviors = React.useMemo(() => {
818
+ if (!declaredBehaviors?.length)
819
+ return hostBehaviors;
820
+ if (!hostBehaviors?.length)
821
+ return declaredBehaviors;
822
+ const hostIds = new Set(hostBehaviors.map((b) => b.id));
823
+ return [...declaredBehaviors.filter((b) => !hostIds.has(b.id)), ...hostBehaviors];
824
+ }, [declaredBehaviors, hostBehaviors]);
825
+ const hasBehaviors = Boolean(behaviors && behaviors.length > 0);
826
+ // Nothing dynamic — no sources, no state, no behaviors, no form bindings, no
827
+ // block action: pass through directly (zero overhead). Write-only surfaces
828
+ // (formBindings without any read source) and state-only wizards must NOT
829
+ // take it — see surfaceNeedsDataBinding() and hasEntityFormBindings().
830
+ if (!hasBehaviors && !surfaceNeedsDataBinding(surface)) {
831
+ const passthrough = {
832
+ // `id` FORWARDED, not dropped. SurfaceRenderer derives its scope from
833
+ // `surface.id`: with an id the per-surface rules key off the attribute
834
+ // VALUE (`[data-pw-surface="<id>"]`), without one they fall back to the
835
+ // value-less `[data-pw-surface]` and every surface in a document shares
836
+ // them. Rebuilding the surface object here silently erased the id, so the
837
+ // scoping degraded on this path while looking correct in the source.
838
+ ...(surface.id !== undefined ? { id: surface.id } : {}),
839
+ blocks,
840
+ ...(appearance !== undefined ? { appearance } : {}),
841
+ ...(capabilities !== undefined ? { capabilities } : {}),
842
+ };
843
+ return onEvent ? (_jsx(SurfaceRenderer, { surface: passthrough, onEvent: onEvent, ...rest })) : (_jsx(SurfaceRenderer, { surface: passthrough, ...rest }));
844
+ }
845
+ // Wrap with DataCapabilityProvider to provide DataService context
846
+ return (_jsx(DataCapabilityProvider, { ...(dataServiceConfig ? { config: dataServiceConfig } : {}), children: onEvent ? (_jsx(DataBoundInner, { ...(surface.id !== undefined ? { surfaceId: surface.id } : {}), blocks: blocks, dataSources: dataSources ?? [], initialState: initialState, appearance: appearance, capabilities: capabilities, ...(behaviors !== undefined ? { behaviors } : {}), ...(onNavigateAction !== undefined ? { onNavigateAction } : {}), onEvent: onEvent, rendererProps: rest })) : (_jsx(DataBoundInner, { ...(surface.id !== undefined ? { surfaceId: surface.id } : {}), blocks: blocks, dataSources: dataSources ?? [], initialState: initialState, appearance: appearance, capabilities: capabilities, ...(behaviors !== undefined ? { behaviors } : {}), ...(onNavigateAction !== undefined ? { onNavigateAction } : {}), rendererProps: rest })) }));
847
+ }
848
+ function DataBoundInner({ surfaceId, blocks, dataSources, initialState, appearance, capabilities, onEvent, behaviors, onNavigateAction, rendererProps, }) {
849
+ const interactionMode = rendererProps.interactionMode ?? 'live';
850
+ const entityFacade = rendererProps.entityFacade;
851
+ // Same pass-through route as entityFacade: declared on
852
+ // DataBoundSurfaceRendererProps, travels inside rendererProps.
853
+ const loadingStrategy = rendererProps.loadingStrategy ?? 'gate';
854
+ // Connections context travels the same pass-through route
855
+ // (declared on SurfaceRendererProps, so it also reaches SurfaceRenderer's
856
+ // SurfaceConnectionsContext provider via the rendererProps spread below).
857
+ const connections = rendererProps.connections;
858
+ const dataCache = rendererProps.dataCache;
859
+ // Keyed by the ids, so a re-render that changes nothing hands useSurfaceData
860
+ // the same set (its expression context depends on it).
861
+ const writeOnlyKey = React.useMemo(() => [...writeOnlySourceIds({ blocks, dataSources, capabilities, behaviors })].sort().join('\n'), [blocks, dataSources, capabilities, behaviors]);
862
+ const writeOnly = React.useMemo(() => new Set(writeOnlyKey === '' ? [] : writeOnlyKey.split('\n')), [writeOnlyKey]);
863
+ const { expressionData, initialLoaded, setStateValue, refreshSource, fetchAllSources } = useSurfaceData(dataSources, initialState, interactionMode, entityFacade, connections, dataCache, writeOnly);
864
+ // Resolve expressions in blocks
865
+ const resolvedBlocks = React.useMemo(() => resolveBlockExpressions(blocks, expressionData), [blocks, expressionData]);
866
+ // Shared action dispatcher — handles all six action types
867
+ const dispatchAction = React.useCallback((action) => {
868
+ switch (action.type) {
869
+ case 'setState':
870
+ setStateValue(action.key, action.value);
871
+ break;
872
+ case 'refresh':
873
+ refreshSource(action.sourceId);
874
+ break;
875
+ case 'refreshAll':
876
+ fetchAllSources();
877
+ break;
878
+ case 'showBlock':
879
+ setStateValue('__show:' + action.blockId, true);
880
+ break;
881
+ case 'hideBlock':
882
+ setStateValue('__show:' + action.blockId, false);
883
+ break;
884
+ case 'navigate':
885
+ // Give the host a chance to handle this in place first (e.g. a
886
+ // connected-preview mode swapping to a sibling page) before falling
887
+ // through to a real hard navigation.
888
+ if (onNavigateAction?.(action.url))
889
+ break;
890
+ if (isSafeNavigateUrl(action.url)) {
891
+ window.location.assign(action.url);
892
+ }
893
+ else {
894
+ console.warn('[surface] blocked unsafe navigate URL:', action.url);
895
+ }
896
+ break;
897
+ }
898
+ }, [setStateValue, refreshSource, fetchAllSources, onNavigateAction]);
899
+ // Event interceptor
900
+ const handleEvent = React.useCallback((event) => {
901
+ // Inert (editor/preview): skip block-dispatched surface actions (state
902
+ // mutations / data refreshes) so the surface doesn't mutate its own state
903
+ // while authoring. `onEvent` is a pure notification and STILL fires below
904
+ // — only the real data side-effects are gated.
905
+ if (interactionMode !== 'inert') {
906
+ // Check if the event payload contains a surface action
907
+ const payload = event.payload;
908
+ const action = payload?.action;
909
+ if (isSurfaceAction(action)) {
910
+ dispatchAction(action);
911
+ }
912
+ // `canonicalType` is normally stamped by the bus bridge (renderer.tsx's
913
+ // busOnEvent), but some deployed block bundles still call
914
+ // `context.onEvent` directly (the pre-bus legacy path) — for those,
915
+ // `canonicalType` is absent. Block-scoped click events reaching here
916
+ // always mean a real click occurred, so treat a canonicalType-less
917
+ // 'click' as 'block:click' rather than silently dropping it.
918
+ const fired = event.canonicalType ?? (event.type === 'click' && event.blockId ? 'block:click' : '');
919
+ // A block's own `action` prop (one action or a list, run in order) is
920
+ // what its CLICK does — and only its click. It used to run on every
921
+ // event the block emitted, so a block that also reported hover, focus
922
+ // or a value change would have fired its click action on each of them.
923
+ // Looked up in the RESOLVED tree, so a repeat clone (`id--N`) carries
924
+ // its own row's values.
925
+ if (fired === 'block:click') {
926
+ blockActionsOf(findBlockAction(resolvedBlocks, event.blockId)).forEach(dispatchAction);
927
+ }
928
+ // Execute stored behaviors whose trigger matches this canonical event.
929
+ if (behaviors && behaviors.length > 0) {
930
+ for (const b of behaviors) {
931
+ if (canonicalizeEventType(b.event) !== fired)
932
+ continue;
933
+ if (b.targetBlockId && b.targetBlockId !== event.blockId)
934
+ continue;
935
+ b.actions.forEach(dispatchAction);
936
+ }
937
+ }
938
+ }
939
+ // Forward to original handler — a pure notification, always fires (the
940
+ // host decides what side-effects, if any, to run).
941
+ onEvent?.(event);
942
+ }, [interactionMode, onEvent, dispatchAction, resolvedBlocks, behaviors]);
943
+ // Loading state — 'gate' strategy only (the historical default): the whole
944
+ // block tree is withheld until the first fetch pass settles. 'immediate'
945
+ // skips this and renders the tree at once; unresolved bindings evaluate to
946
+ // undefined and fill in per source (see loadingStrategy prop docs).
947
+ if (!initialLoaded && loadingStrategy !== 'immediate') {
948
+ return (_jsx("div", { "data-surface-loading": true, style: {
949
+ display: 'flex',
950
+ alignItems: 'center',
951
+ justifyContent: 'center',
952
+ padding: '48px',
953
+ // Theme-tokened with the old hardcoded values as fallbacks, so the
954
+ // gate no longer paints light-theme blue on a dark-themed surface.
955
+ color: 'var(--pw-color-text-muted, #666)',
956
+ }, children: _jsxs("div", { style: { textAlign: 'center' }, children: [_jsx("div", { style: {
957
+ width: '32px',
958
+ height: '32px',
959
+ border: '3px solid var(--pw-color-border, #e5e7eb)',
960
+ borderTopColor: 'var(--pw-color-primary, #3b82f6)',
961
+ borderRadius: '50%',
962
+ animation: 'spin 0.8s linear infinite',
963
+ margin: '0 auto 12px',
964
+ } }), "Loading data..."] }) }));
965
+ }
966
+ const resolvedSurface = {
967
+ // See the passthrough branch above — same reason, same one-line omission.
968
+ ...(surfaceId !== undefined ? { id: surfaceId } : {}),
969
+ blocks: resolvedBlocks,
970
+ ...(appearance !== undefined ? { appearance } : {}),
971
+ ...(capabilities !== undefined ? { capabilities } : {}),
972
+ };
973
+ return (
974
+ // What is bound right now, for the providers under SurfaceRenderer that
975
+ // must act on it — the form writer decides create-vs-update from the
976
+ // record a source has loaded, and could not see one before this.
977
+ _jsx(SurfaceDataContext.Provider, { value: expressionData, children: _jsx(SurfaceRenderer, { surface: resolvedSurface, onEvent: handleEvent, ...rendererProps }) }));
978
+ }
979
+ // ============================================================
980
+ // HELPERS
981
+ // ============================================================
982
+ /**
983
+ * Find the action prop of a block by ID in the resolved block tree.
984
+ */
985
+ function findBlockAction(blocks, blockId) {
986
+ for (const block of blocks) {
987
+ if (block.id === blockId) {
988
+ return block.props?.action;
989
+ }
990
+ if (block.children) {
991
+ const found = findBlockAction(block.children, blockId);
992
+ if (found !== undefined)
993
+ return found;
994
+ }
995
+ }
996
+ return undefined;
997
+ }
998
+ //# sourceMappingURL=DataBoundSurfaceRenderer.js.map