tova 0.9.14 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tova",
3
- "version": "0.9.14",
3
+ "version": "0.10.1",
4
4
  "description": "Tova — a modern programming language that transpiles to JavaScript, unifying frontend and backend",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,6 +1,6 @@
1
1
  import { Scope, Symbol } from './scope.js';
2
2
  import { PIPE_TARGET } from '../parser/ast.js';
3
- import { BUILTIN_NAMES } from '../stdlib/inline.js';
3
+ import { BUILTIN_NAMES, DEPRECATED_NAMES, SNAKE_TO_CAMEL } from '../stdlib/inline.js';
4
4
  import { BlockRegistry } from '../registry/register-all.js';
5
5
  import {
6
6
  Type, PrimitiveType, NilType, AnyType, UnknownType,
@@ -854,6 +854,15 @@ export class Analyzer {
854
854
  this.visitExpression(node.collection);
855
855
  return;
856
856
  case 'CallExpression':
857
+ // W_DEPRECATED_STDLIB: warn on snake_case stdlib function usage
858
+ if (node.callee.type === 'Identifier' && DEPRECATED_NAMES.has(node.callee.name)) {
859
+ this.warn(
860
+ `'${node.callee.name}' is deprecated, use '${SNAKE_TO_CAMEL[node.callee.name]}' instead`,
861
+ node.callee.loc || node.loc,
862
+ `rename to ${SNAKE_TO_CAMEL[node.callee.name]}`,
863
+ { code: 'W_DEPRECATED_STDLIB' }
864
+ );
865
+ }
857
866
  // W_DANGEROUS_API: detect setTimeout/setInterval with string args
858
867
  if (node.callee.type === 'Identifier') {
859
868
  const calleeName = node.callee.name;
@@ -88,4 +88,168 @@ export class TypeRegistry {
88
88
  }
89
89
  return [];
90
90
  }
91
+
92
+ /**
93
+ * Extract the base type name from a composite type string.
94
+ * "[Int]" → "Array", "Result<Int, String>" → "Result", "Option<String>" → "Option", "String" → "String"
95
+ */
96
+ static extractBaseType(typeName) {
97
+ if (!typeName) return null;
98
+ if (typeName.startsWith('[') && typeName.endsWith(']')) return 'Array';
99
+ if (typeName.startsWith('Result<') || typeName === 'Result') return 'Result';
100
+ if (typeName.startsWith('Option<') || typeName === 'Option') return 'Option';
101
+ if (typeName.startsWith('Map<') || typeName === 'Map') return 'Map';
102
+ if (typeName.startsWith('Set<') || typeName === 'Set') return 'Set';
103
+ return typeName;
104
+ }
105
+
106
+ /**
107
+ * Get built-in members (fields + methods) for a built-in type.
108
+ * Returns { fields: Map, methods: [] } or null if not a built-in type.
109
+ */
110
+ getBuiltinMembers(typeName) {
111
+ const base = TypeRegistry.extractBaseType(typeName);
112
+ const descriptors = TypeRegistry.BUILTIN_MEMBERS[base];
113
+ if (!descriptors) return null;
114
+
115
+ const fields = new Map();
116
+ const methods = [];
117
+ for (const d of descriptors) {
118
+ if (d.kind === 'field') {
119
+ fields.set(d.name, d.returnType);
120
+ } else {
121
+ methods.push(d);
122
+ }
123
+ }
124
+ return { fields, methods };
125
+ }
126
+ }
127
+
128
+ // ─── Built-in type member descriptors ──────────────────────────
129
+
130
+ function field(name, returnType, doc) {
131
+ return { kind: 'field', name, returnType, doc };
91
132
  }
133
+
134
+ function method(name, params, returnType, doc) {
135
+ return { kind: 'method', name, params, returnType, doc };
136
+ }
137
+
138
+ TypeRegistry.BUILTIN_MEMBERS = {
139
+ String: [
140
+ field('length', 'Int', 'Number of characters'),
141
+ method('slice', ['start: Int', 'end?: Int'], 'String', 'Extract a section of the string'),
142
+ method('includes', ['search: String'], 'Bool', 'Check if string contains substring'),
143
+ method('indexOf', ['search: String'], 'Int', 'First index of substring, or -1'),
144
+ method('lastIndexOf', ['search: String'], 'Int', 'Last index of substring, or -1'),
145
+ method('startsWith', ['prefix: String'], 'Bool', 'Check if string starts with prefix'),
146
+ method('endsWith', ['suffix: String'], 'Bool', 'Check if string ends with suffix'),
147
+ method('trim', [], 'String', 'Remove whitespace from both ends'),
148
+ method('trimStart', [], 'String', 'Remove whitespace from start'),
149
+ method('trimEnd', [], 'String', 'Remove whitespace from end'),
150
+ method('toUpperCase', [], 'String', 'Convert to uppercase'),
151
+ method('toLowerCase', [], 'String', 'Convert to lowercase'),
152
+ method('split', ['separator: String'], '[String]', 'Split into array of substrings'),
153
+ method('replace', ['search: String', 'replacement: String'], 'String', 'Replace first occurrence'),
154
+ method('replaceAll', ['search: String', 'replacement: String'], 'String', 'Replace all occurrences'),
155
+ method('repeat', ['count: Int'], 'String', 'Repeat the string n times'),
156
+ method('charAt', ['index: Int'], 'String', 'Character at index'),
157
+ method('charCodeAt', ['index: Int'], 'Int', 'Character code at index'),
158
+ method('concat', ['other: String'], 'String', 'Concatenate strings'),
159
+ method('padStart', ['length: Int', 'fill?: String'], 'String', 'Pad from start to length'),
160
+ method('padEnd', ['length: Int', 'fill?: String'], 'String', 'Pad from end to length'),
161
+ method('match', ['pattern: String'], 'Option', 'Match against regex pattern'),
162
+ method('search', ['pattern: String'], 'Int', 'Search for regex pattern'),
163
+ method('toString', [], 'String', 'Convert to string'),
164
+ method('substring', ['start: Int', 'end?: Int'], 'String', 'Extract characters between indices'),
165
+ method('at', ['index: Int'], 'String', 'Character at index (supports negative)'),
166
+ ],
167
+ Array: [
168
+ field('length', 'Int', 'Number of elements'),
169
+ method('push', ['item: T'], 'Int', 'Add element to end, returns new length'),
170
+ method('pop', [], 'T', 'Remove and return last element'),
171
+ method('shift', [], 'T', 'Remove and return first element'),
172
+ method('unshift', ['item: T'], 'Int', 'Add element to start, returns new length'),
173
+ method('splice', ['start: Int', 'deleteCount?: Int'], '[T]', 'Remove/replace elements'),
174
+ method('slice', ['start?: Int', 'end?: Int'], '[T]', 'Extract a section of the array'),
175
+ method('concat', ['other: [T]'], '[T]', 'Merge arrays'),
176
+ method('join', ['separator?: String'], 'String', 'Join elements into string'),
177
+ method('reverse', [], '[T]', 'Reverse array in place'),
178
+ method('sort', ['compareFn?: fn(T, T) -> Int'], '[T]', 'Sort array in place'),
179
+ method('map', ['fn: fn(T) -> U'], '[U]', 'Transform each element'),
180
+ method('filter', ['fn: fn(T) -> Bool'], '[T]', 'Keep elements matching predicate'),
181
+ method('reduce', ['fn: fn(acc, T) -> U', 'initial: U'], 'U', 'Reduce to single value'),
182
+ method('find', ['fn: fn(T) -> Bool'], 'Option', 'Find first matching element'),
183
+ method('findIndex', ['fn: fn(T) -> Bool'], 'Int', 'Index of first match, or -1'),
184
+ method('some', ['fn: fn(T) -> Bool'], 'Bool', 'Check if any element matches'),
185
+ method('every', ['fn: fn(T) -> Bool'], 'Bool', 'Check if all elements match'),
186
+ method('includes', ['item: T'], 'Bool', 'Check if array contains element'),
187
+ method('indexOf', ['item: T'], 'Int', 'First index of element, or -1'),
188
+ method('flat', [], '[T]', 'Flatten one level of nesting'),
189
+ method('flatMap', ['fn: fn(T) -> [U]'], '[U]', 'Map then flatten'),
190
+ method('fill', ['value: T', 'start?: Int', 'end?: Int'], '[T]', 'Fill with value'),
191
+ method('forEach', ['fn: fn(T) -> Nil'], 'Nil', 'Execute function for each element'),
192
+ method('at', ['index: Int'], 'T', 'Element at index (supports negative)'),
193
+ ],
194
+ Result: [
195
+ method('map', ['fn: fn(T) -> U'], 'Result', 'Transform Ok value'),
196
+ method('flatMap', ['fn: fn(T) -> Result'], 'Result', 'Chain Result-returning function'),
197
+ method('andThen', ['fn: fn(T) -> Result'], 'Result', 'Alias for flatMap'),
198
+ method('unwrap', [], 'T', 'Get Ok value or throw on Err'),
199
+ method('unwrapOr', ['default: T'], 'T', 'Get Ok value or return default'),
200
+ method('expect', ['msg: String'], 'T', 'Get Ok value or throw with message'),
201
+ method('isOk', [], 'Bool', 'Check if Result is Ok'),
202
+ method('isErr', [], 'Bool', 'Check if Result is Err'),
203
+ method('mapErr', ['fn: fn(E) -> F'], 'Result', 'Transform Err value'),
204
+ method('unwrapErr', [], 'E', 'Get Err value or throw on Ok'),
205
+ method('or', ['other: Result'], 'Result', 'Return self if Ok, otherwise other'),
206
+ method('and', ['other: Result'], 'Result', 'Return other if Ok, otherwise self'),
207
+ method('context', ['msg: String'], 'Result', 'Add context to Err'),
208
+ ],
209
+ Option: [
210
+ method('map', ['fn: fn(T) -> U'], 'Option', 'Transform Some value'),
211
+ method('flatMap', ['fn: fn(T) -> Option'], 'Option', 'Chain Option-returning function'),
212
+ method('andThen', ['fn: fn(T) -> Option'], 'Option', 'Alias for flatMap'),
213
+ method('unwrap', [], 'T', 'Get Some value or throw on None'),
214
+ method('unwrapOr', ['default: T'], 'T', 'Get Some value or return default'),
215
+ method('expect', ['msg: String'], 'T', 'Get Some value or throw with message'),
216
+ method('isSome', [], 'Bool', 'Check if Option is Some'),
217
+ method('isNone', [], 'Bool', 'Check if Option is None'),
218
+ method('or', ['other: Option'], 'Option', 'Return self if Some, otherwise other'),
219
+ method('and', ['other: Option'], 'Option', 'Return other if Some, otherwise self'),
220
+ method('filter', ['fn: fn(T) -> Bool'], 'Option', 'Keep Some if predicate matches'),
221
+ ],
222
+ Map: [
223
+ field('size', 'Int', 'Number of key-value pairs'),
224
+ method('get', ['key: K'], 'V', 'Get value by key'),
225
+ method('set', ['key: K', 'value: V'], 'Map', 'Set key-value pair'),
226
+ method('has', ['key: K'], 'Bool', 'Check if key exists'),
227
+ method('delete', ['key: K'], 'Bool', 'Remove key-value pair'),
228
+ method('clear', [], 'Nil', 'Remove all entries'),
229
+ method('keys', [], '[K]', 'Get all keys'),
230
+ method('values', [], '[V]', 'Get all values'),
231
+ method('entries', [], '[(K, V)]', 'Get all key-value pairs'),
232
+ method('forEach', ['fn: fn(V, K) -> Nil'], 'Nil', 'Execute function for each entry'),
233
+ ],
234
+ Set: [
235
+ field('size', 'Int', 'Number of elements'),
236
+ method('add', ['value: T'], 'Set', 'Add element'),
237
+ method('has', ['value: T'], 'Bool', 'Check if element exists'),
238
+ method('delete', ['value: T'], 'Bool', 'Remove element'),
239
+ method('clear', [], 'Nil', 'Remove all elements'),
240
+ method('keys', [], '[T]', 'Get all values (alias)'),
241
+ method('values', [], '[T]', 'Get all values'),
242
+ method('entries', [], '[(T, T)]', 'Get all value-value pairs'),
243
+ method('forEach', ['fn: fn(T) -> Nil'], 'Nil', 'Execute function for each element'),
244
+ ],
245
+ Int: [
246
+ method('toString', [], 'String', 'Convert to string'),
247
+ method('toFixed', ['digits?: Int'], 'String', 'Format with fixed decimal places'),
248
+ method('toPrecision', ['precision?: Int'], 'String', 'Format to specified precision'),
249
+ ],
250
+ Float: [
251
+ method('toString', [], 'String', 'Convert to string'),
252
+ method('toFixed', ['digits?: Int'], 'String', 'Format with fixed decimal places'),
253
+ method('toPrecision', ['precision?: Int'], 'String', 'Format to specified precision'),
254
+ ],
255
+ };
package/src/lsp/server.js CHANGED
@@ -8,7 +8,7 @@ import { Analyzer } from '../analyzer/analyzer.js';
8
8
  import { TokenType } from '../lexer/tokens.js';
9
9
  import { Formatter } from '../formatter/formatter.js';
10
10
  import { TypeRegistry } from '../analyzer/type-registry.js';
11
- import { BUILTIN_NAMES, BUILTIN_FUNCTIONS } from '../stdlib/inline.js';
11
+ import { BUILTIN_NAMES, BUILTIN_FUNCTIONS, DEPRECATED_NAMES, SNAKE_TO_CAMEL } from '../stdlib/inline.js';
12
12
 
13
13
  class TovaLanguageServer {
14
14
  static MAX_CACHE_SIZE = 100; // max cached diagnostics entries
@@ -532,7 +532,12 @@ class TovaLanguageServer {
532
532
  for (const fn of BUILTIN_NAMES) {
533
533
  if (fn.startsWith(prefix) && !fn.startsWith('__')) {
534
534
  const detail = this._getBuiltinDetail(fn);
535
- items.push({ label: fn, kind: 3 /* Function */, detail });
535
+ const item = { label: fn, kind: 3 /* Function */, detail };
536
+ if (DEPRECATED_NAMES.has(fn)) {
537
+ item.tags = [1]; // CompletionItemTag.Deprecated
538
+ item.detail = `(deprecated) use ${SNAKE_TO_CAMEL[fn]} → ${detail}`;
539
+ }
540
+ items.push(item);
536
541
  }
537
542
  }
538
543
  // Runtime types
@@ -629,6 +634,36 @@ class TovaLanguageServer {
629
634
  });
630
635
  }
631
636
  }
