@growth-labs/cms 0.1.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/README.md +165 -0
- package/dist/engine/activity-log.d.ts +17 -0
- package/dist/engine/activity-log.d.ts.map +1 -0
- package/dist/engine/activity-log.js +17 -0
- package/dist/engine/activity-log.js.map +1 -0
- package/dist/engine/ai-prompts.d.ts +57 -0
- package/dist/engine/ai-prompts.d.ts.map +1 -0
- package/dist/engine/ai-prompts.js +90 -0
- package/dist/engine/ai-prompts.js.map +1 -0
- package/dist/engine/ai-writeback.d.ts +36 -0
- package/dist/engine/ai-writeback.d.ts.map +1 -0
- package/dist/engine/ai-writeback.js +45 -0
- package/dist/engine/ai-writeback.js.map +1 -0
- package/dist/engine/api-keys.d.ts +76 -0
- package/dist/engine/api-keys.d.ts.map +1 -0
- package/dist/engine/api-keys.js +165 -0
- package/dist/engine/api-keys.js.map +1 -0
- package/dist/engine/content-insights.d.ts +36 -0
- package/dist/engine/content-insights.d.ts.map +1 -0
- package/dist/engine/content-insights.js +114 -0
- package/dist/engine/content-insights.js.map +1 -0
- package/dist/engine/contributors.d.ts +25 -0
- package/dist/engine/contributors.d.ts.map +1 -0
- package/dist/engine/contributors.js +59 -0
- package/dist/engine/contributors.js.map +1 -0
- package/dist/engine/cron.d.ts +15 -0
- package/dist/engine/cron.d.ts.map +1 -0
- package/dist/engine/cron.js +33 -0
- package/dist/engine/cron.js.map +1 -0
- package/dist/engine/d1.d.ts +16 -0
- package/dist/engine/d1.d.ts.map +1 -0
- package/dist/engine/d1.js +13 -0
- package/dist/engine/d1.js.map +1 -0
- package/dist/engine/foundry-dispatch.d.ts +52 -0
- package/dist/engine/foundry-dispatch.d.ts.map +1 -0
- package/dist/engine/foundry-dispatch.js +290 -0
- package/dist/engine/foundry-dispatch.js.map +1 -0
- package/dist/engine/import-parsers.d.ts +11 -0
- package/dist/engine/import-parsers.d.ts.map +1 -0
- package/dist/engine/import-parsers.js +373 -0
- package/dist/engine/import-parsers.js.map +1 -0
- package/dist/engine/index.d.ts +28 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +57 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/invites.d.ts +78 -0
- package/dist/engine/invites.d.ts.map +1 -0
- package/dist/engine/invites.js +158 -0
- package/dist/engine/invites.js.map +1 -0
- package/dist/engine/members.d.ts +59 -0
- package/dist/engine/members.d.ts.map +1 -0
- package/dist/engine/members.js +124 -0
- package/dist/engine/members.js.map +1 -0
- package/dist/engine/membership-rules.d.ts +25 -0
- package/dist/engine/membership-rules.d.ts.map +1 -0
- package/dist/engine/membership-rules.js +44 -0
- package/dist/engine/membership-rules.js.map +1 -0
- package/dist/engine/og-render.d.ts +40 -0
- package/dist/engine/og-render.d.ts.map +1 -0
- package/dist/engine/og-render.js +26 -0
- package/dist/engine/og-render.js.map +1 -0
- package/dist/engine/publish-guard.d.ts +58 -0
- package/dist/engine/publish-guard.d.ts.map +1 -0
- package/dist/engine/publish-guard.js +80 -0
- package/dist/engine/publish-guard.js.map +1 -0
- package/dist/engine/publisher.d.ts +171 -0
- package/dist/engine/publisher.d.ts.map +1 -0
- package/dist/engine/publisher.js +597 -0
- package/dist/engine/publisher.js.map +1 -0
- package/dist/engine/revisions.d.ts +39 -0
- package/dist/engine/revisions.d.ts.map +1 -0
- package/dist/engine/revisions.js +203 -0
- package/dist/engine/revisions.js.map +1 -0
- package/dist/engine/sanitize.d.ts +52 -0
- package/dist/engine/sanitize.d.ts.map +1 -0
- package/dist/engine/sanitize.js +155 -0
- package/dist/engine/sanitize.js.map +1 -0
- package/dist/engine/seed-membership.d.ts +29 -0
- package/dist/engine/seed-membership.d.ts.map +1 -0
- package/dist/engine/seed-membership.js +65 -0
- package/dist/engine/seed-membership.js.map +1 -0
- package/dist/engine/seo.d.ts +20 -0
- package/dist/engine/seo.d.ts.map +1 -0
- package/dist/engine/seo.js +50 -0
- package/dist/engine/seo.js.map +1 -0
- package/dist/engine/slug-redirects.d.ts +8 -0
- package/dist/engine/slug-redirects.d.ts.map +1 -0
- package/dist/engine/slug-redirects.js +26 -0
- package/dist/engine/slug-redirects.js.map +1 -0
- package/dist/engine/slug.d.ts +6 -0
- package/dist/engine/slug.d.ts.map +1 -0
- package/dist/engine/slug.js +28 -0
- package/dist/engine/slug.js.map +1 -0
- package/dist/engine/soft-delete.d.ts +8 -0
- package/dist/engine/soft-delete.d.ts.map +1 -0
- package/dist/engine/soft-delete.js +28 -0
- package/dist/engine/soft-delete.js.map +1 -0
- package/dist/engine/tags.d.ts +14 -0
- package/dist/engine/tags.d.ts.map +1 -0
- package/dist/engine/tags.js +79 -0
- package/dist/engine/tags.js.map +1 -0
- package/dist/engine/topics.d.ts +10 -0
- package/dist/engine/topics.d.ts.map +1 -0
- package/dist/engine/topics.js +140 -0
- package/dist/engine/topics.js.map +1 -0
- package/dist/engine/url-guard.d.ts +12 -0
- package/dist/engine/url-guard.d.ts.map +1 -0
- package/dist/engine/url-guard.js +129 -0
- package/dist/engine/url-guard.js.map +1 -0
- package/dist/engine/validator/checks/bare-url-not-autolinked.d.ts +20 -0
- package/dist/engine/validator/checks/bare-url-not-autolinked.d.ts.map +1 -0
- package/dist/engine/validator/checks/bare-url-not-autolinked.js +54 -0
- package/dist/engine/validator/checks/bare-url-not-autolinked.js.map +1 -0
- package/dist/engine/validator/checks/broken-footnote-label.d.ts +16 -0
- package/dist/engine/validator/checks/broken-footnote-label.d.ts.map +1 -0
- package/dist/engine/validator/checks/broken-footnote-label.js +17 -0
- package/dist/engine/validator/checks/broken-footnote-label.js.map +1 -0
- package/dist/engine/validator/checks/double-encoded-entities.d.ts +18 -0
- package/dist/engine/validator/checks/double-encoded-entities.d.ts.map +1 -0
- package/dist/engine/validator/checks/double-encoded-entities.js +23 -0
- package/dist/engine/validator/checks/double-encoded-entities.js.map +1 -0
- package/dist/engine/validator/checks/empty-alt-text.d.ts +14 -0
- package/dist/engine/validator/checks/empty-alt-text.d.ts.map +1 -0
- package/dist/engine/validator/checks/empty-alt-text.js +23 -0
- package/dist/engine/validator/checks/empty-alt-text.js.map +1 -0
- package/dist/engine/validator/checks/heading-hierarchy-skip.d.ts +11 -0
- package/dist/engine/validator/checks/heading-hierarchy-skip.d.ts.map +1 -0
- package/dist/engine/validator/checks/heading-hierarchy-skip.js +20 -0
- package/dist/engine/validator/checks/heading-hierarchy-skip.js.map +1 -0
- package/dist/engine/validator/checks/html-comment-leak.d.ts +20 -0
- package/dist/engine/validator/checks/html-comment-leak.d.ts.map +1 -0
- package/dist/engine/validator/checks/html-comment-leak.js +30 -0
- package/dist/engine/validator/checks/html-comment-leak.js.map +1 -0
- package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.d.ts +12 -0
- package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.d.ts.map +1 -0
- package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.js +17 -0
- package/dist/engine/validator/checks/iframe-missing-dims-and-wrapper.js.map +1 -0
- package/dist/engine/validator/checks/invisible-control-chars.d.ts +24 -0
- package/dist/engine/validator/checks/invisible-control-chars.d.ts.map +1 -0
- package/dist/engine/validator/checks/invisible-control-chars.js +30 -0
- package/dist/engine/validator/checks/invisible-control-chars.js.map +1 -0
- package/dist/engine/validator/checks/paywall-marker-leak.d.ts +17 -0
- package/dist/engine/validator/checks/paywall-marker-leak.d.ts.map +1 -0
- package/dist/engine/validator/checks/paywall-marker-leak.js +22 -0
- package/dist/engine/validator/checks/paywall-marker-leak.js.map +1 -0
- package/dist/engine/validator/checks/raw-block-html.d.ts +28 -0
- package/dist/engine/validator/checks/raw-block-html.d.ts.map +1 -0
- package/dist/engine/validator/checks/raw-block-html.js +38 -0
- package/dist/engine/validator/checks/raw-block-html.js.map +1 -0
- package/dist/engine/validator/checks/stale-body-html.d.ts +28 -0
- package/dist/engine/validator/checks/stale-body-html.d.ts.map +1 -0
- package/dist/engine/validator/checks/stale-body-html.js +15 -0
- package/dist/engine/validator/checks/stale-body-html.js.map +1 -0
- package/dist/engine/validator/checks/unresolved-footnote-anchor.d.ts +11 -0
- package/dist/engine/validator/checks/unresolved-footnote-anchor.d.ts.map +1 -0
- package/dist/engine/validator/checks/unresolved-footnote-anchor.js +48 -0
- package/dist/engine/validator/checks/unresolved-footnote-anchor.js.map +1 -0
- package/dist/engine/validator/checks/word-gdocs-paste-artifacts.d.ts +23 -0
- package/dist/engine/validator/checks/word-gdocs-paste-artifacts.d.ts.map +1 -0
- package/dist/engine/validator/checks/word-gdocs-paste-artifacts.js +47 -0
- package/dist/engine/validator/checks/word-gdocs-paste-artifacts.js.map +1 -0
- package/dist/engine/validator/index.d.ts +75 -0
- package/dist/engine/validator/index.d.ts.map +1 -0
- package/dist/engine/validator/index.js +313 -0
- package/dist/engine/validator/index.js.map +1 -0
- package/dist/engine/validator/scan.d.ts +28 -0
- package/dist/engine/validator/scan.d.ts.map +1 -0
- package/dist/engine/validator/scan.js +97 -0
- package/dist/engine/validator/scan.js.map +1 -0
- package/dist/engine/validator/types.d.ts +50 -0
- package/dist/engine/validator/types.d.ts.map +1 -0
- package/dist/engine/validator/types.js +51 -0
- package/dist/engine/validator/types.js.map +1 -0
- package/dist/engine/webhook-signer.d.ts +39 -0
- package/dist/engine/webhook-signer.d.ts.map +1 -0
- package/dist/engine/webhook-signer.js +117 -0
- package/dist/engine/webhook-signer.js.map +1 -0
- package/dist/engine/webhooks.d.ts +75 -0
- package/dist/engine/webhooks.d.ts.map +1 -0
- package/dist/engine/webhooks.js +139 -0
- package/dist/engine/webhooks.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/integration/index.d.ts +6 -0
- package/dist/integration/index.d.ts.map +1 -0
- package/dist/integration/index.js +294 -0
- package/dist/integration/index.js.map +1 -0
- package/dist/integration/options.d.ts +105 -0
- package/dist/integration/options.d.ts.map +1 -0
- package/dist/integration/options.js +25 -0
- package/dist/integration/options.js.map +1 -0
- package/dist/integration/vite-plugin.d.ts +4 -0
- package/dist/integration/vite-plugin.d.ts.map +1 -0
- package/dist/integration/vite-plugin.js +37 -0
- package/dist/integration/vite-plugin.js.map +1 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/null.d.ts +9 -0
- package/dist/providers/null.d.ts.map +1 -0
- package/dist/providers/null.js +144 -0
- package/dist/providers/null.js.map +1 -0
- package/dist/providers/types.d.ts +277 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/routes/ai.d.ts +25 -0
- package/dist/routes/ai.d.ts.map +1 -0
- package/dist/routes/ai.js +381 -0
- package/dist/routes/ai.js.map +1 -0
- package/dist/routes/analytics.d.ts +15 -0
- package/dist/routes/analytics.d.ts.map +1 -0
- package/dist/routes/analytics.js +61 -0
- package/dist/routes/analytics.js.map +1 -0
- package/dist/routes/api-keys.d.ts +13 -0
- package/dist/routes/api-keys.d.ts.map +1 -0
- package/dist/routes/api-keys.js +109 -0
- package/dist/routes/api-keys.js.map +1 -0
- package/dist/routes/authors.d.ts +19 -0
- package/dist/routes/authors.d.ts.map +1 -0
- package/dist/routes/authors.js +202 -0
- package/dist/routes/authors.js.map +1 -0
- package/dist/routes/authz-matrix.d.ts +78 -0
- package/dist/routes/authz-matrix.d.ts.map +1 -0
- package/dist/routes/authz-matrix.js +170 -0
- package/dist/routes/authz-matrix.js.map +1 -0
- package/dist/routes/calendar.d.ts +19 -0
- package/dist/routes/calendar.d.ts.map +1 -0
- package/dist/routes/calendar.js +89 -0
- package/dist/routes/calendar.js.map +1 -0
- package/dist/routes/config.d.ts +70 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +23 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/content-insights.d.ts +18 -0
- package/dist/routes/content-insights.d.ts.map +1 -0
- package/dist/routes/content-insights.js +137 -0
- package/dist/routes/content-insights.js.map +1 -0
- package/dist/routes/content.d.ts +145 -0
- package/dist/routes/content.d.ts.map +1 -0
- package/dist/routes/content.js +1374 -0
- package/dist/routes/content.js.map +1 -0
- package/dist/routes/context.d.ts +104 -0
- package/dist/routes/context.d.ts.map +1 -0
- package/dist/routes/context.js +26 -0
- package/dist/routes/context.js.map +1 -0
- package/dist/routes/cron.d.ts +8 -0
- package/dist/routes/cron.d.ts.map +1 -0
- package/dist/routes/cron.js +20 -0
- package/dist/routes/cron.js.map +1 -0
- package/dist/routes/dashboard.d.ts +12 -0
- package/dist/routes/dashboard.d.ts.map +1 -0
- package/dist/routes/dashboard.js +113 -0
- package/dist/routes/dashboard.js.map +1 -0
- package/dist/routes/imports.d.ts +10 -0
- package/dist/routes/imports.d.ts.map +1 -0
- package/dist/routes/imports.js +149 -0
- package/dist/routes/imports.js.map +1 -0
- package/dist/routes/index.d.ts +75 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +141 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/media-lib.d.ts +75 -0
- package/dist/routes/media-lib.d.ts.map +1 -0
- package/dist/routes/media-lib.js +305 -0
- package/dist/routes/media-lib.js.map +1 -0
- package/dist/routes/media.d.ts +32 -0
- package/dist/routes/media.d.ts.map +1 -0
- package/dist/routes/media.js +756 -0
- package/dist/routes/media.js.map +1 -0
- package/dist/routes/preview.d.ts +19 -0
- package/dist/routes/preview.d.ts.map +1 -0
- package/dist/routes/preview.js +150 -0
- package/dist/routes/preview.js.map +1 -0
- package/dist/routes/rbac-invites.d.ts +31 -0
- package/dist/routes/rbac-invites.d.ts.map +1 -0
- package/dist/routes/rbac-invites.js +174 -0
- package/dist/routes/rbac-invites.js.map +1 -0
- package/dist/routes/rbac.d.ts +12 -0
- package/dist/routes/rbac.d.ts.map +1 -0
- package/dist/routes/rbac.js +126 -0
- package/dist/routes/rbac.js.map +1 -0
- package/dist/routes/shell.d.ts +22 -0
- package/dist/routes/shell.d.ts.map +1 -0
- package/dist/routes/shell.js +123 -0
- package/dist/routes/shell.js.map +1 -0
- package/dist/routes/subscriptions.d.ts +21 -0
- package/dist/routes/subscriptions.d.ts.map +1 -0
- package/dist/routes/subscriptions.js +127 -0
- package/dist/routes/subscriptions.js.map +1 -0
- package/dist/routes/tags.d.ts +23 -0
- package/dist/routes/tags.d.ts.map +1 -0
- package/dist/routes/tags.js +68 -0
- package/dist/routes/tags.js.map +1 -0
- package/dist/routes/topics.d.ts +12 -0
- package/dist/routes/topics.d.ts.map +1 -0
- package/dist/routes/topics.js +49 -0
- package/dist/routes/topics.js.map +1 -0
- package/dist/routes/webhooks.d.ts +31 -0
- package/dist/routes/webhooks.d.ts.map +1 -0
- package/dist/routes/webhooks.js +173 -0
- package/dist/routes/webhooks.js.map +1 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +6 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/insights-ingest.d.ts +959 -0
- package/dist/schema/insights-ingest.d.ts.map +1 -0
- package/dist/schema/insights-ingest.js +112 -0
- package/dist/schema/insights-ingest.js.map +1 -0
- package/dist/schema/migrations.d.ts +63 -0
- package/dist/schema/migrations.d.ts.map +1 -0
- package/dist/schema/migrations.js +589 -0
- package/dist/schema/migrations.js.map +1 -0
- package/dist/schema/tables.d.ts +11 -0
- package/dist/schema/tables.d.ts.map +1 -0
- package/dist/schema/tables.js +56 -0
- package/dist/schema/tables.js.map +1 -0
- package/dist/schema/types.d.ts +476 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +37 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/ui/api/_authz.d.ts +6 -0
- package/dist/ui/api/_authz.d.ts.map +1 -0
- package/dist/ui/api/_authz.js +74 -0
- package/dist/ui/api/_authz.js.map +1 -0
- package/dist/ui/api/_content-config.d.ts +22 -0
- package/dist/ui/api/_content-config.d.ts.map +1 -0
- package/dist/ui/api/_content-config.js +50 -0
- package/dist/ui/api/_content-config.js.map +1 -0
- package/dist/ui/api/activity.d.ts +3 -0
- package/dist/ui/api/activity.d.ts.map +1 -0
- package/dist/ui/api/activity.js +28 -0
- package/dist/ui/api/activity.js.map +1 -0
- package/dist/ui/api/analytics.d.ts +3 -0
- package/dist/ui/api/analytics.d.ts.map +1 -0
- package/dist/ui/api/analytics.js +36 -0
- package/dist/ui/api/analytics.js.map +1 -0
- package/dist/ui/api/authors/[id].d.ts +4 -0
- package/dist/ui/api/authors/[id].d.ts.map +1 -0
- package/dist/ui/api/authors/[id].js +17 -0
- package/dist/ui/api/authors/[id].js.map +1 -0
- package/dist/ui/api/authors.d.ts +4 -0
- package/dist/ui/api/authors.d.ts.map +1 -0
- package/dist/ui/api/authors.js +12 -0
- package/dist/ui/api/authors.js.map +1 -0
- package/dist/ui/api/calendar.d.ts +3 -0
- package/dist/ui/api/calendar.d.ts.map +1 -0
- package/dist/ui/api/calendar.js +16 -0
- package/dist/ui/api/calendar.js.map +1 -0
- package/dist/ui/api/content/[id]/ai/headlines.d.ts +3 -0
- package/dist/ui/api/content/[id]/ai/headlines.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/ai/headlines.js +7 -0
- package/dist/ui/api/content/[id]/ai/headlines.js.map +1 -0
- package/dist/ui/api/content/[id]/ai/meta-description.d.ts +3 -0
- package/dist/ui/api/content/[id]/ai/meta-description.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/ai/meta-description.js +6 -0
- package/dist/ui/api/content/[id]/ai/meta-description.js.map +1 -0
- package/dist/ui/api/content/[id]/ai/og-image.d.ts +3 -0
- package/dist/ui/api/content/[id]/ai/og-image.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/ai/og-image.js +7 -0
- package/dist/ui/api/content/[id]/ai/og-image.js.map +1 -0
- package/dist/ui/api/content/[id]/ai/proofread.d.ts +3 -0
- package/dist/ui/api/content/[id]/ai/proofread.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/ai/proofread.js +7 -0
- package/dist/ui/api/content/[id]/ai/proofread.js.map +1 -0
- package/dist/ui/api/content/[id]/ai/takeaways.d.ts +3 -0
- package/dist/ui/api/content/[id]/ai/takeaways.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/ai/takeaways.js +6 -0
- package/dist/ui/api/content/[id]/ai/takeaways.js.map +1 -0
- package/dist/ui/api/content/[id]/contributors.d.ts +4 -0
- package/dist/ui/api/content/[id]/contributors.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/contributors.js +8 -0
- package/dist/ui/api/content/[id]/contributors.js.map +1 -0
- package/dist/ui/api/content/[id]/preview-token.d.ts +3 -0
- package/dist/ui/api/content/[id]/preview-token.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/preview-token.js +16 -0
- package/dist/ui/api/content/[id]/preview-token.js.map +1 -0
- package/dist/ui/api/content/[id]/revisions/[rev]/restore.d.ts +3 -0
- package/dist/ui/api/content/[id]/revisions/[rev]/restore.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/revisions/[rev]/restore.js +7 -0
- package/dist/ui/api/content/[id]/revisions/[rev]/restore.js.map +1 -0
- package/dist/ui/api/content/[id]/revisions/[rev].d.ts +3 -0
- package/dist/ui/api/content/[id]/revisions/[rev].d.ts.map +1 -0
- package/dist/ui/api/content/[id]/revisions/[rev].js +6 -0
- package/dist/ui/api/content/[id]/revisions/[rev].js.map +1 -0
- package/dist/ui/api/content/[id]/revisions.d.ts +3 -0
- package/dist/ui/api/content/[id]/revisions.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/revisions.js +6 -0
- package/dist/ui/api/content/[id]/revisions.js.map +1 -0
- package/dist/ui/api/content/[id]/seo-score.d.ts +3 -0
- package/dist/ui/api/content/[id]/seo-score.d.ts.map +1 -0
- package/dist/ui/api/content/[id]/seo-score.js +7 -0
- package/dist/ui/api/content/[id]/seo-score.js.map +1 -0
- package/dist/ui/api/content/[id].d.ts +5 -0
- package/dist/ui/api/content/[id].d.ts.map +1 -0
- package/dist/ui/api/content/[id].js +17 -0
- package/dist/ui/api/content/[id].js.map +1 -0
- package/dist/ui/api/content/bulk.d.ts +3 -0
- package/dist/ui/api/content/bulk.d.ts.map +1 -0
- package/dist/ui/api/content/bulk.js +7 -0
- package/dist/ui/api/content/bulk.js.map +1 -0
- package/dist/ui/api/content/counts.d.ts +3 -0
- package/dist/ui/api/content/counts.d.ts.map +1 -0
- package/dist/ui/api/content/counts.js +8 -0
- package/dist/ui/api/content/counts.js.map +1 -0
- package/dist/ui/api/content/foundry-callback.d.ts +3 -0
- package/dist/ui/api/content/foundry-callback.d.ts.map +1 -0
- package/dist/ui/api/content/foundry-callback.js +8 -0
- package/dist/ui/api/content/foundry-callback.js.map +1 -0
- package/dist/ui/api/content/import/confirm.d.ts +3 -0
- package/dist/ui/api/content/import/confirm.d.ts.map +1 -0
- package/dist/ui/api/content/import/confirm.js +11 -0
- package/dist/ui/api/content/import/confirm.js.map +1 -0
- package/dist/ui/api/content/import/parse.d.ts +3 -0
- package/dist/ui/api/content/import/parse.d.ts.map +1 -0
- package/dist/ui/api/content/import/parse.js +11 -0
- package/dist/ui/api/content/import/parse.js.map +1 -0
- package/dist/ui/api/content/insights-ingest.d.ts +3 -0
- package/dist/ui/api/content/insights-ingest.d.ts.map +1 -0
- package/dist/ui/api/content/insights-ingest.js +15 -0
- package/dist/ui/api/content/insights-ingest.js.map +1 -0
- package/dist/ui/api/content-insights/dismiss.d.ts +3 -0
- package/dist/ui/api/content-insights/dismiss.d.ts.map +1 -0
- package/dist/ui/api/content-insights/dismiss.js +16 -0
- package/dist/ui/api/content-insights/dismiss.js.map +1 -0
- package/dist/ui/api/content-insights/index.d.ts +3 -0
- package/dist/ui/api/content-insights/index.d.ts.map +1 -0
- package/dist/ui/api/content-insights/index.js +12 -0
- package/dist/ui/api/content-insights/index.js.map +1 -0
- package/dist/ui/api/content-insights/undismiss.d.ts +3 -0
- package/dist/ui/api/content-insights/undismiss.d.ts.map +1 -0
- package/dist/ui/api/content-insights/undismiss.js +16 -0
- package/dist/ui/api/content-insights/undismiss.js.map +1 -0
- package/dist/ui/api/content.d.ts +4 -0
- package/dist/ui/api/content.d.ts.map +1 -0
- package/dist/ui/api/content.js +16 -0
- package/dist/ui/api/content.js.map +1 -0
- package/dist/ui/api/dashboard.d.ts +3 -0
- package/dist/ui/api/dashboard.d.ts.map +1 -0
- package/dist/ui/api/dashboard.js +22 -0
- package/dist/ui/api/dashboard.js.map +1 -0
- package/dist/ui/api/me.d.ts +3 -0
- package/dist/ui/api/me.d.ts.map +1 -0
- package/dist/ui/api/me.js +18 -0
- package/dist/ui/api/me.js.map +1 -0
- package/dist/ui/api/media/[id].d.ts +3 -0
- package/dist/ui/api/media/[id].d.ts.map +1 -0
- package/dist/ui/api/media/[id].js +17 -0
- package/dist/ui/api/media/[id].js.map +1 -0
- package/dist/ui/api/media/images.d.ts +3 -0
- package/dist/ui/api/media/images.d.ts.map +1 -0
- package/dist/ui/api/media/images.js +6 -0
- package/dist/ui/api/media/images.js.map +1 -0
- package/dist/ui/api/media/library/[id].d.ts +3 -0
- package/dist/ui/api/media/library/[id].d.ts.map +1 -0
- package/dist/ui/api/media/library/[id].js +6 -0
- package/dist/ui/api/media/library/[id].js.map +1 -0
- package/dist/ui/api/media/library.d.ts +3 -0
- package/dist/ui/api/media/library.d.ts.map +1 -0
- package/dist/ui/api/media/library.js +17 -0
- package/dist/ui/api/media/library.js.map +1 -0
- package/dist/ui/api/media/podcast/abort.d.ts +3 -0
- package/dist/ui/api/media/podcast/abort.d.ts.map +1 -0
- package/dist/ui/api/media/podcast/abort.js +4 -0
- package/dist/ui/api/media/podcast/abort.js.map +1 -0
- package/dist/ui/api/media/podcast/complete.d.ts +3 -0
- package/dist/ui/api/media/podcast/complete.d.ts.map +1 -0
- package/dist/ui/api/media/podcast/complete.js +4 -0
- package/dist/ui/api/media/podcast/complete.js.map +1 -0
- package/dist/ui/api/media/podcast/init.d.ts +3 -0
- package/dist/ui/api/media/podcast/init.d.ts.map +1 -0
- package/dist/ui/api/media/podcast/init.js +4 -0
- package/dist/ui/api/media/podcast/init.js.map +1 -0
- package/dist/ui/api/media/podcast/part.d.ts +3 -0
- package/dist/ui/api/media/podcast/part.d.ts.map +1 -0
- package/dist/ui/api/media/podcast/part.js +4 -0
- package/dist/ui/api/media/podcast/part.js.map +1 -0
- package/dist/ui/api/media/podcast.d.ts +3 -0
- package/dist/ui/api/media/podcast.d.ts.map +1 -0
- package/dist/ui/api/media/podcast.js +6 -0
- package/dist/ui/api/media/podcast.js.map +1 -0
- package/dist/ui/api/media/videos/abort.d.ts +3 -0
- package/dist/ui/api/media/videos/abort.d.ts.map +1 -0
- package/dist/ui/api/media/videos/abort.js +6 -0
- package/dist/ui/api/media/videos/abort.js.map +1 -0
- package/dist/ui/api/media/videos/complete.d.ts +3 -0
- package/dist/ui/api/media/videos/complete.d.ts.map +1 -0
- package/dist/ui/api/media/videos/complete.js +6 -0
- package/dist/ui/api/media/videos/complete.js.map +1 -0
- package/dist/ui/api/media/videos/init.d.ts +3 -0
- package/dist/ui/api/media/videos/init.d.ts.map +1 -0
- package/dist/ui/api/media/videos/init.js +6 -0
- package/dist/ui/api/media/videos/init.js.map +1 -0
- package/dist/ui/api/media/videos/part.d.ts +3 -0
- package/dist/ui/api/media/videos/part.d.ts.map +1 -0
- package/dist/ui/api/media/videos/part.js +6 -0
- package/dist/ui/api/media/videos/part.js.map +1 -0
- package/dist/ui/api/notifications.d.ts +4 -0
- package/dist/ui/api/notifications.d.ts.map +1 -0
- package/dist/ui/api/notifications.js +20 -0
- package/dist/ui/api/notifications.js.map +1 -0
- package/dist/ui/api/search.d.ts +3 -0
- package/dist/ui/api/search.d.ts.map +1 -0
- package/dist/ui/api/search.js +18 -0
- package/dist/ui/api/search.js.map +1 -0
- package/dist/ui/api/settings/api-keys/[id].d.ts +3 -0
- package/dist/ui/api/settings/api-keys/[id].d.ts.map +1 -0
- package/dist/ui/api/settings/api-keys/[id].js +22 -0
- package/dist/ui/api/settings/api-keys/[id].js.map +1 -0
- package/dist/ui/api/settings/api-keys.d.ts +4 -0
- package/dist/ui/api/settings/api-keys.d.ts.map +1 -0
- package/dist/ui/api/settings/api-keys.js +19 -0
- package/dist/ui/api/settings/api-keys.js.map +1 -0
- package/dist/ui/api/settings/domains.d.ts +3 -0
- package/dist/ui/api/settings/domains.d.ts.map +1 -0
- package/dist/ui/api/settings/domains.js +32 -0
- package/dist/ui/api/settings/domains.js.map +1 -0
- package/dist/ui/api/settings/integrations.d.ts +3 -0
- package/dist/ui/api/settings/integrations.d.ts.map +1 -0
- package/dist/ui/api/settings/integrations.js +32 -0
- package/dist/ui/api/settings/integrations.js.map +1 -0
- package/dist/ui/api/settings/members/[userId].d.ts +4 -0
- package/dist/ui/api/settings/members/[userId].d.ts.map +1 -0
- package/dist/ui/api/settings/members/[userId].js +26 -0
- package/dist/ui/api/settings/members/[userId].js.map +1 -0
- package/dist/ui/api/settings/members/invite.d.ts +3 -0
- package/dist/ui/api/settings/members/invite.d.ts.map +1 -0
- package/dist/ui/api/settings/members/invite.js +21 -0
- package/dist/ui/api/settings/members/invite.js.map +1 -0
- package/dist/ui/api/settings/members.d.ts +3 -0
- package/dist/ui/api/settings/members.d.ts.map +1 -0
- package/dist/ui/api/settings/members.js +17 -0
- package/dist/ui/api/settings/members.js.map +1 -0
- package/dist/ui/api/settings/webhooks/[id]/test.d.ts +3 -0
- package/dist/ui/api/settings/webhooks/[id]/test.d.ts.map +1 -0
- package/dist/ui/api/settings/webhooks/[id]/test.js +24 -0
- package/dist/ui/api/settings/webhooks/[id]/test.js.map +1 -0
- package/dist/ui/api/settings/webhooks/[id].d.ts +3 -0
- package/dist/ui/api/settings/webhooks/[id].d.ts.map +1 -0
- package/dist/ui/api/settings/webhooks/[id].js +23 -0
- package/dist/ui/api/settings/webhooks/[id].js.map +1 -0
- package/dist/ui/api/settings/webhooks.d.ts +4 -0
- package/dist/ui/api/settings/webhooks.d.ts.map +1 -0
- package/dist/ui/api/settings/webhooks.js +21 -0
- package/dist/ui/api/settings/webhooks.js.map +1 -0
- package/dist/ui/api/subscriptions.d.ts +4 -0
- package/dist/ui/api/subscriptions.d.ts.map +1 -0
- package/dist/ui/api/subscriptions.js +47 -0
- package/dist/ui/api/subscriptions.js.map +1 -0
- package/dist/ui/api/tags/[id].d.ts +4 -0
- package/dist/ui/api/tags/[id].d.ts.map +1 -0
- package/dist/ui/api/tags/[id].js +18 -0
- package/dist/ui/api/tags/[id].js.map +1 -0
- package/dist/ui/api/tags/index.d.ts +4 -0
- package/dist/ui/api/tags/index.d.ts.map +1 -0
- package/dist/ui/api/tags/index.js +13 -0
- package/dist/ui/api/tags/index.js.map +1 -0
- package/dist/ui/api/topics/[id].d.ts +3 -0
- package/dist/ui/api/topics/[id].d.ts.map +1 -0
- package/dist/ui/api/topics/[id].js +15 -0
- package/dist/ui/api/topics/[id].js.map +1 -0
- package/dist/ui/api/topics/index.d.ts +4 -0
- package/dist/ui/api/topics/index.d.ts.map +1 -0
- package/dist/ui/api/topics/index.js +12 -0
- package/dist/ui/api/topics/index.js.map +1 -0
- package/dist/ui/api/workspace-settings.d.ts +4 -0
- package/dist/ui/api/workspace-settings.d.ts.map +1 -0
- package/dist/ui/api/workspace-settings.js +20 -0
- package/dist/ui/api/workspace-settings.js.map +1 -0
- package/dist/ui/client/boot-state.d.ts +15 -0
- package/dist/ui/client/boot-state.d.ts.map +1 -0
- package/dist/ui/client/boot-state.js +36 -0
- package/dist/ui/client/boot-state.js.map +1 -0
- package/dist/ui/client/mount.d.ts +3 -0
- package/dist/ui/client/mount.d.ts.map +1 -0
- package/dist/ui/client/mount.js +37 -0
- package/dist/ui/client/mount.js.map +1 -0
- package/dist/ui/commands.d.ts +23 -0
- package/dist/ui/commands.d.ts.map +1 -0
- package/dist/ui/commands.js +48 -0
- package/dist/ui/commands.js.map +1 -0
- package/dist/ui/components/CmsApp.d.ts +16 -0
- package/dist/ui/components/CmsApp.d.ts.map +1 -0
- package/dist/ui/components/CmsApp.js +74 -0
- package/dist/ui/components/CmsApp.js.map +1 -0
- package/dist/ui/components/CommandPalette.d.ts +7 -0
- package/dist/ui/components/CommandPalette.d.ts.map +1 -0
- package/dist/ui/components/CommandPalette.js +61 -0
- package/dist/ui/components/CommandPalette.js.map +1 -0
- package/dist/ui/components/NoAccessScreen.d.ts +2 -0
- package/dist/ui/components/NoAccessScreen.d.ts.map +1 -0
- package/dist/ui/components/NoAccessScreen.js +42 -0
- package/dist/ui/components/NoAccessScreen.js.map +1 -0
- package/dist/ui/components/ShareModal.d.ts +27 -0
- package/dist/ui/components/ShareModal.d.ts.map +1 -0
- package/dist/ui/components/ShareModal.js +208 -0
- package/dist/ui/components/ShareModal.js.map +1 -0
- package/dist/ui/components/SharePickers.d.ts +39 -0
- package/dist/ui/components/SharePickers.d.ts.map +1 -0
- package/dist/ui/components/SharePickers.js +352 -0
- package/dist/ui/components/SharePickers.js.map +1 -0
- package/dist/ui/components/ShareStatsPanel.d.ts +22 -0
- package/dist/ui/components/ShareStatsPanel.d.ts.map +1 -0
- package/dist/ui/components/ShareStatsPanel.js +317 -0
- package/dist/ui/components/ShareStatsPanel.js.map +1 -0
- package/dist/ui/components/Sidebar.d.ts +7 -0
- package/dist/ui/components/Sidebar.d.ts.map +1 -0
- package/dist/ui/components/Sidebar.js +20 -0
- package/dist/ui/components/Sidebar.js.map +1 -0
- package/dist/ui/components/SiteSwitcher.d.ts +4 -0
- package/dist/ui/components/SiteSwitcher.d.ts.map +1 -0
- package/dist/ui/components/SiteSwitcher.js +35 -0
- package/dist/ui/components/SiteSwitcher.js.map +1 -0
- package/dist/ui/components/Topbar.d.ts +9 -0
- package/dist/ui/components/Topbar.d.ts.map +1 -0
- package/dist/ui/components/Topbar.js +20 -0
- package/dist/ui/components/Topbar.js.map +1 -0
- package/dist/ui/editor/AiAssistPanel.d.ts +8 -0
- package/dist/ui/editor/AiAssistPanel.d.ts.map +1 -0
- package/dist/ui/editor/AiAssistPanel.js +221 -0
- package/dist/ui/editor/AiAssistPanel.js.map +1 -0
- package/dist/ui/editor/ContentForm.d.ts +46 -0
- package/dist/ui/editor/ContentForm.d.ts.map +1 -0
- package/dist/ui/editor/ContentForm.js +821 -0
- package/dist/ui/editor/ContentForm.js.map +1 -0
- package/dist/ui/editor/Rte.d.ts +16 -0
- package/dist/ui/editor/Rte.d.ts.map +1 -0
- package/dist/ui/editor/Rte.js +272 -0
- package/dist/ui/editor/Rte.js.map +1 -0
- package/dist/ui/editor/ai-assist.d.ts +43 -0
- package/dist/ui/editor/ai-assist.d.ts.map +1 -0
- package/dist/ui/editor/ai-assist.js +114 -0
- package/dist/ui/editor/ai-assist.js.map +1 -0
- package/dist/ui/editor/autosave.d.ts +18 -0
- package/dist/ui/editor/autosave.d.ts.map +1 -0
- package/dist/ui/editor/autosave.js +23 -0
- package/dist/ui/editor/autosave.js.map +1 -0
- package/dist/ui/editor/content-payload.d.ts +19 -0
- package/dist/ui/editor/content-payload.d.ts.map +1 -0
- package/dist/ui/editor/content-payload.js +97 -0
- package/dist/ui/editor/content-payload.js.map +1 -0
- package/dist/ui/editor/editor-media-upload.d.ts +6 -0
- package/dist/ui/editor/editor-media-upload.d.ts.map +1 -0
- package/dist/ui/editor/editor-media-upload.js +20 -0
- package/dist/ui/editor/editor-media-upload.js.map +1 -0
- package/dist/ui/editor/serialize.d.ts +6 -0
- package/dist/ui/editor/serialize.d.ts.map +1 -0
- package/dist/ui/editor/serialize.js +479 -0
- package/dist/ui/editor/serialize.js.map +1 -0
- package/dist/ui/editor/tweet-embed.d.ts +4 -0
- package/dist/ui/editor/tweet-embed.d.ts.map +1 -0
- package/dist/ui/editor/tweet-embed.js +49 -0
- package/dist/ui/editor/tweet-embed.js.map +1 -0
- package/dist/ui/hash-router.d.ts +5 -0
- package/dist/ui/hash-router.d.ts.map +1 -0
- package/dist/ui/hash-router.js +25 -0
- package/dist/ui/hash-router.js.map +1 -0
- package/dist/ui/icons.d.ts +32 -0
- package/dist/ui/icons.d.ts.map +1 -0
- package/dist/ui/icons.js +86 -0
- package/dist/ui/icons.js.map +1 -0
- package/dist/ui/inspector/Field.d.ts +12 -0
- package/dist/ui/inspector/Field.d.ts.map +1 -0
- package/dist/ui/inspector/Field.js +8 -0
- package/dist/ui/inspector/Field.js.map +1 -0
- package/dist/ui/inspector/FoundryTab.d.ts +9 -0
- package/dist/ui/inspector/FoundryTab.d.ts.map +1 -0
- package/dist/ui/inspector/FoundryTab.js +362 -0
- package/dist/ui/inspector/FoundryTab.js.map +1 -0
- package/dist/ui/inspector/HistoryTab.d.ts +7 -0
- package/dist/ui/inspector/HistoryTab.d.ts.map +1 -0
- package/dist/ui/inspector/HistoryTab.js +289 -0
- package/dist/ui/inspector/HistoryTab.js.map +1 -0
- package/dist/ui/inspector/Inspector.d.ts +13 -0
- package/dist/ui/inspector/Inspector.d.ts.map +1 -0
- package/dist/ui/inspector/Inspector.js +163 -0
- package/dist/ui/inspector/Inspector.js.map +1 -0
- package/dist/ui/inspector/OrganizeTab.d.ts +15 -0
- package/dist/ui/inspector/OrganizeTab.d.ts.map +1 -0
- package/dist/ui/inspector/OrganizeTab.js +319 -0
- package/dist/ui/inspector/OrganizeTab.js.map +1 -0
- package/dist/ui/inspector/PublishTab.d.ts +18 -0
- package/dist/ui/inspector/PublishTab.d.ts.map +1 -0
- package/dist/ui/inspector/PublishTab.js +339 -0
- package/dist/ui/inspector/PublishTab.js.map +1 -0
- package/dist/ui/inspector/Section.d.ts +10 -0
- package/dist/ui/inspector/Section.d.ts.map +1 -0
- package/dist/ui/inspector/Section.js +40 -0
- package/dist/ui/inspector/Section.js.map +1 -0
- package/dist/ui/inspector/SeoTab.d.ts +19 -0
- package/dist/ui/inspector/SeoTab.d.ts.map +1 -0
- package/dist/ui/inspector/SeoTab.js +328 -0
- package/dist/ui/inspector/SeoTab.js.map +1 -0
- package/dist/ui/inspector/foundry-stages.d.ts +36 -0
- package/dist/ui/inspector/foundry-stages.d.ts.map +1 -0
- package/dist/ui/inspector/foundry-stages.js +101 -0
- package/dist/ui/inspector/foundry-stages.js.map +1 -0
- package/dist/ui/inspector/inspector-data.d.ts +80 -0
- package/dist/ui/inspector/inspector-data.d.ts.map +1 -0
- package/dist/ui/inspector/inspector-data.js +172 -0
- package/dist/ui/inspector/inspector-data.js.map +1 -0
- package/dist/ui/inspector/organize-data.d.ts +23 -0
- package/dist/ui/inspector/organize-data.d.ts.map +1 -0
- package/dist/ui/inspector/organize-data.js +28 -0
- package/dist/ui/inspector/organize-data.js.map +1 -0
- package/dist/ui/inspector/revision-diff.d.ts +49 -0
- package/dist/ui/inspector/revision-diff.d.ts.map +1 -0
- package/dist/ui/inspector/revision-diff.js +166 -0
- package/dist/ui/inspector/revision-diff.js.map +1 -0
- package/dist/ui/inspector/seo-helpers.d.ts +37 -0
- package/dist/ui/inspector/seo-helpers.d.ts.map +1 -0
- package/dist/ui/inspector/seo-helpers.js +37 -0
- package/dist/ui/inspector/seo-helpers.js.map +1 -0
- package/dist/ui/inspector/tab-visibility.d.ts +14 -0
- package/dist/ui/inspector/tab-visibility.d.ts.map +1 -0
- package/dist/ui/inspector/tab-visibility.js +28 -0
- package/dist/ui/inspector/tab-visibility.js.map +1 -0
- package/dist/ui/nav.d.ts +16 -0
- package/dist/ui/nav.d.ts.map +1 -0
- package/dist/ui/nav.js +33 -0
- package/dist/ui/nav.js.map +1 -0
- package/dist/ui/pages/admin.astro +32 -0
- package/dist/ui/preview/draft-page.d.ts +37 -0
- package/dist/ui/preview/draft-page.d.ts.map +1 -0
- package/dist/ui/preview/draft-page.js +212 -0
- package/dist/ui/preview/draft-page.js.map +1 -0
- package/dist/ui/preview/preview-layout.d.ts +23 -0
- package/dist/ui/preview/preview-layout.d.ts.map +1 -0
- package/dist/ui/preview/preview-layout.js +30 -0
- package/dist/ui/preview/preview-layout.js.map +1 -0
- package/dist/ui/screens/AnalyticsScreen.d.ts +2 -0
- package/dist/ui/screens/AnalyticsScreen.d.ts.map +1 -0
- package/dist/ui/screens/AnalyticsScreen.js +408 -0
- package/dist/ui/screens/AnalyticsScreen.js.map +1 -0
- package/dist/ui/screens/AuthorsScreen.d.ts +2 -0
- package/dist/ui/screens/AuthorsScreen.d.ts.map +1 -0
- package/dist/ui/screens/AuthorsScreen.js +225 -0
- package/dist/ui/screens/AuthorsScreen.js.map +1 -0
- package/dist/ui/screens/CalendarScreen.d.ts +6 -0
- package/dist/ui/screens/CalendarScreen.d.ts.map +1 -0
- package/dist/ui/screens/CalendarScreen.js +327 -0
- package/dist/ui/screens/CalendarScreen.js.map +1 -0
- package/dist/ui/screens/ContentInsightsScreen.d.ts +2 -0
- package/dist/ui/screens/ContentInsightsScreen.d.ts.map +1 -0
- package/dist/ui/screens/ContentInsightsScreen.js +129 -0
- package/dist/ui/screens/ContentInsightsScreen.js.map +1 -0
- package/dist/ui/screens/ContentRoute.d.ts +2 -0
- package/dist/ui/screens/ContentRoute.d.ts.map +1 -0
- package/dist/ui/screens/ContentRoute.js +32 -0
- package/dist/ui/screens/ContentRoute.js.map +1 -0
- package/dist/ui/screens/DashboardScreen.d.ts +6 -0
- package/dist/ui/screens/DashboardScreen.d.ts.map +1 -0
- package/dist/ui/screens/DashboardScreen.js +273 -0
- package/dist/ui/screens/DashboardScreen.js.map +1 -0
- package/dist/ui/screens/EditorScreen.d.ts +7 -0
- package/dist/ui/screens/EditorScreen.d.ts.map +1 -0
- package/dist/ui/screens/EditorScreen.js +426 -0
- package/dist/ui/screens/EditorScreen.js.map +1 -0
- package/dist/ui/screens/LibraryScreen.d.ts +6 -0
- package/dist/ui/screens/LibraryScreen.d.ts.map +1 -0
- package/dist/ui/screens/LibraryScreen.js +580 -0
- package/dist/ui/screens/LibraryScreen.js.map +1 -0
- package/dist/ui/screens/MediaScreen.d.ts +2 -0
- package/dist/ui/screens/MediaScreen.d.ts.map +1 -0
- package/dist/ui/screens/MediaScreen.js +173 -0
- package/dist/ui/screens/MediaScreen.js.map +1 -0
- package/dist/ui/screens/SettingsScreen.d.ts +4 -0
- package/dist/ui/screens/SettingsScreen.d.ts.map +1 -0
- package/dist/ui/screens/SettingsScreen.js +751 -0
- package/dist/ui/screens/SettingsScreen.js.map +1 -0
- package/dist/ui/screens/SocialShareScreen.d.ts +2 -0
- package/dist/ui/screens/SocialShareScreen.d.ts.map +1 -0
- package/dist/ui/screens/SocialShareScreen.js +224 -0
- package/dist/ui/screens/SocialShareScreen.js.map +1 -0
- package/dist/ui/screens/SubscriptionsScreen.d.ts +2 -0
- package/dist/ui/screens/SubscriptionsScreen.d.ts.map +1 -0
- package/dist/ui/screens/SubscriptionsScreen.js +441 -0
- package/dist/ui/screens/SubscriptionsScreen.js.map +1 -0
- package/dist/ui/screens/TopicsScreen.d.ts +2 -0
- package/dist/ui/screens/TopicsScreen.d.ts.map +1 -0
- package/dist/ui/screens/TopicsScreen.js +360 -0
- package/dist/ui/screens/TopicsScreen.js.map +1 -0
- package/dist/ui/screens/analytics-data.d.ts +19 -0
- package/dist/ui/screens/analytics-data.d.ts.map +1 -0
- package/dist/ui/screens/analytics-data.js +42 -0
- package/dist/ui/screens/analytics-data.js.map +1 -0
- package/dist/ui/screens/calendar-data.d.ts +45 -0
- package/dist/ui/screens/calendar-data.d.ts.map +1 -0
- package/dist/ui/screens/calendar-data.js +70 -0
- package/dist/ui/screens/calendar-data.js.map +1 -0
- package/dist/ui/screens/content-insights-data.d.ts +54 -0
- package/dist/ui/screens/content-insights-data.d.ts.map +1 -0
- package/dist/ui/screens/content-insights-data.js +82 -0
- package/dist/ui/screens/content-insights-data.js.map +1 -0
- package/dist/ui/screens/content-view.d.ts +21 -0
- package/dist/ui/screens/content-view.d.ts.map +1 -0
- package/dist/ui/screens/content-view.js +17 -0
- package/dist/ui/screens/content-view.js.map +1 -0
- package/dist/ui/screens/library-data.d.ts +76 -0
- package/dist/ui/screens/library-data.d.ts.map +1 -0
- package/dist/ui/screens/library-data.js +116 -0
- package/dist/ui/screens/library-data.js.map +1 -0
- package/dist/ui/screens/media-upload.d.ts +19 -0
- package/dist/ui/screens/media-upload.d.ts.map +1 -0
- package/dist/ui/screens/media-upload.js +187 -0
- package/dist/ui/screens/media-upload.js.map +1 -0
- package/dist/ui/screens/public-url.d.ts +24 -0
- package/dist/ui/screens/public-url.d.ts.map +1 -0
- package/dist/ui/screens/public-url.js +74 -0
- package/dist/ui/screens/public-url.js.map +1 -0
- package/dist/ui/screens/registry.d.ts +3 -0
- package/dist/ui/screens/registry.d.ts.map +1 -0
- package/dist/ui/screens/registry.js +38 -0
- package/dist/ui/screens/registry.js.map +1 -0
- package/dist/ui/screens/render-state.d.ts +105 -0
- package/dist/ui/screens/render-state.d.ts.map +1 -0
- package/dist/ui/screens/render-state.js +127 -0
- package/dist/ui/screens/render-state.js.map +1 -0
- package/dist/ui/screens/settings-data.d.ts +55 -0
- package/dist/ui/screens/settings-data.d.ts.map +1 -0
- package/dist/ui/screens/settings-data.js +89 -0
- package/dist/ui/screens/settings-data.js.map +1 -0
- package/dist/ui/screens/settings-panels-data.d.ts +58 -0
- package/dist/ui/screens/settings-panels-data.d.ts.map +1 -0
- package/dist/ui/screens/settings-panels-data.js +88 -0
- package/dist/ui/screens/settings-panels-data.js.map +1 -0
- package/dist/ui/screens/social-share-data.d.ts +307 -0
- package/dist/ui/screens/social-share-data.d.ts.map +1 -0
- package/dist/ui/screens/social-share-data.js +447 -0
- package/dist/ui/screens/social-share-data.js.map +1 -0
- package/dist/ui/screens/topics-data.d.ts +38 -0
- package/dist/ui/screens/topics-data.d.ts.map +1 -0
- package/dist/ui/screens/topics-data.js +50 -0
- package/dist/ui/screens/topics-data.js.map +1 -0
- package/dist/ui/styles/broadsheet.css +394 -0
- package/dist/ui/theme.d.ts +23 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +87 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/tweaks.d.ts +7 -0
- package/dist/ui/tweaks.d.ts.map +1 -0
- package/dist/ui/tweaks.js +31 -0
- package/dist/ui/tweaks.js.map +1 -0
- package/dist/ui/use-hash-router.d.ts +6 -0
- package/dist/ui/use-hash-router.d.ts.map +1 -0
- package/dist/ui/use-hash-router.js +15 -0
- package/dist/ui/use-hash-router.js.map +1 -0
- package/dist/ui/use-tweaks.d.ts +6 -0
- package/dist/ui/use-tweaks.d.ts.map +1 -0
- package/dist/ui/use-tweaks.js +24 -0
- package/dist/ui/use-tweaks.js.map +1 -0
- package/dist/ui/workspace-context.d.ts +34 -0
- package/dist/ui/workspace-context.d.ts.map +1 -0
- package/dist/ui/workspace-context.js +32 -0
- package/dist/ui/workspace-context.js.map +1 -0
- package/migrations/0001_create_cms_tables.sql +200 -0
- package/migrations/0002_review_softdelete_board.sql +112 -0
- package/migrations/0003_content_contributors.sql +16 -0
- package/migrations/0004_seo_faq_columns.sql +6 -0
- package/migrations/0005_revision_delta_columns.sql +5 -0
- package/migrations/0006_processing_trigger_token.sql +4 -0
- package/migrations/0007_content_slug_redirects.sql +9 -0
- package/migrations/0008_workspace_settings.sql +17 -0
- package/migrations/0009_workspace_memberships.sql +36 -0
- package/migrations/0010_api_keys_webhooks.sql +21 -0
- package/migrations/0011_notifications_activity.sql +22 -0
- package/migrations/0012_content_imports.sql +10 -0
- package/migrations/0013_media_normalization.sql +4 -0
- package/migrations/0014_api_key_prefix.sql +3 -0
- package/migrations/0015_cms_topics.sql +7 -0
- package/migrations/0016_content_insights.sql +53 -0
- package/package.json +82 -0
- package/src/engine/activity-log.ts +39 -0
- package/src/engine/ai-prompts.ts +124 -0
- package/src/engine/ai-writeback.ts +62 -0
- package/src/engine/api-keys.ts +239 -0
- package/src/engine/content-insights.ts +198 -0
- package/src/engine/contributors.ts +95 -0
- package/src/engine/cron.ts +62 -0
- package/src/engine/d1.ts +29 -0
- package/src/engine/foundry-dispatch.ts +417 -0
- package/src/engine/import-parsers.ts +478 -0
- package/src/engine/index.ts +230 -0
- package/src/engine/invites.ts +271 -0
- package/src/engine/members.ts +216 -0
- package/src/engine/membership-rules.ts +63 -0
- package/src/engine/og-render.ts +59 -0
- package/src/engine/publish-guard.ts +123 -0
- package/src/engine/publisher.ts +1032 -0
- package/src/engine/revisions.ts +292 -0
- package/src/engine/sanitize.ts +183 -0
- package/src/engine/seed-membership.ts +92 -0
- package/src/engine/seo.ts +72 -0
- package/src/engine/slug-redirects.ts +34 -0
- package/src/engine/slug.ts +33 -0
- package/src/engine/soft-delete.ts +42 -0
- package/src/engine/tags.ts +95 -0
- package/src/engine/topics.ts +158 -0
- package/src/engine/url-guard.ts +136 -0
- package/src/engine/validator/checks/bare-url-not-autolinked.ts +78 -0
- package/src/engine/validator/checks/broken-footnote-label.ts +33 -0
- package/src/engine/validator/checks/double-encoded-entities.ts +46 -0
- package/src/engine/validator/checks/empty-alt-text.ts +35 -0
- package/src/engine/validator/checks/heading-hierarchy-skip.ts +33 -0
- package/src/engine/validator/checks/html-comment-leak.ts +58 -0
- package/src/engine/validator/checks/iframe-missing-dims-and-wrapper.ts +34 -0
- package/src/engine/validator/checks/invisible-control-chars.ts +58 -0
- package/src/engine/validator/checks/paywall-marker-leak.ts +43 -0
- package/src/engine/validator/checks/raw-block-html.ts +65 -0
- package/src/engine/validator/checks/stale-body-html.ts +39 -0
- package/src/engine/validator/checks/unresolved-footnote-anchor.ts +61 -0
- package/src/engine/validator/checks/word-gdocs-paste-artifacts.ts +72 -0
- package/src/engine/validator/index.ts +385 -0
- package/src/engine/validator/scan.ts +103 -0
- package/src/engine/validator/types.ts +114 -0
- package/src/engine/webhook-signer.ts +139 -0
- package/src/engine/webhooks.ts +224 -0
- package/src/index.ts +79 -0
- package/src/integration/index.ts +298 -0
- package/src/integration/options.ts +30 -0
- package/src/integration/vite-plugin.ts +37 -0
- package/src/providers/index.ts +2 -0
- package/src/providers/null.ts +160 -0
- package/src/providers/types.ts +284 -0
- package/src/routes/ai.ts +461 -0
- package/src/routes/analytics.ts +78 -0
- package/src/routes/api-keys.ts +133 -0
- package/src/routes/authors.ts +282 -0
- package/src/routes/authz-matrix.ts +239 -0
- package/src/routes/calendar.ts +127 -0
- package/src/routes/config.ts +99 -0
- package/src/routes/content-insights.ts +159 -0
- package/src/routes/content.ts +1753 -0
- package/src/routes/context.ts +146 -0
- package/src/routes/cron.ts +27 -0
- package/src/routes/dashboard.ts +174 -0
- package/src/routes/imports.ts +190 -0
- package/src/routes/index.ts +295 -0
- package/src/routes/media-lib.ts +405 -0
- package/src/routes/media.ts +944 -0
- package/src/routes/preview.ts +182 -0
- package/src/routes/rbac-invites.ts +220 -0
- package/src/routes/rbac.ts +155 -0
- package/src/routes/shell.ts +163 -0
- package/src/routes/subscriptions.ts +167 -0
- package/src/routes/tags.ts +93 -0
- package/src/routes/topics.ts +58 -0
- package/src/routes/webhooks.ts +233 -0
- package/src/schema/index.ts +45 -0
- package/src/schema/insights-ingest.ts +126 -0
- package/src/schema/migrations.ts +599 -0
- package/src/schema/tables.ts +59 -0
- package/src/schema/types.ts +576 -0
- package/src/ui/api/_authz.ts +100 -0
- package/src/ui/api/_content-config.ts +75 -0
- package/src/ui/api/activity.ts +33 -0
- package/src/ui/api/analytics.ts +42 -0
- package/src/ui/api/authors/[id].ts +23 -0
- package/src/ui/api/authors.ts +19 -0
- package/src/ui/api/calendar.ts +21 -0
- package/src/ui/api/content/[id]/ai/headlines.ts +10 -0
- package/src/ui/api/content/[id]/ai/meta-description.ts +11 -0
- package/src/ui/api/content/[id]/ai/og-image.ts +10 -0
- package/src/ui/api/content/[id]/ai/proofread.ts +10 -0
- package/src/ui/api/content/[id]/ai/takeaways.ts +11 -0
- package/src/ui/api/content/[id]/contributors.ts +13 -0
- package/src/ui/api/content/[id]/preview-token.ts +21 -0
- package/src/ui/api/content/[id]/revisions/[rev]/restore.ts +12 -0
- package/src/ui/api/content/[id]/revisions/[rev].ts +11 -0
- package/src/ui/api/content/[id]/revisions.ts +9 -0
- package/src/ui/api/content/[id]/seo-score.ts +10 -0
- package/src/ui/api/content/[id].ts +23 -0
- package/src/ui/api/content/bulk.ts +10 -0
- package/src/ui/api/content/counts.ts +11 -0
- package/src/ui/api/content/foundry-callback.ts +11 -0
- package/src/ui/api/content/import/confirm.ts +16 -0
- package/src/ui/api/content/import/parse.ts +16 -0
- package/src/ui/api/content/insights-ingest.ts +24 -0
- package/src/ui/api/content-insights/dismiss.ts +23 -0
- package/src/ui/api/content-insights/index.ts +21 -0
- package/src/ui/api/content-insights/undismiss.ts +23 -0
- package/src/ui/api/content.ts +21 -0
- package/src/ui/api/dashboard.ts +28 -0
- package/src/ui/api/me.ts +23 -0
- package/src/ui/api/media/[id].ts +22 -0
- package/src/ui/api/media/images.ts +9 -0
- package/src/ui/api/media/library/[id].ts +9 -0
- package/src/ui/api/media/library.ts +22 -0
- package/src/ui/api/media/podcast/abort.ts +6 -0
- package/src/ui/api/media/podcast/complete.ts +6 -0
- package/src/ui/api/media/podcast/init.ts +6 -0
- package/src/ui/api/media/podcast/part.ts +6 -0
- package/src/ui/api/media/podcast.ts +9 -0
- package/src/ui/api/media/videos/abort.ts +9 -0
- package/src/ui/api/media/videos/complete.ts +9 -0
- package/src/ui/api/media/videos/init.ts +9 -0
- package/src/ui/api/media/videos/part.ts +9 -0
- package/src/ui/api/notifications.ts +26 -0
- package/src/ui/api/search.ts +23 -0
- package/src/ui/api/settings/api-keys/[id].ts +28 -0
- package/src/ui/api/settings/api-keys.ts +25 -0
- package/src/ui/api/settings/domains.ts +37 -0
- package/src/ui/api/settings/integrations.ts +40 -0
- package/src/ui/api/settings/members/[userId].ts +33 -0
- package/src/ui/api/settings/members/invite.ts +27 -0
- package/src/ui/api/settings/members.ts +23 -0
- package/src/ui/api/settings/webhooks/[id]/test.ts +30 -0
- package/src/ui/api/settings/webhooks/[id].ts +29 -0
- package/src/ui/api/settings/webhooks.ts +27 -0
- package/src/ui/api/subscriptions.ts +56 -0
- package/src/ui/api/tags/[id].ts +24 -0
- package/src/ui/api/tags/index.ts +18 -0
- package/src/ui/api/topics/[id].ts +20 -0
- package/src/ui/api/topics/index.ts +17 -0
- package/src/ui/api/workspace-settings.ts +26 -0
- package/src/ui/client/boot-state.ts +42 -0
- package/src/ui/client/mount.tsx +41 -0
- package/src/ui/commands.ts +62 -0
- package/src/ui/components/CmsApp.tsx +149 -0
- package/src/ui/components/CommandPalette.tsx +118 -0
- package/src/ui/components/NoAccessScreen.tsx +79 -0
- package/src/ui/components/ShareModal.tsx +650 -0
- package/src/ui/components/SharePickers.tsx +790 -0
- package/src/ui/components/ShareStatsPanel.tsx +721 -0
- package/src/ui/components/Sidebar.tsx +86 -0
- package/src/ui/components/SiteSwitcher.tsx +100 -0
- package/src/ui/components/Topbar.tsx +93 -0
- package/src/ui/editor/AiAssistPanel.tsx +407 -0
- package/src/ui/editor/ContentForm.tsx +1462 -0
- package/src/ui/editor/Rte.tsx +382 -0
- package/src/ui/editor/ai-assist.ts +139 -0
- package/src/ui/editor/autosave.ts +36 -0
- package/src/ui/editor/content-payload.ts +125 -0
- package/src/ui/editor/editor-media-upload.ts +26 -0
- package/src/ui/editor/serialize.ts +522 -0
- package/src/ui/editor/tweet-embed.ts +60 -0
- package/src/ui/hash-router.ts +30 -0
- package/src/ui/icons.tsx +208 -0
- package/src/ui/inspector/Field.tsx +30 -0
- package/src/ui/inspector/FoundryTab.tsx +613 -0
- package/src/ui/inspector/HistoryTab.tsx +482 -0
- package/src/ui/inspector/Inspector.tsx +328 -0
- package/src/ui/inspector/OrganizeTab.tsx +534 -0
- package/src/ui/inspector/PublishTab.tsx +626 -0
- package/src/ui/inspector/Section.tsx +81 -0
- package/src/ui/inspector/SeoTab.tsx +573 -0
- package/src/ui/inspector/foundry-stages.ts +140 -0
- package/src/ui/inspector/inspector-data.ts +232 -0
- package/src/ui/inspector/organize-data.ts +51 -0
- package/src/ui/inspector/revision-diff.ts +213 -0
- package/src/ui/inspector/seo-helpers.ts +71 -0
- package/src/ui/inspector/tab-visibility.ts +37 -0
- package/src/ui/nav.ts +48 -0
- package/src/ui/pages/admin.astro +32 -0
- package/src/ui/preview/draft-page.tsx +395 -0
- package/src/ui/preview/preview-layout.ts +49 -0
- package/src/ui/screens/AnalyticsScreen.tsx +938 -0
- package/src/ui/screens/AuthorsScreen.tsx +524 -0
- package/src/ui/screens/CalendarScreen.tsx +694 -0
- package/src/ui/screens/ContentInsightsScreen.tsx +417 -0
- package/src/ui/screens/ContentRoute.tsx +35 -0
- package/src/ui/screens/DashboardScreen.tsx +654 -0
- package/src/ui/screens/EditorScreen.tsx +673 -0
- package/src/ui/screens/LibraryScreen.tsx +1350 -0
- package/src/ui/screens/MediaScreen.tsx +357 -0
- package/src/ui/screens/SettingsScreen.tsx +1841 -0
- package/src/ui/screens/SocialShareScreen.tsx +670 -0
- package/src/ui/screens/SubscriptionsScreen.tsx +1240 -0
- package/src/ui/screens/TopicsScreen.tsx +912 -0
- package/src/ui/screens/analytics-data.ts +68 -0
- package/src/ui/screens/calendar-data.ts +126 -0
- package/src/ui/screens/content-insights-data.ts +127 -0
- package/src/ui/screens/content-view.ts +30 -0
- package/src/ui/screens/library-data.ts +177 -0
- package/src/ui/screens/media-upload.ts +283 -0
- package/src/ui/screens/public-url.ts +81 -0
- package/src/ui/screens/registry.tsx +53 -0
- package/src/ui/screens/render-state.ts +228 -0
- package/src/ui/screens/settings-data.ts +140 -0
- package/src/ui/screens/settings-panels-data.ts +142 -0
- package/src/ui/screens/social-share-data.ts +753 -0
- package/src/ui/screens/topics-data.ts +75 -0
- package/src/ui/styles/broadsheet.css +394 -0
- package/src/ui/theme.ts +104 -0
- package/src/ui/tweaks.ts +37 -0
- package/src/ui/use-hash-router.ts +17 -0
- package/src/ui/use-tweaks.ts +31 -0
- package/src/ui/workspace-context.tsx +62 -0
- package/src/virtual.d.ts +4 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// Revision engine — createRevisionWithDelta / listRevisions / getRevision / restoreRevision.
|
|
2
|
+
//
|
|
3
|
+
// Backs the History tab (spec §4 "ContentRevisionPayload"). Builds on the
|
|
4
|
+
// existing content_revisions table (0001 baseline) + the 0005 delta columns
|
|
5
|
+
// (words_added, words_removed, tag). Snapshot shape is formalized as
|
|
6
|
+
// ContentRevisionPayload, aligned to getContentSnapshot's output.
|
|
7
|
+
|
|
8
|
+
import type { D1Database } from './d1.js'
|
|
9
|
+
import { countWords, getContentItem, getContentSnapshot, updateContentItem } from './publisher.js'
|
|
10
|
+
|
|
11
|
+
/** The one canonical revision snapshot shape (spec §4 "ContentRevisionPayload"). */
|
|
12
|
+
export interface ContentRevisionPayload {
|
|
13
|
+
item: Record<string, unknown>
|
|
14
|
+
content: Record<string, unknown> | null
|
|
15
|
+
tags: string[]
|
|
16
|
+
related: Array<{ related_id: string; rank: number; reason: string | null }>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RevisionSummary {
|
|
20
|
+
id: string
|
|
21
|
+
contentId: string
|
|
22
|
+
createdAt: number
|
|
23
|
+
createdBy: string | null
|
|
24
|
+
wordsAdded: number | null
|
|
25
|
+
wordsRemoved: number | null
|
|
26
|
+
tag: string | null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function bodyWordCount(payload: ContentRevisionPayload | null): number {
|
|
30
|
+
const md =
|
|
31
|
+
payload?.content && typeof payload.content.body_markdown === 'string'
|
|
32
|
+
? (payload.content.body_markdown as string)
|
|
33
|
+
: ''
|
|
34
|
+
return countWords(md)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function latestRevisionPayload(
|
|
38
|
+
db: D1Database,
|
|
39
|
+
contentId: string,
|
|
40
|
+
): Promise<ContentRevisionPayload | null> {
|
|
41
|
+
const row = await db
|
|
42
|
+
.prepare(
|
|
43
|
+
'SELECT payload_json FROM content_revisions WHERE content_id = ? ORDER BY created_at DESC, rowid DESC LIMIT 1',
|
|
44
|
+
)
|
|
45
|
+
.bind(contentId)
|
|
46
|
+
.first<{ payload_json: string }>()
|
|
47
|
+
if (!row) return null
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(row.payload_json) as ContentRevisionPayload
|
|
50
|
+
} catch {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Snapshot the item now, computing word deltas vs the previous revision. */
|
|
56
|
+
export async function createRevisionWithDelta(
|
|
57
|
+
db: D1Database,
|
|
58
|
+
contentId: string,
|
|
59
|
+
opts: { createdBy?: string | null; tag?: string | null } = {},
|
|
60
|
+
): Promise<RevisionSummary | null> {
|
|
61
|
+
const snapshot = (await getContentSnapshot(db, contentId)) as ContentRevisionPayload | null
|
|
62
|
+
if (!snapshot) return null
|
|
63
|
+
const prev = await latestRevisionPayload(db, contentId)
|
|
64
|
+
const prevWc = bodyWordCount(prev)
|
|
65
|
+
const nextWc = bodyWordCount(snapshot)
|
|
66
|
+
const wordsAdded = Math.max(0, nextWc - prevWc)
|
|
67
|
+
const wordsRemoved = Math.max(0, prevWc - nextWc)
|
|
68
|
+
const id = crypto.randomUUID()
|
|
69
|
+
await db
|
|
70
|
+
.prepare(
|
|
71
|
+
'INSERT INTO content_revisions (id, content_id, payload_json, created_at, created_by, words_added, words_removed, tag) VALUES (?, ?, ?, unixepoch(), ?, ?, ?, ?)',
|
|
72
|
+
)
|
|
73
|
+
.bind(
|
|
74
|
+
id,
|
|
75
|
+
contentId,
|
|
76
|
+
JSON.stringify(snapshot),
|
|
77
|
+
opts.createdBy ?? null,
|
|
78
|
+
wordsAdded,
|
|
79
|
+
wordsRemoved,
|
|
80
|
+
opts.tag ?? null,
|
|
81
|
+
)
|
|
82
|
+
.run()
|
|
83
|
+
// NOTE: this is the autosave/History trigger (fired on a debounce while
|
|
84
|
+
// editing a draft, and by restoreRevision). It must NOT move
|
|
85
|
+
// content_items.published_revision_id — that pointer tracks the PUBLISHED
|
|
86
|
+
// snapshot and is only advanced by the publish path (createRevision in
|
|
87
|
+
// publisher.ts).
|
|
88
|
+
const row = await db
|
|
89
|
+
.prepare('SELECT created_at FROM content_revisions WHERE id = ?')
|
|
90
|
+
.bind(id)
|
|
91
|
+
.first<{ created_at: number }>()
|
|
92
|
+
return {
|
|
93
|
+
id,
|
|
94
|
+
contentId,
|
|
95
|
+
createdAt: row?.created_at ?? 0,
|
|
96
|
+
createdBy: opts.createdBy ?? null,
|
|
97
|
+
wordsAdded,
|
|
98
|
+
wordsRemoved,
|
|
99
|
+
tag: opts.tag ?? null,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function listRevisions(db: D1Database, contentId: string): Promise<RevisionSummary[]> {
|
|
104
|
+
const result = await db
|
|
105
|
+
.prepare(
|
|
106
|
+
'SELECT id, content_id, created_at, created_by, words_added, words_removed, tag FROM content_revisions WHERE content_id = ? ORDER BY created_at DESC, rowid DESC',
|
|
107
|
+
)
|
|
108
|
+
.bind(contentId)
|
|
109
|
+
.all<{
|
|
110
|
+
id: string
|
|
111
|
+
content_id: string
|
|
112
|
+
created_at: number
|
|
113
|
+
created_by: string | null
|
|
114
|
+
words_added: number | null
|
|
115
|
+
words_removed: number | null
|
|
116
|
+
tag: string | null
|
|
117
|
+
}>()
|
|
118
|
+
return (result.results || []).map((r) => ({
|
|
119
|
+
id: r.id,
|
|
120
|
+
contentId: r.content_id,
|
|
121
|
+
createdAt: r.created_at,
|
|
122
|
+
createdBy: r.created_by,
|
|
123
|
+
wordsAdded: r.words_added,
|
|
124
|
+
wordsRemoved: r.words_removed,
|
|
125
|
+
tag: r.tag,
|
|
126
|
+
}))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function getRevision(
|
|
130
|
+
db: D1Database,
|
|
131
|
+
contentId: string,
|
|
132
|
+
revisionId: string,
|
|
133
|
+
): Promise<{ summary: RevisionSummary; payload: ContentRevisionPayload } | null> {
|
|
134
|
+
const row = await db
|
|
135
|
+
.prepare(
|
|
136
|
+
'SELECT id, content_id, payload_json, created_at, created_by, words_added, words_removed, tag FROM content_revisions WHERE id = ? AND content_id = ? LIMIT 1',
|
|
137
|
+
)
|
|
138
|
+
.bind(revisionId, contentId)
|
|
139
|
+
.first<{
|
|
140
|
+
id: string
|
|
141
|
+
content_id: string
|
|
142
|
+
payload_json: string
|
|
143
|
+
created_at: number
|
|
144
|
+
created_by: string | null
|
|
145
|
+
words_added: number | null
|
|
146
|
+
words_removed: number | null
|
|
147
|
+
tag: string | null
|
|
148
|
+
}>()
|
|
149
|
+
if (!row) return null
|
|
150
|
+
let payload: ContentRevisionPayload
|
|
151
|
+
try {
|
|
152
|
+
payload = JSON.parse(row.payload_json) as ContentRevisionPayload
|
|
153
|
+
} catch {
|
|
154
|
+
return null
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
summary: {
|
|
158
|
+
id: row.id,
|
|
159
|
+
contentId: row.content_id,
|
|
160
|
+
createdAt: row.created_at,
|
|
161
|
+
createdBy: row.created_by,
|
|
162
|
+
wordsAdded: row.words_added,
|
|
163
|
+
wordsRemoved: row.words_removed,
|
|
164
|
+
tag: row.tag,
|
|
165
|
+
},
|
|
166
|
+
payload,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Restore: replay the revision's item+body+tags+relations through updateContentItem,
|
|
172
|
+
* then snapshot the restore as a new revision tagged 'restore'.
|
|
173
|
+
*/
|
|
174
|
+
export async function restoreRevision(
|
|
175
|
+
db: D1Database,
|
|
176
|
+
contentId: string,
|
|
177
|
+
revisionId: string,
|
|
178
|
+
opts: { createdBy?: string | null } = {},
|
|
179
|
+
): Promise<boolean> {
|
|
180
|
+
const target = await getRevision(db, contentId, revisionId)
|
|
181
|
+
if (!target) return false
|
|
182
|
+
const existing = await getContentItem(db, contentId)
|
|
183
|
+
if (!existing) return false
|
|
184
|
+
const item = target.payload.item as Record<string, unknown>
|
|
185
|
+
const content = target.payload.content as Record<string, unknown> | null
|
|
186
|
+
// Map the snapshot's snake_case row back onto the updateContentItem camelCase input.
|
|
187
|
+
const body = content as {
|
|
188
|
+
body_markdown?: string
|
|
189
|
+
body_html?: string | null
|
|
190
|
+
subtitle?: string | null
|
|
191
|
+
script?: string
|
|
192
|
+
video_id?: string
|
|
193
|
+
duration_seconds?: number | null
|
|
194
|
+
thumbnail_image_id?: string | null
|
|
195
|
+
transcript?: string
|
|
196
|
+
audio_r2_key?: string
|
|
197
|
+
editor_takeaways?: string | null
|
|
198
|
+
faq_json?: string | null
|
|
199
|
+
} | null
|
|
200
|
+
|
|
201
|
+
// Editor takeaways are stored as a JSON-encoded string array on article_content.
|
|
202
|
+
// updateContentItem takes them as a string[] via content.editorTakeaways, so
|
|
203
|
+
// parse the snapshot's stored JSON back into an array for a faithful restore.
|
|
204
|
+
function parseTakeaways(raw: string | null | undefined): string[] | null {
|
|
205
|
+
if (!raw) return null
|
|
206
|
+
try {
|
|
207
|
+
const parsed = JSON.parse(raw)
|
|
208
|
+
return Array.isArray(parsed) ? (parsed as string[]) : null
|
|
209
|
+
} catch {
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const restoredTakeaways = parseTakeaways(body?.editor_takeaways)
|
|
214
|
+
// Restore intentionally rolls back CONTENT fields (title/body/tags/relations/
|
|
215
|
+
// featured/seo/hero/etc.) to the snapshot, but NOT publication state
|
|
216
|
+
// (status / publish_at / published_at / published_revision_id) — that is by
|
|
217
|
+
// design: restoring an old draft must not silently re-publish or un-publish.
|
|
218
|
+
await updateContentItem(db, contentId, {
|
|
219
|
+
title: (item.title as string) ?? existing.title,
|
|
220
|
+
featured: Boolean(item.featured),
|
|
221
|
+
seoTitle: (item.seo_title as string | null) ?? null,
|
|
222
|
+
description: (item.description as string | null) ?? null,
|
|
223
|
+
excerpt: (item.excerpt as string | null) ?? null,
|
|
224
|
+
byline: (item.byline as string | null) ?? null,
|
|
225
|
+
primaryCategory: (item.channel as string | null) ?? null,
|
|
226
|
+
primaryTopic: (item.primary_topic as string | null) ?? null,
|
|
227
|
+
visibility: (item.visibility as 'free' | 'premium') ?? existing.visibility,
|
|
228
|
+
authorId: (item.author_id as string | null) ?? null,
|
|
229
|
+
heroImageId: (item.hero_image_id as string | null) ?? null,
|
|
230
|
+
heroImageAlt: (item.hero_image_alt as string | null) ?? null,
|
|
231
|
+
heroImageCaption: (item.hero_image_caption as string | null) ?? null,
|
|
232
|
+
socialImageId: (item.social_image_id as string | null) ?? null,
|
|
233
|
+
canonicalUrl: (item.canonical_url as string | null) ?? null,
|
|
234
|
+
// SEO focus keyword is part of the snapshot — restore it too.
|
|
235
|
+
seoFocusKeyword: (item.seo_focus_keyword as string | null) ?? null,
|
|
236
|
+
tags: target.payload.tags,
|
|
237
|
+
relations: target.payload.related.map((r) => ({
|
|
238
|
+
relatedId: r.related_id,
|
|
239
|
+
rank: r.rank,
|
|
240
|
+
reason: r.reason,
|
|
241
|
+
})),
|
|
242
|
+
content:
|
|
243
|
+
existing.type === 'article' || existing.type === 'newsletter'
|
|
244
|
+
? {
|
|
245
|
+
bodyMarkdown: body?.body_markdown ?? '',
|
|
246
|
+
bodyHtml: body?.body_html ?? null,
|
|
247
|
+
subtitle: body?.subtitle ?? null,
|
|
248
|
+
// Editor takeaways were captured in the snapshot — roll them back.
|
|
249
|
+
editorTakeaways: restoredTakeaways,
|
|
250
|
+
}
|
|
251
|
+
: existing.type === 'video'
|
|
252
|
+
? {
|
|
253
|
+
script: body?.script ?? '',
|
|
254
|
+
videoId: body?.video_id ?? '',
|
|
255
|
+
durationSeconds: body?.duration_seconds ?? null,
|
|
256
|
+
thumbnailImageId: body?.thumbnail_image_id ?? null,
|
|
257
|
+
}
|
|
258
|
+
: {
|
|
259
|
+
transcript: body?.transcript ?? '',
|
|
260
|
+
audioR2Key: body?.audio_r2_key ?? '',
|
|
261
|
+
durationSeconds: body?.duration_seconds ?? null,
|
|
262
|
+
},
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
// updateContentItem does not carry seo_score / ai_og_image_id (these are set
|
|
266
|
+
// by SEO scoring + AI-OG generation, not the editor form) or faq_json. They
|
|
267
|
+
// ARE captured in the snapshot, so restore them directly for fidelity — a
|
|
268
|
+
// restore must reproduce the snapshot, not silently drop SEO/AI metadata.
|
|
269
|
+
await db
|
|
270
|
+
.prepare(
|
|
271
|
+
'UPDATE content_items SET seo_score = ?, ai_og_image_id = ?, updated_at = unixepoch() WHERE id = ?',
|
|
272
|
+
)
|
|
273
|
+
.bind(
|
|
274
|
+
(item.seo_score as number | null) ?? null,
|
|
275
|
+
(item.ai_og_image_id as string | null) ?? null,
|
|
276
|
+
contentId,
|
|
277
|
+
)
|
|
278
|
+
.run()
|
|
279
|
+
|
|
280
|
+
if (existing.type === 'article' || existing.type === 'newsletter') {
|
|
281
|
+
await db
|
|
282
|
+
.prepare('UPDATE article_content SET faq_json = ? WHERE content_id = ?')
|
|
283
|
+
.bind(body?.faq_json ?? null, contentId)
|
|
284
|
+
.run()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await createRevisionWithDelta(db, contentId, {
|
|
288
|
+
createdBy: opts.createdBy ?? null,
|
|
289
|
+
tag: 'restore',
|
|
290
|
+
})
|
|
291
|
+
return true
|
|
292
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sanitize.ts
|
|
3
|
+
*
|
|
4
|
+
* Shared L2 sanitization passes that run AFTER the validator has emitted
|
|
5
|
+
* its report. A site's renderers (article, podcast, video) consume these
|
|
6
|
+
* so they never drift on what counts as "safe stored body".
|
|
7
|
+
*
|
|
8
|
+
* Ported verbatim from fulcrum-labs/fronts src/lib/content-sanitize.ts
|
|
9
|
+
* (inventory §1: extract — the sanitize helper). Each pass is pure and
|
|
10
|
+
* composable; the default {@link sanitizeBody} orchestration is
|
|
11
|
+
* idempotent. It reuses the validator checks' `repair` functions so the
|
|
12
|
+
* detector and the fixer can never diverge.
|
|
13
|
+
*
|
|
14
|
+
* Negative invariants:
|
|
15
|
+
* - Fenced code blocks are preserved unchanged.
|
|
16
|
+
* - Inline code spans are preserved unchanged.
|
|
17
|
+
* - URLs inside `[label](url)` are preserved unchanged.
|
|
18
|
+
* - Podcast/video speaker prefixes (`Alice:`, `[00:01:23] Bob:`) are
|
|
19
|
+
* preserved unchanged — no pass strips the colon or numeric label.
|
|
20
|
+
* - Legitimate markdown escapes (`\*emphasis\*`, `\[brackets\]`) are
|
|
21
|
+
* preserved unchanged.
|
|
22
|
+
*/
|
|
23
|
+
import { repair as repairBrokenFootnoteLabel } from './validator/checks/broken-footnote-label.js'
|
|
24
|
+
import { repair as repairDoubleEncodedEntities } from './validator/checks/double-encoded-entities.js'
|
|
25
|
+
import { repair as repairHtmlCommentLeak } from './validator/checks/html-comment-leak.js'
|
|
26
|
+
import { repair as repairInvisibleControl } from './validator/checks/invisible-control-chars.js'
|
|
27
|
+
import { repair as repairPaywallMarker } from './validator/checks/paywall-marker-leak.js'
|
|
28
|
+
import { repair as repairRawBlockHtml } from './validator/checks/raw-block-html.js'
|
|
29
|
+
import { repair as repairWordGdocs } from './validator/checks/word-gdocs-paste-artifacts.js'
|
|
30
|
+
|
|
31
|
+
/* -------------------------------------------------------------------------- */
|
|
32
|
+
/* Mirror with validator */
|
|
33
|
+
/* -------------------------------------------------------------------------- */
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Mirror of the legit-escape set in `./validator/index.ts`. If you
|
|
37
|
+
* change one, change the other.
|
|
38
|
+
*/
|
|
39
|
+
const LEGIT_ESCAPE_CHARS = '*_[]{}|`#+!>\\-'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Phase 1 strip: drop the leading backslash before any character that
|
|
43
|
+
* is not a legitimate markdown escape. Mirror of
|
|
44
|
+
* {@link checkBackslashOverEscape}'s detector.
|
|
45
|
+
*
|
|
46
|
+
* Also drops HTML-entity-encoded backslashes (`\`, `\`,
|
|
47
|
+
* `\`) that bypass the literal-backslash strip.
|
|
48
|
+
*/
|
|
49
|
+
export function stripOverEscapes(value: string): string {
|
|
50
|
+
const literal = value.replace(/\\(.)/g, (match, ch: string) =>
|
|
51
|
+
LEGIT_ESCAPE_CHARS.includes(ch) ? match : ch,
|
|
52
|
+
)
|
|
53
|
+
return literal.replace(/(?:\|[Cc];|\)(.)/g, (match, ch: string) =>
|
|
54
|
+
LEGIT_ESCAPE_CHARS.includes(ch) ? match : ch,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Phase 1 strip (heading-scoped): decode HTML entities only inside
|
|
60
|
+
* heading lines. Entities inside paragraph prose are intentionally
|
|
61
|
+
* left alone — they're sometimes part of legitimate publication
|
|
62
|
+
* names (`Tools & Trade`).
|
|
63
|
+
*/
|
|
64
|
+
export function decodeHeadingEntities(value: string): string {
|
|
65
|
+
const ENTITY_MAP: Record<string, string> = {
|
|
66
|
+
'"': '"',
|
|
67
|
+
''': "'",
|
|
68
|
+
''': "'",
|
|
69
|
+
'&': '&',
|
|
70
|
+
'<': '<',
|
|
71
|
+
'>': '>',
|
|
72
|
+
}
|
|
73
|
+
return value
|
|
74
|
+
.split('\n')
|
|
75
|
+
.map((line) => {
|
|
76
|
+
if (!/^\s*#{1,6}\s+/.test(line)) return line
|
|
77
|
+
return line.replace(
|
|
78
|
+
/&(?:quot|apos|amp|lt|gt|#\d+);/g,
|
|
79
|
+
(entity) => ENTITY_MAP[entity] ?? entity,
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
.join('\n')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* -------------------------------------------------------------------------- */
|
|
86
|
+
/* Phase 2 — sanitize passes */
|
|
87
|
+
/* -------------------------------------------------------------------------- */
|
|
88
|
+
|
|
89
|
+
export interface SanitizeOptions {
|
|
90
|
+
/**
|
|
91
|
+
* When true, the speaker-aware passes leave podcast/video speaker
|
|
92
|
+
* prefixes alone. Default true (the safe option for all callers).
|
|
93
|
+
*/
|
|
94
|
+
preserveSpeakerPrefixes: boolean
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const DEFAULT_SANITIZE_OPTIONS: SanitizeOptions = {
|
|
98
|
+
preserveSpeakerPrefixes: true,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Phase 2 strip composition. Order matters in a few places — see the
|
|
103
|
+
* inline comments. The composition is idempotent.
|
|
104
|
+
*
|
|
105
|
+
* Order:
|
|
106
|
+
* 1. Word/Gdocs paste artifacts (strip BEFORE we look for HTML
|
|
107
|
+
* comments, because Word paste residue is itself a comment).
|
|
108
|
+
* 2. HTML comment leak (whitelisted-only).
|
|
109
|
+
* 3. Paywall marker leak (defensive — even though the splitter
|
|
110
|
+
* should have eaten these already).
|
|
111
|
+
* 4. Double-encoded entities (`&quot;` → `"` — must run
|
|
112
|
+
* BEFORE the heading entity decode so it sees the decoded form).
|
|
113
|
+
* 5. Backslash over-escape (Phase 1 strip). CODE-AWARE: fenced and
|
|
114
|
+
* inline code regions are protected.
|
|
115
|
+
* 6. Heading-entity decode (Phase 1 strip).
|
|
116
|
+
* 7. Broken footnote label `[[N]](#a)` → `[N](#a)`.
|
|
117
|
+
* 8. Invisible control chars (last — they're invisible in raw text
|
|
118
|
+
* so order vs other passes doesn't matter, but doing it last
|
|
119
|
+
* means subsequent regex passes can't get tripped up by them).
|
|
120
|
+
* 9. `<br>` lines → blank line (raw-block-html repair).
|
|
121
|
+
*/
|
|
122
|
+
export function sanitizeBody(
|
|
123
|
+
value: string,
|
|
124
|
+
_options: SanitizeOptions = DEFAULT_SANITIZE_OPTIONS,
|
|
125
|
+
): string {
|
|
126
|
+
let out = value
|
|
127
|
+
out = repairWordGdocs(out)
|
|
128
|
+
out = repairHtmlCommentLeak(out)
|
|
129
|
+
out = repairPaywallMarker(out)
|
|
130
|
+
out = repairDoubleEncodedEntities(out)
|
|
131
|
+
out = stripOverEscapesPreservingCode(out)
|
|
132
|
+
out = decodeHeadingEntities(out)
|
|
133
|
+
out = repairBrokenFootnoteLabel(out)
|
|
134
|
+
out = repairInvisibleControl(out)
|
|
135
|
+
out = repairRawBlockHtml(out)
|
|
136
|
+
return out
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Code-aware variant of {@link stripOverEscapes}. Fenced (` ``` `) and
|
|
141
|
+
* inline (` ` `) code regions are extracted, the strip runs on the
|
|
142
|
+
* surrounding prose, and the code regions are spliced back in
|
|
143
|
+
* verbatim. This preserves intentional escape sequences in code
|
|
144
|
+
* examples (`'\, \.'` etc.) while still cleaning prose.
|
|
145
|
+
*/
|
|
146
|
+
function stripOverEscapesPreservingCode(value: string): string {
|
|
147
|
+
const segments: Array<{ kind: 'prose' | 'code'; text: string }> = []
|
|
148
|
+
const fenceRe = /(^|\n)([ \t]*)(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n\2\3/g
|
|
149
|
+
const inlineRe = /`[^`\n]+`/g
|
|
150
|
+
type Span = { start: number; end: number }
|
|
151
|
+
const protectedSpans: Span[] = []
|
|
152
|
+
for (const m of value.matchAll(fenceRe)) {
|
|
153
|
+
protectedSpans.push({
|
|
154
|
+
start: m.index ?? 0,
|
|
155
|
+
end: (m.index ?? 0) + m[0].length,
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
for (const m of value.matchAll(inlineRe)) {
|
|
159
|
+
const start = m.index ?? 0
|
|
160
|
+
const end = start + m[0].length
|
|
161
|
+
if (protectedSpans.some((s) => start >= s.start && end <= s.end)) continue
|
|
162
|
+
protectedSpans.push({ start, end })
|
|
163
|
+
}
|
|
164
|
+
protectedSpans.sort((a, b) => a.start - b.start)
|
|
165
|
+
let cursor = 0
|
|
166
|
+
for (const span of protectedSpans) {
|
|
167
|
+
if (span.start > cursor) {
|
|
168
|
+
segments.push({ kind: 'prose', text: value.slice(cursor, span.start) })
|
|
169
|
+
}
|
|
170
|
+
segments.push({ kind: 'code', text: value.slice(span.start, span.end) })
|
|
171
|
+
cursor = span.end
|
|
172
|
+
}
|
|
173
|
+
if (cursor < value.length) {
|
|
174
|
+
segments.push({ kind: 'prose', text: value.slice(cursor) })
|
|
175
|
+
}
|
|
176
|
+
return segments.map((s) => (s.kind === 'prose' ? stripOverEscapes(s.text) : s.text)).join('')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Convenience export — the renderer side of the same module. Returns
|
|
181
|
+
* the input cleaned via every pass once.
|
|
182
|
+
*/
|
|
183
|
+
export const sanitize = sanitizeBody
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// seed-membership.ts — Parameterized membership pre-seed helper (P7 Task 6).
|
|
2
|
+
//
|
|
3
|
+
// PRE-SEED IS AN OPERATOR/CUTOVER STEP.
|
|
4
|
+
//
|
|
5
|
+
// Look up each person's REAL subject in the Fulcrum issuer D1 by email —
|
|
6
|
+
// subjects are minted + stable in the issuer before first login. NEVER
|
|
7
|
+
// hardcode subjects or emails; pass them at call time.
|
|
8
|
+
//
|
|
9
|
+
// A placeholder id → no membership match → 403 on every request.
|
|
10
|
+
// A placeholder email on the UNIQUE masthead_users.email column → 500 on the
|
|
11
|
+
// real person's first login (the exact inbox-platform bug, §16 risk 10).
|
|
12
|
+
//
|
|
13
|
+
// Usage (at cutover, after looking up real subjects in the issuer D1):
|
|
14
|
+
//
|
|
15
|
+
// await seedMembership(db, { workspaceId: 'ws-prod', subject: 'user:<real-uuid>', email, name, role: 'owner' })
|
|
16
|
+
//
|
|
17
|
+
// This helper is idempotent: calling it twice with the same (workspaceId,
|
|
18
|
+
// subject) pair updates the membership and user profile in place — it does NOT
|
|
19
|
+
// insert a duplicate or fail silently.
|
|
20
|
+
|
|
21
|
+
import type { CmsRole } from '../schema/types.js'
|
|
22
|
+
import type { D1Database } from './d1.js'
|
|
23
|
+
import { upsertMastheadUser } from './members.js'
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Input type
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export interface SeedMembershipInput {
|
|
30
|
+
workspaceId: string
|
|
31
|
+
/**
|
|
32
|
+
* The REAL OpenAuth subject from the Fulcrum issuer — MUST start with
|
|
33
|
+
* 'user:'. Never pass a locally-minted or placeholder id here.
|
|
34
|
+
*/
|
|
35
|
+
subject: string
|
|
36
|
+
email: string
|
|
37
|
+
name: string | null
|
|
38
|
+
role: CmsRole
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// seedMembership
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Seed a workspace membership from a known real OpenAuth subject.
|
|
47
|
+
*
|
|
48
|
+
* Validates that the subject starts with 'user:' (rejects placeholder ids).
|
|
49
|
+
* Upserts `masthead_users` (id = subject) and `workspace_memberships` with
|
|
50
|
+
* `accepted_at` set — a seeded member is pre-accepted (no invite required).
|
|
51
|
+
*
|
|
52
|
+
* Idempotent: safe to run multiple times; the second call updates in place.
|
|
53
|
+
*
|
|
54
|
+
* Throws if:
|
|
55
|
+
* - subject does not start with 'user:' (placeholder guard)
|
|
56
|
+
* - a different id already holds the same email (UNIQUE collision on
|
|
57
|
+
* masthead_users.email — surfaces as an error, never a silent 500)
|
|
58
|
+
*/
|
|
59
|
+
export async function seedMembership(db: D1Database, input: SeedMembershipInput): Promise<void> {
|
|
60
|
+
if (!input.subject?.startsWith('user:')) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`seedMembership: subject must start with 'user:' — received: ${JSON.stringify(input.subject)}. ` +
|
|
63
|
+
'Look up the real subject in the Fulcrum issuer D1 by email before seeding. ' +
|
|
64
|
+
'A placeholder id causes 403 on every request (§16 risk 10).',
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const now = Math.floor(Date.now() / 1000)
|
|
69
|
+
|
|
70
|
+
// Upsert masthead_users — will throw a UNIQUE constraint error if a
|
|
71
|
+
// DIFFERENT id already holds this email (the §16 risk-10 collision surface).
|
|
72
|
+
// We deliberately do NOT silence that error here.
|
|
73
|
+
await upsertMastheadUser(db, {
|
|
74
|
+
id: input.subject,
|
|
75
|
+
email: input.email,
|
|
76
|
+
name: input.name,
|
|
77
|
+
image: null,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Upsert the membership row with accepted_at set (pre-accepted seed).
|
|
81
|
+
await db
|
|
82
|
+
.prepare(
|
|
83
|
+
`INSERT INTO workspace_memberships (workspace_id, user_id, role, invited_by, accepted_at, created_at, updated_at)
|
|
84
|
+
VALUES (?, ?, ?, NULL, ?, ?, ?)
|
|
85
|
+
ON CONFLICT(workspace_id, user_id) DO UPDATE SET
|
|
86
|
+
role = excluded.role,
|
|
87
|
+
accepted_at = COALESCE(workspace_memberships.accepted_at, excluded.accepted_at),
|
|
88
|
+
updated_at = excluded.updated_at`,
|
|
89
|
+
)
|
|
90
|
+
.bind(input.workspaceId, input.subject, input.role, now, now, now)
|
|
91
|
+
.run()
|
|
92
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { countWords } from './publisher.js'
|
|
2
|
+
|
|
3
|
+
export interface SeoScoreInput {
|
|
4
|
+
title: string
|
|
5
|
+
seoTitle?: string | null
|
|
6
|
+
description?: string | null
|
|
7
|
+
focusKeyword?: string | null
|
|
8
|
+
bodyMarkdown?: string | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SeoCheck {
|
|
12
|
+
id: string
|
|
13
|
+
label: string
|
|
14
|
+
pass: boolean
|
|
15
|
+
hint: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SeoScoreResult {
|
|
19
|
+
score: number
|
|
20
|
+
checks: SeoCheck[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const MIN_DESC = 50
|
|
24
|
+
const MAX_DESC = 160
|
|
25
|
+
const MIN_TITLE = 15
|
|
26
|
+
const MAX_TITLE = 60
|
|
27
|
+
const MIN_BODY_WORDS = 300
|
|
28
|
+
|
|
29
|
+
/** The in-package default SeoProvider scorer: 5 checks × 20 pts (spec §8). */
|
|
30
|
+
export function scoreDraft(input: SeoScoreInput): SeoScoreResult {
|
|
31
|
+
const title = (input.seoTitle || input.title || '').trim()
|
|
32
|
+
const description = (input.description || '').trim()
|
|
33
|
+
const keyword = (input.focusKeyword || '').trim().toLowerCase()
|
|
34
|
+
const bodyWords = countWords(input.bodyMarkdown || '')
|
|
35
|
+
const lcTitle = title.toLowerCase()
|
|
36
|
+
const lcBody = (input.bodyMarkdown || '').toLowerCase()
|
|
37
|
+
|
|
38
|
+
const checks: SeoCheck[] = [
|
|
39
|
+
{
|
|
40
|
+
id: 'title-length',
|
|
41
|
+
label: 'Title length',
|
|
42
|
+
pass: title.length >= MIN_TITLE && title.length <= MAX_TITLE,
|
|
43
|
+
hint: `Keep the title between ${MIN_TITLE} and ${MAX_TITLE} characters.`,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'meta-description',
|
|
47
|
+
label: 'Meta description',
|
|
48
|
+
pass: description.length >= MIN_DESC && description.length <= MAX_DESC,
|
|
49
|
+
hint: `Write a meta description ${MIN_DESC}–${MAX_DESC} characters long.`,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'keyword-in-title',
|
|
53
|
+
label: 'Focus keyword in title',
|
|
54
|
+
pass: keyword.length > 0 && lcTitle.includes(keyword),
|
|
55
|
+
hint: 'Include the focus keyword in the title.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'keyword-in-body',
|
|
59
|
+
label: 'Focus keyword in body',
|
|
60
|
+
pass: keyword.length > 0 && lcBody.includes(keyword),
|
|
61
|
+
hint: 'Mention the focus keyword in the body.',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'body-length',
|
|
65
|
+
label: 'Body length',
|
|
66
|
+
pass: bodyWords >= MIN_BODY_WORDS,
|
|
67
|
+
hint: `Write at least ${MIN_BODY_WORDS} words (currently ${bodyWords}).`,
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
const score = checks.reduce((sum, c) => sum + (c.pass ? 20 : 0), 0)
|
|
71
|
+
return { score, checks }
|
|
72
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Slug-redirect engine (P3 Task 2).
|
|
2
|
+
//
|
|
3
|
+
// When a content item's slug is renamed, the old slug must be preserved as a
|
|
4
|
+
// redirect so live URLs survive (spec §8 "slug editor with
|
|
5
|
+
// content_slug_redirects"). This module is the write-side; the redirect lookup
|
|
6
|
+
// lives at the CDN / Fronts edge layer (out of scope for this package).
|
|
7
|
+
//
|
|
8
|
+
// The table `content_slug_redirects` was created in migration 0007:
|
|
9
|
+
// old_slug TEXT PRIMARY KEY,
|
|
10
|
+
// content_id TEXT NOT NULL,
|
|
11
|
+
// created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
12
|
+
// ON CONFLICT(old_slug) DO NOTHING ensures idempotency.
|
|
13
|
+
|
|
14
|
+
import type { D1Database } from './d1.js'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Record `oldSlug` as a redirect pointing at `contentId`.
|
|
18
|
+
* Idempotent: if `old_slug` is already in the table the row is silently kept
|
|
19
|
+
* as-is (INSERT … ON CONFLICT(old_slug) DO NOTHING).
|
|
20
|
+
*/
|
|
21
|
+
export async function recordSlugRedirect(
|
|
22
|
+
db: D1Database,
|
|
23
|
+
oldSlug: string,
|
|
24
|
+
contentId: string,
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
await db
|
|
27
|
+
.prepare(
|
|
28
|
+
`INSERT INTO content_slug_redirects (old_slug, content_id)
|
|
29
|
+
VALUES (?, ?)
|
|
30
|
+
ON CONFLICT(old_slug) DO NOTHING`,
|
|
31
|
+
)
|
|
32
|
+
.bind(oldSlug, contentId)
|
|
33
|
+
.run()
|
|
34
|
+
}
|