densing 0.1.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/dist/index.js ADDED
@@ -0,0 +1,870 @@
1
+ // src/schema/builder.ts
2
+ var bool = (name, defaultValue = false) => ({
3
+ type: "bool",
4
+ name,
5
+ defaultValue
6
+ });
7
+ var int = (name, min, max, defaultValue) => {
8
+ if (max < min)
9
+ throw new Error(`int "${name}": max < min`);
10
+ return { type: "int", name, min, max, defaultValue: defaultValue ?? min };
11
+ };
12
+ var enumeration = (name, options, defaultValue) => {
13
+ if (options.length < 2)
14
+ throw new Error(`enum "${name}": must have at least 2 values`);
15
+ if (new Set(options).size !== options.length)
16
+ throw new Error(`enum "${name}": duplicate values`);
17
+ return { type: "enum", name, options, defaultValue: defaultValue ?? options[0] };
18
+ };
19
+ var fixed = (name, min, max, precision, defaultValue) => {
20
+ if (precision <= 0) {
21
+ throw new Error(`fixed "${name}": precision must be > 0`);
22
+ }
23
+ const scale = 1 / precision;
24
+ if (!Number.isInteger(scale)) {
25
+ throw new Error(`fixed "${name}": 1 / precision must be an integer`);
26
+ }
27
+ if (max < min) {
28
+ throw new Error(`fixed "${name}": max < min`);
29
+ }
30
+ return { type: "fixed", name, min, max, precision, defaultValue: defaultValue ?? min };
31
+ };
32
+ var minMaxValidation = (type, name, minLength, maxLength) => {
33
+ if (!Number.isInteger(minLength) || minLength < 0)
34
+ throw new Error(`fixedArray ${type} "${name}": minLength (${minLength}) must be a positive integer`);
35
+ if (!Number.isInteger(maxLength) || maxLength <= 0)
36
+ throw new Error(`fixedArray ${type} "${name}": maxLength (${maxLength}) must be a positive integer`);
37
+ if (maxLength < minLength)
38
+ throw new Error(`fixedArray ${type} "${name}": maxLength (${maxLength}) must be larger than minLength (${minLength})`);
39
+ };
40
+ var array = (name, minLength, maxLength, items) => {
41
+ minMaxValidation("array", name, minLength, maxLength);
42
+ return {
43
+ type: "array",
44
+ name,
45
+ minLength,
46
+ maxLength,
47
+ items
48
+ };
49
+ };
50
+ var discriminatorValidation = (name, discriminator, variants) => {
51
+ for (const value of discriminator.options)
52
+ if (!variants[value])
53
+ throw new Error(`union "${name}": missing variant definition for "${value}"`);
54
+ };
55
+ var union = (name, discriminator, variants) => {
56
+ discriminatorValidation(name, discriminator, variants);
57
+ return {
58
+ type: "union",
59
+ name,
60
+ discriminator,
61
+ variants
62
+ };
63
+ };
64
+ var enumArray = (name, enumDef, minLength, maxLength, defaultValue) => {
65
+ minMaxValidation("enum_array", name, minLength, maxLength);
66
+ return {
67
+ type: "enum_array",
68
+ name,
69
+ enum: enumDef,
70
+ minLength,
71
+ maxLength,
72
+ defaultValue: defaultValue ?? []
73
+ };
74
+ };
75
+ var optional = (name, field, defaultValue) => {
76
+ return {
77
+ type: "optional",
78
+ name,
79
+ field,
80
+ defaultValue
81
+ };
82
+ };
83
+ var object = (name, ...fields) => {
84
+ return {
85
+ type: "object",
86
+ name,
87
+ fields
88
+ };
89
+ };
90
+ var schema = (...fields) => ({
91
+ fields
92
+ });
93
+ // src/schema/default-data.ts
94
+ var getDefaultData = (schema2) => {
95
+ const result = {};
96
+ schema2.fields.forEach((field) => {
97
+ result[field.name] = getDefaultValueForField(field);
98
+ });
99
+ return result;
100
+ };
101
+ var getDefaultValueForField = (field) => {
102
+ switch (field.type) {
103
+ case "bool":
104
+ case "int":
105
+ case "enum":
106
+ case "fixed":
107
+ case "enum_array":
108
+ return field.defaultValue;
109
+ case "optional":
110
+ return field.defaultValue !== undefined ? field.defaultValue : null;
111
+ case "array":
112
+ return Array.from({ length: field.minLength }, () => getDefaultValueForField(field.items));
113
+ case "object":
114
+ return Object.fromEntries(field.fields.map((f) => [f.name, getDefaultValueForField(f)]));
115
+ case "union": {
116
+ const defaultDiscriminatorValue = field.discriminator.defaultValue;
117
+ const result = {
118
+ [field.discriminator.name]: defaultDiscriminatorValue
119
+ };
120
+ const variantFields = field.variants[defaultDiscriminatorValue];
121
+ if (variantFields) {
122
+ variantFields.forEach((variantField) => {
123
+ result[variantField.name] = getDefaultValueForField(variantField);
124
+ });
125
+ }
126
+ return result;
127
+ }
128
+ }
129
+ };
130
+ // src/schema/recursive-builder-helper.ts
131
+ var createRecursiveUnion = (name, discriminatorOptions, createVariants, maxDepth, currentDepth = 0) => {
132
+ if (currentDepth >= maxDepth) {
133
+ const terminalVariants = createVariants((fieldName) => {
134
+ return union(fieldName, enumeration("type", ["end", "dummy"]), {
135
+ end: [],
136
+ dummy: []
137
+ });
138
+ });
139
+ return union(name, enumeration("type", discriminatorOptions), terminalVariants);
140
+ }
141
+ const recursiveVariants = createVariants((fieldName, depth = currentDepth + 1) => {
142
+ return createRecursiveUnion(fieldName, discriminatorOptions, createVariants, maxDepth, depth);
143
+ });
144
+ return union(name, enumeration("type", discriminatorOptions), recursiveVariants);
145
+ };
146
+ // src/schema/type-generator.ts
147
+ import { writeFileSync } from "fs";
148
+ import { resolve } from "path";
149
+ var generateTypes = (schema2, rootTypeName = "SchemaData") => {
150
+ const types = [];
151
+ const processedTypes = new Set;
152
+ const rootFields = schema2.fields.map((field) => {
153
+ const fieldType = getFieldType(field, types, processedTypes);
154
+ return ` ${field.name}: ${fieldType};`;
155
+ }).join(`
156
+ `);
157
+ const rootType = `export interface ${rootTypeName} {
158
+ ${rootFields}
159
+ }`;
160
+ return [...types, rootType].join(`
161
+
162
+ `);
163
+ };
164
+ var generateTypesFile = (schema2, outputPath, rootTypeName = "SchemaData") => {
165
+ const types = generateTypes(schema2, rootTypeName);
166
+ const fullPath = resolve(outputPath);
167
+ writeFileSync(fullPath, types, "utf-8");
168
+ console.log(`Types written to: ${fullPath}`);
169
+ };
170
+ var getFieldType = (field, types, processedTypes) => {
171
+ switch (field.type) {
172
+ case "bool":
173
+ return "boolean";
174
+ case "int":
175
+ case "fixed":
176
+ return "number";
177
+ case "enum":
178
+ return field.options.map((opt) => `'${opt}'`).join(" | ");
179
+ case "enum_array":
180
+ return `(${field.enum.options.map((opt) => `'${opt}'`).join(" | ")})[]`;
181
+ case "array": {
182
+ const itemType = getFieldType(field.items, types, processedTypes);
183
+ return `${itemType}[]`;
184
+ }
185
+ case "optional": {
186
+ const innerType = getFieldType(field.field, types, processedTypes);
187
+ return `${innerType} | null`;
188
+ }
189
+ case "object": {
190
+ const typeName = capitalize(field.name);
191
+ if (!processedTypes.has(typeName)) {
192
+ processedTypes.add(typeName);
193
+ const objectFields = field.fields.map((f) => {
194
+ const fieldType = getFieldType(f, types, processedTypes);
195
+ return ` ${f.name}: ${fieldType};`;
196
+ }).join(`
197
+ `);
198
+ types.push(`export interface ${typeName} {
199
+ ${objectFields}
200
+ }`);
201
+ }
202
+ return typeName;
203
+ }
204
+ case "union": {
205
+ const typeName = capitalize(field.name);
206
+ if (!processedTypes.has(typeName)) {
207
+ processedTypes.add(typeName);
208
+ const options = field.discriminator.options;
209
+ const variants = options.map((option) => {
210
+ const variantTypeName = `${typeName}_${capitalize(option)}`;
211
+ const variantFields = field.variants[option] || [];
212
+ const fields = [
213
+ ` ${field.discriminator.name}: '${option}';`,
214
+ ...variantFields.map((f) => {
215
+ const fieldType = getFieldType(f, types, processedTypes);
216
+ return ` ${f.name}: ${fieldType};`;
217
+ })
218
+ ].join(`
219
+ `);
220
+ types.push(`export interface ${variantTypeName} {
221
+ ${fields}
222
+ }`);
223
+ return variantTypeName;
224
+ });
225
+ types.push(`export type ${typeName} = ${variants.join(" | ")};`);
226
+ }
227
+ return typeName;
228
+ }
229
+ default:
230
+ return "unknown";
231
+ }
232
+ };
233
+ var capitalize = (str) => {
234
+ return str.charAt(0).toUpperCase() + str.slice(1);
235
+ };
236
+ var printTypes = (schema2, rootTypeName = "SchemaData") => {
237
+ console.log(generateTypes(schema2, rootTypeName));
238
+ };
239
+ // src/schema/validation.ts
240
+ var validate = (schema2, data) => {
241
+ const errors = [];
242
+ for (const field of schema2.fields) {
243
+ validateField(field, data[field.name], field.name, errors);
244
+ }
245
+ return {
246
+ valid: errors.length === 0,
247
+ errors
248
+ };
249
+ };
250
+ var validateField = (field, value, path, errors) => {
251
+ if (field.type === "optional") {
252
+ if (value === undefined || value === null) {
253
+ return;
254
+ }
255
+ validateField(field.field, value, path, errors);
256
+ return;
257
+ }
258
+ if (value === undefined) {
259
+ errors.push({
260
+ path,
261
+ message: "missing value"
262
+ });
263
+ return;
264
+ }
265
+ switch (field.type) {
266
+ case "bool":
267
+ if (typeof value !== "boolean") {
268
+ errors.push({ path, message: "expected boolean" });
269
+ }
270
+ return;
271
+ case "int":
272
+ if (!Number.isInteger(value)) {
273
+ errors.push({ path, message: "expected integer" });
274
+ return;
275
+ }
276
+ if (value < field.min || value > field.max) {
277
+ errors.push({
278
+ path,
279
+ message: `value ${value} out of range [${field.min}, ${field.max}]`
280
+ });
281
+ }
282
+ return;
283
+ case "fixed": {
284
+ if (typeof value !== "number") {
285
+ errors.push({ path, message: "expected number" });
286
+ return;
287
+ }
288
+ const scale = 1 / field.precision;
289
+ const scaled = (value - field.min) * scale;
290
+ if (value < field.min || value > field.max) {
291
+ errors.push({
292
+ path,
293
+ message: `value ${value} out of range [${field.min}, ${field.max}]`
294
+ });
295
+ } else if (!Number.isInteger(Math.round(scaled))) {
296
+ errors.push({
297
+ path,
298
+ message: `value ${value} does not align with precision ${field.precision}`
299
+ });
300
+ }
301
+ return;
302
+ }
303
+ case "enum":
304
+ if (typeof value !== "string") {
305
+ errors.push({ path, message: "expected string for enum value" });
306
+ return;
307
+ }
308
+ if (!field.options.includes(value)) {
309
+ const optionsStr = field.options.join(", ");
310
+ errors.push({
311
+ path,
312
+ message: `invalid enum value ${value}, expected one of [${optionsStr}]`
313
+ });
314
+ }
315
+ return;
316
+ case "array":
317
+ if (!Array.isArray(value)) {
318
+ errors.push({ path, message: "expected array" });
319
+ return;
320
+ }
321
+ if (value.length < field.minLength) {
322
+ errors.push({
323
+ path,
324
+ message: `array length ${value.length} is less than minLength ${field.minLength}`
325
+ });
326
+ }
327
+ if (value.length > field.maxLength) {
328
+ errors.push({
329
+ path,
330
+ message: `array length ${value.length} exceeds maxLength ${field.maxLength}`
331
+ });
332
+ }
333
+ value.forEach((item, i) => validateField(field.items, item, `${path}[${i}]`, errors));
334
+ return;
335
+ case "union": {
336
+ if (typeof value !== "object" || value === null) {
337
+ errors.push({ path, message: "expected object" });
338
+ return;
339
+ }
340
+ const discName = field.discriminator.name;
341
+ const discValue = value[discName];
342
+ if (typeof discValue !== "string" || !field.discriminator.options.includes(discValue)) {
343
+ errors.push({
344
+ path: `${path}.${discName}`,
345
+ message: `invalid discriminator "${discValue}", expected one of [${field.discriminator.options.join(", ")}]`
346
+ });
347
+ return;
348
+ }
349
+ const variantFields = field.variants[discValue];
350
+ if (!variantFields) {
351
+ errors.push({
352
+ path: `${path}.${discName}`,
353
+ message: `no variant definition for discriminator "${discValue}"`
354
+ });
355
+ return;
356
+ }
357
+ for (const f of variantFields) {
358
+ validateField(f, value[f.name], `${path}.${f.name}`, errors);
359
+ }
360
+ return;
361
+ }
362
+ case "enum_array":
363
+ if (!Array.isArray(value)) {
364
+ errors.push({ path, message: "expected an array" });
365
+ return;
366
+ }
367
+ if (value.length < field.minLength) {
368
+ errors.push({
369
+ path,
370
+ message: `array length ${value.length} is less than minLength ${field.minLength}`
371
+ });
372
+ }
373
+ if (value.length > field.maxLength) {
374
+ errors.push({
375
+ path,
376
+ message: `array length ${value.length} exceeds maxLength ${field.maxLength}`
377
+ });
378
+ }
379
+ value.forEach((v, i) => {
380
+ if (typeof v !== "string" || !field.enum.options.includes(v)) {
381
+ const optionsStr = field.enum.options.join(", ");
382
+ errors.push({
383
+ path: `${path}[${i}]`,
384
+ message: `invalid enum value ${v}, expected one of [${optionsStr}]`
385
+ });
386
+ }
387
+ });
388
+ return;
389
+ case "object": {
390
+ if (typeof value !== "object" || value === null) {
391
+ errors.push({ path, message: "expected object" });
392
+ return;
393
+ }
394
+ for (const f of field.fields) {
395
+ validateField(f, value[f.name], `${path}.${f.name}`, errors);
396
+ }
397
+ return;
398
+ }
399
+ }
400
+ };
401
+ // src/helpers.ts
402
+ var BaseTypes = ["base64url", "baseQRCode45UrlSafe", "binary"];
403
+ var base64url = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
404
+ var baseQRCode45UrlSafe = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-.";
405
+ var binary = "01";
406
+ var baseCharTypes = {
407
+ base64url,
408
+ baseQRCode45UrlSafe,
409
+ binary
410
+ };
411
+ var getMinRequiredCharsForBase = (bitWidth, baseChars) => Math.ceil(bitWidth / Math.log2(baseChars.length));
412
+ var getMaxBitWidthForBase = (baseString, baseChars) => Math.floor(baseString.length * Math.log2(baseChars.length));
413
+ var getBaseStringFromBigInt = (bigInt, baseChars, bitWidth) => {
414
+ const minChars = bitWidth ? getMinRequiredCharsForBase(bitWidth, baseChars) : 0;
415
+ const maxBits = Math.floor(minChars * Math.log2(baseChars.length));
416
+ let adjustedBigInt = bigInt;
417
+ if (bitWidth && maxBits > bitWidth)
418
+ adjustedBigInt = bigInt << BigInt(maxBits - bitWidth);
419
+ const base = BigInt(baseChars.length);
420
+ const acc = [];
421
+ while (adjustedBigInt > 0)
422
+ acc.push(Number(adjustedBigInt % base)), adjustedBigInt = adjustedBigInt / base;
423
+ while (minChars > 0 && acc.length < minChars)
424
+ acc.push(0);
425
+ return acc.reverse().map((n) => baseChars.charAt(n)).join("");
426
+ };
427
+ var getCharsForBase = (base) => BaseTypes.includes(base) ? baseCharTypes[base] : base;
428
+ var getBigIntFromBaseString = (baseString, baseChars) => {
429
+ const base = BigInt(baseChars.length);
430
+ return baseString.split("").map((c) => baseChars.indexOf(c)).reduce((acc, n) => acc * base + BigInt(n), 0n);
431
+ };
432
+ var getBigIntFromBase64 = (base64) => getBigIntFromBaseString(base64, base64url);
433
+ var getBase64FromBigInt = (bigInt, bitWidth) => getBaseStringFromBigInt(bigInt, base64url, bitWidth);
434
+ var getbaseQRCode45UrlSafeFromBigInt = (bigInt, bitWidth) => getBaseStringFromBigInt(bigInt, baseQRCode45UrlSafe, bitWidth);
435
+ var getBigIntFrombaseQRCode45UrlSafe = (baseQRCode45UrlSafe2) => getBigIntFromBaseString(baseQRCode45UrlSafe2, baseQRCode45UrlSafe2);
436
+
437
+ class BitWriter {
438
+ buffer;
439
+ bitsWritten;
440
+ constructor() {
441
+ this.buffer = 0n;
442
+ this.bitsWritten = 0;
443
+ }
444
+ writeUInt = (value, bitWidth) => {
445
+ if (bitWidth <= 0)
446
+ return;
447
+ const bw = BigInt(bitWidth);
448
+ const v = BigInt(value);
449
+ const masked = v & (1n << bw) - 1n;
450
+ this.buffer = this.buffer << bw | masked;
451
+ this.bitsWritten += bitWidth;
452
+ };
453
+ getBigInt = () => this.buffer;
454
+ getBitLength = () => Number(this.bitsWritten);
455
+ getFromBase = (base = "base64url") => getBaseStringFromBigInt(this.buffer, getCharsForBase(base), this.bitsWritten);
456
+ }
457
+
458
+ class BitReader {
459
+ buffer;
460
+ bitsLeft;
461
+ constructor(bigInt, totalBits) {
462
+ this.buffer = bigInt;
463
+ this.bitsLeft = totalBits;
464
+ }
465
+ readUInt = (bitWidth) => {
466
+ if (bitWidth === 0)
467
+ return 0;
468
+ if (bitWidth > 32)
469
+ throw new Error("Cannot read more than 32 bits into a UInt at a time");
470
+ return Number(this.readUBigInt(bitWidth));
471
+ };
472
+ readUBigInt = (bitWidth) => {
473
+ if (bitWidth === 0)
474
+ return 0n;
475
+ if (bitWidth > this.bitsLeft)
476
+ throw new Error(`Not enough bits left (${this.bitsLeft}) when trying to get ${bitWidth}`);
477
+ this.bitsLeft -= bitWidth;
478
+ const shift = BigInt(this.bitsLeft);
479
+ const bw = BigInt(bitWidth);
480
+ const mask = (1n << bw) - 1n;
481
+ const value = this.buffer >> shift & mask;
482
+ return value;
483
+ };
484
+ static getFromBase = (baseString, base) => new BitReader(getBigIntFromBaseString(baseString, getCharsForBase(base)), getMaxBitWidthForBase(baseString, getCharsForBase(base)));
485
+ }
486
+
487
+ // src/densing.ts
488
+ var bitsForRange = (range) => range <= 1 ? 0 : Math.ceil(Math.log2(range));
489
+ var scaleForPrecision = (precision) => Math.round(1 / precision);
490
+ var bitsForInt = (min, max) => bitsForRange(Math.round(max - min) + 1);
491
+ var bitsForFixed = (min, max, precision) => bitsForRange(Math.round((max - min) * scaleForPrecision(precision)) + 1);
492
+ var uIntForRange = (value) => Math.round(value);
493
+ var uIntForInt = (value, min) => uIntForRange(value - min);
494
+ var uIntForFixed = (value, min, precision) => uIntForRange((value - min) * scaleForPrecision(precision));
495
+ var intFromUint = (uInt, min) => uIntForRange(uInt) + min;
496
+ var fixedFromUint = (uInt, min, precision) => uIntForRange(uInt) * precision + min;
497
+ var bitsForMinMaxLength = (minLength, maxLength) => bitsForRange(maxLength - minLength + 1);
498
+ var uIntForMinMaxLength = (value, minLength) => value - minLength;
499
+ var lengthForUIntMinMaxLength = (uInt, minLength) => uInt + minLength;
500
+ var bitsForEnumArrayContent = (length, base) => length < 1 ? 0 : Math.ceil(length * Math.log2(base));
501
+ var bitsForOptions = (options) => bitsForRange(options.length);
502
+ var sizeForOptions = (options) => options.length;
503
+ var densing = (denseSchema, data, base = "base64url") => {
504
+ const w = new BitWriter;
505
+ denseSchema.fields.forEach((f) => densingField(w, f, data[f.name]));
506
+ return w.getFromBase(base);
507
+ };
508
+ var getUIntForConstantBitWidthField = (field, value) => {
509
+ switch (field.type) {
510
+ case "bool":
511
+ return value ? 1 : 0;
512
+ case "int":
513
+ return uIntForInt(value, field.min);
514
+ case "enum":
515
+ return field.options.indexOf(value);
516
+ case "fixed":
517
+ return uIntForFixed(value, field.min, field.precision);
518
+ }
519
+ };
520
+ var getBitWidthForContantBitWidthFields = (field) => {
521
+ switch (field.type) {
522
+ case "bool":
523
+ return 1;
524
+ case "int":
525
+ return bitsForInt(field.min, field.max);
526
+ case "enum":
527
+ return bitsForOptions(field.options);
528
+ case "fixed":
529
+ return bitsForFixed(field.min, field.max, field.precision);
530
+ }
531
+ };
532
+ var densingField = (w, field, value) => {
533
+ switch (field.type) {
534
+ case "bool":
535
+ case "int":
536
+ case "enum":
537
+ case "fixed":
538
+ w.writeUInt(getUIntForConstantBitWidthField(field, value), getBitWidthForContantBitWidthFields(field));
539
+ break;
540
+ case "array": {
541
+ if (!Array.isArray(value))
542
+ throw new Error("value of `array` is not an array");
543
+ const arrayLengthBits = bitsForMinMaxLength(field.minLength, field.maxLength);
544
+ if (arrayLengthBits !== 0)
545
+ w.writeUInt(uIntForMinMaxLength(value.length, field.minLength), arrayLengthBits);
546
+ value.forEach((v) => densingField(w, field.items, v));
547
+ break;
548
+ }
549
+ case "union": {
550
+ const discValue = value[field.discriminator.name];
551
+ const discIdx = field.discriminator.options.indexOf(discValue);
552
+ if (discIdx === -1)
553
+ throw new Error(`Invalid union discriminator value: ${discValue}`);
554
+ w.writeUInt(discIdx, bitsForOptions(field.discriminator.options));
555
+ field.variants[discValue].forEach((f) => densingField(w, f, value[f.name]));
556
+ break;
557
+ }
558
+ case "enum_array": {
559
+ if (!Array.isArray(value))
560
+ throw new Error("value of `enum_array` is not an array");
561
+ const arrayLengthBits = bitsForMinMaxLength(field.minLength, field.maxLength);
562
+ if (arrayLengthBits !== 0)
563
+ w.writeUInt(uIntForMinMaxLength(value.length, field.minLength), arrayLengthBits);
564
+ const base = BigInt(sizeForOptions(field.enum.options));
565
+ const result = value.reduce((acc, c) => {
566
+ const idx = field.enum.options.indexOf(c);
567
+ if (idx === -1)
568
+ throw new Error(`Invalid enum value in array: ${c}`);
569
+ return acc * base + BigInt(idx);
570
+ }, 0n);
571
+ const contentBits = bitsForEnumArrayContent(value.length, sizeForOptions(field.enum.options));
572
+ w.writeUInt(result, contentBits);
573
+ break;
574
+ }
575
+ case "optional": {
576
+ const isPresent = value !== undefined && value !== null;
577
+ w.writeUInt(isPresent ? 1 : 0, 1);
578
+ if (isPresent) {
579
+ densingField(w, field.field, value);
580
+ }
581
+ break;
582
+ }
583
+ case "object": {
584
+ if (typeof value !== "object" || value === null)
585
+ throw new Error("value of `object` is not an object");
586
+ field.fields.forEach((f) => densingField(w, f, value[f.name]));
587
+ break;
588
+ }
589
+ }
590
+ };
591
+ var undensing = (denseSchema, baseString, base = "base64url") => {
592
+ const r = BitReader.getFromBase(baseString, base);
593
+ const obj = {};
594
+ denseSchema.fields.forEach((f) => obj[f.name] = undensingField(r, f));
595
+ return obj;
596
+ };
597
+ var undensingDataForConstantBitWidthField = (field, unsignedInt) => {
598
+ switch (field.type) {
599
+ case "bool":
600
+ return Boolean(unsignedInt);
601
+ case "int":
602
+ return intFromUint(unsignedInt, field.min);
603
+ case "enum":
604
+ return field.options[unsignedInt];
605
+ case "fixed":
606
+ return fixedFromUint(unsignedInt, field.min, field.precision);
607
+ }
608
+ };
609
+ var undensingField = (r, denseField) => {
610
+ switch (denseField.type) {
611
+ case "bool":
612
+ case "int":
613
+ case "enum":
614
+ case "fixed":
615
+ return undensingDataForConstantBitWidthField(denseField, r.readUInt(getBitWidthForContantBitWidthFields(denseField)));
616
+ case "array": {
617
+ const length2 = lengthForUIntMinMaxLength(r.readUInt(bitsForMinMaxLength(denseField.minLength, denseField.maxLength)), denseField.minLength);
618
+ return Array.from({ length: length2 }, () => undensingField(r, denseField.items));
619
+ }
620
+ case "union": {
621
+ const idx = r.readUInt(bitsForOptions(denseField.discriminator.options));
622
+ const key = denseField.discriminator.options[idx];
623
+ const obj = { [denseField.discriminator.name]: key };
624
+ denseField.variants[key].forEach((f) => obj[f.name] = undensingField(r, f));
625
+ return obj;
626
+ }
627
+ case "enum_array":
628
+ const base = BigInt(sizeForOptions(denseField.enum.options));
629
+ const arrayLengthBits = bitsForMinMaxLength(denseField.minLength, denseField.maxLength);
630
+ const length = lengthForUIntMinMaxLength(r.readUInt(arrayLengthBits), denseField.minLength);
631
+ const contentBits = bitsForEnumArrayContent(length, sizeForOptions(denseField.enum.options));
632
+ let bigIntValue = r.readUBigInt(contentBits);
633
+ const result = [];
634
+ for (let i = 0;i < length; i++) {
635
+ const idx = Number(bigIntValue % base);
636
+ result.unshift(denseField.enum.options[idx]);
637
+ bigIntValue = bigIntValue / base;
638
+ }
639
+ return result;
640
+ case "optional":
641
+ return Boolean(r.readUInt(1)) ? undensingField(r, denseField.field) : denseField.defaultValue !== undefined ? denseField.defaultValue : null;
642
+ case "object":
643
+ return Object.fromEntries(denseField.fields.map((f) => [f.name, undensingField(r, f)]));
644
+ }
645
+ };
646
+ // src/api.ts
647
+ var getDenseFieldBitWidthRange = (field) => {
648
+ switch (field.type) {
649
+ case "bool":
650
+ case "int":
651
+ case "fixed":
652
+ case "enum":
653
+ const bits = getBitWidthForContantBitWidthFields(field);
654
+ return { min: bits, max: bits };
655
+ case "optional": {
656
+ const innerRange = getDenseFieldBitWidthRange(field.field);
657
+ return {
658
+ min: 1,
659
+ max: 1 + innerRange.max
660
+ };
661
+ }
662
+ case "array": {
663
+ const lengthBits = bitsForMinMaxLength(field.minLength, field.maxLength);
664
+ const itemRange = getDenseFieldBitWidthRange(field.items);
665
+ return {
666
+ min: lengthBits + field.minLength * itemRange.min,
667
+ max: lengthBits + field.maxLength * itemRange.max
668
+ };
669
+ }
670
+ case "enum_array": {
671
+ const lengthBits = bitsForMinMaxLength(field.minLength, field.maxLength);
672
+ const base = field.enum.options.length;
673
+ const minContentBits = field.minLength === 0 ? 0 : Math.ceil(field.minLength * Math.log2(base));
674
+ const maxContentBits = field.maxLength === 0 ? 0 : Math.ceil(field.maxLength * Math.log2(base));
675
+ return {
676
+ min: lengthBits + minContentBits,
677
+ max: lengthBits + maxContentBits
678
+ };
679
+ }
680
+ case "object": {
681
+ return field.fields.reduce((acc, f) => {
682
+ const range = getDenseFieldBitWidthRange(f);
683
+ return { min: acc.min + range.min, max: acc.max + range.max };
684
+ }, { min: 0, max: 0 });
685
+ }
686
+ case "union": {
687
+ const discriminatorBits = bitsForOptions(field.discriminator.options);
688
+ const variantRanges = Object.values(field.variants).map((fields) => fields.reduce((sum, f) => {
689
+ const range = getDenseFieldBitWidthRange(f);
690
+ return { min: sum.min + range.min, max: sum.max + range.max };
691
+ }, { min: 0, max: 0 }));
692
+ return {
693
+ min: discriminatorBits + Math.min(...variantRanges.map((r) => r.min)),
694
+ max: discriminatorBits + Math.max(...variantRanges.map((r) => r.max))
695
+ };
696
+ }
697
+ default:
698
+ return { min: 0, max: 0 };
699
+ }
700
+ };
701
+ var calculateDenseFieldBitWidth = (field, value) => {
702
+ switch (field.type) {
703
+ case "bool":
704
+ case "int":
705
+ case "fixed":
706
+ case "enum":
707
+ return getBitWidthForContantBitWidthFields(field);
708
+ case "optional": {
709
+ const isPresent = value !== null && value !== undefined;
710
+ return 1 + (isPresent ? calculateDenseFieldBitWidth(field.field, value) : 0);
711
+ }
712
+ case "array": {
713
+ if (!Array.isArray(value))
714
+ return 0;
715
+ const lengthBits = bitsForMinMaxLength(field.minLength, field.maxLength);
716
+ const contentBits = value.reduce((sum, item) => sum + calculateDenseFieldBitWidth(field.items, item), 0);
717
+ return lengthBits + contentBits;
718
+ }
719
+ case "enum_array": {
720
+ if (!Array.isArray(value))
721
+ return 0;
722
+ const lengthBits = bitsForMinMaxLength(field.minLength, field.maxLength);
723
+ const base = field.enum.options.length;
724
+ const contentBits = value.length === 0 ? 0 : Math.ceil(value.length * Math.log2(base));
725
+ return lengthBits + contentBits;
726
+ }
727
+ case "object": {
728
+ return field.fields.reduce((sum, f) => sum + calculateDenseFieldBitWidth(f, value?.[f.name]), 0);
729
+ }
730
+ case "union": {
731
+ const discriminatorBits = bitsForOptions(field.discriminator.options);
732
+ const variantType = value?.[field.discriminator.name];
733
+ if (!variantType)
734
+ return discriminatorBits;
735
+ const variantFields = field.variants[variantType] || [];
736
+ const variantBits = variantFields.reduce((sum, f) => sum + calculateDenseFieldBitWidth(f, value), 0);
737
+ return discriminatorBits + variantBits;
738
+ }
739
+ default:
740
+ return 0;
741
+ }
742
+ };
743
+ var analyzeDenseSchemaSize = (schema2) => {
744
+ const fieldRanges = {};
745
+ let totalMin = 0;
746
+ let totalMax = 0;
747
+ schema2.fields.forEach((field) => {
748
+ const range = getDenseFieldBitWidthRange(field);
749
+ fieldRanges[field.name] = range;
750
+ totalMin += range.min;
751
+ totalMax += range.max;
752
+ });
753
+ return {
754
+ staticRange: {
755
+ minBits: totalMin,
756
+ maxBits: totalMax,
757
+ minBytes: Math.ceil(totalMin / 8),
758
+ maxBytes: Math.ceil(totalMax / 8),
759
+ minBase64Chars: Math.ceil(totalMin / 6),
760
+ maxBase64Chars: Math.ceil(totalMax / 6)
761
+ },
762
+ fieldRanges
763
+ };
764
+ };
765
+ var calculateDenseDataSize = (schema2, data) => {
766
+ const schemaAnalysis = analyzeDenseSchemaSize(schema2);
767
+ const fieldSizes = {};
768
+ let totalBits = 0;
769
+ schema2.fields.forEach((field) => {
770
+ const bits = calculateDenseFieldBitWidth(field, data[field.name]);
771
+ fieldSizes[field.name] = bits;
772
+ totalBits += bits;
773
+ });
774
+ const minBits = schemaAnalysis.staticRange.minBits;
775
+ const maxBits = schemaAnalysis.staticRange.maxBits;
776
+ const utilizationPercent = maxBits === minBits ? 100 : (totalBits - minBits) / (maxBits - minBits) * 100;
777
+ return {
778
+ totalBits,
779
+ totalBytes: Math.ceil(totalBits / 8),
780
+ base64Length: Math.ceil(totalBits / 6),
781
+ fieldSizes,
782
+ efficiency: {
783
+ usedBits: totalBits,
784
+ minPossibleBits: minBits,
785
+ maxPossibleBits: maxBits,
786
+ utilizationPercent
787
+ }
788
+ };
789
+ };
790
+ var getFieldByPath = (schema2, path) => {
791
+ const parts = path.split(".");
792
+ let current = null;
793
+ for (let i = 0;i < parts.length; i++) {
794
+ const part = parts[i];
795
+ const fields = i === 0 ? schema2.fields : current?.fields;
796
+ if (!fields)
797
+ return null;
798
+ current = fields.find((f) => f.name === part) || null;
799
+ if (!current)
800
+ return null;
801
+ if (current.type === "array" && i < parts.length - 1) {
802
+ current = current.items;
803
+ }
804
+ }
805
+ return current;
806
+ };
807
+ var walkDenseSchema = (schema2, callback, prefix = "") => {
808
+ schema2.fields.forEach((field) => {
809
+ const fieldPath = prefix ? `${prefix}.${field.name}` : field.name;
810
+ callback(field, fieldPath);
811
+ if (field.type === "object") {
812
+ walkDenseSchema({ fields: field.fields }, callback, fieldPath);
813
+ } else if (field.type === "array" && field.items.type === "object") {
814
+ walkDenseSchema({ fields: field.items.fields }, callback, `${fieldPath}[]`);
815
+ } else if (field.type === "optional" && field.field.type === "object") {
816
+ walkDenseSchema({ fields: field.field.fields }, callback, fieldPath);
817
+ }
818
+ });
819
+ };
820
+ var getAllDenseSchemaPaths = (schema2) => {
821
+ const paths = [];
822
+ walkDenseSchema(schema2, (field, path) => paths.push(path));
823
+ return paths;
824
+ };
825
+ export {
826
+ walkDenseSchema,
827
+ validate,
828
+ union,
829
+ undensingField,
830
+ undensingDataForConstantBitWidthField,
831
+ undensing,
832
+ schema,
833
+ printTypes,
834
+ optional,
835
+ object,
836
+ lengthForUIntMinMaxLength,
837
+ int,
838
+ getbaseQRCode45UrlSafeFromBigInt,
839
+ getUIntForConstantBitWidthField,
840
+ getFieldByPath,
841
+ getDenseFieldBitWidthRange,
842
+ getDefaultData,
843
+ getBitWidthForContantBitWidthFields,
844
+ getBigIntFrombaseQRCode45UrlSafe,
845
+ getBigIntFromBase64,
846
+ getBase64FromBigInt,
847
+ getAllDenseSchemaPaths,
848
+ generateTypesFile,
849
+ generateTypes,
850
+ fixed,
851
+ enumeration,
852
+ enumArray,
853
+ densingField,
854
+ densing,
855
+ createRecursiveUnion,
856
+ calculateDenseFieldBitWidth,
857
+ calculateDenseDataSize,
858
+ bool,
859
+ bitsForRange,
860
+ bitsForOptions,
861
+ bitsForMinMaxLength,
862
+ binary,
863
+ baseQRCode45UrlSafe,
864
+ base64url,
865
+ array,
866
+ analyzeDenseSchemaSize,
867
+ BitWriter,
868
+ BitReader,
869
+ BaseTypes
870
+ };