637
+
638
+ // Fall back to built-in type members if no user-defined members found
639
+ if (items.length === 0) {
640
+ const builtin = typeRegistry.getBuiltinMembers(typeName);
641
+ if (builtin) {
642
+ for (const [fieldName, fieldType] of builtin.fields) {
643
+ if (!partial || fieldName.startsWith(partial)) {
644
+ items.push({
645
+ label: fieldName,
646
+ kind: 5, // Field
647
+ detail: fieldType || 'field',
648
+ sortText: `0${fieldName}`,
649
+ });
650
+ }
651
+ }
652
+ for (const m of builtin.methods) {
653
+ if (!partial || m.name.startsWith(partial)) {
654
+ const paramStr = (m.params || []).join(', ');
655
+ const retStr = m.returnType ? ` -> ${m.returnType}` : '';
656
+ items.push({
657
+ label: m.name,
658
+ kind: 2, // Method
659
+ detail: `fn(${paramStr})${retStr}`,
660
+ documentation: m.doc,
661
+ sortText: `1${m.name}`,
662
+ });
663
+ }
664
+ }
665
+ }
666
+ }
632
667
  }
633
668
  }
634
669
 
@@ -1618,6 +1618,238 @@ function __chart_empty(w, h, msg) { return '<svg xmlns="http://www.w3.org/2000/s
1618
1618
  heatmap: `function heatmap(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var xFn = opts.x; var yFn = opts.y; var valueFn = opts.value; var title = opts.title || ''; var margin = { top: title ? 50 : 40, right: 40, bottom: 60, left: 80 }; var xCats = []; var yCats = []; var xSet = new Set(); var ySet = new Set(); for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); if (!xSet.has(xv)) { xSet.add(xv); xCats.push(xv); } if (!ySet.has(yv)) { ySet.add(yv); yCats.push(yv); } } var grid = {}; var vMn = Infinity; var vMx = -Infinity; for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); var val = Number(valueFn(rows[i])); grid[xv + '|' + yv] = val; if (val < vMn) vMn = val; if (val > vMx) vMx = val; } var vR = vMx - vMn || 1; var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var cellW = plotW / xCats.length; var cellH = plotH / yCats.length; function hc(val) { var t = (val - vMn) / vR; var r = Math.round(255 - t * (255 - 79)); var g = Math.round(255 - t * (255 - 70)); var b = Math.round(255 - t * (255 - 229)); return 'rgb(' + r + ',' + g + ',' + b + ')'; } var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var xi = 0; xi < xCats.length; xi++) { for (var yi = 0; yi < yCats.length; yi++) { var key = xCats[xi] + '|' + yCats[yi]; var val = grid[key]; var rx = margin.left + xi * cellW; var ry = margin.top + yi * cellH; var fill = val !== undefined ? hc(val) : '#f3f4f6'; p.push('<rect x="' + rx + '" y="' + ry + '" width="' + cellW + '" height="' + cellH + '" fill="' + fill + '" stroke="#fff" stroke-width="1"/>'); if (val !== undefined) { var tc = ((val - vMn) / vR) > 0.5 ? '#fff' : '#111'; p.push('<text x="' + (rx + cellW / 2) + '" y="' + (ry + cellH / 2 + 4) + '" text-anchor="middle" font-size="11" fill="' + tc + '">' + __chart_formatNum(val) + '</text>'); } } } for (var xi = 0; xi < xCats.length; xi++) { var lx = margin.left + xi * cellW + cellW / 2; var ly = margin.top + plotH + 16; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="11" fill="#666">' + __chart_esc(xCats[xi]) + '</text>'); } for (var yi = 0; yi < yCats.length; yi++) { var lx = margin.left - 8; var ly = margin.top + yi * cellH + cellH / 2 + 4; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="end" font-size="11" fill="#666">' + __chart_esc(yCats[yi]) + '</text>'); } p.push('</svg>'); return p.join('\\n'); }`,
1619
1619
  };
