@lsmsgpack/client 0.0.0-stage → 2026.10.2201

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/writer.js ADDED
@@ -0,0 +1,685 @@
1
+ // Plain MsgPack for request bodies (application/msgpack): objects as maps keyed by their property names, as the formatters read them with their Plain options.
2
+ // The property $type is written as the type id (the key "", first): the class of an object where the declared type is a base class, interface or object.
3
+ import { TypeProperty } from './decoder.js';
4
+ import { MsgPackError } from './errors.js';
5
+ import { MsgPackExtension } from './reader.js';
6
+ import { dateOnlyDayNumber, encodeDecimal, guidBytes, parseOffsetText, timeOnlyTicks, timeSpanTicks } from './values.js';
7
+ const textEncoder = new TextEncoder();
8
+ /**
9
+ * Encodes a value: null and undefined as nil (undefined properties are left out), numbers as the smallest integer or a float64, bigint as a 64 bit integer,
10
+ * Date as a timestamp, Uint8Array (and other typed arrays, ArrayBuffer) as bin, Map and plain objects as maps, MsgPackExtension as an extension.
11
+ * The property $type of an object (a string: the .NET type name, as the decoder sets it) is written as the type id, the key "" (a Map is written as it is).
12
+ */
13
+ export function encode(value, options = {}) {
14
+ const writer = new Writer(options.maxDepth ?? 256, options.models, options.generateCode ?? true);
15
+ writer.write(value, 0);
16
+ return writer.result();
17
+ }
18
+ class Writer {
19
+ constructor(maxDepth, models, generateCode) {
20
+ this.maxDepth = maxDepth;
21
+ this.models = models;
22
+ this.generateCode = generateCode;
23
+ this.buf = new Uint8Array(256);
24
+ this.view = new DataView(this.buf.buffer);
25
+ this.pos = 0;
26
+ }
27
+ isModel(value) {
28
+ return this.models?.of(value) !== undefined;
29
+ }
30
+ result() {
31
+ return this.buf.slice(0, this.pos);
32
+ }
33
+ ensure(count) {
34
+ if (this.pos + count > this.buf.length)
35
+ this.grow(count); // small enough to be inlined
36
+ }
37
+ grow(count) {
38
+ let size = this.buf.length * 2;
39
+ while (size < this.pos + count)
40
+ size *= 2;
41
+ const grown = new Uint8Array(size);
42
+ grown.set(this.buf.subarray(0, this.pos));
43
+ this.buf = grown;
44
+ this.view = new DataView(grown.buffer);
45
+ }
46
+ u8(b) {
47
+ this.ensure(1);
48
+ this.buf[this.pos++] = b;
49
+ }
50
+ header(b, n, size) {
51
+ this.ensure(1 + size);
52
+ this.buf[this.pos++] = b;
53
+ if (size === 1)
54
+ this.buf[this.pos] = n;
55
+ else if (size === 2)
56
+ this.view.setUint16(this.pos, n);
57
+ else
58
+ this.view.setUint32(this.pos, n);
59
+ this.pos += size;
60
+ }
61
+ write(value, depth) {
62
+ if (value === null || value === undefined)
63
+ return this.u8(0xc0);
64
+ switch (typeof value) {
65
+ case 'boolean': return this.u8(value ? 0xc3 : 0xc2);
66
+ case 'number': return this.number(value);
67
+ case 'bigint': return this.bigint(value);
68
+ case 'string': return this.string(value);
69
+ }
70
+ if (depth >= this.maxDepth)
71
+ throw new MsgPackError(`The value is nested deeper than ${this.maxDepth} levels.`);
72
+ if (value instanceof Date)
73
+ return this.date(value);
74
+ if (value instanceof Uint8Array)
75
+ return this.bin(value);
76
+ if (ArrayBuffer.isView(value))
77
+ return this.bin(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
78
+ if (value instanceof ArrayBuffer)
79
+ return this.bin(new Uint8Array(value));
80
+ if (value instanceof MsgPackExtension)
81
+ return this.ext(value.type, value.data);
82
+ if (Array.isArray(value)) {
83
+ this.arrayHeader(value.length);
84
+ for (let t = 0; t < value.length; t++)
85
+ this.write(value[t], depth + 1);
86
+ return;
87
+ }
88
+ if (value instanceof Map) {
89
+ this.mapHeader(value.size);
90
+ for (const [k, v] of value) {
91
+ this.write(k, depth + 1);
92
+ this.write(v, depth + 1);
93
+ }
94
+ return;
95
+ }
96
+ if (typeof value === 'object')
97
+ return this.object(value, depth, undefined);
98
+ throw new MsgPackError(`A ${typeof value} cannot be written as MsgPack.`);
99
+ }
100
+ /** An array of values of one declared type (the models). */
101
+ list(value, element, depth) {
102
+ if (depth >= this.maxDepth)
103
+ throw new MsgPackError(`The value is nested deeper than ${this.maxDepth} levels.`);
104
+ this.arrayHeader(value.length);
105
+ for (let t = 0; t < value.length; t++)
106
+ element(this, value[t], depth + 1);
107
+ }
108
+ /** An array of values of the declared types (a tuple; KeyValuePair as [key, value]). */
109
+ items(value, items, depth) {
110
+ if (depth >= this.maxDepth)
111
+ throw new MsgPackError(`The value is nested deeper than ${this.maxDepth} levels.`);
112
+ this.arrayHeader(value.length);
113
+ for (let t = 0; t < value.length; t++)
114
+ (t < items.length ? items[t] : writeAsIs)(this, value[t], depth + 1);
115
+ }
116
+ /** A dictionary (a plain object): the keys as the declared key type (text in JavaScript: an int key is written as a number). */
117
+ dictionary(o, key, value, depth) {
118
+ if (depth >= this.maxDepth)
119
+ throw new MsgPackError(`The value is nested deeper than ${this.maxDepth} levels.`);
120
+ const keys = Object.keys(o).filter((k) => o[k] !== undefined && typeof o[k] !== 'function');
121
+ this.mapHeader(keys.length);
122
+ for (let t = 0; t < keys.length; t++) {
123
+ key(this, keys[t], depth + 1);
124
+ value(this, o[keys[t]], depth + 1);
125
+ }
126
+ }
127
+ /** A Guid as .NET writes it (bin 16, Guid.ToByteArray()), read from its text without a copy; other text as it is. */
128
+ guid(text) {
129
+ if (text.length === 36 && text.charCodeAt(8) === 45 && text.charCodeAt(13) === 45 && text.charCodeAt(18) === 45 && text.charCodeAt(23) === 45) {
130
+ this.ensure(18);
131
+ const b = this.buf, p = this.pos;
132
+ let bad = 0;
133
+ for (let t = 0; t < 16; t++) {
134
+ const c = GuidSource[t];
135
+ const hi = hexValue(text.charCodeAt(c)), lo = hexValue(text.charCodeAt(c + 1));
136
+ bad |= hi | lo; // -1 for a character that is not a hexadecimal digit
137
+ b[p + 2 + t] = (hi << 4) | lo;
138
+ }
139
+ if (bad >= 0) {
140
+ b[p] = 0xc4;
141
+ b[p + 1] = 16;
142
+ this.pos = p + 18;
143
+ return;
144
+ }
145
+ }
146
+ if (/^[{]?[0-9a-fA-F]{8}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{12}[}]?$/.test(text))
147
+ return this.bin(guidBytes(text)); // braces or no dashes
148
+ this.string(text);
149
+ }
150
+ /** A string of a kind with a text form (TimeSpan, DateOnly...): what .NET writes for it, or the text when it is not one. */
151
+ text(value, kind, depth) {
152
+ switch (kind) {
153
+ case 'timespan': {
154
+ const ticks = timeSpanTicks(value);
155
+ if (ticks !== undefined)
156
+ return typeof ticks === 'bigint' ? this.bigint(ticks) : this.number(ticks);
157
+ break;
158
+ }
159
+ case 'timeonly': {
160
+ const ticks = timeOnlyTicks(value);
161
+ if (ticks !== undefined)
162
+ return this.number(ticks);
163
+ break;
164
+ }
165
+ case 'dateonly': {
166
+ const day = dateOnlyDayNumber(value);
167
+ if (day !== undefined)
168
+ return this.number(day);
169
+ break;
170
+ }
171
+ case 'char':
172
+ if (value.length === 1)
173
+ return this.number(value.charCodeAt(0));
174
+ break;
175
+ case 'datetime': {
176
+ const ms = Date.parse(value);
177
+ if (!Number.isNaN(ms))
178
+ return this.date(new Date(ms));
179
+ break;
180
+ }
181
+ case 'dto':
182
+ case 'dto-clock': {
183
+ const parsed = parseOffsetText(value);
184
+ if (parsed === undefined)
185
+ break;
186
+ this.arrayHeader(2); // DateTimeOffsetFormat TimestampAndOffset (dto) or ClockTimeAndOffset (dto-clock)
187
+ this.date(kind === 'dto' ? parsed.moment : new Date(parsed.moment.getTime() + parsed.offset * 60000));
188
+ return this.number(parsed.offset);
189
+ }
190
+ case 'decimal': {
191
+ const bytes = encodeDecimal(value);
192
+ if (bytes !== undefined)
193
+ return this.ext(1, bytes); // the decimal extension: every digit
194
+ break;
195
+ }
196
+ case 'int':
197
+ case 'long':
198
+ case 'ulong':
199
+ case 'float':
200
+ if (value.trim() !== '' && !Number.isNaN(Number(value)))
201
+ return this.write(Number(value), depth); // a dictionary key
202
+ break;
203
+ }
204
+ this.string(value);
205
+ }
206
+ /**
207
+ * An object as a map of its own enumerable properties (not undefined or functions), $type as the type id first. An instance of a model class: see model.
208
+ * @param declared the type name the place declares (a class of the models), AnyDeclared (object, an interface), undefined when not known (the root, untyped places)
209
+ */
210
+ object(o, depth, declared) {
211
+ const model = this.models?.of(o);
212
+ if (model !== undefined)
213
+ return this.model(o, model, depth, declared);
214
+ let n = 0;
215
+ for (const k in o)
216
+ if (hasOwn.call(o, k))
217
+ n++;
218
+ const start = this.pos;
219
+ this.mapHeader(n); // one pass: the header for every key, made smaller when some are left out
220
+ let count = 0;
221
+ for (const k in o) {
222
+ if (!hasOwn.call(o, k))
223
+ continue;
224
+ const v = o[k];
225
+ if (v === undefined || typeof v === 'function')
226
+ continue;
227
+ if (k === TypeProperty) {
228
+ if (count !== 0) { // after other properties: written again with the type id first
229
+ this.pos = start;
230
+ return this.typedObject(o, Object.keys(o), depth);
231
+ }
232
+ if (typeof v !== 'string')
233
+ throw typePropertyError(v);
234
+ this.u8(0xa0); // the key "": the type id, first (LtMsgPack's fast path, System.Text.Json also wants it first)
235
+ this.string(v);
236
+ }
237
+ else {
238
+ this.key(k);
239
+ this.write(v, depth + 1);
240
+ }
241
+ count++;
242
+ }
243
+ if (count !== n)
244
+ this.patchMapHeader(start, n, count);
245
+ }
246
+ /** An object whose $type is not its first property: the type id first. */
247
+ typedObject(o, keys, depth) {
248
+ let count = 0, typeName;
249
+ for (let t = 0; t < keys.length; t++) {
250
+ const v = o[keys[t]];
251
+ if (v === undefined || typeof v === 'function')
252
+ continue;
253
+ if (keys[t] === TypeProperty) {
254
+ if (typeof v !== 'string')
255
+ throw typePropertyError(v);
256
+ typeName = v;
257
+ }
258
+ count++;
259
+ }
260
+ this.mapHeader(count);
261
+ this.u8(0xa0);
262
+ this.string(typeName);
263
+ for (let t = 0; t < keys.length; t++) {
264
+ const k = keys[t], v = o[k];
265
+ if (v === undefined || typeof v === 'function' || k === TypeProperty)
266
+ continue;
267
+ this.key(k);
268
+ this.write(v, depth + 1);
269
+ }
270
+ }
271
+ /** Replaces the map header written at start for n entries by the one for count (smaller): the entries move when it is shorter. */
272
+ patchMapHeader(start, n, count) {
273
+ const reserved = n <= 15 ? 1 : n <= 0xffff ? 3 : 5;
274
+ const size = count <= 15 ? 1 : count <= 0xffff ? 3 : 5;
275
+ const end = this.pos;
276
+ if (size !== reserved)
277
+ this.buf.copyWithin(start + size, start + reserved, end);
278
+ this.pos = start;
279
+ this.mapHeader(count);
280
+ this.pos = end - reserved + size;
281
+ }
282
+ /** A property name: the encoded names are kept (they repeat in every object of a type). */
283
+ key(k) {
284
+ let bytes = encodedKeys.get(k);
285
+ if (bytes === undefined) {
286
+ if (k.length > 32 || encodedKeys.size >= MaxEncodedKeys)
287
+ return this.string(k);
288
+ bytes = encode(k);
289
+ encodedKeys.set(k, bytes);
290
+ }
291
+ this.small(bytes);
292
+ }
293
+ /**
294
+ * An instance of a model class: its properties as declared, then other own properties as they are. The type id where the declared type is not its class
295
+ * (where it is not known: when the class extends another one, e.g. the elements of an array of animals).
296
+ */
297
+ model(o, model, depth, declared) {
298
+ if (depth >= this.maxDepth)
299
+ throw new MsgPackError(`The value is nested deeper than ${this.maxDepth} levels.`);
300
+ const typeId = declared === undefined ? model.derived : declared !== model.typeName;
301
+ const plan = planOf(model);
302
+ const keys = Object.keys(o);
303
+ const extra = sameKeys(keys, plan.shape) ? undefined : this.extraKeys(o, keys, model, plan);
304
+ const max = (typeId ? 1 : 0) + plan.names.length + (extra?.length ?? 0), start = this.pos;
305
+ this.mapHeader(max); // one pass: the header for every property, made smaller for the undefined ones
306
+ let count = 0;
307
+ if (typeId) {
308
+ this.u8(0xa0);
309
+ this.string(model.typeName);
310
+ count++;
311
+ }
312
+ if (this.generateCode && plan.generated === undefined)
313
+ plan.generated = generateModelWriter(plan);
314
+ if (this.generateCode && plan.generated !== null)
315
+ count += plan.generated(this, o, depth + 1);
316
+ else {
317
+ const names = plan.names, writers = plan.writers, encoded = plan.encoded;
318
+ for (let t = 0; t < names.length; t++) {
319
+ const v = o[names[t]];
320
+ if (v === undefined)
321
+ continue;
322
+ this.small(encoded[t]);
323
+ writers[t](this, v, depth + 1);
324
+ count++;
325
+ }
326
+ }
327
+ if (extra !== undefined)
328
+ for (let t = 0; t < extra.length; t++) {
329
+ this.string(extra[t]);
330
+ this.write(o[extra[t]], depth + 1);
331
+ count++;
332
+ }
333
+ if (count !== max)
334
+ this.patchMapHeader(start, max, count);
335
+ }
336
+ /** The own properties the model does not have (e.g. one the server has and the models not yet). Keys without any are remembered: the next instance with them is not checked. */
337
+ extraKeys(o, keys, model, plan) {
338
+ let extra, other = false;
339
+ for (let t = 0; t < keys.length; t++) {
340
+ const k = keys[t], v = o[k];
341
+ if (model.fields.has(k) || k === TypeProperty)
342
+ continue;
343
+ if (v !== undefined && typeof v !== 'function')
344
+ (extra ?? (extra = [])).push(k);
345
+ else
346
+ other = true; // left out now, but such a key may have a value in the next instance: not a shape to remember
347
+ }
348
+ if (extra === undefined && !other)
349
+ plan.shape = keys;
350
+ return extra;
351
+ }
352
+ /** Pre-encoded bytes (a property name), copied by a loop: set() costs more for a few bytes. */
353
+ small(bytes) {
354
+ const n = bytes.length;
355
+ this.ensure(n);
356
+ const b = this.buf;
357
+ let p = this.pos;
358
+ for (let t = 0; t < n; t++)
359
+ b[p++] = bytes[t];
360
+ this.pos = p;
361
+ }
362
+ number(n) {
363
+ if (!Number.isSafeInteger(n) || Object.is(n, -0)) { // beyond 2^53 a number is not an exact integer: as it is, a double
364
+ this.ensure(9);
365
+ this.buf[this.pos] = 0xcb;
366
+ this.view.setFloat64(this.pos + 1, n);
367
+ this.pos += 9;
368
+ return;
369
+ }
370
+ if (n >= 0) {
371
+ if (n <= 0x7f)
372
+ return this.u8(n);
373
+ if (n <= 0xff)
374
+ return this.header(0xcc, n, 1);
375
+ if (n <= 0xffff)
376
+ return this.header(0xcd, n, 2);
377
+ if (n <= 0xffffffff)
378
+ return this.header(0xce, n, 4);
379
+ return this.bigint(BigInt(n));
380
+ }
381
+ if (n >= -32)
382
+ return this.u8(n & 0xff);
383
+ if (n >= -128)
384
+ return this.header(0xd0, n & 0xff, 1);
385
+ if (n >= -32768)
386
+ return this.header(0xd1, n & 0xffff, 2);
387
+ if (n >= -2147483648)
388
+ return this.header(0xd2, n >>> 0, 4);
389
+ return this.bigint(BigInt(n));
390
+ }
391
+ bigint(n) {
392
+ if (n >= -(2n ** 63n) && n < 0n) {
393
+ this.ensure(9);
394
+ this.buf[this.pos] = 0xd3;
395
+ this.view.setBigInt64(this.pos + 1, n);
396
+ }
397
+ else if (n >= 0n && n < 2n ** 64n) {
398
+ this.ensure(9);
399
+ this.buf[this.pos] = 0xcf;
400
+ this.view.setBigUint64(this.pos + 1, n);
401
+ }
402
+ else
403
+ throw new MsgPackError(`${n} does not fit in 64 bits.`);
404
+ this.pos += 9;
405
+ }
406
+ /**
407
+ * Strings up to 32 characters by a loop, longer ones with encodeInto after the largest header (moved back when a smaller one fits). TextEncoder.encode per string
408
+ * (a call into the runtime and a new array) made writing the invoices 6x slower. Lone surrogates become U+FFFD, as TextEncoder writes them.
409
+ */
410
+ string(s) {
411
+ const length = s.length;
412
+ if (length > 32)
413
+ return this.longString(s);
414
+ this.ensure(2 + length * 3); // at most 3 bytes per UTF-16 unit (a surrogate pair is 4 bytes for 2), and one more header byte for str8
415
+ const b = this.buf, start = this.pos;
416
+ let p = start + 1;
417
+ for (let i = 0; i < length; i++) {
418
+ let c = s.charCodeAt(i);
419
+ if (c < 0x80)
420
+ b[p++] = c;
421
+ else if (c < 0x800) {
422
+ b[p++] = 0xc0 | (c >> 6);
423
+ b[p++] = 0x80 | (c & 0x3f);
424
+ }
425
+ else {
426
+ if (c >= 0xd800 && c <= 0xdfff) {
427
+ const d = c <= 0xdbff && i + 1 < length ? s.charCodeAt(i + 1) : 0;
428
+ if (d >= 0xdc00 && d <= 0xdfff) {
429
+ i++;
430
+ c = 0x10000 + ((c - 0xd800) << 10) + (d - 0xdc00);
431
+ b[p++] = 0xf0 | (c >> 18);
432
+ b[p++] = 0x80 | ((c >> 12) & 0x3f);
433
+ b[p++] = 0x80 | ((c >> 6) & 0x3f);
434
+ b[p++] = 0x80 | (c & 0x3f);
435
+ continue;
436
+ }
437
+ c = 0xfffd;
438
+ }
439
+ b[p++] = 0xe0 | (c >> 12);
440
+ b[p++] = 0x80 | ((c >> 6) & 0x3f);
441
+ b[p++] = 0x80 | (c & 0x3f);
442
+ }
443
+ }
444
+ const n = p - start - 1;
445
+ if (n <= 31) {
446
+ b[start] = 0xa0 | n;
447
+ this.pos = p;
448
+ return;
449
+ }
450
+ b.copyWithin(start + 2, start + 1, p); // str8
451
+ b[start] = 0xd9;
452
+ b[start + 1] = n;
453
+ this.pos = p + 1;
454
+ }
455
+ longString(s) {
456
+ this.ensure(5 + s.length * 3);
457
+ const start = this.pos;
458
+ const n = textEncoder.encodeInto(s, this.buf.subarray(start + 5)).written; // at least 33 bytes: str8 or larger
459
+ const size = n <= 0xff ? 2 : n <= 0xffff ? 3 : 5;
460
+ if (size !== 5)
461
+ this.buf.copyWithin(start + size, start + 5, start + 5 + n);
462
+ if (size === 2) {
463
+ this.buf[start] = 0xd9;
464
+ this.buf[start + 1] = n;
465
+ }
466
+ else if (size === 3) {
467
+ this.buf[start] = 0xda;
468
+ this.view.setUint16(start + 1, n);
469
+ }
470
+ else {
471
+ this.buf[start] = 0xdb;
472
+ this.view.setUint32(start + 1, n);
473
+ }
474
+ this.pos = start + size + n;
475
+ }
476
+ raw(bytes) {
477
+ this.ensure(bytes.length);
478
+ this.buf.set(bytes, this.pos);
479
+ this.pos += bytes.length;
480
+ }
481
+ bin(bytes) {
482
+ const n = bytes.length;
483
+ if (n <= 0xff)
484
+ this.header(0xc4, n, 1);
485
+ else if (n <= 0xffff)
486
+ this.header(0xc5, n, 2);
487
+ else
488
+ this.header(0xc6, n, 4);
489
+ this.raw(bytes);
490
+ }
491
+ arrayHeader(n) {
492
+ if (n <= 15)
493
+ this.u8(0x90 | n);
494
+ else if (n <= 0xffff)
495
+ this.header(0xdc, n, 2);
496
+ else
497
+ this.header(0xdd, n, 4);
498
+ }
499
+ mapHeader(n) {
500
+ if (n <= 15)
501
+ this.u8(0x80 | n);
502
+ else if (n <= 0xffff)
503
+ this.header(0xde, n, 2);
504
+ else
505
+ this.header(0xdf, n, 4);
506
+ }
507
+ ext(type, data) {
508
+ const n = data.length;
509
+ const fixed = n === 1 ? 0xd4 : n === 2 ? 0xd5 : n === 4 ? 0xd6 : n === 8 ? 0xd7 : n === 16 ? 0xd8 : 0;
510
+ if (fixed !== 0)
511
+ this.u8(fixed);
512
+ else if (n <= 0xff)
513
+ this.header(0xc7, n, 1);
514
+ else if (n <= 0xffff)
515
+ this.header(0xc8, n, 2);
516
+ else
517
+ this.header(0xc9, n, 4);
518
+ this.u8(type & 0xff);
519
+ this.raw(data);
520
+ }
521
+ /** The timestamp extension (-1) in its smallest form, as the specification and LsMsgPack write it. */
522
+ date(date) {
523
+ const ms = date.getTime();
524
+ if (Number.isNaN(ms))
525
+ throw new MsgPackError('An invalid Date cannot be written.');
526
+ const seconds = Math.floor(ms / 1000);
527
+ const nanoseconds = (ms - seconds * 1000) * 1000000;
528
+ if (seconds >= 0 && seconds <= 0x3ffffffff) {
529
+ if (nanoseconds === 0 && seconds <= 0xffffffff) {
530
+ this.ensure(6);
531
+ this.buf[this.pos++] = 0xd6;
532
+ this.buf[this.pos++] = 0xff;
533
+ this.view.setUint32(this.pos, seconds);
534
+ this.pos += 4;
535
+ return;
536
+ }
537
+ this.ensure(10);
538
+ this.buf[this.pos++] = 0xd7;
539
+ this.buf[this.pos++] = 0xff;
540
+ this.view.setUint32(this.pos, nanoseconds * 4 + Math.floor(seconds / 4294967296));
541
+ this.view.setUint32(this.pos + 4, seconds >>> 0);
542
+ this.pos += 8;
543
+ return;
544
+ }
545
+ this.ensure(15);
546
+ this.buf[this.pos++] = 0xc7;
547
+ this.buf[this.pos++] = 12;
548
+ this.buf[this.pos++] = 0xff;
549
+ this.view.setUint32(this.pos, nanoseconds);
550
+ this.view.setBigInt64(this.pos + 4, BigInt(seconds));
551
+ this.pos += 12;
552
+ }
553
+ }
554
+ const hasOwn = Object.prototype.hasOwnProperty;
555
+ const encodedKeys = new Map();
556
+ const MaxEncodedKeys = 4096;
557
+ function typePropertyError(value) {
558
+ return new MsgPackError(`${TypeProperty} is a .NET type name (a string), not a ${typeof value}.`);
559
+ }
560
+ /** The declared type object (or an interface, an abstract class the models do not have): an instance of a model always gets its type id. */
561
+ const AnyDeclared = '\u0000any';
562
+ const AnyKind = { k: 'kind', kind: 'any' };
563
+ /** An object written as a map (not an array, Date, bytes, Map or extension). */
564
+ function isPlainObject(value) {
565
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array) && !ArrayBuffer.isView(value)
566
+ && !(value instanceof ArrayBuffer) && !(value instanceof MsgPackExtension) && !(value instanceof Map);
567
+ }
568
+ const writeAsIs = (w, v, d) => w.write(v, d);
569
+ const compiled = new WeakMap();
570
+ function writerOf(type) {
571
+ let writer = compiled.get(type);
572
+ if (writer === undefined) {
573
+ writer = compile(type);
574
+ compiled.set(type, writer);
575
+ }
576
+ return writer;
577
+ }
578
+ function compile(type) {
579
+ switch (type.k) {
580
+ case 'nullable': return writerOf(type.of);
581
+ case 'kind': return kindWriter(type.kind);
582
+ case 'model': {
583
+ const name = type.name;
584
+ return (w, v, d) => (isPlainObject(v) ? w.object(v, d, name) : w.write(v, d));
585
+ }
586
+ case 'list': {
587
+ const element = writerOf(type.of);
588
+ return (w, v, d) => (Array.isArray(v) ? w.list(v, element, d) : w.write(v, d));
589
+ }
590
+ case 'tuple': {
591
+ const items = type.items.map(writerOf);
592
+ return (w, v, d) => (Array.isArray(v) ? w.items(v, items, d) : w.write(v, d));
593
+ }
594
+ case 'pair': {
595
+ const items = [writerOf(type.key), writerOf(type.value)];
596
+ return (w, v, d) => {
597
+ if (!isPlainObject(v))
598
+ return w.write(v, d);
599
+ const pair = v;
600
+ if ('Key' in pair)
601
+ return w.items([pair.Key, pair.Value], items, d);
602
+ if ('key' in pair)
603
+ return w.items([pair.key, pair.value], items, d); // propertyNames: 'camelCase'
604
+ w.write(v, d);
605
+ };
606
+ }
607
+ case 'dict': {
608
+ const key = writerOf(type.key), value = writerOf(type.value);
609
+ return (w, v, d) => (isPlainObject(v) && !w.isModel(v) ? w.dictionary(v, key, value, d) : w.write(v, d));
610
+ }
611
+ }
612
+ return writeAsIs; // an entry of a schema: not in models
613
+ }
614
+ /** The values in the form the kind usually has are written without the dispatch of write. */
615
+ function kindWriter(kind) {
616
+ switch (kind) {
617
+ case 'string': return (w, v, d) => (typeof v === 'string' ? w.string(v) : w.write(v, d));
618
+ case 'guid': return (w, v, d) => (typeof v === 'string' ? w.guid(v) : w.write(v, d));
619
+ case 'any': return (w, v, d) => (isPlainObject(v) ? w.object(v, d, AnyDeclared) : w.write(v, d));
620
+ case 'datetime':
621
+ case 'dto':
622
+ case 'dto-clock':
623
+ return (w, v, d) => (v instanceof Date ? w.date(v) : typeof v === 'string' ? w.text(v, kind, d) : w.write(v, d));
624
+ case 'decimal':
625
+ case 'int':
626
+ case 'long':
627
+ case 'ulong':
628
+ case 'float':
629
+ return (w, v, d) => (typeof v === 'number' ? w.number(v) : typeof v === 'string' ? w.text(v, kind, d) : w.write(v, d));
630
+ case 'timespan':
631
+ case 'timeonly':
632
+ case 'dateonly':
633
+ case 'char':
634
+ return (w, v, d) => (typeof v === 'string' ? w.text(v, kind, d) : w.write(v, d));
635
+ }
636
+ return writeAsIs; // string, bool, bin, uri, bigint: as they are
637
+ }
638
+ let codeAllowed = true; // false once new Function failed (Content-Security-Policy without 'unsafe-eval')
639
+ /**
640
+ * The properties of a model class as code: the names as literals (V8 then sees one property load per place, of instances of one class), the encoded names and the
641
+ * value writers as constants (one function per call site). Undefined values are left out as in the generic loop.
642
+ */
643
+ function generateModelWriter(plan) {
644
+ const names = plan.names;
645
+ if (!codeAllowed || names.length === 0 || names.length > 512)
646
+ return null;
647
+ let code = `const ${names.map((_, t) => `e${t} = encoded[${t}], w${t} = writers[${t}]`).join(', ')};\nreturn function writeProperties(w, o, d) {\nlet c = 0, v;\n`;
648
+ names.forEach((n, t) => {
649
+ code += `v = o[${JSON.stringify(n)}];\nif (v !== undefined) { w.small(e${t}); w${t}(w, v, d); c++; }\n`;
650
+ });
651
+ code += 'return c;\n};';
652
+ try {
653
+ return new Function('encoded', 'writers', code)(plan.encoded, plan.writers);
654
+ }
655
+ catch {
656
+ codeAllowed = false;
657
+ return null;
658
+ }
659
+ }
660
+ const plans = new WeakMap();
661
+ function planOf(model) {
662
+ let plan = plans.get(model);
663
+ if (plan === undefined) {
664
+ plan = { names: model.properties.map((p) => p.field), writers: model.properties.map((p) => writerOf(p.type)), encoded: model.properties.map((p) => encode(p.name)) };
665
+ plans.set(model, plan);
666
+ }
667
+ return plan;
668
+ }
669
+ function sameKeys(keys, shape) {
670
+ if (shape === undefined || keys.length !== shape.length)
671
+ return false;
672
+ for (let t = 0; t < keys.length; t++)
673
+ if (keys[t] !== shape[t])
674
+ return false;
675
+ return true;
676
+ }
677
+ /** The characters of the hexadecimal digits of byte t of Guid.ToByteArray() in the text (the first three groups are little-endian). */
678
+ const GuidSource = [6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34];
679
+ function hexValue(c) {
680
+ if (c >= 48 && c <= 57)
681
+ return c - 48;
682
+ c |= 32; // lower case
683
+ return c >= 97 && c <= 102 ? c - 87 : -1;
684
+ }
685
+ //# sourceMappingURL=writer.js.map