bonsai-js 1.0.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +24 -4
  3. package/dist/artifact.d.mts +47 -0
  4. package/dist/artifact.mjs +139 -0
  5. package/dist/artifact.mjs.map +1 -0
  6. package/dist/builtins/index.d.mts +91 -0
  7. package/dist/builtins/index.mjs +93 -0
  8. package/dist/builtins/index.mjs.map +1 -0
  9. package/dist/check/bind.mjs +117 -0
  10. package/dist/check/bind.mjs.map +1 -0
  11. package/dist/check/checker.mjs +34 -120
  12. package/dist/check/checker.mjs.map +1 -1
  13. package/dist/compile/compiler.mjs +2 -3
  14. package/dist/compile/compiler.mjs.map +1 -1
  15. package/dist/declared.mjs +8 -1
  16. package/dist/declared.mjs.map +1 -1
  17. package/dist/digest.mjs +109 -0
  18. package/dist/digest.mjs.map +1 -0
  19. package/dist/environment.d.mts +14 -1
  20. package/dist/environment.mjs +65 -315
  21. package/dist/environment.mjs.map +1 -1
  22. package/dist/errors.d.mts +21 -2
  23. package/dist/errors.mjs +17 -1
  24. package/dist/errors.mjs.map +1 -1
  25. package/dist/features.mjs +3 -3
  26. package/dist/features.mjs.map +1 -1
  27. package/dist/functions/builtins.mjs +551 -474
  28. package/dist/functions/builtins.mjs.map +1 -1
  29. package/dist/functions/define.mjs +53 -11
  30. package/dist/functions/define.mjs.map +1 -1
  31. package/dist/functions/descriptions.mjs +110 -0
  32. package/dist/functions/descriptions.mjs.map +1 -0
  33. package/dist/index.d.mts +4 -3
  34. package/dist/index.mjs +3 -2
  35. package/dist/lite/index.d.mts +4 -3
  36. package/dist/lite/index.mjs +3 -2
  37. package/dist/lite/index.mjs.map +1 -1
  38. package/dist/load.mjs +307 -0
  39. package/dist/load.mjs.map +1 -0
  40. package/dist/program.mjs +321 -0
  41. package/dist/program.mjs.map +1 -0
  42. package/dist/query/index.mjs +1 -1
  43. package/dist/runtime/core.d.mts +18 -0
  44. package/dist/runtime/core.mjs +31 -0
  45. package/dist/runtime/core.mjs.map +1 -0
  46. package/dist/runtime/create.d.mts +61 -0
  47. package/dist/runtime/create.mjs +76 -0
  48. package/dist/runtime/create.mjs.map +1 -0
  49. package/dist/runtime/index.d.mts +24 -0
  50. package/dist/runtime/index.mjs +34 -0
  51. package/dist/runtime/index.mjs.map +1 -0
  52. package/dist/service/index.mjs +12 -8
  53. package/dist/service/index.mjs.map +1 -1
  54. package/dist/syntax/parser.mjs +1 -1
  55. package/dist/syntax/printer.mjs +1 -1
  56. package/dist/types.mjs +103 -23
  57. package/dist/types.mjs.map +1 -1
  58. package/dist/version.mjs +7 -0
  59. package/dist/version.mjs.map +1 -0
  60. package/package.json +26 -2
@@ -0,0 +1,34 @@
1
+ import { BonsaiArtifactError, BonsaiError, BonsaiLimitError, BonsaiRuntimeError, isBonsaiError } from "../errors.mjs";
2
+ import { Duration } from "./values.mjs";
3
+ import { formatType, t } from "../types.mjs";
4
+ import { BUILTINS } from "../functions/builtins.mjs";
5
+ import { fn, settingsFrom, withContext } from "../environment.mjs";
6
+ import { assertRuntimeOptions, createRuntime } from "./create.mjs";
7
+ //#region src/runtime/index.ts
8
+ /**
9
+ * `bonsai-js/runtime`: loads and evaluates artifacts made by `toArtifact()`
10
+ * from `bonsai-js`, without the parser's checker. Check and compile rules where
11
+ * the full toolchain runs (a server, CI, an admin backend), ship the artifacts,
12
+ * and evaluate them here with the same results. Every built-in is included;
13
+ * bonsai-js/runtime/core includes only those passed in `builtins`.
14
+ */
15
+ /**
16
+ * Creates a runtime: the host functions, variables, and limits artifacts are
17
+ * loaded and evaluated against. Declare them in a module the producing
18
+ * environment shares, since an artifact loads only where its called functions
19
+ * and read variables are declared exactly as where it was made.
20
+ *
21
+ * ```ts
22
+ * const rt = runtime({ variables, functions })
23
+ * const rule = rt.load(artifact, { expect: t.boolean() })
24
+ * rule.evaluateSync({ user: { age: 30 } }) // true
25
+ * ```
26
+ */
27
+ function runtime(options = {}) {
28
+ assertRuntimeOptions(options, false);
29
+ return createRuntime(settingsFrom(void 0, options, BUILTINS), false);
30
+ }
31
+ //#endregion
32
+ export { BonsaiArtifactError, BonsaiError, BonsaiLimitError, BonsaiRuntimeError, Duration, fn, formatType, isBonsaiError, runtime, t, withContext };
33
+
34
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/runtime/index.ts"],"sourcesContent":["/**\n * `bonsai-js/runtime`: loads and evaluates artifacts made by `toArtifact()`\n * from `bonsai-js`, without the parser's checker. Check and compile rules where\n * the full toolchain runs (a server, CI, an admin backend), ship the artifacts,\n * and evaluate them here with the same results. Every built-in is included;\n * bonsai-js/runtime/core includes only those passed in `builtins`.\n */\nimport { settingsFrom, type ContextFor, type Library, type NoVariables } from '../environment.js'\nimport { BUILTINS } from '../functions/builtins.js'\nimport type { Type } from '../types.js'\nimport { assertRuntimeOptions, createRuntime, type Runtime, type RuntimeOptions } from './create.js'\n\n/**\n * Creates a runtime: the host functions, variables, and limits artifacts are\n * loaded and evaluated against. Declare them in a module the producing\n * environment shares, since an artifact loads only where its called functions\n * and read variables are declared exactly as where it was made.\n *\n * ```ts\n * const rt = runtime({ variables, functions })\n * const rule = rt.load(artifact, { expect: t.boolean() })\n * rule.evaluateSync({ user: { age: 30 } }) // true\n * ```\n */\nexport function runtime<\n const V extends Readonly<Record<string, Type>> = NoVariables,\n const L extends readonly Library[] = [],\n const S extends boolean | undefined = undefined,\n>(options: RuntimeOptions<V, L> & { readonly strict?: S } = {}): Runtime<ContextFor<V, L, S>> {\n assertRuntimeOptions(options, false)\n return createRuntime<ContextFor<V, L, S>>(settingsFrom(undefined, options, BUILTINS), false)\n}\n\nexport type { Runtime, RuntimeOptions } from './create.js'\nexport * from './exports.js'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,QAId,UAA0D,CAAC,GAAiC;CAC5F,qBAAqB,SAAS,KAAK;CACnC,OAAO,cAAmC,aAAa,KAAA,GAAW,SAAS,QAAQ,GAAG,KAAK;AAC7F"}
@@ -1,9 +1,10 @@
1
1
  import { BonsaiError, BonsaiSyntaxError, locate } from "../errors.mjs";
2
- import { fieldOf, formatType, isExact, nonNull, t, unionMembers, unionOf, widen } from "../types.mjs";
3
- import { assertType } from "../functions/define.mjs";
2
+ import { fieldOf, formatType, isExact, nonNull, t, typeData, unionMembers, unionOf, widen } from "../types.mjs";
3
+ import { assertType, signatureText } from "../functions/define.mjs";
4
4
  import { forEachChild } from "../syntax/ast.mjs";
5
5
  import { parse } from "../syntax/parser.mjs";
6
- import { acceptsArgument, analyze, signatureText } from "../check/checker.mjs";
6
+ import { acceptsArgument, analyze } from "../check/checker.mjs";
7
+ import { descriptionOf } from "../functions/descriptions.mjs";
7
8
  import { internalsOf } from "../environment.mjs";
8
9
  //#region src/service/index.ts
9
10
  const PROBE = "__bonsai_probe__";
@@ -42,8 +43,11 @@ function serviceExpect(options) {
42
43
  if (typeof options !== "object" || options === null || Array.isArray(options)) throw new TypeError("Language service options must be an object");
43
44
  for (const key of Object.keys(options)) if (key !== "expect") throw new TypeError(`Unknown language service option key "${key}" (expected one of: expect)`);
44
45
  const expect = options.expect;
45
- if (expect !== void 0) assertType(expect, "Language service option \"expect\"");
46
- return expect;
46
+ if (expect === void 0) return void 0;
47
+ assertType(expect, "Language service option \"expect\"");
48
+ const own = typeData(expect);
49
+ if (own !== expect) assertType(own, "Language service option \"expect\"");
50
+ return own;
47
51
  }
