@malloy-publisher/server 0.0.198-dev → 0.0.198-dev1
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/README.docker.md +135 -20
- package/README.md +15 -0
- package/build.ts +42 -1
- package/dist/app/api-doc.yaml +51 -0
- package/dist/app/assets/EnvironmentPage-Dpee_Kn6.js +1 -0
- package/dist/app/assets/HomePage-DLRWTNoL.js +1 -0
- package/dist/app/assets/MainPage-DsVt5QGM.js +2 -0
- package/dist/app/assets/ModelPage-AwAugZ37.js +1 -0
- package/dist/app/assets/PackagePage-XQ-EWGTC.js +1 -0
- package/dist/app/assets/RouteError-3Mv8JQw7.js +1 -0
- package/dist/app/assets/WorkbookPage-DHYYpcYc.js +1 -0
- package/dist/app/assets/{core-w79IMXAG.es-Bd0UlzOL.js → core-DfcpQGVP.es-DQggNOdX.js} +14 -14
- package/dist/app/assets/{index-C513UodQ.js → index-BUp81Qdm.js} +15 -15
- package/dist/app/assets/index-D1pdwrUW.js +1803 -0
- package/dist/app/assets/index-Dv5bF4Ii.js +451 -0
- package/dist/app/assets/{index.umd-BMeMPq_9.js → index.umd-CQH4LZU8.js} +1 -1
- package/dist/app/index.html +2 -3
- package/dist/compile_worker.mjs +628 -0
- package/dist/default-publisher.config.json +23 -0
- package/dist/instrumentation.mjs +36 -38
- package/dist/server.mjs +2060 -913
- package/package.json +11 -12
- package/publisher.config.example.bigquery.json +33 -0
- package/publisher.config.example.duckdb.json +23 -0
- package/publisher.config.json +1 -11
- package/src/compile/compile_pool.spec.ts +227 -0
- package/src/compile/compile_pool.ts +729 -0
- package/src/compile/compile_worker.ts +683 -0
- package/src/compile/protocol.ts +251 -0
- package/src/config.spec.ts +306 -0
- package/src/config.ts +222 -2
- package/src/controller/compile.controller.ts +3 -1
- package/src/controller/connection.controller.ts +1 -1
- package/src/controller/model.controller.ts +8 -1
- package/src/controller/package.controller.ts +70 -29
- package/src/controller/query.controller.ts +3 -0
- package/src/default-publisher.config.json +23 -0
- package/src/errors.spec.ts +42 -0
- package/src/errors.ts +21 -0
- package/src/health.spec.ts +90 -0
- package/src/health.ts +86 -45
- package/src/logger.ts +1 -3
- package/src/mcp/tools/discovery_tools.ts +6 -2
- package/src/mcp/tools/execute_query_tool.ts +12 -0
- package/src/path_safety.spec.ts +158 -0
- package/src/path_safety.ts +140 -0
- package/src/pg_helpers.spec.ts +226 -0
- package/src/pg_helpers.ts +129 -0
- package/src/server-old.ts +3 -23
- package/src/server.ts +49 -0
- package/src/service/connection.spec.ts +6 -4
- package/src/service/connection.ts +8 -3
- package/src/service/connection_config.ts +2 -2
- package/src/service/environment.ts +621 -176
- package/src/service/environment_admission.spec.ts +180 -0
- package/src/service/environment_store.ts +22 -0
- package/src/service/filter_integration.spec.ts +110 -0
- package/src/service/givens_integration.spec.ts +192 -0
- package/src/service/manifest_service.spec.ts +7 -2
- package/src/service/manifest_service.ts +8 -2
- package/src/service/materialization_service.ts +14 -3
- package/src/service/model.spec.ts +105 -0
- package/src/service/model.ts +317 -10
- package/src/service/model_worker_path.spec.ts +125 -0
- package/src/service/package.ts +4 -3
- package/src/service/package_memory_governor.spec.ts +173 -0
- package/src/service/package_memory_governor.ts +233 -0
- package/src/service/package_race.spec.ts +208 -0
- package/src/storage/StorageManager.ts +71 -11
- package/src/storage/duckdb/schema.ts +41 -0
- package/src/utils.ts +11 -0
- package/tests/harness/rest_e2e.ts +2 -2
- package/tests/integration/concurrent_package/concurrent_package.integration.spec.ts +280 -0
- package/tests/integration/legacy_routes/legacy_routes.integration.spec.ts +259 -0
- package/tests/unit/duckdb/attached_databases.test.ts +5 -5
- package/tests/unit/duckdb/legacy_schema_migration.test.ts +194 -0
- package/tests/unit/storage/StorageManager.test.ts +166 -0
- package/dist/app/assets/EnvironmentPage-1j6QDWAy.js +0 -1
- package/dist/app/assets/HomePage-DMop21VG.js +0 -1
- package/dist/app/assets/MainPage-BbE8ETz1.js +0 -2
- package/dist/app/assets/ModelPage-D2jvfe3t.js +0 -1
- package/dist/app/assets/PackagePage-BbnhGoD3.js +0 -1
- package/dist/app/assets/RouteError-D3LGEZ3i.js +0 -1
- package/dist/app/assets/WorkbookPage-DttVIj4u.js +0 -1
- package/dist/app/assets/index-5K9YjIxF.js +0 -456
- package/dist/app/assets/index-DIgzgp69.js +0 -1742
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
2
|
+
|
|
3
|
+
// TODO: Remove this during projects cleanup
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
6
|
+
import fs from "fs/promises";
|
|
7
|
+
import os from "os";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { DuckDBConnection } from "../../../src/storage/duckdb/DuckDBConnection";
|
|
10
|
+
import { initializeSchema } from "../../../src/storage/duckdb/schema";
|
|
11
|
+
|
|
12
|
+
const TEST_DB_DIR = path.join(os.tmpdir(), "duckdb-legacy-migration-tests");
|
|
13
|
+
|
|
14
|
+
// Seed a pre-rename schema on an *already-open* connection. We deliberately
|
|
15
|
+
// avoid opening, closing, and reopening the same DuckDB file within a test:
|
|
16
|
+
// on Windows runners the second `duckdb.Database(path)` call sometimes fails
|
|
17
|
+
// with `Invalid Error` because the OS hasn't released the file handle yet.
|
|
18
|
+
// Sharing one connection between seed and assertion sidesteps that entirely.
|
|
19
|
+
async function seedLegacySchema(db: DuckDBConnection): Promise<void> {
|
|
20
|
+
// Seed a pre-rename schema: parent table named `projects` and child
|
|
21
|
+
// tables with `project_id` foreign-key columns. Mirrors what an existing
|
|
22
|
+
// installation looked like before the projects→environments rename.
|
|
23
|
+
await db.run(`
|
|
24
|
+
CREATE TABLE projects (
|
|
25
|
+
id VARCHAR PRIMARY KEY,
|
|
26
|
+
name VARCHAR NOT NULL UNIQUE,
|
|
27
|
+
path VARCHAR NOT NULL,
|
|
28
|
+
description VARCHAR,
|
|
29
|
+
metadata JSON,
|
|
30
|
+
created_at TIMESTAMP NOT NULL,
|
|
31
|
+
updated_at TIMESTAMP NOT NULL
|
|
32
|
+
)
|
|
33
|
+
`);
|
|
34
|
+
await db.run(`
|
|
35
|
+
CREATE TABLE packages (
|
|
36
|
+
id VARCHAR PRIMARY KEY,
|
|
37
|
+
project_id VARCHAR NOT NULL,
|
|
38
|
+
name VARCHAR NOT NULL,
|
|
39
|
+
description VARCHAR,
|
|
40
|
+
manifest_path VARCHAR NOT NULL,
|
|
41
|
+
metadata JSON,
|
|
42
|
+
created_at TIMESTAMP NOT NULL,
|
|
43
|
+
updated_at TIMESTAMP NOT NULL,
|
|
44
|
+
FOREIGN KEY (project_id) REFERENCES projects(id)
|
|
45
|
+
)
|
|
46
|
+
`);
|
|
47
|
+
await db.run(`
|
|
48
|
+
CREATE TABLE connections (
|
|
49
|
+
id VARCHAR PRIMARY KEY,
|
|
50
|
+
project_id VARCHAR NOT NULL,
|
|
51
|
+
name VARCHAR NOT NULL,
|
|
52
|
+
type VARCHAR NOT NULL,
|
|
53
|
+
config JSON NOT NULL,
|
|
54
|
+
created_at TIMESTAMP NOT NULL,
|
|
55
|
+
updated_at TIMESTAMP NOT NULL,
|
|
56
|
+
FOREIGN KEY (project_id) REFERENCES projects(id)
|
|
57
|
+
)
|
|
58
|
+
`);
|
|
59
|
+
await db.run(`
|
|
60
|
+
CREATE TABLE materializations (
|
|
61
|
+
id VARCHAR PRIMARY KEY,
|
|
62
|
+
project_id VARCHAR NOT NULL,
|
|
63
|
+
package_name VARCHAR NOT NULL,
|
|
64
|
+
status VARCHAR NOT NULL,
|
|
65
|
+
active_key VARCHAR,
|
|
66
|
+
started_at TIMESTAMP,
|
|
67
|
+
completed_at TIMESTAMP,
|
|
68
|
+
error TEXT,
|
|
69
|
+
metadata JSON,
|
|
70
|
+
created_at TIMESTAMP NOT NULL,
|
|
71
|
+
updated_at TIMESTAMP NOT NULL,
|
|
72
|
+
FOREIGN KEY (project_id) REFERENCES projects(id)
|
|
73
|
+
)
|
|
74
|
+
`);
|
|
75
|
+
await db.run(`
|
|
76
|
+
CREATE TABLE build_manifests (
|
|
77
|
+
id VARCHAR PRIMARY KEY,
|
|
78
|
+
project_id VARCHAR NOT NULL,
|
|
79
|
+
package_name VARCHAR NOT NULL,
|
|
80
|
+
build_id VARCHAR NOT NULL,
|
|
81
|
+
table_name VARCHAR NOT NULL,
|
|
82
|
+
source_name VARCHAR NOT NULL,
|
|
83
|
+
connection_name VARCHAR NOT NULL,
|
|
84
|
+
created_at TIMESTAMP NOT NULL,
|
|
85
|
+
updated_at TIMESTAMP NOT NULL,
|
|
86
|
+
FOREIGN KEY (project_id) REFERENCES projects(id)
|
|
87
|
+
)
|
|
88
|
+
`);
|
|
89
|
+
|
|
90
|
+
await db.run(
|
|
91
|
+
`INSERT INTO projects VALUES ('p1', 'proj-one', '/p1', 'd1', NULL,
|
|
92
|
+
TIMESTAMP '2024-01-01 00:00:00', TIMESTAMP '2024-01-01 00:00:00')`,
|
|
93
|
+
);
|
|
94
|
+
await db.run(
|
|
95
|
+
`INSERT INTO packages VALUES ('pkg1', 'p1', 'pkg-one', NULL, '/m', NULL,
|
|
96
|
+
TIMESTAMP '2024-01-01 00:00:00', TIMESTAMP '2024-01-01 00:00:00')`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
describe("DuckDB legacy projects schema cleanup", () => {
|
|
101
|
+
beforeEach(async () => {
|
|
102
|
+
await fs.mkdir(TEST_DB_DIR, { recursive: true });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
afterEach(async () => {
|
|
106
|
+
try {
|
|
107
|
+
await fs.rm(TEST_DB_DIR, { recursive: true, force: true });
|
|
108
|
+
} catch {
|
|
109
|
+
// ignore
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("drops legacy projects schema and creates the new environments schema cleanly", async () => {
|
|
114
|
+
const dbPath = path.join(TEST_DB_DIR, "legacy.duckdb");
|
|
115
|
+
const db = new DuckDBConnection(dbPath);
|
|
116
|
+
await db.initialize();
|
|
117
|
+
|
|
118
|
+
// Seed the legacy schema on the same connection, then run the
|
|
119
|
+
// production schema-init path. This mirrors a server upgrade
|
|
120
|
+
// (legacy data on disk, new code starting up) without forcing a
|
|
121
|
+
// close+reopen, which is unreliable on Windows runners.
|
|
122
|
+
await seedLegacySchema(db);
|
|
123
|
+
await initializeSchema(db);
|
|
124
|
+
|
|
125
|
+
// Legacy parent table is gone.
|
|
126
|
+
const legacyProjects = await db.all<{ name: string }>(
|
|
127
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='projects'",
|
|
128
|
+
);
|
|
129
|
+
expect(legacyProjects.length).toBe(0);
|
|
130
|
+
|
|
131
|
+
// New `environments` table exists and is empty (legacy data dropped).
|
|
132
|
+
const envs = await db.all<{ id: string }>("SELECT id FROM environments");
|
|
133
|
+
expect(envs.length).toBe(0);
|
|
134
|
+
|
|
135
|
+
// Child tables are queryable by `environment_id` (the new column),
|
|
136
|
+
// proving they were recreated with the new schema rather than left on
|
|
137
|
+
// the old `project_id` column.
|
|
138
|
+
const pkgs = await db.all<{ id: string }>(
|
|
139
|
+
"SELECT id FROM packages WHERE environment_id = ?",
|
|
140
|
+
["p1"],
|
|
141
|
+
);
|
|
142
|
+
expect(pkgs.length).toBe(0);
|
|
143
|
+
const conns = await db.all<{ id: string }>(
|
|
144
|
+
"SELECT id FROM connections WHERE environment_id = ?",
|
|
145
|
+
["p1"],
|
|
146
|
+
);
|
|
147
|
+
expect(conns.length).toBe(0);
|
|
148
|
+
const mats = await db.all<{ id: string }>(
|
|
149
|
+
"SELECT id FROM materializations WHERE environment_id = ?",
|
|
150
|
+
["p1"],
|
|
151
|
+
);
|
|
152
|
+
expect(mats.length).toBe(0);
|
|
153
|
+
const manifests = await db.all<{ id: string }>(
|
|
154
|
+
"SELECT id FROM build_manifests WHERE environment_id = ?",
|
|
155
|
+
["p1"],
|
|
156
|
+
);
|
|
157
|
+
expect(manifests.length).toBe(0);
|
|
158
|
+
|
|
159
|
+
await db.close();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("is idempotent: running initializeSchema twice on a migrated DB is a no-op", async () => {
|
|
163
|
+
const dbPath = path.join(TEST_DB_DIR, "legacy_idempotent.duckdb");
|
|
164
|
+
const db = new DuckDBConnection(dbPath);
|
|
165
|
+
await db.initialize();
|
|
166
|
+
|
|
167
|
+
await seedLegacySchema(db);
|
|
168
|
+
await initializeSchema(db);
|
|
169
|
+
// Second call should hit the early-return path (isInitialized() === true).
|
|
170
|
+
await initializeSchema(db);
|
|
171
|
+
|
|
172
|
+
const envs = await db.all<{ id: string }>("SELECT id FROM environments");
|
|
173
|
+
expect(envs.length).toBe(0);
|
|
174
|
+
|
|
175
|
+
await db.close();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("creates a fresh schema unchanged when no legacy projects table is present", async () => {
|
|
179
|
+
const dbPath = path.join(TEST_DB_DIR, "fresh.duckdb");
|
|
180
|
+
const db = new DuckDBConnection(dbPath);
|
|
181
|
+
await db.initialize();
|
|
182
|
+
await initializeSchema(db);
|
|
183
|
+
|
|
184
|
+
const envs = await db.all<{ id: string }>("SELECT id FROM environments");
|
|
185
|
+
expect(envs.length).toBe(0);
|
|
186
|
+
|
|
187
|
+
const legacy = await db.all<{ name: string }>(
|
|
188
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='projects'",
|
|
189
|
+
);
|
|
190
|
+
expect(legacy.length).toBe(0);
|
|
191
|
+
|
|
192
|
+
await db.close();
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Unit tests for the catch-block wiring in
|
|
2
|
+
// StorageManager.attachDuckLakeCatalog. The pure helpers from
|
|
3
|
+
// `pg_helpers.ts` are tested directly in `pg_helpers.spec.ts`; this file
|
|
4
|
+
// covers the integration — that the helpers are invoked in the right
|
|
5
|
+
// order and the right places inside `initializeDuckLakeForEnvironment`.
|
|
6
|
+
//
|
|
7
|
+
// Stubs DuckDBConnection.run instead of using a real DuckDB so we can
|
|
8
|
+
// inject libpq-style errors at the ATTACH boundary without standing up a
|
|
9
|
+
// real Postgres.
|
|
10
|
+
//
|
|
11
|
+
// Lives under `tests/unit/` (not `src/`) on purpose: the `src/` unit-spec
|
|
12
|
+
// process imports `service/environment_store.spec.ts`, which calls
|
|
13
|
+
// `mock.module("../storage/StorageManager", ...)`. Bun's module mocks
|
|
14
|
+
// persist process-wide across spec files, so a sibling spec in `src/` that
|
|
15
|
+
// `import`s the real StorageManager would get the mock instead. Running
|
|
16
|
+
// here puts us in the separate `test:integration` process with a clean
|
|
17
|
+
// module cache.
|
|
18
|
+
import { describe, expect, it } from "bun:test";
|
|
19
|
+
import { ConnectionAuthError } from "../../../src/errors";
|
|
20
|
+
import { StorageManager } from "../../../src/storage/StorageManager";
|
|
21
|
+
|
|
22
|
+
interface PrivateStorageManager {
|
|
23
|
+
duckDbConnection: { run: (sql: string) => Promise<unknown> } | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PG_CONFIG = {
|
|
27
|
+
catalogUrl: "postgres:host=h user=u password=hunter2 dbname=catalog",
|
|
28
|
+
dataPath: "gs://bucket/path",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function setupWithStubbedConn(runHandler: (sql: string) => Promise<unknown>): {
|
|
32
|
+
sm: StorageManager;
|
|
33
|
+
calls: string[];
|
|
34
|
+
} {
|
|
35
|
+
const sm = new StorageManager({ type: "duckdb" });
|
|
36
|
+
const calls: string[] = [];
|
|
37
|
+
const stub = {
|
|
38
|
+
run: async (sql: string): Promise<unknown> => {
|
|
39
|
+
calls.push(sql);
|
|
40
|
+
return runHandler(sql);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
(sm as unknown as PrivateStorageManager).duckDbConnection = stub;
|
|
44
|
+
return { sm, calls };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("StorageManager.attachDuckLakeCatalog wiring", () => {
|
|
48
|
+
it("classifies libpq auth failure on ATTACH as ConnectionAuthError", async () => {
|
|
49
|
+
const { sm } = setupWithStubbedConn(async (sql) => {
|
|
50
|
+
if (sql.startsWith("ATTACH")) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'IO Error: Unable to connect to Postgres at "host=h user=u password=hunter2 ...": FATAL: password authentication failed for user "u"',
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await expect(
|
|
59
|
+
sm.initializeDuckLakeForEnvironment("env-1", "env-name", PG_CONFIG),
|
|
60
|
+
).rejects.toBeInstanceOf(ConnectionAuthError);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("redacts the embedded password in the classified error", async () => {
|
|
64
|
+
const { sm } = setupWithStubbedConn(async (sql) => {
|
|
65
|
+
if (sql.startsWith("ATTACH")) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"password authentication failed: tried host=h password=hunter2",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
await sm.initializeDuckLakeForEnvironment(
|
|
75
|
+
"env-1",
|
|
76
|
+
"env-name",
|
|
77
|
+
PG_CONFIG,
|
|
78
|
+
);
|
|
79
|
+
throw new Error("expected ATTACH to throw");
|
|
80
|
+
} catch (e) {
|
|
81
|
+
expect(e).toBeInstanceOf(ConnectionAuthError);
|
|
82
|
+
expect((e as Error).message).toContain("password=***");
|
|
83
|
+
expect((e as Error).message).not.toContain("hunter2");
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("injects connect_timeout into PG catalogUrl before ATTACH", async () => {
|
|
88
|
+
const { sm, calls } = setupWithStubbedConn(async () => undefined);
|
|
89
|
+
|
|
90
|
+
await sm.initializeDuckLakeForEnvironment("env-1", "env-name", PG_CONFIG);
|
|
91
|
+
|
|
92
|
+
const attachSql = calls.find((s) => s.startsWith("ATTACH"));
|
|
93
|
+
expect(attachSql).toBeDefined();
|
|
94
|
+
expect(attachSql).toContain("connect_timeout=");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("honors PG_CONNECT_TIMEOUT_SECONDS env override in the emitted SQL", async () => {
|
|
98
|
+
const original = process.env.PG_CONNECT_TIMEOUT_SECONDS;
|
|
99
|
+
process.env.PG_CONNECT_TIMEOUT_SECONDS = "17";
|
|
100
|
+
try {
|
|
101
|
+
const { sm, calls } = setupWithStubbedConn(async () => undefined);
|
|
102
|
+
await sm.initializeDuckLakeForEnvironment(
|
|
103
|
+
"env-1",
|
|
104
|
+
"env-name",
|
|
105
|
+
PG_CONFIG,
|
|
106
|
+
);
|
|
107
|
+
const attachSql = calls.find((s) => s.startsWith("ATTACH"));
|
|
108
|
+
expect(attachSql).toContain("connect_timeout=17");
|
|
109
|
+
} finally {
|
|
110
|
+
if (original === undefined) {
|
|
111
|
+
delete process.env.PG_CONNECT_TIMEOUT_SECONDS;
|
|
112
|
+
} else {
|
|
113
|
+
process.env.PG_CONNECT_TIMEOUT_SECONDS = original;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("leaves a non-PG catalogUrl untouched (no connect_timeout)", async () => {
|
|
119
|
+
const { sm, calls } = setupWithStubbedConn(async () => undefined);
|
|
120
|
+
const sqliteConfig = {
|
|
121
|
+
catalogUrl: "sqlite:/tmp/x.db",
|
|
122
|
+
dataPath: "/tmp/data",
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
await sm.initializeDuckLakeForEnvironment(
|
|
126
|
+
"env-1",
|
|
127
|
+
"env-name",
|
|
128
|
+
sqliteConfig,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const attachSql = calls.find((s) => s.startsWith("ATTACH"));
|
|
132
|
+
expect(attachSql).toBeDefined();
|
|
133
|
+
expect(attachSql).not.toContain("connect_timeout");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("rethrows non-auth errors from ATTACH unchanged (preserves cause)", async () => {
|
|
137
|
+
const original = new Error("disk I/O error: read failed");
|
|
138
|
+
const { sm } = setupWithStubbedConn(async (sql) => {
|
|
139
|
+
if (sql.startsWith("ATTACH")) throw original;
|
|
140
|
+
return undefined;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await expect(
|
|
144
|
+
sm.initializeDuckLakeForEnvironment("env-1", "env-name", PG_CONFIG),
|
|
145
|
+
).rejects.toBe(original);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("does not call connect_timeout injection when catalogUrl lacks postgres: prefix", async () => {
|
|
149
|
+
// Sanity: the isPostgres branch is detected purely by string prefix.
|
|
150
|
+
// A keyword-form string without the prefix shouldn't be misclassified.
|
|
151
|
+
const { sm, calls } = setupWithStubbedConn(async () => undefined);
|
|
152
|
+
const ambiguousConfig = {
|
|
153
|
+
catalogUrl: "host=h dbname=d", // no scheme prefix at all
|
|
154
|
+
dataPath: "/tmp/data",
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
await sm.initializeDuckLakeForEnvironment(
|
|
158
|
+
"env-1",
|
|
159
|
+
"env-name",
|
|
160
|
+
ambiguousConfig,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const attachSql = calls.find((s) => s.startsWith("ATTACH"));
|
|
164
|
+
expect(attachSql).not.toContain("connect_timeout");
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as r,t as a,j as e,E as i,J as o}from"./index-5K9YjIxF.js";function m(){const s=r(),{environmentName:n}=a();if(n){const t=i({environmentName:n});return e.jsx(o,{onSelectPackage:s,resourceUri:t})}else return e.jsx("div",{children:e.jsx("h2",{children:"Missing environment name"})})}export{m as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as n,j as t,a as o}from"./index-5K9YjIxF.js";function s(){const a=n();return t.jsx(o,{onClickEnvironment:a})}export{s as default};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{u as V,g as F,r as g,R as _,b as X,c as j,e as M,f as A,j as r,s as m,h as w,i as v,k as I,P as Y,m as R,l as J,n as z,B as K,o as N,p as Z,T as U,q,t as oo,d as eo,v as f,C as S,w as ro,x as to,I as ao,M as so,y as $,S as no,z as lo,A as io,O as co,D as po}from"./index-5K9YjIxF.js";function uo(o,e,t,a,n){const[s,i]=g.useState(()=>n&&t?t(o).matches:a?a(o).matches:e);return X(()=>{if(!t)return;const p=t(o),u=()=>{i(p.matches)};return u(),p.addEventListener("change",u),()=>{p.removeEventListener("change",u)}},[o,t]),s}const go={..._},L=go.useSyncExternalStore;function xo(o,e,t,a,n){const s=g.useCallback(()=>e,[e]),i=g.useMemo(()=>{if(n&&t)return()=>t(o).matches;if(a!==null){const{matches:c}=a(o);return()=>c}return s},[s,o,a,n,t]),[p,u]=g.useMemo(()=>{if(t===null)return[s,()=>()=>{}];const c=t(o);return[()=>c.matches,l=>(c.addEventListener("change",l),()=>{c.removeEventListener("change",l)})]},[s,t,o]);return L(u,p,i)}function O(o={}){const{themeId:e}=o;return function(a,n={}){let s=V();s&&e&&(s=s[e]||s);const i=typeof window<"u"&&typeof window.matchMedia<"u",{defaultMatches:p=!1,matchMedia:u=i?window.matchMedia:null,ssrMatchMedia:d=null,noSsr:c=!1}=F({name:"MuiUseMediaQuery",props:n,theme:s});let l=typeof a=="function"?a(s):a;return l=l.replace(/^@media( ?)/m,""),l.includes("print")&&console.warn(["MUI: You have provided a `print` query to the `useMediaQuery` hook.","Using the print media query to modify print styles can lead to unexpected results.","Consider using the `displayPrint` field in the `sx` prop instead.","More information about `displayPrint` on our docs: https://mui.com/system/display/#display-in-print."].join(`
|
|
2
|
-
`)),(L!==void 0?xo:uo)(l,p,u,d,c)}}O();function mo(o){return j("MuiAppBar",o)}M("MuiAppBar",["root","positionFixed","positionAbsolute","positionSticky","positionStatic","positionRelative","colorDefault","colorPrimary","colorSecondary","colorInherit","colorTransparent","colorError","colorInfo","colorSuccess","colorWarning"]);const fo=o=>{const{color:e,position:t,classes:a}=o,n={root:["root",`color${v(e)}`,`position${v(t)}`]};return I(n,mo,a)},D=(o,e)=>o?`${o?.replace(")","")}, ${e})`:e,bo=m(Y,{name:"MuiAppBar",slot:"Root",overridesResolver:(o,e)=>{const{ownerState:t}=o;return[e.root,e[`position${v(t.position)}`],e[`color${v(t.color)}`]]}})(R(({theme:o})=>({display:"flex",flexDirection:"column",width:"100%",boxSizing:"border-box",flexShrink:0,variants:[{props:{position:"fixed"},style:{position:"fixed",zIndex:(o.vars||o).zIndex.appBar,top:0,left:"auto",right:0,"@media print":{position:"absolute"}}},{props:{position:"absolute"},style:{position:"absolute",zIndex:(o.vars||o).zIndex.appBar,top:0,left:"auto",right:0}},{props:{position:"sticky"},style:{position:"sticky",zIndex:(o.vars||o).zIndex.appBar,top:0,left:"auto",right:0}},{props:{position:"static"},style:{position:"static"}},{props:{position:"relative"},style:{position:"relative"}},{props:{color:"inherit"},style:{"--AppBar-color":"inherit"}},{props:{color:"default"},style:{"--AppBar-background":o.vars?o.vars.palette.AppBar.defaultBg:o.palette.grey[100],"--AppBar-color":o.vars?o.vars.palette.text.primary:o.palette.getContrastText(o.palette.grey[100]),...o.applyStyles("dark",{"--AppBar-background":o.vars?o.vars.palette.AppBar.defaultBg:o.palette.grey[900],"--AppBar-color":o.vars?o.vars.palette.text.primary:o.palette.getContrastText(o.palette.grey[900])})}},...Object.entries(o.palette).filter(J(["contrastText"])).map(([e])=>({props:{color:e},style:{"--AppBar-background":(o.vars??o).palette[e].main,"--AppBar-color":(o.vars??o).palette[e].contrastText}})),{props:e=>e.enableColorOnDark===!0&&!["inherit","transparent"].includes(e.color),style:{backgroundColor:"var(--AppBar-background)",color:"var(--AppBar-color)"}},{props:e=>e.enableColorOnDark===!1&&!["inherit","transparent"].includes(e.color),style:{backgroundColor:"var(--AppBar-background)",color:"var(--AppBar-color)",...o.applyStyles("dark",{backgroundColor:o.vars?D(o.vars.palette.AppBar.darkBg,"var(--AppBar-background)"):null,color:o.vars?D(o.vars.palette.AppBar.darkColor,"var(--AppBar-color)"):null})}},{props:{color:"transparent"},style:{"--AppBar-background":"transparent","--AppBar-color":"inherit",backgroundColor:"var(--AppBar-background)",color:"var(--AppBar-color)",...o.applyStyles("dark",{backgroundImage:"none"})}}]}))),ho=g.forwardRef(function(e,t){const a=A({props:e,name:"MuiAppBar"}),{className:n,color:s="primary",enableColorOnDark:i=!1,position:p="fixed",...u}=a,d={...a,color:s,position:p,enableColorOnDark:i},c=fo(d);return r.jsx(bo,{square:!0,component:"header",ownerState:d,elevation:4,className:w(c.root,n,p==="fixed"&&"mui-fixed"),ref:t,...u})}),yo=z(r.jsx("path",{d:"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"})),vo=m(K,{name:"MuiBreadcrumbCollapsed"})(R(({theme:o})=>({display:"flex",marginLeft:`calc(${o.spacing(1)} * 0.5)`,marginRight:`calc(${o.spacing(1)} * 0.5)`,...o.palette.mode==="light"?{backgroundColor:o.palette.grey[100],color:o.palette.grey[700]}:{backgroundColor:o.palette.grey[700],color:o.palette.grey[100]},borderRadius:2,"&:hover, &:focus":{...o.palette.mode==="light"?{backgroundColor:o.palette.grey[200]}:{backgroundColor:o.palette.grey[600]}},"&:active":{boxShadow:o.shadows[0],...o.palette.mode==="light"?{backgroundColor:N(o.palette.grey[200],.12)}:{backgroundColor:N(o.palette.grey[600],.12)}}}))),ko=m(yo)({width:24,height:16});function Bo(o){const{slots:e={},slotProps:t={},...a}=o,n=o;return r.jsx("li",{children:r.jsx(vo,{focusRipple:!0,...a,ownerState:n,children:r.jsx(ko,{as:e.CollapsedIcon,ownerState:n,...t.collapsedIcon})})})}function Co(o){return j("MuiBreadcrumbs",o)}const So=M("MuiBreadcrumbs",["root","ol","li","separator"]),jo=o=>{const{classes:e}=o;return I({root:["root"],li:["li"],ol:["ol"],separator:["separator"]},Co,e)},Mo=m(U,{name:"MuiBreadcrumbs",slot:"Root",overridesResolver:(o,e)=>[{[`& .${So.li}`]:e.li},e.root]})({}),Ao=m("ol",{name:"MuiBreadcrumbs",slot:"Ol"})({display:"flex",flexWrap:"wrap",alignItems:"center",padding:0,margin:0,listStyle:"none"}),wo=m("li",{name:"MuiBreadcrumbs",slot:"Separator"})({display:"flex",userSelect:"none",marginLeft:8,marginRight:8});function Io(o,e,t,a){return o.reduce((n,s,i)=>(i<o.length-1?n=n.concat(s,r.jsx(wo,{"aria-hidden":!0,className:e,ownerState:a,children:t},`separator-${i}`)):n.push(s),n),[])}const Ro=g.forwardRef(function(e,t){const a=A({props:e,name:"MuiBreadcrumbs"}),{children:n,className:s,component:i="nav",slots:p={},slotProps:u={},expandText:d="Show path",itemsAfterCollapse:c=1,itemsBeforeCollapse:l=1,maxItems:h=8,separator:k="/",...Q}=a,[T,W]=g.useState(!1),b={...a,component:i,expanded:T,expandText:d,itemsAfterCollapse:c,itemsBeforeCollapse:l,maxItems:h,separator:k},y=jo(b),H=Z({elementType:p.CollapsedIcon,externalSlotProps:u.collapsedIcon,ownerState:b}),P=g.useRef(null),G=x=>{const C=()=>{W(!0);const E=P.current.querySelector("a[href],button,[tabindex]");E&&E.focus()};return l+c>=x.length?x:[...x.slice(0,l),r.jsx(Bo,{"aria-label":d,slots:{CollapsedIcon:p.CollapsedIcon},slotProps:{collapsedIcon:H},onClick:C},"ellipsis"),...x.slice(x.length-c,x.length)]},B=g.Children.toArray(n).filter(x=>g.isValidElement(x)).map((x,C)=>r.jsx("li",{className:y.li,children:x},`child-${C}`));return r.jsx(Mo,{ref:t,component:i,color:"textSecondary",className:w(y.root,s),ownerState:b,...Q,children:r.jsx(Ao,{className:y.ol,ref:P,ownerState:b,children:Io(T||h&&B.length<=h?B:G(B),y.separator,k,b)})})});function zo(o){return j("MuiToolbar",o)}M("MuiToolbar",["root","gutters","regular","dense"]);const To=o=>{const{classes:e,disableGutters:t,variant:a}=o;return I({root:["root",!t&&"gutters",a]},zo,e)},Po=m("div",{name:"MuiToolbar",slot:"Root",overridesResolver:(o,e)=>{const{ownerState:t}=o;return[e.root,!t.disableGutters&&e.gutters,e[t.variant]]}})(R(({theme:o})=>({position:"relative",display:"flex",alignItems:"center",variants:[{props:({ownerState:e})=>!e.disableGutters,style:{paddingLeft:o.spacing(2),paddingRight:o.spacing(2),[o.breakpoints.up("sm")]:{paddingLeft:o.spacing(3),paddingRight:o.spacing(3)}}},{props:{variant:"dense"},style:{minHeight:48}},{props:{variant:"regular"},style:o.mixins.toolbar}]}))),Eo=g.forwardRef(function(e,t){const a=A({props:e,name:"MuiToolbar"}),{className:n,component:s="div",disableGutters:i=!1,variant:p="regular",...u}=a,d={...a,component:s,disableGutters:i,variant:p},c=To(d);return r.jsx(Po,{as:s,className:w(c.root,n),ref:t,ownerState:d,...u})}),No=O({themeId:q}),$o=z(r.jsx("path",{d:"M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"})),Do=z(r.jsx("path",{d:"M3 18h18v-2H3zm0-5h18v-2H3zm0-7v2h18V6z"}));function Uo(){const o=oo(),e=o["*"],t=eo();return r.jsx(f,{sx:{display:"flex",alignItems:"center"},children:r.jsxs(Ro,{"aria-label":"breadcrumb",separator:r.jsx($o,{sx:{fontSize:14,color:"text.secondary"}}),sx:{"& .MuiBreadcrumbs-separator":{margin:"0 6px"}},children:[o.environmentName&&r.jsx(S,{onClick:a=>t(`/${o.environmentName}/`,a),label:o.environmentName,size:"medium",sx:{backgroundColor:"background.paper",color:"primary.main",fontWeight:500,height:"32px",fontSize:"1rem",cursor:"pointer","&:hover":{backgroundColor:"primary.100"}}}),o.packageName&&r.jsx(S,{onClick:a=>t(`/${o.environmentName}/${o.packageName}/`,a),label:o.packageName,size:"medium",sx:{backgroundColor:"background.paper",color:"primary.main",fontWeight:500,height:"32px",fontSize:"1rem",cursor:"pointer","&:hover":{backgroundColor:"secondary.100"}}}),e&&r.jsx(S,{onClick:a=>t(`/${o.environmentName}/${o.packageName}/${e}`,a),label:e,size:"medium",sx:{backgroundColor:"background.paper",color:"primary.main",fontWeight:500,height:"32px",fontSize:"1rem",cursor:"pointer","&:hover":{backgroundColor:"grey.200"}}})]})})}function Lo({logoHeader:o,endCap:e}){const t=ro(),a=to(),n=No(a.breakpoints.down("sm")),[s,i]=g.useState(null),p=!!s,u=l=>{i(l.currentTarget)},d=()=>i(null),c=[{label:"Malloy Docs",link:"https://docs.malloydata.dev/documentation/",sx:{color:"primary.main"}},{label:"Publisher Docs",link:"https://github.com/malloydata/publisher/blob/main/README.md",sx:{color:"primary.main"}},{label:"Publisher API",link:"/api-doc.html",sx:{color:"primary.main"}}];return r.jsxs(ho,{position:"sticky",elevation:0,sx:{backgroundColor:"background.paper",borderBottom:"1px solid",borderColor:"divider"},children:[r.jsxs(Eo,{sx:{justifyContent:"space-between",flexWrap:"nowrap",minHeight:44},children:[o||r.jsxs(f,{sx:{display:"flex",alignItems:"center",gap:1,cursor:"pointer"},onClick:()=>t("/"),children:[r.jsx(f,{component:"img",src:"/logo.svg",alt:"Malloy",sx:{width:28,height:28}}),r.jsx(U,{variant:"h6",sx:{color:"text.primary",fontWeight:700,letterSpacing:"-0.025em",fontSize:{xs:"1.1rem",sm:"1.25rem"}},children:"Malloy Publisher"})]}),n?r.jsxs(r.Fragment,{children:[r.jsx(ao,{color:"inherit",onClick:u,children:r.jsx(Do,{})}),r.jsxs(so,{anchorEl:s,open:p,onClose:d,anchorOrigin:{vertical:"bottom",horizontal:"right"},children:[c.map(l=>r.jsx($,{onClick:()=>{d(),window.location.href=l.link},sx:l.sx,children:l.label},l.label)),e&&r.jsx($,{children:e})]})]}):r.jsxs(no,{direction:"row",spacing:2,alignItems:"center",children:[c.map(l=>r.jsx(lo,{href:l.link,sx:l.sx,children:l.label},l.label)),e]})]}),r.jsx(f,{sx:{borderTop:"1px solid white",paddingLeft:"16px",paddingRight:"16px",marginBottom:"1px",overflowX:"auto"},children:r.jsx(Uo,{})})]})}function Qo({headerProps:o}){return r.jsxs(f,{sx:{display:"flex",flexDirection:"column",minHeight:"100vh"},children:[r.jsx(Lo,{...o}),r.jsx(io,{maxWidth:"xl",component:"main",sx:{flex:1,display:"flex",flexDirection:"column",py:2,gap:2},children:r.jsx(f,{sx:{flex:1},children:r.jsx(g.Suspense,{fallback:r.jsx(po,{}),children:r.jsx(co,{})})})})]})}export{Qo as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as r,j as e,E as i,F as t,G as m}from"./index-5K9YjIxF.js";function d(){const n=r(),a=n["*"];if(!n.environmentName)return e.jsx("div",{children:e.jsx("h2",{children:"Missing environment name"})});if(!n.packageName)return e.jsx("div",{children:e.jsx("h2",{children:"Missing package name"})});const s=i({environmentName:n.environmentName,packageName:n.packageName,modelPath:a});return a?.endsWith(".malloy")?e.jsx(t,{resourceUri:s,runOnDemand:!0,maxResultSize:512*1024}):a?.endsWith(".malloynb")?e.jsx(m,{resourceUri:s,maxResultSize:1024*1024}):e.jsx("div",{children:e.jsxs("h2",{children:["Unrecognized file type: ",a]})})}export{d as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as r,d as t,j as e,E as c,H as o}from"./index-5K9YjIxF.js";function m(){const{environmentName:n,packageName:s}=r(),a=t();if(n)if(s){const i=c({environmentName:n,packageName:s});return e.jsx(o,{onClickPackageFile:a,resourceUri:i})}else return e.jsx("div",{children:e.jsx("h2",{children:"Missing package name"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing environment name"})})}export{m as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{K as o,j as r,A as s,S as n,v as t,T as a}from"./index-5K9YjIxF.js";function x(){const e=o();return console.error(e),r.jsx(s,{maxWidth:"lg",component:"main",sx:{display:"flex",flexDirection:"column",my:2,gap:0},children:r.jsxs(n,{sx:{m:"auto",flexDirection:"column"},children:[r.jsx(t,{sx:{height:"300px"}}),r.jsx("img",{src:"/error.png"}),r.jsx(a,{variant:"subtitle1",children:"An unexpected error occurred"})]})})}export{x as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as a,j as e,E as t,Z as c}from"./index-5K9YjIxF.js";function d(){const{workspace:r,workbookPath:s,environmentName:i,packageName:n}=a();if(r)if(s)if(i)if(n){const o=t({environmentName:i,packageName:n});return e.jsx(c,{workbookPath:{path:s,workspace:r},resourceUri:o},`${s}`)}else return e.jsx("div",{children:e.jsx("h2",{children:"Missing package name"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing environment name"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing workbook path"})});else return e.jsx("div",{children:e.jsx("h2",{children:"Missing workspace"})})}export{d as default};
|