@litert/typeguard 1.0.1 → 1.3.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGES.md +16 -0
  2. package/lib/BuiltInTypeCompiler.d.ts +4 -4
  3. package/lib/BuiltInTypeCompiler.d.ts.map +1 -1
  4. package/lib/BuiltInTypeCompiler.js +167 -166
  5. package/lib/BuiltInTypeCompiler.js.map +1 -1
  6. package/lib/BuiltInTypes.d.ts +1 -1
  7. package/lib/BuiltInTypes.js +37 -36
  8. package/lib/BuiltInTypes.js.map +1 -1
  9. package/lib/Common.d.ts +27 -10
  10. package/lib/Common.d.ts.map +1 -1
  11. package/lib/Common.js +1 -1
  12. package/lib/Compiler.d.ts +2 -2
  13. package/lib/Compiler.d.ts.map +1 -1
  14. package/lib/Compiler.js +159 -102
  15. package/lib/Compiler.js.map +1 -1
  16. package/lib/Context.d.ts +6 -5
  17. package/lib/Context.d.ts.map +1 -1
  18. package/lib/Context.js +11 -7
  19. package/lib/Context.js.map +1 -1
  20. package/lib/FilterCompiler.d.ts +5 -5
  21. package/lib/FilterCompiler.d.ts.map +1 -1
  22. package/lib/FilterCompiler.js +25 -24
  23. package/lib/FilterCompiler.js.map +1 -1
  24. package/lib/InlineCompiler.d.ts +12 -5
  25. package/lib/InlineCompiler.d.ts.map +1 -1
  26. package/lib/InlineCompiler.js +18 -6
  27. package/lib/InlineCompiler.js.map +1 -1
  28. package/lib/Internal.d.ts +6 -4
  29. package/lib/Internal.d.ts.map +1 -1
  30. package/lib/Internal.js +12 -10
  31. package/lib/Internal.js.map +1 -1
  32. package/lib/Modifiers.d.ts +1 -1
  33. package/lib/Modifiers.js +2 -1
  34. package/lib/Modifiers.js.map +1 -1
  35. package/lib/index.d.ts +5 -5
  36. package/lib/index.js +19 -7
  37. package/lib/index.js.map +1 -1
  38. package/lib/langs/JavaScript.d.ts +2 -2
  39. package/lib/langs/JavaScript.d.ts.map +1 -1
  40. package/lib/langs/JavaScript.js +55 -29
  41. package/lib/langs/JavaScript.js.map +1 -1
  42. package/package.json +17 -13
  43. package/src/examples/quick-start.ts +172 -0
  44. package/src/lib/BuiltInTypeCompiler.ts +171 -171
  45. package/src/lib/BuiltInTypes.ts +36 -36
  46. package/src/lib/Common.ts +49 -10
  47. package/src/lib/Compiler.ts +354 -247
  48. package/src/lib/Context.ts +11 -13
  49. package/src/lib/FilterCompiler.ts +84 -84
  50. package/src/lib/InlineCompiler.ts +41 -15
  51. package/src/lib/Internal.ts +17 -13
  52. package/src/lib/Modifiers.ts +2 -2
  53. package/src/lib/index.ts +5 -5
  54. package/src/lib/langs/JavaScript.ts +84 -31
  55. package/src/test/00-all.ts +10 -10
  56. package/src/test/01-elemental-types.ts +1111 -1110
  57. package/src/test/02-array-and-list.ts +75 -75
  58. package/src/test/03-tuple.ts +87 -87
  59. package/src/test/04-from-string.ts +849 -848
  60. package/src/test/05-string-asserts.ts +422 -422
  61. package/src/test/06-structure.ts +107 -107
  62. package/src/test/07-modifiers.ts +151 -42
  63. package/src/test/08-map-and-dict.ts +46 -46
  64. package/src/test/09-exceptions.ts +30 -9
  65. package/src/test/abstracts.ts +83 -45
  66. package/dist/typeguard.amd.d.ts +0 -588
  67. package/dist/typeguard.amd.d.ts.map +0 -1
  68. package/dist/typeguard.amd.js +0 -2069
  69. package/dist/typeguard.amd.js.map +0 -1
  70. package/dist/typeguard.system.d.ts +0 -588
  71. package/dist/typeguard.system.d.ts.map +0 -1
  72. package/dist/typeguard.system.js +0 -2185
  73. package/dist/typeguard.system.js.map +0 -1
  74. package/src/samples/quick-start.ts +0 -52
  75. package/tsconfig-amd.json +0 -72
  76. package/tsconfig-systemjs.json +0 -72
