@holochain/client 0.17.0-dev.7 → 0.17.0-dev.8
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/lib/api/admin/types.d.ts +5 -0
- package/lib/api/admin/websocket.js +3 -3
- package/lib/api/app/websocket.js +4 -4
- package/lib/api/app-agent/websocket.js +5 -5
- package/lib/api/client.d.ts +4 -2
- package/lib/api/client.js +19 -16
- package/lib/api/common.d.ts +10 -0
- package/lib/api/common.js +2 -2
- package/lib/api/index.d.ts +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +3 -2
package/lib/api/admin/types.d.ts
CHANGED
|
@@ -7,6 +7,11 @@ import { DisableCloneCellRequest } from "../index.js";
|
|
|
7
7
|
*/
|
|
8
8
|
export type AttachAppInterfaceRequest = {
|
|
9
9
|
port?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Comma separated list of origins, or `*` to allow any origin.
|
|
12
|
+
* For example: `http://localhost:3000,http://localhost:3001`
|
|
13
|
+
*/
|
|
14
|
+
allowed_origins: string;
|
|
10
15
|
};
|
|
11
16
|
/**
|
|
12
17
|
* @public
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getLauncherEnvironment } from "../../environments/launcher.js";
|
|
2
2
|
import { GrantedFunctionsType, } from "../../hdk/capabilities.js";
|
|
3
3
|
import { WsClient } from "../client.js";
|
|
4
|
-
import { catchError, DEFAULT_TIMEOUT, promiseTimeout, requesterTransformer, } from "../common.js";
|
|
4
|
+
import { catchError, DEFAULT_TIMEOUT, promiseTimeout, requesterTransformer, HolochainError, } from "../common.js";
|
|
5
5
|
import { generateSigningKeyPair, randomCapSecret, setSigningCredentials, } from "../zome-call-signing.js";
|
|
6
6
|
import { AppStatusFilter, } from "./types.js";
|
|
7
7
|
/**
|
|
@@ -36,9 +36,9 @@ export class AdminWebsocket {
|
|
|
36
36
|
options.url = new URL(`ws://127.0.0.1:${env.ADMIN_INTERFACE_PORT}`);
|
|
37
37
|
}
|
|
38
38
|
if (!options.url) {
|
|
39
|
-
throw new
|
|
39
|
+
throw new HolochainError("ConnectionUrlMissing", `unable to connect to Conductor API - no url provided and not in a launcher environment.`);
|
|
40
40
|
}
|
|
41
|
-
const wsClient = await WsClient.connect(options.url);
|
|
41
|
+
const wsClient = await WsClient.connect(options.url, options.wsClientOptions);
|
|
42
42
|
return new AdminWebsocket(wsClient, options.defaultTimeout);
|
|
43
43
|
}
|
|
44
44
|
_requester(tag, transformer) {
|
package/lib/api/app/websocket.js
CHANGED
|
@@ -5,7 +5,7 @@ import Emittery from "emittery";
|
|
|
5
5
|
import { getLauncherEnvironment, signZomeCallTauri, signZomeCallElectron, getHostZomeCallSigner, } from "../../environments/launcher.js";
|
|
6
6
|
import { encodeHashToBase64 } from "../../utils/base64.js";
|
|
7
7
|
import { WsClient } from "../client.js";
|
|
8
|
-
import { DEFAULT_TIMEOUT, catchError, promiseTimeout, requesterTransformer, } from "../common.js";
|
|
8
|
+
import { DEFAULT_TIMEOUT, catchError, promiseTimeout, requesterTransformer, HolochainError, } from "../common.js";
|
|
9
9
|
import { getNonceExpiration, getSigningCredentials, randomNonce, } from "../zome-call-signing.js";
|
|
10
10
|
/**
|
|
11
11
|
* A class to establish a websocket connection to an App interface of a
|
|
@@ -46,9 +46,9 @@ export class AppWebsocket extends Emittery {
|
|
|
46
46
|
options.url = new URL(`ws://127.0.0.1:${env.APP_INTERFACE_PORT}`);
|
|
47
47
|
}
|
|
48
48
|
if (!options.url) {
|
|
49
|
-
throw new
|
|
49
|
+
throw new HolochainError("ConnectionUrlMissing", `unable to connect to Conductor API - no url provided and not in a launcher environment.`);
|
|
50
50
|
}
|
|
51
|
-
const wsClient = await WsClient.connect(options.url);
|
|
51
|
+
const wsClient = await WsClient.connect(options.url, options.wsClientOptions);
|
|
52
52
|
const appWebsocket = new AppWebsocket(wsClient, options.defaultTimeout, env?.INSTALLED_APP_ID);
|
|
53
53
|
wsClient.on("signal", (signal) => appWebsocket.emit("signal", signal));
|
|
54
54
|
return appWebsocket;
|
|
@@ -134,7 +134,7 @@ const appInfoTransform = (appWs) => ({
|
|
|
134
134
|
export const signZomeCall = async (request) => {
|
|
135
135
|
const signingCredentialsForCell = getSigningCredentials(request.cell_id);
|
|
136
136
|
if (!signingCredentialsForCell) {
|
|
137
|
-
throw new
|
|
137
|
+
throw new HolochainError("NoSigningCredentialsForCell", `no signing credentials have been authorized for cell [${encodeHashToBase64(request.cell_id[0])}, ${encodeHashToBase64(request.cell_id[1])}]`);
|
|
138
138
|
}
|
|
139
139
|
const unsignedZomeCallPayload = {
|
|
140
140
|
cap_secret: signingCredentialsForCell.capSecret,
|
|
@@ -81,20 +81,20 @@ export class AppAgentWebsocket {
|
|
|
81
81
|
if (isCloneId(roleName)) {
|
|
82
82
|
const baseRoleName = getBaseRoleNameFromCloneId(roleName);
|
|
83
83
|
if (!(baseRoleName in appInfo.cell_info)) {
|
|
84
|
-
throw new
|
|
84
|
+
throw new HolochainError("NoCellForRoleName", `no cell found with role_name ${roleName}`);
|
|
85
85
|
}
|
|
86
86
|
const cloneCell = appInfo.cell_info[baseRoleName].find((c) => CellType.Cloned in c && c[CellType.Cloned].clone_id === roleName);
|
|
87
87
|
if (!cloneCell || !(CellType.Cloned in cloneCell)) {
|
|
88
|
-
throw new
|
|
88
|
+
throw new HolochainError("NoCellForCloneId", `no clone cell found with clone id ${roleName}`);
|
|
89
89
|
}
|
|
90
90
|
return cloneCell[CellType.Cloned].cell_id;
|
|
91
91
|
}
|
|
92
92
|
if (!(roleName in appInfo.cell_info)) {
|
|
93
|
-
throw new
|
|
93
|
+
throw new HolochainError("NoCellForRoleName", `no cell found with role_name ${roleName}`);
|
|
94
94
|
}
|
|
95
95
|
const cell = appInfo.cell_info[roleName].find((c) => CellType.Provisioned in c);
|
|
96
96
|
if (!cell || !(CellType.Provisioned in cell)) {
|
|
97
|
-
throw new
|
|
97
|
+
throw new HolochainError("NoProvisionedCellForRoleName", `no provisioned cell found with role_name ${roleName}`);
|
|
98
98
|
}
|
|
99
99
|
return cell[CellType.Provisioned].cell_id;
|
|
100
100
|
}
|
|
@@ -125,7 +125,7 @@ export class AppAgentWebsocket {
|
|
|
125
125
|
else if ("cell_id" in request && request.cell_id) {
|
|
126
126
|
return this.appWebsocket.callZome(request, timeout);
|
|
127
127
|
}
|
|
128
|
-
throw new
|
|
128
|
+
throw new HolochainError("MissingRoleNameOrCellId", "callZome requires a role_name or cell_id argument");
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
131
|
* Clone an existing provisioned cell.
|
package/lib/api/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="ws" />
|
|
2
2
|
import Emittery from "emittery";
|
|
3
3
|
import IsoWebSocket from "isomorphic-ws";
|
|
4
|
+
import { WsClientOptions } from "./common.js";
|
|
4
5
|
/**
|
|
5
6
|
* A WebSocket client which can make requests and receive responses,
|
|
6
7
|
* as well as send and receive signals.
|
|
@@ -12,9 +13,10 @@ import IsoWebSocket from "isomorphic-ws";
|
|
|
12
13
|
export declare class WsClient extends Emittery {
|
|
13
14
|
socket: IsoWebSocket;
|
|
14
15
|
url: URL | undefined;
|
|
16
|
+
options: WsClientOptions;
|
|
15
17
|
private pendingRequests;
|
|
16
18
|
private index;
|
|
17
|
-
constructor(socket: IsoWebSocket, url?: URL);
|
|
19
|
+
constructor(socket: IsoWebSocket, url?: URL, options?: WsClientOptions);
|
|
18
20
|
private setupSocket;
|
|
19
21
|
/**
|
|
20
22
|
* Instance factory for creating WsClients.
|
|
@@ -22,7 +24,7 @@ export declare class WsClient extends Emittery {
|
|
|
22
24
|
* @param url - The WebSocket URL to connect to.
|
|
23
25
|
* @returns An new instance of the WsClient.
|
|
24
26
|
*/
|
|
25
|
-
static connect(url: URL): Promise<WsClient>;
|
|
27
|
+
static connect(url: URL, options?: WsClientOptions): Promise<WsClient>;
|
|
26
28
|
/**
|
|
27
29
|
* Sends data as a signal.
|
|
28
30
|
*
|
package/lib/api/client.js
CHANGED
|
@@ -2,6 +2,7 @@ import { decode, encode } from "@msgpack/msgpack";
|
|
|
2
2
|
import Emittery from "emittery";
|
|
3
3
|
import IsoWebSocket from "isomorphic-ws";
|
|
4
4
|
import { SignalType } from "./app/types.js";
|
|
5
|
+
import { HolochainError } from "./common.js";
|
|
5
6
|
/**
|
|
6
7
|
* A WebSocket client which can make requests and receive responses,
|
|
7
8
|
* as well as send and receive signals.
|
|
@@ -13,12 +14,14 @@ import { SignalType } from "./app/types.js";
|
|
|
13
14
|
export class WsClient extends Emittery {
|
|
14
15
|
socket;
|
|
15
16
|
url;
|
|
17
|
+
options;
|
|
16
18
|
pendingRequests;
|
|
17
19
|
index;
|
|
18
|
-
constructor(socket, url) {
|
|
20
|
+
constructor(socket, url, options) {
|
|
19
21
|
super();
|
|
20
22
|
this.socket = socket;
|
|
21
23
|
this.url = url;
|
|
24
|
+
this.options = options || {};
|
|
22
25
|
this.pendingRequests = {};
|
|
23
26
|
this.index = 0;
|
|
24
27
|
this.setupSocket();
|
|
@@ -37,14 +40,14 @@ export class WsClient extends Emittery {
|
|
|
37
40
|
deserializedData = serializedMessage.data;
|
|
38
41
|
}
|
|
39
42
|
else {
|
|
40
|
-
throw new
|
|
43
|
+
throw new HolochainError("UnknownMessageFormat", `incoming message has unknown message format - ${deserializedData}`);
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
const message = decode(deserializedData);
|
|
44
47
|
assertHolochainMessage(message);
|
|
45
48
|
if (message.type === "signal") {
|
|
46
49
|
if (message.data === null) {
|
|
47
|
-
throw new
|
|
50
|
+
throw new HolochainError("UnknownSignalFormat", "incoming signal has no data");
|
|
48
51
|
}
|
|
49
52
|
const deserializedSignal = decode(message.data);
|
|
50
53
|
assertHolochainSignal(deserializedSignal);
|
|
@@ -66,14 +69,14 @@ export class WsClient extends Emittery {
|
|
|
66
69
|
this.handleResponse(message);
|
|
67
70
|
}
|
|
68
71
|
else {
|
|
69
|
-
|
|
72
|
+
throw new HolochainError("UnknownMessageType", `incoming message has unknown type - ${message.type}`);
|
|
70
73
|
}
|
|
71
74
|
};
|
|
72
75
|
this.socket.onclose = (event) => {
|
|
73
76
|
const pendingRequestIds = Object.keys(this.pendingRequests).map((id) => parseInt(id));
|
|
74
77
|
if (pendingRequestIds.length) {
|
|
75
78
|
pendingRequestIds.forEach((id) => {
|
|
76
|
-
const error = new
|
|
79
|
+
const error = new HolochainError("ClientClosedWithPendingRequests", `client closed with pending requests - close event code: ${event.code}, request id: ${id}`);
|
|
77
80
|
this.pendingRequests[id].reject(error);
|
|
78
81
|
delete this.pendingRequests[id];
|
|
79
82
|
});
|
|
@@ -86,14 +89,14 @@ export class WsClient extends Emittery {
|
|
|
86
89
|
* @param url - The WebSocket URL to connect to.
|
|
87
90
|
* @returns An new instance of the WsClient.
|
|
88
91
|
*/
|
|
89
|
-
static connect(url) {
|
|
92
|
+
static connect(url, options) {
|
|
90
93
|
return new Promise((resolve, reject) => {
|
|
91
|
-
const socket = new IsoWebSocket(url);
|
|
92
|
-
socket.onerror = () => {
|
|
93
|
-
reject(new
|
|
94
|
+
const socket = new IsoWebSocket(url, options);
|
|
95
|
+
socket.onerror = (errorEvent) => {
|
|
96
|
+
reject(new HolochainError("ConnectionError", `could not connect to Holochain Conductor API at ${url} - ${errorEvent.error}`));
|
|
94
97
|
};
|
|
95
98
|
socket.onopen = () => {
|
|
96
|
-
const client = new WsClient(socket, url);
|
|
99
|
+
const client = new WsClient(socket, url, options);
|
|
97
100
|
resolve(client);
|
|
98
101
|
};
|
|
99
102
|
});
|
|
@@ -126,10 +129,10 @@ export class WsClient extends Emittery {
|
|
|
126
129
|
else if (this.url) {
|
|
127
130
|
const response = new Promise((resolve, reject) => {
|
|
128
131
|
// typescript forgets in this promise scope that this.url is not undefined
|
|
129
|
-
const socket = new IsoWebSocket(this.url);
|
|
132
|
+
const socket = new IsoWebSocket(this.url, this.options);
|
|
130
133
|
this.socket = socket;
|
|
131
|
-
socket.onerror = () => {
|
|
132
|
-
reject(new
|
|
134
|
+
socket.onerror = (errorEvent) => {
|
|
135
|
+
reject(new HolochainError("ConnectionError", `could not connect to Holochain Conductor API at ${this.url} - ${errorEvent.error}`));
|
|
133
136
|
};
|
|
134
137
|
socket.onopen = () => {
|
|
135
138
|
this.sendMessage(request, resolve, reject);
|
|
@@ -165,7 +168,7 @@ export class WsClient extends Emittery {
|
|
|
165
168
|
delete this.pendingRequests[id];
|
|
166
169
|
}
|
|
167
170
|
else {
|
|
168
|
-
console.error(`
|
|
171
|
+
console.error(`got response with no matching request. id = ${id} msg = ${msg}`);
|
|
169
172
|
}
|
|
170
173
|
}
|
|
171
174
|
/**
|
|
@@ -190,7 +193,7 @@ function assertHolochainMessage(message) {
|
|
|
190
193
|
"data" in message) {
|
|
191
194
|
return;
|
|
192
195
|
}
|
|
193
|
-
throw new
|
|
196
|
+
throw new HolochainError("UnknownMessageFormat", `incoming message has unknown message format ${JSON.stringify(message, null, 4)}`);
|
|
194
197
|
}
|
|
195
198
|
function assertHolochainSignal(signal) {
|
|
196
199
|
if (typeof signal === "object" &&
|
|
@@ -198,6 +201,6 @@ function assertHolochainSignal(signal) {
|
|
|
198
201
|
Object.values(SignalType).some((type) => type in signal)) {
|
|
199
202
|
return;
|
|
200
203
|
}
|
|
201
|
-
throw new
|
|
204
|
+
throw new HolochainError("UnknownSignalFormat", `incoming signal has unknown signal format ${JSON.stringify(signal, null, 4)}`);
|
|
202
205
|
}
|
|
203
206
|
export { IsoWebSocket };
|
package/lib/api/common.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/// <reference types="ws" />
|
|
1
2
|
import { RoleName } from "../types.js";
|
|
3
|
+
import { IsoWebSocket } from "./client.js";
|
|
2
4
|
export declare const DEFAULT_TIMEOUT = 60000;
|
|
3
5
|
/**
|
|
4
6
|
* @public
|
|
@@ -78,6 +80,10 @@ export declare class CloneId {
|
|
|
78
80
|
toString(): string;
|
|
79
81
|
getBaseRoleName(): string;
|
|
80
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
export type WsClientOptions = Pick<IsoWebSocket.ClientOptions, "origin">;
|
|
81
87
|
/**
|
|
82
88
|
* Options for a Websocket connection.
|
|
83
89
|
*
|
|
@@ -88,6 +94,10 @@ export interface WebsocketConnectionOptions {
|
|
|
88
94
|
* The `ws://` URL of the Websocket server to connect to. Not required when connecting to App API from a Launcher or Kangaroo environment.
|
|
89
95
|
*/
|
|
90
96
|
url?: URL;
|
|
97
|
+
/**
|
|
98
|
+
* Options to pass to the underlying websocket connection.
|
|
99
|
+
*/
|
|
100
|
+
wsClientOptions?: WsClientOptions;
|
|
91
101
|
/**
|
|
92
102
|
* Timeout to default to for all operations.
|
|
93
103
|
*/
|
package/lib/api/common.js
CHANGED
|
@@ -75,7 +75,7 @@ export const isCloneId = (roleName) => roleName.includes(CLONE_ID_DELIMITER);
|
|
|
75
75
|
*/
|
|
76
76
|
export const getBaseRoleNameFromCloneId = (roleName) => {
|
|
77
77
|
if (!isCloneId(roleName)) {
|
|
78
|
-
throw new
|
|
78
|
+
throw new HolochainError("MissingCloneIdDelimiter", `invalid clone id - no clone id delimiter found in role name ${roleName}`);
|
|
79
79
|
}
|
|
80
80
|
return roleName.split(CLONE_ID_DELIMITER)[0];
|
|
81
81
|
};
|
|
@@ -102,7 +102,7 @@ export class CloneId {
|
|
|
102
102
|
static fromRoleName(roleName) {
|
|
103
103
|
const parts = roleName.split(CLONE_ID_DELIMITER);
|
|
104
104
|
if (parts.length !== 2) {
|
|
105
|
-
throw new
|
|
105
|
+
throw new HolochainError("MalformedCloneId", `clone id must consist of 'role_id.clone_index', but got ${roleName}`);
|
|
106
106
|
}
|
|
107
107
|
return new CloneId(parts[0], parseInt(parts[1]));
|
|
108
108
|
}
|
package/lib/api/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export * from "./admin/index.js";
|
|
|
3
3
|
export * from "./app-agent/index.js";
|
|
4
4
|
export * from "./app/index.js";
|
|
5
5
|
export { IsoWebSocket, WsClient } from "./client.js";
|
|
6
|
-
export { CloneId, HolochainError, Requester, Transformer, WebsocketConnectionOptions, getBaseRoleNameFromCloneId, isCloneId, } from "./common.js";
|
|
6
|
+
export { CloneId, HolochainError, Requester, Transformer, WebsocketConnectionOptions, WsClientOptions, getBaseRoleNameFromCloneId, isCloneId, } from "./common.js";
|
|
7
7
|
export * from "./zome-call-signing.js";
|
package/lib/tsdoc-metadata.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holochain/client",
|
|
3
|
-
"version": "0.17.0-dev.
|
|
3
|
+
"version": "0.17.0-dev.8",
|
|
4
4
|
"description": "A JavaScript client for the Holochain Conductor API",
|
|
5
5
|
"author": "Holochain Foundation <info@holochain.org> (http://holochain.org)",
|
|
6
6
|
"license": "CAL-1.0",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"test": "RUST_LOG=error RUST_BACKTRACE=1 node --loader ts-node/esm test/index.ts",
|
|
38
38
|
"build:lib": "rimraf ./lib && tsc -p tsconfig.build.json",
|
|
39
39
|
"build:docs": "api-extractor run --local && api-documenter markdown -i docs/temp -o docs",
|
|
40
|
-
"build": "npm run build:lib && npm run build:docs"
|
|
40
|
+
"build": "npm run build:lib && npm run build:docs",
|
|
41
|
+
"prepublishOnly": "npm run lint && npm run build"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
44
|
"@bitgo/blake2b": "^3.2.4",
|