semanticdb-v2 2.0.0-alpha.4 → 2.0.0-alpha.6

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 (104) hide show
  1. package/README.md +190 -17
  2. package/dist/cache/redis-query-cache.esm.js +1 -1
  3. package/dist/cache/redis-query-cache.js +1 -1
  4. package/dist/cache/types.d.ts +2 -0
  5. package/dist/compiler/compile-cte.d.ts +1 -1
  6. package/dist/errors.d.ts +2 -1
  7. package/dist/executor/components.d.ts +8 -3
  8. package/dist/executor/connection-registry.d.ts +2 -2
  9. package/dist/executor/execute-query.d.ts +6 -1
  10. package/dist/executor/federated-execution.d.ts +37 -0
  11. package/dist/executor/fetch-entities.d.ts +5 -1
  12. package/dist/executor/result-hydrator/types.d.ts +2 -0
  13. package/dist/executor/total.d.ts +11 -0
  14. package/dist/executor/types.d.ts +41 -6
  15. package/dist/i18n/index.d.ts +4 -0
  16. package/dist/i18n/locales/en-US.d.ts +2 -0
  17. package/dist/i18n/locales/zh-CN.d.ts +2 -0
  18. package/dist/i18n/registry.d.ts +13 -0
  19. package/dist/i18n/types.d.ts +9 -0
  20. package/dist/index.d.ts +11 -8
  21. package/dist/logicform-types.d.ts +44 -12
  22. package/dist/normalizer/core.d.ts +5 -0
  23. package/dist/normalizer/default-pred-name.d.ts +2 -0
  24. package/dist/normalizer/index.d.ts +1 -0
  25. package/dist/normalizer/pred-expression.d.ts +6 -0
  26. package/dist/normalizer/property-resolve.d.ts +1 -0
  27. package/dist/operators/builtins/period-name.d.ts +2 -1
  28. package/dist/operators/builtins/query-expression.d.ts +3 -0
  29. package/dist/operators/index.d.ts +2 -1
  30. package/dist/operators/registry.d.ts +1 -1
  31. package/dist/operators/resolve.d.ts +3 -0
  32. package/dist/operators/types.d.ts +55 -15
  33. package/dist/physical/clickhouse-provider.d.ts +14 -2
  34. package/dist/physical/connection.d.ts +57 -15
  35. package/dist/physical/dameng-provider.d.ts +61 -0
  36. package/dist/physical/databricks-provider.d.ts +83 -0
  37. package/dist/physical/display-query.d.ts +3 -0
  38. package/dist/physical/doris-provider.d.ts +2 -1
  39. package/dist/physical/duckdb-coordinator-provider.d.ts +37 -0
  40. package/dist/physical/gbase-provider.d.ts +11 -0
  41. package/dist/physical/index.d.ts +21 -9
  42. package/dist/physical/load-driver.d.ts +1 -0
  43. package/dist/physical/maxcompute-provider.d.ts +37 -0
  44. package/dist/physical/mysql-protocol-provider.d.ts +21 -6
  45. package/dist/physical/mysql-provider.d.ts +2 -1
  46. package/dist/physical/oracle-provider.d.ts +15 -4
  47. package/dist/physical/postgresql-provider.d.ts +18 -4
  48. package/dist/physical/provider.d.ts +16 -1
  49. package/dist/physical/snowflake-provider.d.ts +15 -3
  50. package/dist/physical/sqlserver-provider.d.ts +65 -0
  51. package/dist/physical/starrocks-provider.d.ts +2 -1
  52. package/dist/planner/build-lineage.d.ts +1 -1
  53. package/dist/planner/create-logical-plan.d.ts +7 -0
  54. package/dist/planner/index.d.ts +4 -2
  55. package/dist/planner/mapping-granularity.d.ts +7 -0
  56. package/dist/planner/rewrite-nested-components.d.ts +8 -0
  57. package/dist/planner/select-schema-mapping.d.ts +16 -2
  58. package/dist/planner/semantics/index.d.ts +0 -3
  59. package/dist/planner/semantics/periods.d.ts +13 -0
  60. package/dist/planner/semantics/registry-defaults.d.ts +2 -1
  61. package/dist/planner/types.d.ts +80 -12
  62. package/dist/plugins/hierarchy/index.d.ts +1 -1
  63. package/dist/plugins/hierarchy/mapping-granularity.d.ts +4 -0
  64. package/dist/plugins/hierarchy/types.d.ts +17 -0
  65. package/dist/plugins/index.d.ts +10 -1
  66. package/dist/{planner/semantics → plugins}/observation/classification.d.ts +1 -1
  67. package/dist/plugins/observation/index.d.ts +17 -0
  68. package/dist/{planner/semantics → plugins}/observation/operator-policy.d.ts +2 -3
  69. package/dist/plugins/observation/periods.d.ts +18 -0
  70. package/dist/{planner/semantics → plugins}/observation/rule.d.ts +1 -1
  71. package/dist/{planner/semantics → plugins}/observation/types.d.ts +1 -1
  72. package/dist/plugins/property-hierarchy/index.d.ts +11 -0
  73. package/dist/plugins/recursive/index.d.ts +26 -0
  74. package/dist/plugins/temporal/index.d.ts +13 -0
  75. package/dist/plugins/types.d.ts +165 -1
  76. package/dist/plugins/validity/classification.d.ts +5 -0
  77. package/dist/plugins/validity/index.d.ts +15 -0
  78. package/dist/plugins/validity/periods.d.ts +15 -0
  79. package/dist/plugins/validity/query-policy.d.ts +3 -0
  80. package/dist/plugins/validity/rule.d.ts +2 -0
  81. package/dist/plugins/validity/semantic-types.d.ts +3 -0
  82. package/dist/schema/dependent-projection.d.ts +8 -0
  83. package/dist/schema/index.d.ts +1 -0
  84. package/dist/schema-mappings/freshness.d.ts +1 -1
  85. package/dist/schema-mappings/granularity.d.ts +5 -0
  86. package/dist/schema-mappings/index.d.ts +2 -1
  87. package/dist/schema-mappings/registry.d.ts +5 -3
  88. package/dist/schema-mappings/types.d.ts +43 -0
  89. package/dist/schema-mappings/yiask.d.ts +6 -2
  90. package/dist/semantic-types/index.d.ts +5 -3
  91. package/dist/semantic-types/registry.d.ts +2 -0
  92. package/dist/semantic-types/resolve-schema.d.ts +1 -1
  93. package/dist/semantic-types/types.d.ts +6 -13
  94. package/dist/semantic-types/validate-resolved-schema.d.ts +2 -5
  95. package/dist/semanticdb.esm.js +4 -3
  96. package/dist/semanticdb.js +4 -3
  97. package/package.json +47 -5
  98. package/dist/planner/semantics/lifecycle/classification.d.ts +0 -7
  99. package/dist/planner/semantics/lifecycle/index.d.ts +0 -3
  100. package/dist/planner/semantics/lifecycle/query-policy.d.ts +0 -3
  101. package/dist/planner/semantics/lifecycle/rule.d.ts +0 -2
  102. package/dist/planner/semantics/observation/index.d.ts +0 -4
  103. package/dist/semantic-types/builtins/end-date.d.ts +0 -2
  104. package/dist/semantic-types/builtins/start-date.d.ts +0 -2
