You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
24 KiB
625 lines
24 KiB
"use strict";
|
|
// Copyright 2021-2024 Buf Technologies, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.fromJsonString = fromJsonString;
|
|
exports.mergeFromJsonString = mergeFromJsonString;
|
|
exports.fromJson = fromJson;
|
|
exports.mergeFromJson = mergeFromJson;
|
|
exports.enumFromJson = enumFromJson;
|
|
exports.isEnumJson = isEnumJson;
|
|
/* eslint-disable no-case-declarations,@typescript-eslint/restrict-template-expressions */
|
|
const descriptors_js_1 = require("./descriptors.js");
|
|
const proto_int64_js_1 = require("./proto-int64.js");
|
|
const create_js_1 = require("./create.js");
|
|
const reflect_js_1 = require("./reflect/reflect.js");
|
|
const error_js_1 = require("./reflect/error.js");
|
|
const reflect_check_js_1 = require("./reflect/reflect-check.js");
|
|
const scalar_js_1 = require("./reflect/scalar.js");
|
|
const base64_encoding_js_1 = require("./wire/base64-encoding.js");
|
|
const index_js_1 = require("./wkt/index.js");
|
|
const extensions_js_1 = require("./extensions.js");
|
|
// Default options for parsing JSON.
|
|
const jsonReadDefaults = {
|
|
ignoreUnknownFields: false,
|
|
};
|
|
function makeReadOptions(options) {
|
|
return options ? Object.assign(Object.assign({}, jsonReadDefaults), options) : jsonReadDefaults;
|
|
}
|
|
/**
|
|
* Parse a message from a JSON string.
|
|
*/
|
|
function fromJsonString(schema, json, options) {
|
|
return fromJson(schema, parseJsonString(json, schema.typeName), options);
|
|
}
|
|
/**
|
|
* Parse a message from a JSON string, merging fields.
|
|
*
|
|
* Repeated fields are appended. Map entries are added, overwriting
|
|
* existing keys.
|
|
*
|
|
* If a message field is already present, it will be merged with the
|
|
* new data.
|
|
*/
|
|
function mergeFromJsonString(schema, target, json, options) {
|
|
return mergeFromJson(schema, target, parseJsonString(json, schema.typeName), options);
|
|
}
|
|
/**
|
|
* Parse a message from a JSON value.
|
|
*/
|
|
function fromJson(schema, json, options) {
|
|
const msg = (0, reflect_js_1.reflect)(schema);
|
|
try {
|
|
readMessage(msg, json, makeReadOptions(options));
|
|
}
|
|
catch (e) {
|
|
if ((0, error_js_1.isFieldError)(e)) {
|
|
// @ts-expect-error we use the ES2022 error CTOR option "cause" for better stack traces
|
|
throw new Error(`cannot decode ${e.field()} from JSON: ${e.message}`, {
|
|
cause: e,
|
|
});
|
|
}
|
|
throw e;
|
|
}
|
|
return msg.message;
|
|
}
|
|
/**
|
|
* Parse a message from a JSON value, merging fields.
|
|
*
|
|
* Repeated fields are appended. Map entries are added, overwriting
|
|
* existing keys.
|
|
*
|
|
* If a message field is already present, it will be merged with the
|
|
* new data.
|
|
*/
|
|
function mergeFromJson(schema, target, json, options) {
|
|
try {
|
|
readMessage((0, reflect_js_1.reflect)(schema, target), json, makeReadOptions(options));
|
|
}
|
|
catch (e) {
|
|
if ((0, error_js_1.isFieldError)(e)) {
|
|
// @ts-expect-error we use the ES2022 error CTOR option "cause" for better stack traces
|
|
throw new Error(`cannot decode ${e.field()} from JSON: ${e.message}`, {
|
|
cause: e,
|
|
});
|
|
}
|
|
throw e;
|
|
}
|
|
return target;
|
|
}
|
|
/**
|
|
* Parses an enum value from JSON.
|
|
*/
|
|
function enumFromJson(descEnum, json) {
|
|
const val = readEnum(descEnum, json, false, false);
|
|
if (val === tokenIgnoredUnknownEnum) {
|
|
throw new Error(`cannot decode ${String(descEnum)} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
return val;
|
|
}
|
|
/**
|
|
* Is the given value a JSON enum value?
|
|
*/
|
|
function isEnumJson(descEnum, value) {
|
|
return undefined !== descEnum.values.find((v) => v.name === value);
|
|
}
|
|
function readMessage(msg, json, opts) {
|
|
var _a;
|
|
if (tryWktFromJson(msg, json, opts)) {
|
|
return;
|
|
}
|
|
if (json == null || Array.isArray(json) || typeof json != "object") {
|
|
throw new Error(`cannot decode ${msg.desc} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
const oneofSeen = new Map();
|
|
const jsonNames = new Map();
|
|
for (const field of msg.desc.fields) {
|
|
jsonNames.set(field.name, field).set(field.jsonName, field);
|
|
}
|
|
for (const [jsonKey, jsonValue] of Object.entries(json)) {
|
|
const field = jsonNames.get(jsonKey);
|
|
if (field) {
|
|
if (field.oneof) {
|
|
if (jsonValue === null && field.fieldKind == "scalar") {
|
|
// see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
|
|
continue;
|
|
}
|
|
const seen = oneofSeen.get(field.oneof);
|
|
if (seen !== undefined) {
|
|
throw new error_js_1.FieldError(field.oneof, `oneof set multiple times by ${seen.name} and ${field.name}`);
|
|
}
|
|
oneofSeen.set(field.oneof, field);
|
|
}
|
|
readField(msg, field, jsonValue, opts);
|
|
}
|
|
else {
|
|
let extension = undefined;
|
|
if (jsonKey.startsWith("[") &&
|
|
jsonKey.endsWith("]") &&
|
|
(extension = (_a = opts.registry) === null || _a === void 0 ? void 0 : _a.getExtension(jsonKey.substring(1, jsonKey.length - 1))) &&
|
|
extension.extendee.typeName === msg.desc.typeName) {
|
|
const [container, field, get] = (0, extensions_js_1.createExtensionContainer)(extension);
|
|
readField(container, field, jsonValue, opts);
|
|
(0, extensions_js_1.setExtension)(msg.message, extension, get());
|
|
}
|
|
if (!extension && !opts.ignoreUnknownFields) {
|
|
throw new Error(`cannot decode ${msg.desc} from JSON: key "${jsonKey}" is unknown`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function readField(msg, field, json, opts) {
|
|
switch (field.fieldKind) {
|
|
case "scalar":
|
|
readScalarField(msg, field, json);
|
|
break;
|
|
case "enum":
|
|
readEnumField(msg, field, json, opts);
|
|
break;
|
|
case "message":
|
|
readMessageField(msg, field, json, opts);
|
|
break;
|
|
case "list":
|
|
readListField(msg.get(field), json, opts);
|
|
break;
|
|
case "map":
|
|
readMapField(msg.get(field), json, opts);
|
|
break;
|
|
}
|
|
}
|
|
function readMapField(map, json, opts) {
|
|
if (json === null) {
|
|
return;
|
|
}
|
|
const field = map.field();
|
|
if (typeof json != "object" || Array.isArray(json)) {
|
|
throw new error_js_1.FieldError(field, "expected object, got " + (0, reflect_check_js_1.formatVal)(json));
|
|
}
|
|
for (const [jsonMapKey, jsonMapValue] of Object.entries(json)) {
|
|
if (jsonMapValue === null) {
|
|
throw new error_js_1.FieldError(field, "map value must not be null");
|
|
}
|
|
let value;
|
|
switch (field.mapKind) {
|
|
case "message":
|
|
const msgValue = (0, reflect_js_1.reflect)(field.message);
|
|
readMessage(msgValue, jsonMapValue, opts);
|
|
value = msgValue;
|
|
break;
|
|
case "enum":
|
|
value = readEnum(field.enum, jsonMapValue, opts.ignoreUnknownFields, true);
|
|
if (value === tokenIgnoredUnknownEnum) {
|
|
return;
|
|
}
|
|
break;
|
|
case "scalar":
|
|
value = scalarFromJson(field, jsonMapValue, true);
|
|
break;
|
|
}
|
|
const key = mapKeyFromJson(field.mapKey, jsonMapKey);
|
|
map.set(key, value);
|
|
}
|
|
}
|
|
function readListField(list, json, opts) {
|
|
if (json === null) {
|
|
return;
|
|
}
|
|
const field = list.field();
|
|
if (!Array.isArray(json)) {
|
|
throw new error_js_1.FieldError(field, "expected Array, got " + (0, reflect_check_js_1.formatVal)(json));
|
|
}
|
|
for (const jsonItem of json) {
|
|
if (jsonItem === null) {
|
|
throw new error_js_1.FieldError(field, "list item must not be null");
|
|
}
|
|
switch (field.listKind) {
|
|
case "message":
|
|
const msgValue = (0, reflect_js_1.reflect)(field.message);
|
|
readMessage(msgValue, jsonItem, opts);
|
|
list.add(msgValue);
|
|
break;
|
|
case "enum":
|
|
const enumValue = readEnum(field.enum, jsonItem, opts.ignoreUnknownFields, true);
|
|
if (enumValue !== tokenIgnoredUnknownEnum) {
|
|
list.add(enumValue);
|
|
}
|
|
break;
|
|
case "scalar":
|
|
list.add(scalarFromJson(field, jsonItem, true));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
function readMessageField(msg, field, json, opts) {
|
|
if (json === null && field.message.typeName != "google.protobuf.Value") {
|
|
msg.clear(field);
|
|
return;
|
|
}
|
|
const msgValue = msg.isSet(field) ? msg.get(field) : (0, reflect_js_1.reflect)(field.message);
|
|
readMessage(msgValue, json, opts);
|
|
msg.set(field, msgValue);
|
|
}
|
|
function readEnumField(msg, field, json, opts) {
|
|
const enumValue = readEnum(field.enum, json, opts.ignoreUnknownFields, false);
|
|
if (enumValue === tokenNull) {
|
|
msg.clear(field);
|
|
}
|
|
else if (enumValue !== tokenIgnoredUnknownEnum) {
|
|
msg.set(field, enumValue);
|
|
}
|
|
}
|
|
function readScalarField(msg, field, json) {
|
|
const scalarValue = scalarFromJson(field, json, false);
|
|
if (scalarValue === tokenNull) {
|
|
msg.clear(field);
|
|
}
|
|
else {
|
|
msg.set(field, scalarValue);
|
|
}
|
|
}
|
|
const tokenIgnoredUnknownEnum = Symbol();
|
|
function readEnum(desc, json, ignoreUnknownFields, nullAsZeroValue) {
|
|
if (json === null) {
|
|
if (desc.typeName == "google.protobuf.NullValue") {
|
|
return 0; // google.protobuf.NullValue.NULL_VALUE = 0
|
|
}
|
|
return nullAsZeroValue ? desc.values[0].number : tokenNull;
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
switch (typeof json) {
|
|
case "number":
|
|
if (Number.isInteger(json)) {
|
|
return json;
|
|
}
|
|
break;
|
|
case "string":
|
|
const value = desc.values.find((ev) => ev.name === json);
|
|
if (value !== undefined) {
|
|
return value.number;
|
|
}
|
|
if (ignoreUnknownFields) {
|
|
return tokenIgnoredUnknownEnum;
|
|
}
|
|
break;
|
|
}
|
|
throw new Error(`cannot decode ${desc} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
const tokenNull = Symbol();
|
|
function scalarFromJson(field, json, nullAsZeroValue) {
|
|
if (json === null) {
|
|
if (nullAsZeroValue) {
|
|
return (0, scalar_js_1.scalarZeroValue)(field.scalar, false);
|
|
}
|
|
return tokenNull;
|
|
}
|
|
// int64, sfixed64, sint64, fixed64, uint64: Reflect supports string and number.
|
|
// string, bool: Supported by reflect.
|
|
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
switch (field.scalar) {
|
|
// float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity".
|
|
// Either numbers or strings are accepted. Exponent notation is also accepted.
|
|
case descriptors_js_1.ScalarType.DOUBLE:
|
|
case descriptors_js_1.ScalarType.FLOAT:
|
|
if (json === "NaN")
|
|
return NaN;
|
|
if (json === "Infinity")
|
|
return Number.POSITIVE_INFINITY;
|
|
if (json === "-Infinity")
|
|
return Number.NEGATIVE_INFINITY;
|
|
if (typeof json == "number") {
|
|
if (isNaN(json)) {
|
|
// NaN must be encoded with string constants
|
|
throw new error_js_1.FieldError(field, "unexpected NaN number");
|
|
}
|
|
if (!isFinite(json)) {
|
|
// Infinity must be encoded with string constants
|
|
throw new error_js_1.FieldError(field, "unexpected infinite number");
|
|
}
|
|
break;
|
|
}
|
|
if (typeof json == "string") {
|
|
if (json === "") {
|
|
// empty string is not a number
|
|
break;
|
|
}
|
|
if (json.trim().length !== json.length) {
|
|
// extra whitespace
|
|
break;
|
|
}
|
|
const float = Number(json);
|
|
if (!isFinite(float)) {
|
|
// Infinity and NaN must be encoded with string constants
|
|
break;
|
|
}
|
|
return float;
|
|
}
|
|
break;
|
|
// int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted.
|
|
case descriptors_js_1.ScalarType.INT32:
|
|
case descriptors_js_1.ScalarType.FIXED32:
|
|
case descriptors_js_1.ScalarType.SFIXED32:
|
|
case descriptors_js_1.ScalarType.SINT32:
|
|
case descriptors_js_1.ScalarType.UINT32:
|
|
return int32FromJson(json);
|
|
// bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings.
|
|
// Either standard or URL-safe base64 encoding with/without paddings are accepted.
|
|
case descriptors_js_1.ScalarType.BYTES:
|
|
if (typeof json == "string") {
|
|
if (json === "") {
|
|
return new Uint8Array(0);
|
|
}
|
|
try {
|
|
return (0, base64_encoding_js_1.base64Decode)(json);
|
|
}
|
|
catch (e) {
|
|
const message = e instanceof Error ? e.message : String(e);
|
|
throw new error_js_1.FieldError(field, message);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return json;
|
|
}
|
|
/**
|
|
* Try to parse a JSON value to a map key for the reflect API.
|
|
*
|
|
* Returns the input if the JSON value cannot be converted.
|
|
*/
|
|
function mapKeyFromJson(type, json) {
|
|
switch (type) {
|
|
case descriptors_js_1.ScalarType.BOOL:
|
|
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
switch (json) {
|
|
case "true":
|
|
return true;
|
|
case "false":
|
|
return false;
|
|
}
|
|
return json;
|
|
case descriptors_js_1.ScalarType.INT32:
|
|
case descriptors_js_1.ScalarType.FIXED32:
|
|
case descriptors_js_1.ScalarType.UINT32:
|
|
case descriptors_js_1.ScalarType.SFIXED32:
|
|
case descriptors_js_1.ScalarType.SINT32:
|
|
return int32FromJson(json);
|
|
default:
|
|
return json;
|
|
}
|
|
}
|
|
/**
|
|
* Try to parse a JSON value to a 32-bit integer for the reflect API.
|
|
*
|
|
* Returns the input if the JSON value cannot be converted.
|
|
*/
|
|
function int32FromJson(json) {
|
|
if (typeof json == "string") {
|
|
if (json === "") {
|
|
// empty string is not a number
|
|
return json;
|
|
}
|
|
if (json.trim().length !== json.length) {
|
|
// extra whitespace
|
|
return json;
|
|
}
|
|
const num = Number(json);
|
|
if (Number.isNaN(num)) {
|
|
// not a number
|
|
return json;
|
|
}
|
|
return num;
|
|
}
|
|
return json;
|
|
}
|
|
function parseJsonString(jsonString, typeName) {
|
|
try {
|
|
return JSON.parse(jsonString);
|
|
}
|
|
catch (e) {
|
|
const message = e instanceof Error ? e.message : String(e);
|
|
throw new Error(`cannot decode message ${typeName} from JSON: ${message}`,
|
|
// @ts-expect-error we use the ES2022 error CTOR option "cause" for better stack traces
|
|
{ cause: e });
|
|
}
|
|
}
|
|
function tryWktFromJson(msg, jsonValue, opts) {
|
|
if (!msg.desc.typeName.startsWith("google.protobuf.")) {
|
|
return false;
|
|
}
|
|
switch (msg.desc.typeName) {
|
|
case "google.protobuf.Any":
|
|
anyFromJson(msg.message, jsonValue, opts);
|
|
return true;
|
|
case "google.protobuf.Timestamp":
|
|
timestampFromJson(msg.message, jsonValue);
|
|
return true;
|
|
case "google.protobuf.Duration":
|
|
durationFromJson(msg.message, jsonValue);
|
|
return true;
|
|
case "google.protobuf.FieldMask":
|
|
fieldMaskFromJson(msg.message, jsonValue);
|
|
return true;
|
|
case "google.protobuf.Struct":
|
|
structFromJson(msg.message, jsonValue);
|
|
return true;
|
|
case "google.protobuf.Value":
|
|
valueFromJson(msg.message, jsonValue);
|
|
return true;
|
|
case "google.protobuf.ListValue":
|
|
listValueFromJson(msg.message, jsonValue);
|
|
return true;
|
|
default:
|
|
if ((0, index_js_1.isWrapperDesc)(msg.desc)) {
|
|
const valueField = msg.desc.fields[0];
|
|
if (jsonValue === null) {
|
|
msg.clear(valueField);
|
|
}
|
|
else {
|
|
msg.set(valueField, scalarFromJson(valueField, jsonValue, true));
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
function anyFromJson(any, json, opts) {
|
|
var _a;
|
|
if (json === null || Array.isArray(json) || typeof json != "object") {
|
|
throw new Error(`cannot decode message ${any.$typeName} from JSON: expected object but got ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
if (Object.keys(json).length == 0) {
|
|
return;
|
|
}
|
|
const typeUrl = json["@type"];
|
|
if (typeof typeUrl != "string" || typeUrl == "") {
|
|
throw new Error(`cannot decode message ${any.$typeName} from JSON: "@type" is empty`);
|
|
}
|
|
const typeName = typeUrl.includes("/")
|
|
? typeUrl.substring(typeUrl.lastIndexOf("/") + 1)
|
|
: typeUrl;
|
|
if (!typeName.length) {
|
|
throw new Error(`cannot decode message ${any.$typeName} from JSON: "@type" is invalid`);
|
|
}
|
|
const desc = (_a = opts.registry) === null || _a === void 0 ? void 0 : _a.getMessage(typeName);
|
|
if (!desc) {
|
|
throw new Error(`cannot decode message ${any.$typeName} from JSON: ${typeUrl} is not in the type registry`);
|
|
}
|
|
const msg = (0, reflect_js_1.reflect)(desc);
|
|
if (typeName.startsWith("google.protobuf.") &&
|
|
Object.prototype.hasOwnProperty.call(json, "value")) {
|
|
const value = json["value"];
|
|
readMessage(msg, value, opts);
|
|
}
|
|
else {
|
|
const copy = Object.assign({}, json);
|
|
delete copy["@type"];
|
|
readMessage(msg, copy, opts);
|
|
}
|
|
(0, index_js_1.anyPack)(msg.desc, msg.message, any);
|
|
}
|
|
function timestampFromJson(timestamp, json) {
|
|
if (typeof json !== "string") {
|
|
throw new Error(`cannot decode message ${timestamp.$typeName} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
const matches = json.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:Z|\.([0-9]{3,9})Z|([+-][0-9][0-9]:[0-9][0-9]))$/);
|
|
if (!matches) {
|
|
throw new Error(`cannot decode message ${timestamp.$typeName} from JSON: invalid RFC 3339 string`);
|
|
}
|
|
const ms = Date.parse(
|
|
//prettier-ignore
|
|
matches[1] + "-" + matches[2] + "-" + matches[3] + "T" + matches[4] + ":" + matches[5] + ":" + matches[6] + (matches[8] ? matches[8] : "Z"));
|
|
if (Number.isNaN(ms)) {
|
|
throw new Error(`cannot decode message ${timestamp.$typeName} from JSON: invalid RFC 3339 string`);
|
|
}
|
|
if (ms < Date.parse("0001-01-01T00:00:00Z") ||
|
|
ms > Date.parse("9999-12-31T23:59:59Z")) {
|
|
throw new Error(`cannot decode message ${timestamp.$typeName} from JSON: must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive`);
|
|
}
|
|
timestamp.seconds = proto_int64_js_1.protoInt64.parse(ms / 1000);
|
|
timestamp.nanos = 0;
|
|
if (matches[7]) {
|
|
timestamp.nanos =
|
|
parseInt("1" + matches[7] + "0".repeat(9 - matches[7].length)) -
|
|
1000000000;
|
|
}
|
|
}
|
|
function durationFromJson(duration, json) {
|
|
if (typeof json !== "string") {
|
|
throw new Error(`cannot decode message ${duration.$typeName} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
const match = json.match(/^(-?[0-9]+)(?:\.([0-9]+))?s/);
|
|
if (match === null) {
|
|
throw new Error(`cannot decode message ${duration.$typeName} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
const longSeconds = Number(match[1]);
|
|
if (longSeconds > 315576000000 || longSeconds < -315576000000) {
|
|
throw new Error(`cannot decode message ${duration.$typeName} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
duration.seconds = proto_int64_js_1.protoInt64.parse(longSeconds);
|
|
if (typeof match[2] !== "string") {
|
|
return;
|
|
}
|
|
const nanosStr = match[2] + "0".repeat(9 - match[2].length);
|
|
duration.nanos = parseInt(nanosStr);
|
|
if (longSeconds < 0 || Object.is(longSeconds, -0)) {
|
|
duration.nanos = -duration.nanos;
|
|
}
|
|
}
|
|
function fieldMaskFromJson(fieldMask, json) {
|
|
if (typeof json !== "string") {
|
|
throw new Error(`cannot decode message ${fieldMask.$typeName} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
if (json === "") {
|
|
return;
|
|
}
|
|
function camelToSnake(str) {
|
|
if (str.includes("_")) {
|
|
throw new Error(`cannot decode message ${fieldMask.$typeName} from JSON: path names must be lowerCamelCase`);
|
|
}
|
|
const sc = str.replace(/[A-Z]/g, (letter) => "_" + letter.toLowerCase());
|
|
return sc[0] === "_" ? sc.substring(1) : sc;
|
|
}
|
|
fieldMask.paths = json.split(",").map(camelToSnake);
|
|
}
|
|
function structFromJson(struct, json) {
|
|
if (typeof json != "object" || json == null || Array.isArray(json)) {
|
|
throw new Error(`cannot decode message ${struct.$typeName} from JSON ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
for (const [k, v] of Object.entries(json)) {
|
|
const parsedV = (0, create_js_1.create)(index_js_1.ValueSchema);
|
|
valueFromJson(parsedV, v);
|
|
struct.fields[k] = parsedV;
|
|
}
|
|
}
|
|
function valueFromJson(value, json) {
|
|
switch (typeof json) {
|
|
case "number":
|
|
value.kind = { case: "numberValue", value: json };
|
|
break;
|
|
case "string":
|
|
value.kind = { case: "stringValue", value: json };
|
|
break;
|
|
case "boolean":
|
|
value.kind = { case: "boolValue", value: json };
|
|
break;
|
|
case "object":
|
|
if (json === null) {
|
|
value.kind = { case: "nullValue", value: index_js_1.NullValue.NULL_VALUE };
|
|
}
|
|
else if (Array.isArray(json)) {
|
|
const listValue = (0, create_js_1.create)(index_js_1.ListValueSchema);
|
|
listValueFromJson(listValue, json);
|
|
value.kind = { case: "listValue", value: listValue };
|
|
}
|
|
else {
|
|
const struct = (0, create_js_1.create)(index_js_1.StructSchema);
|
|
structFromJson(struct, json);
|
|
value.kind = { case: "structValue", value: struct };
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`cannot decode message ${value.$typeName} from JSON ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
return value;
|
|
}
|
|
function listValueFromJson(listValue, json) {
|
|
if (!Array.isArray(json)) {
|
|
throw new Error(`cannot decode message ${listValue.$typeName} from JSON ${(0, reflect_check_js_1.formatVal)(json)}`);
|
|
}
|
|
for (const e of json) {
|
|
const value = (0, create_js_1.create)(index_js_1.ValueSchema);
|
|
valueFromJson(value, e);
|
|
listValue.values.push(value);
|
|
}
|
|
}
|