@aptre/protobuf-es-lite 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/binary.d.ts +1 -1
- package/dist/binary.js +11 -15
- package/dist/field-wrapper.d.ts +13 -0
- package/dist/field-wrapper.js +7 -0
- package/dist/field.d.ts +1 -1
- package/dist/field.js +9 -5
- package/dist/google/protobuf/timestamp.pb.d.ts +2 -1
- package/dist/google/protobuf/timestamp.pb.js +12 -0
- package/dist/json.js +44 -32
- package/dist/message.d.ts +1 -1
- package/dist/message.js +36 -102
- package/dist/partial.d.ts +2 -2
- package/dist/partial.js +9 -7
- package/dist/protoc-gen-es-lite/typescript.js +33 -1
- package/dist/scalar.d.ts +3 -2
- package/dist/scalar.js +6 -5
- package/example/tsconfig.json +1 -3
- package/package.json +11 -9
package/dist/binary.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ declare function readMapEntry(field: FieldInfo & {
|
|
|
50
50
|
}, reader: IBinaryReader, options: BinaryReadOptions): [string | number, ScalarValue | AnyMessage | undefined];
|
|
51
51
|
declare function readScalar(reader: IBinaryReader, type: ScalarType): ScalarValue;
|
|
52
52
|
declare function readScalarLTString(reader: IBinaryReader, type: ScalarType): Exclude<ScalarValue, bigint>;
|
|
53
|
-
declare function readMessage<T>(message: T, fields: FieldList, reader: IBinaryReader, lengthOrEndTagFieldNo: number, options: BinaryReadOptions, delimitedMessageEncoding
|
|
53
|
+
declare function readMessage<T>(message: T, fields: FieldList, reader: IBinaryReader, lengthOrEndTagFieldNo: number, options: BinaryReadOptions, delimitedMessageEncoding: boolean): void;
|
|
54
54
|
/**
|
|
55
55
|
* Serialize a message to binary data.
|
|
56
56
|
*/
|
package/dist/binary.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isFieldSet, resolveMessageType, } from "./field.js";
|
|
2
2
|
import { handleUnknownField, unknownFieldsSymbol } from "./unknown.js";
|
|
3
|
-
import { wrapField } from "./field-wrapper.js";
|
|
3
|
+
import { unwrapField, wrapField } from "./field-wrapper.js";
|
|
4
4
|
import { LongType, ScalarType, scalarZeroValue, } from "./scalar.js";
|
|
5
5
|
import { assert } from "./assert.js";
|
|
6
6
|
import { BinaryReader, BinaryWriter, WireType, } from "./binary-encoding.js";
|
|
@@ -26,7 +26,7 @@ reader, field, wireType, options) {
|
|
|
26
26
|
if (field.oneof) {
|
|
27
27
|
var oneofMsg = target[field.oneof.localName];
|
|
28
28
|
if (!oneofMsg) {
|
|
29
|
-
oneofMsg = target[field.oneof.localName] =
|
|
29
|
+
oneofMsg = target[field.oneof.localName] = Object.create(null);
|
|
30
30
|
}
|
|
31
31
|
target = oneofMsg;
|
|
32
32
|
if (target.case != localName) {
|
|
@@ -74,19 +74,16 @@ reader, field, wireType, options) {
|
|
|
74
74
|
if (!Array.isArray(tgtArr)) {
|
|
75
75
|
tgtArr = target[localName] = [];
|
|
76
76
|
}
|
|
77
|
-
tgtArr.push(readMessageField(reader,
|
|
77
|
+
tgtArr.push(unwrapField(messageType.fieldWrapper, readMessageField(reader, Object.create(null), messageType.fields, options, field)));
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
|
-
target[localName] = readMessageField(reader,
|
|
81
|
-
if (messageType.fieldWrapper && !field.oneof && !field.repeated) {
|
|
82
|
-
target[localName] = messageType.fieldWrapper.unwrapField(target[localName]);
|
|
83
|
-
}
|
|
80
|
+
target[localName] = unwrapField(messageType.fieldWrapper, readMessageField(reader, Object.create(null), messageType.fields, options, field));
|
|
84
81
|
}
|
|
85
82
|
break;
|
|
86
83
|
case "map":
|
|
87
84
|
let [mapKey, mapVal] = readMapEntry(field, reader, options);
|
|
88
85
|
if (typeof target[localName] !== "object") {
|
|
89
|
-
target[localName] =
|
|
86
|
+
target[localName] = Object.create(null);
|
|
90
87
|
}
|
|
91
88
|
// safe to assume presence of map object, oneof cannot contain repeated values
|
|
92
89
|
target[localName][mapKey] = mapVal;
|
|
@@ -113,7 +110,7 @@ function readMapEntry(field, reader, options) {
|
|
|
113
110
|
break;
|
|
114
111
|
case "message":
|
|
115
112
|
const messageType = resolveMessageType(field.V.T);
|
|
116
|
-
val = readMessageField(reader,
|
|
113
|
+
val = readMessageField(reader, Object.create(null), messageType.fields, options, undefined);
|
|
117
114
|
break;
|
|
118
115
|
}
|
|
119
116
|
break;
|
|
@@ -135,7 +132,7 @@ function readMapEntry(field, reader, options) {
|
|
|
135
132
|
val = field.V.T.values[0].no;
|
|
136
133
|
break;
|
|
137
134
|
case "message":
|
|
138
|
-
val =
|
|
135
|
+
val = Object.create(null);
|
|
139
136
|
break;
|
|
140
137
|
}
|
|
141
138
|
}
|
|
@@ -184,13 +181,10 @@ function readScalarLTString(reader, type) {
|
|
|
184
181
|
// Read a message, avoiding MessageType.fromBinary() to re-use the
|
|
185
182
|
// BinaryReadOptions and the IBinaryReader.
|
|
186
183
|
function readMessageField(reader, message, fields, options, field) {
|
|
187
|
-
|
|
188
|
-
readMessage(message, fields, reader, delimited ? field.no : reader.uint32(), // eslint-disable-line @typescript-eslint/strict-boolean-expressions
|
|
189
|
-
options, delimited);
|
|
184
|
+
readMessage(message, fields, reader, field?.delimited ? field.no : reader.uint32(), options, field?.delimited ?? false);
|
|
190
185
|
return message;
|
|
191
186
|
}
|
|
192
187
|
function readMessage(message, fields, reader, lengthOrEndTagFieldNo, options, delimitedMessageEncoding) {
|
|
193
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
194
188
|
const end = delimitedMessageEncoding ? reader.len : reader.pos + lengthOrEndTagFieldNo;
|
|
195
189
|
let fieldNo, wireType;
|
|
196
190
|
while (reader.pos < end) {
|
|
@@ -227,7 +221,9 @@ function writeMessage(message, fields, writer, options) {
|
|
|
227
221
|
const value = field.oneof ?
|
|
228
222
|
message[field.oneof.localName].value
|
|
229
223
|
: message[field.localName];
|
|
230
|
-
|
|
224
|
+
if (value !== undefined) {
|
|
225
|
+
writeField(field, value, writer, options);
|
|
226
|
+
}
|
|
231
227
|
}
|
|
232
228
|
if (options.writeUnknownFields) {
|
|
233
229
|
writeUnknownFields(message, writer);
|
package/dist/field-wrapper.d.ts
CHANGED
|
@@ -5,7 +5,15 @@ import { ScalarType } from "./scalar.js";
|
|
|
5
5
|
* ergonomic for use as a message field.
|
|
6
6
|
*/
|
|
7
7
|
export interface FieldWrapper<T = any, U = any> {
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a primitive message field value in its corresponding wrapper
|
|
10
|
+
* message. This function is idempotent.
|
|
11
|
+
*/
|
|
8
12
|
wrapField(value: U | null | undefined): T;
|
|
13
|
+
/**
|
|
14
|
+
* If the given field uses one of the well-known wrapper types, return
|
|
15
|
+
* the primitive type it wraps.
|
|
16
|
+
*/
|
|
9
17
|
unwrapField(value: T): U | null | undefined;
|
|
10
18
|
}
|
|
11
19
|
/**
|
|
@@ -13,6 +21,11 @@ export interface FieldWrapper<T = any, U = any> {
|
|
|
13
21
|
* message. This function is idempotent.
|
|
14
22
|
*/
|
|
15
23
|
export declare function wrapField<T>(fieldWrapper: FieldWrapper<T> | undefined, value: any): T;
|
|
24
|
+
/**
|
|
25
|
+
* Wrap a primitive message field value in its corresponding wrapper
|
|
26
|
+
* message. This function is idempotent.
|
|
27
|
+
*/
|
|
28
|
+
export declare function unwrapField<T, U = T>(fieldWrapper: FieldWrapper<T> | undefined, value: any): U;
|
|
16
29
|
/**
|
|
17
30
|
* If the given field uses one of the well-known wrapper types, return
|
|
18
31
|
* the primitive type it wraps.
|
package/dist/field-wrapper.js
CHANGED
|
@@ -9,6 +9,13 @@ export function wrapField(fieldWrapper, value) {
|
|
|
9
9
|
}
|
|
10
10
|
return fieldWrapper.wrapField(value);
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Wrap a primitive message field value in its corresponding wrapper
|
|
14
|
+
* message. This function is idempotent.
|
|
15
|
+
*/
|
|
16
|
+
export function unwrapField(fieldWrapper, value) {
|
|
17
|
+
return !!fieldWrapper ? fieldWrapper.unwrapField(value) : value;
|
|
18
|
+
}
|
|
12
19
|
/**
|
|
13
20
|
* If the given field uses one of the well-known wrapper types, return
|
|
14
21
|
* the primitive type it wraps.
|
package/dist/field.d.ts
CHANGED
|
@@ -113,7 +113,7 @@ export declare function newFieldList(fields: FieldListSource, packedByDefault: b
|
|
|
113
113
|
/**
|
|
114
114
|
* Returns true if the field is set.
|
|
115
115
|
*/
|
|
116
|
-
export declare function isFieldSet(field: FieldInfo, target: Record<string, any>): boolean;
|
|
116
|
+
export declare function isFieldSet(field: FieldInfo, target: Record<string, any> | null | undefined): boolean;
|
|
117
117
|
/**
|
|
118
118
|
* Returns the JSON name for a protobuf field, exactly like protoc does.
|
|
119
119
|
*/
|
package/dist/field.js
CHANGED
|
@@ -95,28 +95,32 @@ export function newFieldList(fields, packedByDefault) {
|
|
|
95
95
|
*/
|
|
96
96
|
export function isFieldSet(field, target) {
|
|
97
97
|
const localName = field.localName;
|
|
98
|
+
if (!target) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
98
101
|
if (field.repeated) {
|
|
99
|
-
return target[localName]
|
|
102
|
+
return !!target[localName]?.length;
|
|
100
103
|
}
|
|
101
104
|
if (field.oneof) {
|
|
102
|
-
return target[field.oneof.localName]
|
|
105
|
+
return target[field.oneof.localName]?.case === localName; // eslint-disable-line @typescript-eslint/no-unsafe-member-access
|
|
103
106
|
}
|
|
104
107
|
switch (field.kind) {
|
|
105
108
|
case "enum":
|
|
106
109
|
case "scalar":
|
|
107
110
|
if (field.opt || field.req) {
|
|
108
111
|
// explicit presence
|
|
109
|
-
return target[localName]
|
|
112
|
+
return target[localName] != null;
|
|
110
113
|
}
|
|
111
114
|
// implicit presence
|
|
112
115
|
if (field.kind == "enum") {
|
|
113
116
|
return target[localName] !== field.T.values[0].no;
|
|
114
117
|
}
|
|
118
|
+
// not zero value
|
|
115
119
|
return !isScalarZeroValue(field.T, target[localName]);
|
|
116
120
|
case "message":
|
|
117
|
-
return target[localName]
|
|
121
|
+
return target[localName] != null;
|
|
118
122
|
case "map":
|
|
119
|
-
return Object.keys(target[localName]).length
|
|
123
|
+
return (target[localName] != null && !!Object.keys(target[localName]).length); // eslint-disable-line @typescript-eslint/no-unsafe-argument
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
/**
|
|
@@ -118,7 +118,8 @@ export type Timestamp = Message<{
|
|
|
118
118
|
declare const Timestamp_Wkt: {
|
|
119
119
|
fromJson(json: JsonValue): Timestamp;
|
|
120
120
|
toJson(msg: Timestamp): JsonValue;
|
|
121
|
-
toDate(msg: Timestamp): Date;
|
|
121
|
+
toDate(msg: Timestamp | null | undefined): Date | null;
|
|
122
|
+
fromDate(value: Date | null | undefined): Timestamp;
|
|
122
123
|
};
|
|
123
124
|
export declare const Timestamp: MessageType<Timestamp> & typeof Timestamp_Wkt;
|
|
124
125
|
export {};
|
|
@@ -89,8 +89,20 @@ const Timestamp_Wkt = {
|
|
|
89
89
|
return new Date(ms).toISOString().replace(".000Z", z);
|
|
90
90
|
},
|
|
91
91
|
toDate(msg) {
|
|
92
|
+
if (!msg?.seconds && !msg?.nanos) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
92
95
|
return new Date(Number(msg.seconds ?? 0) * 1000 + Math.ceil((msg.nanos ?? 0) / 1000000));
|
|
93
96
|
},
|
|
97
|
+
fromDate(value) {
|
|
98
|
+
if (value == null) {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
const ms = value.getTime();
|
|
102
|
+
const seconds = Math.floor(ms / 1000);
|
|
103
|
+
const nanos = (ms % 1000) * 1000000;
|
|
104
|
+
return { seconds: protoInt64.parse(seconds), nanos: nanos };
|
|
105
|
+
},
|
|
94
106
|
};
|
|
95
107
|
// Timestamp contains the message type declaration for Timestamp.
|
|
96
108
|
export const Timestamp = createMessageType({
|
package/dist/json.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { isFieldSet, resolveMessageType, } from "./field.js";
|
|
2
2
|
import { assert, assertFloat32, assertInt32, assertUInt32 } from "./assert.js";
|
|
3
|
-
import { LongType, ScalarType, scalarZeroValue, } from "./scalar.js";
|
|
4
|
-
import { wrapField } from "./field-wrapper.js";
|
|
3
|
+
import { LongType, ScalarType, isScalarZeroValue, normalizeScalarValue, scalarZeroValue, } from "./scalar.js";
|
|
4
|
+
import { unwrapField, wrapField } from "./field-wrapper.js";
|
|
5
|
+
import { enumZeroValue, normalizeEnumValue } from "./enum.js";
|
|
5
6
|
import { protoInt64 } from "./proto-int64.js";
|
|
6
7
|
import { protoBase64 } from "./proto-base64.js";
|
|
7
8
|
// Default options for parsing JSON.
|
|
@@ -65,7 +66,7 @@ function readMessage(fields, typeName, json, options, message) {
|
|
|
65
66
|
return message;
|
|
66
67
|
}
|
|
67
68
|
function writeMessage(message, fields, options) {
|
|
68
|
-
const json =
|
|
69
|
+
const json = Object.create(null);
|
|
69
70
|
let field;
|
|
70
71
|
try {
|
|
71
72
|
for (field of fields.byNumber()) {
|
|
@@ -123,7 +124,7 @@ function readField(target, jsonValue, field, options) {
|
|
|
123
124
|
switch (field.kind) {
|
|
124
125
|
case "message":
|
|
125
126
|
const messageType = resolveMessageType(field.T);
|
|
126
|
-
targetArray.push(messageType.fromJson(jsonItem, options));
|
|
127
|
+
targetArray.push(unwrapField(messageType.fieldWrapper, messageType.fromJson(jsonItem, options)));
|
|
127
128
|
break;
|
|
128
129
|
case "enum":
|
|
129
130
|
const enumValue = readEnum(field.T, jsonItem, options.ignoreUnknownFields, true);
|
|
@@ -155,7 +156,7 @@ function readField(target, jsonValue, field, options) {
|
|
|
155
156
|
}
|
|
156
157
|
var targetMap = target[localName];
|
|
157
158
|
if (typeof targetMap !== "object") {
|
|
158
|
-
targetMap = target[localName] =
|
|
159
|
+
targetMap = target[localName] = Object.create(null);
|
|
159
160
|
}
|
|
160
161
|
for (const [jsonMapKey, jsonMapValue] of Object.entries(jsonValue)) {
|
|
161
162
|
if (jsonMapValue === null) {
|
|
@@ -210,11 +211,7 @@ function readField(target, jsonValue, field, options) {
|
|
|
210
211
|
messageType.typeName != "google.protobuf.Value") {
|
|
211
212
|
return;
|
|
212
213
|
}
|
|
213
|
-
|
|
214
|
-
target[localName] = currentValue = messageType.fromJson(jsonValue, options);
|
|
215
|
-
if (messageType.fieldWrapper && !field.oneof) {
|
|
216
|
-
target[localName] = messageType.fieldWrapper.unwrapField(currentValue);
|
|
217
|
-
}
|
|
214
|
+
target[localName] = unwrapField(messageType.fieldWrapper, messageType.fromJson(jsonValue, options));
|
|
218
215
|
break;
|
|
219
216
|
case "enum":
|
|
220
217
|
const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields, false);
|
|
@@ -418,7 +415,7 @@ export function clearField(field, target) {
|
|
|
418
415
|
else {
|
|
419
416
|
switch (field.kind) {
|
|
420
417
|
case "map":
|
|
421
|
-
target[localName] =
|
|
418
|
+
target[localName] = Object.create(null);
|
|
422
419
|
break;
|
|
423
420
|
case "enum":
|
|
424
421
|
target[localName] = implicitPresence ? field.T.values[0].no : undefined;
|
|
@@ -436,7 +433,6 @@ export function clearField(field, target) {
|
|
|
436
433
|
// Decide whether an unset field should be emitted with JSON write option `emitDefaultValues`
|
|
437
434
|
function canEmitFieldDefaultValue(field) {
|
|
438
435
|
if (field.repeated || field.kind == "map") {
|
|
439
|
-
// maps are {}, repeated fields are []
|
|
440
436
|
return true;
|
|
441
437
|
}
|
|
442
438
|
if (field.oneof) {
|
|
@@ -456,9 +452,9 @@ function canEmitFieldDefaultValue(field) {
|
|
|
456
452
|
}
|
|
457
453
|
function writeField(field, value, options) {
|
|
458
454
|
if (field.kind == "map") {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const entries = Object.entries(value);
|
|
455
|
+
const jsonObj = Object.create(null);
|
|
456
|
+
assert(!value || typeof value === "object");
|
|
457
|
+
const entries = value ? Object.entries(value) : [];
|
|
462
458
|
switch (field.V.kind) {
|
|
463
459
|
case "scalar":
|
|
464
460
|
for (const [entryKey, entryValue] of entries) {
|
|
@@ -485,24 +481,28 @@ function writeField(field, value, options) {
|
|
|
485
481
|
: undefined;
|
|
486
482
|
}
|
|
487
483
|
if (field.repeated) {
|
|
488
|
-
assert(Array.isArray(value));
|
|
484
|
+
assert(!value || Array.isArray(value));
|
|
489
485
|
const jsonArr = [];
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
486
|
+
const valueArr = value;
|
|
487
|
+
if (valueArr && valueArr.length) {
|
|
488
|
+
switch (field.kind) {
|
|
489
|
+
case "scalar":
|
|
490
|
+
for (let i = 0; i < valueArr.length; i++) {
|
|
491
|
+
jsonArr.push(writeScalar(field.T, valueArr[i]));
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
case "enum":
|
|
495
|
+
for (let i = 0; i < valueArr.length; i++) {
|
|
496
|
+
jsonArr.push(writeEnum(field.T, valueArr[i], options.enumAsInteger));
|
|
497
|
+
}
|
|
498
|
+
break;
|
|
499
|
+
case "message":
|
|
500
|
+
const messageType = resolveMessageType(field.T);
|
|
501
|
+
for (let i = 0; i < valueArr.length; i++) {
|
|
502
|
+
jsonArr.push(messageType.toJson(wrapField(messageType.fieldWrapper, valueArr[i])));
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
506
|
}
|
|
507
507
|
return options.emitDefaultValues || jsonArr.length > 0 ?
|
|
508
508
|
jsonArr
|
|
@@ -510,10 +510,22 @@ function writeField(field, value, options) {
|
|
|
510
510
|
}
|
|
511
511
|
switch (field.kind) {
|
|
512
512
|
case "scalar":
|
|
513
|
+
const scalarValue = normalizeScalarValue(field.T, value, false);
|
|
514
|
+
if (!options.emitDefaultValues
|
|
515
|
+
&& isScalarZeroValue(field.T, scalarValue)) {
|
|
516
|
+
return undefined;
|
|
517
|
+
}
|
|
513
518
|
return writeScalar(field.T, value);
|
|
514
519
|
case "enum":
|
|
520
|
+
const enumValue = normalizeEnumValue(field.T, value);
|
|
521
|
+
if (!options.emitDefaultValues && enumZeroValue(field.T) === enumValue) {
|
|
522
|
+
return undefined;
|
|
523
|
+
}
|
|
515
524
|
return writeEnum(field.T, value, options.enumAsInteger);
|
|
516
525
|
case "message":
|
|
526
|
+
if (!options.emitDefaultValues && value == null) {
|
|
527
|
+
return undefined;
|
|
528
|
+
}
|
|
517
529
|
const messageType = resolveMessageType(field.T);
|
|
518
530
|
return messageType.toJson(wrapField(messageType.fieldWrapper, value));
|
|
519
531
|
}
|
package/dist/message.d.ts
CHANGED
|
@@ -111,7 +111,7 @@ export type MessageTypeParams<T extends Message<T>> = Pick<MessageType<T>, "fiel
|
|
|
111
111
|
*/
|
|
112
112
|
export declare function createMessageType<T extends Message<T>, E extends Record<string, Function> = {}>(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
|
-
export declare function cloneMessage<T extends Message<T>>(message: T, fields: FieldList): T;
|
|
114
|
+
export declare function cloneMessage<T extends Message<T>>(message: T | null | undefined, fields: FieldList): T | null;
|
|
115
115
|
/**
|
|
116
116
|
* createCompleteMessage recursively builds a message filled with zero values based on the given FieldList.
|
|
117
117
|
*/
|
package/dist/message.js
CHANGED
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
import { newFieldList, resolveMessageType, } from "./field.js";
|
|
15
15
|
import { applyPartialMessage } from "./partial.js";
|
|
16
|
-
import { ScalarType, scalarEquals } from "./scalar.js";
|
|
16
|
+
import { LongType, ScalarType, scalarEquals, scalarZeroValue, } from "./scalar.js";
|
|
17
17
|
import { binaryReadMessage, binaryWriteMessage, binaryMakeReadOptions, binaryMakeWriteOptions, } from "./binary.js";
|
|
18
18
|
import { jsonReadMessage, jsonWriteMessage, jsonMakeReadOptions, jsonMakeWriteOptions, } from "./json.js";
|
|
19
|
+
import { throwSanitizeKey } from "./names.js";
|
|
20
|
+
import { enumZeroValue } from "./enum.js";
|
|
19
21
|
/**
|
|
20
22
|
* createMessageType creates a new message type.
|
|
21
23
|
*
|
|
@@ -52,7 +54,7 @@ export function createMessageType(params, exts) {
|
|
|
52
54
|
const message = {};
|
|
53
55
|
if (bytes && bytes.length) {
|
|
54
56
|
const opt = binaryMakeReadOptions(options);
|
|
55
|
-
binaryReadMessage(message, fields, opt.readerFactory(bytes), bytes.byteLength, opt, delimitedMessageEncoding);
|
|
57
|
+
binaryReadMessage(message, fields, opt.readerFactory(bytes), bytes.byteLength, opt, delimitedMessageEncoding ?? false);
|
|
56
58
|
}
|
|
57
59
|
return message;
|
|
58
60
|
},
|
|
@@ -109,9 +111,12 @@ export function compareMessages(fields, a, b) {
|
|
|
109
111
|
const va = a[m.localName];
|
|
110
112
|
const vb = b[m.localName];
|
|
111
113
|
if (m.repeated) {
|
|
112
|
-
if (va
|
|
114
|
+
if ((va?.length ?? 0) !== (vb?.length ?? 0)) {
|
|
113
115
|
return false;
|
|
114
116
|
}
|
|
117
|
+
if (!va?.length) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
115
120
|
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- repeated fields are never "map"
|
|
116
121
|
switch (m.kind) {
|
|
117
122
|
case "message":
|
|
@@ -132,9 +137,12 @@ export function compareMessages(fields, a, b) {
|
|
|
132
137
|
case "scalar":
|
|
133
138
|
return scalarEquals(m.T, va, vb);
|
|
134
139
|
case "oneof":
|
|
135
|
-
if (va
|
|
140
|
+
if (va?.case !== vb?.case) {
|
|
136
141
|
return false;
|
|
137
142
|
}
|
|
143
|
+
if (va == null) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
138
146
|
const s = m.findField(va.case);
|
|
139
147
|
if (s === undefined) {
|
|
140
148
|
return true;
|
|
@@ -165,61 +173,12 @@ export function compareMessages(fields, a, b) {
|
|
|
165
173
|
}
|
|
166
174
|
});
|
|
167
175
|
}
|
|
168
|
-
// clone a single field value - i.e. the element type of repeated fields, the value type of maps
|
|
169
|
-
function cloneSingularField(value, fieldInfo) {
|
|
170
|
-
if (value === undefined) {
|
|
171
|
-
return value;
|
|
172
|
-
}
|
|
173
|
-
if (fieldInfo.kind === "message") {
|
|
174
|
-
return cloneMessage(value, resolveMessageType(fieldInfo.T).fields);
|
|
175
|
-
}
|
|
176
|
-
if (fieldInfo.kind === "oneof") {
|
|
177
|
-
if (value.case === undefined) {
|
|
178
|
-
return undefined;
|
|
179
|
-
}
|
|
180
|
-
const selectedField = fieldInfo.findField(value.case);
|
|
181
|
-
if (!selectedField) {
|
|
182
|
-
throw new Error(`Invalid oneof case "${value.case}" for ${fieldInfo.name}`);
|
|
183
|
-
}
|
|
184
|
-
return {
|
|
185
|
-
case: value.case,
|
|
186
|
-
value: cloneSingularField(value.value, selectedField),
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
if (value instanceof Uint8Array) {
|
|
190
|
-
const c = new Uint8Array(value.byteLength);
|
|
191
|
-
c.set(value);
|
|
192
|
-
return c;
|
|
193
|
-
}
|
|
194
|
-
return value;
|
|
195
|
-
}
|
|
196
|
-
// TODO use isFieldSet() here to support future field presence
|
|
197
176
|
export function cloneMessage(message, fields) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const source = message[member.localName];
|
|
201
|
-
let copy;
|
|
202
|
-
if (member.repeated) {
|
|
203
|
-
copy = source.map((v) => cloneSingularField(v, member));
|
|
204
|
-
}
|
|
205
|
-
else if (member.kind == "map") {
|
|
206
|
-
copy = {};
|
|
207
|
-
for (const [key, v] of Object.entries(source)) {
|
|
208
|
-
copy[key] = cloneSingularField(v, member);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
else if (member.kind == "oneof") {
|
|
212
|
-
const f = member.findField(source.case);
|
|
213
|
-
copy =
|
|
214
|
-
f ?
|
|
215
|
-
{ case: source.case, value: cloneSingularField(source.value, member) }
|
|
216
|
-
: { case: undefined };
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
copy = cloneSingularField(source, member);
|
|
220
|
-
}
|
|
221
|
-
clone[member.localName] = copy;
|
|
177
|
+
if (message == null) {
|
|
178
|
+
return null;
|
|
222
179
|
}
|
|
180
|
+
const clone = Object.create(null);
|
|
181
|
+
applyPartialMessage(message, clone, fields, true);
|
|
223
182
|
return clone;
|
|
224
183
|
}
|
|
225
184
|
/**
|
|
@@ -227,67 +186,42 @@ export function cloneMessage(message, fields) {
|
|
|
227
186
|
*/
|
|
228
187
|
export function createCompleteMessage(fields) {
|
|
229
188
|
const message = {};
|
|
230
|
-
for (const field of fields.
|
|
231
|
-
const fieldKind = field
|
|
189
|
+
for (const field of fields.byMember()) {
|
|
190
|
+
const { localName, kind: fieldKind } = field;
|
|
191
|
+
throwSanitizeKey(localName);
|
|
232
192
|
switch (fieldKind) {
|
|
193
|
+
case "oneof":
|
|
194
|
+
message[localName] = Object.create(null);
|
|
195
|
+
message[localName].case = undefined;
|
|
196
|
+
break;
|
|
233
197
|
case "scalar":
|
|
234
198
|
if (field.repeated) {
|
|
235
|
-
message[
|
|
199
|
+
message[localName] = [];
|
|
236
200
|
}
|
|
237
201
|
else {
|
|
238
|
-
|
|
239
|
-
case ScalarType.DOUBLE:
|
|
240
|
-
case ScalarType.FLOAT:
|
|
241
|
-
message[field.localName] = 0;
|
|
242
|
-
break;
|
|
243
|
-
case ScalarType.INT64:
|
|
244
|
-
case ScalarType.UINT64:
|
|
245
|
-
case ScalarType.INT32:
|
|
246
|
-
case ScalarType.FIXED64:
|
|
247
|
-
case ScalarType.FIXED32:
|
|
248
|
-
case ScalarType.UINT32:
|
|
249
|
-
case ScalarType.SFIXED32:
|
|
250
|
-
case ScalarType.SFIXED64:
|
|
251
|
-
case ScalarType.SINT32:
|
|
252
|
-
case ScalarType.SINT64:
|
|
253
|
-
message[field.localName] = 0;
|
|
254
|
-
break;
|
|
255
|
-
case ScalarType.BOOL:
|
|
256
|
-
message[field.localName] = false;
|
|
257
|
-
break;
|
|
258
|
-
case ScalarType.STRING:
|
|
259
|
-
message[field.localName] = "";
|
|
260
|
-
break;
|
|
261
|
-
case ScalarType.BYTES:
|
|
262
|
-
message[field.localName] =
|
|
263
|
-
new Uint8Array();
|
|
264
|
-
break;
|
|
265
|
-
}
|
|
202
|
+
message[localName] = scalarZeroValue(field.T, LongType.BIGINT);
|
|
266
203
|
}
|
|
267
204
|
break;
|
|
268
205
|
case "enum":
|
|
269
|
-
|
|
270
|
-
message[field.localName] = [];
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
message[field.localName] = 0;
|
|
274
|
-
}
|
|
206
|
+
message[localName] = field.repeated ? [] : enumZeroValue(field.T);
|
|
275
207
|
break;
|
|
276
208
|
case "message":
|
|
209
|
+
// oneofs are handled above
|
|
277
210
|
if (field.oneof) {
|
|
278
|
-
|
|
279
|
-
continue;
|
|
211
|
+
break;
|
|
280
212
|
}
|
|
281
|
-
const messageType = resolveMessageType(field.T);
|
|
282
213
|
if (field.repeated) {
|
|
283
|
-
message[
|
|
284
|
-
|
|
285
|
-
else {
|
|
286
|
-
message[field.localName] = createCompleteMessage(messageType.fields);
|
|
214
|
+
message[localName] = [];
|
|
215
|
+
break;
|
|
287
216
|
}
|
|
217
|
+
const messageType = resolveMessageType(field.T);
|
|
218
|
+
message[localName] =
|
|
219
|
+
!!messageType.fieldWrapper ?
|
|
220
|
+
messageType.fieldWrapper.unwrapField(null)
|
|
221
|
+
: createCompleteMessage(messageType.fields);
|
|
288
222
|
break;
|
|
289
223
|
case "map":
|
|
290
|
-
message[
|
|
224
|
+
message[localName] = Object.create(null);
|
|
291
225
|
break;
|
|
292
226
|
default:
|
|
293
227
|
field;
|
package/dist/partial.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { FieldList, MapValueInfo } from "./field.js";
|
|
2
2
|
import { Message } from "./message.js";
|
|
3
|
-
export declare function applyPartialMessage<T extends Message<T>>(source: Message<T> | undefined, target: Message<T>, fields: FieldList): void;
|
|
4
|
-
export declare function applyPartialMap(sourceMap: Record<string, any> | undefined, targetMap: Record<string, any>, value: MapValueInfo): void;
|
|
3
|
+
export declare function applyPartialMessage<T extends Message<T>>(source: Message<T> | undefined, target: Message<T>, fields: FieldList, clone?: boolean): void;
|
|
4
|
+
export declare function applyPartialMap(sourceMap: Record<string, any> | undefined, targetMap: Record<string, any>, value: MapValueInfo, clone: boolean): void;
|
package/dist/partial.js
CHANGED
|
@@ -4,7 +4,9 @@ import { createCompleteMessage } from "./message.js";
|
|
|
4
4
|
import { throwSanitizeKey } from "./names.js";
|
|
5
5
|
import { normalizeScalarValue } from "./scalar.js";
|
|
6
6
|
// applyPartialMessage applies a partial source message to a target message.
|
|
7
|
-
|
|
7
|
+
//
|
|
8
|
+
// if clone is set: values are deep-copied including Uint8Arrays.
|
|
9
|
+
export function applyPartialMessage(source, target, fields, clone = false) {
|
|
8
10
|
if (source == null || target == null) {
|
|
9
11
|
return;
|
|
10
12
|
}
|
|
@@ -62,7 +64,7 @@ export function applyPartialMessage(source, target, fields) {
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
else if (sourceField.kind === "scalar") {
|
|
65
|
-
dv.value = normalizeScalarValue(sourceField.T, sv);
|
|
67
|
+
dv.value = normalizeScalarValue(sourceField.T, sv, clone);
|
|
66
68
|
}
|
|
67
69
|
else {
|
|
68
70
|
dv.value = sv;
|
|
@@ -77,10 +79,10 @@ export function applyPartialMessage(source, target, fields) {
|
|
|
77
79
|
if (dst == null || !Array.isArray(dst)) {
|
|
78
80
|
dst = t[localName] = [];
|
|
79
81
|
}
|
|
80
|
-
dst.push(...sourceValue.map((v) => normalizeScalarValue(member.T, v)));
|
|
82
|
+
dst.push(...sourceValue.map((v) => normalizeScalarValue(member.T, v, clone)));
|
|
81
83
|
break;
|
|
82
84
|
}
|
|
83
|
-
t[localName] = normalizeScalarValue(member.T, sourceValue);
|
|
85
|
+
t[localName] = normalizeScalarValue(member.T, sourceValue, clone);
|
|
84
86
|
break;
|
|
85
87
|
case "enum":
|
|
86
88
|
t[localName] = normalizeEnumValue(member.T, sourceValue);
|
|
@@ -93,7 +95,7 @@ export function applyPartialMessage(source, target, fields) {
|
|
|
93
95
|
if (typeof tMap !== "object") {
|
|
94
96
|
tMap = t[localName] = Object.create(null);
|
|
95
97
|
}
|
|
96
|
-
applyPartialMap(sourceValue, tMap, member.V);
|
|
98
|
+
applyPartialMap(sourceValue, tMap, member.V, clone);
|
|
97
99
|
break;
|
|
98
100
|
case "message":
|
|
99
101
|
const mt = resolveMessageType(member.T);
|
|
@@ -137,7 +139,7 @@ export function applyPartialMessage(source, target, fields) {
|
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
// applyPartialMap applies a partial source map to a target map.
|
|
140
|
-
export function applyPartialMap(sourceMap, targetMap, value) {
|
|
142
|
+
export function applyPartialMap(sourceMap, targetMap, value, clone) {
|
|
141
143
|
if (sourceMap == null) {
|
|
142
144
|
return;
|
|
143
145
|
}
|
|
@@ -149,7 +151,7 @@ export function applyPartialMap(sourceMap, targetMap, value) {
|
|
|
149
151
|
for (const [k, v] of Object.entries(sourceMap)) {
|
|
150
152
|
throwSanitizeKey(k);
|
|
151
153
|
if (v !== undefined) {
|
|
152
|
-
targetMap[k] = normalizeScalarValue(value.T, v);
|
|
154
|
+
targetMap[k] = normalizeScalarValue(value.T, v, clone);
|
|
153
155
|
}
|
|
154
156
|
else {
|
|
155
157
|
delete targetMap[k];
|
|
@@ -384,9 +384,17 @@ function generateWktMethods(schema, f, message, ref) {
|
|
|
384
384
|
f.print(" }");
|
|
385
385
|
f.print(` return new Date(ms).toISOString().replace(".000Z", z);`);
|
|
386
386
|
f.print(" },");
|
|
387
|
-
f.print(" toDate(msg: ", message, "): Date {");
|
|
387
|
+
f.print(" toDate(msg: ", message, " | null | undefined): Date | null {");
|
|
388
|
+
f.print(" if (!msg?.", localName(ref.seconds), " && !msg?.", localName(ref.nanos), ") { return null; }");
|
|
388
389
|
f.print(" return new Date(Number(msg.", localName(ref.seconds), " ?? 0) * 1000 + Math.ceil((msg.", localName(ref.nanos), " ?? 0) / 1000000));");
|
|
389
390
|
f.print(" },");
|
|
391
|
+
f.print(" fromDate(value: Date | null | undefined): ", message, " {");
|
|
392
|
+
f.print(" if (value == null) { return {}; }");
|
|
393
|
+
f.print(" const ms = value.getTime();");
|
|
394
|
+
f.print(" const seconds = Math.floor(ms / 1000);");
|
|
395
|
+
f.print(" const nanos = (ms % 1000) * 1000000;");
|
|
396
|
+
f.print(" return { ", localName(ref.seconds), ": ", protoInt64, ".parse(seconds), ", localName(ref.nanos), ": nanos };");
|
|
397
|
+
f.print(" },");
|
|
390
398
|
break;
|
|
391
399
|
case "google.protobuf.Duration":
|
|
392
400
|
f.print(" fromJson(json: ", JsonValue, " | null | undefined, _options?: Partial<", JsonReadOptions, ">): ", message, " {");
|
|
@@ -552,6 +560,30 @@ function generateWktMethods(schema, f, message, ref) {
|
|
|
552
560
|
}
|
|
553
561
|
function generateWktFieldWrapper(f, message, ref) {
|
|
554
562
|
switch (ref?.typeName) {
|
|
563
|
+
/* TODO Wrap Timestamp => Date
|
|
564
|
+
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;
|
|
585
|
+
}
|
|
586
|
+
*/
|
|
555
587
|
case "google.protobuf.DoubleValue":
|
|
556
588
|
case "google.protobuf.FloatValue":
|
|
557
589
|
case "google.protobuf.Int64Value":
|
package/dist/scalar.d.ts
CHANGED
|
@@ -72,6 +72,7 @@ export declare function isScalarZeroValue(type: ScalarType, value: unknown): boo
|
|
|
72
72
|
* Zero or null is cast to the zero value.
|
|
73
73
|
* Bytes is cast to a Uint8Array.
|
|
74
74
|
* The BigInt long type is used.
|
|
75
|
+
* If clone is set, Uint8Array will always be copied to a new value.
|
|
75
76
|
*/
|
|
76
|
-
export declare function normalizeScalarValue<T>(type: ScalarType, value: T | null | undefined, longType?: LongType): T;
|
|
77
|
-
export declare function toU8Arr(input: ArrayLike<number
|
|
77
|
+
export declare function normalizeScalarValue<T>(type: ScalarType, value: T | null | undefined, clone: boolean, longType?: LongType): T;
|
|
78
|
+
export declare function toU8Arr(input: ArrayLike<number>, clone: boolean): Uint8Array;
|
package/dist/scalar.js
CHANGED
|
@@ -166,21 +166,22 @@ export function isScalarZeroValue(type, value) {
|
|
|
166
166
|
* Zero or null is cast to the zero value.
|
|
167
167
|
* Bytes is cast to a Uint8Array.
|
|
168
168
|
* The BigInt long type is used.
|
|
169
|
+
* If clone is set, Uint8Array will always be copied to a new value.
|
|
169
170
|
*/
|
|
170
|
-
export function normalizeScalarValue(type, value, longType = LongType.BIGINT) {
|
|
171
|
+
export function normalizeScalarValue(type, value, clone, longType = LongType.BIGINT) {
|
|
171
172
|
if (value == null) {
|
|
172
173
|
return scalarZeroValue(type, longType);
|
|
173
174
|
}
|
|
174
175
|
if (type === ScalarType.BYTES) {
|
|
175
|
-
return toU8Arr(value);
|
|
176
|
+
return toU8Arr(value, clone);
|
|
176
177
|
}
|
|
177
178
|
if (isScalarZeroValue(type, value)) {
|
|
178
179
|
return scalarZeroValue(type, longType);
|
|
179
180
|
}
|
|
180
|
-
// TODO: enforce correct type for other values as well.
|
|
181
181
|
return value;
|
|
182
182
|
}
|
|
183
183
|
// converts any ArrayLike<number> to Uint8Array if necessary.
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
// if clone is set, force clones the array to a copy.
|
|
185
|
+
export function toU8Arr(input, clone) {
|
|
186
|
+
return !clone && input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
186
187
|
}
|
package/example/tsconfig.json
CHANGED
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.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "git+ssh://git@github.com/aperturerobotics/protobuf-es-lite.git"
|
|
@@ -49,18 +49,19 @@
|
|
|
49
49
|
"scripts": {
|
|
50
50
|
"clean": "rimraf ./dist",
|
|
51
51
|
"build": "npm run clean && tsc --project tsconfig.json --outDir ./dist",
|
|
52
|
-
"release:version": "npm version patch -m \"release: v%s\" --no-git-tag-version",
|
|
53
|
-
"release:version:minor": "npm version minor -m \"release: v%s\" --no-git-tag-version",
|
|
54
|
-
"release:commit": "git reset && git add package.json && git commit -s -m \"release: v$npm_package_version\" && git tag v$npm_package_version",
|
|
55
|
-
"release:publish": "git push && git push --tags && npm run build && npm publish",
|
|
56
|
-
"release": "npm run release:version && npm run release:commit",
|
|
57
|
-
"release:minor": "npm run release:version:minor && npm run release:commit",
|
|
58
52
|
"typecheck": "tsc --noEmit --project tsconfig.json --outDir ./dist",
|
|
59
53
|
"gen": "npm run build && npm run gen:wkt && npm run gen:example",
|
|
60
54
|
"gen:wkt": "cd ./src && bash gen.bash",
|
|
61
55
|
"gen:example": "bash gen.bash ./example/example.proto",
|
|
62
56
|
"format": "prettier --write './src/**/(*.ts|*.tsx|*.html|*.css|*.scss)'",
|
|
63
|
-
"precommit": "lint-staged"
|
|
57
|
+
"precommit": "lint-staged",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"release:version": "npm version patch -m \"release: v%s\" --no-git-tag-version",
|
|
60
|
+
"release:version:minor": "npm version minor -m \"release: v%s\" --no-git-tag-version",
|
|
61
|
+
"release:commit": "git reset && git add package.json && git commit -s -m \"release: v$npm_package_version\" && git tag v$npm_package_version",
|
|
62
|
+
"release:publish": "git push && git push --tags && npm run build && npm publish",
|
|
63
|
+
"release": "npm run release:version && npm run release:commit",
|
|
64
|
+
"release:minor": "npm run release:version:minor && npm run release:commit"
|
|
64
65
|
},
|
|
65
66
|
"preferUnplugged": true,
|
|
66
67
|
"pre-commit": [
|
|
@@ -76,7 +77,8 @@
|
|
|
76
77
|
"pre-commit": "^1.2.2",
|
|
77
78
|
"prettier": "^3.2.5",
|
|
78
79
|
"rimraf": "^5.0.5",
|
|
79
|
-
"typescript": "^5.4.5"
|
|
80
|
+
"typescript": "^5.4.5",
|
|
81
|
+
"vitest": "^1.5.3"
|
|
80
82
|
},
|
|
81
83
|
"lint-staged": {
|
|
82
84
|
"package.json": "prettier --write",
|