package/README.md CHANGED
@@ -1,9 +1,23 @@
1
1
  # SemanticDB
2
2
 
3
- SemanticDB translates its own LogicForm into SQL and executes it on a single core compute layer.
3
+ SemanticDB translates its own LogicForm into SQL and executes it on one or more core compute layers.
4
4
  Callers provide schemas, schema mappings, operators, and database connection information.
5
5
  SemanticDB creates the matching provider and owns its connection lifecycle.
6
6
 
7
+ Install SemanticDB together with only the database driver used by the application. Database
8
+ drivers are optional peer dependencies and are loaded on the first execution for that connection;
9
+ Redis cache support is included by the main package.
10
+
11
+ ```bash
12
+ npm install semanticdb-v2 mysql2
13
+ ```
14
+
15
+ Use `@clickhouse/client`, `@databricks/sql`, `dmdb`, `mysql2` (MySQL, Doris, StarRocks, and GBase),
16
+ `mssql`, `pg`, `snowflake-sdk`, or `oracledb` for the corresponding connection kind. MaxCompute
17
+ uses a caller-supplied parameterized execution client. If a configured driver is missing, execution
18
+ fails with an installation command for that driver. SemanticDB caches its internally created
19
+ providers across executions; call `await Logicform.close()` during application shutdown.
20
+
7
21
  ## Public API
8
22
 
9
23
  ```ts
@@ -34,8 +48,9 @@ const response = await Logicform.execute(
34
48
  connections: [{
35
49
  id: 'analytics',
36
50
  kind: 'clickhouse',
37
- url: 'http://localhost:8123',
38
- username: 'default',
51
+ host: 'localhost',
52
+ port: 8123,
53
+ user: 'default',
39
54
  password: process.env.CLICKHOUSE_PASSWORD,
40
55
  database: 'analytics',
41
56
  }],
@@ -49,6 +64,11 @@ const response = await Logicform.execute(
49
64
  );
50
65
  ```
51
66
 
67
+ A successful response always includes `total`, the number of result rows before global
68
+ `limit`/`page` pagination. For grouped LogicForms this is the number of groups after `having` and
69
+ `limitBy`. Paginated physical queries include a separate `sqls` entry with `type: 'total'` for the
70
+ count query; unpaginated and executor-merged results derive the value without another SQL query.
71
+
52
72
  Every executable LogicForm, including LogicForm objects used as subqueries, must declare `version`. This
53
73
  SDK accepts semantic versions greater than or equal to `2.0`; missing, malformed, or older versions
54
74
  fail with `UNSUPPORTED_LOGICFORM_VERSION` before Schema resolution or database access. A relation
@@ -56,19 +76,28 @@ query shaped as `{ schema, query }` is part of its parent LogicForm and inherits
56
76
 
57
77
  PredItems without an explicit `name` receive a human-readable name during normalization. Generated
58
78
  names default to Chinese; pass `locale: 'en-US'` in `ExecuteOptions` for the built-in English
59
- names. Every operator owns its localized default names and receives the requested locale directly:
79
+ names. SemanticDB ships `zh-CN` and `en-US` locale files backed by i18next. Applications can add or
80
+ override locale files through `ExecuteOptions.locales`:
60
81
 
61
82
  ```ts
62
- operators.register({
63
- name: '$weightedAverage',
64
- defaultName: ({ operandName, locale }) => locale.toLowerCase().startsWith('en')
65
- ? `Weighted average of ${operandName}`
66
- : `${operandName}加权平均值`,
67
- kind: 'aggregate',
68
- toSQL: ({ column }) => `weighted_avg(${column})`,
83
+ await Logicform.execute(logicform, {
84
+ locale: 'ja-JP',
85
+ locales: {
86
+ locale: 'ja-JP',
87
+ messages: {
88
+ 'operator.$sum': '{operandName}の合計',
89
+ 'component.numerator': '{outputName} 分子',
90
+ 'component.denominator': '{outputName} 分母',
91
+ },
92
+ },
93
+ // schemas, schemaMappings, connections, ...
69
94
  });
70
95
  ```
71
96
 
97
+ Locale lookup uses the exact locale first, then the matching language, and finally `zh-CN`.
98
+ Multiple locale files can be supplied as an array. A custom operator still owns its fallback name
99
+ through `defaultName()` and can call the context's `t()` helper when it defines translation keys.
100
+
72
101
  The same locale applies to `preds`, `groupby`, nested expressions, custom-function subqueries,
73
102
  and entity-population queries. A PredItem's normalized `query` is included in its
74
103
  generated name, for example `门店为上海的销售额合计`. Operator-specific `args` also participate
@@ -90,12 +119,18 @@ Plugins may own Schema metadata, legacy conversion, `entity_id` selection, and O
90
119
  remaining scoped to one execution. See [Register an external plugin](docs/register-plugin.md).
91
120
 
92
121
  Parent-child Schemas may declare `parentProperty`. `$recursive` filters a property to a root node and
93
- all descendants; object properties infer the tree Schema from `ref`. MySQL, Doris, StarRocks, and
94
- PostgreSQL compile this as a parameterized recursive CTE.
122
+ all descendants; object properties infer the tree Schema from `ref`. MySQL, Doris, StarRocks,
123
+ PostgreSQL, ClickHouse 24.4+, and Databricks compile this as a parameterized recursive CTE.
95
124
 
96
125
  Top-level projections may set `schema` to read metrics from another registered Schema. SemanticDB
97
126
  splits those projections into independent provider queries, aligns shared query/groupby dimensions,
98
127
  and merges rows by the group keys before applying `having`, `sort`, `limitBy`, and pagination.
128
+ When the selected Schema mappings use separate physical connections, SemanticDB executes every
129
+ single-connection fragment with its native provider and registers the aggregated fragment results
130
+ in an in-process DuckDB coordinator. DuckDB applies the final full-outer group merge, cross-fragment
131
+ conditions, ordering, totals, and pagination. It is embedded in the Node.js process and does not
132
+ require a service or listening port. `federation.maxMaterializedRows` limits the total number of
133
+ fragment rows admitted to the coordinator and defaults to 100,000.
99
134
 
100
135
  The progress stream emits `started`, `validated`, `normalized`, `planned`, `lineage`,
101
136
  `compiled`, `executing`, `hydrating`, `result`, and `completed`. Every event includes a cumulative
@@ -108,7 +143,9 @@ SQL string for diagnostics:
108
143
 
