@valbuild/core 0.12.0 → 0.13.0
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/jest.config.js +4 -0
- package/package.json +1 -1
- package/src/Json.ts +4 -0
- package/src/expr/README.md +193 -0
- package/src/expr/eval.test.ts +202 -0
- package/src/expr/eval.ts +248 -0
- package/src/expr/expr.ts +91 -0
- package/src/expr/index.ts +3 -0
- package/src/expr/parser.test.ts +158 -0
- package/src/expr/parser.ts +229 -0
- package/src/expr/repl.ts +93 -0
- package/src/expr/tokenizer.test.ts +539 -0
- package/src/expr/tokenizer.ts +117 -0
- package/src/fetchVal.test.ts +164 -0
- package/src/fetchVal.ts +211 -0
- package/src/fp/array.ts +30 -0
- package/src/fp/index.ts +3 -0
- package/src/fp/result.ts +214 -0
- package/src/fp/util.ts +52 -0
- package/src/index.ts +55 -0
- package/src/initSchema.ts +45 -0
- package/src/initVal.ts +96 -0
- package/src/module.test.ts +170 -0
- package/src/module.ts +333 -0
- package/src/patch/deref.test.ts +300 -0
- package/src/patch/deref.ts +128 -0
- package/src/patch/index.ts +11 -0
- package/src/patch/json.test.ts +583 -0
- package/src/patch/json.ts +304 -0
- package/src/patch/operation.ts +74 -0
- package/src/patch/ops.ts +83 -0
- package/src/patch/parse.test.ts +202 -0
- package/src/patch/parse.ts +187 -0
- package/src/patch/patch.ts +46 -0
- package/src/patch/util.ts +67 -0
- package/src/schema/array.ts +52 -0
- package/src/schema/boolean.ts +38 -0
- package/src/schema/i18n.ts +65 -0
- package/src/schema/image.ts +70 -0
- package/src/schema/index.ts +46 -0
- package/src/schema/literal.ts +42 -0
- package/src/schema/number.ts +45 -0
- package/src/schema/object.ts +67 -0
- package/src/schema/oneOf.ts +60 -0
- package/src/schema/richtext.ts +417 -0
- package/src/schema/string.ts +49 -0
- package/src/schema/union.ts +62 -0
- package/src/selector/ExprProxy.test.ts +203 -0
- package/src/selector/ExprProxy.ts +209 -0
- package/src/selector/SelectorProxy.test.ts +172 -0
- package/src/selector/SelectorProxy.ts +237 -0
- package/src/selector/array.ts +37 -0
- package/src/selector/boolean.ts +4 -0
- package/src/selector/file.ts +14 -0
- package/src/selector/i18n.ts +13 -0
- package/src/selector/index.ts +159 -0
- package/src/selector/number.ts +4 -0
- package/src/selector/object.ts +22 -0
- package/src/selector/primitive.ts +17 -0
- package/src/selector/remote.ts +9 -0
- package/src/selector/selector.test.ts +453 -0
- package/src/selector/selectorOf.ts +7 -0
- package/src/selector/string.ts +4 -0
- package/src/source/file.ts +45 -0
- package/src/source/i18n.ts +60 -0
- package/src/source/index.ts +50 -0
- package/src/source/remote.ts +54 -0
- package/src/val/array.ts +10 -0
- package/src/val/index.ts +90 -0
- package/src/val/object.ts +13 -0
- package/src/val/primitive.ts +8 -0
@@ -0,0 +1,237 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
+
import { Path, GenericSelector, SourceOrExpr, GetSchema } from ".";
|
3
|
+
import { Expr } from "../expr/expr";
|
4
|
+
import { Schema } from "../schema";
|
5
|
+
import { convertImageSource } from "../schema/image";
|
6
|
+
import { Source, SourcePrimitive, VAL_EXTENSION } from "../source";
|
7
|
+
import { FILE_REF_PROP } from "../source/file";
|
8
|
+
import { isSerializedVal, SourcePath } from "../val";
|
9
|
+
|
10
|
+
function hasOwn<T extends PropertyKey>(obj: object, prop: T): boolean {
|
11
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
12
|
+
}
|
13
|
+
|
14
|
+
function andThen(f: (...args: any[]) => any, source: any, path?: SourcePath) {
|
15
|
+
if (source) {
|
16
|
+
return newSelectorProxy(f(newSelectorProxy(source, path)));
|
17
|
+
}
|
18
|
+
return newSelectorProxy(source, path);
|
19
|
+
}
|
20
|
+
|
21
|
+
export function isSelector(source: any): source is GenericSelector<Source> {
|
22
|
+
return (
|
23
|
+
typeof source === "object" &&
|
24
|
+
source !== null &&
|
25
|
+
(SourceOrExpr in source || Path in source)
|
26
|
+
);
|
27
|
+
}
|
28
|
+
|
29
|
+
export function newSelectorProxy(
|
30
|
+
source: any,
|
31
|
+
path?: SourcePath,
|
32
|
+
moduleSchema?: any
|
33
|
+
): any {
|
34
|
+
if (typeof source === "object") {
|
35
|
+
if (isSelector(source)) {
|
36
|
+
return source;
|
37
|
+
} else if (isSerializedVal(source)) {
|
38
|
+
return newSelectorProxy(source.val, source.valPath);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
if (source && source[FILE_REF_PROP] && source[VAL_EXTENSION] === "file") {
|
43
|
+
const fileRef = source[FILE_REF_PROP];
|
44
|
+
if (typeof fileRef !== "string") {
|
45
|
+
throw Error("Invalid file ref: " + fileRef);
|
46
|
+
}
|
47
|
+
return newSelectorProxy(convertImageSource(source), path, moduleSchema);
|
48
|
+
}
|
49
|
+
|
50
|
+
switch (typeof source) {
|
51
|
+
case "function":
|
52
|
+
case "symbol":
|
53
|
+
throw Error(`Invalid selector type: ${typeof source}: ${source}`);
|
54
|
+
case "object":
|
55
|
+
// Handles both objects and arrays!
|
56
|
+
if (source !== null) {
|
57
|
+
return new Proxy(source, {
|
58
|
+
// TODO: see proxy docs if we want more traps
|
59
|
+
has(target, prop: string | symbol) {
|
60
|
+
if (prop === SourceOrExpr) {
|
61
|
+
return true;
|
62
|
+
}
|
63
|
+
if (prop === Path) {
|
64
|
+
return true;
|
65
|
+
}
|
66
|
+
if (prop === "andThen") {
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
if (prop === GetSchema) {
|
70
|
+
return true;
|
71
|
+
}
|
72
|
+
return prop in target;
|
73
|
+
},
|
74
|
+
get(target, prop: string | symbol) {
|
75
|
+
if (prop === SourceOrExpr) {
|
76
|
+
return source;
|
77
|
+
}
|
78
|
+
if (prop === Path) {
|
79
|
+
return path;
|
80
|
+
}
|
81
|
+
if (prop === GetSchema) {
|
82
|
+
return moduleSchema;
|
83
|
+
}
|
84
|
+
if (prop === "andThen") {
|
85
|
+
return (f: any) => andThen(f, source, path);
|
86
|
+
}
|
87
|
+
if (Array.isArray(target)) {
|
88
|
+
if (prop === "filter") {
|
89
|
+
return (f: any) => {
|
90
|
+
const filtered = target
|
91
|
+
.map((a, i) =>
|
92
|
+
newSelectorProxy(
|
93
|
+
a,
|
94
|
+
createValPathOfItem(path, i),
|
95
|
+
moduleSchema?.item
|
96
|
+
)
|
97
|
+
)
|
98
|
+
.filter((a) => {
|
99
|
+
if (f && f instanceof Schema<Source>) {
|
100
|
+
return f.match(unValify(a));
|
101
|
+
} else {
|
102
|
+
return unValify(f(a));
|
103
|
+
}
|
104
|
+
});
|
105
|
+
return newSelectorProxy(filtered, path, moduleSchema);
|
106
|
+
};
|
107
|
+
} else if (prop === "map") {
|
108
|
+
return (f: any) => {
|
109
|
+
const filtered = target.map((a, i) => {
|
110
|
+
const valueOrSelector = f(
|
111
|
+
newSelectorProxy(
|
112
|
+
a,
|
113
|
+
createValPathOfItem(path, i),
|
114
|
+
moduleSchema?.item
|
115
|
+
),
|
116
|
+
newSelectorProxy(i)
|
117
|
+
);
|
118
|
+
if (isSelector(valueOrSelector)) {
|
119
|
+
return valueOrSelector;
|
120
|
+
}
|
121
|
+
return newSelectorProxy(valueOrSelector);
|
122
|
+
});
|
123
|
+
return newSelectorProxy(filtered, path, moduleSchema);
|
124
|
+
};
|
125
|
+
}
|
126
|
+
}
|
127
|
+
if (Array.isArray(target) && prop === "length") {
|
128
|
+
return newSelectorProxy(target.length);
|
129
|
+
}
|
130
|
+
const reflectedValue = Reflect.get(target, prop);
|
131
|
+
|
132
|
+
if (hasOwn(source, prop)) {
|
133
|
+
if (!Number.isNaN(Number(prop))) {
|
134
|
+
return newSelectorProxy(
|
135
|
+
reflectedValue,
|
136
|
+
createValPathOfItem(path, Number(prop)),
|
137
|
+
moduleSchema?.item
|
138
|
+
);
|
139
|
+
}
|
140
|
+
return newSelectorProxy(
|
141
|
+
reflectedValue,
|
142
|
+
createValPathOfItem(path, prop),
|
143
|
+
moduleSchema?.items[prop]
|
144
|
+
);
|
145
|
+
}
|
146
|
+
return reflectedValue;
|
147
|
+
},
|
148
|
+
});
|
149
|
+
}
|
150
|
+
// intentional fallthrough
|
151
|
+
// eslint-disable-next-line no-fallthrough
|
152
|
+
default:
|
153
|
+
return {
|
154
|
+
eq: (other: SourcePrimitive | GenericSelector<Source>) => {
|
155
|
+
let otherValue: any = other;
|
156
|
+
if (isSelector(other)) {
|
157
|
+
otherValue = other[SourceOrExpr];
|
158
|
+
if (otherValue instanceof Expr) {
|
159
|
+
throw Error("TODO: Cannot evaluate equality with an Expr");
|
160
|
+
}
|
161
|
+
}
|
162
|
+
return newSelectorProxy(source === otherValue, undefined);
|
163
|
+
},
|
164
|
+
andThen: (f: any) => {
|
165
|
+
return andThen(f, source === undefined ? null : source, path);
|
166
|
+
},
|
167
|
+
[SourceOrExpr]: source === undefined ? null : source,
|
168
|
+
[Path]: path,
|
169
|
+
};
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
function selectorAsVal(sel: any): any {
|
174
|
+
if (isSerializedVal(sel)) {
|
175
|
+
// is a serialized val
|
176
|
+
return selectorAsVal(newSelectorProxy(sel.val, sel.valPath));
|
177
|
+
} else if (
|
178
|
+
typeof sel === "object" &&
|
179
|
+
sel &&
|
180
|
+
!(SourceOrExpr in sel) &&
|
181
|
+
!Array.isArray(sel)
|
182
|
+
) {
|
183
|
+
// is object
|
184
|
+
return Object.fromEntries(
|
185
|
+
Object.entries(sel).map(([k, v]) => [k, selectorAsVal(v)])
|
186
|
+
);
|
187
|
+
} else if (
|
188
|
+
typeof sel === "object" &&
|
189
|
+
sel &&
|
190
|
+
!(SourceOrExpr in sel) &&
|
191
|
+
Array.isArray(sel)
|
192
|
+
) {
|
193
|
+
// is array
|
194
|
+
return sel.map((v) => selectorAsVal(v));
|
195
|
+
} else if (
|
196
|
+
typeof sel === "object" &&
|
197
|
+
sel &&
|
198
|
+
(SourceOrExpr in sel || Path in sel)
|
199
|
+
) {
|
200
|
+
return selectorAsVal(sel?.[SourceOrExpr]);
|
201
|
+
} else if (sel === undefined) {
|
202
|
+
return null;
|
203
|
+
}
|
204
|
+
return sel;
|
205
|
+
}
|
206
|
+
|
207
|
+
export function createValPathOfItem(
|
208
|
+
arrayPath: SourcePath | undefined,
|
209
|
+
prop: string | number | symbol
|
210
|
+
) {
|
211
|
+
if (typeof prop === "symbol") {
|
212
|
+
throw Error(
|
213
|
+
`Cannot create val path of array item with symbol prop: ${prop.toString()}`
|
214
|
+
);
|
215
|
+
}
|
216
|
+
return arrayPath && (`${arrayPath}.${JSON.stringify(prop)}` as SourcePath);
|
217
|
+
}
|
218
|
+
|
219
|
+
export function selectorToVal(s: any): any {
|
220
|
+
const v = selectorAsVal(s?.[SourceOrExpr]);
|
221
|
+
return {
|
222
|
+
val: v,
|
223
|
+
[Path]: s?.[Path],
|
224
|
+
};
|
225
|
+
}
|
226
|
+
|
227
|
+
// TODO: could we do .val on the objects instead?
|
228
|
+
function unValify(valueOrSelector: any) {
|
229
|
+
if (
|
230
|
+
typeof valueOrSelector === "object" &&
|
231
|
+
(SourceOrExpr in valueOrSelector || Path in valueOrSelector)
|
232
|
+
) {
|
233
|
+
const selectorValue = valueOrSelector[SourceOrExpr];
|
234
|
+
return selectorValue;
|
235
|
+
}
|
236
|
+
return valueOrSelector;
|
237
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import {
|
2
|
+
Selector as UnknownSelector,
|
3
|
+
GenericSelector,
|
4
|
+
SelectorOf,
|
5
|
+
SelectorSource,
|
6
|
+
} from ".";
|
7
|
+
import { Schema } from "../schema";
|
8
|
+
import { Source, SourceArray } from "../source";
|
9
|
+
import { Selector as BooleanSelector } from "./boolean";
|
10
|
+
import { Selector as NumberSelector } from "./number";
|
11
|
+
|
12
|
+
export type UndistributedSourceArray<T extends SourceArray> = [T] extends [
|
13
|
+
infer U // infer here to avoid Type instantiation is excessively deep and possibly infinite. See: https://github.com/microsoft/TypeScript/issues/30188#issuecomment-478938437. Avoiding infer extends to keep us below TS 4.9 compat
|
14
|
+
]
|
15
|
+
? U extends Source[]
|
16
|
+
? Selector<U>
|
17
|
+
: never
|
18
|
+
: never;
|
19
|
+
|
20
|
+
// TODO: docs
|
21
|
+
export type Selector<T extends SourceArray> = GenericSelector<T> & {
|
22
|
+
readonly [key: number]: UnknownSelector<T[number]>;
|
23
|
+
} & {
|
24
|
+
length: NumberSelector<number>;
|
25
|
+
filter(
|
26
|
+
predicate: (
|
27
|
+
v: UnknownSelector<T[number]>
|
28
|
+
) => BooleanSelector<boolean> | boolean
|
29
|
+
): Selector<T>;
|
30
|
+
filter<U extends Source>(schema: Schema<U>): Selector<U[]>;
|
31
|
+
map<U extends SelectorSource>(
|
32
|
+
f: (v: UnknownSelector<T[number]>, i: UnknownSelector<number>) => U
|
33
|
+
): SelectorOf<U[]>; // TODO: this should be SelectorOf<ArraySelectorSourceBranded<U[]>>;
|
34
|
+
andThen<U extends SelectorSource>(
|
35
|
+
f: (v: UnknownSelector<NonNullable<T>>) => U
|
36
|
+
): SelectorOf<U | T>;
|
37
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import {
|
2
|
+
Selector as UnknownSelector,
|
3
|
+
GenericSelector,
|
4
|
+
SelectorOf,
|
5
|
+
SelectorSource,
|
6
|
+
} from ".";
|
7
|
+
|
8
|
+
// TODO: docs
|
9
|
+
export type FileSelector = GenericSelector<{ url: string }> & {
|
10
|
+
readonly url: UnknownSelector<string>;
|
11
|
+
andThen<U extends SelectorSource>(
|
12
|
+
f: (v: UnknownSelector<NonNullable<{ url: string }>>) => U
|
13
|
+
): SelectorOf<U> | UnknownSelector<boolean>;
|
14
|
+
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { SourceArray, SourceObject, SourcePrimitive } from "../source";
|
2
|
+
import { Selector as UnknownSelector } from ".";
|
3
|
+
import { FileSource } from "../source/file";
|
4
|
+
|
5
|
+
declare const brand: unique symbol;
|
6
|
+
|
7
|
+
export type I18nSelector<
|
8
|
+
Locales extends readonly string[],
|
9
|
+
T extends SourcePrimitive | SourceObject | SourceArray | FileSource
|
10
|
+
> = UnknownSelector<T> & {
|
11
|
+
readonly [brand]: "I18nSelector";
|
12
|
+
all(): { [locale in Locales[number]]: UnknownSelector<T> };
|
13
|
+
};
|
@@ -0,0 +1,159 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
2
|
+
import { I18nSelector } from "./i18n";
|
3
|
+
import { Selector as ObjectSelector } from "./object";
|
4
|
+
import { UndistributedSourceArray as ArraySelector } from "./array";
|
5
|
+
import { Selector as NumberSelector } from "./number";
|
6
|
+
import { Selector as StringSelector } from "./string";
|
7
|
+
import { Selector as BooleanSelector } from "./boolean";
|
8
|
+
import { Selector as PrimitiveSelector } from "./primitive";
|
9
|
+
import { FileSelector } from "./file";
|
10
|
+
import { SourcePath } from "../val";
|
11
|
+
import { Source, SourceArray, SourceObject, SourcePrimitive } from "../source";
|
12
|
+
import { Schema } from "../schema";
|
13
|
+
import { Expr } from "../expr/expr";
|
14
|
+
import { RemoteSelector } from "./remote";
|
15
|
+
import { A } from "ts-toolbelt";
|
16
|
+
import { I18nSource, I18nCompatibleSource } from "../source/i18n";
|
17
|
+
import { RemoteCompatibleSource, RemoteSource } from "../source/remote";
|
18
|
+
import { FileSource } from "../source/file";
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Selectors can be used to select parts of a Val module.
|
22
|
+
* Unlike queries, joins, aggregates etc is and will not be supported.
|
23
|
+
*
|
24
|
+
* They are designed to be be used as if they were "normal" JSON data,
|
25
|
+
* though some concessions had to be made because of TypeScript limitations.
|
26
|
+
*
|
27
|
+
* Selectors works equally on source content, defined in code, and remote content.
|
28
|
+
*
|
29
|
+
* @example
|
30
|
+
* // Select the title of a document
|
31
|
+
* const titles = useVal(docsVal.map((doc) => doc.title));
|
32
|
+
*
|
33
|
+
* @example
|
34
|
+
* // Match on a union type
|
35
|
+
* const titles = useVal(docsVal.map((doc) => doc.fold("type")({
|
36
|
+
* newsletter: (newsletter) => newsletter.title,
|
37
|
+
* email: (email) => email.subject,
|
38
|
+
* }));
|
39
|
+
*
|
40
|
+
*/
|
41
|
+
export type Selector<T extends Source> = Source extends T
|
42
|
+
? GenericSelector<T>
|
43
|
+
: T extends I18nSource<infer L, infer S>
|
44
|
+
? I18nSelector<L, S>
|
45
|
+
: T extends RemoteSource<infer S>
|
46
|
+
? S extends RemoteCompatibleSource
|
47
|
+
? RemoteSelector<S>
|
48
|
+
: GenericSelector<Source, "Could not determine remote source">
|
49
|
+
: T extends FileSource
|
50
|
+
? FileSelector
|
51
|
+
: T extends SourceObject
|
52
|
+
? ObjectSelector<T>
|
53
|
+
: T extends SourceArray
|
54
|
+
? ArraySelector<T>
|
55
|
+
: T extends string
|
56
|
+
? StringSelector<T>
|
57
|
+
: T extends number
|
58
|
+
? NumberSelector<T>
|
59
|
+
: T extends boolean
|
60
|
+
? BooleanSelector<T>
|
61
|
+
: T extends null
|
62
|
+
? PrimitiveSelector<null>
|
63
|
+
: never;
|
64
|
+
|
65
|
+
export type SelectorSource =
|
66
|
+
| SourcePrimitive
|
67
|
+
| undefined
|
68
|
+
| readonly SelectorSource[]
|
69
|
+
| {
|
70
|
+
[key: string]: SelectorSource;
|
71
|
+
}
|
72
|
+
| I18nSource<readonly string[], I18nCompatibleSource>
|
73
|
+
| RemoteSource<RemoteCompatibleSource>
|
74
|
+
| FileSource
|
75
|
+
| GenericSelector<Source>;
|
76
|
+
|
77
|
+
/**
|
78
|
+
* @internal
|
79
|
+
*/
|
80
|
+
export const GetSchema = Symbol("GetSchema");
|
81
|
+
/**
|
82
|
+
/**
|
83
|
+
* @internal
|
84
|
+
*/
|
85
|
+
export const Path = Symbol("Path");
|
86
|
+
/**
|
87
|
+
* @internal
|
88
|
+
*/
|
89
|
+
export const SourceOrExpr = Symbol("SourceOrExpr");
|
90
|
+
/**
|
91
|
+
* @internal
|
92
|
+
*/
|
93
|
+
export const ValError = Symbol("ValError");
|
94
|
+
export abstract class GenericSelector<
|
95
|
+
out T extends Source,
|
96
|
+
Error extends string | undefined = undefined
|
97
|
+
> {
|
98
|
+
readonly [Path]: SourcePath | undefined;
|
99
|
+
readonly [SourceOrExpr]: T | Expr;
|
100
|
+
readonly [ValError]: Error | undefined;
|
101
|
+
readonly [GetSchema]: Schema<T> | undefined;
|
102
|
+
constructor(
|
103
|
+
valOrExpr: T,
|
104
|
+
path: SourcePath | undefined,
|
105
|
+
schema?: Schema<T>,
|
106
|
+
error?: Error
|
107
|
+
) {
|
108
|
+
this[Path] = path;
|
109
|
+
this[SourceOrExpr] = valOrExpr;
|
110
|
+
this[ValError] = error;
|
111
|
+
this[GetSchema] = schema;
|
112
|
+
}
|
113
|
+
|
114
|
+
assert<U extends Source, E extends Source = null>(
|
115
|
+
schema: Schema<U>,
|
116
|
+
other?: () => E
|
117
|
+
): SelectorOf<U | E> {
|
118
|
+
throw new Error("Not implemented");
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
export type SourceOf<T extends SelectorSource> = Source extends T
|
123
|
+
? Source
|
124
|
+
: T extends Source
|
125
|
+
? T
|
126
|
+
: T extends undefined
|
127
|
+
? null
|
128
|
+
: T extends GenericSelector<infer S>
|
129
|
+
? S
|
130
|
+
: T extends readonly (infer S)[] // NOTE: the infer S instead of Selector Source here, is to avoid infinite recursion
|
131
|
+
? S extends SelectorSource
|
132
|
+
? {
|
133
|
+
[key in keyof T]: SourceOf<A.Try<T[key], SelectorSource>>;
|
134
|
+
}
|
135
|
+
: never
|
136
|
+
: T extends { [key: string]: SelectorSource }
|
137
|
+
? {
|
138
|
+
[key in keyof T]: SourceOf<A.Try<T[key], SelectorSource>>;
|
139
|
+
}
|
140
|
+
: never;
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Use this type to convert types that accepts both Source and Selectors
|
144
|
+
*
|
145
|
+
* An example would be where literals are supported like in most higher order functions (e.g. map in array)
|
146
|
+
**/
|
147
|
+
export type SelectorOf<U extends SelectorSource> = Source extends U
|
148
|
+
? GenericSelector<Source>
|
149
|
+
: SourceOf<U> extends infer S // we need this to avoid infinite recursion
|
150
|
+
? S extends Source
|
151
|
+
? Selector<S>
|
152
|
+
: GenericSelector<Source, "Could not determine selector of source">
|
153
|
+
: GenericSelector<Source, "Could not determine source">;
|
154
|
+
|
155
|
+
export function getSchema(
|
156
|
+
selector: Selector<Source>
|
157
|
+
): Schema<SelectorSource> | undefined {
|
158
|
+
return selector[GetSchema];
|
159
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import {
|
2
|
+
Selector as UnknownSelector,
|
3
|
+
GenericSelector,
|
4
|
+
SelectorOf,
|
5
|
+
SelectorSource,
|
6
|
+
} from ".";
|
7
|
+
import { SourceObject } from "../source";
|
8
|
+
|
9
|
+
// TODO: docs
|
10
|
+
export type Selector<T extends SourceObject> = GenericSelector<T> & {
|
11
|
+
fold<Tag extends string>(
|
12
|
+
key: Tag
|
13
|
+
): <U extends SelectorSource>(cases: {
|
14
|
+
[key in T[Tag & keyof T] & string]: (v: UnknownSelector<T>) => U;
|
15
|
+
}) => SelectorOf<U>;
|
16
|
+
|
17
|
+
andThen<U extends SelectorSource>(
|
18
|
+
f: (v: UnknownSelector<NonNullable<T>>) => U
|
19
|
+
): SelectorOf<U>;
|
20
|
+
} & {
|
21
|
+
readonly [key in keyof T]: UnknownSelector<T[key]>;
|
22
|
+
};
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import {
|
2
|
+
Selector as UnknownSelector,
|
3
|
+
GenericSelector,
|
4
|
+
SelectorOf,
|
5
|
+
SelectorSource,
|
6
|
+
} from ".";
|
7
|
+
import { Source, SourcePrimitive } from "../source";
|
8
|
+
import { Selector as BooleanSelector } from "./boolean";
|
9
|
+
|
10
|
+
export type Selector<T extends SourcePrimitive> = GenericSelector<T> & {
|
11
|
+
eq(other: Source): BooleanSelector<boolean>;
|
12
|
+
andThen<U extends SelectorSource>(
|
13
|
+
f: (v: UnknownSelector<NonNullable<T>>) => U
|
14
|
+
): SelectorOf<U | NullableOf<T>>;
|
15
|
+
};
|
16
|
+
|
17
|
+
type NullableOf<T extends Source> = T extends null ? null : never;
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { Selector as UnknownSelector } from ".";
|
2
|
+
import { RemoteCompatibleSource } from "../source/remote";
|
3
|
+
|
4
|
+
declare const brand: unique symbol;
|
5
|
+
|
6
|
+
export type RemoteSelector<T extends RemoteCompatibleSource> =
|
7
|
+
UnknownSelector<T> & {
|
8
|
+
readonly [brand]: "RemoteSelector";
|
9
|
+
};
|