@makerx/graphql-core 3.3.0 → 3.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/README.md +34 -10
- package/context.d.cts +7 -0
- package/context.d.mts +7 -0
- package/context.d.ts +7 -0
- package/context.js +6 -5
- package/context.js.map +1 -1
- package/context.mjs +6 -5
- package/context.mjs.map +1 -1
- package/package.json +1 -1
- package/subscriptions/context.d.cts +7 -0
- package/subscriptions/context.d.mts +7 -0
- package/subscriptions/context.d.ts +7 -0
- package/subscriptions/context.js +5 -5
- package/subscriptions/context.js.map +1 -1
- package/subscriptions/context.mjs +6 -6
- package/subscriptions/context.mjs.map +1 -1
- package/subscriptions/server.d.cts +8 -2
- package/subscriptions/server.d.mts +8 -2
- package/subscriptions/server.d.ts +8 -2
- package/subscriptions/server.js +5 -5
- package/subscriptions/server.js.map +1 -1
- package/subscriptions/server.mjs +6 -6
- package/subscriptions/server.mjs.map +1 -1
- package/subscriptions/utils.d.cts +2 -0
- package/subscriptions/utils.d.mts +2 -0
- package/subscriptions/utils.d.ts +2 -0
- package/subscriptions/utils.js +2 -0
- package/subscriptions/utils.js.map +1 -1
- package/subscriptions/utils.mjs +2 -1
- package/subscriptions/utils.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -137,15 +137,15 @@ By default, if `claims` (decoded token `JwtPayload`) are available, the `GraphQL
|
|
|
137
137
|
|
|
138
138
|
The User class adds some handy getters over raw claims (decodedJWT payload) and provides access to the JWT (access token) for on-behalf-of downstream authentication flows. Note this may represent a user or service principal (system) identity.
|
|
139
139
|
|
|
140
|
-
| Property | Description
|
|
141
|
-
| -------- |
|
|
142
|
-
| claims | The decoded JWT payload, set via the RequestInput.user field.
|
|
143
|
-
| token | The bearer token from the
|
|
144
|
-
| email | The user's email via coalesced claim values: email, emails, preferred_username, unique_name, upn.
|
|
145
|
-
| name | The user's name (via the name or given_name and family_name claims).
|
|
146
|
-
| id | The user's unique and immutable ID, useful for contextual differentiation e.g. session keys (the oid or sub claim).
|
|
147
|
-
| scopes | The user's scopes, via the scp claim split into an array of scopes.
|
|
148
|
-
| roles | The user's roles (via the roles claim).
|
|
140
|
+
| Property | Description |
|
|
141
|
+
| -------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| claims | The decoded JWT payload, set via the RequestInput.user field. |
|
|
143
|
+
| token | The access token. By default, the bearer token from the `Authorization` header (see [Token extraction](#token-extraction)). |
|
|
144
|
+
| email | The user's email via coalesced claim values: email, emails, preferred_username, unique_name, upn. |
|
|
145
|
+
| name | The user's name (via the name or given_name and family_name claims). |
|
|
146
|
+
| id | The user's unique and immutable ID, useful for contextual differentiation e.g. session keys (the oid or sub claim). |
|
|
147
|
+
| scopes | The user's scopes, via the scp claim split into an array of scopes. |
|
|
148
|
+
| roles | The user's roles (via the roles claim). |
|
|
149
149
|
|
|
150
150
|
### Custom user
|
|
151
151
|
|
|
@@ -163,6 +163,19 @@ const graphqlServer = createServer({
|
|
|
163
163
|
})
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
+
### Token extraction
|
|
167
|
+
|
|
168
|
+
The default `createUser` sets `User.token` to the bearer token from the `Authorization` header. If clients authenticate another way, for example with a JWT assertion header that a proxy adds to the request, supply `extractToken` to return the token:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const createContext = createContextFactory({
|
|
172
|
+
requestLogger,
|
|
173
|
+
extractToken: ({ req }) => req.headers['x-goog-iap-jwt-assertion'] as string | undefined,
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`extractToken` is not called when you supply `createUser`. For subscriptions, see [GraphQL subscriptions](#graphql-subscriptions).
|
|
178
|
+
|
|
166
179
|
## Shield (authorization)
|
|
167
180
|
|
|
168
181
|
The `shield` module provides typed helpers around [graphql-shield](https://the-guild.dev/graphql/shield) for defining authorization middleware against your generated `Resolvers` type.
|
|
@@ -363,7 +376,7 @@ This library includes a `subscriptions` module to provide simple setup using the
|
|
|
363
376
|
1. Create a subscriptions server, using the ws-server cleanup function in your server lifecycle.
|
|
364
377
|
|
|
365
378
|
The `useSubscriptionsServer` function sets up:
|
|
366
|
-
- Auth token validation as part of establishing (or rejecting) the connection (behaviour defined by `verifyToken` and `requireAuth` args)
|
|
379
|
+
- Auth token validation as part of establishing (or rejecting) the connection (behaviour defined by `verifyToken`, `extractToken` and `requireAuth` args)
|
|
367
380
|
- GraphQL context creation
|
|
368
381
|
- Logging from the server `onConnect`, `onDisconnect`, `onOperation`, `onNext` and `onError` callbacks
|
|
369
382
|
|
|
@@ -400,6 +413,17 @@ This library includes a `subscriptions` module to provide simple setup using the
|
|
|
400
413
|
|
|
401
414
|
1. For authorisation, clients can include a connection parameter named `authorization` or `Authorization` using the HTTP header format `Bearer <token>`. Note: [Apollo Sandbox](https://studio.apollographql.com/sandbox/explorer) will include an `Authorization` connection parameter when you specify an HTTP `Authorization` header via the UI.
|
|
402
415
|
|
|
416
|
+
If the token arrives another way, supply an `extractToken` function to both `useSubscriptionsServer` (which passes the token to `verifyToken`) and `createSubscriptionContextFactory` (which sets it as `User.token`). It receives the `connectRequest` (the HTTP upgrade request) and the `connectionParams`, and returns the token or `undefined`. For example, to read a JWT assertion header that a proxy adds to the upgrade request (browsers cannot set custom headers on a websocket request):
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
const extractToken: ExtractSubscriptionToken = ({ connectRequest }) =>
|
|
420
|
+
connectRequest.headers['x-goog-iap-jwt-assertion'] as string | undefined
|
|
421
|
+
|
|
422
|
+
const createSubscriptionContext = createSubscriptionContextFactory({ requestLogger, extractToken })
|
|
423
|
+
|
|
424
|
+
useSubscriptionsServer({ schema, httpServer, logger, createSubscriptionContext, extractToken, requireAuth: true, verifyToken })
|
|
425
|
+
```
|
|
426
|
+
|
|
403
427
|
### wrapSubscriptionIterator
|
|
404
428
|
|
|
405
429
|
Wraps an `AsyncIterableIterator` (e.g. one returned by a pubsub `asyncIterableIterator(...)` call) with two optional behaviours:
|
package/context.d.cts
CHANGED
|
@@ -51,12 +51,19 @@ export interface ContextInput {
|
|
|
51
51
|
export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
|
|
52
52
|
export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
|
|
53
53
|
export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
|
|
54
|
+
export type ExtractToken = (input: ContextInput) => string | undefined;
|
|
54
55
|
export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
55
56
|
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
56
57
|
augmentRequestInfo?: AugmentRequestInfo;
|
|
57
58
|
claimsToLog?: string[];
|
|
58
59
|
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
59
60
|
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
61
|
+
/**
|
|
62
|
+
* Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion
|
|
63
|
+
* header. Not called when you supply `createUser`. Defaults to the bearer token in the
|
|
64
|
+
* `Authorization` header.
|
|
65
|
+
*/
|
|
66
|
+
extractToken?: ExtractToken;
|
|
60
67
|
} & ([User | undefined] extends [TUser] ? {
|
|
61
68
|
createUser?: CreateUser<TUser>;
|
|
62
69
|
} : {
|
package/context.d.mts
CHANGED
|
@@ -51,12 +51,19 @@ export interface ContextInput {
|
|
|
51
51
|
export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
|
|
52
52
|
export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
|
|
53
53
|
export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
|
|
54
|
+
export type ExtractToken = (input: ContextInput) => string | undefined;
|
|
54
55
|
export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
55
56
|
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
56
57
|
augmentRequestInfo?: AugmentRequestInfo;
|
|
57
58
|
claimsToLog?: string[];
|
|
58
59
|
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
59
60
|
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
61
|
+
/**
|
|
62
|
+
* Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion
|
|
63
|
+
* header. Not called when you supply `createUser`. Defaults to the bearer token in the
|
|
64
|
+
* `Authorization` header.
|
|
65
|
+
*/
|
|
66
|
+
extractToken?: ExtractToken;
|
|
60
67
|
} & ([User | undefined] extends [TUser] ? {
|
|
61
68
|
createUser?: CreateUser<TUser>;
|
|
62
69
|
} : {
|
package/context.d.ts
CHANGED
|
@@ -51,12 +51,19 @@ export interface ContextInput {
|
|
|
51
51
|
export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
|
|
52
52
|
export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
|
|
53
53
|
export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
|
|
54
|
+
export type ExtractToken = (input: ContextInput) => string | undefined;
|
|
54
55
|
export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
55
56
|
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
56
57
|
augmentRequestInfo?: AugmentRequestInfo;
|
|
57
58
|
claimsToLog?: string[];
|
|
58
59
|
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
59
60
|
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
61
|
+
/**
|
|
62
|
+
* Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion
|
|
63
|
+
* header. Not called when you supply `createUser`. Defaults to the bearer token in the
|
|
64
|
+
* `Authorization` header.
|
|
65
|
+
*/
|
|
66
|
+
extractToken?: ExtractToken;
|
|
60
67
|
} & ([User | undefined] extends [TUser] ? {
|
|
61
68
|
createUser?: CreateUser<TUser>;
|
|
62
69
|
} : {
|
package/context.js
CHANGED
|
@@ -8,7 +8,7 @@ const createContextFactory = (config) => {
|
|
|
8
8
|
const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
|
|
9
9
|
// The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is
|
|
10
10
|
// narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.
|
|
11
|
-
const createUser = (config.createUser ??
|
|
11
|
+
const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken));
|
|
12
12
|
return async (input) => {
|
|
13
13
|
const { req, claims, context } = input;
|
|
14
14
|
// build base request info from the request
|
|
@@ -57,12 +57,13 @@ const createContextFactory = (config) => {
|
|
|
57
57
|
return augmentedGraphQLContext;
|
|
58
58
|
};
|
|
59
59
|
};
|
|
60
|
-
const
|
|
61
|
-
|
|
60
|
+
const defaultExtractToken = ({ req }) => req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined;
|
|
61
|
+
const buildDefaultCreateUser = (extractToken) => (input) => {
|
|
62
|
+
if (!input.claims)
|
|
62
63
|
return Promise.resolve(undefined);
|
|
63
|
-
|
|
64
|
-
return Promise.resolve(new User.User(claims, accessToken));
|
|
64
|
+
return Promise.resolve(new User.User(input.claims, extractToken(input) ?? ''));
|
|
65
65
|
};
|
|
66
|
+
const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken);
|
|
66
67
|
|
|
67
68
|
exports.createContextFactory = createContextFactory;
|
|
68
69
|
exports.defaultCreateUser = defaultCreateUser;
|
package/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ??
|
|
1
|
+
{"version":3,"file":"context.js","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\nexport type ExtractToken = (input: ContextInput) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion\n * header. Not called when you supply `createUser`. Defaults to the bearer token in the\n * `Authorization` header.\n */\n extractToken?: ExtractToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken)) as CreateUser<TUser>\n\n return async (input: ContextInput) => {\n const { req, claims, context } = input\n\n // build base request info from the request\n const baseRequestInfo: BaseRequestInfo = buildBaseRequestInfo(req)\n\n // add lambda info from the context, if present\n let lambdaRequestInfo: LambdaRequestInfo | undefined\n if (context) {\n const { awsRequestId, functionName } = context\n lambdaRequestInfo = {\n awsRequestId,\n functionName,\n ...baseRequestInfo,\n }\n }\n\n // augment request info with the supplied function, if present\n let requestInfo: RequestInfo = lambdaRequestInfo ?? baseRequestInfo\n if (augmentRequestInfo)\n requestInfo = {\n ...requestInfo,\n ...augmentRequestInfo(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst defaultExtractToken: ExtractToken = ({ req }) =>\n req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractToken): CreateUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n\nexport const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken)\n"],"names":["buildBaseRequestInfo","requestInfo","pick","User"],"mappings":";;;;;;AA6FO,MAAM,oBAAoB,GAAG,CAKlC,MAAqD,KACoB;AACzE,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAI,mBAAmB,CAAC,CAAsB;AAEjI,IAAA,OAAO,OAAO,KAAmB,KAAI;QACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK;;AAGtC,QAAA,MAAM,eAAe,GAAoBA,gCAAoB,CAAC,GAAG,CAAC;;AAGlE,QAAA,IAAI,iBAAgD;QACpD,IAAI,OAAO,EAAE;AACX,YAAA,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO;AAC9C,YAAA,iBAAiB,GAAG;gBAClB,YAAY;gBACZ,YAAY;AACZ,gBAAA,GAAG,eAAe;aACnB;QACH;;AAGA,QAAA,IAAIC,aAAW,GAAgB,iBAAiB,IAAI,eAAe;AACnE,QAAA,IAAI,kBAAkB;AACpB,YAAAA,aAAW,GAAG;AACZ,gBAAA,GAAGA,aAAW;gBACd,GAAG,kBAAkB,CAAC,KAAK,CAAC;aAC7B;AAEH,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAGC,WAAI,CAACD,aAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAGC,WAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;yBAClED,aAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,mBAAmB,GAAiB,CAAC,EAAE,GAAG,EAAE,KAChD,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;AAEtG,MAAM,sBAAsB,GAC1B,CAAC,YAA0B,KAC3B,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAIE,SAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;MAEU,iBAAiB,GAAG,sBAAsB,CAAC,mBAAmB;;;;;"}
|
package/context.mjs
CHANGED
|
@@ -6,7 +6,7 @@ const createContextFactory = (config) => {
|
|
|
6
6
|
const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
|
|
7
7
|
// The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is
|
|
8
8
|
// narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.
|
|
9
|
-
const createUser = (config.createUser ??
|
|
9
|
+
const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken));
|
|
10
10
|
return async (input) => {
|
|
11
11
|
const { req, claims, context } = input;
|
|
12
12
|
// build base request info from the request
|
|
@@ -55,12 +55,13 @@ const createContextFactory = (config) => {
|
|
|
55
55
|
return augmentedGraphQLContext;
|
|
56
56
|
};
|
|
57
57
|
};
|
|
58
|
-
const
|
|
59
|
-
|
|
58
|
+
const defaultExtractToken = ({ req }) => req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined;
|
|
59
|
+
const buildDefaultCreateUser = (extractToken) => (input) => {
|
|
60
|
+
if (!input.claims)
|
|
60
61
|
return Promise.resolve(undefined);
|
|
61
|
-
|
|
62
|
-
return Promise.resolve(new User(claims, accessToken));
|
|
62
|
+
return Promise.resolve(new User(input.claims, extractToken(input) ?? ''));
|
|
63
63
|
};
|
|
64
|
+
const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken);
|
|
64
65
|
|
|
65
66
|
export { createContextFactory, defaultCreateUser };
|
|
66
67
|
//# sourceMappingURL=context.mjs.map
|
package/context.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.mjs","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ??
|
|
1
|
+
{"version":3,"file":"context.mjs","sources":["../src/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { Request } from 'express'\nimport { buildBaseRequestInfo, type BaseRequestInfo } from './request-info'\nimport { User } from './User'\n\nexport interface GraphQLContext<\n TLogger extends Logger = Logger,\n TRequestInfo extends BaseRequestInfo = RequestInfo,\n TUser = User | undefined,\n> {\n logger: TLogger\n requestInfo: TRequestInfo\n user: TUser\n started: number\n}\n\nexport type AnyGraphqlContext = GraphQLContext<any, any, any>\n\nexport interface LambdaContext {\n functionName?: string\n awsRequestId?: string\n}\nexport type LambdaEvent = never\nexport type LambdaRequestInfo = BaseRequestInfo & LambdaContext\nexport type RequestInfo = BaseRequestInfo | LambdaRequestInfo\n\n// Strips the `[key: string]: unknown` index signature so `keyof` returns only the declared keys.\ntype KnownKeys<T> = keyof {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n}\n// Known keys for autocomplete, plus `(string & {})` to keep arbitrary augmented fields assignable.\nexport type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {})\n\n// standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1\nexport interface JwtPayload {\n [key: string]: unknown\n name?: string | undefined\n email?: string | undefined\n given_name?: string | undefined\n family_name?: string | undefined\n preferred_username?: string | undefined\n upn?: string | undefined\n unique_name?: string | undefined\n oid?: string | undefined\n iss?: string | undefined\n sub?: string | undefined\n aud?: string | string[] | undefined\n roles?: string[] | undefined\n scp?: string | undefined\n exp?: number | undefined\n nbf?: number | undefined\n iat?: number | undefined\n jti?: string | undefined\n}\n\nexport type InferUserFromContext<TContext extends AnyGraphqlContext> =\n TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never\nexport type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T\nexport interface ContextInput {\n req: Request\n claims?: JwtPayload\n context?: LambdaContext\n event?: LambdaEvent\n}\nexport type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>\nexport type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (\n requestMetadata: Record<string, unknown>,\n user: TUser,\n) => TLogger\nexport type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>\nexport type ExtractToken = (input: ContextInput) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. `defaultCreateUser` can satisfy it), and required when TUser is narrower.\nexport type CreateContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`, e.g. from a JWT assertion\n * header. Not called when you supply `createUser`. Defaults to the bearer token in the\n * `Authorization` header.\n */\n extractToken?: ExtractToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateUser<TUser> } : { createUser: CreateUser<TUser> })\n\nexport const createContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateContextConfig<TUser, TAugment, TLogger>,\n): CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateContextConfig guarantees `createUser` is provided when TUser is\n // narrower than `User | undefined`, so defaulting to defaultCreateUser is sound here.\n const createUser = (config.createUser ?? buildDefaultCreateUser(config.extractToken ?? defaultExtractToken)) as CreateUser<TUser>\n\n return async (input: ContextInput) => {\n const { req, claims, context } = input\n\n // build base request info from the request\n const baseRequestInfo: BaseRequestInfo = buildBaseRequestInfo(req)\n\n // add lambda info from the context, if present\n let lambdaRequestInfo: LambdaRequestInfo | undefined\n if (context) {\n const { awsRequestId, functionName } = context\n lambdaRequestInfo = {\n awsRequestId,\n functionName,\n ...baseRequestInfo,\n }\n }\n\n // augment request info with the supplied function, if present\n let requestInfo: RequestInfo = lambdaRequestInfo ?? baseRequestInfo\n if (augmentRequestInfo)\n requestInfo = {\n ...requestInfo,\n ...augmentRequestInfo(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst defaultExtractToken: ExtractToken = ({ req }) =>\n req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization.substring(7) : undefined\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractToken): CreateUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n\nexport const defaultCreateUser = buildDefaultCreateUser(defaultExtractToken)\n"],"names":[],"mappings":";;;;AA6FO,MAAM,oBAAoB,GAAG,CAKlC,MAAqD,KACoB;AACzE,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU,IAAI,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAI,mBAAmB,CAAC,CAAsB;AAEjI,IAAA,OAAO,OAAO,KAAmB,KAAI;QACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK;;AAGtC,QAAA,MAAM,eAAe,GAAoB,oBAAoB,CAAC,GAAG,CAAC;;AAGlE,QAAA,IAAI,iBAAgD;QACpD,IAAI,OAAO,EAAE;AACX,YAAA,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO;AAC9C,YAAA,iBAAiB,GAAG;gBAClB,YAAY;gBACZ,YAAY;AACZ,gBAAA,GAAG,eAAe;aACnB;QACH;;AAGA,QAAA,IAAI,WAAW,GAAgB,iBAAiB,IAAI,eAAe;AACnE,QAAA,IAAI,kBAAkB;AACpB,YAAA,WAAW,GAAG;AACZ,gBAAA,GAAG,WAAW;gBACd,GAAG,kBAAkB,CAAC,KAAK,CAAC;aAC7B;AAEH,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;YAClE,WAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,mBAAmB,GAAiB,CAAC,EAAE,GAAG,EAAE,KAChD,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;AAEtG,MAAM,sBAAsB,GAC1B,CAAC,YAA0B,KAC3B,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;MAEU,iBAAiB,GAAG,sBAAsB,CAAC,mBAAmB;;;;"}
|
package/package.json
CHANGED
|
@@ -10,12 +10,19 @@ export interface SubscriptionContextInput {
|
|
|
10
10
|
export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
|
|
11
11
|
export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
|
|
12
12
|
export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
|
|
13
|
+
export type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined;
|
|
13
14
|
export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
14
15
|
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
15
16
|
augmentRequestInfo?: AugmentSubscriptionRequestInfo;
|
|
16
17
|
claimsToLog?: string[];
|
|
17
18
|
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
18
19
|
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the token that the default `createUser` sets as `User.token`. Use the same function as
|
|
22
|
+
* the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.
|
|
23
|
+
* Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.
|
|
24
|
+
*/
|
|
25
|
+
extractToken?: ExtractSubscriptionToken;
|
|
19
26
|
} & ([User | undefined] extends [TUser] ? {
|
|
20
27
|
createUser?: CreateSubscriptionUser<TUser>;
|
|
21
28
|
} : {
|
|
@@ -10,12 +10,19 @@ export interface SubscriptionContextInput {
|
|
|
10
10
|
export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
|
|
11
11
|
export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
|
|
12
12
|
export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
|
|
13
|
+
export type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined;
|
|
13
14
|
export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
14
15
|
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
15
16
|
augmentRequestInfo?: AugmentSubscriptionRequestInfo;
|
|
16
17
|
claimsToLog?: string[];
|
|
17
18
|
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
18
19
|
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the token that the default `createUser` sets as `User.token`. Use the same function as
|
|
22
|
+
* the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.
|
|
23
|
+
* Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.
|
|
24
|
+
*/
|
|
25
|
+
extractToken?: ExtractSubscriptionToken;
|
|
19
26
|
} & ([User | undefined] extends [TUser] ? {
|
|
20
27
|
createUser?: CreateSubscriptionUser<TUser>;
|
|
21
28
|
} : {
|
|
@@ -10,12 +10,19 @@ export interface SubscriptionContextInput {
|
|
|
10
10
|
export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
|
|
11
11
|
export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
|
|
12
12
|
export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
|
|
13
|
+
export type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined;
|
|
13
14
|
export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
14
15
|
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
15
16
|
augmentRequestInfo?: AugmentSubscriptionRequestInfo;
|
|
16
17
|
claimsToLog?: string[];
|
|
17
18
|
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
18
19
|
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the token that the default `createUser` sets as `User.token`. Use the same function as
|
|
22
|
+
* the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.
|
|
23
|
+
* Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.
|
|
24
|
+
*/
|
|
25
|
+
extractToken?: ExtractSubscriptionToken;
|
|
19
26
|
} & ([User | undefined] extends [TUser] ? {
|
|
20
27
|
createUser?: CreateSubscriptionUser<TUser>;
|
|
21
28
|
} : {
|
package/subscriptions/context.js
CHANGED
|
@@ -9,7 +9,8 @@ const createSubscriptionContextFactory = (config) => {
|
|
|
9
9
|
const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
|
|
10
10
|
// The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided
|
|
11
11
|
// when TUser is narrower than `User | undefined`, so defaulting is sound here.
|
|
12
|
-
const createUser = (config.createUser ??
|
|
12
|
+
const createUser = (config.createUser ??
|
|
13
|
+
buildDefaultCreateUser(config.extractToken ?? utils.defaultExtractSubscriptionToken));
|
|
13
14
|
return async (input) => {
|
|
14
15
|
const { connectRequest: req, claims } = input;
|
|
15
16
|
// build request info from the connect request and socket
|
|
@@ -44,11 +45,10 @@ const createSubscriptionContextFactory = (config) => {
|
|
|
44
45
|
return augmentedGraphQLContext;
|
|
45
46
|
};
|
|
46
47
|
};
|
|
47
|
-
const
|
|
48
|
-
if (!claims)
|
|
48
|
+
const buildDefaultCreateUser = (extractToken) => (input) => {
|
|
49
|
+
if (!input.claims)
|
|
49
50
|
return Promise.resolve(undefined);
|
|
50
|
-
|
|
51
|
-
return Promise.resolve(new User.User(claims, accessToken ?? ''));
|
|
51
|
+
return Promise.resolve(new User.User(input.claims, extractToken(input) ?? ''));
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
exports.createSubscriptionContextFactory = createSubscriptionContextFactory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport {
|
|
1
|
+
{"version":3,"file":"context.js","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport { defaultExtractSubscriptionToken } from './utils'\n\nexport interface SubscriptionContextInput {\n connectRequest: IncomingMessage\n connectionParams?: Readonly<Record<string, unknown>>\n claims?: JwtPayload\n}\n\nexport type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T\nexport type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>\nexport type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>\n// The token is extracted before the connection is authenticated, so the input has no claims.\nexport type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. defaultCreateUser can satisfy it), and required when TUser is narrower.\nexport type CreateSubscriptionContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentSubscriptionRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`. Use the same function as\n * the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.\n * Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.\n */\n extractToken?: ExtractSubscriptionToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateSubscriptionUser<TUser> } : { createUser: CreateSubscriptionUser<TUser> })\n\nexport const createSubscriptionContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>,\n): CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided\n // when TUser is narrower than `User | undefined`, so defaulting is sound here.\n const createUser = (config.createUser ??\n buildDefaultCreateUser(config.extractToken ?? defaultExtractSubscriptionToken)) as CreateSubscriptionUser<TUser>\n\n return async (input: SubscriptionContextInput) => {\n const { connectRequest: req, claims } = input\n\n // build request info from the connect request and socket\n const requestInfo: RequestInfo = {\n ...buildConnectRequestInfo(req),\n ...augmentRequestInfo?.(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractSubscriptionToken): CreateSubscriptionUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n"],"names":["defaultExtractSubscriptionToken","requestInfo","buildConnectRequestInfo","pick","User"],"mappings":";;;;;;;AAwCO,MAAM,gCAAgC,GAAG,CAK9C,MAAiE,KACoB;AACrF,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU;QACnC,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAIA,qCAA+B,CAAC,CAAkC;AAElH,IAAA,OAAO,OAAO,KAA+B,KAAI;QAC/C,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK;;AAG7C,QAAA,MAAMC,aAAW,GAAgB;YAC/B,GAAGC,mCAAuB,CAAC,GAAG,CAAC;AAC/B,YAAA,GAAG,kBAAkB,GAAG,KAAK,CAAC;SAC/B;AAED,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAGC,WAAI,CAACF,aAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAGE,WAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;yBAClEF,aAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,sBAAsB,GAC1B,CAAC,YAAsC,KACvC,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAIG,SAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;;;;"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { pick } from 'es-toolkit/compat';
|
|
2
2
|
import { User } from '../User.mjs';
|
|
3
3
|
import { buildConnectRequestInfo } from '../request-info.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { defaultExtractSubscriptionToken } from './utils.mjs';
|
|
5
5
|
|
|
6
6
|
const createSubscriptionContextFactory = (config) => {
|
|
7
7
|
const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config;
|
|
8
8
|
// The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided
|
|
9
9
|
// when TUser is narrower than `User | undefined`, so defaulting is sound here.
|
|
10
|
-
const createUser = (config.createUser ??
|
|
10
|
+
const createUser = (config.createUser ??
|
|
11
|
+
buildDefaultCreateUser(config.extractToken ?? defaultExtractSubscriptionToken));
|
|
11
12
|
return async (input) => {
|
|
12
13
|
const { connectRequest: req, claims } = input;
|
|
13
14
|
// build request info from the connect request and socket
|
|
@@ -42,11 +43,10 @@ const createSubscriptionContextFactory = (config) => {
|
|
|
42
43
|
return augmentedGraphQLContext;
|
|
43
44
|
};
|
|
44
45
|
};
|
|
45
|
-
const
|
|
46
|
-
if (!claims)
|
|
46
|
+
const buildDefaultCreateUser = (extractToken) => (input) => {
|
|
47
|
+
if (!input.claims)
|
|
47
48
|
return Promise.resolve(undefined);
|
|
48
|
-
|
|
49
|
-
return Promise.resolve(new User(claims, accessToken ?? ''));
|
|
49
|
+
return Promise.resolve(new User(input.claims, extractToken(input) ?? ''));
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
export { createSubscriptionContextFactory };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.mjs","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport {
|
|
1
|
+
{"version":3,"file":"context.mjs","sources":["../../src/subscriptions/context.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { IncomingMessage } from 'http'\nimport { User } from '../User'\nimport type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context'\nimport { buildConnectRequestInfo } from '../request-info'\nimport { defaultExtractSubscriptionToken } from './utils'\n\nexport interface SubscriptionContextInput {\n connectRequest: IncomingMessage\n connectionParams?: Readonly<Record<string, unknown>>\n claims?: JwtPayload\n}\n\nexport type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T\nexport type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>\nexport type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>\n// The token is extracted before the connection is authenticated, so the input has no claims.\nexport type ExtractSubscriptionToken = (input: Omit<SubscriptionContextInput, 'claims'>) => string | undefined\n\n// `createUser` is optional when TUser is compatible with the default `User | undefined`\n// (i.e. defaultCreateUser can satisfy it), and required when TUser is narrower.\nexport type CreateSubscriptionContextConfig<\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n> = {\n requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger\n augmentRequestInfo?: AugmentSubscriptionRequestInfo\n claimsToLog?: string[]\n requestInfoToLog?: Array<RequestInfoLogKey>\n augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>\n /**\n * Returns the token that the default `createUser` sets as `User.token`. Use the same function as\n * the `useSubscriptionsServer` `extractToken` option. Not called when you supply `createUser`.\n * Defaults to the bearer token in the `authorization` or `Authorization` connection parameter.\n */\n extractToken?: ExtractSubscriptionToken\n} & ([User | undefined] extends [TUser] ? { createUser?: CreateSubscriptionUser<TUser> } : { createUser: CreateSubscriptionUser<TUser> })\n\nexport const createSubscriptionContextFactory = <\n TUser = User | undefined,\n TAugment extends Record<string, unknown> = Record<string, never>,\n TLogger extends Logger = Logger,\n>(\n config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>,\n): CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment> => {\n const { requestLogger, augmentRequestInfo, claimsToLog, requestInfoToLog, augmentContext } = config\n // The conditional type on CreateSubscriptionContextConfig guarantees `createUser` is provided\n // when TUser is narrower than `User | undefined`, so defaulting is sound here.\n const createUser = (config.createUser ??\n buildDefaultCreateUser(config.extractToken ?? defaultExtractSubscriptionToken)) as CreateSubscriptionUser<TUser>\n\n return async (input: SubscriptionContextInput) => {\n const { connectRequest: req, claims } = input\n\n // build request info from the connect request and socket\n const requestInfo: RequestInfo = {\n ...buildConnectRequestInfo(req),\n ...augmentRequestInfo?.(input),\n }\n\n const user = await createUser(input)\n\n // create request logger\n let logger: TLogger\n if (typeof requestLogger === 'function') {\n // build request logger metadata\n const requestLoggerMetadata: Record<string, unknown> = {}\n // add request info to log\n if (requestInfoToLog?.length) requestLoggerMetadata.request = pick(requestInfo, requestInfoToLog)\n // add user claims to log\n if (claims && claimsToLog?.length) requestLoggerMetadata.user = pick(claims, claimsToLog)\n // build the request logger\n logger = requestLogger(requestLoggerMetadata, user)\n } else logger = requestLogger\n\n const graphqlContext: GraphQLContext<TLogger, RequestInfo, TUser> = {\n requestInfo,\n logger,\n user,\n started: Date.now(),\n }\n\n const augmentedGraphQLContext = augmentContext ? { ...graphqlContext, ...(await augmentContext(graphqlContext)) } : graphqlContext\n\n return augmentedGraphQLContext as GraphQLContext<TLogger, RequestInfo, TUser> & TAugment\n }\n}\n\nconst buildDefaultCreateUser =\n (extractToken: ExtractSubscriptionToken): CreateSubscriptionUser<User | undefined> =>\n (input) => {\n if (!input.claims) return Promise.resolve(undefined)\n return Promise.resolve(new User(input.claims, extractToken(input) ?? ''))\n }\n"],"names":[],"mappings":";;;;;AAwCO,MAAM,gCAAgC,GAAG,CAK9C,MAAiE,KACoB;AACrF,IAAA,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,MAAM;;;AAGnG,IAAA,MAAM,UAAU,IAAI,MAAM,CAAC,UAAU;QACnC,sBAAsB,CAAC,MAAM,CAAC,YAAY,IAAI,+BAA+B,CAAC,CAAkC;AAElH,IAAA,OAAO,OAAO,KAA+B,KAAI;QAC/C,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK;;AAG7C,QAAA,MAAM,WAAW,GAAgB;YAC/B,GAAG,uBAAuB,CAAC,GAAG,CAAC;AAC/B,YAAA,GAAG,kBAAkB,GAAG,KAAK,CAAC;SAC/B;AAED,QAAA,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;;AAGpC,QAAA,IAAI,MAAe;AACnB,QAAA,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;;YAEvC,MAAM,qBAAqB,GAA4B,EAAE;;YAEzD,IAAI,gBAAgB,EAAE,MAAM;gBAAE,qBAAqB,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC;;AAEjG,YAAA,IAAI,MAAM,IAAI,WAAW,EAAE,MAAM;gBAAE,qBAAqB,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;;AAEzF,YAAA,MAAM,GAAG,aAAa,CAAC,qBAAqB,EAAE,IAAI,CAAC;QACrD;;YAAO,MAAM,GAAG,aAAa;AAE7B,QAAA,MAAM,cAAc,GAAgD;YAClE,WAAW;YACX,MAAM;YACN,IAAI;AACJ,YAAA,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB;QAED,MAAM,uBAAuB,GAAG,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,IAAI,MAAM,cAAc,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,cAAc;AAElI,QAAA,OAAO,uBAAiF;AAC1F,IAAA,CAAC;AACH;AAEA,MAAM,sBAAsB,GAC1B,CAAC,YAAsC,KACvC,CAAC,KAAK,KAAI;IACR,IAAI,CAAC,KAAK,CAAC,MAAM;AAAE,QAAA,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;AACpD,IAAA,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3E,CAAC;;;;"}
|
|
@@ -2,8 +2,8 @@ import type { Logger } from '@makerx/node-common';
|
|
|
2
2
|
import type { GraphQLSchema } from 'graphql';
|
|
3
3
|
import type { Server } from 'http';
|
|
4
4
|
import type { GraphQLContext, JwtPayload } from '../context';
|
|
5
|
-
import type { CreateSubscriptionContext } from './context';
|
|
6
|
-
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
|
|
5
|
+
import type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context';
|
|
6
|
+
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, extractToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
|
|
7
7
|
schema: GraphQLSchema;
|
|
8
8
|
httpServer: Server;
|
|
9
9
|
createSubscriptionContext: CreateSubscriptionContext;
|
|
@@ -11,6 +11,12 @@ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>(
|
|
|
11
11
|
operationLogLevel?: keyof TLogger;
|
|
12
12
|
path?: string;
|
|
13
13
|
verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds
|
|
16
|
+
* to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`
|
|
17
|
+
* connection parameter. Pass the same function to `createSubscriptionContextFactory`.
|
|
18
|
+
*/
|
|
19
|
+
extractToken?: ExtractSubscriptionToken;
|
|
14
20
|
requireAuth?: boolean;
|
|
15
21
|
jwtClaimsToLog?: string[];
|
|
16
22
|
resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
|
|
@@ -2,8 +2,8 @@ import type { Logger } from '@makerx/node-common';
|
|
|
2
2
|
import type { GraphQLSchema } from 'graphql';
|
|
3
3
|
import type { Server } from 'http';
|
|
4
4
|
import type { GraphQLContext, JwtPayload } from '../context.mjs';
|
|
5
|
-
import type { CreateSubscriptionContext } from './context.mjs';
|
|
6
|
-
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
|
|
5
|
+
import type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context.mjs';
|
|
6
|
+
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, extractToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
|
|
7
7
|
schema: GraphQLSchema;
|
|
8
8
|
httpServer: Server;
|
|
9
9
|
createSubscriptionContext: CreateSubscriptionContext;
|
|
@@ -11,6 +11,12 @@ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>(
|
|
|
11
11
|
operationLogLevel?: keyof TLogger;
|
|
12
12
|
path?: string;
|
|
13
13
|
verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds
|
|
16
|
+
* to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`
|
|
17
|
+
* connection parameter. Pass the same function to `createSubscriptionContextFactory`.
|
|
18
|
+
*/
|
|
19
|
+
extractToken?: ExtractSubscriptionToken;
|
|
14
20
|
requireAuth?: boolean;
|
|
15
21
|
jwtClaimsToLog?: string[];
|
|
16
22
|
resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
|
|
@@ -2,8 +2,8 @@ import type { Logger } from '@makerx/node-common';
|
|
|
2
2
|
import type { GraphQLSchema } from 'graphql';
|
|
3
3
|
import type { Server } from 'http';
|
|
4
4
|
import type { GraphQLContext, JwtPayload } from '../context';
|
|
5
|
-
import type { CreateSubscriptionContext } from './context';
|
|
6
|
-
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
|
|
5
|
+
import type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context';
|
|
6
|
+
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, extractToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, includeDeprecatedElements, }: {
|
|
7
7
|
schema: GraphQLSchema;
|
|
8
8
|
httpServer: Server;
|
|
9
9
|
createSubscriptionContext: CreateSubscriptionContext;
|
|
@@ -11,6 +11,12 @@ export declare function useSubscriptionsServer<TLogger extends Logger = Logger>(
|
|
|
11
11
|
operationLogLevel?: keyof TLogger;
|
|
12
12
|
path?: string;
|
|
13
13
|
verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds
|
|
16
|
+
* to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`
|
|
17
|
+
* connection parameter. Pass the same function to `createSubscriptionContextFactory`.
|
|
18
|
+
*/
|
|
19
|
+
extractToken?: ExtractSubscriptionToken;
|
|
14
20
|
requireAuth?: boolean;
|
|
15
21
|
jwtClaimsToLog?: string[];
|
|
16
22
|
resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
|
package/subscriptions/server.js
CHANGED
|
@@ -7,7 +7,7 @@ var ws = require('ws');
|
|
|
7
7
|
var logging = require('../logging.js');
|
|
8
8
|
var utils = require('./utils.js');
|
|
9
9
|
|
|
10
|
-
function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
|
|
10
|
+
function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, extractToken = utils.defaultExtractSubscriptionToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
|
|
11
11
|
if (requireAuth && !verifyToken)
|
|
12
12
|
throw new Error('verifyToken must be supplied when requireAuth is true');
|
|
13
13
|
const wsServer = new ws.WebSocketServer({
|
|
@@ -25,10 +25,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
|
|
|
25
25
|
logger.info(connectionEstablished);
|
|
26
26
|
return true;
|
|
27
27
|
}
|
|
28
|
-
const token =
|
|
28
|
+
const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
|
|
29
29
|
if (!token) {
|
|
30
30
|
if (requireAuth) {
|
|
31
|
-
logger.error('No
|
|
31
|
+
logger.error('No auth token was supplied with the websocket connection');
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
34
|
logger.info(connectionEstablished);
|
|
@@ -50,10 +50,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
|
|
|
50
50
|
onSubscribe: async (ctx) => {
|
|
51
51
|
if (!verifyToken)
|
|
52
52
|
return;
|
|
53
|
-
const token =
|
|
53
|
+
const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
|
|
54
54
|
if (!token) {
|
|
55
55
|
if (requireAuth) {
|
|
56
|
-
logger.error('No
|
|
56
|
+
logger.error('No auth token was supplied with the websocket connection');
|
|
57
57
|
ctx.extra.socket.close(graphqlWs.CloseCode.Forbidden, 'Forbidden');
|
|
58
58
|
}
|
|
59
59
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext } from './context'\nimport {
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context'\nimport { defaultExtractSubscriptionToken, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n extractToken = defaultExtractSubscriptionToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n /**\n * Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds\n * to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`\n * connection parameter. Pass the same function to `createSubscriptionContextFactory`.\n */\n extractToken?: ExtractSubscriptionToken\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n /**\n * If true, deprecated schema elements a subscription uses are collected and logged when it is\n * established. Not collected per emitted payload: the usage is a property of the operation.\n */\n includeDeprecatedElements?: boolean\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({\n id,\n args,\n logLevel: operationLogLevel,\n resolveLogger: resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":["defaultExtractSubscriptionToken","WebSocketServer","useServer","getHost","pick","CloseCode","logSubscriptionOperation"],"mappings":"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"}
|
package/subscriptions/server.mjs
CHANGED
|
@@ -3,9 +3,9 @@ import { CloseCode } from 'graphql-ws';
|
|
|
3
3
|
import { useServer } from 'graphql-ws/use/ws';
|
|
4
4
|
import { WebSocketServer } from 'ws';
|
|
5
5
|
import { logSubscriptionOperation } from '../logging.mjs';
|
|
6
|
-
import {
|
|
6
|
+
import { defaultExtractSubscriptionToken, getHost } from './utils.mjs';
|
|
7
7
|
|
|
8
|
-
function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
|
|
8
|
+
function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel = 'info', path = '/graphql', verifyToken, extractToken = defaultExtractSubscriptionToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], resolveSubscriptionOperationLogger, includeDeprecatedElements, }) {
|
|
9
9
|
if (requireAuth && !verifyToken)
|
|
10
10
|
throw new Error('verifyToken must be supplied when requireAuth is true');
|
|
11
11
|
const wsServer = new WebSocketServer({
|
|
@@ -23,10 +23,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
|
|
|
23
23
|
logger.info(connectionEstablished);
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
|
-
const token =
|
|
26
|
+
const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
|
|
27
27
|
if (!token) {
|
|
28
28
|
if (requireAuth) {
|
|
29
|
-
logger.error('No
|
|
29
|
+
logger.error('No auth token was supplied with the websocket connection');
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
32
|
logger.info(connectionEstablished);
|
|
@@ -48,10 +48,10 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
|
|
|
48
48
|
onSubscribe: async (ctx) => {
|
|
49
49
|
if (!verifyToken)
|
|
50
50
|
return;
|
|
51
|
-
const token =
|
|
51
|
+
const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams });
|
|
52
52
|
if (!token) {
|
|
53
53
|
if (requireAuth) {
|
|
54
|
-
logger.error('No
|
|
54
|
+
logger.error('No auth token was supplied with the websocket connection');
|
|
55
55
|
ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden');
|
|
56
56
|
}
|
|
57
57
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext } from './context'\nimport {
|
|
1
|
+
{"version":3,"file":"server.mjs","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext, ExtractSubscriptionToken } from './context'\nimport { defaultExtractSubscriptionToken, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n extractToken = defaultExtractSubscriptionToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n /**\n * Returns the token to pass to `verifyToken`, e.g. from a JWT assertion header that a proxy adds\n * to the connect request. Defaults to the bearer token in the `authorization` or `Authorization`\n * connection parameter. Pass the same function to `createSubscriptionContextFactory`.\n */\n extractToken?: ExtractSubscriptionToken\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n /**\n * If true, deprecated schema elements a subscription uses are collected and logged when it is\n * established. Not collected per emitted payload: the usage is a property of the operation.\n */\n includeDeprecatedElements?: boolean\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractToken({ connectRequest: ctx.extra.request, connectionParams: ctx.connectionParams })\n if (!token) {\n if (requireAuth) {\n logger.error('No auth token was supplied with the websocket connection')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({\n id,\n args,\n logLevel: operationLogLevel,\n resolveLogger: resolveSubscriptionOperationLogger,\n includeDeprecatedElements,\n })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'http';
|
|
2
|
+
import type { ExtractSubscriptionToken } from './context';
|
|
2
3
|
/**
|
|
3
4
|
* Extracts a token from a connection parameter named `authorization` or `Authorization`.
|
|
4
5
|
* The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
|
|
@@ -7,4 +8,5 @@ import type { IncomingMessage } from 'http';
|
|
|
7
8
|
* @returns
|
|
8
9
|
*/
|
|
9
10
|
export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
|
|
11
|
+
export declare const defaultExtractSubscriptionToken: ExtractSubscriptionToken;
|
|
10
12
|
export declare function getHost(request: IncomingMessage): string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'http';
|
|
2
|
+
import type { ExtractSubscriptionToken } from './context.mjs';
|
|
2
3
|
/**
|
|
3
4
|
* Extracts a token from a connection parameter named `authorization` or `Authorization`.
|
|
4
5
|
* The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
|
|
@@ -7,4 +8,5 @@ import type { IncomingMessage } from 'http';
|
|
|
7
8
|
* @returns
|
|
8
9
|
*/
|
|
9
10
|
export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
|
|
11
|
+
export declare const defaultExtractSubscriptionToken: ExtractSubscriptionToken;
|
|
10
12
|
export declare function getHost(request: IncomingMessage): string;
|
package/subscriptions/utils.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'http';
|
|
2
|
+
import type { ExtractSubscriptionToken } from './context';
|
|
2
3
|
/**
|
|
3
4
|
* Extracts a token from a connection parameter named `authorization` or `Authorization`.
|
|
4
5
|
* The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
|
|
@@ -7,4 +8,5 @@ import type { IncomingMessage } from 'http';
|
|
|
7
8
|
* @returns
|
|
8
9
|
*/
|
|
9
10
|
export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
|
|
11
|
+
export declare const defaultExtractSubscriptionToken: ExtractSubscriptionToken;
|
|
10
12
|
export declare function getHost(request: IncomingMessage): string;
|
package/subscriptions/utils.js
CHANGED
|
@@ -13,12 +13,14 @@ function extractTokenFromConnectionParams(connectionParams) {
|
|
|
13
13
|
return undefined;
|
|
14
14
|
return bearerTokenValue.substring(7);
|
|
15
15
|
}
|
|
16
|
+
const defaultExtractSubscriptionToken = ({ connectionParams }) => extractTokenFromConnectionParams(connectionParams);
|
|
16
17
|
function getHost(request) {
|
|
17
18
|
const proxyHostHeader = request.headers['x-forwarded-host'];
|
|
18
19
|
const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined);
|
|
19
20
|
return host ?? 'subscriptions';
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
exports.defaultExtractSubscriptionToken = defaultExtractSubscriptionToken;
|
|
22
24
|
exports.extractTokenFromConnectionParams = extractTokenFromConnectionParams;
|
|
23
25
|
exports.getHost = getHost;
|
|
24
26
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\nimport type { ExtractSubscriptionToken } from './context'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport const defaultExtractSubscriptionToken: ExtractSubscriptionToken = ({ connectionParams }) =>\n extractTokenFromConnectionParams(connectionParams)\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":";;AAGA;;;;;;AAMG;AACG,SAAU,gCAAgC,CAAC,gBAAoD,EAAA;IACnG,MAAM,gBAAgB,IAAI,gBAAgB,EAAE,aAAa,IAAI,gBAAgB,EAAE,aAAa,CAAuB;AACnH,IAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,SAAS,CAAC;AAAE,QAAA,OAAO,SAAS;AAC9D,IAAA,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACtC;AAEO,MAAM,+BAA+B,GAA6B,CAAC,EAAE,gBAAgB,EAAE,KAC5F,gCAAgC,CAAC,gBAAgB;AAE7C,SAAU,OAAO,CAAC,OAAwB,EAAA;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC;AAC3D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACxI,OAAO,IAAI,IAAI,eAAe;AAChC;;;;;;"}
|
package/subscriptions/utils.mjs
CHANGED
|
@@ -11,11 +11,12 @@ function extractTokenFromConnectionParams(connectionParams) {
|
|
|
11
11
|
return undefined;
|
|
12
12
|
return bearerTokenValue.substring(7);
|
|
13
13
|
}
|
|
14
|
+
const defaultExtractSubscriptionToken = ({ connectionParams }) => extractTokenFromConnectionParams(connectionParams);
|
|
14
15
|
function getHost(request) {
|
|
15
16
|
const proxyHostHeader = request.headers['x-forwarded-host'];
|
|
16
17
|
const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined);
|
|
17
18
|
return host ?? 'subscriptions';
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
export { extractTokenFromConnectionParams, getHost };
|
|
21
|
+
export { defaultExtractSubscriptionToken, extractTokenFromConnectionParams, getHost };
|
|
21
22
|
//# sourceMappingURL=utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.mjs","sources":["../../src/subscriptions/utils.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\nimport type { ExtractSubscriptionToken } from './context'\n\n/**\n * Extracts a token from a connection parameter named `authorization` or `Authorization`.\n * The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.\n * Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.\n * @param connectionParams\n * @returns\n */\nexport function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>) {\n const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization) as string | undefined\n if (!bearerTokenValue?.startsWith('Bearer ')) return undefined\n return bearerTokenValue.substring(7)\n}\n\nexport const defaultExtractSubscriptionToken: ExtractSubscriptionToken = ({ connectionParams }) =>\n extractTokenFromConnectionParams(connectionParams)\n\nexport function getHost(request: IncomingMessage) {\n const proxyHostHeader = request.headers['x-forwarded-host']\n const host = Array.isArray(proxyHostHeader) ? (proxyHostHeader[0] ?? undefined) : (proxyHostHeader ?? request.headers.host ?? undefined)\n return host ?? 'subscriptions'\n}\n"],"names":[],"mappings":"AAGA;;;;;;AAMG;AACG,SAAU,gCAAgC,CAAC,gBAAoD,EAAA;IACnG,MAAM,gBAAgB,IAAI,gBAAgB,EAAE,aAAa,IAAI,gBAAgB,EAAE,aAAa,CAAuB;AACnH,IAAA,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,SAAS,CAAC;AAAE,QAAA,OAAO,SAAS;AAC9D,IAAA,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC;AACtC;AAEO,MAAM,+BAA+B,GAA6B,CAAC,EAAE,gBAAgB,EAAE,KAC5F,gCAAgC,CAAC,gBAAgB;AAE7C,SAAU,OAAO,CAAC,OAAwB,EAAA;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC;AAC3D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACxI,OAAO,IAAI,IAAI,eAAe;AAChC;;;;"}
|