aws-appsync-subscription-link 4.0.0 → 4.0.1
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 +7 -0
- package/__tests__/link/realtime-subscription-handshake-link-test.ts +114 -0
- package/lib/index.d.mts +21 -0
- package/lib/index.d.ts +21 -0
- package/lib/index.js +10 -7
- package/lib/index.mjs +10 -7
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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="4.1.0"></a>
|
|
7
|
+
# [4.1.0] (Unreleased)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **aws-appsync-subscription-link:** add `proxy` config option to support routing subscriptions through CloudFront or other CDN proxies ([#710](https://github.com/aws-amplify/amplify-category-api/issues/710))
|
|
12
|
+
|
|
6
13
|
<a name="4.0.0"></a>
|
|
7
14
|
# [4.0.0](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-subscription-link@4.0.0-next.1...aws-appsync-subscription-link@4.0.0) (2025-11-19)
|
|
8
15
|
|
|
@@ -428,6 +428,120 @@ describe("RealTime subscription link", () => {
|
|
|
428
428
|
});
|
|
429
429
|
});
|
|
430
430
|
|
|
431
|
+
test("Can instantiate link with proxy config", () => {
|
|
432
|
+
expect.assertions(1);
|
|
433
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
434
|
+
auth: {
|
|
435
|
+
type: AUTH_TYPE.API_KEY,
|
|
436
|
+
apiKey: "xxxxx",
|
|
437
|
+
},
|
|
438
|
+
region: "us-west-2",
|
|
439
|
+
url: "https://firsttesturl12345678901234.appsync-api.us-west-2.amazonaws.com/graphql",
|
|
440
|
+
proxy: {
|
|
441
|
+
url: "https://d111111abcdef8.cloudfront.net/graphql",
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
expect(link).toBeInstanceOf(AppSyncRealTimeSubscriptionHandshakeLink);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test("Initialize WebSocket through proxy for API KEY - routes to proxy URL with AppSync host in auth", (done) => {
|
|
449
|
+
expect.assertions(2);
|
|
450
|
+
jest.spyOn(Date.prototype, "toISOString").mockImplementation(
|
|
451
|
+
jest.fn(() => {
|
|
452
|
+
return "2019-11-13T18:47:04.733Z";
|
|
453
|
+
})
|
|
454
|
+
);
|
|
455
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
|
|
456
|
+
(url, protocol) => {
|
|
457
|
+
// WebSocket should connect to the proxy, not directly to AppSync
|
|
458
|
+
// The header should contain the original AppSync host for auth
|
|
459
|
+
const urlObj = new URL(url);
|
|
460
|
+
expect(urlObj.origin + urlObj.pathname).toBe(
|
|
461
|
+
"wss://d111111abcdef8.cloudfront.net/realtime"
|
|
462
|
+
);
|
|
463
|
+
// Decode the header to verify the host is the AppSync endpoint, not CloudFront
|
|
464
|
+
const headerB64 = urlObj.searchParams.get("header");
|
|
465
|
+
const header = JSON.parse(Buffer.from(headerB64, "base64").toString());
|
|
466
|
+
expect(header.host).toBe(
|
|
467
|
+
"proxytesturl12345678901234567.appsync-api.us-west-2.amazonaws.com"
|
|
468
|
+
);
|
|
469
|
+
done();
|
|
470
|
+
return new myWebSocket();
|
|
471
|
+
}
|
|
472
|
+
);
|
|
473
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
474
|
+
auth: {
|
|
475
|
+
type: AUTH_TYPE.API_KEY,
|
|
476
|
+
apiKey: "xxxxx",
|
|
477
|
+
},
|
|
478
|
+
region: "us-west-2",
|
|
479
|
+
url: "https://proxytesturl12345678901234567.appsync-api.us-west-2.amazonaws.com/graphql",
|
|
480
|
+
proxy: {
|
|
481
|
+
url: "https://d111111abcdef8.cloudfront.net/graphql",
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
execute(link, { query }, { client: mockClient }).subscribe({
|
|
486
|
+
error: (err) => {
|
|
487
|
+
fail;
|
|
488
|
+
},
|
|
489
|
+
next: (data) => {
|
|
490
|
+
done();
|
|
491
|
+
},
|
|
492
|
+
complete: () => {
|
|
493
|
+
done();
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test("Initialize WebSocket through proxy for COGNITO USER POOLS - host in auth is AppSync not proxy", (done) => {
|
|
499
|
+
expect.assertions(2);
|
|
500
|
+
jest.spyOn(Date.prototype, "toISOString").mockImplementation(
|
|
501
|
+
jest.fn(() => {
|
|
502
|
+
return "2019-11-13T18:47:04.733Z";
|
|
503
|
+
})
|
|
504
|
+
);
|
|
505
|
+
AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
|
|
506
|
+
(url, protocol) => {
|
|
507
|
+
const urlObj = new URL(url);
|
|
508
|
+
expect(urlObj.origin + urlObj.pathname).toBe(
|
|
509
|
+
"wss://mycdn.example.com/realtime"
|
|
510
|
+
);
|
|
511
|
+
const headerB64 = urlObj.searchParams.get("header");
|
|
512
|
+
const header = JSON.parse(Buffer.from(headerB64, "base64").toString());
|
|
513
|
+
expect(header.host).toBe(
|
|
514
|
+
"cognitoproxytesturl123456789.appsync-api.us-west-2.amazonaws.com"
|
|
515
|
+
);
|
|
516
|
+
done();
|
|
517
|
+
return new myWebSocket();
|
|
518
|
+
}
|
|
519
|
+
);
|
|
520
|
+
const link = new AppSyncRealTimeSubscriptionHandshakeLink({
|
|
521
|
+
auth: {
|
|
522
|
+
type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
|
|
523
|
+
jwtToken: "token",
|
|
524
|
+
},
|
|
525
|
+
region: "us-west-2",
|
|
526
|
+
url: "https://cognitoproxytesturl123456789.appsync-api.us-west-2.amazonaws.com/graphql",
|
|
527
|
+
proxy: {
|
|
528
|
+
url: "https://mycdn.example.com/graphql",
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
execute(link, { query }, { client: mockClient }).subscribe({
|
|
533
|
+
error: (err) => {
|
|
534
|
+
fail;
|
|
535
|
+
},
|
|
536
|
+
next: (data) => {
|
|
537
|
+
done();
|
|
538
|
+
},
|
|
539
|
+
complete: () => {
|
|
540
|
+
done();
|
|
541
|
+
},
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
|
|
431
545
|
test("Can use a custom keepAliveTimeoutMs", (done) => {
|
|
432
546
|
const id = "abcd-efgh-ijkl-mnop";
|
|
433
547
|
jest.mocked(uuid).mockImplementationOnce(() => id);
|
package/lib/index.d.mts
CHANGED
|
@@ -13,8 +13,29 @@ type UrlInfo = {
|
|
|
13
13
|
auth: AuthOptions;
|
|
14
14
|
region: string;
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Route connections through a proxy (e.g. CloudFront) instead of connecting
|
|
18
|
+
* directly to AppSync. The library will send traffic to the proxy URL while
|
|
19
|
+
* using the original AppSync host for authentication.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* {
|
|
24
|
+
* url: 'https://xxx.appsync-api.us-east-1.amazonaws.com/graphql',
|
|
25
|
+
* region: 'us-east-1',
|
|
26
|
+
* auth: { type: AUTH_TYPE.API_KEY, apiKey: 'da2-xxx' },
|
|
27
|
+
* proxy: { url: 'https://d111111abcdef8.cloudfront.net/graphql' }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
type ProxyConfig = {
|
|
32
|
+
/** The proxy endpoint that sits in front of AppSync (e.g. a CloudFront distribution URL). */
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
16
35
|
type AppSyncRealTimeSubscriptionConfig = UrlInfo & {
|
|
17
36
|
keepAliveTimeoutMs?: number;
|
|
37
|
+
/** Optional proxy configuration for routing through CloudFront or another CDN. */
|
|
38
|
+
proxy?: ProxyConfig;
|
|
18
39
|
};
|
|
19
40
|
|
|
20
41
|
declare function createSubscriptionHandshakeLink(args: AppSyncRealTimeSubscriptionConfig, resultsFetcherLink?: ApolloLink): ApolloLink;
|
package/lib/index.d.ts
CHANGED
|
@@ -13,8 +13,29 @@ type UrlInfo = {
|
|
|
13
13
|
auth: AuthOptions;
|
|
14
14
|
region: string;
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Route connections through a proxy (e.g. CloudFront) instead of connecting
|
|
18
|
+
* directly to AppSync. The library will send traffic to the proxy URL while
|
|
19
|
+
* using the original AppSync host for authentication.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* {
|
|
24
|
+
* url: 'https://xxx.appsync-api.us-east-1.amazonaws.com/graphql',
|
|
25
|
+
* region: 'us-east-1',
|
|
26
|
+
* auth: { type: AUTH_TYPE.API_KEY, apiKey: 'da2-xxx' },
|
|
27
|
+
* proxy: { url: 'https://d111111abcdef8.cloudfront.net/graphql' }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
type ProxyConfig = {
|
|
32
|
+
/** The proxy endpoint that sits in front of AppSync (e.g. a CloudFront distribution URL). */
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
16
35
|
type AppSyncRealTimeSubscriptionConfig = UrlInfo & {
|
|
17
36
|
keepAliveTimeoutMs?: number;
|
|
37
|
+
/** Optional proxy configuration for routing through CloudFront or another CDN. */
|
|
38
|
+
proxy?: ProxyConfig;
|
|
18
39
|
};
|
|
19
40
|
|
|
20
41
|
declare function createSubscriptionHandshakeLink(args: AppSyncRealTimeSubscriptionConfig, resultsFetcherLink?: ApolloLink): ApolloLink;
|
package/lib/index.js
CHANGED
|
@@ -2694,7 +2694,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
|
|
|
2694
2694
|
"use strict";
|
|
2695
2695
|
_inherits(_AppSyncRealTimeSubscriptionHandshakeLink, _import_core3_ApolloLink);
|
|
2696
2696
|
function _AppSyncRealTimeSubscriptionHandshakeLink(param) {
|
|
2697
|
-
var theUrl = param.url, theRegion = param.region, theAuth = param.auth, keepAliveTimeoutMs = param.keepAliveTimeoutMs;
|
|
2697
|
+
var theUrl = param.url, theRegion = param.region, theAuth = param.auth, keepAliveTimeoutMs = param.keepAliveTimeoutMs, proxy = param.proxy;
|
|
2698
2698
|
_class_call_check(this, _AppSyncRealTimeSubscriptionHandshakeLink);
|
|
2699
2699
|
var _this;
|
|
2700
2700
|
_this = _call_super(this, _AppSyncRealTimeSubscriptionHandshakeLink);
|
|
@@ -2705,6 +2705,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
|
|
|
2705
2705
|
_this.url = theUrl;
|
|
2706
2706
|
_this.region = theRegion;
|
|
2707
2707
|
_this.auth = theAuth;
|
|
2708
|
+
_this.proxyUrl = proxy === null || proxy === void 0 ? void 0 : proxy.url;
|
|
2708
2709
|
_this.keepAliveTimeout = keepAliveTimeoutMs;
|
|
2709
2710
|
if (_this.keepAliveTimeout < SERVER_KEEP_ALIVE_TIMEOUT) {
|
|
2710
2711
|
var configName = "keepAliveTimeoutMs";
|
|
@@ -3046,13 +3047,15 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
|
|
|
3046
3047
|
]);
|
|
3047
3048
|
headerQs = Buffer.from(headerString).toString("base64");
|
|
3048
3049
|
payloadQs = Buffer.from(payloadString).toString("base64");
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3050
|
+
if (this.proxyUrl) {
|
|
3051
|
+
discoverableEndpoint = this.proxyUrl.replace(/\/graphql$/, "").concat(customDomainPath).replace("https://", "wss://").replace("http://", "ws://");
|
|
3052
|
+
} else if (this.isCustomDomain(appSyncGraphqlEndpoint)) {
|
|
3053
|
+
discoverableEndpoint = appSyncGraphqlEndpoint.concat(customDomainPath);
|
|
3054
|
+
discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
|
|
3052
3055
|
} else {
|
|
3053
|
-
discoverableEndpoint =
|
|
3056
|
+
discoverableEndpoint = appSyncGraphqlEndpoint.replace("appsync-api", "appsync-realtime-api").replace("gogi-beta", "grt-beta");
|
|
3057
|
+
discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
|
|
3054
3058
|
}
|
|
3055
|
-
discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
|
|
3056
3059
|
awsRealTimeUrl = "".concat(discoverableEndpoint, "?header=").concat(headerQs, "&payload=").concat(payloadQs);
|
|
3057
3060
|
return [
|
|
3058
3061
|
4,
|
|
@@ -3123,7 +3126,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
|
|
|
3123
3126
|
{}
|
|
3124
3127
|
];
|
|
3125
3128
|
}
|
|
3126
|
-
host = url.parse(
|
|
3129
|
+
host = url.parse(this.url).host;
|
|
3127
3130
|
return [
|
|
3128
3131
|
4,
|
|
3129
3132
|
handler({
|
package/lib/index.mjs
CHANGED
|
@@ -2671,7 +2671,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
|
|
|
2671
2671
|
"use strict";
|
|
2672
2672
|
_inherits(_AppSyncRealTimeSubscriptionHandshakeLink, ApolloLink3);
|
|
2673
2673
|
function _AppSyncRealTimeSubscriptionHandshakeLink(param) {
|
|
2674
|
-
var theUrl = param.url, theRegion = param.region, theAuth = param.auth, keepAliveTimeoutMs = param.keepAliveTimeoutMs;
|
|
2674
|
+
var theUrl = param.url, theRegion = param.region, theAuth = param.auth, keepAliveTimeoutMs = param.keepAliveTimeoutMs, proxy = param.proxy;
|
|
2675
2675
|
_class_call_check(this, _AppSyncRealTimeSubscriptionHandshakeLink);
|
|
2676
2676
|
var _this;
|
|
2677
2677
|
_this = _call_super(this, _AppSyncRealTimeSubscriptionHandshakeLink);
|
|
@@ -2682,6 +2682,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
|
|
|
2682
2682
|
_this.url = theUrl;
|
|
2683
2683
|
_this.region = theRegion;
|
|
2684
2684
|
_this.auth = theAuth;
|
|
2685
|
+
_this.proxyUrl = proxy === null || proxy === void 0 ? void 0 : proxy.url;
|
|
2685
2686
|
_this.keepAliveTimeout = keepAliveTimeoutMs;
|
|
2686
2687
|
if (_this.keepAliveTimeout < SERVER_KEEP_ALIVE_TIMEOUT) {
|
|
2687
2688
|
var configName = "keepAliveTimeoutMs";
|
|
@@ -3023,13 +3024,15 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
|
|
|
3023
3024
|
]);
|
|
3024
3025
|
headerQs = Buffer.from(headerString).toString("base64");
|
|
3025
3026
|
payloadQs = Buffer.from(payloadString).toString("base64");
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3027
|
+
if (this.proxyUrl) {
|
|
3028
|
+
discoverableEndpoint = this.proxyUrl.replace(/\/graphql$/, "").concat(customDomainPath).replace("https://", "wss://").replace("http://", "ws://");
|
|
3029
|
+
} else if (this.isCustomDomain(appSyncGraphqlEndpoint)) {
|
|
3030
|
+
discoverableEndpoint = appSyncGraphqlEndpoint.concat(customDomainPath);
|
|
3031
|
+
discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
|
|
3029
3032
|
} else {
|
|
3030
|
-
discoverableEndpoint =
|
|
3033
|
+
discoverableEndpoint = appSyncGraphqlEndpoint.replace("appsync-api", "appsync-realtime-api").replace("gogi-beta", "grt-beta");
|
|
3034
|
+
discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
|
|
3031
3035
|
}
|
|
3032
|
-
discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
|
|
3033
3036
|
awsRealTimeUrl = "".concat(discoverableEndpoint, "?header=").concat(headerQs, "&payload=").concat(payloadQs);
|
|
3034
3037
|
return [
|
|
3035
3038
|
4,
|
|
@@ -3100,7 +3103,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
|
|
|
3100
3103
|
{}
|
|
3101
3104
|
];
|
|
3102
3105
|
}
|
|
3103
|
-
host = url.parse(
|
|
3106
|
+
host = url.parse(this.url).host;
|
|
3104
3107
|
return [
|
|
3105
3108
|
4,
|
|
3106
3109
|
handler({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aws-appsync-subscription-link",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"module": "lib/index.mjs",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test-watch": "jest --watch"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"aws-appsync-auth-link": "^4.0.
|
|
33
|
+
"aws-appsync-auth-link": "^4.0.1",
|
|
34
34
|
"debug": "2.6.9",
|
|
35
35
|
"url": "^0.11.0",
|
|
36
36
|
"zen-observable-ts": "^1.2.5"
|