mirror of
https://github.com/EthanShoeDev/fressh.git
synced 2026-01-11 06:12:51 +00:00
release configs
This commit is contained in:
@@ -1,42 +1,42 @@
|
||||
import type { Config } from 'release-it';
|
||||
|
||||
export default {
|
||||
git: {
|
||||
requireCleanWorkingDir: true,
|
||||
tagName: '${npm.name}-v${version}',
|
||||
tagAnnotation: '${npm.name} v${version}',
|
||||
tagMatch: '${npm.name}-v*',
|
||||
commitMessage: 'chore(${npm.name}): release v${version}',
|
||||
push: true,
|
||||
},
|
||||
git: {
|
||||
requireCleanWorkingDir: true,
|
||||
tagName: '${npm.name}-v${version}',
|
||||
tagAnnotation: '${npm.name} v${version}',
|
||||
tagMatch: '${npm.name}-v*',
|
||||
commitMessage: 'chore(${npm.name}): release v${version}',
|
||||
push: true,
|
||||
},
|
||||
|
||||
// This one *does* publish to npm
|
||||
npm: {
|
||||
publish: true,
|
||||
// pass flags you’d give to `npm publish`
|
||||
publishArgs: ['--access', 'public'],
|
||||
// (optional) skip npm’s own prepublish checks:
|
||||
// skipChecks: true
|
||||
},
|
||||
// This one *does* publish to npm
|
||||
npm: {
|
||||
publish: true,
|
||||
// pass flags you’d give to `npm publish`
|
||||
publishArgs: ['--access', 'public'],
|
||||
// (optional) skip npm’s own prepublish checks:
|
||||
// skipChecks: true
|
||||
},
|
||||
|
||||
github: {
|
||||
release: true,
|
||||
releaseName: '${npm.name} v${version}',
|
||||
// optional: attach build artifacts
|
||||
// assets: ['dist/**']
|
||||
},
|
||||
github: {
|
||||
release: true,
|
||||
releaseName: '${npm.name} v${version}',
|
||||
// optional: attach build artifacts
|
||||
// assets: ['dist/**']
|
||||
},
|
||||
|
||||
plugins: {
|
||||
'@release-it/conventional-changelog': {
|
||||
preset: 'conventionalcommits',
|
||||
infile: 'CHANGELOG.md',
|
||||
gitRawCommitsOpts: { path: 'packages/react-native-uniffi-russh' },
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
'@release-it/conventional-changelog': {
|
||||
preset: 'conventionalcommits',
|
||||
infile: 'CHANGELOG.md',
|
||||
gitRawCommitsOpts: { path: 'packages/react-native-uniffi-russh' },
|
||||
},
|
||||
},
|
||||
|
||||
hooks: {
|
||||
'before:init': ['turbo run lint:check'],
|
||||
'before:npm:release': 'turbo run build:android build:ios',
|
||||
'after:release': 'echo "Published ${npm.name} v${version} to npm"',
|
||||
},
|
||||
hooks: {
|
||||
'before:init': ['turbo run lint:check'],
|
||||
'before:npm:release': 'turbo run build:android build:ios',
|
||||
'after:release': 'echo "Published ${npm.name} v${version} to npm"',
|
||||
},
|
||||
} satisfies Config;
|
||||
|
||||
@@ -2,32 +2,50 @@
|
||||
|
||||
Contributions are always welcome, no matter how large or small!
|
||||
|
||||
We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md).
|
||||
We want this community to be friendly and respectful to each other. Please
|
||||
follow it in all your interactions with the project. Before contributing, please
|
||||
read the [code of conduct](./CODE_OF_CONDUCT.md).
|
||||
|
||||
## Development workflow
|
||||
|
||||
This project is a monorepo managed using [Yarn workspaces](https://yarnpkg.com/features/workspaces). It contains the following packages:
|
||||
This project is a monorepo managed using
|
||||
[Yarn workspaces](https://yarnpkg.com/features/workspaces). It contains the
|
||||
following packages:
|
||||
|
||||
- The library package in the root directory.
|
||||
- An example app in the `example/` directory.
|
||||
|
||||
To get started with the project, make sure you have the correct version of [Node.js](https://nodejs.org/) installed. See the [`.nvmrc`](./.nvmrc) file for the version used in this project.
|
||||
To get started with the project, make sure you have the correct version of
|
||||
[Node.js](https://nodejs.org/) installed. See the [`.nvmrc`](./.nvmrc) file for
|
||||
the version used in this project.
|
||||
|
||||
Run `yarn` in the root directory to install the required dependencies for each package:
|
||||
Run `yarn` in the root directory to install the required dependencies for each
|
||||
package:
|
||||
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
|
||||
> Since the project relies on Yarn workspaces, you cannot use [`npm`](https://github.com/npm/cli) for development without manually migrating.
|
||||
> Since the project relies on Yarn workspaces, you cannot use
|
||||
> [`npm`](https://github.com/npm/cli) for development without manually
|
||||
> migrating.
|
||||
|
||||
The [example app](/example/) demonstrates usage of the library. You need to run it to test any changes you make.
|
||||
The [example app](/example/) demonstrates usage of the library. You need to run
|
||||
it to test any changes you make.
|
||||
|
||||
It is configured to use the local version of the library, so any changes you make to the library's source code will be reflected in the example app. Changes to the library's JavaScript code will be reflected in the example app without a rebuild, but native code changes will require a rebuild of the example app.
|
||||
It is configured to use the local version of the library, so any changes you
|
||||
make to the library's source code will be reflected in the example app. Changes
|
||||
to the library's JavaScript code will be reflected in the example app without a
|
||||
rebuild, but native code changes will require a rebuild of the example app.
|
||||
|
||||
If you want to use Android Studio or XCode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/UniffiRusshExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-uniffi-russh`.
|
||||
If you want to use Android Studio or XCode to edit the native code, you can open
|
||||
the `example/android` or `example/ios` directories respectively in those
|
||||
editors. To edit the Objective-C or Swift files, open
|
||||
`example/ios/UniffiRusshExample.xcworkspace` in XCode and find the source files
|
||||
at `Pods > Development Pods > react-native-uniffi-russh`.
|
||||
|
||||
To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-uniffi-russh` under `Android`.
|
||||
To edit the Java or Kotlin files, open `example/android` in Android studio and
|
||||
find the source files at `react-native-uniffi-russh` under `Android`.
|
||||
|
||||
You can use various commands from the root directory to work with the project.
|
||||
|
||||
@@ -49,7 +67,8 @@ To run the example app on iOS:
|
||||
yarn example ios
|
||||
```
|
||||
|
||||
To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this:
|
||||
To confirm that the app is running with the new architecture, you can check the
|
||||
Metro logs for a message like this:
|
||||
|
||||
```sh
|
||||
Running "UniffiRusshExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1}
|
||||
@@ -78,7 +97,9 @@ yarn test
|
||||
|
||||
### Commit message convention
|
||||
|
||||
We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:
|
||||
We follow the
|
||||
[conventional commits specification](https://www.conventionalcommits.org/en) for
|
||||
our commit messages:
|
||||
|
||||
- `fix`: bug fixes, e.g. fix crash due to deprecated method.
|
||||
- `feat`: new features, e.g. add new method to the module.
|
||||
@@ -87,19 +108,25 @@ We follow the [conventional commits specification](https://www.conventionalcommi
|
||||
- `test`: adding or updating tests, e.g. add integration tests using detox.
|
||||
- `chore`: tooling changes, e.g. change CI config.
|
||||
|
||||
Our pre-commit hooks verify that your commit message matches this format when committing.
|
||||
Our pre-commit hooks verify that your commit message matches this format when
|
||||
committing.
|
||||
|
||||
### Linting and tests
|
||||
|
||||
[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
|
||||
[ESLint](https://eslint.org/), [Prettier](https://prettier.io/),
|
||||
[TypeScript](https://www.typescriptlang.org/)
|
||||
|
||||
We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
|
||||
We use [TypeScript](https://www.typescriptlang.org/) for type checking,
|
||||
[ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting
|
||||
and formatting the code, and [Jest](https://jestjs.io/) for testing.
|
||||
|
||||
Our pre-commit hooks verify that the linter and tests pass when committing.
|
||||
|
||||
### Publishing to npm
|
||||
|
||||
We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
|
||||
We use [release-it](https://github.com/release-it/release-it) to make it easier
|
||||
to publish new versions. It handles common tasks like bumping version based on
|
||||
semver, creating tags and releases etc.
|
||||
|
||||
To publish new versions, run the following:
|
||||
|
||||
@@ -121,7 +148,9 @@ The `package.json` file contains various scripts for common tasks:
|
||||
|
||||
### Sending a pull request
|
||||
|
||||
> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
|
||||
> **Working on your first pull request?** You can learn how from this _free_
|
||||
> series:
|
||||
> [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
|
||||
|
||||
When you're sending a pull request:
|
||||
|
||||
@@ -129,4 +158,5 @@ When you're sending a pull request:
|
||||
- Verify that linters and tests are passing.
|
||||
- Review the documentation to make sure it looks good.
|
||||
- Follow the pull request template when opening a pull request.
|
||||
- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.
|
||||
- For pull requests that change the API or implementation, discuss with
|
||||
maintainers first by opening an issue.
|
||||
|
||||
21
packages/react-native-uniffi-russh/LICENSE
Normal file
21
packages/react-native-uniffi-russh/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 EthanShoeDev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -30,4 +30,5 @@ MIT
|
||||
|
||||
---
|
||||
|
||||
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
||||
Made with
|
||||
[create-react-native-library](https://github.com/callstack/react-native-builder-bob)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module.exports = {
|
||||
overrides: [
|
||||
{
|
||||
exclude: /\/node_modules\//,
|
||||
presets: ['module:react-native-builder-bob/babel-preset'],
|
||||
},
|
||||
{
|
||||
include: /\/node_modules\//,
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
},
|
||||
],
|
||||
export default {
|
||||
overrides: [
|
||||
{
|
||||
exclude: /\/node_modules\//,
|
||||
presets: ['module:react-native-builder-bob/babel-preset'],
|
||||
},
|
||||
{
|
||||
include: /\/node_modules\//,
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -8,19 +8,25 @@ import { fileURLToPath } from 'node:url';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
extends: fixupConfigRules(compat.extends('@react-native')),
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['node_modules/', 'lib/', 'src/generated/', 'eslint.config.mjs'],
|
||||
},
|
||||
{
|
||||
extends: fixupConfigRules(compat.extends('@react-native')),
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'node_modules/',
|
||||
'lib/',
|
||||
'src/generated/',
|
||||
'eslint.config.mjs',
|
||||
'prettier.config.mjs',
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@fressh/react-native-uniffi-russh",
|
||||
"homepage": "https://github.com/EthanShoeDev/fressh",
|
||||
"license": "UNKNOWN",
|
||||
"license": "MIT",
|
||||
"description": "Uniffi bindings for russh",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./lib/module/api.js",
|
||||
"types": "./lib/typescript/src/api.d.ts",
|
||||
"exports": {
|
||||
@@ -20,6 +21,7 @@
|
||||
"cpp",
|
||||
"*.podspec",
|
||||
"react-native.config.js",
|
||||
"LICENSE",
|
||||
"!ios/build",
|
||||
"!android/build",
|
||||
"!android/gradle",
|
||||
@@ -70,6 +72,7 @@
|
||||
"@react-native-community/cli": "20.0.2",
|
||||
"@react-native/babel-preset": "0.81.1",
|
||||
"@react-native/eslint-config": "^0.81.1",
|
||||
"@epic-web/config": "^1.21.3",
|
||||
"@release-it/conventional-changelog": "^10.0.1",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/react": "~19.1.12",
|
||||
@@ -98,13 +101,6 @@
|
||||
"<rootDir>/lib/"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"quoteProps": "consistent",
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
},
|
||||
"react-native-builder-bob": {
|
||||
"source": "src",
|
||||
"output": "lib",
|
||||
|
||||
14
packages/react-native-uniffi-russh/prettier.config.mjs
Normal file
14
packages/react-native-uniffi-russh/prettier.config.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import epicConfig from '@epic-web/config/prettier';
|
||||
// Sometimes this plugin can remove imports that are being edited.
|
||||
// As a workaround we will only use this in the cli. (pnpm run fmt)
|
||||
const sortImports = process.env.SORT_IMPORTS === 'true-never';
|
||||
|
||||
/** @type {import("prettier").Options} */
|
||||
export default {
|
||||
...epicConfig,
|
||||
semi: true,
|
||||
plugins: [
|
||||
...(sortImports ? ['prettier-plugin-organize-imports'] : []),
|
||||
...(epicConfig.plugins || []),
|
||||
],
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
module.exports = {
|
||||
dependency: {
|
||||
platforms: {
|
||||
android: { sourceDir: 'android' },
|
||||
// add ios if you generate it later:
|
||||
// ios: { project: 'ios/ReactNativeUniffiRussh.xcodeproj' },
|
||||
},
|
||||
},
|
||||
dependency: {
|
||||
platforms: {
|
||||
android: { sourceDir: 'android' },
|
||||
// add ios if you generate it later:
|
||||
// ios: { project: 'ios/ReactNativeUniffiRussh.xcodeproj' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,18 +6,18 @@ type Target = (typeof targetOptions)[number];
|
||||
|
||||
const envTarget = process.env.MOBILE_TARGET as Target | undefined;
|
||||
if (envTarget && !targetOptions.includes(envTarget))
|
||||
throw new Error(`Invalid target: ${envTarget}`);
|
||||
throw new Error(`Invalid target: ${envTarget}`);
|
||||
|
||||
const target =
|
||||
envTarget ??
|
||||
(() => {
|
||||
const uname = os.platform();
|
||||
if (uname === 'darwin') return 'ios';
|
||||
return 'android';
|
||||
})();
|
||||
envTarget ??
|
||||
(() => {
|
||||
const uname = os.platform();
|
||||
if (uname === 'darwin') return 'ios';
|
||||
return 'android';
|
||||
})();
|
||||
|
||||
console.log(`Building for ${target}`);
|
||||
|
||||
child.execSync(`turbo run build:${target} --ui stream`, {
|
||||
stdio: 'inherit',
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
@@ -20,21 +20,21 @@ import * as GeneratedRussh from './index';
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export type TerminalType =
|
||||
| 'Vanilla'
|
||||
| 'Vt100'
|
||||
| 'Vt102'
|
||||
| 'Vt220'
|
||||
| 'Ansi'
|
||||
| 'Xterm'
|
||||
| 'Xterm256';
|
||||
| 'Vanilla'
|
||||
| 'Vt100'
|
||||
| 'Vt102'
|
||||
| 'Vt220'
|
||||
| 'Ansi'
|
||||
| 'Xterm'
|
||||
| 'Xterm256';
|
||||
|
||||
export type ConnectionDetails = {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
security:
|
||||
| { type: 'password'; password: string }
|
||||
| { type: 'key'; privateKey: string };
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
security:
|
||||
| { type: 'password'; password: string }
|
||||
| { type: 'key'; privateKey: string };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -44,58 +44,58 @@ export type ConnectionDetails = {
|
||||
* It is no longer relevant after the connect() promise is resolved.
|
||||
*/
|
||||
export type SshConnectionProgress =
|
||||
| 'tcpConnected' // TCP established, starting SSH handshake
|
||||
| 'sshHandshake'; // SSH protocol negotiation complete
|
||||
| 'tcpConnected' // TCP established, starting SSH handshake
|
||||
| 'sshHandshake'; // SSH protocol negotiation complete
|
||||
|
||||
export type ConnectOptions = ConnectionDetails & {
|
||||
onConnectionProgress?: (status: SshConnectionProgress) => void;
|
||||
onDisconnected?: (connectionId: string) => void;
|
||||
onServerKey: (
|
||||
serverKeyInfo: GeneratedRussh.ServerPublicKeyInfo,
|
||||
signal?: AbortSignal
|
||||
) => Promise<boolean>;
|
||||
abortSignal?: AbortSignal;
|
||||
onConnectionProgress?: (status: SshConnectionProgress) => void;
|
||||
onDisconnected?: (connectionId: string) => void;
|
||||
onServerKey: (
|
||||
serverKeyInfo: GeneratedRussh.ServerPublicKeyInfo,
|
||||
signal?: AbortSignal,
|
||||
) => Promise<boolean>;
|
||||
abortSignal?: AbortSignal;
|
||||
};
|
||||
|
||||
export type StartShellOptions = {
|
||||
term: TerminalType;
|
||||
terminalMode?: GeneratedRussh.TerminalMode[];
|
||||
terminalPixelSize?: GeneratedRussh.TerminalPixelSize;
|
||||
terminalSize?: GeneratedRussh.TerminalSize;
|
||||
onClosed?: (shellId: number) => void;
|
||||
abortSignal?: AbortSignal;
|
||||
term: TerminalType;
|
||||
terminalMode?: GeneratedRussh.TerminalMode[];
|
||||
terminalPixelSize?: GeneratedRussh.TerminalPixelSize;
|
||||
terminalSize?: GeneratedRussh.TerminalSize;
|
||||
onClosed?: (shellId: number) => void;
|
||||
abortSignal?: AbortSignal;
|
||||
};
|
||||
|
||||
export type StreamKind = 'stdout' | 'stderr';
|
||||
|
||||
export type TerminalChunk = {
|
||||
seq: bigint;
|
||||
/** Milliseconds since UNIX epoch (double). */
|
||||
tMs: number;
|
||||
stream: StreamKind;
|
||||
bytes: ArrayBuffer;
|
||||
seq: bigint;
|
||||
/** Milliseconds since UNIX epoch (double). */
|
||||
tMs: number;
|
||||
stream: StreamKind;
|
||||
bytes: ArrayBuffer;
|
||||
};
|
||||
|
||||
export type DropNotice = { kind: 'dropped'; fromSeq: bigint; toSeq: bigint };
|
||||
export type ListenerEvent = TerminalChunk | DropNotice;
|
||||
|
||||
export type Cursor =
|
||||
| { mode: 'head' } // earliest available in ring
|
||||
| { mode: 'tailBytes'; bytes: bigint } // last N bytes (best-effort)
|
||||
| { mode: 'seq'; seq: bigint } // from a given sequence
|
||||
| { mode: 'time'; tMs: number } // from timestamp
|
||||
| { mode: 'live' }; // no replay, live only
|
||||
| { mode: 'head' } // earliest available in ring
|
||||
| { mode: 'tailBytes'; bytes: bigint } // last N bytes (best-effort)
|
||||
| { mode: 'seq'; seq: bigint } // from a given sequence
|
||||
| { mode: 'time'; tMs: number } // from timestamp
|
||||
| { mode: 'live' }; // no replay, live only
|
||||
|
||||
export type ListenerOptions = {
|
||||
cursor: Cursor;
|
||||
/** Optional per-listener coalescing window in ms (e.g., 10–25). */
|
||||
coalesceMs?: number;
|
||||
cursor: Cursor;
|
||||
/** Optional per-listener coalescing window in ms (e.g., 10–25). */
|
||||
coalesceMs?: number;
|
||||
};
|
||||
|
||||
export type BufferReadResult = {
|
||||
chunks: TerminalChunk[];
|
||||
nextSeq: bigint;
|
||||
dropped?: { fromSeq: bigint; toSeq: bigint };
|
||||
chunks: TerminalChunk[];
|
||||
nextSeq: bigint;
|
||||
dropped?: { fromSeq: bigint; toSeq: bigint };
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -103,66 +103,66 @@ export type BufferReadResult = {
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
type ProgressTimings = {
|
||||
tcpEstablishedAtMs: number;
|
||||
sshHandshakeAtMs: number;
|
||||
tcpEstablishedAtMs: number;
|
||||
sshHandshakeAtMs: number;
|
||||
};
|
||||
|
||||
export type SshConnection = {
|
||||
readonly connectionId: string;
|
||||
readonly createdAtMs: number;
|
||||
readonly connectedAtMs: number;
|
||||
readonly connectionDetails: ConnectionDetails;
|
||||
readonly progressTimings: ProgressTimings;
|
||||
readonly connectionId: string;
|
||||
readonly createdAtMs: number;
|
||||
readonly connectedAtMs: number;
|
||||
readonly connectionDetails: ConnectionDetails;
|
||||
readonly progressTimings: ProgressTimings;
|
||||
|
||||
startShell: (opts: StartShellOptions) => Promise<SshShell>;
|
||||
disconnect: (opts?: { signal?: AbortSignal }) => Promise<void>;
|
||||
startShell: (opts: StartShellOptions) => Promise<SshShell>;
|
||||
disconnect: (opts?: { signal?: AbortSignal }) => Promise<void>;
|
||||
};
|
||||
|
||||
export type SshShell = {
|
||||
readonly channelId: number;
|
||||
readonly createdAtMs: number;
|
||||
readonly pty: TerminalType;
|
||||
readonly connectionId: string;
|
||||
readonly channelId: number;
|
||||
readonly createdAtMs: number;
|
||||
readonly pty: TerminalType;
|
||||
readonly connectionId: string;
|
||||
|
||||
// I/O
|
||||
sendData: (
|
||||
data: ArrayBuffer,
|
||||
opts?: { signal?: AbortSignal }
|
||||
) => Promise<void>;
|
||||
close: (opts?: { signal?: AbortSignal }) => Promise<void>;
|
||||
// I/O
|
||||
sendData: (
|
||||
data: ArrayBuffer,
|
||||
opts?: { signal?: AbortSignal },
|
||||
) => Promise<void>;
|
||||
close: (opts?: { signal?: AbortSignal }) => Promise<void>;
|
||||
|
||||
// Buffer policy & stats
|
||||
// setBufferPolicy: (policy: {
|
||||
// ringBytes?: number;
|
||||
// coalesceMs?: number;
|
||||
// }) => Promise<void>;
|
||||
bufferStats: () => GeneratedRussh.BufferStats;
|
||||
currentSeq: () => number;
|
||||
// Buffer policy & stats
|
||||
// setBufferPolicy: (policy: {
|
||||
// ringBytes?: number;
|
||||
// coalesceMs?: number;
|
||||
// }) => Promise<void>;
|
||||
bufferStats: () => GeneratedRussh.BufferStats;
|
||||
currentSeq: () => number;
|
||||
|
||||
// Replay + live
|
||||
readBuffer: (cursor: Cursor, maxBytes?: bigint) => BufferReadResult;
|
||||
addListener: (
|
||||
cb: (ev: ListenerEvent) => void,
|
||||
opts: ListenerOptions
|
||||
) => bigint;
|
||||
removeListener: (id: bigint) => void;
|
||||
// Replay + live
|
||||
readBuffer: (cursor: Cursor, maxBytes?: bigint) => BufferReadResult;
|
||||
addListener: (
|
||||
cb: (ev: ListenerEvent) => void,
|
||||
opts: ListenerOptions,
|
||||
) => bigint;
|
||||
removeListener: (id: bigint) => void;
|
||||
};
|
||||
|
||||
type RusshApi = {
|
||||
uniffiInitAsync: () => Promise<void>;
|
||||
connect: (opts: ConnectOptions) => Promise<SshConnection>;
|
||||
generateKeyPair: (
|
||||
type: 'rsa' | 'ecdsa' | 'ed25519'
|
||||
// TODO: Add these
|
||||
// passphrase?: string;
|
||||
// keySize?: number;
|
||||
// comment?: string;
|
||||
) => Promise<string>;
|
||||
validatePrivateKey: (
|
||||
key: string
|
||||
) =>
|
||||
| { valid: true; error?: never }
|
||||
| { valid: false; error: GeneratedRussh.SshError };
|
||||
uniffiInitAsync: () => Promise<void>;
|
||||
connect: (opts: ConnectOptions) => Promise<SshConnection>;
|
||||
generateKeyPair: (
|
||||
type: 'rsa' | 'ecdsa' | 'ed25519',
|
||||
// TODO: Add these
|
||||
// passphrase?: string;
|
||||
// keySize?: number;
|
||||
// comment?: string;
|
||||
) => Promise<string>;
|
||||
validatePrivateKey: (
|
||||
key: string,
|
||||
) =>
|
||||
| { valid: true; error?: never }
|
||||
| { valid: false; error: GeneratedRussh.SshError };
|
||||
};
|
||||
|
||||
// #endregion
|
||||
@@ -170,242 +170,242 @@ type RusshApi = {
|
||||
// #region Wrapper to match the ideal API
|
||||
|
||||
const terminalTypeLiteralToEnum = {
|
||||
Vanilla: GeneratedRussh.TerminalType.Vanilla,
|
||||
Vt100: GeneratedRussh.TerminalType.Vt100,
|
||||
Vt102: GeneratedRussh.TerminalType.Vt102,
|
||||
Vt220: GeneratedRussh.TerminalType.Vt220,
|
||||
Ansi: GeneratedRussh.TerminalType.Ansi,
|
||||
Xterm: GeneratedRussh.TerminalType.Xterm,
|
||||
Xterm256: GeneratedRussh.TerminalType.Xterm256,
|
||||
Vanilla: GeneratedRussh.TerminalType.Vanilla,
|
||||
Vt100: GeneratedRussh.TerminalType.Vt100,
|
||||
Vt102: GeneratedRussh.TerminalType.Vt102,
|
||||
Vt220: GeneratedRussh.TerminalType.Vt220,
|
||||
Ansi: GeneratedRussh.TerminalType.Ansi,
|
||||
Xterm: GeneratedRussh.TerminalType.Xterm,
|
||||
Xterm256: GeneratedRussh.TerminalType.Xterm256,
|
||||
} as const satisfies Record<string, GeneratedRussh.TerminalType>;
|
||||
|
||||
const terminalTypeEnumToLiteral: Record<
|
||||
GeneratedRussh.TerminalType,
|
||||
TerminalType
|
||||
GeneratedRussh.TerminalType,
|
||||
TerminalType
|
||||
> = {
|
||||
[GeneratedRussh.TerminalType.Vanilla]: 'Vanilla',
|
||||
[GeneratedRussh.TerminalType.Vt100]: 'Vt100',
|
||||
[GeneratedRussh.TerminalType.Vt102]: 'Vt102',
|
||||
[GeneratedRussh.TerminalType.Vt220]: 'Vt220',
|
||||
[GeneratedRussh.TerminalType.Ansi]: 'Ansi',
|
||||
[GeneratedRussh.TerminalType.Xterm]: 'Xterm',
|
||||
[GeneratedRussh.TerminalType.Xterm256]: 'Xterm256',
|
||||
[GeneratedRussh.TerminalType.Vanilla]: 'Vanilla',
|
||||
[GeneratedRussh.TerminalType.Vt100]: 'Vt100',
|
||||
[GeneratedRussh.TerminalType.Vt102]: 'Vt102',
|
||||
[GeneratedRussh.TerminalType.Vt220]: 'Vt220',
|
||||
[GeneratedRussh.TerminalType.Ansi]: 'Ansi',
|
||||
[GeneratedRussh.TerminalType.Xterm]: 'Xterm',
|
||||
[GeneratedRussh.TerminalType.Xterm256]: 'Xterm256',
|
||||
};
|
||||
|
||||
const sshConnProgressEnumToLiteral = {
|
||||
[GeneratedRussh.SshConnectionProgressEvent.TcpConnected]: 'tcpConnected',
|
||||
[GeneratedRussh.SshConnectionProgressEvent.SshHandshake]: 'sshHandshake',
|
||||
[GeneratedRussh.SshConnectionProgressEvent.TcpConnected]: 'tcpConnected',
|
||||
[GeneratedRussh.SshConnectionProgressEvent.SshHandshake]: 'sshHandshake',
|
||||
} as const satisfies Record<
|
||||
GeneratedRussh.SshConnectionProgressEvent,
|
||||
SshConnectionProgress
|
||||
GeneratedRussh.SshConnectionProgressEvent,
|
||||
SshConnectionProgress
|
||||
>;
|
||||
|
||||
const streamEnumToLiteral = {
|
||||
[GeneratedRussh.StreamKind.Stdout]: 'stdout',
|
||||
[GeneratedRussh.StreamKind.Stderr]: 'stderr',
|
||||
[GeneratedRussh.StreamKind.Stdout]: 'stdout',
|
||||
[GeneratedRussh.StreamKind.Stderr]: 'stderr',
|
||||
} as const satisfies Record<GeneratedRussh.StreamKind, StreamKind>;
|
||||
|
||||
function generatedConnDetailsToIdeal(
|
||||
details: GeneratedRussh.ConnectionDetails
|
||||
details: GeneratedRussh.ConnectionDetails,
|
||||
): ConnectionDetails {
|
||||
const security: ConnectionDetails['security'] =
|
||||
details.security instanceof GeneratedRussh.Security.Password
|
||||
? { type: 'password', password: details.security.inner.password }
|
||||
: { type: 'key', privateKey: details.security.inner.privateKeyContent };
|
||||
return {
|
||||
host: details.host,
|
||||
port: details.port,
|
||||
username: details.username,
|
||||
security,
|
||||
};
|
||||
const security: ConnectionDetails['security'] =
|
||||
details.security instanceof GeneratedRussh.Security.Password
|
||||
? { type: 'password', password: details.security.inner.password }
|
||||
: { type: 'key', privateKey: details.security.inner.privateKeyContent };
|
||||
return {
|
||||
host: details.host,
|
||||
port: details.port,
|
||||
username: details.username,
|
||||
security,
|
||||
};
|
||||
}
|
||||
|
||||
function cursorToGenerated(cursor: Cursor): GeneratedRussh.Cursor {
|
||||
switch (cursor.mode) {
|
||||
case 'head':
|
||||
return new GeneratedRussh.Cursor.Head();
|
||||
case 'tailBytes':
|
||||
return new GeneratedRussh.Cursor.TailBytes({
|
||||
bytes: cursor.bytes,
|
||||
});
|
||||
case 'seq':
|
||||
return new GeneratedRussh.Cursor.Seq({ seq: cursor.seq });
|
||||
case 'time':
|
||||
return new GeneratedRussh.Cursor.TimeMs({ tMs: cursor.tMs });
|
||||
case 'live':
|
||||
return new GeneratedRussh.Cursor.Live();
|
||||
}
|
||||
switch (cursor.mode) {
|
||||
case 'head':
|
||||
return new GeneratedRussh.Cursor.Head();
|
||||
case 'tailBytes':
|
||||
return new GeneratedRussh.Cursor.TailBytes({
|
||||
bytes: cursor.bytes,
|
||||
});
|
||||
case 'seq':
|
||||
return new GeneratedRussh.Cursor.Seq({ seq: cursor.seq });
|
||||
case 'time':
|
||||
return new GeneratedRussh.Cursor.TimeMs({ tMs: cursor.tMs });
|
||||
case 'live':
|
||||
return new GeneratedRussh.Cursor.Live();
|
||||
}
|
||||
}
|
||||
|
||||
function toTerminalChunk(ch: GeneratedRussh.TerminalChunk): TerminalChunk {
|
||||
return {
|
||||
seq: ch.seq,
|
||||
tMs: ch.tMs,
|
||||
stream: streamEnumToLiteral[ch.stream],
|
||||
bytes: ch.bytes,
|
||||
};
|
||||
return {
|
||||
seq: ch.seq,
|
||||
tMs: ch.tMs,
|
||||
stream: streamEnumToLiteral[ch.stream],
|
||||
bytes: ch.bytes,
|
||||
};
|
||||
}
|
||||
|
||||
function wrapShellSession(
|
||||
shell: GeneratedRussh.ShellSessionInterface
|
||||
shell: GeneratedRussh.ShellSessionInterface,
|
||||
): SshShell {
|
||||
const info = shell.getInfo();
|
||||
const info = shell.getInfo();
|
||||
|
||||
const readBuffer: SshShell['readBuffer'] = (cursor, maxBytes) => {
|
||||
const res = shell.readBuffer(cursorToGenerated(cursor), maxBytes);
|
||||
return {
|
||||
chunks: res.chunks.map(toTerminalChunk),
|
||||
nextSeq: res.nextSeq,
|
||||
dropped: res.dropped,
|
||||
} satisfies BufferReadResult;
|
||||
};
|
||||
const readBuffer: SshShell['readBuffer'] = (cursor, maxBytes) => {
|
||||
const res = shell.readBuffer(cursorToGenerated(cursor), maxBytes);
|
||||
return {
|
||||
chunks: res.chunks.map(toTerminalChunk),
|
||||
nextSeq: res.nextSeq,
|
||||
dropped: res.dropped,
|
||||
} satisfies BufferReadResult;
|
||||
};
|
||||
|
||||
const addListener: SshShell['addListener'] = (cb, opts) => {
|
||||
const listener = {
|
||||
onEvent: (ev: GeneratedRussh.ShellEvent) => {
|
||||
if (ev instanceof GeneratedRussh.ShellEvent.Chunk) {
|
||||
cb(toTerminalChunk(ev.inner[0]!));
|
||||
} else if (ev instanceof GeneratedRussh.ShellEvent.Dropped) {
|
||||
cb({
|
||||
kind: 'dropped',
|
||||
fromSeq: ev.inner.fromSeq,
|
||||
toSeq: ev.inner.toSeq,
|
||||
});
|
||||
}
|
||||
},
|
||||
} satisfies GeneratedRussh.ShellListener;
|
||||
const addListener: SshShell['addListener'] = (cb, opts) => {
|
||||
const listener = {
|
||||
onEvent: (ev: GeneratedRussh.ShellEvent) => {
|
||||
if (ev instanceof GeneratedRussh.ShellEvent.Chunk) {
|
||||
cb(toTerminalChunk(ev.inner[0]!));
|
||||
} else if (ev instanceof GeneratedRussh.ShellEvent.Dropped) {
|
||||
cb({
|
||||
kind: 'dropped',
|
||||
fromSeq: ev.inner.fromSeq,
|
||||
toSeq: ev.inner.toSeq,
|
||||
});
|
||||
}
|
||||
},
|
||||
} satisfies GeneratedRussh.ShellListener;
|
||||
|
||||
try {
|
||||
const id = shell.addListener(listener, {
|
||||
cursor: cursorToGenerated(opts.cursor),
|
||||
coalesceMs: opts.coalesceMs,
|
||||
});
|
||||
if (id === 0n) {
|
||||
throw new Error('Failed to attach shell listener (id=0)');
|
||||
}
|
||||
return id;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`addListener failed: ${String((e as any)?.message ?? e)}`
|
||||
);
|
||||
}
|
||||
};
|
||||
try {
|
||||
const id = shell.addListener(listener, {
|
||||
cursor: cursorToGenerated(opts.cursor),
|
||||
coalesceMs: opts.coalesceMs,
|
||||
});
|
||||
if (id === 0n) {
|
||||
throw new Error('Failed to attach shell listener (id=0)');
|
||||
}
|
||||
return id;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`addListener failed: ${String((e as any)?.message ?? e)}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
channelId: info.channelId,
|
||||
createdAtMs: info.createdAtMs,
|
||||
pty: terminalTypeEnumToLiteral[info.term],
|
||||
connectionId: info.connectionId,
|
||||
sendData: (data, o) =>
|
||||
shell.sendData(data, o?.signal ? { signal: o.signal } : undefined),
|
||||
close: (o) => shell.close(o?.signal ? { signal: o.signal } : undefined),
|
||||
// setBufferPolicy,
|
||||
bufferStats: shell.bufferStats,
|
||||
currentSeq: () => Number(shell.currentSeq()),
|
||||
readBuffer,
|
||||
addListener,
|
||||
removeListener: (id) => shell.removeListener(id),
|
||||
};
|
||||
return {
|
||||
channelId: info.channelId,
|
||||
createdAtMs: info.createdAtMs,
|
||||
pty: terminalTypeEnumToLiteral[info.term],
|
||||
connectionId: info.connectionId,
|
||||
sendData: (data, o) =>
|
||||
shell.sendData(data, o?.signal ? { signal: o.signal } : undefined),
|
||||
close: (o) => shell.close(o?.signal ? { signal: o.signal } : undefined),
|
||||
// setBufferPolicy,
|
||||
bufferStats: shell.bufferStats,
|
||||
currentSeq: () => Number(shell.currentSeq()),
|
||||
readBuffer,
|
||||
addListener,
|
||||
removeListener: (id) => shell.removeListener(id),
|
||||
};
|
||||
}
|
||||
|
||||
function wrapConnection(
|
||||
conn: GeneratedRussh.SshConnectionInterface
|
||||
conn: GeneratedRussh.SshConnectionInterface,
|
||||
): SshConnection {
|
||||
const info = conn.getInfo();
|
||||
return {
|
||||
connectionId: info.connectionId,
|
||||
connectionDetails: generatedConnDetailsToIdeal(info.connectionDetails),
|
||||
createdAtMs: info.createdAtMs,
|
||||
connectedAtMs: info.connectedAtMs,
|
||||
progressTimings: {
|
||||
tcpEstablishedAtMs: info.progressTimings.tcpEstablishedAtMs,
|
||||
sshHandshakeAtMs: info.progressTimings.sshHandshakeAtMs,
|
||||
},
|
||||
startShell: async ({ onClosed, ...params }) => {
|
||||
const shell = await conn.startShell(
|
||||
{
|
||||
term: terminalTypeLiteralToEnum[params.term],
|
||||
onClosedCallback: onClosed
|
||||
? {
|
||||
onChange: (channelId) => onClosed(channelId),
|
||||
}
|
||||
: undefined,
|
||||
terminalMode: params.terminalMode,
|
||||
terminalPixelSize: params.terminalPixelSize,
|
||||
terminalSize: params.terminalSize,
|
||||
},
|
||||
params.abortSignal ? { signal: params.abortSignal } : undefined
|
||||
);
|
||||
return wrapShellSession(shell);
|
||||
},
|
||||
disconnect: (opts) =>
|
||||
conn.disconnect(opts?.signal ? { signal: opts.signal } : undefined),
|
||||
};
|
||||
const info = conn.getInfo();
|
||||
return {
|
||||
connectionId: info.connectionId,
|
||||
connectionDetails: generatedConnDetailsToIdeal(info.connectionDetails),
|
||||
createdAtMs: info.createdAtMs,
|
||||
connectedAtMs: info.connectedAtMs,
|
||||
progressTimings: {
|
||||
tcpEstablishedAtMs: info.progressTimings.tcpEstablishedAtMs,
|
||||
sshHandshakeAtMs: info.progressTimings.sshHandshakeAtMs,
|
||||
},
|
||||
startShell: async ({ onClosed, ...params }) => {
|
||||
const shell = await conn.startShell(
|
||||
{
|
||||
term: terminalTypeLiteralToEnum[params.term],
|
||||
onClosedCallback: onClosed
|
||||
? {
|
||||
onChange: (channelId) => onClosed(channelId),
|
||||
}
|
||||
: undefined,
|
||||
terminalMode: params.terminalMode,
|
||||
terminalPixelSize: params.terminalPixelSize,
|
||||
terminalSize: params.terminalSize,
|
||||
},
|
||||
params.abortSignal ? { signal: params.abortSignal } : undefined,
|
||||
);
|
||||
return wrapShellSession(shell);
|
||||
},
|
||||
disconnect: (opts) =>
|
||||
conn.disconnect(opts?.signal ? { signal: opts.signal } : undefined),
|
||||
};
|
||||
}
|
||||
|
||||
async function connect({
|
||||
onServerKey,
|
||||
onConnectionProgress,
|
||||
onDisconnected,
|
||||
...options
|
||||
onServerKey,
|
||||
onConnectionProgress,
|
||||
onDisconnected,
|
||||
...options
|
||||
}: ConnectOptions): Promise<SshConnection> {
|
||||
const security =
|
||||
options.security.type === 'password'
|
||||
? new GeneratedRussh.Security.Password({
|
||||
password: options.security.password,
|
||||
})
|
||||
: new GeneratedRussh.Security.Key({
|
||||
privateKeyContent: options.security.privateKey,
|
||||
});
|
||||
const sshConnection = await GeneratedRussh.connect(
|
||||
{
|
||||
connectionDetails: {
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
username: options.username,
|
||||
security,
|
||||
},
|
||||
onConnectionProgressCallback: onConnectionProgress
|
||||
? {
|
||||
onChange: (statusEnum) =>
|
||||
onConnectionProgress(sshConnProgressEnumToLiteral[statusEnum]),
|
||||
}
|
||||
: undefined,
|
||||
onDisconnectedCallback: onDisconnected
|
||||
? {
|
||||
onChange: (connectionId) => onDisconnected(connectionId),
|
||||
}
|
||||
: undefined,
|
||||
onServerKeyCallback: {
|
||||
onChange: (serverKeyInfo) =>
|
||||
onServerKey(serverKeyInfo, options.abortSignal),
|
||||
},
|
||||
},
|
||||
options.abortSignal ? { signal: options.abortSignal } : undefined
|
||||
);
|
||||
return wrapConnection(sshConnection);
|
||||
const security =
|
||||
options.security.type === 'password'
|
||||
? new GeneratedRussh.Security.Password({
|
||||
password: options.security.password,
|
||||
})
|
||||
: new GeneratedRussh.Security.Key({
|
||||
privateKeyContent: options.security.privateKey,
|
||||
});
|
||||
const sshConnection = await GeneratedRussh.connect(
|
||||
{
|
||||
connectionDetails: {
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
username: options.username,
|
||||
security,
|
||||
},
|
||||
onConnectionProgressCallback: onConnectionProgress
|
||||
? {
|
||||
onChange: (statusEnum) =>
|
||||
onConnectionProgress(sshConnProgressEnumToLiteral[statusEnum]),
|
||||
}
|
||||
: undefined,
|
||||
onDisconnectedCallback: onDisconnected
|
||||
? {
|
||||
onChange: (connectionId) => onDisconnected(connectionId),
|
||||
}
|
||||
: undefined,
|
||||
onServerKeyCallback: {
|
||||
onChange: (serverKeyInfo) =>
|
||||
onServerKey(serverKeyInfo, options.abortSignal),
|
||||
},
|
||||
},
|
||||
options.abortSignal ? { signal: options.abortSignal } : undefined,
|
||||
);
|
||||
return wrapConnection(sshConnection);
|
||||
}
|
||||
|
||||
async function generateKeyPair(type: 'rsa' | 'ecdsa' | 'ed25519') {
|
||||
const map = {
|
||||
rsa: GeneratedRussh.KeyType.Rsa,
|
||||
ecdsa: GeneratedRussh.KeyType.Ecdsa,
|
||||
ed25519: GeneratedRussh.KeyType.Ed25519,
|
||||
} as const;
|
||||
return GeneratedRussh.generateKeyPair(map[type]);
|
||||
const map = {
|
||||
rsa: GeneratedRussh.KeyType.Rsa,
|
||||
ecdsa: GeneratedRussh.KeyType.Ecdsa,
|
||||
ed25519: GeneratedRussh.KeyType.Ed25519,
|
||||
} as const;
|
||||
return GeneratedRussh.generateKeyPair(map[type]);
|
||||
}
|
||||
|
||||
function validatePrivateKey(
|
||||
key: string
|
||||
key: string,
|
||||
):
|
||||
| { valid: true; error?: never }
|
||||
| { valid: false; error: GeneratedRussh.SshError } {
|
||||
try {
|
||||
GeneratedRussh.validatePrivateKey(key);
|
||||
return { valid: true };
|
||||
} catch (e) {
|
||||
return { valid: false, error: e as GeneratedRussh.SshError };
|
||||
}
|
||||
| { valid: true; error?: never }
|
||||
| { valid: false; error: GeneratedRussh.SshError } {
|
||||
try {
|
||||
GeneratedRussh.validatePrivateKey(key);
|
||||
return { valid: true };
|
||||
} catch (e) {
|
||||
return { valid: false, error: e as GeneratedRussh.SshError };
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
@@ -413,8 +413,8 @@ function validatePrivateKey(
|
||||
export { SshError, SshError_Tags } from './generated/uniffi_russh';
|
||||
|
||||
export const RnRussh = {
|
||||
uniffiInitAsync: GeneratedRussh.uniffiInitAsync,
|
||||
connect,
|
||||
generateKeyPair,
|
||||
validatePrivateKey,
|
||||
uniffiInitAsync: GeneratedRussh.uniffiInitAsync,
|
||||
connect,
|
||||
generateKeyPair,
|
||||
validatePrivateKey,
|
||||
} satisfies RusshApi;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"exclude": ["example", "lib", "scripts"],
|
||||
"compilerOptions": {
|
||||
"noUnusedParameters": false,
|
||||
"noUnusedLocals": false
|
||||
}
|
||||
"extends": "./tsconfig",
|
||||
"exclude": ["example", "lib", "scripts"],
|
||||
"compilerOptions": {
|
||||
"noUnusedParameters": false,
|
||||
"noUnusedLocals": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"paths": {
|
||||
"react-native-uniffi-russh": ["./src/index"]
|
||||
},
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"customConditions": ["react-native-strict-api"],
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"noEmit": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitUseStrict": false,
|
||||
"noStrictGenericChecks": false,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"verbatimModuleSyntax": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false
|
||||
}
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"paths": {
|
||||
"react-native-uniffi-russh": ["./src/index"]
|
||||
},
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"customConditions": ["react-native-strict-api"],
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"noEmit": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitUseStrict": false,
|
||||
"noStrictGenericChecks": false,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"verbatimModuleSyntax": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
// Default overrides
|
||||
"fmt": { "with": ["//#fmt:root", "fmt:rust"] },
|
||||
"fmt:check": { "with": ["//#fmt:check:root", "fmt:rust:check"] },
|
||||
"lint": {
|
||||
"dependsOn": ["fmt", "^build", "build:bob", "fmt:rust"],
|
||||
"with": ["typecheck", "//#lint:root", "lint:rust"],
|
||||
},
|
||||
"lint:check": {
|
||||
"dependsOn": ["^build", "build:bob"],
|
||||
"with": [
|
||||
"fmt:check",
|
||||
"typecheck",
|
||||
"//#lint:check:root",
|
||||
"lint:rust",
|
||||
"lint:rust:check",
|
||||
"fmt:rust:check",
|
||||
],
|
||||
},
|
||||
"build": {
|
||||
"dependsOn": ["build:bob"],
|
||||
},
|
||||
"typecheck": {
|
||||
"dependsOn": ["build:native"],
|
||||
},
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
// Default overrides
|
||||
"fmt": { "with": ["//#fmt:root", "fmt:rust"] },
|
||||
"fmt:check": { "with": ["//#fmt:check:root", "fmt:rust:check"] },
|
||||
"lint": {
|
||||
"dependsOn": ["fmt", "^build", "build:bob", "fmt:rust"],
|
||||
"with": ["typecheck", "//#lint:root", "lint:rust"],
|
||||
},
|
||||
"lint:check": {
|
||||
"dependsOn": ["^build", "build:bob"],
|
||||
"with": [
|
||||
"fmt:check",
|
||||
"typecheck",
|
||||
"//#lint:check:root",
|
||||
"lint:rust",
|
||||
"lint:rust:check",
|
||||
"fmt:rust:check",
|
||||
],
|
||||
},
|
||||
"build": {
|
||||
"dependsOn": ["build:bob"],
|
||||
},
|
||||
"typecheck": {
|
||||
"dependsOn": ["build:native"],
|
||||
},
|
||||
|
||||
// Special tasks
|
||||
"lint:rust": {},
|
||||
"lint:rust:check": {},
|
||||
"fmt:rust": {},
|
||||
"fmt:rust:check": {},
|
||||
"build:bob": {
|
||||
"dependsOn": ["build:native"],
|
||||
"inputs": ["src/**"],
|
||||
"outputs": ["lib/**"],
|
||||
},
|
||||
"build:native": {},
|
||||
"build:android": {
|
||||
"inputs": ["rust/**", "!rust/target"],
|
||||
"outputs": [
|
||||
"android/**",
|
||||
"cpp/**",
|
||||
"src/generated/**",
|
||||
"src/index.ts",
|
||||
"src/NativeReactNativeUniffi*.ts",
|
||||
],
|
||||
},
|
||||
"build:ios": {
|
||||
"inputs": ["rust/**", "!rust/target"],
|
||||
"outputs": [
|
||||
"ios/**",
|
||||
"cpp/**",
|
||||
"src/generated/**",
|
||||
"src/index.ts",
|
||||
"src/NativeReactNativeUniffi*.ts",
|
||||
],
|
||||
},
|
||||
},
|
||||
// Special tasks
|
||||
"lint:rust": {},
|
||||
"lint:rust:check": {},
|
||||
"fmt:rust": {},
|
||||
"fmt:rust:check": {},
|
||||
"build:bob": {
|
||||
"dependsOn": ["build:native"],
|
||||
"inputs": ["src/**"],
|
||||
"outputs": ["lib/**"],
|
||||
},
|
||||
"build:native": {},
|
||||
"build:android": {
|
||||
"inputs": ["rust/**", "!rust/target"],
|
||||
"outputs": [
|
||||
"android/**",
|
||||
"cpp/**",
|
||||
"src/generated/**",
|
||||
"src/index.ts",
|
||||
"src/NativeReactNativeUniffi*.ts",
|
||||
],
|
||||
},
|
||||
"build:ios": {
|
||||
"inputs": ["rust/**", "!rust/target"],
|
||||
"outputs": [
|
||||
"ios/**",
|
||||
"cpp/**",
|
||||
"src/generated/**",
|
||||
"src/index.ts",
|
||||
"src/NativeReactNativeUniffi*.ts",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { type Config } from 'release-it';
|
||||
|
||||
export default {
|
||||
// Avoid double-publish from the built-in npm plugin
|
||||
npm: {
|
||||
publish: true,
|
||||
publishArgs: ['--access', 'public'],
|
||||
},
|
||||
git: {
|
||||
requireCleanWorkingDir: true,
|
||||
tagName: '${npm.name}-v${version}',
|
||||
@@ -10,15 +15,6 @@ export default {
|
||||
push: true,
|
||||
},
|
||||
|
||||
// This one *does* publish to npm
|
||||
npm: {
|
||||
publish: true,
|
||||
// pass flags you’d give to `npm publish`
|
||||
publishArgs: ['--access', 'public'],
|
||||
// (optional) skip npm’s own prepublish checks:
|
||||
// skipChecks: true
|
||||
},
|
||||
|
||||
github: {
|
||||
release: true,
|
||||
releaseName: '${npm.name} v${version}',
|
||||
@@ -36,7 +32,7 @@ export default {
|
||||
|
||||
hooks: {
|
||||
'before:init': ['turbo run lint:check'],
|
||||
'before:npm:release': 'turbo run build',
|
||||
'before:github:release': 'turbo run build',
|
||||
'after:release': 'echo "Published ${npm.name} v${version} to npm"',
|
||||
},
|
||||
} satisfies Config;
|
||||
|
||||
20
packages/react-native-xtermjs-webview/LICENSE
Normal file
20
packages/react-native-xtermjs-webview/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 EthanShoeDev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,8 +1,17 @@
|
||||
{
|
||||
"name": "@fressh/react-native-xtermjs-webview",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"private": false,
|
||||
"version": "0.0.3",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"dist-internal",
|
||||
"!node_modules",
|
||||
"!.turbo",
|
||||
"*"
|
||||
],
|
||||
"exports": {
|
||||
".": "./dist/index.js"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user