Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 0 additions & 29 deletions .eslintrc.js

This file was deleted.

17 changes: 13 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
- package-ecosystem: 'npm'
directory: '/'
groups:
npm-dependencies:
patterns: ['*']
schedule:
interval: 'weekly'
cooldown:
default-days: 7
- package-ecosystem: 'github-actions'
directory: '/'
groups:
github-actions:
patterns: ["*"]
patterns: ['*']
schedule:
interval: "weekly"
interval: 'weekly'
cooldown:
default-days: 7
31 changes: 16 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [^16, ^18, ^20]
etcd-version: [v3.1.5, v3.2.4, v3.3.9]
node-version: [24]
etcd-version: [3.1.5, 3.2.4, 3.3.9, 3.5.33, 3.6.14]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: curl -L https://github.com/coreos/etcd/releases/download/${{ matrix.etcd-version }}/etcd-${{ matrix.etcd-version }}-linux-amd64.tar.gz -o /tmp/etcd-${{ matrix.etcd-version }}-linux-amd64.tar.gz
- run: mkdir -p /tmp/etcd
- run: tar xzvf /tmp/etcd-${{ matrix.etcd-version }}-linux-amd64.tar.gz -C /tmp/etcd --strip-components=1
- run: /tmp/etcd/etcd --advertise-client-urls https://127.0.0.1:2379 --listen-client-urls https://127.0.0.1:2379 --cert-file ${{ github.workspace }}/src/test/certs/certs/etcd0.localhost.crt --key-file ${{ github.workspace }}/src/test/certs/private/etcd0.localhost.key > /dev/null &
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: |
curl --fail --location https://github.com/etcd-io/etcd/releases/download/v${{ matrix.etcd-version }}/etcd-v${{ matrix.etcd-version }}-linux-amd64.tar.gz --output "$RUNNER_TEMP/etcd.tar.gz"
mkdir -p "$RUNNER_TEMP/etcd"
tar xzf "$RUNNER_TEMP/etcd.tar.gz" --directory "$RUNNER_TEMP/etcd" --strip-components=1
"$RUNNER_TEMP/etcd/etcd" --advertise-client-urls https://127.0.0.1:2379 --listen-client-urls https://127.0.0.1:2379 --cert-file ${{ github.workspace }}/src/test/certs/certs/etcd0.localhost.crt --key-file ${{ github.workspace }}/src/test/certs/private/etcd0.localhost.key > /dev/null &

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build:ts
- run: npm test
env:
ETCD_VERSION: ${{ matrix.etcd-version }}
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build:ts
- run: npm test
env:
ETCD_VERSION: ${{ matrix.etcd-version }}
6 changes: 0 additions & 6 deletions .mocharc.js

This file was deleted.

4 changes: 0 additions & 4 deletions .npmignore

This file was deleted.

