@stll/template-conditions 0.2.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +16 -6
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -6,7 +6,14 @@ import { panic } from "better-result";
6
6
  * the condition engine (index.ts) and the arithmetic evaluator (compute.ts) can
7
7
  * import it without a dependency cycle.
8
8
  */
9
- /** Narrow `unknown` to a string-keyed record. */
9
+ /**
10
+ * Narrow `unknown` to something a path segment can be read from.
11
+ *
12
+ * Unlike the same-named predicates that screen JSON payload shapes, this one
13
+ * accepts arrays on purpose: a dotted path segment may be an array index
14
+ * (`parties.1.name`), and rejecting arrays would silently resolve those to
15
+ * `undefined`.
16
+ */
10
17
  const isRecord = (v) => typeof v === "object" && v !== null;
11
18
  /** Resolve a dotted path like `company.name` against data. */
12
19
  const resolvePath = (path, data) => {
@@ -101,7 +108,7 @@ const callFunction = (name, args) => {
101
108
  const factor = 10 ** Math.trunc(digits);
102
109
  return Math.round(value * factor) / factor;
103
110
  }
104
- default: return name;
111
+ default: return panic(`Unhandled name: ${String(name)}`);
105
112
  }
106
113
  };
107
114
  const evaluateNumericExpression = (expression, data) => {
@@ -175,8 +182,6 @@ const evaluateNumericExpression = (expression, data) => {
175
182
  };
176
183
  //#endregion
177
184
  //#region src/parse.ts
178
- const NON_STRING_TOKEN_RE = /(?<token>==|!=|>=|<=|>|<|!(?!=)|and\b|or\b|contains\b|[()]|-?\d[\p{N}_.]*|[\p{L}\p{N}_.]+(?:-[\p{L}\p{N}_.]+)*)/uy;
179
- const STARTS_WITH_DIGIT_RE = /^-?\d/u;
180
185
  const COMPARE_SYMBOL_TO_OP = {
181
186
  "==": "eq",
182
187
  "!=": "neq",
@@ -185,6 +190,11 @@ const COMPARE_SYMBOL_TO_OP = {
185
190
  ">=": "gte",
186
191
  "<=": "lte"
187
192
  };
193
+ const escapeRegexLiteral = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
194
+ const COMPARE_SYMBOL_PATTERN = Object.keys(COMPARE_SYMBOL_TO_OP).sort((left, right) => right.length - left.length).map(escapeRegexLiteral).join("|");
195
+ const NON_STRING_TOKEN_RE = new RegExp(String.raw`(?<token>${COMPARE_SYMBOL_PATTERN}|!(?!=)|and\b|or\b|contains\b|[()]|-?\d[\p{N}_.]*|[\p{L}\p{N}_.]+(?:-[\p{L}\p{N}_.]+)*)`, "uy");
196
+ const STARTS_WITH_DIGIT_RE = /^-?\d/u;
197
+ const isCompareSymbol = (raw) => Object.hasOwn(COMPARE_SYMBOL_TO_OP, raw);
188
198
  /**
189
199
  * Scan a `"..."` literal starting at `expr[start]` (the opening quote),
190
200
  * honoring `\\`-escapes without interpreting them. A single linear pass:
@@ -213,7 +223,7 @@ const classifyNonString = (raw) => {
213
223
  if (raw === "and") return { type: "and" };
214
224
  if (raw === "or") return { type: "or" };
215
225
  if (raw === "!") return { type: "not" };
216
- if (raw === "contains" || Object.hasOwn(COMPARE_SYMBOL_TO_OP, raw)) return {
226
+ if (raw === "contains" || isCompareSymbol(raw)) return {
217
227
  type: "op",
218
228
  raw
219
229
  };
@@ -371,7 +381,7 @@ const parseCondition = (expression) => {
371
381
  return {
372
382
  type: "compare",
373
383
  left,
374
- op: COMPARE_SYMBOL_TO_OP[opTok.raw] ?? "eq",
384
+ op: COMPARE_SYMBOL_TO_OP[opTok.raw],
375
385
  right: operandFromToken(rightTok)
376
386
  };
377
387
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/template-conditions",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A document-template marker and condition engine: {{...}} marker grammar, parse, compute, serialize, field values.",
5
5
  "keywords": [
6
6
  "condition",
@@ -41,21 +41,21 @@
41
41
  "build": "tsdown",
42
42
  "pack:dry-run": "bun pm pack --dry-run",
43
43
  "test": "bun test",
44
- "test:property": "bun test $(grep -rlF 'fc.assert' src --include='*.test.ts')",
44
+ "test:property": "bun test --preload @stll/property-testing/preload $(grep -rlF 'fc.assert' src --include='*.test.ts')",
45
45
  "typecheck": "bun ../../packages/scripts/src/tsc-native.ts --noEmit",
46
46
  "lint": "cd ../.. && bun --bun oxlint -c oxlint.config.ts --report-unused-disable-directives-severity=error --deny-warnings --type-aware packages/template-conditions",
47
47
  "lint:fix": "cd ../.. && bun --bun oxlint -c oxlint.config.ts --type-aware --fix packages/template-conditions",
48
- "format": "oxfmt .",
48
+ "format": "bun ../../scripts/run-oxfmt.ts .",
49
49
  "prepack": "bun run build"
50
50
  },
51
51
  "dependencies": {
52
- "@stll/conditions": "^0.2.0",
52
+ "@stll/conditions": "^0.3.1",
53
53
  "better-result": "3.0.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@stll/property-testing": "0.0.0",
57
57
  "@stll/typescript-config": "0.0.0",
58
- "@types/bun": "1.3.14",
58
+ "bun-types": "1.4.2",
59
59
  "fast-check": "^4.9.0",
60
60
  "tsdown": "0.22.14"
61
61
  },