@bufbuild/protobuf 2.2.2 → 2.2.3

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.
@@ -1,9 +1,41 @@
1
1
  import type { MessageShape } from "./types.js";
2
- import type { DescMessage } from "./descriptors.js";
2
+ import { type DescMessage } from "./descriptors.js";
3
+ import type { Registry } from "./registry.js";
4
+ interface EqualsOptions {
5
+ /**
6
+ * A registry to look up extensions, and messages packed in Any.
7
+ *
8
+ * @private Experimental API, does not follow semantic versioning.
9
+ */
10
+ registry: Registry;
11
+ /**
12
+ * Unpack google.protobuf.Any before comparing.
13
+ * If a type is not in the registry, comparison falls back to comparing the
14
+ * fields of Any.
15
+ *
16
+ * @private Experimental API, does not follow semantic versioning.
17
+ */
18
+ unpackAny?: boolean;
19
+ /**
20
+ * Consider extensions when comparing.
21
+ *
22
+ * @private Experimental API, does not follow semantic versioning.
23
+ */
24
+ extensions?: boolean;
25
+ /**
26
+ * Consider unknown fields when comparing.
27
+ * The registry is used to distinguish between extensions, and unknown fields
28
+ * caused by schema changes.
29
+ *
30
+ * @private Experimental API, does not follow semantic versioning.
31
+ */
32
+ unknown?: boolean;
33
+ }
3
34
  /**
4
35
  * Compare two messages of the same type.
5
36
  *
6
37
  * Note that this function disregards extensions and unknown fields, and that
7
38
  * NaN is not equal NaN, following the IEEE standard.
8
39
  */
9
- export declare function equals<Desc extends DescMessage>(schema: Desc, a: MessageShape<Desc>, b: MessageShape<Desc>): boolean;
40
+ export declare function equals<Desc extends DescMessage>(schema: Desc, a: MessageShape<Desc>, b: MessageShape<Desc>, options?: EqualsOptions): boolean;
41
+ export {};
@@ -16,30 +16,43 @@ Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.equals = equals;
17
17
  const scalar_js_1 = require("./reflect/scalar.js");
18
18
  const reflect_js_1 = require("./reflect/reflect.js");
19
+ const descriptors_js_1 = require("./descriptors.js");
20
+ const index_js_1 = require("./wkt/index.js");
21
+ const extensions_js_1 = require("./extensions.js");
19
22
  /**
20
23
  * Compare two messages of the same type.
21
24
  *
22
25
  * Note that this function disregards extensions and unknown fields, and that
23
26
  * NaN is not equal NaN, following the IEEE standard.
24
27
  */
