node-osc 11.2.0 → 11.2.1
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/.github/workflows/bump-version.yml +3 -3
- package/.github/workflows/create-release.yml +4 -4
- package/.github/workflows/nodejs.yml +3 -3
- package/dist/lib/Client.js +1 -1
- package/dist/lib/Message.js +3 -1
- package/dist/lib/osc.js +3 -2
- package/dist/test/lib/osc.js +3 -2
- package/dist/test/test-client.js +22 -0
- package/dist/test/test-encode-decode.js +359 -0
- package/dist/test/test-message.js +147 -0
- package/dist/test/test-promises.js +22 -0
- package/lib/Client.mjs +1 -1
- package/lib/Message.mjs +3 -1
- package/lib/osc.mjs +3 -2
- package/package.json +1 -1
- package/rollup.config.mjs +1 -0
- package/test/test-client.mjs +22 -0
- package/test/test-encode-decode.mjs +359 -0
- package/test/test-message.mjs +147 -0
- package/test/test-promises.mjs +22 -0
- package/types/Message.d.mts.map +1 -1
- package/types/osc.d.mts.map +1 -1
|
@@ -35,14 +35,14 @@ jobs:
|
|
|
35
35
|
# run that runs on: tag. (Using the GitHub token would
|
|
36
36
|
# not run the workflow to prevent infinite recursion.)
|
|
37
37
|
- name: Check out source
|
|
38
|
-
uses: actions/checkout@
|
|
38
|
+
uses: actions/checkout@v6
|
|
39
39
|
with:
|
|
40
40
|
ssh-key: ${{ secrets.DEPLOY_KEY }}
|
|
41
41
|
|
|
42
42
|
- name: Setup Node.js
|
|
43
|
-
uses: actions/setup-node@
|
|
43
|
+
uses: actions/setup-node@v6
|
|
44
44
|
with:
|
|
45
|
-
node-version:
|
|
45
|
+
node-version: 24
|
|
46
46
|
cache: 'npm'
|
|
47
47
|
|
|
48
48
|
- name: Install npm packages
|
|
@@ -17,11 +17,11 @@ jobs:
|
|
|
17
17
|
id-token: write
|
|
18
18
|
steps:
|
|
19
19
|
- name: Checkout source
|
|
20
|
-
uses: actions/checkout@
|
|
20
|
+
uses: actions/checkout@v6
|
|
21
21
|
- name: Setup node
|
|
22
|
-
uses: actions/setup-node@
|
|
22
|
+
uses: actions/setup-node@v6
|
|
23
23
|
with:
|
|
24
|
-
node-version:
|
|
24
|
+
node-version: 24
|
|
25
25
|
registry-url: 'https://registry.npmjs.org'
|
|
26
26
|
cache: npm
|
|
27
27
|
- name: Install latest npm
|
|
@@ -39,7 +39,7 @@ jobs:
|
|
|
39
39
|
contents: write
|
|
40
40
|
steps:
|
|
41
41
|
- name: Checkout code
|
|
42
|
-
uses: actions/checkout@
|
|
42
|
+
uses: actions/checkout@v6
|
|
43
43
|
- name: Create Release
|
|
44
44
|
run: gh release create ${{ github.ref }} --generate-notes
|
|
45
45
|
env:
|
|
@@ -14,16 +14,16 @@ jobs:
|
|
|
14
14
|
run-tests:
|
|
15
15
|
strategy:
|
|
16
16
|
matrix:
|
|
17
|
-
node-version: ['24', '22', '20']
|
|
17
|
+
node-version: ['25', '24', '22', '20']
|
|
18
18
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
19
19
|
|
|
20
20
|
runs-on: ${{ matrix.os }}
|
|
21
21
|
|
|
22
22
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
24
|
|
|
25
25
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
26
|
-
uses: actions/setup-node@
|
|
26
|
+
uses: actions/setup-node@v6
|
|
27
27
|
with:
|
|
28
28
|
node-version: ${{ matrix.node-version }}
|
|
29
29
|
cache: 'npm'
|
package/dist/lib/Client.js
CHANGED
package/dist/lib/Message.js
CHANGED
|
@@ -111,7 +111,9 @@ class Message {
|
|
|
111
111
|
let argOut;
|
|
112
112
|
switch (typeof arg) {
|
|
113
113
|
case 'object':
|
|
114
|
-
if (arg
|
|
114
|
+
if (Buffer.isBuffer(arg)) {
|
|
115
|
+
this.args.push(arg);
|
|
116
|
+
} else if (arg instanceof Array) {
|
|
115
117
|
arg.forEach(a => this.append(a));
|
|
116
118
|
} else if (arg.type) {
|
|
117
119
|
if (typeTags[arg.type]) arg.type = typeTags[arg.type];
|
package/dist/lib/osc.js
CHANGED
|
@@ -8,8 +8,9 @@ var node_buffer = require('node:buffer');
|
|
|
8
8
|
|
|
9
9
|
function padString(str) {
|
|
10
10
|
const nullTerminated = str + '\0';
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const byteLength = node_buffer.Buffer.byteLength(nullTerminated);
|
|
12
|
+
const padding = (4 - (byteLength % 4)) % 4;
|
|
13
|
+
return nullTerminated + '\0'.repeat(padding);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function readString(buffer, offset) {
|
package/dist/test/lib/osc.js
CHANGED
|
@@ -8,8 +8,9 @@ var node_buffer = require('node:buffer');
|
|
|
8
8
|
|
|
9
9
|
function padString(str) {
|
|
10
10
|
const nullTerminated = str + '\0';
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const byteLength = node_buffer.Buffer.byteLength(nullTerminated);
|
|
12
|
+
const padding = (4 - (byteLength % 4)) % 4;
|
|
13
|
+
return nullTerminated + '\0'.repeat(padding);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function readString(buffer, offset) {
|
package/dist/test/test-client.js
CHANGED
|
@@ -23,6 +23,28 @@ tap.test('client: with array', (t) => {
|
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
tap.test('client: array is not mutated when sent', (t) => {
|
|
27
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
28
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
29
|
+
|
|
30
|
+
t.plan(3);
|
|
31
|
+
|
|
32
|
+
const originalArray = ['/test', 0, 1, 'testing', true];
|
|
33
|
+
const expectedArray = ['/test', 0, 1, 'testing', true];
|
|
34
|
+
|
|
35
|
+
oscServer.on('message', (msg) => {
|
|
36
|
+
oscServer.close();
|
|
37
|
+
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
|
|
38
|
+
// Verify the original array was not mutated
|
|
39
|
+
t.same(originalArray, expectedArray, 'Original array should not be mutated');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
client.send(originalArray, (err) => {
|
|
43
|
+
t.error(err, 'there should be no error');
|
|
44
|
+
client.close();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
26
48
|
tap.test('client: with string', (t) => {
|
|
27
49
|
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
28
50
|
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
@@ -847,3 +847,362 @@ tap.test('encode and decode: type "boolean" with false value', (t) => {
|
|
|
847
847
|
t.equal(decoded.args[0].value, false);
|
|
848
848
|
t.end();
|
|
849
849
|
});
|
|
850
|
+
|
|
851
|
+
// Tests for UTF-8 string padding to 4-byte boundaries
|
|
852
|
+
// The padString function ensures OSC strings are padded based on byte length
|
|
853
|
+
// (not character count) to handle multi-byte UTF-8 correctly
|
|
854
|
+
|
|
855
|
+
tap.test('encode and decode: UTF-8 string padding - ASCII 1 char', (t) => {
|
|
856
|
+
// 1 byte + 1 null terminator = 2 bytes, needs 2 padding bytes to reach 4-byte boundary
|
|
857
|
+
const message = {
|
|
858
|
+
oscType: 'message',
|
|
859
|
+
address: '/test',
|
|
860
|
+
args: [{ type: 'string', value: 'a' }]
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
const buffer = nodeOsc.encode(message);
|
|
864
|
+
const decoded = nodeOsc.decode(buffer);
|
|
865
|
+
|
|
866
|
+
t.equal(decoded.args[0].value, 'a', 'should correctly encode and decode single ASCII character');
|
|
867
|
+
|
|
868
|
+
// Verify the string is properly padded in the buffer
|
|
869
|
+
// Address "/test" is 5 bytes + 1 null = 6 bytes, padded to 8 bytes
|
|
870
|
+
// Type tag ",s" is 2 bytes + 1 null = 3 bytes, padded to 4 bytes
|
|
871
|
+
// String "a" is 1 byte + 1 null = 2 bytes, padded to 4 bytes
|
|
872
|
+
const expectedMinLength = 8 + 4 + 4; // 16 bytes minimum
|
|
873
|
+
t.ok(buffer.length >= expectedMinLength, 'buffer should contain properly padded string');
|
|
874
|
+
|
|
875
|
+
t.end();
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
tap.test('encode and decode: UTF-8 string padding - ASCII 2 chars', (t) => {
|
|
879
|
+
// 2 bytes + 1 null = 3 bytes, needs 1 padding byte to reach 4-byte boundary
|
|
880
|
+
const message = {
|
|
881
|
+
oscType: 'message',
|
|
882
|
+
address: '/test',
|
|
883
|
+
args: [{ type: 'string', value: 'ab' }]
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
const buffer = nodeOsc.encode(message);
|
|
887
|
+
const decoded = nodeOsc.decode(buffer);
|
|
888
|
+
|
|
889
|
+
t.equal(decoded.args[0].value, 'ab', 'should correctly encode and decode 2-byte string');
|
|
890
|
+
t.end();
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
tap.test('encode and decode: UTF-8 string padding - ASCII 3 chars', (t) => {
|
|
894
|
+
// 3 bytes + 1 null terminator = 4 bytes, needs 0 padding (already aligned)
|
|
895
|
+
const message = {
|
|
896
|
+
oscType: 'message',
|
|
897
|
+
address: '/test',
|
|
898
|
+
args: [{ type: 'string', value: 'abc' }]
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
const buffer = nodeOsc.encode(message);
|
|
902
|
+
const decoded = nodeOsc.decode(buffer);
|
|
903
|
+
|
|
904
|
+
t.equal(decoded.args[0].value, 'abc', 'should correctly encode and decode 3-char ASCII string');
|
|
905
|
+
t.end();
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
tap.test('encode and decode: UTF-8 string padding - ASCII 5 chars', (t) => {
|
|
909
|
+
// 5 bytes + 1 null terminator = 6 bytes, needs 2 padding bytes to reach 8-byte boundary
|
|
910
|
+
const message = {
|
|
911
|
+
oscType: 'message',
|
|
912
|
+
address: '/test',
|
|
913
|
+
args: [{ type: 'string', value: 'hello' }]
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
const buffer = nodeOsc.encode(message);
|
|
917
|
+
const decoded = nodeOsc.decode(buffer);
|
|
918
|
+
|
|
919
|
+
t.equal(decoded.args[0].value, 'hello', 'should correctly encode and decode 5-char ASCII string');
|
|
920
|
+
t.end();
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
tap.test('encode and decode: UTF-8 string padding - ASCII 6 chars', (t) => {
|
|
924
|
+
// 6 bytes + 1 null = 7 bytes, needs 1 padding byte to reach 8-byte boundary
|
|
925
|
+
const message = {
|
|
926
|
+
oscType: 'message',
|
|
927
|
+
address: '/test',
|
|
928
|
+
args: [{ type: 'string', value: 'abcdef' }]
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
const buffer = nodeOsc.encode(message);
|
|
932
|
+
const decoded = nodeOsc.decode(buffer);
|
|
933
|
+
|
|
934
|
+
t.equal(decoded.args[0].value, 'abcdef', 'should correctly encode and decode 6-byte string');
|
|
935
|
+
t.end();
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
tap.test('encode and decode: UTF-8 string padding - ASCII 7 chars', (t) => {
|
|
939
|
+
// 7 bytes + 1 null terminator = 8 bytes, needs 0 padding (already aligned)
|
|
940
|
+
const message = {
|
|
941
|
+
oscType: 'message',
|
|
942
|
+
address: '/test',
|
|
943
|
+
args: [{ type: 'string', value: 'testing' }]
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
const buffer = nodeOsc.encode(message);
|
|
947
|
+
const decoded = nodeOsc.decode(buffer);
|
|
948
|
+
|
|
949
|
+
t.equal(decoded.args[0].value, 'testing', 'should correctly encode and decode 7-char ASCII string');
|
|
950
|
+
t.end();
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
tap.test('encode and decode: UTF-8 string padding - empty string', (t) => {
|
|
954
|
+
// 0 bytes + 1 null terminator = 1 byte, needs 3 padding bytes to reach 4-byte boundary
|
|
955
|
+
const message = {
|
|
956
|
+
oscType: 'message',
|
|
957
|
+
address: '/test',
|
|
958
|
+
args: [{ type: 'string', value: '' }]
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
const buffer = nodeOsc.encode(message);
|
|
962
|
+
const decoded = nodeOsc.decode(buffer);
|
|
963
|
+
|
|
964
|
+
t.equal(decoded.args[0].value, '', 'should correctly encode and decode empty string');
|
|
965
|
+
t.end();
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
tap.test('encode and decode: UTF-8 string padding - emoji character', (t) => {
|
|
969
|
+
// Emoji '😀' is 4 bytes in UTF-8 + 1 null = 5 bytes, needs 3 padding to reach 8-byte boundary
|
|
970
|
+
const message = {
|
|
971
|
+
oscType: 'message',
|
|
972
|
+
address: '/test',
|
|
973
|
+
args: [{ type: 'string', value: '😀' }]
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
const buffer = nodeOsc.encode(message);
|
|
977
|
+
const decoded = nodeOsc.decode(buffer);
|
|
978
|
+
|
|
979
|
+
t.equal(decoded.args[0].value, '😀', 'should correctly encode and decode emoji character');
|
|
980
|
+
|
|
981
|
+
// Verify byte length calculation is correct
|
|
982
|
+
const emojiByteLength = Buffer.byteLength('😀');
|
|
983
|
+
t.equal(emojiByteLength, 4, 'emoji should be 4 bytes in UTF-8');
|
|
984
|
+
|
|
985
|
+
t.end();
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
tap.test('encode and decode: UTF-8 string padding - Japanese character', (t) => {
|
|
989
|
+
// Japanese 'あ' is 3 bytes in UTF-8 + 1 null = 4 bytes, needs 0 padding (already aligned)
|
|
990
|
+
const message = {
|
|
991
|
+
oscType: 'message',
|
|
992
|
+
address: '/test',
|
|
993
|
+
args: [{ type: 'string', value: 'あ' }]
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
const buffer = nodeOsc.encode(message);
|
|
997
|
+
const decoded = nodeOsc.decode(buffer);
|
|
998
|
+
|
|
999
|
+
t.equal(decoded.args[0].value, 'あ', 'should correctly encode and decode Japanese character');
|
|
1000
|
+
|
|
1001
|
+
const japaneseByteLength = Buffer.byteLength('あ');
|
|
1002
|
+
t.equal(japaneseByteLength, 3, 'Japanese character should be 3 bytes in UTF-8');
|
|
1003
|
+
|
|
1004
|
+
t.end();
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
tap.test('encode and decode: UTF-8 string padding - Chinese character', (t) => {
|
|
1008
|
+
// Chinese '中' is 3 bytes in UTF-8 + 1 null = 4 bytes, needs 0 padding (already aligned)
|
|
1009
|
+
const message = {
|
|
1010
|
+
oscType: 'message',
|
|
1011
|
+
address: '/test',
|
|
1012
|
+
args: [{ type: 'string', value: '中' }]
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
const buffer = nodeOsc.encode(message);
|
|
1016
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1017
|
+
|
|
1018
|
+
t.equal(decoded.args[0].value, '中', 'should correctly encode and decode Chinese character');
|
|
1019
|
+
t.end();
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
tap.test('encode and decode: UTF-8 string padding - mixed ASCII and emoji', (t) => {
|
|
1023
|
+
// 'a' (1 byte) + '😀' (4 bytes) + 'b' (1 byte) = 6 bytes + 1 null = 7 bytes
|
|
1024
|
+
// needs 1 padding byte to reach 8-byte boundary
|
|
1025
|
+
const message = {
|
|
1026
|
+
oscType: 'message',
|
|
1027
|
+
address: '/test',
|
|
1028
|
+
args: [{ type: 'string', value: 'a😀b' }]
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
const buffer = nodeOsc.encode(message);
|
|
1032
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1033
|
+
|
|
1034
|
+
t.equal(decoded.args[0].value, 'a😀b', 'should correctly encode and decode mixed ASCII and emoji');
|
|
1035
|
+
|
|
1036
|
+
const mixedByteLength = Buffer.byteLength('a😀b');
|
|
1037
|
+
t.equal(mixedByteLength, 6, 'mixed string should be 6 bytes in UTF-8');
|
|
1038
|
+
|
|
1039
|
+
t.end();
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
tap.test('encode and decode: UTF-8 string padding - Japanese string', (t) => {
|
|
1043
|
+
// 'こんにちは' (Hello in Japanese) - 5 characters, each 3 bytes = 15 bytes
|
|
1044
|
+
// 15 bytes + 1 null = 16 bytes, needs 0 padding (already aligned)
|
|
1045
|
+
const message = {
|
|
1046
|
+
oscType: 'message',
|
|
1047
|
+
address: '/test',
|
|
1048
|
+
args: [{ type: 'string', value: 'こんにちは' }]
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
const buffer = nodeOsc.encode(message);
|
|
1052
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1053
|
+
|
|
1054
|
+
t.equal(decoded.args[0].value, 'こんにちは', 'should correctly encode and decode Japanese string');
|
|
1055
|
+
|
|
1056
|
+
const japaneseStringByteLength = Buffer.byteLength('こんにちは');
|
|
1057
|
+
t.equal(japaneseStringByteLength, 15, 'Japanese string should be 15 bytes in UTF-8');
|
|
1058
|
+
|
|
1059
|
+
t.end();
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
tap.test('encode and decode: UTF-8 string padding - accented characters', (t) => {
|
|
1063
|
+
// 'café' - 4 characters but 'é' is 2 bytes in UTF-8
|
|
1064
|
+
// 'c' (1) + 'a' (1) + 'f' (1) + 'é' (2) = 5 bytes + 1 null = 6 bytes
|
|
1065
|
+
// needs 2 padding bytes to reach 8-byte boundary
|
|
1066
|
+
const message = {
|
|
1067
|
+
oscType: 'message',
|
|
1068
|
+
address: '/test',
|
|
1069
|
+
args: [{ type: 'string', value: 'café' }]
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
const buffer = nodeOsc.encode(message);
|
|
1073
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1074
|
+
|
|
1075
|
+
t.equal(decoded.args[0].value, 'café', 'should correctly encode and decode accented string');
|
|
1076
|
+
|
|
1077
|
+
const accentedByteLength = Buffer.byteLength('café');
|
|
1078
|
+
t.equal(accentedByteLength, 5, 'café should be 5 bytes in UTF-8');
|
|
1079
|
+
|
|
1080
|
+
t.end();
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
tap.test('encode and decode: UTF-8 string padding - multiple strings', (t) => {
|
|
1084
|
+
// Test multiple strings with different byte lengths in one message
|
|
1085
|
+
const message = {
|
|
1086
|
+
oscType: 'message',
|
|
1087
|
+
address: '/multi',
|
|
1088
|
+
args: [
|
|
1089
|
+
{ type: 'string', value: 'a' }, // 1 byte + null
|
|
1090
|
+
{ type: 'string', value: '😀' }, // 4 bytes + null
|
|
1091
|
+
{ type: 'string', value: 'abc' }, // 3 bytes + null
|
|
1092
|
+
{ type: 'string', value: '' } // 0 bytes + null
|
|
1093
|
+
]
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
const buffer = nodeOsc.encode(message);
|
|
1097
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1098
|
+
|
|
1099
|
+
t.equal(decoded.args.length, 4, 'should have 4 arguments');
|
|
1100
|
+
t.equal(decoded.args[0].value, 'a', 'first string should be correct');
|
|
1101
|
+
t.equal(decoded.args[1].value, '😀', 'second string should be correct');
|
|
1102
|
+
t.equal(decoded.args[2].value, 'abc', 'third string should be correct');
|
|
1103
|
+
t.equal(decoded.args[3].value, '', 'fourth string should be correct');
|
|
1104
|
+
|
|
1105
|
+
t.end();
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
tap.test('encode and decode: UTF-8 string padding - address with emoji', (t) => {
|
|
1109
|
+
// OSC addresses can also contain UTF-8 characters and must be properly padded
|
|
1110
|
+
const message = {
|
|
1111
|
+
oscType: 'message',
|
|
1112
|
+
address: '/test/😀',
|
|
1113
|
+
args: [{ type: 'string', value: 'data' }]
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
const buffer = nodeOsc.encode(message);
|
|
1117
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1118
|
+
|
|
1119
|
+
t.equal(decoded.address, '/test/😀', 'should correctly encode and decode address with emoji');
|
|
1120
|
+
t.equal(decoded.args[0].value, 'data', 'should correctly encode and decode argument');
|
|
1121
|
+
|
|
1122
|
+
t.end();
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
tap.test('encode and decode: UTF-8 string padding - long mixed string', (t) => {
|
|
1126
|
+
// Test a longer string with mixed content
|
|
1127
|
+
const longString = 'Hello 世界 🌍! Testing UTF-8 encoding with café and naïve.';
|
|
1128
|
+
const message = {
|
|
1129
|
+
oscType: 'message',
|
|
1130
|
+
address: '/test',
|
|
1131
|
+
args: [{ type: 'string', value: longString }]
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
const buffer = nodeOsc.encode(message);
|
|
1135
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1136
|
+
|
|
1137
|
+
t.equal(decoded.args[0].value, longString, 'should correctly encode and decode long mixed UTF-8 string');
|
|
1138
|
+
|
|
1139
|
+
t.end();
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
tap.test('encode and decode: UTF-8 string padding - special characters', (t) => {
|
|
1143
|
+
// Test various special characters that may have different byte lengths
|
|
1144
|
+
const specialChars = '!@#$%^&*()_+-=[]{}|;:,.<>?/~`';
|
|
1145
|
+
const message = {
|
|
1146
|
+
oscType: 'message',
|
|
1147
|
+
address: '/test',
|
|
1148
|
+
args: [{ type: 'string', value: specialChars }]
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
const buffer = nodeOsc.encode(message);
|
|
1152
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1153
|
+
|
|
1154
|
+
t.equal(decoded.args[0].value, specialChars, 'should correctly encode and decode special ASCII characters');
|
|
1155
|
+
t.end();
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
tap.test('encode and decode: UTF-8 string padding - control characters', (t) => {
|
|
1159
|
+
// Test control characters
|
|
1160
|
+
const controlChars = 'line1\nline2\ttab';
|
|
1161
|
+
const message = {
|
|
1162
|
+
oscType: 'message',
|
|
1163
|
+
address: '/test',
|
|
1164
|
+
args: [{ type: 'string', value: controlChars }]
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
const buffer = nodeOsc.encode(message);
|
|
1168
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1169
|
+
|
|
1170
|
+
t.equal(decoded.args[0].value, controlChars, 'should correctly encode and decode strings with newlines and tabs');
|
|
1171
|
+
t.end();
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
tap.test('encode and decode: UTF-8 string padding - surrogate pairs', (t) => {
|
|
1175
|
+
// Test various emoji that are 4-byte UTF-8 sequences
|
|
1176
|
+
const emojis = '🎉🎊🎈🎁';
|
|
1177
|
+
const message = {
|
|
1178
|
+
oscType: 'message',
|
|
1179
|
+
address: '/test',
|
|
1180
|
+
args: [{ type: 'string', value: emojis }]
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
const buffer = nodeOsc.encode(message);
|
|
1184
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1185
|
+
|
|
1186
|
+
t.equal(decoded.args[0].value, emojis, 'should correctly encode and decode multiple 4-byte emoji');
|
|
1187
|
+
|
|
1188
|
+
const emojisByteLength = Buffer.byteLength(emojis);
|
|
1189
|
+
t.equal(emojisByteLength, 16, 'four 4-byte emoji should total 16 bytes');
|
|
1190
|
+
|
|
1191
|
+
t.end();
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
tap.test('encode and decode: UTF-8 string padding - zero-width characters', (t) => {
|
|
1195
|
+
// Test zero-width joiner and other special Unicode characters
|
|
1196
|
+
const zwj = 'a\u200Db'; // zero-width joiner
|
|
1197
|
+
const message = {
|
|
1198
|
+
oscType: 'message',
|
|
1199
|
+
address: '/test',
|
|
1200
|
+
args: [{ type: 'string', value: zwj }]
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
const buffer = nodeOsc.encode(message);
|
|
1204
|
+
const decoded = nodeOsc.decode(buffer);
|
|
1205
|
+
|
|
1206
|
+
t.equal(decoded.args[0].value, zwj, 'should correctly encode and decode strings with zero-width characters');
|
|
1207
|
+
t.end();
|
|
1208
|
+
});
|
|
@@ -173,6 +173,29 @@ tap.test('message: blob', (t) => {
|
|
|
173
173
|
});
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
+
tap.test('message: Buffer as blob', (t) => {
|
|
177
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
178
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
179
|
+
const m = new nodeOsc.Message('/address');
|
|
180
|
+
const buf = Buffer.from('test buffer data');
|
|
181
|
+
// Directly append Buffer without wrapping in object
|
|
182
|
+
m.append(buf);
|
|
183
|
+
|
|
184
|
+
oscServer.on('message', (msg) => {
|
|
185
|
+
const expected = [
|
|
186
|
+
'/address',
|
|
187
|
+
buf
|
|
188
|
+
];
|
|
189
|
+
t.same(msg, expected, `We received the buffer payload: ${msg}`);
|
|
190
|
+
oscServer.close();
|
|
191
|
+
t.end();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
client.send(m, () => {
|
|
195
|
+
client.close();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
176
199
|
// test('message: timetag', (t) => {
|
|
177
200
|
// const oscServer = new osc.Server(3333, '127.0.0.1');
|
|
178
201
|
// const client = new osc.Client('127.0.0.1', 3333);
|
|
@@ -192,6 +215,130 @@ tap.test('message: blob', (t) => {
|
|
|
192
215
|
// });
|
|
193
216
|
// });
|
|
194
217
|
|
|
218
|
+
tap.test('message: Buffer with multiple arguments', (t) => {
|
|
219
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
220
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
221
|
+
const m = new nodeOsc.Message('/address');
|
|
222
|
+
const buf1 = Buffer.from('first');
|
|
223
|
+
const buf2 = Buffer.from('second');
|
|
224
|
+
|
|
225
|
+
m.append('string');
|
|
226
|
+
m.append(42);
|
|
227
|
+
m.append(buf1);
|
|
228
|
+
m.append(3.14);
|
|
229
|
+
m.append(buf2);
|
|
230
|
+
|
|
231
|
+
oscServer.on('message', (msg) => {
|
|
232
|
+
t.equal(msg[0], '/address', 'Address matches');
|
|
233
|
+
t.equal(msg[1], 'string', 'String matches');
|
|
234
|
+
t.equal(msg[2], 42, 'Integer matches');
|
|
235
|
+
t.same(msg[3], buf1, 'First buffer matches');
|
|
236
|
+
t.equal(round(msg[4]), 3.14, 'Float matches');
|
|
237
|
+
t.same(msg[5], buf2, 'Second buffer matches');
|
|
238
|
+
oscServer.close();
|
|
239
|
+
t.end();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
client.send(m, () => {
|
|
243
|
+
client.close();
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
tap.test('message: Buffer in constructor', (t) => {
|
|
248
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
249
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
250
|
+
const buf = Buffer.from('constructor buffer');
|
|
251
|
+
const m = new nodeOsc.Message('/address', 'test', buf, 123);
|
|
252
|
+
|
|
253
|
+
oscServer.on('message', (msg) => {
|
|
254
|
+
const expected = [
|
|
255
|
+
'/address',
|
|
256
|
+
'test',
|
|
257
|
+
buf,
|
|
258
|
+
123
|
|
259
|
+
];
|
|
260
|
+
t.same(msg, expected, `We received the constructor buffer payload: ${msg}`);
|
|
261
|
+
oscServer.close();
|
|
262
|
+
t.end();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
client.send(m, () => {
|
|
266
|
+
client.close();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
tap.test('message: Buffer in array', (t) => {
|
|
271
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
272
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
273
|
+
const m = new nodeOsc.Message('/address');
|
|
274
|
+
const buf1 = Buffer.from('array1');
|
|
275
|
+
const buf2 = Buffer.from('array2');
|
|
276
|
+
|
|
277
|
+
m.append([buf1, 'string', buf2, 456]);
|
|
278
|
+
|
|
279
|
+
oscServer.on('message', (msg) => {
|
|
280
|
+
const expected = [
|
|
281
|
+
'/address',
|
|
282
|
+
buf1,
|
|
283
|
+
'string',
|
|
284
|
+
buf2,
|
|
285
|
+
456
|
|
286
|
+
];
|
|
287
|
+
t.same(msg, expected, `We received the array with buffers: ${msg}`);
|
|
288
|
+
oscServer.close();
|
|
289
|
+
t.end();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
client.send(m, () => {
|
|
293
|
+
client.close();
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
tap.test('message: empty Buffer', (t) => {
|
|
298
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
299
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
300
|
+
const m = new nodeOsc.Message('/address');
|
|
301
|
+
const buf = Buffer.from('');
|
|
302
|
+
|
|
303
|
+
m.append(buf);
|
|
304
|
+
|
|
305
|
+
oscServer.on('message', (msg) => {
|
|
306
|
+
const expected = [
|
|
307
|
+
'/address',
|
|
308
|
+
buf
|
|
309
|
+
];
|
|
310
|
+
t.same(msg, expected, `We received the empty buffer: ${msg}`);
|
|
311
|
+
oscServer.close();
|
|
312
|
+
t.end();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
client.send(m, () => {
|
|
316
|
+
client.close();
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
tap.test('message: large Buffer', (t) => {
|
|
321
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
322
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
323
|
+
const m = new nodeOsc.Message('/address');
|
|
324
|
+
const buf = Buffer.alloc(1024, 'x');
|
|
325
|
+
|
|
326
|
+
m.append(buf);
|
|
327
|
+
|
|
328
|
+
oscServer.on('message', (msg) => {
|
|
329
|
+
t.equal(msg[0], '/address', 'Address matches');
|
|
330
|
+
t.ok(Buffer.isBuffer(msg[1]), 'Second element is a Buffer');
|
|
331
|
+
t.equal(msg[1].length, 1024, 'Buffer size matches');
|
|
332
|
+
t.same(msg[1], buf, 'Buffer content matches');
|
|
333
|
+
oscServer.close();
|
|
334
|
+
t.end();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
client.send(m, () => {
|
|
338
|
+
client.close();
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
195
342
|
tap.test('message: error', (t) => {
|
|
196
343
|
const m = new nodeOsc.Message('/address');
|
|
197
344
|
t.plan(2);
|
|
@@ -22,6 +22,28 @@ tap.test('client: send with promise - array', async (t) => {
|
|
|
22
22
|
await client.close();
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
tap.test('client: array is not mutated when sent with promise', async (t) => {
|
|
26
|
+
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
27
|
+
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
|
28
|
+
|
|
29
|
+
t.plan(2);
|
|
30
|
+
|
|
31
|
+
const originalArray = ['/test', 0, 1, 'testing', true];
|
|
32
|
+
const expectedArray = ['/test', 0, 1, 'testing', true];
|
|
33
|
+
|
|
34
|
+
oscServer.on('message', (msg) => {
|
|
35
|
+
oscServer.close();
|
|
36
|
+
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await client.send(originalArray);
|
|
40
|
+
|
|
41
|
+
// Verify the original array was not mutated
|
|
42
|
+
t.same(originalArray, expectedArray, 'Original array should not be mutated');
|
|
43
|
+
|
|
44
|
+
await client.close();
|
|
45
|
+
});
|
|
46
|
+
|
|
25
47
|
tap.test('client: send with promise - string', async (t) => {
|
|
26
48
|
const oscServer = new nodeOsc.Server(t.context.port, '127.0.0.1');
|
|
27
49
|
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
|
package/lib/Client.mjs
CHANGED
package/lib/Message.mjs
CHANGED
|
@@ -109,7 +109,9 @@ class Message {
|
|
|
109
109
|
let argOut;
|
|
110
110
|
switch (typeof arg) {
|
|
111
111
|
case 'object':
|
|
112
|
-
if (arg
|
|
112
|
+
if (Buffer.isBuffer(arg)) {
|
|
113
|
+
this.args.push(arg);
|
|
114
|
+
} else if (arg instanceof Array) {
|
|
113
115
|
arg.forEach(a => this.append(a));
|
|
114
116
|
} else if (arg.type) {
|
|
115
117
|
if (typeTags[arg.type]) arg.type = typeTags[arg.type];
|
package/lib/osc.mjs
CHANGED
|
@@ -7,8 +7,9 @@ import { Buffer } from 'node:buffer';
|
|
|
7
7
|
|
|
8
8
|
function padString(str) {
|
|
9
9
|
const nullTerminated = str + '\0';
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const byteLength = Buffer.byteLength(nullTerminated);
|
|
11
|
+
const padding = (4 - (byteLength % 4)) % 4;
|
|
12
|
+
return nullTerminated + '\0'.repeat(padding);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
function readString(buffer, offset) {
|
package/package.json
CHANGED
package/rollup.config.mjs
CHANGED
package/test/test-client.mjs
CHANGED
|
@@ -22,6 +22,28 @@ test('client: with array', (t) => {
|
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
test('client: array is not mutated when sent', (t) => {
|
|
26
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
27
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
28
|
+
|
|
29
|
+
t.plan(3);
|
|
30
|
+
|
|
31
|
+
const originalArray = ['/test', 0, 1, 'testing', true];
|
|
32
|
+
const expectedArray = ['/test', 0, 1, 'testing', true];
|
|
33
|
+
|
|
34
|
+
oscServer.on('message', (msg) => {
|
|
35
|
+
oscServer.close();
|
|
36
|
+
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
|
|
37
|
+
// Verify the original array was not mutated
|
|
38
|
+
t.same(originalArray, expectedArray, 'Original array should not be mutated');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
client.send(originalArray, (err) => {
|
|
42
|
+
t.error(err, 'there should be no error');
|
|
43
|
+
client.close();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
25
47
|
test('client: with string', (t) => {
|
|
26
48
|
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
27
49
|
const client = new Client('127.0.0.1', t.context.port);
|
|
@@ -845,3 +845,362 @@ test('encode and decode: type "boolean" with false value', (t) => {
|
|
|
845
845
|
t.equal(decoded.args[0].value, false);
|
|
846
846
|
t.end();
|
|
847
847
|
});
|
|
848
|
+
|
|
849
|
+
// Tests for UTF-8 string padding to 4-byte boundaries
|
|
850
|
+
// The padString function ensures OSC strings are padded based on byte length
|
|
851
|
+
// (not character count) to handle multi-byte UTF-8 correctly
|
|
852
|
+
|
|
853
|
+
test('encode and decode: UTF-8 string padding - ASCII 1 char', (t) => {
|
|
854
|
+
// 1 byte + 1 null terminator = 2 bytes, needs 2 padding bytes to reach 4-byte boundary
|
|
855
|
+
const message = {
|
|
856
|
+
oscType: 'message',
|
|
857
|
+
address: '/test',
|
|
858
|
+
args: [{ type: 'string', value: 'a' }]
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
const buffer = encode(message);
|
|
862
|
+
const decoded = decode(buffer);
|
|
863
|
+
|
|
864
|
+
t.equal(decoded.args[0].value, 'a', 'should correctly encode and decode single ASCII character');
|
|
865
|
+
|
|
866
|
+
// Verify the string is properly padded in the buffer
|
|
867
|
+
// Address "/test" is 5 bytes + 1 null = 6 bytes, padded to 8 bytes
|
|
868
|
+
// Type tag ",s" is 2 bytes + 1 null = 3 bytes, padded to 4 bytes
|
|
869
|
+
// String "a" is 1 byte + 1 null = 2 bytes, padded to 4 bytes
|
|
870
|
+
const expectedMinLength = 8 + 4 + 4; // 16 bytes minimum
|
|
871
|
+
t.ok(buffer.length >= expectedMinLength, 'buffer should contain properly padded string');
|
|
872
|
+
|
|
873
|
+
t.end();
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
test('encode and decode: UTF-8 string padding - ASCII 2 chars', (t) => {
|
|
877
|
+
// 2 bytes + 1 null = 3 bytes, needs 1 padding byte to reach 4-byte boundary
|
|
878
|
+
const message = {
|
|
879
|
+
oscType: 'message',
|
|
880
|
+
address: '/test',
|
|
881
|
+
args: [{ type: 'string', value: 'ab' }]
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
const buffer = encode(message);
|
|
885
|
+
const decoded = decode(buffer);
|
|
886
|
+
|
|
887
|
+
t.equal(decoded.args[0].value, 'ab', 'should correctly encode and decode 2-byte string');
|
|
888
|
+
t.end();
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
test('encode and decode: UTF-8 string padding - ASCII 3 chars', (t) => {
|
|
892
|
+
// 3 bytes + 1 null terminator = 4 bytes, needs 0 padding (already aligned)
|
|
893
|
+
const message = {
|
|
894
|
+
oscType: 'message',
|
|
895
|
+
address: '/test',
|
|
896
|
+
args: [{ type: 'string', value: 'abc' }]
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
const buffer = encode(message);
|
|
900
|
+
const decoded = decode(buffer);
|
|
901
|
+
|
|
902
|
+
t.equal(decoded.args[0].value, 'abc', 'should correctly encode and decode 3-char ASCII string');
|
|
903
|
+
t.end();
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
test('encode and decode: UTF-8 string padding - ASCII 5 chars', (t) => {
|
|
907
|
+
// 5 bytes + 1 null terminator = 6 bytes, needs 2 padding bytes to reach 8-byte boundary
|
|
908
|
+
const message = {
|
|
909
|
+
oscType: 'message',
|
|
910
|
+
address: '/test',
|
|
911
|
+
args: [{ type: 'string', value: 'hello' }]
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
const buffer = encode(message);
|
|
915
|
+
const decoded = decode(buffer);
|
|
916
|
+
|
|
917
|
+
t.equal(decoded.args[0].value, 'hello', 'should correctly encode and decode 5-char ASCII string');
|
|
918
|
+
t.end();
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
test('encode and decode: UTF-8 string padding - ASCII 6 chars', (t) => {
|
|
922
|
+
// 6 bytes + 1 null = 7 bytes, needs 1 padding byte to reach 8-byte boundary
|
|
923
|
+
const message = {
|
|
924
|
+
oscType: 'message',
|
|
925
|
+
address: '/test',
|
|
926
|
+
args: [{ type: 'string', value: 'abcdef' }]
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
const buffer = encode(message);
|
|
930
|
+
const decoded = decode(buffer);
|
|
931
|
+
|
|
932
|
+
t.equal(decoded.args[0].value, 'abcdef', 'should correctly encode and decode 6-byte string');
|
|
933
|
+
t.end();
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
test('encode and decode: UTF-8 string padding - ASCII 7 chars', (t) => {
|
|
937
|
+
// 7 bytes + 1 null terminator = 8 bytes, needs 0 padding (already aligned)
|
|
938
|
+
const message = {
|
|
939
|
+
oscType: 'message',
|
|
940
|
+
address: '/test',
|
|
941
|
+
args: [{ type: 'string', value: 'testing' }]
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
const buffer = encode(message);
|
|
945
|
+
const decoded = decode(buffer);
|
|
946
|
+
|
|
947
|
+
t.equal(decoded.args[0].value, 'testing', 'should correctly encode and decode 7-char ASCII string');
|
|
948
|
+
t.end();
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
test('encode and decode: UTF-8 string padding - empty string', (t) => {
|
|
952
|
+
// 0 bytes + 1 null terminator = 1 byte, needs 3 padding bytes to reach 4-byte boundary
|
|
953
|
+
const message = {
|
|
954
|
+
oscType: 'message',
|
|
955
|
+
address: '/test',
|
|
956
|
+
args: [{ type: 'string', value: '' }]
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
const buffer = encode(message);
|
|
960
|
+
const decoded = decode(buffer);
|
|
961
|
+
|
|
962
|
+
t.equal(decoded.args[0].value, '', 'should correctly encode and decode empty string');
|
|
963
|
+
t.end();
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
test('encode and decode: UTF-8 string padding - emoji character', (t) => {
|
|
967
|
+
// Emoji '😀' is 4 bytes in UTF-8 + 1 null = 5 bytes, needs 3 padding to reach 8-byte boundary
|
|
968
|
+
const message = {
|
|
969
|
+
oscType: 'message',
|
|
970
|
+
address: '/test',
|
|
971
|
+
args: [{ type: 'string', value: '😀' }]
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
const buffer = encode(message);
|
|
975
|
+
const decoded = decode(buffer);
|
|
976
|
+
|
|
977
|
+
t.equal(decoded.args[0].value, '😀', 'should correctly encode and decode emoji character');
|
|
978
|
+
|
|
979
|
+
// Verify byte length calculation is correct
|
|
980
|
+
const emojiByteLength = Buffer.byteLength('😀');
|
|
981
|
+
t.equal(emojiByteLength, 4, 'emoji should be 4 bytes in UTF-8');
|
|
982
|
+
|
|
983
|
+
t.end();
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
test('encode and decode: UTF-8 string padding - Japanese character', (t) => {
|
|
987
|
+
// Japanese 'あ' is 3 bytes in UTF-8 + 1 null = 4 bytes, needs 0 padding (already aligned)
|
|
988
|
+
const message = {
|
|
989
|
+
oscType: 'message',
|
|
990
|
+
address: '/test',
|
|
991
|
+
args: [{ type: 'string', value: 'あ' }]
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
const buffer = encode(message);
|
|
995
|
+
const decoded = decode(buffer);
|
|
996
|
+
|
|
997
|
+
t.equal(decoded.args[0].value, 'あ', 'should correctly encode and decode Japanese character');
|
|
998
|
+
|
|
999
|
+
const japaneseByteLength = Buffer.byteLength('あ');
|
|
1000
|
+
t.equal(japaneseByteLength, 3, 'Japanese character should be 3 bytes in UTF-8');
|
|
1001
|
+
|
|
1002
|
+
t.end();
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
test('encode and decode: UTF-8 string padding - Chinese character', (t) => {
|
|
1006
|
+
// Chinese '中' is 3 bytes in UTF-8 + 1 null = 4 bytes, needs 0 padding (already aligned)
|
|
1007
|
+
const message = {
|
|
1008
|
+
oscType: 'message',
|
|
1009
|
+
address: '/test',
|
|
1010
|
+
args: [{ type: 'string', value: '中' }]
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
const buffer = encode(message);
|
|
1014
|
+
const decoded = decode(buffer);
|
|
1015
|
+
|
|
1016
|
+
t.equal(decoded.args[0].value, '中', 'should correctly encode and decode Chinese character');
|
|
1017
|
+
t.end();
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
test('encode and decode: UTF-8 string padding - mixed ASCII and emoji', (t) => {
|
|
1021
|
+
// 'a' (1 byte) + '😀' (4 bytes) + 'b' (1 byte) = 6 bytes + 1 null = 7 bytes
|
|
1022
|
+
// needs 1 padding byte to reach 8-byte boundary
|
|
1023
|
+
const message = {
|
|
1024
|
+
oscType: 'message',
|
|
1025
|
+
address: '/test',
|
|
1026
|
+
args: [{ type: 'string', value: 'a😀b' }]
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
const buffer = encode(message);
|
|
1030
|
+
const decoded = decode(buffer);
|
|
1031
|
+
|
|
1032
|
+
t.equal(decoded.args[0].value, 'a😀b', 'should correctly encode and decode mixed ASCII and emoji');
|
|
1033
|
+
|
|
1034
|
+
const mixedByteLength = Buffer.byteLength('a😀b');
|
|
1035
|
+
t.equal(mixedByteLength, 6, 'mixed string should be 6 bytes in UTF-8');
|
|
1036
|
+
|
|
1037
|
+
t.end();
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
test('encode and decode: UTF-8 string padding - Japanese string', (t) => {
|
|
1041
|
+
// 'こんにちは' (Hello in Japanese) - 5 characters, each 3 bytes = 15 bytes
|
|
1042
|
+
// 15 bytes + 1 null = 16 bytes, needs 0 padding (already aligned)
|
|
1043
|
+
const message = {
|
|
1044
|
+
oscType: 'message',
|
|
1045
|
+
address: '/test',
|
|
1046
|
+
args: [{ type: 'string', value: 'こんにちは' }]
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
const buffer = encode(message);
|
|
1050
|
+
const decoded = decode(buffer);
|
|
1051
|
+
|
|
1052
|
+
t.equal(decoded.args[0].value, 'こんにちは', 'should correctly encode and decode Japanese string');
|
|
1053
|
+
|
|
1054
|
+
const japaneseStringByteLength = Buffer.byteLength('こんにちは');
|
|
1055
|
+
t.equal(japaneseStringByteLength, 15, 'Japanese string should be 15 bytes in UTF-8');
|
|
1056
|
+
|
|
1057
|
+
t.end();
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
test('encode and decode: UTF-8 string padding - accented characters', (t) => {
|
|
1061
|
+
// 'café' - 4 characters but 'é' is 2 bytes in UTF-8
|
|
1062
|
+
// 'c' (1) + 'a' (1) + 'f' (1) + 'é' (2) = 5 bytes + 1 null = 6 bytes
|
|
1063
|
+
// needs 2 padding bytes to reach 8-byte boundary
|
|
1064
|
+
const message = {
|
|
1065
|
+
oscType: 'message',
|
|
1066
|
+
address: '/test',
|
|
1067
|
+
args: [{ type: 'string', value: 'café' }]
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
const buffer = encode(message);
|
|
1071
|
+
const decoded = decode(buffer);
|
|
1072
|
+
|
|
1073
|
+
t.equal(decoded.args[0].value, 'café', 'should correctly encode and decode accented string');
|
|
1074
|
+
|
|
1075
|
+
const accentedByteLength = Buffer.byteLength('café');
|
|
1076
|
+
t.equal(accentedByteLength, 5, 'café should be 5 bytes in UTF-8');
|
|
1077
|
+
|
|
1078
|
+
t.end();
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
test('encode and decode: UTF-8 string padding - multiple strings', (t) => {
|
|
1082
|
+
// Test multiple strings with different byte lengths in one message
|
|
1083
|
+
const message = {
|
|
1084
|
+
oscType: 'message',
|
|
1085
|
+
address: '/multi',
|
|
1086
|
+
args: [
|
|
1087
|
+
{ type: 'string', value: 'a' }, // 1 byte + null
|
|
1088
|
+
{ type: 'string', value: '😀' }, // 4 bytes + null
|
|
1089
|
+
{ type: 'string', value: 'abc' }, // 3 bytes + null
|
|
1090
|
+
{ type: 'string', value: '' } // 0 bytes + null
|
|
1091
|
+
]
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
const buffer = encode(message);
|
|
1095
|
+
const decoded = decode(buffer);
|
|
1096
|
+
|
|
1097
|
+
t.equal(decoded.args.length, 4, 'should have 4 arguments');
|
|
1098
|
+
t.equal(decoded.args[0].value, 'a', 'first string should be correct');
|
|
1099
|
+
t.equal(decoded.args[1].value, '😀', 'second string should be correct');
|
|
1100
|
+
t.equal(decoded.args[2].value, 'abc', 'third string should be correct');
|
|
1101
|
+
t.equal(decoded.args[3].value, '', 'fourth string should be correct');
|
|
1102
|
+
|
|
1103
|
+
t.end();
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
test('encode and decode: UTF-8 string padding - address with emoji', (t) => {
|
|
1107
|
+
// OSC addresses can also contain UTF-8 characters and must be properly padded
|
|
1108
|
+
const message = {
|
|
1109
|
+
oscType: 'message',
|
|
1110
|
+
address: '/test/😀',
|
|
1111
|
+
args: [{ type: 'string', value: 'data' }]
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
const buffer = encode(message);
|
|
1115
|
+
const decoded = decode(buffer);
|
|
1116
|
+
|
|
1117
|
+
t.equal(decoded.address, '/test/😀', 'should correctly encode and decode address with emoji');
|
|
1118
|
+
t.equal(decoded.args[0].value, 'data', 'should correctly encode and decode argument');
|
|
1119
|
+
|
|
1120
|
+
t.end();
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
test('encode and decode: UTF-8 string padding - long mixed string', (t) => {
|
|
1124
|
+
// Test a longer string with mixed content
|
|
1125
|
+
const longString = 'Hello 世界 🌍! Testing UTF-8 encoding with café and naïve.';
|
|
1126
|
+
const message = {
|
|
1127
|
+
oscType: 'message',
|
|
1128
|
+
address: '/test',
|
|
1129
|
+
args: [{ type: 'string', value: longString }]
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
const buffer = encode(message);
|
|
1133
|
+
const decoded = decode(buffer);
|
|
1134
|
+
|
|
1135
|
+
t.equal(decoded.args[0].value, longString, 'should correctly encode and decode long mixed UTF-8 string');
|
|
1136
|
+
|
|
1137
|
+
t.end();
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
test('encode and decode: UTF-8 string padding - special characters', (t) => {
|
|
1141
|
+
// Test various special characters that may have different byte lengths
|
|
1142
|
+
const specialChars = '!@#$%^&*()_+-=[]{}|;:,.<>?/~`';
|
|
1143
|
+
const message = {
|
|
1144
|
+
oscType: 'message',
|
|
1145
|
+
address: '/test',
|
|
1146
|
+
args: [{ type: 'string', value: specialChars }]
|
|
1147
|
+
};
|
|
1148
|
+
|
|
1149
|
+
const buffer = encode(message);
|
|
1150
|
+
const decoded = decode(buffer);
|
|
1151
|
+
|
|
1152
|
+
t.equal(decoded.args[0].value, specialChars, 'should correctly encode and decode special ASCII characters');
|
|
1153
|
+
t.end();
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
test('encode and decode: UTF-8 string padding - control characters', (t) => {
|
|
1157
|
+
// Test control characters
|
|
1158
|
+
const controlChars = 'line1\nline2\ttab';
|
|
1159
|
+
const message = {
|
|
1160
|
+
oscType: 'message',
|
|
1161
|
+
address: '/test',
|
|
1162
|
+
args: [{ type: 'string', value: controlChars }]
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
const buffer = encode(message);
|
|
1166
|
+
const decoded = decode(buffer);
|
|
1167
|
+
|
|
1168
|
+
t.equal(decoded.args[0].value, controlChars, 'should correctly encode and decode strings with newlines and tabs');
|
|
1169
|
+
t.end();
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
test('encode and decode: UTF-8 string padding - surrogate pairs', (t) => {
|
|
1173
|
+
// Test various emoji that are 4-byte UTF-8 sequences
|
|
1174
|
+
const emojis = '🎉🎊🎈🎁';
|
|
1175
|
+
const message = {
|
|
1176
|
+
oscType: 'message',
|
|
1177
|
+
address: '/test',
|
|
1178
|
+
args: [{ type: 'string', value: emojis }]
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
const buffer = encode(message);
|
|
1182
|
+
const decoded = decode(buffer);
|
|
1183
|
+
|
|
1184
|
+
t.equal(decoded.args[0].value, emojis, 'should correctly encode and decode multiple 4-byte emoji');
|
|
1185
|
+
|
|
1186
|
+
const emojisByteLength = Buffer.byteLength(emojis);
|
|
1187
|
+
t.equal(emojisByteLength, 16, 'four 4-byte emoji should total 16 bytes');
|
|
1188
|
+
|
|
1189
|
+
t.end();
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
test('encode and decode: UTF-8 string padding - zero-width characters', (t) => {
|
|
1193
|
+
// Test zero-width joiner and other special Unicode characters
|
|
1194
|
+
const zwj = 'a\u200Db'; // zero-width joiner
|
|
1195
|
+
const message = {
|
|
1196
|
+
oscType: 'message',
|
|
1197
|
+
address: '/test',
|
|
1198
|
+
args: [{ type: 'string', value: zwj }]
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
const buffer = encode(message);
|
|
1202
|
+
const decoded = decode(buffer);
|
|
1203
|
+
|
|
1204
|
+
t.equal(decoded.args[0].value, zwj, 'should correctly encode and decode strings with zero-width characters');
|
|
1205
|
+
t.end();
|
|
1206
|
+
});
|
package/test/test-message.mjs
CHANGED
|
@@ -172,6 +172,29 @@ test('message: blob', (t) => {
|
|
|
172
172
|
});
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
+
test('message: Buffer as blob', (t) => {
|
|
176
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
177
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
178
|
+
const m = new Message('/address');
|
|
179
|
+
const buf = Buffer.from('test buffer data');
|
|
180
|
+
// Directly append Buffer without wrapping in object
|
|
181
|
+
m.append(buf);
|
|
182
|
+
|
|
183
|
+
oscServer.on('message', (msg) => {
|
|
184
|
+
const expected = [
|
|
185
|
+
'/address',
|
|
186
|
+
buf
|
|
187
|
+
];
|
|
188
|
+
t.same(msg, expected, `We received the buffer payload: ${msg}`);
|
|
189
|
+
oscServer.close();
|
|
190
|
+
t.end();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
client.send(m, () => {
|
|
194
|
+
client.close();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
175
198
|
// test('message: timetag', (t) => {
|
|
176
199
|
// const oscServer = new osc.Server(3333, '127.0.0.1');
|
|
177
200
|
// const client = new osc.Client('127.0.0.1', 3333);
|
|
@@ -191,6 +214,130 @@ test('message: blob', (t) => {
|
|
|
191
214
|
// });
|
|
192
215
|
// });
|
|
193
216
|
|
|
217
|
+
test('message: Buffer with multiple arguments', (t) => {
|
|
218
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
219
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
220
|
+
const m = new Message('/address');
|
|
221
|
+
const buf1 = Buffer.from('first');
|
|
222
|
+
const buf2 = Buffer.from('second');
|
|
223
|
+
|
|
224
|
+
m.append('string');
|
|
225
|
+
m.append(42);
|
|
226
|
+
m.append(buf1);
|
|
227
|
+
m.append(3.14);
|
|
228
|
+
m.append(buf2);
|
|
229
|
+
|
|
230
|
+
oscServer.on('message', (msg) => {
|
|
231
|
+
t.equal(msg[0], '/address', 'Address matches');
|
|
232
|
+
t.equal(msg[1], 'string', 'String matches');
|
|
233
|
+
t.equal(msg[2], 42, 'Integer matches');
|
|
234
|
+
t.same(msg[3], buf1, 'First buffer matches');
|
|
235
|
+
t.equal(round(msg[4]), 3.14, 'Float matches');
|
|
236
|
+
t.same(msg[5], buf2, 'Second buffer matches');
|
|
237
|
+
oscServer.close();
|
|
238
|
+
t.end();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
client.send(m, () => {
|
|
242
|
+
client.close();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test('message: Buffer in constructor', (t) => {
|
|
247
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
248
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
249
|
+
const buf = Buffer.from('constructor buffer');
|
|
250
|
+
const m = new Message('/address', 'test', buf, 123);
|
|
251
|
+
|
|
252
|
+
oscServer.on('message', (msg) => {
|
|
253
|
+
const expected = [
|
|
254
|
+
'/address',
|
|
255
|
+
'test',
|
|
256
|
+
buf,
|
|
257
|
+
123
|
|
258
|
+
];
|
|
259
|
+
t.same(msg, expected, `We received the constructor buffer payload: ${msg}`);
|
|
260
|
+
oscServer.close();
|
|
261
|
+
t.end();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
client.send(m, () => {
|
|
265
|
+
client.close();
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('message: Buffer in array', (t) => {
|
|
270
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
271
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
272
|
+
const m = new Message('/address');
|
|
273
|
+
const buf1 = Buffer.from('array1');
|
|
274
|
+
const buf2 = Buffer.from('array2');
|
|
275
|
+
|
|
276
|
+
m.append([buf1, 'string', buf2, 456]);
|
|
277
|
+
|
|
278
|
+
oscServer.on('message', (msg) => {
|
|
279
|
+
const expected = [
|
|
280
|
+
'/address',
|
|
281
|
+
buf1,
|
|
282
|
+
'string',
|
|
283
|
+
buf2,
|
|
284
|
+
456
|
|
285
|
+
];
|
|
286
|
+
t.same(msg, expected, `We received the array with buffers: ${msg}`);
|
|
287
|
+
oscServer.close();
|
|
288
|
+
t.end();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
client.send(m, () => {
|
|
292
|
+
client.close();
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test('message: empty Buffer', (t) => {
|
|
297
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
298
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
299
|
+
const m = new Message('/address');
|
|
300
|
+
const buf = Buffer.from('');
|
|
301
|
+
|
|
302
|
+
m.append(buf);
|
|
303
|
+
|
|
304
|
+
oscServer.on('message', (msg) => {
|
|
305
|
+
const expected = [
|
|
306
|
+
'/address',
|
|
307
|
+
buf
|
|
308
|
+
];
|
|
309
|
+
t.same(msg, expected, `We received the empty buffer: ${msg}`);
|
|
310
|
+
oscServer.close();
|
|
311
|
+
t.end();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
client.send(m, () => {
|
|
315
|
+
client.close();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('message: large Buffer', (t) => {
|
|
320
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
321
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
322
|
+
const m = new Message('/address');
|
|
323
|
+
const buf = Buffer.alloc(1024, 'x');
|
|
324
|
+
|
|
325
|
+
m.append(buf);
|
|
326
|
+
|
|
327
|
+
oscServer.on('message', (msg) => {
|
|
328
|
+
t.equal(msg[0], '/address', 'Address matches');
|
|
329
|
+
t.ok(Buffer.isBuffer(msg[1]), 'Second element is a Buffer');
|
|
330
|
+
t.equal(msg[1].length, 1024, 'Buffer size matches');
|
|
331
|
+
t.same(msg[1], buf, 'Buffer content matches');
|
|
332
|
+
oscServer.close();
|
|
333
|
+
t.end();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
client.send(m, () => {
|
|
337
|
+
client.close();
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
194
341
|
test('message: error', (t) => {
|
|
195
342
|
const m = new Message('/address');
|
|
196
343
|
t.plan(2);
|
package/test/test-promises.mjs
CHANGED
|
@@ -21,6 +21,28 @@ test('client: send with promise - array', async (t) => {
|
|
|
21
21
|
await client.close();
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
+
test('client: array is not mutated when sent with promise', async (t) => {
|
|
25
|
+
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
26
|
+
const client = new Client('127.0.0.1', t.context.port);
|
|
27
|
+
|
|
28
|
+
t.plan(2);
|
|
29
|
+
|
|
30
|
+
const originalArray = ['/test', 0, 1, 'testing', true];
|
|
31
|
+
const expectedArray = ['/test', 0, 1, 'testing', true];
|
|
32
|
+
|
|
33
|
+
oscServer.on('message', (msg) => {
|
|
34
|
+
oscServer.close();
|
|
35
|
+
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await client.send(originalArray);
|
|
39
|
+
|
|
40
|
+
// Verify the original array was not mutated
|
|
41
|
+
t.same(originalArray, expectedArray, 'Original array should not be mutated');
|
|
42
|
+
|
|
43
|
+
await client.close();
|
|
44
|
+
});
|
|
45
|
+
|
|
24
46
|
test('client: send with promise - string', async (t) => {
|
|
25
47
|
const oscServer = new Server(t.context.port, '127.0.0.1');
|
|
26
48
|
const client = new Client('127.0.0.1', t.context.port);
|
package/types/Message.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.mts","sourceRoot":"","sources":["../lib/Message.mjs"],"names":[],"mappings":";AAyBA;;;;;;;;;;;;;;;;;;GAkBG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH,qBAZW,MAAM,WACH,GAAC,EAAA,EAed;IAHC,gBAAwB;IACxB,gBAAsB;IACtB,YAAgB;IAGlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,YA7BW,GAAC,
|
|
1
|
+
{"version":3,"file":"Message.d.mts","sourceRoot":"","sources":["../lib/Message.mjs"],"names":[],"mappings":";AAyBA;;;;;;;;;;;;;;;;;;GAkBG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH,qBAZW,MAAM,WACH,GAAC,EAAA,EAed;IAHC,gBAAwB;IACxB,gBAAsB;IACtB,YAAgB;IAGlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,YA7BW,GAAC,QA6DX;CACF"}
|
package/types/osc.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"osc.d.mts","sourceRoot":"","sources":["../lib/osc.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"osc.d.mts","sourceRoot":"","sources":["../lib/osc.mjs"],"names":[],"mappings":"AAgNA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,sCAtBa,MAAM,CA4BlB;AA6CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,+BAzBW,MAAM,OAgChB;uBAnUsB,aAAa"}
|