@readymadeit/mcp 0.0.0-stage → 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/README.md +114 -2
- package/bundle/LICENSE +201 -0
- package/bundle/boards/aggregator-demo-board.json +54 -0
- package/bundle/boards/alpaca-board.json +316 -0
- package/bundle/boards/animate-board.json +159 -0
- package/bundle/boards/ascii-art-demo-board.json +33 -0
- package/bundle/boards/ascii-cam-board.json +184 -0
- package/bundle/boards/asset-demo-board.json +54 -0
- package/bundle/boards/audio-encode-demo-board.json +153 -0
- package/bundle/boards/audiolize-board.json +152 -0
- package/bundle/boards/batcher-demo-board.json +43 -0
- package/bundle/boards/buffer-demo-board.json +53 -0
- package/bundle/boards/cache-demo-board.json +59 -0
- package/bundle/boards/camera-demo-board.json +28 -0
- package/bundle/boards/canvas-demo-board.json +55 -0
- package/bundle/boards/changes-demo-board.json +60 -0
- package/bundle/boards/court-booking-browser-demo-board.json +221 -0
- package/bundle/boards/court-booking-demo-board.json +218 -0
- package/bundle/boards/current-time-board.json +32 -0
- package/bundle/boards/debounce-demo-board.json +43 -0
- package/bundle/boards/decrypt-demo-board.json +47 -0
- package/bundle/boards/delay-demo-board.json +28 -0
- package/bundle/boards/detect-demo-board.json +64 -0
- package/bundle/boards/document-extract-demo-board.json +55 -0
- package/bundle/boards/dropitapp-board.json +138 -0
- package/bundle/boards/encode-demo-board.json +39 -0
- package/bundle/boards/encrypt-board.json +185 -0
- package/bundle/boards/encrypt-demo-board.json +39 -0
- package/bundle/boards/face-alert-board.json +181 -0
- package/bundle/boards/fetcher-demo-board.json +40 -0
- package/bundle/boards/fft-demo-board.json +28 -0
- package/bundle/boards/filter-demo-board.json +55 -0
- package/bundle/boards/flat-map-demo-board.json +42 -0
- package/bundle/boards/frame-limit-test-board.json +130 -0
- package/bundle/boards/game-board.json +413 -0
- package/bundle/boards/game-of-life-board.json +139 -0
- package/bundle/boards/game-of-life-demo-board.json +34 -0
- package/bundle/boards/gather-demo-board.json +62 -0
- package/bundle/boards/group-by-demo-board.json +62 -0
- package/bundle/boards/hacker-demo-board.json +45 -0
- package/bundle/boards/hacker-news-board.json +741 -0
- package/bundle/boards/hash-demo-board.json +40 -0
- package/bundle/boards/health-log-browser-demo-board.json +537 -0
- package/bundle/boards/health-log-demo-board.json +498 -0
- package/bundle/boards/helloworld-board.json +57 -0
- package/bundle/boards/hold-demo-board.json +67 -0
- package/bundle/boards/hold-scopes-demo-board.json +141 -0
- package/bundle/boards/http-client-browser-demo-board.json +379 -0
- package/bundle/boards/http-client-demo-board.json +380 -0
- package/bundle/boards/image-picker-demo-board.json +22 -0
- package/bundle/boards/imap-email-demo-board.json +35 -0
- package/bundle/boards/injector-demo-board.json +55 -0
- package/bundle/boards/iterator-demo-board.json +55 -0
- package/bundle/boards/limit-demo-board.json +66 -0
- package/bundle/boards/link-debugger.json +32 -0
- package/bundle/boards/live-location-demo-board.json +93 -0
- package/bundle/boards/live-radio-cloud-demo-board.json +216 -0
- package/bundle/boards/live-radio-cloud-direct-demo-board.json +311 -0
- package/bundle/boards/live-radio-demo-board.json +211 -0
- package/bundle/boards/live-radio-members-demo-board.json +314 -0
- package/bundle/boards/local-storage-demo-board.json +30 -0
- package/bundle/boards/lz-compress-demo-board.json +52 -0
- package/bundle/boards/map-demo-board.json +46 -0
- package/bundle/boards/match-filter-demo-board.json +43 -0
- package/bundle/boards/meeting-poll-browser-demo-board.json +649 -0
- package/bundle/boards/meeting-poll-demo-board.json +650 -0
- package/bundle/boards/microphone-monitor-demo-board.json +28 -0
- package/bundle/boards/microphone-speaker-demo-board.json +86 -0
- package/bundle/boards/monitor-demo-board.json +52 -0
- package/bundle/boards/mounted-endpoint-demo-board-hkp-rt.json +84 -0
- package/bundle/boards/mounted-endpoint-demo-board.json +84 -0
- package/bundle/boards/nested-rhythm-demo-board.json +1160 -0
- package/bundle/boards/next-bus-board.json +52 -0
- package/bundle/boards/next-bus-switch-board.json +79 -0
- package/bundle/boards/night-groove-demo-board.json +742 -0
- package/bundle/boards/noise-alert-board.json +146 -0
- package/bundle/boards/offer-followup-demo-board.json +291 -0
- package/bundle/boards/offer-intake-demo-board.json +289 -0
- package/bundle/boards/offer-intake-hetzner-demo-board.json +286 -0
- package/bundle/boards/offer-intake-imap-demo-board.json +289 -0
- package/bundle/boards/offer-intake-imap-hetzner-demo-board.json +354 -0
- package/bundle/boards/p2p-receiver-demo-board.json +65 -0
- package/bundle/boards/p2p-sender-demo-board.json +94 -0
- package/bundle/boards/pad-groove-demo-board.json +644 -0
- package/bundle/boards/peer-chat-board.json +109 -0
- package/bundle/boards/peer-chat-node-board.json +97 -0
- package/bundle/boards/qr-code-demo-board.json +22 -0
- package/bundle/boards/queue-demo-board.json +239 -0
- package/bundle/boards/reduce-demo-board.json +54 -0
- package/bundle/boards/remote-named-demo-board-hkp-rt.json +61 -0
- package/bundle/boards/remote-named-demo-board.json +61 -0
- package/bundle/boards/rss-demo-board.json +530 -0
- package/bundle/boards/rss-radio-board.json +515 -0
- package/bundle/boards/rss-radio-brief-one-board.json +237 -0
- package/bundle/boards/rss-radio-editor-unit-board.json +555 -0
- package/bundle/boards/rss-radio-library-unit-board.json +293 -0
- package/bundle/boards/rss-radio-unit-board.json +809 -0
- package/bundle/boards/rss-radio-voice-unit-board.json +146 -0
- package/bundle/boards/runtime-configs/hold-example.json +41 -0
- package/bundle/boards/runtime-configs/injector-example.json +23 -0
- package/bundle/boards/runtime-configs/speech-to-text-example.json +35 -0
- package/bundle/boards/runtime-configs/text-generation-example.json +31 -0
- package/bundle/boards/runtime-configs/text-to-speech-example.json +28 -0
- package/bundle/boards/runtime-configs/text-to-speech-inflect-example.json +28 -0
- package/bundle/boards/runtime-configs/ws-audio-playback-example.json +23 -0
- package/bundle/boards/select-demo-board.json +32 -0
- package/bundle/boards/send-ntfy-board.json +52 -0
- package/bundle/boards/shared-costs-demo-board.json +361 -0
- package/bundle/boards/sign-demo-board.json +41 -0
- package/bundle/boards/skill-router-demo-board.json +210 -0
- package/bundle/boards/smooth-demo-board.json +91 -0
- package/bundle/boards/smtp-email-demo-board.json +34 -0
- package/bundle/boards/sort-demo-board.json +55 -0
- package/bundle/boards/sound-demo-board.json +65 -0
- package/bundle/boards/spectral-modifier-board.json +98 -0
- package/bundle/boards/speech-synth-demo-board.json +37 -0
- package/bundle/boards/speech-to-text-demo-board.json +146 -0
- package/bundle/boards/spotify-board.json +75 -0
- package/bundle/boards/spread-demo-board.json +35 -0
- package/bundle/boards/sql-demo-board.json +56 -0
- package/bundle/boards/sql-explorer-board.json +447 -0
- package/bundle/boards/sql-explorer-node-board.json +474 -0
- package/bundle/boards/sql-import-board.json +156 -0
- package/bundle/boards/state-demo-board.json +65 -0
- package/bundle/boards/stopper-demo-board.json +40 -0
- package/bundle/boards/storage-demo-board.json +193 -0
- package/bundle/boards/store-demo-board.json +63 -0
- package/bundle/boards/swimlanes-demo-board.json +197 -0
- package/bundle/boards/switch-demo-board.json +51 -0
- package/bundle/boards/syn-board.json +19 -0
- package/bundle/boards/syn-booking-unit-board.json +998 -0
- package/bundle/boards/syn-conversations-anthropic-demo-board.json +925 -0
- package/bundle/boards/syn-conversations-demo-board.json +925 -0
- package/bundle/boards/syn-hotels-unit-board.json +287 -0
- package/bundle/boards/syn-mail-desk-board.json +25 -0
- package/bundle/boards/syn-mail-desk-unit-board.json +254 -0
- package/bundle/boards/telegram-listener-demo-board.json +31 -0
- package/bundle/boards/text-generation-anthropic-demo-board.json +66 -0
- package/bundle/boards/text-generation-demo-board.json +51 -0
- package/bundle/boards/text-generation-local-demo-board.json +80 -0
- package/bundle/boards/text-to-speech-demo-board.json +58 -0
- package/bundle/boards/timeline-demo-board.json +477 -0
- package/bundle/boards/timer-demo-board.json +35 -0
- package/bundle/boards/tracks-demo-board.json +95 -0
- package/bundle/boards/trigger-pad-demo-board.json +47 -0
- package/bundle/boards/triggerpad-board.json +31 -0
- package/bundle/boards/triggerpad-recorder-board.json +56 -0
- package/bundle/boards/uuid-generator-demo-board.json +166 -0
- package/bundle/boards/voice-assistant-demo-board.json +230 -0
- package/bundle/boards/voice-assistant-hkp-rt-demo-board.json +239 -0
- package/bundle/boards/voice-assistant-local-demo-board.json +232 -0
- package/bundle/boards/voice-assistant-skills-demo-board.json +316 -0
- package/bundle/boards/voice-assistant-skills-local-demo-board.json +316 -0
- package/bundle/boards/xy-pad-demo-board.json +36 -0
- package/bundle/docs/content/board-json.md +277 -0
- package/bundle/docs/content/boards/alpaca-board.md +56 -0
- package/bundle/docs/content/boards/animate-board.md +40 -0
- package/bundle/docs/content/boards/ascii-cam-board.md +37 -0
- package/bundle/docs/content/boards/audiolize-board.md +47 -0
- package/bundle/docs/content/boards/court-booking-browser-demo-board.md +43 -0
- package/bundle/docs/content/boards/court-booking-demo-board.md +120 -0
- package/bundle/docs/content/boards/current-time-board.md +32 -0
- package/bundle/docs/content/boards/dropitapp-board.md +57 -0
- package/bundle/docs/content/boards/encrypt-board.md +36 -0
- package/bundle/docs/content/boards/face-alert-board.md +50 -0
- package/bundle/docs/content/boards/game-board.md +69 -0
- package/bundle/docs/content/boards/game-of-life-board.md +47 -0
- package/bundle/docs/content/boards/hacker-news-board.md +76 -0
- package/bundle/docs/content/boards/health-log-browser-demo-board.md +56 -0
- package/bundle/docs/content/boards/health-log-demo-board.md +138 -0
- package/bundle/docs/content/boards/helloworld-board.md +40 -0
- package/bundle/docs/content/boards/link-debugger.md +31 -0
- package/bundle/docs/content/boards/live-radio-cloud-demo-board.md +70 -0
- package/bundle/docs/content/boards/live-radio-cloud-direct-demo-board.md +89 -0
- package/bundle/docs/content/boards/live-radio-demo-board.md +102 -0
- package/bundle/docs/content/boards/live-radio-members-demo-board.md +91 -0
- package/bundle/docs/content/boards/meeting-poll-browser-demo-board.md +45 -0
- package/bundle/docs/content/boards/meeting-poll-demo-board.md +121 -0
- package/bundle/docs/content/boards/mounted-endpoint-demo-board-hkp-rt.md +50 -0
- package/bundle/docs/content/boards/nested-rhythm-demo-board.md +165 -0
- package/bundle/docs/content/boards/next-bus-board.md +38 -0
- package/bundle/docs/content/boards/next-bus-switch-board.md +44 -0
- package/bundle/docs/content/boards/night-groove-demo-board.md +110 -0
- package/bundle/docs/content/boards/noise-alert-board.md +44 -0
- package/bundle/docs/content/boards/pad-groove-demo-board.md +93 -0
- package/bundle/docs/content/boards/peer-chat-board.md +49 -0
- package/bundle/docs/content/boards/peer-chat-node-board.md +47 -0
- package/bundle/docs/content/boards/rss-radio-board.md +221 -0
- package/bundle/docs/content/boards/send-ntfy-board.md +33 -0
- package/bundle/docs/content/boards/shared-costs-demo-board.md +65 -0
- package/bundle/docs/content/boards/spectral-modifier-board.md +55 -0
- package/bundle/docs/content/boards/spotify-board.md +46 -0
- package/bundle/docs/content/boards/sql-explorer-board.md +70 -0
- package/bundle/docs/content/boards/sql-explorer-node-board.md +76 -0
- package/bundle/docs/content/boards/sql-import-board.md +37 -0
- package/bundle/docs/content/boards/swimlanes-demo-board.md +148 -0
- package/bundle/docs/content/boards/syn-mail-desk-board.md +52 -0
- package/bundle/docs/content/boards/timeline-demo-board.md +115 -0
- package/bundle/docs/content/boards/triggerpad-board.md +32 -0
- package/bundle/docs/content/boards/triggerpad-recorder-board.md +52 -0
- package/bundle/docs/content/boards/voice-assistant-demo-board.md +51 -0
- package/bundle/docs/content/concepts/assets.md +245 -0
- package/bundle/docs/content/concepts/blocks.md +300 -0
- package/bundle/docs/content/concepts/board-assistant.md +307 -0
- package/bundle/docs/content/concepts/board.md +265 -0
- package/bundle/docs/content/concepts/cloud-boards.md +803 -0
- package/bundle/docs/content/concepts/coordinator.md +297 -0
- package/bundle/docs/content/concepts/logging.md +293 -0
- package/bundle/docs/content/concepts/mounts.md +314 -0
- package/bundle/docs/content/concepts/presets.md +360 -0
- package/bundle/docs/content/concepts/remotes.md +423 -0
- package/bundle/docs/content/concepts/runtime.md +664 -0
- package/bundle/docs/content/concepts/scopes.md +256 -0
- package/bundle/docs/content/concepts/service.md +227 -0
- package/bundle/docs/content/concepts/units.md +321 -0
- package/bundle/docs/content/introduction.md +163 -0
- package/bundle/docs/content/llm/circle-text-board.json +55 -0
- package/bundle/docs/content/llm/howto-workflow.md +369 -0
- package/bundle/docs/content/repository.md +161 -0
- package/bundle/docs/content/services/aggregator.md +76 -0
- package/bundle/docs/content/services/analyzer.md +44 -0
- package/bundle/docs/content/services/ascii-art.md +58 -0
- package/bundle/docs/content/services/asset.md +88 -0
- package/bundle/docs/content/services/audio-editor.md +57 -0
- package/bundle/docs/content/services/audio-encode.md +115 -0
- package/bundle/docs/content/services/audio-io.md +173 -0
- package/bundle/docs/content/services/batcher.md +49 -0
- package/bundle/docs/content/services/board-service.md +48 -0
- package/bundle/docs/content/services/browser-sub-service.md +75 -0
- package/bundle/docs/content/services/buffer.md +70 -0
- package/bundle/docs/content/services/cache.md +92 -0
- package/bundle/docs/content/services/camera.md +85 -0
- package/bundle/docs/content/services/canvas.md +196 -0
- package/bundle/docs/content/services/changes.md +87 -0
- package/bundle/docs/content/services/chunked-file-provider.md +63 -0
- package/bundle/docs/content/services/communication-dispatcher.md +190 -0
- package/bundle/docs/content/services/configurator.md +66 -0
- package/bundle/docs/content/services/conversations.md +244 -0
- package/bundle/docs/content/services/debounce.md +45 -0
- package/bundle/docs/content/services/decrypt.md +53 -0
- package/bundle/docs/content/services/delay.md +55 -0
- package/bundle/docs/content/services/detect.md +101 -0
- package/bundle/docs/content/services/document-extract.md +174 -0
- package/bundle/docs/content/services/download.md +67 -0
- package/bundle/docs/content/services/encode.md +63 -0
- package/bundle/docs/content/services/encrypt.md +53 -0
- package/bundle/docs/content/services/fetcher.md +122 -0
- package/bundle/docs/content/services/fft.md +96 -0
- package/bundle/docs/content/services/file-source.md +67 -0
- package/bundle/docs/content/services/filesystem.md +157 -0
- package/bundle/docs/content/services/filter.md +133 -0
- package/bundle/docs/content/services/flat-map.md +62 -0
- package/bundle/docs/content/services/game-of-life.md +93 -0
- package/bundle/docs/content/services/gather.md +135 -0
- package/bundle/docs/content/services/gif-encoder.md +56 -0
- package/bundle/docs/content/services/github.md +85 -0
- package/bundle/docs/content/services/group-by.md +54 -0
- package/bundle/docs/content/services/hacker.md +79 -0
- package/bundle/docs/content/services/hash.md +59 -0
- package/bundle/docs/content/services/hold.md +182 -0
- package/bundle/docs/content/services/http-relay-client.md +48 -0
- package/bundle/docs/content/services/http-uploader.md +62 -0
- package/bundle/docs/content/services/http.md +424 -0
- package/bundle/docs/content/services/if.md +79 -0
- package/bundle/docs/content/services/ifft.md +46 -0
- package/bundle/docs/content/services/image-picker.md +50 -0
- package/bundle/docs/content/services/imap-email.md +104 -0
- package/bundle/docs/content/services/injector.md +129 -0
- package/bundle/docs/content/services/input.md +90 -0
- package/bundle/docs/content/services/iterator.md +186 -0
- package/bundle/docs/content/services/join.md +153 -0
- package/bundle/docs/content/services/limit.md +47 -0
- package/bundle/docs/content/services/local-storage.md +47 -0
- package/bundle/docs/content/services/looper.md +62 -0
- package/bundle/docs/content/services/lz-compress.md +52 -0
- package/bundle/docs/content/services/map.md +257 -0
- package/bundle/docs/content/services/match-filter.md +38 -0
- package/bundle/docs/content/services/microphone-monitor.md +66 -0
- package/bundle/docs/content/services/monitor.md +67 -0
- package/bundle/docs/content/services/ollama.md +116 -0
- package/bundle/docs/content/services/openai-prompt.md +74 -0
- package/bundle/docs/content/services/output.md +107 -0
- package/bundle/docs/content/services/peer-socket.md +61 -0
- package/bundle/docs/content/services/qr-code.md +48 -0
- package/bundle/docs/content/services/queue.md +201 -0
- package/bundle/docs/content/services/reduce.md +51 -0
- package/bundle/docs/content/services/rss.md +232 -0
- package/bundle/docs/content/services/select.md +47 -0
- package/bundle/docs/content/services/sign.md +53 -0
- package/bundle/docs/content/services/skill-router.md +154 -0
- package/bundle/docs/content/services/smooth.md +59 -0
- package/bundle/docs/content/services/smtp-email.md +129 -0
- package/bundle/docs/content/services/sort.md +48 -0
- package/bundle/docs/content/services/sound.md +87 -0
- package/bundle/docs/content/services/speech-synth.md +46 -0
- package/bundle/docs/content/services/speech-to-text.md +172 -0
- package/bundle/docs/content/services/spotify.md +77 -0
- package/bundle/docs/content/services/spread.md +111 -0
- package/bundle/docs/content/services/sql.md +491 -0
- package/bundle/docs/content/services/state.md +57 -0
- package/bundle/docs/content/services/static.md +47 -0
- package/bundle/docs/content/services/stopper.md +55 -0
- package/bundle/docs/content/services/storage.md +155 -0
- package/bundle/docs/content/services/store.md +255 -0
- package/bundle/docs/content/services/sub-service.md +152 -0
- package/bundle/docs/content/services/switch.md +67 -0
- package/bundle/docs/content/services/telegram-listener.md +76 -0
- package/bundle/docs/content/services/text-generation.md +390 -0
- package/bundle/docs/content/services/text-to-speech.md +130 -0
- package/bundle/docs/content/services/timeline.md +333 -0
- package/bundle/docs/content/services/timer.md +113 -0
- package/bundle/docs/content/services/tracks.md +207 -0
- package/bundle/docs/content/services/trigger-pad.md +68 -0
- package/bundle/docs/content/services/uuid-generator.md +48 -0
- package/bundle/docs/content/services/wav-reader.md +66 -0
- package/bundle/docs/content/services/websocket.md +189 -0
- package/bundle/docs/content/services/workflow-board-builder.md +65 -0
- package/bundle/docs/content/services/xy-pad.md +60 -0
- package/bundle/docs/content/targets.md +434 -0
- package/bundle/docs/content/testing.md +263 -0
- package/bundle/docs/content/vocabulary.md +171 -0
- package/bundle/hkp-frontend/src/runtime/browser/BrowserRegistry.tsx +141 -0
- package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntime.tsx +202 -0
- package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntimeApi.ts +306 -0
- package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntimeApp.ts +310 -0
- package/bundle/hkp-frontend/src/runtime/browser/BrowserRuntimeScope.ts +500 -0
- package/bundle/hkp-frontend/src/runtime/browser/UIRegistry.ts +150 -0
- package/bundle/hkp-frontend/src/runtime/browser/assetResolver.ts +123 -0
- package/bundle/hkp-frontend/src/runtime/browser/pipelineNames.ts +65 -0
- package/bundle/hkp-frontend/src/runtime/browser/registry/Default.ts +237 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Aggregator.ts +127 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AggregatorUI.tsx +83 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Analyzer.tsx +43 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/Bars.tsx +33 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/HorizontalSpectrogram.tsx +53 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/VerticalSpectogram.tsx +56 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/Waveform.tsx +109 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AnalyzerUI/index.tsx +99 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AsciiArt.ts +119 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AsciiArtUI.tsx +131 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Asset.ts +125 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AssetUI.tsx +37 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AudioDeviceSettings.tsx +56 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AudioEditor.ts +129 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AudioEditorUI.tsx +549 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AudioInput.ts +298 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AudioInputUI.tsx +257 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AudioOutput.ts +189 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/AudioOutputUI.tsx +63 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Batcher.ts +11 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/BoardService.tsx +86 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/BoardServiceUI.tsx +410 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/BrowserGather.ts +311 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/BrowserIterator.ts +167 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/BrowserSubService.tsx +583 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/BrowserSubServiceUI.tsx +187 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/BrowserTracks.tsx +477 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Buffer.ts +11 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Cache.ts +134 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CacheUI.tsx +125 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Camera.ts +90 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CameraUI.tsx +321 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Canvas.ts +139 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/canvasDraw.ts +543 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/canvasUtils.ts +39 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/index.tsx +287 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/tests/canvasDraw.test.ts +355 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/tests/canvasUtils.test.ts +89 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/CanvasUI/tests/fullscreen.test.tsx +26 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Changes.ts +201 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/ChangesUI.tsx +85 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/ChunkedFileProvider.tsx +183 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Configurator.ts +258 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/ConfiguratorUI.tsx +83 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/ConsideredHacker.tsx +28 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Debounce.ts +75 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/DebounceUI.tsx +70 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Decrypt.ts +127 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/DecryptUI.tsx +39 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Delay.tsx +110 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Detect.ts +214 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/DetectUI.tsx +86 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Download.ts +120 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/DownloadUI.tsx +61 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Encode.ts +138 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/EncodeUI.tsx +52 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Encrypt.ts +125 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/EncryptUI.tsx +39 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FFT.tsx +238 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FFTUI.tsx +112 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FeedbackService.ts +247 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FeedbackServiceUI.tsx +35 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Fetcher.tsx +232 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FetcherUI.tsx +154 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FileSource.tsx +171 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Filter.tsx +18 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FilterUI.tsx +89 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FlatMap.ts +68 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/FlatMapUI.tsx +38 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLife.ts +210 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLifeRenderer.ts +182 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLifeRendererUI.tsx +48 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GameOfLifeUI.tsx +58 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GatherUI.tsx +84 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GifEncoder.tsx +651 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GithubAPI.ts +294 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GithubComponents.tsx +389 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GithubSink.tsx +109 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GithubSinkUI.tsx +143 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GithubSource.tsx +86 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GithubSourceUI.tsx +162 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/GroupBy.ts +11 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/HTTPUploader.ts +102 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Hacker.ts +73 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/HackerUI.tsx +134 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Hash.ts +134 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/HashUI.tsx +45 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Hold.ts +288 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/HoldUI.tsx +43 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/HttpClient.ts +558 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/HttpClientUI.tsx +212 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/HttpUploaderTokenInjector.ts +149 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/IfService.ts +231 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/IfServiceUI.tsx +50 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/ImagePicker.tsx +118 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Injector.ts +61 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/InjectorUI.tsx +389 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Input.ts +214 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/InputUI.tsx +71 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/IteratorUI.tsx +59 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/LZCompress.ts +63 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Limit.ts +39 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/LimitUI.tsx +41 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/LocalStorage.ts +65 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/LocalStorageUI.tsx +72 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Map.ts +14 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/MapUI.tsx +334 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/MapUIModeAssist.tsx +61 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/MatchFilter.ts +11 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/MicrophoneMonitor.ts +190 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/MicrophoneMonitorUI.tsx +94 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Monitor.tsx +15 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/MonitorUI.tsx +281 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPrompt.ts +237 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/BusyIndicator.tsx +18 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/PromptInput.tsx +71 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/StreamingPreview.tsx +24 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OllamaPromptUI/index.tsx +156 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OpenAIPrompt.ts +254 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OpenAIPromptUI.tsx +189 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Output.tsx +145 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/OutputUI.tsx +118 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/PeerChatTemplate.json +43 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/PeerConnection.ts +260 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/PeerSocket.tsx +268 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/PeerSocketReceiverTemplate.json +29 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/PeerSocketUI.tsx +341 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/PlaceholderUI.tsx +72 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/QrCode.ts +63 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/QrCodeUI.tsx +197 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Select.ts +40 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SelectUI.tsx +41 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/ServiceBase.ts +50 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SetRuntimeVariable.ts +65 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SetRuntimeVariableUI.tsx +50 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Sign.ts +182 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SignUI.tsx +55 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Smooth.ts +101 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SmoothUI.tsx +50 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Sort.ts +59 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SortUI.tsx +61 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Sound.ts +541 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SoundUI.tsx +183 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SpeechSynth.ts +56 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SpeechSynthUI.tsx +108 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Spotify.tsx +111 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SpotifyAPI.ts +113 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SpotifyComponents.tsx +104 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SpotifyUI.tsx +186 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Spread.ts +117 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SpreadUI.tsx +38 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Sql.ts +448 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SqlInspector.tsx +332 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SqlUI.tsx +226 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/State.ts +149 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Stopper.ts +29 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Switch.tsx +419 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/SwitchUI.tsx +171 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TickRuleDelta.ts +101 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TickRuleDeltaUI.tsx +56 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Timeline.ts +709 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/TimelinePanel.tsx +79 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/TimelineRows.tsx +574 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/Transport.tsx +141 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/index.tsx +119 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/model.ts +425 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TimelineUI/parts.tsx +708 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/Timer.ts +15 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TimerUI.tsx +213 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TracksUI.tsx +81 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TriggerPad.ts +108 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/TriggerPadUI.tsx +160 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/UuidGenerator.ts +74 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/UuidGeneratorUI.tsx +59 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/WaveformDisplay.tsx +104 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/WebsocketClient.ts +114 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/WebsocketClientUI.tsx +42 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/WorkflowBoardBuilder.ts +499 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/WorkflowBoardBuilderUI.tsx +521 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/XYPad.ts +68 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/XYPadUI.tsx +274 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/audio/audioHelper.ts +36 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/Batcher.ts +76 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/Buffer.ts +142 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/Filter.ts +125 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/GroupBy.ts +101 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/Map.ts +543 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/MatchFilter.ts +49 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/Monitor.ts +75 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/Reduce.ts +74 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/Timer.ts +342 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/bundle.js +7375 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/eval.ts +208 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/expressionEval.ts +223 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/helpers.ts +10 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/base/index.ts +37 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/camera/videoCapture.ts +78 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/changes-modes.ts +9 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/colorpalette.ts +26 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/detect/DetectionPreview.tsx +76 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/detect/mediapipe.ts +34 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/detect/models/README.md +14 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/detect/models/blaze_face_short_range.tflite +0 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/detect-modes.ts +7 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/encode-modes.ts +10 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/helpers.ts +300 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/http-client-methods.ts +10 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/sql-database.ts +594 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/sql-dump.ts +192 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/sql-inspect.ts +130 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/sql-inspector.css +155 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/sql-modes.ts +27 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/TESTING_APPROACH.md +169 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/__snapshots__/service-ui.future-enhancements.test.tsx.snap +5 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/audio-input-permissions.test.ts +42 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/audiolizeLanes.test.ts +70 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/browser-scopes.test.ts +305 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/cache-service.test.ts +53 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/camera-video-capture.test.ts +24 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/changes-service.test.ts +133 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/crypto-secrets.test.ts +196 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/detect-service.test.ts +119 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/download.test.ts +100 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/expressionEval.test.ts +117 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/fetcher-panel.test.tsx +35 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/fetcher-service.test.ts +80 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/filter-service.test.ts +37 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/github-secrets.test.ts +108 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/group-by-board.test.ts +42 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/hold-service.test.ts +199 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/http-client-service.test.ts +474 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/http-uploader-token-injector.test.ts +152 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/iterator-gather-service.test.ts +277 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/map-service.test.ts +1047 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/map-ui.test.tsx +70 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/nested-rhythm-board.test.ts +311 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/night-groove-board.test.ts +290 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/pad-groove-board.test.ts +230 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/peer-host-discovery.test.ts +77 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/peer-socket-discovery.test.ts +69 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/pipeline-names.test.ts +63 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/secret-canary.test.ts +121 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-modules.contract.test.ts +229 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.advanced.test.tsx +670 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.future-enhancements.test.tsx +219 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.integration.test.tsx +432 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/service-ui.smoke.test.tsx +140 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/sound-service.test.ts +293 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/spread-service.test.ts +137 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql-dump.test.ts +172 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql-inspect.test.ts +163 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql-inspector.test.tsx +121 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/sql.test.ts +702 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/sub-service-in-block.test.tsx +84 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/switch-service.test.ts +254 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-board.test.ts +218 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-service.test.ts +1002 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-ui-model.test.ts +230 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/timeline-ui.test.tsx +371 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/timer-service.test.ts +902 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/tracks-service.test.ts +275 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/tracks-ui.test.tsx +126 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/triggerpad-recorder-board.test.ts +100 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/tests/uuid-generator-service.test.ts +214 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/timeline-core.ts +410 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/DefaultWorkflowBoard.ts +90 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/RefinementSession.ts +121 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/SystemPromptCatalog.ts +245 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/catalog/browser-service-specs.json +110 -0
- package/bundle/hkp-frontend/src/runtime/browser/services/workflow-prompt/catalog/runtime-capabilities.json +37 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/asset-service.test.ts +73 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/board-persistence.integration.test.tsx +391 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/browser-runtime.identity-binding.test.tsx +81 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/demo-boards.regression.test.tsx +386 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/failed-call-lifecycle.test.ts +56 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/http-client.integration.test.ts +146 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/nested-run.integration.test.ts +415 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/replay-next.test.ts +391 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/rerun.test.ts +71 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/run-no-input.test.ts +72 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-add.integration.test.tsx +256 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-operations.integration.test.tsx +310 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-processing.integration.test.ts +311 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-removal.integration.test.tsx +454 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/runtime-update.identity.integration.test.tsx +145 -0
- package/bundle/hkp-frontend/src/runtime/browser/tests/service-operations.integration.test.tsx +511 -0
- package/bundle/hkp-node/src/address.ts +72 -0
- package/bundle/hkp-node/src/assets.ts +582 -0
- package/bundle/hkp-node/src/auth.ts +369 -0
- package/bundle/hkp-node/src/callReport.ts +281 -0
- package/bundle/hkp-node/src/coordinator/auth.ts +85 -0
- package/bundle/hkp-node/src/coordinator/binaryFrame.ts +189 -0
- package/bundle/hkp-node/src/coordinator/boardStore.ts +0 -0
- package/bundle/hkp-node/src/coordinator/bridge.ts +242 -0
- package/bundle/hkp-node/src/coordinator/bridgeProtocol.ts +200 -0
- package/bundle/hkp-node/src/coordinator/coordinator.ts +594 -0
- package/bundle/hkp-node/src/coordinator/facadeAccess.ts +426 -0
- package/bundle/hkp-node/src/coordinator/fileBoardStore.ts +159 -0
- package/bundle/hkp-node/src/coordinator/frameLimits.ts +41 -0
- package/bundle/hkp-node/src/coordinator/index.ts +12 -0
- package/bundle/hkp-node/src/coordinator/join.ts +62 -0
- package/bundle/hkp-node/src/coordinator/logStore.ts +335 -0
- package/bundle/hkp-node/src/coordinator/members.ts +133 -0
- package/bundle/hkp-node/src/coordinator/mount.ts +124 -0
- package/bundle/hkp-node/src/coordinator/participantProtocol.ts +169 -0
- package/bundle/hkp-node/src/coordinator/participants.ts +666 -0
- package/bundle/hkp-node/src/coordinator/router.ts +416 -0
- package/bundle/hkp-node/src/coordinator/session.ts +1610 -0
- package/bundle/hkp-node/src/coordinator/types.ts +95 -0
- package/bundle/hkp-node/src/coordinatorLinks.ts +712 -0
- package/bundle/hkp-node/src/credentialedFetch.ts +122 -0
- package/bundle/hkp-node/src/egress.ts +344 -0
- package/bundle/hkp-node/src/expressionEval.ts +223 -0
- package/bundle/hkp-node/src/index.ts +470 -0
- package/bundle/hkp-node/src/instance.ts +172 -0
- package/bundle/hkp-node/src/mounts.ts +296 -0
- package/bundle/hkp-node/src/origins.ts +201 -0
- package/bundle/hkp-node/src/reset.ts +187 -0
- package/bundle/hkp-node/src/runtime.ts +1345 -0
- package/bundle/hkp-node/src/secrets.ts +292 -0
- package/bundle/hkp-node/src/server.ts +2120 -0
- package/bundle/hkp-node/src/services/asset.ts +150 -0
- package/bundle/hkp-node/src/services/claudeModels.ts +370 -0
- package/bundle/hkp-node/src/services/communication-dispatcher.ts +735 -0
- package/bundle/hkp-node/src/services/conversations.ts +914 -0
- package/bundle/hkp-node/src/services/database.ts +386 -0
- package/bundle/hkp-node/src/services/document-extract.ts +575 -0
- package/bundle/hkp-node/src/services/expression.ts +417 -0
- package/bundle/hkp-node/src/services/fileStore.ts +369 -0
- package/bundle/hkp-node/src/services/filesystem.ts +280 -0
- package/bundle/hkp-node/src/services/gather.ts +335 -0
- package/bundle/hkp-node/src/services/hold.ts +275 -0
- package/bundle/hkp-node/src/services/http-client.ts +525 -0
- package/bundle/hkp-node/src/services/http-server.ts +1222 -0
- package/bundle/hkp-node/src/services/imap-email.ts +627 -0
- package/bundle/hkp-node/src/services/injector.ts +135 -0
- package/bundle/hkp-node/src/services/iterator.ts +203 -0
- package/bundle/hkp-node/src/services/join.ts +226 -0
- package/bundle/hkp-node/src/services/map.ts +632 -0
- package/bundle/hkp-node/src/services/monitor.ts +128 -0
- package/bundle/hkp-node/src/services/nested-pipeline.ts +405 -0
- package/bundle/hkp-node/src/services/peer-server.ts +268 -0
- package/bundle/hkp-node/src/services/queue-store.ts +301 -0
- package/bundle/hkp-node/src/services/queue.ts +316 -0
- package/bundle/hkp-node/src/services/recordStore.ts +341 -0
- package/bundle/hkp-node/src/services/rss-parse.ts +281 -0
- package/bundle/hkp-node/src/services/rss.ts +392 -0
- package/bundle/hkp-node/src/services/smtp-email.ts +398 -0
- package/bundle/hkp-node/src/services/spread.ts +153 -0
- package/bundle/hkp-node/src/services/sql-dump.ts +192 -0
- package/bundle/hkp-node/src/services/sql.ts +429 -0
- package/bundle/hkp-node/src/services/stopper.ts +69 -0
- package/bundle/hkp-node/src/services/storage.ts +332 -0
- package/bundle/hkp-node/src/services/store.ts +609 -0
- package/bundle/hkp-node/src/services/stream.ts +367 -0
- package/bundle/hkp-node/src/services/sub-service.ts +600 -0
- package/bundle/hkp-node/src/services/telegram-listener.ts +263 -0
- package/bundle/hkp-node/src/services/telegram-sender.ts +136 -0
- package/bundle/hkp-node/src/services/text-generation.ts +1249 -0
- package/bundle/hkp-node/src/services/timer.ts +273 -0
- package/bundle/hkp-node/src/services/tracks.ts +381 -0
- package/bundle/hkp-node/src/services/websocket-reader.ts +364 -0
- package/bundle/hkp-node/src/types.ts +465 -0
- package/bundle/hkp-node/src/wsTickets.ts +77 -0
- package/bundle/hkp-node/src/yas.ts +95 -0
- package/bundle/hkp-python/src/hkp/__init__.py +1 -0
- package/bundle/hkp-python/src/hkp/__main__.py +241 -0
- package/bundle/hkp-python/src/hkp/address.py +56 -0
- package/bundle/hkp-python/src/hkp/assets.py +439 -0
- package/bundle/hkp-python/src/hkp/auth.py +252 -0
- package/bundle/hkp-python/src/hkp/binary_frame.py +100 -0
- package/bundle/hkp-python/src/hkp/call_report.py +201 -0
- package/bundle/hkp-python/src/hkp/coordinator_links.py +667 -0
- package/bundle/hkp-python/src/hkp/data.py +118 -0
- package/bundle/hkp-python/src/hkp/egress.py +310 -0
- package/bundle/hkp-python/src/hkp/mount.py +46 -0
- package/bundle/hkp-python/src/hkp/mounts.py +288 -0
- package/bundle/hkp-python/src/hkp/origins.py +169 -0
- package/bundle/hkp-python/src/hkp/runtime.py +1131 -0
- package/bundle/hkp-python/src/hkp/secrets.py +261 -0
- package/bundle/hkp-python/src/hkp/server.py +1714 -0
- package/bundle/hkp-python/src/hkp/services/__init__.py +0 -0
- package/bundle/hkp-python/src/hkp/services/asset.py +123 -0
- package/bundle/hkp-python/src/hkp/services/audio_encode.py +190 -0
- package/bundle/hkp-python/src/hkp/services/expression.py +942 -0
- package/bundle/hkp-python/src/hkp/services/gather.py +266 -0
- package/bundle/hkp-python/src/hkp/services/hold.py +246 -0
- package/bundle/hkp-python/src/hkp/services/http_client.py +444 -0
- package/bundle/hkp-python/src/hkp/services/http_server.py +790 -0
- package/bundle/hkp-python/src/hkp/services/iterator.py +141 -0
- package/bundle/hkp-python/src/hkp/services/join.py +173 -0
- package/bundle/hkp-python/src/hkp/services/map_service.py +452 -0
- package/bundle/hkp-python/src/hkp/services/monitor.py +99 -0
- package/bundle/hkp-python/src/hkp/services/nested_pipeline.py +278 -0
- package/bundle/hkp-python/src/hkp/services/skill_router.py +388 -0
- package/bundle/hkp-python/src/hkp/services/speech_to_text.py +186 -0
- package/bundle/hkp-python/src/hkp/services/spread.py +114 -0
- package/bundle/hkp-python/src/hkp/services/stopper.py +59 -0
- package/bundle/hkp-python/src/hkp/services/sub_service.py +417 -0
- package/bundle/hkp-python/src/hkp/services/text_generation.py +498 -0
- package/bundle/hkp-python/src/hkp/services/text_to_speech.py +237 -0
- package/bundle/hkp-python/src/hkp/services/timer.py +267 -0
- package/bundle/hkp-python/src/hkp/services/tracks.py +251 -0
- package/bundle/hkp-python/src/hkp/types.py +412 -0
- package/bundle/hkp-python/src/hkp/ws_tickets.py +54 -0
- package/bundle/hkp-python/src/hkp/yas.py +192 -0
- package/bundle/hkp-rt/lib/CMakeLists.txt +193 -0
- package/bundle/hkp-rt/lib/hkp-bundles/CMakeLists.txt +55 -0
- package/bundle/hkp-rt/lib/hkp-bundles/plugin_register.cpp +14 -0
- package/bundle/hkp-rt/lib/include/app.h +201 -0
- package/bundle/hkp-rt/lib/include/assets.h +239 -0
- package/bundle/hkp-rt/lib/include/auth.h +167 -0
- package/bundle/hkp-rt/lib/include/call_report.h +197 -0
- package/bundle/hkp-rt/lib/include/coordinator_links.h +127 -0
- package/bundle/hkp-rt/lib/include/discovery_hooks.h +29 -0
- package/bundle/hkp-rt/lib/include/egress.h +379 -0
- package/bundle/hkp-rt/lib/include/files.h +275 -0
- package/bundle/hkp-rt/lib/include/log_entry.h +131 -0
- package/bundle/hkp-rt/lib/include/mounts.h +139 -0
- package/bundle/hkp-rt/lib/include/origins.h +363 -0
- package/bundle/hkp-rt/lib/include/registry.h +119 -0
- package/bundle/hkp-rt/lib/include/secrets.h +414 -0
- package/bundle/hkp-rt/lib/include/server.h +122 -0
- package/bundle/hkp-rt/lib/include/service.h +190 -0
- package/bundle/hkp-rt/lib/include/slot_store.h +41 -0
- package/bundle/hkp-rt/lib/include/standalone_config.h +253 -0
- package/bundle/hkp-rt/lib/include/types/audio.h +3 -0
- package/bundle/hkp-rt/lib/include/types/data.h +152 -0
- package/bundle/hkp-rt/lib/include/types/message.h +56 -0
- package/bundle/hkp-rt/lib/include/types/models.h +154 -0
- package/bundle/hkp-rt/lib/include/types/owns_me.h +19 -0
- package/bundle/hkp-rt/lib/include/types/ringbuffer.h +86 -0
- package/bundle/hkp-rt/lib/include/types/types.h +33 -0
- package/bundle/hkp-rt/lib/include/types/validation.h +205 -0
- package/bundle/hkp-rt/lib/src/address.h +88 -0
- package/bundle/hkp-rt/lib/src/app.cpp +591 -0
- package/bundle/hkp-rt/lib/src/assets.cpp +686 -0
- package/bundle/hkp-rt/lib/src/binary_frame.h +154 -0
- package/bundle/hkp-rt/lib/src/common/expression.h +1481 -0
- package/bundle/hkp-rt/lib/src/common/inja.h +36 -0
- package/bundle/hkp-rt/lib/src/common/link_socket.cpp +437 -0
- package/bundle/hkp-rt/lib/src/common/link_socket.h +97 -0
- package/bundle/hkp-rt/lib/src/common/realtime_wakeup.h +120 -0
- package/bundle/hkp-rt/lib/src/common/stream_broadcast.h +250 -0
- package/bundle/hkp-rt/lib/src/common/string_util.h +31 -0
- package/bundle/hkp-rt/lib/src/common/websocket_client_session.cpp +217 -0
- package/bundle/hkp-rt/lib/src/common/websocket_client_session.h +37 -0
- package/bundle/hkp-rt/lib/src/common/websocket_config.h +119 -0
- package/bundle/hkp-rt/lib/src/common/websocket_protocol.h +32 -0
- package/bundle/hkp-rt/lib/src/common/websocket_server.cpp +82 -0
- package/bundle/hkp-rt/lib/src/common/websocket_server.h +57 -0
- package/bundle/hkp-rt/lib/src/common/websocket_server_listener.cpp +115 -0
- package/bundle/hkp-rt/lib/src/common/websocket_server_listener.h +35 -0
- package/bundle/hkp-rt/lib/src/common/websocket_server_session.cpp +172 -0
- package/bundle/hkp-rt/lib/src/common/websocket_server_session.h +53 -0
- package/bundle/hkp-rt/lib/src/common.h +13 -0
- package/bundle/hkp-rt/lib/src/coordinator_links.cpp +1142 -0
- package/bundle/hkp-rt/lib/src/discovery/discovery.cpp +707 -0
- package/bundle/hkp-rt/lib/src/discovery/discovery.h +89 -0
- package/bundle/hkp-rt/lib/src/http/auth.cpp +569 -0
- package/bundle/hkp-rt/lib/src/http/front_door.cpp +518 -0
- package/bundle/hkp-rt/lib/src/http/front_door.h +131 -0
- package/bundle/hkp-rt/lib/src/http/server.cpp +1539 -0
- package/bundle/hkp-rt/lib/src/http/ws_tickets.h +125 -0
- package/bundle/hkp-rt/lib/src/mount.h +63 -0
- package/bundle/hkp-rt/lib/src/process_context.h +335 -0
- package/bundle/hkp-rt/lib/src/process_depth.h +20 -0
- package/bundle/hkp-rt/lib/src/registry.cpp +233 -0
- package/bundle/hkp-rt/lib/src/runtime.cpp +986 -0
- package/bundle/hkp-rt/lib/src/runtime.h +225 -0
- package/bundle/hkp-rt/lib/src/runtime_host.h +180 -0
- package/bundle/hkp-rt/lib/src/service.cpp +315 -0
- package/bundle/hkp-rt/lib/src/services/asset.h +185 -0
- package/bundle/hkp-rt/lib/src/services/audio_encode.cpp +372 -0
- package/bundle/hkp-rt/lib/src/services/audio_encode.h +97 -0
- package/bundle/hkp-rt/lib/src/services/cache.h +362 -0
- package/bundle/hkp-rt/lib/src/services/cache_subservices.h +463 -0
- package/bundle/hkp-rt/lib/src/services/core_audio.cpp +384 -0
- package/bundle/hkp-rt/lib/src/services/core_audio.h +22 -0
- package/bundle/hkp-rt/lib/src/services/core_input.h +337 -0
- package/bundle/hkp-rt/lib/src/services/core_output.h +370 -0
- package/bundle/hkp-rt/lib/src/services/ffmpeg/detail.cpp +749 -0
- package/bundle/hkp-rt/lib/src/services/ffmpeg/detail.h +29 -0
- package/bundle/hkp-rt/lib/src/services/ffmpeg/ffmpeg.cpp +193 -0
- package/bundle/hkp-rt/lib/src/services/ffmpeg/ffmpeg.h +40 -0
- package/bundle/hkp-rt/lib/src/services/ffmpeg/types.h +120 -0
- package/bundle/hkp-rt/lib/src/services/fft_service.h +190 -0
- package/bundle/hkp-rt/lib/src/services/filesystem.h +309 -0
- package/bundle/hkp-rt/lib/src/services/filter.h +208 -0
- package/bundle/hkp-rt/lib/src/services/gather.h +272 -0
- package/bundle/hkp-rt/lib/src/services/hold.h +304 -0
- package/bundle/hkp-rt/lib/src/services/http_client.cpp +347 -0
- package/bundle/hkp-rt/lib/src/services/http_client.h +67 -0
- package/bundle/hkp-rt/lib/src/services/http_client_impl.h +45 -0
- package/bundle/hkp-rt/lib/src/services/http_client_template_examples.md +202 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_listener.cpp +130 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_listener.h +33 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_server.cpp +351 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_server.h +79 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_server_impl.cpp +230 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_server_impl.h +64 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_server_subservices.cpp +950 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_server_subservices.h +253 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_session.cpp +495 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_session.h +169 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_stream_listener.cpp +270 -0
- package/bundle/hkp-rt/lib/src/services/http_server/http_stream_listener.h +104 -0
- package/bundle/hkp-rt/lib/src/services/http_server/request_decode.h +164 -0
- package/bundle/hkp-rt/lib/src/services/if_service.h +210 -0
- package/bundle/hkp-rt/lib/src/services/ifft_service.h +198 -0
- package/bundle/hkp-rt/lib/src/services/injector.h +162 -0
- package/bundle/hkp-rt/lib/src/services/iterator.h +201 -0
- package/bundle/hkp-rt/lib/src/services/map.h +641 -0
- package/bundle/hkp-rt/lib/src/services/monitor.h +161 -0
- package/bundle/hkp-rt/lib/src/services/mp3_frames.h +119 -0
- package/bundle/hkp-rt/lib/src/services/mp4_to_wav.cpp +281 -0
- package/bundle/hkp-rt/lib/src/services/mp4_to_wav.h +263 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_registry.cpp +88 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_registry.h +43 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_server.h +163 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_listener.cpp +133 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_listener.h +46 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_session.cpp +165 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_server_session.h +44 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_ws_session.cpp +184 -0
- package/bundle/hkp-rt/lib/src/services/peer_server/peer_ws_session.h +55 -0
- package/bundle/hkp-rt/lib/src/services/receiver_session.h +172 -0
- package/bundle/hkp-rt/lib/src/services/root_certificates.h +3959 -0
- package/bundle/hkp-rt/lib/src/services/sha256_openssl.h +49 -0
- package/bundle/hkp-rt/lib/src/services/speech_http.h +306 -0
- package/bundle/hkp-rt/lib/src/services/speech_to_text.cpp +595 -0
- package/bundle/hkp-rt/lib/src/services/speech_to_text.h +69 -0
- package/bundle/hkp-rt/lib/src/services/static.cpp +45 -0
- package/bundle/hkp-rt/lib/src/services/static.h +41 -0
- package/bundle/hkp-rt/lib/src/services/stopper.h +72 -0
- package/bundle/hkp-rt/lib/src/services/sub_service.h +286 -0
- package/bundle/hkp-rt/lib/src/services/text_generation.cpp +1132 -0
- package/bundle/hkp-rt/lib/src/services/text_generation.h +71 -0
- package/bundle/hkp-rt/lib/src/services/text_to_speech.cpp +853 -0
- package/bundle/hkp-rt/lib/src/services/text_to_speech.h +97 -0
- package/bundle/hkp-rt/lib/src/services/timer.h +300 -0
- package/bundle/hkp-rt/lib/src/services/tracks.h +397 -0
- package/bundle/hkp-rt/lib/src/services/tts_inflect.cpp +149 -0
- package/bundle/hkp-rt/lib/src/services/tts_inflect.h +82 -0
- package/bundle/hkp-rt/lib/src/services/wav_reader.h +213 -0
- package/bundle/hkp-rt/lib/src/services/websocket_client_service.h +182 -0
- package/bundle/hkp-rt/lib/src/services/websocket_reader.h +148 -0
- package/bundle/hkp-rt/lib/src/services/websocket_server_service.h +196 -0
- package/bundle/hkp-rt/lib/src/services/websocket_writer.cpp +641 -0
- package/bundle/hkp-rt/lib/src/services/websocket_writer.h +117 -0
- package/bundle/hkp-rt/lib/src/sub_runtime.cpp +234 -0
- package/bundle/hkp-rt/lib/src/sub_runtime.h +160 -0
- package/bundle/hkp-rt/lib/src/types/data.cpp +237 -0
- package/bundle/hkp-rt/lib/src/types/message.cpp +148 -0
- package/bundle/hkp-rt/lib/src/types/models.cpp +83 -0
- package/bundle/hkp-rt/lib/src/types/ringbuffer.cpp +336 -0
- package/bundle/hkp-rt/lib/src/uuid.h +12 -0
- package/bundle/readymade-mcp-commits.json +5 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +79 -0
- package/dist/git.d.ts +8 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +53 -0
- package/dist/http.d.ts +17 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +130 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/repository.d.ts +49 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +452 -0
- package/dist/schemas.d.ts +156 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +43 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +83 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/validation.d.ts +9 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +101 -0
- package/package.json +52 -4
|
@@ -0,0 +1,942 @@
|
|
|
1
|
+
"""Expression evaluation for services that let a board author write small
|
|
2
|
+
dynamic terms — a Map template's ``key=`` rows, for example.
|
|
3
|
+
|
|
4
|
+
An expression is a single JavaScript-style expression evaluated with the
|
|
5
|
+
incoming data bound to ``params`` and the helper functions below in scope. The
|
|
6
|
+
browser, Node and C++ runtimes evaluate the same sources, so the dialect and the
|
|
7
|
+
helper set are kept aligned: a template authored in the shared Map UI behaves
|
|
8
|
+
the same whichever runtime hosts the service. Helpers that need a browser (DOM,
|
|
9
|
+
vault, AudioContext) have no counterpart here.
|
|
10
|
+
|
|
11
|
+
Supported: literals (number, string, true/false/null), array literals,
|
|
12
|
+
identifiers, member and index access, calls into the builtin table, unary
|
|
13
|
+
``!``/``-``/``+``, ``* / %``, ``+ -``, ``< <= > >=``, ``== != === !==``,
|
|
14
|
+
``&& ||`` and the ternary operator. There are no assignments, no statements and
|
|
15
|
+
no lambdas — ``find``/``filter`` take their predicate as an expression string
|
|
16
|
+
with the element bound to ``item`` (and its position to ``index``).
|
|
17
|
+
|
|
18
|
+
Python spellings are accepted alongside the JavaScript ones (``and``/``or``/
|
|
19
|
+
``not``, ``True``/``False``/``None``) and the builtin table carries the Python
|
|
20
|
+
conversions the previous evaluator exposed, so flat templates written against
|
|
21
|
+
it keep working.
|
|
22
|
+
|
|
23
|
+
expression = Expression.parse("round(params.value * 2)")
|
|
24
|
+
result = expression.evaluate(input) # raises EvaluationError on failure
|
|
25
|
+
|
|
26
|
+
A member of nothing is nothing: ``params.a.b`` with no ``a`` is ``None``, as in
|
|
27
|
+
the browser, Node and C++ runtimes. Whoever wants to know which reads came up
|
|
28
|
+
empty passes ``on_missing``.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import json as json_module
|
|
34
|
+
import math
|
|
35
|
+
import random
|
|
36
|
+
import re
|
|
37
|
+
import time
|
|
38
|
+
import uuid as uuid_module
|
|
39
|
+
from contextvars import ContextVar
|
|
40
|
+
from dataclasses import dataclass, field
|
|
41
|
+
from datetime import datetime
|
|
42
|
+
from typing import Any, Callable
|
|
43
|
+
from urllib.parse import quote
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ParseError(Exception):
|
|
47
|
+
"""Raised when an expression source cannot be parsed."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class EvaluationError(Exception):
|
|
51
|
+
"""Raised when a parsed expression cannot be evaluated."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ── Value helpers ──────────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_truthy(value: Any) -> bool:
|
|
58
|
+
if value is None or value is False:
|
|
59
|
+
return False
|
|
60
|
+
if value is True:
|
|
61
|
+
return True
|
|
62
|
+
if isinstance(value, (int, float)):
|
|
63
|
+
return value != 0
|
|
64
|
+
if isinstance(value, str):
|
|
65
|
+
return value != ""
|
|
66
|
+
return True # objects and arrays are truthy, empty or not
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def to_number(value: Any) -> float:
|
|
70
|
+
"""JS-style numeric coercion. Returns NaN for anything that is not numeric."""
|
|
71
|
+
if isinstance(value, bool):
|
|
72
|
+
return 1.0 if value else 0.0
|
|
73
|
+
if isinstance(value, (int, float)):
|
|
74
|
+
return float(value)
|
|
75
|
+
if value is None:
|
|
76
|
+
return 0.0
|
|
77
|
+
if isinstance(value, str):
|
|
78
|
+
try:
|
|
79
|
+
return float(value.strip())
|
|
80
|
+
except ValueError:
|
|
81
|
+
return math.nan
|
|
82
|
+
return math.nan
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def to_text(value: Any) -> str:
|
|
86
|
+
"""Renders a value the way JSON.stringify would, so 42 does not become 42.0."""
|
|
87
|
+
if isinstance(value, str):
|
|
88
|
+
return value
|
|
89
|
+
if value is None:
|
|
90
|
+
return "null"
|
|
91
|
+
if value is True:
|
|
92
|
+
return "true"
|
|
93
|
+
if value is False:
|
|
94
|
+
return "false"
|
|
95
|
+
if isinstance(value, float) and value.is_integer():
|
|
96
|
+
return str(int(value))
|
|
97
|
+
if isinstance(value, (int, float)):
|
|
98
|
+
return str(value)
|
|
99
|
+
return json_module.dumps(value, separators=(",", ":"))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _number(value: float) -> Any:
|
|
103
|
+
"""An integral result is stored as an int; NaN has no JSON form and is null."""
|
|
104
|
+
if isinstance(value, float):
|
|
105
|
+
if math.isnan(value) or math.isinf(value):
|
|
106
|
+
return None
|
|
107
|
+
if value.is_integer():
|
|
108
|
+
return int(value)
|
|
109
|
+
return value
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# ── Date helpers (moment-style token subset shared with the other runtimes) ────
|
|
113
|
+
|
|
114
|
+
_MONTHS = [
|
|
115
|
+
"January", "February", "March", "April", "May", "June",
|
|
116
|
+
"July", "August", "September", "October", "November", "December",
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
_WEEKDAYS = [
|
|
120
|
+
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
# Longest token first, so "MMMM" is matched before "MM".
|
|
124
|
+
_TOKEN_PATTERN = re.compile(
|
|
125
|
+
r"\[([^\]]*)\]|YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|A|a"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _format_date(value: datetime, fmt: str) -> str:
|
|
130
|
+
weekday = (value.weekday() + 1) % 7 # datetime weeks start on Monday
|
|
131
|
+
hours12 = value.hour % 12 or 12
|
|
132
|
+
|
|
133
|
+
def render(match: re.Match[str]) -> str:
|
|
134
|
+
if match.group(1) is not None:
|
|
135
|
+
return match.group(1)
|
|
136
|
+
token = match.group(0)
|
|
137
|
+
return {
|
|
138
|
+
"YYYY": f"{value.year}",
|
|
139
|
+
"YY": f"{value.year % 100:02d}",
|
|
140
|
+
"MMMM": _MONTHS[value.month - 1],
|
|
141
|
+
"MMM": _MONTHS[value.month - 1][:3],
|
|
142
|
+
"MM": f"{value.month:02d}",
|
|
143
|
+
"M": f"{value.month}",
|
|
144
|
+
"DD": f"{value.day:02d}",
|
|
145
|
+
"D": f"{value.day}",
|
|
146
|
+
"dddd": _WEEKDAYS[weekday],
|
|
147
|
+
"ddd": _WEEKDAYS[weekday][:3],
|
|
148
|
+
"HH": f"{value.hour:02d}",
|
|
149
|
+
"H": f"{value.hour}",
|
|
150
|
+
"hh": f"{hours12:02d}",
|
|
151
|
+
"h": f"{hours12}",
|
|
152
|
+
"mm": f"{value.minute:02d}",
|
|
153
|
+
"m": f"{value.minute}",
|
|
154
|
+
"ss": f"{value.second:02d}",
|
|
155
|
+
"s": f"{value.second}",
|
|
156
|
+
"A": "AM" if value.hour < 12 else "PM",
|
|
157
|
+
"a": "am" if value.hour < 12 else "pm",
|
|
158
|
+
}[token]
|
|
159
|
+
|
|
160
|
+
return _TOKEN_PATTERN.sub(render, fmt)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _parse_date(text: str, fmt: str) -> datetime | None:
|
|
164
|
+
"""Reads a date written in the token subset above, or None when it does not fit."""
|
|
165
|
+
pattern = ""
|
|
166
|
+
captured: list[str] = []
|
|
167
|
+
last = 0
|
|
168
|
+
|
|
169
|
+
for match in _TOKEN_PATTERN.finditer(fmt):
|
|
170
|
+
pattern += re.escape(fmt[last : match.start()])
|
|
171
|
+
last = match.end()
|
|
172
|
+
|
|
173
|
+
if match.group(1) is not None:
|
|
174
|
+
pattern += re.escape(match.group(1))
|
|
175
|
+
continue
|
|
176
|
+
|
|
177
|
+
token = match.group(0)
|
|
178
|
+
captured.append(token)
|
|
179
|
+
if token == "YYYY":
|
|
180
|
+
pattern += r"(\d{4})"
|
|
181
|
+
elif token in ("MMMM", "MMM", "dddd", "ddd"):
|
|
182
|
+
pattern += r"([A-Za-z]+)"
|
|
183
|
+
elif token in ("A", "a"):
|
|
184
|
+
pattern += r"([AaPp][Mm])"
|
|
185
|
+
else:
|
|
186
|
+
pattern += r"(\d{1,2})"
|
|
187
|
+
pattern += re.escape(fmt[last:])
|
|
188
|
+
|
|
189
|
+
if not captured:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
match = re.fullmatch(pattern, str(text).strip())
|
|
193
|
+
if not match:
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
parts = {"year": 1970, "month": 1, "day": 1, "hour": 0, "minute": 0, "second": 0}
|
|
197
|
+
pm = False
|
|
198
|
+
hours12 = False
|
|
199
|
+
|
|
200
|
+
for token, raw in zip(captured, match.groups()):
|
|
201
|
+
if token == "YYYY":
|
|
202
|
+
parts["year"] = int(raw)
|
|
203
|
+
elif token == "YY":
|
|
204
|
+
parts["year"] = 2000 + int(raw)
|
|
205
|
+
elif token in ("MMMM", "MMM"):
|
|
206
|
+
name = raw.lower()
|
|
207
|
+
for index, month in enumerate(_MONTHS):
|
|
208
|
+
if month.lower().startswith(name):
|
|
209
|
+
parts["month"] = index + 1
|
|
210
|
+
break
|
|
211
|
+
elif token in ("MM", "M"):
|
|
212
|
+
parts["month"] = int(raw)
|
|
213
|
+
elif token in ("DD", "D"):
|
|
214
|
+
parts["day"] = int(raw)
|
|
215
|
+
elif token in ("HH", "H"):
|
|
216
|
+
parts["hour"] = int(raw)
|
|
217
|
+
elif token in ("hh", "h"):
|
|
218
|
+
parts["hour"] = int(raw)
|
|
219
|
+
hours12 = True
|
|
220
|
+
elif token in ("mm", "m"):
|
|
221
|
+
parts["minute"] = int(raw)
|
|
222
|
+
elif token in ("ss", "s"):
|
|
223
|
+
parts["second"] = int(raw)
|
|
224
|
+
elif token in ("A", "a"):
|
|
225
|
+
pm = raw.lower() == "pm"
|
|
226
|
+
|
|
227
|
+
if hours12:
|
|
228
|
+
parts["hour"] = (parts["hour"] % 12) + (12 if pm else 0)
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
return datetime(**parts) # type: ignore[arg-type]
|
|
232
|
+
except ValueError:
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _to_epoch_millis(value: Any) -> float | None:
|
|
237
|
+
"""Milliseconds for anything the other runtimes accept: a number or ISO-8601."""
|
|
238
|
+
if isinstance(value, bool) or value is None:
|
|
239
|
+
return None
|
|
240
|
+
if isinstance(value, (int, float)):
|
|
241
|
+
return float(value)
|
|
242
|
+
if not isinstance(value, str):
|
|
243
|
+
return None
|
|
244
|
+
text = value.strip().replace("Z", "+00:00")
|
|
245
|
+
try:
|
|
246
|
+
return datetime.fromisoformat(text).timestamp() * 1000
|
|
247
|
+
except ValueError:
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _now_millis() -> int:
|
|
252
|
+
return int(time.time() * 1000)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _uuid_v7() -> str:
|
|
256
|
+
millis = _now_millis()
|
|
257
|
+
data = bytearray(random.getrandbits(8) for _ in range(16))
|
|
258
|
+
for i in range(6):
|
|
259
|
+
data[i] = (millis >> (8 * (5 - i))) & 0xFF
|
|
260
|
+
data[6] = (data[6] & 0x0F) | 0x70 # version 7
|
|
261
|
+
data[8] = (data[8] & 0x3F) | 0x80 # variant
|
|
262
|
+
return str(uuid_module.UUID(bytes=bytes(data)))
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# ── AST ────────────────────────────────────────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@dataclass
|
|
269
|
+
class _Node:
|
|
270
|
+
kind: str
|
|
271
|
+
value: Any = None
|
|
272
|
+
name: str = ""
|
|
273
|
+
computed: bool = False
|
|
274
|
+
left: "_Node | None" = None
|
|
275
|
+
right: "_Node | None" = None
|
|
276
|
+
third: "_Node | None" = None
|
|
277
|
+
items: list["_Node"] = field(default_factory=list)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# Lowest precedence first. The Python spellings are aliases for the JS operators.
|
|
281
|
+
_BINARY_LEVELS: list[list[str]] = [
|
|
282
|
+
["||", "or"],
|
|
283
|
+
["&&", "and"],
|
|
284
|
+
["===", "!==", "==", "!="],
|
|
285
|
+
["<=", ">=", "<", ">"],
|
|
286
|
+
["+", "-"],
|
|
287
|
+
["*", "/", "%"],
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
_WORD_OPERATORS = {"or", "and", "not"}
|
|
291
|
+
|
|
292
|
+
_IDENTIFIER_START = re.compile(r"[A-Za-z_$]")
|
|
293
|
+
_IDENTIFIER_PART = re.compile(r"[A-Za-z0-9_$]")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class _Parser:
|
|
297
|
+
def __init__(self, source: str) -> None:
|
|
298
|
+
self._source = source
|
|
299
|
+
self._pos = 0
|
|
300
|
+
|
|
301
|
+
def parse(self) -> _Node:
|
|
302
|
+
self._skip_space()
|
|
303
|
+
node = self._parse_expression()
|
|
304
|
+
self._skip_space()
|
|
305
|
+
if self._pos != len(self._source):
|
|
306
|
+
raise ParseError(f"unexpected {self._source[self._pos:]!r}")
|
|
307
|
+
return node
|
|
308
|
+
|
|
309
|
+
# ── Grammar ────────────────────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
def _parse_expression(self) -> _Node:
|
|
312
|
+
return self._parse_conditional()
|
|
313
|
+
|
|
314
|
+
def _parse_conditional(self) -> _Node:
|
|
315
|
+
test = self._parse_binary(0)
|
|
316
|
+
self._skip_space()
|
|
317
|
+
if self._peek() != "?":
|
|
318
|
+
return test
|
|
319
|
+
self._pos += 1
|
|
320
|
+
consequent = self._parse_expression()
|
|
321
|
+
self._skip_space()
|
|
322
|
+
self._expect(":")
|
|
323
|
+
alternate = self._parse_expression()
|
|
324
|
+
return _Node("conditional", left=test, right=consequent, third=alternate)
|
|
325
|
+
|
|
326
|
+
def _parse_binary(self, level: int) -> _Node:
|
|
327
|
+
if level >= len(_BINARY_LEVELS):
|
|
328
|
+
return self._parse_unary()
|
|
329
|
+
|
|
330
|
+
left = self._parse_binary(level + 1)
|
|
331
|
+
while True:
|
|
332
|
+
self._skip_space()
|
|
333
|
+
matched = self._match_operator(_BINARY_LEVELS[level])
|
|
334
|
+
if not matched:
|
|
335
|
+
return left
|
|
336
|
+
right = self._parse_binary(level + 1)
|
|
337
|
+
kind = "logical" if matched in ("&&", "||", "and", "or") else "binary"
|
|
338
|
+
left = _Node(kind, name=matched, left=left, right=right)
|
|
339
|
+
|
|
340
|
+
def _match_operator(self, operators: list[str]) -> str | None:
|
|
341
|
+
for operator in operators:
|
|
342
|
+
if not self._source.startswith(operator, self._pos):
|
|
343
|
+
continue
|
|
344
|
+
end = self._pos + len(operator)
|
|
345
|
+
if operator in _WORD_OPERATORS:
|
|
346
|
+
# "and"/"or" are operators, "android" is an identifier.
|
|
347
|
+
if end < len(self._source) and _IDENTIFIER_PART.match(self._source[end]):
|
|
348
|
+
continue
|
|
349
|
+
self._pos = end
|
|
350
|
+
return operator
|
|
351
|
+
return None
|
|
352
|
+
|
|
353
|
+
def _parse_unary(self) -> _Node:
|
|
354
|
+
self._skip_space()
|
|
355
|
+
char = self._peek()
|
|
356
|
+
if char in ("!", "-", "+") and not self._source.startswith("!=", self._pos):
|
|
357
|
+
self._pos += 1
|
|
358
|
+
return _Node("unary", name=char, left=self._parse_unary())
|
|
359
|
+
if self._match_operator(["not"]):
|
|
360
|
+
return _Node("unary", name="!", left=self._parse_unary())
|
|
361
|
+
return self._parse_postfix()
|
|
362
|
+
|
|
363
|
+
def _parse_postfix(self) -> _Node:
|
|
364
|
+
node = self._parse_primary()
|
|
365
|
+
while True:
|
|
366
|
+
self._skip_space()
|
|
367
|
+
char = self._peek()
|
|
368
|
+
if char == ".":
|
|
369
|
+
self._pos += 1
|
|
370
|
+
node = _Node("member", left=node, name=self._parse_identifier())
|
|
371
|
+
elif char == "[":
|
|
372
|
+
self._pos += 1
|
|
373
|
+
index = self._parse_expression()
|
|
374
|
+
self._skip_space()
|
|
375
|
+
self._expect("]")
|
|
376
|
+
node = _Node("member", left=node, right=index, computed=True)
|
|
377
|
+
elif char == "(":
|
|
378
|
+
self._pos += 1
|
|
379
|
+
node = _Node("call", left=node, items=self._parse_arguments(")"))
|
|
380
|
+
else:
|
|
381
|
+
return node
|
|
382
|
+
|
|
383
|
+
def _parse_arguments(self, closing: str) -> list[_Node]:
|
|
384
|
+
args: list[_Node] = []
|
|
385
|
+
self._skip_space()
|
|
386
|
+
if self._peek() == closing:
|
|
387
|
+
self._pos += 1
|
|
388
|
+
return args
|
|
389
|
+
|
|
390
|
+
while True:
|
|
391
|
+
args.append(self._parse_expression())
|
|
392
|
+
self._skip_space()
|
|
393
|
+
if self._peek() == ",":
|
|
394
|
+
self._pos += 1
|
|
395
|
+
continue
|
|
396
|
+
self._expect(closing)
|
|
397
|
+
return args
|
|
398
|
+
|
|
399
|
+
def _parse_primary(self) -> _Node:
|
|
400
|
+
self._skip_space()
|
|
401
|
+
char = self._peek()
|
|
402
|
+
|
|
403
|
+
if char == "":
|
|
404
|
+
raise ParseError("unexpected end of expression")
|
|
405
|
+
|
|
406
|
+
if char == "(":
|
|
407
|
+
self._pos += 1
|
|
408
|
+
node = self._parse_expression()
|
|
409
|
+
self._skip_space()
|
|
410
|
+
self._expect(")")
|
|
411
|
+
return node
|
|
412
|
+
|
|
413
|
+
if char == "[":
|
|
414
|
+
self._pos += 1
|
|
415
|
+
return _Node("array", items=self._parse_arguments("]"))
|
|
416
|
+
|
|
417
|
+
if char in ("'", '"'):
|
|
418
|
+
return _Node("literal", value=self._parse_string(char))
|
|
419
|
+
|
|
420
|
+
if char.isdigit() or (char == "." and self._peek(1).isdigit()):
|
|
421
|
+
return _Node("literal", value=self._parse_number())
|
|
422
|
+
|
|
423
|
+
name = self._parse_identifier()
|
|
424
|
+
if name in ("true", "True"):
|
|
425
|
+
return _Node("literal", value=True)
|
|
426
|
+
if name in ("false", "False"):
|
|
427
|
+
return _Node("literal", value=False)
|
|
428
|
+
if name in ("null", "undefined", "None"):
|
|
429
|
+
return _Node("literal", value=None)
|
|
430
|
+
return _Node("identifier", name=name)
|
|
431
|
+
|
|
432
|
+
def _parse_string(self, quote_char: str) -> str:
|
|
433
|
+
self._pos += 1 # opening quote
|
|
434
|
+
text = ""
|
|
435
|
+
escapes = {"n": "\n", "t": "\t", "r": "\r"}
|
|
436
|
+
while self._pos < len(self._source) and self._source[self._pos] != quote_char:
|
|
437
|
+
char = self._source[self._pos]
|
|
438
|
+
if char == "\\" and self._pos + 1 < len(self._source):
|
|
439
|
+
self._pos += 1
|
|
440
|
+
char = escapes.get(self._source[self._pos], self._source[self._pos])
|
|
441
|
+
text += char
|
|
442
|
+
self._pos += 1
|
|
443
|
+
if self._pos >= len(self._source):
|
|
444
|
+
raise ParseError("unterminated string literal")
|
|
445
|
+
self._pos += 1 # closing quote
|
|
446
|
+
return text
|
|
447
|
+
|
|
448
|
+
def _parse_number(self) -> Any:
|
|
449
|
+
start = self._pos
|
|
450
|
+
while self._pos < len(self._source):
|
|
451
|
+
char = self._source[self._pos]
|
|
452
|
+
previous = self._source[self._pos - 1] if self._pos > start else ""
|
|
453
|
+
if char.isdigit() or char in (".", "e", "E"):
|
|
454
|
+
self._pos += 1
|
|
455
|
+
elif char in ("+", "-") and previous in ("e", "E"):
|
|
456
|
+
self._pos += 1
|
|
457
|
+
else:
|
|
458
|
+
break
|
|
459
|
+
|
|
460
|
+
text = self._source[start : self._pos]
|
|
461
|
+
try:
|
|
462
|
+
return _number(float(text))
|
|
463
|
+
except ValueError as error:
|
|
464
|
+
raise ParseError(f"invalid number {text!r}") from error
|
|
465
|
+
|
|
466
|
+
def _parse_identifier(self) -> str:
|
|
467
|
+
self._skip_space()
|
|
468
|
+
start = self._pos
|
|
469
|
+
if self._pos < len(self._source) and _IDENTIFIER_START.match(self._source[self._pos]):
|
|
470
|
+
self._pos += 1
|
|
471
|
+
while self._pos < len(self._source) and _IDENTIFIER_PART.match(
|
|
472
|
+
self._source[self._pos]
|
|
473
|
+
):
|
|
474
|
+
self._pos += 1
|
|
475
|
+
if self._pos == start:
|
|
476
|
+
raise ParseError(f"expected an identifier at position {start}")
|
|
477
|
+
return self._source[start : self._pos]
|
|
478
|
+
|
|
479
|
+
# ── Scanning ───────────────────────────────────────────────────────────────
|
|
480
|
+
|
|
481
|
+
def _skip_space(self) -> None:
|
|
482
|
+
while self._pos < len(self._source) and self._source[self._pos].isspace():
|
|
483
|
+
self._pos += 1
|
|
484
|
+
|
|
485
|
+
def _expect(self, char: str) -> None:
|
|
486
|
+
if self._peek() != char:
|
|
487
|
+
raise ParseError(f"expected {char!r}")
|
|
488
|
+
self._pos += 1
|
|
489
|
+
|
|
490
|
+
def _peek(self, offset: int = 0) -> str:
|
|
491
|
+
index = self._pos + offset
|
|
492
|
+
return self._source[index] if index < len(self._source) else ""
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def _callee_name(node: _Node | None) -> str | None:
|
|
496
|
+
"""Flattens an identifier/member chain into a dotted name, so ``uuid.v4`` can
|
|
497
|
+
be looked up in the builtin table. Returns None for anything else."""
|
|
498
|
+
if node is None:
|
|
499
|
+
return None
|
|
500
|
+
if node.kind == "identifier":
|
|
501
|
+
return node.name
|
|
502
|
+
if node.kind == "member" and not node.computed:
|
|
503
|
+
prefix = _callee_name(node.left)
|
|
504
|
+
return None if prefix is None else f"{prefix}.{node.name}"
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
# Told the path of a read that found nothing — ``params.titel`` — during the
|
|
509
|
+
# evaluation that asked for it, and only that one: a find/filter predicate
|
|
510
|
+
# evaluated inside it reports nothing, as in the other runtimes.
|
|
511
|
+
_on_missing: ContextVar[Callable[[str], None] | None] = ContextVar("_on_missing", default=None)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _path_of(node: _Node | None) -> str:
|
|
515
|
+
"""How a read is named when it is reported: ``params.items[0].title``."""
|
|
516
|
+
if node is None:
|
|
517
|
+
return "…"
|
|
518
|
+
if node.kind == "identifier":
|
|
519
|
+
return node.name
|
|
520
|
+
if node.kind == "member":
|
|
521
|
+
obj = _path_of(node.left)
|
|
522
|
+
if not node.computed:
|
|
523
|
+
return f"{obj}.{node.name}"
|
|
524
|
+
if node.right is not None and node.right.kind == "literal":
|
|
525
|
+
return f"{obj}[{json_module.dumps(node.right.value)}]"
|
|
526
|
+
return f"{obj}[…]"
|
|
527
|
+
return "…"
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
# What a member read returns when nothing is there, told apart from a None that
|
|
531
|
+
# is.
|
|
532
|
+
_MISSING = object()
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _member(obj: Any, key: Any) -> Any:
|
|
536
|
+
"""What ``obj[key]`` finds, or ``_MISSING`` when nothing is there.
|
|
537
|
+
|
|
538
|
+
One set of rules for every container, so what a read finds and whether it
|
|
539
|
+
found anything cannot disagree: a list, tuple or string is a sequence — its
|
|
540
|
+
``length``, or the item at an index within it — and a dict is read by the
|
|
541
|
+
key as text.
|
|
542
|
+
"""
|
|
543
|
+
if isinstance(obj, (list, tuple, str)):
|
|
544
|
+
if key == "length":
|
|
545
|
+
return len(obj)
|
|
546
|
+
index = to_number(key)
|
|
547
|
+
if math.isnan(index) or not 0 <= index < len(obj):
|
|
548
|
+
return _MISSING
|
|
549
|
+
return obj[int(index)]
|
|
550
|
+
if isinstance(obj, dict):
|
|
551
|
+
return obj.get(key if isinstance(key, str) else to_text(key), _MISSING)
|
|
552
|
+
return _MISSING
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def _read_member(node: _Node, scope: dict[str, Any]) -> tuple[Any, bool]:
|
|
556
|
+
"""A member read's value, and whether it found something there.
|
|
557
|
+
|
|
558
|
+
A read past a link that came up empty was reported with it; one past a link
|
|
559
|
+
that is there but None is reported itself. Only a member read can tell the
|
|
560
|
+
two apart, which is why the chain is walked here rather than through
|
|
561
|
+
``_evaluate_node``.
|
|
562
|
+
"""
|
|
563
|
+
if node.left is not None and node.left.kind == "member":
|
|
564
|
+
obj, obj_found = _read_member(node.left, scope)
|
|
565
|
+
else:
|
|
566
|
+
obj, obj_found = _evaluate_node(node.left, scope), True
|
|
567
|
+
key = _evaluate_node(node.right, scope) if node.computed else node.name
|
|
568
|
+
value = _member(obj, key)
|
|
569
|
+
if value is not _MISSING:
|
|
570
|
+
return value, True
|
|
571
|
+
on_missing = _on_missing.get()
|
|
572
|
+
if on_missing is not None and obj_found:
|
|
573
|
+
on_missing(_path_of(node))
|
|
574
|
+
return None, False
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def _loose_equals(left: Any, right: Any) -> bool:
|
|
578
|
+
number_types = (int, float)
|
|
579
|
+
if isinstance(left, bool) or isinstance(right, bool):
|
|
580
|
+
return left is right
|
|
581
|
+
if isinstance(left, number_types) and isinstance(right, number_types):
|
|
582
|
+
return float(left) == float(right)
|
|
583
|
+
if isinstance(left, number_types) and isinstance(right, str):
|
|
584
|
+
other = to_number(right)
|
|
585
|
+
return not math.isnan(other) and float(left) == other
|
|
586
|
+
if isinstance(left, str) and isinstance(right, number_types):
|
|
587
|
+
other = to_number(left)
|
|
588
|
+
return not math.isnan(other) and other == float(right)
|
|
589
|
+
return left == right
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def _apply_binary(operator: str, left: Any, right: Any) -> Any:
|
|
593
|
+
if operator == "+":
|
|
594
|
+
if isinstance(left, str) or isinstance(right, str):
|
|
595
|
+
return to_text(left) + to_text(right)
|
|
596
|
+
return _number(to_number(left) + to_number(right))
|
|
597
|
+
if operator == "-":
|
|
598
|
+
return _number(to_number(left) - to_number(right))
|
|
599
|
+
if operator == "*":
|
|
600
|
+
return _number(to_number(left) * to_number(right))
|
|
601
|
+
if operator == "/":
|
|
602
|
+
divisor = to_number(right)
|
|
603
|
+
if divisor == 0:
|
|
604
|
+
return None # JS yields Infinity/NaN, neither of which is JSON
|
|
605
|
+
return _number(to_number(left) / divisor)
|
|
606
|
+
if operator == "%":
|
|
607
|
+
divisor = to_number(right)
|
|
608
|
+
if divisor == 0:
|
|
609
|
+
return None
|
|
610
|
+
return _number(math.fmod(to_number(left), divisor))
|
|
611
|
+
|
|
612
|
+
if operator == "==":
|
|
613
|
+
return _loose_equals(left, right)
|
|
614
|
+
if operator == "!=":
|
|
615
|
+
return not _loose_equals(left, right)
|
|
616
|
+
if operator == "===":
|
|
617
|
+
return type(left) is type(right) and left == right
|
|
618
|
+
if operator == "!==":
|
|
619
|
+
return not (type(left) is type(right) and left == right)
|
|
620
|
+
|
|
621
|
+
if isinstance(left, str) and isinstance(right, str):
|
|
622
|
+
values: tuple[Any, Any] = (left, right)
|
|
623
|
+
else:
|
|
624
|
+
values = (to_number(left), to_number(right))
|
|
625
|
+
if any(isinstance(value, float) and math.isnan(value) for value in values):
|
|
626
|
+
return False
|
|
627
|
+
|
|
628
|
+
if operator == "<":
|
|
629
|
+
return values[0] < values[1]
|
|
630
|
+
if operator == "<=":
|
|
631
|
+
return values[0] <= values[1]
|
|
632
|
+
if operator == ">":
|
|
633
|
+
return values[0] > values[1]
|
|
634
|
+
if operator == ">=":
|
|
635
|
+
return values[0] >= values[1]
|
|
636
|
+
|
|
637
|
+
raise EvaluationError(f"unknown operator {operator!r}")
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def _evaluate_node(node: _Node | None, scope: dict[str, Any]) -> Any:
|
|
641
|
+
if node is None:
|
|
642
|
+
return None
|
|
643
|
+
|
|
644
|
+
if node.kind == "literal":
|
|
645
|
+
return node.value
|
|
646
|
+
|
|
647
|
+
if node.kind == "identifier":
|
|
648
|
+
if node.name in scope:
|
|
649
|
+
return scope[node.name]
|
|
650
|
+
if node.name in _builtins():
|
|
651
|
+
raise EvaluationError(f"{node.name!r} must be called")
|
|
652
|
+
return None
|
|
653
|
+
|
|
654
|
+
if node.kind == "member":
|
|
655
|
+
return _read_member(node, scope)[0]
|
|
656
|
+
|
|
657
|
+
if node.kind == "array":
|
|
658
|
+
return [_evaluate_node(item, scope) for item in node.items]
|
|
659
|
+
|
|
660
|
+
if node.kind == "call":
|
|
661
|
+
name = _callee_name(node.left)
|
|
662
|
+
if name is None:
|
|
663
|
+
raise EvaluationError("only builtin functions can be called")
|
|
664
|
+
builtin = _builtins().get(name)
|
|
665
|
+
if builtin is None:
|
|
666
|
+
raise EvaluationError(f"unknown function {name!r}")
|
|
667
|
+
return builtin([_evaluate_node(item, scope) for item in node.items])
|
|
668
|
+
|
|
669
|
+
if node.kind == "unary":
|
|
670
|
+
operand = _evaluate_node(node.left, scope)
|
|
671
|
+
if node.name == "!":
|
|
672
|
+
return not is_truthy(operand)
|
|
673
|
+
if node.name == "-":
|
|
674
|
+
return _number(-to_number(operand))
|
|
675
|
+
return _number(to_number(operand))
|
|
676
|
+
|
|
677
|
+
if node.kind == "logical":
|
|
678
|
+
left = _evaluate_node(node.left, scope)
|
|
679
|
+
if node.name in ("&&", "and"):
|
|
680
|
+
return _evaluate_node(node.right, scope) if is_truthy(left) else left
|
|
681
|
+
return left if is_truthy(left) else _evaluate_node(node.right, scope)
|
|
682
|
+
|
|
683
|
+
if node.kind == "binary":
|
|
684
|
+
return _apply_binary(
|
|
685
|
+
node.name,
|
|
686
|
+
_evaluate_node(node.left, scope),
|
|
687
|
+
_evaluate_node(node.right, scope),
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
if node.kind == "conditional":
|
|
691
|
+
return (
|
|
692
|
+
_evaluate_node(node.right, scope)
|
|
693
|
+
if is_truthy(_evaluate_node(node.left, scope))
|
|
694
|
+
else _evaluate_node(node.third, scope)
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
raise EvaluationError(f"unknown node {node.kind!r}")
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
class Expression:
|
|
701
|
+
"""A parsed expression. Parsing happens once — when a service is configured —
|
|
702
|
+
so evaluating per input costs only the tree walk."""
|
|
703
|
+
|
|
704
|
+
def __init__(self, source: str, root: _Node) -> None:
|
|
705
|
+
self.source = source
|
|
706
|
+
self._root = root
|
|
707
|
+
|
|
708
|
+
@staticmethod
|
|
709
|
+
def parse(source: str) -> "Expression":
|
|
710
|
+
return Expression(source, _Parser(source).parse())
|
|
711
|
+
|
|
712
|
+
def evaluate(
|
|
713
|
+
self,
|
|
714
|
+
params: Any = None,
|
|
715
|
+
scope: dict[str, Any] | None = None,
|
|
716
|
+
on_missing: Callable[[str], None] | None = None,
|
|
717
|
+
) -> Any:
|
|
718
|
+
"""``on_missing`` hears about every read that found nothing, once per
|
|
719
|
+
chain: in ``params.a.b.c`` with no ``a``, only ``params.a``."""
|
|
720
|
+
token = _on_missing.set(on_missing)
|
|
721
|
+
try:
|
|
722
|
+
return _evaluate_node(self._root, scope if scope is not None else {"params": params})
|
|
723
|
+
finally:
|
|
724
|
+
_on_missing.reset(token)
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
# ── Builtins ───────────────────────────────────────────────────────────────────
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
def _arg(args: list[Any], index: int, default: Any = None) -> Any:
|
|
731
|
+
return args[index] if index < len(args) else default
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def _matches(predicate: Expression, item: Any, index: int) -> bool:
|
|
735
|
+
return is_truthy(predicate.evaluate(scope={"item": item, "index": index}))
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
def _find(args: list[Any]) -> Any:
|
|
739
|
+
array, source = _arg(args, 0), _arg(args, 1)
|
|
740
|
+
if not isinstance(array, list) or not isinstance(source, str):
|
|
741
|
+
return None
|
|
742
|
+
predicate = Expression.parse(source)
|
|
743
|
+
for index, item in enumerate(array):
|
|
744
|
+
if _matches(predicate, item, index):
|
|
745
|
+
return item
|
|
746
|
+
return None
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
def _filter(args: list[Any]) -> Any:
|
|
750
|
+
array, source = _arg(args, 0), _arg(args, 1)
|
|
751
|
+
if not isinstance(array, list) or not isinstance(source, str):
|
|
752
|
+
return []
|
|
753
|
+
predicate = Expression.parse(source)
|
|
754
|
+
return [item for index, item in enumerate(array) if _matches(predicate, item, index)]
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def _slice(args: list[Any]) -> Any:
|
|
758
|
+
array = _arg(args, 0)
|
|
759
|
+
if not isinstance(array, list):
|
|
760
|
+
return []
|
|
761
|
+
offset = int(to_number(_arg(args, 1, 0)) or 0)
|
|
762
|
+
raw_step = to_number(_arg(args, 2, 1))
|
|
763
|
+
step = 1 if math.isnan(raw_step) or raw_step < 1 else int(raw_step)
|
|
764
|
+
raw_end = to_number(_arg(args, 3, len(array)))
|
|
765
|
+
end = len(array) if math.isnan(raw_end) else int(raw_end)
|
|
766
|
+
return array[max(0, offset) : end : step]
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
# A web address in running text, with whatever label introduced it. The label
|
|
770
|
+
# goes because it is only there to introduce the address: "Article URL:" with
|
|
771
|
+
# nothing after it says less than nothing. Bounded to a short run of words so it
|
|
772
|
+
# cannot swallow a sentence.
|
|
773
|
+
_URL_IN_TEXT = re.compile(
|
|
774
|
+
r"(?:[A-Za-z][\w ]{0,30}:[ \t]*)?(?:https?://|www\.)\S+", re.IGNORECASE
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def _without_urls(value: Any) -> str:
|
|
779
|
+
"""Text with the web addresses taken out of it.
|
|
780
|
+
|
|
781
|
+
Feeds are written for programs as much as for people, and some of them are
|
|
782
|
+
mostly machinery: a summary reading "Article URL: https://… Comments URL:
|
|
783
|
+
https://… Points: 48" is a fine thing for a reader to show as a link and a
|
|
784
|
+
miserable thing to hear read aloud, where an address becomes a minute of
|
|
785
|
+
punctuation. This is for text on its way to something that speaks or
|
|
786
|
+
summarises it — never for what a board stores or publishes, where the
|
|
787
|
+
address is the useful part.
|
|
788
|
+
"""
|
|
789
|
+
# An absent value is empty text, not the word "null": this answers with
|
|
790
|
+
# text, and a board reading a field that is not there wants nothing said.
|
|
791
|
+
text = _URL_IN_TEXT.sub(" ", "" if value is None else to_text(value))
|
|
792
|
+
# What removal leaves behind: doubled spaces, and punctuation adrift from
|
|
793
|
+
# the word it belonged to.
|
|
794
|
+
text = re.sub(r"[ \t]{2,}", " ", text)
|
|
795
|
+
return re.sub(r"\s+([.,;:!?])", r"\1", text).strip()
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def _parse_json(args: list[Any]) -> Any:
|
|
799
|
+
value = _arg(args, 0)
|
|
800
|
+
if not isinstance(value, str):
|
|
801
|
+
return "<parse undefined>"
|
|
802
|
+
try:
|
|
803
|
+
return json_module.loads(value)
|
|
804
|
+
except ValueError:
|
|
805
|
+
return None
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _reformat_date(args: list[Any]) -> Any:
|
|
809
|
+
text = to_text(_arg(args, 0))
|
|
810
|
+
parsed = _parse_date(text, to_text(_arg(args, 1)))
|
|
811
|
+
if parsed is None:
|
|
812
|
+
millis = _to_epoch_millis(_arg(args, 0))
|
|
813
|
+
if millis is None:
|
|
814
|
+
return text
|
|
815
|
+
parsed = datetime.fromtimestamp(millis / 1000)
|
|
816
|
+
return _format_date(parsed, to_text(_arg(args, 2)))
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def _numeric_list(args: list[Any]) -> list[float] | None:
|
|
820
|
+
array = _arg(args, 0)
|
|
821
|
+
return [to_number(entry) for entry in array] if isinstance(array, list) else None
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
def _sum(args: list[Any]) -> Any:
|
|
825
|
+
values = _numeric_list(args)
|
|
826
|
+
return _arg(args, 0) if values is None else _number(math.fsum(values))
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def _avg(args: list[Any]) -> Any:
|
|
830
|
+
values = _numeric_list(args)
|
|
831
|
+
if values is None:
|
|
832
|
+
return _arg(args, 0)
|
|
833
|
+
return None if not values else _number(math.fsum(values) / len(values))
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
def _flat_sum(args: list[Any]) -> Any:
|
|
837
|
+
array = _arg(args, 0)
|
|
838
|
+
if not isinstance(array, list):
|
|
839
|
+
return 0
|
|
840
|
+
total = 0.0
|
|
841
|
+
for entry in array:
|
|
842
|
+
if isinstance(entry, list):
|
|
843
|
+
total += math.fsum(to_number(inner) for inner in entry)
|
|
844
|
+
else:
|
|
845
|
+
total += to_number(entry)
|
|
846
|
+
return _number(total)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def _at(args: list[Any]) -> Any:
|
|
850
|
+
array = _arg(args, 0)
|
|
851
|
+
if not isinstance(array, list) or not array:
|
|
852
|
+
return None
|
|
853
|
+
return array[int(abs(round(to_number(_arg(args, 1, 0))))) % len(array)]
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def _join(args: list[Any]) -> str:
|
|
857
|
+
"""An array's elements as one text; an empty element is nothing, as Array.join has it."""
|
|
858
|
+
array = _arg(args, 0)
|
|
859
|
+
separator = _arg(args, 1)
|
|
860
|
+
separator = "" if separator is None else to_text(separator)
|
|
861
|
+
if isinstance(array, list):
|
|
862
|
+
return separator.join("" if item is None else to_text(item) for item in array)
|
|
863
|
+
return "" if array is None else to_text(array)
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
_BUILTINS: dict[str, Callable[[list[Any]], Any]] | None = None
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def _builtins() -> dict[str, Callable[[list[Any]], Any]]:
|
|
870
|
+
global _BUILTINS
|
|
871
|
+
if _BUILTINS is not None:
|
|
872
|
+
return _BUILTINS
|
|
873
|
+
|
|
874
|
+
_BUILTINS = {
|
|
875
|
+
"print": lambda args: print(to_text(_arg(args, 0))),
|
|
876
|
+
"log": lambda args: print(to_text(_arg(args, 0))),
|
|
877
|
+
"round": lambda args: _number(float(round(to_number(_arg(args, 0))))),
|
|
878
|
+
"sin": lambda args: math.sin(to_number(_arg(args, 0))),
|
|
879
|
+
"min": lambda args: _number(min((to_number(a) for a in args), default=math.nan)),
|
|
880
|
+
"max": lambda args: _number(max((to_number(a) for a in args), default=math.nan)),
|
|
881
|
+
"rand": lambda args: random.random(),
|
|
882
|
+
"number": lambda args: _number(to_number(_arg(args, 0))),
|
|
883
|
+
"string": lambda args: to_text(_arg(args, 0)),
|
|
884
|
+
"stringify": lambda args: json_module.dumps(_arg(args, 0), separators=(",", ":")),
|
|
885
|
+
"parse": _parse_json,
|
|
886
|
+
"concat": lambda args: "".join(to_text(a) for a in args),
|
|
887
|
+
"join": _join,
|
|
888
|
+
"encodeURI": lambda args: quote(to_text(_arg(args, 0)), safe="-_.!~*'();/?:@&=+$,#"),
|
|
889
|
+
"slug": lambda args: re.sub(r"[^a-z0-9_-]", "", to_text(_arg(args, 0)).lower()),
|
|
890
|
+
"withoutUrls": lambda args: _without_urls(_arg(args, 0)),
|
|
891
|
+
"now": lambda args: _now_millis(),
|
|
892
|
+
"range": lambda args: list(range(max(0, int(round(to_number(_arg(args, 0, 0))))))),
|
|
893
|
+
"sum": _sum,
|
|
894
|
+
"avg": _avg,
|
|
895
|
+
"flatSum": _flat_sum,
|
|
896
|
+
"at": _at,
|
|
897
|
+
"slice": _slice,
|
|
898
|
+
"find": _find,
|
|
899
|
+
"filter": _filter,
|
|
900
|
+
"isFuture": lambda args: (lambda ms: ms is not None and ms >= _now_millis())(
|
|
901
|
+
_to_epoch_millis(_arg(args, 0))
|
|
902
|
+
),
|
|
903
|
+
"isPast": lambda args: (lambda ms: ms is not None and ms < _now_millis())(
|
|
904
|
+
_to_epoch_millis(_arg(args, 0))
|
|
905
|
+
),
|
|
906
|
+
"formatNow": lambda args: _format_date(datetime.now(), to_text(_arg(args, 0))),
|
|
907
|
+
"reformatDate": _reformat_date,
|
|
908
|
+
"uuid.v4": lambda args: str(uuid_module.uuid4()),
|
|
909
|
+
"uuid.v7": lambda args: _uuid_v7(),
|
|
910
|
+
# Python conversions the previous evaluator exposed, kept so flat
|
|
911
|
+
# templates written against it keep working.
|
|
912
|
+
"len": lambda args: len(_arg(args, 0)) if hasattr(_arg(args, 0), "__len__") else None,
|
|
913
|
+
"str": lambda args: to_text(_arg(args, 0)),
|
|
914
|
+
"int": lambda args: int(to_number(_arg(args, 0))),
|
|
915
|
+
"float": lambda args: to_number(_arg(args, 0)),
|
|
916
|
+
"bool": lambda args: is_truthy(_arg(args, 0)),
|
|
917
|
+
"abs": lambda args: _number(abs(to_number(_arg(args, 0)))),
|
|
918
|
+
"list": lambda args: list(_arg(args, 0)) if isinstance(_arg(args, 0), (list, tuple)) else [],
|
|
919
|
+
}
|
|
920
|
+
return _BUILTINS
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def compile_expression(source: Any) -> Callable[[Any], Any]:
|
|
924
|
+
"""Compiles an expression source into a callable taking ``params`` and an
|
|
925
|
+
optional ``on_missing``. Non-string sources are constants and are returned
|
|
926
|
+
as-is; a source that does not parse raises when called and carries
|
|
927
|
+
``parse_error``, so the caller decides how a broken term is reported."""
|
|
928
|
+
if not isinstance(source, str):
|
|
929
|
+
return lambda params, on_missing=None: source
|
|
930
|
+
|
|
931
|
+
try:
|
|
932
|
+
expression = Expression.parse(source)
|
|
933
|
+
except ParseError as error:
|
|
934
|
+
parse_error = f"invalid expression {source!r}: {error}"
|
|
935
|
+
|
|
936
|
+
def broken(params: Any, on_missing: Any = None) -> Any:
|
|
937
|
+
raise EvaluationError(parse_error)
|
|
938
|
+
|
|
939
|
+
broken.parse_error = parse_error # type: ignore[attr-defined]
|
|
940
|
+
return broken
|
|
941
|
+
|
|
942
|
+
return lambda params, on_missing=None: expression.evaluate(params, on_missing=on_missing)
|