1620
1620
 
1621
+ // ─── Snake_case → camelCase Migration ──────────────────────────
1622
+ // Maps deprecated snake_case stdlib names to their camelCase replacements.
1623
+ // Both names work; old names emit a deprecation warning via the analyzer.
1624
+ export const SNAKE_TO_CAMEL = {
1625
+ // Collections / iterators
1626
+ type_of: 'typeOf',
1627
+ flat_map: 'flatMap',
1628
+ group_by: 'groupBy',
1629
+ sort_by: 'sortBy',
1630
+ find_index: 'findIndex',
1631
+ min_by: 'minBy',
1632
+ max_by: 'maxBy',
1633
+ sum_by: 'sumBy',
1634
+ zip_with: 'zipWith',
1635
+ sliding_window: 'slidingWindow',
1636
+ binary_search: 'binarySearch',
1637
+ is_sorted: 'isSorted',
1638
+ insert_at: 'insertAt',
1639
+ remove_at: 'removeAt',
1640
+ update_at: 'updateAt',
1641
+ repeat_value: 'repeatValue',
1642
+ from_entries: 'fromEntries',
1643
+ has_key: 'hasKey',
1644
+ map_values: 'mapValues',
1645
+ drop_duplicates: 'dropDuplicates',
1646
+ drop_nil: 'dropNil',
1647
+ fill_nil: 'fillNil',
1648
+ filter_ok: 'filterOk',
1649
+ filter_err: 'filterErr',
1650
+ // Strings
1651
+ starts_with: 'startsWith',
1652
+ ends_with: 'endsWith',
1653
+ title_case: 'titleCase',
1654
+ snake_case: 'snakeCase',
1655
+ camel_case: 'camelCase',
1656
+ kebab_case: 'kebabCase',
1657
+ replace_first: 'replaceFirst',
1658
+ pad_start: 'padStart',
1659
+ pad_end: 'padEnd',
1660
+ char_at: 'charAt',
1661
+ trim_start: 'trimStart',
1662
+ trim_end: 'trimEnd',
1663
+ index_of: 'indexOf',
1664
+ last_index_of: 'lastIndexOf',
1665
+ count_of: 'countOf',
1666
+ reverse_str: 'reverseStr',
1667
+ indent_str: 'indentStr',
1668
+ escape_html: 'escapeHtml',
1669
+ unescape_html: 'unescapeHtml',
1670
+ word_wrap: 'wordWrap',
1671
+ is_empty: 'isEmpty',
1672
+ // Conversions
1673
+ to_int: 'toInt',
1674
+ to_float: 'toFloat',
1675
+ to_string: 'toString',
1676
+ to_bool: 'toBool',
1677
+ to_hex: 'toHex',
1678
+ to_binary: 'toBinary',
1679
+ to_octal: 'toOctal',
1680
+ to_fixed: 'toFixed',
1681
+ to_radians: 'toRadians',
1682
+ to_degrees: 'toDegrees',
1683
+ // Math
1684
+ is_nan: 'isNaN',
1685
+ is_finite: 'isFinite',
1686
+ is_close: 'isClose',
1687
+ random_int: 'randomInt',
1688
+ random_float: 'randomFloat',
1689
+ format_number: 'formatNumber',
1690
+ // Testing
1691
+ assert_eq: 'assertEq',
1692
+ assert_ne: 'assertNe',
1693
+ assert_throws: 'assertThrows',
1694
+ assert_snapshot: 'assertSnapshot',
1695
+ create_spy: 'createSpy',
1696
+ create_mock: 'createMock',
1697
+ // JSON / encoding
1698
+ json_parse: 'jsonParse',
1699
+ json_stringify: 'jsonStringify',
1700
+ json_pretty: 'jsonPretty',
1701
+ base64_encode: 'base64Encode',
1702
+ base64_decode: 'base64Decode',
1703
+ url_encode: 'urlEncode',
1704
+ url_decode: 'urlDecode',
1705
+ hex_encode: 'hexEncode',
1706
+ hex_decode: 'hexDecode',
1707
+ // URL
1708
+ parse_url: 'parseUrl',
1709
+ build_url: 'buildUrl',
1710
+ parse_query: 'parseQuery',
1711
+ build_query: 'buildQuery',
1712
+ // Date / time
1713
+ date_parse: 'dateParse',
1714
+ date_format: 'dateFormat',
1715
+ date_from: 'dateFrom',
1716
+ date_add: 'dateAdd',
1717
+ date_diff: 'dateDiff',
1718
+ date_part: 'datePart',
1719
+ now_iso: 'nowIso',
1720
+ time_ago: 'timeAgo',
1721
+ // Regex
1722
+ regex_test: 'regexTest',
1723
+ regex_match: 'regexMatch',
1724
+ regex_find_all: 'regexFindAll',
1725
+ regex_replace: 'regexReplace',
1726
+ regex_split: 'regexSplit',
1727
+ regex_capture: 'regexCapture',
1728
+ regex_builder: 'regexBuilder',
1729
+ // Validation
1730
+ is_email: 'isEmail',
1731
+ is_url: 'isUrl',
1732
+ is_numeric: 'isNumeric',
1733
+ is_alpha: 'isAlpha',
1734
+ is_alphanumeric: 'isAlphanumeric',
1735
+ is_uuid: 'isUuid',
1736
+ is_hex: 'isHex',
1737
+ // Sets
1738
+ is_subset: 'isSubset',
1739
+ is_superset: 'isSuperset',
1740
+ symmetric_difference: 'symmetricDifference',
1741
+ // Functional
1742
+ compare_by: 'compareBy',
1743
+ pipe_fn: 'pipeFn',
1744
+ try_fn: 'tryFn',
1745
+ try_async: 'tryAsync',
1746
+ schema_of: 'schemaOf',
1747
+ // File system
1748
+ is_file: 'isFile',
1749
+ is_dir: 'isDir',
1750
+ is_symlink: 'isSymlink',
1751
+ glob_files: 'globFiles',
1752
+ read_text: 'readText',
1753
+ read_bytes: 'readBytes',
1754
+ write_text: 'writeText',
1755
+ read_stdin: 'readStdin',
1756
+ read_lines: 'readLines',
1757
+ file_stat: 'fileStat',
1758
+ file_size: 'fileSize',
1759
+ // Path
1760
+ path_join: 'pathJoin',
1761
+ path_dirname: 'pathDirname',
1762
+ path_basename: 'pathBasename',
1763
+ path_resolve: 'pathResolve',
1764
+ path_ext: 'pathExt',
1765
+ path_relative: 'pathRelative',
1766
+ // Script
1767
+ script_path: 'scriptPath',
1768
+ script_dir: 'scriptDir',
1769
+ // Process
1770
+ set_env: 'setEnv',
1771
+ on_signal: 'onSignal',
1772
+ parse_args: 'parseArgs',
1773
+ // CLI
1774
+ choose_many: 'chooseMany',
1775
+ // Concurrency
1776
+ parallel_map: 'parallelMap',
1777
+ // Charts
1778
+ bar_chart: 'barChart',
1779
+ line_chart: 'lineChart',
1780
+ scatter_chart: 'scatterChart',
1781
+ pie_chart: 'pieChart',
1782
+ // Table operations
1783
+ table_where: 'tableWhere',
1784
+ table_select: 'tableSelect',
1785
+ table_derive: 'tableDerive',
1786
+ table_group_by: 'tableGroupBy',
1787
+ table_agg: 'tableAgg',
1788
+ table_sort_by: 'tableSortBy',
1789
+ table_limit: 'tableLimit',
1790
+ table_join: 'tableJoin',
1791
+ table_pivot: 'tablePivot',
1792
+ table_unpivot: 'tableUnpivot',
1793
+ table_explode: 'tableExplode',
1794
+ table_union: 'tableUnion',
1795
+ table_drop_duplicates: 'tableDropDuplicates',
1796
+ table_rename: 'tableRename',
1797
+ table_window: 'tableWindow',
1798
+ table_sample: 'tableSample',
1799
+ table_stratified_sample: 'tableStratifiedSample',
1800
+ // Aggregation functions
1801
+ agg_sum: 'aggSum',
1802
+ agg_count: 'aggCount',
1803
+ agg_mean: 'aggMean',
1804
+ agg_median: 'aggMedian',
1805
+ agg_min: 'aggMin',
1806
+ agg_max: 'aggMax',
1807
+ // Window functions
1808
+ win_row_number: 'winRowNumber',
1809
+ win_rank: 'winRank',
1810
+ win_dense_rank: 'winDenseRank',
1811
+ win_percent_rank: 'winPercentRank',
1812
+ win_ntile: 'winNtile',
1813
+ win_lag: 'winLag',
1814
+ win_lead: 'winLead',
1815
+ win_first_value: 'winFirstValue',
1816
+ win_last_value: 'winLastValue',
1817
+ win_running_sum: 'winRunningSum',
1818
+ win_running_count: 'winRunningCount',
1819
+ win_running_avg: 'winRunningAvg',
1820
+ win_running_min: 'winRunningMin',
1821
+ win_running_max: 'winRunningMax',
1822
+ win_moving_avg: 'winMovingAvg',
1823
+ // Typed arrays
1824
+ typed_sum: 'typedSum',
1825
+ typed_dot: 'typedDot',
1826
+ typed_add: 'typedAdd',
1827
+ typed_scale: 'typedScale',
1828
+ typed_map: 'typedMap',
1829
+ typed_reduce: 'typedReduce',
1830
+ typed_sort: 'typedSort',
1831
+ typed_zeros: 'typedZeros',
1832
+ typed_ones: 'typedOnes',
1833
+ typed_fill: 'typedFill',
1834
+ typed_linspace: 'typedLinspace',
1835
+ typed_norm: 'typedNorm',
1836
+ typed_range: 'typedRange',
1837
+ };
1838
+
1839
+ export const DEPRECATED_NAMES = new Set(Object.keys(SNAKE_TO_CAMEL));
1840
+
1841
+ // Reverse mapping: camelCase → snake_case (for tooling)
1842
+ export const CAMEL_TO_SNAKE = Object.fromEntries(
1843
+ Object.entries(SNAKE_TO_CAMEL).map(([s, c]) => [c, s])
1844
+ );
1845
+
1846
+ // Generate camelCase wrapper functions that delegate to snake_case originals
1847
+ for (const [snake, camel] of Object.entries(SNAKE_TO_CAMEL)) {
1848
+ if (BUILTIN_FUNCTIONS[snake] && !BUILTIN_FUNCTIONS[camel]) {
1849
+ BUILTIN_FUNCTIONS[camel] = `function ${camel}() { return ${snake}.apply(null, arguments); }`;
1850
+ }
1851
+ }
1852
+
1621
1853
  // All known builtin names for matching
