semanticdb-v2 2.0.0-alpha.1

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 (306) hide show
  1. package/README.md +448 -0
  2. package/dist/cache/expiry.d.ts +5 -0
  3. package/dist/cache/expiry.js +1 -0
  4. package/dist/cache/identity.d.ts +3 -0
  5. package/dist/cache/identity.js +1 -0
  6. package/dist/cache/index.d.ts +3 -0
  7. package/dist/cache/redis-query-cache.d.ts +37 -0
  8. package/dist/cache/redis-query-cache.js +1 -0
  9. package/dist/cache/serialization.d.ts +3 -0
  10. package/dist/cache/serialization.js +1 -0
  11. package/dist/cache/types.d.ts +27 -0
  12. package/dist/calendar/constants.d.ts +7 -0
  13. package/dist/calendar/constants.js +1 -0
  14. package/dist/calendar/index.d.ts +6 -0
  15. package/dist/calendar/provider-relation-store.d.ts +9 -0
  16. package/dist/calendar/provider-relation-store.js +1 -0
  17. package/dist/calendar/resolver.d.ts +10 -0
  18. package/dist/calendar/resolver.js +1 -0
  19. package/dist/calendar/system-schemas.d.ts +3 -0
  20. package/dist/calendar/system-schemas.js +1 -0
  21. package/dist/calendar/types.d.ts +31 -0
  22. package/dist/compatibility/index.d.ts +2 -0
  23. package/dist/compatibility/logicform.d.ts +13 -0
  24. package/dist/compatibility/logicform.js +1 -0
  25. package/dist/compiler/compile-cte.d.ts +12 -0
  26. package/dist/compiler/compile-cte.js +1 -0
  27. package/dist/compiler/compile-plan.d.ts +5 -0
  28. package/dist/compiler/compile-plan.js +1 -0
  29. package/dist/compiler/index.d.ts +4 -0
  30. package/dist/compiler/shorten-sql.d.ts +2 -0
  31. package/dist/compiler/shorten-sql.js +2 -0
  32. package/dist/compiler/types.d.ts +5 -0
  33. package/dist/custom-functions/custom-functions.d.ts +6 -0
  34. package/dist/custom-functions/custom-functions.js +1 -0
  35. package/dist/custom-functions/index.d.ts +3 -0
  36. package/dist/custom-functions/operator-adapter.d.ts +12 -0
  37. package/dist/custom-functions/operator-adapter.js +1 -0
  38. package/dist/custom-functions/types.d.ts +62 -0
  39. package/dist/errors.d.ts +5 -0
  40. package/dist/errors.js +1 -0
  41. package/dist/executor/compatibility.d.ts +6 -0
  42. package/dist/executor/compatibility.js +1 -0
  43. package/dist/executor/components.d.ts +9 -0
  44. package/dist/executor/components.js +1 -0
  45. package/dist/executor/connection-registry.d.ts +12 -0
  46. package/dist/executor/connection-registry.js +1 -0
  47. package/dist/executor/evaluate.d.ts +4 -0
  48. package/dist/executor/evaluate.js +1 -0
  49. package/dist/executor/execute-query.d.ts +13 -0
  50. package/dist/executor/execute-query.js +1 -0
  51. package/dist/executor/execute.d.ts +4 -0
  52. package/dist/executor/execute.js +1 -0
  53. package/dist/executor/fetch-entities.d.ts +26 -0
  54. package/dist/executor/fetch-entities.js +1 -0
  55. package/dist/executor/index.d.ts +3 -0
  56. package/dist/executor/result-hydrator/entity-loader.d.ts +6 -0
  57. package/dist/executor/result-hydrator/entity-loader.js +1 -0
  58. package/dist/executor/result-hydrator/format-values.d.ts +4 -0
  59. package/dist/executor/result-hydrator/format-values.js +1 -0
  60. package/dist/executor/result-hydrator/hydrate-result.d.ts +4 -0
  61. package/dist/executor/result-hydrator/hydrate-result.js +1 -0
  62. package/dist/executor/result-hydrator/index.d.ts +2 -0
  63. package/dist/executor/result-hydrator/missing-entity.d.ts +3 -0
  64. package/dist/executor/result-hydrator/missing-entity.js +1 -0
  65. package/dist/executor/result-hydrator/populate-entities.d.ts +3 -0
  66. package/dist/executor/result-hydrator/populate-entities.js +1 -0
  67. package/dist/executor/result-hydrator/types.d.ts +14 -0
  68. package/dist/executor/row-identity.d.ts +4 -0
  69. package/dist/executor/row-identity.js +1 -0
  70. package/dist/executor/types.d.ts +118 -0
  71. package/dist/index.d.ts +26 -0
  72. package/dist/index.js +1 -0
  73. package/dist/logicform-types.d.ts +125 -0
  74. package/dist/logicform-types.js +1 -0
  75. package/dist/normalizer/core.d.ts +19 -0
  76. package/dist/normalizer/core.js +1 -0
  77. package/dist/normalizer/default-pred-name.d.ts +6 -0
  78. package/dist/normalizer/default-pred-name.js +1 -0
  79. package/dist/normalizer/entity-id.d.ts +4 -0
  80. package/dist/normalizer/entity-id.js +1 -0
  81. package/dist/normalizer/groupby.d.ts +4 -0
  82. package/dist/normalizer/groupby.js +1 -0
  83. package/dist/normalizer/index.d.ts +3 -0
  84. package/dist/normalizer/normalize-logicform.d.ts +4 -0
  85. package/dist/normalizer/normalize-logicform.js +1 -0
  86. package/dist/normalizer/pagination.d.ts +2 -0
  87. package/dist/normalizer/pagination.js +1 -0
  88. package/dist/normalizer/pred-query-name.d.ts +1 -0
  89. package/dist/normalizer/pred-query-name.js +1 -0
  90. package/dist/normalizer/preds.d.ts +4 -0
  91. package/dist/normalizer/preds.js +1 -0
  92. package/dist/normalizer/query.d.ts +5 -0
  93. package/dist/normalizer/query.js +1 -0
  94. package/dist/normalizer/version.d.ts +3 -0
  95. package/dist/normalizer/version.js +1 -0
  96. package/dist/operators/builtins/abs.d.ts +2 -0
  97. package/dist/operators/builtins/abs.js +1 -0
  98. package/dist/operators/builtins/accumulate.d.ts +2 -0
  99. package/dist/operators/builtins/accumulate.js +1 -0
  100. package/dist/operators/builtins/add.d.ts +2 -0
  101. package/dist/operators/builtins/add.js +1 -0
  102. package/dist/operators/builtins/arithmetic.d.ts +3 -0
  103. package/dist/operators/builtins/arithmetic.js +1 -0
  104. package/dist/operators/builtins/average-per-period.d.ts +2 -0
  105. package/dist/operators/builtins/average-per-period.js +1 -0
  106. package/dist/operators/builtins/avg.d.ts +2 -0
  107. package/dist/operators/builtins/avg.js +1 -0
  108. package/dist/operators/builtins/case.d.ts +2 -0
  109. package/dist/operators/builtins/case.js +1 -0
  110. package/dist/operators/builtins/concat.d.ts +2 -0
  111. package/dist/operators/builtins/concat.js +1 -0
  112. package/dist/operators/builtins/count-distinct.d.ts +2 -0
  113. package/dist/operators/builtins/count-distinct.js +1 -0
  114. package/dist/operators/builtins/count.d.ts +2 -0
  115. package/dist/operators/builtins/count.js +1 -0
  116. package/dist/operators/builtins/date-bucket.d.ts +2 -0
  117. package/dist/operators/builtins/date-bucket.js +1 -0
  118. package/dist/operators/builtins/divide.d.ts +2 -0
  119. package/dist/operators/builtins/divide.js +1 -0
  120. package/dist/operators/builtins/index.d.ts +2 -0
  121. package/dist/operators/builtins/index.js +1 -0
  122. package/dist/operators/builtins/max.d.ts +2 -0
  123. package/dist/operators/builtins/max.js +1 -0
  124. package/dist/operators/builtins/min.d.ts +2 -0
  125. package/dist/operators/builtins/min.js +1 -0
  126. package/dist/operators/builtins/multiply.d.ts +2 -0
  127. package/dist/operators/builtins/multiply.js +1 -0
  128. package/dist/operators/builtins/newly.d.ts +2 -0
  129. package/dist/operators/builtins/newly.js +1 -0
  130. package/dist/operators/builtins/period-boundary.d.ts +2 -0
  131. package/dist/operators/builtins/period-boundary.js +1 -0
  132. package/dist/operators/builtins/period-difference.d.ts +2 -0
  133. package/dist/operators/builtins/period-difference.js +1 -0
  134. package/dist/operators/builtins/period-growth.d.ts +2 -0
  135. package/dist/operators/builtins/period-growth.js +1 -0
  136. package/dist/operators/builtins/period-name.d.ts +1 -0
  137. package/dist/operators/builtins/period-name.js +1 -0
  138. package/dist/operators/builtins/period-shift.d.ts +6 -0
  139. package/dist/operators/builtins/period-shift.js +1 -0
  140. package/dist/operators/builtins/rank.d.ts +2 -0
  141. package/dist/operators/builtins/rank.js +1 -0
  142. package/dist/operators/builtins/row-number.d.ts +2 -0
  143. package/dist/operators/builtins/row-number.js +1 -0
  144. package/dist/operators/builtins/share-of-total.d.ts +2 -0
  145. package/dist/operators/builtins/share-of-total.js +1 -0
  146. package/dist/operators/builtins/sql.d.ts +3 -0
  147. package/dist/operators/builtins/sql.js +1 -0
  148. package/dist/operators/builtins/substring.d.ts +2 -0
  149. package/dist/operators/builtins/substring.js +1 -0
  150. package/dist/operators/builtins/subtract.d.ts +2 -0
  151. package/dist/operators/builtins/subtract.js +1 -0
  152. package/dist/operators/builtins/sum.d.ts +2 -0
  153. package/dist/operators/builtins/sum.js +1 -0
  154. package/dist/operators/builtins/time-range.d.ts +13 -0
  155. package/dist/operators/builtins/time-range.js +1 -0
  156. package/dist/operators/groupby.d.ts +3 -0
  157. package/dist/operators/groupby.js +1 -0
  158. package/dist/operators/index.d.ts +7 -0
  159. package/dist/operators/index.js +1 -0
  160. package/dist/operators/registry.d.ts +10 -0
  161. package/dist/operators/registry.js +1 -0
  162. package/dist/operators/sql-expression.d.ts +1 -0
  163. package/dist/operators/sql-expression.js +1 -0
  164. package/dist/operators/types.d.ts +277 -0
  165. package/dist/permissions/index.d.ts +3 -0
  166. package/dist/permissions/permissions.d.ts +10 -0
  167. package/dist/permissions/permissions.js +1 -0
  168. package/dist/permissions/types.d.ts +12 -0
  169. package/dist/physical/clickhouse-provider.d.ts +44 -0
  170. package/dist/physical/clickhouse-provider.js +1 -0
  171. package/dist/physical/connection.d.ts +32 -0
  172. package/dist/physical/connection.js +1 -0
  173. package/dist/physical/display-query.d.ts +4 -0
  174. package/dist/physical/display-query.js +1 -0
  175. package/dist/physical/doris-provider.d.ts +9 -0
  176. package/dist/physical/doris-provider.js +1 -0
  177. package/dist/physical/index.d.ts +18 -0
  178. package/dist/physical/mysql-compatible-sql.d.ts +6 -0
  179. package/dist/physical/mysql-compatible-sql.js +1 -0
  180. package/dist/physical/mysql-protocol-provider.d.ts +34 -0
  181. package/dist/physical/mysql-protocol-provider.js +1 -0
  182. package/dist/physical/mysql-provider.d.ts +8 -0
  183. package/dist/physical/mysql-provider.js +1 -0
  184. package/dist/physical/oracle-provider.d.ts +53 -0
  185. package/dist/physical/oracle-provider.js +1 -0
  186. package/dist/physical/postgresql-provider.d.ts +37 -0
  187. package/dist/physical/postgresql-provider.js +1 -0
  188. package/dist/physical/provider.d.ts +40 -0
  189. package/dist/physical/snowflake-provider.d.ts +45 -0
  190. package/dist/physical/snowflake-provider.js +1 -0
  191. package/dist/physical/starrocks-provider.d.ts +9 -0
  192. package/dist/physical/starrocks-provider.js +1 -0
  193. package/dist/planner/build-lineage.d.ts +5 -0
  194. package/dist/planner/build-lineage.js +1 -0
  195. package/dist/planner/create-logical-plan.d.ts +15 -0
  196. package/dist/planner/create-logical-plan.js +1 -0
  197. package/dist/planner/index.d.ts +7 -0
  198. package/dist/planner/select-schema-mapping.d.ts +14 -0
  199. package/dist/planner/select-schema-mapping.js +1 -0
  200. package/dist/planner/semantics/index.d.ts +5 -0
  201. package/dist/planner/semantics/lifecycle/classification.d.ts +7 -0
  202. package/dist/planner/semantics/lifecycle/classification.js +1 -0
  203. package/dist/planner/semantics/lifecycle/index.d.ts +3 -0
  204. package/dist/planner/semantics/lifecycle/query-policy.d.ts +3 -0
  205. package/dist/planner/semantics/lifecycle/query-policy.js +1 -0
  206. package/dist/planner/semantics/lifecycle/rule.d.ts +2 -0
  207. package/dist/planner/semantics/lifecycle/rule.js +1 -0
  208. package/dist/planner/semantics/observation/classification.d.ts +3 -0
  209. package/dist/planner/semantics/observation/classification.js +1 -0
  210. package/dist/planner/semantics/observation/index.d.ts +4 -0
  211. package/dist/planner/semantics/observation/operator-policy.d.ts +11 -0
  212. package/dist/planner/semantics/observation/operator-policy.js +1 -0
  213. package/dist/planner/semantics/observation/rule.d.ts +2 -0
  214. package/dist/planner/semantics/observation/rule.js +1 -0
  215. package/dist/planner/semantics/observation/types.d.ts +3 -0
  216. package/dist/planner/semantics/registry-defaults.d.ts +2 -0
  217. package/dist/planner/semantics/registry-defaults.js +1 -0
  218. package/dist/planner/semantics/registry.d.ts +32 -0
  219. package/dist/planner/semantics/registry.js +1 -0
  220. package/dist/planner/types.d.ts +202 -0
  221. package/dist/planner/types.js +1 -0
  222. package/dist/plugins/hierarchy/compatibility.d.ts +2 -0
  223. package/dist/plugins/hierarchy/compatibility.js +1 -0
  224. package/dist/plugins/hierarchy/entity-identity.d.ts +2 -0
  225. package/dist/plugins/hierarchy/entity-identity.js +1 -0
  226. package/dist/plugins/hierarchy/index.d.ts +3 -0
  227. package/dist/plugins/hierarchy/index.js +1 -0
  228. package/dist/plugins/hierarchy/operator.d.ts +2 -0
  229. package/dist/plugins/hierarchy/operator.js +1 -0
  230. package/dist/plugins/hierarchy/resolve-schema.d.ts +2 -0
  231. package/dist/plugins/hierarchy/resolve-schema.js +1 -0
  232. package/dist/plugins/hierarchy/types.d.ts +28 -0
  233. package/dist/plugins/index.d.ts +5 -0
  234. package/dist/plugins/index.js +1 -0
  235. package/dist/plugins/types.d.ts +20 -0
  236. package/dist/schema/entity-identity.d.ts +5 -0
  237. package/dist/schema/entity-identity.js +1 -0
  238. package/dist/schema/index.d.ts +4 -0
  239. package/dist/schema/resolve-property-path.d.ts +15 -0
  240. package/dist/schema/resolve-property-path.js +1 -0
  241. package/dist/schema/schema-registry.d.ts +4 -0
  242. package/dist/schema/schema-registry.js +1 -0
  243. package/dist/schema-mappings/freshness.d.ts +7 -0
  244. package/dist/schema-mappings/freshness.js +1 -0
  245. package/dist/schema-mappings/index.d.ts +4 -0
  246. package/dist/schema-mappings/registry.d.ts +9 -0
  247. package/dist/schema-mappings/registry.js +1 -0
  248. package/dist/schema-mappings/types.d.ts +46 -0
  249. package/dist/semantic-types/builtins/boolean.d.ts +2 -0
  250. package/dist/semantic-types/builtins/boolean.js +1 -0
  251. package/dist/semantic-types/builtins/category.d.ts +2 -0
  252. package/dist/semantic-types/builtins/category.js +1 -0
  253. package/dist/semantic-types/builtins/currency.d.ts +2 -0
  254. package/dist/semantic-types/builtins/currency.js +1 -0
  255. package/dist/semantic-types/builtins/date.d.ts +2 -0
  256. package/dist/semantic-types/builtins/date.js +1 -0
  257. package/dist/semantic-types/builtins/duration.d.ts +2 -0
  258. package/dist/semantic-types/builtins/duration.js +1 -0
  259. package/dist/semantic-types/builtins/end-date.d.ts +2 -0
  260. package/dist/semantic-types/builtins/end-date.js +1 -0
  261. package/dist/semantic-types/builtins/entity.d.ts +2 -0
  262. package/dist/semantic-types/builtins/entity.js +1 -0
  263. package/dist/semantic-types/builtins/file.d.ts +2 -0
  264. package/dist/semantic-types/builtins/file.js +1 -0
  265. package/dist/semantic-types/builtins/human-name.d.ts +2 -0
  266. package/dist/semantic-types/builtins/human-name.js +1 -0
  267. package/dist/semantic-types/builtins/id.d.ts +2 -0
  268. package/dist/semantic-types/builtins/id.js +1 -0
  269. package/dist/semantic-types/builtins/image.d.ts +2 -0
  270. package/dist/semantic-types/builtins/image.js +1 -0
  271. package/dist/semantic-types/builtins/index.d.ts +2 -0
  272. package/dist/semantic-types/builtins/index.js +1 -0
  273. package/dist/semantic-types/builtins/int.d.ts +2 -0
  274. package/dist/semantic-types/builtins/int.js +1 -0
  275. package/dist/semantic-types/builtins/name.d.ts +2 -0
  276. package/dist/semantic-types/builtins/name.js +1 -0
  277. package/dist/semantic-types/builtins/number.d.ts +2 -0
  278. package/dist/semantic-types/builtins/number.js +1 -0
  279. package/dist/semantic-types/builtins/object.d.ts +3 -0
  280. package/dist/semantic-types/builtins/object.js +1 -0
  281. package/dist/semantic-types/builtins/order.d.ts +2 -0
  282. package/dist/semantic-types/builtins/order.js +1 -0
  283. package/dist/semantic-types/builtins/percentage.d.ts +2 -0
  284. package/dist/semantic-types/builtins/percentage.js +1 -0
  285. package/dist/semantic-types/builtins/start-date.d.ts +2 -0
  286. package/dist/semantic-types/builtins/start-date.js +1 -0
  287. package/dist/semantic-types/builtins/string.d.ts +2 -0
  288. package/dist/semantic-types/builtins/string.js +1 -0
  289. package/dist/semantic-types/builtins/timestamp.d.ts +2 -0
  290. package/dist/semantic-types/builtins/timestamp.js +1 -0
  291. package/dist/semantic-types/index.d.ts +9 -0
  292. package/dist/semantic-types/index.js +1 -0
  293. package/dist/semantic-types/primal-types/date.d.ts +2 -0
  294. package/dist/semantic-types/primal-types/date.js +1 -0
  295. package/dist/semantic-types/primal-types/index.d.ts +2 -0
  296. package/dist/semantic-types/primal-types/index.js +1 -0
  297. package/dist/semantic-types/registry.d.ts +14 -0
  298. package/dist/semantic-types/registry.js +1 -0
  299. package/dist/semantic-types/resolve-schema.d.ts +4 -0
  300. package/dist/semantic-types/resolve-schema.js +1 -0
  301. package/dist/semantic-types/types.d.ts +90 -0
  302. package/dist/semantic-types/validate-resolved-schema.d.ts +6 -0
  303. package/dist/semantic-types/validate-resolved-schema.js +1 -0
  304. package/dist/utils/clone.d.ts +1 -0
  305. package/dist/utils/clone.js +1 -0
  306. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,448 @@