48
52
  /**
49
53
  * Editor features over an environment. Nothing here evaluates an expression or
@@ -286,7 +290,7 @@ function createLanguageService(env, options) {
286
290
  label: name,
287
291
  kind: "method",
288
292
  detail: applicable.map((o) => signatureText(name, o)).join("\n"),
289
- documentation: def.description,
293
+ documentation: descriptionOf(def),
290
294
  insertText: `${name}(${takesMore(def) ? "" : ")"}`
291
295
  });
292
296
  }
@@ -320,7 +324,7 @@ function createLanguageService(env, options) {
320
324
  label: name,
321
325
  kind: "function",
322
326
  detail: def.overloads.map((o) => signatureText(name, o)).join("\n"),
323
- documentation: def.description,
327
+ documentation: descriptionOf(def),
324
328
  insertText: `${name}(`
325
329
  });
326
330
  }
@@ -352,7 +356,7 @@ function createLanguageService(env, options) {
352
356
  start: node.nameStart,
353
357
  end: node.nameEnd,
354
358
  detail: def.overloads.map((o) => signatureText(def.name, o)).join("\n"),
355
- documentation: def.description
359
+ documentation: descriptionOf(def)
356
360
  };
357
361
  }
358
362
  const type = analysis.types.get(node);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/service/index.ts"],"sourcesContent":["import { acceptsArgument, analyze, signatureText, type Analysis } from '../check/checker.js'\nimport { internalsOf, type Environment, type LiteEnvironment } from '../environment.js'\nimport { BonsaiError, BonsaiSyntaxError, locate, type Diagnostic } from '../errors.js'\nimport { assertType, type FunctionDef } from '../functions/define.js'\nimport { forEachChild, type Node } from '../syntax/ast.js'\nimport { parse } from '../syntax/parser.js'\nimport {\n fieldOf,\n formatType,\n isExact,\n nonNull,\n t,\n unionMembers,\n unionOf,\n widen,\n type MapType,\n type Type,\n} from '../types.js'\n\nexport type CompletionKind =\n | 'value'\n | 'variable'\n | 'local'\n | 'property'\n | 'function'\n | 'method'\n | 'keyword'\n\nexport interface Completion {\n readonly label: string\n readonly kind: CompletionKind\n /** Type or signature text. */\n readonly detail: string\n readonly documentation?: string\n /** Text to insert (e.g. `trim()` for a method). */\n readonly insertText: string\n /**\n * The range this item replaces, when it differs from the result's range: a\n * field that is not a plain name is inserted as `[\"first-name\"]` in place of\n * the `.` before it.\n */\n readonly range?: { readonly start: number; readonly end: number }\n}\n\nexport interface CompletionResult {\n /** The range the completion replaces (the partially typed name). */\n readonly start: number\n readonly end: number\n readonly items: readonly Completion[]\n}\n\nexport interface HoverResult {\n readonly start: number\n readonly end: number\n /** Type of the expression under the cursor, or the function signature. */\n readonly detail: string\n readonly documentation?: string\n}\n\nexport interface LanguageService {\n /** Completions at a UTF-16 offset in `source`. */\n complete: (source: string, offset: number) => CompletionResult\n /** Type information for the expression at `offset`. */\n hover: (source: string, offset: number) => HoverResult | undefined\n /** Every syntax and check diagnostic, never throwing. */\n diagnostics: (source: string) => readonly Diagnostic[]\n}\n\nconst PROBE = '__bonsai_probe__'\n\nconst CLOSER: Readonly<Record<string, string>> = { '(': ')', '[': ']', '{': '}', '${': '}' }\nconst KEYWORDS = ['true', 'false', 'null', 'let', 'has', 'try', 'not in', 'in']\nconst IDENT_CHAR = /[A-Za-z0-9_$]/u\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/u\n\n/**\n * A UTF-16 offset inside `source`: past the end, or NaN, means the end. A\n * negative or fractional offset is a caller bug (RangeError), and so is a\n * source or an offset of the wrong type (TypeError).\n */\nfunction clampOffset(source: string, offset: number): number {\n if (typeof source !== 'string') throw new TypeError('An expression must be a string')\n if (typeof offset !== 'number') {\n throw new TypeError(`An offset must be a number, not ${typeof offset}`)\n }\n if (offset < 0 || (Number.isFinite(offset) && !Number.isInteger(offset))) {\n throw new RangeError(`An offset must be a whole number of at least 0, not ${offset}`)\n }\n return Number.isNaN(offset) ? source.length : Math.min(offset, source.length)\n}\n\nexport interface LanguageServiceOptions {\n /**\n * The type every expression must have, as `check(source, { expect })` takes\n * it: diagnostics then include EXPECTED_TYPE (a filter editor passes\n * `t.boolean()`).\n */\n readonly expect?: Type | undefined\n}\n\n/** Reads the service options once: only `expect`, which must be a type built with t. */\nfunction serviceExpect(options: unknown): Type | undefined {\n if (options === undefined) return undefined\n if (typeof options !== 'object' || options === null || Array.isArray(options))\n throw new TypeError('Language service options must be an object')\n for (const key of Object.keys(options)) {\n if (key !== 'expect')\n throw new TypeError(`Unknown language service option key \"${key}\" (expected one of: expect)`)\n }\n const expect = (options as { expect?: unknown }).expect\n if (expect !== undefined) assertType(expect, 'Language service option \"expect\"')\n return expect\n}\n\n/**\n * Editor features over an environment. Nothing here evaluates an expression or\n * calls a host function: completions and hovers come from the static checker.\n */\nexport function createLanguageService(\n env: Environment | LiteEnvironment,\n options?: LanguageServiceOptions,\n): LanguageService {\n const { checkEnv, parseLimits } = internalsOf(env)\n const expect = serviceExpect(options)\n const checkOptions = expect === undefined ? undefined : { expect }\n\n function tryAnalyze(source: string, probe?: string): Analysis | undefined {\n try {\n return analyze(parse(source, parseLimits), checkEnv, { probe, expected: expect })\n } catch (error) {\n if (error instanceof BonsaiError) return undefined\n throw error\n }\n }\n\n /**\n * How far `source` parses: Infinity when it parses whole, the offset of the\n * first token it cannot take (its length when it only ends too soon), or -1\n * past a limit. A ` ]` after it, which nothing can continue with, turns\n * \"the expression ended\" into an error past the end.\n */\n function parsesTo(source: string, budget: { left: number }): number {\n for (const text of [source, `${source} ]`]) {\n // Out of parses: no answer, which ends the search.\n if (budget.left-- <= 0) return -1\n try {\n parse(text, parseLimits)\n if (text === source) return Number.POSITIVE_INFINITY\n } catch (error) {\n if (!(error instanceof BonsaiError)) throw error\n if (!(error instanceof BonsaiSyntaxError) || error.span === undefined) return -1\n if (text !== source) return Math.min(error.span.start, source.length)\n }\n }\n return -1\n }\n\n /**\n * An ending for nested open constructs no single ending covers, such as\n * `let v = try(o1.`: at each closer, innermost first, and after the last,\n * the fillers the parser gets through (a few at most), within the parses\n * `budget` has left.\n */\n function builtEnding(\n text: string,\n closers: string,\n budget: { left: number },\n ): string | undefined {\n let built = text\n for (let gap = 0; gap <= closers.length; gap++) {\n const rest = closers.slice(gap)\n for (let round = 0; round < MAX_FILLERS; round++) {\n const reached = parsesTo(built + rest, budget)\n // A mistake before the probe is one no ending can mend.\n if (reached < text.length) return undefined\n if (reached === Number.POSITIVE_INFINITY) return built + rest\n // The parser got past this closer: what is missing is further out.\n if (reached > built.length) break\n let filler: string | undefined\n for (const f of FILLERS) {\n if (budget.left <= 0) return undefined\n if (parsesTo(built + f + rest, budget) >= built.length + f.length) {\n filler = f\n break\n }\n }\n if (filler === undefined) return undefined\n built += filler\n }\n if (gap < closers.length) built += closers[gap]\n }\n return undefined\n }\n\n function complete(source: string, offset: number): CompletionResult {\n const cursor = clampOffset(source, offset)\n let from = cursor\n while (from > 0 && IDENT_CHAR.test(source[from - 1])) from--\n let to = cursor\n while (to < source.length && IDENT_CHAR.test(source[to])) to++\n const typed = source.slice(from, cursor)\n const empty: CompletionResult = { start: from, end: to, items: [] }\n\n const prefix = source.slice(0, from)\n const scan = scanOpen(prefix)\n // Inside a string after == or != (user.plan == \"p|), in a list after `in`\n // (user.plan in [\"pro\", \"f|), or as a call argument (roles.includes(\"a|),\n // xs.sortBy(.k, \"d|)): offer the values the other side allows.\n const quoted =\n /(?:==|!=|[(,]|\\bin\\s*\\[(?:\\s*(?:\"[^\"\\\\]*\"|'[^'\\\\]*')\\s*,)*)\\s*(?<quote>[\"'])(?<typed>[^\"'\\\\]*)$/u.exec(\n source.slice(0, cursor),\n )\n if (quoted !== null && scan.inString) {\n const enumPrefix = quoted.groups?.typed ?? ''\n const quoteAt = cursor - enumPrefix.length\n const outer = source.slice(0, quoteAt - 1)\n const outerScan = scanOpen(outer)\n const analysis = tryAnalyze(`${outer}${PROBE}${outerScan.closers}`, PROBE)\n const values =\n analysis === undefined ? [] : literalValues(analysis).filter((v) => typeof v === 'string')\n const items = values.map((v) => ({\n label: v,\n kind: 'value' as const,\n detail: 'value',\n insertText: v,\n }))\n let end = cursor\n while (end < source.length && source[end] !== quoted.groups?.quote) end++\n return { start: quoteAt, end, items: rank(items, enumPrefix) }\n }\n if (scan.inString || scan.inComment) return empty\n if (from > 0 && /[0-9]/u.test(source[from - 1])) return empty\n\n // Whitespace and comments may sit between a `.` and the cursor.\n const before = withoutTrailingTrivia(prefix)\n const afterDot = before.endsWith('?.') || (before.endsWith('.') && !before.endsWith('...'))\n const probeText = afterDot ? `${before}${PROBE}` : `${prefix}${PROBE}`\n\n // Every parse below counts against one budget, so a completion costs at\n // most a fixed number of parses however the source nests, and at most a\n // few parses' worth of text when the source is long.\n const budget = {\n left: Math.min(\n MAX_COMPLETION_PARSES,\n Math.max(MIN_COMPLETION_PARSES, Math.floor(MAX_COMPLETION_CHARS / probeText.length)),\n ),\n }\n let analysis: Analysis | undefined\n for (const suffix of probeSuffixes(scan.closers)) {\n if (budget.left-- <= 0) break\n analysis = tryAnalyze(probeText + suffix, PROBE)\n if (analysis !== undefined) break\n }\n // The last parse is kept for analyzing the ending found.\n budget.left--\n if (analysis === undefined && budget.left > 0) {\n const ended = builtEnding(probeText, scan.closers, budget)\n if (ended !== undefined) analysis = tryAnalyze(ended, PROBE)\n }\n if (analysis === undefined) return empty\n\n const dotAt = afterDot && !before.endsWith('?.') ? before.length - 1 : undefined\n const items = afterDot\n ? memberCompletions(analysis, dotAt === undefined ? undefined : { start: dotAt, end: to })\n : nameCompletions(analysis)\n if (!afterDot && /(?:==|!=)\\s*$/u.test(prefix)) {\n for (const value of literalValues(analysis)) {\n items.unshift({\n label: JSON.stringify(value),\n kind: 'value',\n detail: 'value',\n insertText: JSON.stringify(value),\n })\n }\n }\n return { start: from, end: to, items: rank(items, typed) }\n }\n\n /** Literal members of the type on the other side of `== probe` or `in [..., probe]`. */\n function literalValues(analysis: Analysis): (string | number | boolean)[] {\n let other: Type | undefined\n const isProbe = (node: Node): boolean => node.type === 'Variable' && node.name === PROBE\n const find = (node: Node): void => {\n if (other !== undefined) return\n if (node.type === 'Binary' && (node.operator === '==' || node.operator === '!=')) {\n if (isProbe(node.right)) other = analysis.types.get(node.left)\n else if (isProbe(node.left)) other = analysis.types.get(node.right)\n if (other !== undefined) return\n }\n if (\n node.type === 'Binary' &&\n (node.operator === 'in' || node.operator === 'not in') &&\n node.right.type === 'List' &&\n node.right.items.some((item) => item.type !== 'Spread' && isProbe(item))\n ) {\n other = analysis.types.get(node.left)\n if (other !== undefined) return\n }\n if (node.type === 'Call') {\n const index = node.args.findIndex((arg) => arg.type !== 'Spread' && isProbe(arg))\n if (index > 0) {\n const receiver = node.args[0]\n if (node.name === 'includes' && index === 1 && receiver.type !== 'Spread') {\n // roles.includes(\"a\"): the list's element type.\n const list = analysis.types.get(receiver)\n const listType = list === undefined ? undefined : nonNull(list)\n if (listType?.kind === 'list') other = listType.element\n } else {\n // A parameter declared as literals (sortBy's \"asc\" | \"desc\").\n const plan = analysis.calls.get(node)\n const overload = plan?.def.overloads[plan.candidates[0] ?? 0]\n const param = overload?.params[index] ?? overload?.rest\n if (param !== undefined && unionMembers(param).every((m) => m.kind === 'literal'))\n other = param\n }\n if (other !== undefined) return\n }\n }\n forEachChild(node, find)\n }\n find(analysis.root)\n if (other === undefined) return []\n return unionMembers(other).flatMap((member) =>\n member.kind === 'literal' ? [member.value] : [],\n )\n }\n\n /** Completions after `receiver.`; `dot` is the range of the `.` and the typed name. */\n function memberCompletions(\n analysis: Analysis,\n dot: { start: number; end: number } | undefined,\n ): Completion[] {\n let receiver: Type | undefined\n // `.name` directly after an implicit-lambda `.` (xs.map(.name)).\n let onItem = false\n const find = (node: Node): void => {\n if (receiver !== undefined) return\n if (node.type === 'Member' && node.name === PROBE) {\n receiver = analysis.types.get(node.object)\n onItem = node.object.type === 'It'\n return\n }\n forEachChild(node, find)\n }\n find(analysis.root)\n if (receiver === undefined) return []\n const target = nonNull(receiver)\n const items: Completion[] = []\n const members = unionMembers(target)\n // Fields of every map member (a union of records offers each one's keys).\n const maps = members.filter((member): member is MapType => member.kind === 'map')\n const names = new Set(maps.flatMap((member) => Object.keys(member.fields)))\n // The type a read has, as check() and hover report it: a member without\n // the field reads its `rest` (or null), and a declared object may hold any key.\n const readOf = (member: MapType, name: string): Type => {\n const field = fieldOf(member, name)\n if (field !== undefined) return field\n if (member.rest !== undefined) return t.optional(member.rest)\n return isExact(member) ? t.null() : t.any()\n }\n for (const name of names) {\n const detail = formatType(\n analysis.probeMembers?.get(name) ?? unionOf(maps.map((member) => readOf(member, name))),\n )\n if (IDENTIFIER.test(name)) {\n items.push({ label: name, kind: 'property', detail, insertText: name })\n } else {\n // `user.first-name` would subtract: index it instead. The item's own\n // `.` stays: xs.map(.[\"first-name\"]).\n items.push({\n label: name,\n kind: 'property',\n detail,\n insertText: `${onItem ? '.' : ''}[${JSON.stringify(name)}]`,\n ...(dot === undefined ? {} : { range: dot }),\n })\n }\n }\n if (\n members.some(\n (member) =>\n member.kind === 'list' ||\n member.kind === 'string' ||\n (member.kind === 'literal' && typeof member.value === 'string'),\n )\n ) {\n items.push({ label: 'length', kind: 'property', detail: 'number', insertText: 'length' })\n }\n // Functions whose first parameter accepts the receiver can be called as\n // methods; for a union, every member must be accepted by some signature.\n const receivers = unionMembers(widen(target))\n for (const name of checkEnv.functionNames()) {\n const def = checkEnv.lookup(name) as FunctionDef\n const firstOf = (o: (typeof def.overloads)[number]): Type | undefined => {\n const first = o.params[0] ?? o.rest\n return first === undefined || first.kind === 'function' ? undefined : first\n }\n const accepts = (o: (typeof def.overloads)[number], member: Type): boolean => {\n const first = firstOf(o)\n return first !== undefined && (member.kind === 'any' || acceptsArgument(first, member))\n }\n const applicable = def.overloads.filter((o) => receivers.some((member) => accepts(o, member)))\n if (\n applicable.length === 0 ||\n !receivers.every((member) => applicable.some((o) => accepts(o, member)))\n )\n continue\n items.push({\n label: name,\n kind: 'method',\n detail: applicable.map((o) => signatureText(name, o)).join('\\n'),\n documentation: def.description,\n insertText: `${name}(${takesMore(def) ? '' : ')'}`,\n })\n }\n return items\n }\n\n function nameCompletions(analysis: Analysis): Completion[] {\n const items: Completion[] = []\n const scope = analysis.probeScope\n if (scope?.it !== undefined) {\n items.push({\n label: '.',\n kind: 'local',\n detail: formatType(scope.it),\n documentation: 'The current item',\n insertText: '.',\n })\n }\n for (const [name, type] of scope?.locals ?? []) {\n items.push({ label: name, kind: 'local', detail: formatType(type), insertText: name })\n }\n for (const [name, type] of Object.entries(checkEnv.variables ?? {})) {\n items.push({ label: name, kind: 'variable', detail: formatType(type), insertText: name })\n }\n for (const name of checkEnv.functionNames()) {\n const def = checkEnv.lookup(name) as FunctionDef\n items.push({\n label: name,\n kind: 'function',\n detail: def.overloads.map((o) => signatureText(name, o)).join('\\n'),\n documentation: def.description,\n insertText: `${name}(`,\n })\n }\n for (const keyword of KEYWORDS) {\n items.push({\n label: keyword,\n kind: 'keyword',\n detail: 'keyword',\n insertText: keyword === 'has' || keyword === 'try' ? `${keyword}(` : keyword,\n })\n }\n return items\n }\n\n function hover(source: string, rawOffset: number): HoverResult | undefined {\n const offset = clampOffset(source, rawOffset)\n const analysis = tryAnalyze(source)\n if (analysis === undefined) return undefined\n let best: Node | undefined\n const visit = (node: Node): void => {\n if (offset < node.start || offset > node.end) return\n // The implicit item's `.` ends where the name after it starts (`.cat`):\n // an offset on that name belongs to the member, not the item.\n if (node.type === 'It' && offset === node.end && best?.type === 'Member') return\n best = node\n forEachChild(node, visit)\n }\n visit(analysis.root)\n if (best === undefined) return undefined\n const node: Node = best\n if (\n node.type === 'Call' &&\n node.nameStart !== undefined &&\n node.nameEnd !== undefined &&\n offset >= node.nameStart &&\n offset <= node.nameEnd\n ) {\n const def = checkEnv.lookup(node.name)\n if (def !== undefined) {\n return {\n start: node.nameStart,\n end: node.nameEnd,\n detail: def.overloads.map((o) => signatureText(def.name, o)).join('\\n'),\n documentation: def.description,\n }\n }\n }\n const type = analysis.types.get(node)\n if (type === undefined) return undefined\n return {\n start: node.start,\n end: node.end,\n detail: formatType(type.kind === 'literal' ? widen(type) : type),\n }\n }\n\n function diagnostics(source: string): readonly Diagnostic[] {\n // Spans stay inside the source (an error at the end is zero-width there).\n const found = env.check(source, checkOptions).diagnostics\n if (found.every((d) => d.start >= 0 && d.end >= d.start && d.end <= source.length)) return found\n return locate(\n source,\n found.map((d) => {\n const start = Math.min(Math.max(d.start, 0), source.length)\n return { ...d, start, end: Math.min(Math.max(d.end, start), source.length) }\n }),\n )\n }\n\n return Object.freeze({ complete, hover, diagnostics })\n}\n\nfunction takesMore(def: FunctionDef): boolean {\n return def.overloads.some((o) => o.params.length > 1 || o.rest !== undefined)\n}\n\nfunction rank(items: readonly Completion[], typed: string): Completion[] {\n const needle = typed.toLowerCase()\n const scored = items\n .map((item) => {\n const label = item.label.toLowerCase()\n let score = 0\n if (needle === '') score = 1\n else if (label.startsWith(needle)) score = 2\n else if (label.includes(needle)) score = 1\n return { item, score }\n })\n .filter((entry) => entry.score > 0)\n const order: Record<CompletionKind, number> = {\n value: -1,\n local: 0,\n property: 1,\n variable: 2,\n method: 3,\n function: 4,\n keyword: 5,\n }\n scored.sort(\n (a, b) =>\n b.score - a.score ||\n order[a.item.kind] - order[b.item.kind] ||\n a.item.label.localeCompare(b.item.label),\n )\n const seen = new Set<string>()\n return scored\n .map((entry) => entry.item)\n .filter((item) => !seen.has(item.label) && (seen.add(item.label), true))\n}\n\n/** `text` without the whitespace and comments at its end. */\nfunction withoutTrailingTrivia(text: string): string {\n const startOf = new Map(commentSpans(text).map((span) => [span.end, span.start]))\n let end = text.length\n for (;;) {\n while (end > 0 && /\\s/u.test(text[end - 1])) end--\n const start = startOf.get(end)\n if (start === undefined) break\n end = start\n }\n return text.slice(0, end)\n}\n\n/** The comments in `text` (outside strings and templates), as [start, end) spans. */\nfunction commentSpans(text: string): { start: number; end: number }[] {\n const spans: { start: number; end: number }[] = []\n let i = 0\n let template = 0\n while (i < text.length) {\n const ch = text[i]\n if (template > 0 && ch === '`') {\n template--\n i++\n } else if (template > 0) {\n i += ch === '\\\\' ? 2 : 1\n } else if (ch === '`') {\n template++\n i++\n } else if (ch === '/' && text[i + 1] === '/') {\n let end = text.indexOf('\\n', i)\n if (end === -1) end = text.length\n spans.push({ start: i, end })\n i = end\n } else if (ch === '/' && text[i + 1] === '*') {\n const close = text.indexOf('*/', i + 2)\n const end = close === -1 ? text.length : close + 2\n spans.push({ start: i, end })\n i = end\n } else if (ch === '\"' || ch === \"'\") {\n let j = i + 1\n while (j < text.length && text[j] !== ch) j += text[j] === '\\\\' ? 2 : 1\n i = j + 1\n } else i++\n }\n return spans\n}\n\n/** Open constructs a filler is tried inside of, innermost first. */\nconst PROBE_SPLITS = 4\n\n/** What a nested ending may add at one closer: a missing branch, argument, value, or body. */\nconst FILLERS = [' : null', ', null', ': null', '; null', ' 0']\n/** Fillers one closer may take, as in try(b ? x : null, null). */\nconst MAX_FILLERS = 3\n/**\n * Parses one completion may cost in all (fixed endings, nested ending search,\n * and its analysis): enough for every ending a short source needs.\n */\nconst MAX_COMPLETION_PARSES = 100\n/**\n * Characters one completion may parse in all, so a long source gets fewer\n * tries (each costs a parse of the whole source), but never fewer than\n * {@link MIN_COMPLETION_PARSES}.\n */\nconst MAX_COMPLETION_CHARS = 1_200_000\n/** Enough for the fixed endings a single open construct needs, plus the analysis. */\nconst MIN_COMPLETION_PARSES = 12\n\n/**\n * Endings that make a prefix ending in the probe parse, cheapest first: the\n * closers alone, a missing branch or value inside them, the missing half of\n * an enclosing try(x, fallback) or computed key [k]: v, and a `let` body or\n * `?:` branch after them.\n */\nfunction probeSuffixes(closers: string): string[] {\n const out = [\n closers,\n ` : null${closers}`,\n `; null${closers}`,\n ` 0${closers}`,\n `, null${closers}`,\n `${closers}; null`,\n `${closers} : null`,\n `${closers} : null; null`,\n ]\n for (let at = 1; at <= Math.min(closers.length, PROBE_SPLITS); at++) {\n const inner = closers.slice(0, at)\n const outer = closers.slice(at)\n out.push(`${inner}: null${outer}`, `${inner}, null${outer}`)\n }\n return [...new Set(out)]\n}\n\n/** Tracks open brackets, strings, and templates in a prefix to synthesize closers. */\nfunction scanOpen(text: string): { closers: string; inString: boolean; inComment: boolean } {\n const stack: string[] = []\n let i = 0\n while (i < text.length) {\n const ch = text[i]\n const top = stack[stack.length - 1]\n if (top === '`') {\n if (ch === '\\\\') i += 2\n else if (ch === '`') {\n stack.pop()\n i++\n } else if (ch === '$' && text[i + 1] === '{') {\n stack.push('${')\n i += 2\n } else i++\n continue\n }\n if (ch === '/' && text[i + 1] === '/') {\n const end = text.indexOf('\\n', i)\n if (end === -1) return { closers: '', inString: false, inComment: true }\n i = end\n continue\n }\n if (ch === '/' && text[i + 1] === '*') {\n const end = text.indexOf('*/', i + 2)\n if (end === -1) return { closers: '', inString: false, inComment: true }\n i = end + 2\n continue\n }\n if (ch === '\"' || ch === \"'\") {\n let j = i + 1\n while (j < text.length && text[j] !== ch) j += text[j] === '\\\\' ? 2 : 1\n if (j >= text.length) return { closers: '', inString: true, inComment: false }\n i = j + 1\n continue\n }\n if (ch === '`') stack.push('`')\n else if (ch === '(' || ch === '[' || ch === '{') stack.push(ch)\n else if (ch === ')' || ch === ']' || ch === '}') {\n if (top === '${' && ch === '}') stack.pop()\n else if (top === '(' || top === '[' || top === '{') stack.pop()\n }\n i++\n }\n if (stack[stack.length - 1] === '`') return { closers: '', inString: true, inComment: false }\n const closers = stack\n .reverse()\n .map((open) => CLOSER[open] ?? '`')\n .join('')\n return { closers, inString: false, inComment: false }\n}\n"],"mappings":";;;;;;;;AAoEA,MAAM,QAAQ;AAEd,MAAM,SAA2C;CAAE,KAAK;CAAK,KAAK;CAAK,KAAK;CAAK,MAAM;AAAI;AAC3F,MAAM,WAAW;CAAC;CAAQ;CAAS;CAAQ;CAAO;CAAO;CAAO;CAAU;AAAI;AAC9E,MAAM,aAAa;AACnB,MAAM,aAAa;;;;;;AAOnB,SAAS,YAAY,QAAgB,QAAwB;CAC3D,IAAI,OAAO,WAAW,UAAU,MAAM,IAAI,UAAU,gCAAgC;CACpF,IAAI,OAAO,WAAW,UACpB,MAAM,IAAI,UAAU,mCAAmC,OAAO,QAAQ;CAExE,IAAI,SAAS,KAAM,OAAO,SAAS,MAAM,KAAK,CAAC,OAAO,UAAU,MAAM,GACpE,MAAM,IAAI,WAAW,uDAAuD,QAAQ;CAEtF,OAAO,OAAO,MAAM,MAAM,IAAI,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,MAAM;AAC9E;;AAYA,SAAS,cAAc,SAAoC;CACzD,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,IAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,MAAM,QAAQ,OAAO,GAC1E,MAAM,IAAI,UAAU,4CAA4C;CAClE,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,QAAQ,UACV,MAAM,IAAI,UAAU,wCAAwC,IAAI,4BAA4B;CAEhG,MAAM,SAAU,QAAiC;CACjD,IAAI,WAAW,KAAA,GAAW,WAAW,QAAQ,oCAAkC;CAC/E,OAAO;AACT;;;;;AAMA,SAAgB,sBACd,KACA,SACiB;CACjB,MAAM,EAAE,UAAU,gBAAgB,YAAY,GAAG;CACjD,MAAM,SAAS,cAAc,OAAO;CACpC,MAAM,eAAe,WAAW,KAAA,IAAY,KAAA,IAAY,EAAE,OAAO;CAEjE,SAAS,WAAW,QAAgB,OAAsC;EACxE,IAAI;GACF,OAAO,QAAQ,MAAM,QAAQ,WAAW,GAAG,UAAU;IAAE;IAAO,UAAU;GAAO,CAAC;EAClF,SAAS,OAAO;GACd,IAAI,iBAAiB,aAAa,OAAO,KAAA;GACzC,MAAM;EACR;CACF;;;;;;;CAQA,SAAS,SAAS,QAAgB,QAAkC;EAClE,KAAK,MAAM,QAAQ,CAAC,QAAQ,GAAG,OAAO,GAAG,GAAG;GAE1C,IAAI,OAAO,UAAU,GAAG,OAAO;GAC/B,IAAI;IACF,MAAM,MAAM,WAAW;IACvB,IAAI,SAAS,QAAQ,OAAO,OAAO;GACrC,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,cAAc,MAAM;IAC3C,IAAI,EAAE,iBAAiB,sBAAsB,MAAM,SAAS,KAAA,GAAW,OAAO;IAC9E,IAAI,SAAS,QAAQ,OAAO,KAAK,IAAI,MAAM,KAAK,OAAO,OAAO,MAAM;GACtE;EACF;EACA,OAAO;CACT;;;;;;;CAQA,SAAS,YACP,MACA,SACA,QACoB;EACpB,IAAI,QAAQ;EACZ,KAAK,IAAI,MAAM,GAAG,OAAO,QAAQ,QAAQ,OAAO;GAC9C,MAAM,OAAO,QAAQ,MAAM,GAAG;GAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,aAAa,SAAS;IAChD,MAAM,UAAU,SAAS,QAAQ,MAAM,MAAM;IAE7C,IAAI,UAAU,KAAK,QAAQ,OAAO,KAAA;IAClC,IAAI,YAAY,OAAO,mBAAmB,OAAO,QAAQ;IAEzD,IAAI,UAAU,MAAM,QAAQ;IAC5B,IAAI;IACJ,KAAK,MAAM,KAAK,SAAS;KACvB,IAAI,OAAO,QAAQ,GAAG,OAAO,KAAA;KAC7B,IAAI,SAAS,QAAQ,IAAI,MAAM,MAAM,KAAK,MAAM,SAAS,EAAE,QAAQ;MACjE,SAAS;MACT;KACF;IACF;IACA,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;IACjC,SAAS;GACX;GACA,IAAI,MAAM,QAAQ,QAAQ,SAAS,QAAQ;EAC7C;CAEF;CAEA,SAAS,SAAS,QAAgB,QAAkC;EAClE,MAAM,SAAS,YAAY,QAAQ,MAAM;EACzC,IAAI,OAAO;EACX,OAAO,OAAO,KAAK,WAAW,KAAK,OAAO,OAAO,EAAE,GAAG;EACtD,IAAI,KAAK;EACT,OAAO,KAAK,OAAO,UAAU,WAAW,KAAK,OAAO,GAAG,GAAG;EAC1D,MAAM,QAAQ,OAAO,MAAM,MAAM,MAAM;EACvC,MAAM,QAA0B;GAAE,OAAO;GAAM,KAAK;GAAI,OAAO,CAAC;EAAE;EAElE,MAAM,SAAS,OAAO,MAAM,GAAG,IAAI;EACnC,MAAM,OAAO,SAAS,MAAM;EAI5B,MAAM,SACJ,mGAAmG,KACjG,OAAO,MAAM,GAAG,MAAM,CACxB;EACF,IAAI,WAAW,QAAQ,KAAK,UAAU;GACpC,MAAM,aAAa,OAAO,QAAQ,SAAS;GAC3C,MAAM,UAAU,SAAS,WAAW;GACpC,MAAM,QAAQ,OAAO,MAAM,GAAG,UAAU,CAAC;GACzC,MAAM,YAAY,SAAS,KAAK;GAChC,MAAM,WAAW,WAAW,GAAG,QAAQ,QAAQ,UAAU,WAAW,KAAK;GAGzE,MAAM,SADJ,aAAa,KAAA,IAAY,CAAC,IAAI,cAAc,QAAQ,CAAC,CAAC,QAAQ,MAAM,OAAO,MAAM,QAAQ,EAAA,CACtE,KAAK,OAAO;IAC/B,OAAO;IACP,MAAM;IACN,QAAQ;IACR,YAAY;GACd,EAAE;GACF,IAAI,MAAM;GACV,OAAO,MAAM,OAAO,UAAU,OAAO,SAAS,OAAO,QAAQ,OAAO;GACpE,OAAO;IAAE,OAAO;IAAS;IAAK,OAAO,KAAK,OAAO,UAAU;GAAE;EAC/D;EACA,IAAI,KAAK,YAAY,KAAK,WAAW,OAAO;EAC5C,IAAI,OAAO,KAAK,SAAS,KAAK,OAAO,OAAO,EAAE,GAAG,OAAO;EAGxD,MAAM,SAAS,sBAAsB,MAAM;EAC3C,MAAM,WAAW,OAAO,SAAS,IAAI,KAAM,OAAO,SAAS,GAAG,KAAK,CAAC,OAAO,SAAS,KAAK;EACzF,MAAM,YAAY,WAAW,GAAG,SAAS,UAAU,GAAG,SAAS;EAK/D,MAAM,SAAS,EACb,MAAM,KAAK,IACT,uBACA,KAAK,IAAI,uBAAuB,KAAK,MAAM,uBAAuB,UAAU,MAAM,CAAC,CACrF,EACF;EACA,IAAI;EACJ,KAAK,MAAM,UAAU,cAAc,KAAK,OAAO,GAAG;GAChD,IAAI,OAAO,UAAU,GAAG;GACxB,WAAW,WAAW,YAAY,QAAQ,KAAK;GAC/C,IAAI,aAAa,KAAA,GAAW;EAC9B;EAEA,OAAO;EACP,IAAI,aAAa,KAAA,KAAa,OAAO,OAAO,GAAG;GAC7C,MAAM,QAAQ,YAAY,WAAW,KAAK,SAAS,MAAM;GACzD,IAAI,UAAU,KAAA,GAAW,WAAW,WAAW,OAAO,KAAK;EAC7D;EACA,IAAI,aAAa,KAAA,GAAW,OAAO;EAEnC,MAAM,QAAQ,YAAY,CAAC,OAAO,SAAS,IAAI,IAAI,OAAO,SAAS,IAAI,KAAA;EACvE,MAAM,QAAQ,WACV,kBAAkB,UAAU,UAAU,KAAA,IAAY,KAAA,IAAY;GAAE,OAAO;GAAO,KAAK;EAAG,CAAC,IACvF,gBAAgB,QAAQ;EAC5B,IAAI,CAAC,YAAY,iBAAiB,KAAK,MAAM,GAC3C,KAAK,MAAM,SAAS,cAAc,QAAQ,GACxC,MAAM,QAAQ;GACZ,OAAO,KAAK,UAAU,KAAK;GAC3B,MAAM;GACN,QAAQ;GACR,YAAY,KAAK,UAAU,KAAK;EAClC,CAAC;EAGL,OAAO;GAAE,OAAO;GAAM,KAAK;GAAI,OAAO,KAAK,OAAO,KAAK;EAAE;CAC3D;;CAGA,SAAS,cAAc,UAAmD;EACxE,IAAI;EACJ,MAAM,WAAW,SAAwB,KAAK,SAAS,cAAc,KAAK,SAAS;EACnF,MAAM,QAAQ,SAAqB;GACjC,IAAI,UAAU,KAAA,GAAW;GACzB,IAAI,KAAK,SAAS,aAAa,KAAK,aAAa,QAAQ,KAAK,aAAa,OAAO;IAChF,IAAI,QAAQ,KAAK,KAAK,GAAG,QAAQ,SAAS,MAAM,IAAI,KAAK,IAAI;SACxD,IAAI,QAAQ,KAAK,IAAI,GAAG,QAAQ,SAAS,MAAM,IAAI,KAAK,KAAK;IAClE,IAAI,UAAU,KAAA,GAAW;GAC3B;GACA,IACE,KAAK,SAAS,aACb,KAAK,aAAa,QAAQ,KAAK,aAAa,aAC7C,KAAK,MAAM,SAAS,UACpB,KAAK,MAAM,MAAM,MAAM,SAAS,KAAK,SAAS,YAAY,QAAQ,IAAI,CAAC,GACvE;IACA,QAAQ,SAAS,MAAM,IAAI,KAAK,IAAI;IACpC,IAAI,UAAU,KAAA,GAAW;GAC3B;GACA,IAAI,KAAK,SAAS,QAAQ;IACxB,MAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ,IAAI,SAAS,YAAY,QAAQ,GAAG,CAAC;IAChF,IAAI,QAAQ,GAAG;KACb,MAAM,WAAW,KAAK,KAAK;KAC3B,IAAI,KAAK,SAAS,cAAc,UAAU,KAAK,SAAS,SAAS,UAAU;MAEzE,MAAM,OAAO,SAAS,MAAM,IAAI,QAAQ;MACxC,MAAM,WAAW,SAAS,KAAA,IAAY,KAAA,IAAY,QAAQ,IAAI;MAC9D,IAAI,UAAU,SAAS,QAAQ,QAAQ,SAAS;KAClD,OAAO;MAEL,MAAM,OAAO,SAAS,MAAM,IAAI,IAAI;MACpC,MAAM,WAAW,MAAM,IAAI,UAAU,KAAK,WAAW,MAAM;MAC3D,MAAM,QAAQ,UAAU,OAAO,UAAU,UAAU;MACnD,IAAI,UAAU,KAAA,KAAa,aAAa,KAAK,CAAC,CAAC,OAAO,MAAM,EAAE,SAAS,SAAS,GAC9E,QAAQ;KACZ;KACA,IAAI,UAAU,KAAA,GAAW;IAC3B;GACF;GACA,aAAa,MAAM,IAAI;EACzB;EACA,KAAK,SAAS,IAAI;EAClB,IAAI,UAAU,KAAA,GAAW,OAAO,CAAC;EACjC,OAAO,aAAa,KAAK,CAAC,CAAC,SAAS,WAClC,OAAO,SAAS,YAAY,CAAC,OAAO,KAAK,IAAI,CAAC,CAChD;CACF;;CAGA,SAAS,kBACP,UACA,KACc;EACd,IAAI;EAEJ,IAAI,SAAS;EACb,MAAM,QAAQ,SAAqB;GACjC,IAAI,aAAa,KAAA,GAAW;GAC5B,IAAI,KAAK,SAAS,YAAY,KAAK,SAAS,OAAO;IACjD,WAAW,SAAS,MAAM,IAAI,KAAK,MAAM;IACzC,SAAS,KAAK,OAAO,SAAS;IAC9B;GACF;GACA,aAAa,MAAM,IAAI;EACzB;EACA,KAAK,SAAS,IAAI;EAClB,IAAI,aAAa,KAAA,GAAW,OAAO,CAAC;EACpC,MAAM,SAAS,QAAQ,QAAQ;EAC/B,MAAM,QAAsB,CAAC;EAC7B,MAAM,UAAU,aAAa,MAAM;EAEnC,MAAM,OAAO,QAAQ,QAAQ,WAA8B,OAAO,SAAS,KAAK;EAChF,MAAM,QAAQ,IAAI,IAAI,KAAK,SAAS,WAAW,OAAO,KAAK,OAAO,MAAM,CAAC,CAAC;EAG1E,MAAM,UAAU,QAAiB,SAAuB;GACtD,MAAM,QAAQ,QAAQ,QAAQ,IAAI;GAClC,IAAI,UAAU,KAAA,GAAW,OAAO;GAChC,IAAI,OAAO,SAAS,KAAA,GAAW,OAAO,EAAE,SAAS,OAAO,IAAI;GAC5D,OAAO,QAAQ,MAAM,IAAI,EAAE,KAAK,IAAI,EAAE,IAAI;EAC5C;EACA,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,SAAS,WACb,SAAS,cAAc,IAAI,IAAI,KAAK,QAAQ,KAAK,KAAK,WAAW,OAAO,QAAQ,IAAI,CAAC,CAAC,CACxF;GACA,IAAI,WAAW,KAAK,IAAI,GACtB,MAAM,KAAK;IAAE,OAAO;IAAM,MAAM;IAAY;IAAQ,YAAY;GAAK,CAAC;QAItE,MAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN;IACA,YAAY,GAAG,SAAS,MAAM,GAAG,GAAG,KAAK,UAAU,IAAI,EAAE;IACzD,GAAI,QAAQ,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,IAAI;GAC5C,CAAC;EAEL;EACA,IACE,QAAQ,MACL,WACC,OAAO,SAAS,UAChB,OAAO,SAAS,YACf,OAAO,SAAS,aAAa,OAAO,OAAO,UAAU,QAC1D,GAEA,MAAM,KAAK;GAAE,OAAO;GAAU,MAAM;GAAY,QAAQ;GAAU,YAAY;EAAS,CAAC;EAI1F,MAAM,YAAY,aAAa,MAAM,MAAM,CAAC;EAC5C,KAAK,MAAM,QAAQ,SAAS,cAAc,GAAG;GAC3C,MAAM,MAAM,SAAS,OAAO,IAAI;GAChC,MAAM,WAAW,MAAwD;IACvE,MAAM,QAAQ,EAAE,OAAO,MAAM,EAAE;IAC/B,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,aAAa,KAAA,IAAY;GACxE;GACA,MAAM,WAAW,GAAmC,WAA0B;IAC5E,MAAM,QAAQ,QAAQ,CAAC;IACvB,OAAO,UAAU,KAAA,MAAc,OAAO,SAAS,SAAS,gBAAgB,OAAO,MAAM;GACvF;GACA,MAAM,aAAa,IAAI,UAAU,QAAQ,MAAM,UAAU,MAAM,WAAW,QAAQ,GAAG,MAAM,CAAC,CAAC;GAC7F,IACE,WAAW,WAAW,KACtB,CAAC,UAAU,OAAO,WAAW,WAAW,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,GAEvE;GACF,MAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ,WAAW,KAAK,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;IAC/D,eAAe,IAAI;IACnB,YAAY,GAAG,KAAK,GAAG,UAAU,GAAG,IAAI,KAAK;GAC/C,CAAC;EACH;EACA,OAAO;CACT;CAEA,SAAS,gBAAgB,UAAkC;EACzD,MAAM,QAAsB,CAAC;EAC7B,MAAM,QAAQ,SAAS;EACvB,IAAI,OAAO,OAAO,KAAA,GAChB,MAAM,KAAK;GACT,OAAO;GACP,MAAM;GACN,QAAQ,WAAW,MAAM,EAAE;GAC3B,eAAe;GACf,YAAY;EACd,CAAC;EAEH,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,UAAU,CAAC,GAC3C,MAAM,KAAK;GAAE,OAAO;GAAM,MAAM;GAAS,QAAQ,WAAW,IAAI;GAAG,YAAY;EAAK,CAAC;EAEvF,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,SAAS,aAAa,CAAC,CAAC,GAChE,MAAM,KAAK;GAAE,OAAO;GAAM,MAAM;GAAY,QAAQ,WAAW,IAAI;GAAG,YAAY;EAAK,CAAC;EAE1F,KAAK,MAAM,QAAQ,SAAS,cAAc,GAAG;GAC3C,MAAM,MAAM,SAAS,OAAO,IAAI;GAChC,MAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ,IAAI,UAAU,KAAK,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;IAClE,eAAe,IAAI;IACnB,YAAY,GAAG,KAAK;GACtB,CAAC;EACH;EACA,KAAK,MAAM,WAAW,UACpB,MAAM,KAAK;GACT,OAAO;GACP,MAAM;GACN,QAAQ;GACR,YAAY,YAAY,SAAS,YAAY,QAAQ,GAAG,QAAQ,KAAK;EACvE,CAAC;EAEH,OAAO;CACT;CAEA,SAAS,MAAM,QAAgB,WAA4C;EACzE,MAAM,SAAS,YAAY,QAAQ,SAAS;EAC5C,MAAM,WAAW,WAAW,MAAM;EAClC,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;EACnC,IAAI;EACJ,MAAM,SAAS,SAAqB;GAClC,IAAI,SAAS,KAAK,SAAS,SAAS,KAAK,KAAK;GAG9C,IAAI,KAAK,SAAS,QAAQ,WAAW,KAAK,OAAO,MAAM,SAAS,UAAU;GAC1E,OAAO;GACP,aAAa,MAAM,KAAK;EAC1B;EACA,MAAM,SAAS,IAAI;EACnB,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;EAC/B,MAAM,OAAa;EACnB,IACE,KAAK,SAAS,UACd,KAAK,cAAc,KAAA,KACnB,KAAK,YAAY,KAAA,KACjB,UAAU,KAAK,aACf,UAAU,KAAK,SACf;GACA,MAAM,MAAM,SAAS,OAAO,KAAK,IAAI;GACrC,IAAI,QAAQ,KAAA,GACV,OAAO;IACL,OAAO,KAAK;IACZ,KAAK,KAAK;IACV,QAAQ,IAAI,UAAU,KAAK,MAAM,cAAc,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;IACtE,eAAe,IAAI;GACrB;EAEJ;EACA,MAAM,OAAO,SAAS,MAAM,IAAI,IAAI;EACpC,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;EAC/B,OAAO;GACL,OAAO,KAAK;GACZ,KAAK,KAAK;GACV,QAAQ,WAAW,KAAK,SAAS,YAAY,MAAM,IAAI,IAAI,IAAI;EACjE;CACF;CAEA,SAAS,YAAY,QAAuC;EAE1D,MAAM,QAAQ,IAAI,MAAM,QAAQ,YAAY,CAAC,CAAC;EAC9C,IAAI,MAAM,OAAO,MAAM,EAAE,SAAS,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,OAAO,MAAM,GAAG,OAAO;EAC3F,OAAO,OACL,QACA,MAAM,KAAK,MAAM;GACf,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,EAAE,OAAO,CAAC,GAAG,OAAO,MAAM;GAC1D,OAAO;IAAE,GAAG;IAAG;IAAO,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE,KAAK,KAAK,GAAG,OAAO,MAAM;GAAE;EAC7E,CAAC,CACH;CACF;CAEA,OAAO,OAAO,OAAO;EAAE;EAAU;EAAO;CAAY,CAAC;AACvD;AAEA,SAAS,UAAU,KAA2B;CAC5C,OAAO,IAAI,UAAU,MAAM,MAAM,EAAE,OAAO,SAAS,KAAK,EAAE,SAAS,KAAA,CAAS;AAC9E;AAEA,SAAS,KAAK,OAA8B,OAA6B;CACvE,MAAM,SAAS,MAAM,YAAY;CACjC,MAAM,SAAS,MACZ,KAAK,SAAS;EACb,MAAM,QAAQ,KAAK,MAAM,YAAY;EACrC,IAAI,QAAQ;EACZ,IAAI,WAAW,IAAI,QAAQ;OACtB,IAAI,MAAM,WAAW,MAAM,GAAG,QAAQ;OACtC,IAAI,MAAM,SAAS,MAAM,GAAG,QAAQ;EACzC,OAAO;GAAE;GAAM;EAAM;CACvB,CAAC,CAAC,CACD,QAAQ,UAAU,MAAM,QAAQ,CAAC;CACpC,MAAM,QAAwC;EAC5C,OAAO;EACP,OAAO;EACP,UAAU;EACV,UAAU;EACV,QAAQ;EACR,UAAU;EACV,SAAS;CACX;CACA,OAAO,MACJ,GAAG,MACF,EAAE,QAAQ,EAAE,SACZ,MAAM,EAAE,KAAK,QAAQ,MAAM,EAAE,KAAK,SAClC,EAAE,KAAK,MAAM,cAAc,EAAE,KAAK,KAAK,CAC3C;CACA,MAAM,uBAAO,IAAI,IAAY;CAC7B,OAAO,OACJ,KAAK,UAAU,MAAM,IAAI,CAAC,CAC1B,QAAQ,SAAS,CAAC,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,KAAK,KAAK,GAAG,KAAK;AAC3E;;AAGA,SAAS,sBAAsB,MAAsB;CACnD,MAAM,UAAU,IAAI,IAAI,aAAa,IAAI,CAAC,CAAC,KAAK,SAAS,CAAC,KAAK,KAAK,KAAK,KAAK,CAAC,CAAC;CAChF,IAAI,MAAM,KAAK;CACf,SAAS;EACP,OAAO,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,EAAE,GAAG;EAC7C,MAAM,QAAQ,QAAQ,IAAI,GAAG;EAC7B,IAAI,UAAU,KAAA,GAAW;EACzB,MAAM;CACR;CACA,OAAO,KAAK,MAAM,GAAG,GAAG;AAC1B;;AAGA,SAAS,aAAa,MAAgD;CACpE,MAAM,QAA0C,CAAC;CACjD,IAAI,IAAI;CACR,IAAI,WAAW;CACf,OAAO,IAAI,KAAK,QAAQ;EACtB,MAAM,KAAK,KAAK;EAChB,IAAI,WAAW,KAAK,OAAO,KAAK;GAC9B;GACA;EACF,OAAO,IAAI,WAAW,GACpB,KAAK,OAAO,OAAO,IAAI;OAClB,IAAI,OAAO,KAAK;GACrB;GACA;EACF,OAAO,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GAC5C,IAAI,MAAM,KAAK,QAAQ,MAAM,CAAC;GAC9B,IAAI,QAAQ,IAAI,MAAM,KAAK;GAC3B,MAAM,KAAK;IAAE,OAAO;IAAG;GAAI,CAAC;GAC5B,IAAI;EACN,OAAO,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GAC5C,MAAM,QAAQ,KAAK,QAAQ,MAAM,IAAI,CAAC;GACtC,MAAM,MAAM,UAAU,KAAK,KAAK,SAAS,QAAQ;GACjD,MAAM,KAAK;IAAE,OAAO;IAAG;GAAI,CAAC;GAC5B,IAAI;EACN,OAAO,IAAI,OAAO,QAAO,OAAO,KAAK;GACnC,IAAI,IAAI,IAAI;GACZ,OAAO,IAAI,KAAK,UAAU,KAAK,OAAO,IAAI,KAAK,KAAK,OAAO,OAAO,IAAI;GACtE,IAAI,IAAI;EACV,OAAO;CACT;CACA,OAAO;AACT;;AAGA,MAAM,eAAe;;AAGrB,MAAM,UAAU;CAAC;CAAW;CAAU;CAAU;CAAU;AAAI;;AAE9D,MAAM,cAAc;;;;;AAKpB,MAAM,wBAAwB;;;;;;AAM9B,MAAM,uBAAuB;;AAE7B,MAAM,wBAAwB;;;;;;;AAQ9B,SAAS,cAAc,SAA2B;CAChD,MAAM,MAAM;EACV;EACA,UAAU;EACV,SAAS;EACT,KAAK;EACL,SAAS;EACT,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;CACb;CACA,KAAK,IAAI,KAAK,GAAG,MAAM,KAAK,IAAI,QAAQ,QAAQ,YAAY,GAAG,MAAM;EACnE,MAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE;EACjC,MAAM,QAAQ,QAAQ,MAAM,EAAE;EAC9B,IAAI,KAAK,GAAG,MAAM,QAAQ,SAAS,GAAG,MAAM,QAAQ,OAAO;CAC7D;CACA,OAAO,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC;AACzB;;AAGA,SAAS,SAAS,MAA0E;CAC1F,MAAM,QAAkB,CAAC;CACzB,IAAI,IAAI;CACR,OAAO,IAAI,KAAK,QAAQ;EACtB,MAAM,KAAK,KAAK;EAChB,MAAM,MAAM,MAAM,MAAM,SAAS;EACjC,IAAI,QAAQ,KAAK;GACf,IAAI,OAAO,MAAM,KAAK;QACjB,IAAI,OAAO,KAAK;IACnB,MAAM,IAAI;IACV;GACF,OAAO,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;IAC5C,MAAM,KAAK,IAAI;IACf,KAAK;GACP,OAAO;GACP;EACF;EACA,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GACrC,MAAM,MAAM,KAAK,QAAQ,MAAM,CAAC;GAChC,IAAI,QAAQ,IAAI,OAAO;IAAE,SAAS;IAAI,UAAU;IAAO,WAAW;GAAK;GACvE,IAAI;GACJ;EACF;EACA,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GACrC,MAAM,MAAM,KAAK,QAAQ,MAAM,IAAI,CAAC;GACpC,IAAI,QAAQ,IAAI,OAAO;IAAE,SAAS;IAAI,UAAU;IAAO,WAAW;GAAK;GACvE,IAAI,MAAM;GACV;EACF;EACA,IAAI,OAAO,QAAO,OAAO,KAAK;GAC5B,IAAI,IAAI,IAAI;GACZ,OAAO,IAAI,KAAK,UAAU,KAAK,OAAO,IAAI,KAAK,KAAK,OAAO,OAAO,IAAI;GACtE,IAAI,KAAK,KAAK,QAAQ,OAAO;IAAE,SAAS;IAAI,UAAU;IAAM,WAAW;GAAM;GAC7E,IAAI,IAAI;GACR;EACF;EACA,IAAI,OAAO,KAAK,MAAM,KAAK,GAAG;OACzB,IAAI,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK,MAAM,KAAK,EAAE;OACzD,IAAI,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;GAC/C,IAAI,QAAQ,QAAQ,OAAO,KAAK,MAAM,IAAI;QACrC,IAAI,QAAQ,OAAO,QAAQ,OAAO,QAAQ,KAAK,MAAM,IAAI;EAChE;EACA;CACF;CACA,IAAI,MAAM,MAAM,SAAS,OAAO,KAAK,OAAO;EAAE,SAAS;EAAI,UAAU;EAAM,WAAW;CAAM;CAK5F,OAAO;EAAE,SAJO,MACb,QAAQ,CAAC,CACT,KAAK,SAAS,OAAO,SAAS,GAAG,CAAC,CAClC,KAAK,EACO;EAAG,UAAU;EAAO,WAAW;CAAM;AACtD"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/service/index.ts"],"sourcesContent":["import { acceptsArgument, analyze, type Analysis } from '../check/checker.js'\nimport { internalsOf, type Environment, type LiteEnvironment } from '../environment.js'\nimport { BonsaiError, BonsaiSyntaxError, locate, type Diagnostic } from '../errors.js'\nimport { assertType, signatureText, type FunctionDef } from '../functions/define.js'\nimport { descriptionOf } from '../functions/descriptions.js'\nimport { forEachChild, type Node } from '../syntax/ast.js'\nimport { parse } from '../syntax/parser.js'\nimport {\n fieldOf,\n formatType,\n isExact,\n nonNull,\n t,\n typeData,\n unionMembers,\n unionOf,\n widen,\n type MapType,\n type Type,\n} from '../types.js'\n\nexport type CompletionKind =\n | 'value'\n | 'variable'\n | 'local'\n | 'property'\n | 'function'\n | 'method'\n | 'keyword'\n\nexport interface Completion {\n readonly label: string\n readonly kind: CompletionKind\n /** Type or signature text. */\n readonly detail: string\n readonly documentation?: string\n /** Text to insert (e.g. `trim()` for a method). */\n readonly insertText: string\n /**\n * The range this item replaces, when it differs from the result's range: a\n * field that is not a plain name is inserted as `[\"first-name\"]` in place of\n * the `.` before it.\n */\n readonly range?: { readonly start: number; readonly end: number }\n}\n\nexport interface CompletionResult {\n /** The range the completion replaces (the partially typed name). */\n readonly start: number\n readonly end: number\n readonly items: readonly Completion[]\n}\n\nexport interface HoverResult {\n readonly start: number\n readonly end: number\n /** Type of the expression under the cursor, or the function signature. */\n readonly detail: string\n readonly documentation?: string\n}\n\nexport interface LanguageService {\n /** Completions at a UTF-16 offset in `source`. */\n complete: (source: string, offset: number) => CompletionResult\n /** Type information for the expression at `offset`. */\n hover: (source: string, offset: number) => HoverResult | undefined\n /** Every syntax and check diagnostic, never throwing. */\n diagnostics: (source: string) => readonly Diagnostic[]\n}\n\nconst PROBE = '__bonsai_probe__'\n\nconst CLOSER: Readonly<Record<string, string>> = { '(': ')', '[': ']', '{': '}', '${': '}' }\nconst KEYWORDS = ['true', 'false', 'null', 'let', 'has', 'try', 'not in', 'in']\nconst IDENT_CHAR = /[A-Za-z0-9_$]/u\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/u\n\n/**\n * A UTF-16 offset inside `source`: past the end, or NaN, means the end. A\n * negative or fractional offset is a caller bug (RangeError), and so is a\n * source or an offset of the wrong type (TypeError).\n */\nfunction clampOffset(source: string, offset: number): number {\n if (typeof source !== 'string') throw new TypeError('An expression must be a string')\n if (typeof offset !== 'number') {\n throw new TypeError(`An offset must be a number, not ${typeof offset}`)\n }\n if (offset < 0 || (Number.isFinite(offset) && !Number.isInteger(offset))) {\n throw new RangeError(`An offset must be a whole number of at least 0, not ${offset}`)\n }\n return Number.isNaN(offset) ? source.length : Math.min(offset, source.length)\n}\n\nexport interface LanguageServiceOptions {\n /**\n * The type every expression must have, as `check(source, { expect })` takes\n * it: diagnostics then include EXPECTED_TYPE (a filter editor passes\n * `t.boolean()`).\n */\n readonly expect?: Type | undefined\n}\n\n/** Reads the service options once: only `expect`, which must be a type built with t. */\nfunction serviceExpect(options: unknown): Type | undefined {\n if (options === undefined) return undefined\n if (typeof options !== 'object' || options === null || Array.isArray(options))\n throw new TypeError('Language service options must be an object')\n for (const key of Object.keys(options)) {\n if (key !== 'expect')\n throw new TypeError(`Unknown language service option key \"${key}\" (expected one of: expect)`)\n }\n const expect = (options as { expect?: unknown }).expect\n if (expect === undefined) return undefined\n // Kept as a private copy, like compile()'s, so later changes do not reach it.\n assertType(expect, 'Language service option \"expect\"')\n const own = typeData(expect)\n if (own !== expect) assertType(own, 'Language service option \"expect\"')\n return own\n}\n\n/**\n * Editor features over an environment. Nothing here evaluates an expression or\n * calls a host function: completions and hovers come from the static checker.\n */\nexport function createLanguageService(\n env: Environment | LiteEnvironment,\n options?: LanguageServiceOptions,\n): LanguageService {\n const { checkEnv, parseLimits } = internalsOf(env)\n const expect = serviceExpect(options)\n const checkOptions = expect === undefined ? undefined : { expect }\n\n function tryAnalyze(source: string, probe?: string): Analysis | undefined {\n try {\n return analyze(parse(source, parseLimits), checkEnv, { probe, expected: expect })\n } catch (error) {\n if (error instanceof BonsaiError) return undefined\n throw error\n }\n }\n\n /**\n * How far `source` parses: Infinity when it parses whole, the offset of the\n * first token it cannot take (its length when it only ends too soon), or -1\n * past a limit. A ` ]` after it, which nothing can continue with, turns\n * \"the expression ended\" into an error past the end.\n */\n function parsesTo(source: string, budget: { left: number }): number {\n for (const text of [source, `${source} ]`]) {\n // Out of parses: no answer, which ends the search.\n if (budget.left-- <= 0) return -1\n try {\n parse(text, parseLimits)\n if (text === source) return Number.POSITIVE_INFINITY\n } catch (error) {\n if (!(error instanceof BonsaiError)) throw error\n if (!(error instanceof BonsaiSyntaxError) || error.span === undefined) return -1\n if (text !== source) return Math.min(error.span.start, source.length)\n }\n }\n return -1\n }\n\n /**\n * An ending for nested open constructs no single ending covers, such as\n * `let v = try(o1.`: at each closer, innermost first, and after the last,\n * the fillers the parser gets through (a few at most), within the parses\n * `budget` has left.\n */\n function builtEnding(\n text: string,\n closers: string,\n budget: { left: number },\n ): string | undefined {\n let built = text\n for (let gap = 0; gap <= closers.length; gap++) {\n const rest = closers.slice(gap)\n for (let round = 0; round < MAX_FILLERS; round++) {\n const reached = parsesTo(built + rest, budget)\n // A mistake before the probe is one no ending can mend.\n if (reached < text.length) return undefined\n if (reached === Number.POSITIVE_INFINITY) return built + rest\n // The parser got past this closer: what is missing is further out.\n if (reached > built.length) break\n let filler: string | undefined\n for (const f of FILLERS) {\n if (budget.left <= 0) return undefined\n if (parsesTo(built + f + rest, budget) >= built.length + f.length) {\n filler = f\n break\n }\n }\n if (filler === undefined) return undefined\n built += filler\n }\n if (gap < closers.length) built += closers[gap]\n }\n return undefined\n }\n\n function complete(source: string, offset: number): CompletionResult {\n const cursor = clampOffset(source, offset)\n let from = cursor\n while (from > 0 && IDENT_CHAR.test(source[from - 1])) from--\n let to = cursor\n while (to < source.length && IDENT_CHAR.test(source[to])) to++\n const typed = source.slice(from, cursor)\n const empty: CompletionResult = { start: from, end: to, items: [] }\n\n const prefix = source.slice(0, from)\n const scan = scanOpen(prefix)\n // Inside a string after == or != (user.plan == \"p|), in a list after `in`\n // (user.plan in [\"pro\", \"f|), or as a call argument (roles.includes(\"a|),\n // xs.sortBy(.k, \"d|)): offer the values the other side allows.\n const quoted =\n /(?:==|!=|[(,]|\\bin\\s*\\[(?:\\s*(?:\"[^\"\\\\]*\"|'[^'\\\\]*')\\s*,)*)\\s*(?<quote>[\"'])(?<typed>[^\"'\\\\]*)$/u.exec(\n source.slice(0, cursor),\n )\n if (quoted !== null && scan.inString) {\n const enumPrefix = quoted.groups?.typed ?? ''\n const quoteAt = cursor - enumPrefix.length\n const outer = source.slice(0, quoteAt - 1)\n const outerScan = scanOpen(outer)\n const analysis = tryAnalyze(`${outer}${PROBE}${outerScan.closers}`, PROBE)\n const values =\n analysis === undefined ? [] : literalValues(analysis).filter((v) => typeof v === 'string')\n const items = values.map((v) => ({\n label: v,\n kind: 'value' as const,\n detail: 'value',\n insertText: v,\n }))\n let end = cursor\n while (end < source.length && source[end] !== quoted.groups?.quote) end++\n return { start: quoteAt, end, items: rank(items, enumPrefix) }\n }\n if (scan.inString || scan.inComment) return empty\n if (from > 0 && /[0-9]/u.test(source[from - 1])) return empty\n\n // Whitespace and comments may sit between a `.` and the cursor.\n const before = withoutTrailingTrivia(prefix)\n const afterDot = before.endsWith('?.') || (before.endsWith('.') && !before.endsWith('...'))\n const probeText = afterDot ? `${before}${PROBE}` : `${prefix}${PROBE}`\n\n // Every parse below counts against one budget, so a completion costs at\n // most a fixed number of parses however the source nests, and at most a\n // few parses' worth of text when the source is long.\n const budget = {\n left: Math.min(\n MAX_COMPLETION_PARSES,\n Math.max(MIN_COMPLETION_PARSES, Math.floor(MAX_COMPLETION_CHARS / probeText.length)),\n ),\n }\n let analysis: Analysis | undefined\n for (const suffix of probeSuffixes(scan.closers)) {\n if (budget.left-- <= 0) break\n analysis = tryAnalyze(probeText + suffix, PROBE)\n if (analysis !== undefined) break\n }\n // The last parse is kept for analyzing the ending found.\n budget.left--\n if (analysis === undefined && budget.left > 0) {\n const ended = builtEnding(probeText, scan.closers, budget)\n if (ended !== undefined) analysis = tryAnalyze(ended, PROBE)\n }\n if (analysis === undefined) return empty\n\n const dotAt = afterDot && !before.endsWith('?.') ? before.length - 1 : undefined\n const items = afterDot\n ? memberCompletions(analysis, dotAt === undefined ? undefined : { start: dotAt, end: to })\n : nameCompletions(analysis)\n if (!afterDot && /(?:==|!=)\\s*$/u.test(prefix)) {\n for (const value of literalValues(analysis)) {\n items.unshift({\n label: JSON.stringify(value),\n kind: 'value',\n detail: 'value',\n insertText: JSON.stringify(value),\n })\n }\n }\n return { start: from, end: to, items: rank(items, typed) }\n }\n\n /** Literal members of the type on the other side of `== probe` or `in [..., probe]`. */\n function literalValues(analysis: Analysis): (string | number | boolean)[] {\n let other: Type | undefined\n const isProbe = (node: Node): boolean => node.type === 'Variable' && node.name === PROBE\n const find = (node: Node): void => {\n if (other !== undefined) return\n if (node.type === 'Binary' && (node.operator === '==' || node.operator === '!=')) {\n if (isProbe(node.right)) other = analysis.types.get(node.left)\n else if (isProbe(node.left)) other = analysis.types.get(node.right)\n if (other !== undefined) return\n }\n if (\n node.type === 'Binary' &&\n (node.operator === 'in' || node.operator === 'not in') &&\n node.right.type === 'List' &&\n node.right.items.some((item) => item.type !== 'Spread' && isProbe(item))\n ) {\n other = analysis.types.get(node.left)\n if (other !== undefined) return\n }\n if (node.type === 'Call') {\n const index = node.args.findIndex((arg) => arg.type !== 'Spread' && isProbe(arg))\n if (index > 0) {\n const receiver = node.args[0]\n if (node.name === 'includes' && index === 1 && receiver.type !== 'Spread') {\n // roles.includes(\"a\"): the list's element type.\n const list = analysis.types.get(receiver)\n const listType = list === undefined ? undefined : nonNull(list)\n if (listType?.kind === 'list') other = listType.element\n } else {\n // A parameter declared as literals (sortBy's \"asc\" | \"desc\").\n const plan = analysis.calls.get(node)\n const overload = plan?.def.overloads[plan.candidates[0] ?? 0]\n const param = overload?.params[index] ?? overload?.rest\n if (param !== undefined && unionMembers(param).every((m) => m.kind === 'literal'))\n other = param\n }\n if (other !== undefined) return\n }\n }\n forEachChild(node, find)\n }\n find(analysis.root)\n if (other === undefined) return []\n return unionMembers(other).flatMap((member) =>\n member.kind === 'literal' ? [member.value] : [],\n )\n }\n\n /** Completions after `receiver.`; `dot` is the range of the `.` and the typed name. */\n function memberCompletions(\n analysis: Analysis,\n dot: { start: number; end: number } | undefined,\n ): Completion[] {\n let receiver: Type | undefined\n // `.name` directly after an implicit-lambda `.` (xs.map(.name)).\n let onItem = false\n const find = (node: Node): void => {\n if (receiver !== undefined) return\n if (node.type === 'Member' && node.name === PROBE) {\n receiver = analysis.types.get(node.object)\n onItem = node.object.type === 'It'\n return\n }\n forEachChild(node, find)\n }\n find(analysis.root)\n if (receiver === undefined) return []\n const target = nonNull(receiver)\n const items: Completion[] = []\n const members = unionMembers(target)\n // Fields of every map member (a union of records offers each one's keys).\n const maps = members.filter((member): member is MapType => member.kind === 'map')\n const names = new Set(maps.flatMap((member) => Object.keys(member.fields)))\n // The type a read has, as check() and hover report it: a member without\n // the field reads its `rest` (or null), and a declared object may hold any key.\n const readOf = (member: MapType, name: string): Type => {\n const field = fieldOf(member, name)\n if (field !== undefined) return field\n if (member.rest !== undefined) return t.optional(member.rest)\n return isExact(member) ? t.null() : t.any()\n }\n for (const name of names) {\n const detail = formatType(\n analysis.probeMembers?.get(name) ?? unionOf(maps.map((member) => readOf(member, name))),\n )\n if (IDENTIFIER.test(name)) {\n items.push({ label: name, kind: 'property', detail, insertText: name })\n } else {\n // `user.first-name` would subtract: index it instead. The item's own\n // `.` stays: xs.map(.[\"first-name\"]).\n items.push({\n label: name,\n kind: 'property',\n detail,\n insertText: `${onItem ? '.' : ''}[${JSON.stringify(name)}]`,\n ...(dot === undefined ? {} : { range: dot }),\n })\n }\n }\n if (\n members.some(\n (member) =>\n member.kind === 'list' ||\n member.kind === 'string' ||\n (member.kind === 'literal' && typeof member.value === 'string'),\n )\n ) {\n items.push({ label: 'length', kind: 'property', detail: 'number', insertText: 'length' })\n }\n // Functions whose first parameter accepts the receiver can be called as\n // methods; for a union, every member must be accepted by some signature.\n const receivers = unionMembers(widen(target))\n for (const name of checkEnv.functionNames()) {\n const def = checkEnv.lookup(name) as FunctionDef\n const firstOf = (o: (typeof def.overloads)[number]): Type | undefined => {\n const first = o.params[0] ?? o.rest\n return first === undefined || first.kind === 'function' ? undefined : first\n }\n const accepts = (o: (typeof def.overloads)[number], member: Type): boolean => {\n const first = firstOf(o)\n return first !== undefined && (member.kind === 'any' || acceptsArgument(first, member))\n }\n const applicable = def.overloads.filter((o) => receivers.some((member) => accepts(o, member)))\n if (\n applicable.length === 0 ||\n !receivers.every((member) => applicable.some((o) => accepts(o, member)))\n )\n continue\n items.push({\n label: name,\n kind: 'method',\n detail: applicable.map((o) => signatureText(name, o)).join('\\n'),\n documentation: descriptionOf(def),\n insertText: `${name}(${takesMore(def) ? '' : ')'}`,\n })\n }\n return items\n }\n\n function nameCompletions(analysis: Analysis): Completion[] {\n const items: Completion[] = []\n const scope = analysis.probeScope\n if (scope?.it !== undefined) {\n items.push({\n label: '.',\n kind: 'local',\n detail: formatType(scope.it),\n documentation: 'The current item',\n insertText: '.',\n })\n }\n for (const [name, type] of scope?.locals ?? []) {\n items.push({ label: name, kind: 'local', detail: formatType(type), insertText: name })\n }\n for (const [name, type] of Object.entries(checkEnv.variables ?? {})) {\n items.push({ label: name, kind: 'variable', detail: formatType(type), insertText: name })\n }\n for (const name of checkEnv.functionNames()) {\n const def = checkEnv.lookup(name) as FunctionDef\n items.push({\n label: name,\n kind: 'function',\n detail: def.overloads.map((o) => signatureText(name, o)).join('\\n'),\n documentation: descriptionOf(def),\n insertText: `${name}(`,\n })\n }\n for (const keyword of KEYWORDS) {\n items.push({\n label: keyword,\n kind: 'keyword',\n detail: 'keyword',\n insertText: keyword === 'has' || keyword === 'try' ? `${keyword}(` : keyword,\n })\n }\n return items\n }\n\n function hover(source: string, rawOffset: number): HoverResult | undefined {\n const offset = clampOffset(source, rawOffset)\n const analysis = tryAnalyze(source)\n if (analysis === undefined) return undefined\n let best: Node | undefined\n const visit = (node: Node): void => {\n if (offset < node.start || offset > node.end) return\n // The implicit item's `.` ends where the name after it starts (`.cat`):\n // an offset on that name belongs to the member, not the item.\n if (node.type === 'It' && offset === node.end && best?.type === 'Member') return\n best = node\n forEachChild(node, visit)\n }\n visit(analysis.root)\n if (best === undefined) return undefined\n const node: Node = best\n if (\n node.type === 'Call' &&\n node.nameStart !== undefined &&\n node.nameEnd !== undefined &&\n offset >= node.nameStart &&\n offset <= node.nameEnd\n ) {\n const def = checkEnv.lookup(node.name)\n if (def !== undefined) {\n return {\n start: node.nameStart,\n end: node.nameEnd,\n detail: def.overloads.map((o) => signatureText(def.name, o)).join('\\n'),\n documentation: descriptionOf(def),\n }\n }\n }\n const type = analysis.types.get(node)\n if (type === undefined) return undefined\n return {\n start: node.start,\n end: node.end,\n detail: formatType(type.kind === 'literal' ? widen(type) : type),\n }\n }\n\n function diagnostics(source: string): readonly Diagnostic[] {\n // Spans stay inside the source (an error at the end is zero-width there).\n const found = env.check(source, checkOptions).diagnostics\n if (found.every((d) => d.start >= 0 && d.end >= d.start && d.end <= source.length)) return found\n return locate(\n source,\n found.map((d) => {\n const start = Math.min(Math.max(d.start, 0), source.length)\n return { ...d, start, end: Math.min(Math.max(d.end, start), source.length) }\n }),\n )\n }\n\n return Object.freeze({ complete, hover, diagnostics })\n}\n\nfunction takesMore(def: FunctionDef): boolean {\n return def.overloads.some((o) => o.params.length > 1 || o.rest !== undefined)\n}\n\nfunction rank(items: readonly Completion[], typed: string): Completion[] {\n const needle = typed.toLowerCase()\n const scored = items\n .map((item) => {\n const label = item.label.toLowerCase()\n let score = 0\n if (needle === '') score = 1\n else if (label.startsWith(needle)) score = 2\n else if (label.includes(needle)) score = 1\n return { item, score }\n })\n .filter((entry) => entry.score > 0)\n const order: Record<CompletionKind, number> = {\n value: -1,\n local: 0,\n property: 1,\n variable: 2,\n method: 3,\n function: 4,\n keyword: 5,\n }\n scored.sort(\n (a, b) =>\n b.score - a.score ||\n order[a.item.kind] - order[b.item.kind] ||\n a.item.label.localeCompare(b.item.label),\n )\n const seen = new Set<string>()\n return scored\n .map((entry) => entry.item)\n .filter((item) => !seen.has(item.label) && (seen.add(item.label), true))\n}\n\n/** `text` without the whitespace and comments at its end. */\nfunction withoutTrailingTrivia(text: string): string {\n const startOf = new Map(commentSpans(text).map((span) => [span.end, span.start]))\n let end = text.length\n for (;;) {\n while (end > 0 && /\\s/u.test(text[end - 1])) end--\n const start = startOf.get(end)\n if (start === undefined) break\n end = start\n }\n return text.slice(0, end)\n}\n\n/** The comments in `text` (outside strings and templates), as [start, end) spans. */\nfunction commentSpans(text: string): { start: number; end: number }[] {\n const spans: { start: number; end: number }[] = []\n let i = 0\n let template = 0\n while (i < text.length) {\n const ch = text[i]\n if (template > 0 && ch === '`') {\n template--\n i++\n } else if (template > 0) {\n i += ch === '\\\\' ? 2 : 1\n } else if (ch === '`') {\n template++\n i++\n } else if (ch === '/' && text[i + 1] === '/') {\n let end = text.indexOf('\\n', i)\n if (end === -1) end = text.length\n spans.push({ start: i, end })\n i = end\n } else if (ch === '/' && text[i + 1] === '*') {\n const close = text.indexOf('*/', i + 2)\n const end = close === -1 ? text.length : close + 2\n spans.push({ start: i, end })\n i = end\n } else if (ch === '\"' || ch === \"'\") {\n let j = i + 1\n while (j < text.length && text[j] !== ch) j += text[j] === '\\\\' ? 2 : 1\n i = j + 1\n } else i++\n }\n return spans\n}\n\n/** Open constructs a filler is tried inside of, innermost first. */\nconst PROBE_SPLITS = 4\n\n/** What a nested ending may add at one closer: a missing branch, argument, value, or body. */\nconst FILLERS = [' : null', ', null', ': null', '; null', ' 0']\n/** Fillers one closer may take, as in try(b ? x : null, null). */\nconst MAX_FILLERS = 3\n/**\n * Parses one completion may cost in all (fixed endings, nested ending search,\n * and its analysis): enough for every ending a short source needs.\n */\nconst MAX_COMPLETION_PARSES = 100\n/**\n * Characters one completion may parse in all, so a long source gets fewer\n * tries (each costs a parse of the whole source), but never fewer than\n * {@link MIN_COMPLETION_PARSES}.\n */\nconst MAX_COMPLETION_CHARS = 1_200_000\n/** Enough for the fixed endings a single open construct needs, plus the analysis. */\nconst MIN_COMPLETION_PARSES = 12\n\n/**\n * Endings that make a prefix ending in the probe parse, cheapest first: the\n * closers alone, a missing branch or value inside them, the missing half of\n * an enclosing try(x, fallback) or computed key [k]: v, and a `let` body or\n * `?:` branch after them.\n */\nfunction probeSuffixes(closers: string): string[] {\n const out = [\n closers,\n ` : null${closers}`,\n `; null${closers}`,\n ` 0${closers}`,\n `, null${closers}`,\n `${closers}; null`,\n `${closers} : null`,\n `${closers} : null; null`,\n ]\n for (let at = 1; at <= Math.min(closers.length, PROBE_SPLITS); at++) {\n const inner = closers.slice(0, at)\n const outer = closers.slice(at)\n out.push(`${inner}: null${outer}`, `${inner}, null${outer}`)\n }\n return [...new Set(out)]\n}\n\n/** Tracks open brackets, strings, and templates in a prefix to synthesize closers. */\nfunction scanOpen(text: string): { closers: string; inString: boolean; inComment: boolean } {\n const stack: string[] = []\n let i = 0\n while (i < text.length) {\n const ch = text[i]\n const top = stack[stack.length - 1]\n if (top === '`') {\n if (ch === '\\\\') i += 2\n else if (ch === '`') {\n stack.pop()\n i++\n } else if (ch === '$' && text[i + 1] === '{') {\n stack.push('${')\n i += 2\n } else i++\n continue\n }\n if (ch === '/' && text[i + 1] === '/') {\n const end = text.indexOf('\\n', i)\n if (end === -1) return { closers: '', inString: false, inComment: true }\n i = end\n continue\n }\n if (ch === '/' && text[i + 1] === '*') {\n const end = text.indexOf('*/', i + 2)\n if (end === -1) return { closers: '', inString: false, inComment: true }\n i = end + 2\n continue\n }\n if (ch === '\"' || ch === \"'\") {\n let j = i + 1\n while (j < text.length && text[j] !== ch) j += text[j] === '\\\\' ? 2 : 1\n if (j >= text.length) return { closers: '', inString: true, inComment: false }\n i = j + 1\n continue\n }\n if (ch === '`') stack.push('`')\n else if (ch === '(' || ch === '[' || ch === '{') stack.push(ch)\n else if (ch === ')' || ch === ']' || ch === '}') {\n if (top === '${' && ch === '}') stack.pop()\n else if (top === '(' || top === '[' || top === '{') stack.pop()\n }\n i++\n }\n if (stack[stack.length - 1] === '`') return { closers: '', inString: true, inComment: false }\n const closers = stack\n .reverse()\n .map((open) => CLOSER[open] ?? '`')\n .join('')\n return { closers, inString: false, inComment: false }\n}\n"],"mappings":";;;;;;;;;AAsEA,MAAM,QAAQ;AAEd,MAAM,SAA2C;CAAE,KAAK;CAAK,KAAK;CAAK,KAAK;CAAK,MAAM;AAAI;AAC3F,MAAM,WAAW;CAAC;CAAQ;CAAS;CAAQ;CAAO;CAAO;CAAO;CAAU;AAAI;AAC9E,MAAM,aAAa;AACnB,MAAM,aAAa;;;;;;AAOnB,SAAS,YAAY,QAAgB,QAAwB;CAC3D,IAAI,OAAO,WAAW,UAAU,MAAM,IAAI,UAAU,gCAAgC;CACpF,IAAI,OAAO,WAAW,UACpB,MAAM,IAAI,UAAU,mCAAmC,OAAO,QAAQ;CAExE,IAAI,SAAS,KAAM,OAAO,SAAS,MAAM,KAAK,CAAC,OAAO,UAAU,MAAM,GACpE,MAAM,IAAI,WAAW,uDAAuD,QAAQ;CAEtF,OAAO,OAAO,MAAM,MAAM,IAAI,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,MAAM;AAC9E;;AAYA,SAAS,cAAc,SAAoC;CACzD,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;CAClC,IAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,MAAM,QAAQ,OAAO,GAC1E,MAAM,IAAI,UAAU,4CAA4C;CAClE,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,QAAQ,UACV,MAAM,IAAI,UAAU,wCAAwC,IAAI,4BAA4B;CAEhG,MAAM,SAAU,QAAiC;CACjD,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;CAEjC,WAAW,QAAQ,oCAAkC;CACrD,MAAM,MAAM,SAAS,MAAM;CAC3B,IAAI,QAAQ,QAAQ,WAAW,KAAK,oCAAkC;CACtE,OAAO;AACT;;;;;AAMA,SAAgB,sBACd,KACA,SACiB;CACjB,MAAM,EAAE,UAAU,gBAAgB,YAAY,GAAG;CACjD,MAAM,SAAS,cAAc,OAAO;CACpC,MAAM,eAAe,WAAW,KAAA,IAAY,KAAA,IAAY,EAAE,OAAO;CAEjE,SAAS,WAAW,QAAgB,OAAsC;EACxE,IAAI;GACF,OAAO,QAAQ,MAAM,QAAQ,WAAW,GAAG,UAAU;IAAE;IAAO,UAAU;GAAO,CAAC;EAClF,SAAS,OAAO;GACd,IAAI,iBAAiB,aAAa,OAAO,KAAA;GACzC,MAAM;EACR;CACF;;;;;;;CAQA,SAAS,SAAS,QAAgB,QAAkC;EAClE,KAAK,MAAM,QAAQ,CAAC,QAAQ,GAAG,OAAO,GAAG,GAAG;GAE1C,IAAI,OAAO,UAAU,GAAG,OAAO;GAC/B,IAAI;IACF,MAAM,MAAM,WAAW;IACvB,IAAI,SAAS,QAAQ,OAAO,OAAO;GACrC,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,cAAc,MAAM;IAC3C,IAAI,EAAE,iBAAiB,sBAAsB,MAAM,SAAS,KAAA,GAAW,OAAO;IAC9E,IAAI,SAAS,QAAQ,OAAO,KAAK,IAAI,MAAM,KAAK,OAAO,OAAO,MAAM;GACtE;EACF;EACA,OAAO;CACT;;;;;;;CAQA,SAAS,YACP,MACA,SACA,QACoB;EACpB,IAAI,QAAQ;EACZ,KAAK,IAAI,MAAM,GAAG,OAAO,QAAQ,QAAQ,OAAO;GAC9C,MAAM,OAAO,QAAQ,MAAM,GAAG;GAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,aAAa,SAAS;IAChD,MAAM,UAAU,SAAS,QAAQ,MAAM,MAAM;IAE7C,IAAI,UAAU,KAAK,QAAQ,OAAO,KAAA;IAClC,IAAI,YAAY,OAAO,mBAAmB,OAAO,QAAQ;IAEzD,IAAI,UAAU,MAAM,QAAQ;IAC5B,IAAI;IACJ,KAAK,MAAM,KAAK,SAAS;KACvB,IAAI,OAAO,QAAQ,GAAG,OAAO,KAAA;KAC7B,IAAI,SAAS,QAAQ,IAAI,MAAM,MAAM,KAAK,MAAM,SAAS,EAAE,QAAQ;MACjE,SAAS;MACT;KACF;IACF;IACA,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;IACjC,SAAS;GACX;GACA,IAAI,MAAM,QAAQ,QAAQ,SAAS,QAAQ;EAC7C;CAEF;CAEA,SAAS,SAAS,QAAgB,QAAkC;EAClE,MAAM,SAAS,YAAY,QAAQ,MAAM;EACzC,IAAI,OAAO;EACX,OAAO,OAAO,KAAK,WAAW,KAAK,OAAO,OAAO,EAAE,GAAG;EACtD,IAAI,KAAK;EACT,OAAO,KAAK,OAAO,UAAU,WAAW,KAAK,OAAO,GAAG,GAAG;EAC1D,MAAM,QAAQ,OAAO,MAAM,MAAM,MAAM;EACvC,MAAM,QAA0B;GAAE,OAAO;GAAM,KAAK;GAAI,OAAO,CAAC;EAAE;EAElE,MAAM,SAAS,OAAO,MAAM,GAAG,IAAI;EACnC,MAAM,OAAO,SAAS,MAAM;EAI5B,MAAM,SACJ,mGAAmG,KACjG,OAAO,MAAM,GAAG,MAAM,CACxB;EACF,IAAI,WAAW,QAAQ,KAAK,UAAU;GACpC,MAAM,aAAa,OAAO,QAAQ,SAAS;GAC3C,MAAM,UAAU,SAAS,WAAW;GACpC,MAAM,QAAQ,OAAO,MAAM,GAAG,UAAU,CAAC;GACzC,MAAM,YAAY,SAAS,KAAK;GAChC,MAAM,WAAW,WAAW,GAAG,QAAQ,QAAQ,UAAU,WAAW,KAAK;GAGzE,MAAM,SADJ,aAAa,KAAA,IAAY,CAAC,IAAI,cAAc,QAAQ,CAAC,CAAC,QAAQ,MAAM,OAAO,MAAM,QAAQ,EAAA,CACtE,KAAK,OAAO;IAC/B,OAAO;IACP,MAAM;IACN,QAAQ;IACR,YAAY;GACd,EAAE;GACF,IAAI,MAAM;GACV,OAAO,MAAM,OAAO,UAAU,OAAO,SAAS,OAAO,QAAQ,OAAO;GACpE,OAAO;IAAE,OAAO;IAAS;IAAK,OAAO,KAAK,OAAO,UAAU;GAAE;EAC/D;EACA,IAAI,KAAK,YAAY,KAAK,WAAW,OAAO;EAC5C,IAAI,OAAO,KAAK,SAAS,KAAK,OAAO,OAAO,EAAE,GAAG,OAAO;EAGxD,MAAM,SAAS,sBAAsB,MAAM;EAC3C,MAAM,WAAW,OAAO,SAAS,IAAI,KAAM,OAAO,SAAS,GAAG,KAAK,CAAC,OAAO,SAAS,KAAK;EACzF,MAAM,YAAY,WAAW,GAAG,SAAS,UAAU,GAAG,SAAS;EAK/D,MAAM,SAAS,EACb,MAAM,KAAK,IACT,uBACA,KAAK,IAAI,uBAAuB,KAAK,MAAM,uBAAuB,UAAU,MAAM,CAAC,CACrF,EACF;EACA,IAAI;EACJ,KAAK,MAAM,UAAU,cAAc,KAAK,OAAO,GAAG;GAChD,IAAI,OAAO,UAAU,GAAG;GACxB,WAAW,WAAW,YAAY,QAAQ,KAAK;GAC/C,IAAI,aAAa,KAAA,GAAW;EAC9B;EAEA,OAAO;EACP,IAAI,aAAa,KAAA,KAAa,OAAO,OAAO,GAAG;GAC7C,MAAM,QAAQ,YAAY,WAAW,KAAK,SAAS,MAAM;GACzD,IAAI,UAAU,KAAA,GAAW,WAAW,WAAW,OAAO,KAAK;EAC7D;EACA,IAAI,aAAa,KAAA,GAAW,OAAO;EAEnC,MAAM,QAAQ,YAAY,CAAC,OAAO,SAAS,IAAI,IAAI,OAAO,SAAS,IAAI,KAAA;EACvE,MAAM,QAAQ,WACV,kBAAkB,UAAU,UAAU,KAAA,IAAY,KAAA,IAAY;GAAE,OAAO;GAAO,KAAK;EAAG,CAAC,IACvF,gBAAgB,QAAQ;EAC5B,IAAI,CAAC,YAAY,iBAAiB,KAAK,MAAM,GAC3C,KAAK,MAAM,SAAS,cAAc,QAAQ,GACxC,MAAM,QAAQ;GACZ,OAAO,KAAK,UAAU,KAAK;GAC3B,MAAM;GACN,QAAQ;GACR,YAAY,KAAK,UAAU,KAAK;EAClC,CAAC;EAGL,OAAO;GAAE,OAAO;GAAM,KAAK;GAAI,OAAO,KAAK,OAAO,KAAK;EAAE;CAC3D;;CAGA,SAAS,cAAc,UAAmD;EACxE,IAAI;EACJ,MAAM,WAAW,SAAwB,KAAK,SAAS,cAAc,KAAK,SAAS;EACnF,MAAM,QAAQ,SAAqB;GACjC,IAAI,UAAU,KAAA,GAAW;GACzB,IAAI,KAAK,SAAS,aAAa,KAAK,aAAa,QAAQ,KAAK,aAAa,OAAO;IAChF,IAAI,QAAQ,KAAK,KAAK,GAAG,QAAQ,SAAS,MAAM,IAAI,KAAK,IAAI;SACxD,IAAI,QAAQ,KAAK,IAAI,GAAG,QAAQ,SAAS,MAAM,IAAI,KAAK,KAAK;IAClE,IAAI,UAAU,KAAA,GAAW;GAC3B;GACA,IACE,KAAK,SAAS,aACb,KAAK,aAAa,QAAQ,KAAK,aAAa,aAC7C,KAAK,MAAM,SAAS,UACpB,KAAK,MAAM,MAAM,MAAM,SAAS,KAAK,SAAS,YAAY,QAAQ,IAAI,CAAC,GACvE;IACA,QAAQ,SAAS,MAAM,IAAI,KAAK,IAAI;IACpC,IAAI,UAAU,KAAA,GAAW;GAC3B;GACA,IAAI,KAAK,SAAS,QAAQ;IACxB,MAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ,IAAI,SAAS,YAAY,QAAQ,GAAG,CAAC;IAChF,IAAI,QAAQ,GAAG;KACb,MAAM,WAAW,KAAK,KAAK;KAC3B,IAAI,KAAK,SAAS,cAAc,UAAU,KAAK,SAAS,SAAS,UAAU;MAEzE,MAAM,OAAO,SAAS,MAAM,IAAI,QAAQ;MACxC,MAAM,WAAW,SAAS,KAAA,IAAY,KAAA,IAAY,QAAQ,IAAI;MAC9D,IAAI,UAAU,SAAS,QAAQ,QAAQ,SAAS;KAClD,OAAO;MAEL,MAAM,OAAO,SAAS,MAAM,IAAI,IAAI;MACpC,MAAM,WAAW,MAAM,IAAI,UAAU,KAAK,WAAW,MAAM;MAC3D,MAAM,QAAQ,UAAU,OAAO,UAAU,UAAU;MACnD,IAAI,UAAU,KAAA,KAAa,aAAa,KAAK,CAAC,CAAC,OAAO,MAAM,EAAE,SAAS,SAAS,GAC9E,QAAQ;KACZ;KACA,IAAI,UAAU,KAAA,GAAW;IAC3B;GACF;GACA,aAAa,MAAM,IAAI;EACzB;EACA,KAAK,SAAS,IAAI;EAClB,IAAI,UAAU,KAAA,GAAW,OAAO,CAAC;EACjC,OAAO,aAAa,KAAK,CAAC,CAAC,SAAS,WAClC,OAAO,SAAS,YAAY,CAAC,OAAO,KAAK,IAAI,CAAC,CAChD;CACF;;CAGA,SAAS,kBACP,UACA,KACc;EACd,IAAI;EAEJ,IAAI,SAAS;EACb,MAAM,QAAQ,SAAqB;GACjC,IAAI,aAAa,KAAA,GAAW;GAC5B,IAAI,KAAK,SAAS,YAAY,KAAK,SAAS,OAAO;IACjD,WAAW,SAAS,MAAM,IAAI,KAAK,MAAM;IACzC,SAAS,KAAK,OAAO,SAAS;IAC9B;GACF;GACA,aAAa,MAAM,IAAI;EACzB;EACA,KAAK,SAAS,IAAI;EAClB,IAAI,aAAa,KAAA,GAAW,OAAO,CAAC;EACpC,MAAM,SAAS,QAAQ,QAAQ;EAC/B,MAAM,QAAsB,CAAC;EAC7B,MAAM,UAAU,aAAa,MAAM;EAEnC,MAAM,OAAO,QAAQ,QAAQ,WAA8B,OAAO,SAAS,KAAK;EAChF,MAAM,QAAQ,IAAI,IAAI,KAAK,SAAS,WAAW,OAAO,KAAK,OAAO,MAAM,CAAC,CAAC;EAG1E,MAAM,UAAU,QAAiB,SAAuB;GACtD,MAAM,QAAQ,QAAQ,QAAQ,IAAI;GAClC,IAAI,UAAU,KAAA,GAAW,OAAO;GAChC,IAAI,OAAO,SAAS,KAAA,GAAW,OAAO,EAAE,SAAS,OAAO,IAAI;GAC5D,OAAO,QAAQ,MAAM,IAAI,EAAE,KAAK,IAAI,EAAE,IAAI;EAC5C;EACA,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,SAAS,WACb,SAAS,cAAc,IAAI,IAAI,KAAK,QAAQ,KAAK,KAAK,WAAW,OAAO,QAAQ,IAAI,CAAC,CAAC,CACxF;GACA,IAAI,WAAW,KAAK,IAAI,GACtB,MAAM,KAAK;IAAE,OAAO;IAAM,MAAM;IAAY;IAAQ,YAAY;GAAK,CAAC;QAItE,MAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN;IACA,YAAY,GAAG,SAAS,MAAM,GAAG,GAAG,KAAK,UAAU,IAAI,EAAE;IACzD,GAAI,QAAQ,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,IAAI;GAC5C,CAAC;EAEL;EACA,IACE,QAAQ,MACL,WACC,OAAO,SAAS,UAChB,OAAO,SAAS,YACf,OAAO,SAAS,aAAa,OAAO,OAAO,UAAU,QAC1D,GAEA,MAAM,KAAK;GAAE,OAAO;GAAU,MAAM;GAAY,QAAQ;GAAU,YAAY;EAAS,CAAC;EAI1F,MAAM,YAAY,aAAa,MAAM,MAAM,CAAC;EAC5C,KAAK,MAAM,QAAQ,SAAS,cAAc,GAAG;GAC3C,MAAM,MAAM,SAAS,OAAO,IAAI;GAChC,MAAM,WAAW,MAAwD;IACvE,MAAM,QAAQ,EAAE,OAAO,MAAM,EAAE;IAC/B,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,aAAa,KAAA,IAAY;GACxE;GACA,MAAM,WAAW,GAAmC,WAA0B;IAC5E,MAAM,QAAQ,QAAQ,CAAC;IACvB,OAAO,UAAU,KAAA,MAAc,OAAO,SAAS,SAAS,gBAAgB,OAAO,MAAM;GACvF;GACA,MAAM,aAAa,IAAI,UAAU,QAAQ,MAAM,UAAU,MAAM,WAAW,QAAQ,GAAG,MAAM,CAAC,CAAC;GAC7F,IACE,WAAW,WAAW,KACtB,CAAC,UAAU,OAAO,WAAW,WAAW,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,GAEvE;GACF,MAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ,WAAW,KAAK,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;IAC/D,eAAe,cAAc,GAAG;IAChC,YAAY,GAAG,KAAK,GAAG,UAAU,GAAG,IAAI,KAAK;GAC/C,CAAC;EACH;EACA,OAAO;CACT;CAEA,SAAS,gBAAgB,UAAkC;EACzD,MAAM,QAAsB,CAAC;EAC7B,MAAM,QAAQ,SAAS;EACvB,IAAI,OAAO,OAAO,KAAA,GAChB,MAAM,KAAK;GACT,OAAO;GACP,MAAM;GACN,QAAQ,WAAW,MAAM,EAAE;GAC3B,eAAe;GACf,YAAY;EACd,CAAC;EAEH,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,UAAU,CAAC,GAC3C,MAAM,KAAK;GAAE,OAAO;GAAM,MAAM;GAAS,QAAQ,WAAW,IAAI;GAAG,YAAY;EAAK,CAAC;EAEvF,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,SAAS,aAAa,CAAC,CAAC,GAChE,MAAM,KAAK;GAAE,OAAO;GAAM,MAAM;GAAY,QAAQ,WAAW,IAAI;GAAG,YAAY;EAAK,CAAC;EAE1F,KAAK,MAAM,QAAQ,SAAS,cAAc,GAAG;GAC3C,MAAM,MAAM,SAAS,OAAO,IAAI;GAChC,MAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ,IAAI,UAAU,KAAK,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;IAClE,eAAe,cAAc,GAAG;IAChC,YAAY,GAAG,KAAK;GACtB,CAAC;EACH;EACA,KAAK,MAAM,WAAW,UACpB,MAAM,KAAK;GACT,OAAO;GACP,MAAM;GACN,QAAQ;GACR,YAAY,YAAY,SAAS,YAAY,QAAQ,GAAG,QAAQ,KAAK;EACvE,CAAC;EAEH,OAAO;CACT;CAEA,SAAS,MAAM,QAAgB,WAA4C;EACzE,MAAM,SAAS,YAAY,QAAQ,SAAS;EAC5C,MAAM,WAAW,WAAW,MAAM;EAClC,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;EACnC,IAAI;EACJ,MAAM,SAAS,SAAqB;GAClC,IAAI,SAAS,KAAK,SAAS,SAAS,KAAK,KAAK;GAG9C,IAAI,KAAK,SAAS,QAAQ,WAAW,KAAK,OAAO,MAAM,SAAS,UAAU;GAC1E,OAAO;GACP,aAAa,MAAM,KAAK;EAC1B;EACA,MAAM,SAAS,IAAI;EACnB,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;EAC/B,MAAM,OAAa;EACnB,IACE,KAAK,SAAS,UACd,KAAK,cAAc,KAAA,KACnB,KAAK,YAAY,KAAA,KACjB,UAAU,KAAK,aACf,UAAU,KAAK,SACf;GACA,MAAM,MAAM,SAAS,OAAO,KAAK,IAAI;GACrC,IAAI,QAAQ,KAAA,GACV,OAAO;IACL,OAAO,KAAK;IACZ,KAAK,KAAK;IACV,QAAQ,IAAI,UAAU,KAAK,MAAM,cAAc,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;IACtE,eAAe,cAAc,GAAG;GAClC;EAEJ;EACA,MAAM,OAAO,SAAS,MAAM,IAAI,IAAI;EACpC,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;EAC/B,OAAO;GACL,OAAO,KAAK;GACZ,KAAK,KAAK;GACV,QAAQ,WAAW,KAAK,SAAS,YAAY,MAAM,IAAI,IAAI,IAAI;EACjE;CACF;CAEA,SAAS,YAAY,QAAuC;EAE1D,MAAM,QAAQ,IAAI,MAAM,QAAQ,YAAY,CAAC,CAAC;EAC9C,IAAI,MAAM,OAAO,MAAM,EAAE,SAAS,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,OAAO,MAAM,GAAG,OAAO;EAC3F,OAAO,OACL,QACA,MAAM,KAAK,MAAM;GACf,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,EAAE,OAAO,CAAC,GAAG,OAAO,MAAM;GAC1D,OAAO;IAAE,GAAG;IAAG;IAAO,KAAK,KAAK,IAAI,KAAK,IAAI,EAAE,KAAK,KAAK,GAAG,OAAO,MAAM;GAAE;EAC7E,CAAC,CACH;CACF;CAEA,OAAO,OAAO,OAAO;EAAE;EAAU;EAAO;CAAY,CAAC;AACvD;AAEA,SAAS,UAAU,KAA2B;CAC5C,OAAO,IAAI,UAAU,MAAM,MAAM,EAAE,OAAO,SAAS,KAAK,EAAE,SAAS,KAAA,CAAS;AAC9E;AAEA,SAAS,KAAK,OAA8B,OAA6B;CACvE,MAAM,SAAS,MAAM,YAAY;CACjC,MAAM,SAAS,MACZ,KAAK,SAAS;EACb,MAAM,QAAQ,KAAK,MAAM,YAAY;EACrC,IAAI,QAAQ;EACZ,IAAI,WAAW,IAAI,QAAQ;OACtB,IAAI,MAAM,WAAW,MAAM,GAAG,QAAQ;OACtC,IAAI,MAAM,SAAS,MAAM,GAAG,QAAQ;EACzC,OAAO;GAAE;GAAM;EAAM;CACvB,CAAC,CAAC,CACD,QAAQ,UAAU,MAAM,QAAQ,CAAC;CACpC,MAAM,QAAwC;EAC5C,OAAO;EACP,OAAO;EACP,UAAU;EACV,UAAU;EACV,QAAQ;EACR,UAAU;EACV,SAAS;CACX;CACA,OAAO,MACJ,GAAG,MACF,EAAE,QAAQ,EAAE,SACZ,MAAM,EAAE,KAAK,QAAQ,MAAM,EAAE,KAAK,SAClC,EAAE,KAAK,MAAM,cAAc,EAAE,KAAK,KAAK,CAC3C;CACA,MAAM,uBAAO,IAAI,IAAY;CAC7B,OAAO,OACJ,KAAK,UAAU,MAAM,IAAI,CAAC,CAC1B,QAAQ,SAAS,CAAC,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,IAAI,KAAK,KAAK,GAAG,KAAK;AAC3E;;AAGA,SAAS,sBAAsB,MAAsB;CACnD,MAAM,UAAU,IAAI,IAAI,aAAa,IAAI,CAAC,CAAC,KAAK,SAAS,CAAC,KAAK,KAAK,KAAK,KAAK,CAAC,CAAC;CAChF,IAAI,MAAM,KAAK;CACf,SAAS;EACP,OAAO,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,EAAE,GAAG;EAC7C,MAAM,QAAQ,QAAQ,IAAI,GAAG;EAC7B,IAAI,UAAU,KAAA,GAAW;EACzB,MAAM;CACR;CACA,OAAO,KAAK,MAAM,GAAG,GAAG;AAC1B;;AAGA,SAAS,aAAa,MAAgD;CACpE,MAAM,QAA0C,CAAC;CACjD,IAAI,IAAI;CACR,IAAI,WAAW;CACf,OAAO,IAAI,KAAK,QAAQ;EACtB,MAAM,KAAK,KAAK;EAChB,IAAI,WAAW,KAAK,OAAO,KAAK;GAC9B;GACA;EACF,OAAO,IAAI,WAAW,GACpB,KAAK,OAAO,OAAO,IAAI;OAClB,IAAI,OAAO,KAAK;GACrB;GACA;EACF,OAAO,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GAC5C,IAAI,MAAM,KAAK,QAAQ,MAAM,CAAC;GAC9B,IAAI,QAAQ,IAAI,MAAM,KAAK;GAC3B,MAAM,KAAK;IAAE,OAAO;IAAG;GAAI,CAAC;GAC5B,IAAI;EACN,OAAO,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GAC5C,MAAM,QAAQ,KAAK,QAAQ,MAAM,IAAI,CAAC;GACtC,MAAM,MAAM,UAAU,KAAK,KAAK,SAAS,QAAQ;GACjD,MAAM,KAAK;IAAE,OAAO;IAAG;GAAI,CAAC;GAC5B,IAAI;EACN,OAAO,IAAI,OAAO,QAAO,OAAO,KAAK;GACnC,IAAI,IAAI,IAAI;GACZ,OAAO,IAAI,KAAK,UAAU,KAAK,OAAO,IAAI,KAAK,KAAK,OAAO,OAAO,IAAI;GACtE,IAAI,IAAI;EACV,OAAO;CACT;CACA,OAAO;AACT;;AAGA,MAAM,eAAe;;AAGrB,MAAM,UAAU;CAAC;CAAW;CAAU;CAAU;CAAU;AAAI;;AAE9D,MAAM,cAAc;;;;;AAKpB,MAAM,wBAAwB;;;;;;AAM9B,MAAM,uBAAuB;;AAE7B,MAAM,wBAAwB;;;;;;;AAQ9B,SAAS,cAAc,SAA2B;CAChD,MAAM,MAAM;EACV;EACA,UAAU;EACV,SAAS;EACT,KAAK;EACL,SAAS;EACT,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;CACb;CACA,KAAK,IAAI,KAAK,GAAG,MAAM,KAAK,IAAI,QAAQ,QAAQ,YAAY,GAAG,MAAM;EACnE,MAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE;EACjC,MAAM,QAAQ,QAAQ,MAAM,EAAE;EAC9B,IAAI,KAAK,GAAG,MAAM,QAAQ,SAAS,GAAG,MAAM,QAAQ,OAAO;CAC7D;CACA,OAAO,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC;AACzB;;AAGA,SAAS,SAAS,MAA0E;CAC1F,MAAM,QAAkB,CAAC;CACzB,IAAI,IAAI;CACR,OAAO,IAAI,KAAK,QAAQ;EACtB,MAAM,KAAK,KAAK;EAChB,MAAM,MAAM,MAAM,MAAM,SAAS;EACjC,IAAI,QAAQ,KAAK;GACf,IAAI,OAAO,MAAM,KAAK;QACjB,IAAI,OAAO,KAAK;IACnB,MAAM,IAAI;IACV;GACF,OAAO,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;IAC5C,MAAM,KAAK,IAAI;IACf,KAAK;GACP,OAAO;GACP;EACF;EACA,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GACrC,MAAM,MAAM,KAAK,QAAQ,MAAM,CAAC;GAChC,IAAI,QAAQ,IAAI,OAAO;IAAE,SAAS;IAAI,UAAU;IAAO,WAAW;GAAK;GACvE,IAAI;GACJ;EACF;EACA,IAAI,OAAO,OAAO,KAAK,IAAI,OAAO,KAAK;GACrC,MAAM,MAAM,KAAK,QAAQ,MAAM,IAAI,CAAC;GACpC,IAAI,QAAQ,IAAI,OAAO;IAAE,SAAS;IAAI,UAAU;IAAO,WAAW;GAAK;GACvE,IAAI,MAAM;GACV;EACF;EACA,IAAI,OAAO,QAAO,OAAO,KAAK;GAC5B,IAAI,IAAI,IAAI;GACZ,OAAO,IAAI,KAAK,UAAU,KAAK,OAAO,IAAI,KAAK,KAAK,OAAO,OAAO,IAAI;GACtE,IAAI,KAAK,KAAK,QAAQ,OAAO;IAAE,SAAS;IAAI,UAAU;IAAM,WAAW;GAAM;GAC7E,IAAI,IAAI;GACR;EACF;EACA,IAAI,OAAO,KAAK,MAAM,KAAK,GAAG;OACzB,IAAI,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK,MAAM,KAAK,EAAE;OACzD,IAAI,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;GAC/C,IAAI,QAAQ,QAAQ,OAAO,KAAK,MAAM,IAAI;QACrC,IAAI,QAAQ,OAAO,QAAQ,OAAO,QAAQ,KAAK,MAAM,IAAI;EAChE;EACA;CACF;CACA,IAAI,MAAM,MAAM,SAAS,OAAO,KAAK,OAAO;EAAE,SAAS;EAAI,UAAU;EAAM,WAAW;CAAM;CAK5F,OAAO;EAAE,SAJO,MACb,QAAQ,CAAC,CACT,KAAK,SAAS,OAAO,SAAS,GAAG,CAAC,CAClC,KAAK,EACO;EAAG,UAAU;EAAO,WAAW;CAAM;AACtD"}
@@ -1,7 +1,7 @@
1
1
  import { BonsaiLimitError, BonsaiSyntaxError } from "../errors.mjs";
2
2
  import { BLOCKED_NAMES, tokenize } from "./lexer.mjs";
3
- import { guardDepth } from "../runtime/overflow.mjs";
4
3
  import { forEachChild } from "./ast.mjs";
4
+ import { guardDepth } from "../runtime/overflow.mjs";
5
5
  //#region src/syntax/parser.ts
6
6
  const DEFAULT_PARSE_LIMITS = Object.freeze({
7
7
  maxSourceLength: 1e5,
@@ -1,6 +1,6 @@
1
1
  import { BLOCKED_NAMES, IDENTIFIER, isName } from "./lexer.mjs";
2
- import { isStackOverflow } from "../runtime/overflow.mjs";
3
2
  import { forEachChild } from "./ast.mjs";
3
+ import { isStackOverflow } from "../runtime/overflow.mjs";
4
4
  //#region src/syntax/printer.ts
5
5
  const LET = 0;
6
6
  const CONDITIONAL = 1;
package/dist/types.mjs CHANGED
@@ -7,6 +7,20 @@ const NUMBER = Object.freeze({ kind: "number" });
7
7
  const STRING = Object.freeze({ kind: "string" });
8
8
  const TIMESTAMP = Object.freeze({ kind: "timestamp" });
9
9
  const DURATION = Object.freeze({ kind: "duration" });
10
+ const BUILT = new WeakSet([
11
+ ANY,
12
+ NEVER,
13
+ NULL,
14
+ BOOLEAN,
15
+ NUMBER,
16
+ STRING,
17
+ TIMESTAMP,
18
+ DURATION
19
+ ]);
20
+ function built(type, parts = []) {
21
+ if (parts.every((part) => BUILT.has(part))) BUILT.add(type);
22
+ return type;
23
+ }
10
24
  /**
11
25
  * Union members with nested unions flattened (and repeats of one object
12
26
  * dropped), so `t.optional(t.enum('a', 'b'))` lists "a", "b", and null.
@@ -40,46 +54,55 @@ const t = Object.freeze({
40
54
  string: () => STRING,
41
55
  timestamp: () => TIMESTAMP,
42
56
  duration: () => DURATION,
43
- literal: (value) => Object.freeze({
57
+ literal: (value) => built(Object.freeze({
44
58
  kind: "literal",
45
59
  value: literalValue(value)
46
- }),
60
+ })),
47
61
  /** A union of literals: `t.enum('free', 'pro')`. */
48
- enum: (...values) => Object.freeze({
62
+ enum: (...values) => built(Object.freeze({
49
63
  kind: "union",
50
64
  types: Object.freeze(values.map((value) => Object.freeze({
51
65
  kind: "literal",
52
66
  value: literalValue(value)
53
67
  })))
54
- }),
55
- list: (element) => Object.freeze({
68
+ })),
69
+ list: (element) => built(Object.freeze({
56
70
  kind: "list",
57
71
  element
58
- }),
72
+ }), [element]),
59
73
  /** A closed record with known fields. */
60
- object: (fields) => Object.freeze({
61
- kind: "map",
62
- fields: Object.freeze({ ...fields })
63
- }),
74
+ object: (fields) => {
75
+ const own = Object.freeze({ ...fields });
76
+ return built(Object.freeze({
77
+ kind: "map",
78
+ fields: own
79
+ }), Object.values(own));
80
+ },
64
81
  /** An open record: every key has type `value`. */
65
- record: (value) => Object.freeze({
82
+ record: (value) => built(Object.freeze({
66
83
  kind: "map",
67
84
  fields: Object.freeze({}),
68
85
  rest: value
69
- }),
70
- union: (...types) => Object.freeze({
71
- kind: "union",
72
- types: Object.freeze(flattenMembers(types))
73
- }),
86
+ }), [value]),
87
+ union: (...types) => {
88
+ const members = Object.freeze(flattenMembers(types));
89
+ return built(Object.freeze({
90
+ kind: "union",
91
+ types: members
92
+ }), members);
93
+ },
74
94
  /** `T | null`. Absent keys read as null, so this also marks a field optional. */
75
- optional: (type) => Object.freeze({
76
- kind: "union",
77
- types: Object.freeze(flattenMembers([type, NULL]))
78
- }),
79
- opaque: (name) => Object.freeze({
95
+ optional: (type) => {
96
+ const members = Object.freeze(flattenMembers([type, NULL]));
97
+ return built(Object.freeze({
98
+ kind: "union",
99
+ types: members
100
+ }), members);
101
+ },
102
+ opaque: (name) => built(Object.freeze({
80
103
  kind: "opaque",
81
104
  name
82
- })
105
+ }))
83
106
  });
84
107
  /**
85
108
  * The checker's work budget. Type operations charge it so a small source that
@@ -180,6 +203,50 @@ function exactObject(fields) {
180
203
  function isExact(type) {
181
204
  return EXACT.has(type);
182
205
  }
206
+ /**
207
+ * A type as frozen plain data: only its kind and that kind's own fields, read
208
+ * directly (never through toJSON), keeping exactness. Taken once from a
209
+ * caller's type, it is what a program is checked, cached, and recorded with,
210
+ * whatever later happens to the caller's object. A type built by t is already
211
+ * that, and is returned as it is, so the checker's caches keyed on it stay warm.
212
+ */
213
+ function typeData(type) {
214
+ if (BUILT.has(type)) return type;
215
+ switch (type.kind) {
216
+ case "list": return Object.freeze({
217
+ kind: "list",
218
+ element: typeData(type.element)
219
+ });
220
+ case "union": return Object.freeze({
221
+ kind: "union",
222
+ types: Object.freeze(type.types.map(typeData))
223
+ });
224
+ case "map": {
225
+ const fields = {};
226
+ for (const key of Object.keys(type.fields)) Object.defineProperty(fields, key, {
227
+ value: typeData(type.fields[key]),
228
+ enumerable: true,
229
+ writable: true,
230
+ configurable: true
231
+ });
232
+ if (isExact(type) && type.rest === void 0) return exactObject(fields);
233
+ return Object.freeze({
234
+ kind: "map",
235
+ fields: Object.freeze(fields),
236
+ ...type.rest === void 0 ? {} : { rest: typeData(type.rest) }
237
+ });
238
+ }
239
+ case "literal": return Object.freeze({
240
+ kind: "literal",
241
+ value: type.value
242
+ });
243
+ case "opaque": return Object.freeze({
244
+ kind: "opaque",
245
+ name: type.name
246
+ });
247
+ default: return Object.freeze({ kind: type.kind });
248
+ }
249
+ }
183
250
  /** A copy of a map type with new fields, keeping its exactness. */
184
251
  function withFields(type, fields) {
185
252
  if (isExact(type) && type.rest === void 0) return exactObject(fields);
@@ -627,6 +694,19 @@ function formatType(type, maxLength = MAX_TYPE_TEXT) {
627
694
  }
628
695
  return formatTypeText(type, maxLength);
629
696
  }
697
+ /**
698
+ * Longest type text in a diagnostic message. A message can name a large object
699
+ * type, and a source can repeat the mistake thousands of times, so a short cap
700
+ * keeps diagnostics (and their JSON) near the size of the source.
701
+ */
702
+ const MESSAGE_TYPE_TEXT = 120;
703
+ const typeTexts = /* @__PURE__ */ new WeakMap();
704
+ /** A type's text for a diagnostic message, shortened with an ellipsis. */
705
+ function typeText(type) {
706
+ let text = typeTexts.get(type);
707
+ if (text === void 0) typeTexts.set(type, text = formatType(type, MESSAGE_TYPE_TEXT));
708
+ return text;
709
+ }
630
710
  const HIGH_SURROGATE_FIRST = 55296;
631
711
  const HIGH_SURROGATE_LAST = 56319;
632
712
  function formatTypeText(type, maxLength) {
@@ -709,6 +789,6 @@ function formatTypeText(type, maxLength) {
709
789
  return out;
710
790
  }
711
791
  //#endregion
712
- export { TypeBudgetExceeded, chargeTypeWork, exactObject, fieldOf, formatType, freshLiteral, isAssignable, isExact, isNullable, isProvenAssignable, nonNull, overlaps, sameType, t, unionMembers, unionOf, widen, widenFresh, withFields, withTypeBudget };
792
+ export { TypeBudgetExceeded, chargeTypeWork, exactObject, fieldOf, formatType, freshLiteral, isAssignable, isExact, isNullable, isProvenAssignable, nonNull, overlaps, sameType, t, typeData, typeText, unionMembers, unionOf, widen, widenFresh, withFields, withTypeBudget };
713
793
 
714
794
  //# sourceMappingURL=types.mjs.map