@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,462 @@
1
+ /**
2
+ * Two-way Data Binding
3
+ *
4
+ * P1-5: Extends the binding system with write-back capability.
5
+ * UI changes propagate back to the data source, enabling form inputs,
6
+ * editable fields, and other interactive elements to automatically
7
+ * update the data model.
8
+ *
9
+ * @module @pwfabric/runtime/binding/two-way
10
+ */
11
+ import { resolvePath } from './expression.js';
12
+ /**
13
+ * Parse a dot-notation path into segments for traversal
14
+ */
15
+ function parsePath(path) {
16
+ const segments = [];
17
+ const regex = /(\w+)|\[(\d+)\]/g;
18
+ let match;
19
+ while ((match = regex.exec(path)) !== null) {
20
+ if (match[1] !== undefined) {
21
+ segments.push({ type: 'property', key: match[1] });
22
+ }
23
+ else if (match[2] !== undefined) {
24
+ segments.push({ type: 'index', index: parseInt(match[2], 10) });
25
+ }
26
+ }
27
+ return segments;
28
+ }
29
+ /**
30
+ * Set a value at a dot-notation path in an object, creating
31
+ * intermediate objects/arrays as needed.
32
+ *
33
+ * Mutates the target object in place.
34
+ */
35
+ function setAtPath(obj, path, value) {
36
+ const segments = parsePath(path);
37
+ if (segments.length === 0)
38
+ return;
39
+ let current = obj;
40
+ for (let i = 0; i < segments.length - 1; i++) {
41
+ const segment = segments[i];
42
+ if (segment === undefined)
43
+ return;
44
+ if (segment.type === 'property') {
45
+ if (typeof current !== 'object' || current === null)
46
+ return;
47
+ const record = current;
48
+ if (record[segment.key] === undefined || record[segment.key] === null) {
49
+ // Look ahead to decide whether to create an object or array
50
+ const next = segments[i + 1];
51
+ record[segment.key] = next && next.type === 'index' ? [] : {};
52
+ }
53
+ current = record[segment.key];
54
+ }
55
+ else {
56
+ if (!Array.isArray(current))
57
+ return;
58
+ if (current[segment.index] === undefined || current[segment.index] === null) {
59
+ const next = segments[i + 1];
60
+ current[segment.index] = next && next.type === 'index' ? [] : {};
61
+ }
62
+ current = current[segment.index];
63
+ }
64
+ }
65
+ const last = segments[segments.length - 1];
66
+ if (last === undefined)
67
+ return;
68
+ if (last.type === 'property') {
69
+ if (typeof current === 'object' && current !== null) {
70
+ ;
71
+ current[last.key] = value;
72
+ }
73
+ }
74
+ else {
75
+ if (Array.isArray(current)) {
76
+ current[last.index] = value;
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Deep-clone a plain object/array structure using structured clone
82
+ * semantics (JSON round-trip for simplicity and zero dependencies).
83
+ */
84
+ function deepClone(value) {
85
+ if (value === undefined || value === null)
86
+ return value;
87
+ if (typeof value !== 'object')
88
+ return value;
89
+ return JSON.parse(JSON.stringify(value));
90
+ }
91
+ // ============================================================
92
+ // TWO-WAY BINDING INSTANCE (INTERNAL)
93
+ // ============================================================
94
+ /**
95
+ * Internal class that implements a single two-way binding
96
+ */
97
+ class TwoWayBindingInstance {
98
+ path;
99
+ _value;
100
+ _initialValue;
101
+ _error;
102
+ _isDirty = false;
103
+ _isValidating = false;
104
+ _disposed = false;
105
+ config;
106
+ manager;
107
+ debounceTimer;
108
+ constructor(config, initialValue, manager) {
109
+ this.path = config.path;
110
+ this.config = config;
111
+ this.manager = manager;
112
+ // Apply toView transform for initial value
113
+ const viewValue = config.transform?.toView
114
+ ? config.transform.toView(initialValue)
115
+ : initialValue;
116
+ this._value = viewValue;
117
+ this._initialValue = deepClone(viewValue);
118
+ }
119
+ get value() {
120
+ return this._value;
121
+ }
122
+ get error() {
123
+ return this._error;
124
+ }
125
+ get isDirty() {
126
+ return this._isDirty;
127
+ }
128
+ get isValidating() {
129
+ return this._isValidating;
130
+ }
131
+ setValue(newValue) {
132
+ if (this._disposed)
133
+ return;
134
+ const oldValue = this._value;
135
+ // Run validation if configured
136
+ if (this.config.validate) {
137
+ this._isValidating = true;
138
+ const result = this.config.validate(newValue);
139
+ this._isValidating = false;
140
+ if (!result.valid) {
141
+ this._error = result.error;
142
+ return;
143
+ }
144
+ }
145
+ // Clear any previous error
146
+ this._error = undefined;
147
+ // Update the local value
148
+ this._value = newValue;
149
+ // Track dirty state
150
+ this._isDirty = !isEqual(newValue, this._initialValue);
151
+ // Debounced write-back to model
152
+ this.scheduleWriteBack(newValue, oldValue);
153
+ }
154
+ reset() {
155
+ if (this._disposed)
156
+ return;
157
+ const oldValue = this._value;
158
+ this._value = deepClone(this._initialValue);
159
+ this._isDirty = false;
160
+ this._error = undefined;
161
+ this.clearDebounce();
162
+ // Write the reset value back to model immediately
163
+ const modelValue = this.config.transform?.toModel
164
+ ? this.config.transform.toModel(this._value)
165
+ : this._value;
166
+ this.manager.setValueInternal(this.path, modelValue, oldValue);
167
+ }
168
+ dispose() {
169
+ if (this._disposed)
170
+ return;
171
+ this._disposed = true;
172
+ this.clearDebounce();
173
+ }
174
+ /**
175
+ * Called by the manager when the model value changes externally
176
+ */
177
+ updateFromModel(modelValue) {
178
+ if (this._disposed)
179
+ return;
180
+ const viewValue = this.config.transform?.toView
181
+ ? this.config.transform.toView(modelValue)
182
+ : modelValue;
183
+ this._value = viewValue;
184
+ this._isDirty = !isEqual(viewValue, this._initialValue);
185
+ }
186
+ scheduleWriteBack(newValue, oldValue) {
187
+ this.clearDebounce();
188
+ const delay = this.config.debounceMs ?? DEFAULT_DEBOUNCE_MS;
189
+ if (delay <= 0) {
190
+ // Write immediately
191
+ this.writeBack(newValue, oldValue);
192
+ return;
193
+ }
194
+ this.debounceTimer = setTimeout(() => {
195
+ this.debounceTimer = undefined;
196
+ this.writeBack(newValue, oldValue);
197
+ }, delay);
198
+ }
199
+ writeBack(newValue, oldValue) {
200
+ if (this._disposed)
201
+ return;
202
+ // Apply toModel transform before writing back
203
+ const modelValue = this.config.transform?.toModel
204
+ ? this.config.transform.toModel(newValue)
205
+ : newValue;
206
+ this.manager.setValueInternal(this.path, modelValue, oldValue);
207
+ }
208
+ clearDebounce() {
209
+ if (this.debounceTimer !== undefined) {
210
+ clearTimeout(this.debounceTimer);
211
+ this.debounceTimer = undefined;
212
+ }
213
+ }
214
+ }
215
+ // ============================================================
216
+ // CONSTANTS
217
+ // ============================================================
218
+ /** Default debounce delay in milliseconds */
219
+ const DEFAULT_DEBOUNCE_MS = 300;
220
+ // ============================================================
221
+ // UTILITY
222
+ // ============================================================
223
+ /**
224
+ * Shallow equality check suitable for primitive and simple object values.
225
+ * Uses JSON serialization for object comparison (safe for plain data).
226
+ */
227
+ function isEqual(a, b) {
228
+ if (a === b)
229
+ return true;
230
+ if (a === null || b === null)
231
+ return false;
232
+ if (a === undefined || b === undefined)
233
+ return false;
234
+ if (typeof a !== typeof b)
235
+ return false;
236
+ if (typeof a === 'object') {
237
+ try {
238
+ return JSON.stringify(a) === JSON.stringify(b);
239
+ }
240
+ catch {
241
+ return false;
242
+ }
243
+ }
244
+ return false;
245
+ }
246
+ // ============================================================
247
+ // TWO-WAY BINDING MANAGER
248
+ // ============================================================
249
+ /**
250
+ * Manages two-way data bindings.
251
+ *
252
+ * Provides a central data store with path-based access, change listeners,
253
+ * validation, transforms, and batch update support. Each call to `bind()`
254
+ * creates a `TwoWayBinding` instance that can read/write a specific path
255
+ * in the data model.
256
+ */
257
+ export class TwoWayBindingManager {
258
+ bindings = new Map();
259
+ data;
260
+ listeners = new Map();
261
+ batchDepth = 0;
262
+ pendingNotifications = [];
263
+ constructor(initialData) {
264
+ this.data = initialData ? deepClone(initialData) : {};
265
+ }
266
+ /**
267
+ * Create a two-way binding for a specific data path.
268
+ *
269
+ * Returns a `TwoWayBinding` instance that tracks the value at the path,
270
+ * supports validation and transforms, and writes changes back to the
271
+ * data model with optional debouncing.
272
+ */
273
+ bind(config) {
274
+ // Dispose any existing binding at this path
275
+ const existing = this.bindings.get(config.path);
276
+ if (existing) {
277
+ existing.dispose();
278
+ }
279
+ const currentValue = resolvePath(this.data, config.path);
280
+ const instance = new TwoWayBindingInstance(config, currentValue, this);
281
+ this.bindings.set(config.path, instance);
282
+ return instance;
283
+ }
284
+ /**
285
+ * Get the current value at a data path
286
+ */
287
+ getValue(path) {
288
+ return resolvePath(this.data, path);
289
+ }
290
+ /**
291
+ * Set a value at a data path, triggering change listeners
292
+ */
293
+ setValue(path, value) {
294
+ const oldValue = resolvePath(this.data, path);
295
+ // Write to data store
296
+ setAtPath(this.data, path, value);
297
+ // Update any binding watching this path
298
+ const binding = this.bindings.get(path);
299
+ if (binding) {
300
+ binding.updateFromModel(value);
301
+ }
302
+ // Notify listeners
303
+ this.notifyChange(path, value, oldValue);
304
+ }
305
+ /**
306
+ * Subscribe to changes at a data path.
307
+ *
308
+ * @returns Unsubscribe function
309
+ */
310
+ onChange(path, listener) {
311
+ let pathListeners = this.listeners.get(path);
312
+ if (!pathListeners) {
313
+ pathListeners = new Set();
314
+ this.listeners.set(path, pathListeners);
315
+ }
316
+ pathListeners.add(listener);
317
+ return () => {
318
+ pathListeners.delete(listener);
319
+ if (pathListeners.size === 0) {
320
+ this.listeners.delete(path);
321
+ }
322
+ };
323
+ }
324
+ /**
325
+ * Batch multiple updates together.
326
+ *
327
+ * Listener notifications are deferred until the outermost batch
328
+ * completes, then fired once per changed path.
329
+ */
330
+ batch(updates) {
331
+ this.batchDepth++;
332
+ try {
333
+ updates();
334
+ }
335
+ finally {
336
+ this.batchDepth--;
337
+ if (this.batchDepth === 0) {
338
+ this.flushPendingNotifications();
339
+ }
340
+ }
341
+ }
342
+ /**
343
+ * Get all bindings that have been modified from their initial values
344
+ */
345
+ getDirtyBindings() {
346
+ const dirty = [];
347
+ for (const binding of this.bindings.values()) {
348
+ if (binding.isDirty) {
349
+ dirty.push(binding);
350
+ }
351
+ }
352
+ return dirty;
353
+ }
354
+ /**
355
+ * Reset all bindings to their initial values
356
+ */
357
+ resetAll() {
358
+ for (const binding of this.bindings.values()) {
359
+ binding.reset();
360
+ }
361
+ }
362
+ /**
363
+ * Dispose all bindings and clear all listeners
364
+ */
365
+ dispose() {
366
+ for (const binding of this.bindings.values()) {
367
+ binding.dispose();
368
+ }
369
+ this.bindings.clear();
370
+ this.listeners.clear();
371
+ this.pendingNotifications.length = 0;
372
+ }
373
+ /**
374
+ * Internal method called by binding instances to write back to the model.
375
+ * Not part of the public API.
376
+ */
377
+ setValueInternal(path, modelValue, oldViewValue) {
378
+ const oldModelValue = resolvePath(this.data, path);
379
+ // Write to data store
380
+ setAtPath(this.data, path, modelValue);
381
+ // Notify listeners with model values
382
+ this.notifyChange(path, modelValue, oldModelValue ?? oldViewValue);
383
+ }
384
+ // ============================================================
385
+ // PRIVATE
386
+ // ============================================================
387
+ notifyChange(path, newValue, oldValue) {
388
+ if (this.batchDepth > 0) {
389
+ // During batch, accumulate notifications (last write wins per path)
390
+ const existingIdx = this.pendingNotifications.findIndex((n) => n.path === path);
391
+ if (existingIdx >= 0) {
392
+ const existing = this.pendingNotifications[existingIdx];
393
+ if (existing) {
394
+ // Keep the original oldValue, update newValue
395
+ this.pendingNotifications[existingIdx] = {
396
+ path,
397
+ newValue,
398
+ oldValue: existing.oldValue,
399
+ };
400
+ }
401
+ }
402
+ else {
403
+ this.pendingNotifications.push({ path, newValue, oldValue });
404
+ }
405
+ return;
406
+ }
407
+ this.fireListeners(path, newValue, oldValue);
408
+ }
409
+ flushPendingNotifications() {
410
+ // Copy and clear to handle re-entrant batch calls
411
+ const pending = [...this.pendingNotifications];
412
+ this.pendingNotifications.length = 0;
413
+ for (const { path, newValue, oldValue } of pending) {
414
+ this.fireListeners(path, newValue, oldValue);
415
+ }
416
+ }
417
+ fireListeners(path, newValue, oldValue) {
418
+ const pathListeners = this.listeners.get(path);
419
+ if (!pathListeners)
420
+ return;
421
+ for (const listener of pathListeners) {
422
+ try {
423
+ listener(newValue, oldValue);
424
+ }
425
+ catch {
426
+ // Swallow errors from listeners to prevent cascade failures
427
+ }
428
+ }
429
+ }
430
+ }
431
+ // ============================================================
432
+ // FACTORY
433
+ // ============================================================
434
+ /**
435
+ * Create a two-way binding manager
436
+ *
437
+ * @param initialData - Optional initial data to populate the model
438
+ * @returns A new TwoWayBindingManager instance
439
+ *
440
+ * @example
441
+ * ```ts
442
+ * const manager = createTwoWayBindingManager({
443
+ * user: { name: 'John', email: 'john@example.com' }
444
+ * })
445
+ *
446
+ * const nameBinding = manager.bind<string>({
447
+ * path: 'user.name',
448
+ * debounceMs: 200,
449
+ * validate: (v) => ({
450
+ * valid: typeof v === 'string' && v.length > 0,
451
+ * error: 'Name is required',
452
+ * }),
453
+ * })
454
+ *
455
+ * nameBinding.setValue('Jane')
456
+ * // After debounce, manager.getValue('user.name') === 'Jane'
457
+ * ```
458
+ */
459
+ export function createTwoWayBindingManager(initialData) {
460
+ return new TwoWayBindingManager(initialData);
461
+ }
462
+ //# sourceMappingURL=two-way.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"two-way.js","sourceRoot":"","sources":["../../src/binding/two-way.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Two-way Data Binding\n *\n * P1-5: Extends the binding system with write-back capability.\n * UI changes propagate back to the data source, enabling form inputs,\n * editable fields, and other interactive elements to automatically\n * update the data model.\n *\n * @module @pwfabric/runtime/binding/two-way\n */\n\nimport { resolvePath } from './expression.js'\n\n// ============================================================\n// TYPES\n// ============================================================\n\n/**\n * Configuration for a two-way binding\n */\nexport interface TwoWayBindingConfig {\n /** Data path using dot-notation (e.g., \"user.profile.name\") */\n readonly path: string\n /** Debounce delay in ms for write-back (default: 300) */\n readonly debounceMs?: number\n /** Optional validation function run before setting value */\n readonly validate?: (value: unknown) => ValidationResult\n /** Optional transform functions between UI and model */\n readonly transform?: {\n readonly toModel?: (uiValue: unknown) => unknown\n readonly toView?: (modelValue: unknown) => unknown\n }\n}\n\n/**\n * Result of value validation\n */\nexport interface ValidationResult {\n readonly valid: boolean\n readonly error?: string\n}\n\n/**\n * A two-way binding instance for a specific data path\n */\nexport interface TwoWayBinding<T = unknown> {\n /** The data path this binding targets */\n readonly path: string\n /** Current value at the path */\n readonly value: T\n /** Current validation error, if any */\n readonly error?: string | undefined\n /** Whether the value has changed from the initial value */\n readonly isDirty: boolean\n /** Whether validation is currently running */\n readonly isValidating: boolean\n /** Set a new value (triggers validation, debounced write-back) */\n setValue(newValue: T): void\n /** Reset value to the initial value */\n reset(): void\n /** Dispose the binding and clean up timers */\n dispose(): void\n}\n\n/**\n * Listener called when a value changes at a path\n */\nexport type ChangeListener<T = unknown> = (newValue: T, oldValue: T) => void\n\n// ============================================================\n// PATH UTILITIES\n// ============================================================\n\n/**\n * Parsed segment of a data path\n */\ntype PathSegment = { type: 'property'; key: string } | { type: 'index'; index: number }\n\n/**\n * Parse a dot-notation path into segments for traversal\n */\nfunction parsePath(path: string): PathSegment[] {\n const segments: PathSegment[] = []\n const regex = /(\\w+)|\\[(\\d+)\\]/g\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(path)) !== null) {\n if (match[1] !== undefined) {\n segments.push({ type: 'property', key: match[1] })\n } else if (match[2] !== undefined) {\n segments.push({ type: 'index', index: parseInt(match[2], 10) })\n }\n }\n\n return segments\n}\n\n/**\n * Set a value at a dot-notation path in an object, creating\n * intermediate objects/arrays as needed.\n *\n * Mutates the target object in place.\n */\nfunction setAtPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const segments = parsePath(path)\n if (segments.length === 0) return\n\n let current: unknown = obj\n\n for (let i = 0; i < segments.length - 1; i++) {\n const segment = segments[i]\n if (segment === undefined) return\n\n if (segment.type === 'property') {\n if (typeof current !== 'object' || current === null) return\n const record = current as Record<string, unknown>\n if (record[segment.key] === undefined || record[segment.key] === null) {\n // Look ahead to decide whether to create an object or array\n const next = segments[i + 1]\n record[segment.key] = next && next.type === 'index' ? [] : {}\n }\n current = record[segment.key]\n } else {\n if (!Array.isArray(current)) return\n if (current[segment.index] === undefined || current[segment.index] === null) {\n const next = segments[i + 1]\n current[segment.index] = next && next.type === 'index' ? [] : {}\n }\n current = current[segment.index]\n }\n }\n\n const last = segments[segments.length - 1]\n if (last === undefined) return\n\n if (last.type === 'property') {\n if (typeof current === 'object' && current !== null) {\n ;(current as Record<string, unknown>)[last.key] = value\n }\n } else {\n if (Array.isArray(current)) {\n current[last.index] = value\n }\n }\n}\n\n/**\n * Deep-clone a plain object/array structure using structured clone\n * semantics (JSON round-trip for simplicity and zero dependencies).\n */\nfunction deepClone<T>(value: T): T {\n if (value === undefined || value === null) return value\n if (typeof value !== 'object') return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n\n// ============================================================\n// TWO-WAY BINDING INSTANCE (INTERNAL)\n// ============================================================\n\n/**\n * Internal class that implements a single two-way binding\n */\nclass TwoWayBindingInstance<T = unknown> implements TwoWayBinding<T> {\n readonly path: string\n\n private _value: T\n private _initialValue: T\n private _error: string | undefined\n private _isDirty = false\n private _isValidating = false\n private _disposed = false\n\n private readonly config: TwoWayBindingConfig\n private readonly manager: TwoWayBindingManager\n private debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n constructor(config: TwoWayBindingConfig, initialValue: T, manager: TwoWayBindingManager) {\n this.path = config.path\n this.config = config\n this.manager = manager\n\n // Apply toView transform for initial value\n const viewValue = config.transform?.toView\n ? (config.transform.toView(initialValue) as T)\n : initialValue\n\n this._value = viewValue\n this._initialValue = deepClone(viewValue)\n }\n\n get value(): T {\n return this._value\n }\n\n get error(): string | undefined {\n return this._error\n }\n\n get isDirty(): boolean {\n return this._isDirty\n }\n\n get isValidating(): boolean {\n return this._isValidating\n }\n\n setValue(newValue: T): void {\n if (this._disposed) return\n\n const oldValue = this._value\n\n // Run validation if configured\n if (this.config.validate) {\n this._isValidating = true\n const result = this.config.validate(newValue)\n this._isValidating = false\n\n if (!result.valid) {\n this._error = result.error\n return\n }\n }\n\n // Clear any previous error\n this._error = undefined\n\n // Update the local value\n this._value = newValue\n\n // Track dirty state\n this._isDirty = !isEqual(newValue, this._initialValue)\n\n // Debounced write-back to model\n this.scheduleWriteBack(newValue, oldValue)\n }\n\n reset(): void {\n if (this._disposed) return\n\n const oldValue = this._value\n this._value = deepClone(this._initialValue)\n this._isDirty = false\n this._error = undefined\n this.clearDebounce()\n\n // Write the reset value back to model immediately\n const modelValue = this.config.transform?.toModel\n ? this.config.transform.toModel(this._value)\n : this._value\n\n this.manager.setValueInternal(this.path, modelValue, oldValue)\n }\n\n dispose(): void {\n if (this._disposed) return\n this._disposed = true\n this.clearDebounce()\n }\n\n /**\n * Called by the manager when the model value changes externally\n */\n updateFromModel(modelValue: unknown): void {\n if (this._disposed) return\n\n const viewValue = this.config.transform?.toView\n ? (this.config.transform.toView(modelValue) as T)\n : (modelValue as T)\n\n this._value = viewValue\n this._isDirty = !isEqual(viewValue, this._initialValue)\n }\n\n private scheduleWriteBack(newValue: T, oldValue: T): void {\n this.clearDebounce()\n\n const delay = this.config.debounceMs ?? DEFAULT_DEBOUNCE_MS\n\n if (delay <= 0) {\n // Write immediately\n this.writeBack(newValue, oldValue)\n return\n }\n\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = undefined\n this.writeBack(newValue, oldValue)\n }, delay)\n }\n\n private writeBack(newValue: T, oldValue: T): void {\n if (this._disposed) return\n\n // Apply toModel transform before writing back\n const modelValue = this.config.transform?.toModel\n ? this.config.transform.toModel(newValue)\n : newValue\n\n this.manager.setValueInternal(this.path, modelValue, oldValue)\n }\n\n private clearDebounce(): void {\n if (this.debounceTimer !== undefined) {\n clearTimeout(this.debounceTimer)\n this.debounceTimer = undefined\n }\n }\n}\n\n// ============================================================\n// CONSTANTS\n// ============================================================\n\n/** Default debounce delay in milliseconds */\nconst DEFAULT_DEBOUNCE_MS = 300\n\n// ============================================================\n// UTILITY\n// ============================================================\n\n/**\n * Shallow equality check suitable for primitive and simple object values.\n * Uses JSON serialization for object comparison (safe for plain data).\n */\nfunction isEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n if (a === undefined || b === undefined) return false\n if (typeof a !== typeof b) return false\n if (typeof a === 'object') {\n try {\n return JSON.stringify(a) === JSON.stringify(b)\n } catch {\n return false\n }\n }\n return false\n}\n\n// ============================================================\n// TWO-WAY BINDING MANAGER\n// ============================================================\n\n/**\n * Manages two-way data bindings.\n *\n * Provides a central data store with path-based access, change listeners,\n * validation, transforms, and batch update support. Each call to `bind()`\n * creates a `TwoWayBinding` instance that can read/write a specific path\n * in the data model.\n */\nexport class TwoWayBindingManager {\n private readonly bindings = new Map<string, TwoWayBindingInstance>()\n private data: Record<string, unknown>\n private readonly listeners = new Map<string, Set<ChangeListener>>()\n private batchDepth = 0\n private readonly pendingNotifications: {\n path: string\n newValue: unknown\n oldValue: unknown\n }[] = []\n\n constructor(initialData?: Record<string, unknown>) {\n this.data = initialData ? deepClone(initialData) : {}\n }\n\n /**\n * Create a two-way binding for a specific data path.\n *\n * Returns a `TwoWayBinding` instance that tracks the value at the path,\n * supports validation and transforms, and writes changes back to the\n * data model with optional debouncing.\n */\n bind<T = unknown>(config: TwoWayBindingConfig): TwoWayBinding<T> {\n // Dispose any existing binding at this path\n const existing = this.bindings.get(config.path)\n if (existing) {\n existing.dispose()\n }\n\n const currentValue = resolvePath(this.data, config.path) as T\n const instance = new TwoWayBindingInstance<T>(config, currentValue, this)\n\n this.bindings.set(config.path, instance as TwoWayBindingInstance)\n\n return instance\n }\n\n /**\n * Get the current value at a data path\n */\n getValue(path: string): unknown {\n return resolvePath(this.data, path)\n }\n\n /**\n * Set a value at a data path, triggering change listeners\n */\n setValue(path: string, value: unknown): void {\n const oldValue = resolvePath(this.data, path)\n\n // Write to data store\n setAtPath(this.data, path, value)\n\n // Update any binding watching this path\n const binding = this.bindings.get(path)\n if (binding) {\n binding.updateFromModel(value)\n }\n\n // Notify listeners\n this.notifyChange(path, value, oldValue)\n }\n\n /**\n * Subscribe to changes at a data path.\n *\n * @returns Unsubscribe function\n */\n onChange<T = unknown>(path: string, listener: ChangeListener<T>): () => void {\n let pathListeners = this.listeners.get(path)\n if (!pathListeners) {\n pathListeners = new Set<ChangeListener>()\n this.listeners.set(path, pathListeners)\n }\n\n pathListeners.add(listener as ChangeListener)\n\n return () => {\n pathListeners.delete(listener as ChangeListener)\n if (pathListeners.size === 0) {\n this.listeners.delete(path)\n }\n }\n }\n\n /**\n * Batch multiple updates together.\n *\n * Listener notifications are deferred until the outermost batch\n * completes, then fired once per changed path.\n */\n batch(updates: () => void): void {\n this.batchDepth++\n\n try {\n updates()\n } finally {\n this.batchDepth--\n\n if (this.batchDepth === 0) {\n this.flushPendingNotifications()\n }\n }\n }\n\n /**\n * Get all bindings that have been modified from their initial values\n */\n getDirtyBindings(): readonly TwoWayBinding[] {\n const dirty: TwoWayBinding[] = []\n\n for (const binding of this.bindings.values()) {\n if (binding.isDirty) {\n dirty.push(binding)\n }\n }\n\n return dirty\n }\n\n /**\n * Reset all bindings to their initial values\n */\n resetAll(): void {\n for (const binding of this.bindings.values()) {\n binding.reset()\n }\n }\n\n /**\n * Dispose all bindings and clear all listeners\n */\n dispose(): void {\n for (const binding of this.bindings.values()) {\n binding.dispose()\n }\n this.bindings.clear()\n this.listeners.clear()\n this.pendingNotifications.length = 0\n }\n\n /**\n * Internal method called by binding instances to write back to the model.\n * Not part of the public API.\n */\n setValueInternal(path: string, modelValue: unknown, oldViewValue: unknown): void {\n const oldModelValue = resolvePath(this.data, path)\n\n // Write to data store\n setAtPath(this.data, path, modelValue)\n\n // Notify listeners with model values\n this.notifyChange(path, modelValue, oldModelValue ?? oldViewValue)\n }\n\n // ============================================================\n // PRIVATE\n // ============================================================\n\n private notifyChange(path: string, newValue: unknown, oldValue: unknown): void {\n if (this.batchDepth > 0) {\n // During batch, accumulate notifications (last write wins per path)\n const existingIdx = this.pendingNotifications.findIndex((n) => n.path === path)\n if (existingIdx >= 0) {\n const existing = this.pendingNotifications[existingIdx]\n if (existing) {\n // Keep the original oldValue, update newValue\n this.pendingNotifications[existingIdx] = {\n path,\n newValue,\n oldValue: existing.oldValue,\n }\n }\n } else {\n this.pendingNotifications.push({ path, newValue, oldValue })\n }\n return\n }\n\n this.fireListeners(path, newValue, oldValue)\n }\n\n private flushPendingNotifications(): void {\n // Copy and clear to handle re-entrant batch calls\n const pending = [...this.pendingNotifications]\n this.pendingNotifications.length = 0\n\n for (const { path, newValue, oldValue } of pending) {\n this.fireListeners(path, newValue, oldValue)\n }\n }\n\n private fireListeners(path: string, newValue: unknown, oldValue: unknown): void {\n const pathListeners = this.listeners.get(path)\n if (!pathListeners) return\n\n for (const listener of pathListeners) {\n try {\n listener(newValue, oldValue)\n } catch {\n // Swallow errors from listeners to prevent cascade failures\n }\n }\n }\n}\n\n// ============================================================\n// FACTORY\n// ============================================================\n\n/**\n * Create a two-way binding manager\n *\n * @param initialData - Optional initial data to populate the model\n * @returns A new TwoWayBindingManager instance\n *\n * @example\n * ```ts\n * const manager = createTwoWayBindingManager({\n * user: { name: 'John', email: 'john@example.com' }\n * })\n *\n * const nameBinding = manager.bind<string>({\n * path: 'user.name',\n * debounceMs: 200,\n * validate: (v) => ({\n * valid: typeof v === 'string' && v.length > 0,\n * error: 'Name is required',\n * }),\n * })\n *\n * nameBinding.setValue('Jane')\n * // After debounce, manager.getValue('user.name') === 'Jane'\n * ```\n */\nexport function createTwoWayBindingManager(\n initialData?: Record<string, unknown>\n): TwoWayBindingManager {\n return new TwoWayBindingManager(initialData)\n}\n"]}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Binding System Types
3
+ *
4
+ * Types for capability-to-surface binding
5
+ *
6
+ * @module @pwfabric/runtime/binding/types
7
+ */
8
+ import type { CapabilityId, SurfaceId, ExecutionResult } from '@pwfabric/contracts';
9
+ /**
10
+ * Binding state
11
+ */
12
+ export type BindingState = 'unbound' | 'binding' | 'bound' | 'error' | 'stale';
13
+ /**
14
+ * Binding configuration
15
+ */
16
+ export interface BindingConfig {
17
+ readonly capabilityId: CapabilityId;
18
+ readonly alias?: string;
19
+ readonly transform?: RuntimeBindingTransform;
20
+ readonly cache?: BindingCacheConfig;
21
+ readonly retry?: RetryConfig;
22
+ }
23
+ /**
24
+ * Transform configuration for binding input/output (runtime version)
25
+ * Named differently from contracts BindingTransform to avoid export collision
26
+ */
27
+ export interface RuntimeBindingTransform {
28
+ readonly input?: (data: unknown) => unknown;
29
+ readonly output?: (data: unknown) => unknown;
30
+ }
31
+ /**
32
+ * Cache configuration for binding (runtime version)
33
+ * Named differently from contracts CacheConfig to avoid export collision
34
+ */
35
+ export interface BindingCacheConfig {
36
+ readonly enabled: boolean;
37
+ readonly ttl?: number;
38
+ readonly key?: string;
39
+ }
40
+ /**
41
+ * Retry configuration
42
+ */
43
+ export interface RetryConfig {
44
+ readonly maxAttempts: number;
45
+ readonly backoff: 'fixed' | 'exponential';
46
+ readonly delayMs: number;
47
+ }
48
+ /**
49
+ * A bound capability instance
50
+ */
51
+ export interface BoundCapability<TInput = unknown, TOutput = unknown> {
52
+ readonly id: string;
53
+ readonly capabilityId: CapabilityId;
54
+ readonly state: BindingState;
55
+ readonly config: BindingConfig;
56
+ readonly lastExecuted?: number;
57
+ readonly lastResult?: ExecutionResult<TOutput>;
58
+ readonly error?: BindingError;
59
+ /**
60
+ * Execute the bound capability
61
+ */
62
+ execute(input: TInput): Promise<ExecutionResult<TOutput>>;
63
+ /**
64
+ * Refresh the binding (re-execute with last input)
65
+ */
66
+ refresh(): Promise<ExecutionResult<TOutput>>;
67
+ /**
68
+ * Invalidate cached data
69
+ */
70
+ invalidate(): void;
71
+ }
72
+ /**
73
+ * Binding error
74
+ */
75
+ export interface BindingError {
76
+ readonly type: 'resolution_failed' | 'execution_failed' | 'transform_failed' | 'cache_failed';
77
+ readonly message: string;
78
+ readonly cause?: unknown;
79
+ }
80
+ /**
81
+ * Context for binding resolution
82
+ */
83
+ export interface BindingContext {
84
+ readonly surfaceId: SurfaceId;
85
+ readonly auth?: AuthContext;
86
+ readonly data: Record<string, unknown>;
87
+ readonly bindings: Map<string, BoundCapability>;
88
+ }
89
+ /**
90
+ * Authentication context
91
+ */
92
+ export interface AuthContext {
93
+ readonly isAuthenticated: boolean;
94
+ readonly userId?: string;
95
+ readonly worldId?: string;
96
+ readonly scopes?: readonly string[];
97
+ readonly token?: string;
98
+ readonly expiresAt?: number;
99
+ }
100
+ /**
101
+ * Result of binding resolution
102
+ */
103
+ export type BindingResult<T = unknown> = {
104
+ readonly success: true;
105
+ readonly binding: BoundCapability<unknown, T>;
106
+ } | {
107
+ readonly success: false;
108
+ readonly error: BindingError;
109
+ };
110
+ /**
111
+ * Result of binding all capabilities for a surface
112
+ */
113
+ export type SurfaceBindingResult = {
114
+ readonly success: true;
115
+ readonly context: BindingContext;
116
+ } | {
117
+ readonly success: false;
118
+ readonly errors: readonly BindingError[];
119
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Binding System Types
3
+ *
4
+ * Types for capability-to-surface binding
5
+ *
6
+ * @module @pwfabric/runtime/binding/types
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/binding/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Binding System Types\n *\n * Types for capability-to-surface binding\n *\n * @module @pwfabric/runtime/binding/types\n */\n\nimport type { CapabilityId, SurfaceId, ExecutionResult } from '@pwfabric/contracts'\n\n// ============================================================\n// BINDING TYPES\n// ============================================================\n\n/**\n * Binding state\n */\nexport type BindingState = 'unbound' | 'binding' | 'bound' | 'error' | 'stale'\n\n/**\n * Binding configuration\n */\nexport interface BindingConfig {\n readonly capabilityId: CapabilityId\n readonly alias?: string\n readonly transform?: RuntimeBindingTransform\n readonly cache?: BindingCacheConfig\n readonly retry?: RetryConfig\n}\n\n/**\n * Transform configuration for binding input/output (runtime version)\n * Named differently from contracts BindingTransform to avoid export collision\n */\nexport interface RuntimeBindingTransform {\n readonly input?: (data: unknown) => unknown\n readonly output?: (data: unknown) => unknown\n}\n\n/**\n * Cache configuration for binding (runtime version)\n * Named differently from contracts CacheConfig to avoid export collision\n */\nexport interface BindingCacheConfig {\n readonly enabled: boolean\n readonly ttl?: number\n readonly key?: string\n}\n\n/**\n * Retry configuration\n */\nexport interface RetryConfig {\n readonly maxAttempts: number\n readonly backoff: 'fixed' | 'exponential'\n readonly delayMs: number\n}\n\n// ============================================================\n// BINDING INSTANCE\n// ============================================================\n\n/**\n * A bound capability instance\n */\nexport interface BoundCapability<TInput = unknown, TOutput = unknown> {\n readonly id: string\n readonly capabilityId: CapabilityId\n readonly state: BindingState\n readonly config: BindingConfig\n readonly lastExecuted?: number\n readonly lastResult?: ExecutionResult<TOutput>\n readonly error?: BindingError\n\n /**\n * Execute the bound capability\n */\n execute(input: TInput): Promise<ExecutionResult<TOutput>>\n\n /**\n * Refresh the binding (re-execute with last input)\n */\n refresh(): Promise<ExecutionResult<TOutput>>\n\n /**\n * Invalidate cached data\n */\n invalidate(): void\n}\n\n/**\n * Binding error\n */\nexport interface BindingError {\n readonly type: 'resolution_failed' | 'execution_failed' | 'transform_failed' | 'cache_failed'\n readonly message: string\n readonly cause?: unknown\n}\n\n// ============================================================\n// BINDING CONTEXT\n// ============================================================\n\n/**\n * Context for binding resolution\n */\nexport interface BindingContext {\n readonly surfaceId: SurfaceId\n readonly auth?: AuthContext\n readonly data: Record<string, unknown>\n readonly bindings: Map<string, BoundCapability>\n}\n\n/**\n * Authentication context\n */\nexport interface AuthContext {\n readonly isAuthenticated: boolean\n readonly userId?: string\n readonly worldId?: string\n readonly scopes?: readonly string[]\n readonly token?: string\n readonly expiresAt?: number\n}\n\n// ============================================================\n// BINDING RESULT\n// ============================================================\n\n/**\n * Result of binding resolution\n */\nexport type BindingResult<T = unknown> =\n | { readonly success: true; readonly binding: BoundCapability<unknown, T> }\n | { readonly success: false; readonly error: BindingError }\n\n/**\n * Result of binding all capabilities for a surface\n */\nexport type SurfaceBindingResult =\n | { readonly success: true; readonly context: BindingContext }\n | { readonly success: false; readonly errors: readonly BindingError[] }\n"]}