Skip to content

Commit b6b10c1

Browse files
authored
Merge pull request #98 from benbernard/feature/lvalue-template-expansion
feat: lvalue template expansion, project-wide lint, universal file input, multi-DB fromdb
2 parents 161cf07 + 8ae89f1 commit b6b10c1

50 files changed

Lines changed: 1253 additions & 483 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 223 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"devDependencies": {
2323
"@types/better-sqlite3": "^7.6.13",
2424
"@types/bun": "latest",
25+
"@types/oracledb": "^6.10.1",
2526
"@types/papaparse": "^5.5.2",
27+
"@types/pg": "^8.16.0",
2628
"lefthook": "^2.1.1",
2729
"oxlint": "latest",
2830
"typescript": "^5",
@@ -38,9 +40,12 @@
3840
"ink-spinner": "^5.0.0",
3941
"ink-text-input": "^6.0.0",
4042
"mongodb": "^7.1.0",
43+
"mysql2": "^3.18.0",
4144
"nanoid": "^5.1.6",
4245
"openai": "^6.22.0",
46+
"oracledb": "^6.10.0",
4347
"papaparse": "^5.5.3",
48+
"pg": "^8.18.0",
4449
"react": "^19.2.4",
4550
"string-width": "^8.2.0"
4651
}

scripts/check-no-private.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* CI/lint script that ensures no TypeScript visibility modifiers (private,
33
* protected, public) or JavaScript private class fields (#) appear in the
4-
* Explorer codebase.
4+
* codebase.
55
*
66
* Usage: bun scripts/check-no-private.ts
77
* Exit code 0 = all good, non-zero = violations found.
@@ -12,8 +12,8 @@ import { join, relative } from "node:path";
1212

1313
const ROOT = join(import.meta.dir, "..");
1414
const SCAN_DIRS = [
15-
join(ROOT, "src", "explorer"),
16-
join(ROOT, "tests", "explorer"),
15+
join(ROOT, "src"),
16+
join(ROOT, "tests"),
1717
];
1818

1919
const EXTENSIONS = new Set([".ts", ".tsx"]);

src/Accumulator.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@ import type { Record } from "./Record.ts";
77
* Analogous to App::RecordStream::Accumulator in Perl.
88
*/
99
export class Accumulator {
10-
#records: Record[] = [];
10+
records: Record[] = [];
1111

1212
acceptRecord(record: Record): void {
1313
this.accumulateRecord(record);
1414
}
1515

1616
accumulateRecord(record: Record): void {
17-
this.#records.push(record);
17+
this.records.push(record);
1818
}
1919

2020
getRecords(): Record[] {
21-
return this.#records;
21+
return this.records;
2222
}
2323

2424
clear(): void {
25-
this.#records = [];
25+
this.records = [];
2626
}
2727
}

src/BaseRegistry.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface RegistryEntry<T> {
2020
}
2121

2222
export class BaseRegistry<T> {
23-
#implementations = new Map<string, RegistryEntry<T>>();
23+
implementations = new Map<string, RegistryEntry<T>>();
2424
readonly typeName: string;
2525

2626
constructor(typeName: string) {
@@ -31,10 +31,10 @@ export class BaseRegistry<T> {
3131
* Register an implementation under a name.
3232
*/
3333
register(name: string, entry: RegistryEntry<T>): void {
34-
this.#implementations.set(name, entry);
34+
this.implementations.set(name, entry);
3535
if (entry.aliases) {
3636
for (const alias of entry.aliases) {
37-
this.#implementations.set(alias, entry);
37+
this.implementations.set(alias, entry);
3838
}
3939
}
4040
}
@@ -51,7 +51,7 @@ export class BaseRegistry<T> {
5151
const name = parts[0]!;
5252
const args = parts.slice(1);
5353

54-
const entry = this.#implementations.get(name);
54+
const entry = this.implementations.get(name);
5555
if (!entry) {
5656
throw new Error(`Bad ${this.typeName}: ${name}`);
5757
}
@@ -71,14 +71,14 @@ export class BaseRegistry<T> {
7171
* Check if a name is registered.
7272
*/
7373
has(name: string): boolean {
74-
return this.#implementations.has(name);
74+
return this.implementations.has(name);
7575
}
7676

7777
/**
7878
* Get an entry by name.
7979
*/
8080
get(name: string): RegistryEntry<T> | undefined {
81-
return this.#implementations.get(name);
81+
return this.implementations.get(name);
8282
}
8383

8484
/**
@@ -89,9 +89,9 @@ export class BaseRegistry<T> {
8989
const entryToNames = new Map<RegistryEntry<T>, string[]>();
9090
const entries: RegistryEntry<T>[] = [];
9191

92-
const sortedNames = [...this.#implementations.keys()].sort();
92+
const sortedNames = [...this.implementations.keys()].sort();
9393
for (const name of sortedNames) {
94-
const entry = this.#implementations.get(name)!;
94+
const entry = this.implementations.get(name)!;
9595
let names = entryToNames.get(entry);
9696
if (!names) {
9797
names = [];
@@ -113,7 +113,7 @@ export class BaseRegistry<T> {
113113
* Get the detailed usage for a specific implementation.
114114
*/
115115
showImplementation(name: string): string {
116-
const entry = this.#implementations.get(name);
116+
const entry = this.implementations.get(name);
117117
if (!entry) {
118118
return `Bad ${this.typeName}: ${name}\n`;
119119
}
@@ -124,6 +124,6 @@ export class BaseRegistry<T> {
124124
* Get all registered names.
125125
*/
126126
names(): string[] {
127-
return [...this.#implementations.keys()];
127+
return [...this.implementations.keys()];
128128
}
129129
}

src/DomainLanguage.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ export interface Valuation {
2222

2323
/** A valuation that extracts a field value via KeySpec */
2424
export class KeySpecValuation implements Valuation {
25-
#field: string;
25+
field: string;
2626
constructor(field: string) {
27-
this.#field = field;
27+
this.field = field;
2828
}
2929
evaluateRecord(record: Record): JsonValue {
30-
const v = findKey(record.dataRef(), this.#field, true);
30+
const v = findKey(record.dataRef(), this.field, true);
3131
return v === undefined ? null : v;
3232
}
3333
}
@@ -41,12 +41,12 @@ export class RecordValuation implements Valuation {
4141

4242
/** A valuation from a JS function */
4343
export class FunctionValuation implements Valuation {
44-
#fn: (r: Record) => JsonValue;
44+
fn: (r: Record) => JsonValue;
4545
constructor(fn: (r: Record) => JsonValue) {
46-
this.#fn = fn;
46+
this.fn = fn;
4747
}
4848
evaluateRecord(record: Record): JsonValue {
49-
return this.#fn(record);
49+
return this.fn(record);
5050
}
5151
}
5252

src/Executor.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,31 @@ export interface SnippetDef {
1818
}
1919

2020
export class Executor {
21-
#snippets: Map<string, CompiledSnippet>;
22-
#lineCounter = 0;
23-
#currentFilename = "NONE";
24-
#state: Record<string, unknown> = {};
21+
snippets: Map<string, CompiledSnippet>;
22+
lineCounter = 0;
23+
currentFilename = "NONE";
24+
state: Record<string, unknown> = {};
2525

2626
constructor(codeOrSnippets: string | { [name: string]: SnippetDef }) {
27-
this.#snippets = new Map();
27+
this.snippets = new Map();
2828

2929
if (typeof codeOrSnippets === "string") {
30-
this.#addSnippet("__DEFAULT", {
30+
this.addSnippet("__DEFAULT", {
3131
code: codeOrSnippets,
3232
argNames: ["r"],
3333
});
3434
} else {
3535
for (const [name, def] of Object.entries(codeOrSnippets)) {
36-
this.#addSnippet(name, def);
36+
this.addSnippet(name, def);
3737
}
3838
}
3939
}
4040

41-
#addSnippet(name: string, def: SnippetDef): void {
41+
addSnippet(name: string, def: SnippetDef): void {
4242
const transformedCode = transformCode(def.code);
4343
const argNames = def.argNames ?? ["r"];
44-
const fn = compileSnippet(transformedCode, argNames, this.#state);
45-
this.#snippets.set(name, { fn, argNames });
44+
const fn = compileSnippet(transformedCode, argNames, this.state);
45+
this.snippets.set(name, { fn, argNames });
4646
}
4747

4848
/**
@@ -57,29 +57,29 @@ export class Executor {
5757
* Execute a named snippet with the given arguments.
5858
*/
5959
executeMethod(name: string, ...args: unknown[]): unknown {
60-
const snippet = this.#snippets.get(name);
60+
const snippet = this.snippets.get(name);
6161
if (!snippet) {
6262
throw new Error(`No such snippet: ${name}`);
6363
}
6464

65-
this.#lineCounter++;
66-
return snippet.fn(...args, this.#lineCounter, this.#currentFilename);
65+
this.lineCounter++;
66+
return snippet.fn(...args, this.lineCounter, this.currentFilename);
6767
}
6868

6969
setCurrentFilename(filename: string): void {
70-
this.#currentFilename = filename;
70+
this.currentFilename = filename;
7171
}
7272

7373
getCurrentFilename(): string {
74-
return this.#currentFilename;
74+
return this.currentFilename;
7575
}
7676

7777
getLine(): number {
78-
return this.#lineCounter;
78+
return this.lineCounter;
7979
}
8080

8181
resetLine(): void {
82-
this.#lineCounter = 0;
82+
this.lineCounter = 0;
8383
}
8484
}
8585

0 commit comments

Comments
 (0)