From 0caf5145217339025b9b54b644557d9594e9fc92 Mon Sep 17 00:00:00 2001
From: Wojciech Grzebieniowski <wojciech@akeero.com>
Date: Sat, 23 Mar 2024 15:24:12 +0100
Subject: [PATCH 1/5] Allow to pass Next.js options to fetch

---
 src/PostgrestBuilder.ts          |  5 +++-
 src/PostgrestTransformBuilder.ts | 13 ++++++++-
 src/types.ts                     | 15 ++++++++++
 test/transforms.ts               | 48 ++++++++++++++++++++++++++++++++
 4 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/src/PostgrestBuilder.ts b/src/PostgrestBuilder.ts
index 96a8bc55..fc9987ab 100644
--- a/src/PostgrestBuilder.ts
+++ b/src/PostgrestBuilder.ts
@@ -1,7 +1,7 @@
 // @ts-ignore
 import nodeFetch from '@supabase/node-fetch'
 
-import type { Fetch, PostgrestSingleResponse } from './types'
+import type { Fetch, NextFetchRequestConfig, PostgrestSingleResponse } from './types'
 import PostgrestError from './PostgrestError'
 
 export default abstract class PostgrestBuilder<Result>
@@ -14,6 +14,7 @@ export default abstract class PostgrestBuilder<Result>
   protected body?: unknown
   protected shouldThrowOnError = false
   protected signal?: AbortSignal
+  protected nextOptions?: NextFetchRequestConfig
   protected fetch: Fetch
   protected isMaybeSingle: boolean
 
@@ -25,6 +26,7 @@ export default abstract class PostgrestBuilder<Result>
     this.body = builder.body
     this.shouldThrowOnError = builder.shouldThrowOnError
     this.signal = builder.signal
