@aptre/protobuf-es-lite 0.4.1 → 0.4.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.
package/dist/binary.js CHANGED
@@ -22,9 +22,10 @@ function makeWriteOptions(options) {
22
22
  }
23
23
  function readField(target, // eslint-disable-line @typescript-eslint/no-explicit-any -- `any` is the best choice for dynamic access
24
24
  reader, field, wireType, options) {
25
- let { repeated, localName } = field;
25
+ const { repeated } = field;
26
+ let { localName } = field;
26
27
  if (field.oneof) {
27
- var oneofMsg = target[field.oneof.localName];
28
+ let oneofMsg = target[field.oneof.localName];
28
29
  if (!oneofMsg) {
29
30
  oneofMsg = target[field.oneof.localName] = Object.create(null);
30
31
  }
@@ -37,7 +38,7 @@ reader, field, wireType, options) {
37
38
  }
38
39
  switch (field.kind) {
39
40
  case "scalar":
40
- case "enum":
41
+ case "enum": {
41
42
  const scalarType = field.kind == "enum" ? ScalarType.INT32 : field.T;
42
43
  let read = readScalar;
43
44
  // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison -- acceptable since it's covered by tests
@@ -45,7 +46,7 @@ reader, field, wireType, options) {
45
46
  read = readScalarLTString;
46
47
  }
47
48
  if (repeated) {
48
- var tgtArr = target[localName];
49
+ let tgtArr = target[localName];
49
50
  if (!Array.isArray(tgtArr)) {
50
51
  tgtArr = target[localName] = [];
51
52
  }
@@ -53,7 +54,7 @@ reader, field, wireType, options) {
53
54
  scalarType != ScalarType.STRING &&
54
55
  scalarType != ScalarType.BYTES;
55
56
  if (isPacked) {
56
- let e = reader.uint32() + reader.pos;
57
+ const e = reader.uint32() + reader.pos;
57
58
  while (reader.pos < e) {
58
59
  tgtArr.push(read(reader, scalarType));
59
60
  }
@@ -66,11 +67,12 @@ reader, field, wireType, options) {
66
67
  target[localName] = read(reader, scalarType);
67
68
  }
68
69
  break;
69
- case "message":
70
+ }
71
+ case "message": {
70
72
  const fieldT = field.T;
71
73
  const messageType = fieldT instanceof Function ? fieldT() : fieldT;
72
74
  if (repeated) {
73
- var tgtArr = target[localName];
75
+ let tgtArr = target[localName];
74
76
  if (!Array.isArray(tgtArr)) {
75
77
  tgtArr = target[localName] = [];
76
78
  }
@@ -80,14 +82,16 @@ reader, field, wireType, options) {
80
82
  target[localName] = unwrapField(messageType.fieldWrapper, readMessageField(reader, Object.create(null), messageType.fields, options, field));
81
83
  }
82
84
  break;
83
- case "map":
84
- let [mapKey, mapVal] = readMapEntry(field, reader, options);
85
+ }
86
+ case "map": {
87
+ const [mapKey, mapVal] = readMapEntry(field, reader, options);
85
88
  if (typeof target[localName] !== "object") {
86
89
  target[localName] = Object.create(null);
87
90
  }
88
91
  // safe to assume presence of map object, oneof cannot contain repeated values
89
92
  target[localName][mapKey] = mapVal;
90
93
  break;
94
+ }
91
95
  }
92
96
  }
93
97
  // Read a map field, expecting key field = 1, value field = 2
@@ -108,10 +112,10 @@ function readMapEntry(field, reader, options) {
108
112
  case "enum":
109
113
  val = reader.int32();
110
114
  break;
111
- case "message":
112
- const messageType = resolveMessageType(field.V.T);
113
- val = readMessageField(reader, Object.create(null), messageType.fields, options, undefined);
115
+ case "message": {
116
+ val = readMessageField(reader, Object.create(null), resolveMessageType(field.V.T).fields, options, undefined);
114
117
  break;
118
+ }
115
119
  }
116
120
  break;
117
121
  }
