naystack 1.8.3 → 1.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/auth/client.cjs.js +51 -8
  2. package/dist/auth/client.d.mts +1 -0
  3. package/dist/auth/client.d.ts +1 -0
  4. package/dist/auth/client.esm.js +48 -8
  5. package/dist/auth/email/client.cjs.js +51 -8
  6. package/dist/auth/email/client.d.mts +1 -0
  7. package/dist/auth/email/client.d.ts +1 -0
  8. package/dist/auth/email/client.esm.js +48 -8
  9. package/dist/auth/email/index.cjs.js +4 -2
  10. package/dist/auth/email/index.d.mts +1 -0
  11. package/dist/auth/email/index.d.ts +1 -0
  12. package/dist/auth/email/index.esm.js +4 -2
  13. package/dist/auth/email/next.d.mts +1 -0
  14. package/dist/auth/email/next.d.ts +1 -0
  15. package/dist/auth/email/routes/delete.cjs.js +4 -2
  16. package/dist/auth/email/routes/delete.esm.js +4 -2
  17. package/dist/auth/email/routes/get.cjs.js +4 -2
  18. package/dist/auth/email/routes/get.esm.js +4 -2
  19. package/dist/auth/email/routes/post.cjs.js +4 -2
  20. package/dist/auth/email/routes/post.esm.js +4 -2
  21. package/dist/auth/email/routes/put.cjs.js +4 -2
  22. package/dist/auth/email/routes/put.esm.js +4 -2
  23. package/dist/auth/email/token.cjs.js +4 -2
  24. package/dist/auth/email/token.d.mts +2 -2
  25. package/dist/auth/email/token.d.ts +2 -2
  26. package/dist/auth/email/token.esm.js +4 -2
  27. package/dist/auth/google/get.cjs.js +3 -1
  28. package/dist/auth/google/get.esm.js +3 -1
  29. package/dist/auth/google/index.cjs.js +3 -1
  30. package/dist/auth/google/index.esm.js +3 -1
  31. package/dist/auth/index.cjs.js +4 -2
  32. package/dist/auth/index.d.mts +1 -0
  33. package/dist/auth/index.d.ts +1 -0
  34. package/dist/auth/index.esm.js +4 -2
  35. package/dist/auth/token-store.cjs.js +120 -0
  36. package/dist/auth/token-store.d.mts +42 -0
  37. package/dist/auth/token-store.d.ts +42 -0
  38. package/dist/auth/token-store.esm.js +90 -0
  39. package/dist/graphql/client.cjs.js +109 -35
  40. package/dist/graphql/client.esm.js +102 -29
  41. package/dist/graphql/links.cjs.js +154 -0
  42. package/dist/graphql/links.d.mts +14 -0
  43. package/dist/graphql/links.d.ts +14 -0
  44. package/dist/graphql/links.esm.js +129 -0
  45. package/dist/graphql/next.cjs.js +95 -21
  46. package/dist/graphql/next.esm.js +95 -21
  47. package/package.json +1 -1
