@lokascript/semantic 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +686 -0
- package/dist/browser-ar.ar.global.js +2 -0
- package/dist/browser-core.core.global.js +2 -0
- package/dist/browser-de.de.global.js +2 -0
- package/dist/browser-east-asian.east-asian.global.js +2 -0
- package/dist/browser-en-tr.en-tr.global.js +2 -0
- package/dist/browser-en.en.global.js +2 -0
- package/dist/browser-es-en.es-en.global.js +2 -0
- package/dist/browser-es.es.global.js +2 -0
- package/dist/browser-fr.fr.global.js +2 -0
- package/dist/browser-id.id.global.js +2 -0
- package/dist/browser-ja.ja.global.js +2 -0
- package/dist/browser-ko.ko.global.js +2 -0
- package/dist/browser-lazy.lazy.global.js +2 -0
- package/dist/browser-priority.priority.global.js +2 -0
- package/dist/browser-pt.pt.global.js +2 -0
- package/dist/browser-qu.qu.global.js +2 -0
- package/dist/browser-sw.sw.global.js +2 -0
- package/dist/browser-tr.tr.global.js +2 -0
- package/dist/browser-western.western.global.js +2 -0
- package/dist/browser-zh.zh.global.js +2 -0
- package/dist/browser.global.js +3 -0
- package/dist/browser.global.js.map +1 -0
- package/dist/index.cjs +35051 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3426 -0
- package/dist/index.d.ts +3426 -0
- package/dist/index.js +34890 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/ar.d.ts +78 -0
- package/dist/languages/ar.js +1622 -0
- package/dist/languages/ar.js.map +1 -0
- package/dist/languages/de.d.ts +38 -0
- package/dist/languages/de.js +1168 -0
- package/dist/languages/de.js.map +1 -0
- package/dist/languages/en.d.ts +44 -0
- package/dist/languages/en.js +3491 -0
- package/dist/languages/en.js.map +1 -0
- package/dist/languages/es.d.ts +52 -0
- package/dist/languages/es.js +1493 -0
- package/dist/languages/es.js.map +1 -0
- package/dist/languages/fr.d.ts +37 -0
- package/dist/languages/fr.js +1159 -0
- package/dist/languages/fr.js.map +1 -0
- package/dist/languages/id.d.ts +35 -0
- package/dist/languages/id.js +1152 -0
- package/dist/languages/id.js.map +1 -0
- package/dist/languages/ja.d.ts +53 -0
- package/dist/languages/ja.js +1430 -0
- package/dist/languages/ja.js.map +1 -0
- package/dist/languages/ko.d.ts +51 -0
- package/dist/languages/ko.js +1729 -0
- package/dist/languages/ko.js.map +1 -0
- package/dist/languages/pt.d.ts +37 -0
- package/dist/languages/pt.js +1127 -0
- package/dist/languages/pt.js.map +1 -0
- package/dist/languages/qu.d.ts +36 -0
- package/dist/languages/qu.js +1143 -0
- package/dist/languages/qu.js.map +1 -0
- package/dist/languages/sw.d.ts +35 -0
- package/dist/languages/sw.js +1147 -0
- package/dist/languages/sw.js.map +1 -0
- package/dist/languages/tr.d.ts +45 -0
- package/dist/languages/tr.js +1529 -0
- package/dist/languages/tr.js.map +1 -0
- package/dist/languages/zh.d.ts +58 -0
- package/dist/languages/zh.js +1257 -0
- package/dist/languages/zh.js.map +1 -0
- package/dist/types-C4dcj53L.d.ts +600 -0
- package/package.json +202 -0
- package/src/__test-utils__/index.ts +7 -0
- package/src/__test-utils__/test-helpers.ts +8 -0
- package/src/__types__/test-helpers.ts +122 -0
- package/src/analysis/index.ts +479 -0
- package/src/ast-builder/command-mappers.ts +1133 -0
- package/src/ast-builder/expression-parser/index.ts +41 -0
- package/src/ast-builder/expression-parser/parser.ts +563 -0
- package/src/ast-builder/expression-parser/tokenizer.ts +394 -0
- package/src/ast-builder/expression-parser/types.ts +208 -0
- package/src/ast-builder/index.ts +536 -0
- package/src/ast-builder/value-converters.ts +172 -0
- package/src/bridge.ts +275 -0
- package/src/browser-ar.ts +162 -0
- package/src/browser-core.ts +231 -0
- package/src/browser-de.ts +162 -0
- package/src/browser-east-asian.ts +173 -0
- package/src/browser-en-tr.ts +165 -0
- package/src/browser-en.ts +157 -0
- package/src/browser-es-en.ts +200 -0
- package/src/browser-es.ts +170 -0
- package/src/browser-fr.ts +162 -0
- package/src/browser-id.ts +162 -0
- package/src/browser-ja.ts +162 -0
- package/src/browser-ko.ts +162 -0
- package/src/browser-lazy.ts +189 -0
- package/src/browser-priority.ts +214 -0
- package/src/browser-pt.ts +162 -0
- package/src/browser-qu.ts +162 -0
- package/src/browser-sw.ts +162 -0
- package/src/browser-tr.ts +162 -0
- package/src/browser-western.ts +181 -0
- package/src/browser-zh.ts +162 -0
- package/src/browser.ts +268 -0
- package/src/cache/index.ts +14 -0
- package/src/cache/semantic-cache.ts +344 -0
- package/src/core-bridge.ts +372 -0
- package/src/explicit/converter.ts +258 -0
- package/src/explicit/index.ts +18 -0
- package/src/explicit/parser.ts +236 -0
- package/src/explicit/renderer.ts +424 -0
- package/src/generators/command-schemas.ts +1636 -0
- package/src/generators/event-handler-generator.ts +109 -0
- package/src/generators/index.ts +117 -0
- package/src/generators/language-profiles.ts +139 -0
- package/src/generators/pattern-generator.ts +537 -0
- package/src/generators/profiles/arabic.ts +131 -0
- package/src/generators/profiles/bengali.ts +132 -0
- package/src/generators/profiles/chinese.ts +124 -0
- package/src/generators/profiles/english.ts +113 -0
- package/src/generators/profiles/french.ts +125 -0
- package/src/generators/profiles/german.ts +126 -0
- package/src/generators/profiles/hindi.ts +146 -0
- package/src/generators/profiles/index.ts +46 -0
- package/src/generators/profiles/indonesian.ts +125 -0
- package/src/generators/profiles/italian.ts +139 -0
- package/src/generators/profiles/japanese.ts +149 -0
- package/src/generators/profiles/korean.ts +127 -0
- package/src/generators/profiles/marker-templates.ts +288 -0
- package/src/generators/profiles/ms.ts +130 -0
- package/src/generators/profiles/polish.ts +249 -0
- package/src/generators/profiles/portuguese.ts +115 -0
- package/src/generators/profiles/quechua.ts +113 -0
- package/src/generators/profiles/russian.ts +260 -0
- package/src/generators/profiles/spanish.ts +130 -0
- package/src/generators/profiles/swahili.ts +129 -0
- package/src/generators/profiles/thai.ts +132 -0
- package/src/generators/profiles/tl.ts +128 -0
- package/src/generators/profiles/turkish.ts +124 -0
- package/src/generators/profiles/types.ts +165 -0
- package/src/generators/profiles/ukrainian.ts +270 -0
- package/src/generators/profiles/vietnamese.ts +133 -0
- package/src/generators/schema-error-codes.ts +160 -0
- package/src/generators/schema-validator.ts +391 -0
- package/src/index.ts +429 -0
- package/src/language-building-schema.ts +3170 -0
- package/src/language-loader.ts +394 -0
- package/src/languages/_all.ts +65 -0
- package/src/languages/ar.ts +15 -0
- package/src/languages/bn.ts +16 -0
- package/src/languages/de.ts +15 -0
- package/src/languages/en.ts +29 -0
- package/src/languages/es.ts +15 -0
- package/src/languages/fr.ts +15 -0
- package/src/languages/hi.ts +26 -0
- package/src/languages/id.ts +15 -0
- package/src/languages/index.ts +18 -0
- package/src/languages/it.ts +15 -0
- package/src/languages/ja.ts +15 -0
- package/src/languages/ko.ts +15 -0
- package/src/languages/ms.ts +16 -0
- package/src/languages/pl.ts +18 -0
- package/src/languages/pt.ts +15 -0
- package/src/languages/qu.ts +15 -0
- package/src/languages/ru.ts +26 -0
- package/src/languages/sw.ts +15 -0
- package/src/languages/th.ts +16 -0
- package/src/languages/tl.ts +16 -0
- package/src/languages/tr.ts +15 -0
- package/src/languages/uk.ts +26 -0
- package/src/languages/vi.ts +16 -0
- package/src/languages/zh.ts +15 -0
- package/src/parser/index.ts +15 -0
- package/src/parser/pattern-matcher.ts +1181 -0
- package/src/parser/semantic-parser.ts +573 -0
- package/src/parser/utils/index.ts +35 -0
- package/src/parser/utils/marker-resolution.ts +111 -0
- package/src/parser/utils/possessive-keywords.ts +43 -0
- package/src/parser/utils/role-positioning.ts +70 -0
- package/src/parser/utils/type-validation.ts +134 -0
- package/src/patterns/add/ar.ts +71 -0
- package/src/patterns/add/bn.ts +70 -0
- package/src/patterns/add/hi.ts +69 -0
- package/src/patterns/add/index.ts +87 -0
- package/src/patterns/add/it.ts +61 -0
- package/src/patterns/add/ja.ts +93 -0
- package/src/patterns/add/ko.ts +74 -0
- package/src/patterns/add/ms.ts +30 -0
- package/src/patterns/add/pl.ts +62 -0
- package/src/patterns/add/ru.ts +62 -0
- package/src/patterns/add/th.ts +49 -0
- package/src/patterns/add/tl.ts +30 -0
- package/src/patterns/add/tr.ts +71 -0
- package/src/patterns/add/uk.ts +62 -0
- package/src/patterns/add/vi.ts +61 -0
- package/src/patterns/add/zh.ts +71 -0
- package/src/patterns/builders.ts +207 -0
- package/src/patterns/decrement/bn.ts +70 -0
- package/src/patterns/decrement/de.ts +42 -0
- package/src/patterns/decrement/hi.ts +68 -0
- package/src/patterns/decrement/index.ts +79 -0
- package/src/patterns/decrement/it.ts +69 -0
- package/src/patterns/decrement/ms.ts +30 -0
- package/src/patterns/decrement/pl.ts +58 -0
- package/src/patterns/decrement/ru.ts +58 -0
- package/src/patterns/decrement/th.ts +49 -0
- package/src/patterns/decrement/tl.ts +30 -0
- package/src/patterns/decrement/tr.ts +48 -0
- package/src/patterns/decrement/uk.ts +58 -0
- package/src/patterns/decrement/vi.ts +61 -0
- package/src/patterns/decrement/zh.ts +32 -0
- package/src/patterns/en.ts +302 -0
- package/src/patterns/event-handler/ar.ts +151 -0
- package/src/patterns/event-handler/bn.ts +72 -0
- package/src/patterns/event-handler/de.ts +117 -0
- package/src/patterns/event-handler/en.ts +117 -0
- package/src/patterns/event-handler/es.ts +136 -0
- package/src/patterns/event-handler/fr.ts +117 -0
- package/src/patterns/event-handler/hi.ts +64 -0
- package/src/patterns/event-handler/id.ts +117 -0
- package/src/patterns/event-handler/index.ts +119 -0
- package/src/patterns/event-handler/it.ts +54 -0
- package/src/patterns/event-handler/ja.ts +118 -0
- package/src/patterns/event-handler/ko.ts +133 -0
- package/src/patterns/event-handler/ms.ts +30 -0
- package/src/patterns/event-handler/pl.ts +62 -0
- package/src/patterns/event-handler/pt.ts +117 -0
- package/src/patterns/event-handler/qu.ts +66 -0
- package/src/patterns/event-handler/ru.ts +62 -0
- package/src/patterns/event-handler/shared.ts +270 -0
- package/src/patterns/event-handler/sw.ts +117 -0
- package/src/patterns/event-handler/th.ts +53 -0
- package/src/patterns/event-handler/tl.ts +30 -0
- package/src/patterns/event-handler/tr.ts +170 -0
- package/src/patterns/event-handler/uk.ts +62 -0
- package/src/patterns/event-handler/vi.ts +61 -0
- package/src/patterns/event-handler/zh.ts +150 -0
- package/src/patterns/get/ar.ts +49 -0
- package/src/patterns/get/bn.ts +47 -0
- package/src/patterns/get/de.ts +32 -0
- package/src/patterns/get/hi.ts +52 -0
- package/src/patterns/get/index.ts +83 -0
- package/src/patterns/get/it.ts +56 -0
- package/src/patterns/get/ja.ts +53 -0
- package/src/patterns/get/ko.ts +53 -0
- package/src/patterns/get/ms.ts +30 -0
- package/src/patterns/get/pl.ts +57 -0
- package/src/patterns/get/ru.ts +57 -0
- package/src/patterns/get/th.ts +29 -0
- package/src/patterns/get/tl.ts +30 -0
- package/src/patterns/get/uk.ts +57 -0
- package/src/patterns/get/vi.ts +48 -0
- package/src/patterns/grammar-transformed/index.ts +39 -0
- package/src/patterns/grammar-transformed/ja.ts +1713 -0
- package/src/patterns/grammar-transformed/ko.ts +1311 -0
- package/src/patterns/grammar-transformed/tr.ts +1067 -0
- package/src/patterns/hide/ar.ts +67 -0
- package/src/patterns/hide/bn.ts +47 -0
- package/src/patterns/hide/de.ts +36 -0
- package/src/patterns/hide/hi.ts +61 -0
- package/src/patterns/hide/index.ts +91 -0
- package/src/patterns/hide/it.ts +56 -0
- package/src/patterns/hide/ja.ts +69 -0
- package/src/patterns/hide/ko.ts +69 -0
- package/src/patterns/hide/ms.ts +30 -0
- package/src/patterns/hide/pl.ts +57 -0
- package/src/patterns/hide/ru.ts +57 -0
- package/src/patterns/hide/th.ts +29 -0
- package/src/patterns/hide/tl.ts +30 -0
- package/src/patterns/hide/tr.ts +65 -0
- package/src/patterns/hide/uk.ts +57 -0
- package/src/patterns/hide/vi.ts +56 -0
- package/src/patterns/hide/zh.ts +68 -0
- package/src/patterns/increment/bn.ts +70 -0
- package/src/patterns/increment/de.ts +36 -0
- package/src/patterns/increment/hi.ts +68 -0
- package/src/patterns/increment/index.ts +79 -0
- package/src/patterns/increment/it.ts +69 -0
- package/src/patterns/increment/ms.ts +30 -0
- package/src/patterns/increment/pl.ts +58 -0
- package/src/patterns/increment/ru.ts +58 -0
- package/src/patterns/increment/th.ts +49 -0
- package/src/patterns/increment/tl.ts +30 -0
- package/src/patterns/increment/tr.ts +52 -0
- package/src/patterns/increment/uk.ts +58 -0
- package/src/patterns/increment/vi.ts +61 -0
- package/src/patterns/increment/zh.ts +32 -0
- package/src/patterns/index.ts +84 -0
- package/src/patterns/languages/en/control-flow.ts +93 -0
- package/src/patterns/languages/en/fetch.ts +62 -0
- package/src/patterns/languages/en/index.ts +42 -0
- package/src/patterns/languages/en/repeat.ts +67 -0
- package/src/patterns/languages/en/set.ts +48 -0
- package/src/patterns/languages/en/swap.ts +38 -0
- package/src/patterns/languages/en/temporal.ts +57 -0
- package/src/patterns/put/ar.ts +74 -0
- package/src/patterns/put/bn.ts +53 -0
- package/src/patterns/put/en.ts +74 -0
- package/src/patterns/put/es.ts +74 -0
- package/src/patterns/put/hi.ts +69 -0
- package/src/patterns/put/id.ts +96 -0
- package/src/patterns/put/index.ts +99 -0
- package/src/patterns/put/it.ts +56 -0
- package/src/patterns/put/ja.ts +75 -0
- package/src/patterns/put/ko.ts +67 -0
- package/src/patterns/put/ms.ts +30 -0
- package/src/patterns/put/pl.ts +81 -0
- package/src/patterns/put/ru.ts +85 -0
- package/src/patterns/put/th.ts +32 -0
- package/src/patterns/put/tl.ts +30 -0
- package/src/patterns/put/tr.ts +67 -0
- package/src/patterns/put/uk.ts +85 -0
- package/src/patterns/put/vi.ts +72 -0
- package/src/patterns/put/zh.ts +62 -0
- package/src/patterns/registry.ts +163 -0
- package/src/patterns/remove/ar.ts +71 -0
- package/src/patterns/remove/bn.ts +68 -0
- package/src/patterns/remove/hi.ts +69 -0
- package/src/patterns/remove/index.ts +87 -0
- package/src/patterns/remove/it.ts +69 -0
- package/src/patterns/remove/ja.ts +74 -0
- package/src/patterns/remove/ko.ts +78 -0
- package/src/patterns/remove/ms.ts +30 -0
- package/src/patterns/remove/pl.ts +62 -0
- package/src/patterns/remove/ru.ts +62 -0
- package/src/patterns/remove/th.ts +49 -0
- package/src/patterns/remove/tl.ts +30 -0
- package/src/patterns/remove/tr.ts +78 -0
- package/src/patterns/remove/uk.ts +62 -0
- package/src/patterns/remove/vi.ts +61 -0
- package/src/patterns/remove/zh.ts +72 -0
- package/src/patterns/set/ar.ts +84 -0
- package/src/patterns/set/bn.ts +53 -0
- package/src/patterns/set/de.ts +84 -0
- package/src/patterns/set/es.ts +92 -0
- package/src/patterns/set/fr.ts +88 -0
- package/src/patterns/set/hi.ts +56 -0
- package/src/patterns/set/id.ts +84 -0
- package/src/patterns/set/index.ts +107 -0
- package/src/patterns/set/it.ts +56 -0
- package/src/patterns/set/ja.ts +86 -0
- package/src/patterns/set/ko.ts +85 -0
- package/src/patterns/set/ms.ts +30 -0
- package/src/patterns/set/pl.ts +57 -0
- package/src/patterns/set/pt.ts +84 -0
- package/src/patterns/set/ru.ts +57 -0
- package/src/patterns/set/th.ts +31 -0
- package/src/patterns/set/tl.ts +30 -0
- package/src/patterns/set/tr.ts +107 -0
- package/src/patterns/set/uk.ts +57 -0
- package/src/patterns/set/vi.ts +53 -0
- package/src/patterns/set/zh.ts +84 -0
- package/src/patterns/show/ar.ts +67 -0
- package/src/patterns/show/bn.ts +47 -0
- package/src/patterns/show/de.ts +32 -0
- package/src/patterns/show/fr.ts +32 -0
- package/src/patterns/show/hi.ts +61 -0
- package/src/patterns/show/index.ts +95 -0
- package/src/patterns/show/it.ts +56 -0
- package/src/patterns/show/ja.ts +69 -0
- package/src/patterns/show/ko.ts +73 -0
- package/src/patterns/show/ms.ts +30 -0
- package/src/patterns/show/pl.ts +57 -0
- package/src/patterns/show/ru.ts +57 -0
- package/src/patterns/show/th.ts +29 -0
- package/src/patterns/show/tl.ts +30 -0
- package/src/patterns/show/tr.ts +65 -0
- package/src/patterns/show/uk.ts +57 -0
- package/src/patterns/show/vi.ts +56 -0
- package/src/patterns/show/zh.ts +68 -0
- package/src/patterns/take/ar.ts +51 -0
- package/src/patterns/take/index.ts +31 -0
- package/src/patterns/toggle/ar.ts +61 -0
- package/src/patterns/toggle/bn.ts +70 -0
- package/src/patterns/toggle/en.ts +61 -0
- package/src/patterns/toggle/es.ts +61 -0
- package/src/patterns/toggle/hi.ts +80 -0
- package/src/patterns/toggle/index.ts +95 -0
- package/src/patterns/toggle/it.ts +69 -0
- package/src/patterns/toggle/ja.ts +156 -0
- package/src/patterns/toggle/ko.ts +113 -0
- package/src/patterns/toggle/ms.ts +30 -0
- package/src/patterns/toggle/pl.ts +62 -0
- package/src/patterns/toggle/ru.ts +62 -0
- package/src/patterns/toggle/th.ts +50 -0
- package/src/patterns/toggle/tl.ts +30 -0
- package/src/patterns/toggle/tr.ts +88 -0
- package/src/patterns/toggle/uk.ts +62 -0
- package/src/patterns/toggle/vi.ts +61 -0
- package/src/patterns/toggle/zh.ts +99 -0
- package/src/public-api.ts +286 -0
- package/src/registry.ts +441 -0
- package/src/tokenizers/arabic.ts +723 -0
- package/src/tokenizers/base.ts +1300 -0
- package/src/tokenizers/bengali.ts +289 -0
- package/src/tokenizers/chinese.ts +481 -0
- package/src/tokenizers/english.ts +416 -0
- package/src/tokenizers/french.ts +326 -0
- package/src/tokenizers/german.ts +324 -0
- package/src/tokenizers/hindi.ts +319 -0
- package/src/tokenizers/index.ts +127 -0
- package/src/tokenizers/indonesian.ts +306 -0
- package/src/tokenizers/italian.ts +458 -0
- package/src/tokenizers/japanese.ts +447 -0
- package/src/tokenizers/korean.ts +642 -0
- package/src/tokenizers/morphology/arabic-normalizer.ts +242 -0
- package/src/tokenizers/morphology/french-normalizer.ts +268 -0
- package/src/tokenizers/morphology/german-normalizer.ts +256 -0
- package/src/tokenizers/morphology/index.ts +46 -0
- package/src/tokenizers/morphology/italian-normalizer.ts +329 -0
- package/src/tokenizers/morphology/japanese-normalizer.ts +288 -0
- package/src/tokenizers/morphology/korean-normalizer.ts +428 -0
- package/src/tokenizers/morphology/polish-normalizer.ts +264 -0
- package/src/tokenizers/morphology/portuguese-normalizer.ts +310 -0
- package/src/tokenizers/morphology/spanish-normalizer.ts +327 -0
- package/src/tokenizers/morphology/turkish-normalizer.ts +412 -0
- package/src/tokenizers/morphology/types.ts +211 -0
- package/src/tokenizers/ms.ts +198 -0
- package/src/tokenizers/polish.ts +354 -0
- package/src/tokenizers/portuguese.ts +304 -0
- package/src/tokenizers/quechua.ts +339 -0
- package/src/tokenizers/russian.ts +375 -0
- package/src/tokenizers/spanish.ts +403 -0
- package/src/tokenizers/swahili.ts +303 -0
- package/src/tokenizers/thai.ts +236 -0
- package/src/tokenizers/tl.ts +198 -0
- package/src/tokenizers/turkish.ts +411 -0
- package/src/tokenizers/ukrainian.ts +369 -0
- package/src/tokenizers/vietnamese.ts +410 -0
- package/src/types/grammar-types.ts +617 -0
- package/src/types/unified-profile.ts +267 -0
- package/src/types.ts +709 -0
- package/src/utils/confidence-calculator.ts +147 -0
- package/src/validators/command-validator.ts +380 -0
- package/src/validators/index.ts +15 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Static Analysis
|
|
3
|
+
*
|
|
4
|
+
* Analyzes semantic nodes for potential issues:
|
|
5
|
+
* - Conflicting actions on same trigger
|
|
6
|
+
* - Accessibility problems (hover-only interactions)
|
|
7
|
+
* - Performance concerns (high-frequency triggers)
|
|
8
|
+
* - Invalid role combinations
|
|
9
|
+
*
|
|
10
|
+
* Can be used:
|
|
11
|
+
* - Standalone: analyze(input, lang)
|
|
12
|
+
* - Dev mode: Enabled via config, auto-warns on parse
|
|
13
|
+
* - Build time: Integrate with bundlers
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { SemanticNode, SemanticRole } from '../types';
|
|
17
|
+
import { parse } from '../parser';
|
|
18
|
+
import { getSchema } from '../validators';
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Types
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
export type WarningSeverity = 'error' | 'warning' | 'info';
|
|
25
|
+
|
|
26
|
+
export type WarningCode =
|
|
27
|
+
| 'HOVER_ONLY_INTERACTION'
|
|
28
|
+
| 'HIGH_FREQUENCY_TRIGGER'
|
|
29
|
+
| 'MISSING_REQUIRED_ROLE'
|
|
30
|
+
| 'INVALID_ROLE_FOR_COMMAND'
|
|
31
|
+
| 'CONFLICTING_ACTIONS'
|
|
32
|
+
| 'UNREACHABLE_BEHAVIOR'
|
|
33
|
+
| 'POTENTIAL_RACE_CONDITION';
|
|
34
|
+
|
|
35
|
+
export interface AnalysisWarning {
|
|
36
|
+
code: WarningCode;
|
|
37
|
+
severity: WarningSeverity;
|
|
38
|
+
message: string;
|
|
39
|
+
suggestion?: string;
|
|
40
|
+
location?: {
|
|
41
|
+
input: string;
|
|
42
|
+
role?: SemanticRole;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AnalysisResult {
|
|
47
|
+
valid: boolean;
|
|
48
|
+
warnings: AnalysisWarning[];
|
|
49
|
+
node: SemanticNode | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface AnalysisConfig {
|
|
53
|
+
/** Enable accessibility checks (default: true) */
|
|
54
|
+
accessibility?: boolean;
|
|
55
|
+
/** Enable performance checks (default: true) */
|
|
56
|
+
performance?: boolean;
|
|
57
|
+
/** Enable schema validation (default: true) */
|
|
58
|
+
schema?: boolean;
|
|
59
|
+
/** Treat warnings as errors (default: false) */
|
|
60
|
+
strict?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const DEFAULT_CONFIG: Required<AnalysisConfig> = {
|
|
64
|
+
accessibility: true,
|
|
65
|
+
performance: true,
|
|
66
|
+
schema: true,
|
|
67
|
+
strict: false,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// =============================================================================
|
|
71
|
+
// High-Frequency Triggers
|
|
72
|
+
// =============================================================================
|
|
73
|
+
|
|
74
|
+
const HIGH_FREQUENCY_TRIGGERS = new Set([
|
|
75
|
+
'scroll',
|
|
76
|
+
'mousemove',
|
|
77
|
+
'touchmove',
|
|
78
|
+
'resize',
|
|
79
|
+
'input',
|
|
80
|
+
'keydown',
|
|
81
|
+
'keyup',
|
|
82
|
+
'keypress',
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const THROTTLE_RECOMMENDED_TRIGGERS = new Set(['scroll', 'mousemove', 'touchmove', 'resize']);
|
|
86
|
+
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// Analysis Rules
|
|
89
|
+
// =============================================================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check for hover-only interactions (accessibility issue).
|
|
93
|
+
*/
|
|
94
|
+
function checkAccessibility(node: SemanticNode, input: string): AnalysisWarning[] {
|
|
95
|
+
const warnings: AnalysisWarning[] = [];
|
|
96
|
+
|
|
97
|
+
if (node.kind === 'event-handler') {
|
|
98
|
+
const event = node.roles.get('event');
|
|
99
|
+
if (event && event.type === 'literal') {
|
|
100
|
+
const eventValue = String(event.value).toLowerCase();
|
|
101
|
+
|
|
102
|
+
// Hover-only interactions
|
|
103
|
+
if (eventValue === 'mouseenter' || eventValue === 'mouseover' || eventValue === 'hover') {
|
|
104
|
+
warnings.push({
|
|
105
|
+
code: 'HOVER_ONLY_INTERACTION',
|
|
106
|
+
severity: 'warning',
|
|
107
|
+
message: `Hover-only interaction detected (${eventValue}). Not accessible to keyboard or touch users.`,
|
|
108
|
+
suggestion: 'Add keyboard equivalent (focus) or use a click-based interaction.',
|
|
109
|
+
location: { input, role: 'event' },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return warnings;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check for high-frequency events without throttling.
|
|
120
|
+
*/
|
|
121
|
+
function checkPerformance(node: SemanticNode, input: string): AnalysisWarning[] {
|
|
122
|
+
const warnings: AnalysisWarning[] = [];
|
|
123
|
+
|
|
124
|
+
if (node.kind === 'event-handler') {
|
|
125
|
+
const event = node.roles.get('event');
|
|
126
|
+
if (event && event.type === 'literal') {
|
|
127
|
+
const eventValue = String(event.value).toLowerCase();
|
|
128
|
+
|
|
129
|
+
if (HIGH_FREQUENCY_TRIGGERS.has(eventValue)) {
|
|
130
|
+
const severity = THROTTLE_RECOMMENDED_TRIGGERS.has(eventValue) ? 'warning' : 'info';
|
|
131
|
+
warnings.push({
|
|
132
|
+
code: 'HIGH_FREQUENCY_TRIGGER',
|
|
133
|
+
severity,
|
|
134
|
+
message: `High-frequency event '${eventValue}' may cause performance issues.`,
|
|
135
|
+
suggestion: THROTTLE_RECOMMENDED_TRIGGERS.has(eventValue)
|
|
136
|
+
? `Consider using 'on ${eventValue} throttled:100ms' to limit execution frequency.`
|
|
137
|
+
: 'Consider debouncing or throttling this handler.',
|
|
138
|
+
location: { input, role: 'event' },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return warnings;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Validate role combinations against command schema.
|
|
149
|
+
*/
|
|
150
|
+
function checkSchema(node: SemanticNode, input: string): AnalysisWarning[] {
|
|
151
|
+
const warnings: AnalysisWarning[] = [];
|
|
152
|
+
|
|
153
|
+
if (node.kind !== 'command') return warnings;
|
|
154
|
+
|
|
155
|
+
const schema = getSchema(node.action);
|
|
156
|
+
if (!schema) return warnings; // Unknown command, can't validate
|
|
157
|
+
|
|
158
|
+
// Schema.roles is an array of role definitions
|
|
159
|
+
const roleArray = Array.isArray(schema.roles) ? schema.roles : [];
|
|
160
|
+
|
|
161
|
+
// Build a set of valid role names for this command
|
|
162
|
+
const validRoles = new Set<string>();
|
|
163
|
+
for (const roleSpec of roleArray) {
|
|
164
|
+
if (roleSpec && typeof roleSpec === 'object' && 'role' in roleSpec) {
|
|
165
|
+
validRoles.add(roleSpec.role);
|
|
166
|
+
|
|
167
|
+
// Check required roles
|
|
168
|
+
if (roleSpec.required && !node.roles.has(roleSpec.role as SemanticRole)) {
|
|
169
|
+
warnings.push({
|
|
170
|
+
code: 'MISSING_REQUIRED_ROLE',
|
|
171
|
+
severity: 'error',
|
|
172
|
+
message: `Command '${node.action}' requires '${roleSpec.role}' but it was not provided.`,
|
|
173
|
+
suggestion: roleSpec.description,
|
|
174
|
+
location: { input },
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check for unknown roles
|
|
181
|
+
node.roles.forEach((_, role) => {
|
|
182
|
+
if (!validRoles.has(role)) {
|
|
183
|
+
warnings.push({
|
|
184
|
+
code: 'INVALID_ROLE_FOR_COMMAND',
|
|
185
|
+
severity: 'warning',
|
|
186
|
+
message: `Role '${role}' is not typically used with '${node.action}' command.`,
|
|
187
|
+
location: { input, role },
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return warnings;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// =============================================================================
|
|
196
|
+
// Multi-Node Analysis (for detecting conflicts)
|
|
197
|
+
// =============================================================================
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Helper type for event handler nodes with body.
|
|
201
|
+
*/
|
|
202
|
+
interface EventHandlerNode extends SemanticNode {
|
|
203
|
+
body?: SemanticNode[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get the body commands from an event handler node.
|
|
208
|
+
*/
|
|
209
|
+
function getBodyCommands(node: SemanticNode): SemanticNode[] {
|
|
210
|
+
if (node.kind === 'event-handler') {
|
|
211
|
+
const handler = node as EventHandlerNode;
|
|
212
|
+
return handler.body ?? [];
|
|
213
|
+
}
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Analyze multiple nodes together to detect conflicts.
|
|
219
|
+
*/
|
|
220
|
+
export function analyzeMultiple(
|
|
221
|
+
nodes: SemanticNode[],
|
|
222
|
+
_config: AnalysisConfig = {}
|
|
223
|
+
): AnalysisWarning[] {
|
|
224
|
+
const warnings: AnalysisWarning[] = [];
|
|
225
|
+
|
|
226
|
+
// Group event handlers by event + source
|
|
227
|
+
const handlersByEvent = new Map<string, { handler: SemanticNode; commands: SemanticNode[] }[]>();
|
|
228
|
+
|
|
229
|
+
for (const node of nodes) {
|
|
230
|
+
if (node.kind === 'event-handler') {
|
|
231
|
+
const event = node.roles.get('event');
|
|
232
|
+
const source = node.roles.get('source');
|
|
233
|
+
|
|
234
|
+
const key = `${event?.type === 'literal' ? event.value : 'unknown'}:${
|
|
235
|
+
source?.type === 'selector' ? source.value : 'self'
|
|
236
|
+
}`;
|
|
237
|
+
|
|
238
|
+
if (!handlersByEvent.has(key)) {
|
|
239
|
+
handlersByEvent.set(key, []);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const commands = getBodyCommands(node);
|
|
243
|
+
handlersByEvent.get(key)!.push({ handler: node, commands });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check for conflicting actions on same event
|
|
248
|
+
handlersByEvent.forEach((handlers, key) => {
|
|
249
|
+
if (handlers.length > 1) {
|
|
250
|
+
// Collect all actions and patients from body commands
|
|
251
|
+
const allActions: string[] = [];
|
|
252
|
+
const allPatients: string[] = [];
|
|
253
|
+
|
|
254
|
+
for (const { commands } of handlers) {
|
|
255
|
+
for (const cmd of commands) {
|
|
256
|
+
allActions.push(cmd.action);
|
|
257
|
+
const p = cmd.roles.get('patient');
|
|
258
|
+
allPatients.push(p?.type === 'selector' ? p.value : 'unknown');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check for direct conflicts (e.g., toggle + hide on same element)
|
|
263
|
+
const hasToggle = allActions.includes('toggle');
|
|
264
|
+
const hasShow = allActions.includes('show');
|
|
265
|
+
const hasHide = allActions.includes('hide');
|
|
266
|
+
|
|
267
|
+
if (hasToggle && (hasShow || hasHide)) {
|
|
268
|
+
// Check if conflicting actions target the same patient
|
|
269
|
+
const conflictingPatients = allPatients.filter(
|
|
270
|
+
(p, i) =>
|
|
271
|
+
(allActions[i] === 'toggle' || allActions[i] === 'show' || allActions[i] === 'hide') &&
|
|
272
|
+
allPatients.some((p2, i2) => i !== i2 && p === p2)
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
if (conflictingPatients.length > 0) {
|
|
276
|
+
warnings.push({
|
|
277
|
+
code: 'CONFLICTING_ACTIONS',
|
|
278
|
+
severity: 'warning',
|
|
279
|
+
message: `Conflicting actions on '${key}': ${[...new Set(allActions)].join(', ')} may interfere with each other.`,
|
|
280
|
+
suggestion: 'Consider using a single toggle or conditional logic.',
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Check for potential race conditions with async operations
|
|
286
|
+
const hasAsync = allActions.some(a => ['fetch', 'wait', 'send'].includes(a));
|
|
287
|
+
if (hasAsync && handlers.length > 1) {
|
|
288
|
+
warnings.push({
|
|
289
|
+
code: 'POTENTIAL_RACE_CONDITION',
|
|
290
|
+
severity: 'info',
|
|
291
|
+
message: `Multiple handlers on '${key}' include async operations. Consider sequencing.`,
|
|
292
|
+
suggestion: 'Use "then" to chain operations or add loading states.',
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return warnings;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// =============================================================================
|
|
302
|
+
// Main Analysis Function
|
|
303
|
+
// =============================================================================
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Analyze a single hyperscript input for potential issues.
|
|
307
|
+
*
|
|
308
|
+
* @param input - The hyperscript text to analyze
|
|
309
|
+
* @param lang - The language of the input (default: 'en')
|
|
310
|
+
* @param config - Analysis configuration
|
|
311
|
+
* @returns Analysis result with warnings
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```typescript
|
|
315
|
+
* const result = analyze('on hover show .tooltip', 'en');
|
|
316
|
+
* // result.warnings[0].code === 'HOVER_ONLY_INTERACTION'
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export function analyze(
|
|
320
|
+
input: string,
|
|
321
|
+
lang: string = 'en',
|
|
322
|
+
config: AnalysisConfig = {}
|
|
323
|
+
): AnalysisResult {
|
|
324
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
325
|
+
const warnings: AnalysisWarning[] = [];
|
|
326
|
+
|
|
327
|
+
// Parse to semantic node (wrap in try/catch since parse() may throw)
|
|
328
|
+
let node: SemanticNode | null = null;
|
|
329
|
+
try {
|
|
330
|
+
node = parse(input, lang);
|
|
331
|
+
} catch {
|
|
332
|
+
// Parse failed, node stays null
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (!node) {
|
|
336
|
+
return {
|
|
337
|
+
valid: false,
|
|
338
|
+
warnings: [
|
|
339
|
+
{
|
|
340
|
+
code: 'UNREACHABLE_BEHAVIOR',
|
|
341
|
+
severity: 'error',
|
|
342
|
+
message: 'Could not parse input to semantic representation.',
|
|
343
|
+
location: { input },
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
node: null,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Run enabled checks
|
|
351
|
+
if (cfg.accessibility) {
|
|
352
|
+
warnings.push(...checkAccessibility(node, input));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (cfg.performance) {
|
|
356
|
+
warnings.push(...checkPerformance(node, input));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (cfg.schema) {
|
|
360
|
+
warnings.push(...checkSchema(node, input));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Determine validity
|
|
364
|
+
const hasErrors = warnings.some(w => w.severity === 'error');
|
|
365
|
+
const hasWarnings = warnings.some(w => w.severity === 'warning');
|
|
366
|
+
const valid = cfg.strict ? !hasErrors && !hasWarnings : !hasErrors;
|
|
367
|
+
|
|
368
|
+
return { valid, warnings, node };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Analyze multiple hyperscript inputs together.
|
|
373
|
+
*
|
|
374
|
+
* @param inputs - Array of hyperscript texts
|
|
375
|
+
* @param lang - The language of the inputs
|
|
376
|
+
* @param config - Analysis configuration
|
|
377
|
+
* @returns Combined analysis result
|
|
378
|
+
*/
|
|
379
|
+
export function analyzeAll(
|
|
380
|
+
inputs: string[],
|
|
381
|
+
lang: string = 'en',
|
|
382
|
+
config: AnalysisConfig = {}
|
|
383
|
+
): AnalysisResult {
|
|
384
|
+
const allWarnings: AnalysisWarning[] = [];
|
|
385
|
+
const nodes: SemanticNode[] = [];
|
|
386
|
+
|
|
387
|
+
// Analyze each input individually
|
|
388
|
+
for (const input of inputs) {
|
|
389
|
+
const result = analyze(input, lang, config);
|
|
390
|
+
allWarnings.push(...result.warnings);
|
|
391
|
+
if (result.node) {
|
|
392
|
+
nodes.push(result.node);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Run cross-input analysis
|
|
397
|
+
allWarnings.push(...analyzeMultiple(nodes, config));
|
|
398
|
+
|
|
399
|
+
const hasErrors = allWarnings.some(w => w.severity === 'error');
|
|
400
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
401
|
+
const hasWarnings = allWarnings.some(w => w.severity === 'warning');
|
|
402
|
+
const valid = cfg.strict ? !hasErrors && !hasWarnings : !hasErrors;
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
valid,
|
|
406
|
+
warnings: allWarnings,
|
|
407
|
+
node: nodes[0] ?? null, // Return first node for single-input compat
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// =============================================================================
|
|
412
|
+
// Dev Mode Integration
|
|
413
|
+
// =============================================================================
|
|
414
|
+
|
|
415
|
+
let _devModeEnabled = false;
|
|
416
|
+
let _devModeConfig: AnalysisConfig = {};
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Enable dev mode analysis.
|
|
420
|
+
* When enabled, every parse() call will run analysis and log warnings.
|
|
421
|
+
*/
|
|
422
|
+
export function enableDevMode(config: AnalysisConfig = {}): void {
|
|
423
|
+
_devModeEnabled = true;
|
|
424
|
+
_devModeConfig = config;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Disable dev mode analysis.
|
|
429
|
+
*/
|
|
430
|
+
export function disableDevMode(): void {
|
|
431
|
+
_devModeEnabled = false;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Check if dev mode is enabled.
|
|
436
|
+
*/
|
|
437
|
+
export function isDevModeEnabled(): boolean {
|
|
438
|
+
return _devModeEnabled;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Get current dev mode config.
|
|
443
|
+
*/
|
|
444
|
+
export function getDevModeConfig(): AnalysisConfig {
|
|
445
|
+
return { ..._devModeConfig };
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Run dev mode analysis if enabled.
|
|
450
|
+
* Called internally by parser when dev mode is on.
|
|
451
|
+
*/
|
|
452
|
+
export function devModeAnalyze(input: string, lang: string, node: SemanticNode | null): void {
|
|
453
|
+
if (!_devModeEnabled) return;
|
|
454
|
+
|
|
455
|
+
if (!node) {
|
|
456
|
+
console.warn(`[hyperfixi] Parse failed: ${input}`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const result = analyze(input, lang, _devModeConfig);
|
|
461
|
+
|
|
462
|
+
for (const warning of result.warnings) {
|
|
463
|
+
const icon = warning.severity === 'error' ? '❌' : warning.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
464
|
+
const method =
|
|
465
|
+
warning.severity === 'error' ? 'error' : warning.severity === 'warning' ? 'warn' : 'info';
|
|
466
|
+
|
|
467
|
+
console[method](
|
|
468
|
+
`${icon} [hyperfixi:${warning.code}] ${warning.message}`,
|
|
469
|
+
warning.suggestion ? `\n 💡 ${warning.suggestion}` : '',
|
|
470
|
+
warning.location?.input ? `\n 📍 ${warning.location.input}` : ''
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// =============================================================================
|
|
476
|
+
// Convenience Exports
|
|
477
|
+
// =============================================================================
|
|
478
|
+
|
|
479
|
+
export { checkAccessibility, checkPerformance, checkSchema };
|