bonsai-js 1.1.0 → 1.2.0

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/CHANGELOG.md CHANGED
@@ -8,6 +8,15 @@ From 1.0.0, entries are generated at release time from the bump files in
8
8
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
9
9
 
10
10
 
11
+
12
+ ## 1.2.0
13
+ <sub>2026-10-25</sub>
14
+
15
+ - *(minor)*
16
+ Add `bonsai-js/runtime/core` and `bonsai-js/builtins`: a runtime with only the built-in functions passed in `builtins`, so a browser bundle carries just what its rules call (about 30 KB gzipped for typical rules, against 43 KB for `bonsai-js/runtime`). `bonsai()` takes the same `builtins` option, and a call to a built-in left out is a CHECK error, so rules are checked against what the browser can run.
17
+ - *(patch)*
18
+ Built-in function descriptions, which only `describeFunction()`, `listFunctions()`, and the language service read, are no longer bundled with `bonsai-js/runtime`, which is about 1.2 KB smaller gzipped (43 KB). Making each built-in separately removable adds about 0.8 KB to `bonsai-js` (69 KB), `bonsai-js/lite` (57 KB), and the language service (47 KB).
19
+
11
20
  ## 1.1.0
12
21
  <sub>2026-10-24</sub>
13
22
 
package/README.md CHANGED
@@ -62,7 +62,7 @@ Try it in the [Playground](https://danfry1.github.io/bonsai-js/playground).
62
62
  | Evaluation budget for untrusted input (as documented) | Yes, on by default | Parse-time limits only | No expression budget | No | No | No | No |
63
63
  | SQL or MongoDB translation | Yes | No | No | No | No | No | No |
64
64
 
65
- Bonsai is the largest of these in the browser (about 56 KB from `bonsai-js/lite`, without explaining and partial evaluation, and about 44 KB from `bonsai-js/runtime` for rules checked and compiled on a server) and the newest. If rules must also run in Go or Java, CEL or ZEN fit better; if rules are JSON produced by a form builder and size matters most, json-logic-js does. The [full comparison](https://danfry1.github.io/bonsai-js/guide/comparison) covers more libraries, throughput, and the method behind every number.
65
+ Bonsai is the largest of these in the browser (about 57 KB from `bonsai-js/lite`, without explaining and partial evaluation, and about 30 KB from `bonsai-js/runtime/core` for rules checked and compiled on a server, with only the built-in functions they call) and the newest. If rules must also run in Go or Java, CEL or ZEN fit better; if rules are JSON produced by a form builder and size matters most, json-logic-js does. The [full comparison](https://danfry1.github.io/bonsai-js/guide/comparison) covers more libraries, throughput, and the method behind every number.
66
66
 
67
67
  ## Install
68
68
 
@@ -74,7 +74,7 @@ Upgrading from 0.x? The [migration guide](https://danfry1.github.io/bonsai-js/gu
74
74
 
75
75
  Requires Node.js 22 or newer, current Bun, or a modern browser. The package is ESM; `require('bonsai-js')` works where Node can load ES modules synchronously (22.12 and newer); for the TypeScript settings that allow it, see [Install](https://danfry1.github.io/bonsai-js/guide/install#typescript-settings).
76
76
 
77
- In a browser bundle, an app that evaluates expressions adds about 68 KB minified and gzipped: the parser, type checker, compiler, and built-in library, with explaining and partial evaluation included. `bonsai-js/lite` leaves those two out, for about 56 KB, and `bonsai-js/runtime`, which evaluates rules checked and compiled elsewhere, leaves out the checker too, for about 44 KB. `bonsai-js/query` and `bonsai-js/service` are separate entry points; the language service on its own is about 46 KB, since it includes the type checker. Unused modules are tree-shaken: importing only `t` adds under 1 KB, and all the error classes about 1.6 KB. CI enforces these budgets (`bun run check:size`).
77
+ In a browser bundle, an app that evaluates expressions adds about 69 KB minified and gzipped: the parser, type checker, compiler, and built-in library, with explaining and partial evaluation included. `bonsai-js/lite` leaves those two out, for about 57 KB, and `bonsai-js/runtime`, which evaluates rules checked and compiled elsewhere, leaves out the checker too, for about 43 KB, or about 30 KB from `bonsai-js/runtime/core` with only the built-in functions your rules call. `bonsai-js/query` and `bonsai-js/service` are separate entry points; the language service on its own is about 47 KB, since it includes the type checker. Unused modules are tree-shaken: importing only `t` adds under 1 KB, and all the error classes about 1.6 KB. CI enforces these budgets (`bun run check:size`).
78
78
 
79
79
  ## The language in one screen
80
80
 
@@ -94,7 +94,7 @@ startOfDay(order.placedAt, "Europe/Berlin") // calendar math in time
94
94
  // comments work too
95
95
  ```
96
96
 
97
- The full reference is [docs/language.md](https://github.com/danfry1/bonsai-js/blob/v1.1.0/docs/language.md). The rules that matter most:
97
+ The full reference is [docs/language.md](https://github.com/danfry1/bonsai-js/blob/v1.2.0/docs/language.md). The rules that matter most:
98
98
 
99
99
  - There is one absent value, `null`. Missing properties and host `undefined` read as `null`, so `x == null` means "missing or null". A key holding `undefined` is absent everywhere: `{ a: 1, b: undefined }` equals `{ a: 1 }`, and `"b" in` it is false.
100
100
  - `==` compares by value (lists and maps deeply). There is no type coercion: `1 == "1"` is `false` and `"a" + 1` is an error.
@@ -201,7 +201,7 @@ Every limit except `timeout` is on by default; `limits` changes the budget. `0`
201
201
  | `maxPatternLength` | 4,096 | regular expression patterns passed to `matches` |
202
202
  | `timeout` | none | wall-clock milliseconds per evaluation |
203
203
 
204
- The step budget is deterministic: the same expression over the same data uses the same steps on every machine, and at the default budget any expression finishes or fails within about 100 ms on Node (about 150 ms for the slowest shapes we have found). Host functions are trusted code: limits cannot interrupt a synchronous host function that is already running, and a getter or Proxy you put in the context runs when it is read. Set a `timeout` when expressions call slow host functions. See [SECURITY.md](https://github.com/danfry1/bonsai-js/blob/main/SECURITY.md) and [docs/threat-model.md](https://github.com/danfry1/bonsai-js/blob/v1.1.0/docs/threat-model.md).
204
+ The step budget is deterministic: the same expression over the same data uses the same steps on every machine, and at the default budget any expression finishes or fails within about 100 ms on Node (about 150 ms for the slowest shapes we have found). Host functions are trusted code: limits cannot interrupt a synchronous host function that is already running, and a getter or Proxy you put in the context runs when it is read. Set a `timeout` when expressions call slow host functions. See [SECURITY.md](https://github.com/danfry1/bonsai-js/blob/main/SECURITY.md) and [docs/threat-model.md](https://github.com/danfry1/bonsai-js/blob/v1.2.0/docs/threat-model.md).
205
205
 
206
206
  ## Built-in functions
207
207
 
@@ -0,0 +1,91 @@
1
+ import { Builtin } from "../environment.mjs";
2
+ //#region src/builtins/index.d.ts
3
+ export declare const toUpperCase: Builtin;
4
+ export declare const toLowerCase: Builtin;
5
+ export declare const trim: Builtin;
6
+ export declare const trimStart: Builtin;
7
+ export declare const trimEnd: Builtin;
8
+ export declare const startsWith: Builtin;
9
+ export declare const endsWith: Builtin;
10
+ export declare const includes: Builtin;
11
+ export declare const indexOf: Builtin;
12
+ export declare const lastIndexOf: Builtin;
13
+ export declare const slice: Builtin;
14
+ export declare const split: Builtin;
15
+ export declare const replace: Builtin;
16
+ export declare const replaceAll: Builtin;
17
+ export declare const padStart: Builtin;
18
+ export declare const padEnd: Builtin;
19
+ export declare const repeat: Builtin;
20
+ export declare const at: Builtin;
21
+ export declare const matches: Builtin;
22
+ export declare const toString: Builtin;
23
+ export declare const toNumber: Builtin;
24
+ export declare const round: Builtin;
25
+ export declare const floor: Builtin;
26
+ export declare const ceil: Builtin;
27
+ export declare const trunc: Builtin;
28
+ export declare const abs: Builtin;
29
+ export declare const sqrt: Builtin;
30
+ export declare const clamp: Builtin;
31
+ export declare const toFixed: Builtin;
32
+ export declare const formatNumber: Builtin;
33
+ export declare const formatCurrency: Builtin;
34
+ export declare const min: Builtin;
35
+ export declare const max: Builtin;
36
+ export declare const sum: Builtin;
37
+ export declare const avg: Builtin;
38
+ export declare const map: Builtin;
39
+ export declare const filter: Builtin;
40
+ export declare const find: Builtin;
41
+ export declare const findIndex: Builtin;
42
+ export declare const some: Builtin;
43
+ export declare const every: Builtin;
44
+ export declare const none: Builtin;
45
+ export declare const flatMap: Builtin;
46
+ export declare const sortBy: Builtin;
47
+ export declare const groupBy: Builtin;
48
+ export declare const count: Builtin;
49
+ export declare const reduce: Builtin;
50
+ export declare const sort: Builtin;
51
+ export declare const reverse: Builtin;
52
+ export declare const unique: Builtin;
53
+ export declare const flat: Builtin;
54
+ export declare const first: Builtin;
55
+ export declare const last: Builtin;
56
+ export declare const join: Builtin;
57
+ export declare const isEmpty: Builtin;
58
+ export declare const keys: Builtin;
59
+ export declare const values: Builtin;
60
+ export declare const entries: Builtin;
61
+ export declare const type: Builtin;
62
+ export declare const now: Builtin;
63
+ export declare const timestamp: Builtin;
64
+ export declare const duration: Builtin;
65
+ export declare const weeks: Builtin;
66
+ export declare const days: Builtin;
67
+ export declare const hours: Builtin;
68
+ export declare const minutes: Builtin;
69
+ export declare const seconds: Builtin;
70
+ export declare const milliseconds: Builtin;
71
+ export declare const inDays: Builtin;
72
+ export declare const inHours: Builtin;
73
+ export declare const inMinutes: Builtin;
74
+ export declare const inSeconds: Builtin;
75
+ export declare const inMilliseconds: Builtin;
76
+ export declare const year: Builtin;
77
+ export declare const month: Builtin;
78
+ export declare const day: Builtin;
79
+ export declare const hour: Builtin;
80
+ export declare const minute: Builtin;
81
+ export declare const second: Builtin;
82
+ export declare const dayOfWeek: Builtin;
83
+ export declare const startOfDay: Builtin;
84
+ export declare const startOfMonth: Builtin;
85
+ export declare const startOfYear: Builtin;
86
+ export declare const addDays: Builtin;
87
+ export declare const addMonths: Builtin;
88
+ export declare const addYears: Builtin;
89
+ export declare const formatDate: Builtin;
90
+ //#endregion
91
+ export type { Builtin };
@@ -0,0 +1,93 @@
1
+ import { abs as abs$1, addDays as addDays$1, addMonths as addMonths$1, addYears as addYears$1, at as at$1, avg as avg$1, ceil as ceil$1, clamp as clamp$1, count as count$1, day as day$1, dayOfWeek as dayOfWeek$1, days as days$1, duration as duration$1, endsWith as endsWith$1, entries as entries$1, every as every$1, filter as filter$1, find as find$1, findIndex as findIndex$1, first as first$1, flat as flat$1, flatMap as flatMap$1, floor as floor$1, formatCurrency as formatCurrency$1, formatDate as formatDate$1, formatNumber as formatNumber$1, groupBy as groupBy$1, hour as hour$1, hours as hours$1, inDays as inDays$1, inHours as inHours$1, inMilliseconds as inMilliseconds$1, inMinutes as inMinutes$1, inSeconds as inSeconds$1, includes as includes$1, indexOf as indexOf$1, isEmpty as isEmpty$1, join as join$1, keys as keys$1, last as last$1, lastIndexOf as lastIndexOf$1, map as map$1, matches as matches$1, max as max$1, milliseconds as milliseconds$1, min as min$1, minute as minute$1, minutes as minutes$1, month as month$1, none as none$1, now as now$1, padEnd as padEnd$1, padStart as padStart$1, reduce as reduce$1, repeat as repeat$1, replace as replace$1, replaceAll as replaceAll$1, reverse as reverse$1, round as round$1, second as second$1, seconds as seconds$1, slice as slice$1, some as some$1, sort as sort$1, sortBy as sortBy$1, split as split$1, sqrt as sqrt$1, startOfDay as startOfDay$1, startOfMonth as startOfMonth$1, startOfYear as startOfYear$1, startsWith as startsWith$1, sum as sum$1, timestamp as timestamp$1, toFixed as toFixed$1, toLowerCase as toLowerCase$1, toNumber as toNumber$1, toString as toString$1, toUpperCase as toUpperCase$1, trim as trim$1, trimEnd as trimEnd$1, trimStart as trimStart$1, trunc as trunc$1, type as type$1, unique as unique$1, values as values$1, weeks as weeks$1, year as year$1 } from "../functions/builtins.mjs";
2
+ //#region src/builtins/index.ts
3
+ const toUpperCase = toUpperCase$1;
4
+ const toLowerCase = toLowerCase$1;
5
+ const trim = trim$1;
6
+ const trimStart = trimStart$1;
7
+ const trimEnd = trimEnd$1;
8
+ const startsWith = startsWith$1;
9
+ const endsWith = endsWith$1;
10
+ const includes = includes$1;
11
+ const indexOf = indexOf$1;
12
+ const lastIndexOf = lastIndexOf$1;
13
+ const slice = slice$1;
14
+ const split = split$1;
15
+ const replace = replace$1;
16
+ const replaceAll = replaceAll$1;
17
+ const padStart = padStart$1;
18
+ const padEnd = padEnd$1;
19
+ const repeat = repeat$1;
20
+ const at = at$1;
21
+ const matches = matches$1;
22
+ const toString = toString$1;
23
+ const toNumber = toNumber$1;
24
+ const round = round$1;
25
+ const floor = floor$1;
26
+ const ceil = ceil$1;
27
+ const trunc = trunc$1;
28
+ const abs = abs$1;
29
+ const sqrt = sqrt$1;
30
+ const clamp = clamp$1;
31
+ const toFixed = toFixed$1;
32
+ const formatNumber = formatNumber$1;
33
+ const formatCurrency = formatCurrency$1;
34
+ const min = min$1;
35
+ const max = max$1;
36
+ const sum = sum$1;
37
+ const avg = avg$1;
38
+ const map = map$1;
39
+ const filter = filter$1;
40
+ const find = find$1;
41
+ const findIndex = findIndex$1;
42
+ const some = some$1;
43
+ const every = every$1;
44
+ const none = none$1;
45
+ const flatMap = flatMap$1;
46
+ const sortBy = sortBy$1;
47
+ const groupBy = groupBy$1;
48
+ const count = count$1;
49
+ const reduce = reduce$1;
50
+ const sort = sort$1;
51
+ const reverse = reverse$1;
52
+ const unique = unique$1;
53
+ const flat = flat$1;
54
+ const first = first$1;
55
+ const last = last$1;
56
+ const join = join$1;
57
+ const isEmpty = isEmpty$1;
58
+ const keys = keys$1;
59
+ const values = values$1;
60
+ const entries = entries$1;
61
+ const type = type$1;
62
+ const now = now$1;
63
+ const timestamp = timestamp$1;
64
+ const duration = duration$1;
65
+ const weeks = weeks$1;
66
+ const days = days$1;
67
+ const hours = hours$1;
68
+ const minutes = minutes$1;
69
+ const seconds = seconds$1;
70
+ const milliseconds = milliseconds$1;
71
+ const inDays = inDays$1;
72
+ const inHours = inHours$1;
73
+ const inMinutes = inMinutes$1;
74
+ const inSeconds = inSeconds$1;
75
+ const inMilliseconds = inMilliseconds$1;
76
+ const year = year$1;
77
+ const month = month$1;
78
+ const day = day$1;
79
+ const hour = hour$1;
80
+ const minute = minute$1;
81
+ const second = second$1;
82
+ const dayOfWeek = dayOfWeek$1;
83
+ const startOfDay = startOfDay$1;
84
+ const startOfMonth = startOfMonth$1;
85
+ const startOfYear = startOfYear$1;
86
+ const addDays = addDays$1;
87
+ const addMonths = addMonths$1;
88
+ const addYears = addYears$1;
89
+ const formatDate = formatDate$1;
90
+ //#endregion
91
+ export { abs, addDays, addMonths, addYears, at, avg, ceil, clamp, count, day, dayOfWeek, days, duration, endsWith, entries, every, filter, find, findIndex, first, flat, flatMap, floor, formatCurrency, formatDate, formatNumber, groupBy, hour, hours, inDays, inHours, inMilliseconds, inMinutes, inSeconds, includes, indexOf, isEmpty, join, keys, last, lastIndexOf, map, matches, max, milliseconds, min, minute, minutes, month, none, now, padEnd, padStart, reduce, repeat, replace, replaceAll, reverse, round, second, seconds, slice, some, sort, sortBy, split, sqrt, startOfDay, startOfMonth, startOfYear, startsWith, sum, timestamp, toFixed, toLowerCase, toNumber, toString, toUpperCase, trim, trimEnd, trimStart, trunc, type, unique, values, weeks, year };
92
+
93
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["defs.toUpperCase","defs.toLowerCase","defs.trim","defs.trimStart","defs.trimEnd","defs.startsWith","defs.endsWith","defs.includes","defs.indexOf","defs.lastIndexOf","defs.slice","defs.split","defs.replace","defs.replaceAll","defs.padStart","defs.padEnd","defs.repeat","defs.at","defs.matches","defs.toString","defs.toNumber","defs.round","defs.floor","defs.ceil","defs.trunc","defs.abs","defs.sqrt","defs.clamp","defs.toFixed","defs.formatNumber","defs.formatCurrency","defs.min","defs.max","defs.sum","defs.avg","defs.map","defs.filter","defs.find","defs.findIndex","defs.some","defs.every","defs.none","defs.flatMap","defs.sortBy","defs.groupBy","defs.count","defs.reduce","defs.sort","defs.reverse","defs.unique","defs.flat","defs.first","defs.last","defs.join","defs.isEmpty","defs.keys","defs.values","defs.entries","defs.type","defs.now","defs.timestamp","defs.duration","defs.weeks","defs.days","defs.hours","defs.minutes","defs.seconds","defs.milliseconds","defs.inDays","defs.inHours","defs.inMinutes","defs.inSeconds","defs.inMilliseconds","defs.year","defs.month","defs.day","defs.hour","defs.minute","defs.second","defs.dayOfWeek","defs.startOfDay","defs.startOfMonth","defs.startOfYear","defs.addDays","defs.addMonths","defs.addYears","defs.formatDate"],"sources":["../../src/builtins/index.ts"],"sourcesContent":["/**\n * `bonsai-js/builtins`: every built-in function as its own export, for the\n * `builtins` option of bonsai-js/runtime/core's runtime() and of bonsai(). A\n * bundle keeps only the built-ins it imports. Namespace imports avoid clashes\n * with names such as `map` and `keys` in your own code:\n *\n * ```ts\n * import * as b from 'bonsai-js/builtins'\n * const builtins = [b.includes, b.some, b.now, b.days]\n * ```\n *\n * It lists every built-in in src/functions/builtins.ts; tests/runtime-core.test.ts\n * checks that it names each one.\n */\nimport type { Builtin } from '../environment.js'\nimport * as defs from '../functions/builtins.js'\n\nexport const toUpperCase = defs.toUpperCase as unknown as Builtin\nexport const toLowerCase = defs.toLowerCase as unknown as Builtin\nexport const trim = defs.trim as unknown as Builtin\nexport const trimStart = defs.trimStart as unknown as Builtin\nexport const trimEnd = defs.trimEnd as unknown as Builtin\nexport const startsWith = defs.startsWith as unknown as Builtin\nexport const endsWith = defs.endsWith as unknown as Builtin\nexport const includes = defs.includes as unknown as Builtin\nexport const indexOf = defs.indexOf as unknown as Builtin\nexport const lastIndexOf = defs.lastIndexOf as unknown as Builtin\nexport const slice = defs.slice as unknown as Builtin\nexport const split = defs.split as unknown as Builtin\nexport const replace = defs.replace as unknown as Builtin\nexport const replaceAll = defs.replaceAll as unknown as Builtin\nexport const padStart = defs.padStart as unknown as Builtin\nexport const padEnd = defs.padEnd as unknown as Builtin\nexport const repeat = defs.repeat as unknown as Builtin\nexport const at = defs.at as unknown as Builtin\nexport const matches = defs.matches as unknown as Builtin\nexport const toString = defs.toString as unknown as Builtin\nexport const toNumber = defs.toNumber as unknown as Builtin\nexport const round = defs.round as unknown as Builtin\nexport const floor = defs.floor as unknown as Builtin\nexport const ceil = defs.ceil as unknown as Builtin\nexport const trunc = defs.trunc as unknown as Builtin\nexport const abs = defs.abs as unknown as Builtin\nexport const sqrt = defs.sqrt as unknown as Builtin\nexport const clamp = defs.clamp as unknown as Builtin\nexport const toFixed = defs.toFixed as unknown as Builtin\nexport const formatNumber = defs.formatNumber as unknown as Builtin\nexport const formatCurrency = defs.formatCurrency as unknown as Builtin\nexport const min = defs.min as unknown as Builtin\nexport const max = defs.max as unknown as Builtin\nexport const sum = defs.sum as unknown as Builtin\nexport const avg = defs.avg as unknown as Builtin\nexport const map = defs.map as unknown as Builtin\nexport const filter = defs.filter as unknown as Builtin\nexport const find = defs.find as unknown as Builtin\nexport const findIndex = defs.findIndex as unknown as Builtin\nexport const some = defs.some as unknown as Builtin\nexport const every = defs.every as unknown as Builtin\nexport const none = defs.none as unknown as Builtin\nexport const flatMap = defs.flatMap as unknown as Builtin\nexport const sortBy = defs.sortBy as unknown as Builtin\nexport const groupBy = defs.groupBy as unknown as Builtin\nexport const count = defs.count as unknown as Builtin\nexport const reduce = defs.reduce as unknown as Builtin\nexport const sort = defs.sort as unknown as Builtin\nexport const reverse = defs.reverse as unknown as Builtin\nexport const unique = defs.unique as unknown as Builtin\nexport const flat = defs.flat as unknown as Builtin\nexport const first = defs.first as unknown as Builtin\nexport const last = defs.last as unknown as Builtin\nexport const join = defs.join as unknown as Builtin\nexport const isEmpty = defs.isEmpty as unknown as Builtin\nexport const keys = defs.keys as unknown as Builtin\nexport const values = defs.values as unknown as Builtin\nexport const entries = defs.entries as unknown as Builtin\nexport const type = defs.type as unknown as Builtin\nexport const now = defs.now as unknown as Builtin\nexport const timestamp = defs.timestamp as unknown as Builtin\nexport const duration = defs.duration as unknown as Builtin\nexport const weeks = defs.weeks as unknown as Builtin\nexport const days = defs.days as unknown as Builtin\nexport const hours = defs.hours as unknown as Builtin\nexport const minutes = defs.minutes as unknown as Builtin\nexport const seconds = defs.seconds as unknown as Builtin\nexport const milliseconds = defs.milliseconds as unknown as Builtin\nexport const inDays = defs.inDays as unknown as Builtin\nexport const inHours = defs.inHours as unknown as Builtin\nexport const inMinutes = defs.inMinutes as unknown as Builtin\nexport const inSeconds = defs.inSeconds as unknown as Builtin\nexport const inMilliseconds = defs.inMilliseconds as unknown as Builtin\nexport const year = defs.year as unknown as Builtin\nexport const month = defs.month as unknown as Builtin\nexport const day = defs.day as unknown as Builtin\nexport const hour = defs.hour as unknown as Builtin\nexport const minute = defs.minute as unknown as Builtin\nexport const second = defs.second as unknown as Builtin\nexport const dayOfWeek = defs.dayOfWeek as unknown as Builtin\nexport const startOfDay = defs.startOfDay as unknown as Builtin\nexport const startOfMonth = defs.startOfMonth as unknown as Builtin\nexport const startOfYear = defs.startOfYear as unknown as Builtin\nexport const addDays = defs.addDays as unknown as Builtin\nexport const addMonths = defs.addMonths as unknown as Builtin\nexport const addYears = defs.addYears as unknown as Builtin\nexport const formatDate = defs.formatDate as unknown as Builtin\n\nexport type { Builtin } from '../environment.js'\n"],"mappings":";;AAiBA,MAAa,cAAcA;AAC3B,MAAa,cAAcC;AAC3B,MAAa,OAAOC;AACpB,MAAa,YAAYC;AACzB,MAAa,UAAUC;AACvB,MAAa,aAAaC;AAC1B,MAAa,WAAWC;AACxB,MAAa,WAAWC;AACxB,MAAa,UAAUC;AACvB,MAAa,cAAcC;AAC3B,MAAa,QAAQC;AACrB,MAAa,QAAQC;AACrB,MAAa,UAAUC;AACvB,MAAa,aAAaC;AAC1B,MAAa,WAAWC;AACxB,MAAa,SAASC;AACtB,MAAa,SAASC;AACtB,MAAa,KAAKC;AAClB,MAAa,UAAUC;AACvB,MAAa,WAAWC;AACxB,MAAa,WAAWC;AACxB,MAAa,QAAQC;AACrB,MAAa,QAAQC;AACrB,MAAa,OAAOC;AACpB,MAAa,QAAQC;AACrB,MAAa,MAAMC;AACnB,MAAa,OAAOC;AACpB,MAAa,QAAQC;AACrB,MAAa,UAAUC;AACvB,MAAa,eAAeC;AAC5B,MAAa,iBAAiBC;AAC9B,MAAa,MAAMC;AACnB,MAAa,MAAMC;AACnB,MAAa,MAAMC;AACnB,MAAa,MAAMC;AACnB,MAAa,MAAMC;AACnB,MAAa,SAASC;AACtB,MAAa,OAAOC;AACpB,MAAa,YAAYC;AACzB,MAAa,OAAOC;AACpB,MAAa,QAAQC;AACrB,MAAa,OAAOC;AACpB,MAAa,UAAUC;AACvB,MAAa,SAASC;AACtB,MAAa,UAAUC;AACvB,MAAa,QAAQC;AACrB,MAAa,SAASC;AACtB,MAAa,OAAOC;AACpB,MAAa,UAAUC;AACvB,MAAa,SAASC;AACtB,MAAa,OAAOC;AACpB,MAAa,QAAQC;AACrB,MAAa,OAAOC;AACpB,MAAa,OAAOC;AACpB,MAAa,UAAUC;AACvB,MAAa,OAAOC;AACpB,MAAa,SAASC;AACtB,MAAa,UAAUC;AACvB,MAAa,OAAOC;AACpB,MAAa,MAAMC;AACnB,MAAa,YAAYC;AACzB,MAAa,WAAWC;AACxB,MAAa,QAAQC;AACrB,MAAa,OAAOC;AACpB,MAAa,QAAQC;AACrB,MAAa,UAAUC;AACvB,MAAa,UAAUC;AACvB,MAAa,eAAeC;AAC5B,MAAa,SAASC;AACtB,MAAa,UAAUC;AACvB,MAAa,YAAYC;AACzB,MAAa,YAAYC;AACzB,MAAa,iBAAiBC;AAC9B,MAAa,OAAOC;AACpB,MAAa,QAAQC;AACrB,MAAa,MAAMC;AACnB,MAAa,OAAOC;AACpB,MAAa,SAASC;AACtB,MAAa,SAASC;AACtB,MAAa,YAAYC;AACzB,MAAa,aAAaC;AAC1B,MAAa,eAAeC;AAC5B,MAAa,cAAcC;AAC3B,MAAa,UAAUC;AACvB,MAAa,YAAYC;AACzB,MAAa,WAAWC;AACxB,MAAa,aAAaC"}
@@ -4,7 +4,7 @@ import { TypeBudgetExceeded, chargeTypeWork, exactObject, fieldOf, freshLiteral,
4
4
  import { signatureText } from "../functions/define.mjs";
5
5
  import { forEachChild } from "../syntax/ast.mjs";
6
6
  import { bindImplicit } from "./bind.mjs";
7
- import { RESULT_REFINERS } from "../functions/builtins.mjs";
7
+ import { BUILTINS, RESULT_REFINERS } from "../functions/builtins.mjs";
8
8
  import { isStackOverflow, tooDeep } from "../runtime/overflow.mjs";
9
9
  import { closest, didYouMean } from "../suggest.mjs";
10
10
  import { writtenWithExponent } from "../syntax/parser.mjs";
@@ -920,9 +920,25 @@ function analyzeWithin(root, env, options) {
920
920
  return argType;
921
921
  });
922
922
  if (def === void 0) {
923
- const alias = Object.hasOwn(FUNCTION_ALIASES, node.name) ? FUNCTION_ALIASES[node.name] : void 0;
924
- const suggestion = alias === void 0 ? suggest(node.name, () => env.functionNames()) : /"(?<name>[^"]+)"/u.exec(alias)?.groups?.name;
925
- const hint = alias === void 0 ? didYouMean(suggestion) : `; ${alias}`;
923
+ const excluded = BUILTINS.has(node.name);
924
+ let alias = !excluded && Object.hasOwn(FUNCTION_ALIASES, node.name) ? FUNCTION_ALIASES[node.name] : void 0;
925
+ const replacement = alias === void 0 ? void 0 : /"(?<name>[^"]+)"/u.exec(alias)?.groups?.name;
926
+ let leftOut;
927
+ if (replacement !== void 0 && env.lookup(replacement) === void 0) {
928
+ alias = void 0;
929
+ if (BUILTINS.has(replacement)) leftOut = replacement;
930
+ }
931
+ let suggestion;
932
+ let hint;
933
+ if (excluded) hint = `; ${node.name} is a built-in this environment does not include (add it to builtins)`;
934
+ else if (leftOut !== void 0) hint = `; the built-in for this is ${leftOut}, which this environment does not include (add it to builtins)`;
935
+ else if (alias === void 0) {
936
+ suggestion = suggest(node.name, () => env.functionNames());
937
+ hint = didYouMean(suggestion);
938
+ } else {
939
+ suggestion = replacement;
940
+ hint = `; ${alias}`;
941
+ }
926
942
  report("UNKNOWN_FUNCTION", `Unknown function "${node.name}"${hint}`, {
927
943
  start: node.nameStart ?? node.start,
928
944
  end: node.nameEnd ?? node.end