@openhps/protobuf 1.0.2 → 1.1.0

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.
Files changed (104) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +58 -38
  3. package/dist/cjs/InternalProtobufDeserializer.js +30 -15
  4. package/dist/cjs/InternalProtobufDeserializer.js.map +1 -1
  5. package/dist/cjs/InternalProtobufSerializer.js +7 -7
  6. package/dist/cjs/InternalProtobufSerializer.js.map +1 -1
  7. package/dist/cjs/PrimitiveWrapper.js +13 -0
  8. package/dist/cjs/PrimitiveWrapper.js.map +1 -0
  9. package/dist/cjs/ProtobufSerializer.js +27 -1
  10. package/dist/cjs/ProtobufSerializer.js.map +1 -1
  11. package/dist/cjs/bin/generator.js +3 -3
  12. package/dist/cjs/bin/generator.js.map +1 -1
  13. package/dist/cjs/generator/ObjectGenerator.js +28 -11
  14. package/dist/cjs/generator/ObjectGenerator.js.map +1 -1
  15. package/dist/cjs/generator/ProjectGenerator.js +52 -32
  16. package/dist/cjs/generator/ProjectGenerator.js.map +1 -1
  17. package/dist/cjs/generator/constants.js +11 -4
  18. package/dist/cjs/generator/constants.js.map +1 -1
  19. package/dist/cjs/package.json +3 -0
  20. package/dist/cjs/tsconfig.bundle.cjs.tsbuildinfo +1 -0
  21. package/dist/esm/InternalProtobufDeserializer.js +30 -14
  22. package/dist/esm/InternalProtobufDeserializer.js.map +1 -1
  23. package/dist/esm/InternalProtobufSerializer.js +7 -6
  24. package/dist/esm/InternalProtobufSerializer.js.map +1 -1
  25. package/dist/esm/PrimitiveWrapper.js +9 -0
  26. package/dist/esm/PrimitiveWrapper.js.map +1 -0
  27. package/dist/esm/ProtobufSerializer.js +30 -4
  28. package/dist/esm/ProtobufSerializer.js.map +1 -1
  29. package/dist/esm/bin/generator.js +5 -5
  30. package/dist/esm/bin/generator.js.map +1 -1
  31. package/dist/esm/decorators/index.js +1 -1
  32. package/dist/esm/generator/ObjectGenerator.js +30 -13
  33. package/dist/esm/generator/ObjectGenerator.js.map +1 -1
  34. package/dist/esm/generator/ProjectGenerator.js +55 -34
  35. package/dist/esm/generator/ProjectGenerator.js.map +1 -1
  36. package/dist/esm/generator/ServiceGenerator.js +1 -1
  37. package/dist/esm/generator/constants.js +11 -4
  38. package/dist/esm/generator/constants.js.map +1 -1
  39. package/dist/esm/generator/index.js +3 -3
  40. package/dist/esm/index.js +2 -2
  41. package/dist/esm/package.json +3 -0
  42. package/dist/esm/tsconfig.bundle.esm.tsbuildinfo +1 -0
  43. package/dist/esm/tsconfig.bundle.types.tsbuildinfo +1 -0
  44. package/dist/types/InternalProtobufDeserializer.d.ts +0 -2
  45. package/dist/types/InternalProtobufDeserializer.d.ts.map +1 -1
  46. package/dist/types/InternalProtobufSerializer.d.ts +0 -2
  47. package/dist/types/InternalProtobufSerializer.d.ts.map +1 -1
  48. package/dist/types/PrimitiveWrapper.d.ts +8 -0
  49. package/dist/types/PrimitiveWrapper.d.ts.map +1 -0
  50. package/dist/types/ProtobufSerializer.d.ts +6 -0
  51. package/dist/types/ProtobufSerializer.d.ts.map +1 -1
  52. package/dist/types/generator/ObjectGenerator.d.ts +2 -8
  53. package/dist/types/generator/ObjectGenerator.d.ts.map +1 -1
  54. package/dist/types/generator/ProjectGenerator.d.ts +0 -1
  55. package/dist/types/generator/ProjectGenerator.d.ts.map +1 -1
  56. package/dist/types/generator/constants.d.ts +1 -1
  57. package/dist/types/generator/constants.d.ts.map +1 -1
  58. package/dist/web/openhps-protobuf.es.js +16272 -12072
  59. package/dist/web/openhps-protobuf.es.js.map +1 -1
  60. package/dist/web/openhps-protobuf.es.min.js +2 -1
  61. package/dist/web/openhps-protobuf.es.min.js.LICENSE.txt +19 -0
  62. package/dist/web/openhps-protobuf.es.min.js.map +1 -1
  63. package/dist/web/openhps-protobuf.js +17390 -14592
  64. package/dist/web/openhps-protobuf.js.map +1 -1
  65. package/dist/web/openhps-protobuf.min.js +2 -1
  66. package/dist/web/openhps-protobuf.min.js.LICENSE.txt +19 -0
  67. package/dist/web/openhps-protobuf.min.js.map +1 -1
  68. package/package.json +125 -94
  69. package/src/InternalProtobufDeserializer.ts +52 -34
  70. package/src/InternalProtobufSerializer.ts +9 -10
  71. package/src/PrimitiveWrapper.ts +9 -0
  72. package/src/ProtobufSerializer.ts +33 -1
  73. package/src/bin/generator.ts +4 -4
  74. package/src/generator/ObjectGenerator.ts +32 -20
  75. package/src/generator/ProjectGenerator.ts +61 -36
  76. package/src/generator/constants.ts +11 -4
  77. package/dist/esm5/InternalProtobufDeserializer.js +0 -147
  78. package/dist/esm5/InternalProtobufDeserializer.js.map +0 -1
  79. package/dist/esm5/InternalProtobufSerializer.js +0 -132
  80. package/dist/esm5/InternalProtobufSerializer.js.map +0 -1
  81. package/dist/esm5/ProtobufSerializer.js +0 -130
  82. package/dist/esm5/ProtobufSerializer.js.map +0 -1
  83. package/dist/esm5/bin/generator.js +0 -104
  84. package/dist/esm5/bin/generator.js.map +0 -1
  85. package/dist/esm5/decorators/index.js +0 -2
  86. package/dist/esm5/decorators/index.js.map +0 -1
  87. package/dist/esm5/decorators/options.js +0 -2
  88. package/dist/esm5/decorators/options.js.map +0 -1
  89. package/dist/esm5/generator/ObjectGenerator.js +0 -312
  90. package/dist/esm5/generator/ObjectGenerator.js.map +0 -1
  91. package/dist/esm5/generator/ProjectGenerator.js +0 -146
  92. package/dist/esm5/generator/ProjectGenerator.js.map +0 -1
  93. package/dist/esm5/generator/ProtobufGenerator.js +0 -27
  94. package/dist/esm5/generator/ProtobufGenerator.js.map +0 -1
  95. package/dist/esm5/generator/ServiceGenerator.js +0 -10
  96. package/dist/esm5/generator/ServiceGenerator.js.map +0 -1
  97. package/dist/esm5/generator/constants.js +0 -18
  98. package/dist/esm5/generator/constants.js.map +0 -1
  99. package/dist/esm5/generator/index.js +0 -4
  100. package/dist/esm5/generator/index.js.map +0 -1
  101. package/dist/esm5/generator/types.js +0 -2
  102. package/dist/esm5/generator/types.js.map +0 -1
  103. package/dist/esm5/index.js +0 -3
  104. package/dist/esm5/index.js.map +0 -1
