@codehz/json-expr 0.5.0 → 0.5.2
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 +150 -1
- package/dist/index.d.mts +70 -3
- package/dist/index.mjs +337 -421
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ bun install @codehz/json-expr
|
|
|
28
28
|
### 基本用法
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
|
-
import { variable, expr, compile, evaluate, t, lambda } from "@codehz/json-expr";
|
|
31
|
+
import { variable, expr, compile, evaluate, t, lambda, wrap } from "@codehz/json-expr";
|
|
32
32
|
|
|
33
33
|
// 定义类型化变量(使用 TypeScript 泛型)
|
|
34
34
|
const x = variable<number>();
|
|
@@ -54,6 +54,11 @@ const greeting = t`Hello, ${name}!`;
|
|
|
54
54
|
// 使用 lambda 表达式
|
|
55
55
|
const numbers = variable<number[]>();
|
|
56
56
|
const doubled = numbers.map(lambda<[number], number>((n) => expr({ n })("n * 2")));
|
|
57
|
+
|
|
58
|
+
// 使用 wrap 包装静态值
|
|
59
|
+
const pattern = wrap(/^[a-z]+$/i);
|
|
60
|
+
const input = variable<string>();
|
|
61
|
+
const isValid = pattern.test(input);
|
|
57
62
|
```
|
|
58
63
|
|
|
59
64
|
## 核心概念
|
|
@@ -260,8 +265,152 @@ const multiplier = variable<number>();
|
|
|
260
265
|
const scaled = numbers.map(lambda<[number], number>((n) => expr({ n, multiplier })("n * multiplier")));
|
|
261
266
|
```
|
|
262
267
|
|
|
268
|
+
### `wrap<T>(value: T): Proxify<T>`
|
|
269
|
+
|
|
270
|
+
将静态值包装为 Proxy Expression,使其可以像 Variable 一样调用方法和访问属性。
|
|
271
|
+
|
|
272
|
+
**参数:**
|
|
273
|
+
|
|
274
|
+
- `value` - 要包装的静态值(支持原始值、对象、数组、Date、RegExp、BigInt、URL、Map、Set、TypedArray 等)
|
|
275
|
+
|
|
276
|
+
**返回值:** Proxy Expression,可以继续链式调用
|
|
277
|
+
|
|
278
|
+
**示例:**
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// 包装 RegExp
|
|
282
|
+
const pattern = wrap(/^[a-z]+$/i);
|
|
283
|
+
const input = variable<string>();
|
|
284
|
+
const isValid = pattern.test(input);
|
|
285
|
+
|
|
286
|
+
const compiled = compile(isValid, { input });
|
|
287
|
+
evaluate(compiled, { input: "hello" }); // => true
|
|
288
|
+
evaluate(compiled, { input: "hello123" }); // => false
|
|
289
|
+
|
|
290
|
+
// 包装 Date
|
|
291
|
+
const now = wrap(new Date("2024-01-01"));
|
|
292
|
+
const year = now.getFullYear();
|
|
293
|
+
|
|
294
|
+
// 包装数组
|
|
295
|
+
const staticNumbers = wrap([1, 2, 3, 4, 5]);
|
|
296
|
+
const x = variable<number>();
|
|
297
|
+
const doubled = staticNumbers.map(lambda((n: number) => expr({ n, x })("n * x")));
|
|
298
|
+
|
|
299
|
+
// 包装对象
|
|
300
|
+
const config = wrap({ port: 8080, host: "localhost" });
|
|
301
|
+
const port = config.port; // 直接访问属性
|
|
302
|
+
|
|
303
|
+
// 包装 Map
|
|
304
|
+
const map = wrap(
|
|
305
|
+
new Map([
|
|
306
|
+
["a", 1],
|
|
307
|
+
["b", 2],
|
|
308
|
+
])
|
|
309
|
+
);
|
|
310
|
+
const key = variable<string>();
|
|
311
|
+
const value = map.get(key);
|
|
312
|
+
|
|
313
|
+
// 链式调用
|
|
314
|
+
const text = wrap(" hello world ");
|
|
315
|
+
const result = text.trim().toUpperCase().replace("HELLO", "HI");
|
|
316
|
+
// => "HI WORLD"
|
|
317
|
+
```
|
|
318
|
+
|
|
263
319
|
## 高级用法
|
|
264
320
|
|
|
321
|
+
### 包装静态值(wrap)
|
|
322
|
+
|
|
323
|
+
`wrap()` 函数可以将任意静态值转换为 Proxy Expression,使其可以像 Variable 一样调用方法和访问属性。这在需要对常量值执行操作时非常有用。
|
|
324
|
+
|
|
325
|
+
**基本用法:**
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// 不使用 wrap(传统方式)
|
|
329
|
+
interface Validator {
|
|
330
|
+
match(text: string, pattern: RegExp): boolean;
|
|
331
|
+
}
|
|
332
|
+
const validator = variable<Validator>();
|
|
333
|
+
const result = validator.match("hello", /^[a-z]+$/i);
|
|
334
|
+
|
|
335
|
+
// 使用 wrap(推荐方式)
|
|
336
|
+
const pattern = wrap(/^[a-z]+$/i);
|
|
337
|
+
const input = variable<string>();
|
|
338
|
+
const result = pattern.test(input);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**支持的类型:**
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
// 原始值
|
|
345
|
+
const num = wrap(42);
|
|
346
|
+
const str = wrap("hello");
|
|
347
|
+
const bool = wrap(true);
|
|
348
|
+
|
|
349
|
+
// Date 和 RegExp
|
|
350
|
+
const date = wrap(new Date("2024-01-01"));
|
|
351
|
+
const year = date.getFullYear();
|
|
352
|
+
|
|
353
|
+
const regex = wrap(/\d+/g);
|
|
354
|
+
const text = variable<string>();
|
|
355
|
+
const matches = text.match(regex);
|
|
356
|
+
|
|
357
|
+
// BigInt
|
|
358
|
+
const bigNum = wrap(123456789n);
|
|
359
|
+
const x = variable<bigint>();
|
|
360
|
+
const sum = expr({ bigNum, x })("bigNum + x");
|
|
361
|
+
|
|
362
|
+
// URL
|
|
363
|
+
const url = wrap(new URL("https://example.com/path"));
|
|
364
|
+
const host = url.hostname;
|
|
365
|
+
const port = url.port;
|
|
366
|
+
|
|
367
|
+
// Map 和 Set
|
|
368
|
+
const map = wrap(
|
|
369
|
+
new Map([
|
|
370
|
+
["key1", 100],
|
|
371
|
+
["key2", 200],
|
|
372
|
+
])
|
|
373
|
+
);
|
|
374
|
+
const key = variable<string>();
|
|
375
|
+
const value = map.get(key);
|
|
376
|
+
|
|
377
|
+
const set = wrap(new Set([1, 2, 3]));
|
|
378
|
+
const num = variable<number>();
|
|
379
|
+
const has = set.has(num);
|
|
380
|
+
|
|
381
|
+
// TypedArray
|
|
382
|
+
const arr = wrap(new Uint8Array([10, 20, 30]));
|
|
383
|
+
const index = variable<number>();
|
|
384
|
+
const value = expr({ arr, index })("arr[index]");
|
|
385
|
+
|
|
386
|
+
// 数组和对象
|
|
387
|
+
const numbers = wrap([1, 2, 3, 4, 5]);
|
|
388
|
+
const multiplier = variable<number>();
|
|
389
|
+
const scaled = numbers.map(lambda((n: number) => expr({ n, multiplier })("n * multiplier")));
|
|
390
|
+
|
|
391
|
+
const config = wrap({ port: 8080, host: "localhost" });
|
|
392
|
+
const port = config.port;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**链式调用:**
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
const text = wrap(" Hello, World! ");
|
|
399
|
+
const result = text.trim().toLowerCase().replace("world", "universe");
|
|
400
|
+
// => "hello, universe!"
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**与 variable 结合:**
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
const staticData = wrap({ users: ["alice", "bob", "charlie"] });
|
|
407
|
+
const index = variable<number>();
|
|
408
|
+
const username = expr({ staticData, index })("staticData.users[index]");
|
|
409
|
+
|
|
410
|
+
const compiled = compile(username, { index });
|
|
411
|
+
evaluate(compiled, { index: 1 }); // => "bob"
|
|
412
|
+
```
|
|
413
|
+
|
|
265
414
|
### Proxy 变量系统
|
|
266
415
|
|
|
267
416
|
`variable()` 创建的变量是 Proxy 对象,支持链式属性访问和方法调用,所有操作都会自动转换为表达式。
|
package/dist/index.d.mts
CHANGED
|
@@ -15,6 +15,45 @@ type ProxifyArgs<T extends unknown[]> = { [K in keyof T]: T[K] | Proxify<T[K]> }
|
|
|
15
15
|
* 需要特殊处理的数组方法名(有泛型参数,需要显式定义以保留类型推导)
|
|
16
16
|
*/
|
|
17
17
|
type ProxifiedArrayMethods = "map" | "flatMap" | "filter" | "reduce" | "reduceRight" | "find" | "findIndex" | "findLast" | "findLastIndex" | "every" | "some" | "forEach" | "toSorted" | "sort";
|
|
18
|
+
/**
|
|
19
|
+
* 字符串类型的 Proxify 版本
|
|
20
|
+
* 将所有字符串方法返回值包装为 Proxify
|
|
21
|
+
* 参数允许为 Proxy 或原始值
|
|
22
|
+
*/
|
|
23
|
+
type ProxifiedString = {
|
|
24
|
+
charAt(pos: number | Proxify<number>): Proxify<string>;
|
|
25
|
+
charCodeAt(index: number | Proxify<number>): Proxify<number>;
|
|
26
|
+
concat(...strings: (string | Proxify<string>)[]): Proxify<string>;
|
|
27
|
+
indexOf(searchString: string | Proxify<string>, position?: number | Proxify<number>): Proxify<number>;
|
|
28
|
+
lastIndexOf(searchString: string | Proxify<string>, position?: number | Proxify<number>): Proxify<number>;
|
|
29
|
+
localeCompare(that: string | Proxify<string>): Proxify<number>;
|
|
30
|
+
match(regexp: string | RegExp | Proxify<string> | Proxify<RegExp>): Proxify<RegExpMatchArray | null>;
|
|
31
|
+
replace(searchValue: string | RegExp | Proxify<string> | Proxify<RegExp>, replaceValue: string | Proxify<string> | Proxify<(substring: string, ...args: unknown[]) => string>): Proxify<string>;
|
|
32
|
+
replaceAll(searchValue: string | RegExp | Proxify<string> | Proxify<RegExp>, replaceValue: string | Proxify<string> | Proxify<(substring: string, ...args: unknown[]) => string>): Proxify<string>;
|
|
33
|
+
search(regexp: string | RegExp | Proxify<string> | Proxify<RegExp>): Proxify<number>;
|
|
34
|
+
slice(start?: number | Proxify<number>, end?: number | Proxify<number>): Proxify<string>;
|
|
35
|
+
split(separator: string | RegExp | Proxify<string> | Proxify<RegExp>, limit?: number | Proxify<number>): Proxify<string[]>;
|
|
36
|
+
substring(start: number | Proxify<number>, end?: number | Proxify<number>): Proxify<string>;
|
|
37
|
+
toLowerCase(): Proxify<string>;
|
|
38
|
+
toLocaleLowerCase(): Proxify<string>;
|
|
39
|
+
toUpperCase(): Proxify<string>;
|
|
40
|
+
toLocaleUpperCase(): Proxify<string>;
|
|
41
|
+
trim(): Proxify<string>;
|
|
42
|
+
trimStart(): Proxify<string>;
|
|
43
|
+
trimEnd(): Proxify<string>;
|
|
44
|
+
padStart(maxLength: number | Proxify<number>, fillString?: string | Proxify<string>): Proxify<string>;
|
|
45
|
+
padEnd(maxLength: number | Proxify<number>, fillString?: string | Proxify<string>): Proxify<string>;
|
|
46
|
+
repeat(count: number | Proxify<number>): Proxify<string>;
|
|
47
|
+
startsWith(searchString: string | Proxify<string>, position?: number | Proxify<number>): Proxify<boolean>;
|
|
48
|
+
endsWith(searchString: string | Proxify<string>, endPosition?: number | Proxify<number>): Proxify<boolean>;
|
|
49
|
+
includes(searchString: string | Proxify<string>, position?: number | Proxify<number>): Proxify<boolean>;
|
|
50
|
+
at(index: number | Proxify<number>): Proxify<string | undefined>;
|
|
51
|
+
codePointAt(pos: number | Proxify<number>): Proxify<number | undefined>;
|
|
52
|
+
normalize(form?: string | Proxify<string>): Proxify<string>;
|
|
53
|
+
matchAll(regexp: RegExp | Proxify<RegExp>): Proxify<IterableIterator<RegExpMatchArray>>;
|
|
54
|
+
readonly length: Proxify<number>;
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
};
|
|
18
57
|
/**
|
|
19
58
|
* 数组类型的 Proxify 版本
|
|
20
59
|
* 特殊处理泛型方法(map, filter 等),确保返回值类型正确
|
|
@@ -45,11 +84,12 @@ type ProxifiedArray<T> = {
|
|
|
45
84
|
* 将类型 T 转换为 Proxy 包装类型
|
|
46
85
|
* - 始终包含 ProxyExpression<T> 标记,用于 compile 函数类型检查
|
|
47
86
|
* - 函数类型:保持函数签名,但参数允许 Proxy 或原始值,返回值递归应用 Proxify
|
|
87
|
+
* - 字符串类型:使用 ProxifiedString 特殊处理字符串方法
|
|
48
88
|
* - 数组类型:使用 ProxifiedArray 特殊处理泛型方法
|
|
49
89
|
* - 对象类型:映射所有属性为 Proxify
|
|
50
90
|
* - 原始类型:保持不变(返回 ProxyExpression 包装)
|
|
51
91
|
*/
|
|
52
|
-
type Proxify<T> = ProxyExpression<T> & (T extends ((...args: infer Args) => infer R) ? (...args: ProxifyArgs<Args>) => Proxify<R> : T extends readonly (infer E)[] ? ProxifiedArray<E> : T extends object ? { [K in keyof T]: Proxify<T[K]> } : unknown);
|
|
92
|
+
type Proxify<T> = ProxyExpression<T> & (T extends string ? ProxifiedString : T extends ((...args: infer Args) => infer R) ? (...args: ProxifyArgs<Args>) => Proxify<R> : T extends readonly (infer E)[] ? ProxifiedArray<E> : T extends object ? { [K in keyof T]: Proxify<T[K]> } : unknown);
|
|
53
93
|
/**
|
|
54
94
|
* Variable 类型定义
|
|
55
95
|
* 总是返回 Proxy 包装后的类型
|
|
@@ -234,7 +274,7 @@ declare function compile<TResult>(expression: LambdaBodyResult<TResult>, variabl
|
|
|
234
274
|
* // => 6 (3 * 2)
|
|
235
275
|
* ```
|
|
236
276
|
*/
|
|
237
|
-
declare function evaluate<TResult>(data: CompiledData, values: Record<string, unknown>): TResult;
|
|
277
|
+
declare function evaluate<TResult = unknown>(data: CompiledData, values: Record<string, unknown>): TResult;
|
|
238
278
|
//#endregion
|
|
239
279
|
//#region src/type-parser.d.ts
|
|
240
280
|
/** 去除字符串两端空白 */
|
|
@@ -510,5 +550,32 @@ declare function variable<T>(): Variable<T>;
|
|
|
510
550
|
*/
|
|
511
551
|
declare function getVariableId(variable: unknown): symbol | undefined;
|
|
512
552
|
//#endregion
|
|
513
|
-
|
|
553
|
+
//#region src/wrap.d.ts
|
|
554
|
+
/**
|
|
555
|
+
* 将静态值包装为 Proxy Expression
|
|
556
|
+
* 返回的 Proxy 可以像 Variable 一样调用方法和访问属性
|
|
557
|
+
*
|
|
558
|
+
* @template T - 值的类型
|
|
559
|
+
* @param value - 要包装的静态值(支持原始值、对象、数组、Date、RegExp 等)
|
|
560
|
+
* @returns Proxy Expression,可以继续链式调用
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* // 包装 RegExp
|
|
565
|
+
* const pattern = wrap(/^[a-z]+$/i);
|
|
566
|
+
* const input = variable<string>();
|
|
567
|
+
* const result = pattern.match(input);
|
|
568
|
+
*
|
|
569
|
+
* // 包装 Date
|
|
570
|
+
* const now = wrap(new Date());
|
|
571
|
+
* const year = now.getFullYear();
|
|
572
|
+
*
|
|
573
|
+
* // 包装数组
|
|
574
|
+
* const numbers = wrap([1, 2, 3, 4, 5]);
|
|
575
|
+
* const doubled = numbers.map((x) => x * 2);
|
|
576
|
+
* ```
|
|
577
|
+
*/
|
|
578
|
+
declare function wrap<T>(value: T): Proxify<T>;
|
|
579
|
+
//#endregion
|
|
580
|
+
export { type BranchNode, type CompileContext, type CompileOptions, type CompiledData, type CompiledExpression, type ContextTypeMap, type ControlFlowNode, type ExprNode, type Expression, type ExpressionType, type ExtractType, type FilterCallback, type FindCallback, type InferContextType, type InferExpressionResult, type InferExpressionType, type InferLambdaArgs, type InferLambdaReturn, type InferVariableType, type JumpNode, type Lambda, type LambdaBuilder, type LambdaParam, type MapCallback, type ParseExpression, type PhiNode, type Proxify, type ProxyExpression, type ReduceCallback, type SortCallback, type ValidateExpression, type Variable, compile, evaluate, expr, getVariableId, isProxy, isProxyExpression, isProxyVariable, lambda, t, variable, wrap };
|
|
514
581
|
//# sourceMappingURL=index.d.mts.map
|