109
144
  ```ts
110
145
  interface ExecutedSql {
111
- type: 'semantic-resolution' | 'computation';
146
+ type: 'semantic-resolution' | 'computation' | 'total';
147
+ connectionId?: string;
148
+ provider?: CoreComputeProvider['kind'];
112
149
  explain: string;
113
150
  logicform: LogicformType;
114
151
  sql: string;
@@ -121,6 +158,10 @@ interface ExecutedSql {
121
158
  time-watermark resolution and object entity population. `computation` identifies SQL that calculates
122
159
  the requested LogicForm result. `sqls` includes every physical SQL involved in the execution; an
123
160
  entry remains visible when its result is served by the query cache.
161
+ Coordinator SQL uses the reserved connection id `__semanticdb_coordinator__` and provider
162
+ `duckdb`; other entries identify the physical connection that executed them. Successful results
163
+ also expose `providers`, the physical connection/provider pairs that contributed rows. The legacy
164
+ singular `provider` field is `duckdb` for a federated result.
124
165
 
125
166
  Providers execute only parameterized `sql + parameters`; SemanticDB never executes `displaySql`.
126
167
  Because `displaySql` contains literal values and may expose sensitive data, use it only in trusted
@@ -181,13 +222,27 @@ Composite metrics are regular `OperatorDefinition` values with `kind: 'component
181
222
  them in the same `OperatorRegistry` as scalar and aggregate operators. Operator names are free-form;
182
223
  the `$` prefix is a convention used by built-in operators, not a requirement.
183
224
 
225
+ Every composite component is exposed in result rows by default. Its `name` is its final result-column
226
+ name and may contain Chinese characters or spaces. Built-ins derive child names from the parent output,
227
+ for example `总销售额占比分子` and `总销售额占比分母`. Operators do not return `outputName` or an
228
+ exposure flag; the executor owns the parent output binding. Components are joined on groupby output
229
+ names shared by both sides unless an exceptional custom operator explicitly supplies `joinKeys`.
230
+
184
231
  See [Register an operator](docs/register-operator.md) for the component contract.
185
232
 
186
233
  The root entry point accepts a `connections` array of `DatabaseConnection` items. Every item has a unique `id`; set `kind` to `mysql`,
187
- `doris`, `starrocks`, `clickhouse`, `snowflake`, `oracle`, or `postgresql`, then add that driver's
234
+ `doris`, `starrocks`, `gbase`, `maxcompute`, `clickhouse`, `databricks`, `dameng`, `snowflake`,
235
+ `sqlserver`, `oracle`, or `postgresql`, then add that driver's
188
236
  native connection fields directly on the item. Each Schema Mapping selects an item through
189
237
  `connectionId`. SemanticDB lazily creates and reuses the matching internal provider. Call
190
238
  `await Logicform.close()` during application shutdown to close every connection pool and session.
239
+ One LogicForm may reference mappings on multiple connections. This is one logical execution, not
240
+ one cross-database SQL statement: physical fragments run independently and their aggregate results
241
+ are combined by the embedded coordinator. A relation join whose two sides themselves live on
242
+ different connections is not yet materialized automatically. When the related Schema has replicas
243
+ on multiple connections, the Planner selects the mapping colocated with the main mapping and emits
244
+ the `LEFT JOIN` on that connection. It fails explicitly only when no target mapping exists on the
245
+ main connection.
191
246
 
192
247
  MySQL-compatible providers default to 30 connections, at most 10 idle connections, a 60-second
193
248
  idle timeout, and TCP keepalive. Native `mysql2` pool options on the connection item override these
@@ -223,6 +278,66 @@ pagination, date, aggregate, string, regex, parameter, and display-SQL behavior
223
278
  MySQL-compatible SQL helper. Database-specific differences should be implemented by overriding the
224
279
  corresponding internal dialect capability.
225
280
 
281
+ ### GBase connection
282
+
283
+ GBase 8a uses its MySQL-compatible protocol through `mysql2`. It defaults to port `5258`; all
284
+ ordinary `mysql2` connection and pool fields remain available:
285
+
286
+ ```ts
287
+ const gbase = {
288
+ id: 'gbase-main',
289
+ kind: 'gbase',
290
+ host: process.env.GBASE_HOST,
291
+ user: process.env.GBASE_USER,
292
+ password: process.env.GBASE_PASSWORD,
293
+ database: process.env.GBASE_DATABASE,
294
+ } as const;
295
+ ```
296
+
297
+ ### MaxCompute connection
298
+
299
+ MaxCompute requires a caller-owned client that preserves the Provider's parameterized execution
300
+ contract. The client receives SQL and parameters separately and owns the actual MaxCompute/JDBC
301
+ transport:
302
+
303
+ ```ts
304
+ const maxcompute = {
305
+ id: 'maxcompute-main',
306
+ kind: 'maxcompute',
307
+ client: {
308
+ execute: ({ sql, parameters, signal }) => maxComputeGateway.execute({
309
+ sql, parameters, signal,
310
+ }),
311
+ close: () => maxComputeGateway.close(),
312
+ },
313
+ } as const;
314
+ ```
315
+
316
+ The legacy UDBC `Statement` endpoint accepts only a complete SQL string, so SemanticDB does not
317
+ use it or interpolate values locally. A gateway implementation must bind `parameters` before this
318
+ connection can execute queries.
319
+
320
+ ### SQL Server connection
321
+
322
+ SQL Server uses `mssql`, bracket-quoted identifiers, and named `@p0`, `@p1` parameters:
323
+
324
+ ```ts
325
+ const sqlserver = {
326
+ id: 'sqlserver-main',
327
+ kind: 'sqlserver',
328
+ host: process.env.SQLSERVER_HOST,
329
+ port: 1433,
330
+ user: process.env.SQLSERVER_USER,
331
+ password: process.env.SQLSERVER_PASSWORD,
332
+ database: process.env.SQLSERVER_DATABASE,
333
+ options: { encrypt: true },
334
+ } as const;
335
+ ```
336
+
337
+ Pagination uses `OFFSET ... FETCH`; SemanticDB supplies a neutral top-level ordering when the
338
+ LogicForm has no sort. JSON array relations use `OPENJSON`. SQL Server has no native regular
339
+ expression predicate, so `$regex` is rejected explicitly.
340
+
226
341
  ### Snowflake connection
227
342
 
228
343
  Snowflake connection fields are passed to the official `snowflake-sdk`:
@@ -245,6 +360,52 @@ await Logicform.execute(logicform, {
245
360
  });
