@openhps/protobuf 1.0.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 (137) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +165 -0
  3. package/dist/cjs/InternalProtobufDeserializer.js +152 -0
  4. package/dist/cjs/InternalProtobufDeserializer.js.map +1 -0
  5. package/dist/cjs/InternalProtobufSerializer.js +137 -0
  6. package/dist/cjs/InternalProtobufSerializer.js.map +1 -0
  7. package/dist/cjs/ProtobufSerializer.js +136 -0
  8. package/dist/cjs/ProtobufSerializer.js.map +1 -0
  9. package/dist/cjs/bin/generator.js +107 -0
  10. package/dist/cjs/bin/generator.js.map +1 -0
  11. package/dist/cjs/decorators/index.js +4 -0
  12. package/dist/cjs/decorators/index.js.map +1 -0
  13. package/dist/cjs/decorators/options.js +3 -0
  14. package/dist/cjs/decorators/options.js.map +1 -0
  15. package/dist/cjs/generator/ObjectGenerator.js +316 -0
  16. package/dist/cjs/generator/ObjectGenerator.js.map +1 -0
  17. package/dist/cjs/generator/ProjectGenerator.js +120 -0
  18. package/dist/cjs/generator/ProjectGenerator.js.map +1 -0
  19. package/dist/cjs/generator/ProtobufGenerator.js +31 -0
  20. package/dist/cjs/generator/ProtobufGenerator.js.map +1 -0
  21. package/dist/cjs/generator/ServiceGenerator.js +14 -0
  22. package/dist/cjs/generator/ServiceGenerator.js.map +1 -0
  23. package/dist/cjs/generator/constants.js +21 -0
  24. package/dist/cjs/generator/constants.js.map +1 -0
  25. package/dist/cjs/generator/index.js +7 -0
  26. package/dist/cjs/generator/index.js.map +1 -0
  27. package/dist/cjs/generator/types.js +3 -0
  28. package/dist/cjs/generator/types.js.map +1 -0
  29. package/dist/cjs/index.js +6 -0
  30. package/dist/cjs/index.js.map +1 -0
  31. package/dist/esm/InternalProtobufDeserializer.js +147 -0
  32. package/dist/esm/InternalProtobufDeserializer.js.map +1 -0
  33. package/dist/esm/InternalProtobufSerializer.js +132 -0
  34. package/dist/esm/InternalProtobufSerializer.js.map +1 -0
  35. package/dist/esm/ProtobufSerializer.js +131 -0
  36. package/dist/esm/ProtobufSerializer.js.map +1 -0
  37. package/dist/esm/bin/generator.js +104 -0
  38. package/dist/esm/bin/generator.js.map +1 -0
  39. package/dist/esm/decorators/index.js +2 -0
  40. package/dist/esm/decorators/index.js.map +1 -0
  41. package/dist/esm/decorators/options.js +2 -0
  42. package/dist/esm/decorators/options.js.map +1 -0
  43. package/dist/esm/generator/ObjectGenerator.js +311 -0
  44. package/dist/esm/generator/ObjectGenerator.js.map +1 -0
  45. package/dist/esm/generator/ProjectGenerator.js +115 -0
  46. package/dist/esm/generator/ProjectGenerator.js.map +1 -0
  47. package/dist/esm/generator/ProtobufGenerator.js +27 -0
  48. package/dist/esm/generator/ProtobufGenerator.js.map +1 -0
  49. package/dist/esm/generator/ServiceGenerator.js +10 -0
  50. package/dist/esm/generator/ServiceGenerator.js.map +1 -0
  51. package/dist/esm/generator/constants.js +18 -0
  52. package/dist/esm/generator/constants.js.map +1 -0
  53. package/dist/esm/generator/index.js +4 -0
  54. package/dist/esm/generator/index.js.map +1 -0
  55. package/dist/esm/generator/types.js +2 -0
  56. package/dist/esm/generator/types.js.map +1 -0
  57. package/dist/esm/index.js +3 -0
  58. package/dist/esm/index.js.map +1 -0
  59. package/dist/esm5/InternalProtobufDeserializer.js +147 -0
  60. package/dist/esm5/InternalProtobufDeserializer.js.map +1 -0
  61. package/dist/esm5/InternalProtobufSerializer.js +132 -0
  62. package/dist/esm5/InternalProtobufSerializer.js.map +1 -0
  63. package/dist/esm5/ProtobufSerializer.js +131 -0
  64. package/dist/esm5/ProtobufSerializer.js.map +1 -0
  65. package/dist/esm5/bin/generator.js +104 -0
  66. package/dist/esm5/bin/generator.js.map +1 -0
  67. package/dist/esm5/decorators/index.js +2 -0
  68. package/dist/esm5/decorators/index.js.map +1 -0
  69. package/dist/esm5/decorators/options.js +2 -0
  70. package/dist/esm5/decorators/options.js.map +1 -0
  71. package/dist/esm5/generator/ObjectGenerator.js +311 -0
  72. package/dist/esm5/generator/ObjectGenerator.js.map +1 -0
  73. package/dist/esm5/generator/ProjectGenerator.js +115 -0
  74. package/dist/esm5/generator/ProjectGenerator.js.map +1 -0
  75. package/dist/esm5/generator/ProtobufGenerator.js +27 -0
  76. package/dist/esm5/generator/ProtobufGenerator.js.map +1 -0
  77. package/dist/esm5/generator/ServiceGenerator.js +10 -0
  78. package/dist/esm5/generator/ServiceGenerator.js.map +1 -0
  79. package/dist/esm5/generator/constants.js +18 -0
  80. package/dist/esm5/generator/constants.js.map +1 -0
  81. package/dist/esm5/generator/index.js +4 -0
  82. package/dist/esm5/generator/index.js.map +1 -0
  83. package/dist/esm5/generator/types.js +2 -0
  84. package/dist/esm5/generator/types.js.map +1 -0
  85. package/dist/esm5/index.js +3 -0
  86. package/dist/esm5/index.js.map +1 -0
  87. package/dist/types/InternalProtobufDeserializer.d.ts +9 -0
  88. package/dist/types/InternalProtobufDeserializer.d.ts.map +1 -0
  89. package/dist/types/InternalProtobufSerializer.d.ts +23 -0
  90. package/dist/types/InternalProtobufSerializer.d.ts.map +1 -0
  91. package/dist/types/ProtobufSerializer.d.ts +32 -0
  92. package/dist/types/ProtobufSerializer.d.ts.map +1 -0
  93. package/dist/types/bin/generator.d.ts +3 -0
  94. package/dist/types/bin/generator.d.ts.map +1 -0
  95. package/dist/types/decorators/index.d.ts +2 -0
  96. package/dist/types/decorators/index.d.ts.map +1 -0
  97. package/dist/types/decorators/options.d.ts +30 -0
  98. package/dist/types/decorators/options.d.ts.map +1 -0
  99. package/dist/types/generator/ObjectGenerator.d.ts +22 -0
  100. package/dist/types/generator/ObjectGenerator.d.ts.map +1 -0
  101. package/dist/types/generator/ProjectGenerator.d.ts +17 -0
  102. package/dist/types/generator/ProjectGenerator.d.ts.map +1 -0
  103. package/dist/types/generator/ProtobufGenerator.d.ts +26 -0
  104. package/dist/types/generator/ProtobufGenerator.d.ts.map +1 -0
  105. package/dist/types/generator/ServiceGenerator.d.ts +7 -0
  106. package/dist/types/generator/ServiceGenerator.d.ts.map +1 -0
  107. package/dist/types/generator/constants.d.ts +3 -0
  108. package/dist/types/generator/constants.d.ts.map +1 -0
  109. package/dist/types/generator/index.d.ts +4 -0
  110. package/dist/types/generator/index.d.ts.map +1 -0
  111. package/dist/types/generator/types.d.ts +5 -0
  112. package/dist/types/generator/types.d.ts.map +1 -0
  113. package/dist/types/index.d.ts +3 -0
  114. package/dist/types/index.d.ts.map +1 -0
  115. package/dist/web/openhps-protobuf.es.js +15165 -0
  116. package/dist/web/openhps-protobuf.es.js.map +1 -0
  117. package/dist/web/openhps-protobuf.es.min.js +2 -0
  118. package/dist/web/openhps-protobuf.es.min.js.map +1 -0
  119. package/dist/web/openhps-protobuf.js +16722 -0
  120. package/dist/web/openhps-protobuf.js.map +1 -0
  121. package/dist/web/openhps-protobuf.min.js +2 -0
  122. package/dist/web/openhps-protobuf.min.js.map +1 -0
  123. package/package.json +96 -0
  124. package/src/InternalProtobufDeserializer.ts +245 -0
  125. package/src/InternalProtobufSerializer.ts +212 -0
  126. package/src/ProtobufSerializer.ts +168 -0
  127. package/src/bin/generator.ts +131 -0
  128. package/src/decorators/index.ts +1 -0
  129. package/src/decorators/options.ts +30 -0
  130. package/src/generator/ObjectGenerator.ts +377 -0
  131. package/src/generator/ProjectGenerator.ts +130 -0
  132. package/src/generator/ProtobufGenerator.ts +49 -0
  133. package/src/generator/ServiceGenerator.ts +11 -0
  134. package/src/generator/constants.ts +19 -0
  135. package/src/generator/index.ts +3 -0
  136. package/src/generator/types.ts +4 -0
  137. package/src/index.ts +2 -0
