@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,91 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Either from "effect/Either";
|
|
3
|
+
import * as Exit from "effect/Exit";
|
|
4
|
+
import type { PortableOperationIdentityV1 } from "./provider-plugin-portable-identity";
|
|
5
|
+
import {
|
|
6
|
+
acquirePortableProviderPluginInvocationLease,
|
|
7
|
+
createPortableProviderPluginInvocationLeaseContainmentController,
|
|
8
|
+
releasePortableProviderPluginInvocationLease
|
|
9
|
+
} from "./provider-plugin-invocation-lease";
|
|
10
|
+
import { settlePortableProviderPluginCleanup } from "./provider-plugin-cleanup-barrier";
|
|
11
|
+
import {
|
|
12
|
+
acquireWebSessionCleanupAdmission,
|
|
13
|
+
WebSessionCleanupAdmissionBlockedError,
|
|
14
|
+
type WebSessionCleanupAdmissionIdentity
|
|
15
|
+
} from "./web-session-cleanup-admission";
|
|
16
|
+
import type { WebSessionCleanupBarrierRegistrar } from "./web-session-execution";
|
|
17
|
+
import { readAttempt, type ReadEffectFailure } from "./read-effect";
|
|
18
|
+
import { readNative } from "./read-effect-platform";
|
|
19
|
+
import { runReadEffect } from "./read-effect-runtime";
|
|
20
|
+
|
|
21
|
+
type Environment = Readonly<Record<string, string | undefined>>;
|
|
22
|
+
|
|
23
|
+
/** Exact kernel/content custody stays synchronous. The program owns closing
|
|
24
|
+
* registration, joining every registered barrier, and releasing only a proven
|
|
25
|
+
* cleanup-complete controller. An unsafe controller remains durable. */
|
|
26
|
+
export async function withReadCleanupAdmission<A>(
|
|
27
|
+
identity: WebSessionCleanupAdmissionIdentity,
|
|
28
|
+
environment: Environment,
|
|
29
|
+
operation: (register: WebSessionCleanupBarrierRegistrar) => Promise<A>,
|
|
30
|
+
acquiredAt: Date | undefined,
|
|
31
|
+
onBlocked: (error: WebSessionCleanupAdmissionBlockedError) => Promise<A>
|
|
32
|
+
|
|
33
|
+
): Promise<A> {
|
|
34
|
+
const program: Effect.Effect<A, ReadEffectFailure> = Effect.gen(function*() {
|
|
35
|
+
const acquired = yield* Effect.either(readAttempt(() => acquireWebSessionCleanupAdmission(identity, environment, acquiredAt)));
|
|
36
|
+
if (Either.isLeft(acquired)) {
|
|
37
|
+
const cause = acquired.left.cause;
|
|
38
|
+
if (cause instanceof WebSessionCleanupAdmissionBlockedError) return yield* readNative(() => onBlocked(cause));
|
|
39
|
+
return yield* Effect.fail(acquired.left);
|
|
40
|
+
}
|
|
41
|
+
const admission = acquired.right;
|
|
42
|
+
const settlement = yield* Effect.exit(Effect.gen(function*() {
|
|
43
|
+
const outcome = yield* Effect.exit(readNative(() => operation(admission.registerCleanupBarrier)));
|
|
44
|
+
yield* readAttempt(admission.closeRegistration);
|
|
45
|
+
const barriers = yield* Effect.all(
|
|
46
|
+
admission.barriers.map(barrier => Effect.either(readNative(() => barrier))),
|
|
47
|
+
{ concurrency: "unbounded" }
|
|
48
|
+
);
|
|
49
|
+
yield* readAttempt(() => {
|
|
50
|
+
if (barriers.some(Either.isLeft)) admission.cleanupUnsafe();
|
|
51
|
+
else { admission.cleanupComplete(); admission.release(); }
|
|
52
|
+
});
|
|
53
|
+
return yield* outcome;
|
|
54
|
+
}));
|
|
55
|
+
if (Exit.isFailure(settlement)) {
|
|
56
|
+
yield* readAttempt(() => {
|
|
57
|
+
admission.closeRegistration();
|
|
58
|
+
if (admission.current.claim.containment.status === "parent-owned") { admission.cleanupComplete(); admission.release(); }
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return yield* settlement;
|
|
62
|
+
}).pipe(Effect.uninterruptible);
|
|
63
|
+
return runReadEffect(program);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** AsyncLocalStorage in the native portable host retains descendant barrier
|
|
67
|
+
* registration. Its positive proof precedes the synchronous exact lease release;
|
|
68
|
+
* neither a failed operation nor Effect scope completion can substitute for it. */
|
|
69
|
+
export async function withPortableReadAdmission<A>(
|
|
70
|
+
identity: PortableOperationIdentityV1,
|
|
71
|
+
runId: string,
|
|
72
|
+
environment: Environment,
|
|
73
|
+
acquiredAt: Date | undefined,
|
|
74
|
+
operation: () => Promise<A>
|
|
75
|
+
|
|
76
|
+
): Promise<A> {
|
|
77
|
+
const program: Effect.Effect<A, ReadEffectFailure> = Effect.gen(function*() {
|
|
78
|
+
const containment = yield* readAttempt(() => createPortableProviderPluginInvocationLeaseContainmentController(
|
|
79
|
+
acquirePortableProviderPluginInvocationLease(identity, runId, environment, acquiredAt),
|
|
80
|
+
environment
|
|
81
|
+
));
|
|
82
|
+
const outcome = yield* readNative(() => settlePortableProviderPluginCleanup(
|
|
83
|
+
operation,
|
|
84
|
+
{ containment, cleanupComplete: containment.cleanupComplete }
|
|
85
|
+
));
|
|
86
|
+
yield* readAttempt(() => releasePortableProviderPluginInvocationLease(containment.current, environment, new Date()));
|
|
87
|
+
if (outcome.status === "rejected") return yield* readAttempt(() => { throw outcome.reason; });
|
|
88
|
+
return outcome.value;
|
|
89
|
+
}).pipe(Effect.uninterruptible);
|
|
90
|
+
return runReadEffect(program);
|
|
91
|
+
}
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import { canonicalJson, sha256 } from "./canonical-json";
|
|
2
|
+
import { loadAuthSnapshotIfPresent } from "./auth";
|
|
3
|
+
import {
|
|
4
|
+
createPortableProviderPluginCatalog,
|
|
5
|
+
} from "./provider-plugin-portable-catalog";
|
|
6
|
+
import type { ProviderPluginRegistry } from "./provider-plugin-registry";
|
|
7
|
+
import { providerPluginRegistry } from "./provider-plugins";
|
|
8
|
+
import {
|
|
9
|
+
ReadProjectionDurableRepairError,
|
|
10
|
+
READ_PROJECTION_TRANSITION_SETTLEMENT_WAIT_MS,
|
|
11
|
+
isReadProjectionCorruptionError,
|
|
12
|
+
publishReadProjection,
|
|
13
|
+
readReadProjection,
|
|
14
|
+
repairReadProjection,
|
|
15
|
+
removeReadProjection,
|
|
16
|
+
projectionAuthIdentityHash,
|
|
17
|
+
withSettledReadProjectionAuthAdmission,
|
|
18
|
+
type ReadProjectionCacheResult,
|
|
19
|
+
type ReadProjectionPublication,
|
|
20
|
+
type ReadProjectionQuery,
|
|
21
|
+
type ReadProjectionAdmissionSettlementOptions,
|
|
22
|
+
} from "./read-projections";
|
|
23
|
+
import {
|
|
24
|
+
isPublicWebSessionInvocationAuthority,
|
|
25
|
+
publicWebSessionAuthorityIdentityHash,
|
|
26
|
+
} from "./web-session-authentication-policy";
|
|
27
|
+
import {
|
|
28
|
+
createReadProjectionQueryForInvocation,
|
|
29
|
+
executeReadInvocation,
|
|
30
|
+
prepareInvocation,
|
|
31
|
+
type InvocationResult,
|
|
32
|
+
type PreparedInvocation,
|
|
33
|
+
} from "./runtime";
|
|
34
|
+
|
|
35
|
+
type Environment = Readonly<Record<string, string | undefined>>;
|
|
36
|
+
const MAX_FRESH_FOR_MS = 365 * 24 * 60 * 60 * 1_000;
|
|
37
|
+
|
|
38
|
+
export type CapabilityReadRequest = {
|
|
39
|
+
readonly adapterId: string;
|
|
40
|
+
readonly operationId: string;
|
|
41
|
+
readonly input?: unknown;
|
|
42
|
+
readonly authId?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type ReadCapabilityOptions = {
|
|
46
|
+
readonly environment?: Environment;
|
|
47
|
+
readonly registry?: ProviderPluginRegistry;
|
|
48
|
+
readonly freshForMs?: number;
|
|
49
|
+
readonly now?: Date;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type RevalidateCapabilityOptions = ReadCapabilityOptions & {
|
|
53
|
+
readonly headed?: boolean;
|
|
54
|
+
readonly signal?: AbortSignal;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ReadProjectionCacheOutcome =
|
|
58
|
+
| {
|
|
59
|
+
readonly status: "stored";
|
|
60
|
+
readonly publication: ReadProjectionPublication;
|
|
61
|
+
}
|
|
62
|
+
| {
|
|
63
|
+
readonly status: "retained";
|
|
64
|
+
readonly reason: "live-read-failed";
|
|
65
|
+
}
|
|
66
|
+
| {
|
|
67
|
+
readonly status: "miss";
|
|
68
|
+
readonly reason: "no-cached-snapshot";
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
readonly status: "skipped";
|
|
72
|
+
readonly reason: "auth-subject-unbound";
|
|
73
|
+
}
|
|
74
|
+
| {
|
|
75
|
+
readonly status: "error";
|
|
76
|
+
readonly message: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type RevalidatedCapability = {
|
|
80
|
+
readonly cachedBefore: ReadProjectionCacheResult | null;
|
|
81
|
+
readonly live: InvocationResult;
|
|
82
|
+
readonly cache: ReadProjectionCacheOutcome;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
type PreparedReadOptions = RevalidateCapabilityOptions & {
|
|
86
|
+
readonly registry: ProviderPluginRegistry;
|
|
87
|
+
readonly executeRead?: typeof executeReadInvocation;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function selectedRegistry(
|
|
91
|
+
environment: Environment,
|
|
92
|
+
registry: ProviderPluginRegistry | undefined,
|
|
93
|
+
): ProviderPluginRegistry {
|
|
94
|
+
return registry
|
|
95
|
+
?? createPortableProviderPluginCatalog(
|
|
96
|
+
providerPluginRegistry,
|
|
97
|
+
environment,
|
|
98
|
+
).registry;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function prepareCapability(
|
|
102
|
+
request: CapabilityReadRequest,
|
|
103
|
+
environment: Environment,
|
|
104
|
+
registry: ProviderPluginRegistry,
|
|
105
|
+
): PreparedInvocation {
|
|
106
|
+
return prepareInvocation(
|
|
107
|
+
request.adapterId,
|
|
108
|
+
request.operationId,
|
|
109
|
+
request.input === undefined ? {} : request.input,
|
|
110
|
+
request.authId,
|
|
111
|
+
environment,
|
|
112
|
+
registry,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function exactAuthHash(invocation: PreparedInvocation): string {
|
|
117
|
+
return sha256(canonicalJson(invocation.auth));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function withInvocationAuthorityAdmission<T>(
|
|
121
|
+
invocation: PreparedInvocation,
|
|
122
|
+
environment: Environment,
|
|
123
|
+
operation: () => T,
|
|
124
|
+
options: ReadProjectionAdmissionSettlementOptions = {},
|
|
125
|
+
): T {
|
|
126
|
+
return isPublicWebSessionInvocationAuthority(invocation.auth)
|
|
127
|
+
? operation()
|
|
128
|
+
: withSettledReadProjectionAuthAdmission(
|
|
129
|
+
invocation.auth.id,
|
|
130
|
+
environment,
|
|
131
|
+
operation,
|
|
132
|
+
options,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function liveReadDiscardedError(
|
|
137
|
+
invocation: PreparedInvocation,
|
|
138
|
+
cause?: unknown,
|
|
139
|
+
): Error {
|
|
140
|
+
return new Error(
|
|
141
|
+
`auth locator ${invocation.auth.id} changed while the live read was running; its result was discarded`,
|
|
142
|
+
cause === undefined ? undefined : { cause },
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function isOptionalAdmissionRewrite(error: unknown): boolean {
|
|
147
|
+
if (!(error instanceof Error)) return false;
|
|
148
|
+
return error.message.includes("could not safely open optional read projection admission")
|
|
149
|
+
&& error.message.includes("state file changed while it was read");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
type AuthRealmState = "matches" | "missing" | "changed";
|
|
153
|
+
|
|
154
|
+
function authRealmState(
|
|
155
|
+
invocation: PreparedInvocation,
|
|
156
|
+
query: ReadProjectionQuery | null,
|
|
157
|
+
environment: Environment,
|
|
158
|
+
): AuthRealmState {
|
|
159
|
+
if (isPublicWebSessionInvocationAuthority(invocation.auth)) {
|
|
160
|
+
const expected = publicWebSessionAuthorityIdentityHash(invocation.auth);
|
|
161
|
+
return invocation.readProjectionAuthIdentityHash === expected
|
|
162
|
+
&& (query === null || query.identity.auth.hash === expected)
|
|
163
|
+
? "matches"
|
|
164
|
+
: "changed";
|
|
165
|
+
}
|
|
166
|
+
const snapshot = loadAuthSnapshotIfPresent(
|
|
167
|
+
invocation.auth.id,
|
|
168
|
+
environment,
|
|
169
|
+
);
|
|
170
|
+
if (snapshot === null) return "missing";
|
|
171
|
+
const current = snapshot.auth;
|
|
172
|
+
const currentHash = sha256(canonicalJson(current));
|
|
173
|
+
const preparedAuthIdentityHash =
|
|
174
|
+
invocation.readProjectionAuthIdentityHash;
|
|
175
|
+
if (preparedAuthIdentityHash === undefined) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
"prepared invocation is missing its auth lifetime identity; prepare it again",
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if (
|
|
181
|
+
currentHash !== exactAuthHash(invocation)
|
|
182
|
+
|| current.subject !== invocation.auth.subject
|
|
183
|
+
) return "changed";
|
|
184
|
+
const currentAuthIdentityHash = projectionAuthIdentityHash(
|
|
185
|
+
current.id,
|
|
186
|
+
currentHash,
|
|
187
|
+
environment,
|
|
188
|
+
);
|
|
189
|
+
if (
|
|
190
|
+
currentAuthIdentityHash !== preparedAuthIdentityHash
|
|
191
|
+
|| (
|
|
192
|
+
query !== null
|
|
193
|
+
&& query.identity.auth.hash !== preparedAuthIdentityHash
|
|
194
|
+
)
|
|
195
|
+
) return "changed";
|
|
196
|
+
return "matches";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function requireCurrentAuthRealm(
|
|
200
|
+
invocation: PreparedInvocation,
|
|
201
|
+
query: ReadProjectionQuery | null,
|
|
202
|
+
environment: Environment,
|
|
203
|
+
): void {
|
|
204
|
+
if (authRealmState(invocation, query, environment) !== "matches") {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`auth locator ${invocation.auth.id} changed since this invocation was prepared`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function validateReadOptions(options: ReadCapabilityOptions): void {
|
|
212
|
+
if (
|
|
213
|
+
options.now !== undefined
|
|
214
|
+
&& (
|
|
215
|
+
!(options.now instanceof Date)
|
|
216
|
+
|| !Number.isFinite(options.now.getTime())
|
|
217
|
+
)
|
|
218
|
+
) throw new Error("read projection observation time is invalid");
|
|
219
|
+
if (
|
|
220
|
+
options.freshForMs !== undefined
|
|
221
|
+
&& (
|
|
222
|
+
typeof options.freshForMs !== "number"
|
|
223
|
+
|| !Number.isSafeInteger(options.freshForMs)
|
|
224
|
+
|| options.freshForMs < 0
|
|
225
|
+
|| options.freshForMs > MAX_FRESH_FOR_MS
|
|
226
|
+
)
|
|
227
|
+
) throw new Error("read projection freshness window is malformed");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function boundedErrorMessage(error: unknown): string {
|
|
231
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
+
return Buffer.byteLength(message, "utf8") <= 8 * 1024
|
|
233
|
+
? message
|
|
234
|
+
: `${Buffer.from(message, "utf8").subarray(0, 8 * 1024).toString("utf8")}…`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function readCachedPreparedCapability(
|
|
238
|
+
invocation: PreparedInvocation,
|
|
239
|
+
options: ReadCapabilityOptions & { readonly registry: ProviderPluginRegistry },
|
|
240
|
+
): ReadProjectionCacheResult {
|
|
241
|
+
const environment = options.environment ?? process.env;
|
|
242
|
+
validateReadOptions(options);
|
|
243
|
+
return withInvocationAuthorityAdmission(
|
|
244
|
+
invocation,
|
|
245
|
+
environment,
|
|
246
|
+
() => {
|
|
247
|
+
const query = createReadProjectionQueryForInvocation(
|
|
248
|
+
invocation,
|
|
249
|
+
environment,
|
|
250
|
+
options.registry,
|
|
251
|
+
);
|
|
252
|
+
if (authRealmState(invocation, query, environment) !== "matches") {
|
|
253
|
+
return Object.freeze({ status: "miss" as const, key: query.key });
|
|
254
|
+
}
|
|
255
|
+
const cached = readReadProjection(query, {
|
|
256
|
+
environment,
|
|
257
|
+
...(options.now === undefined ? {} : { now: options.now }),
|
|
258
|
+
...(options.freshForMs === undefined
|
|
259
|
+
? {}
|
|
260
|
+
: { freshForMs: options.freshForMs }),
|
|
261
|
+
});
|
|
262
|
+
if (authRealmState(invocation, query, environment) !== "matches") {
|
|
263
|
+
return Object.freeze({ status: "miss" as const, key: query.key });
|
|
264
|
+
}
|
|
265
|
+
return cached;
|
|
266
|
+
},
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function readCachedCapability(
|
|
271
|
+
request: CapabilityReadRequest,
|
|
272
|
+
options: ReadCapabilityOptions = {},
|
|
273
|
+
): ReadProjectionCacheResult {
|
|
274
|
+
const environment = options.environment ?? process.env;
|
|
275
|
+
const registry = selectedRegistry(environment, options.registry);
|
|
276
|
+
return readCachedPreparedCapability(
|
|
277
|
+
prepareCapability(request, environment, registry),
|
|
278
|
+
{ ...options, environment, registry },
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export async function revalidatePreparedCapability(
|
|
283
|
+
invocation: PreparedInvocation,
|
|
284
|
+
options: PreparedReadOptions,
|
|
285
|
+
cachedBeforeOverride?: ReadProjectionCacheResult | null,
|
|
286
|
+
): Promise<RevalidatedCapability> {
|
|
287
|
+
const environment = options.environment ?? process.env;
|
|
288
|
+
validateReadOptions(options);
|
|
289
|
+
const query = withInvocationAuthorityAdmission(
|
|
290
|
+
invocation,
|
|
291
|
+
environment,
|
|
292
|
+
() => {
|
|
293
|
+
const prepared = invocation.auth.subject === undefined
|
|
294
|
+
? null
|
|
295
|
+
: createReadProjectionQueryForInvocation(
|
|
296
|
+
invocation,
|
|
297
|
+
environment,
|
|
298
|
+
options.registry,
|
|
299
|
+
);
|
|
300
|
+
requireCurrentAuthRealm(invocation, prepared, environment);
|
|
301
|
+
return prepared;
|
|
302
|
+
},
|
|
303
|
+
);
|
|
304
|
+
let cacheReadError: unknown = null;
|
|
305
|
+
let cachedBefore: ReadProjectionCacheResult | null;
|
|
306
|
+
try {
|
|
307
|
+
cachedBefore = cachedBeforeOverride !== undefined
|
|
308
|
+
? cachedBeforeOverride
|
|
309
|
+
: query === null
|
|
310
|
+
? null
|
|
311
|
+
: readCachedPreparedCapability(invocation, {
|
|
312
|
+
environment,
|
|
313
|
+
registry: options.registry,
|
|
314
|
+
...(options.now === undefined ? {} : { now: options.now }),
|
|
315
|
+
...(options.freshForMs === undefined
|
|
316
|
+
? {}
|
|
317
|
+
: { freshForMs: options.freshForMs }),
|
|
318
|
+
});
|
|
319
|
+
} catch (error) {
|
|
320
|
+
withInvocationAuthorityAdmission(
|
|
321
|
+
invocation,
|
|
322
|
+
environment,
|
|
323
|
+
() => requireCurrentAuthRealm(invocation, query, environment),
|
|
324
|
+
);
|
|
325
|
+
cacheReadError = error;
|
|
326
|
+
cachedBefore = null;
|
|
327
|
+
}
|
|
328
|
+
withInvocationAuthorityAdmission(
|
|
329
|
+
invocation,
|
|
330
|
+
environment,
|
|
331
|
+
() => requireCurrentAuthRealm(invocation, query, environment),
|
|
332
|
+
);
|
|
333
|
+
const live = await (options.executeRead ?? executeReadInvocation)(
|
|
334
|
+
invocation,
|
|
335
|
+
{
|
|
336
|
+
headed: options.headed ?? false,
|
|
337
|
+
environment,
|
|
338
|
+
registry: options.registry,
|
|
339
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
340
|
+
},
|
|
341
|
+
);
|
|
342
|
+
try {
|
|
343
|
+
return withInvocationAuthorityAdmission(
|
|
344
|
+
invocation,
|
|
345
|
+
environment,
|
|
346
|
+
() => {
|
|
347
|
+
if (authRealmState(invocation, query, environment) !== "matches") {
|
|
348
|
+
throw liveReadDiscardedError(invocation);
|
|
349
|
+
}
|
|
350
|
+
if (query === null) {
|
|
351
|
+
return Object.freeze({
|
|
352
|
+
cachedBefore,
|
|
353
|
+
live,
|
|
354
|
+
cache: Object.freeze({
|
|
355
|
+
status: "skipped" as const,
|
|
356
|
+
reason: "auth-subject-unbound" as const,
|
|
357
|
+
}),
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
if (live.receipt.status !== "succeeded") {
|
|
361
|
+
if (cacheReadError !== null) {
|
|
362
|
+
return Object.freeze({
|
|
363
|
+
cachedBefore,
|
|
364
|
+
live,
|
|
365
|
+
cache: Object.freeze({
|
|
366
|
+
status: "error" as const,
|
|
367
|
+
message: boundedErrorMessage(cacheReadError),
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
if (cachedBefore?.status !== "hit") {
|
|
372
|
+
return Object.freeze({
|
|
373
|
+
cachedBefore,
|
|
374
|
+
live,
|
|
375
|
+
cache: Object.freeze({
|
|
376
|
+
status: "miss" as const,
|
|
377
|
+
reason: "no-cached-snapshot" as const,
|
|
378
|
+
}),
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
return Object.freeze({
|
|
382
|
+
cachedBefore,
|
|
383
|
+
live,
|
|
384
|
+
cache: Object.freeze({
|
|
385
|
+
status: "retained" as const,
|
|
386
|
+
reason: "live-read-failed" as const,
|
|
387
|
+
}),
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
const publicationOptions = {
|
|
391
|
+
environment,
|
|
392
|
+
runId: live.receipt.runId,
|
|
393
|
+
startedAt: live.receipt.startedAt,
|
|
394
|
+
finishedAt: live.receipt.finishedAt,
|
|
395
|
+
} as const;
|
|
396
|
+
let publication: ReadProjectionPublication;
|
|
397
|
+
try {
|
|
398
|
+
if (isReadProjectionCorruptionError(cacheReadError)) {
|
|
399
|
+
publication = repairReadProjection(
|
|
400
|
+
query,
|
|
401
|
+
live.output,
|
|
402
|
+
{
|
|
403
|
+
...publicationOptions,
|
|
404
|
+
corruption: cacheReadError,
|
|
405
|
+
observedBeforeLive: true,
|
|
406
|
+
},
|
|
407
|
+
);
|
|
408
|
+
} else {
|
|
409
|
+
try {
|
|
410
|
+
publication = publishReadProjection(
|
|
411
|
+
query,
|
|
412
|
+
live.output,
|
|
413
|
+
publicationOptions,
|
|
414
|
+
);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
if (!isReadProjectionCorruptionError(error)) throw error;
|
|
417
|
+
publication = repairReadProjection(
|
|
418
|
+
query,
|
|
419
|
+
live.output,
|
|
420
|
+
{
|
|
421
|
+
...publicationOptions,
|
|
422
|
+
corruption: error,
|
|
423
|
+
observedBeforeLive: false,
|
|
424
|
+
},
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (
|
|
430
|
+
error instanceof ReadProjectionDurableRepairError
|
|
431
|
+
&& error.queryKey === query.key
|
|
432
|
+
) {
|
|
433
|
+
publication = error.publication;
|
|
434
|
+
} else {
|
|
435
|
+
return Object.freeze({
|
|
436
|
+
cachedBefore,
|
|
437
|
+
live,
|
|
438
|
+
cache: Object.freeze({
|
|
439
|
+
status: "error" as const,
|
|
440
|
+
message: boundedErrorMessage(error),
|
|
441
|
+
}),
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (authRealmState(invocation, query, environment) !== "matches") {
|
|
446
|
+
removeReadProjection(query, environment);
|
|
447
|
+
throw new Error(
|
|
448
|
+
`auth locator ${invocation.auth.id} changed while the live read was being published; its result was discarded`,
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
return Object.freeze({
|
|
452
|
+
cachedBefore,
|
|
453
|
+
live,
|
|
454
|
+
cache: Object.freeze({
|
|
455
|
+
status: "stored" as const,
|
|
456
|
+
publication,
|
|
457
|
+
}),
|
|
458
|
+
});
|
|
459
|
+
},
|
|
460
|
+
{ maximumWaitMs: READ_PROJECTION_TRANSITION_SETTLEMENT_WAIT_MS },
|
|
461
|
+
);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
if (isOptionalAdmissionRewrite(error)) {
|
|
464
|
+
throw liveReadDiscardedError(invocation, error);
|
|
465
|
+
}
|
|
466
|
+
throw error;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export async function revalidateCapability(
|
|
471
|
+
request: CapabilityReadRequest,
|
|
472
|
+
options: RevalidateCapabilityOptions = {},
|
|
473
|
+
): Promise<RevalidatedCapability> {
|
|
474
|
+
const environment = options.environment ?? process.env;
|
|
475
|
+
const registry = selectedRegistry(environment, options.registry);
|
|
476
|
+
return revalidatePreparedCapability(
|
|
477
|
+
prepareCapability(request, environment, registry),
|
|
478
|
+
{ ...options, environment, registry },
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Return the current exact snapshot synchronously and start one explicit live
|
|
484
|
+
* R1 revalidation. UI callers can render `cached` before awaiting
|
|
485
|
+
* `revalidation`; Ghostget never hides a transport refresh in a cache lookup.
|
|
486
|
+
*/
|
|
487
|
+
export function staleWhileRevalidateCapability(
|
|
488
|
+
request: CapabilityReadRequest,
|
|
489
|
+
options: RevalidateCapabilityOptions = {},
|
|
490
|
+
): {
|
|
491
|
+
readonly cached: ReadProjectionCacheResult | null;
|
|
492
|
+
readonly revalidation: Promise<RevalidatedCapability>;
|
|
493
|
+
} {
|
|
494
|
+
const environment = options.environment ?? process.env;
|
|
495
|
+
const registry = selectedRegistry(environment, options.registry);
|
|
496
|
+
const invocation = prepareCapability(request, environment, registry);
|
|
497
|
+
validateReadOptions(options);
|
|
498
|
+
let cached: ReadProjectionCacheResult | null = null;
|
|
499
|
+
let cacheReadCompleted = false;
|
|
500
|
+
if (invocation.auth.subject !== undefined) {
|
|
501
|
+
try {
|
|
502
|
+
cached = readCachedPreparedCapability(invocation, {
|
|
503
|
+
...options,
|
|
504
|
+
environment,
|
|
505
|
+
registry,
|
|
506
|
+
});
|
|
507
|
+
cacheReadCompleted = true;
|
|
508
|
+
} catch {
|
|
509
|
+
// A corrupt or unavailable local projection must not prevent the live
|
|
510
|
+
// R1 contract from running and reporting its independent outcome.
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return Object.freeze({
|
|
514
|
+
cached,
|
|
515
|
+
revalidation: revalidatePreparedCapability(
|
|
516
|
+
invocation,
|
|
517
|
+
{ ...options, environment, registry },
|
|
518
|
+
cacheReadCompleted ? cached : undefined,
|
|
519
|
+
),
|
|
520
|
+
});
|
|
521
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { ReadEffectFailure } from "./read-effect";
|
|
3
|
+
|
|
4
|
+
/** Adapts a native operation. AbortSignal/native settlement rules belong to the
|
|
5
|
+
* supplied product port, not to interruption of this Promise subscription. */
|
|
6
|
+
export const readNative = <A>(work: () => Promise<A>): Effect.Effect<A, ReadEffectFailure> =>
|
|
7
|
+
Effect.tryPromise({ try: work, catch: cause => new ReadEffectFailure({ cause }) });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Exit from "effect/Exit";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
5
|
+
import type { ReadEffectFailure } from "./read-effect";
|
|
6
|
+
|
|
7
|
+
/** Only genuine Promise/plugin boundaries interpret closed read programs. */
|
|
8
|
+
export async function runReadEffect<A>(program: Effect.Effect<A, ReadEffectFailure>): Promise<A> {
|
|
9
|
+
const exit = await Effect.runPromiseExit(program);
|
|
10
|
+
if (Exit.isSuccess(exit)) return exit.value;
|
|
11
|
+
const failure = Cause.failureOption(exit.cause);
|
|
12
|
+
if (Option.isSome(failure)) throw failure.value.cause;
|
|
13
|
+
throw Cause.squash(exit.cause);
|
|
14
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Data from "effect/Data";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
|
|
7
|
+
/** A foreign boundary failure retains the original rejection privately. */
|
|
8
|
+
export class ReadEffectFailure extends Data.TaggedError("ReadEffectFailure")<{
|
|
9
|
+
readonly cause: unknown;
|
|
10
|
+
readonly cleanupCause?: unknown;
|
|
11
|
+
readonly primaryCause?: unknown;
|
|
12
|
+
}> { }
|
|
13
|
+
|
|
14
|
+
export const readAttempt = <A>(work: () => A): Effect.Effect<A, ReadEffectFailure> =>
|
|
15
|
+
Effect.try({ try: work, catch: cause => new ReadEffectFailure({ cause }) });
|
|
16
|
+
|
|
17
|
+
/** Native acquisition and finalization settle before ownership leaves the scope.
|
|
18
|
+
* A failing finalizer retains the primary failure while preserving the previous
|
|
19
|
+
* finally-block precedence at the Promise boundary. */
|
|
20
|
+
export function withReadResource<Resource, A, R>(
|
|
21
|
+
acquire: Effect.Effect<Resource, ReadEffectFailure, R>,
|
|
22
|
+
use: (resource: Resource) => Effect.Effect<A, ReadEffectFailure, R>,
|
|
23
|
+
release: (resource: Resource) => Effect.Effect<void, ReadEffectFailure, R>,
|
|
24
|
+
|
|
25
|
+
): Effect.Effect<A, ReadEffectFailure, R> {
|
|
26
|
+
return Effect.gen(function*() {
|
|
27
|
+
let finalization: Exit.Exit<void, ReadEffectFailure> = Exit.void;
|
|
28
|
+
const outcome = yield* Effect.scoped(Effect.gen(function*() {
|
|
29
|
+
const resource = yield* Effect.acquireRelease(
|
|
30
|
+
acquire,
|
|
31
|
+
resource =>
|
|
32
|
+
Effect.exit(release(resource)).pipe(Effect.map(exit => { finalization = exit; }))
|
|
33
|
+
);
|
|
34
|
+
return yield* Effect.exit(use(resource));
|
|
35
|
+
}));
|
|
36
|
+
return yield* resumeReadOutcome(outcome, finalization);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Preserve native finalizer rejection precedence and the primary Cause. */
|
|
41
|
+
export function resumeReadOutcome<A>(
|
|
42
|
+
outcome: Exit.Exit<A, ReadEffectFailure>,
|
|
43
|
+
finalization: Exit.Exit<void, ReadEffectFailure>
|
|
44
|
+
|
|
45
|
+
): Effect.Effect<A, ReadEffectFailure> {
|
|
46
|
+
if (Exit.isFailure(finalization)) {
|
|
47
|
+
const cleanup = Cause.failureOption(finalization.cause);
|
|
48
|
+
if (Option.isSome(cleanup) && Exit.isFailure(outcome)) return Effect.fail(new ReadEffectFailure({ cause: cleanup.value.cause, primaryCause: outcome.cause }));
|
|
49
|
+
return Effect.failCause(finalization.cause);
|
|
50
|
+
}
|
|
51
|
+
return outcome;
|
|
52
|
+
}
|