@supersoniks/concorde 5.1.1 → 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.
- package/.agents/skills/concorde-docs/SKILL.md +153 -0
- package/.agents/skills/{concorde-component-docs/template.md → concorde-docs/templates/component.md} +6 -2
- package/.agents/skills/concorde-docs/templates/decorator.md +43 -0
- package/.agents/skills/concorde-docs/templates/guide.md +25 -0
- package/.claude/skills +1 -0
- package/.github/workflows/ci.yml +21 -0
- package/.github-export-ignore +30 -0
- package/CONTRIBUTING.md +59 -0
- package/DEV.md +54 -0
- package/LICENSE +21 -0
- package/OPEN_SOURCE_CHECKPOINT.md +133 -0
- package/README.md +4 -4
- package/ai/README.md +29 -41
- package/ai/skills/concorde/SKILL.md +54 -324
- package/ai/skills/concorde/rules/composition.md +32 -0
- package/ai/skills/concorde/rules/data.md +47 -0
- package/ai/skills/concorde/rules/http.md +25 -0
- package/ai/skills/concorde/rules/lists-forms.md +35 -0
- package/ai/skills/concorde/rules/styling.md +46 -0
- package/ai/skills/concorde/rules/troubleshooting.md +45 -0
- package/build-infos.json +1 -1
- package/concorde-core.bundle.js +177 -177
- package/concorde-core.es.js +4993 -4833
- package/custom-elements-manifest.public.config.mjs +8 -0
- package/{.tmp-cem/custom-elements.json → custom-elements.json} +11758 -8904
- package/dist/concorde-core.bundle.js +177 -177
- package/dist/concorde-core.es.js +4993 -4833
- package/dist/docs-mock-api-sw.js +58 -223
- package/dist/docs-mock-api-sw.js.map +4 -4
- package/dist/img/concorde/plane_01.jpg +0 -0
- package/dist/img/concorde/plane_02.jpg +0 -0
- package/dist/img/concorde/plane_03.jpg +0 -0
- package/dist/img/concorde/plane_04.jpg +0 -0
- package/dist/img/concorde/plane_05.jpg +0 -0
- package/dist/img/concorde/plane_06.jpg +0 -0
- package/dist/img/concorde/plane_07.jpg +0 -0
- package/dist/img/concorde/plane_08.jpg +0 -0
- package/dist/img/concorde/plane_09.jpg +0 -0
- package/dist/img/concorde/plane_10.jpg +0 -0
- package/dist/img/concorde/plane_11.jpg +0 -0
- package/dist/img/concorde/plane_12.jpg +0 -0
- package/dist/img/concorde/plane_13.jpg +0 -0
- package/dist/img/og-concorde.jpg +0 -0
- package/dist/robots.txt +11 -10
- package/docs/assets/index-D_alEsng.js +12122 -0
- package/docs/crawl/core/components/functional/date/date.html +190 -0
- package/docs/crawl/core/components/functional/fetch/fetch.html +109 -0
- package/docs/crawl/core/components/functional/if/if.html +27 -0
- package/docs/crawl/core/components/functional/jsonata/jsonata.html +79 -0
- package/docs/crawl/core/components/functional/list/list.html +70 -0
- package/docs/crawl/core/components/functional/mix/mix.html +45 -0
- package/docs/crawl/core/components/functional/queue/queue.html +96 -0
- package/docs/crawl/core/components/functional/router/router.html +106 -0
- package/docs/crawl/core/components/functional/sdui/sdui-extension-points.html +126 -0
- package/docs/crawl/core/components/functional/sdui/sdui.html +368 -0
- package/docs/crawl/core/components/functional/states/states.html +66 -0
- package/docs/crawl/core/components/functional/submit/submit.html +160 -0
- package/docs/crawl/core/components/functional/subscriber/subscriber.html +67 -0
- package/docs/crawl/core/components/functional/value/value.html +51 -0
- package/docs/crawl/core/components/ui/alert/alert.html +211 -0
- package/docs/crawl/core/components/ui/alert-messages/alert-messages.html +131 -0
- package/docs/crawl/core/components/ui/badge/badge.html +197 -0
- package/docs/crawl/core/components/ui/button/button.html +150 -0
- package/docs/crawl/core/components/ui/captcha/captcha.html +112 -0
- package/docs/crawl/core/components/ui/card/card.html +253 -0
- package/docs/crawl/core/components/ui/divider/divider.html +144 -0
- package/docs/crawl/core/components/ui/form/checkbox/checkbox.html +73 -0
- package/docs/crawl/core/components/ui/form/fieldset/fieldset.html +266 -0
- package/docs/crawl/core/components/ui/form/form-actions/form-actions.html +122 -0
- package/docs/crawl/core/components/ui/form/form-layout/form-layout.html +121 -0
- package/docs/crawl/core/components/ui/form/input/input.html +109 -0
- package/docs/crawl/core/components/ui/form/input-autocomplete/input-autocomplete.html +38 -0
- package/docs/crawl/core/components/ui/form/radio/radio.html +56 -0
- package/docs/crawl/core/components/ui/form/select/select.html +73 -0
- package/docs/crawl/core/components/ui/form/switch/switch.html +56 -0
- package/docs/crawl/core/components/ui/form/textarea/textarea.html +59 -0
- package/docs/crawl/core/components/ui/group/group.html +104 -0
- package/docs/crawl/core/components/ui/icon/icon.html +222 -0
- package/docs/crawl/core/components/ui/image/image.html +298 -0
- package/docs/crawl/core/components/ui/link/link.html +100 -0
- package/docs/crawl/core/components/ui/loader/loader.html +124 -0
- package/docs/crawl/core/components/ui/menu/menu.html +394 -0
- package/docs/crawl/core/components/ui/modal/modal.html +342 -0
- package/docs/crawl/core/components/ui/pop/pop.html +296 -0
- package/docs/crawl/core/components/ui/progress/progress.html +131 -0
- package/docs/crawl/core/components/ui/table/table.html +545 -0
- package/docs/crawl/core/components/ui/theme/theme.html +131 -0
- package/docs/crawl/core/components/ui/toast/toast.html +233 -0
- package/docs/crawl/core/components/ui/tooltip/tooltip.html +140 -0
- package/docs/crawl/docs/_core-concept/dataFlow.html +177 -0
- package/docs/crawl/docs/_core-concept/overview.html +66 -0
- package/docs/crawl/docs/_core-concept/subscriber.html +75 -0
- package/docs/crawl/docs/_decorators/ancestor-attribute.html +117 -0
- package/docs/crawl/docs/_decorators/auto-subscribe.html +202 -0
- package/docs/crawl/docs/_decorators/bind.html +141 -0
- package/docs/crawl/docs/_decorators/get.html +149 -0
- package/docs/crawl/docs/_decorators/handle.html +165 -0
- package/docs/crawl/docs/_decorators/on-assign.html +355 -0
- package/docs/crawl/docs/_decorators/patch.html +78 -0
- package/docs/crawl/docs/_decorators/post.html +139 -0
- package/docs/crawl/docs/_decorators/publish.html +61 -0
- package/docs/crawl/docs/_decorators/put.html +78 -0
- package/docs/crawl/docs/_decorators/subscribe.html +109 -0
- package/docs/crawl/docs/_decorators/wait-for-ancestors.html +137 -0
- package/docs/crawl/docs/_directives/sub.html +93 -0
- package/docs/crawl/docs/_getting-started/ai-agents.html +173 -0
- package/docs/crawl/docs/_getting-started/concorde-manual-install.html +100 -0
- package/docs/crawl/docs/_getting-started/concorde-outside.html +58 -0
- package/docs/crawl/docs/_getting-started/create-a-component.html +111 -0
- package/docs/crawl/docs/_getting-started/my-first-component.html +189 -0
- package/docs/crawl/docs/_getting-started/my-first-subscriber.html +110 -0
- package/docs/crawl/docs/_getting-started/pubsub.html +62 -0
- package/docs/crawl/docs/_getting-started/start.html +76 -0
- package/docs/crawl/docs/_getting-started/theming.html +333 -0
- package/docs/crawl/docs/_misc/api-configuration.html +149 -0
- package/docs/crawl/docs/_misc/dataProviderKey.html +135 -0
- package/docs/crawl/docs/_misc/docs-mock-api.html +158 -0
- package/docs/crawl/docs/_misc/dynamic-path.html +180 -0
- package/docs/crawl/docs/_misc/endpoint.html +50 -0
- package/docs/crawl/docs/_misc/formelement-subscriber-removal.html +138 -0
- package/docs/crawl/docs/_misc/html-integration.html +48 -0
- package/docs/crawl/hubs/decorators.html +547 -0
- package/docs/crawl/hubs/form-components.html +381 -0
- package/docs/crawl/hubs/remote-data.html +452 -0
- package/docs/crawl/index.html +103 -0
- package/docs/index.html +1 -1
- package/docs/llms-full.txt +7174 -0
- package/docs/llms.txt +98 -0
- package/docs/sitemap.xml +243 -0
- package/docs/src/core/components/functional/date/date.md +290 -0
- package/docs/src/core/components/functional/fetch/fetch.md +125 -0
- package/docs/src/core/components/functional/if/if.md +9 -0
- package/docs/src/core/components/functional/jsonata/jsonata.md +56 -0
- package/docs/src/core/components/functional/list/list.md +65 -0
- package/docs/src/core/components/functional/mix/mix.md +41 -0
- package/docs/src/core/components/functional/queue/queue.md +72 -0
- package/docs/src/core/components/functional/router/router.md +94 -0
- package/docs/src/core/components/functional/sdui/component-catalog.gaps.json +511 -0
- package/docs/src/core/components/functional/sdui/component-catalog.json +3103 -0
- package/docs/src/core/components/functional/sdui/default-library.json +108 -0
- package/docs/src/core/components/functional/sdui/example.json +99 -0
- package/docs/src/core/components/functional/sdui/sdui-extension-points.md +94 -0
- package/docs/src/core/components/functional/sdui/sdui.md +478 -0
- package/docs/src/core/components/functional/states/states.md +87 -0
- package/docs/src/core/components/functional/submit/submit.md +114 -0
- package/docs/src/core/components/functional/subscriber/subscriber.md +91 -0
- package/docs/src/core/components/functional/value/value.md +35 -0
- package/docs/src/core/components/ui/alert/alert.md +196 -0
- package/docs/src/core/components/ui/alert-messages/alert-messages.md +85 -0
- package/docs/src/core/components/ui/badge/badge.md +188 -0
- package/docs/src/core/components/ui/button/button.md +183 -0
- package/docs/src/core/components/ui/captcha/captcha.md +71 -0
- package/docs/src/core/components/ui/card/card.md +215 -0
- package/docs/src/core/components/ui/divider/divider.md +129 -0
- package/docs/src/core/components/ui/form/checkbox/checkbox.md +77 -0
- package/docs/src/core/components/ui/form/fieldset/fieldset.md +205 -0
- package/docs/src/core/components/ui/form/form-actions/form-actions.md +100 -0
- package/docs/src/core/components/ui/form/form-layout/form-layout.md +119 -0
- package/docs/src/core/components/ui/form/input/input.md +142 -0
- package/docs/src/core/components/ui/form/radio/radio.md +57 -0
- package/docs/src/core/components/ui/form/select/select.md +71 -0
- package/docs/src/core/components/ui/form/switch/switch.md +57 -0
- package/docs/src/core/components/ui/form/textarea/textarea.md +65 -0
- package/docs/src/core/components/ui/group/group.md +75 -0
- package/docs/src/core/components/ui/icon/icon.md +214 -0
- package/docs/src/core/components/ui/icon/icons.json +1 -0
- package/docs/src/core/components/ui/image/image.md +233 -0
- package/docs/src/core/components/ui/link/link.md +81 -0
- package/docs/src/core/components/ui/loader/loader.md +112 -0
- package/docs/src/core/components/ui/menu/menu.md +370 -0
- package/docs/src/core/components/ui/modal/modal.md +235 -0
- package/docs/src/core/components/ui/pop/pop.md +294 -0
- package/docs/src/core/components/ui/progress/progress.md +109 -0
- package/docs/src/core/components/ui/table/table.md +431 -0
- package/docs/src/core/components/ui/theme/theme.md +122 -0
- package/docs/src/core/components/ui/toast/toast.md +216 -0
- package/docs/src/core/components/ui/tooltip/tooltip.md +124 -0
- package/{src → docs/src}/docs/_core-concept/subscriber.md +0 -0
- package/{src → docs/src}/docs/_getting-started/my-first-subscriber.md +0 -0
- package/{src → docs/src}/docs/_getting-started/pubsub.md +0 -0
- package/docs/src/docs/_getting-started/theming.md +253 -0
- package/docs/src/docs/search/docs-search.json +12242 -0
- package/docs/src/tag-list.json +1 -0
- package/docs/src/tsconfig-model.json +23 -0
- package/docs/src/tsconfig.declarations.json +22 -0
- package/docs/src/tsconfig.emit.json +23 -0
- package/docs/src/tsconfig.json +1332 -0
- package/gitlab/job_github_mirror.sh +39 -0
- package/index.html +157 -5
- package/package.json +22 -4
- package/public/docs-mock-api-sw.js +58 -223
- package/public/docs-mock-api-sw.js.map +4 -4
- package/public/img/concorde/plane_01.jpg +0 -0
- package/public/img/concorde/plane_02.jpg +0 -0
- package/public/img/concorde/plane_03.jpg +0 -0
- package/public/img/concorde/plane_04.jpg +0 -0
- package/public/img/concorde/plane_05.jpg +0 -0
- package/public/img/concorde/plane_06.jpg +0 -0
- package/public/img/concorde/plane_07.jpg +0 -0
- package/public/img/concorde/plane_08.jpg +0 -0
- package/public/img/concorde/plane_09.jpg +0 -0
- package/public/img/concorde/plane_10.jpg +0 -0
- package/public/img/concorde/plane_11.jpg +0 -0
- package/public/img/concorde/plane_12.jpg +0 -0
- package/public/img/concorde/plane_13.jpg +0 -0
- package/public/img/og-concorde.jpg +0 -0
- package/public/robots.txt +11 -10
- package/scripts/ai-init.mjs +104 -379
- package/scripts/ai-init.test.mjs +81 -239
- package/scripts/docs-markdown-vite-plugin.ts +43 -0
- package/scripts/docs-public-url.mjs +56 -0
- package/scripts/docs.htaccess +82 -0
- package/scripts/generate-cem.mjs +244 -0
- package/scripts/generate-cem.test.mjs +122 -0
- package/scripts/generate-crawl-docs.mjs +233 -118
- package/scripts/generate-crawl-docs.test.mjs +124 -0
- package/scripts/generate-sdui-catalog.mjs +11 -10
- package/scripts/github-export.mjs +150 -0
- package/scripts/post-build-docs.js +37 -50
- package/scripts/post-build.mjs +0 -0
- package/scripts/pre-publish.mjs +2 -1
- package/scripts/site-pages.mjs +212 -0
- package/scripts/site-pages.test.mjs +116 -0
- package/site-pages/about.md +22 -0
- package/site-pages/privacy.md +32 -0
- package/src/core/components/functional/date/date.md +91 -231
- package/src/core/components/functional/example/example.ts +3 -1
- package/src/core/components/functional/fetch/fetch.md +66 -83
- package/src/core/components/functional/if/if.md +28 -3
- package/src/core/components/functional/if/if.ts +0 -0
- package/src/core/components/functional/jsonata/jsonata.md +42 -37
- package/src/core/components/functional/list/list.demo.ts +0 -0
- package/src/core/components/functional/list/list.md +103 -43
- package/src/core/components/functional/list/list.ts +17 -4
- package/src/core/components/functional/mix/mix.md +31 -26
- package/src/core/components/functional/queue/queue.demo.ts +10 -7
- package/src/core/components/functional/queue/queue.md +40 -61
- package/src/core/components/functional/router/router.demo.ts +16 -16
- package/src/core/components/functional/router/router.md +64 -65
- package/src/core/components/functional/router/router.ts +0 -0
- package/src/core/components/functional/sdui/SDUIDescriptorTransformer.ts +0 -0
- package/src/core/components/functional/sdui/component-catalog.gaps.json +1 -1
- package/src/core/components/functional/sdui/component-catalog.json +2 -2
- package/src/core/components/functional/sdui/example.json +19 -86
- package/src/core/components/functional/sdui/sdui-extension-points.md +1 -1
- package/src/core/components/functional/sdui/sdui-immutable.spec.ts +74 -0
- package/src/core/components/functional/sdui/sdui.md +187 -358
- package/src/core/components/functional/sdui/sdui.ts +33 -8
- package/src/core/components/functional/sdui/types.ts +0 -0
- package/src/core/components/functional/states/states.demo.ts +21 -22
- package/src/core/components/functional/states/states.md +86 -61
- package/src/core/components/functional/states/states.ts +1 -1
- package/src/core/components/functional/submit/submit.md +64 -72
- package/src/core/components/functional/submit/submit.spec.ts +60 -0
- package/src/core/components/functional/submit/submit.ts +6 -3
- package/src/core/components/functional/subscriber/subscriber.ts +0 -0
- package/src/core/components/functional/translation/translation.md +38 -0
- package/src/core/components/functional/value/value.md +34 -22
- package/src/core/components/functional/value/value.ts +0 -0
- package/src/core/components/ui/_css/size.ts +0 -0
- package/src/core/components/ui/alert/alert.md +48 -58
- package/src/core/components/ui/badge/badge.md +27 -34
- package/src/core/components/ui/button/button.md +139 -106
- package/src/core/components/ui/captcha/altchaStyles.ts +0 -0
- package/src/core/components/ui/captcha/captcha.md +17 -32
- package/src/core/components/ui/card/card.md +6 -6
- package/src/core/components/ui/divider/divider.md +8 -11
- package/src/core/components/ui/form/checkbox/checkbox.md +90 -42
- package/src/core/components/ui/form/fieldset/fieldset.md +31 -75
- package/src/core/components/ui/form/form-actions/form-actions.md +14 -16
- package/src/core/components/ui/form/form-layout/form-layout.md +19 -29
- package/src/core/components/ui/form/input/input.md +108 -90
- package/src/core/components/ui/form/input/input.ts +8 -2
- package/src/core/components/ui/form/radio/radio.md +65 -30
- package/src/core/components/ui/form/select/select.md +60 -45
- package/src/core/components/ui/form/switch/switch.md +57 -34
- package/src/core/components/ui/form/textarea/textarea.md +55 -38
- package/src/core/components/ui/group/group.md +35 -33
- package/src/core/components/ui/icon/icon.md +4 -4
- package/src/core/components/ui/image/image.md +41 -107
- package/src/core/components/ui/link/link.md +6 -6
- package/src/core/components/ui/loader/loader.md +30 -55
- package/src/core/components/ui/menu/menu.md +27 -27
- package/src/core/components/ui/modal/modal.md +72 -96
- package/src/core/components/ui/pop/pop.md +80 -158
- package/src/core/components/ui/progress/progress.md +2 -2
- package/src/core/components/ui/table/table.md +91 -255
- package/src/core/components/ui/theme/theme.md +11 -36
- package/src/core/components/ui/toast/toast.md +48 -117
- package/src/core/components/ui/tooltip/tooltip.md +27 -62
- package/src/core/decorators/subscriber/bind.ts +9 -14
- package/src/core/decorators/subscriber/subscribe.ts +15 -9
- package/src/core/decorators/subscriber/subscribe.type-test.ts +14 -5
- package/src/core/mixins/Subscriber.ts +0 -0
- package/src/core/utils/DataBindObserver.ts +0 -0
- package/src/core/utils/LocationHandler.spec.ts +80 -0
- package/src/core/utils/LocationHandler.ts +5 -9
- package/src/core/utils/Objects.ts +0 -0
- package/src/core/utils/PublisherProxy.ts +166 -125
- package/src/core/utils/deepReadonly.spec.ts +66 -0
- package/src/core/utils/deepReadonly.ts +71 -0
- package/src/core/utils/immutableValue.spec.ts +54 -0
- package/src/core/utils/immutableValue.ts +102 -0
- package/src/core/utils/nestedDotForm.spec.ts +88 -0
- package/src/core/utils/publisherImmutable.spec.ts +76 -0
- package/src/core/utils/route.ts +0 -0
- package/src/core/utils/strictGet.spec.ts +61 -0
- package/src/core/utils/strictGet.ts +102 -0
- package/src/css.d.ts +19 -0
- package/src/docs/components/docs-demo-sources.ts +55 -140
- package/src/docs/components/docs-lit-demo.ts +1 -3
- package/src/docs/components/docs-source-link.ts +0 -82
- package/src/docs/decorators/ancestor-attribute.md +59 -0
- package/src/docs/decorators/auto-subscribe.md +40 -0
- package/src/docs/decorators/bind.md +76 -0
- package/src/docs/decorators/get.md +160 -0
- package/src/docs/decorators/handle.md +119 -0
- package/src/docs/decorators/on-assign.md +63 -0
- package/src/docs/decorators/patch.md +44 -0
- package/src/docs/decorators/post.md +134 -0
- package/src/docs/decorators/publish.md +56 -0
- package/src/docs/decorators/put.md +51 -0
- package/src/docs/decorators/subscribe.md +87 -0
- package/src/docs/decorators/wait-for-ancestors.md +76 -0
- package/src/docs/directives/sub.md +75 -0
- package/src/docs/directives/t.md +73 -0
- package/src/docs/docs-location.spec.ts +56 -0
- package/src/docs/docs-location.ts +133 -32
- package/src/docs/docs.ts +1 -2
- package/src/docs/example/decorators-demo-bind-demos.ts +83 -135
- package/src/docs/example/decorators-demo-geo.ts +30 -19
- package/src/docs/example/decorators-demo-post.ts +88 -186
- package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +165 -268
- package/src/docs/example/decorators-demo.ts +85 -98
- package/src/docs/example/docs-api-config-demos.ts +19 -20
- package/src/docs/example/docs-flight-demos.ts +147 -0
- package/src/docs/example/docs-jsonata-demos.ts +52 -52
- package/src/docs/example/docs-list-demos.ts +43 -123
- package/src/docs/example/docs-provider-keys.ts +21 -28
- package/src/docs/example/docs-queue-demos.ts +37 -77
- package/src/docs/example/docs-router-demos.ts +20 -25
- package/src/docs/example/docs-submit-demos.ts +48 -48
- package/src/docs/example/docs-toggle-demos.ts +4 -3
- package/src/docs/example/users.ts +0 -0
- package/src/docs/getting-started/ai-agents.md +50 -0
- package/src/docs/getting-started/create-a-component.md +136 -0
- package/src/docs/getting-started/dataFlow.md +87 -0
- package/src/docs/getting-started/installation.md +36 -0
- package/src/docs/getting-started/my-first-component.md +227 -0
- package/src/docs/getting-started/start.md +35 -0
- package/src/docs/header/header.ts +11 -6
- package/src/docs/home/catalog.ts +1 -1
- package/src/docs/home/home.ts +20 -11
- package/src/docs/layout.ts +12 -13
- package/src/docs/misc/api-configuration.md +79 -0
- package/src/docs/misc/dataProviderKey.md +168 -0
- package/src/docs/misc/docs-mock-api.md +62 -0
- package/src/docs/misc/dynamic-path.md +71 -0
- package/src/docs/misc/endpoint.md +45 -0
- package/src/docs/misc/html-integration.md +14 -0
- package/src/docs/misc/publisher-get-immutability-docs-crawl.json +902 -0
- package/src/docs/misc/publisher-get-immutability-docs-recheck.json +13 -0
- package/src/docs/misc/publisher-get-immutability-study.md +229 -0
- package/src/docs/mock-api/api-config-mock.ts +5 -0
- package/src/docs/mock-api/fixtures.ts +0 -278
- package/src/docs/mock-api/flights.ts +89 -0
- package/src/docs/mock-api/router.ts +3 -4
- package/src/docs/mock-api/urls.ts +1 -2
- package/src/docs/navigation/navigation.ts +84 -101
- package/src/docs/navigation/toc.ts +7 -4
- package/src/docs/search/docs-search.json +3188 -5153
- package/src/docs/search/markdown-renderer.ts +31 -8
- package/src/docs/search/page.ts +9 -6
- package/src/docs/search/search.ts +154 -122
- package/src/docs/tailwind/css/docs/content.css +4 -5
- package/src/tsconfig.declarations.json +22 -0
- package/src/tsconfig.emit.json +23 -0
- package/src/tsconfig.json +53 -5
- package/src/tsconfig.tsbuildinfo +1 -1
- package/vite.config.mts +11 -2
- package/.agents/skills/concorde-component-docs/SKILL.md +0 -194
- package/.taste-skill/after/desktop-fold.jpg +0 -0
- package/.taste-skill/after/desktop-page-01.jpg +0 -0
- package/.taste-skill/after/desktop-page-02.jpg +0 -0
- package/.taste-skill/after/desktop-page-03.jpg +0 -0
- package/.taste-skill/after/mobile-fold.jpg +0 -0
- package/.taste-skill/after/mobile-page-01.jpg +0 -0
- package/.taste-skill/after/report.json +0 -4103
- package/.taste-skill/after/tablet-fold.jpg +0 -0
- package/.taste-skill/after/tablet-page-01.jpg +0 -0
- package/.taste-skill/after/tablet-page-02.jpg +0 -0
- package/.taste-skill/before/desktop-fold.jpg +0 -0
- package/.taste-skill/before/desktop-page-01.jpg +0 -0
- package/.taste-skill/before/desktop-page-02.jpg +0 -0
- package/.taste-skill/before/mobile-fold.jpg +0 -0
- package/.taste-skill/before/mobile-page-01.jpg +0 -0
- package/.taste-skill/before/report.json +0 -2556
- package/.taste-skill/before/tablet-fold.jpg +0 -0
- package/.taste-skill/before/tablet-page-01.jpg +0 -0
- package/.taste-skill/direction.json +0 -510
- package/.taste-skill/notes.md +0 -84
- package/.taste-skill/prod/desktop-fold.jpg +0 -0
- package/.taste-skill/prod/desktop-page-01.jpg +0 -0
- package/.taste-skill/prod/desktop-page-02.jpg +0 -0
- package/.taste-skill/prod/desktop-page-03.jpg +0 -0
- package/.taste-skill/prod/mobile-fold.jpg +0 -0
- package/.taste-skill/prod/mobile-page-01.jpg +0 -0
- package/.taste-skill/prod/mobile-page-02.jpg +0 -0
- package/.taste-skill/prod/report.json +0 -1973
- package/.taste-skill/prod/tablet-fold.jpg +0 -0
- package/.taste-skill/prod/tablet-page-01.jpg +0 -0
- package/.taste-skill/prod/tablet-page-02.jpg +0 -0
- package/AUDIT.md +0 -228
- package/PLAN-AMELIORATION.md +0 -220
- package/ai/AGENTS.md +0 -56
- package/ai/cursor/rules/concorde-menu.mdc +0 -15
- package/ai/cursor/rules/concorde-scope.mdc +0 -14
- package/ai/cursor/rules/concorde-theme.mdc +0 -13
- package/ai/cursor/rules/concorde.mdc +0 -66
- package/ai/jetbrains/rules/concorde.md +0 -53
- package/ai/skills/concorde-get-set-dp/SKILL.md +0 -194
- package/ai/skills/concorde-imports/SKILL.md +0 -78
- package/ai/skills/concorde-menu/SKILL.md +0 -74
- package/ai/skills/concorde-scope/SKILL.md +0 -70
- package/ai/skills/concorde-theme/SKILL.md +0 -57
- package/docs/assets/index-Bb-_LRyY.js +0 -12280
- package/src/docs/example/docs-input-autocomplete-demos.ts +0 -110
- package/src/docs/example/docs-joke-demos.ts +0 -297
- /package/{src → docs/src}/core/components/ui/form/input-autocomplete/input-autocomplete.md +0 -0
- /package/{src → docs/src}/docs/_core-concept/dataFlow.md +0 -0
- /package/{src → docs/src}/docs/_core-concept/overview.md +0 -0
- /package/{src → docs/src}/docs/_decorators/ancestor-attribute.md +0 -0
- /package/{src → docs/src}/docs/_decorators/auto-subscribe.md +0 -0
- /package/{src → docs/src}/docs/_decorators/bind.md +0 -0
- /package/{src → docs/src}/docs/_decorators/get.md +0 -0
- /package/{src → docs/src}/docs/_decorators/handle.md +0 -0
- /package/{src → docs/src}/docs/_decorators/on-assign.md +0 -0
- /package/{src → docs/src}/docs/_decorators/patch.md +0 -0
- /package/{src → docs/src}/docs/_decorators/post.md +0 -0
- /package/{src → docs/src}/docs/_decorators/publish.md +0 -0
- /package/{src → docs/src}/docs/_decorators/put.md +0 -0
- /package/{src → docs/src}/docs/_decorators/subscribe.md +0 -0
- /package/{src → docs/src}/docs/_decorators/wait-for-ancestors.md +0 -0
- /package/{src → docs/src}/docs/_directives/sub.md +0 -0
- /package/{src → docs/src}/docs/_getting-started/ai-agents.md +0 -0
- /package/{src → docs/src}/docs/_getting-started/concorde-manual-install.md +0 -0
- /package/{src → docs/src}/docs/_getting-started/concorde-outside.md +0 -0
- /package/{src → docs/src}/docs/_getting-started/create-a-component.md +0 -0
- /package/{src → docs/src}/docs/_getting-started/my-first-component.md +0 -0
- /package/{src → docs/src}/docs/_getting-started/start.md +0 -0
- /package/{src → docs/src}/docs/_misc/api-configuration.md +0 -0
- /package/{src → docs/src}/docs/_misc/dataProviderKey.md +0 -0
- /package/{src → docs/src}/docs/_misc/docs-mock-api.md +0 -0
- /package/{src → docs/src}/docs/_misc/dynamic-path.md +0 -0
- /package/{src → docs/src}/docs/_misc/endpoint.md +0 -0
- /package/{src → docs/src}/docs/_misc/formelement-subscriber-removal.md +0 -0
- /package/{src → docs/src}/docs/_misc/html-integration.md +0 -0
- /package/src/docs/{_getting-started → getting-started}/components.md +0 -0
- /package/src/docs/{_getting-started → getting-started}/theming.md +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
|
+
});
|
package/src/core/utils/route.ts
CHANGED
|
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
|
+
}
|