diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/workflows/test-node.yml b/.github/workflows/test-node.yml index e12ceb1..06d9f7d 100644 --- a/.github/workflows/test-node.yml +++ b/.github/workflows/test-node.yml @@ -14,10 +14,10 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - run: npm test + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test diff --git a/.gitignore b/.gitignore index ee50711..c0da0e8 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,8 @@ dist # example.js spec folder /spec + +package-lock.json + +test/test-storage +.vscode diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e5864fb --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +prettier-config-holepunch diff --git a/README.md b/README.md index a7c6c27..150d848 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # hyperdispatch + Generate operations/endpoints using Hyperschema diff --git a/builder.cjs b/builder.cjs index 22234c4..04cdd0b 100644 --- a/builder.cjs +++ b/builder.cjs @@ -9,25 +9,25 @@ const MESSAGES_FILE_NAME = 'messages.js' const DISPATCH_JSON_FILE_NAME = 'dispatch.json' class HyperdispatchNamespace { - constructor (hyperdispatch, name) { + constructor(hyperdispatch, name) { this.hyperdispatch = hyperdispatch this.name = name } - register (description) { + register(description) { const fqn = '@' + this.name + '/' + description.name this.hyperdispatch.register(fqn, description) } } module.exports = class Hyperdispatch { - constructor (schema, dispatchJson, { offset, dispatchDir = null, schemaDir = null } = {}) { + constructor(schema, dispatchJson, { offset, dispatchDir = null, schemaDir = null } = {}) { if (dispatchJson && offset && dispatchJson.offset !== offset) { throw new Error('Cannot change the hyperdispatch offset once it has been defined once') } this.schema = schema this.version = dispatchJson ? dispatchJson.version : 0 - this.offset = dispatchJson ? dispatchJson.offset : (offset || 0) + this.offset = dispatchJson ? dispatchJson.offset : offset || 0 this.dispatchDir = dispatchDir this.schemaDir = schemaDir @@ -51,16 +51,18 @@ module.exports = class Hyperdispatch { static esm = false - namespace (name) { + namespace(name) { return new HyperdispatchNamespace(this, name) } - register (fqn, description) { + register(fqn, description) { const existingByName = this.handlersByName.get(fqn) - const existingById = Number.isInteger(description.id) ? this.handlersById.get(description.id) : null + const existingById = Number.isInteger(description.id) + ? this.handlersById.get(description.id) + : null if (existingByName && existingById) { if (existingByName !== existingById) throw new Error('ID/Name mismatch for handler: ' + fqn) - if (Number.isInteger(description.id) && (existingByName.id !== description.id)) { + if (Number.isInteger(description.id) && existingByName.id !== description.id) { throw new Error('Cannot change the assigned ID for handler: ' + fqn) } } @@ -68,7 +70,7 @@ module.exports = class Hyperdispatch { const type = this.schema.resolve(description.requestType) if (!type) throw new Error('Invalid request type') - if (existingByName && (existingByName.type !== type)) { + if (existingByName && existingByName.type !== type) { throw new Error('Cannot alter the request type for a handler') } @@ -94,7 +96,7 @@ module.exports = class Hyperdispatch { } } - toJSON () { + toJSON() { return { version: this.version, offset: this.offset, @@ -102,7 +104,7 @@ module.exports = class Hyperdispatch { } } - static from (schemaJson, dispatchJson, opts) { + static from(schemaJson, dispatchJson, opts) { const schema = Hyperschema.from(schemaJson) if (typeof dispatchJson === 'string') { const jsonFilePath = p.join(p.resolve(dispatchJson), DISPATCH_JSON_FILE_NAME) @@ -120,11 +122,11 @@ module.exports = class Hyperdispatch { return new this(schema, dispatchJson, opts) } - toCode ({ esm = this.constructor.esm, filename } = {}) { + toCode({ esm = this.constructor.esm, filename } = {}) { return generateCode(this, { esm, filename }) } - static toDisk (hyperdispatch, dispatchDir, opts = {}) { + static toDisk(hyperdispatch, dispatchDir, opts = {}) { if (typeof dispatchDir === 'object' && dispatchDir) { opts = dispatchDir dispatchDir = null @@ -139,7 +141,9 @@ module.exports = class Hyperdispatch { const dispatchJsonPath = p.join(p.resolve(dispatchDir), DISPATCH_JSON_FILE_NAME) const codePath = p.join(p.resolve(dispatchDir), CODE_FILE_NAME) - fs.writeFileSync(dispatchJsonPath, JSON.stringify(hyperdispatch.toJSON(), null, 2), { encoding: 'utf-8' }) + fs.writeFileSync(dispatchJsonPath, JSON.stringify(hyperdispatch.toJSON(), null, 2), { + encoding: 'utf-8' + }) fs.writeFileSync(messagesPath, hyperdispatch.schema.toCode(opts), { encoding: 'utf-8' }) fs.writeFileSync(codePath, generateCode(hyperdispatch, opts), { encoding: 'utf-8' }) } diff --git a/lib/codegen.js b/lib/codegen.js index 3040975..fa3c2d4 100644 --- a/lib/codegen.js +++ b/lib/codegen.js @@ -1,17 +1,17 @@ const s = require('generate-string') -module.exports = function generateCode (hyperdispatch, { esm = false }) { +module.exports = function generateCode(hyperdispatch, { esm = false }) { let str = '' str += '// This file is autogenerated by the hyperdispatch compiler\n' str += '/* eslint-disable camelcase */\n' str += '\n' if (esm) { - str += 'import { c, b4a, assert } from \'hyperdispatch/runtime\'\n' - str += 'import { version, getEncoding, setVersion } from \'./messages.js\'\n' + str += "import { c, b4a, assert } from 'hyperdispatch/runtime'\n" + str += "import { version, getEncoding, setVersion } from './messages.js'\n" } else { - str += 'const { c, b4a, assert } = require(\'hyperdispatch/runtime\')\n' - str += 'const { version, getEncoding, setVersion } = require(\'./messages.js\')\n' + str += "const { c, b4a, assert } = require('hyperdispatch/runtime')\n" + str += "const { version, getEncoding, setVersion } = require('./messages.js')\n" } str += '\n' @@ -35,7 +35,7 @@ module.exports = function generateCode (hyperdispatch, { esm = false }) { str += ' break\n' } str += ' default:\n' - str += ' throw new Error(\'Cannot register a handler for a nonexistent route: \' + name)\n' + str += " throw new Error('Cannot register a handler for a nonexistent route: ' + name)\n" str += ' }\n' str += ' this._missing--\n' str += ' }\n' @@ -61,7 +61,7 @@ module.exports = function generateCode (hyperdispatch, { esm = false }) { str += ` return this._handler${handler.id}(op.value, context)\n` } str += ' default:\n' - str += ' throw new Error(\'Handler not found for ID:\' + op.id)\n' + str += " throw new Error('Handler not found for ID:' + op.id)\n" str += ' }\n' str += ' }\n' str += '}\n' @@ -114,7 +114,7 @@ module.exports = function generateCode (hyperdispatch, { esm = false }) { str += ` return route${handler.id}\n` } str += ' default:\n' - str += ' throw new Error(\'Handler not found for name: \' + name)\n' + str += " throw new Error('Handler not found for name: ' + name)\n" str += ' }\n' str += '}\n' str += '\n' @@ -126,7 +126,7 @@ module.exports = function generateCode (hyperdispatch, { esm = false }) { str += ` return route${handler.id}\n` } str += ' default:\n' - str += ' throw new Error(\'Handler not found for ID: \' + id)\n' + str += " throw new Error('Handler not found for ID: ' + id)\n" str += ' }\n' str += '}\n' str += '\n' diff --git a/package.json b/package.json index b305303..fa0d650 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ } }, "scripts": { - "test": "standard && brittle test/index.js && npm run example:rebuild", + "format": "prettier . --write", + "test": "prettier . --check && brittle test/index.js && npm run example:rebuild", "example:rebuild": "node example.js && node example.js" }, "repository": { @@ -37,7 +38,8 @@ "homepage": "https://github.com/holepunchto/hyperdispatch#readme", "devDependencies": { "brittle": "^3.7.0", - "standard": "^17.1.0", + "prettier": "^3.6.2", + "prettier-config-holepunch": "^2.0.0", "test-tmp": "^1.3.0", "which-runtime": "^1.3.0" }, diff --git a/test/basic.js b/test/basic.js index 38d882f..8c97575 100644 --- a/test/basic.js +++ b/test/basic.js @@ -7,12 +7,12 @@ const whichRuntime = require('which-runtime') const { createTestSchema } = require('./helpers') -test('basic sync switch', async t => { +test('basic sync switch', async (t) => { t.plan(6) const hd = await createTestSchema(t) hd.rebuild({ - schema: schema => { + schema: (schema) => { const ns = schema.namespace('test') ns.register({ name: 'request', @@ -28,7 +28,7 @@ test('basic sync switch', async t => { ] }) }, - dispatch: hyperdispatch => { + dispatch: (hyperdispatch) => { const ns = hyperdispatch.namespace('test') ns.register({ name: 'test-request-1', @@ -58,37 +58,40 @@ test('basic sync switch', async t => { await r.dispatch(encode('@test/test-request-2', { id: 20, str: 'world' }), 'another-context') }) -test('basic sync switch + offset', async t => { +test('basic sync switch + offset', async (t) => { const hd = await createTestSchema(t) - hd.rebuild({ - schema: schema => { - const ns = schema.namespace('test') - ns.register({ - name: 'request', - fields: [ - { - name: 'id', - type: 'uint' - }, - { - name: 'str', - type: 'string' - } - ] - }) + hd.rebuild( + { + schema: (schema) => { + const ns = schema.namespace('test') + ns.register({ + name: 'request', + fields: [ + { + name: 'id', + type: 'uint' + }, + { + name: 'str', + type: 'string' + } + ] + }) + }, + dispatch: (hyperdispatch) => { + const ns = hyperdispatch.namespace('test') + ns.register({ + name: 'test-request-1', + requestType: '@test/request' + }) + ns.register({ + name: 'test-request-2', + requestType: '@test/request' + }) + } }, - dispatch: hyperdispatch => { - const ns = hyperdispatch.namespace('test') - ns.register({ - name: 'test-request-1', - requestType: '@test/request' - }) - ns.register({ - name: 'test-request-2', - requestType: '@test/request' - }) - } - }, { offset: 10 }) + { offset: 10 } + ) const { encode } = hd.module const msg1 = encode('@test/test-request-1', { id: 10, str: 'hello' }) @@ -97,37 +100,40 @@ test('basic sync switch + offset', async t => { t.is(c.decode(c.uint, msg2), 11) }) -test('can both encode and decode ops', async t => { +test('can both encode and decode ops', async (t) => { const hd = await createTestSchema(t) - hd.rebuild({ - schema: schema => { - const ns = schema.namespace('test') - ns.register({ - name: 'request', - fields: [ - { - name: 'id', - type: 'uint' - }, - { - name: 'str', - type: 'string' - } - ] - }) + hd.rebuild( + { + schema: (schema) => { + const ns = schema.namespace('test') + ns.register({ + name: 'request', + fields: [ + { + name: 'id', + type: 'uint' + }, + { + name: 'str', + type: 'string' + } + ] + }) + }, + dispatch: (hyperdispatch) => { + const ns = hyperdispatch.namespace('test') + ns.register({ + name: 'test-request-1', + requestType: '@test/request' + }) + ns.register({ + name: 'test-request-2', + requestType: '@test/request' + }) + } }, - dispatch: hyperdispatch => { - const ns = hyperdispatch.namespace('test') - ns.register({ - name: 'test-request-1', - requestType: '@test/request' - }) - ns.register({ - name: 'test-request-2', - requestType: '@test/request' - }) - } - }, { offset: 10 }) + { offset: 10 } + ) const { encode, decode } = hd.module const encoded1 = encode('@test/test-request-1', { id: 10, str: 'hello' }) @@ -141,66 +147,72 @@ test('can both encode and decode ops', async t => { t.is(decoded2.value.str, 'world') }) -test('basic two namespaces with interleaved op additions', async t => { +test('basic two namespaces with interleaved op additions', async (t) => { t.plan(6) const hd = await createTestSchema(t) - hd.rebuild({ - schema: schema => { - const ns = schema.namespace('test') - ns.register({ - name: 'request', - fields: [ - { - name: 'id', - type: 'uint' - } - ] - }) + hd.rebuild( + { + schema: (schema) => { + const ns = schema.namespace('test') + ns.register({ + name: 'request', + fields: [ + { + name: 'id', + type: 'uint' + } + ] + }) + }, + dispatch: (hyperdispatch) => { + const ns1 = hyperdispatch.namespace('test1') + ns1.register({ + name: 'test-request-1', + requestType: '@test/request' + }) + const ns2 = hyperdispatch.namespace('test2') + ns2.register({ + name: 'test-request-2', + requestType: '@test/request' + }) + } }, - dispatch: hyperdispatch => { - const ns1 = hyperdispatch.namespace('test1') - ns1.register({ - name: 'test-request-1', - requestType: '@test/request' - }) - const ns2 = hyperdispatch.namespace('test2') - ns2.register({ - name: 'test-request-2', - requestType: '@test/request' - }) - } - }, { offset: 2 }) - hd.rebuild({ - schema: schema => { - const ns = schema.namespace('test') - ns.register({ - name: 'request', - fields: [ - { - name: 'id', - type: 'uint' - } - ] - }) + { offset: 2 } + ) + hd.rebuild( + { + schema: (schema) => { + const ns = schema.namespace('test') + ns.register({ + name: 'request', + fields: [ + { + name: 'id', + type: 'uint' + } + ] + }) + }, + dispatch: (hyperdispatch) => { + const ns1 = hyperdispatch.namespace('test1') + ns1.register({ + name: 'test-request-1', + requestType: '@test/request' + }) + const ns2 = hyperdispatch.namespace('test2') + ns2.register({ + name: 'test-request-2', + requestType: '@test/request' + }) + ns1.register({ + name: 'test-request-3', + requestType: '@test/request' + }) + } }, - dispatch: hyperdispatch => { - const ns1 = hyperdispatch.namespace('test1') - ns1.register({ - name: 'test-request-1', - requestType: '@test/request' - }) - const ns2 = hyperdispatch.namespace('test2') - ns2.register({ - name: 'test-request-2', - requestType: '@test/request' - }) - ns1.register({ - name: 'test-request-3', - requestType: '@test/request' - }) - } - }, { offset: 2 }) + { offset: 2 } + ) const { encode, Router } = hd.module const r = new Router() @@ -222,33 +234,11 @@ test('basic two namespaces with interleaved op additions', async t => { await r.dispatch(encode('@test1/test-request-3', { id: 30 }), 'another-context') }) -test('cannot change the offset', async t => { +test('cannot change the offset', async (t) => { const hd = await createTestSchema(t) - hd.rebuild({ - schema: schema => { - const ns = schema.namespace('test') - ns.register({ - name: 'request', - fields: [ - { - name: 'id', - type: 'uint' - } - ] - }) - }, - dispatch: hyperdispatch => { - const ns1 = hyperdispatch.namespace('test1') - ns1.register({ - name: 'test-request-1', - requestType: '@test/request' - }) - } - }, { offset: 2 }) - - try { - hd.rebuild({ - schema: schema => { + hd.rebuild( + { + schema: (schema) => { const ns = schema.namespace('test') ns.register({ name: 'request', @@ -260,25 +250,53 @@ test('cannot change the offset', async t => { ] }) }, - dispatch: hyperdispatch => { + dispatch: (hyperdispatch) => { const ns1 = hyperdispatch.namespace('test1') ns1.register({ name: 'test-request-1', requestType: '@test/request' }) - ns1.register({ - name: 'test-request-2', - requestType: '@test/request' - }) } - }, { offset: 4 }) + }, + { offset: 2 } + ) + + try { + hd.rebuild( + { + schema: (schema) => { + const ns = schema.namespace('test') + ns.register({ + name: 'request', + fields: [ + { + name: 'id', + type: 'uint' + } + ] + }) + }, + dispatch: (hyperdispatch) => { + const ns1 = hyperdispatch.namespace('test1') + ns1.register({ + name: 'test-request-1', + requestType: '@test/request' + }) + ns1.register({ + name: 'test-request-2', + requestType: '@test/request' + }) + } + }, + { offset: 4 } + ) t.fail('rebuilding with different offset did not throw') } catch { t.pass('rebuilding with different offset should throw') } }) -test('test schema passes linter', async t => { +test('test schema passes linter', async (t) => { if (whichRuntime.isWindows) { t.comment('Skipped on windows because standard does not seem to run out of the box') return @@ -289,7 +307,7 @@ test('test schema passes linter', async t => { const dispatchDir = path.join(hd.dir, 'hyperdispatch') hd.rebuild({ - schema: schema => { + schema: (schema) => { const ns = schema.namespace('test') ns.register({ name: 'request', @@ -305,7 +323,7 @@ test('test schema passes linter', async t => { ] }) }, - dispatch: hyperdispatch => { + dispatch: (hyperdispatch) => { const ns = hyperdispatch.namespace('test') ns.register({ name: 'test-request-1', @@ -318,25 +336,27 @@ test('test schema passes linter', async t => { } }) - const exProc = spawn('npx', ['standard', dispatchDir]) + const exProc = spawn('npx', ['lunte', dispatchDir]) exProc.on('close', (status) => { t.is(status, 0, 'linter detected no issues') }) // In case the test is aborted, we kill the standard process - process.on('exit', () => { exProc.kill('SIGKILL') }) + process.on('exit', () => { + exProc.kill('SIGKILL') + }) - exProc.stderr.on('data', d => { + exProc.stderr.on('data', (d) => { console.error(`[linter error output] ${d.toString()}`) }) }) -test('basic dispatch to non-existent route throws', async t => { +test('basic dispatch to non-existent route throws', async (t) => { t.plan(4) const hd = await createTestSchema(t) hd.rebuild({ - schema: schema => { + schema: (schema) => { const ns = schema.namespace('test') ns.register({ name: 'request', @@ -352,7 +372,7 @@ test('basic dispatch to non-existent route throws', async t => { ] }) }, - dispatch: hyperdispatch => { + dispatch: (hyperdispatch) => { const ns = hyperdispatch.namespace('test') ns.register({ name: 'test-request-1', @@ -373,8 +393,5 @@ test('basic dispatch to non-existent route throws', async t => { await r.dispatch(encode('@test/test-request-1', { id: 10, str: 'hello' }), 'some-context') const badMsg = { id: -1, name: '@test/invalid', value: 'error' } - await t.exception( - r.dispatch(badMsg, 'invalid-context'), - /Handler not found for ID:-1/ - ) + await t.exception(r.dispatch(badMsg, 'invalid-context'), /Handler not found for ID:-1/) }) diff --git a/test/helpers/index.js b/test/helpers/index.js index acc3368..a412de8 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -6,7 +6,7 @@ const Hyperschema = require('hyperschema') const Hyperdispatch = require('../../builder.cjs') class TestBuilder { - constructor (dir) { + constructor(dir) { this.dir = dir this.schemaDir = p.join(dir, 'hyperschema') this.dispatchDir = p.join(dir, 'hyperdispatch') @@ -14,7 +14,7 @@ class TestBuilder { this.version = 0 } - rebuild (builder, opts) { + rebuild(builder, opts) { const schema = Hyperschema.from(this.schemaDir) builder.schema(schema) Hyperschema.toDisk(schema) @@ -34,7 +34,7 @@ class TestBuilder { } } -async function createTestSchema (t) { +async function createTestSchema(t) { const dir = await tmp(t, { dir: p.join(__dirname, '../test-storage') }) // Copy the runtime into the tmp dir so that we don't need to override it in the codegen diff --git a/test/index.js b/test/index.js index 5a633ca..a26e2dc 100644 --- a/test/index.js +++ b/test/index.js @@ -2,7 +2,7 @@ runTests() -async function runTests () { +async function runTests() { const test = (await import('brittle')).default test.pause()