@skillstech/thunderlang 0.1.7 → 0.3.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
@@ -3,6 +3,59 @@
3
3
  All notable changes to `@skillstech/thunderlang`. Pre-1.0: the language and the
4
4
  `intent-graph-v1` schema version independently and may still change.
5
5
 
6
+ ## 0.3.0
7
+
8
+ The 14-language lift release. Additive; no breaking changes. Restores the OpenThunder -> ThunderLang
9
+ recover-intent loop for every language OpenThunder's archgraph core can now discover.
10
+
11
+ - **Kotlin, Scala, and Elixir lift adapters.** `thunder lift` now extracts candidate intent from
12
+ `.kt`/`.kts`, `.scala`/`.sc`, and `.ex`/`.exs`, bringing the supported-language count to 14. A new
13
+ `name: Type` parameter parser handles the JVM signature shape; Elixir joins the dynamic-language set.
14
+ - **Fix: repo-walk language coverage.** `LIFT_EXTS` was stale and silently skipped Python, Java, C#,
15
+ Go, C++, PHP, and Ruby during repo/`--all` lifts; it now mirrors the full adapter set.
16
+ - **Fix: single-file lift language detection.** `thunder lift <file>` now auto-detects the language by
17
+ extension instead of defaulting to TypeScript, consistent with `--all`/repo modes.
18
+
19
+ ## 0.2.0
20
+
21
+ The cross-language target execution release. Additive; no breaking changes.
22
+
23
+ Version alignment: starting with 0.2.0, ThunderLang and OpenThunder version in lockstep, since most
24
+ OpenThunder changes land in the language first. (0.1.8 and 0.1.9 were never published; the last
25
+ release on npm was 0.1.7.)
26
+
27
+ - **Live target adapters.** `thunder test <file> --target typescript|python|csharp|java` and
28
+ `thunder conform <file> --run <targets>` now compile the generated decision(s) and execute them
29
+ for real, grading the actual outputs instead of fed results. TypeScript/JS runs in-process,
30
+ Python through `python3`, C# through a throwaway `dotnet` console project, and Java through the
31
+ JDK 11+ single-file launcher (`java ThunderTarget.java`).
32
+ - **`--all-targets`.** `thunder test <file> --all-targets` and `thunder conform <file> --all-targets`
33
+ run every target whose toolchain is available in one pass, side by side, and skip any target whose
34
+ runtime is absent (it stays declared in the conformance matrix) rather than failing.
35
+ - **Static-target type inference.** Because C# and Java are statically typed, each decision
36
+ parameter's type is inferred from the test-case values it receives (numeric wins, then boolean,
37
+ else string), and each language gets its own equality translation.
38
+ - Missing toolchains are detected via cached smoke checks and skipped cleanly, so builds never fail
39
+ for a runtime that is not installed.
40
+
41
+ ## 0.1.8
42
+
43
+ The testing and verification release. Additive; no breaking changes.
44
+
45
+ - **Rename to ThunderLang.** Package is `@skillstech/thunderlang`; the CLI binary is `thunder`
46
+ (with `intent` kept as a legacy alias). `.thunder` is the canonical source extension; `.tl` and
47
+ `.intent` are accepted. The Intent Graph and Intent-Oriented Programming vocabulary are retained.
48
+ - **`thunder prove`** emits an `intent-proof-v1` artifact with honest per-claim verdicts
49
+ (verified / failed / declared / needs_verification, with `provenBy`). An unverified claim never
50
+ reads as proven.
51
+ - **`thunder test --contracts [--strict]`** derives an obligation from every `guarantee` and
52
+ `never`, resolved against the specific test that verifies it. `--strict` fails CI on any unverified.
53
+ - **Verification classification**: `verify by assertion|static|runtime|evidence|tool|approval|formal`.
54
+ - **Stable IDs**: explicit `id INV-G-001` on guarantees / never-rules (slug fallback).
55
+ - **Proof freshness**: the proof records intent hash, compiler, git commit, dependency-lockfile hash,
56
+ and environment; `thunder verify` marks a proof STALE when the implementation, dependencies, or
57
+ compiler move since it was generated.
58
+
6
59
  ## 0.1.6
7
60
 
8
61
  The agent-conformance release (skills on intents + 12-factor scoring). Additive; no breaking
