naystack 1.8.2 → 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 (53) 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/clear-store.cjs.js +47 -0
  40. package/dist/graphql/clear-store.d.mts +16 -0
  41. package/dist/graphql/clear-store.d.ts +16 -0
  42. package/dist/graphql/clear-store.esm.js +23 -0
  43. package/dist/graphql/client.cjs.js +116 -21
  44. package/dist/graphql/client.d.mts +4 -4
  45. package/dist/graphql/client.d.ts +4 -4
  46. package/dist/graphql/client.esm.js +113 -19
  47. package/dist/graphql/links.cjs.js +154 -0
  48. package/dist/graphql/links.d.mts +14 -0
  49. package/dist/graphql/links.d.ts +14 -0
  50. package/dist/graphql/links.esm.js +129 -0
  51. package/dist/graphql/next.cjs.js +99 -6
  52. package/dist/graphql/next.esm.js +98 -5
  53. package/package.json +1 -1
@@ -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,9 +34,32 @@ __export(next_exports, {
34
34
  ApolloWrapper: () => ApolloWrapper
35
35
  });
36
36
  module.exports = __toCommonJS(next_exports);
37
- var import_client = require("@apollo/client");
38
37
  var import_client_integration_nextjs = require("@apollo/client-integration-nextjs");
39
- var import_react = __toESM(require("react"));
38
+ var import_react2 = __toESM(require("react"));
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");
40
63
 
41
64
  // src/env.ts
42
65
  var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
@@ -88,20 +111,90 @@ function getEnv(key, skipCheck) {
88
111
  return value;
89
112
  }
90
113
 
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
+ ]);
184
+ }
185
+
91
186
  // src/graphql/next.tsx
92
187
  function makeClient(cacheConfig) {
93
188
  return new import_client_integration_nextjs.ApolloClient({
94
189
  cache: new import_client_integration_nextjs.InMemoryCache(cacheConfig),
95
- link: new import_client.HttpLink({
96
- uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
97
- })
190
+ link: makeAuthLink()
98
191
  });
99
192
  }
100
193
  var ApolloWrapper = ({
101
194
  children,
102
195
  cacheConfig
103
196
  }) => {
104
- return /* @__PURE__ */ import_react.default.createElement(import_client_integration_nextjs.ApolloNextAppProvider, { makeClient: () => makeClient(cacheConfig) }, children);
197
+ return /* @__PURE__ */ import_react2.default.createElement(import_client_integration_nextjs.ApolloNextAppProvider, { makeClient: () => makeClient(cacheConfig) }, /* @__PURE__ */ import_react2.default.createElement(ClearStoreOnLogout, null), children);
105
198
  };
106
199
  // Annotate the CommonJS export names for ESM import in node:
