emdash 0.8.0 → 0.9.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/dist/{adapters-BKSf3T9R.d.mts → adapters-DoNJiveC.d.mts} +1 -1
- package/dist/{adapters-BKSf3T9R.d.mts.map → adapters-DoNJiveC.d.mts.map} +1 -1
- package/dist/{apply-x0eMK1lX.mjs → apply-BzltprvY.mjs} +85 -135
- package/dist/apply-BzltprvY.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +110 -4
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +6 -7
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +16 -59
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.d.mts.map +1 -1
- package/dist/astro/middleware/redirect.mjs +17 -12
- package/dist/astro/middleware/redirect.mjs.map +1 -1
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +9 -6
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +72 -124
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +26 -10
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{base64-MBPo9ozB.mjs → base64-BRICGH2l.mjs} +1 -1
- package/dist/{base64-MBPo9ozB.mjs.map → base64-BRICGH2l.mjs.map} +1 -1
- package/dist/{byline-Chbr2GoP.mjs → byline-BSaNL1w7.mjs} +4 -4
- package/dist/{byline-Chbr2GoP.mjs.map → byline-BSaNL1w7.mjs.map} +1 -1
- package/dist/bylines-CvJ3PYz2.mjs +113 -0
- package/dist/bylines-CvJ3PYz2.mjs.map +1 -0
- package/dist/cache-C6N_hhN7.mjs +65 -0
- package/dist/cache-C6N_hhN7.mjs.map +1 -0
- package/dist/{chunks-HGz06Soa.mjs → chunks-NBQVDOci.mjs} +8 -2
- package/dist/{chunks-HGz06Soa.mjs.map → chunks-NBQVDOci.mjs.map} +1 -1
- package/dist/cli/index.mjs +224 -30
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +3 -3
- package/dist/client/index.mjs.map +1 -1
- package/dist/{config-BXwuX8Bx.mjs → config-BI0V3ICQ.mjs} +1 -1
- package/dist/{config-BXwuX8Bx.mjs.map → config-BI0V3ICQ.mjs.map} +1 -1
- package/dist/{content-BcQPYxdV.mjs → content-8lOYF0pr.mjs} +32 -15
- package/dist/{content-BcQPYxdV.mjs.map → content-8lOYF0pr.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +2 -2
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/libsql.d.mts.map +1 -1
- package/dist/db/libsql.mjs +7 -2
- package/dist/db/libsql.mjs.map +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.d.mts.map +1 -1
- package/dist/db/sqlite.mjs +8 -3
- package/dist/db/sqlite.mjs.map +1 -1
- package/dist/{db-errors-l1Qh2RPR.mjs → db-errors-WRezodiz.mjs} +1 -1
- package/dist/{db-errors-l1Qh2RPR.mjs.map → db-errors-WRezodiz.mjs.map} +1 -1
- package/dist/{default-DCVqE5ib.mjs → default-D8ksjWhO.mjs} +1 -1
- package/dist/{default-DCVqE5ib.mjs.map → default-D8ksjWhO.mjs.map} +1 -1
- package/dist/{dialect-helpers-DhTzaUxP.mjs → dialect-helpers-BKCvISIQ.mjs} +19 -2
- package/dist/dialect-helpers-BKCvISIQ.mjs.map +1 -0
- package/dist/{error-zG5T1UGA.mjs → error-D_-tqP-I.mjs} +1 -1
- package/dist/{error-zG5T1UGA.mjs.map → error-D_-tqP-I.mjs.map} +1 -1
- package/dist/{index-DIb-CzNx.d.mts → index-BFRaVcD6.d.mts} +94 -34
- package/dist/index-BFRaVcD6.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +29 -27
- package/dist/{load-CyEoextb.mjs → load-DDqMMvZL.mjs} +2 -2
- package/dist/{load-CyEoextb.mjs.map → load-DDqMMvZL.mjs.map} +1 -1
- package/dist/{loader-CndGj8kM.mjs → loader-CKLbBnhK.mjs} +27 -7
- package/dist/loader-CKLbBnhK.mjs.map +1 -0
- package/dist/{manifest-schema-DH9xhc6t.mjs → manifest-schema-DqWNC3lM.mjs} +33 -3
- package/dist/manifest-schema-DqWNC3lM.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/media/local-runtime.mjs +3 -3
- package/dist/{media-D8FbNsl0.mjs → media-BW32b4gi.mjs} +2 -2
- package/dist/{media-D8FbNsl0.mjs.map → media-BW32b4gi.mjs.map} +1 -1
- package/dist/{mode-BnAOqItE.mjs → mode-ier8jbBk.mjs} +1 -1
- package/dist/{mode-BnAOqItE.mjs.map → mode-ier8jbBk.mjs.map} +1 -1
- package/dist/options-BVp3UsTS.mjs +117 -0
- package/dist/options-BVp3UsTS.mjs.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/{placeholder-D29tWZ7o.d.mts → placeholder-BE4o_2dc.d.mts} +1 -1
- package/dist/{placeholder-D29tWZ7o.d.mts.map → placeholder-BE4o_2dc.d.mts.map} +1 -1
- package/dist/{placeholder-C-fk5hYI.mjs → placeholder-CIJejMlK.mjs} +1 -1
- package/dist/{placeholder-C-fk5hYI.mjs.map → placeholder-CIJejMlK.mjs.map} +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.mjs +6 -5
- package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
- package/dist/public-url-DByxYjUw.mjs +51 -0
- package/dist/public-url-DByxYjUw.mjs.map +1 -0
- package/dist/{query-fqEdLFms.mjs → query-Cg9ZKRQ0.mjs} +114 -16
- package/dist/query-Cg9ZKRQ0.mjs.map +1 -0
- package/dist/{redirect-D_pshWdf.mjs → redirect-BhUBKRc1.mjs} +11 -6
- package/dist/redirect-BhUBKRc1.mjs.map +1 -0
- package/dist/{registry-C3Mr0ODu.mjs → registry-Dw70ChxB.mjs} +38 -4
- package/dist/registry-Dw70ChxB.mjs.map +1 -0
- package/dist/{request-cache-Ci7f5pBb.mjs → request-cache-B-bmkipQ.mjs} +1 -1
- package/dist/{request-cache-Ci7f5pBb.mjs.map → request-cache-B-bmkipQ.mjs.map} +1 -1
- package/dist/runner-Bnoj7vjK.d.mts +44 -0
- package/dist/runner-Bnoj7vjK.d.mts.map +1 -0
- package/dist/{runner-tQ7BJ4T7.mjs → runner-C7ADox5q.mjs} +185 -55
- package/dist/{runner-tQ7BJ4T7.mjs.map → runner-C7ADox5q.mjs.map} +1 -1
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +4 -4
- package/dist/{search-BoZYFuUk.mjs → search-dOGEccMa.mjs} +129 -83
- package/dist/search-dOGEccMa.mjs.map +1 -0
- package/dist/secrets-CW3reAnU.mjs +314 -0
- package/dist/secrets-CW3reAnU.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +15 -14
- package/dist/seo/index.d.mts +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-B4IAshV8.mjs → taxonomies-ZlRtD6AG.mjs} +14 -7
- package/dist/taxonomies-ZlRtD6AG.mjs.map +1 -0
- package/dist/{tokens-D9vnZqYS.mjs → tokens-D7zMmWi2.mjs} +2 -2
- package/dist/{tokens-D9vnZqYS.mjs.map → tokens-D7zMmWi2.mjs.map} +1 -1
- package/dist/{transport-C9ugt2Nr.mjs → transport-BeMCmin1.mjs} +6 -5
- package/dist/{transport-C9ugt2Nr.mjs.map → transport-BeMCmin1.mjs.map} +1 -1
- package/dist/{transport-CUnEL3Vs.d.mts → transport-DNEfeMaU.d.mts} +1 -1
- package/dist/{transport-CUnEL3Vs.d.mts.map → transport-DNEfeMaU.d.mts.map} +1 -1
- package/dist/types-4fVtCIm0.mjs +68 -0
- package/dist/types-4fVtCIm0.mjs.map +1 -0
- package/dist/{types-BmPPSUEx.d.mts → types-BSyXeCFW.d.mts} +24 -2
- package/dist/{types-BmPPSUEx.d.mts.map → types-BSyXeCFW.d.mts.map} +1 -1
- package/dist/{types-i36XcA_X.d.mts → types-BuBIptGk.d.mts} +65 -134
- package/dist/types-BuBIptGk.d.mts.map +1 -0
- package/dist/{types-CgqmmMJB.mjs → types-CDbKp7ND.mjs} +1 -1
- package/dist/{types-CgqmmMJB.mjs.map → types-CDbKp7ND.mjs.map} +1 -1
- package/dist/{types-Bm1dn-q3.mjs → types-CIOg5AR8.mjs} +1 -1
- package/dist/{types-Bm1dn-q3.mjs.map → types-CIOg5AR8.mjs.map} +1 -1
- package/dist/{types-BrA0xf5I.d.mts → types-CJsYGpco.d.mts} +1 -1
- package/dist/{types-BrA0xf5I.d.mts.map → types-CJsYGpco.d.mts.map} +1 -1
- package/dist/{types-BIgulNsW.mjs → types-CRxNbK-Z.mjs} +2 -2
- package/dist/{types-BIgulNsW.mjs.map → types-CRxNbK-Z.mjs.map} +1 -1
- package/dist/{types-CS8FIX7L.d.mts → types-CrtWgIvl.d.mts} +1 -1
- package/dist/{types-CS8FIX7L.d.mts.map → types-CrtWgIvl.d.mts.map} +1 -1
- package/dist/{types-DIMwPFub.d.mts → types-M78DQ1lx.d.mts} +1 -1
- package/dist/{types-DIMwPFub.d.mts.map → types-M78DQ1lx.d.mts.map} +1 -1
- package/dist/{validate-CxVsLehf.mjs → validate-Baqf0slj.mjs} +3 -3
- package/dist/{validate-CxVsLehf.mjs.map → validate-Baqf0slj.mjs.map} +1 -1
- package/dist/{validate-DHxmpFJt.d.mts → validate-BfQh_C_y.d.mts} +4 -4
- package/dist/{validate-DHxmpFJt.d.mts.map → validate-BfQh_C_y.d.mts.map} +1 -1
- package/dist/{validation-C-ZpN2GI.mjs → validation-BfEI7tNe.mjs} +6 -6
- package/dist/{validation-C-ZpN2GI.mjs.map → validation-BfEI7tNe.mjs.map} +1 -1
- package/dist/version-DoxrVdYf.mjs +7 -0
- package/dist/{version-Bbq8TCrz.mjs.map → version-DoxrVdYf.mjs.map} +1 -1
- package/dist/{zod-generator-CpwccCIv.mjs → zod-generator-CC0xNe_K.mjs} +4 -4
- package/dist/zod-generator-CC0xNe_K.mjs.map +1 -0
- package/locals.d.ts +1 -6
- package/package.json +9 -8
- package/src/api/handlers/comments.ts +6 -4
- package/src/api/handlers/content.ts +29 -1
- package/src/api/handlers/device-flow.ts +5 -0
- package/src/api/handlers/marketplace.ts +11 -4
- package/src/api/handlers/oauth-authorization.ts +72 -33
- package/src/api/handlers/revision.ts +23 -14
- package/src/api/handlers/taxonomies.ts +3 -6
- package/src/api/public-url.ts +48 -2
- package/src/api/schemas/comments.ts +2 -2
- package/src/api/schemas/content.ts +17 -0
- package/src/api/schemas/sections.ts +3 -3
- package/src/api/schemas/users.ts +1 -1
- package/src/api/types.ts +5 -1
- package/src/astro/integration/index.ts +17 -0
- package/src/astro/integration/runtime.ts +30 -0
- package/src/astro/integration/virtual-modules.ts +32 -2
- package/src/astro/integration/vite-config.ts +6 -1
- package/src/astro/middleware/auth.ts +13 -6
- package/src/astro/middleware/redirect.ts +29 -16
- package/src/astro/middleware/request-context.ts +15 -5
- package/src/astro/middleware.ts +23 -9
- package/src/astro/routes/api/auth/invite/complete.ts +6 -1
- package/src/astro/routes/api/auth/passkey/register/verify.ts +6 -1
- package/src/astro/routes/api/auth/passkey/verify.ts +6 -1
- package/src/astro/routes/api/auth/signup/complete.ts +6 -1
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +2 -2
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +34 -12
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +32 -2
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +3 -2
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +8 -4
- package/src/astro/routes/api/content/[collection]/[id].ts +12 -0
- package/src/astro/routes/api/import/wordpress/execute.ts +3 -1
- package/src/astro/routes/api/import/wordpress/prepare.ts +7 -8
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +3 -1
- package/src/astro/routes/api/manifest.ts +62 -45
- package/src/astro/routes/api/media/[id]/confirm.ts +10 -1
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +12 -3
- package/src/astro/routes/api/openapi.json.ts +27 -10
- package/src/astro/routes/api/redirects/404s/index.ts +10 -4
- package/src/astro/routes/api/redirects/404s/summary.ts +4 -2
- package/src/astro/routes/api/redirects/[id].ts +10 -4
- package/src/astro/routes/api/redirects/index.ts +7 -3
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +1 -1
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +0 -2
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +0 -1
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +0 -1
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +2 -2
- package/src/astro/routes/api/schema/collections/index.ts +1 -1
- package/src/astro/routes/api/search/index.ts +10 -2
- package/src/astro/routes/api/sections/[slug].ts +10 -4
- package/src/astro/routes/api/sections/index.ts +7 -3
- package/src/astro/routes/api/setup/admin-verify.ts +6 -1
- package/src/astro/routes/api/snapshot.ts +44 -18
- package/src/astro/routes/api/taxonomies/index.ts +0 -1
- package/src/astro/routes/api/themes/preview.ts +11 -5
- package/src/astro/types.ts +23 -3
- package/src/auth/allowed-origins.ts +168 -0
- package/src/auth/passkey-config.ts +35 -13
- package/src/bylines/index.ts +37 -88
- package/src/cli/commands/auth.ts +28 -6
- package/src/cli/commands/bundle-utils.ts +11 -2
- package/src/cli/commands/bundle.ts +28 -8
- package/src/cli/commands/content.ts +13 -0
- package/src/cli/commands/login.ts +8 -1
- package/src/cli/commands/publish.ts +24 -0
- package/src/cli/commands/secrets.ts +183 -0
- package/src/cli/credentials.ts +1 -1
- package/src/cli/index.ts +5 -1
- package/src/client/index.ts +4 -4
- package/src/client/transport.ts +17 -7
- package/src/components/Break.astro +2 -2
- package/src/components/EmDashHead.astro +18 -13
- package/src/components/Embed.astro +1 -1
- package/src/components/Gallery.astro +1 -1
- package/src/components/Image.astro +1 -1
- package/src/components/InlinePortableTextEditor.tsx +104 -18
- package/src/config/secrets.ts +528 -0
- package/src/database/dialect-helpers.ts +50 -0
- package/src/database/migrations/034_published_at_index.ts +1 -1
- package/src/database/migrations/035_bounded_404_log.ts +56 -39
- package/src/database/migrations/runner.ts +156 -23
- package/src/database/repositories/content.ts +36 -12
- package/src/database/repositories/redirect.ts +14 -3
- package/src/database/repositories/taxonomy.ts +26 -0
- package/src/db/libsql.ts +1 -3
- package/src/db/sqlite.ts +2 -5
- package/src/emdash-runtime.ts +84 -159
- package/src/index.ts +9 -0
- package/src/loader.ts +24 -1
- package/src/mcp/server.ts +103 -36
- package/src/page/site-identity.ts +58 -0
- package/src/plugins/adapt-sandbox-entry.ts +22 -10
- package/src/plugins/context.ts +13 -10
- package/src/plugins/define-plugin.ts +40 -12
- package/src/plugins/hooks.ts +23 -19
- package/src/plugins/index.ts +9 -0
- package/src/plugins/manifest-schema.ts +37 -2
- package/src/plugins/types.ts +151 -11
- package/src/preview/urls.ts +23 -3
- package/src/query.ts +148 -5
- package/src/redirects/cache.ts +38 -18
- package/src/schema/registry.ts +56 -0
- package/src/schema/zod-generator.ts +27 -5
- package/src/seed/apply.ts +2 -0
- package/src/settings/index.ts +80 -6
- package/src/settings/types.ts +23 -1
- package/src/taxonomies/index.ts +11 -1
- package/dist/apply-x0eMK1lX.mjs.map +0 -1
- package/dist/bylines-CRNsVG88.mjs +0 -157
- package/dist/bylines-CRNsVG88.mjs.map +0 -1
- package/dist/cache-BkKBuIvS.mjs +0 -56
- package/dist/cache-BkKBuIvS.mjs.map +0 -1
- package/dist/chunk-ClPoSABd.mjs +0 -21
- package/dist/dialect-helpers-DhTzaUxP.mjs.map +0 -1
- package/dist/index-DIb-CzNx.d.mts.map +0 -1
- package/dist/loader-CndGj8kM.mjs.map +0 -1
- package/dist/manifest-schema-DH9xhc6t.mjs.map +0 -1
- package/dist/query-fqEdLFms.mjs.map +0 -1
- package/dist/redirect-D_pshWdf.mjs.map +0 -1
- package/dist/registry-C3Mr0ODu.mjs.map +0 -1
- package/dist/runner-OURCaApa.d.mts +0 -34
- package/dist/runner-OURCaApa.d.mts.map +0 -1
- package/dist/search-BoZYFuUk.mjs.map +0 -1
- package/dist/taxonomies-B4IAshV8.mjs.map +0 -1
- package/dist/types-i36XcA_X.d.mts.map +0 -1
- package/dist/version-Bbq8TCrz.mjs +0 -7
- package/dist/zod-generator-CpwccCIv.mjs.map +0 -1
package/src/emdash-runtime.ts
CHANGED
|
@@ -39,7 +39,6 @@ import type {
|
|
|
39
39
|
PageMetadataContribution,
|
|
40
40
|
PageFragmentContribution,
|
|
41
41
|
} from "./plugins/types.js";
|
|
42
|
-
import { invalidateUrlPatternCache } from "./query.js";
|
|
43
42
|
import type { FieldType } from "./schema/types.js";
|
|
44
43
|
import { hashString } from "./utils/hash.js";
|
|
45
44
|
import { COMMIT, VERSION } from "./version.js";
|
|
@@ -111,6 +110,7 @@ import {
|
|
|
111
110
|
DEFAULT_COMMENT_MODERATOR_PLUGIN_ID,
|
|
112
111
|
defaultCommentModerate,
|
|
113
112
|
} from "./comments/moderator.js";
|
|
113
|
+
import { validateEncryptionKeyAtStartup } from "./config/secrets.js";
|
|
114
114
|
import { OptionsRepository } from "./database/repositories/options.js";
|
|
115
115
|
import {
|
|
116
116
|
handleContentList,
|
|
@@ -161,6 +161,7 @@ import { NodeCronScheduler } from "./plugins/scheduler/node.js";
|
|
|
161
161
|
import { PiggybackScheduler } from "./plugins/scheduler/piggyback.js";
|
|
162
162
|
import type { CronScheduler } from "./plugins/scheduler/types.js";
|
|
163
163
|
import { PluginStateRepository } from "./plugins/state.js";
|
|
164
|
+
import { requestCached } from "./request-cache.js";
|
|
164
165
|
import { getRequestContext } from "./request-context.js";
|
|
165
166
|
import { FTSManager } from "./search/fts-manager.js";
|
|
166
167
|
|
|
@@ -287,7 +288,6 @@ export interface EmDashRuntimeParts {
|
|
|
287
288
|
};
|
|
288
289
|
runtimeDeps: RuntimeDependencies;
|
|
289
290
|
pipelineRef: { current: HookPipeline };
|
|
290
|
-
manifestCacheKey: string;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
/**
|
|
@@ -343,10 +343,6 @@ export class EmDashRuntime {
|
|
|
343
343
|
private enabledPlugins: Set<string>;
|
|
344
344
|
private pluginStates: Map<string, string>;
|
|
345
345
|
|
|
346
|
-
private _cachedManifest: EmDashManifest | null = null;
|
|
347
|
-
private _manifestPromise: Promise<EmDashManifest> | null = null;
|
|
348
|
-
private readonly _manifestCacheKey: string;
|
|
349
|
-
|
|
350
346
|
/**
|
|
351
347
|
* Set to true after FTS indexes have been verified for this worker
|
|
352
348
|
* lifetime so we don't re-scan on every admin request. See
|
|
@@ -410,7 +406,6 @@ export class EmDashRuntime {
|
|
|
410
406
|
this.pipelineFactoryOptions = parts.pipelineFactoryOptions;
|
|
411
407
|
this.runtimeDeps = parts.runtimeDeps;
|
|
412
408
|
this.pipelineRef = parts.pipelineRef;
|
|
413
|
-
this._manifestCacheKey = parts.manifestCacheKey;
|
|
414
409
|
}
|
|
415
410
|
|
|
416
411
|
/**
|
|
@@ -460,7 +455,6 @@ export class EmDashRuntime {
|
|
|
460
455
|
this.enabledPlugins.delete(pluginId);
|
|
461
456
|
await this.rebuildHookPipeline();
|
|
462
457
|
}
|
|
463
|
-
this.invalidateManifest();
|
|
464
458
|
}
|
|
465
459
|
|
|
466
460
|
/**
|
|
@@ -634,6 +628,13 @@ export class EmDashRuntime {
|
|
|
634
628
|
// Initialize database (connects, runs migrations if needed)
|
|
635
629
|
const db = await phase("rt.db", "DB init + migrations", () => EmDashRuntime.getDatabase(deps));
|
|
636
630
|
|
|
631
|
+
// Validate EMDASH_ENCRYPTION_KEY once here so a malformed value
|
|
632
|
+
// surfaces in startup logs instead of as request-time 500s. The key
|
|
633
|
+
// itself is not yet consumed (a follow-up PR adds plugin-secret
|
|
634
|
+
// encryption); validating early just guards against silent
|
|
635
|
+
// misconfiguration.
|
|
636
|
+
await phase("rt.secrets", "Validate encryption key", () => validateEncryptionKeyAtStartup());
|
|
637
|
+
|
|
637
638
|
// FTS verify/repair is deferred off the cold-start hot path.
|
|
638
639
|
// See EmDashRuntime.ensureSearchHealthy().
|
|
639
640
|
|
|
@@ -697,7 +698,7 @@ export class EmDashRuntime {
|
|
|
697
698
|
const devConsolePlugin = definePlugin({
|
|
698
699
|
id: DEV_CONSOLE_EMAIL_PLUGIN_ID,
|
|
699
700
|
version: "0.0.0",
|
|
700
|
-
capabilities: ["email:
|
|
701
|
+
capabilities: ["hooks.email-transport:register"],
|
|
701
702
|
hooks: {
|
|
702
703
|
"email:deliver": {
|
|
703
704
|
exclusive: true,
|
|
@@ -720,7 +721,7 @@ export class EmDashRuntime {
|
|
|
720
721
|
const defaultModeratorPlugin = definePlugin({
|
|
721
722
|
id: DEFAULT_COMMENT_MODERATOR_PLUGIN_ID,
|
|
722
723
|
version: "0.0.0",
|
|
723
|
-
capabilities: ["read
|
|
724
|
+
capabilities: ["users:read"],
|
|
724
725
|
hooks: {
|
|
725
726
|
"comment:moderate": {
|
|
726
727
|
exclusive: true,
|
|
@@ -871,22 +872,6 @@ export class EmDashRuntime {
|
|
|
871
872
|
}
|
|
872
873
|
});
|
|
873
874
|
|
|
874
|
-
// SHA of emdash commit + user config that affects the manifest.
|
|
875
|
-
// COMMIT captures emdash code changes; plugin IDs/versions and i18n
|
|
876
|
-
// capture user astro.config changes (e.g. upgrading a plugin package).
|
|
877
|
-
// DB-driven changes (collections, fields, plugin toggle) go through
|
|
878
|
-
// invalidateManifest(). Sorted for stability across nondeterministic
|
|
879
|
-
// plugin ordering.
|
|
880
|
-
const manifestCacheKey = await hashString(
|
|
881
|
-
[
|
|
882
|
-
COMMIT,
|
|
883
|
-
...deps.plugins.map((p) => `${p.id}@${p.version ?? ""}`).toSorted(),
|
|
884
|
-
...deps.sandboxedPluginEntries.map((e) => `${e.id}@${e.version}`).toSorted(),
|
|
885
|
-
virtualConfig?.i18n?.defaultLocale ?? "",
|
|
886
|
-
(virtualConfig?.i18n?.locales ?? []).toSorted().join(","),
|
|
887
|
-
].join("|"),
|
|
888
|
-
);
|
|
889
|
-
|
|
890
875
|
return new EmDashRuntime({
|
|
891
876
|
db,
|
|
892
877
|
storage,
|
|
@@ -906,7 +891,6 @@ export class EmDashRuntime {
|
|
|
906
891
|
pipelineFactoryOptions,
|
|
907
892
|
runtimeDeps: deps,
|
|
908
893
|
pipelineRef,
|
|
909
|
-
manifestCacheKey,
|
|
910
894
|
});
|
|
911
895
|
}
|
|
912
896
|
|
|
@@ -983,18 +967,15 @@ export class EmDashRuntime {
|
|
|
983
967
|
const dialect = deps.createDialect(dbConfig.config);
|
|
984
968
|
const db = new Kysely<Database>({ dialect, log: kyselyLogOption() });
|
|
985
969
|
|
|
986
|
-
|
|
970
|
+
await runMigrations(db);
|
|
987
971
|
|
|
988
|
-
//
|
|
989
|
-
//
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
// Non-fatal
|
|
996
|
-
}
|
|
997
|
-
}
|
|
972
|
+
// Note: legacy installs may carry a stray `emdash:manifest_cache`
|
|
973
|
+
// row in the options table from versions that persisted a JSON
|
|
974
|
+
// manifest. The runtime no longer reads or writes it. We do not
|
|
975
|
+
// proactively delete it: the row is a few hundred bytes of dead
|
|
976
|
+
// weight and is never on the read path, whereas a one-shot
|
|
977
|
+
// cleanup-flag check costs an extra `options.get()` on every
|
|
978
|
+
// isolate cold boot forever. Cheaper to leave it.
|
|
998
979
|
|
|
999
980
|
// Auto-seed schema if no collections exist and setup hasn't run.
|
|
1000
981
|
// This covers first-load on sites that skip the setup wizard.
|
|
@@ -1256,80 +1237,35 @@ export class EmDashRuntime {
|
|
|
1256
1237
|
// =========================================================================
|
|
1257
1238
|
|
|
1258
1239
|
/**
|
|
1259
|
-
*
|
|
1260
|
-
* fallback for cold starts. Avoids N+1 schema registry queries
|
|
1261
|
-
* on every request.
|
|
1240
|
+
* Build the admin manifest from the live database.
|
|
1262
1241
|
*
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1242
|
+
* Used by the admin UI (sidebar collections, content editor field
|
|
1243
|
+
* dispatch, manifest endpoint) and by WordPress import — it's never
|
|
1244
|
+
* read on a public request, so this isn't on any anonymous hot path.
|
|
1245
|
+
*
|
|
1246
|
+
* No cross-request cache. The previous worker-isolate cache produced
|
|
1247
|
+
* a class of cross-isolate staleness bugs (#776, #873, #876, #877)
|
|
1248
|
+
* because Cloudflare Workers keeps multiple warm isolates per region
|
|
1249
|
+
* and there's no fan-out primitive to invalidate them in step. The
|
|
1250
|
+
* cache existed to amortize an N+1 schema query pattern; now that
|
|
1251
|
+
* `listCollectionsWithFields()` does the same work in two queries,
|
|
1252
|
+
* the rebuild is fast enough to pay on every admin request.
|
|
1253
|
+
*
|
|
1254
|
+
* Within a single request, `requestCached` deduplicates concurrent
|
|
1255
|
+
* callers (the manifest endpoint and an admin SSR template, say).
|
|
1265
1256
|
*/
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
// DO-preview sessions), bypass the module-scoped manifest cache —
|
|
1269
|
-
// its schema may diverge from the configured DB. Plain D1 Sessions
|
|
1270
|
-
// routing does NOT set `dbIsIsolated`, so the cache still applies.
|
|
1271
|
-
if (getRequestContext()?.dbIsIsolated) {
|
|
1272
|
-
return this._buildManifest();
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
if (this._cachedManifest) return this._cachedManifest;
|
|
1276
|
-
|
|
1277
|
-
// DB-persisted cache (1 query instead of N+1 rebuild on cold start).
|
|
1278
|
-
// Keyed by SHA of commit + config to bust on deploys. DB-driven
|
|
1279
|
-
// changes (collections, fields, plugins, taxonomies) go through
|
|
1280
|
-
// invalidateManifest().
|
|
1281
|
-
try {
|
|
1282
|
-
const options = new OptionsRepository(this.db);
|
|
1283
|
-
const cached = await options.get<{ key: string; manifest: EmDashManifest }>(
|
|
1284
|
-
"emdash:manifest_cache",
|
|
1285
|
-
);
|
|
1286
|
-
if (cached && cached.key === this._manifestCacheKey && cached.manifest) {
|
|
1287
|
-
this._cachedManifest = cached.manifest;
|
|
1288
|
-
return cached.manifest;
|
|
1289
|
-
}
|
|
1290
|
-
} catch {
|
|
1291
|
-
// Options table may not exist yet
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
// Full rebuild, then persist. Track which promise is current so
|
|
1295
|
-
// an invalidation during the build can't be overwritten.
|
|
1296
|
-
if (!this._manifestPromise) {
|
|
1297
|
-
let manifestPromise: Promise<EmDashManifest>;
|
|
1298
|
-
const isCurrentLoad = () => this._manifestPromise === manifestPromise;
|
|
1299
|
-
manifestPromise = this._loadManifest(isCurrentLoad);
|
|
1300
|
-
this._manifestPromise = manifestPromise;
|
|
1301
|
-
}
|
|
1302
|
-
return this._manifestPromise;
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
private async _loadManifest(isCurrentLoad: () => boolean): Promise<EmDashManifest> {
|
|
1306
|
-
try {
|
|
1307
|
-
const manifest = await this._buildManifest();
|
|
1308
|
-
|
|
1309
|
-
if (isCurrentLoad()) {
|
|
1310
|
-
this._cachedManifest = manifest;
|
|
1311
|
-
|
|
1312
|
-
try {
|
|
1313
|
-
const options = new OptionsRepository(this.db);
|
|
1314
|
-
await options.set("emdash:manifest_cache", {
|
|
1315
|
-
key: this._manifestCacheKey,
|
|
1316
|
-
manifest,
|
|
1317
|
-
});
|
|
1318
|
-
} catch {
|
|
1319
|
-
// Non-fatal — will just rebuild next time
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
return manifest;
|
|
1324
|
-
} finally {
|
|
1325
|
-
if (isCurrentLoad()) {
|
|
1326
|
-
this._manifestPromise = null;
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1257
|
+
getManifest(): Promise<EmDashManifest> {
|
|
1258
|
+
return requestCached("emdash:manifest", () => this._buildManifest());
|
|
1329
1259
|
}
|
|
1330
1260
|
|
|
1331
1261
|
/**
|
|
1332
|
-
* Build the manifest from database
|
|
1262
|
+
* Build the manifest from the database.
|
|
1263
|
+
*
|
|
1264
|
+
* Constant query shapes via `listCollectionsWithFields()` — one query
|
|
1265
|
+
* for collections, one batched query for fields (chunked at
|
|
1266
|
+
* `SQL_BATCH_SIZE` collection IDs to stay under D1's bound-parameter
|
|
1267
|
+
* limit). Typical sites stay well under the chunk threshold, so this
|
|
1268
|
+
* is two queries in practice; never N+1.
|
|
1333
1269
|
*/
|
|
1334
1270
|
private async _buildManifest(): Promise<EmDashManifest> {
|
|
1335
1271
|
// Build collections from database.
|
|
@@ -1338,9 +1274,8 @@ export class EmDashRuntime {
|
|
|
1338
1274
|
const manifestCollections: Record<string, ManifestCollection> = {};
|
|
1339
1275
|
try {
|
|
1340
1276
|
const registry = new SchemaRegistry(this.db);
|
|
1341
|
-
const dbCollections = await registry.
|
|
1277
|
+
const dbCollections = await registry.listCollectionsWithFields();
|
|
1342
1278
|
for (const collection of dbCollections) {
|
|
1343
|
-
const collectionWithFields = await registry.getCollectionWithFields(collection.slug);
|
|
1344
1279
|
const fields: Record<
|
|
1345
1280
|
string,
|
|
1346
1281
|
{
|
|
@@ -1355,34 +1290,32 @@ export class EmDashRuntime {
|
|
|
1355
1290
|
}
|
|
1356
1291
|
> = {};
|
|
1357
1292
|
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
(entry as Record<string, unknown>).validation = field.validation;
|
|
1383
|
-
}
|
|
1384
|
-
fields[field.slug] = entry;
|
|
1293
|
+
for (const field of collection.fields) {
|
|
1294
|
+
const entry: (typeof fields)[string] = {
|
|
1295
|
+
kind: FIELD_TYPE_TO_KIND[field.type] ?? "string",
|
|
1296
|
+
label: field.label,
|
|
1297
|
+
required: field.required,
|
|
1298
|
+
};
|
|
1299
|
+
if (field.widget) entry.widget = field.widget;
|
|
1300
|
+
// Plugin field widgets read their per-field config from `field.options`,
|
|
1301
|
+
// which the seed schema types as `Record<string, unknown>`. Pass it
|
|
1302
|
+
// through to the manifest so plugin widgets in the admin SPA receive it.
|
|
1303
|
+
if (field.options) {
|
|
1304
|
+
entry.options = field.options;
|
|
1305
|
+
}
|
|
1306
|
+
// Legacy: select/multiSelect enum options live on `field.validation.options`.
|
|
1307
|
+
// Wins over `field.options` to preserve existing behavior for enum widgets.
|
|
1308
|
+
if (field.validation?.options) {
|
|
1309
|
+
entry.options = field.validation.options.map((v) => ({
|
|
1310
|
+
value: v,
|
|
1311
|
+
label: v.charAt(0).toUpperCase() + v.slice(1),
|
|
1312
|
+
}));
|
|
1313
|
+
}
|
|
1314
|
+
// Include full validation for repeater fields (subFields, minItems, maxItems)
|
|
1315
|
+
if (field.type === "repeater" && field.validation) {
|
|
1316
|
+
(entry as Record<string, unknown>).validation = field.validation;
|
|
1385
1317
|
}
|
|
1318
|
+
fields[field.slug] = entry;
|
|
1386
1319
|
}
|
|
1387
1320
|
|
|
1388
1321
|
manifestCollections[collection.slug] = {
|
|
@@ -1419,6 +1352,7 @@ export class EmDashRuntime {
|
|
|
1419
1352
|
description?: string;
|
|
1420
1353
|
placeholder?: string;
|
|
1421
1354
|
fields?: Element[];
|
|
1355
|
+
category?: string;
|
|
1422
1356
|
}>;
|
|
1423
1357
|
fieldWidgets?: Array<{
|
|
1424
1358
|
name: string;
|
|
@@ -1555,27 +1489,6 @@ export class EmDashRuntime {
|
|
|
1555
1489
|
};
|
|
1556
1490
|
}
|
|
1557
1491
|
|
|
1558
|
-
/**
|
|
1559
|
-
* Invalidate cached data derived from the manifest/schema.
|
|
1560
|
-
* Called when collections, fields, plugins, or taxonomy defs change.
|
|
1561
|
-
*/
|
|
1562
|
-
invalidateManifest(): void {
|
|
1563
|
-
this._cachedManifest = null;
|
|
1564
|
-
this._manifestPromise = null;
|
|
1565
|
-
invalidateUrlPatternCache();
|
|
1566
|
-
// Delete DB-persisted cache so the next cold start rebuilds.
|
|
1567
|
-
// Fire-and-forget: in-memory is already cleared for this worker,
|
|
1568
|
-
// DB delete is best-effort for the next cold start.
|
|
1569
|
-
try {
|
|
1570
|
-
const options = new OptionsRepository(this.db);
|
|
1571
|
-
options.delete("emdash:manifest_cache").catch((error) => {
|
|
1572
|
-
console.error("Failed to delete persisted manifest cache", error);
|
|
1573
|
-
});
|
|
1574
|
-
} catch (error) {
|
|
1575
|
-
console.error("Failed to initialize manifest cache invalidation", error);
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
1492
|
/**
|
|
1580
1493
|
* Verify and repair FTS indexes on demand. Runs at most once per worker
|
|
1581
1494
|
* lifetime.
|
|
@@ -1779,6 +1692,14 @@ export class EmDashRuntime {
|
|
|
1779
1692
|
status?: string;
|
|
1780
1693
|
authorId?: string | null;
|
|
1781
1694
|
bylines?: Array<{ bylineId: string; roleLabel?: string | null }>;
|
|
1695
|
+
seo?: {
|
|
1696
|
+
title?: string | null;
|
|
1697
|
+
description?: string | null;
|
|
1698
|
+
image?: string | null;
|
|
1699
|
+
canonical?: string | null;
|
|
1700
|
+
noIndex?: boolean;
|
|
1701
|
+
};
|
|
1702
|
+
publishedAt?: string | null;
|
|
1782
1703
|
/** Skip revision creation (used by autosave) */
|
|
1783
1704
|
skipRevision?: boolean;
|
|
1784
1705
|
_rev?: string;
|
|
@@ -2005,8 +1926,12 @@ export class EmDashRuntime {
|
|
|
2005
1926
|
// Publishing & Scheduling Handlers
|
|
2006
1927
|
// =========================================================================
|
|
2007
1928
|
|
|
2008
|
-
async handleContentPublish(
|
|
2009
|
-
|
|
1929
|
+
async handleContentPublish(
|
|
1930
|
+
collection: string,
|
|
1931
|
+
id: string,
|
|
1932
|
+
options: { publishedAt?: string } = {},
|
|
1933
|
+
) {
|
|
1934
|
+
const result = await handleContentPublish(this.db, collection, id, options);
|
|
2010
1935
|
|
|
2011
1936
|
// Run afterPublish hooks (fire-and-forget)
|
|
2012
1937
|
if (result.success && result.data) {
|
package/src/index.ts
CHANGED
|
@@ -262,6 +262,15 @@ export type {
|
|
|
262
262
|
SerializedRequest,
|
|
263
263
|
} from "./plugins/index.js";
|
|
264
264
|
|
|
265
|
+
// Capability normalization (legacy → canonical alias layer)
|
|
266
|
+
export {
|
|
267
|
+
CAPABILITY_RENAMES,
|
|
268
|
+
isDeprecatedCapability,
|
|
269
|
+
normalizeCapability,
|
|
270
|
+
normalizeCapabilities,
|
|
271
|
+
} from "./plugins/index.js";
|
|
272
|
+
export type { CurrentPluginCapability, DeprecatedPluginCapability } from "./plugins/index.js";
|
|
273
|
+
|
|
265
274
|
// Plugin descriptor (for astro.config.mjs)
|
|
266
275
|
export type { PluginDescriptor } from "./astro/integration/runtime.js";
|
|
267
276
|
|
package/src/loader.ts
CHANGED
|
@@ -115,6 +115,16 @@ const INCLUDE_IN_DATA: Record<string, string> = {
|
|
|
115
115
|
/** System date columns that should be converted to Date objects */
|
|
116
116
|
const DATE_COLUMNS = new Set(["created_at", "updated_at", "published_at", "scheduled_at"]);
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Hidden, symbol-keyed property on each mapped data record carrying the raw
|
|
120
|
+
* DB string for every date column. Lets cursor encoders downstream reproduce
|
|
121
|
+
* the loader's exact `nextCursor` format without round-tripping through
|
|
122
|
+
* `new Date()`, which loses precision for stored values that aren't already
|
|
123
|
+
* ISO-with-milliseconds (e.g. `2026-01-01T00:00:00Z` becomes
|
|
124
|
+
* `2026-01-01T00:00:00.000Z`).
|
|
125
|
+
*/
|
|
126
|
+
export const CURSOR_RAW_VALUES: unique symbol = Symbol("emdash:cursorRawValues");
|
|
127
|
+
|
|
118
128
|
/** Safely extract a string value from a record, returning fallback if not a string */
|
|
119
129
|
function rowStr(row: Record<string, unknown>, key: string, fallback = ""): string {
|
|
120
130
|
const val = row[key];
|
|
@@ -128,13 +138,19 @@ function rowStr(row: Record<string, unknown>, key: string, fallback = ""): strin
|
|
|
128
138
|
*/
|
|
129
139
|
function mapRowToData(row: Record<string, unknown>): Record<string, unknown> {
|
|
130
140
|
const data: Record<string, unknown> = {};
|
|
141
|
+
const rawDateValues: Record<string, string> = {};
|
|
131
142
|
|
|
132
143
|
for (const [key, value] of Object.entries(row)) {
|
|
133
144
|
// Include certain system columns (mapped to camelCase where needed)
|
|
134
145
|
if (key in INCLUDE_IN_DATA) {
|
|
135
146
|
// Convert date columns from ISO strings to Date objects
|
|
136
147
|
if (DATE_COLUMNS.has(key)) {
|
|
137
|
-
|
|
148
|
+
if (typeof value === "string") {
|
|
149
|
+
rawDateValues[key] = value;
|
|
150
|
+
data[INCLUDE_IN_DATA[key]] = new Date(value);
|
|
151
|
+
} else {
|
|
152
|
+
data[INCLUDE_IN_DATA[key]] = null;
|
|
153
|
+
}
|
|
138
154
|
} else {
|
|
139
155
|
data[INCLUDE_IN_DATA[key]] = value;
|
|
140
156
|
}
|
|
@@ -160,6 +176,13 @@ function mapRowToData(row: Record<string, unknown>): Record<string, unknown> {
|
|
|
160
176
|
}
|
|
161
177
|
}
|
|
162
178
|
|
|
179
|
+
Object.defineProperty(data, CURSOR_RAW_VALUES, {
|
|
180
|
+
value: rawDateValues,
|
|
181
|
+
enumerable: false,
|
|
182
|
+
configurable: false,
|
|
183
|
+
writable: false,
|
|
184
|
+
});
|
|
185
|
+
|
|
163
186
|
return data;
|
|
164
187
|
}
|
|
165
188
|
|