@bufbuild/protobuf 2.12.1 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +2 -2
  2. package/dist/commonjs/codegenv1/symbols.d.ts +5 -0
  3. package/dist/commonjs/codegenv2/enum.d.ts +15 -4
  4. package/dist/commonjs/codegenv2/enum.js +18 -0
  5. package/dist/commonjs/codegenv2/symbols.d.ts +10 -0
  6. package/dist/commonjs/codegenv2/symbols.js +2 -0
  7. package/dist/commonjs/create.js +3 -3
  8. package/dist/commonjs/descriptors.d.ts +2 -2
  9. package/dist/commonjs/index.d.ts +1 -0
  10. package/dist/commonjs/index.js +1 -0
  11. package/dist/commonjs/reflect/reflect.js +3 -2
  12. package/dist/commonjs/reflect/unsafe.js +1 -1
  13. package/dist/commonjs/registry.js +19 -19
  14. package/dist/commonjs/to-binary.js +1 -1
  15. package/dist/commonjs/to-json.js +2 -2
  16. package/dist/commonjs/txtpb/from-text.d.ts +35 -0
  17. package/dist/commonjs/txtpb/from-text.js +684 -0
  18. package/dist/commonjs/txtpb/index.d.ts +4 -0
  19. package/dist/commonjs/txtpb/index.js +21 -0
  20. package/dist/commonjs/txtpb/is-group-like.d.ts +19 -0
  21. package/dist/commonjs/txtpb/is-group-like.js +47 -0
  22. package/dist/commonjs/txtpb/reader.d.ts +91 -0
  23. package/dist/commonjs/txtpb/reader.js +481 -0
  24. package/dist/commonjs/txtpb/to-text.d.ts +34 -0
  25. package/dist/commonjs/txtpb/to-text.js +358 -0
  26. package/dist/commonjs/txtpb/writer.d.ts +76 -0
  27. package/dist/commonjs/txtpb/writer.js +220 -0
  28. package/dist/commonjs/types.d.ts +7 -0
  29. package/dist/commonjs/unknown-enum.d.ts +10 -0
  30. package/dist/commonjs/unknown-enum.js +26 -0
  31. package/dist/commonjs/wire/size-delimited.d.ts +19 -1
  32. package/dist/commonjs/wire/size-delimited.js +54 -16
  33. package/dist/esm/codegenv1/symbols.d.ts +5 -0
  34. package/dist/esm/codegenv2/enum.d.ts +15 -4
  35. package/dist/esm/codegenv2/enum.js +17 -0
  36. package/dist/esm/codegenv2/symbols.d.ts +10 -0
  37. package/dist/esm/codegenv2/symbols.js +2 -0
  38. package/dist/esm/create.js +3 -3
  39. package/dist/esm/descriptors.d.ts +2 -2
  40. package/dist/esm/index.d.ts +1 -0
  41. package/dist/esm/index.js +1 -0
  42. package/dist/esm/reflect/reflect.js +3 -2
  43. package/dist/esm/reflect/unsafe.js +1 -1
  44. package/dist/esm/registry.js +19 -19
  45. package/dist/esm/to-binary.js +1 -1
  46. package/dist/esm/to-json.js +2 -2
  47. package/dist/esm/txtpb/from-text.d.ts +35 -0
  48. package/dist/esm/txtpb/from-text.js +680 -0
  49. package/dist/esm/txtpb/index.d.ts +4 -0
  50. package/dist/esm/txtpb/index.js +15 -0
  51. package/dist/esm/txtpb/is-group-like.d.ts +19 -0
  52. package/dist/esm/txtpb/is-group-like.js +44 -0
  53. package/dist/esm/txtpb/reader.d.ts +91 -0
  54. package/dist/esm/txtpb/reader.js +477 -0
  55. package/dist/esm/txtpb/to-text.d.ts +34 -0
  56. package/dist/esm/txtpb/to-text.js +355 -0
  57. package/dist/esm/txtpb/writer.d.ts +76 -0
  58. package/dist/esm/txtpb/writer.js +214 -0
  59. package/dist/esm/types.d.ts +7 -0
  60. package/dist/esm/unknown-enum.d.ts +10 -0
  61. package/dist/esm/unknown-enum.js +23 -0
  62. package/dist/esm/wire/size-delimited.d.ts +19 -1
  63. package/dist/esm/wire/size-delimited.js +54 -16
  64. package/package.json +16 -3
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // Copyright 2021-2026 Buf Technologies, Inc.
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.mergeFromText = exports.fromText = exports.toText = void 0;
17
+ var to_text_js_1 = require("./to-text.js");
18
+ Object.defineProperty(exports, "toText", { enumerable: true, get: function () { return to_text_js_1.toText; } });
19
+ var from_text_js_1 = require("./from-text.js");
20
+ Object.defineProperty(exports, "fromText", { enumerable: true, get: function () { return from_text_js_1.fromText; } });
21
+ Object.defineProperty(exports, "mergeFromText", { enumerable: true, get: function () { return from_text_js_1.mergeFromText; } });
@@ -0,0 +1,19 @@
1
+ import type { DescField, DescMessage } from "../descriptors.js";
2
+ /**
3
+ * Returns true if the field is structured like a proto2 group: a delimited
4
+ * message field whose name is the lowercase of its message type name, declared
5
+ * in the same scope as that message.
6
+ *
7
+ * The text format addresses such fields by their message type name (e.g.
8
+ * `MyGroup`) rather than their field name. This is a faithful port of
9
+ * protobuf-go's isGroupLike (internal/filedesc/desc.go), so editions delimited
10
+ * fields are treated exactly like proto2 groups.
11
+ *
12
+ * Testing `field.message` first narrows the DescField union to its three
13
+ * message-bearing variants (singular, list, and map value) — all of which carry
14
+ * `delimitedEncoding` — so it is in scope below without a cast. Maps are
15
+ * excluded automatically, because their `delimitedEncoding` is always false.
16
+ */
17
+ export declare function isGroupLike(field: DescField): field is DescField & {
18
+ message: DescMessage;
19
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ // Copyright 2021-2026 Buf Technologies, Inc.
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.isGroupLike = isGroupLike;
17
+ /**
18
+ * Returns true if the field is structured like a proto2 group: a delimited
19
+ * message field whose name is the lowercase of its message type name, declared
20
+ * in the same scope as that message.
21
+ *
22
+ * The text format addresses such fields by their message type name (e.g.
23
+ * `MyGroup`) rather than their field name. This is a faithful port of
24
+ * protobuf-go's isGroupLike (internal/filedesc/desc.go), so editions delimited
25
+ * fields are treated exactly like proto2 groups.
26
+ *
27
+ * Testing `field.message` first narrows the DescField union to its three
28
+ * message-bearing variants (singular, list, and map value) — all of which carry
29
+ * `delimitedEncoding` — so it is in scope below without a cast. Maps are
30
+ * excluded automatically, because their `delimitedEncoding` is always false.
31
+ */
32
+ function isGroupLike(field) {
33
+ // Groups are always delimited-encoded message fields.
34
+ if (field.message === undefined || !field.delimitedEncoding) {
35
+ return false;
36
+ }
37
+ // Group fields are always named after the lowercase message type name.
38
+ if (field.message.name.toLowerCase() !== field.name) {
39
+ return false;
40
+ }
41
+ // Groups can only be defined in the file they are used in.
42
+ if (field.message.file !== field.parent.file) {
43
+ return false;
44
+ }
45
+ // Group messages are always defined in the same scope as the field.
46
+ return field.message.parent === field.parent;
47
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * A lexical token of the protobuf text format.
3
+ *
4
+ * This is a discriminated union keyed by `type`: punctuation tokens carry no
5
+ * payload, identifiers carry their text, string tokens carry their decoded
6
+ * bytes (the same bytes back both string and bytes fields, and bytes fields may
7
+ * hold sequences that are not valid UTF-8), and numbers carry their literal
8
+ * text plus enough classification for the parser to accept or reject them per
9
+ * field type.
10
+ *
11
+ * The minus sign before a number is its own token rather than part of the
12
+ * number, which keeps numeric sign handling in one place in the parser and,
13
+ * because whitespace and comments between tokens are insignificant, makes
14
+ * `- 42` mean `-42`, matching protobuf-go (decode_number.go). A minus glued to
15
+ * a letter is instead folded into a negative identifier (`-inf`/`-infinity`),
16
+ * because protobuf-go requires the sign glued for those literals — `- inf` is
17
+ * an error there, not negative infinity.
18
+ */
19
+ export type Token = {
20
+ readonly type: Structural | "eof";
21
+ } | {
22
+ readonly type: "identifier";
23
+ readonly value: string;
24
+ } | {
25
+ readonly type: "string";
26
+ readonly value: Uint8Array;
27
+ } | {
28
+ readonly type: "int";
29
+ readonly text: string;
30
+ readonly base: 8 | 10 | 16;
31
+ } | {
32
+ readonly type: "float";
33
+ readonly text: string;
34
+ };
35
+ /**
36
+ * The structural tokens, each the literal source character it represents.
37
+ */
38
+ type Structural = "{" | "}" | "<" | ">" | "[" | "]" | ":" | "," | ";" | "-";
39
+ /**
40
+ * A tokenizer for the protobuf text format.
41
+ *
42
+ * The parser drives it with `peek()` and `next()` (one-token lookahead) and,
43
+ * once it knows it is in a field-name position, asks for the contents of a
44
+ * bracketed name with `readTypeName()` — the `[...]` syntax for extensions and
45
+ * Any type URLs is ambiguous with the list syntax at the lexical level. The
46
+ * structure is modeled on the graphql-js lexer: a single scan position and a
47
+ * per-token reader that returns the decoded value.
48
+ */
49
+ export declare class Reader {
50
+ private readonly input;
51
+ private readonly length;
52
+ private pos;
53
+ private lookahead;
54
+ constructor(input: string);
55
+ /**
56
+ * Return the next token without consuming it.
57
+ */
58
+ peek(): Token;
59
+ /**
60
+ * Consume and return the next token.
61
+ */
62
+ next(): Token;
63
+ /**
64
+ * Read the contents of a bracketed name, used for extension fields and the
65
+ * expanded form of google.protobuf.Any. The opening `[` must already have
66
+ * been consumed with `next()`. Whitespace and comments inside the brackets
67
+ * are insignificant. Returns the inner name with the brackets removed, e.g.
68
+ * "pkg.Message.field" or "type.googleapis.com/pkg.Message".
69
+ *
70
+ * The text format grammar for this is incomplete, so we follow protobuf-go's
71
+ * parseTypeName: the prefix may contain URL characters, `/` separators, and
72
+ * well-formed percent-escapes, and the type name after the last `/` must be a
73
+ * dotted identifier.
74
+ */
75
+ readTypeName(): string;
76
+ private scan;
77
+ private skipSpace;
78
+ private scanIdentifier;
79
+ /**
80
+ * Scan a numeric literal. The sign is a separate token, so a number never
81
+ * starts with `-`. The literal must end at a delimiter, so `10f` is a float
82
+ * but `10bar`, `1.2.3`, `09`, and `0xZ` are errors.
83
+ */
84
+ private scanNumber;
85
+ private expectDelimiter;
86
+ private scanString;
87
+ private scanEscape;
88
+ private takeWhile;
89
+ private charAt;
90
+ }
91
+ export {};
@@ -0,0 +1,481 @@
1
+ "use strict";
2
+ // Copyright 2021-2026 Buf Technologies, Inc.
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.Reader = void 0;
17
+ const text_encoding_js_1 = require("../wire/text-encoding.js");
18
+ const tokenEof = { type: "eof" };
19
+ /**
20
+ * A tokenizer for the protobuf text format.
21
+ *
22
+ * The parser drives it with `peek()` and `next()` (one-token lookahead) and,
23
+ * once it knows it is in a field-name position, asks for the contents of a
24
+ * bracketed name with `readTypeName()` — the `[...]` syntax for extensions and
25
+ * Any type URLs is ambiguous with the list syntax at the lexical level. The
26
+ * structure is modeled on the graphql-js lexer: a single scan position and a
27
+ * per-token reader that returns the decoded value.
28
+ */
29
+ class Reader {
30
+ constructor(input) {
31
+ this.pos = 0;
32
+ // A leading byte-order mark is insignificant; skip it like protobuf-go's
33
+ // tokenizer does.
34
+ this.input = input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
35
+ this.length = this.input.length;
36
+ }
37
+ /**
38
+ * Return the next token without consuming it.
39
+ */
40
+ peek() {
41
+ if (this.lookahead === undefined) {
42
+ this.lookahead = this.scan();
43
+ }
44
+ return this.lookahead;
45
+ }
46
+ /**
47
+ * Consume and return the next token.
48
+ */
49
+ next() {
50
+ const tok = this.peek();
51
+ this.lookahead = undefined;
52
+ return tok;
53
+ }
54
+ /**
55
+ * Read the contents of a bracketed name, used for extension fields and the
56
+ * expanded form of google.protobuf.Any. The opening `[` must already have
57
+ * been consumed with `next()`. Whitespace and comments inside the brackets
58
+ * are insignificant. Returns the inner name with the brackets removed, e.g.
59
+ * "pkg.Message.field" or "type.googleapis.com/pkg.Message".
60
+ *
61
+ * The text format grammar for this is incomplete, so we follow protobuf-go's
62
+ * parseTypeName: the prefix may contain URL characters, `/` separators, and
63
+ * well-formed percent-escapes, and the type name after the last `/` must be a
64
+ * dotted identifier.
65
+ */
66
+ readTypeName() {
67
+ let name = "";
68
+ for (;;) {
69
+ this.skipSpace();
70
+ const c = this.charAt(this.pos);
71
+ if (c === undefined) {
72
+ throw new Error("unterminated [...] name");
73
+ }
74
+ if (c === "]") {
75
+ this.pos++;
76
+ break;
77
+ }
78
+ if (c === "/") {
79
+ name += "/";
80
+ this.pos++;
81
+ }
82
+ else if (c === "%") {
83
+ if (!isHexDigit(this.charAt(this.pos + 1)) ||
84
+ !isHexDigit(this.charAt(this.pos + 2))) {
85
+ throw new Error("invalid percent-escape in [...] name");
86
+ }
87
+ name += this.input.substring(this.pos, this.pos + 3);
88
+ this.pos += 3;
89
+ }
90
+ else if (isUrlChar(c)) {
91
+ name += c;
92
+ this.pos++;
93
+ }
94
+ else {
95
+ throw new Error(`unexpected ${quoteChar(c)} in [...] name`);
96
+ }
97
+ }
98
+ validateTypeName(name);
99
+ return name;
100
+ }
101
+ scan() {
102
+ this.skipSpace();
103
+ const c = this.charAt(this.pos);
104
+ if (c === undefined) {
105
+ return tokenEof;
106
+ }
107
+ switch (c) {
108
+ case "{":
109
+ case "}":
110
+ case "<":
111
+ case ">":
112
+ case "[":
113
+ case "]":
114
+ case ":":
115
+ case ",":
116
+ case ";":
117
+ this.pos++;
118
+ return { type: c };
119
+ case "-":
120
+ // A "-" glued to a letter begins a negative identifier (-inf or
121
+ // -infinity); otherwise it is a sign token. A number may have whitespace
122
+ // between the sign and the digits, so the sign is a separate token the
123
+ // parser reassembles; a float literal may not, matching protobuf-go,
124
+ // where inf/infinity parse through the identifier path with the sign
125
+ // glued (so "- inf" is an error but "- 42" is -42).
126
+ if (isLetter(this.charAt(this.pos + 1))) {
127
+ return this.scanIdentifier();
128
+ }
129
+ this.pos++;
130
+ return { type: "-" };
131
+ case '"':
132
+ case "'":
133
+ return this.scanString(c);
134
+ }
135
+ if (isDigit(c)) {
136
+ return this.scanNumber();
137
+ }
138
+ if (c === "." && isDigit(this.charAt(this.pos + 1))) {
139
+ return this.scanNumber();
140
+ }
141
+ if (isLetter(c)) {
142
+ return this.scanIdentifier();
143
+ }
144
+ throw new Error(`unexpected ${quoteChar(c)}`);
145
+ }
146
+ skipSpace() {
147
+ for (;;) {
148
+ const c = this.input[this.pos];
149
+ if (c === " " ||
150
+ c === "\t" ||
151
+ c === "\n" ||
152
+ c === "\r" ||
153
+ c === "\v" ||
154
+ c === "\f") {
155
+ this.pos++;
156
+ }
157
+ else if (c === "#") {
158
+ // A comment runs to the end of the line.
159
+ this.pos++;
160
+ while (this.pos < this.length && this.input[this.pos] !== "\n") {
161
+ this.pos++;
162
+ }
163
+ }
164
+ else {
165
+ return;
166
+ }
167
+ }
168
+ }
169
+ scanIdentifier() {
170
+ const start = this.pos;
171
+ if (this.charAt(this.pos) === "-") {
172
+ this.pos++; // a glued negative identifier such as -inf
173
+ }
174
+ this.pos++; // the first letter (the caller guarantees one is present)
175
+ while (isLetterOrDigit(this.charAt(this.pos))) {
176
+ this.pos++;
177
+ }
178
+ return { type: "identifier", value: this.input.substring(start, this.pos) };
179
+ }
180
+ /**
181
+ * Scan a numeric literal. The sign is a separate token, so a number never
182
+ * starts with `-`. The literal must end at a delimiter, so `10f` is a float
183
+ * but `10bar`, `1.2.3`, `09`, and `0xZ` are errors.
184
+ */
185
+ scanNumber() {
186
+ var _a, _b, _c, _d;
187
+ const start = this.pos;
188
+ if (this.input[this.pos] === "0" &&
189
+ /[xX]/.test((_a = this.charAt(this.pos + 1)) !== null && _a !== void 0 ? _a : "")) {
190
+ // Hexadecimal: `0x` followed by one or more hex digits.
191
+ this.pos += 2;
192
+ const digits = this.pos;
193
+ while (isHexDigit(this.charAt(this.pos))) {
194
+ this.pos++;
195
+ }
196
+ if (this.pos === digits) {
197
+ throw new Error("invalid hexadecimal literal");
198
+ }
199
+ this.expectDelimiter();
200
+ return {
201
+ type: "int",
202
+ text: this.input.substring(start, this.pos),
203
+ base: 16,
204
+ };
205
+ }
206
+ if (this.input[this.pos] === "0" &&
207
+ isOctalDigit(this.charAt(this.pos + 1))) {
208
+ // Octal: a leading `0` followed by octal digits. A subsequent non-octal
209
+ // digit (as in `078`) ends the run, and the delimiter check rejects it.
210
+ this.pos++;
211
+ while (isOctalDigit(this.charAt(this.pos))) {
212
+ this.pos++;
213
+ }
214
+ this.expectDelimiter();
215
+ return {
216
+ type: "int",
217
+ text: this.input.substring(start, this.pos),
218
+ base: 8,
219
+ };
220
+ }
221
+ // A decimal integer or a floating point literal. A leading "0" stands
222
+ // alone (octal and hex were handled above), so the delimiter check below
223
+ // rejects a following digit — `08` and `09` are malformed, not decimal.
224
+ let isFloat = false;
225
+ if (this.charAt(this.pos) === "0") {
226
+ this.pos++;
227
+ }
228
+ else {
229
+ while (isDigit(this.charAt(this.pos))) {
230
+ this.pos++;
231
+ }
232
+ }
233
+ if (this.charAt(this.pos) === ".") {
234
+ isFloat = true;
235
+ this.pos++;
236
+ while (isDigit(this.charAt(this.pos))) {
237
+ this.pos++;
238
+ }
239
+ }
240
+ if (/[eE]/.test((_b = this.charAt(this.pos)) !== null && _b !== void 0 ? _b : "")) {
241
+ isFloat = true;
242
+ this.pos++;
243
+ if (/[+-]/.test((_c = this.charAt(this.pos)) !== null && _c !== void 0 ? _c : "")) {
244
+ this.pos++;
245
+ }
246
+ const digits = this.pos;
247
+ while (isDigit(this.charAt(this.pos))) {
248
+ this.pos++;
249
+ }
250
+ if (this.pos === digits) {
251
+ throw new Error("invalid exponent");
252
+ }
253
+ }
254
+ // A trailing `f`/`F` marks a float and is not part of the value passed to
255
+ // Number(); capture the end before consuming it so it stays out of the text.
256
+ const end = this.pos;
257
+ if (/[fF]/.test((_d = this.charAt(this.pos)) !== null && _d !== void 0 ? _d : "")) {
258
+ isFloat = true;
259
+ this.pos++;
260
+ }
261
+ this.expectDelimiter();
262
+ const text = this.input.substring(start, end);
263
+ return isFloat ? { type: "float", text } : { type: "int", text, base: 10 };
264
+ }
265
+ // A number must be terminated by a delimiter — any character that cannot
266
+ // continue a name or number. This rejects `09`, `0xZ`, `1.2.3`, and `5bar`.
267
+ expectDelimiter() {
268
+ const c = this.charAt(this.pos);
269
+ if (c !== undefined &&
270
+ (isLetterOrDigit(c) || c === "-" || c === "+" || c === ".")) {
271
+ throw new Error("invalid number");
272
+ }
273
+ }
274
+ scanString(quote) {
275
+ this.pos++; // opening quote
276
+ const bytes = [];
277
+ // Literal characters are accumulated as a run and encoded as UTF-8 in one
278
+ // batch when the run ends (at an escape or the closing quote). Escapes
279
+ // contribute their bytes directly.
280
+ let runStart = this.pos;
281
+ for (;;) {
282
+ const c = this.charAt(this.pos);
283
+ if (c === undefined) {
284
+ throw new Error("unterminated string");
285
+ }
286
+ if (c === quote) {
287
+ pushUtf8(bytes, this.input.substring(runStart, this.pos));
288
+ this.pos++; // closing quote
289
+ return { type: "string", value: new Uint8Array(bytes) };
290
+ }
291
+ if (c === "\\") {
292
+ pushUtf8(bytes, this.input.substring(runStart, this.pos));
293
+ this.pos++; // backslash
294
+ this.scanEscape(bytes);
295
+ runStart = this.pos;
296
+ continue;
297
+ }
298
+ // A raw newline or NUL is not allowed in a string, matching protobuf-go.
299
+ if (c === "\n" || c === "\0") {
300
+ throw new Error(`invalid ${quoteChar(c)} in string`);
301
+ }
302
+ this.pos++;
303
+ }
304
+ }
305
+ scanEscape(bytes) {
306
+ const c = this.charAt(this.pos);
307
+ if (c === undefined) {
308
+ throw new Error("unterminated escape sequence");
309
+ }
310
+ switch (c) {
311
+ case '"':
312
+ case "'":
313
+ case "\\":
314
+ case "?":
315
+ bytes.push(c.charCodeAt(0));
316
+ this.pos++;
317
+ return;
318
+ case "a":
319
+ bytes.push(0x07);
320
+ this.pos++;
321
+ return;
322
+ case "b":
323
+ bytes.push(0x08);
324
+ this.pos++;
325
+ return;
326
+ case "f":
327
+ bytes.push(0x0c);
328
+ this.pos++;
329
+ return;
330
+ case "n":
331
+ bytes.push(0x0a);
332
+ this.pos++;
333
+ return;
334
+ case "r":
335
+ bytes.push(0x0d);
336
+ this.pos++;
337
+ return;
338
+ case "t":
339
+ bytes.push(0x09);
340
+ this.pos++;
341
+ return;
342
+ case "v":
343
+ bytes.push(0x0b);
344
+ this.pos++;
345
+ return;
346
+ case "x": {
347
+ this.pos++;
348
+ const hex = this.takeWhile(isHexDigit, 2);
349
+ if (hex.length === 0) {
350
+ throw new Error("invalid hex escape \\x");
351
+ }
352
+ bytes.push(parseInt(hex, 16));
353
+ return;
354
+ }
355
+ case "u":
356
+ case "U": {
357
+ this.pos++;
358
+ const width = c === "u" ? 4 : 8;
359
+ const hex = this.takeWhile(isHexDigit, width);
360
+ if (hex.length !== width) {
361
+ throw new Error(`invalid unicode escape \\${c}`);
362
+ }
363
+ const code = parseInt(hex, 16);
364
+ // Reject surrogate code points and values beyond U+10FFFF. We
365
+ // deliberately do NOT combine an adjacent `\u` low surrogate into a
366
+ // pair the way protobuf-go does: the conformance suite (the
367
+ // StringLiteral*Surrogate* cases) requires every surrogate escape, lone
368
+ // or paired, to be a parse error. Do not "fix" this toward Go.
369
+ if (code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)) {
370
+ throw new Error(`invalid unicode escape \\${c}${hex}`);
371
+ }
372
+ pushUtf8(bytes, String.fromCodePoint(code));
373
+ return;
374
+ }
375
+ default:
376
+ if (isOctalDigit(c)) {
377
+ const oct = this.takeWhile(isOctalDigit, 3);
378
+ const value = parseInt(oct, 8);
379
+ if (value > 0xff) {
380
+ throw new Error(`octal escape \\${oct} out of range`);
381
+ }
382
+ bytes.push(value);
383
+ return;
384
+ }
385
+ throw new Error(`invalid escape \\${c}`);
386
+ }
387
+ }
388
+ // Consume up to `max` consecutive characters matching `pred` and return them.
389
+ takeWhile(pred, max) {
390
+ const start = this.pos;
391
+ while (this.pos < start + max && pred(this.charAt(this.pos))) {
392
+ this.pos++;
393
+ }
394
+ return this.input.substring(start, this.pos);
395
+ }
396
+ charAt(index) {
397
+ return index < this.length ? this.input[index] : undefined;
398
+ }
399
+ }
400
+ exports.Reader = Reader;
401
+ /**
402
+ * Validate a type name (and URL prefix) from a bracketed name, mirroring
403
+ * protobuf-go's parseTypeName. The type name is everything after the last `/`;
404
+ * the prefix before it is a URL that may carry extra characters and
405
+ * percent-escapes, but must not begin with `/`.
406
+ */
407
+ function validateTypeName(name) {
408
+ const lastSlash = name.lastIndexOf("/");
409
+ if (lastSlash >= 0 && name[0] === "/") {
410
+ throw new Error("invalid type name: empty URL host");
411
+ }
412
+ const typeName = name.substring(lastSlash + 1);
413
+ if (typeName.length === 0) {
414
+ throw new Error("invalid type name: empty");
415
+ }
416
+ for (const part of typeName.split(".")) {
417
+ if (part.length === 0) {
418
+ throw new Error("invalid type name: empty component");
419
+ }
420
+ }
421
+ for (const c of typeName) {
422
+ if (!(isLetterOrDigit(c) || c === "." || c === "-")) {
423
+ throw new Error(`unexpected ${quoteChar(c)} in type name`);
424
+ }
425
+ }
426
+ }
427
+ function isDigit(c) {
428
+ return c !== undefined && c >= "0" && c <= "9";
429
+ }
430
+ function isOctalDigit(c) {
431
+ return c !== undefined && c >= "0" && c <= "7";
432
+ }
433
+ function isHexDigit(c) {
434
+ return (c !== undefined &&
435
+ ((c >= "0" && c <= "9") || (c >= "a" && c <= "f") || (c >= "A" && c <= "F")));
436
+ }
437
+ function isLetter(c) {
438
+ return (c !== undefined &&
439
+ ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c === "_"));
440
+ }
441
+ function isLetterOrDigit(c) {
442
+ return isLetter(c) || isDigit(c);
443
+ }
444
+ /**
445
+ * A character permitted in the URL prefix of an Any type name, matching
446
+ * protobuf-go's isUrlChar plus the type-name characters.
447
+ */
448
+ function isUrlChar(c) {
449
+ return (isLetterOrDigit(c) ||
450
+ c === "-" ||
451
+ c === "." ||
452
+ c === "~" ||
453
+ c === "!" ||
454
+ c === "$" ||
455
+ c === "&" ||
456
+ c === "(" ||
457
+ c === ")" ||
458
+ c === "*" ||
459
+ c === "+" ||
460
+ c === "," ||
461
+ c === ";" ||
462
+ c === "=");
463
+ }
464
+ function quoteChar(c) {
465
+ var _a;
466
+ const code = (_a = c.codePointAt(0)) !== null && _a !== void 0 ? _a : 0;
467
+ return code >= 0x20 && code <= 0x7e
468
+ ? `"${c}"`
469
+ : `U+${code.toString(16).toUpperCase().padStart(4, "0")}`;
470
+ }
471
+ // Append the UTF-8 encoding of `text` to `out`, using the same Text Encoding
472
+ // API as the rest of the library. Unpaired surrogates become U+FFFD (the
473
+ // standard TextEncoder behavior), matching binary serialization.
474
+ function pushUtf8(out, text) {
475
+ if (text.length === 0) {
476
+ return;
477
+ }
478
+ for (const byte of (0, text_encoding_js_1.getTextEncoding)().encodeUtf8(text)) {
479
+ out.push(byte);
480
+ }
481
+ }