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.
- package/dist/cjs/Arbitrary.js +595 -426
- package/dist/cjs/Arbitrary.js.map +1 -1
- package/dist/cjs/Context.js.map +1 -1
- package/dist/cjs/Schema.js +3 -3
- package/dist/cjs/Schema.js.map +1 -1
- package/dist/cjs/SchemaAST.js +7 -2
- package/dist/cjs/SchemaAST.js.map +1 -1
- package/dist/cjs/internal/context.js.map +1 -1
- package/dist/cjs/internal/version.js +1 -1
- package/dist/dts/Arbitrary.d.ts.map +1 -1
- package/dist/dts/Context.d.ts +4 -8
- package/dist/dts/Context.d.ts.map +1 -1
- package/dist/dts/Schema.d.ts +1 -1
- package/dist/dts/Schema.d.ts.map +1 -1
- package/dist/dts/SchemaAST.d.ts +5 -0
- package/dist/dts/SchemaAST.d.ts.map +1 -1
- package/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/esm/Arbitrary.js +593 -423
- package/dist/esm/Arbitrary.js.map +1 -1
- package/dist/esm/Context.js.map +1 -1
- package/dist/esm/Schema.js +3 -3
- package/dist/esm/Schema.js.map +1 -1
- package/dist/esm/SchemaAST.js +5 -0
- package/dist/esm/SchemaAST.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/context.js.map +1 -1
- package/dist/esm/internal/version.js +1 -1
- package/package.json +1 -1
- package/src/Arbitrary.ts +799 -477
- package/src/Context.ts +8 -9
- package/src/Schema.ts +4 -3
- package/src/SchemaAST.ts +6 -0
- package/src/index.ts +1 -0
- package/src/internal/context.ts +9 -9
- package/src/internal/version.ts +1 -1
package/dist/esm/Arbitrary.js
CHANGED
|
@@ -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
|
|
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 =>
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
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[
|
|
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
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
|
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
|
-
|
|
239
|
+
{
|
|
240
|
+
return {
|
|
241
|
+
_tag: "Literal",
|
|
242
|
+
literal: ast.literal,
|
|
243
|
+
path,
|
|
244
|
+
refinements: [],
|
|
245
|
+
annotations: []
|
|
246
|
+
};
|
|
247
|
+
}
|
|
240
248
|
case "UniqueSymbol":
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
|
475
|
+
return buildStringConstraints(description);
|
|
252
476
|
case "NumberKeyword":
|
|
253
|
-
return
|
|
254
|
-
case "BooleanKeyword":
|
|
255
|
-
return new Succeed(fc => fc.boolean());
|
|
477
|
+
return buildNumberConstraints(description);
|
|
256
478
|
case "BigIntKeyword":
|
|
257
|
-
return
|
|
258
|
-
case "
|
|
259
|
-
return
|
|
260
|
-
case "
|
|
261
|
-
return
|
|
262
|
-
|
|
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
|
-
|
|
265
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
case "Refinement":
|
|
593
|
+
}
|
|
594
|
+
case "StringKeyword":
|
|
305
595
|
{
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
|
638
|
+
// handle rest element
|
|
330
639
|
// ---------------------------------------------
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const arr = ctx.depthIdentifier !== undefined ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, 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
|
|
373
|
-
return
|
|
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
|
|
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(
|
|
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
|
|
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,
|
|
393
|
-
if (
|
|
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 -
|
|
772
|
+
out.minLength = Math.max(out.minLength - len, 0);
|
|
401
773
|
}
|
|
402
774
|
if (out.maxLength !== undefined) {
|
|
403
|
-
out.maxLength = Math.max(out.maxLength -
|
|
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
|