From b865dd78bfb2b4d6805c7aa9526d85819da7a5ee Mon Sep 17 00:00:00 2001 From: tuzua Date: Thu, 9 Jul 2026 17:32:04 +0100 Subject: [PATCH] feat: support emdash 0.28 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: support emdash 0.28 Migrate @dashcommerce/core and the starter from emdash 0.6.0 to 0.28.1. - Build the native ResolvedPlugin with definePlugin (single-arg RouteContext handlers) instead of adaptSandboxEntry, whose 0.28 form flattens the request and drops the body — breaking the Stripe webhook. - Update capabilities to the current vocabulary (network:request, content:read, content:write, media:read, users:read); bump the emdash peer range to >=0.28.0 <0.29.0. - Re-author the emdash patch for 0.28.1: plugin routes may return a raw Response (cookies, redirects, webhook 200s), and the raw request body is preserved so ctx.request.text() works for Stripe signature verification. - Wire a Cloudflare Worker entry (src/worker.ts) + Cron Trigger so plugin cron (abandoned-cart recovery, dunning, stock-lock sweeps) runs on Workers — emdash 0.19+ drives cron from a scheduled() handler. --- .changeset/support-emdash-0-28.md | 24 ++++ bun.lock | 94 ++++++++++---- package.json | 2 +- packages/core/package.json | 6 +- packages/core/src/index.ts | 20 +-- packages/core/src/sandbox-entry.ts | 201 +++++++++++++++++------------ packages/starter/emdash-env.d.ts | 37 ++++-- packages/starter/package.json | 4 +- packages/starter/src/worker.ts | 15 +++ packages/starter/wrangler.jsonc | 24 ++-- patches/emdash@0.28.1.patch | 49 +++++++ patches/emdash@0.6.0.patch | 62 --------- 12 files changed, 332 insertions(+), 206 deletions(-) create mode 100644 .changeset/support-emdash-0-28.md create mode 100644 packages/starter/src/worker.ts create mode 100644 patches/emdash@0.28.1.patch delete mode 100644 patches/emdash@0.6.0.patch diff --git a/.changeset/support-emdash-0-28.md b/.changeset/support-emdash-0-28.md new file mode 100644 index 0000000..1db9652 --- /dev/null +++ b/.changeset/support-emdash-0-28.md @@ -0,0 +1,24 @@ +--- +"@dashcommerce/core": patch +"@dashcommerce/starter": patch +--- + +Support EmDash 0.28. + +Migrate from emdash 0.6 to 0.28.1. The plugin now builds its native +`ResolvedPlugin` via `definePlugin` with single-argument `RouteContext` +handlers (emdash's native route shape) instead of `adaptSandboxEntry`, whose +0.28 form flattens the request and would break the Stripe webhook's raw-body +signature check. Capability names are updated to the current vocabulary +(`network:request`, `content:read`, `content:write`, `media:read`, +`users:read`), and the emdash peer range is now `>=0.28.0 <0.29.0`. + +The bundled emdash patch is re-authored for 0.28.1: plugin route handlers may +still return a raw `Response` (cookies, redirects, webhook 200s), and the raw +request body is preserved so `ctx.request.text()` works for Stripe webhook +signature verification. + +The starter adds a Cloudflare Worker entry (`src/worker.ts`) plus a Cron +Trigger so plugin cron — abandoned-cart recovery, subscription dunning, and +stock-lock sweeps — runs on Workers (emdash 0.19+ drives cron from a +`scheduled()` handler, not request side effects). diff --git a/bun.lock b/bun.lock index 4ce200e..74e83d8 100644 --- a/bun.lock +++ b/bun.lock @@ -13,24 +13,24 @@ }, "packages/core": { "name": "@dashcommerce/core", - "version": "0.1.3", + "version": "0.1.4", "bin": { "dashcommerce-merge-seed": "./dist/cli/merge-seed.js", }, "devDependencies": { - "@emdash-cms/admin": "^0.6.0", + "@emdash-cms/admin": "^0.28.1", "@stripe/react-stripe-js": "^3.0.0", "@stripe/stripe-js": "^5.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "emdash": "^0.6.0", + "emdash": "^0.28.1", "react": "^19.0.0", "react-dom": "^19.0.0", "tsdown": "^0.20.0", "typescript": "^5.9.3", }, "peerDependencies": { - "emdash": ">=0.5.0 <0.7.0", + "emdash": ">=0.28.0 <0.29.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", }, @@ -41,7 +41,7 @@ }, "packages/create": { "name": "@dashcommerce/create", - "version": "0.1.0", + "version": "0.2.0", "bin": { "create-dashcommerce": "./dist/index.mjs", }, @@ -59,18 +59,18 @@ }, "packages/starter": { "name": "@dashcommerce/starter", - "version": "0.2.0", + "version": "0.3.0", "dependencies": { "@astrojs/cloudflare": "^13.1.10", "@astrojs/node": "^10.0.0", "@astrojs/react": "^5.0.0", "@astrojs/sitemap": "^3.4.0", "@dashcommerce/core": "workspace:*", - "@emdash-cms/cloudflare": "^0.6.0", + "@emdash-cms/cloudflare": "^0.28.1", "@stripe/react-stripe-js": "^6.0.0", "@stripe/stripe-js": "^9.0.0", "astro": "^6.0.0", - "emdash": "^0.6.0", + "emdash": "^0.28.1", "react": "^19.0.0", "react-dom": "^19.0.0", }, @@ -86,7 +86,7 @@ }, }, "patchedDependencies": { - "emdash@0.6.0": "patches/emdash@0.6.0.patch", + "emdash@0.28.1": "patches/emdash@0.28.1.patch", }, "packages": { "@astrojs/check": ["@astrojs/check@0.9.8", "", { "dependencies": { "@astrojs/language-server": "^2.16.5", "chokidar": "^4.0.3", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "astro-check": "bin/astro-check.js" } }, "sha512-LDng8446QLS5ToKjRHd3bgUdirvemVVExV7nRyJfW2wV36xuv7vDxwy5NWN9zqeSEDgg0Tv84sP+T3yEq+Zlkw=="], @@ -115,6 +115,24 @@ "@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.3", "", { "dependencies": { "yaml": "^2.8.2" } }, "sha512-PJzRmgQzUxI2uwpdX2lXSHtP4G8ocp24/t+bZyf5Fy0SZLSF9f9KXZoMlFM/XCGue+B0nH/2IZ7FpBYQATBsCg=="], + "@atcute/atproto": ["@atcute/atproto@4.0.3", "", { "dependencies": { "@atcute/lexicons": "^2.0.2" } }, "sha512-BNylfO7nK0yYBpSpnGhOYgrJTeZWrXHPrb6tOQmp9A3Am0epctIWm6/5lPC4ZNPHpUbwr5w/LzH/v7kjAoKEDg=="], + + "@atcute/client": ["@atcute/client@5.1.1", "", { "dependencies": { "@atcute/identity": "^2.0.1", "@atcute/lexicons": "^2.0.2" } }, "sha512-cn5/Zi/qo37WtQG6gzIC7JPs0RDzX9Z4eaceX45SpKgLZoc3fCFDJcE7C8xsbxBNfjry2T6PmUxWA8obebZsEQ=="], + + "@atcute/identity": ["@atcute/identity@2.0.1", "", { "dependencies": { "@atcute/lexicons": "^2.0.2", "valibot": "^1.4.1" } }, "sha512-FEURUvl30SyyWWikkvm+MLz0Snuf0OF10L/qxRhWjj6qDB5Ib+XWhiBuwidjvhCkrCepTUNLbj4TlUm/gHaUig=="], + + "@atcute/identity-resolver": ["@atcute/identity-resolver@2.0.1", "", { "dependencies": { "@atcute/lexicons": "^2.0.2", "@atcute/util-fetch": "^2.0.1", "valibot": "^1.4.1" }, "peerDependencies": { "@atcute/identity": "^2.0.0" } }, "sha512-0enA9w7XnbbqsZ5Rcl6jXLf7ZZuwFQ9dBmxFq3qOxPHLaCETsqsrQflXDPqiM27TnZwYq8sqCV5D1mFOksggDQ=="], + + "@atcute/lexicons": ["@atcute/lexicons@2.0.2", "", { "dependencies": { "@atcute/uint8array": "^1.1.3", "@atcute/util-text": "^1.3.3", "@standard-schema/spec": "^1.1.0", "esm-env": "^1.2.2" } }, "sha512-ATBADJAy4KQ76NB86BjgYKrRdbDRUo76Cbqna4WIfQAgN105Rcy972MiNKs+BSmcOOM3WakilgTm0CXD4RC0iA=="], + + "@atcute/multibase": ["@atcute/multibase@1.2.4", "", { "dependencies": { "@atcute/uint8array": "^1.1.3" } }, "sha512-WeX12hvFZEim6C+cyv7Eqd93w6DzubNWQGmTFBghjsEuXvMe4HbBCYvsti0OUnbA5qLBPlsTyssQUJeLlHCzIw=="], + + "@atcute/uint8array": ["@atcute/uint8array@1.1.4", "", {}, "sha512-rSW5AFVCIN4ooH7vEZB+J60+uWjn5fRBQAQL58qHLiDm8+xDPmHfEU5GfYOJuuD+7UBj8KKiQugIzohFn8/xPw=="], + + "@atcute/util-fetch": ["@atcute/util-fetch@2.0.1", "", { "dependencies": { "valibot": "^1.4.1" } }, "sha512-ugWTOLemA8OxSOj7c8q6ncRmBGFDHSwwE1YinO+PCtaw6WLQFGBfHn+yikQ0e3wTK2t4IPjQ5PxZcRXm961ZVA=="], + + "@atcute/util-text": ["@atcute/util-text@1.3.3", "", { "dependencies": { "unicode-segmenter": "^0.14.5" } }, "sha512-WhedTmg/msFhrdwXw9RjnNcDl8Vmisxl4+Vzyf5k3+8Gj5TKQg72dLSDtBNmNLd61RbHjgfQRBgE0ez6q/jciw=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], @@ -155,9 +173,9 @@ "@babel/types": ["@babel/types@8.0.0-rc.2", "", { "dependencies": { "@babel/helper-string-parser": "^8.0.0-rc.2", "@babel/helper-validator-identifier": "^8.0.0-rc.2" } }, "sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw=="], - "@base-ui/react": ["@base-ui/react@1.4.1", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@base-ui/utils": "0.2.8", "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@date-fns/tz": "^1.2.0", "@types/react": "^17 || ^18 || ^19", "date-fns": "^4.0.0", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@date-fns/tz", "@types/react", "date-fns"] }, "sha512-Ab5/LIhcmL8BQcsBUYiOfkSDRdLpvgUBzMK30cu684JPcLclYlztharvCZyNNgzJtbAiREzI9q0pI5erHCMgCw=="], + "@base-ui/react": ["@base-ui/react@1.6.0", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@base-ui/utils": "0.3.1", "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@date-fns/tz": "^1.2.0", "@types/react": "^17 || ^18 || ^19", "date-fns": "^4.0.0", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@date-fns/tz", "@types/react", "date-fns"] }, "sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw=="], - "@base-ui/utils": ["@base-ui/utils@0.2.8", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-jvOi+c+ftGlGotNcKnzPVg2IhCaDTB6/6R3JeqdjdXktuAJi3wKH9T7+svuaKh1mmfVU11UWzUZVH74JDfi/wQ=="], + "@base-ui/utils": ["@base-ui/utils@0.3.1", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.2.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg=="], "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], @@ -221,7 +239,7 @@ "@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="], - "@cloudflare/kumo": ["@cloudflare/kumo@1.19.0", "", { "dependencies": { "@base-ui/react": "^1.2.0", "@shikijs/langs": "^4.0.0", "@shikijs/themes": "^4.0.0", "clsx": "^2.1.1", "motion": "^12.34.1", "react-day-picker": "^9.13.2", "shiki": "^4.0.0", "tailwind-merge": "^3.4.0" }, "peerDependencies": { "@phosphor-icons/react": "^2.1.10", "echarts": "^6.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "zod": "^4.0.0" }, "optionalPeers": ["zod"], "bin": { "kumo": "bin/kumo.js" } }, "sha512-Ob91fqIbVwXMk/DTrod8oqs/uLiHOA1AbddJxfoPAuKRSjvVeImck34aow6ZJhWrfJi3uxLFOAI3569r5QFE0A=="], + "@cloudflare/kumo": ["@cloudflare/kumo@2.6.0", "", { "dependencies": { "@base-ui/react": "^1.5.0", "@shikijs/langs": "^4.0.0", "@shikijs/themes": "^4.0.0", "clsx": "^2.1.1", "motion": "^12.34.1", "react-day-picker": "^9.13.2", "shiki": "^4.0.0", "tailwind-merge": "^3.4.0" }, "peerDependencies": { "@phosphor-icons/react": "^2.1.10", "echarts": "^6.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "zod": "^4.0.0" }, "optionalPeers": ["echarts", "zod"], "bin": { "kumo": "bin/kumo.js" } }, "sha512-rcUUvhrtxI0veNJZLgKVSnxH/L0M48jtc4UoNhvu0l0RiRmjHORFTBYq/phFQyt7JGG3s98QPZc5w1eW+UQIOg=="], "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="], @@ -259,15 +277,21 @@ "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="], - "@emdash-cms/admin": ["@emdash-cms/admin@0.6.0", "", { "dependencies": { "@cloudflare/kumo": "^1.16.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@emdash-cms/blocks": "0.6.0", "@floating-ui/react": "^0.27.16", "@lingui/core": "^5.9.4", "@lingui/react": "^5.9.4", "@phosphor-icons/react": "^2.1.10", "@tanstack/react-query": "5.90.21", "@tanstack/react-router": "1.163.2", "@tiptap/core": "^3.20.0", "@tiptap/extension-character-count": "^3.20.0", "@tiptap/extension-drag-handle": "^3.20.0", "@tiptap/extension-drag-handle-react": "^3.20.0", "@tiptap/extension-dropcursor": "^3.20.0", "@tiptap/extension-focus": "^3.20.0", "@tiptap/extension-link": "^3.20.0", "@tiptap/extension-node-range": "^3.20.0", "@tiptap/extension-placeholder": "^3.20.0", "@tiptap/extension-text-align": "^3.20.0", "@tiptap/extension-typography": "^3.20.0", "@tiptap/extension-underline": "^3.20.0", "@tiptap/pm": "^3.20.0", "@tiptap/react": "^3.20.0", "@tiptap/starter-kit": "^3.20.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dompurify": "^3.3.2", "marked": "^17.0.3", "react": "19.2.4", "react-dom": "19.2.4", "react-hotkeys-hook": "^5.2.4", "tailwind-merge": "^3.3.0" } }, "sha512-RH3HQ8ZovTrKA6l3GvGhp1xifdNxINOUV2ohsVa43dbSLRL+h5ojExCOCQ/g7YoUAYg11F3jjzdDOZxnlOcxcA=="], + "@emdash-cms/admin": ["@emdash-cms/admin@0.28.1", "", { "dependencies": { "@atcute/identity-resolver": "^2.0.0", "@atcute/lexicons": "^2.0.0", "@cloudflare/kumo": "2.6.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@emdash-cms/blocks": "0.28.1", "@emdash-cms/plugin-types": "0.1.0", "@emdash-cms/registry-client": "0.3.2", "@emdash-cms/registry-lexicons": "0.1.1", "@floating-ui/react": "^0.27.16", "@lingui/core": "^5.9.4", "@lingui/react": "^5.9.4", "@phosphor-icons/react": "^2.1.10", "@tanstack/react-query": "5.90.21", "@tanstack/react-router": "1.163.2", "@tiptap/core": "^3.20.0", "@tiptap/extension-character-count": "^3.20.0", "@tiptap/extension-code-block": "^3.20.0", "@tiptap/extension-collaboration": "^3.20.0", "@tiptap/extension-drag-handle": "^3.20.0", "@tiptap/extension-drag-handle-react": "^3.20.0", "@tiptap/extension-dropcursor": "^3.20.0", "@tiptap/extension-focus": "^3.20.0", "@tiptap/extension-link": "^3.20.0", "@tiptap/extension-node-range": "^3.20.0", "@tiptap/extension-placeholder": "^3.20.0", "@tiptap/extension-table": "^3.20.0", "@tiptap/extension-table-cell": "^3.20.0", "@tiptap/extension-table-header": "^3.20.0", "@tiptap/extension-table-row": "^3.20.0", "@tiptap/extension-text-align": "^3.20.0", "@tiptap/extension-typography": "^3.20.0", "@tiptap/extension-underline": "^3.20.0", "@tiptap/pm": "^3.20.0", "@tiptap/react": "^3.20.0", "@tiptap/starter-kit": "^3.20.0", "@tiptap/y-tiptap": "^3.0.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dompurify": "^3.3.2", "marked": "^17.0.3", "react-hotkeys-hook": "^5.2.4", "tailwind-merge": "^3.3.0", "y-protocols": "^1.0.7", "yjs": "^13.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-AuqvR1A54YCtjH68jKy3DITmLuXlmFi7cETE2Q5yFuSK+jKEUY4xbbg2hql6JPG7G5zjDxHQsIdHfy/gQthDkw=="], + + "@emdash-cms/auth": ["@emdash-cms/auth@0.28.1", "", { "dependencies": { "@oslojs/crypto": "^1.0.1", "@oslojs/encoding": "^1.1.0", "@oslojs/webauthn": "^1.0.0", "ulidx": "^2.4.1", "zod": "^4.4.1" }, "peerDependencies": { "astro": ">=6.0.0-beta.0", "kysely": "^0.29.0" }, "optionalPeers": ["kysely"] }, "sha512-JKCcMvYK9x0h/2BkK3AF1dfKcZZq5uJG5TkkQ+zETjUoEO60KARQ94Pu0bqb14GymG6GN8aSvn6gDhyyju6YAg=="], + + "@emdash-cms/blocks": ["@emdash-cms/blocks@0.28.1", "", { "dependencies": { "@cloudflare/kumo": "2.6.0", "@phosphor-icons/react": "^2.1.10", "clsx": "^2.1.1", "echarts": "^6.0.0", "tailwind-merge": "^3.3.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-Kp4RXmKcGQrIIvBMDL+RQQcMV8G8PzmqA8VtquvrP/NZ/pDmsTRtO1Kw8ZwwWHiOjry7jtlWRP6YYZ1EsZmcpw=="], + + "@emdash-cms/cloudflare": ["@emdash-cms/cloudflare@0.28.1", "", { "dependencies": { "emdash": "0.28.1", "jose": "^6.1.3", "kysely-d1": "^0.4.0", "ulidx": "^2.4.1" }, "peerDependencies": { "@astrojs/cloudflare": ">=12.0.0", "@cloudflare/workers-types": ">=4.0.0", "astro": ">=6.0.0-beta.0", "kysely": ">=0.28.17", "pg": ">=8.16.3" }, "optionalPeers": ["pg"] }, "sha512-Lf0YVxF7VkvCAsXIufi3as59BQKIqI6pSHVvB7SOkH7kIwihdiKWxAfkXmSAJ/k+WiJ/jJh+YAgXNLwP5921sA=="], - "@emdash-cms/auth": ["@emdash-cms/auth@0.6.0", "", { "dependencies": { "@oslojs/crypto": "^1.0.1", "@oslojs/encoding": "^1.1.0", "@oslojs/webauthn": "^1.0.0", "ulidx": "^2.4.1", "zod": "^4.3.5" }, "peerDependencies": { "astro": ">=6.0.0-beta.0", "kysely": "^0.27.0" }, "optionalPeers": ["kysely"] }, "sha512-1ikvF2jYifAMvAIn5ulS8ljnl5eDy3qNKZoVIfGaqtTKQqxtpXcVBH4AY6W7ssebrei7Kpyz/IfRRsxLQZRSOw=="], + "@emdash-cms/gutenberg-to-portable-text": ["@emdash-cms/gutenberg-to-portable-text@0.28.1", "", { "dependencies": { "@wordpress/block-serialization-default-parser": "^5.13.0", "parse5": "^7.2.1" } }, "sha512-8Fme8aHLl4pWdlsKqoY2WB+5VrsDEPiQ/x4JBDT/nVF4e1abommtg992qAehYXilF+4Y/7gSKOBUbkqiqIPQYw=="], - "@emdash-cms/blocks": ["@emdash-cms/blocks@0.6.0", "", { "dependencies": { "@cloudflare/kumo": "^1.10.0", "@phosphor-icons/react": "^2.1.10", "clsx": "^2.1.1", "echarts": "^6.0.0", "tailwind-merge": "^3.3.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-Sh9Iy180+wexCkoyz12EEQMfdae3U8+sLY/Kgqvi54ZpplSibsWbwQ/ASnI/ONZ10uo4Vxy0zY1Hj/PEJ/09ww=="], + "@emdash-cms/plugin-types": ["@emdash-cms/plugin-types@0.1.0", "", {}, "sha512-aV6bQ/XUK/KBjuG6wVMhtMPWjG4n5VM5qjZY1SVJed9qFWKmzvyMlf00x2Y/nQfp5Hik2Yj2AVeAGf0dtuhvOw=="], - "@emdash-cms/cloudflare": ["@emdash-cms/cloudflare@0.6.0", "", { "dependencies": { "emdash": "0.6.0", "jose": "^6.1.3", "kysely-d1": "^0.4.0", "ulidx": "^2.4.1" }, "peerDependencies": { "@cloudflare/workers-types": ">=4.0.0", "astro": ">=6.0.0-beta.0", "kysely": ">=0.27.0" } }, "sha512-UWIyaQ4pkDZHhakkxIEFqK0JkeBNoIEhExs1hGtXMoqbd2qd4IIvmNxLOUFZZRewc7yLF6TUCla3BppJGzVP8A=="], + "@emdash-cms/registry-client": ["@emdash-cms/registry-client@0.3.2", "", { "dependencies": { "@atcute/atproto": "^4.0.2", "@atcute/client": "^5.0.0", "@atcute/lexicons": "^2.0.0", "@emdash-cms/registry-lexicons": "0.1.1" } }, "sha512-2mjKuMUbFNeLVuRoBwbB4xu1AXpsCWFSJLIJcH0tceddd6MZEAYpkaiQLFpBhrIDYKFM5Gg73Qha7UX9PmQ7PA=="], - "@emdash-cms/gutenberg-to-portable-text": ["@emdash-cms/gutenberg-to-portable-text@0.6.0", "", { "dependencies": { "@wordpress/block-serialization-default-parser": "^5.13.0", "parse5": "^7.2.1" } }, "sha512-8Mp4+0joY59Jd8U5pzyapRiGZaSYPy1z+7JIeJX3t2a6pcP6cJMPfbSHx5ObChP0pDYWR68DcdPnWGrOcUsRNw=="], + "@emdash-cms/registry-lexicons": ["@emdash-cms/registry-lexicons@0.1.1", "", { "dependencies": { "@atcute/atproto": "^4.0.2", "@atcute/lexicons": "^2.0.0" } }, "sha512-0en1SWbqP4lICGafxHhJzxpCbAYB2fk2uPYmsAAi4lILGtR0/fY6HCLZ17ta16zTf12Q7H0VhuA0zEl9Gc93Sw=="], "@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="], @@ -599,6 +623,8 @@ "@speed-highlight/core": ["@speed-highlight/core@1.2.15", "", {}, "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@stripe/react-stripe-js": ["@stripe/react-stripe-js@3.10.0", "", { "dependencies": { "prop-types": "^15.7.2" }, "peerDependencies": { "@stripe/stripe-js": ">=1.44.1 <8.0.0", "react": ">=16.8.0 <20.0.0", "react-dom": ">=16.8.0 <20.0.0" } }, "sha512-UPqHZwMwDzGSax0ZI7XlxR3tZSpgIiZdk3CiwjbTK978phwR/fFXeAXQcN/h8wTAjR4ZIAzdlI9DbOqJhuJdeg=="], "@stripe/stripe-js": ["@stripe/stripe-js@5.10.0", "", {}, "sha512-PTigkxMdMUP6B5ISS7jMqJAKhgrhZwjprDqR1eATtFfh0OpKVNp110xiH+goeVdrJ29/4LeZJR4FaHHWstsu0A=="], @@ -679,6 +705,14 @@ "@tiptap/extension-strike": ["@tiptap/extension-strike@3.22.4", "", { "peerDependencies": { "@tiptap/core": "3.22.4" } }, "sha512-aRHWQj42HiailXSC9LkKYM3jWMcSeGwOjbqM4PiuxQZmHVDRFmeHkfJItOdn2cSHaO0vuEVK+TvrWUWsBFi3pg=="], + "@tiptap/extension-table": ["@tiptap/extension-table@3.27.3", "", { "peerDependencies": { "@tiptap/core": "3.27.3", "@tiptap/pm": "3.27.3" } }, "sha512-P7iQ0SkkDWZwnXzQDOVFLXktXlX67VQ3s2YxoJYKTHlc8rPZ4UCaSogFq0G2APyBlizlY3LbMNrDeZAVFOD8wQ=="], + + "@tiptap/extension-table-cell": ["@tiptap/extension-table-cell@3.27.3", "", { "peerDependencies": { "@tiptap/extension-table": "3.27.3" } }, "sha512-W8onBLgjge3D2PlYetbPC/BvwH5jgYfL9PLqMurfCPD7t8fyU4s+4E3SwbZkuf6eCH0twBjqyapH7+sOW/sPhA=="], + + "@tiptap/extension-table-header": ["@tiptap/extension-table-header@3.27.3", "", { "peerDependencies": { "@tiptap/extension-table": "3.27.3" } }, "sha512-8iBOxh0S1Wh5VC1rmVTZVJKanGhJpZd5E5O7Pycf+KKUu+BcWQZ17ewj3+VVSdU76sDCUWIjQE0qJ/EyI1vZeg=="], + + "@tiptap/extension-table-row": ["@tiptap/extension-table-row@3.27.3", "", { "peerDependencies": { "@tiptap/extension-table": "3.27.3" } }, "sha512-AFMVQI+1kRh97++g+NA6MjATpwQPvjar4JUyaGlnSBIt+b5sYLTW3qQS8EzpMbKPqUp7uZOX9EkLgpvepWYSow=="], + "@tiptap/extension-text": ["@tiptap/extension-text@3.22.4", "", { "peerDependencies": { "@tiptap/core": "3.22.4" } }, "sha512-mM69uUW5cSxIhyEpWXi/YcfyupcJMDLCPEfYi62awH0iOP/LRoCv/nHjJq4Hyj/KxRJbe8HKwIUnqaCUf7m5Pg=="], "@tiptap/extension-text-align": ["@tiptap/extension-text-align@3.22.4", "", { "peerDependencies": { "@tiptap/core": "3.22.4" } }, "sha512-W7TnXWSyfDXSatGXp5y/CahE8G4btrQPb0/sy+eG+42FxdzYsqvh1ys3OE9j2XSuTrZ1q/tZA/NLPUkc7vw6Kw=="], @@ -697,7 +731,7 @@ "@tiptap/suggestion": ["@tiptap/suggestion@3.22.4", "", { "peerDependencies": { "@tiptap/core": "3.22.4", "@tiptap/pm": "3.22.4" } }, "sha512-1buvLZemITTeKmPf2wGFWvvhRFKjdQ+JgMqc67xBraOKeDd8wQi1e2XlhCYAtlVMm5f6j+qlLC/MvwuHI2jHeQ=="], - "@tiptap/y-tiptap": ["@tiptap/y-tiptap@3.0.3", "", { "dependencies": { "lib0": "^0.2.100" }, "peerDependencies": { "prosemirror-model": "^1.7.1", "prosemirror-state": "^1.2.3", "prosemirror-view": "^1.9.10", "y-protocols": "^1.0.1", "yjs": "^13.5.38" } }, "sha512-8UvuV4lTisCE9cMTc/X8kRyTn9edUO7Kball0I6wb17VwZSjNDfh/YKtP4O5vcPawEzFHQIvZGq/k1h37kAf0w=="], + "@tiptap/y-tiptap": ["@tiptap/y-tiptap@3.0.6", "", { "dependencies": { "lib0": "^0.2.100" }, "peerDependencies": { "prosemirror-model": "^1.7.1", "prosemirror-state": "^1.2.3", "prosemirror-view": "^1.9.10", "y-protocols": "^1.0.1", "yjs": "^13.5.38" } }, "sha512-kcGeVGKtq/cPGVseNKjtmtcY2WXUAEm1SqS5x0Smubj4nOCRyPiHg6kY4QuuZhmXjTK7hdo8chokkPUKWXPE9Q=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], @@ -973,7 +1007,7 @@ "electron-to-chromium": ["electron-to-chromium@1.5.340", "", {}, "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA=="], - "emdash": ["emdash@0.6.0", "", { "dependencies": { "@emdash-cms/admin": "0.6.0", "@emdash-cms/auth": "0.6.0", "@emdash-cms/gutenberg-to-portable-text": "0.6.0", "@floating-ui/react": "^0.27.16", "@modelcontextprotocol/sdk": "^1.26.0", "@portabletext/toolkit": "^5.0.1", "@tiptap/core": "^3.20.0", "@tiptap/extension-focus": "^3.20.0", "@tiptap/extension-image": "^3.20.0", "@tiptap/extension-link": "^3.20.0", "@tiptap/extension-placeholder": "^3.20.0", "@tiptap/extension-text-align": "^3.20.0", "@tiptap/extension-typography": "^3.20.0", "@tiptap/extension-underline": "^3.20.0", "@tiptap/react": "^3.20.0", "@tiptap/starter-kit": "^3.20.0", "@tiptap/suggestion": "^3.20.0", "@unpic/placeholder": "^0.1.2", "arctic": "^3.7.0", "astro-portabletext": "^0.11.0", "better-sqlite3": "^12.8.0", "blurhash": "^2.0.5", "citty": "^0.1.6", "consola": "^3.4.2", "croner": "^10.0.1", "image-size": "^2.0.2", "jose": "^6.1.3", "jpeg-js": "^0.4.4", "kysely": "^0.27.0", "mime": "^4.1.0", "modern-tar": "^0.7.5", "picocolors": "^1.1.1", "sanitize-html": "^2.17.1", "sax": "^1.4.1", "ulidx": "^2.4.1", "upng-js": "^2.1.0", "zod": "^4.3.5" }, "optionalDependencies": { "@libsql/kysely-libsql": "^0.4.0", "pg": "^8.0.0" }, "peerDependencies": { "@astrojs/react": ">=5.0.0-beta.0", "@tanstack/react-query": ">=5.0.0", "@tanstack/react-router": ">=1.100.0", "astro": ">=6.0.0-beta.0", "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "bin": { "emdash": "dist/cli/index.mjs", "em": "dist/cli/index.mjs" } }, "sha512-lc9AinEPoJ9xD1dCN5TuyKL3PtMLDL1jUTncwfFQNoRlUvq5UGo5djrrWWpqav8iEwVucH+GkgHqVB4Tk13s8A=="], + "emdash": ["emdash@0.28.1", "", { "dependencies": { "@atcute/client": "^5.0.0", "@atcute/lexicons": "^2.0.0", "@atcute/multibase": "^1.2.0", "@emdash-cms/admin": "0.28.1", "@emdash-cms/auth": "0.28.1", "@emdash-cms/gutenberg-to-portable-text": "0.28.1", "@emdash-cms/plugin-types": "0.1.0", "@emdash-cms/registry-client": "0.3.2", "@floating-ui/react": "^0.27.16", "@modelcontextprotocol/sdk": "^1.26.0", "@oslojs/crypto": "^1.0.1", "@oslojs/encoding": "^1.1.0", "@portabletext/toolkit": "^5.0.1", "@tiptap/core": "^3.20.0", "@tiptap/extension-code-block": "^3.20.0", "@tiptap/extension-focus": "^3.20.0", "@tiptap/extension-image": "^3.20.0", "@tiptap/extension-link": "^3.20.0", "@tiptap/extension-placeholder": "^3.20.0", "@tiptap/extension-text-align": "^3.20.0", "@tiptap/extension-typography": "^3.20.0", "@tiptap/extension-underline": "^3.20.0", "@tiptap/react": "^3.20.0", "@tiptap/starter-kit": "^3.20.0", "@tiptap/suggestion": "^3.20.0", "@unpic/placeholder": "^0.1.2", "arctic": "^3.7.0", "astro-portabletext": "^0.11.0", "better-sqlite3": "^12.8.0", "blurhash": "^2.0.5", "citty": "^0.1.6", "consola": "^3.4.2", "croner": "^10.0.1", "image-size": "^2.0.2", "jose": "^6.1.3", "jpeg-js": "^0.4.4", "kysely": "^0.29.0", "mime": "^4.1.0", "modern-tar": "^0.7.5", "picocolors": "^1.1.1", "sanitize-html": "^2.17.1", "sax": "^1.4.1", "ulidx": "^2.4.1", "upng-js": "^2.1.0", "zod": "^4.4.1" }, "optionalDependencies": { "@libsql/kysely-libsql": "^0.4.0", "pg": "^8.0.0" }, "peerDependencies": { "@astrojs/react": ">=5.0.0-beta.0", "@emdash-cms/auth-atproto": ">=0.2.8", "astro": ">=6.0.0-beta.0", "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "optionalPeers": ["@emdash-cms/auth-atproto"], "bin": { "emdash": "./dist/cli/index.mjs", "em": "./dist/cli/index.mjs" } }, "sha512-xpZ1My5Jme/5lH5iT2gMdNAvgeLkbpz109dhVnblhBDLF6mavtcjJGduGJZrkiVVYgI4gvRBO4QO9aw1ihHAYA=="], "emmet": ["emmet@2.4.11", "", { "dependencies": { "@emmetio/abbreviation": "^2.3.3", "@emmetio/css-abbreviation": "^2.1.8" } }, "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ=="], @@ -1007,6 +1041,8 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], @@ -1221,7 +1257,7 @@ "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], - "kysely": ["kysely@0.27.6", "", {}, "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ=="], + "kysely": ["kysely@0.29.3", "", {}, "sha512-VHtBdW6XB/pgoTSqraM3UAa2rYoYdNXqnNPpX+8XXP+cwYbVEFuAp3HyPt1vpNfU9l7Y2kpUrA9QDPsy8uUqOQ=="], "kysely-d1": ["kysely-d1@0.4.0", "", { "peerDependencies": { "kysely": "*" } }, "sha512-wUcVvQNtm30OTfuo7Ad5vYJ1qHqPXOCZc+zWchVKNyuvqY3u8OuGw4gmUx1Ypdx2wRVFLHVQC9I7v0pTmF7Nkw=="], @@ -1603,7 +1639,7 @@ "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + "reselect": ["reselect@5.2.0", "", {}, "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw=="], "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], @@ -1781,6 +1817,8 @@ "unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="], + "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], "unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], @@ -1819,6 +1857,8 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "valibot": ["valibot@1.4.2", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], @@ -1915,7 +1955,7 @@ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], @@ -1927,6 +1967,8 @@ "@astrojs/language-server/@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="], + "@astrojs/sitemap/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], "@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], @@ -1963,10 +2005,6 @@ "@dashcommerce/starter/@stripe/stripe-js": ["@stripe/stripe-js@9.2.0", "", {}, "sha512-YSzLC0t6VS9MDdPTynSMqU8IxrItFUjkDORALFT6sSMR/XZ5Vgm3RDp/Gk7z727MC4A9s4MFVel0gF0c7+kdrg=="], - "@emdash-cms/admin/react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], - - "@emdash-cms/admin/react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], - "@libsql/hrana-client/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], @@ -1977,6 +2015,8 @@ "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="], "@oslojs/webauthn/@oslojs/crypto": ["@oslojs/crypto@1.0.0", "", { "dependencies": { "@oslojs/asn1": "1.0.0", "@oslojs/binary": "1.0.0" } }, "sha512-dVz8TkkgYdr3tlwxHd7SCYGxoN7ynwHLA0nei/Aq9C+ERU0BK+U8+/3soEzBUxUNKYBf42351DyJUZ2REla50w=="], @@ -2013,6 +2053,8 @@ "astro/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "astro/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "astro-portabletext/@portabletext/toolkit": ["@portabletext/toolkit@3.0.3", "", { "dependencies": { "@portabletext/types": "^2.0.15" } }, "sha512-EI59/l4E0E5wCyWUzvW1FPKWfqDsbebCnCf9ZDHjXGH95X4EORlCtnPuA79PpKaHv0LTChORy55trRzW6tYnOQ=="], "astro-portabletext/@portabletext/types": ["@portabletext/types@2.0.15", "", {}, "sha512-2e6i2gSQsrA/5OL5Gm4/9bxB9MNO73Fa47zj+0mT93xkoQUCGCWX5fZh1YBJ86hszaRYlqvqG08oULxvvPPp/Q=="], diff --git a/package.json b/package.json index 3bb0585..43bbf5e 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,6 @@ "bun": ">=1.1.0" }, "patchedDependencies": { - "emdash@0.6.0": "patches/emdash@0.6.0.patch" + "emdash@0.28.1": "patches/emdash@0.28.1.patch" } } diff --git a/packages/core/package.json b/packages/core/package.json index 9881023..75c3e73 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -65,7 +65,7 @@ "test": "bun test" }, "peerDependencies": { - "emdash": ">=0.5.0 <0.7.0", + "emdash": ">=0.28.0 <0.29.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, @@ -78,12 +78,12 @@ } }, "devDependencies": { - "@emdash-cms/admin": "^0.6.0", + "@emdash-cms/admin": "^0.28.1", "@stripe/react-stripe-js": "^3.0.0", "@stripe/stripe-js": "^5.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "emdash": "^0.6.0", + "emdash": "^0.28.1", "react": "^19.0.0", "react-dom": "^19.0.0", "tsdown": "^0.20.0", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2008862..66b7bed 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -51,11 +51,11 @@ export function dashcommerce(options: DashCommerceOptions = {}): PluginDescripto format: "native", entrypoint: "@dashcommerce/core/sandbox", capabilities: [ - "read:content", - "write:content", - "read:media", - "read:users", - "network:fetch", + "content:read", + "content:write", + "media:read", + "users:read", + "network:request", "email:send", ], allowedHosts: ["api.stripe.com", "files.stripe.com"], @@ -159,11 +159,11 @@ export function dashcommerce(options: DashCommerceOptions = {}): PluginDescripto id, version: DASHCOMMERCE_VERSION, capabilities: [ - "read:content", - "write:content", - "read:media", - "read:users", - "network:fetch", + "content:read", + "content:write", + "media:read", + "users:read", + "network:request", "email:send", ], allowedHosts: ["api.stripe.com", "files.stripe.com"], diff --git a/packages/core/src/sandbox-entry.ts b/packages/core/src/sandbox-entry.ts index c9bb90d..411d617 100644 --- a/packages/core/src/sandbox-entry.ts +++ b/packages/core/src/sandbox-entry.ts @@ -10,13 +10,33 @@ * - All crypto via `crypto.subtle` (Web Crypto). * * Emdash's astro integration calls `createPlugin(options)` at build time - * for native-format plugins. `options` comes from the descriptor's - * `options` field (see src/index.ts). We feed them into - * `adaptSandboxEntry` to produce a `ResolvedPlugin` — the same shape - * standard-format plugins get for free. + * for native-format plugins (`format: "native"` + `entrypoint` in the + * descriptor — see src/index.ts). `options` comes from the descriptor's + * `options` field. We build a native `ResolvedPlugin` with `definePlugin`. + * + * emdash 0.28 note: route handlers are the NATIVE single-arg shape + * `(ctx: RouteContext) => Promise`, where `RouteContext` extends + * `PluginContext` — so `ctx` carries both the request (`ctx.request`, + * `ctx.input`) and the plugin surface (`ctx.storage`, `ctx.kv`, `ctx.http`…). + * DashCommerce's route handlers are authored two-arg `(routeCtx, ctx)`; we + * adapt them to the single-arg form below (passing the one context as both). + * We deliberately do NOT use `adaptSandboxEntry` here: it flattens + * `routeCtx.request` to `{ url, method, headers }` with no body, which would + * break the Stripe webhook's raw-body signature verification. */ -import { adaptSandboxEntry, definePlugin, type PluginDescriptor } from "emdash"; +import { + definePlugin, + type FieldWidgetConfig, + type PluginAdminConfig, + type PluginAdminPage, + type PluginCapability, + type PluginContext, + type PluginDescriptor, + type PluginRoute, + type PortableTextBlockConfig, + type RouteContext, +} from "emdash"; import { productBeforeSave } from "./hooks/content"; import { cronHandler } from "./hooks/cron"; @@ -33,59 +53,96 @@ import { subscriptionsPublicRoutes } from "./routes/subscriptions-public"; import { webhookRoutes } from "./routes/webhook"; import { DASHCOMMERCE_STORAGE } from "./storage-collections"; -const DEFAULT_CAPABILITIES = [ - "read:content", - "write:content", - "read:media", - "read:users", - "network:fetch", +const DEFAULT_CAPABILITIES: PluginCapability[] = [ + "content:read", + "content:write", + "media:read", + "users:read", + "network:request", "email:send", ]; const DEFAULT_ALLOWED_HOSTS = ["api.stripe.com", "files.stripe.com"]; -const definition = definePlugin({ - hooks: { - "content:beforeSave": { - handler: productBeforeSave, - }, - cron: { - handler: cronHandler, - }, - "plugin:install": { - handler: onInstall, - }, - "plugin:activate": { - handler: onActivate, - }, - }, - routes: { - ...cartRoutes, - ...checkoutRoutes, - ...configCheckRoutes, - ...customerPortalRoutes, - ...downloadsRoutes, - ...ordersPublicRoutes, - ...reviewsPublicRoutes, - ...subscriptionsPublicRoutes, - ...webhookRoutes, - ...adminApiRoutes, - }, -}); +/** + * DashCommerce's route handlers are authored in the two-arg convention + * `(routeCtx, ctx)` — `routeCtx` for request data (`.request`, `.input`) and + * `ctx` for the plugin surface. In emdash's native format both are the same + * `RouteContext` (which extends `PluginContext`), so a single context serves + * as both. This is the loose entry shape those route maps satisfy. + */ +type CommerceRouteEntry = { + public?: boolean; + input?: PluginRoute["input"]; + handler: (routeCtx: RouteContext, ctx: PluginContext) => Promise; +}; + +const HOOKS = { + "content:beforeSave": { handler: productBeforeSave }, + cron: { handler: cronHandler }, + "plugin:install": { handler: onInstall }, + "plugin:activate": { handler: onActivate }, +}; + +const ROUTES = { + ...cartRoutes, + ...checkoutRoutes, + ...configCheckRoutes, + ...customerPortalRoutes, + ...downloadsRoutes, + ...ordersPublicRoutes, + ...reviewsPublicRoutes, + ...subscriptionsPublicRoutes, + ...webhookRoutes, + ...adminApiRoutes, +} as unknown as Record; + +/** + * Adapt the authored two-arg route handlers into emdash's native single-arg + * `PluginRoute` form. `RouteContext` extends `PluginContext`, so the one + * `ctx` is passed as both arguments. `public` and `input` pass through. + */ +function toNativeRoutes( + routes: Record, +): Record { + const out: Record = {}; + for (const [name, route] of Object.entries(routes)) { + out[name] = { + public: route.public, + input: route.input, + handler: (ctx) => route.handler(ctx, ctx), + }; + } + return out; +} + +/** + * emdash's `PluginDefinition.storage` requires an `indexes` array on every + * collection; the descriptor's `storage` declaration leaves it optional. + * Normalize to the runtime shape. + */ +function toStorageConfig( + decl: NonNullable, +): Record { + const out: Record = {}; + for (const [name, cfg] of Object.entries(decl)) { + out[name] = { indexes: cfg.indexes ?? [], uniqueIndexes: cfg.uniqueIndexes }; + } + return out; +} export interface CreatePluginOptions { id?: string; version?: string; - capabilities?: string[]; + capabilities?: PluginCapability[]; allowedHosts?: string[]; } /** - * Static admin page list — must match src/index.ts adminPages. - * Defined here so `adaptSandboxEntry` can populate `admin.pages`, - * which the emdash plugin-manager API reads to set `hasAdminPages` - * and show the Settings gear link on the Plugins page. + * Static admin page list — must match src/index.ts adminPages. Populates + * `admin.pages`, which the emdash plugin-manager API reads to set + * `hasAdminPages` and show the Settings gear link on the Plugins page. */ -const ADMIN_PAGES: PluginDescriptor["adminPages"] = [ +const ADMIN_PAGES: PluginAdminPage[] = [ { path: "/orders", label: "Orders", icon: "shopping-bag" }, { path: "/customers", label: "Customers", icon: "users" }, { path: "/coupons", label: "Coupons", icon: "tag" }, @@ -99,7 +156,7 @@ const ADMIN_PAGES: PluginDescriptor["adminPages"] = [ { path: "/settings", label: "Settings", icon: "settings" }, ]; -const ADMIN_WIDGETS: PluginDescriptor["adminWidgets"] = [ +const ADMIN_WIDGETS: NonNullable = [ { id: "revenue-snapshot", title: "Revenue", size: "half" }, { id: "low-stock-alerts", title: "Low Stock", size: "half" }, { id: "recent-orders", title: "Recent Orders", size: "full" }, @@ -110,13 +167,7 @@ const ADMIN_WIDGETS: PluginDescriptor["adminWidgets"] = [ // Custom content-field widgets this plugin provides. The content editor // resolves `widget: "dashcommerce:"` on a field to the React // component exported from `admin/entry.tsx` under `fields[name]`. -// -// `adaptSandboxEntry` currently only forwards `adminPages`/`adminWidgets` -// from the descriptor, so we merge these onto `resolved.admin` ourselves -// below. Without that merge the admin manifest wouldn't know the widgets -// exist and the editor would fall back to the default JSON textarea — -// exactly the "prices field is still a text input" symptom. -const FIELD_WIDGETS = [ +const FIELD_WIDGETS: FieldWidgetConfig[] = [ { name: "vendor-select", label: "Vendor picker", @@ -129,7 +180,7 @@ const FIELD_WIDGETS = [ }, ]; -const PORTABLE_TEXT_BLOCKS = [ +const PORTABLE_TEXT_BLOCKS: PortableTextBlockConfig[] = [ { type: "product-embed", label: "Embed Product", @@ -151,40 +202,30 @@ const PORTABLE_TEXT_BLOCKS = [ ]; /** - * Native-format entry called by emdash at build time with the options - * serialized from the descriptor. Returns a ResolvedPlugin ready for the + * Native-format entry called by emdash with the options serialized from the + * descriptor (see src/index.ts). Returns a `ResolvedPlugin` ready for the * HookPipeline. */ export function createPlugin(options: CreatePluginOptions = {}) { - // adaptSandboxEntry reads id/version/capabilities/allowedHosts/ - // storage/adminPages/adminWidgets from the descriptor to build the - // ResolvedPlugin. adminPages must be present so the emdash plugin-manager - // API returns hasAdminPages:true and the Settings link appears. - const descriptor: PluginDescriptor = { + return definePlugin({ id: options.id ?? "dashcommerce", version: options.version ?? "0.0.0", - entrypoint: "@dashcommerce/core/sandbox", capabilities: options.capabilities ?? DEFAULT_CAPABILITIES, allowedHosts: options.allowedHosts ?? DEFAULT_ALLOWED_HOSTS, - storage: DASHCOMMERCE_STORAGE, - adminPages: ADMIN_PAGES, - adminWidgets: ADMIN_WIDGETS, - }; - const resolved = adaptSandboxEntry(definition, descriptor); - // Merge fieldWidgets / portableTextBlocks onto the resolved admin - // config. adaptSandboxEntry drops these today; without the merge the - // admin manifest would never learn about `dashcommerce:price-map` - // and the content editor would render the raw JSON textarea. - // biome-ignore lint/suspicious/noExplicitAny: widening PluginAdminConfig to attach fields emdash's type doesn't yet declare - const admin: any = (resolved as unknown as { admin?: unknown }).admin ?? {}; - admin.fieldWidgets = FIELD_WIDGETS; - admin.portableTextBlocks = PORTABLE_TEXT_BLOCKS; - (resolved as unknown as { admin: unknown }).admin = admin; - return resolved; + storage: toStorageConfig(DASHCOMMERCE_STORAGE), + hooks: HOOKS, + routes: toNativeRoutes(ROUTES), + admin: { + pages: ADMIN_PAGES, + widgets: ADMIN_WIDGETS, + fieldWidgets: FIELD_WIDGETS, + portableTextBlocks: PORTABLE_TEXT_BLOCKS, + }, + }); } /** - * Default export kept for direct-import consumers + tests that read the - * raw { hooks, routes } definition without going through adaptSandboxEntry. + * Default export kept for direct-import consumers + tests that read the raw + * `{ hooks, routes }` definition without going through `createPlugin`. */ -export default definition; +export default { hooks: HOOKS, routes: ROUTES }; diff --git a/packages/starter/emdash-env.d.ts b/packages/starter/emdash-env.d.ts index 0ffc660..183a75f 100644 --- a/packages/starter/emdash-env.d.ts +++ b/packages/starter/emdash-env.d.ts @@ -3,7 +3,7 @@ /// -import type { ContentBylineCredit, PortableTextBlock } from "emdash"; +import type { ContentBylineCredit, TaxonomyTerm, PortableTextBlock } from "emdash"; export interface Announcement { id: string; @@ -18,6 +18,7 @@ export interface Announcement { updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface Banner { @@ -36,15 +37,16 @@ export interface Banner { alignment?: string; bg_type?: string; bg_css?: string; - bg_image?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + bg_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; bg_focal_point?: string; bg_video_url?: string; - bg_video_poster?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + bg_video_poster?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; bg_overlay?: string; createdAt: Date; updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface CategoryTile { @@ -56,12 +58,13 @@ export interface CategoryTile { href: string; size?: string; bg_css?: string; - image?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; sort_order?: number; createdAt: Date; updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface HeroSlide { @@ -75,10 +78,10 @@ export interface HeroSlide { cta_href?: string; bg_type?: string; bg_css?: string; - bg_image?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + bg_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; bg_focal_point?: string; bg_video_url?: string; - bg_video_poster?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + bg_video_poster?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; bg_overlay?: string; accent_css?: string; sort_order?: number; @@ -86,6 +89,7 @@ export interface HeroSlide { updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface Post { @@ -94,7 +98,7 @@ export interface Post { status: string; title: string; excerpt?: string; - cover_image?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + cover_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; cover_gradient?: string; author_name?: string; published_date?: string; @@ -104,6 +108,7 @@ export interface Post { updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface Product { @@ -111,11 +116,11 @@ export interface Product { slug: string | null; status: string; title: string; - featured_image?: { id: string; src?: string; alt?: string; width?: number; height?: number }; - gallery_2?: { id: string; src?: string; alt?: string; width?: number; height?: number }; - gallery_3?: { id: string; src?: string; alt?: string; width?: number; height?: number }; - gallery_4?: { id: string; src?: string; alt?: string; width?: number; height?: number }; - gallery_5?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + featured_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; + gallery_2?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; + gallery_3?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; + gallery_4?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; + gallery_5?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; description?: PortableTextBlock[]; type: string; sku: string; @@ -141,6 +146,7 @@ export interface Product { updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface SectionHeader { @@ -158,15 +164,16 @@ export interface SectionHeader { alignment?: string; bg_type?: string; bg_css?: string; - bg_image?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + bg_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; bg_focal_point?: string; bg_video_url?: string; - bg_video_poster?: { id: string; src?: string; alt?: string; width?: number; height?: number }; + bg_video_poster?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record }; bg_overlay?: string; createdAt: Date; updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface SiteText { @@ -193,6 +200,7 @@ export interface SiteText { updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } export interface TrustItem { @@ -207,6 +215,7 @@ export interface TrustItem { updatedAt: Date; publishedAt: Date | null; bylines?: ContentBylineCredit[]; + terms?: Record; } declare module "emdash" { diff --git a/packages/starter/package.json b/packages/starter/package.json index c500b39..46c038e 100644 --- a/packages/starter/package.json +++ b/packages/starter/package.json @@ -31,11 +31,11 @@ "@astrojs/react": "^5.0.0", "@astrojs/sitemap": "^3.4.0", "@dashcommerce/core": "workspace:*", - "@emdash-cms/cloudflare": "^0.6.0", + "@emdash-cms/cloudflare": "^0.28.1", "@stripe/react-stripe-js": "^6.0.0", "@stripe/stripe-js": "^9.0.0", "astro": "^6.0.0", - "emdash": "^0.6.0", + "emdash": "^0.28.1", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/packages/starter/src/worker.ts b/packages/starter/src/worker.ts new file mode 100644 index 0000000..99beb58 --- /dev/null +++ b/packages/starter/src/worker.ts @@ -0,0 +1,15 @@ +// Cloudflare Worker entry. +// +// Astro's @astrojs/cloudflare adapter generates a fetch-only Worker. EmDash's +// background work — plugin cron (DashCommerce's abandoned-cart recovery, dunning +// retries, and stock-lock sweeps), scheduled publishing, and system cleanup — is +// driven by a `scheduled()` handler, NOT by request side effects (emdash 0.19+). +// +// `@emdash-cms/cloudflare/worker` wraps the adapter's server handler with that +// `scheduled()` handler. The Cron Trigger in wrangler.jsonc (`triggers.crons`) +// invokes it once a minute. Without this entry + trigger, DashCommerce's cron +// jobs silently never run on Cloudflare. +// +// `PluginBridge` is the sandbox Durable Object, re-exported so its binding +// resolves if sandboxed plugins are ever added (none are wired by default). +export { default, PluginBridge } from "@emdash-cms/cloudflare/worker"; diff --git a/packages/starter/wrangler.jsonc b/packages/starter/wrangler.jsonc index 2919828..7779826 100644 --- a/packages/starter/wrangler.jsonc +++ b/packages/starter/wrangler.jsonc @@ -1,18 +1,26 @@ { "$schema": "node_modules/wrangler/config-schema.json", "name": "dashcommerce-demo", + // Custom Worker entry: Astro's fetch handler + EmDash's scheduled() handler, + // which the Cron Trigger below drives (plugin cron, scheduled publishing, + // system cleanup). See src/worker.ts. The @astrojs/cloudflare adapter merges + // its `assets` binding into the generated `dist/server/wrangler.json` at + // build time; `bun run build:cf` emits that file and we deploy against it. + "main": "./src/worker.ts", "compatibility_date": "2026-04-01", "compatibility_flags": [ "nodejs_compat" ], - // `main` and `assets` are intentionally omitted here. @astrojs/cloudflare - // v13+ emits a fully-resolved `dist/server/wrangler.json` at build time - // that merges these fields with the bindings + vars below. Always deploy - // against that generated file: - // - // wrangler deploy --config dist/server/wrangler.json - // - // Use `bun run cf:deploy` which handles this automatically. + // Cron Trigger — drives DashCommerce's abandoned-cart recovery, subscription + // dunning, and stock-lock sweeps (plus emdash scheduled publishing). Without + // it these background jobs never run on Cloudflare (emdash 0.19+). + "triggers": { + "crons": [ + "* * * * *" + ] + }, + // Deploy against the adapter-generated `dist/server/wrangler.json`, which + // merges `assets` + the bindings/vars below. Use `bun run cf:deploy`. "vars": { "SITE_URL": "https://demo.dashcommerce.dev" }, diff --git a/patches/emdash@0.28.1.patch b/patches/emdash@0.28.1.patch new file mode 100644 index 0000000..fa71f37 --- /dev/null +++ b/patches/emdash@0.28.1.patch @@ -0,0 +1,49 @@ +diff --git a/dist/astro/middleware.mjs b/dist/astro/middleware.mjs +index 0159c9ed9353975db214e657959839be113351dc..83430cb0cd9217164c03f9a4fcb4f5110c3cf2cb 100644 +--- a/dist/astro/middleware.mjs ++++ b/dist/astro/middleware.mjs +@@ -2029,7 +2029,7 @@ var EmDashRuntime = class EmDashRuntime { + const routeKey = path.replace(LEADING_SLASH_PATTERN, ""); + let body = void 0; + try { +- body = await request.json(); ++ body = await request.clone().json(); + } catch {} + return routeRegistry.invoke(pluginId, routeKey, { + request, +@@ -2211,7 +2211,7 @@ var EmDashRuntime = class EmDashRuntime { + const routeName = path.replace(LEADING_SLASH_PATTERN, ""); + let body = void 0; + try { +- body = await request.json(); ++ body = await request.clone().json(); + } catch {} + try { + const headers = sanitizeHeadersForSandbox(request.headers); +diff --git a/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs b/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +index 2bacc5aa46a090e3336e98ca7406bbdee5d2d889..bc989f6b3db6b02a59bc48d49620e54b2ef3d013 100644 +--- a/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs ++++ b/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +@@ -57,6 +57,9 @@ const handleRequest = async ({ params, request, locals }) => { + const code = result.error?.code ?? "PLUGIN_ERROR"; + return apiError(code, code === "INTERNAL_ERROR" ? "Plugin route error" : result.error?.message ?? "Plugin route error", result.status ?? (code === "NOT_FOUND" ? 404 : 400)); + } ++ // DashCommerce patch: let plugin route handlers return a raw Response ++ // (cookies, redirects, webhook 200s). apiSuccess would serialize it to {}. ++ if (result.data instanceof Response) return result.data; + return apiSuccess(result.data); + }; + const GET = handleRequest; +diff --git a/dist/menus-b2T4clbO.mjs b/dist/menus-b2T4clbO.mjs +index 4ca88fd2241381099f7b9d492b7cffd03ccbabf9..8d0673262ec217c8cebcc51ed55f501302621ff1 100644 +--- a/dist/menus-b2T4clbO.mjs ++++ b/dist/menus-b2T4clbO.mjs +@@ -1998,7 +1998,7 @@ var PluginRouteHandler = class { + const routeContext = { + ...this.contextFactory.createContext(this.plugin), + input: validatedInput, +- request: guardConsumedRequestBody(options.request), ++ request: options.request, + requestMeta: extractRequestMeta(options.request, this.trustedProxyHeaders) + }; + try { diff --git a/patches/emdash@0.6.0.patch b/patches/emdash@0.6.0.patch deleted file mode 100644 index 532f2fc..0000000 --- a/patches/emdash@0.6.0.patch +++ /dev/null @@ -1,62 +0,0 @@ -diff --git a/dist/astro/middleware.mjs b/dist/astro/middleware.mjs -index 466f69f8eb08c1f779ebc0f804b73bd0637536b3..d83a6106d7ed6bc50a0318c4fae40fd9dd116848 100644 ---- a/dist/astro/middleware.mjs -+++ b/dist/astro/middleware.mjs -@@ -1401,7 +1401,7 @@ var EmDashRuntime = class EmDashRuntime { - const routeKey = path.replace(LEADING_SLASH_PATTERN, ""); - let body = void 0; - try { -- body = await request.json(); -+ body = await request.clone().json(); - } catch {} - return routeRegistry.invoke(pluginId, routeKey, { - request, -@@ -1534,7 +1534,7 @@ var EmDashRuntime = class EmDashRuntime { - const routeName = path.replace(LEADING_SLASH_PATTERN, ""); - let body = void 0; - try { -- body = await request.json(); -+ body = await request.clone().json(); - } catch {} - try { - const headers = sanitizeHeadersForSandbox(request.headers); -diff --git a/src/astro/routes/api/plugins/[pluginId]/[...path].ts b/src/astro/routes/api/plugins/[pluginId]/[...path].ts -index b295a642565f495ae12215c3c7be7494bc4ba43b..285539036ba344c43726b12fdf8f757c5247558d 100644 ---- a/src/astro/routes/api/plugins/[pluginId]/[...path].ts -+++ b/src/astro/routes/api/plugins/[pluginId]/[...path].ts -@@ -81,6 +81,13 @@ const handleRequest: APIRoute = async ({ params, request, locals }) => { - return apiError(code, message, status); - } - -+ // Handlers may return a raw Response (status, headers, JSON body). The -+ // default success path uses apiSuccess(), which would serialize a Response -+ // as {} and drop Set-Cookie. Pass through so plugin routes control the wire. -+ if (result.data instanceof Response) { -+ return result.data; -+ } -+ - return apiSuccess(result.data); - }; - -diff --git a/src/emdash-runtime.ts b/src/emdash-runtime.ts -index dd9192aa624b9da20d36b03e2854547b155ce6a7..f19f37483060b1b8f6295c4f7484638df3f1e96f 100644 ---- a/src/emdash-runtime.ts -+++ b/src/emdash-runtime.ts -@@ -2087,7 +2087,7 @@ export class EmDashRuntime { - - let body: unknown = undefined; - try { -- body = await request.json(); -+ body = await request.clone().json(); - } catch { - // No body or not JSON - } -@@ -2314,7 +2314,7 @@ export class EmDashRuntime { - - let body: unknown = undefined; - try { -- body = await request.json(); -+ body = await request.clone().json(); - } catch { - // No body or not JSON - }