├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── binaries.ts ├── downloadBinaries.ts ├── holochain.ts ├── index.ts └── options.ts ├── test └── run-test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | test/data 2 | test/test.happ 3 | binaries 4 | 5 | .DS_Store 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | # Diagnostic reports (https://nodejs.org/api/report.html) 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 17 | 18 | # Runtime data 19 | pids 20 | *.pid 21 | *.seed 22 | *.pid.lock 23 | 24 | # Directory for instrumented libs generated by jscoverage/JSCover 25 | lib-cov 26 | 27 | # Coverage directory used by tools like istanbul 28 | coverage 29 | *.lcov 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # TypeScript v1 declaration files 51 | typings/ 52 | 53 | # TypeScript cache 54 | *.tsbuildinfo 55 | 56 | # Optional npm cache directory 57 | .npm 58 | 59 | # Optional eslint cache 60 | .eslintcache 61 | 62 | # Microbundle cache 63 | .rpt2_cache/ 64 | .rts2_cache_cjs/ 65 | .rts2_cache_es/ 66 | .rts2_cache_umd/ 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | .env.test 80 | 81 | # parcel-bundler cache (https://parceljs.org/) 82 | .cache 83 | 84 | # Next.js build output 85 | .next 86 | 87 | # Nuxt.js build / generate output 88 | .nuxt 89 | dist 90 | 91 | # Gatsby files 92 | .cache/ 93 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 94 | # https://nextjs.org/blog/next-9-1#public-directory-support 95 | # public 96 | 97 | # vuepress build output 98 | .vuepress/dist 99 | 100 | # Serverless directories 101 | .serverless/ 102 | 103 | # FuseBox cache 104 | .fusebox/ 105 | 106 | # DynamoDB Local files 107 | .dynamodb/ 108 | 109 | # TernJS port file 110 | .tern-port 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # electron-holochain 2 | 3 | **Built-in Holochain Version: [v0.2.3-beta-rc.1](https://github.com/holochain/holochain/blob/main-0.2/CHANGELOG.md#20230930114759)** 4 | 5 | **Important: Expects an HAPP built with HDK [~v0.2.2](https://docs.rs/hdk/0.2.2/hdk/index.html) and HDI [~v0.3.2](https://docs.rs/hdi/0.3.2/hdi/index.html)** 6 | 7 | An alternative Holochain conductor binary useful for quick startup and including handling of key generation and hApp installation. Useful for production and development environments. 8 | 9 | manage holochain processes during an electron application runtime, using [holochain-runner binary](https://github.com/lightningrodlabs/holochain-runner). 10 | 11 | ```typescript 12 | // function initAgent( 13 | // app: App, 14 | // opts: ElectronHolochainOptions, 15 | // pathOptions?: PathOptions 16 | // ): Promise<{ statusEmitter: StatusUpdates, shutdown: () => Promise }> 17 | 18 | import {app} from 'electron' 19 | import initAgent, { 20 | StateSignal, 21 | StatusUpdates, 22 | STATUS_EVENT, 23 | APP_PORT_EVENT, 24 | ERROR_EVENT, 25 | LOG_EVENT, 26 | ElectronHolochainOptions, 27 | PathOptions 28 | } from 'electron-holochain' 29 | 30 | const runnerOptions: ElectronHolochainOptions = { 31 | happPath: 'pathtomyhapp.happ', 32 | keystorePath: string 33 | passphrase: string 34 | // datastorePath?: 'string' default: databases 35 | // appId?: string 36 | // appWsPort?: number 37 | // adminWsPort?: number 38 | // webrtcSignalUrl?: string 39 | // bootstrapUrl?: string 40 | // networkSeed?: string 41 | } 42 | 43 | const { statusEmitter, shutdown } = await initAgent(app, runnerOptions) 44 | 45 | // listen on the statusEmitter for status update 46 | statusEmitter.on(STATUS_EVENT, (status: StateSignal) => { 47 | // do stuff 48 | }) 49 | 50 | // listen on the statusEmitter for the websocket port used for app 51 | statusEmitter.on(APP_PORT_EVENT, (appPort: string) => { 52 | // do stuff 53 | } 54 | 55 | // listen for normal log messages 56 | statusEmitter.on(LOG_EVENT, (log: string) => { 57 | // do stuff 58 | }) 59 | 60 | // listen for errors 61 | statusEmitter.on(ERROR_EVENT, (error: Error) => { 62 | // do stuff 63 | }) 64 | // when the app quits, holochain-runner and lair-keystore will shut down automatically 65 | 66 | // you can also call the following, to shut them down 67 | await shutdown() 68 | ``` 69 | 70 | 71 | ## Updating holochain-runner version and releasing 72 | 73 | Go to [./src/downloadBinaries.ts](./src/downloadBinaries.ts) and search for 'version-bump'. 74 | Change the value of `const holochainRunnerTag = 'v0.7.9'` to the version of holochain-runner you want to bundle. 75 | This will instructs clients to download that version of the holochain-runner binary from Github, during `npm install`. 76 | 77 | To get this to trigger, run `npm run try-binary-download`. 78 | 79 | ## Publishing 80 | 81 | Bump the package.json version. 82 | Commit. Tag. Push. 83 | Run `npm run build` to build the typescript. 84 | Then run `npm publish --access public` to publish to npmjs.com. 85 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lightningrodlabs/electron-holochain", 3 | "version": "0.7.12", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@lightningrodlabs/electron-holochain", 9 | "version": "0.7.12", 10 | "hasInstallScript": true, 11 | "license": "CAL-1.0", 12 | "dependencies": { 13 | "request": "^2.88.2", 14 | "split": "^1.0.1", 15 | "tar": "^6.1.11", 16 | "tree-kill": "^1.2.2" 17 | }, 18 | "devDependencies": { 19 | "electron": "20.0.0", 20 | "typescript": "4.3.5" 21 | } 22 | }, 23 | "node_modules/@electron/get": { 24 | "version": "1.14.1", 25 | "dev": true, 26 | "license": "MIT", 27 | "dependencies": { 28 | "debug": "^4.1.1", 29 | "env-paths": "^2.2.0", 30 | "fs-extra": "^8.1.0", 31 | "got": "^9.6.0", 32 | "progress": "^2.0.3", 33 | "semver": "^6.2.0", 34 | "sumchecker": "^3.0.1" 35 | }, 36 | "engines": { 37 | "node": ">=8.6" 38 | }, 39 | "optionalDependencies": { 40 | "global-agent": "^3.0.0", 41 | "global-tunnel-ng": "^2.7.1" 42 | } 43 | }, 44 | "node_modules/@sindresorhus/is": { 45 | "version": "0.14.0", 46 | "dev": true, 47 | "license": "MIT", 48 | "engines": { 49 | "node": ">=6" 50 | } 51 | }, 52 | "node_modules/@szmarczak/http-timer": { 53 | "version": "1.1.2", 54 | "dev": true, 55 | "license": "MIT", 56 | "dependencies": { 57 | "defer-to-connect": "^1.0.1" 58 | }, 59 | "engines": { 60 | "node": ">=6" 61 | } 62 | }, 63 | "node_modules/@types/node": { 64 | "version": "16.11.45", 65 | "dev": true, 66 | "license": "MIT" 67 | }, 68 | "node_modules/@types/yauzl": { 69 | "version": "2.10.0", 70 | "dev": true, 71 | "license": "MIT", 72 | "optional": true, 73 | "dependencies": { 74 | "@types/node": "*" 75 | } 76 | }, 77 | "node_modules/ajv": { 78 | "version": "6.12.6", 79 | "license": "MIT", 80 | "dependencies": { 81 | "fast-deep-equal": "^3.1.1", 82 | "fast-json-stable-stringify": "^2.0.0", 83 | "json-schema-traverse": "^0.4.1", 84 | "uri-js": "^4.2.2" 85 | }, 86 | "funding": { 87 | "type": "github", 88 | "url": "https://github.com/sponsors/epoberezkin" 89 | } 90 | }, 91 | "node_modules/asn1": { 92 | "version": "0.2.6", 93 | "license": "MIT", 94 | "dependencies": { 95 | "safer-buffer": "~2.1.0" 96 | } 97 | }, 98 | "node_modules/assert-plus": { 99 | "version": "1.0.0", 100 | "license": "MIT", 101 | "engines": { 102 | "node": ">=0.8" 103 | } 104 | }, 105 | "node_modules/asynckit": { 106 | "version": "0.4.0", 107 | "license": "MIT" 108 | }, 109 | "node_modules/aws-sign2": { 110 | "version": "0.7.0", 111 | "license": "Apache-2.0", 112 | "engines": { 113 | "node": "*" 114 | } 115 | }, 116 | "node_modules/aws4": { 117 | "version": "1.11.0", 118 | "license": "MIT" 119 | }, 120 | "node_modules/bcrypt-pbkdf": { 121 | "version": "1.0.2", 122 | "license": "BSD-3-Clause", 123 | "dependencies": { 124 | "tweetnacl": "^0.14.3" 125 | } 126 | }, 127 | "node_modules/boolean": { 128 | "version": "3.2.0", 129 | "dev": true, 130 | "license": "MIT", 131 | "optional": true 132 | }, 133 | "node_modules/buffer-crc32": { 134 | "version": "0.2.13", 135 | "dev": true, 136 | "license": "MIT", 137 | "engines": { 138 | "node": "*" 139 | } 140 | }, 141 | "node_modules/cacheable-request": { 142 | "version": "6.1.0", 143 | "dev": true, 144 | "license": "MIT", 145 | "dependencies": { 146 | "clone-response": "^1.0.2", 147 | "get-stream": "^5.1.0", 148 | "http-cache-semantics": "^4.0.0", 149 | "keyv": "^3.0.0", 150 | "lowercase-keys": "^2.0.0", 151 | "normalize-url": "^4.1.0", 152 | "responselike": "^1.0.2" 153 | }, 154 | "engines": { 155 | "node": ">=8" 156 | } 157 | }, 158 | "node_modules/cacheable-request/node_modules/get-stream": { 159 | "version": "5.2.0", 160 | "dev": true, 161 | "license": "MIT", 162 | "dependencies": { 163 | "pump": "^3.0.0" 164 | }, 165 | "engines": { 166 | "node": ">=8" 167 | }, 168 | "funding": { 169 | "url": "https://github.com/sponsors/sindresorhus" 170 | } 171 | }, 172 | "node_modules/cacheable-request/node_modules/lowercase-keys": { 173 | "version": "2.0.0", 174 | "dev": true, 175 | "license": "MIT", 176 | "engines": { 177 | "node": ">=8" 178 | } 179 | }, 180 | "node_modules/caseless": { 181 | "version": "0.12.0", 182 | "license": "Apache-2.0" 183 | }, 184 | "node_modules/chownr": { 185 | "version": "2.0.0", 186 | "license": "ISC", 187 | "engines": { 188 | "node": ">=10" 189 | } 190 | }, 191 | "node_modules/clone-response": { 192 | "version": "1.0.2", 193 | "dev": true, 194 | "license": "MIT", 195 | "dependencies": { 196 | "mimic-response": "^1.0.0" 197 | } 198 | }, 199 | "node_modules/combined-stream": { 200 | "version": "1.0.8", 201 | "license": "MIT", 202 | "dependencies": { 203 | "delayed-stream": "~1.0.0" 204 | }, 205 | "engines": { 206 | "node": ">= 0.8" 207 | } 208 | }, 209 | "node_modules/config-chain": { 210 | "version": "1.1.13", 211 | "dev": true, 212 | "license": "MIT", 213 | "optional": true, 214 | "dependencies": { 215 | "ini": "^1.3.4", 216 | "proto-list": "~1.2.1" 217 | } 218 | }, 219 | "node_modules/dashdash": { 220 | "version": "1.14.1", 221 | "license": "MIT", 222 | "dependencies": { 223 | "assert-plus": "^1.0.0" 224 | }, 225 | "engines": { 226 | "node": ">=0.10" 227 | } 228 | }, 229 | "node_modules/debug": { 230 | "version": "4.3.4", 231 | "dev": true, 232 | "license": "MIT", 233 | "dependencies": { 234 | "ms": "2.1.2" 235 | }, 236 | "engines": { 237 | "node": ">=6.0" 238 | }, 239 | "peerDependenciesMeta": { 240 | "supports-color": { 241 | "optional": true 242 | } 243 | } 244 | }, 245 | "node_modules/decompress-response": { 246 | "version": "3.3.0", 247 | "dev": true, 248 | "license": "MIT", 249 | "dependencies": { 250 | "mimic-response": "^1.0.0" 251 | }, 252 | "engines": { 253 | "node": ">=4" 254 | } 255 | }, 256 | "node_modules/defer-to-connect": { 257 | "version": "1.1.3", 258 | "dev": true, 259 | "license": "MIT" 260 | }, 261 | "node_modules/define-properties": { 262 | "version": "1.1.3", 263 | "dev": true, 264 | "license": "MIT", 265 | "optional": true, 266 | "dependencies": { 267 | "object-keys": "^1.0.12" 268 | }, 269 | "engines": { 270 | "node": ">= 0.4" 271 | } 272 | }, 273 | "node_modules/delayed-stream": { 274 | "version": "1.0.0", 275 | "license": "MIT", 276 | "engines": { 277 | "node": ">=0.4.0" 278 | } 279 | }, 280 | "node_modules/detect-node": { 281 | "version": "2.1.0", 282 | "dev": true, 283 | "license": "MIT", 284 | "optional": true 285 | }, 286 | "node_modules/duplexer3": { 287 | "version": "0.1.4", 288 | "dev": true, 289 | "license": "BSD-3-Clause" 290 | }, 291 | "node_modules/ecc-jsbn": { 292 | "version": "0.1.2", 293 | "license": "MIT", 294 | "dependencies": { 295 | "jsbn": "~0.1.0", 296 | "safer-buffer": "^2.1.0" 297 | } 298 | }, 299 | "node_modules/electron": { 300 | "version": "20.0.0", 301 | "dev": true, 302 | "hasInstallScript": true, 303 | "license": "MIT", 304 | "dependencies": { 305 | "@electron/get": "^1.14.1", 306 | "@types/node": "^16.11.26", 307 | "extract-zip": "^2.0.1" 308 | }, 309 | "bin": { 310 | "electron": "cli.js" 311 | }, 312 | "engines": { 313 | "node": ">= 10.17.0" 314 | } 315 | }, 316 | "node_modules/encodeurl": { 317 | "version": "1.0.2", 318 | "dev": true, 319 | "license": "MIT", 320 | "optional": true, 321 | "engines": { 322 | "node": ">= 0.8" 323 | } 324 | }, 325 | "node_modules/end-of-stream": { 326 | "version": "1.4.4", 327 | "dev": true, 328 | "license": "MIT", 329 | "dependencies": { 330 | "once": "^1.4.0" 331 | } 332 | }, 333 | "node_modules/env-paths": { 334 | "version": "2.2.1", 335 | "dev": true, 336 | "license": "MIT", 337 | "engines": { 338 | "node": ">=6" 339 | } 340 | }, 341 | "node_modules/es6-error": { 342 | "version": "4.1.1", 343 | "dev": true, 344 | "license": "MIT", 345 | "optional": true 346 | }, 347 | "node_modules/escape-string-regexp": { 348 | "version": "4.0.0", 349 | "dev": true, 350 | "license": "MIT", 351 | "optional": true, 352 | "engines": { 353 | "node": ">=10" 354 | }, 355 | "funding": { 356 | "url": "https://github.com/sponsors/sindresorhus" 357 | } 358 | }, 359 | "node_modules/extend": { 360 | "version": "3.0.2", 361 | "license": "MIT" 362 | }, 363 | "node_modules/extract-zip": { 364 | "version": "2.0.1", 365 | "dev": true, 366 | "license": "BSD-2-Clause", 367 | "dependencies": { 368 | "debug": "^4.1.1", 369 | "get-stream": "^5.1.0", 370 | "yauzl": "^2.10.0" 371 | }, 372 | "bin": { 373 | "extract-zip": "cli.js" 374 | }, 375 | "engines": { 376 | "node": ">= 10.17.0" 377 | }, 378 | "optionalDependencies": { 379 | "@types/yauzl": "^2.9.1" 380 | } 381 | }, 382 | "node_modules/extract-zip/node_modules/get-stream": { 383 | "version": "5.2.0", 384 | "dev": true, 385 | "license": "MIT", 386 | "dependencies": { 387 | "pump": "^3.0.0" 388 | }, 389 | "engines": { 390 | "node": ">=8" 391 | }, 392 | "funding": { 393 | "url": "https://github.com/sponsors/sindresorhus" 394 | } 395 | }, 396 | "node_modules/extsprintf": { 397 | "version": "1.3.0", 398 | "engines": [ 399 | "node >=0.6.0" 400 | ], 401 | "license": "MIT" 402 | }, 403 | "node_modules/fast-deep-equal": { 404 | "version": "3.1.3", 405 | "license": "MIT" 406 | }, 407 | "node_modules/fast-json-stable-stringify": { 408 | "version": "2.1.0", 409 | "license": "MIT" 410 | }, 411 | "node_modules/fd-slicer": { 412 | "version": "1.1.0", 413 | "dev": true, 414 | "license": "MIT", 415 | "dependencies": { 416 | "pend": "~1.2.0" 417 | } 418 | }, 419 | "node_modules/forever-agent": { 420 | "version": "0.6.1", 421 | "license": "Apache-2.0", 422 | "engines": { 423 | "node": "*" 424 | } 425 | }, 426 | "node_modules/form-data": { 427 | "version": "2.3.3", 428 | "license": "MIT", 429 | "dependencies": { 430 | "asynckit": "^0.4.0", 431 | "combined-stream": "^1.0.6", 432 | "mime-types": "^2.1.12" 433 | }, 434 | "engines": { 435 | "node": ">= 0.12" 436 | } 437 | }, 438 | "node_modules/fs-extra": { 439 | "version": "8.1.0", 440 | "dev": true, 441 | "license": "MIT", 442 | "dependencies": { 443 | "graceful-fs": "^4.2.0", 444 | "jsonfile": "^4.0.0", 445 | "universalify": "^0.1.0" 446 | }, 447 | "engines": { 448 | "node": ">=6 <7 || >=8" 449 | } 450 | }, 451 | "node_modules/fs-minipass": { 452 | "version": "2.1.0", 453 | "license": "ISC", 454 | "dependencies": { 455 | "minipass": "^3.0.0" 456 | }, 457 | "engines": { 458 | "node": ">= 8" 459 | } 460 | }, 461 | "node_modules/get-stream": { 462 | "version": "4.1.0", 463 | "dev": true, 464 | "license": "MIT", 465 | "dependencies": { 466 | "pump": "^3.0.0" 467 | }, 468 | "engines": { 469 | "node": ">=6" 470 | } 471 | }, 472 | "node_modules/getpass": { 473 | "version": "0.1.7", 474 | "license": "MIT", 475 | "dependencies": { 476 | "assert-plus": "^1.0.0" 477 | } 478 | }, 479 | "node_modules/global-agent": { 480 | "version": "3.0.0", 481 | "dev": true, 482 | "license": "BSD-3-Clause", 483 | "optional": true, 484 | "dependencies": { 485 | "boolean": "^3.0.1", 486 | "es6-error": "^4.1.1", 487 | "matcher": "^3.0.0", 488 | "roarr": "^2.15.3", 489 | "semver": "^7.3.2", 490 | "serialize-error": "^7.0.1" 491 | }, 492 | "engines": { 493 | "node": ">=10.0" 494 | } 495 | }, 496 | "node_modules/global-agent/node_modules/semver": { 497 | "version": "7.3.6", 498 | "dev": true, 499 | "license": "ISC", 500 | "optional": true, 501 | "dependencies": { 502 | "lru-cache": "^7.4.0" 503 | }, 504 | "bin": { 505 | "semver": "bin/semver.js" 506 | }, 507 | "engines": { 508 | "node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0" 509 | } 510 | }, 511 | "node_modules/global-tunnel-ng": { 512 | "version": "2.7.1", 513 | "dev": true, 514 | "license": "BSD-3-Clause", 515 | "optional": true, 516 | "dependencies": { 517 | "encodeurl": "^1.0.2", 518 | "lodash": "^4.17.10", 519 | "npm-conf": "^1.1.3", 520 | "tunnel": "^0.0.6" 521 | }, 522 | "engines": { 523 | "node": ">=0.10" 524 | } 525 | }, 526 | "node_modules/globalthis": { 527 | "version": "1.0.2", 528 | "dev": true, 529 | "license": "MIT", 530 | "optional": true, 531 | "dependencies": { 532 | "define-properties": "^1.1.3" 533 | }, 534 | "engines": { 535 | "node": ">= 0.4" 536 | }, 537 | "funding": { 538 | "url": "https://github.com/sponsors/ljharb" 539 | } 540 | }, 541 | "node_modules/got": { 542 | "version": "9.6.0", 543 | "dev": true, 544 | "license": "MIT", 545 | "dependencies": { 546 | "@sindresorhus/is": "^0.14.0", 547 | "@szmarczak/http-timer": "^1.1.2", 548 | "cacheable-request": "^6.0.0", 549 | "decompress-response": "^3.3.0", 550 | "duplexer3": "^0.1.4", 551 | "get-stream": "^4.1.0", 552 | "lowercase-keys": "^1.0.1", 553 | "mimic-response": "^1.0.1", 554 | "p-cancelable": "^1.0.0", 555 | "to-readable-stream": "^1.0.0", 556 | "url-parse-lax": "^3.0.0" 557 | }, 558 | "engines": { 559 | "node": ">=8.6" 560 | } 561 | }, 562 | "node_modules/graceful-fs": { 563 | "version": "4.2.10", 564 | "dev": true, 565 | "license": "ISC" 566 | }, 567 | "node_modules/har-schema": { 568 | "version": "2.0.0", 569 | "license": "ISC", 570 | "engines": { 571 | "node": ">=4" 572 | } 573 | }, 574 | "node_modules/har-validator": { 575 | "version": "5.1.5", 576 | "license": "MIT", 577 | "dependencies": { 578 | "ajv": "^6.12.3", 579 | "har-schema": "^2.0.0" 580 | }, 581 | "engines": { 582 | "node": ">=6" 583 | } 584 | }, 585 | "node_modules/http-cache-semantics": { 586 | "version": "4.1.0", 587 | "dev": true, 588 | "license": "BSD-2-Clause" 589 | }, 590 | "node_modules/http-signature": { 591 | "version": "1.2.0", 592 | "license": "MIT", 593 | "dependencies": { 594 | "assert-plus": "^1.0.0", 595 | "jsprim": "^1.2.2", 596 | "sshpk": "^1.7.0" 597 | }, 598 | "engines": { 599 | "node": ">=0.8", 600 | "npm": ">=1.3.7" 601 | } 602 | }, 603 | "node_modules/ini": { 604 | "version": "1.3.8", 605 | "dev": true, 606 | "license": "ISC", 607 | "optional": true 608 | }, 609 | "node_modules/is-typedarray": { 610 | "version": "1.0.0", 611 | "license": "MIT" 612 | }, 613 | "node_modules/isstream": { 614 | "version": "0.1.2", 615 | "license": "MIT" 616 | }, 617 | "node_modules/jsbn": { 618 | "version": "0.1.1", 619 | "license": "MIT" 620 | }, 621 | "node_modules/json-buffer": { 622 | "version": "3.0.0", 623 | "dev": true, 624 | "license": "MIT" 625 | }, 626 | "node_modules/json-schema": { 627 | "version": "0.4.0", 628 | "license": "(AFL-2.1 OR BSD-3-Clause)" 629 | }, 630 | "node_modules/json-schema-traverse": { 631 | "version": "0.4.1", 632 | "license": "MIT" 633 | }, 634 | "node_modules/json-stringify-safe": { 635 | "version": "5.0.1", 636 | "license": "ISC" 637 | }, 638 | "node_modules/jsonfile": { 639 | "version": "4.0.0", 640 | "dev": true, 641 | "license": "MIT", 642 | "optionalDependencies": { 643 | "graceful-fs": "^4.1.6" 644 | } 645 | }, 646 | "node_modules/jsprim": { 647 | "version": "1.4.2", 648 | "license": "MIT", 649 | "dependencies": { 650 | "assert-plus": "1.0.0", 651 | "extsprintf": "1.3.0", 652 | "json-schema": "0.4.0", 653 | "verror": "1.10.0" 654 | }, 655 | "engines": { 656 | "node": ">=0.6.0" 657 | } 658 | }, 659 | "node_modules/keyv": { 660 | "version": "3.1.0", 661 | "dev": true, 662 | "license": "MIT", 663 | "dependencies": { 664 | "json-buffer": "3.0.0" 665 | } 666 | }, 667 | "node_modules/lodash": { 668 | "version": "4.17.21", 669 | "dev": true, 670 | "license": "MIT", 671 | "optional": true 672 | }, 673 | "node_modules/lowercase-keys": { 674 | "version": "1.0.1", 675 | "dev": true, 676 | "license": "MIT", 677 | "engines": { 678 | "node": ">=0.10.0" 679 | } 680 | }, 681 | "node_modules/lru-cache": { 682 | "version": "7.8.0", 683 | "dev": true, 684 | "license": "ISC", 685 | "optional": true, 686 | "engines": { 687 | "node": ">=12" 688 | } 689 | }, 690 | "node_modules/matcher": { 691 | "version": "3.0.0", 692 | "dev": true, 693 | "license": "MIT", 694 | "optional": true, 695 | "dependencies": { 696 | "escape-string-regexp": "^4.0.0" 697 | }, 698 | "engines": { 699 | "node": ">=10" 700 | } 701 | }, 702 | "node_modules/mime-db": { 703 | "version": "1.52.0", 704 | "license": "MIT", 705 | "engines": { 706 | "node": ">= 0.6" 707 | } 708 | }, 709 | "node_modules/mime-types": { 710 | "version": "2.1.35", 711 | "license": "MIT", 712 | "dependencies": { 713 | "mime-db": "1.52.0" 714 | }, 715 | "engines": { 716 | "node": ">= 0.6" 717 | } 718 | }, 719 | "node_modules/mimic-response": { 720 | "version": "1.0.1", 721 | "dev": true, 722 | "license": "MIT", 723 | "engines": { 724 | "node": ">=4" 725 | } 726 | }, 727 | "node_modules/minipass": { 728 | "version": "3.1.6", 729 | "license": "ISC", 730 | "dependencies": { 731 | "yallist": "^4.0.0" 732 | }, 733 | "engines": { 734 | "node": ">=8" 735 | } 736 | }, 737 | "node_modules/minizlib": { 738 | "version": "2.1.2", 739 | "license": "MIT", 740 | "dependencies": { 741 | "minipass": "^3.0.0", 742 | "yallist": "^4.0.0" 743 | }, 744 | "engines": { 745 | "node": ">= 8" 746 | } 747 | }, 748 | "node_modules/ms": { 749 | "version": "2.1.2", 750 | "dev": true, 751 | "license": "MIT" 752 | }, 753 | "node_modules/normalize-url": { 754 | "version": "4.5.1", 755 | "dev": true, 756 | "license": "MIT", 757 | "engines": { 758 | "node": ">=8" 759 | } 760 | }, 761 | "node_modules/npm-conf": { 762 | "version": "1.1.3", 763 | "dev": true, 764 | "license": "MIT", 765 | "optional": true, 766 | "dependencies": { 767 | "config-chain": "^1.1.11", 768 | "pify": "^3.0.0" 769 | }, 770 | "engines": { 771 | "node": ">=4" 772 | } 773 | }, 774 | "node_modules/oauth-sign": { 775 | "version": "0.9.0", 776 | "license": "Apache-2.0", 777 | "engines": { 778 | "node": "*" 779 | } 780 | }, 781 | "node_modules/object-keys": { 782 | "version": "1.1.1", 783 | "dev": true, 784 | "license": "MIT", 785 | "optional": true, 786 | "engines": { 787 | "node": ">= 0.4" 788 | } 789 | }, 790 | "node_modules/once": { 791 | "version": "1.4.0", 792 | "dev": true, 793 | "license": "ISC", 794 | "dependencies": { 795 | "wrappy": "1" 796 | } 797 | }, 798 | "node_modules/p-cancelable": { 799 | "version": "1.1.0", 800 | "dev": true, 801 | "license": "MIT", 802 | "engines": { 803 | "node": ">=6" 804 | } 805 | }, 806 | "node_modules/pend": { 807 | "version": "1.2.0", 808 | "dev": true, 809 | "license": "MIT" 810 | }, 811 | "node_modules/performance-now": { 812 | "version": "2.1.0", 813 | "license": "MIT" 814 | }, 815 | "node_modules/pify": { 816 | "version": "3.0.0", 817 | "dev": true, 818 | "license": "MIT", 819 | "optional": true, 820 | "engines": { 821 | "node": ">=4" 822 | } 823 | }, 824 | "node_modules/prepend-http": { 825 | "version": "2.0.0", 826 | "dev": true, 827 | "license": "MIT", 828 | "engines": { 829 | "node": ">=4" 830 | } 831 | }, 832 | "node_modules/progress": { 833 | "version": "2.0.3", 834 | "dev": true, 835 | "license": "MIT", 836 | "engines": { 837 | "node": ">=0.4.0" 838 | } 839 | }, 840 | "node_modules/proto-list": { 841 | "version": "1.2.4", 842 | "dev": true, 843 | "license": "ISC", 844 | "optional": true 845 | }, 846 | "node_modules/psl": { 847 | "version": "1.8.0", 848 | "license": "MIT" 849 | }, 850 | "node_modules/pump": { 851 | "version": "3.0.0", 852 | "dev": true, 853 | "license": "MIT", 854 | "dependencies": { 855 | "end-of-stream": "^1.1.0", 856 | "once": "^1.3.1" 857 | } 858 | }, 859 | "node_modules/punycode": { 860 | "version": "2.1.1", 861 | "license": "MIT", 862 | "engines": { 863 | "node": ">=6" 864 | } 865 | }, 866 | "node_modules/qs": { 867 | "version": "6.5.3", 868 | "license": "BSD-3-Clause", 869 | "engines": { 870 | "node": ">=0.6" 871 | } 872 | }, 873 | "node_modules/request": { 874 | "version": "2.88.2", 875 | "license": "Apache-2.0", 876 | "dependencies": { 877 | "aws-sign2": "~0.7.0", 878 | "aws4": "^1.8.0", 879 | "caseless": "~0.12.0", 880 | "combined-stream": "~1.0.6", 881 | "extend": "~3.0.2", 882 | "forever-agent": "~0.6.1", 883 | "form-data": "~2.3.2", 884 | "har-validator": "~5.1.3", 885 | "http-signature": "~1.2.0", 886 | "is-typedarray": "~1.0.0", 887 | "isstream": "~0.1.2", 888 | "json-stringify-safe": "~5.0.1", 889 | "mime-types": "~2.1.19", 890 | "oauth-sign": "~0.9.0", 891 | "performance-now": "^2.1.0", 892 | "qs": "~6.5.2", 893 | "safe-buffer": "^5.1.2", 894 | "tough-cookie": "~2.5.0", 895 | "tunnel-agent": "^0.6.0", 896 | "uuid": "^3.3.2" 897 | }, 898 | "engines": { 899 | "node": ">= 6" 900 | } 901 | }, 902 | "node_modules/responselike": { 903 | "version": "1.0.2", 904 | "dev": true, 905 | "license": "MIT", 906 | "dependencies": { 907 | "lowercase-keys": "^1.0.0" 908 | } 909 | }, 910 | "node_modules/roarr": { 911 | "version": "2.15.4", 912 | "dev": true, 913 | "license": "BSD-3-Clause", 914 | "optional": true, 915 | "dependencies": { 916 | "boolean": "^3.0.1", 917 | "detect-node": "^2.0.4", 918 | "globalthis": "^1.0.1", 919 | "json-stringify-safe": "^5.0.1", 920 | "semver-compare": "^1.0.0", 921 | "sprintf-js": "^1.1.2" 922 | }, 923 | "engines": { 924 | "node": ">=8.0" 925 | } 926 | }, 927 | "node_modules/safe-buffer": { 928 | "version": "5.1.2", 929 | "license": "MIT" 930 | }, 931 | "node_modules/safer-buffer": { 932 | "version": "2.1.2", 933 | "license": "MIT" 934 | }, 935 | "node_modules/semver": { 936 | "version": "6.3.0", 937 | "dev": true, 938 | "license": "ISC", 939 | "bin": { 940 | "semver": "bin/semver.js" 941 | } 942 | }, 943 | "node_modules/semver-compare": { 944 | "version": "1.0.0", 945 | "dev": true, 946 | "license": "MIT", 947 | "optional": true 948 | }, 949 | "node_modules/serialize-error": { 950 | "version": "7.0.1", 951 | "dev": true, 952 | "license": "MIT", 953 | "optional": true, 954 | "dependencies": { 955 | "type-fest": "^0.13.1" 956 | }, 957 | "engines": { 958 | "node": ">=10" 959 | }, 960 | "funding": { 961 | "url": "https://github.com/sponsors/sindresorhus" 962 | } 963 | }, 964 | "node_modules/split": { 965 | "version": "1.0.1", 966 | "license": "MIT", 967 | "dependencies": { 968 | "through": "2" 969 | }, 970 | "engines": { 971 | "node": "*" 972 | } 973 | }, 974 | "node_modules/sprintf-js": { 975 | "version": "1.1.2", 976 | "dev": true, 977 | "license": "BSD-3-Clause", 978 | "optional": true 979 | }, 980 | "node_modules/sshpk": { 981 | "version": "1.17.0", 982 | "license": "MIT", 983 | "dependencies": { 984 | "asn1": "~0.2.3", 985 | "assert-plus": "^1.0.0", 986 | "bcrypt-pbkdf": "^1.0.0", 987 | "dashdash": "^1.12.0", 988 | "ecc-jsbn": "~0.1.1", 989 | "getpass": "^0.1.1", 990 | "jsbn": "~0.1.0", 991 | "safer-buffer": "^2.0.2", 992 | "tweetnacl": "~0.14.0" 993 | }, 994 | "bin": { 995 | "sshpk-conv": "bin/sshpk-conv", 996 | "sshpk-sign": "bin/sshpk-sign", 997 | "sshpk-verify": "bin/sshpk-verify" 998 | }, 999 | "engines": { 1000 | "node": ">=0.10.0" 1001 | } 1002 | }, 1003 | "node_modules/sumchecker": { 1004 | "version": "3.0.1", 1005 | "dev": true, 1006 | "license": "Apache-2.0", 1007 | "dependencies": { 1008 | "debug": "^4.1.0" 1009 | }, 1010 | "engines": { 1011 | "node": ">= 8.0" 1012 | } 1013 | }, 1014 | "node_modules/tar": { 1015 | "version": "6.1.11", 1016 | "license": "ISC", 1017 | "dependencies": { 1018 | "chownr": "^2.0.0", 1019 | "fs-minipass": "^2.0.0", 1020 | "minipass": "^3.0.0", 1021 | "minizlib": "^2.1.1", 1022 | "mkdirp": "^1.0.3", 1023 | "yallist": "^4.0.0" 1024 | }, 1025 | "engines": { 1026 | "node": ">= 10" 1027 | } 1028 | }, 1029 | "node_modules/tar/node_modules/mkdirp": { 1030 | "version": "1.0.4", 1031 | "license": "MIT", 1032 | "bin": { 1033 | "mkdirp": "bin/cmd.js" 1034 | }, 1035 | "engines": { 1036 | "node": ">=10" 1037 | } 1038 | }, 1039 | "node_modules/through": { 1040 | "version": "2.3.8", 1041 | "license": "MIT" 1042 | }, 1043 | "node_modules/to-readable-stream": { 1044 | "version": "1.0.0", 1045 | "dev": true, 1046 | "license": "MIT", 1047 | "engines": { 1048 | "node": ">=6" 1049 | } 1050 | }, 1051 | "node_modules/tough-cookie": { 1052 | "version": "2.5.0", 1053 | "license": "BSD-3-Clause", 1054 | "dependencies": { 1055 | "psl": "^1.1.28", 1056 | "punycode": "^2.1.1" 1057 | }, 1058 | "engines": { 1059 | "node": ">=0.8" 1060 | } 1061 | }, 1062 | "node_modules/tree-kill": { 1063 | "version": "1.2.2", 1064 | "license": "MIT", 1065 | "bin": { 1066 | "tree-kill": "cli.js" 1067 | } 1068 | }, 1069 | "node_modules/tunnel": { 1070 | "version": "0.0.6", 1071 | "dev": true, 1072 | "license": "MIT", 1073 | "optional": true, 1074 | "engines": { 1075 | "node": ">=0.6.11 <=0.7.0 || >=0.7.3" 1076 | } 1077 | }, 1078 | "node_modules/tunnel-agent": { 1079 | "version": "0.6.0", 1080 | "license": "Apache-2.0", 1081 | "dependencies": { 1082 | "safe-buffer": "^5.0.1" 1083 | }, 1084 | "engines": { 1085 | "node": "*" 1086 | } 1087 | }, 1088 | "node_modules/tweetnacl": { 1089 | "version": "0.14.5", 1090 | "license": "Unlicense" 1091 | }, 1092 | "node_modules/type-fest": { 1093 | "version": "0.13.1", 1094 | "dev": true, 1095 | "license": "(MIT OR CC0-1.0)", 1096 | "optional": true, 1097 | "engines": { 1098 | "node": ">=10" 1099 | }, 1100 | "funding": { 1101 | "url": "https://github.com/sponsors/sindresorhus" 1102 | } 1103 | }, 1104 | "node_modules/typescript": { 1105 | "version": "4.3.5", 1106 | "dev": true, 1107 | "license": "Apache-2.0", 1108 | "bin": { 1109 | "tsc": "bin/tsc", 1110 | "tsserver": "bin/tsserver" 1111 | }, 1112 | "engines": { 1113 | "node": ">=4.2.0" 1114 | } 1115 | }, 1116 | "node_modules/universalify": { 1117 | "version": "0.1.2", 1118 | "dev": true, 1119 | "license": "MIT", 1120 | "engines": { 1121 | "node": ">= 4.0.0" 1122 | } 1123 | }, 1124 | "node_modules/uri-js": { 1125 | "version": "4.4.1", 1126 | "license": "BSD-2-Clause", 1127 | "dependencies": { 1128 | "punycode": "^2.1.0" 1129 | } 1130 | }, 1131 | "node_modules/url-parse-lax": { 1132 | "version": "3.0.0", 1133 | "dev": true, 1134 | "license": "MIT", 1135 | "dependencies": { 1136 | "prepend-http": "^2.0.0" 1137 | }, 1138 | "engines": { 1139 | "node": ">=4" 1140 | } 1141 | }, 1142 | "node_modules/uuid": { 1143 | "version": "3.4.0", 1144 | "license": "MIT", 1145 | "bin": { 1146 | "uuid": "bin/uuid" 1147 | } 1148 | }, 1149 | "node_modules/verror": { 1150 | "version": "1.10.0", 1151 | "engines": [ 1152 | "node >=0.6.0" 1153 | ], 1154 | "license": "MIT", 1155 | "dependencies": { 1156 | "assert-plus": "^1.0.0", 1157 | "core-util-is": "1.0.2", 1158 | "extsprintf": "^1.2.0" 1159 | } 1160 | }, 1161 | "node_modules/verror/node_modules/core-util-is": { 1162 | "version": "1.0.2", 1163 | "license": "MIT" 1164 | }, 1165 | "node_modules/wrappy": { 1166 | "version": "1.0.2", 1167 | "dev": true, 1168 | "license": "ISC" 1169 | }, 1170 | "node_modules/yallist": { 1171 | "version": "4.0.0", 1172 | "license": "ISC" 1173 | }, 1174 | "node_modules/yauzl": { 1175 | "version": "2.10.0", 1176 | "dev": true, 1177 | "license": "MIT", 1178 | "dependencies": { 1179 | "buffer-crc32": "~0.2.3", 1180 | "fd-slicer": "~1.1.0" 1181 | } 1182 | } 1183 | }, 1184 | "dependencies": { 1185 | "@electron/get": { 1186 | "version": "1.14.1", 1187 | "dev": true, 1188 | "requires": { 1189 | "debug": "^4.1.1", 1190 | "env-paths": "^2.2.0", 1191 | "fs-extra": "^8.1.0", 1192 | "global-agent": "^3.0.0", 1193 | "global-tunnel-ng": "^2.7.1", 1194 | "got": "^9.6.0", 1195 | "progress": "^2.0.3", 1196 | "semver": "^6.2.0", 1197 | "sumchecker": "^3.0.1" 1198 | } 1199 | }, 1200 | "@sindresorhus/is": { 1201 | "version": "0.14.0", 1202 | "dev": true 1203 | }, 1204 | "@szmarczak/http-timer": { 1205 | "version": "1.1.2", 1206 | "dev": true, 1207 | "requires": { 1208 | "defer-to-connect": "^1.0.1" 1209 | } 1210 | }, 1211 | "@types/node": { 1212 | "version": "16.11.45", 1213 | "dev": true 1214 | }, 1215 | "@types/yauzl": { 1216 | "version": "2.10.0", 1217 | "dev": true, 1218 | "optional": true, 1219 | "requires": { 1220 | "@types/node": "*" 1221 | } 1222 | }, 1223 | "ajv": { 1224 | "version": "6.12.6", 1225 | "requires": { 1226 | "fast-deep-equal": "^3.1.1", 1227 | "fast-json-stable-stringify": "^2.0.0", 1228 | "json-schema-traverse": "^0.4.1", 1229 | "uri-js": "^4.2.2" 1230 | } 1231 | }, 1232 | "asn1": { 1233 | "version": "0.2.6", 1234 | "requires": { 1235 | "safer-buffer": "~2.1.0" 1236 | } 1237 | }, 1238 | "assert-plus": { 1239 | "version": "1.0.0" 1240 | }, 1241 | "asynckit": { 1242 | "version": "0.4.0" 1243 | }, 1244 | "aws-sign2": { 1245 | "version": "0.7.0" 1246 | }, 1247 | "aws4": { 1248 | "version": "1.11.0" 1249 | }, 1250 | "bcrypt-pbkdf": { 1251 | "version": "1.0.2", 1252 | "requires": { 1253 | "tweetnacl": "^0.14.3" 1254 | } 1255 | }, 1256 | "boolean": { 1257 | "version": "3.2.0", 1258 | "dev": true, 1259 | "optional": true 1260 | }, 1261 | "buffer-crc32": { 1262 | "version": "0.2.13", 1263 | "dev": true 1264 | }, 1265 | "cacheable-request": { 1266 | "version": "6.1.0", 1267 | "dev": true, 1268 | "requires": { 1269 | "clone-response": "^1.0.2", 1270 | "get-stream": "^5.1.0", 1271 | "http-cache-semantics": "^4.0.0", 1272 | "keyv": "^3.0.0", 1273 | "lowercase-keys": "^2.0.0", 1274 | "normalize-url": "^4.1.0", 1275 | "responselike": "^1.0.2" 1276 | }, 1277 | "dependencies": { 1278 | "get-stream": { 1279 | "version": "5.2.0", 1280 | "dev": true, 1281 | "requires": { 1282 | "pump": "^3.0.0" 1283 | } 1284 | }, 1285 | "lowercase-keys": { 1286 | "version": "2.0.0", 1287 | "dev": true 1288 | } 1289 | } 1290 | }, 1291 | "caseless": { 1292 | "version": "0.12.0" 1293 | }, 1294 | "chownr": { 1295 | "version": "2.0.0" 1296 | }, 1297 | "clone-response": { 1298 | "version": "1.0.2", 1299 | "dev": true, 1300 | "requires": { 1301 | "mimic-response": "^1.0.0" 1302 | } 1303 | }, 1304 | "combined-stream": { 1305 | "version": "1.0.8", 1306 | "requires": { 1307 | "delayed-stream": "~1.0.0" 1308 | } 1309 | }, 1310 | "config-chain": { 1311 | "version": "1.1.13", 1312 | "dev": true, 1313 | "optional": true, 1314 | "requires": { 1315 | "ini": "^1.3.4", 1316 | "proto-list": "~1.2.1" 1317 | } 1318 | }, 1319 | "dashdash": { 1320 | "version": "1.14.1", 1321 | "requires": { 1322 | "assert-plus": "^1.0.0" 1323 | } 1324 | }, 1325 | "debug": { 1326 | "version": "4.3.4", 1327 | "dev": true, 1328 | "requires": { 1329 | "ms": "2.1.2" 1330 | } 1331 | }, 1332 | "decompress-response": { 1333 | "version": "3.3.0", 1334 | "dev": true, 1335 | "requires": { 1336 | "mimic-response": "^1.0.0" 1337 | } 1338 | }, 1339 | "defer-to-connect": { 1340 | "version": "1.1.3", 1341 | "dev": true 1342 | }, 1343 | "define-properties": { 1344 | "version": "1.1.3", 1345 | "dev": true, 1346 | "optional": true, 1347 | "requires": { 1348 | "object-keys": "^1.0.12" 1349 | } 1350 | }, 1351 | "delayed-stream": { 1352 | "version": "1.0.0" 1353 | }, 1354 | "detect-node": { 1355 | "version": "2.1.0", 1356 | "dev": true, 1357 | "optional": true 1358 | }, 1359 | "duplexer3": { 1360 | "version": "0.1.4", 1361 | "dev": true 1362 | }, 1363 | "ecc-jsbn": { 1364 | "version": "0.1.2", 1365 | "requires": { 1366 | "jsbn": "~0.1.0", 1367 | "safer-buffer": "^2.1.0" 1368 | } 1369 | }, 1370 | "electron": { 1371 | "version": "20.0.0", 1372 | "dev": true, 1373 | "requires": { 1374 | "@electron/get": "^1.14.1", 1375 | "@types/node": "^16.11.26", 1376 | "extract-zip": "^2.0.1" 1377 | } 1378 | }, 1379 | "encodeurl": { 1380 | "version": "1.0.2", 1381 | "dev": true, 1382 | "optional": true 1383 | }, 1384 | "end-of-stream": { 1385 | "version": "1.4.4", 1386 | "dev": true, 1387 | "requires": { 1388 | "once": "^1.4.0" 1389 | } 1390 | }, 1391 | "env-paths": { 1392 | "version": "2.2.1", 1393 | "dev": true 1394 | }, 1395 | "es6-error": { 1396 | "version": "4.1.1", 1397 | "dev": true, 1398 | "optional": true 1399 | }, 1400 | "escape-string-regexp": { 1401 | "version": "4.0.0", 1402 | "dev": true, 1403 | "optional": true 1404 | }, 1405 | "extend": { 1406 | "version": "3.0.2" 1407 | }, 1408 | "extract-zip": { 1409 | "version": "2.0.1", 1410 | "dev": true, 1411 | "requires": { 1412 | "@types/yauzl": "^2.9.1", 1413 | "debug": "^4.1.1", 1414 | "get-stream": "^5.1.0", 1415 | "yauzl": "^2.10.0" 1416 | }, 1417 | "dependencies": { 1418 | "get-stream": { 1419 | "version": "5.2.0", 1420 | "dev": true, 1421 | "requires": { 1422 | "pump": "^3.0.0" 1423 | } 1424 | } 1425 | } 1426 | }, 1427 | "extsprintf": { 1428 | "version": "1.3.0" 1429 | }, 1430 | "fast-deep-equal": { 1431 | "version": "3.1.3" 1432 | }, 1433 | "fast-json-stable-stringify": { 1434 | "version": "2.1.0" 1435 | }, 1436 | "fd-slicer": { 1437 | "version": "1.1.0", 1438 | "dev": true, 1439 | "requires": { 1440 | "pend": "~1.2.0" 1441 | } 1442 | }, 1443 | "forever-agent": { 1444 | "version": "0.6.1" 1445 | }, 1446 | "form-data": { 1447 | "version": "2.3.3", 1448 | "requires": { 1449 | "asynckit": "^0.4.0", 1450 | "combined-stream": "^1.0.6", 1451 | "mime-types": "^2.1.12" 1452 | } 1453 | }, 1454 | "fs-extra": { 1455 | "version": "8.1.0", 1456 | "dev": true, 1457 | "requires": { 1458 | "graceful-fs": "^4.2.0", 1459 | "jsonfile": "^4.0.0", 1460 | "universalify": "^0.1.0" 1461 | } 1462 | }, 1463 | "fs-minipass": { 1464 | "version": "2.1.0", 1465 | "requires": { 1466 | "minipass": "^3.0.0" 1467 | } 1468 | }, 1469 | "get-stream": { 1470 | "version": "4.1.0", 1471 | "dev": true, 1472 | "requires": { 1473 | "pump": "^3.0.0" 1474 | } 1475 | }, 1476 | "getpass": { 1477 | "version": "0.1.7", 1478 | "requires": { 1479 | "assert-plus": "^1.0.0" 1480 | } 1481 | }, 1482 | "global-agent": { 1483 | "version": "3.0.0", 1484 | "dev": true, 1485 | "optional": true, 1486 | "requires": { 1487 | "boolean": "^3.0.1", 1488 | "es6-error": "^4.1.1", 1489 | "matcher": "^3.0.0", 1490 | "roarr": "^2.15.3", 1491 | "semver": "^7.3.2", 1492 | "serialize-error": "^7.0.1" 1493 | }, 1494 | "dependencies": { 1495 | "semver": { 1496 | "version": "7.3.6", 1497 | "dev": true, 1498 | "optional": true, 1499 | "requires": { 1500 | "lru-cache": "^7.4.0" 1501 | } 1502 | } 1503 | } 1504 | }, 1505 | "global-tunnel-ng": { 1506 | "version": "2.7.1", 1507 | "dev": true, 1508 | "optional": true, 1509 | "requires": { 1510 | "encodeurl": "^1.0.2", 1511 | "lodash": "^4.17.10", 1512 | "npm-conf": "^1.1.3", 1513 | "tunnel": "^0.0.6" 1514 | } 1515 | }, 1516 | "globalthis": { 1517 | "version": "1.0.2", 1518 | "dev": true, 1519 | "optional": true, 1520 | "requires": { 1521 | "define-properties": "^1.1.3" 1522 | } 1523 | }, 1524 | "got": { 1525 | "version": "9.6.0", 1526 | "dev": true, 1527 | "requires": { 1528 | "@sindresorhus/is": "^0.14.0", 1529 | "@szmarczak/http-timer": "^1.1.2", 1530 | "cacheable-request": "^6.0.0", 1531 | "decompress-response": "^3.3.0", 1532 | "duplexer3": "^0.1.4", 1533 | "get-stream": "^4.1.0", 1534 | "lowercase-keys": "^1.0.1", 1535 | "mimic-response": "^1.0.1", 1536 | "p-cancelable": "^1.0.0", 1537 | "to-readable-stream": "^1.0.0", 1538 | "url-parse-lax": "^3.0.0" 1539 | } 1540 | }, 1541 | "graceful-fs": { 1542 | "version": "4.2.10", 1543 | "dev": true 1544 | }, 1545 | "har-schema": { 1546 | "version": "2.0.0" 1547 | }, 1548 | "har-validator": { 1549 | "version": "5.1.5", 1550 | "requires": { 1551 | "ajv": "^6.12.3", 1552 | "har-schema": "^2.0.0" 1553 | } 1554 | }, 1555 | "http-cache-semantics": { 1556 | "version": "4.1.0", 1557 | "dev": true 1558 | }, 1559 | "http-signature": { 1560 | "version": "1.2.0", 1561 | "requires": { 1562 | "assert-plus": "^1.0.0", 1563 | "jsprim": "^1.2.2", 1564 | "sshpk": "^1.7.0" 1565 | } 1566 | }, 1567 | "ini": { 1568 | "version": "1.3.8", 1569 | "dev": true, 1570 | "optional": true 1571 | }, 1572 | "is-typedarray": { 1573 | "version": "1.0.0" 1574 | }, 1575 | "isstream": { 1576 | "version": "0.1.2" 1577 | }, 1578 | "jsbn": { 1579 | "version": "0.1.1" 1580 | }, 1581 | "json-buffer": { 1582 | "version": "3.0.0", 1583 | "dev": true 1584 | }, 1585 | "json-schema": { 1586 | "version": "0.4.0" 1587 | }, 1588 | "json-schema-traverse": { 1589 | "version": "0.4.1" 1590 | }, 1591 | "json-stringify-safe": { 1592 | "version": "5.0.1" 1593 | }, 1594 | "jsonfile": { 1595 | "version": "4.0.0", 1596 | "dev": true, 1597 | "requires": { 1598 | "graceful-fs": "^4.1.6" 1599 | } 1600 | }, 1601 | "jsprim": { 1602 | "version": "1.4.2", 1603 | "requires": { 1604 | "assert-plus": "1.0.0", 1605 | "extsprintf": "1.3.0", 1606 | "json-schema": "0.4.0", 1607 | "verror": "1.10.0" 1608 | } 1609 | }, 1610 | "keyv": { 1611 | "version": "3.1.0", 1612 | "dev": true, 1613 | "requires": { 1614 | "json-buffer": "3.0.0" 1615 | } 1616 | }, 1617 | "lodash": { 1618 | "version": "4.17.21", 1619 | "dev": true, 1620 | "optional": true 1621 | }, 1622 | "lowercase-keys": { 1623 | "version": "1.0.1", 1624 | "dev": true 1625 | }, 1626 | "lru-cache": { 1627 | "version": "7.8.0", 1628 | "dev": true, 1629 | "optional": true 1630 | }, 1631 | "matcher": { 1632 | "version": "3.0.0", 1633 | "dev": true, 1634 | "optional": true, 1635 | "requires": { 1636 | "escape-string-regexp": "^4.0.0" 1637 | } 1638 | }, 1639 | "mime-db": { 1640 | "version": "1.52.0" 1641 | }, 1642 | "mime-types": { 1643 | "version": "2.1.35", 1644 | "requires": { 1645 | "mime-db": "1.52.0" 1646 | } 1647 | }, 1648 | "mimic-response": { 1649 | "version": "1.0.1", 1650 | "dev": true 1651 | }, 1652 | "minipass": { 1653 | "version": "3.1.6", 1654 | "requires": { 1655 | "yallist": "^4.0.0" 1656 | } 1657 | }, 1658 | "minizlib": { 1659 | "version": "2.1.2", 1660 | "requires": { 1661 | "minipass": "^3.0.0", 1662 | "yallist": "^4.0.0" 1663 | } 1664 | }, 1665 | "ms": { 1666 | "version": "2.1.2", 1667 | "dev": true 1668 | }, 1669 | "normalize-url": { 1670 | "version": "4.5.1", 1671 | "dev": true 1672 | }, 1673 | "npm-conf": { 1674 | "version": "1.1.3", 1675 | "dev": true, 1676 | "optional": true, 1677 | "requires": { 1678 | "config-chain": "^1.1.11", 1679 | "pify": "^3.0.0" 1680 | } 1681 | }, 1682 | "oauth-sign": { 1683 | "version": "0.9.0" 1684 | }, 1685 | "object-keys": { 1686 | "version": "1.1.1", 1687 | "dev": true, 1688 | "optional": true 1689 | }, 1690 | "once": { 1691 | "version": "1.4.0", 1692 | "dev": true, 1693 | "requires": { 1694 | "wrappy": "1" 1695 | } 1696 | }, 1697 | "p-cancelable": { 1698 | "version": "1.1.0", 1699 | "dev": true 1700 | }, 1701 | "pend": { 1702 | "version": "1.2.0", 1703 | "dev": true 1704 | }, 1705 | "performance-now": { 1706 | "version": "2.1.0" 1707 | }, 1708 | "pify": { 1709 | "version": "3.0.0", 1710 | "dev": true, 1711 | "optional": true 1712 | }, 1713 | "prepend-http": { 1714 | "version": "2.0.0", 1715 | "dev": true 1716 | }, 1717 | "progress": { 1718 | "version": "2.0.3", 1719 | "dev": true 1720 | }, 1721 | "proto-list": { 1722 | "version": "1.2.4", 1723 | "dev": true, 1724 | "optional": true 1725 | }, 1726 | "psl": { 1727 | "version": "1.8.0" 1728 | }, 1729 | "pump": { 1730 | "version": "3.0.0", 1731 | "dev": true, 1732 | "requires": { 1733 | "end-of-stream": "^1.1.0", 1734 | "once": "^1.3.1" 1735 | } 1736 | }, 1737 | "punycode": { 1738 | "version": "2.1.1" 1739 | }, 1740 | "qs": { 1741 | "version": "6.5.3" 1742 | }, 1743 | "request": { 1744 | "version": "2.88.2", 1745 | "requires": { 1746 | "aws-sign2": "~0.7.0", 1747 | "aws4": "^1.8.0", 1748 | "caseless": "~0.12.0", 1749 | "combined-stream": "~1.0.6", 1750 | "extend": "~3.0.2", 1751 | "forever-agent": "~0.6.1", 1752 | "form-data": "~2.3.2", 1753 | "har-validator": "~5.1.3", 1754 | "http-signature": "~1.2.0", 1755 | "is-typedarray": "~1.0.0", 1756 | "isstream": "~0.1.2", 1757 | "json-stringify-safe": "~5.0.1", 1758 | "mime-types": "~2.1.19", 1759 | "oauth-sign": "~0.9.0", 1760 | "performance-now": "^2.1.0", 1761 | "qs": "~6.5.2", 1762 | "safe-buffer": "^5.1.2", 1763 | "tough-cookie": "~2.5.0", 1764 | "tunnel-agent": "^0.6.0", 1765 | "uuid": "^3.3.2" 1766 | } 1767 | }, 1768 | "responselike": { 1769 | "version": "1.0.2", 1770 | "dev": true, 1771 | "requires": { 1772 | "lowercase-keys": "^1.0.0" 1773 | } 1774 | }, 1775 | "roarr": { 1776 | "version": "2.15.4", 1777 | "dev": true, 1778 | "optional": true, 1779 | "requires": { 1780 | "boolean": "^3.0.1", 1781 | "detect-node": "^2.0.4", 1782 | "globalthis": "^1.0.1", 1783 | "json-stringify-safe": "^5.0.1", 1784 | "semver-compare": "^1.0.0", 1785 | "sprintf-js": "^1.1.2" 1786 | } 1787 | }, 1788 | "safe-buffer": { 1789 | "version": "5.1.2" 1790 | }, 1791 | "safer-buffer": { 1792 | "version": "2.1.2" 1793 | }, 1794 | "semver": { 1795 | "version": "6.3.0", 1796 | "dev": true 1797 | }, 1798 | "semver-compare": { 1799 | "version": "1.0.0", 1800 | "dev": true, 1801 | "optional": true 1802 | }, 1803 | "serialize-error": { 1804 | "version": "7.0.1", 1805 | "dev": true, 1806 | "optional": true, 1807 | "requires": { 1808 | "type-fest": "^0.13.1" 1809 | } 1810 | }, 1811 | "split": { 1812 | "version": "1.0.1", 1813 | "requires": { 1814 | "through": "2" 1815 | } 1816 | }, 1817 | "sprintf-js": { 1818 | "version": "1.1.2", 1819 | "dev": true, 1820 | "optional": true 1821 | }, 1822 | "sshpk": { 1823 | "version": "1.17.0", 1824 | "requires": { 1825 | "asn1": "~0.2.3", 1826 | "assert-plus": "^1.0.0", 1827 | "bcrypt-pbkdf": "^1.0.0", 1828 | "dashdash": "^1.12.0", 1829 | "ecc-jsbn": "~0.1.1", 1830 | "getpass": "^0.1.1", 1831 | "jsbn": "~0.1.0", 1832 | "safer-buffer": "^2.0.2", 1833 | "tweetnacl": "~0.14.0" 1834 | } 1835 | }, 1836 | "sumchecker": { 1837 | "version": "3.0.1", 1838 | "dev": true, 1839 | "requires": { 1840 | "debug": "^4.1.0" 1841 | } 1842 | }, 1843 | "tar": { 1844 | "version": "6.1.11", 1845 | "requires": { 1846 | "chownr": "^2.0.0", 1847 | "fs-minipass": "^2.0.0", 1848 | "minipass": "^3.0.0", 1849 | "minizlib": "^2.1.1", 1850 | "mkdirp": "^1.0.3", 1851 | "yallist": "^4.0.0" 1852 | }, 1853 | "dependencies": { 1854 | "mkdirp": { 1855 | "version": "1.0.4" 1856 | } 1857 | } 1858 | }, 1859 | "through": { 1860 | "version": "2.3.8" 1861 | }, 1862 | "to-readable-stream": { 1863 | "version": "1.0.0", 1864 | "dev": true 1865 | }, 1866 | "tough-cookie": { 1867 | "version": "2.5.0", 1868 | "requires": { 1869 | "psl": "^1.1.28", 1870 | "punycode": "^2.1.1" 1871 | } 1872 | }, 1873 | "tree-kill": { 1874 | "version": "1.2.2" 1875 | }, 1876 | "tunnel": { 1877 | "version": "0.0.6", 1878 | "dev": true, 1879 | "optional": true 1880 | }, 1881 | "tunnel-agent": { 1882 | "version": "0.6.0", 1883 | "requires": { 1884 | "safe-buffer": "^5.0.1" 1885 | } 1886 | }, 1887 | "tweetnacl": { 1888 | "version": "0.14.5" 1889 | }, 1890 | "type-fest": { 1891 | "version": "0.13.1", 1892 | "dev": true, 1893 | "optional": true 1894 | }, 1895 | "typescript": { 1896 | "version": "4.3.5", 1897 | "dev": true 1898 | }, 1899 | "universalify": { 1900 | "version": "0.1.2", 1901 | "dev": true 1902 | }, 1903 | "uri-js": { 1904 | "version": "4.4.1", 1905 | "requires": { 1906 | "punycode": "^2.1.0" 1907 | } 1908 | }, 1909 | "url-parse-lax": { 1910 | "version": "3.0.0", 1911 | "dev": true, 1912 | "requires": { 1913 | "prepend-http": "^2.0.0" 1914 | } 1915 | }, 1916 | "uuid": { 1917 | "version": "3.4.0" 1918 | }, 1919 | "verror": { 1920 | "version": "1.10.0", 1921 | "requires": { 1922 | "assert-plus": "^1.0.0", 1923 | "core-util-is": "1.0.2", 1924 | "extsprintf": "^1.2.0" 1925 | }, 1926 | "dependencies": { 1927 | "core-util-is": { 1928 | "version": "1.0.2" 1929 | } 1930 | } 1931 | }, 1932 | "wrappy": { 1933 | "version": "1.0.2", 1934 | "dev": true 1935 | }, 1936 | "yallist": { 1937 | "version": "4.0.0" 1938 | }, 1939 | "yauzl": { 1940 | "version": "2.10.0", 1941 | "dev": true, 1942 | "requires": { 1943 | "buffer-crc32": "~0.2.3", 1944 | "fd-slicer": "~1.1.0" 1945 | } 1946 | } 1947 | } 1948 | } 1949 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lightningrodlabs/electron-holochain", 3 | "version": "0.7.12", 4 | "description": "manage holochain as a sub-process within an electron application runtime", 5 | "main": "dist/src/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "test": "tsc && npx electron ./dist/test/run-test.js", 9 | "try-binary-download": "tsc && node ./dist/src/downloadBinaries.js", 10 | "postinstall": "node ./dist/src/downloadBinaries.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/lightningrodlabs/electron-holochain.git" 15 | }, 16 | "keywords": [ 17 | "holochain", 18 | "electron", 19 | "holochain-runner" 20 | ], 21 | "author": "Connor Turland ", 22 | "license": "CAL-1.0", 23 | "bugs": { 24 | "url": "https://github.com/lightningrodlabs/electron-holochain/issues" 25 | }, 26 | "homepage": "https://github.com/lightningrodlabs/electron-holochain#readme", 27 | "devDependencies": { 28 | "electron": "20.0.0", 29 | "typescript": "4.3.5" 30 | }, 31 | "dependencies": { 32 | "request": "^2.88.2", 33 | "split": "^1.0.1", 34 | "tar": "^6.1.11", 35 | "tree-kill": "^1.2.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/binaries.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | 3 | const windowsExtension = process.platform === 'win32' ? '.exe' : '' 4 | const holochainRunnerBinName = `holochain-runner${windowsExtension}` 5 | 6 | const binariesDirectory = path.join( 7 | __dirname, 8 | // is relative to the directory where this file compiles to: dist/src 9 | '../../binaries' 10 | ) 11 | 12 | const defaultHolochainRunnerBinaryPath = path.join( 13 | binariesDirectory, 14 | holochainRunnerBinName 15 | ) 16 | 17 | export { 18 | holochainRunnerBinName, 19 | binariesDirectory, 20 | defaultHolochainRunnerBinaryPath, 21 | } 22 | -------------------------------------------------------------------------------- /src/downloadBinaries.ts: -------------------------------------------------------------------------------- 1 | import * as request from 'request' 2 | import * as fs from 'fs' 3 | import { chmod } from 'fs/promises' 4 | import * as path from 'path' 5 | import * as tar from 'tar' 6 | import { 7 | binariesDirectory, 8 | defaultHolochainRunnerBinaryPath, 9 | } from './binaries.js' 10 | 11 | async function download(url: string, dest: string) { 12 | /* Create an empty file where we can save data */ 13 | const file = fs.createWriteStream(dest) 14 | 15 | /* Using Promises so that we can use the ASYNC AWAIT syntax */ 16 | await new Promise((resolve, reject) => { 17 | request({ 18 | /* Here you should specify the exact link to the file you are trying to download */ 19 | uri: url, 20 | gzip: true, 21 | }) 22 | .pipe(file) 23 | .on('finish', async () => { 24 | console.log(`${dest} is finished downloading.`) 25 | resolve() 26 | }) 27 | .on('error', (error: Error) => { 28 | reject(error) 29 | }) 30 | }).catch((error) => { 31 | console.log(`Something happened: ${error}`) 32 | }) 33 | } 34 | 35 | async function downloadBinaries(tag: string) { 36 | fs.rmSync(binariesDirectory, { recursive: true, force: true }) 37 | fs.mkdirSync(binariesDirectory) 38 | 39 | let platformArch: string = process.platform 40 | // darwin means MacOS 41 | if (platformArch === 'darwin') { 42 | if (process.arch === 'arm64') { 43 | platformArch = 'darwinArm64' 44 | } else if (process.arch === 'x64') { 45 | platformArch = 'darwinX64' 46 | } 47 | } 48 | const holochainRunnerFilenames = { 49 | win32: 'holochain-runner-x86_64-pc-windows-msvc.tar.gz', 50 | darwinX64: 'holochain-runner-x86_64-apple-darwin.tar.gz', 51 | darwinArm64: 'holochain-runner-arm64-apple-darwin.tar.gz', 52 | linux: 'holochain-runner-x86_64-unknown-linux-gnu.tar.gz', 53 | } 54 | const holochainRunnerCompressedUrl = `https://github.com/lightningrodlabs/holochain-runner/releases/download/${tag}/${holochainRunnerFilenames[platformArch]}` 55 | const compressedTempFilename = path.join( 56 | binariesDirectory, 57 | holochainRunnerFilenames[platformArch] 58 | ) 59 | await download(holochainRunnerCompressedUrl, compressedTempFilename) 60 | await tar.x({ file: compressedTempFilename, cwd: binariesDirectory }) 61 | fs.rmSync(compressedTempFilename) 62 | // defaultHolochainRunnerBinaryPath 63 | await chmod(defaultHolochainRunnerBinaryPath, 511) 64 | } 65 | 66 | ;(async () => { 67 | try { 68 | // current holochain-runner release version 69 | // version-bump 70 | const holochainRunnerTag = 'v0.7.12' 71 | await downloadBinaries(holochainRunnerTag) 72 | } catch (e) { 73 | console.log(e) 74 | } 75 | })() 76 | -------------------------------------------------------------------------------- /src/holochain.ts: -------------------------------------------------------------------------------- 1 | import * as childProcess from 'child_process' 2 | import * as path from 'path' 3 | import * as fs from 'fs' 4 | import { EventEmitter } from 'events' 5 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 6 | // @ts-ignore 7 | import * as split from 'split' 8 | import { 9 | constructOptions, 10 | ElectronHolochainOptions, 11 | PathOptions, 12 | } from './options.js' 13 | import { 14 | defaultHolochainRunnerBinaryPath, 15 | } from './binaries.js' 16 | 17 | 18 | type STATUS_EVENT = 'status' 19 | const STATUS_EVENT = 'status' 20 | type APP_PORT_EVENT = 'port' 21 | const APP_PORT_EVENT = 'port' 22 | type LAIR_SOCKET_EVENT = 'lair' 23 | const LAIR_SOCKET_EVENT = 'lair' 24 | type ERROR_EVENT = 'error' 25 | const ERROR_EVENT = 'error' 26 | type HOLOCHAIN_LOG_EVENT = 'holochain_log' 27 | const HOLOCHAIN_LOG_EVENT = 'holochain_log' 28 | type WASM_LOG_EVENT = 'wasm_log' 29 | const WASM_LOG_EVENT = 'wasm_log' 30 | type HOLOCHAIN_RUNNER_QUIT = 'holochain_runner_quit' 31 | const HOLOCHAIN_RUNNER_QUIT = 'holochain_runner_quit' 32 | export { 33 | STATUS_EVENT, 34 | APP_PORT_EVENT, 35 | LAIR_SOCKET_EVENT, 36 | ERROR_EVENT, 37 | HOLOCHAIN_LOG_EVENT, 38 | WASM_LOG_EVENT, 39 | HOLOCHAIN_RUNNER_QUIT, 40 | } 41 | 42 | export declare interface StatusUpdates { 43 | on( 44 | event: 45 | | STATUS_EVENT 46 | | APP_PORT_EVENT 47 | | LAIR_SOCKET_EVENT 48 | | ERROR_EVENT 49 | | HOLOCHAIN_LOG_EVENT 50 | | WASM_LOG_EVENT 51 | | HOLOCHAIN_RUNNER_QUIT, 52 | listener: (status: StateSignal | string | Error) => void 53 | ): this 54 | } 55 | 56 | export class StatusUpdates extends EventEmitter { 57 | emitStatus(status: StateSignal): void { 58 | this.emit(STATUS_EVENT, status) 59 | } 60 | emitAppPort(port: string): void { 61 | this.emit(APP_PORT_EVENT, port) 62 | } 63 | emitLairSocket(socket: string): void { 64 | this.emit(LAIR_SOCKET_EVENT, socket) 65 | } 66 | emitWasmLog(line: string): void { 67 | this.emit(WASM_LOG_EVENT, line) 68 | } 69 | emitHolochainLog(line: string): void { 70 | this.emit(HOLOCHAIN_LOG_EVENT, line) 71 | } 72 | emitError(error: Error): void { 73 | this.emit(ERROR_EVENT, error) 74 | } 75 | emitHolochainRunnerQuit(): void { 76 | this.emit(HOLOCHAIN_RUNNER_QUIT) 77 | } 78 | } 79 | 80 | export enum StateSignal { 81 | IsFirstRun, 82 | IsNotFirstRun, 83 | CreatingKeys, 84 | RegisteringDna, 85 | InstallingApp, 86 | EnablingApp, 87 | AddingAppInterface, 88 | IsReady, 89 | } 90 | 91 | function stdoutToStateSignal(string: string): StateSignal { 92 | switch (string) { 93 | case '0': 94 | return StateSignal.IsFirstRun 95 | case '1': 96 | return StateSignal.IsNotFirstRun 97 | // IsFirstRun events 98 | case '2': 99 | return StateSignal.CreatingKeys 100 | case '3': 101 | return StateSignal.RegisteringDna 102 | case '4': 103 | return StateSignal.InstallingApp 104 | case '5': 105 | return StateSignal.EnablingApp 106 | case '6': 107 | return StateSignal.AddingAppInterface 108 | // Done/Ready Event 109 | case '7': 110 | return StateSignal.IsReady 111 | default: 112 | return null 113 | } 114 | } 115 | 116 | function checkLairInitialized(pathToLairConfig: string): boolean { 117 | // this file should be sitting on the file system 118 | // if the `lair-keystore init` command has been previously run in that directory 119 | const configFile = path.join(pathToLairConfig, 'lair-keystore-config.yaml') 120 | return fs.existsSync(configFile) 121 | } 122 | 123 | export async function runHolochain( 124 | statusEmitter: StatusUpdates, 125 | options: ElectronHolochainOptions, 126 | pathOptions?: PathOptions 127 | ): Promise<{ 128 | holochainRunnerHandle: childProcess.ChildProcessWithoutNullStreams 129 | }> { 130 | const holochainRunnerBinaryPath = pathOptions 131 | ? pathOptions.holochainRunnerBinaryPath 132 | : defaultHolochainRunnerBinaryPath 133 | 134 | // create the keystore folder if it doesn't 135 | // exist 136 | if (options.keystorePath && !checkLairInitialized(options.keystorePath)) { 137 | fs.mkdirSync(options.keystorePath, { 138 | recursive: true 139 | }) 140 | } 141 | 142 | const optionsArray = constructOptions(options) 143 | // spawn holochain-runner and pass it a version 144 | // of the given options that it can digest 145 | const holochainRunnerHandle = childProcess.spawn( 146 | holochainRunnerBinaryPath, 147 | optionsArray, 148 | { 149 | env: { 150 | RUST_LOG: 'info,' + 151 | // this thrashes on startup 152 | 'wasmer_compiler_cranelift=error,' + 153 | // this gives a bunch of warnings about how long db accesses are taking, tmi 154 | 'holochain_sqlite::db::access=error,' + 155 | // this gives a lot of "search_and_discover_peer_connect: no peers found, retrying after delay" messages on INFO 156 | 'kitsune_p2p::spawn::actor::discover=error' 157 | // RUST_LOG: 'info', 158 | } 159 | } 160 | ) 161 | // write the passphrase through stdin 162 | holochainRunnerHandle.stdin.write(options.passphrase) 163 | holochainRunnerHandle.stdin.end() 164 | 165 | // split divides up the stream line by line 166 | holochainRunnerHandle.stdout.pipe(split()).on('data', (line: string) => { 167 | // Check for state signal 168 | const checkIfSignal = stdoutToStateSignal(line) 169 | const appPort = parseForAppPort(line) 170 | const lairKeystoreSocket = parseForLairKeystoreSocket(line) 171 | if (checkIfSignal !== null) { 172 | statusEmitter.emitStatus(checkIfSignal) 173 | } else if (appPort !== null) { 174 | statusEmitter.emitAppPort(appPort) 175 | } else if (lairKeystoreSocket !== null) { 176 | statusEmitter.emitLairSocket(lairKeystoreSocket) 177 | } else { 178 | // per this discussion 179 | // https://github.com/holochain/holochain/issues/1449#issuecomment-1499888044 180 | // WASM logs are the ones being passed through stdout 181 | statusEmitter.emitWasmLog(line) 182 | } 183 | }) 184 | holochainRunnerHandle.stderr.pipe(split()).on('data', (line: string) => { 185 | if (holochainRunnerHandle.killed) return; 186 | // per this discussion 187 | // https://github.com/holochain/holochain/issues/1449#issuecomment-1499888044 188 | // holochain logs are the ones being passed through stderr 189 | statusEmitter.emitHolochainLog(line) 190 | }) 191 | holochainRunnerHandle.on('error', (error) => { 192 | if (holochainRunnerHandle.killed) return; 193 | statusEmitter.emitError(error) 194 | }) 195 | holochainRunnerHandle.on('close', (code) => { 196 | if (holochainRunnerHandle.killed) return; 197 | console.log('holochain-runner closed with code: ', code) 198 | statusEmitter.emitHolochainRunnerQuit() 199 | }) 200 | 201 | return { 202 | holochainRunnerHandle, 203 | } 204 | } 205 | 206 | function parseForAppPort(line: string): string | null { 207 | return parseForRegexp(line, /APP_WS_PORT: ([0-9]*)/gm); 208 | } 209 | 210 | function parseForLairKeystoreSocket(line: string): string | null { 211 | return parseForRegexp(line, /\# lair-keystore connection_url \# (.*) \#/gm); 212 | } 213 | 214 | function parseForRegexp(line: string, regex: RegExp): string | null { 215 | let match = regex.exec(line) 216 | if (match === undefined || match === null || match.length === 0) { 217 | return null 218 | } 219 | return match[1] 220 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'electron' 2 | import { ElectronHolochainOptions, PathOptions } from './options' 3 | import { 4 | runHolochain, 5 | StateSignal, 6 | StatusUpdates, 7 | STATUS_EVENT, 8 | APP_PORT_EVENT, 9 | LAIR_SOCKET_EVENT, 10 | ERROR_EVENT, 11 | HOLOCHAIN_LOG_EVENT, 12 | WASM_LOG_EVENT, 13 | HOLOCHAIN_RUNNER_QUIT, 14 | } from './holochain' 15 | import { 16 | defaultHolochainRunnerBinaryPath, 17 | } from './binaries' 18 | import * as childProcess from 'child_process' 19 | import * as fs from 'fs' 20 | import * as kill from 'tree-kill' 21 | 22 | export { 23 | StateSignal, 24 | StatusUpdates, 25 | ElectronHolochainOptions, 26 | PathOptions, 27 | STATUS_EVENT, 28 | HOLOCHAIN_LOG_EVENT, 29 | WASM_LOG_EVENT, 30 | APP_PORT_EVENT, 31 | LAIR_SOCKET_EVENT, 32 | ERROR_EVENT, 33 | HOLOCHAIN_RUNNER_QUIT, 34 | } 35 | 36 | // start up lair and holochain-runner processes, 37 | // automatically shut them down on app quit, 38 | // and emit events for status updates on their installation progress 39 | export default async function initAgent( 40 | app: App, 41 | opts: ElectronHolochainOptions, 42 | binaryPaths?: PathOptions 43 | ): Promise<{ statusEmitter: StatusUpdates; shutdown: () => Promise }> { 44 | // wait for the app to be ready 45 | await app.whenReady() 46 | const statusEmitter = new StatusUpdates() 47 | // execute this in a callback 48 | // so that we can continue and return 49 | // the statusEmitter to the caller 50 | let holochainRunnerHandle: childProcess.ChildProcessWithoutNullStreams 51 | ;(async () => { 52 | let handles = await runHolochain( 53 | statusEmitter, 54 | opts, 55 | binaryPaths 56 | ) 57 | holochainRunnerHandle = handles.holochainRunnerHandle 58 | 59 | app.on('will-quit', async () => { 60 | // SIGTERM signal is the default, and that's good 61 | await killHolochain(holochainRunnerHandle) 62 | }) 63 | })() 64 | return { 65 | statusEmitter, 66 | shutdown: async () => { 67 | // SIGTERM signal is the default, and that's good 68 | await killHolochain(holochainRunnerHandle) 69 | }, 70 | } 71 | } 72 | 73 | /** 74 | * 75 | */ 76 | function sleep(ms) { 77 | return new Promise((resolve) => setTimeout(resolve, ms)) 78 | } 79 | 80 | /** 81 | * Kill handles and their children 82 | */ 83 | async function killHolochain( 84 | holochainRunnerHandle: childProcess.ChildProcessWithoutNullStreams 85 | ) { 86 | // Kill holochain and its children 87 | let canWaitForHolochain = false 88 | if (holochainRunnerHandle && holochainRunnerHandle.pid) { 89 | canWaitForHolochain = true 90 | console.debug('Killing holochain sub processes...') 91 | kill(holochainRunnerHandle.pid, function (err) { 92 | canWaitForHolochain = false 93 | if (!err) { 94 | console.debug('killed all holochain sub processes') 95 | } else { 96 | console.error(err) 97 | } 98 | }) 99 | holochainRunnerHandle.kill() 100 | } 101 | // Wait for the kill commands to complete and exit anyway after a timeout 102 | console.debug('waiting...') 103 | const start_time = Date.now() 104 | while (canWaitForHolochain) { 105 | await sleep(10) 106 | if (Date.now() - start_time > 5 * 1000) { 107 | console.error('Killing sub-processes TIMED-OUT. Aborted.') 108 | break 109 | } 110 | } 111 | console.debug('Killing sub-processes DONE.') 112 | } 113 | 114 | /** 115 | * 116 | */ 117 | export function getRunnerVersion(runnerBinaryPath?: string): string { 118 | const holochainRunnerBinaryPath: string = runnerBinaryPath 119 | ? runnerBinaryPath 120 | : defaultHolochainRunnerBinaryPath 121 | 122 | if (!fs.existsSync(holochainRunnerBinaryPath)) { 123 | console.error( 124 | 'holochain-runner binary not found at path: ' + holochainRunnerBinaryPath 125 | ) 126 | return 'holochain-runner missing' 127 | } 128 | 129 | const holochainHandle = childProcess.spawnSync(holochainRunnerBinaryPath, [ 130 | '--version', 131 | ]) 132 | 133 | if (holochainHandle.error) { 134 | console.error( 135 | 'Calling holochain-runner failed: ' + holochainHandle.error.message 136 | ) 137 | return 'holochain-runner broken' 138 | } 139 | 140 | return holochainHandle.stdout.toString() 141 | } 142 | -------------------------------------------------------------------------------- /src/options.ts: -------------------------------------------------------------------------------- 1 | // translate the options object into 2 | // an array of arguments that get passed to a ChildProcess exec call 3 | // for the holochain-runner binary 4 | export function constructOptions(options: HolochainRunnerOptions): string[] { 5 | let optionsArr = [] 6 | 7 | // optionals 8 | if (options.keystorePath) { 9 | optionsArr = optionsArr.concat(['--keystore-path', options.keystorePath]) 10 | } 11 | if (options.appId) { 12 | optionsArr = optionsArr.concat(['--app-id', options.appId]) 13 | } 14 | if (options.appWsPort) { 15 | optionsArr = optionsArr.concat([ 16 | '--app-ws-port', 17 | options.appWsPort.toString(), 18 | ]) 19 | } 20 | if (options.adminWsPort) { 21 | optionsArr = optionsArr.concat([ 22 | '--admin-ws-port', 23 | options.adminWsPort.toString(), 24 | ]) 25 | } 26 | if (options.webrtcSignalUrl) { 27 | optionsArr = optionsArr.concat(['--webrtc-signal-url', options.webrtcSignalUrl]) 28 | } 29 | // if (options.membraneProof) { 30 | // optionsArr = optionsArr.concat(['--membrane-proof', options.membraneProof]) 31 | // } 32 | if (options.bootstrapUrl) { 33 | optionsArr = optionsArr.concat(['--bootstrap-url', options.bootstrapUrl]) 34 | } 35 | if (options.networkSeed) { 36 | optionsArr = optionsArr.concat(['--network-seed', options.networkSeed]) 37 | } 38 | if (options.gossipArcClamping) { 39 | optionsArr = optionsArr.concat(['--gossip-arc-clamping', options.gossipArcClamping]) 40 | } 41 | if (options.logging) { 42 | optionsArr = optionsArr.concat(['--logging', options.logging]) 43 | } 44 | // happPath is required, and needs to be passed at the end 45 | optionsArr = optionsArr.concat([options.happPath]) 46 | if (options.datastorePath) { 47 | optionsArr = optionsArr.concat([options.datastorePath]) 48 | } 49 | return optionsArr 50 | } 51 | 52 | // match the command line 53 | // options of holochain-runner 54 | export interface HolochainRunnerOptions { 55 | happPath: string 56 | datastorePath?: string 57 | appId?: string 58 | appWsPort?: number 59 | adminWsPort?: number 60 | webrtcSignalUrl?: string 61 | bootstrapUrl?: string 62 | networkSeed?: string 63 | keystorePath?: string 64 | gossipArcClamping?: "full" | "empty" | "none" 65 | logging?: 'None' | 'Log' | 'Compact' | 'Json' 66 | } 67 | 68 | // exposing this externally instead 69 | export interface ExternalOnlyOptions { 70 | passphrase: string 71 | } 72 | 73 | export type ElectronHolochainOptions = HolochainRunnerOptions & ExternalOnlyOptions 74 | 75 | export interface PathOptions { 76 | holochainRunnerBinaryPath: string, 77 | } 78 | -------------------------------------------------------------------------------- /test/run-test.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import {app} from 'electron' 3 | import initAgent, { 4 | StateSignal, 5 | STATUS_EVENT, 6 | LAIR_SOCKET_EVENT, 7 | } from '../src' 8 | import { ElectronHolochainOptions } from '../src/options' 9 | 10 | const runnerOptions: ElectronHolochainOptions = { 11 | happPath: path.join(__dirname, '../../test/test.happ'), 12 | datastorePath: path.join(__dirname, '../../test/data/databases'), 13 | keystorePath: path.join(__dirname, '../../test/data/keystore'), 14 | passphrase: '1234abcd', 15 | // networkSeed: 'asdfiu12jekljasdf' 16 | // appId?: string 17 | // appWsPort?: number 18 | // adminWsPort?: number 19 | // webrtcSignalUrl?: string 20 | } 21 | 22 | app.on('ready', async () => { 23 | try { 24 | const { statusEmitter } = await initAgent(app, runnerOptions) 25 | statusEmitter.on(STATUS_EVENT, (state: StateSignal) => { 26 | console.log('holochain-runner state:', state) 27 | }) 28 | 29 | statusEmitter.on(LAIR_SOCKET_EVENT, (socket: string) => { 30 | console.log('lair socket path:', socket) 31 | }) 32 | } catch (e) { 33 | console.log(e) 34 | } 35 | }) -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*", "test/**/*.ts"], 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "module": "CommonJS", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "sourceMap": true, 10 | "outDir": "dist" 11 | } 12 | } --------------------------------------------------------------------------------