data-finder-ts 0.1.3 → 0.1.4

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 (52) hide show
  1. package/README.md +17 -55
  2. package/dist/{chunk-V647VNMY.js → chunk-4JVW4TAB.js} +59 -14
  3. package/dist/chunk-4JVW4TAB.js.map +1 -0
  4. package/dist/{chunk-5K2Z3YDY.js → chunk-4LNZV6EN.js} +138 -31
  5. package/dist/chunk-4LNZV6EN.js.map +1 -0
  6. package/dist/{chunk-6QTCGJWX.js → chunk-FJYA2ASS.js} +46 -8
  7. package/dist/chunk-FJYA2ASS.js.map +1 -0
  8. package/dist/{chunk-DFYZVBII.js → chunk-MH4XMYVB.js} +186 -142
  9. package/dist/chunk-MH4XMYVB.js.map +1 -0
  10. package/dist/{chunk-EYXBYV2X.js → chunk-PMPLCEWQ.js} +2 -2
  11. package/dist/{chunk-3SSWFES4.js → chunk-UYMOE6YU.js} +64 -3
  12. package/dist/chunk-UYMOE6YU.js.map +1 -0
  13. package/dist/datafinder/attribute.cjs +64 -13
  14. package/dist/datafinder/attribute.cjs.map +1 -1
  15. package/dist/datafinder/attribute.d.cts +48 -7
  16. package/dist/datafinder/attribute.d.ts +48 -7
  17. package/dist/datafinder/attribute.js +2 -2
  18. package/dist/datafinder/runner.cjs +389 -140
  19. package/dist/datafinder/runner.cjs.map +1 -1
  20. package/dist/datafinder/runner.d.cts +14 -3
  21. package/dist/datafinder/runner.d.ts +14 -3
  22. package/dist/datafinder/runner.js +12 -6
  23. package/dist/datafinder/sql-generator.cjs +344 -138
  24. package/dist/datafinder/sql-generator.cjs.map +1 -1
  25. package/dist/datafinder/sql-generator.d.cts +4 -13
  26. package/dist/datafinder/sql-generator.d.ts +4 -13
  27. package/dist/datafinder/sql-generator.js +5 -7
  28. package/dist/datafinder/typed-attributes.cjs +199 -41
  29. package/dist/datafinder/typed-attributes.cjs.map +1 -1
  30. package/dist/datafinder/typed-attributes.d.cts +120 -13
  31. package/dist/datafinder/typed-attributes.d.ts +120 -13
  32. package/dist/datafinder/typed-attributes.js +3 -3
  33. package/dist/index.cjs +507 -176
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +16 -15
  36. package/dist/index.d.ts +16 -15
  37. package/dist/index.js +49 -9
  38. package/dist/index.js.map +1 -1
  39. package/dist/model/milestoning.cjs.map +1 -1
  40. package/dist/model/milestoning.js +2 -2
  41. package/dist/model/relational.cjs +66 -1
  42. package/dist/model/relational.cjs.map +1 -1
  43. package/dist/model/relational.d.cts +47 -2
  44. package/dist/model/relational.d.ts +47 -2
  45. package/dist/model/relational.js +9 -1
  46. package/package.json +4 -2
  47. package/dist/chunk-3SSWFES4.js.map +0 -1
  48. package/dist/chunk-5K2Z3YDY.js.map +0 -1
  49. package/dist/chunk-6QTCGJWX.js.map +0 -1
  50. package/dist/chunk-DFYZVBII.js.map +0 -1
  51. package/dist/chunk-V647VNMY.js.map +0 -1
  52. /package/dist/{chunk-EYXBYV2X.js.map → chunk-PMPLCEWQ.js.map} +0 -0
package/README.md CHANGED
@@ -1,69 +1,42 @@
1
1
  # data-finder-ts
2
2
 
