committed by
GitHub
32 changed files with 435 additions and 88 deletions
@ -1,37 +0,0 @@ |
|||||
name: Pull Request |
|
||||
|
|
||||
on: |
|
||||
push: |
|
||||
paths: |
|
||||
- "packages/core" |
|
||||
- "packages/transport-deno" |
|
||||
- "packages/transport-http" |
|
||||
- "packages/transport-web-bluetooth" |
|
||||
- "packages/transport-web-serial" |
|
||||
branches: |
|
||||
- main |
|
||||
|
|
||||
jobs: |
|
||||
build: |
|
||||
runs-on: ubuntu-latest |
|
||||
|
|
||||
permissions: |
|
||||
contents: read |
|
||||
id-token: write |
|
||||
|
|
||||
steps: |
|
||||
- name: Checkout code |
|
||||
uses: actions/checkout@v4 |
|
||||
|
|
||||
- uses: denoland/setup-deno@v2 |
|
||||
with: |
|
||||
deno-version: v2.x |
|
||||
|
|
||||
- name: Check formatting |
|
||||
run: deno fmt --check |
|
||||
|
|
||||
- name: Check types |
|
||||
run: deno lint |
|
||||
|
|
||||
- name: Publish to JSR |
|
||||
run: npx jsr publish |
|
||||
@ -1,9 +1,25 @@ |
|||||
{ |
{ |
||||
"version": "5", |
"version": "5", |
||||
"specifiers": { |
"specifiers": { |
||||
|
"jsr:@david/code-block-writer@^13.0.2": "13.0.3", |
||||
|
"jsr:@deno/[email protected]": "0.20.1", |
||||
|
"jsr:@deno/dnt@~0.42.1": "0.42.1", |
||||
|
"jsr:@deno/[email protected]": "0.86.9", |
||||
"jsr:@meshtastic/protobufs@^2.7.0": "2.7.0", |
"jsr:@meshtastic/protobufs@^2.7.0": "2.7.0", |
||||
|
"jsr:@std/bytes@^1.0.5": "1.0.6", |
||||
|
"jsr:@std/fmt@1": "1.0.8", |
||||
|
"jsr:@std/fmt@^1.0.3": "1.0.8", |
||||
|
"jsr:@std/fs@1": "1.0.19", |
||||
|
"jsr:@std/fs@^1.0.6": "1.0.19", |
||||
"jsr:@std/internal@^1.0.9": "1.0.9", |
"jsr:@std/internal@^1.0.9": "1.0.9", |
||||
|
"jsr:@std/[email protected]": "0.225.2", |
||||
|
"jsr:@std/path@*": "1.1.1", |
||||
|
"jsr:@std/path@1": "1.1.1", |
||||
|
"jsr:@std/path@^1.0.8": "1.1.1", |
||||
"jsr:@std/path@^1.1.0": "1.1.1", |
"jsr:@std/path@^1.1.0": "1.1.1", |
||||
|
"jsr:@std/path@^1.1.1": "1.1.1", |
||||
|
"jsr:@ts-morph/[email protected]": "0.25.0", |
||||
|
"jsr:@ts-morph/[email protected]": "0.25.0", |
||||
"npm:@bufbuild/protobuf@^2.2.3": "2.6.0", |
"npm:@bufbuild/protobuf@^2.2.3": "2.6.0", |
||||
"npm:@bufbuild/protobuf@^2.6.0": "2.6.0", |
"npm:@bufbuild/protobuf@^2.6.0": "2.6.0", |
||||
"npm:@hookform/resolvers@^5.1.1": "[email protected][email protected][email protected]", |
"npm:@hookform/resolvers@^5.1.1": "[email protected][email protected][email protected]", |
||||
@ -95,20 +111,79 @@ |
|||||
"npm:[email protected]": "5.0.6_@[email protected][email protected][email protected]" |
"npm:[email protected]": "5.0.6_@[email protected][email protected][email protected]" |
||||
}, |
}, |
||||
"jsr": { |
"jsr": { |
||||
|
"@david/[email protected]": { |
||||
|
"integrity": "f98c77d320f5957899a61bfb7a9bead7c6d83ad1515daee92dbacc861e13bb7f" |
||||
|
}, |
||||
|
"@deno/[email protected]": { |
||||
|
"integrity": "dc4f3add14307f3ff3b712441ea4acabcbfc9a13f67c5adc78c3aac16ac5e2a0", |
||||
|
"dependencies": [ |
||||
|
"jsr:@deno/graph", |
||||
|
"jsr:@std/fmt@^1.0.3", |
||||
|
"jsr:@std/fs@^1.0.6", |
||||
|
"jsr:@std/io", |
||||
|
"jsr:@std/path@^1.0.8" |
||||
|
] |
||||
|
}, |
||||
|
"@deno/[email protected]": { |
||||
|
"integrity": "85322b38eb40d4e8c5216d62536152c35b1bda9dc47c8c60860610397b960223", |
||||
|
"dependencies": [ |
||||
|
"jsr:@david/code-block-writer", |
||||
|
"jsr:@deno/cache-dir", |
||||
|
"jsr:@std/fmt@1", |
||||
|
"jsr:@std/fs@1", |
||||
|
"jsr:@std/path@1", |
||||
|
"jsr:@ts-morph/bootstrap" |
||||
|
] |
||||
|
}, |
||||
|
"@deno/[email protected]": { |
||||
|
"integrity": "c4f353a695bcc5246c099602977dabc6534eacea9999a35a8cb24e807192e6a1" |
||||
|
}, |
||||
"@meshtastic/[email protected]": { |
"@meshtastic/[email protected]": { |
||||
"integrity": "38357241bd8a7431c87366dbe12ce9e69f204ebb6ec23da12f7682765b6c8376", |
"integrity": "38357241bd8a7431c87366dbe12ce9e69f204ebb6ec23da12f7682765b6c8376", |
||||
"dependencies": [ |
"dependencies": [ |
||||
"npm:@bufbuild/protobuf@^2.2.3" |
"npm:@bufbuild/protobuf@^2.2.3" |
||||
] |
] |
||||
}, |
}, |
||||
|
"@std/[email protected]": { |
||||
|
"integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a" |
||||
|
}, |
||||
|
"@std/[email protected]": { |
||||
|
"integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" |
||||
|
}, |
||||
|
"@std/[email protected]": { |
||||
|
"integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06", |
||||
|
"dependencies": [ |
||||
|
"jsr:@std/internal", |
||||
|
"jsr:@std/path@^1.1.1" |
||||
|
] |
||||
|
}, |
||||
"@std/[email protected]": { |
"@std/[email protected]": { |
||||
"integrity": "bdfb97f83e4db7a13e8faab26fb1958d1b80cc64366501af78a0aee151696eb8" |
"integrity": "bdfb97f83e4db7a13e8faab26fb1958d1b80cc64366501af78a0aee151696eb8" |
||||
}, |
}, |
||||
|
"@std/[email protected]": { |
||||
|
"integrity": "3c740cd4ee4c082e6cfc86458f47e2ab7cb353dc6234d5e9b1f91a2de5f4d6c7", |
||||
|
"dependencies": [ |
||||
|
"jsr:@std/bytes" |
||||
|
] |
||||
|
}, |
||||
"@std/[email protected]": { |
"@std/[email protected]": { |
||||
"integrity": "fe00026bd3a7e6a27f73709b83c607798be40e20c81dde655ce34052fd82ec76", |
"integrity": "fe00026bd3a7e6a27f73709b83c607798be40e20c81dde655ce34052fd82ec76", |
||||
"dependencies": [ |
"dependencies": [ |
||||
"jsr:@std/internal" |
"jsr:@std/internal" |
||||
] |
] |
||||
|
}, |
||||
|
"@ts-morph/[email protected]": { |
||||
|
"integrity": "3cd33ee80ac0aab8e5d2660c639a02187f0c8abfe454636ce86c00eb7e8407db", |
||||
|
"dependencies": [ |
||||
|
"jsr:@ts-morph/common" |
||||
|
] |
||||
|
}, |
||||
|
"@ts-morph/[email protected]": { |
||||
|
"integrity": "e3ed1771e2fb61fbc3d2cb39ebbc4f89cd686d6d9bc6d91a71372be055ac1967", |
||||
|
"dependencies": [ |
||||
|
"jsr:@std/fs@1", |
||||
|
"jsr:@std/path@1" |
||||
|
] |
||||
} |
} |
||||
}, |
}, |
||||
"npm": { |
"npm": { |
||||
@ -5106,6 +5181,7 @@ |
|||||
}, |
}, |
||||
"workspace": { |
"workspace": { |
||||
"dependencies": [ |
"dependencies": [ |
||||
|
"jsr:@deno/dnt@~0.42.1", |
||||
"jsr:@meshtastic/protobufs@^2.7.0", |
"jsr:@meshtastic/protobufs@^2.7.0", |
||||
"npm:@bufbuild/protobuf@^2.2.3", |
"npm:@bufbuild/protobuf@^2.2.3", |
||||
"npm:@types/node@^22.13.10", |
"npm:@types/node@^22.13.10", |
||||
|
|||||
@ -0,0 +1,28 @@ |
|||||
|
# @meshtastic/transport-node |
||||
|
|
||||
|
[](https://jsr.io/@meshtastic/transport-node) |
||||
|
[](https://github.com/meshtastic/js/actions/workflows/ci.yml) |
||||
|
[](https://cla-assistant.io/meshtastic/meshtastic.js) |
||||
|
[](https://opencollective.com/meshtastic/) |
||||
|
[](https://vercel.com?utm_source=meshtastic&utm_campaign=oss) |
||||
|
|
||||
|
## Overview |
||||
|
|
||||
|
`@meshtastic/transport-node` Provides TCP transport (Node) for Meshtastic |
||||
|
devices. Installation instructions are available at |
||||
|
[JSR](https://jsr.io/@meshtastic/transport-node) |
||||
|
[NPM](https://www.npmjs.com/package/@meshtastic/transport-node) |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
```ts |
||||
|
import { MeshDevice } from "@meshtastic/core"; |
||||
|
import { TransportNode } from "@meshtastic/transport-node"; |
||||
|
|
||||
|
const transport = await TransportNode.create("10.10.0.57"); |
||||
|
const device = new MeshDevice(transport); |
||||
|
``` |
||||
|
|
||||
|
## Stats |
||||
|
|
||||
|
 |
||||
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"name": "@meshtastic/transport-node", |
||||
|
"version": "0.0.1", |
||||
|
"description": "NodeJS-specific transport layer for Meshtastic web applications.", |
||||
|
"exports": { |
||||
|
".": "./mod.ts" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export { TransportNode } from "./src/transport.ts"; |
||||
@ -0,0 +1,77 @@ |
|||||
|
import { Utils } from "@meshtastic/core"; |
||||
|
import type { Types } from "@meshtastic/core"; |
||||
|
import { Socket } from "node:net"; |
||||
|
import { Readable, Writable } from "node:stream"; |
||||
|
|
||||
|
export class TransportNode implements Types.Transport { |
||||
|
private readonly _toDevice: WritableStream<Uint8Array>; |
||||
|
private readonly _fromDevice: ReadableStream<Types.DeviceOutput>; |
||||
|
|
||||
|
/** |
||||
|
* Creates and connects a new TransportNode instance. |
||||
|
* @param hostname - The IP address or hostname of the Meshtastic device. |
||||
|
* @param port - The port number for the TCP connection (defaults to 4403). |
||||
|
* @returns A promise that resolves with a connected TransportNode instance. |
||||
|
*/ |
||||
|
public static create(hostname: string, port = 4403): Promise<TransportNode> { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
const socket = new Socket(); |
||||
|
|
||||
|
const onError = (err: Error) => { |
||||
|
socket.destroy(); |
||||
|
reject(err); |
||||
|
}; |
||||
|
|
||||
|
socket.once("error", onError); |
||||
|
|
||||
|
socket.connect(port, hostname, () => { |
||||
|
socket.removeListener("error", onError); |
||||
|
resolve(new TransportNode(socket)); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Constructs a new TransportNode. |
||||
|
* @param connection - An active Node.js net.Socket connection. |
||||
|
*/ |
||||
|
constructor(connection: Socket) { |
||||
|
connection.on("error", (err) => { |
||||
|
console.error("Socket connection error:", err); |
||||
|
}); |
||||
|
|
||||
|
const fromDeviceSource = Readable.toWeb(connection) as ReadableStream< |
||||
|
Uint8Array |
||||
|
>; |
||||
|
this._fromDevice = fromDeviceSource.pipeThrough(Utils.fromDeviceStream()); |
||||
|
|
||||
|
// Stream for data going FROM the application TO the Meshtastic device.
|
||||
|
const toDeviceTransform = new TransformStream<Uint8Array, Uint8Array>(); |
||||
|
this._toDevice = toDeviceTransform.writable; |
||||
|
|
||||
|
// The readable end of the transform is then piped to the Node.js socket.
|
||||
|
// A similar assertion is needed here because `Writable.toWeb` also returns
|
||||
|
// a generically typed stream (`WritableStream<any>`).
|
||||
|
toDeviceTransform.readable.pipeTo( |
||||
|
Writable.toWeb(connection) as WritableStream<Uint8Array>, |
||||
|
) |
||||
|
.catch((err) => { |
||||
|
console.error("Error piping data to socket:", err); |
||||
|
connection.destroy(err as Error); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The WritableStream to send data to the Meshtastic device. |
||||
|
*/ |
||||
|
public get toDevice(): WritableStream<Uint8Array> { |
||||
|
return this._toDevice; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The ReadableStream to receive data from the Meshtastic device. |
||||
|
*/ |
||||
|
public get fromDevice(): ReadableStream<Types.DeviceOutput> { |
||||
|
return this._fromDevice; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
import { build, emptyDir } from "@deno/dnt"; |
||||
|
import { join } from "jsr:@std/path@1/join"; |
||||
|
|
||||
|
interface DenoJsonConfig { |
||||
|
name: string; |
||||
|
version: string; |
||||
|
description: string; |
||||
|
imports?: Record<string, string>; |
||||
|
exports?: Record<string, string>; |
||||
|
} |
||||
|
|
||||
|
async function getJson(filePath: string) { |
||||
|
try { |
||||
|
return JSON.parse(await Deno.readTextFile(filePath)); |
||||
|
} catch (e) { |
||||
|
if (e instanceof Error) { |
||||
|
throw new Error(`Error reading or parsing ${filePath}: ${e.message}`); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Deno.args.length !== 1) { |
||||
|
console.error("Usage: deno task build:npm <path-to-package>"); |
||||
|
console.error( |
||||
|
"Example: deno task build:npm packages/core", |
||||
|
); |
||||
|
Deno.exit(1); |
||||
|
} |
||||
|
|
||||
|
const packagePath = Deno.args[0]; |
||||
|
const denoJsonPath = join(packagePath, "deno.json"); |
||||
|
const outDir = join(packagePath, "npm"); |
||||
|
|
||||
|
// Read the deno.json file to get the package metadata.
|
||||
|
let jsonContent: DenoJsonConfig; |
||||
|
|
||||
|
try { |
||||
|
jsonContent = await getJson(denoJsonPath); |
||||
|
} catch (error) { |
||||
|
console.log(`Error reading or parsing ${denoJsonPath}:`, error); |
||||
|
|
||||
|
if (error instanceof Deno.errors.NotFound) { |
||||
|
console.error(`Error: Config file not found at ${denoJsonPath}`); |
||||
|
} else { |
||||
|
console.error(`Error reading or parsing ${denoJsonPath}:`, error); |
||||
|
} |
||||
|
Deno.exit(1); |
||||
|
} |
||||
|
|
||||
|
const { name, version, description } = jsonContent; |
||||
|
|
||||
|
if (!name || !version || !description) { |
||||
|
console.error( |
||||
|
`Error: 'name', 'version', and 'description' must be defined in ${denoJsonPath}`, |
||||
|
); |
||||
|
Deno.exit(1); |
||||
|
} |
||||
|
|
||||
|
console.log(`Building ${name}@${version} from ${packagePath}...`); |
||||
|
|
||||
|
// Clean the output directory before building.
|
||||
|
await emptyDir(outDir); |
||||
|
|
||||
|
try { |
||||
|
await build({ |
||||
|
entryPoints: [join(packagePath, "mod.ts")], |
||||
|
outDir, |
||||
|
test: false, |
||||
|
shims: { |
||||
|
deno: true, |
||||
|
}, |
||||
|
package: { |
||||
|
name, |
||||
|
version, |
||||
|
description, |
||||
|
license: "GPL-3.0-only", |
||||
|
repository: { |
||||
|
type: "git", |
||||
|
url: "git+https://github.com/meshtastic/web.git", |
||||
|
}, |
||||
|
bugs: { |
||||
|
url: "https://github.com/meshtastic/web/issues", |
||||
|
}, |
||||
|
}, |
||||
|
compilerOptions: { |
||||
|
lib: ["DOM", "ESNext"], |
||||
|
}, |
||||
|
postBuild() { |
||||
|
Deno.copyFileSync("LICENSE", join(outDir, "LICENSE")); |
||||
|
Deno.copyFileSync( |
||||
|
join(packagePath, "README.md"), |
||||
|
join(outDir, "README.md"), |
||||
|
); |
||||
|
}, |
||||
|
}); |
||||
|
} catch (error) { |
||||
|
console.error(`Error building ${name}@${version}:`, error); |
||||
|
Deno.exit(1); |
||||
|
} |
||||
|
|
||||
|
console.log(`✅ Successfully built ${name}@${version} to ${outDir}`); |
||||
Loading…
Reference in new issue