@ambarltd/core 0.1.17
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/README.md +10 -0
- package/dist/callable.d.ts +1 -0
- package/dist/callable.js +6 -0
- package/dist/future.d.ts +102 -0
- package/dist/future.js +164 -0
- package/dist/helpers/object.d.ts +9 -0
- package/dist/helpers/object.js +31 -0
- package/dist/json/decoder.d.ts +99 -0
- package/dist/json/decoder.js +213 -0
- package/dist/json/encoder.d.ts +50 -0
- package/dist/json/encoder.js +115 -0
- package/dist/json/schema.d.ts +78 -0
- package/dist/json/schema.js +168 -0
- package/dist/json/types.d.ts +6 -0
- package/dist/json/types.js +1 -0
- package/dist/list.d.ts +35 -0
- package/dist/list.js +130 -0
- package/dist/maybe.d.ts +104 -0
- package/dist/maybe.js +106 -0
- package/dist/remote-data.d.ts +117 -0
- package/dist/remote-data.js +148 -0
- package/dist/result.d.ts +75 -0
- package/dist/result.js +126 -0
- package/dist/router.d.ts +117 -0
- package/dist/router.js +106 -0
- package/dist/test.d.ts +63 -0
- package/dist/test.js +470 -0
- package/dist/time.d.ts +118 -0
- package/dist/time.js +387 -0
- package/dist/tracing/opentelemetry.d.ts +27 -0
- package/dist/tracing/opentelemetry.js +215 -0
- package/dist/tracing/proxy.d.ts +20 -0
- package/dist/tracing/proxy.js +103 -0
- package/dist/tracing/simple.d.ts +10 -0
- package/dist/tracing/simple.js +224 -0
- package/dist/tracing.d.ts +29 -0
- package/dist/tracing.js +55 -0
- package/dist/trampoline.d.ts +24 -0
- package/dist/trampoline.js +46 -0
- package/dist/tree-map.d.ts +73 -0
- package/dist/tree-map.js +169 -0
- package/dist/tree-set.d.ts +63 -0
- package/dist/tree-set.js +114 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.js +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Maybe, Nullable } from "../maybe";
|
|
2
|
+
import { Json } from "./types";
|
|
3
|
+
/** Infer the type from an encoder definition. */
|
|
4
|
+
type Infer<A extends Encoder<any>> = A extends Encoder<infer B> ? B : never;
|
|
5
|
+
/**
|
|
6
|
+
* An Encoder is the opposite of a Decoder.
|
|
7
|
+
*
|
|
8
|
+
* It takes a structured value and transforms it into
|
|
9
|
+
* an object of type Json.
|
|
10
|
+
*/
|
|
11
|
+
declare class Encoder<A> {
|
|
12
|
+
readonly run: (v: A) => Json;
|
|
13
|
+
constructor(f: (v: A) => Json);
|
|
14
|
+
rmap<W>(f: (v: W) => A): Encoder<W>;
|
|
15
|
+
}
|
|
16
|
+
type EncoderDef<A> = {
|
|
17
|
+
[P in keyof A]: Encoder<A[P]> | EncoderOptional<A[P]>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Encode two values into a single one.
|
|
21
|
+
* Conflicts result in properties being overwritten.
|
|
22
|
+
*/
|
|
23
|
+
declare const both: <T, U>(left: Encoder<T>, right: Encoder<U>) => Encoder<[T, U]>;
|
|
24
|
+
declare const json: Encoder<Json>;
|
|
25
|
+
declare const boolean: Encoder<boolean>;
|
|
26
|
+
declare const number: Encoder<number>;
|
|
27
|
+
declare const string: Encoder<string>;
|
|
28
|
+
declare const array: <A>(encoder: Encoder<A>) => Encoder<Array<A>>;
|
|
29
|
+
declare const object: <A>(encoders: EncoderDef<A>) => Encoder<A>;
|
|
30
|
+
declare const pair: <L, R>(sleft: Encoder<L>, sright: Encoder<R>) => Encoder<[L, R]>;
|
|
31
|
+
declare const triple: <A, B, C>(sA: Encoder<A>, sB: Encoder<B>, sC: Encoder<C>) => Encoder<[A, B, C]>;
|
|
32
|
+
declare const maybe: <V>(encoder: Encoder<V>) => Encoder<Maybe<V>>;
|
|
33
|
+
declare const nullable: <V>(encoder: Encoder<V>) => Encoder<Nullable<V>>;
|
|
34
|
+
/** An encoder for object keys that omits the field if the value is Nothing. */
|
|
35
|
+
declare class EncoderOptional<A> {
|
|
36
|
+
readonly encoder: Encoder<A>;
|
|
37
|
+
private constructor();
|
|
38
|
+
static from<T>(e: Encoder<Maybe<T>>): EncoderOptional<Maybe<T>>;
|
|
39
|
+
rmap<B>(f: (v: B) => A): EncoderOptional<B>;
|
|
40
|
+
}
|
|
41
|
+
declare const optionalMaybe: <V>(encoder: Encoder<V>) => EncoderOptional<Maybe<V>>;
|
|
42
|
+
declare const optionalNullable: <V>(encoder: Encoder<NonNullable<V>>) => EncoderOptional<Nullable<V>>;
|
|
43
|
+
declare const optional: <V>(encoder: Encoder<V>) => EncoderOptional<V | undefined>;
|
|
44
|
+
declare const oneOf: <V>(f: (v: V) => Encoder<V>) => Encoder<V>;
|
|
45
|
+
declare const stringEnum: <const T extends string[]>(strs: T) => Encoder<T[number]>;
|
|
46
|
+
/** Encode a value into a JSON string by stringifying its JSON representation. */
|
|
47
|
+
declare const stringified: <T>(inner: Encoder<T>) => Encoder<T>;
|
|
48
|
+
/** Define a recursive encoder. */
|
|
49
|
+
declare function recursive<A>(f: (p: Encoder<A>) => Encoder<A>): Encoder<A>;
|
|
50
|
+
export { type Infer, Encoder, type EncoderDef, type EncoderOptional, json, boolean, number, string, array, object, pair, maybe, nullable, triple, optional, oneOf, both, stringEnum, stringified, optionalNullable, optionalMaybe, recursive, };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Nothing, Just } from "../maybe";
|
|
2
|
+
/**
|
|
3
|
+
* An Encoder is the opposite of a Decoder.
|
|
4
|
+
*
|
|
5
|
+
* It takes a structured value and transforms it into
|
|
6
|
+
* an object of type Json.
|
|
7
|
+
*/
|
|
8
|
+
class Encoder {
|
|
9
|
+
run;
|
|
10
|
+
constructor(f) {
|
|
11
|
+
this.run = f;
|
|
12
|
+
}
|
|
13
|
+
rmap(f) {
|
|
14
|
+
return new Encoder(v => this.run(f(v)));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const toAny = () => new Encoder(v => v);
|
|
18
|
+
/**
|
|
19
|
+
* Encode two values into a single one.
|
|
20
|
+
* Conflicts result in properties being overwritten.
|
|
21
|
+
*/
|
|
22
|
+
const both = (left, right) => new Encoder(([l, r]) => {
|
|
23
|
+
return Object.assign({}, left.run(l), right.run(r));
|
|
24
|
+
});
|
|
25
|
+
const json = toAny();
|
|
26
|
+
const boolean = toAny();
|
|
27
|
+
const number = new Encoder(input => {
|
|
28
|
+
if (!Number.isFinite(input)) {
|
|
29
|
+
throw new Error(`Cannot encode non-finite number: ${String(input)}`);
|
|
30
|
+
}
|
|
31
|
+
return input;
|
|
32
|
+
});
|
|
33
|
+
const string = toAny();
|
|
34
|
+
const array = (encoder) => new Encoder((input) => input.map(encoder.run));
|
|
35
|
+
const object = (encoders) => new Encoder(input => {
|
|
36
|
+
const result = {};
|
|
37
|
+
for (const field in encoders) {
|
|
38
|
+
const encoder = encoders[field];
|
|
39
|
+
switch (true) {
|
|
40
|
+
case encoder instanceof EncoderOptional: {
|
|
41
|
+
const encoded = encoder.encoder.run(input[field]);
|
|
42
|
+
if (typeof encoded != "object" || encoded === null || !("nothing" in encoded || "just" in encoded)) {
|
|
43
|
+
throw new Error(`Invalid output of EncoderOptional: ${encoded}`);
|
|
44
|
+
}
|
|
45
|
+
if ("just" in encoded) {
|
|
46
|
+
result[field] = encoded["just"];
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case encoder instanceof Encoder:
|
|
51
|
+
const encoded = encoder.run(input[field]);
|
|
52
|
+
result[field] = encoded;
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
encoder;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
});
|
|
60
|
+
const pair = (sleft, sright) => new Encoder(input => {
|
|
61
|
+
const [left, right] = input;
|
|
62
|
+
return [sleft.run(left), sright.run(right)];
|
|
63
|
+
});
|
|
64
|
+
const triple = (sA, sB, sC) => new Encoder(input => {
|
|
65
|
+
const [a, b, c] = input;
|
|
66
|
+
return [sA.run(a), sB.run(b), sC.run(c)];
|
|
67
|
+
});
|
|
68
|
+
const maybe = (encoder) => new Encoder(input => (input instanceof Nothing ? { nothing: {} } : { just: encoder.run(input.value) }));
|
|
69
|
+
const nullable = (encoder) => new Encoder(input => (input === null ? null : encoder.run(input)));
|
|
70
|
+
/** An encoder for object keys that omits the field if the value is Nothing. */
|
|
71
|
+
class EncoderOptional {
|
|
72
|
+
encoder;
|
|
73
|
+
constructor(encoder) {
|
|
74
|
+
this.encoder = encoder;
|
|
75
|
+
}
|
|
76
|
+
static from(e) {
|
|
77
|
+
return new EncoderOptional(e);
|
|
78
|
+
}
|
|
79
|
+
rmap(f) {
|
|
80
|
+
return new EncoderOptional(this.encoder.rmap(f));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const optionalMaybe = (encoder) => EncoderOptional.from(maybe(encoder));
|
|
84
|
+
const optionalNullable = (encoder) => optionalMaybe(encoder).rmap(v => v === null ? Nothing()
|
|
85
|
+
: v === undefined ? Nothing()
|
|
86
|
+
: Just(v));
|
|
87
|
+
const optional = (encoder) => optionalMaybe(encoder).rmap((input) => {
|
|
88
|
+
if (typeof input === "undefined") {
|
|
89
|
+
return Nothing();
|
|
90
|
+
}
|
|
91
|
+
return Just(input);
|
|
92
|
+
});
|
|
93
|
+
const oneOf = (f) => new Encoder(input => {
|
|
94
|
+
const encoder = f(input);
|
|
95
|
+
return encoder.run(input);
|
|
96
|
+
});
|
|
97
|
+
const stringEnum = (strs) => string.rmap(input => {
|
|
98
|
+
if (!strs.includes(input)) {
|
|
99
|
+
throw new Error(`Cannot encode '${input}'. Expected one of '${strs.join(", ")}'"`);
|
|
100
|
+
}
|
|
101
|
+
return input;
|
|
102
|
+
});
|
|
103
|
+
/** Encode a value into a JSON string by stringifying its JSON representation. */
|
|
104
|
+
const stringified = (inner) => string.rmap(v => JSON.stringify(inner.run(v)));
|
|
105
|
+
/** Define a recursive encoder. */
|
|
106
|
+
function recursive(f) {
|
|
107
|
+
const base = new Encoder(_ => {
|
|
108
|
+
throw new Error("A recursive encoder cannot immediately call itself.");
|
|
109
|
+
});
|
|
110
|
+
const top = f(base);
|
|
111
|
+
// @ts-expect-error assigning to read-only prop
|
|
112
|
+
base.run = top.run;
|
|
113
|
+
return top;
|
|
114
|
+
}
|
|
115
|
+
export { Encoder, json, boolean, number, string, array, object, pair, maybe, nullable, triple, optional, oneOf, both, stringEnum, stringified, optionalNullable, optionalMaybe, recursive, };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as decoder from "./decoder";
|
|
2
|
+
import * as encoder from "./encoder";
|
|
3
|
+
import { Result } from "../result";
|
|
4
|
+
import { Decoder, type DecoderOptional } from "./decoder";
|
|
5
|
+
import { Encoder, type EncoderOptional } from "./encoder";
|
|
6
|
+
import { Json } from "./types";
|
|
7
|
+
import { Maybe, Nullable } from "../maybe";
|
|
8
|
+
/** Infer the type from a schema definition. */
|
|
9
|
+
type Infer<A extends Schema<any>> = A extends Schema<infer B> ? B : never;
|
|
10
|
+
/**
|
|
11
|
+
* A `Schema<A>` contains information to encode and decode a value.
|
|
12
|
+
*
|
|
13
|
+
* This allows us to have safe conversion to and from JSON.
|
|
14
|
+
*/
|
|
15
|
+
declare class Schema<A> {
|
|
16
|
+
decoder: Decoder<A>;
|
|
17
|
+
encoder: Encoder<A>;
|
|
18
|
+
constructor(decoder: Decoder<A>, encoder: Encoder<A>);
|
|
19
|
+
dimap<W>(p: (v: A) => W, s: (v: W) => A): Schema<W>;
|
|
20
|
+
chain<W>(p: (v: A) => Decoder<W>, s: (v: W) => A): Schema<W>;
|
|
21
|
+
}
|
|
22
|
+
declare function decode<A>(schema: Schema<A>, input: unknown): Result<string, A>;
|
|
23
|
+
declare function encode<A>(schema: Schema<A>, input: A): Json;
|
|
24
|
+
/** Create a Schema. */
|
|
25
|
+
declare function from<A>(decoder: Decoder<A>, encoder: Encoder<A>): Schema<A>;
|
|
26
|
+
type SchemaDef<A> = {
|
|
27
|
+
[D in keyof A]: Schema<A[D]> | SchemaOptional<A[D]>;
|
|
28
|
+
};
|
|
29
|
+
declare const json: Schema<Json>;
|
|
30
|
+
declare const boolean: Schema<boolean>;
|
|
31
|
+
declare const number: Schema<number>;
|
|
32
|
+
declare const string: Schema<string>;
|
|
33
|
+
declare const array: <A>(schema: Schema<A>) => Schema<Array<A>>;
|
|
34
|
+
declare const both: <T, U>(left: Schema<T>, right: Schema<U>) => Schema<[T, U]>;
|
|
35
|
+
/** Schema for an object field that may not be present. */
|
|
36
|
+
declare class SchemaOptional<A> {
|
|
37
|
+
readonly decoder: DecoderOptional<A>;
|
|
38
|
+
readonly encoder: EncoderOptional<A>;
|
|
39
|
+
constructor(decoder: DecoderOptional<A>, encoder: EncoderOptional<A>);
|
|
40
|
+
dimap<W>(p: (v: A) => W, s: (v: W) => A): SchemaOptional<W>;
|
|
41
|
+
}
|
|
42
|
+
/** An object field that may be absent. */
|
|
43
|
+
declare const optional: <A>(s: Schema<A>) => SchemaOptional<A | undefined>;
|
|
44
|
+
declare const optionalNullable: <A>(schema: Schema<NonNullable<A>>) => SchemaOptional<Nullable<A>>;
|
|
45
|
+
declare const optionalMaybe: <A>(schema: Schema<A>) => SchemaOptional<Maybe<A>>;
|
|
46
|
+
/**
|
|
47
|
+
* When decoding, if the field is absent, then the default will be used.
|
|
48
|
+
* When encoding, the field is required.
|
|
49
|
+
*/
|
|
50
|
+
declare const optionalDefault: <A>(def: A, schema: Schema<A>) => SchemaOptional<A>;
|
|
51
|
+
declare function object<A>(def: SchemaDef<A>): Schema<A>;
|
|
52
|
+
declare const pair: <L, R>(l: Schema<L>, r: Schema<R>) => Schema<[L, R]>;
|
|
53
|
+
declare const triple: <A, B, C>(a: Schema<A>, b: Schema<B>, c: Schema<C>) => Schema<[A, B, C]>;
|
|
54
|
+
declare const map: <A>(s: Schema<A>) => Schema<Map<string, A>>;
|
|
55
|
+
declare const maybe: <A>(s: Schema<A>) => Schema<Maybe<A>>;
|
|
56
|
+
declare const result: <E, T>(error: Schema<E>, success: Schema<T>) => Schema<Result<E, T>>;
|
|
57
|
+
declare const nullable: <A>(s: Schema<A>) => Schema<Nullable<A>>;
|
|
58
|
+
declare const stringLiteral: <T extends string>(str: T) => Schema<T>;
|
|
59
|
+
declare const stringEnum: <const T extends string[]>(strs: T) => Schema<T[number]>;
|
|
60
|
+
declare const oneOf: <V>(f: (v: V) => Schema<V>, ss: Array<Schema<V>>) => Schema<V>;
|
|
61
|
+
declare const discriminatedUnion: <const Variants extends readonly Variant<any>[]>(vars: Variants) => Schema<Infer<Variants[number]["schema"]>>;
|
|
62
|
+
/**
|
|
63
|
+
* One option in a sum type.
|
|
64
|
+
* Includes the pattern that differentiates it from the other options in the type.
|
|
65
|
+
*/
|
|
66
|
+
declare class Variant<T> {
|
|
67
|
+
readonly pattern: Record<string, string>;
|
|
68
|
+
readonly schema: Schema<T>;
|
|
69
|
+
constructor(pattern: Record<string, string>, schema: Schema<T>);
|
|
70
|
+
}
|
|
71
|
+
type VariantDef<T extends string, A> = {
|
|
72
|
+
[D in keyof A]: Schema<A[D]> | SchemaOptional<A[D]> | (A[D] & T);
|
|
73
|
+
};
|
|
74
|
+
declare const variant: <const T extends string, const A>(def: VariantDef<T, A>) => Variant<A>;
|
|
75
|
+
/** Schema for a stringified JSON representation. */
|
|
76
|
+
declare const stringified: <T>(inner: Schema<T>) => Schema<T>;
|
|
77
|
+
declare const recursive: <T>(f: (s: Schema<T>) => Schema<T>) => Schema<T>;
|
|
78
|
+
export { Schema, type Infer, type SchemaDef, type SchemaOptional, type Variant, object, pair, triple, map, boolean, number, string, array, json, both, maybe, result, nullable, optional, optionalNullable, optionalMaybe, optionalDefault, stringLiteral, stringEnum, stringified, oneOf, discriminatedUnion, variant, from, decode, encode, decoder, encoder, recursive, };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as decoder from "./decoder";
|
|
2
|
+
import * as encoder from "./encoder";
|
|
3
|
+
import { Success, Failure } from "../result";
|
|
4
|
+
import { Encoder } from "./encoder";
|
|
5
|
+
import { filterMap, mapValues } from "../helpers/object";
|
|
6
|
+
/**
|
|
7
|
+
* A `Schema<A>` contains information to encode and decode a value.
|
|
8
|
+
*
|
|
9
|
+
* This allows us to have safe conversion to and from JSON.
|
|
10
|
+
*/
|
|
11
|
+
class Schema {
|
|
12
|
+
decoder;
|
|
13
|
+
encoder;
|
|
14
|
+
constructor(decoder, encoder) {
|
|
15
|
+
this.decoder = decoder;
|
|
16
|
+
this.encoder = encoder;
|
|
17
|
+
}
|
|
18
|
+
dimap(p, s) {
|
|
19
|
+
return new Schema(this.decoder.map(p), this.encoder.rmap(s));
|
|
20
|
+
}
|
|
21
|
+
chain(p, s) {
|
|
22
|
+
return new Schema(this.decoder.chain(p), this.encoder.rmap(s));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function decode(schema, input) {
|
|
26
|
+
return decoder.decode(input, schema.decoder);
|
|
27
|
+
}
|
|
28
|
+
function encode(schema, input) {
|
|
29
|
+
return schema.encoder.run(input);
|
|
30
|
+
}
|
|
31
|
+
/** Create a Schema. */
|
|
32
|
+
function from(decoder, encoder) {
|
|
33
|
+
return new Schema(decoder, encoder);
|
|
34
|
+
}
|
|
35
|
+
const json = new Schema(decoder.json, encoder.json);
|
|
36
|
+
const boolean = new Schema(decoder.boolean, encoder.boolean);
|
|
37
|
+
const number = new Schema(decoder.number, encoder.number);
|
|
38
|
+
const string = new Schema(decoder.string, encoder.string);
|
|
39
|
+
const array = (schema) => new Schema(decoder.array(schema.decoder), encoder.array(schema.encoder));
|
|
40
|
+
const both = (left, right) => new Schema(decoder.both(left.decoder, right.decoder), encoder.both(left.encoder, right.encoder));
|
|
41
|
+
/** Schema for an object field that may not be present. */
|
|
42
|
+
class SchemaOptional {
|
|
43
|
+
decoder;
|
|
44
|
+
encoder;
|
|
45
|
+
constructor(decoder, encoder) {
|
|
46
|
+
this.decoder = decoder;
|
|
47
|
+
this.encoder = encoder;
|
|
48
|
+
}
|
|
49
|
+
dimap(p, s) {
|
|
50
|
+
return new SchemaOptional(this.decoder.map(p), this.encoder.rmap(s));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** An object field that may be absent. */
|
|
54
|
+
const optional = (s) => new SchemaOptional(decoder.optional(s.decoder), encoder.optional(s.encoder));
|
|
55
|
+
const optionalNullable = (schema) => new SchemaOptional(decoder.optionalNullable(schema.decoder), encoder.optionalNullable(schema.encoder));
|
|
56
|
+
const optionalMaybe = (schema) => new SchemaOptional(decoder.optionalMaybe(schema.decoder), encoder.optionalMaybe(schema.encoder));
|
|
57
|
+
/**
|
|
58
|
+
* When decoding, if the field is absent, then the default will be used.
|
|
59
|
+
* When encoding, the field is required.
|
|
60
|
+
*/
|
|
61
|
+
const optionalDefault = (def, schema) => new SchemaOptional(decoder.optionalDefault(def, schema.decoder), encoder.optional(schema.encoder));
|
|
62
|
+
function object(def) {
|
|
63
|
+
const pdef = {};
|
|
64
|
+
const sdef = {};
|
|
65
|
+
for (const key in def) {
|
|
66
|
+
const schema = def[key];
|
|
67
|
+
pdef[key] = schema.decoder;
|
|
68
|
+
sdef[key] = schema.encoder;
|
|
69
|
+
}
|
|
70
|
+
return new Schema(decoder.object(pdef), encoder.object(sdef));
|
|
71
|
+
}
|
|
72
|
+
const pair = (l, r) => new Schema(decoder.pair(l.decoder, r.decoder), encoder.pair(l.encoder, r.encoder));
|
|
73
|
+
const triple = (a, b, c) => new Schema(decoder.triple(a.decoder, b.decoder, c.decoder), encoder.triple(a.encoder, b.encoder, c.encoder));
|
|
74
|
+
const map = (s) => array(pair(string, s)).dimap(xs => xs.reduce((acc, [k, v]) => acc.set(k, v), new Map()), m => Array.from(m.entries()));
|
|
75
|
+
const maybe = (s) => new Schema(decoder.maybe(s.decoder), encoder.maybe(s.encoder));
|
|
76
|
+
const result = (error, success) => discriminatedUnion([
|
|
77
|
+
variant({
|
|
78
|
+
type: "Success",
|
|
79
|
+
value: success,
|
|
80
|
+
}),
|
|
81
|
+
variant({
|
|
82
|
+
type: "Failure",
|
|
83
|
+
error,
|
|
84
|
+
}),
|
|
85
|
+
]).dimap(v => {
|
|
86
|
+
switch (v.type) {
|
|
87
|
+
case "Success":
|
|
88
|
+
return Success(v.value);
|
|
89
|
+
case "Failure":
|
|
90
|
+
return Failure(v.error);
|
|
91
|
+
default:
|
|
92
|
+
return v;
|
|
93
|
+
}
|
|
94
|
+
}, r => r.either(error => ({ type: "Failure", error }), value => ({ type: "Success", value })));
|
|
95
|
+
const nullable = (s) => new Schema(decoder.nullable(s.decoder), encoder.nullable(s.encoder));
|
|
96
|
+
const stringLiteral = (str) => new Schema(decoder.stringLiteral(str), encoder.string.rmap(input => {
|
|
97
|
+
if (input != str) {
|
|
98
|
+
throw new Error(`Cannot encode '${input}'. Expected literal '${str}'"`);
|
|
99
|
+
}
|
|
100
|
+
return input;
|
|
101
|
+
}));
|
|
102
|
+
const stringEnum = (strs) => new Schema(decoder.stringEnum(strs), encoder.stringEnum(strs));
|
|
103
|
+
const oneOf = (f, ss) => new Schema(decoder.oneOf(ss.map(s => s.decoder)), encoder.oneOf(v => f(v).encoder));
|
|
104
|
+
const discriminatedUnion = (vars) => {
|
|
105
|
+
const d = decoder.oneOf(vars.map(v => v.schema.decoder));
|
|
106
|
+
const e = encoder.oneOf(v => {
|
|
107
|
+
const found = vars.find(variant => matches(variant.pattern, v));
|
|
108
|
+
if (found == undefined) {
|
|
109
|
+
throw new Error(`Invalid discriminant in union type: '${v}'`);
|
|
110
|
+
}
|
|
111
|
+
return found.schema.encoder;
|
|
112
|
+
});
|
|
113
|
+
return new Schema(d, e);
|
|
114
|
+
};
|
|
115
|
+
/** Check whether a value matches a pattern. */
|
|
116
|
+
const matches = (pattern, val) => {
|
|
117
|
+
if (typeof val !== "object" || val === null) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
for (const key in pattern) {
|
|
121
|
+
if (!(key in val)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (pattern[key] != undefined && pattern[key] !== val[key]) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* One option in a sum type.
|
|
132
|
+
* Includes the pattern that differentiates it from the other options in the type.
|
|
133
|
+
*/
|
|
134
|
+
class Variant {
|
|
135
|
+
pattern;
|
|
136
|
+
schema;
|
|
137
|
+
constructor(pattern, schema) {
|
|
138
|
+
this.pattern = pattern;
|
|
139
|
+
this.schema = schema;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const variant = (def) => {
|
|
143
|
+
const pattern = filterMap(def, (_, v) => (typeof v == "string" ? v : undefined));
|
|
144
|
+
if (Object.keys(pattern).length == 0) {
|
|
145
|
+
throw new Error("Invalid variant definition. No discriminant identified. Discriminant must be provided as a string");
|
|
146
|
+
}
|
|
147
|
+
const schemaDef = mapValues(def, (_, value) =>
|
|
148
|
+
// @ts-expect-error hard to prove the types, but this is correct.
|
|
149
|
+
typeof value == "string" ? stringLiteral(value) : value);
|
|
150
|
+
const schema = object(schemaDef);
|
|
151
|
+
return new Variant(pattern, schema);
|
|
152
|
+
};
|
|
153
|
+
/** Schema for a stringified JSON representation. */
|
|
154
|
+
const stringified = (inner) => new Schema(decoder.stringified(inner.decoder), encoder.stringified(inner.encoder));
|
|
155
|
+
const recursive = (f) => {
|
|
156
|
+
const baseEncoder = new Encoder(_ => {
|
|
157
|
+
throw new Error("A recursive encoder cannot immediately call itself.");
|
|
158
|
+
});
|
|
159
|
+
const baseDecoder = decoder.fail("A recursive decoder cannot immediately call itself.");
|
|
160
|
+
const base = new Schema(baseDecoder, baseEncoder);
|
|
161
|
+
const top = f(base);
|
|
162
|
+
// @ts-expect-error assigning to read-only prop
|
|
163
|
+
base.encoder.run = top.encoder.run;
|
|
164
|
+
// @ts-expect-error assigning to read-only prop
|
|
165
|
+
base.decoder.run = top.decoder.run;
|
|
166
|
+
return top;
|
|
167
|
+
};
|
|
168
|
+
export { Schema, object, pair, triple, map, boolean, number, string, array, json, both, maybe, result, nullable, optional, optionalNullable, optionalMaybe, optionalDefault, stringLiteral, stringEnum, stringified, oneOf, discriminatedUnion, variant, from, decode, encode, decoder, encoder, recursive, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/list.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Maybe } from "./maybe";
|
|
2
|
+
type Content<T> = {
|
|
3
|
+
head: T;
|
|
4
|
+
tail: List<T>;
|
|
5
|
+
} | {
|
|
6
|
+
empty: null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* An immutable singly-linked list with cheap prepending (`cons`) and
|
|
10
|
+
* iteration. Empty lists share a common representation; otherwise, each
|
|
11
|
+
* node holds a `head` and a reference to the next list.
|
|
12
|
+
*/
|
|
13
|
+
declare class List<T> {
|
|
14
|
+
readonly value: Content<T>;
|
|
15
|
+
static empty<T>(): List<T>;
|
|
16
|
+
static cons<T>(head: T, tail: List<T>): List<T>;
|
|
17
|
+
static singleton<T>(v: T): List<T>;
|
|
18
|
+
static concat<T>(x: List<T>, y: List<T>): List<T>;
|
|
19
|
+
static from<T>(array: Array<T>): List<T>;
|
|
20
|
+
private constructor();
|
|
21
|
+
isEmpty(): boolean;
|
|
22
|
+
head(): Maybe<T>;
|
|
23
|
+
tail(): List<T>;
|
|
24
|
+
length(): number;
|
|
25
|
+
map<W>(f: (v: T) => W): List<W>;
|
|
26
|
+
reduce<U>(init: U, f: (acc: U, elem: T) => U): U;
|
|
27
|
+
find(predicate: (elem: T) => boolean): Maybe<T>;
|
|
28
|
+
contains(elem: T): boolean;
|
|
29
|
+
filter(predicate: (v: T) => boolean): List<T>;
|
|
30
|
+
reverse(): List<T>;
|
|
31
|
+
append(item: T): List<T>;
|
|
32
|
+
toArray(): Array<T>;
|
|
33
|
+
[Symbol.iterator](): IterableIterator<T>;
|
|
34
|
+
}
|
|
35
|
+
export { List };
|
package/dist/list.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Just, Nothing } from "./maybe";
|
|
2
|
+
/**
|
|
3
|
+
* An immutable singly-linked list with cheap prepending (`cons`) and
|
|
4
|
+
* iteration. Empty lists share a common representation; otherwise, each
|
|
5
|
+
* node holds a `head` and a reference to the next list.
|
|
6
|
+
*/
|
|
7
|
+
class List {
|
|
8
|
+
value;
|
|
9
|
+
static empty() {
|
|
10
|
+
return new List({ empty: null });
|
|
11
|
+
}
|
|
12
|
+
static cons(head, tail) {
|
|
13
|
+
return new List({ head, tail });
|
|
14
|
+
}
|
|
15
|
+
static singleton(v) {
|
|
16
|
+
return List.cons(v, List.empty());
|
|
17
|
+
}
|
|
18
|
+
static concat(x, y) {
|
|
19
|
+
let r = y;
|
|
20
|
+
for (const el of x.reverse()) {
|
|
21
|
+
r = List.cons(el, r);
|
|
22
|
+
}
|
|
23
|
+
return r;
|
|
24
|
+
}
|
|
25
|
+
static from(array) {
|
|
26
|
+
let result = List.empty();
|
|
27
|
+
for (let i = array.length - 1; i >= 0; i--) {
|
|
28
|
+
result = List.cons(array[i], result);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
constructor(v) {
|
|
33
|
+
this.value = v;
|
|
34
|
+
}
|
|
35
|
+
isEmpty() {
|
|
36
|
+
switch (true) {
|
|
37
|
+
case "empty" in this.value:
|
|
38
|
+
return true;
|
|
39
|
+
case "head" in this.value:
|
|
40
|
+
return false;
|
|
41
|
+
default:
|
|
42
|
+
return this.value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
head() {
|
|
46
|
+
switch (true) {
|
|
47
|
+
case "empty" in this.value:
|
|
48
|
+
return Nothing();
|
|
49
|
+
case "head" in this.value:
|
|
50
|
+
return Just(this.value.head);
|
|
51
|
+
default:
|
|
52
|
+
return this.value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
tail() {
|
|
56
|
+
switch (true) {
|
|
57
|
+
case "empty" in this.value:
|
|
58
|
+
return List.empty();
|
|
59
|
+
case "head" in this.value:
|
|
60
|
+
return this.value.tail;
|
|
61
|
+
default:
|
|
62
|
+
return this.value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
length() {
|
|
66
|
+
let len = 0;
|
|
67
|
+
for (const _ of this) {
|
|
68
|
+
len++;
|
|
69
|
+
}
|
|
70
|
+
return len;
|
|
71
|
+
}
|
|
72
|
+
map(f) {
|
|
73
|
+
let r = List.empty();
|
|
74
|
+
for (const item of this.reverse()) {
|
|
75
|
+
r = List.cons(f(item), r);
|
|
76
|
+
}
|
|
77
|
+
return r;
|
|
78
|
+
}
|
|
79
|
+
reduce(init, f) {
|
|
80
|
+
let acc = init;
|
|
81
|
+
for (const item of this) {
|
|
82
|
+
acc = f(acc, item);
|
|
83
|
+
}
|
|
84
|
+
return acc;
|
|
85
|
+
}
|
|
86
|
+
find(predicate) {
|
|
87
|
+
for (const item of this) {
|
|
88
|
+
if (predicate(item))
|
|
89
|
+
return Just(item);
|
|
90
|
+
}
|
|
91
|
+
return Nothing();
|
|
92
|
+
}
|
|
93
|
+
contains(elem) {
|
|
94
|
+
return this.find(x => x === elem).isJust();
|
|
95
|
+
}
|
|
96
|
+
filter(predicate) {
|
|
97
|
+
let result = List.empty();
|
|
98
|
+
for (const item of this.reverse()) {
|
|
99
|
+
if (predicate(item)) {
|
|
100
|
+
result = List.cons(item, result);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
reverse() {
|
|
106
|
+
let r = List.empty();
|
|
107
|
+
for (const item of this) {
|
|
108
|
+
r = List.cons(item, r);
|
|
109
|
+
}
|
|
110
|
+
return r;
|
|
111
|
+
}
|
|
112
|
+
append(item) {
|
|
113
|
+
return List.concat(this, List.singleton(item));
|
|
114
|
+
}
|
|
115
|
+
toArray() {
|
|
116
|
+
const result = [];
|
|
117
|
+
for (const item of this) {
|
|
118
|
+
result.push(item);
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
*[Symbol.iterator]() {
|
|
123
|
+
let list = this;
|
|
124
|
+
while ("head" in list.value) {
|
|
125
|
+
yield list.value.head;
|
|
126
|
+
list = list.value.tail;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export { List };
|