@nxgt/janus-graphql 0.2.0 → 0.4.0

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/index.js CHANGED
@@ -1,7 +1,3 @@
1
- // src/directives/apply.ts
2
- import { MapperKind, mapSchema } from "@graphql-tools/utils";
3
- import { defaultFieldResolver } from "graphql";
4
-
5
1
  // src/context.ts
6
2
  import {
7
3
  systemClock
@@ -38,14 +34,15 @@ function memoized(access) {
38
34
  // src/context.ts
39
35
  var checks = new WeakMap;
40
36
  var clocks = new WeakMap;
37
+ var answers = new WeakMap;
41
38
  function createJanusContext(request, options) {
42
- const { auth, access, type, clock } = options;
39
+ const { auth, access, type, clock, caller = "useJanus()" } = options;
43
40
  let answer;
44
41
  let asked = false;
45
42
  const authenticated = () => {
46
43
  if (!asked) {
47
44
  asked = true;
48
- answer = request === undefined ? Promise.reject(new TypeError("useJanus(): the GraphQL context has no request to authenticate — build the context from an HTTP request")) : auth.authenticate(request, type === undefined ? undefined : { type });
45
+ answer = request === undefined ? Promise.reject(new TypeError(`${caller}: the GraphQL context has no request to authenticate — build the context from an HTTP request, or accept a graphql-ws connection with janusConnection().onConnect`)) : auth.authenticate(request, type === undefined ? undefined : { type });
49
46
  }
50
47
  return answer;
51
48
  };
@@ -63,8 +60,12 @@ function createJanusContext(request, options) {
63
60
  }
64
61
  if (clock !== undefined)
65
62
  clocks.set(janus, clock);
63
+ answers.set(janus, () => asked ? answer : undefined);
66
64
  return janus;
67
65
  }
66
+ function answerOf(janus) {
67
+ return answers.get(janus)?.();
68
+ }
68
69
  function clockOf(janus) {
69
70
  return clocks.get(janus) ?? systemClock;
70
71
  }
@@ -83,12 +84,118 @@ function janusOf(ctx, caller) {
83
84
  return janus;
84
85
  }
85
86
 
87
+ // src/options.ts
88
+ function checkWiring(caller, { auth, access, clock }) {
89
+ if (typeof auth?.authenticate !== "function") {
90
+ throw new TypeError(`${caller}: auth is not what janus() answered — pass { auth }`);
91
+ }
92
+ if (access !== undefined && typeof access?.can !== "function") {
93
+ throw new TypeError(`${caller}: access is not what permissions() answered — pass { auth, access }`);
94
+ }
95
+ if (clock !== undefined && typeof clock?.now !== "function") {
96
+ throw new TypeError(`${caller}: clock is not a Clock — pass the clock given to janus(), or leave it out for the system clock`);
97
+ }
98
+ }
99
+
100
+ // src/connection/credential.ts
101
+ var BEARER = /^\s*bearer(?:\s|$)/i;
102
+ function credentialOf(ctx, upgrade) {
103
+ const params = ctx.connectionParams;
104
+ if (params !== undefined && Object.hasOwn(params, "authorization")) {
105
+ const { authorization } = params;
106
+ if (typeof authorization !== "string")
107
+ return null;
108
+ if (BEARER.test(authorization))
109
+ return { authorization };
110
+ }
111
+ const request = upgrade === undefined ? ctx.extra?.request : upgrade(ctx);
112
+ return request ?? null;
113
+ }
114
+ var accepted = new WeakMap;
115
+ function accept(ctx, credential) {
116
+ const { extra } = ctx;
117
+ if (typeof extra !== "object" || extra === null) {
118
+ throw new TypeError("janusConnection(): the connection's extra is not an object — pass the context graphql-ws gave onConnect");
119
+ }
120
+ accepted.set(extra, credential);
121
+ }
122
+ function acceptedOf(context) {
123
+ const extra = context?.extra;
124
+ if (typeof extra !== "object" || extra === null)
125
+ return;
126
+ return accepted.get(extra);
127
+ }
128
+
129
+ // src/connection/connection.ts
130
+ function janusConnection(options) {
131
+ const { auth, access, type, clock } = options;
132
+ checkWiring("janusConnection()", { auth, access, clock });
133
+ if (options.upgrade !== undefined && typeof options.upgrade !== "function") {
134
+ throw new TypeError("janusConnection(): upgrade is not a function — pass (ctx) => the upgrade request, or leave it out to read ctx.extra.request");
135
+ }
136
+ const upgrade = options.upgrade?.bind(options);
137
+ const wiring = {
138
+ auth,
139
+ access,
140
+ type,
141
+ clock,
142
+ caller: "janusConnection().context"
143
+ };
144
+ return {
145
+ onConnect: async (ctx) => {
146
+ const credential = credentialOf(ctx, upgrade);
147
+ if (credential === null)
148
+ return false;
149
+ const current = await auth.authenticate(credential, type === undefined ? undefined : { type });
150
+ if (current === null)
151
+ return false;
152
+ accept(ctx, credential);
153
+ return true;
154
+ },
155
+ context: (ctx) => ({
156
+ janus: createJanusContext(acceptedOf(ctx), wiring)
157
+ })
158
+ };
159
+ }
160
+ // src/directives/apply.ts
161
+ import { MapperKind, mapSchema } from "@graphql-tools/utils";
162
+ import { defaultFieldResolver } from "graphql";
163
+
86
164
  // src/errors.ts
165
+ import { JanusError as JanusError2, statusOf as statusOf2 } from "@nxgt/janus";
166
+ import { GraphQLError as GraphQLError2 } from "graphql";
167
+
168
+ // src/report.ts
87
169
  import { JanusError, statusOf } from "@nxgt/janus";
88
170
  import { GraphQLError } from "graphql";
171
+ function janusErrorIn(error) {
172
+ let current = error;
173
+ while (current instanceof GraphQLError)
174
+ current = current.originalError;
175
+ return current instanceof JanusError ? current : null;
176
+ }
177
+ function reporting(report) {
178
+ const seen = new WeakSet;
179
+ return (error) => {
180
+ const janusError = janusErrorIn(error);
181
+ if (janusError === null || seen.has(janusError))
182
+ return;
183
+ if (statusOf(janusError.code) < 500)
184
+ return;
185
+ seen.add(janusError);
186
+ const warn = (failure) => process.emitWarning(`janusMaskError: report failed on ${janusError.code}: ${failure instanceof Error ? failure.name : typeof failure}`);
187
+ try {
188
+ Promise.resolve(report(janusError)).then(undefined, warn);
189
+ } catch (failure) {
190
+ warn(failure);
191
+ }
192
+ };
193
+ }
194
+
195
+ // src/errors.ts
89
196
  function denial(code, message) {
90
197
  const [status, fallback] = denialOf(code);
91
- return new GraphQLError(message ?? fallback, {
198
+ return new GraphQLError2(message ?? fallback, {
92
199
  extensions: { code, http: { status } }
93
200
  });
94
201
  }
@@ -103,9 +210,9 @@ function denialOf(code) {
103
210
  }
104
211
  }
105
212
  function janusGraphQLError(error) {
106
- const status = statusOf(error.code);
213
+ const status = statusOf2(error.code);
107
214
  const code = error.code === "STORE_FAILED" ? "SERVICE_UNAVAILABLE" : error.code;
108
- return new GraphQLError(messageOf(status), {
215
+ return new GraphQLError2(messageOf(status), {
109
216
  originalError: error,
110
217
  extensions: { code, http: { status }, ...actionable(error) }
111
218
  });
@@ -142,14 +249,17 @@ function actionable(error) {
142
249
  }
143
250
  }
144
251
  function rethrown(error) {
145
- throw error instanceof JanusError ? janusGraphQLError(error) : error;
252
+ throw error instanceof JanusError2 ? janusGraphQLError(error) : error;
146
253
  }
147
- function janusMaskError(fallback = masked) {
254
+ function janusMaskError(options = {}) {
255
+ const { report, fallback = masked } = typeof options === "function" ? { fallback: options } : options;
256
+ const reported = report === undefined ? undefined : reporting(report);
148
257
  return (error, message, isDev) => {
149
- const original = error instanceof GraphQLError ? error.originalError : error;
150
- if (original instanceof JanusError) {
258
+ reported?.(error);
259
+ const original = error instanceof GraphQLError2 ? error.originalError : error;
260
+ if (original instanceof JanusError2) {
151
261
  const answer = janusGraphQLError(original);
152
- return error instanceof GraphQLError ? new GraphQLError(answer.message, {
262
+ return error instanceof GraphQLError2 ? new GraphQLError2(answer.message, {
153
263
  nodes: error.nodes ?? null,
154
264
  path: error.path ?? null,
155
265
  originalError: original,
@@ -160,18 +270,18 @@ function janusMaskError(fallback = masked) {
160
270
  };
161
271
  }
162
272
  function masked(error, message) {
163
- if (error instanceof GraphQLError) {
273
+ if (error instanceof GraphQLError2) {
164
274
  const original = error.originalError;
165
- if (original === undefined || original instanceof GraphQLError) {
275
+ if (original === undefined || original instanceof GraphQLError2) {
166
276
  return error;
167
277
  }
168
- return new GraphQLError(message, {
278
+ return new GraphQLError2(message, {
169
279
  nodes: error.nodes ?? null,
170
280
  path: error.path ?? null,
171
281
  extensions: { code: "INTERNAL_SERVER_ERROR" }
172
282
  });
173
283
  }
174
- return new GraphQLError(message, {
284
+ return new GraphQLError2(message, {
175
285
  extensions: { code: "INTERNAL_SERVER_ERROR" }
176
286
  });
177
287
  }
@@ -625,28 +735,59 @@ async function can(ctx, permission, object, ...options) {
625
735
  const subject = await janus.user().then(undefined, rethrown);
626
736
  return check(subject, permission, object, options[0]).then(undefined, rethrown);
627
737
  }
738
+ // src/renewal.ts
739
+ var contexts = new WeakMap;
740
+ function track(request, janus) {
741
+ const known = contexts.get(request);
742
+ if (known === undefined)
743
+ contexts.set(request, [janus]);
744
+ else
745
+ known.push(janus);
746
+ }
747
+ async function resendRenewed({ request, response }, cookie) {
748
+ const known = contexts.get(request);
749
+ if (cookie === undefined || known === undefined)
750
+ return;
751
+ for (const janus of known) {
752
+ const current = await answerOf(janus)?.then((answer) => answer, () => null);
753
+ if (current?.renewed === true && cookieOf(request, cookie.name) === current.token && !sets(response, cookie.name)) {
754
+ response.headers.append("Set-Cookie", cookie.serialize(current.token, current.session));
755
+ }
756
+ }
757
+ }
758
+ function cookieOf(request, name) {
759
+ const header = request.headers.get("cookie");
760
+ if (header === null)
761
+ return null;
762
+ for (const pair of header.split(";")) {
763
+ const at = pair.indexOf("=");
764
+ if (at !== -1 && pair.slice(0, at).trim() === name) {
765
+ return pair.slice(at + 1).trim();
766
+ }
767
+ }
768
+ return null;
769
+ }
770
+ function sets(response, name) {
771
+ return response.headers.getSetCookie().some((value) => value.startsWith(`${name}=`));
772
+ }
773
+
628
774
  // src/plugin.ts
629
775
  function useJanus(options) {
630
776
  const { auth, access, type, clock, loaders, conditions } = options;
631
- if (typeof auth?.authenticate !== "function") {
632
- throw new TypeError("useJanus(): auth is not what janus() answered — pass { auth }");
633
- }
634
- if (access !== undefined && typeof access?.can !== "function") {
635
- throw new TypeError("useJanus(): access is not what permissions() answered — pass { auth, access }");
636
- }
637
- if (clock !== undefined && typeof clock?.now !== "function") {
638
- throw new TypeError("useJanus(): clock is not a Clock — pass the clock given to janus(), or leave it out for the system clock");
639
- }
777
+ checkWiring("useJanus()", { auth, access, clock });
640
778
  const applied = new WeakSet;
641
779
  return {
642
780
  onContextBuilding: ({ context, extendContext }) => {
643
781
  const { request } = context;
644
- const janus = createJanusContext(request, {
782
+ const janus = createJanusContext(request ?? acceptedOf(context), {
645
783
  auth,
646
784
  access,
647
785
  type,
648
786
  clock
649
787
  });
788
+ if (typeof request === "object" && request !== null) {
789
+ track(request, janus);
790
+ }
650
791
  extendContext({ janus });
651
792
  },
652
793
  onSchemaChange: ({ schema, replaceSchema }) => {
@@ -661,7 +802,8 @@ function useJanus(options) {
661
802
  });
662
803
  applied.add(next);
663
804
  replaceSchema(next);
664
- }
805
+ },
806
+ onResponse: (payload) => resendRenewed(payload, auth.cookie)
665
807
  };
666
808
  }
667
809
  // src/sdl.ts
@@ -714,6 +856,7 @@ export {
714
856
  applyJanusDirectives,
715
857
  can,
716
858
  denial,
859
+ janusConnection,
717
860
  janusGraphQLError,
718
861
  janusMaskError,
719
862
  janusTypeDefs,
@@ -722,5 +865,5 @@ export {
722
865
  useJanus
723
866
  };
724
867
 
725
- //# debugId=50FC1B1149A482D964756E2164756E21
868
+ //# debugId=33AAA17AADE0081364756E2164756E21
726
869
  //# sourceMappingURL=index.js.map