@holochain/hc-spin 0.500.1 → 0.500.2
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/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/dist/main/index.js +45 -13
- package/package.json +5 -5
- package/src/main/index.ts +12 -4
- package/src/main/windows.ts +14 -13
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
9
9
|
### Changed
|
|
10
10
|
### Removed
|
|
11
11
|
|
|
12
|
+
## 2025-07-09: v0.500.2
|
|
13
|
+
### Fixed
|
|
14
|
+
- Fixes issue [#31](https://github.com/holochain/hc-spin/issues/30) that made initial zome calls fail in cases where the UI loaded faster than the zome call signing logic was ready (PR [#31](https://github.com/holochain/hc-spin/pull/31))
|
|
15
|
+
|
|
12
16
|
## 2025-04-30: v0.500.1
|
|
13
17
|
### Fixed
|
|
14
18
|
- Fixed an error that could prevent hc-spin to start up properly (`ERROR: error: invalid value 'undefined' for '--bootstrap <BOOTSTRAP>': relative URL without a base`) ([#25](https://github.com/holochain/hc-spin/pull/25))
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ npm install --save-dev @holochain/hc-spin@">=0.500.0 <0.600.0"
|
|
|
25
25
|
```
|
|
26
26
|
Usage: hc-spin [options] <path>
|
|
27
27
|
|
|
28
|
-
CLI to run Holochain
|
|
28
|
+
CLI to run Holochain apps during development.
|
|
29
29
|
|
|
30
30
|
Arguments:
|
|
31
31
|
path Path to .webhapp or .happ file to launch. If a .happ file is passed, either a UI path must be specified via
|
package/dist/main/index.js
CHANGED
|
@@ -62,7 +62,7 @@ function nanoid(size = 21) {
|
|
|
62
62
|
}
|
|
63
63
|
return id;
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
async function createHappWindow(uiSource, appId, agentNum, appPort, appAuthToken, appDataRootDir) {
|
|
66
66
|
if (!appPort) throw new Error("App port not defined.");
|
|
67
67
|
const partition = `persist:${agentNum}:${appId}`;
|
|
68
68
|
if (uiSource.type === "path") {
|
|
@@ -107,7 +107,7 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
107
107
|
console.error("Failed to get icon.png: ", e);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
return new electron.BrowserWindow({
|
|
111
111
|
width: 1200,
|
|
112
112
|
height: 800,
|
|
113
113
|
show: false,
|
|
@@ -118,6 +118,8 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
118
118
|
partition
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
|
+
}
|
|
122
|
+
async function loadHappWindow(happWindow, uiSource, happOrWebhappPath, agentNum, openDevtools) {
|
|
121
123
|
const [windowPositionX, windowPositionY] = happWindow.getPosition();
|
|
122
124
|
const windowPositionXMoved = windowPositionX + agentNum * 20;
|
|
123
125
|
const windowPositionYMoved = windowPositionY + agentNum * 20;
|
|
@@ -138,8 +140,7 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
138
140
|
} else {
|
|
139
141
|
happWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
|
|
140
142
|
}
|
|
141
|
-
|
|
142
|
-
return happWindow;
|
|
143
|
+
return;
|
|
143
144
|
}
|
|
144
145
|
await happWindow.loadURL(`http://localhost:${uiSource.port}`);
|
|
145
146
|
} else if (uiSource.type === "path") {
|
|
@@ -153,15 +154,13 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
153
154
|
const notFoundPath = happOrWebhappPath.type === "webhapp" ? path.join(__dirname, "../renderer/indexNotFound1.html") : path.join(__dirname, "../renderer/indexNotFound2.html");
|
|
154
155
|
happWindow.loadFile(notFoundPath);
|
|
155
156
|
}
|
|
156
|
-
|
|
157
|
-
return happWindow;
|
|
157
|
+
return;
|
|
158
158
|
}
|
|
159
159
|
} else {
|
|
160
160
|
throw new Error("Unsupported uiSource type: ", uiSource.type);
|
|
161
161
|
}
|
|
162
162
|
happWindow.show();
|
|
163
|
-
|
|
164
|
-
};
|
|
163
|
+
}
|
|
165
164
|
function setLinkOpenHandlers(browserWindow) {
|
|
166
165
|
browserWindow.webContents.on("will-navigate", (e) => {
|
|
167
166
|
if (e.url.startsWith("http://localhost") || e.url.startsWith("http://127.0.0.1")) {
|
|
@@ -11617,6 +11616,8 @@ class AppWebsocket {
|
|
|
11617
11616
|
callZomeTransform;
|
|
11618
11617
|
cachedAppInfo;
|
|
11619
11618
|
appInfoRequester;
|
|
11619
|
+
agentInfoRequester;
|
|
11620
|
+
agentMetaInfoRequester;
|
|
11620
11621
|
callZomeRequester;
|
|
11621
11622
|
provideMemproofRequester;
|
|
11622
11623
|
enableAppRequester;
|
|
@@ -11637,6 +11638,8 @@ class AppWebsocket {
|
|
|
11637
11638
|
this.emitter = new Emittery();
|
|
11638
11639
|
this.cachedAppInfo = appInfo;
|
|
11639
11640
|
this.appInfoRequester = AppWebsocket.requester(this.client, "app_info", this.defaultTimeout);
|
|
11641
|
+
this.agentInfoRequester = AppWebsocket.requester(this.client, "agent_info", this.defaultTimeout);
|
|
11642
|
+
this.agentMetaInfoRequester = AppWebsocket.requester(this.client, "agent_meta_info", this.defaultTimeout);
|
|
11640
11643
|
this.callZomeRequester = AppWebsocket.requester(this.client, "call_zome", this.defaultTimeout, this.callZomeTransform);
|
|
11641
11644
|
this.provideMemproofRequester = AppWebsocket.requester(this.client, "provide_memproofs", this.defaultTimeout);
|
|
11642
11645
|
this.enableAppRequester = AppWebsocket.requester(this.client, "enable_app", this.defaultTimeout);
|
|
@@ -11698,6 +11701,26 @@ class AppWebsocket {
|
|
|
11698
11701
|
this.cachedAppInfo = appInfo;
|
|
11699
11702
|
return appInfo;
|
|
11700
11703
|
}
|
|
11704
|
+
/**
|
|
11705
|
+
* Request the currently known agents of the app.
|
|
11706
|
+
*
|
|
11707
|
+
* @param req - An array of DNA hashes or null
|
|
11708
|
+
* @returns The app's agent infos as JSON string.
|
|
11709
|
+
*/
|
|
11710
|
+
async agentInfo(req, timeout2) {
|
|
11711
|
+
const agentInfos = await this.agentInfoRequester(req, timeout2);
|
|
11712
|
+
return agentInfos;
|
|
11713
|
+
}
|
|
11714
|
+
/**
|
|
11715
|
+
* Request agent meta info for an agent by peer Url.
|
|
11716
|
+
*
|
|
11717
|
+
* @param req - The peer Url of the agent and an optional array of DNA hashes
|
|
11718
|
+
* @returns The app's agent infos as JSON string.
|
|
11719
|
+
*/
|
|
11720
|
+
async agentMetaInfo(req, timeout2) {
|
|
11721
|
+
const agentInfos = await this.agentMetaInfoRequester(req, timeout2);
|
|
11722
|
+
return agentInfos;
|
|
11723
|
+
}
|
|
11701
11724
|
/**
|
|
11702
11725
|
* Request network stats.
|
|
11703
11726
|
*
|
|
@@ -11969,7 +11992,7 @@ class WsClient extends Emittery {
|
|
|
11969
11992
|
this.registerCloseListener(socket);
|
|
11970
11993
|
this.socket = socket;
|
|
11971
11994
|
this.url = url2;
|
|
11972
|
-
this.options = options
|
|
11995
|
+
this.options = options;
|
|
11973
11996
|
this.pendingRequests = {};
|
|
11974
11997
|
this.index = 0;
|
|
11975
11998
|
}
|
|
@@ -12304,6 +12327,10 @@ class AdminWebsocket {
|
|
|
12304
12327
|
* Add an existing agent to Holochain.
|
|
12305
12328
|
*/
|
|
12306
12329
|
addAgentInfo = this._requester("add_agent_info");
|
|
12330
|
+
/**
|
|
12331
|
+
* Request agent meta info about an agent.
|
|
12332
|
+
*/
|
|
12333
|
+
agentMetaInfo = this._requester("agent_meta_info");
|
|
12307
12334
|
/**
|
|
12308
12335
|
* Delete a disabled clone cell.
|
|
12309
12336
|
*/
|
|
@@ -12533,7 +12560,7 @@ const rustUtils = require("@holochain/hc-spin-rust-utils");
|
|
|
12533
12560
|
const cliPackageJsonPath = path.resolve(path.join(electron.app.getAppPath(), "../../package.json"));
|
|
12534
12561
|
const cliPackageJson = require(cliPackageJsonPath);
|
|
12535
12562
|
const cli = new commander.Command();
|
|
12536
|
-
cli.name("hc-spin").description("CLI to run Holochain
|
|
12563
|
+
cli.name("hc-spin").description("CLI to run Holochain apps during development.").version(`${cliPackageJson.version} (built for holochain ${cliPackageJson.holochainVersion})`).argument(
|
|
12537
12564
|
"<path>",
|
|
12538
12565
|
"Path to .webhapp or .happ file to launch. If a .happ file is passed, either a UI path must be specified via --ui-path or a port pointing to a localhost server via --ui-port"
|
|
12539
12566
|
).option(
|
|
@@ -12760,18 +12787,23 @@ electron.app.whenReady().then(async () => {
|
|
|
12760
12787
|
if (!appInfo) throw new Error("AppInfo is null.");
|
|
12761
12788
|
const happWindow = await createHappWindow(
|
|
12762
12789
|
CLI_OPTS.uiSource,
|
|
12763
|
-
CLI_OPTS.happOrWebhappPath,
|
|
12764
12790
|
CLI_OPTS.appId,
|
|
12765
12791
|
i + 1,
|
|
12766
12792
|
appPort,
|
|
12767
12793
|
appAuthTokenResponse.token,
|
|
12768
|
-
DATA_ROOT_DIR
|
|
12769
|
-
CLI_OPTS.openDevtools
|
|
12794
|
+
DATA_ROOT_DIR
|
|
12770
12795
|
);
|
|
12771
12796
|
WINDOW_INFO_MAP[happWindow.webContents.id] = {
|
|
12772
12797
|
agentPubKey: appInfo.agent_pub_key,
|
|
12773
12798
|
zomeCallSigner
|
|
12774
12799
|
};
|
|
12800
|
+
await loadHappWindow(
|
|
12801
|
+
happWindow,
|
|
12802
|
+
CLI_OPTS.uiSource,
|
|
12803
|
+
CLI_OPTS.happOrWebhappPath,
|
|
12804
|
+
i + 1,
|
|
12805
|
+
CLI_OPTS.openDevtools
|
|
12806
|
+
);
|
|
12775
12807
|
}
|
|
12776
12808
|
});
|
|
12777
12809
|
electron.app.on("quit", () => {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holochain/hc-spin",
|
|
3
|
-
"version": "0.500.
|
|
4
|
-
"holochainVersion": "0.5.
|
|
5
|
-
"description": "CLI to run Holochain
|
|
3
|
+
"version": "0.500.2",
|
|
4
|
+
"holochainVersion": "0.5.3",
|
|
5
|
+
"description": "CLI to run Holochain apps during development.",
|
|
6
6
|
"author": "matthme",
|
|
7
7
|
"homepage": "https://developer.holochain.org",
|
|
8
8
|
"repository": {
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@electron-toolkit/preload": "^3.0.0",
|
|
37
37
|
"@electron-toolkit/utils": "^3.0.0",
|
|
38
|
-
"@holochain/client": "0.19.
|
|
39
|
-
"@holochain/hc-spin-rust-utils": "0.500.0",
|
|
38
|
+
"@holochain/client": "^0.19.1",
|
|
39
|
+
"@holochain/hc-spin-rust-utils": "^0.500.0",
|
|
40
40
|
"@msgpack/msgpack": "^2.8.0",
|
|
41
41
|
"bufferutil": "4.0.8",
|
|
42
42
|
"commander": "11.1.0",
|
package/src/main/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import contextMenu from 'electron-context-menu';
|
|
|
7
7
|
import split from 'split';
|
|
8
8
|
import * as childProcess from 'child_process';
|
|
9
9
|
import { ZomeCallSigner } from '@holochain/hc-spin-rust-utils';
|
|
10
|
-
import { createHappWindow } from './windows';
|
|
10
|
+
import { createHappWindow, loadHappWindow } from './windows';
|
|
11
11
|
import getPort from 'get-port';
|
|
12
12
|
import {
|
|
13
13
|
AdminWebsocket,
|
|
@@ -32,7 +32,7 @@ const cli = new Command();
|
|
|
32
32
|
|
|
33
33
|
cli
|
|
34
34
|
.name('hc-spin')
|
|
35
|
-
.description('CLI to run Holochain
|
|
35
|
+
.description('CLI to run Holochain apps during development.')
|
|
36
36
|
.version(`${cliPackageJson.version} (built for holochain ${cliPackageJson.holochainVersion})`)
|
|
37
37
|
.argument(
|
|
38
38
|
'<path>',
|
|
@@ -352,18 +352,26 @@ app.whenReady().then(async () => {
|
|
|
352
352
|
if (!appInfo) throw new Error('AppInfo is null.');
|
|
353
353
|
const happWindow = await createHappWindow(
|
|
354
354
|
CLI_OPTS.uiSource,
|
|
355
|
-
CLI_OPTS.happOrWebhappPath,
|
|
356
355
|
CLI_OPTS.appId,
|
|
357
356
|
i + 1,
|
|
358
357
|
appPort,
|
|
359
358
|
appAuthTokenResponse.token,
|
|
360
359
|
DATA_ROOT_DIR,
|
|
361
|
-
CLI_OPTS.openDevtools,
|
|
362
360
|
);
|
|
361
|
+
// We need to add the window to the window map before loading its UI, otherwise
|
|
362
|
+
// zome calls can be made before handleSignZomeCall() can verify that the
|
|
363
|
+
// zome call is made from an authorized window (https://github.com/holochain/hc-spin/issues/30)
|
|
363
364
|
WINDOW_INFO_MAP[happWindow.webContents.id] = {
|
|
364
365
|
agentPubKey: appInfo.agent_pub_key,
|
|
365
366
|
zomeCallSigner,
|
|
366
367
|
};
|
|
368
|
+
await loadHappWindow(
|
|
369
|
+
happWindow,
|
|
370
|
+
CLI_OPTS.uiSource,
|
|
371
|
+
CLI_OPTS.happOrWebhappPath,
|
|
372
|
+
i + 1,
|
|
373
|
+
CLI_OPTS.openDevtools,
|
|
374
|
+
);
|
|
367
375
|
}
|
|
368
376
|
|
|
369
377
|
// app.on('activate', function () {
|
package/src/main/windows.ts
CHANGED
|
@@ -16,16 +16,14 @@ export type UISource =
|
|
|
16
16
|
port: number;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export
|
|
19
|
+
export async function createHappWindow(
|
|
20
20
|
uiSource: UISource,
|
|
21
|
-
happOrWebhappPath: HappOrWebhappPath,
|
|
22
21
|
appId: InstalledAppId,
|
|
23
22
|
agentNum: number,
|
|
24
23
|
appPort: number,
|
|
25
24
|
appAuthToken: AppAuthenticationToken,
|
|
26
25
|
appDataRootDir: string,
|
|
27
|
-
|
|
28
|
-
): Promise<BrowserWindow> => {
|
|
26
|
+
): Promise<BrowserWindow> {
|
|
29
27
|
// TODO create mapping between installed-app-id's and window ids
|
|
30
28
|
if (!appPort) throw new Error('App port not defined.');
|
|
31
29
|
|
|
@@ -81,7 +79,7 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
81
79
|
}
|
|
82
80
|
}
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
return new BrowserWindow({
|
|
85
83
|
width: 1200,
|
|
86
84
|
height: 800,
|
|
87
85
|
show: false,
|
|
@@ -92,7 +90,15 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
92
90
|
partition,
|
|
93
91
|
},
|
|
94
92
|
});
|
|
93
|
+
}
|
|
95
94
|
|
|
95
|
+
export async function loadHappWindow(
|
|
96
|
+
happWindow: BrowserWindow,
|
|
97
|
+
uiSource: UISource,
|
|
98
|
+
happOrWebhappPath: HappOrWebhappPath,
|
|
99
|
+
agentNum: number,
|
|
100
|
+
openDevtools: boolean,
|
|
101
|
+
): Promise<void> {
|
|
96
102
|
const [windowPositionX, windowPositionY] = happWindow.getPosition();
|
|
97
103
|
const windowPositionXMoved = windowPositionX + agentNum * 20;
|
|
98
104
|
const windowPositionYMoved = windowPositionY + agentNum * 20;
|
|
@@ -119,8 +125,7 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
119
125
|
} else {
|
|
120
126
|
happWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
|
|
121
127
|
}
|
|
122
|
-
|
|
123
|
-
return happWindow;
|
|
128
|
+
return;
|
|
124
129
|
}
|
|
125
130
|
await happWindow.loadURL(`http://localhost:${uiSource.port}`);
|
|
126
131
|
} else if (uiSource.type === 'path') {
|
|
@@ -138,18 +143,14 @@ electron.contextBridge.exposeInMainWorld("__HC_LAUNCHER_ENV__", {
|
|
|
138
143
|
: path.join(__dirname, '../renderer/indexNotFound2.html');
|
|
139
144
|
happWindow.loadFile(notFoundPath);
|
|
140
145
|
}
|
|
141
|
-
|
|
142
|
-
happWindow.show();
|
|
143
|
-
return happWindow;
|
|
146
|
+
return;
|
|
144
147
|
}
|
|
145
148
|
} else {
|
|
146
149
|
throw new Error('Unsupported uiSource type: ', (uiSource as any).type);
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
happWindow.show();
|
|
150
|
-
|
|
151
|
-
return happWindow;
|
|
152
|
-
};
|
|
153
|
+
}
|
|
153
154
|
|
|
154
155
|
export function setLinkOpenHandlers(browserWindow: BrowserWindow): void {
|
|
155
156
|
// links in happ windows should open in the system default application
|