@trevrpc/trevrpc-js 1.5.0 → 1.7.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.
package/src/int64.js DELETED
@@ -1,288 +0,0 @@
1
- import protobuf from "protobufjs";
2
-
3
- import { invalidArgument } from "./status.js";
4
-
5
- /**
6
- * How decoded 64-bit integer fields are represented: `bigint`, `number` (exact up to 2^53),
7
- * decimal `string`, or protobuf.js's `long` (a Long object, as protobuf.js decodes them).
8
- */
9
- export const Int64Representations = Object.freeze(["bigint", "number", "string", "long"]);
10
-
11
- /** The representation of each root's 64-bit fields, for roots made with createRoot(). */
12
- const rootRepresentations = new WeakMap();
13
- /** The conversion plan of each message type, or null when its messages need none. */
14
- const plans = new WeakMap();
15
-
16
- const Int64Types = new Set(["int64", "uint64", "sint64", "fixed64", "sfixed64"]);
17
- const UnsignedTypes = new Set(["uint64", "fixed64"]);
18
-
19
- /**
20
- * Sets how the decoded messages of `root`'s types represent 64-bit integers. Without it they keep
21
- * protobuf.js's Long objects. A field's `jstype` option (`JS_STRING` or `JS_NUMBER`) overrides it.
22
- */
23
- export function setInt64Representation(root, representation) {
24
- if (!Int64Representations.includes(representation)) {
25
- throw invalidArgument(
26
- `int64 must be one of ${Int64Representations.join(", ")}, not ${JSON.stringify(representation)}`,
27
- );
28
- }
29
- rootRepresentations.set(root, representation);
30
- }
31
-
32
- /**
33
- * Returns a function that converts, in place, the 64-bit integers of a message of `type` that
34
- * protobuf.js decoded, as its root's representation says, and returns the message; or undefined
35
- * when its messages need no conversion. 64-bit map keys, which protobuf.js decodes as 8-byte hash
36
- * strings, become decimal strings in every representation.
37
- */
38
- export function int64Converter(type) {
39
- const plan = planFor(type);
40
- return plan === null ? undefined : (message) => applyPlan(plan, message);
41
- }
42
-
43
- /**
44
- * Converts a 64-bit integer, as a bigint, a number, a decimal string, or a Long-like object with
45
- * `low`, `high` and `unsigned`, to a bigint. Null and undefined are zero.
46
- */
47
- export function int64ToBigInt(value, unsigned = value?.unsigned === true) {
48
- switch (typeof value) {
49
- case "bigint":
50
- return value;
51
- case "number":
52
- return BigInt(Math.trunc(value));
53
- case "string":
54
- return BigInt(value);
55
- default: {
56
- if (value == null) {
57
- return 0n;
58
- }
59
- const bits = (BigInt(value.high >>> 0) << 32n) | BigInt(value.low >>> 0);
60
- return unsigned ? bits : BigInt.asIntN(64, bits);
61
- }
62
- }
63
- }
64
-
65
- function planFor(type) {
66
- const cached = plans.get(type);
67
- if (cached !== undefined) {
68
- return cached;
69
- }
70
- const representation = rootRepresentations.get(type.root);
71
- if (representation === undefined) {
72
- plans.set(type, null);
73
- return null;
74
- }
75
- buildPlans(type, representation);
76
- return plans.get(type);
77
- }
78
-
79
- /**
80
- * Builds the plans of `start` and every message type reachable from it. A type needs a plan when
81
- * it has a 64-bit field to convert, or a message field whose type needs one; that is settled for
82
- * every reachable type at once, so recursive types are handled.
83
- */
84
- function buildPlans(start, representation) {
85
- const steps = new Map();
86
- const pending = [start];
87
- steps.set(start, undefined);
88
- while (pending.length > 0) {
89
- const type = pending.pop();
90
- const typeSteps = [];
91
- for (const field of type.fieldsArray) {
92
- field.resolve();
93
- const step = fieldStep(type, field, representation);
94
- if (step !== undefined) {
95
- typeSteps.push(step);
96
- }
97
- if (field.resolvedType instanceof protobuf.Type && !steps.has(field.resolvedType)) {
98
- steps.set(field.resolvedType, undefined);
99
- pending.push(field.resolvedType);
100
- }
101
- }
102
- steps.set(type, typeSteps);
103
- }
104
-
105
- // A type needs a plan when it converts a value itself or holds a type that needs one.
106
- const needed = new Set();
107
- for (let changed = true; changed;) {
108
- changed = false;
109
- for (const [type, typeSteps] of steps) {
110
- if (
111
- !needed.has(type) &&
112
- typeSteps.some((step) => convertsItself(step) || needed.has(step.type))
113
- ) {
114
- needed.add(type);
115
- changed = true;
116
- }
117
- }
118
- }
119
-
120
- const built = new Map();
121
- for (const type of steps.keys()) {
122
- if (!plans.has(type)) {
123
- built.set(type, needed.has(type) ? [] : null);
124
- restoreNullDefaults(type);
125
- }
126
- }
127
- for (const [type, plan] of built) {
128
- plans.set(type, plan);
129
- }
130
- for (const [type, plan] of built) {
131
- if (plan === null) {
132
- continue;
133
- }
134
- for (const step of steps.get(type)) {
135
- const nested = step.type === undefined ? undefined : plans.get(step.type);
136
- if (!convertsItself(step) && nested == null) {
137
- continue;
138
- }
139
- plan.push({ ...step, nested: nested ?? undefined });
140
- }
141
- patchDefaults(type, plan);
142
- }
143
- }
144
-
145
- /** Reports whether a step converts values or keys of its own, not only nested messages. */
146
- function convertsItself(step) {
147
- return step.convert !== undefined || step.convertKey !== undefined;
148
- }
149
-
150
- /**
151
- * The conversion step of one field of `type`, or undefined when it holds neither a 64-bit integer
152
- * nor a message. `convert` converts its 64-bit values, `convertKey` its 64-bit map keys, and
153
- * `type` is the message type of its values.
154
- */
155
- function fieldStep(type, field, representation) {
156
- const messageType = field.resolvedType instanceof protobuf.Type ? field.resolvedType : undefined;
157
- const convert = Int64Types.has(field.type)
158
- ? valueConverter(fieldRepresentation(field, representation), UnsignedTypes.has(field.type))
159
- : undefined;
160
- if (field.map) {
161
- const convertKey = Int64Types.has(field.keyType)
162
- ? keyConverter(UnsignedTypes.has(field.keyType))
163
- : undefined;
164
- if (convert === undefined && convertKey === undefined && messageType === undefined) {
165
- return undefined;
166
- }
167
- return { name: field.name, shape: "map", convert, convertKey, type: messageType };
168
- }
169
- if (convert === undefined && messageType === undefined) {
170
- return undefined;
171
- }
172
- const shape = field.repeated ? "repeated" : "single";
173
- return { name: field.name, shape, convert, type: messageType };
174
- }
175
-
176
- /** The representation of a 64-bit field: its `jstype` option's, or the root's. */
177
- function fieldRepresentation(field, representation) {
178
- switch (field.options?.jstype) {
179
- case "JS_STRING":
180
- return "string";
181
- case "JS_NUMBER":
182
- return "number";
183
- default:
184
- return representation;
185
- }
186
- }
187
-
188
- /** Returns a function converting a decoded 64-bit value, or undefined for Long objects. */
189
- function valueConverter(representation, unsigned) {
190
- switch (representation) {
191
- case "bigint":
192
- return (value) => int64ToBigInt(value, unsigned);
193
- case "number":
194
- return (value) => Number(int64ToBigInt(value, unsigned));
195
- case "string":
196
- return (value) => int64ToBigInt(value, unsigned).toString();
197
- default:
198
- return undefined;
199
- }
200
- }
201
-
202
- /** Returns a function converting a decoded 64-bit map key, an 8-byte hash, to decimal. */
203
- function keyConverter(unsigned) {
204
- return (key) => int64ToBigInt(protobuf.util.longFromHash(key, unsigned), unsigned).toString();
205
- }
206
-
207
- /**
208
- * Makes unset proto3 `optional` 64-bit fields of `type`'s messages read null, as other fields with
209
- * presence do: protobuf.js turns their null default into a Long zero.
210
- */
211
- function restoreNullDefaults(type) {
212
- for (const field of type.fieldsArray) {
213
- if (Int64Types.has(field.type) && field.options?.proto3_optional && !field.repeated) {
214
- type.ctor.prototype[field.name] = null;
215
- }
216
- }
217
- }
218
-
219
- /**
220
- * Converts the defaults that `type`'s messages read from their prototype for unset singular 64-bit
221
- * fields, so that an unset field reads like a set one.
222
- */
223
- function patchDefaults(type, plan) {
224
- const prototype = type.ctor.prototype;
225
- for (const step of plan) {
226
- // A field with presence, such as a proto3 optional one, reads null when unset.
227
- if (step.shape === "single" && step.convert !== undefined && prototype[step.name] != null) {
228
- prototype[step.name] = step.convert(prototype[step.name]);
229
- }
230
- }
231
- }
232
-
233
- function applyPlan(plan, message) {
234
- for (const step of plan) {
235
- if (!Object.hasOwn(message, step.name)) {
236
- continue;
237
- }
238
- const value = message[step.name];
239
- if (value == null) {
240
- continue;
241
- }
242
- switch (step.shape) {
243
- case "single":
244
- message[step.name] =
245
- step.convert === undefined ? applyPlan(step.nested, value) : step.convert(value);
246
- break;
247
- case "repeated":
248
- for (let index = 0; index < value.length; index += 1) {
249
- value[index] =
250
- step.convert === undefined
251
- ? applyPlan(step.nested, value[index])
252
- : step.convert(value[index]);
253
- }
254
- break;
255
- case "map":
256
- if (step.convertKey === undefined) {
257
- convertMapValues(step, value);
258
- } else {
259
- message[step.name] = convertMap(step, value);
260
- }
261
- break;
262
- }
263
- }
264
- return message;
265
- }
266
-
267
- /** Returns a copy of a decoded map with converted keys and values. */
268
- function convertMap(step, map) {
269
- const converted = {};
270
- for (const [key, value] of Object.entries(map)) {
271
- converted[step.convertKey(key)] = convertMapValue(step, value);
272
- }
273
- return converted;
274
- }
275
-
276
- /** Converts the values of a decoded map in place. */
277
- function convertMapValues(step, map) {
278
- for (const [key, value] of Object.entries(map)) {
279
- map[key] = convertMapValue(step, value);
280
- }
281
- }
282
-
283
- function convertMapValue(step, value) {
284
- if (step.convert !== undefined) {
285
- return step.convert(value);
286
- }
287
- return step.nested !== undefined && value != null ? applyPlan(step.nested, value) : value;
288
- }
package/src/wkt.js DELETED
@@ -1,137 +0,0 @@
1
- // Helpers for the well-known types google.protobuf.Timestamp, Duration, Struct, Value and
2
- // ListValue, whose declarations generated code imports from the runtime.
3
- import { int64ToBigInt } from "./int64.js";
4
- import { invalidArgument } from "./status.js";
5
-
6
- /** google.protobuf.NullValue. */
7
- export const NullValue = Object.freeze({ NULL_VALUE: 0 });
8
-
9
- const MsPerSecond = 1000n;
10
- const NanosPerMs = 1_000_000;
11
-
12
- /** Returns the Timestamp of a Date. */
13
- export function timestampFromDate(date) {
14
- return timestampFromMs(date.getTime());
15
- }
16
-
17
- /** Returns the Timestamp of milliseconds since the Unix epoch. */
18
- export function timestampFromMs(ms) {
19
- checkFinite(ms, "timestamp");
20
- const whole = Math.floor(ms);
21
- const seconds = Math.floor(whole / 1000);
22
- return { seconds: BigInt(seconds), nanos: (whole - seconds * 1000) * NanosPerMs };
23
- }
24
-
25
- /** Returns the Date of a Timestamp. */
26
- export function timestampDate(timestamp) {
27
- return new Date(timestampMs(timestamp));
28
- }
29
-
30
- /** Returns the milliseconds since the Unix epoch of a Timestamp, rounded down. */
31
- export function timestampMs(timestamp) {
32
- return toMs(timestamp, Math.floor);
33
- }
34
-
35
- /** Returns the Duration of a number of milliseconds. */
36
- export function durationFromMs(ms) {
37
- checkFinite(ms, "duration");
38
- const whole = Math.trunc(ms);
39
- const seconds = Math.trunc(whole / 1000);
40
- // Seconds and nanos share the duration's sign; `+ 0` turns -0 into 0.
41
- return { seconds: BigInt(seconds), nanos: (whole - seconds * 1000) * NanosPerMs + 0 };
42
- }
43
-
44
- /** Returns the milliseconds of a Duration, rounded towards zero. */
45
- export function durationMs(duration) {
46
- return toMs(duration, Math.trunc);
47
- }
48
-
49
- /** Returns the Struct of a JSON object. */
50
- export function structFromJson(json) {
51
- if (json === null || typeof json !== "object" || Array.isArray(json)) {
52
- throw invalidArgument("a Struct is made from a JSON object");
53
- }
54
- const fields = {};
55
- for (const [key, value] of Object.entries(json)) {
56
- fields[key] = valueFromJson(value);
57
- }
58
- return { fields };
59
- }
60
-
61
- /** Returns the JSON object of a Struct. */
62
- export function structToJson(struct) {
63
- const json = {};
64
- for (const [key, value] of Object.entries(struct?.fields ?? {})) {
65
- json[key] = valueToJson(value);
66
- }
67
- return json;
68
- }
69
-
70
- /** Returns the Value of a JSON value. */
71
- export function valueFromJson(json) {
72
- if (json === null) {
73
- return { nullValue: NullValue.NULL_VALUE };
74
- }
75
- switch (typeof json) {
76
- case "number":
77
- return { numberValue: json };
78
- case "string":
79
- return { stringValue: json };
80
- case "boolean":
81
- return { boolValue: json };
82
- case "object":
83
- return Array.isArray(json)
84
- ? { listValue: { values: json.map(valueFromJson) } }
85
- : { structValue: structFromJson(json) };
86
- default:
87
- throw invalidArgument(`a Value cannot hold a ${typeof json}`);
88
- }
89
- }
90
-
91
- /** Returns the JSON value of a Value; a Value with no kind set is null. */
92
- export function valueToJson(value) {
93
- switch (valueKind(value)) {
94
- case "numberValue":
95
- return value.numberValue;
96
- case "stringValue":
97
- return value.stringValue;
98
- case "boolValue":
99
- return value.boolValue;
100
- case "structValue":
101
- return structToJson(value.structValue);
102
- case "listValue":
103
- return (value.listValue.values ?? []).map(valueToJson);
104
- default:
105
- return null;
106
- }
107
- }
108
-
109
- const ValueKinds = [
110
- "nullValue",
111
- "numberValue",
112
- "stringValue",
113
- "boolValue",
114
- "structValue",
115
- "listValue",
116
- ];
117
-
118
- /**
119
- * The member of a Value's `kind` oneof that is set. A decoded Value reads the defaults of the
120
- * other members from its prototype, so only own properties count.
121
- */
122
- function valueKind(value) {
123
- return ValueKinds.find(
124
- (kind) => value != null && Object.hasOwn(value, kind) && value[kind] != null,
125
- );
126
- }
127
-
128
- /** The milliseconds of a Timestamp or Duration, with sub-millisecond nanos rounded by `round`. */
129
- function toMs({ seconds, nanos } = {}, round) {
130
- return Number(int64ToBigInt(seconds) * MsPerSecond) + round((nanos ?? 0) / NanosPerMs);
131
- }
132
-
133
- function checkFinite(ms, what) {
134
- if (!Number.isFinite(ms)) {
135
- throw invalidArgument(`a ${what} needs a finite number of milliseconds, not ${ms}`);
136
- }
137
- }