25
- function equals(schema, a, b) {
28
+ function equals(schema, a, b, options) {
26
29
  if (a.$typeName != schema.typeName || b.$typeName != schema.typeName) {
27
30
  return false;
28
31
  }
29
32
  if (a === b) {
30
33
  return true;
31
34
  }
32
- return reflectEquals((0, reflect_js_1.reflect)(schema, a), (0, reflect_js_1.reflect)(schema, b));
35
+ return reflectEquals((0, reflect_js_1.reflect)(schema, a), (0, reflect_js_1.reflect)(schema, b), options);
33
36
  }
34
- function reflectEquals(a, b) {
37
+ function reflectEquals(a, b, opts) {
38
+ if (a.desc.typeName === "google.protobuf.Any" && (opts === null || opts === void 0 ? void 0 : opts.unpackAny) == true) {
39
+ return anyUnpackedEquals(a.message, b.message, opts);
40
+ }
35
41
  for (const f of a.fields) {
36
- if (!fieldEquals(f, a, b)) {
42
+ if (!fieldEquals(f, a, b, opts)) {
37
43
  return false;
38
44
  }
39
45
  }
46
+ if ((opts === null || opts === void 0 ? void 0 : opts.unknown) == true && !unknownEquals(a, b, opts.registry)) {
47
+ return false;
48
+ }
49
+ if ((opts === null || opts === void 0 ? void 0 : opts.extensions) == true && !extensionsEquals(a, b, opts)) {
50
+ return false;
51
+ }
40
52
  return true;
41
53
  }
42
- function fieldEquals(f, a, b) {
54
+ // TODO(tstamm) add an option to consider NaN equal to NaN?
55
+ function fieldEquals(f, a, b, opts) {
43
56
  if (!a.isSet(f) && !b.isSet(f)) {
44
57
  return true;
45
58
  }
@@ -52,25 +65,26 @@ function fieldEquals(f, a, b) {
52
65
  case "enum":
53
66
  return a.get(f) === b.get(f);
54
67
  case "message":
55
- return reflectEquals(a.get(f), b.get(f));
68
+ return reflectEquals(a.get(f), b.get(f), opts);
56
69
  case "map": {
57
- const ma = a.get(f);
58
- const mb = b.get(f);
59
- const keysA = [];
60
- for (const k of ma.keys()) {
61
- if (!mb.has(k)) {
70
+ // TODO(tstamm) can't we compare sizes first?
71
+ const mapA = a.get(f);
72
+ const mapB = b.get(f);
73
+ const keys = [];
74
+ for (const k of mapA.keys()) {
75
+ if (!mapB.has(k)) {
62
76
  return false;
63
77
  }
64
- keysA.push(k);
78
+ keys.push(k);
65
79
  }
66
- for (const k of mb.keys()) {
67
- if (!ma.has(k)) {
80
+ for (const k of mapB.keys()) {
81
+ if (!mapA.has(k)) {
68
82
  return false;
69
83
  }
70
84
  }
71
- for (const key of keysA) {
72
- const va = ma.get(key);
73
- const vb = mb.get(key);
85
+ for (const key of keys) {
86
+ const va = mapA.get(key);
87
+ const vb = mapB.get(key);
74
88
  if (va === vb) {
75
89
  continue;
76
90
  }
@@ -78,7 +92,7 @@ function fieldEquals(f, a, b) {
78
92
  case "enum":
79
93
  return false;
80
94
  case "message":
81
- if (!reflectEquals(va, vb)) {
95
+ if (!reflectEquals(va, vb, opts)) {
82
96
  return false;
83
97
  }
84
98
  break;
@@ -92,14 +106,14 @@ function fieldEquals(f, a, b) {
92
106
  break;
93
107
  }
94
108
  case "list": {
95
- const la = a.get(f);
96
- const lb = b.get(f);
97
- if (la.size != lb.size) {
109
+ const listA = a.get(f);
110
+ const listB = b.get(f);
111
+ if (listA.size != listB.size) {
98
112
  return false;
99
113
  }
100
- for (let i = 0; i < la.size; i++) {
101
- const va = la.get(i);
102
- const vb = lb.get(i);
114
+ for (let i = 0; i < listA.size; i++) {
115
+ const va = listA.get(i);
116
+ const vb = listB.get(i);
103
117
  if (va === vb) {
104
118
  continue;
105
119
  }
@@ -107,7 +121,7 @@ function fieldEquals(f, a, b) {
107
121
  case "enum":
108
122
  return false;
109
123
  case "message":
110
- if (!reflectEquals(va, vb)) {
124
+ if (!reflectEquals(va, vb, opts)) {
111
125
  return false;
112
126
  }
113
127
  break;
@@ -123,3 +137,67 @@ function fieldEquals(f, a, b) {
123
137
  }
124
138
  return true;
125
139
  }
140
+ function anyUnpackedEquals(a, b, opts) {
141
+ if (a.typeUrl !== b.typeUrl) {
142
+ return false;
143
+ }
144
+ const unpackedA = (0, index_js_1.anyUnpack)(a, opts.registry);
145
+ const unpackedB = (0, index_js_1.anyUnpack)(b, opts.registry);
146
+ if (unpackedA && unpackedB) {
147
+ const schema = opts.registry.getMessage(unpackedA.$typeName);
148
+ if (schema) {
149
+ return equals(schema, unpackedA, unpackedB, opts);
150
+ }
151
+ }
152
+ return (0, scalar_js_1.scalarEquals)(descriptors_js_1.ScalarType.BYTES, a.value, b.value);
153
+ }
154
+ function unknownEquals(a, b, registry) {
155
+ function getTrulyUnknown(msg, registry) {
156
+ var _a;
157
+ const u = (_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : [];
158
+ return registry
159
+ ? u.filter((uf) => !registry.getExtensionFor(msg.desc, uf.no))
160
+ : u;
161
+ }
162
+ const unknownA = getTrulyUnknown(a, registry);
163
+ const unknownB = getTrulyUnknown(b, registry);
164
+ if (unknownA.length != unknownB.length) {
165
+ return false;
166
+ }
167
+ for (let i = 0; i < unknownA.length; i++) {
168
+ const a = unknownA[i], b = unknownB[i];
169
+ if (a.no != b.no) {
170
+ return false;
171
+ }
172
+ if (a.wireType != b.wireType) {
173
+ return false;
174
+ }
175
+ if (!(0, scalar_js_1.scalarEquals)(descriptors_js_1.ScalarType.BYTES, a.data, b.data)) {
176
+ return false;
177
+ }
178
+ }
179
+ return true;
180
+ }
181
+ function extensionsEquals(a, b, opts) {
182
+ function getSetExtensions(msg, registry) {
183
+ var _a;
184
+ return ((_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : [])
185
+ .map((uf) => registry.getExtensionFor(msg.desc, uf.no))
186
+ .filter((e) => e != undefined)
187
+ .filter((e, index, arr) => arr.indexOf(e) === index);
188
+ }
189
+ const extensionsA = getSetExtensions(a, opts.registry);
190
+ const extensionsB = getSetExtensions(b, opts.registry);
191
+ if (extensionsA.length != extensionsB.length ||
192
+ extensionsA.some((e) => !extensionsB.includes(e))) {
193
+ return false;
194
+ }
195
+ for (const extension of extensionsA) {
196
+ const [containerA, field] = (0, extensions_js_1.createExtensionContainer)(extension, (0, extensions_js_1.getExtension)(a.message, extension));
197
+ const [containerB] = (0, extensions_js_1.createExtensionContainer)(extension, (0, extensions_js_1.getExtension)(b.message, extension));
198
+ if (!fieldEquals(field, containerA, containerB, opts)) {
199
+ return false;
200
+ }
201
+ }
202
+ return true;
203
+ }
@@ -23,7 +23,7 @@ var __await = (this && this.__await) || function (v) { return this instanceof __
23
23
  var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
24
24
  if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
25
25
  var g = generator.apply(thisArg, _arguments || []), i, q = [];
26
- return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
26
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
27
27
  function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
28
28
  function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
29
29
  function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
@@ -48,7 +48,7 @@ function anyUnpack(any, registryOrMessageDesc) {
48
48
  const desc = registryOrMessageDesc.kind == "message"
49
49
  ? registryOrMessageDesc
50
50
  : registryOrMessageDesc.getMessage(typeUrlToName(any.typeUrl));
51
- if (!desc) {
51
+ if (!desc || !anyIs(any, desc)) {
52
52
  return undefined;
53
53
  }
54
54
  return (0, from_binary_js_1.fromBinary)(desc, any.value);
@@ -57,7 +57,7 @@ function anyUnpack(any, registryOrMessageDesc) {
57
57
  * Same as anyUnpack but unpacks into the target message.
58
58
  */
59
59
  function anyUnpackTo(any, schema, message) {
60
- if (any.typeUrl === "") {
60
+ if (!anyIs(any, schema)) {
61
61
  return undefined;
62
62
  }
63
63
  return (0, from_binary_js_1.mergeFromBinary)(schema, message, any.value);
@@ -1,9 +1,41 @@
1
1
  import type { MessageShape } from "./types.js";
2
- import type { DescMessage } from "./descriptors.js";
2
+ import { type DescMessage } from "./descriptors.js";
3
+ import type { Registry } from "./registry.js";
4
+ interface EqualsOptions {
5
+ /**
6
+ * A registry to look up extensions, and messages packed in Any.
7
+ *
8
+ * @private Experimental API, does not follow semantic versioning.
9
+ */
10
+ registry: Registry;
11
+ /**
12
+ * Unpack google.protobuf.Any before comparing.
13
+ * If a type is not in the registry, comparison falls back to comparing the
14
+ * fields of Any.
15
+ *
16
+ * @private Experimental API, does not follow semantic versioning.
17
+ */
18
+ unpackAny?: boolean;
19
+ /**
20
+ * Consider extensions when comparing.
21
+ *
22
+ * @private Experimental API, does not follow semantic versioning.
23
+ */
24
+ extensions?: boolean;
25
+ /**
26
+ * Consider unknown fields when comparing.
27
+ * The registry is used to distinguish between extensions, and unknown fields
28
+ * caused by schema changes.
29
+ *
30
+ * @private Experimental API, does not follow semantic versioning.
31
+ */
32
+ unknown?: boolean;
33
+ }
3
34
  /**
4
35
  * Compare two messages of the same type.
5
36
  *
6
37
  * Note that this function disregards extensions and unknown fields, and that
7
38
  * NaN is not equal NaN, following the IEEE standard.
8
39
  */
9
- export declare function equals<Desc extends DescMessage>(schema: Desc, a: MessageShape<Desc>, b: MessageShape<Desc>): boolean;
40
+ export declare function equals<Desc extends DescMessage>(schema: Desc, a: MessageShape<Desc>, b: MessageShape<Desc>, options?: EqualsOptions): boolean;
41
+ export {};
@@ -13,30 +13,43 @@
13
13
  // limitations under the License.
14
14
  import { scalarEquals } from "./reflect/scalar.js";
15
15
  import { reflect } from "./reflect/reflect.js";
16
+ import { ScalarType, } from "./descriptors.js";
17
+ import { anyUnpack } from "./wkt/index.js";
18
+ import { createExtensionContainer, getExtension } from "./extensions.js";
16
19
  /**
17
20
  * Compare two messages of the same type.
18
21
  *
19
22
  * Note that this function disregards extensions and unknown fields, and that
20
23
  * NaN is not equal NaN, following the IEEE standard.
21
24
  */
22
- export function equals(schema, a, b) {
25
+ export function equals(schema, a, b, options) {
23
26
  if (a.$typeName != schema.typeName || b.$typeName != schema.typeName) {
24
27
  return false;
25
28
  }
26
29
  if (a === b) {
27
30
  return true;
28
31
  }
29
- return reflectEquals(reflect(schema, a), reflect(schema, b));
32
+ return reflectEquals(reflect(schema, a), reflect(schema, b), options);
30
33
  }
31
- function reflectEquals(a, b) {
34
+ function reflectEquals(a, b, opts) {
35
+ if (a.desc.typeName === "google.protobuf.Any" && (opts === null || opts === void 0 ? void 0 : opts.unpackAny) == true) {
36
+ return anyUnpackedEquals(a.message, b.message, opts);
37
+ }
32
38
  for (const f of a.fields) {
33
- if (!fieldEquals(f, a, b)) {
39
+ if (!fieldEquals(f, a, b, opts)) {
34
40
  return false;
35
41
  }
36
42
  }
43
+ if ((opts === null || opts === void 0 ? void 0 : opts.unknown) == true && !unknownEquals(a, b, opts.registry)) {
44
+ return false;
45
+ }
46
+ if ((opts === null || opts === void 0 ? void 0 : opts.extensions) == true && !extensionsEquals(a, b, opts)) {
47
+ return false;
48
+ }
37
49
  return true;
38
50
  }
39
- function fieldEquals(f, a, b) {
51
+ // TODO(tstamm) add an option to consider NaN equal to NaN?
52
+ function fieldEquals(f, a, b, opts) {
40
53
  if (!a.isSet(f) && !b.isSet(f)) {
41
54
  return true;
42
55
  }
@@ -49,25 +62,26 @@ function fieldEquals(f, a, b) {
49
62
  case "enum":
50
63
  return a.get(f) === b.get(f);
51
64
  case "message":
52
- return reflectEquals(a.get(f), b.get(f));
65
+ return reflectEquals(a.get(f), b.get(f), opts);
53
66
  case "map": {
54
- const ma = a.get(f);
55
- const mb = b.get(f);
56
- const keysA = [];
57
- for (const k of ma.keys()) {
58
- if (!mb.has(k)) {
67
+ // TODO(tstamm) can't we compare sizes first?
68
+ const mapA = a.get(f);
69
+ const mapB = b.get(f);
70
+ const keys = [];
71
+ for (const k of mapA.keys()) {
72
+ if (!mapB.has(k)) {
59
73
  return false;
60
74
  }
61
- keysA.push(k);
75
+ keys.push(k);
62
76
  }
63
- for (const k of mb.keys()) {
64
- if (!ma.has(k)) {
77
+ for (const k of mapB.keys()) {
78
+ if (!mapA.has(k)) {
65
79
  return false;
66
80
  }
67
81
  }
68
- for (const key of keysA) {
69
- const va = ma.get(key);
70
- const vb = mb.get(key);
82
+ for (const key of keys) {
83
+ const va = mapA.get(key);
84
+ const vb = mapB.get(key);
71
85
  if (va === vb) {
72
86
  continue;
73
87
  }
@@ -75,7 +89,7 @@ function fieldEquals(f, a, b) {
75
89
  case "enum":
76
90
  return false;
77
91
  case "message":
78
- if (!reflectEquals(va, vb)) {
92
+ if (!reflectEquals(va, vb, opts)) {
79
93
  return false;
80
94
  }
81
95
  break;
@@ -89,14 +103,14 @@ function fieldEquals(f, a, b) {
89
103
  break;
90
104
  }
91
105
  case "list": {
92
- const la = a.get(f);
93
- const lb = b.get(f);
94
- if (la.size != lb.size) {
106
+ const listA = a.get(f);
107
+ const listB = b.get(f);
108
+ if (listA.size != listB.size) {
95
109
  return false;
96
110
  }
97
- for (let i = 0; i < la.size; i++) {
98
- const va = la.get(i);
99
- const vb = lb.get(i);
111
+ for (let i = 0; i < listA.size; i++) {
112
+ const va = listA.get(i);
113
+ const vb = listB.get(i);
100
114
  if (va === vb) {
101
115
  continue;
102
116
  }
@@ -104,7 +118,7 @@ function fieldEquals(f, a, b) {
104
118
  case "enum":
105
119
  return false;
106
120
  case "message":
107
- if (!reflectEquals(va, vb)) {
121
+ if (!reflectEquals(va, vb, opts)) {
108
122
  return false;
109
123
  }
110
124
  break;
@@ -120,3 +134,67 @@ function fieldEquals(f, a, b) {
120
134
  }
121
135
  return true;
122
136
  }
137
+ function anyUnpackedEquals(a, b, opts) {
138
+ if (a.typeUrl !== b.typeUrl) {
139
+ return false;
140
+ }
141
+ const unpackedA = anyUnpack(a, opts.registry);
142
+ const unpackedB = anyUnpack(b, opts.registry);
143
+ if (unpackedA && unpackedB) {
144
+ const schema = opts.registry.getMessage(unpackedA.$typeName);
145
+ if (schema) {
146
+ return equals(schema, unpackedA, unpackedB, opts);
147
+ }
148
+ }
149
+ return scalarEquals(ScalarType.BYTES, a.value, b.value);
150
+ }
151
+ function unknownEquals(a, b, registry) {
152
+ function getTrulyUnknown(msg, registry) {
153
+ var _a;
154
+ const u = (_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : [];
155
+ return registry
156
+ ? u.filter((uf) => !registry.getExtensionFor(msg.desc, uf.no))
157
+ : u;
158
+ }
159
+ const unknownA = getTrulyUnknown(a, registry);
160
+ const unknownB = getTrulyUnknown(b, registry);
161
+ if (unknownA.length != unknownB.length) {
162
+ return false;
163
+ }
164
+ for (let i = 0; i < unknownA.length; i++) {
165
+ const a = unknownA[i], b = unknownB[i];
166
+ if (a.no != b.no) {
167
+ return false;
168
+ }
169
+ if (a.wireType != b.wireType) {
170
+ return false;
171
+ }
172
+ if (!scalarEquals(ScalarType.BYTES, a.data, b.data)) {
173
+ return false;
174
+ }
175
+ }
176
+ return true;
177
+ }
178
+ function extensionsEquals(a, b, opts) {
179
+ function getSetExtensions(msg, registry) {
180
+ var _a;
181
+ return ((_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : [])
182
+ .map((uf) => registry.getExtensionFor(msg.desc, uf.no))
183
+ .filter((e) => e != undefined)
184
+ .filter((e, index, arr) => arr.indexOf(e) === index);
185
+ }
186
+ const extensionsA = getSetExtensions(a, opts.registry);
187
+ const extensionsB = getSetExtensions(b, opts.registry);
188
+ if (extensionsA.length != extensionsB.length ||
189
+ extensionsA.some((e) => !extensionsB.includes(e))) {
190
+ return false;
191
+ }
192
+ for (const extension of extensionsA) {
193
+ const [containerA, field] = createExtensionContainer(extension, getExtension(a.message, extension));
194
+ const [containerB] = createExtensionContainer(extension, getExtension(b.message, extension));
195
+ if (!fieldEquals(field, containerA, containerB, opts)) {
196
+ return false;
197
+ }
198
+ }
199
+ return true;
200
+ }
@@ -22,7 +22,7 @@ var __await = (this && this.__await) || function (v) { return this instanceof __
22
22
  var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
23
23
  if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
24
24
  var g = generator.apply(thisArg, _arguments || []), i, q = [];
25
- return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
25
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
26
26
  function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
27
27
  function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
28
28
  function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
@@ -42,7 +42,7 @@ export function anyUnpack(any, registryOrMessageDesc) {
42
42
  const desc = registryOrMessageDesc.kind == "message"
43
43
  ? registryOrMessageDesc
44
44
  : registryOrMessageDesc.getMessage(typeUrlToName(any.typeUrl));
45
- if (!desc) {
45
+ if (!desc || !anyIs(any, desc)) {
46
46
  return undefined;
47
47
  }
48
48
  return fromBinary(desc, any.value);
@@ -51,7 +51,7 @@ export function anyUnpack(any, registryOrMessageDesc) {
51
51
  * Same as anyUnpack but unpacks into the target message.
52
52
  */
53
53
  export function anyUnpackTo(any, schema, message) {
54
- if (any.typeUrl === "") {
54
+ if (!anyIs(any, schema)) {
55
55
  return undefined;
56
56
  }
57
57
  return mergeFromBinary(schema, message, any.value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bufbuild/protobuf",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "license": "(Apache-2.0 AND BSD-3-Clause)",
5
5
  "description": "A complete implementation of Protocol Buffers in TypeScript, suitable for web browsers and Node.js.",
6
6
  "keywords": [