New:Socket for Asana Is Now Available.Learn more
Get Started

@bufbuild/protobuf

Package Overview
Dependencies
Maintainers
6
Versions
78
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bufbuild/protobuf - npm Package Compare versions

Comparing version
2.13.0
to
2.14.0
+49
dist/commonjs/reflect/message.d.ts
import type { DescField, DescMessage } from "../descriptors.js";
import type { JsonObject, JsonValue } from "../json-value.js";
import type { Struct } from "../wkt/gen/google/protobuf/struct_pb.js";
/**
* Mapper between the local representation of a message field value
* and the message it represents. For most fields, the local value is the
* message itself. Types from google/protobuf/wrappers.proto are unwrapped
* to the wrapped scalar value when used in a singular field that is not
* part of a oneof group, and google.protobuf.Struct is represented with
* JsonObject when used in a field, except when used in
* google.protobuf.Value.
*
* @private
*/
export interface LocalMessageMapper {
/**
* Wrap a local value in the message it represents. For undefined - an
* unset field - a new message is created. Like the reflect API, wrapping
* an existing Struct field value creates a normalized copy, so that
* merging does not mutate the previous value in place.
*/
toMessage(local: unknown): Record<string, unknown>;
/**
* Convert a message to the local representation of the field value.
*/
toLocal(message: Record<string, unknown>): unknown;
}
/**
* Return the conversions between the local representation of the field
* value and the message it represents.
*
* @private
*/
export declare function localMessageMapper(field: DescField & {
message: DescMessage;
}): LocalMessageMapper;
/**
* Convert the JsonValue representation of a google.protobuf.Struct to the
* message representation.
*
* @private
*/
export declare function wktStructToReflect(json: JsonValue): Struct;
/**
* Convert a google.protobuf.Struct message to its JsonValue representation.
*
* @private
*/
export declare function wktStructToLocal(val: Struct): JsonObject;
"use strict";
// Copyright 2021-2026 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.localMessageMapper = localMessageMapper;
exports.wktStructToReflect = wktStructToReflect;
exports.wktStructToLocal = wktStructToLocal;
const create_js_1 = require("../create.js");
const guard_js_1 = require("./guard.js");
const wrappers_js_1 = require("../wkt/wrappers.js");
// google.protobuf.NullValue.NULL_VALUE;
const NULL_VALUE = 0;
/**
* Return the conversions between the local representation of the field
* value and the message it represents.
*
* @private
*/
function localMessageMapper(field) {
// google.protobuf.Struct fields are stored as JsonObject.
if (usesJsonRepresentation(field)) {
return {
toMessage: (local) => wktStructToReflect(local),
toLocal: (message) => wktStructToLocal(message),
};
}
// Singular wrapper fields outside a oneof are unwrapped to the scalar value.
if (field.fieldKind == "message" &&
!field.oneof &&
(0, wrappers_js_1.isWrapperDesc)(field.message)) {
const wrapperDesc = field.message;
const valueLocalName = wrapperDesc.fields[0].localName;
return {
toMessage: (local) => {
const message = (0, create_js_1.create)(wrapperDesc);
if (local !== undefined) {
message[valueLocalName] = local;
}
return message;
},
toLocal: (message) => message[valueLocalName],
};
}
// For all other fields, the local value is the message itself.
const childDesc = field.message;
return {
toMessage: (local) => (local === undefined ? (0, create_js_1.create)(childDesc) : local),
toLocal: (message) => message,
};
}
/**
* Returns true if values of this field are stored as JsonValue instead of
* a message: google.protobuf.Struct is represented with JsonObject when
* used in a field, except when used in google.protobuf.Value.
*/
function usesJsonRepresentation(field) {
return (field.message.typeName == "google.protobuf.Struct" &&
field.parent.typeName != "google.protobuf.Value");
}
/**
* Convert the JsonValue representation of a google.protobuf.Struct to the
* message representation.
*
* @private
*/
function wktStructToReflect(json) {
const struct = {
$typeName: "google.protobuf.Struct",
fields: {},
};
if ((0, guard_js_1.isObject)(json)) {
for (const k of Object.keys(json)) {
struct.fields[k] = wktValueToReflect(json[k]);
}
}
return struct;
}
/**
* Convert a google.protobuf.Struct message to its JsonValue representation.
*
* @private
*/
function wktStructToLocal(val) {
const json = {};
for (const k of Object.keys(val.fields)) {
json[k] = wktValueToLocal(val.fields[k]);
}
return json;
}
function wktValueToLocal(val) {
switch (val.kind.case) {
case "structValue":
return wktStructToLocal(val.kind.value);
case "listValue":
return val.kind.value.values.map(wktValueToLocal);
case "nullValue":
case undefined:
return null;
default:
return val.kind.value;
}
}
function wktValueToReflect(json) {
const value = {
$typeName: "google.protobuf.Value",
kind: { case: undefined },
};
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: NULL_VALUE };
}
else if (Array.isArray(json)) {
const listValue = {
$typeName: "google.protobuf.ListValue",
values: [],
};
if (Array.isArray(json)) {
for (const e of json) {
listValue.values.push(wktValueToReflect(e));
}
}
value.kind = {
case: "listValue",
value: listValue,
};
}
else {
value.kind = {
case: "structValue",
value: wktStructToReflect(json),
};
}
break;
}
return value;
}
/**
* Minimum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const timestampMsMin: number;
/**
* Maximum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const timestampMsMax: number;
/**
* Minimum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const durationSecondsMin = -315576000000;
/**
* Maximum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const durationSecondsMax = 315576000000;
"use strict";
// Copyright 2021-2026 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.durationSecondsMax = exports.durationSecondsMin = exports.timestampMsMax = exports.timestampMsMin = void 0;
/**
* Minimum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
exports.timestampMsMin = Date.parse("0001-01-01T00:00:00Z");
/**
* Maximum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
exports.timestampMsMax = Date.parse("9999-12-31T23:59:59Z");
/**
* Minimum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
exports.durationSecondsMin = -315576000000;
/**
* Maximum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
exports.durationSecondsMax = 315576000000;
import type { DescField, DescMessage } from "../descriptors.js";
import type { JsonObject, JsonValue } from "../json-value.js";
import type { Struct } from "../wkt/gen/google/protobuf/struct_pb.js";
/**
* Mapper between the local representation of a message field value
* and the message it represents. For most fields, the local value is the
* message itself. Types from google/protobuf/wrappers.proto are unwrapped
* to the wrapped scalar value when used in a singular field that is not
* part of a oneof group, and google.protobuf.Struct is represented with
* JsonObject when used in a field, except when used in
* google.protobuf.Value.
*
* @private
*/
export interface LocalMessageMapper {
/**
* Wrap a local value in the message it represents. For undefined - an
* unset field - a new message is created. Like the reflect API, wrapping
* an existing Struct field value creates a normalized copy, so that
* merging does not mutate the previous value in place.
*/
toMessage(local: unknown): Record<string, unknown>;
/**
* Convert a message to the local representation of the field value.
*/
toLocal(message: Record<string, unknown>): unknown;
}
/**
* Return the conversions between the local representation of the field
* value and the message it represents.
*
* @private
*/
export declare function localMessageMapper(field: DescField & {
message: DescMessage;
}): LocalMessageMapper;
/**
* Convert the JsonValue representation of a google.protobuf.Struct to the
* message representation.
*
* @private
*/
export declare function wktStructToReflect(json: JsonValue): Struct;
/**
* Convert a google.protobuf.Struct message to its JsonValue representation.
*
* @private
*/
export declare function wktStructToLocal(val: Struct): JsonObject;
// Copyright 2021-2026 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.
import { create } from "../create.js";
import { isObject } from "./guard.js";
import { isWrapperDesc } from "../wkt/wrappers.js";
// google.protobuf.NullValue.NULL_VALUE;
const NULL_VALUE = 0;
/**
* Return the conversions between the local representation of the field
* value and the message it represents.
*
* @private
*/
export function localMessageMapper(field) {
// google.protobuf.Struct fields are stored as JsonObject.
if (usesJsonRepresentation(field)) {
return {
toMessage: (local) => wktStructToReflect(local),
toLocal: (message) => wktStructToLocal(message),
};
}
// Singular wrapper fields outside a oneof are unwrapped to the scalar value.
if (field.fieldKind == "message" &&
!field.oneof &&
isWrapperDesc(field.message)) {
const wrapperDesc = field.message;
const valueLocalName = wrapperDesc.fields[0].localName;
return {
toMessage: (local) => {
const message = create(wrapperDesc);
if (local !== undefined) {
message[valueLocalName] = local;
}
return message;
},
toLocal: (message) => message[valueLocalName],
};
}
// For all other fields, the local value is the message itself.
const childDesc = field.message;
return {
toMessage: (local) => (local === undefined ? create(childDesc) : local),
toLocal: (message) => message,
};
}
/**
* Returns true if values of this field are stored as JsonValue instead of
* a message: google.protobuf.Struct is represented with JsonObject when
* used in a field, except when used in google.protobuf.Value.
*/
function usesJsonRepresentation(field) {
return (field.message.typeName == "google.protobuf.Struct" &&
field.parent.typeName != "google.protobuf.Value");
}
/**
* Convert the JsonValue representation of a google.protobuf.Struct to the
* message representation.
*
* @private
*/
export function wktStructToReflect(json) {
const struct = {
$typeName: "google.protobuf.Struct",
fields: {},
};
if (isObject(json)) {
for (const k of Object.keys(json)) {
struct.fields[k] = wktValueToReflect(json[k]);
}
}
return struct;
}
/**
* Convert a google.protobuf.Struct message to its JsonValue representation.
*
* @private
*/
export function wktStructToLocal(val) {
const json = {};
for (const k of Object.keys(val.fields)) {
json[k] = wktValueToLocal(val.fields[k]);
}
return json;
}
function wktValueToLocal(val) {
switch (val.kind.case) {
case "structValue":
return wktStructToLocal(val.kind.value);
case "listValue":
return val.kind.value.values.map(wktValueToLocal);
case "nullValue":
case undefined:
return null;
default:
return val.kind.value;
}
}
function wktValueToReflect(json) {
const value = {
$typeName: "google.protobuf.Value",
kind: { case: undefined },
};
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: NULL_VALUE };
}
else if (Array.isArray(json)) {
const listValue = {
$typeName: "google.protobuf.ListValue",
values: [],
};
if (Array.isArray(json)) {
for (const e of json) {
listValue.values.push(wktValueToReflect(e));
}
}
value.kind = {
case: "listValue",
value: listValue,
};
}
else {
value.kind = {
case: "structValue",
value: wktStructToReflect(json),
};
}
break;
}
return value;
}
/**
* Minimum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const timestampMsMin: number;
/**
* Maximum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const timestampMsMax: number;
/**
* Minimum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const durationSecondsMin = -315576000000;
/**
* Maximum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
export declare const durationSecondsMax = 315576000000;
// Copyright 2021-2026 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.
/**
* Minimum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
export const timestampMsMin = /*@__PURE__*/ Date.parse("0001-01-01T00:00:00Z");
/**
* Maximum google.protobuf.Timestamp in milliseconds (inclusive).
* Only enforced in ProtoJSON.
*
* @private
*/
export const timestampMsMax = /*@__PURE__*/ Date.parse("9999-12-31T23:59:59Z");
/**
* Minimum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
export const durationSecondsMin = -315576000000;
/**
* Maximum google.protobuf.Duration in seconds.
* Only enforced in ProtoJSON.
*
* @private
*/
export const durationSecondsMax = 315576000000;
+199
-162

@@ -21,3 +21,2 @@ "use strict";

const guard_js_1 = require("./reflect/guard.js");
const unsafe_js_1 = require("./reflect/unsafe.js");
const wrappers_js_1 = require("./wkt/wrappers.js");

