@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
package/dx.config.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config2 } from '@dxos/app-framework/config';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
export default Config2.make({
|
|
9
|
+
plugin: {
|
|
10
|
+
key: 'org.dxos.plugin.magazine',
|
|
11
|
+
name: 'Magazine',
|
|
12
|
+
author: 'DXOS',
|
|
13
|
+
description: trim`
|
|
14
|
+
MagazinePlugin brings syndicated content into DXOS Composer by syncing RSS feeds
|
|
15
|
+
and ATproto (Bluesky) feeds as ECHO objects stored in the user's space.
|
|
16
|
+
Subscribed feeds are polled incrementally using a cursor so only new posts
|
|
17
|
+
are fetched on each sync, and feed metadata (name, icon, description) is
|
|
18
|
+
persisted alongside the posts for offline access.
|
|
19
|
+
|
|
20
|
+
Magazines are agent-curated collections drawn from one or more feeds. Each
|
|
21
|
+
Magazine carries the user's topic instructions describing what it should
|
|
22
|
+
cover; pressing the Curate button (or a scheduled trigger) syncs all
|
|
23
|
+
referenced feeds in parallel, then runs a single-shot agent pass that selects
|
|
24
|
+
matching posts and returns their ids, which the operation adds mechanically.
|
|
25
|
+
The agent's base methodology — how to select, how to dedup, and the structured
|
|
26
|
+
output contract — lives in the MagazineSkill. Each magazine stores a
|
|
27
|
+
persisted curation Routine (created at magazine creation time) whose instructions
|
|
28
|
+
hold the editorial brief; pressing Curate runs that Routine against the synced
|
|
29
|
+
candidates via RunInstructions. The skill also exposes a FetchArticleContent
|
|
30
|
+
tool the agent may call to read a candidate's full text when title and
|
|
31
|
+
description are insufficient to judge.
|
|
32
|
+
A short snippet and hero image are derived mechanically from each post for
|
|
33
|
+
display. A Masonry grid renders the curated tiles with images, titles, author
|
|
34
|
+
metadata, and read-state dimming.
|
|
35
|
+
|
|
36
|
+
Clicking a tile opens the PostArticle detail surface via the deck's layout
|
|
37
|
+
dispatch, routing to the appropriate location (complementary drawer, sibling
|
|
38
|
+
plank, or deck companion) based on the current layout mode. The schema also
|
|
39
|
+
scaffolds a future tagging feature — posts carry an optional tags array and
|
|
40
|
+
the tile renders a tag row when tags are present — enabling navigation and
|
|
41
|
+
feed-discovery flows in a later revision.
|
|
42
|
+
`,
|
|
43
|
+
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-magazine',
|
|
44
|
+
icon: { key: 'ph--book-open-text--regular', hue: 'orange' },
|
|
45
|
+
spec: 'PLUGIN.mdl',
|
|
46
|
+
tags: ['labs'],
|
|
47
|
+
},
|
|
48
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/plugin-magazine",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Magazine plugin for RSS and open protocol subscriptions.",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
12
|
+
"author": "DXOS.org",
|
|
13
|
+
"sideEffects": true,
|
|
14
|
+
"type": "module",
|
|
15
|
+
"imports": {
|
|
16
|
+
"#atoms": {
|
|
17
|
+
"source": "./src/atoms/index.ts",
|
|
18
|
+
"types": "./dist/types/src/atoms/index.d.ts",
|
|
19
|
+
"default": "./dist/lib/neutral/atoms/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"#skills": {
|
|
22
|
+
"source": "./src/skills/index.ts",
|
|
23
|
+
"types": "./dist/types/src/skills/index.d.ts",
|
|
24
|
+
"default": "./dist/lib/neutral/skills/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"#capabilities": {
|
|
27
|
+
"source": "./src/capabilities/index.ts",
|
|
28
|
+
"types": "./dist/types/src/capabilities/index.d.ts",
|
|
29
|
+
"default": "./dist/lib/neutral/capabilities/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"#components": {
|
|
32
|
+
"source": "./src/components/index.ts",
|
|
33
|
+
"types": "./dist/types/src/components/index.d.ts",
|
|
34
|
+
"default": "./dist/lib/neutral/components/index.mjs"
|
|
35
|
+
},
|
|
36
|
+
"#containers": {
|
|
37
|
+
"source": "./src/containers/index.ts",
|
|
38
|
+
"types": "./dist/types/src/containers/index.d.ts",
|
|
39
|
+
"default": "./dist/lib/neutral/containers/index.mjs"
|
|
40
|
+
},
|
|
41
|
+
"#meta": {
|
|
42
|
+
"source": "./src/meta.ts",
|
|
43
|
+
"types": "./dist/types/src/meta.d.ts",
|
|
44
|
+
"default": "./dist/lib/neutral/meta.mjs"
|
|
45
|
+
},
|
|
46
|
+
"#operations": {
|
|
47
|
+
"source": "./src/operations/index.ts",
|
|
48
|
+
"types": "./dist/types/src/operations/index.d.ts",
|
|
49
|
+
"default": "./dist/lib/neutral/operations/index.mjs"
|
|
50
|
+
},
|
|
51
|
+
"#plugin": {
|
|
52
|
+
"source": {
|
|
53
|
+
"workerd": "./src/MagazinePlugin.workerd.ts",
|
|
54
|
+
"default": "./src/MagazinePlugin.tsx"
|
|
55
|
+
},
|
|
56
|
+
"types": "./dist/types/src/MagazinePlugin.d.ts",
|
|
57
|
+
"workerd": "./dist/lib/neutral/MagazinePlugin.workerd.mjs",
|
|
58
|
+
"default": "./dist/lib/neutral/MagazinePlugin.mjs"
|
|
59
|
+
},
|
|
60
|
+
"#testing": "./src/testing/index.ts",
|
|
61
|
+
"#translations": {
|
|
62
|
+
"source": "./src/translations.ts",
|
|
63
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
64
|
+
"default": "./dist/lib/neutral/translations.mjs"
|
|
65
|
+
},
|
|
66
|
+
"#types": {
|
|
67
|
+
"source": "./src/types/index.ts",
|
|
68
|
+
"types": "./dist/types/src/types/index.d.ts",
|
|
69
|
+
"default": "./dist/lib/neutral/types/index.mjs"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"exports": {
|
|
73
|
+
".": {
|
|
74
|
+
"source": "./src/index.ts",
|
|
75
|
+
"types": "./dist/types/src/index.d.ts",
|
|
76
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
77
|
+
},
|
|
78
|
+
"./assets/PLUGIN.mdl": "./PLUGIN.mdl",
|
|
79
|
+
"./plugin": {
|
|
80
|
+
"source": {
|
|
81
|
+
"workerd": "./src/plugin.workerd.ts",
|
|
82
|
+
"default": "./src/plugin.ts"
|
|
83
|
+
},
|
|
84
|
+
"types": "./dist/types/src/plugin.d.ts",
|
|
85
|
+
"workerd": "./dist/lib/neutral/plugin.workerd.mjs",
|
|
86
|
+
"default": "./dist/lib/neutral/plugin.mjs"
|
|
87
|
+
},
|
|
88
|
+
"./translations": {
|
|
89
|
+
"source": "./src/translations.ts",
|
|
90
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
91
|
+
"default": "./dist/lib/neutral/translations.mjs"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"types": "dist/types/src/index.d.ts",
|
|
95
|
+
"files": [
|
|
96
|
+
"dist",
|
|
97
|
+
"dx.config.ts",
|
|
98
|
+
"src",
|
|
99
|
+
"PLUGIN.mdl"
|
|
100
|
+
],
|
|
101
|
+
"dependencies": {
|
|
102
|
+
"@dxos/app-framework": "workspace:*",
|
|
103
|
+
"@dxos/app-toolkit": "workspace:*",
|
|
104
|
+
"@dxos/assistant-toolkit": "workspace:*",
|
|
105
|
+
"@dxos/client": "workspace:*",
|
|
106
|
+
"@dxos/compute": "workspace:*",
|
|
107
|
+
"@dxos/echo": "workspace:*",
|
|
108
|
+
"@dxos/echo-react": "workspace:*",
|
|
109
|
+
"@dxos/effect": "workspace:*",
|
|
110
|
+
"@dxos/invariant": "workspace:*",
|
|
111
|
+
"@dxos/keys": "workspace:*",
|
|
112
|
+
"@dxos/log": "workspace:*",
|
|
113
|
+
"@dxos/markdown": "workspace:*",
|
|
114
|
+
"@dxos/plugin-assistant": "workspace:*",
|
|
115
|
+
"@dxos/plugin-attention": "workspace:*",
|
|
116
|
+
"@dxos/plugin-client": "workspace:*",
|
|
117
|
+
"@dxos/plugin-graph": "workspace:*",
|
|
118
|
+
"@dxos/plugin-routine": "workspace:*",
|
|
119
|
+
"@dxos/plugin-space": "workspace:*",
|
|
120
|
+
"@dxos/react-client": "workspace:*",
|
|
121
|
+
"@dxos/react-ui-attention": "workspace:*",
|
|
122
|
+
"@dxos/react-ui-form": "workspace:*",
|
|
123
|
+
"@dxos/react-ui-markdown": "workspace:*",
|
|
124
|
+
"@dxos/react-ui-masonry": "workspace:*",
|
|
125
|
+
"@dxos/react-ui-menu": "workspace:*",
|
|
126
|
+
"@dxos/schema": "workspace:*",
|
|
127
|
+
"@dxos/util": "workspace:*",
|
|
128
|
+
"@effect-atom/atom-react": "catalog:",
|
|
129
|
+
"@effect/platform": "catalog:",
|
|
130
|
+
"date-fns": "catalog:",
|
|
131
|
+
"defuddle": "catalog:",
|
|
132
|
+
"fast-xml-parser": "catalog:"
|
|
133
|
+
},
|
|
134
|
+
"devDependencies": {
|
|
135
|
+
"@dxos/ai": "workspace:*",
|
|
136
|
+
"@dxos/echo-client": "workspace:*",
|
|
137
|
+
"@dxos/functions-runtime": "workspace:*",
|
|
138
|
+
"@dxos/plugin-testing": "workspace:*",
|
|
139
|
+
"@dxos/random": "workspace:*",
|
|
140
|
+
"@dxos/react-ui": "workspace:*",
|
|
141
|
+
"@dxos/react-ui-mosaic": "workspace:*",
|
|
142
|
+
"@dxos/react-ui-syntax-highlighter": "workspace:*",
|
|
143
|
+
"@dxos/storybook-utils": "workspace:*",
|
|
144
|
+
"@dxos/ui-theme": "workspace:*",
|
|
145
|
+
"@storybook/react-vite": "catalog:",
|
|
146
|
+
"@types/react": "catalog:",
|
|
147
|
+
"@types/react-dom": "catalog:",
|
|
148
|
+
"effect": "catalog:",
|
|
149
|
+
"react": "catalog:",
|
|
150
|
+
"react-dom": "catalog:",
|
|
151
|
+
"vite": "catalog:"
|
|
152
|
+
},
|
|
153
|
+
"peerDependencies": {
|
|
154
|
+
"@dxos/react-ui": "workspace:*",
|
|
155
|
+
"@dxos/react-ui-mosaic": "workspace:*",
|
|
156
|
+
"@dxos/ui-theme": "workspace:*",
|
|
157
|
+
"effect": "catalog:",
|
|
158
|
+
"react": "catalog:",
|
|
159
|
+
"react-dom": "catalog:"
|
|
160
|
+
},
|
|
161
|
+
"publishConfig": {
|
|
162
|
+
"access": "public"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { ActivationEvents } from '@dxos/app-framework';
|
|
8
|
+
import { ClientPlugin } from '@dxos/plugin-client/plugin';
|
|
9
|
+
import { createComposerTestApp } from '@dxos/plugin-testing/harness';
|
|
10
|
+
|
|
11
|
+
import { MagazinePlugin } from '#plugin';
|
|
12
|
+
|
|
13
|
+
import { meta } from './meta';
|
|
14
|
+
|
|
15
|
+
const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
|
|
16
|
+
|
|
17
|
+
describe('MagazinePlugin', () => {
|
|
18
|
+
test('modules activate on the expected events', async ({ expect }) => {
|
|
19
|
+
await using harness = await createComposerTestApp({
|
|
20
|
+
plugins: [ClientPlugin({}), MagazinePlugin()],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(harness.manager.getActive()).toEqual(
|
|
24
|
+
expect.arrayContaining([
|
|
25
|
+
moduleId('AppGraphBuilder'),
|
|
26
|
+
moduleId('CreateObject'),
|
|
27
|
+
moduleId('schema'),
|
|
28
|
+
moduleId('ReactSurface'),
|
|
29
|
+
]),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Operation handlers are not loaded on startup — SetupProcessManager fires lazily when an operation is invoked.
|
|
33
|
+
await harness.fire(ActivationEvents.SetupProcessManager);
|
|
34
|
+
expect(harness.manager.getActive()).toContain(moduleId('OperationHandler'));
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ActivationEvent, Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
|
|
7
|
+
import { Instructions } from '@dxos/compute';
|
|
8
|
+
import { AttentionEvents } from '@dxos/plugin-attention';
|
|
9
|
+
import { ClientEvents } from '@dxos/plugin-client';
|
|
10
|
+
import { StateMap, TagIndex } from '@dxos/schema';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
AppGraphBuilder,
|
|
14
|
+
CreateObject,
|
|
15
|
+
NavigationResolver,
|
|
16
|
+
OperationHandler,
|
|
17
|
+
ReactSurface,
|
|
18
|
+
RoutineTemplates,
|
|
19
|
+
SkillDefinition,
|
|
20
|
+
} from '#capabilities';
|
|
21
|
+
import { meta } from '#meta';
|
|
22
|
+
import { translations } from '#translations';
|
|
23
|
+
import { Magazine, Subscription } from '#types';
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line import/no-relative-packages
|
|
26
|
+
import pluginSpec from '../PLUGIN.mdl?raw';
|
|
27
|
+
|
|
28
|
+
export const MagazinePlugin = Plugin.define(meta).pipe(
|
|
29
|
+
AppPlugin.addAppGraphModule({
|
|
30
|
+
activatesOn: ActivationEvent.allOf(AppActivationEvents.SetupAppGraph, AttentionEvents.AttentionReady),
|
|
31
|
+
activate: AppGraphBuilder,
|
|
32
|
+
}),
|
|
33
|
+
AppPlugin.addCreateObjectModule({ activate: CreateObject }),
|
|
34
|
+
AppPlugin.addNavigationResolverModule({ activatesOn: ClientEvents.ClientReady, activate: NavigationResolver }),
|
|
35
|
+
AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),
|
|
36
|
+
AppPlugin.addPluginAssetModule({
|
|
37
|
+
asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },
|
|
38
|
+
}),
|
|
39
|
+
AppPlugin.addSchemaModule({
|
|
40
|
+
schema: [
|
|
41
|
+
Subscription.Subscription,
|
|
42
|
+
Subscription.Post,
|
|
43
|
+
Subscription.PostContent,
|
|
44
|
+
Magazine.Magazine,
|
|
45
|
+
Instructions.Instructions,
|
|
46
|
+
StateMap.StateMap,
|
|
47
|
+
TagIndex.TagIndex,
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
AppPlugin.addSkillDefinitionModule({ activate: SkillDefinition }),
|
|
51
|
+
AppPlugin.addSurfaceModule({ activate: ReactSurface }),
|
|
52
|
+
AppPlugin.addTranslationsModule({ translations }),
|
|
53
|
+
Plugin.addModule({
|
|
54
|
+
id: 'magazine-automation-templates',
|
|
55
|
+
activatesOn: AppActivationEvents.SetupSchema,
|
|
56
|
+
activate: RoutineTemplates,
|
|
57
|
+
}),
|
|
58
|
+
Plugin.make,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export default MagazinePlugin;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { AppPlugin } from '@dxos/app-toolkit';
|
|
7
|
+
import { StateMap, TagIndex } from '@dxos/schema';
|
|
8
|
+
|
|
9
|
+
import { meta } from '#meta';
|
|
10
|
+
import { Magazine, Subscription } from '#types';
|
|
11
|
+
|
|
12
|
+
import OperationHandler from './capabilities/operation-handler';
|
|
13
|
+
import SkillDefinition from './capabilities/skill-definition';
|
|
14
|
+
|
|
15
|
+
export const MagazinePlugin = Plugin.define(meta).pipe(
|
|
16
|
+
AppPlugin.addSkillDefinitionModule({ id: 'skill-definition', activate: SkillDefinition }),
|
|
17
|
+
AppPlugin.addOperationHandlerModule({ id: 'operation-handler', activate: OperationHandler }),
|
|
18
|
+
AppPlugin.addSchemaModule({
|
|
19
|
+
schema: [
|
|
20
|
+
Subscription.Subscription,
|
|
21
|
+
Subscription.Post,
|
|
22
|
+
Subscription.PostContent,
|
|
23
|
+
Magazine.Magazine,
|
|
24
|
+
StateMap.StateMap,
|
|
25
|
+
TagIndex.TagIndex,
|
|
26
|
+
],
|
|
27
|
+
}),
|
|
28
|
+
Plugin.make,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export default MagazinePlugin;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Data from 'effect/Data';
|
|
7
|
+
import { afterEach, beforeEach, describe, test } from 'vitest';
|
|
8
|
+
|
|
9
|
+
import { Feed, Obj, Ref, Tag } from '@dxos/echo';
|
|
10
|
+
import { EchoTestBuilder } from '@dxos/echo-client/testing';
|
|
11
|
+
import { StateMap, TagIndex } from '@dxos/schema';
|
|
12
|
+
|
|
13
|
+
import { Magazine, Subscription } from '../types';
|
|
14
|
+
import { postCurationAtom } from './post-curation';
|
|
15
|
+
import { postDisplayAtom } from './post-display';
|
|
16
|
+
import { postReadAtom } from './post-read';
|
|
17
|
+
import { postTagsAtom } from './post-tags';
|
|
18
|
+
|
|
19
|
+
describe('postReadAtom', () => {
|
|
20
|
+
let builder: EchoTestBuilder;
|
|
21
|
+
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
builder = await new EchoTestBuilder().open();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
await builder.close();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('fires when this post is marked read', async ({ expect }) => {
|
|
31
|
+
const { db } = await builder.createDatabase({
|
|
32
|
+
types: [Tag.Tag, Subscription.Subscription, Subscription.Post, StateMap.StateMap, TagIndex.TagIndex],
|
|
33
|
+
});
|
|
34
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
35
|
+
const post = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
36
|
+
await db.flush();
|
|
37
|
+
|
|
38
|
+
const registry = Registry.make();
|
|
39
|
+
const atom = postReadAtom(post);
|
|
40
|
+
let fireCount = 0;
|
|
41
|
+
registry.subscribe(atom, () => fireCount++, { immediate: true });
|
|
42
|
+
expect(fireCount).toBe(1);
|
|
43
|
+
|
|
44
|
+
Subscription.setReadAt(subscription, post.id, new Date().toISOString());
|
|
45
|
+
await db.flush();
|
|
46
|
+
expect(fireCount).toBe(2);
|
|
47
|
+
expect(registry.get(atom).readAt).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('does not fire when a sibling post is marked read', async ({ expect }) => {
|
|
51
|
+
const { db } = await builder.createDatabase({
|
|
52
|
+
types: [Tag.Tag, Subscription.Subscription, Subscription.Post, StateMap.StateMap, TagIndex.TagIndex],
|
|
53
|
+
});
|
|
54
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
55
|
+
const postA = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
56
|
+
const postB = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
57
|
+
await db.flush();
|
|
58
|
+
|
|
59
|
+
const registry = Registry.make();
|
|
60
|
+
const atomA = postReadAtom(postA);
|
|
61
|
+
let fireCount = 0;
|
|
62
|
+
registry.subscribe(atomA, () => fireCount++, { immediate: true });
|
|
63
|
+
expect(fireCount).toBe(1);
|
|
64
|
+
|
|
65
|
+
Subscription.setReadAt(subscription, postB.id, new Date().toISOString());
|
|
66
|
+
await db.flush();
|
|
67
|
+
expect(fireCount).toBe(1); // postA's readAt unchanged — should not re-fire.
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('does not fire when an unrelated subscription field changes', async ({ expect }) => {
|
|
71
|
+
const { db } = await builder.createDatabase({
|
|
72
|
+
types: [Tag.Tag, Subscription.Subscription, Subscription.Post, StateMap.StateMap, TagIndex.TagIndex],
|
|
73
|
+
});
|
|
74
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
75
|
+
const post = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
76
|
+
await db.flush();
|
|
77
|
+
|
|
78
|
+
const registry = Registry.make();
|
|
79
|
+
const atom = postReadAtom(post);
|
|
80
|
+
let fireCount = 0;
|
|
81
|
+
registry.subscribe(atom, () => fireCount++, { immediate: true });
|
|
82
|
+
expect(fireCount).toBe(1);
|
|
83
|
+
|
|
84
|
+
Obj.update(subscription, (subscription) => {
|
|
85
|
+
subscription.name = 'Updated';
|
|
86
|
+
});
|
|
87
|
+
await db.flush();
|
|
88
|
+
expect(fireCount).toBe(1); // name change — should not re-fire.
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('postTagsAtom', () => {
|
|
93
|
+
let builder: EchoTestBuilder;
|
|
94
|
+
|
|
95
|
+
beforeEach(async () => {
|
|
96
|
+
builder = await new EchoTestBuilder().open();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
afterEach(async () => {
|
|
100
|
+
await builder.close();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('fires when this post is starred', async ({ expect }) => {
|
|
104
|
+
const { db } = await builder.createDatabase({
|
|
105
|
+
types: [Tag.Tag, Subscription.Subscription, Subscription.Post, StateMap.StateMap, TagIndex.TagIndex],
|
|
106
|
+
});
|
|
107
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
108
|
+
const post = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
109
|
+
await db.flush();
|
|
110
|
+
|
|
111
|
+
const registry = Registry.make();
|
|
112
|
+
const atom = postTagsAtom(post);
|
|
113
|
+
let fireCount = 0;
|
|
114
|
+
registry.subscribe(atom, () => fireCount++, { immediate: true });
|
|
115
|
+
expect(fireCount).toBe(1);
|
|
116
|
+
expect(registry.get(atom).starred).toBe(false);
|
|
117
|
+
|
|
118
|
+
await Subscription.setTag(subscription, post.id, db, 'starred', true);
|
|
119
|
+
await db.flush();
|
|
120
|
+
// tagUrisAtom fires once when Tag.Tag is created (intermediate re-run, still starred=false),
|
|
121
|
+
// then Obj.subscribe fires when subscription.tags is mutated (starred=true, setSelf).
|
|
122
|
+
expect(fireCount).toBe(3);
|
|
123
|
+
expect(registry.get(atom).starred).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('does not fire when a sibling post is starred (after tag warm-up)', async ({ expect }) => {
|
|
127
|
+
const { db } = await builder.createDatabase({
|
|
128
|
+
types: [Tag.Tag, Subscription.Subscription, Subscription.Post, StateMap.StateMap, TagIndex.TagIndex],
|
|
129
|
+
});
|
|
130
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
131
|
+
const postA = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
132
|
+
const postB = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
133
|
+
// Pre-warm the Tag.Tag so tagUrisAtom is already populated when the atom subscribes.
|
|
134
|
+
// Without this, the first setTag creates Tag.Tag → tagUrisAtom fires → one extra body re-run.
|
|
135
|
+
await Subscription.setTag(subscription, postA.id, db, 'starred', true);
|
|
136
|
+
await Subscription.setTag(subscription, postA.id, db, 'starred', false);
|
|
137
|
+
await db.flush();
|
|
138
|
+
|
|
139
|
+
const registry = Registry.make();
|
|
140
|
+
const atomA = postTagsAtom(postA);
|
|
141
|
+
let fireCount = 0;
|
|
142
|
+
registry.subscribe(atomA, () => fireCount++, { immediate: true });
|
|
143
|
+
expect(fireCount).toBe(1);
|
|
144
|
+
|
|
145
|
+
await Subscription.setTag(subscription, postB.id, db, 'starred', true);
|
|
146
|
+
await db.flush();
|
|
147
|
+
expect(fireCount).toBe(1); // postA's tags unchanged — should not re-fire.
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('postCurationAtom', () => {
|
|
152
|
+
let builder: EchoTestBuilder;
|
|
153
|
+
|
|
154
|
+
beforeEach(async () => {
|
|
155
|
+
builder = await new EchoTestBuilder().open();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
afterEach(async () => {
|
|
159
|
+
await builder.close();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('fires when this post gets a curated snippet', async ({ expect }) => {
|
|
163
|
+
const { db } = await builder.createDatabase({
|
|
164
|
+
types: [
|
|
165
|
+
Tag.Tag,
|
|
166
|
+
Feed.Feed,
|
|
167
|
+
Subscription.Subscription,
|
|
168
|
+
Subscription.Post,
|
|
169
|
+
Magazine.Magazine,
|
|
170
|
+
StateMap.StateMap,
|
|
171
|
+
TagIndex.TagIndex,
|
|
172
|
+
],
|
|
173
|
+
});
|
|
174
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
175
|
+
const post = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
176
|
+
const magazine = db.add(Magazine.make({ feeds: [Ref.make(subscription)] }));
|
|
177
|
+
await db.flush();
|
|
178
|
+
|
|
179
|
+
const registry = Registry.make();
|
|
180
|
+
const atom = postCurationAtom(Data.tuple(post, magazine));
|
|
181
|
+
let fireCount = 0;
|
|
182
|
+
registry.subscribe(atom, () => fireCount++, { immediate: true });
|
|
183
|
+
expect(fireCount).toBe(1);
|
|
184
|
+
expect(registry.get(atom).snippet).toBeUndefined();
|
|
185
|
+
|
|
186
|
+
Magazine.patchPostState(magazine, post.id, { snippet: 'A great article about AI.' });
|
|
187
|
+
await db.flush();
|
|
188
|
+
expect(fireCount).toBe(2);
|
|
189
|
+
expect(registry.get(atom).snippet).toBe('A great article about AI.');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('does not fire when a sibling post gets a snippet', async ({ expect }) => {
|
|
193
|
+
const { db } = await builder.createDatabase({
|
|
194
|
+
types: [
|
|
195
|
+
Tag.Tag,
|
|
196
|
+
Feed.Feed,
|
|
197
|
+
Subscription.Subscription,
|
|
198
|
+
Subscription.Post,
|
|
199
|
+
Magazine.Magazine,
|
|
200
|
+
StateMap.StateMap,
|
|
201
|
+
TagIndex.TagIndex,
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
205
|
+
const postA = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
206
|
+
const postB = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
207
|
+
const magazine = db.add(Magazine.make({ feeds: [Ref.make(subscription)] }));
|
|
208
|
+
await db.flush();
|
|
209
|
+
|
|
210
|
+
const registry = Registry.make();
|
|
211
|
+
const atomA = postCurationAtom(Data.tuple(postA, magazine));
|
|
212
|
+
let fireCount = 0;
|
|
213
|
+
registry.subscribe(atomA, () => fireCount++, { immediate: true });
|
|
214
|
+
expect(fireCount).toBe(1);
|
|
215
|
+
|
|
216
|
+
Magazine.patchPostState(magazine, postB.id, { snippet: 'Sibling snippet.' });
|
|
217
|
+
await db.flush();
|
|
218
|
+
expect(fireCount).toBe(1); // postA's curation unchanged — should not re-fire.
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('does not fire when an unrelated magazine field changes', async ({ expect }) => {
|
|
222
|
+
const { db } = await builder.createDatabase({
|
|
223
|
+
types: [
|
|
224
|
+
Tag.Tag,
|
|
225
|
+
Feed.Feed,
|
|
226
|
+
Subscription.Subscription,
|
|
227
|
+
Subscription.Post,
|
|
228
|
+
Magazine.Magazine,
|
|
229
|
+
StateMap.StateMap,
|
|
230
|
+
TagIndex.TagIndex,
|
|
231
|
+
],
|
|
232
|
+
});
|
|
233
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Test', url: 'https://example.com' }));
|
|
234
|
+
const post = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
235
|
+
const magazine = db.add(Magazine.make({ feeds: [Ref.make(subscription)] }));
|
|
236
|
+
await db.flush();
|
|
237
|
+
|
|
238
|
+
const registry = Registry.make();
|
|
239
|
+
const atom = postCurationAtom(Data.tuple(post, magazine));
|
|
240
|
+
let fireCount = 0;
|
|
241
|
+
registry.subscribe(atom, () => fireCount++, { immediate: true });
|
|
242
|
+
expect(fireCount).toBe(1);
|
|
243
|
+
|
|
244
|
+
Obj.update(magazine, (magazine) => {
|
|
245
|
+
magazine.name = 'Updated Magazine';
|
|
246
|
+
});
|
|
247
|
+
await db.flush();
|
|
248
|
+
expect(fireCount).toBe(1); // name change — should not re-fire.
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('postDisplayAtom', () => {
|
|
253
|
+
let builder: EchoTestBuilder;
|
|
254
|
+
|
|
255
|
+
beforeEach(async () => {
|
|
256
|
+
builder = await new EchoTestBuilder().open();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
afterEach(async () => {
|
|
260
|
+
await builder.close();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('prefers agent snippet over RSS description', async ({ expect }) => {
|
|
264
|
+
const { db } = await builder.createDatabase({
|
|
265
|
+
types: [
|
|
266
|
+
Tag.Tag,
|
|
267
|
+
Feed.Feed,
|
|
268
|
+
Subscription.Subscription,
|
|
269
|
+
Subscription.Post,
|
|
270
|
+
Magazine.Magazine,
|
|
271
|
+
StateMap.StateMap,
|
|
272
|
+
TagIndex.TagIndex,
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Feed', url: 'https://example.com' }));
|
|
276
|
+
const post = db.add(
|
|
277
|
+
Obj.make(Subscription.Post, { source: Ref.make(subscription), description: 'RSS description.' }),
|
|
278
|
+
);
|
|
279
|
+
const magazine = db.add(Magazine.make({ feeds: [Ref.make(subscription)], posts: [Ref.make(post)] }));
|
|
280
|
+
await db.flush();
|
|
281
|
+
|
|
282
|
+
const registry = Registry.make();
|
|
283
|
+
const atom = postDisplayAtom(Data.tuple(post, magazine));
|
|
284
|
+
registry.subscribe(atom, () => {}, { immediate: true });
|
|
285
|
+
|
|
286
|
+
// Before curation: falls back to RSS description.
|
|
287
|
+
expect(registry.get(atom).snippet).toContain('RSS description');
|
|
288
|
+
|
|
289
|
+
Magazine.patchPostState(magazine, post.id, { snippet: 'Agent snippet.' });
|
|
290
|
+
await db.flush();
|
|
291
|
+
|
|
292
|
+
expect(registry.get(atom).snippet).toBe('Agent snippet.');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('fires on read state change', async ({ expect }) => {
|
|
296
|
+
const { db } = await builder.createDatabase({
|
|
297
|
+
types: [
|
|
298
|
+
Tag.Tag,
|
|
299
|
+
Feed.Feed,
|
|
300
|
+
Subscription.Subscription,
|
|
301
|
+
Subscription.Post,
|
|
302
|
+
Magazine.Magazine,
|
|
303
|
+
StateMap.StateMap,
|
|
304
|
+
TagIndex.TagIndex,
|
|
305
|
+
],
|
|
306
|
+
});
|
|
307
|
+
const subscription = db.add(Subscription.makeSubscription({ name: 'Feed', url: 'https://example.com' }));
|
|
308
|
+
const post = db.add(Obj.make(Subscription.Post, { source: Ref.make(subscription) }));
|
|
309
|
+
const magazine = db.add(Magazine.make({ feeds: [Ref.make(subscription)], posts: [Ref.make(post)] }));
|
|
310
|
+
await db.flush();
|
|
311
|
+
|
|
312
|
+
const registry = Registry.make();
|
|
313
|
+
const atom = postDisplayAtom(Data.tuple(post, magazine));
|
|
314
|
+
let fireCount = 0;
|
|
315
|
+
registry.subscribe(atom, () => fireCount++, { immediate: true });
|
|
316
|
+
expect(registry.get(atom).read).toBe(false);
|
|
317
|
+
|
|
318
|
+
Subscription.setReadAt(subscription, post.id, new Date().toISOString());
|
|
319
|
+
await db.flush();
|
|
320
|
+
expect(registry.get(atom).read).toBe(true);
|
|
321
|
+
expect(fireCount).toBe(2);
|
|
322
|
+
});
|
|
323
|
+
});
|