effect-app 1.32.0 → 2.0.1
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/CHANGELOG.md +12 -0
- package/_cjs/Array.cjs +132 -0
- package/_cjs/Array.cjs.map +1 -0
- package/_cjs/Chunk.cjs +128 -0
- package/_cjs/Chunk.cjs.map +1 -0
- package/_cjs/Config/SecretURL.cjs +3 -3
- package/_cjs/Config/SecretURL.cjs.map +1 -1
- package/_cjs/Config/internal/configSecretURL.cjs +2 -2
- package/_cjs/Config/internal/configSecretURL.cjs.map +1 -1
- package/_cjs/Context.cjs +243 -0
- package/_cjs/Context.cjs.map +1 -0
- package/_cjs/Dictionary.cjs +522 -0
- package/_cjs/Dictionary.cjs.map +1 -0
- package/_cjs/Effect.cjs +202 -0
- package/_cjs/Effect.cjs.map +1 -0
- package/_cjs/Function.cjs +107 -0
- package/_cjs/Function.cjs.map +1 -0
- package/_cjs/NonEmptySet.cjs +26 -0
- package/_cjs/NonEmptySet.cjs.map +1 -0
- package/_cjs/NonEmptySetAspects.cjs +17 -0
- package/_cjs/NonEmptySetAspects.cjs.map +1 -0
- package/_cjs/NonEmptySetBase.cjs +82 -0
- package/_cjs/NonEmptySetBase.cjs.map +1 -0
- package/_cjs/Object.cjs +24 -0
- package/_cjs/Object.cjs.map +1 -0
- package/_cjs/Operations.cjs +1 -1
- package/_cjs/Option.cjs +88 -0
- package/_cjs/Option.cjs.map +1 -0
- package/_cjs/Prelude.cjs +53 -0
- package/_cjs/Prelude.cjs.map +1 -0
- package/_cjs/Prelude.code.cjs +53 -0
- package/_cjs/Prelude.code.cjs.map +1 -0
- package/_cjs/Pure.cjs +39 -36
- package/_cjs/Pure.cjs.map +1 -1
- package/_cjs/Schema/Class.cjs +61 -0
- package/_cjs/Schema/Class.cjs.map +1 -0
- package/_cjs/Schema/FastCheck.cjs +39 -0
- package/_cjs/Schema/FastCheck.cjs.map +1 -0
- package/_cjs/Schema/Methods.cjs +6 -0
- package/_cjs/Schema/Methods.cjs.map +1 -0
- package/_cjs/Schema/brand.cjs +17 -0
- package/_cjs/Schema/brand.cjs.map +1 -0
- package/_cjs/Schema/email.cjs +20 -0
- package/_cjs/Schema/email.cjs.map +1 -0
- package/_cjs/Schema/ext.cjs +174 -0
- package/_cjs/Schema/ext.cjs.map +1 -0
- package/_cjs/Schema/moreStrings.cjs +124 -0
- package/_cjs/Schema/moreStrings.cjs.map +1 -0
- package/_cjs/Schema/numbers.cjs +52 -0
- package/_cjs/Schema/numbers.cjs.map +1 -0
- package/_cjs/Schema/phoneNumber.cjs +22 -0
- package/_cjs/Schema/phoneNumber.cjs.map +1 -0
- package/_cjs/Schema/schema.cjs +15 -0
- package/_cjs/Schema/schema.cjs.map +1 -0
- package/_cjs/Schema/strings.cjs +32 -0
- package/_cjs/Schema/strings.cjs.map +1 -0
- package/_cjs/Schema.cjs +334 -0
- package/_cjs/Schema.cjs.map +1 -0
- package/_cjs/Set.cjs +567 -0
- package/_cjs/Set.cjs.map +1 -0
- package/_cjs/Struct.cjs +51 -0
- package/_cjs/Struct.cjs.map +1 -0
- package/_cjs/Tag.cjs +25 -0
- package/_cjs/Tag.cjs.map +1 -0
- package/_cjs/Unify.cjs +40 -0
- package/_cjs/Unify.cjs.map +1 -0
- package/_cjs/_ext/misc.cjs +16 -16
- package/_cjs/_ext/misc.cjs.map +1 -1
- package/_cjs/_ext/ord.ext.cjs +52 -0
- package/_cjs/_ext/ord.ext.cjs.map +1 -0
- package/_cjs/builtin.cjs +6 -0
- package/_cjs/builtin.cjs.map +1 -0
- package/_cjs/client/clientFor.cjs +5 -3
- package/_cjs/client/clientFor.cjs.map +1 -1
- package/_cjs/client/config.cjs +4 -4
- package/_cjs/client/config.cjs.map +1 -1
- package/_cjs/client/errors.cjs +9 -9
- package/_cjs/client/errors.cjs.map +1 -1
- package/_cjs/http/http-client.cjs +35 -0
- package/_cjs/http/http-client.cjs.map +1 -0
- package/_cjs/ids.cjs +5 -5
- package/_cjs/ids.cjs.map +1 -1
- package/_cjs/index.cjs +1 -1
- package/_cjs/lib.cjs +178 -9
- package/_cjs/lib.cjs.map +1 -1
- package/_cjs/logger.cjs +1 -1
- package/_cjs/transform.cjs +45 -0
- package/_cjs/transform.cjs.map +1 -0
- package/_cjs/utils/effectify.cjs +25 -0
- package/_cjs/utils/effectify.cjs.map +1 -0
- package/_cjs/utils/extend.cjs +17 -0
- package/_cjs/utils/extend.cjs.map +1 -0
- package/_cjs/utils/logger.cjs +23 -0
- package/_cjs/utils/logger.cjs.map +1 -0
- package/_cjs/utils.cjs +431 -4
- package/_cjs/utils.cjs.map +1 -1
- package/_cjs/validation/validators.cjs +32 -0
- package/_cjs/validation/validators.cjs.map +1 -0
- package/_cjs/validation.cjs +45 -0
- package/_cjs/validation.cjs.map +1 -0
- package/dist/Array.d.ts +64 -0
- package/dist/Array.d.ts.map +1 -0
- package/dist/Array.js +89 -0
- package/dist/Chunk.d.ts +68 -0
- package/dist/Chunk.d.ts.map +1 -0
- package/dist/Chunk.js +93 -0
- package/dist/Config/SecretURL.d.ts +3 -3
- package/dist/Config/SecretURL.d.ts.map +1 -1
- package/dist/Config/SecretURL.js +2 -2
- package/dist/Config/internal/configSecretURL.js +2 -2
- package/dist/Context.d.ts +55 -0
- package/dist/Context.d.ts.map +1 -0
- package/dist/Context.js +204 -0
- package/dist/Dictionary.d.ts +260 -0
- package/dist/Dictionary.d.ts.map +1 -0
- package/dist/Dictionary.js +448 -0
- package/dist/Effect.d.ts +115 -0
- package/dist/Effect.d.ts.map +1 -0
- package/dist/Effect.js +153 -0
- package/dist/Function.d.ts +72 -0
- package/dist/Function.d.ts.map +1 -0
- package/dist/Function.js +71 -0
- package/dist/Inputify.type.d.ts +1 -1
- package/dist/Inputify.type.d.ts.map +1 -1
- package/dist/NonEmptySet.d.ts +3 -0
- package/dist/NonEmptySet.d.ts.map +1 -0
- package/dist/NonEmptySet.js +3 -0
- package/dist/NonEmptySetAspects.d.ts +2 -0
- package/dist/NonEmptySetAspects.d.ts.map +1 -0
- package/dist/NonEmptySetAspects.js +2 -0
- package/dist/NonEmptySetBase.d.ts +39 -0
- package/dist/NonEmptySetBase.d.ts.map +1 -0
- package/dist/NonEmptySetBase.js +63 -0
- package/dist/Object.d.ts +7 -0
- package/dist/Object.d.ts.map +1 -0
- package/dist/Object.js +7 -0
- package/dist/Operations.d.ts +2 -2
- package/dist/Operations.js +1 -1
- package/dist/Option.d.ts +39 -0
- package/dist/Option.d.ts.map +1 -0
- package/dist/Option.js +55 -0
- package/dist/Prelude.code.d.ts +25 -0
- package/dist/Prelude.code.d.ts.map +1 -0
- package/dist/Prelude.code.js +24 -0
- package/dist/Prelude.d.ts +163 -0
- package/dist/Prelude.d.ts.map +1 -0
- package/dist/Prelude.js +24 -0
- package/dist/Pure.d.ts +13 -12
- package/dist/Pure.d.ts.map +1 -1
- package/dist/Pure.js +18 -17
- package/dist/Schema/Class.d.ts +45 -0
- package/dist/Schema/Class.d.ts.map +1 -0
- package/dist/Schema/Class.js +50 -0
- package/dist/Schema/FastCheck.d.ts +10 -0
- package/dist/Schema/FastCheck.d.ts.map +1 -0
- package/dist/Schema/FastCheck.js +12 -0
- package/dist/Schema/Methods.d.ts +6 -0
- package/dist/Schema/Methods.d.ts.map +1 -0
- package/dist/Schema/Methods.js +2 -0
- package/dist/Schema/brand.d.ts +35 -0
- package/dist/Schema/brand.d.ts.map +1 -0
- package/dist/Schema/brand.js +7 -0
- package/dist/Schema/email.d.ts +9 -0
- package/dist/Schema/email.d.ts.map +1 -0
- package/dist/Schema/email.js +12 -0
- package/dist/Schema/ext.d.ts +89 -0
- package/dist/Schema/ext.d.ts.map +1 -0
- package/dist/Schema/ext.js +111 -0
- package/dist/Schema/moreStrings.d.ts +109 -0
- package/dist/Schema/moreStrings.d.ts.map +1 -0
- package/dist/Schema/moreStrings.js +108 -0
- package/dist/Schema/numbers.d.ts +46 -0
- package/dist/Schema/numbers.d.ts.map +1 -0
- package/dist/Schema/numbers.js +28 -0
- package/dist/Schema/phoneNumber.d.ts +9 -0
- package/dist/Schema/phoneNumber.d.ts.map +1 -0
- package/dist/Schema/phoneNumber.js +14 -0
- package/dist/Schema/schema.d.ts +6 -0
- package/dist/Schema/schema.d.ts.map +1 -0
- package/dist/Schema/schema.js +5 -0
- package/dist/Schema/strings.d.ts +19 -0
- package/dist/Schema/strings.d.ts.map +1 -0
- package/dist/Schema/strings.js +32 -0
- package/dist/{schema.d.ts → Schema.d.ts} +25 -9
- package/dist/Schema.d.ts.map +1 -0
- package/dist/Schema.js +89 -0
- package/dist/Set.d.ts +221 -0
- package/dist/Set.d.ts.map +1 -0
- package/dist/Set.js +516 -0
- package/dist/Struct.d.ts +44 -0
- package/dist/Struct.d.ts.map +1 -0
- package/dist/Struct.js +29 -0
- package/dist/Tag.d.ts +9 -0
- package/dist/Tag.d.ts.map +1 -0
- package/dist/Tag.js +18 -0
- package/dist/Unify.d.ts +43 -0
- package/dist/Unify.d.ts.map +1 -0
- package/dist/Unify.js +31 -0
- package/dist/Widen.type.d.ts +1 -1
- package/dist/Widen.type.d.ts.map +1 -1
- package/dist/_ext/misc.d.ts +11 -11
- package/dist/_ext/misc.d.ts.map +1 -1
- package/dist/_ext/misc.js +8 -8
- package/dist/_ext/ord.ext.d.ts +23 -0
- package/dist/_ext/ord.ext.d.ts.map +1 -0
- package/dist/_ext/ord.ext.js +44 -0
- package/dist/builtin.d.ts +51 -0
- package/dist/builtin.d.ts.map +1 -0
- package/dist/builtin.js +2 -0
- package/dist/client/clientFor.d.ts +1 -1
- package/dist/client/clientFor.d.ts.map +1 -1
- package/dist/client/clientFor.js +2 -2
- package/dist/client/config.d.ts +1 -1
- package/dist/client/config.d.ts.map +1 -1
- package/dist/client/config.js +2 -2
- package/dist/client/errors.js +1 -1
- package/dist/http/http-client.d.ts +43 -0
- package/dist/http/http-client.d.ts.map +1 -0
- package/dist/http/http-client.js +30 -0
- package/dist/ids.d.ts +3 -3
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/lib.d.ts +22 -4
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +20 -5
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -2
- package/dist/transform.d.ts +12 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +39 -0
- package/dist/utils/effectify.d.ts +210 -0
- package/dist/utils/effectify.d.ts.map +1 -0
- package/dist/utils/effectify.js +19 -0
- package/dist/utils/extend.d.ts +3 -0
- package/dist/utils/extend.d.ts.map +1 -0
- package/dist/utils/extend.js +9 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +15 -0
- package/dist/utils.d.ts +192 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +332 -2
- package/dist/validation/validators.d.ts +11 -0
- package/dist/validation/validators.d.ts.map +1 -0
- package/dist/validation/validators.js +26 -0
- package/dist/validation.d.ts +13 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +14 -0
- package/package.json +360 -9
- package/src/Array.ts +127 -0
- package/src/Chunk.ts +112 -0
- package/src/Config/SecretURL.ts +3 -3
- package/src/Config/internal/configSecretURL.ts +1 -1
- package/src/Context.ts +293 -0
- package/src/Dictionary.ts +669 -0
- package/src/Effect.ts +265 -0
- package/src/Function.ts +175 -0
- package/src/Inputify.type.ts +1 -1
- package/src/NonEmptySet.ts +3 -0
- package/src/NonEmptySetAspects.ts +1 -0
- package/src/NonEmptySetBase.ts +109 -0
- package/src/Object.ts +19 -0
- package/src/Operations.ts +1 -1
- package/src/Option.ts +82 -0
- package/src/Prelude.code.ts +27 -0
- package/src/Prelude.ts +220 -0
- package/src/Pure.ts +37 -30
- package/src/Schema/Class.ts +225 -0
- package/src/Schema/FastCheck.ts +23 -0
- package/src/Schema/Methods.ts +8 -0
- package/src/Schema/brand.ts +50 -0
- package/src/Schema/email.ts +22 -0
- package/src/Schema/ext.ts +273 -0
- package/src/Schema/moreStrings.ts +260 -0
- package/src/Schema/numbers.ts +83 -0
- package/src/Schema/phoneNumber.ts +24 -0
- package/src/Schema/schema.ts +6 -0
- package/src/Schema/strings.ts +60 -0
- package/src/{schema.ts → Schema.ts} +52 -20
- package/src/Set.ts +674 -0
- package/src/Struct.ts +54 -0
- package/src/Tag.ts +20 -0
- package/src/Unify.ts +56 -0
- package/src/Widen.type.ts +1 -1
- package/src/_ext/misc.ts +22 -22
- package/src/_ext/ord.ext.ts +49 -0
- package/src/builtin.ts +64 -0
- package/src/client/clientFor.ts +1 -1
- package/src/client/config.ts +1 -1
- package/src/client/errors.ts +1 -1
- package/src/http/http-client.ts +73 -0
- package/src/ids.ts +3 -3
- package/src/index.ts +1 -1
- package/src/lib.ts +88 -4
- package/src/logger.ts +1 -1
- package/src/transform.ts +80 -0
- package/src/utils/effectify.ts +259 -0
- package/src/utils/extend.ts +9 -0
- package/src/utils/logger.ts +24 -0
- package/src/utils.ts +805 -1
- package/src/validation/validators.ts +32 -0
- package/src/validation.ts +18 -0
- package/tsconfig.src.json +1 -5
- package/_cjs/schema.cjs +0 -110
- package/_cjs/schema.cjs.map +0 -1
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js +0 -73
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type * as fc from "fast-check"
|
|
2
|
+
|
|
3
|
+
export type { Arbitrary } from "fast-check"
|
|
4
|
+
type FC = typeof fc
|
|
5
|
+
|
|
6
|
+
export type { FC }
|
|
7
|
+
|
|
8
|
+
export const ALPHABET = "abcdefghijklmnopqrstuvwxyz"
|
|
9
|
+
export const ALPHABET_UC = ALPHABET.toUpperCase()
|
|
10
|
+
|
|
11
|
+
// uppercase A-Z
|
|
12
|
+
const UC_char = (fc: Pick<FC, "integer">) => fc.integer({ min: 0x41, max: 0x5a }).map(String.fromCharCode)
|
|
13
|
+
export const UC_alphabet = (min: number, max: number) => (fc: FC) =>
|
|
14
|
+
fc.array(UC_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join(""))
|
|
15
|
+
// lowercase A-Z
|
|
16
|
+
const LC_char = (fc: Pick<FC, "integer">) => fc.integer({ min: 0x61, max: 0x7a }).map(String.fromCharCode)
|
|
17
|
+
export const LC_alphabet = (min: number, max: number) => (fc: Pick<FC, "array" | "integer">) =>
|
|
18
|
+
fc.array(LC_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join(""))
|
|
19
|
+
|
|
20
|
+
// numbers 0-9
|
|
21
|
+
const N_char = (fc: Pick<FC, "integer">) => fc.integer({ min: 0x30, max: 0x39 }).map(String.fromCharCode)
|
|
22
|
+
export const Numbers = (min: number, max: number) => (fc: Pick<FC, "array" | "integer">) =>
|
|
23
|
+
fc.array(N_char(fc), { minLength: min, maxLength: max }).map((arr) => arr.join(""))
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
|
+
import type { Option } from "effect"
|
|
4
|
+
import * as B from "effect/Brand"
|
|
5
|
+
import type * as Brand from "effect/Brand"
|
|
6
|
+
import type * as Either from "effect/Either"
|
|
7
|
+
import * as S from "effect/Schema"
|
|
8
|
+
|
|
9
|
+
export interface Constructor<in out A extends B.Brand<any>> {
|
|
10
|
+
readonly [B.RefinedConstructorsTypeId]: B.RefinedConstructorsTypeId
|
|
11
|
+
/**
|
|
12
|
+
* Constructs a branded type from a value of type `A`, throwing an error if
|
|
13
|
+
* the provided `A` is not valid.
|
|
14
|
+
*/
|
|
15
|
+
(args: Unbranded<A>): A
|
|
16
|
+
/**
|
|
17
|
+
* Constructs a branded type from a value of type `A`, returning `Some<A>`
|
|
18
|
+
* if the provided `A` is valid, `None` otherwise.
|
|
19
|
+
*/
|
|
20
|
+
option(args: Unbranded<A>): Option.Option<A>
|
|
21
|
+
/**
|
|
22
|
+
* Constructs a branded type from a value of type `A`, returning `Right<A>`
|
|
23
|
+
* if the provided `A` is valid, `Left<BrandError>` otherwise.
|
|
24
|
+
*/
|
|
25
|
+
either(args: Unbranded<A>): Either.Either<A, Brand.Brand.BrandErrors>
|
|
26
|
+
/**
|
|
27
|
+
* Attempts to refine the provided value of type `A`, returning `true` if
|
|
28
|
+
* the provided `A` is valid, `false` otherwise.
|
|
29
|
+
*/
|
|
30
|
+
is(a: Unbranded<A>): a is Unbranded<A> & A
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const fromBrand = <C extends Brand.Brand<string | symbol>>(
|
|
34
|
+
constructor: Constructor<C>,
|
|
35
|
+
options?: S.Annotations.Filter<Unbranded<C>>
|
|
36
|
+
) =>
|
|
37
|
+
<R, I, A extends Unbranded<C>>(self: S.Schema<A, I, R>): S.Schema<A & C, I, R> => {
|
|
38
|
+
return S.fromBrand(constructor as any, options as any)(self as any) as any
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type Brands<P> = P extends B.Brand<any> ? { readonly [B.BrandTypeId]: P[B.BrandTypeId] }
|
|
42
|
+
: never
|
|
43
|
+
|
|
44
|
+
export type Unbranded<P> = P extends infer Q & Brands<P> ? Q : P
|
|
45
|
+
|
|
46
|
+
export const nominal: <A extends B.Brand<any>>() => Constructor<A> = <
|
|
47
|
+
A extends B.Brand<any>
|
|
48
|
+
>(): Constructor<
|
|
49
|
+
A
|
|
50
|
+
> => B.nominal<A>() as any
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Refinement } from "effect-app/Function"
|
|
2
|
+
import { isValidEmail } from "effect-app/validation"
|
|
3
|
+
import * as S from "effect/Schema"
|
|
4
|
+
import type { Simplify } from "effect/Types"
|
|
5
|
+
import type { B } from "./schema.js"
|
|
6
|
+
import type { NonEmptyStringBrand } from "./strings.js"
|
|
7
|
+
|
|
8
|
+
export interface EmailBrand extends Simplify<NonEmptyStringBrand & B.Brand<"Email">> {}
|
|
9
|
+
|
|
10
|
+
export type Email = string & EmailBrand
|
|
11
|
+
|
|
12
|
+
export const Email = S
|
|
13
|
+
.String
|
|
14
|
+
.pipe(
|
|
15
|
+
S.filter(isValidEmail as Refinement<string, Email>, {
|
|
16
|
+
identifier: "Email",
|
|
17
|
+
title: "Email",
|
|
18
|
+
description: "an email according to RFC 5322",
|
|
19
|
+
jsonSchema: { format: "email" },
|
|
20
|
+
arbitrary: () => (fc) => fc.emailAddress().map((_) => _ as Email)
|
|
21
|
+
})
|
|
22
|
+
)
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
3
|
+
import { Effect, pipe } from "effect"
|
|
4
|
+
import type { ParseResult, SchemaAST } from "effect"
|
|
5
|
+
import type { NonEmptyReadonlyArray } from "effect/Array"
|
|
6
|
+
import type { Schema } from "effect/Schema"
|
|
7
|
+
import * as S from "effect/Schema"
|
|
8
|
+
import { extendM, typedKeysOf } from "../utils.js"
|
|
9
|
+
|
|
10
|
+
export const withDefaultConstructor: <A, I, R>(
|
|
11
|
+
makeDefault: () => NoInfer<A>
|
|
12
|
+
) => (self: Schema<A, I, R>) => S.PropertySignature<":", A, never, ":", I, true, R> = (makeDefault) => (self) =>
|
|
13
|
+
S.propertySignature(self).pipe(S.withConstructorDefault(makeDefault))
|
|
14
|
+
|
|
15
|
+
export const Date = Object.assign(S.Date, {
|
|
16
|
+
withDefault: S.Date.pipe(withDefaultConstructor(() => new global.Date()))
|
|
17
|
+
})
|
|
18
|
+
export const Boolean = Object.assign(S.Boolean, {
|
|
19
|
+
withDefault: S.Boolean.pipe(withDefaultConstructor(() => false))
|
|
20
|
+
})
|
|
21
|
+
export const Number = Object.assign(S.Number, { withDefault: S.Number.pipe(withDefaultConstructor(() => 0)) })
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Like the default Schema `Struct` but with batching enabled by default
|
|
25
|
+
*/
|
|
26
|
+
export function Struct<Fields extends S.Struct.Fields, const Records extends S.IndexSignature.NonEmptyRecords>(
|
|
27
|
+
fields: Fields,
|
|
28
|
+
...records: Records
|
|
29
|
+
): S.TypeLiteral<Fields, Records>
|
|
30
|
+
export function Struct<Fields extends S.Struct.Fields>(fields: Fields): S.Struct<Fields>
|
|
31
|
+
export function Struct<Fields extends S.Struct.Fields, const Records extends S.IndexSignature.Records>(
|
|
32
|
+
fields: Fields,
|
|
33
|
+
...records: Records
|
|
34
|
+
): S.TypeLiteral<Fields, Records> {
|
|
35
|
+
return S.Struct(fields, ...records as any).pipe(S.annotations({ batching: true }))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export declare namespace Struct {
|
|
39
|
+
export type Fields = S.Struct.Fields
|
|
40
|
+
export type Type<F extends Fields> = S.Struct.Type<F>
|
|
41
|
+
export type Encoded<F extends Fields> = S.Struct.Encoded<F>
|
|
42
|
+
export type Context<F extends Fields> = Schema.Context<F[keyof F]>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Like the default Schema `tuple` but with batching enabled by default
|
|
47
|
+
*/
|
|
48
|
+
export function Tuple<
|
|
49
|
+
const Elements extends S.TupleType.Elements,
|
|
50
|
+
Rest extends NonEmptyReadonlyArray<Schema.Any>
|
|
51
|
+
>(elements: Elements, ...rest: Rest): S.TupleType<Elements, Rest>
|
|
52
|
+
export function Tuple<Elements extends S.TupleType.Elements>(...elements: Elements): S.Tuple<Elements>
|
|
53
|
+
export function Tuple(...args: ReadonlyArray<any>): any {
|
|
54
|
+
return S.Tuple(...args).pipe(S.annotations({ batching: true }))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Like the default Schema `NonEmptyArray` but with batching enabled by default
|
|
59
|
+
*/
|
|
60
|
+
export function NonEmptyArray<Value extends Schema.Any>(value: Value): S.NonEmptyArray<Value> {
|
|
61
|
+
return pipe(
|
|
62
|
+
S.NonEmptyArray(value),
|
|
63
|
+
S.annotations({ batching: true })
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Like the default Schema `Array` but with `withDefault` and batching enabled by default
|
|
69
|
+
*/
|
|
70
|
+
export function Array<Value extends Schema.Any>(value: Value) {
|
|
71
|
+
return pipe(
|
|
72
|
+
S.Array(value),
|
|
73
|
+
S.annotations({ batching: true }),
|
|
74
|
+
(s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => [])) })
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Like the default Schema `ReadonlySet` but with `withDefault` and batching enabled by default
|
|
80
|
+
*/
|
|
81
|
+
export const ReadonlySet = <Value extends Schema.Any>(value: Value) =>
|
|
82
|
+
pipe(
|
|
83
|
+
S.ReadonlySet(value),
|
|
84
|
+
S.annotations({ batching: true }),
|
|
85
|
+
(s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new Set<S.Schema.Type<Value>>())) })
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Like the default Schema `ReadonlyMap` but with `withDefault` and batching enabled by default
|
|
90
|
+
*/
|
|
91
|
+
export const ReadonlyMap = <K extends Schema.Any, V extends Schema.Any>(pair: {
|
|
92
|
+
readonly key: K
|
|
93
|
+
readonly value: V
|
|
94
|
+
}) =>
|
|
95
|
+
pipe(
|
|
96
|
+
S.ReadonlyMap(pair),
|
|
97
|
+
S.annotations({ batching: true }),
|
|
98
|
+
(s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => new Map())) })
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Like the default Schema `NullOr` but with `withDefault`
|
|
103
|
+
*/
|
|
104
|
+
export const NullOr = <S extends Schema.Any>(self: S) =>
|
|
105
|
+
pipe(
|
|
106
|
+
S.NullOr(self),
|
|
107
|
+
(s) => Object.assign(s, { withDefault: s.pipe(withDefaultConstructor(() => null)) })
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
export const defaultDate = <I, R>(s: Schema<Date, I, R>) => s.pipe(withDefaultConstructor(() => new global.Date()))
|
|
111
|
+
|
|
112
|
+
export const defaultBool = <I, R>(s: Schema<boolean, I, R>) => s.pipe(withDefaultConstructor(() => false))
|
|
113
|
+
|
|
114
|
+
export const defaultNullable = <A, I, R>(
|
|
115
|
+
s: Schema<A | null, I, R>
|
|
116
|
+
) => s.pipe(withDefaultConstructor(() => null))
|
|
117
|
+
|
|
118
|
+
export const defaultArray = <A, I, R>(s: Schema<ReadonlyArray<A>, I, R>) => s.pipe(withDefaultConstructor(() => []))
|
|
119
|
+
|
|
120
|
+
export const defaultMap = <A, A2, I, R>(s: Schema<ReadonlyMap<A, A2>, I, R>) =>
|
|
121
|
+
s.pipe(withDefaultConstructor(() => new Map()))
|
|
122
|
+
|
|
123
|
+
export const defaultSet = <A, I, R>(s: Schema<ReadonlySet<A>, I, R>) =>
|
|
124
|
+
s.pipe(withDefaultConstructor(() => new Set<A>()))
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @tsplus getter effect/schema/Schema withDefaultMake
|
|
128
|
+
*/
|
|
129
|
+
export const withDefaultMake = <Self extends S.Schema<any, any, never>>(s: Self) => {
|
|
130
|
+
const a = Object.assign(S.decodeSync(s) as WithDefaults<Self>, s)
|
|
131
|
+
Object.setPrototypeOf(a, s)
|
|
132
|
+
return a
|
|
133
|
+
|
|
134
|
+
// return s as Self & WithDefaults<Self>
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type WithDefaults<Self extends S.Schema<any, any, never>> = (
|
|
138
|
+
i: S.Schema.Encoded<Self>,
|
|
139
|
+
options?: SchemaAST.ParseOptions
|
|
140
|
+
) => S.Schema.Type<Self>
|
|
141
|
+
|
|
142
|
+
// type GetKeys<U> = U extends Record<infer K, any> ? K : never
|
|
143
|
+
// type UnionToIntersection2<U extends object> = {
|
|
144
|
+
// readonly [K in GetKeys<U>]: U extends Record<K, infer T> ? T : never
|
|
145
|
+
// }
|
|
146
|
+
|
|
147
|
+
// export type Test<P extends B.Brand<any>> = {
|
|
148
|
+
// [K in keyof P[B.BrandTypeId]]: K extends string | symbol ? {
|
|
149
|
+
// readonly [k in K]: k
|
|
150
|
+
// }
|
|
151
|
+
// : never
|
|
152
|
+
// }[keyof P[B.BrandTypeId]]
|
|
153
|
+
// export type UnionToIntersection3<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I
|
|
154
|
+
// : never
|
|
155
|
+
|
|
156
|
+
export const inputDate = extendM(
|
|
157
|
+
S.Union(S.ValidDateFromSelf, S.Date),
|
|
158
|
+
(s) => ({ withDefault: s.pipe(withDefaultConstructor(() => new globalThis.Date())) })
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
export interface UnionBrand {}
|
|
162
|
+
|
|
163
|
+
const makeOpt = (self: S.PropertySignature.Any, exact?: boolean) => {
|
|
164
|
+
const ast = self.ast
|
|
165
|
+
switch (ast._tag) {
|
|
166
|
+
case "PropertySignatureDeclaration": {
|
|
167
|
+
return S.makePropertySignature(
|
|
168
|
+
new S.PropertySignatureDeclaration(
|
|
169
|
+
exact ? ast.type : S.UndefinedOr(S.make(ast.type)).ast,
|
|
170
|
+
true,
|
|
171
|
+
ast.isReadonly,
|
|
172
|
+
ast.annotations,
|
|
173
|
+
ast.defaultValue
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
case "PropertySignatureTransformation": {
|
|
178
|
+
return S.makePropertySignature(
|
|
179
|
+
new S.PropertySignatureTransformation(
|
|
180
|
+
new S.FromPropertySignature(
|
|
181
|
+
exact ? ast.from.type : S.UndefinedOr(S.make(ast.from.type)).ast,
|
|
182
|
+
true,
|
|
183
|
+
ast.from.isReadonly,
|
|
184
|
+
ast.from.annotations
|
|
185
|
+
),
|
|
186
|
+
new S.ToPropertySignature(
|
|
187
|
+
exact ? ast.to.type : S.UndefinedOr(S.make(ast.to.type)).ast,
|
|
188
|
+
true,
|
|
189
|
+
ast.to.isReadonly,
|
|
190
|
+
ast.to.annotations,
|
|
191
|
+
ast.to.defaultValue
|
|
192
|
+
),
|
|
193
|
+
ast.decode,
|
|
194
|
+
ast.encode
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function makeOptional<NER extends S.Struct.Fields | S.PropertySignature.Any>(
|
|
202
|
+
t: NER // TODO: enforce non empty
|
|
203
|
+
): {
|
|
204
|
+
[K in keyof NER]: S.PropertySignature<
|
|
205
|
+
"?:",
|
|
206
|
+
Schema.Type<NER[K]> | undefined,
|
|
207
|
+
never,
|
|
208
|
+
"?:",
|
|
209
|
+
Schema.Encoded<NER[K]> | undefined,
|
|
210
|
+
NER[K] extends S.PropertySignature<any, any, any, any, any, infer Z, any> ? Z : false,
|
|
211
|
+
Schema.Context<NER[K]>
|
|
212
|
+
>
|
|
213
|
+
} {
|
|
214
|
+
return typedKeysOf(t).reduce((prev, cur) => {
|
|
215
|
+
if (S.isSchema(t[cur])) {
|
|
216
|
+
prev[cur] = S.optional(t[cur] as any)
|
|
217
|
+
} else {
|
|
218
|
+
prev[cur] = makeOpt(t[cur] as any)
|
|
219
|
+
}
|
|
220
|
+
return prev
|
|
221
|
+
}, {} as any)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function makeExactOptional<NER extends S.Struct.Fields>(
|
|
225
|
+
t: NER // TODO: enforce non empty
|
|
226
|
+
): {
|
|
227
|
+
[K in keyof NER]: S.PropertySignature<
|
|
228
|
+
"?:",
|
|
229
|
+
Schema.Type<NER[K]>,
|
|
230
|
+
never,
|
|
231
|
+
"?:",
|
|
232
|
+
Schema.Encoded<NER[K]>,
|
|
233
|
+
NER[K] extends S.PropertySignature<any, any, any, any, any, infer Z, any> ? Z : false,
|
|
234
|
+
Schema.Context<NER[K]>
|
|
235
|
+
>
|
|
236
|
+
} {
|
|
237
|
+
return typedKeysOf(t).reduce((prev, cur) => {
|
|
238
|
+
if (S.isSchema(t[cur])) {
|
|
239
|
+
prev[cur] = S.optionalWith(t[cur] as any, { exact: true })
|
|
240
|
+
} else {
|
|
241
|
+
prev[cur] = makeOpt(t[cur] as any)
|
|
242
|
+
}
|
|
243
|
+
return prev
|
|
244
|
+
}, {} as any)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** A version of transform which is only a one way mapping of From->To */
|
|
248
|
+
export const transformTo = <To extends Schema.Any, From extends Schema.Any>(
|
|
249
|
+
from: From,
|
|
250
|
+
to: To,
|
|
251
|
+
decode: (
|
|
252
|
+
fromA: Schema.Type<From>,
|
|
253
|
+
options: SchemaAST.ParseOptions,
|
|
254
|
+
ast: SchemaAST.Transformation,
|
|
255
|
+
fromI: Schema.Encoded<From>
|
|
256
|
+
) => Schema.Encoded<To>
|
|
257
|
+
) =>
|
|
258
|
+
S.transformOrFail<To, From, never, never>(
|
|
259
|
+
from,
|
|
260
|
+
to,
|
|
261
|
+
{ decode: (...args) => Effect.sync(() => decode(...args)), encode: () => Effect.die("one way schema") }
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
/** A version of transformOrFail which is only a one way mapping of From->To */
|
|
265
|
+
export const transformToOrFail = <To extends Schema.Any, From extends Schema.Any, RD>(
|
|
266
|
+
from: From,
|
|
267
|
+
to: To,
|
|
268
|
+
decode: (
|
|
269
|
+
fromA: Schema.Type<From>,
|
|
270
|
+
options: SchemaAST.ParseOptions,
|
|
271
|
+
ast: SchemaAST.Transformation
|
|
272
|
+
) => Effect.Effect<Schema.Encoded<To>, ParseResult.ParseIssue, RD>
|
|
273
|
+
) => S.transformOrFail<To, From, RD, never>(from, to, { decode, encode: () => Effect.die("one way schema") })
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { pipe } from "effect"
|
|
2
|
+
import type { Refinement } from "effect-app/Function"
|
|
3
|
+
import { extendM } from "effect-app/utils"
|
|
4
|
+
import type { LazyArbitrary } from "effect/Arbitrary"
|
|
5
|
+
import * as S from "effect/Schema"
|
|
6
|
+
import type { Simplify } from "effect/Types"
|
|
7
|
+
import { customRandom, nanoid, urlAlphabet } from "nanoid"
|
|
8
|
+
import validator from "validator"
|
|
9
|
+
import { fromBrand, nominal } from "./brand.js"
|
|
10
|
+
import type { WithDefaults } from "./ext.js"
|
|
11
|
+
import { withDefaultConstructor, withDefaultMake } from "./ext.js"
|
|
12
|
+
import { type B } from "./schema.js"
|
|
13
|
+
import type { NonEmptyString255Brand, NonEmptyStringBrand } from "./strings.js"
|
|
14
|
+
|
|
15
|
+
const nonEmptyString = S.String.pipe(S.nonEmptyString())
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A string that is at least 1 character long and a maximum of 50.
|
|
19
|
+
*/
|
|
20
|
+
export interface NonEmptyString50Brand extends Simplify<B.Brand<"NonEmptyString50"> & NonEmptyString64Brand> {}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A string that is at least 1 character long and a maximum of 50.
|
|
24
|
+
*/
|
|
25
|
+
export type NonEmptyString50 = string & NonEmptyString50Brand
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A string that is at least 1 character long and a maximum of 50.
|
|
29
|
+
*/
|
|
30
|
+
export const NonEmptyString50 = nonEmptyString.pipe(
|
|
31
|
+
S.maxLength(50),
|
|
32
|
+
fromBrand(nominal<NonEmptyString50>(), {
|
|
33
|
+
identifier: "NonEmptyString50",
|
|
34
|
+
title: "NonEmptyString50",
|
|
35
|
+
jsonSchema: {}
|
|
36
|
+
}),
|
|
37
|
+
withDefaultMake
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A string that is at least 1 character long and a maximum of 64.
|
|
42
|
+
*/
|
|
43
|
+
export interface NonEmptyString64Brand extends Simplify<B.Brand<"NonEmptyString64"> & NonEmptyString80Brand> {}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A string that is at least 1 character long and a maximum of 64.
|
|
47
|
+
*/
|
|
48
|
+
export type NonEmptyString64 = string & NonEmptyString64Brand
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A string that is at least 1 character long and a maximum of 64.
|
|
52
|
+
*/
|
|
53
|
+
export const NonEmptyString64 = nonEmptyString.pipe(
|
|
54
|
+
S.maxLength(64),
|
|
55
|
+
fromBrand(nominal<NonEmptyString64>(), {
|
|
56
|
+
identifier: "NonEmptyString64",
|
|
57
|
+
title: "NonEmptyString64",
|
|
58
|
+
jsonSchema: {}
|
|
59
|
+
}),
|
|
60
|
+
withDefaultMake
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A string that is at least 1 character long and a maximum of 80.
|
|
65
|
+
*/
|
|
66
|
+
export interface NonEmptyString80Brand extends Simplify<B.Brand<"NonEmptyString80"> & NonEmptyString100Brand> {}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A string that is at least 1 character long and a maximum of 80.
|
|
70
|
+
*/
|
|
71
|
+
export type NonEmptyString80 = string & NonEmptyString80Brand
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A string that is at least 1 character long and a maximum of 80.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
export const NonEmptyString80 = nonEmptyString.pipe(
|
|
78
|
+
S.maxLength(80),
|
|
79
|
+
fromBrand(nominal<NonEmptyString80>(), {
|
|
80
|
+
identifier: "NonEmptyString80",
|
|
81
|
+
title: "NonEmptyString80",
|
|
82
|
+
jsonSchema: {}
|
|
83
|
+
}),
|
|
84
|
+
withDefaultMake
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A string that is at least 1 character long and a maximum of 100.
|
|
89
|
+
*/
|
|
90
|
+
export interface NonEmptyString100Brand extends Simplify<B.Brand<"NonEmptyString100"> & NonEmptyString255Brand> {}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A string that is at least 1 character long and a maximum of 100.
|
|
94
|
+
*/
|
|
95
|
+
export type NonEmptyString100 = string & NonEmptyString100Brand
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A string that is at least 1 character long and a maximum of 100.
|
|
99
|
+
*/
|
|
100
|
+
export const NonEmptyString100 = nonEmptyString.pipe(
|
|
101
|
+
S.maxLength(100),
|
|
102
|
+
fromBrand(nominal<NonEmptyString100>(), {
|
|
103
|
+
identifier: "NonEmptyString100",
|
|
104
|
+
title: "NonEmptyString100",
|
|
105
|
+
jsonSchema: {}
|
|
106
|
+
}),
|
|
107
|
+
withDefaultMake
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A string that is at least 3 character long and a maximum of 255.
|
|
112
|
+
*/
|
|
113
|
+
export interface Min3String255Brand extends Simplify<B.Brand<"Min3String255"> & NonEmptyString255Brand> {}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A string that is at least 3 character long and a maximum of 255.
|
|
117
|
+
*/
|
|
118
|
+
export type Min3String255 = string & Min3String255Brand
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A string that is at least 3 character long and a maximum of 255.
|
|
122
|
+
*/
|
|
123
|
+
export const Min3String255 = pipe(
|
|
124
|
+
S.String,
|
|
125
|
+
S.minLength(3),
|
|
126
|
+
S.maxLength(255),
|
|
127
|
+
fromBrand(nominal<Min3String255>(), { identifier: "Min3String255", title: "Min3String255", jsonSchema: {} }),
|
|
128
|
+
withDefaultMake
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A string that is at least 6 characters long and a maximum of 50.
|
|
133
|
+
*/
|
|
134
|
+
export interface StringIdBrand extends Simplify<B.Brand<"StringId"> & NonEmptyString50Brand> {}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A string that is at least 6 characters long and a maximum of 50.
|
|
138
|
+
*/
|
|
139
|
+
export type StringId = string & StringIdBrand
|
|
140
|
+
|
|
141
|
+
const makeStringId = (): StringId => nanoid() as unknown as StringId
|
|
142
|
+
const minLength = 6
|
|
143
|
+
const maxLength = 50
|
|
144
|
+
const size = 21
|
|
145
|
+
const length = 10 * size
|
|
146
|
+
const StringIdArb = (): LazyArbitrary<string> => (fc) =>
|
|
147
|
+
fc
|
|
148
|
+
.uint8Array({ minLength: length, maxLength: length })
|
|
149
|
+
.map((_) => customRandom(urlAlphabet, size, (size) => _.subarray(0, size))())
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* A string that is at least 6 characters long and a maximum of 50.
|
|
153
|
+
*/
|
|
154
|
+
export const StringId = extendM(
|
|
155
|
+
pipe(
|
|
156
|
+
S.String,
|
|
157
|
+
S.minLength(minLength),
|
|
158
|
+
S.maxLength(maxLength),
|
|
159
|
+
fromBrand(nominal<StringId>(), {
|
|
160
|
+
identifier: "StringId",
|
|
161
|
+
title: "StringId",
|
|
162
|
+
arbitrary: StringIdArb,
|
|
163
|
+
jsonSchema: {}
|
|
164
|
+
})
|
|
165
|
+
),
|
|
166
|
+
(s) => ({
|
|
167
|
+
make: makeStringId,
|
|
168
|
+
withDefault: s.pipe(withDefaultConstructor(makeStringId))
|
|
169
|
+
})
|
|
170
|
+
)
|
|
171
|
+
.pipe(withDefaultMake)
|
|
172
|
+
|
|
173
|
+
// const prefixedStringIdUnsafe = (prefix: string) => StringId(prefix + StringId.make())
|
|
174
|
+
|
|
175
|
+
// const prefixedStringIdUnsafeThunk = (prefix: string) => () => prefixedStringIdUnsafe(prefix)
|
|
176
|
+
|
|
177
|
+
export function prefixedStringId<Brand extends StringId>() {
|
|
178
|
+
return <Prefix extends string, Separator extends string = "-">(
|
|
179
|
+
prefix: Prefix,
|
|
180
|
+
name: string,
|
|
181
|
+
separator?: Separator
|
|
182
|
+
) => {
|
|
183
|
+
type FullPrefix = `${Prefix}${Separator}`
|
|
184
|
+
const pref = `${prefix}${separator ?? "-"}` as FullPrefix
|
|
185
|
+
const arb = (): LazyArbitrary<string & Brand> => (fc) =>
|
|
186
|
+
StringIdArb()(fc).map(
|
|
187
|
+
(x) => (pref + x.substring(0, 50 - pref.length)) as Brand
|
|
188
|
+
)
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
190
|
+
const s: S.Schema<string & Brand, string> = StringId
|
|
191
|
+
.pipe(
|
|
192
|
+
S.filter((x: StringId): x is string & Brand => x.startsWith(pref), {
|
|
193
|
+
arbitrary: arb,
|
|
194
|
+
identifier: name,
|
|
195
|
+
title: name
|
|
196
|
+
})
|
|
197
|
+
)
|
|
198
|
+
const schema = s.pipe(withDefaultMake)
|
|
199
|
+
const make = () => (pref + StringId.make().substring(0, 50 - pref.length)) as Brand
|
|
200
|
+
|
|
201
|
+
return extendM(
|
|
202
|
+
schema,
|
|
203
|
+
(ex): PrefixedStringUtils<Brand, Prefix, Separator> => ({
|
|
204
|
+
make,
|
|
205
|
+
/**
|
|
206
|
+
* Automatically adds the prefix.
|
|
207
|
+
*/
|
|
208
|
+
unsafeFrom: (str: string) => ex(pref + str),
|
|
209
|
+
/**
|
|
210
|
+
* Must provide a literal string starting with prefix.
|
|
211
|
+
*/
|
|
212
|
+
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => ex(str),
|
|
213
|
+
prefix,
|
|
214
|
+
withDefault: schema.pipe(withDefaultConstructor(make))
|
|
215
|
+
})
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export const brandedStringId = <
|
|
221
|
+
Brand extends StringIdBrand
|
|
222
|
+
>() =>
|
|
223
|
+
withDefaultMake(
|
|
224
|
+
Object.assign(Object.create(StringId), StringId) as S.Schema<string & Brand, string> & {
|
|
225
|
+
make: () => string & Brand
|
|
226
|
+
withDefault: S.PropertySignature<":", string & Brand, never, ":", string, true, never>
|
|
227
|
+
} & WithDefaults<S.Schema<string & Brand, string>>
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
export interface PrefixedStringUtils<
|
|
231
|
+
Brand extends StringId,
|
|
232
|
+
Prefix extends string,
|
|
233
|
+
Separator extends string
|
|
234
|
+
> {
|
|
235
|
+
readonly make: () => Brand
|
|
236
|
+
readonly unsafeFrom: (str: string) => Brand
|
|
237
|
+
prefixSafe: <REST extends string>(str: `${Prefix}${Separator}${REST}`) => Brand
|
|
238
|
+
readonly prefix: Prefix
|
|
239
|
+
readonly withDefault: S.PropertySignature<":", Brand, never, ":", string, true, never>
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface UrlBrand extends Simplify<B.Brand<"Url"> & NonEmptyStringBrand> {}
|
|
243
|
+
|
|
244
|
+
export type Url = string & UrlBrand
|
|
245
|
+
|
|
246
|
+
const isUrl: Refinement<string, Url> = (s: string): s is Url => {
|
|
247
|
+
return validator.default.isURL(s, { require_tld: false })
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export const Url = S
|
|
251
|
+
.String
|
|
252
|
+
.pipe(
|
|
253
|
+
S.filter(isUrl, {
|
|
254
|
+
arbitrary: (): LazyArbitrary<Url> => (fc) => fc.webUrl().map((_) => _ as Url),
|
|
255
|
+
identifier: "Url",
|
|
256
|
+
title: "Url",
|
|
257
|
+
jsonSchema: { format: "uri" }
|
|
258
|
+
}),
|
|
259
|
+
withDefaultMake
|
|
260
|
+
)
|