@malloy-publisher/server 0.0.219 → 0.0.221
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/app/api-doc.yaml +291 -20
- package/dist/app/assets/{EnvironmentPage-gehnjfC6.js → EnvironmentPage-D6G5n6mY.js} +1 -1
- package/dist/app/assets/{HomePage-8LQBytE4.js → HomePage-DgZluD8u.js} +1 -1
- package/dist/app/assets/LightMode-w9v0pEbg.js +1 -0
- package/dist/app/assets/MainPage-Bi_Tukvr.js +2 -0
- package/dist/app/assets/{MaterializationsPage-D7P1Kp6O.js → MaterializationsPage-BVERjnmG.js} +1 -1
- package/dist/app/assets/ModelPage-2OjS259-.js +1 -0
- package/dist/app/assets/{PackagePage-Cz9fVwSG.js → PackagePage-C8L2On4z.js} +1 -1
- package/dist/app/assets/{RouteError-UONCloyN.js → RouteError-DJoNC_Vl.js} +1 -1
- package/dist/app/assets/ThemeEditorPage-B-hJ1zgd.js +1 -0
- package/dist/app/assets/{WorkbookPage-Bhzqvbq_.js → WorkbookPage-CTbDgGeJ.js} +1 -1
- package/dist/app/assets/{core-BiGj7BML.es-kMHAa8tP.js → core-D9Hl0IDY.es-CmgzHn4c.js} +1 -1
- package/dist/app/assets/github-dark-DenFmJkN.es-DHJKELXO.js +1 -0
- package/dist/app/assets/index-8E2uLeV9.js +2541 -0
- package/dist/app/assets/index-BEjJCJjX.js +1761 -0
- package/dist/app/assets/index-BF8PkFm8.js +1 -0
- package/dist/app/assets/index-Cr-asgoV.js +527 -0
- package/dist/app/assets/index-DcCw_qxr.js +18 -0
- package/dist/app/assets/index-w_0OQJgZ.js +23 -0
- package/dist/app/index.html +1 -1
- package/dist/package_load_worker.mjs +20 -2
- package/dist/runtime/publisher.js +6 -1
- package/dist/server.mjs +412 -55
- package/dist/sshcrypto-8m50vnmb.node +0 -0
- package/package.json +12 -12
- package/src/config.theme.spec.ts +178 -0
- package/src/config.ts +179 -0
- package/src/controller/materialization.controller.spec.ts +4 -4
- package/src/controller/materialization.controller.ts +2 -2
- package/src/controller/package.controller.spec.ts +89 -1
- package/src/controller/package.controller.ts +28 -3
- package/src/controller/theme.controller.ts +83 -0
- package/src/dto/connection.dto.ts +6 -0
- package/src/materialization_metrics.ts +1 -1
- package/src/package_load/package_load_pool.ts +2 -1
- package/src/package_load/package_load_worker.ts +5 -2
- package/src/package_load/protocol.ts +2 -1
- package/src/runtime/publisher.js +6 -1
- package/src/server.ts +58 -1
- package/src/service/build_plan.spec.ts +61 -17
- package/src/service/build_plan.ts +35 -11
- package/src/service/connection.ts +52 -6
- package/src/service/connection_fingerprint.spec.ts +102 -0
- package/src/service/manifest_loader.spec.ts +5 -5
- package/src/service/manifest_loader.ts +4 -4
- package/src/service/materialization_cron_gate.spec.ts +137 -0
- package/src/service/materialization_schedule_surface.spec.ts +30 -1
- package/src/service/materialization_service.spec.ts +47 -37
- package/src/service/materialization_service.ts +59 -50
- package/src/service/materialization_test_fixtures.ts +22 -6
- package/src/service/model.ts +1 -1
- package/src/service/package.ts +50 -4
- package/src/service/package_manifest.spec.ts +42 -4
- package/src/service/package_manifest.ts +53 -4
- package/src/service/theme_store.ts +199 -0
- package/src/storage/DatabaseInterface.ts +1 -1
- package/src/storage/StorageManager.ts +17 -0
- package/src/storage/duckdb/schema.ts +27 -6
- package/src/theme_key_parity.spec.ts +57 -0
- package/tests/integration/materialization/manifest_binding.integration.spec.ts +1 -1
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +5 -5
- package/tests/unit/controller/theme.controller.spec.ts +141 -0
- package/tests/unit/service/theme_store.spec.ts +139 -0
- package/dist/app/assets/MainPage-DDaZLJw-.js +0 -2
- package/dist/app/assets/ModelPage-Do_vhxOc.js +0 -1
- package/dist/app/assets/index-VzRbxcF7.js +0 -455
- package/dist/app/assets/index-ddq4-5hu.js +0 -1760
- package/dist/app/assets/index-qOQF9CXq.js +0 -40
- package/dist/app/assets/index.umd-B2kmxDh7.js +0 -2467
package/dist/server.mjs
CHANGED
|
@@ -158407,6 +158407,11 @@ var require_utils74 = __commonJS((exports, module) => {
|
|
|
158407
158407
|
};
|
|
158408
158408
|
});
|
|
158409
158409
|
|
|
158410
|
+
// ../../node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
|
|
158411
|
+
var require_sshcrypto = __commonJS((exports, module) => {
|
|
158412
|
+
module.exports = __require("./sshcrypto-8m50vnmb.node");
|
|
158413
|
+
});
|
|
158414
|
+
|
|
158410
158415
|
// ../../node_modules/ssh2/lib/protocol/crypto/poly1305.js
|
|
158411
158416
|
var require_poly1305 = __commonJS((exports, module) => {
|
|
158412
158417
|
var __dirname = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto", __filename = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto/poly1305.js";
|
|
@@ -158893,7 +158898,7 @@ var require_crypto = __commonJS((exports, module) => {
|
|
|
158893
158898
|
var ChaChaPolyDecipher;
|
|
158894
158899
|
var GenericDecipher;
|
|
158895
158900
|
try {
|
|
158896
|
-
binding = (
|
|
158901
|
+
binding = require_sshcrypto();
|
|
158897
158902
|
({
|
|
158898
158903
|
AESGCMCipher,
|
|
158899
158904
|
ChaChaPolyCipher,
|
|
@@ -242924,6 +242929,15 @@ function resolvePublisherConfigPath(serverRoot) {
|
|
|
242924
242929
|
}
|
|
242925
242930
|
return null;
|
|
242926
242931
|
}
|
|
242932
|
+
var PER_MODE_COLOR_KEYS = [
|
|
242933
|
+
"background",
|
|
242934
|
+
"tableHeader",
|
|
242935
|
+
"tableHeaderBackground",
|
|
242936
|
+
"tableBody",
|
|
242937
|
+
"tile",
|
|
242938
|
+
"tileTitle",
|
|
242939
|
+
"mapColor"
|
|
242940
|
+
];
|
|
242927
242941
|
var DEFAULT_HIGH_WATER_FRACTION = 0.8;
|
|
242928
242942
|
var DEFAULT_LOW_WATER_FRACTION = 0.7;
|
|
242929
242943
|
var DEFAULT_CHECK_INTERVAL_MS = 5000;
|
|
@@ -243113,11 +243127,98 @@ var getPublisherConfig = (serverRoot) => {
|
|
|
243113
243127
|
if (processedConfig && typeof processedConfig === "object" && "frozenConfig" in processedConfig) {
|
|
243114
243128
|
frozenConfig = Boolean(processedConfig.frozenConfig);
|
|
243115
243129
|
}
|
|
243130
|
+
const instanceTheme = sanitizeTheme(processedConfig && typeof processedConfig === "object" && "theme" in processedConfig ? processedConfig.theme : undefined, "publisher.config.json");
|
|
243116
243131
|
return {
|
|
243117
243132
|
frozenConfig,
|
|
243133
|
+
...instanceTheme ? { theme: instanceTheme } : {},
|
|
243118
243134
|
environments
|
|
243119
243135
|
};
|
|
243120
243136
|
};
|
|
243137
|
+
function sanitizeTheme(raw, context) {
|
|
243138
|
+
if (raw === undefined || raw === null)
|
|
243139
|
+
return;
|
|
243140
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
243141
|
+
logger.warn(`Invalid "theme" in ${context}: expected an object. Ignoring.`);
|
|
243142
|
+
return;
|
|
243143
|
+
}
|
|
243144
|
+
const obj = raw;
|
|
243145
|
+
const theme = {};
|
|
243146
|
+
if ("defaultMode" in obj) {
|
|
243147
|
+
const mode = obj.defaultMode;
|
|
243148
|
+
if (mode === "light" || mode === "dark" || mode === "auto") {
|
|
243149
|
+
theme.defaultMode = mode;
|
|
243150
|
+
} else {
|
|
243151
|
+
logger.warn(`Invalid "theme.defaultMode" in ${context}: expected "light" | "dark" | "auto" (got ${JSON.stringify(mode)}). Ignoring field.`);
|
|
243152
|
+
}
|
|
243153
|
+
}
|
|
243154
|
+
if ("allowUserToggle" in obj) {
|
|
243155
|
+
const value = obj.allowUserToggle;
|
|
243156
|
+
if (typeof value === "boolean") {
|
|
243157
|
+
theme.allowUserToggle = value;
|
|
243158
|
+
} else {
|
|
243159
|
+
logger.warn(`Invalid "theme.allowUserToggle" in ${context}: expected a boolean (got ${JSON.stringify(value)}). Ignoring field.`);
|
|
243160
|
+
}
|
|
243161
|
+
}
|
|
243162
|
+
if ("palette" in obj && obj.palette && typeof obj.palette === "object") {
|
|
243163
|
+
const palette = obj.palette;
|
|
243164
|
+
const sanitized = {};
|
|
243165
|
+
if (Array.isArray(palette.series)) {
|
|
243166
|
+
sanitized.series = palette.series.filter((c) => typeof c === "string");
|
|
243167
|
+
}
|
|
243168
|
+
for (const key of PER_MODE_COLOR_KEYS) {
|
|
243169
|
+
const raw2 = palette[key];
|
|
243170
|
+
if (!raw2 || typeof raw2 !== "object" || Array.isArray(raw2))
|
|
243171
|
+
continue;
|
|
243172
|
+
const r = raw2;
|
|
243173
|
+
const out = {};
|
|
243174
|
+
if (typeof r.light === "string")
|
|
243175
|
+
out.light = r.light;
|
|
243176
|
+
if (typeof r.dark === "string")
|
|
243177
|
+
out.dark = r.dark;
|
|
243178
|
+
if (Object.keys(out).length > 0) {
|
|
243179
|
+
sanitized[key] = out;
|
|
243180
|
+
}
|
|
243181
|
+
}
|
|
243182
|
+
if (Object.keys(sanitized).length > 0)
|
|
243183
|
+
theme.palette = sanitized;
|
|
243184
|
+
}
|
|
243185
|
+
if ("font" in obj && obj.font && typeof obj.font === "object") {
|
|
243186
|
+
const font = obj.font;
|
|
243187
|
+
const sanitized = {};
|
|
243188
|
+
if (typeof font.family === "string")
|
|
243189
|
+
sanitized.family = font.family;
|
|
243190
|
+
if (typeof font.size === "number" && Number.isFinite(font.size)) {
|
|
243191
|
+
sanitized.size = font.size;
|
|
243192
|
+
}
|
|
243193
|
+
if (Object.keys(sanitized).length > 0)
|
|
243194
|
+
theme.font = sanitized;
|
|
243195
|
+
}
|
|
243196
|
+
return Object.keys(theme).length > 0 ? theme : undefined;
|
|
243197
|
+
}
|
|
243198
|
+
function mergeThemes(base, override) {
|
|
243199
|
+
if (!base)
|
|
243200
|
+
return override;
|
|
243201
|
+
if (!override)
|
|
243202
|
+
return base;
|
|
243203
|
+
const merged = { ...base, ...override };
|
|
243204
|
+
if (base.palette || override.palette) {
|
|
243205
|
+
merged.palette = {
|
|
243206
|
+
...base.palette ?? {},
|
|
243207
|
+
...override.palette ?? {}
|
|
243208
|
+
};
|
|
243209
|
+
for (const key of PER_MODE_COLOR_KEYS) {
|
|
243210
|
+
const b = base.palette?.[key];
|
|
243211
|
+
const o = override.palette?.[key];
|
|
243212
|
+
if (b || o) {
|
|
243213
|
+
merged.palette[key] = { ...b ?? {}, ...o ?? {} };
|
|
243214
|
+
}
|
|
243215
|
+
}
|
|
243216
|
+
}
|
|
243217
|
+
if (base.font || override.font) {
|
|
243218
|
+
merged.font = { ...base.font ?? {}, ...override.font ?? {} };
|
|
243219
|
+
}
|
|
243220
|
+
return merged;
|
|
243221
|
+
}
|
|
243121
243222
|
var isPublisherConfigFrozen = (serverRoot) => {
|
|
243122
243223
|
try {
|
|
243123
243224
|
const publisherConfig = getPublisherConfig(serverRoot);
|
|
@@ -243193,17 +243294,29 @@ var getProcessedPublisherConfig = (serverRoot) => {
|
|
|
243193
243294
|
logger.warn(`Environment "${environment.name}" has no valid packages. Skipping entry.`);
|
|
243194
243295
|
continue;
|
|
243195
243296
|
}
|
|
243297
|
+
const envTheme = sanitizeTheme(environment.theme, `environment "${environment.name}"`);
|
|
243298
|
+
const resolvedTheme = mergeThemes(rawConfig.theme, envTheme);
|
|
243196
243299
|
validEnvironments.push({
|
|
243197
243300
|
name: environment.name,
|
|
243198
243301
|
packages: validPackages,
|
|
243199
|
-
connections: convertConnectionsToApiConnections(environment.connections || [])
|
|
243302
|
+
connections: convertConnectionsToApiConnections(environment.connections || []),
|
|
243303
|
+
...resolvedTheme ? { theme: resolvedTheme } : {}
|
|
243200
243304
|
});
|
|
243201
243305
|
}
|
|
243202
243306
|
return {
|
|
243203
243307
|
frozenConfig: rawConfig.frozenConfig ?? false,
|
|
243308
|
+
...rawConfig.theme ? { theme: rawConfig.theme } : {},
|
|
243204
243309
|
environments: validEnvironments
|
|
243205
243310
|
};
|
|
243206
243311
|
};
|
|
243312
|
+
var getInstanceTheme = (serverRoot) => {
|
|
243313
|
+
try {
|
|
243314
|
+
return getPublisherConfig(serverRoot).theme;
|
|
243315
|
+
} catch (error) {
|
|
243316
|
+
logger.error(`Error reading instance theme from ${PUBLISHER_CONFIG_NAME}`, { error });
|
|
243317
|
+
return;
|
|
243318
|
+
}
|
|
243319
|
+
};
|
|
243207
243320
|
|
|
243208
243321
|
// src/controller/connection.controller.ts
|
|
243209
243322
|
init_errors();
|
|
@@ -247871,6 +247984,13 @@ function buildAzureDuckDBConnection(metadata, entry) {
|
|
|
247871
247984
|
function getMetadataForLookup(metadata, name) {
|
|
247872
247985
|
return name ? metadata.get(name) : undefined;
|
|
247873
247986
|
}
|
|
247987
|
+
function applyConnectionFingerprint(connection, metadata) {
|
|
247988
|
+
const fingerprint = metadata?.apiConnection.fingerprint;
|
|
247989
|
+
if (fingerprint) {
|
|
247990
|
+
connection.getDigest = () => fingerprint;
|
|
247991
|
+
}
|
|
247992
|
+
return connection;
|
|
247993
|
+
}
|
|
247874
247994
|
function isDuckDBConnection(connection) {
|
|
247875
247995
|
return connection instanceof DuckDBConnection;
|
|
247876
247996
|
}
|
|
@@ -247896,9 +248016,8 @@ function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", is
|
|
|
247896
248016
|
}
|
|
247897
248017
|
await attachPromise;
|
|
247898
248018
|
}
|
|
247899
|
-
malloyConfig.wrapConnections((base) =>
|
|
247900
|
-
|
|
247901
|
-
const metadata = getMetadataForLookup(assembled.metadata, name);
|
|
248019
|
+
malloyConfig.wrapConnections((base) => {
|
|
248020
|
+
const resolveConnection = async (name, metadata) => {
|
|
247902
248021
|
if (metadata?.isDuckLake) {
|
|
247903
248022
|
let connectionPromise = duckLakeCache.get(name);
|
|
247904
248023
|
if (!connectionPromise) {
|
|
@@ -247970,8 +248089,14 @@ function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", is
|
|
|
247970
248089
|
await attachOnce(connection, metadata);
|
|
247971
248090
|
}
|
|
247972
248091
|
return connection;
|
|
247973
|
-
}
|
|
247974
|
-
|
|
248092
|
+
};
|
|
248093
|
+
return {
|
|
248094
|
+
lookupConnection: async (name) => {
|
|
248095
|
+
const metadata = getMetadataForLookup(assembled.metadata, name);
|
|
248096
|
+
return applyConnectionFingerprint(await resolveConnection(name, metadata), metadata);
|
|
248097
|
+
}
|
|
248098
|
+
};
|
|
248099
|
+
});
|
|
247975
248100
|
return {
|
|
247976
248101
|
malloyConfig,
|
|
247977
248102
|
apiConnections: assembled.apiConnections,
|
|
@@ -249832,6 +249957,14 @@ class ModelController {
|
|
|
249832
249957
|
// src/controller/package.controller.ts
|
|
249833
249958
|
init_constants();
|
|
249834
249959
|
init_errors();
|
|
249960
|
+
function formatPublishRejections(pkg, exploresOverride) {
|
|
249961
|
+
const message = [
|
|
249962
|
+
pkg.formatInvalidExplores(exploresOverride),
|
|
249963
|
+
pkg.formatInvalidSchedule()
|
|
249964
|
+
].filter(Boolean).join(`
|
|
249965
|
+
`);
|
|
249966
|
+
return message || undefined;
|
|
249967
|
+
}
|
|
249835
249968
|
|
|
249836
249969
|
class PackageController {
|
|
249837
249970
|
environmentStore;
|
|
@@ -249872,7 +250005,7 @@ class PackageController {
|
|
|
249872
250005
|
let result;
|
|
249873
250006
|
if (body.location) {
|
|
249874
250007
|
const bodyLocation = body.location;
|
|
249875
|
-
result = await environment.installPackage(packageName, (stagingPath) => this.downloadInto(environmentName, packageName, bodyLocation, stagingPath), (pkg) => pkg
|
|
250008
|
+
result = await environment.installPackage(packageName, (stagingPath) => this.downloadInto(environmentName, packageName, bodyLocation, stagingPath), (pkg) => formatPublishRejections(pkg));
|
|
249876
250009
|
} else {
|
|
249877
250010
|
result = await environment.addPackage(packageName);
|
|
249878
250011
|
}
|
|
@@ -249880,7 +250013,7 @@ class PackageController {
|
|
|
249880
250013
|
throw new Error(`Failed to create package ${packageName}`);
|
|
249881
250014
|
}
|
|
249882
250015
|
if (!body.location) {
|
|
249883
|
-
const invalidMsg = result
|
|
250016
|
+
const invalidMsg = formatPublishRejections(result);
|
|
249884
250017
|
if (invalidMsg) {
|
|
249885
250018
|
await environment.unloadPackage(packageName).catch(() => {});
|
|
249886
250019
|
throw new BadRequestError(invalidMsg);
|
|
@@ -249905,7 +250038,7 @@ class PackageController {
|
|
|
249905
250038
|
const environment = await this.environmentStore.getEnvironment(environmentName, false);
|
|
249906
250039
|
if (body.location) {
|
|
249907
250040
|
const bodyLocation = body.location;
|
|
249908
|
-
await environment.installPackage(packageName, (stagingPath) => this.downloadInto(environmentName, packageName, bodyLocation, stagingPath), (pkg) => pkg
|
|
250041
|
+
await environment.installPackage(packageName, (stagingPath) => this.downloadInto(environmentName, packageName, bodyLocation, stagingPath), (pkg) => formatPublishRejections(pkg, body.explores?.map(normalizeModelPath)));
|
|
249909
250042
|
}
|
|
249910
250043
|
const result = await environment.updatePackage(packageName, body);
|
|
249911
250044
|
await this.environmentStore.addPackageToDatabase(environmentName, packageName);
|
|
@@ -256512,13 +256645,10 @@ class DuckDBRepository {
|
|
|
256512
256645
|
init_logger();
|
|
256513
256646
|
async function initializeSchema(db, force = false) {
|
|
256514
256647
|
const initialized = await db.isInitialized();
|
|
256515
|
-
if (initialized && !force) {
|
|
256516
|
-
return;
|
|
256517
|
-
}
|
|
256518
256648
|
if (force) {
|
|
256519
256649
|
logger.info("Reinitializing database schema dropping and recreating all tables");
|
|
256520
256650
|
await dropAllTables(db);
|
|
256521
|
-
} else {
|
|
256651
|
+
} else if (!initialized) {
|
|
256522
256652
|
await dropLegacyProjectSchema(db);
|
|
256523
256653
|
logger.info("Creating database schema for the first time...");
|
|
256524
256654
|
}
|
|
@@ -256577,6 +256707,14 @@ async function initializeSchema(db, force = false) {
|
|
|
256577
256707
|
FOREIGN KEY (environment_id) REFERENCES environments(id)
|
|
256578
256708
|
)
|
|
256579
256709
|
`);
|
|
256710
|
+
await db.run(`
|
|
256711
|
+
CREATE TABLE IF NOT EXISTS themes (
|
|
256712
|
+
id VARCHAR PRIMARY KEY,
|
|
256713
|
+
payload JSON NOT NULL,
|
|
256714
|
+
created_at TIMESTAMP NOT NULL,
|
|
256715
|
+
updated_at TIMESTAMP NOT NULL
|
|
256716
|
+
)
|
|
256717
|
+
`);
|
|
256580
256718
|
await db.run("CREATE INDEX IF NOT EXISTS idx_packages_environment_id ON packages(environment_id)");
|
|
256581
256719
|
await db.run("CREATE INDEX IF NOT EXISTS idx_connections_environment_id ON connections(environment_id)");
|
|
256582
256720
|
await db.run("CREATE INDEX IF NOT EXISTS idx_materializations_environment_package ON materializations(environment_id, package_name)");
|
|
@@ -256609,7 +256747,8 @@ async function dropAllTables(db) {
|
|
|
256609
256747
|
"materializations",
|
|
256610
256748
|
"packages",
|
|
256611
256749
|
"connections",
|
|
256612
|
-
"environments"
|
|
256750
|
+
"environments",
|
|
256751
|
+
"themes"
|
|
256613
256752
|
];
|
|
256614
256753
|
logger.info("Dropping tables:", tables.join(", "));
|
|
256615
256754
|
for (const table of tables) {
|
|
@@ -256655,6 +256794,15 @@ class StorageManager {
|
|
|
256655
256794
|
this.connection = connection;
|
|
256656
256795
|
this.repository = new DuckDBRepository(connection);
|
|
256657
256796
|
}
|
|
256797
|
+
getDuckDbConnection() {
|
|
256798
|
+
if (!this.connection) {
|
|
256799
|
+
throw new Error("Storage not initialized");
|
|
256800
|
+
}
|
|
256801
|
+
if (!(this.connection instanceof DuckDBConnection2)) {
|
|
256802
|
+
throw new Error("Underlying storage connection is not DuckDB");
|
|
256803
|
+
}
|
|
256804
|
+
return this.connection;
|
|
256805
|
+
}
|
|
256658
256806
|
getRepository() {
|
|
256659
256807
|
if (!this.repository) {
|
|
256660
256808
|
throw new Error("Storage not initialized. Call initialize() first.");
|
|
@@ -256801,16 +256949,16 @@ async function fetchManifestEntries(uri) {
|
|
|
256801
256949
|
throw new Error(`Failed to parse build manifest at ${uri}: ${err instanceof Error ? err.message : String(err)}`);
|
|
256802
256950
|
}
|
|
256803
256951
|
const entries = {};
|
|
256804
|
-
for (const [
|
|
256952
|
+
for (const [sourceEntityId, entry] of Object.entries(parsed.entries ?? {})) {
|
|
256805
256953
|
const physicalTableName = entry?.physicalTableName;
|
|
256806
256954
|
if (!physicalTableName) {
|
|
256807
256955
|
logger.warn("Manifest entry has no physicalTableName; skipping", {
|
|
256808
256956
|
uri,
|
|
256809
|
-
|
|
256957
|
+
sourceEntityId
|
|
256810
256958
|
});
|
|
256811
256959
|
continue;
|
|
256812
256960
|
}
|
|
256813
|
-
entries[
|
|
256961
|
+
entries[sourceEntityId] = { tableName: physicalTableName };
|
|
256814
256962
|
}
|
|
256815
256963
|
return entries;
|
|
256816
256964
|
}
|
|
@@ -258302,7 +258450,7 @@ function* iterGraphSources(graph, sources) {
|
|
|
258302
258450
|
}
|
|
258303
258451
|
}
|
|
258304
258452
|
}
|
|
258305
|
-
function
|
|
258453
|
+
function computeSourceEntityId(source, connectionDigests) {
|
|
258306
258454
|
return source.makeBuildId(connectionDigests[source.connectionName], source.getSQL());
|
|
258307
258455
|
}
|
|
258308
258456
|
async function resolvePackageConnections(pkg, names) {
|
|
@@ -258385,15 +258533,18 @@ function deriveBuildPlan(graphs, sources, connectionDigests, sourceNames, source
|
|
|
258385
258533
|
for (const [sourceID, source] of Object.entries(sources)) {
|
|
258386
258534
|
if (include && !include.has(source.name))
|
|
258387
258535
|
continue;
|
|
258536
|
+
const annotationFields = deriveAnnotationFields(source);
|
|
258388
258537
|
wireSources[sourceID] = {
|
|
258389
258538
|
name: source.name,
|
|
258390
258539
|
sourceID: source.sourceID,
|
|
258391
258540
|
connectionName: source.connectionName,
|
|
258392
258541
|
dialect: source.dialectName,
|
|
258393
|
-
|
|
258542
|
+
sourceEntityId: computeSourceEntityId(source, connectionDigests),
|
|
258394
258543
|
sql: source.getSQL(),
|
|
258544
|
+
sharing: annotationFields.sharing ?? null,
|
|
258545
|
+
refresh: annotationFields.refresh ?? null,
|
|
258395
258546
|
columns: deriveColumns(source),
|
|
258396
|
-
annotationFields
|
|
258547
|
+
annotationFields,
|
|
258397
258548
|
modelPath: sourceModelPaths?.[sourceID]
|
|
258398
258549
|
};
|
|
258399
258550
|
}
|
|
@@ -258559,7 +258710,8 @@ class Package {
|
|
|
258559
258710
|
queryableSources: outcome.packageMetadata.queryableSources,
|
|
258560
258711
|
manifestLocation: outcome.packageMetadata.manifestLocation ?? null,
|
|
258561
258712
|
materialization: outcome.packageMetadata.materialization ?? {
|
|
258562
|
-
schedule: null
|
|
258713
|
+
schedule: null,
|
|
258714
|
+
freshness: null
|
|
258563
258715
|
}
|
|
258564
258716
|
};
|
|
258565
258717
|
const models = new Map;
|
|
@@ -258613,6 +258765,13 @@ class Package {
|
|
|
258613
258765
|
detail: invalidMsg
|
|
258614
258766
|
});
|
|
258615
258767
|
}
|
|
258768
|
+
const invalidSchedule = pkg.formatInvalidSchedule();
|
|
258769
|
+
if (invalidSchedule) {
|
|
258770
|
+
logger.warn(`Package ${packageName} has an invalid cron schedule`, {
|
|
258771
|
+
packageName,
|
|
258772
|
+
detail: invalidSchedule
|
|
258773
|
+
});
|
|
258774
|
+
}
|
|
258616
258775
|
pkg.logEmptyDiscoveryWarnings();
|
|
258617
258776
|
return pkg;
|
|
258618
258777
|
}
|
|
@@ -258684,6 +258843,17 @@ class Package {
|
|
|
258684
258843
|
}
|
|
258685
258844
|
formatInvalidExplores(exploresOverride) {
|
|
258686
258845
|
return this.exploreWarnings(exploresOverride).join(`
|
|
258846
|
+
`);
|
|
258847
|
+
}
|
|
258848
|
+
scheduleWarnings() {
|
|
258849
|
+
const schedule = this.packageMetadata.materialization?.schedule;
|
|
258850
|
+
if (!schedule)
|
|
258851
|
+
return [];
|
|
258852
|
+
const sources = Object.values(this.buildPlan?.sources ?? {});
|
|
258853
|
+
return sources.filter((source) => source.sharing !== "private").map((source) => `materialization.schedule (cron) in ${PACKAGE_MANIFEST_NAME} requires every ` + `persist source to declare '#@ persist ... sharing=private'; source ` + `'${source.name}' resolves to ${source.sharing ? `'${source.sharing}'` : "unset"}. Declare 'materialization.freshness.window' instead (the control plane ` + `schedules refreshes from it), or mark every persist source sharing=private.`);
|
|
258854
|
+
}
|
|
258855
|
+
formatInvalidSchedule() {
|
|
258856
|
+
return this.scheduleWarnings().join(`
|
|
258687
258857
|
`);
|
|
258688
258858
|
}
|
|
258689
258859
|
emptyDiscoveryWarnings() {
|
|
@@ -261030,7 +261200,7 @@ class MaterializationController {
|
|
|
261030
261200
|
}
|
|
261031
261201
|
const instruction = raw;
|
|
261032
261202
|
const required = [
|
|
261033
|
-
"
|
|
261203
|
+
"sourceEntityId",
|
|
261034
261204
|
"materializedTableId",
|
|
261035
261205
|
"physicalTableName",
|
|
261036
261206
|
"realization"
|
|
@@ -261044,7 +261214,7 @@ class MaterializationController {
|
|
|
261044
261214
|
throw new BadRequestError("Build instruction 'realization' must be COPY or SNAPSHOT");
|
|
261045
261215
|
}
|
|
261046
261216
|
return {
|
|
261047
|
-
|
|
261217
|
+
sourceEntityId: instruction.sourceEntityId,
|
|
261048
261218
|
sourceID: typeof instruction.sourceID === "string" ? instruction.sourceID : undefined,
|
|
261049
261219
|
materializedTableId: instruction.materializedTableId,
|
|
261050
261220
|
physicalTableName: instruction.physicalTableName,
|
|
@@ -261065,6 +261235,40 @@ class MaterializationController {
|
|
|
261065
261235
|
}
|
|
261066
261236
|
}
|
|
261067
261237
|
|
|
261238
|
+
// src/controller/theme.controller.ts
|
|
261239
|
+
init_errors();
|
|
261240
|
+
class ThemeController {
|
|
261241
|
+
themeStore;
|
|
261242
|
+
serverRoot;
|
|
261243
|
+
constructor(themeStore, serverRoot) {
|
|
261244
|
+
this.themeStore = themeStore;
|
|
261245
|
+
this.serverRoot = serverRoot;
|
|
261246
|
+
}
|
|
261247
|
+
getTheme = async () => {
|
|
261248
|
+
const theme = await this.themeStore.get();
|
|
261249
|
+
return theme ?? {};
|
|
261250
|
+
};
|
|
261251
|
+
putTheme = async (body) => {
|
|
261252
|
+
if (isPublisherConfigFrozen(this.serverRoot)) {
|
|
261253
|
+
throw new FrozenConfigError('Cannot edit theme: publisher.config.json has "frozenConfig": true.');
|
|
261254
|
+
}
|
|
261255
|
+
const isEmptyObject2 = typeof body === "object" && body !== null && !Array.isArray(body) && Object.keys(body).length === 0;
|
|
261256
|
+
const sanitized = sanitizeTheme(body, "PUT /api/v0/theme");
|
|
261257
|
+
if (!sanitized && !isEmptyObject2) {
|
|
261258
|
+
throw new BadRequestError("Theme payload was malformed (expected an object).");
|
|
261259
|
+
}
|
|
261260
|
+
const saved = await this.themeStore.set(sanitized ?? {});
|
|
261261
|
+
return saved;
|
|
261262
|
+
};
|
|
261263
|
+
resetTheme = async () => {
|
|
261264
|
+
if (isPublisherConfigFrozen(this.serverRoot)) {
|
|
261265
|
+
throw new FrozenConfigError('Cannot reset theme: publisher.config.json has "frozenConfig": true.');
|
|
261266
|
+
}
|
|
261267
|
+
const reseeded = await this.themeStore.reset();
|
|
261268
|
+
return reseeded ?? {};
|
|
261269
|
+
};
|
|
261270
|
+
}
|
|
261271
|
+
|
|
261068
261272
|
// ../../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
261069
261273
|
var DEFAULT_REQUEST_TIMEOUT_MSEC = 60000;
|
|
261070
261274
|
|
|
@@ -267628,9 +267832,9 @@ async function resolveEnvironmentId(repository, environmentName) {
|
|
|
267628
267832
|
}
|
|
267629
267833
|
|
|
267630
267834
|
// src/service/materialization_service.ts
|
|
267631
|
-
var
|
|
267632
|
-
function stagingSuffix(
|
|
267633
|
-
return `_${
|
|
267835
|
+
var STAGING_ID_LEN = 12;
|
|
267836
|
+
function stagingSuffix(sourceEntityId) {
|
|
267837
|
+
return `_${sourceEntityId.replace(/-/g, "").substring(0, STAGING_ID_LEN)}`;
|
|
267634
267838
|
}
|
|
267635
267839
|
function selfAssignTableName(persistSource) {
|
|
267636
267840
|
return deriveAnnotationFields(persistSource).name || persistSource.name;
|
|
@@ -267792,18 +267996,18 @@ class MaterializationService {
|
|
|
267792
267996
|
for (const persistSource of iterGraphSources(graph, compiled.sources)) {
|
|
267793
267997
|
if (include && !include.has(persistSource.name))
|
|
267794
267998
|
continue;
|
|
267795
|
-
const
|
|
267796
|
-
if (seen.has(
|
|
267999
|
+
const sourceEntityId = computeSourceEntityId(persistSource, compiled.connectionDigests);
|
|
268000
|
+
if (seen.has(sourceEntityId))
|
|
267797
268001
|
continue;
|
|
267798
|
-
seen.add(
|
|
267799
|
-
const prior = priorEntries[
|
|
268002
|
+
seen.add(sourceEntityId);
|
|
268003
|
+
const prior = priorEntries[sourceEntityId];
|
|
267800
268004
|
if (prior && prior.physicalTableName) {
|
|
267801
|
-
carried[
|
|
268005
|
+
carried[sourceEntityId] = prior;
|
|
267802
268006
|
continue;
|
|
267803
268007
|
}
|
|
267804
268008
|
instructions.push({
|
|
267805
|
-
|
|
267806
|
-
materializedTableId: `local-${
|
|
268009
|
+
sourceEntityId,
|
|
268010
|
+
materializedTableId: `local-${sourceEntityId.substring(0, STAGING_ID_LEN)}`,
|
|
267807
268011
|
physicalTableName: selfAssignTableName(persistSource),
|
|
267808
268012
|
realization: "COPY"
|
|
267809
268013
|
});
|
|
@@ -267824,9 +268028,11 @@ class MaterializationService {
|
|
|
267824
268028
|
}
|
|
267825
268029
|
async autoLoadManifest(environment, packageName, entries) {
|
|
267826
268030
|
const manifestEntries = {};
|
|
267827
|
-
for (const [
|
|
268031
|
+
for (const [sourceEntityId, entry] of Object.entries(entries)) {
|
|
267828
268032
|
if (entry.physicalTableName) {
|
|
267829
|
-
manifestEntries[
|
|
268033
|
+
manifestEntries[sourceEntityId] = {
|
|
268034
|
+
tableName: entry.physicalTableName
|
|
268035
|
+
};
|
|
267830
268036
|
}
|
|
267831
268037
|
}
|
|
267832
268038
|
try {
|
|
@@ -267848,13 +268054,13 @@ class MaterializationService {
|
|
|
267848
268054
|
if (!plan) {
|
|
267849
268055
|
throw new BadRequestError("Package has no persist sources; buildInstructions cannot be applied");
|
|
267850
268056
|
}
|
|
267851
|
-
const
|
|
268057
|
+
const plannedSourceEntityIds = new Set;
|
|
267852
268058
|
for (const source of Object.values(plan.sources)) {
|
|
267853
|
-
|
|
268059
|
+
plannedSourceEntityIds.add(source.sourceEntityId);
|
|
267854
268060
|
}
|
|
267855
268061
|
for (const instruction of instructions) {
|
|
267856
|
-
if (!
|
|
267857
|
-
throw new BadRequestError(`Instruction references unknown
|
|
268062
|
+
if (!plannedSourceEntityIds.has(instruction.sourceEntityId)) {
|
|
268063
|
+
throw new BadRequestError(`Instruction references unknown sourceEntityId '${instruction.sourceEntityId}'`);
|
|
267858
268064
|
}
|
|
267859
268065
|
if (instruction.realization === "SNAPSHOT") {
|
|
267860
268066
|
throw new BadRequestError("realization=SNAPSHOT is not supported (COPY only)");
|
|
@@ -267864,20 +268070,22 @@ class MaterializationService {
|
|
|
267864
268070
|
async executeInstructedBuild(compiled, instructions, seedEntries, signal) {
|
|
267865
268071
|
const { graphs, sources, connectionDigests, connections } = compiled;
|
|
267866
268072
|
const bySourceID = new Map;
|
|
267867
|
-
const
|
|
268073
|
+
const bySourceEntityId = new Map;
|
|
267868
268074
|
for (const instruction of instructions) {
|
|
267869
268075
|
if (instruction.sourceID) {
|
|
267870
268076
|
bySourceID.set(instruction.sourceID, instruction);
|
|
267871
268077
|
}
|
|
267872
|
-
|
|
268078
|
+
bySourceEntityId.set(instruction.sourceEntityId, instruction);
|
|
267873
268079
|
}
|
|
267874
268080
|
const manifest = new Manifest;
|
|
267875
268081
|
const entries = {};
|
|
267876
|
-
for (const [
|
|
268082
|
+
for (const [sourceEntityId, entry] of Object.entries(seedEntries)) {
|
|
267877
268083
|
if (entry.physicalTableName) {
|
|
267878
|
-
manifest.update(
|
|
268084
|
+
manifest.update(sourceEntityId, {
|
|
268085
|
+
tableName: entry.physicalTableName
|
|
268086
|
+
});
|
|
267879
268087
|
}
|
|
267880
|
-
entries[
|
|
268088
|
+
entries[sourceEntityId] = entry;
|
|
267881
268089
|
}
|
|
267882
268090
|
for (const graph of graphs) {
|
|
267883
268091
|
const connection = connections.get(graph.connectionName);
|
|
@@ -267887,25 +268095,25 @@ class MaterializationService {
|
|
|
267887
268095
|
for (const persistSource of iterGraphSources(graph, sources)) {
|
|
267888
268096
|
if (signal.aborted)
|
|
267889
268097
|
throw new Error("Build cancelled");
|
|
267890
|
-
const
|
|
267891
|
-
const instruction = bySourceID.get(persistSource.sourceID) ??
|
|
268098
|
+
const sourceEntityId = computeSourceEntityId(persistSource, connectionDigests);
|
|
268099
|
+
const instruction = bySourceID.get(persistSource.sourceID) ?? bySourceEntityId.get(sourceEntityId);
|
|
267892
268100
|
if (!instruction)
|
|
267893
268101
|
continue;
|
|
267894
268102
|
const entry = await this.buildOneSource(persistSource, instruction, connection, connectionDigests, manifest);
|
|
267895
|
-
entries[
|
|
268103
|
+
entries[sourceEntityId] = entry;
|
|
267896
268104
|
}
|
|
267897
268105
|
}
|
|
267898
268106
|
return entries;
|
|
267899
268107
|
}
|
|
267900
268108
|
async buildOneSource(persistSource, instruction, connection, connectionDigests, manifest) {
|
|
267901
|
-
const
|
|
268109
|
+
const sourceEntityId = instruction.sourceEntityId;
|
|
267902
268110
|
const physicalTableName = instruction.physicalTableName;
|
|
267903
268111
|
const buildSQL = persistSource.getSQL({
|
|
267904
268112
|
buildManifest: manifest.buildManifest,
|
|
267905
268113
|
connectionDigests
|
|
267906
268114
|
});
|
|
267907
268115
|
const bareName = bareTableName(physicalTableName);
|
|
267908
|
-
const stagingTableName = `${physicalTableName}${stagingSuffix(
|
|
268116
|
+
const stagingTableName = `${physicalTableName}${stagingSuffix(sourceEntityId)}`;
|
|
267909
268117
|
const dialect = persistSource.dialectName;
|
|
267910
268118
|
const quotedStaging = quoteTablePath(stagingTableName, dialect);
|
|
267911
268119
|
const quotedPhysical = quoteTablePath(physicalTableName, dialect);
|
|
@@ -267928,7 +268136,7 @@ class MaterializationService {
|
|
|
267928
268136
|
}
|
|
267929
268137
|
throw err;
|
|
267930
268138
|
}
|
|
267931
|
-
manifest.update(
|
|
268139
|
+
manifest.update(sourceEntityId, { tableName: physicalTableName });
|
|
267932
268140
|
const durationMs = Math.round(performance.now() - startTime);
|
|
267933
268141
|
recordSourceBuildDuration(durationMs);
|
|
267934
268142
|
logger.info(`Built materialized source ${persistSource.name}`, {
|
|
@@ -267936,7 +268144,7 @@ class MaterializationService {
|
|
|
267936
268144
|
durationMs
|
|
267937
268145
|
});
|
|
267938
268146
|
return {
|
|
267939
|
-
|
|
268147
|
+
sourceEntityId,
|
|
267940
268148
|
sourceName: persistSource.name,
|
|
267941
268149
|
materializedTableId: instruction.materializedTableId,
|
|
267942
268150
|
physicalTableName,
|
|
@@ -267993,7 +268201,7 @@ class MaterializationService {
|
|
|
267993
268201
|
if (!connectionName || !physicalTableName) {
|
|
267994
268202
|
logger.warn("Skipping manifest entry with no connection/table", {
|
|
267995
268203
|
materializationId: m.id,
|
|
267996
|
-
|
|
268204
|
+
sourceEntityId: entry.sourceEntityId
|
|
267997
268205
|
});
|
|
267998
268206
|
continue;
|
|
267999
268207
|
}
|
|
@@ -268005,7 +268213,7 @@ class MaterializationService {
|
|
|
268005
268213
|
}
|
|
268006
268214
|
const dialect = connection.dialectName;
|
|
268007
268215
|
await connection.runSQL(`DROP TABLE IF EXISTS ${quoteTablePath(physicalTableName, dialect)}`);
|
|
268008
|
-
await connection.runSQL(`DROP TABLE IF EXISTS ${quoteTablePath(`${physicalTableName}${stagingSuffix(entry.
|
|
268216
|
+
await connection.runSQL(`DROP TABLE IF EXISTS ${quoteTablePath(`${physicalTableName}${stagingSuffix(entry.sourceEntityId)}`, dialect)}`);
|
|
268009
268217
|
recordDropTables("success");
|
|
268010
268218
|
logger.info("Dropped materialized table on delete", {
|
|
268011
268219
|
materializationId: m.id,
|
|
@@ -268172,6 +268380,122 @@ class PackageMemoryGovernor {
|
|
|
268172
268380
|
}
|
|
268173
268381
|
}
|
|
268174
268382
|
|
|
268383
|
+
// src/service/theme_store.ts
|
|
268384
|
+
init_logger();
|
|
268385
|
+
var THEME_ROW_ID = "default";
|
|
268386
|
+
|
|
268387
|
+
class ThemeStore {
|
|
268388
|
+
storageManager;
|
|
268389
|
+
serverRoot;
|
|
268390
|
+
mutex = new Mutex;
|
|
268391
|
+
cached;
|
|
268392
|
+
cacheLoaded = false;
|
|
268393
|
+
constructor(storageManager, serverRoot) {
|
|
268394
|
+
this.storageManager = storageManager;
|
|
268395
|
+
this.serverRoot = serverRoot;
|
|
268396
|
+
}
|
|
268397
|
+
get db() {
|
|
268398
|
+
return this.storageManager.getDuckDbConnection();
|
|
268399
|
+
}
|
|
268400
|
+
async initialize() {
|
|
268401
|
+
await this.mutex.runExclusive(async () => {
|
|
268402
|
+
await this.loadLocked();
|
|
268403
|
+
});
|
|
268404
|
+
}
|
|
268405
|
+
async get() {
|
|
268406
|
+
if (!this.cacheLoaded) {
|
|
268407
|
+
await this.initialize();
|
|
268408
|
+
}
|
|
268409
|
+
return this.cached;
|
|
268410
|
+
}
|
|
268411
|
+
async set(theme) {
|
|
268412
|
+
return this.mutex.runExclusive(async () => {
|
|
268413
|
+
await this.persistLocked(theme);
|
|
268414
|
+
this.cached = theme;
|
|
268415
|
+
this.cacheLoaded = true;
|
|
268416
|
+
return theme;
|
|
268417
|
+
});
|
|
268418
|
+
}
|
|
268419
|
+
async reset() {
|
|
268420
|
+
return this.mutex.runExclusive(async () => {
|
|
268421
|
+
await this.db.run("DELETE FROM themes WHERE id = ?", [THEME_ROW_ID]);
|
|
268422
|
+
this.cached = undefined;
|
|
268423
|
+
this.cacheLoaded = false;
|
|
268424
|
+
await this.loadLocked();
|
|
268425
|
+
return this.cached;
|
|
268426
|
+
});
|
|
268427
|
+
}
|
|
268428
|
+
async loadLocked() {
|
|
268429
|
+
if (this.cacheLoaded)
|
|
268430
|
+
return;
|
|
268431
|
+
const row = await this.db.get("SELECT payload FROM themes WHERE id = ?", [THEME_ROW_ID]);
|
|
268432
|
+
if (row?.payload) {
|
|
268433
|
+
let parsed;
|
|
268434
|
+
try {
|
|
268435
|
+
parsed = JSON.parse(row.payload);
|
|
268436
|
+
} catch (error) {
|
|
268437
|
+
logger.warn("ThemeStore: stored theme JSON is unparseable; treating as empty", { error });
|
|
268438
|
+
this.cacheLoaded = true;
|
|
268439
|
+
return;
|
|
268440
|
+
}
|
|
268441
|
+
const sanitized = sanitizeTheme(parsed, "ThemeStore.load");
|
|
268442
|
+
const dropped = listSanitizerDrops(parsed, sanitized);
|
|
268443
|
+
if (dropped.length > 0) {
|
|
268444
|
+
logger.info("ThemeStore: dropped stale fields from stored theme during load", { dropped });
|
|
268445
|
+
}
|
|
268446
|
+
this.cached = sanitized;
|
|
268447
|
+
} else {
|
|
268448
|
+
const seed = getInstanceTheme(this.serverRoot);
|
|
268449
|
+
if (seed) {
|
|
268450
|
+
this.cached = seed;
|
|
268451
|
+
await this.persistLocked(seed);
|
|
268452
|
+
}
|
|
268453
|
+
}
|
|
268454
|
+
this.cacheLoaded = true;
|
|
268455
|
+
}
|
|
268456
|
+
async persistLocked(theme) {
|
|
268457
|
+
const now = new Date().toISOString();
|
|
268458
|
+
const payload = JSON.stringify(theme);
|
|
268459
|
+
await this.db.run(`INSERT INTO themes (id, payload, created_at, updated_at)
|
|
268460
|
+
VALUES (?, ?, ?, ?)
|
|
268461
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
268462
|
+
payload = EXCLUDED.payload,
|
|
268463
|
+
updated_at = EXCLUDED.updated_at`, [THEME_ROW_ID, payload, now, now]);
|
|
268464
|
+
}
|
|
268465
|
+
}
|
|
268466
|
+
function listSanitizerDrops(raw, sanitized) {
|
|
268467
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
268468
|
+
return [];
|
|
268469
|
+
const r = raw;
|
|
268470
|
+
const drops = [];
|
|
268471
|
+
const topKeys = ["defaultMode", "allowUserToggle"];
|
|
268472
|
+
for (const k of topKeys) {
|
|
268473
|
+
if (r[k] !== undefined && sanitized?.[k] === undefined)
|
|
268474
|
+
drops.push(k);
|
|
268475
|
+
}
|
|
268476
|
+
if (r.palette && typeof r.palette === "object" && !Array.isArray(r.palette)) {
|
|
268477
|
+
const rp = r.palette;
|
|
268478
|
+
const sp = sanitized?.palette ?? {};
|
|
268479
|
+
for (const k of Object.keys(rp)) {
|
|
268480
|
+
if (rp[k] !== undefined && sp[k] === undefined)
|
|
268481
|
+
drops.push(`palette.${k}`);
|
|
268482
|
+
}
|
|
268483
|
+
} else if (r.palette !== undefined && (!sanitized || sanitized.palette === undefined)) {
|
|
268484
|
+
drops.push("palette");
|
|
268485
|
+
}
|
|
268486
|
+
if (r.font && typeof r.font === "object" && !Array.isArray(r.font)) {
|
|
268487
|
+
const rf = r.font;
|
|
268488
|
+
const sf = sanitized?.font ?? {};
|
|
268489
|
+
for (const k of Object.keys(rf)) {
|
|
268490
|
+
if (rf[k] !== undefined && sf[k] === undefined)
|
|
268491
|
+
drops.push(`font.${k}`);
|
|
268492
|
+
}
|
|
268493
|
+
} else if (r.font !== undefined && (!sanitized || sanitized.font === undefined)) {
|
|
268494
|
+
drops.push("font");
|
|
268495
|
+
}
|
|
268496
|
+
return drops;
|
|
268497
|
+
}
|
|
268498
|
+
|
|
268175
268499
|
// src/server.ts
|
|
268176
268500
|
function parseArgs() {
|
|
268177
268501
|
const args = process.argv.slice(2);
|
|
@@ -268266,6 +268590,8 @@ var queryController = new QueryController(environmentStore);
|
|
|
268266
268590
|
var compileController = new CompileController(environmentStore);
|
|
268267
268591
|
var materializationService = new MaterializationService(environmentStore);
|
|
268268
268592
|
var materializationController = new MaterializationController(materializationService);
|
|
268593
|
+
var themeStore = new ThemeStore(environmentStore.storageManager, SERVER_ROOT);
|
|
268594
|
+
var themeController = new ThemeController(themeStore, SERVER_ROOT);
|
|
268269
268595
|
var mcpApp = import_express2.default();
|
|
268270
268596
|
registerHealthEndpoints(mcpApp);
|
|
268271
268597
|
mcpApp.use(MCP_ENDPOINT, import_express2.default.json());
|
|
@@ -268539,13 +268865,44 @@ app.get(`${API_PREFIX2}/environments/:environmentName/packages/:packageName/page
|
|
|
268539
268865
|
app.get(`${API_PREFIX2}/status`, async (_req, res) => {
|
|
268540
268866
|
try {
|
|
268541
268867
|
const status = await environmentStore.getStatus();
|
|
268542
|
-
|
|
268868
|
+
const theme = environmentStore.storageManager.isInitialized() ? await themeStore.get() : undefined;
|
|
268869
|
+
res.status(200).json({ ...status, theme: theme ?? {} });
|
|
268543
268870
|
} catch (error) {
|
|
268544
268871
|
logger.error("Error getting status", { error });
|
|
268545
268872
|
const { json, status } = internalErrorToHttpError(error);
|
|
268546
268873
|
res.status(status).json(json);
|
|
268547
268874
|
}
|
|
268548
268875
|
});
|
|
268876
|
+
app.get(`${API_PREFIX2}/theme`, async (_req, res) => {
|
|
268877
|
+
try {
|
|
268878
|
+
if (!environmentStore.storageManager.isInitialized()) {
|
|
268879
|
+
throw new ServiceUnavailableError("Theme storage is still initializing. Retry shortly.");
|
|
268880
|
+
}
|
|
268881
|
+
res.status(200).json(await themeController.getTheme());
|
|
268882
|
+
} catch (error) {
|
|
268883
|
+
logger.error("Error getting theme", { error });
|
|
268884
|
+
const { json, status } = internalErrorToHttpError(error);
|
|
268885
|
+
res.status(status).json(json);
|
|
268886
|
+
}
|
|
268887
|
+
});
|
|
268888
|
+
app.put(`${API_PREFIX2}/theme`, async (req, res) => {
|
|
268889
|
+
try {
|
|
268890
|
+
res.status(200).json(await themeController.putTheme(req.body));
|
|
268891
|
+
} catch (error) {
|
|
268892
|
+
logger.error("Error saving theme", { error });
|
|
268893
|
+
const { json, status } = internalErrorToHttpError(error);
|
|
268894
|
+
res.status(status).json(json);
|
|
268895
|
+
}
|
|
268896
|
+
});
|
|
268897
|
+
app.delete(`${API_PREFIX2}/theme`, async (_req, res) => {
|
|
268898
|
+
try {
|
|
268899
|
+
res.status(200).json(await themeController.resetTheme());
|
|
268900
|
+
} catch (error) {
|
|
268901
|
+
logger.error("Error resetting theme", { error });
|
|
268902
|
+
const { json, status } = internalErrorToHttpError(error);
|
|
268903
|
+
res.status(status).json(json);
|
|
268904
|
+
}
|
|
268905
|
+
});
|
|
268549
268906
|
app.get(`${API_PREFIX2}/watch-mode/status`, watchModeController.getWatchStatus);
|
|
268550
268907
|
app.post(`${API_PREFIX2}/watch-mode/start`, watchModeController.startWatching);
|
|
268551
268908
|
app.post(`${API_PREFIX2}/watch-mode/stop`, watchModeController.stopWatchMode);
|