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
@@ -65,7 +65,9 @@ var REFRESH_COOKIE_NAME = "refresh";
65
65
 
66
66
  // src/auth/email/token.ts
67
67
  function generateRefreshToken(id) {
68
- return sign({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */));
68
+ return sign({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */), {
69
+ expiresIn: "365d"
70
+ });
69
71
  }
70
72
 
71
73
  // src/auth/google/get.ts
@@ -91,7 +91,9 @@ var REFRESH_COOKIE_NAME = "refresh";
91
91
 
92
92
  // src/auth/email/token.ts
93
93
  function generateRefreshToken(id) {
94
- return (0, import_jsonwebtoken.sign)({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */));
94
+ return (0, import_jsonwebtoken.sign)({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */), {
95
+ expiresIn: "365d"
96
+ });
95
97
  }
96
98
 
97
99
  // src/auth/google/get.ts
@@ -65,7 +65,9 @@ var REFRESH_COOKIE_NAME = "refresh";
65
65
 
66
66
  // src/auth/email/token.ts
67
67
  function generateRefreshToken(id) {
68
- return sign({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */));
68
+ return sign({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */), {
69
+ expiresIn: "365d"
70
+ });
69
71
  }
70
72
 
71
73
  // src/auth/google/get.ts