1622
1854
  export const BUILTIN_NAMES = new Set(Object.keys(BUILTIN_FUNCTIONS));
1623
1855
 
@@ -1709,6 +1941,14 @@ export const STDLIB_DEPS = {
1709
1941
  heatmap: ['Table', '__chart_helpers'],
1710
1942
  };
1711
1943
 
1944
+ // Generate STDLIB_DEPS entries for camelCase wrappers
1945
+ // Each camelCase wrapper depends on its snake_case original (+ that original's deps)
1946
+ for (const [snake, camel] of Object.entries(SNAKE_TO_CAMEL)) {
1947
+ if (BUILTIN_FUNCTIONS[camel]) {
1948
+ STDLIB_DEPS[camel] = [snake, ...(STDLIB_DEPS[snake] || [])];
1949
+ }
1950
+ }
1951
+
1712
1952
  // Resolve all transitive dependencies for a set of used names
1713
1953
  export function resolveStdlibDeps(usedNames) {
1714
1954
  const resolved = new Set(usedNames);
@@ -1757,16 +1997,20 @@ function _topoSort(names) {
1757
1997
  // Only includes non-internal, non-table functions for backward compat with tests/playground
1758
1998
  const _LEGACY_NAMES = [
1759
1999
  'print', 'len', 'range', 'enumerate', 'sum', 'sorted', 'reversed', 'zip',
1760
- 'min', 'max', 'type_of', 'filter', 'map', 'find', 'any', 'all', 'flat_map',
1761
- 'reduce', 'unique', 'group_by', 'chunk', 'flatten', 'take', 'drop', 'first',
1762
- 'last', 'count', 'partition', 'abs', 'floor', 'ceil', 'round', 'clamp',
1763
- 'sqrt', 'pow', 'random', 'trim', 'split', 'join', 'replace', 'repeat',
2000
+ 'min', 'max', 'type_of', 'typeOf', 'filter', 'map', 'find', 'any', 'all',
2001
+ 'flat_map', 'flatMap', 'reduce', 'unique', 'group_by', 'groupBy',
2002
+ 'chunk', 'flatten', 'take', 'drop', 'first', 'last', 'count', 'partition',
2003
+ 'abs', 'floor', 'ceil', 'round', 'clamp', 'sqrt', 'pow', 'random',
2004
+ 'trim', 'split', 'join', 'replace', 'repeat',
1764
2005
  'keys', 'values', 'entries', 'merge', 'freeze', 'clone', 'sleep',
1765
- 'upper', 'lower', 'contains', 'starts_with', 'ends_with', 'chars', 'words',
1766
- 'lines', 'capitalize', 'title_case', 'snake_case', 'camel_case',
1767
- 'assert_eq', 'assert_ne', 'assert', 'assert_throws',
1768
- 'create_spy', 'create_mock',
1769
- 'parallel_map',
2006
+ 'upper', 'lower', 'contains', 'starts_with', 'startsWith',
2007
+ 'ends_with', 'endsWith', 'chars', 'words', 'lines', 'capitalize',
2008
+ 'title_case', 'titleCase', 'snake_case', 'snakeCase',
2009
+ 'camel_case', 'camelCase',
2010
+ 'assert_eq', 'assertEq', 'assert_ne', 'assertNe', 'assert',
2011
+ 'assert_throws', 'assertThrows',
2012
+ 'create_spy', 'createSpy', 'create_mock', 'createMock',
2013
+ 'parallel_map', 'parallelMap',
1770
2014
  ];
1771
2015
  export const BUILTINS = _LEGACY_NAMES.map(n => BUILTIN_FUNCTIONS[n]).join('\n');
1772
2016
 
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/embed-runtime.js — do not edit
2
- export const VERSION = "0.9.14";
2
+ export const VERSION = "0.10.1";