effect 3.14.0 → 3.14.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.
@@ -9,16 +9,19 @@ import * as schemaId_ from "./internal/schema/schemaId.js";
9
9
  import * as util_ from "./internal/schema/util.js";
10
10
  import * as Option from "./Option.js";
11
11
  import * as Predicate from "./Predicate.js";
12
- import * as AST from "./SchemaAST.js";
12
+ import * as SchemaAST from "./SchemaAST.js";
13
13
  /**
14
14
  * Returns a LazyArbitrary for the `A` type of the provided schema.
15
15
  *
16
16
  * @category arbitrary
17
17
  * @since 3.10.0
18
18
  */
19
- export const makeLazy = schema => go(schema.ast, {
20
- maxDepth: 2
21
- }, []);
19
+ export const makeLazy = schema => {
20
+ const description = getDescription(schema.ast, []);
21
+ return go(description, {
22
+ maxDepth: 2
23
+ });
24
+ };
22
25
  /**
23
26
  * Returns a fast-check Arbitrary for the `A` type of the provided schema.
24
27
  *
@@ -26,60 +29,6 @@ export const makeLazy = schema => go(schema.ast, {
26
29
  * @since 3.10.0
27
30
  */
28
31
  export const make = schema => makeLazy(schema)(FastCheck);
29
- const getArbitraryAnnotation = /*#__PURE__*/AST.getAnnotation(AST.ArbitraryAnnotationId);
30
- /**
31
- * Represents an arbitrary with optional filters.
32
- */
33
- class Succeed {
34
- lazyArbitrary;
35
- filters;
36
- _tag = "Succeed";
37
- constructor(lazyArbitrary, filters = []) {
38
- this.lazyArbitrary = lazyArbitrary;
39
- this.filters = filters;
40
- }
41
- toLazyArbitrary() {
42
- return fc => {
43
- let out = this.lazyArbitrary(fc);
44
- for (const f of this.filters) {
45
- out = out.filter(f);
46
- }
47
- return out;
48
- };
49
- }
50
- }
51
- /**
52
- * Represents a deferred arbitrary value generator with optional filters.
53
- */
54
- class Deferred {
55
- config;
56
- filters;
57
- _tag = "Deferred";
58
- constructor(config, filters = []) {
59
- this.config = config;
60
- this.filters = filters;
61
- }
62
- toLazyArbitrary(ctx, path) {
63
- const config = this.config;
64
- switch (config._tag) {
65
- case "StringConstraints":
66
- {
67
- const pattern = config.pattern;
68
- return pattern !== undefined ? fc => fc.stringMatching(new RegExp(pattern)) : fc => fc.string(config.constraints);
69
- }
70
- case "NumberConstraints":
71
- {
72
- return config.isInteger ? fc => fc.integer(config.constraints) : fc => fc.float(config.constraints);
73
- }
74
- case "BigIntConstraints":
75
- return fc => fc.bigInt(config.constraints);
76
- case "DateConstraints":
77
- return fc => fc.date(config.constraints);
78
- case "ArrayConstraints":
79
- return goTupleType(config.ast, ctx, path, config.constraints);
80
- }
81
- }
82
- }
83
32
  /** @internal */