@@ -150,11 +150,13 @@ function getEnv(key, skipCheck) {
150
150
  // src/auth/email/token.ts
151
151
  function generateAccessToken(id) {
152
152
  return (0, import_jsonwebtoken.sign)({ id }, getEnv("SIGNING_KEY" /* SIGNING_KEY */), {
153
- expiresIn: "2h"
153
+ expiresIn: "24h"
154
154
  });
155
155
  }
156
156
  function generateRefreshToken(id) {
157
- return (0, import_jsonwebtoken.sign)({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */));
157
+ return (0, import_jsonwebtoken.sign)({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */), {
158
+ expiresIn: "365d"
159
+ });
158
160
  }
159
161
  function getTokenizedResponse(id) {
160
162
  const accessToken = id ? generateAccessToken(id) : void 0;
@@ -13,4 +13,5 @@ import 'googleapis';
13
13
  import '../socials/instagram/types.mjs';
14
14
  import 'react';
15
15
  import './email/client.mjs';
16
+ import './token-store.mjs';
16
17
  import '../graphql/types.mjs';
@@ -13,4 +13,5 @@ import 'googleapis';
13
13
  import '../socials/instagram/types.js';
14
14
  import 'react';
15
15
  import './email/client.js';
16
+ import './token-store.js';
16
17
  import '../graphql/types.js';
@@ -104,11 +104,13 @@ function getEnv(key, skipCheck) {
104
104
  // src/auth/email/token.ts
105
105
  function generateAccessToken(id) {
106
106
  return sign({ id }, getEnv("SIGNING_KEY" /* SIGNING_KEY */), {
107
- expiresIn: "2h"
107
+ expiresIn: "24h"
108
108
  });
109
109
  }
110
110
  function generateRefreshToken(id) {
111
- return sign({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */));
111
+ return sign({ id }, getEnv("REFRESH_KEY" /* REFRESH_KEY */), {
112
+ expiresIn: "365d"
113
+ });
112
114
  }
113
115
  function getTokenizedResponse(id) {
114
116
  const accessToken = id ? generateAccessToken(id) : void 0;
@@ -0,0 +1,120 @@
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/auth/token-store.ts
21
+ var token_store_exports = {};
22
+ __export(token_store_exports, {
23
+ getAccessToken: () => getAccessToken,
24
+ refreshAccessToken: () => refreshAccessToken,
25
+ setAccessToken: () => setAccessToken,
26
+ subscribeToAccessToken: () => subscribeToAccessToken
27
+ });
28
+ module.exports = __toCommonJS(token_store_exports);
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 subscribeToAccessToken(listener) {
93
+ listeners.add(listener);
94
+ return () => {
95
+ listeners.delete(listener);
96
+ };
97
+ }
98
+ function refreshAccessToken() {
99
+ if (inFlightRefresh) return inFlightRefresh;
100
+ inFlightRefresh = (async () => {
101
+ const res = await fetch(
102
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
103
+ { credentials: "include" }
104
+ );
105
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
106
+ const token = await res.text() || null;
107
+ setAccessToken(token);
108
+ return token;
109
+ })().finally(() => {
110
+ inFlightRefresh = null;
111
+ });
112
+ return inFlightRefresh;
113
+ }
114
+ // Annotate the CommonJS export names for ESM import in node:
115
+ 0 && (module.exports = {
116
+ getAccessToken,
117
+ refreshAccessToken,
118
+ setAccessToken,
119
+ subscribeToAccessToken
120
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Reads the current access token synchronously, outside React.
3
+ * Used by the Apollo auth link; components should use `useToken()`.
4
+ *
5
+ * @returns The access token, `null` when logged out, or `undefined` before the first fetch resolves.
6
+ * @category Auth
7
+ */
8
+ declare function getAccessToken(): string | null | undefined;
9
+ /**
10
+ * Writes the access token and notifies subscribers (which updates `TokenContext`).
11
+ *
12
+ * @param token - New token, or `null` to clear the session.
13
+ * @category Auth
14
+ */
15
+ declare function setAccessToken(token: string | null | undefined): void;
16
+ /**
17
+ * Subscribes to token changes. `AuthWrapper` uses this to mirror the store into React state.
18
+ *
19
+ * @param listener - Called with the new token on every change.
20
+ * @returns Unsubscribe function.
21
+ * @category Auth
22
+ */
23
+ declare function subscribeToAccessToken(listener: (token: string | null | undefined) => void): () => void;
24
+ /**
25
+ * Exchanges the httpOnly refresh cookie for a fresh access token via the auth endpoint,
26
+ * and stores the result. Concurrent callers share one request.
27
+ *
28
+ * The auth endpoint answers 200 in both directions, so the body is the signal:
29
+ * a token means the session is alive, an empty body means the refresh cookie is
30
+ * gone or expired and the user is logged out (which clears the token, and in turn
31
+ * trips `ClearStoreOnLogout`).
32
+ *
33
+ * A transport failure (offline, 5xx) rejects **without** clearing the token — a
34
+ * flaky network shouldn't end a valid session.
35
+ *
36
+ * @returns The new access token, or `null` if the session has ended.
37
+ * @throws If the request fails at the transport level or the endpoint returns non-2xx.
38
+ * @category Auth
39
+ */
40
+ declare function refreshAccessToken(): Promise<string | null>;
41
+
42
+ export { getAccessToken, refreshAccessToken, setAccessToken, subscribeToAccessToken };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Reads the current access token synchronously, outside React.
3
+ * Used by the Apollo auth link; components should use `useToken()`.
4
+ *
5
+ * @returns The access token, `null` when logged out, or `undefined` before the first fetch resolves.
6
+ * @category Auth
7
+ */
8
+ declare function getAccessToken(): string | null | undefined;
9
+ /**
10
+ * Writes the access token and notifies subscribers (which updates `TokenContext`).
11
+ *
12
+ * @param token - New token, or `null` to clear the session.
13
+ * @category Auth
14
+ */
15
+ declare function setAccessToken(token: string | null | undefined): void;
16
+ /**
17
+ * Subscribes to token changes. `AuthWrapper` uses this to mirror the store into React state.
18
+ *
19
+ * @param listener - Called with the new token on every change.
20
+ * @returns Unsubscribe function.
21
+ * @category Auth
22
+ */
23
+ declare function subscribeToAccessToken(listener: (token: string | null | undefined) => void): () => void;
24
+ /**
25
+ * Exchanges the httpOnly refresh cookie for a fresh access token via the auth endpoint,
26
+ * and stores the result. Concurrent callers share one request.
27
+ *
28
+ * The auth endpoint answers 200 in both directions, so the body is the signal:
29
+ * a token means the session is alive, an empty body means the refresh cookie is
30
+ * gone or expired and the user is logged out (which clears the token, and in turn
31
+ * trips `ClearStoreOnLogout`).
32
+ *
33
+ * A transport failure (offline, 5xx) rejects **without** clearing the token — a
34
+ * flaky network shouldn't end a valid session.
35
+ *
36
+ * @returns The new access token, or `null` if the session has ended.
37
+ * @throws If the request fails at the transport level or the endpoint returns non-2xx.
38
+ * @category Auth
39
+ */
40
+ declare function refreshAccessToken(): Promise<string | null>;
41
+
42
+ export { getAccessToken, refreshAccessToken, setAccessToken, subscribeToAccessToken };
@@ -0,0 +1,90 @@
1
+ // src/env.ts
2
+ var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
3
+ var getEnvValue = (key) => {
4
+ switch (key) {
5
+ case "NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */:
6
+ return process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || EXTRA_ENV.GRAPHQL_ENDPOINT;
7
+ case "NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */:
8
+ return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
9
+ case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
10
+ return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
11
+ case "NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */:
12
+ return process.env.NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT || EXTRA_ENV.INSTAGRAM_AUTH_ENDPOINT;
13
+ case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
14
+ return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
15
+ case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
16
+ return process.env.NEXT_PUBLIC_BASE_URL || EXTRA_ENV.BASE_URL;
17
+ case "REFRESH_KEY" /* REFRESH_KEY */:
18
+ return process.env.REFRESH_KEY;
19
+ case "SIGNING_KEY" /* SIGNING_KEY */:
20
+ return process.env.SIGNING_KEY;
21
+ case "INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */:
22
+ return process.env.INSTAGRAM_CLIENT_SECRET;
23
+ case "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */:
24
+ return process.env.INSTAGRAM_CLIENT_ID;
25
+ case "GOOGLE_CLIENT_SECRET" /* GOOGLE_CLIENT_SECRET */:
26
+ return process.env.GOOGLE_CLIENT_SECRET;
27
+ case "GOOGLE_CLIENT_ID" /* GOOGLE_CLIENT_ID */:
28
+ return process.env.GOOGLE_CLIENT_ID;
29
+ case "TURNSTILE_KEY" /* TURNSTILE_KEY */:
30
+ return process.env.TURNSTILE_KEY;
31
+ case "S3_ACCESS_KEY_ID" /* S3_ACCESS_KEY_ID */:
32
+ return process.env.S3_ACCESS_KEY_ID;
33
+ case "S3_ACCESS_KEY_SECRET" /* S3_ACCESS_KEY_SECRET */:
34
+ return process.env.S3_ACCESS_KEY_SECRET;
35
+ case "S3_REGION" /* S3_REGION */:
36
+ return process.env.S3_REGION;
37
+ case "S3_BUCKET" /* S3_BUCKET */:
38
+ return process.env.S3_BUCKET;
39
+ case "NODE_ENV" /* NODE_ENV */:
40
+ return process.env.NODE_ENV;
41
+ default:
42
+ return process.env[key];
43
+ }
44
+ };
45
+ function getEnv(key, skipCheck) {
46
+ const value = getEnvValue(key);
47
+ if (!skipCheck && !value) throw new Error(`${key} is not defined`);
48
+ return value;
49
+ }
50
+
51
+ // src/auth/token-store.ts
52
+ var currentToken;
53
+ var listeners = /* @__PURE__ */ new Set();
54
+ var inFlightRefresh = null;
55
+ function getAccessToken() {
56
+ return currentToken;
57
+ }
58
+ function setAccessToken(token) {
59
+ if (token === currentToken) return;
60
+ currentToken = token;
61
+ listeners.forEach((listener) => listener(token));
62
+ }
63
+ function subscribeToAccessToken(listener) {
64
+ listeners.add(listener);
65
+ return () => {
66
+ listeners.delete(listener);
67
+ };
68
+ }
69
+ function refreshAccessToken() {
70
+ if (inFlightRefresh) return inFlightRefresh;
71
+ inFlightRefresh = (async () => {
72
+ const res = await fetch(
73
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
74
+ { credentials: "include" }
75
+ );
76
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
77
+ const token = await res.text() || null;
78
+ setAccessToken(token);
79
+ return token;
80
+ })().finally(() => {
81
+ inFlightRefresh = null;
82
+ });
83
+ return inFlightRefresh;
84
+ }
85
+ export {
86
+ getAccessToken,
87
+ refreshAccessToken,
88
+ setAccessToken,
89
+ subscribeToAccessToken
90
+ };
@@ -37,10 +37,34 @@ __export(client_exports, {
37
37
  useAuthQuery: () => useAuthQuery
38
38
  });
39
39
  module.exports = __toCommonJS(client_exports);
40
- var import_client3 = require("@apollo/client");
41
- var import_client4 = require("naystack/auth/client");
40
+ var import_client4 = require("@apollo/client");
41
+ var import_client5 = require("naystack/auth/client");
42
42
  var import_react2 = __toESM(require("react"));
43
43
 
44
+ // src/graphql/clear-store.tsx
45
+ var import_client = require("@apollo/client");
46
+ var import_client2 = require("naystack/auth/client");
47
+ var import_react = require("react");
48
+ function ClearStoreOnLogout() {
49
+ const client = (0, import_client.useApolloClient)();
50
+ const token = (0, import_client2.useToken)();
51
+ const hadToken = (0, import_react.useRef)(false);
52
+ (0, import_react.useEffect)(() => {
53
+ if (token) {
54
+ hadToken.current = true;
55
+ } else if (token === null && hadToken.current) {
56
+ hadToken.current = false;
57
+ void client.clearStore();
58
+ }
59
+ }, [client, token]);
60
+ return null;
61
+ }
62
+
63
+ // src/graphql/links.ts
64
+ var import_client3 = require("@apollo/client");
65
+ var import_context = require("@apollo/client/link/context");
66
+ var import_error = require("@apollo/client/link/error");
67
+
44
68
  // src/env.ts
45
69
  var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
46
70
  var getEnvValue = (key) => {
@@ -91,32 +115,83 @@ function getEnv(key, skipCheck) {
91
115
  return value;
92
116
  }
93
117
 
94
- // src/graphql/clear-store.tsx
95
- var import_client = require("@apollo/client");
96
- var import_client2 = require("naystack/auth/client");
97
- var import_react = require("react");
98
- function ClearStoreOnLogout() {
99
- const client = (0, import_client.useApolloClient)();
100
- const token = (0, import_client2.useToken)();
101
- const hadToken = (0, import_react.useRef)(false);
102
- (0, import_react.useEffect)(() => {
103
- if (token) {
104
- hadToken.current = true;
105
- } else if (token === null && hadToken.current) {
106
- hadToken.current = false;
107
- void client.clearStore();
108
- }
109
- }, [client, token]);
110
- return null;
118
+ // src/auth/token-store.ts
119
+ var currentToken;
120
+ var listeners = /* @__PURE__ */ new Set();
121
+ var inFlightRefresh = null;
122
+ function getAccessToken() {
123
+ return currentToken;
124
+ }
125
+ function setAccessToken(token) {
126
+ if (token === currentToken) return;
127
+ currentToken = token;
128
+ listeners.forEach((listener) => listener(token));
129
+ }
130
+ function refreshAccessToken() {
131
+ if (inFlightRefresh) return inFlightRefresh;
132
+ inFlightRefresh = (async () => {
133
+ const res = await fetch(
134
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
135
+ { credentials: "include" }
136
+ );
137
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
138
+ const token = await res.text() || null;
139
+ setAccessToken(token);
140
+ return token;
141
+ })().finally(() => {
142
+ inFlightRefresh = null;
143
+ });
144
+ return inFlightRefresh;
111
145
  }
112
146
 
113
- // src/graphql/client.tsx
114
- function makeClient(cacheConfig) {
115
- return new import_client3.ApolloClient({
116
- cache: new import_client3.InMemoryCache(cacheConfig),
117
- link: new import_client3.HttpLink({
147
+ // src/graphql/links.ts
148
+ var RETRY_CONTEXT_KEY = "__naystackRetried";
149
+ function isExpiredTokenError(error) {
150
+ return error.extensions?.code === "UNAUTHENTICATED";
151
+ }
152
+ var authLink = (0, import_context.setContext)((_, previousContext) => {
153
+ const token = getAccessToken();
154
+ if (!token || previousContext.credentials === "include")
155
+ return previousContext;
156
+ return {
157
+ ...previousContext,
158
+ headers: {
159
+ ...previousContext.headers,
160
+ authorization: `Bearer ${token}`
161
+ },
162
+ credentials: "omit"
163
+ };
164
+ });
165
+ var refreshLink = (0, import_error.onError)(({ graphQLErrors, operation, forward }) => {
166
+ if (!graphQLErrors?.some(isExpiredTokenError)) return;
167
+ if (operation.getContext()[RETRY_CONTEXT_KEY]) return;
168
+ return (0, import_client3.fromPromise)(
169
+ // A failed refresh still replays once: the retry reproduces the original
170
+ // error for the caller instead of surfacing the refresh's error in its place.
171
+ refreshAccessToken().catch(() => null)
172
+ ).flatMap(() => {
173
+ operation.setContext((previousContext) => ({
174
+ ...previousContext,
175
+ [RETRY_CONTEXT_KEY]: true
176
+ }));
177
+ return forward(operation);
178
+ });
179
+ });
180
+ function makeAuthLink() {
181
+ return import_client3.ApolloLink.from([
182
+ refreshLink,
183
+ authLink,
184
+ new import_client3.HttpLink({
118
185
  uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
119
186
  })
187
+ ]);
188
+ }
189
+
190
+ // src/graphql/client.tsx
191
+ function makeClient(cacheConfig) {
192
+ return new import_client4.ApolloClient({
193
+ cache: new import_client4.InMemoryCache(cacheConfig),
194
+ link: makeAuthLink()
120
195
  });
121
196
  }
122
197
  var ApolloWrapper = ({
@@ -125,7 +200,7 @@ var ApolloWrapper = ({
125
200
  }) => {
126
201
  const clientRef = (0, import_react2.useRef)(null);
127
202
  if (!clientRef.current) clientRef.current = makeClient(cacheConfig);
128
- return /* @__PURE__ */ import_react2.default.createElement(import_client3.ApolloProvider, { client: clientRef.current }, /* @__PURE__ */ import_react2.default.createElement(ClearStoreOnLogout, null), children);
203
+ return /* @__PURE__ */ import_react2.default.createElement(import_client4.ApolloProvider, { client: clientRef.current }, /* @__PURE__ */ import_react2.default.createElement(ClearStoreOnLogout, null), children);
129
204
  };
130
205
  var tokenContext = (token) => {
131
206
  if (!token) return void 0;
@@ -137,7 +212,7 @@ var tokenContext = (token) => {
137
212
  };
138
213
  };
139
214
  function useAuthQuery(query, variables, options) {
140
- const [fetch, result] = (0, import_client3.useLazyQuery)(query, {
215
+ const [fetch2, result] = (0, import_client4.useLazyQuery)(query, {
141
216
  fetchPolicy: "no-cache",
142
217
  ...options
143
218
  });
@@ -146,7 +221,7 @@ function useAuthQuery(query, variables, options) {
146
221
  const serialized = JSON.stringify(variables);
147
222
  if (variables && prevVarsRef.current !== serialized) {
148
223
  prevVarsRef.current = serialized;
149
- void fetch({
224
+ void fetch2({
150
225
  // @ts-expect-error -- to allow dynamic props
151
226
  variables: { input: variables },
152
227
  context: {
@@ -154,29 +229,28 @@ function useAuthQuery(query, variables, options) {
154
229
  }
155
230
  });
156
231
  }
157
- }, [fetch, variables]);
232
+ }, [fetch2, variables]);
158
233
  const reFetch = (0, import_react2.useCallback)(
159
- (input) => fetch({
234
+ (input) => fetch2({
160
235
  // @ts-expect-error -- to allow dynamic props
161
236
  variables: { input },
162
237
  context: {
163
238
  credentials: `include`
164
239
  }
165
240
  }),
166
- [fetch]
241
+ [fetch2]
167
242
  );
168
243
  return [reFetch, result];
169
244
  }
170
245
  function useAuthMutation(mutation, options) {
171
- const token = (0, import_client4.useToken)();
172
- const [mutate, result] = (0, import_client3.useMutation)(mutation, options);
246
+ const token = (0, import_client5.useToken)();
247
+ const [mutate, result] = (0, import_client4.useMutation)(mutation, options);
173
248
  const method = (0, import_react2.useCallback)(
174
249
  (input) => mutate({
175
250
  // @ts-expect-error -- to allow dynamic props
176
- variables: { input },
177
- context: tokenContext(token)
251
+ variables: { input }
178
252
  }),
179
- [token]
253
+ [mutate]
180
254
  );
181
255
  return [method, { ...result, hasAuth: !!token }];
182
256
  }