@@ -0,0 +1,377 @@
1
+ import {
2
+ ArrayTypeDescriptor,
3
+ DataSerializerUtils,
4
+ MapTypeDescriptor,
5
+ ObjectMemberMetadata,
6
+ ObjectMetadata,
7
+ SerializableMemberOptions,
8
+ TypeDescriptor,
9
+ SerializationUtils,
10
+ ConcreteTypeDescriptor,
11
+ } from '@openhps/core';
12
+ import chalk from 'chalk';
13
+ import { AnyT, Constructor } from 'typedjson';
14
+ import { HEADER } from './constants';
15
+ import { ProtobufGenerator, ProtobufMessage } from './ProtobufGenerator';
16
+ import { ProjectBuildOptions } from './types';
17
+
18
+ /**
19
+ * Protobuf object generator
20
+ */
21
+ export class ObjectGenerator extends ProtobufGenerator<Object> { // eslint-disable-line
22
+ processObject(object: Constructor<Object>, metaData: ObjectMemberMetadata): Promise<void> { // eslint-disable-line
23
+ throw new Error('Method not implemented.');
24
+ }
25
+
26
+ generate(object: Constructor<Object>, metaData: ObjectMemberMetadata): Promise<ProtobufMessage> { // eslint-disable-line
27
+ throw new Error('Method not implemented.');
28
+ }
29
+
30
+ protected static typeMapping(
31
+ object: ObjectMetadata,
32
+ type: TypeDescriptor,
33
+ memberOptions: ObjectMemberMetadata,
34
+ buildOptions: ProjectBuildOptions,
35
+ ): TypeMapping {
36
+ switch (type.ctor) {
37
+ case String:
38
+ return {
39
+ syntax: 'string',
40
+ };
41
+ case Number: {
42
+ const options: SerializableMemberOptions = memberOptions.options;
43
+ const numberType = options ? this.getNumberType(options.numberType) : 'int32';
44
+ if (numberType === 'string' && buildOptions.logLevel > 1) {
45
+ console.warn(
46
+ chalk.yellow(
47
+ `${object.classType.name}[${memberOptions.name}] defines a number without specifing a type. This will affect performance.`,
48
+ ),
49
+ );
50
+ }
51
+ return {
52
+ syntax: numberType,
53
+ };
54
+ }
55
+ case Boolean:
56
+ return {
57
+ syntax: 'bool',
58
+ };
59
+ case Array: {
60
+ const mappings = this.typeMapping(
61
+ object,
62
+ (type as ArrayTypeDescriptor).elementType,
63
+ memberOptions,
64
+ buildOptions,
65
+ );
66
+ return {
67
+ syntax: `repeated ${mappings.syntax}`,
68
+ types: [mappings],
69
+ };
70
+ }
71
+ case Map: {
72
+ const key = this.typeMapping(object, (type as MapTypeDescriptor).keyType, memberOptions, buildOptions);
73
+ const value = this.typeMapping(
74
+ object,
75
+ (type as MapTypeDescriptor).valueType,
76
+ memberOptions,
77
+ buildOptions,
78
+ );
79
+ if (key === undefined || value === undefined) {
80
+ return undefined;
81
+ }
82
+ return {
83
+ syntax: `map<${key.syntax},${value.syntax}>`,
84
+ types: [value, key],
85
+ };
86
+ }
87
+ case Set:
88
+ case Object:
89
+ case Function:
90
+ // Non-serializable types
91
+ return undefined;
92
+ case Buffer:
93
+ case Uint8Array:
94
+ return {
95
+ syntax: 'bytes',
96
+ };
97
+ default: {
98
+ const memberMetadata = DataSerializerUtils.getOwnMetadata(type.ctor);
99
+ if (!memberMetadata) {
100
+ return {
101
+ syntax: 'google.protobuf.Any',
102
+ };
103
+ } else if (
104
+ memberMetadata.knownTypes.size > 1 &&
105
+ memberMetadata.protobuf.generator.type &&
106
+ memberMetadata.protobuf.generator.type !== object.classType
107
+ ) {
108
+ return this.typeMapping(
109
+ DataSerializerUtils.getOwnMetadata(memberMetadata.protobuf.generator.type),
110
+ type,
111
+ memberOptions,
112
+ buildOptions,
113
+ );
114
+ } else if (
115
+ memberMetadata.knownTypes.size > 1 &&
116
+ !memberMetadata.protobuf.generator.type &&
117
+ memberOptions &&
118
+ (buildOptions.useAnyType || !memberMetadata.protobuf.generator.allowOverride)
119
+ ) {
120
+ return {
121
+ syntax: 'google.protobuf.Any',
122
+ };
123
+ } else {
124
+ const packageStr = type.ctor.prototype._module ? type.ctor.prototype._module : '@openhps/core';
125
+ return {
126
+ syntax: type.ctor.name,
127
+ package: packageStr,
128
+ };
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ static processObject(object: ObjectMetadata, buildOptions: ProjectBuildOptions): void {
135
+ object.protobuf = object.protobuf ?? {
136
+ generator: {
137
+ subModules: new Set(),
138
+ subTypes: [],
139
+ allowOverride: true,
140
+ },
141
+ };
142
+
143
+ const dataMembers = object.dataMembers;
144
+ // Parse object itself
145
+ if (object.knownTypes.size > 1) {
146
+ const subTypes = [];
147
+ const modules = new Set<string>();
148
+ const dataMembersClone = SerializationUtils.cloneDeep(dataMembers);
149
+ object.knownTypes.forEach((knownType) => {
150
+ const knownTypeMeta = DataSerializerUtils.getOwnMetadata(knownType);
151
+ const knownTypeDataMembers = knownTypeMeta.dataMembers;
152
+ modules.add(knownType.prototype._module);
153
+
154
+ if (knownType !== object.classType && knownType.prototype instanceof object.classType) {
155
+ subTypes.push(knownType);
156
+ }
157
+
158
+ knownTypeDataMembers.forEach((member, key) => {
159
+ member.options = member.options ?? {};
160
+ member.options.protobuf = member.options.protobuf ?? {};
161
+
162
+ if (!dataMembers.has(key)) {
163
+ const memberClone = SerializationUtils.cloneDeep(member);
164
+ // Optional
165
+ memberClone.options.protobuf = {
166
+ optional: true,
167
+ subMembers: new Map(),
168
+ };
169
+ dataMembersClone.set(key, memberClone);
170
+ } else if (dataMembers.get(key).type().ctor !== member.type().ctor) {
171
+ const memberClone = dataMembersClone.get(member.key);
172
+ object.protobuf.generator.allowOverride = false;
173
+ memberClone.options = memberClone.options ?? {};
174
+ memberClone.options.protobuf = memberClone.options.protobuf ?? { subMembers: new Map() };
175
+ const subMembers = (memberClone.options.protobuf as any).subMembers ?? new Map();
176
+ const memberName = member.name + '_' + member.type().ctor.name.toLowerCase();
177
+ subMembers.set(memberName, member);
178
+ member.options = member.options ?? {};
179
+ member.options.protobuf = member.options.protobuf ?? {};
180
+ member.options.protobuf.name = memberName;
181
+ }
182
+ });
183
+ });
184
+
185
+ object.protobuf.generator.subTypes = subTypes;
186
+ object.protobuf.generator.subModules = modules;
187
+ if (subTypes.length > 0 && (modules.size === 1 || !buildOptions.useAnyType)) {
188
+ object.protobuf.generator.dataMembers = dataMembersClone;
189
+ object.protobuf.generator.type = object.protobuf.generator.type ?? object.classType;
190
+ subTypes.forEach((type) => {
191
+ const subTypeMeta = DataSerializerUtils.getOwnMetadata(type);
192
+ subTypeMeta.protobuf = subTypeMeta.protobuf ?? { generator: {} };
193
+ subTypeMeta.protobuf.generator.type = object.classType;
194
+ });
195
+ }
196
+ }
197
+ }
198
+
199
+ static createProtoMessage(object: ObjectMetadata, buildOptions: ProjectBuildOptions): [string, string] {
200
+ const rootMeta = DataSerializerUtils.getRootMetadata(object.classType);
201
+ const dataMembers = object.protobuf.generator.dataMembers ?? object.dataMembers;
202
+ const packageStr = object.classType.prototype._module ? object.classType.prototype._module : '@openhps/core';
203
+ const imports = [];
204
+
205
+ // Parse object itself
206
+ let dataTypesEnum = undefined;
207
+
208
+ if (object.knownTypes.size > 1) {
209
+ if (
210
+ object.protobuf.generator.subTypes.length > 0 &&
211
+ (object.protobuf.generator.subModules.size === 1 || !buildOptions.useAnyType)
212
+ ) {
213
+ imports.push(`import "../../common.proto";`);
214
+ dataTypesEnum =
215
+ `\nenum ${object.classType.name}Type {\n` +
216
+ `\tUNSPECIFIED = 0;\n` +
217
+ [object.classType, ...object.protobuf.generator.subTypes]
218
+ .map((type) => {
219
+ return `\t${type.name
220
+ .replace(/(?:^|\.?)(([A-Z0-9][a-z0-9]|$)|([0-9]+[A-Z]))/g, (_, y) => {
221
+ return '_' + y;
222
+ })
223
+ .replace(/(^_)|(_$)/g, '')
224
+ .toUpperCase()} = ${
225
+ Array.from(rootMeta.knownTypes.values()).indexOf(type) + 1
226
+ } [\n\t\t(className) = "${type.name}",\n\t\t(packageName) = "${type.prototype._module}"\n\t]`;
227
+ })
228
+ .join(';\n') +
229
+ `;\n}\n`;
230
+ }
231
+ }
232
+
233
+ if (rootMeta.classType !== object.classType) {
234
+ const rootMetaType = this.typeMapping(
235
+ rootMeta,
236
+ new ConcreteTypeDescriptor(rootMeta.classType),
237
+ undefined,
238
+ buildOptions,
239
+ );
240
+ if (rootMetaType.package && rootMetaType.package === packageStr) {
241
+ imports.push(`import "${rootMetaType.syntax}.proto";`);
242
+ } else if (rootMetaType.package) {
243
+ imports.push(`import "../../${rootMetaType.package}/${rootMetaType.syntax}.proto";`);
244
+ }
245
+ }
246
+
247
+ let index = 1;
248
+ const members = Array.from(dataMembers.values())
249
+ .map((member) => {
250
+ const options: any =
251
+ member.options && (member.options as any).protobuf ? (member.options as any).protobuf : {};
252
+ let type: TypeMapping = undefined;
253
+ const memberName = options.name ?? member.name;
254
+
255
+ if (memberName === 'uid') {
256
+ // Handle as UUID or string
257
+ type = {
258
+ syntax: 'oneof',
259
+ types: [
260
+ {
261
+ syntax: 'string',
262
+ name: 'uid_string',
263
+ },
264
+ {
265
+ syntax: 'bytes',
266
+ name: 'uid_bytes',
267
+ },
268
+ ],
269
+ };
270
+ } else if (member.type() === AnyT) {
271
+ if (options.subMembers && options.subMembers.size > 0) {
272
+ type = {
273
+ syntax: 'oneof',
274
+ types: [
275
+ ...Array.from(options.subMembers.keys()).map((key: string) => {
276
+ const subMember = options.subMembers.get(key);
277
+ const typeMapping = this.typeMapping(
278
+ object,
279
+ subMember.type(),
280
+ subMember,
281
+ buildOptions,
282
+ );
283
+ typeMapping.name = key;
284
+ return typeMapping;
285
+ }),
286
+ {
287
+ syntax: 'google.protobuf.Any',
288
+ name: memberName + '_' + 'any',
289
+ },
290
+ ],
291
+ };
292
+ } else {
293
+ type = {
294
+ syntax: 'google.protobuf.Any',
295
+ };
296
+ }
297
+ } else if (member.serializer && member.type === undefined) {
298
+ // Custom serializer
299
+ return undefined;
300
+ } else {
301
+ type = this.typeMapping(object, member.type(), member, buildOptions);
302
+ }
303
+
304
+ if (!type) {
305
+ return undefined;
306
+ }
307
+
308
+ if (type.package && type.package === packageStr) {
309
+ imports.push(`import "${type.syntax}.proto";`);
310
+ } else if (type.package) {
311
+ imports.push(`import "../../${type.package}/${type.syntax}.proto";`);
312
+ } else if (type.syntax === 'google.protobuf.Any') {
313
+ imports.push(`import "google/protobuf/any.proto";`);
314
+ }
315
+
316
+ if (type.types) {
317
+ type.types.forEach((dependency) => {
318
+ if (dependency.package && dependency.package === packageStr) {
319
+ imports.push(`import "${dependency.syntax}.proto";`);
320
+ } else if (dependency.package) {
321
+ imports.push(`import "../../${dependency.package}/${dependency.syntax}.proto";`);
322
+ } else if (dependency.syntax === 'google.protobuf.Any') {
323
+ imports.push(`import "google/protobuf/any.proto";`);
324
+ }
325
+ });
326
+ }
327
+
328
+ if (type.syntax === 'oneof') {
329
+ return (
330
+ `\toneof ${memberName} {\n` +
331
+ type.types
332
+ .map((subType) => {
333
+ return `\t\t${subType.syntax} ${subType.name} = ${index++}`;
334
+ })
335
+ .join(';\n') +
336
+ `;\n\t}`
337
+ );
338
+ } else {
339
+ return `\t${
340
+ options.optional && !(type.syntax.includes('<') || type.syntax.includes('repeated'))
341
+ ? 'optional '
342
+ : ''
343
+ }${type.syntax} ${memberName} = ${index++};`;
344
+ }
345
+ })
346
+ .filter((value) => value !== undefined);
347
+
348
+ return [
349
+ packageStr,
350
+ HEADER +
351
+ `package ${packageStr.replace('@', '').replace('/', '.')};\n` +
352
+ `syntax = "proto3";\n` +
353
+ imports
354
+ .filter((value, idx) => {
355
+ return imports.indexOf(value) === idx;
356
+ })
357
+ .join('\n') +
358
+ `\n` +
359
+ (dataTypesEnum ? dataTypesEnum : '') +
360
+ `\nmessage ${object.classType.name} {\n` +
361
+ (rootMeta.protobuf.generator.type
362
+ ? `\t${rootMeta.protobuf.generator.type.name}Type _type = 0;\n`
363
+ : '') +
364
+ members.join('\n') +
365
+ `\n` +
366
+ `}\n`,
367
+ ];
368
+ }
369
+ }
370
+
371
+ export type ProtobufPrimitive = 'string' | 'int32' | 'bool' | 'bytes';
372
+ export interface TypeMapping {
373
+ syntax: ProtobufPrimitive | string;
374
+ types?: TypeMapping[];
375
+ package?: string;
376
+ name?: string;
377
+ }
@@ -0,0 +1,130 @@
1
+ import { DataSerializer, DataSerializerUtils, ObjectMetadata, Serializable } from '@openhps/core';
2
+ import { ObjectGenerator } from './ObjectGenerator';
3
+ import * as fs from 'fs-extra';
4
+ import * as path from 'path';
5
+ import chalk from 'chalk';
6
+ import { COMMON } from './constants';
7
+ import { ProjectBuildOptions } from './types';
8
+
9
+ /**
10
+ * Project generator
11
+ */
12
+ export class ProjectGenerator extends DataSerializer {
13
+ private static _modules: Set<string> = new Set();
14
+ private static _packages: Set<string> = new Set();
15
+
16
+ static findModule(dir: string): string {
17
+ const packageFile = path.join(dir, 'package.json');
18
+ if (fs.existsSync(packageFile)) {
19
+ const packageJson = JSON.parse(fs.readFileSync(packageFile, { encoding: 'utf-8' }));
20
+ return packageJson.name;
21
+ } else {
22
+ const completeDir = dir.split(path.sep);
23
+ completeDir.pop();
24
+ return this.findModule(completeDir.join(path.sep));
25
+ }
26
+ }
27
+
28
+ static loadModules(objects: Array<Serializable<any>>, module: NodeModule = require.main) {
29
+ if (module === undefined) {
30
+ // Use cache instead
31
+ Object.values(require.cache).map((m) => this.loadModules(objects, m));
32
+ return;
33
+ }
34
+ this._modules.add(module.id);
35
+ Object.keys(module.exports).forEach((key) => {
36
+ const childModule = module.exports[key];
37
+ if (objects.includes(childModule)) {
38
+ childModule.prototype._module = this.findModule(path.dirname(require.resolve(module.id)));
39
+ this._packages.add(childModule.prototype._module);
40
+ }
41
+ });
42
+ module.children.forEach((module) => {
43
+ if (!this._modules.has(module.id)) {
44
+ this.loadModules(objects, module);
45
+ }
46
+ });
47
+ }
48
+
49
+ static getPackages(): string[] {
50
+ if (this._packages.size === 0) {
51
+ this.loadClasses();
52
+ }
53
+ return Array.from(this._packages.values());
54
+ }
55
+
56
+ static loadClasses(): Array<ObjectMetadata> {
57
+ const declarations: Array<ObjectMetadata> = [];
58
+ this.knownTypes.forEach((value) => {
59
+ const metadata = DataSerializerUtils.getOwnMetadata(value);
60
+ if (metadata) {
61
+ declarations.push(metadata);
62
+ }
63
+ });
64
+ this.loadModules(declarations.map((d) => d.classType));
65
+ return declarations;
66
+ }
67
+
68
+ static generateProtoMessages(options: ProjectBuildOptions): Promise<Map<string, [string, string]>> {
69
+ return new Promise((resolve) => {
70
+ const classes = new Map();
71
+ const metaData = this.loadClasses();
72
+
73
+ metaData.forEach((objectMetadata) => {
74
+ ObjectGenerator.processObject(objectMetadata, options);
75
+ });
76
+
77
+ metaData.forEach((objectMetadata) => {
78
+ if (options.logLevel > 2) {
79
+ console.log(
80
+ chalk.italic(
81
+ `Generating ${objectMetadata.classType.name}`,
82
+ objectMetadata.classType.prototype._module
83
+ ? `of module ${objectMetadata.classType.prototype._module}`
84
+ : '',
85
+ ),
86
+ );
87
+ }
88
+ const javaClass = ObjectGenerator.createProtoMessage(objectMetadata, options);
89
+ classes.set(objectMetadata.classType.name, javaClass);
90
+ });
91
+ resolve(classes);
92
+ });
93
+ }
94
+
95
+ static buildProject(directory: string, options: ProjectBuildOptions = {}): Promise<number> {
96
+ return new Promise((resolve, reject) => {
97
+ // Prepare directories
98
+ if (fs.existsSync(directory)) {
99
+ fs.rmSync(directory, { recursive: true });
100
+ }
101
+ fs.mkdirSync(directory, { recursive: true });
102
+
103
+ // Get all class sources
104
+ if (options.logLevel > 0) {
105
+ console.log('Generating protocol buffer messages ...');
106
+ console.log('Use of any types = ', options.useAnyType);
107
+ }
108
+ ProjectGenerator.generateProtoMessages(options)
109
+ .then((classes) => {
110
+ if (options.logLevel > 0) console.log('Saving generating protocol buffer messages ...');
111
+ classes.forEach((value, key) => {
112
+ const packageDir = path.join(directory, ...value[0].split('.'));
113
+ if (!fs.existsSync(packageDir)) {
114
+ fs.mkdirsSync(packageDir);
115
+ }
116
+ fs.writeFileSync(path.join(packageDir, key + '.proto'), value[1], {
117
+ encoding: 'utf-8',
118
+ });
119
+ });
120
+
121
+ fs.writeFileSync(path.join(directory, 'common.proto'), COMMON, {
122
+ encoding: 'utf-8',
123
+ });
124
+
125
+ resolve(classes.size);
126
+ })
127
+ .catch(reject);
128
+ });
129
+ }
130
+ }
@@ -0,0 +1,49 @@
1
+ import { Constructor, NumberType, ObjectMemberMetadata } from '@openhps/core';
2
+
3
+ export abstract class ProtobufGenerator<T> {
4
+ protected options: ProtobufGeneratorOptions;
5
+ /**
6
+ * Logger
7
+ */
8
+ logger: (level: string, message: any) => void = () => {};
9
+
10
+ constructor(options?: ProtobufGeneratorOptions) {
11
+ this.options = options;
12
+ }
13
+
14
+ protected static getNumberType(numberType: NumberType): string {
15
+ switch (numberType) {
16
+ case NumberType.DECIMAL:
17
+ case NumberType.DOUBLE:
18
+ return 'double';
19
+ case NumberType.FLOAT:
20
+ return 'float';
21
+ case NumberType.LONG:
22
+ return 'int64';
23
+ case NumberType.INTEGER:
24
+ case NumberType.SHORT:
25
+ return 'int32';
26
+ default:
27
+ return 'string';
28
+ }
29
+ }
30
+
31
+ abstract processObject(object: Constructor<T>, metaData: ObjectMemberMetadata): Promise<void>;
32
+
33
+ abstract generate(object: Constructor<T>, metaData: ObjectMemberMetadata): Promise<ProtobufMessage>;
34
+ }
35
+
36
+ export interface ProtobufGeneratorOptions {}
37
+
38
+ export interface ProtobufMessage {
39
+ body: string;
40
+ namespace: string;
41
+ }
42
+
43
+ export type ProtobufPrimitive = 'string' | 'int32' | 'bool' | 'bytes';
44
+ export interface TypeMapping {
45
+ syntax: ProtobufPrimitive | string;
46
+ types?: TypeMapping[];
47
+ package?: string;
48
+ name?: string;
49
+ }
@@ -0,0 +1,11 @@
1
+ import { Constructor, ObjectMemberMetadata, Service } from '@openhps/core';
2
+ import { ProtobufGenerator, ProtobufMessage } from './ProtobufGenerator';
3
+
4
+ export class ServiceGenerator extends ProtobufGenerator<Service> {
5
+ processObject(object: Constructor<Service>, metaData: ObjectMemberMetadata): Promise<void> { // eslint-disable-line
6
+ throw new Error('Method not implemented.');
7
+ }
8
+ generate(object: Constructor<Service>, metaData: ObjectMemberMetadata): Promise<ProtobufMessage> { // eslint-disable-line
9
+ throw new Error('Method not implemented.');
10
+ }
11
+ }
@@ -0,0 +1,19 @@
1
+ export const HEADER = `/**
2
+ * OpenHPS Protocol Buffer
3
+ * This file was automatically generated with @openhps/protobuf
4
+ *
5
+ * (c) 2019-2023 Maxim Van de Wynckel & Vrije Universiteit Brussel
6
+ **/\n
7
+ `;
8
+
9
+ export const COMMON =
10
+ HEADER +
11
+ `syntax = "proto3";` +
12
+ `\n\nextend google.protobuf.EnumValueOptions {\n` +
13
+ `\toptional string className = 1;\n` +
14
+ `\toptional string packageName = 2;\n` +
15
+ `}\n`;
16
+ // `\nextend google.protobuf.MessageOptions {\n` +
17
+ // `\toptional string className = 1001;\n` +
18
+ // `\toptional string packageName = 1002;\n` +
19
+ // `}\n`;
@@ -0,0 +1,3 @@
1
+ export * from './ObjectGenerator';
2
+ export * from './ProjectGenerator';
3
+ export * from '../decorators';
@@ -0,0 +1,4 @@
1
+ export interface ProjectBuildOptions {
2
+ logLevel?: number;
3
+ useAnyType?: boolean;
4
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './ProtobufSerializer';
2
+ export * from './decorators';