1
+ # SemanticDB
2
+
3
+ SemanticDB translates its own LogicForm into SQL and executes it on a single core compute layer.
4
+ Callers provide schemas, schema mappings, operators, and database connection information.
5
+ SemanticDB creates the matching provider and owns its connection lifecycle.
6
+
7
+ ## Public API
8
+
9
+ ```ts
10
+ import { Logicform } from 'semanticdb-v2';
11
+ import { SchemaMappingRegistry } from 'semanticdb-v2';
12
+
13
+ const schemaMappings = new SchemaMappingRegistry([
14
+ {
15
+ schemaId: 'sales',
16
+ connectionId: 'analytics',
17
+ source: 'analytics.sales_detail',
18
+ freshness: { mode: 'ttl', ttlSeconds: 300 },
19
+ },
20
+ ]);
21
+
22
+ const response = await Logicform.execute(
23
+ {
24
+ version: '2.0',
25
+ schema: 'sales',
26
+ groupby: [{
27
+ operator: '$dateBucket',
28
+ pred: 'order_date',
29
+ args: { granularity: 'month' },
30
+ }],
31
+ preds: [{ operator: '$sum', pred: 'amount', name: 'revenue' }],
32
+ },
33
+ {
34
+ connections: [{
35
+ id: 'analytics',
36
+ kind: 'clickhouse',
37
+ url: 'http://localhost:8123',
38
+ username: 'default',
39
+ password: process.env.CLICKHOUSE_PASSWORD,
40
+ database: 'analytics',
41
+ }],
42
+ schemas,
43
+ schemaMappings,
44
+ operators,
45
+ onProgress(event) {
46
+ console.log(event.stage, event);
47
+ },
48
+ },
49
+ );
50
+ ```
51
+
52
+ Every executable LogicForm, including LogicForms used as subqueries, must declare `version`. This
53
+ SDK accepts semantic versions greater than or equal to `2.0`; missing, malformed, or older versions
54
+ fail with `UNSUPPORTED_LOGICFORM_VERSION` before Schema resolution or database access. A relation
55
+ query shaped as `{ schema, query }` is part of its parent LogicForm and inherits the parent's version.
56
+
57
+ PredItems without an explicit `name` receive a human-readable name during normalization. Generated
58
+ 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:
60
+
61
+ ```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})`,
69
+ });
70
+ ```
71
+
72
+ The same locale applies to `preds`, `groupby`, nested expressions, custom-function components,
73
+ and entity-population queries. A PredItem's normalized `query` is included in its
74
+ generated name, for example `门店为上海的销售额合计`. Operator-specific `args` also participate
75
+ through each operator's `defaultName()`; period operators distinguish同比、环比 and other offsets.
76
+ An explicitly authored `name` is preserved unchanged.
77
+
78
+ `entity_id` identifies one entity by `hierarchy.property` on hierarchy Schemas and by the unique
79
+ `type: 'ID'` property on other Schemas. Normalization adds that identity filter unless the query
80
+ already contains one, in which case the explicit query wins. The legacy value `placeholder` is
81
+ preserved as entity context but never becomes a SQL filter. This also applies to relation queries
82
+ shaped as `{ schema, query, entity_id }`. `representation` is presentation metadata for frontend
83
+ consumers and does not change normalization, planning, or execution. See
84
+ [Hierarchy plugin](docs/hierarchy-plugin.md) for Schema configuration, `$hierarchyLevel`, legacy
85
+ compatibility, and its plugin boundary.
86
+
87
+ Compound capabilities can be supplied by external packages through `ExecuteOptions.plugins`.
88
+ Plugins may own Schema metadata, legacy conversion, `entity_id` selection, and Operators while
89
+ remaining scoped to one execution. See [Register an external plugin](docs/register-plugin.md).
90
+
91
+ Parent-child Schemas may declare `parentProperty`. `$recursive` filters a property to a root node and
92
+ all descendants; object properties infer the tree Schema from `ref`. MySQL, Doris, StarRocks, and
93
+ PostgreSQL compile this as a parameterized recursive CTE.
94
+
95
+ Top-level projections may set `schema` to read metrics from another registered Schema. SemanticDB
96
+ splits those projections into independent provider queries, aligns shared query/groupby dimensions,
97
+ and merges rows by the group keys before applying `having`, `sort`, `limitBy`, and pagination.
98
+
99
+ The progress stream emits `started`, `validated`, `normalized`, `planned`, `lineage`,
100
+ `compiled`, `executing`, `hydrating`, `result`, and `completed`. Every event includes a cumulative
101
+ `partialResult`; for example, `compiled` includes `sqls`, while `result` includes `sqls` and
102
+ `result`. Failures emit `failed` before rejection and preserve the partial result accumulated so
103
+ far.
104
+
105
+ Each `sqls` item keeps the executable statement and its parameters together, plus a complete
106
+ SQL string for diagnostics:
107
+
108
+ ```ts
109
+ interface ExecutedSql {
110
+ type: 'semantic-resolution' | 'computation';
111
+ sql: string;
112
+ parameters: readonly unknown[];
113
+ displaySql: string;
114
+ }
115
+ ```
116
+
117
+ `semantic-resolution` identifies supporting SQL used to resolve semantic input or output, such as
118
+ latest-data-date lookup and object entity population. `computation` identifies SQL that calculates
119
+ the requested LogicForm result. `sqls` includes every physical SQL involved in the execution; an
120
+ entry remains visible when its result is served by the query cache.
121
+
122
+ Providers execute only parameterized `sql + parameters`; SemanticDB never executes `displaySql`.
123
+ Because `displaySql` contains literal values and may expose sensitive data, use it only in trusted
124
+ debugging and observability channels.
125
+
126
+ ### Permissions
127
+
128
+ Pass table, row, and column permissions together through `ExecuteOptions.permissions`:
129
+
130
+ ```ts
131
+ import type { Permissions } from 'semanticdb-v2';
132
+
133
+ const permissions = {
134
+ orders: {
135
+ rowPermissions: {
136
+ 订单状态: { $ne: '已删除' },
137
+ 所属部门: {
138
+ schema: 'departments',
139
+ query: {
140
+ 部门名称: { $in: ['华东销售部', '华南销售部'] },
141
+ },
142
+ },
143
+ },
144
+ columnPermissions: {
145
+ whitelist: ['ID', '订单状态', '销售额', '所属部门', 'createdAt'],
146
+ },
147
+ },
148
+ departments: {
149
+ rowPermissions: {
150
+ 部门名称: { $in: ['华东销售部', '华南销售部'] },
151
+ },
152
+ columnPermissions: {
153
+ whitelist: ['ID', '部门名称'],
154
+ },
155
+ },
156
+ } satisfies Permissions;
157
+
158
+ await Logicform.execute(logicform, {
159
+ schemas,
160
+ schemaMappings,
161
+ connection,
162
+ permissions,
163
+ });
164
+ ```
165
+
166
+ When `permissions` is omitted, execution remains unrestricted. When it is present, its Schema keys
167
+ are the table allowlist. `rowPermissions` is always combined with the caller query using `AND`,
168
+ including internal latest-data-date queries, related-table joins, and object population queries.
169
+ `columnPermissions.whitelist` accepts property names or IDs and restricts caller projections,
170
+ filters, grouping, operator inputs, and relation paths. A row policy may still use a non-whitelisted
171
+ column as an internal filter. If an ID is not whitelisted, SemanticDB generates the executor-owned
172
+ result-row `_id` instead of exposing the physical ID as that identity.
173
+
174
+ ### Custom composite metrics
175
+
176
+ Pass legacy-compatible `genComponents`, `mergeComponents`, and `genMergeSQL` definitions through
177
+ `ExecuteOptions.customFunctions`. JavaScript merge supports async callbacks; SQL merge compiles
178
+ components into CTEs. `Logicform.genComponents()` expands the same definitions into an indicator
179
+ tree without accessing a database. Cross-Schema components independently pass table, row, and
180
+ column permissions. See [Custom composite metrics](docs/custom-functions.md) for the complete
181
+ contract, merge-mode selection, and trusted function-string behavior.
182
+
183
+ The root entry point accepts a `connections` array of `DatabaseConnection` items. Every item has a unique `id`; set `kind` to `mysql`,
184
+ `doris`, `starrocks`, `clickhouse`, `snowflake`, `oracle`, or `postgresql`, then add that driver's
185
+ native connection fields directly on the item. Each Schema Mapping selects an item through
186
+ `connectionId`. SemanticDB lazily creates and reuses the matching internal provider. Call
187
+ `await Logicform.close()` during application shutdown to close every connection pool and session.
188
+
189
+ ### PostgreSQL connection
190
+
191
+ PostgreSQL uses the official `pg` pool and PostgreSQL-native `$1`, `$2` parameters:
192
+
193
+ ```ts
194
+ await Logicform.execute(logicform, {
195
+ schemas,
196
+ schemaMappings,
197
+ connections: [{
198
+ id: 'primary',
199
+ kind: 'postgresql',
200
+ connectionString: process.env.DATABASE_URL,
201
+ }],
202
+ });
203
+ ```
204
+
205
+ ### Doris and StarRocks connections
206
+
207
+ Doris and StarRocks expose MySQL-compatible protocols, so both use `mysql2` connection fields:
208
+
209
+ ```ts
210
+ const doris = { id: 'doris-main', kind: 'doris', host, port, user, password, database } as const;
211
+ const starrocks = { id: 'starrocks-main', kind: 'starrocks', host, port, user, password, database } as const;
212
+ ```
213
+
214
+ They are independent providers with distinct `kind` values. Their currently compatible identifier,
215
+ pagination, date, aggregate, string, regex, parameter, and display-SQL behavior comes from a shared
216
+ MySQL-compatible SQL helper. Database-specific differences should be implemented by overriding the
217
+ corresponding internal dialect capability.
218
+
219
+ ### Snowflake connection
220
+
221
+ Snowflake connection fields are passed to the official `snowflake-sdk`:
222
+
223
+ ```ts
224
+ await Logicform.execute(logicform, {
225
+ schemas,
226
+ schemaMappings,
227
+ connections: [{
228
+ id: 'warehouse',
229
+ kind: 'snowflake',
230
+ account: process.env.SNOWFLAKE_ACCOUNT,
231
+ username: process.env.SNOWFLAKE_USERNAME,
232
+ password: process.env.SNOWFLAKE_PASSWORD,
233
+ warehouse: process.env.SNOWFLAKE_WAREHOUSE,
234
+ database: process.env.SNOWFLAKE_DATABASE,
235
+ schema: process.env.SNOWFLAKE_SCHEMA,
236
+ role: process.env.SNOWFLAKE_ROLE,
237
+ }],
238
+ });
239
+ ```
240
+
241
+ ### Oracle connection
242
+
243
+ Oracle uses the official `oracledb` driver in Thin mode and does not require Oracle Client
244
+ libraries:
245
+
246
+ ```ts
247
+ await Logicform.execute(logicform, {
248
+ schemas,
249
+ schemaMappings,
250
+ connections: [{
251
+ id: 'oracle-main',
252
+ kind: 'oracle',
253
+ user: process.env.ORACLE_USER,
254
+ password: process.env.ORACLE_PASSWORD,
255
+ connectString: process.env.ORACLE_CONNECT_STRING,
256
+ }],
257
+ });
258
+ ```
259
+
260
+ ### Semantic types
261
+
262
+ `Schema.properties[].type` is an extensible semantic type. SemanticDB supplies the built-in
263
+ types and derives each property's `primal_type` while resolving the Schema. A supplied
264
+ `primal_type` must match the registered type. `isArray` describes the value container and
265
+ defaults to `false`; for example, tags are represented as `type: 'category', isArray: true`.
266
+
267
+ ```ts
268
+ import { createSemanticTypeRegistry, Logicform } from 'semanticdb-v2';
269
+
270
+ const semanticTypes = createSemanticTypeRegistry();
271
+ semanticTypes.register({
272
+ name: 'email',
273
+ primalType: 'string',
274
+ validateProperty(property) {
275
+ if (property.isArray) throw new Error('email must be scalar');
276
+ },
277
+ });
278
+
279
+ await Logicform.execute(logicform, { schemas, schemaMappings, connection, semanticTypes });
280
+ ```
281
+
282
+ Registries are instance-local. Applications can reuse one registry across executions without
283
+ introducing global mutable registration.
284
+
285
+ See [Register a custom SemanticType](docs/register-semantic-type.md) for the complete external
286
+ registration API, validation lifecycle, and Schema example.
287
+
288
+ ### SQL-derived properties
289
+
290
+ A Property can map to a trusted, provider-specific SQL expression instead of one physical column:
291
+
292
+ ```ts
293
+ {
294
+ id: 'net_amount',
295
+ name: 'Net amount',
296
+ type: 'currency',
297
+ udf: { sql: 'COALESCE(amount, 0) - COALESCE(discount, 0)' },
298
+ }
299
+ ```
300
+
301
+ `udf.sql` is used consistently for projection, filtering, grouping, operator input, and relation
302
+ keys. In an event Schema, `$TS` expands to the quoted physical timestamp column. The expression is
303
+ trusted model metadata, must match the active provider dialect, and cannot contain statements,
304
+ comments, or parameter placeholders. Property UDFs do not support the legacy `function` or
305
+ `dependencies` fields; application-side computed values belong outside this query SDK.
306
+
307
+ ### Query cache
308
+
309
+ All compiled provider queries, including the internal latest-data-date query, can share one
310
+ caller-owned query cache. Redis is published from `semanticdb-v2/cache/redis`; cache identity
311
+ includes namespace, provider, SQL, and parameters. Physical expiration belongs to each Schema
312
+ mapping's `freshness` policy. See [Query cache and Redis](docs/query-cache.md).
313
+
314
+ When Redis is shared with other applications, pass an application-specific key prefix:
315
+
316
+ ```ts
317
+ import { createRedisQueryCache } from 'semanticdb-v2/cache/redis';
318
+
319
+ const cache = await createRedisQueryCache({
320
+ url: process.env.REDIS_URL,
321
+ keyPrefix: process.env.REDIS_KEY_PREFIX,
322
+ });
323
+ ```
324
+
325
+ Operators convert SQL expressions and declare the semantic type of their single output column. See
326
+ [LogicForm operators](docs/operators.md) for the built-in syntax and composition rules, and
327
+ [Register an operator](docs/register-operator.md) for the extension contract.
328
+
329
+ Schema and Property metadata use only `id`; neither defines `sid`. See the
330
+ [breaking-change migration checklist](docs/breaking-change-schema-property-id.md) before
331
+ updating an upstream Schema producer or downstream consumer.
332
+
333
+ ## Architecture
334
+
335
+ The execution pipeline is split into explicit layers:
336
+
337
+ ```text
338
+ LogicForm
339
+ -> normalizer validates and canonicalizes the request
340
+ -> planner builds a provider-independent LogicalPlan and lineage
341
+ -> compiler renders the LogicalPlan through the provider contract
342
+ -> physical executes SQL through a concrete database provider
343
+ ```
344
+
345
+ `executor` is the application layer that coordinates the pipeline and publishes progress
346
+ events. `semantic-types` validates and resolves Schema property types before normalization;
347
+ `schema` owns Schema lookup and logical field mapping, while `schema-mappings` owns the
348
+ physical source bindings. Each layer owns its types, while the provider contract and concrete
349
+ database adapters live in `physical`. Physical adapters do not own normalization,
350
+ planning, or compilation rules.
351
+
352
+ After physical execution, the executor's Result Hydrator runs SemanticType result hooks and
353
+ batch-populates directly selected object properties. See [Result Hydrator](docs/result-hydrator.md).
354
+
355
+ Each layer exposes an `index.ts` boundary. Cross-layer imports should use that boundary rather
356
+ than reach into another layer's implementation files.
357
+
358
+ See [LogicForm to SQL pipeline](docs/logicform-to-sql-pipeline.md) for the responsibility and
359
+ input/output contract of every stage.
360
+
361
+ The [legacy test migration report](docs/legacy-test-migration.md) records how the old project's
362
+ test suite maps to this rewrite, including migrated behavior and explicit non-migration reasons.
363
+
364
+ Scalar object properties can be traversed with paths such as `门店_城市`. The planner resolves
365
+ these paths into provider-independent LEFT JOIN plans. See
366
+ [Object relations and LEFT JOIN](docs/object-relations.md).
367
+
368
+ ## MySQL test configuration
369
+
370
+ The test suite includes a real MySQL execution test. Configure it through the project `.env`:
371
+
372
+ ```bash
373
+ cp .env.example .env
374
+ npm test
375
+ ```
376
+
377
+ The supported variables are `TEST_MYSQL_HOST`, `TEST_MYSQL_PORT`, `TEST_MYSQL_USER`,
378
+ `TEST_MYSQL_PASSWORD`, and `TEST_MYSQL_DATABASE`. The test creates an isolated table, verifies
379
+ the returned aggregates, and removes the table afterward. If `TEST_MYSQL_DATABASE` is omitted,
380
+ it creates and reuses the isolated `semanticdb_v2_integration` database.
381
+
382
+ ## PostgreSQL test configuration
383
+
384
+ Set `TEST_POSTGRESQL_URL` in `.env` to enable the real PostgreSQL integration test. It creates an
385
+ isolated table in the configured database, verifies parameterized execution and LogicForm
386
+ aggregation, and removes the table afterward.
387
+
388
+ ## Redis test configuration
389
+
390
+ The test suite also includes a real Redis integration test. Configure the connection in `.env`:
391
+
392
+ ```bash
393
+ REDIS_URL=redis://localhost:6379/0
394
+ REDIS_KEY_PREFIX=semanticdb-v2
395
+ ```
396
+
397
+ `REDIS_KEY_PREFIX` is the application namespace and should be unique when Redis is shared. The
398
+ test appends its own process ID and UUID, verifies real expiry and source-index behavior, and
399
+ removes its keys afterward. It is skipped only when `REDIS_URL` is absent; a configured but
400
+ unreachable Redis server fails the test.
401
+
402
+ ## Snowflake test configuration
403
+
404
+ The ordinary test suite includes a real Snowflake provider and LogicForm execution test. Uncomment
405
+ and fill the `TEST_SNOWFLAKE_*` variables in `.env`; `ACCOUNT`, `USERNAME`, `PASSWORD`, `WAREHOUSE`,
406
+ and `DATABASE` are required, while `SCHEMA` and `ROLE` are optional. The test only reads
407
+ `INFORMATION_SCHEMA.TABLES`. It is skipped when the required configuration is absent and fails when
408
+ configured credentials cannot connect.
409
+
410
+ ## Oracle test configuration
411
+
412
+ The ordinary test suite includes real Oracle provider and LogicForm execution tests. Uncomment
413
+ and fill `TEST_ORACLE_USER`, `TEST_ORACLE_PASSWORD`, and `TEST_ORACLE_CONNECT_STRING` in `.env`.
414
+ Thin mode is the default. To test Thick mode, set `TEST_ORACLE_MODE=thick` and optionally set
415
+ `TEST_ORACLE_CLIENT_LIB_DIR` and `TEST_ORACLE_CONFIG_DIR`. The tests query Oracle's `DUAL` table;
416
+ they are skipped when required connection values are absent and fail when configured Oracle
417
+ credentials, client libraries, or connectivity are invalid.
418
+
419
+ ## Debugging the pipeline
420
+
421
+ [`debug/debug.ts`](debug/debug.ts) prepares an isolated MySQL table and executes one complete
422
+ LogicForm pipeline against it. It uses the same `TEST_MYSQL_*` variables from `.env` as the
423
+ integration tests.
424
+
425
+ In Trae or VS Code, open **Run and Debug**, select **Debug SemanticDB pipeline**, set a breakpoint
426
+ on the `Logicform.execute()` call or inside `src/`, and start debugging. The same entry point can
427
+ be run without the IDE:
428
+
429
+ ```bash
430
+ npm run debug
431
+ ```
432
+
433
+ ## Current scope
434
+
435
+ - ClickHouse, MySQL, Oracle, and Snowflake core compute providers
436
+ - Projections, conditional aggregates, and `$sum`, `$count`, `$countDistinct`, `$avg`, `$min`, `$max`
437
+ - Structured arithmetic, share-of-total, period comparison, period-boundary, date-bucket,
438
+ cumulative period, trusted SQL expression, string, case, and window operators
439
+ - Declarative operators
440
+ - Grouping with hour/day/week/month/quarter/year time buckets
441
+ - `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$exists`, `$contains`, `$regex`
442
+ - `$and` and `$or`, HAVING, sorting, row-count and percentage pagination, and per-group `limitBy`
443
+ - Query-level and output-column lineage
444
+ - Multi-level scalar and array object relation paths, array groupby, and batched entity population
445
+ - Nested `from` LogicForms compiled as parameterized derived tables
446
+
447
+ Exports and metadata persistence are intentionally deferred. Directly selected array-object
448
+ properties are populated without expanding the root result rows.
@@ -0,0 +1,5 @@
1
+ import type { SourceFreshnessPolicy } from '../schema-mappings/index';
2
+ export declare const nextDailyExpiration: (policy: Extract<SourceFreshnessPolicy, {
3
+ readonly mode: "daily";
4
+ }>, now: Date) => Date;
5
+ export declare const resolveQueryExpiration: (policies: readonly SourceFreshnessPolicy[], now: Date) => Date | undefined;
@@ -0,0 +1 @@
1
+ import{parseDailyExpireAt as s}from"../schema-mappings/freshness.js";const i=(n,o)=>{const r=new Intl.DateTimeFormat("en-US",{timeZone:o,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(n),e=a=>Number(r.find(t=>t.type===a)?.value);return{year:e("year"),month:e("month"),day:e("day"),hour:e("hour"),minute:e("minute"),second:e("second")}},m=(n,o)=>{const r=Date.UTC(n.year,n.month-1,n.day,n.hour,n.minute,n.second);let e=r;for(let a=0;a<3;a+=1){const t=i(new Date(e),o),u=Date.UTC(t.year,t.month-1,t.day,t.hour,t.minute,t.second);e+=r-u}return new Date(e)},d=(n,o)=>{const r=s(n.expireAt);if(!r)throw new Error(`Invalid daily expiration time: ${n.expireAt}`);if(!n.timeZone){const t=new Date(o);return t.setHours(r.hour,r.minute,r.second,0),t.getTime()<=o.getTime()&&t.setDate(t.getDate()+1),t}const e=i(o,n.timeZone);let a=m({...e,...r},n.timeZone);if(a.getTime()<=o.getTime()){const t=new Date(Date.UTC(e.year,e.month-1,e.day+1));a=m({year:t.getUTCFullYear(),month:t.getUTCMonth()+1,day:t.getUTCDate(),...r},n.timeZone)}return a},h=(n,o)=>{const r=n.flatMap(e=>e.mode==="manual"?[]:e.mode==="ttl"?[new Date(o.getTime()+e.ttlSeconds*1e3)]:[d(e,o)]);return r.length===0?void 0:new Date(Math.min(...r.map(e=>e.getTime())))};export{d as nextDailyExpiration,h as resolveQueryExpiration};
@@ -0,0 +1,3 @@
1
+ import type { QueryCacheIdentity } from './types';
2
+ export declare const queryCacheIdentityHash: (identity: QueryCacheIdentity) => string;
3
+ export declare const sourceCacheIndexHash: (namespace: string, source: string) => string;
@@ -0,0 +1 @@
1
+ import{createHash as s}from"node:crypto";const n=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(n):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).sort(([r],[t])=>r.localeCompare(t)).map(([r,t])=>[r,n(t)])):e,i=e=>s("sha256").update(JSON.stringify(n(e))).digest("hex"),o=e=>i({namespace:e.namespace,provider:e.provider,sql:e.sql,parameters:e.parameters}),p=(e,r)=>i({namespace:e,source:r});export{o as queryCacheIdentityHash,p as sourceCacheIndexHash};
@@ -0,0 +1,3 @@
1
+ export { nextDailyExpiration, resolveQueryExpiration, } from './expiry';
2
+ export { queryCacheIdentityHash, sourceCacheIndexHash, } from './identity';
3
+ export type { QueryCache, QueryCacheExecutionOptions, QueryCacheIdentity, QueryCacheWriteOptions, } from './types';
@@ -0,0 +1,37 @@
1
+ import type { QueryCache, QueryCacheIdentity, QueryCacheWriteOptions } from './types';
2
+ import type { DataRow } from '../physical/index';
3
+ export interface RedisMultiLike {
4
+ set(key: string, value: string, options?: {
5
+ readonly PXAT: number;
6
+ }): this;
7
+ zRemRangeByScore(key: string, min: number, max: number): this;
8
+ zAdd(key: string, entry: {
9
+ readonly score: number;
10
+ readonly value: string;
11
+ }): this;
12
+ exec(): Promise<unknown>;
13
+ }
14
+ export interface RedisQueryCacheClient {
15
+ get(key: string): Promise<string | null>;
16
+ multi(): RedisMultiLike;
17
+ zRange(key: string, start: number, stop: number): Promise<string[]>;
18
+ del(keys: string | string[]): Promise<number>;
19
+ close(): Promise<unknown>;
20
+ }
21
+ export interface RedisQueryCacheOptions {
22
+ readonly url?: string;
23
+ readonly keyPrefix?: string;
24
+ }
25
+ /** Redis implementation of the provider-independent QueryCache contract. */
26
+ export declare class RedisQueryCache implements QueryCache {
27
+ #private;
28
+ constructor(client: RedisQueryCacheClient, options?: {
29
+ readonly keyPrefix?: string;
30
+ readonly ownsClient?: boolean;
31
+ });
32
+ get(identity: QueryCacheIdentity): Promise<readonly DataRow[] | undefined>;
33
+ set(identity: QueryCacheIdentity, rows: readonly DataRow[], options: QueryCacheWriteOptions): Promise<void>;
34
+ invalidateSource(namespace: string, source: string): Promise<void>;
35
+ close(): Promise<void>;
36
+ }
37
+ export declare const createRedisQueryCache: (options?: RedisQueryCacheOptions) => Promise<RedisQueryCache>;
@@ -0,0 +1 @@
1
+ import{createClient as y}from"redis";import{queryCacheIdentityHash as d,sourceCacheIndexHash as f}from"./identity.js";import{deserializeQueryRows as w,serializeQueryRows as x}from"./serialization.js";const m=Number.MAX_SAFE_INTEGER;class u{#e;#t;#s;constructor(e,t={}){this.#e=e,this.#t=t.keyPrefix??"semanticdb:cache",this.#s=t.ownsClient??!1}#r(e){return`${this.#t}:query:${d(e)}`}#i(e,t){return`${this.#t}:source:${f(e,t)}`}async get(e){const t=await this.#e.get(this.#r(e));if(t!==null)return w(t)}async set(e,t,i){const s=this.#r(e),a=x(t),c=i.expiresAt?.getTime(),l=c??m,n=this.#e.multi();c===void 0?n.set(s,a):n.set(s,a,{PXAT:c});for(const h of new Set(i.sources)){const o=this.#i(e.namespace,h);n.zRemRangeByScore(o,0,Date.now()),n.zAdd(o,{score:l,value:s})}await n.exec()}async invalidateSource(e,t){const i=this.#i(e,t),s=await this.#e.zRange(i,0,-1);s.length>0&&await this.#e.del(s),await this.#e.del(i)}async close(){this.#s&&await this.#e.close()}}const C=async(r={})=>{const e=y(r.url?{url:r.url}:{});return e.on("error",()=>{}),await e.connect(),new u(e,{...r.keyPrefix?{keyPrefix:r.keyPrefix}:{},ownsClient:!0})};export{u as RedisQueryCache,C as createRedisQueryCache};
@@ -0,0 +1,3 @@
1
+ import type { DataRow } from '../physical/index';
2
+ export declare const serializeQueryRows: (rows: readonly DataRow[]) => string;
3
+ export declare const deserializeQueryRows: (serialized: string) => DataRow[] | undefined;
@@ -0,0 +1 @@
1
+ const n=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(n)}:e&&typeof e=="object"?{type:"object",value:Object.entries(e).map(([r,t])=>[r,n(t)])}:e,i=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(i);case"object":return Object.fromEntries(e.value.map(([r,t])=>[r,i(t)]))}},s=e=>JSON.stringify(n(e)),o=e=>{const r=i(JSON.parse(e));if(!(!Array.isArray(r)||r.some(t=>!t||typeof t!="object"||Array.isArray(t))))return r};export{o as deserializeQueryRows,s as serializeQueryRows};
@@ -0,0 +1,27 @@
1
+ import type { CoreComputeProvider, DataRow, SqlParameters } from '../physical/index';
2
+ export interface QueryCacheIdentity {
3
+ readonly namespace: string;
4
+ readonly provider: CoreComputeProvider['kind'];
5
+ readonly sql: string;
6
+ readonly parameters: SqlParameters;
7
+ }
8
+ export interface QueryCacheWriteOptions {
9
+ readonly sources: readonly string[];
10
+ /** Omitted for manually invalidated entries. */
11
+ readonly expiresAt?: Date;
12
+ }
13
+ /**
14
+ * Cross-execution cache for compiled provider queries. Implementations must
15
+ * treat SQL parameters and the data-source namespace as part of the identity.
16
+ */
17
+ export interface QueryCache {
18
+ get(identity: QueryCacheIdentity): Promise<readonly DataRow[] | undefined>;
19
+ set(identity: QueryCacheIdentity, rows: readonly DataRow[], options: QueryCacheWriteOptions): Promise<void>;
20
+ invalidateSource(namespace: string, source: string): Promise<void>;
21
+ close(): Promise<void>;
22
+ }
23
+ export interface QueryCacheExecutionOptions {
24
+ readonly store: QueryCache;
25
+ /** Isolates tenants and physical data sources that can produce identical SQL. */
26
+ readonly namespace: string;
27
+ }
@@ -0,0 +1,7 @@
1
+ /** Fixed internal physical table names. These are intentionally not configurable yet. */
2
+ export declare const CALENDAR_DATES_TABLE = "semanticdb_calendar_dates";
3
+ export declare const CALENDAR_RELATIONS_TABLE = "semanticdb_calendar_relations";
4
+ export declare const CALENDAR_SYSTEM_SCHEMA_IDS: {
5
+ readonly dates: "__semanticdb_calendar_dates";
6
+ readonly relations: "__semanticdb_calendar_relations";
7
+ };
@@ -0,0 +1 @@
1
+ const _="semanticdb_calendar_dates",a="semanticdb_calendar_relations",e={dates:"__semanticdb_calendar_dates",relations:"__semanticdb_calendar_relations"};export{_ as CALENDAR_DATES_TABLE,a as CALENDAR_RELATIONS_TABLE,e as CALENDAR_SYSTEM_SCHEMA_IDS};
@@ -0,0 +1,6 @@
1
+ export { CALENDAR_DATES_TABLE, CALENDAR_RELATIONS_TABLE, CALENDAR_SYSTEM_SCHEMA_IDS, } from './constants';
2
+ export { resolveCalendarPeriod, resolveCalendarPeriodFromStore, selectCalendarDates, } from './resolver';
3
+ export type { CalendarRelationStore } from './resolver';
4
+ export { ProviderCalendarRelationStore } from './provider-relation-store';
5
+ export { calendarSystemSchemas } from './system-schemas';
6
+ export type { CalendarAlignment, CalendarComparison, CalendarDateRow, CalendarRelation, CalendarRelationRow, ResolvedCalendarPeriod, } from './types';
@@ -0,0 +1,9 @@
1
+ import type { CoreComputeProvider } from '../physical/index';
2
+ import type { CalendarRelationStore } from './resolver';
3
+ import type { CalendarComparison, CalendarRelationRow } from './types';
4
+ /** Loads calendar mappings from the fixed internal relations table. */
5
+ export declare class ProviderCalendarRelationStore implements CalendarRelationStore {
6
+ private readonly provider;
7
+ constructor(provider: CoreComputeProvider);
8
+ loadRelations(currentDateIds: readonly (string | number)[], comparison: CalendarComparison): Promise<readonly CalendarRelationRow[]>;
9
+ }
@@ -0,0 +1 @@
1
+ import{CALENDAR_RELATIONS_TABLE as s}from"./constants.js";const g=["source_date_id","target_date_id","calendar","grain","relation","relation_key"];class _{provider;constructor(t){this.provider=t}async loadRelations(t,i){if(t.length===0)return[];const n=[...t,i.calendar,i.grain,i.relation];i.key!==void 0&&n.push(i.key);const l=t.map((e,d)=>this.provider.parameter(d,e)),a=e=>this.provider.parameter(e,n[e]),r=e=>this.provider.quoteIdentifier(e),o=[`${r("source_date_id")} IN (${l.join(", ")})`,`${r("calendar")} = ${a(t.length)}`,`${r("grain")} = ${a(t.length+1)}`,`${r("relation")} = ${a(t.length+2)}`];return i.key!==void 0&&o.push(`${r("relation_key")} = ${a(t.length+3)}`),(await this.provider.execute({sql:`SELECT ${g.map(r).join(", ")} FROM ${r(s)} WHERE ${o.join(" AND ")}`,parameters:n})).map(e=>({sourceDateId:e.source_date_id,targetDateId:e.target_date_id,calendar:String(e.calendar),grain:String(e.grain),relation:String(e.relation),...typeof e.relation_key=="string"||typeof e.relation_key=="number"?{key:String(e.relation_key)}:{}}))}}export{_ as ProviderCalendarRelationStore};
@@ -0,0 +1,10 @@
1
+ import type { CalendarComparison, CalendarDateRow, CalendarRelationRow, ResolvedCalendarPeriod } from './types';
2
+ export interface CalendarRelationStore {
3
+ loadRelations(currentDateIds: readonly (string | number)[], comparison: CalendarComparison): Promise<readonly CalendarRelationRow[]>;
4
+ }
5
+ /** Resolve current natural-day IDs through an explicit calendar relation. */
6
+ export declare const resolveCalendarPeriod: (currentDateIds: readonly (string | number)[], comparison: CalendarComparison, relations: readonly CalendarRelationRow[]) => ResolvedCalendarPeriod;
7
+ /** Async resolver boundary used by the executor; storage remains provider-owned. */
8
+ export declare const resolveCalendarPeriodFromStore: (currentDateIds: readonly (string | number)[], comparison: CalendarComparison, store: CalendarRelationStore) => Promise<ResolvedCalendarPeriod>;
9
+ /** Select natural days by calendar coordinates before resolving a relation. */
10
+ export declare const selectCalendarDates: (rows: readonly CalendarDateRow[], predicate: Partial<Pick<CalendarDateRow, "calendar" | "grain" | "periodKey" | "relativeDay">>) => readonly (string | number)[];
@@ -0,0 +1 @@
1
+ const l=n=>`${typeof n}:${String(n)}`,p=(n,t)=>{const r=typeof n=="number"?n:Number(n),o=typeof t=="number"?t:Number(t);return Number.isFinite(r)&&Number.isFinite(o)?r-o:String(n).localeCompare(String(t))},m=(n,t,r)=>{const o=r.filter(e=>e.calendar===t.calendar&&e.grain===t.grain&&e.relation===t.relation&&(t.key===void 0||e.key===t.key)),d=new Map(o.map(e=>[l(e.sourceDateId),e])),b=n.flatMap(e=>{const a=d.get(l(e))?.targetDateId;return a==null?[]:[a]}),f=[...new Map(b.map(e=>[l(e),e])).values()].sort(p),s=[];for(const e of f){const a=s[s.length-1];if(a&&typeof a.end=="number"&&typeof e=="number"&&e===a.end+1)a.end=e;else if(a&&String(a.end).match(/^\d{8}$/)&&String(e).match(/^\d{8}$/)){const i=Number(a.end),c=Number(e),u=new Date(Date.UTC(Math.floor(i/1e4),Math.floor(i/100)%100-1,i%100));u.setUTCDate(u.getUTCDate()+1);const g=new Date(Date.UTC(Math.floor(c/1e4),Math.floor(c/100)%100-1,c%100));if(u.getTime()===g.getTime()){a.end=e;continue}s.push({start:e,end:e})}else s.push({start:e,end:e})}return{dateIds:f,ranges:s}},y=async(n,t,r)=>m(n,t,await r.loadRelations(n,t)),D=(n,t)=>n.filter(r=>Object.entries(t).every(([o,d])=>r[o]===d)).map(r=>r.dateId);export{m as resolveCalendarPeriod,y as resolveCalendarPeriodFromStore,D as selectCalendarDates};
@@ -0,0 +1,3 @@
1
+ import type { SemanticSchemaDraft } from '../semantic-types/index';
2
+ /** Internal logical metadata for the fixed calendar tables. Not part of caller schemas. */
3
+ export declare const calendarSystemSchemas: Readonly<Record<string, SemanticSchemaDraft>>;
@@ -0,0 +1 @@
1
+ import{CALENDAR_RELATIONS_TABLE as t,CALENDAR_SYSTEM_SCHEMA_IDS as e,CALENDAR_DATES_TABLE as i}from"./constants.js";const r={[e.dates]:{id:e.dates,name:e.dates,type:"entity",source:i,properties:[{id:"date_id",name:"date_id",type:"id",primal_type:"string"},{id:"calendar",name:"calendar",type:"string",primal_type:"string"},{id:"grain",name:"grain",type:"string",primal_type:"string"},{id:"period_key",name:"period_key",type:"string",primal_type:"string"},{id:"relative_day",name:"relative_day",type:"int",primal_type:"number"}]},[e.relations]:{id:e.relations,name:e.relations,type:"entity",source:t,properties:[{id:"source_date_id",name:"source_date_id",type:"string",primal_type:"string"},{id:"target_date_id",name:"target_date_id",type:"string",primal_type:"string"},{id:"calendar",name:"calendar",type:"string",primal_type:"string"},{id:"grain",name:"grain",type:"string",primal_type:"string"},{id:"relation",name:"relation",type:"string",primal_type:"string"},{id:"relation_key",name:"relation_key",type:"string",primal_type:"string"}]}};export{r as calendarSystemSchemas};
@@ -0,0 +1,31 @@
1
+ export type CalendarRelation = 'previous_period' | 'next_period' | 'previous_occurrence' | 'next_occurrence';
2
+ export type CalendarAlignment = 'same_period' | 'same_elapsed_position' | 'same_relative_day';
3
+ export interface CalendarComparison {
4
+ readonly calendar: string;
5
+ readonly grain: string;
6
+ readonly relation: CalendarRelation;
7
+ readonly alignment?: CalendarAlignment;
8
+ readonly key?: string;
9
+ }
10
+ export interface CalendarDateRow {
11
+ readonly dateId: string | number;
12
+ readonly calendar: string;
13
+ readonly grain: string;
14
+ readonly periodKey?: string | number;
15
+ readonly relativeDay?: number;
16
+ }
17
+ export interface CalendarRelationRow {
18
+ readonly sourceDateId: string | number;
19
+ readonly targetDateId: string | number | null;
20
+ readonly calendar: string;
21
+ readonly grain: string;
22
+ readonly relation: CalendarRelation;
23
+ readonly key?: string;
24
+ }
25
+ export interface ResolvedCalendarPeriod {
26
+ readonly dateIds: readonly (string | number)[];
27
+ readonly ranges: readonly {
28
+ readonly start: string | number;
29
+ readonly end: string | number;
30
+ }[];
31
+ }
@@ -0,0 +1,2 @@
1
+ export { assertNoLegacyLogicformFields, normalizeLegacyLogicform, splitLegacySchemas, } from './logicform';
2
+ export { normalizeSchemas } from '../executor/compatibility';