emdash 0.8.0 → 0.10.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-BktHA7EO.d.mts} +1 -1
- package/dist/{adapters-BKSf3T9R.d.mts.map → adapters-BktHA7EO.d.mts.map} +1 -1
- package/dist/{apply-x0eMK1lX.mjs → apply-UsrFuO7l.mjs} +207 -355
- package/dist/apply-UsrFuO7l.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 +118 -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 +14 -57
- 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 +15 -10
- 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 +8 -5
- 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 +70 -121
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +25 -10
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{byline-Chbr2GoP.mjs → byline-C3vnhIpU.mjs} +4 -4
- package/dist/{byline-Chbr2GoP.mjs.map → byline-C3vnhIpU.mjs.map} +1 -1
- package/dist/bylines-esI7ioa9.mjs +113 -0
- package/dist/bylines-esI7ioa9.mjs.map +1 -0
- package/dist/cache-fTzxgMFJ.mjs +65 -0
- package/dist/cache-fTzxgMFJ.mjs.map +1 -0
- package/dist/{chunks-HGz06Soa.mjs → chunks-Da2-b-oA.mjs} +8 -2
- package/dist/{chunks-HGz06Soa.mjs.map → chunks-Da2-b-oA.mjs.map} +1 -1
- package/dist/cli/index.mjs +456 -90
- 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-CVssduLe.mjs} +1 -1
- package/dist/{config-BXwuX8Bx.mjs.map → config-CVssduLe.mjs.map} +1 -1
- package/dist/{content-BcQPYxdV.mjs → content-C7G4QXkK.mjs} +42 -14
- package/dist/content-C7G4QXkK.mjs.map +1 -0
- 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-B7P2pSCn.mjs} +1 -1
- package/dist/{db-errors-l1Qh2RPR.mjs.map → db-errors-B7P2pSCn.mjs.map} +1 -1
- package/dist/{default-DCVqE5ib.mjs → default-pHuz9WF6.mjs} +1 -1
- package/dist/{default-DCVqE5ib.mjs.map → default-pHuz9WF6.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-DqnRMM5z.mjs} +1 -1
- package/dist/{error-zG5T1UGA.mjs.map → error-DqnRMM5z.mjs.map} +1 -1
- package/dist/{index-DIb-CzNx.d.mts → index-DjPMOfO0.d.mts} +162 -87
- package/dist/index-DjPMOfO0.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +27 -24
- package/dist/{load-CyEoextb.mjs → load-sXRuM7Us.mjs} +2 -2
- package/dist/{load-CyEoextb.mjs.map → load-sXRuM7Us.mjs.map} +1 -1
- package/dist/{loader-CndGj8kM.mjs → loader-Bx2_9-5e.mjs} +53 -8
- package/dist/loader-Bx2_9-5e.mjs.map +1 -0
- package/dist/{manifest-schema-DH9xhc6t.mjs → manifest-schema-CXAbd1vH.mjs} +33 -3
- package/dist/manifest-schema-CXAbd1vH.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/{mode-BnAOqItE.mjs → mode-YhqNVef_.mjs} +1 -1
- package/dist/{mode-BnAOqItE.mjs.map → mode-YhqNVef_.mjs.map} +1 -1
- package/dist/options-nPxWnrya.mjs +117 -0
- package/dist/options-nPxWnrya.mjs.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/{patterns-CrCYkMBb.mjs → patterns-DsUZ4uxI.mjs} +1 -1
- package/dist/{patterns-CrCYkMBb.mjs.map → patterns-DsUZ4uxI.mjs.map} +1 -1
- package/dist/{placeholder-D29tWZ7o.d.mts → placeholder-CDPtkelt.d.mts} +1 -1
- package/dist/{placeholder-D29tWZ7o.d.mts.map → placeholder-CDPtkelt.d.mts.map} +1 -1
- package/dist/{placeholder-C-fk5hYI.mjs → placeholder-Ci0RLeCk.mjs} +1 -1
- package/dist/{placeholder-C-fk5hYI.mjs.map → placeholder-Ci0RLeCk.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-B1AxbbbQ.mjs +51 -0
- package/dist/public-url-B1AxbbbQ.mjs.map +1 -0
- package/dist/{query-fqEdLFms.mjs → query-Bo-msrmu.mjs} +114 -16
- package/dist/query-Bo-msrmu.mjs.map +1 -0
- package/dist/{redirect-D_pshWdf.mjs → redirect-C5H7VGIX.mjs} +11 -6
- package/dist/redirect-C5H7VGIX.mjs.map +1 -0
- package/dist/{registry-C3Mr0ODu.mjs → registry-Beb7wxFc.mjs} +39 -5
- package/dist/registry-Beb7wxFc.mjs.map +1 -0
- package/dist/{request-cache-Ci7f5pBb.mjs → request-cache-C-tIpYIw.mjs} +1 -1
- package/dist/{request-cache-Ci7f5pBb.mjs.map → request-cache-C-tIpYIw.mjs.map} +1 -1
- package/dist/runner-Clwe4Mme.d.mts +44 -0
- package/dist/runner-Clwe4Mme.d.mts.map +1 -0
- package/dist/{runner-tQ7BJ4T7.mjs → runner-DMnlIkh4.mjs} +616 -191
- package/dist/runner-DMnlIkh4.mjs.map +1 -0
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +2 -2
- package/dist/{search-BoZYFuUk.mjs → search-DkN-BqsS.mjs} +270 -152
- package/dist/search-DkN-BqsS.mjs.map +1 -0
- package/dist/secrets-CZ8rxLX3.mjs +314 -0
- package/dist/secrets-CZ8rxLX3.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +13 -11
- 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-CTtewrSQ.mjs +407 -0
- package/dist/taxonomies-CTtewrSQ.mjs.map +1 -0
- package/dist/taxonomy-DSxx2K2L.mjs +218 -0
- package/dist/taxonomy-DSxx2K2L.mjs.map +1 -0
- package/dist/{tokens-D9vnZqYS.mjs → tokens-CyRDPVW2.mjs} +1 -1
- package/dist/{tokens-D9vnZqYS.mjs.map → tokens-CyRDPVW2.mjs.map} +1 -1
- package/dist/{transaction-Cn2rjY78.mjs → transaction-D44LBXvU.mjs} +1 -1
- package/dist/{transaction-Cn2rjY78.mjs.map → transaction-D44LBXvU.mjs.map} +1 -1
- package/dist/{transport-CUnEL3Vs.d.mts → transport-DX_5rpsq.d.mts} +1 -1
- package/dist/{transport-CUnEL3Vs.d.mts.map → transport-DX_5rpsq.d.mts.map} +1 -1
- package/dist/{transport-C9ugt2Nr.mjs → transport-xpzIjCIB.mjs} +6 -5
- package/dist/{transport-C9ugt2Nr.mjs.map → transport-xpzIjCIB.mjs.map} +1 -1
- package/dist/{types-BrA0xf5I.d.mts → types-B_CXXnzh.d.mts} +1 -1
- package/dist/{types-BrA0xf5I.d.mts.map → types-B_CXXnzh.d.mts.map} +1 -1
- package/dist/{types-DIMwPFub.d.mts → types-C-aFbqmA.d.mts} +1 -1
- package/dist/{types-DIMwPFub.d.mts.map → types-C-aFbqmA.d.mts.map} +1 -1
- package/dist/types-CoO6mpV3.mjs +68 -0
- package/dist/types-CoO6mpV3.mjs.map +1 -0
- package/dist/{types-i36XcA_X.d.mts → types-D19uBYWn.d.mts} +83 -7
- package/dist/types-D19uBYWn.d.mts.map +1 -0
- package/dist/{types-BmPPSUEx.d.mts → types-Dl1fgFjn.d.mts} +24 -2
- package/dist/{types-BmPPSUEx.d.mts.map → types-Dl1fgFjn.d.mts.map} +1 -1
- package/dist/{types-CS8FIX7L.d.mts → types-Dtx1mSMX.d.mts} +9 -1
- package/dist/types-Dtx1mSMX.d.mts.map +1 -0
- package/dist/{types-Bm1dn-q3.mjs → types-Eg829jj9.mjs} +1 -1
- package/dist/{types-Bm1dn-q3.mjs.map → types-Eg829jj9.mjs.map} +1 -1
- package/dist/{types-CgqmmMJB.mjs → types-K-EkEQCI.mjs} +1 -1
- package/dist/{types-CgqmmMJB.mjs.map → types-K-EkEQCI.mjs.map} +1 -1
- package/dist/{validate-CxVsLehf.mjs → validate-CBIbxM3L.mjs} +14 -10
- package/dist/validate-CBIbxM3L.mjs.map +1 -0
- package/dist/{validate-DHxmpFJt.d.mts → validate-DHGwADqO.d.mts} +18 -5
- package/dist/validate-DHGwADqO.d.mts.map +1 -0
- package/dist/{validation-C-ZpN2GI.mjs → validation-B1NYiEos.mjs} +6 -6
- package/dist/{validation-C-ZpN2GI.mjs.map → validation-B1NYiEos.mjs.map} +1 -1
- package/dist/version-CMD42IRC.mjs +7 -0
- package/dist/{version-Bbq8TCrz.mjs.map → version-CMD42IRC.mjs.map} +1 -1
- package/dist/{zod-generator-CpwccCIv.mjs → zod-generator-BNJDQBSZ.mjs} +11 -6
- package/dist/{zod-generator-CpwccCIv.mjs.map → zod-generator-BNJDQBSZ.mjs.map} +1 -1
- 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 +40 -1
- package/src/api/handlers/dashboard.ts +29 -36
- package/src/api/handlers/device-flow.ts +5 -0
- package/src/api/handlers/marketplace.ts +11 -4
- package/src/api/handlers/menus.ts +256 -75
- package/src/api/handlers/oauth-authorization.ts +72 -33
- package/src/api/handlers/revision.ts +23 -14
- package/src/api/handlers/taxonomies.ts +273 -100
- package/src/api/public-url.ts +48 -2
- package/src/api/schemas/comments.ts +2 -2
- package/src/api/schemas/common.ts +7 -0
- package/src/api/schemas/content.ts +17 -0
- package/src/api/schemas/menus.ts +23 -0
- package/src/api/schemas/sections.ts +3 -3
- package/src/api/schemas/taxonomies.ts +39 -0
- 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/routes.ts +10 -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]/permanent.ts +1 -1
- 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/rewrite-url-helpers.ts +196 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +9 -177
- 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/menus/[name]/items.ts +16 -6
- package/src/astro/routes/api/menus/[name]/reorder.ts +8 -3
- package/src/astro/routes/api/menus/[name]/translations.ts +82 -0
- package/src/astro/routes/api/menus/[name].ts +19 -10
- package/src/astro/routes/api/menus/index.ts +9 -6
- 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/[name]/terms/[slug]/translations.ts +89 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +22 -22
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +11 -14
- package/src/astro/routes/api/taxonomies/index.ts +9 -7
- 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/export-seed.ts +82 -21
- package/src/cli/commands/login.ts +8 -1
- package/src/cli/commands/plugin-init.ts +216 -90
- 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/036_i18n_menus_and_taxonomies.ts +477 -0
- package/src/database/migrations/runner.ts +158 -23
- package/src/database/repositories/content.ts +47 -12
- package/src/database/repositories/redirect.ts +14 -3
- package/src/database/repositories/taxonomy.ts +212 -82
- package/src/database/types.ts +10 -2
- package/src/db/libsql.ts +1 -3
- package/src/db/sqlite.ts +2 -5
- package/src/emdash-runtime.ts +84 -159
- package/src/i18n/resolve.ts +37 -0
- package/src/index.ts +9 -0
- package/src/loader.ts +73 -3
- package/src/mcp/server.ts +180 -54
- package/src/menus/index.ts +143 -124
- package/src/menus/types.ts +15 -1
- 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 +39 -7
- package/src/seed/apply.ts +142 -54
- package/src/seed/types.ts +14 -1
- package/src/seed/validate.ts +27 -13
- package/src/settings/index.ts +80 -6
- package/src/settings/types.ts +23 -1
- package/src/taxonomies/index.ts +237 -210
- package/src/taxonomies/types.ts +10 -0
- 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/content-BcQPYxdV.mjs.map +0 -1
- 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/runner-tQ7BJ4T7.mjs.map +0 -1
- package/dist/search-BoZYFuUk.mjs.map +0 -1
- package/dist/taxonomies-B4IAshV8.mjs +0 -308
- package/dist/taxonomies-B4IAshV8.mjs.map +0 -1
- package/dist/types-CS8FIX7L.d.mts.map +0 -1
- package/dist/types-i36XcA_X.d.mts.map +0 -1
- package/dist/validate-CxVsLehf.mjs.map +0 -1
- package/dist/validate-DHxmpFJt.d.mts.map +0 -1
- package/dist/version-Bbq8TCrz.mjs +0 -7
|
@@ -1,12 +1,30 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-ClPoSABd.mjs";
|
|
2
1
|
import { t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
|
|
3
|
-
import {
|
|
2
|
+
import { c as listTablesLike, i as currentTimestampValue, n as columnExists, o as isSqlite, r as currentTimestamp, t as binaryType } from "./dialect-helpers-BKCvISIQ.mjs";
|
|
3
|
+
import { n as getI18nConfig } from "./config-CVssduLe.mjs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
4
5
|
import { Migrator, sql } from "kysely";
|
|
5
6
|
|
|
7
|
+
//#region \0rolldown/runtime.js
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __exportAll = (all, no_symbols) => {
|
|
10
|
+
let target = {};
|
|
11
|
+
for (var name in all) {
|
|
12
|
+
__defProp(target, name, {
|
|
13
|
+
get: all[name],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
if (!no_symbols) {
|
|
18
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
19
|
+
}
|
|
20
|
+
return target;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
6
24
|
//#region src/database/migrations/001_initial.ts
|
|
7
25
|
var _001_initial_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
-
down: () => down$
|
|
9
|
-
up: () => up$
|
|
26
|
+
down: () => down$34,
|
|
27
|
+
up: () => up$34
|
|
10
28
|
});
|
|
11
29
|
/**
|
|
12
30
|
* Initial schema migration
|
|
@@ -15,7 +33,7 @@ var _001_initial_exports = /* @__PURE__ */ __exportAll({
|
|
|
15
33
|
* by the SchemaRegistry when collections are added via the admin UI.
|
|
16
34
|
* This migration only creates system tables.
|
|
17
35
|
*/
|
|
18
|
-
async function up$
|
|
36
|
+
async function up$34(db) {
|
|
19
37
|
await db.schema.createTable("revisions").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection", "text", (col) => col.notNull()).addColumn("entry_id", "text", (col) => col.notNull()).addColumn("data", "text", (col) => col.notNull()).addColumn("author_id", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
20
38
|
await db.schema.createIndex("idx_revisions_entry").ifNotExists().on("revisions").columns(["collection", "entry_id"]).execute();
|
|
21
39
|
await db.schema.createTable("taxonomies").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull()).addColumn("slug", "text", (col) => col.notNull()).addColumn("label", "text", (col) => col.notNull()).addColumn("parent_id", "text").addColumn("data", "text").addUniqueConstraint("taxonomies_name_slug_unique", ["name", "slug"]).addForeignKeyConstraint("taxonomies_parent_fk", ["parent_id"], "taxonomies", ["id"], (cb) => cb.onDelete("set null")).execute();
|
|
@@ -35,7 +53,7 @@ async function up$33(db) {
|
|
|
35
53
|
await db.schema.createIndex("idx_audit_action").ifNotExists().on("audit_logs").column("action").execute();
|
|
36
54
|
await db.schema.createIndex("idx_audit_timestamp").ifNotExists().on("audit_logs").column("timestamp").execute();
|
|
37
55
|
}
|
|
38
|
-
async function down$
|
|
56
|
+
async function down$34(db) {
|
|
39
57
|
await db.schema.dropTable("audit_logs").execute();
|
|
40
58
|
await db.schema.dropTable("options").execute();
|
|
41
59
|
await db.schema.dropTable("users").execute();
|
|
@@ -48,26 +66,26 @@ async function down$33(db) {
|
|
|
48
66
|
//#endregion
|
|
49
67
|
//#region src/database/migrations/002_media_status.ts
|
|
50
68
|
var _002_media_status_exports = /* @__PURE__ */ __exportAll({
|
|
51
|
-
down: () => down$
|
|
52
|
-
up: () => up$
|
|
69
|
+
down: () => down$33,
|
|
70
|
+
up: () => up$33
|
|
53
71
|
});
|
|
54
72
|
/**
|
|
55
73
|
* Add status column to media table for tracking upload state.
|
|
56
74
|
* Status values: 'pending' | 'ready' | 'failed'
|
|
57
75
|
*/
|
|
58
|
-
async function up$
|
|
76
|
+
async function up$33(db) {
|
|
59
77
|
await db.schema.alterTable("media").addColumn("status", "text", (col) => col.notNull().defaultTo("ready")).execute();
|
|
60
78
|
await db.schema.createIndex("idx_media_status").on("media").column("status").execute();
|
|
61
79
|
}
|
|
62
|
-
async function down$
|
|
80
|
+
async function down$33(db) {
|
|
63
81
|
await db.schema.dropIndex("idx_media_status").execute();
|
|
64
82
|
}
|
|
65
83
|
|
|
66
84
|
//#endregion
|
|
67
85
|
//#region src/database/migrations/003_schema_registry.ts
|
|
68
86
|
var _003_schema_registry_exports = /* @__PURE__ */ __exportAll({
|
|
69
|
-
down: () => down$
|
|
70
|
-
up: () => up$
|
|
87
|
+
down: () => down$32,
|
|
88
|
+
up: () => up$32
|
|
71
89
|
});
|
|
72
90
|
/**
|
|
73
91
|
* Migration: Schema Registry Tables
|
|
@@ -75,14 +93,14 @@ var _003_schema_registry_exports = /* @__PURE__ */ __exportAll({
|
|
|
75
93
|
* Creates the schema registry tables that store collection and field definitions.
|
|
76
94
|
* This enables dynamic schema management where D1 is the source of truth.
|
|
77
95
|
*/
|
|
78
|
-
async function up$
|
|
96
|
+
async function up$32(db) {
|
|
79
97
|
await db.schema.createTable("_emdash_collections").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("label_singular", "text").addColumn("description", "text").addColumn("icon", "text").addColumn("supports", "text").addColumn("source", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
80
98
|
await db.schema.createTable("_emdash_fields").addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection_id", "text", (col) => col.notNull()).addColumn("slug", "text", (col) => col.notNull()).addColumn("label", "text", (col) => col.notNull()).addColumn("type", "text", (col) => col.notNull()).addColumn("column_type", "text", (col) => col.notNull()).addColumn("required", "integer", (col) => col.defaultTo(0)).addColumn("unique", "integer", (col) => col.defaultTo(0)).addColumn("default_value", "text").addColumn("validation", "text").addColumn("widget", "text").addColumn("options", "text").addColumn("sort_order", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("fields_collection_fk", ["collection_id"], "_emdash_collections", ["id"], (cb) => cb.onDelete("cascade")).execute();
|
|
81
99
|
await db.schema.createIndex("idx_fields_collection_slug").on("_emdash_fields").columns(["collection_id", "slug"]).unique().execute();
|
|
82
100
|
await db.schema.createIndex("idx_fields_collection").on("_emdash_fields").column("collection_id").execute();
|
|
83
101
|
await db.schema.createIndex("idx_fields_sort").on("_emdash_fields").columns(["collection_id", "sort_order"]).execute();
|
|
84
102
|
}
|
|
85
|
-
async function down$
|
|
103
|
+
async function down$32(db) {
|
|
86
104
|
await db.schema.dropTable("_emdash_fields").execute();
|
|
87
105
|
await db.schema.dropTable("_emdash_collections").execute();
|
|
88
106
|
}
|
|
@@ -90,8 +108,8 @@ async function down$31(db) {
|
|
|
90
108
|
//#endregion
|
|
91
109
|
//#region src/database/migrations/004_plugins.ts
|
|
92
110
|
var _004_plugins_exports = /* @__PURE__ */ __exportAll({
|
|
93
|
-
down: () => down$
|
|
94
|
-
up: () => up$
|
|
111
|
+
down: () => down$31,
|
|
112
|
+
up: () => up$31
|
|
95
113
|
});
|
|
96
114
|
/**
|
|
97
115
|
* Migration: Plugin System Tables
|
|
@@ -101,7 +119,7 @@ var _004_plugins_exports = /* @__PURE__ */ __exportAll({
|
|
|
101
119
|
*
|
|
102
120
|
* @see PLUGIN-SYSTEM.md § Plugin Storage
|
|
103
121
|
*/
|
|
104
|
-
async function up$
|
|
122
|
+
async function up$31(db) {
|
|
105
123
|
await db.schema.createTable("_plugin_storage").addColumn("plugin_id", "text", (col) => col.notNull()).addColumn("collection", "text", (col) => col.notNull()).addColumn("id", "text", (col) => col.notNull()).addColumn("data", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("pk_plugin_storage", [
|
|
106
124
|
"plugin_id",
|
|
107
125
|
"collection",
|
|
@@ -119,7 +137,7 @@ async function up$30(db) {
|
|
|
119
137
|
"index_name"
|
|
120
138
|
]).execute();
|
|
121
139
|
}
|
|
122
|
-
async function down$
|
|
140
|
+
async function down$31(db) {
|
|
123
141
|
await db.schema.dropTable("_plugin_indexes").execute();
|
|
124
142
|
await db.schema.dropTable("_plugin_state").execute();
|
|
125
143
|
await db.schema.dropTable("_plugin_storage").execute();
|
|
@@ -128,8 +146,8 @@ async function down$30(db) {
|
|
|
128
146
|
//#endregion
|
|
129
147
|
//#region src/database/migrations/005_menus.ts
|
|
130
148
|
var _005_menus_exports = /* @__PURE__ */ __exportAll({
|
|
131
|
-
down: () => down$
|
|
132
|
-
up: () => up$
|
|
149
|
+
down: () => down$30,
|
|
150
|
+
up: () => up$30
|
|
133
151
|
});
|
|
134
152
|
/**
|
|
135
153
|
* Navigation Menus migration
|
|
@@ -137,13 +155,13 @@ var _005_menus_exports = /* @__PURE__ */ __exportAll({
|
|
|
137
155
|
* Creates tables for admin-editable navigation menus.
|
|
138
156
|
* Menu items can reference content entries, taxonomy terms, or custom URLs.
|
|
139
157
|
*/
|
|
140
|
-
async function up$
|
|
158
|
+
async function up$30(db) {
|
|
141
159
|
await db.schema.createTable("_emdash_menus").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
142
160
|
await db.schema.createTable("_emdash_menu_items").addColumn("id", "text", (col) => col.primaryKey()).addColumn("menu_id", "text", (col) => col.notNull()).addColumn("parent_id", "text").addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0)).addColumn("type", "text", (col) => col.notNull()).addColumn("reference_collection", "text").addColumn("reference_id", "text").addColumn("custom_url", "text").addColumn("label", "text", (col) => col.notNull()).addColumn("title_attr", "text").addColumn("target", "text").addColumn("css_classes", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("menu_items_menu_fk", ["menu_id"], "_emdash_menus", ["id"], (cb) => cb.onDelete("cascade")).addForeignKeyConstraint("menu_items_parent_fk", ["parent_id"], "_emdash_menu_items", ["id"], (cb) => cb.onDelete("cascade")).execute();
|
|
143
161
|
await db.schema.createIndex("idx_menu_items_menu").on("_emdash_menu_items").columns(["menu_id", "sort_order"]).execute();
|
|
144
162
|
await db.schema.createIndex("idx_menu_items_parent").on("_emdash_menu_items").column("parent_id").execute();
|
|
145
163
|
}
|
|
146
|
-
async function down$
|
|
164
|
+
async function down$30(db) {
|
|
147
165
|
await db.schema.dropTable("_emdash_menu_items").execute();
|
|
148
166
|
await db.schema.dropTable("_emdash_menus").execute();
|
|
149
167
|
}
|
|
@@ -151,8 +169,8 @@ async function down$29(db) {
|
|
|
151
169
|
//#endregion
|
|
152
170
|
//#region src/database/migrations/006_taxonomy_defs.ts
|
|
153
171
|
var _006_taxonomy_defs_exports = /* @__PURE__ */ __exportAll({
|
|
154
|
-
down: () => down$
|
|
155
|
-
up: () => up$
|
|
172
|
+
down: () => down$29,
|
|
173
|
+
up: () => up$29
|
|
156
174
|
});
|
|
157
175
|
/**
|
|
158
176
|
* Taxonomy definitions migration
|
|
@@ -160,7 +178,7 @@ var _006_taxonomy_defs_exports = /* @__PURE__ */ __exportAll({
|
|
|
160
178
|
* Adds _emdash_taxonomy_defs table to store taxonomy definitions (category, tag, custom)
|
|
161
179
|
* and seeds default category and tag taxonomies.
|
|
162
180
|
*/
|
|
163
|
-
async function up$
|
|
181
|
+
async function up$29(db) {
|
|
164
182
|
await db.schema.createTable("_emdash_taxonomy_defs").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("label_singular", "text").addColumn("hierarchical", "integer", (col) => col.defaultTo(0)).addColumn("collections", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
165
183
|
await db.insertInto("_emdash_taxonomy_defs").values([{
|
|
166
184
|
id: "taxdef_category",
|
|
@@ -178,22 +196,22 @@ async function up$28(db) {
|
|
|
178
196
|
collections: JSON.stringify(["posts"])
|
|
179
197
|
}]).execute();
|
|
180
198
|
}
|
|
181
|
-
async function down$
|
|
199
|
+
async function down$29(db) {
|
|
182
200
|
await db.schema.dropTable("_emdash_taxonomy_defs").execute();
|
|
183
201
|
}
|
|
184
202
|
|
|
185
203
|
//#endregion
|
|
186
204
|
//#region src/database/migrations/007_widgets.ts
|
|
187
205
|
var _007_widgets_exports = /* @__PURE__ */ __exportAll({
|
|
188
|
-
down: () => down$
|
|
189
|
-
up: () => up$
|
|
206
|
+
down: () => down$28,
|
|
207
|
+
up: () => up$28
|
|
190
208
|
});
|
|
191
|
-
async function up$
|
|
209
|
+
async function up$28(db) {
|
|
192
210
|
await db.schema.createTable("_emdash_widget_areas").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("description", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
193
211
|
await db.schema.createTable("_emdash_widgets").addColumn("id", "text", (col) => col.primaryKey()).addColumn("area_id", "text", (col) => col.notNull().references("_emdash_widget_areas.id").onDelete("cascade")).addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0)).addColumn("type", "text", (col) => col.notNull()).addColumn("title", "text").addColumn("content", "text").addColumn("menu_name", "text").addColumn("component_id", "text").addColumn("component_props", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
194
212
|
await db.schema.createIndex("idx_widgets_area").on("_emdash_widgets").columns(["area_id", "sort_order"]).execute();
|
|
195
213
|
}
|
|
196
|
-
async function down$
|
|
214
|
+
async function down$28(db) {
|
|
197
215
|
await db.schema.dropTable("_emdash_widgets").execute();
|
|
198
216
|
await db.schema.dropTable("_emdash_widget_areas").execute();
|
|
199
217
|
}
|
|
@@ -201,8 +219,8 @@ async function down$27(db) {
|
|
|
201
219
|
//#endregion
|
|
202
220
|
//#region src/database/migrations/008_auth.ts
|
|
203
221
|
var _008_auth_exports = /* @__PURE__ */ __exportAll({
|
|
204
|
-
down: () => down$
|
|
205
|
-
up: () => up$
|
|
222
|
+
down: () => down$27,
|
|
223
|
+
up: () => up$27
|
|
206
224
|
});
|
|
207
225
|
/**
|
|
208
226
|
* Auth migration - passkey-first authentication
|
|
@@ -216,7 +234,7 @@ var _008_auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
216
234
|
* - Creates oauth_accounts table (external provider links)
|
|
217
235
|
* - Creates allowed_domains table (self-signup)
|
|
218
236
|
*/
|
|
219
|
-
async function up$
|
|
237
|
+
async function up$27(db) {
|
|
220
238
|
await db.schema.createTable("users_new").addColumn("id", "text", (col) => col.primaryKey()).addColumn("email", "text", (col) => col.notNull().unique()).addColumn("name", "text").addColumn("avatar_url", "text").addColumn("role", "integer", (col) => col.notNull().defaultTo(10)).addColumn("email_verified", "integer", (col) => col.notNull().defaultTo(0)).addColumn("data", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
221
239
|
await sql`
|
|
222
240
|
INSERT INTO users_new (id, email, name, role, data, created_at, updated_at)
|
|
@@ -249,7 +267,7 @@ async function up$26(db) {
|
|
|
249
267
|
await db.schema.createTable("auth_challenges").addColumn("challenge", "text", (col) => col.primaryKey()).addColumn("type", "text", (col) => col.notNull()).addColumn("user_id", "text").addColumn("data", "text").addColumn("expires_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
250
268
|
await db.schema.createIndex("idx_auth_challenges_expires").on("auth_challenges").column("expires_at").execute();
|
|
251
269
|
}
|
|
252
|
-
async function down$
|
|
270
|
+
async function down$27(db) {
|
|
253
271
|
await db.schema.dropTable("auth_challenges").execute();
|
|
254
272
|
await db.schema.dropTable("allowed_domains").execute();
|
|
255
273
|
await db.schema.dropTable("oauth_accounts").execute();
|
|
@@ -282,8 +300,8 @@ async function down$26(db) {
|
|
|
282
300
|
//#endregion
|
|
283
301
|
//#region src/database/migrations/009_user_disabled.ts
|
|
284
302
|
var _009_user_disabled_exports = /* @__PURE__ */ __exportAll({
|
|
285
|
-
down: () => down$
|
|
286
|
-
up: () => up$
|
|
303
|
+
down: () => down$26,
|
|
304
|
+
up: () => up$26
|
|
287
305
|
});
|
|
288
306
|
/**
|
|
289
307
|
* User disabled column - for soft-disabling users
|
|
@@ -292,19 +310,19 @@ var _009_user_disabled_exports = /* @__PURE__ */ __exportAll({
|
|
|
292
310
|
* - Adds disabled column to users table (INTEGER, default 0)
|
|
293
311
|
* - Disabled users cannot log in
|
|
294
312
|
*/
|
|
295
|
-
async function up$
|
|
313
|
+
async function up$26(db) {
|
|
296
314
|
await sql`ALTER TABLE users ADD COLUMN disabled INTEGER NOT NULL DEFAULT 0`.execute(db);
|
|
297
315
|
await db.schema.createIndex("idx_users_disabled").on("users").column("disabled").execute();
|
|
298
316
|
}
|
|
299
|
-
async function down$
|
|
317
|
+
async function down$26(db) {
|
|
300
318
|
await db.schema.dropIndex("idx_users_disabled").execute();
|
|
301
319
|
}
|
|
302
320
|
|
|
303
321
|
//#endregion
|
|
304
322
|
//#region src/database/migrations/011_sections.ts
|
|
305
323
|
var _011_sections_exports = /* @__PURE__ */ __exportAll({
|
|
306
|
-
down: () => down$
|
|
307
|
-
up: () => up$
|
|
324
|
+
down: () => down$25,
|
|
325
|
+
up: () => up$25
|
|
308
326
|
});
|
|
309
327
|
/**
|
|
310
328
|
* Migration: Add sections tables and performance indexes
|
|
@@ -313,13 +331,13 @@ var _011_sections_exports = /* @__PURE__ */ __exportAll({
|
|
|
313
331
|
* They provide a library of pre-built page sections (heroes, CTAs, testimonials, etc.)
|
|
314
332
|
* that content authors can browse and insert with a single click.
|
|
315
333
|
*/
|
|
316
|
-
async function up$
|
|
334
|
+
async function up$25(db) {
|
|
317
335
|
await db.schema.createTable("_emdash_section_categories").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("sort_order", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
318
336
|
await db.schema.createTable("_emdash_sections").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("title", "text", (col) => col.notNull()).addColumn("description", "text").addColumn("category_id", "text", (col) => col.references("_emdash_section_categories.id").onDelete("set null")).addColumn("keywords", "text").addColumn("content", "text", (col) => col.notNull()).addColumn("preview_media_id", "text").addColumn("source", "text", (col) => col.notNull().defaultTo("user")).addColumn("theme_id", "text").addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).addColumn("updated_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
319
337
|
await db.schema.createIndex("idx_sections_category").on("_emdash_sections").columns(["category_id"]).execute();
|
|
320
338
|
await db.schema.createIndex("idx_sections_source").on("_emdash_sections").columns(["source"]).execute();
|
|
321
339
|
}
|
|
322
|
-
async function down$
|
|
340
|
+
async function down$25(db) {
|
|
323
341
|
await db.schema.dropIndex("idx_sections_source").execute();
|
|
324
342
|
await db.schema.dropIndex("idx_sections_category").execute();
|
|
325
343
|
await db.schema.dropTable("_emdash_sections").execute();
|
|
@@ -329,8 +347,8 @@ async function down$24(db) {
|
|
|
329
347
|
//#endregion
|
|
330
348
|
//#region src/database/migrations/012_search.ts
|
|
331
349
|
var _012_search_exports = /* @__PURE__ */ __exportAll({
|
|
332
|
-
down: () => down$
|
|
333
|
-
up: () => up$
|
|
350
|
+
down: () => down$24,
|
|
351
|
+
up: () => up$24
|
|
334
352
|
});
|
|
335
353
|
/**
|
|
336
354
|
* Migration: Search Support
|
|
@@ -338,11 +356,11 @@ var _012_search_exports = /* @__PURE__ */ __exportAll({
|
|
|
338
356
|
* Adds search configuration to collections and searchable flag to fields.
|
|
339
357
|
* FTS5 tables are created dynamically when search is enabled for a collection.
|
|
340
358
|
*/
|
|
341
|
-
async function up$
|
|
359
|
+
async function up$24(db) {
|
|
342
360
|
await db.schema.alterTable("_emdash_collections").addColumn("search_config", "text").execute();
|
|
343
361
|
await db.schema.alterTable("_emdash_fields").addColumn("searchable", "integer", (col) => col.defaultTo(0)).execute();
|
|
344
362
|
}
|
|
345
|
-
async function down$
|
|
363
|
+
async function down$24(db) {
|
|
346
364
|
await db.schema.alterTable("_emdash_fields").dropColumn("searchable").execute();
|
|
347
365
|
await db.schema.alterTable("_emdash_collections").dropColumn("search_config").execute();
|
|
348
366
|
}
|
|
@@ -350,8 +368,8 @@ async function down$23(db) {
|
|
|
350
368
|
//#endregion
|
|
351
369
|
//#region src/database/migrations/013_scheduled_publishing.ts
|
|
352
370
|
var _013_scheduled_publishing_exports = /* @__PURE__ */ __exportAll({
|
|
353
|
-
down: () => down$
|
|
354
|
-
up: () => up$
|
|
371
|
+
down: () => down$23,
|
|
372
|
+
up: () => up$23
|
|
355
373
|
});
|
|
356
374
|
/**
|
|
357
375
|
* Migration: Add scheduled publishing support
|
|
@@ -360,7 +378,7 @@ var _013_scheduled_publishing_exports = /* @__PURE__ */ __exportAll({
|
|
|
360
378
|
* When scheduled_at is set and status is 'scheduled', the content
|
|
361
379
|
* will be auto-published when the scheduled time is reached.
|
|
362
380
|
*/
|
|
363
|
-
async function up$
|
|
381
|
+
async function up$23(db) {
|
|
364
382
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
365
383
|
for (const tableName of tableNames) {
|
|
366
384
|
const table = { name: tableName };
|
|
@@ -375,7 +393,7 @@ async function up$22(db) {
|
|
|
375
393
|
`.execute(db);
|
|
376
394
|
}
|
|
377
395
|
}
|
|
378
|
-
async function down$
|
|
396
|
+
async function down$23(db) {
|
|
379
397
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
380
398
|
for (const tableName of tableNames) {
|
|
381
399
|
const table = { name: tableName };
|
|
@@ -392,10 +410,10 @@ async function down$22(db) {
|
|
|
392
410
|
//#endregion
|
|
393
411
|
//#region src/database/migrations/014_draft_revisions.ts
|
|
394
412
|
var _014_draft_revisions_exports = /* @__PURE__ */ __exportAll({
|
|
395
|
-
down: () => down$
|
|
396
|
-
up: () => up$
|
|
413
|
+
down: () => down$22,
|
|
414
|
+
up: () => up$22
|
|
397
415
|
});
|
|
398
|
-
async function up$
|
|
416
|
+
async function up$22(db) {
|
|
399
417
|
const tables = await db.selectFrom("_emdash_collections").select("slug").execute();
|
|
400
418
|
for (const row of tables) {
|
|
401
419
|
const tableName = `ec_${row.slug}`;
|
|
@@ -417,7 +435,7 @@ async function up$21(db) {
|
|
|
417
435
|
`.execute(db);
|
|
418
436
|
}
|
|
419
437
|
}
|
|
420
|
-
async function down$
|
|
438
|
+
async function down$22(db) {
|
|
421
439
|
const tables = await db.selectFrom("_emdash_collections").select("slug").execute();
|
|
422
440
|
for (const row of tables) {
|
|
423
441
|
const tableName = `ec_${row.slug}`;
|
|
@@ -441,8 +459,8 @@ async function down$21(db) {
|
|
|
441
459
|
//#endregion
|
|
442
460
|
//#region src/database/migrations/015_indexes.ts
|
|
443
461
|
var _015_indexes_exports = /* @__PURE__ */ __exportAll({
|
|
444
|
-
down: () => down$
|
|
445
|
-
up: () => up$
|
|
462
|
+
down: () => down$21,
|
|
463
|
+
up: () => up$21
|
|
446
464
|
});
|
|
447
465
|
/**
|
|
448
466
|
* Add performance indexes for common query patterns.
|
|
@@ -455,7 +473,7 @@ var _015_indexes_exports = /* @__PURE__ */ __exportAll({
|
|
|
455
473
|
* 5. Retroactive author_id + updated_at on existing ec_* content tables
|
|
456
474
|
* (new tables get these from createContentTable() in registry.ts)
|
|
457
475
|
*/
|
|
458
|
-
async function up$
|
|
476
|
+
async function up$21(db) {
|
|
459
477
|
await db.schema.createIndex("idx_media_mime_type").on("media").column("mime_type").execute();
|
|
460
478
|
await db.schema.createIndex("idx_media_filename").on("media").column("filename").execute();
|
|
461
479
|
await db.schema.createIndex("idx_media_created_at").on("media").column("created_at").execute();
|
|
@@ -475,7 +493,7 @@ async function up$20(db) {
|
|
|
475
493
|
`.execute(db);
|
|
476
494
|
}
|
|
477
495
|
}
|
|
478
|
-
async function down$
|
|
496
|
+
async function down$21(db) {
|
|
479
497
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
480
498
|
for (const tableName of tableNames) {
|
|
481
499
|
const table = { name: tableName };
|
|
@@ -493,8 +511,8 @@ async function down$20(db) {
|
|
|
493
511
|
//#endregion
|
|
494
512
|
//#region src/database/migrations/016_api_tokens.ts
|
|
495
513
|
var _016_api_tokens_exports = /* @__PURE__ */ __exportAll({
|
|
496
|
-
down: () => down$
|
|
497
|
-
up: () => up$
|
|
514
|
+
down: () => down$20,
|
|
515
|
+
up: () => up$20
|
|
498
516
|
});
|
|
499
517
|
/**
|
|
500
518
|
* API token tables for programmatic access.
|
|
@@ -504,7 +522,7 @@ var _016_api_tokens_exports = /* @__PURE__ */ __exportAll({
|
|
|
504
522
|
* 2. _emdash_oauth_tokens — OAuth access/refresh tokens (ec_oat_/ec_ort_...)
|
|
505
523
|
* 3. _emdash_device_codes — OAuth Device Flow state (RFC 8628)
|
|
506
524
|
*/
|
|
507
|
-
async function up$
|
|
525
|
+
async function up$20(db) {
|
|
508
526
|
await db.schema.createTable("_emdash_api_tokens").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull()).addColumn("token_hash", "text", (col) => col.notNull().unique()).addColumn("prefix", "text", (col) => col.notNull()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("expires_at", "text").addColumn("last_used_at", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("api_tokens_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).execute();
|
|
509
527
|
await db.schema.createIndex("idx_api_tokens_token_hash").on("_emdash_api_tokens").column("token_hash").execute();
|
|
510
528
|
await db.schema.createIndex("idx_api_tokens_user_id").on("_emdash_api_tokens").column("user_id").execute();
|
|
@@ -513,7 +531,7 @@ async function up$19(db) {
|
|
|
513
531
|
await db.schema.createIndex("idx_oauth_tokens_expires").on("_emdash_oauth_tokens").column("expires_at").execute();
|
|
514
532
|
await db.schema.createTable("_emdash_device_codes").addColumn("device_code", "text", (col) => col.primaryKey()).addColumn("user_code", "text", (col) => col.notNull().unique()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("user_id", "text").addColumn("status", "text", (col) => col.notNull().defaultTo("pending")).addColumn("expires_at", "text", (col) => col.notNull()).addColumn("interval", "integer", (col) => col.notNull().defaultTo(5)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
515
533
|
}
|
|
516
|
-
async function down$
|
|
534
|
+
async function down$20(db) {
|
|
517
535
|
await db.schema.dropTable("_emdash_device_codes").execute();
|
|
518
536
|
await db.schema.dropTable("_emdash_oauth_tokens").execute();
|
|
519
537
|
await db.schema.dropTable("_emdash_api_tokens").execute();
|
|
@@ -522,8 +540,8 @@ async function down$19(db) {
|
|
|
522
540
|
//#endregion
|
|
523
541
|
//#region src/database/migrations/017_authorization_codes.ts
|
|
524
542
|
var _017_authorization_codes_exports = /* @__PURE__ */ __exportAll({
|
|
525
|
-
down: () => down$
|
|
526
|
-
up: () => up$
|
|
543
|
+
down: () => down$19,
|
|
544
|
+
up: () => up$19
|
|
527
545
|
});
|
|
528
546
|
/**
|
|
529
547
|
* Authorization codes for OAuth 2.1 Authorization Code + PKCE flow.
|
|
@@ -533,20 +551,20 @@ var _017_authorization_codes_exports = /* @__PURE__ */ __exportAll({
|
|
|
533
551
|
*
|
|
534
552
|
* Also adds client_id tracking to oauth_tokens for per-client revocation.
|
|
535
553
|
*/
|
|
536
|
-
async function up$
|
|
554
|
+
async function up$19(db) {
|
|
537
555
|
await db.schema.createTable("_emdash_authorization_codes").addColumn("code_hash", "text", (col) => col.primaryKey()).addColumn("client_id", "text", (col) => col.notNull()).addColumn("redirect_uri", "text", (col) => col.notNull()).addColumn("user_id", "text", (col) => col.notNull()).addColumn("scopes", "text", (col) => col.notNull()).addColumn("code_challenge", "text", (col) => col.notNull()).addColumn("code_challenge_method", "text", (col) => col.notNull().defaultTo("S256")).addColumn("resource", "text").addColumn("expires_at", "text", (col) => col.notNull()).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addForeignKeyConstraint("auth_codes_user_fk", ["user_id"], "users", ["id"], (cb) => cb.onDelete("cascade")).execute();
|
|
538
556
|
await db.schema.createIndex("idx_auth_codes_expires").on("_emdash_authorization_codes").column("expires_at").execute();
|
|
539
557
|
await sql`ALTER TABLE _emdash_oauth_tokens ADD COLUMN client_id TEXT`.execute(db);
|
|
540
558
|
}
|
|
541
|
-
async function down$
|
|
559
|
+
async function down$19(db) {
|
|
542
560
|
await db.schema.dropTable("_emdash_authorization_codes").execute();
|
|
543
561
|
}
|
|
544
562
|
|
|
545
563
|
//#endregion
|
|
546
564
|
//#region src/database/migrations/018_seo.ts
|
|
547
565
|
var _018_seo_exports = /* @__PURE__ */ __exportAll({
|
|
548
|
-
down: () => down$
|
|
549
|
-
up: () => up$
|
|
566
|
+
down: () => down$18,
|
|
567
|
+
up: () => up$18
|
|
550
568
|
});
|
|
551
569
|
/**
|
|
552
570
|
* Migration: SEO support
|
|
@@ -559,7 +577,7 @@ var _018_seo_exports = /* @__PURE__ */ __exportAll({
|
|
|
559
577
|
* need it. The `has_seo` flag controls whether the admin shows SEO fields
|
|
560
578
|
* and whether the collection's content appears in sitemaps.
|
|
561
579
|
*/
|
|
562
|
-
async function up$
|
|
580
|
+
async function up$18(db) {
|
|
563
581
|
await db.schema.createTable("_emdash_seo").addColumn("collection", "text", (col) => col.notNull()).addColumn("content_id", "text", (col) => col.notNull()).addColumn("seo_title", "text").addColumn("seo_description", "text").addColumn("seo_image", "text").addColumn("seo_canonical", "text").addColumn("seo_no_index", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.notNull().defaultTo(currentTimestamp(db))).addPrimaryKeyConstraint("_emdash_seo_pk", ["collection", "content_id"]).execute();
|
|
564
582
|
await sql`
|
|
565
583
|
CREATE INDEX idx_emdash_seo_collection
|
|
@@ -570,7 +588,7 @@ async function up$17(db) {
|
|
|
570
588
|
ADD COLUMN has_seo INTEGER NOT NULL DEFAULT 0
|
|
571
589
|
`.execute(db);
|
|
572
590
|
}
|
|
573
|
-
async function down$
|
|
591
|
+
async function down$18(db) {
|
|
574
592
|
await sql`DROP TABLE IF EXISTS _emdash_seo`.execute(db);
|
|
575
593
|
await sql`
|
|
576
594
|
ALTER TABLE _emdash_collections
|
|
@@ -581,8 +599,8 @@ async function down$17(db) {
|
|
|
581
599
|
//#endregion
|
|
582
600
|
//#region src/database/migrations/019_i18n.ts
|
|
583
601
|
var _019_i18n_exports = /* @__PURE__ */ __exportAll({
|
|
584
|
-
down: () => down$
|
|
585
|
-
up: () => up$
|
|
602
|
+
down: () => down$17,
|
|
603
|
+
up: () => up$17
|
|
586
604
|
});
|
|
587
605
|
/**
|
|
588
606
|
* Quote an identifier for use in raw SQL. Escapes embedded double-quotes
|
|
@@ -695,7 +713,7 @@ async function upPostgres(db) {
|
|
|
695
713
|
ADD COLUMN translatable INTEGER NOT NULL DEFAULT 1
|
|
696
714
|
`.execute(db);
|
|
697
715
|
}
|
|
698
|
-
async function up$
|
|
716
|
+
async function up$17(db) {
|
|
699
717
|
if (!isSqlite(db)) return upPostgres(db);
|
|
700
718
|
const orphanedTmps = await listTablesLike(db, "ec_%_i18n_tmp");
|
|
701
719
|
for (const tmpName of orphanedTmps) {
|
|
@@ -812,7 +830,7 @@ async function downPostgres(db) {
|
|
|
812
830
|
await sql`ALTER TABLE ${sql.ref(t)} DROP COLUMN translation_group`.execute(db);
|
|
813
831
|
}
|
|
814
832
|
}
|
|
815
|
-
async function down$
|
|
833
|
+
async function down$17(db) {
|
|
816
834
|
if (!isSqlite(db)) return downPostgres(db);
|
|
817
835
|
await sql`
|
|
818
836
|
ALTER TABLE _emdash_fields
|
|
@@ -919,8 +937,8 @@ async function down$16(db) {
|
|
|
919
937
|
//#endregion
|
|
920
938
|
//#region src/database/migrations/020_collection_url_pattern.ts
|
|
921
939
|
var _020_collection_url_pattern_exports = /* @__PURE__ */ __exportAll({
|
|
922
|
-
down: () => down$
|
|
923
|
-
up: () => up$
|
|
940
|
+
down: () => down$16,
|
|
941
|
+
up: () => up$16
|
|
924
942
|
});
|
|
925
943
|
/**
|
|
926
944
|
* Migration: URL pattern for collections
|
|
@@ -929,13 +947,13 @@ var _020_collection_url_pattern_exports = /* @__PURE__ */ __exportAll({
|
|
|
929
947
|
* can declare its own URL structure (e.g. "/{slug}" for pages, "/blog/{slug}"
|
|
930
948
|
* for posts). Used for menu URL resolution, sitemaps, and path-based lookups.
|
|
931
949
|
*/
|
|
932
|
-
async function up$
|
|
950
|
+
async function up$16(db) {
|
|
933
951
|
await sql`
|
|
934
952
|
ALTER TABLE _emdash_collections
|
|
935
953
|
ADD COLUMN url_pattern TEXT
|
|
936
954
|
`.execute(db);
|
|
937
955
|
}
|
|
938
|
-
async function down$
|
|
956
|
+
async function down$16(db) {
|
|
939
957
|
await sql`
|
|
940
958
|
ALTER TABLE _emdash_collections
|
|
941
959
|
DROP COLUMN url_pattern
|
|
@@ -945,8 +963,8 @@ async function down$15(db) {
|
|
|
945
963
|
//#endregion
|
|
946
964
|
//#region src/database/migrations/021_remove_section_categories.ts
|
|
947
965
|
var _021_remove_section_categories_exports = /* @__PURE__ */ __exportAll({
|
|
948
|
-
down: () => down$
|
|
949
|
-
up: () => up$
|
|
966
|
+
down: () => down$15,
|
|
967
|
+
up: () => up$15
|
|
950
968
|
});
|
|
951
969
|
/**
|
|
952
970
|
* Migration: Remove section categories
|
|
@@ -955,12 +973,12 @@ var _021_remove_section_categories_exports = /* @__PURE__ */ __exportAll({
|
|
|
955
973
|
* Rather than building the missing UI for a feature with very little need at this stage,
|
|
956
974
|
* we're removing the feature entirely.
|
|
957
975
|
*/
|
|
958
|
-
async function up$
|
|
976
|
+
async function up$15(db) {
|
|
959
977
|
await db.schema.dropIndex("idx_sections_category").ifExists().execute();
|
|
960
978
|
await db.schema.alterTable("_emdash_sections").dropColumn("category_id").execute();
|
|
961
979
|
await db.schema.dropTable("_emdash_section_categories").execute();
|
|
962
980
|
}
|
|
963
|
-
async function down$
|
|
981
|
+
async function down$15(db) {
|
|
964
982
|
await db.schema.createTable("_emdash_section_categories").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("label", "text", (col) => col.notNull()).addColumn("sort_order", "integer", (col) => col.defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
965
983
|
await db.schema.alterTable("_emdash_sections").addColumn("category_id", "text", (col) => col.references("_emdash_section_categories.id").onDelete("set null")).execute();
|
|
966
984
|
await db.schema.createIndex("idx_sections_category").on("_emdash_sections").columns(["category_id"]).execute();
|
|
@@ -969,8 +987,8 @@ async function down$14(db) {
|
|
|
969
987
|
//#endregion
|
|
970
988
|
//#region src/database/migrations/022_marketplace_plugin_state.ts
|
|
971
989
|
var _022_marketplace_plugin_state_exports = /* @__PURE__ */ __exportAll({
|
|
972
|
-
down: () => down$
|
|
973
|
-
up: () => up$
|
|
990
|
+
down: () => down$14,
|
|
991
|
+
up: () => up$14
|
|
974
992
|
});
|
|
975
993
|
/**
|
|
976
994
|
* Migration: Add marketplace fields to _plugin_state
|
|
@@ -979,7 +997,7 @@ var _022_marketplace_plugin_state_exports = /* @__PURE__ */ __exportAll({
|
|
|
979
997
|
* whether a plugin was installed from config or marketplace,
|
|
980
998
|
* and which marketplace version is installed.
|
|
981
999
|
*/
|
|
982
|
-
async function up$
|
|
1000
|
+
async function up$14(db) {
|
|
983
1001
|
await sql`
|
|
984
1002
|
ALTER TABLE _plugin_state
|
|
985
1003
|
ADD COLUMN source TEXT NOT NULL DEFAULT 'config'
|
|
@@ -994,7 +1012,7 @@ async function up$13(db) {
|
|
|
994
1012
|
WHERE source = 'marketplace'
|
|
995
1013
|
`.execute(db);
|
|
996
1014
|
}
|
|
997
|
-
async function down$
|
|
1015
|
+
async function down$14(db) {
|
|
998
1016
|
await sql`
|
|
999
1017
|
DROP INDEX IF EXISTS idx_plugin_state_source
|
|
1000
1018
|
`.execute(db);
|
|
@@ -1011,8 +1029,8 @@ async function down$13(db) {
|
|
|
1011
1029
|
//#endregion
|
|
1012
1030
|
//#region src/database/migrations/023_plugin_metadata.ts
|
|
1013
1031
|
var _023_plugin_metadata_exports = /* @__PURE__ */ __exportAll({
|
|
1014
|
-
down: () => down$
|
|
1015
|
-
up: () => up$
|
|
1032
|
+
down: () => down$13,
|
|
1033
|
+
up: () => up$13
|
|
1016
1034
|
});
|
|
1017
1035
|
/**
|
|
1018
1036
|
* Migration: Add display metadata to _plugin_state
|
|
@@ -1021,7 +1039,7 @@ var _023_plugin_metadata_exports = /* @__PURE__ */ __exportAll({
|
|
|
1021
1039
|
* so the admin UI can show meaningful info without re-fetching
|
|
1022
1040
|
* from the marketplace on every page load.
|
|
1023
1041
|
*/
|
|
1024
|
-
async function up$
|
|
1042
|
+
async function up$13(db) {
|
|
1025
1043
|
await sql`
|
|
1026
1044
|
ALTER TABLE _plugin_state
|
|
1027
1045
|
ADD COLUMN display_name TEXT
|
|
@@ -1031,7 +1049,7 @@ async function up$12(db) {
|
|
|
1031
1049
|
ADD COLUMN description TEXT
|
|
1032
1050
|
`.execute(db);
|
|
1033
1051
|
}
|
|
1034
|
-
async function down$
|
|
1052
|
+
async function down$13(db) {
|
|
1035
1053
|
await sql`
|
|
1036
1054
|
ALTER TABLE _plugin_state
|
|
1037
1055
|
DROP COLUMN description
|
|
@@ -1045,8 +1063,8 @@ async function down$12(db) {
|
|
|
1045
1063
|
//#endregion
|
|
1046
1064
|
//#region src/database/migrations/024_media_placeholders.ts
|
|
1047
1065
|
var _024_media_placeholders_exports = /* @__PURE__ */ __exportAll({
|
|
1048
|
-
down: () => down$
|
|
1049
|
-
up: () => up$
|
|
1066
|
+
down: () => down$12,
|
|
1067
|
+
up: () => up$12
|
|
1050
1068
|
});
|
|
1051
1069
|
/**
|
|
1052
1070
|
* Migration: Add placeholder columns to media table
|
|
@@ -1054,7 +1072,7 @@ var _024_media_placeholders_exports = /* @__PURE__ */ __exportAll({
|
|
|
1054
1072
|
* Stores blurhash and dominant_color for LQIP (Low Quality Image Placeholder)
|
|
1055
1073
|
* support. Generated at upload time from image pixel data.
|
|
1056
1074
|
*/
|
|
1057
|
-
async function up$
|
|
1075
|
+
async function up$12(db) {
|
|
1058
1076
|
await sql`
|
|
1059
1077
|
ALTER TABLE media
|
|
1060
1078
|
ADD COLUMN blurhash TEXT
|
|
@@ -1064,7 +1082,7 @@ async function up$11(db) {
|
|
|
1064
1082
|
ADD COLUMN dominant_color TEXT
|
|
1065
1083
|
`.execute(db);
|
|
1066
1084
|
}
|
|
1067
|
-
async function down$
|
|
1085
|
+
async function down$12(db) {
|
|
1068
1086
|
await sql`
|
|
1069
1087
|
ALTER TABLE media
|
|
1070
1088
|
DROP COLUMN dominant_color
|
|
@@ -1078,8 +1096,8 @@ async function down$11(db) {
|
|
|
1078
1096
|
//#endregion
|
|
1079
1097
|
//#region src/database/migrations/025_oauth_clients.ts
|
|
1080
1098
|
var _025_oauth_clients_exports = /* @__PURE__ */ __exportAll({
|
|
1081
|
-
down: () => down$
|
|
1082
|
-
up: () => up$
|
|
1099
|
+
down: () => down$11,
|
|
1100
|
+
up: () => up$11
|
|
1083
1101
|
});
|
|
1084
1102
|
/**
|
|
1085
1103
|
* Migration: Create OAuth clients table
|
|
@@ -1090,18 +1108,18 @@ var _025_oauth_clients_exports = /* @__PURE__ */ __exportAll({
|
|
|
1090
1108
|
* Each client has a set of pre-registered redirect URIs (JSON array).
|
|
1091
1109
|
* The authorize endpoint rejects any redirect_uri not in the client's list.
|
|
1092
1110
|
*/
|
|
1093
|
-
async function up$
|
|
1111
|
+
async function up$11(db) {
|
|
1094
1112
|
await db.schema.createTable("_emdash_oauth_clients").addColumn("id", "text", (col) => col.primaryKey()).addColumn("name", "text", (col) => col.notNull()).addColumn("redirect_uris", "text", (col) => col.notNull()).addColumn("scopes", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
1095
1113
|
}
|
|
1096
|
-
async function down$
|
|
1114
|
+
async function down$11(db) {
|
|
1097
1115
|
await db.schema.dropTable("_emdash_oauth_clients").execute();
|
|
1098
1116
|
}
|
|
1099
1117
|
|
|
1100
1118
|
//#endregion
|
|
1101
1119
|
//#region src/database/migrations/026_cron_tasks.ts
|
|
1102
1120
|
var _026_cron_tasks_exports = /* @__PURE__ */ __exportAll({
|
|
1103
|
-
down: () => down$
|
|
1104
|
-
up: () => up$
|
|
1121
|
+
down: () => down$10,
|
|
1122
|
+
up: () => up$10
|
|
1105
1123
|
});
|
|
1106
1124
|
/**
|
|
1107
1125
|
* Migration: Create cron tasks table for plugin scheduled tasks.
|
|
@@ -1112,7 +1130,7 @@ var _026_cron_tasks_exports = /* @__PURE__ */ __exportAll({
|
|
|
1112
1130
|
* The `next_run_at` + `status` + `enabled` index drives the "find overdue
|
|
1113
1131
|
* tasks" query used by CronExecutor.tick().
|
|
1114
1132
|
*/
|
|
1115
|
-
async function up$
|
|
1133
|
+
async function up$10(db) {
|
|
1116
1134
|
await db.schema.createTable("_emdash_cron_tasks").addColumn("id", "text", (col) => col.primaryKey()).addColumn("plugin_id", "text", (col) => col.notNull()).addColumn("task_name", "text", (col) => col.notNull()).addColumn("schedule", "text", (col) => col.notNull()).addColumn("is_oneshot", "integer", (col) => col.notNull().defaultTo(0)).addColumn("data", "text").addColumn("next_run_at", "text", (col) => col.notNull()).addColumn("last_run_at", "text").addColumn("status", "text", (col) => col.notNull().defaultTo("idle")).addColumn("locked_at", "text").addColumn("enabled", "integer", (col) => col.notNull().defaultTo(1)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addUniqueConstraint("uq_cron_tasks_plugin_task", ["plugin_id", "task_name"]).execute();
|
|
1117
1135
|
await db.schema.createIndex("idx_cron_tasks_due").on("_emdash_cron_tasks").columns([
|
|
1118
1136
|
"enabled",
|
|
@@ -1121,17 +1139,17 @@ async function up$9(db) {
|
|
|
1121
1139
|
]).execute();
|
|
1122
1140
|
await db.schema.createIndex("idx_cron_tasks_plugin").on("_emdash_cron_tasks").column("plugin_id").execute();
|
|
1123
1141
|
}
|
|
1124
|
-
async function down$
|
|
1142
|
+
async function down$10(db) {
|
|
1125
1143
|
await db.schema.dropTable("_emdash_cron_tasks").execute();
|
|
1126
1144
|
}
|
|
1127
1145
|
|
|
1128
1146
|
//#endregion
|
|
1129
1147
|
//#region src/database/migrations/027_comments.ts
|
|
1130
1148
|
var _027_comments_exports = /* @__PURE__ */ __exportAll({
|
|
1131
|
-
down: () => down$
|
|
1132
|
-
up: () => up$
|
|
1149
|
+
down: () => down$9,
|
|
1150
|
+
up: () => up$9
|
|
1133
1151
|
});
|
|
1134
|
-
async function up$
|
|
1152
|
+
async function up$9(db) {
|
|
1135
1153
|
await db.schema.createTable("_emdash_comments").addColumn("id", "text", (col) => col.primaryKey()).addColumn("collection", "text", (col) => col.notNull()).addColumn("content_id", "text", (col) => col.notNull()).addColumn("parent_id", "text", (col) => col.references("_emdash_comments.id").onDelete("cascade")).addColumn("author_name", "text", (col) => col.notNull()).addColumn("author_email", "text", (col) => col.notNull()).addColumn("author_url", "text").addColumn("author_user_id", "text", (col) => col.references("users.id").onDelete("set null")).addColumn("body", "text", (col) => col.notNull()).addColumn("status", "text", (col) => col.notNull().defaultTo("pending")).addColumn("ip_hash", "text").addColumn("user_agent", "text").addColumn("moderation_metadata", "text").addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
1136
1154
|
await db.schema.createIndex("idx_comments_content").on("_emdash_comments").columns([
|
|
1137
1155
|
"collection",
|
|
@@ -1147,30 +1165,30 @@ async function up$8(db) {
|
|
|
1147
1165
|
await db.schema.alterTable("_emdash_collections").addColumn("comments_closed_after_days", "integer", (col) => col.defaultTo(90)).execute();
|
|
1148
1166
|
await db.schema.alterTable("_emdash_collections").addColumn("comments_auto_approve_users", "integer", (col) => col.defaultTo(1)).execute();
|
|
1149
1167
|
}
|
|
1150
|
-
async function down$
|
|
1168
|
+
async function down$9(db) {
|
|
1151
1169
|
await db.schema.dropTable("_emdash_comments").execute();
|
|
1152
1170
|
}
|
|
1153
1171
|
|
|
1154
1172
|
//#endregion
|
|
1155
1173
|
//#region src/database/migrations/028_drop_author_url.ts
|
|
1156
1174
|
var _028_drop_author_url_exports = /* @__PURE__ */ __exportAll({
|
|
1157
|
-
down: () => down$
|
|
1158
|
-
up: () => up$
|
|
1175
|
+
down: () => down$8,
|
|
1176
|
+
up: () => up$8
|
|
1159
1177
|
});
|
|
1160
|
-
async function up$
|
|
1178
|
+
async function up$8(db) {
|
|
1161
1179
|
await sql`ALTER TABLE _emdash_comments DROP COLUMN author_url`.execute(db);
|
|
1162
1180
|
}
|
|
1163
|
-
async function down$
|
|
1181
|
+
async function down$8(db) {
|
|
1164
1182
|
await db.schema.alterTable("_emdash_comments").addColumn("author_url", "text").execute();
|
|
1165
1183
|
}
|
|
1166
1184
|
|
|
1167
1185
|
//#endregion
|
|
1168
1186
|
//#region src/database/migrations/029_redirects.ts
|
|
1169
1187
|
var _029_redirects_exports = /* @__PURE__ */ __exportAll({
|
|
1170
|
-
down: () => down$
|
|
1171
|
-
up: () => up$
|
|
1188
|
+
down: () => down$7,
|
|
1189
|
+
up: () => up$7
|
|
1172
1190
|
});
|
|
1173
|
-
async function up$
|
|
1191
|
+
async function up$7(db) {
|
|
1174
1192
|
await db.schema.createTable("_emdash_redirects").addColumn("id", "text", (col) => col.primaryKey()).addColumn("source", "text", (col) => col.notNull()).addColumn("destination", "text", (col) => col.notNull()).addColumn("type", "integer", (col) => col.notNull().defaultTo(301)).addColumn("is_pattern", "integer", (col) => col.notNull().defaultTo(0)).addColumn("enabled", "integer", (col) => col.notNull().defaultTo(1)).addColumn("hits", "integer", (col) => col.notNull().defaultTo(0)).addColumn("last_hit_at", "text").addColumn("group_name", "text").addColumn("auto", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
1175
1193
|
await db.schema.createIndex("idx_redirects_source").on("_emdash_redirects").column("source").execute();
|
|
1176
1194
|
await db.schema.createIndex("idx_redirects_enabled").on("_emdash_redirects").column("enabled").execute();
|
|
@@ -1179,7 +1197,7 @@ async function up$6(db) {
|
|
|
1179
1197
|
await db.schema.createIndex("idx_404_log_path").on("_emdash_404_log").column("path").execute();
|
|
1180
1198
|
await db.schema.createIndex("idx_404_log_created").on("_emdash_404_log").column("created_at").execute();
|
|
1181
1199
|
}
|
|
1182
|
-
async function down$
|
|
1200
|
+
async function down$7(db) {
|
|
1183
1201
|
await db.schema.dropTable("_emdash_404_log").execute();
|
|
1184
1202
|
await db.schema.dropTable("_emdash_redirects").execute();
|
|
1185
1203
|
}
|
|
@@ -1187,8 +1205,8 @@ async function down$6(db) {
|
|
|
1187
1205
|
//#endregion
|
|
1188
1206
|
//#region src/database/migrations/030_widen_scheduled_index.ts
|
|
1189
1207
|
var _030_widen_scheduled_index_exports = /* @__PURE__ */ __exportAll({
|
|
1190
|
-
down: () => down$
|
|
1191
|
-
up: () => up$
|
|
1208
|
+
down: () => down$6,
|
|
1209
|
+
up: () => up$6
|
|
1192
1210
|
});
|
|
1193
1211
|
/**
|
|
1194
1212
|
* Migration: Widen scheduled publishing index
|
|
@@ -1197,7 +1215,7 @@ var _030_widen_scheduled_index_exports = /* @__PURE__ */ __exportAll({
|
|
|
1197
1215
|
* Published posts can now have scheduled draft changes, so widen the
|
|
1198
1216
|
* index to cover all rows where scheduled_at IS NOT NULL.
|
|
1199
1217
|
*/
|
|
1200
|
-
async function up$
|
|
1218
|
+
async function up$6(db) {
|
|
1201
1219
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
1202
1220
|
for (const tableName of tableNames) {
|
|
1203
1221
|
const table = { name: tableName };
|
|
@@ -1211,7 +1229,7 @@ async function up$5(db) {
|
|
|
1211
1229
|
`.execute(db);
|
|
1212
1230
|
}
|
|
1213
1231
|
}
|
|
1214
|
-
async function down$
|
|
1232
|
+
async function down$6(db) {
|
|
1215
1233
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
1216
1234
|
for (const tableName of tableNames) {
|
|
1217
1235
|
const table = { name: tableName };
|
|
@@ -1229,10 +1247,10 @@ async function down$5(db) {
|
|
|
1229
1247
|
//#endregion
|
|
1230
1248
|
//#region src/database/migrations/031_bylines.ts
|
|
1231
1249
|
var _031_bylines_exports = /* @__PURE__ */ __exportAll({
|
|
1232
|
-
down: () => down$
|
|
1233
|
-
up: () => up$
|
|
1250
|
+
down: () => down$5,
|
|
1251
|
+
up: () => up$5
|
|
1234
1252
|
});
|
|
1235
|
-
async function up$
|
|
1253
|
+
async function up$5(db) {
|
|
1236
1254
|
await db.schema.createTable("_emdash_bylines").addColumn("id", "text", (col) => col.primaryKey()).addColumn("slug", "text", (col) => col.notNull().unique()).addColumn("display_name", "text", (col) => col.notNull()).addColumn("bio", "text").addColumn("avatar_media_id", "text", (col) => col.references("media.id").onDelete("set null")).addColumn("website_url", "text").addColumn("user_id", "text", (col) => col.references("users.id").onDelete("set null")).addColumn("is_guest", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db))).execute();
|
|
1237
1255
|
await sql`
|
|
1238
1256
|
CREATE UNIQUE INDEX ${sql.ref("idx_bylines_user_id_unique")}
|
|
@@ -1264,7 +1282,7 @@ async function up$4(db) {
|
|
|
1264
1282
|
`.execute(db);
|
|
1265
1283
|
}
|
|
1266
1284
|
}
|
|
1267
|
-
async function down$
|
|
1285
|
+
async function down$5(db) {
|
|
1268
1286
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
1269
1287
|
for (const tableName of tableNames) {
|
|
1270
1288
|
await sql`
|
|
@@ -1282,8 +1300,8 @@ async function down$4(db) {
|
|
|
1282
1300
|
//#endregion
|
|
1283
1301
|
//#region src/database/migrations/032_rate_limits.ts
|
|
1284
1302
|
var _032_rate_limits_exports = /* @__PURE__ */ __exportAll({
|
|
1285
|
-
down: () => down$
|
|
1286
|
-
up: () => up$
|
|
1303
|
+
down: () => down$4,
|
|
1304
|
+
up: () => up$4
|
|
1287
1305
|
});
|
|
1288
1306
|
/**
|
|
1289
1307
|
* Migration: Rate limits table + device code polling tracking.
|
|
@@ -1294,12 +1312,12 @@ var _032_rate_limits_exports = /* @__PURE__ */ __exportAll({
|
|
|
1294
1312
|
* 2. Add last_polled_at column to _emdash_device_codes for
|
|
1295
1313
|
* RFC 8628 slow_down enforcement.
|
|
1296
1314
|
*/
|
|
1297
|
-
async function up$
|
|
1315
|
+
async function up$4(db) {
|
|
1298
1316
|
await db.schema.createTable("_emdash_rate_limits").addColumn("key", "text", (col) => col.notNull()).addColumn("window", "text", (col) => col.notNull()).addColumn("count", "integer", (col) => col.notNull().defaultTo(1)).addPrimaryKeyConstraint("pk_rate_limits", ["key", "window"]).execute();
|
|
1299
1317
|
await db.schema.createIndex("idx_rate_limits_window").on("_emdash_rate_limits").column("window").execute();
|
|
1300
1318
|
await db.schema.alterTable("_emdash_device_codes").addColumn("last_polled_at", "text").execute();
|
|
1301
1319
|
}
|
|
1302
|
-
async function down$
|
|
1320
|
+
async function down$4(db) {
|
|
1303
1321
|
await db.schema.dropTable("_emdash_rate_limits").execute();
|
|
1304
1322
|
await db.schema.alterTable("_emdash_device_codes").dropColumn("last_polled_at").execute();
|
|
1305
1323
|
}
|
|
@@ -1307,8 +1325,8 @@ async function down$3(db) {
|
|
|
1307
1325
|
//#endregion
|
|
1308
1326
|
//#region src/database/migrations/033_optimize_content_indexes.ts
|
|
1309
1327
|
var _033_optimize_content_indexes_exports = /* @__PURE__ */ __exportAll({
|
|
1310
|
-
down: () => down$
|
|
1311
|
-
up: () => up$
|
|
1328
|
+
down: () => down$3,
|
|
1329
|
+
up: () => up$3
|
|
1312
1330
|
});
|
|
1313
1331
|
/**
|
|
1314
1332
|
* Migration: Optimize content table indexes for D1 performance
|
|
@@ -1321,7 +1339,7 @@ var _033_optimize_content_indexes_exports = /* @__PURE__ */ __exportAll({
|
|
|
1321
1339
|
*
|
|
1322
1340
|
* Impact: Reduces D1 row reads by 90%+ for admin panel operations.
|
|
1323
1341
|
*/
|
|
1324
|
-
async function up$
|
|
1342
|
+
async function up$3(db) {
|
|
1325
1343
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
1326
1344
|
for (const tableName of tableNames) {
|
|
1327
1345
|
const table = { name: tableName };
|
|
@@ -1363,7 +1381,7 @@ async function up$2(db) {
|
|
|
1363
1381
|
WHERE status = 'trash'
|
|
1364
1382
|
`.execute(db);
|
|
1365
1383
|
}
|
|
1366
|
-
async function down$
|
|
1384
|
+
async function down$3(db) {
|
|
1367
1385
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
1368
1386
|
for (const tableName of tableNames) {
|
|
1369
1387
|
const table = { name: tableName };
|
|
@@ -1396,20 +1414,20 @@ async function down$2(db) {
|
|
|
1396
1414
|
//#endregion
|
|
1397
1415
|
//#region src/database/migrations/034_published_at_index.ts
|
|
1398
1416
|
var _034_published_at_index_exports = /* @__PURE__ */ __exportAll({
|
|
1399
|
-
down: () => down$
|
|
1400
|
-
up: () => up$
|
|
1417
|
+
down: () => down$2,
|
|
1418
|
+
up: () => up$2
|
|
1401
1419
|
});
|
|
1402
|
-
async function up$
|
|
1420
|
+
async function up$2(db) {
|
|
1403
1421
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
1404
1422
|
for (const tableName of tableNames) {
|
|
1405
1423
|
const table = { name: tableName };
|
|
1406
1424
|
await sql`
|
|
1407
|
-
CREATE INDEX ${sql.ref(`idx_${table.name}_deleted_published_id`)}
|
|
1425
|
+
CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_published_id`)}
|
|
1408
1426
|
ON ${sql.ref(table.name)} (deleted_at, published_at DESC, id DESC)
|
|
1409
1427
|
`.execute(db);
|
|
1410
1428
|
}
|
|
1411
1429
|
}
|
|
1412
|
-
async function down$
|
|
1430
|
+
async function down$2(db) {
|
|
1413
1431
|
const tableNames = await listTablesLike(db, "ec_%");
|
|
1414
1432
|
for (const tableName of tableNames) {
|
|
1415
1433
|
const table = { name: tableName };
|
|
@@ -1420,8 +1438,8 @@ async function down$1(db) {
|
|
|
1420
1438
|
//#endregion
|
|
1421
1439
|
//#region src/database/migrations/035_bounded_404_log.ts
|
|
1422
1440
|
var _035_bounded_404_log_exports = /* @__PURE__ */ __exportAll({
|
|
1423
|
-
down: () => down,
|
|
1424
|
-
up: () => up
|
|
1441
|
+
down: () => down$1,
|
|
1442
|
+
up: () => up$1
|
|
1425
1443
|
});
|
|
1426
1444
|
/**
|
|
1427
1445
|
* Migration: Bounded 404 logging
|
|
@@ -1439,58 +1457,354 @@ var _035_bounded_404_log_exports = /* @__PURE__ */ __exportAll({
|
|
|
1439
1457
|
* path and summing hits
|
|
1440
1458
|
* - Adds a UNIQUE index on `path` so upsert semantics work
|
|
1441
1459
|
*/
|
|
1442
|
-
async function up(db) {
|
|
1443
|
-
await db
|
|
1444
|
-
await db.schema.alterTable("_emdash_404_log").addColumn("
|
|
1460
|
+
async function up$1(db) {
|
|
1461
|
+
const hitsExists = await columnExists(db, "_emdash_404_log", "hits");
|
|
1462
|
+
if (!hitsExists) await db.schema.alterTable("_emdash_404_log").addColumn("hits", "integer", (col) => col.notNull().defaultTo(1)).execute();
|
|
1463
|
+
if (!await columnExists(db, "_emdash_404_log", "last_seen_at")) await db.schema.alterTable("_emdash_404_log").addColumn("last_seen_at", "text").execute();
|
|
1445
1464
|
await sql`
|
|
1446
1465
|
UPDATE _emdash_404_log
|
|
1447
1466
|
SET last_seen_at = created_at
|
|
1448
1467
|
WHERE last_seen_at IS NULL
|
|
1449
1468
|
`.execute(db);
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
id,
|
|
1454
|
-
path,
|
|
1455
|
-
ROW_NUMBER() OVER (
|
|
1456
|
-
PARTITION BY path
|
|
1457
|
-
ORDER BY created_at DESC, id DESC
|
|
1458
|
-
) AS rn,
|
|
1459
|
-
COUNT(*) OVER (PARTITION BY path) AS path_count,
|
|
1460
|
-
MAX(created_at) OVER (PARTITION BY path) AS latest_created_at
|
|
1461
|
-
FROM _emdash_404_log
|
|
1462
|
-
)
|
|
1463
|
-
UPDATE _emdash_404_log
|
|
1464
|
-
SET
|
|
1465
|
-
hits = (SELECT path_count FROM ranked WHERE ranked.id = _emdash_404_log.id),
|
|
1466
|
-
last_seen_at = (SELECT latest_created_at FROM ranked WHERE ranked.id = _emdash_404_log.id)
|
|
1467
|
-
WHERE id IN (SELECT id FROM ranked WHERE rn = 1)
|
|
1468
|
-
`.execute(db);
|
|
1469
|
-
await sql`
|
|
1470
|
-
DELETE FROM _emdash_404_log
|
|
1471
|
-
WHERE id IN (
|
|
1472
|
-
SELECT id FROM (
|
|
1469
|
+
if (!hitsExists) {
|
|
1470
|
+
await sql`
|
|
1471
|
+
WITH ranked AS (
|
|
1473
1472
|
SELECT
|
|
1474
1473
|
id,
|
|
1474
|
+
path,
|
|
1475
1475
|
ROW_NUMBER() OVER (
|
|
1476
1476
|
PARTITION BY path
|
|
1477
1477
|
ORDER BY created_at DESC, id DESC
|
|
1478
|
-
) AS rn
|
|
1478
|
+
) AS rn,
|
|
1479
|
+
COUNT(*) OVER (PARTITION BY path) AS path_count,
|
|
1480
|
+
MAX(created_at) OVER (PARTITION BY path) AS latest_created_at
|
|
1479
1481
|
FROM _emdash_404_log
|
|
1480
|
-
)
|
|
1481
|
-
|
|
1482
|
+
)
|
|
1483
|
+
UPDATE _emdash_404_log
|
|
1484
|
+
SET
|
|
1485
|
+
hits = (SELECT path_count FROM ranked WHERE ranked.id = _emdash_404_log.id),
|
|
1486
|
+
last_seen_at = (SELECT latest_created_at FROM ranked WHERE ranked.id = _emdash_404_log.id)
|
|
1487
|
+
WHERE id IN (SELECT id FROM ranked WHERE rn = 1)
|
|
1488
|
+
`.execute(db);
|
|
1489
|
+
await sql`
|
|
1490
|
+
DELETE FROM _emdash_404_log
|
|
1491
|
+
WHERE id IN (
|
|
1492
|
+
SELECT id FROM (
|
|
1493
|
+
SELECT
|
|
1494
|
+
id,
|
|
1495
|
+
ROW_NUMBER() OVER (
|
|
1496
|
+
PARTITION BY path
|
|
1497
|
+
ORDER BY created_at DESC, id DESC
|
|
1498
|
+
) AS rn
|
|
1499
|
+
FROM _emdash_404_log
|
|
1500
|
+
) AS ranked
|
|
1501
|
+
WHERE rn > 1
|
|
1502
|
+
)
|
|
1503
|
+
`.execute(db);
|
|
1504
|
+
}
|
|
1505
|
+
await db.schema.createIndex("idx_404_log_path_unique").ifNotExists().on("_emdash_404_log").column("path").unique().execute();
|
|
1506
|
+
await db.schema.dropIndex("idx_404_log_path").ifExists().execute();
|
|
1507
|
+
await db.schema.createIndex("idx_404_log_last_seen").ifNotExists().on("_emdash_404_log").column("last_seen_at").execute();
|
|
1508
|
+
}
|
|
1509
|
+
async function down$1(db) {
|
|
1510
|
+
await db.schema.dropIndex("idx_404_log_last_seen").ifExists().execute();
|
|
1511
|
+
await db.schema.dropIndex("idx_404_log_path_unique").ifExists().execute();
|
|
1512
|
+
await db.schema.createIndex("idx_404_log_path").ifNotExists().on("_emdash_404_log").column("path").execute();
|
|
1513
|
+
await db.schema.alterTable("_emdash_404_log").dropColumn("last_seen_at").execute();
|
|
1514
|
+
await db.schema.alterTable("_emdash_404_log").dropColumn("hits").execute();
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
//#endregion
|
|
1518
|
+
//#region src/database/migrations/036_i18n_menus_and_taxonomies.ts
|
|
1519
|
+
var _036_i18n_menus_and_taxonomies_exports = /* @__PURE__ */ __exportAll({
|
|
1520
|
+
down: () => down,
|
|
1521
|
+
up: () => up
|
|
1522
|
+
});
|
|
1523
|
+
/**
|
|
1524
|
+
* i18n for menus + taxonomies. Adds `locale` + `translation_group` to system
|
|
1525
|
+
* tables and stores translation_groups (not row ids) in
|
|
1526
|
+
* `_emdash_menu_items.reference_id` and `content_taxonomies.taxonomy_id`.
|
|
1527
|
+
* Backfill locale and column DEFAULTs use the site's configured defaultLocale.
|
|
1528
|
+
*/
|
|
1529
|
+
function getDefaultLocale() {
|
|
1530
|
+
return getI18nConfig()?.defaultLocale ?? "en";
|
|
1531
|
+
}
|
|
1532
|
+
async function up(db) {
|
|
1533
|
+
const defaultLocale = getDefaultLocale();
|
|
1534
|
+
if (isSqlite(db)) {
|
|
1535
|
+
await sql.raw(`PRAGMA foreign_keys = OFF`).execute(db);
|
|
1536
|
+
try {
|
|
1537
|
+
await rebuildMenus(db, defaultLocale);
|
|
1538
|
+
await addItemColumns(db, defaultLocale);
|
|
1539
|
+
await rebuildTaxonomies(db, defaultLocale);
|
|
1540
|
+
await rebuildTaxonomyDefs(db, defaultLocale);
|
|
1541
|
+
await rebuildContentTaxonomies(db);
|
|
1542
|
+
await remapMenuItemRefs(db);
|
|
1543
|
+
} finally {
|
|
1544
|
+
await sql.raw(`PRAGMA foreign_keys = ON`).execute(db);
|
|
1545
|
+
}
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
await pgWiden(db, "_emdash_menus", ["name"], ["name", "locale"], defaultLocale);
|
|
1549
|
+
await pgWiden(db, "_emdash_menu_items", null, null, defaultLocale);
|
|
1550
|
+
await pgWiden(db, "taxonomies", ["name", "slug"], [
|
|
1551
|
+
"name",
|
|
1552
|
+
"slug",
|
|
1553
|
+
"locale"
|
|
1554
|
+
], defaultLocale);
|
|
1555
|
+
await pgWiden(db, "_emdash_taxonomy_defs", ["name"], ["name", "locale"], defaultLocale);
|
|
1556
|
+
await pgRemapContentTaxonomies(db);
|
|
1557
|
+
await remapMenuItemRefs(db);
|
|
1558
|
+
}
|
|
1559
|
+
async function rebuildMenus(db, defaultLocale) {
|
|
1560
|
+
if (await hasColumn(db, "_emdash_menus", "locale")) return;
|
|
1561
|
+
await sql.raw(`DROP TABLE IF EXISTS "_emdash_menus_new"`).execute(db);
|
|
1562
|
+
await db.schema.createTable("_emdash_menus_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text").addUniqueConstraint("_emdash_menus_name_locale_unique", ["name", "locale"]).execute();
|
|
1563
|
+
await sql`
|
|
1564
|
+
INSERT INTO _emdash_menus_new (id, name, label, created_at, updated_at, locale, translation_group)
|
|
1565
|
+
SELECT id, name, label, created_at, updated_at, ${defaultLocale}, id FROM _emdash_menus
|
|
1566
|
+
`.execute(db);
|
|
1567
|
+
await db.schema.dropTable("_emdash_menus").execute();
|
|
1568
|
+
await sql`ALTER TABLE _emdash_menus_new RENAME TO _emdash_menus`.execute(db);
|
|
1569
|
+
await db.schema.createIndex("idx__emdash_menus_locale").on("_emdash_menus").column("locale").execute();
|
|
1570
|
+
await db.schema.createIndex("idx__emdash_menus_translation_group").on("_emdash_menus").column("translation_group").execute();
|
|
1571
|
+
}
|
|
1572
|
+
async function addItemColumns(db, defaultLocale) {
|
|
1573
|
+
if (await hasColumn(db, "_emdash_menu_items", "locale")) return;
|
|
1574
|
+
await db.schema.alterTable("_emdash_menu_items").addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).execute();
|
|
1575
|
+
await db.schema.alterTable("_emdash_menu_items").addColumn("translation_group", "text").execute();
|
|
1576
|
+
await sql`UPDATE _emdash_menu_items SET translation_group = id`.execute(db);
|
|
1577
|
+
await db.schema.createIndex("idx__emdash_menu_items_locale").on("_emdash_menu_items").column("locale").execute();
|
|
1578
|
+
await db.schema.createIndex("idx__emdash_menu_items_translation_group").on("_emdash_menu_items").column("translation_group").execute();
|
|
1579
|
+
}
|
|
1580
|
+
async function rebuildTaxonomies(db, defaultLocale) {
|
|
1581
|
+
if (await hasColumn(db, "taxonomies", "locale")) return;
|
|
1582
|
+
await sql.raw(`DROP TABLE IF EXISTS "taxonomies_new"`).execute(db);
|
|
1583
|
+
await sql`DROP INDEX IF EXISTS idx_taxonomies_name`.execute(db);
|
|
1584
|
+
await db.schema.createTable("taxonomies_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("slug", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("parent_id", "text").addColumn("data", "text").addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text").addUniqueConstraint("taxonomies_name_slug_locale_unique", [
|
|
1585
|
+
"name",
|
|
1586
|
+
"slug",
|
|
1587
|
+
"locale"
|
|
1588
|
+
]).addForeignKeyConstraint("taxonomies_parent_fk", ["parent_id"], "taxonomies", ["id"], (cb) => cb.onDelete("set null")).execute();
|
|
1589
|
+
await sql`
|
|
1590
|
+
INSERT INTO taxonomies_new (id, name, slug, label, parent_id, data, locale, translation_group)
|
|
1591
|
+
SELECT id, name, slug, label, parent_id, data, ${defaultLocale}, id FROM taxonomies
|
|
1592
|
+
`.execute(db);
|
|
1593
|
+
await db.schema.dropTable("taxonomies").execute();
|
|
1594
|
+
await sql`ALTER TABLE taxonomies_new RENAME TO taxonomies`.execute(db);
|
|
1595
|
+
await db.schema.createIndex("idx_taxonomies_name").on("taxonomies").column("name").execute();
|
|
1596
|
+
await db.schema.createIndex("idx_taxonomies_locale").on("taxonomies").column("locale").execute();
|
|
1597
|
+
await db.schema.createIndex("idx_taxonomies_translation_group").on("taxonomies").column("translation_group").execute();
|
|
1598
|
+
}
|
|
1599
|
+
async function rebuildTaxonomyDefs(db, defaultLocale) {
|
|
1600
|
+
if (await hasColumn(db, "_emdash_taxonomy_defs", "locale")) return;
|
|
1601
|
+
await sql.raw(`DROP TABLE IF EXISTS "_emdash_taxonomy_defs_new"`).execute(db);
|
|
1602
|
+
await db.schema.createTable("_emdash_taxonomy_defs_new").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("label_singular", "text").addColumn("hierarchical", "integer", (c) => c.defaultTo(0)).addColumn("collections", "text").addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("locale", "text", (c) => c.notNull().defaultTo(defaultLocale)).addColumn("translation_group", "text").addUniqueConstraint("_emdash_taxonomy_defs_name_locale_unique", ["name", "locale"]).execute();
|
|
1603
|
+
await sql`
|
|
1604
|
+
INSERT INTO _emdash_taxonomy_defs_new
|
|
1605
|
+
(id, name, label, label_singular, hierarchical, collections, created_at, locale, translation_group)
|
|
1606
|
+
SELECT id, name, label, label_singular, hierarchical, collections, created_at, ${defaultLocale}, id
|
|
1607
|
+
FROM _emdash_taxonomy_defs
|
|
1608
|
+
`.execute(db);
|
|
1609
|
+
await db.schema.dropTable("_emdash_taxonomy_defs").execute();
|
|
1610
|
+
await sql`ALTER TABLE _emdash_taxonomy_defs_new RENAME TO _emdash_taxonomy_defs`.execute(db);
|
|
1611
|
+
await db.schema.createIndex("idx__emdash_taxonomy_defs_locale").on("_emdash_taxonomy_defs").column("locale").execute();
|
|
1612
|
+
await db.schema.createIndex("idx__emdash_taxonomy_defs_translation_group").on("_emdash_taxonomy_defs").column("translation_group").execute();
|
|
1613
|
+
}
|
|
1614
|
+
async function rebuildContentTaxonomies(db) {
|
|
1615
|
+
if ((await sql`PRAGMA foreign_key_list(content_taxonomies)`.execute(db)).rows.length === 0) return;
|
|
1616
|
+
await sql.raw(`DROP TABLE IF EXISTS "content_taxonomies_new"`).execute(db);
|
|
1617
|
+
await db.schema.createTable("content_taxonomies_new").addColumn("collection", "text", (c) => c.notNull()).addColumn("entry_id", "text", (c) => c.notNull()).addColumn("taxonomy_id", "text", (c) => c.notNull()).addPrimaryKeyConstraint("content_taxonomies_pk", [
|
|
1618
|
+
"collection",
|
|
1619
|
+
"entry_id",
|
|
1620
|
+
"taxonomy_id"
|
|
1621
|
+
]).execute();
|
|
1622
|
+
await sql`
|
|
1623
|
+
INSERT OR IGNORE INTO content_taxonomies_new (collection, entry_id, taxonomy_id)
|
|
1624
|
+
SELECT ct.collection, ct.entry_id, COALESCE(
|
|
1625
|
+
(SELECT t.translation_group FROM taxonomies t WHERE t.id = ct.taxonomy_id),
|
|
1626
|
+
ct.taxonomy_id
|
|
1627
|
+
)
|
|
1628
|
+
FROM content_taxonomies ct
|
|
1629
|
+
`.execute(db);
|
|
1630
|
+
await db.schema.dropTable("content_taxonomies").execute();
|
|
1631
|
+
await sql`ALTER TABLE content_taxonomies_new RENAME TO content_taxonomies`.execute(db);
|
|
1632
|
+
}
|
|
1633
|
+
async function remapMenuItemRefs(db) {
|
|
1634
|
+
const collections = await sql`SELECT slug FROM _emdash_collections`.execute(db);
|
|
1635
|
+
for (const { slug } of collections.rows) {
|
|
1636
|
+
validateIdentifier(slug, "collection slug");
|
|
1637
|
+
const ec = sql.ref(`ec_${slug}`);
|
|
1638
|
+
await sql`
|
|
1639
|
+
UPDATE _emdash_menu_items SET reference_id = (
|
|
1640
|
+
SELECT translation_group FROM ${ec} WHERE ${ec}.id = _emdash_menu_items.reference_id
|
|
1641
|
+
)
|
|
1642
|
+
WHERE reference_collection = ${slug} AND reference_id IS NOT NULL
|
|
1643
|
+
AND EXISTS (SELECT 1 FROM ${ec} WHERE ${ec}.id = _emdash_menu_items.reference_id)
|
|
1644
|
+
`.execute(db);
|
|
1645
|
+
}
|
|
1646
|
+
await sql`
|
|
1647
|
+
UPDATE _emdash_menu_items SET reference_id = (
|
|
1648
|
+
SELECT translation_group FROM taxonomies WHERE taxonomies.id = _emdash_menu_items.reference_id
|
|
1482
1649
|
)
|
|
1650
|
+
WHERE type = 'taxonomy' AND reference_id IS NOT NULL
|
|
1651
|
+
AND EXISTS (SELECT 1 FROM taxonomies WHERE taxonomies.id = _emdash_menu_items.reference_id)
|
|
1652
|
+
`.execute(db);
|
|
1653
|
+
}
|
|
1654
|
+
async function pgWiden(db, table, oldCols, newCols, defaultLocale) {
|
|
1655
|
+
validateSystemIdent(table);
|
|
1656
|
+
const ref = sql.ref(table);
|
|
1657
|
+
await sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS locale TEXT NOT NULL DEFAULT ${sql.lit(defaultLocale)}`.execute(db);
|
|
1658
|
+
await sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS translation_group TEXT`.execute(db);
|
|
1659
|
+
await sql`UPDATE ${ref} SET translation_group = id WHERE translation_group IS NULL`.execute(db);
|
|
1660
|
+
await sql`CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table}_locale`)} ON ${ref} (locale)`.execute(db);
|
|
1661
|
+
await sql`
|
|
1662
|
+
CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table}_translation_group`)} ON ${ref} (translation_group)
|
|
1663
|
+
`.execute(db);
|
|
1664
|
+
if (!oldCols || !newCols) return;
|
|
1665
|
+
for (const c of [...oldCols, ...newCols]) validateSystemIdent(c);
|
|
1666
|
+
const cons = await sql`
|
|
1667
|
+
SELECT conname FROM pg_constraint c
|
|
1668
|
+
WHERE c.conrelid = ${table}::regclass AND c.contype = 'u'
|
|
1669
|
+
AND array_length(c.conkey, 1) = ${oldCols.length}
|
|
1670
|
+
AND (
|
|
1671
|
+
SELECT array_agg(a.attname ORDER BY pos.ord)
|
|
1672
|
+
FROM unnest(c.conkey) WITH ORDINALITY AS pos(attnum, ord)
|
|
1673
|
+
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = pos.attnum
|
|
1674
|
+
)::text[] = ${oldCols}::text[]
|
|
1675
|
+
`.execute(db);
|
|
1676
|
+
for (const c of cons.rows) await sql`ALTER TABLE ${ref} DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);
|
|
1677
|
+
const cols = sql.join(newCols.map((c) => sql.ref(c)), sql`, `);
|
|
1678
|
+
await sql`
|
|
1679
|
+
ALTER TABLE ${ref}
|
|
1680
|
+
ADD CONSTRAINT ${sql.ref(`${table}_${newCols.join("_")}_unique`)} UNIQUE (${cols})
|
|
1483
1681
|
`.execute(db);
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
await
|
|
1682
|
+
}
|
|
1683
|
+
async function pgRemapContentTaxonomies(db) {
|
|
1684
|
+
const fks = await sql`
|
|
1685
|
+
SELECT conname FROM pg_constraint
|
|
1686
|
+
WHERE conrelid = 'content_taxonomies'::regclass AND contype = 'f'
|
|
1687
|
+
`.execute(db);
|
|
1688
|
+
for (const c of fks.rows) await sql`ALTER TABLE content_taxonomies DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);
|
|
1689
|
+
await sql`
|
|
1690
|
+
UPDATE content_taxonomies SET taxonomy_id = t.translation_group
|
|
1691
|
+
FROM taxonomies t WHERE t.id = content_taxonomies.taxonomy_id
|
|
1692
|
+
`.execute(db);
|
|
1693
|
+
}
|
|
1694
|
+
async function hasColumn(db, table, column) {
|
|
1695
|
+
return (await sql`PRAGMA table_info(${sql.ref(table)})`.execute(db)).rows.some((r) => r.name === column);
|
|
1696
|
+
}
|
|
1697
|
+
const SYSTEM_IDENT = /^[_a-z][a-z0-9_]*$/;
|
|
1698
|
+
function validateSystemIdent(name) {
|
|
1699
|
+
if (!SYSTEM_IDENT.test(name)) throw new Error(`Invalid identifier: "${name}"`);
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* down() is destructive on multi-locale installs (dropping `locale` collapses
|
|
1703
|
+
* translated rows onto an ambiguous unique key). Refuse to run when any row
|
|
1704
|
+
* sits at a locale other than the configured defaultLocale.
|
|
1705
|
+
*/
|
|
1706
|
+
async function assertSingleLocale(db, defaultLocale) {
|
|
1707
|
+
for (const table of [
|
|
1708
|
+
"_emdash_menus",
|
|
1709
|
+
"_emdash_menu_items",
|
|
1710
|
+
"taxonomies",
|
|
1711
|
+
"_emdash_taxonomy_defs"
|
|
1712
|
+
]) {
|
|
1713
|
+
validateSystemIdent(table);
|
|
1714
|
+
const result = await sql`
|
|
1715
|
+
SELECT COUNT(*) AS count FROM ${sql.ref(table)} WHERE locale != ${defaultLocale}
|
|
1716
|
+
`.execute(db);
|
|
1717
|
+
const count = Number(result.rows[0]?.count ?? 0);
|
|
1718
|
+
if (count > 0) throw new Error(`Cannot revert migration 036_i18n_menus_and_taxonomies: ${count} row(s) in "${table}" use a non-default locale (defaultLocale="${defaultLocale}"). Reverting would drop them silently. Export translations first (or delete them) and re-run the rollback. See packages/core/src/database/migrations/036_i18n_menus_and_taxonomies.ts.`);
|
|
1719
|
+
}
|
|
1487
1720
|
}
|
|
1488
1721
|
async function down(db) {
|
|
1489
|
-
|
|
1490
|
-
await db
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1722
|
+
const defaultLocale = getDefaultLocale();
|
|
1723
|
+
await assertSingleLocale(db, defaultLocale);
|
|
1724
|
+
const widenedTables = [
|
|
1725
|
+
"_emdash_menus",
|
|
1726
|
+
"_emdash_menu_items",
|
|
1727
|
+
"taxonomies",
|
|
1728
|
+
"_emdash_taxonomy_defs"
|
|
1729
|
+
];
|
|
1730
|
+
if (isSqlite(db)) {
|
|
1731
|
+
await sql.raw(`PRAGMA foreign_keys = OFF`).execute(db);
|
|
1732
|
+
try {
|
|
1733
|
+
for (const t of widenedTables) {
|
|
1734
|
+
await sql.raw(`DROP INDEX IF EXISTS idx_${t}_locale`).execute(db);
|
|
1735
|
+
await sql.raw(`DROP INDEX IF EXISTS idx_${t}_translation_group`).execute(db);
|
|
1736
|
+
}
|
|
1737
|
+
await rebuildContentTaxonomiesDown(db, defaultLocale);
|
|
1738
|
+
await rebuildMenusDown(db);
|
|
1739
|
+
await rebuildMenuItemsDown(db);
|
|
1740
|
+
await rebuildTaxonomiesDown(db);
|
|
1741
|
+
await rebuildTaxonomyDefsDown(db);
|
|
1742
|
+
} finally {
|
|
1743
|
+
await sql.raw(`PRAGMA foreign_keys = ON`).execute(db);
|
|
1744
|
+
}
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
for (const t of widenedTables) {
|
|
1748
|
+
await sql.raw(`DROP INDEX IF EXISTS idx_${t}_locale`).execute(db);
|
|
1749
|
+
await sql.raw(`DROP INDEX IF EXISTS idx_${t}_translation_group`).execute(db);
|
|
1750
|
+
await sql.raw(`ALTER TABLE "${t}" DROP COLUMN IF EXISTS locale`).execute(db);
|
|
1751
|
+
await sql.raw(`ALTER TABLE "${t}" DROP COLUMN IF EXISTS translation_group`).execute(db);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
async function rebuildContentTaxonomiesDown(db, defaultLocale) {
|
|
1755
|
+
await sql.raw(`DROP TABLE IF EXISTS "content_taxonomies_new"`).execute(db);
|
|
1756
|
+
await db.schema.createTable("content_taxonomies_new").addColumn("collection", "text", (c) => c.notNull()).addColumn("entry_id", "text", (c) => c.notNull()).addColumn("taxonomy_id", "text", (c) => c.notNull()).addPrimaryKeyConstraint("content_taxonomies_pk", [
|
|
1757
|
+
"collection",
|
|
1758
|
+
"entry_id",
|
|
1759
|
+
"taxonomy_id"
|
|
1760
|
+
]).addForeignKeyConstraint("content_taxonomies_taxonomy_fk", ["taxonomy_id"], "taxonomies", ["id"], (cb) => cb.onDelete("cascade")).execute();
|
|
1761
|
+
await sql`
|
|
1762
|
+
INSERT OR IGNORE INTO content_taxonomies_new (collection, entry_id, taxonomy_id)
|
|
1763
|
+
SELECT ct.collection, ct.entry_id, COALESCE(
|
|
1764
|
+
(SELECT t.id FROM taxonomies t WHERE t.translation_group = ct.taxonomy_id AND t.locale = ${defaultLocale}),
|
|
1765
|
+
ct.taxonomy_id
|
|
1766
|
+
)
|
|
1767
|
+
FROM content_taxonomies ct
|
|
1768
|
+
`.execute(db);
|
|
1769
|
+
await db.schema.dropTable("content_taxonomies").execute();
|
|
1770
|
+
await sql`ALTER TABLE content_taxonomies_new RENAME TO content_taxonomies`.execute(db);
|
|
1771
|
+
}
|
|
1772
|
+
async function rebuildMenusDown(db) {
|
|
1773
|
+
await sql.raw(`DROP TABLE IF EXISTS "_emdash_menus_old"`).execute(db);
|
|
1774
|
+
await db.schema.createTable("_emdash_menus_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull().unique()).addColumn("label", "text", (c) => c.notNull()).addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).addColumn("updated_at", "text", (c) => c.defaultTo(currentTimestamp(db))).execute();
|
|
1775
|
+
await sql`
|
|
1776
|
+
INSERT INTO _emdash_menus_old (id, name, label, created_at, updated_at)
|
|
1777
|
+
SELECT id, name, label, created_at, updated_at FROM _emdash_menus
|
|
1778
|
+
`.execute(db);
|
|
1779
|
+
await db.schema.dropTable("_emdash_menus").execute();
|
|
1780
|
+
await sql`ALTER TABLE _emdash_menus_old RENAME TO _emdash_menus`.execute(db);
|
|
1781
|
+
}
|
|
1782
|
+
async function rebuildMenuItemsDown(db) {
|
|
1783
|
+
await sql.raw(`ALTER TABLE _emdash_menu_items DROP COLUMN locale`).execute(db);
|
|
1784
|
+
await sql.raw(`ALTER TABLE _emdash_menu_items DROP COLUMN translation_group`).execute(db);
|
|
1785
|
+
}
|
|
1786
|
+
async function rebuildTaxonomiesDown(db) {
|
|
1787
|
+
await sql.raw(`DROP TABLE IF EXISTS "taxonomies_old"`).execute(db);
|
|
1788
|
+
await db.schema.createTable("taxonomies_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull()).addColumn("slug", "text", (c) => c.notNull()).addColumn("label", "text", (c) => c.notNull()).addColumn("parent_id", "text").addColumn("data", "text").addUniqueConstraint("taxonomies_name_slug_unique", ["name", "slug"]).addForeignKeyConstraint("taxonomies_parent_fk", ["parent_id"], "taxonomies_old", ["id"], (cb) => cb.onDelete("set null")).execute();
|
|
1789
|
+
await sql`
|
|
1790
|
+
INSERT INTO taxonomies_old (id, name, slug, label, parent_id, data)
|
|
1791
|
+
SELECT id, name, slug, label, parent_id, data FROM taxonomies
|
|
1792
|
+
`.execute(db);
|
|
1793
|
+
await db.schema.dropTable("taxonomies").execute();
|
|
1794
|
+
await sql`ALTER TABLE taxonomies_old RENAME TO taxonomies`.execute(db);
|
|
1795
|
+
await db.schema.createIndex("idx_taxonomies_name").on("taxonomies").column("name").execute();
|
|
1796
|
+
}
|
|
1797
|
+
async function rebuildTaxonomyDefsDown(db) {
|
|
1798
|
+
await sql.raw(`DROP TABLE IF EXISTS "_emdash_taxonomy_defs_old"`).execute(db);
|
|
1799
|
+
await db.schema.createTable("_emdash_taxonomy_defs_old").addColumn("id", "text", (c) => c.primaryKey()).addColumn("name", "text", (c) => c.notNull().unique()).addColumn("label", "text", (c) => c.notNull()).addColumn("label_singular", "text").addColumn("hierarchical", "integer", (c) => c.defaultTo(0)).addColumn("collections", "text").addColumn("created_at", "text", (c) => c.defaultTo(currentTimestamp(db))).execute();
|
|
1800
|
+
await sql`
|
|
1801
|
+
INSERT INTO _emdash_taxonomy_defs_old
|
|
1802
|
+
(id, name, label, label_singular, hierarchical, collections, created_at)
|
|
1803
|
+
SELECT id, name, label, label_singular, hierarchical, collections, created_at
|
|
1804
|
+
FROM _emdash_taxonomy_defs
|
|
1805
|
+
`.execute(db);
|
|
1806
|
+
await db.schema.dropTable("_emdash_taxonomy_defs").execute();
|
|
1807
|
+
await sql`ALTER TABLE _emdash_taxonomy_defs_old RENAME TO _emdash_taxonomy_defs`.execute(db);
|
|
1494
1808
|
}
|
|
1495
1809
|
|
|
1496
1810
|
//#endregion
|
|
@@ -1529,7 +1843,8 @@ const MIGRATIONS = Object.freeze({
|
|
|
1529
1843
|
"032_rate_limits": _032_rate_limits_exports,
|
|
1530
1844
|
"033_optimize_content_indexes": _033_optimize_content_indexes_exports,
|
|
1531
1845
|
"034_published_at_index": _034_published_at_index_exports,
|
|
1532
|
-
"035_bounded_404_log": _035_bounded_404_log_exports
|
|
1846
|
+
"035_bounded_404_log": _035_bounded_404_log_exports,
|
|
1847
|
+
"036_i18n_menus_and_taxonomies": _036_i18n_menus_and_taxonomies_exports
|
|
1533
1848
|
});
|
|
1534
1849
|
/** Total number of registered migrations. Exported for use in tests. */
|
|
1535
1850
|
const MIGRATION_COUNT = Object.keys(MIGRATIONS).length;
|
|
@@ -1564,22 +1879,130 @@ async function getMigrationStatus(db) {
|
|
|
1564
1879
|
pending
|
|
1565
1880
|
};
|
|
1566
1881
|
}
|
|
1882
|
+
/** Pattern for escaping special regex characters. Matches the shared helper in `database/repositories/content.ts`. */
|
|
1883
|
+
const REGEX_ESCAPE_PATTERN = /[.*+?^${}()|[\]\\]/g;
|
|
1884
|
+
/** Escape special regex characters so a string can be embedded literally in `new RegExp()`. */
|
|
1885
|
+
function escapeRegExp(value) {
|
|
1886
|
+
return value.replace(REGEX_ESCAPE_PATTERN, "\\$&");
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Pattern used to detect the concurrent-migration race. The Kysely
|
|
1890
|
+
* `SqliteAdapter.acquireMigrationLock` is a no-op (inherited by `kysely-d1`
|
|
1891
|
+
* and our `EmDashD1Dialect`), so two isolates running migrations against the
|
|
1892
|
+
* same database can both attempt `INSERT INTO _emdash_migrations` for the
|
|
1893
|
+
* same migration name. The losing insert fails with a UNIQUE constraint
|
|
1894
|
+
* error, which is benign: the other isolate is applying the same schema.
|
|
1895
|
+
*
|
|
1896
|
+
* We match on the table name (not the full error text) because different
|
|
1897
|
+
* SQLite drivers phrase the message differently
|
|
1898
|
+
* (`UNIQUE constraint failed: _emdash_migrations.name` for better-sqlite3,
|
|
1899
|
+
* `D1_ERROR: UNIQUE constraint failed: _emdash_migrations.name: SQLITE_CONSTRAINT`
|
|
1900
|
+
* for D1, etc.). The pattern is built from `MIGRATION_TABLE` so a rename
|
|
1901
|
+
* cannot silently disable race detection.
|
|
1902
|
+
*/
|
|
1903
|
+
const MIGRATION_RACE_PATTERN = new RegExp(`UNIQUE constraint failed: ${escapeRegExp(MIGRATION_TABLE)}\\.name`, "i");
|
|
1904
|
+
/** How long to wait for a concurrent migrator to finish before giving up. */
|
|
1905
|
+
const MIGRATION_RACE_WAIT_MS = 1e4;
|
|
1906
|
+
/** Polling interval while waiting for a concurrent migrator. */
|
|
1907
|
+
const MIGRATION_RACE_POLL_MS = 100;
|
|
1908
|
+
/**
|
|
1909
|
+
* Pattern used to detect "table does not exist" errors across the dialects
|
|
1910
|
+
* EmDash supports. The phrasing differs by driver:
|
|
1911
|
+
*
|
|
1912
|
+
* - better-sqlite3: `no such table: _emdash_migrations`
|
|
1913
|
+
* - D1: `D1_ERROR: no such table: _emdash_migrations: SQLITE_ERROR`
|
|
1914
|
+
* - PostgreSQL: `relation "_emdash_migrations" does not exist`
|
|
1915
|
+
* (also occasionally `table "_emdash_migrations" does not exist`)
|
|
1916
|
+
*
|
|
1917
|
+
* We deliberately match on the migration table name (rather than using the
|
|
1918
|
+
* generic `isMissingTableError` helper) so an unexpected missing-table error
|
|
1919
|
+
* naming a different table — implausible today since
|
|
1920
|
+
* `getAppliedMigrationCount` only references `MIGRATION_TABLE`, but cheap
|
|
1921
|
+
* insurance against future edits — is not silently swallowed. The pattern is
|
|
1922
|
+
* built from `MIGRATION_TABLE` so a rename cannot drift.
|
|
1923
|
+
*/
|
|
1924
|
+
const MIGRATION_TABLE_MISSING_PATTERN = new RegExp(`(?:no such table:\\s*${escapeRegExp(MIGRATION_TABLE)}\\b|(?:relation|table)\\s+"?${escapeRegExp(MIGRATION_TABLE)}"?\\s+does(?:n't| not) exist\\b)`, "i");
|
|
1925
|
+
/**
|
|
1926
|
+
* Read the count of applied migrations.
|
|
1927
|
+
*
|
|
1928
|
+
* Returns `null` only when the migration table does not exist yet (which is
|
|
1929
|
+
* the normal state on a fresh database before the first migration runs).
|
|
1930
|
+
* Any other error is rethrown so callers — particularly
|
|
1931
|
+
* `waitForConcurrentMigrator` — don't silently mask connection failures,
|
|
1932
|
+
* permission errors, or other unexpected driver problems behind a 10s wait
|
|
1933
|
+
* and a bogus "we're done" verdict.
|
|
1934
|
+
*/
|
|
1935
|
+
async function getAppliedMigrationCount(db) {
|
|
1936
|
+
try {
|
|
1937
|
+
const result = await sql`
|
|
1938
|
+
SELECT COUNT(*) as count FROM ${sql.ref(MIGRATION_TABLE)}
|
|
1939
|
+
`.execute(db);
|
|
1940
|
+
return Number(result.rows[0]?.count ?? 0);
|
|
1941
|
+
} catch (error) {
|
|
1942
|
+
if (MIGRATION_TABLE_MISSING_PATTERN.test(deepErrorMessage(error))) return null;
|
|
1943
|
+
throw error;
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Wait for a concurrent migrator to finish applying all migrations.
|
|
1948
|
+
*
|
|
1949
|
+
* Resolves to `true` once the migration table contains at least
|
|
1950
|
+
* `MIGRATION_COUNT` rows (i.e. every migration this build knows about has
|
|
1951
|
+
* been recorded), `false` if the deadline elapses first. We use `>=` rather
|
|
1952
|
+
* than `===` so that an old isolate observing a database that has already
|
|
1953
|
+
* been migrated by a newer build still treats the wait as settled instead
|
|
1954
|
+
* of timing out.
|
|
1955
|
+
*/
|
|
1956
|
+
async function waitForConcurrentMigrator(db) {
|
|
1957
|
+
const deadline = Date.now() + MIGRATION_RACE_WAIT_MS;
|
|
1958
|
+
while (Date.now() < deadline) {
|
|
1959
|
+
const count = await getAppliedMigrationCount(db);
|
|
1960
|
+
if (count !== null && count >= MIGRATION_COUNT) return true;
|
|
1961
|
+
await new Promise((resolve) => setTimeout(resolve, MIGRATION_RACE_POLL_MS));
|
|
1962
|
+
}
|
|
1963
|
+
const finalCount = await getAppliedMigrationCount(db);
|
|
1964
|
+
return finalCount !== null && finalCount >= MIGRATION_COUNT;
|
|
1965
|
+
}
|
|
1966
|
+
/** Extract the deepest error message available from a thrown value. */
|
|
1967
|
+
function deepErrorMessage(error) {
|
|
1968
|
+
if (error instanceof Error) {
|
|
1969
|
+
const own = error.message ?? "";
|
|
1970
|
+
if (error.cause) {
|
|
1971
|
+
const causeMsg = deepErrorMessage(error.cause);
|
|
1972
|
+
return own ? `${own}: ${causeMsg}` : causeMsg;
|
|
1973
|
+
}
|
|
1974
|
+
return own;
|
|
1975
|
+
}
|
|
1976
|
+
if (typeof error === "string") return error;
|
|
1977
|
+
try {
|
|
1978
|
+
return JSON.stringify(error);
|
|
1979
|
+
} catch {
|
|
1980
|
+
return String(error);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1567
1983
|
/**
|
|
1568
1984
|
* Run all pending migrations.
|
|
1569
1985
|
*
|
|
1570
1986
|
* Includes a fast-path: if the migration table already exists and contains
|
|
1571
|
-
*
|
|
1572
|
-
* skip the Kysely Migrator entirely. This avoids
|
|
1573
|
-
* `pragma_table_info` introspection that Kysely runs for
|
|
1574
|
-
* database (twice!) just to check if the migration
|
|
1575
|
-
* On D1 with ~57 tables, that's ~116 queries saved per init.
|
|
1987
|
+
* at least MIGRATION_COUNT rows, all migrations this build knows about have
|
|
1988
|
+
* been applied and we can skip the Kysely Migrator entirely. This avoids
|
|
1989
|
+
* the expensive `pragma_table_info` introspection that Kysely runs for
|
|
1990
|
+
* every table in the database (twice!) just to check if the migration
|
|
1991
|
+
* tables exist. On D1 with ~57 tables, that's ~116 queries saved per init.
|
|
1992
|
+
*
|
|
1993
|
+
* Concurrent-migration safety: the Kysely Migrator's `acquireMigrationLock`
|
|
1994
|
+
* is a no-op for SQLite (and therefore D1), so two callers running this
|
|
1995
|
+
* concurrently against the same database will both try to apply pending
|
|
1996
|
+
* migrations. SQLite serializes the writes, but the loser still surfaces a
|
|
1997
|
+
* `UNIQUE constraint failed: _emdash_migrations.name` error. We treat that
|
|
1998
|
+
* specific error as benign: another caller is already applying the same
|
|
1999
|
+
* schema. We wait for the concurrent migrator to finish, then return
|
|
2000
|
+
* success. This matches the user-observable expectation that running
|
|
2001
|
+
* migrations twice in a row is a no-op.
|
|
1576
2002
|
*/
|
|
1577
2003
|
async function runMigrations(db) {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
SELECT COUNT(*) as count FROM ${sql.ref(MIGRATION_TABLE)}
|
|
1581
|
-
`.execute(db)).rows[0]?.count === MIGRATION_COUNT) return { applied: [] };
|
|
1582
|
-
} catch {}
|
|
2004
|
+
const initialCount = await getAppliedMigrationCount(db);
|
|
2005
|
+
if (initialCount !== null && initialCount >= MIGRATION_COUNT) return { applied: [] };
|
|
1583
2006
|
const { error, results } = await new Migrator({
|
|
1584
2007
|
db,
|
|
1585
2008
|
provider: new StaticMigrationProvider(),
|
|
@@ -1588,11 +2011,13 @@ async function runMigrations(db) {
|
|
|
1588
2011
|
}).migrateToLatest();
|
|
1589
2012
|
const applied = results?.filter((r) => r.status === "Success").map((r) => r.migrationName) ?? [];
|
|
1590
2013
|
if (error) {
|
|
1591
|
-
|
|
1592
|
-
if (!msg && error instanceof Error && error.cause) msg = error.cause instanceof Error ? error.cause.message : JSON.stringify(error.cause);
|
|
2014
|
+
const msg = deepErrorMessage(error);
|
|
1593
2015
|
const failedMigration = results?.find((r) => r.status === "Error");
|
|
1594
|
-
if (
|
|
1595
|
-
|
|
2016
|
+
if (MIGRATION_RACE_PATTERN.test(msg)) {
|
|
2017
|
+
if (await waitForConcurrentMigrator(db)) return { applied };
|
|
2018
|
+
}
|
|
2019
|
+
const failedSuffix = failedMigration ? ` (migration: ${failedMigration.migrationName})` : "";
|
|
2020
|
+
throw new Error(`Migration failed: ${msg || "unknown error"}${failedSuffix}`);
|
|
1596
2021
|
}
|
|
1597
2022
|
return { applied };
|
|
1598
2023
|
}
|
|
@@ -1615,5 +2040,5 @@ async function rollbackMigration(db) {
|
|
|
1615
2040
|
}
|
|
1616
2041
|
|
|
1617
2042
|
//#endregion
|
|
1618
|
-
export { rollbackMigration as n, runMigrations as r, getMigrationStatus as t };
|
|
1619
|
-
//# sourceMappingURL=runner-
|
|
2043
|
+
export { __exportAll as i, rollbackMigration as n, runMigrations as r, getMigrationStatus as t };
|
|
2044
|
+
//# sourceMappingURL=runner-DMnlIkh4.mjs.map
|