package/lib/Compiler.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Copyright 2019 Angus.Fenying <fenying@litert.org>
3
+ * Copyright 2022 Angus Fenying <fenying@litert.org>
4
4
  *
5
5
  * Licensed under the Apache License, Version 2.0 (the "License");
6
6
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.createCompiler = void 0;
18
19
  const I = require("./Internal");
19
20
  const M = require("./Modifiers");
20
21
  const Context_1 = require("./Context");
@@ -34,23 +35,48 @@ class Compiler {
34
35
  }
35
36
  return null;
36
37
  }
38
+ _addTrace(ctx) {
39
+ if (!ctx.vTraceName) {
40
+ return this._lang.literalFalse;
41
+ }
42
+ return this._lang.addTrace(ctx.vTraceName, ctx.vTracePrefix, ctx.tracePath);
43
+ }
44
+ _addTraceOr(ctx, expr, subPath = '') {
45
+ if (!ctx.vTraceName) {
46
+ return expr;
47
+ }
48
+ return this._lang.orAddTrace(expr, ctx.vTraceName, ctx.vTracePrefix, `${ctx.tracePath}${subPath}`);
49
+ }
37
50
  compile(options) {
38
51
  const referredTypes = {};
39
- const ctx = new Context_1.Context(this._lang.varName("entry"), this._lang.varName("types"), referredTypes);
52
+ const ctx = new Context_1.Context(this._lang.varName('entry'), options.traceErrors ? this._lang.varName('failedAsserts') : '', options.traceErrors ? this._lang.varName('tracePrefix') : '', this._lang.varName('types'), referredTypes);
40
53
  if (options.name) {
41
54
  this._compile(ctx, [M.TYPE, options.name, options.rule]);
42
55
  return this._defTypes[options.name];
43
56
  }
44
57
  else {
45
58
  const ret = {
46
- source: "",
59
+ source: '',
47
60
  arguments: [{
48
- "name": ctx.vName,
49
- "type": "unknown"
61
+ 'name': ctx.vName,
62
+ 'type': 'unknown',
63
+ 'initial': ''
50
64
  }],
51
65
  typeSlotName: ctx.typeSlotName,
52
66
  referredTypes: []
53
67
  };
68
+ if (ctx.vTraceName) {
69
+ ret.arguments.push({
70
+ 'name': ctx.vTraceName,
71
+ 'type': 'string[]',
72
+ 'initial': '[]'
73
+ });
74
+ ret.arguments.push({
75
+ 'name': ctx.vTracePrefix,
76
+ 'type': 'string',
77
+ 'initial': this._lang.literal('data')
78
+ });
79
+ }
54
80
  ret.source = this._compile(ctx, options.rule);
55
81
  ret.referredTypes = Object.keys(ctx.referredTypes);
56
82
  return ret;
@@ -58,9 +84,9 @@ class Compiler {
58
84
  }
59
85
  _compile(ctx, rules) {
60
86
  switch (typeof rules) {
61
- case "string":
87
+ case 'string':
62
88
  return this._compileStringRule(ctx, rules);
63
- case "boolean":
89
+ case 'boolean':
64
90
  if (ctx.flags[I.EFlags.FROM_STRING]) {
65
91
  return this._lang.or([
66
92
  this._lang.eq(ctx.vName, this._lang.literal(rules)),
@@ -68,7 +94,7 @@ class Compiler {
68
94
  ]);
69
95
  }
70
96
  return this._lang.eq(ctx.vName, this._lang.literal(rules));
71
- case "number":
97
+ case 'number':
72
98
  if (ctx.flags[I.EFlags.FROM_STRING]) {
73
99
  return this._lang.or([
74
100
  this._lang.eq(ctx.vName, this._lang.literal(rules)),
@@ -76,7 +102,7 @@ class Compiler {
76
102
  ]);
77
103
  }
78
104
  return this._lang.eq(ctx.vName, this._lang.literal(rules));
79
- case "object":
105
+ case 'object':
80
106
  if (Array.isArray(rules)) {
81
107
  return this._compileModifiedRule(ctx, rules);
82
108
  }
@@ -84,16 +110,18 @@ class Compiler {
84
110
  if (ctx.flags[I.EFlags.FROM_STRING]) {
85
111
  return this._lang.or([
86
112
  this._lang.isNull(ctx.vName, true),
87
- this._lang.eq(ctx.vName, this._lang.literal("null"))
113
+ this._lang.eq(ctx.vName, this._lang.literal('null'))
88
114
  ]);
89
115
  }
90
116
  return this._lang.isNull(ctx.vName, true);
91
117
  }
92
118
  return this._compileStructuredRule(ctx, rules);
93
- case "undefined":
119
+ case 'undefined':
94
120
  return this._lang.isUndefined(ctx.vName, true);
121
+ default:
122
+ break;
95
123
  }
96
- throw new TypeError("Unknwn rules.");
124
+ throw new TypeError('Unknwn rules.');
97
125
  }
98
126
  _compileStringRule(ctx, rule) {
99
127
  const strAssert = this._useStringAssert(ctx, rule);
@@ -103,55 +131,67 @@ class Compiler {
103
131
  strAssert
104
132
  ]);
105
133
  }
106
- if (rule[0] === I.IMPLICIT_SYMBOL) {
134
+ if (rule.startsWith(I.IMPLICIT_SYMBOL)) {
107
135
  return this._lang.or([
108
136
  this._builtInTypes.compile(B.VOID, ctx, []),
109
137
  this._compileStringRule(ctx, rule.slice(1))
110
138
  ]);
111
139
  }
112
- if (rule[0] === I.NEGATIVE_SYMBOL) {
140
+ if (rule.startsWith(I.NEGATIVE_SYMBOL)) {
113
141
  return this._lang.not(this._compileStringRule(ctx, rule.slice(1)));
114
142
  }
115
- let regResult;
143
+ let regResult = /\[\s*(\d*|\d+\s*,\s*\d*)\s*\]$/.exec(rule);
116
144
  /**
117
- * For rules like `xxx[123]` or `xxx[1,5]`.
145
+ * For rules like `xxx[123]` or `xxx[1,5]` or `xxx[1,]`.
118
146
  */
119
- if (regResult = rule.match(/\[\s*(\d*|\d+\s*,\s*\d*)\s*\]$/)) {
147
+ if (regResult) {
120
148
  if (regResult[1]) {
121
- let range = regResult[1].split(",").map((x) => parseInt(x.trim()));
149
+ const range = regResult[1].split(',').map((x) => parseInt(x.trim()));
122
150
  if (range.length === 1) {
151
+ /**
152
+ * For rules like `xxx[123]`.
153
+ */
123
154
  return this._compileModifiedRule(ctx, [
124
155
  M.ARRAY,
125
156
  range[0],
126
- rule.substr(0, regResult.index)
157
+ rule.slice(0, regResult.index)
127
158
  ]);
128
159
  }
129
160
  else if (Number.isNaN(range[1])) {
161
+ /**
162
+ * For rules like `xxx[1,]`.
163
+ */
130
164
  return this._compileModifiedRule(ctx, [
131
165
  M.ARRAY,
132
166
  [range[0]],
133
- rule.substr(0, regResult.index)
167
+ rule.slice(0, regResult.index)
134
168
  ]);
135
169
  }
136
170
  else {
171
+ /**
172
+ * For rules like `xxx[1,5]`.
173
+ */
137
174
  return this._compileModifiedRule(ctx, [
138
175
  M.ARRAY,
139
176
  range,
140
- rule.substr(0, regResult.index)
177
+ rule.slice(0, regResult.index)
141
178
  ]);
142
179
  }
143
180
  }
144
181
  else {
182
+ /**
183
+ * For rules like `xxx[]`.
184
+ */
145
185
  return this._compileModifiedRule(ctx, [
146
186
  M.LIST,
147
- rule.substr(0, regResult.index)
187
+ rule.slice(0, regResult.index)
148
188
  ]);
149
189
  }
150
190
  }
151
- /**
152
- * For rules like `xxx{}`.
153
- */
154
191
  if (rule.endsWith(I.MAP_SUFFIX)) {
192
+ /**
193
+ * For rules like `xxx{}`.
194
+ */
155
195
  return this._lang.and([
156
196
  this._compileModifiedRule(ctx, [
157
197
  M.MAP,
@@ -159,9 +199,10 @@ class Compiler {
159
199
  ])
160
200
  ]);
161
201
  }
162
- if (rule[0] === I.PREDEF_TYPE_SYMBOL) {
202
+ if (rule.startsWith(I.PREDEF_TYPE_SYMBOL)) {
163
203
  return this._usePredefinedType(ctx, rule.slice(1));
164
204
  }
205
+ regResult = /^(\w+)(\(\s*(-?\d+(\.\d+)?)?\s*,?\s*(-?\d+(\.\d+)?)?\s*\))?$/.exec(rule);
165
206
  /**
166
207
  * For built-in-type rules like:
167
208
  *
@@ -170,14 +211,14 @@ class Compiler {
170
211
  * - `string`
171
212
  * - `int(12, 34)`
172
213
  */
173
- if (regResult = rule.match(/^(\w+)(\(\s*(-?\d+(\.\d+)?)?\s*,?\s*(-?\d+(\.\d+)?)?\s*\))?$/)) {
214
+ if (regResult) {
174
215
  if (regResult[2]) {
175
- const args = regResult[2].slice(1, -1).trim().split(",").map((x) => parseFloat(x.trim()));
216
+ const args = regResult[2].slice(1, -1).trim().split(',').map((x) => parseFloat(x.trim()));
176
217
  return this._builtInTypes.compile(regResult[1], ctx, args);
177
218
  }
178
219
  return this._builtInTypes.compile(regResult[1], ctx, []);
179
220
  }
180
- if (rule[0] === I.FILTER_PREFIX) {
221
+ if (rule.startsWith(I.FILTER_PREFIX)) {
181
222
  return this._filters.compile(rule, ctx);
182
223
  }
183
224
  throw new TypeError(`Unknown type "${rule}".`);
@@ -188,64 +229,65 @@ class Compiler {
188
229
  return this._lang.call(this._lang.fieldIndex(ctx.typeSlotName, this._lang.literal(typeName)), ctx.vName);
189
230
  }
190
231
  _useStringAssert(ctx, rule) {
191
- const assertRule = rule.match(/^:([-\w]+):/);
232
+ var _a;
233
+ const assertRule = /^:([-\w]+):/.exec(rule);
192
234
  const offset = assertRule ? assertRule[1].length + 2 : 2;
193
- switch ((assertRule && assertRule[1]) || rule.substr(0, 2)) {
194
- case "==":
195
- case "equal":
235
+ switch ((_a = (assertRule === null || assertRule === void 0 ? void 0 : assertRule[1])) !== null && _a !== void 0 ? _a : rule.slice(0, 2)) {
236
+ case '==':
237
+ case 'equal':
196
238
  return this._lang.eq(ctx.vName, this._lang.literal(rule.slice(offset)));
197
- case "%=":
198
- case "equal-i":
239
+ case '%=':
240
+ case 'equal-i':
199
241
  return this._lang.eq(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase()));
200
- case "!=":
201
- case "not-equal":
242
+ case '!=':
243
+ case 'not-equal':
202
244
  return this._lang.ne(ctx.vName, this._lang.literal(rule.slice(offset)));
203
- case "%!":
204
- case "not-equal-i":
245
+ case '%!':
246
+ case 'not-equal-i':
205
247
  return this._lang.ne(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase()));
206
- case "~=":
207
- case "match":
248
+ case '~=':
249
+ case 'match':
208
250
  return this._lang.matchRegExp(ctx.vName, rule.slice(offset));
209
- case "~!":
210
- case "not-match":
251
+ case '~!':
252
+ case 'not-match':
211
253
  return this._lang.not(this._lang.matchRegExp(ctx.vName, rule.slice(offset)));
212
- case "?=":
213
- case "include":
254
+ case '?=':
255
+ case 'include':
214
256
  return this._lang.instr(ctx.vName, this._lang.literal(rule.slice(offset)));
215
- case "?!":
216
- case "not-include":
257
+ case '?!':
258
+ case 'not-include':
217
259
  return this._lang.not(this._lang.instr(ctx.vName, this._lang.literal(rule.slice(offset))));
218
- case "*=":
219
- case "include-i":
260
+ case '*=':
261
+ case 'include-i':
220
262
  return this._lang.instr(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase()));
221
- case "*!":
222
- case "not-include-i":
263
+ case '*!':
264
+ case 'not-include-i':
223
265
  return this._lang.not(this._lang.instr(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase())));
224
- case "^=":
225
- case "start-with":
266
+ case '^=':
267
+ case 'start-with':
226
268
  return this._lang.startsWith(ctx.vName, this._lang.literal(rule.slice(offset)));
227
- case "start-with-i":
269
+ case 'start-with-i':
228
270
  return this._lang.startsWith(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase()));
229
- case "^!":
230
- case "not-start-with":
271
+ case '^!':
272
+ case 'not-start-with':
231
273
  return this._lang.not(this._lang.startsWith(ctx.vName, this._lang.literal(rule.slice(offset))));
232
- case "not-start-with-i":
274
+ case 'not-start-with-i':
233
275
  return this._lang.not(this._lang.startsWith(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase())));
234
- case "$=":
235
- case "end-with":
276
+ case '$=':
277
+ case 'end-with':
236
278
  return this._lang.endsWith(ctx.vName, this._lang.literal(rule.slice(offset)));
237
- case "end-with-i":
279
+ case 'end-with-i':
238
280
  return this._lang.endsWith(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase()));
239
- case "$!":
240
- case "not-end-with":
281
+ case '$!':
282
+ case 'not-end-with':
241
283
  return this._lang.not(this._lang.endsWith(ctx.vName, this._lang.literal(rule.slice(offset))));
242
- case "not-end-with-i":
284
+ case 'not-end-with-i':
243
285
  return this._lang.not(this._lang.endsWith(this._lang.lowerCase(ctx.vName), this._lang.literal(rule.slice(offset).toLowerCase())));
244
286
  default:
245
- if (rule.startsWith("=")) {
287
+ if (rule.startsWith('=')) {
246
288
  return this._lang.eq(ctx.vName, this._lang.literal(rule.slice(1)));
247
289
  }
248
- if (rule.startsWith("~")) {
290
+ if (rule.startsWith('~')) {
249
291
  return this._lang.matchRegExp(ctx.vName, rule.slice(1));
250
292
  }
251
293
  }
@@ -253,12 +295,12 @@ class Compiler {
253
295
  }
254
296
  _compileModifiedRule(ctx, rules) {
255
297
  if (!rules.length) {
256
- throw new TypeError(`Unknwon type "[]".`);
298
+ throw new TypeError('Unknwon type "[]".');
257
299
  }
258
300
  /**
259
301
  * By default, use OR modifier.
260
302
  */
261
- if (typeof rules[0] !== "string" ||
303
+ if (typeof rules[0] !== 'string' ||
262
304
  !rules[0].startsWith(I.MODIFIER_PREFIX)) {
263
305
  if (rules.length === 1) {
264
306
  return this._compile(ctx, rules[0]);
@@ -313,7 +355,7 @@ class Compiler {
313
355
  return result;
314
356
  }
315
357
  _compileModifierOR(ctx, rules) {
316
- let result = [];
358
+ const result = [];
317
359
  for (const r of rules) {
318
360
  ctx.trap();
319
361
  result.push(this._compile(ctx, r));
@@ -324,7 +366,7 @@ class Compiler {
324
366
  _compileModifierAND(ctx, rules) {
325
367
  return this._lang.and(rules.map((rule) => this._compile(ctx, rule)));
326
368
  }
327
- _compileModifierLIST(ctx, rules) {
369
+ _compileModifierLIST(ctx, rules, traceOffset = 0) {
328
370
  const result = [];
329
371
  if (!ctx.flags[I.EFlags.ARRAY]) {
330
372
  result.push(this._lang.isArray(ctx.vName, true));
@@ -334,16 +376,18 @@ class Compiler {
334
376
  const CLOSURE_ARG = ctx.vName;
335
377
  const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
336
378
  ctx.vName = this._lang.varName(ctx.vCursor++);
379
+ const vIter = this._lang.varName(ctx.vCursor++);
380
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.numberTemplateVar(traceOffset ? this._lang.add(traceOffset, vIter) : vIter)}]`;
337
381
  if (rules[0] !== B.ANY) {
338
382
  result.push(this._lang.closure([CLOSURE_PARAM], [CLOSURE_ARG], this._lang.series([
339
- this._lang.forEach(CLOSURE_PARAM, ctx.vName, this._lang.ifThen(this._lang.not(this._compile(ctx, rules)), this._lang.returnValue(this._lang.literal(false)))),
383
+ this._lang.forEach(CLOSURE_PARAM, vIter, ctx.vName, this._lang.ifThen(this._lang.not(this._compile(ctx, rules)), this._lang.returnValue(this._addTrace(ctx)))),
340
384
  this._lang.returnValue(this._lang.literal(true))
341
385
  ])));
342
386
  }
343
387
  ctx.untrap();
344
388
  return this._lang.and(result);
345
389
  }
346
- _compileModifierARRAY(ctx, rules) {
390
+ _compileModifierARRAY(ctx, rules, traceOffset = 0) {
347
391
  let a = 0;
348
392
  let b = -1;
349
393
  if (Number.isInteger(rules[0]) && rules[0] >= 0) {
@@ -389,6 +433,8 @@ class Compiler {
389
433
  const CLOSURE_ARG = ctx.vName;
390
434
  const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
391
435
  ctx.vName = this._lang.varName(ctx.vCursor++);
436
+ const vIter = this._lang.varName(ctx.vCursor++);
437
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.numberTemplateVar(traceOffset ? this._lang.add(traceOffset, vIter) : vIter)}]`;
392
438
  switch (b) {
393
439
  case -1: {
394
440
  result.push(this._lang.eq(this._lang.arrayLength(CLOSURE_ARG), a));
@@ -405,7 +451,7 @@ class Compiler {
405
451
  }
406
452
  if (rules[1] !== B.ANY) {
407
453
  result.push(this._lang.closure([CLOSURE_PARAM], [CLOSURE_ARG], this._lang.series([
408
- this._lang.forEach(CLOSURE_PARAM, ctx.vName, this._lang.ifThen(this._lang.not(this._compile(ctx, rules.slice(1))), this._lang.returnValue(this._lang.literal(false)))),
454
+ this._lang.forEach(CLOSURE_PARAM, vIter, ctx.vName, this._lang.ifThen(this._lang.not(this._compile(ctx, rules.slice(1))), this._lang.returnValue(this._addTrace(ctx)))),
409
455
  this._lang.returnValue(this._lang.literal(true))
410
456
  ])));
411
457
  }
@@ -423,18 +469,19 @@ class Compiler {
423
469
  let i = 0;
424
470
  const types = rules.slice();
425
471
  let tupleLength = 0;
472
+ let tupleLengthMin = 0;
426
473
  while (1) {
427
474
  type = types.shift();
428
475
  if (type === undefined) {
429
476
  break;
430
477
  }
431
- if (typeof type === "string" && type.startsWith("...")) {
478
+ if (typeof type === 'string' && type.startsWith('...')) {
432
479
  throw new TypeError(`Invalid syntax for tuple: ${JSON.stringify(rules)}`);
433
480
  }
434
- if (typeof types[0] === "string" && types[0].startsWith("...")) {
481
+ if (typeof types[0] === 'string' && types[0].startsWith('...')) {
435
482
  ctx.trap();
436
483
  dots = types.shift();
437
- if (dots === "...") {
484
+ if (dots === '...') {
438
485
  /**
439
486
  * No more elements because "..." means all rest elements.
440
487
  */
@@ -442,9 +489,10 @@ class Compiler {
442
489
  throw new TypeError(`Invalid syntax for tuple: ${JSON.stringify(rules)}`);
443
490
  }
444
491
  ctx.vName = this._lang.arraySlice(ctx.vName, i);
445
- if (type !== "any") {
446
- result.push(this._compileModifierLIST(ctx, type));
492
+ if (type !== 'any') {
493
+ result.push(this._compileModifierLIST(ctx, type, i));
447
494
  }
495
+ tupleLengthMin = tupleLength;
448
496
  tupleLength = -1;
449
497
  }
450
498
  else if (!/^\d+$/.test(dots.slice(3))) {
@@ -452,16 +500,18 @@ class Compiler {
452
500
  }
453
501
  else {
454
502
  const length = parseInt(dots.slice(3));
455
- if (length <= 3) {
503
+ if (length === 0) {
504
+ throw new TypeError(`Invalid syntax for tuple: ${dots}`);
505
+ }
506
+ else if (length === 1) {
456
507
  const vName = ctx.vName;
457
- for (let j = 0; j < length; j++) {
458
- ctx.vName = this._lang.arrayIndex(vName, i++);
459
- result.push(this._compile(ctx, type));
460
- }
508
+ ctx.tracePath = `${ctx.tracePath}[${i}]`;
509
+ ctx.vName = this._lang.arrayIndex(vName, i++);
510
+ result.push(this._addTraceOr(ctx, this._compile(ctx, type)));
461
511
  }
462
512
  else {
463
513
  ctx.vName = this._lang.arraySlice(ctx.vName, i, i + length);
464
- result.push(this._compileModifierARRAY(ctx, [length, type]));
514
+ result.push(this._compileModifierARRAY(ctx, [length, type], i));
465
515
  i += length;
466
516
  }
467
517
  tupleLength += length;
@@ -469,19 +519,23 @@ class Compiler {
469
519
  }
470
520
  else {
471
521
  ctx.trap(true);
522
+ ctx.tracePath = `${ctx.tracePath}[${i}]`;
472
523
  ctx.vName = this._lang.arrayIndex(ctx.vName, i++);
473
- result.push(this._compile(ctx, type));
524
+ result.push(this._addTraceOr(ctx, this._compile(ctx, type)));
474
525
  tupleLength++;
475
526
  }
476
527
  ctx.untrap();
477
528
  }
478
529
  if (tupleLength >= 0) {
479
- result.splice(1, -1, this._lang.eq(this._lang.arrayLength(ctx.vName), tupleLength));
530
+ result.splice(1, -1, this._addTraceOr(ctx, this._lang.eq(this._lang.arrayLength(ctx.vName), tupleLength), '.length'));
531
+ }
532
+ else if (tupleLengthMin >= 0) {
533
+ result.splice(1, -1, this._addTraceOr(ctx, this._lang.gte(this._lang.arrayLength(ctx.vName), tupleLengthMin), '.length'));
480
534
  }
481
535
  return this._lang.and(result);
482
536
  }
483
537
  _validateTypeName(name) {
484
- if (typeof name !== "string" || !/^\w+$/.test(name)) {
538
+ if (typeof name !== 'string' || !I.RE_VALID_CUSTOM_TYPE_NAME.test(name)) {
485
539
  throw new TypeError(`Invalid name ${JSON.stringify(name)} for a pre-defined type.`);
486
540
  }
487
541
  }
@@ -491,7 +545,8 @@ class Compiler {
491
545
  throw new Error(`Dplicated name ${JSON.stringify(rules[0])} for a pre-defined type.`);
492
546
  }
493
547
  this._defTypes[rules[0]] = this.compile({
494
- rule: rules.slice(1)
548
+ rule: rules.slice(1),
549
+ traceErrors: !!ctx.vTraceName
495
550
  });
496
551
  return this._usePredefinedType(ctx, rules[0]);
497
552
  }
@@ -520,14 +575,15 @@ class Compiler {
520
575
  rules = rules[0];
521
576
  }
522
577
  ctx.trap(true);
523
- const CLOSURE_ARG = ctx.vName;
524
- const CLOSURE_PARAM = this._lang.varName(ctx.vCursor++);
525
- const FOR_IN_KEY = this._lang.varName(ctx.vCursor++);
578
+ const vCArg = ctx.vName;
579
+ const vCParam = this._lang.varName(ctx.vCursor++);
580
+ const vKey = this._lang.varName(ctx.vCursor++);
526
581
  ctx.vName = this._lang.varName(ctx.vCursor++);
582
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.stringTemplateVar(vKey)}]`;
527
583
  const result = this._lang.and([
528
- this._lang.isStrucutre(CLOSURE_ARG, true),
529
- this._lang.closure([CLOSURE_PARAM], [CLOSURE_ARG], this._lang.series([
530
- this._lang.forIn(CLOSURE_PARAM, FOR_IN_KEY, ctx.vName, this._lang.ifThen(this._lang.not(this._compile(ctx, rules)), this._lang.returnValue(this._lang.literal(false)))),
584
+ this._lang.isStrucutre(vCArg, true),
585
+ this._lang.closure([vCParam], [vCArg], this._lang.series([
586
+ this._lang.forIn(vCParam, vKey, ctx.vName, this._lang.ifThen(this._lang.not(this._compile(ctx, rules)), this._lang.returnValue(this._addTrace(ctx)))),
531
587
  this._lang.returnValue(this._lang.literal(true))
532
588
  ]))
533
589
  ]);
@@ -538,15 +594,15 @@ class Compiler {
538
594
  if (rules.length < 2 || !Array.isArray(rules[0])) {
539
595
  throw new SyntaxError(`Invalid dict ${JSON.stringify(rules)}.`);
540
596
  }
541
- let tmp = {};
597
+ const tmp = {};
542
598
  const id = `${Date.now()}${Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}`;
543
599
  this._compileModifierTYPE(ctx, [
544
600
  this._lang.varName(id),
545
601
  rules.slice(1)
546
602
  ]);
547
603
  const type = `${I.PREDEF_TYPE_SYMBOL}${this._lang.varName(id)}`;
548
- for (let key of rules[0]) {
549
- if (typeof key !== "string") {
604
+ for (const key of rules[0]) {
605
+ if (typeof key !== 'string') {
550
606
  throw new SyntaxError(`Invalid key ${JSON.stringify(key)} for dict.`);
551
607
  }
552
608
  tmp[key] = type;
@@ -556,7 +612,7 @@ class Compiler {
556
612
  _compileSimpleStructure(ctx, rules) {
557
613
  const strict = !!ctx.flags[I.EFlags.STRICT];
558
614
  const result = [
559
- this._lang.isStrucutre(ctx.vName, true)
615
+ this._addTraceOr(ctx, this._lang.isStrucutre(ctx.vName, true), '!object')
560
616
  ];
561
617
  const keys = [];
562
618
  for (let k in rules) {
@@ -584,7 +640,7 @@ class Compiler {
584
640
  rule = [M.EQUAL, rule];
585
641
  }
586
642
  else {
587
- const matchResult = k.match(I.KEY_ARRAY_SUFFIX);
643
+ const matchResult = I.KEY_ARRAY_SUFFIX.exec(k);
588
644
  if (matchResult) {
589
645
  k = k.slice(0, matchResult.index);
590
646
  if (matchResult[3]) {
@@ -615,7 +671,8 @@ class Compiler {
615
671
  }
616
672
  keys.push(k);
617
673
  ctx.vName = this._lang.fieldIndex(ctx.vName, this._lang.literal(k));
618
- result.push(this._compile(ctx, rule));
674
+ ctx.tracePath = `${ctx.tracePath}[${this._lang.literal(k)}]`;
675
+ result.push(this._addTraceOr(ctx, this._compile(ctx, rule)));
619
676
  ctx.untrap();
620
677
  }
621
678
  if (strict && keys.length) {
@@ -626,7 +683,7 @@ class Compiler {
626
683
  _compileStructuredRule(ctx, rules) {
627
684
  const mapSymbol = Object.keys(rules).filter((x) => x.startsWith(M.MAP));
628
685
  if (mapSymbol.length > 1) {
629
- throw new SyntaxError("Only one '$.map' is allowed as rest-mapping.");
686
+ throw new SyntaxError('Only one \'$.map\' is allowed as rest-mapping.');
630
687
  }
631
688
  else if (mapSymbol.length === 0) {
632
689
  return this._compileSimpleStructure(ctx, rules);
@@ -666,7 +723,7 @@ class Compiler {
666
723
  rule = [M.EQUAL, rule];
667
724
  }
668
725
  else {
669
- const matchResult = k.match(I.KEY_ARRAY_SUFFIX);
726
+ const matchResult = I.KEY_ARRAY_SUFFIX.exec(k);
670
727
  if (matchResult) {
671
728
  k = k.slice(0, matchResult.index);
672
729
  if (matchResult[3]) {
@@ -718,7 +775,7 @@ class Compiler {
718
775
  }
719
776
  _isOrRule(rule) {
720
777
  return Array.isArray(rule) && (rule[0] === M.OR ||
721
- typeof rule[0] !== "string" ||
778
+ typeof rule[0] !== 'string' ||
722
779
  !rule[0].startsWith(I.MODIFIER_PREFIX));
723
780
  }
724
781
  }