effect-app 4.0.0-beta.320 → 4.0.0-beta.321

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.
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
- import { identity, pipe } from "effect/Function"
4
- import * as Match from "effect/Match"
3
+ import { identity } from "effect/Function"
5
4
  import * as Array from "../../Array.ts"
6
5
  import { toNonEmptyArray } from "../../Array.ts"
7
6
  import * as Option from "../../Option.ts"
@@ -192,197 +191,204 @@ const interpret = <
192
191
  ? { ..._, path: `${path}.-1.${_.path}` }
193
192
  : { ..._, result: _.result.map(applyPath(path)) }
194
193
 
195
- pipe(
196
- a,
197
- Match.valueTags({
198
- value: () => {
199
- // data.filter.push(value)
200
- },
201
- where: ({ current, operation, relation, subPath }) => {
202
- upd(interpret(current))
203
- if (typeof operation === "function") {
204
- data.filter.push(
205
- {
206
- t: "where-scope",
207
- result: interpret(operation(make())).filter.map(subPath ? applyPath(subPath) : identity),
208
- relation
209
- }
210
- )
211
- } else {
212
- data.filter.push(
213
- {
214
- t: "where",
215
- path: operation[0],
216
- op: operation.length === 2 ? "eq" : operation[1],
217
- value: operation.length === 2 ? operation[1] : operation[2]
218
- }
219
- )
220
- }
221
- },
222
- and: ({ current, operation, relation }) => {
223
- upd(interpret(current))
224
- if (typeof operation === "function") {
225
- data.filter.push(
226
- { t: "and-scope", result: interpret(operation(make())).filter, relation }
227
- )
228
- } else {
229
- data.filter.push(
230
- {
231
- t: "and",
232
- path: operation[0],
233
- op: operation.length === 2 ? "eq" : operation[1],
234
- value: operation.length === 2 ? operation[1] : operation[2]
235
- }
236
- )
237
- }
238
- },
239
- or: ({ current, operation, relation }) => {
240
- upd(interpret(current))
241
- if (typeof operation === "function") {
242
- data.filter.push(
243
- { t: "or-scope", result: interpret(operation(make())).filter, relation }
244
- )
245
- } else {
246
- data.filter.push(
247
- {
248
- t: "or",
249
- path: operation[0],
250
- op: operation.length === 2 ? "eq" : operation[1],
251
- value: operation.length === 2 ? operation[1] : operation[2]
252
- }
253
- )
254
- }
255
- },
256
- one: ({ current }) => {
257
- upd(interpret(current))
258
- data.limit = 1
259
- data.ttype = "one"
260
- },
261
- count: ({ current }) => {
262
- upd(interpret(current))
263
- data.ttype = "count"
264
- data.schema = S.Struct({ id: S.String }) as any
265
- },
266
- order: ({ current, direction, field }) => {
267
- upd(interpret(current))
268
- data.order.push({ key: field, direction })
269
- },
270
- page: (v) => {
271
- upd(interpret(v.current))
272
- data.limit = v.take
273
- data.skip = v.skip
274
- },
275
- project: (v) => {
276
- upd(interpret(v.current))
277
- if (v.mode === "aggregate" && v.aggregateMap) {
278
- data.schema = v.schema
279
- data.mode = "aggregate"
280
- data.aggregateMap = Object.fromEntries(
281
- Object.entries(v.aggregateMap).map(([key, expression]) => {
282
- switch (expression._tag) {
283
- case "agg-field":
284
- return [key, { _tag: "agg-field" as const, path: expression.path }]
285
- case "agg-count":
286
- return [key, { _tag: "agg-count" as const }]
287
- case "agg-count-when": {
288
- const filter = interpret(expression.operation(make())).filter
289
- return [key, { _tag: "agg-count-when" as const, filter }]
290
- }
291
- case "agg-sum":
292
- return [key, { _tag: "agg-sum" as const, field: expression.field }]
293
- case "agg-min":
294
- return [key, { _tag: "agg-min" as const, field: expression.field }]
295
- case "agg-max":
296
- return [key, { _tag: "agg-max" as const, field: expression.field }]
297
- }
298
- })
299
- )
300
- return
301
- }
302
- if (v.computed && v.mode === "transform") {
303
- throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'")
304
- }
305
- data.schema = v.schema
306
- data.mode = v.computed
307
- ? v.mode === "collect" ? "collect" : "project"
308
- : v.mode
309
- data.computed = v.computed
310
- ? Object.fromEntries(
311
- Object.entries(v.computed).map(([key, expression]) => {
312
- const e = expression
313
- const op = "operation" in e ? e.operation : undefined
314
- const filter = op ? interpret(op(make())).filter.map(applyPath(e.path)) : []
315
- switch (e._tag) {
316
- case "relation-count":
317
- case "relation-any":
318
- case "relation-every":
319
- return [key, { _tag: e._tag, path: e.path, filter }]
320
- case "relation-distinct-count":
321
- case "relation-sum":
322
- return [
323
- key,
324
- { _tag: e._tag, path: e.path, field: e.field, filter }
325
- ]
326
- case "relation-sum-expr":
327
- return [
328
- key,
329
- { _tag: e._tag, path: e.path, expression: e.expression, filter }
330
- ]
331
- case "relation-sum-expr-by":
332
- return [
333
- key,
334
- {
335
- _tag: e._tag,
336
- path: e.path,
337
- expression: e.expression,
338
- unit: e.unit,
339
- filter
340
- }
341
- ]
342
- case "relation-sum-expr-normalized":
343
- return [
344
- key,
345
- {
346
- _tag: e._tag,
347
- path: e.path,
348
- expression: e.expression,
349
- unit: e.unit,
350
- toBase: e.toBase,
351
- factors: e.factors,
352
- filter
353
- }
354
- ]
355
- case "relation-collect":
356
- return [
357
- key,
358
- {
359
- _tag: e._tag,
360
- path: e.path,
361
- field: e.field,
362
- distinct: e.distinct,
363
- filter
364
- }
365
- ]
366
- case "relation-collect-fields":
367
- return [
368
- key,
369
- {
370
- _tag: e._tag,
371
- path: e.path,
372
- fields: e.fields,
373
- distinct: e.distinct,
374
- filter
375
- }
376
- ]
377
- case "relation-length":
378
- return [key, { _tag: e._tag, path: e.path }]
194
+ switch (a._tag) {
195
+ case "value":
196
+ break
197
+ case "where": {
198
+ const { current, operation, relation, subPath } = a
199
+ upd(interpret(current))
200
+ if (typeof operation === "function") {
201
+ data.filter.push(
202
+ {
203
+ t: "where-scope",
204
+ result: interpret(operation(make())).filter.map(subPath ? applyPath(subPath) : identity),
205
+ relation
206
+ }
207
+ )
208
+ } else {
209
+ data.filter.push(
210
+ {
211
+ t: "where",
212
+ path: operation[0],
213
+ op: operation.length === 2 ? "eq" : operation[1],
214
+ value: operation.length === 2 ? operation[1] : operation[2]
215
+ }
216
+ )
217
+ }
218
+ break
219
+ }
220
+ case "and": {
221
+ const { current, operation, relation } = a
222
+ upd(interpret(current))
223
+ if (typeof operation === "function") {
224
+ data.filter.push(
225
+ { t: "and-scope", result: interpret(operation(make())).filter, relation }
226
+ )
227
+ } else {
228
+ data.filter.push(
229
+ {
230
+ t: "and",
231
+ path: operation[0],
232
+ op: operation.length === 2 ? "eq" : operation[1],
233
+ value: operation.length === 2 ? operation[1] : operation[2]
234
+ }
235
+ )
236
+ }
237
+ break
238
+ }
239
+ case "or": {
240
+ const { current, operation, relation } = a
241
+ upd(interpret(current))
242
+ if (typeof operation === "function") {
243
+ data.filter.push(
244
+ { t: "or-scope", result: interpret(operation(make())).filter, relation }
245
+ )
246
+ } else {
247
+ data.filter.push(
248
+ {
249
+ t: "or",
250
+ path: operation[0],
251
+ op: operation.length === 2 ? "eq" : operation[1],
252
+ value: operation.length === 2 ? operation[1] : operation[2]
253
+ }
254
+ )
255
+ }
256
+ break
257
+ }
258
+ case "one": {
259
+ upd(interpret(a.current))
260
+ data.limit = 1
261
+ data.ttype = "one"
262
+ break
263
+ }
264
+ case "count": {
265
+ upd(interpret(a.current))
266
+ data.ttype = "count"
267
+ data.schema = S.Struct({ id: S.String }) as any
268
+ break
269
+ }
270
+ case "order": {
271
+ upd(interpret(a.current))
272
+ data.order.push({ key: a.field, direction: a.direction })
273
+ break
274
+ }
275
+ case "page": {
276
+ upd(interpret(a.current))
277
+ data.limit = a.take
278
+ data.skip = a.skip
279
+ break
280
+ }
281
+ case "project": {
282
+ upd(interpret(a.current))
283
+ if (a.mode === "aggregate" && a.aggregateMap) {
284
+ data.schema = a.schema
285
+ data.mode = "aggregate"
286
+ data.aggregateMap = Object.fromEntries(
287
+ Object.entries(a.aggregateMap).map(([key, expression]) => {
288
+ switch (expression._tag) {
289
+ case "agg-field":
290
+ return [key, { _tag: "agg-field" as const, path: expression.path }]
291
+ case "agg-count":
292
+ return [key, { _tag: "agg-count" as const }]
293
+ case "agg-count-when": {
294
+ const filter = interpret(expression.operation(make())).filter
295
+ return [key, { _tag: "agg-count-when" as const, filter }]
379
296
  }
380
- })
381
- )
382
- : undefined
297
+ case "agg-sum":
298
+ return [key, { _tag: "agg-sum" as const, field: expression.field }]
299
+ case "agg-min":
300
+ return [key, { _tag: "agg-min" as const, field: expression.field }]
301
+ case "agg-max":
302
+ return [key, { _tag: "agg-max" as const, field: expression.field }]
303
+ }
304
+ })
305
+ )
306
+ break
383
307
  }
384
- })
385
- )
308
+ if (a.computed && a.mode === "transform") {
309
+ throw new Error("Computed projections require mode 'project' or 'collect', not 'transform'")
310
+ }
311
+ data.schema = a.schema
312
+ data.mode = a.computed
313
+ ? a.mode === "collect" ? "collect" : "project"
314
+ : a.mode
315
+ data.computed = a.computed
316
+ ? Object.fromEntries(
317
+ Object.entries(a.computed).map(([key, expression]) => {
318
+ const e = expression
319
+ const op = "operation" in e ? e.operation : undefined
320
+ const filter = op ? interpret(op(make())).filter.map(applyPath(e.path)) : []
321
+ switch (e._tag) {
322
+ case "relation-count":
323
+ case "relation-any":
324
+ case "relation-every":
325
+ return [key, { _tag: e._tag, path: e.path, filter }]
326
+ case "relation-distinct-count":
327
+ case "relation-sum":
328
+ return [
329
+ key,
330
+ { _tag: e._tag, path: e.path, field: e.field, filter }
331
+ ]
332
+ case "relation-sum-expr":
333
+ return [
334
+ key,
335
+ { _tag: e._tag, path: e.path, expression: e.expression, filter }
336
+ ]
337
+ case "relation-sum-expr-by":
338
+ return [
339
+ key,
340
+ {
341
+ _tag: e._tag,
342
+ path: e.path,
343
+ expression: e.expression,
344
+ unit: e.unit,
345
+ filter
346
+ }
347
+ ]
348
+ case "relation-sum-expr-normalized":
349
+ return [
350
+ key,
351
+ {
352
+ _tag: e._tag,
353
+ path: e.path,
354
+ expression: e.expression,
355
+ unit: e.unit,
356
+ toBase: e.toBase,
357
+ factors: e.factors,
358
+ filter
359
+ }
360
+ ]
361
+ case "relation-collect":
362
+ return [
363
+ key,
364
+ {
365
+ _tag: e._tag,
366
+ path: e.path,
367
+ field: e.field,
368
+ distinct: e.distinct,
369
+ filter
370
+ }
371
+ ]
372
+ case "relation-collect-fields":
373
+ return [
374
+ key,
375
+ {
376
+ _tag: e._tag,
377
+ path: e.path,
378
+ fields: e.fields,
379
+ distinct: e.distinct,
380
+ filter
381
+ }
382
+ ]
383
+ case "relation-length":
384
+ return [key, { _tag: e._tag, path: e.path }]
385
+ }
386
+ })
387
+ )
388
+ : undefined
389
+ break
390
+ }
391
+ }
386
392
 
387
393
  return data
388
394
  }
@@ -28,12 +28,7 @@ export interface Constructor<in out A extends B.Brand<any>> {
28
28
  is(a: Unbranded<A>): a is Unbranded<A> & A
29
29
  }
30
30
 
31
- type BrandAnnotations<C extends B.Brand<any>> =
32
- & S.Annotations.Filter
33
- & (
34
- C extends string ? { readonly toArbitrary?: S.Annotations.ToArbitrary.Declaration<C, readonly []> }
35
- : {}
36
- )
31
+ type BrandAnnotations = S.Annotations.Filter
37
32
 
38
33
  export interface BrandedSchema<S extends S.Top, C extends B.Brand<any>> extends
39
34
  S.Bottom<
@@ -57,7 +52,7 @@ export interface BrandedSchema<S extends S.Top, C extends B.Brand<any>> extends
57
52
 
58
53
  export const fromBrand = <C extends B.Brand<any>>(
59
54
  constructor: Constructor<C>,
60
- options?: BrandAnnotations<C>
55
+ options?: BrandAnnotations
61
56
  ) =>
62
57
  <Self extends S.Top>(self: Self): BrandedSchema<Self, C> => {
63
58
  const branded = S.fromBrand(options?.identifier ?? "Brand", constructor as any)(self as any)
@@ -22,8 +22,5 @@ export const Email = S
22
22
  identifier: "Email",
23
23
  description: "an email according to RFC 5322",
24
24
  jsonSchema: { format: "email", minLength: 3, maxLength: 998 }
25
- }),
26
- S.annotate({
27
- toArbitrary: () => (fc) => fc.emailAddress().map((_) => _ as Email)
28
25
  })
