@dxos/plugin-magazine 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +105 -0
- package/PLUGIN.mdl +653 -0
- package/dist/lib/neutral/FeedArticle-PEWCFFSP.mjs +90 -0
- package/dist/lib/neutral/FeedArticle-PEWCFFSP.mjs.map +7 -0
- package/dist/lib/neutral/FeedProperties-EIO7IZOA.mjs +97 -0
- package/dist/lib/neutral/FeedProperties-EIO7IZOA.mjs.map +7 -0
- package/dist/lib/neutral/MagazineArticle-DEUTR23F.mjs +325 -0
- package/dist/lib/neutral/MagazineArticle-DEUTR23F.mjs.map +7 -0
- package/dist/lib/neutral/MagazinePlugin.mjs +62 -0
- package/dist/lib/neutral/MagazinePlugin.mjs.map +7 -0
- package/dist/lib/neutral/MagazinePlugin.workerd.mjs +12 -0
- package/dist/lib/neutral/MagazinePlugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/PostArticle-RERHXPXC.mjs +230 -0
- package/dist/lib/neutral/PostArticle-RERHXPXC.mjs.map +7 -0
- package/dist/lib/neutral/PostCard-VDGN7BBA.mjs +60 -0
- package/dist/lib/neutral/PostCard-VDGN7BBA.mjs.map +7 -0
- package/dist/lib/neutral/app-graph-builder-JDPRBVHL.mjs +121 -0
- package/dist/lib/neutral/app-graph-builder-JDPRBVHL.mjs.map +7 -0
- package/dist/lib/neutral/capabilities/index.mjs +21 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/neutral/chunk-3K4AQKBZ.mjs +12 -0
- package/dist/lib/neutral/chunk-3K4AQKBZ.mjs.map +7 -0
- package/dist/lib/neutral/chunk-AFPBWBTK.mjs +591 -0
- package/dist/lib/neutral/chunk-AFPBWBTK.mjs.map +7 -0
- package/dist/lib/neutral/chunk-APQRTYT3.mjs +57 -0
- package/dist/lib/neutral/chunk-APQRTYT3.mjs.map +7 -0
- package/dist/lib/neutral/chunk-GU725ZF4.mjs +11 -0
- package/dist/lib/neutral/chunk-GU725ZF4.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/neutral/chunk-JFPR6OV4.mjs +60 -0
- package/dist/lib/neutral/chunk-JFPR6OV4.mjs.map +7 -0
- package/dist/lib/neutral/chunk-KTYLUOUG.mjs +36 -0
- package/dist/lib/neutral/chunk-KTYLUOUG.mjs.map +7 -0
- package/dist/lib/neutral/chunk-MBPX7RPI.mjs +14 -0
- package/dist/lib/neutral/chunk-MBPX7RPI.mjs.map +7 -0
- package/dist/lib/neutral/chunk-MJ2AY2CK.mjs +8 -0
- package/dist/lib/neutral/chunk-MJ2AY2CK.mjs.map +7 -0
- package/dist/lib/neutral/chunk-OYCDRSWB.mjs +624 -0
- package/dist/lib/neutral/chunk-OYCDRSWB.mjs.map +7 -0
- package/dist/lib/neutral/chunk-SAWKLGJU.mjs +28 -0
- package/dist/lib/neutral/chunk-SAWKLGJU.mjs.map +7 -0
- package/dist/lib/neutral/chunk-XCNCK47N.mjs +15 -0
- package/dist/lib/neutral/chunk-XCNCK47N.mjs.map +7 -0
- package/dist/lib/neutral/clear-magazine-KNY4OWY4.mjs +59 -0
- package/dist/lib/neutral/clear-magazine-KNY4OWY4.mjs.map +7 -0
- package/dist/lib/neutral/components/index.mjs +338 -0
- package/dist/lib/neutral/components/index.mjs.map +7 -0
- package/dist/lib/neutral/containers/index.mjs +17 -0
- package/dist/lib/neutral/containers/index.mjs.map +7 -0
- package/dist/lib/neutral/create-object-SYZMRR4H.mjs +98 -0
- package/dist/lib/neutral/create-object-SYZMRR4H.mjs.map +7 -0
- package/dist/lib/neutral/curate-magazine-MA6OLK2X.mjs +184 -0
- package/dist/lib/neutral/curate-magazine-MA6OLK2X.mjs.map +7 -0
- package/dist/lib/neutral/fetch-article-content-CZO6D3QD.mjs +31 -0
- package/dist/lib/neutral/fetch-article-content-CZO6D3QD.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +18 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/load-post-content-XGL6VFF6.mjs +65 -0
- package/dist/lib/neutral/load-post-content-XGL6VFF6.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +8 -0
- package/dist/lib/neutral/meta.mjs.map +7 -0
- package/dist/lib/neutral/navigation-resolver-IN4OMCDR.mjs +18 -0
- package/dist/lib/neutral/navigation-resolver-IN4OMCDR.mjs.map +7 -0
- package/dist/lib/neutral/operation-handler-JE3743BU.mjs +8 -0
- package/dist/lib/neutral/operation-handler-JE3743BU.mjs.map +7 -0
- package/dist/lib/neutral/operations/index.mjs +8 -0
- package/dist/lib/neutral/operations/index.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +16 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/plugin.workerd.mjs +16 -0
- package/dist/lib/neutral/plugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/react-surface-TZESAMG2.mjs +60 -0
- package/dist/lib/neutral/react-surface-TZESAMG2.mjs.map +7 -0
- package/dist/lib/neutral/routine-templates-A2XTJOZO.mjs +52 -0
- package/dist/lib/neutral/routine-templates-A2XTJOZO.mjs.map +7 -0
- package/dist/lib/neutral/skill-definition-3KLMUEC5.mjs +8 -0
- package/dist/lib/neutral/skill-definition-3KLMUEC5.mjs.map +7 -0
- package/dist/lib/neutral/skills/index.mjs +46 -0
- package/dist/lib/neutral/skills/index.mjs.map +7 -0
- package/dist/lib/neutral/sync-feed-UZX2XHSQ.mjs +99 -0
- package/dist/lib/neutral/sync-feed-UZX2XHSQ.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +81 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +14 -0
- package/dist/lib/neutral/types/index.mjs.map +7 -0
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/MagazinePlugin.d.ts +4 -0
- package/dist/types/src/MagazinePlugin.d.ts.map +1 -0
- package/dist/types/src/MagazinePlugin.test.d.ts +2 -0
- package/dist/types/src/MagazinePlugin.test.d.ts.map +1 -0
- package/dist/types/src/MagazinePlugin.workerd.d.ts +4 -0
- package/dist/types/src/MagazinePlugin.workerd.d.ts.map +1 -0
- package/dist/types/src/atoms/atoms.test.d.ts +2 -0
- package/dist/types/src/atoms/atoms.test.d.ts.map +1 -0
- package/dist/types/src/atoms/index.d.ts +7 -0
- package/dist/types/src/atoms/index.d.ts.map +1 -0
- package/dist/types/src/atoms/magazine-posts.d.ts +16 -0
- package/dist/types/src/atoms/magazine-posts.d.ts.map +1 -0
- package/dist/types/src/atoms/post-content.d.ts +7 -0
- package/dist/types/src/atoms/post-content.d.ts.map +1 -0
- package/dist/types/src/atoms/post-curation.d.ts +16 -0
- package/dist/types/src/atoms/post-curation.d.ts.map +1 -0
- package/dist/types/src/atoms/post-display.d.ts +22 -0
- package/dist/types/src/atoms/post-display.d.ts.map +1 -0
- package/dist/types/src/atoms/post-read.d.ts +14 -0
- package/dist/types/src/atoms/post-read.d.ts.map +1 -0
- package/dist/types/src/atoms/post-tags.d.ts +16 -0
- package/dist/types/src/atoms/post-tags.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +5 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/create-object.d.ts +5 -0
- package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +10 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/navigation-resolver.d.ts +5 -0
- package/dist/types/src/capabilities/navigation-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/routine-templates.d.ts +5 -0
- package/dist/types/src/capabilities/routine-templates.d.ts.map +1 -0
- package/dist/types/src/capabilities/skill-definition.d.ts +5 -0
- package/dist/types/src/capabilities/skill-definition.d.ts.map +1 -0
- package/dist/types/src/components/PostContent/PostContent.d.ts +30 -0
- package/dist/types/src/components/PostContent/PostContent.d.ts.map +1 -0
- package/dist/types/src/components/PostContent/PostContent.test.d.ts +2 -0
- package/dist/types/src/components/PostContent/PostContent.test.d.ts.map +1 -0
- package/dist/types/src/components/PostContent/index.d.ts +2 -0
- package/dist/types/src/components/PostContent/index.d.ts.map +1 -0
- package/dist/types/src/components/PostStack/PostStack.d.ts +17 -0
- package/dist/types/src/components/PostStack/PostStack.d.ts.map +1 -0
- package/dist/types/src/components/PostStack/PostStack.stories.d.ts +10 -0
- package/dist/types/src/components/PostStack/PostStack.stories.d.ts.map +1 -0
- package/dist/types/src/components/PostStack/index.d.ts +2 -0
- package/dist/types/src/components/PostStack/index.d.ts.map +1 -0
- package/dist/types/src/components/SubscriptionStack/SubscriptionStack.d.ts +23 -0
- package/dist/types/src/components/SubscriptionStack/SubscriptionStack.d.ts.map +1 -0
- package/dist/types/src/components/SubscriptionStack/SubscriptionStack.stories.d.ts +8 -0
- package/dist/types/src/components/SubscriptionStack/SubscriptionStack.stories.d.ts.map +1 -0
- package/dist/types/src/components/SubscriptionStack/index.d.ts +2 -0
- package/dist/types/src/components/SubscriptionStack/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +4 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/containers/FeedArticle/FeedArticle.d.ts +6 -0
- package/dist/types/src/containers/FeedArticle/FeedArticle.d.ts.map +1 -0
- package/dist/types/src/containers/FeedArticle/FeedArticle.stories.d.ts +12 -0
- package/dist/types/src/containers/FeedArticle/FeedArticle.stories.d.ts.map +1 -0
- package/dist/types/src/containers/FeedArticle/FeedToolbar.d.ts +10 -0
- package/dist/types/src/containers/FeedArticle/FeedToolbar.d.ts.map +1 -0
- package/dist/types/src/containers/FeedArticle/index.d.ts +2 -0
- package/dist/types/src/containers/FeedArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/FeedProperties/FeedProperties.d.ts +6 -0
- package/dist/types/src/containers/FeedProperties/FeedProperties.d.ts.map +1 -0
- package/dist/types/src/containers/FeedProperties/index.d.ts +2 -0
- package/dist/types/src/containers/FeedProperties/index.d.ts.map +1 -0
- package/dist/types/src/containers/MagazineArticle/MagazineArticle.d.ts +6 -0
- package/dist/types/src/containers/MagazineArticle/MagazineArticle.d.ts.map +1 -0
- package/dist/types/src/containers/MagazineArticle/MagazineArticle.stories.d.ts +9 -0
- package/dist/types/src/containers/MagazineArticle/MagazineArticle.stories.d.ts.map +1 -0
- package/dist/types/src/containers/MagazineArticle/MagazineTile.d.ts +11 -0
- package/dist/types/src/containers/MagazineArticle/MagazineTile.d.ts.map +1 -0
- package/dist/types/src/containers/MagazineArticle/index.d.ts +2 -0
- package/dist/types/src/containers/MagazineArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/MagazineArticle/useToolbar.d.ts +18 -0
- package/dist/types/src/containers/MagazineArticle/useToolbar.d.ts.map +1 -0
- package/dist/types/src/containers/PostArticle/PostArticle.d.ts +6 -0
- package/dist/types/src/containers/PostArticle/PostArticle.d.ts.map +1 -0
- package/dist/types/src/containers/PostArticle/PostArticle.stories.d.ts +8 -0
- package/dist/types/src/containers/PostArticle/PostArticle.stories.d.ts.map +1 -0
- package/dist/types/src/containers/PostArticle/PostToolbar.d.ts +19 -0
- package/dist/types/src/containers/PostArticle/PostToolbar.d.ts.map +1 -0
- package/dist/types/src/containers/PostArticle/index.d.ts +2 -0
- package/dist/types/src/containers/PostArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/PostCard/PostCard.d.ts +11 -0
- package/dist/types/src/containers/PostCard/PostCard.d.ts.map +1 -0
- package/dist/types/src/containers/PostCard/index.d.ts +2 -0
- package/dist/types/src/containers/PostCard/index.d.ts.map +1 -0
- package/dist/types/src/containers/SubscriptionsArticle/SubscriptionsArticle.d.ts +5 -0
- package/dist/types/src/containers/SubscriptionsArticle/SubscriptionsArticle.d.ts.map +1 -0
- package/dist/types/src/containers/SubscriptionsArticle/SubscriptionsArticle.stories.d.ts +8 -0
- package/dist/types/src/containers/SubscriptionsArticle/SubscriptionsArticle.stories.d.ts.map +1 -0
- package/dist/types/src/containers/SubscriptionsArticle/index.d.ts +2 -0
- package/dist/types/src/containers/SubscriptionsArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +7 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +33 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/operations/clear-magazine.d.ts +10 -0
- package/dist/types/src/operations/clear-magazine.d.ts.map +1 -0
- package/dist/types/src/operations/curate-magazine.d.ts +25 -0
- package/dist/types/src/operations/curate-magazine.d.ts.map +1 -0
- package/dist/types/src/operations/curate-magazine.skill.test.d.ts +2 -0
- package/dist/types/src/operations/curate-magazine.skill.test.d.ts.map +1 -0
- package/dist/types/src/operations/curate-magazine.test.d.ts +2 -0
- package/dist/types/src/operations/curate-magazine.test.d.ts.map +1 -0
- package/dist/types/src/operations/extraction/article.d.ts +37 -0
- package/dist/types/src/operations/extraction/article.d.ts.map +1 -0
- package/dist/types/src/operations/extraction/article.test.d.ts +2 -0
- package/dist/types/src/operations/extraction/article.test.d.ts.map +1 -0
- package/dist/types/src/operations/extraction/index.d.ts +2 -0
- package/dist/types/src/operations/extraction/index.d.ts.map +1 -0
- package/dist/types/src/operations/fetch-article-content.d.ts +5 -0
- package/dist/types/src/operations/fetch-article-content.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/load-post-content.d.ts +8 -0
- package/dist/types/src/operations/load-post-content.d.ts.map +1 -0
- package/dist/types/src/operations/sources/article.d.ts +24 -0
- package/dist/types/src/operations/sources/article.d.ts.map +1 -0
- package/dist/types/src/operations/sources/cors.d.ts +12 -0
- package/dist/types/src/operations/sources/cors.d.ts.map +1 -0
- package/dist/types/src/operations/sources/feed-fetcher.d.ts +22 -0
- package/dist/types/src/operations/sources/feed-fetcher.d.ts.map +1 -0
- package/dist/types/src/operations/sources/feed-fetcher.test.d.ts +2 -0
- package/dist/types/src/operations/sources/feed-fetcher.test.d.ts.map +1 -0
- package/dist/types/src/operations/sources/http.d.ts +9 -0
- package/dist/types/src/operations/sources/http.d.ts.map +1 -0
- package/dist/types/src/operations/sources/index.d.ts +6 -0
- package/dist/types/src/operations/sources/index.d.ts.map +1 -0
- package/dist/types/src/operations/sources/rss.d.ts +4 -0
- package/dist/types/src/operations/sources/rss.d.ts.map +1 -0
- package/dist/types/src/operations/sources/rss.test.d.ts +2 -0
- package/dist/types/src/operations/sources/rss.test.d.ts.map +1 -0
- package/dist/types/src/operations/sources/standard-site.d.ts +33 -0
- package/dist/types/src/operations/sources/standard-site.d.ts.map +1 -0
- package/dist/types/src/operations/sync-feed.d.ts +5 -0
- package/dist/types/src/operations/sync-feed.d.ts.map +1 -0
- package/dist/types/src/operations/sync-feed.test.d.ts +2 -0
- package/dist/types/src/operations/sync-feed.test.d.ts.map +1 -0
- package/dist/types/src/operations/util.d.ts +25 -0
- package/dist/types/src/operations/util.d.ts.map +1 -0
- package/dist/types/src/operations/util.test.d.ts +2 -0
- package/dist/types/src/operations/util.test.d.ts.map +1 -0
- package/dist/types/src/paths.d.ts +3 -0
- package/dist/types/src/paths.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/plugin.workerd.d.ts +3 -0
- package/dist/types/src/plugin.workerd.d.ts.map +1 -0
- package/dist/types/src/skills/index.d.ts +2 -0
- package/dist/types/src/skills/index.d.ts.map +1 -0
- package/dist/types/src/skills/magazine-skill.d.ts +4 -0
- package/dist/types/src/skills/magazine-skill.d.ts.map +1 -0
- package/dist/types/src/stories/ArticleExtractor.stories.d.ts +267 -0
- package/dist/types/src/stories/ArticleExtractor.stories.d.ts.map +1 -0
- package/dist/types/src/stories/MagazineCurate.stories.d.ts +32 -0
- package/dist/types/src/stories/MagazineCurate.stories.d.ts.map +1 -0
- package/dist/types/src/stories/theregister-fixture.test.d.ts +2 -0
- package/dist/types/src/stories/theregister-fixture.test.d.ts.map +1 -0
- package/dist/types/src/templates/magazine-curation.d.ts +7 -0
- package/dist/types/src/templates/magazine-curation.d.ts.map +1 -0
- package/dist/types/src/testing/builder.d.ts +28 -0
- package/dist/types/src/testing/builder.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +247 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types/CreateSubscription.d.ts +29 -0
- package/dist/types/src/types/CreateSubscription.d.ts.map +1 -0
- package/dist/types/src/types/FeedOperation.d.ts +52 -0
- package/dist/types/src/types/FeedOperation.d.ts.map +1 -0
- package/dist/types/src/types/Magazine.d.ts +87 -0
- package/dist/types/src/types/Magazine.d.ts.map +1 -0
- package/dist/types/src/types/Magazine.test.d.ts +2 -0
- package/dist/types/src/types/Magazine.test.d.ts.map +1 -0
- package/dist/types/src/types/Subscription.d.ts +144 -0
- package/dist/types/src/types/Subscription.d.ts.map +1 -0
- package/dist/types/src/types/Subscription.test.d.ts +2 -0
- package/dist/types/src/types/Subscription.test.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +5 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/util/date.d.ts +8 -0
- package/dist/types/src/util/date.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +4 -0
- package/dist/types/src/util/index.d.ts.map +1 -0
- package/dist/types/src/util/post-content.d.ts +14 -0
- package/dist/types/src/util/post-content.d.ts.map +1 -0
- package/dist/types/src/util/text.d.ts +18 -0
- package/dist/types/src/util/text.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/dx.config.ts +48 -0
- package/package.json +164 -0
- package/src/MagazinePlugin.test.ts +36 -0
- package/src/MagazinePlugin.tsx +61 -0
- package/src/MagazinePlugin.workerd.ts +31 -0
- package/src/atoms/atoms.test.ts +323 -0
- package/src/atoms/index.ts +10 -0
- package/src/atoms/magazine-posts.ts +70 -0
- package/src/atoms/post-content.ts +34 -0
- package/src/atoms/post-curation.ts +36 -0
- package/src/atoms/post-display.ts +59 -0
- package/src/atoms/post-read.ts +40 -0
- package/src/atoms/post-tags.ts +66 -0
- package/src/capabilities/app-graph-builder.ts +125 -0
- package/src/capabilities/create-object.ts +127 -0
- package/src/capabilities/index.ts +17 -0
- package/src/capabilities/navigation-resolver.ts +21 -0
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +53 -0
- package/src/capabilities/routine-templates.ts +16 -0
- package/src/capabilities/skill-definition.ts +14 -0
- package/src/components/PostContent/PostContent.test.ts +81 -0
- package/src/components/PostContent/PostContent.tsx +111 -0
- package/src/components/PostContent/index.ts +5 -0
- package/src/components/PostStack/PostStack.stories.tsx +39 -0
- package/src/components/PostStack/PostStack.tsx +151 -0
- package/src/components/PostStack/index.ts +5 -0
- package/src/components/SubscriptionStack/SubscriptionStack.stories.tsx +52 -0
- package/src/components/SubscriptionStack/SubscriptionStack.tsx +161 -0
- package/src/components/SubscriptionStack/index.ts +5 -0
- package/src/components/index.ts +7 -0
- package/src/containers/FeedArticle/FeedArticle.stories.tsx +102 -0
- package/src/containers/FeedArticle/FeedArticle.tsx +68 -0
- package/src/containers/FeedArticle/FeedToolbar.tsx +47 -0
- package/src/containers/FeedArticle/index.ts +5 -0
- package/src/containers/FeedProperties/FeedProperties.tsx +107 -0
- package/src/containers/FeedProperties/index.ts +5 -0
- package/src/containers/MagazineArticle/MagazineArticle.stories.tsx +244 -0
- package/src/containers/MagazineArticle/MagazineArticle.tsx +146 -0
- package/src/containers/MagazineArticle/MagazineTile.tsx +88 -0
- package/src/containers/MagazineArticle/index.ts +5 -0
- package/src/containers/MagazineArticle/useToolbar.tsx +145 -0
- package/src/containers/PostArticle/PostArticle.stories.tsx +117 -0
- package/src/containers/PostArticle/PostArticle.tsx +140 -0
- package/src/containers/PostArticle/PostToolbar.tsx +120 -0
- package/src/containers/PostArticle/index.ts +5 -0
- package/src/containers/PostCard/PostCard.tsx +70 -0
- package/src/containers/PostCard/index.ts +5 -0
- package/src/containers/SubscriptionsArticle/SubscriptionsArticle.stories.tsx +80 -0
- package/src/containers/SubscriptionsArticle/SubscriptionsArticle.tsx +99 -0
- package/src/containers/SubscriptionsArticle/index.ts +5 -0
- package/src/containers/index.ts +11 -0
- package/src/index.ts +6 -0
- package/src/meta.ts +9 -0
- package/src/operations/clear-magazine.ts +83 -0
- package/src/operations/curate-magazine.skill.conversations.json +1 -0
- package/src/operations/curate-magazine.skill.test.ts +167 -0
- package/src/operations/curate-magazine.test.ts +91 -0
- package/src/operations/curate-magazine.ts +178 -0
- package/src/operations/extraction/article.test.ts +154 -0
- package/src/operations/extraction/article.ts +270 -0
- package/src/operations/extraction/index.ts +5 -0
- package/src/operations/fetch-article-content.ts +27 -0
- package/src/operations/index.ts +13 -0
- package/src/operations/load-post-content.ts +76 -0
- package/src/operations/sources/article.ts +122 -0
- package/src/operations/sources/cors.ts +19 -0
- package/src/operations/sources/feed-fetcher.test.ts +200 -0
- package/src/operations/sources/feed-fetcher.ts +26 -0
- package/src/operations/sources/http.ts +42 -0
- package/src/operations/sources/index.ts +9 -0
- package/src/operations/sources/rss.test.ts +201 -0
- package/src/operations/sources/rss.ts +138 -0
- package/src/operations/sources/standard-site.ts +381 -0
- package/src/operations/sources/testing/feed.xml +477 -0
- package/src/operations/sync-feed.test.ts +146 -0
- package/src/operations/sync-feed.ts +140 -0
- package/src/operations/util.test.ts +67 -0
- package/src/operations/util.ts +65 -0
- package/src/paths.ts +14 -0
- package/src/plugin.ts +11 -0
- package/src/plugin.workerd.ts +6 -0
- package/src/skills/index.ts +5 -0
- package/src/skills/magazine-skill.ts +45 -0
- package/src/stories/ArticleExtractor.stories.tsx +204 -0
- package/src/stories/MagazineCurate.stories.tsx +217 -0
- package/src/stories/fixtures/theregister-ai.xml +45 -0
- package/src/stories/theregister-fixture.test.ts +35 -0
- package/src/templates/magazine-curation.ts +47 -0
- package/src/testing/builder.ts +77 -0
- package/src/testing/index.ts +5 -0
- package/src/translations.ts +81 -0
- package/src/types/CreateSubscription.ts +61 -0
- package/src/types/FeedOperation.ts +134 -0
- package/src/types/Magazine.test.ts +69 -0
- package/src/types/Magazine.ts +214 -0
- package/src/types/Subscription.test.ts +171 -0
- package/src/types/Subscription.ts +366 -0
- package/src/types/index.ts +8 -0
- package/src/util/date.ts +27 -0
- package/src/util/index.ts +7 -0
- package/src/util/post-content.ts +19 -0
- package/src/util/text.ts +98 -0
- package/src/vite-env.d.ts +10 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
|
|
6
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
9
|
+
|
|
10
|
+
import { Subscription } from '#types';
|
|
11
|
+
|
|
12
|
+
import { makeSnippet } from '../../util/text';
|
|
13
|
+
import { type FeedFetcher, FeedFetchError, type FetchOptions } from './feed-fetcher';
|
|
14
|
+
import { getJson } from './http';
|
|
15
|
+
|
|
16
|
+
// Standard.site (`site.standard.*`) is the AT Protocol lexicon for long-form articles. Reading is
|
|
17
|
+
// fully decentralized and auth-free: resolve handle → DID → PDS, then list the actor's public
|
|
18
|
+
// `site.standard.document` records. The same records this repo publishes via
|
|
19
|
+
// `docs/scripts/sync-to-atproto.ts`. Standard.site lexicons aren't in `@atproto/api`, so the
|
|
20
|
+
// response shapes are modelled locally and fetched with the platform `HttpClient` (mirroring the AT
|
|
21
|
+
// Protocol endpoints used by `plugin-bluesky`'s `BlueskyApi.resolvePds`).
|
|
22
|
+
|
|
23
|
+
const BSKY_PUBLIC_API = 'https://public.api.bsky.app/xrpc';
|
|
24
|
+
const PLC_DIRECTORY = 'https://plc.directory';
|
|
25
|
+
const DOCUMENT_COLLECTION = 'site.standard.document';
|
|
26
|
+
const MARKDOWN_CONTENT_TYPE = 'site.standard.content.markdown';
|
|
27
|
+
|
|
28
|
+
/** URL builders for the AT Protocol XRPC endpoints used by this module. */
|
|
29
|
+
const endpoints = {
|
|
30
|
+
resolveHandle: (handle: string) =>
|
|
31
|
+
`${BSKY_PUBLIC_API}/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`,
|
|
32
|
+
getProfile: (actor: string) => `${BSKY_PUBLIC_API}/app.bsky.actor.getProfile?actor=${encodeURIComponent(actor)}`,
|
|
33
|
+
searchActors: (query: string, limit: number) =>
|
|
34
|
+
`${BSKY_PUBLIC_API}/app.bsky.actor.searchActorsTypeahead?q=${encodeURIComponent(query)}&limit=${limit}`,
|
|
35
|
+
plcDoc: (did: string) => `${PLC_DIRECTORY}/${did}`,
|
|
36
|
+
listRecords: (pds: string, did: string, collection: string, limit: number) =>
|
|
37
|
+
`${pds}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(collection)}&limit=${limit}`,
|
|
38
|
+
getRecord: (pds: string, did: string, collection: string, rkey: string) =>
|
|
39
|
+
`${pds}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(collection)}&rkey=${encodeURIComponent(rkey)}`,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** A publication a handle publishes under, keyed by its Standard.site `site` reference. */
|
|
43
|
+
export type Publication = {
|
|
44
|
+
/** The document `site` reference (`at://…` URI or `https://…` URL); the stable selection key. */
|
|
45
|
+
site: string;
|
|
46
|
+
/** Canonical site URL (e.g. `https://dxos.org`), trailing slash stripped. */
|
|
47
|
+
url?: string;
|
|
48
|
+
/** Publication display name. */
|
|
49
|
+
name?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extracts an atproto handle or DID from a URL or raw identifier.
|
|
54
|
+
* Supports: bare handles (`dxos.org`), `@handle`, `did:plc:…`/`did:web:…`, or a
|
|
55
|
+
* `https://bsky.app/profile/{actor}` URL.
|
|
56
|
+
*/
|
|
57
|
+
export const parseStandardSiteActor = (url: string): string => {
|
|
58
|
+
const match = url.match(/bsky\.app\/profile\/([^/?#]+)/);
|
|
59
|
+
if (match) {
|
|
60
|
+
return match[1];
|
|
61
|
+
}
|
|
62
|
+
// Already a DID or bare handle.
|
|
63
|
+
return url.replace(/^@/, '').trim();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Lists the publications a handle publishes under (deduped by `site`), for publication selection. */
|
|
67
|
+
export const listStandardSitePublications = (
|
|
68
|
+
actorOrUrl: string,
|
|
69
|
+
options?: FetchOptions,
|
|
70
|
+
): Effect.Effect<Publication[], FeedFetchError> =>
|
|
71
|
+
Effect.gen(function* () {
|
|
72
|
+
const proxy = options?.corsProxy;
|
|
73
|
+
const actor = parseStandardSiteActor(actorOrUrl);
|
|
74
|
+
const did = yield* resolveDid(actor, proxy);
|
|
75
|
+
const pds = yield* resolvePds(did, proxy);
|
|
76
|
+
const records = yield* listDocuments(pds, did, proxy);
|
|
77
|
+
const sites = distinct(records.map((record) => record.value.site).filter(isString));
|
|
78
|
+
return yield* Effect.forEach(
|
|
79
|
+
sites,
|
|
80
|
+
(site) => resolvePublication(site, proxy).pipe(Effect.catchAll(() => Effect.succeed({ site }))),
|
|
81
|
+
{ concurrency: 'unbounded' },
|
|
82
|
+
);
|
|
83
|
+
}).pipe(Effect.provide(FetchHttpClient.layer));
|
|
84
|
+
|
|
85
|
+
/** A handle suggestion returned by typeahead search. */
|
|
86
|
+
export type HandleSuggestion = { handle: string; displayName?: string };
|
|
87
|
+
|
|
88
|
+
/** Searches atproto handles by prefix (typeahead), for combobox suggestions; empty on blank/failed query. */
|
|
89
|
+
export const searchStandardSiteHandles = (
|
|
90
|
+
query: string,
|
|
91
|
+
options?: FetchOptions,
|
|
92
|
+
): Effect.Effect<HandleSuggestion[], never> =>
|
|
93
|
+
query.trim().length === 0
|
|
94
|
+
? Effect.succeed([])
|
|
95
|
+
: getJson(SearchActorsResponse, endpoints.searchActors(query.trim(), 8), options?.corsProxy).pipe(
|
|
96
|
+
Effect.map((response) =>
|
|
97
|
+
(response.actors ?? []).map((actor) => ({ handle: actor.handle, displayName: actor.displayName })),
|
|
98
|
+
),
|
|
99
|
+
Effect.orElseSucceed((): HandleSuggestion[] => []),
|
|
100
|
+
Effect.provide(FetchHttpClient.layer),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Fetches a Standard.site feed. `url` is the publication's `site` reference — either an `at://` URI
|
|
105
|
+
* (DID extracted directly) or an `https://` URL (DID resolved via `/.well-known/atproto-did`). In both
|
|
106
|
+
* cases, all documents in the author's repo filtered to that publication are fetched.
|
|
107
|
+
*/
|
|
108
|
+
export const fetchStandardSite: FeedFetcher = (url, options) =>
|
|
109
|
+
Effect.gen(function* () {
|
|
110
|
+
const proxy = options?.corsProxy;
|
|
111
|
+
const did = yield* resolveDidFromSite(url, proxy);
|
|
112
|
+
const pds = yield* resolvePds(did, proxy);
|
|
113
|
+
|
|
114
|
+
// List all documents in the author's repo scoped to this publication.
|
|
115
|
+
const allRecords = yield* listDocuments(pds, did, proxy);
|
|
116
|
+
const records = allRecords.filter((record) => record.value.site === url);
|
|
117
|
+
|
|
118
|
+
// Author profile (best-effort) supplies feed name/icon and per-post author.
|
|
119
|
+
const profile = yield* fetchProfile(did, proxy);
|
|
120
|
+
const authorName = profile?.displayName ?? profile?.handle ?? did;
|
|
121
|
+
|
|
122
|
+
// Resolve the publication record once to get the canonical URL and name.
|
|
123
|
+
const publication = yield* resolvePublication(url, proxy).pipe(
|
|
124
|
+
Effect.catchAll((): Effect.Effect<Publication> => Effect.succeed({ site: url })),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const posts = records.map((record) => {
|
|
128
|
+
const value = record.value;
|
|
129
|
+
const content = value.content?.$type === MARKDOWN_CONTENT_TYPE ? value.content.text : undefined;
|
|
130
|
+
const description = value.description ?? (value.textContent ? makeSnippet(value.textContent) : undefined);
|
|
131
|
+
|
|
132
|
+
return Subscription.makePost({
|
|
133
|
+
title: value.title,
|
|
134
|
+
link: joinUrl(publication.url, value.path),
|
|
135
|
+
description,
|
|
136
|
+
content,
|
|
137
|
+
author: authorName,
|
|
138
|
+
published: value.publishedAt,
|
|
139
|
+
// The record's AT-URI is the stable dedup key (see `sync-feed.ts` `postKey`).
|
|
140
|
+
guid: record.uri,
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Newest first so `sync-feed`'s cursor (posts[0]) advances correctly.
|
|
145
|
+
posts.sort((postA, postB) => (postB.published ?? '').localeCompare(postA.published ?? ''));
|
|
146
|
+
|
|
147
|
+
const feed = Subscription.makeSubscription({
|
|
148
|
+
name: publication.name ?? authorName,
|
|
149
|
+
url,
|
|
150
|
+
description: profile?.description ?? `Standard.site articles from @${profile?.handle ?? did}`,
|
|
151
|
+
iconUrl: profile?.avatar,
|
|
152
|
+
type: 'standard-site',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return { feed, posts };
|
|
156
|
+
}).pipe(Effect.provide(FetchHttpClient.layer));
|
|
157
|
+
|
|
158
|
+
//
|
|
159
|
+
// Response schemas for the XRPC / Standard.site shapes (public APIs, no auth). Excess fields are ignored.
|
|
160
|
+
//
|
|
161
|
+
|
|
162
|
+
const ResolveHandleResponse = Schema.Struct({ did: Schema.optional(Schema.String) });
|
|
163
|
+
|
|
164
|
+
const SearchActorsResponse = Schema.Struct({
|
|
165
|
+
actors: Schema.optional(
|
|
166
|
+
Schema.Array(Schema.Struct({ handle: Schema.String, displayName: Schema.optional(Schema.String) })),
|
|
167
|
+
),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const DidDocument = Schema.Struct({
|
|
171
|
+
service: Schema.optional(
|
|
172
|
+
Schema.Array(
|
|
173
|
+
Schema.Struct({
|
|
174
|
+
id: Schema.optional(Schema.String),
|
|
175
|
+
type: Schema.optional(Schema.String),
|
|
176
|
+
serviceEndpoint: Schema.optional(Schema.String),
|
|
177
|
+
}),
|
|
178
|
+
),
|
|
179
|
+
),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const Profile = Schema.Struct({
|
|
183
|
+
handle: Schema.optional(Schema.String),
|
|
184
|
+
displayName: Schema.optional(Schema.String),
|
|
185
|
+
avatar: Schema.optional(Schema.String),
|
|
186
|
+
description: Schema.optional(Schema.String),
|
|
187
|
+
});
|
|
188
|
+
type Profile = Schema.Schema.Type<typeof Profile>;
|
|
189
|
+
|
|
190
|
+
const StandardSiteDocument = Schema.Struct({
|
|
191
|
+
site: Schema.optional(Schema.String),
|
|
192
|
+
title: Schema.optional(Schema.String),
|
|
193
|
+
publishedAt: Schema.optional(Schema.String),
|
|
194
|
+
path: Schema.optional(Schema.String),
|
|
195
|
+
description: Schema.optional(Schema.String),
|
|
196
|
+
content: Schema.optional(
|
|
197
|
+
Schema.Struct({
|
|
198
|
+
$type: Schema.optional(Schema.String),
|
|
199
|
+
text: Schema.optional(Schema.String),
|
|
200
|
+
}),
|
|
201
|
+
),
|
|
202
|
+
textContent: Schema.optional(Schema.String),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const ListRecordsResponse = Schema.Struct({
|
|
206
|
+
records: Schema.optional(Schema.Array(Schema.Struct({ uri: Schema.String, value: StandardSiteDocument }))),
|
|
207
|
+
cursor: Schema.optional(Schema.String),
|
|
208
|
+
});
|
|
209
|
+
type DocumentRecord = NonNullable<Schema.Schema.Type<typeof ListRecordsResponse>['records']>[number];
|
|
210
|
+
|
|
211
|
+
const PublicationRecord = Schema.Struct({
|
|
212
|
+
value: Schema.optional(Schema.Struct({ url: Schema.optional(Schema.String), name: Schema.optional(Schema.String) })),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
//
|
|
216
|
+
// Resolution chain (each step requires an `HttpClient`, provided by the public entrypoints above).
|
|
217
|
+
//
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Resolves a publication `site` reference to its author's DID:
|
|
221
|
+
* - `at://did:…/…` → DID extracted directly from the URI.
|
|
222
|
+
* - `https://…` → DID fetched from `/.well-known/atproto-did` on that domain.
|
|
223
|
+
*/
|
|
224
|
+
const resolveDidFromSite = (
|
|
225
|
+
site: string,
|
|
226
|
+
proxy?: string,
|
|
227
|
+
): Effect.Effect<string, FeedFetchError, HttpClient.HttpClient> => {
|
|
228
|
+
if (site.startsWith('at://')) {
|
|
229
|
+
const parsed = parseAtUri(site);
|
|
230
|
+
return parsed
|
|
231
|
+
? Effect.succeed(parsed.did)
|
|
232
|
+
: Effect.fail(new FeedFetchError({ message: `Malformed at:// site reference: ${site}` }));
|
|
233
|
+
}
|
|
234
|
+
if (site.startsWith('https://')) {
|
|
235
|
+
const { hostname } = new URL(site);
|
|
236
|
+
return getJson(
|
|
237
|
+
Schema.Struct({ did: Schema.optional(Schema.String) }),
|
|
238
|
+
`https://${hostname}/.well-known/atproto-did`,
|
|
239
|
+
proxy,
|
|
240
|
+
).pipe(
|
|
241
|
+
Effect.flatMap((result) =>
|
|
242
|
+
result.did
|
|
243
|
+
? Effect.succeed(result.did)
|
|
244
|
+
: Effect.fail(new FeedFetchError({ message: `No atproto DID found at ${hostname}/.well-known/atproto-did` })),
|
|
245
|
+
),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return Effect.fail(new FeedFetchError({ message: `Cannot resolve DID from site reference: ${site}` }));
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/** Resolves a handle to a DID (no-op when already a DID) via the public `resolveHandle` XRPC. */
|
|
252
|
+
const resolveDid = (actor: string, proxy?: string): Effect.Effect<string, FeedFetchError, HttpClient.HttpClient> =>
|
|
253
|
+
actor.startsWith('did:')
|
|
254
|
+
? Effect.succeed(actor)
|
|
255
|
+
: getJson(ResolveHandleResponse, endpoints.resolveHandle(actor), proxy).pipe(
|
|
256
|
+
Effect.flatMap((resolved) =>
|
|
257
|
+
resolved.did
|
|
258
|
+
? Effect.succeed(resolved.did)
|
|
259
|
+
: Effect.fail(new FeedFetchError({ message: `Could not resolve handle to DID: ${actor}` })),
|
|
260
|
+
),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Resolves a DID to its PDS endpoint: `did:plc` via the PLC directory DID-doc, `did:web` via the
|
|
265
|
+
* domain's `/.well-known/did.json`. Mirrors `plugin-bluesky`'s `BlueskyApi.resolvePds` endpoints.
|
|
266
|
+
*/
|
|
267
|
+
const resolvePds = (did: string, proxy?: string): Effect.Effect<string, FeedFetchError, HttpClient.HttpClient> => {
|
|
268
|
+
if (did.startsWith('did:plc:')) {
|
|
269
|
+
return getJson(DidDocument, endpoints.plcDoc(did), proxy).pipe(Effect.flatMap((doc) => extractPds(doc, did)));
|
|
270
|
+
}
|
|
271
|
+
if (did.startsWith('did:web:')) {
|
|
272
|
+
// Per the did:web spec `:` separates host from path segments (each percent-encoded); a bare host
|
|
273
|
+
// resolves to `/.well-known/did.json`, path segments to `/<path>/did.json`.
|
|
274
|
+
const [host, ...segments] = did
|
|
275
|
+
.slice('did:web:'.length)
|
|
276
|
+
.split(':')
|
|
277
|
+
.map((segment) => decodeURIComponent(segment));
|
|
278
|
+
if (!host) {
|
|
279
|
+
return Effect.fail(new FeedFetchError({ message: `Invalid did:web identifier: ${did}` }));
|
|
280
|
+
}
|
|
281
|
+
const path = segments.length > 0 ? `/${segments.join('/')}/did.json` : '/.well-known/did.json';
|
|
282
|
+
return getJson(DidDocument, `https://${host}${path}`, proxy).pipe(Effect.flatMap((doc) => extractPds(doc, did)));
|
|
283
|
+
}
|
|
284
|
+
return Effect.fail(new FeedFetchError({ message: `Unsupported DID method: ${did}` }));
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const extractPds = (
|
|
288
|
+
doc: Schema.Schema.Type<typeof DidDocument>,
|
|
289
|
+
did: string,
|
|
290
|
+
): Effect.Effect<string, FeedFetchError> => {
|
|
291
|
+
const service = doc.service?.find(
|
|
292
|
+
(entry) => entry.id === '#atproto_pds' || entry.type === 'AtprotoPersonalDataServer',
|
|
293
|
+
);
|
|
294
|
+
const endpoint = service?.serviceEndpoint;
|
|
295
|
+
return typeof endpoint === 'string' && endpoint.length > 0
|
|
296
|
+
? Effect.succeed(endpoint.replace(/\/$/, ''))
|
|
297
|
+
: Effect.fail(new FeedFetchError({ message: `No PDS endpoint found for DID: ${did}` }));
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/** Lists ALL of the actor's `site.standard.document` records by following pagination cursors. */
|
|
301
|
+
const listDocuments = (
|
|
302
|
+
pds: string,
|
|
303
|
+
did: string,
|
|
304
|
+
proxy?: string,
|
|
305
|
+
): Effect.Effect<readonly DocumentRecord[], FeedFetchError, HttpClient.HttpClient> =>
|
|
306
|
+
Effect.gen(function* () {
|
|
307
|
+
const all: DocumentRecord[] = [];
|
|
308
|
+
let cursor: string | undefined;
|
|
309
|
+
do {
|
|
310
|
+
const url =
|
|
311
|
+
endpoints.listRecords(pds, did, DOCUMENT_COLLECTION, 100) +
|
|
312
|
+
(cursor ? `&cursor=${encodeURIComponent(cursor)}` : '');
|
|
313
|
+
const page = yield* getJson(ListRecordsResponse, url, proxy);
|
|
314
|
+
all.push(...(page.records ?? []));
|
|
315
|
+
cursor = page.cursor;
|
|
316
|
+
} while (cursor);
|
|
317
|
+
return all;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
/** Best-effort author profile lookup (display name / avatar / bio); undefined on any failure. */
|
|
321
|
+
const fetchProfile = (
|
|
322
|
+
actor: string,
|
|
323
|
+
proxy?: string,
|
|
324
|
+
): Effect.Effect<Profile | undefined, never, HttpClient.HttpClient> =>
|
|
325
|
+
getJson(Profile, endpoints.getProfile(actor), proxy).pipe(Effect.catchAll(() => Effect.succeed(undefined)));
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Resolves a publication `site` reference to its {@link Publication} metadata:
|
|
329
|
+
* - `https://` → canonical URL directly (no record fetch needed).
|
|
330
|
+
* - `at://` → `getRecord` the publication record for `url`/`name`.
|
|
331
|
+
*/
|
|
332
|
+
const resolvePublication = (
|
|
333
|
+
site: string,
|
|
334
|
+
proxy?: string,
|
|
335
|
+
): Effect.Effect<Publication, FeedFetchError, HttpClient.HttpClient> => {
|
|
336
|
+
if (site.startsWith('http')) {
|
|
337
|
+
return Effect.succeed({ site, url: site.replace(/\/$/, '') });
|
|
338
|
+
}
|
|
339
|
+
if (site.startsWith('at://')) {
|
|
340
|
+
const parsed = parseAtUri(site);
|
|
341
|
+
if (!parsed) {
|
|
342
|
+
return Effect.succeed({ site });
|
|
343
|
+
}
|
|
344
|
+
return Effect.gen(function* () {
|
|
345
|
+
const pds = yield* resolvePds(parsed.did, proxy);
|
|
346
|
+
const record = yield* getJson(
|
|
347
|
+
PublicationRecord,
|
|
348
|
+
endpoints.getRecord(pds, parsed.did, parsed.collection, parsed.rkey),
|
|
349
|
+
proxy,
|
|
350
|
+
);
|
|
351
|
+
const value = record.value ?? {};
|
|
352
|
+
return { site, url: value.url ? value.url.replace(/\/$/, '') : undefined, name: value.name };
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return Effect.succeed({ site });
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
//
|
|
359
|
+
// Pure helpers.
|
|
360
|
+
//
|
|
361
|
+
|
|
362
|
+
/** Splits an `at://{did}/{collection}/{rkey}` URI into its parts; undefined when malformed. */
|
|
363
|
+
const parseAtUri = (uri: string): { did: string; collection: string; rkey: string } | undefined => {
|
|
364
|
+
const match = uri.match(/^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/);
|
|
365
|
+
return match ? { did: match[1], collection: match[2], rkey: match[3] } : undefined;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
/** Joins a publication base URL with a document path into a canonical link. */
|
|
369
|
+
const joinUrl = (base: string | undefined, path: string | undefined): string | undefined => {
|
|
370
|
+
if (!base) {
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
if (!path) {
|
|
374
|
+
return base;
|
|
375
|
+
}
|
|
376
|
+
return `${base}${path.startsWith('/') ? '' : '/'}${path}`;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const isString = (value: string | undefined): value is string => typeof value === 'string';
|
|
380
|
+
|
|
381
|
+
const distinct = (values: readonly string[]): string[] => [...new Set(values)];
|