84
33
  export const makeStringConstraints = options => {
85
34
  const out = {
@@ -156,9 +105,7 @@ export const makeArrayConstraints = options => {
156
105
  export const makeDateConstraints = options => {
157
106
  const out = {
158
107
  _tag: "DateConstraints",
159
- constraints: {
160
- noInvalidDate: options.noInvalidDate ?? false
161
- }
108
+ constraints: {}
162
109
  };
163
110
  if (Predicate.isDate(options.min)) {
164
111
  out.constraints.min = options.min;
@@ -166,54 +113,14 @@ export const makeDateConstraints = options => {
166
113
  if (Predicate.isDate(options.max)) {
167
114
  out.constraints.max = options.max;
168
115
  }
169
- return out;
170
- };
171
- const makeArrayConfig = (options, ast) => {
172
- return {
173
- ast,
174
- ...makeArrayConstraints(options)
175
- };
176
- };
177
- const arbitraryMemoMap = /*#__PURE__*/globalValue( /*#__PURE__*/Symbol.for("effect/Arbitrary/arbitraryMemoMap"), () => new WeakMap());
178
- const go = (ast, ctx, path) => {
179
- const hook = getArbitraryAnnotation(ast);
180
- if (Option.isSome(hook)) {
181
- switch (ast._tag) {
182
- case "Declaration":
183
- return hook.value(...ast.typeParameters.map(p => go(p, ctx, path)), ctx);
184
- case "Refinement":
185
- {
186
- const op = toOp(ast, ctx, path);
187
- ctx = op._tag === "Deferred" ? {
188
- ...ctx,
189
- constraints: op.config
190
- } : ctx;
191
- const from = go(ast.from, ctx, path);
192
- return new Succeed(hook.value(from, ctx), op.filters).toLazyArbitrary();
193
- }
194
- default:
195
- return hook.value(ctx);
196
- }
197
- }
198
- if (AST.isDeclaration(ast)) {
199
- throw new Error(errors_.getArbitraryMissingAnnotationErrorMessage(path, ast));
200
- }
201
- const op = toOp(ast, ctx, path);
202
- switch (op._tag) {
203
- case "Succeed":
204
- return op.toLazyArbitrary();
205
- case "Deferred":
206
- return new Succeed(op.toLazyArbitrary(ctx, path), op.filters).toLazyArbitrary();
116
+ if (Predicate.isBoolean(options.noInvalidDate)) {
117
+ out.constraints.noInvalidDate = options.noInvalidDate;
207
118
  }
119
+ return out;
208
120
  };
209
- const constStringConstraints = /*#__PURE__*/makeStringConstraints({});
210
- const constNumberConstraints = /*#__PURE__*/makeNumberConstraints({});
211
- const constBigIntConstraints = /*#__PURE__*/makeBigIntConstraints({});
212
- const defaultSuspendedArrayConstraints = {
213
- maxLength: 2
214
- };
121
+ const getArbitraryAnnotation = /*#__PURE__*/SchemaAST.getAnnotation(SchemaAST.ArbitraryAnnotationId);
215
122
  const getASTConstraints = ast => {
216
- const TypeAnnotationId = ast.annotations[AST.SchemaIdAnnotationId];
123
+ const TypeAnnotationId = ast.annotations[SchemaAST.SchemaIdAnnotationId];
217
124
  if (Predicate.isPropertyKey(TypeAnnotationId)) {
218
125
  const out = ast.annotations[TypeAnnotationId];
219
126
  if (Predicate.isReadonlyRecord(out)) {
@@ -221,122 +128,573 @@ const getASTConstraints = ast => {
221
128
  }
222
129
  }
223
130
  };
131
+ const idMemoMap = /*#__PURE__*/globalValue( /*#__PURE__*/Symbol.for("effect/Arbitrary/IdMemoMap"), () => new Map());
132
+ let counter = 0;
133
+ function wrapGetDescription(f, g) {
134
+ return (ast, path) => f(ast, g(ast, path));
135
+ }
136
+ function parseMeta(ast) {
137
+ const jsonSchema = SchemaAST.getJSONSchemaAnnotation(ast).pipe(Option.filter(Predicate.isReadonlyRecord), Option.getOrUndefined);
138
+ const schemaId = Option.getOrElse(SchemaAST.getSchemaIdAnnotation(ast), () => undefined);
139
+ const schemaParams = Option.fromNullable(schemaId).pipe(Option.map(id => ast.annotations[id]), Option.filter(Predicate.isReadonlyRecord), Option.getOrUndefined);
140
+ return [schemaId, {
141
+ ...schemaParams,
142
+ ...jsonSchema
143
+ }];
144
+ }
224
145
  /** @internal */
225
- export const toOp = (ast, ctx, path) => {
146
+ export const getDescription = /*#__PURE__*/wrapGetDescription((ast, description) => {
147
+ const annotation = getArbitraryAnnotation(ast);
148
+ if (Option.isSome(annotation)) {
149
+ return {
150
+ ...description,
151
+ annotations: [...description.annotations, annotation.value]
152
+ };
153
+ }
154
+ return description;
155
+ }, (ast, path) => {
156
+ const [schemaId, meta] = parseMeta(ast);
226
157
  switch (ast._tag) {
158
+ case "Refinement":
159
+ {
160
+ const from = getDescription(ast.from, path);
161
+ switch (from._tag) {
162
+ case "StringKeyword":
163
+ return {
164
+ ...from,
165
+ constraints: [...from.constraints, makeStringConstraints(meta)],
166
+ refinements: [...from.refinements, ast]
167
+ };
168
+ case "NumberKeyword":
169
+ {
170
+ const c = schemaId === schemaId_.NonNaNSchemaId ? makeNumberConstraints({
171
+ noNaN: true
172
+ }) : makeNumberConstraints({
173
+ isInteger: "type" in meta && meta.type === "integer",
174
+ noNaN: "type" in meta && meta.type === "number" ? true : undefined,
175
+ noDefaultInfinity: "type" in meta && meta.type === "number" ? true : undefined,
176
+ min: meta.exclusiveMinimum ?? meta.minimum,
177
+ minExcluded: "exclusiveMinimum" in meta ? true : undefined,
178
+ max: meta.exclusiveMaximum ?? meta.maximum,
179
+ maxExcluded: "exclusiveMaximum" in meta ? true : undefined
180
+ });
181
+ return {
182
+ ...from,
183
+ constraints: [...from.constraints, c],
184
+ refinements: [...from.refinements, ast]
185
+ };
186
+ }
187
+ case "BigIntKeyword":
188
+ {
189
+ const c = getASTConstraints(ast);
190
+ return {
191
+ ...from,
192
+ constraints: c !== undefined ? [...from.constraints, makeBigIntConstraints(c)] : from.constraints,
193
+ refinements: [...from.refinements, ast]
194
+ };
195
+ }
196
+ case "TupleType":
197
+ return {
198
+ ...from,
199
+ constraints: [...from.constraints, makeArrayConstraints({
200
+ minLength: meta.minItems,
201
+ maxLength: meta.maxItems
202
+ })],
203
+ refinements: [...from.refinements, ast]
204
+ };
205
+ case "DateFromSelf":
206
+ return {
207
+ ...from,
208
+ constraints: [...from.constraints, makeDateConstraints(meta)],
209
+ refinements: [...from.refinements, ast]
210
+ };
211
+ default:
212
+ return {
213
+ ...from,
214
+ refinements: [...from.refinements, ast]
215
+ };
216
+ }
217
+ }
227
218
  case "Declaration":
228
219
  {
229
- const TypeAnnotationId = ast.annotations[AST.SchemaIdAnnotationId];
230
- if (TypeAnnotationId === schemaId_.DateFromSelfSchemaId) {
231
- const c = getASTConstraints(ast);
232
- if (c !== undefined) {
233
- return new Deferred(makeDateConstraints(c));
234
- }
220
+ if (schemaId === schemaId_.DateFromSelfSchemaId) {
221
+ return {
222
+ _tag: "DateFromSelf",
223
+ constraints: [makeDateConstraints(meta)],
224
+ path,
225
+ refinements: [],
226
+ annotations: []
227
+ };
235
228
  }
236
- return new Succeed(go(ast, ctx, path));
229
+ return {
230
+ _tag: "Declaration",
231
+ typeParameters: ast.typeParameters.map(ast => getDescription(ast, path)),
232
+ path,
233
+ refinements: [],
234
+ annotations: [],
235
+ ast
236
+ };
237
237
  }
238
238
  case "Literal":
239
- return new Succeed(fc => fc.constant(ast.literal));
239
+ {
240
+ return {
241
+ _tag: "Literal",
242
+ literal: ast.literal,
243
+ path,
244
+ refinements: [],
245
+ annotations: []
246
+ };
247
+ }
240
248
  case "UniqueSymbol":
241
- return new Succeed(fc => fc.constant(ast.symbol));
242
- case "UndefinedKeyword":
243
- return new Succeed(fc => fc.constant(undefined));
249
+ {
250
+ return {
251
+ _tag: "UniqueSymbol",
252
+ symbol: ast.symbol,
253
+ path,
254
+ refinements: [],
255
+ annotations: []
256
+ };
257
+ }
258
+ case "Enums":
259
+ {
260
+ return {
261
+ _tag: "Enums",
262
+ enums: ast.enums,
263
+ path,
264
+ refinements: [],
265
+ annotations: [],
266
+ ast
267
+ };
268
+ }
269
+ case "TemplateLiteral":
270
+ {
271
+ return {
272
+ _tag: "TemplateLiteral",
273
+ head: ast.head,
274
+ spans: ast.spans.map(span => ({
275
+ description: getDescription(span.type, path),
276
+ literal: span.literal
277
+ })),
278
+ path,
279
+ refinements: [],
280
+ annotations: []
281
+ };
282
+ }
283
+ case "StringKeyword":
284
+ return {
285
+ _tag: "StringKeyword",
286
+ constraints: [],
287
+ path,
288
+ refinements: [],
289
+ annotations: []
290
+ };
291
+ case "NumberKeyword":
292
+ return {
293
+ _tag: "NumberKeyword",
294
+ constraints: [],
295
+ path,
296
+ refinements: [],
297
+ annotations: []
298
+ };
299
+ case "BigIntKeyword":
300
+ return {
301
+ _tag: "BigIntKeyword",
302
+ constraints: [],
303
+ path,
304
+ refinements: [],
305
+ annotations: []
306
+ };
307
+ case "TupleType":
308
+ return {
309
+ _tag: "TupleType",
310
+ constraints: [],
311
+ elements: ast.elements.map((element, i) => ({
312
+ isOptional: element.isOptional,
313
+ description: getDescription(element.type, [...path, i])
314
+ })),
315
+ rest: ast.rest.map((element, i) => getDescription(element.type, [...path, i])),
316
+ path,
317
+ refinements: [],
318
+ annotations: []
319
+ };
320
+ case "TypeLiteral":
321
+ return {
322
+ _tag: "TypeLiteral",
323
+ propertySignatures: ast.propertySignatures.map(ps => ({
324
+ isOptional: ps.isOptional,
325
+ name: ps.name,
326
+ value: getDescription(ps.type, [...path, ps.name])
327
+ })),
328
+ indexSignatures: ast.indexSignatures.map(is => ({
329
+ parameter: getDescription(is.parameter, path),
330
+ value: getDescription(is.type, path)
331
+ })),
332
+ path,
333
+ refinements: [],
334
+ annotations: []
335
+ };
336
+ case "Union":
337
+ return {
338
+ _tag: "Union",
339
+ members: ast.types.map((member, i) => getDescription(member, [...path, i])),
340
+ path,
341
+ refinements: [],
342
+ annotations: []
343
+ };
344
+ case "Suspend":
345
+ {
346
+ const memoId = idMemoMap.get(ast);
347
+ if (memoId !== undefined) {
348
+ return {
349
+ _tag: "Ref",
350
+ id: memoId,
351
+ ast,
352
+ path,
353
+ refinements: [],
354
+ annotations: []
355
+ };
356
+ }
357
+ counter++;
358
+ const id = `__id-${counter}__`;
359
+ idMemoMap.set(ast, id);
360
+ return {
361
+ _tag: "Suspend",
362
+ id,
363
+ ast,
364
+ description: () => getDescription(ast.f(), path),
365
+ path,
366
+ refinements: [],
367
+ annotations: []
368
+ };
369
+ }
370
+ case "Transformation":
371
+ return getDescription(ast.to, path);
244
372
  case "NeverKeyword":
245
- throw new Error(errors_.getArbitraryMissingAnnotationErrorMessage(path, ast));
246
- case "VoidKeyword":
247
- case "UnknownKeyword":
248
- case "AnyKeyword":
249
- return new Succeed(fc => fc.anything());
373
+ return {
374
+ _tag: "NeverKeyword",
375
+ path,
376
+ refinements: [],
377
+ annotations: [],
378
+ ast
379
+ };
380
+ default:
381
+ {
382
+ return {
383
+ _tag: "Keyword",
384
+ value: ast._tag,
385
+ path,
386
+ refinements: [],
387
+ annotations: []
388
+ };
389
+ }
390
+ }
391
+ });
392
+ function getMax(n1, n2) {
393
+ return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n2 : n1;
394
+ }
395
+ function getMin(n1, n2) {
396
+ return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n1 : n2;
397
+ }
398
+ const getOr = (a, b) => {
399
+ return a === undefined ? b : b === undefined ? a : a || b;
400
+ };
401
+ function mergePattern(pattern1, pattern2) {
402
+ if (pattern1 === undefined) {
403
+ return pattern2;
404
+ }
405
+ if (pattern2 === undefined) {
406
+ return pattern1;
407
+ }
408
+ return `(?:${pattern1})|(?:${pattern2})`;
409
+ }
410
+ function mergeStringConstraints(c1, c2) {
411
+ return makeStringConstraints({
412
+ minLength: getMax(c1.constraints.minLength, c2.constraints.minLength),
413
+ maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength),
414
+ pattern: mergePattern(c1.pattern, c2.pattern)
415
+ });
416
+ }
417
+ function buildStringConstraints(description) {
418
+ return description.constraints.length === 0 ? undefined : description.constraints.reduce(mergeStringConstraints);
419
+ }
420
+ function mergeNumberConstraints(c1, c2) {
421
+ return makeNumberConstraints({
422
+ isInteger: c1.isInteger || c2.isInteger,
423
+ min: getMax(c1.constraints.min, c2.constraints.min),
424
+ minExcluded: getOr(c1.constraints.minExcluded, c2.constraints.minExcluded),
425
+ max: getMin(c1.constraints.max, c2.constraints.max),
426
+ maxExcluded: getOr(c1.constraints.maxExcluded, c2.constraints.maxExcluded),
427
+ noNaN: getOr(c1.constraints.noNaN, c2.constraints.noNaN),
428
+ noDefaultInfinity: getOr(c1.constraints.noDefaultInfinity, c2.constraints.noDefaultInfinity)
429
+ });
430
+ }
431
+ function buildNumberConstraints(description) {
432
+ return description.constraints.length === 0 ? undefined : description.constraints.reduce(mergeNumberConstraints);
433
+ }
434
+ function mergeBigIntConstraints(c1, c2) {
435
+ return makeBigIntConstraints({
436
+ min: getMax(c1.constraints.min, c2.constraints.min),
437
+ max: getMin(c1.constraints.max, c2.constraints.max)
438
+ });
439
+ }
440
+ function buildBigIntConstraints(description) {
441
+ return description.constraints.length === 0 ? undefined : description.constraints.reduce(mergeBigIntConstraints);
442
+ }
443
+ function mergeDateConstraints(c1, c2) {
444
+ return makeDateConstraints({
445
+ min: getMax(c1.constraints.min, c2.constraints.min),
446
+ max: getMin(c1.constraints.max, c2.constraints.max),
447
+ noInvalidDate: getOr(c1.constraints.noInvalidDate, c2.constraints.noInvalidDate)
448
+ });
449
+ }
450
+ function buildDateConstraints(description) {
451
+ return description.constraints.length === 0 ? undefined : description.constraints.reduce(mergeDateConstraints);
452
+ }
453
+ const constArrayConstraints = /*#__PURE__*/makeArrayConstraints({});
454
+ function mergeArrayConstraints(c1, c2) {
455
+ return makeArrayConstraints({
456
+ minLength: getMax(c1.constraints.minLength, c2.constraints.minLength),
457
+ maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength)
458
+ });
459
+ }
460
+ function buildArrayConstraints(description) {
461
+ return description.constraints.length === 0 ? undefined : description.constraints.reduce(mergeArrayConstraints);
462
+ }
463
+ const arbitraryMemoMap = /*#__PURE__*/globalValue( /*#__PURE__*/Symbol.for("effect/Arbitrary/arbitraryMemoMap"), () => new WeakMap());
464
+ function applyFilters(filters, arb) {
465
+ return fc => filters.reduce((arb, filter) => arb.filter(filter), arb(fc));
466
+ }
467
+ function absurd(message) {
468
+ return () => {
469
+ throw new Error(message);
470
+ };
471
+ }
472
+ function getContextConstraints(description) {
473
+ switch (description._tag) {
250
474
  case "StringKeyword":
251
- return new Deferred(constStringConstraints);
475
+ return buildStringConstraints(description);
252
476
  case "NumberKeyword":
253
- return new Deferred(constNumberConstraints);
254
- case "BooleanKeyword":
255
- return new Succeed(fc => fc.boolean());
477
+ return buildNumberConstraints(description);
256
478
  case "BigIntKeyword":
257
- return new Deferred(constBigIntConstraints);
258
- case "SymbolKeyword":
259
- return new Succeed(fc => fc.string().map(s => Symbol.for(s)));
260
- case "ObjectKeyword":
261
- return new Succeed(fc => fc.oneof(fc.object(), fc.array(fc.anything())));
262
- case "Enums":
479
+ return buildBigIntConstraints(description);
480
+ case "DateFromSelf":
481
+ return buildDateConstraints(description);
482
+ case "TupleType":
483
+ return buildArrayConstraints(description);
484
+ }
485
+ }
486
+ function wrapGo(f, g) {
487
+ return (description, ctx) => f(description, ctx, g(description, ctx));
488
+ }
489
+ const go = /*#__PURE__*/wrapGo((description, ctx, lazyArb) => {
490
+ const annotation = description.annotations[description.annotations.length - 1];
491
+ // error handling
492
+ if (annotation === undefined) {
493
+ switch (description._tag) {
494
+ case "Declaration":
495
+ case "NeverKeyword":
496
+ throw new Error(errors_.getArbitraryMissingAnnotationErrorMessage(description.path, description.ast));
497
+ case "Enums":
498
+ if (description.enums.length === 0) {
499
+ throw new Error(errors_.getArbitraryEmptyEnumErrorMessage(description.path));
500
+ }
501
+ }
502
+ }
503
+ const filters = description.refinements.map(ast => a => Option.isNone(ast.filter(a, SchemaAST.defaultParseOption, ast)));
504
+ if (annotation === undefined) {
505
+ return applyFilters(filters, lazyArb);
506
+ }
507
+ const constraints = getContextConstraints(description);
508
+ if (constraints !== undefined) {
509
+ ctx = {
510
+ ...ctx,
511
+ constraints
512
+ };
513
+ }
514
+ if (description._tag === "Declaration") {
515
+ return applyFilters(filters, annotation(...description.typeParameters.map(p => go(p, ctx)), ctx));
516
+ }
517
+ if (description.refinements.length > 0) {
518
+ // TODO(4.0): remove the `lazyArb` parameter
519
+ return applyFilters(filters, annotation(lazyArb, ctx));
520
+ }
521
+ return annotation(ctx);
522
+ }, (description, ctx) => {
523
+ switch (description._tag) {
524
+ case "DateFromSelf":
525
+ {
526
+ const constraints = buildDateConstraints(description);
527
+ return fc => fc.date(constraints?.constraints);
528
+ }
529
+ case "Declaration":
530
+ case "NeverKeyword":
531
+ return absurd(`BUG: cannot generate an arbitrary for ${description._tag}`);
532
+ case "Literal":
533
+ return fc => fc.constant(description.literal);
534
+ case "UniqueSymbol":
535
+ return fc => fc.constant(description.symbol);
536
+ case "Keyword":
263
537
  {
264
- if (ast.enums.length === 0) {
265
- throw new Error(errors_.getArbitraryEmptyEnumErrorMessage(path));
538
+ switch (description.value) {
539
+ case "UndefinedKeyword":
540
+ return fc => fc.constant(undefined);
541
+ case "VoidKeyword":
542
+ case "UnknownKeyword":
543
+ case "AnyKeyword":
544
+ return fc => fc.anything();
545
+ case "BooleanKeyword":
546
+ return fc => fc.boolean();
547
+ case "SymbolKeyword":
548
+ return fc => fc.string().map(s => Symbol.for(s));
549
+ case "ObjectKeyword":
550
+ return fc => fc.oneof(fc.object(), fc.array(fc.anything()));
266
551
  }
267
- return new Succeed(fc => fc.oneof(...ast.enums.map(([_, value]) => fc.constant(value))));
268
552
  }
553
+ case "Enums":
554
+ return fc => fc.oneof(...description.enums.map(([_, value]) => fc.constant(value)));
269
555
  case "TemplateLiteral":
270
- return new Succeed(fc => {
271
- const string = fc.string({
272
- maxLength: 5
273
- });
274
- const number = fc.float({
275
- noDefaultInfinity: true,
276
- noNaN: true
277
- });
278
- const getTemplateLiteralArb = ast => {
279
- const components = ast.head !== "" ? [fc.constant(ast.head)] : [];
280
- const getTemplateLiteralSpanTypeArb = ast => {
281
- switch (ast._tag) {
282
- case "StringKeyword":
283
- return string;
284
- case "NumberKeyword":
285
- return number;
286
- case "Literal":
287
- return fc.constant(String(ast.literal));
288
- case "Union":
289
- return fc.oneof(...ast.types.map(getTemplateLiteralSpanTypeArb));
290
- case "TemplateLiteral":
291
- return getTemplateLiteralArb(ast);
292
- }
293
- };
294
- ast.spans.forEach(span => {
295
- components.push(getTemplateLiteralSpanTypeArb(span.type));
296
- if (span.literal !== "") {
297
- components.push(fc.constant(span.literal));
298
- }
556
+ {
557
+ return fc => {
558
+ const string = fc.string({
559
+ maxLength: 5
560
+ });
561
+ const number = fc.float({
562
+ noDefaultInfinity: true,
563
+ noNaN: true
299
564
  });
300
- return fc.tuple(...components).map(spans => spans.join(""));
565
+ const getTemplateLiteralArb = description => {
566
+ const components = description.head !== "" ? [fc.constant(description.head)] : [];
567
+ const getTemplateLiteralSpanTypeArb = description => {
568
+ switch (description._tag) {
569
+ case "StringKeyword":
570
+ return string;
571
+ case "NumberKeyword":
572
+ return number;
573
+ case "Literal":
574
+ return fc.constant(String(description.literal));
575
+ case "Union":
576
+ return fc.oneof(...description.members.map(getTemplateLiteralSpanTypeArb));
577
+ case "TemplateLiteral":
578
+ return getTemplateLiteralArb(description);
579
+ default:
580
+ return fc.constant("");
581
+ }
582
+ };
583
+ description.spans.forEach(span => {
584
+ components.push(getTemplateLiteralSpanTypeArb(span.description));
585
+ if (span.literal !== "") {
586
+ components.push(fc.constant(span.literal));
587
+ }
588
+ });
589
+ return fc.tuple(...components).map(spans => spans.join(""));
590
+ };
591
+ return getTemplateLiteralArb(description);
301
592
  };
302
- return getTemplateLiteralArb(ast);
303
- });
304
- case "Refinement":
593
+ }
594
+ case "StringKeyword":
305
595
  {
306
- const from = toOp(ast.from, ctx, path);
307
- const filters = [...from.filters, a => Option.isNone(ast.filter(a, AST.defaultParseOption, ast))];
308
- switch (from._tag) {
309
- case "Succeed":
310
- {
311
- return new Succeed(from.lazyArbitrary, filters);
312
- }
313
- case "Deferred":
314
- {
315
- return new Deferred(merge(from.config, getRefinementConstraints(from.config._tag, ast)), filters);
316
- }
317
- }
596
+ const constraints = buildStringConstraints(description);
597
+ const pattern = constraints?.pattern;
598
+ return pattern !== undefined ? fc => fc.stringMatching(new RegExp(pattern)) : fc => fc.string(constraints?.constraints);
599
+ }
600
+ case "NumberKeyword":
601
+ {
602
+ const constraints = buildNumberConstraints(description);
603
+ return constraints?.isInteger ? fc => fc.integer(constraints.constraints) : fc => fc.float(constraints?.constraints);
604
+ }
605
+ case "BigIntKeyword":
606
+ {
607
+ const constraints = buildBigIntConstraints(description);
608
+ return fc => fc.bigInt(constraints?.constraints ?? {});
318
609
  }
319
610
  case "TupleType":
320
- return new Deferred(makeArrayConfig({}, ast));
321
- case "TypeLiteral":
322
611
  {
323
- const propertySignaturesTypes = ast.propertySignatures.map(ps => go(ps.type, ctx, path.concat(ps.name)));
324
- const indexSignatures = ast.indexSignatures.map(is => [go(is.parameter, ctx, path), go(is.type, ctx, path)]);
325
- return new Succeed(fc => {
326
- const arbs = {};
327
- const requiredKeys = [];
612
+ const elements = [];
613
+ let hasOptionals = false;
614
+ for (const element of description.elements) {
615
+ elements.push(go(element.description, ctx));
616
+ if (element.isOptional) {
617
+ hasOptionals = true;
618
+ }
619
+ }
620
+ const rest = description.rest.map(d => go(d, ctx));
621
+ return fc => {
622
+ // ---------------------------------------------
623
+ // handle elements
624
+ // ---------------------------------------------
625
+ let output = fc.tuple(...elements.map(arb => arb(fc)));
626
+ if (hasOptionals) {
627
+ const indexes = fc.tuple(...description.elements.map(element => element.isOptional ? fc.boolean() : fc.constant(true)));
628
+ output = output.chain(tuple => indexes.map(booleans => {
629
+ for (const [i, b] of booleans.reverse().entries()) {
630
+ if (!b) {
631
+ tuple.splice(booleans.length - i, 1);
632
+ }
633
+ }
634
+ return tuple;
635
+ }));
636
+ }
328
637
  // ---------------------------------------------
329
- // handle property signatures
638
+ // handle rest element
330
639
  // ---------------------------------------------
331
- for (let i = 0; i < propertySignaturesTypes.length; i++) {
332
- const ps = ast.propertySignatures[i];
333
- const name = ps.name;
334
- if (!ps.isOptional) {
335
- requiredKeys.push(name);
640
+ if (Arr.isNonEmptyReadonlyArray(rest)) {
641
+ const constraints = buildArrayConstraints(description) ?? constArrayConstraints;
642
+ const [head, ...tail] = rest;
643
+ const item = head(fc);
644
+ output = output.chain(as => {
645
+ const len = as.length;
646
+ // We must adjust the constraints for the rest element
647
+ // because the elements might have generated some values
648
+ const restArrayConstraints = subtractElementsLength(constraints.constraints, len);
649
+ if (restArrayConstraints.maxLength === 0) {
650
+ return fc.constant(as);
651
+ }
652
+ /*
653
+ `getSuspendedArray` is used to generate less values in
654
+ the context of a recursive schema. Without it, the following schema
655
+ would generate an big amount of values possibly leading to a stack
656
+ overflow:
657
+ ```ts
658
+ type A = ReadonlyArray<A | null>
659
+ const schema = S.Array(
660
+ S.NullOr(S.suspend((): S.Schema<A> => schema))
661
+ )
662
+ ```
663
+ */
664
+ const arr = ctx.depthIdentifier !== undefined ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, restArrayConstraints) : fc.array(item, restArrayConstraints);
665
+ if (len === 0) {
666
+ return arr;
667
+ }
668
+ return arr.map(rest => [...as, ...rest]);
669
+ });
670
+ // ---------------------------------------------
671
+ // handle post rest elements
672
+ // ---------------------------------------------
673
+ for (let j = 0; j < tail.length; j++) {
674
+ output = output.chain(as => tail[j](fc).map(a => [...as, a]));
336
675
  }
337
- arbs[name] = propertySignaturesTypes[i](fc);
338
676
  }
339
- let output = fc.record(arbs, {
677
+ return output;
678
+ };
679
+ }
680
+ case "TypeLiteral":
681
+ {
682
+ const propertySignatures = [];
683
+ const requiredKeys = [];
684
+ for (const ps of description.propertySignatures) {
685
+ if (!ps.isOptional) {
686
+ requiredKeys.push(ps.name);
687
+ }
688
+ propertySignatures.push(go(ps.value, ctx));
689
+ }
690
+ const indexSignatures = description.indexSignatures.map(is => [go(is.parameter, ctx), go(is.value, ctx)]);
691
+ return fc => {
692
+ const pps = {};
693
+ for (let i = 0; i < propertySignatures.length; i++) {
694
+ const ps = description.propertySignatures[i];
695
+ pps[ps.name] = propertySignatures[i](fc);
696
+ }
697
+ let output = fc.record(pps, {
340
698
  requiredKeys
341
699
  });
342
700
  // ---------------------------------------------
@@ -348,16 +706,18 @@ export const toOp = (ast, ctx, path) => {
348
706
  output = output.chain(o => {
349
707
  const item = fc.tuple(key, value);
350
708
  /*
351
- `getSuspendedArray` is used to generate less key/value pairs in
352
- the context of a recursive schema. Without it, the following schema
353
- would generate an big amount of values possibly leading to a stack
354
- overflow:
355
- ```ts
356
- type A = { [_: string]: A }
357
- const schema = S.Record({ key: S.String, value: S.suspend((): S.Schema<A> => schema) })
358
- ```
359
- */
360
- const arr = ctx.depthIdentifier !== undefined ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, defaultSuspendedArrayConstraints) : fc.array(item);
709
+ `getSuspendedArray` is used to generate less key/value pairs in
710
+ the context of a recursive schema. Without it, the following schema
711
+ would generate an big amount of values possibly leading to a stack
712
+ overflow:
713
+ ```ts
714
+ type A = { [_: string]: A }
715
+ const schema = S.Record({ key: S.String, value: S.suspend((): S.Schema<A> => schema) })
716
+ ```
717
+ */
718
+ const arr = ctx.depthIdentifier !== undefined ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, {
719
+ maxLength: 2
720
+ }) : fc.array(item);
361
721
  return arr.map(tuples => ({
362
722
  ...Object.fromEntries(tuples),
363
723
  ...o
@@ -365,237 +725,57 @@ export const toOp = (ast, ctx, path) => {
365
725
  });
366
726
  }
367
727
  return output;
368
- });
728
+ };
369
729
  }
370
730
  case "Union":
371
731
  {
372
- const types = ast.types.map(member => go(member, ctx, path));
373
- return new Succeed(fc => fc.oneof(...types.map(arb => arb(fc))));
732
+ const members = description.members.map(member => go(member, ctx));
733
+ return fc => fc.oneof(...members.map(arb => arb(fc)));
374
734
  }
375
735
  case "Suspend":
376
736
  {
377
- const memo = arbitraryMemoMap.get(ast);
737
+ const memo = arbitraryMemoMap.get(description.ast);
378
738
  if (memo) {
379
- return new Succeed(memo);
739
+ return memo;
740
+ }
741
+ if (ctx.depthIdentifier === undefined) {
742
+ ctx = {
743
+ ...ctx,
744
+ depthIdentifier: description.id
745
+ };
380
746
  }
381
747
  const get = util_.memoizeThunk(() => {
382
- return go(ast.f(), getSuspendedContext(ctx, ast), path);
748
+ return go(description.description(), ctx);
383
749
  });
384
750
  const out = fc => fc.constant(null).chain(() => get()(fc));
385
- arbitraryMemoMap.set(ast, out);
386
- return new Succeed(out);
751
+ arbitraryMemoMap.set(description.ast, out);
752
+ return out;
753
+ }
754
+ case "Ref":
755
+ {
756
+ const memo = arbitraryMemoMap.get(description.ast);
757
+ if (memo) {
758
+ return memo;
759
+ }
760
+ throw new Error(`BUG: Ref ${JSON.stringify(description.id)} not found`);
387
761
  }
388
- case "Transformation":
389
- return toOp(ast.to, ctx, path);
390
762
  }
391
- };
392
- function subtractElementsLength(constraints, elementsLength) {
393
- if (elementsLength === 0 || constraints.minLength === undefined && constraints.maxLength === undefined) {
763
+ });
764
+ function subtractElementsLength(constraints, len) {
765
+ if (len === 0 || constraints.minLength === undefined && constraints.maxLength === undefined) {
394
766
  return constraints;
395
767
  }
396
768
  const out = {
397
769
  ...constraints
398
770
  };
399
771
  if (out.minLength !== undefined) {
400
- out.minLength = Math.max(out.minLength - elementsLength, 0);
772
+ out.minLength = Math.max(out.minLength - len, 0);
401
773
  }
402
774
  if (out.maxLength !== undefined) {
403
- out.maxLength = Math.max(out.maxLength - elementsLength, 0);
775
+ out.maxLength = Math.max(out.maxLength - len, 0);
404
776
  }
405
777
  return out;
406
778
  }
407
- const goTupleType = (ast, ctx, path, constraints) => {
408
- const elements = [];
409
- let hasOptionals = false;
410
- let i = 0;
411
- for (const element of ast.elements) {
412
- elements.push(go(element.type, ctx, path.concat(i++)));
413
- if (element.isOptional) {
414
- hasOptionals = true;
415
- }
416
- }
417
- const rest = ast.rest.map(annotatedAST => go(annotatedAST.type, ctx, path));
418
- return fc => {
419
- // ---------------------------------------------
420
- // handle elements
421
- // ---------------------------------------------
422
- let output = fc.tuple(...elements.map(arb => arb(fc)));
423
- if (hasOptionals) {
424
- const indexes = fc.tuple(...ast.elements.map(element => element.isOptional ? fc.boolean() : fc.constant(true)));
425
- output = output.chain(tuple => indexes.map(booleans => {
426
- for (const [i, b] of booleans.reverse().entries()) {
427
- if (!b) {
428
- tuple.splice(booleans.length - i, 1);
429
- }
430
- }
431
- return tuple;
432
- }));
433
- }
434
- // ---------------------------------------------
435
- // handle rest element
436
- // ---------------------------------------------
437
- if (Arr.isNonEmptyReadonlyArray(rest)) {
438
- const [head, ...tail] = rest;
439
- const item = head(fc);
440
- output = output.chain(as => {
441
- const len = as.length;
442
- // We must adjust the constraints for the rest element
443
- // because the elements might have generated some values
444
- const restArrayConstraints = subtractElementsLength(constraints, len);
445
- if (restArrayConstraints.maxLength === 0) {
446
- return fc.constant(as);
447
- }
448
- /*
449
- `getSuspendedArray` is used to generate less values in
450
- the context of a recursive schema. Without it, the following schema
451
- would generate an big amount of values possibly leading to a stack
452
- overflow:
453
- ```ts
454
- type A = ReadonlyArray<A | null>
455
- const schema = S.Array(
456
- S.NullOr(S.suspend((): S.Schema<A> => schema))
457
- )
458
- ```
459
- */
460
- const arr = ctx.depthIdentifier !== undefined ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, restArrayConstraints) : fc.array(item, restArrayConstraints);
461
- if (len === 0) {
462
- return arr;
463
- }
464
- return arr.map(rest => [...as, ...rest]);
465
- });
466
- // ---------------------------------------------
467
- // handle post rest elements
468
- // ---------------------------------------------
469
- for (let j = 0; j < tail.length; j++) {
470
- output = output.chain(as => tail[j](fc).map(a => [...as, a]));
471
- }
472
- }
473
- return output;
474
- };
475
- };
476
- const getRefinementConstraints = (_tag, ast) => {
477
- const TypeAnnotationId = ast.annotations[AST.SchemaIdAnnotationId];
478
- const jsonSchema = Option.getOrElse(AST.getJSONSchemaAnnotation(ast), () => ({}));
479
- switch (_tag) {
480
- case "StringConstraints":
481
- return makeStringConstraints(jsonSchema);
482
- case "NumberConstraints":
483
- {
484
- if (TypeAnnotationId === schemaId_.NonNaNSchemaId) {
485
- return makeNumberConstraints({
486
- noNaN: true
487
- });
488
- } else {
489
- return makeNumberConstraints({
490
- isInteger: "type" in jsonSchema && jsonSchema.type === "integer",
491
- noNaN: "type" in jsonSchema && jsonSchema.type === "number" ? true : undefined,
492
- noDefaultInfinity: "type" in jsonSchema && jsonSchema.type === "number" ? true : undefined,
493
- min: jsonSchema.exclusiveMinimum ?? jsonSchema.minimum,
494
- minExcluded: "exclusiveMinimum" in jsonSchema ? true : undefined,
495
- max: jsonSchema.exclusiveMaximum ?? jsonSchema.maximum,
496
- maxExcluded: "exclusiveMaximum" in jsonSchema ? true : undefined
497
- });
498
- }
499
- }
500
- case "BigIntConstraints":
501
- {
502
- const c = getASTConstraints(ast);
503
- return c !== undefined ? makeBigIntConstraints(c) : undefined;
504
- }
505
- case "DateConstraints":
506
- {
507
- const c = getASTConstraints(ast);
508
- return c !== undefined ? makeDateConstraints(c) : undefined;
509
- }
510
- case "ArrayConstraints":
511
- return makeArrayConstraints({
512
- minLength: jsonSchema.minItems,
513
- maxLength: jsonSchema.maxItems
514
- });
515
- }
516
- };
517
- function getMax(n1, n2) {
518
- return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n2 : n1;
519
- }
520
- function getMin(n1, n2) {
521
- return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n1 : n2;
522
- }
523
- const getOr = (a, b) => {
524
- return a === undefined ? b : b === undefined ? a : a || b;
525
- };
526
- function mergePattern(pattern1, pattern2) {
527
- if (pattern1 === undefined) {
528
- return pattern2;
529
- }
530
- if (pattern2 === undefined) {
531
- return pattern1;
532
- }
533
- return `(?:${pattern1})|(?:${pattern2})`;
534
- }
535
- const merge = (c1, c2) => {
536
- if (c2) {
537
- switch (c1._tag) {
538
- case "StringConstraints":
539
- {
540
- if (c2._tag === "StringConstraints") {
541
- return makeStringConstraints({
542
- minLength: getMax(c1.constraints.minLength, c2.constraints.minLength),
543
- maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength),
544
- pattern: mergePattern(c1.pattern, c2.pattern)
545
- });
546
- }
547
- break;
548
- }
549
- case "NumberConstraints":
550
- {
551
- if (c2._tag === "NumberConstraints") {
552
- return makeNumberConstraints({
553
- isInteger: c1.isInteger || c2.isInteger,
554
- min: getMax(c1.constraints.min, c2.constraints.min),
555
- minExcluded: getOr(c1.constraints.minExcluded, c2.constraints.minExcluded),
556
- max: getMin(c1.constraints.max, c2.constraints.max),
557
- maxExcluded: getOr(c1.constraints.maxExcluded, c2.constraints.maxExcluded),
558
- noNaN: getOr(c1.constraints.noNaN, c2.constraints.noNaN),
559
- noDefaultInfinity: getOr(c1.constraints.noDefaultInfinity, c2.constraints.noDefaultInfinity)
560
- });
561
- }
562
- break;
563
- }
564
- case "BigIntConstraints":
565
- {
566
- if (c2._tag === "BigIntConstraints") {
567
- return makeBigIntConstraints({
568
- min: getMax(c1.constraints.min, c2.constraints.min),
569
- max: getMin(c1.constraints.max, c2.constraints.max)
570
- });
571
- }
572
- break;
573
- }
574
- case "DateConstraints":
575
- {
576
- if (c2._tag === "DateConstraints") {
577
- return makeDateConstraints({
578
- min: getMax(c1.constraints.min, c2.constraints.min),
579
- max: getMin(c1.constraints.max, c2.constraints.max),
580
- noInvalidDate: getOr(c1.constraints.noInvalidDate, c2.constraints.noInvalidDate)
581
- });
582
- }
583
- break;
584
- }
585
- case "ArrayConstraints":
586
- {
587
- if (c2._tag === "ArrayConstraints") {
588
- return makeArrayConfig({
589
- minLength: getMax(c1.constraints.minLength, c2.constraints.minLength),
590
- maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength)
591
- }, c1.ast);
592
- }
593
- break;
594
- }
595
- }
596
- }
597
- return c1;
598
- };
599
779
  const getSuspendedArray = (fc, depthIdentifier, maxDepth, item, constraints) => {
600
780
  // In the context of a recursive schema, we don't want a `maxLength` greater than 2.
601
781
  // The only exception is when `minLength` is also set, in which case we set
@@ -612,14 +792,4 @@ const getSuspendedArray = (fc, depthIdentifier, maxDepth, item, constraints) =>
612
792
  depthIdentifier
613
793
  }, fc.constant([]), fc.array(item, constraints));
614
794
  };
615
- const getSuspendedContext = (ctx, ast) => {
616
- if (ctx.depthIdentifier !== undefined) {
617
- return ctx;
618
- }
619
- const depthIdentifier = AST.getIdentifierAnnotation(ast).pipe(Option.orElse(() => AST.getIdentifierAnnotation(ast.f())), Option.getOrElse(() => "SuspendDefaultDepthIdentifier"));
620
- return {
621
- ...ctx,
622
- depthIdentifier
623
- };
624
- };
625
795
  //# sourceMappingURL=Arbitrary.js.map