@@ -4,7 +4,6 @@
4
4
  import {
5
5
  ApolloClient,
6
6
  ApolloProvider,
7
- HttpLink,
8
7
  InMemoryCache,
9
8
  useLazyQuery,
10
9
  useMutation
@@ -16,6 +15,30 @@ import React, {
16
15
  useRef as useRef2
17
16
  } from "react";
18
17
 
18
+ // src/graphql/clear-store.tsx
19
+ import { useApolloClient } from "@apollo/client";
20
+ import { useToken } from "naystack/auth/client";
21
+ import { useEffect, useRef } from "react";
22
+ function ClearStoreOnLogout() {
23
+ const client = useApolloClient();
24
+ const token = useToken();
25
+ const hadToken = useRef(false);
26
+ useEffect(() => {
27
+ if (token) {
28
+ hadToken.current = true;
29
+ } else if (token === null && hadToken.current) {
30
+ hadToken.current = false;
31
+ void client.clearStore();
32
+ }
33
+ }, [client, token]);
34
+ return null;
35
+ }
36
+
37
+ // src/graphql/links.ts
38
+ import { ApolloLink, fromPromise, HttpLink } from "@apollo/client";
39
+ import { setContext } from "@apollo/client/link/context";
40
+ import { onError } from "@apollo/client/link/error";
41
+
19
42
  // src/env.ts
20
43
  var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
21
44
  var getEnvValue = (key) => {
@@ -66,32 +89,83 @@ function getEnv(key, skipCheck) {
66
89
  return value;
67
90
  }
68
91
 
69
- // src/graphql/clear-store.tsx
70
- import { useApolloClient } from "@apollo/client";
71
- import { useToken } from "naystack/auth/client";
72
- import { useEffect, useRef } from "react";
73
- function ClearStoreOnLogout() {
74
- const client = useApolloClient();
75
- const token = useToken();
76
- const hadToken = useRef(false);
77
- useEffect(() => {
78
- if (token) {
79
- hadToken.current = true;
80
- } else if (token === null && hadToken.current) {
81
- hadToken.current = false;
82
- void client.clearStore();
83
- }
84
- }, [client, token]);
85
- return null;
92
+ // src/auth/token-store.ts
93
+ var currentToken;
94
+ var listeners = /* @__PURE__ */ new Set();
95
+ var inFlightRefresh = null;
96
+ function getAccessToken() {
97
+ return currentToken;
98
+ }
99
+ function setAccessToken(token) {
100
+ if (token === currentToken) return;
101
+ currentToken = token;
102
+ listeners.forEach((listener) => listener(token));
103
+ }
104
+ function refreshAccessToken() {
105
+ if (inFlightRefresh) return inFlightRefresh;
106
+ inFlightRefresh = (async () => {
107
+ const res = await fetch(
108
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
109
+ { credentials: "include" }
110
+ );
111
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
112
+ const token = await res.text() || null;
113
+ setAccessToken(token);
114
+ return token;
115
+ })().finally(() => {
116
+ inFlightRefresh = null;
117
+ });
118
+ return inFlightRefresh;
119
+ }
120
+
121
+ // src/graphql/links.ts
122
+ var RETRY_CONTEXT_KEY = "__naystackRetried";
123
+ function isExpiredTokenError(error) {
124
+ return error.extensions?.code === "UNAUTHENTICATED";
125
+ }
126
+ var authLink = setContext((_, previousContext) => {
127
+ const token = getAccessToken();
128
+ if (!token || previousContext.credentials === "include")
129
+ return previousContext;
130
+ return {
131
+ ...previousContext,
132
+ headers: {
133
+ ...previousContext.headers,
134
+ authorization: `Bearer ${token}`
135
+ },
136
+ credentials: "omit"
137
+ };
138
+ });
139
+ var refreshLink = onError(({ graphQLErrors, operation, forward }) => {
140
+ if (!graphQLErrors?.some(isExpiredTokenError)) return;
141
+ if (operation.getContext()[RETRY_CONTEXT_KEY]) return;
142
+ return fromPromise(
143
+ // A failed refresh still replays once: the retry reproduces the original
144
+ // error for the caller instead of surfacing the refresh's error in its place.
145
+ refreshAccessToken().catch(() => null)
146
+ ).flatMap(() => {
147
+ operation.setContext((previousContext) => ({
148
+ ...previousContext,
149
+ [RETRY_CONTEXT_KEY]: true
150
+ }));
151
+ return forward(operation);
152
+ });
153
+ });
154
+ function makeAuthLink() {
155
+ return ApolloLink.from([
156
+ refreshLink,
157
+ authLink,
158
+ new HttpLink({
159
+ uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
160
+ })
161
+ ]);
86
162
  }
87
163
 
88
164
  // src/graphql/client.tsx
89
165
  function makeClient(cacheConfig) {
90
166
  return new ApolloClient({
91
167
  cache: new InMemoryCache(cacheConfig),
92
- link: new HttpLink({
93
- uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
94
- })
168
+ link: makeAuthLink()
95
169
  });
96
170
  }
97
171
  var ApolloWrapper = ({
@@ -112,7 +186,7 @@ var tokenContext = (token) => {
112
186
  };
113
187
  };
114
188
  function useAuthQuery(query, variables, options) {
115
- const [fetch, result] = useLazyQuery(query, {
189
+ const [fetch2, result] = useLazyQuery(query, {
116
190
  fetchPolicy: "no-cache",
117
191
  ...options
118
192
  });
@@ -121,7 +195,7 @@ function useAuthQuery(query, variables, options) {
121
195
  const serialized = JSON.stringify(variables);
122
196
  if (variables && prevVarsRef.current !== serialized) {
123
197
  prevVarsRef.current = serialized;
124
- void fetch({
198
+ void fetch2({
125
199
  // @ts-expect-error -- to allow dynamic props
126
200
  variables: { input: variables },
127
201
  context: {
@@ -129,16 +203,16 @@ function useAuthQuery(query, variables, options) {
129
203
  }
130
204
  });
131
205
  }
132
- }, [fetch, variables]);
206
+ }, [fetch2, variables]);
133
207
  const reFetch = useCallback(
134
- (input) => fetch({
208
+ (input) => fetch2({
135
209
  // @ts-expect-error -- to allow dynamic props
136
210
  variables: { input },
137
211
  context: {
138
212
  credentials: `include`
139
213
  }
140
214
  }),
141
- [fetch]
215
+ [fetch2]
142
216
  );
143
217
  return [reFetch, result];
144
218
  }
@@ -148,10 +222,9 @@ function useAuthMutation(mutation, options) {
148
222
  const method = useCallback(
149
223
  (input) => mutate({
150
224
  // @ts-expect-error -- to allow dynamic props
151
- variables: { input },
152
- context: tokenContext(token)
225
+ variables: { input }
153
226
  }),
154
- [token]
227
+ [mutate]
155
228
  );
156
229
  return [method, { ...result, hasAuth: !!token }];
157
230
  }
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/graphql/links.ts
21
+ var links_exports = {};
22
+ __export(links_exports, {
23
+ makeAuthLink: () => makeAuthLink
24
+ });
25
+ module.exports = __toCommonJS(links_exports);
26
+ var import_client = require("@apollo/client");
27
+ var import_context = require("@apollo/client/link/context");
28
+ var import_error = require("@apollo/client/link/error");
29
+
30
+ // src/env.ts
31
+ var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
32
+ var getEnvValue = (key) => {
33
+ switch (key) {
34
+ case "NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */:
35
+ return process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || EXTRA_ENV.GRAPHQL_ENDPOINT;
36
+ case "NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */:
37
+ return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
38
+ case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
39
+ return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
40
+ case "NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */:
41
+ return process.env.NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT || EXTRA_ENV.INSTAGRAM_AUTH_ENDPOINT;
42
+ case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
43
+ return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
44
+ case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
45
+ return process.env.NEXT_PUBLIC_BASE_URL || EXTRA_ENV.BASE_URL;
46
+ case "REFRESH_KEY" /* REFRESH_KEY */:
47
+ return process.env.REFRESH_KEY;
48
+ case "SIGNING_KEY" /* SIGNING_KEY */:
49
+ return process.env.SIGNING_KEY;
50
+ case "INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */:
51
+ return process.env.INSTAGRAM_CLIENT_SECRET;
52
+ case "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */:
53
+ return process.env.INSTAGRAM_CLIENT_ID;
54
+ case "GOOGLE_CLIENT_SECRET" /* GOOGLE_CLIENT_SECRET */:
55
+ return process.env.GOOGLE_CLIENT_SECRET;
56
+ case "GOOGLE_CLIENT_ID" /* GOOGLE_CLIENT_ID */:
57
+ return process.env.GOOGLE_CLIENT_ID;
58
+ case "TURNSTILE_KEY" /* TURNSTILE_KEY */:
59
+ return process.env.TURNSTILE_KEY;
60
+ case "S3_ACCESS_KEY_ID" /* S3_ACCESS_KEY_ID */:
61
+ return process.env.S3_ACCESS_KEY_ID;
62
+ case "S3_ACCESS_KEY_SECRET" /* S3_ACCESS_KEY_SECRET */:
63
+ return process.env.S3_ACCESS_KEY_SECRET;
64
+ case "S3_REGION" /* S3_REGION */:
65
+ return process.env.S3_REGION;
66
+ case "S3_BUCKET" /* S3_BUCKET */:
67
+ return process.env.S3_BUCKET;
68
+ case "NODE_ENV" /* NODE_ENV */:
69
+ return process.env.NODE_ENV;
70
+ default:
71
+ return process.env[key];
72
+ }
73
+ };
74
+ function getEnv(key, skipCheck) {
75
+ const value = getEnvValue(key);
76
+ if (!skipCheck && !value) throw new Error(`${key} is not defined`);
77
+ return value;
78
+ }
79
+
80
+ // src/auth/token-store.ts
81
+ var currentToken;
82
+ var listeners = /* @__PURE__ */ new Set();
83
+ var inFlightRefresh = null;
84
+ function getAccessToken() {
85
+ return currentToken;
86
+ }
87
+ function setAccessToken(token) {
88
+ if (token === currentToken) return;
89
+ currentToken = token;
90
+ listeners.forEach((listener) => listener(token));
91
+ }
92
+ function refreshAccessToken() {
93
+ if (inFlightRefresh) return inFlightRefresh;
94
+ inFlightRefresh = (async () => {
95
+ const res = await fetch(
96
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
97
+ { credentials: "include" }
98
+ );
99
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
100
+ const token = await res.text() || null;
101
+ setAccessToken(token);
102
+ return token;
103
+ })().finally(() => {
104
+ inFlightRefresh = null;
105
+ });
106
+ return inFlightRefresh;
107
+ }
108
+
109
+ // src/graphql/links.ts
110
+ var RETRY_CONTEXT_KEY = "__naystackRetried";
111
+ function isExpiredTokenError(error) {
112
+ return error.extensions?.code === "UNAUTHENTICATED";
113
+ }
114
+ var authLink = (0, import_context.setContext)((_, previousContext) => {
115
+ const token = getAccessToken();
116
+ if (!token || previousContext.credentials === "include")
117
+ return previousContext;
118
+ return {
119
+ ...previousContext,
120
+ headers: {
121
+ ...previousContext.headers,
122
+ authorization: `Bearer ${token}`
123
+ },
124
+ credentials: "omit"
125
+ };
126
+ });
127
+ var refreshLink = (0, import_error.onError)(({ graphQLErrors, operation, forward }) => {
128
+ if (!graphQLErrors?.some(isExpiredTokenError)) return;
129
+ if (operation.getContext()[RETRY_CONTEXT_KEY]) return;
130
+ return (0, import_client.fromPromise)(
131
+ // A failed refresh still replays once: the retry reproduces the original
132
+ // error for the caller instead of surfacing the refresh's error in its place.
133
+ refreshAccessToken().catch(() => null)
134
+ ).flatMap(() => {
135
+ operation.setContext((previousContext) => ({
136
+ ...previousContext,
137
+ [RETRY_CONTEXT_KEY]: true
138
+ }));
139
+ return forward(operation);
140
+ });
141
+ });
142
+ function makeAuthLink() {
143
+ return import_client.ApolloLink.from([
144
+ refreshLink,
145
+ authLink,
146
+ new import_client.HttpLink({
147
+ uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
148
+ })
149
+ ]);
150
+ }
151
+ // Annotate the CommonJS export names for ESM import in node:
152
+ 0 && (module.exports = {
153
+ makeAuthLink
154
+ });
@@ -0,0 +1,14 @@
1
+ import { ApolloLink } from '@apollo/client';
2
+
3
+ /**
4
+ * Builds the client link chain: refresh-and-retry → auth header → HTTP.
5
+ *
6
+ * `refreshLink` sits outermost so its replayed operation passes back through
7
+ * `authLink` and picks up the refreshed token.
8
+ *
9
+ * @returns An `ApolloLink` for the `link` option of `ApolloClient`.
10
+ * @category GraphQL
11
+ */
12
+ declare function makeAuthLink(): ApolloLink;
13
+
14
+ export { makeAuthLink };
@@ -0,0 +1,14 @@
1
+ import { ApolloLink } from '@apollo/client';
2
+
3
+ /**
4
+ * Builds the client link chain: refresh-and-retry → auth header → HTTP.
5
+ *
6
+ * `refreshLink` sits outermost so its replayed operation passes back through
7
+ * `authLink` and picks up the refreshed token.
8
+ *
9
+ * @returns An `ApolloLink` for the `link` option of `ApolloClient`.
10
+ * @category GraphQL
11
+ */
12
+ declare function makeAuthLink(): ApolloLink;
13
+
14
+ export { makeAuthLink };
@@ -0,0 +1,129 @@
1
+ // src/graphql/links.ts
2
+ import { ApolloLink, fromPromise, HttpLink } from "@apollo/client";
3
+ import { setContext } from "@apollo/client/link/context";
4
+ import { onError } from "@apollo/client/link/error";
5
+
6
+ // src/env.ts
7
+ var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
8
+ var getEnvValue = (key) => {
9
+ switch (key) {
10
+ case "NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */:
11
+ return process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || EXTRA_ENV.GRAPHQL_ENDPOINT;
12
+ case "NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */:
13
+ return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
14
+ case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
15
+ return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
16
+ case "NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */:
17
+ return process.env.NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT || EXTRA_ENV.INSTAGRAM_AUTH_ENDPOINT;
18
+ case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
19
+ return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
20
+ case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
21
+ return process.env.NEXT_PUBLIC_BASE_URL || EXTRA_ENV.BASE_URL;
22
+ case "REFRESH_KEY" /* REFRESH_KEY */:
23
+ return process.env.REFRESH_KEY;
24
+ case "SIGNING_KEY" /* SIGNING_KEY */:
25
+ return process.env.SIGNING_KEY;
26
+ case "INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */:
27
+ return process.env.INSTAGRAM_CLIENT_SECRET;
28
+ case "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */:
29
+ return process.env.INSTAGRAM_CLIENT_ID;
30
+ case "GOOGLE_CLIENT_SECRET" /* GOOGLE_CLIENT_SECRET */:
31
+ return process.env.GOOGLE_CLIENT_SECRET;
32
+ case "GOOGLE_CLIENT_ID" /* GOOGLE_CLIENT_ID */:
33
+ return process.env.GOOGLE_CLIENT_ID;
34
+ case "TURNSTILE_KEY" /* TURNSTILE_KEY */:
35
+ return process.env.TURNSTILE_KEY;
36
+ case "S3_ACCESS_KEY_ID" /* S3_ACCESS_KEY_ID */:
37
+ return process.env.S3_ACCESS_KEY_ID;
38
+ case "S3_ACCESS_KEY_SECRET" /* S3_ACCESS_KEY_SECRET */:
39
+ return process.env.S3_ACCESS_KEY_SECRET;
40
+ case "S3_REGION" /* S3_REGION */:
41
+ return process.env.S3_REGION;
42
+ case "S3_BUCKET" /* S3_BUCKET */:
43
+ return process.env.S3_BUCKET;
44
+ case "NODE_ENV" /* NODE_ENV */:
45
+ return process.env.NODE_ENV;
46
+ default:
47
+ return process.env[key];
48
+ }
49
+ };
50
+ function getEnv(key, skipCheck) {
51
+ const value = getEnvValue(key);
52
+ if (!skipCheck && !value) throw new Error(`${key} is not defined`);
53
+ return value;
54
+ }
55
+
56
+ // src/auth/token-store.ts
57
+ var currentToken;
58
+ var listeners = /* @__PURE__ */ new Set();
59
+ var inFlightRefresh = null;
60
+ function getAccessToken() {
61
+ return currentToken;
62
+ }
63
+ function setAccessToken(token) {
64
+ if (token === currentToken) return;
65
+ currentToken = token;
66
+ listeners.forEach((listener) => listener(token));
67
+ }
68
+ function refreshAccessToken() {
69
+ if (inFlightRefresh) return inFlightRefresh;
70
+ inFlightRefresh = (async () => {
71
+ const res = await fetch(
72
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
73
+ { credentials: "include" }
74
+ );
75
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
76
+ const token = await res.text() || null;
77
+ setAccessToken(token);
78
+ return token;
79
+ })().finally(() => {
80
+ inFlightRefresh = null;
81
+ });
82
+ return inFlightRefresh;
83
+ }
84
+
85
+ // src/graphql/links.ts
86
+ var RETRY_CONTEXT_KEY = "__naystackRetried";
87
+ function isExpiredTokenError(error) {
88
+ return error.extensions?.code === "UNAUTHENTICATED";
89
+ }
90
+ var authLink = setContext((_, previousContext) => {
91
+ const token = getAccessToken();
92
+ if (!token || previousContext.credentials === "include")
93
+ return previousContext;
94
+ return {
95
+ ...previousContext,
96
+ headers: {
97
+ ...previousContext.headers,
98
+ authorization: `Bearer ${token}`
99
+ },
100
+ credentials: "omit"
101
+ };
102
+ });
103
+ var refreshLink = onError(({ graphQLErrors, operation, forward }) => {
104
+ if (!graphQLErrors?.some(isExpiredTokenError)) return;
105
+ if (operation.getContext()[RETRY_CONTEXT_KEY]) return;
106
+ return fromPromise(
107
+ // A failed refresh still replays once: the retry reproduces the original
108
+ // error for the caller instead of surfacing the refresh's error in its place.
109
+ refreshAccessToken().catch(() => null)
110
+ ).flatMap(() => {
111
+ operation.setContext((previousContext) => ({
112
+ ...previousContext,
113
+ [RETRY_CONTEXT_KEY]: true
114
+ }));
115
+ return forward(operation);
116
+ });
117
+ });
118
+ function makeAuthLink() {
119
+ return ApolloLink.from([
120
+ refreshLink,
121
+ authLink,
122
+ new HttpLink({
123
+ uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
124
+ })
125
+ ]);
126
+ }
127
+ export {
128
+ makeAuthLink
129
+ };
@@ -34,10 +34,33 @@ __export(next_exports, {
34
34
  ApolloWrapper: () => ApolloWrapper
35
35
  });
36
36
  module.exports = __toCommonJS(next_exports);
37
- var import_client3 = require("@apollo/client");
38
37
  var import_client_integration_nextjs = require("@apollo/client-integration-nextjs");
39
38
  var import_react2 = __toESM(require("react"));
40
39
 
40
+ // src/graphql/clear-store.tsx
41
+ var import_client = require("@apollo/client");
42
+ var import_client2 = require("naystack/auth/client");
43
+ var import_react = require("react");
44
+ function ClearStoreOnLogout() {
45
+ const client = (0, import_client.useApolloClient)();
46
+ const token = (0, import_client2.useToken)();
47
+ const hadToken = (0, import_react.useRef)(false);
48
+ (0, import_react.useEffect)(() => {
49
+ if (token) {
50
+ hadToken.current = true;
51
+ } else if (token === null && hadToken.current) {
52
+ hadToken.current = false;
53
+ void client.clearStore();
54
+ }
55
+ }, [client, token]);
56
+ return null;
57
+ }
58
+
59
+ // src/graphql/links.ts
60
+ var import_client3 = require("@apollo/client");
61
+ var import_context = require("@apollo/client/link/context");
62
+ var import_error = require("@apollo/client/link/error");
63
+
41
64
  // src/env.ts
42
65
  var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
43
66
  var getEnvValue = (key) => {
@@ -88,32 +111,83 @@ function getEnv(key, skipCheck) {
88
111
  return value;
89
112
  }
90
113
 
91
- // src/graphql/clear-store.tsx
92
- var import_client = require("@apollo/client");
93
- var import_client2 = require("naystack/auth/client");
94
- var import_react = require("react");
95
- function ClearStoreOnLogout() {
96
- const client = (0, import_client.useApolloClient)();
97
- const token = (0, import_client2.useToken)();
98
- const hadToken = (0, import_react.useRef)(false);
99
- (0, import_react.useEffect)(() => {
100
- if (token) {
101
- hadToken.current = true;
102
- } else if (token === null && hadToken.current) {
103
- hadToken.current = false;
104
- void client.clearStore();
105
- }
106
- }, [client, token]);
107
- return null;
114
+ // src/auth/token-store.ts
115
+ var currentToken;
116
+ var listeners = /* @__PURE__ */ new Set();
117
+ var inFlightRefresh = null;
118
+ function getAccessToken() {
119
+ return currentToken;
120
+ }
121
+ function setAccessToken(token) {
122
+ if (token === currentToken) return;
123
+ currentToken = token;
124
+ listeners.forEach((listener) => listener(token));
125
+ }
126
+ function refreshAccessToken() {
127
+ if (inFlightRefresh) return inFlightRefresh;
128
+ inFlightRefresh = (async () => {
129
+ const res = await fetch(
130
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
131
+ { credentials: "include" }
132
+ );
133
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
134
+ const token = await res.text() || null;
135
+ setAccessToken(token);
136
+ return token;
137
+ })().finally(() => {
138
+ inFlightRefresh = null;
139
+ });
140
+ return inFlightRefresh;
141
+ }
142
+
143
+ // src/graphql/links.ts
144
+ var RETRY_CONTEXT_KEY = "__naystackRetried";
145
+ function isExpiredTokenError(error) {
146
+ return error.extensions?.code === "UNAUTHENTICATED";
147
+ }
148
+ var authLink = (0, import_context.setContext)((_, previousContext) => {
149
+ const token = getAccessToken();
150
+ if (!token || previousContext.credentials === "include")
151
+ return previousContext;
152
+ return {
153
+ ...previousContext,
154
+ headers: {
155
+ ...previousContext.headers,
156
+ authorization: `Bearer ${token}`
157
+ },
158
+ credentials: "omit"
159
+ };
160
+ });
161
+ var refreshLink = (0, import_error.onError)(({ graphQLErrors, operation, forward }) => {
162
+ if (!graphQLErrors?.some(isExpiredTokenError)) return;
163
+ if (operation.getContext()[RETRY_CONTEXT_KEY]) return;
164
+ return (0, import_client3.fromPromise)(
165
+ // A failed refresh still replays once: the retry reproduces the original
166
+ // error for the caller instead of surfacing the refresh's error in its place.
167
+ refreshAccessToken().catch(() => null)
168
+ ).flatMap(() => {
169
+ operation.setContext((previousContext) => ({
170
+ ...previousContext,
171
+ [RETRY_CONTEXT_KEY]: true
172
+ }));
173
+ return forward(operation);
174
+ });
175
+ });
176
+ function makeAuthLink() {
177
+ return import_client3.ApolloLink.from([
178
+ refreshLink,
179
+ authLink,
180
+ new import_client3.HttpLink({
181
+ uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
182
+ })
183
+ ]);
108
184
  }
109
185
 
110
186
  // src/graphql/next.tsx
111
187
  function makeClient(cacheConfig) {
112
188
  return new import_client_integration_nextjs.ApolloClient({
113
189
  cache: new import_client_integration_nextjs.InMemoryCache(cacheConfig),
114
- link: new import_client3.HttpLink({
115
- uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
116
- })
190
+ link: makeAuthLink()
117
191
  });
118
192
  }
119
193
  var ApolloWrapper = ({