@@ -119,8 +123,8 @@ function readMapEntry(field, reader, options) {
119
123
  if (key === undefined) {
120
124
  key = scalarZeroValue(field.K, LongType.BIGINT);
121
125
  }
122
- if (typeof key != "string" && typeof key != "number") {
123
- key = key.toString();
126
+ if (typeof key !== "string" && typeof key !== "number") {
127
+ key = key?.toString() ?? "";
124
128
  }
125
129
  if (val === undefined) {
126
130
  const fieldKind = field.V.kind;
@@ -170,6 +174,10 @@ function readScalar(reader, type) {
170
174
  return reader.uint32();
171
175
  case ScalarType.SINT32:
172
176
  return reader.sint32();
177
+ case ScalarType.DATE:
178
+ throw new Error("cannot read a date with readScalar");
179
+ default:
180
+ throw new Error("unknown scalar type");
173
181
  }
174
182
  }
175
183
  // Read a scalar value, but return 64 bit integral types (int64, uint64,
@@ -234,8 +242,8 @@ function writeField(field, value, writer, options) {
234
242
  const repeated = field.repeated;
235
243
  switch (field.kind) {
236
244
  case "scalar":
237
- case "enum":
238
- let scalarType = field.kind == "enum" ? ScalarType.INT32 : field.T;
245
+ case "enum": {
246
+ const scalarType = field.kind == "enum" ? ScalarType.INT32 : field.T;
239
247
  if (repeated) {
240
248
  assert(Array.isArray(value));
241
249
  if (field.packed) {
@@ -251,6 +259,7 @@ function writeField(field, value, writer, options) {
251
259
  writeScalar(writer, scalarType, field.no, value);
252
260
  }
253
261
  break;
262
+ }
254
263
  case "message":
255
264
  if (repeated) {
256
265
  assert(Array.isArray(value));
@@ -296,7 +305,7 @@ function writeMessageField(writer, options, field, value) {
296
305
  }
297
306
  function writeScalar(writer, type, fieldNo, value) {
298
307
  assert(value !== undefined);
299
- let [wireType, method] = scalarTypeInfo(type);
308
+ const [wireType, method] = scalarTypeInfo(type);
300
309
  writer.tag(fieldNo, wireType)[method](value);
301
310
  }
302
311
  function writePacked(writer, type, fieldNo, value) {
@@ -304,7 +313,7 @@ function writePacked(writer, type, fieldNo, value) {
304
313
  return;
305
314
  }
306
315
  writer.tag(fieldNo, WireType.LengthDelimited).fork();
307
- let [, method] = scalarTypeInfo(type);
316
+ const [, method] = scalarTypeInfo(type);
308
317
  for (let i = 0; i < value.length; i++) {
309
318
  writer[method](value[i]);
310
319
  }
@@ -373,13 +382,14 @@ function writeMapEntry(writer, options, field, key, value) {
373
382
  case "enum":
374
383
  writeScalar(writer, ScalarType.INT32, 2, value);
375
384
  break;
376
- case "message":
385
+ case "message": {
377
386
  assert(value !== undefined);
378
387
  const messageType = resolveMessageType(field.V.T);
379
388
  writer
380
389
  .tag(2, WireType.LengthDelimited)
381
390
  .bytes(messageType.toBinary(value, options));
382
391
  break;
392
+ }
383
393
  }
384
394
  writer.join();
385
395
  }
@@ -1,5 +1,5 @@
1
1
  import { localName } from "./names.js";
2
- import { getUnwrappedFieldType } from "./field-wrapper.js";
2
+ import { getUnwrappedFieldType, getUnwrappedMessageType } from "./field-wrapper.js";
3
3
  import { scalarZeroValue } from "./scalar.js";
4
4
  type RuntimeSymbolInfo = {
5
5
  typeOnly: boolean;
@@ -11,6 +11,7 @@ export declare const codegenInfo: {
11
11
  readonly packageName: "@aptre/protobuf-es-lite";
12
12
  readonly localName: typeof localName;
13
13
  readonly getUnwrappedFieldType: typeof getUnwrappedFieldType;
14
+ readonly getUnwrappedMessageType: typeof getUnwrappedMessageType;
14
15
  readonly scalarZeroValue: typeof scalarZeroValue;
15
16
  readonly safeIdentifier: (name: string) => string;
16
17
  readonly safeObjectProperty: (name: string) => string;
@@ -13,7 +13,7 @@
13
13
  // See the License for the specific language governing permissions and
14
14
  // limitations under the License.
15
15
  import { localName, safeIdentifier, safeObjectProperty } from "./names.js";
16
- import { getUnwrappedFieldType } from "./field-wrapper.js";
16
+ import { getUnwrappedFieldType, getUnwrappedMessageType, } from "./field-wrapper.js";
17
17
  import { scalarZeroValue } from "./scalar.js";
18
18
  export const packageName = "@aptre/protobuf-es-lite";
19
19
  const symbolInfo = (typeOnly, privateImportPath) => ({
@@ -55,6 +55,7 @@ export const codegenInfo = {
55
55
  packageName,
56
56
  localName,
57
57
  getUnwrappedFieldType,
58
+ getUnwrappedMessageType,
58
59
  scalarZeroValue,
59
60
  safeIdentifier,
60
61
  safeObjectProperty,
@@ -1,4 +1,4 @@
1
- import { DescExtension, DescField } from "./descriptor-set.js";
1
+ import { DescExtension, DescField, DescMessage } from "./descriptor-set.js";
2
2
  import { ScalarType } from "./scalar.js";
3
3
  /**
4
4
  * A field wrapper unwraps a message to a primitive value that is more
@@ -31,3 +31,8 @@ export declare function unwrapField<T, U = T>(fieldWrapper: FieldWrapper<T> | un
31
31
  * the primitive type it wraps.
32
32
  */
33
33
  export declare function getUnwrappedFieldType(field: DescField | DescExtension): ScalarType | undefined;
34
+ /**
35
+ * If the given field uses one of the well-known wrapper types, return
36
+ * the primitive type it wraps.
37
+ */
38
+ export declare function getUnwrappedMessageType(msg: DescMessage): ScalarType | undefined;
@@ -14,7 +14,7 @@ export function wrapField(fieldWrapper, value) {
14
14
  * message. This function is idempotent.
15
15
  */
16
16
  export function unwrapField(fieldWrapper, value) {
17
- return !!fieldWrapper ? fieldWrapper.unwrapField(value) : value;
17
+ return fieldWrapper ? fieldWrapper.unwrapField(value) : value;
18
18
  }
19
19
  /**
20
20
  * If the given field uses one of the well-known wrapper types, return
@@ -24,15 +24,25 @@ export function getUnwrappedFieldType(field) {
24
24
  if (field.fieldKind !== "message") {
25
25
  return undefined;
26
26
  }
27
- if (field.repeated) {
28
- return undefined;
27
+ if (field.message.typeName !== "google.protobuf.Timestamp") {
28
+ if (field.repeated || field.oneof != null) {
29
+ return undefined;
30
+ }
29
31
  }
30
- if (field.oneof != undefined) {
32
+ return getUnwrappedMessageType(field.message);
33
+ }
34
+ /**
35
+ * If the given field uses one of the well-known wrapper types, return
36
+ * the primitive type it wraps.
37
+ */
38
+ export function getUnwrappedMessageType(msg) {
39
+ if (msg.kind !== "message") {
31
40
  return undefined;
32
41
  }
33
- return wktWrapperToScalarType[field.message.typeName];
42
+ return wktWrapperToScalarType[msg.typeName];
34
43
  }
35
44
  const wktWrapperToScalarType = {
45
+ "google.protobuf.Timestamp": ScalarType.DATE,
36
46
  "google.protobuf.DoubleValue": ScalarType.DOUBLE,
37
47
  "google.protobuf.FloatValue": ScalarType.FLOAT,
38
48
  "google.protobuf.Int64Value": ScalarType.INT64,
@@ -120,6 +120,7 @@ declare const Timestamp_Wkt: {
120
120
  toJson(msg: Timestamp): JsonValue;
121
121
  toDate(msg: Timestamp | null | undefined): Date | null;
122
122
  fromDate(value: Date | null | undefined): Timestamp;
123
+ equals(a: Timestamp | Date | undefined | null, b: Timestamp | Date | undefined | null): boolean;
123
124
  };
124
125
  export declare const Timestamp: MessageType<Timestamp> & typeof Timestamp_Wkt;
125
126
  export {};
@@ -103,6 +103,17 @@ const Timestamp_Wkt = {
103
103
  const nanos = (ms % 1000) * 1000000;
104
104
  return { seconds: protoInt64.parse(seconds), nanos: nanos };
105
105
  },
106
+ equals(a, b) {
107
+ const aDate = a instanceof Date ? a : Timestamp_Wkt.toDate(a);
108
+ const bDate = b instanceof Date ? b : Timestamp_Wkt.toDate(b);
109
+ if (aDate === bDate) {
110
+ return true;
111
+ }
112
+ if (aDate == null || bDate == null) {
113
+ return aDate === bDate;
114
+ }
115
+ return +aDate === +bDate;
116
+ },
106
117
  };
107
118
  // Timestamp contains the message type declaration for Timestamp.
108
119
  export const Timestamp = createMessageType({
@@ -112,4 +123,15 @@ export const Timestamp = createMessageType({
112
123
  { no: 2, name: "nanos", kind: "scalar", T: ScalarType.INT32 },
113
124
  ],
114
125
  packedByDefault: true,
126
+ fieldWrapper: {
127
+ wrapField(value) {
128
+ if (value == null || value instanceof Date) {
129
+ return Timestamp_Wkt.fromDate(value);
130
+ }
131
+ return Timestamp.createComplete(value);
132
+ },
133
+ unwrapField(msg) {
134
+ return Timestamp_Wkt.toDate(msg);
135
+ },
136
+ },
115
137
  }, Timestamp_Wkt);
@@ -41,9 +41,10 @@ export function isField(value, field) {
41
41
  switch (fieldKind) {
42
42
  case "scalar":
43
43
  return true;
44
- case "message":
44
+ case "message": {
45
45
  const messageType = resolveMessageType(field.T);
46
46
  return isMessage(value, messageType.fields.list());
47
+ }
47
48
  case "enum":
48
49
  return typeof value === "number";
49
50
  case "map":
@@ -54,9 +55,10 @@ export function isField(value, field) {
54
55
  return true;
55
56
  case "enum":
56
57
  return typeof val === "number";
57
- case "message":
58
+ case "message": {
58
59
  const messageType = resolveMessageType(field.V.T);
59
60
  return isMessage(val, messageType.fields.list());
61
+ }
60
62
  default:
61
63
  return valueKind;
62
64
  }
@@ -102,9 +104,10 @@ export function isCompleteField(value, field) {
102
104
  switch (fieldKind) {
103
105
  case "scalar":
104
106
  return true;
105
- case "message":
107
+ case "message": {
106
108
  const messageType = resolveMessageType(field.T);
107
109
  return isCompleteMessage(value, messageType.fields.list());
110
+ }
108
111
  case "enum":
109
112
  return typeof value === "number";
110
113
  case "map":
@@ -115,9 +118,10 @@ export function isCompleteField(value, field) {
115
118
  return true;
116
119
  case "enum":
117
120
  return typeof val === "number";
118
- case "message":
121
+ case "message": {
119
122
  const messageType = resolveMessageType(field.V.T);
120
123
  return isCompleteMessage(val, messageType.fields.list());
124
+ }
121
125
  default:
122
126
  return valueKind;
123
127
  }
package/dist/json.js CHANGED
@@ -5,6 +5,7 @@ import { unwrapField, wrapField } from "./field-wrapper.js";
5
5
  import { enumZeroValue, normalizeEnumValue } from "./enum.js";
6
6
  import { protoInt64 } from "./proto-int64.js";
7
7
  import { protoBase64 } from "./proto-base64.js";
8
+ import { throwSanitizeKey } from "./names.js";
8
9
  // Default options for parsing JSON.
9
10
  const jsonReadDefaults = {
10
11
  ignoreUnknownFields: false,
@@ -57,7 +58,7 @@ function readMessage(fields, typeName, json, options, message) {
57
58
  readField(message, jsonValue, field, options);
58
59
  }
59
60
  else {
60
- let found = false;
61
+ const found = false;
61
62
  if (!found && !options.ignoreUnknownFields) {
62
63
  throw new Error(`cannot decode message ${typeName} from JSON: key "${jsonKey}" is unknown`);
63
64
  }
@@ -113,7 +114,7 @@ function readField(target, jsonValue, field, options) {
113
114
  if (!Array.isArray(jsonValue)) {
114
115
  throw new Error(`cannot decode field ${field.name} from JSON: ${jsonDebugValue(jsonValue)}`);
115
116
  }
116
- var targetArray = target[localName];
117
+ let targetArray = target[localName];
117
118
  if (!Array.isArray(targetArray)) {
118
119
  targetArray = target[localName] = [];
119
120
  }
@@ -122,16 +123,18 @@ function readField(target, jsonValue, field, options) {
122
123
  throw new Error(`cannot decode field ${field.name} from JSON: ${jsonDebugValue(jsonItem)}`);
123
124
  }
124
125
  switch (field.kind) {
125
- case "message":
126
+ case "message": {
126
127
  const messageType = resolveMessageType(field.T);
127
128
  targetArray.push(unwrapField(messageType.fieldWrapper, messageType.fromJson(jsonItem, options)));
128
129
  break;
129
- case "enum":
130
+ }
131
+ case "enum": {
130
132
  const enumValue = readEnum(field.T, jsonItem, options.ignoreUnknownFields, true);
131
133
  if (enumValue !== tokenIgnoredUnknownEnum) {
132
134
  targetArray.push(enumValue);
133
135
  }
134
136
  break;
137
+ }
135
138
  case "scalar":
136
139
  try {
137
140
  targetArray.push(readScalar(field.T, jsonItem, field.L, true));
@@ -154,7 +157,7 @@ function readField(target, jsonValue, field, options) {
154
157
  if (typeof jsonValue != "object" || Array.isArray(jsonValue)) {
155
158
  throw new Error(`cannot decode field ${field.name} from JSON: ${jsonDebugValue(jsonValue)}`);
156
159
  }
157
- var targetMap = target[localName];
160
+ let targetMap = target[localName];
158
161
  if (typeof targetMap !== "object") {
159
162
  targetMap = target[localName] = Object.create(null);
160
163
  }
@@ -173,17 +176,20 @@ function readField(target, jsonValue, field, options) {
173
176
  }
174
177
  throw new Error(m);
175
178
  }
179
+ throwSanitizeKey(key);
176
180
  switch (field.V.kind) {
177
- case "message":
181
+ case "message": {
178
182
  const messageType = resolveMessageType(field.V.T);
179
183
  targetMap[key] = messageType.fromJson(jsonMapValue, options);
180
184
  break;
181
- case "enum":
185
+ }
186
+ case "enum": {
182
187
  const enumValue = readEnum(field.V.T, jsonMapValue, options.ignoreUnknownFields, true);
183
188
  if (enumValue !== tokenIgnoredUnknownEnum) {
184
189
  targetMap[key] = enumValue;
185
190
  }
186
191
  break;
192
+ }
187
193
  case "scalar":
188
194
  try {
189
195
  targetMap[key] = readScalar(field.V.T, jsonMapValue, LongType.BIGINT, true);
@@ -205,7 +211,7 @@ function readField(target, jsonValue, field, options) {
205
211
  localName = "value";
206
212
  }
207
213
  switch (field.kind) {
208
- case "message":
214
+ case "message": {
209
215
  const messageType = resolveMessageType(field.T);
210
216
  if (jsonValue === null &&
211
217
  messageType.typeName != "google.protobuf.Value") {
@@ -213,7 +219,8 @@ function readField(target, jsonValue, field, options) {
213
219
  }
214
220
  target[localName] = unwrapField(messageType.fieldWrapper, messageType.fromJson(jsonValue, options));
215
221
  break;
216
- case "enum":
222
+ }
223
+ case "enum": {
217
224
  const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields, false);
218
225
  switch (enumValue) {
219
226
  case tokenNull:
@@ -226,6 +233,7 @@ function readField(target, jsonValue, field, options) {
226
233
  break;
227
234
  }
228
235
  break;
236
+ }
229
237
  case "scalar":
230
238
  try {
231
239
  const scalarValue = readScalar(field.T, jsonValue, field.L, false);
@@ -265,7 +273,7 @@ function readEnum(type, json, ignoreUnknownFields, nullAsZeroValue) {
265
273
  return json;
266
274
  }
267
275
  break;
268
- case "string":
276
+ case "string": {
269
277
  const value = type.findName(json);
270
278
  if (value !== undefined) {
271
279
  return value.no;
@@ -274,6 +282,7 @@ function readEnum(type, json, ignoreUnknownFields, nullAsZeroValue) {
274
282
  return tokenIgnoredUnknownEnum;
275
283
  }
276
284
  break;
285
+ }
277
286
  }
278
287
  throw new Error(`cannot decode enum ${type.typeName} from JSON: ${jsonDebugValue(json)}`);
279
288
  }
@@ -290,7 +299,7 @@ function readScalar(type, json, longType = LongType.BIGINT, nullAsZeroValue = tr
290
299
  // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity".
291
300
  // Either numbers or strings are accepted. Exponent notation is also accepted.
292
301
  case ScalarType.DOUBLE:
293
- case ScalarType.FLOAT:
302
+ case ScalarType.FLOAT: {
294
303
  if (json === "NaN")
295
304
  return Number.NaN;
296
305
  if (json === "Infinity")
@@ -320,12 +329,13 @@ function readScalar(type, json, longType = LongType.BIGINT, nullAsZeroValue = tr
320
329
  if (type == ScalarType.FLOAT)
321
330
  assertFloat32(float);
322
331
  return float;
332
+ }
323
333
  // int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted.
324
334
  case ScalarType.INT32:
325
335
  case ScalarType.FIXED32:
326
336
  case ScalarType.SFIXED32:
327
337
  case ScalarType.SINT32:
328
- case ScalarType.UINT32:
338
+ case ScalarType.UINT32: {
329
339
  let int32;
330
340
  if (typeof json == "number")
331
341
  int32 = json;
@@ -340,22 +350,25 @@ function readScalar(type, json, longType = LongType.BIGINT, nullAsZeroValue = tr
340
350
  else
341
351
  assertInt32(int32);
342
352
  return int32;
353
+ }
343
354
  // int64, fixed64, uint64: JSON value will be a decimal string. Either numbers or strings are accepted.
344
355
  case ScalarType.INT64:
345
356
  case ScalarType.SFIXED64:
346
- case ScalarType.SINT64:
357
+ case ScalarType.SINT64: {
347
358
  if (typeof json != "number" && typeof json != "string")
348
359
  break;
349
360
  const long = protoInt64.parse(json);
350
361
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
351
362
  return longType ? long.toString() : long;
363
+ }
352
364
  case ScalarType.FIXED64:
353
- case ScalarType.UINT64:
365
+ case ScalarType.UINT64: {
354
366
  if (typeof json != "number" && typeof json != "string")
355
367
  break;
356
368
  const uLong = protoInt64.uParse(json);
357
369
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
358
370
  return longType ? uLong.toString() : uLong;
371
+ }
359
372
  // bool:
360
373
  case ScalarType.BOOL:
361
374
  if (typeof json !== "boolean")
@@ -398,7 +411,7 @@ function readMapKey(type, json) {
398
411
  break;
399
412
  }
400
413
  }
401
- return readScalar(type, json, LongType.BIGINT, true).toString();
414
+ return readScalar(type, json, LongType.BIGINT, true)?.toString() ?? "";
402
415
  }
403
416
  /**
404
417
  * Resets the field, so that isFieldSet() will return false.
@@ -468,13 +481,14 @@ function writeField(field, value, options) {
468
481
  jsonObj[entryKey.toString()] = messageType.toJson(entryValue, options);
469
482
  }
470
483
  break;
471
- case "enum":
484
+ case "enum": {
472
485
  const enumType = field.V.T;
473
486
  for (const [entryKey, entryValue] of entries) {
474
487
  // JSON standard allows only (double quoted) string as property key
475
488
  jsonObj[entryKey.toString()] = writeEnum(enumType, entryValue, options.enumAsInteger);
476
489
  }
477
490
  break;
491
+ }
478
492
  }
479
493
  return options.emitDefaultValues || entries.length > 0 ?
480
494
  jsonObj
@@ -496,12 +510,13 @@ function writeField(field, value, options) {
496
510
  jsonArr.push(writeEnum(field.T, valueArr[i], options.enumAsInteger));
497
511
  }
498
512
  break;
499
- case "message":
513
+ case "message": {
500
514
  const messageType = resolveMessageType(field.T);
501
515
  for (let i = 0; i < valueArr.length; i++) {
502
516
  jsonArr.push(messageType.toJson(wrapField(messageType.fieldWrapper, valueArr[i])));
503
517
  }
504
518
  break;
519
+ }
505
520
  }
506
521
  }
507
522
  return options.emitDefaultValues || jsonArr.length > 0 ?
@@ -509,25 +524,28 @@ function writeField(field, value, options) {
509
524
  : undefined;
510
525
  }
511
526
  switch (field.kind) {
512
- case "scalar":
527
+ case "scalar": {
513
528
  const scalarValue = normalizeScalarValue(field.T, value, false);
514
- if (!options.emitDefaultValues
515
- && isScalarZeroValue(field.T, scalarValue)) {
529
+ if (!options.emitDefaultValues &&
530
+ isScalarZeroValue(field.T, scalarValue)) {
516
531
  return undefined;
517
532
  }
518
533
  return writeScalar(field.T, value);
519
- case "enum":
534
+ }
535
+ case "enum": {
520
536
  const enumValue = normalizeEnumValue(field.T, value);
521
537
  if (!options.emitDefaultValues && enumZeroValue(field.T) === enumValue) {
522
538
  return undefined;
523
539
  }
524
540
  return writeEnum(field.T, value, options.enumAsInteger);
525
- case "message":
541
+ }
542
+ case "message": {
526
543
  if (!options.emitDefaultValues && value == null) {
527
544
  return undefined;
528
545
  }
529
546
  const messageType = resolveMessageType(field.T);
530
547
  return messageType.toJson(wrapField(messageType.fieldWrapper, value));
548
+ }
531
549
  }
532
550
  }
533
551
  function writeScalar(type, value) {
@@ -576,6 +594,10 @@ function writeScalar(type, value) {
576
594
  case ScalarType.BYTES:
577
595
  assert(value instanceof Uint8Array);
578
596
  return protoBase64.enc(value);
597
+ case ScalarType.DATE:
598
+ throw new Error("cannot write date with writeScalar");
599
+ default:
600
+ throw new Error("unknown scalar type");
579
601
  }
580
602
  }
581
603
  function writeEnum(type, value, enumAsInteger) {
package/dist/message.d.ts CHANGED
@@ -109,7 +109,7 @@ export type MessageTypeParams<T extends Message<T>> = Pick<MessageType<T>, "fiel
109
109
  * The argument `packedByDefault` specifies whether fields that do not specify
110
110
  * `packed` should be packed (proto3) or unpacked (proto2).
111
111
  */
112
- export declare function createMessageType<T extends Message<T>, E extends Record<string, Function> = {}>(params: MessageTypeParams<T>, exts?: E): MessageType<T> & E;
112
+ export declare function createMessageType<T extends Message<T>, E extends Partial<MessageType<T>> = Partial<MessageType<T>>>(params: MessageTypeParams<T>, exts?: E): MessageType<T> & E;
113
113
  export declare function compareMessages<T extends Message<T>>(fields: FieldList, a: T | undefined | null, b: T | undefined | null): boolean;
114
114
  export declare function cloneMessage<T extends Message<T>>(message: T | null | undefined, fields: FieldList): T | null;
115
115
  /**
package/dist/message.js CHANGED
@@ -119,9 +119,10 @@ export function compareMessages(fields, a, b) {
119
119
  }
120
120
  // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- repeated fields are never "map"
121
121
  switch (m.kind) {
122
- case "message":
122
+ case "message": {
123
123
  const messageType = resolveMessageType(m.T);
124
124
  return va.every((a, i) => messageType.equals(a, vb[i]));
125
+ }
125
126
  case "scalar":
126
127
  return va.every((a, i) => scalarEquals(m.T, a, vb[i]));
127
128
  case "enum":
@@ -136,7 +137,7 @@ export function compareMessages(fields, a, b) {
136
137
  return scalarEquals(ScalarType.INT32, va, vb);
137
138
  case "scalar":
138
139
  return scalarEquals(m.T, va, vb);
139
- case "oneof":
140
+ case "oneof": {
140
141
  if (va?.case !== vb?.case) {
141
142
  return false;
142
143
  }
@@ -149,27 +150,32 @@ export function compareMessages(fields, a, b) {
149
150
  }
150
151
  // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- oneof fields are never "map"
151
152
  switch (s.kind) {
152
- case "message":
153
+ case "message": {
153
154
  const messageType = resolveMessageType(s.T);
154
155
  return messageType.equals(va.value, vb.value);
156
+ }
155
157
  case "enum":
156
158
  return scalarEquals(ScalarType.INT32, va.value, vb.value);
157
159
  case "scalar":
158
160
  return scalarEquals(s.T, va.value, vb.value);
159
161
  }
160
162
  throw new Error(`oneof cannot contain ${s.kind}`);
161
- case "map":
163
+ }
164
+ case "map": {
162
165
  const keys = Object.keys(va).concat(Object.keys(vb));
163
166
  switch (m.V.kind) {
164
- case "message":
167
+ case "message": {
165
168
  const messageType = resolveMessageType(m.V.T);
166
169
  return keys.every((k) => messageType.equals(va[k], vb[k]));
170
+ }
167
171
  case "enum":
168
172
  return keys.every((k) => scalarEquals(ScalarType.INT32, va[k], vb[k]));
169
- case "scalar":
173
+ case "scalar": {
170
174
  const scalarType = m.V.T;
171
175
  return keys.every((k) => scalarEquals(scalarType, va[k], vb[k]));
176
+ }
172
177
  }
178
+ }
173
179
  }
174
180
  });
175
181
  }
@@ -205,7 +211,7 @@ export function createCompleteMessage(fields) {
205
211
  case "enum":
206
212
  message[localName] = field.repeated ? [] : enumZeroValue(field.T);
207
213
  break;
208
- case "message":
214
+ case "message": {
209
215
  // oneofs are handled above
210
216
  if (field.oneof) {
211
217
  break;
@@ -216,10 +222,11 @@ export function createCompleteMessage(fields) {
216
222
  }
217
223
  const messageType = resolveMessageType(field.T);
218
224
  message[localName] =
219
- !!messageType.fieldWrapper ?
225
+ messageType.fieldWrapper ?
220
226
  messageType.fieldWrapper.unwrapField(null)
221
227
  : createCompleteMessage(messageType.fields);
222
228
  break;
229
+ }
223
230
  case "map":
224
231
  message[localName] = Object.create(null);
225
232
  break;
package/dist/names.js CHANGED
@@ -209,6 +209,8 @@ const reservedObjectProperties = new Set([
209
209
  "toString",
210
210
  "toJSON",
211
211
  "valueOf",
212
+ "__proto__",
213
+ "prototype",
212
214
  ]);
213
215
  /**
214
216
  * Names that cannot be used for object properties because they are reserved
@@ -246,7 +248,9 @@ export const safeIdentifier = (name) => {
246
248
  return name;
247
249
  };
248
250
  export function checkSanitizeKey(key) {
249
- return typeof key === "string" && key !== "__proto__";
251
+ return (typeof key === "string" &&
252
+ !!key.length &&
253
+ !reservedObjectProperties.has(key));
250
254
  }
251
255
  export function throwSanitizeKey(key) {
252
256
  if (typeof key !== "string") {
package/dist/partial.js CHANGED
@@ -23,7 +23,7 @@ export function applyPartialMessage(source, target, fields, clone = false) {
23
23
  continue;
24
24
  }
25
25
  switch (member.kind) {
26
- case "oneof":
26
+ case "oneof": {
27
27
  if (typeof sourceValue !== "object") {
28
28
  throw new Error(`field ${localName}: invalid oneof: must be an object with case and value`);
29
29
  }
@@ -70,7 +70,8 @@ export function applyPartialMessage(source, target, fields, clone = false) {
70
70
  dv.value = sv;
71
71
  }
72
72
  break;
73
- case "scalar":
73
+ }
74
+ case "scalar": {
74
75
  if (member.repeated) {
75
76
  if (!Array.isArray(sourceValue)) {
76
77
  throw new Error(`field ${localName}: invalid value: must be array`);
@@ -84,10 +85,12 @@ export function applyPartialMessage(source, target, fields, clone = false) {
84
85
  }
85
86
  t[localName] = normalizeScalarValue(member.T, sourceValue, clone);
86
87
  break;
87
- case "enum":
88
+ }
89
+ case "enum": {
88
90
  t[localName] = normalizeEnumValue(member.T, sourceValue);
89
91
  break;
90
- case "map":
92
+ }
93
+ case "map": {
91
94
  if (typeof sourceValue !== "object") {
92
95
  throw new Error(`field ${member.localName}: invalid value: must be object`);
93
96
  }
@@ -97,7 +100,8 @@ export function applyPartialMessage(source, target, fields, clone = false) {
97
100
  }
98
101
  applyPartialMap(sourceValue, tMap, member.V, clone);
99
102
  break;
100
- case "message":
103
+ }
104
+ case "message": {
101
105
  const mt = resolveMessageType(member.T);
102
106
  if (member.repeated) {
103
107
  // skip null or undefined values
@@ -135,6 +139,7 @@ export function applyPartialMessage(source, target, fields, clone = false) {
135
139
  applyPartialMessage(sourceValue, destMsg, mt.fields);
136
140
  }
137
141
  break;
142
+ }
138
143
  }
139
144
  }
140
145
  }
@@ -169,7 +174,7 @@ export function applyPartialMap(sourceMap, targetMap, value, clone) {
169
174
  }
170
175
  }
171
176
  break;
172
- case "message":
177
+ case "message": {
173
178
  const messageType = resolveMessageType(value.T);
174
179
  for (const [k, v] of Object.entries(sourceMap)) {
175
180
  throwSanitizeKey(k);
@@ -181,7 +186,7 @@ export function applyPartialMap(sourceMap, targetMap, value, clone) {
181
186
  throw new Error(`invalid value: must be object`);
182
187
  }
183
188
  let val = targetMap[k];
184
- if (!!messageType.fieldWrapper) {
189
+ if (messageType.fieldWrapper) {
185
190
  // For wrapper type messages, call createCompleteMessage.
186
191
  val = targetMap[k] = createCompleteMessage(messageType.fields);
187
192
  }
@@ -192,5 +197,6 @@ export function applyPartialMap(sourceMap, targetMap, value, clone) {
192
197
  applyPartialMessage(v, val, messageType.fields);
193
198
  }
194
199
  break;
200
+ }
195
201
  }
196
202
  }
@@ -28,32 +28,8 @@ export function generateTs(schema) {
28
28
  }
29
29
  const messageTypes = [];
30
30
  const dependencies = new Map();
31
- function collectMessages(message) {
32
- if (message.file !== file) {
33
- return;
34
- }
35
- for (const nestedEnum of message.nestedEnums) {
36
- generateEnum(schema, f, nestedEnum);
37
- }
38
- messageTypes.push(message);
39
- const deps = new Set();
40
- for (const field of message.fields) {
41
- if (field.fieldKind === "message" && field.message.file === file) {
42
- deps.add(field.message);
43
- }
44
- else if (field.fieldKind === "map" &&
45
- field.mapValue.kind === "message" &&
46
- field.mapValue.message.file === file) {
47
- deps.add(field.mapValue.message);
48
- }
49
- }
50
- dependencies.set(message, deps);
51
- for (const nestedMessage of message.nestedMessages) {
52
- collectMessages(nestedMessage);
53
- }
54
- }
55
31
  for (const message of file.messages) {
56
- collectMessages(message);
32
+ collectMessages(schema, file, message, messageTypes, dependencies, f);
57
33
  }
58
34
  // Topological sort to ensure consts are declared in the right order.
59
35
  const sortedMessageTypes = topologicalSort(messageTypes, dependencies);
@@ -63,6 +39,31 @@ export function generateTs(schema) {
63
39
  // We do not generate anything for services or extensions
64
40
  }
65
41
  }
42
+ // collectMessages collects a list of all message types and their dependencies.
43
+ function collectMessages(schema, file, message, messageTypes, dependencies, f) {
44
+ if (message.file !== file) {
45
+ return;
46
+ }
47
+ for (const nestedEnum of message.nestedEnums) {
48
+ generateEnum(schema, f, nestedEnum);
49
+ }
50
+ messageTypes.push(message);
51
+ const deps = new Set();
52
+ for (const field of message.fields) {
53
+ if (field.fieldKind === "message" && field.message.file === file) {
54
+ deps.add(field.message);
55
+ }
56
+ else if (field.fieldKind === "map" &&
57
+ field.mapValue.kind === "message" &&
58
+ field.mapValue.message.file === file) {
59
+ deps.add(field.mapValue.message);
60
+ }
61
+ }
62
+ dependencies.set(message, deps);
63
+ for (const nestedMessage of message.nestedMessages) {
64
+ collectMessages(schema, file, nestedMessage, messageTypes, dependencies, f);
65
+ }
66
+ }
66
67
  // topologicalSort sorts the list of messages by dependency order.
67
68
  function topologicalSort(messages, dependencies) {
68
69
  const result = [];
@@ -162,7 +163,7 @@ function generateField(f, field) {
162
163
  function generateOneof(f, oneof) {
163
164
  f.print();
164
165
  f.print(f.jsDoc(oneof, " "));
165
- var oneOfCases = oneof.fields
166
+ const oneOfCases = oneof.fields
166
167
  .map((field) => {
167
168
  const { typing } = getFieldTypeInfo(field);
168
169
  const doc = f.jsDoc(field, " ");
@@ -186,7 +187,7 @@ export function generateFieldInfo(f, schema, field) {
186
187
  f.print(" ", getFieldInfoLiteral(schema, field), ",");
187
188
  }
188
189
  export const createTypeImport = (desc) => {
189
- var name = localName(desc);
190
+ let name = localName(desc);
190
191
  if (desc.kind === "enum") {
191
192
  name += "_Enum";
192
193
  }
@@ -395,6 +396,17 @@ function generateWktMethods(schema, f, message, ref) {
395
396
  f.print(" const nanos = (ms % 1000) * 1000000;");
396
397
  f.print(" return { ", localName(ref.seconds), ": ", protoInt64, ".parse(seconds), ", localName(ref.nanos), ": nanos };");
397
398
  f.print(" },");
399
+ f.print(" equals(a: ", message, " | Date | undefined | null, b: ", message, " | Date | undefined | null): boolean {");
400
+ f.print(" const aDate = a instanceof Date ? a : ", message, "_Wkt.toDate(a);");
401
+ f.print(" const bDate = b instanceof Date ? b : ", message, "_Wkt.toDate(b);");
402
+ f.print(" if (aDate === bDate) {");
403
+ f.print(" return true;");
404
+ f.print(" }");
405
+ f.print(" if (aDate == null || bDate == null) {");
406
+ f.print(" return aDate === bDate;");
407
+ f.print(" }");
408
+ f.print(" return +aDate === +bDate;");
409
+ f.print(" },");
398
410
  break;
399
411
  case "google.protobuf.Duration":
400
412
  f.print(" fromJson(json: ", JsonValue, " | null | undefined, _options?: Partial<", JsonReadOptions, ">): ", message, " {");
@@ -560,30 +572,18 @@ function generateWktMethods(schema, f, message, ref) {
560
572
  }
561
573
  function generateWktFieldWrapper(f, message, ref) {
562
574
  switch (ref?.typeName) {
563
- /* TODO Wrap Timestamp => Date
564
575
  case "google.protobuf.Timestamp": {
565
- f.print(" fieldWrapper: {");
566
- f.print(
567
- " wrapField(value: ",
568
- message,
569
- " | Date | null | undefined): ",
570
- message,
571
- " {",
572
- );
573
- f.print(
574
- " if (value == null || value instanceof Date) { return ",
575
- message,
576
- "_Wkt.fromDate(value); }",
577
- );
578
- f.print(" return ", message, ".createComplete(value);");
579
- f.print(" },");
580
- f.print(" unwrapField(msg: ", message, "): Date | null {");
581
- f.print(" return ", message, "_Wkt.toDate(msg);");
582
- f.print(" }");
583
- f.print(" } as const,");
584
- break;
576
+ f.print(" fieldWrapper: {");
577
+ f.print(" wrapField(value: ", message, " | Date | null | undefined): ", message, " {");
578
+ f.print(" if (value == null || value instanceof Date) { return ", message, "_Wkt.fromDate(value); }");
579
+ f.print(" return ", message, ".createComplete(value);");
580
+ f.print(" },");
581
+ f.print(" unwrapField(msg: ", message, "): Date | null {");
582
+ f.print(" return ", message, "_Wkt.toDate(msg);");
583
+ f.print(" }");
584
+ f.print(" } as const,");
585
+ break;
585
586
  }
586
- */
587
587
  case "google.protobuf.DoubleValue":
588
588
  case "google.protobuf.FloatValue":
589
589
  case "google.protobuf.Int64Value":
@@ -13,7 +13,6 @@
13
13
  // limitations under the License.
14
14
  import ts from "typescript";
15
15
  import { createDefaultMapFromNodeModules, createSystem, createVirtualCompilerHost, } from "@typescript/vfs";
16
- /* eslint-disable import/no-named-as-default-member */
17
16
  // The default options used to auto-transpile if needed.
18
17
  const defaultOptions = {
19
18
  // Type checking
package/dist/scalar.d.ts CHANGED
@@ -18,7 +18,8 @@ export declare enum ScalarType {
18
18
  SFIXED32 = 15,
19
19
  SFIXED64 = 16,
20
20
  SINT32 = 17,// Uses ZigZag encoding.
21
- SINT64 = 18
21
+ SINT64 = 18,// Uses ZigZag encoding.
22
+ DATE = 100
22
23
  }
23
24
  /**
24
25
  * JavaScript representation of fields with 64 bit integral types (int64, uint64,
@@ -50,11 +51,11 @@ export declare enum LongType {
50
51
  /**
51
52
  * ScalarValue maps from a scalar field type to a TypeScript value type.
52
53
  */
53
- export type ScalarValue<T = ScalarType, L extends LongType = LongType.STRING | LongType.BIGINT> = T extends ScalarType.STRING ? string : T extends ScalarType.INT32 ? number : T extends ScalarType.UINT32 ? number : T extends ScalarType.UINT32 ? number : T extends ScalarType.SINT32 ? number : T extends ScalarType.FIXED32 ? number : T extends ScalarType.SFIXED32 ? number : T extends ScalarType.FLOAT ? number : T extends ScalarType.DOUBLE ? number : T extends ScalarType.INT64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.SINT64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.SFIXED64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.UINT64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.FIXED64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.BOOL ? boolean : T extends ScalarType.BYTES ? Uint8Array : never;
54
+ export type ScalarValue<T = ScalarType, L extends LongType = LongType.STRING | LongType.BIGINT> = T extends ScalarType.STRING ? string : T extends ScalarType.INT32 ? number : T extends ScalarType.UINT32 ? number : T extends ScalarType.UINT32 ? number : T extends ScalarType.SINT32 ? number : T extends ScalarType.FIXED32 ? number : T extends ScalarType.SFIXED32 ? number : T extends ScalarType.FLOAT ? number : T extends ScalarType.DOUBLE ? number : T extends ScalarType.INT64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.SINT64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.SFIXED64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.UINT64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.FIXED64 ? (L extends LongType.STRING ? string : bigint) : T extends ScalarType.BOOL ? boolean : T extends ScalarType.BYTES ? Uint8Array : T extends ScalarType.DATE ? null : never;
54
55
  /**
55
56
  * Returns true if both scalar values are equal.
56
57
  */
57
- export declare function scalarEquals(type: ScalarType, a: string | boolean | number | bigint | Uint8Array | undefined, b: string | boolean | number | bigint | Uint8Array | undefined): boolean;
58
+ export declare function scalarEquals(type: ScalarType, a: string | boolean | number | bigint | Uint8Array | Date | null | undefined, b: string | boolean | number | bigint | Uint8Array | Date | null | undefined): boolean;
58
59
  /**
59
60
  * Returns the zero value for the given scalar type.
60
61
  */
package/dist/scalar.js CHANGED
@@ -48,6 +48,8 @@ export var ScalarType;
48
48
  ScalarType[ScalarType["SFIXED64"] = 16] = "SFIXED64";
49
49
  ScalarType[ScalarType["SINT32"] = 17] = "SINT32";
50
50
  ScalarType[ScalarType["SINT64"] = 18] = "SINT64";
51
+ // DATE is not a Protobuf scalar type but used internally for the Timestamp wkt.
52
+ ScalarType[ScalarType["DATE"] = 100] = "DATE";
51
53
  })(ScalarType || (ScalarType = {}));
52
54
  /**
53
55
  * JavaScript representation of fields with 64 bit integral types (int64, uint64,
@@ -85,6 +87,10 @@ export function scalarEquals(type, a, b) {
85
87
  // This correctly matches equal values except BYTES and (possibly) 64-bit integers.
86
88
  return true;
87
89
  }
90
+ // Special case null/undefined - only equal if both are null/undefined
91
+ if (a == null || b == null) {
92
+ return a === b;
93
+ }
88
94
  // Special case BYTES - we need to compare each byte individually
89
95
  if (type == ScalarType.BYTES) {
90
96
  if (!(a instanceof Uint8Array) || !(b instanceof Uint8Array)) {
@@ -100,6 +106,15 @@ export function scalarEquals(type, a, b) {
100
106
  }
101
107
  return true;
102
108
  }
109
+ // Special case DATE - we need to compare the numeric value
110
+ if (type == ScalarType.DATE) {
111
+ const dateA = toDate(a, false);
112
+ const dateB = toDate(b, false);
113
+ if (dateA == null || dateB == null) {
114
+ return dateA === dateB;
115
+ }
116
+ return dateA != null && dateB != null && +dateA === +dateB;
117
+ }
103
118
  // Special case 64-bit integers - we support number, string and bigint representation.
104
119
  // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
105
120
  switch (type) {
@@ -136,12 +151,15 @@ export function scalarZeroValue(type, longType) {
136
151
  return new Uint8Array(0);
137
152
  case ScalarType.STRING:
138
153
  return "";
154
+ case ScalarType.DATE:
155
+ return null;
139
156
  default:
140
157
  // Handles INT32, UINT32, SINT32, FIXED32, SFIXED32.
141
158
  // We do not use individual cases to save a few bytes code size.
142
159
  return 0;
143
160
  }
144
161
  }
162
+ const dateZeroValue = +new Date(0);
145
163
  /**
146
164
  * Returns true for a zero-value. For example, an integer has the zero-value `0`,
147
165
  * a boolean is `false`, a string is `""`, and bytes is an empty Uint8Array.
@@ -151,6 +169,8 @@ export function scalarZeroValue(type, longType) {
151
169
  */
152
170
  export function isScalarZeroValue(type, value) {
153
171
  switch (type) {
172
+ case ScalarType.DATE:
173
+ return value == null || +value === dateZeroValue;
154
174
  case ScalarType.BOOL:
155
175
  return value === false;
156
176
  case ScalarType.STRING:
@@ -178,6 +198,9 @@ export function normalizeScalarValue(type, value, clone, longType = LongType.BIG
178
198
  if (isScalarZeroValue(type, value)) {
179
199
  return scalarZeroValue(type, longType);
180
200
  }
201
+ if (type === ScalarType.DATE) {
202
+ return toDate(value, clone);
203
+ }
181
204
  return value;
182
205
  }
183
206
  // converts any ArrayLike<number> to Uint8Array if necessary.
@@ -185,3 +208,13 @@ export function normalizeScalarValue(type, value, clone, longType = LongType.BIG
185
208
  export function toU8Arr(input, clone) {
186
209
  return !clone && input instanceof Uint8Array ? input : new Uint8Array(input);
187
210
  }
211
+ function toDate(input, clone) {
212
+ if (input instanceof Date) {
213
+ return clone ? new Date(input.getTime()) : input;
214
+ }
215
+ if (typeof input === "string" || typeof input === "number") {
216
+ const date = new Date(input);
217
+ return isNaN(date.getTime()) ? null : date;
218
+ }
219
+ return null;
220
+ }
@@ -57,6 +57,11 @@ export function parseTextFormatScalarValue(type, value) {
57
57
  case ScalarType.FIXED32:
58
58
  case ScalarType.SFIXED32:
59
59
  return parseInt(value, 10);
60
+ case ScalarType.DATE:
61
+ // TODO
62
+ throw new Error("unimplemented: parse Date from text format");
63
+ default:
64
+ throw new Error(`cannot parse ${ScalarType[type]}`);
60
65
  }
61
66
  }
62
67
  /**
package/dist/util.js CHANGED
@@ -29,17 +29,7 @@ export function getFieldTypeInfo(field) {
29
29
  typingInferrableFromZeroValue = true;
30
30
  break;
31
31
  case "message": {
32
- const baseType = codegenInfo.getUnwrappedFieldType(field);
33
- if (baseType !== undefined) {
34
- typing.push(scalarTypeScriptType(baseType, LongType.BIGINT));
35
- }
36
- else {
37
- typing.push({
38
- kind: "es_ref_message",
39
- type: field.message,
40
- typeOnly: true,
41
- });
42
- }
32
+ typing.push(getUnwrappedFieldScriptType(field));
43
33
  optional = true;
44
34
  typingInferrableFromZeroValue = true;
45
35
  break;
@@ -75,11 +65,7 @@ export function getFieldTypeInfo(field) {
75
65
  valueType = scalarTypeScriptType(field.mapValue.scalar, LongType.BIGINT);
76
66
  break;
77
67
  case "message":
78
- valueType = {
79
- kind: "es_ref_message",
80
- type: field.mapValue.message,
81
- typeOnly: true,
82
- };
68
+ valueType = getUnwrappedMessageScriptType(field.mapValue.message);
83
69
  break;
84
70
  case "enum":
85
71
  valueType = {
@@ -127,6 +113,8 @@ export function getFieldDefaultValueExpression(field, enumAs = "enum_value_as_is
127
113
  }
128
114
  case "scalar":
129
115
  return literalScalarValue(defaultValue, field);
116
+ default:
117
+ return undefined;
130
118
  }
131
119
  }
132
120
  /**
@@ -205,6 +193,13 @@ function literalScalarValue(value, field) {
205
193
  longType: field.longType,
206
194
  value,
207
195
  };
196
+ case ScalarType.DATE:
197
+ if (value == null) {
198
+ return `null`;
199
+ }
200
+ return `new Date(${(value instanceof Date ? +value : value).toString()})`;
201
+ default:
202
+ throw new Error("unsupported scalar type for literalScalarValue");
208
203
  }
209
204
  }
210
205
  function literalEnumValue(value, enumAs) {
@@ -251,7 +246,29 @@ function scalarTypeScriptType(type, longType) {
251
246
  return "bigint";
252
247
  case ScalarType.BYTES:
253
248
  return "Uint8Array";
249
+ case ScalarType.DATE:
250
+ return "Date";
254
251
  default:
255
252
  return "number";
256
253
  }
257
254
  }
255
+ function getUnwrappedFieldScriptType(field, longType) {
256
+ const baseType = codegenInfo.getUnwrappedFieldType(field);
257
+ return baseType ?
258
+ scalarTypeScriptType(baseType, longType ?? field.longType ?? LongType.BIGINT)
259
+ : {
260
+ kind: "es_ref_message",
261
+ type: field.message,
262
+ typeOnly: true,
263
+ };
264
+ }
265
+ function getUnwrappedMessageScriptType(msg, longType = LongType.BIGINT) {
266
+ const baseType = codegenInfo.getUnwrappedMessageType(msg);
267
+ return baseType !== undefined ?
268
+ scalarTypeScriptType(baseType, longType)
269
+ : {
270
+ kind: "es_ref_message",
271
+ type: msg,
272
+ typeOnly: true,
273
+ };
274
+ }
@@ -2,14 +2,14 @@
2
2
  // @generated from file example/example.proto (package example, syntax proto3)
3
3
  /* eslint-disable */
4
4
 
5
- import type { MessageType, PartialFieldInfo } from "@aptre/protobuf-es-lite";
5
+ import type { MessageType, PartialFieldInfo } from "../src/index.js";
6
6
  import {
7
7
  createEnumType,
8
8
  createMessageType,
9
9
  Message,
10
10
  ScalarType,
11
11
  Timestamp,
12
- } from "@aptre/protobuf-es-lite";
12
+ } from "../src/index.js";
13
13
 
14
14
  export const protobufPackage = "example";
15
15
 
@@ -55,11 +55,11 @@ export type EchoMsg = Message<{
55
55
  /**
56
56
  * @generated from field: google.protobuf.Timestamp ts = 2;
57
57
  */
58
- ts?: Timestamp;
58
+ ts?: Date;
59
59
  /**
60
60
  * @generated from field: repeated google.protobuf.Timestamp timestamps = 5;
61
61
  */
62
- timestamps?: Timestamp[];
62
+ timestamps?: Date[];
63
63
 
64
64
  /**
65
65
  * @generated from oneof example.EchoMsg.demo
@@ -2,6 +2,7 @@
2
2
  "extends": "../tsconfig.json",
3
3
  "files": ["example.pb.ts"],
4
4
  "compilerOptions": {
5
+ "rootDir": "../",
5
6
  "paths": {
6
7
  "@aptre/protobuf-es-lite": [
7
8
  "../src",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aptre/protobuf-es-lite",
3
3
  "description": "Lightweight Protobuf codegen for TypeScript and JavaScript.",
4
- "version": "0.4.1",
4
+ "version": "0.4.3",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "url": "git+ssh://git@github.com/aperturerobotics/protobuf-es-lite.git"
@@ -56,6 +56,7 @@
56
56
  "format": "prettier --write './src/**/(*.ts|*.tsx|*.html|*.css|*.scss)'",
57
57
  "precommit": "lint-staged",
58
58
  "test": "vitest run",
59
+ "lint": "ESLINT_USE_FLAT_CONFIG=false eslint -c .eslintrc.cjs ./",
59
60
  "release:version": "npm version patch -m \"release: v%s\" --no-git-tag-version",
60
61
  "release:version:minor": "npm version minor -m \"release: v%s\" --no-git-tag-version",
61
62
  "release:commit": "git reset && git add package.json && git commit -s -m \"release: v$npm_package_version\" && git tag v$npm_package_version",
@@ -73,6 +74,11 @@
73
74
  },
74
75
  "devDependencies": {
75
76
  "@types/node": "^20.12.7",
77
+ "@typescript-eslint/eslint-plugin": "^7.8.0",
78
+ "@typescript-eslint/parser": "^7.8.0",
79
+ "eslint": "^8.0.0",
80
+ "eslint-config-prettier": "^9.1.0",
81
+ "eslint-plugin-unused-imports": "^3.2.0",
76
82
  "lint-staged": ">=10",
77
83
  "pre-commit": "^1.2.2",
78
84
  "prettier": "^3.2.5",
@@ -3,7 +3,6 @@
3
3
  "target": "es2022",
4
4
  "module": "Node16",
5
5
  "moduleResolution": "Node16",
6
- "esModuleInterop": false,
7
6
  "forceConsistentCasingInFileNames": true,
8
7
  "strict": true,
9
8
  "noImplicitAny": true,
@@ -17,8 +16,9 @@
17
16
  "noImplicitReturns": true,
18
17
  "noFallthroughCasesInSwitch": true,
19
18
  "noImplicitOverride": true,
20
- "verbatimModuleSyntax": true,
21
- "skipLibCheck": false,
22
- "declaration": true
19
+ "declaration": true,
20
+ "verbatimModuleSyntax": false,
21
+ "esModuleInterop": true,
22
+ "skipLibCheck": true,
23
23
  }
24
24
  }
package/tsconfig.json CHANGED
@@ -2,9 +2,6 @@
2
2
  "files": ["src/protoc-gen-es-lite/protoc-gen-es-lite-plugin.ts", "src/index.ts", "src/protoplugin/index.ts", "src/protoplugin/ecmascript/index.ts"],
3
3
  "extends": "./tsconfig.base.json",
4
4
  "compilerOptions": {
5
- "verbatimModuleSyntax": false,
6
- "esModuleInterop": true,
7
- "skipLibCheck": true,
8
5
  "rootDir": "./src"
9
6
  }
10
7
  }