29
26
  )
package/src/Schema/ext.ts CHANGED
@@ -54,8 +54,8 @@ type ProvidedCodec<Self extends S.Top, R> = S.Codec<
54
54
 
55
55
  const concurrencySetting = Effect.runSync(
56
56
  Config
57
- .literal("unbounded", "SCHEMA_CONCURRENCY")
58
- .pipe(Config.orElse(() => Config.number("SCHEMA_CONCURRENCY")), Config.option)
57
+ .Literal("unbounded", "SCHEMA_CONCURRENCY")
58
+ .pipe(Config.orElse(() => Config.Number("SCHEMA_CONCURRENCY")), Config.option)
59
59
  )
60
60
 
61
61
  export const DefaultParseOptions: SchemaAST.ParseOptions = {
@@ -611,7 +611,7 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
611
611
  from.pipe(
612
612
  S.decodeTo(
613
613
  to,
614
- SchemaTransformation.transformOrFail({
614
+ SchemaTransformation.transformEffect({
615
615
  decode: (input: any, options: any) => Effect.sync(() => decode(input, options)),
616
616
  encode: (i: any) =>
617
617
  Effect.fail(
@@ -624,7 +624,7 @@ export const transformTo = <To extends S.Top, From extends S.Top>(
624
624
  )
625
625
  )
626
626
 
627
- /** A version of transformOrFail which is only a one way mapping of From->To */
627
+ /** A version of transformEffect which is only a one way mapping of From->To */
628
628
  export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
629
629
  from: From,
630
630
  to: To,
@@ -636,7 +636,7 @@ export const transformToOrFail = <To extends S.Top, From extends S.Top, RD>(
636
636
  from.pipe(
637
637
  S.decodeTo(
638
638
  to,
639
- SchemaTransformation.transformOrFail({
639
+ SchemaTransformation.transformEffect({
640
640
  decode,
641
641
  encode: (i: any) =>
642
642
  Effect.fail(
@@ -15,7 +15,7 @@ import * as Effect from "effect/Effect"
15
15
  import { pipe } from "effect/Function"
16
16
  import * as S from "effect/Schema"
17
17
  import type { Simplify } from "effect/Types"
18
- import { customRandom, nanoid, urlAlphabet } from "nanoid"
18
+ import { nanoid } from "nanoid"
19
19
  import validator from "validator"
20
20
  import type * as SchemaAST from "../SchemaAST.ts"
21
21
  import { type BrandedSchema, fromBrand, nominal } from "./brand.ts"
@@ -164,23 +164,16 @@ export type StringId = string & StringIdBrand
164
164
 
165
165
  const minLength = 6
166
166
  const maxLength = 50
167
- const size = 21
168
- const length = 10 * size
169
167
  const StringIdSchemaBase = pipe(
170
168
  S.String,
171
169
  S.check(S.isMinLength(minLength), S.isMaxLength(maxLength)),
172
170
  fromBrand<StringId>(nominal<StringId>(), {
173
171
  identifier: "StringId",
174
- toArbitrary: () => (fc) => StringIdArb()(fc),
175
172
  jsonSchema: {}
176
173
  })
177
174
  )
178
175
  const makeStringId = (s?: string): StringId =>
179
176
  s !== undefined ? S.decodeSync(StringIdSchemaBase)(s) : nanoid() as unknown as StringId
180
- const StringIdArb = (): S.Arbitrary<StringId> => (fc) =>
181
- fc
182
- .uint8Array({ minLength: length, maxLength: length })
183
- .map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))() as StringId)
184
177
  /**
185
178
  * A string that is at least 6 characters long and a maximum of 50.
186
179
  *
@@ -226,18 +219,11 @@ export function prefixedStringId<Type extends StringId>() {
226
219
  ) => {
227
220
  type FullPrefix = `${Prefix}${Separator}`
228
221
  const pref = `${prefix}${separator ?? "-"}` as FullPrefix
229
- const arb = (): S.Arbitrary<Type> => (fc) =>
230
- StringIdArb()(fc).map(
231
- (x) => (pref + x.substring(0, 50 - pref.length)) as Type
232
- )
233
222
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
234
223
  const s = StringIdSchemaBase
235
224
  .pipe(
236
225
  S.refine((x: string): x is Type => x.startsWith(pref), {
237
226
  identifier: name
238
- }),
239
- S.annotate({
240
- toArbitrary: () => (fc) => arb()(fc)
241
227
  })
242
228
  )
243
229
  const schema = s.pipe(withDefaultMake)
@@ -331,8 +317,5 @@ export const Url: UrlSchema = S
331
317
  identifier: "Url",
332
318
  jsonSchema: { format: "uri" }
333
319
  }),
334
- S.annotate({
335
- toArbitrary: () => (fc) => fc.webUrl().map((_) => _ as Url)
336
- }),
337
320
  withDefaultMake
338
321
  )
@@ -3,7 +3,6 @@ import { isValidPhone } from "effect-app/validation"
3
3
  import * as S from "effect/Schema"
4
4
  import type { Simplify } from "effect/Types"
5
5
  import { withDefaultMake } from "./ext.ts"
6
- import { Numbers } from "./FastCheck.ts"
7
6
  import type { B } from "./schema.ts"
8
7
  import type { NonEmptyStringBrand } from "./strings.ts"
9
8
 
@@ -23,8 +22,5 @@ export const PhoneNumber = S
23
22
  description: "a phone number with at least 7 digits",
24
23
  jsonSchema: { format: "phone" }
25
24
  }),
26
- S.annotate({
27
- toArbitrary: () => (fc) => Numbers(7, 10)(fc).map((_) => _ as PhoneNumber)
28
- }),
29
25
  withDefaultMake
30
26
  )
package/src/Schema.ts CHANGED
@@ -2,10 +2,11 @@ import * as S from "effect/Schema"
2
2
  import { type Simplify } from "effect/Struct"
3
3
  import type * as Tracer from "effect/Tracer"
4
4
  import type { RequiredKeys } from "effect/Types"
5
+ import type { Arbitrary as FastCheckArbitrary } from "fast-check"
5
6
  import type { NonEmptyReadonlyArray } from "./Array.ts"
6
- import { fakerArb } from "./faker.ts"
7
7
  import { Email as EmailT, type Email as EmailType } from "./Schema/email.ts"
8
8
  import { concurrencyUnbounded, withDefaultMake, withDefaultParseOptions } from "./Schema/ext.ts"
9
+ import type { FC } from "./Schema/FastCheck.ts"
9
10
  import { PhoneNumber as PhoneNumberT, type PhoneNumber as PhoneNumberType } from "./Schema/phoneNumber.ts"
10
11
  import { type AST } from "./Schema/schema.ts"
11
12
  import * as SchemaAST from "./SchemaAST.ts"
@@ -129,6 +130,9 @@ export { NonEmptyString } from "./Schema/strings.ts"
129
130
 
130
131
  export * as SchemaIssue from "effect/SchemaIssue"
131
132
 
133
+ /** Fast-check generator factory previously exported as `Schema.Arbitrary`. */
134
+ export type Arbitrary<T> = (fc: FC) => FastCheckArbitrary<T>
135
+
132
136
  export const decodeEffectConcurrently: typeof S.decodeEffect = withDefaultParseOptions(S.decodeEffect)
133
137
  export const decodeUnknownEffectConcurrently: typeof S.decodeUnknownEffect = withDefaultParseOptions(
134
138
  S.decodeUnknownEffect
@@ -347,29 +351,11 @@ export interface WithOptionalSpan {
347
351
  [SpanId]?: Tracer.Span
348
352
  }
349
353
 
350
- const makeEmail = S.decodeSync(EmailT as any) as (value: string) => EmailType
351
- const makePhoneNumber = S.decodeSync(PhoneNumberT as any) as (value: string) => PhoneNumberType
352
-
353
- export const Email = EmailT
354
- .pipe(
355
- S.annotate({
356
- // eslint-disable-next-line @typescript-eslint/unbound-method
357
- toArbitrary: () => (fc) => fakerArb((faker) => faker.internet.exampleEmail)(fc).map(makeEmail)
358
- }),
359
- withDefaultMake
360
- )
354
+ export const Email = EmailT.pipe(withDefaultMake)
361
355
 
362
356
  export type Email = EmailType
363
357
 
364
- export const PhoneNumber = PhoneNumberT
365
- .pipe(
366
- S.annotate({
367
- toArbitrary: () => (fc) =>
368
- // eslint-disable-next-line @typescript-eslint/unbound-method
369
- fakerArb((faker) => faker.phone.number)(fc).map(makePhoneNumber)
370
- }),
371
- withDefaultMake
372
- )
358
+ export const PhoneNumber = PhoneNumberT.pipe(withDefaultMake)
373
359
 
374
360
  export type PhoneNumber = PhoneNumberType
375
361
 
@@ -25,13 +25,8 @@ export interface ApiConfig {
25
25
  }
26
26
 
27
27
  export const DefaultApiConfig = Config.all({
28
- url: Config.string("apiUrl").pipe(Config.withDefault("/api")),
29
- headers: Config
30
- .schema(
31
- Config.Record(Schema.String, Schema.String),
32
- "headers"
33
- )
34
- .pipe(Config.option)
28
+ url: Config.String("apiUrl").pipe(Config.withDefault("/api")),
29
+ headers: Config.Record(Schema.String, Schema.String, "headers").pipe(Config.option)
35
30
  })
36
31
 
37
32
  export type Req = S.Top & {
package/src/runtime.ts CHANGED
@@ -9,8 +9,9 @@ import * as Layer from "./Layer.ts"
9
9
 
10
10
  export const makeAppRuntime = Effect.fnUntraced(function*<A, E>(layer: Layer.Layer<A, E>) {
11
11
  const l = layer.pipe(
12
- Layer.provide(Logger.layer([Logger.consolePretty()]))
13
- ) as Layer.Layer<A>
12
+ Layer.provide(Logger.layer([Logger.consolePretty()])),
13
+ Layer.orDie
14
+ )
14
15
  const mrt = ManagedRuntime.make(l)
15
16
  yield* mrt.contextEffect
16
17
  return Object.assign(mrt, {
@@ -1,5 +1,4 @@
1
1
  import * as S from "effect-app/Schema"
2
- import * as fc from "fast-check"
3
2
  import { urlAlphabet } from "nanoid"
4
3
  import { test } from "vitest"
5
4
 
@@ -7,11 +6,10 @@ const nanoidAlphabet = new Set(urlAlphabet)
7
6
 
8
7
  const isNanoId = (value: string) => value.length === 21 && Array.from(value).every((char) => nanoidAlphabet.has(char))
9
8
 
10
- test("StringId arbitrary generates nanoid-shaped values", () => {
11
- fc.assert(
12
- fc.property(S.toArbitrary(S.StringId)(fc), (value) => {
13
- expect(isNanoId(value)).toBe(true)
14
- expect(S.is(S.StringId)(value)).toBe(true)
15
- })
16
- )
9
+ test("StringId make generates nanoid-shaped values", () => {
10
+ for (let i = 0; i < 20; i++) {
11
+ const value = S.StringId.make()
12
+ expect(isNanoId(value)).toBe(true)
13
+ expect(S.is(S.StringId)(value)).toBe(true)
14
+ }
17
15
  })