107
200
  0 && (module.exports = {
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
 
3
3
  // src/graphql/next.tsx
4
- import { HttpLink } from "@apollo/client";
5
4
  import {
6
5
  ApolloClient,
7
6
  ApolloNextAppProvider,
@@ -9,6 +8,30 @@ import {
9
8
  } from "@apollo/client-integration-nextjs";
10
9
  import React from "react";
11
10
 
11
+ // src/graphql/clear-store.tsx
12
+ import { useApolloClient } from "@apollo/client";
13
+ import { useToken } from "naystack/auth/client";
14
+ import { useEffect, useRef } from "react";
15
+ function ClearStoreOnLogout() {
16
+ const client = useApolloClient();
17
+ const token = useToken();
18
+ const hadToken = useRef(false);
19
+ useEffect(() => {
20
+ if (token) {
21
+ hadToken.current = true;
22
+ } else if (token === null && hadToken.current) {
23
+ hadToken.current = false;
24
+ void client.clearStore();
25
+ }
26
+ }, [client, token]);
27
+ return null;
28
+ }
29
+
30
+ // src/graphql/links.ts
31
+ import { ApolloLink, fromPromise, HttpLink } from "@apollo/client";
32
+ import { setContext } from "@apollo/client/link/context";
33
+ import { onError } from "@apollo/client/link/error";
34
+
12
35
  // src/env.ts
13
36
  var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
14
37
  var getEnvValue = (key) => {
@@ -59,20 +82,90 @@ function getEnv(key, skipCheck) {
59
82
  return value;
60
83
  }
61
84
 
85
+ // src/auth/token-store.ts
86
+ var currentToken;
87
+ var listeners = /* @__PURE__ */ new Set();
88
+ var inFlightRefresh = null;
89
+ function getAccessToken() {
90
+ return currentToken;
91
+ }
92
+ function setAccessToken(token) {
93
+ if (token === currentToken) return;
94
+ currentToken = token;
95
+ listeners.forEach((listener) => listener(token));
96
+ }
97
+ function refreshAccessToken() {
98
+ if (inFlightRefresh) return inFlightRefresh;
99
+ inFlightRefresh = (async () => {
100
+ const res = await fetch(
101
+ getEnv("NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */),
102
+ { credentials: "include" }
103
+ );
104
+ if (!res.ok) throw new Error(`Token refresh failed: ${res.status}`);
105
+ const token = await res.text() || null;
106
+ setAccessToken(token);
107
+ return token;
108
+ })().finally(() => {
109
+ inFlightRefresh = null;
110
+ });
111
+ return inFlightRefresh;
112
+ }
113
+
114
+ // src/graphql/links.ts
115
+ var RETRY_CONTEXT_KEY = "__naystackRetried";
116
+ function isExpiredTokenError(error) {
117
+ return error.extensions?.code === "UNAUTHENTICATED";
118
+ }
119
+ var authLink = setContext((_, previousContext) => {
120
+ const token = getAccessToken();
121
+ if (!token || previousContext.credentials === "include")
122
+ return previousContext;
123
+ return {
124
+ ...previousContext,
125
+ headers: {
126
+ ...previousContext.headers,
127
+ authorization: `Bearer ${token}`
128
+ },
129
+ credentials: "omit"
130
+ };
131
+ });
132
+ var refreshLink = onError(({ graphQLErrors, operation, forward }) => {
133
+ if (!graphQLErrors?.some(isExpiredTokenError)) return;
134
+ if (operation.getContext()[RETRY_CONTEXT_KEY]) return;
135
+ return fromPromise(
136
+ // A failed refresh still replays once: the retry reproduces the original
137
+ // error for the caller instead of surfacing the refresh's error in its place.
138
+ refreshAccessToken().catch(() => null)
139
+ ).flatMap(() => {
140
+ operation.setContext((previousContext) => ({
141
+ ...previousContext,
142
+ [RETRY_CONTEXT_KEY]: true
143
+ }));
144
+ return forward(operation);
145
+ });
146
+ });
147
+ function makeAuthLink() {
148
+ return ApolloLink.from([
149
+ refreshLink,
150
+ authLink,
151
+ new HttpLink({
152
+ uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
153
+ })
154
+ ]);
155
+ }
156
+
62
157
  // src/graphql/next.tsx
63
158
  function makeClient(cacheConfig) {
64
159
  return new ApolloClient({
65
160
  cache: new InMemoryCache(cacheConfig),
66
- link: new HttpLink({
67
- uri: getEnv("NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */)
68
- })
161
+ link: makeAuthLink()
69
162
  });
70
163
  }
71
164
  var ApolloWrapper = ({
72
165
  children,
73
166
  cacheConfig
74
167
  }) => {
75
- return /* @__PURE__ */ React.createElement(ApolloNextAppProvider, { makeClient: () => makeClient(cacheConfig) }, children);
168
+ return /* @__PURE__ */ React.createElement(ApolloNextAppProvider, { makeClient: () => makeClient(cacheConfig) }, /* @__PURE__ */ React.createElement(ClearStoreOnLogout, null), children);
76
169
  };
77
170
  export {
78
171
  ApolloWrapper
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naystack",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "A stack built with Next + GraphQL + S3 + Auth",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",