@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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/platformclient',
6
- version: '4.1.1',
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSw0QkFBNEI7SUFDbEMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLG1GQUFtRjtDQUNqRyxDQUFBIn0=
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
- this.qenvInstance = new plugins.qenv.Qenv();
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
- if (!this.typedsocket || this.typedsocket.getStatus() !== 'connected' || !this.identity
139
- || this.identity.expiresAt <= Date.now())
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
- return this.typedsocket;
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.snapshotRegisterCloudlyClientSessionRequestV1({
177
- schemaVersion: 1, identity: { jwt: identity.jwt },
224
+ const requestData = plugins.servezoneInterfaces.data.snapshotRegisterCloudlyClientSessionRequest({
225
+ identity: { jwt: identity.jwt }, protocol: platformSessionProtocolOffer,
178
226
  });
179
- const registration = createRequest('registerCloudlyClientSessionV1');
227
+ const registration = createRequest('registerCloudlyClientSession');
180
228
  registration.skipHooks = true;
181
- const registered = await registration.fire(requestData, { maxRetries: 0, abortSignal: signal });
182
- plugins.servezoneInterfaces.data.snapshotRegisterCloudlyClientSessionResponseV1(registered);
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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
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMseUNBQXlDLENBQUM7QUFDeEQsY0FBYyxtQ0FBbUMsQ0FBQztBQUNsRCxjQUFjLHVDQUF1QyxDQUFDIn0=
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": "4.1.1",
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.4.3",
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.0",
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": "1.2.0",
19
- "@types/node": "26.5.0"
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.1.0",
25
- "@push.rocks/qenv": "^7.0.0",
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": "^30.13.0"
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 registers that JWT on the physical socket 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.
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 its identity is valid; `typedsocket` is optional. The mail router is private to its own connection, so Cloudly and Web Push peers cannot invoke inbound mail handlers.
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({
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/platformclient',
6
- version: '4.1.1',
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
  }
@@ -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
- private qenvInstance = new plugins.qenv.Qenv();
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(this.authenticate(context.createTypedRequest, context.abortSignal, generation));
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
- if (!this.typedsocket || this.typedsocket.getStatus() !== 'connected' || !this.identity
192
- || this.identity.expiresAt <= Date.now()) throw new Error('Cloudly platform session is unavailable.');
193
- return this.typedsocket;
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.snapshotRegisterCloudlyClientSessionRequestV1({
231
- schemaVersion: 1, identity: { jwt: identity.jwt },
295
+ const requestData = plugins.servezoneInterfaces.data.snapshotRegisterCloudlyClientSessionRequest({
296
+ identity: { jwt: identity.jwt }, protocol: platformSessionProtocolOffer,
232
297
  });
233
- const registration = createRequest<plugins.servezoneInterfaces.requests.runtimesession.IReq_Any_Cloudly_RegisterCloudlyClientSessionV1>(
234
- 'registerCloudlyClientSessionV1');
298
+ const registration = createRequest<plugins.servezoneInterfaces.requests.runtimesession.IReq_Any_Cloudly_RegisterCloudlyClientSession>(
299
+ 'registerCloudlyClientSession');
235
300
  registration.skipHooks = true;
236
- const registered = await registration.fire(requestData, { maxRetries: 0, abortSignal: signal });
237
- plugins.servezoneInterfaces.data.snapshotRegisterCloudlyClientSessionResponseV1(registered);
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
+ };