@@ -40,77 +39,197 @@ // bootstrap-inject google.protobuf.Edition.EDITION_PROTO3: const $name = $number;

}
const message = createZeroMessage(schema);
if (init !== undefined) {
initMessage(schema, message, init);
return compiledCreate(schema)(init);
}
const compiledCreates = new WeakMap();
/**
* Return the compiled create function for a message, compiling it on first use. */
function compiledCreate(desc) {
let compiled = compiledCreates.get(desc);
if (compiled === undefined) {
compiled = compileCreate(desc);
compiledCreates.set(desc, compiled);
}
return message;
return compiled;
}
/** Singular field: scalar, enum, or message. */
const INIT_SINGULAR = 0;
/** List field: a zero message has a fresh empty array. */
const INIT_LIST = 1;
/** Map field: a zero message has a fresh empty object. */
const INIT_MAP = 2;
/** Oneof group: the ADT is always stored, cases convert by case name. */
const INIT_ONEOF = 3;
/* Compile the create function for this message type. */
function compileCreate(desc) {
const typeName = desc.typeName;
const { properties, prototype } = compileInitMessage(desc);
return (init) => {
let message;
if (prototype !== undefined) {
message = Object.create(prototype);
message.$typeName = typeName;
}
else {
message = { $typeName: typeName };
}
for (let i = 0; i < properties.length; i++) {
const property = properties[i];
const name = property.name;
const initValue = init === null || init === void 0 ? void 0 : init[name];
switch (property.kind) {
case INIT_SINGULAR:
if (initValue != null) {
message[name] =
property.convert !== undefined
? property.convert(initValue)
: initValue;
}
else if (property.constant !== undefined) {
message[name] = property.constant;
}
break;
case INIT_LIST:
message[name] =
property.convert !== undefined && Array.isArray(initValue)
? initValue.map(property.convert)
: (initValue !== null && initValue !== void 0 ? initValue : []);
break;
case INIT_MAP:
// Object.create(null) would be desirable for the fresh map, but is
// unsupported by React:
// https://react.dev/reference/react/use-server#serializable-parameters-and-return-values
if (property.convert === undefined || !(0, guard_js_1.isObject)(initValue)) {
message[name] = initValue !== null && initValue !== void 0 ? initValue : {};
}
else {
const converted = {};
const keys = Object.keys(initValue);
for (let k = 0; k < keys.length; k++) {
converted[keys[k]] = property.convert(initValue[keys[k]]);
}
message[name] = converted;
}
break;
case INIT_ONEOF: {
const oneofValue = initValue;
if ((oneofValue === null || oneofValue === void 0 ? void 0 : oneofValue.case) != null) {
const convert = property.convert.get(oneofValue.case);
if (convert !== undefined) {
message[name] = {
case: oneofValue.case,
value: convert(oneofValue.value),
};
break;
}
}
message[name] = { case: undefined };
break;
}
}
}
return message;
};
}
/**
* Sets field values from a MessageInitShape on a zero message.
* Classify every member once, so that creating a message is a walk over a
* compact list instead of a walk over the descriptor.
*/
function initMessage(messageDesc, message, init) {
for (const member of messageDesc.members) {
let value = init[member.localName];
if (value == null) {
// intentionally ignore undefined and null
function compileInitMessage(desc) {
var _a, _b;
const properties = [];
const prototype = {};
const usePrototype = needsPrototypeChain(desc);
for (const member of desc.members) {
const name = member.localName;
if (member.kind == "oneof") {
properties.push({
name,
kind: INIT_ONEOF,
constant: undefined,
convert: compileConvertOneof(member),
});
continue;
}
let field;
if (member.kind == "oneof") {
const oneofField = (0, unsafe_js_1.unsafeOneofCase)(init, member);
if (!oneofField) {
continue;
switch (member.fieldKind) {
case "message": {
// Singular message fields are absent from a zero message.
properties.push({
name,
kind: INIT_SINGULAR,
constant: undefined,
convert: compileConvertMessage(member),
});
break;
}
field = oneofField;
value = (0, unsafe_js_1.unsafeGet)(init, oneofField);
}
else {
field = member;
}
switch (field.fieldKind) {
case "message":
value = toMessage(field, value);
case "list": {
properties.push({
name,
kind: INIT_LIST,
constant: undefined,
convert: member.listKind == "message"
? ((_a = compileConvertMessage(member)) !== null && _a !== void 0 ? _a : ((value) => value))
: member.scalar == descriptors_js_1.ScalarType.BYTES
? toU8Arr
: undefined,
});
break;
case "scalar":
value = initScalar(field, value);
}
case "map": {
properties.push({
name,
kind: INIT_MAP,
constant: undefined,
convert: member.mapKind == "message"
? ((_b = compileConvertMessage(member)) !== null && _b !== void 0 ? _b : ((value) => value))
: member.scalar == descriptors_js_1.ScalarType.BYTES
? toU8Arr
: undefined,
});
break;
case "list":
value = initList(field, value);
}
default: {
const zeroValue = createZeroValue(member);
properties.push({
name,
kind: INIT_SINGULAR,
constant: member.presence == IMPLICIT ? zeroValue : undefined,
convert: member.fieldKind == "scalar" && member.scalar == descriptors_js_1.ScalarType.BYTES
? toU8Arr
: undefined,
});
if (usePrototype) {
prototype[name] = zeroValue;
}
break;
case "map":
value = initMap(field, value);
break;
}
}
(0, unsafe_js_1.unsafeSet)(message, field, value);
}
return message;
return {
properties,
prototype: usePrototype ? prototype : undefined,
};
}
function initScalar(field, value) {
if (field.scalar == descriptors_js_1.ScalarType.BYTES) {
return toU8Arr(value);
}
return value;
}
function initMap(field, value) {
if ((0, guard_js_1.isObject)(value)) {
if (field.scalar == descriptors_js_1.ScalarType.BYTES) {
return convertObjectValues(value, toU8Arr);
/**
* Compile the conversion of each case of a oneof group, keyed by case name.
*/
function compileConvertOneof(oneof) {
const converters = new Map();
for (const field of oneof.fields) {
let convert;
if (field.fieldKind == "message") {
convert = compileConvertMessage(field);
}
if (field.mapKind == "message") {
return convertObjectValues(value, (val) => toMessage(field, val));
else if (field.fieldKind == "scalar" &&
field.scalar == descriptors_js_1.ScalarType.BYTES) {
convert = toU8Arr;
}
converters.set(field.localName, convert !== null && convert !== void 0 ? convert : ((value) => value));
}
return value;
return converters;
}
function initList(field, value) {
if (Array.isArray(value)) {
if (field.scalar == descriptors_js_1.ScalarType.BYTES) {
return value.map(toU8Arr);
}
if (field.listKind == "message") {
return value.map((item) => toMessage(field, item));
}
}
return value;
}
function toMessage(field, value) {
/**
* Compile the conversion of an init value for a message field, a message
* list item, or a message map value. Returns undefined if values are used
* as-is.
*/
function compileConvertMessage(field) {
if (field.fieldKind == "message" &&

@@ -121,16 +240,23 @@ !field.oneof &&

// a singular field that is not part of a oneof group.
return initScalar(field.message.fields[0], value);
return field.message.fields[0].scalar == descriptors_js_1.ScalarType.BYTES
? toU8Arr
: undefined;
}
if ((0, guard_js_1.isObject)(value)) {
if (field.message.typeName == "google.protobuf.Struct" &&
field.parent.typeName !== "google.protobuf.Value") {
// google.protobuf.Struct is represented with JsonObject when used in a
// field, except when used in google.protobuf.Value.
if (field.message.typeName == "google.protobuf.Struct" &&
field.parent.typeName !== "google.protobuf.Value") {
// google.protobuf.Struct is represented with JsonObject when used in a
// field, except when used in google.protobuf.Value.
return undefined;
}
const messageDesc = field.message;
// Resolved on first use, not here: the message type can be this very field's
// parent, whose create function is still being compiled.
let compiled;
return (value) => {
if (!(0, guard_js_1.isObject)(value) || (0, is_message_js_1.isMessage)(value, messageDesc)) {
return value;
}
if (!(0, is_message_js_1.isMessage)(value, field.message)) {
return create(field.message, value);
}
}
return value;
compiled !== null && compiled !== void 0 ? compiled : (compiled = compiledCreate(messageDesc));
return compiled(value);
};
}

@@ -141,80 +267,3 @@ // converts any ArrayLike<number> to Uint8Array if necessary.

}
function convertObjectValues(obj, fn) {
const ret = {};
for (const entry of Object.entries(obj)) {
ret[entry[0]] = fn(entry[1]);
}
return ret;
}
const tokenZeroMessageField = Symbol();
const messagePrototypes = new WeakMap();
/**
* Create a zero message.
*/
function createZeroMessage(desc) {
let msg;
if (!needsPrototypeChain(desc)) {
msg = {
$typeName: desc.typeName,
};
for (const member of desc.members) {
if (member.kind == "oneof" || member.presence == IMPLICIT) {
msg[member.localName] = createZeroField(member);
}
}
}
else {
// Support default values and track presence via the prototype chain
const cached = messagePrototypes.get(desc);
let prototype;
let members;
if (cached) {
({ prototype, members } = cached);
}
else {
prototype = {};
members = new Set();
for (const member of desc.members) {
if (member.kind == "oneof") {
// we can only put immutable values on the prototype,
// oneof ADTs are mutable
continue;
}
if (member.fieldKind != "scalar" && member.fieldKind != "enum") {
// only scalar and enum values are immutable, map, list, and message
// are not
continue;
}
if (member.presence == IMPLICIT) {
// implicit presence tracks field presence by zero values - e.g. 0, false, "", are unset, 1, true, "x" are set.
// message, map, list fields are mutable, and also have IMPLICIT presence.
continue;
}
members.add(member);
prototype[member.localName] = createZeroField(member);
}
messagePrototypes.set(desc, { prototype, members });
}
msg = Object.create(prototype);
msg.$typeName = desc.typeName;
for (const member of desc.members) {
if (members.has(member)) {
continue;
}
if (member.kind == "field") {
if (member.fieldKind == "message") {
continue;
}
if (member.fieldKind == "scalar" || member.fieldKind == "enum") {
if (member.presence != IMPLICIT) {
continue;
}
}
}
msg[member.localName] = createZeroField(member);
}
}
return msg;
}
/**
* Do we need the prototype chain to track field presence?

@@ -238,18 +287,6 @@ */

/**
* Returns a zero value for oneof groups, and for every field kind except
* messages. Scalar and enum fields can have default values.
* Returns the zero value for a scalar or enum field. Scalar and enum fields
* can have default values.
*/
function createZeroField(field) {
if (field.kind == "oneof") {
return { case: undefined };
}
if (field.fieldKind == "list") {
return [];
}
if (field.fieldKind == "map") {
return {}; // Object.create(null) would be desirable here, but is unsupported by react https://react.dev/reference/react/use-server#serializable-parameters-and-return-values
}
if (field.fieldKind == "message") {
return tokenZeroMessageField;
}
function createZeroValue(field) {
const defaultValue = field.getDefaultValue();

@@ -256,0 +293,0 @@ if (defaultValue !== undefined) {

@@ -22,3 +22,6 @@ "use strict";

const scalar_js_1 = require("./reflect/scalar.js");
const reflect_js_1 = require("./reflect/reflect.js");
const error_js_1 = require("./reflect/error.js");
const unsafe_js_1 = require("./reflect/unsafe.js");
const message_js_1 = require("./reflect/message.js");
const create_js_1 = require("./create.js");
const binary_encoding_js_1 = require("./wire/binary-encoding.js");

@@ -36,5 +39,5 @@ const varint_js_1 = require("./wire/varint.js");

function fromBinary(schema, bytes, options) {
const msg = (0, reflect_js_1.reflect)(schema, undefined, false);
readMessage(msg, new binary_encoding_js_1.BinaryReader(bytes), makeReadContext(options), false, bytes.byteLength);
return msg.message;
const message = (0, create_js_1.create)(schema);
compiledReader(schema).read(message, new binary_encoding_js_1.BinaryReader(bytes), makeReadContext(options), bytes.byteLength);
return message;
}

@@ -51,48 +54,103 @@ /**

function mergeFromBinary(schema, target, bytes, options) {
readMessage((0, reflect_js_1.reflect)(schema, target, false), new binary_encoding_js_1.BinaryReader(bytes), makeReadContext(options), false, bytes.byteLength);
if (target.$typeName !== schema.typeName &&
schema.fields.length > 0) {
throw new error_js_1.FieldError(schema.fields[0], `cannot use ${schema.fields[0]} with message ${target.$typeName}`, "ForeignFieldError");
}
compiledReader(schema).read(target, new binary_encoding_js_1.BinaryReader(bytes), makeReadContext(options), bytes.byteLength);
return target;
}
const compiledReaders = new WeakMap();
/**
* If `delimited` is false, read the length given in `lengthOrDelimitedFieldNo`.
*
* If `delimited` is true, read until an EndGroup tag. `lengthOrDelimitedFieldNo`
* is the expected field number.
*
* @private
* Return the compiled decoder for a message, compiling it on first use.
*/
function readMessage(message, reader, ctx, delimited, lengthOrDelimitedFieldNo) {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${message.desc} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
function compiledReader(desc) {
let compiled = compiledReaders.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
const end = delimited ? reader.len : reader.pos + lengthOrDelimitedFieldNo;
let fieldNo;
let wireType;
const unknownFields = (_a = message.getUnknown()) !== null && _a !== void 0 ? _a : [];
while (reader.pos < end) {
[fieldNo, wireType] = reader.tag();
if (delimited && wireType == binary_encoding_js_1.WireType.EndGroup) {
break;
return compiled;
}
function compileMessage(desc) {
const descString = String(desc);
const fieldReaders = new Map();
const compiled = {
read: compileMessageReader(descString, fieldReaders),
readGroup: compileGroupReader(descString, fieldReaders),
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledReaders.set(desc, compiled);
for (const field of desc.fields) {
fieldReaders.set(field.number, compileFieldReader(field));
}
return compiled;
}
/**
* Create a decoder for a length-prefixed message body, dispatching wire
* records to the compiled field decoders by field number.
*/
function compileMessageReader(descString, fieldReaders) {
return (message, reader, ctx, length) => {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
const field = message.findNumber(fieldNo);
if (!field) {
// Use remaining recursion budget for skipping nested groups
const recursionLimit = ctx.recursionLimit - ctx.depth;
const data = reader.skip(wireType, fieldNo, recursionLimit);
if (ctx.readUnknownFields) {
unknownFields.push({ no: fieldNo, wireType, data });
const end = reader.pos + length;
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
while (reader.pos < end) {
const [fieldNo, wireType] = reader.tag();
const fieldReader = fieldReaders.get(fieldNo);
if (fieldReader === undefined) {
// Use remaining recursion budget for skipping nested groups
const data = reader.skip(wireType, fieldNo, ctx.recursionLimit - ctx.depth);
if (ctx.readUnknownFields) {
unknownFields.push({ no: fieldNo, wireType, data });
}
continue;
}
continue;
fieldReader(message, reader, ctx, wireType);
}
readField(message, reader, field, wireType, ctx);
}
if (delimited) {
if (wireType != binary_encoding_js_1.WireType.EndGroup || fieldNo !== lengthOrDelimitedFieldNo) {
if (unknownFields.length > 0) {
message.$unknown = unknownFields;
}
ctx.depth--;
};
}
/**
* Create a decoder for a message with the delimited encoding (group),
* reading until the EndGroup tag, like compileMessageReader.
*/
function compileGroupReader(descString, fieldReaders) {
return (message, reader, ctx, fieldNo) => {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
let recordFieldNo;
let wireType;
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
while (reader.pos < reader.len) {
[recordFieldNo, wireType] = reader.tag();
if (wireType == binary_encoding_js_1.WireType.EndGroup) {
break;
}
const fieldReader = fieldReaders.get(recordFieldNo);
if (fieldReader === undefined) {
// Use remaining recursion budget for skipping nested groups
const data = reader.skip(wireType, recordFieldNo, ctx.recursionLimit - ctx.depth);
if (ctx.readUnknownFields) {
unknownFields.push({ no: recordFieldNo, wireType, data });
}
continue;
}
fieldReader(message, reader, ctx, wireType);
}
if (wireType != binary_encoding_js_1.WireType.EndGroup || recordFieldNo !== fieldNo) {
throw new Error("invalid end group tag");
}
}
if (unknownFields.length > 0) {
message.setUnknown(unknownFields);
}
ctx.depth--;
if (unknownFields.length > 0) {
message.$unknown = unknownFields;
}
ctx.depth--;
};
}

@@ -103,149 +161,259 @@ /**

function readField(message, reader, field, wireType, ctx) {
var _a;
compileFieldReader(field)(message[unsafe_js_1.unsafeLocal], reader, ctx, wireType);
}
function compileFieldReader(field) {
switch (field.fieldKind) {
case "scalar":
message.set(field, readScalar(reader, field.scalar, field.utf8Validation));
break;
return compileScalarFieldReader(field);
case "enum":
const val = readScalar(reader, descriptors_js_1.ScalarType.INT32);
if (field.enum.open) {
message.set(field, val);
}
else {
const ok = field.enum.values.some((v) => v.number === val);
if (ok) {
message.set(field, val);
}
else if (ctx.readUnknownFields) {
const bytes = [];
(0, varint_js_1.varint32write)(val, bytes);
const unknownFields = (_a = message.getUnknown()) !== null && _a !== void 0 ? _a : [];
unknownFields.push({
no: field.number,
wireType,
data: new Uint8Array(bytes),
});
message.setUnknown(unknownFields);
}
}
break;
return compileEnumFieldReader(field);
case "message":
message.set(field, readMessageField(reader, ctx, field, message.get(field)));
break;
return compileMessageFieldReader(field);
case "list":
readListField(reader, wireType, message.get(field), ctx);
break;
return compileListFieldReader(field);
case "map":
readMapEntry(reader, message.get(field), ctx);
break;
return compileMapFieldReader(field);
}
}
// Read a map field, expecting key field = 1, value field = 2
function readMapEntry(reader, map, ctx) {
const field = map.field();
let key;
let val;
// Read the length of the map entry, which is a varint.
const len = reader.uint32();
// WARNING: Calculate end AFTER advancing reader.pos (above), so that
// reader.pos is at the start of the map entry.
const end = reader.pos + len;
while (reader.pos < end) {
const [fieldNo] = reader.tag();
switch (fieldNo) {
case 1:
key = readScalar(reader, field.mapKey, field.utf8Validation);
break;
case 2:
switch (field.mapKind) {
case "scalar":
val = readScalar(reader, field.scalar, field.utf8Validation);
break;
case "enum":
val = reader.int32();
break;
case "message":
val = readMessageField(reader, ctx, field);
break;
}
break;
function compileScalarFieldReader(field) {
const readScalar = compileScalarReader(field.scalar, field.utf8Validation, field.longAsString);
const localName = field.localName;
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, reader) => {
message[oneofLocalName] = {
case: localName,
value: readScalar(reader),
};
};
}
return (message, reader) => {
message[localName] = readScalar(reader);
};
}
function compileEnumFieldReader(field) {
var _a;
const localName = field.localName;
const oneofLocalName = (_a = field.oneof) === null || _a === void 0 ? void 0 : _a.localName;
if (field.enum.open) {
if (oneofLocalName !== undefined) {
return (message, reader) => {
message[oneofLocalName] = { case: localName, value: reader.int32() };
};
}
return (message, reader) => {
message[localName] = reader.int32();
};
}
if (key === undefined) {
key = (0, scalar_js_1.scalarZeroValue)(field.mapKey, false);
}
if (val === undefined) {
switch (field.mapKind) {
case "scalar":
val = (0, scalar_js_1.scalarZeroValue)(field.scalar, false);
break;
case "enum":
val = field.enum.values[0].number;
break;
case "message":
val = (0, reflect_js_1.reflect)(field.message, undefined, false);
break;
// Closed enums: unknown values are stored as unknown fields.
const values = field.enum.values;
const fieldNo = field.number;
return (message, reader, ctx, wireType) => {
var _a;
const val = reader.int32();
if (values.some((v) => v.number === val)) {
if (oneofLocalName !== undefined) {
message[oneofLocalName] = { case: localName, value: val };
}
else {
message[localName] = val;
}
}
else if (ctx.readUnknownFields) {
const bytes = [];
(0, varint_js_1.varint32write)(val, bytes);
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
unknownFields.push({
no: fieldNo,
wireType,
data: new Uint8Array(bytes),
});
message.$unknown = unknownFields;
}
};
}
function compileMessageFieldReader(field) {
const localName = field.localName;
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
const readChild = compileChildReader(field);
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, reader, ctx) => {
const oneof = message[oneofLocalName];
const child = toMessage(oneof.case === localName ? oneof.value : undefined);
readChild(child, reader, ctx);
message[oneofLocalName] = { case: localName, value: toLocal(child) };
};
}
map.set(key, val);
return (message, reader, ctx) => {
const child = toMessage(message[localName]);
readChild(child, reader, ctx);
message[localName] = toLocal(child);
};
}
function readListField(reader, wireType, list, ctx) {
var _a;
const field = list.field();
if (field.listKind === "message") {
list.add(readMessageField(reader, ctx, field));
return;
/**
* Compile a decoder for the wire format of a message field, honoring the
* delimited encoding of the field.
*/
function compileChildReader(field) {
const compiledChild = compiledReader(field.message);
if (field.delimitedEncoding) {
const fieldNo = field.number;
return (child, reader, ctx) => compiledChild.readGroup(child, reader, ctx, fieldNo);
}
const scalarType = (_a = field.scalar) !== null && _a !== void 0 ? _a : descriptors_js_1.ScalarType.INT32;
const packed = wireType == binary_encoding_js_1.WireType.LengthDelimited &&
scalarType != descriptors_js_1.ScalarType.STRING &&
scalarType != descriptors_js_1.ScalarType.BYTES;
if (!packed) {
list.add(readScalar(reader, scalarType, field.utf8Validation));
return;
return (child, reader, ctx) => compiledChild.read(child, reader, ctx, reader.uint32());
}
function compileListFieldReader(field) {
const localName = field.localName;
if (field.listKind == "message") {
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
const readChild = compileChildReader(field);
return (message, reader, ctx) => {
const child = toMessage(undefined);
readChild(child, reader, ctx);
message[localName].push(toLocal(child));
};
}
const e = reader.uint32() + reader.pos;
while (reader.pos < e) {
list.add(readScalar(reader, scalarType, field.utf8Validation));
const scalarType = field.listKind == "enum" ? descriptors_js_1.ScalarType.INT32 : field.scalar;
const longAsString = field.listKind == "scalar" ? field.longAsString : false;
const readScalar = compileScalarReader(scalarType, field.utf8Validation, longAsString);
const packedPossible = scalarType != descriptors_js_1.ScalarType.STRING && scalarType != descriptors_js_1.ScalarType.BYTES;
return (message, reader, ctx, wireType) => {
const items = message[localName];
if (wireType == binary_encoding_js_1.WireType.LengthDelimited && packedPossible) {
const end = reader.uint32() + reader.pos;
while (reader.pos < end) {
items.push(readScalar(reader));
}
}
else {
items.push(readScalar(reader));
}
};
}
function compileMapFieldReader(field) {
const localName = field.localName;
const readKey = compileScalarReader(field.mapKey, field.utf8Validation, false);
const keyZero = (0, scalar_js_1.scalarZeroValue)(field.mapKey, false);
let readValue;
let valueDefault;
switch (field.mapKind) {
case "scalar": {
const scalar = field.scalar;
const readScalar = compileScalarReader(scalar, field.utf8Validation, false);
readValue = (reader) => readScalar(reader);
// Bytes zero values are created per entry, so that entries do not share
// one instance.
if (scalar == descriptors_js_1.ScalarType.BYTES) {
valueDefault = () => new Uint8Array(0);
}
else {
const zero = (0, scalar_js_1.scalarZeroValue)(scalar, false);
valueDefault = () => zero;
}
break;
}
case "enum": {
const zero = field.enum.values[0].number;
readValue = (reader) => reader.int32();
valueDefault = () => zero;
break;
}
case "message": {
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
const readChild = compiledReader(field.message).read;
readValue = (reader, ctx) => {
const child = toMessage(undefined);
readChild(child, reader, ctx, reader.uint32());
return toLocal(child);
};
valueDefault = () => toLocal(toMessage(undefined));
break;
}
}
return (message, reader, ctx) => {
const record = message[localName];
let key;
let val;
// Read the length of the map entry, which is a varint.
const len = reader.uint32();
// Calculate end AFTER advancing reader.pos (above), so that reader.pos is
// at the start of the map entry.
const end = reader.pos + len;
while (reader.pos < end) {
// Map entries have the key in field 1, and the value in field 2.
const [fieldNo] = reader.tag();
switch (fieldNo) {
case 1:
key = readKey(reader);
break;
case 2:
val = readValue(reader, ctx);
break;
}
}
if (key === undefined) {
key = keyZero;
}
if (val === undefined) {
val = valueDefault();
}
// Object property keys are always strings or symbols. Assigning with a
// boolean, number, or bigint key implicitly converts it to a string.
record[key] = val;
};
}
function readMessageField(reader, ctx, field, mergeMessage) {
const delimited = field.delimitedEncoding;
const message = mergeMessage !== null && mergeMessage !== void 0 ? mergeMessage : (0, reflect_js_1.reflect)(field.message, undefined, false);
readMessage(message, reader, ctx, delimited, delimited ? field.number : reader.uint32());
return message;
}
function readScalar(reader, type, validateUtf8 = false) {
/**
* Returns a reader for a scalar value. For 64-bit integers, BinaryReader
* already returns the local representation (bigint or string), so, unlike in
* the reflection layer, no validation is needed here.
*/
function compileScalarReader(type, utf8Validation, longAsString) {
switch (type) {
case descriptors_js_1.ScalarType.STRING:
return reader.string(validateUtf8);
return (reader) => reader.string(utf8Validation);
case descriptors_js_1.ScalarType.BOOL:
return reader.bool();
return (reader) => reader.bool();
case descriptors_js_1.ScalarType.DOUBLE:
return reader.double();
return (reader) => reader.double();
case descriptors_js_1.ScalarType.FLOAT:
return reader.float();
return (reader) => reader.float();
case descriptors_js_1.ScalarType.INT32:
return reader.int32();
return (reader) => reader.int32();
case descriptors_js_1.ScalarType.INT64:
return reader.int64();
if (longAsString) {
return (reader) => String(reader.int64());
}
return (reader) => reader.int64();
case descriptors_js_1.ScalarType.UINT64:
return reader.uint64();
if (longAsString) {
return (reader) => String(reader.uint64());
}
return (reader) => reader.uint64();
case descriptors_js_1.ScalarType.FIXED64:
return reader.fixed64();
if (longAsString) {
return (reader) => String(reader.fixed64());
}
return (reader) => reader.fixed64();
case descriptors_js_1.ScalarType.BYTES:
return reader.bytes();
return (reader) => reader.bytes();
case descriptors_js_1.ScalarType.FIXED32:
return reader.fixed32();
return (reader) => reader.fixed32();
case descriptors_js_1.ScalarType.SFIXED32:
return reader.sfixed32();
return (reader) => reader.sfixed32();
case descriptors_js_1.ScalarType.SFIXED64:
return reader.sfixed64();
if (longAsString) {
return (reader) => String(reader.sfixed64());
}
return (reader) => reader.sfixed64();
case descriptors_js_1.ScalarType.SINT64:
return reader.sint64();
if (longAsString) {
return (reader) => String(reader.sint64());
}
return (reader) => reader.sint64();
case descriptors_js_1.ScalarType.UINT32:
return reader.uint32();
return (reader) => reader.uint32();
case descriptors_js_1.ScalarType.SINT32:
return reader.sint32();
return (reader) => reader.sint32();
}
}

@@ -25,9 +25,14 @@ "use strict";

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 names_js_1 = require("./reflect/names.js");
const scalar_js_1 = require("./reflect/scalar.js");
const unsafe_js_1 = require("./reflect/unsafe.js");
const message_js_1 = require("./reflect/message.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");
const json_js_1 = require("./wkt/json.js");
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.IMPLICIT: const $name = $number;
const IMPLICIT = 2;
function makeReadContext(options) {

@@ -66,16 +71,5 @@ return Object.assign(Object.assign({ ignoreUnknownFields: false, recursionLimit: 100 }, options), { depth: 0 });

function fromJson(schema, json, options) {
const msg = (0, reflect_js_1.reflect)(schema);
try {
readMessage(msg, json, makeReadContext(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;
const message = (0, create_js_1.create)(schema);
readMessage(schema, message, json, options);
return message;
}

@@ -95,4 +89,16 @@ /**

function mergeFromJson(schema, target, json, options) {
if (target.$typeName !== schema.typeName &&
schema.fields.length > 0) {
throw new error_js_1.FieldError(schema.fields[0], `cannot use ${schema.fields[0]} with message ${target.$typeName}`, "ForeignFieldError");
}
readMessage(schema, target, json, options);
return target;
}
/**
* Run the compiled decoder for the message, wrapping FieldErrors with the
* standard error message.
*/
function readMessage(schema, message, json, options) {
try {
readMessage((0, reflect_js_1.reflect)(schema, target), json, makeReadContext(options));
compiledReader(schema)(message, json, makeReadContext(options));
}

@@ -108,3 +114,2 @@ catch (e) {

}
return target;
}

@@ -115,3 +120,5 @@ /**

function enumFromJson(descEnum, json) {
return readEnum(descEnum, json, false);
// With ignoreUnknownFields false, the converter never returns the token
// for ignored unknown enum values.
return compileEnumConverter(descEnum)(json, false);
}

@@ -124,214 +131,506 @@ /**

}
const messageJsonFields = new WeakMap();
function getJsonField(desc, jsonKey) {
var _a;
if (!messageJsonFields.has(desc)) {
const jsonNames = new Map();
for (const field of desc.fields) {
jsonNames.set(field.name, field).set(field.jsonName, field);
}
messageJsonFields.set(desc, jsonNames);
const compiledReaders = new WeakMap();
/**
* Return the compiled decoder for a message, compiling it on first use.
*/
function compiledReader(desc) {
let compiled = compiledReaders.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
return (_a = messageJsonFields.get(desc)) === null || _a === void 0 ? void 0 : _a.get(jsonKey);
return compiled;
}
function readMessage(msg, json, ctx) {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${msg.desc} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
function compileMessage(desc) {
const descString = String(desc);
const readWkt = compileWkt(desc);
if (readWkt !== undefined) {
// All message decoders count against the recursion limit, including
// well-known types with a custom JSON representation.
const compiled = (message, json, ctx) => {
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
readWkt(message, json, ctx);
ctx.depth--;
};
compiledReaders.set(desc, compiled);
return compiled;
}
if (tryWktFromJson(msg, json, ctx)) {
ctx.depth--;
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 fieldSeen = new Set();
for (const [jsonKey, jsonValue] of Object.entries(json)) {
const field = getJsonField(msg.desc, jsonKey);
if (field) {
if (fieldSeen.has(field)) {
// The same field may be set by its proto name and its JSON name, or by
// a duplicate or unicode-escaped key that JSON.parse already collapsed.
// Checked before the null-skip below so that a null entry still counts.
throw new error_js_1.FieldError(field, "set multiple times");
const typeName = desc.typeName;
// Fields are looked up by their proto name and their JSON name.
const fieldsByJsonKey = new Map();
const compiled = (message, json, ctx) => {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
if (json == null || Array.isArray(json) || typeof json != "object") {
throw new Error(`cannot decode ${descString} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);
}
const oneofSeen = new Map();
const fieldSeen = new Set();
const jsonKeys = Object.keys(json);
for (let i = 0; i < jsonKeys.length; i++) {
const jsonKey = jsonKeys[i];
const jsonValue = json[jsonKey];
const entry = fieldsByJsonKey.get(jsonKey);
if (entry !== undefined) {
const field = entry.field;
if (fieldSeen.has(field)) {
// The same field may be set by its proto name and its JSON name, or by
// a duplicate or unicode-escaped key that JSON.parse already collapsed.
// Checked before the null-skip below so that a null entry still counts.
throw new error_js_1.FieldError(field, "set multiple times");
}
fieldSeen.add(field);
if (entry.oneofScalarNullSkip && jsonValue === null) {
continue;
}
if (entry.oneof) {
const seen = oneofSeen.get(entry.oneof);
if (seen !== undefined) {
throw new error_js_1.FieldError(entry.oneof, `oneof set multiple times by ${seen.name} and ${field.name}`);
}
oneofSeen.set(entry.oneof, field);
}
entry.read(message, jsonValue, ctx);
}
fieldSeen.add(field);
if (field.oneof && jsonValue === null && field.fieldKind == "scalar") {
// see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
continue;
}
if (field.oneof) {
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}`);
else {
const extension = jsonKey.startsWith("[") && jsonKey.endsWith("]")
? (_a = ctx.registry) === null || _a === void 0 ? void 0 : _a.getExtension(jsonKey.substring(1, jsonKey.length - 1))
: undefined;
if ((extension === null || extension === void 0 ? void 0 : extension.extendee.typeName) == typeName) {
const [container, field, get] = (0, extensions_js_1.createExtensionContainer)(extension);
compileFieldReader(field)(container[unsafe_js_1.unsafeLocal], jsonValue, ctx);
(0, extensions_js_1.setExtension)(message, extension, get());
}
oneofSeen.set(field.oneof, field);
if (extension === undefined && !ctx.ignoreUnknownFields) {
throw new Error(`cannot decode ${descString} from JSON: key "${jsonKey}" is unknown`);
}
}
readField(msg, field, jsonValue, ctx);
}
else {
let extension = undefined;
if (jsonKey.startsWith("[") &&
jsonKey.endsWith("]") &&
// biome-ignore lint/suspicious/noAssignInExpressions: no
(extension = (_a = ctx.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, ctx);
(0, extensions_js_1.setExtension)(msg.message, extension, get());
ctx.depth--;
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledReaders.set(desc, compiled);
for (const field of desc.fields) {
const entry = {
read: compileFieldReader(field),
field,
oneof: field.oneof,
oneofScalarNullSkip: field.oneof !== undefined && field.fieldKind == "scalar",
};
fieldsByJsonKey.set(field.name, entry).set(field.jsonName, entry);
}
return compiled;
}
/**
* Compile a decoder for a well-known type with a custom JSON representation,
* or return undefined for other messages. The recursion limit is enforced by
* the caller.
*/
function compileWkt(desc) {
if (!desc.typeName.startsWith("google.protobuf.")) {
return undefined;
}
switch (desc.typeName) {
case "google.protobuf.Any":
return (message, json, ctx) => anyFromJson(message, json, ctx);
case "google.protobuf.Timestamp":
return (message, json) => timestampFromJson(message, json);
case "google.protobuf.Duration":
return (message, json) => durationFromJson(message, json);
case "google.protobuf.FieldMask":
return (message, json) => fieldMaskFromJson(message, json);
case "google.protobuf.Struct":
return (message, json, ctx) => structFromJson(message, json, ctx);
case "google.protobuf.Value":
return (message, json, ctx) => valueFromJson(message, json, ctx);
case "google.protobuf.ListValue":
return (message, json, ctx) => listValueFromJson(message, json, ctx);
default:
if ((0, index_js_1.isWrapperDesc)(desc)) {
const valueField = desc.fields[0];
const localName = valueField.localName;
const scalar = valueField.scalar;
const longAsString = valueField.longAsString;
const readScalar = compileScalarConverter(valueField);
return (message, json) => {
if (json === null) {
message[localName] = (0, scalar_js_1.scalarZeroValue)(scalar, longAsString);
}
else {
message[localName] = readScalar(json);
}
};
}
if (!extension && !ctx.ignoreUnknownFields) {
throw new Error(`cannot decode ${msg.desc} from JSON: key "${jsonKey}" is unknown`);
}
}
return undefined;
}
ctx.depth--;
}
function readField(msg, field, json, ctx) {
function compileFieldReader(field) {
switch (field.fieldKind) {
case "scalar":
readScalarField(msg, field, json);
break;
return compileScalarFieldReader(field);
case "enum":
readEnumField(msg, field, json, ctx);
break;
return compileEnumFieldReader(field);
case "message":
readMessageField(msg, field, json, ctx);
break;
return compileMessageFieldReader(field);
case "list":
readListField(msg.get(field), json, ctx);
break;
return compileListFieldReader(field);
case "map":
readMapField(msg.get(field), json, ctx);
break;
return compileMapFieldReader(field);
}
}
function readListOrMapItem(field, json, ctx) {
if (field.scalar && json !== null) {
return scalarFromJson(field, json);
function compileScalarFieldReader(field) {
const readScalar = compileScalarConverter(field);
const localName = field.localName;
if (field.oneof) {
// JSON null for a oneof scalar member is skipped by the message decoder.
const oneofLocalName = field.oneof.localName;
return (message, json) => {
message[oneofLocalName] = {
case: localName,
value: readScalar(json),
};
};
}
if (field.message && !isResetSentinelNullValue(field, json)) {
const msgValue = (0, reflect_js_1.reflect)(field.message);
readMessage(msgValue, json, ctx);
return msgValue;
const clear = compileClear(field);
return (message, json) => {
if (json === null) {
clear(message);
}
else {
message[localName] = readScalar(json);
}
};
}
/**
* Compile a function that resets the field to unset, mirroring the clear
* operation of the reflect API for fields that are not part of a oneof.
*/
function compileClear(field) {
const localName = field.localName;
if (field.presence != IMPLICIT) {
// Fields with explicit presence have properties on the prototype chain
// for default / zero values (except for proto3). By deleting their own
// property, the field is reset.
return (message) => {
delete message[localName];
};
}
if (field.enum && !isResetSentinelNullValue(field, json)) {
return readEnum(field.enum, json, ctx.ignoreUnknownFields);
if (field.fieldKind == "enum") {
const zero = field.enum.values[0].number;
return (message) => {
message[localName] = zero;
};
}
throw new error_js_1.FieldError(field, `${field.fieldKind === "list" ? "list item" : "map value"} must not be null`);
const scalar = field.scalar;
const longAsString = field.longAsString;
return (message) => {
message[localName] = (0, scalar_js_1.scalarZeroValue)(scalar, longAsString);
};
}
function readMapField(map, json, ctx) {
if (json === null) {
return;
function compileEnumFieldReader(field) {
const readEnumValue = compileEnumConverter(field.enum);
const checkEnum = compileEnumCheck(field.enum);
const localName = field.localName;
// Fields with enum google.protobuf.NullValue permit a Protobuf-serializable
// null; for all other enums, JSON null resets the field.
const nullResets = field.enum.typeName != "google.protobuf.NullValue";
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, json, ctx) => {
if (json === null && nullResets) {
const oneof = message[oneofLocalName];
if (oneof.case === localName) {
message[oneofLocalName] = { case: undefined };
}
return;
}
const value = readEnumValue(json, ctx.ignoreUnknownFields);
if (value === tokenIgnoredUnknownEnum) {
return;
}
const check = checkEnum(value);
if (check !== true) {
throw new error_js_1.FieldError(field, (0, reflect_check_js_1.reasonSingular)(field, value, check));
}
message[oneofLocalName] = { case: localName, value };
};
}
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));
}
const seen = new Set();
for (const [jsonMapKey, jsonMapValue] of Object.entries(json)) {
const key = mapKeyFromJson(field.mapKey, jsonMapKey);
if (seen.has(key)) {
throw new error_js_1.FieldError(field, `duplicate map key "${jsonMapKey}"`);
const clear = compileClear(field);
return (message, json, ctx) => {
if (json === null && nullResets) {
clear(message);
return;
}
seen.add(key);
const value = readListOrMapItem(field, jsonMapValue, ctx);
if (value !== tokenIgnoredUnknownEnum) {
map.set(key, value);
const value = readEnumValue(json, ctx.ignoreUnknownFields);
if (value === tokenIgnoredUnknownEnum) {
return;
}
}
const check = checkEnum(value);
if (check !== true) {
throw new error_js_1.FieldError(field, (0, reflect_check_js_1.reasonSingular)(field, value, check));
}
message[localName] = value;
};
}
function readListField(list, json, ctx) {
if (json === null) {
return;
function compileMessageFieldReader(field) {
const localName = field.localName;
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
const readChild = compiledReader(field.message);
// Fields with message google.protobuf.Value permit a Protobuf-serializable
// null; for all other messages, JSON null resets the field.
const nullResets = field.message.typeName != "google.protobuf.Value";
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, json, ctx) => {
const oneof = message[oneofLocalName];
if (json === null && nullResets) {
if (oneof.case === localName) {
message[oneofLocalName] = { case: undefined };
}
return;
}
const child = toMessage(oneof.case === localName ? oneof.value : undefined);
readChild(child, json, ctx);
message[oneofLocalName] = { case: localName, value: toLocal(child) };
};
}
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) {
const value = readListOrMapItem(field, jsonItem, ctx);
if (value !== tokenIgnoredUnknownEnum) {
list.add(value);
return (message, json, ctx) => {
if (json === null && nullResets) {
delete message[localName];
return;
}
}
const child = toMessage(message[localName]);
readChild(child, json, ctx);
message[localName] = toLocal(child);
};
}
function readMessageField(msg, field, json, ctx) {
if (isResetSentinelNullValue(field, json)) {
msg.clear(field);
return;
}
const msgValue = msg.isSet(field) ? msg.get(field) : (0, reflect_js_1.reflect)(field.message);
readMessage(msgValue, json, ctx);
msg.set(field, msgValue);
function compileListFieldReader(field) {
const localName = field.localName;
const readItem = compileListItemReader(field);
return (message, json, ctx) => {
if (json === null) {
return;
}
if (!Array.isArray(json)) {
throw new error_js_1.FieldError(field, "expected Array, got " + (0, reflect_check_js_1.formatVal)(json));
}
const items = message[localName];
for (let i = 0; i < json.length; i++) {
const value = readItem(json[i], ctx, items.length);
if (value !== tokenIgnoredUnknownEnum) {
items.push(value);
}
}
};
}
function readEnumField(msg, field, json, ctx) {
if (isResetSentinelNullValue(field, json)) {
msg.clear(field);
return;
/**
* Compile a decoder for a list item. The index is only used in errors, and
* accounts for previously merged items.
*/
function compileListItemReader(field) {
switch (field.listKind) {
case "scalar": {
const parseScalar = compileScalarParse(field);
const checkValue = (0, reflect_check_js_1.checkScalarValue)(field.scalar);
const toLocal = compileScalarToLocal(field);
return (json, ctx, index) => {
if (json === null) {
throw new error_js_1.FieldError(field, "list item must not be null");
}
const value = parseScalar(json);
const check = checkValue(value);
if (check !== true) {
throw new error_js_1.FieldError(field, `list item #${index + 1}: ${(0, reflect_check_js_1.reasonSingular)(field, value, check)}`);
}
return toLocal(value);
};
}
case "enum": {
const readEnumValue = compileEnumConverter(field.enum);
const checkEnum = compileEnumCheck(field.enum);
const nullResets = field.enum.typeName != "google.protobuf.NullValue";
return (json, ctx, index) => {
if (json === null && nullResets) {
throw new error_js_1.FieldError(field, "list item must not be null");
}
const value = readEnumValue(json, ctx.ignoreUnknownFields);
if (value === tokenIgnoredUnknownEnum) {
return value;
}
const check = checkEnum(value);
if (check !== true) {
throw new error_js_1.FieldError(field, `list item #${index + 1}: ${(0, reflect_check_js_1.reasonSingular)(field, value, check)}`);
}
return value;
};
}
case "message": {
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
const readChild = compiledReader(field.message);
const nullResets = field.message.typeName != "google.protobuf.Value";
return (json, ctx) => {
if (json === null && nullResets) {
throw new error_js_1.FieldError(field, "list item must not be null");
}
const child = toMessage(undefined);
readChild(child, json, ctx);
return toLocal(child);
};
}
}
const enumValue = readEnum(field.enum, json, ctx.ignoreUnknownFields);
if (enumValue !== tokenIgnoredUnknownEnum) {
msg.set(field, enumValue);
}
}
function readScalarField(msg, field, json) {
if (json === null) {
msg.clear(field);
function compileMapFieldReader(field) {
const localName = field.localName;
const mapKey = field.mapKey;
const parseMapKey = compileMapKeyParse(mapKey);
const checkMapKey = (0, reflect_check_js_1.checkScalarValue)(mapKey);
let parseValue;
// Additional validation for scalar and enum values, matching the checks
// of the reflect API. Message values need no validation.
let checkValue;
let toLocalValue = (value) => value;
// Fields with google.protobuf.Value or google.protobuf.NullValue values
// permit a Protobuf-serializable null.
let nullResets = true;
switch (field.mapKind) {
case "scalar": {
parseValue = compileScalarParse(field);
checkValue = (0, reflect_check_js_1.checkScalarValue)(field.scalar);
toLocalValue = compileScalarToLocal(field);
break;
}
case "enum": {
const readEnumValue = compileEnumConverter(field.enum);
parseValue = (json, ctx) => readEnumValue(json, ctx.ignoreUnknownFields);
checkValue = compileEnumCheck(field.enum);
nullResets = field.enum.typeName != "google.protobuf.NullValue";
break;
}
case "message": {
const { toMessage, toLocal } = (0, message_js_1.localMessageMapper)(field);
const readChild = compiledReader(field.message);
nullResets = field.message.typeName != "google.protobuf.Value";
parseValue = (json, ctx) => {
const child = toMessage(undefined);
readChild(child, json, ctx);
return toLocal(child);
};
break;
}
}
else {
msg.set(field, scalarFromJson(field, json));
}
return (message, json, ctx) => {
if (json === null) {
return;
}
if (typeof json != "object" || Array.isArray(json)) {
throw new error_js_1.FieldError(field, "expected object, got " + (0, reflect_check_js_1.formatVal)(json));
}
const record = message[localName];
const seen = new Set();
const jsonMapKeys = Object.keys(json);
for (let i = 0; i < jsonMapKeys.length; i++) {
const jsonMapKey = jsonMapKeys[i];
const jsonMapValue = json[jsonMapKey];
const key = parseMapKey(jsonMapKey);
if (seen.has(key)) {
throw new error_js_1.FieldError(field, `duplicate map key "${jsonMapKey}"`);
}
seen.add(key);
if (jsonMapValue === null && nullResets) {
throw new error_js_1.FieldError(field, "map value must not be null");
}
const value = parseValue(jsonMapValue, ctx);
if (value === tokenIgnoredUnknownEnum) {
continue;
}
const checkKey = checkMapKey(key);
if (checkKey !== true) {
throw new error_js_1.FieldError(field, `invalid map key: ${(0, reflect_check_js_1.reasonSingular)({ scalar: mapKey }, key, checkKey)}`);
}
if (checkValue !== undefined) {
const check = checkValue(value);
if (check !== true) {
throw new error_js_1.FieldError(field, `map entry ${(0, reflect_check_js_1.formatVal)(key)}: ${(0, reflect_check_js_1.reasonSingular)(field, value, check)}`);
}
}
// Object property keys are always strings or symbols. Assigning with a
// boolean, number, or bigint key implicitly converts it to a string.
record[key] = toLocalValue(value);
}
};
}
const tokenIgnoredUnknownEnum = Symbol();
/**
* Indicates whether a value is a sentinel for reseting a field.
*
* For this to be true, the value must be a JSON null and the field must not
* permit a present, Protobuf-serializable null.
*
* Only message google.protobuf.Value and enum google.protobuf.NullValue fields
* permit Protobuf-serializable nulls.
*
* Note that field-resetting sentinel nulls are not permitted in lists and maps.
* Compile a converter from a JSON value to an enum value. JSON null returns
* the enum's first value. With ignoreUnknownFields false, unknown string
* values raise an error; with true, they return tokenIgnoredUnknownEnum.
* The value is not checked against the enum's values, see compileEnumCheck.
*/
function isResetSentinelNullValue(field, json) {
var _a, _b;
return (json === null &&
((_a = field.message) === null || _a === void 0 ? void 0 : _a.typeName) != "google.protobuf.Value" &&
((_b = field.enum) === null || _b === void 0 ? void 0 : _b.typeName) != "google.protobuf.NullValue");
function compileEnumConverter(desc) {
const zero = desc.values[0].number;
const values = desc.values;
return (json, ignoreUnknownFields) => {
if (json === null) {
return zero;
}
switch (typeof json) {
case "number":
if (Number.isInteger(json)) {
return json;
}
break;
case "string": {
const value = 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 tokenIgnoredUnknownEnum = Symbol();
function readEnum(desc, json, ignoreUnknownFields) {
if (json === null) {
return desc.values[0].number;
/**
* Compile the check that the reflect API performs for enum values: open
* enums accept any int32 value, closed enums accept only declared values.
*/
function compileEnumCheck(desc) {
if (desc.open) {
return (0, reflect_check_js_1.checkScalarValue)(descriptors_js_1.ScalarType.INT32);
}
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 values = desc.values;
return (value) => values.some((v) => v.number === value);
}
/**
* Try to parse a JSON value to a scalar value for the reflect API.
*
* Returns the input if the JSON value cannot be converted. Raises a FieldError
* if conversion would be ambiguous.
* Compile a converter from a JSON value to the local representation of a
* scalar, fusing JSON parsing, the validation of the reflect API, and the
* conversion to the local 64-bit integer representation.
*/
function scalarFromJson(field, json) {
// int64, sfixed64, sint64, fixed64, uint64: Reflect supports string and number.
// string, bool: Supported by reflect.
function compileScalarConverter(field) {
const parseScalar = compileScalarParse(field);
const checkValue = (0, reflect_check_js_1.checkScalarValue)(field.scalar);
const toLocal = compileScalarToLocal(field);
return (json) => {
const value = parseScalar(json);
const check = checkValue(value);
if (check !== true) {
throw new error_js_1.FieldError(field, (0, reflect_check_js_1.reasonSingular)(field, value, check));
}
return toLocal(value);
};
}
/**
* Compile the JSON-specific parsing step for a scalar value: the special
* string values of float and double, string-encoded numbers, and base64
* bytes. Returns the input unchanged if the JSON value cannot be converted;
* the validation step raises an error for it.
*/
function compileScalarParse(field) {
switch (field.scalar) {

@@ -342,36 +641,38 @@ // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity".

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 (Number.isNaN(json)) {
// NaN must be encoded with string constants
throw new error_js_1.FieldError(field, "unexpected NaN number");
return (json) => {
if (json === "NaN")
return NaN;
if (json === "Infinity")
return Number.POSITIVE_INFINITY;
if (json === "-Infinity")
return Number.NEGATIVE_INFINITY;
if (typeof json == "number") {
if (Number.isNaN(json)) {
// NaN must be encoded with string constants
throw new error_js_1.FieldError(field, "unexpected NaN number");
}
if (!Number.isFinite(json)) {
// Infinity must be encoded with string constants
throw new error_js_1.FieldError(field, "unexpected infinite number");
}
return json;
}
if (!Number.isFinite(json)) {
// Infinity must be encoded with string constants
throw new error_js_1.FieldError(field, "unexpected infinite number");
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 float = Number(json);
if (!Number.isFinite(float)) {
// Infinity and NaN must be encoded with string constants
return json;
}
return float;
}
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 (!Number.isFinite(float)) {
// Infinity and NaN must be encoded with string constants
break;
}
return float;
}
break;
return json;
};
// int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted.

@@ -383,38 +684,74 @@ case descriptors_js_1.ScalarType.INT32:

case descriptors_js_1.ScalarType.UINT32:
return int32FromJson(json);
return int32FromJson;
// 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);
return (json) => {
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);
}
}
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);
}
return json;
};
// int64, sfixed64, sint64, fixed64, uint64: The validation step accepts
// string and number. string, bool: no conversion.
default:
return (json) => json;
}
}
/**
* Compile the conversion of a validated scalar value to its local
* representation: 64-bit integers become bigint, or string with the
* longAsString option.
*/
function compileScalarToLocal(field) {
const longAsString = field.fieldKind !== "map" && field.longAsString;
switch (field.scalar) {
case descriptors_js_1.ScalarType.INT64:
case descriptors_js_1.ScalarType.SFIXED64:
case descriptors_js_1.ScalarType.SINT64:
if (longAsString) {
return (value) => String(value);
}
break;
return (value) => typeof value == "string" || typeof value == "number"
? proto_int64_js_1.protoInt64.parse(value)
: value;
case descriptors_js_1.ScalarType.FIXED64:
case descriptors_js_1.ScalarType.UINT64:
if (longAsString) {
return (value) => String(value);
}
return (value) => typeof value == "string" || typeof value == "number"
? proto_int64_js_1.protoInt64.uParse(value)
: value;
default:
return (value) => value;
}
return json;
}
/**
* Try to parse a JSON value to a map key for the reflect API.
* Canonicalizes 64-bit integers given as string, so that "01 and "1" are one
* key, and duplicates can raise an error.
* Returns the input if the JSON value cannot be converted.
* Return a parser from a JSON value to a map key for the given key type.
* Canonicalizes 64-bit integers given as string, so that "01" and "1" are
* one key, and duplicates can raise an error.
* The parser returns the input if the JSON value cannot be converted.
*/
function mapKeyFromJson(type, jsonString) {
function compileMapKeyParse(type) {
switch (type) {
case descriptors_js_1.ScalarType.BOOL:
switch (jsonString) {
case "true":
return true;
case "false":
return false;
}
return jsonString;
return (jsonString) => {
switch (jsonString) {
case "true":
return true;
case "false":
return false;
}
return jsonString;
};
case descriptors_js_1.ScalarType.INT32:

@@ -425,3 +762,3 @@ case descriptors_js_1.ScalarType.FIXED32:

case descriptors_js_1.ScalarType.SINT32:
return int32FromJson(jsonString);
return int32FromJson;
case descriptors_js_1.ScalarType.INT64:

@@ -432,7 +769,8 @@ case descriptors_js_1.ScalarType.SINT64:

case descriptors_js_1.ScalarType.FIXED64:
return /^-?0+$/.test(jsonString)
return (jsonString) => /^-?0+$/.test(jsonString)
? "0"
: jsonString.replace(/^(-?)0+(?=\d)/, "$1");
default:
return jsonString;
// ScalarType.STRING
return (jsonString) => jsonString;
}

@@ -557,42 +895,2 @@ }

}
function tryWktFromJson(msg, jsonValue, ctx) {
if (!msg.desc.typeName.startsWith("google.protobuf.")) {
return false;
}
switch (msg.desc.typeName) {
case "google.protobuf.Any":
anyFromJson(msg.message, jsonValue, ctx);
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, ctx);
return true;
case "google.protobuf.Value":
valueFromJson(msg.message, jsonValue, ctx);
return true;
case "google.protobuf.ListValue":
listValueFromJson(msg.message, jsonValue, ctx);
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));
}
return true;
}
return false;
}
}
function anyFromJson(any, json, ctx) {

@@ -620,7 +918,6 @@ var _a;

}
const msg = (0, reflect_js_1.reflect)(desc);
const message = (0, create_js_1.create)(desc);
if ((0, index_js_1.hasCustomJsonRepresentation)(desc) &&
Object.prototype.hasOwnProperty.call(json, "value")) {
const value = json.value;
readMessage(msg, value, ctx);
compiledReader(desc)(message, json.value, ctx);
}

@@ -631,5 +928,5 @@ else {

delete copy["@type"];
readMessage(msg, copy, ctx);
compiledReader(desc)(message, copy, ctx);
}
(0, index_js_1.anyPack)(msg.desc, msg.message, any);
(0, index_js_1.anyPack)(desc, message, any);
}

@@ -650,4 +947,3 @@ function timestampFromJson(timestamp, json) {

}
if (ms < Date.parse("0001-01-01T00:00:00Z") ||
ms > Date.parse("9999-12-31T23:59:59Z")) {
if (ms < json_js_1.timestampMsMin || ms > json_js_1.timestampMsMax) {
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`);

@@ -672,3 +968,3 @@ }

const longSeconds = Number(match[1]);
if (longSeconds > 315576000000 || longSeconds < -315576000000) {
if (longSeconds > json_js_1.durationSecondsMax || longSeconds < json_js_1.durationSecondsMin) {
throw new Error(`cannot decode message ${duration.$typeName} from JSON: ${(0, reflect_check_js_1.formatVal)(json)}`);

@@ -704,6 +1000,8 @@ }

}
for (const [k, v] of Object.entries(json)) {
const parsedV = (0, create_js_1.create)(index_js_1.ValueSchema);
valueFromJson(parsedV, v, ctx);
struct.fields[k] = parsedV;
const keys = Object.keys(json);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const parsedValue = (0, create_js_1.create)(index_js_1.ValueSchema);
valueFromJson(parsedValue, json[key], ctx);
struct.fields[key] = parsedValue;
}

@@ -750,7 +1048,7 @@ }

}
for (const e of json) {
for (let i = 0; i < json.length; i++) {
const value = (0, create_js_1.create)(index_js_1.ValueSchema);
valueFromJson(value, e, ctx);
valueFromJson(value, json[i], ctx);
listValue.values.push(value);
}
}

@@ -1,2 +0,2 @@

import { type DescField } from "../descriptors.js";
import { type DescEnum, type DescField, type DescMessage, ScalarType } from "../descriptors.js";
import { FieldError } from "./error.js";

@@ -19,2 +19,28 @@ /**

}, key: unknown, value: unknown): FieldError | undefined;
type InvalidScalarValueErr = false | "invalid UTF8" | `${string} out of range`;
/**
* Return the check for values of the given scalar type.
*
* @private
*/
export declare function checkScalarValue(scalar: ScalarType): (value: unknown) => true | InvalidScalarValueErr;
/**
* Format the reason why a value is invalid for a singular field.
*
* @private
*/
export declare function reasonSingular(field: {
scalar: ScalarType;
message?: undefined;
enum?: undefined;
} | {
scalar?: undefined;
message: DescMessage;
enum?: undefined;
} | {
scalar?: undefined;
message?: undefined;
enum: DescEnum;
}, val: unknown, details?: string | false): string;
export declare function formatVal(val: unknown): string;
export {};

@@ -19,2 +19,4 @@ "use strict";

exports.checkMapEntry = checkMapEntry;
exports.checkScalarValue = checkScalarValue;
exports.reasonSingular = reasonSingular;
exports.formatVal = formatVal;

@@ -68,3 +70,3 @@ const descriptors_js_1 = require("../descriptors.js");

function checkMapEntry(field, key, value) {
const checkKey = checkScalarValue(key, field.mapKey);
const checkKey = checkScalarValue(field.mapKey)(key);
if (checkKey !== true) {

@@ -81,3 +83,3 @@ return new error_js_1.FieldError(field, `invalid map key: ${reasonSingular({ scalar: field.mapKey }, key, checkKey)}`);

if (field.scalar !== undefined) {
return checkScalarValue(value, field.scalar);
return checkScalarValue(field.scalar)(value);
}

@@ -88,3 +90,3 @@ if (field.enum !== undefined) {

// int32 (see https://protobuf.dev/programming-guides/proto3/#enum).
return checkScalarValue(value, descriptors_js_1.ScalarType.INT32);
return checkScalarValue(descriptors_js_1.ScalarType.INT32)(value);
}

@@ -95,17 +97,24 @@ return field.enum.values.some((v) => v.number === value);

}
function checkScalarValue(value, scalar) {
/**
* Return the check for values of the given scalar type.
*
* @private
*/
function checkScalarValue(scalar) {
switch (scalar) {
case descriptors_js_1.ScalarType.DOUBLE:
return typeof value == "number";
return (value) => typeof value == "number";
case descriptors_js_1.ScalarType.FLOAT:
if (typeof value != "number") {
return false;
}
if (Number.isNaN(value) || !Number.isFinite(value)) {
return (value) => {
if (typeof value != "number") {
return false;
}
if (Number.isNaN(value) || !Number.isFinite(value)) {
return true;
}
if (value > binary_encoding_js_1.FLOAT32_MAX || value < binary_encoding_js_1.FLOAT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
}
if (value > binary_encoding_js_1.FLOAT32_MAX || value < binary_encoding_js_1.FLOAT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
};
case descriptors_js_1.ScalarType.INT32:

@@ -115,28 +124,34 @@ case descriptors_js_1.ScalarType.SFIXED32:

// signed
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > binary_encoding_js_1.INT32_MAX || value < binary_encoding_js_1.INT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
return (value) => {
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > binary_encoding_js_1.INT32_MAX || value < binary_encoding_js_1.INT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
};
case descriptors_js_1.ScalarType.FIXED32:
case descriptors_js_1.ScalarType.UINT32:
// unsigned
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > binary_encoding_js_1.UINT32_MAX || value < 0) {
return `${value.toFixed()} out of range`;
}
return true;
return (value) => {
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > binary_encoding_js_1.UINT32_MAX || value < 0) {
return `${value.toFixed()} out of range`;
}
return true;
};
case descriptors_js_1.ScalarType.BOOL:
return typeof value == "boolean";
return (value) => typeof value == "boolean";
case descriptors_js_1.ScalarType.STRING:
if (typeof value != "string") {
return false;
}
return (0, text_encoding_js_1.getTextEncoding)().checkUtf8(value) || "invalid UTF8";
return (value) => {
if (typeof value != "string") {
return false;
}
return (0, text_encoding_js_1.getTextEncoding)().checkUtf8(value) || "invalid UTF8";
};
case descriptors_js_1.ScalarType.BYTES:
return value instanceof Uint8Array;
return (value) => value instanceof Uint8Array;
case descriptors_js_1.ScalarType.INT64:

@@ -146,31 +161,40 @@ case descriptors_js_1.ScalarType.SFIXED64:

// signed
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
proto_int64_js_1.protoInt64.parse(value);
return true;
return (value) => {
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
proto_int64_js_1.protoInt64.parse(value);
return true;
}
catch (_) {
return `${value} out of range`;
}
}
catch (_) {
return `${value} out of range`;
}
}
return false;
return false;
};
case descriptors_js_1.ScalarType.FIXED64:
case descriptors_js_1.ScalarType.UINT64:
// unsigned
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
proto_int64_js_1.protoInt64.uParse(value);
return true;
return (value) => {
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
proto_int64_js_1.protoInt64.uParse(value);
return true;
}
catch (_) {
return `${value} out of range`;
}
}
catch (_) {
return `${value} out of range`;
}
}
return false;
return false;
};
}
}
/**
* Format the reason why a value is invalid for a singular field.
*
* @private
*/
function reasonSingular(field, val, details) {

@@ -177,0 +201,0 @@ details =

@@ -28,4 +28,3 @@ "use strict";

const guard_js_1 = require("./guard.js");
// google.protobuf.NullValue.NULL_VALUE;
const NULL_VALUE = 0;
const message_js_1 = require("./message.js");
/**

@@ -324,3 +323,3 @@ * Create a ReflectMessage.

// field, except when used in google.protobuf.Value.
return wktStructToLocal(value.message);
return (0, message_js_1.wktStructToLocal)(value.message);
}

@@ -346,3 +345,3 @@ return value.message;

// field, except when used in google.protobuf.Value.
value = wktStructToReflect(value);
value = (0, message_js_1.wktStructToReflect)(value);
}

@@ -474,77 +473,1 @@ }

}
function wktStructToReflect(json) {
const struct = {
$typeName: "google.protobuf.Struct",
fields: {},
};
if ((0, guard_js_1.isObject)(json)) {
for (const [k, v] of Object.entries(json)) {
struct.fields[k] = wktValueToReflect(v);
}
}
return struct;
}
function wktStructToLocal(val) {
const json = {};
for (const [k, v] of Object.entries(val.fields)) {
json[k] = wktValueToLocal(v);
}
return json;
}
function wktValueToLocal(val) {
switch (val.kind.case) {
case "structValue":
return wktStructToLocal(val.kind.value);
case "listValue":
return val.kind.value.values.map(wktValueToLocal);
case "nullValue":
case undefined:
return null;
default:
return val.kind.value;
}
}
function wktValueToReflect(json) {
const value = {
$typeName: "google.protobuf.Value",
kind: { case: undefined },
};
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: NULL_VALUE };
}
else if (Array.isArray(json)) {
const listValue = {
$typeName: "google.protobuf.ListValue",
values: [],
};
if (Array.isArray(json)) {
for (const e of json) {
listValue.values.push(wktValueToReflect(e));
}
}
value.kind = {
case: "listValue",
value: listValue,
};
}
else {
value.kind = {
case: "structValue",
value: wktStructToReflect(json),
};
}
break;
}
return value;
}

@@ -584,2 +584,3 @@ "use strict";

};
let toStr;
if (isExtension) {

@@ -596,3 +597,3 @@ // extension field

field.jsonName = `[${typeName}]`; // option json_name is not allowed on extension fields
field.toString = () => `extension ${typeName}`;
toStr = () => `extension ${typeName}`;
const extendee = reg.getMessage(trimLeadingDot(proto.extendee));

@@ -612,4 +613,12 @@ assert(extendee, `invalid FieldDescriptorProto: extendee ${proto.extendee} not found`);

field.jsonName = proto.jsonName;
field.toString = () => `field ${parent.typeName}.${proto.name}`;
toStr = () => `field ${parent.typeName}.${proto.name}`;
}
// A plain assignment throws where built-in prototypes are frozen. The
// attributes match what an assignment produces.
Object.defineProperty(field, "toString", {
value: toStr,
writable: true,
enumerable: true,
configurable: true,
});
const label = proto.label;

@@ -616,0 +625,0 @@ const type = proto.type;

@@ -22,4 +22,8 @@ import type { MessageShape } from "./types.js";

/**
* Write a single field to binary format, if it is set. Used to serialize
* extensions: extensions always have explicit presence, so an extension
* value that was just set on the container is always written.
*
* @private
*/
export declare function writeField(writer: BinaryWriter, opts: BinaryWriteOptions, msg: ReflectMessage, field: DescField): void;

@@ -18,5 +18,10 @@ "use strict";

exports.writeField = writeField;
const reflect_js_1 = require("./reflect/reflect.js");
const binary_encoding_js_1 = require("./wire/binary-encoding.js");
const descriptors_js_1 = require("./descriptors.js");
const error_js_1 = require("./reflect/error.js");
const unsafe_js_1 = require("./reflect/unsafe.js");
const message_js_1 = require("./reflect/message.js");
const proto_int64_js_1 = require("./proto-int64.js");
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.IMPLICIT: const $name = $number;
const IMPLICIT = 2;
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.LEGACY_REQUIRED: const $name = $number;

@@ -32,154 +37,387 @@ const LEGACY_REQUIRED = 3;

function toBinary(schema, message, options) {
return writeFields(new binary_encoding_js_1.BinaryWriter(), makeWriteOptions(options), (0, reflect_js_1.reflect)(schema, message)).finish();
const writer = new binary_encoding_js_1.BinaryWriter();
compiledWriter(schema)(writer, makeWriteOptions(options), message);
return writer.finish();
}
function writeFields(writer, opts, msg) {
var _a;
for (const f of msg.sortedFields) {
if (!msg.isSet(f)) {
if (f.presence == LEGACY_REQUIRED) {
throw new Error(`cannot encode ${f} to binary: required field not set`);
const compiledWriters = new WeakMap();
/**
* Return the compiled encoder for a message, compiling it on first use.
*/
function compiledWriter(desc) {
let compiled = compiledWriters.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
return compiled;
}
function compileMessage(desc) {
const typeName = desc.typeName;
const sortedFields = desc.fields.concat().sort((a, b) => a.number - b.number);
// The field reported in ForeignFieldError.
const foreignField = sortedFields[0];
const fieldWriters = [];
const compiled = (writer, opts, message) => {
if (message.$typeName !== typeName && foreignField !== undefined) {
throw new error_js_1.FieldError(foreignField, `cannot use ${foreignField} with message ${message.$typeName}`, "ForeignFieldError");
}
for (let i = 0; i < fieldWriters.length; i++) {
fieldWriters[i](writer, opts, message);
}
const unknown = message.$unknown;
if (unknown !== undefined && opts.writeUnknownFields) {
for (let i = 0; i < unknown.length; i++) {
const { no, wireType, data } = unknown[i];
writer.tag(no, wireType).raw(data);
}
continue;
}
writeField(writer, opts, msg, f);
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledWriters.set(desc, compiled);
for (const field of sortedFields) {
fieldWriters.push(compileField(field));
}
if (opts.writeUnknownFields) {
for (const { no, wireType, data } of (_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : []) {
writer.tag(no, wireType).raw(data);
}
}
return writer;
return compiled;
}
/**
* @private
*/
function writeField(writer, opts, msg, field) {
var _a;
function compileField(field) {
switch (field.fieldKind) {
case "message":
case "scalar":
case "enum":
writeScalar(writer, msg.desc.typeName, field.name, (_a = field.scalar) !== null && _a !== void 0 ? _a : descriptors_js_1.ScalarType.INT32, field.number, msg.get(field));
break;
return compileSingularField(field);
case "list":
writeListField(writer, opts, field, msg.get(field));
break;
case "message":
writeMessageField(writer, opts, field, msg.get(field));
break;
return compileListField(field);
case "map":
for (const [key, val] of msg.get(field)) {
writeMapEntry(writer, opts, field, key, val);
}
break;
return compileMapField(field);
}
}
function writeScalar(writer, msgName, fieldName, scalarType, fieldNo, value) {
writeScalarValue(writer.tag(fieldNo, writeTypeOfScalar(scalarType)), msgName, fieldName, scalarType, value);
}
function writeMessageField(writer, opts, field, message) {
if (field.delimitedEncoding) {
writeFields(writer.tag(field.number, binary_encoding_js_1.WireType.StartGroup), opts, message).tag(field.number, binary_encoding_js_1.WireType.EndGroup);
/**
* Compile an encoder for a singular field: the presence check, and the
* value encoder.
*/
function compileSingularField(field) {
const writeValue = compileSingularValue(field);
const localName = field.localName;
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (writer, opts, message) => {
const oneof = message[oneofLocalName];
if (oneof.case === localName) {
writeValue(writer, opts, oneof.value);
}
};
}
else {
writeFields(writer.tag(field.number, binary_encoding_js_1.WireType.LengthDelimited).fork(), opts, message).join();
if (field.presence != IMPLICIT) {
const requiredError = field.presence == LEGACY_REQUIRED
? `cannot encode ${field} to binary: required field not set`
: undefined;
return (writer, opts, message) => {
const value = message[localName];
// Fields with explicit presence have properties on the prototype
// chain for default / zero values (except for proto3).
if (value !== undefined &&
Object.prototype.hasOwnProperty.call(message, localName)) {
writeValue(writer, opts, value);
}
else if (requiredError !== undefined) {
throw new Error(requiredError);
}
};
}
// Implicit presence: the field is set when the value is not the zero
// value. The check is inlined per type, see isScalarZeroValue.
if (field.fieldKind == "enum") {
const zero = field.enum.values[0].number;
return (writer, opts, message) => {
const value = message[localName];
if (value !== zero) {
writeValue(writer, opts, value);
}
};
}
switch (field.scalar) {
case descriptors_js_1.ScalarType.BOOL:
return (writer, opts, message) => {
const value = message[localName];
if (value !== false) {
writeValue(writer, opts, value);
}
};
case descriptors_js_1.ScalarType.STRING:
return (writer, opts, message) => {
const value = message[localName];
if (value !== "") {
writeValue(writer, opts, value);
}
};
case descriptors_js_1.ScalarType.BYTES:
return (writer, opts, message) => {
const value = message[localName];
if (!(value instanceof Uint8Array) || value.byteLength > 0) {
writeValue(writer, opts, value);
}
};
case descriptors_js_1.ScalarType.DOUBLE:
case descriptors_js_1.ScalarType.FLOAT:
return (writer, opts, message) => {
const value = message[localName];
// Object.is distinguishes -0 from 0.
if (!Object.is(value, 0)) {
writeValue(writer, opts, value);
}
};
default:
return (writer, opts, message) => {
const value = message[localName];
// Loose comparison matches 0n, 0 and "0".
if (value != 0) {
writeValue(writer, opts, value);
}
};
}
}
function writeListField(writer, opts, field, list) {
var _a;
if (field.listKind == "message") {
for (const item of list) {
writeMessageField(writer, opts, field, item);
/**
* Compile an encoder for the value of a singular field, including the tag.
*/
function compileSingularValue(field) {
switch (field.fieldKind) {
case "message": {
const { toMessage } = (0, message_js_1.localMessageMapper)(field);
const writeChild = compileChildWriter(field);
return (writer, opts, value) => {
writeChild(writer, opts, toMessage(value));
};
}
return;
case "scalar":
case "enum": {
const scalarType = field.fieldKind == "enum" ? descriptors_js_1.ScalarType.INT32 : field.scalar;
const fieldNo = field.number;
const wireType = writeTypeOfScalar(scalarType);
const writeScalar = compileScalarValue(scalarType, field.parent.typeName, field.name);
return (writer, opts, value) => {
writer.tag(fieldNo, wireType);
writeScalar(writer, value);
};
}
}
const scalarType = (_a = field.scalar) !== null && _a !== void 0 ? _a : descriptors_js_1.ScalarType.INT32;
if (field.packed) {
if (!list.size) {
return;
}
function compileListField(field) {
const localName = field.localName;
const fieldNo = field.number;
switch (field.listKind) {
case "message": {
const { toMessage } = (0, message_js_1.localMessageMapper)(field);
const writeChild = compileChildWriter(field);
return (writer, opts, message) => {
const items = message[localName];
for (let i = 0; i < items.length; i++) {
writeChild(writer, opts, toMessage(items[i]));
}
};
}
writer.tag(field.number, binary_encoding_js_1.WireType.LengthDelimited).fork();
for (const item of list) {
writeScalarValue(writer, field.parent.typeName, field.name, scalarType, item);
case "scalar":
case "enum": {
const scalarType = field.listKind == "enum" ? descriptors_js_1.ScalarType.INT32 : field.scalar;
const writeScalar = compileScalarValue(scalarType, field.parent.typeName, field.name);
if (field.packed) {
return (writer, opts, message) => {
const items = message[localName];
if (items.length == 0) {
return;
}
writer.tag(fieldNo, binary_encoding_js_1.WireType.LengthDelimited).fork();
for (let i = 0; i < items.length; i++) {
writeScalar(writer, items[i]);
}
writer.join();
};
}
const wireType = writeTypeOfScalar(scalarType);
return (writer, opts, message) => {
const items = message[localName];
for (let i = 0; i < items.length; i++) {
writer.tag(fieldNo, wireType);
writeScalar(writer, items[i]);
}
};
}
writer.join();
return;
}
for (const item of list) {
writeScalar(writer, field.parent.typeName, field.name, scalarType, field.number, item);
}
function compileMapField(field) {
const localName = field.localName;
const fieldNo = field.number;
const writeKey = compileMapKey(field);
if (field.mapKind == "message") {
const { toMessage } = (0, message_js_1.localMessageMapper)(field);
const writeMessage = compiledWriter(field.message);
return (writer, opts, message) => {
const record = message[localName];
const keys = Object.keys(record);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
writer.tag(fieldNo, binary_encoding_js_1.WireType.LengthDelimited).fork();
writeKey(writer, key);
// The value of a map entry is always field number 2.
writer.tag(2, binary_encoding_js_1.WireType.LengthDelimited).fork();
writeMessage(writer, opts, toMessage(record[key]));
writer.join();
writer.join();
}
};
}
const scalarType = field.mapKind == "enum" ? descriptors_js_1.ScalarType.INT32 : field.scalar;
const valueWireType = writeTypeOfScalar(scalarType);
const writeScalar = compileScalarValue(scalarType, field.parent.typeName, field.name);
return (writer, opts, message) => {
const record = message[localName];
const keys = Object.keys(record);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
writer.tag(fieldNo, binary_encoding_js_1.WireType.LengthDelimited).fork();
writeKey(writer, key);
// The value of a map entry is always field number 2.
writer.tag(2, valueWireType);
writeScalar(writer, record[key]);
writer.join();
}
};
}
function writeMapEntry(writer, opts, field, key, value) {
var _a;
writer.tag(field.number, binary_encoding_js_1.WireType.LengthDelimited).fork();
// write key, expecting key field number = 1
writeScalar(writer, field.parent.typeName, field.name, field.mapKey, 1, key);
// write value, expecting value field number = 2
switch (field.mapKind) {
case "scalar":
case "enum":
writeScalar(writer, field.parent.typeName, field.name, (_a = field.scalar) !== null && _a !== void 0 ? _a : descriptors_js_1.ScalarType.INT32, 2, value);
break;
case "message":
writeFields(writer.tag(2, binary_encoding_js_1.WireType.LengthDelimited).fork(), opts, value).join();
break;
/**
* Compile an encoder for a map key. Map keys are stored as object keys and
* are always strings locally. Convert them to their scalar type before
* writing, like the reflect API does when iterating map entries.
*/
function compileMapKey(field) {
const wireType = writeTypeOfScalar(field.mapKey);
const writeScalar = compileScalarValue(field.mapKey, field.parent.typeName, field.name);
const convertKey = compileMapKeyConverter(field.mapKey);
return (writer, key) => {
// The key of a map entry is always field number 1.
writer.tag(1, wireType);
writeScalar(writer, convertKey(key));
};
}
/**
* Returns a converter from an object key (always a string) to the closest
* possible type for the map key type. Invalid keys are passed through to
* the scalar writer, which raises an error for them.
*/
function compileMapKeyConverter(type) {
switch (type) {
case descriptors_js_1.ScalarType.STRING:
return (key) => key;
case descriptors_js_1.ScalarType.BOOL:
return (key) => (key === "true" ? true : key === "false" ? false : key);
case descriptors_js_1.ScalarType.UINT64:
case descriptors_js_1.ScalarType.FIXED64:
return (key) => {
try {
return proto_int64_js_1.protoInt64.uParse(key);
}
catch (_a) {
return key;
}
};
case descriptors_js_1.ScalarType.INT64:
case descriptors_js_1.ScalarType.SFIXED64:
case descriptors_js_1.ScalarType.SINT64:
return (key) => {
try {
return proto_int64_js_1.protoInt64.parse(key);
}
catch (_a) {
return key;
}
};
default:
// Handles INT32, UINT32, SINT32, FIXED32, SFIXED32.
// We do not use individual cases to save a few bytes code size.
return (key) => {
const n = Number.parseInt(key);
return Number.isFinite(n) ? n : key;
};
}
writer.join();
}
function writeScalarValue(writer, msgName, fieldName, type, value) {
try {
switch (type) {
case descriptors_js_1.ScalarType.STRING:
writer.string(value);
break;
case descriptors_js_1.ScalarType.BOOL:
writer.bool(value);
break;
case descriptors_js_1.ScalarType.DOUBLE:
writer.double(value);
break;
case descriptors_js_1.ScalarType.FLOAT:
writer.float(value);
break;
case descriptors_js_1.ScalarType.INT32:
writer.int32(value);
break;
case descriptors_js_1.ScalarType.INT64:
writer.int64(value);
break;
case descriptors_js_1.ScalarType.UINT64:
writer.uint64(value);
break;
case descriptors_js_1.ScalarType.FIXED64:
writer.fixed64(value);
break;
case descriptors_js_1.ScalarType.BYTES:
writer.bytes(value);
break;
case descriptors_js_1.ScalarType.FIXED32:
writer.fixed32(value);
break;
case descriptors_js_1.ScalarType.SFIXED32:
writer.sfixed32(value);
break;
case descriptors_js_1.ScalarType.SFIXED64:
writer.sfixed64(value);
break;
case descriptors_js_1.ScalarType.SINT64:
writer.sint64(value);
break;
case descriptors_js_1.ScalarType.UINT32:
writer.uint32(value);
break;
case descriptors_js_1.ScalarType.SINT32:
writer.sint32(value);
break;
/**
* Compile an encoder for a bare scalar value (no tag), wrapping errors from
* the writer with the message and field name.
*/
function compileScalarValue(type, messageName, fieldName) {
const writeScalar = compileScalarWrite(type);
return (writer, value) => {
try {
writeScalar(writer, value);
}
}
catch (e) {
if (e instanceof Error) {
throw new Error(`cannot encode field ${msgName}.${fieldName} to binary: ${e.message}`);
catch (e) {
if (e instanceof Error) {
throw new Error(`cannot encode field ${messageName}.${fieldName} to binary: ${e.message}`);
}
throw e;
}
throw e;
};
}
function compileScalarWrite(type) {
switch (type) {
case descriptors_js_1.ScalarType.STRING:
return (writer, value) => writer.string(value);
case descriptors_js_1.ScalarType.BOOL:
return (writer, value) => writer.bool(value);
case descriptors_js_1.ScalarType.DOUBLE:
return (writer, value) => writer.double(value);
case descriptors_js_1.ScalarType.FLOAT:
return (writer, value) => writer.float(value);
case descriptors_js_1.ScalarType.INT32:
return (writer, value) => writer.int32(value);
case descriptors_js_1.ScalarType.INT64:
return (writer, value) => writer.int64(value);
case descriptors_js_1.ScalarType.UINT64:
return (writer, value) => writer.uint64(value);
case descriptors_js_1.ScalarType.FIXED64:
return (writer, value) => writer.fixed64(value);
case descriptors_js_1.ScalarType.BYTES:
return (writer, value) => writer.bytes(value);
case descriptors_js_1.ScalarType.FIXED32:
return (writer, value) => writer.fixed32(value);
case descriptors_js_1.ScalarType.SFIXED32:
return (writer, value) => writer.sfixed32(value);
case descriptors_js_1.ScalarType.SFIXED64:
return (writer, value) => writer.sfixed64(value);
case descriptors_js_1.ScalarType.SINT64:
return (writer, value) => writer.sint64(value);
case descriptors_js_1.ScalarType.UINT32:
return (writer, value) => writer.uint32(value);
case descriptors_js_1.ScalarType.SINT32:
return (writer, value) => writer.sint32(value);
}
}
/**
* Write a single field to binary format, if it is set. Used to serialize
* extensions: extensions always have explicit presence, so an extension
* value that was just set on the container is always written.
*
* @private
*/
function writeField(writer, opts, msg, field) {
compileField(field)(writer, opts, msg[unsafe_js_1.unsafeLocal]);
}
/**
* Compile an encoder for the wire format of a message field, honoring the
* delimited encoding of the field. The tag is written by the encoder.
*/
function compileChildWriter(field) {
const fieldNo = field.number;
const writeMessage = compiledWriter(field.message);
if (field.delimitedEncoding) {
return (writer, opts, child) => {
writer.tag(fieldNo, binary_encoding_js_1.WireType.StartGroup);
writeMessage(writer, opts, child);
writer.tag(fieldNo, binary_encoding_js_1.WireType.EndGroup);
};
}
return (writer, opts, child) => {
writer.tag(fieldNo, binary_encoding_js_1.WireType.LengthDelimited).fork();
writeMessage(writer, opts, child);
writer.join();
};
}
function writeTypeOfScalar(type) {

@@ -186,0 +424,0 @@ switch (type) {

@@ -21,8 +21,12 @@ "use strict";

const names_js_1 = require("./reflect/names.js");
const reflect_js_1 = require("./reflect/reflect.js");
const index_js_1 = require("./wkt/index.js");
const wrappers_js_1 = require("./wkt/wrappers.js");
const json_js_1 = require("./wkt/json.js");
const index_js_2 = require("./wire/index.js");
const extensions_js_1 = require("./extensions.js");
const reflect_check_js_1 = require("./reflect/reflect-check.js");
const error_js_1 = require("./reflect/error.js");
const unsafe_js_1 = require("./reflect/unsafe.js");
const scalar_js_1 = require("./reflect/scalar.js");
const message_js_1 = require("./reflect/message.js");
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.LEGACY_REQUIRED: const $name = $number;

@@ -46,3 +50,3 @@ const LEGACY_REQUIRED = 3;

function toJson(schema, message, options) {
return reflectToJson((0, reflect_js_1.reflect)(schema, message), makeWriteOptions(options));
return compiledWriter(schema)(makeWriteOptions(options), message);
}

@@ -71,119 +75,333 @@ /**

}
function reflectToJson(msg, opts) {
var _a;
const wktJson = tryWktToJson(msg, opts);
if (wktJson !== undefined)
return wktJson;
const json = {};
for (const f of msg.sortedFields) {
if (!msg.isSet(f)) {
if (f.presence == LEGACY_REQUIRED) {
throw new Error(`cannot encode ${f} to JSON: required field not set`);
const compiledWriters = new WeakMap();
/**
* Return the compiled encoder for a message, compiling it on first use.
*/
function compiledWriter(desc) {
let compiled = compiledWriters.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
return compiled;
}
function compileMessage(desc) {
const typeName = desc.typeName;
const writeWkt = compileWkt(desc);
if (writeWkt !== undefined) {
// The field reported in ForeignFieldError. All well-known types with a
// custom JSON representation have at least one field.
const foreignField = desc.fields[0];
const compiledWriter = (opts, message) => {
if (message.$typeName !== typeName && foreignField !== undefined) {
throw new error_js_1.FieldError(foreignField, `cannot use ${foreignField} with message ${message.$typeName}`, "ForeignFieldError");
}
if (!opts.alwaysEmitImplicit || f.presence !== IMPLICIT) {
// Fields with implicit presence omit zero values (e.g. empty string) by default
continue;
}
return writeWkt(opts, message);
};
compiledWriters.set(desc, compiledWriter);
return compiledWriter;
}
const sortedFields = desc.fields.concat().sort((a, b) => a.number - b.number);
// The field reported in ForeignFieldError.
const foreignField = sortedFields[0];
const fieldWriters = [];
const compiledWriter = (opts, message) => {
if (message.$typeName !== typeName && foreignField !== undefined) {
throw new error_js_1.FieldError(foreignField, `cannot use ${foreignField} with message ${message.$typeName}`, "ForeignFieldError");
}
const jsonValue = fieldToJson(f, msg.get(f), opts);
if (jsonValue !== undefined) {
json[jsonName(f, opts)] = jsonValue;
const json = {};
for (let i = 0; i < fieldWriters.length; i++) {
fieldWriters[i](opts, message, json);
}
if (opts.registry) {
writeExtensions(json, opts, opts.registry, message, desc);
}
return json;
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledWriters.set(desc, compiledWriter);
for (const field of sortedFields) {
fieldWriters.push(compileField(field));
}
if (opts.registry) {
const tagSeen = new Set();
for (const { no } of (_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : []) {
// Same tag can appear multiple times, so we
// keep track and skip identical ones.
if (!tagSeen.has(no)) {
tagSeen.add(no);
const extension = opts.registry.getExtensionFor(msg.desc, no);
if (!extension) {
continue;
return compiledWriter;
}
/**
* Compile an encoder for a well-known type with a custom JSON representation,
* or return undefined for other messages.
*/
function compileWkt(desc) {
if (!desc.typeName.startsWith("google.protobuf.")) {
return undefined;
}
switch (desc.typeName) {
case "google.protobuf.Any":
return (opts, message) => anyToJson(message, opts);
case "google.protobuf.Timestamp":
return (opts, message) => timestampToJson(message);
case "google.protobuf.Duration":
return (opts, message) => durationToJson(message);
case "google.protobuf.FieldMask":
return (opts, message) => fieldMaskToJson(message);
case "google.protobuf.Struct":
return (opts, message) => structToJson(message);
case "google.protobuf.Value":
return (opts, message) => valueToJson(message);
case "google.protobuf.ListValue":
return (opts, message) => listValueToJson(message);
default:
if ((0, wrappers_js_1.isWrapperDesc)(desc)) {
const valueField = desc.fields[0];
const localName = valueField.localName;
const zero = (0, scalar_js_1.scalarZeroValue)(valueField.scalar, false);
const writeScalar = compileScalarValue(valueField);
return (opts, message) => {
const value = message[localName];
return writeScalar(opts, value === undefined ? zero : value);
};
}
return undefined;
}
}
function compileField(field) {
switch (field.fieldKind) {
case "scalar":
case "enum":
case "message":
return compileSingularField(field);
case "list":
case "map": {
const writeValue = field.fieldKind == "list"
? compileListValue(field)
: compileMapValue(field);
const protoName = field.name;
const jsonKey = field.jsonName;
const localName = field.localName;
return (opts, message, json) => {
const value = writeValue(opts, message[localName]);
if (value !== undefined) {
json[opts.useProtoFieldName ? protoName : jsonKey] = value;
}
const value = (0, extensions_js_1.getExtension)(msg.message, extension);
const [container, field] = (0, extensions_js_1.createExtensionContainer)(extension, value);
const jsonValue = fieldToJson(field, container.get(field), opts);
if (jsonValue !== undefined) {
json[extension.jsonName] = jsonValue;
}
}
};
}
}
return json;
}
function fieldToJson(f, val, opts) {
switch (f.fieldKind) {
/**
* Compile an encoder for a singular field: the presence check, and the
* value encoder.
*/
function compileSingularField(field) {
const writeValue = compileSingularValue(field);
const protoName = field.name;
const jsonKey = field.jsonName;
const localName = field.localName;
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (opts, message, json) => {
const oneof = message[oneofLocalName];
if (oneof.case === localName) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, oneof.value);
}
};
}
if (field.presence != IMPLICIT) {
const requiredError = field.presence == LEGACY_REQUIRED
? `cannot encode ${field} to JSON: required field not set`
: undefined;
return (opts, message, json) => {
const value = message[localName];
// Fields with explicit presence have properties on the prototype
// chain for default / zero values (except for proto3).
if (value !== undefined &&
Object.prototype.hasOwnProperty.call(message, localName)) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
else if (requiredError !== undefined) {
throw new Error(requiredError);
}
};
}
// Implicit presence: the field is emitted when the value is not the zero
// value, or when alwaysEmitImplicit is enabled. The zero check is inlined
// per type, see isScalarZeroValue.
if (field.fieldKind == "enum") {
const zero = field.enum.values[0].number;
return (opts, message, json) => {
const value = message[localName];
if (value !== zero || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
}
switch (field.scalar) {
case descriptors_js_1.ScalarType.BOOL:
return (opts, message, json) => {
const value = message[localName];
if (value !== false || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
case descriptors_js_1.ScalarType.STRING:
return (opts, message, json) => {
const value = message[localName];
if (value !== "" || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
case descriptors_js_1.ScalarType.BYTES:
return (opts, message, json) => {
const value = message[localName];
if (!(value instanceof Uint8Array) ||
value.byteLength > 0 ||
opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
case descriptors_js_1.ScalarType.DOUBLE:
case descriptors_js_1.ScalarType.FLOAT:
return (opts, message, json) => {
const value = message[localName];
// Object.is distinguishes -0 from 0.
if (!Object.is(value, 0) || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
default:
return (opts, message, json) => {
const value = message[localName];
// Loose comparison matches 0n, 0 and "0".
if (value != 0 || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
}
}
/**
* Compile an encoder for the value of a field of any kind. Used for
* extension values.
*/
function compileFieldValue(field) {
switch (field.fieldKind) {
case "scalar":
return scalarToJson(f, val);
case "enum":
case "message":
return reflectToJson(val, opts);
case "enum":
return enumToJsonInternal(f.enum, val, opts.enumAsInteger);
return compileSingularValue(field);
case "list":
return listToJson(val, opts);
return compileListValue(field);
case "map":
return mapToJson(val, opts);
return compileMapValue(field);
}
}
function mapToJson(map, opts) {
const f = map.field();
const jsonObj = {};
switch (f.mapKind) {
/**
* Compile an encoder for the value of a singular field.
*/
function compileSingularValue(field) {
switch (field.fieldKind) {
case "scalar":
for (const [entryKey, entryValue] of map) {
jsonObj[entryKey] = scalarToJson(f, entryValue);
}
break;
return compileScalarValue(field);
case "enum":
return compileEnumValue(field);
case "message":
for (const [entryKey, entryValue] of map) {
jsonObj[entryKey] = reflectToJson(entryValue, opts);
}
break;
return compileMessageValue(field);
}
}
/**
* Compile an encoder for the value of a message field.
*/
function compileMessageValue(field) {
const { toMessage } = (0, message_js_1.localMessageMapper)(field);
const writeMessage = compiledWriter(field.message);
return (opts, value) => writeMessage(opts, toMessage(value));
}
/**
* Compile an encoder for a list field value. Returns undefined for an empty
* list, unless alwaysEmitImplicit is enabled.
*/
function compileListValue(field) {
const writeItem = compileListItemValue(field);
return (opts, value) => {
const items = value;
if (items.length == 0 && !opts.alwaysEmitImplicit) {
return undefined;
}
const jsonArray = [];
for (let i = 0; i < items.length; i++) {
jsonArray.push(writeItem(opts, items[i]));
}
return jsonArray;
};
}
function compileListItemValue(field) {
switch (field.listKind) {
case "scalar":
return compileScalarValue(field);
case "enum":
for (const [entryKey, entryValue] of map) {
jsonObj[entryKey] = enumToJsonInternal(f.enum, entryValue, opts.enumAsInteger);
}
break;
return compileEnumValue(field);
case "message":
return compileMessageValue(field);
}
return opts.alwaysEmitImplicit || map.size > 0 ? jsonObj : undefined;
}
function listToJson(list, opts) {
const f = list.field();
const jsonArr = [];
switch (f.listKind) {
/**
* Compile an encoder for a map field value. Returns undefined for an empty
* map, unless alwaysEmitImplicit is enabled. Map keys are stored as object
* keys and are used as JSON keys as-is.
*/
function compileMapValue(field) {
const writeMapValue = compileMapEntryValue(field);
return (opts, value) => {
const record = value;
const keys = Object.keys(record);
if (keys.length == 0 && !opts.alwaysEmitImplicit) {
return undefined;
}
const jsonObject = {};
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
jsonObject[key] = writeMapValue(opts, record[key]);
}
return jsonObject;
};
}
function compileMapEntryValue(field) {
switch (field.mapKind) {
case "scalar":
for (const item of list) {
jsonArr.push(scalarToJson(f, item));
}
break;
return compileScalarValue(field);
case "enum":
for (const item of list) {
jsonArr.push(enumToJsonInternal(f.enum, item, opts.enumAsInteger));
}
break;
return compileEnumValue(field);
case "message":
for (const item of list) {
jsonArr.push(reflectToJson(item, opts));
}
break;
return compileMessageValue(field);
}
return opts.alwaysEmitImplicit || jsonArr.length > 0 ? jsonArr : undefined;
}
function enumToJsonInternal(desc, value, enumAsInteger) {
var _a;
if (typeof value != "number") {
throw new Error(`cannot encode ${desc} to JSON: expected number, got ${(0, reflect_check_js_1.formatVal)(value)}`);
}
/**
* Compile an encoder for an enum value.
*/
function compileEnumValue(field) {
const desc = field.enum;
if (desc.typeName == "google.protobuf.NullValue") {
return null;
return (opts, value) => {
if (typeof value != "number") {
throw errorEnumValue(desc, value);
}
return null;
};
}
if (enumAsInteger) {
return value;
}
const val = desc.value[value];
return (_a = val === null || val === void 0 ? void 0 : val.name) !== null && _a !== void 0 ? _a : value; // if we don't know the enum value, just return the number
return (opts, value) => {
var _a, _b;
if (typeof value != "number") {
throw errorEnumValue(desc, value);
}
if (opts.enumAsInteger) {
return value;
}
// If we don't know the enum value, just return the number.
return (_b = (_a = desc.value[value]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : value;
};
}
function scalarToJson(field, value) {
var _a, _b, _c, _d, _e, _f;
function errorEnumValue(desc, value) {
return new Error(`cannot encode ${desc} to JSON: expected number, got ${(0, reflect_check_js_1.formatVal)(value)}`);
}
/**
* Compile an encoder for a scalar value. Errors report the original field
* descriptor, which may be a list or map field for items of those fields.
*/
function compileScalarValue(field) {
switch (field.scalar) {

@@ -196,32 +414,40 @@ // int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted.

case descriptors_js_1.ScalarType.UINT32:
if (typeof value != "number") {
throw new Error(`cannot encode ${field} to JSON: ${(_a = (0, reflect_check_js_1.checkField)(field, value)) === null || _a === void 0 ? void 0 : _a.message}`);
}
return value;
return (opts, value) => {
if (typeof value != "number") {
throw errorScalarValue(field, value);
}
return value;
};
// 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.FLOAT:
case descriptors_js_1.ScalarType.DOUBLE: // eslint-disable-line no-fallthrough
if (typeof value != "number") {
throw new Error(`cannot encode ${field} to JSON: ${(_b = (0, reflect_check_js_1.checkField)(field, value)) === null || _b === void 0 ? void 0 : _b.message}`);
}
if (Number.isNaN(value))
return "NaN";
if (value === Number.POSITIVE_INFINITY)
return "Infinity";
if (value === Number.NEGATIVE_INFINITY)
return "-Infinity";
return value;
case descriptors_js_1.ScalarType.DOUBLE:
return (opts, value) => {
if (typeof value != "number") {
throw errorScalarValue(field, value);
}
if (Number.isNaN(value))
return "NaN";
if (value === Number.POSITIVE_INFINITY)
return "Infinity";
if (value === Number.NEGATIVE_INFINITY)
return "-Infinity";
return value;
};
// string:
case descriptors_js_1.ScalarType.STRING:
if (typeof value != "string") {
throw new Error(`cannot encode ${field} to JSON: ${(_c = (0, reflect_check_js_1.checkField)(field, value)) === null || _c === void 0 ? void 0 : _c.message}`);
}
return value;
return (opts, value) => {
if (typeof value != "string") {
throw errorScalarValue(field, value);
}
return value;
};
// bool:
case descriptors_js_1.ScalarType.BOOL:
if (typeof value != "boolean") {
throw new Error(`cannot encode ${field} to JSON: ${(_d = (0, reflect_check_js_1.checkField)(field, value)) === null || _d === void 0 ? void 0 : _d.message}`);
}
return value;
return (opts, value) => {
if (typeof value != "boolean") {
throw errorScalarValue(field, value);
}
return value;
};
// JSON value will be a decimal string. Either numbers or strings are accepted.

@@ -233,46 +459,52 @@ case descriptors_js_1.ScalarType.UINT64:

case descriptors_js_1.ScalarType.SINT64:
if (typeof value == "bigint" ||
typeof value == "string" ||
(typeof value == "number" && Number.isInteger(value))) {
return value.toString();
}
throw new Error(`cannot encode ${field} to JSON: ${(_e = (0, reflect_check_js_1.checkField)(field, value)) === null || _e === void 0 ? void 0 : _e.message}`);
return (opts, value) => {
if (typeof value == "bigint" ||
typeof value == "string" ||
(typeof value == "number" && Number.isInteger(value))) {
return value.toString();
}
throw errorScalarValue(field, value);
};
// 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 (value instanceof Uint8Array) {
return (0, index_js_2.base64Encode)(value);
}
throw new Error(`cannot encode ${field} to JSON: ${(_f = (0, reflect_check_js_1.checkField)(field, value)) === null || _f === void 0 ? void 0 : _f.message}`);
return (opts, value) => {
if (value instanceof Uint8Array) {
return (0, index_js_2.base64Encode)(value);
}
throw errorScalarValue(field, value);
};
}
}
function jsonName(f, opts) {
return opts.useProtoFieldName ? f.name : f.jsonName;
function errorScalarValue(field, value) {
var _a;
return new Error(`cannot encode ${field} to JSON: ${(_a = (0, reflect_check_js_1.checkField)(field, value)) === null || _a === void 0 ? void 0 : _a.message}`);
}
// returns a json value if wkt, otherwise returns undefined.
function tryWktToJson(msg, opts) {
if (!msg.desc.typeName.startsWith("google.protobuf.")) {
return undefined;
/**
* Write extensions for unknown fields that are found in the registry.
*/
function writeExtensions(json, opts, registry, message, desc) {
const unknown = message.$unknown;
if (unknown === undefined) {
return;
}
switch (msg.desc.typeName) {
case "google.protobuf.Any":
return anyToJson(msg.message, opts);
case "google.protobuf.Timestamp":
return timestampToJson(msg.message);
case "google.protobuf.Duration":
return durationToJson(msg.message);
case "google.protobuf.FieldMask":
return fieldMaskToJson(msg.message);
case "google.protobuf.Struct":
return structToJson(msg.message);
case "google.protobuf.Value":
return valueToJson(msg.message);
case "google.protobuf.ListValue":
return listValueToJson(msg.message);
default:
if ((0, wrappers_js_1.isWrapperDesc)(msg.desc)) {
const valueField = msg.desc.fields[0];
return scalarToJson(valueField, msg.get(valueField));
const tagSeen = new Set();
for (let i = 0; i < unknown.length; i++) {
const { no } = unknown[i];
// Same tag can appear multiple times, so we
// keep track and skip identical ones.
if (!tagSeen.has(no)) {
tagSeen.add(no);
const extension = registry.getExtensionFor(desc, no);
if (!extension) {
continue;
}
return undefined;
const value = (0, extensions_js_1.getExtension)(message, extension);
const [container, field] = (0, extensions_js_1.createExtensionContainer)(extension, value);
const local = container[unsafe_js_1.unsafeLocal];
const jsonValue = compileFieldValue(field)(opts, local[field.localName]);
if (jsonValue !== undefined) {
json[extension.jsonName] = jsonValue;
}
}
}

@@ -296,6 +528,7 @@ }

}
const reflected = (0, reflect_js_1.reflect)(desc, message);
const json = (0, wrappers_js_1.hasCustomJsonRepresentation)(desc)
? { value: tryWktToJson(reflected, opts) }
: reflectToJson(reflected, opts);
? {
value: compiledWriter(desc)(opts, message),
}
: compiledWriter(desc)(opts, message);
json["@type"] = val.typeUrl;

@@ -307,3 +540,3 @@ return json;

const nanos = val.nanos;
if (seconds > 315576000000 || seconds < -315576000000) {
if (seconds > json_js_1.durationSecondsMax || seconds < json_js_1.durationSecondsMin) {
throw new Error(`cannot encode message ${val.$typeName} to JSON: value out of range`);

@@ -343,4 +576,6 @@ }

const json = {};
for (const [k, v] of Object.entries(val.fields)) {
json[k] = valueToJson(v);
const keys = Object.keys(val.fields);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
json[key] = valueToJson(val.fields[key]);
}

@@ -375,4 +610,3 @@ return json;

const ms = Number(val.seconds) * 1000;
if (ms < Date.parse("0001-01-01T00:00:00Z") ||
ms > Date.parse("9999-12-31T23:59:59Z")) {
if (ms < json_js_1.timestampMsMin || ms > json_js_1.timestampMsMax) {
throw new Error(`cannot encode message ${val.$typeName} to JSON: must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive`);

@@ -379,0 +613,0 @@ }

@@ -13,2 +13,3 @@ /**

export declare function base64Decode(base64Str: string): Uint8Array<ArrayBuffer>;
type Base64Encoding = "std" | "std_raw" | "url";
/**

@@ -24,2 +25,3 @@ * Encode a byte array to a base64 string.

*/
export declare function base64Encode(bytes: Uint8Array, encoding?: "std" | "std_raw" | "url"): string;
export declare function base64Encode(bytes: Uint8Array, encoding?: Base64Encoding): string;
export {};

@@ -18,2 +18,4 @@ "use strict";

exports.base64Encode = base64Encode;
// Native Uint8Array.prototype.setFromBase64, if the runtime provides it.
const nativeSetFromBase64 = Uint8Array.prototype.setFromBase64;
/**

@@ -31,10 +33,31 @@ * Decodes a base64 string to a byte array.

function base64Decode(base64Str) {
const len = base64Str.length;
// Decoded size, assuming a well-formed string: three bytes per group of
// four characters, minus one byte for each padding character.
let size = len - ((len + 3) >> 2);
if ((len & 3) == 0 && base64Str[len - 1] == "=") {
size -= base64Str[len - 2] == "=" ? 2 : 1;
}
const bytes = new Uint8Array(size);
let written = -1;
if (nativeSetFromBase64) {
try {
const result = nativeSetFromBase64.call(bytes, base64Str);
if (result.read == len) {
written = result.written;
}
}
catch (_a) {
// The native decoder rejects base64url and inner padding, which we accept.
}
}
if (written < 0) {
written = setFromBase64(bytes, base64Str);
}
return written == size ? bytes : bytes.subarray(0, written);
}
/** Writes into `bytes` from index 0 and returns the number of bytes written. */
function setFromBase64(bytes, base64Str) {
const table = getDecodeTable();
// estimate byte size, not accounting for inner padding and whitespace
let es = (base64Str.length * 3) / 4;
if (base64Str[base64Str.length - 2] == "=")
es -= 2;
else if (base64Str[base64Str.length - 1] == "=")
es -= 1;
let bytes = new Uint8Array(es), bytePos = 0, // position in byte array
let bytePos = 0, // position in byte array
groupPos = 0, // position in base64 group

@@ -82,4 +105,10 @@ b, // current byte

throw Error("invalid base64 string");
return bytes.subarray(0, bytePos);
return bytePos;
}
const nativeToBase64 = Uint8Array.prototype.toBase64;
const toBase64OptionsMap = {
std: { alphabet: "base64", omitPadding: false },
std_raw: { alphabet: "base64", omitPadding: true },
url: { alphabet: "base64url", omitPadding: true },
};
/**

@@ -96,2 +125,5 @@ * Encode a byte array to a base64 string.

function base64Encode(bytes, encoding = "std") {
if (nativeToBase64) {
return nativeToBase64.call(bytes, toBase64OptionsMap[encoding]);
}
const table = getEncodeTable(encoding);

@@ -98,0 +130,0 @@ const pad = encoding == "std";

@@ -63,26 +63,34 @@ /**

export declare class BinaryWriter {
private readonly encodeUtf8;
/**
* We cannot allocate a buffer for the entire output
* because we don't know its size.
*
* So we collect smaller chunks of known size and
* concat them later.
*
* Use `raw()` to push data to this array. It will flush
* `buf` first.
* Growable byte buffer. We allocate a reasonably sized
* initial buffer and double its capacity when needed.
*/
private chunks;
private buffer;
/**
* A growing buffer for byte values. If you don't know
* the size of the data you are writing, push to this
* array.
* Cached DataView for fixed-width writes. Read it via `view()`, which
* rebuilds it if `buffer` has since grown.
*/
protected buf: number[];
private viewCache;
/**
* Previous fork states.
* Current write position in the buffer.
*/
private stack;
private pos;
/**
* Previous fork positions (the write position at the time
* `fork()` was called).
*/
private stackPos;
/**
* UTF-8 codec used by `string()`. Uses the text encoding's `encodeUtf8Into`,
* or emulates it if a custom `encodeUtf8` was passed to the constructor.
*/
private readonly encodeUtf8Into;
constructor(encodeUtf8?: (text: string) => Uint8Array);
private ensureCapacity;
/**
* The DataView over `buffer`, rebuilt only if the buffer has grown since it
* was last used.
*/
private view;
/**
* Return all bytes written and reset this writer.

@@ -175,2 +183,10 @@ */

uint64(value: string | number | bigint): this;
/**
* Write a 64-bit varint directly into the buffer. Accepts the value as
* split low/high 32-bit words.
*
* Ported from varint64write() to avoid the intermediate number[] buffer.
* See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/writer.js#L344
*/
private writeVarint64;
}

@@ -187,3 +203,3 @@ export declare class BinaryReader {

readonly len: number;
protected readonly buf: Uint8Array;
private readonly buf;
private readonly view;

@@ -205,7 +221,9 @@ constructor(buf: Uint8Array, decodeUtf8?: (bytes: Uint8Array, strict?: boolean) => string);

skip(wireType: WireType, fieldNo?: number, recursionLimit?: number): Uint8Array;
protected varint64: () => [number, number];
private varint64Lo;
private varint64Hi;
private varint64;
/**
* Throws error if position in byte array is out of range.
*/
protected assertBounds(): void;
private assertBounds;
/**

@@ -212,0 +230,0 @@ * Read a `uint32` field, an unsigned 32 bit varint.

@@ -83,30 +83,51 @@ "use strict";

class BinaryWriter {
constructor(encodeUtf8 = (0, text_encoding_js_1.getTextEncoding)().encodeUtf8) {
this.encodeUtf8 = encodeUtf8;
constructor(encodeUtf8) {
/**
* Previous fork states.
* Previous fork positions (the write position at the time
* `fork()` was called).
*/
this.stack = [];
this.chunks = [];
this.buf = [];
this.stackPos = [];
this.encodeUtf8Into = encodeUtf8
? (0, text_encoding_js_1.emulateEncodeInto)(encodeUtf8)
: (0, text_encoding_js_1.getTextEncoding)().encodeUtf8Into;
this.buffer = EMPTY_BUFFER;
this.viewCache = EMPTY_VIEW;
this.pos = 0;
}
ensureCapacity(size) {
const required = this.pos + size;
if (required > this.buffer.length) {
let newLen = this.buffer.length || INITIAL_SIZE;
while (newLen < required)
newLen *= 2;
const newBuf = new Uint8Array(newLen);
if (this.pos > 0)
newBuf.set(this.buffer);
this.buffer = newBuf;
}
}
/**
* The DataView over `buffer`, rebuilt only if the buffer has grown since it
* was last used.
*/
view() {
const bytes = this.buffer;
const view = this.viewCache;
// Since ensureCapacity() only ever replaces the buffer with a strictly larger one,
// equal lengths mean the view is still current. This is faster than comparing
// buffers directly.
if (view.byteLength === bytes.byteLength)
return view;
const newView = new DataView(bytes.buffer);
this.viewCache = newView;
return newView;
}
/**
* Return all bytes written and reset this writer.
*/
finish() {
if (this.buf.length) {
this.chunks.push(new Uint8Array(this.buf)); // flush the buffer
this.buf = [];
}
let len = 0;
for (let i = 0; i < this.chunks.length; i++)
len += this.chunks[i].length;
let bytes = new Uint8Array(len);
let offset = 0;
for (let i = 0; i < this.chunks.length; i++) {
bytes.set(this.chunks[i], offset);
offset += this.chunks[i].length;
}
this.chunks = [];
return bytes;
const result = this.buffer.slice(0, this.pos);
this.pos = 0;
this.stackPos = [];
return result;
}

@@ -120,5 +141,7 @@ /**

fork() {
this.stack.push({ chunks: this.chunks, buf: this.buf });
this.chunks = [];
this.buf = [];
this.stackPos.push(this.pos);
// Reserve room for the length prefix. Payloads under 128 bytes, fairly
// common, will need no copy in join().
this.ensureCapacity(DEFAULT_LEN_PREFIX_SIZE);
this.buffer[this.pos++] = 0;
return this;

@@ -131,13 +154,20 @@ }

join() {
// get chunk of fork
let chunk = this.finish();
// restore previous state
let prev = this.stack.pop();
if (!prev)
const forkPos = this.stackPos.pop();
if (forkPos === undefined)
throw new Error("invalid state, fork stack empty");
this.chunks = prev.chunks;
this.buf = prev.buf;
// write length of chunk as varint
this.uint32(chunk.byteLength);
return this.raw(chunk);
// fork() presumed the payload would fit the prefix it reserved. If it
// doesn't, we need to shift the bytes we just wrote.
const len = this.pos - forkPos - DEFAULT_LEN_PREFIX_SIZE;
const lenPrefixSize = varint32Size(len);
if (lenPrefixSize > DEFAULT_LEN_PREFIX_SIZE) {
// Widening pushes the payload past the end of the buffer, so grow first:
// copyWithin clamps to the buffer instead of throwing, so a short buffer
// would silently drop the tail of the payload.
this.ensureCapacity(lenPrefixSize - DEFAULT_LEN_PREFIX_SIZE);
this.buffer.copyWithin(forkPos + lenPrefixSize, forkPos + DEFAULT_LEN_PREFIX_SIZE, this.pos);
}
this.pos = forkPos;
this.uint32(len);
this.pos += len;
return this;
}

@@ -158,7 +188,5 @@ /**

raw(chunk) {
if (this.buf.length) {
this.chunks.push(new Uint8Array(this.buf));
this.buf = [];
}
this.chunks.push(chunk);
this.ensureCapacity(chunk.length);
this.buffer.set(chunk, this.pos);
this.pos += chunk.length;
return this;

@@ -171,8 +199,14 @@ }

assertUInt32(value);
// write value as varint 32, inlined for speed
// uint32 varints are at most 5 bytes; reserve once and avoid per-byte
// capacity checks.
this.ensureCapacity(5);
if (value < 0x80) {
this.buffer[this.pos++] = value;
return this;
}
while (value > 0x7f) {
this.buf.push((value & 0x7f) | 0x80);
value = value >>> 7;
this.buffer[this.pos++] = (value & 0x7f) | 0x80;
value >>>= 7;
}
this.buf.push(value);
this.buffer[this.pos++] = value;
return this;

@@ -185,3 +219,12 @@ }

assertInt32(value);
(0, varint_js_1.varint32write)(value, this.buf);
if (value >= 0) {
return this.uint32(value);
}
// Negative: sign-extend to 64 bits, encodes to 10 bytes.
this.ensureCapacity(10);
for (let i = 0; i < 9; i++) {
this.buffer[this.pos++] = (value & 0x7f) | 0x80;
value >>= 7;
}
this.buffer[this.pos++] = 1;
return this;

@@ -193,3 +236,4 @@ }

bool(value) {
this.buf.push(value ? 1 : 0);
this.ensureCapacity(1);
this.buffer[this.pos++] = value ? 1 : 0;
return this;

@@ -201,3 +245,3 @@ }

bytes(value) {
this.uint32(value.byteLength); // write length of chunk as varint
this.uint32(value.byteLength);
return this.raw(value);

@@ -209,5 +253,45 @@ }

string(value) {
let chunk = this.encodeUtf8(value);
this.uint32(chunk.byteLength); // write length of chunk as varint
return this.raw(chunk);
// TextEncoder.encode() coerces its argument to string, but encodeInto()
// rejects non-strings.
if (typeof value !== "string") {
value = String(value);
}
const len = value.length;
// Fast path for ASCII.
if (len <= ASCII_MAX_LENGTH) {
this.ensureCapacity(len + 1);
const ascii = this.buffer;
let pos = this.pos;
ascii[pos++] = len;
let i = 0;
for (; i < len; i++) {
const code = value.charCodeAt(i);
if (code > 0x7f)
break;
ascii[pos++] = code;
}
if (i == len) {
this.pos = pos;
return this;
}
}
// encodeUtf8Into needs the full-length buffer upfront. The length prefix
// can be upto 5 bytes, and a UTF-16 code unit takes at most 3 UTF-8 bytes.
this.ensureCapacity(len * 3 + 5);
// The length prefix goes first, but the byte length is only known after
// encoding. We guess the final varint size here (assuming most text is
// ASCII) and then encode.
const lenPrefixSizeGuess = varint32Size(len);
const buf = this.buffer;
const start = this.pos;
const { written } = this.encodeUtf8Into(value, buf.subarray(start + lenPrefixSizeGuess));
// If our guess was incorrect, we need to shift the bytes we just wrote.
const lenPrefixSize = varint32Size(written);
if (lenPrefixSize != lenPrefixSizeGuess) {
buf.copyWithin(start + lenPrefixSize, start + lenPrefixSizeGuess, start + lenPrefixSizeGuess + written);
}
// Write the lenPrefix and advance the pos.
this.uint32(written);
this.pos += written;
return this;
}

@@ -219,5 +303,6 @@ /**

assertFloat32(value);
let chunk = new Uint8Array(4);
new DataView(chunk.buffer).setFloat32(0, value, true);
return this.raw(chunk);
this.ensureCapacity(4);
this.view().setFloat32(this.pos, value, true);
this.pos += 4;
return this;
}

@@ -228,5 +313,6 @@ /**

double(value) {
let chunk = new Uint8Array(8);
new DataView(chunk.buffer).setFloat64(0, value, true);
return this.raw(chunk);
this.ensureCapacity(8);
this.view().setFloat64(this.pos, value, true);
this.pos += 8;
return this;
}

@@ -238,5 +324,6 @@ /**

assertUInt32(value);
let chunk = new Uint8Array(4);
new DataView(chunk.buffer).setUint32(0, value, true);
return this.raw(chunk);
this.ensureCapacity(4);
this.view().setUint32(this.pos, value, true);
this.pos += 4;
return this;
}

@@ -248,5 +335,6 @@ /**

assertInt32(value);
let chunk = new Uint8Array(4);
new DataView(chunk.buffer).setInt32(0, value, true);
return this.raw(chunk);
this.ensureCapacity(4);
this.view().setInt32(this.pos, value, true);
this.pos += 4;
return this;
}

@@ -258,6 +346,4 @@ /**

assertInt32(value);
// zigzag encode
value = ((value << 1) ^ (value >> 31)) >>> 0;
(0, varint_js_1.varint32write)(value, this.buf);
return this;
// zigzag encode then emit as uint32 varint
return this.uint32(((value << 1) ^ (value >> 31)) >>> 0);
}

@@ -268,6 +354,9 @@ /**

sfixed64(value) {
let chunk = new Uint8Array(8), view = new DataView(chunk.buffer), tc = proto_int64_js_1.protoInt64.enc(value);
view.setInt32(0, tc.lo, true);
view.setInt32(4, tc.hi, true);
return this.raw(chunk);
const tc = proto_int64_js_1.protoInt64.enc(value);
this.ensureCapacity(8);
const view = this.view();
view.setInt32(this.pos, tc.lo, true);
view.setInt32(this.pos + 4, tc.hi, true);
this.pos += 8;
return this;
}

@@ -278,6 +367,9 @@ /**

fixed64(value) {
let chunk = new Uint8Array(8), view = new DataView(chunk.buffer), tc = proto_int64_js_1.protoInt64.uEnc(value);
view.setInt32(0, tc.lo, true);
view.setInt32(4, tc.hi, true);
return this.raw(chunk);
const tc = proto_int64_js_1.protoInt64.uEnc(value);
this.ensureCapacity(8);
const view = this.view();
view.setInt32(this.pos, tc.lo, true);
view.setInt32(this.pos + 4, tc.hi, true);
this.pos += 8;
return this;
}

@@ -288,5 +380,4 @@ /**

int64(value) {
let tc = proto_int64_js_1.protoInt64.enc(value);
(0, varint_js_1.varint64write)(tc.lo, tc.hi, this.buf);
return this;
const tc = proto_int64_js_1.protoInt64.enc(value);
return this.writeVarint64(tc.lo, tc.hi);
}

@@ -300,4 +391,3 @@ /**

sign = tc.hi >> 31, lo = (tc.lo << 1) ^ sign, hi = ((tc.hi << 1) | (tc.lo >>> 31)) ^ sign;
(0, varint_js_1.varint64write)(lo, hi, this.buf);
return this;
return this.writeVarint64(lo, hi);
}

@@ -309,3 +399,43 @@ /**

const tc = proto_int64_js_1.protoInt64.uEnc(value);
(0, varint_js_1.varint64write)(tc.lo, tc.hi, this.buf);
return this.writeVarint64(tc.lo, tc.hi);
}
/**
* Write a 64-bit varint directly into the buffer. Accepts the value as
* split low/high 32-bit words.
*
* Ported from varint64write() to avoid the intermediate number[] buffer.
* See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/writer.js#L344
*/
writeVarint64(lo, hi) {
// Worst case: 10 bytes.
this.ensureCapacity(10);
const buf = this.buffer;
let pos = this.pos;
for (let i = 0; i < 28; i = i + 7) {
const shift = lo >>> i;
const hasNext = !(shift >>> 7 == 0 && hi == 0);
buf[pos++] = (hasNext ? shift | 0x80 : shift) & 0xff;
if (!hasNext) {
this.pos = pos;
return this;
}
}
const splitBits = ((lo >>> 28) & 0x0f) | ((hi & 0x07) << 4);
const hasMoreBits = !(hi >> 3 == 0);
buf[pos++] = (hasMoreBits ? splitBits | 0x80 : splitBits) & 0xff;
if (!hasMoreBits) {
this.pos = pos;
return this;
}
for (let i = 3; i < 31; i = i + 7) {
const shift = hi >>> i;
const hasNext = !(shift >>> 7 == 0);
buf[pos++] = (hasNext ? shift | 0x80 : shift) & 0xff;
if (!hasNext) {
this.pos = pos;
return this;
}
}
buf[pos++] = (hi >>> 31) & 0x01;
this.pos = pos;
return this;

@@ -315,5 +445,47 @@ }

exports.BinaryWriter = BinaryWriter;
/**
* Capacity of the buffer allocated by the first write..
*/
const INITIAL_SIZE = 128;
/**
* Bytes `fork()` reserves for the length prefix, betting that the payload will
* be under 128 bytes. `join()` fills them in, and widens them if the bet was
* wrong.
*/
const DEFAULT_LEN_PREFIX_SIZE = 1;
/**
* Shared empty buffer used as the initial value before the first write.
* Avoids allocating and zeroing `INITIAL_SIZE` bytes per BinaryWriter when a
* writer is only used for a tiny message (or not used at all).
*/
const EMPTY_BUFFER = new Uint8Array(0);
/**
* Shared empty view, paired with `EMPTY_BUFFER`. Never written to: any
* fixed-width write first grows the buffer, which replaces this view.
*/
const EMPTY_VIEW = new DataView(EMPTY_BUFFER.buffer);
/**
* Longest string on the ASCII fast paths. Must stay below 0x80, so
* that the writer's length prefix always fits a single varint byte.
*/
const ASCII_MAX_LENGTH = 32;
/**
* Number of bytes needed to encode `value` as an unsigned 32-bit varint.
*/
function varint32Size(value) {
if (value < 0x80)
return 1;
if (value < 0x4000)
return 2;
if (value < 0x200000)
return 3;
if (value < 0x10000000)
return 4;
return 5;
}
class BinaryReader {
constructor(buf, decodeUtf8 = (0, text_encoding_js_1.getTextEncoding)().decodeUtf8) {
this.decodeUtf8 = decodeUtf8;
this.varint64Lo = 0;
this.varint64Hi = 0;
this.varint64 = varint_js_1.varint64read; // dirty cast for `this`

@@ -419,3 +591,4 @@ /**

int64() {
return proto_int64_js_1.protoInt64.dec(...this.varint64());
this.varint64();
return proto_int64_js_1.protoInt64.dec(this.varint64Lo, this.varint64Hi);
}

@@ -426,3 +599,4 @@ /**

uint64() {
return proto_int64_js_1.protoInt64.uDec(...this.varint64());
this.varint64();
return proto_int64_js_1.protoInt64.uDec(this.varint64Lo, this.varint64Hi);
}

@@ -433,3 +607,5 @@ /**

sint64() {
let [lo, hi] = this.varint64();
this.varint64();
let lo = this.varint64Lo;
let hi = this.varint64Hi;
// decode zig zag

@@ -445,4 +621,10 @@ let s = -(lo & 1);

bool() {
let [lo, hi] = this.varint64();
return lo !== 0 || hi !== 0;
// Fast path: most bools are 0x0 or 0x1.
const b = this.buf[this.pos];
if (b < 0x80) {
this.pos++;
return b !== 0;
}
this.varint64();
return this.varint64Lo !== 0 || this.varint64Hi !== 0;
}

@@ -503,3 +685,17 @@ /**

string(strict) {
return this.decodeUtf8(this.bytes(), strict);
const bytes = this.bytes();
const len = bytes.length;
// Fast path for ASCII.
if (len <= ASCII_MAX_LENGTH) {
const codes = new Array(len);
for (let i = 0; i < len; i++) {
const byte = bytes[i];
if (byte > 0x7f) {
return this.decodeUtf8(bytes, strict);
}
codes[i] = byte;
}
return String.fromCharCode.apply(String, codes);
}
return this.decodeUtf8(bytes, strict);
}

@@ -506,0 +702,0 @@ }

export * from "./binary-encoding.js";
export * from "./base64-encoding.js";
export * from "./text-encoding.js";
export { getTextEncoding, configureTextEncoding } from "./text-encoding.js";
export * from "./text-format.js";
export * from "./size-delimited.js";

@@ -30,6 +30,9 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.configureTextEncoding = exports.getTextEncoding = void 0;
__exportStar(require("./binary-encoding.js"), exports);
__exportStar(require("./base64-encoding.js"), exports);
__exportStar(require("./text-encoding.js"), exports);
var text_encoding_js_1 = require("./text-encoding.js");
Object.defineProperty(exports, "getTextEncoding", { enumerable: true, get: function () { return text_encoding_js_1.getTextEncoding; } });
Object.defineProperty(exports, "configureTextEncoding", { enumerable: true, get: function () { return text_encoding_js_1.configureTextEncoding; } });
__exportStar(require("./text-format.js"), exports);
__exportStar(require("./size-delimited.js"), exports);

@@ -11,2 +11,8 @@ interface TextEncoding {

/**
* Encode UTF-8 text to a Uint8Array. The destination must be large enough.
*/
encodeUtf8Into: (text: string, dest: Uint8Array) => {
written: number;
};
/**
* Decode UTF-8 text from binary. If `strict` is true, throw on invalid byte

@@ -18,2 +24,3 @@ * sequences instead of silently substituting U+FFFD. Implementations that

}
type TextEncodingConfig = Omit<TextEncoding, "encodeUtf8Into"> & Partial<Pick<TextEncoding, "encodeUtf8Into">>;
/**

@@ -25,7 +32,17 @@ * Protobuf-ES requires the Text Encoding API to convert UTF-8 from and to

*
* Providing `encodeUtf8Into` is optional for backwards compatibility. If it
* is omitted, we emulate it with a wrapper that calls `encodeUtf8`.
*
* Note that the Text Encoding API does not provide a way to validate UTF-8.
* Our implementation falls back to use encodeURIComponent().
* Our implementation uses String.prototype.isWellFormed, and falls back
* to use encodeURIComponent().
*/
export declare function configureTextEncoding(textEncoding: TextEncoding): void;
export declare function configureTextEncoding(textEncoding: TextEncodingConfig): void;
export declare function getTextEncoding(): TextEncoding;
/**
* Simplistic polyfill for encodeUtf8Into.
*
* @private
*/
export declare function emulateEncodeInto(encodeUtf8: (str: string) => Uint8Array): TextEncoding["encodeUtf8Into"];
export {};

@@ -18,2 +18,3 @@ "use strict";

exports.getTextEncoding = getTextEncoding;
exports.emulateEncodeInto = emulateEncodeInto;
const symbol = Symbol.for("@bufbuild/protobuf/text-encoding");

@@ -26,25 +27,33 @@ /**

*
* Providing `encodeUtf8Into` is optional for backwards compatibility. If it
* is omitted, we emulate it with a wrapper that calls `encodeUtf8`.
*
* Note that the Text Encoding API does not provide a way to validate UTF-8.
* Our implementation falls back to use encodeURIComponent().
* Our implementation uses String.prototype.isWellFormed, and falls back
* to use encodeURIComponent().
*/
function configureTextEncoding(textEncoding) {
globalThis[symbol] = textEncoding;
var _a;
globalThis[symbol] = Object.assign(Object.assign({}, textEncoding), { encodeUtf8Into: (_a = textEncoding.encodeUtf8Into) !== null && _a !== void 0 ? _a : emulateEncodeInto(textEncoding.encodeUtf8.bind(textEncoding)) });
}
function getTextEncoding() {
if (globalThis[symbol] == undefined) {
const te = new globalThis.TextEncoder();
const td = new globalThis.TextDecoder();
let tdStrict;
globalThis[symbol] = {
const globals = globalThis;
if (!globals[symbol]) {
const textEncoder = new globals.TextEncoder();
const textDecoder = new globals.TextDecoder();
let textDecoderStrict;
const config = {
encodeUtf8(text) {
return te.encode(text);
return textEncoder.encode(text);
},
decodeUtf8(bytes, strict) {
if (strict) {
if (tdStrict === undefined) {
tdStrict = new globalThis.TextDecoder("utf-8", { fatal: true });
if (!textDecoderStrict) {
textDecoderStrict = new globals.TextDecoder("utf-8", {
fatal: true,
});
}
return tdStrict.decode(bytes);
return textDecoderStrict.decode(bytes);
}
return td.decode(bytes);
return textDecoder.decode(bytes);
},

@@ -61,4 +70,29 @@ checkUtf8(text) {

};
// If encodeInto is available, use it. Otherwise, configureTextEncoding
// fills in a slower fallback that uses encodeUtf8.
if (textEncoder.encodeInto) {
config.encodeUtf8Into = textEncoder.encodeInto.bind(textEncoder);
}
// Native String.prototype.isWellFormed, if the runtime provides it.
const nativeStringIsWellFormed = String.prototype.isWellFormed;
if (nativeStringIsWellFormed) {
config.checkUtf8 = (text) => {
return nativeStringIsWellFormed.call(text);
};
}
configureTextEncoding(config);
}
return globalThis[symbol];
return globals[symbol];
}
/**
* Simplistic polyfill for encodeUtf8Into.
*
* @private
*/
function emulateEncodeInto(encodeUtf8) {
return (text, dest) => {
const bytes = encodeUtf8(text);
dest.set(bytes);
return { written: bytes.byteLength };
};
}
/**
* Read a 64 bit varint as two JS numbers.
*
* Returns tuple:
* [0]: low bits
* [1]: high bits
* Stores the low and high words on the reader.
*

@@ -12,3 +10,3 @@ * Copyright 2008 Google Inc. All rights reserved.

*/
export declare function varint64read<T extends ReaderLike>(this: T): [number, number];
export declare function varint64read<T extends ReaderLike>(this: T): void;
/**

@@ -69,4 +67,6 @@ * Write a 64 bit varint, given as two JS numbers, to the given bytes array.

len: number;
varint64Lo: number;
varint64Hi: number;
assertBounds(): void;
};
export {};

@@ -45,5 +45,3 @@ "use strict";

*
* Returns tuple:
* [0]: low bits
* [1]: high bits
* Stores the low and high words on the reader.
*

@@ -55,27 +53,38 @@ * Copyright 2008 Google Inc. All rights reserved.

function varint64read() {
let lowBits = 0;
let highBits = 0;
const buf = this.buf;
let pos = this.pos;
let lo = 0;
let hi = 0;
for (let shift = 0; shift < 28; shift += 7) {
let b = this.buf[this.pos++];
lowBits |= (b & 0x7f) << shift;
const b = buf[pos++];
lo |= (b & 0x7f) << shift;
if ((b & 0x80) == 0) {
this.pos = pos;
this.assertBounds();
return [lowBits, highBits];
this.varint64Lo = lo;
this.varint64Hi = hi;
return;
}
}
let middleByte = this.buf[this.pos++];
const middleByte = buf[pos++];
// last four bits of the first 32 bit number
lowBits |= (middleByte & 0x0f) << 28;
lo |= (middleByte & 0x0f) << 28;
// 3 upper bits are part of the next 32 bit number
highBits = (middleByte & 0x70) >> 4;
hi = (middleByte & 0x70) >> 4;
if ((middleByte & 0x80) == 0) {
this.pos = pos;
this.assertBounds();
return [lowBits, highBits];
this.varint64Lo = lo;
this.varint64Hi = hi;
return;
}
for (let shift = 3; shift <= 31; shift += 7) {
let b = this.buf[this.pos++];
highBits |= (b & 0x7f) << shift;
const b = buf[pos++];
hi |= (b & 0x7f) << shift;
if ((b & 0x80) == 0) {
this.pos = pos;
this.assertBounds();
return [lowBits, highBits];
this.varint64Lo = lo;
this.varint64Hi = hi;
return;
}

@@ -268,2 +277,6 @@ }

function varint32write(value, bytes) {
if (value >>> 0 < 0x80) {
bytes.push(value);
return;
}
if (value >= 0) {

@@ -292,10 +305,10 @@ // write value as varint 32

let b = this.buf[this.pos++];
let result = b & 0x7f;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();
return result;
return b;
}
let result = b & 0x7f;
b = this.buf[this.pos++];
result |= (b & 0x7f) << 7;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();

@@ -306,3 +319,3 @@ return result;

result |= (b & 0x7f) << 14;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();

@@ -313,3 +326,3 @@ return result;

result |= (b & 0x7f) << 21;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();

@@ -323,7 +336,6 @@ return result;

b = this.buf[this.pos++];
if ((b & 0x80) != 0)
if ((b & 0x80) !== 0)
throw new Error("invalid varint");
this.assertBounds();
// Result can have 32 bits, convert it to unsigned
return result >>> 0;
}

@@ -53,15 +53,15 @@ "use strict";

}
const wrapperTypeNames = /*@__PURE__*/ new Set([
"google.protobuf.DoubleValue",
"google.protobuf.FloatValue",
"google.protobuf.Int64Value",
"google.protobuf.UInt64Value",
"google.protobuf.Int32Value",
"google.protobuf.UInt32Value",
"google.protobuf.BoolValue",
"google.protobuf.StringValue",
"google.protobuf.BytesValue",
]);
function isWrapperTypeName(name) {
return (name.startsWith("google.protobuf.") &&
[
"DoubleValue",
"FloatValue",
"Int64Value",
"UInt64Value",
"Int32Value",
"UInt32Value",
"BoolValue",
"StringValue",
"BytesValue",
].includes(name.substring(16)));
return wrapperTypeNames.has(name);
}

@@ -18,3 +18,2 @@ // Copyright 2021-2026 Buf Technologies, Inc.

import { isObject } from "./reflect/guard.js";
import { unsafeGet, unsafeOneofCase, unsafeSet } from "./reflect/unsafe.js";
import { isWrapperDesc } from "./wkt/wrappers.js";

@@ -37,77 +36,197 @@ // bootstrap-inject google.protobuf.Edition.EDITION_PROTO3: const $name = $number;

}
const message = createZeroMessage(schema);
if (init !== undefined) {
initMessage(schema, message, init);
return compiledCreate(schema)(init);
}
const compiledCreates = new WeakMap();
/**
* Return the compiled create function for a message, compiling it on first use. */
function compiledCreate(desc) {
let compiled = compiledCreates.get(desc);
if (compiled === undefined) {
compiled = compileCreate(desc);
compiledCreates.set(desc, compiled);
}
return message;
return compiled;
}
/** Singular field: scalar, enum, or message. */
const INIT_SINGULAR = 0;
/** List field: a zero message has a fresh empty array. */
const INIT_LIST = 1;
/** Map field: a zero message has a fresh empty object. */
const INIT_MAP = 2;
/** Oneof group: the ADT is always stored, cases convert by case name. */
const INIT_ONEOF = 3;
/* Compile the create function for this message type. */
function compileCreate(desc) {
const typeName = desc.typeName;
const { properties, prototype } = compileInitMessage(desc);
return (init) => {
let message;
if (prototype !== undefined) {
message = Object.create(prototype);
message.$typeName = typeName;
}
else {
message = { $typeName: typeName };
}
for (let i = 0; i < properties.length; i++) {
const property = properties[i];
const name = property.name;
const initValue = init === null || init === void 0 ? void 0 : init[name];
switch (property.kind) {
case INIT_SINGULAR:
if (initValue != null) {
message[name] =
property.convert !== undefined
? property.convert(initValue)
: initValue;
}
else if (property.constant !== undefined) {
message[name] = property.constant;
}
break;
case INIT_LIST:
message[name] =
property.convert !== undefined && Array.isArray(initValue)
? initValue.map(property.convert)
: (initValue !== null && initValue !== void 0 ? initValue : []);
break;
case INIT_MAP:
// Object.create(null) would be desirable for the fresh map, but is
// unsupported by React:
// https://react.dev/reference/react/use-server#serializable-parameters-and-return-values
if (property.convert === undefined || !isObject(initValue)) {
message[name] = initValue !== null && initValue !== void 0 ? initValue : {};
}
else {
const converted = {};
const keys = Object.keys(initValue);
for (let k = 0; k < keys.length; k++) {
converted[keys[k]] = property.convert(initValue[keys[k]]);
}
message[name] = converted;
}
break;
case INIT_ONEOF: {
const oneofValue = initValue;
if ((oneofValue === null || oneofValue === void 0 ? void 0 : oneofValue.case) != null) {
const convert = property.convert.get(oneofValue.case);
if (convert !== undefined) {
message[name] = {
case: oneofValue.case,
value: convert(oneofValue.value),
};
break;
}
}
message[name] = { case: undefined };
break;
}
}
}
return message;
};
}
/**
* Sets field values from a MessageInitShape on a zero message.
* Classify every member once, so that creating a message is a walk over a
* compact list instead of a walk over the descriptor.
*/
function initMessage(messageDesc, message, init) {
for (const member of messageDesc.members) {
let value = init[member.localName];
if (value == null) {
// intentionally ignore undefined and null
function compileInitMessage(desc) {
var _a, _b;
const properties = [];
const prototype = {};
const usePrototype = needsPrototypeChain(desc);
for (const member of desc.members) {
const name = member.localName;
if (member.kind == "oneof") {
properties.push({
name,
kind: INIT_ONEOF,
constant: undefined,
convert: compileConvertOneof(member),
});
continue;
}
let field;
if (member.kind == "oneof") {
const oneofField = unsafeOneofCase(init, member);
if (!oneofField) {
continue;
switch (member.fieldKind) {
case "message": {
// Singular message fields are absent from a zero message.
properties.push({
name,
kind: INIT_SINGULAR,
constant: undefined,
convert: compileConvertMessage(member),
});
break;
}
field = oneofField;
value = unsafeGet(init, oneofField);
}
else {
field = member;
}
switch (field.fieldKind) {
case "message":
value = toMessage(field, value);
case "list": {
properties.push({
name,
kind: INIT_LIST,
constant: undefined,
convert: member.listKind == "message"
? ((_a = compileConvertMessage(member)) !== null && _a !== void 0 ? _a : ((value) => value))
: member.scalar == ScalarType.BYTES
? toU8Arr
: undefined,
});
break;
case "scalar":
value = initScalar(field, value);
}
case "map": {
properties.push({
name,
kind: INIT_MAP,
constant: undefined,
convert: member.mapKind == "message"
? ((_b = compileConvertMessage(member)) !== null && _b !== void 0 ? _b : ((value) => value))
: member.scalar == ScalarType.BYTES
? toU8Arr
: undefined,
});
break;
case "list":
value = initList(field, value);
}
default: {
const zeroValue = createZeroValue(member);
properties.push({
name,
kind: INIT_SINGULAR,
constant: member.presence == IMPLICIT ? zeroValue : undefined,
convert: member.fieldKind == "scalar" && member.scalar == ScalarType.BYTES
? toU8Arr
: undefined,
});
if (usePrototype) {
prototype[name] = zeroValue;
}
break;
case "map":
value = initMap(field, value);
break;
}
}
unsafeSet(message, field, value);
}
return message;
return {
properties,
prototype: usePrototype ? prototype : undefined,
};
}
function initScalar(field, value) {
if (field.scalar == ScalarType.BYTES) {
return toU8Arr(value);
}
return value;
}
function initMap(field, value) {
if (isObject(value)) {
if (field.scalar == ScalarType.BYTES) {
return convertObjectValues(value, toU8Arr);
/**
* Compile the conversion of each case of a oneof group, keyed by case name.
*/
function compileConvertOneof(oneof) {
const converters = new Map();
for (const field of oneof.fields) {
let convert;
if (field.fieldKind == "message") {
convert = compileConvertMessage(field);
}
if (field.mapKind == "message") {
return convertObjectValues(value, (val) => toMessage(field, val));
else if (field.fieldKind == "scalar" &&
field.scalar == ScalarType.BYTES) {
convert = toU8Arr;
}
converters.set(field.localName, convert !== null && convert !== void 0 ? convert : ((value) => value));
}
return value;
return converters;
}
function initList(field, value) {
if (Array.isArray(value)) {
if (field.scalar == ScalarType.BYTES) {
return value.map(toU8Arr);
}
if (field.listKind == "message") {
return value.map((item) => toMessage(field, item));
}
}
return value;
}
function toMessage(field, value) {
/**
* Compile the conversion of an init value for a message field, a message
* list item, or a message map value. Returns undefined if values are used
* as-is.
*/
function compileConvertMessage(field) {
if (field.fieldKind == "message" &&

@@ -118,16 +237,23 @@ !field.oneof &&

// a singular field that is not part of a oneof group.
return initScalar(field.message.fields[0], value);
return field.message.fields[0].scalar == ScalarType.BYTES
? toU8Arr
: undefined;
}
if (isObject(value)) {
if (field.message.typeName == "google.protobuf.Struct" &&
field.parent.typeName !== "google.protobuf.Value") {
// google.protobuf.Struct is represented with JsonObject when used in a
// field, except when used in google.protobuf.Value.
if (field.message.typeName == "google.protobuf.Struct" &&
field.parent.typeName !== "google.protobuf.Value") {
// google.protobuf.Struct is represented with JsonObject when used in a
// field, except when used in google.protobuf.Value.
return undefined;
}
const messageDesc = field.message;
// Resolved on first use, not here: the message type can be this very field's
// parent, whose create function is still being compiled.
let compiled;
return (value) => {
if (!isObject(value) || isMessage(value, messageDesc)) {
return value;
}
if (!isMessage(value, field.message)) {
return create(field.message, value);
}
}
return value;
compiled !== null && compiled !== void 0 ? compiled : (compiled = compiledCreate(messageDesc));
return compiled(value);
};
}

@@ -138,80 +264,3 @@ // converts any ArrayLike<number> to Uint8Array if necessary.

}
function convertObjectValues(obj, fn) {
const ret = {};
for (const entry of Object.entries(obj)) {
ret[entry[0]] = fn(entry[1]);
}
return ret;
}
const tokenZeroMessageField = Symbol();
const messagePrototypes = new WeakMap();
/**
* Create a zero message.
*/
function createZeroMessage(desc) {
let msg;
if (!needsPrototypeChain(desc)) {
msg = {
$typeName: desc.typeName,
};
for (const member of desc.members) {
if (member.kind == "oneof" || member.presence == IMPLICIT) {
msg[member.localName] = createZeroField(member);
}
}
}
else {
// Support default values and track presence via the prototype chain
const cached = messagePrototypes.get(desc);
let prototype;
let members;
if (cached) {
({ prototype, members } = cached);
}
else {
prototype = {};
members = new Set();
for (const member of desc.members) {
if (member.kind == "oneof") {
// we can only put immutable values on the prototype,
// oneof ADTs are mutable
continue;
}
if (member.fieldKind != "scalar" && member.fieldKind != "enum") {
// only scalar and enum values are immutable, map, list, and message
// are not
continue;
}
if (member.presence == IMPLICIT) {
// implicit presence tracks field presence by zero values - e.g. 0, false, "", are unset, 1, true, "x" are set.
// message, map, list fields are mutable, and also have IMPLICIT presence.
continue;
}
members.add(member);
prototype[member.localName] = createZeroField(member);
}
messagePrototypes.set(desc, { prototype, members });
}
msg = Object.create(prototype);
msg.$typeName = desc.typeName;
for (const member of desc.members) {
if (members.has(member)) {
continue;
}
if (member.kind == "field") {
if (member.fieldKind == "message") {
continue;
}
if (member.fieldKind == "scalar" || member.fieldKind == "enum") {
if (member.presence != IMPLICIT) {
continue;
}
}
}
msg[member.localName] = createZeroField(member);
}
}
return msg;
}
/**
* Do we need the prototype chain to track field presence?

@@ -235,18 +284,6 @@ */

/**
* Returns a zero value for oneof groups, and for every field kind except
* messages. Scalar and enum fields can have default values.
* Returns the zero value for a scalar or enum field. Scalar and enum fields
* can have default values.
*/
function createZeroField(field) {
if (field.kind == "oneof") {
return { case: undefined };
}
if (field.fieldKind == "list") {
return [];
}
if (field.fieldKind == "map") {
return {}; // Object.create(null) would be desirable here, but is unsupported by react https://react.dev/reference/react/use-server#serializable-parameters-and-return-values
}
if (field.fieldKind == "message") {
return tokenZeroMessageField;
}
function createZeroValue(field) {
const defaultValue = field.getDefaultValue();

@@ -253,0 +290,0 @@ if (defaultValue !== undefined) {

@@ -16,3 +16,6 @@ // Copyright 2021-2026 Buf Technologies, Inc.

import { scalarZeroValue } from "./reflect/scalar.js";
import { reflect } from "./reflect/reflect.js";
import { FieldError } from "./reflect/error.js";
import { unsafeLocal } from "./reflect/unsafe.js";
import { localMessageMapper } from "./reflect/message.js";
import { create } from "./create.js";
import { BinaryReader, WireType } from "./wire/binary-encoding.js";

@@ -30,5 +33,5 @@ import { varint32write } from "./wire/varint.js";

export function fromBinary(schema, bytes, options) {
const msg = reflect(schema, undefined, false);
readMessage(msg, new BinaryReader(bytes), makeReadContext(options), false, bytes.byteLength);
return msg.message;
const message = create(schema);
compiledReader(schema).read(message, new BinaryReader(bytes), makeReadContext(options), bytes.byteLength);
return message;
}

@@ -45,48 +48,103 @@ /**

export function mergeFromBinary(schema, target, bytes, options) {
readMessage(reflect(schema, target, false), new BinaryReader(bytes), makeReadContext(options), false, bytes.byteLength);
if (target.$typeName !== schema.typeName &&
schema.fields.length > 0) {
throw new FieldError(schema.fields[0], `cannot use ${schema.fields[0]} with message ${target.$typeName}`, "ForeignFieldError");
}
compiledReader(schema).read(target, new BinaryReader(bytes), makeReadContext(options), bytes.byteLength);
return target;
}
const compiledReaders = new WeakMap();
/**
* If `delimited` is false, read the length given in `lengthOrDelimitedFieldNo`.
*
* If `delimited` is true, read until an EndGroup tag. `lengthOrDelimitedFieldNo`
* is the expected field number.
*
* @private
* Return the compiled decoder for a message, compiling it on first use.
*/
function readMessage(message, reader, ctx, delimited, lengthOrDelimitedFieldNo) {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${message.desc} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
function compiledReader(desc) {
let compiled = compiledReaders.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
const end = delimited ? reader.len : reader.pos + lengthOrDelimitedFieldNo;
let fieldNo;
let wireType;
const unknownFields = (_a = message.getUnknown()) !== null && _a !== void 0 ? _a : [];
while (reader.pos < end) {
[fieldNo, wireType] = reader.tag();
if (delimited && wireType == WireType.EndGroup) {
break;
return compiled;
}
function compileMessage(desc) {
const descString = String(desc);
const fieldReaders = new Map();
const compiled = {
read: compileMessageReader(descString, fieldReaders),
readGroup: compileGroupReader(descString, fieldReaders),
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledReaders.set(desc, compiled);
for (const field of desc.fields) {
fieldReaders.set(field.number, compileFieldReader(field));
}
return compiled;
}
/**
* Create a decoder for a length-prefixed message body, dispatching wire
* records to the compiled field decoders by field number.
*/
function compileMessageReader(descString, fieldReaders) {
return (message, reader, ctx, length) => {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
const field = message.findNumber(fieldNo);
if (!field) {
// Use remaining recursion budget for skipping nested groups
const recursionLimit = ctx.recursionLimit - ctx.depth;
const data = reader.skip(wireType, fieldNo, recursionLimit);
if (ctx.readUnknownFields) {
unknownFields.push({ no: fieldNo, wireType, data });
const end = reader.pos + length;
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
while (reader.pos < end) {
const [fieldNo, wireType] = reader.tag();
const fieldReader = fieldReaders.get(fieldNo);
if (fieldReader === undefined) {
// Use remaining recursion budget for skipping nested groups
const data = reader.skip(wireType, fieldNo, ctx.recursionLimit - ctx.depth);
if (ctx.readUnknownFields) {
unknownFields.push({ no: fieldNo, wireType, data });
}
continue;
}
continue;
fieldReader(message, reader, ctx, wireType);
}
readField(message, reader, field, wireType, ctx);
}
if (delimited) {
if (wireType != WireType.EndGroup || fieldNo !== lengthOrDelimitedFieldNo) {
if (unknownFields.length > 0) {
message.$unknown = unknownFields;
}
ctx.depth--;
};
}
/**
* Create a decoder for a message with the delimited encoding (group),
* reading until the EndGroup tag, like compileMessageReader.
*/
function compileGroupReader(descString, fieldReaders) {
return (message, reader, ctx, fieldNo) => {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
let recordFieldNo;
let wireType;
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
while (reader.pos < reader.len) {
[recordFieldNo, wireType] = reader.tag();
if (wireType == WireType.EndGroup) {
break;
}
const fieldReader = fieldReaders.get(recordFieldNo);
if (fieldReader === undefined) {
// Use remaining recursion budget for skipping nested groups
const data = reader.skip(wireType, recordFieldNo, ctx.recursionLimit - ctx.depth);
if (ctx.readUnknownFields) {
unknownFields.push({ no: recordFieldNo, wireType, data });
}
continue;
}
fieldReader(message, reader, ctx, wireType);
}
if (wireType != WireType.EndGroup || recordFieldNo !== fieldNo) {
throw new Error("invalid end group tag");
}
}
if (unknownFields.length > 0) {
message.setUnknown(unknownFields);
}
ctx.depth--;
if (unknownFields.length > 0) {
message.$unknown = unknownFields;
}
ctx.depth--;
};
}

@@ -97,149 +155,259 @@ /**

export function readField(message, reader, field, wireType, ctx) {
var _a;
compileFieldReader(field)(message[unsafeLocal], reader, ctx, wireType);
}
function compileFieldReader(field) {
switch (field.fieldKind) {
case "scalar":
message.set(field, readScalar(reader, field.scalar, field.utf8Validation));
break;
return compileScalarFieldReader(field);
case "enum":
const val = readScalar(reader, ScalarType.INT32);
if (field.enum.open) {
message.set(field, val);
}
else {
const ok = field.enum.values.some((v) => v.number === val);
if (ok) {
message.set(field, val);
}
else if (ctx.readUnknownFields) {
const bytes = [];
varint32write(val, bytes);
const unknownFields = (_a = message.getUnknown()) !== null && _a !== void 0 ? _a : [];
unknownFields.push({
no: field.number,
wireType,
data: new Uint8Array(bytes),
});
message.setUnknown(unknownFields);
}
}
break;
return compileEnumFieldReader(field);
case "message":
message.set(field, readMessageField(reader, ctx, field, message.get(field)));
break;
return compileMessageFieldReader(field);
case "list":
readListField(reader, wireType, message.get(field), ctx);
break;
return compileListFieldReader(field);
case "map":
readMapEntry(reader, message.get(field), ctx);
break;
return compileMapFieldReader(field);
}
}
// Read a map field, expecting key field = 1, value field = 2
function readMapEntry(reader, map, ctx) {
const field = map.field();
let key;
let val;
// Read the length of the map entry, which is a varint.
const len = reader.uint32();
// WARNING: Calculate end AFTER advancing reader.pos (above), so that
// reader.pos is at the start of the map entry.
const end = reader.pos + len;
while (reader.pos < end) {
const [fieldNo] = reader.tag();
switch (fieldNo) {
case 1:
key = readScalar(reader, field.mapKey, field.utf8Validation);
break;
case 2:
switch (field.mapKind) {
case "scalar":
val = readScalar(reader, field.scalar, field.utf8Validation);
break;
case "enum":
val = reader.int32();
break;
case "message":
val = readMessageField(reader, ctx, field);
break;
}
break;
function compileScalarFieldReader(field) {
const readScalar = compileScalarReader(field.scalar, field.utf8Validation, field.longAsString);
const localName = field.localName;
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, reader) => {
message[oneofLocalName] = {
case: localName,
value: readScalar(reader),
};
};
}
return (message, reader) => {
message[localName] = readScalar(reader);
};
}
function compileEnumFieldReader(field) {
var _a;
const localName = field.localName;
const oneofLocalName = (_a = field.oneof) === null || _a === void 0 ? void 0 : _a.localName;
if (field.enum.open) {
if (oneofLocalName !== undefined) {
return (message, reader) => {
message[oneofLocalName] = { case: localName, value: reader.int32() };
};
}
return (message, reader) => {
message[localName] = reader.int32();
};
}
if (key === undefined) {
key = scalarZeroValue(field.mapKey, false);
}
if (val === undefined) {
switch (field.mapKind) {
case "scalar":
val = scalarZeroValue(field.scalar, false);
break;
case "enum":
val = field.enum.values[0].number;
break;
case "message":
val = reflect(field.message, undefined, false);
break;
// Closed enums: unknown values are stored as unknown fields.
const values = field.enum.values;
const fieldNo = field.number;
return (message, reader, ctx, wireType) => {
var _a;
const val = reader.int32();
if (values.some((v) => v.number === val)) {
if (oneofLocalName !== undefined) {
message[oneofLocalName] = { case: localName, value: val };
}
else {
message[localName] = val;
}
}
else if (ctx.readUnknownFields) {
const bytes = [];
varint32write(val, bytes);
const unknownFields = (_a = message.$unknown) !== null && _a !== void 0 ? _a : [];
unknownFields.push({
no: fieldNo,
wireType,
data: new Uint8Array(bytes),
});
message.$unknown = unknownFields;
}
};
}
function compileMessageFieldReader(field) {
const localName = field.localName;
const { toMessage, toLocal } = localMessageMapper(field);
const readChild = compileChildReader(field);
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, reader, ctx) => {
const oneof = message[oneofLocalName];
const child = toMessage(oneof.case === localName ? oneof.value : undefined);
readChild(child, reader, ctx);
message[oneofLocalName] = { case: localName, value: toLocal(child) };
};
}
map.set(key, val);
return (message, reader, ctx) => {
const child = toMessage(message[localName]);
readChild(child, reader, ctx);
message[localName] = toLocal(child);
};
}
function readListField(reader, wireType, list, ctx) {
var _a;
const field = list.field();
if (field.listKind === "message") {
list.add(readMessageField(reader, ctx, field));
return;
/**
* Compile a decoder for the wire format of a message field, honoring the
* delimited encoding of the field.
*/
function compileChildReader(field) {
const compiledChild = compiledReader(field.message);
if (field.delimitedEncoding) {
const fieldNo = field.number;
return (child, reader, ctx) => compiledChild.readGroup(child, reader, ctx, fieldNo);
}
const scalarType = (_a = field.scalar) !== null && _a !== void 0 ? _a : ScalarType.INT32;
const packed = wireType == WireType.LengthDelimited &&
scalarType != ScalarType.STRING &&
scalarType != ScalarType.BYTES;
if (!packed) {
list.add(readScalar(reader, scalarType, field.utf8Validation));
return;
return (child, reader, ctx) => compiledChild.read(child, reader, ctx, reader.uint32());
}
function compileListFieldReader(field) {
const localName = field.localName;
if (field.listKind == "message") {
const { toMessage, toLocal } = localMessageMapper(field);
const readChild = compileChildReader(field);
return (message, reader, ctx) => {
const child = toMessage(undefined);
readChild(child, reader, ctx);
message[localName].push(toLocal(child));
};
}
const e = reader.uint32() + reader.pos;
while (reader.pos < e) {
list.add(readScalar(reader, scalarType, field.utf8Validation));
const scalarType = field.listKind == "enum" ? ScalarType.INT32 : field.scalar;
const longAsString = field.listKind == "scalar" ? field.longAsString : false;
const readScalar = compileScalarReader(scalarType, field.utf8Validation, longAsString);
const packedPossible = scalarType != ScalarType.STRING && scalarType != ScalarType.BYTES;
return (message, reader, ctx, wireType) => {
const items = message[localName];
if (wireType == WireType.LengthDelimited && packedPossible) {
const end = reader.uint32() + reader.pos;
while (reader.pos < end) {
items.push(readScalar(reader));
}
}
else {
items.push(readScalar(reader));
}
};
}
function compileMapFieldReader(field) {
const localName = field.localName;
const readKey = compileScalarReader(field.mapKey, field.utf8Validation, false);
const keyZero = scalarZeroValue(field.mapKey, false);
let readValue;
let valueDefault;
switch (field.mapKind) {
case "scalar": {
const scalar = field.scalar;
const readScalar = compileScalarReader(scalar, field.utf8Validation, false);
readValue = (reader) => readScalar(reader);
// Bytes zero values are created per entry, so that entries do not share
// one instance.
if (scalar == ScalarType.BYTES) {
valueDefault = () => new Uint8Array(0);
}
else {
const zero = scalarZeroValue(scalar, false);
valueDefault = () => zero;
}
break;
}
case "enum": {
const zero = field.enum.values[0].number;
readValue = (reader) => reader.int32();
valueDefault = () => zero;
break;
}
case "message": {
const { toMessage, toLocal } = localMessageMapper(field);
const readChild = compiledReader(field.message).read;
readValue = (reader, ctx) => {
const child = toMessage(undefined);
readChild(child, reader, ctx, reader.uint32());
return toLocal(child);
};
valueDefault = () => toLocal(toMessage(undefined));
break;
}
}
return (message, reader, ctx) => {
const record = message[localName];
let key;
let val;
// Read the length of the map entry, which is a varint.
const len = reader.uint32();
// Calculate end AFTER advancing reader.pos (above), so that reader.pos is
// at the start of the map entry.
const end = reader.pos + len;
while (reader.pos < end) {
// Map entries have the key in field 1, and the value in field 2.
const [fieldNo] = reader.tag();
switch (fieldNo) {
case 1:
key = readKey(reader);
break;
case 2:
val = readValue(reader, ctx);
break;
}
}
if (key === undefined) {
key = keyZero;
}
if (val === undefined) {
val = valueDefault();
}
// Object property keys are always strings or symbols. Assigning with a
// boolean, number, or bigint key implicitly converts it to a string.
record[key] = val;
};
}
function readMessageField(reader, ctx, field, mergeMessage) {
const delimited = field.delimitedEncoding;
const message = mergeMessage !== null && mergeMessage !== void 0 ? mergeMessage : reflect(field.message, undefined, false);
readMessage(message, reader, ctx, delimited, delimited ? field.number : reader.uint32());
return message;
}
function readScalar(reader, type, validateUtf8 = false) {
/**
* Returns a reader for a scalar value. For 64-bit integers, BinaryReader
* already returns the local representation (bigint or string), so, unlike in
* the reflection layer, no validation is needed here.
*/
function compileScalarReader(type, utf8Validation, longAsString) {
switch (type) {
case ScalarType.STRING:
return reader.string(validateUtf8);
return (reader) => reader.string(utf8Validation);
case ScalarType.BOOL:
return reader.bool();
return (reader) => reader.bool();
case ScalarType.DOUBLE:
return reader.double();
return (reader) => reader.double();
case ScalarType.FLOAT:
return reader.float();
return (reader) => reader.float();
case ScalarType.INT32:
return reader.int32();
return (reader) => reader.int32();
case ScalarType.INT64:
return reader.int64();
if (longAsString) {
return (reader) => String(reader.int64());
}
return (reader) => reader.int64();
case ScalarType.UINT64:
return reader.uint64();
if (longAsString) {
return (reader) => String(reader.uint64());
}
return (reader) => reader.uint64();
case ScalarType.FIXED64:
return reader.fixed64();
if (longAsString) {
return (reader) => String(reader.fixed64());
}
return (reader) => reader.fixed64();
case ScalarType.BYTES:
return reader.bytes();
return (reader) => reader.bytes();
case ScalarType.FIXED32:
return reader.fixed32();
return (reader) => reader.fixed32();
case ScalarType.SFIXED32:
return reader.sfixed32();
return (reader) => reader.sfixed32();
case ScalarType.SFIXED64:
return reader.sfixed64();
if (longAsString) {
return (reader) => String(reader.sfixed64());
}
return (reader) => reader.sfixed64();
case ScalarType.SINT64:
return reader.sint64();
if (longAsString) {
return (reader) => String(reader.sint64());
}
return (reader) => reader.sint64();
case ScalarType.UINT32:
return reader.uint32();
return (reader) => reader.uint32();
case ScalarType.SINT32:
return reader.sint32();
return (reader) => reader.sint32();
}
}

@@ -17,9 +17,14 @@ // Copyright 2021-2026 Buf Technologies, Inc.

import { create } from "./create.js";
import { reflect } from "./reflect/reflect.js";
import { FieldError, isFieldError } from "./reflect/error.js";
import { formatVal } from "./reflect/reflect-check.js";
import { formatVal, reasonSingular, checkScalarValue, } from "./reflect/reflect-check.js";
import { protoSnakeCase } from "./reflect/names.js";
import { scalarZeroValue } from "./reflect/scalar.js";
import { unsafeLocal } from "./reflect/unsafe.js";
import { localMessageMapper } from "./reflect/message.js";
import { base64Decode } from "./wire/base64-encoding.js";
import { hasCustomJsonRepresentation, isWrapperDesc, anyPack, ListValueSchema, NullValue, StructSchema, ValueSchema, } from "./wkt/index.js";
import { createExtensionContainer, setExtension } from "./extensions.js";
import { durationSecondsMax, durationSecondsMin, timestampMsMax, timestampMsMin, } from "./wkt/json.js";
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.IMPLICIT: const $name = $number;
const IMPLICIT = 2;
function makeReadContext(options) {

@@ -58,16 +63,5 @@ return Object.assign(Object.assign({ ignoreUnknownFields: false, recursionLimit: 100 }, options), { depth: 0 });

export function fromJson(schema, json, options) {
const msg = reflect(schema);
try {
readMessage(msg, json, makeReadContext(options));
}
catch (e) {
if (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;
const message = create(schema);
readMessage(schema, message, json, options);
return message;
}

@@ -87,4 +81,16 @@ /**

export function mergeFromJson(schema, target, json, options) {
if (target.$typeName !== schema.typeName &&
schema.fields.length > 0) {
throw new FieldError(schema.fields[0], `cannot use ${schema.fields[0]} with message ${target.$typeName}`, "ForeignFieldError");
}
readMessage(schema, target, json, options);
return target;
}
/**
* Run the compiled decoder for the message, wrapping FieldErrors with the
* standard error message.
*/
function readMessage(schema, message, json, options) {
try {
readMessage(reflect(schema, target), json, makeReadContext(options));
compiledReader(schema)(message, json, makeReadContext(options));
}

@@ -100,3 +106,2 @@ catch (e) {

}
return target;
}

@@ -107,3 +112,5 @@ /**

export function enumFromJson(descEnum, json) {
return readEnum(descEnum, json, false);
// With ignoreUnknownFields false, the converter never returns the token
// for ignored unknown enum values.
return compileEnumConverter(descEnum)(json, false);
}

@@ -116,214 +123,506 @@ /**

}
const messageJsonFields = new WeakMap();
function getJsonField(desc, jsonKey) {
var _a;
if (!messageJsonFields.has(desc)) {
const jsonNames = new Map();
for (const field of desc.fields) {
jsonNames.set(field.name, field).set(field.jsonName, field);
}
messageJsonFields.set(desc, jsonNames);
const compiledReaders = new WeakMap();
/**
* Return the compiled decoder for a message, compiling it on first use.
*/
function compiledReader(desc) {
let compiled = compiledReaders.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
return (_a = messageJsonFields.get(desc)) === null || _a === void 0 ? void 0 : _a.get(jsonKey);
return compiled;
}
function readMessage(msg, json, ctx) {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${msg.desc} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
function compileMessage(desc) {
const descString = String(desc);
const readWkt = compileWkt(desc);
if (readWkt !== undefined) {
// All message decoders count against the recursion limit, including
// well-known types with a custom JSON representation.
const compiled = (message, json, ctx) => {
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
readWkt(message, json, ctx);
ctx.depth--;
};
compiledReaders.set(desc, compiled);
return compiled;
}
if (tryWktFromJson(msg, json, ctx)) {
ctx.depth--;
return;
}
if (json == null || Array.isArray(json) || typeof json != "object") {
throw new Error(`cannot decode ${msg.desc} from JSON: ${formatVal(json)}`);
}
const oneofSeen = new Map();
const fieldSeen = new Set();
for (const [jsonKey, jsonValue] of Object.entries(json)) {
const field = getJsonField(msg.desc, jsonKey);
if (field) {
if (fieldSeen.has(field)) {
// The same field may be set by its proto name and its JSON name, or by
// a duplicate or unicode-escaped key that JSON.parse already collapsed.
// Checked before the null-skip below so that a null entry still counts.
throw new FieldError(field, "set multiple times");
const typeName = desc.typeName;
// Fields are looked up by their proto name and their JSON name.
const fieldsByJsonKey = new Map();
const compiled = (message, json, ctx) => {
var _a;
if (++ctx.depth > ctx.recursionLimit) {
throw new Error(`cannot decode ${descString} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
}
if (json == null || Array.isArray(json) || typeof json != "object") {
throw new Error(`cannot decode ${descString} from JSON: ${formatVal(json)}`);
}
const oneofSeen = new Map();
const fieldSeen = new Set();
const jsonKeys = Object.keys(json);
for (let i = 0; i < jsonKeys.length; i++) {
const jsonKey = jsonKeys[i];
const jsonValue = json[jsonKey];
const entry = fieldsByJsonKey.get(jsonKey);
if (entry !== undefined) {
const field = entry.field;
if (fieldSeen.has(field)) {
// The same field may be set by its proto name and its JSON name, or by
// a duplicate or unicode-escaped key that JSON.parse already collapsed.
// Checked before the null-skip below so that a null entry still counts.
throw new FieldError(field, "set multiple times");
}
fieldSeen.add(field);
if (entry.oneofScalarNullSkip && jsonValue === null) {
continue;
}
if (entry.oneof) {
const seen = oneofSeen.get(entry.oneof);
if (seen !== undefined) {
throw new FieldError(entry.oneof, `oneof set multiple times by ${seen.name} and ${field.name}`);
}
oneofSeen.set(entry.oneof, field);
}
entry.read(message, jsonValue, ctx);
}
fieldSeen.add(field);
if (field.oneof && jsonValue === null && field.fieldKind == "scalar") {
// see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
continue;
}
if (field.oneof) {
const seen = oneofSeen.get(field.oneof);
if (seen !== undefined) {
throw new FieldError(field.oneof, `oneof set multiple times by ${seen.name} and ${field.name}`);
else {
const extension = jsonKey.startsWith("[") && jsonKey.endsWith("]")
? (_a = ctx.registry) === null || _a === void 0 ? void 0 : _a.getExtension(jsonKey.substring(1, jsonKey.length - 1))
: undefined;
if ((extension === null || extension === void 0 ? void 0 : extension.extendee.typeName) == typeName) {
const [container, field, get] = createExtensionContainer(extension);
compileFieldReader(field)(container[unsafeLocal], jsonValue, ctx);
setExtension(message, extension, get());
}
oneofSeen.set(field.oneof, field);
if (extension === undefined && !ctx.ignoreUnknownFields) {
throw new Error(`cannot decode ${descString} from JSON: key "${jsonKey}" is unknown`);
}
}
readField(msg, field, jsonValue, ctx);
}
else {
let extension = undefined;
if (jsonKey.startsWith("[") &&
jsonKey.endsWith("]") &&
// biome-ignore lint/suspicious/noAssignInExpressions: no
(extension = (_a = ctx.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] = createExtensionContainer(extension);
readField(container, field, jsonValue, ctx);
setExtension(msg.message, extension, get());
ctx.depth--;
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledReaders.set(desc, compiled);
for (const field of desc.fields) {
const entry = {
read: compileFieldReader(field),
field,
oneof: field.oneof,
oneofScalarNullSkip: field.oneof !== undefined && field.fieldKind == "scalar",
};
fieldsByJsonKey.set(field.name, entry).set(field.jsonName, entry);
}
return compiled;
}
/**
* Compile a decoder for a well-known type with a custom JSON representation,
* or return undefined for other messages. The recursion limit is enforced by
* the caller.
*/
function compileWkt(desc) {
if (!desc.typeName.startsWith("google.protobuf.")) {
return undefined;
}
switch (desc.typeName) {
case "google.protobuf.Any":
return (message, json, ctx) => anyFromJson(message, json, ctx);
case "google.protobuf.Timestamp":
return (message, json) => timestampFromJson(message, json);
case "google.protobuf.Duration":
return (message, json) => durationFromJson(message, json);
case "google.protobuf.FieldMask":
return (message, json) => fieldMaskFromJson(message, json);
case "google.protobuf.Struct":
return (message, json, ctx) => structFromJson(message, json, ctx);
case "google.protobuf.Value":
return (message, json, ctx) => valueFromJson(message, json, ctx);
case "google.protobuf.ListValue":
return (message, json, ctx) => listValueFromJson(message, json, ctx);
default:
if (isWrapperDesc(desc)) {
const valueField = desc.fields[0];
const localName = valueField.localName;
const scalar = valueField.scalar;
const longAsString = valueField.longAsString;
const readScalar = compileScalarConverter(valueField);
return (message, json) => {
if (json === null) {
message[localName] = scalarZeroValue(scalar, longAsString);
}
else {
message[localName] = readScalar(json);
}
};
}
if (!extension && !ctx.ignoreUnknownFields) {
throw new Error(`cannot decode ${msg.desc} from JSON: key "${jsonKey}" is unknown`);
}
}
return undefined;
}
ctx.depth--;
}
function readField(msg, field, json, ctx) {
function compileFieldReader(field) {
switch (field.fieldKind) {
case "scalar":
readScalarField(msg, field, json);
break;
return compileScalarFieldReader(field);
case "enum":
readEnumField(msg, field, json, ctx);
break;
return compileEnumFieldReader(field);
case "message":
readMessageField(msg, field, json, ctx);
break;
return compileMessageFieldReader(field);
case "list":
readListField(msg.get(field), json, ctx);
break;
return compileListFieldReader(field);
case "map":
readMapField(msg.get(field), json, ctx);
break;
return compileMapFieldReader(field);
}
}
function readListOrMapItem(field, json, ctx) {
if (field.scalar && json !== null) {
return scalarFromJson(field, json);
function compileScalarFieldReader(field) {
const readScalar = compileScalarConverter(field);
const localName = field.localName;
if (field.oneof) {
// JSON null for a oneof scalar member is skipped by the message decoder.
const oneofLocalName = field.oneof.localName;
return (message, json) => {
message[oneofLocalName] = {
case: localName,
value: readScalar(json),
};
};
}
if (field.message && !isResetSentinelNullValue(field, json)) {
const msgValue = reflect(field.message);
readMessage(msgValue, json, ctx);
return msgValue;
const clear = compileClear(field);
return (message, json) => {
if (json === null) {
clear(message);
}
else {
message[localName] = readScalar(json);
}
};
}
/**
* Compile a function that resets the field to unset, mirroring the clear
* operation of the reflect API for fields that are not part of a oneof.
*/
function compileClear(field) {
const localName = field.localName;
if (field.presence != IMPLICIT) {
// Fields with explicit presence have properties on the prototype chain
// for default / zero values (except for proto3). By deleting their own
// property, the field is reset.
return (message) => {
delete message[localName];
};
}
if (field.enum && !isResetSentinelNullValue(field, json)) {
return readEnum(field.enum, json, ctx.ignoreUnknownFields);
if (field.fieldKind == "enum") {
const zero = field.enum.values[0].number;
return (message) => {
message[localName] = zero;
};
}
throw new FieldError(field, `${field.fieldKind === "list" ? "list item" : "map value"} must not be null`);
const scalar = field.scalar;
const longAsString = field.longAsString;
return (message) => {
message[localName] = scalarZeroValue(scalar, longAsString);
};
}
function readMapField(map, json, ctx) {
if (json === null) {
return;
function compileEnumFieldReader(field) {
const readEnumValue = compileEnumConverter(field.enum);
const checkEnum = compileEnumCheck(field.enum);
const localName = field.localName;
// Fields with enum google.protobuf.NullValue permit a Protobuf-serializable
// null; for all other enums, JSON null resets the field.
const nullResets = field.enum.typeName != "google.protobuf.NullValue";
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, json, ctx) => {
if (json === null && nullResets) {
const oneof = message[oneofLocalName];
if (oneof.case === localName) {
message[oneofLocalName] = { case: undefined };
}
return;
}
const value = readEnumValue(json, ctx.ignoreUnknownFields);
if (value === tokenIgnoredUnknownEnum) {
return;
}
const check = checkEnum(value);
if (check !== true) {
throw new FieldError(field, reasonSingular(field, value, check));
}
message[oneofLocalName] = { case: localName, value };
};
}
const field = map.field();
if (typeof json != "object" || Array.isArray(json)) {
throw new FieldError(field, "expected object, got " + formatVal(json));
}
const seen = new Set();
for (const [jsonMapKey, jsonMapValue] of Object.entries(json)) {
const key = mapKeyFromJson(field.mapKey, jsonMapKey);
if (seen.has(key)) {
throw new FieldError(field, `duplicate map key "${jsonMapKey}"`);
const clear = compileClear(field);
return (message, json, ctx) => {
if (json === null && nullResets) {
clear(message);
return;
}
seen.add(key);
const value = readListOrMapItem(field, jsonMapValue, ctx);
if (value !== tokenIgnoredUnknownEnum) {
map.set(key, value);
const value = readEnumValue(json, ctx.ignoreUnknownFields);
if (value === tokenIgnoredUnknownEnum) {
return;
}
}
const check = checkEnum(value);
if (check !== true) {
throw new FieldError(field, reasonSingular(field, value, check));
}
message[localName] = value;
};
}
function readListField(list, json, ctx) {
if (json === null) {
return;
function compileMessageFieldReader(field) {
const localName = field.localName;
const { toMessage, toLocal } = localMessageMapper(field);
const readChild = compiledReader(field.message);
// Fields with message google.protobuf.Value permit a Protobuf-serializable
// null; for all other messages, JSON null resets the field.
const nullResets = field.message.typeName != "google.protobuf.Value";
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (message, json, ctx) => {
const oneof = message[oneofLocalName];
if (json === null && nullResets) {
if (oneof.case === localName) {
message[oneofLocalName] = { case: undefined };
}
return;
}
const child = toMessage(oneof.case === localName ? oneof.value : undefined);
readChild(child, json, ctx);
message[oneofLocalName] = { case: localName, value: toLocal(child) };
};
}
const field = list.field();
if (!Array.isArray(json)) {
throw new FieldError(field, "expected Array, got " + formatVal(json));
}
for (const jsonItem of json) {
const value = readListOrMapItem(field, jsonItem, ctx);
if (value !== tokenIgnoredUnknownEnum) {
list.add(value);
return (message, json, ctx) => {
if (json === null && nullResets) {
delete message[localName];
return;
}
}
const child = toMessage(message[localName]);
readChild(child, json, ctx);
message[localName] = toLocal(child);
};
}
function readMessageField(msg, field, json, ctx) {
if (isResetSentinelNullValue(field, json)) {
msg.clear(field);
return;
}
const msgValue = msg.isSet(field) ? msg.get(field) : reflect(field.message);
readMessage(msgValue, json, ctx);
msg.set(field, msgValue);
function compileListFieldReader(field) {
const localName = field.localName;
const readItem = compileListItemReader(field);
return (message, json, ctx) => {
if (json === null) {
return;
}
if (!Array.isArray(json)) {
throw new FieldError(field, "expected Array, got " + formatVal(json));
}
const items = message[localName];
for (let i = 0; i < json.length; i++) {
const value = readItem(json[i], ctx, items.length);
if (value !== tokenIgnoredUnknownEnum) {
items.push(value);
}
}
};
}
function readEnumField(msg, field, json, ctx) {
if (isResetSentinelNullValue(field, json)) {
msg.clear(field);
return;
/**
* Compile a decoder for a list item. The index is only used in errors, and
* accounts for previously merged items.
*/
function compileListItemReader(field) {
switch (field.listKind) {
case "scalar": {
const parseScalar = compileScalarParse(field);
const checkValue = checkScalarValue(field.scalar);
const toLocal = compileScalarToLocal(field);
return (json, ctx, index) => {
if (json === null) {
throw new FieldError(field, "list item must not be null");
}
const value = parseScalar(json);
const check = checkValue(value);
if (check !== true) {
throw new FieldError(field, `list item #${index + 1}: ${reasonSingular(field, value, check)}`);
}
return toLocal(value);
};
}
case "enum": {
const readEnumValue = compileEnumConverter(field.enum);
const checkEnum = compileEnumCheck(field.enum);
const nullResets = field.enum.typeName != "google.protobuf.NullValue";
return (json, ctx, index) => {
if (json === null && nullResets) {
throw new FieldError(field, "list item must not be null");
}
const value = readEnumValue(json, ctx.ignoreUnknownFields);
if (value === tokenIgnoredUnknownEnum) {
return value;
}
const check = checkEnum(value);
if (check !== true) {
throw new FieldError(field, `list item #${index + 1}: ${reasonSingular(field, value, check)}`);
}
return value;
};
}
case "message": {
const { toMessage, toLocal } = localMessageMapper(field);
const readChild = compiledReader(field.message);
const nullResets = field.message.typeName != "google.protobuf.Value";
return (json, ctx) => {
if (json === null && nullResets) {
throw new FieldError(field, "list item must not be null");
}
const child = toMessage(undefined);
readChild(child, json, ctx);
return toLocal(child);
};
}
}
const enumValue = readEnum(field.enum, json, ctx.ignoreUnknownFields);
if (enumValue !== tokenIgnoredUnknownEnum) {
msg.set(field, enumValue);
}
}
function readScalarField(msg, field, json) {
if (json === null) {
msg.clear(field);
function compileMapFieldReader(field) {
const localName = field.localName;
const mapKey = field.mapKey;
const parseMapKey = compileMapKeyParse(mapKey);
const checkMapKey = checkScalarValue(mapKey);
let parseValue;
// Additional validation for scalar and enum values, matching the checks
// of the reflect API. Message values need no validation.
let checkValue;
let toLocalValue = (value) => value;
// Fields with google.protobuf.Value or google.protobuf.NullValue values
// permit a Protobuf-serializable null.
let nullResets = true;
switch (field.mapKind) {
case "scalar": {
parseValue = compileScalarParse(field);
checkValue = checkScalarValue(field.scalar);
toLocalValue = compileScalarToLocal(field);
break;
}
case "enum": {
const readEnumValue = compileEnumConverter(field.enum);
parseValue = (json, ctx) => readEnumValue(json, ctx.ignoreUnknownFields);
checkValue = compileEnumCheck(field.enum);
nullResets = field.enum.typeName != "google.protobuf.NullValue";
break;
}
case "message": {
const { toMessage, toLocal } = localMessageMapper(field);
const readChild = compiledReader(field.message);
nullResets = field.message.typeName != "google.protobuf.Value";
parseValue = (json, ctx) => {
const child = toMessage(undefined);
readChild(child, json, ctx);
return toLocal(child);
};
break;
}
}
else {
msg.set(field, scalarFromJson(field, json));
}
return (message, json, ctx) => {
if (json === null) {
return;
}
if (typeof json != "object" || Array.isArray(json)) {
throw new FieldError(field, "expected object, got " + formatVal(json));
}
const record = message[localName];
const seen = new Set();
const jsonMapKeys = Object.keys(json);
for (let i = 0; i < jsonMapKeys.length; i++) {
const jsonMapKey = jsonMapKeys[i];
const jsonMapValue = json[jsonMapKey];
const key = parseMapKey(jsonMapKey);
if (seen.has(key)) {
throw new FieldError(field, `duplicate map key "${jsonMapKey}"`);
}
seen.add(key);
if (jsonMapValue === null && nullResets) {
throw new FieldError(field, "map value must not be null");
}
const value = parseValue(jsonMapValue, ctx);
if (value === tokenIgnoredUnknownEnum) {
continue;
}
const checkKey = checkMapKey(key);
if (checkKey !== true) {
throw new FieldError(field, `invalid map key: ${reasonSingular({ scalar: mapKey }, key, checkKey)}`);
}
if (checkValue !== undefined) {
const check = checkValue(value);
if (check !== true) {
throw new FieldError(field, `map entry ${formatVal(key)}: ${reasonSingular(field, value, check)}`);
}
}
// Object property keys are always strings or symbols. Assigning with a
// boolean, number, or bigint key implicitly converts it to a string.
record[key] = toLocalValue(value);
}
};
}
const tokenIgnoredUnknownEnum = Symbol();
/**
* Indicates whether a value is a sentinel for reseting a field.
*
* For this to be true, the value must be a JSON null and the field must not
* permit a present, Protobuf-serializable null.
*
* Only message google.protobuf.Value and enum google.protobuf.NullValue fields
* permit Protobuf-serializable nulls.
*
* Note that field-resetting sentinel nulls are not permitted in lists and maps.
* Compile a converter from a JSON value to an enum value. JSON null returns
* the enum's first value. With ignoreUnknownFields false, unknown string
* values raise an error; with true, they return tokenIgnoredUnknownEnum.
* The value is not checked against the enum's values, see compileEnumCheck.
*/
function isResetSentinelNullValue(field, json) {
var _a, _b;
return (json === null &&
((_a = field.message) === null || _a === void 0 ? void 0 : _a.typeName) != "google.protobuf.Value" &&
((_b = field.enum) === null || _b === void 0 ? void 0 : _b.typeName) != "google.protobuf.NullValue");
function compileEnumConverter(desc) {
const zero = desc.values[0].number;
const values = desc.values;
return (json, ignoreUnknownFields) => {
if (json === null) {
return zero;
}
switch (typeof json) {
case "number":
if (Number.isInteger(json)) {
return json;
}
break;
case "string": {
const value = 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: ${formatVal(json)}`);
};
}
const tokenIgnoredUnknownEnum = Symbol();
function readEnum(desc, json, ignoreUnknownFields) {
if (json === null) {
return desc.values[0].number;
/**
* Compile the check that the reflect API performs for enum values: open
* enums accept any int32 value, closed enums accept only declared values.
*/
function compileEnumCheck(desc) {
if (desc.open) {
return checkScalarValue(ScalarType.INT32);
}
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: ${formatVal(json)}`);
const values = desc.values;
return (value) => values.some((v) => v.number === value);
}
/**
* Try to parse a JSON value to a scalar value for the reflect API.
*
* Returns the input if the JSON value cannot be converted. Raises a FieldError
* if conversion would be ambiguous.
* Compile a converter from a JSON value to the local representation of a
* scalar, fusing JSON parsing, the validation of the reflect API, and the
* conversion to the local 64-bit integer representation.
*/
function scalarFromJson(field, json) {
// int64, sfixed64, sint64, fixed64, uint64: Reflect supports string and number.
// string, bool: Supported by reflect.
function compileScalarConverter(field) {
const parseScalar = compileScalarParse(field);
const checkValue = checkScalarValue(field.scalar);
const toLocal = compileScalarToLocal(field);
return (json) => {
const value = parseScalar(json);
const check = checkValue(value);
if (check !== true) {
throw new FieldError(field, reasonSingular(field, value, check));
}
return toLocal(value);
};
}
/**
* Compile the JSON-specific parsing step for a scalar value: the special
* string values of float and double, string-encoded numbers, and base64
* bytes. Returns the input unchanged if the JSON value cannot be converted;
* the validation step raises an error for it.
*/
function compileScalarParse(field) {
switch (field.scalar) {

@@ -334,36 +633,38 @@ // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity".

case 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 (Number.isNaN(json)) {
// NaN must be encoded with string constants
throw new FieldError(field, "unexpected NaN number");
return (json) => {
if (json === "NaN")
return NaN;
if (json === "Infinity")
return Number.POSITIVE_INFINITY;
if (json === "-Infinity")
return Number.NEGATIVE_INFINITY;
if (typeof json == "number") {
if (Number.isNaN(json)) {
// NaN must be encoded with string constants
throw new FieldError(field, "unexpected NaN number");
}
if (!Number.isFinite(json)) {
// Infinity must be encoded with string constants
throw new FieldError(field, "unexpected infinite number");
}
return json;
}
if (!Number.isFinite(json)) {
// Infinity must be encoded with string constants
throw new FieldError(field, "unexpected infinite number");
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 float = Number(json);
if (!Number.isFinite(float)) {
// Infinity and NaN must be encoded with string constants
return json;
}
return float;
}
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 (!Number.isFinite(float)) {
// Infinity and NaN must be encoded with string constants
break;
}
return float;
}
break;
return json;
};
// int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted.

@@ -375,38 +676,74 @@ case ScalarType.INT32:

case ScalarType.UINT32:
return int32FromJson(json);
return int32FromJson;
// 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 ScalarType.BYTES:
if (typeof json == "string") {
if (json === "") {
return new Uint8Array(0);
return (json) => {
if (typeof json == "string") {
if (json === "") {
return new Uint8Array(0);
}
try {
return base64Decode(json);
}
catch (e) {
const message = e instanceof Error ? e.message : String(e);
throw new FieldError(field, message);
}
}
try {
return base64Decode(json);
}
catch (e) {
const message = e instanceof Error ? e.message : String(e);
throw new FieldError(field, message);
}
return json;
};
// int64, sfixed64, sint64, fixed64, uint64: The validation step accepts
// string and number. string, bool: no conversion.
default:
return (json) => json;
}
}
/**
* Compile the conversion of a validated scalar value to its local
* representation: 64-bit integers become bigint, or string with the
* longAsString option.
*/
function compileScalarToLocal(field) {
const longAsString = field.fieldKind !== "map" && field.longAsString;
switch (field.scalar) {
case ScalarType.INT64:
case ScalarType.SFIXED64:
case ScalarType.SINT64:
if (longAsString) {
return (value) => String(value);
}
break;
return (value) => typeof value == "string" || typeof value == "number"
? protoInt64.parse(value)
: value;
case ScalarType.FIXED64:
case ScalarType.UINT64:
if (longAsString) {
return (value) => String(value);
}
return (value) => typeof value == "string" || typeof value == "number"
? protoInt64.uParse(value)
: value;
default:
return (value) => value;
}
return json;
}
/**
* Try to parse a JSON value to a map key for the reflect API.
* Canonicalizes 64-bit integers given as string, so that "01 and "1" are one
* key, and duplicates can raise an error.
* Returns the input if the JSON value cannot be converted.
* Return a parser from a JSON value to a map key for the given key type.
* Canonicalizes 64-bit integers given as string, so that "01" and "1" are
* one key, and duplicates can raise an error.
* The parser returns the input if the JSON value cannot be converted.
*/
function mapKeyFromJson(type, jsonString) {
function compileMapKeyParse(type) {
switch (type) {
case ScalarType.BOOL:
switch (jsonString) {
case "true":
return true;
case "false":
return false;
}
return jsonString;
return (jsonString) => {
switch (jsonString) {
case "true":
return true;
case "false":
return false;
}
return jsonString;
};
case ScalarType.INT32:

@@ -417,3 +754,3 @@ case ScalarType.FIXED32:

case ScalarType.SINT32:
return int32FromJson(jsonString);
return int32FromJson;
case ScalarType.INT64:

@@ -424,7 +761,8 @@ case ScalarType.SINT64:

case ScalarType.FIXED64:
return /^-?0+$/.test(jsonString)
return (jsonString) => /^-?0+$/.test(jsonString)
? "0"
: jsonString.replace(/^(-?)0+(?=\d)/, "$1");
default:
return jsonString;
// ScalarType.STRING
return (jsonString) => jsonString;
}

@@ -549,42 +887,2 @@ }

}
function tryWktFromJson(msg, jsonValue, ctx) {
if (!msg.desc.typeName.startsWith("google.protobuf.")) {
return false;
}
switch (msg.desc.typeName) {
case "google.protobuf.Any":
anyFromJson(msg.message, jsonValue, ctx);
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, ctx);
return true;
case "google.protobuf.Value":
valueFromJson(msg.message, jsonValue, ctx);
return true;
case "google.protobuf.ListValue":
listValueFromJson(msg.message, jsonValue, ctx);
return true;
default:
if (isWrapperDesc(msg.desc)) {
const valueField = msg.desc.fields[0];
if (jsonValue === null) {
msg.clear(valueField);
}
else {
msg.set(valueField, scalarFromJson(valueField, jsonValue));
}
return true;
}
return false;
}
}
function anyFromJson(any, json, ctx) {

@@ -612,7 +910,6 @@ var _a;

}
const msg = reflect(desc);
const message = create(desc);
if (hasCustomJsonRepresentation(desc) &&
Object.prototype.hasOwnProperty.call(json, "value")) {
const value = json.value;
readMessage(msg, value, ctx);
compiledReader(desc)(message, json.value, ctx);
}

@@ -623,5 +920,5 @@ else {

delete copy["@type"];
readMessage(msg, copy, ctx);
compiledReader(desc)(message, copy, ctx);
}
anyPack(msg.desc, msg.message, any);
anyPack(desc, message, any);
}

@@ -642,4 +939,3 @@ function timestampFromJson(timestamp, json) {

}
if (ms < Date.parse("0001-01-01T00:00:00Z") ||
ms > Date.parse("9999-12-31T23:59:59Z")) {
if (ms < timestampMsMin || ms > timestampMsMax) {
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`);

@@ -664,3 +960,3 @@ }

const longSeconds = Number(match[1]);
if (longSeconds > 315576000000 || longSeconds < -315576000000) {
if (longSeconds > durationSecondsMax || longSeconds < durationSecondsMin) {
throw new Error(`cannot decode message ${duration.$typeName} from JSON: ${formatVal(json)}`);

@@ -696,6 +992,8 @@ }

}
for (const [k, v] of Object.entries(json)) {
const parsedV = create(ValueSchema);
valueFromJson(parsedV, v, ctx);
struct.fields[k] = parsedV;
const keys = Object.keys(json);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const parsedValue = create(ValueSchema);
valueFromJson(parsedValue, json[key], ctx);
struct.fields[key] = parsedValue;
}

@@ -742,7 +1040,7 @@ }

}
for (const e of json) {
for (let i = 0; i < json.length; i++) {
const value = create(ValueSchema);
valueFromJson(value, e, ctx);
valueFromJson(value, json[i], ctx);
listValue.values.push(value);
}
}

@@ -1,2 +0,2 @@

import { type DescField } from "../descriptors.js";
import { type DescEnum, type DescField, type DescMessage, ScalarType } from "../descriptors.js";
import { FieldError } from "./error.js";

@@ -19,2 +19,28 @@ /**

}, key: unknown, value: unknown): FieldError | undefined;
type InvalidScalarValueErr = false | "invalid UTF8" | `${string} out of range`;
/**
* Return the check for values of the given scalar type.
*
* @private
*/
export declare function checkScalarValue(scalar: ScalarType): (value: unknown) => true | InvalidScalarValueErr;
/**
* Format the reason why a value is invalid for a singular field.
*
* @private
*/
export declare function reasonSingular(field: {
scalar: ScalarType;
message?: undefined;
enum?: undefined;
} | {
scalar?: undefined;
message: DescMessage;
enum?: undefined;
} | {
scalar?: undefined;
message?: undefined;
enum: DescEnum;
}, val: unknown, details?: string | false): string;
export declare function formatVal(val: unknown): string;
export {};

@@ -61,3 +61,3 @@ // Copyright 2021-2026 Buf Technologies, Inc.

export function checkMapEntry(field, key, value) {
const checkKey = checkScalarValue(key, field.mapKey);
const checkKey = checkScalarValue(field.mapKey)(key);
if (checkKey !== true) {

@@ -74,3 +74,3 @@ return new FieldError(field, `invalid map key: ${reasonSingular({ scalar: field.mapKey }, key, checkKey)}`);

if (field.scalar !== undefined) {
return checkScalarValue(value, field.scalar);
return checkScalarValue(field.scalar)(value);
}

@@ -81,3 +81,3 @@ if (field.enum !== undefined) {

// int32 (see https://protobuf.dev/programming-guides/proto3/#enum).
return checkScalarValue(value, ScalarType.INT32);
return checkScalarValue(ScalarType.INT32)(value);
}

@@ -88,17 +88,24 @@ return field.enum.values.some((v) => v.number === value);

}
function checkScalarValue(value, scalar) {
/**
* Return the check for values of the given scalar type.
*
* @private
*/
export function checkScalarValue(scalar) {
switch (scalar) {
case ScalarType.DOUBLE:
return typeof value == "number";
return (value) => typeof value == "number";
case ScalarType.FLOAT:
if (typeof value != "number") {
return false;
}
if (Number.isNaN(value) || !Number.isFinite(value)) {
return (value) => {
if (typeof value != "number") {
return false;
}
if (Number.isNaN(value) || !Number.isFinite(value)) {
return true;
}
if (value > FLOAT32_MAX || value < FLOAT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
}
if (value > FLOAT32_MAX || value < FLOAT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
};
case ScalarType.INT32:

@@ -108,28 +115,34 @@ case ScalarType.SFIXED32:

// signed
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > INT32_MAX || value < INT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
return (value) => {
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > INT32_MAX || value < INT32_MIN) {
return `${value.toFixed()} out of range`;
}
return true;
};
case ScalarType.FIXED32:
case ScalarType.UINT32:
// unsigned
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > UINT32_MAX || value < 0) {
return `${value.toFixed()} out of range`;
}
return true;
return (value) => {
if (typeof value !== "number" || !Number.isInteger(value)) {
return false;
}
if (value > UINT32_MAX || value < 0) {
return `${value.toFixed()} out of range`;
}
return true;
};
case ScalarType.BOOL:
return typeof value == "boolean";
return (value) => typeof value == "boolean";
case ScalarType.STRING:
if (typeof value != "string") {
return false;
}
return getTextEncoding().checkUtf8(value) || "invalid UTF8";
return (value) => {
if (typeof value != "string") {
return false;
}
return getTextEncoding().checkUtf8(value) || "invalid UTF8";
};
case ScalarType.BYTES:
return value instanceof Uint8Array;
return (value) => value instanceof Uint8Array;
case ScalarType.INT64:

@@ -139,32 +152,41 @@ case ScalarType.SFIXED64:

// signed
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
protoInt64.parse(value);
return true;
return (value) => {
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
protoInt64.parse(value);
return true;
}
catch (_) {
return `${value} out of range`;
}
}
catch (_) {
return `${value} out of range`;
}
}
return false;
return false;
};
case ScalarType.FIXED64:
case ScalarType.UINT64:
// unsigned
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
protoInt64.uParse(value);
return true;
return (value) => {
if (typeof value == "bigint" ||
typeof value == "number" ||
(typeof value == "string" && value.length > 0)) {
try {
protoInt64.uParse(value);
return true;
}
catch (_) {
return `${value} out of range`;
}
}
catch (_) {
return `${value} out of range`;
}
}
return false;
return false;
};
}
}
function reasonSingular(field, val, details) {
/**
* Format the reason why a value is invalid for a singular field.
*
* @private
*/
export function reasonSingular(field, val, details) {
details =

@@ -171,0 +193,0 @@ typeof details == "string" ? `: ${details}` : `, got ${formatVal(val)}`;

@@ -23,4 +23,3 @@ // Copyright 2021-2026 Buf Technologies, Inc.

import { isObject, isReflectList, isReflectMap, isReflectMessage, } from "./guard.js";
// google.protobuf.NullValue.NULL_VALUE;
const NULL_VALUE = 0;
import { wktStructToLocal, wktStructToReflect } from "./message.js";
/**

@@ -467,77 +466,1 @@ * Create a ReflectMessage.

}
function wktStructToReflect(json) {
const struct = {
$typeName: "google.protobuf.Struct",
fields: {},
};
if (isObject(json)) {
for (const [k, v] of Object.entries(json)) {
struct.fields[k] = wktValueToReflect(v);
}
}
return struct;
}
function wktStructToLocal(val) {
const json = {};
for (const [k, v] of Object.entries(val.fields)) {
json[k] = wktValueToLocal(v);
}
return json;
}
function wktValueToLocal(val) {
switch (val.kind.case) {
case "structValue":
return wktStructToLocal(val.kind.value);
case "listValue":
return val.kind.value.values.map(wktValueToLocal);
case "nullValue":
case undefined:
return null;
default:
return val.kind.value;
}
}
function wktValueToReflect(json) {
const value = {
$typeName: "google.protobuf.Value",
kind: { case: undefined },
};
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: NULL_VALUE };
}
else if (Array.isArray(json)) {
const listValue = {
$typeName: "google.protobuf.ListValue",
values: [],
};
if (Array.isArray(json)) {
for (const e of json) {
listValue.values.push(wktValueToReflect(e));
}
}
value.kind = {
case: "listValue",
value: listValue,
};
}
else {
value.kind = {
case: "structValue",
value: wktStructToReflect(json),
};
}
break;
}
return value;
}

@@ -578,2 +578,3 @@ // Copyright 2021-2026 Buf Technologies, Inc.

};
let toStr;
if (isExtension) {

@@ -590,3 +591,3 @@ // extension field

field.jsonName = `[${typeName}]`; // option json_name is not allowed on extension fields
field.toString = () => `extension ${typeName}`;
toStr = () => `extension ${typeName}`;
const extendee = reg.getMessage(trimLeadingDot(proto.extendee));

@@ -606,4 +607,12 @@ assert(extendee, `invalid FieldDescriptorProto: extendee ${proto.extendee} not found`);

field.jsonName = proto.jsonName;
field.toString = () => `field ${parent.typeName}.${proto.name}`;
toStr = () => `field ${parent.typeName}.${proto.name}`;
}
// A plain assignment throws where built-in prototypes are frozen. The
// attributes match what an assignment produces.
Object.defineProperty(field, "toString", {
value: toStr,
writable: true,
enumerable: true,
configurable: true,
});
const label = proto.label;

@@ -610,0 +619,0 @@ const type = proto.type;

@@ -22,4 +22,8 @@ import type { MessageShape } from "./types.js";

/**
* Write a single field to binary format, if it is set. Used to serialize
* extensions: extensions always have explicit presence, so an extension
* value that was just set on the container is always written.
*
* @private
*/
export declare function writeField(writer: BinaryWriter, opts: BinaryWriteOptions, msg: ReflectMessage, field: DescField): void;

@@ -14,5 +14,10 @@ // Copyright 2021-2026 Buf Technologies, Inc.

// limitations under the License.
import { reflect } from "./reflect/reflect.js";
import { BinaryWriter, WireType } from "./wire/binary-encoding.js";
import { ScalarType } from "./descriptors.js";
import { FieldError } from "./reflect/error.js";
import { unsafeLocal } from "./reflect/unsafe.js";
import { localMessageMapper } from "./reflect/message.js";
import { protoInt64 } from "./proto-int64.js";
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.IMPLICIT: const $name = $number;
const IMPLICIT = 2;
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.LEGACY_REQUIRED: const $name = $number;

@@ -28,154 +33,387 @@ const LEGACY_REQUIRED = 3;

export function toBinary(schema, message, options) {
return writeFields(new BinaryWriter(), makeWriteOptions(options), reflect(schema, message)).finish();
const writer = new BinaryWriter();
compiledWriter(schema)(writer, makeWriteOptions(options), message);
return writer.finish();
}
function writeFields(writer, opts, msg) {
var _a;
for (const f of msg.sortedFields) {
if (!msg.isSet(f)) {
if (f.presence == LEGACY_REQUIRED) {
throw new Error(`cannot encode ${f} to binary: required field not set`);
const compiledWriters = new WeakMap();
/**
* Return the compiled encoder for a message, compiling it on first use.
*/
function compiledWriter(desc) {
let compiled = compiledWriters.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
return compiled;
}
function compileMessage(desc) {
const typeName = desc.typeName;
const sortedFields = desc.fields.concat().sort((a, b) => a.number - b.number);
// The field reported in ForeignFieldError.
const foreignField = sortedFields[0];
const fieldWriters = [];
const compiled = (writer, opts, message) => {
if (message.$typeName !== typeName && foreignField !== undefined) {
throw new FieldError(foreignField, `cannot use ${foreignField} with message ${message.$typeName}`, "ForeignFieldError");
}
for (let i = 0; i < fieldWriters.length; i++) {
fieldWriters[i](writer, opts, message);
}
const unknown = message.$unknown;
if (unknown !== undefined && opts.writeUnknownFields) {
for (let i = 0; i < unknown.length; i++) {
const { no, wireType, data } = unknown[i];
writer.tag(no, wireType).raw(data);
}
continue;
}
writeField(writer, opts, msg, f);
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledWriters.set(desc, compiled);
for (const field of sortedFields) {
fieldWriters.push(compileField(field));
}
if (opts.writeUnknownFields) {
for (const { no, wireType, data } of (_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : []) {
writer.tag(no, wireType).raw(data);
}
}
return writer;
return compiled;
}
/**
* @private
*/
export function writeField(writer, opts, msg, field) {
var _a;
function compileField(field) {
switch (field.fieldKind) {
case "message":
case "scalar":
case "enum":
writeScalar(writer, msg.desc.typeName, field.name, (_a = field.scalar) !== null && _a !== void 0 ? _a : ScalarType.INT32, field.number, msg.get(field));
break;
return compileSingularField(field);
case "list":
writeListField(writer, opts, field, msg.get(field));
break;
case "message":
writeMessageField(writer, opts, field, msg.get(field));
break;
return compileListField(field);
case "map":
for (const [key, val] of msg.get(field)) {
writeMapEntry(writer, opts, field, key, val);
}
break;
return compileMapField(field);
}
}
function writeScalar(writer, msgName, fieldName, scalarType, fieldNo, value) {
writeScalarValue(writer.tag(fieldNo, writeTypeOfScalar(scalarType)), msgName, fieldName, scalarType, value);
}
function writeMessageField(writer, opts, field, message) {
if (field.delimitedEncoding) {
writeFields(writer.tag(field.number, WireType.StartGroup), opts, message).tag(field.number, WireType.EndGroup);
/**
* Compile an encoder for a singular field: the presence check, and the
* value encoder.
*/
function compileSingularField(field) {
const writeValue = compileSingularValue(field);
const localName = field.localName;
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (writer, opts, message) => {
const oneof = message[oneofLocalName];
if (oneof.case === localName) {
writeValue(writer, opts, oneof.value);
}
};
}
else {
writeFields(writer.tag(field.number, WireType.LengthDelimited).fork(), opts, message).join();
if (field.presence != IMPLICIT) {
const requiredError = field.presence == LEGACY_REQUIRED
? `cannot encode ${field} to binary: required field not set`
: undefined;
return (writer, opts, message) => {
const value = message[localName];
// Fields with explicit presence have properties on the prototype
// chain for default / zero values (except for proto3).
if (value !== undefined &&
Object.prototype.hasOwnProperty.call(message, localName)) {
writeValue(writer, opts, value);
}
else if (requiredError !== undefined) {
throw new Error(requiredError);
}
};
}
// Implicit presence: the field is set when the value is not the zero
// value. The check is inlined per type, see isScalarZeroValue.
if (field.fieldKind == "enum") {
const zero = field.enum.values[0].number;
return (writer, opts, message) => {
const value = message[localName];
if (value !== zero) {
writeValue(writer, opts, value);
}
};
}
switch (field.scalar) {
case ScalarType.BOOL:
return (writer, opts, message) => {
const value = message[localName];
if (value !== false) {
writeValue(writer, opts, value);
}
};
case ScalarType.STRING:
return (writer, opts, message) => {
const value = message[localName];
if (value !== "") {
writeValue(writer, opts, value);
}
};
case ScalarType.BYTES:
return (writer, opts, message) => {
const value = message[localName];
if (!(value instanceof Uint8Array) || value.byteLength > 0) {
writeValue(writer, opts, value);
}
};
case ScalarType.DOUBLE:
case ScalarType.FLOAT:
return (writer, opts, message) => {
const value = message[localName];
// Object.is distinguishes -0 from 0.
if (!Object.is(value, 0)) {
writeValue(writer, opts, value);
}
};
default:
return (writer, opts, message) => {
const value = message[localName];
// Loose comparison matches 0n, 0 and "0".
if (value != 0) {
writeValue(writer, opts, value);
}
};
}
}
function writeListField(writer, opts, field, list) {
var _a;
if (field.listKind == "message") {
for (const item of list) {
writeMessageField(writer, opts, field, item);
/**
* Compile an encoder for the value of a singular field, including the tag.
*/
function compileSingularValue(field) {
switch (field.fieldKind) {
case "message": {
const { toMessage } = localMessageMapper(field);
const writeChild = compileChildWriter(field);
return (writer, opts, value) => {
writeChild(writer, opts, toMessage(value));
};
}
return;
case "scalar":
case "enum": {
const scalarType = field.fieldKind == "enum" ? ScalarType.INT32 : field.scalar;
const fieldNo = field.number;
const wireType = writeTypeOfScalar(scalarType);
const writeScalar = compileScalarValue(scalarType, field.parent.typeName, field.name);
return (writer, opts, value) => {
writer.tag(fieldNo, wireType);
writeScalar(writer, value);
};
}
}
const scalarType = (_a = field.scalar) !== null && _a !== void 0 ? _a : ScalarType.INT32;
if (field.packed) {
if (!list.size) {
return;
}
function compileListField(field) {
const localName = field.localName;
const fieldNo = field.number;
switch (field.listKind) {
case "message": {
const { toMessage } = localMessageMapper(field);
const writeChild = compileChildWriter(field);
return (writer, opts, message) => {
const items = message[localName];
for (let i = 0; i < items.length; i++) {
writeChild(writer, opts, toMessage(items[i]));
}
};
}
writer.tag(field.number, WireType.LengthDelimited).fork();
for (const item of list) {
writeScalarValue(writer, field.parent.typeName, field.name, scalarType, item);
case "scalar":
case "enum": {
const scalarType = field.listKind == "enum" ? ScalarType.INT32 : field.scalar;
const writeScalar = compileScalarValue(scalarType, field.parent.typeName, field.name);
if (field.packed) {
return (writer, opts, message) => {
const items = message[localName];
if (items.length == 0) {
return;
}
writer.tag(fieldNo, WireType.LengthDelimited).fork();
for (let i = 0; i < items.length; i++) {
writeScalar(writer, items[i]);
}
writer.join();
};
}
const wireType = writeTypeOfScalar(scalarType);
return (writer, opts, message) => {
const items = message[localName];
for (let i = 0; i < items.length; i++) {
writer.tag(fieldNo, wireType);
writeScalar(writer, items[i]);
}
};
}
writer.join();
return;
}
for (const item of list) {
writeScalar(writer, field.parent.typeName, field.name, scalarType, field.number, item);
}
function compileMapField(field) {
const localName = field.localName;
const fieldNo = field.number;
const writeKey = compileMapKey(field);
if (field.mapKind == "message") {
const { toMessage } = localMessageMapper(field);
const writeMessage = compiledWriter(field.message);
return (writer, opts, message) => {
const record = message[localName];
const keys = Object.keys(record);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
writer.tag(fieldNo, WireType.LengthDelimited).fork();
writeKey(writer, key);
// The value of a map entry is always field number 2.
writer.tag(2, WireType.LengthDelimited).fork();
writeMessage(writer, opts, toMessage(record[key]));
writer.join();
writer.join();
}
};
}
const scalarType = field.mapKind == "enum" ? ScalarType.INT32 : field.scalar;
const valueWireType = writeTypeOfScalar(scalarType);
const writeScalar = compileScalarValue(scalarType, field.parent.typeName, field.name);
return (writer, opts, message) => {
const record = message[localName];
const keys = Object.keys(record);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
writer.tag(fieldNo, WireType.LengthDelimited).fork();
writeKey(writer, key);
// The value of a map entry is always field number 2.
writer.tag(2, valueWireType);
writeScalar(writer, record[key]);
writer.join();
}
};
}
function writeMapEntry(writer, opts, field, key, value) {
var _a;
writer.tag(field.number, WireType.LengthDelimited).fork();
// write key, expecting key field number = 1
writeScalar(writer, field.parent.typeName, field.name, field.mapKey, 1, key);
// write value, expecting value field number = 2
switch (field.mapKind) {
case "scalar":
case "enum":
writeScalar(writer, field.parent.typeName, field.name, (_a = field.scalar) !== null && _a !== void 0 ? _a : ScalarType.INT32, 2, value);
break;
case "message":
writeFields(writer.tag(2, WireType.LengthDelimited).fork(), opts, value).join();
break;
/**
* Compile an encoder for a map key. Map keys are stored as object keys and
* are always strings locally. Convert them to their scalar type before
* writing, like the reflect API does when iterating map entries.
*/
function compileMapKey(field) {
const wireType = writeTypeOfScalar(field.mapKey);
const writeScalar = compileScalarValue(field.mapKey, field.parent.typeName, field.name);
const convertKey = compileMapKeyConverter(field.mapKey);
return (writer, key) => {
// The key of a map entry is always field number 1.
writer.tag(1, wireType);
writeScalar(writer, convertKey(key));
};
}
/**
* Returns a converter from an object key (always a string) to the closest
* possible type for the map key type. Invalid keys are passed through to
* the scalar writer, which raises an error for them.
*/
function compileMapKeyConverter(type) {
switch (type) {
case ScalarType.STRING:
return (key) => key;
case ScalarType.BOOL:
return (key) => (key === "true" ? true : key === "false" ? false : key);
case ScalarType.UINT64:
case ScalarType.FIXED64:
return (key) => {
try {
return protoInt64.uParse(key);
}
catch (_a) {
return key;
}
};
case ScalarType.INT64:
case ScalarType.SFIXED64:
case ScalarType.SINT64:
return (key) => {
try {
return protoInt64.parse(key);
}
catch (_a) {
return key;
}
};
default:
// Handles INT32, UINT32, SINT32, FIXED32, SFIXED32.
// We do not use individual cases to save a few bytes code size.
return (key) => {
const n = Number.parseInt(key);
return Number.isFinite(n) ? n : key;
};
}
writer.join();
}
function writeScalarValue(writer, msgName, fieldName, type, value) {
try {
switch (type) {
case ScalarType.STRING:
writer.string(value);
break;
case ScalarType.BOOL:
writer.bool(value);
break;
case ScalarType.DOUBLE:
writer.double(value);
break;
case ScalarType.FLOAT:
writer.float(value);
break;
case ScalarType.INT32:
writer.int32(value);
break;
case ScalarType.INT64:
writer.int64(value);
break;
case ScalarType.UINT64:
writer.uint64(value);
break;
case ScalarType.FIXED64:
writer.fixed64(value);
break;
case ScalarType.BYTES:
writer.bytes(value);
break;
case ScalarType.FIXED32:
writer.fixed32(value);
break;
case ScalarType.SFIXED32:
writer.sfixed32(value);
break;
case ScalarType.SFIXED64:
writer.sfixed64(value);
break;
case ScalarType.SINT64:
writer.sint64(value);
break;
case ScalarType.UINT32:
writer.uint32(value);
break;
case ScalarType.SINT32:
writer.sint32(value);
break;
/**
* Compile an encoder for a bare scalar value (no tag), wrapping errors from
* the writer with the message and field name.
*/
function compileScalarValue(type, messageName, fieldName) {
const writeScalar = compileScalarWrite(type);
return (writer, value) => {
try {
writeScalar(writer, value);
}
}
catch (e) {
if (e instanceof Error) {
throw new Error(`cannot encode field ${msgName}.${fieldName} to binary: ${e.message}`);
catch (e) {
if (e instanceof Error) {
throw new Error(`cannot encode field ${messageName}.${fieldName} to binary: ${e.message}`);
}
throw e;
}
throw e;
};
}
function compileScalarWrite(type) {
switch (type) {
case ScalarType.STRING:
return (writer, value) => writer.string(value);
case ScalarType.BOOL:
return (writer, value) => writer.bool(value);
case ScalarType.DOUBLE:
return (writer, value) => writer.double(value);
case ScalarType.FLOAT:
return (writer, value) => writer.float(value);
case ScalarType.INT32:
return (writer, value) => writer.int32(value);
case ScalarType.INT64:
return (writer, value) => writer.int64(value);
case ScalarType.UINT64:
return (writer, value) => writer.uint64(value);
case ScalarType.FIXED64:
return (writer, value) => writer.fixed64(value);
case ScalarType.BYTES:
return (writer, value) => writer.bytes(value);
case ScalarType.FIXED32:
return (writer, value) => writer.fixed32(value);
case ScalarType.SFIXED32:
return (writer, value) => writer.sfixed32(value);
case ScalarType.SFIXED64:
return (writer, value) => writer.sfixed64(value);
case ScalarType.SINT64:
return (writer, value) => writer.sint64(value);
case ScalarType.UINT32:
return (writer, value) => writer.uint32(value);
case ScalarType.SINT32:
return (writer, value) => writer.sint32(value);
}
}
/**
* Write a single field to binary format, if it is set. Used to serialize
* extensions: extensions always have explicit presence, so an extension
* value that was just set on the container is always written.
*
* @private
*/
export function writeField(writer, opts, msg, field) {
compileField(field)(writer, opts, msg[unsafeLocal]);
}
/**
* Compile an encoder for the wire format of a message field, honoring the
* delimited encoding of the field. The tag is written by the encoder.
*/
function compileChildWriter(field) {
const fieldNo = field.number;
const writeMessage = compiledWriter(field.message);
if (field.delimitedEncoding) {
return (writer, opts, child) => {
writer.tag(fieldNo, WireType.StartGroup);
writeMessage(writer, opts, child);
writer.tag(fieldNo, WireType.EndGroup);
};
}
return (writer, opts, child) => {
writer.tag(fieldNo, WireType.LengthDelimited).fork();
writeMessage(writer, opts, child);
writer.join();
};
}
function writeTypeOfScalar(type) {

@@ -182,0 +420,0 @@ switch (type) {

@@ -16,8 +16,12 @@ // Copyright 2021-2026 Buf Technologies, Inc.

import { protoCamelCase, protoSnakeCase } from "./reflect/names.js";
import { reflect } from "./reflect/reflect.js";
import { anyUnpack } from "./wkt/index.js";
import { hasCustomJsonRepresentation, isWrapperDesc } from "./wkt/wrappers.js";
import { durationSecondsMax, durationSecondsMin, timestampMsMax, timestampMsMin, } from "./wkt/json.js";
import { base64Encode } from "./wire/index.js";
import { createExtensionContainer, getExtension } from "./extensions.js";
import { checkField, formatVal } from "./reflect/reflect-check.js";
import { FieldError } from "./reflect/error.js";
import { unsafeLocal } from "./reflect/unsafe.js";
import { scalarZeroValue } from "./reflect/scalar.js";
import { localMessageMapper } from "./reflect/message.js";
// bootstrap-inject google.protobuf.FeatureSet.FieldPresence.LEGACY_REQUIRED: const $name = $number;

@@ -41,3 +45,3 @@ const LEGACY_REQUIRED = 3;

export function toJson(schema, message, options) {
return reflectToJson(reflect(schema, message), makeWriteOptions(options));
return compiledWriter(schema)(makeWriteOptions(options), message);
}

@@ -66,119 +70,333 @@ /**

}
function reflectToJson(msg, opts) {
var _a;
const wktJson = tryWktToJson(msg, opts);
if (wktJson !== undefined)
return wktJson;
const json = {};
for (const f of msg.sortedFields) {
if (!msg.isSet(f)) {
if (f.presence == LEGACY_REQUIRED) {
throw new Error(`cannot encode ${f} to JSON: required field not set`);
const compiledWriters = new WeakMap();
/**
* Return the compiled encoder for a message, compiling it on first use.
*/
function compiledWriter(desc) {
let compiled = compiledWriters.get(desc);
if (compiled === undefined) {
compiled = compileMessage(desc);
}
return compiled;
}
function compileMessage(desc) {
const typeName = desc.typeName;
const writeWkt = compileWkt(desc);
if (writeWkt !== undefined) {
// The field reported in ForeignFieldError. All well-known types with a
// custom JSON representation have at least one field.
const foreignField = desc.fields[0];
const compiledWriter = (opts, message) => {
if (message.$typeName !== typeName && foreignField !== undefined) {
throw new FieldError(foreignField, `cannot use ${foreignField} with message ${message.$typeName}`, "ForeignFieldError");
}
if (!opts.alwaysEmitImplicit || f.presence !== IMPLICIT) {
// Fields with implicit presence omit zero values (e.g. empty string) by default
continue;
}
return writeWkt(opts, message);
};
compiledWriters.set(desc, compiledWriter);
return compiledWriter;
}
const sortedFields = desc.fields.concat().sort((a, b) => a.number - b.number);
// The field reported in ForeignFieldError.
const foreignField = sortedFields[0];
const fieldWriters = [];
const compiledWriter = (opts, message) => {
if (message.$typeName !== typeName && foreignField !== undefined) {
throw new FieldError(foreignField, `cannot use ${foreignField} with message ${message.$typeName}`, "ForeignFieldError");
}
const jsonValue = fieldToJson(f, msg.get(f), opts);
if (jsonValue !== undefined) {
json[jsonName(f, opts)] = jsonValue;
const json = {};
for (let i = 0; i < fieldWriters.length; i++) {
fieldWriters[i](opts, message, json);
}
if (opts.registry) {
writeExtensions(json, opts, opts.registry, message, desc);
}
return json;
};
// Register before compiling fields, so that recursive message types
// resolve to this instance instead of compiling endlessly.
compiledWriters.set(desc, compiledWriter);
for (const field of sortedFields) {
fieldWriters.push(compileField(field));
}
if (opts.registry) {
const tagSeen = new Set();
for (const { no } of (_a = msg.getUnknown()) !== null && _a !== void 0 ? _a : []) {
// Same tag can appear multiple times, so we
// keep track and skip identical ones.
if (!tagSeen.has(no)) {
tagSeen.add(no);
const extension = opts.registry.getExtensionFor(msg.desc, no);
if (!extension) {
continue;
return compiledWriter;
}
/**
* Compile an encoder for a well-known type with a custom JSON representation,
* or return undefined for other messages.
*/
function compileWkt(desc) {
if (!desc.typeName.startsWith("google.protobuf.")) {
return undefined;
}
switch (desc.typeName) {
case "google.protobuf.Any":
return (opts, message) => anyToJson(message, opts);
case "google.protobuf.Timestamp":
return (opts, message) => timestampToJson(message);
case "google.protobuf.Duration":
return (opts, message) => durationToJson(message);
case "google.protobuf.FieldMask":
return (opts, message) => fieldMaskToJson(message);
case "google.protobuf.Struct":
return (opts, message) => structToJson(message);
case "google.protobuf.Value":
return (opts, message) => valueToJson(message);
case "google.protobuf.ListValue":
return (opts, message) => listValueToJson(message);
default:
if (isWrapperDesc(desc)) {
const valueField = desc.fields[0];
const localName = valueField.localName;
const zero = scalarZeroValue(valueField.scalar, false);
const writeScalar = compileScalarValue(valueField);
return (opts, message) => {
const value = message[localName];
return writeScalar(opts, value === undefined ? zero : value);
};
}
return undefined;
}
}
function compileField(field) {
switch (field.fieldKind) {
case "scalar":
case "enum":
case "message":
return compileSingularField(field);
case "list":
case "map": {
const writeValue = field.fieldKind == "list"
? compileListValue(field)
: compileMapValue(field);
const protoName = field.name;
const jsonKey = field.jsonName;
const localName = field.localName;
return (opts, message, json) => {
const value = writeValue(opts, message[localName]);
if (value !== undefined) {
json[opts.useProtoFieldName ? protoName : jsonKey] = value;
}
const value = getExtension(msg.message, extension);
const [container, field] = createExtensionContainer(extension, value);
const jsonValue = fieldToJson(field, container.get(field), opts);
if (jsonValue !== undefined) {
json[extension.jsonName] = jsonValue;
}
}
};
}
}
return json;
}
function fieldToJson(f, val, opts) {
switch (f.fieldKind) {
/**
* Compile an encoder for a singular field: the presence check, and the
* value encoder.
*/
function compileSingularField(field) {
const writeValue = compileSingularValue(field);
const protoName = field.name;
const jsonKey = field.jsonName;
const localName = field.localName;
if (field.oneof) {
const oneofLocalName = field.oneof.localName;
return (opts, message, json) => {
const oneof = message[oneofLocalName];
if (oneof.case === localName) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, oneof.value);
}
};
}
if (field.presence != IMPLICIT) {
const requiredError = field.presence == LEGACY_REQUIRED
? `cannot encode ${field} to JSON: required field not set`
: undefined;
return (opts, message, json) => {
const value = message[localName];
// Fields with explicit presence have properties on the prototype
// chain for default / zero values (except for proto3).
if (value !== undefined &&
Object.prototype.hasOwnProperty.call(message, localName)) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
else if (requiredError !== undefined) {
throw new Error(requiredError);
}
};
}
// Implicit presence: the field is emitted when the value is not the zero
// value, or when alwaysEmitImplicit is enabled. The zero check is inlined
// per type, see isScalarZeroValue.
if (field.fieldKind == "enum") {
const zero = field.enum.values[0].number;
return (opts, message, json) => {
const value = message[localName];
if (value !== zero || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
}
switch (field.scalar) {
case ScalarType.BOOL:
return (opts, message, json) => {
const value = message[localName];
if (value !== false || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
case ScalarType.STRING:
return (opts, message, json) => {
const value = message[localName];
if (value !== "" || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
case ScalarType.BYTES:
return (opts, message, json) => {
const value = message[localName];
if (!(value instanceof Uint8Array) ||
value.byteLength > 0 ||
opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
case ScalarType.DOUBLE:
case ScalarType.FLOAT:
return (opts, message, json) => {
const value = message[localName];
// Object.is distinguishes -0 from 0.
if (!Object.is(value, 0) || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
default:
return (opts, message, json) => {
const value = message[localName];
// Loose comparison matches 0n, 0 and "0".
if (value != 0 || opts.alwaysEmitImplicit) {
json[opts.useProtoFieldName ? protoName : jsonKey] = writeValue(opts, value);
}
};
}
}
/**
* Compile an encoder for the value of a field of any kind. Used for
* extension values.
*/
function compileFieldValue(field) {
switch (field.fieldKind) {
case "scalar":
return scalarToJson(f, val);
case "enum":
case "message":
return reflectToJson(val, opts);
case "enum":
return enumToJsonInternal(f.enum, val, opts.enumAsInteger);
return compileSingularValue(field);
case "list":
return listToJson(val, opts);
return compileListValue(field);
case "map":
return mapToJson(val, opts);
return compileMapValue(field);
}
}
function mapToJson(map, opts) {
const f = map.field();
const jsonObj = {};
switch (f.mapKind) {
/**
* Compile an encoder for the value of a singular field.
*/
function compileSingularValue(field) {
switch (field.fieldKind) {
case "scalar":
for (const [entryKey, entryValue] of map) {
jsonObj[entryKey] = scalarToJson(f, entryValue);
}
break;
return compileScalarValue(field);
case "enum":
return compileEnumValue(field);
case "message":
for (const [entryKey, entryValue] of map) {
jsonObj[entryKey] = reflectToJson(entryValue, opts);
}
break;
return compileMessageValue(field);
}
}
/**
* Compile an encoder for the value of a message field.
*/
function compileMessageValue(field) {
const { toMessage } = localMessageMapper(field);
const writeMessage = compiledWriter(field.message);
return (opts, value) => writeMessage(opts, toMessage(value));
}
/**
* Compile an encoder for a list field value. Returns undefined for an empty
* list, unless alwaysEmitImplicit is enabled.
*/
function compileListValue(field) {
const writeItem = compileListItemValue(field);
return (opts, value) => {
const items = value;
if (items.length == 0 && !opts.alwaysEmitImplicit) {
return undefined;
}
const jsonArray = [];
for (let i = 0; i < items.length; i++) {
jsonArray.push(writeItem(opts, items[i]));
}
return jsonArray;
};
}
function compileListItemValue(field) {
switch (field.listKind) {
case "scalar":
return compileScalarValue(field);
case "enum":
for (const [entryKey, entryValue] of map) {
jsonObj[entryKey] = enumToJsonInternal(f.enum, entryValue, opts.enumAsInteger);
}
break;
return compileEnumValue(field);
case "message":
return compileMessageValue(field);
}
return opts.alwaysEmitImplicit || map.size > 0 ? jsonObj : undefined;
}
function listToJson(list, opts) {
const f = list.field();
const jsonArr = [];
switch (f.listKind) {
/**
* Compile an encoder for a map field value. Returns undefined for an empty
* map, unless alwaysEmitImplicit is enabled. Map keys are stored as object
* keys and are used as JSON keys as-is.
*/
function compileMapValue(field) {
const writeMapValue = compileMapEntryValue(field);
return (opts, value) => {
const record = value;
const keys = Object.keys(record);
if (keys.length == 0 && !opts.alwaysEmitImplicit) {
return undefined;
}
const jsonObject = {};
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
jsonObject[key] = writeMapValue(opts, record[key]);
}
return jsonObject;
};
}
function compileMapEntryValue(field) {
switch (field.mapKind) {
case "scalar":
for (const item of list) {
jsonArr.push(scalarToJson(f, item));
}
break;
return compileScalarValue(field);
case "enum":
for (const item of list) {
jsonArr.push(enumToJsonInternal(f.enum, item, opts.enumAsInteger));
}
break;
return compileEnumValue(field);
case "message":
for (const item of list) {
jsonArr.push(reflectToJson(item, opts));
}
break;
return compileMessageValue(field);
}
return opts.alwaysEmitImplicit || jsonArr.length > 0 ? jsonArr : undefined;
}
function enumToJsonInternal(desc, value, enumAsInteger) {
var _a;
if (typeof value != "number") {
throw new Error(`cannot encode ${desc} to JSON: expected number, got ${formatVal(value)}`);
}
/**
* Compile an encoder for an enum value.
*/
function compileEnumValue(field) {
const desc = field.enum;
if (desc.typeName == "google.protobuf.NullValue") {
return null;
return (opts, value) => {
if (typeof value != "number") {
throw errorEnumValue(desc, value);
}
return null;
};
}
if (enumAsInteger) {
return value;
}
const val = desc.value[value];
return (_a = val === null || val === void 0 ? void 0 : val.name) !== null && _a !== void 0 ? _a : value; // if we don't know the enum value, just return the number
return (opts, value) => {
var _a, _b;
if (typeof value != "number") {
throw errorEnumValue(desc, value);
}
if (opts.enumAsInteger) {
return value;
}
// If we don't know the enum value, just return the number.
return (_b = (_a = desc.value[value]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : value;
};
}
function scalarToJson(field, value) {
var _a, _b, _c, _d, _e, _f;
function errorEnumValue(desc, value) {
return new Error(`cannot encode ${desc} to JSON: expected number, got ${formatVal(value)}`);
}
/**
* Compile an encoder for a scalar value. Errors report the original field
* descriptor, which may be a list or map field for items of those fields.
*/
function compileScalarValue(field) {
switch (field.scalar) {

@@ -191,32 +409,40 @@ // int32, fixed32, uint32: JSON value will be a decimal number. Either numbers or strings are accepted.

case ScalarType.UINT32:
if (typeof value != "number") {
throw new Error(`cannot encode ${field} to JSON: ${(_a = checkField(field, value)) === null || _a === void 0 ? void 0 : _a.message}`);
}
return value;
return (opts, value) => {
if (typeof value != "number") {
throw errorScalarValue(field, value);
}
return value;
};
// 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 ScalarType.FLOAT:
case ScalarType.DOUBLE: // eslint-disable-line no-fallthrough
if (typeof value != "number") {
throw new Error(`cannot encode ${field} to JSON: ${(_b = checkField(field, value)) === null || _b === void 0 ? void 0 : _b.message}`);
}
if (Number.isNaN(value))
return "NaN";
if (value === Number.POSITIVE_INFINITY)
return "Infinity";
if (value === Number.NEGATIVE_INFINITY)
return "-Infinity";
return value;
case ScalarType.DOUBLE:
return (opts, value) => {
if (typeof value != "number") {
throw errorScalarValue(field, value);
}
if (Number.isNaN(value))
return "NaN";
if (value === Number.POSITIVE_INFINITY)
return "Infinity";
if (value === Number.NEGATIVE_INFINITY)
return "-Infinity";
return value;
};
// string:
case ScalarType.STRING:
if (typeof value != "string") {
throw new Error(`cannot encode ${field} to JSON: ${(_c = checkField(field, value)) === null || _c === void 0 ? void 0 : _c.message}`);
}
return value;
return (opts, value) => {
if (typeof value != "string") {
throw errorScalarValue(field, value);
}
return value;
};
// bool:
case ScalarType.BOOL:
if (typeof value != "boolean") {
throw new Error(`cannot encode ${field} to JSON: ${(_d = checkField(field, value)) === null || _d === void 0 ? void 0 : _d.message}`);
}
return value;
return (opts, value) => {
if (typeof value != "boolean") {
throw errorScalarValue(field, value);
}
return value;
};
// JSON value will be a decimal string. Either numbers or strings are accepted.

@@ -228,46 +454,52 @@ case ScalarType.UINT64:

case ScalarType.SINT64:
if (typeof value == "bigint" ||
typeof value == "string" ||
(typeof value == "number" && Number.isInteger(value))) {
return value.toString();
}
throw new Error(`cannot encode ${field} to JSON: ${(_e = checkField(field, value)) === null || _e === void 0 ? void 0 : _e.message}`);
return (opts, value) => {
if (typeof value == "bigint" ||
typeof value == "string" ||
(typeof value == "number" && Number.isInteger(value))) {
return value.toString();
}
throw errorScalarValue(field, value);
};
// 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 ScalarType.BYTES:
if (value instanceof Uint8Array) {
return base64Encode(value);
}
throw new Error(`cannot encode ${field} to JSON: ${(_f = checkField(field, value)) === null || _f === void 0 ? void 0 : _f.message}`);
return (opts, value) => {
if (value instanceof Uint8Array) {
return base64Encode(value);
}
throw errorScalarValue(field, value);
};
}
}
function jsonName(f, opts) {
return opts.useProtoFieldName ? f.name : f.jsonName;
function errorScalarValue(field, value) {
var _a;
return new Error(`cannot encode ${field} to JSON: ${(_a = checkField(field, value)) === null || _a === void 0 ? void 0 : _a.message}`);
}
// returns a json value if wkt, otherwise returns undefined.
function tryWktToJson(msg, opts) {
if (!msg.desc.typeName.startsWith("google.protobuf.")) {
return undefined;
/**
* Write extensions for unknown fields that are found in the registry.
*/
function writeExtensions(json, opts, registry, message, desc) {
const unknown = message.$unknown;
if (unknown === undefined) {
return;
}
switch (msg.desc.typeName) {
case "google.protobuf.Any":
return anyToJson(msg.message, opts);
case "google.protobuf.Timestamp":
return timestampToJson(msg.message);
case "google.protobuf.Duration":
return durationToJson(msg.message);
case "google.protobuf.FieldMask":
return fieldMaskToJson(msg.message);
case "google.protobuf.Struct":
return structToJson(msg.message);
case "google.protobuf.Value":
return valueToJson(msg.message);
case "google.protobuf.ListValue":
return listValueToJson(msg.message);
default:
if (isWrapperDesc(msg.desc)) {
const valueField = msg.desc.fields[0];
return scalarToJson(valueField, msg.get(valueField));
const tagSeen = new Set();
for (let i = 0; i < unknown.length; i++) {
const { no } = unknown[i];
// Same tag can appear multiple times, so we
// keep track and skip identical ones.
if (!tagSeen.has(no)) {
tagSeen.add(no);
const extension = registry.getExtensionFor(desc, no);
if (!extension) {
continue;
}
return undefined;
const value = getExtension(message, extension);
const [container, field] = createExtensionContainer(extension, value);
const local = container[unsafeLocal];
const jsonValue = compileFieldValue(field)(opts, local[field.localName]);
if (jsonValue !== undefined) {
json[extension.jsonName] = jsonValue;
}
}
}

@@ -291,6 +523,7 @@ }

}
const reflected = reflect(desc, message);
const json = hasCustomJsonRepresentation(desc)
? { value: tryWktToJson(reflected, opts) }
: reflectToJson(reflected, opts);
? {
value: compiledWriter(desc)(opts, message),
}
: compiledWriter(desc)(opts, message);
json["@type"] = val.typeUrl;

@@ -302,3 +535,3 @@ return json;

const nanos = val.nanos;
if (seconds > 315576000000 || seconds < -315576000000) {
if (seconds > durationSecondsMax || seconds < durationSecondsMin) {
throw new Error(`cannot encode message ${val.$typeName} to JSON: value out of range`);

@@ -338,4 +571,6 @@ }

const json = {};
for (const [k, v] of Object.entries(val.fields)) {
json[k] = valueToJson(v);
const keys = Object.keys(val.fields);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
json[key] = valueToJson(val.fields[key]);
}

@@ -370,4 +605,3 @@ return json;

const ms = Number(val.seconds) * 1000;
if (ms < Date.parse("0001-01-01T00:00:00Z") ||
ms > Date.parse("9999-12-31T23:59:59Z")) {
if (ms < timestampMsMin || ms > timestampMsMax) {
throw new Error(`cannot encode message ${val.$typeName} to JSON: must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive`);

@@ -374,0 +608,0 @@ }

@@ -13,2 +13,3 @@ /**

export declare function base64Decode(base64Str: string): Uint8Array<ArrayBuffer>;
type Base64Encoding = "std" | "std_raw" | "url";
/**

@@ -24,2 +25,3 @@ * Encode a byte array to a base64 string.

*/
export declare function base64Encode(bytes: Uint8Array, encoding?: "std" | "std_raw" | "url"): string;
export declare function base64Encode(bytes: Uint8Array, encoding?: Base64Encoding): string;
export {};

@@ -14,2 +14,4 @@ // Copyright 2021-2026 Buf Technologies, Inc.

// limitations under the License.
// Native Uint8Array.prototype.setFromBase64, if the runtime provides it.
const nativeSetFromBase64 = Uint8Array.prototype.setFromBase64;
/**

@@ -27,10 +29,31 @@ * Decodes a base64 string to a byte array.

export function base64Decode(base64Str) {
const len = base64Str.length;
// Decoded size, assuming a well-formed string: three bytes per group of
// four characters, minus one byte for each padding character.
let size = len - ((len + 3) >> 2);
if ((len & 3) == 0 && base64Str[len - 1] == "=") {
size -= base64Str[len - 2] == "=" ? 2 : 1;
}
const bytes = new Uint8Array(size);
let written = -1;
if (nativeSetFromBase64) {
try {
const result = nativeSetFromBase64.call(bytes, base64Str);
if (result.read == len) {
written = result.written;
}
}
catch (_a) {
// The native decoder rejects base64url and inner padding, which we accept.
}
}
if (written < 0) {
written = setFromBase64(bytes, base64Str);
}
return written == size ? bytes : bytes.subarray(0, written);
}
/** Writes into `bytes` from index 0 and returns the number of bytes written. */
function setFromBase64(bytes, base64Str) {
const table = getDecodeTable();
// estimate byte size, not accounting for inner padding and whitespace
let es = (base64Str.length * 3) / 4;
if (base64Str[base64Str.length - 2] == "=")
es -= 2;
else if (base64Str[base64Str.length - 1] == "=")
es -= 1;
let bytes = new Uint8Array(es), bytePos = 0, // position in byte array
let bytePos = 0, // position in byte array
groupPos = 0, // position in base64 group

@@ -78,4 +101,10 @@ b, // current byte

throw Error("invalid base64 string");
return bytes.subarray(0, bytePos);
return bytePos;
}
const nativeToBase64 = Uint8Array.prototype.toBase64;
const toBase64OptionsMap = {
std: { alphabet: "base64", omitPadding: false },
std_raw: { alphabet: "base64", omitPadding: true },
url: { alphabet: "base64url", omitPadding: true },
};
/**

@@ -92,2 +121,5 @@ * Encode a byte array to a base64 string.

export function base64Encode(bytes, encoding = "std") {
if (nativeToBase64) {
return nativeToBase64.call(bytes, toBase64OptionsMap[encoding]);
}
const table = getEncodeTable(encoding);

@@ -94,0 +126,0 @@ const pad = encoding == "std";

@@ -63,26 +63,34 @@ /**

export declare class BinaryWriter {
private readonly encodeUtf8;
/**
* We cannot allocate a buffer for the entire output
* because we don't know its size.
*
* So we collect smaller chunks of known size and
* concat them later.
*
* Use `raw()` to push data to this array. It will flush
* `buf` first.
* Growable byte buffer. We allocate a reasonably sized
* initial buffer and double its capacity when needed.
*/
private chunks;
private buffer;
/**
* A growing buffer for byte values. If you don't know
* the size of the data you are writing, push to this
* array.
* Cached DataView for fixed-width writes. Read it via `view()`, which
* rebuilds it if `buffer` has since grown.
*/
protected buf: number[];
private viewCache;
/**
* Previous fork states.
* Current write position in the buffer.
*/
private stack;
private pos;
/**
* Previous fork positions (the write position at the time
* `fork()` was called).
*/
private stackPos;
/**
* UTF-8 codec used by `string()`. Uses the text encoding's `encodeUtf8Into`,
* or emulates it if a custom `encodeUtf8` was passed to the constructor.
*/
private readonly encodeUtf8Into;
constructor(encodeUtf8?: (text: string) => Uint8Array);
private ensureCapacity;
/**
* The DataView over `buffer`, rebuilt only if the buffer has grown since it
* was last used.
*/
private view;
/**
* Return all bytes written and reset this writer.

@@ -175,2 +183,10 @@ */

uint64(value: string | number | bigint): this;
/**
* Write a 64-bit varint directly into the buffer. Accepts the value as
* split low/high 32-bit words.
*
* Ported from varint64write() to avoid the intermediate number[] buffer.
* See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/writer.js#L344
*/
private writeVarint64;
}

@@ -187,3 +203,3 @@ export declare class BinaryReader {

readonly len: number;
protected readonly buf: Uint8Array;
private readonly buf;
private readonly view;

@@ -205,7 +221,9 @@ constructor(buf: Uint8Array, decodeUtf8?: (bytes: Uint8Array, strict?: boolean) => string);

skip(wireType: WireType, fieldNo?: number, recursionLimit?: number): Uint8Array;
protected varint64: () => [number, number];
private varint64Lo;
private varint64Hi;
private varint64;
/**
* Throws error if position in byte array is out of range.
*/
protected assertBounds(): void;
private assertBounds;
/**

@@ -212,0 +230,0 @@ * Read a `uint32` field, an unsigned 32 bit varint.

@@ -14,5 +14,5 @@ // Copyright 2021-2026 Buf Technologies, Inc.

// limitations under the License.
import { varint32read, varint32write, varint64read, varint64write, } from "./varint.js";
import { varint32read, varint64read } from "./varint.js";
import { protoInt64 } from "../proto-int64.js";
import { getTextEncoding } from "./text-encoding.js";
import { emulateEncodeInto, getTextEncoding } from "./text-encoding.js";
/**

@@ -81,30 +81,51 @@ * Protobuf binary format wire types.

export class BinaryWriter {
constructor(encodeUtf8 = getTextEncoding().encodeUtf8) {
this.encodeUtf8 = encodeUtf8;
constructor(encodeUtf8) {
/**
* Previous fork states.
* Previous fork positions (the write position at the time
* `fork()` was called).
*/
this.stack = [];
this.chunks = [];
this.buf = [];
this.stackPos = [];
this.encodeUtf8Into = encodeUtf8
? emulateEncodeInto(encodeUtf8)
: getTextEncoding().encodeUtf8Into;
this.buffer = EMPTY_BUFFER;
this.viewCache = EMPTY_VIEW;
this.pos = 0;
}
ensureCapacity(size) {
const required = this.pos + size;
if (required > this.buffer.length) {
let newLen = this.buffer.length || INITIAL_SIZE;
while (newLen < required)
newLen *= 2;
const newBuf = new Uint8Array(newLen);
if (this.pos > 0)
newBuf.set(this.buffer);
this.buffer = newBuf;
}
}
/**
* The DataView over `buffer`, rebuilt only if the buffer has grown since it
* was last used.
*/
view() {
const bytes = this.buffer;
const view = this.viewCache;
// Since ensureCapacity() only ever replaces the buffer with a strictly larger one,
// equal lengths mean the view is still current. This is faster than comparing
// buffers directly.
if (view.byteLength === bytes.byteLength)
return view;
const newView = new DataView(bytes.buffer);
this.viewCache = newView;
return newView;
}
/**
* Return all bytes written and reset this writer.
*/
finish() {
if (this.buf.length) {
this.chunks.push(new Uint8Array(this.buf)); // flush the buffer
this.buf = [];
}
let len = 0;
for (let i = 0; i < this.chunks.length; i++)
len += this.chunks[i].length;
let bytes = new Uint8Array(len);
let offset = 0;
for (let i = 0; i < this.chunks.length; i++) {
bytes.set(this.chunks[i], offset);
offset += this.chunks[i].length;
}
this.chunks = [];
return bytes;
const result = this.buffer.slice(0, this.pos);
this.pos = 0;
this.stackPos = [];
return result;
}

@@ -118,5 +139,7 @@ /**

fork() {
this.stack.push({ chunks: this.chunks, buf: this.buf });
this.chunks = [];
this.buf = [];
this.stackPos.push(this.pos);
// Reserve room for the length prefix. Payloads under 128 bytes, fairly
// common, will need no copy in join().
this.ensureCapacity(DEFAULT_LEN_PREFIX_SIZE);
this.buffer[this.pos++] = 0;
return this;

@@ -129,13 +152,20 @@ }

join() {
// get chunk of fork
let chunk = this.finish();
// restore previous state
let prev = this.stack.pop();
if (!prev)
const forkPos = this.stackPos.pop();
if (forkPos === undefined)
throw new Error("invalid state, fork stack empty");
this.chunks = prev.chunks;
this.buf = prev.buf;
// write length of chunk as varint
this.uint32(chunk.byteLength);
return this.raw(chunk);
// fork() presumed the payload would fit the prefix it reserved. If it
// doesn't, we need to shift the bytes we just wrote.
const len = this.pos - forkPos - DEFAULT_LEN_PREFIX_SIZE;
const lenPrefixSize = varint32Size(len);
if (lenPrefixSize > DEFAULT_LEN_PREFIX_SIZE) {
// Widening pushes the payload past the end of the buffer, so grow first:
// copyWithin clamps to the buffer instead of throwing, so a short buffer
// would silently drop the tail of the payload.
this.ensureCapacity(lenPrefixSize - DEFAULT_LEN_PREFIX_SIZE);
this.buffer.copyWithin(forkPos + lenPrefixSize, forkPos + DEFAULT_LEN_PREFIX_SIZE, this.pos);
}
this.pos = forkPos;
this.uint32(len);
this.pos += len;
return this;
}

@@ -156,7 +186,5 @@ /**

raw(chunk) {
if (this.buf.length) {
this.chunks.push(new Uint8Array(this.buf));
this.buf = [];
}
this.chunks.push(chunk);
this.ensureCapacity(chunk.length);
this.buffer.set(chunk, this.pos);
this.pos += chunk.length;
return this;

@@ -169,8 +197,14 @@ }

assertUInt32(value);
// write value as varint 32, inlined for speed
// uint32 varints are at most 5 bytes; reserve once and avoid per-byte
// capacity checks.
this.ensureCapacity(5);
if (value < 0x80) {
this.buffer[this.pos++] = value;
return this;
}
while (value > 0x7f) {
this.buf.push((value & 0x7f) | 0x80);
value = value >>> 7;
this.buffer[this.pos++] = (value & 0x7f) | 0x80;
value >>>= 7;
}
this.buf.push(value);
this.buffer[this.pos++] = value;
return this;

@@ -183,3 +217,12 @@ }

assertInt32(value);
varint32write(value, this.buf);
if (value >= 0) {
return this.uint32(value);
}
// Negative: sign-extend to 64 bits, encodes to 10 bytes.
this.ensureCapacity(10);
for (let i = 0; i < 9; i++) {
this.buffer[this.pos++] = (value & 0x7f) | 0x80;
value >>= 7;
}
this.buffer[this.pos++] = 1;
return this;

@@ -191,3 +234,4 @@ }

bool(value) {
this.buf.push(value ? 1 : 0);
this.ensureCapacity(1);
this.buffer[this.pos++] = value ? 1 : 0;
return this;

@@ -199,3 +243,3 @@ }

bytes(value) {
this.uint32(value.byteLength); // write length of chunk as varint
this.uint32(value.byteLength);
return this.raw(value);

@@ -207,5 +251,45 @@ }

string(value) {
let chunk = this.encodeUtf8(value);
this.uint32(chunk.byteLength); // write length of chunk as varint
return this.raw(chunk);
// TextEncoder.encode() coerces its argument to string, but encodeInto()
// rejects non-strings.
if (typeof value !== "string") {
value = String(value);
}
const len = value.length;
// Fast path for ASCII.
if (len <= ASCII_MAX_LENGTH) {
this.ensureCapacity(len + 1);
const ascii = this.buffer;
let pos = this.pos;
ascii[pos++] = len;
let i = 0;
for (; i < len; i++) {
const code = value.charCodeAt(i);
if (code > 0x7f)
break;
ascii[pos++] = code;
}
if (i == len) {
this.pos = pos;
return this;
}
}
// encodeUtf8Into needs the full-length buffer upfront. The length prefix
// can be upto 5 bytes, and a UTF-16 code unit takes at most 3 UTF-8 bytes.
this.ensureCapacity(len * 3 + 5);
// The length prefix goes first, but the byte length is only known after
// encoding. We guess the final varint size here (assuming most text is
// ASCII) and then encode.
const lenPrefixSizeGuess = varint32Size(len);
const buf = this.buffer;
const start = this.pos;
const { written } = this.encodeUtf8Into(value, buf.subarray(start + lenPrefixSizeGuess));
// If our guess was incorrect, we need to shift the bytes we just wrote.
const lenPrefixSize = varint32Size(written);
if (lenPrefixSize != lenPrefixSizeGuess) {
buf.copyWithin(start + lenPrefixSize, start + lenPrefixSizeGuess, start + lenPrefixSizeGuess + written);
}
// Write the lenPrefix and advance the pos.
this.uint32(written);
this.pos += written;
return this;
}

@@ -217,5 +301,6 @@ /**

assertFloat32(value);
let chunk = new Uint8Array(4);
new DataView(chunk.buffer).setFloat32(0, value, true);
return this.raw(chunk);
this.ensureCapacity(4);
this.view().setFloat32(this.pos, value, true);
this.pos += 4;
return this;
}

@@ -226,5 +311,6 @@ /**

double(value) {
let chunk = new Uint8Array(8);
new DataView(chunk.buffer).setFloat64(0, value, true);
return this.raw(chunk);
this.ensureCapacity(8);
this.view().setFloat64(this.pos, value, true);
this.pos += 8;
return this;
}

@@ -236,5 +322,6 @@ /**

assertUInt32(value);
let chunk = new Uint8Array(4);
new DataView(chunk.buffer).setUint32(0, value, true);
return this.raw(chunk);
this.ensureCapacity(4);
this.view().setUint32(this.pos, value, true);
this.pos += 4;
return this;
}

@@ -246,5 +333,6 @@ /**

assertInt32(value);
let chunk = new Uint8Array(4);
new DataView(chunk.buffer).setInt32(0, value, true);
return this.raw(chunk);
this.ensureCapacity(4);
this.view().setInt32(this.pos, value, true);
this.pos += 4;
return this;
}

@@ -256,6 +344,4 @@ /**

assertInt32(value);
// zigzag encode
value = ((value << 1) ^ (value >> 31)) >>> 0;
varint32write(value, this.buf);
return this;
// zigzag encode then emit as uint32 varint
return this.uint32(((value << 1) ^ (value >> 31)) >>> 0);
}

@@ -266,6 +352,9 @@ /**

sfixed64(value) {
let chunk = new Uint8Array(8), view = new DataView(chunk.buffer), tc = protoInt64.enc(value);
view.setInt32(0, tc.lo, true);
view.setInt32(4, tc.hi, true);
return this.raw(chunk);
const tc = protoInt64.enc(value);
this.ensureCapacity(8);
const view = this.view();
view.setInt32(this.pos, tc.lo, true);
view.setInt32(this.pos + 4, tc.hi, true);
this.pos += 8;
return this;
}

@@ -276,6 +365,9 @@ /**

fixed64(value) {
let chunk = new Uint8Array(8), view = new DataView(chunk.buffer), tc = protoInt64.uEnc(value);
view.setInt32(0, tc.lo, true);
view.setInt32(4, tc.hi, true);
return this.raw(chunk);
const tc = protoInt64.uEnc(value);
this.ensureCapacity(8);
const view = this.view();
view.setInt32(this.pos, tc.lo, true);
view.setInt32(this.pos + 4, tc.hi, true);
this.pos += 8;
return this;
}

@@ -286,5 +378,4 @@ /**

int64(value) {
let tc = protoInt64.enc(value);
varint64write(tc.lo, tc.hi, this.buf);
return this;
const tc = protoInt64.enc(value);
return this.writeVarint64(tc.lo, tc.hi);
}

@@ -298,4 +389,3 @@ /**

sign = tc.hi >> 31, lo = (tc.lo << 1) ^ sign, hi = ((tc.hi << 1) | (tc.lo >>> 31)) ^ sign;
varint64write(lo, hi, this.buf);
return this;
return this.writeVarint64(lo, hi);
}

@@ -307,9 +397,91 @@ /**

const tc = protoInt64.uEnc(value);
varint64write(tc.lo, tc.hi, this.buf);
return this.writeVarint64(tc.lo, tc.hi);
}
/**
* Write a 64-bit varint directly into the buffer. Accepts the value as
* split low/high 32-bit words.
*
* Ported from varint64write() to avoid the intermediate number[] buffer.
* See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/writer.js#L344
*/
writeVarint64(lo, hi) {
// Worst case: 10 bytes.
this.ensureCapacity(10);
const buf = this.buffer;
let pos = this.pos;
for (let i = 0; i < 28; i = i + 7) {
const shift = lo >>> i;
const hasNext = !(shift >>> 7 == 0 && hi == 0);
buf[pos++] = (hasNext ? shift | 0x80 : shift) & 0xff;
if (!hasNext) {
this.pos = pos;
return this;
}
}
const splitBits = ((lo >>> 28) & 0x0f) | ((hi & 0x07) << 4);
const hasMoreBits = !(hi >> 3 == 0);
buf[pos++] = (hasMoreBits ? splitBits | 0x80 : splitBits) & 0xff;
if (!hasMoreBits) {
this.pos = pos;
return this;
}
for (let i = 3; i < 31; i = i + 7) {
const shift = hi >>> i;
const hasNext = !(shift >>> 7 == 0);
buf[pos++] = (hasNext ? shift | 0x80 : shift) & 0xff;
if (!hasNext) {
this.pos = pos;
return this;
}
}
buf[pos++] = (hi >>> 31) & 0x01;
this.pos = pos;
return this;
}
}
/**
* Capacity of the buffer allocated by the first write..
*/
const INITIAL_SIZE = 128;
/**
* Bytes `fork()` reserves for the length prefix, betting that the payload will
* be under 128 bytes. `join()` fills them in, and widens them if the bet was
* wrong.
*/
const DEFAULT_LEN_PREFIX_SIZE = 1;
/**
* Shared empty buffer used as the initial value before the first write.
* Avoids allocating and zeroing `INITIAL_SIZE` bytes per BinaryWriter when a
* writer is only used for a tiny message (or not used at all).
*/
const EMPTY_BUFFER = new Uint8Array(0);
/**
* Shared empty view, paired with `EMPTY_BUFFER`. Never written to: any
* fixed-width write first grows the buffer, which replaces this view.
*/
const EMPTY_VIEW = new DataView(EMPTY_BUFFER.buffer);
/**
* Longest string on the ASCII fast paths. Must stay below 0x80, so
* that the writer's length prefix always fits a single varint byte.
*/
const ASCII_MAX_LENGTH = 32;
/**
* Number of bytes needed to encode `value` as an unsigned 32-bit varint.
*/
function varint32Size(value) {
if (value < 0x80)
return 1;
if (value < 0x4000)
return 2;
if (value < 0x200000)
return 3;
if (value < 0x10000000)
return 4;
return 5;
}
export class BinaryReader {
constructor(buf, decodeUtf8 = getTextEncoding().decodeUtf8) {
this.decodeUtf8 = decodeUtf8;
this.varint64Lo = 0;
this.varint64Hi = 0;
this.varint64 = varint64read; // dirty cast for `this`

@@ -415,3 +587,4 @@ /**

int64() {
return protoInt64.dec(...this.varint64());
this.varint64();
return protoInt64.dec(this.varint64Lo, this.varint64Hi);
}

@@ -422,3 +595,4 @@ /**

uint64() {
return protoInt64.uDec(...this.varint64());
this.varint64();
return protoInt64.uDec(this.varint64Lo, this.varint64Hi);
}

@@ -429,3 +603,5 @@ /**

sint64() {
let [lo, hi] = this.varint64();
this.varint64();
let lo = this.varint64Lo;
let hi = this.varint64Hi;
// decode zig zag

@@ -441,4 +617,10 @@ let s = -(lo & 1);

bool() {
let [lo, hi] = this.varint64();
return lo !== 0 || hi !== 0;
// Fast path: most bools are 0x0 or 0x1.
const b = this.buf[this.pos];
if (b < 0x80) {
this.pos++;
return b !== 0;
}
this.varint64();
return this.varint64Lo !== 0 || this.varint64Hi !== 0;
}

@@ -499,3 +681,17 @@ /**

string(strict) {
return this.decodeUtf8(this.bytes(), strict);
const bytes = this.bytes();
const len = bytes.length;
// Fast path for ASCII.
if (len <= ASCII_MAX_LENGTH) {
const codes = new Array(len);
for (let i = 0; i < len; i++) {
const byte = bytes[i];
if (byte > 0x7f) {
return this.decodeUtf8(bytes, strict);
}
codes[i] = byte;
}
return String.fromCharCode.apply(String, codes);
}
return this.decodeUtf8(bytes, strict);
}

@@ -502,0 +698,0 @@ }

export * from "./binary-encoding.js";
export * from "./base64-encoding.js";
export * from "./text-encoding.js";
export { getTextEncoding, configureTextEncoding } from "./text-encoding.js";
export * from "./text-format.js";
export * from "./size-delimited.js";

@@ -16,4 +16,4 @@ // Copyright 2021-2026 Buf Technologies, Inc.

export * from "./base64-encoding.js";
export * from "./text-encoding.js";
export { getTextEncoding, configureTextEncoding } from "./text-encoding.js";
export * from "./text-format.js";
export * from "./size-delimited.js";

@@ -11,2 +11,8 @@ interface TextEncoding {

/**
* Encode UTF-8 text to a Uint8Array. The destination must be large enough.
*/
encodeUtf8Into: (text: string, dest: Uint8Array) => {
written: number;
};
/**
* Decode UTF-8 text from binary. If `strict` is true, throw on invalid byte

@@ -18,2 +24,3 @@ * sequences instead of silently substituting U+FFFD. Implementations that

}
type TextEncodingConfig = Omit<TextEncoding, "encodeUtf8Into"> & Partial<Pick<TextEncoding, "encodeUtf8Into">>;
/**

@@ -25,7 +32,17 @@ * Protobuf-ES requires the Text Encoding API to convert UTF-8 from and to

*
* Providing `encodeUtf8Into` is optional for backwards compatibility. If it
* is omitted, we emulate it with a wrapper that calls `encodeUtf8`.
*
* Note that the Text Encoding API does not provide a way to validate UTF-8.
* Our implementation falls back to use encodeURIComponent().
* Our implementation uses String.prototype.isWellFormed, and falls back
* to use encodeURIComponent().
*/
export declare function configureTextEncoding(textEncoding: TextEncoding): void;
export declare function configureTextEncoding(textEncoding: TextEncodingConfig): void;
export declare function getTextEncoding(): TextEncoding;
/**
* Simplistic polyfill for encodeUtf8Into.
*
* @private
*/
export declare function emulateEncodeInto(encodeUtf8: (str: string) => Uint8Array): TextEncoding["encodeUtf8Into"];
export {};

@@ -21,25 +21,33 @@ // Copyright 2021-2026 Buf Technologies, Inc.

*
* Providing `encodeUtf8Into` is optional for backwards compatibility. If it
* is omitted, we emulate it with a wrapper that calls `encodeUtf8`.
*
* Note that the Text Encoding API does not provide a way to validate UTF-8.
* Our implementation falls back to use encodeURIComponent().
* Our implementation uses String.prototype.isWellFormed, and falls back
* to use encodeURIComponent().
*/
export function configureTextEncoding(textEncoding) {
globalThis[symbol] = textEncoding;
var _a;
globalThis[symbol] = Object.assign(Object.assign({}, textEncoding), { encodeUtf8Into: (_a = textEncoding.encodeUtf8Into) !== null && _a !== void 0 ? _a : emulateEncodeInto(textEncoding.encodeUtf8.bind(textEncoding)) });
}
export function getTextEncoding() {
if (globalThis[symbol] == undefined) {
const te = new globalThis.TextEncoder();
const td = new globalThis.TextDecoder();
let tdStrict;
globalThis[symbol] = {
const globals = globalThis;
if (!globals[symbol]) {
const textEncoder = new globals.TextEncoder();
const textDecoder = new globals.TextDecoder();
let textDecoderStrict;
const config = {
encodeUtf8(text) {
return te.encode(text);
return textEncoder.encode(text);
},
decodeUtf8(bytes, strict) {
if (strict) {
if (tdStrict === undefined) {
tdStrict = new globalThis.TextDecoder("utf-8", { fatal: true });
if (!textDecoderStrict) {
textDecoderStrict = new globals.TextDecoder("utf-8", {
fatal: true,
});
}
return tdStrict.decode(bytes);
return textDecoderStrict.decode(bytes);
}
return td.decode(bytes);
return textDecoder.decode(bytes);
},

@@ -56,4 +64,29 @@ checkUtf8(text) {

};
// If encodeInto is available, use it. Otherwise, configureTextEncoding
// fills in a slower fallback that uses encodeUtf8.
if (textEncoder.encodeInto) {
config.encodeUtf8Into = textEncoder.encodeInto.bind(textEncoder);
}
// Native String.prototype.isWellFormed, if the runtime provides it.
const nativeStringIsWellFormed = String.prototype.isWellFormed;
if (nativeStringIsWellFormed) {
config.checkUtf8 = (text) => {
return nativeStringIsWellFormed.call(text);
};
}
configureTextEncoding(config);
}
return globalThis[symbol];
return globals[symbol];
}
/**
* Simplistic polyfill for encodeUtf8Into.
*
* @private
*/
export function emulateEncodeInto(encodeUtf8) {
return (text, dest) => {
const bytes = encodeUtf8(text);
dest.set(bytes);
return { written: bytes.byteLength };
};
}
/**
* Read a 64 bit varint as two JS numbers.
*
* Returns tuple:
* [0]: low bits
* [1]: high bits
* Stores the low and high words on the reader.
*

@@ -12,3 +10,3 @@ * Copyright 2008 Google Inc. All rights reserved.

*/
export declare function varint64read<T extends ReaderLike>(this: T): [number, number];
export declare function varint64read<T extends ReaderLike>(this: T): void;
/**

@@ -69,4 +67,6 @@ * Write a 64 bit varint, given as two JS numbers, to the given bytes array.

len: number;
varint64Lo: number;
varint64Hi: number;
assertBounds(): void;
};
export {};

@@ -36,5 +36,3 @@ // Copyright 2008 Google Inc. All rights reserved.

*
* Returns tuple:
* [0]: low bits
* [1]: high bits
* Stores the low and high words on the reader.
*

@@ -46,27 +44,38 @@ * Copyright 2008 Google Inc. All rights reserved.

export function varint64read() {
let lowBits = 0;
let highBits = 0;
const buf = this.buf;
let pos = this.pos;
let lo = 0;
let hi = 0;
for (let shift = 0; shift < 28; shift += 7) {
let b = this.buf[this.pos++];
lowBits |= (b & 0x7f) << shift;
const b = buf[pos++];
lo |= (b & 0x7f) << shift;
if ((b & 0x80) == 0) {
this.pos = pos;
this.assertBounds();
return [lowBits, highBits];
this.varint64Lo = lo;
this.varint64Hi = hi;
return;
}
}
let middleByte = this.buf[this.pos++];
const middleByte = buf[pos++];
// last four bits of the first 32 bit number
lowBits |= (middleByte & 0x0f) << 28;
lo |= (middleByte & 0x0f) << 28;
// 3 upper bits are part of the next 32 bit number
highBits = (middleByte & 0x70) >> 4;
hi = (middleByte & 0x70) >> 4;
if ((middleByte & 0x80) == 0) {
this.pos = pos;
this.assertBounds();
return [lowBits, highBits];
this.varint64Lo = lo;
this.varint64Hi = hi;
return;
}
for (let shift = 3; shift <= 31; shift += 7) {
let b = this.buf[this.pos++];
highBits |= (b & 0x7f) << shift;
const b = buf[pos++];
hi |= (b & 0x7f) << shift;
if ((b & 0x80) == 0) {
this.pos = pos;
this.assertBounds();
return [lowBits, highBits];
this.varint64Lo = lo;
this.varint64Hi = hi;
return;
}

@@ -259,2 +268,6 @@ }

export function varint32write(value, bytes) {
if (value >>> 0 < 0x80) {
bytes.push(value);
return;
}
if (value >= 0) {

@@ -283,10 +296,10 @@ // write value as varint 32

let b = this.buf[this.pos++];
let result = b & 0x7f;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();
return result;
return b;
}
let result = b & 0x7f;
b = this.buf[this.pos++];
result |= (b & 0x7f) << 7;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();

@@ -297,3 +310,3 @@ return result;

result |= (b & 0x7f) << 14;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();

@@ -304,3 +317,3 @@ return result;

result |= (b & 0x7f) << 21;
if ((b & 0x80) == 0) {
if ((b & 0x80) === 0) {
this.assertBounds();

@@ -314,7 +327,6 @@ return result;

b = this.buf[this.pos++];
if ((b & 0x80) != 0)
if ((b & 0x80) !== 0)
throw new Error("invalid varint");
this.assertBounds();
// Result can have 32 bits, convert it to unsigned
return result >>> 0;
}

@@ -48,15 +48,15 @@ // Copyright 2021-2026 Buf Technologies, Inc.

}
const wrapperTypeNames = /*@__PURE__*/ new Set([
"google.protobuf.DoubleValue",
"google.protobuf.FloatValue",
"google.protobuf.Int64Value",
"google.protobuf.UInt64Value",
"google.protobuf.Int32Value",
"google.protobuf.UInt32Value",
"google.protobuf.BoolValue",
"google.protobuf.StringValue",
"google.protobuf.BytesValue",
]);
function isWrapperTypeName(name) {
return (name.startsWith("google.protobuf.") &&
[
"DoubleValue",
"FloatValue",
"Int64Value",
"UInt64Value",
"Int32Value",
"UInt32Value",
"BoolValue",
"StringValue",
"BytesValue",
].includes(name.substring(16)));
return wrapperTypeNames.has(name);
}
{
"name": "@bufbuild/protobuf",
"version": "2.13.0",
"version": "2.14.0",
"license": "(Apache-2.0 AND BSD-3-Clause)",
"description": "Protocol Buffers for ECMAScript. The only JavaScript Protobuf library that is fully-compliant with Protobuf conformance tests.",
"description": "Protocol Buffers for ECMAScript. Fully compliant with the Protobuf conformance tests.",
"keywords": [

@@ -7,0 +7,0 @@ "protobuf",

+10
-13

@@ -11,17 +11,14 @@ # @bufbuild/protobuf

**Protobuf-ES** is a solid, modern alternative to existing Protobuf implementations for the JavaScript ecosystem. It's
the first project in this space to provide a comprehensive plugin framework and decouple the base types from RPC
functionality.
**Protobuf-ES** is a solid, modern alternative to existing Protobuf implementations for the JavaScript ecosystem. It
provides a comprehensive plugin framework and decouples the base types from RPC functionality.
Some additional features that set it apart from the others:
Some additional features:
- ECMAScript module support
- First-class TypeScript support
- Generation of idiomatic JavaScript and TypeScript code
- Generation of [much smaller bundles](https://github.com/bufbuild/protobuf-es/tree/main/packages/bundle-size/)
- Implementation of all proto3 features, including the [canonical JSON format](https://protobuf.dev/programming-guides/proto3/#json)
- Implementation of all proto2 features, including extensions and the text format
- Usage of standard JavaScript APIs instead of the [Closure Library](http://googlecode.blogspot.com/2009/11/introducing-closure-tools.html)
- Compatibility is covered by the Protocol Buffers [conformance tests](https://github.com/bufbuild/protobuf-es/tree/main/packages/protobuf-conformance/)
- Descriptor and reflection support
- Generates pure TypeScript
- Plain message objects, no getters/setters
- Reflection, registries, and custom options
- 100% conformant against the official Protobuf test suite
- Standard plugin-based generation, works with the Buf CLI as well as `protoc`
- Write your own code generators with [@bufbuild/protoplugin](https://www.npmjs.com/package/@bufbuild/protoplugin)
- Pairs with [@connectrpc/connect](https://www.npmjs.com/package/@connectrpc/connect) for RPC and [@bufbuild/protovalidate](https://www.npmjs.com/package/@bufbuild/protovalidate) for validation

@@ -28,0 +25,0 @@ ## Installation