package/package.json CHANGED
@@ -1,96 +1,127 @@
1
1
  {
2
- "name": "@openhps/protobuf",
3
- "version": "1.0.2",
4
- "description": "OpenHPS Protocol Buffer Generator",
5
- "main": "dist/cjs/bin/generator.js",
6
- "bin": {
7
- "openhps-protobuf": "./dist/cjs/bin/generator.js"
8
- },
9
- "publishConfig": {
10
- "access": "public"
11
- },
12
- "scripts": {
13
- "benchmark:1": "ts-node test/bench/serialization.bench.ts",
14
- "benchmark:2": "ts-node test/bench/socket.bench.ts",
15
- "benchmark:3": "ts-node test/bench/mqtt.bench.ts",
16
- "start": "node dist/cjs/bin/generator.js",
17
- "start:debug": "ts-node src/bin/generator.ts",
18
- "test": "mocha --config test/.mocharc.json",
19
- "cover": "nyc mocha --config test/.mocharc.json && nyc report --reporter=cobertura",
20
- "cover:ci": "nyc mocha --config test/.mocharc.json --reporter mocha-junit-reporter && nyc report --reporter=cobertura",
21
- "lint": "eslint src/**",
22
- "clean": "shx rm -rf ./dist && shx rm -rf ./docs/out",
23
- "build": "npm run clean && npm-run-all --parallel build:ts:* && npm run build:webpack",
24
- "build:quick": "npm-run-all --parallel build:ts:* && npm run build:webpack",
25
- "build:typedoc": "typedoc --options docs/typedoc.json",
26
- "build:ts:cjs": "tsc --build ./tsconfig/tsconfig.bundle.cjs.json",
27
- "build:ts:esm": "tsc --build ./tsconfig/tsconfig.bundle.esm.json",
28
- "build:ts:esm5": "tsc --build ./tsconfig/tsconfig.bundle.esm5.json",
29
- "build:ts:types": "tsc --build ./tsconfig/tsconfig.bundle.types.json",
30
- "build:webpack": "npm-run-all --parallel build:webpack:*",
31
- "build:webpack:prod": "webpack --env prod --",
32
- "build:webpack:dev": "webpack --env dev --",
33
- "publish:development": "npm run bump:beta && npm publish --tag dev",
34
- "publish:beta": "npm run bump:beta && npm publish --tag beta",
35
- "publish:release": "npm run bump:beta && npm publish --tag latest",
36
- "bump:development": "standard-version --prerelease alpha -m 'chore(alpha): %s' --author='ci <ci@openhps.org>'",
37
- "bump:beta": "standard-version --prerelease beta -m 'chore(beta): %s' --author='ci <ci@openhps.org>'",
38
- "bump:release": "standard-version -m 'chore(release): %s' --author='ci <ci@openhps.org>'"
39
- },
40
- "files": [
41
- "dist/**/*",
42
- "src/**/*",
43
- "LICENSE"
44
- ],
45
- "author": "Maxim Van de Wynckel",
46
- "license": "Apache-2.0",
47
- "devDependencies": {
48
- "@commitlint/cli": "^19.3.0",
49
- "@commitlint/config-conventional": "^19.2.2",
50
- "@openhps/core": "^0.7.2",
51
- "@openhps/mqtt": "^0.1.9",
52
- "@openhps/rdf": "^0.4.64",
53
- "@openhps/rf": "^0.1.62",
54
- "@openhps/socket": "^0.1.8",
55
- "@types/benchmark": "^2.1.5",
56
- "@types/chai": "^4.3.16",
57
- "@types/fs-extra": "^11.0.4",
58
- "@types/long": "^5.0.0",
59
- "@types/mocha": "^10.0.6",
60
- "@types/node": "^20.14.2",
61
- "@typescript-eslint/eslint-plugin": ">=7.13.0",
62
- "@typescript-eslint/parser": ">=7.13.0",
63
- "benchmark": "^2.1.4",
64
- "buffer": "^6.0.3",
65
- "eslint": "^8.56.0",
66
- "eslint-config-prettier": "^9.1.0",
67
- "eslint-plugin-deprecation": "^2.0.0",
68
- "eslint-plugin-import": "^2.29.1",
69
- "eslint-plugin-jsdoc": "^48.0.3",
70
- "eslint-plugin-prettier": "^5.1.3",
71
- "husky": "^9.0.11",
72
- "mocha": "^10.4.0",
73
- "mocha-junit-reporter": "^2.2.1",
74
- "npm-run-all": "^4.1.5",
75
- "nyc": "^17.0.0",
76
- "prettier": "^3.3.2",
77
- "reflect-metadata": ">=0.2.2",
78
- "shx": "^0.3.4",
79
- "standard-version": "^9.5.0",
80
- "ts-node": "^10.9.2",
81
- "typedoc": ">=0.25.13",
82
- "typescript": "^5.4.5",
83
- "webpack": "^5.92.0",
84
- "webpack-cli": "^5.1.4",
85
- "wkt": "^0.1.1"
86
- },
87
- "dependencies": {
88
- "@inquirer/prompts": "^5.0.5",
89
- "chai": "^4",
90
- "chalk": "4.x",
91
- "fs-extra": "^11.2.0",
92
- "long": "^5.2.3",
93
- "protobufjs": "^7.3.2",
94
- "yargs": "^17.7.2"
95
- }
2
+ "name": "@openhps/protobuf",
3
+ "version": "1.1.0",
4
+ "description": "OpenHPS Protocol Buffer Generator",
5
+ "author": "Maxim Van de Wynckel",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/OpenHPS/openhps-protobuf.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/OpenHPS/openhps-protobuf/issues"
13
+ },
14
+ "homepage": "https://openhps.org",
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "type": "commonjs",
19
+ "sideEffects": false,
20
+ "main": "./dist/cjs/index.js",
21
+ "types": "./dist/types/index.d.ts",
22
+ "module": "./dist/esm/index.js",
23
+ "es2015": "./dist/esm/index.js",
24
+ "unpkg": "./dist/web/openhps-protobuf.min.js",
25
+ "jsdelivr": "./dist/web/openhps-protobuf.min.js",
26
+ "bin": {
27
+ "openhps-protobuf": "./dist/cjs/bin/generator.js"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/types/index.d.ts",
32
+ "import": "./dist/esm/index.js",
33
+ "require": "./dist/cjs/index.js"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist/**/*",
39
+ "src/**/*",
40
+ "LICENSE"
41
+ ],
42
+ "scripts": {
43
+ "benchmark:1": "ts-node test/bench/serialization.bench.ts",
44
+ "benchmark:2": "ts-node test/bench/socket.bench.ts",
45
+ "benchmark:3": "ts-node test/bench/mqtt.bench.ts",
46
+ "start": "node dist/cjs/bin/generator.js",
47
+ "start:debug": "ts-node src/bin/generator.ts",
48
+ "test": "mocha --config test/.mocharc.json",
49
+ "cover": "c8 mocha --config test/.mocharc.json",
50
+ "cover:ci": "c8 mocha --config test/.mocharc.ci.json && c8 report --reporter=cobertura",
51
+ "lint": "eslint . && node scripts/check-peers.mjs",
52
+ "clean": "shx rm -rf ./dist && shx rm -rf ./docs/out",
53
+ "build": "npm run clean && npm-run-all --parallel build:ts:* && npm run build:webpack",
54
+ "build:quick": "npm-run-all --parallel build:ts:* && npm run build:webpack",
55
+ "build:typedoc": "typedoc --options docs/typedoc.json",
56
+ "build:ts:cjs": "tsc --build ./tsconfig/tsconfig.bundle.cjs.json",
57
+ "build:ts:esm": "tsc --build ./tsconfig/tsconfig.bundle.esm.json && node ./scripts/finalize-esm.mjs",
58
+ "build:ts:types": "tsc --build ./tsconfig/tsconfig.bundle.types.json",
59
+ "build:webpack": "npm-run-all --parallel build:webpack:*",
60
+ "build:webpack:prod": "webpack --env prod --",
61
+ "build:webpack:dev": "webpack --env dev --",
62
+ "publish:development": "npm run bump:beta && npm publish --tag dev",
63
+ "publish:beta": "npm run bump:beta && npm publish --tag beta",
64
+ "publish:release": "npm run bump:beta && npm publish --tag latest",
65
+ "bump:development": "commit-and-tag-version --prerelease alpha -m 'chore(alpha): %s' --skip.changelog",
66
+ "bump:beta": "commit-and-tag-version --prerelease beta -m 'chore(beta): %s' --skip.changelog",
67
+ "bump:release": "commit-and-tag-version -m 'chore(release): %s' --skip.changelog",
68
+ "prepare": "husky",
69
+ "build:golden": "shx rm -rf test/golden/protos && node -r ./test/tsconfig.js test/helpers/build-protos.ts test/golden/protos"
70
+ },
71
+ "dependencies": {
72
+ "@inquirer/prompts": "^5.0.5",
73
+ "chalk": "^4.1.2",
74
+ "fs-extra": "^11.4.1",
75
+ "long": "^5.2.3",
76
+ "protobufjs": "^8.8.1",
77
+ "tslib": "^2.9.1",
78
+ "yargs": "^17.7.2"
79
+ },
80
+ "devDependencies": {
81
+ "@commitlint/cli": "^21.2.4",
82
+ "@commitlint/config-conventional": "^21.2.4",
83
+ "@eslint/js": "^9.39.5",
84
+ "@openhps/core": "^1.0.16",
85
+ "@openhps/mqtt": "^1.0.2",
86
+ "@openhps/rdf": "^1.1.7",
87
+ "@openhps/rf": "^1.0.4",
88
+ "@openhps/socket": "^0.1.8",
89
+ "@types/benchmark": "^2.1.5",
90
+ "@types/chai": "^5.2.2",
91
+ "@types/fs-extra": "^11.0.4",
92
+ "@types/mocha": "^10.0.10",
93
+ "@types/node": "^26.6.6",
94
+ "benchmark": "^2.1.4",
95
+ "buffer": "^6.0.3",
96
+ "c8": "^12.0.0",
97
+ "chai": "^6.3.0",
98
+ "commit-and-tag-version": "^13.3.0",
99
+ "eslint": "^9.39.5",
100
+ "eslint-config-prettier": "^10.1.8",
101
+ "eslint-plugin-import": "^2.32.0",
102
+ "eslint-plugin-jsdoc": "^65.4.5",
103
+ "eslint-plugin-prettier": "^5.5.7",
104
+ "globals": "^17.14.0",
105
+ "husky": "^9.1.7",
106
+ "lint-staged": "^17.6.0",
107
+ "mocha": "^11.8.0",
108
+ "mocha-junit-reporter": "^2.2.1",
109
+ "mocha-multi-reporters": "^1.5.2",
110
+ "npm-run-all": "^4.1.5",
111
+ "prettier": "^3.10.0",
112
+ "reflect-metadata": ">=0.2.2",
113
+ "shx": "^0.4.0",
114
+ "terser-webpack-plugin": "^5.3.14",
115
+ "ts-node": "^10.9.2",
116
+ "typedoc": "^0.28.20",
117
+ "typescript": "^5.9.3",
118
+ "typescript-eslint": "^8.72.2",
119
+ "webpack": "^5.112.2",
120
+ "webpack-cli": "^7.3.1",
121
+ "wkt": "^0.1.1"
122
+ },
123
+ "engines": {
124
+ "node": ">=22.0.0"
125
+ },
126
+ "packageManager": "npm@10.9.2"
96
127
  }
