@supersoniks/concorde 5.1.2 → 5.1.3-immutable.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 (497) hide show
  1. package/.agents/skills/concorde-docs/SKILL.md +1 -1
  2. package/.claude/skills +1 -0
  3. package/.github/workflows/ci.yml +21 -0
  4. package/.github-export-ignore +30 -0
  5. package/CONTRIBUTING.md +59 -0
  6. package/DEV.md +54 -0
  7. package/LICENSE +21 -0
  8. package/OPEN_SOURCE_CHECKPOINT.md +133 -0
  9. package/README.md +1 -1
  10. package/ai/README.md +29 -41
  11. package/ai/skills/concorde/SKILL.md +54 -324
  12. package/ai/skills/concorde/rules/composition.md +32 -0
  13. package/ai/skills/concorde/rules/data.md +47 -0
  14. package/ai/skills/concorde/rules/http.md +25 -0
  15. package/ai/skills/concorde/rules/lists-forms.md +35 -0
  16. package/ai/skills/concorde/rules/styling.md +46 -0
  17. package/ai/skills/concorde/rules/troubleshooting.md +45 -0
  18. package/build-infos.json +1 -1
  19. package/concorde-core.bundle.js +177 -177
  20. package/concorde-core.es.js +4993 -4833
  21. package/custom-elements-manifest.public.config.mjs +8 -0
  22. package/{.tmp-cem/custom-elements.json → custom-elements.json} +11722 -8868
  23. package/dist/concorde-core.bundle.js +177 -177
  24. package/dist/concorde-core.es.js +4993 -4833
  25. package/dist/docs-mock-api-sw.js +58 -223
  26. package/dist/docs-mock-api-sw.js.map +4 -4
  27. package/dist/img/og-concorde.jpg +0 -0
  28. package/dist/robots.txt +11 -10
  29. package/docs/assets/index-CD4WyL6o.css +1 -0
  30. package/docs/assets/index-D_alEsng.js +12122 -0
  31. package/docs/crawl/core/components/functional/date/date.html +190 -0
  32. package/docs/crawl/core/components/functional/fetch/fetch.html +109 -0
  33. package/docs/crawl/core/components/functional/if/if.html +27 -0
  34. package/docs/crawl/core/components/functional/jsonata/jsonata.html +79 -0
  35. package/docs/crawl/core/components/functional/list/list.html +70 -0
  36. package/docs/crawl/core/components/functional/mix/mix.html +45 -0
  37. package/docs/crawl/core/components/functional/queue/queue.html +96 -0
  38. package/docs/crawl/core/components/functional/router/router.html +106 -0
  39. package/docs/{core → crawl/core}/components/functional/sdui/sdui-extension-points.html +8 -9
  40. package/docs/crawl/core/components/functional/sdui/sdui.html +368 -0
  41. package/docs/crawl/core/components/functional/states/states.html +66 -0
  42. package/docs/crawl/core/components/functional/submit/submit.html +160 -0
  43. package/docs/{core → crawl/core}/components/functional/subscriber/subscriber.html +5 -6
  44. package/docs/crawl/core/components/functional/value/value.html +51 -0
  45. package/docs/{core → crawl/core}/components/ui/alert/alert.html +63 -53
  46. package/docs/{core → crawl/core}/components/ui/alert-messages/alert-messages.html +5 -6
  47. package/docs/{core → crawl/core}/components/ui/badge/badge.html +33 -33
  48. package/docs/crawl/core/components/ui/button/button.html +150 -0
  49. package/docs/crawl/core/components/ui/captcha/captcha.html +112 -0
  50. package/docs/{core → crawl/core}/components/ui/card/card.html +11 -12
  51. package/docs/{core → crawl/core}/components/ui/divider/divider.html +14 -14
  52. package/docs/crawl/core/components/ui/form/checkbox/checkbox.html +73 -0
  53. package/docs/crawl/core/components/ui/form/fieldset/fieldset.html +266 -0
  54. package/docs/{core → crawl/core}/components/ui/form/form-actions/form-actions.html +23 -23
  55. package/docs/crawl/core/components/ui/form/form-layout/form-layout.html +121 -0
  56. package/docs/crawl/core/components/ui/form/input/input.html +109 -0
  57. package/docs/crawl/core/components/ui/form/input-autocomplete/input-autocomplete.html +38 -0
  58. package/docs/crawl/core/components/ui/form/radio/radio.html +56 -0
  59. package/docs/crawl/core/components/ui/form/select/select.html +73 -0
  60. package/docs/crawl/core/components/ui/form/switch/switch.html +56 -0
  61. package/docs/crawl/core/components/ui/form/textarea/textarea.html +59 -0
  62. package/docs/crawl/core/components/ui/group/group.html +104 -0
  63. package/docs/{core → crawl/core}/components/ui/icon/icon.html +9 -10
  64. package/docs/crawl/core/components/ui/image/image.html +298 -0
  65. package/docs/{core → crawl/core}/components/ui/link/link.html +11 -12
  66. package/docs/crawl/core/components/ui/loader/loader.html +124 -0
  67. package/docs/{core → crawl/core}/components/ui/menu/menu.html +33 -34
  68. package/docs/crawl/core/components/ui/modal/modal.html +342 -0
  69. package/docs/crawl/core/components/ui/pop/pop.html +296 -0
  70. package/docs/{core → crawl/core}/components/ui/progress/progress.html +7 -8
  71. package/docs/crawl/core/components/ui/table/table.html +545 -0
  72. package/docs/crawl/core/components/ui/theme/theme.html +131 -0
  73. package/docs/crawl/core/components/ui/toast/toast.html +233 -0
  74. package/docs/crawl/core/components/ui/tooltip/tooltip.html +140 -0
  75. package/docs/{docs/getting-started → crawl/docs/_core-concept}/dataFlow.html +29 -26
  76. package/docs/crawl/docs/_core-concept/overview.html +66 -0
  77. package/docs/crawl/docs/_core-concept/subscriber.html +75 -0
  78. package/docs/crawl/docs/_decorators/ancestor-attribute.html +117 -0
  79. package/docs/crawl/docs/_decorators/auto-subscribe.html +202 -0
  80. package/docs/crawl/docs/_decorators/bind.html +141 -0
  81. package/docs/crawl/docs/_decorators/get.html +149 -0
  82. package/docs/crawl/docs/_decorators/handle.html +165 -0
  83. package/docs/crawl/docs/_decorators/on-assign.html +355 -0
  84. package/docs/crawl/docs/_decorators/patch.html +78 -0
  85. package/docs/crawl/docs/_decorators/post.html +139 -0
  86. package/docs/crawl/docs/_decorators/publish.html +61 -0
  87. package/docs/crawl/docs/_decorators/put.html +78 -0
  88. package/docs/crawl/docs/_decorators/subscribe.html +109 -0
  89. package/docs/crawl/docs/_decorators/wait-for-ancestors.html +137 -0
  90. package/docs/crawl/docs/_directives/sub.html +93 -0
  91. package/docs/{docs/getting-started → crawl/docs/_getting-started}/ai-agents.html +11 -12
  92. package/docs/crawl/docs/_getting-started/concorde-manual-install.html +100 -0
  93. package/docs/crawl/docs/_getting-started/concorde-outside.html +58 -0
  94. package/docs/{docs/getting-started → crawl/docs/_getting-started}/create-a-component.html +14 -11
  95. package/docs/{docs/getting-started → crawl/docs/_getting-started}/my-first-component.html +22 -13
  96. package/docs/crawl/docs/_getting-started/my-first-subscriber.html +110 -0
  97. package/docs/crawl/docs/_getting-started/pubsub.html +62 -0
  98. package/docs/crawl/docs/_getting-started/start.html +76 -0
  99. package/docs/{docs/getting-started → crawl/docs/_getting-started}/theming.html +9 -10
  100. package/docs/{docs/misc → crawl/docs/_misc}/api-configuration.html +18 -18
  101. package/docs/{docs/misc → crawl/docs/_misc}/dataProviderKey.html +11 -12
  102. package/docs/{docs/misc → crawl/docs/_misc}/docs-mock-api.html +28 -25
  103. package/docs/{docs/misc → crawl/docs/_misc}/dynamic-path.html +15 -16
  104. package/docs/{docs/misc → crawl/docs/_misc}/endpoint.html +15 -16
  105. package/docs/crawl/docs/_misc/formelement-subscriber-removal.html +138 -0
  106. package/docs/{docs/misc → crawl/docs/_misc}/html-integration.html +15 -12
  107. package/docs/crawl/hubs/decorators.html +547 -0
  108. package/docs/crawl/hubs/form-components.html +381 -0
  109. package/docs/crawl/hubs/remote-data.html +452 -0
  110. package/docs/crawl/index.html +103 -0
  111. package/docs/docs-mock-api-sw.js +223 -58
  112. package/docs/docs-mock-api-sw.js.map +4 -4
  113. package/docs/index.html +68 -95
  114. package/docs/llms-full.txt +3610 -2231
  115. package/docs/llms.txt +86 -94
  116. package/docs/robots.txt +10 -11
  117. package/docs/sitemap.xml +84 -78
  118. package/docs/src/core/components/functional/date/date.md +231 -91
  119. package/docs/src/core/components/functional/fetch/fetch.md +83 -66
  120. package/docs/src/core/components/functional/if/if.md +3 -28
  121. package/docs/src/core/components/functional/jsonata/jsonata.md +37 -42
  122. package/docs/src/core/components/functional/list/list.md +43 -99
  123. package/docs/src/core/components/functional/mix/mix.md +26 -31
  124. package/docs/src/core/components/functional/queue/queue.md +61 -40
  125. package/docs/src/core/components/functional/router/router.md +65 -64
  126. package/docs/src/core/components/functional/sdui/component-catalog.gaps.json +1 -1
  127. package/docs/src/core/components/functional/sdui/component-catalog.json +1 -1
  128. package/docs/src/core/components/functional/sdui/example.json +86 -19
  129. package/docs/src/core/components/functional/sdui/sdui-extension-points.md +1 -1
  130. package/docs/src/core/components/functional/sdui/sdui.md +358 -187
  131. package/docs/src/core/components/functional/states/states.md +61 -86
  132. package/docs/src/core/components/functional/submit/submit.md +72 -64
  133. package/docs/src/core/components/functional/value/value.md +22 -34
  134. package/docs/src/core/components/ui/alert/alert.md +58 -48
  135. package/docs/src/core/components/ui/badge/badge.md +34 -27
  136. package/docs/src/core/components/ui/button/button.md +106 -139
  137. package/docs/src/core/components/ui/captcha/captcha.md +32 -17
  138. package/docs/src/core/components/ui/card/card.md +6 -6
  139. package/docs/src/core/components/ui/divider/divider.md +11 -8
  140. package/docs/src/core/components/ui/form/checkbox/checkbox.md +42 -90
  141. package/docs/src/core/components/ui/form/fieldset/fieldset.md +75 -31
  142. package/docs/src/core/components/ui/form/form-actions/form-actions.md +16 -14
  143. package/docs/src/core/components/ui/form/form-layout/form-layout.md +29 -19
  144. package/docs/src/core/components/ui/form/input/input.md +95 -95
  145. package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +29 -0
  146. package/docs/src/core/components/ui/form/radio/radio.md +34 -55
  147. package/docs/src/core/components/ui/form/select/select.md +45 -60
  148. package/docs/src/core/components/ui/form/switch/switch.md +34 -57
  149. package/docs/src/core/components/ui/form/textarea/textarea.md +38 -55
  150. package/docs/src/core/components/ui/group/group.md +33 -35
  151. package/docs/src/core/components/ui/icon/icon.md +4 -4
  152. package/docs/src/core/components/ui/image/image.md +107 -41
  153. package/docs/src/core/components/ui/link/link.md +6 -6
  154. package/docs/src/core/components/ui/loader/loader.md +55 -30
  155. package/docs/src/core/components/ui/menu/menu.md +27 -27
  156. package/docs/src/core/components/ui/modal/modal.md +96 -72
  157. package/docs/src/core/components/ui/pop/pop.md +158 -80
  158. package/docs/src/core/components/ui/progress/progress.md +2 -2
  159. package/docs/src/core/components/ui/table/table.md +255 -91
  160. package/docs/src/core/components/ui/theme/theme.md +36 -11
  161. package/docs/src/core/components/ui/toast/toast.md +117 -48
  162. package/docs/src/core/components/ui/tooltip/tooltip.md +62 -27
  163. package/docs/{docs/getting-started → src/docs/_core-concept}/dataFlow.md +18 -17
  164. package/docs/src/docs/_core-concept/overview.md +57 -0
  165. package/docs/src/docs/_core-concept/subscriber.md +75 -0
  166. package/docs/src/docs/_decorators/ancestor-attribute.md +128 -0
  167. package/docs/src/docs/_decorators/auto-subscribe.md +202 -0
  168. package/docs/src/docs/_decorators/bind.md +167 -0
  169. package/docs/src/docs/_decorators/get.md +162 -0
  170. package/docs/src/docs/_decorators/handle.md +175 -0
  171. package/docs/src/docs/_decorators/on-assign.md +390 -0
  172. package/docs/src/docs/_decorators/patch.md +70 -0
  173. package/docs/src/docs/_decorators/post.md +146 -0
  174. package/docs/src/docs/_decorators/publish.md +55 -0
  175. package/docs/src/docs/_decorators/put.md +70 -0
  176. package/docs/src/docs/_decorators/subscribe.md +100 -0
  177. package/docs/src/docs/_decorators/wait-for-ancestors.md +163 -0
  178. package/docs/src/docs/_directives/sub.md +91 -0
  179. package/docs/src/docs/{getting-started → _getting-started}/ai-agents.md +3 -3
  180. package/docs/src/docs/_getting-started/concorde-manual-install.md +133 -0
  181. package/docs/src/docs/_getting-started/concorde-outside.md +33 -0
  182. package/docs/{docs/getting-started → src/docs/_getting-started}/create-a-component.md +6 -3
  183. package/docs/src/docs/{getting-started → _getting-started}/my-first-component.md +14 -5
  184. package/docs/src/docs/_getting-started/my-first-subscriber.md +120 -0
  185. package/docs/src/docs/_getting-started/pubsub.md +37 -0
  186. package/docs/src/docs/{getting-started → _getting-started}/start.md +13 -1
  187. package/docs/src/docs/{getting-started → _getting-started}/theming.md +2 -2
  188. package/docs/src/docs/{misc → _misc}/api-configuration.md +12 -10
  189. package/docs/src/docs/{misc → _misc}/dataProviderKey.md +4 -4
  190. package/docs/{docs/misc → src/docs/_misc}/docs-mock-api.md +13 -12
  191. package/docs/{docs/misc → src/docs/_misc}/dynamic-path.md +8 -8
  192. package/docs/{docs/misc → src/docs/_misc}/endpoint.md +8 -8
  193. package/docs/src/docs/_misc/formelement-subscriber-removal.md +115 -0
  194. package/docs/{docs/misc → src/docs/_misc}/html-integration.md +5 -4
  195. package/docs/src/docs/search/docs-search.json +4375 -2420
  196. package/docs/src/tsconfig.declarations.json +22 -0
  197. package/docs/src/tsconfig.emit.json +23 -0
  198. package/docs/src/tsconfig.json +2 -23
  199. package/gitlab/job_github_mirror.sh +39 -0
  200. package/index.html +89 -0
  201. package/package.json +22 -4
  202. package/public/img/og-concorde.jpg +0 -0
  203. package/scripts/ai-init.mjs +104 -379
  204. package/scripts/ai-init.test.mjs +81 -239
  205. package/scripts/docs-markdown-vite-plugin.ts +11 -5
  206. package/scripts/docs-public-url.mjs +6 -0
  207. package/scripts/docs.htaccess +7 -0
  208. package/scripts/generate-cem.mjs +244 -0
  209. package/scripts/generate-cem.test.mjs +122 -0
  210. package/scripts/generate-crawl-docs.mjs +29 -5
  211. package/scripts/generate-sdui-catalog.mjs +11 -10
  212. package/scripts/github-export.mjs +150 -0
  213. package/scripts/post-build-docs.js +3 -0
  214. package/scripts/post-build.mjs +0 -0
  215. package/scripts/pre-publish.mjs +2 -1
  216. package/scripts/site-pages.mjs +212 -0
  217. package/scripts/site-pages.test.mjs +116 -0
  218. package/site-pages/about.md +22 -0
  219. package/site-pages/privacy.md +32 -0
  220. package/src/core/components/functional/date/date.md +1 -1
  221. package/src/core/components/functional/example/example.ts +3 -1
  222. package/src/core/components/functional/if/if.ts +0 -0
  223. package/src/core/components/functional/list/list.demo.ts +0 -0
  224. package/src/core/components/functional/list/list.md +6 -2
  225. package/src/core/components/functional/list/list.ts +17 -4
  226. package/src/core/components/functional/mix/mix.md +0 -0
  227. package/src/core/components/functional/router/router.demo.ts +0 -0
  228. package/src/core/components/functional/router/router.ts +0 -0
  229. package/src/core/components/functional/sdui/SDUIDescriptorTransformer.ts +0 -0
  230. package/src/core/components/functional/sdui/component-catalog.gaps.json +1 -1
  231. package/src/core/components/functional/sdui/component-catalog.json +2 -2
  232. package/src/core/components/functional/sdui/example.json +0 -0
  233. package/src/core/components/functional/sdui/sdui-immutable.spec.ts +74 -0
  234. package/src/core/components/functional/sdui/sdui.ts +33 -8
  235. package/src/core/components/functional/sdui/types.ts +0 -0
  236. package/src/core/components/functional/states/states.ts +0 -0
  237. package/src/core/components/functional/submit/submit.spec.ts +60 -0
  238. package/src/core/components/functional/submit/submit.ts +6 -3
  239. package/src/core/components/functional/subscriber/subscriber.ts +0 -0
  240. package/src/core/components/functional/value/value.md +0 -0
  241. package/src/core/components/functional/value/value.ts +0 -0
  242. package/src/core/components/ui/_css/size.ts +0 -0
  243. package/src/core/components/ui/captcha/altchaStyles.ts +0 -0
  244. package/src/core/components/ui/card/card.md +1 -1
  245. package/src/core/components/ui/form/input/input.md +19 -1
  246. package/src/core/components/ui/form/input/input.ts +8 -2
  247. package/src/core/components/ui/form/radio/radio.md +14 -0
  248. package/src/core/components/ui/image/image.md +17 -17
  249. package/src/core/components/ui/modal/modal.md +2 -2
  250. package/src/core/decorators/subscriber/bind.ts +9 -14
  251. package/src/core/decorators/subscriber/subscribe.ts +15 -9
  252. package/src/core/decorators/subscriber/subscribe.type-test.ts +14 -5
  253. package/src/core/mixins/Subscriber.ts +0 -0
  254. package/src/core/utils/DataBindObserver.ts +0 -0
  255. package/src/core/utils/Objects.ts +0 -0
  256. package/src/core/utils/PublisherProxy.ts +166 -125
  257. package/src/core/utils/deepReadonly.spec.ts +66 -0
  258. package/src/core/utils/deepReadonly.ts +71 -0
  259. package/src/core/utils/immutableValue.spec.ts +54 -0
  260. package/src/core/utils/immutableValue.ts +102 -0
  261. package/src/core/utils/nestedDotForm.spec.ts +88 -0
  262. package/src/core/utils/publisherImmutable.spec.ts +76 -0
  263. package/src/core/utils/route.ts +0 -0
  264. package/src/core/utils/strictGet.spec.ts +61 -0
  265. package/src/core/utils/strictGet.ts +102 -0
  266. package/src/css.d.ts +19 -0
  267. package/src/docs/components/docs-demo-sources.ts +3 -3
  268. package/src/docs/decorators/bind.md +23 -24
  269. package/src/docs/docs-location.spec.ts +10 -0
  270. package/src/docs/docs-location.ts +10 -0
  271. package/src/docs/example/decorators-demo-bind-demos.ts +11 -28
  272. package/src/docs/example/docs-toggle-demos.ts +1 -1
  273. package/src/docs/example/users.ts +0 -0
  274. package/src/docs/getting-started/ai-agents.md +29 -55
  275. package/src/docs/getting-started/dataFlow.md +2 -2
  276. package/src/docs/home/home.ts +3 -1
  277. package/src/docs/misc/publisher-get-immutability-docs-crawl.json +902 -0
  278. package/src/docs/misc/publisher-get-immutability-docs-recheck.json +13 -0
  279. package/src/docs/misc/publisher-get-immutability-study.md +229 -0
  280. package/src/docs/navigation/navigation.ts +5 -1
  281. package/src/docs/search/docs-search.json +358 -58
  282. package/src/docs/search/markdown-renderer.ts +6 -1
  283. package/src/docs/tailwind/css/docs/content.css +4 -5
  284. package/src/tsconfig.declarations.json +22 -0
  285. package/src/tsconfig.emit.json +23 -0
  286. package/src/tsconfig.json +53 -5
  287. package/src/tsconfig.tsbuildinfo +1 -1
  288. package/.taste-skill/after/desktop-fold.jpg +0 -0
  289. package/.taste-skill/after/desktop-page-01.jpg +0 -0
  290. package/.taste-skill/after/desktop-page-02.jpg +0 -0
  291. package/.taste-skill/after/desktop-page-03.jpg +0 -0
  292. package/.taste-skill/after/mobile-fold.jpg +0 -0
  293. package/.taste-skill/after/mobile-page-01.jpg +0 -0
  294. package/.taste-skill/after/report.json +0 -4103
  295. package/.taste-skill/after/tablet-fold.jpg +0 -0
  296. package/.taste-skill/after/tablet-page-01.jpg +0 -0
  297. package/.taste-skill/after/tablet-page-02.jpg +0 -0
  298. package/.taste-skill/before/desktop-fold.jpg +0 -0
  299. package/.taste-skill/before/desktop-page-01.jpg +0 -0
  300. package/.taste-skill/before/desktop-page-02.jpg +0 -0
  301. package/.taste-skill/before/mobile-fold.jpg +0 -0
  302. package/.taste-skill/before/mobile-page-01.jpg +0 -0
  303. package/.taste-skill/before/report.json +0 -2556
  304. package/.taste-skill/before/tablet-fold.jpg +0 -0
  305. package/.taste-skill/before/tablet-page-01.jpg +0 -0
  306. package/.taste-skill/direction.json +0 -510
  307. package/.taste-skill/notes.md +0 -84
  308. package/.taste-skill/prod/desktop-fold.jpg +0 -0
  309. package/.taste-skill/prod/desktop-page-01.jpg +0 -0
  310. package/.taste-skill/prod/desktop-page-02.jpg +0 -0
  311. package/.taste-skill/prod/desktop-page-03.jpg +0 -0
  312. package/.taste-skill/prod/mobile-fold.jpg +0 -0
  313. package/.taste-skill/prod/mobile-page-01.jpg +0 -0
  314. package/.taste-skill/prod/mobile-page-02.jpg +0 -0
  315. package/.taste-skill/prod/report.json +0 -1973
  316. package/.taste-skill/prod/tablet-fold.jpg +0 -0
  317. package/.taste-skill/prod/tablet-page-01.jpg +0 -0
  318. package/.taste-skill/prod/tablet-page-02.jpg +0 -0
  319. package/AUDIT.md +0 -228
  320. package/PLAN-AMELIORATION.md +0 -220
  321. package/PLAN-DOCS-AGENTS.md +0 -80
  322. package/ai/AGENTS.md +0 -56
  323. package/ai/cursor/rules/concorde-menu.mdc +0 -15
  324. package/ai/cursor/rules/concorde-scope.mdc +0 -14
  325. package/ai/cursor/rules/concorde-theme.mdc +0 -13
  326. package/ai/cursor/rules/concorde.mdc +0 -66
  327. package/ai/jetbrains/rules/concorde.md +0 -53
  328. package/ai/skills/concorde-get-set-dp/SKILL.md +0 -194
  329. package/ai/skills/concorde-imports/SKILL.md +0 -78
  330. package/ai/skills/concorde-menu/SKILL.md +0 -74
  331. package/ai/skills/concorde-scope/SKILL.md +0 -70
  332. package/ai/skills/concorde-theme/SKILL.md +0 -57
  333. package/docs/.htaccess +0 -75
  334. package/docs/404.html +0 -15
  335. package/docs/404.md +0 -5
  336. package/docs/assets/index-BgG6FFPt.js +0 -11765
  337. package/docs/assets/index-x89XINSG.css +0 -1
  338. package/docs/core/components/functional/date/date.html +0 -211
  339. package/docs/core/components/functional/date/date.md +0 -150
  340. package/docs/core/components/functional/fetch/fetch.html +0 -180
  341. package/docs/core/components/functional/fetch/fetch.md +0 -108
  342. package/docs/core/components/functional/if/if.html +0 -65
  343. package/docs/core/components/functional/if/if.md +0 -34
  344. package/docs/core/components/functional/jsonata/jsonata.html +0 -110
  345. package/docs/core/components/functional/jsonata/jsonata.md +0 -61
  346. package/docs/core/components/functional/list/list.html +0 -198
  347. package/docs/core/components/functional/list/list.md +0 -121
  348. package/docs/core/components/functional/mix/mix.html +0 -78
  349. package/docs/core/components/functional/mix/mix.md +0 -46
  350. package/docs/core/components/functional/queue/queue.html +0 -155
  351. package/docs/core/components/functional/queue/queue.md +0 -51
  352. package/docs/core/components/functional/router/router.html +0 -154
  353. package/docs/core/components/functional/router/router.md +0 -93
  354. package/docs/core/components/functional/sdui/sdui-extension-points.md +0 -94
  355. package/docs/core/components/functional/sdui/sdui.html +0 -397
  356. package/docs/core/components/functional/sdui/sdui.md +0 -307
  357. package/docs/core/components/functional/states/states.html +0 -141
  358. package/docs/core/components/functional/states/states.md +0 -112
  359. package/docs/core/components/functional/submit/submit.html +0 -178
  360. package/docs/core/components/functional/submit/submit.md +0 -106
  361. package/docs/core/components/functional/subscriber/subscriber.md +0 -91
  362. package/docs/core/components/functional/translation/translation.html +0 -60
  363. package/docs/core/components/functional/translation/translation.md +0 -38
  364. package/docs/core/components/functional/value/value.html +0 -81
  365. package/docs/core/components/functional/value/value.md +0 -47
  366. package/docs/core/components/ui/alert/alert.md +0 -186
  367. package/docs/core/components/ui/alert-messages/alert-messages.md +0 -85
  368. package/docs/core/components/ui/badge/badge.md +0 -181
  369. package/docs/core/components/ui/button/button.html +0 -309
  370. package/docs/core/components/ui/button/button.md +0 -216
  371. package/docs/core/components/ui/captcha/captcha.html +0 -80
  372. package/docs/core/components/ui/captcha/captcha.md +0 -56
  373. package/docs/core/components/ui/card/card.md +0 -215
  374. package/docs/core/components/ui/divider/divider.md +0 -126
  375. package/docs/core/components/ui/form/checkbox/checkbox.html +0 -200
  376. package/docs/core/components/ui/form/checkbox/checkbox.md +0 -125
  377. package/docs/core/components/ui/form/fieldset/fieldset.html +0 -238
  378. package/docs/core/components/ui/form/fieldset/fieldset.md +0 -161
  379. package/docs/core/components/ui/form/form-actions/form-actions.md +0 -98
  380. package/docs/core/components/ui/form/form-layout/form-layout.html +0 -115
  381. package/docs/core/components/ui/form/form-layout/form-layout.md +0 -109
  382. package/docs/core/components/ui/form/input/input.html +0 -235
  383. package/docs/core/components/ui/form/input/input.md +0 -142
  384. package/docs/core/components/ui/form/radio/radio.html +0 -144
  385. package/docs/core/components/ui/form/radio/radio.md +0 -78
  386. package/docs/core/components/ui/form/select/select.html +0 -178
  387. package/docs/core/components/ui/form/select/select.md +0 -86
  388. package/docs/core/components/ui/form/switch/switch.html +0 -146
  389. package/docs/core/components/ui/form/switch/switch.md +0 -80
  390. package/docs/core/components/ui/form/textarea/textarea.html +0 -162
  391. package/docs/core/components/ui/form/textarea/textarea.md +0 -82
  392. package/docs/core/components/ui/group/group.html +0 -113
  393. package/docs/core/components/ui/group/group.md +0 -77
  394. package/docs/core/components/ui/icon/icon.md +0 -214
  395. package/docs/core/components/ui/image/image.html +0 -194
  396. package/docs/core/components/ui/image/image.md +0 -167
  397. package/docs/core/components/ui/link/link.md +0 -81
  398. package/docs/core/components/ui/loader/loader.html +0 -114
  399. package/docs/core/components/ui/loader/loader.md +0 -87
  400. package/docs/core/components/ui/menu/menu.md +0 -370
  401. package/docs/core/components/ui/modal/modal.html +0 -324
  402. package/docs/core/components/ui/modal/modal.md +0 -211
  403. package/docs/core/components/ui/pop/pop.html +0 -250
  404. package/docs/core/components/ui/pop/pop.md +0 -216
  405. package/docs/core/components/ui/progress/progress.md +0 -109
  406. package/docs/core/components/ui/table/table.html +0 -333
  407. package/docs/core/components/ui/table/table.md +0 -267
  408. package/docs/core/components/ui/theme/theme.html +0 -116
  409. package/docs/core/components/ui/theme/theme.md +0 -97
  410. package/docs/core/components/ui/toast/toast.html +0 -185
  411. package/docs/core/components/ui/toast/toast.md +0 -147
  412. package/docs/core/components/ui/tooltip/tooltip.html +0 -112
  413. package/docs/core/components/ui/tooltip/tooltip.md +0 -89
  414. package/docs/docs/decorators/ancestor-attribute.html +0 -71
  415. package/docs/docs/decorators/ancestor-attribute.md +0 -59
  416. package/docs/docs/decorators/auto-subscribe.html +0 -61
  417. package/docs/docs/decorators/auto-subscribe.md +0 -40
  418. package/docs/docs/decorators/bind.html +0 -86
  419. package/docs/docs/decorators/bind.md +0 -77
  420. package/docs/docs/decorators/get.html +0 -166
  421. package/docs/docs/decorators/get.md +0 -160
  422. package/docs/docs/decorators/handle.html +0 -139
  423. package/docs/docs/decorators/handle.md +0 -119
  424. package/docs/docs/decorators/on-assign.html +0 -72
  425. package/docs/docs/decorators/on-assign.md +0 -63
  426. package/docs/docs/decorators/patch.html +0 -76
  427. package/docs/docs/decorators/patch.md +0 -44
  428. package/docs/docs/decorators/post.html +0 -150
  429. package/docs/docs/decorators/post.md +0 -134
  430. package/docs/docs/decorators/publish.html +0 -74
  431. package/docs/docs/decorators/publish.md +0 -56
  432. package/docs/docs/decorators/put.html +0 -83
  433. package/docs/docs/decorators/put.md +0 -51
  434. package/docs/docs/decorators/subscribe.html +0 -91
  435. package/docs/docs/decorators/subscribe.md +0 -87
  436. package/docs/docs/decorators/wait-for-ancestors.html +0 -82
  437. package/docs/docs/decorators/wait-for-ancestors.md +0 -76
  438. package/docs/docs/directives/sub.html +0 -79
  439. package/docs/docs/directives/sub.md +0 -75
  440. package/docs/docs/directives/t.html +0 -91
  441. package/docs/docs/directives/t.md +0 -73
  442. package/docs/docs/getting-started/ai-agents.md +0 -76
  443. package/docs/docs/getting-started/components.html +0 -27
  444. package/docs/docs/getting-started/components.md +0 -5
  445. package/docs/docs/getting-started/installation.html +0 -69
  446. package/docs/docs/getting-started/installation.md +0 -36
  447. package/docs/docs/getting-started/my-first-component.md +0 -227
  448. package/docs/docs/getting-started/start.html +0 -47
  449. package/docs/docs/getting-started/start.md +0 -35
  450. package/docs/docs/getting-started/theming.md +0 -253
  451. package/docs/docs/index.html +0 -99
  452. package/docs/docs/misc/api-configuration.md +0 -79
  453. package/docs/docs/misc/dataProviderKey.md +0 -168
  454. package/docs/hubs/decorators.html +0 -443
  455. package/docs/hubs/form-components.html +0 -404
  456. package/docs/hubs/remote-data.html +0 -371
  457. package/docs/img/archive/concorde-001-2cv.webp +0 -0
  458. package/docs/img/archive/concorde-red-arrows-2002-poster.jpg +0 -0
  459. package/docs/img/archive/concorde-red-arrows-2002.webm +0 -0
  460. package/docs/img/archive/credits.json +0 -28
  461. package/docs/index.md +0 -22
  462. package/docs/src/core/components/functional/translation/translation.md +0 -38
  463. package/docs/src/docs/decorators/ancestor-attribute.md +0 -59
  464. package/docs/src/docs/decorators/auto-subscribe.md +0 -40
  465. package/docs/src/docs/decorators/bind.md +0 -77
  466. package/docs/src/docs/decorators/get.md +0 -160
  467. package/docs/src/docs/decorators/handle.md +0 -119
  468. package/docs/src/docs/decorators/on-assign.md +0 -63
  469. package/docs/src/docs/decorators/patch.md +0 -44
  470. package/docs/src/docs/decorators/post.md +0 -134
  471. package/docs/src/docs/decorators/publish.md +0 -56
  472. package/docs/src/docs/decorators/put.md +0 -51
  473. package/docs/src/docs/decorators/subscribe.md +0 -87
  474. package/docs/src/docs/decorators/wait-for-ancestors.md +0 -76
  475. package/docs/src/docs/directives/sub.md +0 -75
  476. package/docs/src/docs/directives/t.md +0 -73
  477. package/docs/src/docs/getting-started/components.md +0 -5
  478. package/docs/src/docs/getting-started/create-a-component.md +0 -136
  479. package/docs/src/docs/getting-started/dataFlow.md +0 -87
  480. package/docs/src/docs/getting-started/installation.md +0 -36
  481. package/docs/src/docs/misc/docs-mock-api.md +0 -62
  482. package/docs/src/docs/misc/dynamic-path.md +0 -71
  483. package/docs/src/docs/misc/endpoint.md +0 -45
  484. package/docs/src/docs/misc/html-integration.md +0 -14
  485. /package/{docs → dist}/img/concorde/plane_01.jpg +0 -0
  486. /package/{docs → dist}/img/concorde/plane_02.jpg +0 -0
  487. /package/{docs → dist}/img/concorde/plane_03.jpg +0 -0
  488. /package/{docs → dist}/img/concorde/plane_04.jpg +0 -0
  489. /package/{docs → dist}/img/concorde/plane_05.jpg +0 -0
  490. /package/{docs → dist}/img/concorde/plane_06.jpg +0 -0
  491. /package/{docs → dist}/img/concorde/plane_07.jpg +0 -0
  492. /package/{docs → dist}/img/concorde/plane_08.jpg +0 -0
  493. /package/{docs → dist}/img/concorde/plane_09.jpg +0 -0
  494. /package/{docs → dist}/img/concorde/plane_10.jpg +0 -0
  495. /package/{docs → dist}/img/concorde/plane_11.jpg +0 -0
  496. /package/{docs → dist}/img/concorde/plane_12.jpg +0 -0
  497. /package/{docs → dist}/img/concorde/plane_13.jpg +0 -0
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Ownership helpers for the immutable DataProvider store (REDMINE-79567).
3
+ * External values are cloned then deep-frozen; internal updates rebuild via spread.
4
+ */
5
+
6
+ const warnedUnclonable = new WeakSet<object>();
7
+
8
+ export function isFrozenValue(value: unknown): boolean {
9
+ return value != null && typeof value === "object" && Object.isFrozen(value);
10
+ }
11
+
12
+ /** Deep freeze (in place). Safe to call on already-frozen graphs. */
13
+ export function deepFreeze<T>(value: T): T {
14
+ if (value == null || typeof value !== "object" || Object.isFrozen(value)) {
15
+ return value;
16
+ }
17
+ Object.freeze(value);
18
+ if (Array.isArray(value)) {
19
+ for (const item of value) deepFreeze(item);
20
+ } else {
21
+ for (const key of Reflect.ownKeys(value as object)) {
22
+ deepFreeze((value as Record<string | symbol, unknown>)[key]);
23
+ }
24
+ }
25
+ return value;
26
+ }
27
+
28
+ /**
29
+ * Clone so the publisher owns the data, then freeze.
30
+ * Non-cloneable values (EventSource, etc.): kept as-is, not frozen (warned once).
31
+ */
32
+ export function takeOwnership<T>(value: T): T {
33
+ if (value == null || typeof value !== "object") return value;
34
+ if (isFrozenValue(value)) return value;
35
+ try {
36
+ return deepFreeze(structuredClone(value));
37
+ } catch {
38
+ if (!warnedUnclonable.has(value as object)) {
39
+ warnedUnclonable.add(value as object);
40
+ console.warn(
41
+ "[Concorde] takeOwnership: value is not structuredClone-able; stored without freeze.",
42
+ );
43
+ }
44
+ return value;
45
+ }
46
+ }
47
+
48
+ /** Drop `undefined` own keys on plain objects (before freeze). Arrays unchanged. */
49
+ export function stripUndefinedKeys<T>(value: T): T {
50
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
51
+ return value;
52
+ }
53
+ if (Object.prototype.hasOwnProperty.call(value, "__value")) return value;
54
+ // Keep frozen / already-owned graphs intact (set(get()) identity).
55
+ if (Object.isFrozen(value)) return value;
56
+ let needsStrip = false;
57
+ for (const key of Object.keys(value as object)) {
58
+ if ((value as Record<string, unknown>)[key] === undefined) {
59
+ needsStrip = true;
60
+ break;
61
+ }
62
+ }
63
+ if (!needsStrip) return value;
64
+ const out: Record<string, unknown> = { ...(value as object) };
65
+ for (const key of Object.keys(out)) {
66
+ if (out[key] === undefined) delete out[key];
67
+ }
68
+ return out as T;
69
+ }
70
+
71
+ /** Shallow rebuild with one key set — result is deep-frozen. Nested `value` should already be owned. */
72
+ export function setAtKey<T extends object>(
73
+ root: T | null | undefined,
74
+ key: string | number,
75
+ value: unknown,
76
+ ): T {
77
+ let next: any;
78
+ if (root == null) {
79
+ next = typeof key === "number" || !isNaN(Number(key)) ? [] : {};
80
+ } else if (Array.isArray(root)) {
81
+ next = [...root];
82
+ } else {
83
+ next = { ...(root as object) };
84
+ }
85
+ next[key as any] = value;
86
+ return deepFreeze(next) as T;
87
+ }
88
+
89
+ /** Shallow rebuild without one key — result is deep-frozen. */
90
+ export function deleteAtKey<T extends object>(
91
+ root: T,
92
+ key: string | number,
93
+ ): T {
94
+ if (Array.isArray(root)) {
95
+ const next = [...root];
96
+ delete next[key as any];
97
+ return deepFreeze(next) as T;
98
+ }
99
+ const next = { ...(root as object) } as Record<string, unknown>;
100
+ delete next[String(key)];
101
+ return deepFreeze(next) as T;
102
+ }
@@ -0,0 +1,88 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { PublisherManager, get, set } from "./PublisherProxy";
3
+ import { traverseDotNotation } from "./Objects";
4
+
5
+ afterEach(() => {
6
+ PublisherManager.getInstance().delete("nested-dot-form");
7
+ });
8
+
9
+ describe("nested dot form publishers (immutable)", () => {
10
+ it("set via traverseDotNotation builds nested object", () => {
11
+ const form = PublisherManager.get("nested-dot-form");
12
+ form.set({});
13
+ traverseDotNotation(form as any, "passenger.seat").set("window");
14
+ expect(get("nested-dot-form")).toEqual({ passenger: { seat: "window" } });
15
+ });
16
+
17
+ it("successive nested sets keep siblings", () => {
18
+ const form = PublisherManager.get("nested-dot-form");
19
+ form.set({});
20
+ traverseDotNotation(form as any, "passenger.name").set("Ada");
21
+ traverseDotNotation(form as any, "passenger.seat").set("aisle");
22
+ expect(get("nested-dot-form")).toEqual({
23
+ passenger: { name: "Ada", seat: "aisle" },
24
+ });
25
+ });
26
+
27
+ it("nested get after set is readable", () => {
28
+ set("nested-dot-form", { passenger: { seat: "window" } });
29
+ const form = PublisherManager.get("nested-dot-form");
30
+ expect(traverseDotNotation(form as any, "passenger.seat").get()).toBe(
31
+ "window",
32
+ );
33
+ });
34
+
35
+ it("deep leaf set from empty root creates intermediates (frozen)", () => {
36
+ const form = PublisherManager.get("nested-dot-form");
37
+ traverseDotNotation(form as any, "passenger.seat").set("window");
38
+ const data = get("nested-dot-form") as { passenger: { seat: string } };
39
+ expect(data).toEqual({ passenger: { seat: "window" } });
40
+ expect(Object.isFrozen(data)).toBe(true);
41
+ expect(Object.isFrozen(data.passenger)).toBe(true);
42
+ });
43
+
44
+ it("switching nested radio leaf replaces value", () => {
45
+ const form = PublisherManager.get("nested-dot-form");
46
+ traverseDotNotation(form as any, "passenger.seat").set("window");
47
+ traverseDotNotation(form as any, "passenger.seat").set("aisle");
48
+ expect(get("nested-dot-form")).toEqual({ passenger: { seat: "aisle" } });
49
+ });
50
+
51
+ it("onAssign on nested path receives scalar", () => {
52
+ const form = PublisherManager.get("nested-dot-form");
53
+ const seen: unknown[] = [];
54
+ traverseDotNotation(form as any, "passenger.seat").onAssign((v: unknown) => {
55
+ seen.push(v);
56
+ });
57
+ traverseDotNotation(form as any, "passenger.seat").set("window");
58
+ expect(seen).toContain("window");
59
+ });
60
+
61
+ it("reading nested before set returns null", () => {
62
+ const form = PublisherManager.get("nested-dot-form");
63
+ expect(traverseDotNotation(form as any, "passenger.seat").get()).toBeNull();
64
+ });
65
+
66
+ it("updating leaf after frozen parent set keeps siblings", () => {
67
+ set("nested-dot-form", { passenger: { name: "Ada", seat: "window" } });
68
+ const form = PublisherManager.get("nested-dot-form");
69
+ traverseDotNotation(form as any, "passenger.seat").set("aisle");
70
+ expect(get("nested-dot-form")).toEqual({
71
+ passenger: { name: "Ada", seat: "aisle" },
72
+ });
73
+ });
74
+
75
+ it("checkbox-like array replace via slice keeps frozen parent", () => {
76
+ const form = PublisherManager.get("nested-dot-form");
77
+ traverseDotNotation(form as any, "passenger.tags").set(["a"]);
78
+ const current = traverseDotNotation(form as any, "passenger.tags").get() as
79
+ | string[]
80
+ | null;
81
+ const next = (current ?? []).slice(0);
82
+ next.push("b");
83
+ traverseDotNotation(form as any, "passenger.tags").set(next);
84
+ expect(get("nested-dot-form")).toEqual({
85
+ passenger: { tags: ["a", "b"] },
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,76 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { DataProviderKey } from "./dataProviderKey";
3
+ import { dp, get, PublisherManager, set } from "./PublisherProxy";
4
+
5
+ type Cart = { items: { id: number }[]; total: number };
6
+ type Row = { id: number; _metadata_?: { key: string; even: boolean } };
7
+
8
+ describe("immutable DataProvider store (79567)", () => {
9
+ const cartKey = new DataProviderKey<Cart>("publisherImmutableCart");
10
+
11
+ afterEach(() => {
12
+ PublisherManager.getInstance().delete(cartKey.path);
13
+ PublisherManager.getInstance().delete("publisherImmutableListLoop");
14
+ });
15
+
16
+ it("set takes ownership — mutating the input does not change the store", () => {
17
+ const input: Cart = { items: [{ id: 1 }], total: 1 };
18
+ set(cartKey, input);
19
+ input.total = 99;
20
+ input.items.push({ id: 2 });
21
+ expect(get(cartKey).total).toBe(1);
22
+ expect(get(cartKey).items).toHaveLength(1);
23
+ });
24
+
25
+ it("get returns a frozen value (same ref until next set)", () => {
26
+ set(cartKey, { items: [], total: 0 });
27
+ const a = get(cartKey);
28
+ const b = get(cartKey);
29
+ expect(a).toBe(b);
30
+ expect(Object.isFrozen(a)).toBe(true);
31
+ expect(() => {
32
+ (a as Cart).total = 5;
33
+ }).toThrow();
34
+ expect(get(cartKey).total).toBe(0);
35
+ });
36
+
37
+ it("nested dp().set updates parent immutably", () => {
38
+ set(cartKey, { items: [{ id: 1 }], total: 0 });
39
+ dp(cartKey.total).set(10);
40
+ expect(get(cartKey).total).toBe(10);
41
+ expect(Object.isFrozen(get(cartKey))).toBe(true);
42
+ });
43
+
44
+ it("publisher property assign goes through immutable set", () => {
45
+ set(cartKey, { items: [], total: 0 });
46
+ const pub = dp(cartKey);
47
+ (pub as any).total = 4;
48
+ expect(get(cartKey).total).toBe(4);
49
+ });
50
+
51
+ it("set(get()) is a no-op identity keep", () => {
52
+ set(cartKey, { items: [{ id: 1 }], total: 1 });
53
+ const snap = get(cartKey);
54
+ set(cartKey, snap);
55
+ expect(get(cartKey)).toBe(snap);
56
+ });
57
+
58
+ it("re-assigning the same nested value does not re-publish the parent", () => {
59
+ const listKey = new DataProviderKey<Row[]>("publisherImmutableListLoop");
60
+ set(listKey, [{ id: 1 }]);
61
+ const row = dp(listKey)[0];
62
+ const meta = { key: "0", even: true };
63
+ row._metadata_ = meta as any;
64
+
65
+ const parentAssign = vi.fn();
66
+ dp(listKey).onAssign(parentAssign, false);
67
+ parentAssign.mockClear();
68
+
69
+ // Same logical metadata again (new object) would rebuild without the list guard;
70
+ // engine must still skip parent sync when the child commits a value already at parent[key].
71
+ expect(row._metadata_).toBeDefined();
72
+ const ownedMeta = row._metadata_!.get();
73
+ row._metadata_!.set(ownedMeta as any);
74
+ expect(parentAssign).not.toHaveBeenCalled();
75
+ });
76
+ });
File without changes
@@ -0,0 +1,61 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { DataProviderKey } from "./dataProviderKey";
3
+ import {
4
+ clearStrictGetWarnings,
5
+ PublisherManager,
6
+ dp,
7
+ get,
8
+ set,
9
+ } from "./PublisherProxy";
10
+
11
+ type Cart = { items: { id: number }[]; total: number };
12
+
13
+ describe("strictGet runtime guard (79567)", () => {
14
+ const cartKey = new DataProviderKey<Cart>("strictGetSpecCart");
15
+
16
+ afterEach(() => {
17
+ PublisherManager.strictGet = "off";
18
+ clearStrictGetWarnings();
19
+ vi.restoreAllMocks();
20
+ });
21
+
22
+ it("off: store is frozen — assignment via get() throws", () => {
23
+ PublisherManager.strictGet = "off";
24
+ set(cartKey, { items: [{ id: 1 }], total: 1 });
25
+ const cart = get(cartKey) as Cart;
26
+ expect(() => {
27
+ cart.total = 7;
28
+ }).toThrow();
29
+ expect(get(cartKey).total).toBe(1);
30
+ });
31
+
32
+ it("warn: logs once per path; mutation still blocked by freeze", () => {
33
+ PublisherManager.strictGet = "warn";
34
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
35
+ set(cartKey, { items: [{ id: 1 }], total: 1 });
36
+ const cart = get(cartKey) as Cart;
37
+ expect(() => {
38
+ cart.total = 2;
39
+ }).toThrow();
40
+ expect(warn).toHaveBeenCalled();
41
+ expect(String(warn.mock.calls[0]?.[0])).toContain("strictGet=warn");
42
+ expect(get(cartKey).total).toBe(1);
43
+ });
44
+
45
+ it("throw: TypeError from strictGet before freeze", () => {
46
+ PublisherManager.strictGet = "throw";
47
+ set(cartKey, { items: [{ id: 1 }], total: 1 });
48
+ const cart = get(cartKey) as Cart;
49
+ expect(() => {
50
+ cart.total = 9;
51
+ }).toThrow(/strictGet=throw/);
52
+ expect(dp(cartKey).get().total).toBe(1);
53
+ });
54
+
55
+ it("set() still works while strictGet=throw", () => {
56
+ PublisherManager.strictGet = "throw";
57
+ set(cartKey, { items: [], total: 0 });
58
+ set(cartKey, { items: [{ id: 1 }], total: 1 });
59
+ expect(get(cartKey).total).toBe(1);
60
+ });
61
+ });
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Runtime guard for values returned by publisher `get()` (REDMINE-79567).
3
+ * Wraps objects/arrays in a Proxy — does **not** freeze live `_value_`.
4
+ * Engine writes that touch the target directly still work.
5
+ *
6
+ * - `off` — no wrap (same ref as today)
7
+ * - `warn` — log once per path, mutation still applied (discovery)
8
+ * - `throw` — TypeError, mutation blocked (CI / hardening)
9
+ */
10
+
11
+ export type StrictGetMode = "off" | "warn" | "throw";
12
+
13
+ let strictGetMode: StrictGetMode = "off";
14
+ const warnedKeys = new Set<string>();
15
+ const proxyCache = new WeakMap<object, object>();
16
+
17
+ export function getStrictGetMode(): StrictGetMode {
18
+ return strictGetMode;
19
+ }
20
+
21
+ export function setStrictGetMode(mode: StrictGetMode): void {
22
+ strictGetMode = mode;
23
+ }
24
+
25
+ /** Test helper — clears once-only warn dedupe. */
26
+ export function clearStrictGetWarnings(): void {
27
+ warnedKeys.clear();
28
+ }
29
+
30
+ function report(mode: "warn" | "throw", path: string): void {
31
+ const message = `[Concorde strictGet=${mode}] Mutation via get() at "${path || "(root)"}". Use set() / dp(…).set(…) instead.`;
32
+ if (mode === "throw") {
33
+ throw new TypeError(message);
34
+ }
35
+ if (warnedKeys.has(path)) return;
36
+ warnedKeys.add(path);
37
+ console.warn(message);
38
+ }
39
+
40
+ function wrapDeep<T extends object>(target: T, path: string): T {
41
+ const cached = proxyCache.get(target);
42
+ if (cached) return cached as T;
43
+
44
+ const proxy = new Proxy(target, {
45
+ get(obj, prop, receiver) {
46
+ if (prop === "__strictGetTarget__") return target;
47
+ const value = Reflect.get(obj, prop, receiver);
48
+ if (value == null || typeof value !== "object") return value;
49
+ if (getStrictGetMode() === "off") return value;
50
+ const childPath =
51
+ path === "" ? String(prop) : `${path}.${String(prop)}`;
52
+ return wrapDeep(value as object, childPath);
53
+ },
54
+ set(obj, prop, value, receiver) {
55
+ const mode = getStrictGetMode();
56
+ if (mode === "off") {
57
+ return Reflect.set(obj, prop, value, receiver);
58
+ }
59
+ const childPath =
60
+ path === "" ? String(prop) : `${path}.${String(prop)}`;
61
+ report(mode, childPath);
62
+ if (mode === "throw") return false;
63
+ return Reflect.set(obj, prop, value, receiver);
64
+ },
65
+ deleteProperty(obj, prop) {
66
+ const mode = getStrictGetMode();
67
+ if (mode === "off") {
68
+ return Reflect.deleteProperty(obj, prop);
69
+ }
70
+ const childPath =
71
+ path === "" ? String(prop) : `${path}.${String(prop)}`;
72
+ report(mode, childPath);
73
+ if (mode === "throw") return false;
74
+ return Reflect.deleteProperty(obj, prop);
75
+ },
76
+ defineProperty(obj, prop, desc) {
77
+ const mode = getStrictGetMode();
78
+ if (mode === "off") {
79
+ return Reflect.defineProperty(obj, prop, desc);
80
+ }
81
+ const childPath =
82
+ path === "" ? String(prop) : `${path}.${String(prop)}`;
83
+ report(mode, childPath);
84
+ if (mode === "throw") return false;
85
+ return Reflect.defineProperty(obj, prop, desc);
86
+ },
87
+ });
88
+
89
+ proxyCache.set(target, proxy);
90
+ return proxy;
91
+ }
92
+
93
+ /**
94
+ * When mode is off or value is not an object, returns value as-is (same ref).
95
+ * Otherwise returns a deep Proxy over the **same** target (no clone).
96
+ */
97
+ export function wrapStrictGet<T>(value: T): T {
98
+ if (getStrictGetMode() === "off" || value == null || typeof value !== "object") {
99
+ return value;
100
+ }
101
+ return wrapDeep(value as object, "") as T;
102
+ }
package/src/css.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ declare module "*.css" {
2
+ const css: import("lit").CSSResult;
3
+ export default css;
4
+ }
5
+
6
+ declare module "*.css?inline" {
7
+ const css: string;
8
+ export default css;
9
+ }
10
+
11
+ declare module "*.scss" {
12
+ const css: import("lit").CSSResult;
13
+ export default css;
14
+ }
15
+
16
+ declare module "*.scss?inline" {
17
+ const css: string;
18
+ export default css;
19
+ }
@@ -342,12 +342,12 @@ export const DOCS_DEMO_SOURCE_REGISTRY: Record<string, DocsSource[]> = {
342
342
  },
343
343
  ],
344
344
  "demo-bind": [
345
- { path: "src/docs/example/decorators-demo-bind-demos.ts", line: 32, label: "demo-bind" },
345
+ { path: "src/docs/example/decorators-demo-bind-demos.ts", line: 36, label: "demo-bind" },
346
346
  ],
347
347
  "demo-bind-reflect": [
348
348
  {
349
349
  path: "src/docs/example/decorators-demo-bind-demos.ts",
350
- line: 70,
350
+ line: 74,
351
351
  label: "demo-bind-reflect",
352
352
  },
353
353
  { path: "src/docs/example/decorators-demo-init.ts", label: "demo data" },
@@ -355,7 +355,7 @@ export const DOCS_DEMO_SOURCE_REGISTRY: Record<string, DocsSource[]> = {
355
355
  "demo-bind-dynamic": [
356
356
  {
357
357
  path: "src/docs/example/decorators-demo-bind-demos.ts",
358
- line: 130,
358
+ line: 113,
359
359
  label: "demo-bind-dynamic",
360
360
  },
361
361
  ],
@@ -1,22 +1,26 @@
1
1
  # @bind
2
2
 
3
- Keeps a class property in sync with a path of the DataProvider store, in both directions when needed. Pass a dot-notation string or a [DataProviderKey](/docs/misc/dataProviderKey); add `{ reflect: true }` to push local writes back to the store. For read-only sync prefer [@subscribe](/docs/decorators/subscribe), and see [Data flow](/docs/getting-started/dataFlow) for the overall model.
3
+ `@bind(key)` keeps a class property and one path of the DataProvider store in sync. With `{ reflect: true }` the sync goes both ways: the property follows the store, and each assignment is written back to it. For one direction only, use the complementary decorators: [@subscribe](/docs/decorators/subscribe) to read, [@publish](/docs/decorators/publish) to write. See [Data flow](/docs/getting-started/dataFlow) for the overall model.
4
4
 
5
5
  ## Import
6
6
 
7
7
  <sonic-code language="typescript">
8
8
  <template>
9
9
  import { bind } from "@supersoniks/concorde/decorators";
10
+ import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
11
+
12
+ type Flight = { flight: string; gate: string; boarded: number };
13
+ const flightKey = new DataProviderKey&lt;Flight&gt;("flight");
10
14
 
11
- @bind("flight.gate")
15
+ @bind(flightKey.boarded, { reflect: true })
12
16
  @state()
13
- gate = "";
17
+ boarded = 0;
14
18
  </template>
15
19
  </sonic-code>
16
20
 
17
21
  ## Basic usage
18
22
 
19
- The first path segment is the data provider id, the rest is dot notation (array items use a numeric index: `passengers.0`). The property updates each time the store value changes. Add `@state()` so Lit re-renders.
23
+ The key is a [DataProviderKey](/docs/misc/dataProviderKey): TypeScript checks that the property type matches `T` at that path. The property updates each time the store value changes. Add `@state()` so Lit re-renders.
20
24
 
21
25
  <sonic-code>
22
26
  <template>
@@ -25,26 +29,9 @@ The first path segment is the data provider id, the rest is dot notation (array
25
29
  </template>
26
30
  </sonic-code>
27
31
 
28
- ## Typed key
29
-
30
- With a `DataProviderKey<T>`, TypeScript checks that the property type matches `T` at that path.
31
-
32
- <sonic-code language="typescript">
33
- <template>
34
- import { DataProviderKey } from "@supersoniks/concorde/dataProviderKey";
35
-
36
- type Flight = { flight: string; gate: string; boarded: number };
37
- const flightKey = new DataProviderKey&lt;Flight&gt;("flight");
38
-
39
- @bind(flightKey.boarded, { reflect: true })
40
- @state()
41
- boarded = 0;
42
- </template>
43
- </sonic-code>
44
-
45
32
  ## Reflect
46
33
 
47
- By default (`reflect: false`) the binding is one-way: assigning the property does not touch the store. With `reflect: true`, each assignment calls `set()` on the publisher, so every other subscriber sees the new value. An internal guard prevents update loops.
34
+ With `reflect: true`, each assignment to the property calls `set()` at the key path, so every other subscriber sees the new value. An internal guard prevents update loops. Without `reflect` (the default), assigning the property does not touch the store, and `@bind` behaves like `@subscribe`.
48
35
 
49
36
  <sonic-code>
50
37
  <template>
@@ -55,7 +42,19 @@ By default (`reflect: false`) the binding is one-way: assigning the property doe
55
42
 
56
43
  ## Dynamic path
57
44
 
58
- Placeholders such as `${index}` are resolved from properties of the same component, and `@bind` re-subscribes when they change. Write them in a plain string, not a backtick template literal. While a placeholder is `null` or `undefined` the binding is inactive; `{ skipEmptyPlaceholder: true }` also waits on `""`. Full rules: [Dynamic paths](/docs/misc/dynamic-path).
45
+ Placeholders such as `${index}` are resolved from properties of the same component, and `@bind` re-subscribes when they change. Declare them in the key's second generic and write them in a plain string, not a backtick template literal. While a placeholder is `null` or `undefined` the binding is inactive; `{ skipEmptyPlaceholder: true }` also waits on `""`. Full rules: [Dynamic paths](/docs/misc/dynamic-path).
46
+
47
+ <sonic-code language="typescript">
48
+ <template>
49
+ const personKey = new DataProviderKey&lt;Person, { dataProvider: string; index: number }&gt;(
50
+ "${dataProvider}.${index}",
51
+ );
52
+
53
+ @bind(personKey)
54
+ @state()
55
+ person: Person | null = null;
56
+ </template>
57
+ </sonic-code>
59
58
 
60
59
  <sonic-code>
61
60
  <template>
@@ -72,6 +71,6 @@ The decorator subscribes in `connectedCallback` and unsubscribes in `disconnecte
72
71
 
73
72
  | Argument | Type | Description |
74
73
  |----------|------|-------------|
75
- | `path` | `string` or `DataProviderKey<T>` | Store path to bind. Supports `${prop}` placeholders |
74
+ | `key` | `DataProviderKey<T>` | Store path to bind. Supports `${prop}` placeholders |
76
75
  | `options.reflect` | `boolean` (default `false`) | Write local assignments back to the store |
77
76
  | `options.skipEmptyPlaceholder` | `boolean` (default `false`) | Treat a placeholder resolved to `""` as not ready |
@@ -19,6 +19,16 @@ describe("docsPagePath / docsPageHref", () => {
19
19
  });
20
20
 
21
21
  describe("parseDocsLocation", () => {
22
+ it("maps root site pages to their markdown", () => {
23
+ const r = parseDocsLocation("/about");
24
+ expect(r.url).toBe("about.md");
25
+ expect(r.pagePath).toBe("/about");
26
+ expect(parseDocsLocation("/privacy#your-rights").anchor).toBe("your-rights");
27
+ expect(parseDocsLocation("/about/").url).toBe("about.md");
28
+ expect(parseDocsLocation("/developers").url).toBe("");
29
+ expect(parseDocsLocation("/unknown").url).toBe("");
30
+ });
31
+
22
32
  it("parses pretty pathname doc URLs", () => {
23
33
  const r = parseDocsLocation("/docs/getting-started/start");
24
34
  expect(r.url).toBe("docs/getting-started/start.md");
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import {
12
+ SITE_PAGE_SLUGS,
12
13
  toPublicMdRel,
13
14
  toPublicPath,
14
15
  toSourceId,
@@ -107,6 +108,15 @@ export function parseDocsLocation(location: string): {
107
108
  }
108
109
 
109
110
  const rel = pathname.replace(/^\//, "");
111
+ if (SITE_PAGE_SLUGS.includes(rel)) {
112
+ return {
113
+ url: `${rel}.md`,
114
+ anchor: hash,
115
+ demoSubHash: hash,
116
+ pagePath: pathname,
117
+ pageHash: pathname,
118
+ };
119
+ }
110
120
  if (!rel || !isDocsContentPath(rel)) {
111
121
  return empty;
112
122
  }