@valbuild/core 0.26.0 → 0.27.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/package.json +12 -3
- package/CHANGELOG.md +0 -0
- package/ROADMAP.md +0 -106
- package/jest.config.js +0 -4
- package/src/Json.ts +0 -4
- package/src/ValApi.ts +0 -81
- package/src/expr/README.md +0 -193
- package/src/expr/eval.test.ts +0 -198
- package/src/expr/eval.ts +0 -251
- package/src/expr/expr.ts +0 -91
- package/src/expr/index.ts +0 -3
- package/src/expr/parser.test.ts +0 -158
- package/src/expr/parser.ts +0 -229
- package/src/expr/repl.ts +0 -88
- package/src/expr/tokenizer.test.ts +0 -539
- package/src/expr/tokenizer.ts +0 -117
- package/src/fp/array.ts +0 -30
- package/src/fp/index.ts +0 -3
- package/src/fp/result.ts +0 -214
- package/src/fp/util.ts +0 -52
- package/src/future/fetchVal.test.ts +0 -164
- package/src/future/fetchVal.ts +0 -206
- package/src/getSha256.ts +0 -8
- package/src/index.ts +0 -132
- package/src/initSchema.ts +0 -50
- package/src/initVal.ts +0 -73
- package/src/module.test.ts +0 -170
- package/src/module.ts +0 -397
- package/src/patch/deref.test.ts +0 -298
- package/src/patch/deref.ts +0 -136
- package/src/patch/index.ts +0 -12
- package/src/patch/json.test.ts +0 -582
- package/src/patch/json.ts +0 -304
- package/src/patch/operation.ts +0 -86
- package/src/patch/ops.ts +0 -83
- package/src/patch/parse.test.ts +0 -202
- package/src/patch/parse.ts +0 -202
- package/src/patch/patch.ts +0 -49
- package/src/patch/util.ts +0 -74
- package/src/schema/array.ts +0 -93
- package/src/schema/boolean.ts +0 -49
- package/src/schema/future/i18n.ts +0 -69
- package/src/schema/future/oneOf.ts +0 -63
- package/src/schema/image.ts +0 -137
- package/src/schema/index.ts +0 -70
- package/src/schema/keyOf.ts +0 -167
- package/src/schema/literal.ts +0 -63
- package/src/schema/number.ts +0 -56
- package/src/schema/object.ts +0 -110
- package/src/schema/record.ts +0 -103
- package/src/schema/richtext.ts +0 -44
- package/src/schema/string.ts +0 -95
- package/src/schema/union.ts +0 -63
- package/src/schema/validation/ValidationError.ts +0 -16
- package/src/schema/validation/ValidationFix.ts +0 -6
- package/src/schema/validation.test.ts +0 -291
- package/src/selector/SelectorProxy.ts +0 -238
- package/src/selector/array.ts +0 -13
- package/src/selector/boolean.ts +0 -4
- package/src/selector/file.ts +0 -6
- package/src/selector/future/ExprProxy.test.ts +0 -203
- package/src/selector/future/ExprProxy.ts +0 -216
- package/src/selector/future/SelectorProxy.test.ts +0 -172
- package/src/selector/future/SelectorProxy.ts +0 -238
- package/src/selector/future/array.ts +0 -37
- package/src/selector/future/boolean.ts +0 -4
- package/src/selector/future/file.ts +0 -14
- package/src/selector/future/i18n.ts +0 -13
- package/src/selector/future/index.ts +0 -169
- package/src/selector/future/number.ts +0 -4
- package/src/selector/future/object.ts +0 -22
- package/src/selector/future/primitive.ts +0 -17
- package/src/selector/future/remote.ts +0 -9
- package/src/selector/future/selector.test.ts +0 -429
- package/src/selector/future/selectorOf.ts +0 -7
- package/src/selector/future/string.ts +0 -4
- package/src/selector/index.ts +0 -121
- package/src/selector/number.ts +0 -4
- package/src/selector/object.ts +0 -5
- package/src/selector/primitive.ts +0 -4
- package/src/selector/string.ts +0 -4
- package/src/source/file.ts +0 -45
- package/src/source/future/i18n.ts +0 -60
- package/src/source/future/remote.ts +0 -54
- package/src/source/index.ts +0 -53
- package/src/source/link.ts +0 -14
- package/src/source/richtext.ts +0 -178
- package/src/val/array.ts +0 -10
- package/src/val/index.ts +0 -100
- package/src/val/object.ts +0 -13
- package/src/val/primitive.ts +0 -8
- package/tsconfig.json +0 -8
@@ -1,238 +0,0 @@
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
-
import { Path, GenericSelector, GetSource, GetSchema } from "./index";
|
3
|
-
import { Expr } from "../expr/expr";
|
4
|
-
import { Schema } from "../schema";
|
5
|
-
import { convertFileSource } 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
|
-
(GetSource 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(convertFileSource(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 === GetSource) {
|
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 === GetSource) {
|
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) {
|
100
|
-
return f.assert(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[GetSource];
|
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
|
-
[GetSource]: source === undefined ? null : source,
|
168
|
-
[Path]: path,
|
169
|
-
[GetSchema]: moduleSchema,
|
170
|
-
};
|
171
|
-
}
|
172
|
-
}
|
173
|
-
|
174
|
-
function selectorAsVal(sel: any): any {
|
175
|
-
if (isSerializedVal(sel)) {
|
176
|
-
// is a serialized val
|
177
|
-
return selectorAsVal(newSelectorProxy(sel.val, sel.valPath));
|
178
|
-
} else if (
|
179
|
-
typeof sel === "object" &&
|
180
|
-
sel &&
|
181
|
-
!(GetSource in sel) &&
|
182
|
-
!Array.isArray(sel)
|
183
|
-
) {
|
184
|
-
// is object
|
185
|
-
return Object.fromEntries(
|
186
|
-
Object.entries(sel).map(([k, v]) => [k, selectorAsVal(v)])
|
187
|
-
);
|
188
|
-
} else if (
|
189
|
-
typeof sel === "object" &&
|
190
|
-
sel &&
|
191
|
-
!(GetSource in sel) &&
|
192
|
-
Array.isArray(sel)
|
193
|
-
) {
|
194
|
-
// is array
|
195
|
-
return sel.map((v) => selectorAsVal(v));
|
196
|
-
} else if (
|
197
|
-
typeof sel === "object" &&
|
198
|
-
sel &&
|
199
|
-
(GetSource in sel || Path in sel)
|
200
|
-
) {
|
201
|
-
return selectorAsVal(sel?.[GetSource]);
|
202
|
-
} else if (sel === undefined) {
|
203
|
-
return null;
|
204
|
-
}
|
205
|
-
return sel;
|
206
|
-
}
|
207
|
-
|
208
|
-
export function createValPathOfItem(
|
209
|
-
arrayPath: SourcePath | undefined,
|
210
|
-
prop: string | number | symbol
|
211
|
-
) {
|
212
|
-
if (typeof prop === "symbol") {
|
213
|
-
throw Error(
|
214
|
-
`Cannot create val path of array item with symbol prop: ${prop.toString()}`
|
215
|
-
);
|
216
|
-
}
|
217
|
-
return arrayPath && (`${arrayPath}.${JSON.stringify(prop)}` as SourcePath);
|
218
|
-
}
|
219
|
-
|
220
|
-
export function selectorToVal(s: any): any {
|
221
|
-
const v = selectorAsVal(s?.[GetSource]);
|
222
|
-
return {
|
223
|
-
val: v,
|
224
|
-
[Path]: s?.[Path],
|
225
|
-
};
|
226
|
-
}
|
227
|
-
|
228
|
-
// TODO: could we do .val on the objects instead?
|
229
|
-
function unValify(valueOrSelector: any) {
|
230
|
-
if (
|
231
|
-
typeof valueOrSelector === "object" &&
|
232
|
-
(GetSource in valueOrSelector || Path in valueOrSelector)
|
233
|
-
) {
|
234
|
-
const selectorValue = valueOrSelector[GetSource];
|
235
|
-
return selectorValue;
|
236
|
-
}
|
237
|
-
return valueOrSelector;
|
238
|
-
}
|
package/src/selector/array.ts
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
import { GenericSelector } from ".";
|
2
|
-
import { Source, SourceArray } from "../source";
|
3
|
-
|
4
|
-
export type UndistributedSourceArray<T extends SourceArray> = [T] extends [
|
5
|
-
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
|
6
|
-
]
|
7
|
-
? U extends Source[]
|
8
|
-
? Selector<U>
|
9
|
-
: never
|
10
|
-
: never;
|
11
|
-
|
12
|
-
// TODO: docs
|
13
|
-
export type Selector<T extends SourceArray> = GenericSelector<T>;
|
package/src/selector/boolean.ts
DELETED
package/src/selector/file.ts
DELETED
@@ -1,203 +0,0 @@
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
-
import { convertLiteralProxy, newExprSelectorProxy } from "./ExprProxy";
|
3
|
-
import * as expr from "../../expr/expr";
|
4
|
-
import { GenericSelector, SelectorSource, SourceOrExpr } from ".";
|
5
|
-
import { Source } from "../../source";
|
6
|
-
|
7
|
-
const ExprSelectorTestCases: any[] = [
|
8
|
-
{
|
9
|
-
description: "basic module",
|
10
|
-
input: newExprSelectorProxy<string>(root("/app/foo")),
|
11
|
-
expected: "(val '/app/foo')",
|
12
|
-
},
|
13
|
-
{
|
14
|
-
description: "basic prop",
|
15
|
-
input: newExprSelectorProxy<string[]>(root("/app/foo"))[0],
|
16
|
-
expected: "('0' (val '/app/foo'))",
|
17
|
-
},
|
18
|
-
{
|
19
|
-
description: "noop andThen",
|
20
|
-
input: newExprSelectorProxy<string>(root("/app/foo")).andThen((v) => v),
|
21
|
-
expected: "!(andThen (val '/app/foo') @[0,0])",
|
22
|
-
},
|
23
|
-
{
|
24
|
-
description: "noop map",
|
25
|
-
input: newExprSelectorProxy<string[]>(root("/app/foo")).map((v) => v),
|
26
|
-
expected: "!(map (val '/app/foo') @[0,0])",
|
27
|
-
},
|
28
|
-
{
|
29
|
-
description: "eq string",
|
30
|
-
input: newExprSelectorProxy<string>(root("/app/foo")).eq("hei"),
|
31
|
-
expected: "(eq (val '/app/foo') 'hei')",
|
32
|
-
},
|
33
|
-
{
|
34
|
-
description: "eq undefined",
|
35
|
-
input: newExprSelectorProxy<null>(root("/app/foo")).eq(null),
|
36
|
-
expected: "(eq (val '/app/foo') ())",
|
37
|
-
},
|
38
|
-
{
|
39
|
-
description: "eq number",
|
40
|
-
input: newExprSelectorProxy<number>(root("/app/foo")).eq(1),
|
41
|
-
expected: "(eq (val '/app/foo') (json '1'))",
|
42
|
-
},
|
43
|
-
{
|
44
|
-
description: "eq boolean",
|
45
|
-
input: newExprSelectorProxy<boolean>(root("/app/foo")).eq(true),
|
46
|
-
expected: "(eq (val '/app/foo') (json 'true'))",
|
47
|
-
},
|
48
|
-
{
|
49
|
-
description: "filter string",
|
50
|
-
input: newExprSelectorProxy<string[]>(root("/app/foo")).filter((v) =>
|
51
|
-
v.eq("hei")
|
52
|
-
),
|
53
|
-
expected: "!(filter (val '/app/foo') (eq @[0,0] 'hei'))",
|
54
|
-
},
|
55
|
-
{
|
56
|
-
description: "filter number",
|
57
|
-
input: newExprSelectorProxy<number[]>(root("/app/foo")).filter((v) =>
|
58
|
-
v.eq(1)
|
59
|
-
),
|
60
|
-
expected: "!(filter (val '/app/foo') (eq @[0,0] (json '1')))",
|
61
|
-
},
|
62
|
-
{
|
63
|
-
description: "filter optional",
|
64
|
-
input: newExprSelectorProxy<number[]>(root("/app/foo")).filter((v) =>
|
65
|
-
v.eq(null)
|
66
|
-
),
|
67
|
-
expected: "!(filter (val '/app/foo') (eq @[0,0] ()))",
|
68
|
-
},
|
69
|
-
{
|
70
|
-
description: "basic projection",
|
71
|
-
input: newExprSelectorProxy<string[]>(root("/app/foo")).map((v) => ({
|
72
|
-
foo: v,
|
73
|
-
})),
|
74
|
-
expected: "!(map (val '/app/foo') (json '{\"foo\": ${@[0,0]}}'))",
|
75
|
-
},
|
76
|
-
{
|
77
|
-
description: "nested projection",
|
78
|
-
input: newExprSelectorProxy<string[]>(root("/app/foo")).map((v) => ({
|
79
|
-
foo: {
|
80
|
-
bar: v,
|
81
|
-
},
|
82
|
-
})),
|
83
|
-
expected:
|
84
|
-
// TODO: this could be more readable
|
85
|
-
// Example: "!(map (val '/app/foo') (json '{\"foo\": {\"bar\": {${@[0,0]}}'}'))"
|
86
|
-
"!(map (val '/app/foo') (json '{\"foo\": ${'{\"bar\": ${@[0,0]}}'}}'))",
|
87
|
-
},
|
88
|
-
{
|
89
|
-
description: "multi module",
|
90
|
-
input: newExprSelectorProxy<string>(root("/app/foo")).andThen(() =>
|
91
|
-
newExprSelectorProxy<string[]>(root("/app/bar"))
|
92
|
-
),
|
93
|
-
expected: "!(andThen (val '/app/foo') (val '/app/bar'))",
|
94
|
-
},
|
95
|
-
];
|
96
|
-
|
97
|
-
/**
|
98
|
-
* Useful test cases for literal conversion
|
99
|
-
* There somewhat of an overlap between these cases and the selector cases,
|
100
|
-
* we could prune some of these away if we want to
|
101
|
-
**/
|
102
|
-
const LiteralConversionTestCases: {
|
103
|
-
input: SelectorSource;
|
104
|
-
expected: string;
|
105
|
-
description: string;
|
106
|
-
}[] = [
|
107
|
-
{ description: "basic string", input: "foo", expected: "(json '\"foo\"')" },
|
108
|
-
{ description: "basic number", input: 1, expected: "(json '1')" },
|
109
|
-
{ description: "basic boolean", input: true, expected: "(json 'true')" },
|
110
|
-
{ description: "basic array", input: [1], expected: "(json '[1]')" },
|
111
|
-
{ description: "basic null", input: null, expected: "()" },
|
112
|
-
{
|
113
|
-
description: "array with 2 different",
|
114
|
-
input: [1, "foo"],
|
115
|
-
expected: "(json '[1, \"foo\"]')",
|
116
|
-
},
|
117
|
-
{
|
118
|
-
description: "array with undefined",
|
119
|
-
input: [1, null],
|
120
|
-
expected: "(json '[1, ${()}]')",
|
121
|
-
},
|
122
|
-
{
|
123
|
-
description: "basic object",
|
124
|
-
input: { foo: "one", bar: 1 },
|
125
|
-
expected: '(json \'{"foo": "one", "bar": 1}\')',
|
126
|
-
},
|
127
|
-
{
|
128
|
-
description: "nested array",
|
129
|
-
input: [1, [1]],
|
130
|
-
expected: "(json '[1, [1]]')",
|
131
|
-
},
|
132
|
-
{
|
133
|
-
description: "nested object",
|
134
|
-
input: { foo: "one", bar: { zoo: 1 } },
|
135
|
-
expected: '(json \'{"foo": "one", "bar": {"zoo": 1}}\')',
|
136
|
-
},
|
137
|
-
{
|
138
|
-
description: "nested object with array",
|
139
|
-
input: {
|
140
|
-
foo: "one",
|
141
|
-
bar: { zoo: [1, "inner"] },
|
142
|
-
},
|
143
|
-
expected: '(json \'{"foo": "one", "bar": {"zoo": [1, "inner"]}}\')',
|
144
|
-
},
|
145
|
-
{
|
146
|
-
description: "basic interpolation",
|
147
|
-
input: [1, newExprSelectorProxy<string[]>(root("/app/foo"))],
|
148
|
-
expected: "(json '[1, ${(val '/app/foo')}]')",
|
149
|
-
},
|
150
|
-
{
|
151
|
-
description: "advanced interpolation",
|
152
|
-
input: [
|
153
|
-
123,
|
154
|
-
newExprSelectorProxy<string[]>(root("/app/foo")).map((v) =>
|
155
|
-
v.andThen((a) => ({ bar: a.eq("bar") }))
|
156
|
-
),
|
157
|
-
],
|
158
|
-
expected:
|
159
|
-
"(json '[123, ${!(map (val '/app/foo') !(andThen @[0,0] (json '{\"bar\": ${(eq @[1,0] 'bar')}}')))}]')",
|
160
|
-
},
|
161
|
-
{
|
162
|
-
description: "interpolation with multiple selectors",
|
163
|
-
input: [
|
164
|
-
123,
|
165
|
-
newExprSelectorProxy<string[]>(root("/app/foo")).map((v) =>
|
166
|
-
v.andThen((a) => ({
|
167
|
-
bar: a.eq("bar"),
|
168
|
-
more: newExprSelectorProxy<string[]>(root("/app/foo")),
|
169
|
-
}))
|
170
|
-
),
|
171
|
-
],
|
172
|
-
expected:
|
173
|
-
"(json '[123, ${!(map (val '/app/foo') !(andThen @[0,0] (json '{\"bar\": ${(eq @[1,0] 'bar')}, \"more\": ${(val '/app/foo')}}')))}]')",
|
174
|
-
},
|
175
|
-
];
|
176
|
-
|
177
|
-
describe("expr", () => {
|
178
|
-
test.each(ExprSelectorTestCases)(
|
179
|
-
'expr selector ($description): "$expected"',
|
180
|
-
({ input, expected }) => {
|
181
|
-
const valOrExpr = (input as unknown as GenericSelector<Source>)[
|
182
|
-
SourceOrExpr
|
183
|
-
];
|
184
|
-
if (valOrExpr instanceof expr.Expr) {
|
185
|
-
expect(valOrExpr.transpile()).toBe(expected);
|
186
|
-
} else {
|
187
|
-
expect(valOrExpr).toBe(expect.any(expr.Expr));
|
188
|
-
}
|
189
|
-
}
|
190
|
-
);
|
191
|
-
test.each(LiteralConversionTestCases)(
|
192
|
-
'literal conversion ($description): "$expected"',
|
193
|
-
({ input, expected }) =>
|
194
|
-
expect(convertLiteralProxy(input).transpile()).toBe(expected)
|
195
|
-
);
|
196
|
-
});
|
197
|
-
|
198
|
-
function root(sourcePath: string) {
|
199
|
-
return new expr.Call(
|
200
|
-
[new expr.Sym("val"), new expr.StringLiteral(sourcePath)],
|
201
|
-
false
|
202
|
-
);
|
203
|
-
}
|
@@ -1,216 +0,0 @@
|
|
1
|
-
import { Selector, SelectorSource, SourceOrExpr } from ".";
|
2
|
-
import * as expr from "../../expr/expr";
|
3
|
-
import { Source, SourcePrimitive } from "../../source";
|
4
|
-
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
6
|
-
type AnyFun = (...args: any[]) => any;
|
7
|
-
|
8
|
-
export function newExprSelectorProxy<T extends Source>(
|
9
|
-
root: expr.Expr,
|
10
|
-
depth = 0
|
11
|
-
): Selector<T> {
|
12
|
-
return new Proxy(new GenericExprSelector(root, depth), {
|
13
|
-
get: (target, prop) => {
|
14
|
-
if (prop === SourceOrExpr) {
|
15
|
-
return target[SourceOrExpr];
|
16
|
-
}
|
17
|
-
if (!hasOwn(target, prop)) {
|
18
|
-
return newExprSelectorProxy(
|
19
|
-
new expr.Call([new expr.StringLiteral(prop.toString()), root], false),
|
20
|
-
depth
|
21
|
-
);
|
22
|
-
}
|
23
|
-
return Reflect.get(target, prop);
|
24
|
-
},
|
25
|
-
}) as unknown as Selector<T>;
|
26
|
-
}
|
27
|
-
|
28
|
-
export function root(sourcePath: string) {
|
29
|
-
return new expr.Call(
|
30
|
-
[new expr.Sym("val"), new expr.StringLiteral(sourcePath)],
|
31
|
-
false
|
32
|
-
);
|
33
|
-
}
|
34
|
-
|
35
|
-
class GenericExprSelector {
|
36
|
-
[SourceOrExpr]: expr.Expr;
|
37
|
-
constructor(private readonly root: expr.Expr, private readonly depth = 0) {
|
38
|
-
this[SourceOrExpr] = root;
|
39
|
-
}
|
40
|
-
|
41
|
-
andThen = (f: AnyFun) => {
|
42
|
-
return genericHigherOrderFunction(this.root, "andThen", f, 1, this.depth);
|
43
|
-
};
|
44
|
-
|
45
|
-
map = (f: AnyFun) => {
|
46
|
-
return genericHigherOrderFunction(this.root, "map", f, 2, this.depth);
|
47
|
-
};
|
48
|
-
|
49
|
-
filter = (f: AnyFun) => {
|
50
|
-
return genericHigherOrderFunction(this.root, "filter", f, 1, this.depth);
|
51
|
-
};
|
52
|
-
|
53
|
-
eq = (other: SourcePrimitive) => {
|
54
|
-
return newExprSelectorProxy(
|
55
|
-
new expr.Call(
|
56
|
-
[
|
57
|
-
new expr.Sym("eq"),
|
58
|
-
this.root,
|
59
|
-
typeof other === "string"
|
60
|
-
? new expr.StringLiteral(other)
|
61
|
-
: typeof other === "undefined"
|
62
|
-
? expr.NilSym
|
63
|
-
: convertLiteralProxy(other),
|
64
|
-
],
|
65
|
-
false
|
66
|
-
),
|
67
|
-
this.depth
|
68
|
-
);
|
69
|
-
};
|
70
|
-
}
|
71
|
-
|
72
|
-
function genericHigherOrderFunction(
|
73
|
-
root: expr.Expr,
|
74
|
-
name: string,
|
75
|
-
f: AnyFun,
|
76
|
-
args: number,
|
77
|
-
depth: number
|
78
|
-
) {
|
79
|
-
const argsExprs: Selector<Source>[] = [];
|
80
|
-
for (let i = 0; i < args; i++) {
|
81
|
-
argsExprs.push(
|
82
|
-
newExprSelectorProxy(new expr.Sym(`@[${depth},${i}]`), depth + 1)
|
83
|
-
);
|
84
|
-
}
|
85
|
-
return newExprSelectorProxy(
|
86
|
-
new expr.Call(
|
87
|
-
[new expr.Sym(name), root, convertLiteralProxy(f(...argsExprs))],
|
88
|
-
true
|
89
|
-
),
|
90
|
-
depth
|
91
|
-
);
|
92
|
-
}
|
93
|
-
|
94
|
-
function hasOwn<T extends PropertyKey>(obj: object, prop: T): boolean {
|
95
|
-
return Object.prototype.hasOwnProperty.call(obj, prop);
|
96
|
-
}
|
97
|
-
|
98
|
-
export function convertLiteralProxy(
|
99
|
-
source: expr.Expr | SelectorSource
|
100
|
-
): expr.Expr {
|
101
|
-
const [convertedLiteral] = convertObjectToStringExpr(source, true);
|
102
|
-
return withJsonCall(convertedLiteral, false)[0];
|
103
|
-
}
|
104
|
-
|
105
|
-
/**
|
106
|
-
* Add a json call if the literal must be parsed
|
107
|
-
* Happens at the very least at the top level, but may also happen inside e.g. a string template
|
108
|
-
*
|
109
|
-
*/
|
110
|
-
function withJsonCall(
|
111
|
-
e: expr.Expr,
|
112
|
-
isLiteralScope: boolean
|
113
|
-
): [e: expr.Expr, isJson: boolean] {
|
114
|
-
if (
|
115
|
-
!isLiteralScope &&
|
116
|
-
(e instanceof expr.StringLiteral || e instanceof expr.StringTemplate)
|
117
|
-
) {
|
118
|
-
return [new expr.Call([new expr.Sym("json"), e], false), true];
|
119
|
-
}
|
120
|
-
return [e, false];
|
121
|
-
}
|
122
|
-
|
123
|
-
function convertObjectToStringExpr(
|
124
|
-
source: expr.Expr | SelectorSource,
|
125
|
-
isLiteralScope: boolean
|
126
|
-
): [e: expr.Expr, isJson: boolean] {
|
127
|
-
if (source === null || source === undefined) {
|
128
|
-
return [expr.NilSym, true];
|
129
|
-
} else if (typeof source === "string") {
|
130
|
-
return [new expr.StringLiteral(JSON.stringify(source)), false];
|
131
|
-
} else if (typeof source === "number" || typeof source === "boolean") {
|
132
|
-
return withJsonCall(
|
133
|
-
new expr.StringLiteral(source.toString()),
|
134
|
-
isLiteralScope
|
135
|
-
);
|
136
|
-
} else if (typeof source === "object" && SourceOrExpr in source) {
|
137
|
-
const selector = source;
|
138
|
-
const valOrExpr = selector[SourceOrExpr];
|
139
|
-
if (valOrExpr instanceof expr.Expr) {
|
140
|
-
return [valOrExpr, true];
|
141
|
-
} else {
|
142
|
-
// use Val literal - UNTESTED - may happen if we are referencing local content
|
143
|
-
return convertObjectToStringExpr(valOrExpr, isLiteralScope);
|
144
|
-
}
|
145
|
-
} else if (source instanceof expr.Expr) {
|
146
|
-
return [source, true];
|
147
|
-
} else if (typeof source === "object") {
|
148
|
-
// source is a literal object or array, might have nested selectors
|
149
|
-
const isArray = Array.isArray(source);
|
150
|
-
const entries = isArray
|
151
|
-
? Array.from(source.entries())
|
152
|
-
: Object.entries(source);
|
153
|
-
|
154
|
-
let isStringTemplate = false;
|
155
|
-
const converted = entries.map(([key, v]) => {
|
156
|
-
const [converted, mustInterpolate] =
|
157
|
-
typeof v === "string"
|
158
|
-
? withJsonCall(
|
159
|
-
new expr.StringLiteral(JSON.stringify(v)),
|
160
|
-
isLiteralScope
|
161
|
-
)
|
162
|
-
: convertObjectToStringExpr(v, isLiteralScope);
|
163
|
-
|
164
|
-
isStringTemplate = isStringTemplate || mustInterpolate;
|
165
|
-
return [key, converted] as const;
|
166
|
-
});
|
167
|
-
|
168
|
-
if (!isStringTemplate) {
|
169
|
-
const value = isArray
|
170
|
-
? `[${converted.map(([, v]) => getStringLiteralValue(v)).join(", ")}]`
|
171
|
-
: `{${converted
|
172
|
-
.map(
|
173
|
-
([key, v]) =>
|
174
|
-
`${JSON.stringify(key)}: ${getStringLiteralValue(v)}`
|
175
|
-
)
|
176
|
-
.join(", ")}}`;
|
177
|
-
|
178
|
-
return [new expr.StringLiteral(value), false];
|
179
|
-
}
|
180
|
-
|
181
|
-
return [
|
182
|
-
new expr.StringTemplate([
|
183
|
-
isArray ? new expr.StringLiteral("[") : new expr.StringLiteral("{"),
|
184
|
-
...converted.flatMap(([key, entry], i) => {
|
185
|
-
const convertedEntry =
|
186
|
-
typeof entry === "string"
|
187
|
-
? new expr.StringLiteral(JSON.stringify(entry))
|
188
|
-
: convertObjectToStringExpr(entry, isLiteralScope)[0];
|
189
|
-
const maybeComma =
|
190
|
-
i < converted.length - 1 ? [new expr.StringLiteral(", ")] : [];
|
191
|
-
if (isArray) {
|
192
|
-
return [convertedEntry].concat(maybeComma);
|
193
|
-
} else {
|
194
|
-
return [
|
195
|
-
new expr.StringLiteral(JSON.stringify(key) + ": "),
|
196
|
-
convertedEntry,
|
197
|
-
].concat(maybeComma);
|
198
|
-
}
|
199
|
-
}),
|
200
|
-
isArray ? new expr.StringLiteral("]") : new expr.StringLiteral("}"),
|
201
|
-
]),
|
202
|
-
true,
|
203
|
-
];
|
204
|
-
}
|
205
|
-
throw new Error(
|
206
|
-
`Unsupported type '${typeof source}': ${JSON.stringify(source)}`
|
207
|
-
);
|
208
|
-
}
|
209
|
-
|
210
|
-
function getStringLiteralValue(e: expr.Expr): string {
|
211
|
-
if (e instanceof expr.StringLiteral) {
|
212
|
-
return e.value;
|
213
|
-
} else {
|
214
|
-
throw new Error(`expected a string literal but found: ${e.transpile()}`);
|
215
|
-
}
|
216
|
-
}
|