@player-tools/fluent 0.12.1--canary.241.6077
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/dist/cjs/index.cjs +2396 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +2276 -0
- package/dist/index.mjs +2276 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +38 -0
- package/src/core/base-builder/__tests__/fluent-builder-base.test.ts +2423 -0
- package/src/core/base-builder/__tests__/fluent-partial.test.ts +179 -0
- package/src/core/base-builder/__tests__/id-generator.test.ts +658 -0
- package/src/core/base-builder/__tests__/registry.test.ts +534 -0
- package/src/core/base-builder/__tests__/resolution-mixed-arrays.test.ts +319 -0
- package/src/core/base-builder/__tests__/resolution-pipeline.test.ts +416 -0
- package/src/core/base-builder/__tests__/resolution-switches.test.ts +468 -0
- package/src/core/base-builder/__tests__/resolution-templates.test.ts +255 -0
- package/src/core/base-builder/__tests__/switch.test.ts +815 -0
- package/src/core/base-builder/__tests__/template.test.ts +596 -0
- package/src/core/base-builder/__tests__/value-extraction.test.ts +200 -0
- package/src/core/base-builder/__tests__/value-storage.test.ts +459 -0
- package/src/core/base-builder/conditional/index.ts +64 -0
- package/src/core/base-builder/context.ts +152 -0
- package/src/core/base-builder/errors.ts +69 -0
- package/src/core/base-builder/fluent-builder-base.ts +308 -0
- package/src/core/base-builder/guards.ts +137 -0
- package/src/core/base-builder/id/generator.ts +290 -0
- package/src/core/base-builder/id/registry.ts +152 -0
- package/src/core/base-builder/index.ts +72 -0
- package/src/core/base-builder/resolution/path-resolver.ts +116 -0
- package/src/core/base-builder/resolution/pipeline.ts +103 -0
- package/src/core/base-builder/resolution/steps/__tests__/nested-asset-wrappers.test.ts +206 -0
- package/src/core/base-builder/resolution/steps/asset-id.ts +77 -0
- package/src/core/base-builder/resolution/steps/asset-wrappers.ts +64 -0
- package/src/core/base-builder/resolution/steps/builders.ts +84 -0
- package/src/core/base-builder/resolution/steps/mixed-arrays.ts +95 -0
- package/src/core/base-builder/resolution/steps/nested-asset-wrappers.ts +124 -0
- package/src/core/base-builder/resolution/steps/static-values.ts +35 -0
- package/src/core/base-builder/resolution/steps/switches.ts +71 -0
- package/src/core/base-builder/resolution/steps/templates.ts +40 -0
- package/src/core/base-builder/resolution/value-resolver.ts +333 -0
- package/src/core/base-builder/storage/auxiliary-storage.ts +82 -0
- package/src/core/base-builder/storage/value-storage.ts +282 -0
- package/src/core/base-builder/types.ts +266 -0
- package/src/core/base-builder/utils.ts +10 -0
- package/src/core/flow/__tests__/index.test.ts +292 -0
- package/src/core/flow/index.ts +118 -0
- package/src/core/index.ts +8 -0
- package/src/core/mocks/generated/action.builder.ts +92 -0
- package/src/core/mocks/generated/choice-item.builder.ts +120 -0
- package/src/core/mocks/generated/choice.builder.ts +134 -0
- package/src/core/mocks/generated/collection.builder.ts +93 -0
- package/src/core/mocks/generated/field-collection.builder.ts +86 -0
- package/src/core/mocks/generated/index.ts +10 -0
- package/src/core/mocks/generated/info.builder.ts +64 -0
- package/src/core/mocks/generated/input.builder.ts +63 -0
- package/src/core/mocks/generated/overview-collection.builder.ts +65 -0
- package/src/core/mocks/generated/splash-collection.builder.ts +93 -0
- package/src/core/mocks/generated/text.builder.ts +47 -0
- package/src/core/mocks/index.ts +1 -0
- package/src/core/mocks/types/action.ts +92 -0
- package/src/core/mocks/types/choice.ts +129 -0
- package/src/core/mocks/types/collection.ts +140 -0
- package/src/core/mocks/types/info.ts +7 -0
- package/src/core/mocks/types/input.ts +7 -0
- package/src/core/mocks/types/text.ts +5 -0
- package/src/core/schema/__tests__/index.test.ts +127 -0
- package/src/core/schema/index.ts +195 -0
- package/src/core/schema/types.ts +7 -0
- package/src/core/switch/__tests__/index.test.ts +156 -0
- package/src/core/switch/index.ts +81 -0
- package/src/core/tagged-template/README.md +448 -0
- package/src/core/tagged-template/__tests__/extract-bindings-from-schema.test.ts +207 -0
- package/src/core/tagged-template/__tests__/index.test.ts +190 -0
- package/src/core/tagged-template/__tests__/schema-std-integration.test.ts +580 -0
- package/src/core/tagged-template/binding.ts +95 -0
- package/src/core/tagged-template/expression.ts +92 -0
- package/src/core/tagged-template/extract-bindings-from-schema.ts +120 -0
- package/src/core/tagged-template/index.ts +5 -0
- package/src/core/tagged-template/std.ts +472 -0
- package/src/core/tagged-template/types.ts +123 -0
- package/src/core/template/__tests__/index.test.ts +380 -0
- package/src/core/template/index.ts +196 -0
- package/src/core/utils/index.ts +160 -0
- package/src/fp/README.md +411 -0
- package/src/fp/__tests__/index.test.ts +1178 -0
- package/src/fp/index.ts +386 -0
- package/src/gen/common.ts +15 -0
- package/src/index.ts +5 -0
- package/src/types.ts +203 -0
- package/types/core/base-builder/conditional/index.d.ts +21 -0
- package/types/core/base-builder/context.d.ts +39 -0
- package/types/core/base-builder/errors.d.ts +45 -0
- package/types/core/base-builder/fluent-builder-base.d.ts +147 -0
- package/types/core/base-builder/guards.d.ts +58 -0
- package/types/core/base-builder/id/generator.d.ts +69 -0
- package/types/core/base-builder/id/registry.d.ts +93 -0
- package/types/core/base-builder/index.d.ts +9 -0
- package/types/core/base-builder/resolution/path-resolver.d.ts +15 -0
- package/types/core/base-builder/resolution/pipeline.d.ts +27 -0
- package/types/core/base-builder/resolution/steps/asset-id.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/asset-wrappers.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/builders.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/mixed-arrays.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/nested-asset-wrappers.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/static-values.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/switches.d.ts +15 -0
- package/types/core/base-builder/resolution/steps/templates.d.ts +14 -0
- package/types/core/base-builder/resolution/value-resolver.d.ts +62 -0
- package/types/core/base-builder/storage/auxiliary-storage.d.ts +50 -0
- package/types/core/base-builder/storage/value-storage.d.ts +82 -0
- package/types/core/base-builder/types.d.ts +183 -0
- package/types/core/base-builder/utils.d.ts +2 -0
- package/types/core/flow/index.d.ts +23 -0
- package/types/core/index.d.ts +8 -0
- package/types/core/mocks/index.d.ts +2 -0
- package/types/core/mocks/types/action.d.ts +58 -0
- package/types/core/mocks/types/choice.d.ts +95 -0
- package/types/core/mocks/types/collection.d.ts +102 -0
- package/types/core/mocks/types/info.d.ts +7 -0
- package/types/core/mocks/types/input.d.ts +7 -0
- package/types/core/mocks/types/text.d.ts +5 -0
- package/types/core/schema/index.d.ts +34 -0
- package/types/core/schema/types.d.ts +5 -0
- package/types/core/switch/index.d.ts +21 -0
- package/types/core/tagged-template/binding.d.ts +19 -0
- package/types/core/tagged-template/expression.d.ts +11 -0
- package/types/core/tagged-template/extract-bindings-from-schema.d.ts +7 -0
- package/types/core/tagged-template/index.d.ts +6 -0
- package/types/core/tagged-template/std.d.ts +174 -0
- package/types/core/tagged-template/types.d.ts +69 -0
- package/types/core/template/index.d.ts +97 -0
- package/types/core/utils/index.d.ts +47 -0
- package/types/fp/index.d.ts +149 -0
- package/types/gen/common.d.ts +6 -0
- package/types/index.d.ts +3 -0
- package/types/types.d.ts +163 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isTaggedTemplateValue,
|
|
3
|
+
TaggedTemplateValueSymbol,
|
|
4
|
+
type TaggedTemplateValue,
|
|
5
|
+
type TemplateRefOptions,
|
|
6
|
+
} from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tagged template for creating expression literals
|
|
10
|
+
* Used for generating expressions with proper validation and formatting
|
|
11
|
+
* @param strings - Template string parts
|
|
12
|
+
* @param expressions - Values to interpolate
|
|
13
|
+
* @returns TaggedTemplateValue with expression context
|
|
14
|
+
* @throws Error if expression syntax is invalid
|
|
15
|
+
*/
|
|
16
|
+
export function expression<T = unknown>(
|
|
17
|
+
strings: TemplateStringsArray,
|
|
18
|
+
...expressions: Array<unknown>
|
|
19
|
+
): TaggedTemplateValue<T> {
|
|
20
|
+
/**
|
|
21
|
+
* Validates expression syntax with balanced parentheses
|
|
22
|
+
* @throws Error if parentheses are unbalanced
|
|
23
|
+
*/
|
|
24
|
+
const validateSyntax = (expr: string): void => {
|
|
25
|
+
let openParens = 0;
|
|
26
|
+
// Use position counter to match original behavior exactly
|
|
27
|
+
let position = 0;
|
|
28
|
+
|
|
29
|
+
// Validation loop using position counter
|
|
30
|
+
for (const char of expr) {
|
|
31
|
+
if (char === "(") openParens++;
|
|
32
|
+
if (char === ")") openParens--;
|
|
33
|
+
position++;
|
|
34
|
+
|
|
35
|
+
if (openParens < 0) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Error: Unexpected ) at character ${position} in expression: \n ${expr.slice(
|
|
38
|
+
0,
|
|
39
|
+
position,
|
|
40
|
+
)}█${expr.slice(position)}`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (openParens > 0) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Error: Expected ) at character ${position} in expression: \n ${expr}█`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let result = "";
|
|
53
|
+
const len = strings.length;
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < len; i++) {
|
|
56
|
+
result += strings[i];
|
|
57
|
+
|
|
58
|
+
if (i < expressions.length) {
|
|
59
|
+
const expr = expressions[i];
|
|
60
|
+
if (isTaggedTemplateValue(expr)) {
|
|
61
|
+
// Use appropriate reference formatting for nested templates
|
|
62
|
+
result += expr.toRefString({ nestedContext: "expression" });
|
|
63
|
+
} else {
|
|
64
|
+
// Convert other values to string
|
|
65
|
+
result += String(expr);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
validateSyntax(result);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
[TaggedTemplateValueSymbol]: true,
|
|
74
|
+
|
|
75
|
+
toValue(): string {
|
|
76
|
+
return result;
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
toRefString(options?: TemplateRefOptions): string {
|
|
80
|
+
if (options?.nestedContext === "binding") {
|
|
81
|
+
return `\`${result}\``;
|
|
82
|
+
} else if (options?.nestedContext === "expression") {
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
return `@[${result}]@`;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
toString(): string {
|
|
89
|
+
return `@[${result}]@`;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Schema } from "@player-ui/types";
|
|
2
|
+
import { binding } from "./binding";
|
|
3
|
+
import type { TaggedTemplateValue, ExtractedBindings } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Runtime implementation that builds the actual object structure
|
|
7
|
+
*/
|
|
8
|
+
export function extractBindingsFromSchema<const S extends Schema.Schema>(
|
|
9
|
+
schema: S,
|
|
10
|
+
): ExtractedBindings<S> {
|
|
11
|
+
const primitiveTypes = new Set(["StringType", "NumberType", "BooleanType"]);
|
|
12
|
+
|
|
13
|
+
const isPrimitive = (typeName: string): boolean => {
|
|
14
|
+
return primitiveTypes.has(typeName);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const createBinding = (
|
|
18
|
+
typeName: string,
|
|
19
|
+
path: string,
|
|
20
|
+
): TaggedTemplateValue<unknown> => {
|
|
21
|
+
if (typeName === "StringType") {
|
|
22
|
+
return binding<string>`${path}`;
|
|
23
|
+
}
|
|
24
|
+
if (typeName === "NumberType") {
|
|
25
|
+
return binding<number>`${path}`;
|
|
26
|
+
}
|
|
27
|
+
if (typeName === "BooleanType") {
|
|
28
|
+
return binding<boolean>`${path}`;
|
|
29
|
+
}
|
|
30
|
+
return binding<string>`${path}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const processDataType = (
|
|
34
|
+
dataType: Schema.DataTypes,
|
|
35
|
+
schema: Schema.Schema,
|
|
36
|
+
path: string,
|
|
37
|
+
visited: Set<string> = new Set(),
|
|
38
|
+
): TaggedTemplateValue<unknown> | Record<string, unknown> => {
|
|
39
|
+
const typeName = dataType.type;
|
|
40
|
+
|
|
41
|
+
// Prevent infinite recursion
|
|
42
|
+
if (visited.has(typeName)) {
|
|
43
|
+
return createBinding("StringType", path);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Handle arrays
|
|
47
|
+
if ("isArray" in dataType && dataType.isArray) {
|
|
48
|
+
const arrayPath = path ? `${path}._current_` : "_current_";
|
|
49
|
+
|
|
50
|
+
if (isPrimitive(typeName)) {
|
|
51
|
+
// Special handling for primitive arrays
|
|
52
|
+
if (typeName === "StringType") {
|
|
53
|
+
return { name: createBinding(typeName, arrayPath) };
|
|
54
|
+
} else {
|
|
55
|
+
return { value: createBinding(typeName, arrayPath) };
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
const typeNode = schema[typeName];
|
|
59
|
+
if (typeNode) {
|
|
60
|
+
const newVisited = new Set(visited);
|
|
61
|
+
newVisited.add(typeName);
|
|
62
|
+
return processNode(
|
|
63
|
+
typeNode as Schema.Node,
|
|
64
|
+
schema,
|
|
65
|
+
arrayPath,
|
|
66
|
+
newVisited,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return createBinding("StringType", arrayPath);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Handle records
|
|
74
|
+
if ("isRecord" in dataType && dataType.isRecord) {
|
|
75
|
+
const typeNode = schema[typeName];
|
|
76
|
+
if (typeNode) {
|
|
77
|
+
const newVisited = new Set(visited);
|
|
78
|
+
newVisited.add(typeName);
|
|
79
|
+
return processNode(typeNode as Schema.Node, schema, path, newVisited);
|
|
80
|
+
}
|
|
81
|
+
return createBinding("StringType", path);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Handle primitives
|
|
85
|
+
if (isPrimitive(typeName)) {
|
|
86
|
+
return createBinding(typeName, path);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handle complex types
|
|
90
|
+
const typeNode = schema[typeName];
|
|
91
|
+
if (typeNode) {
|
|
92
|
+
const newVisited = new Set(visited);
|
|
93
|
+
newVisited.add(typeName);
|
|
94
|
+
return processNode(typeNode as Schema.Node, schema, path, newVisited);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Fallback
|
|
98
|
+
return createBinding("StringType", path);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const processNode = (
|
|
102
|
+
node: Schema.Node,
|
|
103
|
+
schema: Schema.Schema,
|
|
104
|
+
basePath: string,
|
|
105
|
+
visited: Set<string> = new Set(),
|
|
106
|
+
): Record<string, unknown> => {
|
|
107
|
+
const result: Record<string, unknown> = {};
|
|
108
|
+
|
|
109
|
+
// Process each property in the node
|
|
110
|
+
Object.entries(node).forEach(([key, dataType]) => {
|
|
111
|
+
const path = basePath ? `${basePath}.${key}` : key;
|
|
112
|
+
result[key] = processDataType(dataType, schema, path, visited);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Start processing from ROOT
|
|
119
|
+
return processNode(schema.ROOT, schema, "") as ExtractedBindings<S>;
|
|
120
|
+
}
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { isTaggedTemplateValue, type TaggedTemplateValue } from "./types";
|
|
2
|
+
import { expression } from "./expression";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Type-safe standard library of expressions for the Player-UI DSL
|
|
6
|
+
* Provides common logical, comparison, and arithmetic operations with full TypeScript type safety
|
|
7
|
+
* using phantom types from TaggedTemplateValue.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Logical Operations
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Logical AND operation - returns true if all arguments are truthy
|
|
16
|
+
* @param args - Values or bindings to evaluate with AND logic
|
|
17
|
+
* @returns TaggedTemplateValue<boolean> representing the AND expression
|
|
18
|
+
*/
|
|
19
|
+
export function and(
|
|
20
|
+
...args: Array<TaggedTemplateValue<unknown> | boolean | string | number>
|
|
21
|
+
): TaggedTemplateValue<boolean> {
|
|
22
|
+
const expressions = args.map((arg) => {
|
|
23
|
+
if (isTaggedTemplateValue(arg)) {
|
|
24
|
+
const expr = arg.toRefString({ nestedContext: "expression" });
|
|
25
|
+
// Wrap OR expressions in parentheses to maintain proper precedence
|
|
26
|
+
// (AND has higher precedence than OR)
|
|
27
|
+
if (expr.includes(" || ") && !expr.startsWith("(")) {
|
|
28
|
+
return `(${expr})`;
|
|
29
|
+
}
|
|
30
|
+
return expr;
|
|
31
|
+
}
|
|
32
|
+
return String(arg);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return expression`${expressions.join(" && ")}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Logical OR operation - returns true if any argument is truthy
|
|
40
|
+
* @param args - Values or bindings to evaluate with OR logic
|
|
41
|
+
* @returns TaggedTemplateValue<boolean> representing the OR expression
|
|
42
|
+
*/
|
|
43
|
+
export function or(
|
|
44
|
+
...args: Array<TaggedTemplateValue<unknown> | boolean | string | number>
|
|
45
|
+
): TaggedTemplateValue<boolean> {
|
|
46
|
+
const expressions = args.map((arg) => {
|
|
47
|
+
if (isTaggedTemplateValue(arg)) {
|
|
48
|
+
return arg.toRefString({ nestedContext: "expression" });
|
|
49
|
+
}
|
|
50
|
+
return String(arg);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return expression`${expressions.join(" || ")}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Logical NOT operation - returns true if argument is falsy
|
|
58
|
+
* @param value - Value or binding to negate
|
|
59
|
+
* @returns TaggedTemplateValue<boolean> representing the NOT expression
|
|
60
|
+
*/
|
|
61
|
+
export function not(
|
|
62
|
+
value: TaggedTemplateValue<unknown> | boolean | string | number,
|
|
63
|
+
): TaggedTemplateValue<boolean> {
|
|
64
|
+
const expr = isTaggedTemplateValue(value)
|
|
65
|
+
? value.toRefString({ nestedContext: "expression" })
|
|
66
|
+
: String(value);
|
|
67
|
+
|
|
68
|
+
return expression`!${expr}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Logical NOR operation - returns true if all arguments are falsy
|
|
73
|
+
* @param args - Values or bindings to evaluate with NOR logic
|
|
74
|
+
* @returns TaggedTemplateValue<boolean> representing the NOR expression
|
|
75
|
+
*/
|
|
76
|
+
export function nor(
|
|
77
|
+
...args: Array<TaggedTemplateValue<unknown> | boolean | string | number>
|
|
78
|
+
): TaggedTemplateValue<boolean> {
|
|
79
|
+
return not(or(...args));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Logical NAND operation - returns false if all arguments are truthy
|
|
84
|
+
* @param args - Values or bindings to evaluate with NAND logic
|
|
85
|
+
* @returns TaggedTemplateValue<boolean> representing the NAND expression
|
|
86
|
+
*/
|
|
87
|
+
export function nand(
|
|
88
|
+
...args: Array<TaggedTemplateValue<unknown> | boolean | string | number>
|
|
89
|
+
): TaggedTemplateValue<boolean> {
|
|
90
|
+
return not(and(...args));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Logical XOR operation - returns true if exactly one argument is truthy
|
|
95
|
+
* @param left - First value to compare
|
|
96
|
+
* @param right - Second value to compare
|
|
97
|
+
* @returns TaggedTemplateValue<boolean> representing the XOR expression
|
|
98
|
+
*/
|
|
99
|
+
export function xor(
|
|
100
|
+
left: TaggedTemplateValue<unknown> | boolean | string | number,
|
|
101
|
+
right: TaggedTemplateValue<unknown> | boolean | string | number,
|
|
102
|
+
): TaggedTemplateValue<boolean> {
|
|
103
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
104
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
105
|
+
: String(left);
|
|
106
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
107
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
108
|
+
: String(right);
|
|
109
|
+
|
|
110
|
+
return expression`(${leftExpr} && !${rightExpr}) || (!${leftExpr} && ${rightExpr})`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Comparison Operations
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Equality comparison (loose equality ==)
|
|
119
|
+
* @param left - First value to compare
|
|
120
|
+
* @param right - Second value to compare
|
|
121
|
+
* @returns TaggedTemplateValue<boolean> representing the equality expression
|
|
122
|
+
*/
|
|
123
|
+
export function equal<T>(
|
|
124
|
+
left: TaggedTemplateValue<T> | T,
|
|
125
|
+
right: TaggedTemplateValue<T> | T,
|
|
126
|
+
): TaggedTemplateValue<boolean> {
|
|
127
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
128
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
129
|
+
: JSON.stringify(left);
|
|
130
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
131
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
132
|
+
: JSON.stringify(right);
|
|
133
|
+
|
|
134
|
+
return expression`${leftExpr} == ${rightExpr}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Strict equality comparison (===)
|
|
139
|
+
* @param left - First value to compare
|
|
140
|
+
* @param right - Second value to compare
|
|
141
|
+
* @returns TaggedTemplateValue<boolean> representing the strict equality expression
|
|
142
|
+
*/
|
|
143
|
+
export function strictEqual<T>(
|
|
144
|
+
left: TaggedTemplateValue<T> | T,
|
|
145
|
+
right: TaggedTemplateValue<T> | T,
|
|
146
|
+
): TaggedTemplateValue<boolean> {
|
|
147
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
148
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
149
|
+
: JSON.stringify(left);
|
|
150
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
151
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
152
|
+
: JSON.stringify(right);
|
|
153
|
+
|
|
154
|
+
return expression`${leftExpr} === ${rightExpr}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Inequality comparison (!=)
|
|
159
|
+
* @param left - First value to compare
|
|
160
|
+
* @param right - Second value to compare
|
|
161
|
+
* @returns TaggedTemplateValue<boolean> representing the inequality expression
|
|
162
|
+
*/
|
|
163
|
+
export function notEqual<T>(
|
|
164
|
+
left: TaggedTemplateValue<T> | T,
|
|
165
|
+
right: TaggedTemplateValue<T> | T,
|
|
166
|
+
): TaggedTemplateValue<boolean> {
|
|
167
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
168
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
169
|
+
: JSON.stringify(left);
|
|
170
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
171
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
172
|
+
: JSON.stringify(right);
|
|
173
|
+
|
|
174
|
+
return expression`${leftExpr} != ${rightExpr}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Strict inequality comparison (!==)
|
|
179
|
+
* @param left - First value to compare
|
|
180
|
+
* @param right - Second value to compare
|
|
181
|
+
* @returns TaggedTemplateValue<boolean> representing the strict inequality expression
|
|
182
|
+
*/
|
|
183
|
+
export function strictNotEqual<T>(
|
|
184
|
+
left: TaggedTemplateValue<T> | T,
|
|
185
|
+
right: TaggedTemplateValue<T> | T,
|
|
186
|
+
): TaggedTemplateValue<boolean> {
|
|
187
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
188
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
189
|
+
: JSON.stringify(left);
|
|
190
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
191
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
192
|
+
: JSON.stringify(right);
|
|
193
|
+
|
|
194
|
+
return expression`${leftExpr} !== ${rightExpr}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Greater than comparison (>)
|
|
199
|
+
* @param left - First value to compare
|
|
200
|
+
* @param right - Second value to compare
|
|
201
|
+
* @returns TaggedTemplateValue<boolean> representing the greater than expression
|
|
202
|
+
*/
|
|
203
|
+
export function greaterThan<T extends number | string>(
|
|
204
|
+
left: TaggedTemplateValue<T> | T,
|
|
205
|
+
right: TaggedTemplateValue<T> | T,
|
|
206
|
+
): TaggedTemplateValue<boolean> {
|
|
207
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
208
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
209
|
+
: JSON.stringify(left);
|
|
210
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
211
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
212
|
+
: JSON.stringify(right);
|
|
213
|
+
|
|
214
|
+
return expression`${leftExpr} > ${rightExpr}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Greater than or equal comparison (>=)
|
|
219
|
+
* @param left - First value to compare
|
|
220
|
+
* @param right - Second value to compare
|
|
221
|
+
* @returns TaggedTemplateValue<boolean> representing the greater than or equal expression
|
|
222
|
+
*/
|
|
223
|
+
export function greaterThanOrEqual<T extends number | string>(
|
|
224
|
+
left: TaggedTemplateValue<T> | T,
|
|
225
|
+
right: TaggedTemplateValue<T> | T,
|
|
226
|
+
): TaggedTemplateValue<boolean> {
|
|
227
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
228
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
229
|
+
: JSON.stringify(left);
|
|
230
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
231
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
232
|
+
: JSON.stringify(right);
|
|
233
|
+
|
|
234
|
+
return expression`${leftExpr} >= ${rightExpr}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Less than comparison (<)
|
|
239
|
+
* @param left - First value to compare
|
|
240
|
+
* @param right - Second value to compare
|
|
241
|
+
* @returns TaggedTemplateValue<boolean> representing the less than expression
|
|
242
|
+
*/
|
|
243
|
+
export function lessThan<T extends number | string>(
|
|
244
|
+
left: TaggedTemplateValue<T> | T,
|
|
245
|
+
right: TaggedTemplateValue<T> | T,
|
|
246
|
+
): TaggedTemplateValue<boolean> {
|
|
247
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
248
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
249
|
+
: JSON.stringify(left);
|
|
250
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
251
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
252
|
+
: JSON.stringify(right);
|
|
253
|
+
|
|
254
|
+
return expression`${leftExpr} < ${rightExpr}`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Less than or equal comparison (<=)
|
|
259
|
+
* @param left - First value to compare
|
|
260
|
+
* @param right - Second value to compare
|
|
261
|
+
* @returns TaggedTemplateValue<boolean> representing the less than or equal expression
|
|
262
|
+
*/
|
|
263
|
+
export function lessThanOrEqual<T extends number | string>(
|
|
264
|
+
left: TaggedTemplateValue<T> | T,
|
|
265
|
+
right: TaggedTemplateValue<T> | T,
|
|
266
|
+
): TaggedTemplateValue<boolean> {
|
|
267
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
268
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
269
|
+
: JSON.stringify(left);
|
|
270
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
271
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
272
|
+
: JSON.stringify(right);
|
|
273
|
+
|
|
274
|
+
return expression`${leftExpr} <= ${rightExpr}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ============================================================================
|
|
278
|
+
// Arithmetic Operations
|
|
279
|
+
// ============================================================================
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Addition operation (+)
|
|
283
|
+
* @param args - Values or bindings to add together
|
|
284
|
+
* @returns TaggedTemplateValue<number> representing the addition expression
|
|
285
|
+
*/
|
|
286
|
+
export function add(
|
|
287
|
+
...args: Array<TaggedTemplateValue<number> | number>
|
|
288
|
+
): TaggedTemplateValue<number> {
|
|
289
|
+
const expressions = args.map((arg) => {
|
|
290
|
+
if (isTaggedTemplateValue(arg)) {
|
|
291
|
+
return arg.toRefString({ nestedContext: "expression" });
|
|
292
|
+
}
|
|
293
|
+
return String(arg);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return expression`${expressions.join(" + ")}` as TaggedTemplateValue<number>;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Subtraction operation (-)
|
|
301
|
+
* @param left - Value to subtract from
|
|
302
|
+
* @param right - Value to subtract
|
|
303
|
+
* @returns TaggedTemplateValue<number> representing the subtraction expression
|
|
304
|
+
*/
|
|
305
|
+
export function subtract(
|
|
306
|
+
left: TaggedTemplateValue<number> | number,
|
|
307
|
+
right: TaggedTemplateValue<number> | number,
|
|
308
|
+
): TaggedTemplateValue<number> {
|
|
309
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
310
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
311
|
+
: String(left);
|
|
312
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
313
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
314
|
+
: String(right);
|
|
315
|
+
|
|
316
|
+
return expression`${leftExpr} - ${rightExpr}` as TaggedTemplateValue<number>;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Multiplication operation (*)
|
|
321
|
+
* @param args - Values or bindings to multiply together
|
|
322
|
+
* @returns TaggedTemplateValue<number> representing the multiplication expression
|
|
323
|
+
*/
|
|
324
|
+
export function multiply(
|
|
325
|
+
...args: Array<TaggedTemplateValue<number> | number>
|
|
326
|
+
): TaggedTemplateValue<number> {
|
|
327
|
+
const expressions = args.map((arg) => {
|
|
328
|
+
if (isTaggedTemplateValue(arg)) {
|
|
329
|
+
return arg.toRefString({ nestedContext: "expression" });
|
|
330
|
+
}
|
|
331
|
+
return String(arg);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return expression`${expressions.join(" * ")}` as TaggedTemplateValue<number>;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Division operation (/)
|
|
339
|
+
* @param left - Dividend
|
|
340
|
+
* @param right - Divisor
|
|
341
|
+
* @returns TaggedTemplateValue<number> representing the division expression
|
|
342
|
+
*/
|
|
343
|
+
export function divide(
|
|
344
|
+
left: TaggedTemplateValue<number> | number,
|
|
345
|
+
right: TaggedTemplateValue<number> | number,
|
|
346
|
+
): TaggedTemplateValue<number> {
|
|
347
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
348
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
349
|
+
: String(left);
|
|
350
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
351
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
352
|
+
: String(right);
|
|
353
|
+
|
|
354
|
+
return expression`${leftExpr} / ${rightExpr}` as TaggedTemplateValue<number>;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Modulo operation (%)
|
|
359
|
+
* @param left - Dividend
|
|
360
|
+
* @param right - Divisor
|
|
361
|
+
* @returns TaggedTemplateValue<number> representing the modulo expression
|
|
362
|
+
*/
|
|
363
|
+
export function modulo(
|
|
364
|
+
left: TaggedTemplateValue<number> | number,
|
|
365
|
+
right: TaggedTemplateValue<number> | number,
|
|
366
|
+
): TaggedTemplateValue<number> {
|
|
367
|
+
const leftExpr = isTaggedTemplateValue(left)
|
|
368
|
+
? left.toRefString({ nestedContext: "expression" })
|
|
369
|
+
: String(left);
|
|
370
|
+
const rightExpr = isTaggedTemplateValue(right)
|
|
371
|
+
? right.toRefString({ nestedContext: "expression" })
|
|
372
|
+
: String(right);
|
|
373
|
+
|
|
374
|
+
return expression`${leftExpr} % ${rightExpr}` as TaggedTemplateValue<number>;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ============================================================================
|
|
378
|
+
// Control Flow Operations
|
|
379
|
+
// ============================================================================
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Conditional (ternary) operation - if-then-else logic
|
|
383
|
+
* @param condition - Condition to evaluate
|
|
384
|
+
* @param ifTrue - Value to return if condition is truthy
|
|
385
|
+
* @param ifFalse - Value to return if condition is falsy
|
|
386
|
+
* @returns TaggedTemplateValue<T> representing the conditional expression
|
|
387
|
+
*/
|
|
388
|
+
export function conditional<T>(
|
|
389
|
+
condition: TaggedTemplateValue<boolean> | boolean,
|
|
390
|
+
ifTrue: TaggedTemplateValue<T> | T,
|
|
391
|
+
ifFalse: TaggedTemplateValue<T> | T,
|
|
392
|
+
): TaggedTemplateValue<T> {
|
|
393
|
+
const conditionExpr = isTaggedTemplateValue(condition)
|
|
394
|
+
? condition.toRefString({ nestedContext: "expression" })
|
|
395
|
+
: String(condition);
|
|
396
|
+
|
|
397
|
+
const trueExpr = isTaggedTemplateValue(ifTrue)
|
|
398
|
+
? ifTrue.toRefString({ nestedContext: "expression" })
|
|
399
|
+
: JSON.stringify(ifTrue);
|
|
400
|
+
|
|
401
|
+
const falseExpr = isTaggedTemplateValue(ifFalse)
|
|
402
|
+
? ifFalse.toRefString({ nestedContext: "expression" })
|
|
403
|
+
: JSON.stringify(ifFalse);
|
|
404
|
+
|
|
405
|
+
return expression`${conditionExpr} ? ${trueExpr} : ${falseExpr}` as TaggedTemplateValue<T>;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Function call expression
|
|
410
|
+
* @param functionName - Name of the function to call
|
|
411
|
+
* @param args - Arguments to pass to the function
|
|
412
|
+
* @returns TaggedTemplateValue<T> representing the function call expression
|
|
413
|
+
*/
|
|
414
|
+
export function call<T = unknown>(
|
|
415
|
+
functionName: string,
|
|
416
|
+
...args: Array<TaggedTemplateValue<unknown> | unknown>
|
|
417
|
+
): TaggedTemplateValue<T> {
|
|
418
|
+
const argExpressions = args.map((arg) => {
|
|
419
|
+
if (isTaggedTemplateValue(arg)) {
|
|
420
|
+
return arg.toRefString({
|
|
421
|
+
nestedContext: "expression",
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
return JSON.stringify(arg);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
return expression`${functionName}(${argExpressions.join(", ")})` as TaggedTemplateValue<T>;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ============================================================================
|
|
431
|
+
// Type Utilities
|
|
432
|
+
// ============================================================================
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Creates a literal value expression
|
|
436
|
+
* @param value - Literal value to create expression for
|
|
437
|
+
* @returns TaggedTemplateValue<T> representing the literal
|
|
438
|
+
*/
|
|
439
|
+
export function literal<T>(value: T): TaggedTemplateValue<T> {
|
|
440
|
+
return expression`${JSON.stringify(value)}` as TaggedTemplateValue<T>;
|
|
441
|
+
}
|
|
442
|
+
// ============================================================================
|
|
443
|
+
// Shorthand Aliases
|
|
444
|
+
// ============================================================================
|
|
445
|
+
|
|
446
|
+
// Logical operations aliases
|
|
447
|
+
export { and as AND };
|
|
448
|
+
export { or as OR };
|
|
449
|
+
export { not as NOT };
|
|
450
|
+
export { nor as NOR };
|
|
451
|
+
export { nand as NAND };
|
|
452
|
+
export { xor as XOR };
|
|
453
|
+
|
|
454
|
+
// Comparison operations aliases
|
|
455
|
+
export { equal as eq };
|
|
456
|
+
export { strictEqual as strictEq };
|
|
457
|
+
export { notEqual as neq };
|
|
458
|
+
export { strictNotEqual as strictNeq };
|
|
459
|
+
export { greaterThan as gt };
|
|
460
|
+
export { greaterThanOrEqual as gte };
|
|
461
|
+
export { lessThan as lt };
|
|
462
|
+
export { lessThanOrEqual as lte };
|
|
463
|
+
|
|
464
|
+
// Arithmetic operations aliases
|
|
465
|
+
export { add as plus };
|
|
466
|
+
export { subtract as minus };
|
|
467
|
+
export { multiply as times };
|
|
468
|
+
export { divide as div };
|
|
469
|
+
export { modulo as mod };
|
|
470
|
+
|
|
471
|
+
// Control flow aliases
|
|
472
|
+
export { conditional as ternary, conditional as ifElse };
|