diff --git a/.gitignore b/.gitignore
index 40b878d..289e347 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
-node_modules/
\ No newline at end of file
+node_modules/
+.next/
+test-results/
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 39a0fff..729fe31 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,3 +1,3 @@
## How publishing this module works
-HarperDB Components require the `config.yaml` to be in the root of the project; however, in order for the Docker
\ No newline at end of file
+HarperDB Components require the `config.yaml` to be in the root of the project; however, in order for the Docker
diff --git a/fixtures/next-13/.npmrc b/fixtures/next-13/.npmrc
new file mode 100644
index 0000000..9cf9495
--- /dev/null
+++ b/fixtures/next-13/.npmrc
@@ -0,0 +1 @@
+package-lock=false
\ No newline at end of file
diff --git a/fixtures/next-13/app/layout.js b/fixtures/next-13/app/layout.js
index ee00d88..4ace4d7 100644
--- a/fixtures/next-13/app/layout.js
+++ b/fixtures/next-13/app/layout.js
@@ -1,5 +1,5 @@
export const metadata = {
- title: 'HarperDB - Next.js v15 App',
+ title: 'HarperDB - Next.js v13 App',
};
export default function RootLayout({ children }) {
diff --git a/fixtures/next-13/app/page.js b/fixtures/next-13/app/page.js
index 4a48cf9..b3b670a 100644
--- a/fixtures/next-13/app/page.js
+++ b/fixtures/next-13/app/page.js
@@ -1,7 +1,7 @@
export default async function Page() {
return (
-
Next.js v15
+ Next.js v13
);
}
diff --git a/fixtures/next-13/package.json b/fixtures/next-13/package.json
index a12eadb..83fa08e 100644
--- a/fixtures/next-13/package.json
+++ b/fixtures/next-13/package.json
@@ -5,10 +5,13 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "postinstall": "npm link @harperdb/nextjs"
},
"dependencies": {
- "@harperdb/nextjs": "file:/@harperdb/nextjs",
- "next": "13.5.7"
+ "@harperdb/nextjs": "*",
+ "react": "^18",
+ "react-dom": "^18",
+ "next": "^13"
}
}
diff --git a/fixtures/next-14/.npmrc b/fixtures/next-14/.npmrc
new file mode 100644
index 0000000..9cf9495
--- /dev/null
+++ b/fixtures/next-14/.npmrc
@@ -0,0 +1 @@
+package-lock=false
\ No newline at end of file
diff --git a/fixtures/next-14/app/layout.js b/fixtures/next-14/app/layout.js
index ee00d88..7fd8992 100644
--- a/fixtures/next-14/app/layout.js
+++ b/fixtures/next-14/app/layout.js
@@ -1,5 +1,5 @@
export const metadata = {
- title: 'HarperDB - Next.js v15 App',
+ title: 'HarperDB - Next.js v14 App',
};
export default function RootLayout({ children }) {
diff --git a/fixtures/next-14/app/page.js b/fixtures/next-14/app/page.js
index 4a48cf9..9d17389 100644
--- a/fixtures/next-14/app/page.js
+++ b/fixtures/next-14/app/page.js
@@ -1,7 +1,7 @@
export default async function Page() {
return (
-
Next.js v15
+ Next.js v14
);
}
diff --git a/fixtures/next-14/package.json b/fixtures/next-14/package.json
index db6467a..63ac233 100644
--- a/fixtures/next-14/package.json
+++ b/fixtures/next-14/package.json
@@ -5,12 +5,13 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "postinstall": "npm link @harperdb/nextjs"
},
"dependencies": {
- "@harperdb/nextjs": "file:/@harperdb/nextjs",
+ "@harperdb/nextjs": "*",
"react": "^18",
"react-dom": "^18",
- "next": "14.2.18"
+ "next": "^14"
}
}
diff --git a/fixtures/next-15/.npmrc b/fixtures/next-15/.npmrc
new file mode 100644
index 0000000..9cf9495
--- /dev/null
+++ b/fixtures/next-15/.npmrc
@@ -0,0 +1 @@
+package-lock=false
\ No newline at end of file
diff --git a/fixtures/next-15/package-lock.json b/fixtures/next-15/package-lock.json
deleted file mode 100644
index 0f8978f..0000000
--- a/fixtures/next-15/package-lock.json
+++ /dev/null
@@ -1,901 +0,0 @@
-{
- "name": "next-15",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "next-15",
- "dependencies": {
- "@harperdb/nextjs": "^0.0.12",
- "next": "15.0.3",
- "react": "19.0.0-rc-66855b96-20241106",
- "react-dom": "19.0.0-rc-66855b96-20241106"
- }
- },
- "node_modules/@emnapi/runtime": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
- "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@harperdb/nextjs": {
- "version": "0.0.12",
- "resolved": "https://registry.npmjs.org/@harperdb/nextjs/-/nextjs-0.0.12.tgz",
- "integrity": "sha512-QClIwYACdhv+iVyvyrJiq5/IFbR/GN4P5CvEi7FdCsu2yT3S/dJkUVni03HMKWGEmlE1NfiwcvJtQb3m4Az1sA==",
- "license": "MIT",
- "dependencies": {
- "shell-quote": "^1.8.1"
- },
- "bin": {
- "harperdb-nextjs": "cli.js"
- }
- },
- "node_modules/@img/sharp-darwin-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
- "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-darwin-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
- "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
- "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
- "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
- "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
- "cpu": [
- "arm"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
- "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-s390x": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
- "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
- "cpu": [
- "s390x"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
- "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
- "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linuxmusl-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
- "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-linux-arm": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
- "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
- "cpu": [
- "arm"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-arm": "1.0.5"
- }
- },
- "node_modules/@img/sharp-linux-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
- "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-arm64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linux-s390x": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
- "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
- "cpu": [
- "s390x"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-s390x": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linux-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
- "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
- "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
- "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
- }
- },
- "node_modules/@img/sharp-wasm32": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
- "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
- "cpu": [
- "wasm32"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/runtime": "^1.2.0"
- },
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-win32-ia32": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
- "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
- "cpu": [
- "ia32"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-win32-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
- "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@next/env": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-15.0.3.tgz",
- "integrity": "sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==",
- "license": "MIT"
- },
- "node_modules/@next/swc-darwin-arm64": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.3.tgz",
- "integrity": "sha512-s3Q/NOorCsLYdCKvQlWU+a+GeAd3C8Rb3L1YnetsgwXzhc3UTWrtQpB/3eCjFOdGUj5QmXfRak12uocd1ZiiQw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-darwin-x64": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.3.tgz",
- "integrity": "sha512-Zxl/TwyXVZPCFSf0u2BNj5sE0F2uR6iSKxWpq4Wlk/Sv9Ob6YCKByQTkV2y6BCic+fkabp9190hyrDdPA/dNrw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-arm64-gnu": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.3.tgz",
- "integrity": "sha512-T5+gg2EwpsY3OoaLxUIofmMb7ohAUlcNZW0fPQ6YAutaWJaxt1Z1h+8zdl4FRIOr5ABAAhXtBcpkZNwUcKI2fw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-arm64-musl": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.3.tgz",
- "integrity": "sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-x64-gnu": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.3.tgz",
- "integrity": "sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-linux-x64-musl": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.3.tgz",
- "integrity": "sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-win32-arm64-msvc": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.3.tgz",
- "integrity": "sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-win32-x64-msvc": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.3.tgz",
- "integrity": "sha512-VNAz+HN4OGgvZs6MOoVfnn41kBzT+M+tB+OK4cww6DNyWS6wKaDpaAm/qLeOUbnMh0oVx1+mg0uoYARF69dJyA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@swc/counter": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
- "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
- "license": "Apache-2.0"
- },
- "node_modules/@swc/helpers": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz",
- "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/busboy": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
- "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
- "dependencies": {
- "streamsearch": "^1.1.0"
- },
- "engines": {
- "node": ">=10.16.0"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001684",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz",
- "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "CC-BY-4.0"
- },
- "node_modules/client-only": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
- "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
- "license": "MIT"
- },
- "node_modules/color": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
- "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-convert": "^2.0.1",
- "color-string": "^1.9.0"
- },
- "engines": {
- "node": ">=12.5.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/color-string": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
- "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
- }
- },
- "node_modules/detect-libc": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
- "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
- "license": "Apache-2.0",
- "optional": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/next": {
- "version": "15.0.3",
- "resolved": "https://registry.npmjs.org/next/-/next-15.0.3.tgz",
- "integrity": "sha512-ontCbCRKJUIoivAdGB34yCaOcPgYXr9AAkV/IwqFfWWTXEPUgLYkSkqBhIk9KK7gGmgjc64B+RdoeIDM13Irnw==",
- "license": "MIT",
- "dependencies": {
- "@next/env": "15.0.3",
- "@swc/counter": "0.1.3",
- "@swc/helpers": "0.5.13",
- "busboy": "1.6.0",
- "caniuse-lite": "^1.0.30001579",
- "postcss": "8.4.31",
- "styled-jsx": "5.1.6"
- },
- "bin": {
- "next": "dist/bin/next"
- },
- "engines": {
- "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
- },
- "optionalDependencies": {
- "@next/swc-darwin-arm64": "15.0.3",
- "@next/swc-darwin-x64": "15.0.3",
- "@next/swc-linux-arm64-gnu": "15.0.3",
- "@next/swc-linux-arm64-musl": "15.0.3",
- "@next/swc-linux-x64-gnu": "15.0.3",
- "@next/swc-linux-x64-musl": "15.0.3",
- "@next/swc-win32-arm64-msvc": "15.0.3",
- "@next/swc-win32-x64-msvc": "15.0.3",
- "sharp": "^0.33.5"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.1.0",
- "@playwright/test": "^1.41.2",
- "babel-plugin-react-compiler": "*",
- "react": "^18.2.0 || 19.0.0-rc-66855b96-20241106",
- "react-dom": "^18.2.0 || 19.0.0-rc-66855b96-20241106",
- "sass": "^1.3.0"
- },
- "peerDependenciesMeta": {
- "@opentelemetry/api": {
- "optional": true
- },
- "@playwright/test": {
- "optional": true
- },
- "babel-plugin-react-compiler": {
- "optional": true
- },
- "sass": {
- "optional": true
- }
- }
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "license": "ISC"
- },
- "node_modules/postcss": {
- "version": "8.4.31",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
- "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.6",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/react": {
- "version": "19.0.0-rc-66855b96-20241106",
- "resolved": "https://registry.npmjs.org/react/-/react-19.0.0-rc-66855b96-20241106.tgz",
- "integrity": "sha512-klH7xkT71SxRCx4hb1hly5FJB21Hz0ACyxbXYAECEqssUjtJeFUAaI2U1DgJAzkGEnvEm3DkxuBchMC/9K4ipg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react-dom": {
- "version": "19.0.0-rc-66855b96-20241106",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0-rc-66855b96-20241106.tgz",
- "integrity": "sha512-D25vdaytZ1wFIRiwNU98NPQ/upS2P8Co4/oNoa02PzHbh8deWdepjm5qwZM/46OdSiGv4WSWwxP55RO9obqJEQ==",
- "license": "MIT",
- "dependencies": {
- "scheduler": "0.25.0-rc-66855b96-20241106"
- },
- "peerDependencies": {
- "react": "19.0.0-rc-66855b96-20241106"
- }
- },
- "node_modules/scheduler": {
- "version": "0.25.0-rc-66855b96-20241106",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-66855b96-20241106.tgz",
- "integrity": "sha512-HQXp/Mnp/MMRSXMQF7urNFla+gmtXW/Gr1KliuR0iboTit4KvZRY8KYaq5ccCTAOJiUqQh2rE2F3wgUekmgdlA==",
- "license": "MIT"
- },
- "node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
- "license": "ISC",
- "optional": true,
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/sharp": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
- "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
- "hasInstallScript": true,
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "color": "^4.2.3",
- "detect-libc": "^2.0.3",
- "semver": "^7.6.3"
- },
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-darwin-arm64": "0.33.5",
- "@img/sharp-darwin-x64": "0.33.5",
- "@img/sharp-libvips-darwin-arm64": "1.0.4",
- "@img/sharp-libvips-darwin-x64": "1.0.4",
- "@img/sharp-libvips-linux-arm": "1.0.5",
- "@img/sharp-libvips-linux-arm64": "1.0.4",
- "@img/sharp-libvips-linux-s390x": "1.0.4",
- "@img/sharp-libvips-linux-x64": "1.0.4",
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
- "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
- "@img/sharp-linux-arm": "0.33.5",
- "@img/sharp-linux-arm64": "0.33.5",
- "@img/sharp-linux-s390x": "0.33.5",
- "@img/sharp-linux-x64": "0.33.5",
- "@img/sharp-linuxmusl-arm64": "0.33.5",
- "@img/sharp-linuxmusl-x64": "0.33.5",
- "@img/sharp-wasm32": "0.33.5",
- "@img/sharp-win32-ia32": "0.33.5",
- "@img/sharp-win32-x64": "0.33.5"
- }
- },
- "node_modules/shell-quote": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
- "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "is-arrayish": "^0.3.1"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/streamsearch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
- "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/styled-jsx": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
- "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
- "license": "MIT",
- "dependencies": {
- "client-only": "0.0.1"
- },
- "engines": {
- "node": ">= 12.0.0"
- },
- "peerDependencies": {
- "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
- },
- "peerDependenciesMeta": {
- "@babel/core": {
- "optional": true
- },
- "babel-plugin-macros": {
- "optional": true
- }
- }
- },
- "node_modules/tslib": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "license": "0BSD"
- }
- }
-}
diff --git a/fixtures/next-15/package.json b/fixtures/next-15/package.json
index ee2e14c..b5e33e1 100644
--- a/fixtures/next-15/package.json
+++ b/fixtures/next-15/package.json
@@ -5,12 +5,13 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "postinstall": "npm link @harperdb/nextjs"
},
"dependencies": {
- "@harperdb/nextjs": "file:/@harperdb/nextjs",
+ "@harperdb/nextjs": "*",
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106",
- "next": "15.0.3"
+ "next": "^15"
}
}
diff --git a/package-lock.json b/package-lock.json
index a749e34..4fbf814 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
},
"devDependencies": {
"@harperdb/code-guidelines": "^0.0.2",
+ "@playwright/test": "^1.49.0",
"prettier": "^3.3.3"
}
},
@@ -29,6 +30,69 @@
"prettier": "3.3.3"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz",
+ "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.49.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/playwright": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz",
+ "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.49.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz",
+ "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/prettier": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
diff --git a/package.json b/package.json
index 5e3a493..ef40864 100644
--- a/package.json
+++ b/package.json
@@ -36,14 +36,15 @@
"format": "prettier .",
"format:check": "npm run format -- --check",
"format:fix": "npm run format -- --write",
- "pretest": "node util/pretest.js",
- "test": "node --test"
+ "pretest": "node util/scripts/pretest.js",
+ "test": "playwright test --workers 3"
},
"dependencies": {
"shell-quote": "^1.8.1"
},
"devDependencies": {
"@harperdb/code-guidelines": "^0.0.2",
+ "@playwright/test": "^1.49.0",
"prettier": "^3.3.3"
},
"prettier": "@harperdb/code-guidelines/prettier"
diff --git a/playwright.config.js b/playwright.config.js
new file mode 100644
index 0000000..8a34c7c
--- /dev/null
+++ b/playwright.config.js
@@ -0,0 +1,22 @@
+import { defineConfig, devices } from '@playwright/test';
+
+const NEXT_MAJORS = ['13', '14', '15'];
+const NODE_MAJORS = ['18', '20', '22'];
+
+export default defineConfig({
+ testDir: 'test',
+ fullyParallel: true,
+ forbidOnly: !!process.env.CI,
+ workers: 3,
+ retries: 2,
+ expect: {
+ timeout: 30000,
+ },
+ projects: NEXT_MAJORS.flatMap((nextMajor) =>
+ NODE_MAJORS.map((nodeMajor) => ({
+ name: `Next.js v${nextMajor} - Node.js v${nodeMajor}`,
+ use: { versions: { nextMajor, nodeMajor }, ...devices['Desktop Chrome'] },
+ testMatch: [`test/next-${nextMajor}.test.js`],
+ }))
+ ),
+});
diff --git a/test/next-13.test.js b/test/next-13.test.js
new file mode 100644
index 0000000..ba46df8
--- /dev/null
+++ b/test/next-13.test.js
@@ -0,0 +1,13 @@
+import { test, expect } from '../util/test-fixture.js';
+
+test.describe.configure({ mode: 'serial' });
+
+test('home page', async ({ nextApp, page }) => {
+ await page.goto(nextApp.rest.toString());
+ await expect(page.locator('h1')).toHaveText('Next.js v13');
+});
+
+test('title', async ({ nextApp, page }) => {
+ await page.goto(nextApp.rest.toString());
+ await expect(page).toHaveTitle('HarperDB - Next.js v13 App');
+});
diff --git a/test/next-14.test.js b/test/next-14.test.js
new file mode 100644
index 0000000..25e590e
--- /dev/null
+++ b/test/next-14.test.js
@@ -0,0 +1,13 @@
+import { test, expect } from '../util/test-fixture.js';
+
+test.describe.configure({ mode: 'serial' });
+
+test('home page', async ({ nextApp, page }) => {
+ await page.goto(nextApp.rest.toString());
+ await expect(page.locator('h1')).toHaveText('Next.js v14');
+});
+
+test('title', async ({ nextApp, page }) => {
+ await page.goto(nextApp.rest.toString());
+ await expect(page).toHaveTitle('HarperDB - Next.js v14 App');
+});
diff --git a/test/next-15-node-18.test.js b/test/next-15-node-18.test.js
deleted file mode 100644
index 3e67c7b..0000000
--- a/test/next-15-node-18.test.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { suite, test, before, after } from 'node:test';
-import { base, next15 } from '../util/tests.js';
-import { Fixture } from '../util/fixture.js';
-
-suite('Next.js v15 - Node.js v18', async () => {
- const ctx = {};
-
- before(async () => {
- ctx.fixture = new Fixture({ nextMajor: '15', nodeMajor: '18' });
- await ctx.fixture.ready;
-
- const restPort = ctx.fixture.portMap.get('9926');
-
- if (!restPort) {
- throw new Error('Rest port not found');
- }
-
- ctx.rest = new URL(`http://${restPort}`);
- });
-
- await Promise.all(base.concat(next15).map(async ({ name, testFunction }) => test(name, (t) => testFunction(t, ctx))));
-
- after(async () => {
- await ctx.fixture.clear();
- });
-});
diff --git a/test/next-15-node-20.test.js b/test/next-15-node-20.test.js
deleted file mode 100644
index 36ee7ac..0000000
--- a/test/next-15-node-20.test.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { suite, test, before, after } from 'node:test';
-import { base, next15 } from '../util/tests.js';
-import { Fixture } from '../util/fixture.js';
-
-suite('Next.js v15 - Node.js v20', async () => {
- const ctx = {};
-
- before(async () => {
- ctx.fixture = new Fixture({ nextMajor: '15', nodeMajor: '20' });
- await ctx.fixture.ready;
- ctx.rest = new URL(`http://${ctx.fixture.portMap.get('9926')}`);
- });
-
- await Promise.all(base.concat(next15).map(async ({ name, testFunction }) => test(name, (t) => testFunction(t, ctx))));
-
- after(async () => {
- await ctx.fixture.clear();
- });
-});
diff --git a/test/next-15-node-22.test.js b/test/next-15-node-22.test.js
deleted file mode 100644
index 8304ac6..0000000
--- a/test/next-15-node-22.test.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { suite, test, before, after } from 'node:test';
-import { base, next15 } from '../util/tests.js';
-import { Fixture } from '../util/fixture.js';
-
-suite('Next.js v15 - Node.js v22', async () => {
- const ctx = {};
-
- before(async () => {
- ctx.fixture = new Fixture({ nextMajor: '15', nodeMajor: '22' });
- await ctx.fixture.ready;
- ctx.rest = new URL(`http://${ctx.fixture.portMap.get('9926')}`);
- });
-
- await Promise.all(base.concat(next15).map(async ({ name, testFunction }) => test(name, (t) => testFunction(t, ctx))));
-
- after(async () => {
- await ctx.fixture.clear();
- });
-});
diff --git a/test/next-15.test.js b/test/next-15.test.js
new file mode 100644
index 0000000..bc985e3
--- /dev/null
+++ b/test/next-15.test.js
@@ -0,0 +1,13 @@
+import { test, expect } from '../util/test-fixture.js';
+
+test.describe.configure({ mode: 'serial' });
+
+test('home page', async ({ nextApp, page }) => {
+ await page.goto(nextApp.rest.toString());
+ await expect(page.locator('h1')).toHaveText('Next.js v15');
+});
+
+test('title', async ({ nextApp, page }) => {
+ await page.goto(nextApp.rest.toString());
+ await expect(page).toHaveTitle('HarperDB - Next.js v15 App');
+});
diff --git a/util/cache-bust.js b/util/cache-bust.js
index 3244b50..75c5edd 100644
--- a/util/cache-bust.js
+++ b/util/cache-bust.js
@@ -1,15 +1,33 @@
-import { spawn } from 'child_process';
-import { createHash } from 'crypto';
-import { join } from 'path';
-import { pipeline } from 'stream/promises';
+import { spawn } from 'node:child_process';
+import { createHash } from 'node:crypto';
+import { join } from 'node:path';
+import { pipeline } from 'node:stream/promises';
+import { readdirSync } from 'node:fs';
-export function getCacheBustValue() {
+import { ROOT } from './constants-and-names.js';
+
+export function getCacheBustValue(files) {
return new Promise((resolve, reject) => {
- const proc = spawn('git', ['status', '--porcelain'], { cwd: join(import.meta.dirname, '..') })
+ const proc = spawn('git', ['status', '--porcelain', ...files], { cwd: ROOT });
+
proc.on('error', reject);
+
const hash = createHash('sha1');
- pipeline(proc.stdout, hash).then(() => resolve(hash.digest('hex'))).catch(reject);
+
+ pipeline(proc.stdout, hash)
+ .then(() => resolve(hash.digest('hex')))
+ .catch(reject);
});
}
-export const CACHE_BUST = getCacheBustValue();
+export const MODULE_CACHE_BUST = getCacheBustValue([
+ 'config.yaml',
+ 'cli.js',
+ 'extension.js',
+ 'schema.graphql',
+ 'package.json',
+]);
+
+export function getNextFixtureCacheBustValue(nextMajor) {
+ return getCacheBustValue(readdirSync(join(ROOT, 'fixtures', `next-${nextMajor}`)));
+}
diff --git a/util/constants-and-names.js b/util/constants-and-names.js
new file mode 100644
index 0000000..5d4d00b
--- /dev/null
+++ b/util/constants-and-names.js
@@ -0,0 +1,23 @@
+import { join } from 'node:path';
+
+export const ROOT = join(import.meta.dirname, '..');
+
+export const NEXT_MAJORS = ['13', '14', '15'];
+
+export const NODE_MAJORS = ['18', '20', '22'];
+
+export const PORTS = ['9925', '9926'];
+
+export const CONTAINER_ENGINE_LIST = ['podman', 'docker'];
+
+export function getNodeBaseImageName(nodeMajor) {
+ return `harperdb-nextjs/node-base-${nodeMajor}`;
+}
+
+export function getNextImageName(nextMajor, nodeMajor) {
+ return `harperdb-nextjs/test-image-next-${nextMajor}-node-${nodeMajor}`;
+}
+
+export function getNextContainerName(nextMajor, nodeMajor) {
+ return `harperdb-nextjs-test-container-next-${nextMajor}-node-${nodeMajor}`;
+}
diff --git a/util/get-container-engine.js b/util/container-engine.js
similarity index 64%
rename from util/get-container-engine.js
rename to util/container-engine.js
index 0708692..185f5ec 100644
--- a/util/get-container-engine.js
+++ b/util/container-engine.js
@@ -1,6 +1,6 @@
-import { spawnSync } from 'child_process';
+import { spawnSync } from 'node:child_process';
-const CONTAINER_ENGINE_LIST = ['podman', 'docker'];
+import { CONTAINER_ENGINE_LIST } from './constants-and-names.js';
export function getContainerEngine() {
for (const engine of CONTAINER_ENGINE_LIST) {
@@ -13,4 +13,4 @@ export function getContainerEngine() {
throw new Error(`No container engine found in ${CONTAINER_ENGINE_LIST.join(', ')}`);
}
-export const containerEngine = getContainerEngine();
+export const CONTAINER_ENGINE = getContainerEngine();
diff --git a/util/base.dockerfile b/util/docker/base.dockerfile
similarity index 100%
rename from util/base.dockerfile
rename to util/docker/base.dockerfile
diff --git a/util/next.dockerfile b/util/docker/next.dockerfile
similarity index 52%
rename from util/next.dockerfile
rename to util/docker/next.dockerfile
index 0e41386..188d49b 100644
--- a/util/next.dockerfile
+++ b/util/docker/next.dockerfile
@@ -7,9 +7,11 @@ FROM ${BASE_IMAGE}
ARG NEXT_MAJOR
-COPY fixtures/next-${NEXT_MAJOR} /hdb/components/next-${NEXT_MAJOR}
+ARG CACHE_BUST
+RUN echo "${CACHE_BUST}"
+COPY --exclude=node_modules --exclude=.next fixtures/next-${NEXT_MAJOR} /hdb/components/next-${NEXT_MAJOR}
-RUN npm install -C hdb/components/next-${NEXT_MAJOR}
+RUN cd hdb/components/next-${NEXT_MAJOR} && npm install
EXPOSE 9925 9926
diff --git a/util/fixture.js b/util/fixture.js
index 414da2e..87ecf5a 100644
--- a/util/fixture.js
+++ b/util/fixture.js
@@ -1,17 +1,26 @@
-import child_process from 'node:child_process';
+import { spawn, spawnSync } from 'node:child_process';
import { Transform } from 'node:stream';
-import { getNextImageName } from './get-next-image-name.js';
-import { containerEngine } from './get-container-engine.js';
+
+import { getNextImageName, getNextContainerName, NEXT_MAJORS, NODE_MAJORS, PORTS } from './constants-and-names.js';
+import { CONTAINER_ENGINE } from './container-engine.js';
+import { CollectedTransform } from './collected-transform.js';
export class Fixture {
constructor({ autoSetup = true, debug = false, nextMajor, nodeMajor }) {
+ if (!NEXT_MAJORS.includes(nextMajor)) {
+ throw new Error(`nextMajor must be one of ${NEXT_MAJORS.join(', ')}`);
+ }
this.nextMajor = nextMajor;
+
+ if (!NODE_MAJORS.includes(nodeMajor)) {
+ throw new Error(`nodeMajor must be one of ${NODE_MAJORS.join(', ')}`);
+ }
this.nodeMajor = nodeMajor;
this.debug = debug || process.env.DEBUG === '1';
this.imageName = getNextImageName(nextMajor, nodeMajor);
- this.containerName = `hdb-next-integration-test-container-next-${nextMajor}-node-${nodeMajor}`;
+ this.containerName = getNextContainerName(nextMajor, nodeMajor);
if (autoSetup) {
this.ready = this.clear().then(() => this.run());
@@ -24,7 +33,7 @@ export class Fixture {
#runCommand(args = [], options = {}) {
return new Promise((resolve, reject) => {
- const childProcess = child_process.spawn(containerEngine, args, {
+ const childProcess = spawn(CONTAINER_ENGINE, args, {
stdio: this.#stdio,
...options,
});
@@ -44,22 +53,11 @@ export class Fixture {
clear() {
return new Promise((resolve, reject) => {
- const psProcess = child_process.spawn(containerEngine, ['ps', '-aq', '-f', `name=${this.containerName}`]);
+ const psProcess = spawn(CONTAINER_ENGINE, ['ps', '-aq', '-f', `name=${this.containerName}`]);
psProcess.on('error', reject);
- const collectedStdout = psProcess.stdout.pipe(
- new Transform({
- construct(callback) {
- this.chunks = [];
- callback(null);
- },
- transform(chunk, encoding, callback) {
- this.chunks.push(chunk);
- callback(null, chunk);
- },
- })
- );
+ const collectedStdout = psProcess.stdout.pipe(new CollectedTransform());
if (this.debug) {
collectedStdout.pipe(process.stdout);
@@ -67,7 +65,7 @@ export class Fixture {
}
psProcess.on('exit', (code) => {
- if (code === 0 && collectedStdout.chunks.length !== 0) {
+ if (code === 0 && collectedStdout.output !== '') {
return this.stop()
.then(() => this.rm())
.then(resolve, reject);
@@ -79,11 +77,9 @@ export class Fixture {
run() {
return new Promise((resolve, reject) => {
- const runProcess = child_process.spawn(
- containerEngine,
- ['run', '-P', '--name', this.containerName, this.imageName],
- { stdio: ['ignore', 'pipe', this.debug ? 'inherit' : 'ignore'] }
- );
+ const runProcess = spawn(CONTAINER_ENGINE, ['run', '-P', '--name', this.containerName, this.imageName], {
+ stdio: ['ignore', 'pipe', this.debug ? 'inherit' : 'ignore'],
+ });
runProcess.on('error', reject);
runProcess.on('exit', resolve);
@@ -107,8 +103,8 @@ export class Fixture {
get portMap() {
const portMap = new Map();
- for (const port of ['9925', '9926']) {
- const { stdout } = child_process.spawnSync(containerEngine, ['port', this.containerName, port]);
+ for (const port of PORTS) {
+ const { stdout } = spawnSync(CONTAINER_ENGINE, ['port', this.containerName, port]);
portMap.set(port, stdout.toString().trim());
}
return portMap;
diff --git a/util/get-next-image-name.js b/util/get-next-image-name.js
deleted file mode 100644
index 0845860..0000000
--- a/util/get-next-image-name.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export function getNextImageName(nextMajor, nodeMajor) {
- return `harperdb-nextjs/test-image-next-${nextMajor}-node-${nodeMajor}`;
-}
diff --git a/util/pretest.js b/util/scripts/pretest.js
similarity index 67%
rename from util/pretest.js
rename to util/scripts/pretest.js
index 38db212..07d0139 100644
--- a/util/pretest.js
+++ b/util/scripts/pretest.js
@@ -1,16 +1,12 @@
import { spawn } from 'node:child_process';
import { join } from 'node:path';
-import { containerEngine } from './get-container-engine.js';
-import { getNextImageName } from './get-next-image-name.js';
-import { CollectedTransform } from './collected-transform.js';
-import { CACHE_BUST } from './cache-bust.js';
-const DEBUG = process.env.DEBUG === '1';
-
-const NODE_MAJORS = ['18', '20', '22'];
-const NEXT_MAJORS = ['13', '14', '15'];
+import { CONTAINER_ENGINE } from '../container-engine.js';
+import { CollectedTransform } from '../collected-transform.js';
+import { MODULE_CACHE_BUST, getNextFixtureCacheBustValue } from '../cache-bust.js';
+import { NODE_MAJORS, NEXT_MAJORS, ROOT, getNodeBaseImageName, getNextImageName } from '../constants-and-names.js';
-const getNodeBaseImageName = (nodeMajor) => `harperdb-nextjs/node-base-${nodeMajor}`;
+const DEBUG = process.env.DEBUG === '1';
function validateResult(result) {
const success = result.code === 0;
@@ -28,12 +24,12 @@ function validateResult(result) {
function build(name, args, options = {}) {
return new Promise((resolve, reject) => {
- const buildProcess = spawn(containerEngine, args, { stdio: ['ignore', 'pipe', 'pipe'], ...options });
+ const buildProcess = spawn(CONTAINER_ENGINE, args, { cwd: ROOT, stdio: ['ignore', 'pipe', 'pipe'], ...options });
const collectedStdout = buildProcess.stdout.pipe(new CollectedTransform());
const collectedStderr = buildProcess.stderr.pipe(new CollectedTransform());
- buildProcess.on('error', (error) => reject(error));
+ buildProcess.on('error', reject);
buildProcess.on('close', (code) =>
resolve({
name,
@@ -45,19 +41,19 @@ function build(name, args, options = {}) {
});
}
-// Build Node Base Images
+// Build Node.js Base Images
for (const nodeMajor of NODE_MAJORS) {
const buildResult = await build(getNodeBaseImageName(nodeMajor), [
'build',
'--build-arg',
`NODE_MAJOR=${nodeMajor}`,
'--build-arg',
- `CACHE_BUST=${CACHE_BUST}`,
+ `CACHE_BUST=${MODULE_CACHE_BUST}`,
'-t',
getNodeBaseImageName(nodeMajor),
'-f',
- join(import.meta.dirname, 'base.dockerfile'),
- join(import.meta.dirname, '..'),
+ join(ROOT, 'util', 'docker', 'base.dockerfile'),
+ ROOT,
]);
validateResult(buildResult);
@@ -73,11 +69,13 @@ for (const nextMajor of NEXT_MAJORS) {
`BASE_IMAGE=${getNodeBaseImageName(nodeMajor)}`,
'--build-arg',
`NEXT_MAJOR=${nextMajor}`,
+ '--build-arg',
+ `CACHE_BUST=${getNextFixtureCacheBustValue(nextMajor)}`,
'-t',
getNextImageName(nextMajor, nodeMajor),
'-f',
- join(import.meta.dirname, 'next.dockerfile'),
- join(import.meta.dirname, '..'),
+ join(ROOT, 'util', 'docker', 'next.dockerfile'),
+ ROOT,
]);
validateResult(buildResult);
diff --git a/util/test-fixture.js b/util/test-fixture.js
new file mode 100644
index 0000000..e95765b
--- /dev/null
+++ b/util/test-fixture.js
@@ -0,0 +1,29 @@
+import { test as base } from '@playwright/test';
+
+import { Fixture } from './fixture';
+
+export const test = base.extend({
+ versions: [
+ { nextMajor: '', nodeMajor: '' },
+ { option: true, scope: 'worker' },
+ ],
+ nextApp: [
+ async ({ versions: { nextMajor, nodeMajor } }, use) => {
+ const fixture = new Fixture({ nextMajor, nodeMajor });
+ await fixture.ready;
+
+ const rest = fixture.portMap.get('9926');
+
+ if (!rest) {
+ throw new Error('Rest port not found');
+ }
+
+ await use({ rest: `http://${rest}` });
+
+ await fixture.clear();
+ },
+ { scope: 'worker', timeout: 60000 },
+ ],
+});
+
+export { expect } from '@playwright/test';
diff --git a/util/tests.js b/util/tests.js
deleted file mode 100644
index 7335be1..0000000
--- a/util/tests.js
+++ /dev/null
@@ -1,32 +0,0 @@
-export const base = [
- {
- name: 'should enable /Dog rest endpoint',
- testFunction: async (t, ctx) => {
- const response = await fetch(`${ctx.rest}/Dog/0`, {
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': 'Basic aGRiX2FkbWluOnBhc3N3b3Jk',
- },
- });
- const json = await response.json();
-
- t.assert.deepStrictEqual(json, { id: '0', name: 'Lincoln', breed: 'Shepherd' });
- },
- },
-];
-
-export const next15 = [
- {
- name: 'should render home page',
- testFunction: async (t, ctx) => {
- const response = await fetch(`${ctx.rest}/`, {
- headers: {
- 'Content-Type': 'text/html',
- },
- });
-
- const text = await response.text();
- t.assert.match(text, /Next\.js v15/);
- },
- },
-];