@holochain/client 0.18.0-dev.9 → 0.19.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/lib/api/admin/types.d.ts +22 -2
- package/lib/api/admin/websocket.d.ts +5 -1
- package/lib/api/admin/websocket.js +4 -0
- package/lib/api/app/types.d.ts +21 -7
- package/lib/api/app/websocket.d.ts +3 -3
- package/lib/api/app/websocket.js +10 -35
- package/lib/api/client.d.ts +8 -5
- package/lib/api/client.js +129 -81
- package/lib/api/common.js +1 -0
- package/lib/api/zome-call-signing.d.ts +4 -6
- package/lib/api/zome-call-signing.js +5 -3
- package/lib/hdk/countersigning.d.ts +17 -0
- package/lib/hdk/link.d.ts +5 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/types.d.ts +29 -2
- package/lib/types.js +1 -5
- package/lib/utils/base64.d.ts +3 -4
- package/lib/utils/base64.js +3 -3
- package/lib/utils/cell.d.ts +11 -0
- package/lib/utils/cell.js +17 -0
- package/lib/utils/fake-hash.d.ts +4 -4
- package/lib/utils/fake-hash.js +20 -17
- package/lib/utils/hash-parts.d.ts +5 -5
- package/lib/utils/hash-parts.js +8 -11
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -15,12 +15,12 @@ A JavaScript client for the Holochain Conductor API (works with browsers as well
|
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
|
+
**JS client v0.19.x** is compatible with **Holochain v0.5.x**.
|
|
19
|
+
|
|
18
20
|
**JS client v0.18.x** is compatible with **Holochain v0.4.x**.
|
|
19
21
|
|
|
20
22
|
**JS client v0.17.x** is compatible with **Holochain v0.3.x**.
|
|
21
23
|
|
|
22
|
-
**JS client v0.16.x** is compatible with **Holochain v0.2.x**.
|
|
23
|
-
|
|
24
24
|
To install from NPM, run
|
|
25
25
|
```bash
|
|
26
26
|
npm install --save-exact @holochain/client
|
|
@@ -179,8 +179,9 @@ You need `holochain` and `hc` on your path, best to get them from nix with `nix-
|
|
|
179
179
|
|
|
180
180
|
To perform the pre-requisite DNA compilation steps, and run the Nodejs test, run:
|
|
181
181
|
```bash
|
|
182
|
-
nix
|
|
183
|
-
./
|
|
182
|
+
nix develop
|
|
183
|
+
./build-fixture.sh
|
|
184
|
+
npm run test
|
|
184
185
|
```
|
|
185
186
|
|
|
186
187
|
## Contribute
|
package/lib/api/admin/types.d.ts
CHANGED
|
@@ -120,6 +120,7 @@ export type AppInfo = {
|
|
|
120
120
|
installed_app_id: InstalledAppId;
|
|
121
121
|
cell_info: Record<RoleName, Array<CellInfo>>;
|
|
122
122
|
status: InstalledAppInfoStatus;
|
|
123
|
+
installed_at: Timestamp;
|
|
123
124
|
};
|
|
124
125
|
/**
|
|
125
126
|
* @public
|
|
@@ -180,6 +181,22 @@ export type GenerateAgentPubKeyRequest = void;
|
|
|
180
181
|
* @public
|
|
181
182
|
*/
|
|
182
183
|
export type GenerateAgentPubKeyResponse = AgentPubKey;
|
|
184
|
+
/**
|
|
185
|
+
* @public
|
|
186
|
+
*/
|
|
187
|
+
export type RevokeAgentKeyRequest = {
|
|
188
|
+
agent_key: AgentPubKey;
|
|
189
|
+
app_id: InstalledAppId;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Contains a list of errors of the cells where deletion was unsuccessful.
|
|
193
|
+
*
|
|
194
|
+
* If the key could not be deleted from all cells, the call
|
|
195
|
+
* {@link RevokeAgentKeyRequest} can be re-attempted to delete the key from the remaining cells.
|
|
196
|
+
*
|
|
197
|
+
* @public
|
|
198
|
+
*/
|
|
199
|
+
export type RevokeAgentKeyResponse = [CellId, string][];
|
|
183
200
|
/**
|
|
184
201
|
* @public
|
|
185
202
|
*/
|
|
@@ -398,8 +415,11 @@ export type NetworkSeed = string;
|
|
|
398
415
|
export type InstallAppRequest = {
|
|
399
416
|
/**
|
|
400
417
|
* The agent to use when creating Cells for this App.
|
|
418
|
+
* If not specified, a new agent will be generated by Holochain.
|
|
419
|
+
* If DPKI is enabled (default), and the agent key is not specified here,
|
|
420
|
+
* a new agent key will be derived from the DPKI device seed and registered with DPKI.
|
|
401
421
|
*/
|
|
402
|
-
agent_key
|
|
422
|
+
agent_key?: AgentPubKey;
|
|
403
423
|
/**
|
|
404
424
|
* The unique identifier for an installed app in this conductor.
|
|
405
425
|
* If not specified, it will be derived from the app name in the bundle manifest.
|
|
@@ -523,7 +543,7 @@ export interface DeleteCloneCellRequest {
|
|
|
523
543
|
/**
|
|
524
544
|
* The clone id or cell id of the clone cell
|
|
525
545
|
*/
|
|
526
|
-
clone_cell_id: RoleName |
|
|
546
|
+
clone_cell_id: RoleName | DnaHash;
|
|
527
547
|
}
|
|
528
548
|
/**
|
|
529
549
|
* @public
|
|
@@ -2,7 +2,7 @@ import { CapSecret, GrantedFunctions } from "../../hdk/index.js";
|
|
|
2
2
|
import type { AgentPubKey, CellId } from "../../types.js";
|
|
3
3
|
import { WsClient } from "../client.js";
|
|
4
4
|
import { Requester, Transformer, WebsocketConnectionOptions } from "../common.js";
|
|
5
|
-
import { AddAgentInfoRequest, AddAgentInfoResponse, AdminApi, AgentInfoRequest, AgentInfoResponse, AttachAppInterfaceRequest, AttachAppInterfaceResponse, DeleteCloneCellRequest, DeleteCloneCellResponse, DisableAppRequest, DisableAppResponse, DumpFullStateRequest, DumpFullStateResponse, DumpNetworkStatsRequest, DumpNetworkStatsResponse, DumpStateRequest, DumpStateResponse, EnableAppRequest, EnableAppResponse, GenerateAgentPubKeyRequest, GenerateAgentPubKeyResponse, GetCompatibleCellsRequest, GetCompatibleCellsResponse, GetDnaDefinitionRequest, GetDnaDefinitionResponse, GrantZomeCallCapabilityRequest, GrantZomeCallCapabilityResponse, InstallAppRequest, InstallAppResponse, IssueAppAuthenticationTokenRequest, IssueAppAuthenticationTokenResponse, ListAppInterfacesRequest, ListAppInterfacesResponse, ListAppsRequest, ListAppsResponse, ListCellIdsRequest, ListCellIdsResponse, ListDnasRequest, ListDnasResponse, RegisterDnaRequest, RegisterDnaResponse, StorageInfoRequest, StorageInfoResponse, UninstallAppRequest, UninstallAppResponse, UpdateCoordinatorsRequest, UpdateCoordinatorsResponse } from "./types.js";
|
|
5
|
+
import { AddAgentInfoRequest, AddAgentInfoResponse, AdminApi, AgentInfoRequest, AgentInfoResponse, AttachAppInterfaceRequest, AttachAppInterfaceResponse, DeleteCloneCellRequest, DeleteCloneCellResponse, DisableAppRequest, DisableAppResponse, DumpFullStateRequest, DumpFullStateResponse, DumpNetworkStatsRequest, DumpNetworkStatsResponse, DumpStateRequest, DumpStateResponse, EnableAppRequest, EnableAppResponse, GenerateAgentPubKeyRequest, GenerateAgentPubKeyResponse, GetCompatibleCellsRequest, GetCompatibleCellsResponse, GetDnaDefinitionRequest, GetDnaDefinitionResponse, GrantZomeCallCapabilityRequest, GrantZomeCallCapabilityResponse, InstallAppRequest, InstallAppResponse, IssueAppAuthenticationTokenRequest, IssueAppAuthenticationTokenResponse, ListAppInterfacesRequest, ListAppInterfacesResponse, ListAppsRequest, ListAppsResponse, ListCellIdsRequest, ListCellIdsResponse, ListDnasRequest, ListDnasResponse, RegisterDnaRequest, RegisterDnaResponse, RevokeAgentKeyRequest, RevokeAgentKeyResponse, StorageInfoRequest, StorageInfoResponse, UninstallAppRequest, UninstallAppResponse, UpdateCoordinatorsRequest, UpdateCoordinatorsResponse } from "./types.js";
|
|
6
6
|
/**
|
|
7
7
|
* A class for interacting with a conductor's Admin API.
|
|
8
8
|
*
|
|
@@ -51,6 +51,10 @@ export declare class AdminWebsocket implements AdminApi {
|
|
|
51
51
|
* Generate a new agent pub key.
|
|
52
52
|
*/
|
|
53
53
|
generateAgentPubKey: Requester<GenerateAgentPubKeyRequest, GenerateAgentPubKeyResponse>;
|
|
54
|
+
/**
|
|
55
|
+
* Generate a new agent pub key.
|
|
56
|
+
*/
|
|
57
|
+
revokeAgentKey: Requester<RevokeAgentKeyRequest, RevokeAgentKeyResponse>;
|
|
54
58
|
/**
|
|
55
59
|
* Register a DNA for later app installation.
|
|
56
60
|
*
|
|
@@ -68,6 +68,10 @@ export class AdminWebsocket {
|
|
|
68
68
|
* Generate a new agent pub key.
|
|
69
69
|
*/
|
|
70
70
|
generateAgentPubKey = this._requester("generate_agent_pub_key");
|
|
71
|
+
/**
|
|
72
|
+
* Generate a new agent pub key.
|
|
73
|
+
*/
|
|
74
|
+
revokeAgentKey = this._requester("revoke_agent_key");
|
|
71
75
|
/**
|
|
72
76
|
* Register a DNA for later app installation.
|
|
73
77
|
*
|
package/lib/api/app/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UnsubscribeFunction } from "emittery";
|
|
2
|
-
import { AgentPubKey, AppAuthenticationToken, AppInfo, CapSecret, CellId, ClonedCell, DnaHash, DnaProperties, FunctionName, InstalledAppId, MembraneProof, MemproofMap, NetworkInfo, NetworkSeed, Nonce256Bit, RoleName, Timestamp, Transformer, WebsocketConnectionOptions, ZomeName } from "../../index.js";
|
|
2
|
+
import { AgentPubKey, AppAuthenticationToken, AppInfo, CapSecret, CellId, ClonedCell, DnaHash, DnaProperties, EntryHash, FunctionName, InstalledAppId, MembraneProof, MemproofMap, NetworkInfo, NetworkSeed, Nonce256Bit, RoleName, Timestamp, Transformer, WebsocketConnectionOptions, ZomeName } from "../../index.js";
|
|
3
3
|
/**
|
|
4
4
|
* @public
|
|
5
5
|
*/
|
|
@@ -40,7 +40,7 @@ export type AppNetworkInfoRequest = Omit<NetworkInfoRequest, "agent_pub_key">;
|
|
|
40
40
|
* @public
|
|
41
41
|
*/
|
|
42
42
|
export interface AppEvents {
|
|
43
|
-
signal:
|
|
43
|
+
signal: Signal;
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* @public
|
|
@@ -152,7 +152,7 @@ export interface DisableCloneCellRequest {
|
|
|
152
152
|
/**
|
|
153
153
|
* The clone id or cell id of the clone cell
|
|
154
154
|
*/
|
|
155
|
-
clone_cell_id: RoleName |
|
|
155
|
+
clone_cell_id: RoleName | DnaHash;
|
|
156
156
|
}
|
|
157
157
|
/**
|
|
158
158
|
* @public
|
|
@@ -193,10 +193,18 @@ export declare const SignalType: {
|
|
|
193
193
|
/**
|
|
194
194
|
* @public
|
|
195
195
|
*/
|
|
196
|
-
export type
|
|
196
|
+
export type RawSignal = {
|
|
197
197
|
[SignalType.App]: EncodedAppSignal;
|
|
198
198
|
} | {
|
|
199
|
-
[SignalType.System]:
|
|
199
|
+
[SignalType.System]: SystemSignal;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* @public
|
|
203
|
+
*/
|
|
204
|
+
export type Signal = {
|
|
205
|
+
[SignalType.App]: AppSignal;
|
|
206
|
+
} | {
|
|
207
|
+
[SignalType.System]: SystemSignal;
|
|
200
208
|
};
|
|
201
209
|
/**
|
|
202
210
|
* @public
|
|
@@ -217,7 +225,13 @@ export type AppSignal = {
|
|
|
217
225
|
/**
|
|
218
226
|
* @public
|
|
219
227
|
*/
|
|
220
|
-
export type
|
|
228
|
+
export type SystemSignal = {
|
|
229
|
+
SuccessfulCountersigning: EntryHash;
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* @public
|
|
233
|
+
*/
|
|
234
|
+
export type SignalCb = (signal: Signal) => void;
|
|
221
235
|
/**
|
|
222
236
|
* @public
|
|
223
237
|
*/
|
|
@@ -227,7 +241,7 @@ export type NetworkInfoResponse = NetworkInfo[];
|
|
|
227
241
|
*/
|
|
228
242
|
export interface AppClient {
|
|
229
243
|
callZome(args: AppCallZomeRequest, timeout?: number): Promise<any>;
|
|
230
|
-
on<Name extends keyof AppEvents>(eventName: Name | readonly Name[], listener:
|
|
244
|
+
on<Name extends keyof AppEvents>(eventName: Name | readonly Name[], listener: SignalCb): UnsubscribeFunction;
|
|
231
245
|
appInfo(): Promise<AppInfoResponse>;
|
|
232
246
|
myPubKey: AgentPubKey;
|
|
233
247
|
installedAppId: InstalledAppId;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UnsubscribeFunction } from "emittery";
|
|
2
2
|
import { AgentPubKey, CellId, InstalledAppId, RoleName } from "../../types.js";
|
|
3
3
|
import { AppInfo, MemproofMap } from "../admin/index.js";
|
|
4
|
-
import { AppCallZomeRequest, AppClient, AppEvents, AppNetworkInfoRequest, AppCreateCloneCellRequest, AppDisableCloneCellRequest, AppEnableCloneCellRequest,
|
|
4
|
+
import { AppCallZomeRequest, AppClient, AppEvents, AppNetworkInfoRequest, AppCreateCloneCellRequest, AppDisableCloneCellRequest, AppEnableCloneCellRequest, SignalCb, CallZomeRequest, CallZomeRequestSigned, CallZomeResponse, CreateCloneCellResponse, DisableCloneCellResponse, EnableCloneCellResponse, NetworkInfoResponse, AppWebsocketConnectionOptions } from "./types.js";
|
|
5
5
|
import { WsClient } from "../client.js";
|
|
6
6
|
/**
|
|
7
7
|
* A class to establish a websocket connection to an App interface, for a
|
|
@@ -16,6 +16,7 @@ export declare class AppWebsocket implements AppClient {
|
|
|
16
16
|
private readonly defaultTimeout;
|
|
17
17
|
private readonly emitter;
|
|
18
18
|
private readonly callZomeTransform;
|
|
19
|
+
private readonly appAuthenticationToken;
|
|
19
20
|
cachedAppInfo?: AppInfo | null;
|
|
20
21
|
private readonly appInfoRequester;
|
|
21
22
|
private readonly callZomeRequester;
|
|
@@ -101,9 +102,8 @@ export declare class AppWebsocket implements AppClient {
|
|
|
101
102
|
* @param listener - The function to call when event is triggered.
|
|
102
103
|
* @returns A function to unsubscribe the event listener.
|
|
103
104
|
*/
|
|
104
|
-
on<Name extends keyof AppEvents>(eventName: Name | readonly Name[], listener:
|
|
105
|
+
on<Name extends keyof AppEvents>(eventName: Name | readonly Name[], listener: SignalCb): UnsubscribeFunction;
|
|
105
106
|
private static requester;
|
|
106
|
-
private containsCell;
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
109
|
* @public
|
package/lib/api/app/websocket.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Emittery from "emittery";
|
|
2
2
|
import { omit } from "lodash-es";
|
|
3
|
-
import { CellType } from "../admin/index.js";
|
|
3
|
+
import { CellType, } from "../admin/index.js";
|
|
4
4
|
import { catchError, DEFAULT_TIMEOUT, getBaseRoleNameFromCloneId, HolochainError, isCloneId, promiseTimeout, requesterTransformer, } from "../common.js";
|
|
5
5
|
import { getHostZomeCallSigner, getLauncherEnvironment, } from "../../environments/launcher.js";
|
|
6
6
|
import { decode, encode } from "@msgpack/msgpack";
|
|
@@ -22,6 +22,7 @@ export class AppWebsocket {
|
|
|
22
22
|
defaultTimeout;
|
|
23
23
|
emitter;
|
|
24
24
|
callZomeTransform;
|
|
25
|
+
appAuthenticationToken;
|
|
25
26
|
cachedAppInfo;
|
|
26
27
|
appInfoRequester;
|
|
27
28
|
callZomeRequester;
|
|
@@ -31,12 +32,13 @@ export class AppWebsocket {
|
|
|
31
32
|
enableCloneCellRequester;
|
|
32
33
|
disableCloneCellRequester;
|
|
33
34
|
networkInfoRequester;
|
|
34
|
-
constructor(client, appInfo, callZomeTransform, defaultTimeout) {
|
|
35
|
+
constructor(client, appInfo, token, callZomeTransform, defaultTimeout) {
|
|
35
36
|
this.client = client;
|
|
36
37
|
this.myPubKey = appInfo.agent_pub_key;
|
|
37
38
|
this.installedAppId = appInfo.installed_app_id;
|
|
38
39
|
this.defaultTimeout = defaultTimeout ?? DEFAULT_TIMEOUT;
|
|
39
40
|
this.callZomeTransform = callZomeTransform ?? defaultCallZomeTransform;
|
|
41
|
+
this.appAuthenticationToken = token;
|
|
40
42
|
this.emitter = new Emittery();
|
|
41
43
|
this.cachedAppInfo = appInfo;
|
|
42
44
|
this.appInfoRequester = AppWebsocket.requester(this.client, "app_info", this.defaultTimeout);
|
|
@@ -57,9 +59,7 @@ export class AppWebsocket {
|
|
|
57
59
|
}
|
|
58
60
|
});
|
|
59
61
|
this.client.on("signal", (signal) => {
|
|
60
|
-
|
|
61
|
-
this.emitter.emit("signal", signal).catch(console.error);
|
|
62
|
-
}
|
|
62
|
+
this.emitter.emit("signal", signal).catch(console.error);
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
@@ -79,21 +79,15 @@ export class AppWebsocket {
|
|
|
79
79
|
throw new HolochainError("ConnectionUrlMissing", `unable to connect to Conductor API - no url provided and not in a launcher environment.`);
|
|
80
80
|
}
|
|
81
81
|
const client = await WsClient.connect(options.url, options.wsClientOptions);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
if (!options.token) {
|
|
88
|
-
throw new HolochainError("AppAuthenticationTokenMissing", `unable to connect to Conductor API - no app authentication token provided.`);
|
|
89
|
-
}
|
|
90
|
-
await client.authenticate({ token: options.token });
|
|
91
|
-
}
|
|
82
|
+
const token = options.token ?? env?.APP_INTERFACE_TOKEN;
|
|
83
|
+
if (!token)
|
|
84
|
+
throw new HolochainError("AppAuthenticationTokenMissing", `unable to connect to Conductor API - no app authentication token provided.`);
|
|
85
|
+
await client.authenticate({ token });
|
|
92
86
|
const appInfo = await AppWebsocket.requester(client, "app_info", DEFAULT_TIMEOUT)(null);
|
|
93
87
|
if (!appInfo) {
|
|
94
88
|
throw new HolochainError("AppNotFound", `The app your connection token was issued for was not found. The app needs to be installed and enabled.`);
|
|
95
89
|
}
|
|
96
|
-
return new AppWebsocket(client, appInfo, options.callZomeTransform, options.defaultTimeout);
|
|
90
|
+
return new AppWebsocket(client, appInfo, token, options.callZomeTransform, options.defaultTimeout);
|
|
97
91
|
}
|
|
98
92
|
/**
|
|
99
93
|
* Request the app's info, including all cell infos.
|
|
@@ -239,25 +233,6 @@ export class AppWebsocket {
|
|
|
239
233
|
static requester(client, tag, defaultTimeout, transformer) {
|
|
240
234
|
return requesterTransformer((req, timeout) => promiseTimeout(client.request(req), tag, timeout || defaultTimeout).then(catchError), tag, transformer);
|
|
241
235
|
}
|
|
242
|
-
containsCell(cellId) {
|
|
243
|
-
const appInfo = this.cachedAppInfo;
|
|
244
|
-
if (!appInfo) {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
for (const roleName of Object.keys(appInfo.cell_info)) {
|
|
248
|
-
for (const cellInfo of appInfo.cell_info[roleName]) {
|
|
249
|
-
const currentCellId = CellType.Provisioned in cellInfo
|
|
250
|
-
? cellInfo[CellType.Provisioned].cell_id
|
|
251
|
-
: CellType.Cloned in cellInfo
|
|
252
|
-
? cellInfo[CellType.Cloned].cell_id
|
|
253
|
-
: undefined;
|
|
254
|
-
if (currentCellId && isSameCell(currentCellId, cellId)) {
|
|
255
|
-
return true;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
236
|
}
|
|
262
237
|
const defaultCallZomeTransform = {
|
|
263
238
|
input: async (request) => {
|
package/lib/api/client.d.ts
CHANGED
|
@@ -23,8 +23,8 @@ export declare class WsClient extends Emittery {
|
|
|
23
23
|
options: WsClientOptions;
|
|
24
24
|
private pendingRequests;
|
|
25
25
|
private index;
|
|
26
|
+
private authenticationToken;
|
|
26
27
|
constructor(socket: IsoWebSocket, url?: URL, options?: WsClientOptions);
|
|
27
|
-
private setupSocket;
|
|
28
28
|
/**
|
|
29
29
|
* Instance factory for creating WsClients.
|
|
30
30
|
*
|
|
@@ -47,6 +47,10 @@ export declare class WsClient extends Emittery {
|
|
|
47
47
|
* @param request - The authentication request, containing an app authentication token.
|
|
48
48
|
*/
|
|
49
49
|
authenticate(request: AppAuthenticationRequest): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Close the websocket connection.
|
|
52
|
+
*/
|
|
53
|
+
close(code?: number): Promise<IsoWebSocket.CloseEvent>;
|
|
50
54
|
/**
|
|
51
55
|
* Send requests to the connected websocket.
|
|
52
56
|
*
|
|
@@ -56,10 +60,9 @@ export declare class WsClient extends Emittery {
|
|
|
56
60
|
request<Response>(request: unknown): Promise<Response>;
|
|
57
61
|
private exchange;
|
|
58
62
|
private sendMessage;
|
|
63
|
+
private registerMessageListener;
|
|
64
|
+
private registerCloseListener;
|
|
65
|
+
private reconnectWebsocket;
|
|
59
66
|
private handleResponse;
|
|
60
|
-
/**
|
|
61
|
-
* Close the websocket connection.
|
|
62
|
-
*/
|
|
63
|
-
close(code?: number): Promise<CloseEvent>;
|
|
64
67
|
}
|
|
65
68
|
export { IsoWebSocket };
|
package/lib/api/client.js
CHANGED
|
@@ -17,71 +17,16 @@ export class WsClient extends Emittery {
|
|
|
17
17
|
options;
|
|
18
18
|
pendingRequests;
|
|
19
19
|
index;
|
|
20
|
+
authenticationToken;
|
|
20
21
|
constructor(socket, url, options) {
|
|
21
22
|
super();
|
|
23
|
+
this.registerMessageListener(socket);
|
|
24
|
+
this.registerCloseListener(socket);
|
|
22
25
|
this.socket = socket;
|
|
23
26
|
this.url = url;
|
|
24
27
|
this.options = options || {};
|
|
25
28
|
this.pendingRequests = {};
|
|
26
29
|
this.index = 0;
|
|
27
|
-
this.setupSocket();
|
|
28
|
-
}
|
|
29
|
-
setupSocket() {
|
|
30
|
-
this.socket.onmessage = async (serializedMessage) => {
|
|
31
|
-
// If data is not a buffer (nodejs), it will be a blob (browser)
|
|
32
|
-
let deserializedData;
|
|
33
|
-
if (globalThis.window &&
|
|
34
|
-
serializedMessage.data instanceof globalThis.window.Blob) {
|
|
35
|
-
deserializedData = await serializedMessage.data.arrayBuffer();
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
if (typeof Buffer !== "undefined" &&
|
|
39
|
-
Buffer.isBuffer(serializedMessage.data)) {
|
|
40
|
-
deserializedData = serializedMessage.data;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
throw new HolochainError("UnknownMessageFormat", `incoming message has unknown message format - ${deserializedData}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const message = decode(deserializedData);
|
|
47
|
-
assertHolochainMessage(message);
|
|
48
|
-
if (message.type === "signal") {
|
|
49
|
-
if (message.data === null) {
|
|
50
|
-
throw new HolochainError("UnknownSignalFormat", "incoming signal has no data");
|
|
51
|
-
}
|
|
52
|
-
const deserializedSignal = decode(message.data);
|
|
53
|
-
assertHolochainSignal(deserializedSignal);
|
|
54
|
-
if (SignalType.System in deserializedSignal) {
|
|
55
|
-
// We have received a system signal, do nothing
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const encodedAppSignal = deserializedSignal[SignalType.App];
|
|
59
|
-
// In order to return readable content to the UI, the signal payload must also be deserialized.
|
|
60
|
-
const payload = decode(encodedAppSignal.signal);
|
|
61
|
-
const signal = {
|
|
62
|
-
cell_id: encodedAppSignal.cell_id,
|
|
63
|
-
zome_name: encodedAppSignal.zome_name,
|
|
64
|
-
payload,
|
|
65
|
-
};
|
|
66
|
-
this.emit("signal", signal);
|
|
67
|
-
}
|
|
68
|
-
else if (message.type === "response") {
|
|
69
|
-
this.handleResponse(message);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
throw new HolochainError("UnknownMessageType", `incoming message has unknown type - ${message.type}`);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
this.socket.onclose = (event) => {
|
|
76
|
-
const pendingRequestIds = Object.keys(this.pendingRequests).map((id) => parseInt(id));
|
|
77
|
-
if (pendingRequestIds.length) {
|
|
78
|
-
pendingRequestIds.forEach((id) => {
|
|
79
|
-
const error = new HolochainError("ClientClosedWithPendingRequests", `client closed with pending requests - close event code: ${event.code}, request id: ${id}`);
|
|
80
|
-
this.pendingRequests[id].reject(error);
|
|
81
|
-
delete this.pendingRequests[id];
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
30
|
}
|
|
86
31
|
/**
|
|
87
32
|
* Instance factory for creating WsClients.
|
|
@@ -93,13 +38,13 @@ export class WsClient extends Emittery {
|
|
|
93
38
|
static connect(url, options) {
|
|
94
39
|
return new Promise((resolve, reject) => {
|
|
95
40
|
const socket = new IsoWebSocket(url, options);
|
|
96
|
-
socket.
|
|
41
|
+
socket.addEventListener("error", (errorEvent) => {
|
|
97
42
|
reject(new HolochainError("ConnectionError", `could not connect to Holochain Conductor API at ${url} - ${errorEvent.error}`));
|
|
98
|
-
};
|
|
99
|
-
socket.
|
|
43
|
+
});
|
|
44
|
+
socket.addEventListener("open", (_) => {
|
|
100
45
|
const client = new WsClient(socket, url, options);
|
|
101
46
|
resolve(client);
|
|
102
|
-
};
|
|
47
|
+
}, { once: true });
|
|
103
48
|
});
|
|
104
49
|
}
|
|
105
50
|
/**
|
|
@@ -122,16 +67,35 @@ export class WsClient extends Emittery {
|
|
|
122
67
|
* @param request - The authentication request, containing an app authentication token.
|
|
123
68
|
*/
|
|
124
69
|
async authenticate(request) {
|
|
125
|
-
|
|
70
|
+
this.authenticationToken = request.token;
|
|
71
|
+
return this.exchange(request, (request, resolve, reject) => {
|
|
72
|
+
const invalidTokenCloseListener = (closeEvent) => {
|
|
73
|
+
this.authenticationToken = undefined;
|
|
74
|
+
reject(new HolochainError("InvalidTokenError", `could not connect to ${this.url} due to an invalid app authentication token - close code ${closeEvent.code}`));
|
|
75
|
+
};
|
|
76
|
+
this.socket.addEventListener("close", invalidTokenCloseListener, {
|
|
77
|
+
once: true,
|
|
78
|
+
});
|
|
126
79
|
const encodedMsg = encode({
|
|
127
80
|
type: "authenticate",
|
|
128
81
|
data: encode(request),
|
|
129
82
|
});
|
|
130
83
|
this.socket.send(encodedMsg);
|
|
131
|
-
//
|
|
132
|
-
|
|
84
|
+
// Wait before resolving in case authentication fails.
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
this.socket.removeEventListener("close", invalidTokenCloseListener);
|
|
87
|
+
resolve(null);
|
|
88
|
+
}, 10);
|
|
133
89
|
});
|
|
134
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Close the websocket connection.
|
|
93
|
+
*/
|
|
94
|
+
close(code = 1000) {
|
|
95
|
+
const closedPromise = new Promise((resolve) => this.socket.addEventListener("close", (closeEvent) => resolve(closeEvent), { once: true }));
|
|
96
|
+
this.socket.close(code);
|
|
97
|
+
return closedPromise;
|
|
98
|
+
}
|
|
135
99
|
/**
|
|
136
100
|
* Send requests to the connected websocket.
|
|
137
101
|
*
|
|
@@ -141,15 +105,22 @@ export class WsClient extends Emittery {
|
|
|
141
105
|
async request(request) {
|
|
142
106
|
return this.exchange(request, this.sendMessage.bind(this));
|
|
143
107
|
}
|
|
144
|
-
exchange(request, sendHandler) {
|
|
108
|
+
async exchange(request, sendHandler) {
|
|
145
109
|
if (this.socket.readyState === this.socket.OPEN) {
|
|
146
110
|
const promise = new Promise((resolve, reject) => {
|
|
147
111
|
sendHandler(request, resolve, reject);
|
|
148
112
|
});
|
|
149
113
|
return promise;
|
|
150
114
|
}
|
|
115
|
+
else if (this.url && this.authenticationToken) {
|
|
116
|
+
await this.reconnectWebsocket(this.url, this.authenticationToken);
|
|
117
|
+
this.registerMessageListener(this.socket);
|
|
118
|
+
this.registerCloseListener(this.socket);
|
|
119
|
+
const promise = new Promise((resolve, reject) => sendHandler(request, resolve, reject));
|
|
120
|
+
return promise;
|
|
121
|
+
}
|
|
151
122
|
else {
|
|
152
|
-
return Promise.reject(new
|
|
123
|
+
return Promise.reject(new HolochainError("WebsocketClosedError", "Websocket is not open"));
|
|
153
124
|
}
|
|
154
125
|
}
|
|
155
126
|
sendMessage(request, resolve, reject) {
|
|
@@ -163,6 +134,97 @@ export class WsClient extends Emittery {
|
|
|
163
134
|
this.pendingRequests[id] = { resolve, reject };
|
|
164
135
|
this.index += 1;
|
|
165
136
|
}
|
|
137
|
+
registerMessageListener(socket) {
|
|
138
|
+
socket.onmessage = async (serializedMessage) => {
|
|
139
|
+
// If data is not a buffer (nodejs), it will be a blob (browser)
|
|
140
|
+
let deserializedData;
|
|
141
|
+
if (globalThis.window &&
|
|
142
|
+
serializedMessage.data instanceof globalThis.window.Blob) {
|
|
143
|
+
deserializedData = await serializedMessage.data.arrayBuffer();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
if (typeof Buffer !== "undefined" &&
|
|
147
|
+
Buffer.isBuffer(serializedMessage.data)) {
|
|
148
|
+
deserializedData = serializedMessage.data;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
throw new HolochainError("UnknownMessageFormat", `incoming message has unknown message format - ${deserializedData}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const message = decode(deserializedData);
|
|
155
|
+
assertHolochainMessage(message);
|
|
156
|
+
if (message.type === "signal") {
|
|
157
|
+
if (message.data === null) {
|
|
158
|
+
throw new HolochainError("UnknownSignalFormat", "incoming signal has no data");
|
|
159
|
+
}
|
|
160
|
+
const deserializedSignal = decode(message.data);
|
|
161
|
+
assertHolochainSignal(deserializedSignal);
|
|
162
|
+
if (SignalType.System in deserializedSignal) {
|
|
163
|
+
this.emit("signal", {
|
|
164
|
+
System: deserializedSignal[SignalType.System],
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const encodedAppSignal = deserializedSignal[SignalType.App];
|
|
169
|
+
// In order to return readable content to the UI, the signal payload must also be deserialized.
|
|
170
|
+
const payload = decode(encodedAppSignal.signal);
|
|
171
|
+
const signal = {
|
|
172
|
+
cell_id: encodedAppSignal.cell_id,
|
|
173
|
+
zome_name: encodedAppSignal.zome_name,
|
|
174
|
+
payload,
|
|
175
|
+
};
|
|
176
|
+
this.emit("signal", { App: signal });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else if (message.type === "response") {
|
|
180
|
+
this.handleResponse(message);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
throw new HolochainError("UnknownMessageType", `incoming message has unknown type - ${message.type}`);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
registerCloseListener(socket) {
|
|
188
|
+
socket.addEventListener("close", (closeEvent) => {
|
|
189
|
+
const pendingRequestIds = Object.keys(this.pendingRequests).map((id) => parseInt(id));
|
|
190
|
+
if (pendingRequestIds.length) {
|
|
191
|
+
pendingRequestIds.forEach((id) => {
|
|
192
|
+
const error = new HolochainError("ClientClosedWithPendingRequests", `client closed with pending requests - close event code: ${closeEvent.code}, request id: ${id}`);
|
|
193
|
+
this.pendingRequests[id].reject(error);
|
|
194
|
+
delete this.pendingRequests[id];
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}, { once: true });
|
|
198
|
+
}
|
|
199
|
+
async reconnectWebsocket(url, token) {
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
this.socket = new IsoWebSocket(url, this.options);
|
|
202
|
+
// This error event never occurs in tests. Could be removed?
|
|
203
|
+
this.socket.addEventListener("error", (errorEvent) => {
|
|
204
|
+
this.authenticationToken = undefined;
|
|
205
|
+
reject(new HolochainError("ConnectionError", `could not connect to Holochain Conductor API at ${url} - ${errorEvent.message}`));
|
|
206
|
+
}, { once: true });
|
|
207
|
+
const invalidTokenCloseListener = (closeEvent) => {
|
|
208
|
+
this.authenticationToken = undefined;
|
|
209
|
+
reject(new HolochainError("InvalidTokenError", `could not connect to ${this.url} due to an invalid app authentication token - close code ${closeEvent.code}`));
|
|
210
|
+
};
|
|
211
|
+
this.socket.addEventListener("close", invalidTokenCloseListener, {
|
|
212
|
+
once: true,
|
|
213
|
+
});
|
|
214
|
+
this.socket.addEventListener("open", async (_) => {
|
|
215
|
+
const encodedMsg = encode({
|
|
216
|
+
type: "authenticate",
|
|
217
|
+
data: encode({ token }),
|
|
218
|
+
});
|
|
219
|
+
this.socket.send(encodedMsg);
|
|
220
|
+
// Wait in case authentication fails.
|
|
221
|
+
setTimeout(() => {
|
|
222
|
+
this.socket.removeEventListener("close", invalidTokenCloseListener);
|
|
223
|
+
resolve();
|
|
224
|
+
}, 10);
|
|
225
|
+
}, { once: true });
|
|
226
|
+
});
|
|
227
|
+
}
|
|
166
228
|
handleResponse(msg) {
|
|
167
229
|
const id = msg.id;
|
|
168
230
|
if (this.pendingRequests[id]) {
|
|
@@ -178,20 +240,6 @@ export class WsClient extends Emittery {
|
|
|
178
240
|
console.error(`got response with no matching request. id = ${id} msg = ${msg}`);
|
|
179
241
|
}
|
|
180
242
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Close the websocket connection.
|
|
183
|
-
*/
|
|
184
|
-
close(code = 1000) {
|
|
185
|
-
const closedPromise = new Promise((resolve) =>
|
|
186
|
-
// for an unknown reason "addEventListener" is seen as a non-callable
|
|
187
|
-
// property and gives a ts2349 error
|
|
188
|
-
// type assertion as workaround
|
|
189
|
-
this.socket.addEventListener("close", (event) => resolve(event))
|
|
190
|
-
// }
|
|
191
|
-
);
|
|
192
|
-
this.socket.close(code);
|
|
193
|
-
return closedPromise;
|
|
194
|
-
}
|
|
195
243
|
}
|
|
196
244
|
function assertHolochainMessage(message) {
|
|
197
245
|
if (typeof message === "object" &&
|
package/lib/api/common.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type KeyPair } from "libsodium-wrappers";
|
|
2
2
|
import type { CapSecret } from "../hdk/capabilities.js";
|
|
3
|
-
import type { CellId } from "../types.js";
|
|
4
|
-
import { AgentPubKey } from "../types.js";
|
|
3
|
+
import type { AgentPubKey, CellId } from "../types.js";
|
|
5
4
|
/**
|
|
6
5
|
* @public
|
|
7
6
|
*/
|
|
@@ -34,14 +33,13 @@ export declare const setSigningCredentials: (cellId: CellId, credentials: Signin
|
|
|
34
33
|
/**
|
|
35
34
|
* Generates a key pair for signing zome calls.
|
|
36
35
|
*
|
|
36
|
+
* @param agentPubKey - The agent pub key to take 4 last bytes (= DHT location)
|
|
37
|
+
* from (optional).
|
|
37
38
|
* @returns The signing key pair and an agent pub key based on the public key.
|
|
38
39
|
*
|
|
39
40
|
* @public
|
|
40
41
|
*/
|
|
41
|
-
export declare const generateSigningKeyPair: () => Promise<[
|
|
42
|
-
KeyPair,
|
|
43
|
-
AgentPubKey
|
|
44
|
-
]>;
|
|
42
|
+
export declare const generateSigningKeyPair: (agentPubKey?: AgentPubKey) => Promise<[KeyPair, AgentPubKey]>;
|
|
45
43
|
/**
|
|
46
44
|
* @public
|
|
47
45
|
*/
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import _sodium from "libsodium-wrappers";
|
|
2
|
-
import { AgentPubKey } from "../types.js";
|
|
3
2
|
import { encodeHashToBase64 } from "../utils/base64.js";
|
|
4
3
|
const signingCredentials = new Map();
|
|
5
4
|
/**
|
|
@@ -28,15 +27,18 @@ export const setSigningCredentials = (cellId, credentials) => {
|
|
|
28
27
|
/**
|
|
29
28
|
* Generates a key pair for signing zome calls.
|
|
30
29
|
*
|
|
30
|
+
* @param agentPubKey - The agent pub key to take 4 last bytes (= DHT location)
|
|
31
|
+
* from (optional).
|
|
31
32
|
* @returns The signing key pair and an agent pub key based on the public key.
|
|
32
33
|
*
|
|
33
34
|
* @public
|
|
34
35
|
*/
|
|
35
|
-
export const generateSigningKeyPair = async () => {
|
|
36
|
+
export const generateSigningKeyPair = async (agentPubKey) => {
|
|
36
37
|
await _sodium.ready;
|
|
37
38
|
const sodium = _sodium;
|
|
38
39
|
const keyPair = sodium.crypto_sign_keypair();
|
|
39
|
-
const
|
|
40
|
+
const locationBytes = agentPubKey ? agentPubKey.subarray(35) : [0, 0, 0, 0];
|
|
41
|
+
const signingKey = new Uint8Array([132, 32, 36].concat(...keyPair.publicKey).concat(...locationBytes));
|
|
40
42
|
return [keyPair, signingKey];
|
|
41
43
|
};
|
|
42
44
|
/**
|
|
@@ -18,6 +18,23 @@ export interface PreflightRequest {
|
|
|
18
18
|
action_base: ActionBase;
|
|
19
19
|
preflight_bytes: PreflightBytes;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export interface PreflightResponse {
|
|
25
|
+
/**
|
|
26
|
+
* The request associated with this response.
|
|
27
|
+
*/
|
|
28
|
+
request: PreflightRequest;
|
|
29
|
+
/**
|
|
30
|
+
* The chain state declaration for the agent that produced this response.
|
|
31
|
+
*/
|
|
32
|
+
agent_state: CountersigningAgentState;
|
|
33
|
+
/**
|
|
34
|
+
* The signature of this response, by the agent that created it.
|
|
35
|
+
*/
|
|
36
|
+
signature: Signature;
|
|
37
|
+
}
|
|
21
38
|
/**
|
|
22
39
|
* @public
|
|
23
40
|
*/
|
package/lib/hdk/link.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { ActionHash, AgentPubKey,
|
|
1
|
+
import { ActionHash, AgentPubKey, EntryHash, ExternalHash, Timestamp } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
export type AnyLinkableHash = EntryHash | ActionHash | ExternalHash;
|
|
2
6
|
/**
|
|
3
7
|
* An internal zome index within the DNA, from 0 to 255.
|
|
4
8
|
*
|
package/lib/tsdoc-metadata.json
CHANGED
package/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
-
import { HoloHash, AgentPubKey, DnaHash, WasmHash, EntryHash, ActionHash, AnyDhtHash, AnyLinkableHash, ExternalHash } from "@spartan-hc/holo-hash";
|
|
2
1
|
/**
|
|
3
2
|
* @public
|
|
4
3
|
*/
|
|
5
|
-
export
|
|
4
|
+
export type HoloHash = Uint8Array;
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export type AgentPubKey = HoloHash;
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export type DnaHash = HoloHash;
|
|
13
|
+
/**
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export type WasmHash = HoloHash;
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type EntryHash = HoloHash;
|
|
21
|
+
/**
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export type ActionHash = HoloHash;
|
|
25
|
+
/**
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export type AnyDhtHash = HoloHash;
|
|
29
|
+
/**
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export type ExternalHash = HoloHash;
|
|
6
33
|
/**
|
|
7
34
|
* @public
|
|
8
35
|
*/
|
package/lib/types.js
CHANGED
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @public
|
|
4
|
-
*/
|
|
5
|
-
export { HoloHash, AgentPubKey, DnaHash, WasmHash, EntryHash, ActionHash, AnyDhtHash, AnyLinkableHash, ExternalHash, };
|
|
1
|
+
export {};
|
package/lib/utils/base64.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { HoloHashB64 } from "../types.js";
|
|
2
|
-
import { HoloHash } from "@spartan-hc/holo-hash";
|
|
1
|
+
import { HoloHash, HoloHashB64 } from "../types.js";
|
|
3
2
|
/**
|
|
4
3
|
* Decodes a Base64 encoded string to a byte array hash.
|
|
5
4
|
*
|
|
@@ -8,7 +7,7 @@ import { HoloHash } from "@spartan-hc/holo-hash";
|
|
|
8
7
|
*
|
|
9
8
|
* @public
|
|
10
9
|
*/
|
|
11
|
-
export declare function decodeHashFromBase64(hash:
|
|
10
|
+
export declare function decodeHashFromBase64(hash: HoloHashB64): HoloHash;
|
|
12
11
|
/**
|
|
13
12
|
* Encode a byte array hash to a Base64 string.
|
|
14
13
|
*
|
|
@@ -17,4 +16,4 @@ export declare function decodeHashFromBase64(hash: string): HoloHash;
|
|
|
17
16
|
*
|
|
18
17
|
* @public
|
|
19
18
|
*/
|
|
20
|
-
export declare function encodeHashToBase64(hash:
|
|
19
|
+
export declare function encodeHashToBase64(hash: HoloHash): HoloHashB64;
|
package/lib/utils/base64.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Base64 } from "js-base64";
|
|
2
2
|
/**
|
|
3
3
|
* Decodes a Base64 encoded string to a byte array hash.
|
|
4
4
|
*
|
|
@@ -8,7 +8,7 @@ import { HoloHash } from "@spartan-hc/holo-hash";
|
|
|
8
8
|
* @public
|
|
9
9
|
*/
|
|
10
10
|
export function decodeHashFromBase64(hash) {
|
|
11
|
-
return
|
|
11
|
+
return Base64.toUint8Array(hash.slice(1));
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Encode a byte array hash to a Base64 string.
|
|
@@ -19,5 +19,5 @@ export function decodeHashFromBase64(hash) {
|
|
|
19
19
|
* @public
|
|
20
20
|
*/
|
|
21
21
|
export function encodeHashToBase64(hash) {
|
|
22
|
-
return
|
|
22
|
+
return `u${Base64.fromUint8Array(hash, true)}`;
|
|
23
23
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CellId } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Check if two cell ids are identical.
|
|
4
|
+
*
|
|
5
|
+
* @param cellId1 - Cell id 1 to compare.
|
|
6
|
+
* @param cellId2 - Cell id 1 to compare.
|
|
7
|
+
* @returns True if the cell ids are identical.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export declare const isSameCell: (cellId1: CellId, cellId2: CellId) => boolean;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { encodeHashToBase64 } from "./base64.js";
|
|
2
|
+
/**
|
|
3
|
+
* Check if two cell ids are identical.
|
|
4
|
+
*
|
|
5
|
+
* @param cellId1 - Cell id 1 to compare.
|
|
6
|
+
* @param cellId2 - Cell id 1 to compare.
|
|
7
|
+
* @returns True if the cell ids are identical.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export const isSameCell = (cellId1, cellId2) => {
|
|
12
|
+
const dnaHashB64_1 = encodeHashToBase64(cellId1[0]);
|
|
13
|
+
const agentPubKeyB64_1 = encodeHashToBase64(cellId1[1]);
|
|
14
|
+
const dnaHashB64_2 = encodeHashToBase64(cellId2[0]);
|
|
15
|
+
const agentPubKeyB64_2 = encodeHashToBase64(cellId2[1]);
|
|
16
|
+
return dnaHashB64_1 === dnaHashB64_2 && agentPubKeyB64_1 === agentPubKeyB64_2;
|
|
17
|
+
};
|
package/lib/utils/fake-hash.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { DnaHash, ActionHash, AgentPubKey, EntryHash } from "../types.js";
|
|
|
5
5
|
* From https://github.com/holochain/holochain/blob/develop/crates/holo_hash/src/hash_type/primitive.rs
|
|
6
6
|
*
|
|
7
7
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
8
|
-
* @returns An
|
|
8
|
+
* @returns An {@link EntryHash}.
|
|
9
9
|
*
|
|
10
10
|
* @public
|
|
11
11
|
*/
|
|
@@ -14,7 +14,7 @@ export declare function fakeEntryHash(coreByte?: number | undefined): Promise<En
|
|
|
14
14
|
* Generate a valid agent key of a non-existing agent.
|
|
15
15
|
*
|
|
16
16
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
17
|
-
* @returns An
|
|
17
|
+
* @returns An {@link AgentPubKey}.
|
|
18
18
|
*
|
|
19
19
|
* @public
|
|
20
20
|
*/
|
|
@@ -23,7 +23,7 @@ export declare function fakeAgentPubKey(coreByte?: number | undefined): Promise<
|
|
|
23
23
|
* Generate a valid hash of a non-existing action.
|
|
24
24
|
*
|
|
25
25
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
26
|
-
* @returns An
|
|
26
|
+
* @returns An {@link ActionHash}.
|
|
27
27
|
*
|
|
28
28
|
* @public
|
|
29
29
|
*/
|
|
@@ -32,7 +32,7 @@ export declare function fakeActionHash(coreByte?: number | undefined): Promise<A
|
|
|
32
32
|
* Generate a valid hash of a non-existing DNA.
|
|
33
33
|
*
|
|
34
34
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
35
|
-
* @returns A
|
|
35
|
+
* @returns A {@link DnaHash}.
|
|
36
36
|
*
|
|
37
37
|
* @public
|
|
38
38
|
*/
|
package/lib/utils/fake-hash.js
CHANGED
|
@@ -1,57 +1,60 @@
|
|
|
1
1
|
import { range } from "lodash-es";
|
|
2
2
|
import { randomByteArray } from "../api/zome-call-signing.js";
|
|
3
|
-
import {
|
|
3
|
+
import { dhtLocationFrom32 } from "./hash-parts.js";
|
|
4
|
+
async function fakeValidHash(prefix, coreByte) {
|
|
5
|
+
let core;
|
|
6
|
+
if (coreByte === undefined) {
|
|
7
|
+
core = await randomByteArray(32);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
core = Uint8Array.from(range(0, 32).map(() => coreByte));
|
|
11
|
+
}
|
|
12
|
+
const checksum = dhtLocationFrom32(core);
|
|
13
|
+
return new Uint8Array([...prefix, ...core, ...Array.from(checksum)]);
|
|
14
|
+
}
|
|
4
15
|
/**
|
|
5
16
|
* Generate a valid hash of a non-existing entry.
|
|
6
17
|
*
|
|
7
18
|
* From https://github.com/holochain/holochain/blob/develop/crates/holo_hash/src/hash_type/primitive.rs
|
|
8
19
|
*
|
|
9
20
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
10
|
-
* @returns An
|
|
21
|
+
* @returns An {@link EntryHash}.
|
|
11
22
|
*
|
|
12
23
|
* @public
|
|
13
24
|
*/
|
|
14
25
|
export async function fakeEntryHash(coreByte = undefined) {
|
|
15
|
-
return
|
|
16
|
-
? Uint8Array.from(range(0, 32).map(() => coreByte))
|
|
17
|
-
: await randomByteArray(32));
|
|
26
|
+
return fakeValidHash([0x84, 0x21, 0x24], coreByte);
|
|
18
27
|
}
|
|
19
28
|
/**
|
|
20
29
|
* Generate a valid agent key of a non-existing agent.
|
|
21
30
|
*
|
|
22
31
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
23
|
-
* @returns An
|
|
32
|
+
* @returns An {@link AgentPubKey}.
|
|
24
33
|
*
|
|
25
34
|
* @public
|
|
26
35
|
*/
|
|
27
36
|
export async function fakeAgentPubKey(coreByte = undefined) {
|
|
28
|
-
return
|
|
29
|
-
? Uint8Array.from(range(0, 32).map(() => coreByte))
|
|
30
|
-
: await randomByteArray(32));
|
|
37
|
+
return fakeValidHash([0x84, 0x20, 0x24], coreByte);
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
40
|
* Generate a valid hash of a non-existing action.
|
|
34
41
|
*
|
|
35
42
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
36
|
-
* @returns An
|
|
43
|
+
* @returns An {@link ActionHash}.
|
|
37
44
|
*
|
|
38
45
|
* @public
|
|
39
46
|
*/
|
|
40
47
|
export async function fakeActionHash(coreByte = undefined) {
|
|
41
|
-
return
|
|
42
|
-
? Uint8Array.from(range(0, 32).map(() => coreByte))
|
|
43
|
-
: await randomByteArray(32));
|
|
48
|
+
return fakeValidHash([0x84, 0x29, 0x24], coreByte);
|
|
44
49
|
}
|
|
45
50
|
/**
|
|
46
51
|
* Generate a valid hash of a non-existing DNA.
|
|
47
52
|
*
|
|
48
53
|
* @param coreByte - Optionally specify a byte to repeat for all core 32 bytes. If undefined will generate random core 32 bytes.
|
|
49
|
-
* @returns A
|
|
54
|
+
* @returns A {@link DnaHash}.
|
|
50
55
|
*
|
|
51
56
|
* @public
|
|
52
57
|
*/
|
|
53
58
|
export async function fakeDnaHash(coreByte = undefined) {
|
|
54
|
-
return
|
|
55
|
-
? Uint8Array.from(range(0, 32).map(() => coreByte))
|
|
56
|
-
: await randomByteArray(32));
|
|
59
|
+
return fakeValidHash([0x84, 0x2d, 0x24], coreByte);
|
|
57
60
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ActionHash, AgentPubKey, EntryHash } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Hash type labels and their 3 byte values (forming the first 3 bytes of hash).
|
|
4
4
|
*
|
|
@@ -23,7 +23,7 @@ export declare const HASH_TYPE_PREFIX: {
|
|
|
23
23
|
*
|
|
24
24
|
* @public
|
|
25
25
|
*/
|
|
26
|
-
export declare function sliceHashType(hash:
|
|
26
|
+
export declare function sliceHashType(hash: AgentPubKey | EntryHash | ActionHash): Uint8Array;
|
|
27
27
|
/**
|
|
28
28
|
* Get core hash from a Holochain hash (32 bytes).
|
|
29
29
|
*
|
|
@@ -34,7 +34,7 @@ export declare function sliceHashType(hash: HoloHash | Uint8Array): Uint8Array;
|
|
|
34
34
|
*
|
|
35
35
|
* @public
|
|
36
36
|
*/
|
|
37
|
-
export declare function sliceCore32(hash:
|
|
37
|
+
export declare function sliceCore32(hash: AgentPubKey | EntryHash | ActionHash): Uint8Array;
|
|
38
38
|
/**
|
|
39
39
|
* Get DHT location (last 4 bytes) from a hash.
|
|
40
40
|
*
|
|
@@ -45,7 +45,7 @@ export declare function sliceCore32(hash: HoloHash | Uint8Array): Uint8Array;
|
|
|
45
45
|
*
|
|
46
46
|
* @public
|
|
47
47
|
*/
|
|
48
|
-
export declare function sliceDhtLocation(hash:
|
|
48
|
+
export declare function sliceDhtLocation(hash: AgentPubKey | EntryHash | ActionHash): Uint8Array;
|
|
49
49
|
/**
|
|
50
50
|
* Generate DHT location (last 4 bytes) from a core hash (middle 32 bytes).
|
|
51
51
|
*
|
|
@@ -68,4 +68,4 @@ export declare function dhtLocationFrom32(hashCore: Uint8Array): Uint8Array;
|
|
|
68
68
|
*
|
|
69
69
|
* @public
|
|
70
70
|
*/
|
|
71
|
-
export declare function hashFrom32AndType(hashCore:
|
|
71
|
+
export declare function hashFrom32AndType(hashCore: AgentPubKey | EntryHash | ActionHash, hashType: "Agent" | "Entry" | "Dna" | "Action" | "External"): Uint8Array;
|
package/lib/utils/hash-parts.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import blake2b from "@bitgo/blake2b";
|
|
2
|
-
import { HoloHash } from "../types.js";
|
|
3
2
|
const HASH_TYPE_START = 0;
|
|
4
3
|
const HASH_TYPE_BYTE_LENGTH = 3;
|
|
5
4
|
const CORE_HASH_BYTE_LENGTH = 32;
|
|
@@ -29,9 +28,7 @@ export const HASH_TYPE_PREFIX = {
|
|
|
29
28
|
* @public
|
|
30
29
|
*/
|
|
31
30
|
export function sliceHashType(hash) {
|
|
32
|
-
|
|
33
|
-
hash = new HoloHash(hash);
|
|
34
|
-
return hash.getPrefix();
|
|
31
|
+
return Uint8Array.from(hash.slice(0, 3));
|
|
35
32
|
}
|
|
36
33
|
/**
|
|
37
34
|
* Get core hash from a Holochain hash (32 bytes).
|
|
@@ -44,11 +41,9 @@ export function sliceHashType(hash) {
|
|
|
44
41
|
* @public
|
|
45
42
|
*/
|
|
46
43
|
export function sliceCore32(hash) {
|
|
47
|
-
if (!(hash instanceof HoloHash))
|
|
48
|
-
hash = new HoloHash(hash);
|
|
49
44
|
const start = HASH_TYPE_START + HASH_TYPE_BYTE_LENGTH;
|
|
50
45
|
const end = start + CORE_HASH_BYTE_LENGTH;
|
|
51
|
-
return hash.
|
|
46
|
+
return Uint8Array.from(hash.slice(start, end));
|
|
52
47
|
}
|
|
53
48
|
/**
|
|
54
49
|
* Get DHT location (last 4 bytes) from a hash.
|
|
@@ -61,11 +56,9 @@ export function sliceCore32(hash) {
|
|
|
61
56
|
* @public
|
|
62
57
|
*/
|
|
63
58
|
export function sliceDhtLocation(hash) {
|
|
64
|
-
if (hash instanceof Uint8Array)
|
|
65
|
-
hash = new HoloHash(hash);
|
|
66
59
|
const start = HASH_TYPE_START + HASH_TYPE_BYTE_LENGTH + CORE_HASH_BYTE_LENGTH;
|
|
67
60
|
const end = start + DHT_LOCATION_BYTE_LENGTH;
|
|
68
|
-
return hash.
|
|
61
|
+
return Uint8Array.from(hash.slice(start, end));
|
|
69
62
|
}
|
|
70
63
|
/**
|
|
71
64
|
* Generate DHT location (last 4 bytes) from a core hash (middle 32 bytes).
|
|
@@ -101,5 +94,9 @@ export function dhtLocationFrom32(hashCore) {
|
|
|
101
94
|
* @public
|
|
102
95
|
*/
|
|
103
96
|
export function hashFrom32AndType(hashCore, hashType) {
|
|
104
|
-
return
|
|
97
|
+
return Uint8Array.from([
|
|
98
|
+
...HASH_TYPE_PREFIX[hashType],
|
|
99
|
+
...hashCore,
|
|
100
|
+
...dhtLocationFrom32(hashCore),
|
|
101
|
+
]);
|
|
105
102
|
}
|
package/lib/utils/index.d.ts
CHANGED
package/lib/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holochain/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0-dev.1",
|
|
4
4
|
"description": "A JavaScript client for the Holochain Conductor API",
|
|
5
5
|
"author": "Holochain Foundation <info@holochain.org> (https://holochain.org)",
|
|
6
6
|
"license": "CAL-1.0",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"@bitgo/blake2b": "^3.2.4",
|
|
46
46
|
"@holochain/serialization": "^0.1.0-beta-rc.3",
|
|
47
47
|
"@msgpack/msgpack": "^2.8.0",
|
|
48
|
-
"@spartan-hc/holo-hash": "^0.7.0",
|
|
49
48
|
"emittery": "^1.0.1",
|
|
50
49
|
"isomorphic-ws": "^5.0.0",
|
|
51
50
|
"js-base64": "^3.7.5",
|
|
@@ -73,4 +72,4 @@
|
|
|
73
72
|
"tsx": "^4.7.2",
|
|
74
73
|
"typescript": "^4.9.5"
|
|
75
74
|
}
|
|
76
|
-
}
|
|
75
|
+
}
|