diff --git a/.circleci/config.yml b/.circleci/config.yml
index aeabb4948f..6946443152 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -33,7 +33,7 @@ references:
defaults: &defaults
working_directory: ~/instantsearch
docker:
- - image: cimg/node:14.18.0
+ - image: cimg/node:16.14.0
workflows:
version: 2
@@ -163,6 +163,7 @@ jobs:
- packages/react-instantsearch/dist
- packages/react-instantsearch-core/dist
- packages/react-instantsearch-router-nextjs/dist
+ - packages/react-instantsearch-nextjs/dist
- packages/vue-instantsearch/vue2
- packages/vue-instantsearch/vue3
diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json
index b6190875f7..d7c063aae8 100644
--- a/.codesandbox/ci.json
+++ b/.codesandbox/ci.json
@@ -3,6 +3,7 @@
"instantsearchjs-es-template-pcw1k",
"github/algolia/instantsearch/tree/templates/react-instantsearch",
"/examples/react/default-theme",
+ "/examples/react/next-app-router",
"/examples/vue/default-theme"
],
"buildCommand": "build --no-private --ignore *-maps --ignore *-native",
@@ -10,8 +11,9 @@
"packages/instantsearch.js",
"packages/react-instantsearch",
"packages/react-instantsearch-core",
+ "packages/react-instantsearch-nextjs",
"packages/vue-instantsearch",
"packages/instantsearch.css"
],
- "node": "14"
+ "node": "16"
}
diff --git a/.nvmrc b/.nvmrc
index c2324e8e46..832d385064 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-14.18.0
+16.14.0
diff --git a/babel.config.js b/babel.config.js
index 114bb8d518..facc811fe7 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -51,6 +51,8 @@ module.exports = (api) => {
'react-dom',
// `use-sync-external-store` also fails if the paths are incomplete
'use-sync-external-store',
+ // `next` imports as peer dependencies fail if paths are incomplete
+ 'next',
],
},
],
diff --git a/bundlesize.config.json b/bundlesize.config.json
index 1013168aeb..9fdfad3fcb 100644
--- a/bundlesize.config.json
+++ b/bundlesize.config.json
@@ -22,7 +22,7 @@
},
{
"path": "packages/react-instantsearch/dist/umd/ReactInstantSearch.min.js",
- "maxSize": "56 kB"
+ "maxSize": "56.25 kB"
},
{
"path": "packages/vue-instantsearch/vue2/umd/index.js",
diff --git a/examples/react/default-theme/package.json b/examples/react/default-theme/package.json
index ee909b0584..da749d603b 100644
--- a/examples/react/default-theme/package.json
+++ b/examples/react/default-theme/package.json
@@ -9,8 +9,8 @@
"dependencies": {
"algoliasearch": "4.14.3",
"instantsearch.js": "4.56.11",
- "react": "18.1.0",
- "react-dom": "18.1.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
"react-instantsearch": "7.0.3"
},
"devDependencies": {
diff --git a/examples/react/e-commerce/package.json b/examples/react/e-commerce/package.json
index 78d11ecbb7..fbec8051b2 100644
--- a/examples/react/e-commerce/package.json
+++ b/examples/react/e-commerce/package.json
@@ -11,9 +11,9 @@
"dependencies": {
"algoliasearch": "4.14.3",
"instantsearch.js": "4.56.11",
- "react": "18.1.0",
+ "react": "18.2.0",
"react-compound-slider": "3.4.0",
- "react-dom": "18.1.0",
+ "react-dom": "18.2.0",
"react-instantsearch": "7.0.3"
},
"devDependencies": {
diff --git a/examples/react/getting-started/package.json b/examples/react/getting-started/package.json
index 4b0f449d70..507ba9964a 100644
--- a/examples/react/getting-started/package.json
+++ b/examples/react/getting-started/package.json
@@ -9,8 +9,8 @@
"dependencies": {
"algoliasearch": "4.14.3",
"instantsearch.js": "4.56.11",
- "react": "18.1.0",
- "react-dom": "18.1.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
"react-instantsearch": "7.0.3"
},
"devDependencies": {
diff --git a/examples/react/next-app-router/.eslintrc b/examples/react/next-app-router/.eslintrc
new file mode 100644
index 0000000000..e6c614fc86
--- /dev/null
+++ b/examples/react/next-app-router/.eslintrc
@@ -0,0 +1,9 @@
+{
+ "extends": ["plugin:@next/next/recommended"],
+ "rules": {
+ // This rule is not able to find the `pages/` folder in the monorepo.
+ "@next/next/no-html-link-for-pages": ["off"],
+ "react/react-in-jsx-scope": "off",
+ "spaced-comment": ["error", "always", { "markers": ["/"] }]
+ }
+}
diff --git a/examples/react/next-app-router/.gitignore b/examples/react/next-app-router/.gitignore
new file mode 100644
index 0000000000..1437c53f70
--- /dev/null
+++ b/examples/react/next-app-router/.gitignore
@@ -0,0 +1,34 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# vercel
+.vercel
diff --git a/examples/react/next-app-router/README.md b/examples/react/next-app-router/README.md
new file mode 100644
index 0000000000..f6a99da12c
--- /dev/null
+++ b/examples/react/next-app-router/README.md
@@ -0,0 +1,18 @@
+This example shows how to do server side rendering with next.js and React InstantSearch. There's a live example here: https://codesandbox.io/s/github/algolia/instantsearch.js/tree/master/examples/react/next.
+
+[![Edit next](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/next)
+
+## Clone the example
+
+```sh
+curl https://codeload.github.com/algolia/instantsearch/tar.gz/master | tar -xz --strip=3 instantsearch-master/examples/react/next
+```
+
+## Start the example
+
+```sh
+yarn install --no-lockfile
+yarn run dev
+```
+
+Read more about React InstantSearch [in our documentation](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/).
diff --git a/examples/react/next-app-router/app/Search.tsx b/examples/react/next-app-router/app/Search.tsx
new file mode 100644
index 0000000000..122275d840
--- /dev/null
+++ b/examples/react/next-app-router/app/Search.tsx
@@ -0,0 +1,57 @@
+'use client';
+
+import algoliasearch from 'algoliasearch/lite';
+import { Hit as AlgoliaHit } from 'instantsearch.js';
+import React from 'react';
+import {
+ Hits,
+ Highlight,
+ SearchBox,
+ RefinementList,
+ DynamicWidgets,
+} from 'react-instantsearch';
+import { InstantSearchNext } from 'react-instantsearch-nextjs';
+
+import { Panel } from '../components/Panel';
+
+const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
+
+type HitProps = {
+ hit: AlgoliaHit<{
+ name: string;
+ price: number;
+ }>;
+};
+
+function Hit({ hit }: HitProps) {
+ return (
+ <>
+
+ ${hit.price}
+ >
+ );
+}
+
+export default function Search() {
+ return (
+
+
+
+ );
+}
+
+function FallbackComponent({ attribute }: { attribute: string }) {
+ return (
+
+
+
+ );
+}
diff --git a/examples/react/next-app-router/app/favicon.ico b/examples/react/next-app-router/app/favicon.ico
new file mode 100644
index 0000000000..718d6fea48
Binary files /dev/null and b/examples/react/next-app-router/app/favicon.ico differ
diff --git a/examples/react/next-app-router/app/globals.css b/examples/react/next-app-router/app/globals.css
new file mode 100644
index 0000000000..0ab8aeba8c
--- /dev/null
+++ b/examples/react/next-app-router/app/globals.css
@@ -0,0 +1,22 @@
+html,
+body {
+ padding: 0;
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
+ Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
+}
+
+body {
+ padding: 0.5rem;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+.Container {
+ display: grid;
+ align-items: flex-start;
+ grid-template-columns: minmax(min-content, 200px) 1fr;
+ gap: 0.5rem;
+}
diff --git a/examples/react/next-app-router/app/layout.tsx b/examples/react/next-app-router/app/layout.tsx
new file mode 100644
index 0000000000..b235b96b60
--- /dev/null
+++ b/examples/react/next-app-router/app/layout.tsx
@@ -0,0 +1,20 @@
+import './globals.css';
+import 'instantsearch.css/themes/satellite-min.css';
+import React from 'react';
+
+export const metadata = {
+ title: 'Next.js',
+ description: 'Generated by Next.js',
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/examples/react/next-app-router/app/page.module.css b/examples/react/next-app-router/app/page.module.css
new file mode 100644
index 0000000000..9411a5e6f2
--- /dev/null
+++ b/examples/react/next-app-router/app/page.module.css
@@ -0,0 +1,229 @@
+.main {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: center;
+ padding: 6rem;
+ min-height: 100vh;
+}
+
+.description {
+ display: inherit;
+ justify-content: inherit;
+ align-items: inherit;
+ font-size: 0.85rem;
+ max-width: var(--max-width);
+ width: 100%;
+ z-index: 2;
+ font-family: var(--font-mono);
+}
+
+.description a {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.description p {
+ position: relative;
+ margin: 0;
+ padding: 1rem;
+ background-color: rgba(var(--callout-rgb), 0.5);
+ border: 1px solid rgba(var(--callout-border-rgb), 0.3);
+ border-radius: var(--border-radius);
+}
+
+.code {
+ font-weight: 700;
+ font-family: var(--font-mono);
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(25%, auto));
+ width: var(--max-width);
+ max-width: 100%;
+}
+
+.card {
+ padding: 1rem 1.2rem;
+ border-radius: var(--border-radius);
+ background: rgba(var(--card-rgb), 0);
+ border: 1px solid rgba(var(--card-border-rgb), 0);
+ transition: background 200ms, border 200ms;
+}
+
+.card span {
+ display: inline-block;
+ transition: transform 200ms;
+}
+
+.card h2 {
+ font-weight: 600;
+ margin-bottom: 0.7rem;
+}
+
+.card p {
+ margin: 0;
+ opacity: 0.6;
+ font-size: 0.9rem;
+ line-height: 1.5;
+ max-width: 30ch;
+}
+
+.center {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ padding: 4rem 0;
+}
+
+.center::before {
+ background: var(--secondary-glow);
+ border-radius: 50%;
+ width: 480px;
+ height: 360px;
+ margin-left: -400px;
+}
+
+.center::after {
+ background: var(--primary-glow);
+ width: 240px;
+ height: 180px;
+ z-index: -1;
+}
+
+.center::before,
+.center::after {
+ content: '';
+ left: 50%;
+ position: absolute;
+ filter: blur(45px);
+ transform: translateZ(0);
+}
+
+.logo {
+ position: relative;
+}
+/* Enable hover only on non-touch devices */
+@media (hover: hover) and (pointer: fine) {
+ .card:hover {
+ background: rgba(var(--card-rgb), 0.1);
+ border: 1px solid rgba(var(--card-border-rgb), 0.15);
+ }
+
+ .card:hover span {
+ transform: translateX(4px);
+ }
+}
+
+@media (prefers-reduced-motion) {
+ .card:hover span {
+ transform: none;
+ }
+}
+
+/* Mobile */
+@media (max-width: 700px) {
+ .content {
+ padding: 4rem;
+ }
+
+ .grid {
+ grid-template-columns: 1fr;
+ margin-bottom: 120px;
+ max-width: 320px;
+ text-align: center;
+ }
+
+ .card {
+ padding: 1rem 2.5rem;
+ }
+
+ .card h2 {
+ margin-bottom: 0.5rem;
+ }
+
+ .center {
+ padding: 8rem 0 6rem;
+ }
+
+ .center::before {
+ transform: none;
+ height: 300px;
+ }
+
+ .description {
+ font-size: 0.8rem;
+ }
+
+ .description a {
+ padding: 1rem;
+ }
+
+ .description p,
+ .description div {
+ display: flex;
+ justify-content: center;
+ position: fixed;
+ width: 100%;
+ }
+
+ .description p {
+ align-items: center;
+ inset: 0 0 auto;
+ padding: 2rem 1rem 1.4rem;
+ border-radius: 0;
+ border: none;
+ border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
+ background: linear-gradient(
+ to bottom,
+ rgba(var(--background-start-rgb), 1),
+ rgba(var(--callout-rgb), 0.5)
+ );
+ background-clip: padding-box;
+ backdrop-filter: blur(24px);
+ }
+
+ .description div {
+ align-items: flex-end;
+ pointer-events: none;
+ inset: auto 0 0;
+ padding: 2rem;
+ height: 200px;
+ background: linear-gradient(
+ to bottom,
+ transparent 0%,
+ rgb(var(--background-end-rgb)) 40%
+ );
+ z-index: 1;
+ }
+}
+
+/* Tablet and Smaller Desktop */
+@media (min-width: 701px) and (max-width: 1120px) {
+ .grid {
+ grid-template-columns: repeat(2, 50%);
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ .vercelLogo {
+ filter: invert(1);
+ }
+
+ .logo {
+ filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
+ }
+}
+
+@keyframes rotate {
+ from {
+ transform: rotate(360deg);
+ }
+ to {
+ transform: rotate(0deg);
+ }
+}
diff --git a/examples/react/next-app-router/app/page.tsx b/examples/react/next-app-router/app/page.tsx
new file mode 100644
index 0000000000..3bdc8880e4
--- /dev/null
+++ b/examples/react/next-app-router/app/page.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import Search from './Search';
+
+export const dynamic = 'force-dynamic';
+
+export default function Page() {
+ return ;
+}
diff --git a/examples/react/next-app-router/components/Panel.tsx b/examples/react/next-app-router/components/Panel.tsx
new file mode 100644
index 0000000000..a33975f38b
--- /dev/null
+++ b/examples/react/next-app-router/components/Panel.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+
+export function Panel({
+ children,
+ header,
+ footer,
+}: {
+ children: React.ReactNode;
+ header?: React.ReactNode;
+ footer?: React.ReactNode;
+}) {
+ return (
+
+ {header &&
{header}
}
+
{children}
+ {footer &&
{footer}
}
+
+ );
+}
diff --git a/examples/react/next-app-router/next-env.d.ts b/examples/react/next-app-router/next-env.d.ts
new file mode 100644
index 0000000000..4f11a03dc6
--- /dev/null
+++ b/examples/react/next-app-router/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/examples/react/next-app-router/package.json b/examples/react/next-app-router/package.json
new file mode 100644
index 0000000000..6d9a97f059
--- /dev/null
+++ b/examples/react/next-app-router/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "example-react-instantsearch-next-app-dir-example",
+ "version": "8.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "algoliasearch": "4.14.3",
+ "instantsearch.css": "8.0.0",
+ "next": "13.4.19",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "react-instantsearch": "7.0.3",
+ "react-instantsearch-router-nextjs": "7.0.3",
+ "react-instantsearch-nextjs": "0.0.1"
+ },
+ "devDependencies": {
+ "@types/node": "17.0.40",
+ "@types/react": "18.0.12",
+ "eslint": "8.4.0",
+ "eslint-config-next": "12.0.7",
+ "typescript": "5.1.3"
+ }
+}
diff --git a/examples/react/next-app-router/tsconfig.json b/examples/react/next-app-router/tsconfig.json
new file mode 100644
index 0000000000..4c773fef2a
--- /dev/null
+++ b/examples/react/next-app-router/tsconfig.json
@@ -0,0 +1,36 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "incremental": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/examples/react/next-routing/package.json b/examples/react/next-routing/package.json
index 63c6412898..ddbfc15715 100644
--- a/examples/react/next-routing/package.json
+++ b/examples/react/next-routing/package.json
@@ -11,9 +11,9 @@
"dependencies": {
"algoliasearch": "4.14.3",
"instantsearch.css": "8.0.0",
- "next": "12.1.6",
- "react": "18.1.0",
- "react-dom": "18.1.0",
+ "next": "13.4.19",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
"react-instantsearch": "7.0.3",
"react-instantsearch-router-nextjs": "7.0.3"
},
diff --git a/examples/react/next/package.json b/examples/react/next/package.json
index 47d9983f91..1ebbc8350b 100644
--- a/examples/react/next/package.json
+++ b/examples/react/next/package.json
@@ -11,9 +11,9 @@
"dependencies": {
"algoliasearch": "4.14.3",
"instantsearch.css": "8.0.0",
- "next": "12.1.6",
- "react": "18.1.0",
- "react-dom": "18.1.0",
+ "next": "13.4.19",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
"react-instantsearch": "7.0.3",
"react-instantsearch-router-nextjs": "7.0.3"
},
diff --git a/examples/react/next/tsconfig.json b/examples/react/next/tsconfig.json
index c4ab2fbfd5..4c773fef2a 100644
--- a/examples/react/next/tsconfig.json
+++ b/examples/react/next/tsconfig.json
@@ -17,12 +17,18 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
- "jsx": "preserve"
+ "jsx": "preserve",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
},
"include": [
"next-env.d.ts",
"**/*.ts",
- "**/*.tsx"
+ "**/*.tsx",
+ ".next/types/**/*.ts"
],
"exclude": [
"node_modules"
diff --git a/examples/react/ssr/package.json b/examples/react/ssr/package.json
index 95d15309cc..8da280acbd 100644
--- a/examples/react/ssr/package.json
+++ b/examples/react/ssr/package.json
@@ -24,8 +24,8 @@
"dependencies": {
"algoliasearch": "4.14.3",
"express": "4.17.1",
- "react": "18.1.0",
- "react-dom": "18.1.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
"react-instantsearch": "7.0.3"
}
}
diff --git a/jest.config.js b/jest.config.js
index c0df242a83..054364825b 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -15,6 +15,7 @@ const config = {
'/packages/algoliasearch-helper',
'/packages/create-instantsearch-app',
'/packages/react-instantsearch-router-nextjs',
+ '/packages/react-instantsearch-nextjs',
'/__utils__/',
],
watchPathIgnorePatterns: [
diff --git a/package.json b/package.json
index 3ff1a87cb1..45a8dacfc2 100644
--- a/package.json
+++ b/package.json
@@ -125,9 +125,9 @@
"places.js": "1.17.1",
"prettier": "^2.4.1",
"prop-types": "15.6.2",
- "react": "18.1.0",
- "react-dom": "18.1.0",
- "react-test-renderer": "18.1.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "react-test-renderer": "18.2.0",
"rheostat": "2.2.0",
"rollup": "1.29.1",
"rollup-plugin-babel": "4.3.3",
diff --git a/packages/instantsearch.js/src/lib/routers/history.ts b/packages/instantsearch.js/src/lib/routers/history.ts
index f0d2819445..5d887ccb08 100644
--- a/packages/instantsearch.js/src/lib/routers/history.ts
+++ b/packages/instantsearch.js/src/lib/routers/history.ts
@@ -86,7 +86,7 @@ class BrowserHistory implements Router {
/**
* Indicates whether the history router is disposed or not.
*/
- private isDisposed: boolean = false;
+ protected isDisposed: boolean = false;
/**
* Indicates the window.history.length before the last call to
diff --git a/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx
index 79b8456f7b..d15e72237f 100644
--- a/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx
+++ b/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx
@@ -607,7 +607,7 @@ describe('InstantSearch', () => {
}),
},
]);
- expect(warn).toHaveBeenCalledTimes(2);
+ expect(warn).toHaveBeenCalledTimes(4);
});
});
@@ -653,7 +653,7 @@ describe('InstantSearch', () => {
rerender();
- expect(warn).not.toHaveBeenCalled();
+ expect(warn).not.toHaveBeenCalledWith(false, expect.any(String));
});
test('updates the index on index prop change', async () => {
@@ -939,7 +939,7 @@ describe('InstantSearch', () => {
);
- expect(warn).toHaveBeenLastCalledWith(false, expect.any(String));
+ expect(warn).toHaveBeenCalledWith(true, expect.any(String));
});
test('does not warn when using Next.js with routing with _isNextRouter', () => {
@@ -965,7 +965,7 @@ describe('InstantSearch', () => {
);
- expect(warn).toHaveBeenLastCalledWith(true, expect.any(String));
+ expect(warn).not.toHaveBeenCalledWith(false, expect.any(String));
});
test('does not warn when using Next.js without routing', () => {
@@ -980,7 +980,7 @@ describe('InstantSearch', () => {
);
- expect(warn).not.toHaveBeenCalled();
+ expect(warn).not.toHaveBeenCalledWith(false, expect.any(String));
});
test('does not warn when not using Next.js', () => {
@@ -998,7 +998,7 @@ describe('InstantSearch', () => {
);
- expect(warn).not.toHaveBeenCalled();
+ expect(warn).not.toHaveBeenCalledWith(false, expect.any(String));
});
});
});
diff --git a/packages/react-instantsearch-core/src/index.ts b/packages/react-instantsearch-core/src/index.ts
index 67f24e3b87..1f89e8b6c8 100644
--- a/packages/react-instantsearch-core/src/index.ts
+++ b/packages/react-instantsearch-core/src/index.ts
@@ -28,4 +28,8 @@ export * from './connectors/useStats';
export * from './connectors/useToggleRefinement';
export * from './hooks/useConnector';
export * from './hooks/useInstantSearch';
+export * from './lib/wrapPromiseWithState';
+export * from './lib/useInstantSearchContext';
+export * from './lib/useRSCContext';
+export * from './lib/InstantSearchRSCContext';
export * from './server';
diff --git a/packages/react-instantsearch-core/src/lib/InstantSearchRSCContext.ts b/packages/react-instantsearch-core/src/lib/InstantSearchRSCContext.ts
new file mode 100644
index 0000000000..851b46bf03
--- /dev/null
+++ b/packages/react-instantsearch-core/src/lib/InstantSearchRSCContext.ts
@@ -0,0 +1,10 @@
+import { createContext } from 'react';
+
+import type { PromiseWithState } from './wrapPromiseWithState';
+import type { MutableRefObject } from 'react';
+
+export type InstantSearchRSCContextApi =
+ MutableRefObject | null> | null;
+
+export const InstantSearchRSCContext =
+ createContext(null);
diff --git a/packages/react-instantsearch-core/src/lib/use.ts b/packages/react-instantsearch-core/src/lib/use.ts
new file mode 100644
index 0000000000..e1d067877f
--- /dev/null
+++ b/packages/react-instantsearch-core/src/lib/use.ts
@@ -0,0 +1,7 @@
+import * as React from 'react';
+
+type Use = (promise: Promise) => T;
+const useKey = 'use' as keyof typeof React;
+
+// @TODO: Remove this file and import directly from React when available.
+export const use = React[useKey] as Use;
diff --git a/packages/react-instantsearch-core/src/lib/useInstantSearchApi.ts b/packages/react-instantsearch-core/src/lib/useInstantSearchApi.ts
index 5df99a675f..514a7317d5 100644
--- a/packages/react-instantsearch-core/src/lib/useInstantSearchApi.ts
+++ b/packages/react-instantsearch-core/src/lib/useInstantSearchApi.ts
@@ -2,11 +2,12 @@ import InstantSearch from 'instantsearch.js/es/lib/InstantSearch';
import { useCallback, useRef, version as ReactVersion } from 'react';
import { useSyncExternalStore } from 'use-sync-external-store/shim';
-import { useInstantSearchServerContext } from '../lib/useInstantSearchServerContext';
-import { useInstantSearchSSRContext } from '../lib/useInstantSearchSSRContext';
import version from '../version';
import { useForceUpdate } from './useForceUpdate';
+import { useInstantSearchServerContext } from './useInstantSearchServerContext';
+import { useInstantSearchSSRContext } from './useInstantSearchSSRContext';
+import { useRSCContext } from './useRSCContext';
import { warn } from './warn';
import type {
@@ -56,9 +57,13 @@ export function useInstantSearchApi(
const forceUpdate = useForceUpdate();
const serverContext = useInstantSearchServerContext();
const serverState = useInstantSearchSSRContext();
+ const waitingForResultsRef = useRSCContext();
const initialResults = serverState?.initialResults;
const prevPropsRef = useRef(props);
+ const shouldRenderAtOnce =
+ serverContext || initialResults || waitingForResultsRef;
+
let searchRef = useRef | null>(
null
);
@@ -91,7 +96,7 @@ export function useInstantSearchApi(
} as typeof search._schedule;
search._schedule.queue = [];
- if (serverContext || initialResults) {
+ if (shouldRenderAtOnce) {
// InstantSearch.js has a private Initial Results API that lets us inject
// results on the search instance.
// On the server, we default the initial results to an empty object so that
@@ -110,7 +115,7 @@ export function useInstantSearchApi(
// On the server, we start the search early to compute the search parameters.
// On SSR, we start the search early to directly catch up with the lifecycle
// and render.
- if (serverContext || initialResults) {
+ if (shouldRenderAtOnce) {
search.start();
}
@@ -121,6 +126,7 @@ export function useInstantSearchApi(
}
warnNextRouter(props.routing);
+ warnNextAppDir(Boolean(waitingForResultsRef));
searchRef.current = search;
}
@@ -266,6 +272,22 @@ You can ignore this warning if you are using a custom router that suits your nee
}
}
+function warnNextAppDir(isRscContextDefined: boolean) {
+ if (!__DEV__ || typeof window === 'undefined' || isRscContextDefined) {
+ return;
+ }
+
+ warn(
+ Boolean((window as any).next?.appDir) === false,
+ `
+We've detected you are using Next.js with the App Router.
+We released an **experimental** package called "react-instantsearch-nextjs" that makes SSR work with the App Router.
+Please check its usage instructions: https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/#with-nextjs
+
+This warning will not be outputted in production builds.`
+ );
+}
+
/**
* Gets the version of Next.js if it is available in the `window` object,
* otherwise it returns the NEXT_RUNTIME environment variable (in SSR),
diff --git a/packages/react-instantsearch-core/src/lib/useRSCContext.ts b/packages/react-instantsearch-core/src/lib/useRSCContext.ts
new file mode 100644
index 0000000000..a1b6774f8c
--- /dev/null
+++ b/packages/react-instantsearch-core/src/lib/useRSCContext.ts
@@ -0,0 +1,7 @@
+import { useContext } from 'react';
+
+import { InstantSearchRSCContext } from './InstantSearchRSCContext';
+
+export function useRSCContext() {
+ return useContext(InstantSearchRSCContext);
+}
diff --git a/packages/react-instantsearch-core/src/lib/useWidget.ts b/packages/react-instantsearch-core/src/lib/useWidget.ts
index 6463803216..8410460ef1 100644
--- a/packages/react-instantsearch-core/src/lib/useWidget.ts
+++ b/packages/react-instantsearch-core/src/lib/useWidget.ts
@@ -1,8 +1,10 @@
import { useEffect, useRef } from 'react';
import { dequal } from './dequal';
+import { use } from './use';
import { useInstantSearchContext } from './useInstantSearchContext';
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
+import { useRSCContext } from './useRSCContext';
import type { Widget } from 'instantsearch.js';
import type { IndexWidget } from 'instantsearch.js/es/widgets/index/index';
@@ -18,6 +20,8 @@ export function useWidget({
props: TProps;
shouldSsr: boolean;
}) {
+ const waitingForResultsRef = useRSCContext();
+
const prevPropsRef = useRef(props);
useEffect(() => {
prevPropsRef.current = props;
@@ -83,7 +87,24 @@ export function useWidget({
};
}, [parentIndex, widget, shouldAddWidgetEarly, search, props]);
- if (shouldAddWidgetEarly) {
+ if (
+ shouldAddWidgetEarly ||
+ waitingForResultsRef?.current?.status === 'pending'
+ ) {
parentIndex.addWidgets([widget]);
}
+
+ if (
+ typeof window === 'undefined' &&
+ waitingForResultsRef?.current &&
+ // We need the widgets contained in the index to be added before we trigger the search request.
+ widget.$$type !== 'ais.index'
+ ) {
+ use(waitingForResultsRef.current);
+ // If we made a second request because of DynamicWidgets, we need to wait for the second result,
+ // except for DynamicWidgets itself which needs to render its children after the first result.
+ if (widget.$$type !== 'ais.dynamicWidgets' && search.helper?.lastResults) {
+ use(waitingForResultsRef.current);
+ }
+ }
}
diff --git a/packages/react-instantsearch-core/src/lib/wrapPromiseWithState.ts b/packages/react-instantsearch-core/src/lib/wrapPromiseWithState.ts
new file mode 100644
index 0000000000..853489eb98
--- /dev/null
+++ b/packages/react-instantsearch-core/src/lib/wrapPromiseWithState.ts
@@ -0,0 +1,60 @@
+// This is needed in order to work with RSC Suspense, perhaps they will later provide a wrapper.
+
+interface PendingPromise extends Promise {
+ status: 'pending';
+}
+
+interface FulfilledPromise extends Promise {
+ status: 'fulfilled';
+ value: TValue;
+}
+
+interface RejectedPromise extends Promise {
+ status: 'rejected';
+ reason: unknown;
+}
+
+export type PromiseWithState =
+ | PendingPromise
+ | FulfilledPromise
+ | RejectedPromise;
+
+function isStatefulPromise(
+ promise: Promise
+): promise is PromiseWithState {
+ return 'status' in promise;
+}
+
+export function wrapPromiseWithState(
+ promise: Promise
+): PromiseWithState {
+ if (isStatefulPromise(promise)) {
+ return promise;
+ }
+
+ const pendingPromise = promise as PendingPromise;
+ pendingPromise.status = 'pending';
+
+ pendingPromise.then(
+ (value) => {
+ if (pendingPromise.status === 'pending') {
+ const fulfilledPromise =
+ pendingPromise as unknown as FulfilledPromise;
+
+ fulfilledPromise.status = 'fulfilled';
+ fulfilledPromise.value = value;
+ }
+ },
+ (reason: unknown) => {
+ if (pendingPromise.status === 'pending') {
+ const rejectedPromise =
+ pendingPromise as unknown as RejectedPromise;
+
+ rejectedPromise.status = 'rejected';
+ rejectedPromise.reason = reason;
+ }
+ }
+ );
+
+ return promise as PromiseWithState;
+}
diff --git a/packages/react-instantsearch-nextjs/README.md b/packages/react-instantsearch-nextjs/README.md
new file mode 100644
index 0000000000..75eee451f1
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/README.md
@@ -0,0 +1,147 @@
+
+
+
+
+- [react-instantsearch-nextjs](#react-instantsearch-nextjs)
+ - [Installation](#installation)
+ - [Usage](#usage)
+ - [API](#api)
+ - [``](#instantsearchnext)
+ - [`routing` prop](#routing-prop)
+ - [Troubleshooting](#troubleshooting)
+ - [Contributing](#contributing)
+ - [License](#license)
+
+
+
+# react-instantsearch-nextjs
+
+This package provides server-side rendering for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js 13 App Router](https://nextjs.org/docs/app).
+
+> [!WARNING]
+> **This package is experimental.**
+
+## Installation
+
+```sh
+yarn add react-instantsearch-nextjs
+# or with npm
+npm install react-instantsearch-nextjs
+```
+
+## Usage
+
+> [!NOTE]
+> You can check this documentation on [Algolia's Documentation website](https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/#app-router-experimental).
+
+Your search component must be in its own file, and it shouldn't be named `page.js` or `page.tsx`.
+
+To render the component in the browser and allow users to interact with it, include the "use client" directive at the top of your code.
+
+```diff
++'use client';
+import algoliasearch from 'algoliasearch/lite';
+import {
+ InstantSearch,
+ SearchBox,
+} from 'react-instantsearch';
+
+const searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
+
+export function Search() {
+ return (
+
+
+ {/* other widgets */}
+
+ );
+}
+```
+
+Import the `` component from the `react-instantsearch-nextjs` package, and replace the <%= widget_link('instantsearch', 'react') %> component with it, without changing the props.
+
+
+```diff
+'use client';
+import algoliasearch from 'algoliasearch/lite';
+import {
+- InstantSearch,
+ SearchBox,
+} from 'react-instantsearch';
++import { InstantSearchNext } from 'react-instantsearch-nextjs';
+
+const searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
+
+export function Search() {
+ return (
+-
++
+
+ {/* other widgets */}
+
+ );
+}
+```
+
+To serve your search page at `/search`, create an `app/search` directory. Inside it, create a `page.js` file (or `page.tsx` if you're using TypeScript).
+
+Make sure to [configure your route segment to be dynamic](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic) so that Next.js generates a new page for each request.
+
+```jsx
+// app/search/page.js or app/search/page.tsx
+import { Search } from './Search'; // change this with the path to your component
+
+export const dynamic = 'force-dynamic';
+
+export default function Page() {
+ return ;
+}
+```
+
+You can now visit `/search` to see your server-side rendered search page.
+
+## API
+
+### ``
+
+The `` component is a drop-in replacement for the `` component. It accepts the same props, and it renders the same UI.
+
+You can check the [InstantSearch API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/) for more information.
+
+### `routing` prop
+
+As with the `` component, you can pass a `routing` prop to the `` component to customize the routing behavior. The difference here is that `routing.router` takes [the same options as the `historyRouter`](https://www.algolia.com/doc/api-reference/widgets/history-router/react/).
+
+## Troubleshooting
+
+If you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch.js/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml).
+
+## Contributing
+
+We welcome all contributors, from casual to regular 💙
+
+- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].
+- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].
+- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.
+- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].
+
+To start contributing to code, you need to:
+
+1. [Fork the project](https://help.github.com/articles/fork-a-repo/)
+1. [Clone the repository](https://help.github.com/articles/cloning-a-repository/)
+1. Install the dependencies: `yarn`
+
+Please read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.
+
+## License
+
+React InstantSearch is [MIT licensed](../../LICENSE).
+
+
+
+[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch
+[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20
+[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch
+[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22
+[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22
+[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22
diff --git a/packages/react-instantsearch-nextjs/__tests__/module/is-cjs-module.cjs b/packages/react-instantsearch-nextjs/__tests__/module/is-cjs-module.cjs
new file mode 100644
index 0000000000..c29e628bfa
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/__tests__/module/is-cjs-module.cjs
@@ -0,0 +1,10 @@
+/* eslint-disable no-console */
+
+const assert = require('assert');
+
+require('next');
+const ReactInstantSearchSSRNext = require('react-instantsearch-nextjs');
+
+assert.ok(ReactInstantSearchSSRNext);
+
+console.log('react-instantsearch-nextjs is valid CJS');
diff --git a/packages/react-instantsearch-nextjs/__tests__/module/is-es-module.mjs b/packages/react-instantsearch-nextjs/__tests__/module/is-es-module.mjs
new file mode 100644
index 0000000000..3d25abdd76
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/__tests__/module/is-es-module.mjs
@@ -0,0 +1,9 @@
+/* eslint-disable no-console */
+import assert from 'assert';
+
+import 'next';
+import * as ReactInstantSearchSSRNext from 'react-instantsearch-nextjs';
+
+assert.ok(ReactInstantSearchSSRNext);
+
+console.log('react-instantsearch-nextjs is valid ESM');
diff --git a/packages/react-instantsearch-nextjs/package.json b/packages/react-instantsearch-nextjs/package.json
new file mode 100644
index 0000000000..4d06f80945
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "react-instantsearch-nextjs",
+ "version": "0.0.1",
+ "description": "React InstantSearch SSR utilities for Next.js",
+ "source": "src/index.ts",
+ "types": "dist/es/index.d.ts",
+ "main": "dist/cjs/index.js",
+ "module": "dist/es/index.js",
+ "type": "module",
+ "exports": {
+ "types": "./dist/es/index.d.ts",
+ "require": "./dist/cjs/index.js",
+ "default": "./dist/es/index.js"
+ },
+ "sideEffects": false,
+ "license": "MIT",
+ "homepage": "https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/algolia/instantsearch"
+ },
+ "author": {
+ "name": "Algolia, Inc.",
+ "url": "https://www.algolia.com"
+ },
+ "keywords": [
+ "algolia",
+ "ssr",
+ "app",
+ "app router",
+ "fast",
+ "instantsearch",
+ "react",
+ "search",
+ "next",
+ "nextjs"
+ ],
+ "files": [
+ "README.md",
+ "dist"
+ ],
+ "scripts": {
+ "clean": "rm -rf dist",
+ "build": "yarn build:cjs && yarn build:es && yarn build:types",
+ "build:cjs": "BABEL_ENV=cjs babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/cjs --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet && ../../scripts/prepare-cjs.sh",
+ "build:es": "BABEL_ENV=es babel src --root-mode upward --extensions '.js,.ts,.tsx' --out-dir dist/es --ignore '**/__tests__/**/*','**/__mocks__/**/*' --quiet",
+ "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/es",
+ "test:exports": "node ./__tests__/module/is-es-module.mjs && node ./__tests__/module/is-cjs-module.cjs"
+ },
+ "devDependencies": {
+ "instantsearch.js": "4.56.11",
+ "next": "13.4.19",
+ "react-instantsearch-core": "7.0.3"
+ },
+ "peerDependencies": {
+ "next": ">= 13.4 && < 14",
+ "react-instantsearch": ">= 7.1.0 && < 8"
+ }
+}
diff --git a/packages/react-instantsearch-nextjs/src/InitializePromise.tsx b/packages/react-instantsearch-nextjs/src/InitializePromise.tsx
new file mode 100644
index 0000000000..df623a4cf5
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/src/InitializePromise.tsx
@@ -0,0 +1,76 @@
+import { getInitialResults } from 'instantsearch.js/es/lib/server';
+import { walkIndex } from 'instantsearch.js/es/lib/utils';
+import { ServerInsertedHTMLContext } from 'next/navigation';
+import React, { useContext } from 'react';
+import {
+ useInstantSearchContext,
+ useRSCContext,
+ wrapPromiseWithState,
+} from 'react-instantsearch-core';
+
+export function InitializePromise() {
+ const search = useInstantSearchContext();
+ const waitForResultsRef = useRSCContext();
+ const insertHTML =
+ useContext(ServerInsertedHTMLContext) ||
+ (() => {
+ throw new Error('Missing ServerInsertedHTMLContext');
+ });
+
+ const waitForResults = () =>
+ new Promise((resolve) => {
+ search.mainHelper!.derivedHelpers[0].on('result', () => {
+ resolve();
+ });
+ });
+
+ const injectInitialResults = () => {
+ let inserted = false;
+ const results = getInitialResults(search.mainIndex);
+ insertHTML(() => {
+ if (inserted) {
+ return <>>;
+ }
+ inserted = true;
+ return (
+
+ );
+ });
+ };
+
+ if (waitForResultsRef?.current === null) {
+ waitForResultsRef.current = wrapPromiseWithState(
+ waitForResults()
+ .then(() => {
+ let shouldRefetch = false;
+ walkIndex(search.mainIndex, (index) => {
+ shouldRefetch = index
+ .getWidgets()
+ .some((widget) => widget.$$type === 'ais.dynamicWidgets');
+ });
+
+ if (shouldRefetch) {
+ waitForResultsRef.current = wrapPromiseWithState(
+ waitForResults().then(injectInitialResults)
+ );
+ }
+
+ return shouldRefetch;
+ })
+ .then((shouldRefetch) => {
+ if (shouldRefetch) {
+ return;
+ }
+ injectInitialResults();
+ })
+ );
+ }
+
+ return null;
+}
diff --git a/packages/react-instantsearch-nextjs/src/InstantSearchNext.tsx b/packages/react-instantsearch-nextjs/src/InstantSearchNext.tsx
new file mode 100644
index 0000000000..73bc8f285d
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/src/InstantSearchNext.tsx
@@ -0,0 +1,115 @@
+import historyRouter from 'instantsearch.js/es/lib/routers/history';
+import { safelyRunOnBrowser } from 'instantsearch.js/es/lib/utils';
+import { headers } from 'next/headers';
+import { usePathname, useSearchParams, useRouter } from 'next/navigation';
+import React, { useRef } from 'react';
+import {
+ InstantSearch,
+ InstantSearchRSCContext,
+ InstantSearchSSRProvider,
+} from 'react-instantsearch-core';
+
+import { InitializePromise } from './InitializePromise';
+import { TriggerSearch } from './TriggerSearch';
+import { warn } from './warn';
+
+import type { InitialResults, StateMapping, UiState } from 'instantsearch.js';
+import type { BrowserHistoryArgs } from 'instantsearch.js/es/lib/routers/history';
+import type {
+ InstantSearchProps,
+ PromiseWithState,
+} from 'react-instantsearch-core';
+
+const InstantSearchInitialResults = Symbol.for('InstantSearchInitialResults');
+declare global {
+ interface Window {
+ [InstantSearchInitialResults]?: InitialResults[];
+ }
+}
+
+export type InstantSearchNextRouting = {
+ router?: BrowserHistoryArgs;
+ stateMapping?: StateMapping;
+};
+
+export type InstantSearchNextProps<
+ TUiState extends UiState = UiState,
+ TRouteState = TUiState
+> = Omit, 'routing'> & {
+ routing?: InstantSearchNextRouting | boolean;
+};
+
+export function InstantSearchNext<
+ TUiState extends UiState = UiState,
+ TRouteState = TUiState
+>({
+ children,
+ routing: passedRouting,
+ ...instantSearchProps
+}: InstantSearchNextProps) {
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
+ const router = useRouter();
+
+ const promiseRef = useRef | null>(null);
+
+ const initialResults = safelyRunOnBrowser(() =>
+ window[InstantSearchInitialResults]?.pop()
+ );
+
+ const routing: InstantSearchProps['routing'] =
+ passedRouting && {};
+ if (routing) {
+ let browserHistoryOptions: Partial> = {};
+
+ browserHistoryOptions.getLocation = () => {
+ if (typeof window === 'undefined') {
+ const url = `${
+ headers().get('x-forwarded-proto') || 'http'
+ }://${headers().get('host')}${pathname}?${searchParams}`;
+ return new URL(url) as unknown as Location;
+ }
+
+ return new URL(
+ `${window.location.protocol}//${window.location.host}${pathname}?${searchParams}`
+ ) as unknown as Location;
+ };
+ browserHistoryOptions.push = function push(
+ this: ReturnType,
+ url
+ ) {
+ // This is to skip the push with empty routeState on dispose as it would clear params set on a
+ if (this.isDisposed) {
+ return;
+ }
+ router.push(url);
+ };
+
+ if (typeof passedRouting === 'object') {
+ browserHistoryOptions = {
+ ...browserHistoryOptions,
+ ...passedRouting.router,
+ };
+ routing.stateMapping = passedRouting.stateMapping;
+ }
+ routing.router = historyRouter(browserHistoryOptions);
+ }
+
+ warn(
+ false,
+ `InstantSearchNext relies on experimental APIs and may break in the future.
+This message will only be displayed in development mode.`
+ );
+
+ return (
+
+
+
+ {!initialResults && }
+ {children}
+ {!initialResults && }
+
+
+
+ );
+}
diff --git a/packages/react-instantsearch-nextjs/src/TriggerSearch.ts b/packages/react-instantsearch-nextjs/src/TriggerSearch.ts
new file mode 100644
index 0000000000..24370d6fdd
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/src/TriggerSearch.ts
@@ -0,0 +1,15 @@
+import {
+ useInstantSearchContext,
+ useRSCContext,
+} from 'react-instantsearch-core';
+
+export function TriggerSearch() {
+ const instantsearch = useInstantSearchContext();
+ const waitForResultsRef = useRSCContext();
+
+ if (waitForResultsRef?.current?.status === 'pending') {
+ instantsearch.mainHelper?.searchOnlyWithDerivedHelpers();
+ }
+
+ return null;
+}
diff --git a/packages/react-instantsearch-nextjs/src/index.ts b/packages/react-instantsearch-nextjs/src/index.ts
new file mode 100644
index 0000000000..3938e6d165
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/src/index.ts
@@ -0,0 +1 @@
+export * from './InstantSearchNext';
diff --git a/packages/react-instantsearch-nextjs/src/warn.ts b/packages/react-instantsearch-nextjs/src/warn.ts
new file mode 100644
index 0000000000..f8cd5ff147
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/src/warn.ts
@@ -0,0 +1,42 @@
+/* eslint-disable no-console, no-empty */
+
+type WarnCache = {
+ current: Record;
+};
+
+export const warnCache: WarnCache = {
+ current: {},
+};
+
+/**
+ * Logs a warning if the condition is not met.
+ * This is used to log issues in development environment only.
+ */
+export function warn(condition: boolean, message: string) {
+ if (!__DEV__) {
+ return;
+ }
+
+ if (condition) {
+ return;
+ }
+
+ const sanitizedMessage = message.trim();
+ const hasAlreadyPrinted = warnCache.current[sanitizedMessage];
+
+ if (!hasAlreadyPrinted) {
+ warnCache.current[sanitizedMessage] = true;
+ const warning = `[react-instantsearch-nextjs] ${sanitizedMessage}`;
+
+ console.warn(warning);
+
+ try {
+ // Welcome to debugging InstantSearch.
+ //
+ // This error was thrown as a convenience so that you can find the source
+ // of the warning that appears in the console by enabling "Pause on exceptions"
+ // in your debugger.
+ throw new Error(warning);
+ } catch (error) {}
+ }
+}
diff --git a/packages/react-instantsearch-nextjs/tsconfig.declaration.json b/packages/react-instantsearch-nextjs/tsconfig.declaration.json
new file mode 100644
index 0000000000..15d816de98
--- /dev/null
+++ b/packages/react-instantsearch-nextjs/tsconfig.declaration.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../../tsconfig.declaration",
+ "exclude": ["**/__tests__/e2e/**/*", "**/dist/**/*"]
+}
diff --git a/yarn.lock b/yarn.lock
index c986d67f74..136d7c7260 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3763,10 +3763,10 @@
resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407"
integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==
-"@next/env@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.6.tgz#5f44823a78335355f00f1687cfc4f1dafa3eca08"
- integrity sha512-Te/OBDXFSodPU6jlXYPAXpmZr/AkG6DCATAxttQxqOWaq6eDFX25Db3dK0120GZrSZmv4QCe9KsZmJKDbWs4OA==
+"@next/env@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.19.tgz#46905b4e6f62da825b040343cbc233144e9578d3"
+ integrity sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ==
"@next/eslint-plugin-next@12.0.7":
version "12.0.7"
@@ -3775,65 +3775,50 @@
dependencies:
glob "7.1.7"
-"@next/swc-android-arm-eabi@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.6.tgz#79a35349b98f2f8c038ab6261aa9cd0d121c03f9"
- integrity sha512-BxBr3QAAAXWgk/K7EedvzxJr2dE014mghBSA9iOEAv0bMgF+MRq4PoASjuHi15M2zfowpcRG8XQhMFtxftCleQ==
-
-"@next/swc-android-arm64@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.6.tgz#ec08ea61794f8752c8ebcacbed0aafc5b9407456"
- integrity sha512-EboEk3ROYY7U6WA2RrMt/cXXMokUTXXfnxe2+CU+DOahvbrO8QSWhlBl9I9ZbFzJx28AGB9Yo3oQHCvph/4Lew==
-
-"@next/swc-darwin-arm64@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.6.tgz#d1053805615fd0706e9b1667893a72271cd87119"
- integrity sha512-P0EXU12BMSdNj1F7vdkP/VrYDuCNwBExtRPDYawgSUakzi6qP0iKJpya2BuLvNzXx+XPU49GFuDC5X+SvY0mOw==
-
-"@next/swc-darwin-x64@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.6.tgz#2d1b926a22f4c5230d5b311f9c56cfdcc406afec"
- integrity sha512-9FptMnbgHJK3dRDzfTpexs9S2hGpzOQxSQbe8omz6Pcl7rnEp9x4uSEKY51ho85JCjL4d0tDLBcXEJZKKLzxNg==
-
-"@next/swc-linux-arm-gnueabihf@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.6.tgz#c021918d2a94a17f823106a5e069335b8a19724f"
- integrity sha512-PvfEa1RR55dsik/IDkCKSFkk6ODNGJqPY3ysVUZqmnWMDSuqFtf7BPWHFa/53znpvVB5XaJ5Z1/6aR5CTIqxPw==
-
-"@next/swc-linux-arm64-gnu@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.6.tgz#ac55c07bfabde378dfa0ce2b8fc1c3b2897e81ae"
- integrity sha512-53QOvX1jBbC2ctnmWHyRhMajGq7QZfl974WYlwclXarVV418X7ed7o/EzGY+YVAEKzIVaAB9JFFWGXn8WWo0gQ==
-
-"@next/swc-linux-arm64-musl@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.6.tgz#e429f826279894be9096be6bec13e75e3d6bd671"
- integrity sha512-CMWAkYqfGdQCS+uuMA1A2UhOfcUYeoqnTW7msLr2RyYAys15pD960hlDfq7QAi8BCAKk0sQ2rjsl0iqMyziohQ==
-
-"@next/swc-linux-x64-gnu@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.6.tgz#1f276c0784a5ca599bfa34b2fcc0b38f3a738e08"
- integrity sha512-AC7jE4Fxpn0s3ujngClIDTiEM/CQiB2N2vkcyWWn6734AmGT03Duq6RYtPMymFobDdAtZGFZd5nR95WjPzbZAQ==
-
-"@next/swc-linux-x64-musl@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.6.tgz#1d9933dd6ba303dcfd8a2acd6ac7c27ed41e2eea"
- integrity sha512-c9Vjmi0EVk0Kou2qbrynskVarnFwfYIi+wKufR9Ad7/IKKuP6aEhOdZiIIdKsYWRtK2IWRF3h3YmdnEa2WLUag==
-
-"@next/swc-win32-arm64-msvc@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.6.tgz#2ef9837f12ca652b1783d72ecb86208906042f02"
- integrity sha512-3UTOL/5XZSKFelM7qN0it35o3Cegm6LsyuERR3/OoqEExyj3aCk7F025b54/707HTMAnjlvQK3DzLhPu/xxO4g==
-
-"@next/swc-win32-ia32-msvc@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.6.tgz#74003d0aa1c59dfa56cb15481a5c607cbc0027b9"
- integrity sha512-8ZWoj6nCq6fI1yCzKq6oK0jE6Mxlz4MrEsRyu0TwDztWQWe7rh4XXGLAa2YVPatYcHhMcUL+fQQbqd1MsgaSDA==
-
-"@next/swc-win32-x64-msvc@12.1.6":
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.6.tgz#a350caf42975e7197b24b495b8d764eec7e6a36e"
- integrity sha512-4ZEwiRuZEicXhXqmhw3+de8Z4EpOLQj/gp+D9fFWo6ii6W1kBkNNvvEx4A90ugppu+74pT1lIJnOuz3A9oQeJA==
+"@next/swc-darwin-arm64@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz#77ad462b5ced4efdc26cb5a0053968d2c7dac1b6"
+ integrity sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==
+
+"@next/swc-darwin-x64@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz#aebe38713a4ce536ee5f2a291673e14b715e633a"
+ integrity sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==
+
+"@next/swc-linux-arm64-gnu@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz#ec54db65b587939c7b94f9a84800f003a380f5a6"
+ integrity sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==
+
+"@next/swc-linux-arm64-musl@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz#1f5e2c1ea6941e7d530d9f185d5d64be04279d86"
+ integrity sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==
+
+"@next/swc-linux-x64-gnu@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz#96b0882492a2f7ffcce747846d3680730f69f4d1"
+ integrity sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==
+
+"@next/swc-linux-x64-musl@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz#f276b618afa321d2f7b17c81fc83f429fb0fd9d8"
+ integrity sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==
+
+"@next/swc-win32-arm64-msvc@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz#1599ae0d401da5ffca0947823dac577697cce577"
+ integrity sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==
+
+"@next/swc-win32-ia32-msvc@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz#55cdd7da90818f03e4da16d976f0cb22045d16fd"
+ integrity sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==
+
+"@next/swc-win32-x64-msvc@13.4.19":
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz#648f79c4e09279212ac90d871646ae12d80cdfce"
+ integrity sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.2":
version "2.1.8-no-fsevents.2"
@@ -6541,6 +6526,13 @@
"@swc/core-win32-ia32-msvc" "1.3.70"
"@swc/core-win32-x64-msvc" "1.3.70"
+"@swc/helpers@0.5.1", "@swc/helpers@^0.5.0":
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a"
+ integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==
+ dependencies:
+ tslib "^2.4.0"
+
"@swc/helpers@^0.4.12":
version "0.4.14"
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
@@ -6548,13 +6540,6 @@
dependencies:
tslib "^2.4.0"
-"@swc/helpers@^0.5.0":
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a"
- integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==
- dependencies:
- tslib "^2.4.0"
-
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@@ -10771,6 +10756,13 @@ bundlesize@0.18.0:
gzip-size "^4.0.0"
prettycli "^1.4.3"
+busboy@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
+ integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
+ dependencies:
+ streamsearch "^1.1.0"
+
byte-size@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3"
@@ -11127,11 +11119,16 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001503:
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001503:
version "1.0.30001517"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8"
integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==
+caniuse-lite@^1.0.30001406:
+ version "1.0.30001528"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001528.tgz#479972fc705b996f1114336c0032418a215fd0aa"
+ integrity sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==
+
capital-case@^1.0.3, capital-case@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
@@ -11591,6 +11588,11 @@ cli-width@^3.0.0:
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
+client-only@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
+ integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
+
clipboard@^2.0.0:
version "2.0.8"
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba"
@@ -16660,6 +16662,11 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
glob@7.1.4:
version "7.1.4"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
@@ -23082,7 +23089,7 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
-nanoid@^3.1.23, nanoid@^3.1.30, nanoid@^3.3.4:
+nanoid@^3.1.23, nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
@@ -23149,28 +23156,29 @@ nested-error-stacks@~2.0.1:
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz#d2cc9fc5235ddb371fc44d506234339c8e4b0a4b"
integrity sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==
-next@12.1.6:
- version "12.1.6"
- resolved "https://registry.yarnpkg.com/next/-/next-12.1.6.tgz#eb205e64af1998651f96f9df44556d47d8bbc533"
- integrity sha512-cebwKxL3/DhNKfg9tPZDQmbRKjueqykHHbgaoG4VBRH3AHQJ2HO0dbKFiS1hPhe1/qgc2d/hFeadsbPicmLD+A==
- dependencies:
- "@next/env" "12.1.6"
- caniuse-lite "^1.0.30001332"
- postcss "8.4.5"
- styled-jsx "5.0.2"
+next@13.4.19:
+ version "13.4.19"
+ resolved "https://registry.yarnpkg.com/next/-/next-13.4.19.tgz#2326e02aeedee2c693d4f37b90e4f0ed6882b35f"
+ integrity sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw==
+ dependencies:
+ "@next/env" "13.4.19"
+ "@swc/helpers" "0.5.1"
+ busboy "1.6.0"
+ caniuse-lite "^1.0.30001406"
+ postcss "8.4.14"
+ styled-jsx "5.1.1"
+ watchpack "2.4.0"
+ zod "3.21.4"
optionalDependencies:
- "@next/swc-android-arm-eabi" "12.1.6"
- "@next/swc-android-arm64" "12.1.6"
- "@next/swc-darwin-arm64" "12.1.6"
- "@next/swc-darwin-x64" "12.1.6"
- "@next/swc-linux-arm-gnueabihf" "12.1.6"
- "@next/swc-linux-arm64-gnu" "12.1.6"
- "@next/swc-linux-arm64-musl" "12.1.6"
- "@next/swc-linux-x64-gnu" "12.1.6"
- "@next/swc-linux-x64-musl" "12.1.6"
- "@next/swc-win32-arm64-msvc" "12.1.6"
- "@next/swc-win32-ia32-msvc" "12.1.6"
- "@next/swc-win32-x64-msvc" "12.1.6"
+ "@next/swc-darwin-arm64" "13.4.19"
+ "@next/swc-darwin-x64" "13.4.19"
+ "@next/swc-linux-arm64-gnu" "13.4.19"
+ "@next/swc-linux-arm64-musl" "13.4.19"
+ "@next/swc-linux-x64-gnu" "13.4.19"
+ "@next/swc-linux-x64-musl" "13.4.19"
+ "@next/swc-win32-arm64-msvc" "13.4.19"
+ "@next/swc-win32-ia32-msvc" "13.4.19"
+ "@next/swc-win32-x64-msvc" "13.4.19"
nice-try@^1.0.4:
version "1.0.4"
@@ -25886,6 +25894,15 @@ postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1:
indexes-of "^1.0.1"
uniq "^1.0.1"
+postcss@8.4.14:
+ version "8.4.14"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
+ integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
+ dependencies:
+ nanoid "^3.3.4"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
postcss@8.4.20, postcss@^8.1.10, postcss@^8.2.4, postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.16, postcss@^8.4.18, postcss@^8.4.19:
version "8.4.20"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56"
@@ -25895,15 +25912,6 @@ postcss@8.4.20, postcss@^8.1.10, postcss@^8.2.4, postcss@^8.3.11, postcss@^8.4.1
picocolors "^1.0.0"
source-map-js "^1.0.2"
-postcss@8.4.5:
- version "8.4.5"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95"
- integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==
- dependencies:
- nanoid "^3.1.30"
- picocolors "^1.0.0"
- source-map-js "^1.0.1"
-
postcss@^5.2.5:
version "5.2.18"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
@@ -26808,13 +26816,13 @@ react-devtools-core@^4.6.0:
shell-quote "^1.6.1"
ws "^7"
-react-dom@18.1.0:
- version "18.1.0"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f"
- integrity sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w==
+react-dom@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+ integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
dependencies:
loose-envify "^1.1.0"
- scheduler "^0.22.0"
+ scheduler "^0.23.0"
react-dom@^16.8.3:
version "16.14.0"
@@ -26923,7 +26931,7 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.1.0:
+"react-is@^16.12.0 || ^17.0.0 || ^18.0.0":
version "18.1.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67"
integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==
@@ -26933,6 +26941,11 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+react-is@^18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
+ integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@@ -27072,14 +27085,14 @@ react-syntax-highlighter@^11.0.2:
prismjs "^1.8.4"
refractor "^2.4.1"
-react-test-renderer@18.1.0:
- version "18.1.0"
- resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.1.0.tgz#35b75754834cf9ab517b6813db94aee0a6b545c3"
- integrity sha512-OfuueprJFW7h69GN+kr4Ywin7stcuqaYAt1g7airM5cUgP0BoF5G5CXsPGmXeDeEkncb2fqYNECO4y18sSqphg==
+react-test-renderer@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
+ integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
dependencies:
- react-is "^18.1.0"
+ react-is "^18.2.0"
react-shallow-renderer "^16.15.0"
- scheduler "^0.22.0"
+ scheduler "^0.23.0"
react-textarea-autosize@^7.1.0:
version "7.1.2"
@@ -27107,10 +27120,10 @@ react@17.0.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
-react@18.1.0:
- version "18.1.0"
- resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890"
- integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ==
+react@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+ integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies:
loose-envify "^1.1.0"
@@ -28538,10 +28551,10 @@ scheduler@^0.20.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
-scheduler@^0.22.0:
- version "0.22.0"
- resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8"
- integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ==
+scheduler@^0.23.0:
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+ integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies:
loose-envify "^1.1.0"
@@ -29827,6 +29840,11 @@ stream-via@^1.0.4:
resolved "https://registry.yarnpkg.com/stream-via/-/stream-via-1.0.4.tgz#8dccbb0ac909328eb8bc8e2a4bd3934afdaf606c"
integrity sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==
+streamsearch@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
+ integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
+
strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
@@ -30157,10 +30175,12 @@ style-to-object@^0.3.0:
dependencies:
inline-style-parser "0.1.1"
-styled-jsx@5.0.2:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.2.tgz#ff230fd593b737e9e68b630a694d460425478729"
- integrity sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==
+styled-jsx@5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f"
+ integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==
+ dependencies:
+ client-only "0.0.1"
stylehacks@^4.0.0:
version "4.0.3"
@@ -32722,6 +32742,14 @@ watchpack-chokidar2@^2.0.1:
dependencies:
chokidar "^2.1.8"
+watchpack@2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
watchpack@^1.7.4:
version "1.7.5"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453"
@@ -33917,6 +33945,11 @@ zip-stream@^2.1.2:
compress-commons "^2.1.1"
readable-stream "^3.4.0"
+zod@3.21.4:
+ version "3.21.4"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
+ integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==
+
zod@^3.17.3:
version "3.20.2"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807"