@youtyan/code-viewer 0.2.2 → 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 +585 -89
- package/package.json +11 -1
- package/web/app.js +224 -46
- package/web/style.css +3 -0
package/dist/code-viewer.js
CHANGED
|
@@ -2853,6 +2853,306 @@ var init_routes = __esm(() => {
|
|
|
2853
2853
|
APP_ENTRY_PATHS = ["/", "/index.html"];
|
|
2854
2854
|
});
|
|
2855
2855
|
|
|
2856
|
+
// web-src/views/media-embed.ts
|
|
2857
|
+
function isImage(p) {
|
|
2858
|
+
return IMAGE_RE.test(p);
|
|
2859
|
+
}
|
|
2860
|
+
function isVideo(p) {
|
|
2861
|
+
return VIDEO_RE.test(p);
|
|
2862
|
+
}
|
|
2863
|
+
function isAudio(p) {
|
|
2864
|
+
return AUDIO_RE.test(p);
|
|
2865
|
+
}
|
|
2866
|
+
var IMAGE_RE, VIDEO_RE, AUDIO_RE;
|
|
2867
|
+
var init_media_embed = __esm(() => {
|
|
2868
|
+
IMAGE_RE = /\.(png|jpe?g|gif|webp|svg|avif|bmp|ico)(\?.*)?$/i;
|
|
2869
|
+
VIDEO_RE = /\.(mp4|webm|mov)$/i;
|
|
2870
|
+
AUDIO_RE = /\.(mp3|wav|ogg|flac|m4a|aac|opus)$/i;
|
|
2871
|
+
});
|
|
2872
|
+
|
|
2873
|
+
// web-src/core/source-meta.ts
|
|
2874
|
+
function sourceFileName(path) {
|
|
2875
|
+
return (path.split("/").pop() || path).toLowerCase();
|
|
2876
|
+
}
|
|
2877
|
+
function sourceFileExtension(name) {
|
|
2878
|
+
const index = name.lastIndexOf(".");
|
|
2879
|
+
return index >= 0 ? name.slice(index + 1) : "";
|
|
2880
|
+
}
|
|
2881
|
+
function isDockerfileName(name) {
|
|
2882
|
+
return /^dockerfile(?:[.-].+)?$/i.test(name);
|
|
2883
|
+
}
|
|
2884
|
+
function isMakefileName(name) {
|
|
2885
|
+
return /^makefile(?:[.-].+)?$/i.test(name);
|
|
2886
|
+
}
|
|
2887
|
+
function isDotenvName(name) {
|
|
2888
|
+
return /^(?:\.?env|.*\.env)(?:[.-].+)?$/i.test(name);
|
|
2889
|
+
}
|
|
2890
|
+
function sourceDisplayKind(path) {
|
|
2891
|
+
if (isVideo(path))
|
|
2892
|
+
return "video";
|
|
2893
|
+
if (isAudio(path))
|
|
2894
|
+
return "audio";
|
|
2895
|
+
if (isImage(path))
|
|
2896
|
+
return "image";
|
|
2897
|
+
if (/\.pdf$/i.test(path))
|
|
2898
|
+
return "pdf";
|
|
2899
|
+
const name = sourceFileName(path);
|
|
2900
|
+
const ext = sourceFileExtension(name);
|
|
2901
|
+
if (TEXT_SOURCE_EXTENSIONS.has(ext))
|
|
2902
|
+
return "text";
|
|
2903
|
+
if (TEXT_SOURCE_FILENAMES.has(name))
|
|
2904
|
+
return "text";
|
|
2905
|
+
if (isDotenvName(name))
|
|
2906
|
+
return "text";
|
|
2907
|
+
if (isDockerfileName(name) || isMakefileName(name))
|
|
2908
|
+
return "text";
|
|
2909
|
+
return "unsupported";
|
|
2910
|
+
}
|
|
2911
|
+
var EXT_TO_LANG, TEXT_SOURCE_EXTENSIONS, TEXT_SOURCE_FILENAMES;
|
|
2912
|
+
var init_source_meta = __esm(() => {
|
|
2913
|
+
init_media_embed();
|
|
2914
|
+
EXT_TO_LANG = {
|
|
2915
|
+
js: "javascript",
|
|
2916
|
+
mjs: "javascript",
|
|
2917
|
+
cjs: "javascript",
|
|
2918
|
+
ts: "typescript",
|
|
2919
|
+
tsx: "typescript",
|
|
2920
|
+
jsx: "javascript",
|
|
2921
|
+
py: "python",
|
|
2922
|
+
rb: "ruby",
|
|
2923
|
+
go: "go",
|
|
2924
|
+
rs: "rust",
|
|
2925
|
+
java: "java",
|
|
2926
|
+
kt: "kotlin",
|
|
2927
|
+
swift: "swift",
|
|
2928
|
+
c: "c",
|
|
2929
|
+
h: "c",
|
|
2930
|
+
cc: "cpp",
|
|
2931
|
+
cpp: "cpp",
|
|
2932
|
+
hpp: "cpp",
|
|
2933
|
+
cs: "csharp",
|
|
2934
|
+
php: "php",
|
|
2935
|
+
lua: "lua",
|
|
2936
|
+
sh: "bash",
|
|
2937
|
+
bash: "bash",
|
|
2938
|
+
zsh: "bash",
|
|
2939
|
+
fish: "bash",
|
|
2940
|
+
sql: "sql",
|
|
2941
|
+
json: "json",
|
|
2942
|
+
yaml: "yaml",
|
|
2943
|
+
yml: "yaml",
|
|
2944
|
+
toml: "toml",
|
|
2945
|
+
tf: "terraform",
|
|
2946
|
+
tfvars: "terraform",
|
|
2947
|
+
hcl: "terraform",
|
|
2948
|
+
xml: "xml",
|
|
2949
|
+
html: "xml",
|
|
2950
|
+
vue: "xml",
|
|
2951
|
+
css: "css",
|
|
2952
|
+
scss: "scss",
|
|
2953
|
+
md: "markdown",
|
|
2954
|
+
dockerfile: "dockerfile",
|
|
2955
|
+
proto: "protobuf",
|
|
2956
|
+
gradle: "gradle",
|
|
2957
|
+
properties: "properties",
|
|
2958
|
+
patch: "diff",
|
|
2959
|
+
diff: "diff",
|
|
2960
|
+
nix: "nix",
|
|
2961
|
+
cue: "cue",
|
|
2962
|
+
rego: "rego",
|
|
2963
|
+
bicep: "bicep",
|
|
2964
|
+
bazel: "starlark",
|
|
2965
|
+
bzl: "starlark",
|
|
2966
|
+
cmake: "cmake",
|
|
2967
|
+
groovy: "groovy",
|
|
2968
|
+
dart: "dart",
|
|
2969
|
+
scala: "scala",
|
|
2970
|
+
clj: "clojure",
|
|
2971
|
+
cljs: "clojure",
|
|
2972
|
+
cljc: "clojure",
|
|
2973
|
+
edn: "clojure",
|
|
2974
|
+
ex: "elixir",
|
|
2975
|
+
exs: "elixir",
|
|
2976
|
+
erl: "erlang",
|
|
2977
|
+
hrl: "erlang",
|
|
2978
|
+
hs: "haskell",
|
|
2979
|
+
lhs: "haskell",
|
|
2980
|
+
ml: "ocaml",
|
|
2981
|
+
mli: "ocaml",
|
|
2982
|
+
jl: "julia",
|
|
2983
|
+
r: "r",
|
|
2984
|
+
rmd: "r",
|
|
2985
|
+
pl: "perl",
|
|
2986
|
+
pm: "perl",
|
|
2987
|
+
tcl: "tcl",
|
|
2988
|
+
vim: "vim",
|
|
2989
|
+
f: "fortran",
|
|
2990
|
+
f90: "fortran",
|
|
2991
|
+
m: "objective-c",
|
|
2992
|
+
mm: "objective-cpp",
|
|
2993
|
+
tex: "tex",
|
|
2994
|
+
bib: "bibtex",
|
|
2995
|
+
rst: "rst"
|
|
2996
|
+
};
|
|
2997
|
+
TEXT_SOURCE_EXTENSIONS = new Set([
|
|
2998
|
+
...Object.keys(EXT_TO_LANG),
|
|
2999
|
+
"txt",
|
|
3000
|
+
"md",
|
|
3001
|
+
"markdown",
|
|
3002
|
+
"mdown",
|
|
3003
|
+
"mkdn",
|
|
3004
|
+
"mdx",
|
|
3005
|
+
"json",
|
|
3006
|
+
"jsonc",
|
|
3007
|
+
"csv",
|
|
3008
|
+
"tsv",
|
|
3009
|
+
"yaml",
|
|
3010
|
+
"yml",
|
|
3011
|
+
"toml",
|
|
3012
|
+
"hcl",
|
|
3013
|
+
"tf",
|
|
3014
|
+
"tfvars",
|
|
3015
|
+
"tfstate",
|
|
3016
|
+
"xml",
|
|
3017
|
+
"html",
|
|
3018
|
+
"htm",
|
|
3019
|
+
"css",
|
|
3020
|
+
"scss",
|
|
3021
|
+
"sass",
|
|
3022
|
+
"less",
|
|
3023
|
+
"js",
|
|
3024
|
+
"jsx",
|
|
3025
|
+
"mjs",
|
|
3026
|
+
"cjs",
|
|
3027
|
+
"ts",
|
|
3028
|
+
"tsx",
|
|
3029
|
+
"mts",
|
|
3030
|
+
"cts",
|
|
3031
|
+
"vue",
|
|
3032
|
+
"svelte",
|
|
3033
|
+
"astro",
|
|
3034
|
+
"rs",
|
|
3035
|
+
"go",
|
|
3036
|
+
"py",
|
|
3037
|
+
"rb",
|
|
3038
|
+
"php",
|
|
3039
|
+
"java",
|
|
3040
|
+
"kt",
|
|
3041
|
+
"kts",
|
|
3042
|
+
"c",
|
|
3043
|
+
"cc",
|
|
3044
|
+
"cpp",
|
|
3045
|
+
"cxx",
|
|
3046
|
+
"h",
|
|
3047
|
+
"hpp",
|
|
3048
|
+
"cs",
|
|
3049
|
+
"swift",
|
|
3050
|
+
"sh",
|
|
3051
|
+
"bash",
|
|
3052
|
+
"zsh",
|
|
3053
|
+
"fish",
|
|
3054
|
+
"ps1",
|
|
3055
|
+
"sql",
|
|
3056
|
+
"graphql",
|
|
3057
|
+
"graphqls",
|
|
3058
|
+
"gql",
|
|
3059
|
+
"ini",
|
|
3060
|
+
"conf",
|
|
3061
|
+
"env",
|
|
3062
|
+
"properties",
|
|
3063
|
+
"gitignore",
|
|
3064
|
+
"dockerignore",
|
|
3065
|
+
"editorconfig",
|
|
3066
|
+
"lock",
|
|
3067
|
+
"log",
|
|
3068
|
+
"patch",
|
|
3069
|
+
"diff",
|
|
3070
|
+
"sum",
|
|
3071
|
+
"mk",
|
|
3072
|
+
"proto",
|
|
3073
|
+
"thrift",
|
|
3074
|
+
"prisma",
|
|
3075
|
+
"gradle",
|
|
3076
|
+
"cmake",
|
|
3077
|
+
"nix",
|
|
3078
|
+
"cue",
|
|
3079
|
+
"rego",
|
|
3080
|
+
"bicep",
|
|
3081
|
+
"bazel",
|
|
3082
|
+
"bzl",
|
|
3083
|
+
"dart",
|
|
3084
|
+
"scala",
|
|
3085
|
+
"clj",
|
|
3086
|
+
"cljs",
|
|
3087
|
+
"cljc",
|
|
3088
|
+
"edn",
|
|
3089
|
+
"ex",
|
|
3090
|
+
"exs",
|
|
3091
|
+
"erl",
|
|
3092
|
+
"hrl",
|
|
3093
|
+
"hs",
|
|
3094
|
+
"lhs",
|
|
3095
|
+
"ml",
|
|
3096
|
+
"mli",
|
|
3097
|
+
"jl",
|
|
3098
|
+
"r",
|
|
3099
|
+
"rmd",
|
|
3100
|
+
"pl",
|
|
3101
|
+
"pm",
|
|
3102
|
+
"tcl",
|
|
3103
|
+
"vim",
|
|
3104
|
+
"groovy",
|
|
3105
|
+
"f",
|
|
3106
|
+
"f90",
|
|
3107
|
+
"m",
|
|
3108
|
+
"mm",
|
|
3109
|
+
"pas",
|
|
3110
|
+
"tex",
|
|
3111
|
+
"bib",
|
|
3112
|
+
"rst",
|
|
3113
|
+
"adoc",
|
|
3114
|
+
"org",
|
|
3115
|
+
"ipynb",
|
|
3116
|
+
"ejs",
|
|
3117
|
+
"hbs",
|
|
3118
|
+
"mustache",
|
|
3119
|
+
"liquid",
|
|
3120
|
+
"pug"
|
|
3121
|
+
]);
|
|
3122
|
+
TEXT_SOURCE_FILENAMES = new Set([
|
|
3123
|
+
"readme",
|
|
3124
|
+
"license",
|
|
3125
|
+
"copying",
|
|
3126
|
+
"authors",
|
|
3127
|
+
"contributors",
|
|
3128
|
+
"notice",
|
|
3129
|
+
"changelog",
|
|
3130
|
+
"todo",
|
|
3131
|
+
"manifest",
|
|
3132
|
+
"version",
|
|
3133
|
+
"codeowners",
|
|
3134
|
+
"go.mod",
|
|
3135
|
+
"build.bazel",
|
|
3136
|
+
"workspace.bazel",
|
|
3137
|
+
"module.bazel",
|
|
3138
|
+
"gemfile",
|
|
3139
|
+
"rakefile",
|
|
3140
|
+
"procfile",
|
|
3141
|
+
"brewfile",
|
|
3142
|
+
"gnumakefile",
|
|
3143
|
+
"bsdmakefile",
|
|
3144
|
+
".gitattributes",
|
|
3145
|
+
".gitmodules",
|
|
3146
|
+
".npmrc",
|
|
3147
|
+
".nvmrc",
|
|
3148
|
+
".yarnrc",
|
|
3149
|
+
".prettierrc",
|
|
3150
|
+
".eslintrc",
|
|
3151
|
+
".babelrc",
|
|
3152
|
+
".stylelintrc"
|
|
3153
|
+
]);
|
|
3154
|
+
});
|
|
3155
|
+
|
|
2856
3156
|
// web-src/server/cache.ts
|
|
2857
3157
|
import { lstatSync as lstatSync2 } from "node:fs";
|
|
2858
3158
|
import { join as join6 } from "node:path";
|
|
@@ -3584,6 +3884,9 @@ import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
|
|
|
3584
3884
|
function dockerDatabasesCacheKey(serviceName, kind, cwd) {
|
|
3585
3885
|
return `${serviceName}\x00${kind}\x00${cwd}`;
|
|
3586
3886
|
}
|
|
3887
|
+
function dockerSchemasCacheKey(serviceName, kind, cwd, database) {
|
|
3888
|
+
return `${serviceName}\x00${kind}\x00${cwd}\x00${database}`;
|
|
3889
|
+
}
|
|
3587
3890
|
function setDockerDatabasesCache(key, value, ttlMs, now = Date.now()) {
|
|
3588
3891
|
const cachedValue = [...value];
|
|
3589
3892
|
dockerDatabasesCache.set(key, {
|
|
@@ -3592,6 +3895,14 @@ function setDockerDatabasesCache(key, value, ttlMs, now = Date.now()) {
|
|
|
3592
3895
|
});
|
|
3593
3896
|
return [...cachedValue];
|
|
3594
3897
|
}
|
|
3898
|
+
function setDockerSchemasCache(key, value, ttlMs, now = Date.now()) {
|
|
3899
|
+
const cachedValue = [...value];
|
|
3900
|
+
dockerSchemasCache.set(key, {
|
|
3901
|
+
value: cachedValue,
|
|
3902
|
+
expiresAt: now + ttlMs
|
|
3903
|
+
});
|
|
3904
|
+
return [...cachedValue];
|
|
3905
|
+
}
|
|
3595
3906
|
function fallbackDockerDatabases(defaultDb) {
|
|
3596
3907
|
return defaultDb ? [defaultDb] : [];
|
|
3597
3908
|
}
|
|
@@ -3909,10 +4220,26 @@ function createDockerAdapter(config) {
|
|
|
3909
4220
|
}
|
|
3910
4221
|
const columnCache = new Map;
|
|
3911
4222
|
const tableMetaCache = createTableMetaCache();
|
|
4223
|
+
function currentPostgresSchema() {
|
|
4224
|
+
return config.schema || "public";
|
|
4225
|
+
}
|
|
4226
|
+
function postgresSchemaLiteral() {
|
|
4227
|
+
return escapeSqlString(currentPostgresSchema());
|
|
4228
|
+
}
|
|
4229
|
+
function tableIdentifier(table) {
|
|
4230
|
+
if (config.kind === "postgresql") {
|
|
4231
|
+
return `${sanitizeIdentifier(currentPostgresSchema(), config.kind)}.${sanitizeIdentifier(table, config.kind)}`;
|
|
4232
|
+
}
|
|
4233
|
+
return sanitizeIdentifier(table, config.kind);
|
|
4234
|
+
}
|
|
4235
|
+
function postgresRegclassLiteral(table) {
|
|
4236
|
+
return escapeSqlString(`${sanitizeIdentifier(currentPostgresSchema(), "postgresql")}.${sanitizeIdentifier(table, "postgresql")}`);
|
|
4237
|
+
}
|
|
3912
4238
|
function buildColumnsSql(table) {
|
|
3913
4239
|
const tableLiteral = table.replace(/'/g, "''");
|
|
3914
4240
|
if (config.kind === "postgresql") {
|
|
3915
|
-
|
|
4241
|
+
const schemaLiteral = postgresSchemaLiteral();
|
|
4242
|
+
return `SELECT c.column_name, c.data_type, c.is_nullable, c.column_default, CASE WHEN pk.column_name IS NULL THEN 'NO' ELSE 'YES' END FROM information_schema.columns c LEFT JOIN (SELECT kcu.column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema AND tc.table_name = kcu.table_name WHERE tc.table_schema = ${schemaLiteral} AND tc.table_name = '${tableLiteral}' AND tc.constraint_type = 'PRIMARY KEY') pk ON pk.column_name = c.column_name WHERE c.table_schema = ${schemaLiteral} AND c.table_name = '${tableLiteral}' ORDER BY c.ordinal_position`;
|
|
3916
4243
|
}
|
|
3917
4244
|
return `SELECT column_name, column_type, is_nullable, column_default, column_key FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = '${tableLiteral}' ORDER BY ordinal_position`;
|
|
3918
4245
|
}
|
|
@@ -3950,37 +4277,8 @@ function createDockerAdapter(config) {
|
|
|
3950
4277
|
return countResult.rows.length > 0 ? Number(countResult.rows[0][0]) || 0 : 0;
|
|
3951
4278
|
}
|
|
3952
4279
|
function fetchColumnsUncached(table) {
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
sql = `SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '${table.replace(/'/g, "''")}' ORDER BY ordinal_position`;
|
|
3956
|
-
} else {
|
|
3957
|
-
sql = `SELECT column_name, column_type, is_nullable, column_default, column_key FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = '${table.replace(/'/g, "''")}' ORDER BY ordinal_position`;
|
|
3958
|
-
}
|
|
3959
|
-
const result = exec(sql);
|
|
3960
|
-
if (config.kind === "postgresql") {
|
|
3961
|
-
const pkSql = `SELECT a.attname FROM pg_index i JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) WHERE i.indrelid = '${table.replace(/'/g, "''")}'::regclass AND i.indisprimary`;
|
|
3962
|
-
let pkCols;
|
|
3963
|
-
try {
|
|
3964
|
-
const pkResult = exec(pkSql);
|
|
3965
|
-
pkCols = new Set(pkResult.rows.map((r) => r[0]));
|
|
3966
|
-
} catch {
|
|
3967
|
-
pkCols = new Set;
|
|
3968
|
-
}
|
|
3969
|
-
return result.rows.map((row) => ({
|
|
3970
|
-
name: row[0],
|
|
3971
|
-
type: row[1],
|
|
3972
|
-
nullable: row[2] === "YES",
|
|
3973
|
-
primaryKey: pkCols.has(row[0]),
|
|
3974
|
-
defaultValue: row[3] === "" ? null : row[3]
|
|
3975
|
-
}));
|
|
3976
|
-
}
|
|
3977
|
-
return result.rows.map((row) => ({
|
|
3978
|
-
name: row[0],
|
|
3979
|
-
type: row[1],
|
|
3980
|
-
nullable: row[2] === "YES",
|
|
3981
|
-
primaryKey: row[4] === "PRI",
|
|
3982
|
-
defaultValue: row[3] === "NULL" ? null : row[3]
|
|
3983
|
-
}));
|
|
4280
|
+
const result = exec(buildColumnsSql(table));
|
|
4281
|
+
return columnsFromInfoRows(result.rows);
|
|
3984
4282
|
}
|
|
3985
4283
|
const adapter = {
|
|
3986
4284
|
kind: config.kind,
|
|
@@ -3989,7 +4287,7 @@ function createDockerAdapter(config) {
|
|
|
3989
4287
|
getTables() {
|
|
3990
4288
|
let sql;
|
|
3991
4289
|
if (config.kind === "postgresql") {
|
|
3992
|
-
sql = `SELECT table_name, table_type FROM information_schema.tables WHERE table_schema =
|
|
4290
|
+
sql = `SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = ${postgresSchemaLiteral()} ORDER BY table_name`;
|
|
3993
4291
|
} else {
|
|
3994
4292
|
sql = `SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = DATABASE() ORDER BY table_name`;
|
|
3995
4293
|
}
|
|
@@ -4011,7 +4309,7 @@ function createDockerAdapter(config) {
|
|
|
4011
4309
|
getIndexes() {
|
|
4012
4310
|
let sql;
|
|
4013
4311
|
if (config.kind === "postgresql") {
|
|
4014
|
-
sql = `SELECT indexname, tablename FROM pg_indexes WHERE schemaname =
|
|
4312
|
+
sql = `SELECT indexname, tablename FROM pg_indexes WHERE schemaname = ${postgresSchemaLiteral()} AND indexname NOT LIKE 'pg_%' ORDER BY indexname`;
|
|
4015
4313
|
} else {
|
|
4016
4314
|
sql = `SELECT DISTINCT index_name, table_name, non_unique FROM information_schema.statistics WHERE table_schema = DATABASE() ORDER BY index_name`;
|
|
4017
4315
|
}
|
|
@@ -4034,18 +4332,25 @@ function createDockerAdapter(config) {
|
|
|
4034
4332
|
getForeignKeys() {
|
|
4035
4333
|
let sql;
|
|
4036
4334
|
if (config.kind === "postgresql") {
|
|
4037
|
-
sql = `SELECT tc.table_name, kcu.column_name, ccu.table_name, ccu.column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema JOIN information_schema.constraint_column_usage ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema =
|
|
4335
|
+
sql = `SELECT tc.table_schema, tc.table_name, kcu.column_name, ccu.table_schema, ccu.table_name, ccu.column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema JOIN information_schema.constraint_column_usage ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = ${postgresSchemaLiteral()}`;
|
|
4038
4336
|
} else {
|
|
4039
4337
|
sql = `SELECT table_name, column_name, referenced_table_name, referenced_column_name FROM information_schema.key_column_usage WHERE table_schema = DATABASE() AND referenced_table_name IS NOT NULL`;
|
|
4040
4338
|
}
|
|
4041
4339
|
try {
|
|
4042
4340
|
const result = exec(sql);
|
|
4043
|
-
return result.rows.map((row) =>
|
|
4341
|
+
return result.rows.map((row) => config.kind === "postgresql" ? {
|
|
4342
|
+
fromSchema: row[0],
|
|
4343
|
+
fromTable: row[1],
|
|
4344
|
+
fromColumn: row[2],
|
|
4345
|
+
toSchema: row[3],
|
|
4346
|
+
toTable: row[4],
|
|
4347
|
+
toColumn: row[5]
|
|
4348
|
+
} : {
|
|
4044
4349
|
fromTable: row[0],
|
|
4045
4350
|
fromColumn: row[1],
|
|
4046
4351
|
toTable: row[2],
|
|
4047
4352
|
toColumn: row[3]
|
|
4048
|
-
})
|
|
4353
|
+
});
|
|
4049
4354
|
} catch {
|
|
4050
4355
|
return [];
|
|
4051
4356
|
}
|
|
@@ -4063,7 +4368,7 @@ function createDockerAdapter(config) {
|
|
|
4063
4368
|
let sql;
|
|
4064
4369
|
if (config.kind === "postgresql") {
|
|
4065
4370
|
const inList = uncached.map((t) => `'${t.replace(/'/g, "''")}'`).join(",");
|
|
4066
|
-
sql = `SELECT table_name, column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema =
|
|
4371
|
+
sql = `SELECT table_name, column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = ${postgresSchemaLiteral()} AND table_name IN (${inList}) ORDER BY table_name, ordinal_position`;
|
|
4067
4372
|
} else {
|
|
4068
4373
|
const inList = uncached.map((t) => `'${t.replace(/'/g, "''")}'`).join(",");
|
|
4069
4374
|
sql = `SELECT table_name, column_name, column_type, is_nullable, column_default, column_key FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name IN (${inList}) ORDER BY table_name, ordinal_position`;
|
|
@@ -4081,7 +4386,7 @@ function createDockerAdapter(config) {
|
|
|
4081
4386
|
if (config.kind === "postgresql") {
|
|
4082
4387
|
try {
|
|
4083
4388
|
const pkInList = uncached.map((t) => `'${t.replace(/'/g, "''")}'`).join(",");
|
|
4084
|
-
const pkResult = exec(`SELECT c.relname, a.attname FROM pg_index i JOIN pg_class c ON c.oid = i.indrelid JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) WHERE i.indisprimary AND c.relname IN (${pkInList})`);
|
|
4389
|
+
const pkResult = exec(`SELECT c.relname, a.attname FROM pg_index i JOIN pg_class c ON c.oid = i.indrelid JOIN pg_namespace n ON n.oid = c.relnamespace JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) WHERE i.indisprimary AND n.nspname = ${postgresSchemaLiteral()} AND c.relname IN (${pkInList})`);
|
|
4085
4390
|
for (const row of pkResult.rows) {
|
|
4086
4391
|
const existing = pkMap.get(row[0]) || new Set;
|
|
4087
4392
|
existing.add(row[1]);
|
|
@@ -4124,7 +4429,7 @@ function createDockerAdapter(config) {
|
|
|
4124
4429
|
return result;
|
|
4125
4430
|
},
|
|
4126
4431
|
getTableRowCount(table) {
|
|
4127
|
-
const id =
|
|
4432
|
+
const id = tableIdentifier(table);
|
|
4128
4433
|
const result = exec(`SELECT COUNT(*) FROM ${id}`);
|
|
4129
4434
|
return result.rows.length > 0 ? Number(result.rows[0][0]) || 0 : 0;
|
|
4130
4435
|
},
|
|
@@ -4133,7 +4438,7 @@ function createDockerAdapter(config) {
|
|
|
4133
4438
|
if (tables.length === 0)
|
|
4134
4439
|
return result;
|
|
4135
4440
|
const parts = tables.map((t) => {
|
|
4136
|
-
const id =
|
|
4441
|
+
const id = tableIdentifier(t);
|
|
4137
4442
|
return `SELECT '${t.replace(/'/g, "''")}' AS tbl, COUNT(*) AS cnt FROM ${id}`;
|
|
4138
4443
|
});
|
|
4139
4444
|
const sql = parts.join(" UNION ALL ");
|
|
@@ -4144,7 +4449,7 @@ function createDockerAdapter(config) {
|
|
|
4144
4449
|
}
|
|
4145
4450
|
} catch {
|
|
4146
4451
|
for (const t of tables) {
|
|
4147
|
-
const id =
|
|
4452
|
+
const id = tableIdentifier(t);
|
|
4148
4453
|
try {
|
|
4149
4454
|
const r = exec(`SELECT COUNT(*) FROM ${id}`);
|
|
4150
4455
|
result.set(t, r.rows.length > 0 ? Number(r.rows[0][0]) || 0 : 0);
|
|
@@ -4156,7 +4461,7 @@ function createDockerAdapter(config) {
|
|
|
4156
4461
|
return result;
|
|
4157
4462
|
},
|
|
4158
4463
|
async getTablePageWithMeta(table, options) {
|
|
4159
|
-
const id =
|
|
4464
|
+
const id = tableIdentifier(table);
|
|
4160
4465
|
const order = buildOrderClause(options.orderBy, config.kind);
|
|
4161
4466
|
const countSql = `SELECT COUNT(*) AS cnt FROM ${id}`;
|
|
4162
4467
|
const columnsPromise = tableMetaCache.getColumns(table, () => fetchColumnsAsyncUncached(table));
|
|
@@ -4171,7 +4476,7 @@ function createDockerAdapter(config) {
|
|
|
4171
4476
|
return tablePageMetaFromResults(columns, dataResult, totalRows);
|
|
4172
4477
|
},
|
|
4173
4478
|
async getFilteredTablePageWithMeta(table, options) {
|
|
4174
|
-
const id =
|
|
4479
|
+
const id = tableIdentifier(table);
|
|
4175
4480
|
const order = buildOrderClause(options.orderBy, config.kind);
|
|
4176
4481
|
const where = buildDockerFilterWhere(options.grouped, config.kind);
|
|
4177
4482
|
const whereClause = where ? ` WHERE ${where}` : "";
|
|
@@ -4188,7 +4493,7 @@ function createDockerAdapter(config) {
|
|
|
4188
4493
|
return tablePageMetaFromResults(columns, dataResult, rowCountFromResult(countResult));
|
|
4189
4494
|
},
|
|
4190
4495
|
getTablePage(table, options) {
|
|
4191
|
-
const id =
|
|
4496
|
+
const id = tableIdentifier(table);
|
|
4192
4497
|
const order = buildOrderClause(options.orderBy, config.kind);
|
|
4193
4498
|
const cols = this.getColumns(table);
|
|
4194
4499
|
const selectList = buildTableSelectList(cols, config.kind);
|
|
@@ -4222,10 +4527,8 @@ function createDockerAdapter(config) {
|
|
|
4222
4527
|
if (BLOCKED_RE.test(upper)) {
|
|
4223
4528
|
throw new Error("Query contains a disallowed statement keyword");
|
|
4224
4529
|
}
|
|
4225
|
-
const readOnlyPreamble = config.kind === "postgresql" ? "BEGIN TRANSACTION READ ONLY; " : "SET SESSION TRANSACTION READ ONLY; ";
|
|
4226
|
-
const readOnlyPostamble = config.kind === "postgresql" ? "; COMMIT" : "; SET SESSION TRANSACTION READ WRITE";
|
|
4227
4530
|
const stripped = trimmed.replace(/;\s*$/, "");
|
|
4228
|
-
const limited =
|
|
4531
|
+
const limited = config.kind === "postgresql" ? `BEGIN TRANSACTION READ ONLY; SET LOCAL search_path = ${sanitizeIdentifier(currentPostgresSchema(), config.kind)}; ${stripped} LIMIT ${maxRows}; COMMIT` : `SET SESSION TRANSACTION READ ONLY; ${stripped} LIMIT ${maxRows}; SET SESSION TRANSACTION READ WRITE`;
|
|
4229
4532
|
const result = exec(limited);
|
|
4230
4533
|
const columnNames = config.kind === "mysql" && result.columns.length > 0 ? result.columns : result.rows.length > 0 ? Array.from({ length: result.rows[0].length }, (_, i) => `col${i + 1}`) : [];
|
|
4231
4534
|
return {
|
|
@@ -4241,14 +4544,14 @@ function createDockerAdapter(config) {
|
|
|
4241
4544
|
getCreateStatement(table) {
|
|
4242
4545
|
if (config.kind === "mysql") {
|
|
4243
4546
|
try {
|
|
4244
|
-
const result = exec(`SHOW CREATE TABLE ${
|
|
4547
|
+
const result = exec(`SHOW CREATE TABLE ${tableIdentifier(table)}`);
|
|
4245
4548
|
return result.rows.length > 0 ? result.rows[0][1] || "" : "";
|
|
4246
4549
|
} catch {
|
|
4247
4550
|
return "";
|
|
4248
4551
|
}
|
|
4249
4552
|
}
|
|
4250
4553
|
try {
|
|
4251
|
-
const result = exec(`SELECT 'CREATE TABLE ' ||
|
|
4554
|
+
const result = exec(`SELECT 'CREATE TABLE ' || ${escapeSqlString(tableIdentifier(table))} || ' (...)' AS ddl`);
|
|
4252
4555
|
return result.rows.length > 0 ? result.rows[0][0] || "" : "";
|
|
4253
4556
|
} catch {
|
|
4254
4557
|
return "";
|
|
@@ -4259,7 +4562,7 @@ function createDockerAdapter(config) {
|
|
|
4259
4562
|
if (config.kind === "mysql") {
|
|
4260
4563
|
sql = `SELECT trigger_name, action_statement FROM information_schema.triggers WHERE event_object_schema = DATABASE() AND event_object_table = '${table.replace(/'/g, "''")}'`;
|
|
4261
4564
|
} else {
|
|
4262
|
-
sql = `SELECT tgname, pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid =
|
|
4565
|
+
sql = `SELECT tgname, pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid = ${postgresRegclassLiteral(table)}::regclass AND NOT tgisinternal`;
|
|
4263
4566
|
}
|
|
4264
4567
|
try {
|
|
4265
4568
|
const result = exec(sql);
|
|
@@ -4317,7 +4620,7 @@ function listDockerDatabases(serviceName, kind, env, cwd) {
|
|
|
4317
4620
|
return [...cached.value];
|
|
4318
4621
|
const containerName = resolveRunningComposeContainerName(serviceName, cwd);
|
|
4319
4622
|
if (!containerName) {
|
|
4320
|
-
return
|
|
4623
|
+
return setDockerSchemasCache(cacheKey, [], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
4321
4624
|
}
|
|
4322
4625
|
const user = env.POSTGRES_USER || env.MYSQL_USER || env.MARIADB_USER || env.POSTGRES_USERNAME || env.MYSQL_USERNAME || env.USER || "root";
|
|
4323
4626
|
const password = env.POSTGRES_PASSWORD || env.MYSQL_PASSWORD || env.MARIADB_PASSWORD || "";
|
|
@@ -4354,7 +4657,43 @@ function listDockerDatabases(serviceName, kind, env, cwd) {
|
|
|
4354
4657
|
return setDockerDatabasesCache(cacheKey, [], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
4355
4658
|
}
|
|
4356
4659
|
}
|
|
4357
|
-
function
|
|
4660
|
+
function listDockerSchemas(serviceName, kind, env, cwd, overrideDatabase) {
|
|
4661
|
+
if (kind !== "postgresql")
|
|
4662
|
+
return [];
|
|
4663
|
+
const user = env.POSTGRES_USER || env.POSTGRES_USERNAME || "postgres";
|
|
4664
|
+
const password = env.POSTGRES_PASSWORD || "";
|
|
4665
|
+
const database = overrideDatabase || env.POSTGRES_DB || "postgres";
|
|
4666
|
+
const cacheKey = dockerSchemasCacheKey(serviceName, kind, cwd, database);
|
|
4667
|
+
const now = Date.now();
|
|
4668
|
+
const cached = dockerSchemasCache.get(cacheKey);
|
|
4669
|
+
if (cached && cached.expiresAt > now)
|
|
4670
|
+
return [...cached.value];
|
|
4671
|
+
const containerName = resolveRunningComposeContainerName(serviceName, cwd);
|
|
4672
|
+
if (!containerName) {
|
|
4673
|
+
return setDockerSchemasCache(cacheKey, [], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
4674
|
+
}
|
|
4675
|
+
const config = {
|
|
4676
|
+
kind,
|
|
4677
|
+
containerName,
|
|
4678
|
+
user,
|
|
4679
|
+
password,
|
|
4680
|
+
database
|
|
4681
|
+
};
|
|
4682
|
+
try {
|
|
4683
|
+
const sql = `SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('pg_catalog', 'information_schema') AND schema_name NOT LIKE 'pg_toast%' AND schema_name NOT LIKE 'pg_temp_%' AND schema_name NOT LIKE 'pg_toast_temp_%' AND has_schema_privilege(schema_name, 'USAGE') ORDER BY CASE WHEN schema_name = 'public' THEN 0 ELSE 1 END, schema_name`;
|
|
4684
|
+
const result = execInContainer(config, sql);
|
|
4685
|
+
if (result.code !== 0) {
|
|
4686
|
+
return setDockerSchemasCache(cacheKey, ["public"], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
4687
|
+
}
|
|
4688
|
+
const parsed = parseTsvOutput(result.stdout, false);
|
|
4689
|
+
const schemas = parsed.rows.map((r) => r[0]).filter(Boolean);
|
|
4690
|
+
const value = schemas.length > 0 ? schemas : ["public"];
|
|
4691
|
+
return setDockerSchemasCache(cacheKey, value, DOCKER_DATABASES_POSITIVE_TTL_MS, now);
|
|
4692
|
+
} catch {
|
|
4693
|
+
return setDockerSchemasCache(cacheKey, ["public"], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
function openDockerAdapter(serviceName, kind, env, cwd, overrideDatabase, schema) {
|
|
4358
4697
|
const containerName = resolveRunningComposeContainerNameOrThrow(serviceName, cwd);
|
|
4359
4698
|
const user = env.POSTGRES_USER || env.MYSQL_USER || env.MARIADB_USER || (kind === "postgresql" ? "postgres" : "root");
|
|
4360
4699
|
const password = env.POSTGRES_PASSWORD || env.MYSQL_PASSWORD || env.MYSQL_ROOT_PASSWORD || env.MARIADB_PASSWORD || env.MARIADB_ROOT_PASSWORD || "";
|
|
@@ -4364,14 +4703,16 @@ function openDockerAdapter(serviceName, kind, env, cwd, overrideDatabase) {
|
|
|
4364
4703
|
containerName,
|
|
4365
4704
|
user,
|
|
4366
4705
|
password,
|
|
4367
|
-
database
|
|
4706
|
+
database,
|
|
4707
|
+
...kind === "postgresql" && schema ? { schema } : {}
|
|
4368
4708
|
});
|
|
4369
4709
|
}
|
|
4370
|
-
var COLUMNS_TTL_MS = 30000, ROWCOUNT_TTL_MS = 15000, DOCKER_DATABASES_POSITIVE_TTL_MS = 15000, DOCKER_DATABASES_NEGATIVE_TTL_MS = 3000, dockerDatabasesCache, spawnSyncImpl2, MYSQL_SPATIAL_TYPES;
|
|
4710
|
+
var COLUMNS_TTL_MS = 30000, ROWCOUNT_TTL_MS = 15000, DOCKER_DATABASES_POSITIVE_TTL_MS = 15000, DOCKER_DATABASES_NEGATIVE_TTL_MS = 3000, dockerDatabasesCache, dockerSchemasCache, spawnSyncImpl2, MYSQL_SPATIAL_TYPES;
|
|
4371
4711
|
var init_docker = __esm(() => {
|
|
4372
4712
|
init_sql_snapshot();
|
|
4373
4713
|
init_docker_utils();
|
|
4374
4714
|
dockerDatabasesCache = new Map;
|
|
4715
|
+
dockerSchemasCache = new Map;
|
|
4375
4716
|
spawnSyncImpl2 = spawnSync3;
|
|
4376
4717
|
MYSQL_SPATIAL_TYPES = new Set([
|
|
4377
4718
|
"geometry",
|
|
@@ -5657,6 +5998,12 @@ function createDockerAdapterCache(maxEntries = DEFAULT_MAX_DOCKER_ADAPTER_CACHE,
|
|
|
5657
5998
|
const cached = cache.get(key);
|
|
5658
5999
|
if (cached)
|
|
5659
6000
|
closeEntry(key, cached);
|
|
6001
|
+
},
|
|
6002
|
+
closePrefix(prefix) {
|
|
6003
|
+
for (const [key, entry] of Array.from(cache)) {
|
|
6004
|
+
if (key.startsWith(prefix))
|
|
6005
|
+
closeEntry(key, entry);
|
|
6006
|
+
}
|
|
5660
6007
|
}
|
|
5661
6008
|
};
|
|
5662
6009
|
}
|
|
@@ -6749,12 +7096,18 @@ function deleteQueryHistoryEntry(state, id) {
|
|
|
6749
7096
|
entries: state.entries.filter((e) => e.id !== id)
|
|
6750
7097
|
};
|
|
6751
7098
|
}
|
|
6752
|
-
function clearQueryHistory(state, dbId) {
|
|
7099
|
+
function clearQueryHistory(state, dbId, schema) {
|
|
6753
7100
|
if (!dbId)
|
|
6754
7101
|
return emptyState();
|
|
6755
7102
|
return {
|
|
6756
7103
|
version: 1,
|
|
6757
|
-
entries: state.entries.filter((e) =>
|
|
7104
|
+
entries: state.entries.filter((e) => {
|
|
7105
|
+
if (e.dbId !== dbId)
|
|
7106
|
+
return true;
|
|
7107
|
+
if (schema === undefined)
|
|
7108
|
+
return false;
|
|
7109
|
+
return (e.schema || "public") !== schema;
|
|
7110
|
+
})
|
|
6758
7111
|
};
|
|
6759
7112
|
}
|
|
6760
7113
|
var CODE_VIEWER_DIR2 = ".code-viewer", HISTORY_FILE_NAME = "query-history.json", MAX_ENTRIES2 = 200, MAX_PREVIEW_ROWS = 100, MAX_JSON_BYTES = 1e6;
|
|
@@ -6795,6 +7148,9 @@ async function getStoreDb(cwd) {
|
|
|
6795
7148
|
storeDb.exec("PRAGMA journal_mode=WAL");
|
|
6796
7149
|
storeDb.exec("PRAGMA foreign_keys=ON");
|
|
6797
7150
|
storeDb.exec(SCHEMA_SQL);
|
|
7151
|
+
try {
|
|
7152
|
+
storeDb.exec("ALTER TABLE snapshots ADD COLUMN schema_name TEXT");
|
|
7153
|
+
} catch {}
|
|
6798
7154
|
return storeDb;
|
|
6799
7155
|
}
|
|
6800
7156
|
function makeId2(prefix) {
|
|
@@ -6803,10 +7159,10 @@ function makeId2(prefix) {
|
|
|
6803
7159
|
function hashPayload(payloadJson) {
|
|
6804
7160
|
return createHash4("sha256").update(payloadJson).digest("hex");
|
|
6805
7161
|
}
|
|
6806
|
-
async function createSnapshot(cwd, dbId, kind, tables, note) {
|
|
7162
|
+
async function createSnapshot(cwd, dbId, kind, tables, note, schema) {
|
|
6807
7163
|
const db = await getStoreDb(cwd);
|
|
6808
7164
|
const id = makeId2("snap");
|
|
6809
|
-
db.prepare("INSERT INTO snapshots (id, db_id, kind, note, created_at, status) VALUES (?, ?, ?, ?, ?, ?)").run(id, dbId, kind, note, new Date().toISOString(), "running");
|
|
7165
|
+
db.prepare("INSERT INTO snapshots (id, db_id, schema_name, kind, note, created_at, status) VALUES (?, ?, ?, ?, ?, ?, ?)").run(id, dbId, schema ?? null, kind, note, new Date().toISOString(), "running");
|
|
6810
7166
|
for (const t of tables) {
|
|
6811
7167
|
db.prepare("INSERT INTO snapshot_tables (snapshot_id, table_name) VALUES (?, ?)").run(id, t);
|
|
6812
7168
|
}
|
|
@@ -6835,19 +7191,22 @@ async function finalizeSnapshot(cwd, snapshotId, error) {
|
|
|
6835
7191
|
db.prepare("UPDATE snapshots SET status = 'done' WHERE id = ?").run(snapshotId);
|
|
6836
7192
|
}
|
|
6837
7193
|
}
|
|
6838
|
-
async function listSnapshots(cwd, dbId) {
|
|
7194
|
+
async function listSnapshots(cwd, dbId, schema) {
|
|
6839
7195
|
const db = await getStoreDb(cwd);
|
|
6840
7196
|
let rows;
|
|
6841
|
-
if (dbId) {
|
|
6842
|
-
rows = db.prepare("SELECT id, db_id, kind, note, created_at, status, error_message FROM snapshots WHERE db_id = ? ORDER BY created_at DESC").all(dbId);
|
|
7197
|
+
if (dbId && schema !== undefined) {
|
|
7198
|
+
rows = db.prepare("SELECT id, db_id, schema_name, kind, note, created_at, status, error_message FROM snapshots WHERE db_id = ? AND COALESCE(schema_name, 'public') = ? ORDER BY created_at DESC").all(dbId, schema);
|
|
7199
|
+
} else if (dbId) {
|
|
7200
|
+
rows = db.prepare("SELECT id, db_id, schema_name, kind, note, created_at, status, error_message FROM snapshots WHERE db_id = ? ORDER BY created_at DESC").all(dbId);
|
|
6843
7201
|
} else {
|
|
6844
|
-
rows = db.prepare("SELECT id, db_id, kind, note, created_at, status, error_message FROM snapshots ORDER BY created_at DESC").all();
|
|
7202
|
+
rows = db.prepare("SELECT id, db_id, schema_name, kind, note, created_at, status, error_message FROM snapshots ORDER BY created_at DESC").all();
|
|
6845
7203
|
}
|
|
6846
7204
|
return rows.map((r) => {
|
|
6847
7205
|
const tableRows = db.prepare("SELECT table_name FROM snapshot_tables WHERE snapshot_id = ?").all(r.id);
|
|
6848
7206
|
return {
|
|
6849
7207
|
id: r.id,
|
|
6850
7208
|
dbId: r.db_id,
|
|
7209
|
+
...r.schema_name ? { schema: r.schema_name } : {},
|
|
6851
7210
|
kind: r.kind,
|
|
6852
7211
|
note: r.note,
|
|
6853
7212
|
createdAt: r.created_at,
|
|
@@ -6872,8 +7231,22 @@ async function deleteSnapshot(cwd, snapshotId) {
|
|
|
6872
7231
|
}
|
|
6873
7232
|
}
|
|
6874
7233
|
}
|
|
7234
|
+
function getSnapshotScope(db, snapshotId) {
|
|
7235
|
+
const row = db.prepare("SELECT db_id, COALESCE(schema_name, 'public') AS schema_name FROM snapshots WHERE id = ?").get(snapshotId);
|
|
7236
|
+
if (!row)
|
|
7237
|
+
throw new Error(`snapshot not found: ${snapshotId}`);
|
|
7238
|
+
return { dbId: row.db_id, schema: row.schema_name };
|
|
7239
|
+
}
|
|
7240
|
+
function assertSameSnapshotScope(db, beforeId, afterId) {
|
|
7241
|
+
const before = getSnapshotScope(db, beforeId);
|
|
7242
|
+
const after = getSnapshotScope(db, afterId);
|
|
7243
|
+
if (before.dbId !== after.dbId || before.schema !== after.schema) {
|
|
7244
|
+
throw new Error(`cannot compare snapshots from different database/schema (${before.dbId}:${before.schema} vs ${after.dbId}:${after.schema})`);
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
6875
7247
|
async function computeDiffTables(cwd, beforeId, afterId) {
|
|
6876
7248
|
const db = await getStoreDb(cwd);
|
|
7249
|
+
assertSameSnapshotScope(db, beforeId, afterId);
|
|
6877
7250
|
const beforeTables = db.prepare("SELECT table_name, table_hash, row_count FROM snapshot_tables WHERE snapshot_id = ?").all(beforeId);
|
|
6878
7251
|
const afterTables = db.prepare("SELECT table_name, table_hash, row_count FROM snapshot_tables WHERE snapshot_id = ?").all(afterId);
|
|
6879
7252
|
const beforeMap = new Map(beforeTables.map((t) => [t.table_name, t]));
|
|
@@ -6896,7 +7269,7 @@ async function computeDiffTables(cwd, beforeId, afterId) {
|
|
|
6896
7269
|
if (!b) {
|
|
6897
7270
|
results.push({
|
|
6898
7271
|
tableName: table,
|
|
6899
|
-
insertedCount: a.row_count,
|
|
7272
|
+
insertedCount: a ? a.row_count : 0,
|
|
6900
7273
|
updatedCount: 0,
|
|
6901
7274
|
deletedCount: 0,
|
|
6902
7275
|
unchangedCount: 0
|
|
@@ -6945,6 +7318,7 @@ async function computeDiffTables(cwd, beforeId, afterId) {
|
|
|
6945
7318
|
}
|
|
6946
7319
|
async function computeDiffRows(cwd, beforeId, afterId, table, offset = 0, limit = 200) {
|
|
6947
7320
|
const db = await getStoreDb(cwd);
|
|
7321
|
+
assertSameSnapshotScope(db, beforeId, afterId);
|
|
6948
7322
|
const allDiffRows = [];
|
|
6949
7323
|
const inserted = db.prepare(`SELECT a.row_key_json, a.payload_hash
|
|
6950
7324
|
FROM snapshot_rows a
|
|
@@ -7014,6 +7388,7 @@ var CODE_VIEWER_DIR3 = ".code-viewer", SNAPSHOT_DB_NAME = "db-snapshots.sqlite",
|
|
|
7014
7388
|
CREATE TABLE IF NOT EXISTS snapshots (
|
|
7015
7389
|
id TEXT PRIMARY KEY,
|
|
7016
7390
|
db_id TEXT NOT NULL,
|
|
7391
|
+
schema_name TEXT,
|
|
7017
7392
|
kind TEXT NOT NULL,
|
|
7018
7393
|
note TEXT NOT NULL DEFAULT '',
|
|
7019
7394
|
created_at TEXT NOT NULL,
|
|
@@ -7067,7 +7442,7 @@ async function runSnapshot(cwd, source, dbId, containers, note, onProgress, opti
|
|
|
7067
7442
|
throw new Error("data source does not support snapshot (missing SnapshotIterable capability)");
|
|
7068
7443
|
}
|
|
7069
7444
|
const snapshotSource = source;
|
|
7070
|
-
const snapshotId = await createSnapshot(cwd, dbId, snapshotSource.kind, containers, note);
|
|
7445
|
+
const snapshotId = await createSnapshot(cwd, dbId, snapshotSource.kind, containers, note, options.schema);
|
|
7071
7446
|
try {
|
|
7072
7447
|
options.onSnapshotId?.(snapshotId);
|
|
7073
7448
|
for (const container of containers) {
|
|
@@ -7300,14 +7675,40 @@ function ensureInit() {
|
|
|
7300
7675
|
async function getAdapter(r, _cwd) {
|
|
7301
7676
|
if (r.docker) {
|
|
7302
7677
|
const docker = r.docker;
|
|
7303
|
-
|
|
7678
|
+
const cacheKey = r.schema ? `${r.dbId}\x00schema=${r.schema}` : r.dbId;
|
|
7679
|
+
return dockerAdapterCache.getOrOpen(cacheKey, () => openDockerAdapter(docker.serviceName, docker.kind, docker.env, docker.composeDir, docker.database, r.schema));
|
|
7304
7680
|
}
|
|
7305
7681
|
return getConnection(r.resolved);
|
|
7306
7682
|
}
|
|
7307
7683
|
function sanitizeFilename(name) {
|
|
7308
7684
|
return name.replace(/["\\\r\n\x00-\x1f]/g, "_");
|
|
7309
7685
|
}
|
|
7310
|
-
function
|
|
7686
|
+
function normalizeSchemaParam(value) {
|
|
7687
|
+
if (value === undefined || value === null || value === "")
|
|
7688
|
+
return;
|
|
7689
|
+
if (value.length > MAX_SCHEMA_NAME_LEN) {
|
|
7690
|
+
return textError("invalid schema parameter", 400);
|
|
7691
|
+
}
|
|
7692
|
+
if (/[\x00-\x1f\x7f]/.test(value)) {
|
|
7693
|
+
return textError("invalid schema parameter", 400);
|
|
7694
|
+
}
|
|
7695
|
+
return value;
|
|
7696
|
+
}
|
|
7697
|
+
function resolvePostgresSchema(info, requestedSchema) {
|
|
7698
|
+
if (info.kind !== "postgresql")
|
|
7699
|
+
return;
|
|
7700
|
+
const schemas = listDockerSchemas(info.serviceName, "postgresql", info.env, info.composeDir, info.database);
|
|
7701
|
+
if (requestedSchema) {
|
|
7702
|
+
if (!schemas.includes(requestedSchema)) {
|
|
7703
|
+
return textError(`schema not found: ${requestedSchema}`, 404);
|
|
7704
|
+
}
|
|
7705
|
+
return requestedSchema;
|
|
7706
|
+
}
|
|
7707
|
+
if (schemas.includes("public"))
|
|
7708
|
+
return "public";
|
|
7709
|
+
return schemas[0] || "public";
|
|
7710
|
+
}
|
|
7711
|
+
function resolveDb(cwd, dbParam, omitDirNames, schemaParam) {
|
|
7311
7712
|
if (!dbParam)
|
|
7312
7713
|
return textError("missing db parameter", 400);
|
|
7313
7714
|
if (dbParam.startsWith("docker:")) {
|
|
@@ -7324,7 +7725,18 @@ function resolveDb(cwd, dbParam, omitDirNames) {
|
|
|
7324
7725
|
return textError("elasticsearch services must use the /_db/elasticsearch/* routes", 400);
|
|
7325
7726
|
}
|
|
7326
7727
|
const resolved2 = parsed.database ? { ...info, database: parsed.database } : info;
|
|
7327
|
-
|
|
7728
|
+
const requestedSchema = normalizeSchemaParam(schemaParam);
|
|
7729
|
+
if (requestedSchema instanceof Response)
|
|
7730
|
+
return requestedSchema;
|
|
7731
|
+
const schema = resolvePostgresSchema(resolved2, requestedSchema);
|
|
7732
|
+
if (schema instanceof Response)
|
|
7733
|
+
return schema;
|
|
7734
|
+
return {
|
|
7735
|
+
resolved: dbParam,
|
|
7736
|
+
dbId: dbParam,
|
|
7737
|
+
docker: resolved2,
|
|
7738
|
+
...schema ? { schema } : {}
|
|
7739
|
+
};
|
|
7328
7740
|
}
|
|
7329
7741
|
const resolved = validateDbPath(cwd, dbParam);
|
|
7330
7742
|
if (!resolved)
|
|
@@ -7379,8 +7791,24 @@ function handleFiles(cwd, omitDirNames) {
|
|
|
7379
7791
|
};
|
|
7380
7792
|
return json(body);
|
|
7381
7793
|
}
|
|
7794
|
+
function handleSchemas(cwd, url, omitDirNames) {
|
|
7795
|
+
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"));
|
|
7796
|
+
if (r instanceof Response)
|
|
7797
|
+
return r;
|
|
7798
|
+
if (!r.docker || r.docker.kind !== "postgresql") {
|
|
7799
|
+
const body2 = { dbId: r.dbId, schemas: [] };
|
|
7800
|
+
return json(body2);
|
|
7801
|
+
}
|
|
7802
|
+
const schemas = listDockerSchemas(r.docker.serviceName, "postgresql", r.docker.env, r.docker.composeDir, r.docker.database);
|
|
7803
|
+
const body = {
|
|
7804
|
+
dbId: r.dbId,
|
|
7805
|
+
schemas: schemas.map((name) => ({ name })),
|
|
7806
|
+
selectedSchema: r.schema
|
|
7807
|
+
};
|
|
7808
|
+
return json(body);
|
|
7809
|
+
}
|
|
7382
7810
|
async function handleSchema(cwd, url, omitDirNames) {
|
|
7383
|
-
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames);
|
|
7811
|
+
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"));
|
|
7384
7812
|
if (r instanceof Response)
|
|
7385
7813
|
return r;
|
|
7386
7814
|
const includeColumns = url.searchParams.get("includeColumns") === "1";
|
|
@@ -7405,6 +7833,7 @@ async function handleSchema(cwd, url, omitDirNames) {
|
|
|
7405
7833
|
const foreignKeys = adapter.getForeignKeys();
|
|
7406
7834
|
const body = {
|
|
7407
7835
|
dbId: r.dbId,
|
|
7836
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7408
7837
|
tables: tablesWithCount,
|
|
7409
7838
|
indexes,
|
|
7410
7839
|
foreignKeys
|
|
@@ -7482,7 +7911,7 @@ function groupFiltersByValue(filters) {
|
|
|
7482
7911
|
return grouped;
|
|
7483
7912
|
}
|
|
7484
7913
|
async function handleTable(cwd, url, omitDirNames) {
|
|
7485
|
-
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames);
|
|
7914
|
+
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"));
|
|
7486
7915
|
if (r instanceof Response)
|
|
7487
7916
|
return r;
|
|
7488
7917
|
const table = url.searchParams.get("table");
|
|
@@ -7517,6 +7946,7 @@ async function handleTable(cwd, url, omitDirNames) {
|
|
|
7517
7946
|
}
|
|
7518
7947
|
const body2 = {
|
|
7519
7948
|
dbId: r.dbId,
|
|
7949
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7520
7950
|
table,
|
|
7521
7951
|
columns: meta.columns,
|
|
7522
7952
|
rows: serializeDbRows(meta.rows),
|
|
@@ -7539,6 +7969,7 @@ async function handleTable(cwd, url, omitDirNames) {
|
|
|
7539
7969
|
}
|
|
7540
7970
|
const body2 = {
|
|
7541
7971
|
dbId: r.dbId,
|
|
7972
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7542
7973
|
table,
|
|
7543
7974
|
columns: meta.columns,
|
|
7544
7975
|
rows: serializeDbRows(meta.rows),
|
|
@@ -7570,6 +8001,7 @@ async function handleTable(cwd, url, omitDirNames) {
|
|
|
7570
8001
|
const dataResult = adapter.executeReadonlyQuery(dataSql, filter.useParams ? [...filter.params, limit, offset] : undefined);
|
|
7571
8002
|
const body2 = {
|
|
7572
8003
|
dbId: r.dbId,
|
|
8004
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7573
8005
|
table,
|
|
7574
8006
|
columns,
|
|
7575
8007
|
rows: serializeDbRows(dataResult.rows),
|
|
@@ -7585,6 +8017,7 @@ async function handleTable(cwd, url, omitDirNames) {
|
|
|
7585
8017
|
const totalRows = result.rowCount < limit ? offset + result.rowCount : adapter.getTableRowCount(table);
|
|
7586
8018
|
const body = {
|
|
7587
8019
|
dbId: r.dbId,
|
|
8020
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7588
8021
|
table,
|
|
7589
8022
|
columns,
|
|
7590
8023
|
rows: serializeDbRows(result.rows),
|
|
@@ -7637,7 +8070,7 @@ async function handleQuery(cwd, req, sendSse, omitDirNames) {
|
|
|
7637
8070
|
return body;
|
|
7638
8071
|
if (!body.db || !body.sql)
|
|
7639
8072
|
return textError("missing db or sql", 400);
|
|
7640
|
-
const r = resolveDb(cwd, body.db, omitDirNames);
|
|
8073
|
+
const r = resolveDb(cwd, body.db, omitDirNames, body.schema);
|
|
7641
8074
|
if (r instanceof Response)
|
|
7642
8075
|
return r;
|
|
7643
8076
|
const maxRows = Math.min(1e4, Math.max(1, body.maxRows || 1000));
|
|
@@ -7652,6 +8085,7 @@ async function handleQuery(cwd, req, sendSse, omitDirNames) {
|
|
|
7652
8085
|
const columnTypes = inferredColumns.length > 0 ? inferredColumns.map((col) => col.type) : result.columnTypes;
|
|
7653
8086
|
const response = {
|
|
7654
8087
|
dbId: body.db,
|
|
8088
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7655
8089
|
columns,
|
|
7656
8090
|
columnTypes,
|
|
7657
8091
|
rows: serializedRows,
|
|
@@ -7663,6 +8097,7 @@ async function handleQuery(cwd, req, sendSse, omitDirNames) {
|
|
|
7663
8097
|
const entry = {
|
|
7664
8098
|
id: makeHistoryId(),
|
|
7665
8099
|
dbId: body.db,
|
|
8100
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7666
8101
|
sql: body.sql,
|
|
7667
8102
|
title: body.title,
|
|
7668
8103
|
body: body.body,
|
|
@@ -7679,7 +8114,12 @@ async function handleQuery(cwd, req, sendSse, omitDirNames) {
|
|
|
7679
8114
|
const state = loadQueryHistory(cwd);
|
|
7680
8115
|
const updated = addQueryHistoryEntry(state, entry);
|
|
7681
8116
|
saveQueryHistory(cwd, updated);
|
|
7682
|
-
sendSse?.("db-query", JSON.stringify({
|
|
8117
|
+
sendSse?.("db-query", JSON.stringify({
|
|
8118
|
+
action: "add",
|
|
8119
|
+
dbId: body.db,
|
|
8120
|
+
schema: r.schema,
|
|
8121
|
+
id: entry.id
|
|
8122
|
+
}));
|
|
7683
8123
|
}
|
|
7684
8124
|
return json(response);
|
|
7685
8125
|
} catch (err) {
|
|
@@ -7690,6 +8130,7 @@ async function handleQuery(cwd, req, sendSse, omitDirNames) {
|
|
|
7690
8130
|
const elapsed = Date.now() - start;
|
|
7691
8131
|
const response = {
|
|
7692
8132
|
dbId: body.db,
|
|
8133
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7693
8134
|
columns: [],
|
|
7694
8135
|
columnTypes: [],
|
|
7695
8136
|
rows: [],
|
|
@@ -7703,11 +8144,20 @@ async function handleQuery(cwd, req, sendSse, omitDirNames) {
|
|
|
7703
8144
|
}
|
|
7704
8145
|
function handleHistory(cwd, url) {
|
|
7705
8146
|
const dbId = url.searchParams.get("db") || undefined;
|
|
8147
|
+
const schema = normalizeSchemaParam(url.searchParams.get("schema"));
|
|
8148
|
+
if (schema instanceof Response)
|
|
8149
|
+
return schema;
|
|
7706
8150
|
const state = loadQueryHistory(cwd);
|
|
7707
8151
|
if (dbId) {
|
|
7708
8152
|
return json({
|
|
7709
8153
|
version: 1,
|
|
7710
|
-
entries: state.entries.filter((e) =>
|
|
8154
|
+
entries: state.entries.filter((e) => {
|
|
8155
|
+
if (e.dbId !== dbId)
|
|
8156
|
+
return false;
|
|
8157
|
+
if (schema === undefined)
|
|
8158
|
+
return true;
|
|
8159
|
+
return (e.schema || "public") === schema;
|
|
8160
|
+
})
|
|
7711
8161
|
});
|
|
7712
8162
|
}
|
|
7713
8163
|
return json(state);
|
|
@@ -7722,7 +8172,12 @@ async function handleHistoryDelete(cwd, req, sendSse) {
|
|
|
7722
8172
|
const deleted = state.entries.find((entry) => entry.id === body.id);
|
|
7723
8173
|
const updated = deleteQueryHistoryEntry(state, body.id);
|
|
7724
8174
|
saveQueryHistory(cwd, updated);
|
|
7725
|
-
sendSse?.("db-query", JSON.stringify({
|
|
8175
|
+
sendSse?.("db-query", JSON.stringify({
|
|
8176
|
+
action: "delete",
|
|
8177
|
+
dbId: deleted?.dbId,
|
|
8178
|
+
schema: deleted?.schema,
|
|
8179
|
+
id: body.id
|
|
8180
|
+
}));
|
|
7726
8181
|
return json({ ok: true });
|
|
7727
8182
|
}
|
|
7728
8183
|
async function handleHistoryClear(cwd, req, sendSse) {
|
|
@@ -7735,9 +8190,12 @@ async function handleHistoryClear(cwd, req, sendSse) {
|
|
|
7735
8190
|
body = {};
|
|
7736
8191
|
}
|
|
7737
8192
|
const state = loadQueryHistory(cwd);
|
|
7738
|
-
const
|
|
8193
|
+
const schema = normalizeSchemaParam(body.schema);
|
|
8194
|
+
if (schema instanceof Response)
|
|
8195
|
+
return schema;
|
|
8196
|
+
const updated = clearQueryHistory(state, body.db, schema);
|
|
7739
8197
|
saveQueryHistory(cwd, updated);
|
|
7740
|
-
sendSse?.("db-query", JSON.stringify({ action: "clear", dbId: body.db }));
|
|
8198
|
+
sendSse?.("db-query", JSON.stringify({ action: "clear", dbId: body.db, schema }));
|
|
7741
8199
|
return json({ ok: true });
|
|
7742
8200
|
}
|
|
7743
8201
|
function formatCsvField(value) {
|
|
@@ -7755,7 +8213,7 @@ function formatCsvField(value) {
|
|
|
7755
8213
|
return str;
|
|
7756
8214
|
}
|
|
7757
8215
|
async function handleExport(cwd, url, omitDirNames) {
|
|
7758
|
-
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames);
|
|
8216
|
+
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"));
|
|
7759
8217
|
if (r instanceof Response)
|
|
7760
8218
|
return r;
|
|
7761
8219
|
const table = url.searchParams.get("table");
|
|
@@ -7856,7 +8314,7 @@ async function handleExport(cwd, url, omitDirNames) {
|
|
|
7856
8314
|
}
|
|
7857
8315
|
}
|
|
7858
8316
|
async function handleColumns(cwd, url, omitDirNames) {
|
|
7859
|
-
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames);
|
|
8317
|
+
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"));
|
|
7860
8318
|
if (r instanceof Response)
|
|
7861
8319
|
return r;
|
|
7862
8320
|
const table = url.searchParams.get("table");
|
|
@@ -7865,13 +8323,18 @@ async function handleColumns(cwd, url, omitDirNames) {
|
|
|
7865
8323
|
try {
|
|
7866
8324
|
const adapter = await getAdapter(r, cwd);
|
|
7867
8325
|
const columns = adapter.getColumns(table);
|
|
7868
|
-
return json({
|
|
8326
|
+
return json({
|
|
8327
|
+
dbId: r.dbId,
|
|
8328
|
+
...r.schema ? { schema: r.schema } : {},
|
|
8329
|
+
table,
|
|
8330
|
+
columns
|
|
8331
|
+
});
|
|
7869
8332
|
} catch (err) {
|
|
7870
8333
|
return handleError("database", "get columns", err);
|
|
7871
8334
|
}
|
|
7872
8335
|
}
|
|
7873
8336
|
async function handleDdl(cwd, url, omitDirNames) {
|
|
7874
|
-
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames);
|
|
8337
|
+
const r = resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"));
|
|
7875
8338
|
if (r instanceof Response)
|
|
7876
8339
|
return r;
|
|
7877
8340
|
const table = url.searchParams.get("table");
|
|
@@ -7881,7 +8344,13 @@ async function handleDdl(cwd, url, omitDirNames) {
|
|
|
7881
8344
|
const adapter = await getAdapter(r, cwd);
|
|
7882
8345
|
const sql = adapter.getCreateStatement(table);
|
|
7883
8346
|
const triggers = adapter.getTriggers(table);
|
|
7884
|
-
return json({
|
|
8347
|
+
return json({
|
|
8348
|
+
dbId: r.dbId,
|
|
8349
|
+
...r.schema ? { schema: r.schema } : {},
|
|
8350
|
+
table,
|
|
8351
|
+
sql,
|
|
8352
|
+
triggers
|
|
8353
|
+
});
|
|
7885
8354
|
} catch (err) {
|
|
7886
8355
|
return handleError("database", "get DDL", err);
|
|
7887
8356
|
}
|
|
@@ -7892,7 +8361,7 @@ async function handleSearchStart(cwd, req, omitDirNames) {
|
|
|
7892
8361
|
return body;
|
|
7893
8362
|
if (!body.db || !body.term)
|
|
7894
8363
|
return textError("missing db or term", 400);
|
|
7895
|
-
const r = resolveDb(cwd, body.db, omitDirNames);
|
|
8364
|
+
const r = resolveDb(cwd, body.db, omitDirNames, body.schema);
|
|
7896
8365
|
if (r instanceof Response)
|
|
7897
8366
|
return r;
|
|
7898
8367
|
const jobId = makeId("search");
|
|
@@ -7900,6 +8369,7 @@ async function handleSearchStart(cwd, req, omitDirNames) {
|
|
|
7900
8369
|
const job = {
|
|
7901
8370
|
id: jobId,
|
|
7902
8371
|
dbId: body.db,
|
|
8372
|
+
...r.schema ? { schema: r.schema } : {},
|
|
7903
8373
|
scannedTables: 0,
|
|
7904
8374
|
totalTables: 0,
|
|
7905
8375
|
hits: [],
|
|
@@ -7937,7 +8407,10 @@ async function handleSearchStart(cwd, req, omitDirNames) {
|
|
|
7937
8407
|
const pkCols = getPrimaryKeyColumns(adapter, table);
|
|
7938
8408
|
const columns = adapter.getColumns(table);
|
|
7939
8409
|
const hits = searchTable(adapter, table, columns, term, maxHitsPerTable, includeNonText, pkCols);
|
|
7940
|
-
job.hits.push(...hits)
|
|
8410
|
+
job.hits.push(...hits.map((hit) => ({
|
|
8411
|
+
...r.schema ? { schema: r.schema } : {},
|
|
8412
|
+
...hit
|
|
8413
|
+
})));
|
|
7941
8414
|
job.scannedTables++;
|
|
7942
8415
|
}
|
|
7943
8416
|
job.done = true;
|
|
@@ -7961,6 +8434,7 @@ function handleSearchStatus(url) {
|
|
|
7961
8434
|
const result = {
|
|
7962
8435
|
jobId: job.id,
|
|
7963
8436
|
dbId: job.dbId,
|
|
8437
|
+
schema: job.schema,
|
|
7964
8438
|
scannedTables: job.scannedTables,
|
|
7965
8439
|
totalTables: job.totalTables,
|
|
7966
8440
|
currentTable: job.currentTable,
|
|
@@ -7992,7 +8466,10 @@ async function openRegisteredDockerSnapshotSource(info, requestedContainers) {
|
|
|
7992
8466
|
}
|
|
7993
8467
|
async function handleSnapshotList(cwd, url) {
|
|
7994
8468
|
const dbId = url.searchParams.get("db") || undefined;
|
|
7995
|
-
const
|
|
8469
|
+
const schema = normalizeSchemaParam(url.searchParams.get("schema"));
|
|
8470
|
+
if (schema instanceof Response)
|
|
8471
|
+
return schema;
|
|
8472
|
+
const snapshots = await listSnapshots(cwd, dbId, schema);
|
|
7996
8473
|
return json({ snapshots });
|
|
7997
8474
|
}
|
|
7998
8475
|
function sanitizeSnapshotTables(tables) {
|
|
@@ -8043,10 +8520,11 @@ async function handleSnapshotCreate(cwd, req, sendSse, omitDirNames) {
|
|
|
8043
8520
|
}
|
|
8044
8521
|
}
|
|
8045
8522
|
if (!source) {
|
|
8046
|
-
const r = resolveDb(cwd, body.db, omitDirNames);
|
|
8523
|
+
const r = resolveDb(cwd, body.db, omitDirNames, body.schema);
|
|
8047
8524
|
if (r instanceof Response)
|
|
8048
8525
|
return r;
|
|
8049
8526
|
source = await getAdapter(r, cwd);
|
|
8527
|
+
body.schema = r.schema;
|
|
8050
8528
|
}
|
|
8051
8529
|
if (!containers || containers.length === 0) {
|
|
8052
8530
|
const sqlAdapter = source;
|
|
@@ -8073,11 +8551,13 @@ async function handleSnapshotCreate(cwd, req, sendSse, omitDirNames) {
|
|
|
8073
8551
|
sendSse?.("db-snapshot", JSON.stringify({
|
|
8074
8552
|
action: "progress",
|
|
8075
8553
|
dbId: snapshotDbId,
|
|
8554
|
+
schema: body.schema,
|
|
8076
8555
|
table,
|
|
8077
8556
|
done
|
|
8078
8557
|
}));
|
|
8079
8558
|
}, {
|
|
8080
8559
|
signal: abortController.signal,
|
|
8560
|
+
schema: body.schema,
|
|
8081
8561
|
onSnapshotId: (id) => {
|
|
8082
8562
|
activeSnapshotId = id;
|
|
8083
8563
|
snapshotJob.snapshotId = id;
|
|
@@ -8085,6 +8565,7 @@ async function handleSnapshotCreate(cwd, req, sendSse, omitDirNames) {
|
|
|
8085
8565
|
sendSse?.("db-snapshot", JSON.stringify({
|
|
8086
8566
|
action: "started",
|
|
8087
8567
|
dbId: snapshotDbId,
|
|
8568
|
+
schema: body.schema,
|
|
8088
8569
|
id
|
|
8089
8570
|
}));
|
|
8090
8571
|
}
|
|
@@ -8092,6 +8573,7 @@ async function handleSnapshotCreate(cwd, req, sendSse, omitDirNames) {
|
|
|
8092
8573
|
sendSse?.("db-snapshot", JSON.stringify({
|
|
8093
8574
|
action: "created",
|
|
8094
8575
|
dbId: snapshotDbId,
|
|
8576
|
+
schema: body.schema,
|
|
8095
8577
|
id: snapshotId
|
|
8096
8578
|
}));
|
|
8097
8579
|
} catch (err) {
|
|
@@ -8099,6 +8581,7 @@ async function handleSnapshotCreate(cwd, req, sendSse, omitDirNames) {
|
|
|
8099
8581
|
sendSse?.("db-snapshot", JSON.stringify({
|
|
8100
8582
|
action: "error",
|
|
8101
8583
|
dbId: snapshotDbId,
|
|
8584
|
+
schema: body.schema,
|
|
8102
8585
|
error: err instanceof Error ? err.message : String(err)
|
|
8103
8586
|
}));
|
|
8104
8587
|
} finally {
|
|
@@ -8223,6 +8706,7 @@ async function handleClose(cwd, req, omitDirNames) {
|
|
|
8223
8706
|
const info = findDockerServiceByDbId(cwd, body.db, undefined, omitDirNames);
|
|
8224
8707
|
if (!info) {
|
|
8225
8708
|
dockerAdapterCache.close(body.db);
|
|
8709
|
+
dockerAdapterCache.closePrefix(`${body.db}\x00`);
|
|
8226
8710
|
closeRedisAdapter(body.db);
|
|
8227
8711
|
closeElasticsearchAdapter(body.db);
|
|
8228
8712
|
return json({ ok: true });
|
|
@@ -8235,6 +8719,7 @@ async function handleClose(cwd, req, omitDirNames) {
|
|
|
8235
8719
|
return r;
|
|
8236
8720
|
if (r.docker) {
|
|
8237
8721
|
dockerAdapterCache.close(r.dbId);
|
|
8722
|
+
dockerAdapterCache.closePrefix(`${r.dbId}\x00`);
|
|
8238
8723
|
} else {
|
|
8239
8724
|
closeConnection(r.resolved);
|
|
8240
8725
|
}
|
|
@@ -8267,6 +8752,10 @@ async function handleDatabaseRoute(req, url, cwd, omitDirNames, sideEffectAllowe
|
|
|
8267
8752
|
methods: ["GET"],
|
|
8268
8753
|
handler: () => handleFiles(cwd, omitDirNames)
|
|
8269
8754
|
},
|
|
8755
|
+
"/_db/schemas": {
|
|
8756
|
+
methods: ["GET"],
|
|
8757
|
+
handler: () => handleSchemas(cwd, url, omitDirNames)
|
|
8758
|
+
},
|
|
8270
8759
|
"/_db/schema": {
|
|
8271
8760
|
methods: ["GET"],
|
|
8272
8761
|
handler: () => handleSchema(cwd, url, omitDirNames)
|
|
@@ -8364,7 +8853,7 @@ async function handleDatabaseRoute(req, url, cwd, omitDirNames, sideEffectAllowe
|
|
|
8364
8853
|
}
|
|
8365
8854
|
}, sideEffectAllowed, wrapResponse, (err) => handleError("database", "handle database request", err));
|
|
8366
8855
|
}
|
|
8367
|
-
var initialized = false, dockerAdapterCache, EXPORT_MAX_ROWS = 1e5, MAX_TABS_BODY_BYTES = 1e6, MAX_SNAPSHOT_TABLES = 512, MAX_SNAPSHOT_TABLE_NAME_LEN = 1024, searchJobs, snapshotJobs, DOCKER_CLOSE_REGISTRY, SNAPSHOT_DOCKER_SOURCE_REGISTRY;
|
|
8856
|
+
var initialized = false, dockerAdapterCache, MAX_SCHEMA_NAME_LEN = 1024, EXPORT_MAX_ROWS = 1e5, MAX_TABS_BODY_BYTES = 1e6, MAX_SNAPSHOT_TABLES = 512, MAX_SNAPSHOT_TABLE_NAME_LEN = 1024, searchJobs, snapshotJobs, DOCKER_CLOSE_REGISTRY, SNAPSHOT_DOCKER_SOURCE_REGISTRY;
|
|
8368
8857
|
var init_handle = __esm(() => {
|
|
8369
8858
|
init_docker();
|
|
8370
8859
|
init_docker_utils();
|
|
@@ -8384,8 +8873,14 @@ var init_handle = __esm(() => {
|
|
|
8384
8873
|
searchJobs = new Map;
|
|
8385
8874
|
snapshotJobs = new Map;
|
|
8386
8875
|
DOCKER_CLOSE_REGISTRY = {
|
|
8387
|
-
postgresql: (dbId) =>
|
|
8388
|
-
|
|
8876
|
+
postgresql: (dbId) => {
|
|
8877
|
+
dockerAdapterCache.close(dbId);
|
|
8878
|
+
dockerAdapterCache.closePrefix(`${dbId}\x00`);
|
|
8879
|
+
},
|
|
8880
|
+
mysql: (dbId) => {
|
|
8881
|
+
dockerAdapterCache.close(dbId);
|
|
8882
|
+
dockerAdapterCache.closePrefix(`${dbId}\x00`);
|
|
8883
|
+
},
|
|
8389
8884
|
redis: closeRedisAdapter,
|
|
8390
8885
|
elasticsearch: closeElasticsearchAdapter
|
|
8391
8886
|
};
|
|
@@ -9572,7 +10067,7 @@ function rawFileHeaders(path, size = null, range, metadata = {}) {
|
|
|
9572
10067
|
".opus": "audio/ogg"
|
|
9573
10068
|
};
|
|
9574
10069
|
const headers = {
|
|
9575
|
-
"Content-Type": mime[extname(path).toLowerCase()] || "application/octet-stream",
|
|
10070
|
+
"Content-Type": mime[extname(path).toLowerCase()] || (sourceDisplayKind(path) === "text" ? "text/plain; charset=utf-8" : "application/octet-stream"),
|
|
9576
10071
|
"Cache-Control": "no-store",
|
|
9577
10072
|
"X-Content-Type-Options": "nosniff",
|
|
9578
10073
|
"Content-Security-Policy": "sandbox",
|
|
@@ -10219,6 +10714,7 @@ async function shutdown(exitCode = 0) {
|
|
|
10219
10714
|
var WEB_ROOT, VERSION, DEFAULT_ARGS, PREVIEW_HUNKS_DEFAULT = 3, PREVIEW_LINES_DEFAULT = 1200, WATCHED_ASSET_FILES, SIZE_SMALL = 2000, SIZE_MEDIUM = 8000, SIZE_LARGE = 20000, LINE_INDEX_MIN_START = 1e4, LINE_INDEX_MAX_FILE_BYTES, BLOB_LINE_CACHE_MAX_BYTES, MAX_UPLOAD_FILE_BYTES, MAX_UPLOAD_TOTAL_BYTES, MAX_UPLOAD_BODY_BYTES, MAX_UPLOAD_FILES = 50, SAFE_UPLOAD_EXTENSIONS, generation = 1, cwd, cliArgs, listenPort = 0, openAfterStart = false, scopeOmitDirNames, scopeOmitDirCliOverride = null, scopeExcludeNames, uploadDisabledByConfig = false, rgAvailableCache = null, enc, sseClients, sseKeepalives, fileCache, metaCache, fileListCache, lineIndexCache, blobLineIndexCache, blobBytesCache, blobLineCacheBytes = 0, server, worktreeWatch = null, shuttingDown = false;
|
|
10220
10715
|
var init_preview = __esm(async () => {
|
|
10221
10716
|
init_routes();
|
|
10717
|
+
init_source_meta();
|
|
10222
10718
|
init_annotations();
|
|
10223
10719
|
init_cache();
|
|
10224
10720
|
init_dev_assets();
|