@serve.zone/platformclient 4.1.1 → 32.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/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.platformclient.d.ts +38 -0
- package/dist_ts/classes.platformclient.js +69 -12
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/protocol.d.ts +29 -0
- package/dist_ts/protocol.js +67 -0
- package/package.json +8 -8
- package/readme.md +27 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.platformclient.ts +86 -12
- package/ts/index.ts +4 -0
- package/ts/protocol.ts +81 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/platformclient',
|
|
6
|
-
version: '
|
|
6
|
+
version: '32.0.1',
|
|
7
7
|
description: 'a module that makes it really easy to use the serve.zone platform inside your app'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSw0QkFBNEI7SUFDbEMsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLG1GQUFtRjtDQUNqRyxDQUFBIn0=
|
|
@@ -4,7 +4,10 @@ import { SzPushNotificationConnector } from './email/classes.pushnotificationcon
|
|
|
4
4
|
import { SzLetterConnector } from './email/classes.letterconnector.js';
|
|
5
5
|
import { SzWebPushConnector } from './webpush/classes.webpushconnector.js';
|
|
6
6
|
import { SzCoreMailConnector, type ISzCoreMailConnectorOptions } from './coremail/classes.coremailconnector.js';
|
|
7
|
+
import { SzPlatformProtocolIncompatibleError } from './protocol.js';
|
|
7
8
|
import * as plugins from './plugins.js';
|
|
9
|
+
/** What a caller can act on: a usable session, none, or a peer of another contract. */
|
|
10
|
+
export type TSzPlatformSessionStatus = 'connected' | 'disconnected' | 'protocol-incompatible';
|
|
8
11
|
export interface ISzPlatformClientOptions {
|
|
9
12
|
authorizationString?: string;
|
|
10
13
|
authorization?: string;
|
|
@@ -24,12 +27,32 @@ export declare class SzPlatformClient {
|
|
|
24
27
|
platformBindings: plugins.servezoneInterfaces.platform.IPlatformBinding[];
|
|
25
28
|
typedrouter: plugins.typedrequest.TypedRouter;
|
|
26
29
|
typedsocket?: plugins.typedsocket.TypedSocket;
|
|
30
|
+
/**
|
|
31
|
+
* The refusal this client met, kept for the caller that never saw it.
|
|
32
|
+
*
|
|
33
|
+
* A refused offer stands until an operator upgrades one of the two sides, and a refusal met
|
|
34
|
+
* while a reconnect restores the session reaches no caller at all, so it is read from here.
|
|
35
|
+
*/
|
|
36
|
+
lastProtocolRefusal?: SzPlatformProtocolIncompatibleError;
|
|
27
37
|
private identity?;
|
|
28
38
|
private authenticationGeneration;
|
|
29
39
|
private authenticationAbort?;
|
|
30
40
|
private authenticationOperations;
|
|
31
41
|
private renewalTimer?;
|
|
32
42
|
private statusSubscription?;
|
|
43
|
+
/**
|
|
44
|
+
* The name resolver behind {@link getConfiguredEnvVar}: process environment, then Docker secrets.
|
|
45
|
+
*
|
|
46
|
+
* This client owns no list of required variables and must never enforce anyone else's. Which
|
|
47
|
+
* names matter is a per-call decision here - the mail connector even composes them from an
|
|
48
|
+
* address token at send time - and every read site below treats a missing value as its own
|
|
49
|
+
* business. So the instance is pointed at this package's own directory, which holds no
|
|
50
|
+
* `qenv.yml`, instead of qenv's default of `process.cwd()`: that is the embedding application's
|
|
51
|
+
* directory, and since qenv 8.0.0 a name its `qenv.yml` lists under `required:` without a value
|
|
52
|
+
* makes the constructor throw `QenvMissingRequiredEnvVarsError`. A host declaring a requirement
|
|
53
|
+
* it has not met yet is the host's decision to act on, never a reason for this library to refuse
|
|
54
|
+
* construction, so `failOnMissing` is false as well.
|
|
55
|
+
*/
|
|
33
56
|
private qenvInstance;
|
|
34
57
|
private stopped;
|
|
35
58
|
private initPromise?;
|
|
@@ -50,6 +73,21 @@ export declare class SzPlatformClient {
|
|
|
50
73
|
private getConnectionAddress;
|
|
51
74
|
private getConfiguredAuthorizationString;
|
|
52
75
|
requireTypedSocket(): plugins.typedsocket.TypedSocket;
|
|
76
|
+
/**
|
|
77
|
+
* The platform session as a caller can act on it.
|
|
78
|
+
*
|
|
79
|
+
* `protocol-incompatible` outranks the transport state: it stands until an operator upgrades
|
|
80
|
+
* one of the two sides, and `lastProtocolRefusal` names both of them.
|
|
81
|
+
*/
|
|
82
|
+
getSessionStatus(): TSzPlatformSessionStatus;
|
|
83
|
+
/**
|
|
84
|
+
* Release the socket lifecycle a restoration was refused in; every other failure passes on.
|
|
85
|
+
*
|
|
86
|
+
* TypedSocket ends its own client on a denied restoration and never reconnects from one, so the
|
|
87
|
+
* reference held here is dead — keeping it would report an unavailable session instead of the
|
|
88
|
+
* incompatible peer.
|
|
89
|
+
*/
|
|
90
|
+
private releaseRefusedSocket;
|
|
53
91
|
private invalidateAuthentication;
|
|
54
92
|
private trackAuthentication;
|
|
55
93
|
private authenticate;
|
|
@@ -4,6 +4,7 @@ import { SzPushNotificationConnector } from './email/classes.pushnotificationcon
|
|
|
4
4
|
import { SzLetterConnector } from './email/classes.letterconnector.js';
|
|
5
5
|
import { SzWebPushConnector } from './webpush/classes.webpushconnector.js';
|
|
6
6
|
import { SzCoreMailConnector, } from './coremail/classes.coremailconnector.js';
|
|
7
|
+
import { platformSessionProtocolOffer, requireCompatibleProtocol, rethrowProtocolRefusal, SzPlatformProtocolIncompatibleError, } from './protocol.js';
|
|
7
8
|
import * as plugins from './plugins.js';
|
|
8
9
|
export class SzPlatformClient {
|
|
9
10
|
constructor(authorizationStringArg) {
|
|
@@ -12,7 +13,20 @@ export class SzPlatformClient {
|
|
|
12
13
|
this.typedrouter = new plugins.typedrequest.TypedRouter();
|
|
13
14
|
this.authenticationGeneration = 0;
|
|
14
15
|
this.authenticationOperations = new Set();
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* The name resolver behind {@link getConfiguredEnvVar}: process environment, then Docker secrets.
|
|
18
|
+
*
|
|
19
|
+
* This client owns no list of required variables and must never enforce anyone else's. Which
|
|
20
|
+
* names matter is a per-call decision here - the mail connector even composes them from an
|
|
21
|
+
* address token at send time - and every read site below treats a missing value as its own
|
|
22
|
+
* business. So the instance is pointed at this package's own directory, which holds no
|
|
23
|
+
* `qenv.yml`, instead of qenv's default of `process.cwd()`: that is the embedding application's
|
|
24
|
+
* directory, and since qenv 8.0.0 a name its `qenv.yml` lists under `required:` without a value
|
|
25
|
+
* makes the constructor throw `QenvMissingRequiredEnvVarsError`. A host declaring a requirement
|
|
26
|
+
* it has not met yet is the host's decision to act on, never a reason for this library to refuse
|
|
27
|
+
* construction, so `failOnMissing` is false as well.
|
|
28
|
+
*/
|
|
29
|
+
this.qenvInstance = new plugins.qenv.Qenv(import.meta.dirname, undefined, false);
|
|
16
30
|
this.stopped = false;
|
|
17
31
|
this.platformSocketAbortController = new AbortController();
|
|
18
32
|
this.emailConnector = new SzEmailConnector(this);
|
|
@@ -60,7 +74,8 @@ export class SzPlatformClient {
|
|
|
60
74
|
restoreConnection: (context) => {
|
|
61
75
|
this.invalidateAuthentication();
|
|
62
76
|
const generation = this.authenticationGeneration;
|
|
63
|
-
return this.trackAuthentication(this.authenticate(context.createTypedRequest, context.abortSignal, generation)
|
|
77
|
+
return this.trackAuthentication(this.authenticate(context.createTypedRequest, context.abortSignal, generation)
|
|
78
|
+
.catch((error) => this.releaseRefusedSocket(error, generation)));
|
|
64
79
|
},
|
|
65
80
|
});
|
|
66
81
|
if (this.stopped) {
|
|
@@ -135,10 +150,43 @@ export class SzPlatformClient {
|
|
|
135
150
|
}
|
|
136
151
|
requireTypedSocket() {
|
|
137
152
|
this.assertRunning();
|
|
138
|
-
|
|
139
|
-
|
|
153
|
+
const socket = this.typedsocket;
|
|
154
|
+
const status = this.getSessionStatus();
|
|
155
|
+
// A refused protocol is the one unavailability a caller can act on, so it keeps its name.
|
|
156
|
+
if (status === 'protocol-incompatible' && this.lastProtocolRefusal)
|
|
157
|
+
throw this.lastProtocolRefusal;
|
|
158
|
+
if (!socket || status !== 'connected') {
|
|
140
159
|
throw new Error('Cloudly platform session is unavailable.');
|
|
141
|
-
|
|
160
|
+
}
|
|
161
|
+
return socket;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The platform session as a caller can act on it.
|
|
165
|
+
*
|
|
166
|
+
* `protocol-incompatible` outranks the transport state: it stands until an operator upgrades
|
|
167
|
+
* one of the two sides, and `lastProtocolRefusal` names both of them.
|
|
168
|
+
*/
|
|
169
|
+
getSessionStatus() {
|
|
170
|
+
if (this.lastProtocolRefusal)
|
|
171
|
+
return 'protocol-incompatible';
|
|
172
|
+
if (this.typedsocket?.getStatus() === 'connected' && this.identity
|
|
173
|
+
&& this.identity.expiresAt > Date.now())
|
|
174
|
+
return 'connected';
|
|
175
|
+
return 'disconnected';
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Release the socket lifecycle a restoration was refused in; every other failure passes on.
|
|
179
|
+
*
|
|
180
|
+
* TypedSocket ends its own client on a denied restoration and never reconnects from one, so the
|
|
181
|
+
* reference held here is dead — keeping it would report an unavailable session instead of the
|
|
182
|
+
* incompatible peer.
|
|
183
|
+
*/
|
|
184
|
+
releaseRefusedSocket(errorArg, generationArg) {
|
|
185
|
+
if (errorArg instanceof SzPlatformProtocolIncompatibleError
|
|
186
|
+
&& generationArg === this.authenticationGeneration) {
|
|
187
|
+
this.typedsocket = undefined;
|
|
188
|
+
}
|
|
189
|
+
throw errorArg;
|
|
142
190
|
}
|
|
143
191
|
invalidateAuthentication() {
|
|
144
192
|
this.authenticationGeneration++;
|
|
@@ -173,20 +221,29 @@ export class SzPlatformClient {
|
|
|
173
221
|
|| !Number.isSafeInteger(identity.expiresAt) || identity.expiresAt <= Date.now() + 1000) {
|
|
174
222
|
throw new Error('Invalid platform identity.');
|
|
175
223
|
}
|
|
176
|
-
const requestData = plugins.servezoneInterfaces.data.
|
|
177
|
-
|
|
224
|
+
const requestData = plugins.servezoneInterfaces.data.snapshotRegisterCloudlyClientSessionRequest({
|
|
225
|
+
identity: { jwt: identity.jwt }, protocol: platformSessionProtocolOffer,
|
|
178
226
|
});
|
|
179
|
-
const registration = createRequest('
|
|
227
|
+
const registration = createRequest('registerCloudlyClientSession');
|
|
180
228
|
registration.skipHooks = true;
|
|
181
|
-
const registered = await registration
|
|
182
|
-
|
|
229
|
+
const registered = await registration
|
|
230
|
+
.fire(requestData, { maxRetries: 0, abortSignal: signal })
|
|
231
|
+
.catch(rethrowProtocolRefusal);
|
|
232
|
+
requireCompatibleProtocol(plugins.servezoneInterfaces.data
|
|
233
|
+
.snapshotRegisterCloudlyClientSessionResponse(registered).protocol);
|
|
183
234
|
requireCurrent();
|
|
184
235
|
this.identity = { type: identity.type, role: identity.role, userId: identity.userId,
|
|
185
236
|
name: identity.userId, expiresAt: identity.expiresAt, jwt: requestData.identity.jwt };
|
|
186
237
|
}
|
|
187
|
-
catch {
|
|
238
|
+
catch (error) {
|
|
188
239
|
if (this.stopped || signal.aborted || generation !== this.authenticationGeneration)
|
|
189
240
|
throw this.createAbortError();
|
|
241
|
+
// A refused offer rejected no credential of this caller's, so it is named rather than
|
|
242
|
+
// collapsed, and kept where a restoration that has no caller can still be read.
|
|
243
|
+
if (error instanceof SzPlatformProtocolIncompatibleError) {
|
|
244
|
+
this.lastProtocolRefusal = error;
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
190
247
|
throw new Error('Cloudly platform authentication failed.');
|
|
191
248
|
}
|
|
192
249
|
}
|
|
@@ -333,4 +390,4 @@ export class SzPlatformClient {
|
|
|
333
390
|
return processEnv?.[envNameArg];
|
|
334
391
|
}
|
|
335
392
|
}
|
|
336
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wbGF0Zm9ybWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMucGxhdGZvcm1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDckUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBQzNGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzNFLE9BQU8sRUFDTCxtQkFBbUIsR0FFcEIsTUFBTSx5Q0FBeUMsQ0FBQztBQUNqRCxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQWV4QyxNQUFNLE9BQU8sZ0JBQWdCO0lBMEIzQixZQUFZLHNCQUEwRDtRQXpCL0QsY0FBUyxHQUFHLEtBQUssQ0FBQztRQUdsQixxQkFBZ0IsR0FBNEQsRUFBRSxDQUFDO1FBQy9FLGdCQUFXLEdBQXFDLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUd0Riw2QkFBd0IsR0FBRyxDQUFDLENBQUM7UUFFN0IsNkJBQXdCLEdBQUcsSUFBSSxHQUFHLEVBQW9CLENBQUM7UUFHdkQsaUJBQVksR0FBRyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdkMsWUFBTyxHQUFHLEtBQUssQ0FBQztRQUdQLGtDQUE2QixHQUFHLElBQUksZUFBZSxFQUFFLENBQUM7UUFFaEUsbUJBQWMsR0FBRyxJQUFJLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzVDLGlCQUFZLEdBQUcsSUFBSSxjQUFjLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEMsOEJBQXlCLEdBQUcsSUFBSSwy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
|
|
393
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wbGF0Zm9ybWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMucGxhdGZvcm1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDckUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBQzNGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzNFLE9BQU8sRUFDTCxtQkFBbUIsR0FFcEIsTUFBTSx5Q0FBeUMsQ0FBQztBQUNqRCxPQUFPLEVBQ0wsNEJBQTRCLEVBQzVCLHlCQUF5QixFQUN6QixzQkFBc0IsRUFDdEIsbUNBQW1DLEdBQ3BDLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBa0J4QyxNQUFNLE9BQU8sZ0JBQWdCO0lBOEMzQixZQUFZLHNCQUEwRDtRQTdDL0QsY0FBUyxHQUFHLEtBQUssQ0FBQztRQUdsQixxQkFBZ0IsR0FBNEQsRUFBRSxDQUFDO1FBQy9FLGdCQUFXLEdBQXFDLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQVV0Riw2QkFBd0IsR0FBRyxDQUFDLENBQUM7UUFFN0IsNkJBQXdCLEdBQUcsSUFBSSxHQUFHLEVBQW9CLENBQUM7UUFHL0Q7Ozs7Ozs7Ozs7OztXQVlHO1FBQ0ssaUJBQVksR0FBRyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUM1RSxZQUFPLEdBQUcsS0FBSyxDQUFDO1FB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
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './classes.platformclient.js';
|
|
2
|
+
export { platformSessionProtocolOffer, SzPlatformProtocolIncompatibleError, } from './protocol.js';
|
|
2
3
|
export * from './coremail/classes.coremailconnector.js';
|
|
3
4
|
export * from './email/classes.emailconnector.js';
|
|
4
5
|
export * from './webpush/classes.webpushconnector.js';
|
package/dist_ts/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './classes.platformclient.js';
|
|
2
|
+
export { platformSessionProtocolOffer, SzPlatformProtocolIncompatibleError, } from './protocol.js';
|
|
2
3
|
export * from './coremail/classes.coremailconnector.js';
|
|
3
4
|
export * from './email/classes.emailconnector.js';
|
|
4
5
|
export * from './webpush/classes.webpushconnector.js';
|
|
5
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLE9BQU8sRUFDTCw0QkFBNEIsRUFDNUIsbUNBQW1DLEdBQ3BDLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLGNBQWMseUNBQXlDLENBQUM7QUFDeEQsY0FBYyxtQ0FBbUMsQ0FBQztBQUNsRCxjQUFjLHVDQUF1QyxDQUFDIn0=
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
/** This build's offer for the one session kind the platform client opens. */
|
|
3
|
+
export declare const platformSessionProtocolOffer: plugins.servezoneInterfaces.protocol.IProtocolOffer;
|
|
4
|
+
/**
|
|
5
|
+
* Two peers that cannot serve one session, named rather than collapsed into an authentication
|
|
6
|
+
* failure.
|
|
7
|
+
*
|
|
8
|
+
* A refusal stands until an operator upgrades one of the two sides, so it is never retried: the
|
|
9
|
+
* caller is rejected with it, and a refusal met while a reconnect restores the session — where no
|
|
10
|
+
* caller waits — ends that socket lifecycle and is retained on the client instead.
|
|
11
|
+
*/
|
|
12
|
+
export declare class SzPlatformProtocolIncompatibleError extends Error {
|
|
13
|
+
readonly refusal: plugins.servezoneInterfaces.protocol.IProtocolRefusal;
|
|
14
|
+
constructor(refusalArg: plugins.servezoneInterfaces.protocol.IProtocolRefusal, optionsArg?: ErrorOptions);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Rethrow a refused offer as this client's named error; every other failure passes untouched.
|
|
18
|
+
*
|
|
19
|
+
* The transport error stays attached as the cause, so the correlation a caller needs to find the
|
|
20
|
+
* hop in a log is not lost behind the refusal.
|
|
21
|
+
*/
|
|
22
|
+
export declare const rethrowProtocolRefusal: (errorArg: unknown) => never;
|
|
23
|
+
/**
|
|
24
|
+
* The controller's own offer, judged from this client's side.
|
|
25
|
+
*
|
|
26
|
+
* Acceptance is the controller's decision about this client; whether this client can serve the
|
|
27
|
+
* session it was just given is this check, and a client that cannot must not use it.
|
|
28
|
+
*/
|
|
29
|
+
export declare const requireCompatibleProtocol: (peerOfferArg: plugins.servezoneInterfaces.protocol.IProtocolOffer) => void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
const protocol = plugins.servezoneInterfaces.protocol;
|
|
3
|
+
/**
|
|
4
|
+
* The oldest Cloudly release this client opens its platform session with.
|
|
5
|
+
*
|
|
6
|
+
* It is raised only by the commit that starts depending on a later `@serve.zone/interfaces`
|
|
7
|
+
* minor, and that raise is named in the changelog: a client that demands more than it needs
|
|
8
|
+
* refuses controllers it could have served. What this client speaks is never stated here — it is
|
|
9
|
+
* the installed release, which `createProtocolOffer` reads from the package itself.
|
|
10
|
+
*/
|
|
11
|
+
const platformSessionMinimumPeerVersion = '32.0.0';
|
|
12
|
+
/** This build's offer for the one session kind the platform client opens. */
|
|
13
|
+
export const platformSessionProtocolOffer = protocol.createProtocolOffer(platformSessionMinimumPeerVersion);
|
|
14
|
+
/**
|
|
15
|
+
* Two peers that cannot serve one session, named rather than collapsed into an authentication
|
|
16
|
+
* failure.
|
|
17
|
+
*
|
|
18
|
+
* A refusal stands until an operator upgrades one of the two sides, so it is never retried: the
|
|
19
|
+
* caller is rejected with it, and a refusal met while a reconnect restores the session — where no
|
|
20
|
+
* caller waits — ends that socket lifecycle and is retained on the client instead.
|
|
21
|
+
*/
|
|
22
|
+
export class SzPlatformProtocolIncompatibleError extends Error {
|
|
23
|
+
constructor(refusalArg, optionsArg) {
|
|
24
|
+
super(protocol.describeProtocolRefusal(refusalArg), optionsArg);
|
|
25
|
+
this.name = 'SzPlatformProtocolIncompatibleError';
|
|
26
|
+
this.refusal = refusalArg;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The payload a failed TypedRequest hop carried.
|
|
31
|
+
*
|
|
32
|
+
* Read structurally rather than through `instanceof TypedResponseError`: the request that fails is
|
|
33
|
+
* created by TypedSocket, which resolves its own `@api.global/typedrequest`, so a class identity
|
|
34
|
+
* check could miss a refusal in a consumer tree that holds a second copy.
|
|
35
|
+
*/
|
|
36
|
+
const readErrorData = (errorArg) => {
|
|
37
|
+
if (typeof errorArg !== 'object' || errorArg === null || !('errorData' in errorArg)) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
return errorArg.errorData;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Rethrow a refused offer as this client's named error; every other failure passes untouched.
|
|
44
|
+
*
|
|
45
|
+
* The transport error stays attached as the cause, so the correlation a caller needs to find the
|
|
46
|
+
* hop in a log is not lost behind the refusal.
|
|
47
|
+
*/
|
|
48
|
+
export const rethrowProtocolRefusal = (errorArg) => {
|
|
49
|
+
const refusal = protocol.readProtocolRefusal(readErrorData(errorArg));
|
|
50
|
+
if (refusal === null) {
|
|
51
|
+
throw errorArg;
|
|
52
|
+
}
|
|
53
|
+
throw new SzPlatformProtocolIncompatibleError(refusal, { cause: errorArg });
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The controller's own offer, judged from this client's side.
|
|
57
|
+
*
|
|
58
|
+
* Acceptance is the controller's decision about this client; whether this client can serve the
|
|
59
|
+
* session it was just given is this check, and a client that cannot must not use it.
|
|
60
|
+
*/
|
|
61
|
+
export const requireCompatibleProtocol = (peerOfferArg) => {
|
|
62
|
+
const negotiation = protocol.negotiateProtocol(platformSessionProtocolOffer, peerOfferArg);
|
|
63
|
+
if (!negotiation.compatible) {
|
|
64
|
+
throw new SzPlatformProtocolIncompatibleError(negotiation.refusal);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdG9jb2wuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9wcm90b2NvbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsbUJBQW1CLENBQUMsUUFBUSxDQUFDO0FBRXREOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLGlDQUFpQyxHQUFHLFFBQVEsQ0FBQztBQUVuRCw2RUFBNkU7QUFDN0UsTUFBTSxDQUFDLE1BQU0sNEJBQTRCLEdBQ3ZDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO0FBRWxFOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLE9BQU8sbUNBQW9DLFNBQVEsS0FBSztJQUc1RCxZQUNFLFVBQWlFLEVBQ2pFLFVBQXlCO1FBRXpCLEtBQUssQ0FBQyxRQUFRLENBQUMsdUJBQXVCLENBQUMsVUFBVSxDQUFDLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDaEUsSUFBSSxDQUFDLElBQUksR0FBRyxxQ0FBcUMsQ0FBQztRQUNsRCxJQUFJLENBQUMsT0FBTyxHQUFHLFVBQVUsQ0FBQztJQUM1QixDQUFDO0NBQ0Y7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLGFBQWEsR0FBRyxDQUFDLFFBQWlCLEVBQVcsRUFBRTtJQUNuRCxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxRQUFRLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxXQUFXLElBQUksUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNwRixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsT0FBTyxRQUFRLENBQUMsU0FBUyxDQUFDO0FBQzVCLENBQUMsQ0FBQztBQUVGOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsQ0FBQyxRQUFpQixFQUFTLEVBQUU7SUFDakUsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLG1CQUFtQixDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLElBQUksT0FBTyxLQUFLLElBQUksRUFBRSxDQUFDO1FBQ3JCLE1BQU0sUUFBUSxDQUFDO0lBQ2pCLENBQUM7SUFDRCxNQUFNLElBQUksbUNBQW1DLENBQUMsT0FBTyxFQUFFLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7QUFDOUUsQ0FBQyxDQUFDO0FBRUY7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxDQUN2QyxZQUFpRSxFQUMzRCxFQUFFO0lBQ1IsTUFBTSxXQUFXLEdBQUcsUUFBUSxDQUFDLGlCQUFpQixDQUFDLDRCQUE0QixFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQzNGLElBQUksQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDNUIsTUFBTSxJQUFJLG1DQUFtQyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNyRSxDQUFDO0FBQ0gsQ0FBQyxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/platformclient",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "32.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "a module that makes it really easy to use the serve.zone platform inside your app",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -9,23 +9,23 @@
|
|
|
9
9
|
"author": "Task Venture Capital GmbH",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@git.zone/tsbuild": "^4.
|
|
12
|
+
"@git.zone/tsbuild": "^4.5.0",
|
|
13
13
|
"@git.zone/tsbundle": "^2.13.0",
|
|
14
14
|
"@git.zone/tsrun": "^2.0.6",
|
|
15
|
-
"@git.zone/tstest": "6.1.
|
|
15
|
+
"@git.zone/tstest": "6.1.1",
|
|
16
16
|
"@push.rocks/smartmongo": "9.0.0",
|
|
17
17
|
"@push.rocks/smartserve": "6.2.1",
|
|
18
|
-
"@serve.zone/coremail": "
|
|
19
|
-
"@types/node": "26.5.
|
|
18
|
+
"@serve.zone/coremail": "32.0.0",
|
|
19
|
+
"@types/node": "26.5.1"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@api.global/typedrequest": "^8.0.4",
|
|
23
23
|
"@api.global/typedrequest-interfaces": "^7.1.0",
|
|
24
|
-
"@api.global/typedsocket": "^8.
|
|
25
|
-
"@push.rocks/qenv": "^
|
|
24
|
+
"@api.global/typedsocket": "^8.4.0",
|
|
25
|
+
"@push.rocks/qenv": "^8.0.0",
|
|
26
26
|
"@push.rocks/smartlog": "^3.2.2",
|
|
27
27
|
"@push.rocks/smartntml": "^2.0.9",
|
|
28
|
-
"@serve.zone/interfaces": "^
|
|
28
|
+
"@serve.zone/interfaces": "^32.1.0"
|
|
29
29
|
},
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
package/readme.md
CHANGED
|
@@ -45,11 +45,11 @@ await platformClient.emailConnector.sendEmail({
|
|
|
45
45
|
| `letterConnector` | `sendLetter()` | `letter` |
|
|
46
46
|
| `coreMailConnector` | `prepareOutboundSubmission()`, `uploadOutboundPart()`, `finalizeOutboundSubmission()`, `getOutboundSubmission()`, `listInboundDeliveries()`, `fetchInboundDelivery()`, `acknowledgeInboundDelivery()` | CoreMail workload binding |
|
|
47
47
|
|
|
48
|
-
The request and response payloads come from `@serve.zone/interfaces`, so TypeScript stays aligned with the serve.zone platform contracts.
|
|
48
|
+
The request and response payloads come from `@serve.zone/interfaces`, so TypeScript stays aligned with the serve.zone platform contracts. The installed `@serve.zone/interfaces` release *is* the contract version this client speaks; no payload states a schema version of its own.
|
|
49
49
|
|
|
50
50
|
## Configuration
|
|
51
51
|
|
|
52
|
-
The shared platform connection requires a Cloudly machine token and an explicit HTTPS Cloudly origin. On each connection it exchanges the token for an API machine identity and
|
|
52
|
+
The shared platform connection requires a Cloudly machine token and an explicit HTTPS Cloudly origin. On each connection it exchanges the token for an API machine identity and calls `registerCloudlyClientSession` with that JWT and its protocol offer before initialization succeeds. Reconnect repeats both steps, and a live connection renews its identity before expiry. Rejected credentials, invalid registration replies, or failed renewal close the connection. Value-free platform bindings describe capabilities; they never select the Cloudly authority or provide credentials.
|
|
53
53
|
|
|
54
54
|
| Value | Sources |
|
|
55
55
|
| --- | --- |
|
|
@@ -57,6 +57,8 @@ The shared platform connection requires a Cloudly machine token and an explicit
|
|
|
57
57
|
| Cloudly URL | `cloudlyUrl`, `url`, `platformUrl`, or `SERVEZONE_PLATFORM_URL`; an explicit HTTPS origin is required. |
|
|
58
58
|
| Platform bindings | Constructor `binding` or `bindings`, `SERVEZONE_PLATFORM_BINDING`, or `SERVEZONE_PLATFORM_BINDINGS`. |
|
|
59
59
|
|
|
60
|
+
Every environment name in this document is read on demand: the process environment first, then the Docker secrets. The client keeps no list of required variables and never reads or enforces the embedding application's `qenv.yml`; a requirement declared there is the application's to act on, and never a reason for `new SzPlatformClient()` to refuse.
|
|
61
|
+
|
|
60
62
|
Workloads should receive their service-scoped `platform:session` token through `SERVEZONE_PLATFORM_AUTHORIZATION`. A deployment token does not grant a platform session. The SDK never sends the token to a binding or mail endpoint. Token exchange and registration bypass request payload hooks and disable automatic RPC retries.
|
|
61
63
|
|
|
62
64
|
Binding environment variables must contain JSON encoded, value-free `IPlatformBinding` objects from `@serve.zone/interfaces`.
|
|
@@ -91,6 +93,28 @@ const client = new SzPlatformClient({
|
|
|
91
93
|
await client.init();
|
|
92
94
|
```
|
|
93
95
|
|
|
96
|
+
## Protocol handshake
|
|
97
|
+
|
|
98
|
+
Every platform session states which contract it speaks. `registerCloudlyClientSession` carries `protocol.createProtocolOffer('32.0.0')` — the installed `@serve.zone/interfaces` version as `interfacesVersion`, and `32.0.0` as the oldest Cloudly release this client accepts — and Cloudly answers with its own offer, which this client negotiates against its own before it uses the session.
|
|
99
|
+
|
|
100
|
+
A peer that cannot serve the session is named, never collapsed into a credential failure: the caller is rejected with `SzPlatformProtocolIncompatibleError`, which carries the `IProtocolRefusal` (`code: 'protocol-incompatible'`, its reason, and both sides' offers). A refusal stands until an operator upgrades one of the two sides, so it is never retried: a refusal met while a reconnect restores the session — where no caller waits — ends that socket lifecycle and is kept on the client.
|
|
101
|
+
|
|
102
|
+
| Member | Meaning |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `getSessionStatus()` | `'connected'`, `'disconnected'`, or `'protocol-incompatible'` — the refusal outranks the transport state |
|
|
105
|
+
| `lastProtocolRefusal` | The retained `SzPlatformProtocolIncompatibleError`, with `refusal.refusing` and `refusal.refused` |
|
|
106
|
+
| `platformSessionProtocolOffer` | This build's offer, exported for callers that report platform compatibility |
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
await client.init().catch((error) => {
|
|
110
|
+
if (client.getSessionStatus() === 'protocol-incompatible') {
|
|
111
|
+
// Both offers are in client.lastProtocolRefusal.refusal; upgrade one side.
|
|
112
|
+
throw client.lastProtocolRefusal;
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
94
118
|
## Socket 8 migration
|
|
95
119
|
|
|
96
120
|
Version 4 requires Socket 8 peers: upgrade Cloudly and the dedicated dcrouter mail/Web Push endpoints together with their clients. Older Socket 4/5 servers are incompatible. Set `SERVEZONE_PLATFORM_URL` to Cloudly's HTTPS origin and retain `MAIL_TYPED_URL` for the dedicated mail endpoint. Replace legacy platform tokens with Cloudly service credentials; initialization no longer accepts a successful transport handshake as authentication. CoreMail, SMTP credential discovery, mail delivery status, attachments, Reply-To, and debug connector behavior remain available.
|
|
@@ -122,7 +146,7 @@ The current SMS verification method is spelled `sendSmsVerifcation()` in code. U
|
|
|
122
146
|
|
|
123
147
|
## Lifecycle
|
|
124
148
|
|
|
125
|
-
`SzPlatformClient` keeps separate TypedSocket connections for Cloudly, CoreMail, typed service mail, and managed Web Push. Call `stop()` to cancel initialization, renewal, and restoration and join their cleanup. `requireTypedSocket()` returns the authenticated Cloudly connection while
|
|
149
|
+
`SzPlatformClient` keeps separate TypedSocket connections for Cloudly, CoreMail, typed service mail, and managed Web Push. Call `stop()` to cancel initialization, renewal, and restoration and join their cleanup. `requireTypedSocket()` returns the authenticated Cloudly connection while `getSessionStatus()` is `connected`; `typedsocket` is optional. The mail router is private to its own connection, so Cloudly and Web Push peers cannot invoke inbound mail handlers.
|
|
126
150
|
|
|
127
151
|
```typescript
|
|
128
152
|
const client = new SzPlatformClient({
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -7,8 +7,17 @@ import {
|
|
|
7
7
|
SzCoreMailConnector,
|
|
8
8
|
type ISzCoreMailConnectorOptions,
|
|
9
9
|
} from './coremail/classes.coremailconnector.js';
|
|
10
|
+
import {
|
|
11
|
+
platformSessionProtocolOffer,
|
|
12
|
+
requireCompatibleProtocol,
|
|
13
|
+
rethrowProtocolRefusal,
|
|
14
|
+
SzPlatformProtocolIncompatibleError,
|
|
15
|
+
} from './protocol.js';
|
|
10
16
|
import * as plugins from './plugins.js';
|
|
11
17
|
|
|
18
|
+
/** What a caller can act on: a usable session, none, or a peer of another contract. */
|
|
19
|
+
export type TSzPlatformSessionStatus = 'connected' | 'disconnected' | 'protocol-incompatible';
|
|
20
|
+
|
|
12
21
|
export interface ISzPlatformClientOptions {
|
|
13
22
|
authorizationString?: string;
|
|
14
23
|
authorization?: string;
|
|
@@ -29,13 +38,33 @@ export class SzPlatformClient {
|
|
|
29
38
|
public platformBindings: plugins.servezoneInterfaces.platform.IPlatformBinding[] = [];
|
|
30
39
|
public typedrouter: plugins.typedrequest.TypedRouter = new plugins.typedrequest.TypedRouter();
|
|
31
40
|
public typedsocket?: plugins.typedsocket.TypedSocket;
|
|
41
|
+
/**
|
|
42
|
+
* The refusal this client met, kept for the caller that never saw it.
|
|
43
|
+
*
|
|
44
|
+
* A refused offer stands until an operator upgrades one of the two sides, and a refusal met
|
|
45
|
+
* while a reconnect restores the session reaches no caller at all, so it is read from here.
|
|
46
|
+
*/
|
|
47
|
+
public lastProtocolRefusal?: SzPlatformProtocolIncompatibleError;
|
|
32
48
|
private identity?: plugins.servezoneInterfaces.data.IIdentity;
|
|
33
49
|
private authenticationGeneration = 0;
|
|
34
50
|
private authenticationAbort?: AbortController;
|
|
35
51
|
private authenticationOperations = new Set<Promise<unknown>>();
|
|
36
52
|
private renewalTimer?: ReturnType<typeof setTimeout>;
|
|
37
53
|
private statusSubscription?: { unsubscribe(): void };
|
|
38
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The name resolver behind {@link getConfiguredEnvVar}: process environment, then Docker secrets.
|
|
56
|
+
*
|
|
57
|
+
* This client owns no list of required variables and must never enforce anyone else's. Which
|
|
58
|
+
* names matter is a per-call decision here - the mail connector even composes them from an
|
|
59
|
+
* address token at send time - and every read site below treats a missing value as its own
|
|
60
|
+
* business. So the instance is pointed at this package's own directory, which holds no
|
|
61
|
+
* `qenv.yml`, instead of qenv's default of `process.cwd()`: that is the embedding application's
|
|
62
|
+
* directory, and since qenv 8.0.0 a name its `qenv.yml` lists under `required:` without a value
|
|
63
|
+
* makes the constructor throw `QenvMissingRequiredEnvVarsError`. A host declaring a requirement
|
|
64
|
+
* it has not met yet is the host's decision to act on, never a reason for this library to refuse
|
|
65
|
+
* construction, so `failOnMissing` is false as well.
|
|
66
|
+
*/
|
|
67
|
+
private qenvInstance = new plugins.qenv.Qenv(import.meta.dirname, undefined, false);
|
|
39
68
|
private stopped = false;
|
|
40
69
|
private initPromise?: Promise<void>;
|
|
41
70
|
private stopPromise?: Promise<void>;
|
|
@@ -98,7 +127,10 @@ export class SzPlatformClient {
|
|
|
98
127
|
restoreConnection: (context) => {
|
|
99
128
|
this.invalidateAuthentication();
|
|
100
129
|
const generation = this.authenticationGeneration;
|
|
101
|
-
return this.trackAuthentication(
|
|
130
|
+
return this.trackAuthentication(
|
|
131
|
+
this.authenticate(context.createTypedRequest, context.abortSignal, generation)
|
|
132
|
+
.catch((error) => this.releaseRefusedSocket(error, generation)),
|
|
133
|
+
);
|
|
102
134
|
},
|
|
103
135
|
},
|
|
104
136
|
);
|
|
@@ -188,9 +220,42 @@ export class SzPlatformClient {
|
|
|
188
220
|
|
|
189
221
|
public requireTypedSocket(): plugins.typedsocket.TypedSocket {
|
|
190
222
|
this.assertRunning();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
223
|
+
const socket = this.typedsocket;
|
|
224
|
+
const status = this.getSessionStatus();
|
|
225
|
+
// A refused protocol is the one unavailability a caller can act on, so it keeps its name.
|
|
226
|
+
if (status === 'protocol-incompatible' && this.lastProtocolRefusal) throw this.lastProtocolRefusal;
|
|
227
|
+
if (!socket || status !== 'connected') {
|
|
228
|
+
throw new Error('Cloudly platform session is unavailable.');
|
|
229
|
+
}
|
|
230
|
+
return socket;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* The platform session as a caller can act on it.
|
|
235
|
+
*
|
|
236
|
+
* `protocol-incompatible` outranks the transport state: it stands until an operator upgrades
|
|
237
|
+
* one of the two sides, and `lastProtocolRefusal` names both of them.
|
|
238
|
+
*/
|
|
239
|
+
public getSessionStatus(): TSzPlatformSessionStatus {
|
|
240
|
+
if (this.lastProtocolRefusal) return 'protocol-incompatible';
|
|
241
|
+
if (this.typedsocket?.getStatus() === 'connected' && this.identity
|
|
242
|
+
&& this.identity.expiresAt > Date.now()) return 'connected';
|
|
243
|
+
return 'disconnected';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Release the socket lifecycle a restoration was refused in; every other failure passes on.
|
|
248
|
+
*
|
|
249
|
+
* TypedSocket ends its own client on a denied restoration and never reconnects from one, so the
|
|
250
|
+
* reference held here is dead — keeping it would report an unavailable session instead of the
|
|
251
|
+
* incompatible peer.
|
|
252
|
+
*/
|
|
253
|
+
private releaseRefusedSocket(errorArg: unknown, generationArg: number): never {
|
|
254
|
+
if (errorArg instanceof SzPlatformProtocolIncompatibleError
|
|
255
|
+
&& generationArg === this.authenticationGeneration) {
|
|
256
|
+
this.typedsocket = undefined;
|
|
257
|
+
}
|
|
258
|
+
throw errorArg;
|
|
194
259
|
}
|
|
195
260
|
|
|
196
261
|
private invalidateAuthentication(): void {
|
|
@@ -227,19 +292,28 @@ export class SzPlatformClient {
|
|
|
227
292
|
|| !Number.isSafeInteger(identity.expiresAt) || identity.expiresAt <= Date.now() + 1000) {
|
|
228
293
|
throw new Error('Invalid platform identity.');
|
|
229
294
|
}
|
|
230
|
-
const requestData = plugins.servezoneInterfaces.data.
|
|
231
|
-
|
|
295
|
+
const requestData = plugins.servezoneInterfaces.data.snapshotRegisterCloudlyClientSessionRequest({
|
|
296
|
+
identity: { jwt: identity.jwt }, protocol: platformSessionProtocolOffer,
|
|
232
297
|
});
|
|
233
|
-
const registration = createRequest<plugins.servezoneInterfaces.requests.runtimesession.
|
|
234
|
-
'
|
|
298
|
+
const registration = createRequest<plugins.servezoneInterfaces.requests.runtimesession.IReq_Any_Cloudly_RegisterCloudlyClientSession>(
|
|
299
|
+
'registerCloudlyClientSession');
|
|
235
300
|
registration.skipHooks = true;
|
|
236
|
-
const registered = await registration
|
|
237
|
-
|
|
301
|
+
const registered = await registration
|
|
302
|
+
.fire(requestData, { maxRetries: 0, abortSignal: signal })
|
|
303
|
+
.catch(rethrowProtocolRefusal);
|
|
304
|
+
requireCompatibleProtocol(plugins.servezoneInterfaces.data
|
|
305
|
+
.snapshotRegisterCloudlyClientSessionResponse(registered).protocol);
|
|
238
306
|
requireCurrent();
|
|
239
307
|
this.identity = { type: identity.type, role: identity.role, userId: identity.userId,
|
|
240
308
|
name: identity.userId, expiresAt: identity.expiresAt, jwt: requestData.identity.jwt };
|
|
241
|
-
} catch {
|
|
309
|
+
} catch (error) {
|
|
242
310
|
if (this.stopped || signal.aborted || generation !== this.authenticationGeneration) throw this.createAbortError();
|
|
311
|
+
// A refused offer rejected no credential of this caller's, so it is named rather than
|
|
312
|
+
// collapsed, and kept where a restoration that has no caller can still be read.
|
|
313
|
+
if (error instanceof SzPlatformProtocolIncompatibleError) {
|
|
314
|
+
this.lastProtocolRefusal = error;
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
243
317
|
throw new Error('Cloudly platform authentication failed.');
|
|
244
318
|
}
|
|
245
319
|
}
|
package/ts/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export * from './classes.platformclient.js';
|
|
2
|
+
export {
|
|
3
|
+
platformSessionProtocolOffer,
|
|
4
|
+
SzPlatformProtocolIncompatibleError,
|
|
5
|
+
} from './protocol.js';
|
|
2
6
|
export * from './coremail/classes.coremailconnector.js';
|
|
3
7
|
export * from './email/classes.emailconnector.js';
|
|
4
8
|
export * from './webpush/classes.webpushconnector.js';
|
package/ts/protocol.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
const protocol = plugins.servezoneInterfaces.protocol;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The oldest Cloudly release this client opens its platform session with.
|
|
7
|
+
*
|
|
8
|
+
* It is raised only by the commit that starts depending on a later `@serve.zone/interfaces`
|
|
9
|
+
* minor, and that raise is named in the changelog: a client that demands more than it needs
|
|
10
|
+
* refuses controllers it could have served. What this client speaks is never stated here — it is
|
|
11
|
+
* the installed release, which `createProtocolOffer` reads from the package itself.
|
|
12
|
+
*/
|
|
13
|
+
const platformSessionMinimumPeerVersion = '32.0.0';
|
|
14
|
+
|
|
15
|
+
/** This build's offer for the one session kind the platform client opens. */
|
|
16
|
+
export const platformSessionProtocolOffer: plugins.servezoneInterfaces.protocol.IProtocolOffer =
|
|
17
|
+
protocol.createProtocolOffer(platformSessionMinimumPeerVersion);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Two peers that cannot serve one session, named rather than collapsed into an authentication
|
|
21
|
+
* failure.
|
|
22
|
+
*
|
|
23
|
+
* A refusal stands until an operator upgrades one of the two sides, so it is never retried: the
|
|
24
|
+
* caller is rejected with it, and a refusal met while a reconnect restores the session — where no
|
|
25
|
+
* caller waits — ends that socket lifecycle and is retained on the client instead.
|
|
26
|
+
*/
|
|
27
|
+
export class SzPlatformProtocolIncompatibleError extends Error {
|
|
28
|
+
public readonly refusal: plugins.servezoneInterfaces.protocol.IProtocolRefusal;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
refusalArg: plugins.servezoneInterfaces.protocol.IProtocolRefusal,
|
|
32
|
+
optionsArg?: ErrorOptions,
|
|
33
|
+
) {
|
|
34
|
+
super(protocol.describeProtocolRefusal(refusalArg), optionsArg);
|
|
35
|
+
this.name = 'SzPlatformProtocolIncompatibleError';
|
|
36
|
+
this.refusal = refusalArg;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The payload a failed TypedRequest hop carried.
|
|
42
|
+
*
|
|
43
|
+
* Read structurally rather than through `instanceof TypedResponseError`: the request that fails is
|
|
44
|
+
* created by TypedSocket, which resolves its own `@api.global/typedrequest`, so a class identity
|
|
45
|
+
* check could miss a refusal in a consumer tree that holds a second copy.
|
|
46
|
+
*/
|
|
47
|
+
const readErrorData = (errorArg: unknown): unknown => {
|
|
48
|
+
if (typeof errorArg !== 'object' || errorArg === null || !('errorData' in errorArg)) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return errorArg.errorData;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Rethrow a refused offer as this client's named error; every other failure passes untouched.
|
|
56
|
+
*
|
|
57
|
+
* The transport error stays attached as the cause, so the correlation a caller needs to find the
|
|
58
|
+
* hop in a log is not lost behind the refusal.
|
|
59
|
+
*/
|
|
60
|
+
export const rethrowProtocolRefusal = (errorArg: unknown): never => {
|
|
61
|
+
const refusal = protocol.readProtocolRefusal(readErrorData(errorArg));
|
|
62
|
+
if (refusal === null) {
|
|
63
|
+
throw errorArg;
|
|
64
|
+
}
|
|
65
|
+
throw new SzPlatformProtocolIncompatibleError(refusal, { cause: errorArg });
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The controller's own offer, judged from this client's side.
|
|
70
|
+
*
|
|
71
|
+
* Acceptance is the controller's decision about this client; whether this client can serve the
|
|
72
|
+
* session it was just given is this check, and a client that cannot must not use it.
|
|
73
|
+
*/
|
|
74
|
+
export const requireCompatibleProtocol = (
|
|
75
|
+
peerOfferArg: plugins.servezoneInterfaces.protocol.IProtocolOffer,
|
|
76
|
+
): void => {
|
|
77
|
+
const negotiation = protocol.negotiateProtocol(platformSessionProtocolOffer, peerOfferArg);
|
|
78
|
+
if (!negotiation.compatible) {
|
|
79
|
+
throw new SzPlatformProtocolIncompatibleError(negotiation.refusal);
|
|
80
|
+
}
|
|
81
|
+
};
|