@eiva/protoc-gen-fletcher 0.1.1-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @eiva/protoc-gen-fletcher
2
+
3
+ [![npm (scoped)](https://img.shields.io/npm/v/@eiva/protoc-gen-fletcher?label=latest)](https://www.npmjs.com/package/@eiva/protoc-gen-fletcher)
4
+
5
+ A `protoc` plugin that generates typed `TypedSchema<T>` modules (`.fletcher.ts`) from `.proto` files. Pairs with [`@eiva/fletcher-gateway-client`](https://www.npmjs.com/package/@eiva/fletcher-gateway-client), Fletcher's WebSocket gateway runtime.
6
+
7
+ This package is a thin Node.js shim. On first invocation it downloads the platform-matching native plugin binary from [Fletcher's GitHub Releases](https://github.com/eivacom/Fletcher/releases) and caches it under `~/.cache/protoc-gen-fletcher/<version>/`. Subsequent invocations exec the cached binary directly.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install --save-dev @eiva/protoc-gen-fletcher
13
+ ```
14
+
15
+ ```bash
16
+ npm install --save-dev @protobuf-ts/protoc
17
+ ```
18
+
19
+ ```bash
20
+ npm install --save-dev fletcher-gateway-client@npm:@eiva/fletcher-gateway-client
21
+ ```
22
+
23
+ Three pieces:
24
+
25
+ - **`@eiva/protoc-gen-fletcher`** — this plugin shim.
26
+ - **`@protobuf-ts/protoc`** — ships the `protoc` compiler binary itself. Declared as a peer dependency; npm 7+ auto-installs it, older npm versions print a warning. Install explicitly above to be safe.
27
+ - **`fletcher-gateway-client`** under the bare alias `npm:@eiva/fletcher-gateway-client` — the generated `.fletcher.ts` files import `WireTypeId` and the `TypedSchema` type from a **bare** `'fletcher-gateway-client'` specifier. Installing under that alias makes the published `@eiva/fletcher-gateway-client` resolve at runtime via standard Node resolution (no tsconfig path-alias trickery needed).
28
+
29
+ ## Usage
30
+
31
+ Wire `proto:gen` into your `package.json`. Adding it as a `prebuild` script keeps generated bindings in sync with `.proto` changes automatically — every `npm run build` regenerates first.
32
+
33
+ ```json
34
+ {
35
+ "scripts": {
36
+ "proto:gen": "node -e \"require('fs').mkdirSync('src/generated',{recursive:true})\" && protoc --fletcher_opt=ts --fletcher_out=src/generated -I proto proto/*.proto",
37
+ "prebuild": "npm run proto:gen",
38
+ "build": "tsc"
39
+ }
40
+ }
41
+ ```
42
+
43
+ Two notes:
44
+
45
+ - The leading `node -e mkdirSync(...)` ensures `src/generated/` exists before `protoc` runs — `protoc` does not auto-create its `--fletcher_out` directory, and `mkdir -p` is not portable to Windows.
46
+ - No `--plugin=` flag is needed. `protoc` searches `PATH` (which npm prepends `node_modules/.bin/` to during script execution) for `protoc-gen-<name>` based on the `--<name>_out` flag, with the OS's executable-extension rules — so the `protoc-gen-fletcher.cmd` wrapper that npm writes alongside the bin shim is found correctly on Windows.
47
+
48
+ Then:
49
+
50
+ ```bash
51
+ npm run build
52
+ ```
53
+
54
+ …regenerates `src/generated/*.fletcher.ts` and compiles. Consume the generated descriptor:
55
+
56
+ ```ts
57
+ import { FletcherClient } from '@eiva/fletcher-gateway-client';
58
+ import { Telemetry, type ITelemetry } from './generated/telemetry.fletcher.js';
59
+
60
+ const client = new FletcherClient({ url: 'ws://localhost:9090' });
61
+ await client.connect();
62
+
63
+ await client.publish('telemetry', Telemetry, {
64
+ sensor_id: 42,
65
+ temperature: 23.5,
66
+ } satisfies ITelemetry);
67
+ ```
68
+
69
+ ## Supported platforms
70
+
71
+ | OS / arch | GitHub Release asset |
72
+ |---|---|
73
+ | `linux/x64` | `protoc-gen-fletcher-linux-x64` |
74
+ | `win32/x64` | `protoc-gen-fletcher-windows-x64.exe` |
75
+
76
+ Adding macOS / arm64 is tracked in [Fletcher's issue tracker](https://github.com/eivacom/Fletcher/issues).
77
+
78
+ ## Environment variables
79
+
80
+ | Variable | Purpose |
81
+ |---|---|
82
+ | `PROTOC_GEN_FLETCHER_RELEASES_URL` | Override the GitHub Releases base URL (e.g. to test against a fork or a mirror). Default: `https://github.com/eivacom/Fletcher/releases/download`. |
83
+
84
+ ## License
85
+
86
+ LGPL-3.0-or-later © The Fletcher Authors. The shim itself is in this repo at [`protoc/npm/`](https://github.com/eivacom/Fletcher/tree/main/protoc/npm); the native plugin source is in [`protoc/`](https://github.com/eivacom/Fletcher/tree/main/protoc).
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: LGPL-3.0-or-later
3
+ // Copyright (C) 2026 The Fletcher Authors
4
+ //
5
+ // Node.js shim for the fletcher-protoc plugin. On first invocation it
6
+ // downloads the platform-matching native binary from this package's matching
7
+ // protoc-v<version> GitHub Release, caches it under
8
+ // ~/.cache/protoc-gen-fletcher/<version>/, and exec's it with inherited
9
+ // stdin/stdout/stderr so protoc's CodeGeneratorRequest / CodeGeneratorResponse
10
+ // pipe protocol passes through the shim unchanged.
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('node:fs');
15
+ const path = require('node:path');
16
+ const os = require('node:os');
17
+ const https = require('node:https');
18
+ const { spawnSync } = require('node:child_process');
19
+
20
+ const { version: VERSION } = require('../package.json');
21
+ const RELEASE_BASE =
22
+ process.env.PROTOC_GEN_FLETCHER_RELEASES_URL ||
23
+ 'https://github.com/eivacom/Fletcher/releases/download';
24
+
25
+ function platformAssetName() {
26
+ const platform = os.platform();
27
+ const arch = os.arch();
28
+ if (platform === 'linux' && arch === 'x64') {
29
+ return 'protoc-gen-fletcher-linux-x64';
30
+ }
31
+ if (platform === 'win32' && arch === 'x64') {
32
+ return 'protoc-gen-fletcher-windows-x64.exe';
33
+ }
34
+ throw new Error(
35
+ `protoc-gen-fletcher: unsupported platform ${platform}/${arch}. ` +
36
+ `Supported: linux/x64, win32/x64.`,
37
+ );
38
+ }
39
+
40
+ function cacheDir() {
41
+ return path.join(os.homedir(), '.cache', 'protoc-gen-fletcher', VERSION);
42
+ }
43
+
44
+ function followRedirects(url, depth) {
45
+ if (depth > 5) {
46
+ return Promise.reject(new Error(`Too many redirects: ${url}`));
47
+ }
48
+ return new Promise((resolve, reject) => {
49
+ https
50
+ .get(url, (res) => {
51
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
52
+ res.resume();
53
+ return resolve(followRedirects(res.headers.location, depth + 1));
54
+ }
55
+ if (res.statusCode !== 200) {
56
+ return reject(
57
+ new Error(
58
+ `Download failed (${res.statusCode}): ${url}\n` +
59
+ `Check that the release exists at ` +
60
+ `https://github.com/eivacom/Fletcher/releases/tag/protoc-v${VERSION}`,
61
+ ),
62
+ );
63
+ }
64
+ const chunks = [];
65
+ res.on('data', (chunk) => chunks.push(chunk));
66
+ res.on('end', () => resolve(Buffer.concat(chunks)));
67
+ res.on('error', reject);
68
+ })
69
+ .on('error', reject);
70
+ });
71
+ }
72
+
73
+ async function ensureBinary() {
74
+ const asset = platformAssetName();
75
+ const cacheBin = path.join(cacheDir(), asset);
76
+ if (fs.existsSync(cacheBin)) {
77
+ return cacheBin;
78
+ }
79
+
80
+ const url = `${RELEASE_BASE}/protoc-v${VERSION}/${asset}`;
81
+ process.stderr.write(`protoc-gen-fletcher: downloading ${url}\n`);
82
+
83
+ fs.mkdirSync(cacheDir(), { recursive: true });
84
+ const buf = await followRedirects(url, 0);
85
+
86
+ // Atomic write: write to a unique tmp file in the same dir, then rename.
87
+ // Multiple npm scripts may invoke the shim in parallel; rename within the
88
+ // same filesystem is atomic so concurrent callers cannot see a half-written
89
+ // file.
90
+ const tmp = `${cacheBin}.tmp.${process.pid}`;
91
+ fs.writeFileSync(tmp, buf, { mode: 0o755 });
92
+ fs.renameSync(tmp, cacheBin);
93
+
94
+ return cacheBin;
95
+ }
96
+
97
+ (async () => {
98
+ try {
99
+ const binary = await ensureBinary();
100
+ // stdio: 'inherit' makes the child binary share this Node process's
101
+ // stdin/stdout/stderr handles. protoc spawns us with pipe-based
102
+ // stdin/stdout for the plugin protocol; the binary inherits those exact
103
+ // pipes and reads/writes them directly. Node never touches the bytes.
104
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: 'inherit' });
105
+ process.exit(result.status === null ? 1 : result.status);
106
+ } catch (err) {
107
+ process.stderr.write(`protoc-gen-fletcher: ${err.message}\n`);
108
+ process.exit(1);
109
+ }
110
+ })();
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@eiva/protoc-gen-fletcher",
3
+ "version": "0.1.1-alpha",
4
+ "description": "protoc plugin that generates typed TypeScript schema descriptors for Fletcher's positional wire format. Downloads the platform-matching native plugin binary from GitHub Releases on first invocation.",
5
+ "license": "LGPL-3.0-or-later",
6
+ "homepage": "https://github.com/eivacom/Fletcher/tree/main/protoc",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/eivacom/Fletcher.git",
10
+ "directory": "protoc/npm"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/eivacom/Fletcher/issues"
14
+ },
15
+ "keywords": [
16
+ "protoc",
17
+ "protobuf",
18
+ "fletcher",
19
+ "code-generation",
20
+ "typescript"
21
+ ],
22
+ "bin": {
23
+ "protoc-gen-fletcher": "./bin/protoc-gen-fletcher.js"
24
+ },
25
+ "files": [
26
+ "bin/",
27
+ "README.md"
28
+ ],
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "peerDependencies": {
33
+ "@protobuf-ts/protoc": "^2.9.5"
34
+ }
35
+ }