3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"search.exclude": {
Expand Down
104 changes: 77 additions & 27 deletions bin/generate-methods.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
'use strict';

/**
* This script parses downloaded protobuf files to output TypeScript typings
* and methods to call declared the declared types.
*
* Usage:
*
* > node bin/generate-methods proto/rpc.proto > src/rpc.ts
* > node bin/generate-methods [proto/rpc.proto] [output-file]
*
* protobufjs does have a TypeScript generator but its output isn't very useful
* for grpc, much less this client. Rather than reprocessing it, let's just
* create the output ourselves since it's pretty simple (~100 lines of code).
*/

const prettier = require('prettier');
const pbjs = require('protobufjs');
const fs = require('fs');
const _ = require('lodash');

const contents = fs.readFileSync(process.argv[2]).toString();
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import pbjs from 'protobufjs';
import prettier from 'prettier';

const inputPath = process.argv[2] ?? fileURLToPath(new URL('../proto/rpc.proto', import.meta.url));
const outputPath = process.argv[3] ?? fileURLToPath(new URL('../src/rpc.ts', import.meta.url));
const packageJson = JSON.parse(
fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
);
const contents = fs.readFileSync(inputPath).toString();
const lines = contents.split('\n');
const rootPackageName = contents.match(/^\s*package\s+([^.;\s]+)/m)?.[1];

const singleLineCommentRe = /\/\/\s*(.+)$/;
const singleLineCommentStandaloneRe = /^\s*\/\/\s*/;
Expand All @@ -28,6 +32,7 @@ const indentation = ' ';
const enums = [];
const services = {};
const templates = {};
const templateTagRe = /<%=([\s\S]+?)%>|<%([\s\S]+?)%>/g;

const pbTypeAliases = {
bool: 'boolean',
Expand Down Expand Up @@ -77,25 +82,56 @@ function emit(string) {
return emit;
}

function writeOut() {
async function writeOut() {
fs.writeFileSync(
`${__dirname}/../src/rpc.ts`,
prettier.format(result, {
...require('../package.json').prettier,
outputPath,
await prettier.format(result, {
...packageJson.prettier,
parser: 'typescript',
}),
);
}

function compileTemplate(contents) {
let body = '';
let cursor = 0;
let interpolationIndex = 0;

for (const match of contents.matchAll(templateTagRe)) {
body += `output += ${JSON.stringify(contents.slice(cursor, match.index))};\n`;
if (match[1] !== undefined) {
const variable = `value${interpolationIndex++}`;
body += `const ${variable} = (${match[1]});\n`;
body += `output += ${variable} == null ? '' : ${variable};\n`;
} else {
body += `${match[2]}\n`;
}
cursor = match.index + match[0].length;
}

body += `output += ${JSON.stringify(contents.slice(cursor))};\n`;
return new Function(
'context',
`let output = '';\nwith (context) {\n${body}}\nreturn output;`,
);
}

function lowerFirst(value) {
return value ? value[0].toLowerCase() + value.slice(1) : '';
}

function template(name, params) {
if (!templates[name]) {
templates[name] = _.template(fs.readFileSync(`${__dirname}/template/${name}.tmpl`, 'utf8'));
templates[name] = compileTemplate(
fs.readFileSync(new URL(`template/${name}.tmpl`, import.meta.url), 'utf8'),
);
}

params = Object.assign(params || {}, {
getCommentPrefixing,
getLineContaining,
formatType,
lowerFirst,
aliases: pbTypeAliases,
});

Expand Down Expand Up @@ -185,7 +221,7 @@ function generateMethodCalls(node, name) {
const service = (services[name] = { cls: `${name}Client`, methods: new Map() });
template('class-header', { name });

_.forOwn(node.methods, (method, mname) => {
for (const [mname, method] of Object.entries(node.methods)) {
const req = messages.find(method.requestType);
const res = messages.find(method.responseType);

Expand All @@ -211,7 +247,7 @@ function generateMethodCalls(node, name) {
} else {
template('basic-method', params);
}
});
}

emit('}\n\n');
}
Expand All @@ -238,16 +274,29 @@ function generateEnum(node, name) {
}

function walk(ast, iterator, path = []) {
_.forOwn(ast, (node, name) => {
const entries = Object.entries(ast);
if (path.length === 0) {
entries.sort(([first], [second]) => {
if (first === rootPackageName) {
return -1;
}
if (second === rootPackageName) {
return 1;
}
return first < second ? -1 : first > second ? 1 : 0;
});
}

for (const [name, node] of entries) {
if (!node) {
return;
continue;
}
if (node.nested) {
walk(node.nested, iterator, path.concat(name));
}

iterator(node, name, path);
});
}
}

function markResponsesFor(message, seen = []) {
Expand All @@ -258,8 +307,7 @@ function markResponsesFor(message, seen = []) {

message.response = true;

_(message.node.fields)
.values()
Object.values(message.node.fields)
.map(f => messages.find(f.type))
.filter(Boolean)
.forEach(m => markResponsesFor(m, seen.concat(name)));
Expand All @@ -273,7 +321,7 @@ function prepareForGeneration(ast) {

if (node.fields) {
messages.add(name, {
empty: _.isEmpty(node.fields),
empty: Object.keys(node.fields).length === 0,
node,
response: false,
});
Expand All @@ -282,8 +330,7 @@ function prepareForGeneration(ast) {

walk(ast, (node, name) => {
if (node.methods) {
_(node.methods)
.values()
Object.values(node.methods)
.map(m => messages.find(m.responseType))
.filter(Boolean)
.forEach(m => markResponsesFor(m));
Expand All @@ -309,11 +356,14 @@ function codeGen(ast) {
}

new pbjs.Root()
.load(process.argv[2], { keepCase: true })
.load(inputPath, { keepCase: true })
.then(ast => {
prepareForGeneration(ast.nested);
template('rpc-prefix');
codeGen(ast.nested);
writeOut();
return writeOut();
})
.catch(err => console.error(err.stack));
.catch(err => {
console.error(err.stack);
process.exitCode = 1;
});
49 changes: 0 additions & 49 deletions bin/tame-typedoc.js

This file was deleted.

4 changes: 2 additions & 2 deletions bin/template/basic-method.tmpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
--<%= getCommentPrefixing(`rpc ${name}(`) %>
public <%= _.lowerFirst(name) %>(<%= req.empty ? '' : `req: ${requestTsType}, ` %>options?: grpc.CallOptions): Promise<<%= responseTsType %>> {
return this.client.exec('<%= service %>', '<%= _.lowerFirst(name) %>', <%= req.empty ? '{}' : 'req' %>, options);
public <%= lowerFirst(name) %>(<%= req.empty ? '' : `req: ${requestTsType}, ` %>options?: grpc.CallOptions): Promise<<%= responseTsType %>> {
return this.client.exec('<%= service %>', '<%= lowerFirst(name) %>', <%= req.empty ? '{}' : 'req' %>, options);
}
2 changes: 1 addition & 1 deletion bin/template/call-context.tmpl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
| { service: '<%= service %>', method: '<%= _.lowerFirst(name) %>', isStream: <%= !!(responseStream || requestStream) %>, <%= req.empty || requestStream ? '' : `params: ${requestTsType}` %> }
| { service: '<%= service %>', method: '<%= lowerFirst(name) %>', isStream: <%= !!(responseStream || requestStream) %>, <%= req.empty || requestStream ? '' : `params: ${requestTsType}` %> }
Loading
Loading