From 72507d3e03c74a7004b1f42b48bc95b80cdbea42 Mon Sep 17 00:00:00 2001 From: Brian Ingles Date: Tue, 22 Oct 2024 09:23:34 -0500 Subject: [PATCH] feat: @deephaven/jsapi-nodejs npm package (#2260) @deephaven/jsapi-nodejs npm package This is initially to serve the vscode extension but should work for any nodejs based app wanting to consume DH jsapis. --- package-lock.json | 667 +++++++++++++++++++++++ package.json | 1 + packages/jsapi-nodejs/README.md | 32 ++ packages/jsapi-nodejs/package.json | 38 ++ packages/jsapi-nodejs/src/errorUtils.ts | 34 ++ packages/jsapi-nodejs/src/fsUtils.ts | 29 + packages/jsapi-nodejs/src/index.ts | 5 + packages/jsapi-nodejs/src/loaderUtils.ts | 95 ++++ packages/jsapi-nodejs/src/polyfillWs.ts | 11 + packages/jsapi-nodejs/src/serverUtils.ts | 145 +++++ packages/jsapi-nodejs/tsconfig.json | 9 + 11 files changed, 1066 insertions(+) create mode 100644 packages/jsapi-nodejs/README.md create mode 100644 packages/jsapi-nodejs/package.json create mode 100644 packages/jsapi-nodejs/src/errorUtils.ts create mode 100644 packages/jsapi-nodejs/src/fsUtils.ts create mode 100644 packages/jsapi-nodejs/src/index.ts create mode 100644 packages/jsapi-nodejs/src/loaderUtils.ts create mode 100644 packages/jsapi-nodejs/src/polyfillWs.ts create mode 100644 packages/jsapi-nodejs/src/serverUtils.ts create mode 100644 packages/jsapi-nodejs/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 504c1d2d68..9332be26cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@deephaven/iris-grid": "file:packages/iris-grid", "@deephaven/jsapi-bootstrap": "file:packages/jsapi-bootstrap", "@deephaven/jsapi-components": "file:packages/jsapi-components", + "@deephaven/jsapi-nodejs": "file:packages/jsapi-nodejs", "@deephaven/jsapi-shim": "file:packages/jsapi-shim", "@deephaven/jsapi-types": "^1.0.0-dev0.34.0", "@deephaven/jsapi-utils": "file:packages/jsapi-utils", @@ -2103,6 +2104,10 @@ "resolved": "packages/jsapi-components", "link": true }, + "node_modules/@deephaven/jsapi-nodejs": { + "resolved": "packages/jsapi-nodejs", + "link": true + }, "node_modules/@deephaven/jsapi-shim": { "resolved": "packages/jsapi-shim", "link": true @@ -2501,6 +2506,21 @@ "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", @@ -9658,6 +9678,15 @@ "version": "2.0.6", "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", @@ -30374,6 +30403,434 @@ "react": ">=16.8.0" } }, + "packages/jsapi-nodejs": { + "name": "@deephaven/jsapi-nodejs", + "version": "0.96.0", + "license": "Apache-2.0", + "dependencies": { + "esbuild": "^0.24.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@types/node": "^22.7.5", + "@types/ws": "^8.5.12" + }, + "engines": { + "node": ">=16" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "packages/jsapi-nodejs/node_modules/@types/node": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", + "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "packages/jsapi-nodejs/node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "packages/jsapi-nodejs/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "packages/jsapi-shim": { "name": "@deephaven/jsapi-shim", "version": "0.96.0", @@ -32623,6 +33080,201 @@ "react-test-renderer": "^17.0.2" } }, + "@deephaven/jsapi-nodejs": { + "version": "file:packages/jsapi-nodejs", + "requires": { + "@types/node": "^22.7.5", + "@types/ws": "^8.5.12", + "esbuild": "^0.24.0", + "ws": "^8.18.0" + }, + "dependencies": { + "@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "optional": true + }, + "@types/node": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", + "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "dev": true, + "requires": { + "undici-types": "~6.19.2" + } + }, + "esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "requires": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "requires": {} + } + } + }, "@deephaven/jsapi-shim": { "version": "file:packages/jsapi-shim", "requires": { @@ -32902,6 +33554,12 @@ "dev": true, "optional": true }, + "@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "optional": true + }, "@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", @@ -38349,6 +39007,15 @@ "@types/unist": { "version": "2.0.6" }, + "@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "17.0.19", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", diff --git a/package.json b/package.json index 9c78c56895..2b90a0a01e 100644 --- a/package.json +++ b/package.json @@ -174,6 +174,7 @@ "@deephaven/iris-grid": "file:packages/iris-grid", "@deephaven/jsapi-bootstrap": "file:packages/jsapi-bootstrap", "@deephaven/jsapi-components": "file:packages/jsapi-components", + "@deephaven/jsapi-nodejs": "file:packages/jsapi-nodejs", "@deephaven/jsapi-shim": "file:packages/jsapi-shim", "@deephaven/jsapi-types": "^1.0.0-dev0.34.0", "@deephaven/jsapi-utils": "file:packages/jsapi-utils", diff --git a/packages/jsapi-nodejs/README.md b/packages/jsapi-nodejs/README.md new file mode 100644 index 0000000000..5a3b6b4429 --- /dev/null +++ b/packages/jsapi-nodejs/README.md @@ -0,0 +1,32 @@ +# @deephaven/jsapi-components + +Deephaven utils for consuming Jsapi from a server from a nodejs app. It can +optionally convert the server module format from `ESM` -> `CJS` or `CJS` -> `ESM` +if the server and consumer don't use the same module format. + +## Install + +```bash +npm install --save @deephaven/jsapi-nodejs +``` + +## Usage + +```typescript +import fs from 'node:fs'; +import path from 'node:path'; + +import { loadModules } from '@deephaven/jsapi-nodejs'; + +const tmpDir = path.join(__dirname, 'tmp'); + +// Download jsapi `ESM` files from DH Community server and export as `CJS` module. +const dhc = await loadModules({ + serverUrl: new URL('http://localhost:10000'), + serverPaths: ['jsapi/dh-core.js', 'jsapi/dh-internal.js'], + download: true, + storageDir: tmpDir, + sourceModuleType: 'esm', + targetModuleType: 'cjs', +}); +``` \ No newline at end of file diff --git a/packages/jsapi-nodejs/package.json b/packages/jsapi-nodejs/package.json new file mode 100644 index 0000000000..caec47bc22 --- /dev/null +++ b/packages/jsapi-nodejs/package.json @@ -0,0 +1,38 @@ +{ + "name": "@deephaven/jsapi-nodejs", + "version": "0.96.0", + "description": "Deephaven utils for consuming Jsapi from a server", + "author": "Deephaven Data Labs LLC", + "license": "Apache-2.0", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/deephaven/web-client-ui.git", + "directory": "packages/jsapi-nodejs" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "source": "src/index.ts", + "engines": { + "node": ">=16" + }, + "scripts": { + "build": "cross-env NODE_ENV=production run-p build:*", + "build:babel": "babel ./src --out-dir ./dist --extensions \".ts,.tsx,.js,.jsx\" --source-maps --root-mode upward" + }, + "dependencies": { + "esbuild": "^0.24.0", + "ws": "^8.18.0" + }, + "devDependencies": { + "@types/node": "^22.7.5", + "@types/ws": "^8.5.12" + }, + "files": [ + "dist" + ], + "sideEffects": false, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/jsapi-nodejs/src/errorUtils.ts b/packages/jsapi-nodejs/src/errorUtils.ts new file mode 100644 index 0000000000..e71ec2a52e --- /dev/null +++ b/packages/jsapi-nodejs/src/errorUtils.ts @@ -0,0 +1,34 @@ +/** + * Return true if given error has a code:string prop. Optionally check if the + * code matches a given value. + * @param err Error to check + * @param code Optional code to check + */ +export function hasErrorCode( + err: unknown, + code?: string +): err is { code: string } { + if ( + err != null && + typeof err === 'object' && + 'code' in err && + typeof err.code === 'string' + ) { + return code == null || err.code === code; + } + + return false; +} + +/** + * Returns true if the given error is an AggregateError. Optionally checks if + * a given code matches the error's code. + * @param err Error to check + * @param code Optional code to check + */ +export function isAggregateError( + err: unknown, + code?: string +): err is { code: string } { + return hasErrorCode(err, code) && String(err) === 'AggregateError'; +} diff --git a/packages/jsapi-nodejs/src/fsUtils.ts b/packages/jsapi-nodejs/src/fsUtils.ts new file mode 100644 index 0000000000..74e434cbad --- /dev/null +++ b/packages/jsapi-nodejs/src/fsUtils.ts @@ -0,0 +1,29 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +/** + * Create directories if they do not exist. + * @param dirPaths The paths of the directories to create. + */ +export function ensureDirectoriesExist(dirPaths: string[]): void { + dirPaths.forEach(dirPath => { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } + }); +} + +/** + * Get download paths for a list of server paths. + * @param targetDir The directory to download the files to. + * @param serverPaths The paths of the files on the server. + * @returns The paths to download the files to. + */ +export function getDownloadPaths( + targetDir: string, + serverPaths: string[] +): string[] { + return serverPaths.map(filePath => + path.join(targetDir, path.basename(filePath)) + ); +} diff --git a/packages/jsapi-nodejs/src/index.ts b/packages/jsapi-nodejs/src/index.ts new file mode 100644 index 0000000000..951e56337b --- /dev/null +++ b/packages/jsapi-nodejs/src/index.ts @@ -0,0 +1,5 @@ +export * from './errorUtils'; +export * from './fsUtils'; +export * from './loaderUtils'; +export * from './polyfillWs'; +export * from './serverUtils'; diff --git a/packages/jsapi-nodejs/src/loaderUtils.ts b/packages/jsapi-nodejs/src/loaderUtils.ts new file mode 100644 index 0000000000..6de081c326 --- /dev/null +++ b/packages/jsapi-nodejs/src/loaderUtils.ts @@ -0,0 +1,95 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import esbuild from 'esbuild'; + +import { downloadFromURL, urlToDirectoryName } from './serverUtils'; +import { polyfillWs } from './polyfillWs'; +import { ensureDirectoriesExist, getDownloadPaths } from './fsUtils'; + +type NonEmptyArray = [T, ...T[]]; + +/** + * Load a list of modules from a server. + * @param serverUrl The URL of the server to load from. + * @param serverPaths The paths of the modules on the server. + * @param download Whether to download the modules from the server. If set to false, + * it's assumed that the modules have already been downloaded and still exist in + * the storage directory. + * @param storageDir The directory to store the downloaded modules. + * @param sourceModuleType module format from the server. + * @param targetFormat (optional) format to be exported. Defaults to + * sourceModuleType. + * @returns The default export of the first module in `serverPaths`. + */ +export async function loadModules({ + serverUrl, + serverPaths, + download, + storageDir, + sourceModuleType, + targetModuleType = sourceModuleType, +}: { + serverUrl: URL; + serverPaths: NonEmptyArray; + download: boolean; + storageDir: string; + sourceModuleType: 'esm' | 'cjs'; + targetModuleType?: 'esm' | 'cjs'; +}): Promise { + polyfillWs(); + + const serverStorageDir = path.join(storageDir, urlToDirectoryName(serverUrl)); + const targetDir = path.join(serverStorageDir, 'target'); + + if (download) { + const needsTranspile = sourceModuleType !== targetModuleType; + const sourceDir = path.join(serverStorageDir, 'source'); + + ensureDirectoriesExist( + needsTranspile ? [sourceDir, targetDir] : [targetDir] + ); + + // Download from server + const serverUrls = serverPaths.map( + serverPath => new URL(serverPath, serverUrl) + ); + const contents = await Promise.all( + serverUrls.map(url => downloadFromURL(url)) + ); + + // Write to disk + const downloadPaths = getDownloadPaths( + needsTranspile ? sourceDir : targetDir, + serverPaths + ); + downloadPaths.forEach((downloadPath, i) => { + fs.writeFileSync(downloadPath, contents[i]); + }); + + // Transpile if source and target module types differ + if (needsTranspile) { + await esbuild.build({ + entryPoints: downloadPaths, + bundle: false, + format: targetModuleType, + logLevel: 'error', + platform: 'node', + outdir: targetDir, + }); + } + } + + // We assume the first module is the main module + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const firstFileName = serverPaths[0].split('/').pop()!; + const mainModulePath = path.join(targetDir, firstFileName); + + if (targetModuleType === 'esm') { + return import(mainModulePath); + } + + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(mainModulePath); +} + +export default loadModules; diff --git a/packages/jsapi-nodejs/src/polyfillWs.ts b/packages/jsapi-nodejs/src/polyfillWs.ts new file mode 100644 index 0000000000..73861f4211 --- /dev/null +++ b/packages/jsapi-nodejs/src/polyfillWs.ts @@ -0,0 +1,11 @@ +import ws from 'ws'; + +export function polyfillWs(): void { + if (globalThis.WebSocket == null) { + // Newer versions of NodeJS should eventually have first-class support for + // WebSocket, but for older versions as late as v20, we need to polyfill it. + globalThis.WebSocket = ws as unknown as (typeof globalThis)['WebSocket']; + } +} + +export default polyfillWs; diff --git a/packages/jsapi-nodejs/src/serverUtils.ts b/packages/jsapi-nodejs/src/serverUtils.ts new file mode 100644 index 0000000000..e40310dd69 --- /dev/null +++ b/packages/jsapi-nodejs/src/serverUtils.ts @@ -0,0 +1,145 @@ +import * as http from 'node:http'; +import * as https from 'node:https'; +import { hasErrorCode, isAggregateError } from './errorUtils'; + +export const SERVER_STATUS_CHECK_TIMEOUT = 3000; + +/** + * Require a JS module from a URL. Loads the module in memory and returns its exports + * Copy / modified from https://github.com/deephaven/deephaven.io/blob/main/tools/run-examples/includeAPI.mjs + * + * @param url The URL with protocol to require from. Supports http or https + * @param retries The number of retries on failure + * @param retryDelay The delay between retries in milliseconds + * @param logger An optional logger object. Defaults to `console` + * @returns Promise which resolves to the module's exports + */ +export async function downloadFromURL( + url: URL, + retries = 10, + retryDelay = 1000, + logger: { error: (...args: unknown[]) => void } = console +): Promise { + return new Promise((resolve, reject) => { + const urlObj = new URL(url); + + let transporter: typeof http | typeof https; + if (urlObj.protocol === 'http:') { + transporter = http; + } else if (urlObj.protocol === 'https:') { + transporter = https; + } else { + reject( + new Error( + `Only http: and https: protocols are supported. Received ${urlObj.protocol}` + ) + ); + return; + } + + transporter + .get(url, { timeout: 5000 }, res => { + let file = ''; + res.on('data', d => { + file += d; + }); + + res.on('end', async () => { + resolve(file); + }); + }) + .on('timeout', () => { + logger.error('Failed download of url:', url); + reject(); + }) + .on('error', e => { + if (retries > 0) { + logger.error('Retrying url:', url); + setTimeout( + () => + downloadFromURL(url, retries - 1, retryDelay, logger).then( + resolve, + reject + ), + retryDelay + ); + } else { + logger.error( + `Hit retry limit. Stopping attempted include from ${url} with error` + ); + logger.error(e); + reject(e); + } + }); + }); +} + +/** + * Check if a given url returns an expected status code. + * @param url The URL to check + * @param statusCodes The expected status codes + * @param logger An optional logger object. Defaults to `console` + * @returns Promise which resolves to true if the status code matches, false otherwise + */ +export async function hasStatusCode( + url: URL, + statusCodes: number[], + logger: { error: (...args: unknown[]) => void } = console +): Promise { + return new Promise(resolve => { + const transporter = url.protocol === 'http:' ? http : https; + + const request = transporter + .request( + url, + // Using OPTIONS method to avoid downloading the entire file. Could also + // use HEAD, but the response seems slightly smaller for OPTIONS. + { method: 'OPTIONS', timeout: SERVER_STATUS_CHECK_TIMEOUT }, + res => { + removeListenersAndResolve( + statusCodes.includes(res.statusCode as number) + ); + } + ) + .on('timeout', () => { + removeListenersAndResolve(false); + }) + .on('error', err => { + // Expected errors for non-existing / stopped servers. + const isServerStoppedError = isAggregateError(err, 'ECONNREFUSED'); + const isServerNotFoundError = hasErrorCode(err, 'ENOTFOUND'); + + if (!isServerStoppedError && !isServerNotFoundError) { + logger.error('Error when checking:', url.toString(), err); + } + + removeListenersAndResolve(false); + }) + .end(); + + /** + * Any time we resolve the Promise, remove listeners to avoid handling + * additional events and destroy the request stream to avoid any additional + * processing. + */ + function removeListenersAndResolve(value: boolean): void { + request.removeAllListeners(); + request.destroy(); + + resolve(value); + } + }); +} + +/** + * Converts url to `${hostname}_${port}` replacing `.` with `_` + * @param url The URL to convert + */ +export function urlToDirectoryName(url: string | URL): string { + if (typeof url === 'string') { + // eslint-disable-next-line no-param-reassign + url = new URL(url); + } + + return url.host.replace(/[:.]/g, '_'); +} diff --git a/packages/jsapi-nodejs/tsconfig.json b/packages/jsapi-nodejs/tsconfig.json new file mode 100644 index 0000000000..8c183dff36 --- /dev/null +++ b/packages/jsapi-nodejs/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src/", + "outDir": "dist/" + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], + "exclude": ["node_modules", "src/**/*.test.*", "src/**/__mocks__/*"] +}