@1771technologies/lytenyte-pro 2.0.4-dev.3 → 2.0.4-dev.4

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 (175) hide show
  1. package/css/components/expression-editor.css +105 -0
  2. package/dist/components/column-manager/column-manager.d.ts +6 -1
  3. package/dist/components/column-manager/column-manager.js +23 -2
  4. package/dist/components/expressions-editor/(expressions)/expression.play.d.ts +4 -0
  5. package/dist/components/expressions-editor/(expressions)/expression.play.js +69 -0
  6. package/dist/components/expressions-editor/create-completion-provider.d.ts +3 -0
  7. package/dist/components/expressions-editor/create-completion-provider.js +161 -0
  8. package/dist/components/expressions-editor/create-key-down-handler.d.ts +18 -0
  9. package/dist/components/expressions-editor/create-key-down-handler.js +49 -0
  10. package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.d.ts +2 -0
  11. package/dist/components/expressions-editor/cursor-position/measure-cursor-at-pointer.js +56 -0
  12. package/dist/components/expressions-editor/cursor-position/use-cursor-position.d.ts +5 -0
  13. package/dist/components/expressions-editor/cursor-position/use-cursor-position.js +17 -0
  14. package/dist/components/expressions-editor/expression-editor.d.ts +3 -0
  15. package/dist/components/expressions-editor/expression-editor.js +138 -0
  16. package/dist/components/expressions-editor/get-indentation.d.ts +1 -0
  17. package/dist/components/expressions-editor/get-indentation.js +7 -0
  18. package/dist/components/expressions-editor/handle-completion-key-down.d.ts +2 -0
  19. package/dist/components/expressions-editor/handle-completion-key-down.js +22 -0
  20. package/dist/components/expressions-editor/handle-enter-key.d.ts +4 -0
  21. package/dist/components/expressions-editor/handle-enter-key.js +11 -0
  22. package/dist/components/expressions-editor/handle-manual-trigger.d.ts +2 -0
  23. package/dist/components/expressions-editor/handle-manual-trigger.js +3 -0
  24. package/dist/components/expressions-editor/index.d.ts +17 -0
  25. package/dist/components/expressions-editor/index.js +11 -0
  26. package/dist/components/expressions-editor/intellisence/completion-context.d.ts +16 -0
  27. package/dist/components/expressions-editor/intellisence/completion-context.js +7 -0
  28. package/dist/components/expressions-editor/intellisence/completion-item.d.ts +5 -0
  29. package/dist/components/expressions-editor/intellisence/completion-item.js +11 -0
  30. package/dist/components/expressions-editor/intellisence/completion-list.d.ts +8 -0
  31. package/dist/components/expressions-editor/intellisence/completion-list.js +18 -0
  32. package/dist/components/expressions-editor/intellisence/completion-popover.d.ts +1 -0
  33. package/dist/components/expressions-editor/intellisence/completion-popover.js +30 -0
  34. package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.d.ts +2 -0
  35. package/dist/components/expressions-editor/intellisence/filter-completions-by-prefix.js +6 -0
  36. package/dist/components/expressions-editor/intellisence/get-word-at-cursor.d.ts +2 -0
  37. package/dist/components/expressions-editor/intellisence/get-word-at-cursor.js +12 -0
  38. package/dist/components/expressions-editor/intellisence/use-complete-trigger.d.ts +13 -0
  39. package/dist/components/expressions-editor/intellisence/use-complete-trigger.js +41 -0
  40. package/dist/components/expressions-editor/intellisence/use-completions.d.ts +14 -0
  41. package/dist/components/expressions-editor/intellisence/use-completions.js +78 -0
  42. package/dist/components/expressions-editor/replace-word-at-cursor.d.ts +5 -0
  43. package/dist/components/expressions-editor/replace-word-at-cursor.js +7 -0
  44. package/dist/components/expressions-editor/token-highlighter-default.d.ts +4 -0
  45. package/dist/components/expressions-editor/token-highlighter-default.js +18 -0
  46. package/dist/components/expressions-editor/types.d.ts +47 -0
  47. package/dist/components/expressions-editor/types.js +1 -0
  48. package/dist/components/expressions-editor/use-keyboard-navigation.d.ts +10 -0
  49. package/dist/components/expressions-editor/use-keyboard-navigation.js +16 -0
  50. package/dist/components/pill-manager/container.js +55 -9
  51. package/dist/components/pill-manager/item.js +15 -1
  52. package/dist/components/select-all.js +1 -1
  53. package/dist/components/tree-view/root.d.ts +9 -0
  54. package/dist/components/tree-view/root.js +4 -2
  55. package/dist/data-source-client/hooks/use-flattened-groups.js +2 -2
  56. package/dist/expressions/compiler/compile.d.ts +3 -0
  57. package/dist/expressions/compiler/compile.js +4 -0
  58. package/dist/expressions/compiler/eval-constant.d.ts +2 -0
  59. package/dist/expressions/compiler/eval-constant.js +19 -0
  60. package/dist/expressions/compiler/is-constant.d.ts +2 -0
  61. package/dist/expressions/compiler/is-constant.js +6 -0
  62. package/dist/expressions/compiler/make-constant.d.ts +2 -0
  63. package/dist/expressions/compiler/make-constant.js +11 -0
  64. package/dist/expressions/compiler/optimize.d.ts +3 -0
  65. package/dist/expressions/compiler/optimize.js +47 -0
  66. package/dist/expressions/compiler/value-of.d.ts +2 -0
  67. package/dist/expressions/compiler/value-of.js +11 -0
  68. package/dist/expressions/errors/clamp-offset.d.ts +1 -0
  69. package/dist/expressions/errors/clamp-offset.js +3 -0
  70. package/dist/expressions/errors/expression-error.d.ts +10 -0
  71. package/dist/expressions/errors/expression-error.js +19 -0
  72. package/dist/expressions/errors/format-error.d.ts +3 -0
  73. package/dist/expressions/errors/format-error.js +27 -0
  74. package/dist/expressions/errors/get-line-end.d.ts +1 -0
  75. package/dist/expressions/errors/get-line-end.js +8 -0
  76. package/dist/expressions/errors/get-line-start.d.ts +1 -0
  77. package/dist/expressions/errors/get-line-start.js +8 -0
  78. package/dist/expressions/errors/index.d.ts +7 -0
  79. package/dist/expressions/errors/index.js +6 -0
  80. package/dist/expressions/errors/offset-to-position.d.ts +2 -0
  81. package/dist/expressions/errors/offset-to-position.js +17 -0
  82. package/dist/expressions/errors/types.d.ts +10 -0
  83. package/dist/expressions/errors/types.js +1 -0
  84. package/dist/expressions/evaluator/evaluate-binary.d.ts +4 -0
  85. package/dist/expressions/evaluator/evaluate-binary.js +21 -0
  86. package/dist/expressions/evaluator/evaluate-unary.d.ts +2 -0
  87. package/dist/expressions/evaluator/evaluate-unary.js +10 -0
  88. package/dist/expressions/evaluator/evaluate.d.ts +15 -0
  89. package/dist/expressions/evaluator/evaluate.js +62 -0
  90. package/dist/expressions/index.d.ts +5 -0
  91. package/dist/expressions/index.js +3 -0
  92. package/dist/expressions/lexer/predicates/is-alpha-numeric.d.ts +1 -0
  93. package/dist/expressions/lexer/predicates/is-alpha-numeric.js +5 -0
  94. package/dist/expressions/lexer/predicates/is-alpha.d.ts +1 -0
  95. package/dist/expressions/lexer/predicates/is-alpha.js +3 -0
  96. package/dist/expressions/lexer/predicates/is-binary-digit.d.ts +1 -0
  97. package/dist/expressions/lexer/predicates/is-binary-digit.js +3 -0
  98. package/dist/expressions/lexer/predicates/is-digit.d.ts +1 -0
  99. package/dist/expressions/lexer/predicates/is-digit.js +3 -0
  100. package/dist/expressions/lexer/predicates/is-hex-digit.d.ts +1 -0
  101. package/dist/expressions/lexer/predicates/is-hex-digit.js +4 -0
  102. package/dist/expressions/lexer/predicates/is-octal-digit.d.ts +1 -0
  103. package/dist/expressions/lexer/predicates/is-octal-digit.js +3 -0
  104. package/dist/expressions/lexer/tokenize/consume-digits.d.ts +1 -0
  105. package/dist/expressions/lexer/tokenize/consume-digits.js +5 -0
  106. package/dist/expressions/lexer/tokenize/scan-identifier.d.ts +6 -0
  107. package/dist/expressions/lexer/tokenize/scan-identifier.js +20 -0
  108. package/dist/expressions/lexer/tokenize/scan-number.d.ts +4 -0
  109. package/dist/expressions/lexer/tokenize/scan-number.js +32 -0
  110. package/dist/expressions/lexer/tokenize/scan-operator.d.ts +6 -0
  111. package/dist/expressions/lexer/tokenize/scan-operator.js +71 -0
  112. package/dist/expressions/lexer/tokenize/scan-string-literal.d.ts +4 -0
  113. package/dist/expressions/lexer/tokenize/scan-string-literal.js +22 -0
  114. package/dist/expressions/lexer/tokenize/scan-string.d.ts +4 -0
  115. package/dist/expressions/lexer/tokenize/scan-string.js +98 -0
  116. package/dist/expressions/lexer/tokenize/scan-template-literal.d.ts +4 -0
  117. package/dist/expressions/lexer/tokenize/scan-template-literal.js +51 -0
  118. package/dist/expressions/lexer/tokenize/tokenize.d.ts +4 -0
  119. package/dist/expressions/lexer/tokenize/tokenize.js +99 -0
  120. package/dist/expressions/lexer/types.d.ts +12 -0
  121. package/dist/expressions/lexer/types.js +1 -0
  122. package/dist/expressions/parser/parse-array-literal.d.ts +3 -0
  123. package/dist/expressions/parser/parse-array-literal.js +15 -0
  124. package/dist/expressions/parser/parse-expression.d.ts +3 -0
  125. package/dist/expressions/parser/parse-expression.js +74 -0
  126. package/dist/expressions/parser/parse-object-literal.d.ts +3 -0
  127. package/dist/expressions/parser/parse-object-literal.js +69 -0
  128. package/dist/expressions/parser/parse-pipe-transform.d.ts +3 -0
  129. package/dist/expressions/parser/parse-pipe-transform.js +26 -0
  130. package/dist/expressions/parser/parse-postfix.d.ts +3 -0
  131. package/dist/expressions/parser/parse-postfix.js +23 -0
  132. package/dist/expressions/parser/parse-primary.d.ts +3 -0
  133. package/dist/expressions/parser/parse-primary.js +35 -0
  134. package/dist/expressions/parser/parse-template-literal.d.ts +3 -0
  135. package/dist/expressions/parser/parse-template-literal.js +41 -0
  136. package/dist/expressions/parser/parse-unary.d.ts +3 -0
  137. package/dist/expressions/parser/parse-unary.js +28 -0
  138. package/dist/expressions/parser/parse.d.ts +3 -0
  139. package/dist/expressions/parser/parse.js +26 -0
  140. package/dist/expressions/parser/parser-context.d.ts +12 -0
  141. package/dist/expressions/parser/parser-context.js +14 -0
  142. package/dist/expressions/parser/precedence.d.ts +1 -0
  143. package/dist/expressions/parser/precedence.js +8 -0
  144. package/dist/expressions/parser/types.d.ts +97 -0
  145. package/dist/expressions/parser/types.js +1 -0
  146. package/dist/expressions/plugin.d.ts +20 -0
  147. package/dist/expressions/plugin.js +1 -0
  148. package/dist/expressions/plugins/access.d.ts +2 -0
  149. package/dist/expressions/plugins/access.js +138 -0
  150. package/dist/expressions/plugins/arrows.d.ts +2 -0
  151. package/dist/expressions/plugins/arrows.js +83 -0
  152. package/dist/expressions/plugins/booleans.d.ts +2 -0
  153. package/dist/expressions/plugins/booleans.js +34 -0
  154. package/dist/expressions/plugins/collections.d.ts +2 -0
  155. package/dist/expressions/plugins/collections.js +70 -0
  156. package/dist/expressions/plugins/comparison.d.ts +2 -0
  157. package/dist/expressions/plugins/comparison.js +51 -0
  158. package/dist/expressions/plugins/logical.d.ts +2 -0
  159. package/dist/expressions/plugins/logical.js +103 -0
  160. package/dist/expressions/plugins/membership.d.ts +2 -0
  161. package/dist/expressions/plugins/membership.js +71 -0
  162. package/dist/expressions/plugins/pipe.d.ts +2 -0
  163. package/dist/expressions/plugins/pipe.js +57 -0
  164. package/dist/expressions/plugins/resolved-identifier.d.ts +5 -0
  165. package/dist/expressions/plugins/resolved-identifier.js +19 -0
  166. package/dist/expressions/plugins/standard.d.ts +13 -0
  167. package/dist/expressions/plugins/standard.js +23 -0
  168. package/dist/expressions/plugins/strings.d.ts +2 -0
  169. package/dist/expressions/plugins/strings.js +44 -0
  170. package/dist/expressions/plugins/ternary.d.ts +2 -0
  171. package/dist/expressions/plugins/ternary.js +58 -0
  172. package/dist/expressions.d.ts +2 -0
  173. package/dist/expressions.js +2 -0
  174. package/dist/index.d.ts +1 -0
  175. package/package.json +11 -5
