25 Commits

Author SHA1 Message Date
jwetzell cc14c4c886 bump @jwetzell/osc to v1.6.0 2024-10-25 12:15:25 -05:00
jwetzell a4b29de9c9 Merge pull request #27 from jwetzell/dependabot/npm_and_yarn/types/node-22.7.7
Bump @types/node from 22.7.5 to 22.7.7
2024-10-25 12:13:41 -05:00
jwetzell 86f062a2ce fix import in decode test 2024-10-25 09:25:07 -05:00
jwetzell b7d129556a Merge pull request #29 from jwetzell/esm-and-cjs
add support for bundling both esm and cjs versions
2024-10-25 09:15:48 -05:00
jwetzell 2ee515ae5b npm run format:write 2024-10-25 09:10:52 -05:00
jwetzell 2089893a25 fix import in tests 2024-10-25 09:05:33 -05:00
jwetzell 2377599f12 add support for bundling both esm and cjs versions 2024-10-24 16:59:17 -05:00
jwetzell f18327d7b1 Create LICENSE 2024-10-22 08:35:24 -05:00
dependabot[bot] ad4efcf43d Bump @types/node from 22.7.5 to 22.7.7
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.7.5 to 22.7.7.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 17:47:28 +00:00
jwetzell 1b10930a8e bump @jwetzell/osc to v1.5.0 2024-10-21 12:46:15 -05:00
jwetzell 0c9fafc129 Merge pull request #28 from jwetzell/generic-decode
add function to guess type and decode
2024-10-21 12:45:52 -05:00
jwetzell aa1cae428b add function to guess type and decode 2024-10-21 12:44:43 -05:00
jwetzell 5449c8ec5b bump @jwetzell/osc to v1.4.0 2024-10-15 21:57:18 -05:00
jwetzell bf355706bb more bundle tests 2024-10-15 21:55:51 -05:00
jwetzell 31e0cb7d23 Merge pull request #26 from jwetzell/bundle-min-size
fix bundle minimum size check
2024-10-15 21:48:08 -05:00
jwetzell af6dd46303 fix bundle minimum size check 2024-10-15 21:47:33 -05:00
jwetzell 2ea1b928ba more tests 2024-10-15 21:41:47 -05:00
jwetzell 7bffb53691 split up passing and failing tests 2024-10-15 21:38:53 -05:00
jwetzell abc9445b2c npm run format:write 2024-10-15 21:36:53 -05:00
jwetzell 69a3eeec9f Merge pull request #25 from jwetzell/osc-address-only
allow no type string
2024-10-15 21:36:16 -05:00
jwetzell 99ae9d8a3b allow no type string 2024-10-15 21:35:53 -05:00
jwetzell b3dc125df3 npm run format:write 2024-10-14 21:35:16 -05:00
jwetzell 713fdc8959 Merge pull request #24 from jwetzell/message-error-handling
rework message testing for easier addition of failing tests
2024-10-14 21:34:39 -05:00
jwetzell 1a9decfa30 rework tests to have easily add tests that should fail 2024-10-14 21:33:35 -05:00
jwetzell 374bf0d39f messageToBuffer should error on invalid address 2024-10-14 21:15:29 -05:00
15 changed files with 421 additions and 170 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Joel Wetzell
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.
+5 -5
View File
@@ -255,9 +255,9 @@
"peer": true
},
"node_modules/@types/node": {
"version": "22.7.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz",
"integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
"version": "22.7.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz",
"integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==",
"dev": true,
"dependencies": {
"undici-types": "~6.19.2"
@@ -3651,10 +3651,10 @@
},
"packages/osc": {
"name": "@jwetzell/osc",
"version": "1.3.0",
"version": "1.6.0",
"license": "MIT",
"devDependencies": {
"@types/node": "22.7.5",
"@types/node": "22.7.7",
"rimraf": "6.0.1",
"typescript": "5.6.3"
}
+16 -5
View File
@@ -1,12 +1,23 @@
{
"name": "@jwetzell/osc",
"version": "1.3.0",
"version": "1.6.0",
"description": "",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts"
}
},
"scripts": {
"prebuild": "rimraf dist",
"build": "tsc",
"build": "npm run build:cjs && npm run build:esm && npm run build:types",
"build:cjs": "tsc -p tsconfig.cjs.json",
"build:esm": "tsc -p tsconfig.esm.json",
"build:types": "tsc -p tsconfig.types.json",
"prepublishOnly": "npm run build",
"pretest": "npm run build",
"test": "node --test --experimental-test-coverage"
@@ -25,7 +36,7 @@
},
"license": "MIT",
"devDependencies": {
"@types/node": "22.7.5",
"@types/node": "22.7.7",
"rimraf": "6.0.1",
"typescript": "5.6.3"
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { OSCBundle } from './models';
import { oscTypeConverterMap } from './osc-types';
export function bundleFromBuffer(bytes: Uint8Array): [OSCBundle | undefined, Uint8Array | undefined] {
if (bytes.length < 8) {
if (bytes.length < 20) {
throw new Error('bundle has to be at least 20 bytes');
}
+14
View File
@@ -1,3 +1,17 @@
import { bundleFromBuffer } from './bundle';
import { messageFromBuffer } from './message';
import { OSCBundle, OSCMessage } from './models';
export * from './models';
export * from './message';
export * from './bundle';
export function fromBuffer(bytes: Uint8Array): [OSCBundle | OSCMessage | undefined, Uint8Array | undefined] {
if (bytes[0] === 47) { // starts with '/'
return messageFromBuffer(bytes);
} else if (bytes[0] === 35) { // starts with '#'
return bundleFromBuffer(bytes)
} else {
throw new Error('bytes do not look like an OSC message or bundle');
}
}
+38 -23
View File
@@ -28,28 +28,32 @@ function argsToBuffer(args: OSCArg[]) {
}
export function messageToBuffer(message: OSCMessage): Uint8Array {
if (!message.address.startsWith('/')) {
throw new Error('osc message must start with a /');
}
const addressBuffer = oscTypeConverterMap.s.toBuffer(message.address);
if (addressBuffer === undefined) {
throw new Error('problem encoding address');
}
let typeString = ','
let typeString = ',';
const flatOSCArgs: OSCArg[] = []
const flatOSCArgs: OSCArg[] = [];
message.args.forEach((oscArg)=> {
if(Array.isArray(oscArg)){
typeString += '['
oscArg.forEach((oscArg)=>{
typeString += oscArg.type
flatOSCArgs.push(oscArg)
})
typeString += ']'
}else {
typeString += oscArg.type
flatOSCArgs.push(oscArg)
message.args.forEach((oscArg) => {
if (Array.isArray(oscArg)) {
typeString += '[';
oscArg.forEach((oscArg) => {
typeString += oscArg.type;
flatOSCArgs.push(oscArg);
});
typeString += ']';
} else {
typeString += oscArg.type;
flatOSCArgs.push(oscArg);
}
})
});
const typesBuffer = oscTypeConverterMap.s.toBuffer(typeString);
if (typesBuffer === undefined) {
@@ -72,6 +76,17 @@ export function messageFromBuffer(bytes: Uint8Array): [OSCMessage | undefined, U
const [address, bytesAfterAddress] = oscTypeConverterMap.s.fromBuffer(bytes);
if (typeof address === 'string') {
if (bytesAfterAddress.length === 0) {
// NOTE(jwetzell): OSC 1.0 spec says that messages without a type string should be allowed
return [
{
address,
args: [],
},
bytesAfterAddress,
];
}
let [typeString, bytesAfterType] = oscTypeConverterMap.s.fromBuffer(bytesAfterAddress);
if (typeof typeString === 'string') {
if (!typeString.startsWith(',')) {
@@ -83,18 +98,18 @@ export function messageFromBuffer(bytes: Uint8Array): [OSCMessage | undefined, U
for (let index = 1; index < typeString.length; index++) {
const argType = typeString.charAt(index) as OSCType;
if (argType === '['){
if (insideArgArray){
throw new Error('osc arg array opened without closing previous arg array')
if (argType === '[') {
if (insideArgArray) {
throw new Error('osc arg array opened without closing previous arg array');
}
oscArrayArg = [];
insideArgArray = true;
continue;
}
if (argType === ']'){
if (!insideArgArray){
throw new Error('osc arg array closed without opening arg array')
if (argType === ']') {
if (!insideArgArray) {
throw new Error('osc arg array closed without opening arg array');
}
oscArgs.push(oscArrayArg);
oscArrayArg = [];
@@ -112,9 +127,9 @@ export function messageFromBuffer(bytes: Uint8Array): [OSCMessage | undefined, U
type: argType,
value: value,
};
if(insideArgArray){
oscArrayArg.push(arg)
}else {
if (insideArgArray) {
oscArrayArg.push(arg);
} else {
oscArgs.push(arg);
}
}
+106 -5
View File
@@ -1,8 +1,8 @@
const { deepEqual, equal } = require('assert');
const { deepEqual, equal, throws } = require('assert');
const { describe, it } = require('node:test');
const osc = require('../dist/index');
const osc = require('../dist/cjs/index');
const tests = [
const goodTests = [
{
description: 'simple contents single message',
bytes: new Uint8Array([
@@ -20,6 +20,34 @@ const tests = [
contents: [{ address: '/oscillator/4/frequency', args: [{ type: 'f', value: 440 }] }],
},
},
{
description: 'simple contents single bundle',
bytes: new Uint8Array([
...new TextEncoder().encode('#bundle'),
...new Uint8Array([0x00]),
...new Uint8Array([0, 0, 0, 32, 0, 0, 0, 0]),
...new Uint8Array([0x00, 0x00, 0x00, 0x34]),
...new Uint8Array([
...new TextEncoder().encode('#bundle'),
...new Uint8Array([0x00]),
...new Uint8Array([0, 0, 0, 32, 0, 0, 0, 0]),
...new Uint8Array([0x00, 0x00, 0x00, 0x20]),
...new Uint8Array([
0x2f, 0x6f, 0x73, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x34, 0x2f, 0x66, 0x72, 0x65, 0x71,
0x75, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x43, 0xdc, 0x00, 0x00,
]),
]),
]),
expected: {
timeTag: [32, 0],
contents: [
{
timeTag: [32, 0],
contents: [{ address: '/oscillator/4/frequency', args: [{ type: 'f', value: 440 }] }],
},
],
},
},
{
description: 'resolume bundle example',
bytes: new Uint8Array([
@@ -88,8 +116,71 @@ const tests = [
},
];
describe('OSC Bundle Decoding', () => {
tests.forEach((bundleTest) => {
const badTests = [
{
description: 'bad bundle header',
bytes: new Uint8Array([
...new TextEncoder().encode('bundle'),
...new Uint8Array([0x00]),
...new Uint8Array([0, 0, 0, 32, 0, 0, 0, 0]),
...new Uint8Array([0x00, 0x00, 0x00, 0x20]),
...new Uint8Array([
0x2f, 0x6f, 0x73, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x34, 0x2f, 0x66, 0x72, 0x65, 0x71,
0x75, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x43, 0xdc, 0x00, 0x00,
]),
]),
throwsMessage: {
name: /^Error$/,
message: 'bundle must start with #bundle',
},
},
{
description: 'incomplete bundle',
bytes: new Uint8Array([
...new TextEncoder().encode('#bundle'),
...new Uint8Array([0x00]),
...new Uint8Array([0, 0, 0, 32, 0, 0, 0, 0]),
]),
throwsMessage: {
name: /^Error$/,
message: 'bundle has to be at least 20 bytes',
},
},
{
description: 'bundle not enough bytes',
bytes: new Uint8Array([
...new TextEncoder().encode('#bundle'),
...new Uint8Array([0x00]),
...new Uint8Array([0, 0, 0, 32, 0, 0, 0, 0]),
...new Uint8Array([0x00, 0x00, 0x00, 0x20]),
...new Uint8Array([0x2f, 0x6f, 0x73, 0x63, 0x69, 0x6c, 0x6c, 0x61]),
]),
throwsMessage: {
name: /^Error$/,
message: 'bundle does not contain enough data',
},
},
{
description: 'bad bundle contents',
bytes: new Uint8Array([
...new TextEncoder().encode('#bundle'),
...new Uint8Array([0x00]),
...new Uint8Array([0, 0, 0, 32, 0, 0, 0, 0]),
...new Uint8Array([0x00, 0x00, 0x00, 0x20]),
...new Uint8Array([
0x2a, 0x6f, 0x73, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x34, 0x2f, 0x66, 0x72, 0x65, 0x71,
0x75, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x43, 0xdc, 0x00, 0x00,
]),
]),
throwsMessage: {
name: /^Error$/,
message: 'bundle contents does not look like a OSC message or bundle',
},
},
];
describe('OSC Bundle Decoding Pass', () => {
goodTests.forEach((bundleTest) => {
it(bundleTest.description, () => {
const [encoded, remainingBytes] = osc.bundleFromBuffer(bundleTest.bytes);
equal(remainingBytes.length, 0);
@@ -97,3 +188,13 @@ describe('OSC Bundle Decoding', () => {
});
});
});
describe('OSC Bundle Decoding Throws', () => {
badTests.forEach((bundleTest) => {
it(bundleTest.description, () => {
throws(() => {
osc.bundleFromBuffer(bundleTest.bytes);
}, bundleTest.throwsMessage);
});
});
});
+1 -1
View File
@@ -1,6 +1,6 @@
const { deepEqual } = require('assert');
const { describe, it } = require('node:test');
const osc = require('../dist/index');
const osc = require('../dist/cjs/index');
const tests = [
{
+41
View File
@@ -0,0 +1,41 @@
const { deepEqual, equal } = require('assert');
const { describe, it } = require('node:test');
const osc = require('../dist/cjs/index');
const tests = [
{
description: 'simple bundle',
expected: {
timeTag: [32, 0],
contents: [{ address: '/oscillator/4/frequency', args: [{ type: 'f', value: 440 }] }],
},
bytes: new Uint8Array([
...new TextEncoder().encode('#bundle'),
...new Uint8Array([0x00]),
...new Uint8Array([0, 0, 0, 32, 0, 0, 0, 0]),
...new Uint8Array([0x00, 0x00, 0x00, 0x20]),
...new Uint8Array([
0x2f, 0x6f, 0x73, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x34, 0x2f, 0x66, 0x72, 0x65, 0x71,
0x75, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x43, 0xdc, 0x00, 0x00,
]),
]),
},
{
description: 'osc 1.0 spec example 1',
bytes: new Uint8Array([
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44,
102, 0, 0, 67, 220, 0, 0,
]),
expected: { address: '/oscillator/4/frequency', args: [{ type: 'f', value: 440 }] },
},
]
describe('OSC Bytes Decoding', () => {
tests.forEach((bytesTest) => {
it(bytesTest.description, () => {
const [decoded, remainingBytes] = osc.fromBuffer(bytesTest.bytes);
deepEqual(decoded, bytesTest.expected);
equal(remainingBytes.length, 0)
});
});
});
+87 -75
View File
@@ -1,8 +1,8 @@
const { deepEqual, throws, equal } = require('assert');
const { describe, it } = require('node:test');
const osc = require('../dist/index');
const osc = require('../dist/cjs/index');
const tests = [
const goodTests = [
{
description: 'simple address no args',
bytes: new Uint8Array([47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0]),
@@ -68,9 +68,26 @@ const tests = [
{
description: 'simple address array arg',
bytes: new Uint8Array([
47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6, 0, 0, 3, 232
47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
0, 0, 3, 232,
]),
expected: { address: '/hello', args: [[{ type: 'd', value: 12.7654763 }, { type: 'i', value: 1000}]] },
expected: {
address: '/hello',
args: [
[
{ type: 'd', value: 12.7654763 },
{ type: 'i', value: 1000 },
],
],
},
},
{
description: 'simple address no type string',
bytes: new Uint8Array([47, 104, 101, 108, 108, 111, 0, 0]),
expected: {
address: '/hello',
args: [],
},
},
{
description: 'osc 1.0 spec example 1',
@@ -100,83 +117,78 @@ const tests = [
},
];
describe('OSC Message Decoding', () => {
tests.forEach((messageTest) => {
const badTests = [
{
description: 'bad address',
bytes: new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x42, 0x0a, 0x00, 0x00]),
throwsMessage: { name: /^Error$/, message: /must start with/ },
},
{
description: 'bad type string',
bytes: new Uint8Array([0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x42, 0x0a, 0x00]),
throwsMessage: { name: /^Error$/, message: /type string must start with/ },
},
{
description: 'unknown type',
bytes: new Uint8Array([
0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x7a, 0x00, 0x00, 0x42, 0x0a, 0x00, 0x00,
]),
throwsMessage: { name: /^Error$/, message: /unknown/ },
},
{
description: 'float arg missing bytes',
bytes: new Uint8Array([0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x42, 0x0a, 0x00]),
throwsMessage: { name: /^Error$/, message: /not enough bytes/ },
},
{
description: 'int arg missing bytes',
bytes: new Uint8Array([0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00]),
throwsMessage: { name: /^Error$/, message: /not enough bytes/ },
},
{
description: 'blob bytes too small',
bytes: new Uint8Array([
0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x62, 0x6c, 0x6f,
]),
throwsMessage: { name: /^Error$/, message: /not enough bytes/ },
},
{
description: 'float64 missing bytes',
bytes: new Uint8Array([
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9,
]),
throwsMessage: { name: /^Error$/, message: /not enough bytes/ },
},
{
description: 'int64 missing bytes',
bytes: new Uint8Array([47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255]),
throwsMessage: { name: /^Error$/, message: /must be at least/ },
},
{
description: 'color missing bytes',
bytes: new Uint8Array([47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22]),
throwsMessage: { name: /^Error$/, message: /must be at least/ },
},
];
describe('OSC Message Decoding Pass', () => {
goodTests.forEach((messageTest) => {
it(messageTest.description, () => {
const [decoded, remainingBytes] = osc.messageFromBuffer(messageTest.bytes);
equal(remainingBytes.length, 0);
deepEqual(decoded, messageTest.expected);
});
});
});
it('bad address', () => {
throws(
() => {
osc.messageFromBuffer(
new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x42, 0x0a, 0x00, 0x00])
);
},
{ name: /^Error$/, message: /must start with/ }
);
});
it('bad type string', () => {
throws(
() => {
osc.messageFromBuffer(
new Uint8Array([0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x42, 0x0a, 0x00])
);
},
{ name: /^Error$/, message: /type string must start with/ }
);
});
it('unknown type', () => {
throws(
() => {
osc.messageFromBuffer(
new Uint8Array([
0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x7a, 0x00, 0x00, 0x42, 0x0a, 0x00, 0x00,
])
);
},
{ name: /^Error$/, message: /unknown/ }
);
});
it('float arg missing bytes', () => {
throws(
() => {
osc.messageFromBuffer(
new Uint8Array([0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x66, 0x00, 0x00, 0x42, 0x0a, 0x00])
);
},
{ name: /^Error$/, message: /not enough bytes/ }
);
});
it('int arg missing bytes', () => {
throws(
() => {
osc.messageFromBuffer(
new Uint8Array([0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00])
);
},
{ name: /^Error$/, message: /not enough bytes/ }
);
});
it('blob bytes too small', () => {
throws(
() => {
osc.messageFromBuffer(
new Uint8Array([
0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x2c, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x62, 0x6c,
0x6f,
])
);
},
{ name: /^Error$/, message: /not enough bytes/ }
);
describe('OSC Message Decoding Throws', () => {
badTests.forEach((messageTest) => {
it(messageTest.description, () => {
throws(() => {
osc.messageFromBuffer(messageTest.bytes);
}, messageTest.throwsMessage);
});
});
});
+60 -48
View File
@@ -1,8 +1,8 @@
const { deepEqual, throws } = require('assert');
const { describe, it } = require('node:test');
const osc = require('../dist/index');
const osc = require('../dist/cjs/index');
const tests = [
const goodTests = [
{
description: 'simple address no args',
message: { address: '/hello', args: [] },
@@ -62,9 +62,18 @@ const tests = [
},
{
description: 'simple address array arg',
message: { address: '/hello', args: [[{ type: 'd', value: 12.7654763 }, { type: 'i', value: 1000}]] },
message: {
address: '/hello',
args: [
[
{ type: 'd', value: 12.7654763 },
{ type: 'i', value: 1000 },
],
],
},
expected: new Uint8Array([
47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6, 0, 0, 3, 232
47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
0, 0, 3, 232,
]),
},
{
@@ -95,55 +104,58 @@ const tests = [
},
];
describe('OSC Message Encoding', () => {
tests.forEach((messageTest) => {
const badTests = [
{
description: 'bad string arg',
message: { address: '/address', args: [{ type: 's', value: 123 }] },
throwsMessage: { name: /^TypeError$/, message: /non string/ },
},
{
description: 'bad integer arg',
message: { address: '/address', args: [{ type: 'i', value: 'hi' }] },
throwsMessage: { name: /^TypeError$/, message: /non number/ },
},
{
description: 'bad float arg',
message: { address: '/address', args: [{ type: 'f', value: 'hi' }] },
throwsMessage: { name: /^TypeError$/, message: /non number/ },
},
{
description: 'bad blob arg',
message: { address: '/address', args: [{ type: 'b', value: 123 }] },
throwsMessage: { name: /^TypeError$/, message: /non Uint8Array/ },
},
{
description: 'unknown arg type',
message: { address: '/address', args: [{ type: 'z', value: 123 }] },
throwsMessage: { name: /^TypeError$/, message: /unknown type z/ },
},
{
description: 'address that does not start with / should throw',
message: { address: 'address', args: [] },
throwsMessage: {
name: /^Error$/,
message: 'osc message must start with a /',
},
},
];
describe('OSC Message Encoding Pass', () => {
goodTests.forEach((messageTest) => {
it(messageTest.description, () => {
const encoded = osc.messageToBuffer(messageTest.message);
deepEqual(encoded, messageTest.expected);
});
});
it('bad string arg', () => {
throws(
() => {
osc.messageToBuffer({ address: '/address', args: [{ type: 's', value: 123 }] });
},
{ name: /^TypeError$/ }
);
});
});
it('bad integer arg', () => {
throws(
() => {
osc.messageToBuffer({ address: '/address', args: [{ type: 'i', value: 'hi' }] });
},
{ name: /^TypeError$/ }
);
});
it('bad float arg', () => {
throws(
() => {
osc.messageToBuffer({ address: '/address', args: [{ type: 'f', value: 'hi' }] });
},
{ name: /^TypeError$/ }
);
});
it('bad blob arg', () => {
throws(
() => {
osc.messageToBuffer({ address: '/address', args: [{ type: 'b', value: 123 }] });
},
{ name: /^TypeError$/ }
);
});
it('unknown arg type', () => {
throws(
() => {
osc.messageToBuffer({ address: '/address', args: [{ type: 'z', value: 123 }] });
},
{ name: /^TypeError$/ }
);
describe('OSC Message Encoding Throws', () => {
badTests.forEach((messageTest) => {
it(messageTest.description, () => {
throws(() => {
osc.messageToBuffer(messageTest.message);
}, messageTest.throwsMessage);
});
});
});
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS",
"declaration": false,
"outDir": "./dist/cjs"
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"declaration": false,
"outDir": "./dist/esm"
}
}
+7 -7
View File
@@ -1,12 +1,12 @@
{
"compilerOptions": {
"moduleResolution": "Node",
"lib": ["ESNext"],
"target": "ESNext",
"esModuleInterop": true,
"module": "commonjs",
"target": "ES2020",
"declaration": true,
"rootDir": "./src",
"strict": true,
"outDir": "dist"
"isolatedModules": true,
"rootDir": "src/",
"strict": true
},
"exclude": ["node_modules", "dist", "examples"]
"include": ["src/"]
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"emitDeclarationOnly": true,
"declaration": true,
"outDir": "./dist/types"
}
}