q-type 0.1.2
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/README.md +312 -0
- package/dist/index.cjs +602 -0
- package/dist/index.d.cts +612 -0
- package/dist/index.d.ts +612 -0
- package/dist/index.js +559 -0
- package/package.json +26 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
var import_node_module = require("node:module");
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
function __accessProp(key) {
|
|
7
|
+
return this[key];
|
|
8
|
+
}
|
|
9
|
+
var __toCommonJS = (from) => {
|
|
10
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
11
|
+
if (entry)
|
|
12
|
+
return entry;
|
|
13
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (var key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(entry, key))
|
|
17
|
+
__defProp(entry, key, {
|
|
18
|
+
get: __accessProp.bind(from, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
__moduleCache.set(from, entry);
|
|
23
|
+
return entry;
|
|
24
|
+
};
|
|
25
|
+
var __moduleCache;
|
|
26
|
+
var __returnValue = (v) => v;
|
|
27
|
+
function __exportSetter(name, newValue) {
|
|
28
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
29
|
+
}
|
|
30
|
+
var __export = (target, all) => {
|
|
31
|
+
for (var name in all)
|
|
32
|
+
__defProp(target, name, {
|
|
33
|
+
get: all[name],
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
set: __exportSetter.bind(all, name)
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/index.ts
|
|
41
|
+
var exports_src = {};
|
|
42
|
+
__export(exports_src, {
|
|
43
|
+
q: () => q,
|
|
44
|
+
isPositioned: () => isPositioned,
|
|
45
|
+
QtypeParseError: () => QtypeParseError,
|
|
46
|
+
QUnion: () => QUnion,
|
|
47
|
+
QType: () => QType,
|
|
48
|
+
QTuple: () => QTuple,
|
|
49
|
+
QTransform: () => QTransform,
|
|
50
|
+
QString: () => QString,
|
|
51
|
+
QSchema: () => QSchema,
|
|
52
|
+
QPositioned: () => QPositioned,
|
|
53
|
+
QOptional: () => QOptional,
|
|
54
|
+
QObject: () => QObject,
|
|
55
|
+
QNumber: () => QNumber,
|
|
56
|
+
QNullable: () => QNullable,
|
|
57
|
+
QLiteral: () => QLiteral,
|
|
58
|
+
QInteger: () => QInteger,
|
|
59
|
+
QEnum: () => QEnum,
|
|
60
|
+
QDefault: () => QDefault,
|
|
61
|
+
QDate: () => QDate,
|
|
62
|
+
QBoolean: () => QBoolean,
|
|
63
|
+
QArray: () => QArray
|
|
64
|
+
});
|
|
65
|
+
module.exports = __toCommonJS(exports_src);
|
|
66
|
+
|
|
67
|
+
// src/types.ts
|
|
68
|
+
class QType {
|
|
69
|
+
}
|
|
70
|
+
// src/errors.ts
|
|
71
|
+
class QtypeParseError extends Error {
|
|
72
|
+
key;
|
|
73
|
+
path;
|
|
74
|
+
rawValue;
|
|
75
|
+
constructor(message, ctx) {
|
|
76
|
+
super(`[Qtype] ${ctx.path}: ${message} (got "${ctx.raw}")`);
|
|
77
|
+
this.name = "QtypeParseError";
|
|
78
|
+
this.key = ctx.key;
|
|
79
|
+
this.path = ctx.path;
|
|
80
|
+
this.rawValue = ctx.raw;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// src/primitives.ts
|
|
84
|
+
class QString extends QType {
|
|
85
|
+
kind = "string";
|
|
86
|
+
decode(raw, ctx) {
|
|
87
|
+
if (raw === "") {
|
|
88
|
+
throw new QtypeParseError("required segment is empty", ctx);
|
|
89
|
+
}
|
|
90
|
+
return raw;
|
|
91
|
+
}
|
|
92
|
+
encode(value, _opts) {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class QNumber extends QType {
|
|
98
|
+
kind = "number";
|
|
99
|
+
decode(raw, ctx) {
|
|
100
|
+
if (raw === "") {
|
|
101
|
+
throw new QtypeParseError("required segment is empty", ctx);
|
|
102
|
+
}
|
|
103
|
+
const n = Number(raw);
|
|
104
|
+
if (Number.isNaN(n)) {
|
|
105
|
+
throw new QtypeParseError(`"${raw}" is not a valid number`, ctx);
|
|
106
|
+
}
|
|
107
|
+
return n;
|
|
108
|
+
}
|
|
109
|
+
encode(value, _opts) {
|
|
110
|
+
return String(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
class QInteger extends QType {
|
|
115
|
+
kind = "integer";
|
|
116
|
+
radix;
|
|
117
|
+
constructor(radix = 10) {
|
|
118
|
+
super();
|
|
119
|
+
this.radix = radix;
|
|
120
|
+
}
|
|
121
|
+
decode(raw, ctx) {
|
|
122
|
+
if (raw === "") {
|
|
123
|
+
throw new QtypeParseError("required segment is empty", ctx);
|
|
124
|
+
}
|
|
125
|
+
const n = parseInt(raw, this.radix);
|
|
126
|
+
if (Number.isNaN(n)) {
|
|
127
|
+
throw new QtypeParseError(`"${raw}" is not a valid integer`, ctx);
|
|
128
|
+
}
|
|
129
|
+
return n;
|
|
130
|
+
}
|
|
131
|
+
encode(value, _opts) {
|
|
132
|
+
return value.toString(this.radix);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class QBoolean extends QType {
|
|
137
|
+
kind = "boolean";
|
|
138
|
+
truthy;
|
|
139
|
+
constructor(truthy = ["1", "true", "Y", "y"]) {
|
|
140
|
+
super();
|
|
141
|
+
this.truthy = truthy;
|
|
142
|
+
}
|
|
143
|
+
decode(raw, ctx) {
|
|
144
|
+
if (raw === "") {
|
|
145
|
+
throw new QtypeParseError("required segment is empty", ctx);
|
|
146
|
+
}
|
|
147
|
+
return this.truthy.includes(raw);
|
|
148
|
+
}
|
|
149
|
+
encode(value, _opts) {
|
|
150
|
+
return value ? this.truthy[0] ?? "1" : "0";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class QDate extends QType {
|
|
155
|
+
kind = "date";
|
|
156
|
+
format;
|
|
157
|
+
constructor(format = "iso") {
|
|
158
|
+
super();
|
|
159
|
+
this.format = format;
|
|
160
|
+
}
|
|
161
|
+
decode(raw, ctx) {
|
|
162
|
+
if (raw === "") {
|
|
163
|
+
throw new QtypeParseError("required segment is empty", ctx);
|
|
164
|
+
}
|
|
165
|
+
let d;
|
|
166
|
+
if (this.format === "yyyyMMdd") {
|
|
167
|
+
if (!/^\d{8}$/.test(raw)) {
|
|
168
|
+
throw new QtypeParseError(`"${raw}" does not match yyyyMMdd`, ctx);
|
|
169
|
+
}
|
|
170
|
+
const Y = parseInt(raw.slice(0, 4), 10);
|
|
171
|
+
const M = parseInt(raw.slice(4, 6), 10) - 1;
|
|
172
|
+
const D = parseInt(raw.slice(6, 8), 10);
|
|
173
|
+
d = new Date(Y, M, D);
|
|
174
|
+
} else {
|
|
175
|
+
d = new Date(raw);
|
|
176
|
+
}
|
|
177
|
+
if (Number.isNaN(d.getTime())) {
|
|
178
|
+
throw new QtypeParseError(`"${raw}" is not a valid date`, ctx);
|
|
179
|
+
}
|
|
180
|
+
return d;
|
|
181
|
+
}
|
|
182
|
+
encode(value, _opts) {
|
|
183
|
+
if (this.format === "yyyyMMdd") {
|
|
184
|
+
const y = value.getFullYear();
|
|
185
|
+
const m = String(value.getMonth() + 1).padStart(2, "0");
|
|
186
|
+
const d = String(value.getDate()).padStart(2, "0");
|
|
187
|
+
return `${y}${m}${d}`;
|
|
188
|
+
}
|
|
189
|
+
return value.toISOString();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class QLiteral extends QType {
|
|
194
|
+
kind = "literal";
|
|
195
|
+
value;
|
|
196
|
+
constructor(value) {
|
|
197
|
+
super();
|
|
198
|
+
this.value = value;
|
|
199
|
+
}
|
|
200
|
+
decode(raw, ctx) {
|
|
201
|
+
if (raw !== String(this.value)) {
|
|
202
|
+
throw new QtypeParseError(`expected literal "${String(this.value)}", got "${raw}"`, ctx);
|
|
203
|
+
}
|
|
204
|
+
return this.value;
|
|
205
|
+
}
|
|
206
|
+
encode(value, _opts) {
|
|
207
|
+
return String(value);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
class QEnum extends QType {
|
|
212
|
+
kind = "enum";
|
|
213
|
+
values;
|
|
214
|
+
constructor(values) {
|
|
215
|
+
super();
|
|
216
|
+
this.values = values;
|
|
217
|
+
}
|
|
218
|
+
decode(raw, ctx) {
|
|
219
|
+
if (raw === "" || !this.values.includes(raw)) {
|
|
220
|
+
throw new QtypeParseError(`"${raw}" is not one of [${this.values.join(", ")}]`, ctx);
|
|
221
|
+
}
|
|
222
|
+
return raw;
|
|
223
|
+
}
|
|
224
|
+
encode(value, _opts) {
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// src/combinators.ts
|
|
229
|
+
class QOptional extends QType {
|
|
230
|
+
kind = "optional";
|
|
231
|
+
inner;
|
|
232
|
+
constructor(schema) {
|
|
233
|
+
super();
|
|
234
|
+
this.inner = schema;
|
|
235
|
+
}
|
|
236
|
+
decode(raw, ctx) {
|
|
237
|
+
if (raw === "")
|
|
238
|
+
return;
|
|
239
|
+
return this.inner.decode(raw, ctx);
|
|
240
|
+
}
|
|
241
|
+
encode(value, _opts) {
|
|
242
|
+
if (value === undefined)
|
|
243
|
+
return "";
|
|
244
|
+
return this.inner.encode(value);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
class QNullable extends QType {
|
|
249
|
+
kind = "nullable";
|
|
250
|
+
inner;
|
|
251
|
+
constructor(schema) {
|
|
252
|
+
super();
|
|
253
|
+
this.inner = schema;
|
|
254
|
+
}
|
|
255
|
+
decode(raw, ctx) {
|
|
256
|
+
if (raw === "")
|
|
257
|
+
return null;
|
|
258
|
+
return this.inner.decode(raw, ctx);
|
|
259
|
+
}
|
|
260
|
+
encode(value, _opts) {
|
|
261
|
+
if (value === null)
|
|
262
|
+
return "";
|
|
263
|
+
return this.inner.encode(value);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
class QDefault extends QType {
|
|
268
|
+
kind = "default";
|
|
269
|
+
inner;
|
|
270
|
+
defaultValue;
|
|
271
|
+
constructor(schema, defaultValue) {
|
|
272
|
+
super();
|
|
273
|
+
this.inner = schema;
|
|
274
|
+
this.defaultValue = defaultValue;
|
|
275
|
+
}
|
|
276
|
+
decode(raw, ctx) {
|
|
277
|
+
if (raw === "")
|
|
278
|
+
return this.defaultValue;
|
|
279
|
+
return this.inner.decode(raw, ctx);
|
|
280
|
+
}
|
|
281
|
+
encode(value, _opts) {
|
|
282
|
+
return this.inner.encode(value);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
class QTransform extends QType {
|
|
287
|
+
kind = "transform";
|
|
288
|
+
inner;
|
|
289
|
+
decodeFn;
|
|
290
|
+
encodeFn;
|
|
291
|
+
constructor(schema, decodeFn, encodeFn) {
|
|
292
|
+
super();
|
|
293
|
+
this.inner = schema;
|
|
294
|
+
this.decodeFn = decodeFn;
|
|
295
|
+
this.encodeFn = encodeFn;
|
|
296
|
+
}
|
|
297
|
+
decode(raw, ctx) {
|
|
298
|
+
return this.decodeFn(this.inner.decode(raw, ctx));
|
|
299
|
+
}
|
|
300
|
+
encode(value, opts) {
|
|
301
|
+
return this.inner.encode(this.encodeFn(value), opts);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
class QPositioned extends QType {
|
|
306
|
+
kind = "positioned";
|
|
307
|
+
index;
|
|
308
|
+
inner;
|
|
309
|
+
constructor(index, schema) {
|
|
310
|
+
super();
|
|
311
|
+
this.index = index;
|
|
312
|
+
this.inner = schema;
|
|
313
|
+
}
|
|
314
|
+
decode(raw, ctx) {
|
|
315
|
+
return this.inner.decode(raw, ctx);
|
|
316
|
+
}
|
|
317
|
+
encode(value, opts) {
|
|
318
|
+
return this.inner.encode(value, opts);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function isPositioned(schema) {
|
|
322
|
+
return schema.kind === "positioned";
|
|
323
|
+
}
|
|
324
|
+
// src/compound.ts
|
|
325
|
+
class QArray extends QType {
|
|
326
|
+
kind = "array";
|
|
327
|
+
element;
|
|
328
|
+
sep;
|
|
329
|
+
constructor(schema, opts) {
|
|
330
|
+
super();
|
|
331
|
+
this.element = schema;
|
|
332
|
+
this.sep = opts?.sep ?? ";";
|
|
333
|
+
}
|
|
334
|
+
decode(raw, ctx) {
|
|
335
|
+
if (raw === "")
|
|
336
|
+
return [];
|
|
337
|
+
return raw.split(this.sep).map((part, i) => this.element.decode(part, {
|
|
338
|
+
key: `${ctx.key}[${i}]`,
|
|
339
|
+
path: `${ctx.path}[${i}]`,
|
|
340
|
+
raw: part
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
encode(value, _opts) {
|
|
344
|
+
return value.map((v) => this.element.encode(v)).join(this.sep);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
class QObject extends QType {
|
|
349
|
+
kind = "object";
|
|
350
|
+
properties;
|
|
351
|
+
sep;
|
|
352
|
+
kvSep;
|
|
353
|
+
constructor(properties, opts) {
|
|
354
|
+
super();
|
|
355
|
+
this.properties = properties;
|
|
356
|
+
this.sep = opts?.sep ?? ";";
|
|
357
|
+
this.kvSep = opts?.kvSep ?? ":";
|
|
358
|
+
}
|
|
359
|
+
decode(raw, ctx) {
|
|
360
|
+
const result = {};
|
|
361
|
+
if (raw === "") {
|
|
362
|
+
throw new QtypeParseError("empty segment for Object", ctx);
|
|
363
|
+
}
|
|
364
|
+
const pairs = raw.split(this.sep);
|
|
365
|
+
const seen = new Set;
|
|
366
|
+
for (const pair of pairs) {
|
|
367
|
+
const idx = pair.indexOf(this.kvSep);
|
|
368
|
+
if (idx === -1) {
|
|
369
|
+
throw new QtypeParseError(`"${pair}" is not a valid key-value pair (expected "${this.kvSep}" separator)`, ctx);
|
|
370
|
+
}
|
|
371
|
+
const key = pair.slice(0, idx);
|
|
372
|
+
const value = pair.slice(idx + 1);
|
|
373
|
+
if (!(key in this.properties)) {
|
|
374
|
+
throw new QtypeParseError(`unknown key "${key}"`, {
|
|
375
|
+
...ctx,
|
|
376
|
+
key: `${ctx.key}.${key}`
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
seen.add(key);
|
|
380
|
+
const schema = this.properties[key];
|
|
381
|
+
result[key] = schema.decode(value, {
|
|
382
|
+
key: `${ctx.key}.${key}`,
|
|
383
|
+
path: `${ctx.path}.${key}`,
|
|
384
|
+
raw: value
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
for (const key of Object.keys(this.properties)) {
|
|
388
|
+
if (!seen.has(key)) {
|
|
389
|
+
const schema = this.properties[key];
|
|
390
|
+
result[key] = schema.decode("", {
|
|
391
|
+
key: `${ctx.key}.${key}`,
|
|
392
|
+
path: `${ctx.path}.${key}`,
|
|
393
|
+
raw: ""
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
encode(value, _opts) {
|
|
400
|
+
const parts = [];
|
|
401
|
+
for (const [key, schema] of Object.entries(this.properties)) {
|
|
402
|
+
const v = value[key];
|
|
403
|
+
if (v === undefined || v === null)
|
|
404
|
+
continue;
|
|
405
|
+
parts.push(`${key}${this.kvSep}${schema.encode(v)}`);
|
|
406
|
+
}
|
|
407
|
+
return parts.join(this.sep);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
class QTuple extends QType {
|
|
412
|
+
kind = "tuple";
|
|
413
|
+
items;
|
|
414
|
+
sep;
|
|
415
|
+
constructor(items, opts) {
|
|
416
|
+
super();
|
|
417
|
+
this.items = items;
|
|
418
|
+
this.sep = opts?.sep ?? ";";
|
|
419
|
+
}
|
|
420
|
+
decode(raw, ctx) {
|
|
421
|
+
if (raw === "") {
|
|
422
|
+
throw new QtypeParseError("empty segment for Tuple", ctx);
|
|
423
|
+
}
|
|
424
|
+
const parts = raw.split(this.sep);
|
|
425
|
+
if (parts.length < this.items.length) {
|
|
426
|
+
throw new QtypeParseError(`tuple expected ${this.items.length} elements but got ${parts.length}`, ctx);
|
|
427
|
+
}
|
|
428
|
+
return this.items.map((schema, i) => schema.decode(parts[i] ?? "", {
|
|
429
|
+
key: `${ctx.key}[${i}]`,
|
|
430
|
+
path: `${ctx.path}[${i}]`,
|
|
431
|
+
raw: parts[i] ?? ""
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
encode(value, _opts) {
|
|
435
|
+
return this.items.map((schema, i) => schema.encode(value[i])).join(this.sep);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
class QSchema extends QType {
|
|
440
|
+
kind = "schema";
|
|
441
|
+
properties;
|
|
442
|
+
delimiter;
|
|
443
|
+
positions;
|
|
444
|
+
constructor(properties, opts) {
|
|
445
|
+
super();
|
|
446
|
+
this.properties = properties;
|
|
447
|
+
this.delimiter = opts?.delimiter ?? "|";
|
|
448
|
+
const entries = Object.entries(properties);
|
|
449
|
+
const explicitPos = new Set(entries.filter(([, s]) => isPositioned(s)).map(([, s]) => s.index));
|
|
450
|
+
const map = new Map;
|
|
451
|
+
let nextAuto = 0;
|
|
452
|
+
for (const [key, schema] of entries) {
|
|
453
|
+
if (isPositioned(schema)) {
|
|
454
|
+
map.set(key, schema.index);
|
|
455
|
+
} else {
|
|
456
|
+
while (explicitPos.has(nextAuto))
|
|
457
|
+
nextAuto++;
|
|
458
|
+
map.set(key, nextAuto);
|
|
459
|
+
nextAuto++;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
this.positions = map;
|
|
463
|
+
}
|
|
464
|
+
parse(raw) {
|
|
465
|
+
return this.decode(raw, { key: "$", path: "$", raw });
|
|
466
|
+
}
|
|
467
|
+
safeParse(raw) {
|
|
468
|
+
try {
|
|
469
|
+
return { success: true, data: this.parse(raw) };
|
|
470
|
+
} catch (err) {
|
|
471
|
+
if (err instanceof QtypeParseError) {
|
|
472
|
+
return { success: false, error: err };
|
|
473
|
+
}
|
|
474
|
+
throw err;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
decode(raw, ctx) {
|
|
478
|
+
const segments = raw.split(this.delimiter);
|
|
479
|
+
const result = {};
|
|
480
|
+
for (const [key, schema] of Object.entries(this.properties)) {
|
|
481
|
+
const pos = this.positions.get(key);
|
|
482
|
+
const segment = segments[pos] ?? "";
|
|
483
|
+
try {
|
|
484
|
+
result[key] = schema.decode(segment, {
|
|
485
|
+
key,
|
|
486
|
+
path: ctx.path !== "$" ? `${ctx.path}.${key}` : key,
|
|
487
|
+
raw: segment
|
|
488
|
+
});
|
|
489
|
+
} catch (err) {
|
|
490
|
+
if (err instanceof QtypeParseError)
|
|
491
|
+
throw err;
|
|
492
|
+
throw new QtypeParseError(err instanceof Error ? err.message : String(err), {
|
|
493
|
+
key,
|
|
494
|
+
path: ctx.path !== "$" ? `${ctx.path}.${key}` : key,
|
|
495
|
+
raw: segment
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
500
|
+
}
|
|
501
|
+
encode(value, opts) {
|
|
502
|
+
const entries = Object.entries(this.properties);
|
|
503
|
+
const indices = entries.map(([key]) => this.positions.get(key));
|
|
504
|
+
const maxIndex = indices.length > 0 ? Math.max(...indices) : -1;
|
|
505
|
+
const width = Math.max(opts?.length ?? 0, maxIndex + 1);
|
|
506
|
+
const out = new Array(width).fill("");
|
|
507
|
+
for (const [key, schema] of entries) {
|
|
508
|
+
const pos = this.positions.get(key);
|
|
509
|
+
out[pos] = schema.encode(value[key]);
|
|
510
|
+
}
|
|
511
|
+
return out.join(this.delimiter);
|
|
512
|
+
}
|
|
513
|
+
serialize(value, opts) {
|
|
514
|
+
return this.encode(value, opts);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// src/union.ts
|
|
518
|
+
class QUnion extends QType {
|
|
519
|
+
kind = "union";
|
|
520
|
+
variants;
|
|
521
|
+
discriminator;
|
|
522
|
+
constructor(variants, opts) {
|
|
523
|
+
super();
|
|
524
|
+
this.variants = variants;
|
|
525
|
+
this.discriminator = opts?.discriminator;
|
|
526
|
+
}
|
|
527
|
+
parse(raw) {
|
|
528
|
+
return this.decode(raw, { key: "$", path: "$", raw });
|
|
529
|
+
}
|
|
530
|
+
safeParse(raw) {
|
|
531
|
+
try {
|
|
532
|
+
return { success: true, data: this.parse(raw) };
|
|
533
|
+
} catch (err) {
|
|
534
|
+
if (err instanceof QtypeParseError)
|
|
535
|
+
return { success: false, error: err };
|
|
536
|
+
throw err;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
decode(raw, ctx) {
|
|
540
|
+
const errors = [];
|
|
541
|
+
for (const variant of this.variants) {
|
|
542
|
+
try {
|
|
543
|
+
return variant.decode(raw, ctx);
|
|
544
|
+
} catch (err) {
|
|
545
|
+
errors.push(err instanceof Error ? err.message : String(err));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
throw new QtypeParseError(`no variant matched: ${errors.join("; ")}`, ctx);
|
|
549
|
+
}
|
|
550
|
+
encode(value, opts) {
|
|
551
|
+
for (const variant of this.variants) {
|
|
552
|
+
try {
|
|
553
|
+
return variant.encode(value, opts);
|
|
554
|
+
} catch {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
throw new Error("QUnion: no variant could encode the value");
|
|
559
|
+
}
|
|
560
|
+
serialize(value, opts) {
|
|
561
|
+
return this.encode(value, opts);
|
|
562
|
+
}
|
|
563
|
+
match(value) {
|
|
564
|
+
if (!this.discriminator)
|
|
565
|
+
return;
|
|
566
|
+
for (const variant of this.variants) {
|
|
567
|
+
if (variant instanceof QSchema) {
|
|
568
|
+
const props = variant.properties;
|
|
569
|
+
const discSchema = props[this.discriminator];
|
|
570
|
+
if (!discSchema)
|
|
571
|
+
continue;
|
|
572
|
+
if (discSchema instanceof QLiteral && String(discSchema.value) === value) {
|
|
573
|
+
return variant;
|
|
574
|
+
}
|
|
575
|
+
if (discSchema instanceof QEnum && discSchema.values.includes(value)) {
|
|
576
|
+
return variant;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// src/index.ts
|
|
584
|
+
var q = {
|
|
585
|
+
String: () => new QString,
|
|
586
|
+
Number: () => new QNumber,
|
|
587
|
+
Integer: (radix) => new QInteger(radix),
|
|
588
|
+
Boolean: (truthy) => new QBoolean(truthy),
|
|
589
|
+
Date: (format) => new QDate(format),
|
|
590
|
+
Literal: (value) => new QLiteral(value),
|
|
591
|
+
Enum: (values) => new QEnum(values),
|
|
592
|
+
Array: (schema, opts) => new QArray(schema, opts),
|
|
593
|
+
Object: (props, opts) => new QObject(props, opts),
|
|
594
|
+
Tuple: (items, opts) => new QTuple(items, opts),
|
|
595
|
+
Schema: (props, opts) => new QSchema(props, opts),
|
|
596
|
+
Optional: (schema) => new QOptional(schema),
|
|
597
|
+
Nullable: (schema) => new QNullable(schema),
|
|
598
|
+
Default: (schema, defaultValue) => new QDefault(schema, defaultValue),
|
|
599
|
+
Transform: (schema, decode, encode) => new QTransform(schema, decode, encode),
|
|
600
|
+
at: (index, schema) => new QPositioned(index, schema),
|
|
601
|
+
Union: (variants, opts) => new QUnion(variants, opts)
|
|
602
|
+
};
|