@aptre/protobuf-es-lite 0.3.1 → 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/enum.d.ts +11 -1
- package/dist/enum.js +56 -7
- package/dist/field-wrapper.d.ts +13 -0
- package/dist/field-wrapper.js +7 -0
- package/dist/field.d.ts +38 -12
- 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/google/protobuf/wrappers.pb.js +9 -9
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/json.js +44 -32
- package/dist/message.d.ts +8 -4
- package/dist/message.js +44 -105
- package/dist/names.d.ts +2 -0
- package/dist/names.js +13 -20
- package/dist/partial.d.ts +3 -2
- package/dist/partial.js +164 -67
- package/dist/protoc-gen-es-lite/typescript.js +34 -2
- package/dist/scalar.d.ts +9 -0
- package/dist/scalar.js +24 -0
- package/dist/util.d.ts +1 -1
- package/dist/util.js +2 -2
- package/dist/zero-value.d.ts +23 -0
- package/dist/zero-value.js +88 -0
- package/example/tsconfig.json +1 -3
- package/package.json +11 -9
package/dist/message.d.ts
CHANGED
|
@@ -38,10 +38,14 @@ export interface MessageType<T extends Message<T> = AnyMessage> {
|
|
|
38
38
|
* When used as a field, unwrap this message to a simple value.
|
|
39
39
|
*/
|
|
40
40
|
readonly fieldWrapper?: FieldWrapper<T>;
|
|
41
|
+
/**
|
|
42
|
+
* Create a new empty instance of this message applying the partial message.
|
|
43
|
+
*/
|
|
44
|
+
create(partial?: Message<T>): Message<T>;
|
|
41
45
|
/**
|
|
42
46
|
* Create a new instance of this message with zero values for fields.
|
|
43
47
|
*/
|
|
44
|
-
|
|
48
|
+
createComplete(partial?: Message<T>): CompleteMessage<T>;
|
|
45
49
|
/**
|
|
46
50
|
* Create a deep copy.
|
|
47
51
|
*/
|
|
@@ -107,8 +111,8 @@ export type MessageTypeParams<T extends Message<T>> = Pick<MessageType<T>, "fiel
|
|
|
107
111
|
*/
|
|
108
112
|
export declare function createMessageType<T extends Message<T>, E extends Record<string, Function> = {}>(params: MessageTypeParams<T>, exts?: E): MessageType<T> & E;
|
|
109
113
|
export declare function compareMessages<T extends Message<T>>(fields: FieldList, a: T | undefined | null, b: T | undefined | null): boolean;
|
|
110
|
-
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;
|
|
111
115
|
/**
|
|
112
|
-
*
|
|
116
|
+
* createCompleteMessage recursively builds a message filled with zero values based on the given FieldList.
|
|
113
117
|
*/
|
|
114
|
-
export declare function
|
|
118
|
+
export declare function createCompleteMessage<T extends Message<T>>(fields: FieldList): CompleteMessage<T>;
|
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
|
*
|
|
@@ -30,7 +32,12 @@ export function createMessageType(params, exts) {
|
|
|
30
32
|
fields,
|
|
31
33
|
fieldWrapper,
|
|
32
34
|
create(partial) {
|
|
33
|
-
const message =
|
|
35
|
+
const message = Object.create(null);
|
|
36
|
+
applyPartialMessage(partial, message, fields);
|
|
37
|
+
return message;
|
|
38
|
+
},
|
|
39
|
+
createComplete(partial) {
|
|
40
|
+
const message = createCompleteMessage(fields);
|
|
34
41
|
applyPartialMessage(partial, message, fields);
|
|
35
42
|
return message;
|
|
36
43
|
},
|
|
@@ -47,7 +54,7 @@ export function createMessageType(params, exts) {
|
|
|
47
54
|
const message = {};
|
|
48
55
|
if (bytes && bytes.length) {
|
|
49
56
|
const opt = binaryMakeReadOptions(options);
|
|
50
|
-
binaryReadMessage(message, fields, opt.readerFactory(bytes), bytes.byteLength, opt, delimitedMessageEncoding);
|
|
57
|
+
binaryReadMessage(message, fields, opt.readerFactory(bytes), bytes.byteLength, opt, delimitedMessageEncoding ?? false);
|
|
51
58
|
}
|
|
52
59
|
return message;
|
|
53
60
|
},
|
|
@@ -104,9 +111,12 @@ export function compareMessages(fields, a, b) {
|
|
|
104
111
|
const va = a[m.localName];
|
|
105
112
|
const vb = b[m.localName];
|
|
106
113
|
if (m.repeated) {
|
|
107
|
-
if (va
|
|
114
|
+
if ((va?.length ?? 0) !== (vb?.length ?? 0)) {
|
|
108
115
|
return false;
|
|
109
116
|
}
|
|
117
|
+
if (!va?.length) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
110
120
|
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- repeated fields are never "map"
|
|
111
121
|
switch (m.kind) {
|
|
112
122
|
case "message":
|
|
@@ -127,9 +137,12 @@ export function compareMessages(fields, a, b) {
|
|
|
127
137
|
case "scalar":
|
|
128
138
|
return scalarEquals(m.T, va, vb);
|
|
129
139
|
case "oneof":
|
|
130
|
-
if (va
|
|
140
|
+
if (va?.case !== vb?.case) {
|
|
131
141
|
return false;
|
|
132
142
|
}
|
|
143
|
+
if (va == null) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
133
146
|
const s = m.findField(va.case);
|
|
134
147
|
if (s === undefined) {
|
|
135
148
|
return true;
|
|
@@ -160,129 +173,55 @@ export function compareMessages(fields, a, b) {
|
|
|
160
173
|
}
|
|
161
174
|
});
|
|
162
175
|
}
|
|
163
|
-
// clone a single field value - i.e. the element type of repeated fields, the value type of maps
|
|
164
|
-
function cloneSingularField(value, fieldInfo) {
|
|
165
|
-
if (value === undefined) {
|
|
166
|
-
return value;
|
|
167
|
-
}
|
|
168
|
-
if (fieldInfo.kind === "message") {
|
|
169
|
-
return cloneMessage(value, resolveMessageType(fieldInfo.T).fields);
|
|
170
|
-
}
|
|
171
|
-
if (fieldInfo.kind === "oneof") {
|
|
172
|
-
if (value.case === undefined) {
|
|
173
|
-
return undefined;
|
|
174
|
-
}
|
|
175
|
-
const selectedField = fieldInfo.findField(value.case);
|
|
176
|
-
if (!selectedField) {
|
|
177
|
-
throw new Error(`Invalid oneof case "${value.case}" for ${fieldInfo.name}`);
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
case: value.case,
|
|
181
|
-
value: cloneSingularField(value.value, selectedField),
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
if (value instanceof Uint8Array) {
|
|
185
|
-
const c = new Uint8Array(value.byteLength);
|
|
186
|
-
c.set(value);
|
|
187
|
-
return c;
|
|
188
|
-
}
|
|
189
|
-
return value;
|
|
190
|
-
}
|
|
191
|
-
// TODO use isFieldSet() here to support future field presence
|
|
192
176
|
export function cloneMessage(message, fields) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const source = message[member.localName];
|
|
196
|
-
let copy;
|
|
197
|
-
if (member.repeated) {
|
|
198
|
-
copy = source.map((v) => cloneSingularField(v, member));
|
|
199
|
-
}
|
|
200
|
-
else if (member.kind == "map") {
|
|
201
|
-
copy = {};
|
|
202
|
-
for (const [key, v] of Object.entries(source)) {
|
|
203
|
-
copy[key] = cloneSingularField(v, member);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else if (member.kind == "oneof") {
|
|
207
|
-
const f = member.findField(source.case);
|
|
208
|
-
copy =
|
|
209
|
-
f ?
|
|
210
|
-
{ case: source.case, value: cloneSingularField(source.value, member) }
|
|
211
|
-
: { case: undefined };
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
copy = cloneSingularField(source, member);
|
|
215
|
-
}
|
|
216
|
-
clone[member.localName] = copy;
|
|
177
|
+
if (message == null) {
|
|
178
|
+
return null;
|
|
217
179
|
}
|
|
180
|
+
const clone = Object.create(null);
|
|
181
|
+
applyPartialMessage(message, clone, fields, true);
|
|
218
182
|
return clone;
|
|
219
183
|
}
|
|
220
184
|
/**
|
|
221
|
-
*
|
|
185
|
+
* createCompleteMessage recursively builds a message filled with zero values based on the given FieldList.
|
|
222
186
|
*/
|
|
223
|
-
export function
|
|
187
|
+
export function createCompleteMessage(fields) {
|
|
224
188
|
const message = {};
|
|
225
|
-
for (const field of fields.
|
|
226
|
-
const fieldKind = field
|
|
189
|
+
for (const field of fields.byMember()) {
|
|
190
|
+
const { localName, kind: fieldKind } = field;
|
|
191
|
+
throwSanitizeKey(localName);
|
|
227
192
|
switch (fieldKind) {
|
|
193
|
+
case "oneof":
|
|
194
|
+
message[localName] = Object.create(null);
|
|
195
|
+
message[localName].case = undefined;
|
|
196
|
+
break;
|
|
228
197
|
case "scalar":
|
|
229
198
|
if (field.repeated) {
|
|
230
|
-
message[
|
|
199
|
+
message[localName] = [];
|
|
231
200
|
}
|
|
232
201
|
else {
|
|
233
|
-
|
|
234
|
-
case ScalarType.DOUBLE:
|
|
235
|
-
case ScalarType.FLOAT:
|
|
236
|
-
message[field.localName] = 0;
|
|
237
|
-
break;
|
|
238
|
-
case ScalarType.INT64:
|
|
239
|
-
case ScalarType.UINT64:
|
|
240
|
-
case ScalarType.INT32:
|
|
241
|
-
case ScalarType.FIXED64:
|
|
242
|
-
case ScalarType.FIXED32:
|
|
243
|
-
case ScalarType.UINT32:
|
|
244
|
-
case ScalarType.SFIXED32:
|
|
245
|
-
case ScalarType.SFIXED64:
|
|
246
|
-
case ScalarType.SINT32:
|
|
247
|
-
case ScalarType.SINT64:
|
|
248
|
-
message[field.localName] = 0;
|
|
249
|
-
break;
|
|
250
|
-
case ScalarType.BOOL:
|
|
251
|
-
message[field.localName] = false;
|
|
252
|
-
break;
|
|
253
|
-
case ScalarType.STRING:
|
|
254
|
-
message[field.localName] = "";
|
|
255
|
-
break;
|
|
256
|
-
case ScalarType.BYTES:
|
|
257
|
-
message[field.localName] =
|
|
258
|
-
new Uint8Array();
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
202
|
+
message[localName] = scalarZeroValue(field.T, LongType.BIGINT);
|
|
261
203
|
}
|
|
262
204
|
break;
|
|
263
205
|
case "enum":
|
|
264
|
-
|
|
265
|
-
message[field.localName] = [];
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
message[field.localName] = 0;
|
|
269
|
-
}
|
|
206
|
+
message[localName] = field.repeated ? [] : enumZeroValue(field.T);
|
|
270
207
|
break;
|
|
271
208
|
case "message":
|
|
209
|
+
// oneofs are handled above
|
|
272
210
|
if (field.oneof) {
|
|
273
|
-
|
|
274
|
-
continue;
|
|
211
|
+
break;
|
|
275
212
|
}
|
|
276
|
-
const messageType = resolveMessageType(field.T);
|
|
277
213
|
if (field.repeated) {
|
|
278
|
-
message[
|
|
279
|
-
|
|
280
|
-
else {
|
|
281
|
-
message[field.localName] = createMessage(messageType.fields);
|
|
214
|
+
message[localName] = [];
|
|
215
|
+
break;
|
|
282
216
|
}
|
|
217
|
+
const messageType = resolveMessageType(field.T);
|
|
218
|
+
message[localName] =
|
|
219
|
+
!!messageType.fieldWrapper ?
|
|
220
|
+
messageType.fieldWrapper.unwrapField(null)
|
|
221
|
+
: createCompleteMessage(messageType.fields);
|
|
283
222
|
break;
|
|
284
223
|
case "map":
|
|
285
|
-
message[
|
|
224
|
+
message[localName] = Object.create(null);
|
|
286
225
|
break;
|
|
287
226
|
default:
|
|
288
227
|
field;
|
package/dist/names.d.ts
CHANGED
|
@@ -41,3 +41,5 @@ export declare const safeObjectProperty: (name: string) => string;
|
|
|
41
41
|
* Names that can be used for identifiers or class properties
|
|
42
42
|
*/
|
|
43
43
|
export declare const safeIdentifier: (name: string) => string;
|
|
44
|
+
export declare function checkSanitizeKey(key: string): boolean;
|
|
45
|
+
export declare function throwSanitizeKey(key: string): void;
|
package/dist/names.js
CHANGED
|
@@ -32,12 +32,7 @@ export function localName(desc) {
|
|
|
32
32
|
const pkg = desc.file.proto.package;
|
|
33
33
|
const offset = pkg === undefined ? 0 : pkg.length + 1;
|
|
34
34
|
const name = desc.typeName.substring(offset).replace(/\./g, "_");
|
|
35
|
-
|
|
36
|
-
// but we have shipped v1 with a bug that respected object properties, and we
|
|
37
|
-
// do not want to introduce a breaking change, so we continue to escape for
|
|
38
|
-
// safe object properties.
|
|
39
|
-
// See https://github.com/bufbuild/protobuf-es/pull/391
|
|
40
|
-
return safeObjectProperty(safeIdentifier(name));
|
|
35
|
+
return safeIdentifier(name);
|
|
41
36
|
}
|
|
42
37
|
case "enum_value": {
|
|
43
38
|
let name = desc.name;
|
|
@@ -219,20 +214,7 @@ const reservedObjectProperties = new Set([
|
|
|
219
214
|
* Names that cannot be used for object properties because they are reserved
|
|
220
215
|
* by the runtime.
|
|
221
216
|
*/
|
|
222
|
-
const reservedMessageProperties = new Set([
|
|
223
|
-
// names reserved by the runtime
|
|
224
|
-
"getType",
|
|
225
|
-
"clone",
|
|
226
|
-
"equals",
|
|
227
|
-
"fromBinary",
|
|
228
|
-
"fromJson",
|
|
229
|
-
"fromJsonString",
|
|
230
|
-
"toBinary",
|
|
231
|
-
"toJson",
|
|
232
|
-
"toJsonString",
|
|
233
|
-
// names reserved by the runtime for the future
|
|
234
|
-
"toObject",
|
|
235
|
-
]);
|
|
217
|
+
const reservedMessageProperties = new Set(["__proto__"]);
|
|
236
218
|
const fallback = (name) => `${name}$`;
|
|
237
219
|
/**
|
|
238
220
|
* Will wrap names that are Object prototype properties or names reserved
|
|
@@ -263,3 +245,14 @@ export const safeIdentifier = (name) => {
|
|
|
263
245
|
}
|
|
264
246
|
return name;
|
|
265
247
|
};
|
|
248
|
+
export function checkSanitizeKey(key) {
|
|
249
|
+
return typeof key === "string" && key !== "__proto__";
|
|
250
|
+
}
|
|
251
|
+
export function throwSanitizeKey(key) {
|
|
252
|
+
if (typeof key !== "string") {
|
|
253
|
+
throw new Error("illegal non-string object key: " + typeof key);
|
|
254
|
+
}
|
|
255
|
+
if (!checkSanitizeKey(key)) {
|
|
256
|
+
throw new Error("illegal object key: " + key);
|
|
257
|
+
}
|
|
258
|
+
}
|
package/dist/partial.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { FieldList } from "./field.js";
|
|
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;
|
|
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
|
@@ -1,99 +1,196 @@
|
|
|
1
|
+
import { normalizeEnumValue } from "./enum.js";
|
|
1
2
|
import { resolveMessageType } from "./field.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { createCompleteMessage } from "./message.js";
|
|
4
|
+
import { throwSanitizeKey } from "./names.js";
|
|
5
|
+
import { normalizeScalarValue } from "./scalar.js";
|
|
6
|
+
// applyPartialMessage applies a partial source message to a target message.
|
|
7
|
+
//
|
|
8
|
+
// if clone is set: values are deep-copied including Uint8Arrays.
|
|
9
|
+
export function applyPartialMessage(source, target, fields, clone = false) {
|
|
10
|
+
if (source == null || target == null) {
|
|
6
11
|
return;
|
|
7
12
|
}
|
|
13
|
+
const t = target, s = source;
|
|
8
14
|
for (const member of fields.byMember()) {
|
|
9
|
-
const localName = member.localName
|
|
10
|
-
|
|
15
|
+
const localName = member.localName;
|
|
16
|
+
throwSanitizeKey(localName);
|
|
17
|
+
if (!(localName in s) || s[localName] === undefined) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const sourceValue = s[localName];
|
|
21
|
+
if (sourceValue === null) {
|
|
22
|
+
delete t[localName];
|
|
11
23
|
continue;
|
|
12
24
|
}
|
|
13
25
|
switch (member.kind) {
|
|
14
26
|
case "oneof":
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
if (typeof sourceValue !== "object") {
|
|
28
|
+
throw new Error(`field ${localName}: invalid oneof: must be an object with case and value`);
|
|
29
|
+
}
|
|
30
|
+
// sk, sk are the source case and value
|
|
31
|
+
const { case: sk, value: sv } = sourceValue;
|
|
32
|
+
// sourceField is the field set by the source case, if any.
|
|
33
|
+
const sourceField = sk != null ? member.findField(sk) : null;
|
|
34
|
+
// dv is the destination oneof object
|
|
35
|
+
let dv = localName in t ? t[localName] : undefined;
|
|
36
|
+
if (typeof dv !== "object") {
|
|
37
|
+
dv = Object.create(null);
|
|
38
|
+
}
|
|
39
|
+
// check the case is valid and throw if not
|
|
40
|
+
if (sk != null && sourceField == null) {
|
|
41
|
+
throw new Error(`field ${localName}: invalid oneof case: ${sk}`);
|
|
42
|
+
}
|
|
43
|
+
// update the case
|
|
44
|
+
dv.case = sk;
|
|
45
|
+
// if the case was different or null, clear the value.
|
|
46
|
+
if (dv.case !== sk || sk == null) {
|
|
47
|
+
delete dv.value;
|
|
18
48
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (sourceField
|
|
22
|
-
|
|
23
|
-
|
|
49
|
+
t[localName] = dv;
|
|
50
|
+
// stop here if there was no valid case selected
|
|
51
|
+
if (!sourceField) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
if (sourceField.kind === "message") {
|
|
55
|
+
// apply the partial to the value
|
|
56
|
+
let dest = dv.value;
|
|
57
|
+
if (typeof dest !== "object") {
|
|
58
|
+
dest = dv.value = Object.create(null);
|
|
59
|
+
}
|
|
60
|
+
// skip zero or null value
|
|
61
|
+
if (sv != null) {
|
|
62
|
+
const sourceFieldMt = resolveMessageType(sourceField.T);
|
|
63
|
+
applyPartialMessage(sv, dest, sourceFieldMt.fields);
|
|
24
64
|
}
|
|
25
65
|
}
|
|
26
|
-
else if (sourceField
|
|
27
|
-
sourceField.
|
|
28
|
-
|
|
29
|
-
|
|
66
|
+
else if (sourceField.kind === "scalar") {
|
|
67
|
+
dv.value = normalizeScalarValue(sourceField.T, sv, clone);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
dv.value = sv;
|
|
30
71
|
}
|
|
31
|
-
t[localName] = { case: sk, value: val };
|
|
32
72
|
break;
|
|
33
73
|
case "scalar":
|
|
74
|
+
if (member.repeated) {
|
|
75
|
+
if (!Array.isArray(sourceValue)) {
|
|
76
|
+
throw new Error(`field ${localName}: invalid value: must be array`);
|
|
77
|
+
}
|
|
78
|
+
let dst = localName in t ? t[localName] : null;
|
|
79
|
+
if (dst == null || !Array.isArray(dst)) {
|
|
80
|
+
dst = t[localName] = [];
|
|
81
|
+
}
|
|
82
|
+
dst.push(...sourceValue.map((v) => normalizeScalarValue(member.T, v, clone)));
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
t[localName] = normalizeScalarValue(member.T, sourceValue, clone);
|
|
86
|
+
break;
|
|
34
87
|
case "enum":
|
|
35
|
-
|
|
36
|
-
if (member.T === ScalarType.BYTES) {
|
|
37
|
-
copy =
|
|
38
|
-
member.repeated ?
|
|
39
|
-
copy.map(toU8Arr)
|
|
40
|
-
: toU8Arr(copy);
|
|
41
|
-
}
|
|
42
|
-
t[localName] = copy;
|
|
88
|
+
t[localName] = normalizeEnumValue(member.T, sourceValue);
|
|
43
89
|
break;
|
|
44
90
|
case "map":
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
Object.assign(t[localName], s[localName]);
|
|
55
|
-
}
|
|
56
|
-
break;
|
|
57
|
-
case "message":
|
|
58
|
-
const messageType = resolveMessageType(member.V.T);
|
|
59
|
-
for (const k of Object.keys(s[localName])) {
|
|
60
|
-
let val = s[localName][k];
|
|
61
|
-
if (!messageType.fieldWrapper) {
|
|
62
|
-
if (val === undefined) {
|
|
63
|
-
val = {};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
t[localName][k] = val;
|
|
67
|
-
}
|
|
68
|
-
break;
|
|
91
|
+
if (typeof sourceValue !== "object") {
|
|
92
|
+
throw new Error(`field ${member.localName}: invalid value: must be object`);
|
|
93
|
+
}
|
|
94
|
+
let tMap = t[localName];
|
|
95
|
+
if (typeof tMap !== "object") {
|
|
96
|
+
tMap = t[localName] = Object.create(null);
|
|
69
97
|
}
|
|
98
|
+
applyPartialMap(sourceValue, tMap, member.V, clone);
|
|
70
99
|
break;
|
|
71
100
|
case "message":
|
|
72
101
|
const mt = resolveMessageType(member.T);
|
|
73
102
|
if (member.repeated) {
|
|
74
|
-
|
|
103
|
+
// skip null or undefined values
|
|
104
|
+
if (!Array.isArray(sourceValue)) {
|
|
105
|
+
throw new Error(`field ${localName}: invalid value: must be array`);
|
|
106
|
+
}
|
|
107
|
+
let tArr = t[localName];
|
|
108
|
+
if (!Array.isArray(tArr)) {
|
|
109
|
+
tArr = t[localName] = [];
|
|
110
|
+
}
|
|
111
|
+
for (const v of sourceValue) {
|
|
112
|
+
// skip null or undefined values
|
|
113
|
+
if (v != null) {
|
|
114
|
+
if (mt.fieldWrapper) {
|
|
115
|
+
tArr.push(mt.fieldWrapper.unwrapField(mt.fieldWrapper.wrapField(v)));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
tArr.push(mt.create(v));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
if (mt.fieldWrapper) {
|
|
125
|
+
t[localName] = mt.fieldWrapper.unwrapField(mt.fieldWrapper.wrapField(sourceValue));
|
|
75
126
|
}
|
|
76
127
|
else {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
// We can't use BytesValue.typeName as that will create a circular import
|
|
81
|
-
mt.typeName === "google.protobuf.BytesValue") {
|
|
82
|
-
t[localName] = toU8Arr(val);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
t[localName] = val;
|
|
86
|
-
}
|
|
128
|
+
if (typeof sourceValue !== "object") {
|
|
129
|
+
throw new Error(`field ${member.localName}: invalid value: must be object`);
|
|
87
130
|
}
|
|
88
|
-
|
|
89
|
-
|
|
131
|
+
let destMsg = t[localName];
|
|
132
|
+
if (typeof destMsg !== "object") {
|
|
133
|
+
destMsg = t[localName] = Object.create(null);
|
|
90
134
|
}
|
|
135
|
+
applyPartialMessage(sourceValue, destMsg, mt.fields);
|
|
91
136
|
}
|
|
92
137
|
break;
|
|
93
138
|
}
|
|
94
139
|
}
|
|
95
140
|
}
|
|
96
|
-
//
|
|
97
|
-
function
|
|
98
|
-
|
|
141
|
+
// applyPartialMap applies a partial source map to a target map.
|
|
142
|
+
export function applyPartialMap(sourceMap, targetMap, value, clone) {
|
|
143
|
+
if (sourceMap == null) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (typeof sourceMap !== "object") {
|
|
147
|
+
throw new Error(`invalid map: must be object`);
|
|
148
|
+
}
|
|
149
|
+
switch (value.kind) {
|
|
150
|
+
case "scalar":
|
|
151
|
+
for (const [k, v] of Object.entries(sourceMap)) {
|
|
152
|
+
throwSanitizeKey(k);
|
|
153
|
+
if (v !== undefined) {
|
|
154
|
+
targetMap[k] = normalizeScalarValue(value.T, v, clone);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
delete targetMap[k];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
case "enum":
|
|
162
|
+
for (const [k, v] of Object.entries(sourceMap)) {
|
|
163
|
+
throwSanitizeKey(k);
|
|
164
|
+
if (v !== undefined) {
|
|
165
|
+
targetMap[k] = normalizeEnumValue(value.T, v);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
delete targetMap[k];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
case "message":
|
|
173
|
+
const messageType = resolveMessageType(value.T);
|
|
174
|
+
for (const [k, v] of Object.entries(sourceMap)) {
|
|
175
|
+
throwSanitizeKey(k);
|
|
176
|
+
if (v === undefined) {
|
|
177
|
+
delete targetMap[k];
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (typeof v !== "object") {
|
|
181
|
+
throw new Error(`invalid value: must be object`);
|
|
182
|
+
}
|
|
183
|
+
let val = targetMap[k];
|
|
184
|
+
if (!!messageType.fieldWrapper) {
|
|
185
|
+
// For wrapper type messages, call createCompleteMessage.
|
|
186
|
+
val = targetMap[k] = createCompleteMessage(messageType.fields);
|
|
187
|
+
}
|
|
188
|
+
else if (typeof val !== "object") {
|
|
189
|
+
// Otherwise apply the partial to the existing value, if any.
|
|
190
|
+
val = targetMap[k] = Object.create(null);
|
|
191
|
+
}
|
|
192
|
+
applyPartialMessage(v, val, messageType.fields);
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
99
196
|
}
|
|
@@ -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":
|
|
@@ -564,7 +596,7 @@ function generateWktFieldWrapper(f, message, ref) {
|
|
|
564
596
|
const { typing } = getFieldTypeInfo(ref.value);
|
|
565
597
|
f.print(" fieldWrapper: {");
|
|
566
598
|
f.print(" wrapField(value: ", typing, " | null | undefined): ", message, " {");
|
|
567
|
-
f.print(" return ", message, ".
|
|
599
|
+
f.print(" return ", message, ".createComplete({ value: value ?? undefined });");
|
|
568
600
|
f.print(" },");
|
|
569
601
|
f.print(" unwrapField(msg: ", message, "): ", typing, " | null | undefined {");
|
|
570
602
|
f.print(" return msg.", localName(ref.value), ";");
|
package/dist/scalar.d.ts
CHANGED
|
@@ -67,3 +67,12 @@ export declare function scalarZeroValue<T extends ScalarType, L extends LongType
|
|
|
67
67
|
* optional or repeated.
|
|
68
68
|
*/
|
|
69
69
|
export declare function isScalarZeroValue(type: ScalarType, value: unknown): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Returns the normalized version of the scalar value.
|
|
72
|
+
* Zero or null is cast to the zero value.
|
|
73
|
+
* Bytes is cast to a Uint8Array.
|
|
74
|
+
* The BigInt long type is used.
|
|
75
|
+
* If clone is set, Uint8Array will always be copied to a new value.
|
|
76
|
+
*/
|
|
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;
|