246
361
  ```
247
362
 
363
+ ### Databricks connection
364
+
365
+ Databricks uses `@databricks/sql` and supports a personal access token or OAuth M2M. `catalog` and
366
+ `schema` select the initial SQL Warehouse session namespace (`database` is accepted as a legacy
367
+ alias for `schema`):
368
+
369
+ ```ts
370
+ const databricks = {
371
+ id: 'databricks-main',
372
+ kind: 'databricks',
373
+ host: process.env.DATABRICKS_SERVER_HOSTNAME,
374
+ path: process.env.DATABRICKS_HTTP_PATH,
375
+ authType: 'databricks-oauth',
376
+ oauthClientId: process.env.DATABRICKS_CLIENT_ID,
377
+ oauthClientSecret: process.env.DATABRICKS_CLIENT_SECRET,
378
+ catalog: 'main',
379
+ schema: 'analytics',
380
+ } as const;
381
+ ```
382
+
383
+ Use `authType: 'access-token'` with `token` for PAT authentication. Values are sent through the
384
+ driver's ordinal parameter binding and are not interpolated into executable SQL. `authType` is
385
+ required: PAT and OAuth fields are mutually exclusive, and authentication is never inferred from
386
+ the presence of credentials.
387
+
388
+ ### Dameng connection
389
+
390
+ Dameng uses the official `dmdb` pool and the `dameng` connection kind:
391
+
392
+ ```ts
393
+ const dameng = {
394
+ id: 'dameng-main',
395
+ kind: 'dameng',
396
+ host: process.env.DAMENG_HOST,
397
+ port: 5236,
398
+ user: process.env.DAMENG_USER,
399
+ password: process.env.DAMENG_PASSWORD,
400
+ schema: process.env.DAMENG_SCHEMA,
401
+ } as const;
402
+ ```
403
+
404
+ The provider uses positional binds and object-format result rows. Recursive hierarchy queries and
405
+ JSON-array relations are rejected until their behavior can be verified against supported DM server
406
+ versions; scalar queries, grouping, date operations, regex, windows, joins, and pagination compile
407
+ with the DM/Oracle-style dialect.
408
+
248
409
  ### Oracle connection
249
410
 
250
411
  Oracle uses the official `oracledb` driver in Thin mode and does not require Oracle Client
@@ -315,9 +476,20 @@ comments, or parameter placeholders. Property UDFs do not support the legacy `fu
315
476
 
316
477
  All compiled provider queries, including the internal time-watermark query, can share one
317
478
  caller-owned query cache. Redis is published from `semanticdb-v2/cache/redis`; cache identity
318
- includes namespace, provider, SQL, and parameters. Physical expiration belongs to each Schema
479
+ includes namespace, connection id, provider, SQL, and parameters. Physical expiration belongs to each Schema
319
480
  mapping's `freshness` policy. See [Query cache and Redis](docs/query-cache.md).
320
481
 
482
+ Schema mappings can also expose same-connection entity feature tables as lazy physical
483
+ enrichments. A mapped feature property adds a `LEFT JOIN` only when referenced, and a point-in-time
484
+ feature can be assigned to the containing valid-time entity row. See
485
+ [Schema Mapping feature enrichments](docs/schema-mapping-enrichments.md).
486
+
487
+ For aggregate sources, set `granularity` on mapped date or hierarchy Properties.
488
+ Other available dimensions are inferred from their Property mappings, so there is no
489
+ separate Schema Mapping `dimensions` field. The planner selects a source that covers
490
+ the requested properties and physical time or hierarchy grain. See
491
+ [Schema Mapping physical granularity](docs/schema-mapping-granularity.md).
492
+
321
493
  When Redis is shared with other applications, pass an application-specific key prefix:
322
494
 
