@hraness/ghostget 0.17.2
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/CHANGELOG.md +458 -0
- package/LICENSE +21 -0
- package/README.md +1359 -0
- package/bunfig.toml +2 -0
- package/dist/apple-photos-client.js +704 -0
- package/dist/beeper-client.js +4602 -0
- package/dist/client.js +1605 -0
- package/dist/index-26yq8q16.js +31 -0
- package/dist/index-9t13b4k7.js +508 -0
- package/dist/index-axwm9b3v.js +5 -0
- package/dist/index-gwk7rbyj.js +30 -0
- package/dist/index.js +701 -0
- package/dist/messaging.js +1130 -0
- package/dist/omni-client.js +1406 -0
- package/dist/whatsapp-client.js +355 -0
- package/docs/imessage-direct-provider.md +106 -0
- package/docs/rental-listings.md +94 -0
- package/package.json +600 -0
- package/skills/ghostget/SKILL.md +251 -0
- package/skills/ghostget/agents/openai.yaml +4 -0
- package/skills/ghostget/references/adapter-contract.md +181 -0
- package/skills/ghostget/references/apple-photos.md +115 -0
- package/skills/ghostget/references/article-drafts.md +235 -0
- package/skills/ghostget/references/code-owned-provider-scaffold.md +134 -0
- package/skills/ghostget/references/cross-posting-video.md +139 -0
- package/skills/ghostget/references/cross-posting.md +82 -0
- package/skills/ghostget/references/derivation.md +390 -0
- package/skills/ghostget/references/hraness-social-profile-stats.json +269 -0
- package/skills/ghostget/references/install.md +66 -0
- package/skills/ghostget/references/linkedin-adapter.md +442 -0
- package/skills/ghostget/references/messaging.md +168 -0
- package/skills/ghostget/references/meta-comet-contract.md +153 -0
- package/skills/ghostget/references/platform-patterns.md +122 -0
- package/skills/ghostget/references/provider-plugins.md +496 -0
- package/skills/ghostget/references/reddit-flair.md +78 -0
- package/skills/ghostget/references/safety-and-state.md +214 -0
- package/skills/ghostget/references/settlement-and-duplicate-cleanup.md +79 -0
- package/skills/ghostget/references/social-platform-routing.md +66 -0
- package/skills/ghostget/references/social-profile-stats.md +195 -0
- package/skills/ghostget/references/social-video-platform-routing.md +89 -0
- package/skills/ghostget/references/whatsapp-adapter.md +101 -0
- package/skills/ghostget/references/x-adapter.md +332 -0
- package/skills/ghostget/references/x-ai-disclosure.md +71 -0
- package/src/apple-photos-cli.ts +639 -0
- package/src/apple-photos-client-types.ts +168 -0
- package/src/apple-photos-client.ts +201 -0
- package/src/apple-photos-contact-evidence.ts +838 -0
- package/src/apple-photos-local-source.ts +1131 -0
- package/src/args.ts +2053 -0
- package/src/article-draft-document.ts +417 -0
- package/src/article-draft-embeds.ts +123 -0
- package/src/article-draft-images.ts +214 -0
- package/src/assets/adapter-template/wrench-adapter.json +40 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.json +1500 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.0.0.json +129 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v1.1.0.json +207 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.0.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.1.0.json +1463 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.2.0.json +1475 -0
- package/src/assets/adapters/beeper/wrench-web-adapter.v2.3.0.json +1488 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.0.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.1.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.2.0.json +581 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.3.0.json +615 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.4.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.5.0.json +684 -0
- package/src/assets/adapters/bluesky/wrench-web-adapter.v1.6.0.json +684 -0
- package/src/assets/adapters/clasificados/wrench-web-adapter.json +54 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.json +750 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.0.0.json +706 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.1.0.json +703 -0
- package/src/assets/adapters/facebook/wrench-web-adapter.v1.2.0.json +750 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.json +682 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.0.0.json +583 -0
- package/src/assets/adapters/facebook-group/wrench-web-adapter.v1.1.0.json +682 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.0.0.json +383 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.0.json +437 -0
- package/src/assets/adapters/facebook-marketplace/wrench-web-adapter.v1.1.1.json +437 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.json +880 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.0.0.json +740 -0
- package/src/assets/adapters/facebook-page/wrench-web-adapter.v1.1.0.json +880 -0
- package/src/assets/adapters/github/wrench-web-adapter.json +69 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/github/wrench-web-adapter.v1.1.0.json +69 -0
- package/src/assets/adapters/gmail/wrench-adapter.json +161 -0
- package/src/assets/adapters/gmail/wrench-adapter.v1.2.0.json +157 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.json +315 -0
- package/src/assets/adapters/hacker-news/wrench-web-adapter.v1.0.0.json +315 -0
- package/src/assets/adapters/imessage/wrench-web-adapter.json +236 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.0.0.json +627 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.1.0.json +609 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.2.0.json +640 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.3.0.json +671 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.4.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.5.0.json +710 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.6.0.json +719 -0
- package/src/assets/adapters/instagram/wrench-web-adapter.v1.7.0.json +719 -0
- package/src/assets/adapters/linkedin/wrench-adapter.json +442 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v0.4.0.json +124 -0
- package/src/assets/adapters/linkedin/wrench-adapter.v1.0.0.json +411 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.json +914 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.0.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.10.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.11.0.json +809 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.12.0.json +820 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.13.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.14.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.15.0.json +819 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.16.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.17.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.18.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.19.0.json +874 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.2.0.json +731 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.3.0.json +768 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.4.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.5.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.6.0.json +808 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.7.0.json +825 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.8.0.json +792 -0
- package/src/assets/adapters/linkedin/wrench-web-adapter.v1.9.0.json +809 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.json +855 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.0.0.json +577 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.1.0.json +604 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.10.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.2.0.json +653 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.3.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.4.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.5.0.json +712 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.6.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.7.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.8.0.json +713 -0
- package/src/assets/adapters/reddit/wrench-web-adapter.v1.9.0.json +713 -0
- package/src/assets/adapters/substack/wrench-web-adapter.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.0.0.json +817 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.1.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.2.0.json +812 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.3.0.json +902 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.4.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.5.0.json +936 -0
- package/src/assets/adapters/substack/wrench-web-adapter.v1.6.0.json +936 -0
- package/src/assets/adapters/threads/wrench-web-adapter.json +692 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.0.0.json +629 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.1.0.json +623 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.2.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.3.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.4.0.json +622 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.5.0.json +699 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.6.0.json +693 -0
- package/src/assets/adapters/threads/wrench-web-adapter.v1.7.0.json +692 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.0.0.json +681 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.1.0.json +708 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.2.0.json +762 -0
- package/src/assets/adapters/tiktok/wrench-web-adapter.v1.3.0.json +762 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.json +42 -0
- package/src/assets/adapters/twitch/wrench-web-adapter.v1.0.0.json +42 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.json +400 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.0.0.json +355 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.1.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.2.0.json +386 -0
- package/src/assets/adapters/whatsapp/wrench-web-adapter.v1.3.0.json +400 -0
- package/src/assets/adapters/x/wrench-adapter.json +749 -0
- package/src/assets/adapters/x/wrench-adapter.v1.0.0.json +700 -0
- package/src/assets/adapters/x/wrench-adapter.v1.1.0.json +707 -0
- package/src/assets/adapters/x/wrench-web-adapter.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.1.0.json +579 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.10.0.json +685 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.11.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.12.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.13.0.json +712 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.2.0.json +578 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.3.0.json +610 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.4.0.json +617 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.5.0.json +634 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.6.0.json +632 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.7.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.8.0.json +649 -0
- package/src/assets/adapters/x/wrench-web-adapter.v1.9.0.json +683 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.0.0.json +465 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.1.0.json +519 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.2.0.json +561 -0
- package/src/assets/adapters/youtube/wrench-web-adapter.v1.3.0.json +561 -0
- package/src/assets/code-owned-provider-template/plugin.test.ts.template +54 -0
- package/src/assets/code-owned-provider-template/plugin.ts.template +102 -0
- package/src/assets/code-owned-provider-template/runtime.internal.test.ts.template +44 -0
- package/src/assets/code-owned-provider-template/runtime.ts.template +59 -0
- package/src/assets/code-owned-provider-template/wrench-adapter.json.template +33 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +14 -0
- package/src/assets.d.ts +4 -0
- package/src/auth.ts +1157 -0
- package/src/beeper-client-types.ts +169 -0
- package/src/beeper-client.ts +250 -0
- package/src/beeper-contact-interactions-cli.ts +180 -0
- package/src/beeper-contact-interactions.ts +1612 -0
- package/src/beeper-message-bundle-v1.ts +257 -0
- package/src/beeper-message-like-me-cli.ts +84 -0
- package/src/beeper-message-like-me-export.ts +2274 -0
- package/src/beeper-message-like-me-recovery.ts +931 -0
- package/src/beeper-message-like-me-source.ts +4034 -0
- package/src/browser-admission.ts +928 -0
- package/src/browser-snapshots.ts +260 -0
- package/src/browser.ts +3873 -0
- package/src/canonical-json.ts +60 -0
- package/src/catalog-cli.ts +638 -0
- package/src/cli.ts +208 -0
- package/src/client-types.ts +301 -0
- package/src/client.ts +2726 -0
- package/src/confirmed-write-failure.ts +44 -0
- package/src/confirmed-write-model.ts +72 -0
- package/src/confirmed-write-platform.ts +1055 -0
- package/src/confirmed-write-program.ts +224 -0
- package/src/confirmed-write-runtime.ts +16 -0
- package/src/cursor-token.ts +228 -0
- package/src/derivation-dnr-readiness.ts +367 -0
- package/src/derivation-file-chooser.ts +384 -0
- package/src/derivation-network-boundary.ts +835 -0
- package/src/derivation-network-guard.ts +782 -0
- package/src/derivation-network-proxy-helper.ts +440 -0
- package/src/derivation-network-proxy.ts +385 -0
- package/src/derive-command-helper.ts +693 -0
- package/src/derive-fixtures.ts +343 -0
- package/src/derive-review.ts +980 -0
- package/src/derive.ts +4754 -0
- package/src/fixtures/beeper-message-like-me-v1/accounts.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/conversations.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/manifest.json +1 -0
- package/src/fixtures/beeper-message-like-me-v1/messages.ndjson +2 -0
- package/src/fixtures/beeper-message-like-me-v1/participants.ndjson +3 -0
- package/src/fixtures/beeper-message-like-me-v1/reactions.ndjson +1 -0
- package/src/fixtures/beeper-message-like-me-v1/tombstones.ndjson +1 -0
- package/src/ghostget.ts +3672 -0
- package/src/gmail-capture.ts +1244 -0
- package/src/har-internal.ts +1242 -0
- package/src/har.ts +743 -0
- package/src/index.ts +218 -0
- package/src/invocation-read-platform.ts +55 -0
- package/src/invocation-read-program.ts +93 -0
- package/src/invocation-read-runtime.ts +11 -0
- package/src/linked-device-lifecycle-admission.ts +450 -0
- package/src/linked-device-lifecycle-journal.ts +1560 -0
- package/src/linked-device-lifecycle-runtime.ts +1602 -0
- package/src/local-cli-admission.ts +135 -0
- package/src/local-cli-contracts.ts +271 -0
- package/src/local-cli-execution.ts +98 -0
- package/src/local-cli-surface-contract.ts +1510 -0
- package/src/local-cli-tool-identity.ts +336 -0
- package/src/media/archive.ts +2325 -0
- package/src/media/args.ts +303 -0
- package/src/media/cli.ts +287 -0
- package/src/media/doctor.ts +475 -0
- package/src/media/ffmpeg.ts +1330 -0
- package/src/media/http-capture.ts +711 -0
- package/src/media/http-probe.ts +593 -0
- package/src/media/http.ts +387 -0
- package/src/media/index.ts +10 -0
- package/src/media/local-transcription.ts +613 -0
- package/src/media/lock.ts +321 -0
- package/src/media/manifest.ts +1712 -0
- package/src/media/metadata.ts +906 -0
- package/src/media/process.ts +723 -0
- package/src/media/revision.ts +166 -0
- package/src/media/runtime-closure.ts +1063 -0
- package/src/media/source-router.ts +187 -0
- package/src/media/transcriber-config.ts +1122 -0
- package/src/media/transcript-persistence-model.ts +15 -0
- package/src/media/transcript-persistence-platform.ts +89 -0
- package/src/media/transcript-persistence-program.ts +39 -0
- package/src/media/transcript-persistence-runtime.ts +24 -0
- package/src/media/transcript.ts +616 -0
- package/src/media/utf8-order.ts +30 -0
- package/src/media/whisper-cpp.ts +464 -0
- package/src/media/whisper-language.ts +58 -0
- package/src/media/yt-dlp.ts +439 -0
- package/src/message-like-me-agentic-messaging.ts +882 -0
- package/src/messaging-action-store.ts +793 -0
- package/src/messaging-runtime.ts +2136 -0
- package/src/messaging-store.ts +539 -0
- package/src/messaging-types.ts +1663 -0
- package/src/messaging.ts +334 -0
- package/src/model.ts +2671 -0
- package/src/oauth-google.ts +888 -0
- package/src/omni-client-types.ts +306 -0
- package/src/omni-client.ts +2225 -0
- package/src/omni-limits.ts +34 -0
- package/src/omni-model.ts +1614 -0
- package/src/omni-request.ts +379 -0
- package/src/omni-runtime.ts +1632 -0
- package/src/operation-deadline.ts +189 -0
- package/src/path-helper.ts +1656 -0
- package/src/pinned-https.ts +194 -0
- package/src/plan-assets.ts +832 -0
- package/src/platform-catalog.ts +1832 -0
- package/src/plugins/beeper-linked-device/plugin.ts +310 -0
- package/src/plugins/bluesky-web/plugin.ts +178 -0
- package/src/plugins/clasificados-web/plugin.ts +66 -0
- package/src/plugins/github-web/plugin.ts +80 -0
- package/src/plugins/gmail-official/plugin.ts +110 -0
- package/src/plugins/hacker-news-web/plugin.ts +52 -0
- package/src/plugins/imessage-direct/plugin.ts +195 -0
- package/src/plugins/imessage-direct/vendor/0001-fix-keep-AppleScript-send-payloads-out-of-child-argv.patch +1430 -0
- package/src/plugins/imessage-direct/vendor/0002-feat-rpc-add-exact-chat-lookup.patch +225 -0
- package/src/plugins/imessage-direct/vendor/provenance.json +80 -0
- package/src/plugins/linkedin-official/plugin.ts +84 -0
- package/src/plugins/linkedin-web/plugin.ts +412 -0
- package/src/plugins/meta-web/plugin.ts +317 -0
- package/src/plugins/reddit-web/flair.ts +492 -0
- package/src/plugins/reddit-web/plugin.ts +477 -0
- package/src/plugins/substack-web/plugin.ts +124 -0
- package/src/plugins/tiktok-web/plugin.ts +67 -0
- package/src/plugins/twitch-web/plugin.ts +54 -0
- package/src/plugins/whatsapp-linked-device/plugin.ts +105 -0
- package/src/plugins/x-official/plugin.ts +64 -0
- package/src/plugins/x-web/plugin.ts +488 -0
- package/src/plugins/youtube-web/plugin.ts +101 -0
- package/src/portable-run-recovery.ts +466 -0
- package/src/process-identity.ts +501 -0
- package/src/profile-clone-helper.ts +104 -0
- package/src/provider-catalog-views.ts +51 -0
- package/src/provider-context.ts +69 -0
- package/src/provider-contract-definitions-x.ts +45 -0
- package/src/provider-contract-definitions.ts +948 -0
- package/src/provider-contract-input-gmail.ts +176 -0
- package/src/provider-contract-input-linkedin.ts +77 -0
- package/src/provider-contract-input-x.ts +304 -0
- package/src/provider-contract-planning.ts +23 -0
- package/src/provider-contract-semantic-identity.ts +54 -0
- package/src/provider-contracts.ts +251 -0
- package/src/provider-http.ts +723 -0
- package/src/provider-plugin-auth.ts +36 -0
- package/src/provider-plugin-builtins.ts +229 -0
- package/src/provider-plugin-cleanup-barrier.ts +229 -0
- package/src/provider-plugin-cleanup-execution.ts +79 -0
- package/src/provider-plugin-cleanup-resource.ts +470 -0
- package/src/provider-plugin-contract-identity.ts +707 -0
- package/src/provider-plugin-host.ts +1972 -0
- package/src/provider-plugin-identifiers.ts +60 -0
- package/src/provider-plugin-invocation-lease.ts +889 -0
- package/src/provider-plugin-lifecycle-kernel.ts +566 -0
- package/src/provider-plugin-lifecycle.ts +1455 -0
- package/src/provider-plugin-module-analysis.ts +336 -0
- package/src/provider-plugin-package.ts +2530 -0
- package/src/provider-plugin-portable-authority.ts +106 -0
- package/src/provider-plugin-portable-catalog.ts +94 -0
- package/src/provider-plugin-portable-identity.ts +315 -0
- package/src/provider-plugin-portable-runtime.ts +2057 -0
- package/src/provider-plugin-protocol.ts +1625 -0
- package/src/provider-plugin-registry.ts +3342 -0
- package/src/provider-plugin-store.ts +2155 -0
- package/src/provider-plugin-test-fixture.ts +5 -0
- package/src/provider-plugin.ts +4907 -0
- package/src/provider-plugins.generated.ts +43 -0
- package/src/provider-plugins.ts +8 -0
- package/src/provider-subject.ts +41 -0
- package/src/provider.ts +315 -0
- package/src/providers/beeper-local-runtime.ts +5426 -0
- package/src/providers/beeper-local.ts +2739 -0
- package/src/providers/beeper-messaging.ts +462 -0
- package/src/providers/beeper-omni.ts +1159 -0
- package/src/providers/bluesky-web-runtime.ts +2895 -0
- package/src/providers/bluesky-web.ts +1614 -0
- package/src/providers/clasificados-web-runtime.ts +255 -0
- package/src/providers/clasificados-web.ts +503 -0
- package/src/providers/contact-projection.ts +301 -0
- package/src/providers/github-read-model.ts +94 -0
- package/src/providers/github-read-platform.ts +165 -0
- package/src/providers/github-read-program.ts +97 -0
- package/src/providers/github-web-runtime.ts +320 -0
- package/src/providers/github-web.ts +403 -0
- package/src/providers/gmail-api.ts +2834 -0
- package/src/providers/gmail-omni.ts +830 -0
- package/src/providers/gmail.ts +1130 -0
- package/src/providers/hacker-news-web-runtime.ts +220 -0
- package/src/providers/hacker-news-web.ts +809 -0
- package/src/providers/imessage-direct-install.ts +354 -0
- package/src/providers/imessage-direct-messaging.ts +532 -0
- package/src/providers/imessage-direct-omni.ts +400 -0
- package/src/providers/imessage-direct-runtime.ts +1498 -0
- package/src/providers/imessage-direct.ts +351 -0
- package/src/providers/instagram-video-foundations.ts +913 -0
- package/src/providers/instagram-web-profile-browser.ts +600 -0
- package/src/providers/iso-bmff.ts +338 -0
- package/src/providers/linkedin-company-platform.ts +42 -0
- package/src/providers/linkedin-company-program.ts +88 -0
- package/src/providers/linkedin-contact-failure.ts +84 -0
- package/src/providers/linkedin-contact-platform.ts +45 -0
- package/src/providers/linkedin-contact-program.ts +91 -0
- package/src/providers/linkedin-profile-activity-failure.ts +76 -0
- package/src/providers/linkedin-profile-activity-platform.ts +41 -0
- package/src/providers/linkedin-profile-activity-program.ts +67 -0
- package/src/providers/linkedin-read-failure.ts +52 -0
- package/src/providers/linkedin-self-platform.ts +53 -0
- package/src/providers/linkedin-self-program.ts +122 -0
- package/src/providers/linkedin-web-article-browser.ts +794 -0
- package/src/providers/linkedin-web-bootstrap.ts +146 -0
- package/src/providers/linkedin-web-contact.ts +830 -0
- package/src/providers/linkedin-web-feed-browser.ts +832 -0
- package/src/providers/linkedin-web-feed.ts +864 -0
- package/src/providers/linkedin-web-post-browser.ts +653 -0
- package/src/providers/linkedin-web-profile-browser.ts +683 -0
- package/src/providers/linkedin-web-runtime.ts +2388 -0
- package/src/providers/linkedin-web.ts +3305 -0
- package/src/providers/linkedin.ts +1885 -0
- package/src/providers/meta-bootstrap.ts +1043 -0
- package/src/providers/meta-facebook-group.ts +690 -0
- package/src/providers/meta-marketplace-relay.ts +747 -0
- package/src/providers/meta-omni.ts +373 -0
- package/src/providers/meta-relay-bundle.ts +1049 -0
- package/src/providers/meta-web-descriptors.ts +1740 -0
- package/src/providers/meta-web-runtime.ts +3388 -0
- package/src/providers/meta-web.ts +2931 -0
- package/src/providers/read-failure.ts +139 -0
- package/src/providers/reddit-omni.ts +462 -0
- package/src/providers/reddit-web-runtime.ts +1929 -0
- package/src/providers/reddit-web.ts +1611 -0
- package/src/providers/rental-listings.ts +460 -0
- package/src/providers/substack-omni.ts +406 -0
- package/src/providers/substack-video-mp4.ts +33 -0
- package/src/providers/substack-web-runtime.ts +2812 -0
- package/src/providers/substack-web.ts +1502 -0
- package/src/providers/tiktok-video-mp4.ts +33 -0
- package/src/providers/tiktok-web-runtime.ts +770 -0
- package/src/providers/tiktok-web.ts +2786 -0
- package/src/providers/twitch-web-runtime.ts +226 -0
- package/src/providers/twitch-web.ts +274 -0
- package/src/providers/whatsapp-account-identity.ts +55 -0
- package/src/providers/whatsapp-contact-projection-helper.ts +874 -0
- package/src/providers/whatsapp-contact-projection-protocol.ts +403 -0
- package/src/providers/whatsapp-interaction-projection-helper.ts +1452 -0
- package/src/providers/whatsapp-interaction-projection-protocol.ts +462 -0
- package/src/providers/whatsapp-message-export-projection-protocol.ts +532 -0
- package/src/providers/whatsapp-messaging.ts +132 -0
- package/src/providers/whatsapp-omni.ts +586 -0
- package/src/providers/whatsapp-web-runtime.ts +3108 -0
- package/src/providers/whatsapp-web.ts +1037 -0
- package/src/providers/x-image-provenance.ts +523 -0
- package/src/providers/x-made-with-ai.ts +106 -0
- package/src/providers/x-transaction-id.ts +413 -0
- package/src/providers/x-web-runtime.ts +3485 -0
- package/src/providers/x-web.ts +2332 -0
- package/src/providers/x.ts +1889 -0
- package/src/providers/youtube-web-runtime.ts +1400 -0
- package/src/providers/youtube-web.ts +1221 -0
- package/src/read-admission-runtime.ts +91 -0
- package/src/read-client.ts +521 -0
- package/src/read-effect-platform.ts +7 -0
- package/src/read-effect-runtime.ts +14 -0
- package/src/read-effect.ts +52 -0
- package/src/read-projection-admission.ts +866 -0
- package/src/read-projections.ts +3151 -0
- package/src/recovery.ts +1492 -0
- package/src/reviewed-template.ts +146 -0
- package/src/run-journal.ts +1272 -0
- package/src/runtime.ts +5116 -0
- package/src/scripts/generate-provider-plugin-catalog.ts +121 -0
- package/src/scripts/install-whatsapp-protocol.sh +232 -0
- package/src/scripts/recover-install-stages.ts +1491 -0
- package/src/scripts/resolve-state-home.ts +30 -0
- package/src/scripts/scaffold-web-provider.ts +973 -0
- package/src/scripts/sync-bundled-adapters.ts +695 -0
- package/src/session-secrets.ts +1183 -0
- package/src/state-helper.bunfig.toml +2 -0
- package/src/state-helper.ts +2914 -0
- package/src/storage.ts +3842 -0
- package/src/transport-policy.ts +2 -0
- package/src/usage.ts +182 -0
- package/src/version.ts +2 -0
- package/src/web-session-authentication-policy.ts +136 -0
- package/src/web-session-cleanup-admission.ts +2344 -0
- package/src/web-session-cleanup-join.ts +99 -0
- package/src/web-session-client.ts +996 -0
- package/src/web-session-contract-definitions.ts +820 -0
- package/src/web-session-contract-planning.ts +42 -0
- package/src/web-session-contracts.ts +228 -0
- package/src/web-session-cookies.ts +500 -0
- package/src/web-session-execution.ts +385 -0
- package/src/web-session-http.ts +373 -0
- package/src/web-session-read-errors.ts +19 -0
- package/src/web-session-read-runtime.ts +124 -0
- package/src/web-session-recovery.ts +818 -0
- package/src/web-session-sites.ts +58 -0
- package/src/web-session-template.ts +994 -0
- package/src/web-session.ts +93 -0
- package/src/whatsapp-client-binding.ts +21 -0
- package/src/whatsapp-client-types.ts +75 -0
- package/src/whatsapp-client.ts +395 -0
- package/src/whatsapp-export-coordinate.ts +17 -0
- package/src/whatsapp-message-bundle-v2.ts +195 -0
- package/src/whatsapp-message-like-me-cli.ts +195 -0
- package/src/whatsapp-message-like-me-export.ts +1072 -0
- package/src/whatsapp-message-like-me-source.ts +1012 -0
- package/tsconfig.json +67 -0
|
@@ -0,0 +1,1049 @@
|
|
|
1
|
+
const META_ASSET_ORIGIN = "https://static.xx.fbcdn.net";
|
|
2
|
+
const MAX_ROOT_HTML_BYTES = 16 * 1024 * 1024;
|
|
3
|
+
const MAX_ASSET_URL_CHARACTERS = 4_096;
|
|
4
|
+
const MAX_LINK_ELEMENTS = 4_096;
|
|
5
|
+
const MAX_JSON_SCRIPT_ELEMENTS = 512;
|
|
6
|
+
const MAX_JSON_SCRIPT_BYTES = 12 * 1024 * 1024;
|
|
7
|
+
const MAX_LINK_ATTRIBUTES = 128;
|
|
8
|
+
const MAX_ELEMENT_TAG_CHARACTERS = 64 * 1024;
|
|
9
|
+
const MAX_ASSET_URLS = 16;
|
|
10
|
+
const MAX_BUNDLE_BYTES = 16 * 1024 * 1024;
|
|
11
|
+
const MAX_TOTAL_BUNDLE_BYTES = 64 * 1024 * 1024;
|
|
12
|
+
const MAX_BUNDLES = 64;
|
|
13
|
+
const UTF8_ENCODER = new TextEncoder();
|
|
14
|
+
|
|
15
|
+
const FRIENDLY_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_]{2,160}$/u;
|
|
16
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/u;
|
|
17
|
+
const HTML_ATTRIBUTE_NAME_PATTERN = /^[A-Za-z_:][A-Za-z0-9_.:-]*$/u;
|
|
18
|
+
const RAW_TEXT_ELEMENT_NAMES = new Set([
|
|
19
|
+
"iframe",
|
|
20
|
+
"noembed",
|
|
21
|
+
"noframes",
|
|
22
|
+
"noscript",
|
|
23
|
+
"script",
|
|
24
|
+
"style",
|
|
25
|
+
"textarea",
|
|
26
|
+
"title",
|
|
27
|
+
"xmp",
|
|
28
|
+
]);
|
|
29
|
+
const CONTROL_HEADER_KEYWORDS = new Set([
|
|
30
|
+
"catch",
|
|
31
|
+
"for",
|
|
32
|
+
"if",
|
|
33
|
+
"switch",
|
|
34
|
+
"while",
|
|
35
|
+
"with",
|
|
36
|
+
]);
|
|
37
|
+
const REGEX_PREFIX_KEYWORDS = new Set([
|
|
38
|
+
"await",
|
|
39
|
+
"case",
|
|
40
|
+
"delete",
|
|
41
|
+
"do",
|
|
42
|
+
"else",
|
|
43
|
+
"in",
|
|
44
|
+
"instanceof",
|
|
45
|
+
"new",
|
|
46
|
+
"return",
|
|
47
|
+
"throw",
|
|
48
|
+
"typeof",
|
|
49
|
+
"void",
|
|
50
|
+
"yield",
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
export type MetaRelayOperationRevision = {
|
|
54
|
+
readonly schemaVersion: 1;
|
|
55
|
+
readonly friendlyName: string;
|
|
56
|
+
readonly moduleName: string;
|
|
57
|
+
readonly docId: string;
|
|
58
|
+
readonly agreeingBundleCount: number;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
function utf8ByteLengthWithin(value: string, maximum: number): number {
|
|
62
|
+
if (value.length > maximum) return maximum + 1;
|
|
63
|
+
const bytes = UTF8_ENCODER.encode(value).byteLength;
|
|
64
|
+
return bytes > maximum ? maximum + 1 : bytes;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function boundedText(
|
|
68
|
+
value: unknown,
|
|
69
|
+
maximumBytes: number,
|
|
70
|
+
label: string,
|
|
71
|
+
): { readonly text: string; readonly bytes: number } {
|
|
72
|
+
const bytes = typeof value === "string"
|
|
73
|
+
? utf8ByteLengthWithin(value, maximumBytes)
|
|
74
|
+
: maximumBytes + 1;
|
|
75
|
+
if (
|
|
76
|
+
typeof value !== "string"
|
|
77
|
+
|| value.length < 1
|
|
78
|
+
|| bytes > maximumBytes
|
|
79
|
+
|| value.includes("\0")
|
|
80
|
+
) throw new Error(`${label} must be bounded inert text`);
|
|
81
|
+
return { text: value, bytes };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function inspected<T>(read: () => T, label: string): T {
|
|
85
|
+
try {
|
|
86
|
+
return read();
|
|
87
|
+
} catch {
|
|
88
|
+
throw new Error(`${label} could not be inspected as plain data`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function denseArray(value: unknown, label: string, maximum: number): readonly unknown[] {
|
|
93
|
+
if (!inspected(() => Array.isArray(value), label)) {
|
|
94
|
+
throw new Error(`${label} must contain between 1 and ${maximum} entries`);
|
|
95
|
+
}
|
|
96
|
+
const array = value as readonly unknown[];
|
|
97
|
+
const length = inspected(() => array.length, label);
|
|
98
|
+
if (length < 1 || length > maximum) {
|
|
99
|
+
throw new Error(`${label} must contain between 1 and ${maximum} entries`);
|
|
100
|
+
}
|
|
101
|
+
if (
|
|
102
|
+
inspected(() => Object.getPrototypeOf(array) as unknown, label)
|
|
103
|
+
!== Array.prototype
|
|
104
|
+
) {
|
|
105
|
+
throw new Error(`${label} must be a dense plain array`);
|
|
106
|
+
}
|
|
107
|
+
const result: unknown[] = [];
|
|
108
|
+
for (let index = 0; index < length; index += 1) {
|
|
109
|
+
const descriptor = inspected(
|
|
110
|
+
() => Object.getOwnPropertyDescriptor(array, String(index)),
|
|
111
|
+
label,
|
|
112
|
+
);
|
|
113
|
+
if (
|
|
114
|
+
descriptor === undefined
|
|
115
|
+
|| !Object.hasOwn(descriptor, "value")
|
|
116
|
+
|| descriptor.enumerable !== true
|
|
117
|
+
) throw new Error(`${label} must be a dense plain array`);
|
|
118
|
+
result.push(descriptor.value);
|
|
119
|
+
}
|
|
120
|
+
const keys = inspected(() => Reflect.ownKeys(array), label);
|
|
121
|
+
if (
|
|
122
|
+
keys.length !== length + 1
|
|
123
|
+
|| keys.some((key) =>
|
|
124
|
+
typeof key !== "string"
|
|
125
|
+
|| (key !== "length" && !/^(?:0|[1-9][0-9]*)$/u.test(key)))
|
|
126
|
+
) throw new Error(`${label} contained unsupported fields`);
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function exactFriendlyName(value: unknown): string {
|
|
131
|
+
if (typeof value !== "string" || !FRIENDLY_NAME_PATTERN.test(value)) {
|
|
132
|
+
throw new Error("Meta Relay friendly name changed its reviewed grammar");
|
|
133
|
+
}
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function exactMetaAssetUrl(raw: string): string {
|
|
138
|
+
if (
|
|
139
|
+
raw.length < 1
|
|
140
|
+
|| raw.length > MAX_ASSET_URL_CHARACTERS
|
|
141
|
+
|| raw.includes("\\")
|
|
142
|
+
) throw new Error("Meta root HTML contained a malformed JavaScript asset source");
|
|
143
|
+
if (
|
|
144
|
+
!raw.startsWith(`${META_ASSET_ORIGIN}/rsrc.php/`)
|
|
145
|
+
|| raw.includes("?")
|
|
146
|
+
|| raw.includes("#")
|
|
147
|
+
|| raw.includes("%")
|
|
148
|
+
) throw new Error("Meta root HTML contained a noncanonical JavaScript asset source");
|
|
149
|
+
|
|
150
|
+
let parsed: URL;
|
|
151
|
+
try {
|
|
152
|
+
parsed = new URL(raw);
|
|
153
|
+
} catch {
|
|
154
|
+
throw new Error("Meta root HTML contained a malformed JavaScript asset source");
|
|
155
|
+
}
|
|
156
|
+
if (
|
|
157
|
+
parsed.protocol !== "https:"
|
|
158
|
+
|| parsed.origin !== META_ASSET_ORIGIN
|
|
159
|
+
|| parsed.hostname !== "static.xx.fbcdn.net"
|
|
160
|
+
|| parsed.port !== ""
|
|
161
|
+
|| parsed.username !== ""
|
|
162
|
+
|| parsed.password !== ""
|
|
163
|
+
|| parsed.search !== ""
|
|
164
|
+
|| parsed.hash !== ""
|
|
165
|
+
|| parsed.href !== raw
|
|
166
|
+
|| parsed.pathname.length > 2_048
|
|
167
|
+
|| !/^\/rsrc\.php\/(?:[A-Za-z0-9_-]{1,128}\/){1,16}[A-Za-z0-9_-]{1,1024}\.js$/u.test(
|
|
168
|
+
parsed.pathname,
|
|
169
|
+
)
|
|
170
|
+
) throw new Error("Meta root HTML contained an unreviewed JavaScript asset source");
|
|
171
|
+
return parsed.href;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
type HtmlAttribute = {
|
|
175
|
+
readonly value: string | null;
|
|
176
|
+
readonly quoted: boolean;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
type HtmlTag = {
|
|
180
|
+
readonly closing: boolean;
|
|
181
|
+
readonly end: number;
|
|
182
|
+
readonly name: string;
|
|
183
|
+
readonly nameEnd: number;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
function isHtmlSpace(value: string | undefined): boolean {
|
|
187
|
+
return value === " "
|
|
188
|
+
|| value === "\t"
|
|
189
|
+
|| value === "\n"
|
|
190
|
+
|| value === "\r"
|
|
191
|
+
|| value === "\f";
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function isHtmlNameCharacter(value: string | undefined): boolean {
|
|
195
|
+
return value !== undefined && /[A-Za-z0-9_:-]/u.test(value);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function htmlTagAt(source: string, start: number): HtmlTag | null {
|
|
199
|
+
let index = start + 1;
|
|
200
|
+
let closing = false;
|
|
201
|
+
if (source[index] === "/") {
|
|
202
|
+
closing = true;
|
|
203
|
+
index += 1;
|
|
204
|
+
}
|
|
205
|
+
const nameStart = index;
|
|
206
|
+
while (isHtmlNameCharacter(source[index])) index += 1;
|
|
207
|
+
if (index === nameStart) return null;
|
|
208
|
+
const boundary = source[index];
|
|
209
|
+
if (
|
|
210
|
+
boundary !== ">"
|
|
211
|
+
&& boundary !== "/"
|
|
212
|
+
&& !isHtmlSpace(boundary)
|
|
213
|
+
) return null;
|
|
214
|
+
|
|
215
|
+
let quote: "'" | "\"" | null = null;
|
|
216
|
+
for (let cursor = index; cursor < source.length; cursor += 1) {
|
|
217
|
+
if ((cursor + 1) - start > MAX_ELEMENT_TAG_CHARACTERS) {
|
|
218
|
+
throw new Error("Meta root HTML contained an oversized element");
|
|
219
|
+
}
|
|
220
|
+
const character = source[cursor];
|
|
221
|
+
if (quote !== null) {
|
|
222
|
+
if (character === quote) quote = null;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (character === "'" || character === "\"") {
|
|
226
|
+
quote = character;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (character === "<") {
|
|
230
|
+
throw new Error("Meta root HTML contained a malformed element");
|
|
231
|
+
}
|
|
232
|
+
if (character === ">") {
|
|
233
|
+
return {
|
|
234
|
+
closing,
|
|
235
|
+
end: cursor + 1,
|
|
236
|
+
name: source.slice(nameStart, index).toLowerCase(),
|
|
237
|
+
nameEnd: index,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
throw new Error("Meta root HTML contained a malformed element");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function isExactRawTextClosingTag(
|
|
245
|
+
source: string,
|
|
246
|
+
tag: HtmlTag | null,
|
|
247
|
+
elementName: string,
|
|
248
|
+
): tag is HtmlTag {
|
|
249
|
+
if (
|
|
250
|
+
tag?.closing !== true
|
|
251
|
+
|| tag.name !== elementName
|
|
252
|
+
) return false;
|
|
253
|
+
for (let index = tag.nameEnd; index < tag.end - 1; index += 1) {
|
|
254
|
+
if (!isHtmlSpace(source[index])) return false;
|
|
255
|
+
}
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function skipHtmlComment(source: string, start: number): number {
|
|
260
|
+
const end = source.indexOf("-->", start + 4);
|
|
261
|
+
if (end < 0) throw new Error("Meta root HTML contained a malformed comment");
|
|
262
|
+
return end + 3;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function skipHtmlDeclaration(source: string, start: number): number {
|
|
266
|
+
if (source.startsWith("<![CDATA[", start)) {
|
|
267
|
+
const end = source.indexOf("]]>", start + 9);
|
|
268
|
+
if (end < 0) throw new Error("Meta root HTML contained malformed CDATA");
|
|
269
|
+
return end + 3;
|
|
270
|
+
}
|
|
271
|
+
const end = source.indexOf(">", start + 2);
|
|
272
|
+
if (end < 0) throw new Error("Meta root HTML contained a malformed declaration");
|
|
273
|
+
return end + 1;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function rawTextElementEnd(
|
|
277
|
+
source: string,
|
|
278
|
+
start: number,
|
|
279
|
+
elementName: string,
|
|
280
|
+
): number {
|
|
281
|
+
let index = start;
|
|
282
|
+
while (index < source.length) {
|
|
283
|
+
const candidate = source.indexOf("</", index);
|
|
284
|
+
if (candidate < 0) {
|
|
285
|
+
throw new Error("Meta root HTML contained an unterminated raw-text element");
|
|
286
|
+
}
|
|
287
|
+
const tag = htmlTagAt(source, candidate);
|
|
288
|
+
if (isExactRawTextClosingTag(source, tag, elementName)) return tag.end;
|
|
289
|
+
index = candidate + 2;
|
|
290
|
+
}
|
|
291
|
+
throw new Error("Meta root HTML contained an unterminated raw-text element");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function rawTextElementClosingTag(
|
|
295
|
+
source: string,
|
|
296
|
+
start: number,
|
|
297
|
+
elementName: string,
|
|
298
|
+
): { readonly start: number; readonly end: number } {
|
|
299
|
+
let index = start;
|
|
300
|
+
while (index < source.length) {
|
|
301
|
+
const candidate = source.indexOf("</", index);
|
|
302
|
+
if (candidate < 0) {
|
|
303
|
+
throw new Error("Meta root HTML contained an unterminated raw-text element");
|
|
304
|
+
}
|
|
305
|
+
const tag = htmlTagAt(source, candidate);
|
|
306
|
+
if (isExactRawTextClosingTag(source, tag, elementName)) {
|
|
307
|
+
return { start: candidate, end: tag.end };
|
|
308
|
+
}
|
|
309
|
+
index = candidate + 2;
|
|
310
|
+
}
|
|
311
|
+
throw new Error("Meta root HTML contained an unterminated raw-text element");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function htmlAttributes(
|
|
315
|
+
source: string,
|
|
316
|
+
tagStart: number,
|
|
317
|
+
tag: HtmlTag,
|
|
318
|
+
): ReadonlyMap<string, HtmlAttribute> {
|
|
319
|
+
if (tag.end - tagStart > MAX_ELEMENT_TAG_CHARACTERS) {
|
|
320
|
+
throw new Error("Meta root HTML contained an oversized element");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const attributes = new Map<string, HtmlAttribute>();
|
|
324
|
+
const bodyEnd = tag.end - 1;
|
|
325
|
+
let index = tag.nameEnd;
|
|
326
|
+
while (index < bodyEnd) {
|
|
327
|
+
let spaces = 0;
|
|
328
|
+
while (isHtmlSpace(source[index])) {
|
|
329
|
+
index += 1;
|
|
330
|
+
spaces += 1;
|
|
331
|
+
}
|
|
332
|
+
if (index >= bodyEnd) break;
|
|
333
|
+
if (source[index] === "/") {
|
|
334
|
+
index += 1;
|
|
335
|
+
while (isHtmlSpace(source[index])) index += 1;
|
|
336
|
+
if (index !== bodyEnd) {
|
|
337
|
+
throw new Error("Meta root HTML contained malformed element attributes");
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
if (spaces < 1) {
|
|
342
|
+
throw new Error("Meta root HTML contained ambiguous element attributes");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const nameStart = index;
|
|
346
|
+
while (
|
|
347
|
+
source[index] !== undefined
|
|
348
|
+
&& /[A-Za-z0-9_.:-]/u.test(source[index] ?? "")
|
|
349
|
+
) index += 1;
|
|
350
|
+
const rawName = source.slice(nameStart, index);
|
|
351
|
+
if (!HTML_ATTRIBUTE_NAME_PATTERN.test(rawName)) {
|
|
352
|
+
throw new Error("Meta root HTML contained malformed element attributes");
|
|
353
|
+
}
|
|
354
|
+
const name = rawName.toLowerCase();
|
|
355
|
+
if (attributes.has(name)) {
|
|
356
|
+
throw new Error("Meta root HTML contained duplicate element attributes");
|
|
357
|
+
}
|
|
358
|
+
if (attributes.size >= MAX_LINK_ATTRIBUTES) {
|
|
359
|
+
throw new Error("Meta root HTML exceeded its reviewed element-attribute bound");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const afterName = index;
|
|
363
|
+
while (isHtmlSpace(source[index])) index += 1;
|
|
364
|
+
let value: string | null = null;
|
|
365
|
+
let quoted = false;
|
|
366
|
+
if (source[index] === "=") {
|
|
367
|
+
index += 1;
|
|
368
|
+
while (isHtmlSpace(source[index])) index += 1;
|
|
369
|
+
const quote = source[index];
|
|
370
|
+
if (quote === "'" || quote === "\"") {
|
|
371
|
+
quoted = true;
|
|
372
|
+
index += 1;
|
|
373
|
+
const valueStart = index;
|
|
374
|
+
while (index < bodyEnd && source[index] !== quote) index += 1;
|
|
375
|
+
if (index >= bodyEnd) {
|
|
376
|
+
throw new Error("Meta root HTML contained malformed element attributes");
|
|
377
|
+
}
|
|
378
|
+
value = source.slice(valueStart, index);
|
|
379
|
+
index += 1;
|
|
380
|
+
if (
|
|
381
|
+
index < bodyEnd
|
|
382
|
+
&& !isHtmlSpace(source[index])
|
|
383
|
+
&& source[index] !== "/"
|
|
384
|
+
) {
|
|
385
|
+
throw new Error("Meta root HTML contained ambiguous element attributes");
|
|
386
|
+
}
|
|
387
|
+
} else {
|
|
388
|
+
const valueStart = index;
|
|
389
|
+
while (
|
|
390
|
+
index < bodyEnd
|
|
391
|
+
&& !isHtmlSpace(source[index])
|
|
392
|
+
) index += 1;
|
|
393
|
+
value = source.slice(valueStart, index);
|
|
394
|
+
if (
|
|
395
|
+
value.length < 1
|
|
396
|
+
|| /["'`=<>]/u.test(value)
|
|
397
|
+
) {
|
|
398
|
+
throw new Error("Meta root HTML contained malformed element attributes");
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
index = afterName;
|
|
403
|
+
}
|
|
404
|
+
attributes.set(name, { value, quoted });
|
|
405
|
+
}
|
|
406
|
+
return attributes;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function exactPreloadScriptHref(
|
|
410
|
+
attributes: ReadonlyMap<string, HtmlAttribute>,
|
|
411
|
+
): string | null {
|
|
412
|
+
const rel = attributes.get("rel");
|
|
413
|
+
const as = attributes.get("as");
|
|
414
|
+
const relTokens = rel?.value?.split(/[\t\n\f\r ]+/u).filter(
|
|
415
|
+
(token) => token.length > 0,
|
|
416
|
+
) ?? [];
|
|
417
|
+
const hasPreloadSemantics = relTokens.some(
|
|
418
|
+
(token) => token.toLowerCase() === "preload",
|
|
419
|
+
);
|
|
420
|
+
if (hasPreloadSemantics && rel?.value !== "preload") {
|
|
421
|
+
throw new Error("Meta root HTML contained an ambiguous preload relation");
|
|
422
|
+
}
|
|
423
|
+
const hasScriptSemantics = as?.value?.toLowerCase() === "script";
|
|
424
|
+
if (hasScriptSemantics && as?.value !== "script") {
|
|
425
|
+
throw new Error("Meta root HTML contained an ambiguous script destination");
|
|
426
|
+
}
|
|
427
|
+
if (!hasPreloadSemantics || !hasScriptSemantics) return null;
|
|
428
|
+
|
|
429
|
+
const href = attributes.get("href");
|
|
430
|
+
if (href?.quoted !== true || href.value === null) {
|
|
431
|
+
throw new Error("Meta preload-script link omitted one quoted href");
|
|
432
|
+
}
|
|
433
|
+
return href.value;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Extract canonical first-party JavaScript bundle URLs from actual inert HTML
|
|
438
|
+
* preload-script links. The helper never parses resource-map text or fetches a
|
|
439
|
+
* returned URL.
|
|
440
|
+
*/
|
|
441
|
+
export function extractMetaRelayBundleUrls(htmlValue: unknown): readonly string[] {
|
|
442
|
+
const { text: html } = boundedText(
|
|
443
|
+
htmlValue,
|
|
444
|
+
MAX_ROOT_HTML_BYTES,
|
|
445
|
+
"Meta root HTML",
|
|
446
|
+
);
|
|
447
|
+
const urls: string[] = [];
|
|
448
|
+
const unique = new Set<string>();
|
|
449
|
+
let linkElements = 0;
|
|
450
|
+
let index = 0;
|
|
451
|
+
while (index < html.length) {
|
|
452
|
+
const tagStart = html.indexOf("<", index);
|
|
453
|
+
if (tagStart < 0) break;
|
|
454
|
+
if (html.startsWith("<!--", tagStart)) {
|
|
455
|
+
index = skipHtmlComment(html, tagStart);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (html.startsWith("<!", tagStart) || html.startsWith("<?", tagStart)) {
|
|
459
|
+
index = skipHtmlDeclaration(html, tagStart);
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const tag = htmlTagAt(html, tagStart);
|
|
464
|
+
if (tag === null) {
|
|
465
|
+
index = tagStart + 1;
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
index = tag.end;
|
|
469
|
+
if (!tag.closing && RAW_TEXT_ELEMENT_NAMES.has(tag.name)) {
|
|
470
|
+
index = rawTextElementEnd(html, tag.end, tag.name);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (tag.closing || tag.name !== "link") continue;
|
|
474
|
+
|
|
475
|
+
linkElements += 1;
|
|
476
|
+
if (linkElements > MAX_LINK_ELEMENTS) {
|
|
477
|
+
throw new Error("Meta root HTML exceeded its reviewed link-element bound");
|
|
478
|
+
}
|
|
479
|
+
const href = exactPreloadScriptHref(htmlAttributes(html, tagStart, tag));
|
|
480
|
+
if (href === null) continue;
|
|
481
|
+
const exact = exactMetaAssetUrl(href);
|
|
482
|
+
if (unique.has(exact)) continue;
|
|
483
|
+
unique.add(exact);
|
|
484
|
+
urls.push(exact);
|
|
485
|
+
if (urls.length > MAX_ASSET_URLS) {
|
|
486
|
+
throw new Error("Meta root HTML exposed too many preload-script assets");
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (urls.length < 1) {
|
|
490
|
+
throw new Error("Meta root HTML omitted a reviewed preload-script asset");
|
|
491
|
+
}
|
|
492
|
+
return Object.freeze(urls);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Extract bodies from actual `<script type="application/json">` elements.
|
|
497
|
+
* Attribute names are parsed exactly, duplicates are rejected, and lookalikes
|
|
498
|
+
* such as `data-type` never acquire script-type semantics.
|
|
499
|
+
*/
|
|
500
|
+
export function extractMetaJsonScriptTexts(
|
|
501
|
+
htmlValue: unknown,
|
|
502
|
+
): readonly string[] {
|
|
503
|
+
const { text: html } = boundedText(
|
|
504
|
+
htmlValue,
|
|
505
|
+
MAX_ROOT_HTML_BYTES,
|
|
506
|
+
"Meta root HTML",
|
|
507
|
+
);
|
|
508
|
+
const texts: string[] = [];
|
|
509
|
+
let scriptElements = 0;
|
|
510
|
+
let index = 0;
|
|
511
|
+
while (index < html.length) {
|
|
512
|
+
const tagStart = html.indexOf("<", index);
|
|
513
|
+
if (tagStart < 0) break;
|
|
514
|
+
if (html.startsWith("<!--", tagStart)) {
|
|
515
|
+
index = skipHtmlComment(html, tagStart);
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (html.startsWith("<!", tagStart) || html.startsWith("<?", tagStart)) {
|
|
519
|
+
index = skipHtmlDeclaration(html, tagStart);
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
const tag = htmlTagAt(html, tagStart);
|
|
523
|
+
if (tag === null) {
|
|
524
|
+
index = tagStart + 1;
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
index = tag.end;
|
|
528
|
+
if (tag.closing || tag.name !== "script") {
|
|
529
|
+
if (!tag.closing && RAW_TEXT_ELEMENT_NAMES.has(tag.name)) {
|
|
530
|
+
index = rawTextElementEnd(html, tag.end, tag.name);
|
|
531
|
+
}
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
scriptElements += 1;
|
|
535
|
+
if (scriptElements > MAX_JSON_SCRIPT_ELEMENTS) {
|
|
536
|
+
throw new Error("Meta root HTML exceeded its reviewed script-element bound");
|
|
537
|
+
}
|
|
538
|
+
const attributes = htmlAttributes(html, tagStart, tag);
|
|
539
|
+
const closing = rawTextElementClosingTag(html, tag.end, "script");
|
|
540
|
+
index = closing.end;
|
|
541
|
+
const type = attributes.get("type");
|
|
542
|
+
if (type?.value?.toLowerCase() === "application/json") {
|
|
543
|
+
if (type.quoted !== true || type.value !== "application/json") {
|
|
544
|
+
throw new Error("Meta JSON script contained an ambiguous type attribute");
|
|
545
|
+
}
|
|
546
|
+
const text = html.slice(tag.end, closing.start);
|
|
547
|
+
if (utf8ByteLengthWithin(text, MAX_JSON_SCRIPT_BYTES) > MAX_JSON_SCRIPT_BYTES) {
|
|
548
|
+
throw new Error("Meta bootloader JSON script exceeded its reviewed bound");
|
|
549
|
+
}
|
|
550
|
+
texts.push(text);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (texts.length < 1) {
|
|
554
|
+
throw new Error("Meta HTML response omitted bootloader JSON");
|
|
555
|
+
}
|
|
556
|
+
return Object.freeze(texts);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function skipQuoted(
|
|
560
|
+
source: string,
|
|
561
|
+
start: number,
|
|
562
|
+
quote: "'" | "\"" | "`",
|
|
563
|
+
): number {
|
|
564
|
+
for (let index = start + 1; index < source.length; index += 1) {
|
|
565
|
+
const character = source[index];
|
|
566
|
+
if (character === "\\") {
|
|
567
|
+
index += 1;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (character === quote) return index + 1;
|
|
571
|
+
if (quote !== "`" && isLineTerminator(character)) {
|
|
572
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function isLineTerminator(value: string | undefined): boolean {
|
|
579
|
+
return value === "\n"
|
|
580
|
+
|| value === "\r"
|
|
581
|
+
|| value === "\u2028"
|
|
582
|
+
|| value === "\u2029";
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function skipLineComment(source: string, start: number): number {
|
|
586
|
+
for (let index = start + 2; index < source.length; index += 1) {
|
|
587
|
+
if (isLineTerminator(source[index])) {
|
|
588
|
+
return source[index] === "\r" && source[index + 1] === "\n"
|
|
589
|
+
? index + 2
|
|
590
|
+
: index + 1;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return source.length;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function skipBlockComment(source: string, start: number): number {
|
|
597
|
+
const end = source.indexOf("*/", start + 2);
|
|
598
|
+
if (end < 0) throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
599
|
+
return end + 2;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function skipRegexLiteral(source: string, start: number): number {
|
|
603
|
+
let inClass = false;
|
|
604
|
+
for (let index = start + 1; index < source.length; index += 1) {
|
|
605
|
+
const character = source[index];
|
|
606
|
+
if (character === "\\") {
|
|
607
|
+
index += 1;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (isLineTerminator(character)) {
|
|
611
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
612
|
+
}
|
|
613
|
+
if (character === "[") inClass = true;
|
|
614
|
+
else if (character === "]") inClass = false;
|
|
615
|
+
else if (character === "/" && !inClass) {
|
|
616
|
+
index += 1;
|
|
617
|
+
while (/[A-Za-z]/u.test(source[index] ?? "")) index += 1;
|
|
618
|
+
return index;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function identifierEnd(source: string, start: number): number {
|
|
625
|
+
let index = start + 1;
|
|
626
|
+
while (/[A-Za-z0-9_$]/u.test(source[index] ?? "")) index += 1;
|
|
627
|
+
return index;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function numberEnd(source: string, start: number): number {
|
|
631
|
+
let index = start + 1;
|
|
632
|
+
while (/[A-Za-z0-9._]/u.test(source[index] ?? "")) index += 1;
|
|
633
|
+
return index;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function nextNonWhitespace(source: string, start: number): number {
|
|
637
|
+
let index = start;
|
|
638
|
+
while (/\s/u.test(source[index] ?? "")) index += 1;
|
|
639
|
+
return index;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function previousNonWhitespace(source: string, start: number): string | null {
|
|
643
|
+
for (let index = start - 1; index >= 0; index -= 1) {
|
|
644
|
+
const character = source[index];
|
|
645
|
+
if (character !== undefined && !/\s/u.test(character)) return character;
|
|
646
|
+
}
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function hasReviewedCallTerminator(source: string, start: number): boolean {
|
|
651
|
+
let index = start;
|
|
652
|
+
while (index < source.length) {
|
|
653
|
+
const character = source[index];
|
|
654
|
+
const next = source[index + 1];
|
|
655
|
+
if (character !== undefined && /\s/u.test(character)) {
|
|
656
|
+
index += 1;
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
if (character === "/" && next === "/") {
|
|
660
|
+
index = skipLineComment(source, index);
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (character === "/" && next === "*") {
|
|
664
|
+
index = skipBlockComment(source, index);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
return character === ";";
|
|
668
|
+
}
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function findCallEnd(source: string, openParenthesis: number): number {
|
|
673
|
+
let depth = 1;
|
|
674
|
+
let index = openParenthesis + 1;
|
|
675
|
+
let canStartRegex = true;
|
|
676
|
+
let pendingControlParenthesis = false;
|
|
677
|
+
const controlParentheses = [false];
|
|
678
|
+
while (index < source.length) {
|
|
679
|
+
const character = source[index];
|
|
680
|
+
const next = source[index + 1];
|
|
681
|
+
if (character === "'" || character === "\"" || character === "`") {
|
|
682
|
+
index = skipQuoted(source, index, character);
|
|
683
|
+
canStartRegex = false;
|
|
684
|
+
pendingControlParenthesis = false;
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (character === "/" && next === "/") {
|
|
688
|
+
index = skipLineComment(source, index);
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (character === "/" && next === "*") {
|
|
692
|
+
index = skipBlockComment(source, index);
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if (character === "/" && canStartRegex) {
|
|
696
|
+
index = skipRegexLiteral(source, index);
|
|
697
|
+
canStartRegex = false;
|
|
698
|
+
pendingControlParenthesis = false;
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
if (character === "/") {
|
|
702
|
+
index += 1;
|
|
703
|
+
canStartRegex = true;
|
|
704
|
+
pendingControlParenthesis = false;
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (character !== undefined && /[A-Za-z_$]/u.test(character)) {
|
|
708
|
+
const end = identifierEnd(source, index);
|
|
709
|
+
const identifier = source.slice(index, end);
|
|
710
|
+
canStartRegex = REGEX_PREFIX_KEYWORDS.has(identifier);
|
|
711
|
+
pendingControlParenthesis = CONTROL_HEADER_KEYWORDS.has(identifier);
|
|
712
|
+
index = end;
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if (character !== undefined && /[0-9]/u.test(character)) {
|
|
716
|
+
index = numberEnd(source, index);
|
|
717
|
+
canStartRegex = false;
|
|
718
|
+
pendingControlParenthesis = false;
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
if (character === "(") {
|
|
722
|
+
depth += 1;
|
|
723
|
+
controlParentheses.push(pendingControlParenthesis);
|
|
724
|
+
canStartRegex = true;
|
|
725
|
+
pendingControlParenthesis = false;
|
|
726
|
+
} else if (character === ")") {
|
|
727
|
+
depth -= 1;
|
|
728
|
+
const closedControlHeader = controlParentheses.pop() ?? false;
|
|
729
|
+
if (depth === 0) return index + 1;
|
|
730
|
+
if (depth < 0) throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
731
|
+
canStartRegex = closedControlHeader;
|
|
732
|
+
pendingControlParenthesis = false;
|
|
733
|
+
} else if (character === "]" || character === "}") {
|
|
734
|
+
canStartRegex = false;
|
|
735
|
+
pendingControlParenthesis = false;
|
|
736
|
+
} else if (character !== undefined && !/\s/u.test(character)) {
|
|
737
|
+
canStartRegex = character !== "." && character !== "?";
|
|
738
|
+
pendingControlParenthesis = false;
|
|
739
|
+
}
|
|
740
|
+
index += 1;
|
|
741
|
+
}
|
|
742
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function moduleNameFromCall(call: string): string | null {
|
|
746
|
+
const match = /^__d\s*\(\s*"([A-Za-z][A-Za-z0-9_]{2,220})"\s*,/u.exec(call);
|
|
747
|
+
return match?.[1] ?? null;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function escapeRegExp(value: string): string {
|
|
751
|
+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function docIdFromReviewedModule(call: string, moduleName: string): string {
|
|
755
|
+
const escapedName = escapeRegExp(moduleName);
|
|
756
|
+
const wrapper = new RegExp(
|
|
757
|
+
`^__d\\s*\\(\\s*"${escapedName}"\\s*,\\s*\\[\\s*\\]\\s*,\\s*\\(\\s*function\\s*\\(([^)]*)\\)\\s*\\{([\\s\\S]*)\\}\\s*\\)\\s*,\\s*(?:null|-?[0-9]{1,7})\\s*\\)$`,
|
|
758
|
+
"u",
|
|
759
|
+
);
|
|
760
|
+
const wrapped = wrapper.exec(call);
|
|
761
|
+
if (wrapped?.[1] === undefined || wrapped[2] === undefined) {
|
|
762
|
+
throw new Error("Meta Relay operation module changed its reviewed boundary");
|
|
763
|
+
}
|
|
764
|
+
const parameters = wrapped[1].split(",").map((value) => value.trim());
|
|
765
|
+
if (
|
|
766
|
+
parameters.length !== 6
|
|
767
|
+
|| parameters.some((value) => !IDENTIFIER_PATTERN.test(value))
|
|
768
|
+
|| new Set(parameters).size !== parameters.length
|
|
769
|
+
) throw new Error("Meta Relay operation module changed its reviewed function shape");
|
|
770
|
+
const exporter = parameters[4];
|
|
771
|
+
if (exporter === undefined) {
|
|
772
|
+
throw new Error("Meta Relay operation module omitted its reviewed exporter");
|
|
773
|
+
}
|
|
774
|
+
const exportPattern = new RegExp(
|
|
775
|
+
`^\\s*(?:"use strict";\\s*)?${escapeRegExp(exporter)}\\.exports\\s*=\\s*(["'])([0-9]{10,24})\\1\\s*;?\\s*$`,
|
|
776
|
+
"u",
|
|
777
|
+
);
|
|
778
|
+
const exported = exportPattern.exec(wrapped[2]);
|
|
779
|
+
if (exported?.[2] === undefined) {
|
|
780
|
+
throw new Error("Meta Relay operation module did not uniquely export a bounded doc ID");
|
|
781
|
+
}
|
|
782
|
+
return exported[2];
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function parsedTargetCallPositions(
|
|
786
|
+
source: string,
|
|
787
|
+
moduleName: string,
|
|
788
|
+
): {
|
|
789
|
+
readonly all: ReadonlySet<number>;
|
|
790
|
+
readonly directStatements: ReadonlySet<number>;
|
|
791
|
+
} {
|
|
792
|
+
const sourceFile = ts.createSourceFile(
|
|
793
|
+
"meta-relay-bundle.js",
|
|
794
|
+
source,
|
|
795
|
+
ts.ScriptTarget.Latest,
|
|
796
|
+
false,
|
|
797
|
+
ts.ScriptKind.JS,
|
|
798
|
+
);
|
|
799
|
+
const diagnostics = (
|
|
800
|
+
sourceFile as ts.SourceFile & {
|
|
801
|
+
readonly parseDiagnostics?: readonly ts.Diagnostic[];
|
|
802
|
+
}
|
|
803
|
+
).parseDiagnostics ?? [];
|
|
804
|
+
if (diagnostics.length > 0) {
|
|
805
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
806
|
+
}
|
|
807
|
+
const isTargetCall = (node: ts.Node): node is ts.CallExpression => {
|
|
808
|
+
if (!ts.isCallExpression(node)) return false;
|
|
809
|
+
const first = node.arguments[0];
|
|
810
|
+
if (
|
|
811
|
+
first === undefined
|
|
812
|
+
|| (!ts.isStringLiteral(first) && !ts.isNoSubstitutionTemplateLiteral(first))
|
|
813
|
+
|| first.text !== moduleName
|
|
814
|
+
) return false;
|
|
815
|
+
return ts.isIdentifier(node.expression)
|
|
816
|
+
? node.expression.text === "__d"
|
|
817
|
+
: ts.isPropertyAccessExpression(node.expression)
|
|
818
|
+
&& node.expression.name.text === "__d";
|
|
819
|
+
};
|
|
820
|
+
const all = new Set<number>();
|
|
821
|
+
const visit = (node: ts.Node): void => {
|
|
822
|
+
if (isTargetCall(node)) {
|
|
823
|
+
const expression = node.expression;
|
|
824
|
+
all.add(
|
|
825
|
+
ts.isIdentifier(expression)
|
|
826
|
+
? expression.getStart(sourceFile)
|
|
827
|
+
: ts.isPropertyAccessExpression(expression)
|
|
828
|
+
? expression.name.getStart(sourceFile)
|
|
829
|
+
: node.getStart(sourceFile),
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
ts.forEachChild(node, visit);
|
|
833
|
+
};
|
|
834
|
+
visit(sourceFile);
|
|
835
|
+
|
|
836
|
+
const directStatements = new Set<number>();
|
|
837
|
+
for (const statement of sourceFile.statements) {
|
|
838
|
+
if (
|
|
839
|
+
ts.isExpressionStatement(statement)
|
|
840
|
+
&& isTargetCall(statement.expression)
|
|
841
|
+
&& ts.isIdentifier(statement.expression.expression)
|
|
842
|
+
) {
|
|
843
|
+
directStatements.add(statement.expression.getStart(sourceFile));
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return { all, directStatements };
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function targetModuleDocIds(source: string, moduleName: string): readonly string[] {
|
|
850
|
+
const ids: string[] = [];
|
|
851
|
+
const parsedPositions = parsedTargetCallPositions(source, moduleName);
|
|
852
|
+
let parenthesisDepth = 0;
|
|
853
|
+
let bracketDepth = 0;
|
|
854
|
+
let braceDepth = 0;
|
|
855
|
+
let canStartRegex = true;
|
|
856
|
+
let atStatementBoundary = true;
|
|
857
|
+
let pendingControlParenthesis = false;
|
|
858
|
+
const controlParentheses: boolean[] = [];
|
|
859
|
+
let index = 0;
|
|
860
|
+
|
|
861
|
+
while (index < source.length) {
|
|
862
|
+
const character = source[index];
|
|
863
|
+
const next = source[index + 1];
|
|
864
|
+
if (character === "'" || character === "\"" || character === "`") {
|
|
865
|
+
index = skipQuoted(source, index, character);
|
|
866
|
+
canStartRegex = false;
|
|
867
|
+
atStatementBoundary = false;
|
|
868
|
+
pendingControlParenthesis = false;
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
if (character === "/" && next === "/") {
|
|
872
|
+
index = skipLineComment(source, index);
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
if (character === "/" && next === "*") {
|
|
876
|
+
index = skipBlockComment(source, index);
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
if (character === "/" && canStartRegex) {
|
|
880
|
+
index = skipRegexLiteral(source, index);
|
|
881
|
+
canStartRegex = false;
|
|
882
|
+
atStatementBoundary = false;
|
|
883
|
+
pendingControlParenthesis = false;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (character === "/") {
|
|
887
|
+
index += 1;
|
|
888
|
+
canStartRegex = true;
|
|
889
|
+
atStatementBoundary = false;
|
|
890
|
+
pendingControlParenthesis = false;
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (character !== undefined && /[A-Za-z_$]/u.test(character)) {
|
|
894
|
+
const end = identifierEnd(source, index);
|
|
895
|
+
const identifier = source.slice(index, end);
|
|
896
|
+
const open = nextNonWhitespace(source, end);
|
|
897
|
+
if (
|
|
898
|
+
identifier === "__d"
|
|
899
|
+
&& source[open] === "("
|
|
900
|
+
&& parenthesisDepth === 0
|
|
901
|
+
&& bracketDepth === 0
|
|
902
|
+
&& braceDepth === 0
|
|
903
|
+
&& parsedPositions.all.has(index)
|
|
904
|
+
) {
|
|
905
|
+
const callEnd = findCallEnd(source, open);
|
|
906
|
+
const call = source.slice(index, callEnd);
|
|
907
|
+
if (moduleNameFromCall(call) === moduleName) {
|
|
908
|
+
if (
|
|
909
|
+
!parsedPositions.directStatements.has(index)
|
|
910
|
+
|| !atStatementBoundary
|
|
911
|
+
|| previousNonWhitespace(source, index) === "."
|
|
912
|
+
|| !hasReviewedCallTerminator(source, callEnd)
|
|
913
|
+
) {
|
|
914
|
+
throw new Error("Meta Relay operation module changed its reviewed boundary");
|
|
915
|
+
}
|
|
916
|
+
ids.push(docIdFromReviewedModule(call, moduleName));
|
|
917
|
+
if (ids.length > 1) {
|
|
918
|
+
throw new Error("Meta Relay bundle contained a duplicate operation module");
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
index = callEnd;
|
|
922
|
+
canStartRegex = false;
|
|
923
|
+
atStatementBoundary = false;
|
|
924
|
+
pendingControlParenthesis = false;
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
canStartRegex = REGEX_PREFIX_KEYWORDS.has(identifier);
|
|
928
|
+
atStatementBoundary = false;
|
|
929
|
+
pendingControlParenthesis = CONTROL_HEADER_KEYWORDS.has(identifier);
|
|
930
|
+
index = end;
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
if (character !== undefined && /[0-9]/u.test(character)) {
|
|
934
|
+
index = numberEnd(source, index);
|
|
935
|
+
canStartRegex = false;
|
|
936
|
+
atStatementBoundary = false;
|
|
937
|
+
pendingControlParenthesis = false;
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
let closedControlHeader = false;
|
|
942
|
+
if (character === "(") {
|
|
943
|
+
parenthesisDepth += 1;
|
|
944
|
+
controlParentheses.push(pendingControlParenthesis);
|
|
945
|
+
pendingControlParenthesis = false;
|
|
946
|
+
} else if (character === ")") {
|
|
947
|
+
parenthesisDepth -= 1;
|
|
948
|
+
closedControlHeader = controlParentheses.pop() ?? false;
|
|
949
|
+
pendingControlParenthesis = false;
|
|
950
|
+
} else if (character === "[") bracketDepth += 1;
|
|
951
|
+
else if (character === "]") bracketDepth -= 1;
|
|
952
|
+
else if (character === "{") braceDepth += 1;
|
|
953
|
+
else if (character === "}") braceDepth -= 1;
|
|
954
|
+
if (parenthesisDepth < 0 || bracketDepth < 0 || braceDepth < 0) {
|
|
955
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
956
|
+
}
|
|
957
|
+
if (
|
|
958
|
+
character === ";"
|
|
959
|
+
&& parenthesisDepth === 0
|
|
960
|
+
&& bracketDepth === 0
|
|
961
|
+
&& braceDepth === 0
|
|
962
|
+
) {
|
|
963
|
+
atStatementBoundary = true;
|
|
964
|
+
} else if (
|
|
965
|
+
character === "}"
|
|
966
|
+
&& parenthesisDepth === 0
|
|
967
|
+
&& bracketDepth === 0
|
|
968
|
+
&& braceDepth === 0
|
|
969
|
+
) {
|
|
970
|
+
atStatementBoundary = true;
|
|
971
|
+
} else if (character !== undefined && !/\s/u.test(character)) {
|
|
972
|
+
atStatementBoundary = false;
|
|
973
|
+
pendingControlParenthesis = false;
|
|
974
|
+
}
|
|
975
|
+
if (character === ")") {
|
|
976
|
+
canStartRegex = closedControlHeader;
|
|
977
|
+
} else if (character === "]" || character === "}") {
|
|
978
|
+
canStartRegex = false;
|
|
979
|
+
} else if (character !== undefined && !/\s/u.test(character)) {
|
|
980
|
+
canStartRegex = character !== "." && character !== "?";
|
|
981
|
+
}
|
|
982
|
+
index += 1;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
if (parenthesisDepth !== 0 || bracketDepth !== 0 || braceDepth !== 0) {
|
|
986
|
+
throw new Error("Meta Relay bundle contained malformed JavaScript");
|
|
987
|
+
}
|
|
988
|
+
return ids;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Resolve current registered-operation evidence from already-fetched,
|
|
993
|
+
* first-party bundle text. Duplicate evidence may agree only across bundles.
|
|
994
|
+
*/
|
|
995
|
+
export function resolveMetaRelayOperationRevision(
|
|
996
|
+
bundleTextsValue: unknown,
|
|
997
|
+
friendlyNameValue: unknown,
|
|
998
|
+
): MetaRelayOperationRevision {
|
|
999
|
+
const friendlyName = exactFriendlyName(friendlyNameValue);
|
|
1000
|
+
const moduleName = `${friendlyName}_facebookRelayOperation`;
|
|
1001
|
+
const values = denseArray(bundleTextsValue, "Meta Relay bundle list", MAX_BUNDLES);
|
|
1002
|
+
const texts: string[] = [];
|
|
1003
|
+
let totalBytes = 0;
|
|
1004
|
+
|
|
1005
|
+
for (const value of values) {
|
|
1006
|
+
const { text, bytes } = boundedText(
|
|
1007
|
+
value,
|
|
1008
|
+
MAX_BUNDLE_BYTES,
|
|
1009
|
+
"Meta Relay bundle",
|
|
1010
|
+
);
|
|
1011
|
+
totalBytes += bytes;
|
|
1012
|
+
if (totalBytes > MAX_TOTAL_BUNDLE_BYTES) {
|
|
1013
|
+
throw new Error("Meta Relay bundles exceeded their reviewed aggregate byte bound");
|
|
1014
|
+
}
|
|
1015
|
+
texts.push(text);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const ids: string[] = [];
|
|
1019
|
+
for (const text of texts) {
|
|
1020
|
+
const matches = targetModuleDocIds(text, moduleName);
|
|
1021
|
+
if (matches[0] !== undefined) ids.push(matches[0]);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
if (ids.length < 1) {
|
|
1025
|
+
throw new Error("Meta Relay bundles omitted the exact reviewed operation module");
|
|
1026
|
+
}
|
|
1027
|
+
if (new Set(ids).size !== 1) {
|
|
1028
|
+
throw new Error("Meta Relay bundles contained ambiguous registered-operation revisions");
|
|
1029
|
+
}
|
|
1030
|
+
const docId = ids[0];
|
|
1031
|
+
if (docId === undefined) {
|
|
1032
|
+
throw new Error("Meta Relay bundles omitted the exact reviewed operation module");
|
|
1033
|
+
}
|
|
1034
|
+
return Object.freeze({
|
|
1035
|
+
schemaVersion: 1,
|
|
1036
|
+
friendlyName,
|
|
1037
|
+
moduleName,
|
|
1038
|
+
docId,
|
|
1039
|
+
agreeingBundleCount: ids.length,
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
export function resolveMetaRelayDocId(
|
|
1044
|
+
bundleTextsValue: unknown,
|
|
1045
|
+
friendlyNameValue: unknown,
|
|
1046
|
+
): string {
|
|
1047
|
+
return resolveMetaRelayOperationRevision(bundleTextsValue, friendlyNameValue).docId;
|
|
1048
|
+
}
|
|
1049
|
+
import ts from "typescript";
|