d1s 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/.next/.next/BUILD_ID +1 -0
- package/dist/.next/.next/app-build-manifest.json +109 -0
- package/dist/.next/.next/app-path-routes-manifest.json +1 -0
- package/dist/.next/.next/build-manifest.json +33 -0
- package/dist/.next/.next/cache/.rscinfo +1 -0
- package/dist/.next/.next/cache/.tsbuildinfo +1 -0
- package/dist/.next/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/2.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/dist/.next/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/dist/.next/.next/diagnostics/build-diagnostics.json +6 -0
- package/dist/.next/.next/diagnostics/framework.json +1 -0
- package/dist/.next/.next/export-marker.json +1 -0
- package/dist/.next/.next/images-manifest.json +1 -0
- package/dist/.next/.next/next-minimal-server.js.nft.json +1 -0
- package/dist/.next/.next/next-server.js.nft.json +1 -0
- package/dist/.next/.next/package.json +1 -0
- package/dist/.next/.next/prerender-manifest.json +1 -0
- package/dist/.next/.next/react-loadable-manifest.json +1 -0
- package/dist/.next/.next/required-server-files.json +1 -0
- package/dist/.next/.next/routes-manifest.json +1 -0
- package/dist/.next/.next/server/app/_not-found/page.js +1 -0
- package/dist/.next/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/dist/.next/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/_not-found.html +1 -0
- package/dist/.next/.next/server/app/_not-found.meta +8 -0
- package/dist/.next/.next/server/app/_not-found.rsc +16 -0
- package/dist/.next/.next/server/app/api/cloudflare/route.js +1 -0
- package/dist/.next/.next/server/app/api/cloudflare/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/databases/route.js +1 -0
- package/dist/.next/.next/server/app/api/databases/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/databases/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync/route.js +12 -0
- package/dist/.next/.next/server/app/api/migrations/sync/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync-files/route.js +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/preview/route.js +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/push-remote/confirm/route.js +14 -0
- package/dist/.next/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/push-remote/preview/route.js +14 -0
- package/dist/.next/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/query/route.js +8 -0
- package/dist/.next/.next/server/app/api/query/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/query/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/tables/[name]/route.js +5 -0
- package/dist/.next/.next/server/app/api/tables/[name]/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/api/tables/route.js +7 -0
- package/dist/.next/.next/server/app/api/tables/route.js.nft.json +1 -0
- package/dist/.next/.next/server/app/api/tables/route_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app/index.html +3 -0
- package/dist/.next/.next/server/app/index.meta +7 -0
- package/dist/.next/.next/server/app/index.rsc +20 -0
- package/dist/.next/.next/server/app/page.js +58 -0
- package/dist/.next/.next/server/app/page.js.nft.json +1 -0
- package/dist/.next/.next/server/app/page_client-reference-manifest.js +1 -0
- package/dist/.next/.next/server/app-paths-manifest.json +15 -0
- package/dist/.next/.next/server/chunks/336.js +1 -0
- package/dist/.next/.next/server/chunks/627.js +6 -0
- package/dist/.next/.next/server/chunks/638.js +13 -0
- package/dist/.next/.next/server/chunks/801.js +55 -0
- package/dist/.next/.next/server/chunks/973.js +2 -0
- package/dist/.next/.next/server/chunks/992.js +3 -0
- package/dist/.next/.next/server/functions-config-manifest.json +1 -0
- package/dist/.next/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/dist/.next/.next/server/middleware-build-manifest.js +1 -0
- package/dist/.next/.next/server/middleware-manifest.json +6 -0
- package/dist/.next/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/dist/.next/.next/server/next-font-manifest.js +1 -0
- package/dist/.next/.next/server/next-font-manifest.json +1 -0
- package/dist/.next/.next/server/pages/404.html +1 -0
- package/dist/.next/.next/server/pages/500.html +1 -0
- package/dist/.next/.next/server/pages/_app.js +1 -0
- package/dist/.next/.next/server/pages/_app.js.nft.json +1 -0
- package/dist/.next/.next/server/pages/_document.js +1 -0
- package/dist/.next/.next/server/pages/_document.js.nft.json +1 -0
- package/dist/.next/.next/server/pages/_error.js +1 -0
- package/dist/.next/.next/server/pages/_error.js.nft.json +1 -0
- package/dist/.next/.next/server/pages-manifest.json +1 -0
- package/dist/.next/.next/server/server-reference-manifest.js +1 -0
- package/dist/.next/.next/server/server-reference-manifest.json +1 -0
- package/dist/.next/.next/server/webpack-runtime.js +1 -0
- package/dist/.next/.next/static/ODMSSwVqLoDcBgbFHUjr8/_buildManifest.js +1 -0
- package/dist/.next/.next/static/ODMSSwVqLoDcBgbFHUjr8/_ssgManifest.js +1 -0
- package/dist/.next/.next/static/chunks/422-6fa3c14fc64ad67b.js +1 -0
- package/dist/.next/.next/static/chunks/4bd1b696-8c908eb707f85a27.js +1 -0
- package/dist/.next/.next/static/chunks/517-3b698a5d3da06ff7.js +2 -0
- package/dist/.next/.next/static/chunks/692-ede418e1d28a1394.js +1 -0
- package/dist/.next/.next/static/chunks/735-c9144179c6bd7d06.js +1 -0
- package/dist/.next/.next/static/chunks/92-c4466e0f1ca8a3a8.js +1 -0
- package/dist/.next/.next/static/chunks/app/_not-found/page-b786c3dd79ad15a5.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/cloudflare/route-3458d78022185d7f.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/databases/route-64255424877a9f72.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/migrations/sync/route-03766b3afcf043ba.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/migrations/sync-files/route-02fa291c8000dadf.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/pull-remote/confirm/route-7e05bb8920127abe.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/pull-remote/preview/route-0fa15f783e8a306c.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/push-remote/confirm/route-96691f1880e4ed14.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/push-remote/preview/route-a798679c67374552.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/query/route-e90d0030a5083e20.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/tables/[name]/route-1efe63e73b480e58.js +1 -0
- package/dist/.next/.next/static/chunks/app/api/tables/route-3772b4f480c4b4a4.js +1 -0
- package/dist/.next/.next/static/chunks/app/layout-8360fab69b848283.js +1 -0
- package/dist/.next/.next/static/chunks/app/page-75081ac7a88cb706.js +1 -0
- package/dist/.next/.next/static/chunks/framework-d29117d969504448.js +1 -0
- package/dist/.next/.next/static/chunks/main-8d0ab17774ffa031.js +1 -0
- package/dist/.next/.next/static/chunks/main-app-d36cdfb402b40f13.js +1 -0
- package/dist/.next/.next/static/chunks/pages/_app-d23763e3e6c904ff.js +1 -0
- package/dist/.next/.next/static/chunks/pages/_error-9b7125ad1a1e68fa.js +1 -0
- package/dist/.next/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/.next/.next/static/chunks/webpack-c76ac1e586b2802a.js +1 -0
- package/dist/.next/.next/static/css/c51a7ef325e30c16.css +3 -0
- package/dist/.next/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
- package/dist/.next/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/dist/.next/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/dist/.next/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- package/dist/.next/.next/trace +3 -0
- package/dist/.next/.next/types/app/api/cloudflare/route.ts +347 -0
- package/dist/.next/.next/types/app/api/databases/route.ts +347 -0
- package/dist/.next/.next/types/app/api/migrations/sync/route.ts +347 -0
- package/dist/.next/.next/types/app/api/migrations/sync-files/route.ts +347 -0
- package/dist/.next/.next/types/app/api/pull-remote/confirm/route.ts +347 -0
- package/dist/.next/.next/types/app/api/pull-remote/preview/route.ts +347 -0
- package/dist/.next/.next/types/app/api/push-remote/confirm/route.ts +347 -0
- package/dist/.next/.next/types/app/api/push-remote/preview/route.ts +347 -0
- package/dist/.next/.next/types/app/api/query/route.ts +347 -0
- package/dist/.next/.next/types/app/api/tables/[name]/route.ts +347 -0
- package/dist/.next/.next/types/app/api/tables/route.ts +347 -0
- package/dist/.next/.next/types/app/page.ts +84 -0
- package/dist/.next/.next/types/cache-life.d.ts +141 -0
- package/dist/.next/.next/types/package.json +1 -0
- package/dist/.next/BUILD_ID +1 -0
- package/dist/.next/app-build-manifest.json +109 -0
- package/dist/.next/app-path-routes-manifest.json +1 -0
- package/dist/.next/build-manifest.json +33 -0
- package/dist/.next/cache/.rscinfo +1 -0
- package/dist/.next/cache/.tsbuildinfo +1 -0
- package/dist/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/dist/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/diagnostics/build-diagnostics.json +6 -0
- package/dist/.next/diagnostics/framework.json +1 -0
- package/dist/.next/export-marker.json +1 -0
- package/dist/.next/images-manifest.json +1 -0
- package/dist/.next/next-minimal-server.js.nft.json +1 -0
- package/dist/.next/next-server.js.nft.json +1 -0
- package/dist/.next/package.json +1 -0
- package/dist/.next/prerender-manifest.json +1 -0
- package/dist/.next/react-loadable-manifest.json +1 -0
- package/dist/.next/required-server-files.json +1 -0
- package/dist/.next/routes-manifest.json +1 -0
- package/dist/.next/server/app/_not-found/page.js +1 -0
- package/dist/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/_not-found.html +1 -0
- package/dist/.next/server/app/_not-found.meta +8 -0
- package/dist/.next/server/app/_not-found.rsc +16 -0
- package/dist/.next/server/app/api/cloudflare/route.js +1 -0
- package/dist/.next/server/app/api/cloudflare/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/databases/route.js +1 -0
- package/dist/.next/server/app/api/databases/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/databases/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/migrations/sync/route.js +12 -0
- package/dist/.next/server/app/api/migrations/sync/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/migrations/sync-files/route.js +1 -0
- package/dist/.next/server/app/api/migrations/sync-files/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/pull-remote/confirm/route.js +1 -0
- package/dist/.next/server/app/api/pull-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/pull-remote/preview/route.js +1 -0
- package/dist/.next/server/app/api/pull-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/push-remote/confirm/route.js +14 -0
- package/dist/.next/server/app/api/push-remote/confirm/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/push-remote/preview/route.js +14 -0
- package/dist/.next/server/app/api/push-remote/preview/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/query/route.js +8 -0
- package/dist/.next/server/app/api/query/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/query/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/tables/[name]/route.js +5 -0
- package/dist/.next/server/app/api/tables/[name]/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/api/tables/route.js +7 -0
- package/dist/.next/server/app/api/tables/route.js.nft.json +1 -0
- package/dist/.next/server/app/api/tables/route_client-reference-manifest.js +1 -0
- package/dist/.next/server/app/index.html +3 -0
- package/dist/.next/server/app/index.meta +7 -0
- package/dist/.next/server/app/index.rsc +20 -0
- package/dist/.next/server/app/page.js +58 -0
- package/dist/.next/server/app/page.js.nft.json +1 -0
- package/dist/.next/server/app/page_client-reference-manifest.js +1 -0
- package/dist/.next/server/app-paths-manifest.json +15 -0
- package/dist/.next/server/chunks/336.js +1 -0
- package/dist/.next/server/chunks/627.js +6 -0
- package/dist/.next/server/chunks/638.js +13 -0
- package/dist/.next/server/chunks/801.js +55 -0
- package/dist/.next/server/chunks/973.js +2 -0
- package/dist/.next/server/chunks/992.js +3 -0
- package/dist/.next/server/functions-config-manifest.json +1 -0
- package/dist/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/dist/.next/server/middleware-build-manifest.js +1 -0
- package/dist/.next/server/middleware-manifest.json +6 -0
- package/dist/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/dist/.next/server/next-font-manifest.js +1 -0
- package/dist/.next/server/next-font-manifest.json +1 -0
- package/dist/.next/server/pages/404.html +1 -0
- package/dist/.next/server/pages/500.html +1 -0
- package/dist/.next/server/pages/_app.js +1 -0
- package/dist/.next/server/pages/_app.js.nft.json +1 -0
- package/dist/.next/server/pages/_document.js +1 -0
- package/dist/.next/server/pages/_document.js.nft.json +1 -0
- package/dist/.next/server/pages/_error.js +1 -0
- package/dist/.next/server/pages/_error.js.nft.json +1 -0
- package/dist/.next/server/pages-manifest.json +1 -0
- package/dist/.next/server/server-reference-manifest.js +1 -0
- package/dist/.next/server/server-reference-manifest.json +1 -0
- package/dist/.next/server/webpack-runtime.js +1 -0
- package/dist/.next/static/OmPVuV0d1lrcjSBQzxkdH/_buildManifest.js +1 -0
- package/dist/.next/static/OmPVuV0d1lrcjSBQzxkdH/_ssgManifest.js +1 -0
- package/dist/.next/static/chunks/422-6fa3c14fc64ad67b.js +1 -0
- package/dist/.next/static/chunks/4bd1b696-8c908eb707f85a27.js +1 -0
- package/dist/.next/static/chunks/517-3b698a5d3da06ff7.js +2 -0
- package/dist/.next/static/chunks/692-ede418e1d28a1394.js +1 -0
- package/dist/.next/static/chunks/735-c9144179c6bd7d06.js +1 -0
- package/dist/.next/static/chunks/92-c4466e0f1ca8a3a8.js +1 -0
- package/dist/.next/static/chunks/app/_not-found/page-b786c3dd79ad15a5.js +1 -0
- package/dist/.next/static/chunks/app/api/cloudflare/route-3458d78022185d7f.js +1 -0
- package/dist/.next/static/chunks/app/api/databases/route-64255424877a9f72.js +1 -0
- package/dist/.next/static/chunks/app/api/migrations/sync/route-03766b3afcf043ba.js +1 -0
- package/dist/.next/static/chunks/app/api/migrations/sync-files/route-02fa291c8000dadf.js +1 -0
- package/dist/.next/static/chunks/app/api/pull-remote/confirm/route-7e05bb8920127abe.js +1 -0
- package/dist/.next/static/chunks/app/api/pull-remote/preview/route-0fa15f783e8a306c.js +1 -0
- package/dist/.next/static/chunks/app/api/push-remote/confirm/route-96691f1880e4ed14.js +1 -0
- package/dist/.next/static/chunks/app/api/push-remote/preview/route-a798679c67374552.js +1 -0
- package/dist/.next/static/chunks/app/api/query/route-e90d0030a5083e20.js +1 -0
- package/dist/.next/static/chunks/app/api/tables/[name]/route-1efe63e73b480e58.js +1 -0
- package/dist/.next/static/chunks/app/api/tables/route-3772b4f480c4b4a4.js +1 -0
- package/dist/.next/static/chunks/app/layout-8360fab69b848283.js +1 -0
- package/dist/.next/static/chunks/app/page-75081ac7a88cb706.js +1 -0
- package/dist/.next/static/chunks/framework-d29117d969504448.js +1 -0
- package/dist/.next/static/chunks/main-8d0ab17774ffa031.js +1 -0
- package/dist/.next/static/chunks/main-app-d36cdfb402b40f13.js +1 -0
- package/dist/.next/static/chunks/pages/_app-d23763e3e6c904ff.js +1 -0
- package/dist/.next/static/chunks/pages/_error-9b7125ad1a1e68fa.js +1 -0
- package/dist/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/.next/static/chunks/webpack-c76ac1e586b2802a.js +1 -0
- package/dist/.next/static/css/c51a7ef325e30c16.css +3 -0
- package/dist/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
- package/dist/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/dist/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/dist/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- package/dist/.next/trace +13 -0
- package/dist/.next/types/app/api/cloudflare/route.ts +347 -0
- package/dist/.next/types/app/api/databases/route.ts +347 -0
- package/dist/.next/types/app/api/migrations/sync/route.ts +347 -0
- package/dist/.next/types/app/api/migrations/sync-files/route.ts +347 -0
- package/dist/.next/types/app/api/pull-remote/confirm/route.ts +347 -0
- package/dist/.next/types/app/api/pull-remote/preview/route.ts +347 -0
- package/dist/.next/types/app/api/push-remote/confirm/route.ts +347 -0
- package/dist/.next/types/app/api/push-remote/preview/route.ts +347 -0
- package/dist/.next/types/app/api/query/route.ts +347 -0
- package/dist/.next/types/app/api/tables/[name]/route.ts +347 -0
- package/dist/.next/types/app/api/tables/route.ts +347 -0
- package/dist/.next/types/app/page.ts +84 -0
- package/dist/.next/types/cache-life.d.ts +141 -0
- package/dist/.next/types/package.json +1 -0
- package/dist/app/api/cloudflare/route.js +146 -0
- package/dist/app/api/databases/route.js +72 -0
- package/dist/app/api/migrations/sync/route.js +172 -0
- package/dist/app/api/migrations/sync-files/route.js +172 -0
- package/dist/app/api/pull-remote/confirm/route.js +277 -0
- package/dist/app/api/pull-remote/preview/route.js +143 -0
- package/dist/app/api/push-remote/confirm/route.js +196 -0
- package/dist/app/api/push-remote/preview/route.js +178 -0
- package/dist/app/api/query/route.js +160 -0
- package/dist/app/api/tables/[name]/route.js +173 -0
- package/dist/app/api/tables/route.js +174 -0
- package/dist/app/app/api/cloudflare/route.js +146 -0
- package/dist/app/app/api/databases/route.js +72 -0
- package/dist/app/app/api/migrations/sync/route.js +172 -0
- package/dist/app/app/api/migrations/sync-files/route.js +172 -0
- package/dist/app/app/api/pull-remote/confirm/route.js +277 -0
- package/dist/app/app/api/pull-remote/preview/route.js +143 -0
- package/dist/app/app/api/push-remote/confirm/route.js +196 -0
- package/dist/app/app/api/push-remote/preview/route.js +178 -0
- package/dist/app/app/api/query/route.js +160 -0
- package/dist/app/app/api/tables/[name]/route.js +173 -0
- package/dist/app/app/api/tables/route.js +174 -0
- package/dist/app/app/globals.css +93 -0
- package/dist/app/app/layout.jsx +35 -0
- package/dist/app/app/page.jsx +37 -0
- package/dist/app/globals.css +93 -0
- package/dist/app/layout.jsx +35 -0
- package/dist/app/page.jsx +37 -0
- package/dist/d1-studio.js +344 -0
- package/dist/lib/database-queue.js +33 -0
- package/dist/lib/error.js +20 -0
- package/dist/lib/find-wrangler-config.js +67 -0
- package/dist/lib/license.js +99 -0
- package/dist/lib/migrations.js +415 -0
- package/dist/lib/parse-sql-error.js +200 -0
- package/dist/lib/request-deduplication.js +24 -0
- package/dist/lib/schema-operations.js +103 -0
- package/dist/lib/sql-error-types.js +14 -0
- package/dist/lib/sql-operations.js +557 -0
- package/dist/lib/utils.js +6 -0
- package/next.config.mjs +18 -0
- package/package.json +97 -0
- package/postcss.config.mjs +8 -0
- package/scripts/studio.js +344 -0
- package/scripts/test.js +168 -0
- package/src/app/api/cloudflare/route.js +146 -0
- package/src/app/api/databases/route.js +72 -0
- package/src/app/api/migrations/sync/route.js +172 -0
- package/src/app/api/migrations/sync-files/route.js +172 -0
- package/src/app/api/pull-remote/confirm/route.js +277 -0
- package/src/app/api/pull-remote/preview/route.js +143 -0
- package/src/app/api/push-remote/confirm/route.js +196 -0
- package/src/app/api/push-remote/preview/route.js +178 -0
- package/src/app/api/query/route.js +160 -0
- package/src/app/api/tables/[name]/route.js +173 -0
- package/src/app/api/tables/route.js +174 -0
- package/src/app/globals.css +93 -0
- package/src/app/layout.jsx +35 -0
- package/src/app/page.jsx +37 -0
- package/src/components/cloudflare-credentials-modal.jsx +94 -0
- package/src/components/column-selector.jsx +133 -0
- package/src/components/column-sorting.jsx +65 -0
- package/src/components/delete-records-dialog.jsx +64 -0
- package/src/components/download-button.jsx +205 -0
- package/src/components/edit-error-alert.jsx +30 -0
- package/src/components/editable-cell.jsx +163 -0
- package/src/components/editable-row.jsx +169 -0
- package/src/components/environment-handler.jsx +278 -0
- package/src/components/environment-select.jsx +59 -0
- package/src/components/pull-preview-toast.jsx +188 -0
- package/src/components/push-error-alert.jsx +72 -0
- package/src/components/push-preview-toast.jsx +183 -0
- package/src/components/query-results.jsx +112 -0
- package/src/components/refresh-button.jsx +61 -0
- package/src/components/resize-columns.jsx +101 -0
- package/src/components/sidebar.jsx +236 -0
- package/src/components/sql-editor.jsx +411 -0
- package/src/components/sql-error-display.jsx +167 -0
- package/src/components/sync-migrations-button.jsx +87 -0
- package/src/components/table-error-dialog.jsx +65 -0
- package/src/components/table-toolbar.jsx +285 -0
- package/src/components/table-view.jsx +843 -0
- package/src/components/ui/alert.jsx +47 -0
- package/src/components/ui/button.jsx +48 -0
- package/src/components/ui/card.jsx +50 -0
- package/src/components/ui/checkbox.jsx +24 -0
- package/src/components/ui/collapsible.jsx +11 -0
- package/src/components/ui/dialog.jsx +96 -0
- package/src/components/ui/dropdown-menu.jsx +158 -0
- package/src/components/ui/input.jsx +19 -0
- package/src/components/ui/scroll-area.jsx +40 -0
- package/src/components/ui/select.jsx +121 -0
- package/src/components/ui/separator.jsx +25 -0
- package/src/components/ui/sheet.jsx +109 -0
- package/src/components/ui/sidebar.jsx +620 -0
- package/src/components/ui/skeleton.jsx +14 -0
- package/src/components/ui/table.jsx +86 -0
- package/src/components/ui/tabs.jsx +43 -0
- package/src/components/ui/toast.jsx +86 -0
- package/src/components/ui/toaster.jsx +35 -0
- package/src/components/ui/tooltip.jsx +28 -0
- package/src/context/database-context.js +510 -0
- package/src/hooks/use-mobile.jsx +19 -0
- package/src/hooks/use-sql-error.ts +23 -0
- package/src/hooks/use-toast.js +155 -0
- package/src/lib/database-queue.js +33 -0
- package/src/lib/error.js +20 -0
- package/src/lib/find-wrangler-config.js +67 -0
- package/src/lib/license.js +99 -0
- package/src/lib/migrations.js +415 -0
- package/src/lib/parse-sql-error.js +200 -0
- package/src/lib/request-deduplication.js +24 -0
- package/src/lib/schema-operations.js +103 -0
- package/src/lib/sql-error-types.js +14 -0
- package/src/lib/sql-operations.js +557 -0
- package/src/lib/utils.js +6 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// api/pull-remote/preview/route.js
|
|
2
|
+
|
|
3
|
+
import { NextResponse } from 'next/server';
|
|
4
|
+
import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
|
|
5
|
+
import { ErrorTypes,} from '@/lib/sql-operations';
|
|
6
|
+
import { getCurrentSchema, analyzeDependencies } from '@/lib/schema-operations';
|
|
7
|
+
import { getRemoteMigrations } from '@/lib/migrations.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates a preview for pulling remote migrations.
|
|
11
|
+
* Retrieves the remote schema and migration metadata, orders tables by dependency,
|
|
12
|
+
* and builds a preview object that lists which tables will be created and which migrations will be tracked.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} projectDir - The project directory path.
|
|
15
|
+
* @param {string} accountId - Cloudflare account ID.
|
|
16
|
+
* @param {string} apiToken - Cloudflare API token.
|
|
17
|
+
* @param {string} databaseName - The target D1 database name.
|
|
18
|
+
* @returns {object} Preview object containing migration details.
|
|
19
|
+
*/
|
|
20
|
+
async function generatePullPreview(projectDir, accountId, apiToken, databaseName) {
|
|
21
|
+
const env = {
|
|
22
|
+
...process.env,
|
|
23
|
+
CLOUDFLARE_API_TOKEN: apiToken,
|
|
24
|
+
CLOUDFLARE_ACCOUNT_ID: accountId
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Get the remote schema and migration metadata using provided helpers
|
|
29
|
+
const remoteSchema = await getCurrentSchema(projectDir, databaseName, env, true);
|
|
30
|
+
const remoteMigrations = await getRemoteMigrations(projectDir, databaseName, env);
|
|
31
|
+
|
|
32
|
+
// Analyze dependencies to order tables for creation
|
|
33
|
+
const orderedTables = analyzeDependencies(remoteSchema);
|
|
34
|
+
|
|
35
|
+
// Build the preview object
|
|
36
|
+
const preview = {
|
|
37
|
+
preview: [],
|
|
38
|
+
totalTables: remoteSchema.length,
|
|
39
|
+
totalMigrations: remoteMigrations.length
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Add each table (ordered by dependency) to the preview list
|
|
43
|
+
orderedTables.forEach((tableName, index) => {
|
|
44
|
+
const table = remoteSchema.find(t => t.name === tableName);
|
|
45
|
+
if (table) {
|
|
46
|
+
preview.preview.push({
|
|
47
|
+
name: tableName,
|
|
48
|
+
type: 'table',
|
|
49
|
+
action: 'will be created',
|
|
50
|
+
order: index + 1
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Add migration records to the preview.
|
|
56
|
+
// Since remote migrations do not include a checksum, we omit it.
|
|
57
|
+
remoteMigrations.forEach(migration => {
|
|
58
|
+
preview.preview.push({
|
|
59
|
+
name: migration.name,
|
|
60
|
+
type: 'migration',
|
|
61
|
+
action: 'will be tracked'
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return preview;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Pull preview generation error:', error);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* POST handler for pull-remote preview.
|
|
74
|
+
* Reads the Wrangler configuration (via findWranglerConfig) to get the D1 database name,
|
|
75
|
+
* then generates a preview of the migrations to be pulled.
|
|
76
|
+
*/
|
|
77
|
+
export async function POST(req) {
|
|
78
|
+
try {
|
|
79
|
+
const { accountId, apiToken } = await req.json();
|
|
80
|
+
const projectDir = process.env.D1_PROJECT_DIR;
|
|
81
|
+
|
|
82
|
+
if (!projectDir) {
|
|
83
|
+
return NextResponse.json({
|
|
84
|
+
success: false,
|
|
85
|
+
error: {
|
|
86
|
+
type: ErrorTypes.VALIDATION_ERROR,
|
|
87
|
+
message: 'Project directory not configured'
|
|
88
|
+
}
|
|
89
|
+
}, { status: 500 });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Load Wrangler configuration.
|
|
93
|
+
const wranglerResult = await findWranglerConfig(projectDir);
|
|
94
|
+
if (!wranglerResult || !wranglerResult.config) {
|
|
95
|
+
return NextResponse.json({
|
|
96
|
+
success: false,
|
|
97
|
+
error: {
|
|
98
|
+
type: ErrorTypes.VALIDATION_ERROR,
|
|
99
|
+
message: 'Could not find Wrangler configuration'
|
|
100
|
+
}
|
|
101
|
+
}, { status: 400 });
|
|
102
|
+
}
|
|
103
|
+
const config = wranglerResult.config;
|
|
104
|
+
|
|
105
|
+
// Ensure that at least one D1 database is configured.
|
|
106
|
+
if (!config.d1_databases?.length) {
|
|
107
|
+
return NextResponse.json({
|
|
108
|
+
success: false,
|
|
109
|
+
error: {
|
|
110
|
+
type: ErrorTypes.VALIDATION_ERROR,
|
|
111
|
+
message: 'No D1 databases configured'
|
|
112
|
+
}
|
|
113
|
+
}, { status: 400 });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Use the first D1 database's name from the configuration.
|
|
117
|
+
const databaseName = config.d1_databases[0].database_name;
|
|
118
|
+
const preview = await generatePullPreview(projectDir, accountId, apiToken, databaseName);
|
|
119
|
+
|
|
120
|
+
return NextResponse.json({
|
|
121
|
+
success: true,
|
|
122
|
+
message: 'Pull migration preview generated',
|
|
123
|
+
details: preview
|
|
124
|
+
});
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('Pull remote preview error:', error);
|
|
127
|
+
return NextResponse.json({
|
|
128
|
+
success: false,
|
|
129
|
+
error: {
|
|
130
|
+
type: error.type || ErrorTypes.EXECUTION_ERROR,
|
|
131
|
+
message: error.message || 'Failed to generate pull migration preview',
|
|
132
|
+
details: error.details
|
|
133
|
+
}
|
|
134
|
+
}, { status: 500 });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* OPTIONS handler for pre-flight requests.
|
|
140
|
+
*/
|
|
141
|
+
export async function OPTIONS() {
|
|
142
|
+
return new NextResponse(null, { status: 204 });
|
|
143
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// api/push-remote/confirm/route.js
|
|
2
|
+
|
|
3
|
+
import { NextResponse } from 'next/server';
|
|
4
|
+
import { readFile, readdir } from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
|
|
7
|
+
import { executeSQL, ErrorTypes, tableExists, parseStatement, removeSQLComments } from '@/lib/sql-operations';
|
|
8
|
+
import { validateMigrationFile } from '@/lib/migrations.js';
|
|
9
|
+
|
|
10
|
+
async function pushToRemote(projectDir, accountId, apiToken, databaseName, preview = false) {
|
|
11
|
+
const env = {
|
|
12
|
+
...process.env,
|
|
13
|
+
CLOUDFLARE_ACCOUNT_ID: accountId,
|
|
14
|
+
CLOUDFLARE_API_TOKEN: apiToken,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Create local migrations table (with checksum) and remote migrations table (without checksum)
|
|
19
|
+
const localCreateMigrationsTable = `
|
|
20
|
+
CREATE TABLE IF NOT EXISTS d1_migrations (
|
|
21
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
22
|
+
name TEXT NOT NULL UNIQUE,
|
|
23
|
+
checksum TEXT NOT NULL,
|
|
24
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
25
|
+
);
|
|
26
|
+
`;
|
|
27
|
+
const remoteCreateMigrationsTable = `
|
|
28
|
+
CREATE TABLE IF NOT EXISTS d1_migrations (
|
|
29
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
30
|
+
name TEXT NOT NULL UNIQUE,
|
|
31
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
32
|
+
);
|
|
33
|
+
`;
|
|
34
|
+
await executeSQL(projectDir, databaseName, localCreateMigrationsTable);
|
|
35
|
+
await executeSQL(projectDir, databaseName, remoteCreateMigrationsTable, env, true);
|
|
36
|
+
|
|
37
|
+
// Get remote migration records using applied_at.
|
|
38
|
+
const remoteMigrations = await executeSQL(
|
|
39
|
+
projectDir,
|
|
40
|
+
databaseName,
|
|
41
|
+
'SELECT name FROM d1_migrations ORDER BY applied_at;',
|
|
42
|
+
env,
|
|
43
|
+
true
|
|
44
|
+
);
|
|
45
|
+
const parseResults = (stdout) => {
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(stdout);
|
|
48
|
+
return parsed[0]?.results || [];
|
|
49
|
+
} catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const remoteMigrationsMap = new Map(
|
|
54
|
+
parseResults(remoteMigrations).map(m => [m.name, m])
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Read migration files from disk.
|
|
58
|
+
const migrationsDir = path.join(projectDir, 'migrations');
|
|
59
|
+
const files = await readdir(migrationsDir);
|
|
60
|
+
const migrationFiles = files.filter(f => f.endsWith('.sql')).sort();
|
|
61
|
+
|
|
62
|
+
const results = { applied: [], skipped: [], preview: [] };
|
|
63
|
+
|
|
64
|
+
// Process each migration file.
|
|
65
|
+
for (const migrationFile of migrationFiles) {
|
|
66
|
+
const fullPath = path.join(migrationsDir, migrationFile);
|
|
67
|
+
const previewRecord = { name: migrationFile, action: '', reason: '' };
|
|
68
|
+
try {
|
|
69
|
+
const validation = await validateMigrationFile(fullPath);
|
|
70
|
+
if (!validation.isValid) {
|
|
71
|
+
previewRecord.action = 'failed validation';
|
|
72
|
+
previewRecord.reason = 'Migration validation failed';
|
|
73
|
+
results.preview.push(previewRecord);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// For remote, compare by migration file name.
|
|
77
|
+
if (remoteMigrationsMap.has(migrationFile)) {
|
|
78
|
+
previewRecord.action = 'skipped';
|
|
79
|
+
previewRecord.reason = 'Already applied';
|
|
80
|
+
results.preview.push(previewRecord);
|
|
81
|
+
results.skipped.push({ name: migrationFile, reason: previewRecord.reason });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// Read the migration content.
|
|
85
|
+
let content = await readFile(fullPath, 'utf8');
|
|
86
|
+
// Strip out any header comments (lines starting with "--").
|
|
87
|
+
content = removeSQLComments(content);
|
|
88
|
+
// Parse the SQL content to extract details.
|
|
89
|
+
const parsedStatement = parseStatement(content);
|
|
90
|
+
const tableName = parsedStatement.tableName;
|
|
91
|
+
|
|
92
|
+
// Only if the migration is a CREATE_TABLE do we check for existing table.
|
|
93
|
+
if (parsedStatement.type === 'CREATE_TABLE' && tableName) {
|
|
94
|
+
const exists = await tableExists(projectDir, databaseName, env, tableName, true);
|
|
95
|
+
if (exists) {
|
|
96
|
+
previewRecord.action = 'skipped';
|
|
97
|
+
previewRecord.reason = `Table "${tableName}" already exists`;
|
|
98
|
+
results.skipped.push({ name: migrationFile, reason: previewRecord.reason });
|
|
99
|
+
results.preview.push(previewRecord);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// For ALTER_TABLE and other migrations, we do not skip based on table existence.
|
|
104
|
+
previewRecord.action = 'will be applied';
|
|
105
|
+
results.preview.push(previewRecord);
|
|
106
|
+
if (!preview) {
|
|
107
|
+
// Execute the migration SQL on the remote database.
|
|
108
|
+
await executeSQL(projectDir, databaseName, content, env, true);
|
|
109
|
+
// Record the migration on remote (without checksum).
|
|
110
|
+
await executeSQL(
|
|
111
|
+
projectDir,
|
|
112
|
+
databaseName,
|
|
113
|
+
`INSERT INTO d1_migrations (name) VALUES ('${migrationFile}');`,
|
|
114
|
+
env,
|
|
115
|
+
true
|
|
116
|
+
);
|
|
117
|
+
previewRecord.action = 'applied';
|
|
118
|
+
results.applied.push({ name: migrationFile });
|
|
119
|
+
}
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// If error indicates the object already exists, mark as skipped.
|
|
122
|
+
const errMsg = err.message || '';
|
|
123
|
+
const errStderr = (err.details && err.details.stderr) || '';
|
|
124
|
+
if (errMsg.includes("already exists") || errStderr.includes("already exists")) {
|
|
125
|
+
previewRecord.action = 'skipped';
|
|
126
|
+
previewRecord.reason = 'Already applied';
|
|
127
|
+
results.skipped.push({ name: migrationFile, reason: previewRecord.reason });
|
|
128
|
+
} else {
|
|
129
|
+
previewRecord.action = 'failed';
|
|
130
|
+
previewRecord.reason = errMsg || 'Failed to apply migration';
|
|
131
|
+
results.skipped.push({ name: migrationFile, reason: previewRecord.reason });
|
|
132
|
+
}
|
|
133
|
+
results.preview.push(previewRecord);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
console.log("Push Confirm Result (server):", results);
|
|
137
|
+
return { success: true, preview: false, applied: results.applied.length, skipped: results.skipped.length, details: results };
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw {
|
|
140
|
+
type: ErrorTypes.EXECUTION_ERROR,
|
|
141
|
+
message: error.message || 'Failed to push migrations',
|
|
142
|
+
details: error.details || { error: error.stderr || error.message }
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function POST(req) {
|
|
148
|
+
try {
|
|
149
|
+
const { accountId, apiToken } = await req.json();
|
|
150
|
+
const projectDir = process.env.D1_PROJECT_DIR;
|
|
151
|
+
if (!projectDir) {
|
|
152
|
+
return NextResponse.json({
|
|
153
|
+
success: false,
|
|
154
|
+
error: { message: 'Project directory not configured', type: ErrorTypes.VALIDATION_ERROR }
|
|
155
|
+
}, { status: 500 });
|
|
156
|
+
}
|
|
157
|
+
const wranglerResult = await findWranglerConfig(projectDir);
|
|
158
|
+
if (!wranglerResult || !wranglerResult.config) {
|
|
159
|
+
return NextResponse.json({
|
|
160
|
+
success: false,
|
|
161
|
+
error: { message: 'Could not find Wrangler configuration', type: ErrorTypes.VALIDATION_ERROR }
|
|
162
|
+
}, { status: 400 });
|
|
163
|
+
}
|
|
164
|
+
const config = wranglerResult.config;
|
|
165
|
+
if (!config.d1_databases?.length) {
|
|
166
|
+
return NextResponse.json({
|
|
167
|
+
success: false,
|
|
168
|
+
error: { message: 'No D1 databases configured', type: ErrorTypes.VALIDATION_ERROR }
|
|
169
|
+
}, { status: 400 });
|
|
170
|
+
}
|
|
171
|
+
const databaseName = config.d1_databases[0].database_name;
|
|
172
|
+
// Force preview to be false for actual execution.
|
|
173
|
+
const result = await pushToRemote(projectDir, accountId, apiToken, databaseName, false);
|
|
174
|
+
return NextResponse.json({
|
|
175
|
+
success: true,
|
|
176
|
+
message: 'Successfully pushed migrations to remote',
|
|
177
|
+
details: result
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('Push to remote confirm error:', error);
|
|
181
|
+
return NextResponse.json({
|
|
182
|
+
success: false,
|
|
183
|
+
error: {
|
|
184
|
+
type: error.type || ErrorTypes.EXECUTION_ERROR,
|
|
185
|
+
message: error.message || 'Failed to push to remote',
|
|
186
|
+
details: error.details || { error: error.stderr || error.message }
|
|
187
|
+
}
|
|
188
|
+
}, { status: error.type === ErrorTypes.VALIDATION_ERROR ? 400 : 500 });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export async function OPTIONS() {
|
|
193
|
+
return new NextResponse(null, { status: 204 });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// api/push-remote/preview/route.js
|
|
2
|
+
|
|
3
|
+
import { NextResponse } from 'next/server';
|
|
4
|
+
import { readdir } from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
|
|
7
|
+
import { executeSQL, ErrorTypes } from '@/lib/sql-operations';
|
|
8
|
+
import { validateMigrationFile } from '@/lib/migrations.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Pushes migration preview to remote.
|
|
12
|
+
* Validates migration files against remote migration records and returns a preview
|
|
13
|
+
* of which migrations will be applied, skipped, or failed.
|
|
14
|
+
*
|
|
15
|
+
* Note: For remote, we no longer use the checksum.
|
|
16
|
+
*/
|
|
17
|
+
async function pushToRemote(projectDir, accountId, apiToken, databaseName, preview = true) {
|
|
18
|
+
const env = {
|
|
19
|
+
...process.env,
|
|
20
|
+
CLOUDFLARE_ACCOUNT_ID: accountId,
|
|
21
|
+
CLOUDFLARE_API_TOKEN: apiToken,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Create local migrations table (with checksum)
|
|
26
|
+
const localCreateMigrationsTable = `
|
|
27
|
+
CREATE TABLE IF NOT EXISTS d1_migrations (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
name TEXT NOT NULL UNIQUE,
|
|
30
|
+
checksum TEXT NOT NULL,
|
|
31
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
32
|
+
);
|
|
33
|
+
`;
|
|
34
|
+
// Create remote migrations table (without checksum; use applied_at)
|
|
35
|
+
const remoteCreateMigrationsTable = `
|
|
36
|
+
CREATE TABLE IF NOT EXISTS d1_migrations (
|
|
37
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
38
|
+
name TEXT NOT NULL UNIQUE,
|
|
39
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
40
|
+
);
|
|
41
|
+
`;
|
|
42
|
+
await executeSQL(projectDir, databaseName, localCreateMigrationsTable);
|
|
43
|
+
await executeSQL(projectDir, databaseName, remoteCreateMigrationsTable, env, true);
|
|
44
|
+
|
|
45
|
+
// Get remote migration records using applied_at for ordering.
|
|
46
|
+
const remoteMigrations = await executeSQL(
|
|
47
|
+
projectDir,
|
|
48
|
+
databaseName,
|
|
49
|
+
'SELECT name FROM d1_migrations ORDER BY applied_at;',
|
|
50
|
+
env,
|
|
51
|
+
true
|
|
52
|
+
);
|
|
53
|
+
const parseResults = (stdout) => {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(stdout);
|
|
56
|
+
return parsed[0]?.results || [];
|
|
57
|
+
} catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
// Use migration file name as key
|
|
62
|
+
const remoteMigrationsMap = new Map(
|
|
63
|
+
parseResults(remoteMigrations).map(m => [m.name, m])
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Read migration files from disk.
|
|
67
|
+
const migrationsDir = path.join(projectDir, 'migrations');
|
|
68
|
+
const files = await readdir(migrationsDir);
|
|
69
|
+
const migrationFiles = files.filter(f => f.endsWith('.sql')).sort();
|
|
70
|
+
|
|
71
|
+
const results = { applied: [], skipped: [], preview: [] };
|
|
72
|
+
|
|
73
|
+
// Process each migration file.
|
|
74
|
+
for (const migrationFile of migrationFiles) {
|
|
75
|
+
const fullPath = path.join(migrationsDir, migrationFile);
|
|
76
|
+
const previewRecord = { name: migrationFile, action: '', reason: '' };
|
|
77
|
+
try {
|
|
78
|
+
const validation = await validateMigrationFile(fullPath);
|
|
79
|
+
if (!validation.isValid) {
|
|
80
|
+
previewRecord.action = 'failed validation';
|
|
81
|
+
previewRecord.reason = 'Migration validation failed';
|
|
82
|
+
results.preview.push(previewRecord);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
// For push preview, compare by migration file name.
|
|
86
|
+
if (remoteMigrationsMap.has(migrationFile)) {
|
|
87
|
+
previewRecord.action = 'skipped';
|
|
88
|
+
previewRecord.reason = 'Already applied';
|
|
89
|
+
results.preview.push(previewRecord);
|
|
90
|
+
results.skipped.push({ name: migrationFile, reason: previewRecord.reason });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
previewRecord.action = 'will be applied';
|
|
94
|
+
results.preview.push(previewRecord);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
previewRecord.action = 'failed';
|
|
97
|
+
previewRecord.reason = error.message || 'Failed to process migration';
|
|
98
|
+
results.preview.push(previewRecord);
|
|
99
|
+
results.skipped.push({ name: migrationFile, reason: previewRecord.reason });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log("Push Preview Result (server):", results);
|
|
104
|
+
return { success: true, preview: true, applied: results.applied.length, skipped: results.skipped.length, details: results };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw {
|
|
107
|
+
type: ErrorTypes.EXECUTION_ERROR,
|
|
108
|
+
message: error.message || 'Failed to push migrations',
|
|
109
|
+
details: error.details || { error: error.stderr || error.message }
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* POST handler for push-remote preview.
|
|
116
|
+
* Loads the Wrangler configuration to obtain the D1 database name,
|
|
117
|
+
* then calls pushToRemote in preview mode and returns a preview.
|
|
118
|
+
*/
|
|
119
|
+
export async function POST(req) {
|
|
120
|
+
try {
|
|
121
|
+
const { accountId, apiToken } = await req.json();
|
|
122
|
+
const projectDir = process.env.D1_PROJECT_DIR;
|
|
123
|
+
if (!projectDir) {
|
|
124
|
+
return NextResponse.json({
|
|
125
|
+
success: false,
|
|
126
|
+
error: {
|
|
127
|
+
type: ErrorTypes.VALIDATION_ERROR,
|
|
128
|
+
message: 'Project directory not configured'
|
|
129
|
+
}
|
|
130
|
+
}, { status: 500 });
|
|
131
|
+
}
|
|
132
|
+
const wranglerResult = await findWranglerConfig(projectDir);
|
|
133
|
+
if (!wranglerResult || !wranglerResult.config) {
|
|
134
|
+
return NextResponse.json({
|
|
135
|
+
success: false,
|
|
136
|
+
error: {
|
|
137
|
+
type: ErrorTypes.VALIDATION_ERROR,
|
|
138
|
+
message: 'Could not find Wrangler configuration'
|
|
139
|
+
}
|
|
140
|
+
}, { status: 400 });
|
|
141
|
+
}
|
|
142
|
+
const config = wranglerResult.config;
|
|
143
|
+
if (!config.d1_databases?.length) {
|
|
144
|
+
return NextResponse.json({
|
|
145
|
+
success: false,
|
|
146
|
+
error: {
|
|
147
|
+
type: ErrorTypes.VALIDATION_ERROR,
|
|
148
|
+
message: 'No D1 databases configured'
|
|
149
|
+
}
|
|
150
|
+
}, { status: 400 });
|
|
151
|
+
}
|
|
152
|
+
const databaseName = config.d1_databases[0].database_name;
|
|
153
|
+
const result = await pushToRemote(projectDir, accountId, apiToken, databaseName, true);
|
|
154
|
+
return NextResponse.json({
|
|
155
|
+
success: true,
|
|
156
|
+
message: 'Preview generated for migrations to be pushed to remote',
|
|
157
|
+
details: result
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error('Push to remote preview error:', error);
|
|
161
|
+
return NextResponse.json({
|
|
162
|
+
success: false,
|
|
163
|
+
error: {
|
|
164
|
+
type: error.type || ErrorTypes.EXECUTION_ERROR,
|
|
165
|
+
message: error.message || 'Failed to push to remote preview',
|
|
166
|
+
details: error.details || { error: error.stderr || error.message }
|
|
167
|
+
}
|
|
168
|
+
}, { status: error.type === ErrorTypes.VALIDATION_ERROR ? 400 : 500 });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* OPTIONS handler for pre-flight requests.
|
|
174
|
+
*/
|
|
175
|
+
export async function OPTIONS() {
|
|
176
|
+
return new NextResponse(null, { status: 204 });
|
|
177
|
+
}
|
|
178
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// api/query/route.js
|
|
2
|
+
|
|
3
|
+
import { NextResponse } from 'next/server';
|
|
4
|
+
import { findWranglerConfig } from '@/lib/find-wrangler-config.js';
|
|
5
|
+
import {
|
|
6
|
+
ErrorTypes,
|
|
7
|
+
parseQuery,
|
|
8
|
+
executeSQL,
|
|
9
|
+
validateStatement
|
|
10
|
+
} from '@/lib/sql-operations';
|
|
11
|
+
import { handleSchemaChange } from '@/lib/migrations';
|
|
12
|
+
import { isSchemaChange } from '@/lib/sql-operations';
|
|
13
|
+
import { parseSQLError } from '@/lib/parse-sql-error';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* API POST handler to execute SQL queries against the configured D1 database.
|
|
17
|
+
*/
|
|
18
|
+
export async function POST(req) {
|
|
19
|
+
try {
|
|
20
|
+
// Ensure that the project directory is set.
|
|
21
|
+
const projectDir = process.env.D1_PROJECT_DIR;
|
|
22
|
+
if (!projectDir) {
|
|
23
|
+
return NextResponse.json(
|
|
24
|
+
{ error: 'Project directory not configured' },
|
|
25
|
+
{ status: 500 }
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Extract the SQL query from the request body.
|
|
30
|
+
const { query } = await req.json();
|
|
31
|
+
console.log('Received query:', query);
|
|
32
|
+
|
|
33
|
+
if (!query?.trim()) {
|
|
34
|
+
return NextResponse.json(
|
|
35
|
+
{ success: false, error: { message: 'Query cannot be empty' } },
|
|
36
|
+
{ status: 400 }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Retrieve the Wrangler configuration using the centralized helper.
|
|
41
|
+
const wranglerResult = await findWranglerConfig(projectDir);
|
|
42
|
+
if (!wranglerResult || !wranglerResult.config) {
|
|
43
|
+
return NextResponse.json(
|
|
44
|
+
{ success: false, error: { message: 'No Wrangler configuration file found' } },
|
|
45
|
+
{ status: 404 }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const config = wranglerResult.config;
|
|
49
|
+
|
|
50
|
+
// Check if there is at least one D1 database configured.
|
|
51
|
+
if (!config.d1_databases?.length) {
|
|
52
|
+
return NextResponse.json(
|
|
53
|
+
{ success: false, error: { message: 'No D1 databases configured' } },
|
|
54
|
+
{ status: 404 }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Use the first configured database.
|
|
59
|
+
const databaseName = config.d1_databases[0].database_name;
|
|
60
|
+
console.log('Using database:', databaseName);
|
|
61
|
+
|
|
62
|
+
// Parse the query into individual SQL statements.
|
|
63
|
+
let statements = parseQuery(query);
|
|
64
|
+
console.log('Parsed statements:', statements);
|
|
65
|
+
|
|
66
|
+
if (!statements.length) {
|
|
67
|
+
return NextResponse.json(
|
|
68
|
+
{ success: false, error: { message: 'No valid SQL statements found' } },
|
|
69
|
+
{ status: 400 }
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Validate each parsed statement.
|
|
74
|
+
statements = statements.map(validateStatement);
|
|
75
|
+
console.log('Validated statements:', statements);
|
|
76
|
+
|
|
77
|
+
// Initialize the migrations table (if it doesn't already exist).
|
|
78
|
+
const createMigrationsTable = `
|
|
79
|
+
CREATE TABLE IF NOT EXISTS d1_migrations (
|
|
80
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
81
|
+
name TEXT NOT NULL UNIQUE,
|
|
82
|
+
checksum TEXT NOT NULL,
|
|
83
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
84
|
+
);
|
|
85
|
+
`;
|
|
86
|
+
await executeSQL(projectDir, databaseName, createMigrationsTable);
|
|
87
|
+
|
|
88
|
+
// Determine whether the query involves any schema changes.
|
|
89
|
+
const hasSchemaChanges = statements.some(isSchemaChange);
|
|
90
|
+
console.log('Has schema changes:', hasSchemaChanges);
|
|
91
|
+
|
|
92
|
+
let result;
|
|
93
|
+
if (hasSchemaChanges) {
|
|
94
|
+
// Process schema changes and capture migrations.
|
|
95
|
+
// The handleSchemaChange helper is responsible for executing the DDL statements.
|
|
96
|
+
const migrations = await handleSchemaChange(projectDir, databaseName, statements);
|
|
97
|
+
console.log('Schema changes processed:', migrations);
|
|
98
|
+
|
|
99
|
+
result = {
|
|
100
|
+
success: true,
|
|
101
|
+
migrations,
|
|
102
|
+
results: [{ results: [] }]
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
// For data modification queries, execute the statements.
|
|
106
|
+
const results = [];
|
|
107
|
+
for (const stmt of statements) {
|
|
108
|
+
console.log('Executing data modification:', stmt.sql);
|
|
109
|
+
const output = await executeSQL(projectDir, databaseName, stmt.sql);
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(output);
|
|
113
|
+
const queryResults = parsed[0]?.results || [];
|
|
114
|
+
results.push(...queryResults);
|
|
115
|
+
} catch (parseError) {
|
|
116
|
+
console.log('Non-JSON output:', output);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
result = {
|
|
122
|
+
success: true,
|
|
123
|
+
results: [{ results }]
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return NextResponse.json(result);
|
|
128
|
+
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('Query execution error:', error);
|
|
131
|
+
const parsedError = parseSQLError(error);
|
|
132
|
+
|
|
133
|
+
const statusCode =
|
|
134
|
+
error.type === ErrorTypes.VALIDATION_ERROR ? 400 :
|
|
135
|
+
error.type === ErrorTypes.DEPENDENCY_ERROR ? 409 :
|
|
136
|
+
500;
|
|
137
|
+
|
|
138
|
+
return NextResponse.json(
|
|
139
|
+
{
|
|
140
|
+
success: false,
|
|
141
|
+
error: parsedError
|
|
142
|
+
},
|
|
143
|
+
{ status: statusCode }
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* OPTIONS handler for pre-flight requests.
|
|
150
|
+
*/
|
|
151
|
+
export async function OPTIONS() {
|
|
152
|
+
return new NextResponse(null, {
|
|
153
|
+
status: 204,
|
|
154
|
+
headers: {
|
|
155
|
+
'Access-Control-Allow-Origin': '*',
|
|
156
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
157
|
+
'Access-Control-Allow-Headers': 'Content-Type'
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|