package/dist/core.cjs CHANGED
@@ -789,6 +789,60 @@ function parseDecision(name, node2) {
789
789
  }
790
790
  return dec;
791
791
  }
792
+ function parseProperty(name, node2) {
793
+ const prop = { name, vars: [], decide: null, expects: [], line: node2.line };
794
+ for (const c of node2.children.filter((x) => !isNote(x))) {
795
+ const k = firstWord(c.text);
796
+ const a = rest(c.text);
797
+ if (k === "forAll" || k === "forall") {
798
+ for (const v of c.children.filter((x) => !isNote(x))) {
799
+ const m = v.text.trim().match(/^([A-Za-z_]\w*)\s*:\s*(\w+)(?:\s+where\s+(.+))?$/);
800
+ if (!m) continue;
801
+ const whereChild = (v.children || []).find((x) => firstWord(x.text) === "where");
802
+ prop.vars.push({ name: m[1], type: m[2], where: m[3] || (whereChild ? rest(whereChild.text) : null) });
803
+ }
804
+ } else if (k === "decide") prop.decide = a || c.children[0] && c.children[0].text.trim() || null;
805
+ else if (k === "when") {
806
+ const dm = a.match(/(?:decide|execute)\s+(\w+)/);
807
+ if (dm) prop.decide = dm[1];
808
+ } else if (k === "expect") for (const e of c.children.filter((x) => !isNote(x))) prop.expects.push(e.text.trim());
809
+ }
810
+ return prop;
811
+ }
812
+ function parseEvaluation(name, node2) {
813
+ const ev = { name, dataset: null, requires: [], line: node2.line };
814
+ for (const c of node2.children.filter((x) => !isNote(x))) {
815
+ const k = firstWord(c.text);
816
+ const a = rest(c.text);
817
+ if (k === "dataset") ev.dataset = a || c.children[0] && c.children[0].text.trim() || null;
818
+ else if (k === "require") {
819
+ for (const r of c.children.filter((x) => !isNote(x))) {
820
+ const m = r.text.trim().match(/^([A-Za-z_][\w.]*)\s*(>=|<=|==|!=|>|<)\s*(-?[\d.]+)$/);
821
+ if (m) ev.requires.push({ metric: m[1], op: m[2], threshold: parseFloat(m[3]) });
822
+ }
823
+ }
824
+ }
825
+ return ev;
826
+ }
827
+ function parseScenario(name, node2) {
828
+ const sc = { name, given: [], when: [], then: [], never: [], eventually: [], line: node2.line };
829
+ const clausesOf = (c) => {
830
+ const items = leafItems(c);
831
+ return items.length ? items : rest(c.text) ? [rest(c.text)] : [];
832
+ };
833
+ for (const c of node2.children.filter((x) => !isNote(x))) {
834
+ const k = firstWord(c.text);
835
+ if (k === "given") sc.given.push(...clausesOf(c));
836
+ else if (k === "when") sc.when.push(...clausesOf(c));
837
+ else if (k === "then") sc.then.push(...clausesOf(c));
838
+ else if (k === "never") sc.never.push(...clausesOf(c));
839
+ else if (k === "eventually") {
840
+ const within = (rest(c.text).match(/within\s+(.+)/) || [])[1] || null;
841
+ sc.eventually.push({ within, clauses: leafItems(c) });
842
+ }
843
+ }
844
+ return sc;
845
+ }
792
846
  function parseLifecycle(name, node2) {
793
847
  const lc = { name, states: [], transitions: [], terminals: [], line: node2.line };
794
848
  for (const c of node2.children.filter((x) => !isNote(x))) {
@@ -821,7 +875,13 @@ function parseInvariant(name, node2) {
821
875
  for (const c of kids(node2)) {
822
876
  const k = firstWord(c.text);
823
877
  if (k === "verify") inv.verify.push(rest(c.text));
824
- else if (k === "because") inv.because = rest(c.text);
878
+ else if (k === "id") {
879
+ const v = rest(c.text);
880
+ if (v) {
881
+ inv.id = v;
882
+ inv.stableId = v;
883
+ }
884
+ } else if (k === "because") inv.because = rest(c.text);
825
885
  else if (k === "statement") inv.statement = rest(c.text) || leafItems(c).join(" ");
826
886
  else if (k === "scope") inv.scope = (rest(c.text) || leafItems(c).join(" ")).toLowerCase();
827
887
  else if (k === "severity") inv.severity = (rest(c.text) || leafItems(c).join(" ")).toLowerCase();
@@ -907,11 +967,12 @@ function parseHandler(trigger, node2) {
907
967
  actions
908
968
  };
909
969
  }
970
+ var VERIFY_KINDS = /* @__PURE__ */ new Set(["assertion", "static", "runtime", "evidence", "tool", "approval", "formal"]);
910
971
  function upsertRule(list, statement, line) {
911
972
  const id = slug(statement);
912
973
  let r = list.find((x) => x.id === id);
913
974
  if (!r) {
914
- r = { id, statement, because: null, verify: [], notes: [], line };
975
+ r = { id, statement, because: null, verify: [], verifications: [], notes: [], line };
915
976
  list.push(r);
916
977
  }
917
978
  return r;
@@ -920,8 +981,28 @@ function applyDetail(rule, node2) {
920
981
  for (const c of node2.children) {
921
982
  const kw = firstWord(c.text);
922
983
  if (kw === "because") rule.because = rest(c.text) || c.children[0] && c.children[0].text || null;
923
- else if (kw === "verify") rule.verify.push(rest(c.text) || c.children[0] && c.children[0].text || "");
924
- else if (isNote(c)) rule.notes.push(parseNoteNode(c));
984
+ else if (kw === "verify") {
985
+ const a = rest(c.text);
986
+ const m = a && a.match(/^by\s+(\w+)/i);
987
+ if (m) {
988
+ const kind = m[1].toLowerCase();
989
+ const details = (c.children || []).filter((x) => !isNote(x)).map((x) => x.text.trim()).filter(Boolean);
990
+ rule.verifications.push({ kind: VERIFY_KINDS.has(kind) ? kind : "unknown", declared: kind, details });
991
+ rule.verify.push(details.length ? `${kind}: ${details.join("; ")}` : `by ${kind}`);
992
+ } else {
993
+ const v = a || c.children[0] && c.children[0].text || "";
994
+ if (v) {
995
+ rule.verify.push(v);
996
+ rule.verifications.push({ kind: "unclassified", declared: null, details: [v] });
997
+ }
998
+ }
999
+ } else if (kw === "id") {
1000
+ const v = rest(c.text) || c.children[0] && c.children[0].text;
1001
+ if (v) {
1002
+ rule.id = v;
1003
+ rule.stableId = v;
1004
+ }
1005
+ } else if (isNote(c)) rule.notes.push(parseNoteNode(c));
925
1006
  }
926
1007
  rule.verify = rule.verify.filter(Boolean);
927
1008
  }
@@ -986,6 +1067,9 @@ function parseIntent(source) {
986
1067
  handlers: [],
987
1068
  // Decisions, rules, process (Gap 4)
988
1069
  decisions: [],
1070
+ properties: [],
1071
+ scenarios: [],
1072
+ evaluations: [],
989
1073
  // Governance: waivers , governed exceptions to blocking diagnostics (Gap 5)
990
1074
  waivers: [],
991
1075
  // Data purpose + privacy , governed data elements (Gap 6)
@@ -1214,6 +1298,15 @@ function parseIntent(source) {
1214
1298
  case "decision":
1215
1299
  ast.decisions.push(parseDecision(arg, node2));
1216
1300
  break;
1301
+ case "property":
1302
+ ast.properties.push(parseProperty(arg, node2));
1303
+ break;
1304
+ case "scenario":
1305
+ ast.scenarios.push(parseScenario(arg, node2));
1306
+ break;
1307
+ case "evaluation":
1308
+ ast.evaluations.push(parseEvaluation(arg, node2));
1309
+ break;
1217
1310
  // ── System profile ──
1218
1311
  case "capability": {
1219
1312
  const cap = { name: arg, description: null, implements: [], line: node2.line };
@@ -3198,21 +3291,23 @@ function compileExpr(src) {
3198
3291
  function evalExpr(src, inputs = {}) {
3199
3292
  return compileExpr(src)(inputs);
3200
3293
  }
3294
+ var C_LOGIC = { and: (a, b) => `(${a} && ${b})`, or: (a, b) => `(${a} || ${b})`, not: (a) => `!${a}`, bool: (v) => String(v) };
3201
3295
  var DIALECTS = {
3202
- js: { eq: (a, b) => `${a} === ${b}`, neq: (a, b) => `${a} !== ${b}`, inList: (list, x) => `[${list.join(", ")}].includes(${x})`, nil: "undefined" },
3203
- csharp: { eq: (a, b) => `${a} == ${b}`, neq: (a, b) => `${a} != ${b}`, inList: (list, x) => `new[]{${list.join(", ")}}.Contains(${x})`, nil: "null" },
3204
- java: { eq: (a, b) => `java.util.Objects.equals(${a}, ${b})`, neq: (a, b) => `!java.util.Objects.equals(${a}, ${b})`, inList: (list, x) => `java.util.List.of(${list.join(", ")}).contains(${x})`, nil: "null" }
3296
+ js: { ...C_LOGIC, eq: (a, b) => `${a} === ${b}`, neq: (a, b) => `${a} !== ${b}`, inList: (list, x) => `[${list.join(", ")}].includes(${x})`, nil: "undefined" },
3297
+ csharp: { ...C_LOGIC, eq: (a, b) => `${a} == ${b}`, neq: (a, b) => `${a} != ${b}`, inList: (list, x) => `new[]{${list.join(", ")}}.Contains(${x})`, nil: "null" },
3298
+ java: { ...C_LOGIC, eq: (a, b) => `java.util.Objects.equals(${a}, ${b})`, neq: (a, b) => `!java.util.Objects.equals(${a}, ${b})`, inList: (list, x) => `java.util.List.of(${list.join(", ")}).contains(${x})`, nil: "null" },
3299
+ python: { and: (a, b) => `(${a} and ${b})`, or: (a, b) => `(${a} or ${b})`, not: (a) => `(not ${a})`, bool: (v) => v ? "True" : "False", eq: (a, b) => `${a} == ${b}`, neq: (a, b) => `${a} != ${b}`, inList: (list, x) => `${x} in [${list.join(", ")}]`, nil: "None" }
3205
3300
  };
3206
3301
  function renderExpr(src, inputs, D) {
3207
3302
  const known = new Set(inputs);
3208
3303
  const r = (n) => {
3209
3304
  switch (n.k) {
3210
3305
  case "or":
3211
- return `(${r(n.a)} || ${r(n.b)})`;
3306
+ return D.or(r(n.a), r(n.b));
3212
3307
  case "and":
3213
- return `(${r(n.a)} && ${r(n.b)})`;
3308
+ return D.and(r(n.a), r(n.b));
3214
3309
  case "not":
3215
- return `!${r(n.a)}`;
3310
+ return D.not(r(n.a));
3216
3311
  case "neg":
3217
3312
  return `-${r(n.a)}`;
3218
3313
  case "arith":
@@ -3226,7 +3321,7 @@ function renderExpr(src, inputs, D) {
3226
3321
  case "list":
3227
3322
  return `[${n.items.map(r).join(", ")}]`;
3228
3323
  case "lit":
3229
- return typeof n.v === "string" ? JSON.stringify(n.v) : String(n.v);
3324
+ return typeof n.v === "string" ? JSON.stringify(n.v) : typeof n.v === "boolean" ? D.bool(n.v) : String(n.v);
3230
3325
  case "ref":
3231
3326
  return known.has(n.path) || known.has(n.path.split(".")[0]) ? n.path : JSON.stringify(n.path);
3232
3327
  default:
@@ -4193,7 +4288,7 @@ function notesSummary(ast) {
4193
4288
  byLens
4194
4289
  };
4195
4290
  }
4196
- var COMPILER_VERSION = "0.1.0";
4291
+ var COMPILER_VERSION = "0.3.0";
4197
4292
  var PROOF_SCHEMA_VERSION = "0.1.0";
4198
4293
  var SOURCE_PRODUCT = "skillstech-compiler";
4199
4294
  function buildContractGraph(ast, generatedAt) {
@@ -4686,13 +4781,15 @@ function buildProof(ast, { sourceFile, sourceHash, targetsRequested, targetsGene
4686
4781
  id: g.id,
4687
4782
  text: g.statement,
4688
4783
  status: g.verify.length > 0 || verifiedText ? "planned" : "needs_verification",
4689
- evidence: g.verify
4784
+ evidence: g.verify,
4785
+ verifications: g.verifications || []
4690
4786
  })),
4691
4787
  neverRules: ast.neverRules.map((n) => ({
4692
4788
  id: n.id,
4693
4789
  text: n.statement,
4694
4790
  status: n.verify.length > 0 ? "planned" : "needs_verification",
4695
- evidence: n.verify
4791
+ evidence: n.verify,
4792
+ verifications: n.verifications || []
4696
4793
  })),
4697
4794
  errors: (ast.errors || []).map((e) => ({ name: e.name })),
4698
4795
  examples: (ast.examples || []).map((ex) => ({ given: ex.given, expect: ex.expect })),
@@ -5281,6 +5378,112 @@ function extractFactsRuby(source, file = "input.rb") {
5281
5378
  }
5282
5379
  return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "ruby", sourceRoot: file, functions, tests, errors };
5283
5380
  }
5381
+ function parseNameColonTypeParams(raw) {
5382
+ return splitTopLevel(raw, ",").map((p) => p.trim()).filter(Boolean).map((p) => {
5383
+ const cleaned = p.replace(/@\w+(\([^)]*\))?/g, "").replace(/^(?:val|var|vararg|implicit|final|lazy)\s+/, "").replace(/=.*$/, "").trim();
5384
+ const mm = cleaned.match(/^([A-Za-z_]\w*)\s*:\s*(.+)$/);
5385
+ if (mm) return { name: mm[1], type: mm[2].trim() };
5386
+ return { name: cleaned.replace(/[^\w].*$/, "") || cleaned, type: null };
5387
+ });
5388
+ }
5389
+ function extractFactsKotlin(source, file = "input.kt") {
5390
+ let m;
5391
+ const functions = [];
5392
+ const tests = [];
5393
+ const seen = /* @__PURE__ */ new Set();
5394
+ const testMethods = /* @__PURE__ */ new Set();
5395
+ const ta = /@Test\b[\s\S]{0,120}?\bfun\s+(?:`([^`]+)`|(\w+))\s*\(/g;
5396
+ while (m = ta.exec(source)) testMethods.add(m[1] || m[2]);
5397
+ const fnRe = /\bfun\s+(?:<[^>]*>\s*)?(?:[A-Za-z_][\w.]*\.)?(?:`([^`]+)`|([A-Za-z_]\w*))\s*\(([^)]*)\)\s*(?::\s*([^{=\n]+))?/g;
5398
+ while (m = fnRe.exec(source)) {
5399
+ const name = m[1] || m[2];
5400
+ if (testMethods.has(name)) {
5401
+ if (!tests.some((t) => t.name === name)) tests.push({ name, file, line: lineOf(source, m.index) });
5402
+ continue;
5403
+ }
5404
+ if (seen.has(name)) continue;
5405
+ seen.add(name);
5406
+ functions.push({ name, file, line: lineOf(source, m.index), parameters: parseNameColonTypeParams(m[3] || ""), returnType: m[4] ? m[4].trim() : null, evidence: [{ kind: "fun", file, line: lineOf(source, m.index) }] });
5407
+ }
5408
+ const errors = [];
5409
+ const addErr = addErrOf(errors, /* @__PURE__ */ new Set(), { _src: source, _file: file });
5410
+ let mm;
5411
+ const ce = /class\s+(\w*(?:Exception|Error))\b/g;
5412
+ while (mm = ce.exec(source)) addErr(mm[1], mm.index);
5413
+ const th = /throw\s+(\w+)\s*\(/g;
5414
+ while (mm = th.exec(source)) addErr(mm[1], mm.index);
5415
+ return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "kotlin", sourceRoot: file, functions, tests, errors };
5416
+ }
5417
+ function extractFactsScala(source, file = "input.scala") {
5418
+ let m;
5419
+ const functions = [];
5420
+ const tests = [];
5421
+ const seen = /* @__PURE__ */ new Set();
5422
+ const fnRe = /\bdef\s+([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s*\(([^)]*)\)\s*(?::\s*([^={\n]+))?/g;
5423
+ while (m = fnRe.exec(source)) {
5424
+ const name = m[1];
5425
+ if (seen.has(name)) continue;
5426
+ seen.add(name);
5427
+ functions.push({ name, file, line: lineOf(source, m.index), parameters: parseNameColonTypeParams(m[2] || ""), returnType: m[3] ? m[3].trim() : null, evidence: [{ kind: "def", file, line: lineOf(source, m.index) }] });
5428
+ }
5429
+ const seenT = /* @__PURE__ */ new Set();
5430
+ const addTest = (n, idx) => {
5431
+ const k = String(n).toLowerCase();
5432
+ if (n && !seenT.has(k)) {
5433
+ seenT.add(k);
5434
+ tests.push({ name: n, file, line: lineOf(source, idx) });
5435
+ }
5436
+ };
5437
+ const tr = /\btest\s*\(\s*"([^"]+)"/g;
5438
+ while (m = tr.exec(source)) addTest(m[1], m.index);
5439
+ const inRe = /"([^"]+)"\s+(?:in|should|must)\b/g;
5440
+ while (m = inRe.exec(source)) addTest(m[1], m.index);
5441
+ const errors = [];
5442
+ const addErr = addErrOf(errors, /* @__PURE__ */ new Set(), { _src: source, _file: file });
5443
+ let mm;
5444
+ const ce = /class\s+(\w*(?:Exception|Error))\b/g;
5445
+ while (mm = ce.exec(source)) addErr(mm[1], mm.index);
5446
+ const th = /throw\s+new\s+(\w+)\s*\(/g;
5447
+ while (mm = th.exec(source)) addErr(mm[1], mm.index);
5448
+ return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "scala", sourceRoot: file, functions, tests, errors };
5449
+ }
5450
+ function extractFactsElixir(source, file = "input.ex") {
5451
+ let m;
5452
+ const functions = [];
5453
+ const tests = [];
5454
+ const seen = /* @__PURE__ */ new Set();
5455
+ const seenT = /* @__PURE__ */ new Set();
5456
+ const addTest = (n, idx) => {
5457
+ const k = String(n).toLowerCase();
5458
+ if (n && !seenT.has(k)) {
5459
+ seenT.add(k);
5460
+ tests.push({ name: n, file, line: lineOf(source, idx) });
5461
+ }
5462
+ };
5463
+ const tr = /\btest\s+"([^"]+)"/g;
5464
+ while (m = tr.exec(source)) addTest(m[1], m.index);
5465
+ const fnRe = /^[ \t]*defp?\s+([a-z_]\w*[!?]?)\s*(?:\(([^)]*)\))?/gm;
5466
+ while (m = fnRe.exec(source)) {
5467
+ const name = m[1];
5468
+ if (seen.has(name)) continue;
5469
+ seen.add(name);
5470
+ const parameters = splitTopLevel(m[2] || "", ",").map((p) => p.trim()).filter(Boolean).map((p) => ({ name: p.split(/\\\\/)[0].trim().replace(/^%\{?/, "").replace(/[^\w].*$/, "") || p, type: null }));
5471
+ functions.push({ name, file, line: lineOf(source, m.index), indent: (m[0].match(/^[ \t]*/) || [""])[0].length, parameters, returnType: null, evidence: [{ kind: "def", file, line: lineOf(source, m.index) }] });
5472
+ }
5473
+ const errors = [];
5474
+ const seenErr = /* @__PURE__ */ new Set();
5475
+ const addErr = (n, idx) => {
5476
+ if (n && !seenErr.has(n)) {
5477
+ seenErr.add(n);
5478
+ errors.push({ name: n, file, line: lineOf(source, idx) });
5479
+ }
5480
+ };
5481
+ const modErr = /defmodule\s+([\w.]*(?:Error|Exception))\b/g;
5482
+ while (m = modErr.exec(source)) addErr(m[1].split(".").pop(), m.index);
5483
+ const raiseRe = /raise\s+([A-Z][\w.]*)/g;
5484
+ while (m = raiseRe.exec(source)) addErr(m[1].split(".").pop(), m.index);
5485
+ return { schemaVersion: IR_SCHEMA_VERSION, sourceLanguage: "elixir", sourceRoot: file, functions, tests, errors };
5486
+ }
5284
5487
  var ADAPTERS = {
5285
5488
  typescript: extractFactsTypeScript,
5286
5489
  ts: extractFactsTypeScript,
@@ -5304,10 +5507,18 @@ var ADAPTERS = {
5304
5507
  cc: extractFactsCpp,
5305
5508
  php: extractFactsPhp,
5306
5509
  ruby: extractFactsRuby,
5307
- rb: extractFactsRuby
5510
+ rb: extractFactsRuby,
5511
+ kotlin: extractFactsKotlin,
5512
+ kt: extractFactsKotlin,
5513
+ kts: extractFactsKotlin,
5514
+ scala: extractFactsScala,
5515
+ sc: extractFactsScala,
5516
+ elixir: extractFactsElixir,
5517
+ ex: extractFactsElixir,
5518
+ exs: extractFactsElixir
5308
5519
  };
5309
- var SUPPORTED_LANGUAGES = ["typescript", "javascript", "python", "java", "csharp", "go", "rust", "cpp", "php", "ruby", "perl"];
5310
- var DYNAMIC_LANGUAGES = /* @__PURE__ */ new Set(["perl", "javascript", "python", "ruby", "php"]);
5520
+ var SUPPORTED_LANGUAGES = ["typescript", "javascript", "python", "java", "csharp", "go", "rust", "cpp", "php", "ruby", "perl", "kotlin", "scala", "elixir"];
5521
+ var DYNAMIC_LANGUAGES = /* @__PURE__ */ new Set(["perl", "javascript", "python", "ruby", "php", "elixir"]);
5311
5522
  var LANG_DISPLAY = {
5312
5523
  typescript: "TypeScript",
5313
5524
  javascript: "JavaScript",
@@ -5319,7 +5530,10 @@ var LANG_DISPLAY = {
5319
5530
  cpp: "C++",
5320
5531
  php: "PHP",
5321
5532
  ruby: "Ruby",
5322
- perl: "Perl"
5533
+ perl: "Perl",
5534
+ kotlin: "Kotlin",
5535
+ scala: "Scala",
5536
+ elixir: "Elixir"
5323
5537
  };
5324
5538
  function unwrapReturn(ret) {
5325
5539
  if (!ret) return { output: null, error: null };
@@ -5443,6 +5657,9 @@ function languageForFile(file) {
5443
5657
  if (/\.(cpp|cc|cxx|hpp|hh|c|h)$/i.test(file)) return "cpp";
5444
5658
  if (/\.php$/i.test(file)) return "php";
5445
5659
  if (/\.rb$/i.test(file)) return "ruby";
5660
+ if (/\.kts?$/i.test(file)) return "kotlin";
5661
+ if (/\.(scala|sc)$/i.test(file)) return "scala";
5662
+ if (/\.exs?$/i.test(file)) return "elixir";
5446
5663
  if (/\.(mjs|cjs|jsx?)$/i.test(file)) return "javascript";
5447
5664
  return "typescript";
5448
5665
  }
@@ -5450,7 +5667,7 @@ function isPublicFn(fn, language) {
5450
5667
  const name = fn.name || "";
5451
5668
  if (/(?:Internal|Impl|_impl|_helper|_test|Helper)$/.test(name)) return false;
5452
5669
  if (language === "go" || language === "golang") return /^[A-Z]/.test(name) && name !== "Test";
5453
- if (language === "python" || language === "ruby") return !name.startsWith("_") && (fn.indent == null || fn.indent <= 4);
5670
+ if (language === "python" || language === "ruby" || language === "elixir") return !name.startsWith("_") && (fn.indent == null || fn.indent <= 4);
5454
5671
  return !name.startsWith("_") && name !== "init" && name !== "constructor";
5455
5672
  }
5456
5673
  function liftAll(source, { language = "typescript", file = "", publicOnly = true } = {}) {
@@ -5525,7 +5742,10 @@ var LANG_EXT = {
5525
5742
  cpp: "cpp",
5526
5743
  php: "php",
5527
5744
  ruby: "rb",
5528
- perl: "pl"
5745
+ perl: "pl",
5746
+ kotlin: "kt",
5747
+ scala: "scala",
5748
+ elixir: "ex"
5529
5749
  };
5530
5750
  function liftSource(source, { language = "typescript", file = "", seeds = void 0 } = {}) {
5531
5751
  const key = String(language).toLowerCase();