feat: sign-in nip7
This commit is contained in:
parent
fb438c0dbc
commit
eae46663d5
@ -10,7 +10,7 @@ steps:
|
|||||||
- name: build
|
- name: build
|
||||||
image: docker
|
image: docker
|
||||||
privileged: true
|
privileged: true
|
||||||
environment:
|
environment:
|
||||||
TOKEN:
|
TOKEN:
|
||||||
from_secret: registry_token
|
from_secret: registry_token
|
||||||
commands:
|
commands:
|
||||||
@ -18,4 +18,4 @@ steps:
|
|||||||
- docker login -u registry -p $TOKEN registry.v0l.io
|
- docker login -u registry -p $TOKEN registry.v0l.io
|
||||||
- docker build -t registry.v0l.io/lnvps-web:latest .
|
- docker build -t registry.v0l.io/lnvps-web:latest .
|
||||||
- docker push registry.v0l.io/lnvps-web:latest
|
- docker push registry.v0l.io/lnvps-web:latest
|
||||||
- kill $(cat /var/run/docker.pid)
|
- kill $(cat /var/run/docker.pid)
|
||||||
|
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@ -1,6 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": ["arcanis.vscode-zipfs", "dbaeumer.vscode-eslint"]
|
||||||
"arcanis.vscode-zipfs",
|
|
||||||
"dbaeumer.vscode-eslint"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
12
.yarn/sdks/eslint/bin/eslint.js
vendored
12
.yarn/sdks/eslint/bin/eslint.js
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
// Defer to the real eslint/bin/eslint.js your application uses
|
// Defer to the real eslint/bin/eslint.js your application uses
|
||||||
module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`));
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`));
|
||||||
|
12
.yarn/sdks/eslint/lib/api.js
vendored
12
.yarn/sdks/eslint/lib/api.js
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
// Defer to the real eslint your application uses
|
// Defer to the real eslint your application uses
|
||||||
module.exports = wrapWithUserWrapper(absRequire(`eslint`));
|
module.exports = wrapWithUserWrapper(absRequire(`eslint`));
|
||||||
|
12
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
12
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
// Defer to the real eslint/use-at-your-own-risk your application uses
|
// Defer to the real eslint/use-at-your-own-risk your application uses
|
||||||
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));
|
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));
|
||||||
|
12
.yarn/sdks/typescript/bin/tsc
vendored
12
.yarn/sdks/typescript/bin/tsc
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
// Defer to the real typescript/bin/tsc your application uses
|
// Defer to the real typescript/bin/tsc your application uses
|
||||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`));
|
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`));
|
||||||
|
12
.yarn/sdks/typescript/bin/tsserver
vendored
12
.yarn/sdks/typescript/bin/tsserver
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
// Defer to the real typescript/bin/tsserver your application uses
|
// Defer to the real typescript/bin/tsserver your application uses
|
||||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`));
|
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`));
|
||||||
|
12
.yarn/sdks/typescript/lib/tsc.js
vendored
12
.yarn/sdks/typescript/lib/tsc.js
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
// Defer to the real typescript/lib/tsc.js your application uses
|
// Defer to the real typescript/lib/tsc.js your application uses
|
||||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`));
|
module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`));
|
||||||
|
197
.yarn/sdks/typescript/lib/tsserver.js
vendored
197
.yarn/sdks/typescript/lib/tsserver.js
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,28 +25,30 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
const moduleWrapper = exports => {
|
const moduleWrapper = (exports) => {
|
||||||
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||||
};
|
};
|
||||||
|
|
||||||
const moduleWrapperFn = tsserver => {
|
const moduleWrapperFn = (tsserver) => {
|
||||||
if (!process.versions.pnp) {
|
if (!process.versions.pnp) {
|
||||||
return tsserver;
|
return tsserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {isAbsolute} = require(`path`);
|
const { isAbsolute } = require(`path`);
|
||||||
const pnpApi = require(`pnpapi`);
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
const isPortal = str => str.startsWith("portal:/");
|
const isPortal = (str) => str.startsWith("portal:/");
|
||||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
const dependencyTreeRoots = new Set(
|
||||||
return `${locator.name}@${locator.reference}`;
|
pnpApi.getDependencyTreeRoots().map((locator) => {
|
||||||
}));
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
// doesn't understand. This layer makes sure to remove the protocol
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
@ -54,7 +56,11 @@ const moduleWrapperFn = tsserver => {
|
|||||||
|
|
||||||
function toEditorPath(str) {
|
function toEditorPath(str) {
|
||||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
if (
|
||||||
|
isAbsolute(str) &&
|
||||||
|
!str.match(/^\^?(zip:|\/zip\/)/) &&
|
||||||
|
(str.match(/\.zip\//) || isVirtual(str))
|
||||||
|
) {
|
||||||
// We also take the opportunity to turn virtual paths into physical ones;
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
// this makes it much easier to work with workspaces that list peer
|
// this makes it much easier to work with workspaces that list peer
|
||||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
@ -68,7 +74,11 @@ const moduleWrapperFn = tsserver => {
|
|||||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
const locator = pnpApi.findPackageLocator(resolved);
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
if (
|
||||||
|
locator &&
|
||||||
|
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) ||
|
||||||
|
isPortal(locator.reference))
|
||||||
|
) {
|
||||||
str = resolved;
|
str = resolved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,41 +106,55 @@ const moduleWrapperFn = tsserver => {
|
|||||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
//
|
//
|
||||||
case `vscode <1.61`: {
|
case `vscode <1.61`:
|
||||||
str = `^zip:${str}`;
|
{
|
||||||
} break;
|
str = `^zip:${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode <1.66`: {
|
case `vscode <1.66`:
|
||||||
str = `^/zip/${str}`;
|
{
|
||||||
} break;
|
str = `^/zip/${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode <1.68`: {
|
case `vscode <1.68`:
|
||||||
str = `^/zip${str}`;
|
{
|
||||||
} break;
|
str = `^/zip${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode`: {
|
case `vscode`:
|
||||||
str = `^/zip/${str}`;
|
{
|
||||||
} break;
|
str = `^/zip/${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// To make "go to definition" work,
|
// To make "go to definition" work,
|
||||||
// We have to resolve the actual file system path from virtual path
|
// We have to resolve the actual file system path from virtual path
|
||||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
case `coc-nvim`: {
|
case `coc-nvim`:
|
||||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
{
|
||||||
str = resolve(`zipfile:${str}`);
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
} break;
|
str = resolve(`zipfile:${str}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
// We have to resolve the actual file system path from virtual path,
|
// We have to resolve the actual file system path from virtual path,
|
||||||
// everything else is up to neovim
|
// everything else is up to neovim
|
||||||
case `neovim`: {
|
case `neovim`:
|
||||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
{
|
||||||
str = `zipfile://${str}`;
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
} break;
|
str = `zipfile://${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default: {
|
default:
|
||||||
str = `zip:${str}`;
|
{
|
||||||
} break;
|
str = `zip:${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||||
@ -142,26 +166,35 @@ const moduleWrapperFn = tsserver => {
|
|||||||
|
|
||||||
function fromEditorPath(str) {
|
function fromEditorPath(str) {
|
||||||
switch (hostInfo) {
|
switch (hostInfo) {
|
||||||
case `coc-nvim`: {
|
case `coc-nvim`:
|
||||||
str = str.replace(/\.zip::/, `.zip/`);
|
{
|
||||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
// So in order to convert it back, we use .* to match all the thing
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
// before `zipfile:`
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
return process.platform === `win32`
|
// before `zipfile:`
|
||||||
? str.replace(/^.*zipfile:\//, ``)
|
return process.platform === `win32`
|
||||||
: str.replace(/^.*zipfile:/, ``);
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
} break;
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `neovim`: {
|
case `neovim`:
|
||||||
str = str.replace(/\.zip::/, `.zip/`);
|
{
|
||||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
return str.replace(/^zipfile:\/\//, ``);
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
} break;
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode`:
|
case `vscode`:
|
||||||
default: {
|
default:
|
||||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
{
|
||||||
} break;
|
return str.replace(
|
||||||
|
/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/,
|
||||||
|
process.platform === `win32` ? `` : `/`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +206,9 @@ const moduleWrapperFn = tsserver => {
|
|||||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
// https://github.com/microsoft/vscode/issues/45856
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } =
|
||||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||||
this.projectService.allowLocalPluginLoads = true;
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
};
|
};
|
||||||
@ -184,12 +218,13 @@ const moduleWrapperFn = tsserver => {
|
|||||||
// like an absolute path of ours and normalize it.
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
const Session = tsserver.server.Session;
|
const Session = tsserver.server.Session;
|
||||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
const { onMessage: originalOnMessage, send: originalSend } =
|
||||||
|
Session.prototype;
|
||||||
let hostInfo = `unknown`;
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
Object.assign(Session.prototype, {
|
Object.assign(Session.prototype, {
|
||||||
onMessage(/** @type {string | object} */ message) {
|
onMessage(/** @type {string | object} */ message) {
|
||||||
const isStringMessage = typeof message === 'string';
|
const isStringMessage = typeof message === "string";
|
||||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -200,10 +235,12 @@ const moduleWrapperFn = tsserver => {
|
|||||||
) {
|
) {
|
||||||
hostInfo = parsedMessage.arguments.hostInfo;
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
const [, major, minor] = (
|
||||||
// The RegExp from https://semver.org/ but without the caret at the start
|
process.env.VSCODE_IPC_HOOK.match(
|
||||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
) ?? []).map(Number)
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,
|
||||||
|
) ?? []
|
||||||
|
).map(Number);
|
||||||
|
|
||||||
if (major === 1) {
|
if (major === 1) {
|
||||||
if (minor < 61) {
|
if (minor < 61) {
|
||||||
@ -217,27 +254,39 @@ const moduleWrapperFn = tsserver => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
const processedMessageJSON = JSON.stringify(
|
||||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
parsedMessage,
|
||||||
});
|
(key, value) => {
|
||||||
|
return typeof value === "string" ? fromEditorPath(value) : value;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return originalOnMessage.call(
|
return originalOnMessage.call(
|
||||||
this,
|
this,
|
||||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
isStringMessage
|
||||||
|
? processedMessageJSON
|
||||||
|
: JSON.parse(processedMessageJSON),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
send(/** @type {any} */ msg) {
|
send(/** @type {any} */ msg) {
|
||||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
return originalSend.call(
|
||||||
return typeof value === `string` ? toEditorPath(value) : value;
|
this,
|
||||||
})));
|
JSON.parse(
|
||||||
}
|
JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return tsserver;
|
return tsserver;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
const [major, minor] = absRequire(`typescript/package.json`)
|
||||||
|
.version.split(`.`, 2)
|
||||||
|
.map((value) => parseInt(value, 10));
|
||||||
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||||
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||||
if (major > 5 || (major === 5 && minor >= 5)) {
|
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||||
|
197
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
197
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,28 +25,30 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
const moduleWrapper = exports => {
|
const moduleWrapper = (exports) => {
|
||||||
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||||
};
|
};
|
||||||
|
|
||||||
const moduleWrapperFn = tsserver => {
|
const moduleWrapperFn = (tsserver) => {
|
||||||
if (!process.versions.pnp) {
|
if (!process.versions.pnp) {
|
||||||
return tsserver;
|
return tsserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {isAbsolute} = require(`path`);
|
const { isAbsolute } = require(`path`);
|
||||||
const pnpApi = require(`pnpapi`);
|
const pnpApi = require(`pnpapi`);
|
||||||
|
|
||||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||||
const isPortal = str => str.startsWith("portal:/");
|
const isPortal = (str) => str.startsWith("portal:/");
|
||||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||||
|
|
||||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
const dependencyTreeRoots = new Set(
|
||||||
return `${locator.name}@${locator.reference}`;
|
pnpApi.getDependencyTreeRoots().map((locator) => {
|
||||||
}));
|
return `${locator.name}@${locator.reference}`;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||||
// doesn't understand. This layer makes sure to remove the protocol
|
// doesn't understand. This layer makes sure to remove the protocol
|
||||||
@ -54,7 +56,11 @@ const moduleWrapperFn = tsserver => {
|
|||||||
|
|
||||||
function toEditorPath(str) {
|
function toEditorPath(str) {
|
||||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
if (
|
||||||
|
isAbsolute(str) &&
|
||||||
|
!str.match(/^\^?(zip:|\/zip\/)/) &&
|
||||||
|
(str.match(/\.zip\//) || isVirtual(str))
|
||||||
|
) {
|
||||||
// We also take the opportunity to turn virtual paths into physical ones;
|
// We also take the opportunity to turn virtual paths into physical ones;
|
||||||
// this makes it much easier to work with workspaces that list peer
|
// this makes it much easier to work with workspaces that list peer
|
||||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||||
@ -68,7 +74,11 @@ const moduleWrapperFn = tsserver => {
|
|||||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
const locator = pnpApi.findPackageLocator(resolved);
|
const locator = pnpApi.findPackageLocator(resolved);
|
||||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
if (
|
||||||
|
locator &&
|
||||||
|
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) ||
|
||||||
|
isPortal(locator.reference))
|
||||||
|
) {
|
||||||
str = resolved;
|
str = resolved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,41 +106,55 @@ const moduleWrapperFn = tsserver => {
|
|||||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||||
//
|
//
|
||||||
case `vscode <1.61`: {
|
case `vscode <1.61`:
|
||||||
str = `^zip:${str}`;
|
{
|
||||||
} break;
|
str = `^zip:${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode <1.66`: {
|
case `vscode <1.66`:
|
||||||
str = `^/zip/${str}`;
|
{
|
||||||
} break;
|
str = `^/zip/${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode <1.68`: {
|
case `vscode <1.68`:
|
||||||
str = `^/zip${str}`;
|
{
|
||||||
} break;
|
str = `^/zip${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode`: {
|
case `vscode`:
|
||||||
str = `^/zip/${str}`;
|
{
|
||||||
} break;
|
str = `^/zip/${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// To make "go to definition" work,
|
// To make "go to definition" work,
|
||||||
// We have to resolve the actual file system path from virtual path
|
// We have to resolve the actual file system path from virtual path
|
||||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||||
case `coc-nvim`: {
|
case `coc-nvim`:
|
||||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
{
|
||||||
str = resolve(`zipfile:${str}`);
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
} break;
|
str = resolve(`zipfile:${str}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||||
// We have to resolve the actual file system path from virtual path,
|
// We have to resolve the actual file system path from virtual path,
|
||||||
// everything else is up to neovim
|
// everything else is up to neovim
|
||||||
case `neovim`: {
|
case `neovim`:
|
||||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
{
|
||||||
str = `zipfile://${str}`;
|
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||||
} break;
|
str = `zipfile://${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default: {
|
default:
|
||||||
str = `zip:${str}`;
|
{
|
||||||
} break;
|
str = `zip:${str}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||||
@ -142,26 +166,35 @@ const moduleWrapperFn = tsserver => {
|
|||||||
|
|
||||||
function fromEditorPath(str) {
|
function fromEditorPath(str) {
|
||||||
switch (hostInfo) {
|
switch (hostInfo) {
|
||||||
case `coc-nvim`: {
|
case `coc-nvim`:
|
||||||
str = str.replace(/\.zip::/, `.zip/`);
|
{
|
||||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
// So in order to convert it back, we use .* to match all the thing
|
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||||
// before `zipfile:`
|
// So in order to convert it back, we use .* to match all the thing
|
||||||
return process.platform === `win32`
|
// before `zipfile:`
|
||||||
? str.replace(/^.*zipfile:\//, ``)
|
return process.platform === `win32`
|
||||||
: str.replace(/^.*zipfile:/, ``);
|
? str.replace(/^.*zipfile:\//, ``)
|
||||||
} break;
|
: str.replace(/^.*zipfile:/, ``);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `neovim`: {
|
case `neovim`:
|
||||||
str = str.replace(/\.zip::/, `.zip/`);
|
{
|
||||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
str = str.replace(/\.zip::/, `.zip/`);
|
||||||
return str.replace(/^zipfile:\/\//, ``);
|
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||||
} break;
|
return str.replace(/^zipfile:\/\//, ``);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case `vscode`:
|
case `vscode`:
|
||||||
default: {
|
default:
|
||||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
{
|
||||||
} break;
|
return str.replace(
|
||||||
|
/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/,
|
||||||
|
process.platform === `win32` ? `` : `/`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +206,9 @@ const moduleWrapperFn = tsserver => {
|
|||||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||||
// https://github.com/microsoft/vscode/issues/45856
|
// https://github.com/microsoft/vscode/issues/45856
|
||||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } =
|
||||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
ConfiguredProject.prototype;
|
||||||
|
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||||
this.projectService.allowLocalPluginLoads = true;
|
this.projectService.allowLocalPluginLoads = true;
|
||||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||||
};
|
};
|
||||||
@ -184,12 +218,13 @@ const moduleWrapperFn = tsserver => {
|
|||||||
// like an absolute path of ours and normalize it.
|
// like an absolute path of ours and normalize it.
|
||||||
|
|
||||||
const Session = tsserver.server.Session;
|
const Session = tsserver.server.Session;
|
||||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
const { onMessage: originalOnMessage, send: originalSend } =
|
||||||
|
Session.prototype;
|
||||||
let hostInfo = `unknown`;
|
let hostInfo = `unknown`;
|
||||||
|
|
||||||
Object.assign(Session.prototype, {
|
Object.assign(Session.prototype, {
|
||||||
onMessage(/** @type {string | object} */ message) {
|
onMessage(/** @type {string | object} */ message) {
|
||||||
const isStringMessage = typeof message === 'string';
|
const isStringMessage = typeof message === "string";
|
||||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -200,10 +235,12 @@ const moduleWrapperFn = tsserver => {
|
|||||||
) {
|
) {
|
||||||
hostInfo = parsedMessage.arguments.hostInfo;
|
hostInfo = parsedMessage.arguments.hostInfo;
|
||||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
const [, major, minor] = (
|
||||||
// The RegExp from https://semver.org/ but without the caret at the start
|
process.env.VSCODE_IPC_HOOK.match(
|
||||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
// The RegExp from https://semver.org/ but without the caret at the start
|
||||||
) ?? []).map(Number)
|
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,
|
||||||
|
) ?? []
|
||||||
|
).map(Number);
|
||||||
|
|
||||||
if (major === 1) {
|
if (major === 1) {
|
||||||
if (minor < 61) {
|
if (minor < 61) {
|
||||||
@ -217,27 +254,39 @@ const moduleWrapperFn = tsserver => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
const processedMessageJSON = JSON.stringify(
|
||||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
parsedMessage,
|
||||||
});
|
(key, value) => {
|
||||||
|
return typeof value === "string" ? fromEditorPath(value) : value;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return originalOnMessage.call(
|
return originalOnMessage.call(
|
||||||
this,
|
this,
|
||||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
isStringMessage
|
||||||
|
? processedMessageJSON
|
||||||
|
: JSON.parse(processedMessageJSON),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
send(/** @type {any} */ msg) {
|
send(/** @type {any} */ msg) {
|
||||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
return originalSend.call(
|
||||||
return typeof value === `string` ? toEditorPath(value) : value;
|
this,
|
||||||
})));
|
JSON.parse(
|
||||||
}
|
JSON.stringify(msg, (key, value) => {
|
||||||
|
return typeof value === `string` ? toEditorPath(value) : value;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return tsserver;
|
return tsserver;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
const [major, minor] = absRequire(`typescript/package.json`)
|
||||||
|
.version.split(`.`, 2)
|
||||||
|
.map((value) => parseInt(value, 10));
|
||||||
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||||
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||||
if (major > 5 || (major === 5 && minor >= 5)) {
|
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||||
|
12
.yarn/sdks/typescript/lib/typescript.js
vendored
12
.yarn/sdks/typescript/lib/typescript.js
vendored
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const {existsSync} = require(`fs`);
|
const { existsSync } = require(`fs`);
|
||||||
const {createRequire, register} = require(`module`);
|
const { createRequire, register } = require(`module`);
|
||||||
const {resolve} = require(`path`);
|
const { resolve } = require(`path`);
|
||||||
const {pathToFileURL} = require(`url`);
|
const { pathToFileURL } = require(`url`);
|
||||||
|
|
||||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ if (existsSync(absPnpApiPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||||
? exports => absRequire(absUserWrapperPath)(exports)
|
? (exports) => absRequire(absUserWrapperPath)(exports)
|
||||||
: exports => exports;
|
: (exports) => exports;
|
||||||
|
|
||||||
// Defer to the real typescript your application uses
|
// Defer to the real typescript your application uses
|
||||||
module.exports = wrapWithUserWrapper(absRequire(`typescript`));
|
module.exports = wrapWithUserWrapper(absRequire(`typescript`));
|
||||||
|
12
README.md
12
README.md
@ -18,11 +18,11 @@ export default tseslint.config({
|
|||||||
languageOptions: {
|
languageOptions: {
|
||||||
// other options...
|
// other options...
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
project: ["./tsconfig.node.json", "./tsconfig.app.json"],
|
||||||
tsconfigRootDir: import.meta.dirname,
|
tsconfigRootDir: import.meta.dirname,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||||
@ -31,11 +31,11 @@ export default tseslint.config({
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// eslint.config.js
|
// eslint.config.js
|
||||||
import react from 'eslint-plugin-react'
|
import react from "eslint-plugin-react";
|
||||||
|
|
||||||
export default tseslint.config({
|
export default tseslint.config({
|
||||||
// Set the react version
|
// Set the react version
|
||||||
settings: { react: { version: '18.3' } },
|
settings: { react: { version: "18.3" } },
|
||||||
plugins: {
|
plugins: {
|
||||||
// Add the react plugin
|
// Add the react plugin
|
||||||
react,
|
react,
|
||||||
@ -44,7 +44,7 @@ export default tseslint.config({
|
|||||||
// other rules...
|
// other rules...
|
||||||
// Enable its recommended rules
|
// Enable its recommended rules
|
||||||
...react.configs.recommended.rules,
|
...react.configs.recommended.rules,
|
||||||
...react.configs['jsx-runtime'].rules,
|
...react.configs["jsx-runtime"].rules,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
```
|
```
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import js from '@eslint/js'
|
import js from "@eslint/js";
|
||||||
import globals from 'globals'
|
import globals from "globals";
|
||||||
import reactHooks from 'eslint-plugin-react-hooks'
|
import reactHooks from "eslint-plugin-react-hooks";
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
import reactRefresh from "eslint-plugin-react-refresh";
|
||||||
import tseslint from 'typescript-eslint'
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
export default tseslint.config({
|
export default tseslint.config({
|
||||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||||
files: ['**/*.{ts,tsx}'],
|
files: ["**/*.{ts,tsx}"],
|
||||||
ignores: ['dist'],
|
ignores: ["dist"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
globals: globals.browser,
|
globals: globals.browser,
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
'react-hooks': reactHooks,
|
"react-hooks": reactHooks,
|
||||||
'react-refresh': reactRefresh,
|
"react-refresh": reactRefresh,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
...reactHooks.configs.recommended.rules,
|
...reactHooks.configs.recommended.rules,
|
||||||
'react-refresh/only-export-components': [
|
"react-refresh/only-export-components": [
|
||||||
'warn',
|
"warn",
|
||||||
{ allowConstantExport: true },
|
{ allowConstantExport: true },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
57
index.html
57
index.html
@ -1,31 +1,32 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="Bitcoin Lightning VPS provider" />
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="bitcoin lightning fast cheap vps virtual private server hosting web"
|
||||||
|
/>
|
||||||
|
<meta property="og:url" content="https://lnvps.net" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content="LNVPS" />
|
||||||
|
<meta property="og:description" content="Bitcoin Lightning VPS provider" />
|
||||||
|
<meta property="og:image" content="/logo.png" />
|
||||||
|
<title>LNVPS</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body>
|
||||||
<meta charset="UTF-8" />
|
<div id="root"></div>
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<script src="https://btcpay.v0l.io/modal/btcpay.js"></script>
|
||||||
<meta name="theme-color" content="#000000" />
|
</body>
|
||||||
<meta name="description" content="Bitcoin Lightning VPS provider" />
|
</html>
|
||||||
<meta
|
|
||||||
name="keywords"
|
|
||||||
content="bitcoin lightning fast cheap vps virtual private server hosting web" />
|
|
||||||
<meta property="og:url" content="https://lnvps.net" />
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:title" content="LNVPS" />
|
|
||||||
<meta property="og:description" content="Bitcoin Lightning VPS provider" />
|
|
||||||
<meta property="og:image" content="/logo.png" />
|
|
||||||
<title>LNVPS</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap"
|
|
||||||
rel="stylesheet">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
|
||||||
<script src="https://btcpay.v0l.io/modal/btcpay.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"eslint-plugin-react-refresh": "^0.4.9",
|
"eslint-plugin-react-refresh": "^0.4.9",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"postcss": "^8.4.41",
|
"postcss": "^8.4.41",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
"tailwindcss": "^3.4.8",
|
"tailwindcss": "^3.4.8",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"typescript-eslint": "^8.0.0",
|
"typescript-eslint": "^8.0.0",
|
||||||
|
@ -3,4 +3,4 @@ export default {
|
|||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
57
src/App.tsx
57
src/App.tsx
@ -1,9 +1,12 @@
|
|||||||
import { SnortContext } from "@snort/system-react"
|
import { SnortContext } from "@snort/system-react";
|
||||||
import { CostInterval, DiskType, MachineSpec } from "./api"
|
import { CostInterval, DiskType, MachineSpec } from "./api";
|
||||||
import VpsCard from "./components/vps-card"
|
import VpsCard from "./components/vps-card";
|
||||||
import { GiB, NostrProfile } from "./const"
|
import { GiB, NostrProfile } from "./const";
|
||||||
import { NostrSystem } from "@snort/system"
|
import { NostrSystem } from "@snort/system";
|
||||||
import Profile from "./components/profile"
|
import Profile from "./components/profile";
|
||||||
|
import { AsyncButton } from "./components/button";
|
||||||
|
import { loginNip7 } from "./login";
|
||||||
|
import LoginButton from "./components/login-button";
|
||||||
|
|
||||||
const Offers: Array<MachineSpec> = [
|
const Offers: Array<MachineSpec> = [
|
||||||
{
|
{
|
||||||
@ -13,13 +16,13 @@ const Offers: Array<MachineSpec> = [
|
|||||||
ram: 2 * GiB,
|
ram: 2 * GiB,
|
||||||
disk: {
|
disk: {
|
||||||
type: DiskType.SSD,
|
type: DiskType.SSD,
|
||||||
size: 80 * GiB
|
size: 80 * GiB,
|
||||||
},
|
},
|
||||||
cost: {
|
cost: {
|
||||||
interval: CostInterval.Month,
|
interval: CostInterval.Month,
|
||||||
count: 3,
|
count: 3,
|
||||||
currency: "EUR",
|
currency: "EUR",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "4x4x160",
|
id: "4x4x160",
|
||||||
@ -28,13 +31,13 @@ const Offers: Array<MachineSpec> = [
|
|||||||
ram: 4 * GiB,
|
ram: 4 * GiB,
|
||||||
disk: {
|
disk: {
|
||||||
type: DiskType.SSD,
|
type: DiskType.SSD,
|
||||||
size: 160 * GiB
|
size: 160 * GiB,
|
||||||
},
|
},
|
||||||
cost: {
|
cost: {
|
||||||
interval: CostInterval.Month,
|
interval: CostInterval.Month,
|
||||||
count: 5,
|
count: 5,
|
||||||
currency: "EUR",
|
currency: "EUR",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "8x8x400",
|
id: "8x8x400",
|
||||||
@ -43,50 +46,58 @@ const Offers: Array<MachineSpec> = [
|
|||||||
ram: 8 * GiB,
|
ram: 8 * GiB,
|
||||||
disk: {
|
disk: {
|
||||||
type: DiskType.SSD,
|
type: DiskType.SSD,
|
||||||
size: 400 * GiB
|
size: 400 * GiB,
|
||||||
},
|
},
|
||||||
cost: {
|
cost: {
|
||||||
interval: CostInterval.Month,
|
interval: CostInterval.Month,
|
||||||
count: 12,
|
count: 12,
|
||||||
currency: "EUR",
|
currency: "EUR",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
const system = new NostrSystem({});
|
const system = new NostrSystem({});
|
||||||
[
|
[
|
||||||
"wss://relay.snort.social/",
|
"wss://relay.snort.social/",
|
||||||
"wss://relay.damus.io/",
|
"wss://relay.damus.io/",
|
||||||
"wss://relay.nostr.band/",
|
"wss://relay.nostr.band/",
|
||||||
"wss://nos.lol/"
|
"wss://nos.lol/",
|
||||||
].forEach(a => system.ConnectToRelay(a, { read: true, write: true }));
|
].forEach((a) => system.ConnectToRelay(a, { read: true, write: true }));
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SnortContext.Provider value={system}>
|
<SnortContext.Provider value={system}>
|
||||||
<div className="w-[700px] mx-auto m-2 p-2">
|
<div className="w-[700px] mx-auto m-2 p-2">
|
||||||
<h1>LNVPS</h1>
|
<div className="flex items-center justify-between">
|
||||||
|
LNVPS
|
||||||
|
<LoginButton />
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1>VPS</h1>
|
<h1>VPS Offers</h1>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<div className="grid grid-cols-3 gap-2">
|
||||||
{Offers.map(a => <VpsCard spec={a} />)}
|
{Offers.map((a) => (
|
||||||
|
<VpsCard spec={a} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<b>Please email <a href="mailto:sales@lnvps.net">sales</a> after paying the invoice with your order id, desired OS and ssh key.</b>
|
<b>
|
||||||
|
Please email <a href="mailto:sales@lnvps.net">sales</a> after
|
||||||
|
paying the invoice with your order id, desired OS and ssh key.
|
||||||
|
</b>
|
||||||
<b>You can also find us on nostr: </b>
|
<b>You can also find us on nostr: </b>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Profile link={NostrProfile} />
|
<Profile link={NostrProfile} />
|
||||||
<pre className="overflow-x-scroll">{NostrProfile.encode()}</pre>
|
<pre className="overflow-x-scroll">{NostrProfile.encode()}</pre>
|
||||||
</div>
|
</div>
|
||||||
<small>
|
<small>
|
||||||
All VPS come with 1x IPv4 and 1x IPv6 address and unmetered traffic.
|
All VPS come with 1x IPv4 and 1x IPv6 address and unmetered
|
||||||
|
traffic.
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SnortContext.Provider>
|
</SnortContext.Provider>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
21
src/components/button.tsx
Normal file
21
src/components/button.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { forwardRef, HTMLProps } from "react";
|
||||||
|
|
||||||
|
export type AsyncButtonProps = {
|
||||||
|
onClick?: (e: React.MouseEvent) => Promise<void>;
|
||||||
|
} & Omit<HTMLProps<HTMLButtonElement>, "type" | "ref" | "onClick">;
|
||||||
|
|
||||||
|
const AsyncButton = forwardRef<HTMLButtonElement, AsyncButtonProps>(
|
||||||
|
function AsyncButton(props, ref) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
className="bg-slate-700 py-1 px-2 rounded-xl"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export { AsyncButton };
|
@ -1,19 +1,19 @@
|
|||||||
import { GiB, KiB, MiB, TiB } from "../const"
|
import { GiB, KiB, MiB, TiB } from "../const";
|
||||||
|
|
||||||
interface BytesSizeProps {
|
interface BytesSizeProps {
|
||||||
value: number,
|
value: number;
|
||||||
precision?: number
|
precision?: number;
|
||||||
}
|
}
|
||||||
export default function BytesSize(props: BytesSizeProps) {
|
export default function BytesSize(props: BytesSizeProps) {
|
||||||
if (props.value >= TiB) {
|
if (props.value >= TiB) {
|
||||||
return (props.value / TiB).toFixed(props.precision ?? 0) + "TB";
|
return (props.value / TiB).toFixed(props.precision ?? 0) + "TB";
|
||||||
} else if (props.value >= GiB) {
|
} else if (props.value >= GiB) {
|
||||||
return (props.value / GiB).toFixed(props.precision ?? 0) + "GB";
|
return (props.value / GiB).toFixed(props.precision ?? 0) + "GB";
|
||||||
} else if (props.value >= MiB) {
|
} else if (props.value >= MiB) {
|
||||||
return (props.value / MiB).toFixed(props.precision ?? 0) + "MB";
|
return (props.value / MiB).toFixed(props.precision ?? 0) + "MB";
|
||||||
} else if (props.value >= KiB) {
|
} else if (props.value >= KiB) {
|
||||||
return (props.value / KiB).toFixed(props.precision ?? 0) + "KB";
|
return (props.value / KiB).toFixed(props.precision ?? 0) + "KB";
|
||||||
} else {
|
} else {
|
||||||
return (props.value).toFixed(props.precision ?? 0) + "B";
|
return props.value.toFixed(props.precision ?? 0) + "B";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
import { CostInterval, MachineSpec } from "../api";
|
import { CostInterval, MachineSpec } from "../api";
|
||||||
|
|
||||||
export default function CostLabel({ cost }: { cost: MachineSpec["cost"] }) {
|
export default function CostLabel({ cost }: { cost: MachineSpec["cost"] }) {
|
||||||
function intervalName(n: number) {
|
function intervalName(n: number) {
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case CostInterval.Hour: return "Hour"
|
case CostInterval.Hour:
|
||||||
case CostInterval.Day: return "Day"
|
return "Hour";
|
||||||
case CostInterval.Month: return "Month"
|
case CostInterval.Day:
|
||||||
case CostInterval.Year: return "Year"
|
return "Day";
|
||||||
}
|
case CostInterval.Month:
|
||||||
|
return "Month";
|
||||||
|
case CostInterval.Year:
|
||||||
|
return "Year";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return <>{cost.count} {cost.currency}/{intervalName(cost.interval)}</>
|
return (
|
||||||
}
|
<>
|
||||||
|
{cost.count} {cost.currency}/{intervalName(cost.interval)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
24
src/components/login-button.tsx
Normal file
24
src/components/login-button.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { SnortContext } from "@snort/system-react";
|
||||||
|
import { useContext } from "react";
|
||||||
|
import { AsyncButton } from "./button";
|
||||||
|
import { loginNip7 } from "../login";
|
||||||
|
import useLogin from "../hooks/login";
|
||||||
|
import Profile from "./profile";
|
||||||
|
import { NostrLink } from "@snort/system";
|
||||||
|
|
||||||
|
export default function LoginButton() {
|
||||||
|
const system = useContext(SnortContext);
|
||||||
|
const login = useLogin();
|
||||||
|
|
||||||
|
return !login ? (
|
||||||
|
<AsyncButton
|
||||||
|
onClick={async () => {
|
||||||
|
await loginNip7(system);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</AsyncButton>
|
||||||
|
) : (
|
||||||
|
<Profile link={NostrLink.publicKey(login.pubkey)} />
|
||||||
|
);
|
||||||
|
}
|
@ -1,44 +1,64 @@
|
|||||||
import { MachineSpec } from "../api";
|
import { MachineSpec } from "../api";
|
||||||
|
|
||||||
import "./pay-button.css"
|
import "./pay-button.css";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
btcpay?: {
|
btcpay?: {
|
||||||
appendInvoiceFrame(invoiceId: string): void;
|
appendInvoiceFrame(invoiceId: string): void;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VpsPayButton({ spec }: { spec: MachineSpec }) {
|
export default function VpsPayButton({ spec }: { spec: MachineSpec }) {
|
||||||
const serverUrl = "https://btcpay.v0l.io/api/v1/invoices";
|
const serverUrl = "https://btcpay.v0l.io/api/v1/invoices";
|
||||||
|
|
||||||
function handleFormSubmit(event: React.FormEvent) {
|
function handleFormSubmit(event: React.FormEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const form = event.target as HTMLFormElement;
|
const form = event.target as HTMLFormElement;
|
||||||
const xhttp = new XMLHttpRequest();
|
const xhttp = new XMLHttpRequest();
|
||||||
xhttp.onreadystatechange = function () {
|
xhttp.onreadystatechange = function () {
|
||||||
if (this.readyState == 4 && this.status == 200 && this.responseText) {
|
if (this.readyState == 4 && this.status == 200 && this.responseText) {
|
||||||
window.btcpay?.appendInvoiceFrame(JSON.parse(this.responseText).invoiceId);
|
window.btcpay?.appendInvoiceFrame(
|
||||||
}
|
JSON.parse(this.responseText).invoiceId,
|
||||||
};
|
);
|
||||||
xhttp.open('POST', serverUrl, true);
|
}
|
||||||
xhttp.send(new FormData(form));
|
};
|
||||||
}
|
xhttp.open("POST", serverUrl, true);
|
||||||
|
xhttp.send(new FormData(form));
|
||||||
|
}
|
||||||
|
|
||||||
if (!spec.active) {
|
if (!spec.active) {
|
||||||
return <div className="text-center text-xl uppercase bg-red-800 rounded-xl py-3 font-bold">
|
return (
|
||||||
Unavailable
|
<div className="text-center text-xl uppercase bg-red-800 rounded-xl py-3 font-bold">
|
||||||
</div>
|
Unavailable
|
||||||
}
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return <form method="POST" action={serverUrl} className="btcpay-form btcpay-form--block" onSubmit={handleFormSubmit}>
|
return (
|
||||||
<input type="hidden" name="storeId" value="CdaHy1puLx4kLC9BG3A9mu88XNyLJukMJRuuhAfbDrxg" />
|
<form
|
||||||
<input type="hidden" name="jsonResponse" value="true" />
|
method="POST"
|
||||||
<input type="hidden" name="orderId" value={spec.id} />
|
action={serverUrl}
|
||||||
<input type="hidden" name="price" value={spec.cost.count} />
|
className="btcpay-form btcpay-form--block w-full"
|
||||||
<input type="hidden" name="currency" value={spec.cost.currency} />
|
onSubmit={handleFormSubmit}
|
||||||
<input type="image" className="submit" name="submit" src="https://btcpay.v0l.io/img/paybutton/pay.svg"
|
>
|
||||||
alt="Pay with BTCPay Server, a Self-Hosted Bitcoin Payment Processor" />
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="storeId"
|
||||||
|
value="CdaHy1puLx4kLC9BG3A9mu88XNyLJukMJRuuhAfbDrxg"
|
||||||
|
/>
|
||||||
|
<input type="hidden" name="jsonResponse" value="true" />
|
||||||
|
<input type="hidden" name="orderId" value={spec.id} />
|
||||||
|
<input type="hidden" name="price" value={spec.cost.count} />
|
||||||
|
<input type="hidden" name="currency" value={spec.cost.currency} />
|
||||||
|
<input
|
||||||
|
type="image"
|
||||||
|
className="w-full"
|
||||||
|
name="submit"
|
||||||
|
src="https://btcpay.v0l.io/img/paybutton/pay.svg"
|
||||||
|
alt="Pay with BTCPay Server, a Self-Hosted Bitcoin Payment Processor"
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
@ -3,11 +3,18 @@ import { NostrLink } from "@snort/system";
|
|||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
|
||||||
export default function Profile({ link }: { link: NostrLink }) {
|
export default function Profile({ link }: { link: NostrLink }) {
|
||||||
const profile = useUserProfile(link.id);
|
const profile = useUserProfile(link.id);
|
||||||
return <div className="flex gap-2 items-center">
|
return (
|
||||||
<img src={profile?.picture} className="w-12 h-12 rounded-full bg-neutral-500" />
|
<div className="flex gap-2 items-center">
|
||||||
<div>
|
<img
|
||||||
{profile?.display_name ?? profile?.name ?? hexToBech32("npub", link.id).slice(0, 12)}
|
src={profile?.picture}
|
||||||
</div>
|
className="w-12 h-12 rounded-full bg-neutral-500"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
{profile?.display_name ??
|
||||||
|
profile?.name ??
|
||||||
|
hexToBech32("npub", link.id).slice(0, 12)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
@ -4,14 +4,23 @@ import CostLabel from "./cost";
|
|||||||
import VpsPayButton from "./pay-button";
|
import VpsPayButton from "./pay-button";
|
||||||
|
|
||||||
export default function VpsCard({ spec }: { spec: MachineSpec }) {
|
export default function VpsCard({ spec }: { spec: MachineSpec }) {
|
||||||
return <div className="rounded-xl border border-neutral-600 px-3 py-2">
|
return (
|
||||||
<h2>{spec.id}</h2>
|
<div className="rounded-xl border border-neutral-600 px-3 py-2">
|
||||||
<ul>
|
<h2>{spec.id}</h2>
|
||||||
<li>CPU: {spec.cpu}vCPU</li>
|
<ul>
|
||||||
<li>RAM: <BytesSize value={spec.ram} /></li>
|
<li>CPU: {spec.cpu}vCPU</li>
|
||||||
<li>{spec.disk.type === DiskType.SSD ? "SSD" : "HDD"}: <BytesSize value={spec.disk.size} /></li>
|
<li>
|
||||||
</ul>
|
RAM: <BytesSize value={spec.ram} />
|
||||||
<h2><CostLabel cost={spec.cost} /></h2>
|
</li>
|
||||||
<VpsPayButton spec={spec} />
|
<li>
|
||||||
|
{spec.disk.type === DiskType.SSD ? "SSD" : "HDD"}:{" "}
|
||||||
|
<BytesSize value={spec.disk.size} />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h2>
|
||||||
|
<CostLabel cost={spec.cost} />
|
||||||
|
</h2>
|
||||||
|
<VpsPayButton spec={spec} />
|
||||||
</div>
|
</div>
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
16
src/const.ts
16
src/const.ts
@ -12,9 +12,15 @@ export const GB = KB * 1000;
|
|||||||
export const TB = GB * 1000;
|
export const TB = GB * 1000;
|
||||||
export const PB = TB * 1000;
|
export const PB = TB * 1000;
|
||||||
|
|
||||||
export const NostrProfile = new NostrLink(NostrPrefix.Profile,
|
export const NostrProfile = new NostrLink(
|
||||||
"fcd818454002a6c47a980393f0549ac6e629d28d5688114bb60d831b5c1832a7",
|
NostrPrefix.Profile,
|
||||||
undefined, undefined, [
|
"fcd818454002a6c47a980393f0549ac6e629d28d5688114bb60d831b5c1832a7",
|
||||||
"wss://nos.lol/", "wss://relay.nostr.bg/", "wss://relay.damus.io", "wss://relay.snort.social/"
|
undefined,
|
||||||
]
|
undefined,
|
||||||
|
[
|
||||||
|
"wss://nos.lol/",
|
||||||
|
"wss://relay.nostr.bg/",
|
||||||
|
"wss://relay.damus.io",
|
||||||
|
"wss://relay.snort.social/",
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
12
src/hooks/login.tsx
Normal file
12
src/hooks/login.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { useSyncExternalStore } from "react";
|
||||||
|
import { Login } from "../login";
|
||||||
|
|
||||||
|
export default function useLogin() {
|
||||||
|
return useSyncExternalStore(
|
||||||
|
(c) => {
|
||||||
|
Login?.on("change", c);
|
||||||
|
return () => Login?.off("change", c);
|
||||||
|
},
|
||||||
|
() => Login,
|
||||||
|
);
|
||||||
|
}
|
@ -35,4 +35,4 @@ h3 {
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
14
src/login.ts
Normal file
14
src/login.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Nip7Signer, SystemInterface, UserState } from "@snort/system";
|
||||||
|
|
||||||
|
export let Login: UserState<void> | undefined;
|
||||||
|
|
||||||
|
export async function loginNip7(system: SystemInterface) {
|
||||||
|
const signer = new Nip7Signer();
|
||||||
|
const pubkey = await signer.getPubKey();
|
||||||
|
if (pubkey) {
|
||||||
|
Login = new UserState<void>(pubkey);
|
||||||
|
await Login.init(signer, system);
|
||||||
|
} else {
|
||||||
|
throw new Error("No nostr extension found");
|
||||||
|
}
|
||||||
|
}
|
12
src/main.tsx
12
src/main.tsx
@ -1,10 +1,10 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from "react-dom/client";
|
||||||
import App from './App.tsx'
|
import App from "./App.tsx";
|
||||||
import './index.css'
|
import "./index.css";
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
);
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
"./index.html",
|
|
||||||
"./src/**/*.{js,ts,jsx,tsx}",
|
|
||||||
],
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from "vite";
|
||||||
import react from '@vitejs/plugin-react'
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
})
|
});
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -2548,6 +2548,7 @@ __metadata:
|
|||||||
eslint-plugin-react-refresh: "npm:^0.4.9"
|
eslint-plugin-react-refresh: "npm:^0.4.9"
|
||||||
globals: "npm:^15.9.0"
|
globals: "npm:^15.9.0"
|
||||||
postcss: "npm:^8.4.41"
|
postcss: "npm:^8.4.41"
|
||||||
|
prettier: "npm:^3.3.3"
|
||||||
react: "npm:^18.3.1"
|
react: "npm:^18.3.1"
|
||||||
react-dom: "npm:^18.3.1"
|
react-dom: "npm:^18.3.1"
|
||||||
tailwindcss: "npm:^3.4.8"
|
tailwindcss: "npm:^3.4.8"
|
||||||
@ -3146,6 +3147,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"prettier@npm:^3.3.3":
|
||||||
|
version: 3.3.3
|
||||||
|
resolution: "prettier@npm:3.3.3"
|
||||||
|
bin:
|
||||||
|
prettier: bin/prettier.cjs
|
||||||
|
checksum: 10c0/b85828b08e7505716324e4245549b9205c0cacb25342a030ba8885aba2039a115dbcf75a0b7ca3b37bc9d101ee61fab8113fc69ca3359f2a226f1ecc07ad2e26
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"proc-log@npm:^4.1.0, proc-log@npm:^4.2.0":
|
"proc-log@npm:^4.1.0, proc-log@npm:^4.2.0":
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
resolution: "proc-log@npm:4.2.0"
|
resolution: "proc-log@npm:4.2.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user