323
495
  ```ts
@@ -439,7 +611,8 @@ npm run debug
439
611
 
440
612
  ## Current scope
441
613
 
442
- - ClickHouse, MySQL, Oracle, and Snowflake core compute providers
614
+ - ClickHouse, Databricks, Dameng, Doris, GBase, MaxCompute, MySQL, Oracle, PostgreSQL, Snowflake,
615
+ SQL Server, and StarRocks core compute providers
443
616
  - Projections, conditional aggregates, and `$sum`, `$count`, `$countDistinct`, `$avg`, `$min`, `$max`
444
617
  - Structured arithmetic, share-of-total, period comparison, period-boundary, date-bucket,
445
618
  cumulative period, trusted SQL expression, string, case, and window operators
@@ -1 +1 @@
1
- import{createClient as b}from"redis";import{createHash as g}from"node:crypto";const a=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?e.map(a):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,a(r)])):e,d=e=>g("sha256").update(JSON.stringify(a(e))).digest("hex"),A=e=>d({namespace:e.namespace,provider:e.provider,sql:e.sql,parameters:e.parameters}),S=(e,t)=>d({namespace:e,source:t}),o=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?{type:"array",value:e.map(o)}:e&&typeof e=="object"?{type:"object",value:Object.entries(e).map(([t,r])=>[t,o(r)])}:e,y=e=>{if(e===null||typeof e!="object")return e;switch(e.type){case"undefined":return;case"bigint":return BigInt(e.value);case"number":return Number(e.value);case"date":return new Date(e.value);case"array":return e.value.map(y);case"object":return Object.fromEntries(e.value.map(([t,r])=>[t,y(r)]))}},w=e=>JSON.stringify(o(e)),x=e=>{const t=y(JSON.parse(e));if(!(!Array.isArray(t)||t.some(r=>!r||typeof r!="object"||Array.isArray(r))))return t},l=Number.MAX_SAFE_INTEGER;class p{#e;#t;#r;constructor(t,r={}){this.#e=t,this.#t=r.keyPrefix??"semanticdb:cache",this.#r=r.ownsClient??!1}#n(t){return`${this.#t}:query:${A(t)}`}#i(t,r){return`${this.#t}:source:${S(t,r)}`}async get(t){const r=await this.#e.get(this.#n(t));if(r!==null)return x(r)}async set(t,r,i){const n=this.#n(t),f=w(r),c=i.expiresAt?.getTime(),m=c??l,s=this.#e.multi();c===void 0?s.set(n,f):s.set(n,f,{PXAT:c});for(const h of new Set(i.sources)){const u=this.#i(t.namespace,h);s.zRemRangeByScore(u,0,Date.now()),s.zAdd(u,{score:m,value:n})}await s.exec()}async invalidateSource(t,r){const i=this.#i(t,r),n=await this.#e.zRange(i,0,-1);n.length>0&&await this.#e.del(n),await this.#e.del(i)}async close(){this.#r&&await this.#e.close()}}const j=async(e={})=>{const t=b(e.url?{url:e.url}:{});return t.on("error",()=>{}),await t.connect(),new p(t,{...e.keyPrefix?{keyPrefix:e.keyPrefix}:{},ownsClient:!0})};export{p as RedisQueryCache,j as createRedisQueryCache};
1
+ import{createClient as b}from"redis";import{createHash as g}from"node:crypto";const o=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?e.map(o):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,o(r)])):e,d=e=>g("sha256").update(JSON.stringify(o(e))).digest("hex"),A=e=>d({namespace:e.namespace,provider:e.provider,connectionId:e.connectionId,sql:e.sql,parameters:e.parameters}),S=(e,t)=>d({namespace:e,source:t}),a=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?{type:"array",value:e.map(a)}:e&&typeof e=="object"?{type:"object",value:Object.entries(e).map(([t,r])=>[t,a(r)])}:e,f=e=>{if(e===null||typeof e!="object")return e;switch(e.type){case"undefined":return;case"bigint":return BigInt(e.value);case"number":return Number(e.value);case"date":return new Date(e.value);case"array":return e.value.map(f);case"object":return Object.fromEntries(e.value.map(([t,r])=>[t,f(r)]))}},w=e=>JSON.stringify(a(e)),x=e=>{const t=f(JSON.parse(e));if(!(!Array.isArray(t)||t.some(r=>!r||typeof r!="object"||Array.isArray(r))))return t},l=Number.MAX_SAFE_INTEGER;class p{#e;#t;#r;constructor(t,r={}){this.#e=t,this.#t=r.keyPrefix??"semanticdb:cache",this.#r=r.ownsClient??!1}#n(t){return`${this.#t}:query:${A(t)}`}#i(t,r){return`${this.#t}:source:${S(t,r)}`}async get(t){const r=await this.#e.get(this.#n(t));if(r!==null)return x(r)}async set(t,r,i){const n=this.#n(t),y=w(r),c=i.expiresAt?.getTime(),m=c??l,s=this.#e.multi();c===void 0?s.set(n,y):s.set(n,y,{PXAT:c});for(const h of new Set(i.sources)){const u=this.#i(t.namespace,h);s.zRemRangeByScore(u,0,Date.now()),s.zAdd(u,{score:m,value:n})}await s.exec()}async invalidateSource(t,r){const i=this.#i(t,r),n=await this.#e.zRange(i,0,-1);n.length>0&&await this.#e.del(n),await this.#e.del(i)}async close(){this.#r&&await this.#e.close()}}const j=async(e={})=>{const t=b(e.url?{url:e.url}:{});return t.on("error",()=>{}),await t.connect(),new p(t,{...e.keyPrefix?{keyPrefix:e.keyPrefix}:{},ownsClient:!0})};export{p as RedisQueryCache,j as createRedisQueryCache};
@@ -1 +1 @@
1
- "use strict";var b=require("redis"),g=require("node:crypto");const a=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?e.map(a):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,a(r)])):e,d=e=>g.createHash("sha256").update(JSON.stringify(a(e))).digest("hex"),A=e=>d({namespace:e.namespace,provider:e.provider,sql:e.sql,parameters:e.parameters}),S=(e,t)=>d({namespace:e,source:t}),o=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?{type:"array",value:e.map(o)}:e&&typeof e=="object"?{type:"object",value:Object.entries(e).map(([t,r])=>[t,o(r)])}:e,y=e=>{if(e===null||typeof e!="object")return e;switch(e.type){case"undefined":return;case"bigint":return BigInt(e.value);case"number":return Number(e.value);case"date":return new Date(e.value);case"array":return e.value.map(y);case"object":return Object.fromEntries(e.value.map(([t,r])=>[t,y(r)]))}},w=e=>JSON.stringify(o(e)),x=e=>{const t=y(JSON.parse(e));if(!(!Array.isArray(t)||t.some(r=>!r||typeof r!="object"||Array.isArray(r))))return t},l=Number.MAX_SAFE_INTEGER;class p{#e;#t;#r;constructor(t,r={}){this.#e=t,this.#t=r.keyPrefix??"semanticdb:cache",this.#r=r.ownsClient??!1}#n(t){return`${this.#t}:query:${A(t)}`}#i(t,r){return`${this.#t}:source:${S(t,r)}`}async get(t){const r=await this.#e.get(this.#n(t));if(r!==null)return x(r)}async set(t,r,i){const n=this.#n(t),u=w(r),c=i.expiresAt?.getTime(),h=c??l,s=this.#e.multi();c===void 0?s.set(n,u):s.set(n,u,{PXAT:c});for(const m of new Set(i.sources)){const f=this.#i(t.namespace,m);s.zRemRangeByScore(f,0,Date.now()),s.zAdd(f,{score:h,value:n})}await s.exec()}async invalidateSource(t,r){const i=this.#i(t,r),n=await this.#e.zRange(i,0,-1);n.length>0&&await this.#e.del(n),await this.#e.del(i)}async close(){this.#r&&await this.#e.close()}}const C=async(e={})=>{const t=b.createClient(e.url?{url:e.url}:{});return t.on("error",()=>{}),await t.connect(),new p(t,{...e.keyPrefix?{keyPrefix:e.keyPrefix}:{},ownsClient:!0})};exports.RedisQueryCache=p,exports.createRedisQueryCache=C;
1
+ "use strict";var b=require("redis"),g=require("node:crypto");const a=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?e.map(a):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,a(r)])):e,d=e=>g.createHash("sha256").update(JSON.stringify(a(e))).digest("hex"),A=e=>d({namespace:e.namespace,provider:e.provider,connectionId:e.connectionId,sql:e.sql,parameters:e.parameters}),S=(e,t)=>d({namespace:e,source:t}),o=e=>e===void 0?{type:"undefined"}:typeof e=="bigint"?{type:"bigint",value:e.toString()}:typeof e=="number"&&!Number.isFinite(e)?{type:"number",value:String(e)}:e instanceof Date?{type:"date",value:e.toISOString()}:Array.isArray(e)?{type:"array",value:e.map(o)}:e&&typeof e=="object"?{type:"object",value:Object.entries(e).map(([t,r])=>[t,o(r)])}:e,u=e=>{if(e===null||typeof e!="object")return e;switch(e.type){case"undefined":return;case"bigint":return BigInt(e.value);case"number":return Number(e.value);case"date":return new Date(e.value);case"array":return e.value.map(u);case"object":return Object.fromEntries(e.value.map(([t,r])=>[t,u(r)]))}},w=e=>JSON.stringify(o(e)),x=e=>{const t=u(JSON.parse(e));if(!(!Array.isArray(t)||t.some(r=>!r||typeof r!="object"||Array.isArray(r))))return t},l=Number.MAX_SAFE_INTEGER;class p{#e;#t;#r;constructor(t,r={}){this.#e=t,this.#t=r.keyPrefix??"semanticdb:cache",this.#r=r.ownsClient??!1}#n(t){return`${this.#t}:query:${A(t)}`}#i(t,r){return`${this.#t}:source:${S(t,r)}`}async get(t){const r=await this.#e.get(this.#n(t));if(r!==null)return x(r)}async set(t,r,i){const n=this.#n(t),y=w(r),c=i.expiresAt?.getTime(),h=c??l,s=this.#e.multi();c===void 0?s.set(n,y):s.set(n,y,{PXAT:c});for(const m of new Set(i.sources)){const f=this.#i(t.namespace,m);s.zRemRangeByScore(f,0,Date.now()),s.zAdd(f,{score:h,value:n})}await s.exec()}async invalidateSource(t,r){const i=this.#i(t,r),n=await this.#e.zRange(i,0,-1);n.length>0&&await this.#e.del(n),await this.#e.del(i)}async close(){this.#r&&await this.#e.close()}}const C=async(e={})=>{const t=b.createClient(e.url?{url:e.url}:{});return t.on("error",()=>{}),await t.connect(),new p(t,{...e.keyPrefix?{keyPrefix:e.keyPrefix}:{},ownsClient:!0})};exports.RedisQueryCache=p,exports.createRedisQueryCache=C;
@@ -2,6 +2,8 @@ import type { CoreComputeProvider, DataRow, SqlParameters } from '../physical/in
2
2
  export interface QueryCacheIdentity {
3
3
  readonly namespace: string;
4
4
  readonly provider: CoreComputeProvider['kind'];
5
+ /** Distinguishes separate databases using the same provider dialect. */
6
+ readonly connectionId?: string;
5
7
  readonly sql: string;
6
8
  readonly parameters: SqlParameters;
7
9
  }
@@ -24,4 +24,4 @@ export interface PreparedCteQuery {
24
24
  * CTE preserves those ranges while allowing the provider to execute one query.
25
25
  */
26
26
  export declare const compileCteQueries: (units: readonly PreparedCteQuery[], outputPlan: LogicalPlan, provider: CoreComputeProvider, operators?: OperatorRegistry) => CompiledQuery;
27
- export declare const compileCteCalculation: (calculation: OperatorComponentCalculation, components: readonly PreparedCteComponent[], outputPlan: LogicalPlan, provider: CoreComputeProvider, operators: OperatorRegistry) => CompiledQuery;
27
+ export declare const compileCteCalculation: (calculation: OperatorComponentCalculation, components: readonly PreparedCteComponent[], outputPlan: LogicalPlan, outputName: string, provider: CoreComputeProvider, operators: OperatorRegistry) => CompiledQuery;
package/dist/errors.d.ts CHANGED
@@ -1,7 +1,8 @@
1
+ import { type LocaleRegistry } from './i18n/index';
1
2
  export declare class SemanticDBError extends Error {
2
3
  readonly code: string;
3
4
  readonly details?: Readonly<Record<string, unknown>> | undefined;
4
5
  constructor(code: string, message: string, details?: Readonly<Record<string, unknown>> | undefined);
5
6
  /** Return the user-facing message for an execution locale. */
6
- localizedMessage(locale?: string): string;
7
+ localizedMessage(locale?: string, locales?: LocaleRegistry): string;
7
8
  }
@@ -1,9 +1,14 @@
1
1
  import type { LogicformType } from '../logicform-types';
2
- import type { OperatorExecutionComponent, OperatorRegistry } from '../operators/index';
2
+ import type { OperatorComponentCalculation, OperatorJavaScriptCalculation, OperatorQueryComponent, OperatorRegistry } from '../operators/index';
3
3
  import type { DataRow } from '../physical/index';
4
4
  import type { LogicalPlan } from '../planner/index';
5
5
  import type { ResolvedSemanticSchema } from '../semantic-types/index';
6
- export type ExecutionUnit = OperatorExecutionComponent;
7
- export declare const createExecutionUnits: (logicform: LogicformType, plan: LogicalPlan, schema: ResolvedSemanticSchema, operators: OperatorRegistry, resolveSchema: (schemaId: string) => ResolvedSemanticSchema, sourceLogicform?: LogicformType) => readonly ExecutionUnit[] | undefined;
6
+ import { type LocaleTranslate } from '../i18n/index';
7
+ export type ExecutionUnit = OperatorQueryComponent | (OperatorComponentCalculation & {
8
+ readonly outputName: string;
9
+ }) | (OperatorJavaScriptCalculation & {
10
+ readonly outputName: string;
11
+ });
12
+ export declare const createExecutionUnits: (logicform: LogicformType, plan: LogicalPlan, schema: ResolvedSemanticSchema, operators: OperatorRegistry, resolveSchema: (schemaId: string) => ResolvedSemanticSchema, sourceLogicform?: LogicformType, locale?: string, t?: LocaleTranslate) => readonly ExecutionUnit[] | undefined;
8
13
  export declare const mergeComponentRows: (results: readonly (readonly DataRow[])[], groupNames: readonly string[]) => DataRow[];
9
14
  export declare const mergeExecutionResults: (results: readonly (readonly DataRow[])[], plan: LogicalPlan) => DataRow[];
@@ -1,4 +1,4 @@
1
- import type { CoreComputeProvider, DatabaseConnection } from '../physical/index';
1
+ import type { CoreComputeProvider, DatabaseConnection, ProviderRegistry } from '../physical/index';
2
2
  type ProviderFactory = (connection: DatabaseConnection) => CoreComputeProvider;
3
3
  export declare class ConnectionRegistry {
4
4
  #private;
@@ -7,6 +7,6 @@ export declare class ConnectionRegistry {
7
7
  get(connection: DatabaseConnection): CoreComputeProvider;
8
8
  close(): Promise<void>;
9
9
  }
10
- export declare const getConnectionProvider: (connection: DatabaseConnection) => CoreComputeProvider;
10
+ export declare const getConnectionProvider: (connection: DatabaseConnection, providers?: ProviderRegistry) => CoreComputeProvider;
11
11
  export declare const closeConnections: () => Promise<void>;
12
12
  export {};
@@ -1,13 +1,18 @@
1
1
  import type { CompiledQuery } from '../compiler/index';
2
2
  import type { DataRow } from '../physical/index';
3
3
  import type { LogicalPlan } from '../planner/index';
4
+ import type { SchemaMappingDefinition, SchemaMappingRegistry } from '../schema-mappings/index';
4
5
  import type { InternalExecuteOptions } from './types';
5
6
  export interface PhysicalQuerySource {
6
7
  readonly schemaId: string;
7
8
  readonly source: string;
9
+ readonly connectionId?: string;
10
+ /** Base mapping source that owns an enrichment source. */
11
+ readonly mappingSource?: string;
8
12
  }
13
+ export declare const resolvePhysicalSourceMapping: (source: PhysicalQuerySource, mappings: SchemaMappingRegistry) => SchemaMappingDefinition | undefined;
9
14
  export declare const collectPlanSources: (plan: LogicalPlan) => readonly PhysicalQuerySource[];
10
15
  export declare const mergePhysicalSources: (groups: readonly (readonly PhysicalQuerySource[])[]) => readonly PhysicalQuerySource[];
11
- export declare const executeCompiledQuery: (compiled: CompiledQuery, sources: readonly PhysicalQuerySource[], options: Pick<InternalExecuteOptions, "cache" | "provider" | "schemaMappings" | "signal">) => Promise<DataRow[]>;
16
+ export declare const executeCompiledQuery: (compiled: CompiledQuery, sources: readonly PhysicalQuerySource[], options: Pick<InternalExecuteOptions, "cache" | "provider" | "schemaMappings" | "signal" | "connectionId">) => Promise<DataRow[]>;
12
17
  /** Apply final SQL shaping after all plans, views, and derived sources are compiled. */
13
18
  export declare const shortenCompiledQuery: (compiled: CompiledQuery) => CompiledQuery;
@@ -0,0 +1,37 @@
1
+ import type { CompiledQuery } from '../compiler/index';
2
+ import type { LogicformType } from '../logicform-types';
3
+ import type { OperatorRegistry } from '../operators/index';
4
+ import type { CoreComputeProvider, DataRow } from '../physical/index';
5
+ import type { LogicalPlan } from '../planner/index';
6
+ import type { SchemaMappingRegistry } from '../schema-mappings/index';
7
+ import type { InternalExecuteOptions } from './types';
8
+ export declare const coordinatorConnectionId = "__semanticdb_coordinator__";
9
+ export interface FederatedCompiledQuery {
10
+ readonly compiled: CompiledQuery;
11
+ readonly connectionId: string;
12
+ readonly provider: CoreComputeProvider;
13
+ readonly type: 'computation' | 'total';
14
+ readonly logicform: LogicformType;
15
+ }
16
+ interface FederatedExecutionOptions {
17
+ readonly logicform: LogicformType;
18
+ readonly operators: OperatorRegistry;
19
+ readonly schemaMappings: SchemaMappingRegistry;
20
+ readonly providerForConnection: (connectionId: string) => CoreComputeProvider;
21
+ readonly cache?: InternalExecuteOptions['cache'];
22
+ readonly signal?: AbortSignal;
23
+ readonly maxMaterializedRows: number;
24
+ readonly onCompiled: (query: FederatedCompiledQuery) => void;
25
+ }
26
+ export interface FederatedExecutionResult {
27
+ readonly rows: DataRow[];
28
+ readonly total: number;
29
+ readonly connections: readonly {
30
+ readonly connectionId: string;
31
+ readonly provider: CoreComputeProvider['kind'];
32
+ }[];
33
+ }
34
+ export declare const planConnectionIds: (plan: LogicalPlan, schemaMappings: SchemaMappingRegistry) => readonly string[];
35
+ export declare const isFederatedComposition: (plan: LogicalPlan, schemaMappings: SchemaMappingRegistry) => boolean;
36
+ export declare const executeFederatedComposition: (plan: LogicalPlan, options: FederatedExecutionOptions) => Promise<FederatedExecutionResult>;
37
+ export {};
@@ -9,19 +9,23 @@ import type { Permissions } from '../permissions/index';
9
9
  import type { ResolvedSemanticSchema, SemanticTypeRegistry } from '../semantic-types/index';
10
10
  import type { EntityFetchRequest } from './result-hydrator/index';
11
11
  import type { SemanticDBPlugin } from '../plugins/index';
12
+ import type { LocaleRegistry } from '../i18n/index';
12
13
  interface FetchEntityOptions {
13
14
  readonly version: string;
14
15
  readonly operators: OperatorRegistry;
15
16
  readonly locale?: string;
17
+ readonly localeRegistry?: LocaleRegistry;
16
18
  readonly schemaMappings: SchemaMappingRegistry;
17
19
  readonly semanticTypes: SemanticTypeRegistry;
18
20
  readonly plugins?: readonly SemanticDBPlugin[];
19
21
  readonly resolveSchema: (schemaId: string) => ResolvedSemanticSchema;
20
22
  readonly permissions?: Permissions;
21
23
  readonly provider: CoreComputeProvider;
24
+ readonly providerForConnection?: (connectionId: string) => CoreComputeProvider;
25
+ readonly preferredConnectionId?: string;
22
26
  readonly cache?: QueryCacheExecutionOptions;
23
27
  readonly signal?: AbortSignal;
24
- readonly onCompiled?: (compiled: CompiledQuery, logicform: LogicformType) => void;
28
+ readonly onCompiled?: (compiled: CompiledQuery, logicform: LogicformType, provider: CoreComputeProvider, connectionId?: string) => void;
25
29
  }
26
30
  export declare const fetchEntities: (request: EntityFetchRequest, options: FetchEntityOptions) => Promise<DataRow[]>;
27
31
  export {};
@@ -1,6 +1,7 @@
1
1
  import type { ResultProperty } from '../../operators/index';
2
2
  import type { DataRow } from '../../physical/index';
3
3
  import type { ResolvedSemanticProperty, ResolvedSemanticSchema, SemanticTypeRegistry } from '../../semantic-types/index';
4
+ import type { SemanticDBPlugin } from '../../plugins/index';
4
5
  export type HydrationProperty = Pick<ResultProperty, 'isArray' | 'name' | 'primal_type' | 'ref' | 'type'> | ResolvedSemanticProperty;
5
6
  export interface EntityFetchRequest {
6
7
  readonly schema: ResolvedSemanticSchema;
@@ -11,4 +12,5 @@ export interface ResultHydratorOptions {
11
12
  readonly semanticTypes: SemanticTypeRegistry;
12
13
  readonly resolveSchema: (schemaId: string) => ResolvedSemanticSchema;
13
14
  readonly fetchEntities: (request: EntityFetchRequest) => Promise<readonly DataRow[]>;
15
+ readonly plugins?: readonly SemanticDBPlugin[];
14
16
  }
@@ -0,0 +1,11 @@
1
+ import type { CompiledQuery } from '../compiler/index';
2
+ import type { CoreComputeProvider, DataRow } from '../physical/index';
3
+ import type { LogicalPlan } from '../planner/index';
4
+ import type { LogicformType } from '../logicform-types';
5
+ /** Remove global result paging while retaining HAVING and per-group limitBy semantics. */
6
+ export declare const withoutGlobalPagination: (plan: LogicalPlan) => LogicalPlan;
7
+ /** Describe the total SQL with the same unpaged global result as its plan. */
8
+ export declare const withoutGlobalPaginationLogicform: (logicform: LogicformType) => LogicformType;
9
+ /** Count the rows produced by a complete unpaginated logical result. */
10
+ export declare const compileTotalQuery: (unpaged: CompiledQuery, provider: CoreComputeProvider) => CompiledQuery;
11
+ export declare const readTotal: (rows: readonly DataRow[]) => number;
@@ -1,13 +1,14 @@
1
- import type { OperatorRegistry } from '../operators/index';
2
- import type { CoreComputeProvider, DataRow, SqlParameters } from '../physical/index';
1
+ import type { OperatorDefinition, OperatorRegistry } from '../operators/index';
2
+ import type { CoreComputeProvider, DataRow, ProviderRegistry, SqlParameters } from '../physical/index';
3
3
  import type { DatabaseConnection } from '../physical/index';
4
4
  import type { LogicalPlan, QueryLineage } from '../planner/index';
5
- import type { SchemaMappingDefinition, SchemaMappingRegistry } from '../schema-mappings/index';
5
+ import type { SchemaMappingDefinition, SchemaMappingRegistry, SchemaMappingSelection } from '../schema-mappings/index';
6
6
  import type { ResolvedSemanticSchema, SemanticSchemaDraft, SemanticTypeRegistry, TimeWatermarkPolicy } from '../semantic-types/index';
7
7
  import type { LogicformType, PublicLogicformType } from '../logicform-types';
8
8
  import type { QueryCacheExecutionOptions } from '../cache/index';
9
9
  import type { Permissions } from '../permissions/index';
10
10
  import type { SemanticDBPlugin } from '../plugins/index';
11
+ import type { LocaleInput, LocaleRegistry } from '../i18n/index';
11
12
  export type ExecuteProgressEvent = {
12
13
  readonly stage: 'started';
13
14
  readonly logicform: LogicformType;
@@ -35,6 +36,7 @@ export type ExecuteProgressEvent = {
35
36
  } | {
36
37
  readonly stage: 'executing';
37
38
  readonly provider: CoreComputeProvider['kind'];
39
+ readonly connectionId?: string;
38
40
  readonly partialResult: PartialExecuteResult;
39
41
  } | {
40
42
  readonly stage: 'hydrating';
@@ -55,12 +57,16 @@ export type ExecuteProgressEvent = {
55
57
  export type ProgressCallback = (event: ExecuteProgressEvent) => void | Promise<void>;
56
58
  interface ExecuteBaseOptions {
57
59
  readonly schemas: Readonly<Record<string, SemanticSchemaDraft>>;
60
+ /** Caller-owned Registry; plugin SemanticTypes are copied into the execution Registry. */
58
61
  readonly semanticTypes?: SemanticTypeRegistry;
59
- readonly operators?: OperatorRegistry;
62
+ /** Caller-defined Operators; explicit definitions override same-name built-ins or plugin Operators. */
63
+ readonly operators?: OperatorDefinition | readonly OperatorDefinition[];
60
64
  /** Caller-owned capability plugins appended to the built-in plugin set for this execution. */
61
65
  readonly plugins?: readonly SemanticDBPlugin[];
62
66
  /** Locale for generated human-readable PredItem names. Defaults to zh-CN. */
63
67
  readonly locale?: string;
68
+ /** Additional or overriding locale files used by generated names and messages. */
69
+ readonly locales?: LocaleInput;
64
70
  /** Table, row, and column permissions for this execution. */
65
71
  readonly permissions?: Permissions;
66
72
  /** Optional caller-owned cache shared across executions. */
@@ -69,28 +75,54 @@ interface ExecuteBaseOptions {
69
75
  readonly signal?: AbortSignal;
70
76
  /** Default time watermark policy; Schema and Property policies override it. */
71
77
  readonly timeWatermark?: TimeWatermarkPolicy;
78
+ /** Resource limits for in-process combination of separate physical connections. */
79
+ readonly federation?: {
80
+ /** Defaults to 100,000 rows across all materialized aggregate branches. */
81
+ readonly maxMaterializedRows?: number;
82
+ };
72
83
  }
73
84
  export interface ExecuteOptions extends ExecuteBaseOptions {
74
85
  /** Physical mappings supplied as JSON definitions. */
75
86
  readonly schemaMappings: readonly SchemaMappingDefinition[];
76
87
  /** Database connections addressable by SchemaMappingDefinition.connectionId. */
77
88
  readonly connections: readonly DatabaseConnection[];
89
+ /** Provider factories used for built-in or application-defined connection kinds. */
90
+ readonly providerRegistry?: ProviderRegistry;
78
91
  }
79
92
  /** Internal execution seam used by executor tests after a provider has been resolved. */
80
- export interface InternalExecuteOptions extends ExecuteBaseOptions {
93
+ export interface InternalExecuteOptions extends Omit<ExecuteBaseOptions, 'operators'> {
94
+ readonly operators?: OperatorRegistry;
95
+ /** The public execution boundary has already merged built-in, plugin, and explicit Operators. */
96
+ readonly operatorsResolved?: boolean;
97
+ /** The public execution boundary has already merged plugin SemanticTypes. */
98
+ readonly semanticTypesResolved?: boolean;
99
+ readonly localeRegistry?: LocaleRegistry;
81
100
  readonly provider: CoreComputeProvider;
101
+ /** Resolve providers for additional physical connections in a federated execution. */
102
+ readonly providerForConnection?: (connectionId: string) => CoreComputeProvider;
82
103
  readonly schemaMappings: SchemaMappingRegistry;
83
104
  /** Selected connection for public execution; omitted only by internal provider tests. */
84
105
  readonly connectionId?: string;
85
106
  }
86
107
  export interface ExecuteSuccessResult {
87
108
  readonly result: DataRow[];
109
+ /** Result rows or grouped rows before global limit/page pagination. */
110
+ readonly total: number;
88
111
  /** Schema describing the computed result rows. */
89
112
  readonly schema: ResolvedSemanticSchema;
90
113
  readonly normedLogicform: PublicLogicformType;
91
114
  readonly sqls: readonly ExecutedSql[];
92
115
  readonly lineage: QueryLineage;
93
116
  readonly provider: CoreComputeProvider['kind'];
117
+ /** Physical connections that contributed rows to this result. */
118
+ readonly providers: readonly {
119
+ readonly connectionId: string;
120
+ readonly provider: CoreComputeProvider['kind'];
121
+ }[];
122
+ /** Physical mapping decisions, including why every non-selected candidate lost. */
123
+ readonly schemaMappingSelections: readonly SchemaMappingSelection[];
124
+ /** Plugin-owned materialization for a custom LogicForm output type. */
125
+ readonly output?: unknown;
94
126
  }
95
127
  export interface ExecuteFailureResult {
96
128
  readonly errorCode: '100';
@@ -103,10 +135,13 @@ export interface ExecuteFailureResult {
103
135
  readonly lineage?: QueryLineage;
104
136
  }
105
137
  export type ExecuteResult = ExecuteSuccessResult | ExecuteFailureResult;
106
- export type ExecutedSqlType = 'computation' | 'semantic-resolution';
138
+ export type ExecutedSqlType = 'computation' | 'semantic-resolution' | 'total';
107
139
  export interface ExecutedSql {
108
140
  /** Why this SQL participates in the LogicForm execution. */
109
141
  readonly type: ExecutedSqlType;
142
+ /** Physical connection that executed this SQL, or the in-process coordinator id. */
143
+ readonly connectionId?: string;
144
+ readonly provider?: CoreComputeProvider['kind'];
110
145
  /** Human-readable, localized explanation of why this SQL is executed. */
111
146
  readonly explain: string;
112
147
  /** Normalized LogicForm represented by this SQL statement. */
@@ -0,0 +1,4 @@
1
+ export { createLocaleRegistry, defaultLocaleRegistry, LocaleRegistry } from './registry';
2
+ export type { LocaleDefinition, LocaleInput, LocaleTranslate } from './types';
3
+ export { enUS } from './locales/en-US';
4
+ export { zhCN } from './locales/zh-CN';
@@ -0,0 +1,2 @@
1
+ import type { LocaleDefinition } from '../types';
2
+ export declare const enUS: LocaleDefinition;
@@ -0,0 +1,2 @@
1
+ import type { LocaleDefinition } from '../types';
2
+ export declare const zhCN: LocaleDefinition;