@@ -3,14 +3,20 @@ import { forwardRef, memo, useEffect, useRef, useState } from "react";
3
3
  import { usePillRoot } from "./root.context.js";
4
4
  import { usePillRow } from "./pill-row.context.js";
5
5
  import { dragX, dragY, getDragData, useCombinedRefs, useSelector, } from "@1771technologies/lytenyte-core/internal";
6
- import { getFocusables } from "@1771technologies/lytenyte-shared";
6
+ import { equal, getFocusables } from "@1771technologies/lytenyte-shared";
7
7
  function ContainerBase(props, forwarded) {
8
- const { setCloned, orientation, rows, movedRef } = usePillRoot();
8
+ const { setCloned, cloned, orientation, rows, movedRef, onPillRowChange, onPillItemActiveChange } = usePillRoot();
9
9
  const { row } = usePillRow();
10
10
  const [over, setOver] = useState(false);
11
+ const [external, setExternal] = useState(false);
11
12
  const x = useSelector(dragX);
12
13
  const y = useSelector(dragY);
13
14
  const [container, setContainer] = useState(null);
15
+ const ds = getDragData();
16
+ useEffect(() => {
17
+ if (!ds)
18
+ setExternal(false);
19
+ }, [ds]);
14
20
  const wasOver = useRef(false);
15
21
  useEffect(() => {
16
22
  if (!container)
@@ -20,12 +26,26 @@ function ContainerBase(props, forwarded) {
20
26
  setOver(false);
21
27
  return;
22
28
  }
23
- const { item: dragged, id } = ds.pill.data;
29
+ let { item: dragged, id } = ds.pill.data;
30
+ // We resolve the external item to a dragged item here.
31
+ const originalId = id;
32
+ if (id === "__external__") {
33
+ const d = dragged;
34
+ if (!d[row.id])
35
+ return;
36
+ id = row.id;
37
+ dragged = row.pills.find((x) => x.id === d[row.id]);
38
+ if (!dragged) {
39
+ console.error("Specified an external item, but that item is not in the pill group.");
40
+ return;
41
+ }
42
+ }
24
43
  const bb = container.getBoundingClientRect();
25
44
  const isOver = bb.left < x && bb.right > x && bb.top < y && bb.bottom > y;
26
45
  // The current drag is from the current row.
27
46
  if (id === row.id) {
28
47
  movedRef.current = !isOver ? { id: row.id, pillId: dragged.id } : null;
48
+ setExternal(isOver && originalId === "__external__");
29
49
  return;
30
50
  }
31
51
  const thisRow = rows.findIndex((x) => x.id === row.id);
@@ -43,11 +63,11 @@ function ContainerBase(props, forwarded) {
43
63
  setCloned((prev) => {
44
64
  if (!prev)
45
65
  throw new Error("Can't call drag function without cloning nodes.");
46
- const newPill = { ...prev[thisRow] };
47
- newPill.pills = newPills;
48
- const newDef = [...prev];
49
- newDef[thisRow] = newPill;
50
- return newDef;
66
+ const newPillSpec = { ...prev[thisRow] };
67
+ newPillSpec.pills = newPills;
68
+ const newPillRows = [...prev];
69
+ newPillRows[thisRow] = newPillSpec;
70
+ return newPillRows;
51
71
  });
52
72
  }
53
73
  else {
@@ -80,7 +100,7 @@ function ContainerBase(props, forwarded) {
80
100
  }, [container, movedRef, over, row, rows, setCloned, x, y]);
81
101
  const combined = useCombinedRefs(forwarded, setContainer);
82
102
  const rtl = useRef(null);
83
- return (_jsx("div", { ...props, ref: combined, "data-ln-over": over, "data-ln-orientation": orientation, "data-ln-pill-container": true, "data-ln-pill-type": row.type, tabIndex: 0, onKeyDown: (ev) => {
103
+ return (_jsx("div", { ...props, ref: combined, "data-ln-over": over, "data-ln-external-over": external, "data-ln-orientation": orientation, "data-ln-pill-container": true, "data-ln-pill-type": row.type, tabIndex: 0, onKeyDown: (ev) => {
84
104
  if (rtl.current == null) {
85
105
  const dir = getComputedStyle(ev.currentTarget).direction;
86
106
  rtl.current = dir === "rtl";
@@ -118,6 +138,32 @@ function ContainerBase(props, forwarded) {
118
138
  }, onDrop: (e) => {
119
139
  e.preventDefault();
120
140
  e.stopPropagation();
141
+ const ds = getDragData();
142
+ if (!ds?.pill?.data || ds.pill.data.id != "__external__")
143
+ return;
144
+ let { item: dragged } = ds.pill.data;
145
+ // We resolve the external item to a dragged item here.
146
+ const d = dragged;
147
+ if (!d[row.id])
148
+ return;
149
+ dragged = row.pills.find((x) => x.id === d[row.id]);
150
+ const changed = cloned?.filter((x, i) => {
151
+ return !equal(x, rows[i]);
152
+ });
153
+ if (!dragged.active) {
154
+ const next = true;
155
+ const nextPill = { ...dragged, active: next };
156
+ const nextPills = [...row.pills];
157
+ nextPills.splice(nextPills.indexOf(dragged), 1, nextPill);
158
+ const nextRow = { ...row, pills: nextPills };
159
+ const index = rows.indexOf(row);
160
+ onPillItemActiveChange({ index, item: nextPill, row: nextRow });
161
+ }
162
+ dragged.active = true;
163
+ if (changed?.length) {
164
+ onPillRowChange({ changed, full: cloned });
165
+ }
166
+ setCloned(null);
121
167
  }, onDragOver: (e) => {
122
168
  e.dataTransfer.dropEffect = "move";
123
169
  e.preventDefault();
@@ -53,7 +53,18 @@ function PillItemBase({ item, elementEnd, ...props }, ref) {
53
53
  const ds = getDragData();
54
54
  if (!ds?.pill?.data)
55
55
  return;
56
- const { item: dragged, id: dragRowId } = ds.pill.data;
56
+ let { item: dragged, id: dragRowId } = ds.pill.data;
57
+ const original = dragRowId;
58
+ // We resolve the external item to a dragged item here.
59
+ if (dragRowId === "__external__") {
60
+ const d = dragged;
61
+ if (!d[row.id])
62
+ return;
63
+ dragRowId = row.id;
64
+ dragged = row.pills.find((x) => x.id === d[row.id]);
65
+ if (!dragged)
66
+ return;
67
+ }
57
68
  const accepts = new Set([...(row.accepts ?? []), row.id]);
58
69
  if (!accepts.has(dragRowId) && !item.tags?.some((x) => accepts.has(x)))
59
70
  return;
@@ -93,6 +104,9 @@ function PillItemBase({ item, elementEnd, ...props }, ref) {
93
104
  prevSwapId.current = currentSwapId;
94
105
  prevRowId.current = currentRowId;
95
106
  setCloned((prev) => {
107
+ if (original === "__external__" && !prev) {
108
+ prev = structuredClone(rows);
109
+ }
96
110
  if (!prev)
97
111
  throw new Error("Can't call drag function without cloning nodes.");
98
112
  const thisRow = prev.findIndex((x) => x.id === row.id);
@@ -6,7 +6,7 @@ export function SelectAll({ api, slot, }) {
6
6
  const s = api.useSelectionState();
7
7
  const selected = useMemo(() => {
8
8
  const selected = s.kind === "isolated" ? s.selected && s.exceptions.size === 0 : s.selected && s.children.size === 0;
9
- return selected;
9
+ return !!selected;
10
10
  }, [s]);
11
11
  const indeterminate = useMemo(() => {
12
12
  if (s.kind === "isolated")
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { type RowGroup, type RowLeaf, type RowSelectionLinked } from "@1771technologies/lytenyte-shared";
3
3
  import type { TreeViewChildParams, TreeViewItem, TreeViewSelectAllParams } from "./types.js";
4
+ import type { DragItem, RowNode } from "../../types.js";
4
5
  export interface TreeViewProps<T extends TreeViewItem> {
5
6
  readonly items: T[];
6
7
  readonly children?: (props: TreeViewChildParams<T>) => ReactNode;
@@ -22,6 +23,14 @@ export interface TreeViewProps<T extends TreeViewItem> {
22
23
  * and drag is still being prototyped.
23
24
  */
24
25
  readonly draggable?: boolean;
26
+ /**
27
+ * @alpha
28
+ * @internal
29
+ *
30
+ * Do not use this property unless you know what you are doing. Support for tree view drag
31
+ * and drag is still being prototyped.
32
+ */
33
+ readonly getDragTags?: (row: RowNode<T>, leafs: RowLeaf<T>[]) => Record<string, DragItem>;
25
34
  /**
26
35
  * @alpha
27
36
  * @internal
@@ -7,7 +7,7 @@ import { useClientDataSource } from "../../data-source-client/use-client-data-so
7
7
  import { usePiece } from "@1771technologies/lytenyte-core/internal";
8
8
  import { Root } from "../../root/root.js";
9
9
  import { Grid } from "@1771technologies/lytenyte-core";
10
- function TreeViewBase({ items, rowSelectAllShow: rowSelectAll = true, defaultExpansion, rowHeight = 28, children = TreeChildren, branchJoinSeparator = "/", rowSelectionEnabled = true, rowSelection, onRowSelectionChange, rowGroupExpansions, onRowGroupExpansionChange, selectAllSlot, draggable, onItemsReordered, }, forwarded) {
10
+ function TreeViewBase({ items, rowSelectAllShow: rowSelectAll = true, defaultExpansion, rowHeight = 28, children = TreeChildren, branchJoinSeparator = "/", rowSelectionEnabled = true, rowSelection, onRowSelectionChange, rowGroupExpansions, onRowGroupExpansionChange, selectAllSlot, draggable, getDragTags, onItemsReordered, }, forwarded) {
11
11
  const groupFn = useMemo(() => {
12
12
  return (x) => x.data.path;
13
13
  }, []);
@@ -46,8 +46,10 @@ function TreeViewBase({ items, rowSelectAllShow: rowSelectAll = true, defaultExp
46
46
  width: 20,
47
47
  widthMin: 20,
48
48
  cellRenderer: ({ row, rowIndex, api, indeterminate, selected }) => {
49
+ const tags = getDragTags?.(row, api.rowLeafs(row.id).map((x) => api.rowById(x)));
49
50
  const { props, isDragActive } = api.useRowDrag({
50
51
  rowIndex,
52
+ tags,
51
53
  });
52
54
  const over = over$.useValue();
53
55
  const isBefore = isBefore$.useValue();
@@ -76,7 +78,7 @@ function TreeViewBase({ items, rowSelectAllShow: rowSelectAll = true, defaultExp
76
78
  });
77
79
  },
78
80
  };
79
- }, [children, draggable, isBefore$, itemLookup, over$, rowSelectionEnabled, selectAllSlot]);
81
+ }, [children, draggable, getDragTags, isBefore$, itemLookup, over$, rowSelectionEnabled, selectAllSlot]);
80
82
  useImperativeHandle(forwarded, () => {
81
83
  return {
82
84
  rowsSelected: () => (api?.rowsSelected().rows ?? []).filter((x) => x.id !== "__ln_select_all"),
@@ -39,7 +39,7 @@ export function useFlattenedGroups(root, agg, leafs, workingSet, sort, expandedF
39
39
  }, [agg, expandedFn, ignoreIsLast, leafs, root, sort, sortFallback, suppressLeafExpansion, workingSet]);
40
40
  return flat;
41
41
  }
42
- const nodeChildrenToRows = (root, agg, leafs, workingSet, sort, isLast) => {
42
+ const nodeChildrenToRows = (root, agg, leafs, workingSet, sort, _) => {
43
43
  const values = root.values();
44
44
  const rows = [];
45
45
  for (const v of values) {
@@ -57,7 +57,7 @@ const nodeChildrenToRows = (root, agg, leafs, workingSet, sort, isLast) => {
57
57
  rows.push(row);
58
58
  }
59
59
  }
60
- if (!isLast && sort)
60
+ if (sort)
61
61
  return rows.sort(sort);
62
62
  return rows;
63
63
  };
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "../parser/types.js";
2
+ import type { Plugin } from "../plugin.js";
3
+ export declare function compile(ast: ASTNode, plugins?: Plugin[]): ASTNode;
@@ -0,0 +1,4 @@
1
+ import { optimize } from "./optimize.js";
2
+ export function compile(ast, plugins) {
3
+ return optimize(ast, plugins);
4
+ }
@@ -0,0 +1,2 @@
1
+ import type { BinaryExpressionOperator } from "../lexer/types.js";
2
+ export declare function evalConstant(op: BinaryExpressionOperator, left: unknown, right: unknown): unknown | undefined;
@@ -0,0 +1,19 @@
1
+ export function evalConstant(op, left, right) {
2
+ if (typeof left === "number" && typeof right === "number") {
3
+ switch (op) {
4
+ case "+":
5
+ return left + right;
6
+ case "-":
7
+ return left - right;
8
+ case "*":
9
+ return left * right;
10
+ case "/":
11
+ return left / right;
12
+ case "%":
13
+ return left % right;
14
+ case "**":
15
+ return left ** right;
16
+ }
17
+ }
18
+ return undefined;
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { ASTNode } from "../parser/types.js";
2
+ export declare function isConstant(node: ASTNode): boolean;
@@ -0,0 +1,6 @@
1
+ export function isConstant(node) {
2
+ return (node.type === "NumberLiteral" ||
3
+ node.type === "StringLiteral" ||
4
+ node.type === "BooleanLiteral" ||
5
+ node.type === "NullLiteral");
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { ASTNode } from "../parser/types.js";
2
+ export declare function makeConstant(value: unknown, start: number, end: number): ASTNode;
@@ -0,0 +1,11 @@
1
+ export function makeConstant(value, start, end) {
2
+ if (typeof value === "number")
3
+ return { type: "NumberLiteral", value, start, end };
4
+ if (typeof value === "string")
5
+ return { type: "StringLiteral", value, start, end };
6
+ if (typeof value === "boolean")
7
+ return { type: "BooleanLiteral", value, start, end };
8
+ if (value === null)
9
+ return { type: "NullLiteral", value: null, start, end };
10
+ return { type: "UndefinedLiteral", value: undefined, start, end };
11
+ }
@@ -0,0 +1,3 @@
1
+ import type { ASTNode } from "../parser/types.js";
2
+ import type { Plugin } from "../plugin.js";
3
+ export declare function optimize(node: ASTNode, plugins?: Plugin[]): ASTNode;
@@ -0,0 +1,47 @@
1
+ import { isConstant } from "./is-constant.js";
2
+ import { valueOf } from "./value-of.js";
3
+ import { evalConstant } from "./eval-constant.js";
4
+ import { makeConstant } from "./make-constant.js";
5
+ export function optimize(node, plugins) {
6
+ const opt = (n) => optimize(n, plugins);
7
+ // Try plugin optimization first
8
+ if (plugins) {
9
+ for (const plugin of plugins) {
10
+ if (plugin.optimize) {
11
+ const result = plugin.optimize(node, opt);
12
+ if (result)
13
+ return result;
14
+ }
15
+ }
16
+ }
17
+ switch (node.type) {
18
+ case "BinaryExpression": {
19
+ const left = opt(node.left);
20
+ const right = opt(node.right);
21
+ if (isConstant(left) && isConstant(right)) {
22
+ const result = evalConstant(node.operator, valueOf(left), valueOf(right));
23
+ if (result !== undefined) {
24
+ return makeConstant(result, node.start, node.end);
25
+ }
26
+ }
27
+ return { ...node, left, right };
28
+ }
29
+ case "UnaryExpression": {
30
+ const operand = opt(node.operand);
31
+ if (isConstant(operand)) {
32
+ const val = valueOf(operand);
33
+ if (node.operator === "-" && typeof val === "number") {
34
+ return {
35
+ type: "NumberLiteral",
36
+ value: -val,
37
+ start: node.start,
38
+ end: node.end,
39
+ };
40
+ }
41
+ }
42
+ return { ...node, operand };
43
+ }
44
+ default:
45
+ return node;
46
+ }
47
+ }
@@ -0,0 +1,2 @@
1
+ import type { ASTNode } from "../parser/types.js";
2
+ export declare function valueOf(node: ASTNode): unknown;
@@ -0,0 +1,11 @@
1
+ export function valueOf(node) {
2
+ if (node.type === "NumberLiteral")
3
+ return node.value;
4
+ if (node.type === "StringLiteral")
5
+ return node.value;
6
+ if (node.type === "BooleanLiteral")
7
+ return node.value;
8
+ if (node.type === "NullLiteral")
9
+ return null;
10
+ return undefined;
11
+ }
@@ -0,0 +1 @@
1
+ export declare function clampOffset(source: string, offset: number): number;
@@ -0,0 +1,3 @@
1
+ export function clampOffset(source, offset) {
2
+ return Math.max(0, Math.min(offset, source.length));
3
+ }
@@ -0,0 +1,10 @@
1
+ import type { ErrorLocation } from "./types.js";
2
+ /** Syntax error thrown during parsing. Contains source location and formatted caret display. */
3
+ export declare class ExpressionError extends Error {
4
+ readonly rawMessage: string;
5
+ readonly start: number;
6
+ readonly end: number;
7
+ readonly source: string;
8
+ readonly suggestion?: string;
9
+ constructor(message: string, location: ErrorLocation, suggestion?: string);
10
+ }
@@ -0,0 +1,19 @@
1
+ import { formatError } from "./format-error.js";
2
+ /** Syntax error thrown during parsing. Contains source location and formatted caret display. */
3
+ export class ExpressionError extends Error {
4
+ rawMessage;
5
+ start;
6
+ end;
7
+ source;
8
+ suggestion;
9
+ constructor(message, location, suggestion) {
10
+ const formatted = formatError(message, location, suggestion);
11
+ super(formatted);
12
+ this.name = "ExpressionError";
13
+ this.rawMessage = message;
14
+ this.start = location.start;
15
+ this.end = location.end;
16
+ this.source = location.source;
17
+ this.suggestion = suggestion;
18
+ }
19
+ }
@@ -0,0 +1,3 @@
1
+ import type { ErrorLocation } from "./types.js";
2
+ /** Format an error message with source context, caret display, and position info. */
3
+ export declare function formatError(message: string, location: ErrorLocation, suggestion?: string): string;
@@ -0,0 +1,27 @@
1
+ import { clampOffset } from "./clamp-offset.js";
2
+ import { getLineStart } from "./get-line-start.js";
3
+ import { getLineEnd } from "./get-line-end.js";
4
+ import { offsetToPosition } from "./offset-to-position.js";
5
+ /** Format an error message with source context, caret display, and position info. */
6
+ export function formatError(message, location, suggestion) {
7
+ const { source } = location;
8
+ const start = clampOffset(source, location.start);
9
+ const rawEnd = clampOffset(source, location.end);
10
+ const end = Math.max(start + 1, rawEnd);
11
+ const startPos = offsetToPosition(source, start);
12
+ const lineStart = getLineStart(source, start);
13
+ const lineEnd = getLineEnd(source, start);
14
+ const lineText = source.slice(lineStart, lineEnd);
15
+ const caretStart = start - lineStart;
16
+ const caretEnd = Math.min(end, lineEnd);
17
+ const caretLength = Math.max(1, caretEnd - start);
18
+ const gutter = `${startPos.line} | `;
19
+ const padding = " ".repeat(gutter.length + caretStart);
20
+ const carets = "^".repeat(caretLength);
21
+ let result = `${message}\n\n${gutter}${lineText}\n${padding}${carets}`;
22
+ if (suggestion) {
23
+ result += ` Did you mean "${suggestion}"?`;
24
+ }
25
+ result += `\n\nat line ${startPos.line}, column ${startPos.column} (offset ${start}-${end})`;
26
+ return result;
27
+ }
@@ -0,0 +1 @@
1
+ export declare function getLineEnd(source: string, offset: number): number;
@@ -0,0 +1,8 @@
1
+ import { clampOffset } from "./clamp-offset.js";
2
+ const NEWLINE_CODE = 10;
3
+ export function getLineEnd(source, offset) {
4
+ let index = clampOffset(source, offset);
5
+ while (index < source.length && source.charCodeAt(index) !== NEWLINE_CODE)
6
+ index++;
7
+ return index;
8
+ }
@@ -0,0 +1 @@
1
+ export declare function getLineStart(source: string, offset: number): number;
@@ -0,0 +1,8 @@
1
+ import { clampOffset } from "./clamp-offset.js";
2
+ const NEWLINE_CODE = 10;
3
+ export function getLineStart(source, offset) {
4
+ let index = clampOffset(source, offset);
5
+ while (index > 0 && source.charCodeAt(index - 1) !== NEWLINE_CODE)
6
+ index--;
7
+ return index;
8
+ }
@@ -0,0 +1,7 @@
1
+ export type { SourcePosition, ErrorLocation } from "./types.js";
2
+ export { clampOffset } from "./clamp-offset.js";
3
+ export { getLineStart } from "./get-line-start.js";
4
+ export { getLineEnd } from "./get-line-end.js";
5
+ export { offsetToPosition } from "./offset-to-position.js";
6
+ export { formatError } from "./format-error.js";
7
+ export { ExpressionError } from "./expression-error.js";
@@ -0,0 +1,6 @@
1
+ export { clampOffset } from "./clamp-offset.js";
2
+ export { getLineStart } from "./get-line-start.js";
3
+ export { getLineEnd } from "./get-line-end.js";
4
+ export { offsetToPosition } from "./offset-to-position.js";
5
+ export { formatError } from "./format-error.js";
6
+ export { ExpressionError } from "./expression-error.js";
@@ -0,0 +1,2 @@
1
+ import type { SourcePosition } from "./types.js";
2
+ export declare function offsetToPosition(source: string, offset: number): SourcePosition;
@@ -0,0 +1,17 @@
1
+ import { clampOffset } from "./clamp-offset.js";
2
+ const NEWLINE_CODE = 10;
3
+ export function offsetToPosition(source, offset) {
4
+ const safeOffset = clampOffset(source, offset);
5
+ let line = 1;
6
+ let column = 1;
7
+ for (let index = 0; index < safeOffset; index++) {
8
+ if (source.charCodeAt(index) === NEWLINE_CODE) {
9
+ line++;
10
+ column = 1;
11
+ }
12
+ else {
13
+ column++;
14
+ }
15
+ }
16
+ return { line, column, offset: safeOffset };
17
+ }
@@ -0,0 +1,10 @@
1
+ export interface SourcePosition {
2
+ line: number;
3
+ column: number;
4
+ offset: number;
5
+ }
6
+ export interface ErrorLocation {
7
+ source: string;
8
+ start: number;
9
+ end: number;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { BinaryExpression } from "../parser/types.js";
2
+ import type { Plugin } from "../plugin.js";
3
+ import type { RunOptions } from "./evaluate.js";
4
+ export declare function evaluateBinary(node: BinaryExpression, context: Record<string, unknown>, depth: number, plugins?: Plugin[], options?: RunOptions): unknown;
@@ -0,0 +1,21 @@
1
+ import { evaluateNode } from "./evaluate.js";
2
+ export function evaluateBinary(node, context, depth, plugins, options) {
3
+ const left = evaluateNode(node.left, context, depth + 1, plugins, options);
4
+ const right = evaluateNode(node.right, context, depth + 1, plugins, options);
5
+ switch (node.operator) {
6
+ case "+":
7
+ return left + right;
8
+ case "-":
9
+ return left - right;
10
+ case "*":
11
+ return left * right;
12
+ case "/":
13
+ return left / right;
14
+ case "%":
15
+ return left % right;
16
+ case "**":
17
+ return left ** right;
18
+ default:
19
+ throw new Error(`Unknown binary operator: ${node.operator}`);
20
+ }
21
+ }
@@ -0,0 +1,2 @@
1
+ import type { UnaryOperator } from "../lexer/types.js";
2
+ export declare function evaluateUnary(operator: UnaryOperator, operand: unknown): unknown;
@@ -0,0 +1,10 @@
1
+ export function evaluateUnary(operator, operand) {
2
+ switch (operator) {
3
+ case "-":
4
+ return -operand;
5
+ case "+":
6
+ return +operand;
7
+ default:
8
+ throw new Error(`Unknown unary operator: ${operator}`);
9
+ }
10
+ }
@@ -0,0 +1,15 @@
1
+ import type { ASTNode } from "../parser/types.js";
2
+ import type { Token } from "../lexer/types.js";
3
+ import type { Plugin } from "../plugin.js";
4
+ export interface RunOptions {
5
+ undefinedIdentifierFallback?: unknown;
6
+ }
7
+ export declare class Evaluator {
8
+ private plugins?;
9
+ constructor(plugins?: Plugin[]);
10
+ run: (input: string | ASTNode, context?: Record<string, unknown>, options?: RunOptions) => unknown;
11
+ ast: (input: string) => ASTNode;
12
+ tokens: (input: string, tokensizeWhitespace?: boolean) => Token[];
13
+ tokensSafe: (input: string, tokensizeWhitespace?: boolean) => Token[];
14
+ }
15
+ export declare function evaluateNode(node: ASTNode, context: Record<string, unknown>, depth: number, plugins?: Plugin[], options?: RunOptions): unknown;
@@ -0,0 +1,62 @@
1
+ import { parse } from "../parser/parse.js";
2
+ import { compile } from "../compiler/compile.js";
3
+ import { tokenize, tokenizeSafe } from "../lexer/tokenize/tokenize.js";
4
+ import { evaluateBinary } from "./evaluate-binary.js";
5
+ import { evaluateUnary } from "./evaluate-unary.js";
6
+ const MAX_DEPTH = 1000;
7
+ export class Evaluator {
8
+ plugins;
9
+ constructor(plugins) {
10
+ this.plugins = plugins;
11
+ }
12
+ run = (input, context = {}, options) => {
13
+ const node = typeof input === "string" ? compile(parse(input, 0, this.plugins), this.plugins) : input;
14
+ return evaluateNode(node, context, 0, this.plugins, options);
15
+ };
16
+ ast = (input) => {
17
+ return compile(parse(input, 0, this.plugins), this.plugins);
18
+ };
19
+ tokens = (input, tokensizeWhitespace) => {
20
+ return tokenize(input, this.plugins, tokensizeWhitespace);
21
+ };
22
+ tokensSafe = (input, tokensizeWhitespace) => {
23
+ return tokenizeSafe(input, this.plugins, tokensizeWhitespace);
24
+ };
25
+ }
26
+ export function evaluateNode(node, context, depth, plugins, options) {
27
+ if (depth > MAX_DEPTH) {
28
+ throw new Error("Maximum evaluation depth exceeded");
29
+ }
30
+ // Try plugin evaluation first
31
+ if (plugins) {
32
+ const evalFn = (n, ctx) => evaluateNode(n, ctx, depth + 1, plugins, options);
33
+ for (const plugin of plugins) {
34
+ if (plugin.evaluate) {
35
+ const result = plugin.evaluate(node, context, evalFn);
36
+ if (result !== null)
37
+ return result.value;
38
+ }
39
+ }
40
+ }
41
+ // Core built-in evaluation
42
+ switch (node.type) {
43
+ case "NumberLiteral":
44
+ return node.value;
45
+ case "Identifier": {
46
+ if (!(node.name in context)) {
47
+ if (options && "undefinedIdentifierFallback" in options)
48
+ return options.undefinedIdentifierFallback;
49
+ throw new Error(`Identifier "${node.name}" is not defined`);
50
+ }
51
+ return context[node.name];
52
+ }
53
+ case "BinaryExpression":
54
+ return evaluateBinary(node, context, depth, plugins, options);
55
+ case "UnaryExpression":
56
+ return evaluateUnary(node.operator, evaluateNode(node.operand, context, depth + 1, plugins, options));
57
+ case "SpreadElement":
58
+ throw new Error("Unexpected spread element outside of array literal");
59
+ default:
60
+ throw new Error(`Unknown node type: ${node.type}`);
61
+ }
62
+ }
@@ -0,0 +1,5 @@
1
+ export { Evaluator } from "./evaluator/evaluate.js";
2
+ export type { Plugin, EvalFn } from "./plugin.js";
3
+ export type { Token } from "./lexer/types.js";
4
+ export { accessPlugin, arrowsPlugin, booleansPlugin, collectionsPlugin, comparisonPlugin, logicalPlugin, membershipPlugin, pipePlugin, standardPlugins, stringsPlugin, ternaryPlugin, } from "./plugins/standard.js";
5
+ export { createResolvedIdentifierPlugin } from "./plugins/resolved-identifier.js";