@youtyan/code-viewer 0.2.3 → 0.2.4
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/code-viewer.js +283 -88
- package/package.json +11 -1
- package/web/app.js +219 -46
- package/web/style.css +3 -0
package/web/app.js
CHANGED
|
@@ -633,12 +633,14 @@
|
|
|
633
633
|
}
|
|
634
634
|
case "/database": {
|
|
635
635
|
const db = params.get("db") || undefined;
|
|
636
|
+
const schema = params.get("schema") || undefined;
|
|
636
637
|
const table = params.get("table") || undefined;
|
|
637
638
|
const tabRaw = params.get("tab");
|
|
638
639
|
const tab = tabRaw === "data" || tabRaw === "query" || tabRaw === "schema" || tabRaw === "er" || tabRaw === "search" || tabRaw === "snapshot" ? tabRaw : undefined;
|
|
639
640
|
return {
|
|
640
641
|
screen: "database",
|
|
641
642
|
...db ? { db } : {},
|
|
643
|
+
...schema ? { schema } : {},
|
|
642
644
|
...table ? { table } : {},
|
|
643
645
|
...tab ? { tab } : {},
|
|
644
646
|
range
|
|
@@ -694,6 +696,8 @@
|
|
|
694
696
|
const params = new URLSearchParams;
|
|
695
697
|
if (route.db)
|
|
696
698
|
params.set("db", route.db);
|
|
699
|
+
if (route.schema)
|
|
700
|
+
params.set("schema", route.schema);
|
|
697
701
|
if (route.table)
|
|
698
702
|
params.set("table", route.table);
|
|
699
703
|
if (route.tab)
|
|
@@ -7303,6 +7307,8 @@ ${frontmatter.yaml}
|
|
|
7303
7307
|
const parts = ["Database"];
|
|
7304
7308
|
if (entry.target.db)
|
|
7305
7309
|
parts.push(entry.target.db);
|
|
7310
|
+
if (entry.target.schema)
|
|
7311
|
+
parts.push(entry.target.schema);
|
|
7306
7312
|
if (entry.target.table)
|
|
7307
7313
|
parts.push(entry.target.table);
|
|
7308
7314
|
if (entry.target.tab)
|
|
@@ -7326,6 +7332,8 @@ ${frontmatter.yaml}
|
|
|
7326
7332
|
const target = entry.target;
|
|
7327
7333
|
if (target.db && target.db !== route.db)
|
|
7328
7334
|
return false;
|
|
7335
|
+
if (target.schema && target.schema !== route.schema)
|
|
7336
|
+
return false;
|
|
7329
7337
|
if (target.table && target.table !== route.table)
|
|
7330
7338
|
return false;
|
|
7331
7339
|
if (target.tab && target.tab !== (route.tab || "data"))
|
|
@@ -7647,7 +7655,9 @@ ${frontmatter.yaml}
|
|
|
7647
7655
|
return firstLine.length > 90 ? `${firstLine.slice(0, 90)}…` : firstLine;
|
|
7648
7656
|
}
|
|
7649
7657
|
function databaseAnnotationTitle(target) {
|
|
7650
|
-
const parts = [target.table || target.db || "Database"];
|
|
7658
|
+
const parts = [target.table || target.schema || target.db || "Database"];
|
|
7659
|
+
if (target.schema && target.table)
|
|
7660
|
+
parts.unshift(target.schema);
|
|
7651
7661
|
if (target.tab === "data" && target.data?.search)
|
|
7652
7662
|
parts.push(`search: ${target.data.search}`);
|
|
7653
7663
|
else if (target.tab === "query" && target.query?.sql)
|
|
@@ -7954,6 +7964,7 @@ ${frontmatter.yaml}
|
|
|
7954
7964
|
deps.setRoute({
|
|
7955
7965
|
screen: "database",
|
|
7956
7966
|
db: target.db,
|
|
7967
|
+
schema: target.schema,
|
|
7957
7968
|
table: target.table,
|
|
7958
7969
|
tab: target.tab,
|
|
7959
7970
|
range: deps.currentRange()
|
|
@@ -9396,6 +9407,7 @@ ${frontmatter.yaml}
|
|
|
9396
9407
|
if (disposed)
|
|
9397
9408
|
return;
|
|
9398
9409
|
const dbId = deps.getDbId();
|
|
9410
|
+
const schema = deps.getSchema();
|
|
9399
9411
|
if (!dbId)
|
|
9400
9412
|
return;
|
|
9401
9413
|
const term = input.value.trim();
|
|
@@ -9416,6 +9428,7 @@ ${frontmatter.yaml}
|
|
|
9416
9428
|
},
|
|
9417
9429
|
body: JSON.stringify({
|
|
9418
9430
|
db: dbId,
|
|
9431
|
+
...schema ? { schema } : {},
|
|
9419
9432
|
term,
|
|
9420
9433
|
includeNonText: nonTextCheck.checked
|
|
9421
9434
|
})
|
|
@@ -9494,16 +9507,17 @@ ${frontmatter.yaml}
|
|
|
9494
9507
|
return;
|
|
9495
9508
|
const grouped = new Map;
|
|
9496
9509
|
for (const h of hits) {
|
|
9497
|
-
const
|
|
9510
|
+
const key = h.schema ? `${h.schema}.${h.table}` : h.table;
|
|
9511
|
+
const existing = grouped.get(key) || [];
|
|
9498
9512
|
existing.push(h);
|
|
9499
|
-
grouped.set(
|
|
9513
|
+
grouped.set(key, existing);
|
|
9500
9514
|
}
|
|
9501
|
-
for (const [
|
|
9515
|
+
for (const [tableLabel, tableHits] of grouped) {
|
|
9502
9516
|
const section = document.createElement("div");
|
|
9503
9517
|
section.className = "db-search-table-section";
|
|
9504
9518
|
const tableHeader = document.createElement("div");
|
|
9505
9519
|
tableHeader.className = "db-search-table-header";
|
|
9506
|
-
tableHeader.textContent = `${
|
|
9520
|
+
tableHeader.textContent = `${tableLabel} (${tableHits.length}件)`;
|
|
9507
9521
|
section.appendChild(tableHeader);
|
|
9508
9522
|
const hitsList = document.createElement("div");
|
|
9509
9523
|
hitsList.className = "db-search-hits-list";
|
|
@@ -9968,7 +9982,13 @@ ${frontmatter.yaml}
|
|
|
9968
9982
|
}
|
|
9969
9983
|
async function refresh() {
|
|
9970
9984
|
const dbId = callbacks.getDbId();
|
|
9971
|
-
const
|
|
9985
|
+
const schema = callbacks.getSchema();
|
|
9986
|
+
const searchParams = new URLSearchParams;
|
|
9987
|
+
if (dbId)
|
|
9988
|
+
searchParams.set("db", dbId);
|
|
9989
|
+
if (schema)
|
|
9990
|
+
searchParams.set("schema", schema);
|
|
9991
|
+
const params = searchParams.toString() ? `?${searchParams.toString()}` : "";
|
|
9972
9992
|
try {
|
|
9973
9993
|
const res = await fetch(`/_db/history${params}`);
|
|
9974
9994
|
if (!res.ok)
|
|
@@ -10181,13 +10201,14 @@ ${frontmatter.yaml}
|
|
|
10181
10201
|
clearConfirmTimer = null;
|
|
10182
10202
|
}
|
|
10183
10203
|
try {
|
|
10204
|
+
const schema = callbacks.getSchema();
|
|
10184
10205
|
await fetch("/_db/history/clear", {
|
|
10185
10206
|
method: "POST",
|
|
10186
10207
|
headers: {
|
|
10187
10208
|
"Content-Type": "application/json",
|
|
10188
10209
|
"X-Code-Viewer-Action": "1"
|
|
10189
10210
|
},
|
|
10190
|
-
body: JSON.stringify(dbId ? { db: dbId } : {})
|
|
10211
|
+
body: JSON.stringify(dbId ? { db: dbId, ...schema ? { schema } : {} } : {})
|
|
10191
10212
|
});
|
|
10192
10213
|
entries = [];
|
|
10193
10214
|
render();
|
|
@@ -11017,6 +11038,7 @@ ${frontmatter.yaml}
|
|
|
11017
11038
|
});
|
|
11018
11039
|
confirmBtn.addEventListener("click", async () => {
|
|
11019
11040
|
const dbId = deps.getDbId();
|
|
11041
|
+
const schema = deps.getSchema();
|
|
11020
11042
|
if (!dbId)
|
|
11021
11043
|
return;
|
|
11022
11044
|
const tables = getSelectedTables();
|
|
@@ -11027,11 +11049,12 @@ ${frontmatter.yaml}
|
|
|
11027
11049
|
try {
|
|
11028
11050
|
await postJson("/_db/snapshot/create", {
|
|
11029
11051
|
db: dbId,
|
|
11052
|
+
...schema ? { schema } : {},
|
|
11030
11053
|
tables,
|
|
11031
11054
|
note: noteInput.value.trim()
|
|
11032
11055
|
});
|
|
11033
11056
|
tableSelector.hidden = true;
|
|
11034
|
-
scheduleAutoRefresh(dbId);
|
|
11057
|
+
scheduleAutoRefresh(dbId, schema);
|
|
11035
11058
|
} catch {} finally {
|
|
11036
11059
|
if (!disposed) {
|
|
11037
11060
|
confirmBtn.disabled = false;
|
|
@@ -11039,11 +11062,12 @@ ${frontmatter.yaml}
|
|
|
11039
11062
|
}
|
|
11040
11063
|
}
|
|
11041
11064
|
});
|
|
11042
|
-
function scheduleAutoRefresh(dbId) {
|
|
11065
|
+
function scheduleAutoRefresh(dbId, schema) {
|
|
11043
11066
|
const timer = setTimeout(() => {
|
|
11044
11067
|
autoRefreshTimers.delete(timer);
|
|
11045
|
-
if (disposed || deps.getDbId() !== dbId)
|
|
11068
|
+
if (disposed || deps.getDbId() !== dbId || deps.getSchema() !== schema) {
|
|
11046
11069
|
return;
|
|
11070
|
+
}
|
|
11047
11071
|
refreshAndAutoDiff();
|
|
11048
11072
|
}, 3000);
|
|
11049
11073
|
autoRefreshTimers.add(timer);
|
|
@@ -11051,17 +11075,22 @@ ${frontmatter.yaml}
|
|
|
11051
11075
|
refreshBtn.addEventListener("click", refresh);
|
|
11052
11076
|
async function refresh() {
|
|
11053
11077
|
const dbId = deps.getDbId();
|
|
11078
|
+
const schema = deps.getSchema();
|
|
11054
11079
|
if (!dbId)
|
|
11055
11080
|
return;
|
|
11056
11081
|
try {
|
|
11057
|
-
const
|
|
11082
|
+
const params = new URLSearchParams({ db: dbId });
|
|
11083
|
+
if (schema)
|
|
11084
|
+
params.set("schema", schema);
|
|
11085
|
+
const snapRes = await fetch(`/_db/snapshot/list?${params}`);
|
|
11058
11086
|
if (snapRes.ok) {
|
|
11059
11087
|
const data = await snapRes.json();
|
|
11060
|
-
if (disposed || deps.getDbId() !== dbId)
|
|
11088
|
+
if (disposed || deps.getDbId() !== dbId || deps.getSchema() !== schema) {
|
|
11061
11089
|
return;
|
|
11090
|
+
}
|
|
11062
11091
|
snapshots = data.snapshots;
|
|
11063
11092
|
}
|
|
11064
|
-
if (disposed || deps.getDbId() !== dbId)
|
|
11093
|
+
if (disposed || deps.getDbId() !== dbId || deps.getSchema() !== schema)
|
|
11065
11094
|
return;
|
|
11066
11095
|
renderMain();
|
|
11067
11096
|
} catch {}
|
|
@@ -11436,8 +11465,11 @@ ${frontmatter.yaml}
|
|
|
11436
11465
|
try {
|
|
11437
11466
|
const parsed = JSON.parse(data);
|
|
11438
11467
|
const dbId = deps.getDbId();
|
|
11468
|
+
const schema = deps.getSchema();
|
|
11439
11469
|
if (parsed.dbId && dbId && parsed.dbId !== dbId)
|
|
11440
11470
|
return;
|
|
11471
|
+
if (parsed.schema && parsed.schema !== schema)
|
|
11472
|
+
return;
|
|
11441
11473
|
if (parsed.action === "started" && parsed.id) {
|
|
11442
11474
|
setActiveSnapshotId(parsed.id);
|
|
11443
11475
|
}
|
|
@@ -12400,6 +12432,9 @@ ${frontmatter.yaml}
|
|
|
12400
12432
|
function isSqlKind(kind) {
|
|
12401
12433
|
return kind === "sqlite" || kind === "postgresql" || kind === "mysql";
|
|
12402
12434
|
}
|
|
12435
|
+
function isPostgresKind(kind) {
|
|
12436
|
+
return kind === "postgresql";
|
|
12437
|
+
}
|
|
12403
12438
|
function isSqlView(view) {
|
|
12404
12439
|
return view === "data" || view === "query" || view === "schema" || view === "er" || view === "search" || view === "snapshot";
|
|
12405
12440
|
}
|
|
@@ -12453,14 +12488,19 @@ ${frontmatter.yaml}
|
|
|
12453
12488
|
let currentDbInfo = null;
|
|
12454
12489
|
let schemaCache = null;
|
|
12455
12490
|
let lastFiles = [];
|
|
12491
|
+
let currentSchema = initial.schema ?? null;
|
|
12456
12492
|
let currentTable = initial.table ?? null;
|
|
12457
12493
|
let loadGeneration = 0;
|
|
12458
12494
|
const dbSelect = document.createElement("select");
|
|
12459
12495
|
dbSelect.className = "db-file-select";
|
|
12460
12496
|
dbSelect.title = "Select database file";
|
|
12497
|
+
const schemaSelect = document.createElement("select");
|
|
12498
|
+
schemaSelect.className = "db-file-select db-schema-select";
|
|
12499
|
+
schemaSelect.title = "Select PostgreSQL schema";
|
|
12500
|
+
schemaSelect.hidden = true;
|
|
12461
12501
|
const dbToolbar = document.createElement("div");
|
|
12462
12502
|
dbToolbar.className = "db-toolbar";
|
|
12463
|
-
dbToolbar.
|
|
12503
|
+
dbToolbar.append(dbSelect, schemaSelect);
|
|
12464
12504
|
const tabBar = document.createElement("div");
|
|
12465
12505
|
tabBar.className = "db-tab-bar";
|
|
12466
12506
|
const tabData = createInnerTab("Data", true);
|
|
@@ -12551,14 +12591,17 @@ ${frontmatter.yaml}
|
|
|
12551
12591
|
const schemaView = createSchemaView();
|
|
12552
12592
|
const erDiagram = createErDiagram();
|
|
12553
12593
|
const globalSearchView = createGlobalSearchView({
|
|
12554
|
-
getDbId: () => currentDbInfo?.id || null
|
|
12594
|
+
getDbId: () => currentDbInfo?.id || null,
|
|
12595
|
+
getSchema: () => currentSchema
|
|
12555
12596
|
});
|
|
12556
12597
|
const snapshotView = createSnapshotView({
|
|
12557
12598
|
getDbId: () => currentDbInfo?.id || null,
|
|
12599
|
+
getSchema: () => currentSchema,
|
|
12558
12600
|
getTables: () => schemaCache?.tables || []
|
|
12559
12601
|
});
|
|
12560
12602
|
const historyView = createQueryHistoryView({
|
|
12561
12603
|
getDbId: () => currentDbInfo?.id || null,
|
|
12604
|
+
getSchema: () => currentSchema,
|
|
12562
12605
|
copySqlToQuery: (sql) => {
|
|
12563
12606
|
queryEditor.setSql(sql);
|
|
12564
12607
|
setActiveTab("query");
|
|
@@ -12700,6 +12743,7 @@ ${frontmatter.yaml}
|
|
|
12700
12743
|
deps.setRoute({
|
|
12701
12744
|
screen: "database",
|
|
12702
12745
|
db: currentDbInfo?.id,
|
|
12746
|
+
schema: currentSchema ?? undefined,
|
|
12703
12747
|
table: currentTable ?? undefined,
|
|
12704
12748
|
tab: currentTab === "data" ? undefined : currentTab,
|
|
12705
12749
|
range: deps.currentRange()
|
|
@@ -12720,8 +12764,40 @@ ${frontmatter.yaml}
|
|
|
12720
12764
|
const data = await res.json();
|
|
12721
12765
|
return data;
|
|
12722
12766
|
}
|
|
12767
|
+
function withCurrentSchema(params) {
|
|
12768
|
+
if (currentSchema)
|
|
12769
|
+
params.set("schema", currentSchema);
|
|
12770
|
+
return params;
|
|
12771
|
+
}
|
|
12772
|
+
async function fetchSchemas(dbId, preferredSchema) {
|
|
12773
|
+
const params = new URLSearchParams({ db: dbId });
|
|
12774
|
+
if (preferredSchema)
|
|
12775
|
+
params.set("schema", preferredSchema);
|
|
12776
|
+
const res = await deps.trackLoad(fetch(`/_db/schemas?${params}`));
|
|
12777
|
+
if (!res.ok) {
|
|
12778
|
+
throw new Error(await responseErrorMessage(res, "failed to fetch schemas"));
|
|
12779
|
+
}
|
|
12780
|
+
return await res.json();
|
|
12781
|
+
}
|
|
12782
|
+
function renderSchemaOptions(schemas, selected) {
|
|
12783
|
+
schemaSelect.innerHTML = "";
|
|
12784
|
+
for (const schema of schemas) {
|
|
12785
|
+
const opt = document.createElement("option");
|
|
12786
|
+
opt.value = schema;
|
|
12787
|
+
opt.textContent = schema;
|
|
12788
|
+
schemaSelect.appendChild(opt);
|
|
12789
|
+
}
|
|
12790
|
+
schemaSelect.hidden = schemas.length === 0;
|
|
12791
|
+
schemaSelect.disabled = schemas.length === 0;
|
|
12792
|
+
if (selected && schemas.includes(selected)) {
|
|
12793
|
+
schemaSelect.value = selected;
|
|
12794
|
+
} else if (schemas.length > 0) {
|
|
12795
|
+
schemaSelect.value = schemas[0];
|
|
12796
|
+
}
|
|
12797
|
+
}
|
|
12723
12798
|
async function fetchSchema(dbId) {
|
|
12724
|
-
const
|
|
12799
|
+
const params = withCurrentSchema(new URLSearchParams({ db: dbId, includeColumns: "1" }));
|
|
12800
|
+
const res = await deps.trackLoad(fetch(`/_db/schema?${params}`));
|
|
12725
12801
|
if (!res.ok) {
|
|
12726
12802
|
throw new Error(await responseErrorMessage(res, "failed to fetch schema"));
|
|
12727
12803
|
}
|
|
@@ -12736,6 +12812,7 @@ ${frontmatter.yaml}
|
|
|
12736
12812
|
offset: String(offset),
|
|
12737
12813
|
limit: String(limit)
|
|
12738
12814
|
});
|
|
12815
|
+
withCurrentSchema(params);
|
|
12739
12816
|
if (sort) {
|
|
12740
12817
|
params.set("sort", sort.column);
|
|
12741
12818
|
params.set("dir", sort.direction);
|
|
@@ -12760,6 +12837,7 @@ ${frontmatter.yaml}
|
|
|
12760
12837
|
},
|
|
12761
12838
|
body: JSON.stringify({
|
|
12762
12839
|
db: currentDbInfo.id,
|
|
12840
|
+
...currentSchema ? { schema: currentSchema } : {},
|
|
12763
12841
|
sql,
|
|
12764
12842
|
saveHistory: true,
|
|
12765
12843
|
source: "browser",
|
|
@@ -12774,11 +12852,13 @@ ${frontmatter.yaml}
|
|
|
12774
12852
|
historyView.refresh();
|
|
12775
12853
|
return result;
|
|
12776
12854
|
}
|
|
12777
|
-
async function selectDb(dbId, explorerInitial, generation = loadGeneration, preferredTable) {
|
|
12855
|
+
async function selectDb(dbId, explorerInitial, generation = loadGeneration, preferredSchema, preferredTable) {
|
|
12778
12856
|
if (generation !== loadGeneration || currentDbInfo?.id !== dbId)
|
|
12779
12857
|
return;
|
|
12780
12858
|
currentTable = null;
|
|
12781
12859
|
if (currentDbInfo?.kind === "redis" || currentDbInfo?.kind === "elasticsearch") {
|
|
12860
|
+
currentSchema = null;
|
|
12861
|
+
renderSchemaOptions([], null);
|
|
12782
12862
|
currentTab = "data";
|
|
12783
12863
|
tableList.render([]);
|
|
12784
12864
|
grid.clear();
|
|
@@ -12804,6 +12884,34 @@ ${frontmatter.yaml}
|
|
|
12804
12884
|
tableList.render([]);
|
|
12805
12885
|
setTableListStatus("Loading schema...");
|
|
12806
12886
|
grid.clear();
|
|
12887
|
+
if (isPostgresKind(currentDbInfo?.kind)) {
|
|
12888
|
+
const desiredSchema = preferredSchema !== undefined ? preferredSchema : currentSchema;
|
|
12889
|
+
const schemas = await fetchSchemas(dbId, desiredSchema).catch((err) => {
|
|
12890
|
+
if (generation !== loadGeneration || currentDbInfo?.id !== dbId)
|
|
12891
|
+
return;
|
|
12892
|
+
const message = errorMessage(err);
|
|
12893
|
+
currentSchema = null;
|
|
12894
|
+
renderSchemaOptions([], null);
|
|
12895
|
+
schemaCache = null;
|
|
12896
|
+
tableList.render([]);
|
|
12897
|
+
setTableListStatus(message, { error: true });
|
|
12898
|
+
grid.showError(message);
|
|
12899
|
+
schemaView.clear();
|
|
12900
|
+
erDiagram.clear();
|
|
12901
|
+
cb.onStateChange();
|
|
12902
|
+
return null;
|
|
12903
|
+
});
|
|
12904
|
+
if (generation !== loadGeneration || currentDbInfo?.id !== dbId)
|
|
12905
|
+
return;
|
|
12906
|
+
if (!schemas)
|
|
12907
|
+
return;
|
|
12908
|
+
const schemaNames = schemas.schemas.map((s2) => s2.name);
|
|
12909
|
+
currentSchema = schemas.selectedSchema || desiredSchema || (schemaNames.includes("public") ? "public" : schemaNames[0]) || null;
|
|
12910
|
+
renderSchemaOptions(schemaNames, currentSchema);
|
|
12911
|
+
} else {
|
|
12912
|
+
currentSchema = null;
|
|
12913
|
+
renderSchemaOptions([], null);
|
|
12914
|
+
}
|
|
12807
12915
|
const schema = await fetchSchema(dbId).catch((err) => {
|
|
12808
12916
|
if (generation !== loadGeneration || currentDbInfo?.id !== dbId)
|
|
12809
12917
|
return;
|
|
@@ -12822,6 +12930,7 @@ ${frontmatter.yaml}
|
|
|
12822
12930
|
return;
|
|
12823
12931
|
if (!schema)
|
|
12824
12932
|
return;
|
|
12933
|
+
currentSchema = schema.schema || currentSchema;
|
|
12825
12934
|
schemaCache = schema;
|
|
12826
12935
|
tableList.render(schema.tables);
|
|
12827
12936
|
grid.clear();
|
|
@@ -12849,6 +12958,7 @@ ${frontmatter.yaml}
|
|
|
12849
12958
|
deps.setRoute({
|
|
12850
12959
|
screen: "database",
|
|
12851
12960
|
db: currentDbInfo.id,
|
|
12961
|
+
schema: currentSchema ?? undefined,
|
|
12852
12962
|
table: table2,
|
|
12853
12963
|
tab: currentTab === "data" ? undefined : currentTab,
|
|
12854
12964
|
range: deps.currentRange()
|
|
@@ -12879,7 +12989,7 @@ ${frontmatter.yaml}
|
|
|
12879
12989
|
}
|
|
12880
12990
|
if (!currentDbInfo)
|
|
12881
12991
|
return [];
|
|
12882
|
-
const res = await fetch(`/_db/columns
|
|
12992
|
+
const res = await fetch(`/_db/columns?${withCurrentSchema(new URLSearchParams({ db: currentDbInfo.id, table: table2 }))}`);
|
|
12883
12993
|
if (!res.ok)
|
|
12884
12994
|
return [];
|
|
12885
12995
|
const data = await res.json();
|
|
@@ -12897,7 +13007,7 @@ ${frontmatter.yaml}
|
|
|
12897
13007
|
return;
|
|
12898
13008
|
setActiveTab("schema");
|
|
12899
13009
|
try {
|
|
12900
|
-
const res = await fetch(`/_db/ddl
|
|
13010
|
+
const res = await fetch(`/_db/ddl?${withCurrentSchema(new URLSearchParams({ db: currentDbInfo.id, table: table2 }))}`);
|
|
12901
13011
|
if (!res.ok)
|
|
12902
13012
|
return;
|
|
12903
13013
|
const data = await res.json();
|
|
@@ -12930,6 +13040,33 @@ ${frontmatter.yaml}
|
|
|
12930
13040
|
dbSelect.addEventListener("change", () => {
|
|
12931
13041
|
handleDbSelectChange();
|
|
12932
13042
|
});
|
|
13043
|
+
schemaSelect.addEventListener("change", () => {
|
|
13044
|
+
handleSchemaSelectChange();
|
|
13045
|
+
});
|
|
13046
|
+
async function handleSchemaSelectChange() {
|
|
13047
|
+
if (!currentDbInfo || !isPostgresKind(currentDbInfo.kind))
|
|
13048
|
+
return;
|
|
13049
|
+
const schema = schemaSelect.value || null;
|
|
13050
|
+
if (!schema || schema === currentSchema)
|
|
13051
|
+
return;
|
|
13052
|
+
const dbId = currentDbInfo.id;
|
|
13053
|
+
currentSchema = schema;
|
|
13054
|
+
const generation = ++loadGeneration;
|
|
13055
|
+
clearDockerNotice();
|
|
13056
|
+
await selectDb(dbId, undefined, generation, schema, null);
|
|
13057
|
+
if (generation !== loadGeneration || currentDbInfo?.id !== dbId || currentSchema !== schema) {
|
|
13058
|
+
return;
|
|
13059
|
+
}
|
|
13060
|
+
deps.setRoute({
|
|
13061
|
+
screen: "database",
|
|
13062
|
+
db: dbId,
|
|
13063
|
+
schema,
|
|
13064
|
+
table: currentTable ?? undefined,
|
|
13065
|
+
tab: currentTab === "data" ? undefined : currentTab,
|
|
13066
|
+
range: deps.currentRange()
|
|
13067
|
+
}, true);
|
|
13068
|
+
cb.onStateChange();
|
|
13069
|
+
}
|
|
12933
13070
|
async function handleDbSelectChange() {
|
|
12934
13071
|
const dbId = dbSelect.value;
|
|
12935
13072
|
if (!dbId)
|
|
@@ -12944,13 +13081,15 @@ ${frontmatter.yaml}
|
|
|
12944
13081
|
sizeBytes: file?.sizeBytes || 0,
|
|
12945
13082
|
kind: file?.kind || "sqlite"
|
|
12946
13083
|
};
|
|
13084
|
+
currentSchema = null;
|
|
12947
13085
|
clearDockerNotice();
|
|
12948
|
-
await selectDb(dbId, undefined, generation);
|
|
13086
|
+
await selectDb(dbId, undefined, generation, null);
|
|
12949
13087
|
if (generation !== loadGeneration || currentDbInfo?.id !== dbId)
|
|
12950
13088
|
return;
|
|
12951
13089
|
deps.setRoute({
|
|
12952
13090
|
screen: "database",
|
|
12953
13091
|
db: dbId,
|
|
13092
|
+
schema: currentSchema ?? undefined,
|
|
12954
13093
|
table: currentTable ?? undefined,
|
|
12955
13094
|
tab: currentTab === "data" ? undefined : currentTab,
|
|
12956
13095
|
range: deps.currentRange()
|
|
@@ -12959,7 +13098,7 @@ ${frontmatter.yaml}
|
|
|
12959
13098
|
}
|
|
12960
13099
|
let pendingRedisInitial = initial.redis;
|
|
12961
13100
|
let pendingEsInitial = initial.es;
|
|
12962
|
-
async function enter(db, table2, view, options = {}) {
|
|
13101
|
+
async function enter(db, schema, table2, view, options = {}) {
|
|
12963
13102
|
const generation = ++loadGeneration;
|
|
12964
13103
|
const filesResponse = await fetchDbFiles();
|
|
12965
13104
|
if (generation !== loadGeneration)
|
|
@@ -12997,8 +13136,10 @@ ${frontmatter.yaml}
|
|
|
12997
13136
|
if (!db && !autoSelectFirst) {
|
|
12998
13137
|
dbSelect.value = "";
|
|
12999
13138
|
currentDbInfo = null;
|
|
13139
|
+
currentSchema = null;
|
|
13000
13140
|
currentTable = null;
|
|
13001
13141
|
schemaCache = null;
|
|
13142
|
+
renderSchemaOptions([], null);
|
|
13002
13143
|
tableList.render([]);
|
|
13003
13144
|
grid.clear();
|
|
13004
13145
|
schemaView.clear();
|
|
@@ -13018,7 +13159,7 @@ ${frontmatter.yaml}
|
|
|
13018
13159
|
};
|
|
13019
13160
|
pendingRedisInitial = undefined;
|
|
13020
13161
|
pendingEsInitial = undefined;
|
|
13021
|
-
await selectDb(target, explorerInitial, generation, table2);
|
|
13162
|
+
await selectDb(target, explorerInitial, generation, schema !== undefined ? schema : currentSchema, table2);
|
|
13022
13163
|
if (generation !== loadGeneration || currentDbInfo?.id !== target)
|
|
13023
13164
|
return;
|
|
13024
13165
|
if (currentDbInfo?.kind === "redis" || currentDbInfo?.kind === "elasticsearch") {
|
|
@@ -13051,6 +13192,8 @@ ${frontmatter.yaml}
|
|
|
13051
13192
|
function applyAnnotationTarget(target) {
|
|
13052
13193
|
if (!target || target.db !== currentDbInfo?.id)
|
|
13053
13194
|
return;
|
|
13195
|
+
if (target.schema && target.schema !== currentSchema)
|
|
13196
|
+
return;
|
|
13054
13197
|
if (target.data) {
|
|
13055
13198
|
if (target.table && target.table !== currentTable)
|
|
13056
13199
|
return;
|
|
@@ -13087,6 +13230,7 @@ ${frontmatter.yaml}
|
|
|
13087
13230
|
const state = {
|
|
13088
13231
|
id: cb.tabId,
|
|
13089
13232
|
dbId: currentDbInfo?.id ?? null,
|
|
13233
|
+
schema: currentSchema,
|
|
13090
13234
|
table: currentTable ?? activeTable?.dataset.table ?? null,
|
|
13091
13235
|
view: currentTab
|
|
13092
13236
|
};
|
|
@@ -13118,6 +13262,7 @@ ${frontmatter.yaml}
|
|
|
13118
13262
|
const target = {
|
|
13119
13263
|
kind: "database",
|
|
13120
13264
|
db: currentDbInfo.id,
|
|
13265
|
+
...currentSchema ? { schema: currentSchema } : {},
|
|
13121
13266
|
...currentTable ? { table: currentTable } : {},
|
|
13122
13267
|
tab: currentTab
|
|
13123
13268
|
};
|
|
@@ -13134,12 +13279,14 @@ ${frontmatter.yaml}
|
|
|
13134
13279
|
function getLabel() {
|
|
13135
13280
|
if (!currentDbInfo)
|
|
13136
13281
|
return labelFromDbId(initial.dbId);
|
|
13282
|
+
const suffix = currentSchema ? ` / ${currentSchema}` : "";
|
|
13137
13283
|
if (currentDbInfo.id.startsWith("docker:")) {
|
|
13138
13284
|
const m = currentDbInfo.name.match(/^(\S+)/);
|
|
13139
|
-
return m ? m[1] : currentDbInfo.name
|
|
13285
|
+
return `${m ? m[1] : currentDbInfo.name}${suffix}`;
|
|
13140
13286
|
}
|
|
13141
13287
|
const lastSlash = currentDbInfo.path.lastIndexOf("/");
|
|
13142
|
-
|
|
13288
|
+
const label = lastSlash >= 0 ? currentDbInfo.path.slice(lastSlash + 1) : currentDbInfo.path;
|
|
13289
|
+
return `${label}${suffix}`;
|
|
13143
13290
|
}
|
|
13144
13291
|
function dispose() {
|
|
13145
13292
|
loadGeneration++;
|
|
@@ -13154,6 +13301,7 @@ ${frontmatter.yaml}
|
|
|
13154
13301
|
redisExplorer.dispose();
|
|
13155
13302
|
esExplorer.dispose();
|
|
13156
13303
|
currentDbInfo = null;
|
|
13304
|
+
currentSchema = null;
|
|
13157
13305
|
currentTable = null;
|
|
13158
13306
|
schemaCache = null;
|
|
13159
13307
|
}
|
|
@@ -13299,6 +13447,7 @@ ${frontmatter.yaml}
|
|
|
13299
13447
|
deps.setRoute({
|
|
13300
13448
|
screen: "database",
|
|
13301
13449
|
db: s2.dbId ?? undefined,
|
|
13450
|
+
schema: s2.schema ?? undefined,
|
|
13302
13451
|
table: s2.table ?? undefined,
|
|
13303
13452
|
tab: s2.view === "data" ? undefined : s2.view,
|
|
13304
13453
|
range: deps.currentRange()
|
|
@@ -13319,6 +13468,7 @@ ${frontmatter.yaml}
|
|
|
13319
13468
|
for (const tab of tabs) {
|
|
13320
13469
|
const key = JSON.stringify({
|
|
13321
13470
|
dbId: tab.dbId,
|
|
13471
|
+
schema: tab.schema ?? null,
|
|
13322
13472
|
table: tab.table,
|
|
13323
13473
|
view: tab.view,
|
|
13324
13474
|
redis: tab.redis ?? null,
|
|
@@ -13549,6 +13699,7 @@ ${frontmatter.yaml}
|
|
|
13549
13699
|
}
|
|
13550
13700
|
}, {
|
|
13551
13701
|
dbId: initial?.dbId ?? null,
|
|
13702
|
+
schema: initial?.schema ?? null,
|
|
13552
13703
|
table: initial?.table ?? null,
|
|
13553
13704
|
view: initial?.view ?? undefined,
|
|
13554
13705
|
sqlDraft: initial?.sqlDraft,
|
|
@@ -13577,7 +13728,7 @@ ${frontmatter.yaml}
|
|
|
13577
13728
|
if (options.annotationTarget)
|
|
13578
13729
|
restoring = true;
|
|
13579
13730
|
try {
|
|
13580
|
-
await pane.enter(initial?.dbId || undefined, initial?.table || undefined, initial?.view || undefined, options);
|
|
13731
|
+
await pane.enter(initial?.dbId || undefined, initial?.schema || undefined, initial?.table || undefined, initial?.view || undefined, options);
|
|
13581
13732
|
refreshChipLabel(id);
|
|
13582
13733
|
} finally {
|
|
13583
13734
|
if (options.annotationTarget)
|
|
@@ -13614,35 +13765,41 @@ ${frontmatter.yaml}
|
|
|
13614
13765
|
newTabBtn.addEventListener("click", () => {
|
|
13615
13766
|
openTab({ dbId: null, table: null, view: "data" }, { autoSelectFirst: false });
|
|
13616
13767
|
});
|
|
13617
|
-
function routeMatchesState(state, db, table2, view) {
|
|
13618
|
-
if (!db && (table2 || view) && !state.dbId)
|
|
13768
|
+
function routeMatchesState(state, db, schema, table2, view) {
|
|
13769
|
+
if (!db && (schema || table2 || view) && !state.dbId)
|
|
13619
13770
|
return false;
|
|
13620
13771
|
if (db !== undefined && state.dbId !== db)
|
|
13621
13772
|
return false;
|
|
13773
|
+
if (schema !== undefined) {
|
|
13774
|
+
const routeSchema = schema || "public";
|
|
13775
|
+
const stateSchema = state.schema || "public";
|
|
13776
|
+
if (stateSchema !== routeSchema)
|
|
13777
|
+
return false;
|
|
13778
|
+
}
|
|
13622
13779
|
if (table2 !== undefined && state.table !== table2)
|
|
13623
13780
|
return false;
|
|
13624
13781
|
if (view !== undefined && state.view !== view)
|
|
13625
13782
|
return false;
|
|
13626
13783
|
return true;
|
|
13627
13784
|
}
|
|
13628
|
-
async function applyRouteToTab(db, table2, view, options = {}) {
|
|
13629
|
-
async function enterPane(pane, targetDb, targetTable, targetView, enterOptions) {
|
|
13785
|
+
async function applyRouteToTab(db, schema, table2, view, options = {}) {
|
|
13786
|
+
async function enterPane(pane, targetDb, targetSchema, targetTable, targetView, enterOptions) {
|
|
13630
13787
|
if (!enterOptions?.annotationTarget) {
|
|
13631
|
-
await pane.enter(targetDb, targetTable, targetView, enterOptions);
|
|
13788
|
+
await pane.enter(targetDb, targetSchema, targetTable, targetView, enterOptions);
|
|
13632
13789
|
return;
|
|
13633
13790
|
}
|
|
13634
13791
|
restoring = true;
|
|
13635
13792
|
try {
|
|
13636
|
-
await pane.enter(targetDb, targetTable, targetView, enterOptions);
|
|
13793
|
+
await pane.enter(targetDb, targetSchema, targetTable, targetView, enterOptions);
|
|
13637
13794
|
} finally {
|
|
13638
13795
|
restoring = false;
|
|
13639
13796
|
}
|
|
13640
13797
|
}
|
|
13641
13798
|
for (const [id2, entry] of tabsById) {
|
|
13642
|
-
if (routeMatchesState(entry.pane.getState(), db, table2, view)) {
|
|
13799
|
+
if (routeMatchesState(entry.pane.getState(), db, schema, table2, view)) {
|
|
13643
13800
|
setActive(id2);
|
|
13644
13801
|
if (options.annotationTarget) {
|
|
13645
|
-
await enterPane(entry.pane, db, table2, view, options);
|
|
13802
|
+
await enterPane(entry.pane, db, schema, table2, view, options);
|
|
13646
13803
|
if (!mounted)
|
|
13647
13804
|
return;
|
|
13648
13805
|
}
|
|
@@ -13654,7 +13811,7 @@ ${frontmatter.yaml}
|
|
|
13654
13811
|
const targetId = activeTabId;
|
|
13655
13812
|
const active = tabsById.get(activeTabId);
|
|
13656
13813
|
if (active) {
|
|
13657
|
-
await enterPane(active.pane, db, table2, view, options);
|
|
13814
|
+
await enterPane(active.pane, db, schema, table2, view, options);
|
|
13658
13815
|
if (!mounted)
|
|
13659
13816
|
return;
|
|
13660
13817
|
refreshChipLabel(targetId);
|
|
@@ -13663,6 +13820,7 @@ ${frontmatter.yaml}
|
|
|
13663
13820
|
}
|
|
13664
13821
|
const id = openTab({
|
|
13665
13822
|
dbId: db ?? null,
|
|
13823
|
+
schema: schema ?? null,
|
|
13666
13824
|
table: table2 ?? null,
|
|
13667
13825
|
view: view ?? "data"
|
|
13668
13826
|
}, { autoSelectFirst: db === undefined, ...options });
|
|
@@ -13681,7 +13839,17 @@ ${frontmatter.yaml}
|
|
|
13681
13839
|
return null;
|
|
13682
13840
|
}
|
|
13683
13841
|
}
|
|
13684
|
-
|
|
13842
|
+
function parseSseSchema(data) {
|
|
13843
|
+
if (!data)
|
|
13844
|
+
return null;
|
|
13845
|
+
try {
|
|
13846
|
+
const parsed = JSON.parse(data);
|
|
13847
|
+
return typeof parsed.schema === "string" ? parsed.schema : null;
|
|
13848
|
+
} catch {
|
|
13849
|
+
return null;
|
|
13850
|
+
}
|
|
13851
|
+
}
|
|
13852
|
+
async function doEnter(seq, db, schema, table2, view, options = {}) {
|
|
13685
13853
|
if (seq !== lifecycleSeq)
|
|
13686
13854
|
return;
|
|
13687
13855
|
const firstMount = !mounted;
|
|
@@ -13744,8 +13912,8 @@ ${frontmatter.yaml}
|
|
|
13744
13912
|
} finally {
|
|
13745
13913
|
restoring = false;
|
|
13746
13914
|
}
|
|
13747
|
-
if (db || table2 || view) {
|
|
13748
|
-
await applyRouteToTab(db, table2, view, {
|
|
13915
|
+
if (db || schema || table2 || view) {
|
|
13916
|
+
await applyRouteToTab(db, schema ?? null, table2, view, {
|
|
13749
13917
|
...options,
|
|
13750
13918
|
reuseActiveTab: options.reuseActiveTab ?? true
|
|
13751
13919
|
});
|
|
@@ -13770,6 +13938,7 @@ ${frontmatter.yaml}
|
|
|
13770
13938
|
if (tabsById.size === 0) {
|
|
13771
13939
|
const id = openTab({
|
|
13772
13940
|
dbId: db || null,
|
|
13941
|
+
schema: schema || null,
|
|
13773
13942
|
table: table2 || null,
|
|
13774
13943
|
view: view || "data"
|
|
13775
13944
|
}, { autoSelectFirst: !db, ...options });
|
|
@@ -13785,8 +13954,8 @@ ${frontmatter.yaml}
|
|
|
13785
13954
|
const active = tabsById.get(activeTabId);
|
|
13786
13955
|
if (!active)
|
|
13787
13956
|
return;
|
|
13788
|
-
if (db || table2 || view) {
|
|
13789
|
-
await applyRouteToTab(db, table2, view, {
|
|
13957
|
+
if (db || schema || table2 || view) {
|
|
13958
|
+
await applyRouteToTab(db, schema ?? null, table2, view, {
|
|
13790
13959
|
...options,
|
|
13791
13960
|
reuseActiveTab: options.reuseActiveTab ?? true
|
|
13792
13961
|
});
|
|
@@ -13794,9 +13963,9 @@ ${frontmatter.yaml}
|
|
|
13794
13963
|
syncActiveRoute();
|
|
13795
13964
|
}
|
|
13796
13965
|
}
|
|
13797
|
-
async function enter(db, table2, view, options = {}) {
|
|
13966
|
+
async function enter(db, schema, table2, view, options = {}) {
|
|
13798
13967
|
const seq = lifecycleSeq;
|
|
13799
|
-
enterQueue = enterQueue.catch(() => {}).then(() => doEnter(seq, db, table2, view, options));
|
|
13968
|
+
enterQueue = enterQueue.catch(() => {}).then(() => doEnter(seq, db, schema, table2, view, options));
|
|
13800
13969
|
await enterQueue;
|
|
13801
13970
|
}
|
|
13802
13971
|
function leave() {
|
|
@@ -13846,8 +14015,12 @@ ${frontmatter.yaml}
|
|
|
13846
14015
|
if (!mounted)
|
|
13847
14016
|
return;
|
|
13848
14017
|
const dbId = parseSseDbId(data);
|
|
14018
|
+
const schema = parseSseSchema(data);
|
|
13849
14019
|
for (const [, entry] of tabsById) {
|
|
13850
|
-
|
|
14020
|
+
const state = entry.pane.getState();
|
|
14021
|
+
if (dbId && state.dbId !== dbId)
|
|
14022
|
+
continue;
|
|
14023
|
+
if (schema && state.schema !== schema)
|
|
13851
14024
|
continue;
|
|
13852
14025
|
entry.pane.handleSse(event, data);
|
|
13853
14026
|
}
|
|
@@ -23789,7 +23962,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
23789
23962
|
return Promise.resolve(null);
|
|
23790
23963
|
}
|
|
23791
23964
|
if (STATE.route.screen === "database") {
|
|
23792
|
-
DATABASE_VIEW.enter(STATE.route.db, STATE.route.table, STATE.route.tab).then(() => ANNOTATIONS_UI?.applyInlineAnnotations());
|
|
23965
|
+
DATABASE_VIEW.enter(STATE.route.db, STATE.route.schema, STATE.route.table, STATE.route.tab).then(() => ANNOTATIONS_UI?.applyInlineAnnotations());
|
|
23793
23966
|
setStatus("live");
|
|
23794
23967
|
return Promise.resolve(null);
|
|
23795
23968
|
}
|
|
@@ -23860,7 +24033,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
23860
24033
|
HISTORY_VIEW.enterHistory();
|
|
23861
24034
|
} else if (STATE.route.screen === "database") {
|
|
23862
24035
|
setStatus("live");
|
|
23863
|
-
DATABASE_VIEW.enter(STATE.route.db, STATE.route.table, STATE.route.tab).then(() => ANNOTATIONS_UI?.applyInlineAnnotations());
|
|
24036
|
+
DATABASE_VIEW.enter(STATE.route.db, STATE.route.schema, STATE.route.table, STATE.route.tab).then(() => ANNOTATIONS_UI?.applyInlineAnnotations());
|
|
23864
24037
|
} else
|
|
23865
24038
|
load();
|
|
23866
24039
|
syncLineRefPill();
|
|
@@ -24010,7 +24183,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
24010
24183
|
cancelActiveSourceLoad("navigation");
|
|
24011
24184
|
setPageMode();
|
|
24012
24185
|
removeStandaloneSource();
|
|
24013
|
-
DATABASE_VIEW.enter(STATE.route.db, STATE.route.table, STATE.route.tab).then(() => ANNOTATIONS_UI?.applyInlineAnnotations());
|
|
24186
|
+
DATABASE_VIEW.enter(STATE.route.db, STATE.route.schema, STATE.route.table, STATE.route.tab).then(() => ANNOTATIONS_UI?.applyInlineAnnotations());
|
|
24014
24187
|
setStatus("live");
|
|
24015
24188
|
return;
|
|
24016
24189
|
}
|
|
@@ -24128,7 +24301,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
24128
24301
|
},
|
|
24129
24302
|
openDatabaseAnnotation: (target) => {
|
|
24130
24303
|
setStatus("live");
|
|
24131
|
-
return DATABASE_VIEW.enter(target.db, target.table, target.tab, {
|
|
24304
|
+
return DATABASE_VIEW.enter(target.db, target.schema, target.table, target.tab, {
|
|
24132
24305
|
annotationTarget: target,
|
|
24133
24306
|
reuseActiveTab: true
|
|
24134
24307
|
});
|