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.
- package/dist/auth/client.cjs.js +51 -8
- package/dist/auth/client.d.mts +1 -0
- package/dist/auth/client.d.ts +1 -0
- package/dist/auth/client.esm.js +48 -8
- package/dist/auth/email/client.cjs.js +51 -8
- package/dist/auth/email/client.d.mts +1 -0
- package/dist/auth/email/client.d.ts +1 -0
- package/dist/auth/email/client.esm.js +48 -8
- package/dist/auth/email/index.cjs.js +4 -2
- package/dist/auth/email/index.d.mts +1 -0
- package/dist/auth/email/index.d.ts +1 -0
- package/dist/auth/email/index.esm.js +4 -2
- package/dist/auth/email/next.d.mts +1 -0
- package/dist/auth/email/next.d.ts +1 -0
- package/dist/auth/email/routes/delete.cjs.js +4 -2
- package/dist/auth/email/routes/delete.esm.js +4 -2
- package/dist/auth/email/routes/get.cjs.js +4 -2
- package/dist/auth/email/routes/get.esm.js +4 -2
- package/dist/auth/email/routes/post.cjs.js +4 -2
- package/dist/auth/email/routes/post.esm.js +4 -2
- package/dist/auth/email/routes/put.cjs.js +4 -2
- package/dist/auth/email/routes/put.esm.js +4 -2
- package/dist/auth/email/token.cjs.js +4 -2
- package/dist/auth/email/token.d.mts +2 -2
- package/dist/auth/email/token.d.ts +2 -2
- package/dist/auth/email/token.esm.js +4 -2
- package/dist/auth/google/get.cjs.js +3 -1
- package/dist/auth/google/get.esm.js +3 -1
- package/dist/auth/google/index.cjs.js +3 -1
- package/dist/auth/google/index.esm.js +3 -1
- package/dist/auth/index.cjs.js +4 -2
- package/dist/auth/index.d.mts +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.esm.js +4 -2
- package/dist/auth/token-store.cjs.js +120 -0
- package/dist/auth/token-store.d.mts +42 -0
- package/dist/auth/token-store.d.ts +42 -0
- package/dist/auth/token-store.esm.js +90 -0
- package/dist/graphql/client.cjs.js +109 -35
- package/dist/graphql/client.esm.js +102 -29
- package/dist/graphql/links.cjs.js +154 -0
- package/dist/graphql/links.d.mts +14 -0
- package/dist/graphql/links.d.ts +14 -0
- package/dist/graphql/links.esm.js +129 -0
- package/dist/graphql/next.cjs.js +95 -21
- package/dist/graphql/next.esm.js +95 -21
- 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/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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:
|
|
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 [
|
|
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
|
|
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
|
-
}, [
|
|
206
|
+
}, [fetch2, variables]);
|
|
133
207
|
const reFetch = useCallback(
|
|
134
|
-
(input) =>
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
+
};
|
package/dist/graphql/next.cjs.js
CHANGED
|
@@ -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/
|
|
92
|
-
var
|
|
93
|
-
var
|
|
94
|
-
var
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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:
|
|
115
|
-
uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
|
|
116
|
-
})
|
|
190
|
+
link: makeAuthLink()
|
|
117
191
|
});
|
|
118
192
|
}
|
|
119
193
|
var ApolloWrapper = ({
|