@malloy-publisher/server 0.0.203 → 0.0.205
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/build.ts +10 -1
- package/dist/app/api-doc.yaml +146 -0
- package/dist/app/assets/{EnvironmentPage-BVQ7glKP.js → EnvironmentPage-CAge6UHD.js} +1 -1
- package/dist/app/assets/HomePage-DhTe8qpa.js +1 -0
- package/dist/app/assets/{MainPage-bYOWcgDP.js → MainPage-CeTxxGex.js} +2 -2
- package/dist/app/assets/MaterializationsPage-CpDHB70t.js +1 -0
- package/dist/app/assets/ModelPage-D9sSMb75.js +1 -0
- package/dist/app/assets/PackagePage-LRqQWrFY.js +1 -0
- package/dist/app/assets/{RouteError-_J-EBz7W.js → RouteError-xT6kuCNw.js} +1 -1
- package/dist/app/assets/{WorkbookPage-Bjs9Nm-_.js → WorkbookPage-DsIh9svZ.js} +1 -1
- package/dist/app/assets/{core-BPLlx5VM.es-C2ARtwWI.js → core-C2sQrwVu.es-Bjem0hym.js} +1 -1
- package/dist/app/assets/{index-CqUWJELr.js → index-BdOZDcce.js} +2 -2
- package/dist/app/assets/index-DHHAcY5o.js +1812 -0
- package/dist/app/assets/index-RX3QOTde.js +455 -0
- package/dist/app/assets/index.umd-D2WH3D-f.js +2469 -0
- package/dist/app/index.html +1 -1
- package/dist/package_load_worker.mjs +392 -67
- package/dist/runtime/publisher.js +318 -0
- package/dist/server.mjs +982 -346
- package/package.json +15 -14
- package/scripts/bake-duckdb-extensions.js +104 -0
- package/src/controller/watch-mode.controller.ts +176 -46
- package/src/ducklake_version.spec.ts +43 -0
- package/src/ducklake_version.ts +26 -0
- package/src/errors.spec.ts +21 -0
- package/src/errors.ts +18 -1
- package/src/mcp/error_messages.spec.ts +35 -0
- package/src/mcp/error_messages.ts +14 -1
- package/src/mcp/handler_utils.ts +12 -0
- package/src/package_load/package_load_pool.ts +0 -5
- package/src/package_load/package_load_worker.ts +41 -99
- package/src/package_load/protocol.ts +1 -7
- package/src/runtime/publisher.js +318 -0
- package/src/server.ts +479 -2
- package/src/service/annotations.spec.ts +118 -0
- package/src/service/annotations.ts +91 -0
- package/src/service/authorize.spec.ts +132 -0
- package/src/service/authorize.ts +241 -0
- package/src/service/authorize_integration.spec.ts +932 -0
- package/src/service/compile_authorize.spec.ts +85 -0
- package/src/service/connection.ts +1 -1
- package/src/service/environment.ts +67 -9
- package/src/service/environment_store.ts +142 -11
- package/src/service/filter.spec.ts +14 -3
- package/src/service/filter.ts +5 -1
- package/src/service/filter_bypass.spec.ts +418 -0
- package/src/service/given.ts +37 -12
- package/src/service/givens_integration.spec.ts +34 -7
- package/src/service/materialization_service.ts +25 -20
- package/src/service/materialized_table_gc.spec.ts +6 -5
- package/src/service/materialized_table_gc.ts +2 -50
- package/src/service/model.spec.ts +203 -8
- package/src/service/model.ts +349 -155
- package/src/service/package.ts +17 -6
- package/src/service/package_worker_path.spec.ts +113 -0
- package/src/service/quoting.ts +0 -20
- package/src/service/restricted_mode.spec.ts +299 -0
- package/src/service/source_extraction.ts +226 -0
- package/src/storage/StorageManager.ts +73 -0
- package/src/storage/duckdb/DuckDBConnection.ts +70 -124
- package/tests/fixtures/authorize-compile/model.malloy +9 -0
- package/tests/fixtures/authorize-compile/publisher.json +4 -0
- package/tests/fixtures/html-pages-nopublic/model.malloy +1 -0
- package/tests/fixtures/html-pages-nopublic/publisher.json +5 -0
- package/tests/fixtures/html-pages-test/data.csv +3 -0
- package/tests/fixtures/html-pages-test/public/assets/app.css +3 -0
- package/tests/fixtures/html-pages-test/public/data.json +1 -0
- package/tests/fixtures/html-pages-test/public/index.html +9 -0
- package/tests/fixtures/html-pages-test/public/sub/page2.html +9 -0
- package/tests/fixtures/html-pages-test/publisher.json +5 -0
- package/tests/fixtures/html-pages-test/report.malloy +1 -0
- package/tests/integration/authorize/compile_authorize_http.integration.spec.ts +92 -0
- package/tests/integration/duckdb_storage/duckdb_storage.integration.spec.ts +138 -0
- package/tests/integration/html_pages/html_pages.integration.spec.ts +378 -0
- package/tests/integration/watch-mode/watch_mode.integration.spec.ts +421 -0
- package/tests/unit/duckdb/attached_databases.test.ts +111 -0
- package/tests/unit/duckdb/duckdb_connection.test.ts +181 -0
- package/tests/unit/duckdb/repositories.test.ts +208 -0
- package/dist/app/assets/HomePage-D9drXoZX.js +0 -1
- package/dist/app/assets/ModelPage-DT0gjNy1.js +0 -1
- package/dist/app/assets/PackagePage-N1ZBNJul.js +0 -1
- package/dist/app/assets/index-BeNwIeYQ.js +0 -454
- package/dist/app/assets/index-Dx7qi2LO.js +0 -1803
- package/dist/app/assets/index.umd-BXm2lnUO.js +0 -1145
package/dist/app/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
12
12
|
/>
|
|
13
13
|
<title>Malloy Publisher</title>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-RX3QOTde.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-5eLCcNmP.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -11386,16 +11386,156 @@ var init_logger = __esm(() => {
|
|
|
11386
11386
|
DISABLE_RESPONSE_LOGGING = process.env.DISABLE_RESPONSE_LOGGING === "true" || process.env.DISABLE_RESPONSE_LOGGING === "1";
|
|
11387
11387
|
});
|
|
11388
11388
|
|
|
11389
|
+
// src/errors.ts
|
|
11390
|
+
import { MalloyError } from "@malloydata/malloy";
|
|
11391
|
+
function internalErrorToHttpError(error) {
|
|
11392
|
+
if (error instanceof BadRequestError) {
|
|
11393
|
+
return httpError(400, error.message);
|
|
11394
|
+
} else if (error instanceof FrozenConfigError) {
|
|
11395
|
+
return httpError(403, error.message);
|
|
11396
|
+
} else if (error instanceof AccessDeniedError) {
|
|
11397
|
+
return httpError(403, error.message);
|
|
11398
|
+
} else if (error instanceof EnvironmentNotFoundError) {
|
|
11399
|
+
return httpError(404, error.message);
|
|
11400
|
+
} else if (error instanceof PackageNotFoundError) {
|
|
11401
|
+
return httpError(404, error.message);
|
|
11402
|
+
} else if (error instanceof ModelNotFoundError) {
|
|
11403
|
+
return httpError(404, error.message);
|
|
11404
|
+
} else if (error instanceof MalloyError) {
|
|
11405
|
+
return httpError(400, error.message);
|
|
11406
|
+
} else if (error instanceof ConnectionNotFoundError) {
|
|
11407
|
+
return httpError(404, error.message);
|
|
11408
|
+
} else if (error instanceof ConnectionAuthError) {
|
|
11409
|
+
return httpError(422, error.message);
|
|
11410
|
+
} else if (error instanceof ModelCompilationError) {
|
|
11411
|
+
return httpError(424, error.message);
|
|
11412
|
+
} else if (error instanceof ConnectionError) {
|
|
11413
|
+
return httpError(502, error.message);
|
|
11414
|
+
} else if (error instanceof MaterializationNotFoundError) {
|
|
11415
|
+
return httpError(404, error.message);
|
|
11416
|
+
} else if (error instanceof MaterializationConflictError) {
|
|
11417
|
+
return httpError(409, error.message);
|
|
11418
|
+
} else if (error instanceof InvalidStateTransitionError) {
|
|
11419
|
+
return httpError(409, error.message);
|
|
11420
|
+
} else if (error instanceof ServiceUnavailableError) {
|
|
11421
|
+
return httpError(503, error.message);
|
|
11422
|
+
} else if (error instanceof PayloadTooLargeError) {
|
|
11423
|
+
return httpError(413, error.message);
|
|
11424
|
+
} else if (error instanceof QueryTimeoutError) {
|
|
11425
|
+
return httpError(504, error.message);
|
|
11426
|
+
} else {
|
|
11427
|
+
return httpError(500, error.message);
|
|
11428
|
+
}
|
|
11429
|
+
}
|
|
11430
|
+
function httpError(code, message) {
|
|
11431
|
+
return {
|
|
11432
|
+
status: code,
|
|
11433
|
+
json: {
|
|
11434
|
+
code,
|
|
11435
|
+
message
|
|
11436
|
+
}
|
|
11437
|
+
};
|
|
11438
|
+
}
|
|
11439
|
+
var NotImplementedError, BadRequestError, EnvironmentNotFoundError, PackageNotFoundError, ModelNotFoundError, ConnectionNotFoundError, ConnectionError, ConnectionAuthError, ModelCompilationError, FrozenConfigError, AccessDeniedError, MaterializationNotFoundError, MaterializationConflictError, InvalidStateTransitionError, ServiceUnavailableError, PayloadTooLargeError, QueryTimeoutError;
|
|
11440
|
+
var init_errors = __esm(() => {
|
|
11441
|
+
init_constants();
|
|
11442
|
+
NotImplementedError = class NotImplementedError extends Error {
|
|
11443
|
+
constructor(message) {
|
|
11444
|
+
super(message);
|
|
11445
|
+
}
|
|
11446
|
+
};
|
|
11447
|
+
BadRequestError = class BadRequestError extends Error {
|
|
11448
|
+
constructor(message) {
|
|
11449
|
+
super(message);
|
|
11450
|
+
}
|
|
11451
|
+
};
|
|
11452
|
+
EnvironmentNotFoundError = class EnvironmentNotFoundError extends Error {
|
|
11453
|
+
constructor(message) {
|
|
11454
|
+
super(message);
|
|
11455
|
+
}
|
|
11456
|
+
};
|
|
11457
|
+
PackageNotFoundError = class PackageNotFoundError extends Error {
|
|
11458
|
+
constructor(message) {
|
|
11459
|
+
super(message);
|
|
11460
|
+
}
|
|
11461
|
+
};
|
|
11462
|
+
ModelNotFoundError = class ModelNotFoundError extends Error {
|
|
11463
|
+
constructor(message) {
|
|
11464
|
+
super(message);
|
|
11465
|
+
}
|
|
11466
|
+
};
|
|
11467
|
+
ConnectionNotFoundError = class ConnectionNotFoundError extends Error {
|
|
11468
|
+
constructor(message) {
|
|
11469
|
+
super(message);
|
|
11470
|
+
}
|
|
11471
|
+
};
|
|
11472
|
+
ConnectionError = class ConnectionError extends Error {
|
|
11473
|
+
constructor(message) {
|
|
11474
|
+
super(message);
|
|
11475
|
+
}
|
|
11476
|
+
};
|
|
11477
|
+
ConnectionAuthError = class ConnectionAuthError extends Error {
|
|
11478
|
+
constructor(message) {
|
|
11479
|
+
super(message);
|
|
11480
|
+
}
|
|
11481
|
+
};
|
|
11482
|
+
ModelCompilationError = class ModelCompilationError extends Error {
|
|
11483
|
+
constructor(error) {
|
|
11484
|
+
super(error.message);
|
|
11485
|
+
}
|
|
11486
|
+
};
|
|
11487
|
+
FrozenConfigError = class FrozenConfigError extends Error {
|
|
11488
|
+
constructor(message = `Publisher config can't be updated when ${PUBLISHER_CONFIG_NAME} has { "frozenConfig": true }`) {
|
|
11489
|
+
super(message);
|
|
11490
|
+
}
|
|
11491
|
+
};
|
|
11492
|
+
AccessDeniedError = class AccessDeniedError extends Error {
|
|
11493
|
+
constructor(message) {
|
|
11494
|
+
super(message);
|
|
11495
|
+
this.name = "AccessDeniedError";
|
|
11496
|
+
}
|
|
11497
|
+
};
|
|
11498
|
+
MaterializationNotFoundError = class MaterializationNotFoundError extends Error {
|
|
11499
|
+
constructor(message) {
|
|
11500
|
+
super(message);
|
|
11501
|
+
}
|
|
11502
|
+
};
|
|
11503
|
+
MaterializationConflictError = class MaterializationConflictError extends Error {
|
|
11504
|
+
constructor(message) {
|
|
11505
|
+
super(message);
|
|
11506
|
+
}
|
|
11507
|
+
};
|
|
11508
|
+
InvalidStateTransitionError = class InvalidStateTransitionError extends Error {
|
|
11509
|
+
constructor(message) {
|
|
11510
|
+
super(message);
|
|
11511
|
+
}
|
|
11512
|
+
};
|
|
11513
|
+
ServiceUnavailableError = class ServiceUnavailableError extends Error {
|
|
11514
|
+
constructor(message) {
|
|
11515
|
+
super(message);
|
|
11516
|
+
}
|
|
11517
|
+
};
|
|
11518
|
+
PayloadTooLargeError = class PayloadTooLargeError extends Error {
|
|
11519
|
+
constructor(message) {
|
|
11520
|
+
super(message);
|
|
11521
|
+
}
|
|
11522
|
+
};
|
|
11523
|
+
QueryTimeoutError = class QueryTimeoutError extends Error {
|
|
11524
|
+
constructor(message) {
|
|
11525
|
+
super(message);
|
|
11526
|
+
}
|
|
11527
|
+
};
|
|
11528
|
+
});
|
|
11529
|
+
|
|
11389
11530
|
// src/package_load/package_load_worker.ts
|
|
11390
11531
|
init_constants();
|
|
11391
11532
|
var import_recursive_readdir = __toESM(require_recursive_readdir(), 1);
|
|
11392
11533
|
import {
|
|
11393
11534
|
contextOverlay,
|
|
11394
11535
|
MalloyConfig,
|
|
11395
|
-
MalloyError,
|
|
11536
|
+
MalloyError as MalloyError2,
|
|
11396
11537
|
modelDefToModelInfo,
|
|
11397
|
-
Runtime
|
|
11398
|
-
isSourceDef
|
|
11538
|
+
Runtime
|
|
11399
11539
|
} from "@malloydata/malloy";
|
|
11400
11540
|
import {
|
|
11401
11541
|
MalloySQLParser,
|
|
@@ -11454,6 +11594,157 @@ class HackyDataStylesAccumulator {
|
|
|
11454
11594
|
}
|
|
11455
11595
|
}
|
|
11456
11596
|
|
|
11597
|
+
// src/package_load/package_load_worker.ts
|
|
11598
|
+
init_errors();
|
|
11599
|
+
|
|
11600
|
+
// src/service/authorize.ts
|
|
11601
|
+
init_errors();
|
|
11602
|
+
var SOURCE_PREFIX = "#(authorize)";
|
|
11603
|
+
var FILE_PREFIX = "##(authorize)";
|
|
11604
|
+
function buildAuthorizeProbe(exprs) {
|
|
11605
|
+
const selects = exprs.map((expr, i) => `__auth_${i} is (${expr})`).join(`
|
|
11606
|
+
`);
|
|
11607
|
+
return `run: duckdb.sql("SELECT 1 AS __authorize_probe_row") -> {
|
|
11608
|
+
select:
|
|
11609
|
+
${selects}
|
|
11610
|
+
limit: 1
|
|
11611
|
+
}`;
|
|
11612
|
+
}
|
|
11613
|
+
function isProbeTrue(cell) {
|
|
11614
|
+
return cell === true || cell === 1 || cell === "true";
|
|
11615
|
+
}
|
|
11616
|
+
async function evaluateAuthorize(executor, exprs, givens) {
|
|
11617
|
+
for (const expr of exprs) {
|
|
11618
|
+
try {
|
|
11619
|
+
const result = await executor.loadQuery(buildAuthorizeProbe([expr])).run({ rowLimit: 1, givens });
|
|
11620
|
+
const row = result?.data?.value?.[0];
|
|
11621
|
+
if (row && isProbeTrue(row.__auth_0)) {
|
|
11622
|
+
return true;
|
|
11623
|
+
}
|
|
11624
|
+
} catch {
|
|
11625
|
+
continue;
|
|
11626
|
+
}
|
|
11627
|
+
}
|
|
11628
|
+
return false;
|
|
11629
|
+
}
|
|
11630
|
+
async function validateAuthorizeProbes(compiler, sources) {
|
|
11631
|
+
for (const source of sources) {
|
|
11632
|
+
const exprs = source.authorize;
|
|
11633
|
+
if (!exprs || exprs.length === 0)
|
|
11634
|
+
continue;
|
|
11635
|
+
try {
|
|
11636
|
+
await compiler.loadQuery(buildAuthorizeProbe(exprs)).getPreparedQuery();
|
|
11637
|
+
} catch (err) {
|
|
11638
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
11639
|
+
throw new ModelCompilationError({
|
|
11640
|
+
message: `Invalid #(authorize) annotation on source "${source.name ?? "(unnamed)"}" [${exprs.join(" | ")}]: ${detail}`
|
|
11641
|
+
});
|
|
11642
|
+
}
|
|
11643
|
+
}
|
|
11644
|
+
}
|
|
11645
|
+
function parseAuthorizeAnnotation(annotation) {
|
|
11646
|
+
const trimmed = annotation.trim();
|
|
11647
|
+
let body;
|
|
11648
|
+
if (trimmed.startsWith(FILE_PREFIX)) {
|
|
11649
|
+
body = trimmed.slice(FILE_PREFIX.length).trim();
|
|
11650
|
+
} else if (trimmed.startsWith(SOURCE_PREFIX)) {
|
|
11651
|
+
body = trimmed.slice(SOURCE_PREFIX.length).trim();
|
|
11652
|
+
} else {
|
|
11653
|
+
return null;
|
|
11654
|
+
}
|
|
11655
|
+
return unwrapQuotedExpression(body);
|
|
11656
|
+
}
|
|
11657
|
+
function collectAuthorizeExprs(annotations) {
|
|
11658
|
+
const exprs = [];
|
|
11659
|
+
for (const annotation of annotations) {
|
|
11660
|
+
const expr = parseAuthorizeAnnotation(annotation);
|
|
11661
|
+
if (expr !== null) {
|
|
11662
|
+
exprs.push(expr);
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
11665
|
+
return exprs;
|
|
11666
|
+
}
|
|
11667
|
+
function unwrapQuotedExpression(body) {
|
|
11668
|
+
if (body.length < 2 || body[0] !== '"') {
|
|
11669
|
+
throw new Error(`authorize annotation expression must be a double-quoted string, got: ${body || "(empty)"}`);
|
|
11670
|
+
}
|
|
11671
|
+
let expr = "";
|
|
11672
|
+
let i = 1;
|
|
11673
|
+
let closed = false;
|
|
11674
|
+
for (;i < body.length; i++) {
|
|
11675
|
+
const ch = body[i];
|
|
11676
|
+
if (ch === "\\" && i + 1 < body.length) {
|
|
11677
|
+
const next = body[i + 1];
|
|
11678
|
+
if (next === '"' || next === "\\") {
|
|
11679
|
+
expr += next;
|
|
11680
|
+
i++;
|
|
11681
|
+
continue;
|
|
11682
|
+
}
|
|
11683
|
+
}
|
|
11684
|
+
if (ch === '"') {
|
|
11685
|
+
closed = true;
|
|
11686
|
+
i++;
|
|
11687
|
+
break;
|
|
11688
|
+
}
|
|
11689
|
+
expr += ch;
|
|
11690
|
+
}
|
|
11691
|
+
if (!closed) {
|
|
11692
|
+
throw new Error(`authorize annotation has mismatched quotes: ${body}`);
|
|
11693
|
+
}
|
|
11694
|
+
const rest = body.slice(i).trim();
|
|
11695
|
+
if (rest.length > 0) {
|
|
11696
|
+
throw new Error(`authorize annotation has unexpected content after the expression: ${rest}`);
|
|
11697
|
+
}
|
|
11698
|
+
if (expr.trim().length === 0) {
|
|
11699
|
+
throw new Error("authorize annotation has an empty expression body");
|
|
11700
|
+
}
|
|
11701
|
+
return expr;
|
|
11702
|
+
}
|
|
11703
|
+
|
|
11704
|
+
// src/service/source_extraction.ts
|
|
11705
|
+
import {
|
|
11706
|
+
isSourceDef
|
|
11707
|
+
} from "@malloydata/malloy";
|
|
11708
|
+
|
|
11709
|
+
// src/service/annotations.ts
|
|
11710
|
+
import { Annotations } from "@malloydata/malloy";
|
|
11711
|
+
function isReservedRoute(route) {
|
|
11712
|
+
return route === "" || !/[\p{L}\p{N}]/u.test(route);
|
|
11713
|
+
}
|
|
11714
|
+
function modelAnnotations(modelDef) {
|
|
11715
|
+
const registry = modelDef.modelAnnotations ?? {};
|
|
11716
|
+
const visited = new Set;
|
|
11717
|
+
const order = [];
|
|
11718
|
+
const visit = (id) => {
|
|
11719
|
+
if (visited.has(id))
|
|
11720
|
+
return;
|
|
11721
|
+
visited.add(id);
|
|
11722
|
+
const entry = registry[id];
|
|
11723
|
+
if (!entry)
|
|
11724
|
+
return;
|
|
11725
|
+
for (const dep of entry.inheritsFrom)
|
|
11726
|
+
visit(dep);
|
|
11727
|
+
order.push(id);
|
|
11728
|
+
};
|
|
11729
|
+
visit(modelDef.modelID);
|
|
11730
|
+
let folded;
|
|
11731
|
+
for (const id of order) {
|
|
11732
|
+
const own = registry[id].ownNotes;
|
|
11733
|
+
if (!own.notes?.length && !own.blockNotes?.length)
|
|
11734
|
+
continue;
|
|
11735
|
+
folded = {
|
|
11736
|
+
notes: own.notes,
|
|
11737
|
+
blockNotes: own.blockNotes,
|
|
11738
|
+
inherits: folded
|
|
11739
|
+
};
|
|
11740
|
+
}
|
|
11741
|
+
return folded ?? {};
|
|
11742
|
+
}
|
|
11743
|
+
function annotationTexts(annote) {
|
|
11744
|
+
const texts = new Annotations(annote).texts();
|
|
11745
|
+
return texts.length > 0 ? texts : undefined;
|
|
11746
|
+
}
|
|
11747
|
+
|
|
11457
11748
|
// src/service/filter.ts
|
|
11458
11749
|
var VALID_FILTER_TYPES = new Set([
|
|
11459
11750
|
"equal",
|
|
@@ -11605,7 +11896,8 @@ function injectFilterRefinement(query, filterClause) {
|
|
|
11605
11896
|
if (!filterClause) {
|
|
11606
11897
|
return query;
|
|
11607
11898
|
}
|
|
11608
|
-
return `${query.trimEnd()}
|
|
11899
|
+
return `${query.trimEnd()}
|
|
11900
|
+
+ {where: ${filterClause}}`;
|
|
11609
11901
|
}
|
|
11610
11902
|
|
|
11611
11903
|
class FilterValidationError extends Error {
|
|
@@ -11644,6 +11936,81 @@ function tokenize(input) {
|
|
|
11644
11936
|
return tokens;
|
|
11645
11937
|
}
|
|
11646
11938
|
|
|
11939
|
+
// src/service/source_extraction.ts
|
|
11940
|
+
function extractSourcesFromModelDef(modelDef, givens, onParseError) {
|
|
11941
|
+
const filterMap = new Map;
|
|
11942
|
+
const authorizeMap = new Map;
|
|
11943
|
+
const fileLevelAuthorize = collectAuthorizeExprs((modelAnnotations(modelDef).notes ?? []).map((note) => note.text));
|
|
11944
|
+
const sources = Object.values(modelDef.contents).filter((obj) => isSourceDef(obj)).map((sourceObj) => {
|
|
11945
|
+
const struct = sourceObj;
|
|
11946
|
+
const sourceName = struct.as || struct.name;
|
|
11947
|
+
const annotations = annotationTexts(struct.annotations);
|
|
11948
|
+
const collected = [];
|
|
11949
|
+
let cur = struct.annotations;
|
|
11950
|
+
while (cur) {
|
|
11951
|
+
if (cur.blockNotes) {
|
|
11952
|
+
collected.push(cur.blockNotes.map((note) => note.text));
|
|
11953
|
+
}
|
|
11954
|
+
cur = cur.inherits;
|
|
11955
|
+
}
|
|
11956
|
+
const allAnnotations = collected.reverse().flat();
|
|
11957
|
+
let filters;
|
|
11958
|
+
if (allAnnotations.length > 0) {
|
|
11959
|
+
try {
|
|
11960
|
+
const parsed = parseFilters(allAnnotations);
|
|
11961
|
+
if (parsed.length > 0) {
|
|
11962
|
+
filterMap.set(sourceName, parsed);
|
|
11963
|
+
const fields = struct.fields;
|
|
11964
|
+
filters = parsed.map((f) => {
|
|
11965
|
+
const field = fields.find((fd) => (fd.as || fd.name) === f.dimension);
|
|
11966
|
+
return {
|
|
11967
|
+
name: f.name,
|
|
11968
|
+
dimension: f.dimension,
|
|
11969
|
+
type: f.type,
|
|
11970
|
+
implicit: f.implicit,
|
|
11971
|
+
required: f.required,
|
|
11972
|
+
dimensionType: field?.type
|
|
11973
|
+
};
|
|
11974
|
+
});
|
|
11975
|
+
}
|
|
11976
|
+
} catch (err) {
|
|
11977
|
+
onParseError?.(sourceName, err);
|
|
11978
|
+
}
|
|
11979
|
+
}
|
|
11980
|
+
const ownNotes = (struct.annotations?.blockNotes ?? []).map((note) => note.text);
|
|
11981
|
+
const effective = [
|
|
11982
|
+
...fileLevelAuthorize,
|
|
11983
|
+
...collectAuthorizeExprs(ownNotes)
|
|
11984
|
+
];
|
|
11985
|
+
let authorize;
|
|
11986
|
+
if (effective.length > 0) {
|
|
11987
|
+
authorizeMap.set(sourceName, effective);
|
|
11988
|
+
authorize = effective;
|
|
11989
|
+
}
|
|
11990
|
+
const views = struct.fields.filter((field) => field.type === "turtle").filter((turtle) => turtle.pipeline.map((stage) => stage.type).every((type) => type === "reduce")).map((turtle) => ({
|
|
11991
|
+
name: turtle.as || turtle.name,
|
|
11992
|
+
annotations: annotationTexts(turtle.annotations)
|
|
11993
|
+
}));
|
|
11994
|
+
return {
|
|
11995
|
+
name: sourceName,
|
|
11996
|
+
annotations,
|
|
11997
|
+
views,
|
|
11998
|
+
filters,
|
|
11999
|
+
givens,
|
|
12000
|
+
authorize
|
|
12001
|
+
};
|
|
12002
|
+
});
|
|
12003
|
+
return { sources, filterMap, authorizeMap };
|
|
12004
|
+
}
|
|
12005
|
+
function extractQueriesFromModelDef(modelDef) {
|
|
12006
|
+
const isNamedQuery = (obj) => obj.type === "query";
|
|
12007
|
+
return Object.values(modelDef.contents).filter(isNamedQuery).map((queryObj) => ({
|
|
12008
|
+
name: queryObj.as || queryObj.name,
|
|
12009
|
+
sourceName: typeof queryObj.structRef === "string" ? queryObj.structRef : undefined,
|
|
12010
|
+
annotations: annotationTexts(queryObj.annotations)
|
|
12011
|
+
}));
|
|
12012
|
+
}
|
|
12013
|
+
|
|
11647
12014
|
// src/service/given.ts
|
|
11648
12015
|
function malloyGivenToApi(given) {
|
|
11649
12016
|
const type = given.type;
|
|
@@ -11651,7 +12018,8 @@ function malloyGivenToApi(given) {
|
|
|
11651
12018
|
return {
|
|
11652
12019
|
name: given.name,
|
|
11653
12020
|
type: renderedType,
|
|
11654
|
-
annotations: given.
|
|
12021
|
+
annotations: given.annotations.forRoute(undefined).filter((note) => !isReservedRoute(note.route)).map((note) => note.text),
|
|
12022
|
+
default: given._internal?.defaultText
|
|
11655
12023
|
};
|
|
11656
12024
|
}
|
|
11657
12025
|
|
|
@@ -11788,9 +12156,6 @@ function serializeFetchOptions(options) {
|
|
|
11788
12156
|
if (options.refreshTimestamp !== undefined) {
|
|
11789
12157
|
out.refreshTimestamp = options.refreshTimestamp;
|
|
11790
12158
|
}
|
|
11791
|
-
if (options.modelAnnotation !== undefined) {
|
|
11792
|
-
out.modelAnnotation = options.modelAnnotation;
|
|
11793
|
-
}
|
|
11794
12159
|
return out;
|
|
11795
12160
|
}
|
|
11796
12161
|
function makeWorkerUrlReader(jobId) {
|
|
@@ -11888,62 +12253,12 @@ function appendLocalSourceInfos(modelDef, target, importedNames) {
|
|
|
11888
12253
|
target.push(source);
|
|
11889
12254
|
}
|
|
11890
12255
|
}
|
|
11891
|
-
function extractSources(
|
|
11892
|
-
const filterMap =
|
|
11893
|
-
const sources = Object.values(modelDef.contents).filter((obj) => isSourceDef(obj)).map((sourceObj) => {
|
|
11894
|
-
const sourceName = sourceObj.as || sourceObj.name;
|
|
11895
|
-
const annotations = sourceObj.annotation?.blockNotes?.filter((note) => note.at.url.includes(modelPath)).map((note) => note.text);
|
|
11896
|
-
const collected = [];
|
|
11897
|
-
let cur = sourceObj.annotation;
|
|
11898
|
-
while (cur) {
|
|
11899
|
-
if (cur.blockNotes) {
|
|
11900
|
-
collected.push(cur.blockNotes.map((note) => note.text));
|
|
11901
|
-
}
|
|
11902
|
-
cur = cur.inherits;
|
|
11903
|
-
}
|
|
11904
|
-
const allAnnotations = collected.reverse().flat();
|
|
11905
|
-
let filters;
|
|
11906
|
-
if (allAnnotations.length > 0) {
|
|
11907
|
-
try {
|
|
11908
|
-
const parsed = parseFilters(allAnnotations);
|
|
11909
|
-
if (parsed.length > 0) {
|
|
11910
|
-
filterMap.set(sourceName, parsed);
|
|
11911
|
-
const fields = sourceObj.fields;
|
|
11912
|
-
filters = parsed.map((f) => {
|
|
11913
|
-
const field = fields.find((fd) => (fd.as || fd.name) === f.dimension);
|
|
11914
|
-
return {
|
|
11915
|
-
name: f.name,
|
|
11916
|
-
dimension: f.dimension,
|
|
11917
|
-
type: f.type,
|
|
11918
|
-
implicit: f.implicit,
|
|
11919
|
-
required: f.required,
|
|
11920
|
-
dimensionType: field?.type
|
|
11921
|
-
};
|
|
11922
|
-
});
|
|
11923
|
-
}
|
|
11924
|
-
} catch {}
|
|
11925
|
-
}
|
|
11926
|
-
const views = sourceObj.fields.filter((f) => f.type === "turtle").filter((turtle) => turtle.pipeline.map((stage) => stage.type).every((type) => type === "reduce")).map((turtle) => ({
|
|
11927
|
-
name: turtle.as || turtle.name,
|
|
11928
|
-
annotations: turtle?.annotation?.blockNotes?.filter((note) => note.at.url.includes(modelPath)).map((note) => note.text)
|
|
11929
|
-
}));
|
|
11930
|
-
return {
|
|
11931
|
-
name: sourceName,
|
|
11932
|
-
annotations,
|
|
11933
|
-
views,
|
|
11934
|
-
filters,
|
|
11935
|
-
givens
|
|
11936
|
-
};
|
|
11937
|
-
});
|
|
12256
|
+
function extractSources(modelDef, givens) {
|
|
12257
|
+
const { sources, filterMap } = extractSourcesFromModelDef(modelDef, givens);
|
|
11938
12258
|
return { sources, filterMap };
|
|
11939
12259
|
}
|
|
11940
|
-
function extractQueries(
|
|
11941
|
-
|
|
11942
|
-
return Object.values(modelDef.contents).filter(isNamedQuery).map((q) => ({
|
|
11943
|
-
name: q.as || q.name,
|
|
11944
|
-
sourceName: typeof q.structRef === "string" ? q.structRef : undefined,
|
|
11945
|
-
annotations: q?.annotation?.blockNotes?.filter((note) => note.at.url.includes(modelPath)).map((note) => note.text)
|
|
11946
|
-
}));
|
|
12260
|
+
function extractQueries(modelDef) {
|
|
12261
|
+
return extractQueriesFromModelDef(modelDef);
|
|
11947
12262
|
}
|
|
11948
12263
|
function buildRuntimeForModel(job, malloyConfig, jobId) {
|
|
11949
12264
|
const urlReader = new HackyDataStylesAccumulator(makeWorkerUrlReader(jobId));
|
|
@@ -11970,8 +12285,9 @@ async function compileMalloyModel(job, malloyConfig, modelPath) {
|
|
|
11970
12285
|
const givens = malloyGivens.length > 0 ? malloyGivens.map((g) => malloyGivenToApi(g)) : undefined;
|
|
11971
12286
|
const { sourceInfos, importedNames } = await collectImportedSourceInfos(modelDef, runtime, importBaseURL);
|
|
11972
12287
|
appendLocalSourceInfos(modelDef, sourceInfos, importedNames);
|
|
11973
|
-
const { sources, filterMap } = extractSources(
|
|
11974
|
-
const queries = extractQueries(
|
|
12288
|
+
const { sources, filterMap } = extractSources(modelDef, givens);
|
|
12289
|
+
const queries = extractQueries(modelDef);
|
|
12290
|
+
await validateAuthorizeProbes(mm, sources);
|
|
11975
12291
|
return {
|
|
11976
12292
|
modelPath,
|
|
11977
12293
|
modelType: "model",
|
|
@@ -12076,10 +12392,11 @@ async function compileNotebookModel(job, malloyConfig, modelPath) {
|
|
|
12076
12392
|
const collected = await collectImportedSourceInfos(finalModelDef, runtime, importBaseURL);
|
|
12077
12393
|
appendLocalSourceInfos(finalModelDef, collected.sourceInfos, collected.importedNames);
|
|
12078
12394
|
finalSourceInfos = collected.sourceInfos;
|
|
12079
|
-
const extracted = extractSources(
|
|
12395
|
+
const extracted = extractSources(finalModelDef, finalGivens);
|
|
12080
12396
|
finalSources = extracted.sources;
|
|
12081
12397
|
finalFilterMap = extracted.filterMap;
|
|
12082
|
-
finalQueries = extractQueries(
|
|
12398
|
+
finalQueries = extractQueries(finalModelDef);
|
|
12399
|
+
await validateAuthorizeProbes(mm, finalSources);
|
|
12083
12400
|
}
|
|
12084
12401
|
return {
|
|
12085
12402
|
modelPath,
|
|
@@ -12137,7 +12454,7 @@ async function loadPackage(job) {
|
|
|
12137
12454
|
};
|
|
12138
12455
|
}
|
|
12139
12456
|
function serializeError(error) {
|
|
12140
|
-
if (error instanceof
|
|
12457
|
+
if (error instanceof MalloyError2) {
|
|
12141
12458
|
return {
|
|
12142
12459
|
name: error.name,
|
|
12143
12460
|
message: error.message,
|
|
@@ -12146,6 +12463,14 @@ function serializeError(error) {
|
|
|
12146
12463
|
isCompilationError: true
|
|
12147
12464
|
};
|
|
12148
12465
|
}
|
|
12466
|
+
if (error instanceof ModelCompilationError) {
|
|
12467
|
+
return {
|
|
12468
|
+
name: error.name,
|
|
12469
|
+
message: error.message,
|
|
12470
|
+
stack: error.stack,
|
|
12471
|
+
isCompilationError: true
|
|
12472
|
+
};
|
|
12473
|
+
}
|
|
12149
12474
|
if (error instanceof Error) {
|
|
12150
12475
|
return {
|
|
12151
12476
|
name: error.name,
|