@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.
@@ -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
- 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 = 'public' AND tc.table_name = '${tableLiteral}' AND tc.constraint_type = 'PRIMARY KEY') pk ON pk.column_name = c.column_name WHERE c.table_schema = 'public' AND c.table_name = '${tableLiteral}' ORDER BY c.ordinal_position`;
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
- let sql;
3954
- if (config.kind === "postgresql") {
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 = 'public' ORDER BY table_name`;
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 = 'public' AND indexname NOT LIKE 'pg_%' ORDER BY indexname`;
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 = 'public'`;
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 = 'public' AND table_name IN (${inList}) ORDER BY table_name, ordinal_position`;
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 = sanitizeIdentifier(table, config.kind);
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 = sanitizeIdentifier(t, config.kind);
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 = sanitizeIdentifier(t, config.kind);
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 = sanitizeIdentifier(table, config.kind);
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 = sanitizeIdentifier(table, config.kind);
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 = sanitizeIdentifier(table, config.kind);
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 = `${readOnlyPreamble}${stripped} LIMIT ${maxRows}${readOnlyPostamble}`;
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 ${sanitizeIdentifier(table, config.kind)}`);
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 ' || '${table.replace(/'/g, "''")}' || ' (...)' AS ddl`);
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 = '${table.replace(/'/g, "''")}'::regclass AND NOT tgisinternal`;
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 setDockerDatabasesCache(cacheKey, [], DOCKER_DATABASES_NEGATIVE_TTL_MS, now);
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 openDockerAdapter(serviceName, kind, env, cwd, overrideDatabase) {
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) => e.dbId !== dbId)
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
- return dockerAdapterCache.getOrOpen(r.dbId, () => openDockerAdapter(docker.serviceName, docker.kind, docker.env, docker.composeDir, docker.database));
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 resolveDb(cwd, dbParam, omitDirNames) {
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
- return { resolved: dbParam, dbId: dbParam, docker: resolved2 };
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({ action: "add", dbId: body.db, id: entry.id }));
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) => e.dbId === dbId)
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({ action: "delete", dbId: deleted?.dbId, id: body.id }));
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 updated = clearQueryHistory(state, body.db);
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({ dbId: r.dbId, table, columns });
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({ dbId: r.dbId, table, sql, triggers });
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 snapshots = await listSnapshots(cwd, dbId);
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) => dockerAdapterCache.close(dbId),
8388
- mysql: (dbId) => dockerAdapterCache.close(dbId),
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();