@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,33 @@
|
|
|
1
|
+
// Slug helpers ported verbatim from fulcrum-labs/fronts src/lib/slug.ts
|
|
2
|
+
// (inventory §1: `slug.ts` — extract, pure). No Fronts coupling.
|
|
3
|
+
|
|
4
|
+
export function slugify(input: string): string {
|
|
5
|
+
return input
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/['"`]/g, '')
|
|
8
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
9
|
+
.replace(/^-+|-+$/g, '')
|
|
10
|
+
.replace(/--+/g, '-')
|
|
11
|
+
.trim()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function normalizeSlug(input: string | null | undefined): string {
|
|
15
|
+
return slugify(input ?? '')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function ensureTrailingSlash(pathname: string): string {
|
|
19
|
+
if (!pathname) return '/'
|
|
20
|
+
return pathname.endsWith('/') ? pathname : `${pathname}/`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function stripTrailingSlash(pathname: string): string {
|
|
24
|
+
if (!pathname || pathname === '/') return '/'
|
|
25
|
+
return pathname.replace(/\/+$/, '')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildContentPath(
|
|
29
|
+
type: 'article' | 'video' | 'podcast' | 'authors' | 'regions',
|
|
30
|
+
slug: string,
|
|
31
|
+
): string {
|
|
32
|
+
return ensureTrailingSlash(`/${type}/${normalizeSlug(slug)}`)
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { D1Database } from './d1.js'
|
|
2
|
+
|
|
3
|
+
export async function softDeleteContent(
|
|
4
|
+
db: D1Database,
|
|
5
|
+
id: string,
|
|
6
|
+
now?: number,
|
|
7
|
+
): Promise<boolean> {
|
|
8
|
+
const ts = now ?? Math.floor(Date.now() / 1000)
|
|
9
|
+
const res = await db
|
|
10
|
+
.prepare(
|
|
11
|
+
'UPDATE content_items SET deleted_at = ?, updated_at = unixepoch() WHERE id = ? AND deleted_at IS NULL',
|
|
12
|
+
)
|
|
13
|
+
.bind(ts, id)
|
|
14
|
+
.run()
|
|
15
|
+
return Boolean(res.meta && (res.meta as { changes?: number }).changes)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function restoreContent(db: D1Database, id: string): Promise<boolean> {
|
|
19
|
+
const res = await db
|
|
20
|
+
.prepare(
|
|
21
|
+
'UPDATE content_items SET deleted_at = NULL, updated_at = unixepoch() WHERE id = ? AND deleted_at IS NOT NULL',
|
|
22
|
+
)
|
|
23
|
+
.bind(id)
|
|
24
|
+
.run()
|
|
25
|
+
return Boolean(res.meta && (res.meta as { changes?: number }).changes)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Hard delete. content_* child tables cascade via the 0001 FKs. */
|
|
29
|
+
export async function purgeContent(db: D1Database, id: string): Promise<boolean> {
|
|
30
|
+
const res = await db.prepare('DELETE FROM content_items WHERE id = ?').bind(id).run()
|
|
31
|
+
return Boolean(res.meta && (res.meta as { changes?: number }).changes)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Ids of non-trashed content (list/board exclude deleted_at IS NOT NULL). */
|
|
35
|
+
export async function listActiveIds(db: D1Database): Promise<string[]> {
|
|
36
|
+
const result = await db
|
|
37
|
+
.prepare(
|
|
38
|
+
'SELECT id FROM content_items WHERE deleted_at IS NULL ORDER BY updated_at DESC, id DESC',
|
|
39
|
+
)
|
|
40
|
+
.all<{ id: string }>()
|
|
41
|
+
return (result.results || []).map((r) => r.id)
|
|
42
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Tag CRUD engine — backs the Organize-tab tag management.
|
|
2
|
+
// setContentTags / ensureTags (auto-create on assign) stay in publisher.ts;
|
|
3
|
+
// this module adds management over content_tags + content_tag_links.
|
|
4
|
+
|
|
5
|
+
import type { D1Database } from './d1.js'
|
|
6
|
+
import { slugify } from './slug.js'
|
|
7
|
+
|
|
8
|
+
export interface TagSummary {
|
|
9
|
+
id: string
|
|
10
|
+
slug: string
|
|
11
|
+
label: string
|
|
12
|
+
count: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function getTag(db: D1Database, tagId: string): Promise<TagSummary | null> {
|
|
16
|
+
const row = await db
|
|
17
|
+
.prepare(
|
|
18
|
+
`SELECT t.id, t.slug, t.label, COUNT(l.content_id) AS count
|
|
19
|
+
FROM content_tags t
|
|
20
|
+
LEFT JOIN content_tag_links l ON l.tag_id = t.id
|
|
21
|
+
WHERE t.id = ?
|
|
22
|
+
GROUP BY t.id LIMIT 1`,
|
|
23
|
+
)
|
|
24
|
+
.bind(tagId)
|
|
25
|
+
.first<TagSummary>()
|
|
26
|
+
return row ?? null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function createTag(db: D1Database, label: string): Promise<TagSummary | null> {
|
|
30
|
+
const slug = slugify(label)
|
|
31
|
+
const trimmed = label.trim()
|
|
32
|
+
if (!slug || !trimmed) return null
|
|
33
|
+
const existing = await db
|
|
34
|
+
.prepare('SELECT id FROM content_tags WHERE slug = ? LIMIT 1')
|
|
35
|
+
.bind(slug)
|
|
36
|
+
.first<{ id: string }>()
|
|
37
|
+
if (existing) return null
|
|
38
|
+
const id = crypto.randomUUID()
|
|
39
|
+
await db
|
|
40
|
+
.prepare('INSERT INTO content_tags (id, slug, label) VALUES (?, ?, ?)')
|
|
41
|
+
.bind(id, slug, trimmed)
|
|
42
|
+
.run()
|
|
43
|
+
return { id, slug, label: trimmed, count: 0 }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function listTags(db: D1Database): Promise<TagSummary[]> {
|
|
47
|
+
const result = await db
|
|
48
|
+
.prepare(
|
|
49
|
+
`SELECT t.id, t.slug, t.label, COUNT(l.content_id) AS count
|
|
50
|
+
FROM content_tags t
|
|
51
|
+
LEFT JOIN content_tag_links l ON l.tag_id = t.id
|
|
52
|
+
GROUP BY t.id ORDER BY count DESC, t.label ASC`,
|
|
53
|
+
)
|
|
54
|
+
.all<{ id: string; slug: string; label: string; count: number }>()
|
|
55
|
+
return result.results || []
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function searchTags(db: D1Database, q: string): Promise<TagSummary[]> {
|
|
59
|
+
const needle = `${slugify(q)}%`
|
|
60
|
+
const result = await db
|
|
61
|
+
.prepare(
|
|
62
|
+
`SELECT t.id, t.slug, t.label, COUNT(l.content_id) AS count
|
|
63
|
+
FROM content_tags t
|
|
64
|
+
LEFT JOIN content_tag_links l ON l.tag_id = t.id
|
|
65
|
+
WHERE t.slug LIKE ? GROUP BY t.id ORDER BY t.label ASC LIMIT 50`,
|
|
66
|
+
)
|
|
67
|
+
.bind(needle)
|
|
68
|
+
.all<{ id: string; slug: string; label: string; count: number }>()
|
|
69
|
+
return result.results || []
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function renameTag(db: D1Database, tagId: string, label: string): Promise<boolean> {
|
|
73
|
+
const slug = slugify(label)
|
|
74
|
+
const trimmed = label.trim()
|
|
75
|
+
if (!slug || !trimmed) return false
|
|
76
|
+
// content_tags.slug is UNIQUE — a rename whose target slug is already taken by
|
|
77
|
+
// a DIFFERENT tag would throw a constraint error. Fail gracefully (the
|
|
78
|
+
// Promise<boolean> contract) instead, matching the empty-slug early return.
|
|
79
|
+
const clash = await db
|
|
80
|
+
.prepare('SELECT id FROM content_tags WHERE slug = ? AND id != ? LIMIT 1')
|
|
81
|
+
.bind(slug, tagId)
|
|
82
|
+
.first<{ id: string }>()
|
|
83
|
+
if (clash) return false
|
|
84
|
+
const res = await db
|
|
85
|
+
.prepare('UPDATE content_tags SET label = ?, slug = ? WHERE id = ?')
|
|
86
|
+
.bind(trimmed, slug, tagId)
|
|
87
|
+
.run()
|
|
88
|
+
return Boolean(res.meta && (res.meta as { changes?: number }).changes)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function deleteTag(db: D1Database, tagId: string): Promise<boolean> {
|
|
92
|
+
// content_tag_links has ON DELETE CASCADE on tag_id (0001), so links go too.
|
|
93
|
+
const res = await db.prepare('DELETE FROM content_tags WHERE id = ?').bind(tagId).run()
|
|
94
|
+
return Boolean(res.meta && (res.meta as { changes?: number }).changes)
|
|
95
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { D1Database } from './d1.js'
|
|
2
|
+
import { slugify } from './slug.js'
|
|
3
|
+
|
|
4
|
+
export interface TopicSummary {
|
|
5
|
+
slug: string
|
|
6
|
+
label: string
|
|
7
|
+
count: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isMissingTopicsTable(error: unknown): boolean {
|
|
11
|
+
return error instanceof Error && /no such table: cms_topics/i.test(error.message)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function listStoredTopics(db: D1Database): Promise<Array<{ slug: string; label: string }>> {
|
|
15
|
+
try {
|
|
16
|
+
const result = await db
|
|
17
|
+
.prepare('SELECT slug, label FROM cms_topics ORDER BY label ASC')
|
|
18
|
+
.all<{ slug: string; label: string }>()
|
|
19
|
+
return result.results || []
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (isMissingTopicsTable(error)) return []
|
|
22
|
+
throw error
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function hasStoredTopicsTable(db: D1Database): Promise<boolean> {
|
|
27
|
+
try {
|
|
28
|
+
await db.prepare('SELECT 1 FROM cms_topics LIMIT 1').first()
|
|
29
|
+
return true
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (isMissingTopicsTable(error)) return false
|
|
32
|
+
throw error
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function listTopics(db: D1Database): Promise<TopicSummary[]> {
|
|
37
|
+
const result = await db
|
|
38
|
+
.prepare(
|
|
39
|
+
`SELECT primary_topic AS slug, primary_topic AS label, COUNT(*) AS count
|
|
40
|
+
FROM content_items
|
|
41
|
+
WHERE deleted_at IS NULL
|
|
42
|
+
AND primary_topic IS NOT NULL
|
|
43
|
+
AND TRIM(primary_topic) != ''
|
|
44
|
+
GROUP BY primary_topic
|
|
45
|
+
ORDER BY count DESC, primary_topic ASC`,
|
|
46
|
+
)
|
|
47
|
+
.all<TopicSummary>()
|
|
48
|
+
const merged = new Map<string, TopicSummary>()
|
|
49
|
+
for (const row of result.results || []) {
|
|
50
|
+
merged.set(row.slug, { slug: row.slug, label: row.label, count: Number(row.count) || 0 })
|
|
51
|
+
}
|
|
52
|
+
for (const topic of await listStoredTopics(db)) {
|
|
53
|
+
const existing = merged.get(topic.slug)
|
|
54
|
+
merged.set(topic.slug, {
|
|
55
|
+
slug: topic.slug,
|
|
56
|
+
label: topic.label,
|
|
57
|
+
count: existing?.count ?? 0,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
return Array.from(merged.values()).sort(
|
|
61
|
+
(a, b) => b.count - a.count || a.label.localeCompare(b.label),
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function createTopic(db: D1Database, label: string): Promise<TopicSummary | null> {
|
|
66
|
+
const slug = slugify(label)
|
|
67
|
+
if (!slug) return null
|
|
68
|
+
if (!(await hasStoredTopicsTable(db))) return null
|
|
69
|
+
|
|
70
|
+
const assigned = await db
|
|
71
|
+
.prepare('SELECT 1 FROM content_items WHERE primary_topic = ? LIMIT 1')
|
|
72
|
+
.bind(slug)
|
|
73
|
+
.first()
|
|
74
|
+
if (assigned) return null
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await db
|
|
78
|
+
.prepare(
|
|
79
|
+
`INSERT INTO cms_topics (slug, label, created_at, updated_at)
|
|
80
|
+
VALUES (?, ?, unixepoch(), unixepoch())`,
|
|
81
|
+
)
|
|
82
|
+
.bind(slug, label.trim())
|
|
83
|
+
.run()
|
|
84
|
+
} catch {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
return { slug, label: label.trim(), count: 0 }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function renameTopic(
|
|
91
|
+
db: D1Database,
|
|
92
|
+
currentSlug: string,
|
|
93
|
+
label: string,
|
|
94
|
+
): Promise<TopicSummary | null> {
|
|
95
|
+
const nextSlug = slugify(label)
|
|
96
|
+
if (!currentSlug || !nextSlug) return null
|
|
97
|
+
const nextLabel = label.trim()
|
|
98
|
+
if (nextSlug !== currentSlug) {
|
|
99
|
+
const contentClash = await db
|
|
100
|
+
.prepare('SELECT 1 FROM content_items WHERE primary_topic = ? LIMIT 1')
|
|
101
|
+
.bind(nextSlug)
|
|
102
|
+
.first()
|
|
103
|
+
if (contentClash) return null
|
|
104
|
+
try {
|
|
105
|
+
const storedClash = await db
|
|
106
|
+
.prepare('SELECT 1 FROM cms_topics WHERE slug = ? AND slug != ? LIMIT 1')
|
|
107
|
+
.bind(nextSlug, currentSlug)
|
|
108
|
+
.first()
|
|
109
|
+
if (storedClash) return null
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (!isMissingTopicsTable(error)) throw error
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let hadStoredTopic = false
|
|
116
|
+
const storedTopicsAvailable = await hasStoredTopicsTable(db)
|
|
117
|
+
if (storedTopicsAvailable) {
|
|
118
|
+
const stored = await db
|
|
119
|
+
.prepare('SELECT 1 FROM cms_topics WHERE slug = ? LIMIT 1')
|
|
120
|
+
.bind(currentSlug)
|
|
121
|
+
.first()
|
|
122
|
+
hadStoredTopic = Boolean(stored)
|
|
123
|
+
if (hadStoredTopic) {
|
|
124
|
+
await db
|
|
125
|
+
.prepare(
|
|
126
|
+
'UPDATE cms_topics SET slug = ?, label = ?, updated_at = unixepoch() WHERE slug = ?',
|
|
127
|
+
)
|
|
128
|
+
.bind(nextSlug, nextLabel, currentSlug)
|
|
129
|
+
.run()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const res = await db
|
|
134
|
+
.prepare(
|
|
135
|
+
`UPDATE content_items
|
|
136
|
+
SET primary_topic = ?, updated_at = unixepoch()
|
|
137
|
+
WHERE deleted_at IS NULL AND primary_topic = ?`,
|
|
138
|
+
)
|
|
139
|
+
.bind(nextSlug, currentSlug)
|
|
140
|
+
.run()
|
|
141
|
+
const changes = (res.meta as { changes?: number } | undefined)?.changes ?? 0
|
|
142
|
+
if (changes === 0 && !hadStoredTopic) return null
|
|
143
|
+
if (storedTopicsAvailable && !hadStoredTopic) {
|
|
144
|
+
await db
|
|
145
|
+
.prepare(
|
|
146
|
+
`INSERT OR REPLACE INTO cms_topics (slug, label, created_at, updated_at)
|
|
147
|
+
VALUES (
|
|
148
|
+
?,
|
|
149
|
+
?,
|
|
150
|
+
COALESCE((SELECT created_at FROM cms_topics WHERE slug = ?), unixepoch()),
|
|
151
|
+
unixepoch()
|
|
152
|
+
)`,
|
|
153
|
+
)
|
|
154
|
+
.bind(nextSlug, nextLabel, nextSlug)
|
|
155
|
+
.run()
|
|
156
|
+
}
|
|
157
|
+
return { slug: nextSlug, label: nextLabel, count: changes }
|
|
158
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// url-guard.ts — SSRF guard for outbound webhook URLs.
|
|
2
|
+
//
|
|
3
|
+
// The webhook `url` registered by a workspace is later fetched by
|
|
4
|
+
// buildDefaultDispatchWebhook (POST with the signed envelope). Without
|
|
5
|
+
// validation that is an arbitrary-outbound-POST / SSRF primitive — a member
|
|
6
|
+
// could point a hook at an internal service, the cloud metadata endpoint, or a
|
|
7
|
+
// loopback admin port and have the Worker make the request from inside the
|
|
8
|
+
// trust boundary.
|
|
9
|
+
//
|
|
10
|
+
// This module is the single source of truth for "is this URL safe to register
|
|
11
|
+
// and POST to". It is enforced in TWO places:
|
|
12
|
+
// 1. the create/update route — reject with 400 so a bad URL never persists.
|
|
13
|
+
// 2. fire time (buildDefaultDispatchWebhook) — defense in depth, in case a
|
|
14
|
+
// row predates the guard or was written out-of-band.
|
|
15
|
+
//
|
|
16
|
+
// Policy: require `https:`, reject credentials-in-URL, reject hosts that resolve
|
|
17
|
+
// to (or are written as) loopback / link-local / cloud-metadata / RFC-1918
|
|
18
|
+
// private space, and reject internal-only hostname suffixes (`.internal`,
|
|
19
|
+
// `.local`) plus bare single-label hostnames (intranet-style).
|
|
20
|
+
//
|
|
21
|
+
// NOTE: this is a host-literal + name-suffix check, not a DNS resolution check.
|
|
22
|
+
// workerd has no synchronous DNS and the fetch egress is the real boundary; this
|
|
23
|
+
// blocks the obvious literal-IP and well-known-name SSRF classes. DNS-rebinding
|
|
24
|
+
// to a private IP is out of scope for a package-level guard (it requires a
|
|
25
|
+
// pinned-IP fetch the host owns).
|
|
26
|
+
|
|
27
|
+
export interface UrlGuardResult {
|
|
28
|
+
ok: boolean
|
|
29
|
+
reason?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fail(reason: string): UrlGuardResult {
|
|
33
|
+
return { ok: false, reason }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Internal-only hostname suffixes that must never receive a webhook POST. */
|
|
37
|
+
const BLOCKED_HOST_SUFFIXES = ['.internal', '.local']
|
|
38
|
+
|
|
39
|
+
/** Decimal-octet test for an IPv4 literal. */
|
|
40
|
+
function parseIpv4(host: string): [number, number, number, number] | null {
|
|
41
|
+
const parts = host.split('.')
|
|
42
|
+
if (parts.length !== 4) return null
|
|
43
|
+
const octets: number[] = []
|
|
44
|
+
for (const part of parts) {
|
|
45
|
+
if (!/^\d{1,3}$/.test(part)) return null
|
|
46
|
+
const n = Number(part)
|
|
47
|
+
if (n > 255) return null
|
|
48
|
+
octets.push(n)
|
|
49
|
+
}
|
|
50
|
+
return octets as [number, number, number, number]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** True if the IPv4 literal is in a loopback / link-local / private / unspecified range. */
|
|
54
|
+
function isBlockedIpv4(octets: [number, number, number, number]): boolean {
|
|
55
|
+
const [a, b] = octets
|
|
56
|
+
// 0.0.0.0/8 — "this host" / unspecified.
|
|
57
|
+
if (a === 0) return true
|
|
58
|
+
// 127.0.0.0/8 — loopback.
|
|
59
|
+
if (a === 127) return true
|
|
60
|
+
// 10.0.0.0/8 — private.
|
|
61
|
+
if (a === 10) return true
|
|
62
|
+
// 172.16.0.0/12 — private (172.16 – 172.31).
|
|
63
|
+
if (a === 172 && b >= 16 && b <= 31) return true
|
|
64
|
+
// 192.168.0.0/16 — private.
|
|
65
|
+
if (a === 192 && b === 168) return true
|
|
66
|
+
// 169.254.0.0/16 — link-local (incl. 169.254.169.254 cloud metadata).
|
|
67
|
+
if (a === 169 && b === 254) return true
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate a webhook URL against the SSRF policy.
|
|
73
|
+
*
|
|
74
|
+
* Returns `{ ok: true }` only for a public `https:` URL with no credentials
|
|
75
|
+
* whose host is not a loopback/link-local/private/internal target.
|
|
76
|
+
*/
|
|
77
|
+
export function validateWebhookUrl(raw: string): UrlGuardResult {
|
|
78
|
+
if (!raw || typeof raw !== 'string') return fail('url is required')
|
|
79
|
+
|
|
80
|
+
let url: URL
|
|
81
|
+
try {
|
|
82
|
+
url = new URL(raw)
|
|
83
|
+
} catch {
|
|
84
|
+
return fail('url is not a valid absolute URL')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 1. Scheme — https only (rejects http, file, gopher, ftp, data, ...).
|
|
88
|
+
if (url.protocol !== 'https:') {
|
|
89
|
+
return fail('url must use the https scheme')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 2. No credentials in the URL (user:pass@host).
|
|
93
|
+
if (url.username || url.password) {
|
|
94
|
+
return fail('url must not contain credentials')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Normalize the host: strip IPv6 brackets, lowercase, drop a trailing dot.
|
|
98
|
+
let host = url.hostname.toLowerCase()
|
|
99
|
+
if (host.endsWith('.')) host = host.slice(0, -1)
|
|
100
|
+
|
|
101
|
+
if (!host) return fail('url host is empty')
|
|
102
|
+
|
|
103
|
+
// 3. IPv6 loopback / unspecified (URL.hostname strips the brackets).
|
|
104
|
+
if (host === '::1' || host === '::' || host === '0:0:0:0:0:0:0:1') {
|
|
105
|
+
return fail('url host is a loopback address')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 4. localhost (by name).
|
|
109
|
+
if (host === 'localhost' || host.endsWith('.localhost')) {
|
|
110
|
+
return fail('url host is localhost')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 5. IPv4 literal in a blocked range.
|
|
114
|
+
const ipv4 = parseIpv4(host)
|
|
115
|
+
if (ipv4) {
|
|
116
|
+
if (isBlockedIpv4(ipv4)) {
|
|
117
|
+
return fail('url host is a private, loopback, or link-local address')
|
|
118
|
+
}
|
|
119
|
+
// A public IPv4 literal is allowed.
|
|
120
|
+
return { ok: true }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 6. Internal-only name suffixes (.internal, .local).
|
|
124
|
+
for (const suffix of BLOCKED_HOST_SUFFIXES) {
|
|
125
|
+
if (host.endsWith(suffix)) {
|
|
126
|
+
return fail(`url host suffix '${suffix}' is internal-only`)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 7. Bare single-label hostname (no dot) — intranet-style, not public DNS.
|
|
131
|
+
if (!host.includes('.')) {
|
|
132
|
+
return fail('url host must be a fully-qualified public hostname')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { ok: true }
|
|
136
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { buildProtectedMask, isProtectedSpan } from '../scan.js'
|
|
2
|
+
/**
|
|
3
|
+
* `bare-url-not-autolinked`
|
|
4
|
+
*
|
|
5
|
+
* Detect bare `https://…` URLs in prose that aren't wrapped in a
|
|
6
|
+
* markdown link, autolink, fenced code, inline code, or HTML
|
|
7
|
+
* attribute. The renderer auto-links them in newer markdown variants,
|
|
8
|
+
* but Fronts' renderer doesn't — so bare URLs ship as plain text.
|
|
9
|
+
*
|
|
10
|
+
* AST-aware via the shared {@link buildProtectedMask} helper so we
|
|
11
|
+
* don't flag URLs that live inside the protected regions.
|
|
12
|
+
*/
|
|
13
|
+
import type { ArticleBodyInput, ValidationError } from '../types.js'
|
|
14
|
+
import { makeError } from '../types.js'
|
|
15
|
+
|
|
16
|
+
// Greedy URL match — everything that isn't whitespace or a clearly
|
|
17
|
+
// terminal character. Trailing punctuation is trimmed in a second pass.
|
|
18
|
+
const BARE_URL_RE = /\bhttps?:\/\/[^\s<>"'`)\]]+/g
|
|
19
|
+
|
|
20
|
+
const TRAILING_PUNCT_RE = /[.,;:!?]+$/
|
|
21
|
+
|
|
22
|
+
interface UrlMatch {
|
|
23
|
+
start: number
|
|
24
|
+
end: number
|
|
25
|
+
url: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function findBareUrls(value: string): UrlMatch[] {
|
|
29
|
+
const out: UrlMatch[] = []
|
|
30
|
+
for (const match of value.matchAll(BARE_URL_RE)) {
|
|
31
|
+
const start = match.index ?? 0
|
|
32
|
+
let url = match[0]
|
|
33
|
+
// Trim trailing punctuation that isn't part of the URL — `.com).` etc.
|
|
34
|
+
const trimMatch = TRAILING_PUNCT_RE.exec(url)
|
|
35
|
+
if (trimMatch) {
|
|
36
|
+
url = url.slice(0, trimMatch.index)
|
|
37
|
+
}
|
|
38
|
+
out.push({ start, end: start + url.length, url })
|
|
39
|
+
}
|
|
40
|
+
return out
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function check(input: ArticleBodyInput): ValidationError[] {
|
|
44
|
+
if (!input.body_markdown) return []
|
|
45
|
+
const source = input.body_markdown
|
|
46
|
+
const mask = buildProtectedMask(source)
|
|
47
|
+
|
|
48
|
+
for (const m of findBareUrls(source)) {
|
|
49
|
+
if (isProtectedSpan(mask, m.start, m.end)) continue
|
|
50
|
+
return [
|
|
51
|
+
makeError(
|
|
52
|
+
'bare-url-not-autolinked',
|
|
53
|
+
`Bare URL '${m.url}' is not wrapped in a markdown link. Wrap as '[label](${m.url})' or '<${m.url}>'.`,
|
|
54
|
+
{ start: m.start, end: m.end },
|
|
55
|
+
),
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
return []
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Pure repair: wrap bare URLs in `<url>` autolinks. Only used when the
|
|
63
|
+
* operator opts in (some bodies intentionally print URLs as text in
|
|
64
|
+
* citation footnotes — the wrap changes how they render).
|
|
65
|
+
*/
|
|
66
|
+
export function repair(value: string): string {
|
|
67
|
+
const mask = buildProtectedMask(value)
|
|
68
|
+
let out = ''
|
|
69
|
+
let cursor = 0
|
|
70
|
+
for (const m of findBareUrls(value)) {
|
|
71
|
+
if (isProtectedSpan(mask, m.start, m.end)) continue
|
|
72
|
+
out += value.slice(cursor, m.start)
|
|
73
|
+
out += `<${m.url}>`
|
|
74
|
+
cursor = m.end
|
|
75
|
+
}
|
|
76
|
+
out += value.slice(cursor)
|
|
77
|
+
return out
|
|
78
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `broken-footnote-label`
|
|
3
|
+
*
|
|
4
|
+
* After Phase 1's `stripOverEscapes()` pass, legacy imports leave
|
|
5
|
+
* footnote labels as literal `[[1]](#_ftnref1)` — a double-bracketed
|
|
6
|
+
* link whose label is also bracketed. The markdown parser sees an
|
|
7
|
+
* empty alt-text "image" or simply leaks the literal brackets.
|
|
8
|
+
*
|
|
9
|
+
* Repair: collapse `[[N]](#anchor)` → `[N](#anchor)` so the label
|
|
10
|
+
* renders as the numeric footnote marker `[1]`, styled as a real link.
|
|
11
|
+
*/
|
|
12
|
+
import type { ArticleBodyInput, ValidationError } from '../types.js'
|
|
13
|
+
import { makeError } from '../types.js'
|
|
14
|
+
|
|
15
|
+
const PATTERN = /\[\[(\d+)\]\]\((#[^)\s]+)\)/
|
|
16
|
+
|
|
17
|
+
export function check(input: ArticleBodyInput): ValidationError[] {
|
|
18
|
+
if (!input.body_markdown) return []
|
|
19
|
+
const match = PATTERN.exec(input.body_markdown)
|
|
20
|
+
if (!match) return []
|
|
21
|
+
return [
|
|
22
|
+
makeError(
|
|
23
|
+
'broken-footnote-label',
|
|
24
|
+
`body_markdown contains a broken footnote label '${match[0]}' — collapse '[[N]](#…)' to '[N](#…)' so the link renders correctly.`,
|
|
25
|
+
{ start: match.index, end: match.index + match[0].length },
|
|
26
|
+
),
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Pure repair: collapse `[[N]](#anchor)` → `[N](#anchor)`. */
|
|
31
|
+
export function repair(value: string): string {
|
|
32
|
+
return value.replace(/\[\[(\d+)\]\]\((#[^)\s]+)\)/g, '[$1]($2)')
|
|
33
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `double-encoded-entities`
|
|
3
|
+
*
|
|
4
|
+
* Detect `&quot;` `&#39;` `&amp;` `&#x2019;` — the
|
|
5
|
+
* result of HTML-encoding an already-encoded string. The renderer
|
|
6
|
+
* decodes one level, so the reader sees `"` `'` etc. in the
|
|
7
|
+
* prose.
|
|
8
|
+
*
|
|
9
|
+
* Repair: decode one level (`&quot;` → `"`) so the next
|
|
10
|
+
* render pass produces the correct character. We do NOT decode all the
|
|
11
|
+
* way to a raw character — that would change semantics inside HTML
|
|
12
|
+
* attributes where the encoded form is still required.
|
|
13
|
+
*/
|
|
14
|
+
import type { ArticleBodyInput, ValidationError } from '../types.js'
|
|
15
|
+
import { makeError } from '../types.js'
|
|
16
|
+
|
|
17
|
+
// Cover named entities and both decimal and hex numeric references.
|
|
18
|
+
const PATTERN = /&(quot|amp|lt|gt|apos|nbsp|#\d+|#x[0-9a-fA-F]+);/i
|
|
19
|
+
|
|
20
|
+
const PATTERN_GLOBAL = /&(quot|amp|lt|gt|apos|nbsp|#\d+|#x[0-9a-fA-F]+);/gi
|
|
21
|
+
|
|
22
|
+
export function check(input: ArticleBodyInput): ValidationError[] {
|
|
23
|
+
const errors: ValidationError[] = []
|
|
24
|
+
|
|
25
|
+
const checkField = (value: string | null, fieldName: string) => {
|
|
26
|
+
if (!value) return
|
|
27
|
+
const match = PATTERN.exec(value)
|
|
28
|
+
if (!match) return
|
|
29
|
+
errors.push(
|
|
30
|
+
makeError(
|
|
31
|
+
'double-encoded-entities',
|
|
32
|
+
`${fieldName} contains a double-encoded entity '${match[0]}'. Decode one level so it renders as the intended character.`,
|
|
33
|
+
{ start: match.index, end: match.index + match[0].length },
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
checkField(input.body_markdown, 'body_markdown')
|
|
39
|
+
checkField(input.body_html, 'body_html')
|
|
40
|
+
return errors
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Pure repair: decode the `&` prefix one level. */
|
|
44
|
+
export function repair(value: string): string {
|
|
45
|
+
return value.replace(PATTERN_GLOBAL, (_match, inner: string) => `&${inner};`)
|
|
46
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `empty-alt-text`
|
|
3
|
+
*
|
|
4
|
+
* Detect markdown images stored with empty alt text: ``.
|
|
5
|
+
* This is an accessibility regression — screen readers announce the
|
|
6
|
+
* filename or "image" — and it usually means the import dropped the
|
|
7
|
+
* caption rather than the editor making a deliberate decorative
|
|
8
|
+
* choice. Flag-only; auto-fix would require fabricating alt text.
|
|
9
|
+
*/
|
|
10
|
+
import type { ArticleBodyInput, ValidationError } from '../types.js'
|
|
11
|
+
import { makeError } from '../types.js'
|
|
12
|
+
|
|
13
|
+
const PATTERN = /!\[\]\((https?:\/\/[^)\s]+)\)/
|
|
14
|
+
const PATTERN_GLOBAL = /!\[\]\((https?:\/\/[^)\s]+)\)/g
|
|
15
|
+
|
|
16
|
+
export function check(input: ArticleBodyInput): ValidationError[] {
|
|
17
|
+
if (!input.body_markdown) return []
|
|
18
|
+
const match = PATTERN.exec(input.body_markdown)
|
|
19
|
+
if (!match) return []
|
|
20
|
+
return [
|
|
21
|
+
makeError(
|
|
22
|
+
'empty-alt-text',
|
|
23
|
+
`Image '${match[1]}' has empty alt text. Add descriptive alt, or wrap as a decorative image deliberately.`,
|
|
24
|
+
{ start: match.index, end: match.index + match[0].length },
|
|
25
|
+
),
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Count occurrences — used by sweep reporting. */
|
|
30
|
+
export function count(value: string | null): number {
|
|
31
|
+
if (!value) return 0
|
|
32
|
+
let n = 0
|
|
33
|
+
for (const _ of value.matchAll(PATTERN_GLOBAL)) n += 1
|
|
34
|
+
return n
|
|
35
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `heading-hierarchy-skip`
|
|
3
|
+
*
|
|
4
|
+
* Detect headings that skip levels — e.g. an `<h1>` followed directly
|
|
5
|
+
* by an `<h3>` with no `<h2>` between them. This breaks the TOC tree
|
|
6
|
+
* and screen-reader navigation. Skip auto-fix — promoting an `h3` to
|
|
7
|
+
* `h2` is semantic, not mechanical.
|
|
8
|
+
*/
|
|
9
|
+
import type { ArticleBodyInput, ValidationError } from '../types.js'
|
|
10
|
+
import { makeError } from '../types.js'
|
|
11
|
+
|
|
12
|
+
const HEADING_RE = /^(#{1,6})\s+\S/gm
|
|
13
|
+
|
|
14
|
+
export function check(input: ArticleBodyInput): ValidationError[] {
|
|
15
|
+
if (!input.body_markdown) return []
|
|
16
|
+
const source = input.body_markdown
|
|
17
|
+
let lastLevel = 0
|
|
18
|
+
for (const match of source.matchAll(HEADING_RE)) {
|
|
19
|
+
const level = (match[1] ?? '').length
|
|
20
|
+
if (lastLevel > 0 && level > lastLevel + 1) {
|
|
21
|
+
const start = match.index ?? 0
|
|
22
|
+
return [
|
|
23
|
+
makeError(
|
|
24
|
+
'heading-hierarchy-skip',
|
|
25
|
+
`Heading '${match[0].trim()}' skips from h${lastLevel} to h${level}. Insert intermediate level(s) to keep the TOC tree intact.`,
|
|
26
|
+
{ start, end: start + match[0].length },
|
|
27
|
+
),
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
lastLevel = level
|
|
31
|
+
}
|
|
32
|
+
return []
|
|
33
|
+
}
|