@webpieces/ipc-bridge 0.4.879 → 0.4.880
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/package.json +2 -2
- package/src/IpcClientFactory.d.ts +10 -2
- package/src/IpcClientFactory.js +12 -3
- package/src/IpcClientFactory.js.map +1 -1
package/README.md
CHANGED
|
@@ -17,11 +17,13 @@ abstract class PlaybackApi {
|
|
|
17
17
|
@WpIpcEndpoint('play.v1')
|
|
18
18
|
abstract play(request: PlayRequest): Promise<void>;
|
|
19
19
|
}
|
|
20
|
-
const clients = new IpcClientFactory(connection, logging);
|
|
20
|
+
const clients = new IpcClientFactory(connection, logging, ClientRole.END_USER_CLIENT);
|
|
21
21
|
const playback = clients.createClient(PlaybackApi);
|
|
22
22
|
await playback.play(new PlayRequest('track-id'));
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
The third `IpcClientFactory` argument is the `ClientRole` (from `@webpieces/core-util/ipc`), required with no default: `END_USER_CLIENT` when this side acts for a person (a WebView or app shell), so an `unauthorized` reply decodes as `ApiUnauthorizedError` ("log in again"); `SERVER` when the credential this side presents is its own, so the same reply is its own bug (`ApiImplementationError`). See `docs/portable-ipc-and-errors.md`.
|
|
26
|
+
|
|
25
27
|
The DTO, IDs and metadata live in the shared API package. The names of the abstract class and controller are never sent on the wire. Proxy inspection, symbols and `then` do not send requests. Added JSON fields are naturally ignored by typed consumers; missing fields may fail in application logic.
|
|
26
28
|
|
|
27
29
|
Construct `IpcConnection` with an `IpcTransport` adapter and `IpcConnectionOptions`: a positive timeout, a connection-wide unique ID generator, a scheduler returning timer cancellation functions, and an error owner. The adapter sends strings and subscribes once to incoming strings, transport errors and close events. Its send Promise must reject on send failure; parse/message-error callbacks go to the failure subscriber. A vendor bus adapter must not expose vendor Node declarations through its public contract. Do not install a second vendor handler for every API.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/ipc-bridge",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.880",
|
|
4
4
|
"description": "Portable typed duplex IPC client and server factories for browser, React Native and Node",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@webpieces/core-util": "0.4.
|
|
25
|
+
"@webpieces/core-util": "0.4.880"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { IpcCallContext, IpcConnection, IpcApiType, IpcLogging } from '@webpieces/core-util/ipc';
|
|
1
|
+
import { ClientRole, IpcCallContext, IpcConnection, IpcApiType, IpcLogging } from '@webpieces/core-util/ipc';
|
|
2
2
|
/** Typed proxies on one trusted duplex connection; no HTTP decorators or container required. */
|
|
3
3
|
export declare class IpcClientFactory {
|
|
4
4
|
private readonly connection;
|
|
5
5
|
private readonly logging;
|
|
6
|
+
private readonly role;
|
|
6
7
|
private readonly parent?;
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @param role - WHO receives this factory's replies (see {@link ClientRole}). REQUIRED, with no
|
|
10
|
+
* default (#1173): it decides what an `unauthorized` reply means. An end-user client (the
|
|
11
|
+
* WebView or app shell acting for the person) decodes it as `ApiUnauthorizedError` ("log in
|
|
12
|
+
* again"); a server decodes it as `ApiImplementationError`, because the credential it presented
|
|
13
|
+
* was its own.
|
|
14
|
+
*/
|
|
15
|
+
constructor(connection: IpcConnection, logging: IpcLogging, role: ClientRole, parent?: IpcCallContext | undefined);
|
|
8
16
|
/** Explicit scope propagation is safe across concurrent async calls in browser and RN. */
|
|
9
17
|
withContext(context: IpcCallContext): IpcClientFactory;
|
|
10
18
|
createClient<T extends object>(apiClass: IpcApiType<T>): T;
|
package/src/IpcClientFactory.js
CHANGED
|
@@ -7,15 +7,24 @@ const ipc_1 = require("@webpieces/core-util/ipc");
|
|
|
7
7
|
class IpcClientFactory {
|
|
8
8
|
connection;
|
|
9
9
|
logging;
|
|
10
|
+
role;
|
|
10
11
|
parent;
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @param role - WHO receives this factory's replies (see {@link ClientRole}). REQUIRED, with no
|
|
14
|
+
* default (#1173): it decides what an `unauthorized` reply means. An end-user client (the
|
|
15
|
+
* WebView or app shell acting for the person) decodes it as `ApiUnauthorizedError` ("log in
|
|
16
|
+
* again"); a server decodes it as `ApiImplementationError`, because the credential it presented
|
|
17
|
+
* was its own.
|
|
18
|
+
*/
|
|
19
|
+
constructor(connection, logging, role, parent) {
|
|
12
20
|
this.connection = connection;
|
|
13
21
|
this.logging = logging;
|
|
22
|
+
this.role = role;
|
|
14
23
|
this.parent = parent;
|
|
15
24
|
}
|
|
16
25
|
/** Explicit scope propagation is safe across concurrent async calls in browser and RN. */
|
|
17
26
|
withContext(context) {
|
|
18
|
-
return new IpcClientFactory(this.connection, this.logging, context);
|
|
27
|
+
return new IpcClientFactory(this.connection, this.logging, this.role, context);
|
|
19
28
|
}
|
|
20
29
|
createClient(apiClass) {
|
|
21
30
|
const apiId = (0, ipc_1.assertInternalApi)(apiClass);
|
|
@@ -33,7 +42,7 @@ class IpcClientFactory {
|
|
|
33
42
|
// EVERY reply passes the seam, success included, so an app can turn an
|
|
34
43
|
// apparently-successful reply into a throw. `asserts reply is IpcSuccess`
|
|
35
44
|
// is what leaves no `type === 'failure'` branch behind here.
|
|
36
|
-
ipc_1.IpcClientErrorTranslator.throwIfFailure(reply);
|
|
45
|
+
ipc_1.IpcClientErrorTranslator.throwIfFailure(reply, this.role);
|
|
37
46
|
return reply.body === null ? undefined : reply.body;
|
|
38
47
|
});
|
|
39
48
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IpcClientFactory.js","sourceRoot":"","sources":["../../../../../packages/core/ipc-bridge/src/IpcClientFactory.ts"],"names":[],"mappings":";;;AAAA,wDAAqE;AACrE,
|
|
1
|
+
{"version":3,"file":"IpcClientFactory.js","sourceRoot":"","sources":["../../../../../packages/core/ipc-bridge/src/IpcClientFactory.ts"],"names":[],"mappings":";;;AAAA,wDAAqE;AACrE,kDAakC;AAElC,gGAAgG;AAChG,MAAa,gBAAgB;IASJ;IACA;IACA;IACA;IAXrB;;;;;;OAMG;IACH,YACqB,UAAyB,EACzB,OAAmB,EACnB,IAAgB,EAChB,MAAuB;QAHvB,eAAU,GAAV,UAAU,CAAe;QACzB,YAAO,GAAP,OAAO,CAAY;QACnB,SAAI,GAAJ,IAAI,CAAY;QAChB,WAAM,GAAN,MAAM,CAAiB;IACzC,CAAC;IAEJ,0FAA0F;IAC1F,WAAW,CAAC,OAAuB;QAC/B,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnF,CAAC;IAED,YAAY,CAAmB,QAAuB;QAClD,MAAM,KAAK,GAAG,IAAA,uBAAiB,EAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAA,qBAAe,EAAC,QAAQ,CAAC,CAAC;QAC5C,0IAA0I;QAC1I,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuD,CAAC;QAC/E,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACtD,0IAA0I;YAC1I,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,OAAgB,EAAoB,EAAE;gBAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACxD,OAAO,mBAAa,CAAC,OAAO,CACxB,IAAI,CAAC,OAAO,EACZ,OAAO,EACP,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,IAAA,oBAAc,EAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,IAAI,cAAQ,CAAC,EAAE,CAAC,EACjD,OAAO,EACP,KAAK,IAAI,EAAE;oBACP,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS;wBACzC,MAAM,IAAI,+BAAsB,CAC5B,uCAAuC,CAC1C,CAAC;oBACN,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CACvC,IAAI,gBAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CACpD,CAAC;oBACF,uEAAuE;oBACvE,0EAA0E;oBAC1E,6DAA6D;oBAC7D,8BAAwB,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC1D,OAAO,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;gBACxD,CAAC,CACJ,CAAC;YACN,CAAC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAM,EAAE,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7E,CAAC;CACJ;AAxDD,4CAwDC;AAED,MAAM,eAAe;IAGI;IAFrB;IACI,0IAA0I;IACzH,OAAyE;QAAzE,YAAO,GAAP,OAAO,CAAkE;IAC3F,CAAC;IACJ,0IAA0I;IAC1I,GAAG,CAAC,OAAU,EAAE,GAAoB;QAChC,8FAA8F;QAC9F,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IACD,GAAG,CAAC,OAAU,EAAE,GAAoB;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;CACJ","sourcesContent":["import { ApiImplementationError } from '@webpieces/core-util/errors';\nimport {\n ClientRole,\n IpcCallContext,\n IpcClientErrorTranslator,\n IpcConnection,\n IpcApiType,\n IpcLogging,\n IpcRequest,\n IpcCallLogger,\n assertInternalApi,\n getIpcEndpoints,\n getIpcMaskSpec,\n MaskSpec,\n} from '@webpieces/core-util/ipc';\n\n/** Typed proxies on one trusted duplex connection; no HTTP decorators or container required. */\nexport class IpcClientFactory {\n /**\n * @param role - WHO receives this factory's replies (see {@link ClientRole}). REQUIRED, with no\n * default (#1173): it decides what an `unauthorized` reply means. An end-user client (the\n * WebView or app shell acting for the person) decodes it as `ApiUnauthorizedError` (\"log in\n * again\"); a server decodes it as `ApiImplementationError`, because the credential it presented\n * was its own.\n */\n constructor(\n private readonly connection: IpcConnection,\n private readonly logging: IpcLogging,\n private readonly role: ClientRole,\n private readonly parent?: IpcCallContext,\n ) {}\n\n /** Explicit scope propagation is safe across concurrent async calls in browser and RN. */\n withContext(context: IpcCallContext): IpcClientFactory {\n return new IpcClientFactory(this.connection, this.logging, this.role, context);\n }\n\n createClient<T extends object>(apiClass: IpcApiType<T>): T {\n const apiId = assertInternalApi(apiClass);\n const endpoints = getIpcEndpoints(apiClass);\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n const methods = new Map<PropertyKey, (request: unknown) => Promise<unknown>>();\n for (const [key, methodId] of Object.entries(endpoints)) {\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n methods.set(key, async (request: unknown): Promise<unknown> => {\n const context = this.connection.newContext(this.parent);\n return IpcCallLogger.execute(\n this.logging,\n context,\n 'client',\n apiId,\n methodId,\n getIpcMaskSpec(apiClass, key) ?? new MaskSpec({}),\n request,\n async () => {\n if (request === null || request === undefined)\n throw new ApiImplementationError(\n 'IPC requests require one non-null DTO',\n );\n const reply = await this.connection.request(\n new IpcRequest(apiId, methodId, context, request),\n );\n // EVERY reply passes the seam, success included, so an app can turn an\n // apparently-successful reply into a throw. `asserts reply is IpcSuccess`\n // is what leaves no `type === 'failure'` branch behind here.\n IpcClientErrorTranslator.throwIfFailure(reply, this.role);\n return reply.body === null ? undefined : reply.body;\n },\n );\n });\n }\n return new Proxy(Object.create(null) as T, new IpcProxyHandler(methods));\n }\n}\n\nclass IpcProxyHandler<T extends object> implements ProxyHandler<T> {\n constructor(\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n private readonly methods: ReadonlyMap<PropertyKey, (request: unknown) => Promise<unknown>>,\n ) {}\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n get(_target: T, key: string | symbol): unknown {\n // Promise assimilation, inspection, symbols and framework instrumentation aren't RPC methods.\n return this.methods.get(key);\n }\n has(_target: T, key: string | symbol): boolean {\n return this.methods.has(key);\n }\n}\n"]}
|