@@ -10,18 +10,14 @@ import {
10
10
  TypedJSON,
11
11
  UUID,
12
12
  } from '@openhps/core';
13
- import * as protobuf from 'protobufjs';
14
13
  import { AnyT, JsonObjectMetadata } from 'typedjson';
14
+ import type { Type } from 'protobufjs';
15
+ import { primitiveWrapper } from './PrimitiveWrapper';
15
16
 
16
17
  export class InternalProtobufDeserializer extends Deserializer {
17
- protected static primitiveWrapper: protobuf.Type;
18
-
19
18
  constructor() {
20
19
  super();
21
20
 
22
- InternalProtobufDeserializer.primitiveWrapper = new protobuf.Type('PrimitiveWrapperMessage');
23
- InternalProtobufDeserializer.primitiveWrapper.add(new protobuf.Field('value', 1, 'string'));
24
-
25
21
  this.setDeserializationStrategy(Number, (object: any) => {
26
22
  if (typeof object === 'number') {
27
23
  return object;
@@ -121,7 +117,7 @@ export class InternalProtobufDeserializer extends Deserializer {
121
117
  ): IndexedObject | T | undefined {
122
118
  let expectedSelfType = typeDescriptor.ctor;
123
119
  let sourceObjectMetadata: ObjectMetadata = JsonObjectMetadata.getFromConstructor(expectedSelfType);
124
- if (sourceObject.hasOwnProperty('_type') && sourceObject._type !== 0) {
120
+ if (Object.prototype.hasOwnProperty.call(sourceObject, '_type') && sourceObject._type !== 0) {
125
121
  const enumTypeClassname = sourceObjectMetadata.protobuf.enumMapping.get(sourceObject._type);
126
122
  if (enumTypeClassname) {
127
123
  const enumType = knownTypes.get(enumTypeClassname);
@@ -131,18 +127,30 @@ export class InternalProtobufDeserializer extends Deserializer {
131
127
  }
132
128
  }
133
129
  } else if (sourceObject.type_url && sourceObject.value) {
134
- const MessageType = serializerOptions.types.get(sourceObject.type_url) as protobuf.Type;
135
- const message = MessageType.decode(sourceObject.value);
136
- const data = this.convertAsObject(
137
- message,
138
- new ConcreteTypeDescriptor(knownTypes.get(sourceObject.type_url)),
139
- knownTypes,
140
- memberName,
141
- deserializer,
142
- memberOptions,
143
- serializerOptions,
144
- );
145
- return data;
130
+ const MessageType = serializerOptions.types.get(sourceObject.type_url) as Type;
131
+ if (MessageType) {
132
+ const message = MessageType.decode(sourceObject.value);
133
+ const data = this.convertAsObject(
134
+ message,
135
+ new ConcreteTypeDescriptor(knownTypes.get(sourceObject.type_url)),
136
+ knownTypes,
137
+ memberName,
138
+ deserializer,
139
+ memberOptions,
140
+ serializerOptions,
141
+ );
142
+ return data;
143
+ }
144
+ // Unknown type: fall back to the primitive wrapper for primitive values
145
+ const message = primitiveWrapper.decode(sourceObject.value) as any;
146
+ switch (sourceObject.type_url) {
147
+ case 'Boolean':
148
+ return (message.value === 'true') as any;
149
+ case 'Number':
150
+ return Number(message.value) as any;
151
+ default:
152
+ return message.value as any;
153
+ }
146
154
  }
147
155
 
148
156
  const sourceMetadata = sourceObjectMetadata;
@@ -162,32 +170,42 @@ export class InternalProtobufDeserializer extends Deserializer {
162
170
 
163
171
  let revivedValue;
164
172
  if (objMemberMetadata.name === 'uid') {
165
- if (sourceObject.hasOwnProperty('uid_bytes') && sourceObject['uid_bytes'].byteLength > 0) {
173
+ if (
174
+ Object.prototype.hasOwnProperty.call(sourceObject, 'uid_bytes') &&
175
+ sourceObject['uid_bytes'].byteLength > 0
176
+ ) {
166
177
  revivedValue = UUID.fromBuffer(sourceObject['uid_bytes']).toString();
167
- } else if (sourceObject.hasOwnProperty('uid_string')) {
178
+ } else if (Object.prototype.hasOwnProperty.call(sourceObject, 'uid_string')) {
168
179
  revivedValue = sourceObject['uid_string'];
169
180
  } else {
170
181
  return;
171
182
  }
172
- } else if (!sourceObject.hasOwnProperty(memberName) || !objMemberValue) {
183
+ } else if (
184
+ !Object.prototype.hasOwnProperty.call(sourceObject, memberName) ||
185
+ objMemberValue === undefined ||
186
+ objMemberValue === null
187
+ ) {
173
188
  return;
174
189
  } else if (objMemberMetadata.type() === AnyT && objMemberValue.type_url) {
175
- const MessageType = serializerOptions.types.get(sourceObject[memberName].type_url) as protobuf.Type;
190
+ const MessageType = serializerOptions.types.get(sourceObject[memberName].type_url) as Type;
176
191
  if (MessageType) {
177
- const message = MessageType.decode(objMemberValue.value);
178
- revivedValue = deserializer.convertSingleValue(
179
- message,
180
- new ConcreteTypeDescriptor(knownTypes.get(objMemberValue.type_url)),
181
- knownTypes,
182
- objMemberDebugName,
183
- objMemberOptions,
184
- serializerOptions,
185
- );
192
+ const messageType = knownTypes.get(objMemberValue.type_url);
193
+ if (messageType !== undefined) {
194
+ const message = MessageType.decode(objMemberValue.value);
195
+ revivedValue = deserializer.convertSingleValue(
196
+ message,
197
+ new ConcreteTypeDescriptor(messageType),
198
+ knownTypes,
199
+ objMemberDebugName,
200
+ objMemberOptions,
201
+ serializerOptions,
202
+ );
203
+ }
186
204
  } else if (objMemberValue.value) {
187
- const message = InternalProtobufDeserializer.primitiveWrapper.decode(objMemberValue.value) as any;
205
+ const message = primitiveWrapper.decode(objMemberValue.value) as any;
188
206
  switch (objMemberValue.type_url) {
189
207
  case 'Boolean':
190
- revivedValue = Boolean(message.value);
208
+ revivedValue = message.value === 'true';
191
209
  break;
192
210
  case 'Number':
193
211
  revivedValue = Number(message.value);
@@ -9,18 +9,14 @@ import {
9
9
  TypedJSON,
10
10
  UUID,
11
11
  } from '@openhps/core';
12
- import { Type } from 'protobufjs';
13
12
  import { AnyT, JsonObjectMetadata } from 'typedjson';
14
- import * as protobuf from 'protobufjs';
13
+ import type { Type } from 'protobufjs';
14
+ import { primitiveWrapper } from './PrimitiveWrapper';
15
15
 
16
16
  export class InternalProtobufSerializer extends Serializer {
17
- protected static primitiveWrapper: Type;
18
-
19
17
  constructor() {
20
18
  super();
21
19
  this.setSerializationStrategy(Map, this.convertAsMap.bind(this));
22
- InternalProtobufSerializer.primitiveWrapper = new protobuf.Type('PrimitiveWrapperMessage');
23
- InternalProtobufSerializer.primitiveWrapper.add(new protobuf.Field('value', 1, 'string'));
24
20
  }
25
21
 
26
22
  convertSingleValue(
@@ -122,7 +118,10 @@ export class InternalProtobufSerializer extends Serializer {
122
118
  }
123
119
  } else {
124
120
  const MessageType = sourceTypeMetadata.protobuf.messageType;
125
- const field = MessageType.get(objMemberMetadata.name);
121
+ // Look the field up by its wire name: members renamed through
122
+ // `options.protobuf.name` are emitted under that name, so the
123
+ // metadata name would not resolve for them.
124
+ const field = MessageType.get(memberName);
126
125
  serialized = this.convertSingleValue(
127
126
  sourceObject[objMemberMetadata.key],
128
127
  objMemberMetadata.type(),
@@ -139,7 +138,7 @@ export class InternalProtobufSerializer extends Serializer {
139
138
  if (objMemberMetadata.type() === AnyT) {
140
139
  const MessageType = serializerOptions.types.get(
141
140
  sourceObject[objMemberMetadata.key].constructor.name,
142
- ) as protobuf.Type;
141
+ ) as Type;
143
142
  if (MessageType) {
144
143
  const message = MessageType.create(serialized);
145
144
  serialized = {
@@ -147,12 +146,12 @@ export class InternalProtobufSerializer extends Serializer {
147
146
  value: MessageType.encode(message).finish(),
148
147
  };
149
148
  } else {
150
- const message = InternalProtobufSerializer.primitiveWrapper.create({
149
+ const message = primitiveWrapper.create({
151
150
  value: String(sourceObject[objMemberMetadata.key]),
152
151
  });
153
152
  serialized = {
154
153
  type_url: sourceObject[objMemberMetadata.key].constructor.name,
155
- value: InternalProtobufSerializer.primitiveWrapper.encode(message).finish(),
154
+ value: primitiveWrapper.encode(message).finish(),
156
155
  };
157
156
  }
158
157
  }
@@ -0,0 +1,9 @@
1
+ import * as protobuf from 'protobufjs';
2
+
3
+ /**
4
+ * Protobuf type wrapping a primitive value as a string.
5
+ * Shared by the serializer and the deserializer as the `google.protobuf.Any`
6
+ * fallback for primitive values that protobuf cannot express directly.
7
+ */
8
+ export const primitiveWrapper: protobuf.Type = new protobuf.Type('PrimitiveWrapperMessage');
9
+ primitiveWrapper.add(new protobuf.Field('value', 1, 'string'));
@@ -8,6 +8,12 @@ import { InternalProtobufSerializer } from './InternalProtobufSerializer';
8
8
 
9
9
  export class ProtobufSerializer extends DataSerializer {
10
10
  protected static wrapperMessage: protobuf.Type;
11
+ /**
12
+ * Root holding {@link wrapperMessage}. protobufjs 8 resolves edition features by
13
+ * walking up to a root, and throws "Unable to find a parent for WrapperMessage" for
14
+ * a Type that was never added to one. Held as a field so it stays reachable.
15
+ */
16
+ protected static wrapperRoot: protobuf.Root;
11
17
  protected static options: ProtobufSerializerConfig = {
12
18
  serializer: new InternalProtobufSerializer(),
13
19
  deserializer: new InternalProtobufDeserializer(),
@@ -39,6 +45,26 @@ export class ProtobufSerializer extends DataSerializer {
39
45
  static initialize(directory?: string): Promise<void> {
40
46
  return new Promise((resolve, reject) => {
41
47
  ProtobufSerializer.root = new protobuf.Root();
48
+ // Reinitialisation (e.g. per test) must not accumulate stale type entries:
49
+ // build the new map first and only swap it in once the schemas have
50
+ // loaded, so a failed load keeps the previous types instead of wiping them
51
+ const types = new Map<string, protobuf.Type>();
52
+ // The generated schemas extend google.protobuf.EnumValueOptions, which is
53
+ // declared in descriptor.proto. protobufjs resolves imports relative to the
54
+ // origin file, so it looked for google/protobuf/descriptor.proto inside the
55
+ // generated output directory and failed with "unresolvable extensions".
56
+ // protobufjs ships the well-known types itself; point the resolver at them.
57
+ // Resolve the package root from a literal specifier: a template literal here
58
+ // makes webpack build a context module over the whole protobufjs package,
59
+ // which then tries to parse its .d.ts and tsconfig.json as bundle input.
60
+ const protobufRoot = path.dirname(require.resolve('protobufjs/package.json'));
61
+ const defaultResolve = ProtobufSerializer.root.resolvePath.bind(ProtobufSerializer.root);
62
+ ProtobufSerializer.root.resolvePath = (origin: string, target: string) => {
63
+ if (target.startsWith('google/protobuf/')) {
64
+ return path.join(protobufRoot, target);
65
+ }
66
+ return defaultResolve(origin, target);
67
+ };
42
68
  Promise.resolve(
43
69
  !directory ? (ProjectGenerator.buildProject(path.resolve('tmp')) as any) : Promise.resolve(),
44
70
  )
@@ -88,9 +114,10 @@ export class ProtobufSerializer extends DataSerializer {
88
114
  messageTypeEnum: enumNumber,
89
115
  enumMapping,
90
116
  };
91
- this.options.types.set(className, MessageType);
117
+ types.set(className, MessageType);
92
118
  }
93
119
  });
120
+ this.options.types = types;
94
121
  resolve();
95
122
  })
96
123
  .catch((error) => {
@@ -98,9 +125,11 @@ export class ProtobufSerializer extends DataSerializer {
98
125
  });
99
126
  });
100
127
 
128
+ this.wrapperRoot = new protobuf.Root();
101
129
  this.wrapperMessage = new protobuf.Type('WrapperMessage');
102
130
  this.wrapperMessage.add(new protobuf.Field('data', 1, 'google.protobuf.Any'));
103
131
  this.wrapperMessage.addJSON(protobuf.common['google/protobuf/any.proto'].nested);
132
+ this.wrapperRoot.add(this.wrapperMessage);
104
133
  return loadPromise;
105
134
  })
106
135
  .then(() => {
@@ -146,6 +175,9 @@ export class ProtobufSerializer extends DataSerializer {
146
175
  const decodedData: any = this.wrapperMessage.decode(serializedData);
147
176
  const typeURL = decodedData.data.type_url;
148
177
  const MessageType = this.options.types.get(typeURL) as protobuf.Type;
178
+ if (MessageType === undefined) {
179
+ throw new Error(`Unknown protobuf type '${typeURL}'`);
180
+ }
149
181
  const decodedMessage = MessageType.decode(decodedData.data.value);
150
182
  const finalType = dataType ?? (this.knownTypes.get(typeURL) as Constructor<T>);
151
183
  return this.options.deserializer.convertSingleValue(
@@ -20,9 +20,9 @@ import {
20
20
  RelativeDistance,
21
21
  } from '@openhps/core';
22
22
  import { ProtobufSerializer } from '../ProtobufSerializer';
23
- import { expect } from 'chai';
23
+ import assert from 'node:assert/strict';
24
24
 
25
- const args: [K: string] = yargs.argv as any;
25
+ const args = yargs.argv;
26
26
  const data = {
27
27
  directory: path.normalize('tmp'),
28
28
  };
@@ -114,13 +114,13 @@ function test(): Promise<void> {
114
114
  frame.addObject(new DataObject('test2', 'Test Object 2'));
115
115
  const buffer = ProtobufSerializer.serialize(frame);
116
116
  const deserialized: DataFrame = ProtobufSerializer.deserialize(buffer);
117
- expect(deserialized).to.not.be.undefined;
117
+ assert.notStrictEqual(deserialized, undefined, 'deserialization returned undefined');
118
118
  const compare1 = JSON.stringify(DataSerializer.serialize(deserialized), null, 2);
119
119
  const compare2 = JSON.stringify(DataSerializer.serialize(frame), null, 2);
120
120
  // console.log(compare1);
121
121
  // console.log('\n');
122
122
  // console.log(compare2);
123
- expect(compare1).to.eql(compare2);
123
+ assert.deepStrictEqual(compare1, compare2, 'round-tripped frame differs from the original');
124
124
  console.log(chalk.green(`Basic serialization and deserialization test completed!`));
125
125
  resolve();
126
126
  })
@@ -12,20 +12,26 @@ import {
12
12
  import chalk from 'chalk';
13
13
  import { AnyT, Constructor } from 'typedjson';
14
14
  import { HEADER } from './constants';
15
- import { ProtobufGenerator, ProtobufMessage } from './ProtobufGenerator';
15
+ import { ProtobufGenerator, ProtobufMessage, TypeMapping } from './ProtobufGenerator';
16
16
  import { ProjectBuildOptions } from './types';
17
17
 
18
+ // Re-exported so the generator's public surface does not change
19
+ export type { ProtobufPrimitive, TypeMapping } from './ProtobufGenerator';
20
+
18
21
  /**
19
22
  * Protobuf object generator
20
23
  */
21
- export class ObjectGenerator extends ProtobufGenerator<Object> { // eslint-disable-line
22
- processObject(object: Constructor<Object>, metaData: ObjectMemberMetadata): Promise<void> { // eslint-disable-line
24
+ // `Object` here is the global constructor interface used by Constructor<T>, not the wrapper type.
25
+ /* eslint-disable @typescript-eslint/no-wrapper-object-types */
26
+ export class ObjectGenerator extends ProtobufGenerator<Object> {
27
+ processObject(object: Constructor<Object>, metaData: ObjectMemberMetadata): Promise<void> {
23
28
  throw new Error('Method not implemented.');
24
29
  }
25
30
 
26
- generate(object: Constructor<Object>, metaData: ObjectMemberMetadata): Promise<ProtobufMessage> { // eslint-disable-line
31
+ generate(object: Constructor<Object>, metaData: ObjectMemberMetadata): Promise<ProtobufMessage> {
27
32
  throw new Error('Method not implemented.');
28
33
  }
34
+ /* eslint-enable @typescript-eslint/no-wrapper-object-types */
29
35
 
30
36
  protected static typeMapping(
31
37
  object: ObjectMetadata,
@@ -64,6 +70,14 @@ export class ObjectGenerator extends ProtobufGenerator<Object> { // eslint-disab
64
70
  memberOptions,
65
71
  buildOptions,
66
72
  );
73
+ // An array of a type protobuf cannot express — Set, Object, Function or
74
+ // a member with a custom serializer — maps to undefined, exactly as the
75
+ // Map case below already handles. Reading .syntax off it threw
76
+ // "Cannot read properties of undefined". The caller treats undefined as
77
+ // "omit this field".
78
+ if (mappings === undefined) {
79
+ return undefined;
80
+ }
67
81
  return {
68
82
  syntax: `repeated ${mappings.syntax}`,
69
83
  types: [mappings],
@@ -133,12 +147,17 @@ export class ObjectGenerator extends ProtobufGenerator<Object> { // eslint-disab
133
147
  }
134
148
 
135
149
  static processObject(object: ObjectMetadata, buildOptions: ProjectBuildOptions): void {
136
- object.protobuf = object.protobuf ?? {
137
- generator: {
138
- subModules: new Set(),
139
- subTypes: [],
140
- allowOverride: true,
141
- },
150
+ // object.protobuf is a slot shared with the runtime serializer, which fills it in
151
+ // as {messageType, messageTypeEnum, enumMapping} with no `generator` key. Guarding
152
+ // the whole object with `??` kept that value and then dereferenced a `generator`
153
+ // that was not there, so ProtobufSerializer.initialize() threw if anything had
154
+ // already initialised the serializer in the same process. Guard the two levels
155
+ // separately.
156
+ object.protobuf = object.protobuf ?? ({} as any);
157
+ object.protobuf.generator = object.protobuf.generator ?? {
158
+ subModules: new Set(),
159
+ subTypes: [],
160
+ allowOverride: true,
142
161
  };
143
162
 
144
163
  const dataMembers = object.dataMembers;
@@ -190,7 +209,8 @@ export class ObjectGenerator extends ProtobufGenerator<Object> { // eslint-disab
190
209
  object.protobuf.generator.type = object.protobuf.generator.type ?? object.classType;
191
210
  subTypes.forEach((type) => {
192
211
  const subTypeMeta = DataSerializerUtils.getOwnMetadata(type);
193
- subTypeMeta.protobuf = subTypeMeta.protobuf ?? { generator: {} };
212
+ subTypeMeta.protobuf = subTypeMeta.protobuf ?? ({} as any);
213
+ subTypeMeta.protobuf.generator = subTypeMeta.protobuf.generator ?? {};
194
214
  subTypeMeta.protobuf.generator.type = object.classType;
195
215
  });
196
216
  }
@@ -250,7 +270,7 @@ export class ObjectGenerator extends ProtobufGenerator<Object> { // eslint-disab
250
270
  .map((member) => {
251
271
  const options: any =
252
272
  member.options && (member.options as any).protobuf ? (member.options as any).protobuf : {};
253
- let type: TypeMapping = undefined;
273
+ let type: TypeMapping;
254
274
  const memberName = options.name ?? member.name;
255
275
 
256
276
  if (memberName === 'uid') {
@@ -368,11 +388,3 @@ export class ObjectGenerator extends ProtobufGenerator<Object> { // eslint-disab
368
388
  ];
369
389
  }
370
390
  }
371
-
372
- export type ProtobufPrimitive = 'string' | 'int32' | 'bool' | 'bytes';
373
- export interface TypeMapping {
374
- syntax: ProtobufPrimitive | string;
375
- types?: TypeMapping[];
376
- package?: string;
377
- name?: string;
378
- }