emdash 0.0.0-b → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -43
- package/dist/adapters-BLMa4JGD.d.mts +106 -0
- package/dist/adapters-BLMa4JGD.d.mts.map +1 -0
- package/dist/apply-Bjfq_b4-.mjs +1293 -0
- package/dist/apply-Bjfq_b4-.mjs.map +1 -0
- package/dist/astro/index.d.mts +51 -0
- package/dist/astro/index.d.mts.map +1 -0
- package/dist/astro/index.mjs +1336 -0
- package/dist/astro/index.mjs.map +1 -0
- package/dist/astro/middleware/auth.d.mts +31 -0
- package/dist/astro/middleware/auth.d.mts.map +1 -0
- package/dist/astro/middleware/auth.mjs +654 -0
- package/dist/astro/middleware/auth.mjs.map +1 -0
- package/dist/astro/middleware/redirect.d.mts +22 -0
- package/dist/astro/middleware/redirect.d.mts.map +1 -0
- package/dist/astro/middleware/redirect.mjs +63 -0
- package/dist/astro/middleware/redirect.mjs.map +1 -0
- package/dist/astro/middleware/request-context.d.mts +18 -0
- package/dist/astro/middleware/request-context.d.mts.map +1 -0
- package/dist/astro/middleware/request-context.mjs +1310 -0
- package/dist/astro/middleware/request-context.mjs.map +1 -0
- package/dist/astro/middleware/setup.d.mts +20 -0
- package/dist/astro/middleware/setup.d.mts.map +1 -0
- package/dist/astro/middleware/setup.mjs +47 -0
- package/dist/astro/middleware/setup.mjs.map +1 -0
- package/dist/astro/middleware.d.mts +13 -0
- package/dist/astro/middleware.d.mts.map +1 -0
- package/dist/astro/middleware.mjs +1613 -0
- package/dist/astro/middleware.mjs.map +1 -0
- package/dist/astro/types.d.mts +250 -0
- package/dist/astro/types.d.mts.map +1 -0
- package/dist/astro/types.mjs +1 -0
- package/dist/base64-MBPo9ozB.mjs +59 -0
- package/dist/base64-MBPo9ozB.mjs.map +1 -0
- package/dist/byline-CL847F26.mjs +213 -0
- package/dist/byline-CL847F26.mjs.map +1 -0
- package/dist/bylines-C2a-2TGt.mjs +136 -0
- package/dist/bylines-C2a-2TGt.mjs.map +1 -0
- package/dist/chunk-ClPoSABd.mjs +21 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +3909 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/client/cf-access.d.mts +60 -0
- package/dist/client/cf-access.d.mts.map +1 -0
- package/dist/client/cf-access.mjs +179 -0
- package/dist/client/cf-access.mjs.map +1 -0
- package/dist/client/index.d.mts +398 -0
- package/dist/client/index.d.mts.map +1 -0
- package/dist/client/index.mjs +346 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/config-CKE8p9xM.mjs +55 -0
- package/dist/config-CKE8p9xM.mjs.map +1 -0
- package/dist/connection-B4zVnQIa.mjs +40 -0
- package/dist/connection-B4zVnQIa.mjs.map +1 -0
- package/dist/content-D6C2WsZC.mjs +824 -0
- package/dist/content-D6C2WsZC.mjs.map +1 -0
- package/dist/db/index.d.mts +4 -0
- package/dist/db/index.mjs +62 -0
- package/dist/db/index.mjs.map +1 -0
- package/dist/db/libsql.d.mts +11 -0
- package/dist/db/libsql.d.mts.map +1 -0
- package/dist/db/libsql.mjs +17 -0
- package/dist/db/libsql.mjs.map +1 -0
- package/dist/db/postgres.d.mts +11 -0
- package/dist/db/postgres.d.mts.map +1 -0
- package/dist/db/postgres.mjs +30 -0
- package/dist/db/postgres.mjs.map +1 -0
- package/dist/db/sqlite.d.mts +11 -0
- package/dist/db/sqlite.d.mts.map +1 -0
- package/dist/db/sqlite.mjs +16 -0
- package/dist/db/sqlite.mjs.map +1 -0
- package/dist/default-Cyi4aAxu.mjs +81 -0
- package/dist/default-Cyi4aAxu.mjs.map +1 -0
- package/dist/dialect-helpers-B9uSp2GJ.mjs +90 -0
- package/dist/dialect-helpers-B9uSp2GJ.mjs.map +1 -0
- package/dist/error-Cxz0tQeO.mjs +27 -0
- package/dist/error-Cxz0tQeO.mjs.map +1 -0
- package/dist/index-C1xF3OGh.d.mts +4527 -0
- package/dist/index-C1xF3OGh.d.mts.map +1 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +30 -0
- package/dist/load-yOOlckBj.mjs +28 -0
- package/dist/load-yOOlckBj.mjs.map +1 -0
- package/dist/loader-fz8Q_3EO.mjs +447 -0
- package/dist/loader-fz8Q_3EO.mjs.map +1 -0
- package/dist/manifest-schema-Dcl0R6nM.mjs +184 -0
- package/dist/manifest-schema-Dcl0R6nM.mjs.map +1 -0
- package/dist/media/index.d.mts +26 -0
- package/dist/media/index.d.mts.map +1 -0
- package/dist/media/index.mjs +55 -0
- package/dist/media/index.mjs.map +1 -0
- package/dist/media/local-runtime.d.mts +39 -0
- package/dist/media/local-runtime.d.mts.map +1 -0
- package/dist/media/local-runtime.mjs +133 -0
- package/dist/media/local-runtime.mjs.map +1 -0
- package/dist/media-DqHVh136.mjs +200 -0
- package/dist/media-DqHVh136.mjs.map +1 -0
- package/dist/mode-C2EzN1uE.mjs +23 -0
- package/dist/mode-C2EzN1uE.mjs.map +1 -0
- package/dist/page/index.d.mts +140 -0
- package/dist/page/index.d.mts.map +1 -0
- package/dist/page/index.mjs +416 -0
- package/dist/page/index.mjs.map +1 -0
- package/dist/placeholder-CmGAmqeO.d.mts +276 -0
- package/dist/placeholder-CmGAmqeO.d.mts.map +1 -0
- package/dist/placeholder-SmpOx-_v.mjs +243 -0
- package/dist/placeholder-SmpOx-_v.mjs.map +1 -0
- package/dist/plugin-utils.d.mts +58 -0
- package/dist/plugin-utils.d.mts.map +1 -0
- package/dist/plugin-utils.mjs +78 -0
- package/dist/plugin-utils.mjs.map +1 -0
- package/dist/plugins/adapt-sandbox-entry.d.mts +22 -0
- package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -0
- package/dist/plugins/adapt-sandbox-entry.mjs +113 -0
- package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -0
- package/dist/query-CS_iSj34.mjs +460 -0
- package/dist/query-CS_iSj34.mjs.map +1 -0
- package/dist/redirect-DIfIni3r.mjs +329 -0
- package/dist/redirect-DIfIni3r.mjs.map +1 -0
- package/dist/registry-D_w5HW4G.mjs +863 -0
- package/dist/registry-D_w5HW4G.mjs.map +1 -0
- package/dist/request-context.d.mts +49 -0
- package/dist/request-context.d.mts.map +1 -0
- package/dist/request-context.mjs +43 -0
- package/dist/request-context.mjs.map +1 -0
- package/dist/runner-C0hCbYnD.mjs +1412 -0
- package/dist/runner-C0hCbYnD.mjs.map +1 -0
- package/dist/runner-EAtf0ZIe.d.mts +27 -0
- package/dist/runner-EAtf0ZIe.d.mts.map +1 -0
- package/dist/runtime.d.mts +26 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +42 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/search-DG603UrT.mjs +9211 -0
- package/dist/search-DG603UrT.mjs.map +1 -0
- package/dist/seed/index.d.mts +3 -0
- package/dist/seed/index.mjs +15 -0
- package/dist/seo/index.d.mts +70 -0
- package/dist/seo/index.d.mts.map +1 -0
- package/dist/seo/index.mjs +70 -0
- package/dist/seo/index.mjs.map +1 -0
- package/dist/storage/local.d.mts +39 -0
- package/dist/storage/local.d.mts.map +1 -0
- package/dist/storage/local.mjs +166 -0
- package/dist/storage/local.mjs.map +1 -0
- package/dist/storage/s3.d.mts +32 -0
- package/dist/storage/s3.d.mts.map +1 -0
- package/dist/storage/s3.mjs +175 -0
- package/dist/storage/s3.mjs.map +1 -0
- package/dist/tokens-DpgrkrXK.mjs +171 -0
- package/dist/tokens-DpgrkrXK.mjs.map +1 -0
- package/dist/transport-BFGblqwG.d.mts +42 -0
- package/dist/transport-BFGblqwG.d.mts.map +1 -0
- package/dist/transport-yxiQsi8I.mjs +418 -0
- package/dist/transport-yxiQsi8I.mjs.map +1 -0
- package/dist/types-BRuPJGdV.d.mts +102 -0
- package/dist/types-BRuPJGdV.d.mts.map +1 -0
- package/dist/types-C4-fAxN3.d.mts +182 -0
- package/dist/types-C4-fAxN3.d.mts.map +1 -0
- package/dist/types-CMMN0pNg.mjs +31 -0
- package/dist/types-CMMN0pNg.mjs.map +1 -0
- package/dist/types-CUBbjgmP.mjs +16 -0
- package/dist/types-CUBbjgmP.mjs.map +1 -0
- package/dist/types-DRjfYOEv.d.mts +426 -0
- package/dist/types-DRjfYOEv.d.mts.map +1 -0
- package/dist/types-DY5zk5HN.mjs +73 -0
- package/dist/types-DY5zk5HN.mjs.map +1 -0
- package/dist/types-DaNLHo_T.d.mts +184 -0
- package/dist/types-DaNLHo_T.d.mts.map +1 -0
- package/dist/types-DvhsUmSJ.d.mts +1111 -0
- package/dist/types-DvhsUmSJ.d.mts.map +1 -0
- package/dist/validate-CpBtVMsD.d.mts +378 -0
- package/dist/validate-CpBtVMsD.d.mts.map +1 -0
- package/dist/validate-CqRJb_xU.mjs +97 -0
- package/dist/validate-CqRJb_xU.mjs.map +1 -0
- package/dist/validate-O7PWmlnq.mjs +328 -0
- package/dist/validate-O7PWmlnq.mjs.map +1 -0
- package/locals.d.ts +46 -0
- package/package.json +233 -19
- package/src/api/authorize.ts +63 -0
- package/src/api/csrf.ts +48 -0
- package/src/api/error.ts +99 -0
- package/src/api/errors.ts +445 -0
- package/src/api/escape.ts +9 -0
- package/src/api/handlers/api-tokens.ts +240 -0
- package/src/api/handlers/comments.ts +314 -0
- package/src/api/handlers/content.ts +1315 -0
- package/src/api/handlers/dashboard.ts +205 -0
- package/src/api/handlers/device-flow.ts +684 -0
- package/src/api/handlers/index.ts +163 -0
- package/src/api/handlers/manifest.ts +158 -0
- package/src/api/handlers/marketplace.ts +930 -0
- package/src/api/handlers/media.ts +207 -0
- package/src/api/handlers/menus.ts +493 -0
- package/src/api/handlers/oauth-authorization.ts +429 -0
- package/src/api/handlers/oauth-clients.ts +349 -0
- package/src/api/handlers/oauth-user-lookup.ts +39 -0
- package/src/api/handlers/plugins.ts +254 -0
- package/src/api/handlers/redirects.ts +360 -0
- package/src/api/handlers/revision.ts +145 -0
- package/src/api/handlers/schema.ts +534 -0
- package/src/api/handlers/sections.ts +289 -0
- package/src/api/handlers/seo.ts +115 -0
- package/src/api/handlers/settings.ts +49 -0
- package/src/api/handlers/snapshot.ts +350 -0
- package/src/api/handlers/taxonomies.ts +523 -0
- package/src/api/index.ts +6 -0
- package/src/api/openapi/document.ts +2368 -0
- package/src/api/openapi/index.ts +1 -0
- package/src/api/parse.ts +139 -0
- package/src/api/redirect.ts +14 -0
- package/src/api/rev.ts +67 -0
- package/src/api/schemas/auth.ts +112 -0
- package/src/api/schemas/bylines.ts +85 -0
- package/src/api/schemas/comments.ts +117 -0
- package/src/api/schemas/common.ts +89 -0
- package/src/api/schemas/content.ts +191 -0
- package/src/api/schemas/import.ts +52 -0
- package/src/api/schemas/index.ts +17 -0
- package/src/api/schemas/media.ts +116 -0
- package/src/api/schemas/menus.ts +111 -0
- package/src/api/schemas/redirects.ts +155 -0
- package/src/api/schemas/schema.ts +203 -0
- package/src/api/schemas/search.ts +63 -0
- package/src/api/schemas/sections.ts +67 -0
- package/src/api/schemas/settings.ts +63 -0
- package/src/api/schemas/setup.ts +37 -0
- package/src/api/schemas/taxonomies.ts +113 -0
- package/src/api/schemas/users.ts +96 -0
- package/src/api/schemas/widgets.ts +80 -0
- package/src/api/site-url.ts +25 -0
- package/src/api/types.ts +82 -0
- package/src/astro/index.ts +27 -0
- package/src/astro/integration/index.ts +303 -0
- package/src/astro/integration/routes.ts +834 -0
- package/src/astro/integration/runtime.ts +338 -0
- package/src/astro/integration/virtual-modules.ts +469 -0
- package/src/astro/integration/vite-config.ts +335 -0
- package/src/astro/middleware/auth.ts +743 -0
- package/src/astro/middleware/redirect.ts +89 -0
- package/src/astro/middleware/request-context.ts +129 -0
- package/src/astro/middleware/setup.ts +89 -0
- package/src/astro/middleware.ts +398 -0
- package/src/astro/routes/PluginRegistry.tsx +15 -0
- package/src/astro/routes/admin.astro +81 -0
- package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
- package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
- package/src/astro/routes/api/admin/api-tokens/[id].ts +40 -0
- package/src/astro/routes/api/admin/api-tokens/index.ts +68 -0
- package/src/astro/routes/api/admin/bylines/[id]/index.ts +87 -0
- package/src/astro/routes/api/admin/bylines/index.ts +72 -0
- package/src/astro/routes/api/admin/comments/[id]/status.ts +116 -0
- package/src/astro/routes/api/admin/comments/[id].ts +64 -0
- package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
- package/src/astro/routes/api/admin/comments/counts.ts +30 -0
- package/src/astro/routes/api/admin/comments/index.ts +46 -0
- package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +91 -0
- package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
- package/src/astro/routes/api/admin/oauth-clients/[id].ts +110 -0
- package/src/astro/routes/api/admin/oauth-clients/index.ts +71 -0
- package/src/astro/routes/api/admin/plugins/[id]/disable.ts +39 -0
- package/src/astro/routes/api/admin/plugins/[id]/enable.ts +39 -0
- package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +48 -0
- package/src/astro/routes/api/admin/plugins/[id]/update.ts +59 -0
- package/src/astro/routes/api/admin/plugins/index.ts +32 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +61 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +62 -0
- package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +61 -0
- package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
- package/src/astro/routes/api/admin/users/[id]/disable.ts +69 -0
- package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
- package/src/astro/routes/api/admin/users/[id]/index.ts +146 -0
- package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
- package/src/astro/routes/api/admin/users/index.ts +66 -0
- package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
- package/src/astro/routes/api/auth/invite/accept.ts +52 -0
- package/src/astro/routes/api/auth/invite/complete.ts +84 -0
- package/src/astro/routes/api/auth/invite/index.ts +99 -0
- package/src/astro/routes/api/auth/logout.ts +40 -0
- package/src/astro/routes/api/auth/magic-link/send.ts +89 -0
- package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
- package/src/astro/routes/api/auth/me.ts +60 -0
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +219 -0
- package/src/astro/routes/api/auth/oauth/[provider].ts +119 -0
- package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
- package/src/astro/routes/api/auth/passkey/index.ts +54 -0
- package/src/astro/routes/api/auth/passkey/options.ts +82 -0
- package/src/astro/routes/api/auth/passkey/register/options.ts +86 -0
- package/src/astro/routes/api/auth/passkey/register/verify.ts +115 -0
- package/src/astro/routes/api/auth/passkey/verify.ts +66 -0
- package/src/astro/routes/api/auth/signup/complete.ts +85 -0
- package/src/astro/routes/api/auth/signup/request.ts +77 -0
- package/src/astro/routes/api/auth/signup/verify.ts +53 -0
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +312 -0
- package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +54 -0
- package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +61 -0
- package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +33 -0
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +56 -0
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +54 -0
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +101 -0
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +140 -0
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +30 -0
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +56 -0
- package/src/astro/routes/api/content/[collection]/[id].ts +137 -0
- package/src/astro/routes/api/content/[collection]/index.ts +59 -0
- package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
- package/src/astro/routes/api/dashboard.ts +32 -0
- package/src/astro/routes/api/dev/emails.ts +36 -0
- package/src/astro/routes/api/import/probe.ts +47 -0
- package/src/astro/routes/api/import/wordpress/analyze.ts +510 -0
- package/src/astro/routes/api/import/wordpress/execute.ts +283 -0
- package/src/astro/routes/api/import/wordpress/media.ts +338 -0
- package/src/astro/routes/api/import/wordpress/prepare.ts +181 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +393 -0
- package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
- package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +347 -0
- package/src/astro/routes/api/manifest.ts +62 -0
- package/src/astro/routes/api/mcp.ts +124 -0
- package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
- package/src/astro/routes/api/media/[id].ts +145 -0
- package/src/astro/routes/api/media/file/[key].ts +79 -0
- package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +86 -0
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
- package/src/astro/routes/api/media/providers/index.ts +30 -0
- package/src/astro/routes/api/media/upload-url.ts +137 -0
- package/src/astro/routes/api/media.ts +190 -0
- package/src/astro/routes/api/menus/[name]/items.ts +87 -0
- package/src/astro/routes/api/menus/[name]/reorder.ts +33 -0
- package/src/astro/routes/api/menus/[name].ts +65 -0
- package/src/astro/routes/api/menus/index.ts +47 -0
- package/src/astro/routes/api/oauth/authorize.ts +412 -0
- package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
- package/src/astro/routes/api/oauth/device/code.ts +51 -0
- package/src/astro/routes/api/oauth/device/token.ts +69 -0
- package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
- package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
- package/src/astro/routes/api/oauth/token.ts +184 -0
- package/src/astro/routes/api/openapi.json.ts +32 -0
- package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +92 -0
- package/src/astro/routes/api/redirects/404s/index.ts +72 -0
- package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
- package/src/astro/routes/api/redirects/[id].ts +84 -0
- package/src/astro/routes/api/redirects/index.ts +52 -0
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
- package/src/astro/routes/api/revisions/[revisionId]/restore.ts +58 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +76 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +52 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +32 -0
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +80 -0
- package/src/astro/routes/api/schema/collections/index.ts +47 -0
- package/src/astro/routes/api/schema/index.ts +109 -0
- package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
- package/src/astro/routes/api/schema/orphans/index.ts +26 -0
- package/src/astro/routes/api/search/enable.ts +64 -0
- package/src/astro/routes/api/search/index.ts +55 -0
- package/src/astro/routes/api/search/rebuild.ts +72 -0
- package/src/astro/routes/api/search/stats.ts +35 -0
- package/src/astro/routes/api/search/suggest.ts +53 -0
- package/src/astro/routes/api/sections/[slug].ts +84 -0
- package/src/astro/routes/api/sections/index.ts +52 -0
- package/src/astro/routes/api/settings/email.ts +150 -0
- package/src/astro/routes/api/settings.ts +67 -0
- package/src/astro/routes/api/setup/admin-verify.ts +100 -0
- package/src/astro/routes/api/setup/admin.ts +94 -0
- package/src/astro/routes/api/setup/dev-bypass.ts +199 -0
- package/src/astro/routes/api/setup/dev-reset.ts +40 -0
- package/src/astro/routes/api/setup/index.ts +126 -0
- package/src/astro/routes/api/setup/status.ts +122 -0
- package/src/astro/routes/api/snapshot.ts +75 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +95 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +69 -0
- package/src/astro/routes/api/taxonomies/index.ts +59 -0
- package/src/astro/routes/api/themes/preview.ts +77 -0
- package/src/astro/routes/api/typegen.ts +114 -0
- package/src/astro/routes/api/well-known/auth.ts +68 -0
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +44 -0
- package/src/astro/routes/api/well-known/oauth-protected-resource.ts +37 -0
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +68 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +127 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +80 -0
- package/src/astro/routes/api/widget-areas/[name].ts +87 -0
- package/src/astro/routes/api/widget-areas/index.ts +99 -0
- package/src/astro/routes/api/widget-components.ts +22 -0
- package/src/astro/routes/robots.txt.ts +77 -0
- package/src/astro/routes/sitemap.xml.ts +97 -0
- package/src/astro/storage/adapters.ts +74 -0
- package/src/astro/storage/index.ts +19 -0
- package/src/astro/storage/types.ts +60 -0
- package/src/astro/types.ts +346 -0
- package/src/auth/api-tokens.ts +25 -0
- package/src/auth/challenge-store.ts +80 -0
- package/src/auth/mode.ts +96 -0
- package/src/auth/oauth-state-store.ts +96 -0
- package/src/auth/passkey-config.ts +27 -0
- package/src/auth/rate-limit.ts +158 -0
- package/src/auth/scopes.ts +33 -0
- package/src/auth/types.ts +104 -0
- package/src/aws-sdk.d.ts +100 -0
- package/src/bylines/index.ts +237 -0
- package/src/cleanup.ts +153 -0
- package/src/cli/client-factory.ts +100 -0
- package/src/cli/commands/auth.ts +46 -0
- package/src/cli/commands/bundle-utils.ts +247 -0
- package/src/cli/commands/bundle.ts +609 -0
- package/src/cli/commands/content.ts +442 -0
- package/src/cli/commands/dev.ts +191 -0
- package/src/cli/commands/doctor.ts +211 -0
- package/src/cli/commands/export-seed.ts +630 -0
- package/src/cli/commands/import/wordpress.ts +1056 -0
- package/src/cli/commands/init.ts +192 -0
- package/src/cli/commands/login.ts +547 -0
- package/src/cli/commands/media.ts +165 -0
- package/src/cli/commands/menu.ts +67 -0
- package/src/cli/commands/plugin-init.ts +291 -0
- package/src/cli/commands/plugin-validate.ts +31 -0
- package/src/cli/commands/plugin.ts +33 -0
- package/src/cli/commands/publish.ts +697 -0
- package/src/cli/commands/schema.ts +233 -0
- package/src/cli/commands/search-cmd.ts +54 -0
- package/src/cli/commands/seed.ts +286 -0
- package/src/cli/commands/taxonomy.ts +128 -0
- package/src/cli/commands/types.ts +68 -0
- package/src/cli/credentials.ts +236 -0
- package/src/cli/index.ts +70 -0
- package/src/cli/output.ts +75 -0
- package/src/cli/wxr/parser.ts +969 -0
- package/src/client/cf-access.ts +193 -0
- package/src/client/index.ts +854 -0
- package/src/client/portable-text.ts +413 -0
- package/src/client/transport.ts +200 -0
- package/src/comments/moderator.ts +46 -0
- package/src/comments/notifications.ts +144 -0
- package/src/comments/query.ts +105 -0
- package/src/comments/service.ts +213 -0
- package/src/components/Break.astro +45 -0
- package/src/components/Button.astro +71 -0
- package/src/components/Buttons.astro +49 -0
- package/src/components/Code.astro +59 -0
- package/src/components/Columns.astro +59 -0
- package/src/components/CommentForm.astro +315 -0
- package/src/components/Comments.astro +232 -0
- package/src/components/Cover.astro +128 -0
- package/src/components/EmDashBodyEnd.astro +32 -0
- package/src/components/EmDashBodyStart.astro +32 -0
- package/src/components/EmDashHead.astro +53 -0
- package/src/components/EmDashImage.astro +178 -0
- package/src/components/EmDashMedia.astro +167 -0
- package/src/components/Embed.astro +128 -0
- package/src/components/File.astro +122 -0
- package/src/components/Gallery.astro +93 -0
- package/src/components/HtmlBlock.astro +33 -0
- package/src/components/Image.astro +178 -0
- package/src/components/InlineEditor.astro +27 -0
- package/src/components/InlinePortableTextEditor.tsx +1905 -0
- package/src/components/LiveSearch.astro +614 -0
- package/src/components/PortableText.astro +51 -0
- package/src/components/Pullquote.astro +51 -0
- package/src/components/Table.astro +108 -0
- package/src/components/WidgetArea.astro +22 -0
- package/src/components/WidgetRenderer.astro +72 -0
- package/src/components/index.ts +116 -0
- package/src/components/marks/Link.astro +31 -0
- package/src/components/marks/StrikeThrough.astro +7 -0
- package/src/components/marks/Subscript.astro +7 -0
- package/src/components/marks/Superscript.astro +7 -0
- package/src/components/marks/Underline.astro +7 -0
- package/src/components/widgets/Archives.astro +65 -0
- package/src/components/widgets/Categories.astro +35 -0
- package/src/components/widgets/RecentPosts.astro +51 -0
- package/src/components/widgets/Search.astro +18 -0
- package/src/components/widgets/Tags.astro +38 -0
- package/src/content/converters/index.ts +9 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +385 -0
- package/src/content/converters/prosemirror-to-portable-text.ts +413 -0
- package/src/content/converters/types.ts +120 -0
- package/src/content/index.ts +5 -0
- package/src/database/connection.ts +67 -0
- package/src/database/dialect-helpers.ts +138 -0
- package/src/database/index.ts +5 -0
- package/src/database/migrations/001_initial.ts +170 -0
- package/src/database/migrations/002_media_status.ts +26 -0
- package/src/database/migrations/003_schema_registry.ts +79 -0
- package/src/database/migrations/004_plugins.ts +62 -0
- package/src/database/migrations/005_menus.ts +67 -0
- package/src/database/migrations/006_taxonomy_defs.ts +51 -0
- package/src/database/migrations/007_widgets.ts +42 -0
- package/src/database/migrations/008_auth.ts +194 -0
- package/src/database/migrations/009_user_disabled.ts +27 -0
- package/src/database/migrations/011_sections.ts +65 -0
- package/src/database/migrations/012_search.ts +25 -0
- package/src/database/migrations/013_scheduled_publishing.ts +51 -0
- package/src/database/migrations/014_draft_revisions.ts +72 -0
- package/src/database/migrations/015_indexes.ts +82 -0
- package/src/database/migrations/016_api_tokens.ts +89 -0
- package/src/database/migrations/017_authorization_codes.ts +45 -0
- package/src/database/migrations/018_seo.ts +56 -0
- package/src/database/migrations/019_i18n.ts +618 -0
- package/src/database/migrations/020_collection_url_pattern.ts +23 -0
- package/src/database/migrations/021_remove_section_categories.ts +43 -0
- package/src/database/migrations/022_marketplace_plugin_state.ts +46 -0
- package/src/database/migrations/023_plugin_metadata.ts +33 -0
- package/src/database/migrations/024_media_placeholders.ts +32 -0
- package/src/database/migrations/025_oauth_clients.ts +28 -0
- package/src/database/migrations/026_cron_tasks.ts +49 -0
- package/src/database/migrations/027_comments.ts +87 -0
- package/src/database/migrations/028_drop_author_url.ts +9 -0
- package/src/database/migrations/029_redirects.ts +67 -0
- package/src/database/migrations/030_widen_scheduled_index.ts +48 -0
- package/src/database/migrations/031_bylines.ts +90 -0
- package/src/database/migrations/032_rate_limits.ts +39 -0
- package/src/database/migrations/runner.ts +170 -0
- package/src/database/repositories/audit.ts +294 -0
- package/src/database/repositories/byline.ts +387 -0
- package/src/database/repositories/comment.ts +458 -0
- package/src/database/repositories/content.ts +1144 -0
- package/src/database/repositories/index.ts +30 -0
- package/src/database/repositories/media.ts +347 -0
- package/src/database/repositories/options.ts +150 -0
- package/src/database/repositories/plugin-storage.ts +373 -0
- package/src/database/repositories/redirect.ts +480 -0
- package/src/database/repositories/revision.ts +200 -0
- package/src/database/repositories/seo.ts +176 -0
- package/src/database/repositories/taxonomy.ts +294 -0
- package/src/database/repositories/types.ts +132 -0
- package/src/database/repositories/user.ts +258 -0
- package/src/database/transaction.ts +54 -0
- package/src/database/types.ts +501 -0
- package/src/database/validate.ts +138 -0
- package/src/db/adapters.ts +125 -0
- package/src/db/index.ts +37 -0
- package/src/db/libsql.ts +23 -0
- package/src/db/postgres.ts +30 -0
- package/src/db/sqlite.ts +27 -0
- package/src/emdash-runtime.ts +2096 -0
- package/src/fields/boolean.ts +34 -0
- package/src/fields/datetime.ts +44 -0
- package/src/fields/file.ts +41 -0
- package/src/fields/image.ts +34 -0
- package/src/fields/index.ts +42 -0
- package/src/fields/integer.ts +50 -0
- package/src/fields/json.ts +37 -0
- package/src/fields/multiselect.ts +48 -0
- package/src/fields/number.ts +52 -0
- package/src/fields/portable-text.ts +33 -0
- package/src/fields/reference.ts +29 -0
- package/src/fields/richtext.ts +31 -0
- package/src/fields/select.ts +46 -0
- package/src/fields/slug.ts +38 -0
- package/src/fields/text.ts +55 -0
- package/src/fields/textarea.ts +52 -0
- package/src/fields/types.ts +64 -0
- package/src/i18n/config.ts +68 -0
- package/src/import/index.ts +90 -0
- package/src/import/menus.ts +436 -0
- package/src/import/registry.ts +111 -0
- package/src/import/sections.ts +103 -0
- package/src/import/settings.ts +281 -0
- package/src/import/sources/wordpress-plugin.ts +641 -0
- package/src/import/sources/wordpress-rest.ts +191 -0
- package/src/import/sources/wxr.ts +330 -0
- package/src/import/ssrf.ts +260 -0
- package/src/import/types.ts +418 -0
- package/src/import/utils.ts +412 -0
- package/src/index.ts +481 -0
- package/src/loader.ts +770 -0
- package/src/mcp/server.ts +1463 -0
- package/src/media/index.ts +32 -0
- package/src/media/local-runtime.ts +213 -0
- package/src/media/local.ts +46 -0
- package/src/media/normalize.ts +190 -0
- package/src/media/placeholder.ts +150 -0
- package/src/media/provider-loader.ts +78 -0
- package/src/media/types.ts +279 -0
- package/src/menus/index.ts +324 -0
- package/src/menus/types.ts +112 -0
- package/src/page/context.ts +93 -0
- package/src/page/fragments.ts +89 -0
- package/src/page/index.ts +58 -0
- package/src/page/jsonld.ts +94 -0
- package/src/page/metadata.ts +185 -0
- package/src/page/seo-contributions.ts +136 -0
- package/src/plugin-utils.ts +80 -0
- package/src/plugins/adapt-sandbox-entry.ts +207 -0
- package/src/plugins/context.ts +833 -0
- package/src/plugins/cron.ts +361 -0
- package/src/plugins/define-plugin.ts +259 -0
- package/src/plugins/email-console.ts +73 -0
- package/src/plugins/email.ts +209 -0
- package/src/plugins/hooks.ts +1273 -0
- package/src/plugins/index.ts +193 -0
- package/src/plugins/manager.ts +595 -0
- package/src/plugins/manifest-schema.ts +230 -0
- package/src/plugins/marketplace.ts +460 -0
- package/src/plugins/request-meta.ts +139 -0
- package/src/plugins/routes.ts +302 -0
- package/src/plugins/sandbox/index.ts +18 -0
- package/src/plugins/sandbox/noop.ts +76 -0
- package/src/plugins/sandbox/types.ts +173 -0
- package/src/plugins/scheduler/node.ts +122 -0
- package/src/plugins/scheduler/piggyback.ts +71 -0
- package/src/plugins/scheduler/types.ts +27 -0
- package/src/plugins/state.ts +208 -0
- package/src/plugins/storage-indexes.ts +326 -0
- package/src/plugins/storage-query.ts +240 -0
- package/src/plugins/types.ts +1284 -0
- package/src/preview/helpers.ts +27 -0
- package/src/preview/index.ts +40 -0
- package/src/preview/tokens.ts +279 -0
- package/src/preview/urls.ts +118 -0
- package/src/query.ts +674 -0
- package/src/redirects/patterns.ts +224 -0
- package/src/request-context.ts +67 -0
- package/src/runtime.ts +21 -0
- package/src/schema/index.ts +29 -0
- package/src/schema/query.ts +44 -0
- package/src/schema/registry.ts +965 -0
- package/src/schema/types.ts +276 -0
- package/src/schema/zod-generator.ts +413 -0
- package/src/search/fts-manager.ts +452 -0
- package/src/search/index.ts +26 -0
- package/src/search/query.ts +396 -0
- package/src/search/text-extraction.ts +162 -0
- package/src/search/types.ts +114 -0
- package/src/sections/index.ts +226 -0
- package/src/sections/types.ts +86 -0
- package/src/seed/apply.ts +1141 -0
- package/src/seed/default.ts +86 -0
- package/src/seed/index.ts +28 -0
- package/src/seed/load.ts +35 -0
- package/src/seed/types.ts +341 -0
- package/src/seed/validate.ts +642 -0
- package/src/seo/index.ts +179 -0
- package/src/settings/index.ts +203 -0
- package/src/settings/types.ts +58 -0
- package/src/storage/index.ts +28 -0
- package/src/storage/local.ts +249 -0
- package/src/storage/s3.ts +263 -0
- package/src/storage/types.ts +204 -0
- package/src/taxonomies/index.ts +309 -0
- package/src/taxonomies/types.ts +61 -0
- package/src/ui.ts +75 -0
- package/src/utils/base64.ts +73 -0
- package/src/utils/hash.ts +36 -0
- package/src/utils/sanitize.ts +20 -0
- package/src/utils/slugify.ts +29 -0
- package/src/utils/url.ts +48 -0
- package/src/virtual-modules.d.ts +111 -0
- package/src/visual-editing/editable.ts +108 -0
- package/src/visual-editing/toolbar.ts +1229 -0
- package/src/widgets/components.ts +105 -0
- package/src/widgets/index.ts +131 -0
- package/src/widgets/types.ts +81 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FTS5 Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages FTS5 virtual tables and triggers for search indexing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Kysely } from "kysely";
|
|
8
|
+
import { sql } from "kysely";
|
|
9
|
+
|
|
10
|
+
import { isSqlite, tableExists as dialectTableExists } from "../database/dialect-helpers.js";
|
|
11
|
+
import type { Database } from "../database/types.js";
|
|
12
|
+
import { validateIdentifier } from "../database/validate.js";
|
|
13
|
+
import type { SearchConfig } from "./types.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* FTS5 Manager
|
|
17
|
+
*
|
|
18
|
+
* Handles creation, deletion, and management of FTS5 virtual tables
|
|
19
|
+
* for full-text search on content collections.
|
|
20
|
+
*/
|
|
21
|
+
export class FTSManager {
|
|
22
|
+
constructor(private db: Kysely<Database>) {}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validate a collection slug and its searchable field names.
|
|
26
|
+
* Must be called before any raw SQL interpolation.
|
|
27
|
+
*/
|
|
28
|
+
private validateInputs(collectionSlug: string, searchableFields?: string[]): void {
|
|
29
|
+
validateIdentifier(collectionSlug, "collection slug");
|
|
30
|
+
if (searchableFields) {
|
|
31
|
+
for (const field of searchableFields) {
|
|
32
|
+
validateIdentifier(field, "searchable field name");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the FTS table name for a collection
|
|
39
|
+
* Uses _emdash_ prefix to clearly mark as internal/system table
|
|
40
|
+
*/
|
|
41
|
+
getFtsTableName(collectionSlug: string): string {
|
|
42
|
+
return `_emdash_fts_${collectionSlug}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the content table name for a collection
|
|
47
|
+
*/
|
|
48
|
+
getContentTableName(collectionSlug: string): string {
|
|
49
|
+
return `ec_${collectionSlug}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if an FTS table exists for a collection
|
|
54
|
+
*/
|
|
55
|
+
async ftsTableExists(collectionSlug: string): Promise<boolean> {
|
|
56
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
57
|
+
return dialectTableExists(this.db, ftsTable);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create an FTS5 virtual table for a collection.
|
|
62
|
+
* FTS5 is SQLite-only; on other dialects this is a no-op.
|
|
63
|
+
*
|
|
64
|
+
* @param collectionSlug - The collection slug
|
|
65
|
+
* @param searchableFields - Array of field names to index
|
|
66
|
+
* @param weights - Optional field weights for ranking
|
|
67
|
+
*/
|
|
68
|
+
async createFtsTable(
|
|
69
|
+
collectionSlug: string,
|
|
70
|
+
searchableFields: string[],
|
|
71
|
+
_weights?: Record<string, number>,
|
|
72
|
+
): Promise<void> {
|
|
73
|
+
if (!isSqlite(this.db)) return;
|
|
74
|
+
this.validateInputs(collectionSlug, searchableFields);
|
|
75
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
76
|
+
const contentTable = this.getContentTableName(collectionSlug);
|
|
77
|
+
|
|
78
|
+
// Build the column list for FTS5
|
|
79
|
+
// id and locale are UNINDEXED (used for joining/filtering, not searched)
|
|
80
|
+
const columns = ["id UNINDEXED", "locale UNINDEXED", ...searchableFields].join(", ");
|
|
81
|
+
|
|
82
|
+
// Create the FTS5 virtual table
|
|
83
|
+
// Using content= to make it a contentless FTS table (we manage sync ourselves)
|
|
84
|
+
// tokenize='porter unicode61' enables stemming (run matches running, ran, etc.)
|
|
85
|
+
await sql
|
|
86
|
+
.raw(`
|
|
87
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS "${ftsTable}" USING fts5(
|
|
88
|
+
${columns},
|
|
89
|
+
content='${contentTable}',
|
|
90
|
+
content_rowid='rowid',
|
|
91
|
+
tokenize='porter unicode61'
|
|
92
|
+
)
|
|
93
|
+
`)
|
|
94
|
+
.execute(this.db);
|
|
95
|
+
|
|
96
|
+
// Create triggers for automatic sync
|
|
97
|
+
await this.createTriggers(collectionSlug, searchableFields);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create triggers to keep FTS table in sync with content table
|
|
102
|
+
*/
|
|
103
|
+
private async createTriggers(collectionSlug: string, searchableFields: string[]): Promise<void> {
|
|
104
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
105
|
+
const contentTable = this.getContentTableName(collectionSlug);
|
|
106
|
+
const fieldList = searchableFields.join(", ");
|
|
107
|
+
const newFieldList = searchableFields.map((f) => `NEW.${f}`).join(", ");
|
|
108
|
+
|
|
109
|
+
// Insert trigger
|
|
110
|
+
await sql
|
|
111
|
+
.raw(`
|
|
112
|
+
CREATE TRIGGER IF NOT EXISTS "${ftsTable}_insert"
|
|
113
|
+
AFTER INSERT ON "${contentTable}"
|
|
114
|
+
BEGIN
|
|
115
|
+
INSERT INTO "${ftsTable}"(rowid, id, locale, ${fieldList})
|
|
116
|
+
VALUES (NEW.rowid, NEW.id, NEW.locale, ${newFieldList});
|
|
117
|
+
END
|
|
118
|
+
`)
|
|
119
|
+
.execute(this.db);
|
|
120
|
+
|
|
121
|
+
// Update trigger - delete old, insert new
|
|
122
|
+
await sql
|
|
123
|
+
.raw(`
|
|
124
|
+
CREATE TRIGGER IF NOT EXISTS "${ftsTable}_update"
|
|
125
|
+
AFTER UPDATE ON "${contentTable}"
|
|
126
|
+
BEGIN
|
|
127
|
+
DELETE FROM "${ftsTable}" WHERE rowid = OLD.rowid;
|
|
128
|
+
INSERT INTO "${ftsTable}"(rowid, id, locale, ${fieldList})
|
|
129
|
+
VALUES (NEW.rowid, NEW.id, NEW.locale, ${newFieldList});
|
|
130
|
+
END
|
|
131
|
+
`)
|
|
132
|
+
.execute(this.db);
|
|
133
|
+
|
|
134
|
+
// Delete trigger
|
|
135
|
+
await sql
|
|
136
|
+
.raw(`
|
|
137
|
+
CREATE TRIGGER IF NOT EXISTS "${ftsTable}_delete"
|
|
138
|
+
AFTER DELETE ON "${contentTable}"
|
|
139
|
+
BEGIN
|
|
140
|
+
DELETE FROM "${ftsTable}" WHERE rowid = OLD.rowid;
|
|
141
|
+
END
|
|
142
|
+
`)
|
|
143
|
+
.execute(this.db);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Drop triggers for a collection
|
|
148
|
+
*/
|
|
149
|
+
private async dropTriggers(collectionSlug: string): Promise<void> {
|
|
150
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
151
|
+
|
|
152
|
+
await sql.raw(`DROP TRIGGER IF EXISTS "${ftsTable}_insert"`).execute(this.db);
|
|
153
|
+
await sql.raw(`DROP TRIGGER IF EXISTS "${ftsTable}_update"`).execute(this.db);
|
|
154
|
+
await sql.raw(`DROP TRIGGER IF EXISTS "${ftsTable}_delete"`).execute(this.db);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Drop the FTS table and triggers for a collection
|
|
159
|
+
*/
|
|
160
|
+
async dropFtsTable(collectionSlug: string): Promise<void> {
|
|
161
|
+
if (!isSqlite(this.db)) return;
|
|
162
|
+
this.validateInputs(collectionSlug);
|
|
163
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
164
|
+
|
|
165
|
+
// Drop triggers first
|
|
166
|
+
await this.dropTriggers(collectionSlug);
|
|
167
|
+
|
|
168
|
+
// Drop the FTS table
|
|
169
|
+
await sql.raw(`DROP TABLE IF EXISTS "${ftsTable}"`).execute(this.db);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Rebuild the FTS index for a collection
|
|
174
|
+
*
|
|
175
|
+
* This is useful after bulk imports or if the index gets out of sync.
|
|
176
|
+
*/
|
|
177
|
+
async rebuildIndex(
|
|
178
|
+
collectionSlug: string,
|
|
179
|
+
searchableFields: string[],
|
|
180
|
+
weights?: Record<string, number>,
|
|
181
|
+
): Promise<void> {
|
|
182
|
+
if (!isSqlite(this.db)) return;
|
|
183
|
+
// Drop existing table and triggers
|
|
184
|
+
await this.dropFtsTable(collectionSlug);
|
|
185
|
+
|
|
186
|
+
// Recreate table and triggers
|
|
187
|
+
await this.createFtsTable(collectionSlug, searchableFields, weights);
|
|
188
|
+
|
|
189
|
+
// Populate from existing content
|
|
190
|
+
await this.populateFromContent(collectionSlug, searchableFields);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Populate the FTS table from existing content
|
|
195
|
+
*/
|
|
196
|
+
async populateFromContent(collectionSlug: string, searchableFields: string[]): Promise<void> {
|
|
197
|
+
if (!isSqlite(this.db)) return;
|
|
198
|
+
this.validateInputs(collectionSlug, searchableFields);
|
|
199
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
200
|
+
const contentTable = this.getContentTableName(collectionSlug);
|
|
201
|
+
const fieldList = searchableFields.join(", ");
|
|
202
|
+
|
|
203
|
+
// Insert all existing content into FTS table
|
|
204
|
+
await sql
|
|
205
|
+
.raw(`
|
|
206
|
+
INSERT INTO "${ftsTable}"(rowid, id, locale, ${fieldList})
|
|
207
|
+
SELECT rowid, id, locale, ${fieldList} FROM "${contentTable}"
|
|
208
|
+
WHERE deleted_at IS NULL
|
|
209
|
+
`)
|
|
210
|
+
.execute(this.db);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get the search configuration for a collection
|
|
215
|
+
*/
|
|
216
|
+
async getSearchConfig(collectionSlug: string): Promise<SearchConfig | null> {
|
|
217
|
+
const result = await this.db
|
|
218
|
+
.selectFrom("_emdash_collections")
|
|
219
|
+
.select("search_config")
|
|
220
|
+
.where("slug", "=", collectionSlug)
|
|
221
|
+
.executeTakeFirst();
|
|
222
|
+
|
|
223
|
+
if (!result?.search_config) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const parsed: unknown = JSON.parse(result.search_config);
|
|
229
|
+
if (
|
|
230
|
+
typeof parsed !== "object" ||
|
|
231
|
+
parsed === null ||
|
|
232
|
+
!("enabled" in parsed) ||
|
|
233
|
+
typeof parsed.enabled !== "boolean"
|
|
234
|
+
) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const config: SearchConfig = { enabled: parsed.enabled };
|
|
238
|
+
if ("weights" in parsed && typeof parsed.weights === "object" && parsed.weights !== null) {
|
|
239
|
+
// weights is a JSON-parsed object — safe to treat as Record<string, number>
|
|
240
|
+
const weights: Record<string, number> = {};
|
|
241
|
+
for (const [k, v] of Object.entries(parsed.weights)) {
|
|
242
|
+
if (typeof v === "number") {
|
|
243
|
+
weights[k] = v;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
config.weights = weights;
|
|
247
|
+
}
|
|
248
|
+
return config;
|
|
249
|
+
} catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Update the search configuration for a collection
|
|
256
|
+
*/
|
|
257
|
+
async setSearchConfig(collectionSlug: string, config: SearchConfig): Promise<void> {
|
|
258
|
+
await this.db
|
|
259
|
+
.updateTable("_emdash_collections")
|
|
260
|
+
.set({ search_config: JSON.stringify(config) })
|
|
261
|
+
.where("slug", "=", collectionSlug)
|
|
262
|
+
.execute();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get searchable fields for a collection
|
|
267
|
+
*/
|
|
268
|
+
async getSearchableFields(collectionSlug: string): Promise<string[]> {
|
|
269
|
+
const collection = await this.db
|
|
270
|
+
.selectFrom("_emdash_collections")
|
|
271
|
+
.select("id")
|
|
272
|
+
.where("slug", "=", collectionSlug)
|
|
273
|
+
.executeTakeFirst();
|
|
274
|
+
|
|
275
|
+
if (!collection) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const fields = await this.db
|
|
280
|
+
.selectFrom("_emdash_fields")
|
|
281
|
+
.select("slug")
|
|
282
|
+
.where("collection_id", "=", collection.id)
|
|
283
|
+
.where("searchable", "=", 1)
|
|
284
|
+
.execute();
|
|
285
|
+
|
|
286
|
+
return fields.map((f) => f.slug);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Enable search for a collection
|
|
291
|
+
*
|
|
292
|
+
* Creates the FTS table and triggers, and populates from existing content.
|
|
293
|
+
*/
|
|
294
|
+
async enableSearch(
|
|
295
|
+
collectionSlug: string,
|
|
296
|
+
options?: { weights?: Record<string, number> },
|
|
297
|
+
): Promise<void> {
|
|
298
|
+
if (!isSqlite(this.db)) {
|
|
299
|
+
throw new Error("Full-text search is only available with SQLite databases");
|
|
300
|
+
}
|
|
301
|
+
// Get searchable fields
|
|
302
|
+
const searchableFields = await this.getSearchableFields(collectionSlug);
|
|
303
|
+
|
|
304
|
+
if (searchableFields.length === 0) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
`No searchable fields defined for collection "${collectionSlug}". ` +
|
|
307
|
+
`Mark at least one field as searchable before enabling search.`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Create FTS table
|
|
312
|
+
await this.createFtsTable(collectionSlug, searchableFields, options?.weights);
|
|
313
|
+
|
|
314
|
+
// Populate from existing content
|
|
315
|
+
await this.populateFromContent(collectionSlug, searchableFields);
|
|
316
|
+
|
|
317
|
+
// Update search config
|
|
318
|
+
await this.setSearchConfig(collectionSlug, {
|
|
319
|
+
enabled: true,
|
|
320
|
+
weights: options?.weights,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Disable search for a collection
|
|
326
|
+
*
|
|
327
|
+
* Drops the FTS table and triggers.
|
|
328
|
+
*/
|
|
329
|
+
async disableSearch(collectionSlug: string): Promise<void> {
|
|
330
|
+
if (!isSqlite(this.db)) return;
|
|
331
|
+
await this.dropFtsTable(collectionSlug);
|
|
332
|
+
await this.setSearchConfig(collectionSlug, { enabled: false });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get index statistics for a collection
|
|
337
|
+
*/
|
|
338
|
+
async getIndexStats(
|
|
339
|
+
collectionSlug: string,
|
|
340
|
+
): Promise<{ indexed: number; lastRebuilt?: string } | null> {
|
|
341
|
+
if (!isSqlite(this.db)) return null;
|
|
342
|
+
this.validateInputs(collectionSlug);
|
|
343
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
344
|
+
|
|
345
|
+
// Check if table exists
|
|
346
|
+
if (!(await this.ftsTableExists(collectionSlug))) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Count indexed rows
|
|
351
|
+
const result = await sql<{ count: number }>`
|
|
352
|
+
SELECT COUNT(*) as count FROM "${sql.raw(ftsTable)}"
|
|
353
|
+
`.execute(this.db);
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
indexed: result.rows[0]?.count ?? 0,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Verify FTS index integrity and rebuild if corrupted.
|
|
362
|
+
*
|
|
363
|
+
* Checks for two corruption indicators:
|
|
364
|
+
* 1. Row count mismatch between content table and FTS table
|
|
365
|
+
* 2. FTS5 integrity-check failure (catches shadow table inconsistencies)
|
|
366
|
+
*
|
|
367
|
+
* Returns true if the index was rebuilt, false if it was healthy.
|
|
368
|
+
*/
|
|
369
|
+
async verifyAndRepairIndex(collectionSlug: string): Promise<boolean> {
|
|
370
|
+
if (!isSqlite(this.db)) return false;
|
|
371
|
+
this.validateInputs(collectionSlug);
|
|
372
|
+
const ftsTable = this.getFtsTableName(collectionSlug);
|
|
373
|
+
const contentTable = this.getContentTableName(collectionSlug);
|
|
374
|
+
|
|
375
|
+
if (!(await this.ftsTableExists(collectionSlug))) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Check 1: Row count mismatch
|
|
380
|
+
const contentCount = await sql<{ count: number }>`
|
|
381
|
+
SELECT COUNT(*) as count FROM ${sql.ref(contentTable)}
|
|
382
|
+
WHERE deleted_at IS NULL
|
|
383
|
+
`.execute(this.db);
|
|
384
|
+
|
|
385
|
+
const ftsCount = await sql<{ count: number }>`
|
|
386
|
+
SELECT COUNT(*) as count FROM "${sql.raw(ftsTable)}"
|
|
387
|
+
`.execute(this.db);
|
|
388
|
+
|
|
389
|
+
const contentRows = contentCount.rows[0]?.count ?? 0;
|
|
390
|
+
const ftsRows = ftsCount.rows[0]?.count ?? 0;
|
|
391
|
+
|
|
392
|
+
if (contentRows !== ftsRows) {
|
|
393
|
+
console.warn(
|
|
394
|
+
`FTS index for "${collectionSlug}" has ${ftsRows} rows but content table has ${contentRows}. Rebuilding.`,
|
|
395
|
+
);
|
|
396
|
+
const fields = await this.getSearchableFields(collectionSlug);
|
|
397
|
+
const config = await this.getSearchConfig(collectionSlug);
|
|
398
|
+
if (fields.length > 0) {
|
|
399
|
+
await this.rebuildIndex(collectionSlug, fields, config?.weights);
|
|
400
|
+
}
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Check 2: FTS5 integrity check (catches shadow table corruption)
|
|
405
|
+
try {
|
|
406
|
+
await sql
|
|
407
|
+
.raw(`INSERT INTO "${ftsTable}"("${ftsTable}") VALUES('integrity-check')`)
|
|
408
|
+
.execute(this.db);
|
|
409
|
+
} catch {
|
|
410
|
+
console.warn(`FTS integrity check failed for "${collectionSlug}". Rebuilding index.`);
|
|
411
|
+
const fields = await this.getSearchableFields(collectionSlug);
|
|
412
|
+
const config = await this.getSearchConfig(collectionSlug);
|
|
413
|
+
if (fields.length > 0) {
|
|
414
|
+
await this.rebuildIndex(collectionSlug, fields, config?.weights);
|
|
415
|
+
}
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Verify and repair FTS indexes for all search-enabled collections.
|
|
424
|
+
*
|
|
425
|
+
* Intended to run at startup to auto-heal any corruption from
|
|
426
|
+
* previous process crashes.
|
|
427
|
+
*/
|
|
428
|
+
async verifyAndRepairAll(): Promise<number> {
|
|
429
|
+
if (!isSqlite(this.db)) return 0;
|
|
430
|
+
|
|
431
|
+
const collections = await this.db
|
|
432
|
+
.selectFrom("_emdash_collections")
|
|
433
|
+
.select("slug")
|
|
434
|
+
.where("search_config", "is not", null)
|
|
435
|
+
.execute();
|
|
436
|
+
|
|
437
|
+
let repaired = 0;
|
|
438
|
+
for (const { slug } of collections) {
|
|
439
|
+
const config = await this.getSearchConfig(slug);
|
|
440
|
+
if (!config?.enabled) continue;
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
const wasRepaired = await this.verifyAndRepairIndex(slug);
|
|
444
|
+
if (wasRepaired) repaired++;
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.error(`Failed to verify/repair FTS index for "${slug}":`, error);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return repaired;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Module
|
|
3
|
+
*
|
|
4
|
+
* Full-text search for EmDash using SQLite FTS5.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
export type {
|
|
9
|
+
SearchConfig,
|
|
10
|
+
SearchOptions,
|
|
11
|
+
CollectionSearchOptions,
|
|
12
|
+
SearchResult,
|
|
13
|
+
SearchResponse,
|
|
14
|
+
SuggestOptions,
|
|
15
|
+
Suggestion,
|
|
16
|
+
SearchStats,
|
|
17
|
+
} from "./types.js";
|
|
18
|
+
|
|
19
|
+
// FTS Manager
|
|
20
|
+
export { FTSManager } from "./fts-manager.js";
|
|
21
|
+
|
|
22
|
+
// Query functions (public API uses getDb() internally)
|
|
23
|
+
export { search, searchWithDb, searchCollection, getSuggestions, getSearchStats } from "./query.js";
|
|
24
|
+
|
|
25
|
+
// Text extraction
|
|
26
|
+
export { extractPlainText, extractSearchableFields } from "./text-extraction.js";
|