3
- TypeScript port of [data-finder](https://github.com/jackie-h/data-finder), a model-driven SQL query builder with temporal (milestoned) data support. A Python generator reads the same mapping definitions used by the Python project and emits strongly-typed TypeScript finder classes.
3
+ TypeScript port of [data-finder](https://github.com/jackie-h/data-finder), a model-driven SQL query builder with temporal (milestoned) data support. A Python generator reads mapping definitions and emits strongly-typed TypeScript finder classes.
4
4
 
5
- ## What it does
5
+ For runtime usage and query API see **[FINDERS.md](FINDERS.md)**.
6
6
 
7
- You define a mapping between your domain model and relational tables (in Python or Markdown). The generator produces a typed `*Finder` class per entity. You use those classes to build and run SQL queries without writing SQL directly:
7
+ ## Requirements
8
8
 
9
- ```typescript
10
- const tf = new TradeFinder();
9
+ - Node ≥ 24
10
+ - [uv](https://docs.astral.sh/uv/) (Python package manager)
11
11
 
12
- // Filter, join, milestoning applied automatically
13
- const rows = await tf.findAll(
14
- null, // business date (null = not applicable)
15
- new Date(), // processing valid-at (temporal snapshot)
16
- [tf.symbol(), tf.price(), tf.account().name()],
17
- tf.symbol().eq('AAPL'),
18
- ).toRows();
19
- ```
20
-
21
- Temporal filtering (`in_z <= ? AND out_z > ?`) is injected automatically based on the milestoning type declared in the mapping.
22
-
23
- ## Setup
12
+ ## Install
24
13
 
25
14
  ```bash
26
15
  npm install
27
-
28
- # Install Python generator dependencies (system-wide, no virtualenv)
29
- pip3 install jinja2 markdown-it-py
16
+ uv sync # install Python generator dependencies
30
17
  ```
31
18
 
32
- Node ≥ 20 required.
33
-
34
19
  ## Generating finders
35
20
 
36
- Generated files are gitignored — regenerate them before running tests on a fresh clone.
21
+ Generated files are gitignored — regenerate before running tests on a fresh clone.
37
22
 
38
23
  ```bash
39
- # From programmatic mapping definitions (example/mappings.py)
40
- python3 example/generate.py # → example/generated/
41
-
42
- # From a markdown mapping file (finance_mapping.md in sibling data-finder repo)
43
- python3 example/generate_from_markdown.py # → tests/generated_markdown/
24
+ npm run generate # from programmatic mappings (example/mappings.py) → example/generated/
25
+ npm run generate:examples # from all datafinder_examples mappings → tests/generated_examples/
44
26
  ```
45
27
 
46
- The generators import directly from the sibling `../data-finder` Python project, which must be present.
28
+ The generator uses the [`data-finder`](https://pypi.org/project/data-finder/) PyPI package (installed via `uv sync`).
47
29
 
48
30
  ## Running tests
49
31
 
50
32
  ```bash
51
- npm test # all tests
52
- npx vitest run tests/duckdb.test.ts # single file
53
- npm run build # type-check only
33
+ npm test # all tests
34
+ npx vitest run tests/duckdb.test.ts # single file
35
+ npm run build # type-check only
36
+ npm run build:lib # build distributable (tsup)
54
37
  ```
55
38
 
56
- Tests use an in-memory DuckDB instance via `@duckdb/node-api`. `tests/duckdb.test.ts` loads CSV fixtures from `example/data/`; `tests/duckdb-markdown.test.ts` seeds data with INSERT statements.
57
-
58
- ## Milestoning types
59
-
60
- | Type | Mapping class | `findAll` args used |
61
- |---|---|---|
62
- | None | — | neither |
63
- | Processing temporal | `ProcessingDateMilestonesPropertyMapping` | `processingValidAt` |
64
- | Single business date | `SingleBusinessDateMilestonePropertyMapping` | `businessDate` |
65
- | Business date + processing | `BusinessDateAndProcessingMilestonePropertyMapping` | both |
66
- | Bi-temporal | `BiTemporalMilestonePropertyMapping` | both |
39
+ Tests use an in-memory DuckDB instance via `@duckdb/node-api`.
67
40
 
68
41
  ## Defining a mapping (Markdown)
69
42
 
@@ -98,20 +71,9 @@ Tests use an in-memory DuckDB instance via `@duckdb/node-api`. `tests/duckdb.tes
98
71
  Load and generate:
99
72
 
100
73
  ```python
101
- from mapping_markdown.markdown_mapping import load
74
+ from datafinder_examples.finance_mapping import load
102
75
  from ts_generator.generator import generate
103
76
 
104
77
  mapping = load('my_mapping.md')
105
78
  generate(mapping, 'output/')
106
79
  ```
107
-
108
- ## Reverse associations
109
-
110
- When a model association is declared (e.g. `Trade → Account`), the generator adds a `trades()` method on `AccountFinder` pointing back to `TradeRelatedFinder`. Because ESM circular imports can't be resolved with static `import`, finders self-register at module load via `registerRelatedFinderClass`. When using a reverse association in tests, import the source finder's module first:
111
-
112
- ```typescript
113
- await import('./generated/TradeFinder'); // ensures TradeRelatedFinder is registered
114
- const { AccountFinder } = await import('./generated/AccountFinder');
115
- const af = new AccountFinder();
116
- const rows = await af.findAll(null, null, [af.name(), af.trades().symbol()]).toRows();
117
- ```
@@ -7,19 +7,21 @@ import {
7
7
  SortOperation,
8
8
  WindowFunctionOperation,
9
9
  WindowSpecification
10
- } from "./chunk-3SSWFES4.js";
10
+ } from "./chunk-UYMOE6YU.js";
11
11
 
12
12
  // src/datafinder/attribute.ts
13
- var Attribute = class {
13
+ var Attribute = class _Attribute {
14
14
  _displayName;
15
15
  _column;
16
16
  _owner;
17
17
  _parent;
18
- constructor(displayName, columnName, columnDbType, owner, parent) {
18
+ _embedded;
19
+ constructor(displayName, columnName, columnDbType, owner, parent, embedded) {
19
20
  this._displayName = displayName;
20
21
  this._column = new Column(columnName, columnDbType, owner);
21
22
  this._owner = owner;
22
23
  this._parent = parent;
24
+ this._embedded = embedded;
23
25
  }
24
26
  column() {
25
27
  return this._column;
@@ -33,33 +35,58 @@ var Attribute = class {
33
35
  displayName() {
34
36
  return this._displayName;
35
37
  }
38
+ /** Alternate attribute on a flat column of the owning table, used in place of this one when its join is elided. */
39
+ embedded() {
40
+ return this._embedded;
41
+ }
36
42
  cwj() {
37
- return new ColumnWithJoin(this._column, this._parent);
43
+ return new ColumnWithJoin(this._column, this._parent, this._embedded?.cwj());
38
44
  }
45
+ /** Count the number of non-null values. Use in a projection with `groupByOp` to aggregate per group. */
39
46
  count() {
40
47
  return new AggregateOperation(
41
- new ColumnWithJoin(this._column, this._parent),
48
+ this.cwj(),
42
49
  "COUNT" /* COUNT */,
43
50
  this._displayName + " Count"
44
51
  );
45
52
  }
53
+ /** Sort this attribute in ascending order. Pass to `orderByOp` or a window function's `orderBy`. */
46
54
  ascending() {
47
- return new SortOperation(new ColumnWithJoin(this._column, this._parent), "ASC" /* ASC */);
55
+ return new SortOperation(this.cwj(), "ASC" /* ASC */);
48
56
  }
57
+ /** Sort this attribute in descending order. Pass to `orderByOp` or a window function's `orderBy`. */
49
58
  descending() {
50
- return new SortOperation(new ColumnWithJoin(this._column, this._parent), "DESC" /* DESC */);
59
+ return new SortOperation(this.cwj(), "DESC" /* DESC */);
51
60
  }
61
+ /** Filter to rows where this attribute has no value. */
52
62
  isNull() {
53
- return new IsNullOperation(new ColumnWithJoin(this._column, this._parent));
63
+ return new IsNullOperation(this.cwj());
54
64
  }
65
+ /** Filter to rows where this attribute has a value. */
55
66
  isNotNull() {
56
- return new IsNotNullOperation(new ColumnWithJoin(this._column, this._parent));
67
+ return new IsNotNullOperation(this.cwj());
57
68
  }
58
69
  _windowSpec(partitionBy, orderBy) {
59
- const pb = partitionBy === void 0 ? [] : Array.isArray(partitionBy) ? partitionBy : [partitionBy];
70
+ const pbArr = partitionBy === void 0 ? [] : Array.isArray(partitionBy) ? partitionBy : [partitionBy];
71
+ const pb = pbArr.map((x) => x instanceof _Attribute ? x.cwj() : x);
60
72
  const ob = orderBy === void 0 ? [] : Array.isArray(orderBy) ? orderBy : [orderBy];
61
73
  return new WindowSpecification(pb, ob);
62
74
  }
75
+ /**
76
+ * Assign a rank to each row within the ordered window, following pandas `Series.rank` conventions.
77
+ *
78
+ * `method` controls tie-breaking (default `'min'`):
79
+ * - `'min'` — tied rows share the lowest rank; the next rank skips.
80
+ * - `'dense'` — like `'min'` but ranks are contiguous (no gaps).
81
+ * - `'first'` — each row gets a unique sequential integer (no ties).
82
+ * - `'max'` — only meaningful with `pct: true` (see below).
83
+ *
84
+ * When `pct: true` the result is a relative rank between 0 and 1:
85
+ * - `method: 'min'` (default) → fraction of values strictly below this row (PERCENT_RANK).
86
+ * - `method: 'max'` → fraction of values at or below this row (cumulative distribution).
87
+ *
88
+ * `partitionBy` restarts the ranking within each group of distinct values.
89
+ */
63
90
  rank(opts = {}) {
64
91
  const { method = "min", pct = false, partitionBy, orderBy } = opts;
65
92
  let func;
@@ -84,6 +111,11 @@ var Attribute = class {
84
111
  }
85
112
  return new WindowFunctionOperation(null, func, name, void 0, [], this._windowSpec(partitionBy, orderBy));
86
113
  }
114
+ /**
115
+ * Split rows into `buckets` equal-sized quantile bins ordered by `orderBy`.
116
+ * Each row receives a bucket number from 1 (lowest) to `buckets` (highest).
117
+ * Named after `pandas.qcut`.
118
+ */
87
119
  qcut(buckets, opts = {}) {
88
120
  return new WindowFunctionOperation(
89
121
  null,
@@ -94,12 +126,17 @@ var Attribute = class {
94
126
  this._windowSpec(opts.partitionBy, opts.orderBy)
95
127
  );
96
128
  }
129
+ /**
130
+ * Return the value of this attribute from `periods` rows earlier (positive) or later (negative)
131
+ * within the ordered window, following `pandas.Series.shift`.
132
+ * Rows with no neighbour at the given offset return `null` (or `fillValue` if provided).
133
+ */
97
134
  shift(periods = 1, opts = {}) {
98
135
  const extraArgs = opts.fillValue !== void 0 ? [opts.fillValue] : [];
99
136
  const func = periods >= 0 ? "LAG" /* LAG */ : "LEAD" /* LEAD */;
100
137
  const label = periods >= 0 ? "Lag" : "Lead";
101
138
  return new WindowFunctionOperation(
102
- new ColumnWithJoin(this._column, this._parent),
139
+ this.cwj(),
103
140
  func,
104
141
  label + " " + this._displayName,
105
142
  Math.abs(periods),
@@ -107,9 +144,13 @@ var Attribute = class {
107
144
  this._windowSpec(opts.partitionBy, opts.orderBy)
108
145
  );
109
146
  }
147
+ /**
148
+ * Return the first value of this attribute in the window frame.
149
+ * Use `partitionBy` to restart the frame for each group.
150
+ */
110
151
  first(opts = {}) {
111
152
  return new WindowFunctionOperation(
112
- new ColumnWithJoin(this._column, this._parent),
153
+ this.cwj(),
113
154
  "FIRST_VALUE" /* FIRST_VALUE */,
114
155
  "First " + this._displayName,
115
156
  void 0,
@@ -117,9 +158,13 @@ var Attribute = class {
117
158
  this._windowSpec(opts.partitionBy, opts.orderBy)
118
159
  );
119
160
  }
161
+ /**
162
+ * Return the last value of this attribute in the window frame.
163
+ * Use `partitionBy` to restart the frame for each group.
164
+ */
120
165
  last(opts = {}) {
121
166
  return new WindowFunctionOperation(
122
- new ColumnWithJoin(this._column, this._parent),
167
+ this.cwj(),
123
168
  "LAST_VALUE" /* LAST_VALUE */,
124
169
  "Last " + this._displayName,
125
170
  void 0,
@@ -132,4 +177,4 @@ var Attribute = class {
132
177
  export {
133
178
  Attribute
134
179
  };
135
- //# sourceMappingURL=chunk-V647VNMY.js.map
180
+ //# sourceMappingURL=chunk-4JVW4TAB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/datafinder/attribute.ts"],"sourcesContent":["import {\n Column,\n AggregateOperation,\n AggregateOperator,\n ColumnWithJoin,\n SortOperation,\n SortDirection,\n IsNullOperation,\n IsNotNullOperation,\n JoinTreeNodeOperation,\n WindowFunction,\n WindowFunctionOperation,\n WindowSpecification,\n} from '../model/relational';\n\nexport abstract class Attribute {\n private readonly _displayName: string;\n private readonly _column: Column;\n private readonly _owner: string;\n private readonly _parent?: JoinTreeNodeOperation;\n private readonly _embedded?: Attribute;\n\n constructor(\n displayName: string,\n columnName: string,\n columnDbType: string,\n owner: string,\n parent?: JoinTreeNodeOperation,\n embedded?: Attribute,\n ) {\n this._displayName = displayName;\n this._column = new Column(columnName, columnDbType, owner);\n this._owner = owner;\n this._parent = parent;\n this._embedded = embedded;\n }\n\n column(): Column { return this._column; }\n owner(): string { return this._owner; }\n parent(): JoinTreeNodeOperation | undefined { return this._parent; }\n displayName(): string { return this._displayName; }\n\n /** Alternate attribute on a flat column of the owning table, used in place of this one when its join is elided. */\n embedded(): Attribute | undefined { return this._embedded; }\n\n cwj(): ColumnWithJoin {\n return new ColumnWithJoin(this._column, this._parent, this._embedded?.cwj());\n }\n\n /** Count the number of non-null values. Use in a projection with `groupByOp` to aggregate per group. */\n count(): AggregateOperation {\n return new AggregateOperation(\n this.cwj(),\n AggregateOperator.COUNT,\n this._displayName + ' Count',\n );\n }\n\n /** Sort this attribute in ascending order. Pass to `orderByOp` or a window function's `orderBy`. */\n ascending(): SortOperation {\n return new SortOperation(this.cwj(), SortDirection.ASC);\n }\n\n /** Sort this attribute in descending order. Pass to `orderByOp` or a window function's `orderBy`. */\n descending(): SortOperation {\n return new SortOperation(this.cwj(), SortDirection.DESC);\n }\n\n /** Filter to rows where this attribute has no value. */\n isNull(): IsNullOperation {\n return new IsNullOperation(this.cwj());\n }\n\n /** Filter to rows where this attribute has a value. */\n isNotNull(): IsNotNullOperation {\n return new IsNotNullOperation(this.cwj());\n }\n\n private _windowSpec(\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[],\n orderBy?: SortOperation | SortOperation[],\n ): WindowSpecification {\n const pbArr = partitionBy === undefined ? [] : Array.isArray(partitionBy) ? partitionBy : [partitionBy];\n const pb = pbArr.map(x => x instanceof Attribute ? x.cwj() : x);\n const ob = orderBy === undefined ? [] : Array.isArray(orderBy) ? orderBy : [orderBy];\n return new WindowSpecification(pb, ob);\n }\n\n /**\n * Assign a rank to each row within the ordered window, following pandas `Series.rank` conventions.\n *\n * `method` controls tie-breaking (default `'min'`):\n * - `'min'` — tied rows share the lowest rank; the next rank skips.\n * - `'dense'` — like `'min'` but ranks are contiguous (no gaps).\n * - `'first'` — each row gets a unique sequential integer (no ties).\n * - `'max'` — only meaningful with `pct: true` (see below).\n *\n * When `pct: true` the result is a relative rank between 0 and 1:\n * - `method: 'min'` (default) → fraction of values strictly below this row (PERCENT_RANK).\n * - `method: 'max'` → fraction of values at or below this row (cumulative distribution).\n *\n * `partitionBy` restarts the ranking within each group of distinct values.\n */\n rank(opts: {\n method?: 'min' | 'dense' | 'first' | 'max';\n pct?: boolean;\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n const { method = 'min', pct = false, partitionBy, orderBy } = opts;\n let func: WindowFunction;\n let name: string;\n if (pct) {\n if (method === 'max') { func = WindowFunction.CUME_DIST; name = 'Cume Dist'; }\n else { func = WindowFunction.PERCENT_RANK; name = 'Percent Rank'; }\n } else if (method === 'first') { func = WindowFunction.ROW_NUMBER; name = 'Row Number'; }\n else if (method === 'dense') { func = WindowFunction.DENSE_RANK; name = 'Dense Rank'; }\n else { func = WindowFunction.RANK; name = 'Rank'; }\n return new WindowFunctionOperation(null, func, name, undefined, [], this._windowSpec(partitionBy, orderBy));\n }\n\n /**\n * Split rows into `buckets` equal-sized quantile bins ordered by `orderBy`.\n * Each row receives a bucket number from 1 (lowest) to `buckets` (highest).\n * Named after `pandas.qcut`.\n */\n qcut(buckets: number, opts: {\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n return new WindowFunctionOperation(null, WindowFunction.NTILE, 'Quantile', buckets, [],\n this._windowSpec(opts.partitionBy, opts.orderBy));\n }\n\n /**\n * Return the value of this attribute from `periods` rows earlier (positive) or later (negative)\n * within the ordered window, following `pandas.Series.shift`.\n * Rows with no neighbour at the given offset return `null` (or `fillValue` if provided).\n */\n shift(periods = 1, opts: {\n fillValue?: string | number;\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n const extraArgs: (string | number)[] = opts.fillValue !== undefined ? [opts.fillValue] : [];\n const func = periods >= 0 ? WindowFunction.LAG : WindowFunction.LEAD;\n const label = periods >= 0 ? 'Lag' : 'Lead';\n return new WindowFunctionOperation(\n this.cwj(),\n func,\n label + ' ' + this._displayName,\n Math.abs(periods),\n extraArgs,\n this._windowSpec(opts.partitionBy, opts.orderBy),\n );\n }\n\n /**\n * Return the first value of this attribute in the window frame.\n * Use `partitionBy` to restart the frame for each group.\n */\n first(opts: {\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n return new WindowFunctionOperation(\n this.cwj(),\n WindowFunction.FIRST_VALUE,\n 'First ' + this._displayName,\n undefined, [],\n this._windowSpec(opts.partitionBy, opts.orderBy),\n );\n }\n\n /**\n * Return the last value of this attribute in the window frame.\n * Use `partitionBy` to restart the frame for each group.\n */\n last(opts: {\n partitionBy?: Attribute | ColumnWithJoin | (Attribute | ColumnWithJoin)[];\n orderBy?: SortOperation | SortOperation[];\n } = {}): WindowFunctionOperation {\n return new WindowFunctionOperation(\n this.cwj(),\n WindowFunction.LAST_VALUE,\n 'Last ' + this._displayName,\n undefined, [],\n this._windowSpec(opts.partitionBy, opts.orderBy),\n );\n }\n}\n"],"mappings":"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","names":[]}