data-finder-ts 0.1.0 → 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 (64) hide show
  1. package/README.md +17 -55
  2. package/dist/chunk-4JVW4TAB.js +180 -0
  3. package/dist/chunk-4JVW4TAB.js.map +1 -0
  4. package/dist/chunk-4LNZV6EN.js +500 -0
  5. package/dist/chunk-4LNZV6EN.js.map +1 -0
  6. package/dist/chunk-FJYA2ASS.js +136 -0
  7. package/dist/chunk-FJYA2ASS.js.map +1 -0
  8. package/dist/chunk-FLEIUDZZ.js +14 -0
  9. package/dist/chunk-FLEIUDZZ.js.map +1 -0
  10. package/dist/chunk-MH4XMYVB.js +696 -0
  11. package/dist/chunk-MH4XMYVB.js.map +1 -0
  12. package/dist/chunk-PMPLCEWQ.js +90 -0
  13. package/dist/chunk-PMPLCEWQ.js.map +1 -0
  14. package/dist/chunk-UYMOE6YU.js +509 -0
  15. package/dist/chunk-UYMOE6YU.js.map +1 -0
  16. package/dist/datafinder/attribute.cjs +320 -0
  17. package/dist/datafinder/attribute.cjs.map +1 -0
  18. package/dist/datafinder/attribute.d.cts +86 -0
  19. package/dist/datafinder/attribute.d.ts +86 -0
  20. package/dist/datafinder/attribute.js +8 -0
  21. package/dist/datafinder/attribute.js.map +1 -0
  22. package/dist/datafinder/finder-registry.cjs +39 -0
  23. package/dist/datafinder/finder-registry.cjs.map +1 -0
  24. package/dist/datafinder/finder-registry.d.cts +4 -0
  25. package/dist/datafinder/finder-registry.d.ts +4 -0
  26. package/dist/datafinder/finder-registry.js +9 -0
  27. package/dist/datafinder/finder-registry.js.map +1 -0
  28. package/dist/datafinder/runner.cjs +1595 -0
  29. package/dist/datafinder/runner.cjs.map +1 -0
  30. package/dist/datafinder/runner.d.cts +44 -0
  31. package/dist/datafinder/runner.d.ts +44 -0
  32. package/dist/datafinder/runner.js +26 -0
  33. package/dist/datafinder/runner.js.map +1 -0
  34. package/dist/datafinder/sql-generator.cjs +1467 -0
  35. package/dist/datafinder/sql-generator.cjs.map +1 -0
  36. package/dist/datafinder/sql-generator.d.cts +8 -0
  37. package/dist/datafinder/sql-generator.d.ts +8 -0
  38. package/dist/datafinder/sql-generator.js +15 -0
  39. package/dist/datafinder/sql-generator.js.map +1 -0
  40. package/dist/datafinder/typed-attributes.cjs +909 -0
  41. package/dist/datafinder/typed-attributes.cjs.map +1 -0
  42. package/dist/datafinder/typed-attributes.d.cts +242 -0
  43. package/dist/datafinder/typed-attributes.d.ts +242 -0
  44. package/dist/datafinder/typed-attributes.js +25 -0
  45. package/dist/datafinder/typed-attributes.js.map +1 -0
  46. package/dist/index.cjs +2153 -0
  47. package/dist/index.cjs.map +1 -0
  48. package/dist/index.d.cts +28 -0
  49. package/dist/index.d.ts +28 -0
  50. package/dist/index.js +199 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/model/milestoning.cjs +145 -0
  53. package/dist/model/milestoning.cjs.map +1 -0
  54. package/dist/model/milestoning.d.cts +40 -0
  55. package/dist/model/milestoning.d.ts +40 -0
  56. package/dist/model/milestoning.js +18 -0
  57. package/dist/model/milestoning.js.map +1 -0
  58. package/dist/model/relational.cjs +579 -0
  59. package/dist/model/relational.cjs.map +1 -0
  60. package/dist/model/relational.d.cts +291 -0
  61. package/dist/model/relational.d.ts +291 -0
  62. package/dist/model/relational.js +99 -0
  63. package/dist/model/relational.js.map +1 -0
  64. package/package.json +6 -4
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
- ```
@@ -0,0 +1,180 @@
1
+ import {
2
+ AggregateOperation,
3
+ Column,
4
+ ColumnWithJoin,
5
+ IsNotNullOperation,
6
+ IsNullOperation,
7
+ SortOperation,
8
+ WindowFunctionOperation,
9
+ WindowSpecification
10
+ } from "./chunk-UYMOE6YU.js";
11
+
12
+ // src/datafinder/attribute.ts
13
+ var Attribute = class _Attribute {
14
+ _displayName;
15
+ _column;
16
+ _owner;
17
+ _parent;
18
+ _embedded;
19
+ constructor(displayName, columnName, columnDbType, owner, parent, embedded) {
20
+ this._displayName = displayName;
21
+ this._column = new Column(columnName, columnDbType, owner);
22
+ this._owner = owner;
23
+ this._parent = parent;
24
+ this._embedded = embedded;
25
+ }
26
+ column() {
27
+ return this._column;
28
+ }
29
+ owner() {
30
+ return this._owner;
31
+ }
32
+ parent() {
33
+ return this._parent;
34
+ }
35
+ displayName() {
36
+ return this._displayName;
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
+ }
42
+ cwj() {
43
+ return new ColumnWithJoin(this._column, this._parent, this._embedded?.cwj());
44
+ }
45
+ /** Count the number of non-null values. Use in a projection with `groupByOp` to aggregate per group. */
46
+ count() {
47
+ return new AggregateOperation(
48
+ this.cwj(),
49
+ "COUNT" /* COUNT */,
50
+ this._displayName + " Count"
51
+ );
52
+ }
53
+ /** Sort this attribute in ascending order. Pass to `orderByOp` or a window function's `orderBy`. */
54
+ ascending() {
55
+ return new SortOperation(this.cwj(), "ASC" /* ASC */);
56
+ }
57
+ /** Sort this attribute in descending order. Pass to `orderByOp` or a window function's `orderBy`. */
58
+ descending() {
59
+ return new SortOperation(this.cwj(), "DESC" /* DESC */);
60
+ }
61
+ /** Filter to rows where this attribute has no value. */
62
+ isNull() {
63
+ return new IsNullOperation(this.cwj());
64
+ }
65
+ /** Filter to rows where this attribute has a value. */
66
+ isNotNull() {
67
+ return new IsNotNullOperation(this.cwj());
68
+ }
69
+ _windowSpec(partitionBy, orderBy) {
70
+ const pbArr = partitionBy === void 0 ? [] : Array.isArray(partitionBy) ? partitionBy : [partitionBy];
71
+ const pb = pbArr.map((x) => x instanceof _Attribute ? x.cwj() : x);
72
+ const ob = orderBy === void 0 ? [] : Array.isArray(orderBy) ? orderBy : [orderBy];
73
+ return new WindowSpecification(pb, ob);
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
+ */
90
+ rank(opts = {}) {
91
+ const { method = "min", pct = false, partitionBy, orderBy } = opts;
92
+ let func;
93
+ let name;
94
+ if (pct) {
95
+ if (method === "max") {
96
+ func = "CUME_DIST" /* CUME_DIST */;
97
+ name = "Cume Dist";
98
+ } else {
99
+ func = "PERCENT_RANK" /* PERCENT_RANK */;
100
+ name = "Percent Rank";
101
+ }
102
+ } else if (method === "first") {
103
+ func = "ROW_NUMBER" /* ROW_NUMBER */;
104
+ name = "Row Number";
105
+ } else if (method === "dense") {
106
+ func = "DENSE_RANK" /* DENSE_RANK */;
107
+ name = "Dense Rank";
108
+ } else {
109
+ func = "RANK" /* RANK */;
110
+ name = "Rank";
111
+ }
112
+ return new WindowFunctionOperation(null, func, name, void 0, [], this._windowSpec(partitionBy, orderBy));
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
+ */
119
+ qcut(buckets, opts = {}) {
120
+ return new WindowFunctionOperation(
121
+ null,
122
+ "NTILE" /* NTILE */,
123
+ "Quantile",
124
+ buckets,
125
+ [],
126
+ this._windowSpec(opts.partitionBy, opts.orderBy)
127
+ );
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
+ */
134
+ shift(periods = 1, opts = {}) {
135
+ const extraArgs = opts.fillValue !== void 0 ? [opts.fillValue] : [];
136
+ const func = periods >= 0 ? "LAG" /* LAG */ : "LEAD" /* LEAD */;
137
+ const label = periods >= 0 ? "Lag" : "Lead";
138
+ return new WindowFunctionOperation(
139
+ this.cwj(),
140
+ func,
141
+ label + " " + this._displayName,
142
+ Math.abs(periods),
143
+ extraArgs,
144
+ this._windowSpec(opts.partitionBy, opts.orderBy)
145
+ );
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
+ */
151
+ first(opts = {}) {
152
+ return new WindowFunctionOperation(
153
+ this.cwj(),
154
+ "FIRST_VALUE" /* FIRST_VALUE */,
155
+ "First " + this._displayName,
156
+ void 0,
157
+ [],
158
+ this._windowSpec(opts.partitionBy, opts.orderBy)
159
+ );
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
+ */
165
+ last(opts = {}) {
166
+ return new WindowFunctionOperation(
167
+ this.cwj(),
168
+ "LAST_VALUE" /* LAST_VALUE */,
169
+ "Last " + this._displayName,
170
+ void 0,
171
+ [],
172
+ this._windowSpec(opts.partitionBy, opts.orderBy)
173
+ );
174
+ }
175
+ };
176
+
177
+ export {
178
+ Attribute
179
+ };
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":[]}