+    this.nextOptions = builder.nextOptions
     this.isMaybeSingle = builder.isMaybeSingle
 
     if (builder.fetch) {
@@ -74,6 +76,7 @@ export default abstract class PostgrestBuilder<Result>
       headers: this.headers,
       body: JSON.stringify(this.body),
       signal: this.signal,
+      next: this.nextOptions,
     }).then(async (res) => {
       let error = null
       let data = null
diff --git a/src/PostgrestTransformBuilder.ts b/src/PostgrestTransformBuilder.ts
index 219ccd79..d2457555 100644
--- a/src/PostgrestTransformBuilder.ts
+++ b/src/PostgrestTransformBuilder.ts
@@ -1,6 +1,6 @@
 import PostgrestBuilder from './PostgrestBuilder'
 import { GetResult } from './select-query-parser'
-import { GenericSchema } from './types'
+import { GenericSchema, NextFetchRequestConfig } from './types'
 
 export default class PostgrestTransformBuilder<
   Schema extends GenericSchema,
@@ -182,6 +182,17 @@ export default class PostgrestTransformBuilder<
     return this
   }
 
+  /**
+   * Set Next.js's tags for the fetch request.
+   *
+   * @param tags - An array of tags. A tag represents the cache tag associated with the data.
+   * Must be less than or equal to 256 characters. This value is case-sensitive.
+   */
+  next(nextOptions: NextFetchRequestConfig): this {
+    this.nextOptions = nextOptions
+    return this
+  }
+
   /**
    * Return `data` as a single object instead of an array of objects.
    *
diff --git a/src/types.ts b/src/types.ts
index 5379b271..0989fb7d 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -70,3 +70,18 @@ export type GenericSchema = {
 
 // https://twitter.com/mattpocockuk/status/1622730173446557697
 export type Prettify<T> = { [K in keyof T]: T[K] } & {}
+
+export type NextFetchRequestConfig = RequestInit extends { next: infer T }
+  ? T
+  : {
+      revalidate?: number | false
+      tags?: string[]
+    }
+
+declare global {
+  namespace globalThis {
+    interface RequestInit {
+      next?: unknown // Avoid: 'next' is referenced directly or indirectly in its own type annotation.ts(2502)
+    }
+  }
+}
diff --git a/test/transforms.ts b/test/transforms.ts
index a7ca4e76..ccf35499 100644
--- a/test/transforms.ts
+++ b/test/transforms.ts
@@ -473,3 +473,51 @@ test('rollback delete', async () => {
     }
   `)
 })
+
+test('Next.js options', async () => {
+  const fetchSpy = jest.fn(fetch)
+
+  const postgrest = new PostgrestClient<Database>('http://localhost:3000', {
+    fetch: fetchSpy,
+  })
+
+  const builder = postgrest
+    .from('users')
+    .select()
+    .eq('username', 'supabot')
+    .next({
+      tags: ['users', 'supabot'],
+    })
+  const res = await builder
+  expect(res).toMatchInlineSnapshot(`
+    Object {
+      "count": null,
+      "data": Array [
+        Object {
+          "age_range": "[1,2)",
+          "catchphrase": "'cat' 'fat'",
+          "data": null,
+          "status": "ONLINE",
+          "username": "supabot",
+        },
+      ],
+      "error": null,
+      "status": 200,
+      "statusText": "OK",
+    }
+  `)
+  expect(fetchSpy).toHaveBeenCalledWith(
+    'http://localhost:3000/users?select=*&username=eq.supabot',
+    {
+      body: undefined,
+      headers: {
+        'X-Client-Info': 'postgrest-js/0.0.0-automated',
+      },
+      method: 'GET',
+      next: {
+        tags: ['users', 'supabot'],
+      },
+      signal: undefined,
+    }
+  )
+})

From a0145905c39caeef3530a6b916398df2b52ff07d Mon Sep 17 00:00:00 2001
From: Wojciech Grzebieniowski <wojciech@akeero.com>
Date: Tue, 2 Apr 2024 09:45:25 +0200
Subject: [PATCH 2/5] Fix tests error: fetch is not defined

---
 test/transforms.ts | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/test/transforms.ts b/test/transforms.ts
index ccf35499..20e39e81 100644
--- a/test/transforms.ts
+++ b/test/transforms.ts
@@ -2,6 +2,8 @@ import { PostgrestClient } from '../src/index'
 import { Database } from './types'
 
 import { AbortController } from 'node-abort-controller'
+// @ts-ignore
+import nodeFetch from '@supabase/node-fetch'
 
 const postgrest = new PostgrestClient<Database>('http://localhost:3000')
 
@@ -475,7 +477,11 @@ test('rollback delete', async () => {
 })
 
 test('Next.js options', async () => {
-  const fetchSpy = jest.fn(fetch)
+  let fetchImpl = fetch
+  if (typeof fetchImpl === 'undefined') {
+    fetchImpl = nodeFetch
+  }
+  const fetchSpy = jest.fn(fetchImpl)
 
   const postgrest = new PostgrestClient<Database>('http://localhost:3000', {
     fetch: fetchSpy,

From 53a9652845cb0edbaaeac035e318a93128b8e908 Mon Sep 17 00:00:00 2001
From: Wojciech Grzebieniowski <wojciech@akeero.com>
Date: Tue, 2 Apr 2024 14:38:09 +0200
Subject: [PATCH 3/5] Allow to pass any fetchOptions

---
 src/PostgrestBuilder.ts          | 17 +++++++++--------
 src/PostgrestTransformBuilder.ts | 17 ++++++++++-------
 src/types.ts                     | 15 +--------------
 test/transforms.ts               |  8 +++++---
 4 files changed, 25 insertions(+), 32 deletions(-)

diff --git a/src/PostgrestBuilder.ts b/src/PostgrestBuilder.ts
index fc9987ab..ea9ef45f 100644
--- a/src/PostgrestBuilder.ts
+++ b/src/PostgrestBuilder.ts
@@ -1,7 +1,7 @@
 // @ts-ignore
 import nodeFetch from '@supabase/node-fetch'
 
-import type { Fetch, NextFetchRequestConfig, PostgrestSingleResponse } from './types'
+import type { Fetch, FetchOptions, PostgrestSingleResponse } from './types'
 import PostgrestError from './PostgrestError'
 
 export default abstract class PostgrestBuilder<Result>
@@ -13,8 +13,7 @@ export default abstract class PostgrestBuilder<Result>
   protected schema?: string
   protected body?: unknown
   protected shouldThrowOnError = false
-  protected signal?: AbortSignal
-  protected nextOptions?: NextFetchRequestConfig
+  protected fetchOpts: FetchOptions = {}
   protected fetch: Fetch
   protected isMaybeSingle: boolean
 
@@ -25,8 +24,7 @@ export default abstract class PostgrestBuilder<Result>
     this.schema = builder.schema
     this.body = builder.body
     this.shouldThrowOnError = builder.shouldThrowOnError
-    this.signal = builder.signal
-    this.nextOptions = builder.nextOptions
+    this.fetchOpts = builder.fetchOpts
     this.isMaybeSingle = builder.isMaybeSingle
 
     if (builder.fetch) {
@@ -72,11 +70,14 @@ export default abstract class PostgrestBuilder<Result>
     // https://github.com/supabase/postgrest-js/pull/247
     const _fetch = this.fetch
     let res = _fetch(this.url.toString(), {
+      ...this.fetchOpts,
       method: this.method,
-      headers: this.headers,
+      headers: {
+        ...this.fetchOpts.headers,
+        ...this.headers,
+      },
       body: JSON.stringify(this.body),
-      signal: this.signal,
-      next: this.nextOptions,
+      signal: this.fetchOpts.signal,
     }).then(async (res) => {
       let error = null
       let data = null
diff --git a/src/PostgrestTransformBuilder.ts b/src/PostgrestTransformBuilder.ts
index d2457555..206a9315 100644
--- a/src/PostgrestTransformBuilder.ts
+++ b/src/PostgrestTransformBuilder.ts
@@ -1,6 +1,6 @@
 import PostgrestBuilder from './PostgrestBuilder'
 import { GetResult } from './select-query-parser'
-import { GenericSchema, NextFetchRequestConfig } from './types'
+import type { FetchOptions, GenericSchema } from './types'
 
 export default class PostgrestTransformBuilder<
   Schema extends GenericSchema,
@@ -176,20 +176,23 @@ export default class PostgrestTransformBuilder<
    * Set the AbortSignal for the fetch request.
    *
    * @param signal - The AbortSignal to use for the fetch request
+   * @deprecated Use fetchOptions instead. E.g. `fetchOptions({ signal: new AbortController().signal })`
    */
   abortSignal(signal: AbortSignal): this {
-    this.signal = signal
+    this.fetchOpts.signal = signal
     return this
   }
 
   /**
-   * Set Next.js's tags for the fetch request.
+   * Set fetch options for the request.
    *
-   * @param tags - An array of tags. A tag represents the cache tag associated with the data.
-   * Must be less than or equal to 256 characters. This value is case-sensitive.
+   * @param init - Fetch options.
    */
-  next(nextOptions: NextFetchRequestConfig): this {
-    this.nextOptions = nextOptions
+  fetchOptions(init: FetchOptions): this {
+    this.fetchOpts = {
+      signal: this.fetchOpts.signal,
+      ...init,
+    }
     return this
   }
 
diff --git a/src/types.ts b/src/types.ts
index 0989fb7d..aead8585 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -71,17 +71,4 @@ export type GenericSchema = {
 // https://twitter.com/mattpocockuk/status/1622730173446557697
 export type Prettify<T> = { [K in keyof T]: T[K] } & {}
 
-export type NextFetchRequestConfig = RequestInit extends { next: infer T }
-  ? T
-  : {
-      revalidate?: number | false
-      tags?: string[]
-    }
-
-declare global {
-  namespace globalThis {
-    interface RequestInit {
-      next?: unknown // Avoid: 'next' is referenced directly or indirectly in its own type annotation.ts(2502)
-    }
-  }
-}
+export type FetchOptions = Omit<RequestInit, 'method' | 'body'>
diff --git a/test/transforms.ts b/test/transforms.ts
index 1666f825..41d72197 100644
--- a/test/transforms.ts
+++ b/test/transforms.ts
@@ -497,9 +497,11 @@ test('Next.js options', async () => {
     .from('users')
     .select()
     .eq('username', 'supabot')
-    .next({
-      tags: ['users', 'supabot'],
-    })
+    .fetchOptions({
+      next: {
+        tags: ['users', 'supabot'],
+      },
+    } as any)
   const res = await builder
   expect(res).toMatchInlineSnapshot(`
     Object {

From d22e54e554a518c64f2719518d4a2bd8471e288d Mon Sep 17 00:00:00 2001
From: Wojciech Grzebieniowski <wojciech@akeero.com>
Date: Tue, 2 Apr 2024 19:16:44 +0200
Subject: [PATCH 4/5] Fix merging headers

---
 src/PostgrestBuilder.ts          |  8 ++------
 src/PostgrestTransformBuilder.ts | 26 +++++++++++++++++++++++---
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/src/PostgrestBuilder.ts b/src/PostgrestBuilder.ts
index ea9ef45f..bb6488df 100644
--- a/src/PostgrestBuilder.ts
+++ b/src/PostgrestBuilder.ts
@@ -13,7 +13,7 @@ export default abstract class PostgrestBuilder<Result>
   protected schema?: string
   protected body?: unknown
   protected shouldThrowOnError = false
-  protected fetchOpts: FetchOptions = {}
+  protected fetchOpts: Omit<FetchOptions, 'headers'> = {}
   protected fetch: Fetch
   protected isMaybeSingle: boolean
 
@@ -72,12 +72,8 @@ export default abstract class PostgrestBuilder<Result>
     let res = _fetch(this.url.toString(), {
       ...this.fetchOpts,
       method: this.method,
-      headers: {
-        ...this.fetchOpts.headers,
-        ...this.headers,
-      },
+      headers: this.headers,
       body: JSON.stringify(this.body),
-      signal: this.fetchOpts.signal,
     }).then(async (res) => {
       let error = null
       let data = null
diff --git a/src/PostgrestTransformBuilder.ts b/src/PostgrestTransformBuilder.ts
index 206a9315..11a32f6d 100644
--- a/src/PostgrestTransformBuilder.ts
+++ b/src/PostgrestTransformBuilder.ts
@@ -179,7 +179,8 @@ export default class PostgrestTransformBuilder<
    * @deprecated Use fetchOptions instead. E.g. `fetchOptions({ signal: new AbortController().signal })`
    */
   abortSignal(signal: AbortSignal): this {
-    this.fetchOpts.signal = signal
+    this.fetchOptions({ signal })
+
     return this
   }
 
@@ -189,10 +190,29 @@ export default class PostgrestTransformBuilder<
    * @param init - Fetch options.
    */
   fetchOptions(init: FetchOptions): this {
+    const { headers, ...rest } = init
+
     this.fetchOpts = {
-      signal: this.fetchOpts.signal,
-      ...init,
+      ...this.fetchOpts,
+      ...rest,
     }
+
+    if (headers) {
+      let entries: Iterable<string[]>
+
+      if (Array.isArray(headers)) {
+        entries = headers.values()
+      } else if (headers instanceof Headers) {
+        entries = headers.entries()
+      } else {
+        entries = Object.entries(headers)
+      }
+
+      for (const [name, val] of entries) {
+        this.headers[name] = val
+      }
+    }
+
     return this
   }
 

From e7aeb043d96a4844da176fd40f11d445277a7095 Mon Sep 17 00:00:00 2001
From: Wojciech Grzebieniowski <grzebieniowski@gmail.com>
Date: Mon, 4 Nov 2024 10:08:24 +0100
Subject: [PATCH 5/5] Update types.ts

---
 src/types.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/types.ts b/src/types.ts
index 890fc090..effd6628 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -92,4 +92,3 @@ type BuiltIns = Primitive | void | Date | RegExp
 type Primitive = null | undefined | string | number | boolean | symbol | bigint
 
 export type FetchOptions = Omit<RequestInit, 'method' | 'body'>
-  
\ No newline at end of file