aws-appsync-subscription-link 2.2.6 → 2.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/CHANGELOG.md +34 -0
- package/__tests__/link/realtime-subscription-handshake-link-test.ts +338 -18
- package/lib/index.d.ts +2 -2
- package/lib/realtime-subscription-handshake-link.d.ts +6 -6
- package/lib/realtime-subscription-handshake-link.js +46 -26
- package/lib/types/index.d.ts +3 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,40 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
<a name="2.4.0"></a>
|
|
7
|
+
# [2.4.0](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-subscription-link@2.3.0...aws-appsync-subscription-link@2.4.0) (2022-06-24)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* Add keepAliveTimeoutMs config for AppSync WebSocket link ([#725](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/725)) ([c91e507](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/c91e507))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
<a name="2.3.0"></a>
|
|
18
|
+
# [2.3.0](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-subscription-link@2.2.7...aws-appsync-subscription-link@2.3.0) (2022-05-02)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* add custom domain support for Apollo V2 ([#698](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/698)) ([42db16d](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/42db16d))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
<a name="2.2.7"></a>
|
|
29
|
+
## [2.2.7](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-subscription-link@2.2.5...aws-appsync-subscription-link@2.2.7) (2022-03-04)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Bug Fixes
|
|
33
|
+
|
|
34
|
+
* Port over Amplify fix for subscription race conditions for ApolloV2 ([#509](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/509)) ([#705](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/705)) ([f5022ba](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/f5022ba))
|
|
35
|
+
* **aws-appsync-subscription-link:** graphql header refactor to fix IAM-based auth for Apollo V2 packages ([#679](https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/679)) ([09fc430](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/09fc430))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
6
40
|
<a name="2.2.6"></a>
|
|
7
41
|
## [2.2.6](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-subscription-link@2.2.5...aws-appsync-subscription-link@2.2.6) (2021-10-18)
|
|
8
42
|
|
|
@@ -2,11 +2,14 @@ import { AUTH_TYPE } from "aws-appsync-auth-link";
|
|
|
2
2
|
import { execute } from "apollo-link";
|
|
3
3
|
import gql from 'graphql-tag';
|
|
4
4
|
import { AppSyncRealTimeSubscriptionHandshakeLink } from '../../src/realtime-subscription-handshake-link';
|
|
5
|
+
import { MESSAGE_TYPES } from "../../src/types";
|
|
6
|
+
import { v4 as uuid } from "uuid";
|
|
7
|
+
jest.mock('uuid', () => ({ v4: jest.fn() }));
|
|
5
8
|
|
|
6
9
|
const query = gql`subscription { someSubscription { aField } }`
|
|
7
10
|
|
|
8
11
|
class myWebSocket implements WebSocket {
|
|
9
|
-
binaryType: BinaryType;
|
|
12
|
+
binaryType: BinaryType;
|
|
10
13
|
bufferedAmount: number;
|
|
11
14
|
extensions: string;
|
|
12
15
|
onclose: (this: WebSocket, ev: CloseEvent) => any;
|
|
@@ -50,8 +53,22 @@ describe("RealTime subscription link", () => {
|
|
|
50
53
|
type: AUTH_TYPE.API_KEY,
|
|
51
54
|
apiKey: 'xxxxx'
|
|
52
55
|
},
|
|
53
|
-
region: 'us-
|
|
54
|
-
url: 'https://
|
|
56
|
+
region: 'us-west-2',
|
|
57
|
+
url: 'https://firsttesturl12345678901234.appsync-api.us-west-2.amazonaws.com/graphql'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(link).toBeInstanceOf(AppSyncRealTimeSubscriptionHandshakeLink);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("Can instantiate link with custom domain", () => {
|
|
64
|
+
expect.assertions(1);
|
|
65
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
66
|
+
auth: {
|
|
67
|
+
type: AUTH_TYPE.API_KEY,
|
|
68
|
+
apiKey: 'xxxxx'
|
|
69
|
+
},
|
|
70
|
+
region: 'us-west-2',
|
|
71
|
+
url: 'https://test1.testcustomdomain.com/graphql'
|
|
55
72
|
});
|
|
56
73
|
|
|
57
74
|
expect(link).toBeInstanceOf(AppSyncRealTimeSubscriptionHandshakeLink);
|
|
@@ -63,7 +80,7 @@ describe("RealTime subscription link", () => {
|
|
|
63
80
|
return "2019-11-13T18:47:04.733Z";
|
|
64
81
|
}));
|
|
65
82
|
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
66
|
-
expect(url).toBe('wss://
|
|
83
|
+
expect(url).toBe('wss://apikeytesturl1234567890123.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJob3N0IjoiYXBpa2V5dGVzdHVybDEyMzQ1Njc4OTAxMjMuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20iLCJ4LWFtei1kYXRlIjoiMjAxOTExMTNUMTg0NzA0WiIsIngtYXBpLWtleSI6Inh4eHh4In0=&payload=e30=');
|
|
67
84
|
expect(protocol).toBe('graphql-ws');
|
|
68
85
|
done();
|
|
69
86
|
return new myWebSocket();
|
|
@@ -73,13 +90,50 @@ describe("RealTime subscription link", () => {
|
|
|
73
90
|
type: AUTH_TYPE.API_KEY,
|
|
74
91
|
apiKey: 'xxxxx'
|
|
75
92
|
},
|
|
76
|
-
region: 'us-
|
|
77
|
-
url: 'https://
|
|
93
|
+
region: 'us-west-2',
|
|
94
|
+
url: 'https://apikeytesturl1234567890123.appsync-api.us-west-2.amazonaws.com/graphql'
|
|
78
95
|
});
|
|
79
96
|
|
|
80
97
|
execute(link, { query }).subscribe({
|
|
81
98
|
error: (err) => {
|
|
82
|
-
console.log(
|
|
99
|
+
console.log(JSON.stringify(err));
|
|
100
|
+
fail;
|
|
101
|
+
},
|
|
102
|
+
next: (data) => {
|
|
103
|
+
console.log({ data });
|
|
104
|
+
done();
|
|
105
|
+
},
|
|
106
|
+
complete: () => {
|
|
107
|
+
console.log('done with this');
|
|
108
|
+
done();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("Initialize WebSocket correctly for API KEY with custom domain", (done) => {
|
|
115
|
+
expect.assertions(2);
|
|
116
|
+
jest.spyOn(Date.prototype, 'toISOString').mockImplementation(jest.fn(() => {
|
|
117
|
+
return "2019-11-13T18:47:04.733Z";
|
|
118
|
+
}));
|
|
119
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
120
|
+
expect(url).toBe('wss://apikeytest.testcustomdomain.com/graphql/realtime?header=eyJob3N0IjoiYXBpa2V5dGVzdC50ZXN0Y3VzdG9tZG9tYWluLmNvbSIsIngtYW16LWRhdGUiOiIyMDE5MTExM1QxODQ3MDRaIiwieC1hcGkta2V5IjoieHh4eHgifQ==&payload=e30=');
|
|
121
|
+
expect(protocol).toBe('graphql-ws');
|
|
122
|
+
done();
|
|
123
|
+
return new myWebSocket();
|
|
124
|
+
});
|
|
125
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
126
|
+
auth: {
|
|
127
|
+
type: AUTH_TYPE.API_KEY,
|
|
128
|
+
apiKey: 'xxxxx'
|
|
129
|
+
},
|
|
130
|
+
region: 'us-west-2',
|
|
131
|
+
url: 'https://apikeytest.testcustomdomain.com/graphql'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
execute(link, { query }).subscribe({
|
|
135
|
+
error: (err) => {
|
|
136
|
+
console.log(JSON.stringify(err));
|
|
83
137
|
fail;
|
|
84
138
|
},
|
|
85
139
|
next: (data) => {
|
|
@@ -100,7 +154,7 @@ describe("RealTime subscription link", () => {
|
|
|
100
154
|
return "2019-11-13T18:47:04.733Z";
|
|
101
155
|
}));
|
|
102
156
|
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
103
|
-
expect(url).toBe('wss://
|
|
157
|
+
expect(url).toBe('wss://cognitouserpooltesturl1234.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiY29nbml0b3VzZXJwb29sdGVzdHVybDEyMzQuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20ifQ==&payload=e30=');
|
|
104
158
|
expect(protocol).toBe('graphql-ws');
|
|
105
159
|
done();
|
|
106
160
|
return new myWebSocket();
|
|
@@ -110,13 +164,50 @@ describe("RealTime subscription link", () => {
|
|
|
110
164
|
type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
|
|
111
165
|
jwtToken: 'token'
|
|
112
166
|
},
|
|
113
|
-
region: 'us-
|
|
114
|
-
url: 'https://
|
|
167
|
+
region: 'us-west-2',
|
|
168
|
+
url: 'https://cognitouserpooltesturl1234.appsync-api.us-west-2.amazonaws.com/graphql'
|
|
115
169
|
});
|
|
116
170
|
|
|
117
171
|
execute(link, { query }).subscribe({
|
|
118
172
|
error: (err) => {
|
|
119
|
-
console.log(
|
|
173
|
+
console.log(JSON.stringify(err));
|
|
174
|
+
fail;
|
|
175
|
+
},
|
|
176
|
+
next: (data) => {
|
|
177
|
+
console.log({ data });
|
|
178
|
+
done();
|
|
179
|
+
},
|
|
180
|
+
complete: () => {
|
|
181
|
+
console.log('done with this');
|
|
182
|
+
done();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("Initialize WebSocket correctly for COGNITO USER POOLS with custom domain", (done) => {
|
|
189
|
+
expect.assertions(2);
|
|
190
|
+
jest.spyOn(Date.prototype, 'toISOString').mockImplementation(jest.fn(() => {
|
|
191
|
+
return "2019-11-13T18:47:04.733Z";
|
|
192
|
+
}));
|
|
193
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
194
|
+
expect(url).toBe('wss://cognitouserpools.testcustomdomain.com/graphql/realtime?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiY29nbml0b3VzZXJwb29scy50ZXN0Y3VzdG9tZG9tYWluLmNvbSJ9&payload=e30=');
|
|
195
|
+
expect(protocol).toBe('graphql-ws');
|
|
196
|
+
done();
|
|
197
|
+
return new myWebSocket();
|
|
198
|
+
});
|
|
199
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
200
|
+
auth: {
|
|
201
|
+
type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
|
|
202
|
+
jwtToken: 'token'
|
|
203
|
+
},
|
|
204
|
+
region: 'us-west-2',
|
|
205
|
+
url: 'https://cognitouserpools.testcustomdomain.com/graphql'
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
execute(link, { query }).subscribe({
|
|
209
|
+
error: (err) => {
|
|
210
|
+
console.log(JSON.stringify(err));
|
|
120
211
|
fail;
|
|
121
212
|
},
|
|
122
213
|
next: (data) => {
|
|
@@ -137,7 +228,44 @@ describe("RealTime subscription link", () => {
|
|
|
137
228
|
return "2019-11-13T18:47:04.733Z";
|
|
138
229
|
}));
|
|
139
230
|
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
140
|
-
expect(url).toBe('wss://
|
|
231
|
+
expect(url).toBe('wss://openidconnecttesturl123456.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0Ijoib3BlbmlkY29ubmVjdHRlc3R1cmwxMjM0NTYuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20ifQ==&payload=e30=');
|
|
232
|
+
expect(protocol).toBe('graphql-ws');
|
|
233
|
+
done();
|
|
234
|
+
return new myWebSocket();
|
|
235
|
+
});
|
|
236
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
237
|
+
auth: {
|
|
238
|
+
type: AUTH_TYPE.OPENID_CONNECT,
|
|
239
|
+
jwtToken: 'token'
|
|
240
|
+
},
|
|
241
|
+
region: 'us-west-2',
|
|
242
|
+
url: 'https://openidconnecttesturl123456.appsync-api.us-west-2.amazonaws.com/graphql'
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
execute(link, { query }).subscribe({
|
|
246
|
+
error: (err) => {
|
|
247
|
+
console.log(JSON.stringify(err));
|
|
248
|
+
fail;
|
|
249
|
+
},
|
|
250
|
+
next: (data) => {
|
|
251
|
+
console.log({ data });
|
|
252
|
+
done();
|
|
253
|
+
},
|
|
254
|
+
complete: () => {
|
|
255
|
+
console.log('done with this');
|
|
256
|
+
done();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("Initialize WebSocket correctly for OPENID_CONNECT with custom domain", (done) => {
|
|
263
|
+
expect.assertions(2);
|
|
264
|
+
jest.spyOn(Date.prototype, 'toISOString').mockImplementation(jest.fn(() => {
|
|
265
|
+
return "2019-11-13T18:47:04.733Z";
|
|
266
|
+
}));
|
|
267
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
268
|
+
expect(url).toBe('wss://openidconnecttesturl.testcustomdomain.com/graphql/realtime?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0Ijoib3BlbmlkY29ubmVjdHRlc3R1cmwudGVzdGN1c3RvbWRvbWFpbi5jb20ifQ==&payload=e30=');
|
|
141
269
|
expect(protocol).toBe('graphql-ws');
|
|
142
270
|
done();
|
|
143
271
|
return new myWebSocket();
|
|
@@ -147,13 +275,13 @@ describe("RealTime subscription link", () => {
|
|
|
147
275
|
type: AUTH_TYPE.OPENID_CONNECT,
|
|
148
276
|
jwtToken: 'token'
|
|
149
277
|
},
|
|
150
|
-
region: 'us-
|
|
151
|
-
url: 'https://
|
|
278
|
+
region: 'us-west-2',
|
|
279
|
+
url: 'https://openidconnecttesturl.testcustomdomain.com/graphql'
|
|
152
280
|
});
|
|
153
281
|
|
|
154
282
|
execute(link, { query }).subscribe({
|
|
155
283
|
error: (err) => {
|
|
156
|
-
console.log(
|
|
284
|
+
console.log(JSON.stringify(err));
|
|
157
285
|
fail;
|
|
158
286
|
},
|
|
159
287
|
next: (data) => {
|
|
@@ -174,7 +302,7 @@ describe("RealTime subscription link", () => {
|
|
|
174
302
|
return "2019-11-13T18:47:04.733Z";
|
|
175
303
|
}));
|
|
176
304
|
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
177
|
-
expect(url).toBe('wss://
|
|
305
|
+
expect(url).toBe('wss://awslambdatesturl1234567890.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiYXdzbGFtYmRhdGVzdHVybDEyMzQ1Njc4OTAuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20ifQ==&payload=e30=');
|
|
178
306
|
expect(protocol).toBe('graphql-ws');
|
|
179
307
|
done();
|
|
180
308
|
return new myWebSocket();
|
|
@@ -184,8 +312,8 @@ describe("RealTime subscription link", () => {
|
|
|
184
312
|
type: AUTH_TYPE.AWS_LAMBDA,
|
|
185
313
|
token: 'token'
|
|
186
314
|
},
|
|
187
|
-
region: 'us-
|
|
188
|
-
url: 'https://
|
|
315
|
+
region: 'us-west-2',
|
|
316
|
+
url: 'https://awslambdatesturl1234567890.appsync-api.us-west-2.amazonaws.com/graphql'
|
|
189
317
|
});
|
|
190
318
|
|
|
191
319
|
execute(link, { query }).subscribe({
|
|
@@ -202,4 +330,196 @@ describe("RealTime subscription link", () => {
|
|
|
202
330
|
});
|
|
203
331
|
})
|
|
204
332
|
|
|
333
|
+
test('Initialize WebSocket correctly for AWS_LAMBDA with custom domain', (done) => {
|
|
334
|
+
expect.assertions(2);
|
|
335
|
+
jest.spyOn(Date.prototype, 'toISOString').mockImplementation(jest.fn(() => {
|
|
336
|
+
return "2019-11-13T18:47:04.733Z";
|
|
337
|
+
}));
|
|
338
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
339
|
+
expect(url).toBe('wss://awslambdatesturl.testcustomdomain.com/graphql/realtime?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiYXdzbGFtYmRhdGVzdHVybC50ZXN0Y3VzdG9tZG9tYWluLmNvbSJ9&payload=e30=');
|
|
340
|
+
expect(protocol).toBe('graphql-ws');
|
|
341
|
+
done();
|
|
342
|
+
return new myWebSocket();
|
|
343
|
+
});
|
|
344
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
345
|
+
auth: {
|
|
346
|
+
type: AUTH_TYPE.AWS_LAMBDA,
|
|
347
|
+
token: 'token'
|
|
348
|
+
},
|
|
349
|
+
region: 'us-west-2',
|
|
350
|
+
url: 'https://awslambdatesturl.testcustomdomain.com/graphql'
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
execute(link, { query }).subscribe({
|
|
354
|
+
error: (err) => {
|
|
355
|
+
fail;
|
|
356
|
+
},
|
|
357
|
+
next: (data) => {
|
|
358
|
+
done();
|
|
359
|
+
},
|
|
360
|
+
complete: () => {
|
|
361
|
+
done();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test("Can use a custom keepAliveTimeoutMs", (done) => {
|
|
368
|
+
const id = "abcd-efgh-ijkl-mnop";
|
|
369
|
+
uuid.mockImplementationOnce(() => id);
|
|
370
|
+
|
|
371
|
+
expect.assertions(5);
|
|
372
|
+
jest.spyOn(Date.prototype, 'toISOString').mockImplementation(jest.fn(() => {
|
|
373
|
+
return "2019-11-13T18:47:04.733Z";
|
|
374
|
+
}));
|
|
375
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
376
|
+
expect(url).toBe('wss://apikeytest.testcustomdomain.com/graphql/realtime?header=eyJob3N0IjoiYXBpa2V5dGVzdC50ZXN0Y3VzdG9tZG9tYWluLmNvbSIsIngtYW16LWRhdGUiOiIyMDE5MTExM1QxODQ3MDRaIiwieC1hcGkta2V5IjoieHh4eHgifQ==&payload=e30=');
|
|
377
|
+
expect(protocol).toBe('graphql-ws');
|
|
378
|
+
const socket = new myWebSocket();
|
|
379
|
+
|
|
380
|
+
setTimeout(() => {
|
|
381
|
+
socket.close = () => {};
|
|
382
|
+
socket.onopen.call(socket, (undefined as unknown as Event));
|
|
383
|
+
socket.send = (msg: string) => {
|
|
384
|
+
const { type } = JSON.parse(msg);
|
|
385
|
+
|
|
386
|
+
switch (type) {
|
|
387
|
+
case MESSAGE_TYPES.GQL_CONNECTION_INIT:
|
|
388
|
+
socket.onmessage.call(socket, {
|
|
389
|
+
data: JSON.stringify({
|
|
390
|
+
type: MESSAGE_TYPES.GQL_CONNECTION_ACK,
|
|
391
|
+
payload: {
|
|
392
|
+
connectionTimeoutMs: 99999,
|
|
393
|
+
},
|
|
394
|
+
})
|
|
395
|
+
} as MessageEvent);
|
|
396
|
+
setTimeout(() => {
|
|
397
|
+
socket.onmessage.call(socket, {
|
|
398
|
+
data: JSON.stringify({
|
|
399
|
+
id,
|
|
400
|
+
type: MESSAGE_TYPES.GQL_DATA,
|
|
401
|
+
payload: {
|
|
402
|
+
data: { something: 123 },
|
|
403
|
+
},
|
|
404
|
+
})
|
|
405
|
+
} as MessageEvent);
|
|
406
|
+
|
|
407
|
+
}, 100);
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}, 100);
|
|
412
|
+
|
|
413
|
+
return socket;
|
|
414
|
+
});
|
|
415
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
416
|
+
auth: {
|
|
417
|
+
type: AUTH_TYPE.API_KEY,
|
|
418
|
+
apiKey: 'xxxxx'
|
|
419
|
+
},
|
|
420
|
+
region: 'us-west-2',
|
|
421
|
+
url: 'https://apikeytest.testcustomdomain.com/graphql',
|
|
422
|
+
keepAliveTimeoutMs: 123456,
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
expect(link).toBeInstanceOf(AppSyncRealTimeSubscriptionHandshakeLink);
|
|
426
|
+
expect((link as any).keepAliveTimeout).toBe(123456);
|
|
427
|
+
|
|
428
|
+
const sub = execute(link, { query }).subscribe({
|
|
429
|
+
error: (err) => {
|
|
430
|
+
console.log(JSON.stringify(err));
|
|
431
|
+
fail();
|
|
432
|
+
},
|
|
433
|
+
next: (data) => {
|
|
434
|
+
expect((link as any).keepAliveTimeout).toBe(123456);
|
|
435
|
+
done();
|
|
436
|
+
sub.unsubscribe();
|
|
437
|
+
},
|
|
438
|
+
complete: () => {
|
|
439
|
+
console.log('done with this');
|
|
440
|
+
fail();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("Uses service-provided timeout when no custom keepAliveTimeoutMs is configured", (done) => {
|
|
447
|
+
const id = "abcd-efgh-ijkl-mnop";
|
|
448
|
+
uuid.mockImplementationOnce(() => id);
|
|
449
|
+
|
|
450
|
+
expect.assertions(5);
|
|
451
|
+
jest.spyOn(Date.prototype, 'toISOString').mockImplementation(jest.fn(() => {
|
|
452
|
+
return "2019-11-13T18:47:04.733Z";
|
|
453
|
+
}));
|
|
454
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn((url, protocol) => {
|
|
455
|
+
expect(url).toBe('wss://apikeytest.testcustomdomain.com/graphql/realtime?header=eyJob3N0IjoiYXBpa2V5dGVzdC50ZXN0Y3VzdG9tZG9tYWluLmNvbSIsIngtYW16LWRhdGUiOiIyMDE5MTExM1QxODQ3MDRaIiwieC1hcGkta2V5IjoieHh4eHgifQ==&payload=e30=');
|
|
456
|
+
expect(protocol).toBe('graphql-ws');
|
|
457
|
+
const socket = new myWebSocket();
|
|
458
|
+
|
|
459
|
+
setTimeout(() => {
|
|
460
|
+
socket.close = () => {};
|
|
461
|
+
socket.onopen.call(socket, (undefined as unknown as Event));
|
|
462
|
+
socket.send = (msg: string) => {
|
|
463
|
+
const { type } = JSON.parse(msg);
|
|
464
|
+
|
|
465
|
+
switch (type) {
|
|
466
|
+
case MESSAGE_TYPES.GQL_CONNECTION_INIT:
|
|
467
|
+
socket.onmessage.call(socket, {
|
|
468
|
+
data: JSON.stringify({
|
|
469
|
+
type: MESSAGE_TYPES.GQL_CONNECTION_ACK,
|
|
470
|
+
payload: {
|
|
471
|
+
connectionTimeoutMs: 99999,
|
|
472
|
+
},
|
|
473
|
+
})
|
|
474
|
+
} as MessageEvent);
|
|
475
|
+
setTimeout(() => {
|
|
476
|
+
socket.onmessage.call(socket, {
|
|
477
|
+
data: JSON.stringify({
|
|
478
|
+
id,
|
|
479
|
+
type: MESSAGE_TYPES.GQL_DATA,
|
|
480
|
+
payload: {
|
|
481
|
+
data: { something: 123 },
|
|
482
|
+
},
|
|
483
|
+
})
|
|
484
|
+
} as MessageEvent);
|
|
485
|
+
|
|
486
|
+
}, 100);
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
}, 100);
|
|
491
|
+
|
|
492
|
+
return socket;
|
|
493
|
+
});
|
|
494
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
495
|
+
auth: {
|
|
496
|
+
type: AUTH_TYPE.API_KEY,
|
|
497
|
+
apiKey: 'xxxxx'
|
|
498
|
+
},
|
|
499
|
+
region: 'us-west-2',
|
|
500
|
+
url: 'https://apikeytest.testcustomdomain.com/graphql',
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
expect(link).toBeInstanceOf(AppSyncRealTimeSubscriptionHandshakeLink);
|
|
504
|
+
expect((link as any).keepAliveTimeout).toBeUndefined();
|
|
505
|
+
|
|
506
|
+
const sub = execute(link, { query }).subscribe({
|
|
507
|
+
error: (err) => {
|
|
508
|
+
console.log(JSON.stringify(err));
|
|
509
|
+
fail();
|
|
510
|
+
},
|
|
511
|
+
next: (data) => {
|
|
512
|
+
expect((link as any).keepAliveTimeout).toBe(99999);
|
|
513
|
+
done();
|
|
514
|
+
sub.unsubscribe();
|
|
515
|
+
},
|
|
516
|
+
complete: () => {
|
|
517
|
+
console.log('done with this');
|
|
518
|
+
fail();
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
|
|
205
525
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CONTROL_EVENTS_KEY } from "./subscription-handshake-link";
|
|
2
2
|
import { ApolloLink } from "apollo-link";
|
|
3
|
-
import {
|
|
4
|
-
declare function createSubscriptionHandshakeLink(args:
|
|
3
|
+
import { AppSyncRealTimeSubscriptionConfig } from "./types";
|
|
4
|
+
declare function createSubscriptionHandshakeLink(args: AppSyncRealTimeSubscriptionConfig, resultsFetcherLink?: ApolloLink): ApolloLink;
|
|
5
5
|
declare function createSubscriptionHandshakeLink(url: string, resultsFetcherLink?: ApolloLink): ApolloLink;
|
|
6
6
|
export { CONTROL_EVENTS_KEY, createSubscriptionHandshakeLink };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright 2017-
|
|
2
|
+
* Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { ApolloLink, Observable, Operation, FetchResult } from "apollo-link";
|
|
6
|
-
import {
|
|
6
|
+
import { AppSyncRealTimeSubscriptionConfig } from "./types";
|
|
7
7
|
export declare const CONTROL_EVENTS_KEY = "@@controlEvents";
|
|
8
8
|
export declare class AppSyncRealTimeSubscriptionHandshakeLink extends ApolloLink {
|
|
9
9
|
private url;
|
|
@@ -12,17 +12,18 @@ export declare class AppSyncRealTimeSubscriptionHandshakeLink extends ApolloLink
|
|
|
12
12
|
private awsRealTimeSocket;
|
|
13
13
|
private socketStatus;
|
|
14
14
|
private keepAliveTimeoutId;
|
|
15
|
-
private keepAliveTimeout
|
|
15
|
+
private keepAliveTimeout?;
|
|
16
16
|
private subscriptionObserverMap;
|
|
17
17
|
private promiseArray;
|
|
18
|
-
constructor({ url: theUrl, region: theRegion, auth: theAuth }:
|
|
18
|
+
constructor({ url: theUrl, region: theRegion, auth: theAuth, keepAliveTimeoutMs }: AppSyncRealTimeSubscriptionConfig);
|
|
19
|
+
private isCustomDomain;
|
|
19
20
|
request(operation: Operation): Observable<FetchResult<{
|
|
20
21
|
[key: string]: any;
|
|
21
22
|
}, Record<string, any>, Record<string, any>>>;
|
|
22
23
|
private _verifySubscriptionAlreadyStarted;
|
|
23
24
|
private _sendUnsubscriptionMessage;
|
|
24
25
|
private _removeSubscriptionObserver;
|
|
25
|
-
private
|
|
26
|
+
private _closeSocketIfRequired;
|
|
26
27
|
private _startSubscriptionWithAWSAppSyncRealTime;
|
|
27
28
|
private _initializeWebSocketConnection;
|
|
28
29
|
private _awsRealTimeHeaderBasedAuth;
|
|
@@ -35,5 +36,4 @@ export declare class AppSyncRealTimeSubscriptionHandshakeLink extends ApolloLink
|
|
|
35
36
|
private _errorDisconnect;
|
|
36
37
|
private _timeoutStartSubscriptionAck;
|
|
37
38
|
static createWebSocket(awsRealTimeUrl: string, protocol: string): WebSocket;
|
|
38
|
-
private static _discoverAppSyncRealTimeEndpoint;
|
|
39
39
|
}
|
|
@@ -61,7 +61,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
61
61
|
};
|
|
62
62
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
63
|
/*!
|
|
64
|
-
* Copyright 2017-
|
|
64
|
+
* Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
65
65
|
* SPDX-License-Identifier: Apache-2.0
|
|
66
66
|
*/
|
|
67
67
|
var apollo_link_1 = require("apollo-link");
|
|
@@ -89,24 +89,39 @@ var CONNECTION_INIT_TIMEOUT = 15000;
|
|
|
89
89
|
* Time in milliseconds to wait for GQL_START_ACK message
|
|
90
90
|
*/
|
|
91
91
|
var START_ACK_TIMEOUT = 15000;
|
|
92
|
+
/**
|
|
93
|
+
* Frequency in milliseconds in which the server sends GQL_CONNECTION_KEEP_ALIVE messages
|
|
94
|
+
*/
|
|
95
|
+
var SERVER_KEEP_ALIVE_TIMEOUT = 1 * 60 * 1000;
|
|
92
96
|
/**
|
|
93
97
|
* Default Time in milliseconds to wait for GQL_CONNECTION_KEEP_ALIVE message
|
|
94
98
|
*/
|
|
95
99
|
var DEFAULT_KEEP_ALIVE_TIMEOUT = 5 * 60 * 1000;
|
|
100
|
+
var standardDomainPattern = /^https:\/\/\w{26}\.appsync\-api\.\w{2}(?:(?:\-\w{2,})+)\-\d\.amazonaws.com\/graphql$/i;
|
|
101
|
+
var customDomainPath = '/realtime';
|
|
96
102
|
var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super) {
|
|
97
103
|
__extends(AppSyncRealTimeSubscriptionHandshakeLink, _super);
|
|
98
104
|
function AppSyncRealTimeSubscriptionHandshakeLink(_a) {
|
|
99
|
-
var theUrl = _a.url, theRegion = _a.region, theAuth = _a.auth;
|
|
105
|
+
var theUrl = _a.url, theRegion = _a.region, theAuth = _a.auth, keepAliveTimeoutMs = _a.keepAliveTimeoutMs;
|
|
100
106
|
var _this = _super.call(this) || this;
|
|
101
107
|
_this.socketStatus = types_1.SOCKET_STATUS.CLOSED;
|
|
102
|
-
_this.keepAliveTimeout =
|
|
108
|
+
_this.keepAliveTimeout = undefined;
|
|
103
109
|
_this.subscriptionObserverMap = new Map();
|
|
104
110
|
_this.promiseArray = [];
|
|
105
111
|
_this.url = theUrl;
|
|
106
112
|
_this.region = theRegion;
|
|
107
113
|
_this.auth = theAuth;
|
|
114
|
+
_this.keepAliveTimeout = keepAliveTimeoutMs;
|
|
115
|
+
if (_this.keepAliveTimeout < SERVER_KEEP_ALIVE_TIMEOUT) {
|
|
116
|
+
var configName = 'keepAliveTimeoutMs';
|
|
117
|
+
throw new Error(configName + " must be greater than or equal to " + SERVER_KEEP_ALIVE_TIMEOUT + " (" + _this.keepAliveTimeout + " used).");
|
|
118
|
+
}
|
|
108
119
|
return _this;
|
|
109
120
|
}
|
|
121
|
+
// Check if url matches standard domain pattern
|
|
122
|
+
AppSyncRealTimeSubscriptionHandshakeLink.prototype.isCustomDomain = function (url) {
|
|
123
|
+
return url.match(standardDomainPattern) === null;
|
|
124
|
+
};
|
|
110
125
|
AppSyncRealTimeSubscriptionHandshakeLink.prototype.request = function (operation) {
|
|
111
126
|
var _a;
|
|
112
127
|
var _this = this;
|
|
@@ -221,22 +236,25 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super)
|
|
|
221
236
|
};
|
|
222
237
|
AppSyncRealTimeSubscriptionHandshakeLink.prototype._removeSubscriptionObserver = function (subscriptionId) {
|
|
223
238
|
this.subscriptionObserverMap.delete(subscriptionId);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this._closeSocketWhenFlushed();
|
|
227
|
-
}
|
|
239
|
+
// Verifying for 1000ms after removing subscription in case there are new subscriptions on mount / unmount
|
|
240
|
+
setTimeout(this._closeSocketIfRequired.bind(this), 1000);
|
|
228
241
|
};
|
|
229
|
-
AppSyncRealTimeSubscriptionHandshakeLink.prototype.
|
|
230
|
-
|
|
231
|
-
|
|
242
|
+
AppSyncRealTimeSubscriptionHandshakeLink.prototype._closeSocketIfRequired = function () {
|
|
243
|
+
if (this.subscriptionObserverMap.size > 0) {
|
|
244
|
+
// There are active subscriptions on the WebSocket
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
232
247
|
if (!this.awsRealTimeSocket) {
|
|
233
248
|
this.socketStatus = types_1.SOCKET_STATUS.CLOSED;
|
|
234
249
|
return;
|
|
235
250
|
}
|
|
236
251
|
if (this.awsRealTimeSocket.bufferedAmount > 0) {
|
|
237
|
-
|
|
252
|
+
// There is still data on the WebSocket
|
|
253
|
+
setTimeout(this._closeSocketIfRequired.bind(this), 1000);
|
|
238
254
|
}
|
|
239
255
|
else {
|
|
256
|
+
logger("closing WebSocket...");
|
|
257
|
+
clearTimeout(this.keepAliveTimeoutId);
|
|
240
258
|
var tempSocket = this.awsRealTimeSocket;
|
|
241
259
|
tempSocket.close(1000);
|
|
242
260
|
this.awsRealTimeSocket = null;
|
|
@@ -350,7 +368,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super)
|
|
|
350
368
|
return;
|
|
351
369
|
}
|
|
352
370
|
return new Promise(function (res, rej) { return __awaiter(_this, void 0, void 0, function () {
|
|
353
|
-
var
|
|
371
|
+
var payloadString, headerString, _a, _b, headerQs, payloadQs, discoverableEndpoint, awsRealTimeUrl, err_2;
|
|
354
372
|
return __generator(this, function (_c) {
|
|
355
373
|
switch (_c.label) {
|
|
356
374
|
case 0:
|
|
@@ -360,7 +378,6 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super)
|
|
|
360
378
|
case 1:
|
|
361
379
|
_c.trys.push([1, 4, , 5]);
|
|
362
380
|
this.socketStatus = types_1.SOCKET_STATUS.CONNECTING;
|
|
363
|
-
discoverableEndpoint = AppSyncRealTimeSubscriptionHandshakeLink._discoverAppSyncRealTimeEndpoint(this.url);
|
|
364
381
|
payloadString = "{}";
|
|
365
382
|
_b = (_a = JSON).stringify;
|
|
366
383
|
return [4 /*yield*/, this._awsRealTimeHeaderBasedAuth({
|
|
@@ -372,12 +389,22 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super)
|
|
|
372
389
|
region: region,
|
|
373
390
|
credentials: credentials,
|
|
374
391
|
token: token,
|
|
375
|
-
graphql_headers: function () { }
|
|
392
|
+
graphql_headers: function () { }
|
|
376
393
|
})];
|
|
377
394
|
case 2:
|
|
378
395
|
headerString = _b.apply(_a, [_c.sent()]);
|
|
379
396
|
headerQs = Buffer.from(headerString).toString("base64");
|
|
380
397
|
payloadQs = Buffer.from(payloadString).toString("base64");
|
|
398
|
+
discoverableEndpoint = appSyncGraphqlEndpoint;
|
|
399
|
+
if (this.isCustomDomain(discoverableEndpoint)) {
|
|
400
|
+
discoverableEndpoint = discoverableEndpoint.concat(customDomainPath);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
discoverableEndpoint = discoverableEndpoint.replace('appsync-api', 'appsync-realtime-api').replace('gogi-beta', 'grt-beta');
|
|
404
|
+
}
|
|
405
|
+
discoverableEndpoint = discoverableEndpoint
|
|
406
|
+
.replace("https://", "wss://")
|
|
407
|
+
.replace('http://', 'ws://');
|
|
381
408
|
awsRealTimeUrl = discoverableEndpoint + "?header=" + headerQs + "&payload=" + payloadQs;
|
|
382
409
|
return [4 /*yield*/, this._initializeRetryableHandshake({ awsRealTimeUrl: awsRealTimeUrl })];
|
|
383
410
|
case 3:
|
|
@@ -589,14 +616,14 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super)
|
|
|
589
616
|
rej(new Error(JSON.stringify(event)));
|
|
590
617
|
};
|
|
591
618
|
_this.awsRealTimeSocket.onmessage = function (message) {
|
|
619
|
+
var _a;
|
|
592
620
|
logger("subscription message from AWS AppSyncRealTime: " + message.data + " ");
|
|
593
621
|
var data = JSON.parse(message.data);
|
|
594
|
-
var type = data.type,
|
|
622
|
+
var type = data.type, _b = data.payload, _c = (_b === void 0 ? {} : _b).connectionTimeoutMs, connectionTimeoutMs = _c === void 0 ? DEFAULT_KEEP_ALIVE_TIMEOUT : _c;
|
|
595
623
|
if (type === types_1.MESSAGE_TYPES.GQL_CONNECTION_ACK) {
|
|
596
624
|
ackOk = true;
|
|
597
|
-
_this.keepAliveTimeout = connectionTimeoutMs;
|
|
598
|
-
_this.awsRealTimeSocket.onmessage =
|
|
599
|
-
_this._handleIncomingSubscriptionMessage.bind(_this);
|
|
625
|
+
_this.keepAliveTimeout = (_a = _this.keepAliveTimeout) !== null && _a !== void 0 ? _a : connectionTimeoutMs;
|
|
626
|
+
_this.awsRealTimeSocket.onmessage = _this._handleIncomingSubscriptionMessage.bind(_this);
|
|
600
627
|
_this.awsRealTimeSocket.onerror = function (err) {
|
|
601
628
|
logger(err);
|
|
602
629
|
_this._errorDisconnect(types_1.CONTROL_MSG.CONNECTION_CLOSED);
|
|
@@ -609,7 +636,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super)
|
|
|
609
636
|
return;
|
|
610
637
|
}
|
|
611
638
|
if (type === types_1.MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
|
|
612
|
-
var
|
|
639
|
+
var _d = data.payload, _e = (_d === void 0 ? {} : _d).errors, _f = (_e === void 0 ? [] : _e)[0], _g = _f === void 0 ? {} : _f, _h = _g.errorType, errorType = _h === void 0 ? "" : _h, _j = _g.errorCode, errorCode = _j === void 0 ? 0 : _j;
|
|
613
640
|
rej({ errorType: errorType, errorCode: errorCode });
|
|
614
641
|
}
|
|
615
642
|
};
|
|
@@ -759,13 +786,6 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /** @class */ (function (_super)
|
|
|
759
786
|
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = function (awsRealTimeUrl, protocol) {
|
|
760
787
|
return new WebSocket(awsRealTimeUrl, protocol);
|
|
761
788
|
};
|
|
762
|
-
AppSyncRealTimeSubscriptionHandshakeLink._discoverAppSyncRealTimeEndpoint = function (url) {
|
|
763
|
-
return url
|
|
764
|
-
.replace("https://", "wss://")
|
|
765
|
-
.replace("http://", "ws://")
|
|
766
|
-
.replace("appsync-api", "appsync-realtime-api")
|
|
767
|
-
.replace("gogi-beta", "grt-beta");
|
|
768
|
-
};
|
|
769
789
|
return AppSyncRealTimeSubscriptionHandshakeLink;
|
|
770
790
|
}(apollo_link_1.ApolloLink));
|
|
771
791
|
exports.AppSyncRealTimeSubscriptionHandshakeLink = AppSyncRealTimeSubscriptionHandshakeLink;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -72,6 +72,9 @@ export declare type UrlInfo = {
|
|
|
72
72
|
auth: AuthOptions;
|
|
73
73
|
region: string;
|
|
74
74
|
};
|
|
75
|
+
export declare type AppSyncRealTimeSubscriptionConfig = UrlInfo & {
|
|
76
|
+
keepAliveTimeoutMs?: number;
|
|
77
|
+
};
|
|
75
78
|
export declare type ObserverQuery = {
|
|
76
79
|
observer: ZenObservable.SubscriptionObserver<any>;
|
|
77
80
|
query: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aws-appsync-subscription-link",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "AWS Mobile AppSync SDK for JavaScript",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"apollo-link-context": "1.0.11",
|
|
24
24
|
"apollo-link-http": "1.5.8",
|
|
25
25
|
"apollo-link-retry": "2.2.7",
|
|
26
|
-
"aws-appsync-auth-link": "^2.0.
|
|
26
|
+
"aws-appsync-auth-link": "^2.0.8",
|
|
27
27
|
"debug": "2.6.9",
|
|
28
28
|
"url": "^0.11.0"
|
|
29
29
|
},
|