@makerx/graphql-core 1.0.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 +305 -0
- package/User.d.ts +14 -0
- package/User.d.ts.map +1 -0
- package/User.js +49 -0
- package/User.js.map +1 -0
- package/context.d.ts +68 -0
- package/context.d.ts.map +1 -0
- package/context.js +79 -0
- package/context.js.map +1 -0
- package/index.d.ts +6 -0
- package/index.d.ts.map +1 -0
- package/index.js +22 -0
- package/index.js.map +1 -0
- package/logging.d.ts +21 -0
- package/logging.d.ts.map +1 -0
- package/logging.js +38 -0
- package/logging.js.map +1 -0
- package/package.json +43 -0
- package/subscriptions/context.d.ts +23 -0
- package/subscriptions/context.d.ts.map +1 -0
- package/subscriptions/context.js +63 -0
- package/subscriptions/context.js.map +1 -0
- package/subscriptions/index.d.ts +4 -0
- package/subscriptions/index.d.ts.map +1 -0
- package/subscriptions/index.js +22 -0
- package/subscriptions/index.js.map +1 -0
- package/subscriptions/server.d.ts +17 -0
- package/subscriptions/server.d.ts.map +1 -0
- package/subscriptions/server.js +71 -0
- package/subscriptions/server.js.map +1 -0
- package/subscriptions/utils.d.ts +12 -0
- package/subscriptions/utils.d.ts.map +1 -0
- package/subscriptions/utils.js +24 -0
- package/subscriptions/utils.js.map +1 -0
- package/testing.d.ts +7 -0
- package/testing.d.ts.map +1 -0
- package/testing.js +81 -0
- package/testing.js.map +1 -0
- package/utils.d.ts +4 -0
- package/utils.d.ts.map +1 -0
- package/utils.js +10 -0
- package/utils.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# GraphQL Core
|
|
2
|
+
|
|
3
|
+
A set of core GraphQL utilities that MakerX uses to build GraphQL APIs.
|
|
4
|
+
|
|
5
|
+
These utilities avoid dependencies on any particular GraphQL server or logging implementation, providing a standard set of behaviours to use across varying implementations.
|
|
6
|
+
|
|
7
|
+
Note: See explanation on \*Express peer dependency below.
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
`createContextFactory` returns a function that creates your GraphQL context using a standard (extensible) representation, including:
|
|
12
|
+
|
|
13
|
+
- `logger`: a logger instance to use downstream of resolvers, usually logging some request metadata to assist correlating log entries (for example the X-Correlation-Id header value)
|
|
14
|
+
- `requestInfo`: useful request info, for example to define per-request behaviour (multi-tenant apps), pass through correlation headers to downstream services etc
|
|
15
|
+
- `user`: an object representing the user or system identity (see definition below, defaults to creating a `User` based on JWT claims)
|
|
16
|
+
- anything else you wish to add to the context
|
|
17
|
+
|
|
18
|
+
### Step 1 - Define your context + creation
|
|
19
|
+
|
|
20
|
+
context.ts
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// define the base context type, setting the logger type
|
|
24
|
+
type BaseContext = GraphQLContextBase<Logger>
|
|
25
|
+
// define the extra stuff added to our app's context
|
|
26
|
+
type ExtraContext = {
|
|
27
|
+
services: Services
|
|
28
|
+
loaders: Loaders
|
|
29
|
+
}
|
|
30
|
+
// our app's context type, returned from the createContext function
|
|
31
|
+
export type GraphQLContext = BaseContext & ExtraContext
|
|
32
|
+
|
|
33
|
+
// configure the createContext function
|
|
34
|
+
export const createContext = createContextFactory<GraphQLContext>({
|
|
35
|
+
// set the keys of the user claims (JWT payload) we want added to the request metadata passed to the requestLogger factory
|
|
36
|
+
claimsToLog: ['oid', 'aud', 'tid', 'azp', 'iss', 'scp', 'roles'],
|
|
37
|
+
// set the keys of the request info we want added to the request metadata passed to the requestLogger factory
|
|
38
|
+
requestInfoToLog: ['origin', 'requestId', 'correlationId'],
|
|
39
|
+
// use a winston child logger to add metadata to log output
|
|
40
|
+
requestLogger: (requestMetadata) => logger.child(requestMetadata),
|
|
41
|
+
// build the rest of the app context
|
|
42
|
+
augmentContext: (context): ExtraContext => {
|
|
43
|
+
const services = createServices(context)
|
|
44
|
+
const loaders = createLoaders(services)
|
|
45
|
+
return { services, loaders }
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Step 2 - Map the context creation to implementation
|
|
51
|
+
|
|
52
|
+
These examples show how you might map implementation-specific context functions to your implementation-agnostic context creation function (from step 1).
|
|
53
|
+
Note: examples assume that a JWT auth middleware has set `req.user` to the decoded token payload (claims). This is optional.
|
|
54
|
+
|
|
55
|
+
app.ts
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
// wire up the createContext function, providing `ContextInput` for apollo-server-express implementation)
|
|
59
|
+
const server = new ApolloServer({
|
|
60
|
+
...apolloServerConfig,
|
|
61
|
+
context: ({ req }) => createContext({ req, claims: req.user }),
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
lambda.ts
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// wire up the createContext function, providing `ContextInput` for apollo-server-lambda implementation
|
|
69
|
+
const server = new ApolloServer({
|
|
70
|
+
...apolloServerConfig,
|
|
71
|
+
context: ({ event, context, express: { req } }) =>
|
|
72
|
+
createContext({ req, claims: req.user, event: event as LambdaEvent, context: context as LambdaContext }),
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
yoga.ts
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// wire up the createContext function, providing `ContextInput` for graphql-yoga implementation
|
|
80
|
+
const graphqlServer = createServer({
|
|
81
|
+
...yogaServerConfig,
|
|
82
|
+
context: ({ req }) => createContext({ req, claims: req.user }),
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## User
|
|
87
|
+
|
|
88
|
+
By default, if `claims` (decoded token `JwtPayload`) are available, the `GraphQLContext.user` property will be set by constructing a `User` instance.
|
|
89
|
+
|
|
90
|
+
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.
|
|
91
|
+
|
|
92
|
+
| Property | Description |
|
|
93
|
+
| -------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| claims | The decoded JWT payload, set via the RequestInput.user field. |
|
|
95
|
+
| token | The bearer token from the request authorization header. |
|
|
96
|
+
| email | The user's email via coalesced claim values: email, emails, preferred_username, unique_name, upn. |
|
|
97
|
+
| name | The user's name (via the name or given_name and family_name claims). |
|
|
98
|
+
| id | The user's unique and immutable ID, useful for contextual differentiation e.g. session keys (the oid or sub claim). |
|
|
99
|
+
| scopes | The user's scopes, via the scp claim split into an array of scopes. |
|
|
100
|
+
| roles | The user's roles (via the roles claim). |
|
|
101
|
+
|
|
102
|
+
### Custom user
|
|
103
|
+
|
|
104
|
+
If you wish to customise your `GraphQLContext.user` object, provide a `createUser` function to override the default `User` creation.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const createUser: CreateUser = async ({ claims }) => {
|
|
108
|
+
const roles = await loadRoles(claims.email)
|
|
109
|
+
return { name: claims.name, email: claims.email, roles }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const graphqlServer = createServer({
|
|
113
|
+
...config,
|
|
114
|
+
context: ({ req }) => createContext({ req, claims: req.user, createUser }),
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Logging
|
|
119
|
+
|
|
120
|
+
### logGraphQLOperation
|
|
121
|
+
|
|
122
|
+
The `logGraphQLOperation` function will log:
|
|
123
|
+
|
|
124
|
+
- `operationName`: the operation name, if supplied
|
|
125
|
+
- `query`: the formatted graphql query or mutation, or 'IntrospectionQuery'
|
|
126
|
+
- `variables`: the graphql request variables
|
|
127
|
+
- `duration`: the duration of request processing
|
|
128
|
+
- `errors`: the graphql response errors, if any
|
|
129
|
+
|
|
130
|
+
Notes:
|
|
131
|
+
|
|
132
|
+
- null or undefined values will be omitted
|
|
133
|
+
- introspection queries will not be logged outside of production
|
|
134
|
+
|
|
135
|
+
This function can be used across implementations, e.g. in a [GraphQL Envelop plugin](https://www.envelop.dev/docs/plugins) or [ApolloServer plugin](https://www.apollographql.com/docs/apollo-server/integrations/plugins/).
|
|
136
|
+
|
|
137
|
+
### logGraphQLExecutionArgs
|
|
138
|
+
|
|
139
|
+
The `logGraphQLExecutionArgs` will log `operationName`, `query` and `variables` params from the GraphQL `ExecutionArgs`.
|
|
140
|
+
|
|
141
|
+
This can be used when logging from execution callbacks, e.g. graphql-ws Server [onOperation](https://the-guild.dev/graphql/ws/docs/interfaces/server.ServerOptions#onoperation) and [onNext](https://the-guild.dev/graphql/ws/docs/interfaces/server.ServerOptions#onnext).
|
|
142
|
+
|
|
143
|
+
## GraphQL subscriptions
|
|
144
|
+
|
|
145
|
+
This library includes a `subscriptions` module to provide simple setup using the [GraphQL WS](https://the-guild.dev/graphql/ws) package.
|
|
146
|
+
|
|
147
|
+
1. Install subscriptions dependencies (optional peer dependencies of this package):
|
|
148
|
+
```
|
|
149
|
+
npm i graphql-ws ws
|
|
150
|
+
```
|
|
151
|
+
1. Subscription context setup:
|
|
152
|
+
|
|
153
|
+
`createSubscriptionContextFactory` returns a function that creates an equivalent GraphQL context using input supplied to the [graphql-ws Server context callback](https://the-guild.dev/graphql/ws/docs/interfaces/server.ServerOptions#onconnect).
|
|
154
|
+
|
|
155
|
+
Example showing both normal context + subscription context creation:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const augmentContext = (context: GraphQLContext) => {
|
|
159
|
+
const services = createServices(context)
|
|
160
|
+
const dataLoaders = createDataLoaders()
|
|
161
|
+
return { services, dataSource, dataLoaders }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// create a context using request based input
|
|
165
|
+
const createContext = createContextFactory<GraphQLContext>({
|
|
166
|
+
claimsToLog,
|
|
167
|
+
requestInfoToLog,
|
|
168
|
+
requestLogger: (requestMetadata) => logger.child(requestMetadata),
|
|
169
|
+
createUser: ({ claims, req }) => findUpdateOrCreateUser(claims, req.headers.authorization?.substring(7)),
|
|
170
|
+
augmentContext,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// create a context using graphql-ws Server#context callback input
|
|
174
|
+
const createSubscriptionContext = createSubscriptionContextFactory<GraphQLContext>({
|
|
175
|
+
claimsToLog
|
|
176
|
+
requestInfoToLog,
|
|
177
|
+
requestLogger: (requestMetadata) => logger.child(requestMetadata),
|
|
178
|
+
createUser: ({ claims, connectionParams }) => findUpdateOrCreateUser(claims, extractTokenFromConnectionParams(connectionParams)),
|
|
179
|
+
augmentContext,
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
1. Create a subscriptions server, using the ws-server cleanup function in your server lifecycle.
|
|
184
|
+
|
|
185
|
+
The `useSubscriptionsServer` function sets up:
|
|
186
|
+
|
|
187
|
+
- Auth token validation as part of establishing (or rejecting) the connection (behaviour defined by `verifyToken` and `requireAuth` args)
|
|
188
|
+
- GraphQL context creation
|
|
189
|
+
- Logging from the server `onConnect`, `onDisconnect`, `onOperation`, `onNext` and `onError` callbacks
|
|
190
|
+
|
|
191
|
+
Example for Apollo Server (`wsServerCleanup` called in the `drainServer` plugin callback):
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
export const startApolloServer = async (app: Express, httpServer: http.Server) => {
|
|
195
|
+
logger.info('Building schema')
|
|
196
|
+
const schema = createSchema()
|
|
197
|
+
|
|
198
|
+
logger.info('Initialising subscriptions websocket server')
|
|
199
|
+
const wsServerCleanup = useSubscriptionsServer({
|
|
200
|
+
schema,
|
|
201
|
+
httpServer,
|
|
202
|
+
logger,
|
|
203
|
+
createSubscriptionContext,
|
|
204
|
+
jwtClaimsToLog: config.get('logging.userClaimsToLog'),
|
|
205
|
+
requireAuth: true,
|
|
206
|
+
verifyToken: (host, token) => verifyForHost(host, token, config.get('auth.bearer')),
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
logger.info('Starting apollo server')
|
|
210
|
+
const server = new ApolloServer<GraphQLContext>({
|
|
211
|
+
schema,
|
|
212
|
+
plugins: plugins(httpServer, wsServerCleanup),
|
|
213
|
+
introspection: true,
|
|
214
|
+
csrfPrevention: true,
|
|
215
|
+
})
|
|
216
|
+
await server.start()
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
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.
|
|
221
|
+
|
|
222
|
+
## Testing
|
|
223
|
+
|
|
224
|
+
The testing submodule exports utility functions for easily constructing ApolloClient instances for integration testing on NodeJS. The `errorPolicy` is set to `all` so that returned errors can be checked.
|
|
225
|
+
|
|
226
|
+
- `createTestClient` accepts a url and optional accessToken.
|
|
227
|
+
- `createTestClientWithClientCredentials` accepts a url and client credentials config and will fetch and attach an access token to each request.
|
|
228
|
+
|
|
229
|
+
testing.ts
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
export const testClient = createTestClientWithClientCredentials(process.env.INTEGRATION_TEST_URL, clientCredentialsConfig)
|
|
233
|
+
|
|
234
|
+
export const unauthenticatedClient = createTestClient(process.env.INTEGRATION_TEST_URL)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
tweets.spec.ts
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
describe('tweets query', () => {
|
|
241
|
+
const tweetsQuery = gql`
|
|
242
|
+
query Tweets($input: TweetsWhere) {
|
|
243
|
+
tweets(input: $input) {
|
|
244
|
+
data {
|
|
245
|
+
text
|
|
246
|
+
createdAt
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
`
|
|
251
|
+
|
|
252
|
+
it('returns tweets with sensible default limit', async () => {
|
|
253
|
+
const {
|
|
254
|
+
data: { tweets },
|
|
255
|
+
errors,
|
|
256
|
+
} = await testClient.query<TweetsQuery>({
|
|
257
|
+
query: tweetsQuery,
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
expect(errors).toBeUndefined()
|
|
261
|
+
expect(tweets).toBeDefined()
|
|
262
|
+
expect(tweets?.data?.length).toBe(10)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('guards against high limit', async () => {
|
|
266
|
+
const tooHighLimit = 101
|
|
267
|
+
await expect(async () => {
|
|
268
|
+
await testClient.query<TweetsQuery, TweetsQueryVariables>({
|
|
269
|
+
query: tweetsQuery,
|
|
270
|
+
variables: {
|
|
271
|
+
input: {
|
|
272
|
+
maxResults: tooHighLimit,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
})
|
|
276
|
+
}).rejects.toThrowErrorMatchingInlineSnapshot(`"Response not successful: Received status code 400"`)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('requires authorisation', async () => {
|
|
280
|
+
const { data, errors } = await unauthenticatedClient.query<TweetsQuery, TweetsQueryVariables>({
|
|
281
|
+
query: tweetsQuery,
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
expect(data.tweets).toBeNull()
|
|
285
|
+
expect(errors?.length).toBe(1)
|
|
286
|
+
expect(errors?.[0].message).toMatchInlineSnapshot(`"User is not authorized to access Query.tweets"`)
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Utils
|
|
292
|
+
|
|
293
|
+
- `isIntrospectionQuery`: indicates whether the query is an introspection query, based on the operation name or query content.
|
|
294
|
+
|
|
295
|
+
## \*Express peer dependency
|
|
296
|
+
|
|
297
|
+
ApolloServer v3 standardises on the Express request representation.
|
|
298
|
+
|
|
299
|
+
GraphQL Yoga uses the NodeJS http request representation, plus adds the Express version when using an Express server.
|
|
300
|
+
|
|
301
|
+
This library therefore takes a peer dependency on Express as the standard (common) request representation.
|
|
302
|
+
|
|
303
|
+
The ApolloServer [v4 roadmap](https://github.com/apollographql/apollo-server/blob/main/ROADMAP.md#replace-9-core-maintained-bindings-with-a-stable-http-abstraction) will standardise on the NodeJS http request representation.
|
|
304
|
+
|
|
305
|
+
This library may swap to the NodeJS http representation in a future version.
|
package/User.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { JwtPayload } from './context';
|
|
2
|
+
export declare class User {
|
|
3
|
+
readonly token: string;
|
|
4
|
+
readonly claims: JwtPayload;
|
|
5
|
+
constructor(claims: JwtPayload, accessToken: string);
|
|
6
|
+
get email(): string | undefined;
|
|
7
|
+
get name(): string | undefined;
|
|
8
|
+
get id(): string;
|
|
9
|
+
get scopes(): string[];
|
|
10
|
+
get roles(): string[];
|
|
11
|
+
private first;
|
|
12
|
+
private firstOrThrow;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=User.d.ts.map
|
package/User.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../src/User.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAGtC,qBAAa,IAAI;IACf,SAAgB,KAAK,EAAE,MAAM,CAAA;IAC7B,SAAgB,MAAM,EAAE,UAAU,CAAA;gBAEtB,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM;IAKnD,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAQ9B;IAED,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAK7B;IAED,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,MAAM,IAAI,MAAM,EAAE,CAErB;IAED,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,YAAY;CAKrB"}
|
package/User.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.User = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
class User {
|
|
6
|
+
constructor(claims, accessToken) {
|
|
7
|
+
this.claims = claims;
|
|
8
|
+
this.token = accessToken;
|
|
9
|
+
}
|
|
10
|
+
get email() {
|
|
11
|
+
return (this.claims.email ??
|
|
12
|
+
this.claims.emails?.[0] ??
|
|
13
|
+
this.claims.preferred_username ??
|
|
14
|
+
this.claims.unique_name ??
|
|
15
|
+
this.claims.upn);
|
|
16
|
+
}
|
|
17
|
+
get name() {
|
|
18
|
+
if (this.claims.name)
|
|
19
|
+
return this.claims.name;
|
|
20
|
+
const otherNames = [this.claims.given_name, this.claims.family_name].filter(utils_1.isNotNil);
|
|
21
|
+
if (otherNames.length > 0)
|
|
22
|
+
return otherNames.join(' ');
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
get id() {
|
|
26
|
+
return this.firstOrThrow('oid', 'sub');
|
|
27
|
+
}
|
|
28
|
+
get scopes() {
|
|
29
|
+
return this.claims.scp ? this.claims.scp.split(' ') : [];
|
|
30
|
+
}
|
|
31
|
+
get roles() {
|
|
32
|
+
return this.claims.roles ?? [];
|
|
33
|
+
}
|
|
34
|
+
first(...claims) {
|
|
35
|
+
for (const key of claims) {
|
|
36
|
+
if (this.claims[key])
|
|
37
|
+
return this.claims[key];
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
firstOrThrow(...claims) {
|
|
42
|
+
const first = this.first(...claims);
|
|
43
|
+
if (!first)
|
|
44
|
+
throw new Error(`claims do not include any of: ${claims.join(', ')}`);
|
|
45
|
+
return first;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.User = User;
|
|
49
|
+
//# sourceMappingURL=User.js.map
|
package/User.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"User.js","sourceRoot":"","sources":["../src/User.ts"],"names":[],"mappings":";;;AACA,mCAAkC;AAElC,MAAa,IAAI;IAIf,YAAY,MAAkB,EAAE,WAAmB;QACjD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAA;IAC1B,CAAC;IAED,IAAI,KAAK;QACP,OAAO,CACL,IAAI,CAAC,MAAM,CAAC,KAAK;YAChB,IAAI,CAAC,MAAM,CAAC,MAA+B,EAAE,CAAC,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM,CAAC,kBAAkB;YAC9B,IAAI,CAAC,MAAM,CAAC,WAAW;YACvB,IAAI,CAAC,MAAM,CAAC,GAAG,CAChB,CAAA;IACH,CAAC;IAED,IAAI,IAAI;QACN,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAA;QAC7C,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,gBAAQ,CAAC,CAAA;QACrF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACtD,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,EAAE;QACJ,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC1D,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAA;IAChC,CAAC;IAEO,KAAK,CAAC,GAAG,MAA+B;QAC9C,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE;YACxB,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAW,CAAA;SACxD;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAEO,YAAY,CAAC,GAAG,MAA+B;QACrD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAA;QACnC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACjF,OAAO,KAAK,CAAA;IACd,CAAC;CACF;AAlDD,oBAkDC"}
|
package/context.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { Request } from 'express';
|
|
3
|
+
import { User } from './User';
|
|
4
|
+
export interface GraphQLContext<TLogger extends Logger = Logger, TRequestInfo extends BaseRequestInfo = RequestInfo, TUser = User | undefined> {
|
|
5
|
+
logger: TLogger;
|
|
6
|
+
requestInfo: TRequestInfo;
|
|
7
|
+
user: TUser;
|
|
8
|
+
started: number;
|
|
9
|
+
}
|
|
10
|
+
export interface BaseRequestInfo extends Record<string, unknown> {
|
|
11
|
+
requestId: string;
|
|
12
|
+
protocol: 'http' | 'https' | 'ws';
|
|
13
|
+
host: string;
|
|
14
|
+
method: string;
|
|
15
|
+
url: string;
|
|
16
|
+
origin: string;
|
|
17
|
+
referer?: string;
|
|
18
|
+
correlationId?: string;
|
|
19
|
+
arrLogId?: string;
|
|
20
|
+
clientIp?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface LambdaContext {
|
|
23
|
+
functionName?: string;
|
|
24
|
+
awsRequestId?: string;
|
|
25
|
+
}
|
|
26
|
+
export type LambdaEvent = never;
|
|
27
|
+
export type LambdaRequestInfo = BaseRequestInfo & LambdaContext;
|
|
28
|
+
export type RequestInfo = BaseRequestInfo | LambdaRequestInfo;
|
|
29
|
+
export interface JwtPayload {
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
name?: string | undefined;
|
|
32
|
+
email?: string | undefined;
|
|
33
|
+
given_name?: string | undefined;
|
|
34
|
+
family_name?: string | undefined;
|
|
35
|
+
preferred_username?: string | undefined;
|
|
36
|
+
upn?: string | undefined;
|
|
37
|
+
unique_name?: string | undefined;
|
|
38
|
+
oid?: string | undefined;
|
|
39
|
+
iss?: string | undefined;
|
|
40
|
+
sub?: string | undefined;
|
|
41
|
+
aud?: string | string[] | undefined;
|
|
42
|
+
roles?: string[] | undefined;
|
|
43
|
+
scp?: string | undefined;
|
|
44
|
+
exp?: number | undefined;
|
|
45
|
+
nbf?: number | undefined;
|
|
46
|
+
iat?: number | undefined;
|
|
47
|
+
jti?: string | undefined;
|
|
48
|
+
}
|
|
49
|
+
export type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T>;
|
|
50
|
+
export interface ContextInput {
|
|
51
|
+
req: Request;
|
|
52
|
+
claims?: JwtPayload;
|
|
53
|
+
context?: LambdaContext;
|
|
54
|
+
event?: LambdaEvent;
|
|
55
|
+
}
|
|
56
|
+
export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
|
|
57
|
+
export type CreateRequestLogger = (requestMetadata: Record<string, unknown>) => Logger;
|
|
58
|
+
export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
|
|
59
|
+
export interface CreateContextConfig<TContext = GraphQLContext> {
|
|
60
|
+
requestLogger: CreateRequestLogger | Logger;
|
|
61
|
+
augmentRequestInfo?: AugmentRequestInfo;
|
|
62
|
+
claimsToLog?: string[];
|
|
63
|
+
createUser?: CreateUser;
|
|
64
|
+
requestInfoToLog?: Array<keyof RequestInfo>;
|
|
65
|
+
augmentContext?: (context: TContext) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
66
|
+
}
|
|
67
|
+
export declare const createContextFactory: <TContext extends GraphQLContext<Logger, RequestInfo, User | undefined> = GraphQLContext<Logger, RequestInfo, User | undefined>>({ requestLogger, augmentRequestInfo, claimsToLog, createUser, requestInfoToLog, augmentContext, }: CreateContextConfig<TContext>) => CreateContext<TContext>;
|
|
68
|
+
//# sourceMappingURL=context.d.ts.map
|
package/context.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAE7B,MAAM,WAAW,cAAc,CAC7B,OAAO,SAAS,MAAM,GAAG,MAAM,EAC/B,YAAY,SAAS,eAAe,GAAG,WAAW,EAClD,KAAK,GAAG,IAAI,GAAG,SAAS;IAExB,MAAM,EAAE,OAAO,CAAA;IACf,WAAW,EAAE,YAAY,CAAA;IACzB,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAgB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9D,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AACD,MAAM,MAAM,WAAW,GAAG,KAAK,CAAA;AAC/B,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,aAAa,CAAA;AAC/D,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,iBAAiB,CAAA;AAG7D,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAA;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IAC5B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACzB;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AACtG,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,KAAK,CAAC,EAAE,WAAW,CAAA;CACpB;AACD,MAAM,MAAM,aAAa,CAAC,QAAQ,GAAG,cAAc,IAAI,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AACjG,MAAM,MAAM,mBAAmB,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAA;AACtF,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEjF,MAAM,WAAW,mBAAmB,CAAC,QAAQ,GAAG,cAAc;IAC5D,aAAa,EAAE,mBAAmB,GAAG,MAAM,CAAA;IAC3C,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,WAAW,CAAC,CAAA;IAC3C,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACnG;AAED,eAAO,MAAM,oBAAoB,+RAuEhC,CAAA"}
|
package/context.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createContextFactory = void 0;
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const lodash_pick_1 = __importDefault(require("lodash.pick"));
|
|
9
|
+
const User_1 = require("./User");
|
|
10
|
+
const createContextFactory = ({ requestLogger, augmentRequestInfo, claimsToLog, createUser = defaultCreateUser, requestInfoToLog, augmentContext, }) => {
|
|
11
|
+
// the function that creates the GraphQL context
|
|
12
|
+
return async (input) => {
|
|
13
|
+
const { req, claims, context } = input;
|
|
14
|
+
// build base request info from the request
|
|
15
|
+
const baseRequestInfo = {
|
|
16
|
+
requestId: req.headers['x-request-id']?.toString() ?? (0, crypto_1.randomUUID)(),
|
|
17
|
+
protocol: req.protocol,
|
|
18
|
+
host: req.get('Host') ?? '',
|
|
19
|
+
method: req.method ?? '',
|
|
20
|
+
url: req.originalUrl,
|
|
21
|
+
origin: req.get('Origin') ?? '',
|
|
22
|
+
referer: req.headers.referer?.toString() ?? '',
|
|
23
|
+
arrLogId: req.headers['x-arr-log-id']?.toString() ?? undefined,
|
|
24
|
+
clientIp: req.headers['x-forwarded-for']?.toString() ?? req.socket.remoteAddress,
|
|
25
|
+
correlationId: req.headers['x-correlation-id']?.toString() ?? undefined,
|
|
26
|
+
};
|
|
27
|
+
// add lambda info from the context, if present
|
|
28
|
+
let lambdaRequestInfo;
|
|
29
|
+
if (context) {
|
|
30
|
+
const { awsRequestId, functionName } = context;
|
|
31
|
+
lambdaRequestInfo = {
|
|
32
|
+
awsRequestId,
|
|
33
|
+
functionName,
|
|
34
|
+
...baseRequestInfo,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// augment request info with the supplied function, if present
|
|
38
|
+
let requestInfo = lambdaRequestInfo ?? baseRequestInfo;
|
|
39
|
+
if (augmentRequestInfo)
|
|
40
|
+
requestInfo = {
|
|
41
|
+
...requestInfo,
|
|
42
|
+
...augmentRequestInfo(input),
|
|
43
|
+
};
|
|
44
|
+
// create request logger
|
|
45
|
+
let logger;
|
|
46
|
+
if (typeof requestLogger === 'function') {
|
|
47
|
+
// build request logger metadata
|
|
48
|
+
const requestLoggerMetadata = {};
|
|
49
|
+
// add request info to log
|
|
50
|
+
if (requestInfoToLog?.length)
|
|
51
|
+
requestLoggerMetadata.request = (0, lodash_pick_1.default)(requestInfo, requestInfoToLog);
|
|
52
|
+
// add user claims to log
|
|
53
|
+
if (claims && claimsToLog?.length)
|
|
54
|
+
requestLoggerMetadata.user = (0, lodash_pick_1.default)(claims, claimsToLog);
|
|
55
|
+
// build the request logger
|
|
56
|
+
logger = requestLogger(requestLoggerMetadata);
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
logger = requestLogger;
|
|
60
|
+
const graphqlContext = {
|
|
61
|
+
requestInfo,
|
|
62
|
+
logger,
|
|
63
|
+
user: createUser ? await createUser(input) : undefined,
|
|
64
|
+
started: Date.now(),
|
|
65
|
+
};
|
|
66
|
+
const augmentedGraphQLContext = augmentContext
|
|
67
|
+
? { ...graphqlContext, ...(await augmentContext(graphqlContext)) }
|
|
68
|
+
: graphqlContext;
|
|
69
|
+
return augmentedGraphQLContext;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
exports.createContextFactory = createContextFactory;
|
|
73
|
+
const defaultCreateUser = ({ req, claims }) => {
|
|
74
|
+
if (!claims)
|
|
75
|
+
return Promise.resolve(undefined);
|
|
76
|
+
const accessToken = req.headers.authorization?.startsWith('Bearer') ? req.headers.authorization?.substring(7) ?? '' : '';
|
|
77
|
+
return Promise.resolve(new User_1.User(claims, accessToken));
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=context.js.map
|
package/context.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA"}
|
package/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./context"), exports);
|
|
18
|
+
__exportStar(require("./User"), exports);
|
|
19
|
+
__exportStar(require("./utils"), exports);
|
|
20
|
+
__exportStar(require("./logging"), exports);
|
|
21
|
+
__exportStar(require("./subscriptions"), exports);
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAAyB;AACzB,yCAAsB;AACtB,0CAAuB;AACvB,4CAAyB;AACzB,kDAA+B"}
|
package/logging.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Logger } from '@makerx/node-common';
|
|
2
|
+
import { ExecutionArgs, GraphQLFormattedError } from 'graphql';
|
|
3
|
+
interface GraphQLLogOperationInfo {
|
|
4
|
+
started: number;
|
|
5
|
+
operationName?: string | null;
|
|
6
|
+
query?: string | null;
|
|
7
|
+
variables?: Record<string, unknown> | null;
|
|
8
|
+
result: {
|
|
9
|
+
data?: Record<string, unknown> | null;
|
|
10
|
+
errors?: readonly GraphQLFormattedError[] | null;
|
|
11
|
+
};
|
|
12
|
+
logger: Logger;
|
|
13
|
+
}
|
|
14
|
+
export declare const logGraphQLOperation: ({ started, operationName, query, variables, result: { errors }, logger }: GraphQLLogOperationInfo) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Logs `operationName`, `query` and `variables` params from the GraphQL `ExecutionArgs`.
|
|
17
|
+
* If `args.contextValue` has a `logger` property, it will be used, otherwise the `logger` param will be used.
|
|
18
|
+
*/
|
|
19
|
+
export declare const logGraphQLExecutionArgs: (args: ExecutionArgs, message: string, logger?: Logger) => void;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=logging.d.ts.map
|
package/logging.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAS,MAAM,SAAS,CAAA;AAKrE,UAAU,uBAAuB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1C,MAAM,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;QACrC,MAAM,CAAC,EAAE,SAAS,qBAAqB,EAAE,GAAG,IAAI,CAAA;KACjD,CAAA;IACD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,mBAAmB,6EAA8E,uBAAuB,SAgBpI,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,SAAU,aAAa,WAAW,MAAM,WAAW,MAAM,SAc5F,CAAA"}
|
package/logging.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logGraphQLExecutionArgs = exports.logGraphQLOperation = void 0;
|
|
7
|
+
const node_common_1 = require("@makerx/node-common");
|
|
8
|
+
const graphql_1 = require("graphql");
|
|
9
|
+
const lodash_omitby_1 = __importDefault(require("lodash.omitby"));
|
|
10
|
+
const utils_1 = require("./utils");
|
|
11
|
+
const logGraphQLOperation = ({ started, operationName, query, variables, result: { errors }, logger }) => {
|
|
12
|
+
const isIntrospection = query && (0, utils_1.isIntrospectionQuery)(query);
|
|
13
|
+
if (node_common_1.isLocalDev && isIntrospection)
|
|
14
|
+
return;
|
|
15
|
+
logger.info('GraphQL operation', (0, lodash_omitby_1.default)({
|
|
16
|
+
operationName,
|
|
17
|
+
query: isIntrospection ? 'IntrospectionQuery' : query,
|
|
18
|
+
variables: variables && Object.keys(variables).length > 0 ? variables : undefined,
|
|
19
|
+
duration: Date.now() - started,
|
|
20
|
+
errors,
|
|
21
|
+
}, utils_1.isNil));
|
|
22
|
+
};
|
|
23
|
+
exports.logGraphQLOperation = logGraphQLOperation;
|
|
24
|
+
/**
|
|
25
|
+
* Logs `operationName`, `query` and `variables` params from the GraphQL `ExecutionArgs`.
|
|
26
|
+
* If `args.contextValue` has a `logger` property, it will be used, otherwise the `logger` param will be used.
|
|
27
|
+
*/
|
|
28
|
+
const logGraphQLExecutionArgs = (args, message, logger) => {
|
|
29
|
+
const { operationName, variableValues, document } = args;
|
|
30
|
+
const contextLogger = args.contextValue.logger ?? logger;
|
|
31
|
+
contextLogger?.info(message, (0, lodash_omitby_1.default)({
|
|
32
|
+
operationName,
|
|
33
|
+
query: (0, graphql_1.print)(document),
|
|
34
|
+
variables: variableValues,
|
|
35
|
+
}, utils_1.isNil));
|
|
36
|
+
};
|
|
37
|
+
exports.logGraphQLExecutionArgs = logGraphQLExecutionArgs;
|
|
38
|
+
//# sourceMappingURL=logging.js.map
|
package/logging.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":";;;;;;AAAA,qDAAwD;AACxD,qCAAqE;AACrE,kEAAkC;AAClC,mCAAqD;AAe9C,MAAM,mBAAmB,GAAG,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAA2B,EAAE,EAAE;IACvI,MAAM,eAAe,GAAG,KAAK,IAAI,IAAA,4BAAoB,EAAC,KAAK,CAAC,CAAA;IAC5D,IAAI,wBAAU,IAAI,eAAe;QAAE,OAAM;IACzC,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,IAAA,uBAAM,EACJ;QACE,aAAa;QACb,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,KAAK;QACrD,SAAS,EAAE,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACjF,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;QAC9B,MAAM;KACP,EACD,aAAK,CACN,CACF,CAAA;AACH,CAAC,CAAA;AAhBY,QAAA,mBAAmB,uBAgB/B;AAED;;;GAGG;AACI,MAAM,uBAAuB,GAAG,CAAC,IAAmB,EAAE,OAAe,EAAE,MAAe,EAAE,EAAE;IAC/F,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;IACxD,MAAM,aAAa,GAAI,IAAI,CAAC,YAAwC,CAAC,MAAM,IAAI,MAAM,CAAA;IACrF,aAAa,EAAE,IAAI,CACjB,OAAO,EACP,IAAA,uBAAM,EACJ;QACE,aAAa;QACb,KAAK,EAAE,IAAA,eAAK,EAAC,QAAQ,CAAC;QACtB,SAAS,EAAE,cAAc;KAC1B,EACD,aAAK,CACN,CACF,CAAA;AACH,CAAC,CAAA;AAdY,QAAA,uBAAuB,2BAcnC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"main": "index.js",
|
|
3
|
+
"types": "index.d.ts",
|
|
4
|
+
"name": "@makerx/graphql-core",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"private": false,
|
|
7
|
+
"description": "A set of core GraphQL utilities that MakerX uses to build GraphQL APIs",
|
|
8
|
+
"author": "MakerX",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=16.0"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/MakerXStudio/graphql-core/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/MakerXStudio/graphql-core#readme",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/MakerXStudio/graphql-core.git"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@makerx/node-common": "^1.0.3",
|
|
23
|
+
"lodash.omitby": "^4.6.0",
|
|
24
|
+
"lodash.pick": "^4.4.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"express": "*",
|
|
28
|
+
"graphql": "*",
|
|
29
|
+
"graphql-ws": "*",
|
|
30
|
+
"ws": "*"
|
|
31
|
+
},
|
|
32
|
+
"peerDependenciesMeta": {
|
|
33
|
+
"graphql-ws": {
|
|
34
|
+
"optional": true
|
|
35
|
+
},
|
|
36
|
+
"ws": {
|
|
37
|
+
"optional": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"overrides": {
|
|
41
|
+
"semver": "^7.5.3"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { Logger } from '@makerx/node-common';
|
|
3
|
+
import type { IncomingMessage } from 'http';
|
|
4
|
+
import { User } from '../User';
|
|
5
|
+
import { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo } from '../context';
|
|
6
|
+
export interface SubscriptionContextInput {
|
|
7
|
+
connectRequest: IncomingMessage;
|
|
8
|
+
connectionParams?: Readonly<Record<string, unknown>>;
|
|
9
|
+
claims?: JwtPayload;
|
|
10
|
+
}
|
|
11
|
+
export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T>;
|
|
12
|
+
export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
|
|
13
|
+
export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
|
|
14
|
+
export interface CreateSubscriptionContextConfig<TContext = GraphQLContext> {
|
|
15
|
+
requestLogger: CreateRequestLogger | Logger;
|
|
16
|
+
augmentRequestInfo?: AugmentSubscriptionRequestInfo;
|
|
17
|
+
claimsToLog?: string[];
|
|
18
|
+
createUser?: CreateSubscriptionUser;
|
|
19
|
+
requestInfoToLog?: Array<keyof RequestInfo>;
|
|
20
|
+
augmentContext?: (context: TContext) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
21
|
+
}
|
|
22
|
+
export declare const createSubscriptionContextFactory: <TContext extends GraphQLContext<Logger, RequestInfo, User | undefined> = GraphQLContext<Logger, RequestInfo, User | undefined>>({ requestLogger, augmentRequestInfo, claimsToLog, createUser, requestInfoToLog, augmentContext, }: CreateSubscriptionContextConfig<TContext>) => CreateSubscriptionContext<TContext>;
|
|
23
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/subscriptions/context.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAC9B,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGzF,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,eAAe,CAAA;IAC/B,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACpD,MAAM,CAAC,EAAE,UAAU,CAAA;CACpB;AAED,MAAM,MAAM,sBAAsB,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,IAAI,CAAC,KAAK,EAAE,wBAAwB,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAC1G,MAAM,MAAM,yBAAyB,CAAC,QAAQ,GAAG,cAAc,IAAI,CAAC,KAAK,EAAE,wBAAwB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AACzH,MAAM,MAAM,8BAA8B,GAAG,CAAC,KAAK,EAAE,wBAAwB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEzG,MAAM,WAAW,+BAA+B,CAAC,QAAQ,GAAG,cAAc;IACxE,aAAa,EAAE,mBAAmB,GAAG,MAAM,CAAA;IAC3C,kBAAkB,CAAC,EAAE,8BAA8B,CAAA;IACnD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,UAAU,CAAC,EAAE,sBAAsB,CAAA;IACnC,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,WAAW,CAAC,CAAA;IAC3C,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACnG;AAED,eAAO,MAAM,gCAAgC,uTAwD5C,CAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSubscriptionContextFactory = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const lodash_1 = require("lodash");
|
|
6
|
+
const User_1 = require("../User");
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
const createSubscriptionContextFactory = ({ requestLogger, augmentRequestInfo, claimsToLog, createUser = defaultCreateUser, requestInfoToLog, augmentContext, }) => {
|
|
9
|
+
// the function that creates the GraphQL context
|
|
10
|
+
return async (input) => {
|
|
11
|
+
const { connectRequest: req, claims } = input;
|
|
12
|
+
const xForwardedFor = req.headers['x-forwarded-for'];
|
|
13
|
+
const host = Array.isArray(xForwardedFor) ? xForwardedFor[0] : xForwardedFor ?? req.headers.host;
|
|
14
|
+
// build request info from the connect request and socket
|
|
15
|
+
const requestInfo = {
|
|
16
|
+
requestId: req.headers['x-request-id']?.toString() ?? (0, crypto_1.randomUUID)(),
|
|
17
|
+
protocol: 'ws',
|
|
18
|
+
host: host ?? '',
|
|
19
|
+
method: req.method ?? '',
|
|
20
|
+
url: req.url ?? '',
|
|
21
|
+
origin: req.headers['origin'] ?? '',
|
|
22
|
+
referer: req.headers.referer?.toString() ?? '',
|
|
23
|
+
arrLogId: req.headers['x-arr-log-id']?.toString() ?? undefined,
|
|
24
|
+
clientIp: req.headers['x-forwarded-for']?.toString() ?? req.socket.remoteAddress,
|
|
25
|
+
correlationId: req.headers['x-correlation-id']?.toString() ?? undefined,
|
|
26
|
+
...augmentRequestInfo?.(input),
|
|
27
|
+
};
|
|
28
|
+
// create request logger
|
|
29
|
+
let logger;
|
|
30
|
+
if (typeof requestLogger === 'function') {
|
|
31
|
+
// build request logger metadata
|
|
32
|
+
const requestLoggerMetadata = {};
|
|
33
|
+
// add request info to log
|
|
34
|
+
if (requestInfoToLog?.length)
|
|
35
|
+
requestLoggerMetadata.request = (0, lodash_1.pick)(requestInfo, requestInfoToLog);
|
|
36
|
+
// add user claims to log
|
|
37
|
+
if (claims && claimsToLog?.length)
|
|
38
|
+
requestLoggerMetadata.user = (0, lodash_1.pick)(claims, claimsToLog);
|
|
39
|
+
// build the request logger
|
|
40
|
+
logger = requestLogger(requestLoggerMetadata);
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
logger = requestLogger;
|
|
44
|
+
const graphqlContext = {
|
|
45
|
+
requestInfo,
|
|
46
|
+
logger,
|
|
47
|
+
user: await createUser(input),
|
|
48
|
+
started: Date.now(),
|
|
49
|
+
};
|
|
50
|
+
const augmentedGraphQLContext = augmentContext
|
|
51
|
+
? { ...graphqlContext, ...(await augmentContext(graphqlContext)) }
|
|
52
|
+
: graphqlContext;
|
|
53
|
+
return augmentedGraphQLContext;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
exports.createSubscriptionContextFactory = createSubscriptionContextFactory;
|
|
57
|
+
const defaultCreateUser = ({ claims, connectionParams }) => {
|
|
58
|
+
if (!claims)
|
|
59
|
+
return Promise.resolve(undefined);
|
|
60
|
+
const accessToken = (0, utils_1.extractTokenFromConnectionParams)(connectionParams);
|
|
61
|
+
return Promise.resolve(new User_1.User(claims, accessToken ?? ''));
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/subscriptions/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/subscriptions/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,OAAO,EAAE,gCAAgC,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.extractTokenFromConnectionParams = void 0;
|
|
18
|
+
__exportStar(require("./context"), exports);
|
|
19
|
+
__exportStar(require("./server"), exports);
|
|
20
|
+
var utils_1 = require("./utils");
|
|
21
|
+
Object.defineProperty(exports, "extractTokenFromConnectionParams", { enumerable: true, get: function () { return utils_1.extractTokenFromConnectionParams; } });
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/subscriptions/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,4CAAyB;AACzB,2CAAwB;AACxB,iCAA0D;AAAjD,yHAAA,gCAAgC,OAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Logger } from '@makerx/node-common';
|
|
3
|
+
import { GraphQLSchema } from 'graphql';
|
|
4
|
+
import type { Server } from 'http';
|
|
5
|
+
import { JwtPayload } from '../context';
|
|
6
|
+
import { CreateSubscriptionContext } from './context';
|
|
7
|
+
export declare function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, path, verifyToken, requireAuth, jwtClaimsToLog, }: {
|
|
8
|
+
schema: GraphQLSchema;
|
|
9
|
+
httpServer: Server;
|
|
10
|
+
createSubscriptionContext: CreateSubscriptionContext;
|
|
11
|
+
logger: Logger;
|
|
12
|
+
path?: string;
|
|
13
|
+
verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
|
|
14
|
+
requireAuth?: boolean;
|
|
15
|
+
jwtClaimsToLog?: string[];
|
|
16
|
+
}): import("graphql-ws/lib/common").Disposable;
|
|
17
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/subscriptions/server.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAGlC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAGrD,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,UAAU,EACV,yBAAyB,EACzB,MAAM,EACN,IAAiB,EACjB,WAAW,EACX,WAAW,EACX,cAA+B,GAChC,EAAE;IACD,MAAM,EAAE,aAAa,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,yBAAyB,EAAE,yBAAyB,CAAA;IACpD,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IAClE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;CAC1B,8CA8DA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.useSubscriptionsServer = void 0;
|
|
7
|
+
const ws_1 = require("graphql-ws/lib/use/ws");
|
|
8
|
+
const lodash_pick_1 = __importDefault(require("lodash.pick"));
|
|
9
|
+
const ws_2 = require("ws");
|
|
10
|
+
const logging_1 = require("../logging");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext, logger, path = '/graphql', verifyToken, requireAuth, jwtClaimsToLog = ['oid', 'iss'], }) {
|
|
13
|
+
if (requireAuth && !verifyToken)
|
|
14
|
+
throw new Error('verifyToken must be supplied when requireAuth is true');
|
|
15
|
+
const wsServer = new ws_2.WebSocketServer({
|
|
16
|
+
server: httpServer,
|
|
17
|
+
path,
|
|
18
|
+
});
|
|
19
|
+
return (0, ws_1.useServer)({
|
|
20
|
+
schema,
|
|
21
|
+
onError(_ctx, message, errors) {
|
|
22
|
+
logger.error('GraphQL subscriptions server error', { message, errors });
|
|
23
|
+
},
|
|
24
|
+
onConnect: async (ctx) => {
|
|
25
|
+
const connectionEstablished = 'Subscription connection established';
|
|
26
|
+
if (!verifyToken) {
|
|
27
|
+
logger.info(connectionEstablished);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const token = (0, utils_1.extractTokenFromConnectionParams)(ctx.connectionParams);
|
|
31
|
+
if (!token) {
|
|
32
|
+
if (requireAuth) {
|
|
33
|
+
logger.error('No authorization parameter was supplied via websocket connection params');
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
logger.info(connectionEstablished);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const claims = await verifyToken((0, utils_1.getHost)(ctx.extra.request), token);
|
|
41
|
+
ctx.extra.claims = claims;
|
|
42
|
+
logger.info(connectionEstablished, {
|
|
43
|
+
claims: (0, lodash_pick_1.default)(claims, jwtClaimsToLog),
|
|
44
|
+
});
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
logger.error('Failed to verify subscription connection auth token', { error });
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
onDisconnect({ extra: { claims } }) {
|
|
53
|
+
logger.info('Subscription connection disconnected', { claims: (0, lodash_pick_1.default)(claims, jwtClaimsToLog) });
|
|
54
|
+
},
|
|
55
|
+
context: async (ctx) => {
|
|
56
|
+
return createSubscriptionContext({
|
|
57
|
+
connectRequest: ctx.extra.request,
|
|
58
|
+
connectionParams: ctx.connectionParams,
|
|
59
|
+
claims: ctx.extra.claims,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
onOperation(_ctx, _message, args) {
|
|
63
|
+
(0, logging_1.logGraphQLExecutionArgs)(args, 'GraphQL subscription operation');
|
|
64
|
+
},
|
|
65
|
+
onNext(_ctx, _message, args, _result) {
|
|
66
|
+
(0, logging_1.logGraphQLExecutionArgs)(args, 'GraphQL subscription result');
|
|
67
|
+
},
|
|
68
|
+
}, wsServer);
|
|
69
|
+
}
|
|
70
|
+
exports.useSubscriptionsServer = useSubscriptionsServer;
|
|
71
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/subscriptions/server.ts"],"names":[],"mappings":";;;;;;AAEA,8CAAiD;AAEjD,8DAA8B;AAC9B,2BAAoC;AAEpC,wCAAoD;AAEpD,mCAAmE;AAEnE,SAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,UAAU,EACV,yBAAyB,EACzB,MAAM,EACN,IAAI,GAAG,UAAU,EACjB,WAAW,EACX,WAAW,EACX,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAUhC;IACC,IAAI,WAAW,IAAI,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAA;IAEzG,MAAM,QAAQ,GAAG,IAAI,oBAAe,CAAC;QACnC,MAAM,EAAE,UAAU;QAClB,IAAI;KACL,CAAC,CAAA;IAEF,OAAO,IAAA,cAAS,EACd;QACE,MAAM;QACN,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM;YAC3B,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QACzE,CAAC;QACD,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACvB,MAAM,qBAAqB,GAAG,qCAAqC,CAAA;YACnE,IAAI,CAAC,WAAW,EAAE;gBAChB,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;gBAClC,OAAO,IAAI,CAAA;aACZ;YAED,MAAM,KAAK,GAAG,IAAA,wCAAgC,EAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;YACpE,IAAI,CAAC,KAAK,EAAE;gBACV,IAAI,WAAW,EAAE;oBACf,MAAM,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAA;oBACvF,OAAO,KAAK,CAAA;iBACb;gBACD,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;gBAClC,OAAO,IAAI,CAAA;aACZ;YAED,IAAI;gBACF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAA,eAAO,EAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAA;gBACnE,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,MAA8B,CAAA;gBACjD,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE;oBACjC,MAAM,EAAE,IAAA,qBAAI,EAAC,MAAM,EAAE,cAAc,CAAC;iBACrC,CAAC,CAAA;gBACF,OAAO,IAAI,CAAA;aACZ;YAAC,OAAO,KAAK,EAAE;gBACd,MAAM,CAAC,KAAK,CAAC,qDAAqD,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;gBAC9E,OAAO,KAAK,CAAA;aACb;QACH,CAAC;QACD,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE;YAChC,MAAM,CAAC,IAAI,CAAC,sCAAsC,EAAE,EAAE,MAAM,EAAE,IAAA,qBAAI,EAAC,MAAM,EAAE,cAAc,CAAC,EAAE,CAAC,CAAA;QAC/F,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACrB,OAAO,yBAAyB,CAAC;gBAC/B,cAAc,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO;gBACjC,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;gBACtC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,MAAgC;aACnD,CAAC,CAAA;QACJ,CAAC;QACD,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI;YAC9B,IAAA,iCAAuB,EAAC,IAAI,EAAE,gCAAgC,CAAC,CAAA;QACjE,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO;YAClC,IAAA,iCAAuB,EAAC,IAAI,EAAE,6BAA6B,CAAC,CAAA;QAC9D,CAAC;KACF,EACD,QAAQ,CACT,CAAA;AACH,CAAC;AAhFD,wDAgFC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IncomingMessage } from 'http';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts a token from a connection parameter named `authorization` or `Authorization`.
|
|
5
|
+
* The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
|
|
6
|
+
* Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.
|
|
7
|
+
* @param connectionParams
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
|
|
11
|
+
export declare function getHost(request: IncomingMessage): string;
|
|
12
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/subscriptions/utils.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAE3C;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,sBAIpG;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,eAAe,UAI/C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getHost = exports.extractTokenFromConnectionParams = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Extracts a token from a connection parameter named `authorization` or `Authorization`.
|
|
6
|
+
* The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
|
|
7
|
+
* Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.
|
|
8
|
+
* @param connectionParams
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
function extractTokenFromConnectionParams(connectionParams) {
|
|
12
|
+
const bearerTokenValue = (connectionParams?.authorization ?? connectionParams?.Authorization);
|
|
13
|
+
if (!bearerTokenValue?.startsWith('Bearer '))
|
|
14
|
+
return undefined;
|
|
15
|
+
return bearerTokenValue.substring(7);
|
|
16
|
+
}
|
|
17
|
+
exports.extractTokenFromConnectionParams = extractTokenFromConnectionParams;
|
|
18
|
+
function getHost(request) {
|
|
19
|
+
const proxyHostHeader = request.headers['x-forwarded-host'];
|
|
20
|
+
const host = Array.isArray(proxyHostHeader) ? proxyHostHeader[0] ?? undefined : proxyHostHeader ?? request.headers.host ?? undefined;
|
|
21
|
+
return host ?? 'subscriptions';
|
|
22
|
+
}
|
|
23
|
+
exports.getHost = getHost;
|
|
24
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/subscriptions/utils.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACH,SAAgB,gCAAgC,CAAC,gBAAoD;IACnG,MAAM,gBAAgB,GAAG,CAAC,gBAAgB,EAAE,aAAa,IAAI,gBAAgB,EAAE,aAAa,CAAuB,CAAA;IACnH,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAA;IAC9D,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AACtC,CAAC;AAJD,4EAIC;AAED,SAAgB,OAAO,CAAC,OAAwB;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAA;IACpI,OAAO,IAAI,IAAI,eAAe,CAAA;AAChC,CAAC;AAJD,0BAIC"}
|
package/testing.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ApolloClient, NormalizedCacheObject } from '@apollo/client/core';
|
|
2
|
+
import { ClientCredentialsConfig } from '@makerx/node-common';
|
|
3
|
+
import { ApolloClientOptions } from '@apollo/client/core/ApolloClient';
|
|
4
|
+
export * from '@apollo/client/core';
|
|
5
|
+
export declare const createTestClient: (url: string, accessToken?: string, options?: Partial<ApolloClientOptions<NormalizedCacheObject>>) => ApolloClient<NormalizedCacheObject>;
|
|
6
|
+
export declare const createTestClientWithClientCredentials: (url: string, clientCredentialsConfig: ClientCredentialsConfig, options?: Partial<ApolloClientOptions<NormalizedCacheObject>>) => ApolloClient<NormalizedCacheObject>;
|
|
7
|
+
//# sourceMappingURL=testing.d.ts.map
|
package/testing.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuC,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAG9G,OAAO,EAAuB,uBAAuB,EAA6B,MAAM,qBAAqB,CAAA;AAC7G,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAEtE,cAAc,qBAAqB,CAAA;AAuCnC,eAAO,MAAM,gBAAgB,QACtB,MAAM,gBACG,MAAM,YACV,QAAQ,oBAAoB,qBAAqB,CAAC,CAAC,KAC5D,aAAa,qBAAqB,CAUjC,CAAA;AAEJ,eAAO,MAAM,qCAAqC,QAC3C,MAAM,2BACc,uBAAuB,YACtC,QAAQ,oBAAoB,qBAAqB,CAAC,CAAC,KAC5D,aAAa,qBAAqB,CAUjC,CAAA"}
|
package/testing.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.createTestClientWithClientCredentials = exports.createTestClient = void 0;
|
|
21
|
+
const core_1 = require("@apollo/client/core");
|
|
22
|
+
const context_1 = require("@apollo/client/link/context");
|
|
23
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
24
|
+
const node_common_1 = require("@makerx/node-common");
|
|
25
|
+
__exportStar(require("@apollo/client/core"), exports);
|
|
26
|
+
const bearerTokenLink = (accessToken) => (0, context_1.setContext)((_, { headers }) => {
|
|
27
|
+
if (accessToken)
|
|
28
|
+
return {
|
|
29
|
+
headers: {
|
|
30
|
+
...headers,
|
|
31
|
+
authorization: `Bearer ${accessToken}`,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
headers,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
const clientCredentialsLink = (clientCredentialsConfig) => {
|
|
39
|
+
let promise;
|
|
40
|
+
return (0, context_1.setContext)(async () => {
|
|
41
|
+
if (!promise)
|
|
42
|
+
promise = (0, node_common_1.getClientCredentialsToken)(clientCredentialsConfig);
|
|
43
|
+
let tokenResponse = await promise;
|
|
44
|
+
if (tokenResponse.isExpired) {
|
|
45
|
+
promise = (0, node_common_1.getClientCredentialsToken)(clientCredentialsConfig);
|
|
46
|
+
tokenResponse = await promise;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
headers: {
|
|
50
|
+
authorization: `Bearer ${tokenResponse.access_token}`,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
const httpLink = (url) => (0, core_1.createHttpLink)({
|
|
56
|
+
uri: url,
|
|
57
|
+
fetch: node_fetch_1.default,
|
|
58
|
+
});
|
|
59
|
+
const createTestClient = (url, accessToken, options) => new core_1.ApolloClient({
|
|
60
|
+
link: (0, core_1.from)([bearerTokenLink(accessToken), httpLink(url)]),
|
|
61
|
+
cache: new core_1.InMemoryCache(),
|
|
62
|
+
defaultOptions: {
|
|
63
|
+
query: {
|
|
64
|
+
errorPolicy: 'all',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
...options,
|
|
68
|
+
});
|
|
69
|
+
exports.createTestClient = createTestClient;
|
|
70
|
+
const createTestClientWithClientCredentials = (url, clientCredentialsConfig, options) => new core_1.ApolloClient({
|
|
71
|
+
link: (0, core_1.from)([clientCredentialsLink(clientCredentialsConfig), httpLink(url)]),
|
|
72
|
+
cache: new core_1.InMemoryCache(),
|
|
73
|
+
defaultOptions: {
|
|
74
|
+
query: {
|
|
75
|
+
errorPolicy: 'all',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
...options,
|
|
79
|
+
});
|
|
80
|
+
exports.createTestClientWithClientCredentials = createTestClientWithClientCredentials;
|
|
81
|
+
//# sourceMappingURL=testing.js.map
|
package/testing.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,8CAA8G;AAC9G,yDAAwD;AACxD,4DAA8B;AAC9B,qDAA6G;AAG7G,sDAAmC;AAEnC,MAAM,eAAe,GAAG,CAAC,WAAoB,EAAE,EAAE,CAC/C,IAAA,oBAAU,EAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IAC5B,IAAI,WAAW;QACb,OAAO;YACL,OAAO,EAAE;gBACP,GAAG,OAAO;gBACV,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAA;IACH,OAAO;QACL,OAAO;KACR,CAAA;AACH,CAAC,CAAC,CAAA;AAEJ,MAAM,qBAAqB,GAAG,CAAC,uBAAgD,EAAE,EAAE;IACjF,IAAI,OAAiD,CAAA;IACrD,OAAO,IAAA,oBAAU,EAAC,KAAK,IAAI,EAAE;QAC3B,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,IAAA,uCAAyB,EAAC,uBAAuB,CAAC,CAAA;QAC1E,IAAI,aAAa,GAAG,MAAM,OAAO,CAAA;QACjC,IAAI,aAAa,CAAC,SAAS,EAAE;YAC3B,OAAO,GAAG,IAAA,uCAAyB,EAAC,uBAAuB,CAAC,CAAA;YAC5D,aAAa,GAAG,MAAM,OAAO,CAAA;SAC9B;QACD,OAAO;YACL,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,aAAa,CAAC,YAAY,EAAE;aACtD;SACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,EAAE,CAC/B,IAAA,qBAAc,EAAC;IACb,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,oBAA2C;CACnD,CAAC,CAAA;AAEG,MAAM,gBAAgB,GAAG,CAC9B,GAAW,EACX,WAAoB,EACpB,OAA6D,EACxB,EAAE,CACvC,IAAI,mBAAY,CAAC;IACf,IAAI,EAAE,IAAA,WAAI,EAAC,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,KAAK,EAAE,IAAI,oBAAa,EAAE;IAC1B,cAAc,EAAE;QACd,KAAK,EAAE;YACL,WAAW,EAAE,KAAK;SACnB;KACF;IACD,GAAG,OAAO;CACX,CAAC,CAAA;AAdS,QAAA,gBAAgB,oBAczB;AAEG,MAAM,qCAAqC,GAAG,CACnD,GAAW,EACX,uBAAgD,EAChD,OAA6D,EACxB,EAAE,CACvC,IAAI,mBAAY,CAAC;IACf,IAAI,EAAE,IAAA,WAAI,EAAC,CAAC,qBAAqB,CAAC,uBAAuB,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3E,KAAK,EAAE,IAAI,oBAAa,EAAE;IAC1B,cAAc,EAAE;QACd,KAAK,EAAE;YACL,WAAW,EAAE,KAAK;SACnB;KACF;IACD,GAAG,OAAO;CACX,CAAC,CAAA;AAdS,QAAA,qCAAqC,yCAc9C"}
|
package/utils.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const isIntrospectionQuery: (query?: string) => boolean | undefined;
|
|
2
|
+
export declare const isNil: (value: unknown) => value is null | undefined;
|
|
3
|
+
export declare const isNotNil: <T>(value: T) => value is Exclude<T, null | undefined>;
|
|
4
|
+
//# sourceMappingURL=utils.d.ts.map
|
package/utils.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,WAAY,MAAM,wBAAgC,CAAA;AACnF,eAAO,MAAM,KAAK,UAAW,OAAO,8BAAqE,CAAA;AACzG,eAAO,MAAM,QAAQ,wDAAwE,CAAA"}
|
package/utils.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isNotNil = exports.isNil = exports.isIntrospectionQuery = void 0;
|
|
4
|
+
const isIntrospectionQuery = (query) => query?.includes('__schema');
|
|
5
|
+
exports.isIntrospectionQuery = isIntrospectionQuery;
|
|
6
|
+
const isNil = (value) => value === null || value === undefined;
|
|
7
|
+
exports.isNil = isNil;
|
|
8
|
+
const isNotNil = (value) => !(0, exports.isNil)(value);
|
|
9
|
+
exports.isNotNil = isNotNil;
|
|
10
|
+
//# sourceMappingURL=utils.js.map
|
package/utils.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAO,MAAM,oBAAoB,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAA;AAAtE,QAAA,oBAAoB,wBAAkD;AAC5E,MAAM,KAAK,GAAG,CAAC,KAAc,EAA6B,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAA;AAA5F,QAAA,KAAK,SAAuF;AAClG,MAAM,QAAQ,GAAG,CAAI,KAAQ,EAAyC,EAAE,CAAC,CAAC,IAAA,aAAK,EAAC,KAAK,CAAC,CAAA;AAAhF,QAAA,QAAQ,YAAwE"}
|