@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,1043 @@
|
|
|
1
|
+
import {
|
|
2
|
+
metaRelayRequestProofCoordinates,
|
|
3
|
+
type MetaRelayRequest,
|
|
4
|
+
} from "./meta-web-descriptors";
|
|
5
|
+
|
|
6
|
+
const MAX_JSON_ROOTS = 512;
|
|
7
|
+
const MAX_TREE_NODES = 250_000;
|
|
8
|
+
const MAX_TREE_DEPTH = 40;
|
|
9
|
+
const MAX_CONTAINER_ENTRIES = 10_000;
|
|
10
|
+
const MAX_STRING_CHARACTERS = 4 * 1024 * 1024;
|
|
11
|
+
const MAX_MODULE_ID = 1_000_000;
|
|
12
|
+
const MAX_REQUEST_COUNTER = (36 ** 6) - 1;
|
|
13
|
+
const FACEBOOK_CURRENT_USER_ASYNC_KEY_PATTERN =
|
|
14
|
+
/^adp_WebWorkerV2HasteResponsePreloader_[A-Za-z0-9_]{1,192}$/u;
|
|
15
|
+
|
|
16
|
+
const REVIEWED_MODULE_NAMES = Object.freeze([
|
|
17
|
+
"CurrentUserInitialData",
|
|
18
|
+
"RelayAPIConfigDefaults",
|
|
19
|
+
"DTSGInitialData",
|
|
20
|
+
"SprinkleConfig",
|
|
21
|
+
"LSD",
|
|
22
|
+
"SiteData",
|
|
23
|
+
] as const);
|
|
24
|
+
|
|
25
|
+
type ReviewedModuleName = (typeof REVIEWED_MODULE_NAMES)[number];
|
|
26
|
+
type JsonRecord = Readonly<Record<string, unknown>>;
|
|
27
|
+
|
|
28
|
+
export type ParseMetaJsonScripts = (html: unknown) => unknown;
|
|
29
|
+
|
|
30
|
+
export type MetaCometFieldClass =
|
|
31
|
+
| "identity"
|
|
32
|
+
| "csrf-proof"
|
|
33
|
+
| "derived-proof"
|
|
34
|
+
| "bootstrap-proof"
|
|
35
|
+
| "build"
|
|
36
|
+
| "request-counter";
|
|
37
|
+
|
|
38
|
+
export type MetaCometFieldLifetime =
|
|
39
|
+
| "browser-session"
|
|
40
|
+
| "bootstrap"
|
|
41
|
+
| "build"
|
|
42
|
+
| "request";
|
|
43
|
+
|
|
44
|
+
export type MetaCometFieldClassification = {
|
|
45
|
+
readonly name:
|
|
46
|
+
| "viewerId"
|
|
47
|
+
| "actingId"
|
|
48
|
+
| "fb_dtsg"
|
|
49
|
+
| "jazoest"
|
|
50
|
+
| "lsd"
|
|
51
|
+
| "__rev"
|
|
52
|
+
| "__hsi"
|
|
53
|
+
| "__comet_req"
|
|
54
|
+
| "__req";
|
|
55
|
+
readonly class: MetaCometFieldClass;
|
|
56
|
+
readonly source:
|
|
57
|
+
| "bootstrap.viewer"
|
|
58
|
+
| "bootstrap.actor"
|
|
59
|
+
| "bootstrap.fb_dtsg"
|
|
60
|
+
| "derived.fb_dtsg-jazoest"
|
|
61
|
+
| "bootstrap.lsd"
|
|
62
|
+
| "bootstrap.client-revision"
|
|
63
|
+
| "bootstrap.hsi"
|
|
64
|
+
| "bootstrap.comet-environment"
|
|
65
|
+
| "session.request-counter";
|
|
66
|
+
readonly sinks: readonly (
|
|
67
|
+
| "access.viewer-id"
|
|
68
|
+
| "form.__user"
|
|
69
|
+
| "access.actor-id"
|
|
70
|
+
| "form.av"
|
|
71
|
+
| "form.fb_dtsg"
|
|
72
|
+
| "form.jazoest"
|
|
73
|
+
| "form.lsd"
|
|
74
|
+
| "form.__rev"
|
|
75
|
+
| "form.__hsi"
|
|
76
|
+
| "form.__comet_req"
|
|
77
|
+
| "form.__req"
|
|
78
|
+
)[];
|
|
79
|
+
readonly lifetime: MetaCometFieldLifetime;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function classification(
|
|
83
|
+
value: MetaCometFieldClassification,
|
|
84
|
+
): MetaCometFieldClassification {
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
...value,
|
|
87
|
+
sinks: Object.freeze([...value.sinks]),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const META_COMET_FIELD_CLASSIFICATIONS = Object.freeze([
|
|
92
|
+
classification({
|
|
93
|
+
name: "viewerId",
|
|
94
|
+
class: "identity",
|
|
95
|
+
source: "bootstrap.viewer",
|
|
96
|
+
sinks: ["access.viewer-id", "form.__user"],
|
|
97
|
+
lifetime: "browser-session",
|
|
98
|
+
}),
|
|
99
|
+
classification({
|
|
100
|
+
name: "actingId",
|
|
101
|
+
class: "identity",
|
|
102
|
+
source: "bootstrap.actor",
|
|
103
|
+
sinks: ["access.actor-id", "form.av"],
|
|
104
|
+
lifetime: "browser-session",
|
|
105
|
+
}),
|
|
106
|
+
classification({
|
|
107
|
+
name: "fb_dtsg",
|
|
108
|
+
class: "csrf-proof",
|
|
109
|
+
source: "bootstrap.fb_dtsg",
|
|
110
|
+
sinks: ["form.fb_dtsg"],
|
|
111
|
+
lifetime: "bootstrap",
|
|
112
|
+
}),
|
|
113
|
+
classification({
|
|
114
|
+
name: "jazoest",
|
|
115
|
+
class: "derived-proof",
|
|
116
|
+
source: "derived.fb_dtsg-jazoest",
|
|
117
|
+
sinks: ["form.jazoest"],
|
|
118
|
+
lifetime: "bootstrap",
|
|
119
|
+
}),
|
|
120
|
+
classification({
|
|
121
|
+
name: "lsd",
|
|
122
|
+
class: "bootstrap-proof",
|
|
123
|
+
source: "bootstrap.lsd",
|
|
124
|
+
sinks: ["form.lsd"],
|
|
125
|
+
lifetime: "bootstrap",
|
|
126
|
+
}),
|
|
127
|
+
classification({
|
|
128
|
+
name: "__rev",
|
|
129
|
+
class: "build",
|
|
130
|
+
source: "bootstrap.client-revision",
|
|
131
|
+
sinks: ["form.__rev"],
|
|
132
|
+
lifetime: "build",
|
|
133
|
+
}),
|
|
134
|
+
classification({
|
|
135
|
+
name: "__hsi",
|
|
136
|
+
class: "build",
|
|
137
|
+
source: "bootstrap.hsi",
|
|
138
|
+
sinks: ["form.__hsi"],
|
|
139
|
+
lifetime: "bootstrap",
|
|
140
|
+
}),
|
|
141
|
+
classification({
|
|
142
|
+
name: "__comet_req",
|
|
143
|
+
class: "build",
|
|
144
|
+
source: "bootstrap.comet-environment",
|
|
145
|
+
sinks: ["form.__comet_req"],
|
|
146
|
+
lifetime: "build",
|
|
147
|
+
}),
|
|
148
|
+
classification({
|
|
149
|
+
name: "__req",
|
|
150
|
+
class: "request-counter",
|
|
151
|
+
source: "session.request-counter",
|
|
152
|
+
sinks: ["form.__req"],
|
|
153
|
+
lifetime: "request",
|
|
154
|
+
}),
|
|
155
|
+
] as const satisfies readonly MetaCometFieldClassification[]);
|
|
156
|
+
|
|
157
|
+
export type MetaCometBootstrapEvidence = {
|
|
158
|
+
readonly schemaVersion: 1;
|
|
159
|
+
readonly provider: "facebook-comet";
|
|
160
|
+
readonly identityBound: true;
|
|
161
|
+
readonly fields: readonly MetaCometFieldClassification[];
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const BOOTSTRAP_EVIDENCE: MetaCometBootstrapEvidence = Object.freeze({
|
|
165
|
+
schemaVersion: 1,
|
|
166
|
+
provider: "facebook-comet",
|
|
167
|
+
identityBound: true,
|
|
168
|
+
fields: META_COMET_FIELD_CLASSIFICATIONS,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export type MetaCometBootstrap = {
|
|
172
|
+
readonly viewerId: string;
|
|
173
|
+
readonly actingId: string;
|
|
174
|
+
readonly evidence: MetaCometBootstrapEvidence;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export type MetaCometRequestFieldName =
|
|
178
|
+
| "__user"
|
|
179
|
+
| "av"
|
|
180
|
+
| "fb_dtsg"
|
|
181
|
+
| "jazoest"
|
|
182
|
+
| "lsd"
|
|
183
|
+
| "__rev"
|
|
184
|
+
| "__hsi"
|
|
185
|
+
| "__comet_req"
|
|
186
|
+
| "__req";
|
|
187
|
+
|
|
188
|
+
export type MetaCometRequestProofEvidence = {
|
|
189
|
+
readonly schemaVersion: 1;
|
|
190
|
+
readonly provider: "facebook-comet";
|
|
191
|
+
readonly redaction: "raw-values-omitted";
|
|
192
|
+
readonly fields: readonly {
|
|
193
|
+
readonly name: MetaCometRequestFieldName;
|
|
194
|
+
readonly class: MetaCometFieldClass;
|
|
195
|
+
}[];
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const REQUEST_PROOF_EVIDENCE: MetaCometRequestProofEvidence = Object.freeze({
|
|
199
|
+
schemaVersion: 1,
|
|
200
|
+
provider: "facebook-comet",
|
|
201
|
+
redaction: "raw-values-omitted",
|
|
202
|
+
fields: Object.freeze([
|
|
203
|
+
Object.freeze({ name: "__user", class: "identity" }),
|
|
204
|
+
Object.freeze({ name: "av", class: "identity" }),
|
|
205
|
+
Object.freeze({ name: "fb_dtsg", class: "csrf-proof" }),
|
|
206
|
+
Object.freeze({ name: "jazoest", class: "derived-proof" }),
|
|
207
|
+
Object.freeze({ name: "lsd", class: "bootstrap-proof" }),
|
|
208
|
+
Object.freeze({ name: "__rev", class: "build" }),
|
|
209
|
+
Object.freeze({ name: "__hsi", class: "build" }),
|
|
210
|
+
Object.freeze({ name: "__comet_req", class: "build" }),
|
|
211
|
+
Object.freeze({ name: "__req", class: "request-counter" }),
|
|
212
|
+
]),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
declare const META_COMET_REQUEST_PROOF: unique symbol;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* A request-scoped handle. Its only public property is redacted evidence; raw
|
|
219
|
+
* values live in a module-private WeakMap and can cross only the explicit
|
|
220
|
+
* one-use network-request sink below.
|
|
221
|
+
*/
|
|
222
|
+
export type MetaCometRequestProof = {
|
|
223
|
+
readonly evidence: MetaCometRequestProofEvidence;
|
|
224
|
+
readonly [META_COMET_REQUEST_PROOF]: true;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
type BootstrapMaterial = {
|
|
228
|
+
readonly viewerId: string;
|
|
229
|
+
readonly actingId: string;
|
|
230
|
+
readonly fbDtsg: string;
|
|
231
|
+
readonly jazoest: string;
|
|
232
|
+
readonly lsd: string;
|
|
233
|
+
readonly revision: string;
|
|
234
|
+
readonly hsi: string;
|
|
235
|
+
readonly cometRequest: string;
|
|
236
|
+
nextRequestCounter: number;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
type RequestMaterial = {
|
|
240
|
+
readonly fields: readonly (readonly [MetaCometRequestFieldName, string])[];
|
|
241
|
+
readonly request: MetaRelayRequest;
|
|
242
|
+
consumed: boolean;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const bootstrapMaterial = new WeakMap<MetaCometBootstrap, BootstrapMaterial>();
|
|
246
|
+
const requestMaterial = new WeakMap<MetaCometRequestProof, RequestMaterial>();
|
|
247
|
+
const consumedRequests = new WeakSet<MetaRelayRequest>();
|
|
248
|
+
const reviewedModuleNameSet = new Set<string>(REVIEWED_MODULE_NAMES);
|
|
249
|
+
|
|
250
|
+
const PAYLOAD_FIELDS = Object.freeze({
|
|
251
|
+
CurrentUserInitialData: Object.freeze([
|
|
252
|
+
"ACCOUNT_ID",
|
|
253
|
+
"USER_ID",
|
|
254
|
+
"NAME",
|
|
255
|
+
"SHORT_NAME",
|
|
256
|
+
"IS_BUSINESS_PERSON_ACCOUNT",
|
|
257
|
+
"HAS_SECONDARY_BUSINESS_PERSON",
|
|
258
|
+
"IS_FACEBOOK_WORK_ACCOUNT",
|
|
259
|
+
"IS_MESSENGER_ONLY_USER",
|
|
260
|
+
"IS_DEACTIVATED_ALLOWED_ON_MESSENGER",
|
|
261
|
+
"IS_MESSENGER_CALL_GUEST_USER",
|
|
262
|
+
"IS_WORK_MESSENGER_CALL_GUEST_USER",
|
|
263
|
+
"IS_WORKROOMS_USER",
|
|
264
|
+
"APP_ID",
|
|
265
|
+
"IS_BUSINESS_DOMAIN",
|
|
266
|
+
"IS_INSTAGRAM_BUSINESS_PERSON",
|
|
267
|
+
"IS_WABA_BUSINESS_PERSON",
|
|
268
|
+
]),
|
|
269
|
+
RelayAPIConfigDefaults: Object.freeze([
|
|
270
|
+
"accessToken",
|
|
271
|
+
"actorID",
|
|
272
|
+
"customHeaders",
|
|
273
|
+
"enableNetworkLogger",
|
|
274
|
+
"enableVerboseNetworkLogger",
|
|
275
|
+
"fetchTimeout",
|
|
276
|
+
"graphBatchURI",
|
|
277
|
+
"graphURI",
|
|
278
|
+
"retryDelays",
|
|
279
|
+
"useXController",
|
|
280
|
+
"xhrEncoding",
|
|
281
|
+
"subscriptionTopicURI",
|
|
282
|
+
"withCredentials",
|
|
283
|
+
"isProductionEndpoint",
|
|
284
|
+
"workRequestTaggingProduct",
|
|
285
|
+
"encryptionKeyParams",
|
|
286
|
+
]),
|
|
287
|
+
DTSGInitialData: Object.freeze(["token", "async_get_token"]),
|
|
288
|
+
SprinkleConfig: Object.freeze(["param_name", "version", "should_randomize"]),
|
|
289
|
+
LSD: Object.freeze(["token"]),
|
|
290
|
+
SiteData: Object.freeze([
|
|
291
|
+
"server_revision",
|
|
292
|
+
"client_revision",
|
|
293
|
+
"tier",
|
|
294
|
+
"push_phase",
|
|
295
|
+
"pkg_cohort",
|
|
296
|
+
"haste_session",
|
|
297
|
+
"pr",
|
|
298
|
+
"haste_site",
|
|
299
|
+
"manifest_base_uri",
|
|
300
|
+
"manifest_origin",
|
|
301
|
+
"manifest_version_prefix",
|
|
302
|
+
"be_one_ahead",
|
|
303
|
+
"is_rtl",
|
|
304
|
+
"is_comet",
|
|
305
|
+
"is_experimental_tier",
|
|
306
|
+
"is_jit_warmed_up",
|
|
307
|
+
"hsi",
|
|
308
|
+
"semr_host_bucket",
|
|
309
|
+
"bl_hash_version",
|
|
310
|
+
"skip_rd_bl",
|
|
311
|
+
"comet_env",
|
|
312
|
+
"wbloks_env",
|
|
313
|
+
"ef_page",
|
|
314
|
+
"compose_bootloads",
|
|
315
|
+
"spin",
|
|
316
|
+
"__spin_r",
|
|
317
|
+
"__spin_b",
|
|
318
|
+
"__spin_t",
|
|
319
|
+
"__spin_dev_mhenv",
|
|
320
|
+
"vip",
|
|
321
|
+
"polytrace_id",
|
|
322
|
+
]),
|
|
323
|
+
} as const satisfies Readonly<Record<ReviewedModuleName, readonly string[]>>);
|
|
324
|
+
|
|
325
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
326
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function isUnknownArray(value: unknown): value is readonly unknown[] {
|
|
330
|
+
return Array.isArray(value);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function record(value: unknown, label: string): JsonRecord {
|
|
334
|
+
if (!isRecord(value)) throw new Error(`${label} must be an object`);
|
|
335
|
+
const prototype = Object.getPrototypeOf(value) as unknown;
|
|
336
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
337
|
+
throw new Error(`${label} must be plain JSON`);
|
|
338
|
+
}
|
|
339
|
+
return value;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function dataRecordEntries(value: JsonRecord, label: string): readonly (readonly [string, unknown])[] {
|
|
343
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
344
|
+
if (ownKeys.length > MAX_CONTAINER_ENTRIES) {
|
|
345
|
+
throw new Error(`${label} exceeded its reviewed field bound`);
|
|
346
|
+
}
|
|
347
|
+
const entries: (readonly [string, unknown])[] = [];
|
|
348
|
+
for (const key of ownKeys) {
|
|
349
|
+
if (typeof key !== "string") throw new Error(`${label} contained unsupported fields`);
|
|
350
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
351
|
+
if (
|
|
352
|
+
descriptor === undefined
|
|
353
|
+
|| !Object.hasOwn(descriptor, "value")
|
|
354
|
+
|| descriptor.enumerable !== true
|
|
355
|
+
) throw new Error(`${label} must contain only plain JSON fields`);
|
|
356
|
+
entries.push(Object.freeze([key, descriptor.value]));
|
|
357
|
+
}
|
|
358
|
+
return entries;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function dataArrayValues(value: readonly unknown[], label: string): readonly unknown[] {
|
|
362
|
+
if (value.length > MAX_CONTAINER_ENTRIES) {
|
|
363
|
+
throw new Error(`${label} exceeded its reviewed entry bound`);
|
|
364
|
+
}
|
|
365
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
366
|
+
if (
|
|
367
|
+
ownKeys.length !== value.length + 1
|
|
368
|
+
|| ownKeys.some((key) =>
|
|
369
|
+
typeof key !== "string"
|
|
370
|
+
|| (key !== "length" && !/^(?:0|[1-9][0-9]*)$/u.test(key)))
|
|
371
|
+
) throw new Error(`${label} contained unsupported array fields`);
|
|
372
|
+
const values: unknown[] = [];
|
|
373
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
374
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
375
|
+
if (
|
|
376
|
+
descriptor === undefined
|
|
377
|
+
|| !Object.hasOwn(descriptor, "value")
|
|
378
|
+
|| descriptor.enumerable !== true
|
|
379
|
+
) throw new Error(`${label} must be a dense plain JSON array`);
|
|
380
|
+
values.push(descriptor.value);
|
|
381
|
+
}
|
|
382
|
+
return values;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function exactInputKeys(
|
|
386
|
+
value: JsonRecord,
|
|
387
|
+
required: readonly string[],
|
|
388
|
+
optional: readonly string[],
|
|
389
|
+
label: string,
|
|
390
|
+
): void {
|
|
391
|
+
const keys = dataRecordEntries(value, label).map(([key]) => key);
|
|
392
|
+
const allowed = new Set([...required, ...optional]);
|
|
393
|
+
if (
|
|
394
|
+
required.some((key) => !keys.includes(key))
|
|
395
|
+
|| keys.some((key) => !allowed.has(key))
|
|
396
|
+
) throw new Error(`${label} has unsupported fields`);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function validatePayloadFields(
|
|
400
|
+
moduleName: ReviewedModuleName,
|
|
401
|
+
payload: JsonRecord,
|
|
402
|
+
): void {
|
|
403
|
+
const allowed = new Set<string>(PAYLOAD_FIELDS[moduleName]);
|
|
404
|
+
const keys = dataRecordEntries(payload, `Facebook Comet ${moduleName} payload`).map(
|
|
405
|
+
([key]) => key,
|
|
406
|
+
);
|
|
407
|
+
if (keys.some((key) => !allowed.has(key))) {
|
|
408
|
+
throw new Error(`Facebook Comet ${moduleName} payload has unsupported fields`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function decimalId(value: unknown, label: string): string {
|
|
413
|
+
if (typeof value !== "string" || !/^[1-9][0-9]{0,31}$/u.test(value)) {
|
|
414
|
+
throw new Error(`${label} must be a bounded nonzero decimal identifier`);
|
|
415
|
+
}
|
|
416
|
+
return value;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function proofToken(
|
|
420
|
+
value: unknown,
|
|
421
|
+
label: string,
|
|
422
|
+
maximumCharacters: number,
|
|
423
|
+
): string {
|
|
424
|
+
if (
|
|
425
|
+
typeof value !== "string"
|
|
426
|
+
|| value.length < 8
|
|
427
|
+
|| value.length > maximumCharacters
|
|
428
|
+
|| !/^[A-Za-z0-9._~:-]+$/u.test(value)
|
|
429
|
+
) throw new Error(`${label} is missing or malformed`);
|
|
430
|
+
return value;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function boundedPositiveInteger(
|
|
434
|
+
value: unknown,
|
|
435
|
+
label: string,
|
|
436
|
+
maximum: number,
|
|
437
|
+
): number {
|
|
438
|
+
if (!Number.isSafeInteger(value) || (value as number) < 1 || (value as number) > maximum) {
|
|
439
|
+
throw new Error(`${label} must be a bounded positive integer`);
|
|
440
|
+
}
|
|
441
|
+
return value as number;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function directModulePathIsReviewed(path: readonly (string | number)[]): boolean {
|
|
445
|
+
return (
|
|
446
|
+
path.length === 3
|
|
447
|
+
&& typeof path[0] === "number"
|
|
448
|
+
&& (path[1] === "define" || path[1] === "require")
|
|
449
|
+
&& typeof path[2] === "number"
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function scheduledServerModulePathIsReviewed(
|
|
454
|
+
roots: readonly unknown[],
|
|
455
|
+
path: readonly (string | number)[],
|
|
456
|
+
moduleValue: readonly unknown[],
|
|
457
|
+
): boolean {
|
|
458
|
+
if (
|
|
459
|
+
path.length !== 8
|
|
460
|
+
|| typeof path[0] !== "number"
|
|
461
|
+
|| path[1] !== "require"
|
|
462
|
+
|| typeof path[2] !== "number"
|
|
463
|
+
|| path[3] !== 3
|
|
464
|
+
|| typeof path[4] !== "number"
|
|
465
|
+
|| path[5] !== "__bbox"
|
|
466
|
+
|| (path[6] !== "define" && path[6] !== "require")
|
|
467
|
+
|| typeof path[7] !== "number"
|
|
468
|
+
) return false;
|
|
469
|
+
|
|
470
|
+
const root = roots[path[0]];
|
|
471
|
+
if (!isRecord(root)) return false;
|
|
472
|
+
const outerContainer = root.require;
|
|
473
|
+
if (!isUnknownArray(outerContainer)) return false;
|
|
474
|
+
const scheduledValue = outerContainer[path[2]];
|
|
475
|
+
if (!isUnknownArray(scheduledValue)) return false;
|
|
476
|
+
const scheduled = dataArrayValues(
|
|
477
|
+
scheduledValue,
|
|
478
|
+
"Facebook Comet ScheduledServerJS module",
|
|
479
|
+
);
|
|
480
|
+
if (
|
|
481
|
+
scheduled.length !== 4
|
|
482
|
+
|| scheduled[0] !== "ScheduledServerJS"
|
|
483
|
+
|| scheduled[1] !== "handle"
|
|
484
|
+
|| scheduled[2] !== null
|
|
485
|
+
|| !isUnknownArray(scheduled[3])
|
|
486
|
+
) return false;
|
|
487
|
+
const payload = scheduled[3][path[4]];
|
|
488
|
+
if (!isRecord(payload) || !isRecord(payload.__bbox)) return false;
|
|
489
|
+
const innerContainer = payload.__bbox[path[6]];
|
|
490
|
+
return (
|
|
491
|
+
isUnknownArray(innerContainer)
|
|
492
|
+
&& innerContainer[path[7]] === moduleValue
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function hydratedAsyncDataModulePathIsReviewed(
|
|
497
|
+
roots: readonly unknown[],
|
|
498
|
+
path: readonly (string | number)[],
|
|
499
|
+
moduleValue: readonly unknown[],
|
|
500
|
+
): boolean {
|
|
501
|
+
if (
|
|
502
|
+
path.length !== 16
|
|
503
|
+
|| typeof path[0] !== "number"
|
|
504
|
+
|| path[1] !== "require"
|
|
505
|
+
|| typeof path[2] !== "number"
|
|
506
|
+
|| path[3] !== 3
|
|
507
|
+
|| typeof path[4] !== "number"
|
|
508
|
+
|| path[5] !== "__bbox"
|
|
509
|
+
|| path[6] !== "require"
|
|
510
|
+
|| typeof path[7] !== "number"
|
|
511
|
+
|| path[8] !== 3
|
|
512
|
+
|| typeof path[9] !== "number"
|
|
513
|
+
|| path[10] !== "data"
|
|
514
|
+
|| path[11] !== "__bbox"
|
|
515
|
+
|| path[12] !== "hrp"
|
|
516
|
+
|| path[13] !== "jsmods"
|
|
517
|
+
|| path[14] !== "define"
|
|
518
|
+
|| typeof path[15] !== "number"
|
|
519
|
+
) return false;
|
|
520
|
+
|
|
521
|
+
const root = roots[path[0]];
|
|
522
|
+
if (!isRecord(root) || !isUnknownArray(root.require)) return false;
|
|
523
|
+
const scheduledValue = root.require[path[2]];
|
|
524
|
+
if (
|
|
525
|
+
!isUnknownArray(scheduledValue)
|
|
526
|
+
|| scheduledValue.length !== 4
|
|
527
|
+
|| scheduledValue[0] !== "ScheduledServerJS"
|
|
528
|
+
|| scheduledValue[1] !== "handle"
|
|
529
|
+
|| scheduledValue[2] !== null
|
|
530
|
+
|| !isUnknownArray(scheduledValue[3])
|
|
531
|
+
) return false;
|
|
532
|
+
const scheduledPayload = scheduledValue[3][path[4]];
|
|
533
|
+
if (
|
|
534
|
+
!isRecord(scheduledPayload)
|
|
535
|
+
|| !isRecord(scheduledPayload.__bbox)
|
|
536
|
+
|| !isUnknownArray(scheduledPayload.__bbox.require)
|
|
537
|
+
) return false;
|
|
538
|
+
const asyncValue = scheduledPayload.__bbox.require[path[7]];
|
|
539
|
+
if (
|
|
540
|
+
!isUnknownArray(asyncValue)
|
|
541
|
+
|| asyncValue.length !== 4
|
|
542
|
+
|| asyncValue[0] !== "AsyncData"
|
|
543
|
+
|| asyncValue[1] !== "resolve"
|
|
544
|
+
|| !isUnknownArray(asyncValue[2])
|
|
545
|
+
|| asyncValue[2].length !== 0
|
|
546
|
+
|| !isUnknownArray(asyncValue[3])
|
|
547
|
+
|| asyncValue[3].length !== 2
|
|
548
|
+
|| typeof asyncValue[3][0] !== "string"
|
|
549
|
+
|| !FACEBOOK_CURRENT_USER_ASYNC_KEY_PATTERN.test(asyncValue[3][0])
|
|
550
|
+
) return false;
|
|
551
|
+
const asyncPayload = asyncValue[3][path[9]];
|
|
552
|
+
if (!isRecord(asyncPayload) || !isRecord(asyncPayload.data)) return false;
|
|
553
|
+
const dataBbox = asyncPayload.data.__bbox;
|
|
554
|
+
if (!isRecord(dataBbox) || !isRecord(dataBbox.hrp)) return false;
|
|
555
|
+
const jsmods = dataBbox.hrp.jsmods;
|
|
556
|
+
return (
|
|
557
|
+
isRecord(jsmods)
|
|
558
|
+
&& isUnknownArray(jsmods.define)
|
|
559
|
+
&& jsmods.define[path[15]] === moduleValue
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function modulePathIsReviewed(
|
|
564
|
+
roots: readonly unknown[],
|
|
565
|
+
path: readonly (string | number)[],
|
|
566
|
+
moduleValue: readonly unknown[],
|
|
567
|
+
): boolean {
|
|
568
|
+
return (
|
|
569
|
+
directModulePathIsReviewed(path)
|
|
570
|
+
|| scheduledServerModulePathIsReviewed(roots, path, moduleValue)
|
|
571
|
+
|| hydratedAsyncDataModulePathIsReviewed(roots, path, moduleValue)
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function reviewedModuleTuple(
|
|
576
|
+
roots: readonly unknown[],
|
|
577
|
+
value: readonly unknown[],
|
|
578
|
+
path: readonly (string | number)[],
|
|
579
|
+
moduleName: ReviewedModuleName,
|
|
580
|
+
): JsonRecord {
|
|
581
|
+
if (!modulePathIsReviewed(roots, path, value)) {
|
|
582
|
+
throw new Error(`Facebook Comet ${moduleName} appeared outside a reviewed module path`);
|
|
583
|
+
}
|
|
584
|
+
const items = dataArrayValues(value, `Facebook Comet ${moduleName} module`);
|
|
585
|
+
if (
|
|
586
|
+
items.length !== 4
|
|
587
|
+
|| items[0] !== moduleName
|
|
588
|
+
|| !Array.isArray(items[1])
|
|
589
|
+
|| dataArrayValues(items[1], `Facebook Comet ${moduleName} dependencies`).length !== 0
|
|
590
|
+
|| !Number.isSafeInteger(items[3])
|
|
591
|
+
|| (items[3] as number) < 0
|
|
592
|
+
|| (items[3] as number) > MAX_MODULE_ID
|
|
593
|
+
) throw new Error(`Facebook Comet ${moduleName} module is malformed`);
|
|
594
|
+
const payload = record(items[2], `Facebook Comet ${moduleName} payload`);
|
|
595
|
+
validatePayloadFields(moduleName, payload);
|
|
596
|
+
return payload;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function collectReviewedModules(roots: readonly unknown[]): ReadonlyMap<ReviewedModuleName, JsonRecord> {
|
|
600
|
+
const rootsWithModules = new Map<number, Map<ReviewedModuleName, JsonRecord>>();
|
|
601
|
+
const seen = new WeakSet<object>();
|
|
602
|
+
const stack: {
|
|
603
|
+
readonly value: unknown;
|
|
604
|
+
readonly path: readonly (string | number)[];
|
|
605
|
+
readonly depth: number;
|
|
606
|
+
}[] = roots.map((value, index) => ({
|
|
607
|
+
value,
|
|
608
|
+
path: Object.freeze([index]),
|
|
609
|
+
depth: 0,
|
|
610
|
+
}));
|
|
611
|
+
let nodes = 0;
|
|
612
|
+
|
|
613
|
+
while (stack.length > 0) {
|
|
614
|
+
const current = stack.pop();
|
|
615
|
+
if (current === undefined) break;
|
|
616
|
+
nodes += 1;
|
|
617
|
+
if (nodes > MAX_TREE_NODES) {
|
|
618
|
+
throw new Error("Facebook Comet bootstrap exceeded its reviewed structural bound");
|
|
619
|
+
}
|
|
620
|
+
if (current.depth > MAX_TREE_DEPTH) {
|
|
621
|
+
throw new Error("Facebook Comet bootstrap exceeded its reviewed depth bound");
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const value = current.value;
|
|
625
|
+
if (value === null || typeof value === "boolean") continue;
|
|
626
|
+
if (typeof value === "string") {
|
|
627
|
+
if (value.length > MAX_STRING_CHARACTERS) {
|
|
628
|
+
throw new Error("Facebook Comet bootstrap contained oversized text");
|
|
629
|
+
}
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
if (typeof value === "number") {
|
|
633
|
+
if (!Number.isFinite(value)) throw new Error("Facebook Comet bootstrap was not plain JSON");
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (typeof value !== "object") {
|
|
637
|
+
throw new Error("Facebook Comet bootstrap was not plain JSON");
|
|
638
|
+
}
|
|
639
|
+
if (seen.has(value)) {
|
|
640
|
+
throw new Error("Facebook Comet bootstrap contained shared or cyclic structures");
|
|
641
|
+
}
|
|
642
|
+
seen.add(value);
|
|
643
|
+
|
|
644
|
+
if (Array.isArray(value)) {
|
|
645
|
+
const values = dataArrayValues(value, "Facebook Comet bootstrap array");
|
|
646
|
+
const possibleName = values[0];
|
|
647
|
+
if (typeof possibleName === "string" && reviewedModuleNameSet.has(possibleName)) {
|
|
648
|
+
const moduleName = REVIEWED_MODULE_NAMES.find((name) => name === possibleName);
|
|
649
|
+
if (moduleName === undefined) throw new Error("Facebook Comet module name was not reviewed");
|
|
650
|
+
const rootIndex = current.path[0];
|
|
651
|
+
if (typeof rootIndex !== "number") {
|
|
652
|
+
throw new Error("Facebook Comet module root was malformed");
|
|
653
|
+
}
|
|
654
|
+
const found = rootsWithModules.get(rootIndex) ?? new Map<ReviewedModuleName, JsonRecord>();
|
|
655
|
+
if (found.has(moduleName)) {
|
|
656
|
+
throw new Error(`Facebook Comet bootstrap contained duplicate ${moduleName} modules`);
|
|
657
|
+
}
|
|
658
|
+
found.set(moduleName, reviewedModuleTuple(roots, value, current.path, moduleName));
|
|
659
|
+
rootsWithModules.set(rootIndex, found);
|
|
660
|
+
}
|
|
661
|
+
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
662
|
+
stack.push({
|
|
663
|
+
value: values[index],
|
|
664
|
+
path: Object.freeze([...current.path, index]),
|
|
665
|
+
depth: current.depth + 1,
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const entries = dataRecordEntries(
|
|
672
|
+
record(value, "Facebook Comet bootstrap object"),
|
|
673
|
+
"Facebook Comet bootstrap object",
|
|
674
|
+
);
|
|
675
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
676
|
+
const entry = entries[index];
|
|
677
|
+
if (entry === undefined) continue;
|
|
678
|
+
stack.push({
|
|
679
|
+
value: entry[1],
|
|
680
|
+
path: Object.freeze([...current.path, entry[0]]),
|
|
681
|
+
depth: current.depth + 1,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const anchoredRoots = [...rootsWithModules.values()].filter((modules) =>
|
|
687
|
+
modules.has("RelayAPIConfigDefaults"));
|
|
688
|
+
if (anchoredRoots.length > 1) {
|
|
689
|
+
throw new Error("Facebook Comet bootstrap contained multiple RelayAPIConfigDefaults anchor roots");
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Facebook may hydrate later pagelets with fresh DTSG/LSD/SiteData copies.
|
|
693
|
+
// RelayAPIConfigDefaults appears once in the primary bootstrap root, so it
|
|
694
|
+
// anchors one coherent request-proof set without mixing values across roots.
|
|
695
|
+
const selected = anchoredRoots[0]
|
|
696
|
+
?? (rootsWithModules.size === 1 ? [...rootsWithModules.values()][0] : undefined)
|
|
697
|
+
?? new Map<ReviewedModuleName, JsonRecord>();
|
|
698
|
+
const selectedUser = selected.get("CurrentUserInitialData");
|
|
699
|
+
if (selectedUser !== undefined) {
|
|
700
|
+
for (const modules of rootsWithModules.values()) {
|
|
701
|
+
const candidate = modules.get("CurrentUserInitialData");
|
|
702
|
+
if (
|
|
703
|
+
candidate !== undefined
|
|
704
|
+
&& (
|
|
705
|
+
candidate.ACCOUNT_ID !== selectedUser.ACCOUNT_ID
|
|
706
|
+
|| candidate.USER_ID !== selectedUser.USER_ID
|
|
707
|
+
)
|
|
708
|
+
) throw new Error("Facebook Comet bootstrap identities drifted across hydrated roots");
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return selected;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function requiredModule(
|
|
715
|
+
modules: ReadonlyMap<ReviewedModuleName, JsonRecord>,
|
|
716
|
+
name: ReviewedModuleName,
|
|
717
|
+
): JsonRecord {
|
|
718
|
+
const payload = modules.get(name);
|
|
719
|
+
if (payload === undefined) throw new Error(`Facebook Comet bootstrap omitted ${name}`);
|
|
720
|
+
return payload;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Bind a read-only Comet HTML projection to the exact active Relay actor
|
|
725
|
+
* without materializing any request proofs. The caller remains responsible for
|
|
726
|
+
* binding the independently parsed CurrentUser identity and semantic target.
|
|
727
|
+
*/
|
|
728
|
+
export function assertMetaCometReadActor(
|
|
729
|
+
roots: readonly unknown[],
|
|
730
|
+
expectedViewerId: string,
|
|
731
|
+
): void {
|
|
732
|
+
if (roots.length < 1 || roots.length > MAX_JSON_ROOTS) {
|
|
733
|
+
throw new Error("Facebook Comet read actor roots were not a bounded list");
|
|
734
|
+
}
|
|
735
|
+
const expected = decimalId(
|
|
736
|
+
expectedViewerId,
|
|
737
|
+
"Facebook Comet expected read viewer",
|
|
738
|
+
);
|
|
739
|
+
const modules = collectReviewedModules(roots);
|
|
740
|
+
const actingId = decimalId(
|
|
741
|
+
requiredModule(modules, "RelayAPIConfigDefaults").actorID,
|
|
742
|
+
"Facebook Comet RelayAPIConfigDefaults.actorID",
|
|
743
|
+
);
|
|
744
|
+
if (actingId !== expected) {
|
|
745
|
+
throw new Error("Facebook Comet read actor did not match the bound viewer");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function deriveJazoest(fbDtsg: string, sprinkle: JsonRecord): string {
|
|
750
|
+
if (
|
|
751
|
+
sprinkle.param_name !== "jazoest"
|
|
752
|
+
|| sprinkle.version !== 2
|
|
753
|
+
|| sprinkle.should_randomize !== false
|
|
754
|
+
) throw new Error("Facebook Comet SprinkleConfig changed its reviewed jazoest derivation");
|
|
755
|
+
let sum = 0;
|
|
756
|
+
for (let index = 0; index < fbDtsg.length; index += 1) {
|
|
757
|
+
sum += fbDtsg.charCodeAt(index);
|
|
758
|
+
}
|
|
759
|
+
const value = `2${sum}`;
|
|
760
|
+
if (!/^2[0-9]{2,10}$/u.test(value)) {
|
|
761
|
+
throw new Error("Facebook Comet jazoest derivation exceeded its reviewed bound");
|
|
762
|
+
}
|
|
763
|
+
return value;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function isParseMetaJsonScripts(value: unknown): value is ParseMetaJsonScripts {
|
|
767
|
+
return typeof value === "function";
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function parseOptions(value: unknown): {
|
|
771
|
+
readonly parseMetaJsonScripts: ParseMetaJsonScripts;
|
|
772
|
+
readonly expectedViewerId: string | null;
|
|
773
|
+
readonly expectedActingId: string | null;
|
|
774
|
+
} {
|
|
775
|
+
const input = record(value, "Facebook Comet bootstrap options");
|
|
776
|
+
exactInputKeys(
|
|
777
|
+
input,
|
|
778
|
+
["parseMetaJsonScripts"],
|
|
779
|
+
["expectedViewerId", "expectedActingId"],
|
|
780
|
+
"Facebook Comet bootstrap options",
|
|
781
|
+
);
|
|
782
|
+
const parser = input.parseMetaJsonScripts;
|
|
783
|
+
if (!isParseMetaJsonScripts(parser)) {
|
|
784
|
+
throw new Error("Facebook Comet bootstrap parser must be a function");
|
|
785
|
+
}
|
|
786
|
+
return Object.freeze({
|
|
787
|
+
parseMetaJsonScripts: parser,
|
|
788
|
+
expectedViewerId: input.expectedViewerId === undefined
|
|
789
|
+
? null
|
|
790
|
+
: decimalId(input.expectedViewerId, "Facebook Comet expected viewer"),
|
|
791
|
+
expectedActingId: input.expectedActingId === undefined
|
|
792
|
+
? null
|
|
793
|
+
: decimalId(input.expectedActingId, "Facebook Comet expected actor"),
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Parse one inert root-HTML snapshot through an injected JSON-script parser.
|
|
799
|
+
* This function performs no network, browser, clock, random, or filesystem I/O.
|
|
800
|
+
*/
|
|
801
|
+
export function bootstrapMetaComet(
|
|
802
|
+
html: unknown,
|
|
803
|
+
options: unknown,
|
|
804
|
+
): MetaCometBootstrap {
|
|
805
|
+
const parsedOptions = parseOptions(options);
|
|
806
|
+
let parsed: unknown;
|
|
807
|
+
try {
|
|
808
|
+
parsed = parsedOptions.parseMetaJsonScripts(html);
|
|
809
|
+
} catch {
|
|
810
|
+
throw new Error("Facebook Comet bootstrap JSON parsing failed");
|
|
811
|
+
}
|
|
812
|
+
if (
|
|
813
|
+
!Array.isArray(parsed)
|
|
814
|
+
|| parsed.length < 1
|
|
815
|
+
|| parsed.length > MAX_JSON_ROOTS
|
|
816
|
+
) throw new Error("Facebook Comet bootstrap parser returned a malformed bounded root list");
|
|
817
|
+
const roots = dataArrayValues(parsed, "Facebook Comet bootstrap roots");
|
|
818
|
+
const modules = collectReviewedModules(roots);
|
|
819
|
+
|
|
820
|
+
const currentUser = requiredModule(modules, "CurrentUserInitialData");
|
|
821
|
+
const accountId = decimalId(
|
|
822
|
+
currentUser.ACCOUNT_ID,
|
|
823
|
+
"Facebook Comet CurrentUserInitialData.ACCOUNT_ID",
|
|
824
|
+
);
|
|
825
|
+
const userId = decimalId(
|
|
826
|
+
currentUser.USER_ID,
|
|
827
|
+
"Facebook Comet CurrentUserInitialData.USER_ID",
|
|
828
|
+
);
|
|
829
|
+
if (accountId !== userId) {
|
|
830
|
+
throw new Error("Facebook Comet bootstrap viewer identities did not agree");
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const relay = requiredModule(modules, "RelayAPIConfigDefaults");
|
|
834
|
+
const actingId = decimalId(
|
|
835
|
+
relay.actorID,
|
|
836
|
+
"Facebook Comet RelayAPIConfigDefaults.actorID",
|
|
837
|
+
);
|
|
838
|
+
if (actingId !== userId) {
|
|
839
|
+
throw new Error("Facebook Comet bootstrap viewer and actor did not agree");
|
|
840
|
+
}
|
|
841
|
+
if (
|
|
842
|
+
parsedOptions.expectedViewerId !== null
|
|
843
|
+
&& parsedOptions.expectedViewerId !== userId
|
|
844
|
+
) throw new Error("Facebook Comet bootstrap viewer did not match the expected identity");
|
|
845
|
+
if (
|
|
846
|
+
parsedOptions.expectedActingId !== null
|
|
847
|
+
&& parsedOptions.expectedActingId !== actingId
|
|
848
|
+
) throw new Error("Facebook Comet bootstrap actor did not match the expected identity");
|
|
849
|
+
|
|
850
|
+
const dtsg = requiredModule(modules, "DTSGInitialData");
|
|
851
|
+
const fbDtsg = proofToken(
|
|
852
|
+
dtsg.token,
|
|
853
|
+
"Facebook Comet DTSGInitialData.token",
|
|
854
|
+
4_096,
|
|
855
|
+
);
|
|
856
|
+
const lsd = proofToken(
|
|
857
|
+
requiredModule(modules, "LSD").token,
|
|
858
|
+
"Facebook Comet LSD.token",
|
|
859
|
+
512,
|
|
860
|
+
);
|
|
861
|
+
const jazoest = deriveJazoest(
|
|
862
|
+
fbDtsg,
|
|
863
|
+
requiredModule(modules, "SprinkleConfig"),
|
|
864
|
+
);
|
|
865
|
+
|
|
866
|
+
const siteData = requiredModule(modules, "SiteData");
|
|
867
|
+
if (
|
|
868
|
+
siteData.wbloks_env !== false
|
|
869
|
+
|| (
|
|
870
|
+
siteData.is_comet !== undefined
|
|
871
|
+
&& siteData.is_comet !== true
|
|
872
|
+
)
|
|
873
|
+
) {
|
|
874
|
+
throw new Error("Facebook Comet SiteData did not describe a reviewed Comet environment");
|
|
875
|
+
}
|
|
876
|
+
const revision = String(boundedPositiveInteger(
|
|
877
|
+
siteData.client_revision,
|
|
878
|
+
"Facebook Comet SiteData.client_revision",
|
|
879
|
+
Number.MAX_SAFE_INTEGER,
|
|
880
|
+
));
|
|
881
|
+
const hsi = decimalId(siteData.hsi, "Facebook Comet SiteData.hsi");
|
|
882
|
+
const cometRequest = String(boundedPositiveInteger(
|
|
883
|
+
siteData.comet_env,
|
|
884
|
+
"Facebook Comet SiteData.comet_env",
|
|
885
|
+
999,
|
|
886
|
+
));
|
|
887
|
+
|
|
888
|
+
const bootstrap: MetaCometBootstrap = Object.freeze({
|
|
889
|
+
viewerId: userId,
|
|
890
|
+
actingId,
|
|
891
|
+
evidence: BOOTSTRAP_EVIDENCE,
|
|
892
|
+
});
|
|
893
|
+
bootstrapMaterial.set(bootstrap, {
|
|
894
|
+
viewerId: userId,
|
|
895
|
+
actingId,
|
|
896
|
+
fbDtsg,
|
|
897
|
+
jazoest,
|
|
898
|
+
lsd,
|
|
899
|
+
revision,
|
|
900
|
+
hsi,
|
|
901
|
+
cometRequest,
|
|
902
|
+
nextRequestCounter: 1,
|
|
903
|
+
});
|
|
904
|
+
return bootstrap;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function requireBootstrapMaterial(bootstrap: MetaCometBootstrap): BootstrapMaterial {
|
|
908
|
+
const material = bootstrapMaterial.get(bootstrap);
|
|
909
|
+
if (material === undefined) {
|
|
910
|
+
throw new Error("Facebook Comet bootstrap handle is invalid");
|
|
911
|
+
}
|
|
912
|
+
return material;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function takeRequestCounter(material: BootstrapMaterial): string {
|
|
916
|
+
const current = material.nextRequestCounter;
|
|
917
|
+
if (!Number.isSafeInteger(current) || current < 1 || current > MAX_REQUEST_COUNTER) {
|
|
918
|
+
throw new Error("Facebook Comet request counter exhausted its reviewed range");
|
|
919
|
+
}
|
|
920
|
+
material.nextRequestCounter += 1;
|
|
921
|
+
return current.toString(36);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
export function nextMetaCometRequestCounter(
|
|
925
|
+
bootstrap: MetaCometBootstrap,
|
|
926
|
+
): string {
|
|
927
|
+
return takeRequestCounter(requireBootstrapMaterial(bootstrap));
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function requestField(
|
|
931
|
+
name: MetaCometRequestFieldName,
|
|
932
|
+
value: string,
|
|
933
|
+
): readonly [MetaCometRequestFieldName, string] {
|
|
934
|
+
return Object.freeze([name, value]);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
export function materializeMetaCometRequestProof(
|
|
938
|
+
bootstrap: MetaCometBootstrap,
|
|
939
|
+
request: MetaRelayRequest,
|
|
940
|
+
): MetaCometRequestProof {
|
|
941
|
+
const material = requireBootstrapMaterial(bootstrap);
|
|
942
|
+
const coordinates = metaRelayRequestProofCoordinates(request);
|
|
943
|
+
if (
|
|
944
|
+
coordinates.viewerId !== material.viewerId
|
|
945
|
+
|| coordinates.actorId !== material.actingId
|
|
946
|
+
) {
|
|
947
|
+
throw new Error("Facebook Comet request proof did not match its request access coordinates");
|
|
948
|
+
}
|
|
949
|
+
const fieldNames: MetaCometRequestFieldName[] = [];
|
|
950
|
+
for (const name of coordinates.proofFormFields) {
|
|
951
|
+
if (
|
|
952
|
+
!REQUEST_PROOF_EVIDENCE.fields.some((field) => field.name === name)
|
|
953
|
+
|| fieldNames.includes(name)
|
|
954
|
+
) {
|
|
955
|
+
throw new Error("Facebook Comet request proof fields did not match its descriptor");
|
|
956
|
+
}
|
|
957
|
+
fieldNames.push(name);
|
|
958
|
+
}
|
|
959
|
+
const valueFor = (name: MetaCometRequestFieldName): string => {
|
|
960
|
+
switch (name) {
|
|
961
|
+
case "__user": return material.viewerId;
|
|
962
|
+
case "av": return material.actingId;
|
|
963
|
+
case "fb_dtsg": return material.fbDtsg;
|
|
964
|
+
case "jazoest": return material.jazoest;
|
|
965
|
+
case "lsd": return material.lsd;
|
|
966
|
+
case "__rev": return material.revision;
|
|
967
|
+
case "__hsi": return material.hsi;
|
|
968
|
+
case "__comet_req": return material.cometRequest;
|
|
969
|
+
case "__req": return takeRequestCounter(material);
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
const evidence: MetaCometRequestProofEvidence = Object.freeze({
|
|
973
|
+
...REQUEST_PROOF_EVIDENCE,
|
|
974
|
+
fields: Object.freeze(REQUEST_PROOF_EVIDENCE.fields.filter((field) =>
|
|
975
|
+
fieldNames.includes(field.name))),
|
|
976
|
+
});
|
|
977
|
+
const proof = Object.freeze({
|
|
978
|
+
evidence,
|
|
979
|
+
}) as MetaCometRequestProof;
|
|
980
|
+
requestMaterial.set(proof, {
|
|
981
|
+
fields: Object.freeze(fieldNames.map((name) =>
|
|
982
|
+
requestField(name, valueFor(name)))),
|
|
983
|
+
request,
|
|
984
|
+
consumed: false,
|
|
985
|
+
});
|
|
986
|
+
return proof;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
export type MetaCometNetworkRequestSink = {
|
|
990
|
+
readonly sink: "network-request";
|
|
991
|
+
readonly write: (name: MetaCometRequestFieldName, value: string) => void;
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
function isMetaCometNetworkWriter(
|
|
995
|
+
value: unknown,
|
|
996
|
+
): value is MetaCometNetworkRequestSink["write"] {
|
|
997
|
+
return typeof value === "function";
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Move raw proof values once into the immediate network-request builder.
|
|
1002
|
+
* Errors are deliberately replaced with a value-free message.
|
|
1003
|
+
*/
|
|
1004
|
+
export function consumeMetaCometRequestProof(
|
|
1005
|
+
proof: MetaCometRequestProof,
|
|
1006
|
+
request: MetaRelayRequest,
|
|
1007
|
+
sinkValue: unknown,
|
|
1008
|
+
): MetaCometRequestProofEvidence {
|
|
1009
|
+
const material = requestMaterial.get(proof);
|
|
1010
|
+
if (material === undefined) {
|
|
1011
|
+
throw new Error("Facebook Comet request-proof handle is invalid");
|
|
1012
|
+
}
|
|
1013
|
+
if (material.consumed) {
|
|
1014
|
+
throw new Error("Facebook Comet request proof was already consumed");
|
|
1015
|
+
}
|
|
1016
|
+
metaRelayRequestProofCoordinates(request);
|
|
1017
|
+
if (material.request !== request) {
|
|
1018
|
+
throw new Error("Facebook Comet request proof did not match its request handle");
|
|
1019
|
+
}
|
|
1020
|
+
if (consumedRequests.has(request)) {
|
|
1021
|
+
throw new Error("Facebook Comet request handle was already consumed");
|
|
1022
|
+
}
|
|
1023
|
+
const sink = record(sinkValue, "Facebook Comet request-proof sink");
|
|
1024
|
+
exactInputKeys(
|
|
1025
|
+
sink,
|
|
1026
|
+
["sink", "write"],
|
|
1027
|
+
[],
|
|
1028
|
+
"Facebook Comet request-proof sink",
|
|
1029
|
+
);
|
|
1030
|
+
const write = sink.write;
|
|
1031
|
+
if (sink.sink !== "network-request" || !isMetaCometNetworkWriter(write)) {
|
|
1032
|
+
throw new Error("Facebook Comet raw proof may flow only to the network-request sink");
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
material.consumed = true;
|
|
1036
|
+
consumedRequests.add(request);
|
|
1037
|
+
try {
|
|
1038
|
+
for (const [name, value] of material.fields) write(name, value);
|
|
1039
|
+
} catch {
|
|
1040
|
+
throw new Error("Facebook Comet request-proof network sink failed");
|
|
1041
|
+
}
|
|
1042
|
+
return proof.evidence;
|
|
1043
|
+
}
|