@holochain/client 0.17.0-dev.8 → 0.18.0-dev.0
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 -3
- package/lib/api/admin/types.d.ts +52 -22
- package/lib/api/admin/types.js +5 -5
- package/lib/api/admin/websocket.d.ts +4 -3
- package/lib/api/admin/websocket.js +6 -40
- package/lib/api/app/types.d.ts +73 -22
- package/lib/api/app/websocket.d.ts +47 -34
- package/lib/api/app/websocket.js +178 -53
- package/lib/api/client.d.ts +17 -0
- package/lib/api/client.js +24 -17
- package/lib/api/common.d.ts +3 -5
- package/lib/api/common.js +16 -19
- package/lib/api/index.d.ts +2 -3
- package/lib/api/index.js +1 -2
- package/lib/environments/launcher.d.ts +3 -5
- package/lib/environments/launcher.js +0 -60
- package/lib/hdk/capabilities.d.ts +3 -7
- package/lib/hdk/entry.d.ts +1 -5
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +8 -8
- package/lib/api/app-agent/index.d.ts +0 -2
- package/lib/api/app-agent/index.js +0 -2
- package/lib/api/app-agent/types.d.ts +0 -59
- package/lib/api/app-agent/types.js +0 -1
- package/lib/api/app-agent/websocket.d.ts +0 -86
- package/lib/api/app-agent/websocket.js +0 -209
package/lib/api/app/websocket.js
CHANGED
|
@@ -1,41 +1,63 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Emittery from "emittery";
|
|
2
|
+
import { omit } from "lodash-es";
|
|
3
|
+
import { CellType } from "../admin/index.js";
|
|
4
|
+
import { catchError, DEFAULT_TIMEOUT, getBaseRoleNameFromCloneId, HolochainError, isCloneId, promiseTimeout, requesterTransformer, } from "../common.js";
|
|
5
|
+
import { getHostZomeCallSigner, getLauncherEnvironment, } from "../../environments/launcher.js";
|
|
2
6
|
import { decode, encode } from "@msgpack/msgpack";
|
|
7
|
+
import { getNonceExpiration, getSigningCredentials, randomNonce, } from "../zome-call-signing.js";
|
|
8
|
+
import { encodeHashToBase64 } from "../../utils/index.js";
|
|
9
|
+
import { hashZomeCall } from "@holochain/serialization";
|
|
3
10
|
import _sodium from "libsodium-wrappers";
|
|
4
|
-
import Emittery from "emittery";
|
|
5
|
-
import { getLauncherEnvironment, signZomeCallTauri, signZomeCallElectron, getHostZomeCallSigner, } from "../../environments/launcher.js";
|
|
6
|
-
import { encodeHashToBase64 } from "../../utils/base64.js";
|
|
7
11
|
import { WsClient } from "../client.js";
|
|
8
|
-
import { DEFAULT_TIMEOUT, catchError, promiseTimeout, requesterTransformer, HolochainError, } from "../common.js";
|
|
9
|
-
import { getNonceExpiration, getSigningCredentials, randomNonce, } from "../zome-call-signing.js";
|
|
10
12
|
/**
|
|
11
|
-
* A class to establish a websocket connection to an App interface
|
|
12
|
-
*
|
|
13
|
+
* A class to establish a websocket connection to an App interface, for a
|
|
14
|
+
* specific agent and app.
|
|
13
15
|
*
|
|
14
16
|
* @public
|
|
15
17
|
*/
|
|
16
|
-
export class AppWebsocket
|
|
18
|
+
export class AppWebsocket {
|
|
17
19
|
client;
|
|
20
|
+
myPubKey;
|
|
18
21
|
defaultTimeout;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
emitter;
|
|
23
|
+
cachedAppInfo;
|
|
24
|
+
appInfoRequester;
|
|
25
|
+
callZomeRequester;
|
|
26
|
+
createCloneCellRequester;
|
|
27
|
+
enableCloneCellRequester;
|
|
28
|
+
disableCloneCellRequester;
|
|
29
|
+
networkInfoRequester;
|
|
30
|
+
constructor(client, appInfo, defaultTimeout) {
|
|
31
|
+
this.client = client;
|
|
32
|
+
this.myPubKey = appInfo.agent_pub_key;
|
|
33
|
+
this.defaultTimeout = defaultTimeout ?? DEFAULT_TIMEOUT;
|
|
34
|
+
this.emitter = new Emittery();
|
|
35
|
+
this.cachedAppInfo = appInfo;
|
|
36
|
+
this.appInfoRequester = AppWebsocket.requester(this.client, "app_info", this.defaultTimeout);
|
|
37
|
+
this.callZomeRequester = AppWebsocket.requester(this.client, "call_zome", this.defaultTimeout, callZomeTransform);
|
|
38
|
+
this.createCloneCellRequester = AppWebsocket.requester(this.client, "create_clone_cell", this.defaultTimeout);
|
|
39
|
+
this.enableCloneCellRequester = AppWebsocket.requester(this.client, "enable_clone_cell", this.defaultTimeout);
|
|
40
|
+
this.disableCloneCellRequester = AppWebsocket.requester(this.client, "disable_clone_cell", this.defaultTimeout);
|
|
41
|
+
this.networkInfoRequester = AppWebsocket.requester(this.client, "network_info", this.defaultTimeout);
|
|
22
42
|
// Ensure all super methods are bound to this instance because Emittery relies on `this` being the instance.
|
|
23
43
|
// Please retain until the upstream is fixed https://github.com/sindresorhus/emittery/issues/86.
|
|
24
44
|
Object.getOwnPropertyNames(Emittery.prototype).forEach((name) => {
|
|
25
|
-
const to_bind = this[name];
|
|
45
|
+
const to_bind = this.emitter[name];
|
|
26
46
|
if (typeof to_bind === "function") {
|
|
27
|
-
this[name] =
|
|
28
|
-
to_bind.bind(this);
|
|
47
|
+
this.emitter[name] =
|
|
48
|
+
to_bind.bind(this.emitter);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
this.client.on("signal", (signal) => {
|
|
52
|
+
if (this.containsCell(signal.cell_id)) {
|
|
53
|
+
this.emitter.emit("signal", signal).catch(console.error);
|
|
29
54
|
}
|
|
30
55
|
});
|
|
31
|
-
this.client = client;
|
|
32
|
-
this.defaultTimeout =
|
|
33
|
-
defaultTimeout === undefined ? DEFAULT_TIMEOUT : defaultTimeout;
|
|
34
|
-
this.overrideInstalledAppId = overrideInstalledAppId;
|
|
35
56
|
}
|
|
36
57
|
/**
|
|
37
|
-
* Instance factory for creating
|
|
58
|
+
* Instance factory for creating an {@link AppWebsocket}.
|
|
38
59
|
*
|
|
60
|
+
* @param token - A token to authenticate the websocket connection. Get a token using AdminWebsocket#issueAppAuthenticationToken.
|
|
39
61
|
* @param options - {@link (WebsocketConnectionOptions:interface)}
|
|
40
62
|
* @returns A new instance of an AppWebsocket.
|
|
41
63
|
*/
|
|
@@ -43,25 +65,70 @@ export class AppWebsocket extends Emittery {
|
|
|
43
65
|
// Check if we are in the launcher's environment, and if so, redirect the url to connect to
|
|
44
66
|
const env = getLauncherEnvironment();
|
|
45
67
|
if (env?.APP_INTERFACE_PORT) {
|
|
46
|
-
options.url = new URL(`ws://
|
|
68
|
+
options.url = new URL(`ws://localhost:${env.APP_INTERFACE_PORT}`);
|
|
47
69
|
}
|
|
48
70
|
if (!options.url) {
|
|
49
71
|
throw new HolochainError("ConnectionUrlMissing", `unable to connect to Conductor API - no url provided and not in a launcher environment.`);
|
|
50
72
|
}
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
const client = await WsClient.connect(options.url, options.wsClientOptions);
|
|
74
|
+
if (env?.APP_INTERFACE_TOKEN) {
|
|
75
|
+
// Note: This will only work for multiple connections if a single_use = false token is provided
|
|
76
|
+
await client.authenticate({ token: env.APP_INTERFACE_TOKEN });
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
if (!options.token) {
|
|
80
|
+
throw new HolochainError("AppAuthenticationTokenMissing", `unable to connect to Conductor API - no app authentication token provided.`);
|
|
81
|
+
}
|
|
82
|
+
await client.authenticate({ token: options.token });
|
|
83
|
+
}
|
|
84
|
+
const appInfo = await this.requester(client, "app_info", DEFAULT_TIMEOUT)(null);
|
|
85
|
+
if (!appInfo) {
|
|
86
|
+
throw new HolochainError("AppNotFound", `The app your connection token was issued for was not found. The app needs to be installed and enabled.`);
|
|
87
|
+
}
|
|
88
|
+
return new AppWebsocket(client, appInfo, options.defaultTimeout);
|
|
58
89
|
}
|
|
59
90
|
/**
|
|
60
91
|
* Request the app's info, including all cell infos.
|
|
61
92
|
*
|
|
93
|
+
* @param timeout - A timeout to override the default.
|
|
62
94
|
* @returns The app's {@link AppInfo}.
|
|
63
95
|
*/
|
|
64
|
-
appInfo
|
|
96
|
+
async appInfo(timeout) {
|
|
97
|
+
const appInfo = await this.appInfoRequester(null, timeout);
|
|
98
|
+
if (!appInfo) {
|
|
99
|
+
throw new HolochainError("AppNotFound", `App info not found. App needs to be installed and enabled.`);
|
|
100
|
+
}
|
|
101
|
+
this.cachedAppInfo = appInfo;
|
|
102
|
+
return appInfo;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get a cell id by its role name or clone id.
|
|
106
|
+
*
|
|
107
|
+
* @param roleName - The role name or clone id of the cell.
|
|
108
|
+
* @param appInfo - The app info containing all cell infos.
|
|
109
|
+
* @returns The cell id or throws an error if not found.
|
|
110
|
+
*/
|
|
111
|
+
getCellIdFromRoleName(roleName, appInfo) {
|
|
112
|
+
if (isCloneId(roleName)) {
|
|
113
|
+
const baseRoleName = getBaseRoleNameFromCloneId(roleName);
|
|
114
|
+
if (!(baseRoleName in appInfo.cell_info)) {
|
|
115
|
+
throw new HolochainError("NoCellForRoleName", `no cell found with role_name ${roleName}`);
|
|
116
|
+
}
|
|
117
|
+
const cloneCell = appInfo.cell_info[baseRoleName].find((c) => CellType.Cloned in c && c[CellType.Cloned].clone_id === roleName);
|
|
118
|
+
if (!cloneCell || !(CellType.Cloned in cloneCell)) {
|
|
119
|
+
throw new HolochainError("NoCellForCloneId", `no clone cell found with clone id ${roleName}`);
|
|
120
|
+
}
|
|
121
|
+
return cloneCell[CellType.Cloned].cell_id;
|
|
122
|
+
}
|
|
123
|
+
if (!(roleName in appInfo.cell_info)) {
|
|
124
|
+
throw new HolochainError("NoCellForRoleName", `no cell found with role_name ${roleName}`);
|
|
125
|
+
}
|
|
126
|
+
const cell = appInfo.cell_info[roleName].find((c) => CellType.Provisioned in c);
|
|
127
|
+
if (!cell || !(CellType.Provisioned in cell)) {
|
|
128
|
+
throw new HolochainError("NoProvisionedCellForRoleName", `no provisioned cell found with role_name ${roleName}`);
|
|
129
|
+
}
|
|
130
|
+
return cell[CellType.Provisioned].cell_id;
|
|
131
|
+
}
|
|
65
132
|
/**
|
|
66
133
|
* Call a zome.
|
|
67
134
|
*
|
|
@@ -69,31 +136,105 @@ export class AppWebsocket extends Emittery {
|
|
|
69
136
|
* @param timeout - A timeout to override the default.
|
|
70
137
|
* @returns The zome call's response.
|
|
71
138
|
*/
|
|
72
|
-
callZome
|
|
139
|
+
async callZome(request, timeout) {
|
|
140
|
+
if (!("provenance" in request)) {
|
|
141
|
+
request = {
|
|
142
|
+
...request,
|
|
143
|
+
provenance: this.myPubKey,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if ("role_name" in request && request.role_name) {
|
|
147
|
+
const appInfo = this.cachedAppInfo || (await this.appInfo());
|
|
148
|
+
const cell_id = this.getCellIdFromRoleName(request.role_name, appInfo);
|
|
149
|
+
const zomeCallPayload = {
|
|
150
|
+
...omit(request, "role_name"),
|
|
151
|
+
provenance: this.myPubKey,
|
|
152
|
+
cell_id: [cell_id[0], cell_id[1]],
|
|
153
|
+
};
|
|
154
|
+
return this.callZomeRequester(zomeCallPayload, timeout);
|
|
155
|
+
}
|
|
156
|
+
else if ("cell_id" in request && request.cell_id) {
|
|
157
|
+
return this.callZomeRequester(request, timeout);
|
|
158
|
+
}
|
|
159
|
+
throw new HolochainError("MissingRoleNameOrCellId", "callZome requires a role_name or cell_id argument");
|
|
160
|
+
}
|
|
73
161
|
/**
|
|
74
162
|
* Clone an existing provisioned cell.
|
|
75
163
|
*
|
|
76
164
|
* @param args - Specify the cell to clone.
|
|
77
165
|
* @returns The created clone cell.
|
|
78
166
|
*/
|
|
79
|
-
createCloneCell
|
|
167
|
+
async createCloneCell(args) {
|
|
168
|
+
const clonedCell = this.createCloneCellRequester({
|
|
169
|
+
...args,
|
|
170
|
+
});
|
|
171
|
+
this.cachedAppInfo = undefined;
|
|
172
|
+
return clonedCell;
|
|
173
|
+
}
|
|
80
174
|
/**
|
|
81
175
|
* Enable a disabled clone cell.
|
|
82
176
|
*
|
|
83
177
|
* @param args - Specify the clone cell to enable.
|
|
84
178
|
* @returns The enabled clone cell.
|
|
85
179
|
*/
|
|
86
|
-
enableCloneCell
|
|
180
|
+
async enableCloneCell(args) {
|
|
181
|
+
return this.enableCloneCellRequester({
|
|
182
|
+
...args,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
87
185
|
/**
|
|
88
186
|
* Disable an enabled clone cell.
|
|
89
187
|
*
|
|
90
188
|
* @param args - Specify the clone cell to disable.
|
|
91
189
|
*/
|
|
92
|
-
disableCloneCell
|
|
190
|
+
async disableCloneCell(args) {
|
|
191
|
+
return this.disableCloneCellRequester({
|
|
192
|
+
...args,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
93
195
|
/**
|
|
94
196
|
* Request network info about gossip status.
|
|
197
|
+
* @param args - Specify the DNAs for which you want network info
|
|
198
|
+
* @returns Network info for the specified DNAs
|
|
199
|
+
*/
|
|
200
|
+
async networkInfo(args) {
|
|
201
|
+
return this.networkInfoRequester({
|
|
202
|
+
...args,
|
|
203
|
+
agent_pub_key: this.myPubKey,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Register an event listener for signals.
|
|
208
|
+
*
|
|
209
|
+
* @param eventName - Event name to listen to (currently only "signal").
|
|
210
|
+
* @param listener - The function to call when event is triggered.
|
|
211
|
+
* @returns A function to unsubscribe the event listener.
|
|
95
212
|
*/
|
|
96
|
-
|
|
213
|
+
on(eventName, listener) {
|
|
214
|
+
return this.emitter.on(eventName, listener);
|
|
215
|
+
}
|
|
216
|
+
static requester(client, tag, defaultTimeout, transformer) {
|
|
217
|
+
return requesterTransformer((req, timeout) => promiseTimeout(client.request(req), tag, timeout || defaultTimeout).then(catchError), tag, transformer);
|
|
218
|
+
}
|
|
219
|
+
containsCell(cellId) {
|
|
220
|
+
const appInfo = this.cachedAppInfo;
|
|
221
|
+
if (!appInfo) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
for (const roleName of Object.keys(appInfo.cell_info)) {
|
|
225
|
+
for (const cellInfo of appInfo.cell_info[roleName]) {
|
|
226
|
+
const currentCellId = CellType.Provisioned in cellInfo
|
|
227
|
+
? cellInfo[CellType.Provisioned].cell_id
|
|
228
|
+
: CellType.Cloned in cellInfo
|
|
229
|
+
? cellInfo[CellType.Cloned].cell_id
|
|
230
|
+
: undefined;
|
|
231
|
+
if (currentCellId && isSameCell(currentCellId, cellId)) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
97
238
|
}
|
|
98
239
|
const callZomeTransform = {
|
|
99
240
|
input: async (request) => {
|
|
@@ -105,29 +246,13 @@ const callZomeTransform = {
|
|
|
105
246
|
return hostSigner.signZomeCall(request);
|
|
106
247
|
}
|
|
107
248
|
else {
|
|
108
|
-
|
|
109
|
-
if (!env) {
|
|
110
|
-
return signZomeCall(request);
|
|
111
|
-
}
|
|
112
|
-
if (env.FRAMEWORK === "electron") {
|
|
113
|
-
return signZomeCallElectron(request);
|
|
114
|
-
}
|
|
115
|
-
return signZomeCallTauri(request);
|
|
249
|
+
return signZomeCall(request);
|
|
116
250
|
}
|
|
117
251
|
},
|
|
118
252
|
output: (response) => decode(response),
|
|
119
253
|
};
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
if (appWs.overrideInstalledAppId) {
|
|
123
|
-
return {
|
|
124
|
-
installed_app_id: appWs.overrideInstalledAppId,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
return request;
|
|
128
|
-
},
|
|
129
|
-
output: (response) => response,
|
|
130
|
-
});
|
|
254
|
+
const isSameCell = (cellId1, cellId2) => cellId1[0].every((byte, index) => byte === cellId2[0][index]) &&
|
|
255
|
+
cellId1[1].every((byte, index) => byte === cellId2[1][index]);
|
|
131
256
|
/**
|
|
132
257
|
* @public
|
|
133
258
|
*/
|
package/lib/api/client.d.ts
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
import Emittery from "emittery";
|
|
3
3
|
import IsoWebSocket from "isomorphic-ws";
|
|
4
4
|
import { WsClientOptions } from "./common.js";
|
|
5
|
+
import { AppAuthenticationToken } from "./admin/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export interface AppAuthenticationRequest {
|
|
10
|
+
token: AppAuthenticationToken;
|
|
11
|
+
}
|
|
5
12
|
/**
|
|
6
13
|
* A WebSocket client which can make requests and receive responses,
|
|
7
14
|
* as well as send and receive signals.
|
|
@@ -22,6 +29,7 @@ export declare class WsClient extends Emittery {
|
|
|
22
29
|
* Instance factory for creating WsClients.
|
|
23
30
|
*
|
|
24
31
|
* @param url - The WebSocket URL to connect to.
|
|
32
|
+
* @param options - Options for the WsClient.
|
|
25
33
|
* @returns An new instance of the WsClient.
|
|
26
34
|
*/
|
|
27
35
|
static connect(url: URL, options?: WsClientOptions): Promise<WsClient>;
|
|
@@ -31,6 +39,14 @@ export declare class WsClient extends Emittery {
|
|
|
31
39
|
* @param data - Data to send.
|
|
32
40
|
*/
|
|
33
41
|
emitSignal(data: unknown): void;
|
|
42
|
+
/**
|
|
43
|
+
* Authenticate the client with the conductor.
|
|
44
|
+
*
|
|
45
|
+
* This is only relevant for app websockets.
|
|
46
|
+
*
|
|
47
|
+
* @param request - The authentication request, containing an app authentication token.
|
|
48
|
+
*/
|
|
49
|
+
authenticate(request: AppAuthenticationRequest): Promise<void>;
|
|
34
50
|
/**
|
|
35
51
|
* Send requests to the connected websocket.
|
|
36
52
|
*
|
|
@@ -38,6 +54,7 @@ export declare class WsClient extends Emittery {
|
|
|
38
54
|
* @returns
|
|
39
55
|
*/
|
|
40
56
|
request<Response>(request: unknown): Promise<Response>;
|
|
57
|
+
private exchange;
|
|
41
58
|
private sendMessage;
|
|
42
59
|
private handleResponse;
|
|
43
60
|
/**
|
package/lib/api/client.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { decode, encode } from "@msgpack/msgpack";
|
|
2
2
|
import Emittery from "emittery";
|
|
3
3
|
import IsoWebSocket from "isomorphic-ws";
|
|
4
|
-
import { SignalType } from "./app/types.js";
|
|
5
4
|
import { HolochainError } from "./common.js";
|
|
5
|
+
import { SignalType } from "./app/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* A WebSocket client which can make requests and receive responses,
|
|
8
8
|
* as well as send and receive signals.
|
|
@@ -87,6 +87,7 @@ export class WsClient extends Emittery {
|
|
|
87
87
|
* Instance factory for creating WsClients.
|
|
88
88
|
*
|
|
89
89
|
* @param url - The WebSocket URL to connect to.
|
|
90
|
+
* @param options - Options for the WsClient.
|
|
90
91
|
* @returns An new instance of the WsClient.
|
|
91
92
|
*/
|
|
92
93
|
static connect(url, options) {
|
|
@@ -113,6 +114,24 @@ export class WsClient extends Emittery {
|
|
|
113
114
|
});
|
|
114
115
|
this.socket.send(encodedMsg);
|
|
115
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Authenticate the client with the conductor.
|
|
119
|
+
*
|
|
120
|
+
* This is only relevant for app websockets.
|
|
121
|
+
*
|
|
122
|
+
* @param request - The authentication request, containing an app authentication token.
|
|
123
|
+
*/
|
|
124
|
+
async authenticate(request) {
|
|
125
|
+
return this.exchange(request, (request, resolve) => {
|
|
126
|
+
const encodedMsg = encode({
|
|
127
|
+
type: "authenticate",
|
|
128
|
+
data: encode(request),
|
|
129
|
+
});
|
|
130
|
+
this.socket.send(encodedMsg);
|
|
131
|
+
// Message just needs to be sent first, no need to wait for a response or even require a flush
|
|
132
|
+
resolve(null);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
116
135
|
/**
|
|
117
136
|
* Send requests to the connected websocket.
|
|
118
137
|
*
|
|
@@ -120,27 +139,15 @@ export class WsClient extends Emittery {
|
|
|
120
139
|
* @returns
|
|
121
140
|
*/
|
|
122
141
|
async request(request) {
|
|
142
|
+
return this.exchange(request, this.sendMessage.bind(this));
|
|
143
|
+
}
|
|
144
|
+
exchange(request, sendHandler) {
|
|
123
145
|
if (this.socket.readyState === this.socket.OPEN) {
|
|
124
146
|
const promise = new Promise((resolve, reject) => {
|
|
125
|
-
|
|
147
|
+
sendHandler(request, resolve, reject);
|
|
126
148
|
});
|
|
127
149
|
return promise;
|
|
128
150
|
}
|
|
129
|
-
else if (this.url) {
|
|
130
|
-
const response = new Promise((resolve, reject) => {
|
|
131
|
-
// typescript forgets in this promise scope that this.url is not undefined
|
|
132
|
-
const socket = new IsoWebSocket(this.url, this.options);
|
|
133
|
-
this.socket = socket;
|
|
134
|
-
socket.onerror = (errorEvent) => {
|
|
135
|
-
reject(new HolochainError("ConnectionError", `could not connect to Holochain Conductor API at ${this.url} - ${errorEvent.error}`));
|
|
136
|
-
};
|
|
137
|
-
socket.onopen = () => {
|
|
138
|
-
this.sendMessage(request, resolve, reject);
|
|
139
|
-
};
|
|
140
|
-
this.setupSocket();
|
|
141
|
-
});
|
|
142
|
-
return response;
|
|
143
|
-
}
|
|
144
151
|
else {
|
|
145
152
|
return Promise.reject(new Error("Socket is not open"));
|
|
146
153
|
}
|
package/lib/api/common.d.ts
CHANGED
|
@@ -16,14 +16,12 @@ export type Requester<Req, Res> = (req: Req, timeout?: number) => Promise<Res>;
|
|
|
16
16
|
/**
|
|
17
17
|
* @public
|
|
18
18
|
*/
|
|
19
|
-
export type
|
|
19
|
+
export type RequesterNoArg<Res> = (timeout?: number) => Promise<Res>;
|
|
20
20
|
/**
|
|
21
21
|
* @public
|
|
22
22
|
*/
|
|
23
23
|
export type Tagged<T> = {
|
|
24
|
-
type:
|
|
25
|
-
[tag: string]: null;
|
|
26
|
-
};
|
|
24
|
+
type: string;
|
|
27
25
|
data: T;
|
|
28
26
|
};
|
|
29
27
|
/**
|
|
@@ -42,7 +40,7 @@ export declare const requesterTransformer: <ReqI, ReqO, ResI, ResO>(requester: R
|
|
|
42
40
|
export declare class HolochainError extends Error {
|
|
43
41
|
constructor(name: string, message: string);
|
|
44
42
|
}
|
|
45
|
-
export declare const catchError: (
|
|
43
|
+
export declare const catchError: (response: any) => Promise<any>;
|
|
46
44
|
export declare const promiseTimeout: (promise: Promise<unknown>, tag: string, ms: number) => Promise<unknown>;
|
|
47
45
|
/**
|
|
48
46
|
* Check if a cell's role name is a valid clone id.
|
package/lib/api/common.js
CHANGED
|
@@ -9,10 +9,9 @@ export const DEFAULT_TIMEOUT = 60000;
|
|
|
9
9
|
*/
|
|
10
10
|
export const requesterTransformer = (requester, tag, transform = identityTransformer) => async (req, timeout) => {
|
|
11
11
|
const transformedInput = await transform.input(req);
|
|
12
|
-
const input = { type:
|
|
12
|
+
const input = { type: tag, data: transformedInput };
|
|
13
13
|
const response = await requester(input, timeout);
|
|
14
|
-
|
|
15
|
-
return output;
|
|
14
|
+
return transform.output(response.data);
|
|
16
15
|
};
|
|
17
16
|
const identity = (x) => x;
|
|
18
17
|
const identityTransformer = {
|
|
@@ -32,31 +31,29 @@ export class HolochainError extends Error {
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
// this determines the error format of all error responses
|
|
35
|
-
export const catchError = (
|
|
36
|
-
if (
|
|
37
|
-
const errorName =
|
|
38
|
-
const error = new HolochainError(errorName,
|
|
34
|
+
export const catchError = (response) => {
|
|
35
|
+
if (response.type === ERROR_TYPE) {
|
|
36
|
+
const errorName = response.data.type;
|
|
37
|
+
const error = new HolochainError(errorName, response.data.data);
|
|
39
38
|
return Promise.reject(error);
|
|
40
39
|
}
|
|
41
40
|
else {
|
|
42
|
-
return Promise.resolve(
|
|
41
|
+
return Promise.resolve(response);
|
|
43
42
|
}
|
|
44
43
|
};
|
|
45
44
|
export const promiseTimeout = (promise, tag, ms) => {
|
|
46
45
|
let id;
|
|
47
46
|
const timeout = new Promise((_, reject) => {
|
|
48
|
-
id = setTimeout(() => reject(new Error(`
|
|
49
|
-
});
|
|
50
|
-
return new Promise((res, rej) => {
|
|
51
|
-
Promise.race([promise, timeout])
|
|
52
|
-
.then((a) => {
|
|
53
|
-
clearTimeout(id);
|
|
54
|
-
return res(a);
|
|
55
|
-
})
|
|
56
|
-
.catch((e) => {
|
|
57
|
-
return rej(e);
|
|
58
|
-
});
|
|
47
|
+
id = setTimeout(() => reject(new Error(`Request timed out in ${ms} ms: ${tag}`)), ms);
|
|
59
48
|
});
|
|
49
|
+
return new Promise((res, rej) => Promise.race([promise, timeout])
|
|
50
|
+
.then((a) => {
|
|
51
|
+
clearTimeout(id);
|
|
52
|
+
return res(a);
|
|
53
|
+
})
|
|
54
|
+
.catch((e) => {
|
|
55
|
+
return rej(e);
|
|
56
|
+
}));
|
|
60
57
|
};
|
|
61
58
|
const CLONE_ID_DELIMITER = ".";
|
|
62
59
|
/**
|
package/lib/api/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { hashZomeCall } from "@holochain/serialization";
|
|
2
2
|
export * from "./admin/index.js";
|
|
3
|
-
export * from "./app-agent/index.js";
|
|
4
3
|
export * from "./app/index.js";
|
|
5
|
-
export { IsoWebSocket, WsClient } from "./client.js";
|
|
6
|
-
export { CloneId, HolochainError, Requester, Transformer, WebsocketConnectionOptions, WsClientOptions,
|
|
4
|
+
export { IsoWebSocket, WsClient, type AppAuthenticationRequest, } from "./client.js";
|
|
5
|
+
export { CloneId, HolochainError, getBaseRoleNameFromCloneId, isCloneId, type Requester, type Transformer, type WebsocketConnectionOptions, type WsClientOptions, } from "./common.js";
|
|
7
6
|
export * from "./zome-call-signing.js";
|
package/lib/api/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { hashZomeCall } from "@holochain/serialization";
|
|
2
2
|
export * from "./admin/index.js";
|
|
3
|
-
export * from "./app-agent/index.js";
|
|
4
3
|
export * from "./app/index.js";
|
|
5
|
-
export { IsoWebSocket, WsClient } from "./client.js";
|
|
4
|
+
export { IsoWebSocket, WsClient, } from "./client.js";
|
|
6
5
|
export { CloneId, HolochainError, getBaseRoleNameFromCloneId, isCloneId, } from "./common.js";
|
|
7
6
|
export * from "./zome-call-signing.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { CallZomeRequest } from "../api/
|
|
2
|
-
import { CallZomeRequestSigned, CallZomeRequestUnsigned } from "../api/
|
|
1
|
+
import { AppAuthenticationToken, CallZomeRequest } from "../api/index.js";
|
|
2
|
+
import { CallZomeRequestSigned, CallZomeRequestUnsigned } from "../api/index.js";
|
|
3
3
|
import { InstalledAppId } from "../types.js";
|
|
4
4
|
export interface LauncherEnvironment {
|
|
5
5
|
APP_INTERFACE_PORT?: number;
|
|
6
6
|
ADMIN_INTERFACE_PORT?: number;
|
|
7
7
|
INSTALLED_APP_ID?: InstalledAppId;
|
|
8
|
-
|
|
8
|
+
APP_INTERFACE_TOKEN?: AppAuthenticationToken;
|
|
9
9
|
}
|
|
10
10
|
export interface HostZomeCallSigner {
|
|
11
11
|
signZomeCall: (request: CallZomeRequest) => Promise<CallZomeRequestSigned>;
|
|
@@ -40,6 +40,4 @@ interface CallZomeRequestUnsignedElectron extends Omit<CallZomeRequestUnsigned,
|
|
|
40
40
|
nonce: Array<number>;
|
|
41
41
|
expiresAt: number;
|
|
42
42
|
}
|
|
43
|
-
export declare const signZomeCallTauri: (request: CallZomeRequest) => Promise<CallZomeRequestSigned>;
|
|
44
|
-
export declare const signZomeCallElectron: (request: CallZomeRequest) => Promise<CallZomeRequestSigned>;
|
|
45
43
|
export {};
|
|
@@ -1,65 +1,5 @@
|
|
|
1
|
-
import { encode } from "@msgpack/msgpack";
|
|
2
|
-
import { invoke } from "@tauri-apps/api/tauri";
|
|
3
|
-
import { getNonceExpiration, randomNonce } from "../api/zome-call-signing.js";
|
|
4
1
|
const __HC_LAUNCHER_ENV__ = "__HC_LAUNCHER_ENV__";
|
|
5
2
|
const __HC_ZOME_CALL_SIGNER__ = "__HC_ZOME_CALL_SIGNER__";
|
|
6
3
|
export const isLauncher = () => globalThis.window && __HC_LAUNCHER_ENV__ in globalThis.window;
|
|
7
4
|
export const getLauncherEnvironment = () => isLauncher() ? globalThis.window[__HC_LAUNCHER_ENV__] : undefined;
|
|
8
5
|
export const getHostZomeCallSigner = () => globalThis.window && globalThis.window[__HC_ZOME_CALL_SIGNER__];
|
|
9
|
-
export const signZomeCallTauri = async (request) => {
|
|
10
|
-
const zomeCallUnsigned = {
|
|
11
|
-
provenance: Array.from(request.provenance),
|
|
12
|
-
cell_id: [Array.from(request.cell_id[0]), Array.from(request.cell_id[1])],
|
|
13
|
-
zome_name: request.zome_name,
|
|
14
|
-
fn_name: request.fn_name,
|
|
15
|
-
payload: Array.from(encode(request.payload)),
|
|
16
|
-
nonce: Array.from(await randomNonce()),
|
|
17
|
-
expires_at: getNonceExpiration(),
|
|
18
|
-
};
|
|
19
|
-
const signedZomeCallTauri = await invoke("sign_zome_call", { zomeCallUnsigned });
|
|
20
|
-
const signedZomeCall = {
|
|
21
|
-
provenance: Uint8Array.from(signedZomeCallTauri.provenance),
|
|
22
|
-
cap_secret: null,
|
|
23
|
-
cell_id: [
|
|
24
|
-
Uint8Array.from(signedZomeCallTauri.cell_id[0]),
|
|
25
|
-
Uint8Array.from(signedZomeCallTauri.cell_id[1]),
|
|
26
|
-
],
|
|
27
|
-
zome_name: signedZomeCallTauri.zome_name,
|
|
28
|
-
fn_name: signedZomeCallTauri.fn_name,
|
|
29
|
-
payload: Uint8Array.from(signedZomeCallTauri.payload),
|
|
30
|
-
signature: Uint8Array.from(signedZomeCallTauri.signature),
|
|
31
|
-
expires_at: signedZomeCallTauri.expires_at,
|
|
32
|
-
nonce: Uint8Array.from(signedZomeCallTauri.nonce),
|
|
33
|
-
};
|
|
34
|
-
return signedZomeCall;
|
|
35
|
-
};
|
|
36
|
-
export const signZomeCallElectron = async (request) => {
|
|
37
|
-
if (!window.electronAPI) {
|
|
38
|
-
throw Error("Unable to signZomeCallElectron. window.electronAPI not defined");
|
|
39
|
-
}
|
|
40
|
-
const zomeCallUnsignedElectron = {
|
|
41
|
-
provenance: Array.from(request.provenance),
|
|
42
|
-
cellId: [Array.from(request.cell_id[0]), Array.from(request.cell_id[1])],
|
|
43
|
-
zomeName: request.zome_name,
|
|
44
|
-
fnName: request.fn_name,
|
|
45
|
-
payload: Array.from(encode(request.payload)),
|
|
46
|
-
nonce: Array.from(await randomNonce()),
|
|
47
|
-
expiresAt: getNonceExpiration(),
|
|
48
|
-
};
|
|
49
|
-
const zomeCallSignedElectron = await window.electronAPI.signZomeCall(zomeCallUnsignedElectron);
|
|
50
|
-
const zomeCallSigned = {
|
|
51
|
-
provenance: Uint8Array.from(zomeCallSignedElectron.provenance),
|
|
52
|
-
cap_secret: null,
|
|
53
|
-
cell_id: [
|
|
54
|
-
Uint8Array.from(zomeCallSignedElectron.cellId[0]),
|
|
55
|
-
Uint8Array.from(zomeCallSignedElectron.cellId[1]),
|
|
56
|
-
],
|
|
57
|
-
zome_name: zomeCallSignedElectron.zomeName,
|
|
58
|
-
fn_name: zomeCallSignedElectron.fnName,
|
|
59
|
-
payload: Uint8Array.from(zomeCallSignedElectron.payload),
|
|
60
|
-
signature: Uint8Array.from(zomeCallSignedElectron.signature),
|
|
61
|
-
expires_at: zomeCallSignedElectron.expiresAt,
|
|
62
|
-
nonce: Uint8Array.from(zomeCallSignedElectron.nonce),
|
|
63
|
-
};
|
|
64
|
-
return zomeCallSigned;
|
|
65
|
-
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FunctionName, ZomeName } from "../api/admin/
|
|
1
|
+
import { FunctionName, ZomeName } from "../api/admin/index.js";
|
|
2
2
|
import { AgentPubKey } from "../types.js";
|
|
3
3
|
/**
|
|
4
4
|
* @public
|
|
@@ -22,9 +22,7 @@ export declare enum GrantedFunctionsType {
|
|
|
22
22
|
/**
|
|
23
23
|
* @public
|
|
24
24
|
*/
|
|
25
|
-
export type GrantedFunctions = {
|
|
26
|
-
[GrantedFunctionsType.All]: null;
|
|
27
|
-
} | {
|
|
25
|
+
export type GrantedFunctions = GrantedFunctionsType.All | {
|
|
28
26
|
[GrantedFunctionsType.Listed]: [ZomeName, FunctionName][];
|
|
29
27
|
};
|
|
30
28
|
/**
|
|
@@ -46,9 +44,7 @@ export declare enum CapAccessType {
|
|
|
46
44
|
/**
|
|
47
45
|
* @public
|
|
48
46
|
*/
|
|
49
|
-
export type CapAccess = {
|
|
50
|
-
[CapAccessType.Unrestricted]: null;
|
|
51
|
-
} | {
|
|
47
|
+
export type CapAccess = [CapAccessType.Unrestricted] | {
|
|
52
48
|
[CapAccessType.Transferable]: {
|
|
53
49
|
secret: CapSecret;
|
|
54
50
|
};
|
package/lib/hdk/entry.d.ts
CHANGED