@overcrow/mcp 0.0.0 → 0.1.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 +91 -1
- package/dist/audit/index.js +103 -0
- package/dist/audit/lexer.js +417 -0
- package/dist/audit/project.js +200 -0
- package/dist/audit/rules.js +661 -0
- package/dist/bootstrap/download.js +229 -0
- package/dist/bootstrap/install.js +280 -0
- package/dist/bootstrap/pin.js +85 -0
- package/dist/bootstrap/pin.json +119 -0
- package/dist/bootstrap/zip.js +258 -0
- package/dist/cli.js +200 -0
- package/dist/confine.js +244 -0
- package/dist/content/docs/en/cli.md +353 -0
- package/dist/content/docs/en/dev-channel.md +130 -0
- package/dist/content/docs/en/elements.md +469 -0
- package/dist/content/docs/en/forms.md +328 -0
- package/dist/content/docs/en/guide.md +253 -0
- package/dist/content/docs/en/index.md +85 -0
- package/dist/content/docs/en/limits.md +241 -0
- package/dist/content/docs/en/logic.md +446 -0
- package/dist/content/docs/en/manifest.md +381 -0
- package/dist/content/docs/en/mcp.md +165 -0
- package/dist/content/docs/en/package.md +131 -0
- package/dist/content/docs/en/publishing.md +162 -0
- package/dist/content/docs/en/reference.md +55 -0
- package/dist/content/docs/en/result-shapes.md +332 -0
- package/dist/content/docs/en/sdk.md +295 -0
- package/dist/content/docs/en/security.md +115 -0
- package/dist/content/docs/en/service-reference.md +344 -0
- package/dist/content/docs/en/services.md +517 -0
- package/dist/content/docs/en/style-properties.md +270 -0
- package/dist/content/docs/en/style.md +266 -0
- package/dist/content/docs/en/testing.md +400 -0
- package/dist/content/docs/en/view.md +338 -0
- package/dist/content/docs/en/widgets.md +76 -0
- package/dist/content/docs/fr/cli.md +367 -0
- package/dist/content/docs/fr/dev-channel.md +140 -0
- package/dist/content/docs/fr/elements.md +475 -0
- package/dist/content/docs/fr/forms.md +341 -0
- package/dist/content/docs/fr/guide.md +264 -0
- package/dist/content/docs/fr/index.md +90 -0
- package/dist/content/docs/fr/limits.md +244 -0
- package/dist/content/docs/fr/logic.md +461 -0
- package/dist/content/docs/fr/manifest.md +392 -0
- package/dist/content/docs/fr/mcp.md +173 -0
- package/dist/content/docs/fr/package.md +139 -0
- package/dist/content/docs/fr/publishing.md +172 -0
- package/dist/content/docs/fr/reference.md +59 -0
- package/dist/content/docs/fr/result-shapes.md +333 -0
- package/dist/content/docs/fr/sdk.md +301 -0
- package/dist/content/docs/fr/security.md +129 -0
- package/dist/content/docs/fr/service-reference.md +346 -0
- package/dist/content/docs/fr/services.md +540 -0
- package/dist/content/docs/fr/style-properties.md +272 -0
- package/dist/content/docs/fr/style.md +281 -0
- package/dist/content/docs/fr/testing.md +417 -0
- package/dist/content/docs/fr/view.md +352 -0
- package/dist/content/docs/fr/widgets.md +81 -0
- package/dist/content/examples/clock/LICENSE +21 -0
- package/dist/content/examples/clock/README.md +75 -0
- package/dist/content/examples/clock/listing.json +19 -0
- package/dist/content/examples/clock/locales/en.json +4 -0
- package/dist/content/examples/clock/locales/fr.json +4 -0
- package/dist/content/examples/clock/logic.ts +85 -0
- package/dist/content/examples/clock/manifest.json +44 -0
- package/dist/content/examples/clock/package.json +16 -0
- package/dist/content/examples/clock/style.ocss +26 -0
- package/dist/content/examples/clock/tests/hidden.scenario.json +68 -0
- package/dist/content/examples/clock/tests/load-logic.mjs +22 -0
- package/dist/content/examples/clock/tests/logic.test.mjs +89 -0
- package/dist/content/examples/clock/tests/options.scenario.json +108 -0
- package/dist/content/examples/clock/tests/preview.scenario.json +25 -0
- package/dist/content/examples/clock/tests/render.scenario.json +94 -0
- package/dist/content/examples/clock/tests/summer-time.scenario.json +74 -0
- package/dist/content/examples/clock/tests/ticks.scenario.json +120 -0
- package/dist/content/examples/clock/tsconfig.json +16 -0
- package/dist/content/examples/clock/view.ocml +8 -0
- package/dist/content/examples/countdown/LICENSE +20 -0
- package/dist/content/examples/countdown/listing.json +10 -0
- package/dist/content/examples/countdown/locales/en.json +15 -0
- package/dist/content/examples/countdown/locales/fr.json +15 -0
- package/dist/content/examples/countdown/logic.ts +179 -0
- package/dist/content/examples/countdown/manifest.json +34 -0
- package/dist/content/examples/countdown/package.json +15 -0
- package/dist/content/examples/countdown/style.ocss +94 -0
- package/dist/content/examples/countdown/tsconfig.json +16 -0
- package/dist/content/examples/countdown/view.ocml +42 -0
- package/dist/content/examples/fps/LICENSE +21 -0
- package/dist/content/examples/fps/README.md +64 -0
- package/dist/content/examples/fps/listing.json +19 -0
- package/dist/content/examples/fps/locales/en.json +11 -0
- package/dist/content/examples/fps/locales/fr.json +11 -0
- package/dist/content/examples/fps/logic.ts +64 -0
- package/dist/content/examples/fps/manifest.json +30 -0
- package/dist/content/examples/fps/package.json +16 -0
- package/dist/content/examples/fps/style.ocss +31 -0
- package/dist/content/examples/fps/tests/label.scenario.json +63 -0
- package/dist/content/examples/fps/tests/load-logic.mjs +22 -0
- package/dist/content/examples/fps/tests/logic.test.mjs +60 -0
- package/dist/content/examples/fps/tests/preview.scenario.json +29 -0
- package/dist/content/examples/fps/tests/refused.scenario.json +20 -0
- package/dist/content/examples/fps/tests/render.scenario.json +96 -0
- package/dist/content/examples/fps/tests/stale.scenario.json +91 -0
- package/dist/content/examples/fps/tests/unavailable.scenario.json +111 -0
- package/dist/content/examples/fps/tsconfig.json +16 -0
- package/dist/content/examples/fps/view.ocml +11 -0
- package/dist/content/examples/media/LICENSE +21 -0
- package/dist/content/examples/media/README.md +147 -0
- package/dist/content/examples/media/listing.json +19 -0
- package/dist/content/examples/media/locales/en.json +11 -0
- package/dist/content/examples/media/locales/fr.json +11 -0
- package/dist/content/examples/media/logic.ts +191 -0
- package/dist/content/examples/media/manifest.json +35 -0
- package/dist/content/examples/media/package.json +16 -0
- package/dist/content/examples/media/style.ocss +148 -0
- package/dist/content/examples/media/tests/covers.scenario.json +144 -0
- package/dist/content/examples/media/tests/gesture.scenario.json +173 -0
- package/dist/content/examples/media/tests/load-logic.mjs +22 -0
- package/dist/content/examples/media/tests/logic.test.mjs +176 -0
- package/dist/content/examples/media/tests/marquee.scenario.json +127 -0
- package/dist/content/examples/media/tests/preview.scenario.json +52 -0
- package/dist/content/examples/media/tests/read-only.scenario.json +53 -0
- package/dist/content/examples/media/tests/refused.scenario.json +22 -0
- package/dist/content/examples/media/tests/refused.test.mjs +31 -0
- package/dist/content/examples/media/tests/render.scenario.json +136 -0
- package/dist/content/examples/media/tests/states.scenario.json +257 -0
- package/dist/content/examples/media/tests/unavailable.scenario.json +120 -0
- package/dist/content/examples/media/tsconfig.json +16 -0
- package/dist/content/examples/media/view.ocml +41 -0
- package/dist/content/examples/notes/LICENSE +21 -0
- package/dist/content/examples/notes/README.md +219 -0
- package/dist/content/examples/notes/listing.json +19 -0
- package/dist/content/examples/notes/locales/en.json +25 -0
- package/dist/content/examples/notes/locales/fr.json +25 -0
- package/dist/content/examples/notes/logic.ts +799 -0
- package/dist/content/examples/notes/manifest.json +40 -0
- package/dist/content/examples/notes/package.json +16 -0
- package/dist/content/examples/notes/style.ocss +469 -0
- package/dist/content/examples/notes/tests/checks.scenario.json +237 -0
- package/dist/content/examples/notes/tests/create.scenario.json +260 -0
- package/dist/content/examples/notes/tests/delete.scenario.json +342 -0
- package/dist/content/examples/notes/tests/drafts.scenario.json +714 -0
- package/dist/content/examples/notes/tests/editor.scenario.json +232 -0
- package/dist/content/examples/notes/tests/frame.scenario.json +93 -0
- package/dist/content/examples/notes/tests/interactive.scenario.json +344 -0
- package/dist/content/examples/notes/tests/limits.scenario.json +1351 -0
- package/dist/content/examples/notes/tests/load-logic.mjs +22 -0
- package/dist/content/examples/notes/tests/logic.test.mjs +514 -0
- package/dist/content/examples/notes/tests/preview.scenario.json +99 -0
- package/dist/content/examples/notes/tests/read-only.scenario.json +105 -0
- package/dist/content/examples/notes/tests/refused.scenario.json +33 -0
- package/dist/content/examples/notes/tests/save.scenario.json +378 -0
- package/dist/content/examples/notes/tests/states.scenario.json +640 -0
- package/dist/content/examples/notes/tests/typing.scenario.json +562 -0
- package/dist/content/examples/notes/tests/unavailable.scenario.json +246 -0
- package/dist/content/examples/notes/tsconfig.json +16 -0
- package/dist/content/examples/notes/view.ocml +154 -0
- package/dist/content/examples/performance/LICENSE +21 -0
- package/dist/content/examples/performance/README.md +134 -0
- package/dist/content/examples/performance/listing.json +19 -0
- package/dist/content/examples/performance/locales/en.json +20 -0
- package/dist/content/examples/performance/locales/fr.json +20 -0
- package/dist/content/examples/performance/logic.ts +210 -0
- package/dist/content/examples/performance/manifest.json +83 -0
- package/dist/content/examples/performance/package.json +16 -0
- package/dist/content/examples/performance/style.ocss +75 -0
- package/dist/content/examples/performance/tests/fahrenheit.scenario.json +110 -0
- package/dist/content/examples/performance/tests/horizontal.scenario.json +96 -0
- package/dist/content/examples/performance/tests/load-logic.mjs +22 -0
- package/dist/content/examples/performance/tests/logic.test.mjs +226 -0
- package/dist/content/examples/performance/tests/menu.scenario.json +170 -0
- package/dist/content/examples/performance/tests/no-fps-source.scenario.json +47 -0
- package/dist/content/examples/performance/tests/preview.scenario.json +45 -0
- package/dist/content/examples/performance/tests/refused.scenario.json +20 -0
- package/dist/content/examples/performance/tests/refused.test.mjs +19 -0
- package/dist/content/examples/performance/tests/region.scenario.json +89 -0
- package/dist/content/examples/performance/tests/render.scenario.json +112 -0
- package/dist/content/examples/performance/tests/sources.scenario.json +151 -0
- package/dist/content/examples/performance/tests/stale.scenario.json +111 -0
- package/dist/content/examples/performance/tests/telemetry-only.scenario.json +44 -0
- package/dist/content/examples/performance/tests/thresholds.scenario.json +147 -0
- package/dist/content/examples/performance/tests/waiting.scenario.json +105 -0
- package/dist/content/examples/performance/tsconfig.json +16 -0
- package/dist/content/examples/performance/view.ocml +18 -0
- package/dist/content/examples/playervox-journal/LICENSE +21 -0
- package/dist/content/examples/playervox-journal/README.md +150 -0
- package/dist/content/examples/playervox-journal/listing.json +19 -0
- package/dist/content/examples/playervox-journal/locales/en.json +25 -0
- package/dist/content/examples/playervox-journal/locales/fr.json +25 -0
- package/dist/content/examples/playervox-journal/logic.ts +556 -0
- package/dist/content/examples/playervox-journal/manifest.json +19 -0
- package/dist/content/examples/playervox-journal/package.json +16 -0
- package/dist/content/examples/playervox-journal/style.ocss +142 -0
- package/dist/content/examples/playervox-journal/tests/changes.scenario.json +482 -0
- package/dist/content/examples/playervox-journal/tests/delete.scenario.json +432 -0
- package/dist/content/examples/playervox-journal/tests/frame.scenario.json +169 -0
- package/dist/content/examples/playervox-journal/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-journal/tests/logic.test.mjs +349 -0
- package/dist/content/examples/playervox-journal/tests/paging.scenario.json +428 -0
- package/dist/content/examples/playervox-journal/tests/preview.scenario.json +184 -0
- package/dist/content/examples/playervox-journal/tests/read-only.scenario.json +157 -0
- package/dist/content/examples/playervox-journal/tests/refused.scenario.json +39 -0
- package/dist/content/examples/playervox-journal/tests/scale.scenario.json +286 -0
- package/dist/content/examples/playervox-journal/tests/signed-out.scenario.json +134 -0
- package/dist/content/examples/playervox-journal/tests/states.scenario.json +741 -0
- package/dist/content/examples/playervox-journal/tests/unavailable.scenario.json +183 -0
- package/dist/content/examples/playervox-journal/tsconfig.json +16 -0
- package/dist/content/examples/playervox-journal/view.ocml +52 -0
- package/dist/content/examples/playervox-rating/LICENSE +21 -0
- package/dist/content/examples/playervox-rating/README.md +166 -0
- package/dist/content/examples/playervox-rating/listing.json +19 -0
- package/dist/content/examples/playervox-rating/locales/en.json +35 -0
- package/dist/content/examples/playervox-rating/locales/fr.json +35 -0
- package/dist/content/examples/playervox-rating/logic.ts +581 -0
- package/dist/content/examples/playervox-rating/manifest.json +19 -0
- package/dist/content/examples/playervox-rating/package.json +16 -0
- package/dist/content/examples/playervox-rating/style.ocss +333 -0
- package/dist/content/examples/playervox-rating/tests/account.scenario.json +125 -0
- package/dist/content/examples/playervox-rating/tests/bounds.scenario.json +73 -0
- package/dist/content/examples/playervox-rating/tests/draft.scenario.json +199 -0
- package/dist/content/examples/playervox-rating/tests/form.scenario.json +142 -0
- package/dist/content/examples/playervox-rating/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-rating/tests/logic.test.mjs +346 -0
- package/dist/content/examples/playervox-rating/tests/offline.scenario.json +142 -0
- package/dist/content/examples/playervox-rating/tests/preview.scenario.json +44 -0
- package/dist/content/examples/playervox-rating/tests/publish-keyboard.scenario.json +221 -0
- package/dist/content/examples/playervox-rating/tests/publish-pointer.scenario.json +314 -0
- package/dist/content/examples/playervox-rating/tests/read-only.scenario.json +70 -0
- package/dist/content/examples/playervox-rating/tests/refused.scenario.json +45 -0
- package/dist/content/examples/playervox-rating/tests/states.scenario.json +362 -0
- package/dist/content/examples/playervox-rating/tsconfig.json +16 -0
- package/dist/content/examples/playervox-rating/view.ocml +62 -0
- package/dist/content/examples/playervox-reviews/LICENSE +21 -0
- package/dist/content/examples/playervox-reviews/README.md +194 -0
- package/dist/content/examples/playervox-reviews/listing.json +19 -0
- package/dist/content/examples/playervox-reviews/locales/en.json +32 -0
- package/dist/content/examples/playervox-reviews/locales/fr.json +32 -0
- package/dist/content/examples/playervox-reviews/logic.ts +822 -0
- package/dist/content/examples/playervox-reviews/manifest.json +30 -0
- package/dist/content/examples/playervox-reviews/package.json +16 -0
- package/dist/content/examples/playervox-reviews/style.ocss +260 -0
- package/dist/content/examples/playervox-reviews/tests/account.scenario.json +249 -0
- package/dist/content/examples/playervox-reviews/tests/bounds.scenario.json +336 -0
- package/dist/content/examples/playervox-reviews/tests/changes.scenario.json +515 -0
- package/dist/content/examples/playervox-reviews/tests/filter.scenario.json +400 -0
- package/dist/content/examples/playervox-reviews/tests/frame.scenario.json +185 -0
- package/dist/content/examples/playervox-reviews/tests/keyboard.scenario.json +188 -0
- package/dist/content/examples/playervox-reviews/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-reviews/tests/logic.test.mjs +559 -0
- package/dist/content/examples/playervox-reviews/tests/offline-start.scenario.json +152 -0
- package/dist/content/examples/playervox-reviews/tests/offline.scenario.json +311 -0
- package/dist/content/examples/playervox-reviews/tests/paging.scenario.json +497 -0
- package/dist/content/examples/playervox-reviews/tests/preview.scenario.json +286 -0
- package/dist/content/examples/playervox-reviews/tests/reading.scenario.json +591 -0
- package/dist/content/examples/playervox-reviews/tests/refused.scenario.json +54 -0
- package/dist/content/examples/playervox-reviews/tests/scale.scenario.json +303 -0
- package/dist/content/examples/playervox-reviews/tests/states.scenario.json +700 -0
- package/dist/content/examples/playervox-reviews/tests/unavailable.scenario.json +192 -0
- package/dist/content/examples/playervox-reviews/tsconfig.json +16 -0
- package/dist/content/examples/playervox-reviews/view.ocml +110 -0
- package/dist/content/examples/playervox-score/LICENSE +21 -0
- package/dist/content/examples/playervox-score/README.md +131 -0
- package/dist/content/examples/playervox-score/listing.json +19 -0
- package/dist/content/examples/playervox-score/locales/en.json +23 -0
- package/dist/content/examples/playervox-score/locales/fr.json +23 -0
- package/dist/content/examples/playervox-score/logic.ts +430 -0
- package/dist/content/examples/playervox-score/manifest.json +47 -0
- package/dist/content/examples/playervox-score/package.json +16 -0
- package/dist/content/examples/playervox-score/style.ocss +247 -0
- package/dist/content/examples/playervox-score/tests/grades.scenario.json +279 -0
- package/dist/content/examples/playervox-score/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-score/tests/logic.test.mjs +229 -0
- package/dist/content/examples/playervox-score/tests/options.scenario.json +302 -0
- package/dist/content/examples/playervox-score/tests/preview.scenario.json +68 -0
- package/dist/content/examples/playervox-score/tests/pulse.scenario.json +157 -0
- package/dist/content/examples/playervox-score/tests/refused.scenario.json +21 -0
- package/dist/content/examples/playervox-score/tests/signed-out.scenario.json +46 -0
- package/dist/content/examples/playervox-score/tests/states.scenario.json +450 -0
- package/dist/content/examples/playervox-score/tests/unavailable.scenario.json +84 -0
- package/dist/content/examples/playervox-score/tsconfig.json +16 -0
- package/dist/content/examples/playervox-score/view.ocml +51 -0
- package/dist/content/examples/session/LICENSE +21 -0
- package/dist/content/examples/session/README.md +57 -0
- package/dist/content/examples/session/listing.json +19 -0
- package/dist/content/examples/session/locales/en.json +3 -0
- package/dist/content/examples/session/locales/fr.json +3 -0
- package/dist/content/examples/session/logic.ts +26 -0
- package/dist/content/examples/session/manifest.json +19 -0
- package/dist/content/examples/session/package.json +16 -0
- package/dist/content/examples/session/style.ocss +16 -0
- package/dist/content/examples/session/tests/hidden.scenario.json +45 -0
- package/dist/content/examples/session/tests/load-logic.mjs +22 -0
- package/dist/content/examples/session/tests/logic.test.mjs +37 -0
- package/dist/content/examples/session/tests/preview.scenario.json +28 -0
- package/dist/content/examples/session/tests/refused.scenario.json +20 -0
- package/dist/content/examples/session/tests/refused.test.mjs +16 -0
- package/dist/content/examples/session/tests/render.scenario.json +95 -0
- package/dist/content/examples/session/tests/ticks.scenario.json +139 -0
- package/dist/content/examples/session/tests/unknown.scenario.json +37 -0
- package/dist/content/examples/session/tsconfig.json +16 -0
- package/dist/content/examples/session/view.ocml +11 -0
- package/dist/content/examples/stopwatch/LICENSE +21 -0
- package/dist/content/examples/stopwatch/README.md +99 -0
- package/dist/content/examples/stopwatch/listing.json +19 -0
- package/dist/content/examples/stopwatch/locales/en.json +8 -0
- package/dist/content/examples/stopwatch/locales/fr.json +8 -0
- package/dist/content/examples/stopwatch/logic.ts +93 -0
- package/dist/content/examples/stopwatch/manifest.json +19 -0
- package/dist/content/examples/stopwatch/package.json +16 -0
- package/dist/content/examples/stopwatch/style.ocss +54 -0
- package/dist/content/examples/stopwatch/tests/errors.scenario.json +121 -0
- package/dist/content/examples/stopwatch/tests/gesture.scenario.json +154 -0
- package/dist/content/examples/stopwatch/tests/hidden.scenario.json +57 -0
- package/dist/content/examples/stopwatch/tests/hundredths.scenario.json +173 -0
- package/dist/content/examples/stopwatch/tests/load-logic.mjs +22 -0
- package/dist/content/examples/stopwatch/tests/logic.test.mjs +99 -0
- package/dist/content/examples/stopwatch/tests/preview.scenario.json +36 -0
- package/dist/content/examples/stopwatch/tests/read-only.scenario.json +42 -0
- package/dist/content/examples/stopwatch/tests/read-only.test.mjs +18 -0
- package/dist/content/examples/stopwatch/tests/refused.scenario.json +22 -0
- package/dist/content/examples/stopwatch/tests/refused.test.mjs +18 -0
- package/dist/content/examples/stopwatch/tests/render.scenario.json +105 -0
- package/dist/content/examples/stopwatch/tests/states.scenario.json +246 -0
- package/dist/content/examples/stopwatch/tests/unknown.scenario.json +40 -0
- package/dist/content/examples/stopwatch/tsconfig.json +16 -0
- package/dist/content/examples/stopwatch/view.ocml +29 -0
- package/dist/content/examples/twitch-chat/LICENSE +21 -0
- package/dist/content/examples/twitch-chat/README.md +283 -0
- package/dist/content/examples/twitch-chat/listing.json +19 -0
- package/dist/content/examples/twitch-chat/locales/en.json +52 -0
- package/dist/content/examples/twitch-chat/locales/fr.json +52 -0
- package/dist/content/examples/twitch-chat/logic.ts +887 -0
- package/dist/content/examples/twitch-chat/manifest.json +33 -0
- package/dist/content/examples/twitch-chat/package.json +16 -0
- package/dist/content/examples/twitch-chat/style.ocss +544 -0
- package/dist/content/examples/twitch-chat/tests/account.scenario.json +325 -0
- package/dist/content/examples/twitch-chat/tests/burst.scenario.json +1866 -0
- package/dist/content/examples/twitch-chat/tests/channel-refused.scenario.json +372 -0
- package/dist/content/examples/twitch-chat/tests/channel.scenario.json +480 -0
- package/dist/content/examples/twitch-chat/tests/draft.scenario.json +279 -0
- package/dist/content/examples/twitch-chat/tests/fade.scenario.json +269 -0
- package/dist/content/examples/twitch-chat/tests/failures.scenario.json +420 -0
- package/dist/content/examples/twitch-chat/tests/favorites.scenario.json +376 -0
- package/dist/content/examples/twitch-chat/tests/frame.scenario.json +152 -0
- package/dist/content/examples/twitch-chat/tests/lifetime.scenario.json +186 -0
- package/dist/content/examples/twitch-chat/tests/load-logic.mjs +22 -0
- package/dist/content/examples/twitch-chat/tests/logic.test.mjs +1053 -0
- package/dist/content/examples/twitch-chat/tests/passive.scenario.json +536 -0
- package/dist/content/examples/twitch-chat/tests/preview.scenario.json +147 -0
- package/dist/content/examples/twitch-chat/tests/read-only.scenario.json +202 -0
- package/dist/content/examples/twitch-chat/tests/read-only.test.mjs +63 -0
- package/dist/content/examples/twitch-chat/tests/refused.scenario.json +65 -0
- package/dist/content/examples/twitch-chat/tests/refused.test.mjs +43 -0
- package/dist/content/examples/twitch-chat/tests/render.scenario.json +347 -0
- package/dist/content/examples/twitch-chat/tests/reply.scenario.json +384 -0
- package/dist/content/examples/twitch-chat/tests/send-refused.scenario.json +383 -0
- package/dist/content/examples/twitch-chat/tests/send.scenario.json +300 -0
- package/dist/content/examples/twitch-chat/tests/states.scenario.json +455 -0
- package/dist/content/examples/twitch-chat/tests/unavailable.scenario.json +256 -0
- package/dist/content/examples/twitch-chat/tests/unread.scenario.json +997 -0
- package/dist/content/examples/twitch-chat/tsconfig.json +16 -0
- package/dist/content/examples/twitch-chat/view.ocml +141 -0
- package/dist/content/examples/warframe-market/LICENSE +21 -0
- package/dist/content/examples/warframe-market/README.md +176 -0
- package/dist/content/examples/warframe-market/listing.json +19 -0
- package/dist/content/examples/warframe-market/locales/en.json +44 -0
- package/dist/content/examples/warframe-market/locales/fr.json +44 -0
- package/dist/content/examples/warframe-market/logic.ts +1265 -0
- package/dist/content/examples/warframe-market/manifest.json +92 -0
- package/dist/content/examples/warframe-market/package.json +16 -0
- package/dist/content/examples/warframe-market/style.ocss +422 -0
- package/dist/content/examples/warframe-market/tests/bounds.scenario.json +60 -0
- package/dist/content/examples/warframe-market/tests/cached.scenario.json +70 -0
- package/dist/content/examples/warframe-market/tests/detail.scenario.json +251 -0
- package/dist/content/examples/warframe-market/tests/errors.scenario.json +105 -0
- package/dist/content/examples/warframe-market/tests/fixtures.json +295 -0
- package/dist/content/examples/warframe-market/tests/load-logic.mjs +22 -0
- package/dist/content/examples/warframe-market/tests/logic.test.mjs +505 -0
- package/dist/content/examples/warframe-market/tests/no-clipboard.scenario.json +104 -0
- package/dist/content/examples/warframe-market/tests/passive.scenario.json +87 -0
- package/dist/content/examples/warframe-market/tests/preview.scenario.json +146 -0
- package/dist/content/examples/warframe-market/tests/refresh.scenario.json +56 -0
- package/dist/content/examples/warframe-market/tests/refused.scenario.json +74 -0
- package/dist/content/examples/warframe-market/tests/render.scenario.json +210 -0
- package/dist/content/examples/warframe-market/tests/search.scenario.json +189 -0
- package/dist/content/examples/warframe-market/tests/unavailable.scenario.json +54 -0
- package/dist/content/examples/warframe-market/tsconfig.json +16 -0
- package/dist/content/examples/warframe-market/view.ocml +116 -0
- package/dist/content/examples/weather/LICENSE +20 -0
- package/dist/content/examples/weather/listing.json +10 -0
- package/dist/content/examples/weather/locales/en.json +9 -0
- package/dist/content/examples/weather/locales/fr.json +9 -0
- package/dist/content/examples/weather/logic.ts +62 -0
- package/dist/content/examples/weather/manifest.json +51 -0
- package/dist/content/examples/weather/package.json +15 -0
- package/dist/content/examples/weather/style.ocss +29 -0
- package/dist/content/examples/weather/tsconfig.json +16 -0
- package/dist/content/examples/weather/view.ocml +12 -0
- package/dist/content/index.json +3601 -0
- package/dist/content/reference/errors.json +1712 -0
- package/dist/content/reference/limits.json +1154 -0
- package/dist/content/reference/manifest.md +223 -0
- package/dist/content/reference/permissions.json +413 -0
- package/dist/content/templates/blank/locales/en.json +3 -0
- package/dist/content/templates/blank/locales/fr.json +3 -0
- package/dist/content/templates/blank/logic.ts +7 -0
- package/dist/content/templates/blank/manifest.json +16 -0
- package/dist/content/templates/blank/style.ocss +10 -0
- package/dist/content/templates/blank/tests/example.scenario.json +10 -0
- package/dist/content/templates/blank/view.ocml +6 -0
- package/dist/content/templates/chart/locales/en.json +3 -0
- package/dist/content/templates/chart/locales/fr.json +3 -0
- package/dist/content/templates/chart/logic.ts +27 -0
- package/dist/content/templates/chart/manifest.json +16 -0
- package/dist/content/templates/chart/style.ocss +27 -0
- package/dist/content/templates/chart/tests/example.scenario.json +10 -0
- package/dist/content/templates/chart/view.ocml +10 -0
- package/dist/content/templates/counter/locales/en.json +4 -0
- package/dist/content/templates/counter/locales/fr.json +4 -0
- package/dist/content/templates/counter/logic.ts +20 -0
- package/dist/content/templates/counter/manifest.json +16 -0
- package/dist/content/templates/counter/style.ocss +26 -0
- package/dist/content/templates/counter/tests/example.scenario.json +11 -0
- package/dist/content/templates/counter/view.ocml +13 -0
- package/dist/content/templates/list/locales/en.json +8 -0
- package/dist/content/templates/list/locales/fr.json +8 -0
- package/dist/content/templates/list/logic.ts +35 -0
- package/dist/content/templates/list/manifest.json +16 -0
- package/dist/content/templates/list/style.ocss +42 -0
- package/dist/content/templates/list/tests/example.scenario.json +10 -0
- package/dist/content/templates/list/view.ocml +17 -0
- package/dist/content/templates/shared/LICENSE +20 -0
- package/dist/content/templates/shared/package.json +14 -0
- package/dist/content/templates/shared/tsconfig.json +16 -0
- package/dist/content.js +68 -0
- package/dist/env.js +156 -0
- package/dist/fingerprint.js +32 -0
- package/dist/ids.js +27 -0
- package/dist/index.js +82 -0
- package/dist/knowledge.js +222 -0
- package/dist/npm.js +149 -0
- package/dist/prompts.js +47 -0
- package/dist/resources.js +82 -0
- package/dist/run.js +120 -0
- package/dist/server.js +43 -0
- package/dist/session.js +205 -0
- package/dist/sources.js +218 -0
- package/dist/text.js +80 -0
- package/dist/texts.js +44 -0
- package/dist/tools/audit.js +105 -0
- package/dist/tools/build.js +453 -0
- package/dist/tools/common.js +122 -0
- package/dist/tools/docs.js +196 -0
- package/dist/tools/preview.js +87 -0
- package/dist/tools/project.js +178 -0
- package/dist/tools/setup.js +146 -0
- package/dist/tools/submission.js +369 -0
- package/dist/version.js +3 -0
- package/npm-shrinkwrap.json +402 -0
- package/package.json +50 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Valhallab SASU
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1 +1,91 @@
|
|
|
1
|
-
|
|
1
|
+
# @overcrow/mcp
|
|
2
|
+
|
|
3
|
+
An MCP server for creating [PlayerVox OverCrow](https://overcrow.playervox.com/)
|
|
4
|
+
widgets with an AI assistant. Add it to Claude Code, Claude Desktop, Cursor,
|
|
5
|
+
VS Code or Codex, and the assistant can create a widget, check it, test it,
|
|
6
|
+
audit its security and lightness, package it and prepare its submission.
|
|
7
|
+
|
|
8
|
+
You need Node.js 22.18 or later, nothing else: the server installs the
|
|
9
|
+
OverCrow creator tools it needs by itself.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
claude mcp add overcrow -- npx -y @overcrow/mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Guide, other clients and offline use:
|
|
16
|
+
[overcrow.playervox.com/docs/en/mcp/](https://overcrow.playervox.com/docs/en/mcp/)
|
|
17
|
+
([français](https://overcrow.playervox.com/docs/mcp/)).
|
|
18
|
+
|
|
19
|
+
## Clients
|
|
20
|
+
|
|
21
|
+
| Client | Setup |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| Claude Code | `claude mcp add overcrow -- npx -y @overcrow/mcp` |
|
|
24
|
+
| Codex | `codex mcp add overcrow -- npx -y @overcrow/mcp` |
|
|
25
|
+
| Cursor | [`.cursor/mcp.json`](https://github.com/Valhallab/playervox-overcrow-releases/blob/main/mcp/examples/cursor-mcp.json) |
|
|
26
|
+
| VS Code | [`.vscode/mcp.json`](https://github.com/Valhallab/playervox-overcrow-releases/blob/main/mcp/examples/vscode-mcp.json), or `code --add-mcp '{"name":"overcrow","command":"npx","args":["-y","@overcrow/mcp"]}'` |
|
|
27
|
+
| Claude Desktop | [`claude_desktop_config.json`](https://github.com/Valhallab/playervox-overcrow-releases/blob/main/mcp/examples/claude_desktop_config.json), with `--root` and your widgets folder |
|
|
28
|
+
|
|
29
|
+
Options: `--root <folder>` (repeatable) names the folders the server may
|
|
30
|
+
use; `--tools-zip <file>` gives a local copy of the creator tools ZIP.
|
|
31
|
+
|
|
32
|
+
## Tools
|
|
33
|
+
|
|
34
|
+
| Tool | What it does |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `status` | What is ready, and what to do next |
|
|
37
|
+
| `setup` | Downloads and checks the creator tools, once (the only download) |
|
|
38
|
+
| `list_templates`, `create_widget` | Start a widget from a template, in a new folder |
|
|
39
|
+
| `install_sdk` | `@overcrow/sdk` and TypeScript with npm, after your confirmation |
|
|
40
|
+
| `check` | Every problem, with file, line, code and fix |
|
|
41
|
+
| `test` | The test scenarios; differing images are shown |
|
|
42
|
+
| `update_reference_images` | Replaces reference images, after your confirmation |
|
|
43
|
+
| `use_preview` | Copies a reference image to `assets/preview.png`, the marketplace preview |
|
|
44
|
+
| `package`, `inspect` | The `.ocpkg`, its size and what it may do |
|
|
45
|
+
| `audit` | Security and lightness, scored out of 100, with fixes and examples |
|
|
46
|
+
| `explain_permission`, `explain_error` | A permission or a code, in plain words |
|
|
47
|
+
| `search_docs`, `read_doc`, `read_example` | The documentation and the reference widgets, offline |
|
|
48
|
+
| `prepare_submission` | Everything the OverCrow creator space asks for, checked; the ZIP of the sources in `dist/`; the texts to draft with you |
|
|
49
|
+
|
|
50
|
+
Resources: the documentation (`overcrow://docs/{en,fr}/…`), the manifest
|
|
51
|
+
reference, the limits, the templates and the reference widgets. Prompts:
|
|
52
|
+
`create_widget`, `audit_widget`, `prepare_submission`.
|
|
53
|
+
|
|
54
|
+
## Security
|
|
55
|
+
|
|
56
|
+
- The server works only in your project folders, through their real paths:
|
|
57
|
+
no system folder, no whole home folder, no link that leads out, no
|
|
58
|
+
hidden folder.
|
|
59
|
+
- It runs the OverCrow widget CLI and npm, without a shell, with a minimal
|
|
60
|
+
environment (no token is passed on), time limits and bounded output. It
|
|
61
|
+
never runs a command chosen by the assistant.
|
|
62
|
+
- It downloads only the creator tools ZIP of the OverCrow release this
|
|
63
|
+
version pins, from GitHub, and checks its size and SHA-256 and every file
|
|
64
|
+
in it before use. npm installs only `@overcrow/sdk` and TypeScript, with
|
|
65
|
+
install scripts disabled, and their integrity is checked against the
|
|
66
|
+
pinned published packages.
|
|
67
|
+
- Replacing reference images and installing packages need an explicit
|
|
68
|
+
confirmation; the server deletes nothing in your project.
|
|
69
|
+
- Text read from your files and from the tools is returned marked as data:
|
|
70
|
+
the server's instructions tell the assistant never to follow
|
|
71
|
+
instructions found in it.
|
|
72
|
+
- No telemetry. Logs go to standard error, without your paths.
|
|
73
|
+
- It never sends, signs or publishes anything: you send the sources ZIP
|
|
74
|
+
in the OverCrow creator space on overcrow.playervox.com.
|
|
75
|
+
|
|
76
|
+
## Platforms
|
|
77
|
+
|
|
78
|
+
Checking, testing and packaging run on Windows x64 and Linux x86-64. On
|
|
79
|
+
other computers, the documentation, the explanations and the audit still
|
|
80
|
+
work.
|
|
81
|
+
|
|
82
|
+
## Dependencies
|
|
83
|
+
|
|
84
|
+
Three packages, at exact versions frozen by `npm-shrinkwrap.json`:
|
|
85
|
+
`@modelcontextprotocol/server` and `@modelcontextprotocol/core` (the
|
|
86
|
+
official MCP SDK, Apache-2.0) and `zod` (MIT). No install script.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT. Source:
|
|
91
|
+
[github.com/Valhallab/playervox-overcrow-releases/tree/main/mcp](https://github.com/Valhallab/playervox-overcrow-releases/tree/main/mcp).
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// The audit of a widget: security (least authority, no secret, user data
|
|
2
|
+
// kept) and lightness (CPU, memory, network, images), each scored out of
|
|
3
|
+
// 100, with findings ranked by severity, each with a fix and an example
|
|
4
|
+
// from a reference widget. Static: nothing of the widget runs.
|
|
5
|
+
import { permissionsTable, projectFile } from "../content.js";
|
|
6
|
+
import { readWidget } from "./project.js";
|
|
7
|
+
import { runRules } from "./rules.js";
|
|
8
|
+
const PENALTY = { high: 25, medium: 10, low: 3 };
|
|
9
|
+
const ORDER = { high: 0, medium: 1, low: 2 };
|
|
10
|
+
/** Finds an example's line in the embedded reference widgets. */
|
|
11
|
+
export function resolveExample(example) {
|
|
12
|
+
const text = projectFile("examples", example.widget, example.file);
|
|
13
|
+
if (text === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
const lines = text.split("\n");
|
|
16
|
+
const index = lines.findIndex((line) => line.includes(example.find));
|
|
17
|
+
if (index < 0)
|
|
18
|
+
return undefined;
|
|
19
|
+
return {
|
|
20
|
+
widget: example.widget,
|
|
21
|
+
file: example.file,
|
|
22
|
+
line: index + 1,
|
|
23
|
+
excerpt: lines.slice(index, index + 6).join("\n"),
|
|
24
|
+
resource: `overcrow://examples/${example.widget}/${example.file}`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function score(findings, area) {
|
|
28
|
+
return Math.max(0, 100 - findings.filter((f) => f.area === area).reduce((sum, f) => sum + PENALTY[f.severity], 0));
|
|
29
|
+
}
|
|
30
|
+
function verdict(value) {
|
|
31
|
+
if (value >= 90)
|
|
32
|
+
return "good";
|
|
33
|
+
if (value >= 70)
|
|
34
|
+
return "fair";
|
|
35
|
+
return "needs work";
|
|
36
|
+
}
|
|
37
|
+
/** What the manifest lets the widget do, in plain words. */
|
|
38
|
+
export function authoritySummary(manifest) {
|
|
39
|
+
const permissions = manifest?.permissions ?? {};
|
|
40
|
+
const table = permissionsTable();
|
|
41
|
+
const out = [];
|
|
42
|
+
const network = Array.isArray(permissions.network)
|
|
43
|
+
? permissions.network
|
|
44
|
+
: [];
|
|
45
|
+
for (const rule of network) {
|
|
46
|
+
const bound = typeof rule.maxResponseBytes === "number"
|
|
47
|
+
? `${Math.ceil(rule.maxResponseBytes / 1024)} KiB`
|
|
48
|
+
: "1 MiB (default)";
|
|
49
|
+
out.push(`Network: ${String(rule.method ?? "GET")} ${String(rule.origin ?? "")}${String(rule.path ?? "")}, responses up to ${bound}.`);
|
|
50
|
+
}
|
|
51
|
+
if (permissions.storage === true)
|
|
52
|
+
out.push(`Storage: ${table.permissions.storage?.en.meaning ?? "host-managed storage"}`);
|
|
53
|
+
if (permissions.clipboardWrite === true)
|
|
54
|
+
out.push(`Clipboard: ${table.permissions.clipboardWrite?.en.meaning ?? "text writes"}`);
|
|
55
|
+
const events = Array.isArray(permissions.gameEvents) ? permissions.gameEvents : [];
|
|
56
|
+
if (events.length > 0)
|
|
57
|
+
out.push(`Game events: ${events.join(", ")}.`);
|
|
58
|
+
const capabilities = Array.isArray(permissions.capabilities)
|
|
59
|
+
? permissions.capabilities
|
|
60
|
+
: [];
|
|
61
|
+
for (const capability of capabilities) {
|
|
62
|
+
const info = table.capabilities[capability];
|
|
63
|
+
const flags = [
|
|
64
|
+
info?.sensitive ? "sensitive" : null,
|
|
65
|
+
info?.account ? `needs a ${info.account} account` : null,
|
|
66
|
+
].filter(Boolean);
|
|
67
|
+
out.push(`${capability}${flags.length ? ` (${flags.join(", ")})` : ""}: ${info?.en.meaning ?? "unknown capability"}`);
|
|
68
|
+
}
|
|
69
|
+
if (out.length === 0)
|
|
70
|
+
out.push("Nothing: the widget asks for no permission and no capability.");
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** Audits the widget in `directory` (already confined). */
|
|
74
|
+
export async function auditWidget(directory, extra = {}) {
|
|
75
|
+
const project = await readWidget(directory);
|
|
76
|
+
const findings = [...runRules(project), ...(extra.findings ?? [])].sort((a, b) => ORDER[a.severity] - ORDER[b.severity] || a.rule.localeCompare(b.rule));
|
|
77
|
+
const notes = [...(extra.notes ?? [])];
|
|
78
|
+
if (project.manifestError)
|
|
79
|
+
notes.push(`manifest.json could not be read (${project.manifestError}): the permission rules did not run.`);
|
|
80
|
+
if (!project.logic)
|
|
81
|
+
notes.push("No logic.ts or logic.js: the logic rules did not run.");
|
|
82
|
+
if (project.truncated)
|
|
83
|
+
notes.push("The folder is very large: the audit read only part of it.");
|
|
84
|
+
const security = score(findings, "security");
|
|
85
|
+
const lightness = score(findings, "lightness");
|
|
86
|
+
return {
|
|
87
|
+
id: typeof project.manifest?.id === "string" ? project.manifest.id : null,
|
|
88
|
+
version: typeof project.manifest?.version === "string" ? project.manifest.version : null,
|
|
89
|
+
scores: { security, lightness },
|
|
90
|
+
verdicts: { security: verdict(security), lightness: verdict(lightness) },
|
|
91
|
+
counts: {
|
|
92
|
+
high: findings.filter((f) => f.severity === "high").length,
|
|
93
|
+
medium: findings.filter((f) => f.severity === "medium").length,
|
|
94
|
+
low: findings.filter((f) => f.severity === "low").length,
|
|
95
|
+
},
|
|
96
|
+
findings: findings.map(({ example, ...finding }) => {
|
|
97
|
+
const resolved = example ? resolveExample(example) : undefined;
|
|
98
|
+
return resolved ? { ...finding, example: resolved } : finding;
|
|
99
|
+
}),
|
|
100
|
+
authority: authoritySummary(project.manifest),
|
|
101
|
+
notes,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
// A small tokenizer of TypeScript and JavaScript for the static audit: it
|
|
2
|
+
// separates code from comments, strings and template literals so that the
|
|
3
|
+
// rules look at what the logic does, not at what its comments say. It never
|
|
4
|
+
// evaluates anything. Precision is that of a lexer: the rules that use it
|
|
5
|
+
// are worded as findings to check, not as proofs.
|
|
6
|
+
const IDENTIFIER_START = /[A-Za-z_$À-]/;
|
|
7
|
+
const IDENTIFIER_PART = /[A-Za-z0-9_$À-]/;
|
|
8
|
+
const PUNCTUATION = [
|
|
9
|
+
"...",
|
|
10
|
+
"===",
|
|
11
|
+
"!==",
|
|
12
|
+
"**=",
|
|
13
|
+
"<<=",
|
|
14
|
+
">>=",
|
|
15
|
+
">>>",
|
|
16
|
+
"=>",
|
|
17
|
+
"==",
|
|
18
|
+
"!=",
|
|
19
|
+
"<=",
|
|
20
|
+
">=",
|
|
21
|
+
"&&",
|
|
22
|
+
"||",
|
|
23
|
+
"??",
|
|
24
|
+
"?.",
|
|
25
|
+
"++",
|
|
26
|
+
"--",
|
|
27
|
+
"+=",
|
|
28
|
+
"-=",
|
|
29
|
+
"*=",
|
|
30
|
+
"/=",
|
|
31
|
+
"%=",
|
|
32
|
+
"&=",
|
|
33
|
+
"|=",
|
|
34
|
+
"^=",
|
|
35
|
+
"**",
|
|
36
|
+
"<<",
|
|
37
|
+
">>",
|
|
38
|
+
];
|
|
39
|
+
/** After these, a `/` starts a regular expression rather than a division. */
|
|
40
|
+
const REGEX_AFTER_KEYWORDS = new Set([
|
|
41
|
+
"return",
|
|
42
|
+
"typeof",
|
|
43
|
+
"instanceof",
|
|
44
|
+
"in",
|
|
45
|
+
"of",
|
|
46
|
+
"new",
|
|
47
|
+
"delete",
|
|
48
|
+
"void",
|
|
49
|
+
"throw",
|
|
50
|
+
"case",
|
|
51
|
+
"do",
|
|
52
|
+
"else",
|
|
53
|
+
"yield",
|
|
54
|
+
"await",
|
|
55
|
+
]);
|
|
56
|
+
export function tokenize(source) {
|
|
57
|
+
const tokens = [];
|
|
58
|
+
let i = 0;
|
|
59
|
+
let line = 1;
|
|
60
|
+
/** Brace depth at which each open template literal resumes. */
|
|
61
|
+
const templateStack = [];
|
|
62
|
+
let braceDepth = 0;
|
|
63
|
+
const regexAllowed = () => {
|
|
64
|
+
const previous = tokens.at(-1);
|
|
65
|
+
if (!previous)
|
|
66
|
+
return true;
|
|
67
|
+
if (previous.kind === "identifier")
|
|
68
|
+
return REGEX_AFTER_KEYWORDS.has(previous.value);
|
|
69
|
+
if (previous.kind === "number" || previous.kind === "string" || previous.kind === "regex")
|
|
70
|
+
return false;
|
|
71
|
+
if (previous.kind === "template")
|
|
72
|
+
return !previous.end;
|
|
73
|
+
return ![")", "]", "}"].includes(previous.value);
|
|
74
|
+
};
|
|
75
|
+
const readTemplateChunk = () => {
|
|
76
|
+
// `i` is just after the opening backtick or the closing brace of `${`.
|
|
77
|
+
const startLine = line;
|
|
78
|
+
let value = "";
|
|
79
|
+
while (i < source.length) {
|
|
80
|
+
const char = source[i];
|
|
81
|
+
if (char === "\\") {
|
|
82
|
+
value += source.slice(i, i + 2);
|
|
83
|
+
if (source[i + 1] === "\n")
|
|
84
|
+
line += 1;
|
|
85
|
+
i += 2;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (char === "`") {
|
|
89
|
+
i += 1;
|
|
90
|
+
tokens.push({ kind: "template", value, line: startLine, end: true });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (char === "$" && source[i + 1] === "{") {
|
|
94
|
+
i += 2;
|
|
95
|
+
tokens.push({ kind: "template", value, line: startLine, end: false });
|
|
96
|
+
templateStack.push(braceDepth);
|
|
97
|
+
braceDepth += 1;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (char === "\n")
|
|
101
|
+
line += 1;
|
|
102
|
+
value += char;
|
|
103
|
+
i += 1;
|
|
104
|
+
}
|
|
105
|
+
tokens.push({ kind: "template", value, line: startLine, end: true });
|
|
106
|
+
};
|
|
107
|
+
while (i < source.length) {
|
|
108
|
+
const char = source[i];
|
|
109
|
+
const next = source[i + 1];
|
|
110
|
+
if (char === "\n") {
|
|
111
|
+
line += 1;
|
|
112
|
+
i += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (/\s/.test(char)) {
|
|
116
|
+
i += 1;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (char === "/" && next === "/") {
|
|
120
|
+
while (i < source.length && source[i] !== "\n")
|
|
121
|
+
i += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (char === "/" && next === "*") {
|
|
125
|
+
const end = source.indexOf("*/", i + 2);
|
|
126
|
+
const stop = end < 0 ? source.length : end + 2;
|
|
127
|
+
for (let j = i; j < stop; j += 1)
|
|
128
|
+
if (source[j] === "\n")
|
|
129
|
+
line += 1;
|
|
130
|
+
i = stop;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (char === '"' || char === "'") {
|
|
134
|
+
const startLine = line;
|
|
135
|
+
let value = "";
|
|
136
|
+
i += 1;
|
|
137
|
+
while (i < source.length && source[i] !== char && source[i] !== "\n") {
|
|
138
|
+
if (source[i] === "\\") {
|
|
139
|
+
value += source.slice(i, i + 2);
|
|
140
|
+
i += 2;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
value += source[i];
|
|
144
|
+
i += 1;
|
|
145
|
+
}
|
|
146
|
+
i += 1;
|
|
147
|
+
tokens.push({ kind: "string", value, line: startLine });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (char === "`") {
|
|
151
|
+
i += 1;
|
|
152
|
+
readTemplateChunk();
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (char === "}" && templateStack.length > 0 && templateStack.at(-1) === braceDepth - 1) {
|
|
156
|
+
templateStack.pop();
|
|
157
|
+
braceDepth -= 1;
|
|
158
|
+
i += 1;
|
|
159
|
+
readTemplateChunk();
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (char === "/" && regexAllowed()) {
|
|
163
|
+
const startLine = line;
|
|
164
|
+
let value = "";
|
|
165
|
+
let inClass = false;
|
|
166
|
+
i += 1;
|
|
167
|
+
while (i < source.length && source[i] !== "\n") {
|
|
168
|
+
const c = source[i];
|
|
169
|
+
if (c === "\\") {
|
|
170
|
+
value += source.slice(i, i + 2);
|
|
171
|
+
i += 2;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (c === "[")
|
|
175
|
+
inClass = true;
|
|
176
|
+
else if (c === "]")
|
|
177
|
+
inClass = false;
|
|
178
|
+
else if (c === "/" && !inClass)
|
|
179
|
+
break;
|
|
180
|
+
value += c;
|
|
181
|
+
i += 1;
|
|
182
|
+
}
|
|
183
|
+
i += 1;
|
|
184
|
+
while (i < source.length && /[a-z]/.test(source[i]))
|
|
185
|
+
i += 1;
|
|
186
|
+
tokens.push({ kind: "regex", value, line: startLine });
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (/[0-9]/.test(char) || (char === "." && /[0-9]/.test(next ?? ""))) {
|
|
190
|
+
let end = i + 1;
|
|
191
|
+
while (end < source.length && /[0-9A-Za-z_.]/.test(source[end]))
|
|
192
|
+
end += 1;
|
|
193
|
+
tokens.push({ kind: "number", value: source.slice(i, end), line });
|
|
194
|
+
i = end;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (IDENTIFIER_START.test(char)) {
|
|
198
|
+
let end = i + 1;
|
|
199
|
+
while (end < source.length && IDENTIFIER_PART.test(source[end]))
|
|
200
|
+
end += 1;
|
|
201
|
+
tokens.push({ kind: "identifier", value: source.slice(i, end), line });
|
|
202
|
+
i = end;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const operator = PUNCTUATION.find((candidate) => source.startsWith(candidate, i));
|
|
206
|
+
const value = operator ?? char;
|
|
207
|
+
if (value === "{")
|
|
208
|
+
braceDepth += 1;
|
|
209
|
+
if (value === "}")
|
|
210
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
211
|
+
tokens.push({ kind: "punctuation", value, line });
|
|
212
|
+
i += value.length;
|
|
213
|
+
}
|
|
214
|
+
return tokens;
|
|
215
|
+
}
|
|
216
|
+
/** The numeric value of a literal (`30_000`, `0x10`, `1e3`), or undefined. */
|
|
217
|
+
export function numberValue(text) {
|
|
218
|
+
const value = Number(text.replaceAll("_", ""));
|
|
219
|
+
return Number.isFinite(value) ? value : undefined;
|
|
220
|
+
}
|
|
221
|
+
/** Index of the token that closes the bracket opened at `open`, or -1. */
|
|
222
|
+
export function matching(tokens, open) {
|
|
223
|
+
const pairs = { "(": ")", "[": "]", "{": "}" };
|
|
224
|
+
const opener = tokens[open]?.value ?? "";
|
|
225
|
+
const closer = pairs[opener];
|
|
226
|
+
if (!closer)
|
|
227
|
+
return -1;
|
|
228
|
+
let depth = 0;
|
|
229
|
+
for (let index = open; index < tokens.length; index += 1) {
|
|
230
|
+
const token = tokens[index];
|
|
231
|
+
if (token.kind !== "punctuation")
|
|
232
|
+
continue;
|
|
233
|
+
if (token.value === opener)
|
|
234
|
+
depth += 1;
|
|
235
|
+
else if (token.value === closer) {
|
|
236
|
+
depth -= 1;
|
|
237
|
+
if (depth === 0)
|
|
238
|
+
return index;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return -1;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Indexes of calls of a dotted name: `timers.every(` for
|
|
245
|
+
* `["timers", "every"]`. Returns the index of the opening parenthesis.
|
|
246
|
+
*/
|
|
247
|
+
export function calls(tokens, path) {
|
|
248
|
+
const found = [];
|
|
249
|
+
outer: for (let index = 0; index + path.length * 2 - 1 < tokens.length; index += 1) {
|
|
250
|
+
for (let part = 0; part < path.length; part += 1) {
|
|
251
|
+
const token = tokens[index + part * 2];
|
|
252
|
+
if (token?.kind !== "identifier" || token.value !== path[part])
|
|
253
|
+
continue outer;
|
|
254
|
+
if (part < path.length - 1) {
|
|
255
|
+
const dot = tokens[index + part * 2 + 1];
|
|
256
|
+
if (dot?.value !== "." && dot?.value !== "?.")
|
|
257
|
+
continue outer;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Not the tail of a longer chain (`x.timers.every`).
|
|
261
|
+
const before = tokens[index - 1];
|
|
262
|
+
if (before?.value === "." || before?.value === "?.")
|
|
263
|
+
continue;
|
|
264
|
+
const open = tokens[index + path.length * 2 - 1];
|
|
265
|
+
if (open?.value === "(")
|
|
266
|
+
found.push(index + path.length * 2 - 1);
|
|
267
|
+
}
|
|
268
|
+
return found;
|
|
269
|
+
}
|
|
270
|
+
/** Splits the arguments of the call whose `(` is at `open` into token ranges. */
|
|
271
|
+
export function callArguments(tokens, open) {
|
|
272
|
+
const close = matching(tokens, open);
|
|
273
|
+
if (close < 0)
|
|
274
|
+
return [];
|
|
275
|
+
const args = [];
|
|
276
|
+
let current = [];
|
|
277
|
+
let depth = 0;
|
|
278
|
+
for (let index = open + 1; index < close; index += 1) {
|
|
279
|
+
const token = tokens[index];
|
|
280
|
+
if (token.kind === "punctuation" && ["(", "[", "{"].includes(token.value))
|
|
281
|
+
depth += 1;
|
|
282
|
+
if (token.kind === "punctuation" && [")", "]", "}"].includes(token.value))
|
|
283
|
+
depth -= 1;
|
|
284
|
+
if (depth === 0 && token.kind === "punctuation" && token.value === ",") {
|
|
285
|
+
args.push(current);
|
|
286
|
+
current = [];
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
current.push(token);
|
|
290
|
+
}
|
|
291
|
+
if (current.length > 0)
|
|
292
|
+
args.push(current);
|
|
293
|
+
return args;
|
|
294
|
+
}
|
|
295
|
+
/** `const NAME = <number expression>` declarations of a file, evaluated when only literals are multiplied or added. */
|
|
296
|
+
export function numericConstants(tokens) {
|
|
297
|
+
const constants = new Map();
|
|
298
|
+
for (let index = 0; index + 3 < tokens.length; index += 1) {
|
|
299
|
+
if (tokens[index]?.value !== "const" || tokens[index + 1]?.kind !== "identifier")
|
|
300
|
+
continue;
|
|
301
|
+
let cursor = index + 2;
|
|
302
|
+
// An optional type annotation: `const X: number = …`.
|
|
303
|
+
if (tokens[cursor]?.value === ":")
|
|
304
|
+
cursor += 2;
|
|
305
|
+
if (tokens[cursor]?.value !== "=")
|
|
306
|
+
continue;
|
|
307
|
+
const expression = [];
|
|
308
|
+
for (let j = cursor + 1; j < tokens.length; j += 1) {
|
|
309
|
+
const token = tokens[j];
|
|
310
|
+
if (token.value === ";" || token.line !== tokens[cursor].line)
|
|
311
|
+
break;
|
|
312
|
+
expression.push(token);
|
|
313
|
+
}
|
|
314
|
+
const value = evaluate(expression, constants);
|
|
315
|
+
if (value !== undefined)
|
|
316
|
+
constants.set(tokens[index + 1].value, value);
|
|
317
|
+
}
|
|
318
|
+
return constants;
|
|
319
|
+
}
|
|
320
|
+
/** Evaluates `a * b + c` made of number literals and known constants. */
|
|
321
|
+
export function evaluate(expression, constants) {
|
|
322
|
+
if (expression.length === 0 || expression.length > 15)
|
|
323
|
+
return undefined;
|
|
324
|
+
let total = 0;
|
|
325
|
+
let product = 1;
|
|
326
|
+
let expectOperand = true;
|
|
327
|
+
for (const token of expression) {
|
|
328
|
+
if (expectOperand) {
|
|
329
|
+
const value = token.kind === "number"
|
|
330
|
+
? numberValue(token.value)
|
|
331
|
+
: token.kind === "identifier"
|
|
332
|
+
? constants.get(token.value)
|
|
333
|
+
: undefined;
|
|
334
|
+
if (value === undefined)
|
|
335
|
+
return undefined;
|
|
336
|
+
product *= value;
|
|
337
|
+
expectOperand = false;
|
|
338
|
+
}
|
|
339
|
+
else if (token.value === "*") {
|
|
340
|
+
expectOperand = true;
|
|
341
|
+
}
|
|
342
|
+
else if (token.value === "+") {
|
|
343
|
+
total += product;
|
|
344
|
+
product = 1;
|
|
345
|
+
expectOperand = true;
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return expectOperand ? undefined : total + product;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Named functions of a file: `function f(`, `export function f(`,
|
|
355
|
+
* `const f = (…) =>`, `const f = function`, and methods `f(…) {`.
|
|
356
|
+
*/
|
|
357
|
+
export function functions(tokens) {
|
|
358
|
+
const out = [];
|
|
359
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
360
|
+
const token = tokens[index];
|
|
361
|
+
let name;
|
|
362
|
+
let search = index;
|
|
363
|
+
if (token.value === "function" && tokens[index + 1]?.kind === "identifier") {
|
|
364
|
+
name = tokens[index + 1].value;
|
|
365
|
+
search = index + 2;
|
|
366
|
+
}
|
|
367
|
+
else if ((token.value === "const" || token.value === "let") &&
|
|
368
|
+
tokens[index + 1]?.kind === "identifier" &&
|
|
369
|
+
tokens[index + 2]?.value === "=") {
|
|
370
|
+
const after = tokens[index + 3];
|
|
371
|
+
if (after?.value === "(" ||
|
|
372
|
+
after?.value === "async" ||
|
|
373
|
+
after?.value === "function" ||
|
|
374
|
+
after?.kind === "identifier") {
|
|
375
|
+
name = tokens[index + 1].value;
|
|
376
|
+
search = index + 3;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (!name)
|
|
380
|
+
continue;
|
|
381
|
+
// The body is the first `{` after the parameters (and `=>` for arrows).
|
|
382
|
+
let open = -1;
|
|
383
|
+
for (let j = search; j < Math.min(tokens.length, search + 400); j += 1) {
|
|
384
|
+
const candidate = tokens[j];
|
|
385
|
+
if (candidate.value === "(") {
|
|
386
|
+
const close = matching(tokens, j);
|
|
387
|
+
if (close < 0)
|
|
388
|
+
break;
|
|
389
|
+
j = close;
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (candidate.value === ";")
|
|
393
|
+
break;
|
|
394
|
+
if (candidate.value === "{") {
|
|
395
|
+
open = j;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (open < 0)
|
|
400
|
+
continue;
|
|
401
|
+
const end = matching(tokens, open);
|
|
402
|
+
if (end < 0)
|
|
403
|
+
continue;
|
|
404
|
+
const exported = tokens[index - 1]?.value === "export" || tokens[index - 2]?.value === "export";
|
|
405
|
+
out.push({ name, exported, start: open, end });
|
|
406
|
+
}
|
|
407
|
+
return out;
|
|
408
|
+
}
|
|
409
|
+
/** The innermost named function whose body holds token `index`. */
|
|
410
|
+
export function enclosing(ranges, index) {
|
|
411
|
+
let best;
|
|
412
|
+
for (const range of ranges) {
|
|
413
|
+
if (range.start < index && index < range.end && (!best || range.start > best.start))
|
|
414
|
+
best = range;
|
|
415
|
+
}
|
|
416
|
+
return best;
|
|
417
|
+
}
|