@hypequery/datasets 0.17.1 → 0.18.0
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.
- package/dist/api.type-test.js +5 -1
- package/dist/cloud-publishing.d.ts +23 -0
- package/dist/cloud-publishing.d.ts.map +1 -0
- package/dist/cloud-publishing.js +57 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/sql-portability.d.ts.map +1 -1
- package/dist/sql-portability.js +88 -19
- package/dist/utils/collect-cloud-datasets.d.ts +3 -0
- package/dist/utils/collect-cloud-datasets.d.ts.map +1 -0
- package/dist/utils/collect-cloud-datasets.js +12 -0
- package/package.json +3 -3
package/dist/api.type-test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { add, between, buildCanonicalSemanticQuerySchemas, buildProtocolDeploymentContract, createDatasetClient, dataset, desc, dimension, eq, measure, publishDatasets, rehydrateProtocolDeploymentContract, } from './index.js';
|
|
1
|
+
import { add, between, buildCanonicalSemanticQuerySchemas, buildProtocolDeploymentContract, createDatasetClient, dataset, desc, dimension, eq, measure, publishDatasets, publishToCloud, rehydrateProtocolDeploymentContract, } from './index.js';
|
|
2
2
|
const Orders = dataset('orders', {
|
|
3
3
|
source: 'orders',
|
|
4
4
|
tenantKey: 'tenant_id',
|
|
@@ -85,6 +85,10 @@ const _publishedDatasets = publishDatasets()
|
|
|
85
85
|
metrics: { customers: customerCountMetric },
|
|
86
86
|
})
|
|
87
87
|
.build();
|
|
88
|
+
const _cloudPublication = publishToCloud({
|
|
89
|
+
datasets: { Orders, Customers },
|
|
90
|
+
access: { roles: [], scopes: [] },
|
|
91
|
+
});
|
|
88
92
|
// @ts-expect-error published metrics must belong to the dataset being published.
|
|
89
93
|
publishDatasets().publish(Orders, { metrics: { customerCountMetric } });
|
|
90
94
|
const canonicalSchemas = buildCanonicalSemanticQuerySchemas({
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ProtocolDeploymentContract } from '@hypequery/protocol';
|
|
2
|
+
import type { AnyDatasetInstance } from './types.js';
|
|
3
|
+
export interface CloudPublishingOptions {
|
|
4
|
+
/** Datasets exposed to Cloud. Relationships are included as supporting datasets. */
|
|
5
|
+
readonly datasets: Readonly<Record<string, AnyDatasetInstance>>;
|
|
6
|
+
/** Explicit policy for every published dataset unless overridden below. */
|
|
7
|
+
readonly access: {
|
|
8
|
+
readonly roles: readonly string[];
|
|
9
|
+
readonly scopes: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
/** Optional stricter or different policy by published dataset name. */
|
|
12
|
+
readonly datasetAccess?: Readonly<Record<string, {
|
|
13
|
+
readonly roles: readonly string[];
|
|
14
|
+
readonly scopes: readonly string[];
|
|
15
|
+
}>>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Declare the dataset-only contract uploaded by `hypequery deploy`.
|
|
19
|
+
* This function performs no network request and requires no Serve API.
|
|
20
|
+
* Export its result as `cloud` from the deployment entrypoint.
|
|
21
|
+
*/
|
|
22
|
+
export declare function publishToCloud(options: CloudPublishingOptions): ProtocolDeploymentContract;
|
|
23
|
+
//# sourceMappingURL=cloud-publishing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-publishing.d.ts","sourceRoot":"","sources":["../src/cloud-publishing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAA+B,MAAM,qBAAqB,CAAC;AAEnG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,MAAM,WAAW,sBAAsB;IACrC,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAChE,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC;IACF,uEAAuE;IACvE,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC,CAAC,CAAC;CACL;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,0BAA0B,CAyD1F"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { buildProtocolDeploymentContract } from './protocol-deployment-adapter.js';
|
|
2
|
+
import { collectCloudDatasets } from './utils/collect-cloud-datasets.js';
|
|
3
|
+
/**
|
|
4
|
+
* Declare the dataset-only contract uploaded by `hypequery deploy`.
|
|
5
|
+
* This function performs no network request and requires no Serve API.
|
|
6
|
+
* Export its result as `cloud` from the deployment entrypoint.
|
|
7
|
+
*/
|
|
8
|
+
export function publishToCloud(options) {
|
|
9
|
+
const entries = Object.entries(options.datasets);
|
|
10
|
+
if (entries.length === 0)
|
|
11
|
+
throw new Error('Cloud publishing requires at least one dataset.');
|
|
12
|
+
if (!options.access || !Array.isArray(options.access.roles)
|
|
13
|
+
|| !Array.isArray(options.access.scopes)) {
|
|
14
|
+
throw new Error('Cloud publishing requires an explicit authenticated access policy.');
|
|
15
|
+
}
|
|
16
|
+
const datasets = new Map();
|
|
17
|
+
const exposed = new Set();
|
|
18
|
+
for (const [, dataset] of entries) {
|
|
19
|
+
collectCloudDatasets(datasets, dataset);
|
|
20
|
+
exposed.add(dataset.name);
|
|
21
|
+
}
|
|
22
|
+
for (const dataset of datasets.values()) {
|
|
23
|
+
const namedMetrics = dataset.metrics;
|
|
24
|
+
if (namedMetrics && Object.keys(namedMetrics).length > 0) {
|
|
25
|
+
throw new Error(`Cloud dataset "${dataset.name}" contains named metrics. `
|
|
26
|
+
+ 'The dataset-only Cloud publisher does not support named metrics yet.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const name of Object.keys(options.datasetAccess ?? {})) {
|
|
30
|
+
if (!exposed.has(name)) {
|
|
31
|
+
throw new Error(`Cloud access policy names an unpublished dataset: "${name}".`);
|
|
32
|
+
}
|
|
33
|
+
const access = options.datasetAccess?.[name];
|
|
34
|
+
if (!access || !Array.isArray(access.roles) || !Array.isArray(access.scopes)) {
|
|
35
|
+
throw new Error(`Cloud access policy for "${name}" must set roles and scopes.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const endpoints = {};
|
|
39
|
+
for (const dataset of datasets.values()) {
|
|
40
|
+
if (!exposed.has(dataset.name))
|
|
41
|
+
continue;
|
|
42
|
+
const access = options.datasetAccess?.[dataset.name] ?? options.access;
|
|
43
|
+
endpoints[dataset.name] = {
|
|
44
|
+
access: {
|
|
45
|
+
kind: 'authenticated',
|
|
46
|
+
roles: [...new Set(access.roles)].sort(),
|
|
47
|
+
scopes: [...new Set(access.scopes)].sort(),
|
|
48
|
+
},
|
|
49
|
+
tenant: dataset.tenantKey
|
|
50
|
+
? { kind: 'required', mode: 'auto-inject', column: dataset.tenantKey }
|
|
51
|
+
: { kind: 'not-required' },
|
|
52
|
+
maxLimit: dataset.limits?.maxResultSize ?? 1_000,
|
|
53
|
+
path: `/api/analytics/datasets/${dataset.name}/query`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return buildProtocolDeploymentContract([...datasets.values()].sort((left, right) => left.name.localeCompare(right.name)), { endpoints });
|
|
57
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export { buildProtocolDeploymentContract } from './protocol-deployment-adapter.j
|
|
|
22
22
|
export type { BuildProtocolDeploymentContractOptions } from './protocol-deployment-adapter.js';
|
|
23
23
|
export { rehydrateProtocolDeploymentContract } from './protocol-rehydrate.js';
|
|
24
24
|
export type { BuildProtocolDatasetContractOptions } from './protocol-adapter.js';
|
|
25
|
+
export { publishToCloud } from './cloud-publishing.js';
|
|
26
|
+
export type { CloudPublishingOptions } from './cloud-publishing.js';
|
|
25
27
|
export { createPortableSemanticExecutor, PortableExecutionBudgetError, PortableExecutionTenantError, PortableExecutionUnsupportedError, } from './portable-executor.js';
|
|
26
28
|
export type { PortableSemanticBudget, PortableSemanticExecutionInput, PortableSemanticExecutorOptions, } from './portable-executor.js';
|
|
27
29
|
export { compilePortableSqlExpression, DEFAULT_SQL_PORTABILITY_LIMITS, } from './sql-portability.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGhE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGnI,OAAO,EACL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAC/B,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,KAAK,EAAE,IAAI,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EACzB,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAChC,GAAG,EAAE,IAAI,EACT,MAAM,EAAE,KAAK,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AACrG,OAAO,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC;AACjF,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,+BAA+B,EAC/B,4BAA4B,EAC5B,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,6BAA6B,EAC7B,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,YAAY,EACZ,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,iBAAiB,EACjB,gCAAgC,GACjC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AACnF,YAAY,EAAE,sCAAsC,EAAE,MAAM,kCAAkC,CAAC;AAC/F,OAAO,EAAE,mCAAmC,EAAE,MAAM,yBAAyB,CAAC;AAC9E,YAAY,EAAE,mCAAmC,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,7 @@ export { rehydrateProtocolDatasets, UnsupportedContractFeatureError, } from './p
|
|
|
28
28
|
export { buildProtocolDatasetContract } from './protocol-adapter.js';
|
|
29
29
|
export { buildProtocolDeploymentContract } from './protocol-deployment-adapter.js';
|
|
30
30
|
export { rehydrateProtocolDeploymentContract } from './protocol-rehydrate.js';
|
|
31
|
+
export { publishToCloud } from './cloud-publishing.js';
|
|
31
32
|
// Portable native execution of a semantic invocation (decision 0005).
|
|
32
33
|
export { createPortableSemanticExecutor, PortableExecutionBudgetError, PortableExecutionTenantError, PortableExecutionUnsupportedError, } from './portable-executor.js';
|
|
33
34
|
// SQL portability compiler (R1A-07)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-portability.d.ts","sourceRoot":"","sources":["../src/sql-portability.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,MAAM,MAAM,uBAAuB,GAC/B,oBAAoB,GACpB,kCAAkC,GAClC,kCAAkC,GAClC,iCAAiC,GACjC,gCAAgC,GAChC,yBAAyB,GACzB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAED,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAEjF,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,oBAAoB,CAKtE,CAAC;
|
|
1
|
+
{"version":3,"file":"sql-portability.d.ts","sourceRoot":"","sources":["../src/sql-portability.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,MAAM,MAAM,uBAAuB,GAC/B,oBAAoB,GACpB,kCAAkC,GAClC,kCAAkC,GAClC,iCAAiC,GACjC,gCAAgC,GAChC,yBAAyB,GACzB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAED,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAEjF,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,oBAAoB,CAKtE,CAAC;AAi0BL;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CAyBtB"}
|
package/dist/sql-portability.js
CHANGED
|
@@ -141,6 +141,20 @@ function tokenize(sql) {
|
|
|
141
141
|
if (current === '\\') {
|
|
142
142
|
fail('HQ_SQL_PORT_UNSUPPORTED_LITERAL', 'Backslash escapes are not portable; double the quote instead.', index, index + 1);
|
|
143
143
|
}
|
|
144
|
+
const code = current.charCodeAt(0);
|
|
145
|
+
if ((code <= 0x1f && code !== 0x09 && code !== 0x0a && code !== 0x0d)
|
|
146
|
+
|| (code >= 0x7f && code <= 0x9f)) {
|
|
147
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_LITERAL', 'String literals cannot contain control characters.', index, index + 1);
|
|
148
|
+
}
|
|
149
|
+
if (code >= 0xd800 && code <= 0xdfff) {
|
|
150
|
+
const low = sql.charCodeAt(index + 1);
|
|
151
|
+
if (code > 0xdbff || !(low >= 0xdc00 && low <= 0xdfff)) {
|
|
152
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_LITERAL', 'String literals must be well-formed Unicode.', index, index + 1);
|
|
153
|
+
}
|
|
154
|
+
value += current + sql[index + 1];
|
|
155
|
+
index += 2;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
144
158
|
value += current;
|
|
145
159
|
index += 1;
|
|
146
160
|
}
|
|
@@ -222,7 +236,9 @@ function tokenize(sql) {
|
|
|
222
236
|
}
|
|
223
237
|
continue;
|
|
224
238
|
}
|
|
225
|
-
|
|
239
|
+
// Report a whole code point: an astral character is two UTF-16 code units.
|
|
240
|
+
const unexpected = String.fromCodePoint(sql.codePointAt(index));
|
|
241
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_SYNTAX', `Unexpected character "${unexpected}".`, start, start + unexpected.length);
|
|
226
242
|
}
|
|
227
243
|
return tokens;
|
|
228
244
|
}
|
|
@@ -260,12 +276,32 @@ const COMPARISON_OPERATORS = Object.freeze({
|
|
|
260
276
|
function node(expression) {
|
|
261
277
|
return Object.freeze(expression);
|
|
262
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* RFC 0003 caps the operands of one AND/OR node at the expression validator's
|
|
281
|
+
* collection limit.
|
|
282
|
+
*/
|
|
283
|
+
const MAX_LOGICAL_OPERANDS = 100;
|
|
284
|
+
function childExpressions(expression) {
|
|
285
|
+
switch (expression.kind) {
|
|
286
|
+
case 'binary':
|
|
287
|
+
case 'comparison':
|
|
288
|
+
return [expression.left, expression.right];
|
|
289
|
+
case 'logical':
|
|
290
|
+
return 'operands' in expression ? expression.operands : [expression.operand];
|
|
291
|
+
case 'call':
|
|
292
|
+
return expression.args;
|
|
293
|
+
default:
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
263
297
|
class Parser {
|
|
264
298
|
tokens;
|
|
265
299
|
limits;
|
|
266
300
|
position = 0;
|
|
267
301
|
nodes = 0;
|
|
268
302
|
dependencies = new Set();
|
|
303
|
+
/** Tree levels beneath each composite node, counting it; leaves are 1. */
|
|
304
|
+
levels = new WeakMap();
|
|
269
305
|
constructor(tokens, limits) {
|
|
270
306
|
this.tokens = tokens;
|
|
271
307
|
this.limits = limits;
|
|
@@ -303,38 +339,67 @@ class Parser {
|
|
|
303
339
|
fail('HQ_SQL_PORT_TOO_COMPLEX', 'The expression exceeds its depth limit.', token.start, token.end);
|
|
304
340
|
}
|
|
305
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Freeze a composite node, rejecting it once its tree has more levels than
|
|
344
|
+
* the depth limit (RFC 0003 counts the root as depth 1). Parse depth alone
|
|
345
|
+
* undercounts left-associative chains such as `a + b + c`, whose left spine
|
|
346
|
+
* grows with every operator.
|
|
347
|
+
*/
|
|
348
|
+
build(expression, token) {
|
|
349
|
+
let levels = 1;
|
|
350
|
+
for (const child of childExpressions(expression)) {
|
|
351
|
+
levels = Math.max(levels, (this.levels.get(child) ?? 1) + 1);
|
|
352
|
+
}
|
|
353
|
+
if (levels > this.limits.maxDepth) {
|
|
354
|
+
fail('HQ_SQL_PORT_TOO_COMPLEX', 'The expression exceeds its depth limit.', token.start, token.end);
|
|
355
|
+
}
|
|
356
|
+
const frozen = node(expression);
|
|
357
|
+
this.levels.set(frozen, levels);
|
|
358
|
+
return frozen;
|
|
359
|
+
}
|
|
360
|
+
/** Called after consuming AND/OR, before parsing the operand it introduces. */
|
|
361
|
+
rejectExtraOperand(operands) {
|
|
362
|
+
if (operands.length >= MAX_LOGICAL_OPERANDS) {
|
|
363
|
+
const token = this.tokens[this.position - 1];
|
|
364
|
+
fail('HQ_SQL_PORT_TOO_COMPLEX', 'The expression exceeds its operand limit.', token.start, token.end);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
306
367
|
parseOr(depth) {
|
|
307
368
|
const operands = [this.parseAnd(depth)];
|
|
308
369
|
while (this.peek()?.type === 'keyword' && this.peek().value === 'or') {
|
|
309
370
|
this.next();
|
|
371
|
+
this.rejectExtraOperand(operands);
|
|
310
372
|
operands.push(this.parseAnd(depth));
|
|
311
373
|
}
|
|
312
374
|
if (operands.length === 1)
|
|
313
375
|
return operands[0];
|
|
314
|
-
|
|
315
|
-
|
|
376
|
+
const token = this.tokens[this.position - 1];
|
|
377
|
+
this.enter(depth, token);
|
|
378
|
+
return this.build({ kind: 'logical', operator: 'or', operands: Object.freeze(operands) }, token);
|
|
316
379
|
}
|
|
317
380
|
parseAnd(depth) {
|
|
318
381
|
const operands = [this.parseNot(depth)];
|
|
319
382
|
while (this.peek()?.type === 'keyword' && this.peek().value === 'and') {
|
|
320
383
|
this.next();
|
|
384
|
+
this.rejectExtraOperand(operands);
|
|
321
385
|
operands.push(this.parseNot(depth));
|
|
322
386
|
}
|
|
323
387
|
if (operands.length === 1)
|
|
324
388
|
return operands[0];
|
|
325
|
-
|
|
326
|
-
|
|
389
|
+
const token = this.tokens[this.position - 1];
|
|
390
|
+
this.enter(depth, token);
|
|
391
|
+
return this.build({ kind: 'logical', operator: 'and', operands: Object.freeze(operands) }, token);
|
|
327
392
|
}
|
|
328
393
|
parseNot(depth) {
|
|
329
394
|
const token = this.peek();
|
|
330
395
|
if (token?.type === 'keyword' && token.value === 'not') {
|
|
331
396
|
this.next();
|
|
332
397
|
this.enter(depth + 1, token);
|
|
333
|
-
return
|
|
398
|
+
return this.build({
|
|
334
399
|
kind: 'logical',
|
|
335
400
|
operator: 'not',
|
|
336
401
|
operand: this.parseNot(depth + 1),
|
|
337
|
-
});
|
|
402
|
+
}, token);
|
|
338
403
|
}
|
|
339
404
|
return this.parseComparison(depth);
|
|
340
405
|
}
|
|
@@ -349,14 +414,17 @@ class Parser {
|
|
|
349
414
|
const right = this.parseAdditive(depth);
|
|
350
415
|
this.enter(depth + 1, token);
|
|
351
416
|
this.rejectChained();
|
|
352
|
-
return
|
|
417
|
+
return this.build({ kind: 'comparison', operator, left, right }, token);
|
|
353
418
|
}
|
|
354
419
|
if (token.type === 'keyword' && token.value === 'like') {
|
|
355
420
|
this.next();
|
|
356
421
|
const right = this.parseAdditive(depth);
|
|
357
422
|
this.enter(depth + 1, token);
|
|
358
423
|
this.rejectChained();
|
|
359
|
-
|
|
424
|
+
if (right.kind !== 'literal' || typeof right.value !== 'string') {
|
|
425
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_SYNTAX', 'LIKE patterns must be string literals.', token.start, token.end);
|
|
426
|
+
}
|
|
427
|
+
return this.build({ kind: 'comparison', operator: 'like', left, right }, token);
|
|
360
428
|
}
|
|
361
429
|
if (token.type === 'keyword' && token.value === 'between') {
|
|
362
430
|
this.next();
|
|
@@ -369,12 +437,12 @@ class Parser {
|
|
|
369
437
|
const upper = this.parseAdditive(depth);
|
|
370
438
|
this.enter(depth + 1, token);
|
|
371
439
|
this.rejectChained();
|
|
372
|
-
return
|
|
440
|
+
return this.build({
|
|
373
441
|
kind: 'comparison',
|
|
374
442
|
operator: 'between',
|
|
375
443
|
left,
|
|
376
444
|
right: this.tupleLiteral(lower, upper, token),
|
|
377
|
-
});
|
|
445
|
+
}, token);
|
|
378
446
|
}
|
|
379
447
|
if (token.type === 'keyword' && (token.value === 'in' || token.value === 'not')) {
|
|
380
448
|
this.next();
|
|
@@ -449,7 +517,7 @@ class Parser {
|
|
|
449
517
|
}
|
|
450
518
|
this.enter(depth + 1, token);
|
|
451
519
|
this.rejectChained();
|
|
452
|
-
return
|
|
520
|
+
return this.build({
|
|
453
521
|
kind: 'comparison',
|
|
454
522
|
operator,
|
|
455
523
|
left,
|
|
@@ -463,7 +531,7 @@ class Parser {
|
|
|
463
531
|
},
|
|
464
532
|
},
|
|
465
533
|
},
|
|
466
|
-
});
|
|
534
|
+
}, token);
|
|
467
535
|
}
|
|
468
536
|
parseAdditive(depth) {
|
|
469
537
|
let left = this.parseMultiplicative(depth);
|
|
@@ -474,12 +542,12 @@ class Parser {
|
|
|
474
542
|
this.next();
|
|
475
543
|
const right = this.parseMultiplicative(depth + 1);
|
|
476
544
|
this.enter(depth + 1, token);
|
|
477
|
-
left =
|
|
545
|
+
left = this.build({
|
|
478
546
|
kind: 'binary',
|
|
479
547
|
operator: BINARY_OPERATORS[token.value],
|
|
480
548
|
left,
|
|
481
549
|
right,
|
|
482
|
-
});
|
|
550
|
+
}, token);
|
|
483
551
|
}
|
|
484
552
|
return left;
|
|
485
553
|
}
|
|
@@ -492,12 +560,12 @@ class Parser {
|
|
|
492
560
|
this.next();
|
|
493
561
|
const right = this.parseUnary(depth + 1);
|
|
494
562
|
this.enter(depth + 1, token);
|
|
495
|
-
left =
|
|
563
|
+
left = this.build({
|
|
496
564
|
kind: 'binary',
|
|
497
565
|
operator: BINARY_OPERATORS[token.value],
|
|
498
566
|
left,
|
|
499
567
|
right,
|
|
500
|
-
});
|
|
568
|
+
}, token);
|
|
501
569
|
}
|
|
502
570
|
return left;
|
|
503
571
|
}
|
|
@@ -514,6 +582,7 @@ class Parser {
|
|
|
514
582
|
fail('HQ_SQL_PORT_UNSUPPORTED_OPERATOR', 'Negation is only portable on numeric literals.', token.start, token.end);
|
|
515
583
|
}
|
|
516
584
|
this.next();
|
|
585
|
+
this.enter(depth, operand);
|
|
517
586
|
return this.numericLiteral(operand, true);
|
|
518
587
|
}
|
|
519
588
|
return this.parsePrimary(depth);
|
|
@@ -595,11 +664,11 @@ class Parser {
|
|
|
595
664
|
if (args.length < arity[0] || args.length > arity[1]) {
|
|
596
665
|
fail('HQ_SQL_PORT_SYNTAX', `Function "${canonical}" expects ${arity[0] === arity[1] ? arity[0] : `${arity[0]} to ${arity[1]}`} arguments.`, name.start, close.end);
|
|
597
666
|
}
|
|
598
|
-
return
|
|
667
|
+
return this.build({
|
|
599
668
|
kind: 'call',
|
|
600
669
|
function: canonical,
|
|
601
670
|
args: Object.freeze(args),
|
|
602
|
-
});
|
|
671
|
+
}, name);
|
|
603
672
|
}
|
|
604
673
|
numericLiteral(token, negated) {
|
|
605
674
|
const value = Number(token.value) * (negated ? -1 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-cloud-datasets.d.ts","sourceRoot":"","sources":["../../src/utils/collect-cloud-datasets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACzC,OAAO,EAAE,kBAAkB,GAC1B,IAAI,CAUN"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function collectCloudDatasets(datasets, dataset) {
|
|
2
|
+
const existing = datasets.get(dataset.name);
|
|
3
|
+
if (existing && existing !== dataset) {
|
|
4
|
+
throw new Error(`Multiple Dataset definitions use the name "${dataset.name}".`);
|
|
5
|
+
}
|
|
6
|
+
if (existing)
|
|
7
|
+
return;
|
|
8
|
+
datasets.set(dataset.name, dataset);
|
|
9
|
+
for (const relationship of Object.values(dataset.relationships)) {
|
|
10
|
+
collectCloudDatasets(datasets, relationship.target());
|
|
11
|
+
}
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/datasets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Code-first TypeScript semantic layer for ClickHouse datasets, metrics, multi-tenancy, and AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"semantic-layer",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dist"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@hypequery/protocol": "^0.
|
|
45
|
+
"@hypequery/protocol": "^0.15.0",
|
|
46
46
|
"@noble/hashes": "^1.8.0",
|
|
47
47
|
"zod": "^3.22.4",
|
|
48
48
|
"zod-to-json-schema": "^3.23.5"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"typescript": "^5.7.3",
|
|
61
61
|
"@vitest/coverage-v8": "^3.2.6",
|
|
62
62
|
"vitest": "^3.2.6",
|
|
63
|
-
"@hypequery/protocol-conformance": "^0.
|
|
63
|
+
"@hypequery/protocol-conformance": "^0.12.0"
|
|
64
64
|
},
|
|
65
65
|
"repository": {
|
|
66
66
|
"type": "git",
|