@rex0220/kintone-sql-tools 1.12.1 → 1.13.2

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.
@@ -37441,7 +37441,7 @@ function buildInsertPlan(stmt, label) {
37441
37441
  const lines = [];
37442
37442
  if (label) lines.push(label);
37443
37443
  lines.push(` [INSERT]`);
37444
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
37444
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
37445
37445
  lines.push(` records: ${totalRows} \u4EF6\uFF08\u30D0\u30C3\u30C1 ${batchCount} \u56DE \xD7 \u6700\u5927 100 \u4EF6\uFF09`);
37446
37446
  lines.push(` api: POST /k/v1/records.json \xD7 ${batchCount}`);
37447
37447
  lines.push(` fields: ${stmt.fields.join(", ")}`);
@@ -37451,7 +37451,7 @@ function buildInsertSelectPlan(stmt, label) {
37451
37451
  const lines = [];
37452
37452
  if (label) lines.push(label);
37453
37453
  lines.push(` [INSERT SELECT]`);
37454
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
37454
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
37455
37455
  lines.push(` fields: ${stmt.fields.join(", ")}`);
37456
37456
  lines.push(` api: POST /k/v1/records.json\uFF08\u4EF6\u6570\u306F SELECT \u7D50\u679C\u306B\u4F9D\u5B58\u3001100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`);
37457
37457
  lines.push("");
@@ -37464,7 +37464,7 @@ function buildUpdatePlan(stmt, label) {
37464
37464
  const lines = [];
37465
37465
  if (label) lines.push(label);
37466
37466
  lines.push(` [UPDATE]`);
37467
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
37467
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
37468
37468
  lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
37469
37469
  lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
37470
37470
  const setTypes = [];
@@ -37494,7 +37494,7 @@ function buildDeletePlan(stmt, label) {
37494
37494
  const lines = [];
37495
37495
  if (label) lines.push(label);
37496
37496
  lines.push(` [DELETE]`);
37497
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
37497
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
37498
37498
  lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
37499
37499
  lines.push(` api: GET /k/v1/records.json \u2192 DELETE /k/v1/records.json`);
37500
37500
  return lines;
@@ -37505,7 +37505,7 @@ function buildUpsertPlan(stmt, label) {
37505
37505
  return [
37506
37506
  ...label ? [label] : [],
37507
37507
  ` [UPSERT]`,
37508
- ` app: APP${stmt.appId} (${stmt.appId})`,
37508
+ ` target: APP${stmt.appId} (${stmt.appId})`,
37509
37509
  ` records: ${totalRows} \u4EF6\uFF08\u30D0\u30C3\u30C1 ${batchCount} \u56DE \xD7 \u6700\u5927 100 \u4EF6\uFF09`,
37510
37510
  ` key fields: ${stmt.keyFields.join(", ")}`,
37511
37511
  ` fields: ${stmt.fields.join(", ")}`,
@@ -37516,7 +37516,7 @@ function buildUpsertSelectPlan(stmt, label) {
37516
37516
  const lines = [
37517
37517
  ...label ? [label] : [],
37518
37518
  ` [UPSERT SELECT]`,
37519
- ` app: APP${stmt.appId} (${stmt.appId})`,
37519
+ ` target: APP${stmt.appId} (${stmt.appId})`,
37520
37520
  ` key fields: ${stmt.keyFields.join(", ")}`,
37521
37521
  ` fields: ${stmt.fields.join(", ")}`,
37522
37522
  ` api: GET /k/v1/records.json\uFF08\u91CD\u8907\u5224\u5B9A\uFF09\u2192 POST \u307E\u305F\u306F PUT /k/v1/records.json\uFF08100 \u4EF6\u3054\u3068\u306B\u30D0\u30C3\u30C1\uFF09`,
@@ -37532,7 +37532,7 @@ function buildReorderPlan(stmt, label) {
37532
37532
  const lines = [
37533
37533
  ...label ? [label] : [],
37534
37534
  ` [REORDER]`,
37535
- ` app: APP${stmt.appId} (${stmt.appId})`,
37535
+ ` target: APP${stmt.appId} (${stmt.appId})`,
37536
37536
  ` table: ${target}`,
37537
37537
  ` scope: ${scope}`,
37538
37538
  ` by: ${byStr}`,
@@ -37666,195 +37666,177 @@ function buildBatchEnvelope(batch, options = {}) {
37666
37666
  };
37667
37667
  }
37668
37668
 
37669
- // src/node/appProfiles.ts
37670
- function parseTokenMap(raw) {
37671
- const out = {};
37672
- if (!raw.trim()) return out;
37673
- const pairs = raw.split(",");
37674
- for (const pair of pairs) {
37675
- const idx = pair.indexOf("=");
37676
- if (idx <= 0) throw new Error("ArgumentError: --token-map must be APPxxx=token pairs.");
37677
- const key = normalizeAppKey(pair.slice(0, idx).trim());
37678
- const value = pair.slice(idx + 1).trim();
37679
- if (!value) throw new Error(`ArgumentError: token is empty for ${key}.`);
37680
- out[key] = value;
37669
+ // src/node/sqlDiagnostics.ts
37670
+ function restoreSqlDiagnosticValue(value, bindings) {
37671
+ if (typeof value === "string") {
37672
+ const dmlTarget = value.match(/^(\s*target:\s*)APP(\d+) \((\d+)\)\s*$/);
37673
+ if (dmlTarget && dmlTarget[2] === dmlTarget[3]) {
37674
+ const binding = bindings.get(Number(dmlTarget[2]));
37675
+ if (binding) {
37676
+ const target = binding.source === "logical" ? `LAPP_${binding.logicalName} -> APP${binding.appId}@${binding.profile}` : `APP${binding.appId}@${binding.profile}`;
37677
+ return `${dmlTarget[1]}${target}`;
37678
+ }
37679
+ }
37680
+ let restored = value;
37681
+ for (const binding of bindings.values()) {
37682
+ const internal = `APP${binding.mappedAppId}`;
37683
+ const display = binding.source === "logical" ? `LAPP_${binding.logicalName}@${binding.profile}` : `APP${binding.appId}@${binding.profile}`;
37684
+ restored = restored.split(`${internal} (${binding.mappedAppId})`).join(display).split(internal).join(display);
37685
+ }
37686
+ return restored;
37681
37687
  }
37682
- return out;
37683
- }
37684
- function normalizeAppKey(v) {
37685
- const m1 = v.match(/^APP(\d+)$/i);
37686
- if (m1) return `APP${m1[1]}`;
37687
- const m2 = v.match(/^(\d+)$/);
37688
- if (m2) return `APP${m2[1]}`;
37689
- throw new Error(`ArgumentError: invalid app key "${v}"`);
37690
- }
37691
- function extractAppIds(sql) {
37692
- const out = /* @__PURE__ */ new Set();
37693
- for (const t of collectAppProfileTokens(sql)) out.add(t.appId);
37694
- return [...out];
37695
- }
37696
- function isSqlIdentContinue(ch) {
37697
- if (!ch) return false;
37698
- const cp = ch.codePointAt(0);
37699
- return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || cp === 95 || cp === 36 || cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
37700
- }
37701
- function isProfileNameChar(ch) {
37702
- if (!ch) return false;
37703
- const cp = ch.codePointAt(0);
37704
- return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || ch === "_" || ch === "-" || ch === "." || ch === "$";
37705
- }
37706
- function tryParseAppProfileToken(sql, start) {
37707
- const head = sql.slice(start, start + 3);
37708
- if (head.toUpperCase() !== "APP") return null;
37709
- const prev = start > 0 ? sql[start - 1] : "";
37710
- if (isSqlIdentContinue(prev)) return null;
37711
- let i = start + 3;
37712
- const digitStart = i;
37713
- while (i < sql.length && /[0-9]/.test(sql[i])) i++;
37714
- const digitEnd = i;
37715
- if (digitEnd === digitStart) return null;
37716
- if (sql[i] === "$") {
37717
- i++;
37718
- const subStart = i;
37719
- while (i < sql.length && isSqlIdentContinue(sql[i])) i++;
37720
- if (i === subStart) return null;
37688
+ if (Array.isArray(value)) {
37689
+ return value.map((item) => restoreSqlDiagnosticValue(item, bindings));
37721
37690
  }
37722
- const appEnd = i;
37723
- let profile2 = null;
37724
- if (sql[i] === "@") {
37725
- i++;
37726
- const pStart = i;
37727
- while (i < sql.length && isProfileNameChar(sql[i])) i++;
37728
- if (i === pStart) return null;
37729
- profile2 = sql.slice(pStart, i);
37691
+ if (value !== null && typeof value === "object") {
37692
+ return Object.fromEntries(
37693
+ Object.entries(value).map(([key, item]) => [key, restoreSqlDiagnosticValue(item, bindings)])
37694
+ );
37730
37695
  }
37731
- const next = i < sql.length ? sql[i] : "";
37732
- if (isSqlIdentContinue(next)) return null;
37733
- return {
37734
- appId: Number(sql.slice(digitStart, digitEnd)),
37735
- profile: profile2,
37736
- start,
37737
- digitStart,
37738
- digitEnd,
37739
- appEnd,
37740
- fullEnd: i
37741
- };
37696
+ return value;
37742
37697
  }
37743
- function collectAppProfileTokens(sql) {
37744
- const tokens = [];
37745
- let i = 0;
37746
- while (i < sql.length) {
37747
- const ch = sql[i];
37748
- if (ch === "'") {
37749
- i++;
37750
- while (i < sql.length) {
37751
- if (sql[i] === "'") {
37752
- i++;
37753
- if (i < sql.length && sql[i] === "'") {
37754
- i++;
37755
- continue;
37756
- }
37757
- break;
37758
- }
37759
- i++;
37698
+ function restoreSqlContextError(err, sourceSql, context) {
37699
+ if (!(err instanceof Error)) return err;
37700
+ let message = err.message;
37701
+ for (const binding of context.bindings.values()) {
37702
+ if (binding.source === "logical") {
37703
+ message = message.split(`APP${binding.mappedAppId}`).join(`LAPP_${binding.logicalName}@${binding.profile}`);
37704
+ }
37705
+ }
37706
+ const token = err.token;
37707
+ if (typeof token?.pos === "number") {
37708
+ const segment = context.rewriteSegments.find(
37709
+ (candidate) => token.pos >= candidate.normalizedStart && token.pos < candidate.normalizedEnd
37710
+ );
37711
+ if (segment) {
37712
+ const sourcePos = segment.sourceStart + Math.min(
37713
+ token.pos - segment.normalizedStart,
37714
+ Math.max(0, segment.sourceEnd - segment.sourceStart - 1)
37715
+ );
37716
+ message = message.replace(/(位置 \d+、トークン:/, `\uFF08\u4F4D\u7F6E ${sourcePos}\u3001\u30C8\u30FC\u30AF\u30F3:`);
37717
+ const originalRef = sourceSql.slice(segment.sourceStart, segment.sourceEnd);
37718
+ if (segment.bindingMappedAppId !== void 0 && originalRef) {
37719
+ message = message.replace(/(トークン: 「)[^」]*(」)/, `$1${originalRef}$2`);
37760
37720
  }
37761
- continue;
37762
37721
  }
37763
- if (ch === "`") {
37764
- i++;
37765
- while (i < sql.length && sql[i] !== "`") i++;
37766
- if (i < sql.length) i++;
37767
- continue;
37722
+ }
37723
+ err.message = message;
37724
+ return err;
37725
+ }
37726
+
37727
+ // src/node/config.ts
37728
+ var import_fs = require("fs");
37729
+ var LOGICAL_APP_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
37730
+ var PHYSICAL_APP_KEY_RE = /^APP\d+$/i;
37731
+ var NUMERIC_APP_KEY_RE = /^\d+$/;
37732
+ var LOGICAL_SQL_KEY_RE = /^LAPP_/i;
37733
+ function argumentError(message) {
37734
+ return new Error(`ArgumentError: ${message}`);
37735
+ }
37736
+ function normalizeLogicalApps(profileName, value) {
37737
+ if (value === void 0) return void 0;
37738
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
37739
+ throw argumentError(`logicalApps for profile "${profileName}" must be an object.`);
37740
+ }
37741
+ const normalized = {};
37742
+ const physicalIdOwners = /* @__PURE__ */ new Map();
37743
+ for (const [rawName, rawAppId] of Object.entries(value)) {
37744
+ if (PHYSICAL_APP_KEY_RE.test(rawName) || NUMERIC_APP_KEY_RE.test(rawName) || LOGICAL_SQL_KEY_RE.test(rawName)) {
37745
+ throw argumentError(
37746
+ `logical app key "${rawName}" in profile "${profileName}" must be a logical name without APP, numeric, or LAPP_ syntax.`
37747
+ );
37768
37748
  }
37769
- if (ch === "-" && sql[i + 1] === "-") {
37770
- i += 2;
37771
- while (i < sql.length && sql[i] !== "\n") i++;
37772
- continue;
37749
+ if (!LOGICAL_APP_NAME_RE.test(rawName)) {
37750
+ throw argumentError(
37751
+ `logical app key "${rawName}" in profile "${profileName}" must match [A-Z][A-Z0-9_]{0,63}.`
37752
+ );
37773
37753
  }
37774
- if (ch === "/" && sql[i + 1] === "*") {
37775
- i += 2;
37776
- while (i < sql.length) {
37777
- if (sql[i] === "*" && sql[i + 1] === "/") {
37778
- i += 2;
37779
- break;
37780
- }
37781
- i++;
37782
- }
37783
- continue;
37754
+ const logicalName = rawName.toUpperCase();
37755
+ if (Object.prototype.hasOwnProperty.call(normalized, logicalName)) {
37756
+ throw argumentError(
37757
+ `logical app name "${logicalName}" is duplicated after case normalization in profile "${profileName}".`
37758
+ );
37784
37759
  }
37785
- const parsed = tryParseAppProfileToken(sql, i);
37786
- if (!parsed) {
37787
- i++;
37788
- continue;
37760
+ if (typeof rawAppId !== "number" || !Number.isSafeInteger(rawAppId) || rawAppId <= 0) {
37761
+ throw argumentError(
37762
+ `physical app ID for logical app "${logicalName}" in profile "${profileName}" must be a positive safe integer.`
37763
+ );
37789
37764
  }
37790
- tokens.push(parsed);
37791
- i = parsed.fullEnd;
37765
+ const existingName = physicalIdOwners.get(rawAppId);
37766
+ if (existingName !== void 0) {
37767
+ throw argumentError(
37768
+ `logical apps "${existingName}" and "${logicalName}" in profile "${profileName}" map to the same physical app ID ${rawAppId}; physical app aliases are not supported yet.`
37769
+ );
37770
+ }
37771
+ physicalIdOwners.set(rawAppId, logicalName);
37772
+ normalized[logicalName] = rawAppId;
37792
37773
  }
37793
- return tokens;
37774
+ return normalized;
37794
37775
  }
37795
- function nextVirtualAppId(used) {
37796
- let id = 9e8;
37797
- while (used.has(id)) id++;
37798
- used.add(id);
37799
- return id;
37800
- }
37801
- function normalizeSqlAppProfiles(sql, defaultProfile = "dev") {
37802
- const tokens = collectAppProfileTokens(sql);
37803
- const hasProfileSyntax = tokens.some((t) => t.profile !== null);
37804
- const profilesByApp = /* @__PURE__ */ new Map();
37805
- const normalizedProfile = (profile2) => profile2 ?? defaultProfile;
37806
- for (const t of tokens) {
37807
- const p = normalizedProfile(t.profile);
37808
- let set2 = profilesByApp.get(t.appId);
37809
- if (!set2) {
37810
- set2 = /* @__PURE__ */ new Set();
37811
- profilesByApp.set(t.appId, set2);
37812
- }
37813
- set2.add(p.toLowerCase());
37776
+ function validateKsqlConfig(config2) {
37777
+ if (config2 === null || typeof config2 !== "object" || Array.isArray(config2)) {
37778
+ throw argumentError("config must be an object.");
37814
37779
  }
37815
- const usedAppIds = new Set(tokens.map((t) => t.appId));
37816
- const pairToMapped = /* @__PURE__ */ new Map();
37817
- const appBindingByMappedApp = /* @__PURE__ */ new Map();
37818
- for (const [appId, pSet] of profilesByApp.entries()) {
37819
- const profiles = [...pSet].sort();
37820
- if (profiles.length <= 1) continue;
37821
- for (const pLower of profiles) {
37822
- const mapped = nextVirtualAppId(usedAppIds);
37823
- pairToMapped.set(`${appId}@${pLower}`, mapped);
37824
- appBindingByMappedApp.set(mapped, { appId, profile: pLower });
37780
+ if (config2.profiles === void 0) return config2;
37781
+ if (config2.profiles === null || typeof config2.profiles !== "object" || Array.isArray(config2.profiles)) {
37782
+ throw argumentError("profiles must be an object.");
37783
+ }
37784
+ for (const [profileName, profile2] of Object.entries(config2.profiles)) {
37785
+ if (profile2 === null || typeof profile2 !== "object" || Array.isArray(profile2)) {
37786
+ throw argumentError(`profile "${profileName}" must be an object.`);
37787
+ }
37788
+ if (profile2.allowPhysicalAppRefs !== void 0 && typeof profile2.allowPhysicalAppRefs !== "boolean") {
37789
+ throw argumentError(`allowPhysicalAppRefs for profile "${profileName}" must be boolean.`);
37825
37790
  }
37791
+ const logicalApps = normalizeLogicalApps(profileName, profile2.logicalApps);
37792
+ if (logicalApps !== void 0) profile2.logicalApps = logicalApps;
37826
37793
  }
37827
- const out = [];
37828
- let cursor = 0;
37829
- for (const t of tokens) {
37830
- const p = normalizedProfile(t.profile);
37831
- const pLower = p.toLowerCase();
37832
- const mapped = pairToMapped.get(`${t.appId}@${pLower}`) ?? t.appId;
37833
- appBindingByMappedApp.set(mapped, { appId: t.appId, profile: pLower });
37834
- out.push(sql.slice(cursor, t.start));
37835
- out.push(sql.slice(t.start, t.digitStart));
37836
- out.push(String(mapped));
37837
- out.push(sql.slice(t.digitEnd, t.appEnd));
37838
- cursor = t.fullEnd;
37794
+ return config2;
37795
+ }
37796
+ function createAppResolutionContext(config2, defaultProfile) {
37797
+ const profiles = Object.fromEntries(
37798
+ Object.entries(config2.profiles ?? {}).map(([name, profile2]) => [
37799
+ name,
37800
+ {
37801
+ logicalApps: profile2.logicalApps === void 0 ? void 0 : { ...profile2.logicalApps },
37802
+ allowPhysicalAppRefs: profile2.allowPhysicalAppRefs
37803
+ }
37804
+ ])
37805
+ );
37806
+ const implicitDefaultProfile = {};
37807
+ function requireProfile(profileName) {
37808
+ const profile2 = profiles[profileName];
37809
+ if (!profile2 && profileName === defaultProfile) return implicitDefaultProfile;
37810
+ if (!profile2) throw argumentError(`profile "${profileName}" is not defined.`);
37811
+ return profile2;
37839
37812
  }
37840
- out.push(sql.slice(cursor));
37841
37813
  return {
37842
- normalizedSql: out.join(""),
37843
- hasProfileSyntax,
37844
- appBindingByMappedApp
37814
+ resolveLogicalApp(name, profile2) {
37815
+ if (!LOGICAL_APP_NAME_RE.test(name)) {
37816
+ throw argumentError(`logical app name "${name}" must match [A-Z][A-Z0-9_]{0,63}.`);
37817
+ }
37818
+ const profileName = profile2 || defaultProfile;
37819
+ const logicalName = name.toUpperCase();
37820
+ const appId = requireProfile(profileName).logicalApps?.[logicalName];
37821
+ if (appId === void 0) {
37822
+ throw argumentError(`logical app LAPP_${logicalName}@${profileName} is not defined.`);
37823
+ }
37824
+ return appId;
37825
+ },
37826
+ assertPhysicalAppAllowed(profile2) {
37827
+ const profileName = profile2 || defaultProfile;
37828
+ if (!profiles[profileName]) return;
37829
+ if (requireProfile(profileName).allowPhysicalAppRefs === false) {
37830
+ throw argumentError(
37831
+ `physical app references are not allowed for profile "${profileName}"; use LAPP_<NAME>.`
37832
+ );
37833
+ }
37834
+ }
37845
37835
  };
37846
37836
  }
37847
- function buildCacheContext(defaultProfile, appBindingByMappedApp) {
37848
- if (appBindingByMappedApp.size === 0) return `default:${defaultProfile.toLowerCase()}`;
37849
- const pairs = [...appBindingByMappedApp.entries()].sort((a, b) => a[0] - b[0]).map(([mappedAppId, b]) => `M${mappedAppId}=APP${b.appId}@${b.profile}`);
37850
- return `apps:${pairs.join(",")}`;
37851
- }
37852
-
37853
- // src/node/config.ts
37854
- var import_fs = require("fs");
37855
37837
  function loadKsqlConfig(configPath) {
37856
37838
  const raw = (0, import_fs.readFileSync)(configPath, "utf-8");
37857
- return JSON.parse(raw);
37839
+ return validateKsqlConfig(JSON.parse(raw));
37858
37840
  }
37859
37841
  function loadOptionalKsqlConfig(configPath) {
37860
37842
  if (!(0, import_fs.existsSync)(configPath)) return {};
@@ -38051,11 +38033,19 @@ function createNodeKintoneClient(baseUrl, tokenResolver) {
38051
38033
  );
38052
38034
  }
38053
38035
  }
38054
- const res = await fetch(url2, {
38055
- ...init,
38056
- headers,
38057
- signal: AbortSignal.timeout(timeoutMs)
38058
- });
38036
+ const controller = new AbortController();
38037
+ const timeout2 = setTimeout(() => controller.abort(), timeoutMs);
38038
+ timeout2.unref?.();
38039
+ let res;
38040
+ try {
38041
+ res = await fetch(url2, {
38042
+ ...init,
38043
+ headers,
38044
+ signal: controller.signal
38045
+ });
38046
+ } finally {
38047
+ clearTimeout(timeout2);
38048
+ }
38059
38049
  if (!res.ok) {
38060
38050
  const bodyText = await res.text();
38061
38051
  if (tokenResolver.debug) {
@@ -38223,17 +38213,376 @@ function detectSortKind(fieldType, calcFormat) {
38223
38213
  return void 0;
38224
38214
  }
38225
38215
 
38216
+ // src/node/appProfiles.ts
38217
+ function parseTokenMap(raw) {
38218
+ const out = {};
38219
+ if (!raw.trim()) return out;
38220
+ const pairs = raw.split(",");
38221
+ for (const pair of pairs) {
38222
+ const idx = pair.indexOf("=");
38223
+ if (idx <= 0) throw new Error("ArgumentError: --token-map must be APPxxx=token pairs.");
38224
+ const key = normalizeAppKey(pair.slice(0, idx).trim());
38225
+ const value = pair.slice(idx + 1).trim();
38226
+ if (!value) throw new Error(`ArgumentError: token is empty for ${key}.`);
38227
+ out[key] = value;
38228
+ }
38229
+ return out;
38230
+ }
38231
+ function normalizeAppKey(v) {
38232
+ const m1 = v.match(/^APP(\d+)$/i);
38233
+ if (m1) return `APP${m1[1]}`;
38234
+ const m2 = v.match(/^(\d+)$/);
38235
+ if (m2) return `APP${m2[1]}`;
38236
+ throw new Error(`ArgumentError: invalid app key "${v}"`);
38237
+ }
38238
+ function extractAppIds(sql) {
38239
+ const out = /* @__PURE__ */ new Set();
38240
+ for (const t of collectAppProfileTokens(sql)) {
38241
+ if (t.source === "physical") out.add(t.appId);
38242
+ }
38243
+ return [...out];
38244
+ }
38245
+ function isSqlIdentContinue(ch) {
38246
+ if (!ch) return false;
38247
+ const cp = ch.codePointAt(0);
38248
+ return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || cp === 95 || cp === 36 || cp >= 12352 && cp <= 12543 || cp >= 13312 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65281 && cp <= 65376;
38249
+ }
38250
+ function isProfileNameChar(ch) {
38251
+ if (!ch) return false;
38252
+ const cp = ch.codePointAt(0);
38253
+ return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || ch === "_" || ch === "-" || ch === "." || ch === "$";
38254
+ }
38255
+ function isAsciiLogicalNameStart(ch) {
38256
+ return /^[A-Za-z]$/.test(ch);
38257
+ }
38258
+ function isAsciiLogicalNameContinue(ch) {
38259
+ return /^[A-Za-z0-9_]$/.test(ch);
38260
+ }
38261
+ function tryParseAppProfileToken(sql, start) {
38262
+ const prev = start > 0 ? sql[start - 1] : "";
38263
+ if (isSqlIdentContinue(prev)) return null;
38264
+ let source;
38265
+ let appId;
38266
+ let logicalName;
38267
+ let referenceValueStart;
38268
+ let referenceValueEnd;
38269
+ let i;
38270
+ if (sql.slice(start, start + 5).toUpperCase() === "LAPP_") {
38271
+ source = "logical";
38272
+ i = start + 5;
38273
+ referenceValueStart = i;
38274
+ if (!isAsciiLogicalNameStart(sql[i] ?? "")) return null;
38275
+ i++;
38276
+ while (i < sql.length && isAsciiLogicalNameContinue(sql[i])) i++;
38277
+ referenceValueEnd = i;
38278
+ if (referenceValueEnd - referenceValueStart > 64) return null;
38279
+ logicalName = sql.slice(referenceValueStart, referenceValueEnd).toUpperCase();
38280
+ } else if (sql.slice(start, start + 3).toUpperCase() === "APP") {
38281
+ source = "physical";
38282
+ i = start + 3;
38283
+ referenceValueStart = i;
38284
+ while (i < sql.length && /[0-9]/.test(sql[i])) i++;
38285
+ referenceValueEnd = i;
38286
+ if (referenceValueEnd === referenceValueStart) return null;
38287
+ appId = Number(sql.slice(referenceValueStart, referenceValueEnd));
38288
+ } else {
38289
+ return null;
38290
+ }
38291
+ if (sql[i] === "$") {
38292
+ i++;
38293
+ const subStart = i;
38294
+ while (i < sql.length && isSqlIdentContinue(sql[i])) i++;
38295
+ if (i === subStart) return null;
38296
+ }
38297
+ const appEnd = i;
38298
+ let profile2 = null;
38299
+ if (sql[i] === "@") {
38300
+ i++;
38301
+ const pStart = i;
38302
+ while (i < sql.length && isProfileNameChar(sql[i])) i++;
38303
+ if (i === pStart) return null;
38304
+ profile2 = sql.slice(pStart, i);
38305
+ }
38306
+ const next = i < sql.length ? sql[i] : "";
38307
+ if (isSqlIdentContinue(next)) return null;
38308
+ const common = {
38309
+ profile: profile2,
38310
+ start,
38311
+ referenceValueStart,
38312
+ referenceValueEnd,
38313
+ appEnd,
38314
+ fullEnd: i
38315
+ };
38316
+ return source === "physical" ? { ...common, source, appId } : { ...common, source, logicalName };
38317
+ }
38318
+ function collectAppProfileTokens(sql) {
38319
+ const tokens = [];
38320
+ let i = 0;
38321
+ while (i < sql.length) {
38322
+ const ch = sql[i];
38323
+ if (ch === "'") {
38324
+ i++;
38325
+ while (i < sql.length) {
38326
+ if (sql[i] === "'") {
38327
+ i++;
38328
+ if (i < sql.length && sql[i] === "'") {
38329
+ i++;
38330
+ continue;
38331
+ }
38332
+ break;
38333
+ }
38334
+ i++;
38335
+ }
38336
+ continue;
38337
+ }
38338
+ if (ch === "`") {
38339
+ i++;
38340
+ while (i < sql.length && sql[i] !== "`") i++;
38341
+ if (i < sql.length) i++;
38342
+ continue;
38343
+ }
38344
+ if (ch === "-" && sql[i + 1] === "-") {
38345
+ i += 2;
38346
+ while (i < sql.length && sql[i] !== "\n") i++;
38347
+ continue;
38348
+ }
38349
+ if (ch === "/" && sql[i + 1] === "*") {
38350
+ i += 2;
38351
+ while (i < sql.length) {
38352
+ if (sql[i] === "*" && sql[i + 1] === "/") {
38353
+ i += 2;
38354
+ break;
38355
+ }
38356
+ i++;
38357
+ }
38358
+ continue;
38359
+ }
38360
+ const parsed = tryParseAppProfileToken(sql, i);
38361
+ if (!parsed) {
38362
+ i++;
38363
+ continue;
38364
+ }
38365
+ tokens.push(parsed);
38366
+ i = parsed.fullEnd;
38367
+ }
38368
+ return tokens;
38369
+ }
38370
+ function nextVirtualAppId(used) {
38371
+ let id = 9e8;
38372
+ while (used.has(id)) id++;
38373
+ used.add(id);
38374
+ return id;
38375
+ }
38376
+ function normalizeSqlAppProfiles(sql, defaultProfile = "dev", resolutionContext) {
38377
+ const tokens = collectAppProfileTokens(sql);
38378
+ const hasProfileSyntax = tokens.some((t) => t.profile !== null);
38379
+ const profilesByApp = /* @__PURE__ */ new Map();
38380
+ const normalizedProfile = (profile2) => profile2 ?? defaultProfile;
38381
+ for (const t of tokens) {
38382
+ if (t.source !== "physical") continue;
38383
+ const p = normalizedProfile(t.profile);
38384
+ let set2 = profilesByApp.get(t.appId);
38385
+ if (!set2) {
38386
+ set2 = /* @__PURE__ */ new Set();
38387
+ profilesByApp.set(t.appId, set2);
38388
+ }
38389
+ set2.add(p.toLowerCase());
38390
+ }
38391
+ const usedAppIds = new Set(
38392
+ tokens.filter((t) => t.source === "physical").map((t) => t.appId)
38393
+ );
38394
+ const resolvedLogicalApps = /* @__PURE__ */ new Map();
38395
+ for (const t of tokens) {
38396
+ if (t.source !== "logical") continue;
38397
+ const pLower = normalizedProfile(t.profile).toLowerCase();
38398
+ const logicalKey = `logical:${t.logicalName}@${pLower}`;
38399
+ if (resolvedLogicalApps.has(logicalKey)) continue;
38400
+ if (!resolutionContext) {
38401
+ throw new Error(
38402
+ `ArgumentError: logical app LAPP_${t.logicalName}@${pLower} requires logicalApps configuration.`
38403
+ );
38404
+ }
38405
+ const resolvedAppId = resolutionContext.resolveLogicalApp(t.logicalName, pLower);
38406
+ resolvedLogicalApps.set(logicalKey, resolvedAppId);
38407
+ usedAppIds.add(resolvedAppId);
38408
+ }
38409
+ const pairToMapped = /* @__PURE__ */ new Map();
38410
+ const appBindingByMappedApp = /* @__PURE__ */ new Map();
38411
+ for (const [appId, pSet] of profilesByApp.entries()) {
38412
+ const profiles = [...pSet].sort();
38413
+ if (profiles.length <= 1) continue;
38414
+ for (const pLower of profiles) {
38415
+ const mapped = nextVirtualAppId(usedAppIds);
38416
+ pairToMapped.set(`physical:${appId}@${pLower}`, mapped);
38417
+ appBindingByMappedApp.set(mapped, {
38418
+ source: "physical",
38419
+ mappedAppId: mapped,
38420
+ appId,
38421
+ profile: pLower
38422
+ });
38423
+ }
38424
+ }
38425
+ const out = [];
38426
+ const rewriteSegments = [];
38427
+ let normalizedLength = 0;
38428
+ let cursor = 0;
38429
+ const appendSegment = (text, sourceStart, sourceEnd, bindingMappedAppId) => {
38430
+ if (!text && sourceStart === sourceEnd) return;
38431
+ const normalizedStart = normalizedLength;
38432
+ out.push(text);
38433
+ normalizedLength += text.length;
38434
+ rewriteSegments.push({
38435
+ normalizedStart,
38436
+ normalizedEnd: normalizedLength,
38437
+ sourceStart,
38438
+ sourceEnd,
38439
+ ...bindingMappedAppId === void 0 ? {} : { bindingMappedAppId }
38440
+ });
38441
+ };
38442
+ for (const t of tokens) {
38443
+ const p = normalizedProfile(t.profile);
38444
+ const pLower = p.toLowerCase();
38445
+ let binding;
38446
+ if (t.source === "physical") {
38447
+ const mapped = pairToMapped.get(`physical:${t.appId}@${pLower}`) ?? t.appId;
38448
+ binding = {
38449
+ source: "physical",
38450
+ mappedAppId: mapped,
38451
+ appId: t.appId,
38452
+ profile: pLower
38453
+ };
38454
+ } else {
38455
+ const logicalKey = `logical:${t.logicalName}@${pLower}`;
38456
+ let mapped = pairToMapped.get(logicalKey);
38457
+ if (mapped === void 0) {
38458
+ mapped = nextVirtualAppId(usedAppIds);
38459
+ pairToMapped.set(logicalKey, mapped);
38460
+ }
38461
+ binding = {
38462
+ source: "logical",
38463
+ logicalName: t.logicalName,
38464
+ mappedAppId: mapped,
38465
+ appId: resolvedLogicalApps.get(logicalKey),
38466
+ profile: pLower
38467
+ };
38468
+ }
38469
+ appBindingByMappedApp.set(binding.mappedAppId, binding);
38470
+ appendSegment(sql.slice(cursor, t.start), cursor, t.start);
38471
+ const subtableSuffix = sql.slice(t.referenceValueEnd, t.appEnd);
38472
+ const normalizedReference = t.source === "physical" ? `${sql.slice(t.start, t.referenceValueStart)}${binding.mappedAppId}${subtableSuffix}` : `APP${binding.mappedAppId}${subtableSuffix}`;
38473
+ appendSegment(
38474
+ normalizedReference,
38475
+ t.start,
38476
+ t.fullEnd,
38477
+ binding.mappedAppId
38478
+ );
38479
+ cursor = t.fullEnd;
38480
+ }
38481
+ appendSegment(sql.slice(cursor), cursor, sql.length);
38482
+ return {
38483
+ normalizedSql: out.join(""),
38484
+ hasProfileSyntax,
38485
+ appBindingByMappedApp,
38486
+ rewriteSegments
38487
+ };
38488
+ }
38489
+ function buildCacheContext(defaultProfile, appBindingByMappedApp) {
38490
+ if (appBindingByMappedApp.size === 0) return `default:${defaultProfile.toLowerCase()}`;
38491
+ const pairs = [...appBindingByMappedApp.entries()].sort((a, b) => a[0] - b[0]).map(([mappedAppId, b]) => b.source === "logical" ? `M${mappedAppId}=logical:${b.logicalName}:APP${b.appId}@${b.profile}` : `M${mappedAppId}=physical:APP${b.appId}@${b.profile}`);
38492
+ return `apps:${pairs.join(",")}`;
38493
+ }
38494
+
38226
38495
  // src/node/runtime.ts
38496
+ var privateConfigSnapshots = /* @__PURE__ */ new WeakMap();
38497
+ function cloneConfigSnapshot(config2) {
38498
+ return JSON.parse(JSON.stringify(config2));
38499
+ }
38500
+ function toConfigView(config2) {
38501
+ const profiles = Object.fromEntries(Object.entries(config2.profiles ?? {}).map(([name, p]) => [name, {
38502
+ baseUrl: p.baseUrl,
38503
+ logicalApps: p.logicalApps === void 0 ? void 0 : Object.freeze({ ...p.logicalApps }),
38504
+ allowPhysicalAppRefs: p.allowPhysicalAppRefs,
38505
+ passwordEnv: p.passwordEnv,
38506
+ tokenMapSources: p.tokenMap === void 0 ? void 0 : Object.freeze(Object.fromEntries(
38507
+ Object.entries(p.tokenMap).map(([key, value]) => [key, String(value).startsWith("env:") ? String(value).slice(4) : "inline"])
38508
+ ))
38509
+ }]));
38510
+ return Object.freeze({ defaultProfile: config2.defaultProfile, profiles: Object.freeze(profiles) });
38511
+ }
38512
+ function resolveSqlContext(serverOptions, sql, inputProfile) {
38513
+ const configPath = serverOptions.configPath ?? envString("KSQL_CONFIG") ?? "./ksql.config.json";
38514
+ const config2 = cloneConfigSnapshot(loadOptionalKsqlConfig(configPath));
38515
+ const profileName = resolveDefaultProfile(config2, serverOptions, inputProfile);
38516
+ const resolutionContext = createAppResolutionContext(config2, profileName);
38517
+ const normalized = normalizeSqlAppProfiles(sql, profileName, resolutionContext);
38518
+ const bindings = normalized.appBindingByMappedApp;
38519
+ for (const binding of bindings.values()) {
38520
+ if (binding.source === "physical") resolutionContext.assertPhysicalAppAllowed(binding.profile);
38521
+ }
38522
+ const context = {
38523
+ normalizedSql: normalized.normalizedSql,
38524
+ bindings,
38525
+ cacheContext: buildCacheContext(profileName, bindings),
38526
+ profileName,
38527
+ rewriteSegments: Object.freeze(normalized.rewriteSegments.map((segment) => Object.freeze({ ...segment }))),
38528
+ hasProfileSyntax: normalized.hasProfileSyntax,
38529
+ configSnapshot: toConfigView(config2),
38530
+ logicalBindingLabels: new Map(
38531
+ [...bindings.values()].filter((b) => b.source === "logical").map((b) => [b.mappedAppId, `LAPP_${b.logicalName}@${b.profile}`])
38532
+ )
38533
+ };
38534
+ Object.freeze(context);
38535
+ privateConfigSnapshots.set(context, config2);
38536
+ return context;
38537
+ }
38538
+ function resolveTokenByMappedApp(args) {
38539
+ const tokenByMappedApp = /* @__PURE__ */ new Map();
38540
+ const tokenByPhysicalApp = /* @__PURE__ */ new Map();
38541
+ const missing = [];
38542
+ for (const mappedAppId of args.mappedAppIds) {
38543
+ const binding = args.bindings.get(mappedAppId);
38544
+ if (!binding && args.logicalBindingLabels.has(mappedAppId)) {
38545
+ throw new Error(`InternalError: binding is missing for logical app ${args.logicalBindingLabels.get(mappedAppId)}.`);
38546
+ }
38547
+ const appId = binding?.appId ?? mappedAppId;
38548
+ const profile2 = binding?.profile ?? args.profileName;
38549
+ const fromMap = args.effectiveTokenMap[`APP${appId}`];
38550
+ if (fromMap) {
38551
+ const token = resolveTokenValue(fromMap);
38552
+ tokenByMappedApp.set(mappedAppId, token);
38553
+ tokenByPhysicalApp.set(appId, token);
38554
+ continue;
38555
+ }
38556
+ if (binding?.source !== "logical" && args.mappedAppIds.length === 1 && args.singleToken) {
38557
+ tokenByMappedApp.set(mappedAppId, args.singleToken);
38558
+ tokenByPhysicalApp.set(appId, args.singleToken);
38559
+ continue;
38560
+ }
38561
+ missing.push(binding?.source === "logical" ? `LAPP_${binding.logicalName} (APP${appId})@${profile2}` : `APP${appId}@${profile2}`);
38562
+ }
38563
+ return { tokenByMappedApp, tokenByPhysicalApp, missing };
38564
+ }
38565
+ function resolveRuntimeBinding(context, mappedAppId) {
38566
+ const binding = context.bindings.get(mappedAppId);
38567
+ if (binding) return binding;
38568
+ const logicalLabel = context.logicalBindingLabels.get(mappedAppId);
38569
+ if (logicalLabel) {
38570
+ throw new Error(`InternalError: binding is missing for logical app ${logicalLabel}.`);
38571
+ }
38572
+ return { appId: mappedAppId, profile: context.profileName.toLowerCase() };
38573
+ }
38227
38574
  function resolveDefaultProfile(config2, serverOptions, inputProfile) {
38228
38575
  return inputProfile ?? serverOptions.profile ?? envString("KSQL_PROFILE") ?? config2.defaultProfile ?? "dev";
38229
38576
  }
38230
38577
  async function createKsqlRuntime(serverOptions, input) {
38231
- const configPath = serverOptions.configPath ?? envString("KSQL_CONFIG") ?? "./ksql.config.json";
38232
- const config2 = loadOptionalKsqlConfig(configPath);
38233
- const profileName = resolveDefaultProfile(config2, serverOptions, input.profile);
38578
+ const sqlContext = input.sqlContext ?? resolveSqlContext(serverOptions, input.sql, input.profile);
38579
+ const config2 = privateConfigSnapshots.get(sqlContext);
38580
+ if (!config2) {
38581
+ throw new Error("InternalError: private config snapshot is missing for resolved SQL context.");
38582
+ }
38583
+ const profileName = sqlContext.profileName;
38234
38584
  const profile2 = config2.profiles?.[profileName] ?? {};
38235
- const normalized = normalizeSqlAppProfiles(input.sql, profileName);
38236
- const sql = normalized.normalizedSql;
38585
+ const sql = sqlContext.normalizedSql;
38237
38586
  const maxRecords2 = input.maxRecords ?? envInt("KSQL_MAX_RECORDS") ?? profile2.query?.maxRecords ?? 500;
38238
38587
  const fetchParallel2 = input.fetchParallel ?? envInt("KSQL_FETCH_PARALLEL") ?? profile2.query?.fetchParallel ?? 3;
38239
38588
  if (!Number.isInteger(fetchParallel2) || fetchParallel2 < 1 || fetchParallel2 > 10) {
@@ -38249,11 +38598,12 @@ async function createKsqlRuntime(serverOptions, input) {
38249
38598
  for (const appId of appIds) {
38250
38599
  appProfileByApp.set(
38251
38600
  appId,
38252
- normalized.appBindingByMappedApp.get(appId)?.profile ?? profileName.toLowerCase()
38601
+ sqlContext.bindings.get(appId)?.profile ?? profileName.toLowerCase()
38253
38602
  );
38254
38603
  }
38255
38604
  const usedProfiles = /* @__PURE__ */ new Set([...appProfileByApp.values(), profileName]);
38256
38605
  const profileClientMap = /* @__PURE__ */ new Map();
38606
+ const allTokenByMappedApp = /* @__PURE__ */ new Map();
38257
38607
  const missingAppProfiles = [];
38258
38608
  const tokenMapEnv = envString("KSQL_TOKEN_MAP");
38259
38609
  const mapFromEnv = tokenMapEnv ? parseTokenMap(tokenMapEnv) : {};
@@ -38291,21 +38641,19 @@ async function createKsqlRuntime(serverOptions, input) {
38291
38641
  );
38292
38642
  const effectiveTokenMap = { ...mapFromConfig, ...mapFromEnv };
38293
38643
  const assignedAppIds = appIds.filter((appId) => appProfileByApp.get(appId) === pName);
38294
- const tokenByApp = /* @__PURE__ */ new Map();
38295
- for (const mappedAppId of assignedAppIds) {
38296
- const realAppId = normalized.appBindingByMappedApp.get(mappedAppId)?.appId ?? mappedAppId;
38297
- const key = `APP${realAppId}`;
38298
- const fromMap = effectiveTokenMap[key];
38299
- if (fromMap) {
38300
- tokenByApp.set(mappedAppId, resolveTokenValue(fromMap));
38301
- continue;
38302
- }
38303
- if (assignedAppIds.length === 1 && singleToken) {
38304
- tokenByApp.set(mappedAppId, singleToken);
38305
- continue;
38306
- }
38307
- missingAppProfiles.push(`APP${realAppId}@${pName}`);
38644
+ const resolvedTokens = resolveTokenByMappedApp({
38645
+ mappedAppIds: assignedAppIds,
38646
+ profileName: pName,
38647
+ bindings: sqlContext.bindings,
38648
+ logicalBindingLabels: sqlContext.logicalBindingLabels,
38649
+ effectiveTokenMap,
38650
+ singleToken
38651
+ });
38652
+ const tokenByApp = resolvedTokens.tokenByPhysicalApp;
38653
+ for (const [mappedAppId, token] of resolvedTokens.tokenByMappedApp) {
38654
+ allTokenByMappedApp.set(mappedAppId, token);
38308
38655
  }
38656
+ missingAppProfiles.push(...resolvedTokens.missing);
38309
38657
  if (assignedAppIds.length === 0 && singleToken) {
38310
38658
  tokenByApp.set(0, singleToken);
38311
38659
  }
@@ -38328,38 +38676,43 @@ async function createKsqlRuntime(serverOptions, input) {
38328
38676
  if (missingAppProfiles.length > 0) {
38329
38677
  throw new Error(`AuthError: token is missing for ${missingAppProfiles.join(", ")}.`);
38330
38678
  }
38331
- const defaultClient = profileClientMap.get(profileName);
38679
+ const runtimeContext = {
38680
+ sqlContext,
38681
+ tokenByMappedApp: allTokenByMappedApp,
38682
+ clientsByProfile: profileClientMap
38683
+ };
38684
+ const defaultClient = runtimeContext.clientsByProfile.get(profileName);
38332
38685
  if (!defaultClient) {
38333
38686
  throw new Error(`AuthError: profile client is not resolved for "${profileName}".`);
38334
38687
  }
38335
38688
  const routedClient = {
38336
38689
  getRecords: (params) => {
38337
- const binding = normalized.appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
38338
- const routed = profileClientMap.get(binding.profile);
38690
+ const binding = resolveRuntimeBinding(runtimeContext.sqlContext, params.app);
38691
+ const routed = runtimeContext.clientsByProfile.get(binding.profile);
38339
38692
  if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
38340
38693
  return routed.getRecords({ ...params, app: binding.appId });
38341
38694
  },
38342
38695
  postRecords: (params) => {
38343
- const binding = normalized.appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
38344
- const routed = profileClientMap.get(binding.profile);
38696
+ const binding = resolveRuntimeBinding(runtimeContext.sqlContext, params.app);
38697
+ const routed = runtimeContext.clientsByProfile.get(binding.profile);
38345
38698
  if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
38346
38699
  return routed.postRecords({ ...params, app: binding.appId });
38347
38700
  },
38348
38701
  putRecords: (params) => {
38349
- const binding = normalized.appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
38350
- const routed = profileClientMap.get(binding.profile);
38702
+ const binding = resolveRuntimeBinding(runtimeContext.sqlContext, params.app);
38703
+ const routed = runtimeContext.clientsByProfile.get(binding.profile);
38351
38704
  if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
38352
38705
  return routed.putRecords({ ...params, app: binding.appId });
38353
38706
  },
38354
38707
  deleteRecords: (params) => {
38355
- const binding = normalized.appBindingByMappedApp.get(params.app) ?? { appId: params.app, profile: profileName.toLowerCase() };
38356
- const routed = profileClientMap.get(binding.profile);
38708
+ const binding = resolveRuntimeBinding(runtimeContext.sqlContext, params.app);
38709
+ const routed = runtimeContext.clientsByProfile.get(binding.profile);
38357
38710
  if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${params.app}.`);
38358
38711
  return routed.deleteRecords({ ...params, app: binding.appId });
38359
38712
  },
38360
38713
  getFields: (appId) => {
38361
- const binding = normalized.appBindingByMappedApp.get(appId) ?? { appId, profile: profileName.toLowerCase() };
38362
- const routed = profileClientMap.get(binding.profile);
38714
+ const binding = resolveRuntimeBinding(runtimeContext.sqlContext, appId);
38715
+ const routed = runtimeContext.clientsByProfile.get(binding.profile);
38363
38716
  if (!routed) throw new Error(`AuthError: profile "${binding.profile}" is not resolved for APP${appId}.`);
38364
38717
  return routed.getFields(binding.appId);
38365
38718
  },
@@ -38378,7 +38731,7 @@ async function createKsqlRuntime(serverOptions, input) {
38378
38731
  sql,
38379
38732
  profileName,
38380
38733
  client: gatedClient,
38381
- cacheContext: buildCacheContext(profileName, normalized.appBindingByMappedApp),
38734
+ cacheContext: sqlContext.cacheContext,
38382
38735
  maxRecords: maxRecords2,
38383
38736
  fetchParallel: fetchParallel2,
38384
38737
  onLimit: onLimit2,
@@ -38590,11 +38943,6 @@ function noOpClient() {
38590
38943
  getFields: fail
38591
38944
  };
38592
38945
  }
38593
- function getToolProfile(serverOptions, inputProfile) {
38594
- const configPath = getServerConfigPath(serverOptions);
38595
- const config2 = loadOptionalKsqlConfig(configPath);
38596
- return resolveDefaultProfile(config2, serverOptions, inputProfile);
38597
- }
38598
38946
  function getServerConfigPath(serverOptions) {
38599
38947
  return serverOptions.configPath ?? envString("KSQL_CONFIG") ?? "./ksql.config.json";
38600
38948
  }
@@ -38607,16 +38955,34 @@ function getSavedQueryCatalogPath(serverOptions) {
38607
38955
  });
38608
38956
  }
38609
38957
  function normalizeSqlForTool(serverOptions, sql, inputProfile) {
38610
- const profileName = getToolProfile(serverOptions, inputProfile);
38611
- const normalized = normalizeSqlAppProfiles(sql, profileName);
38958
+ const sqlContext = resolveSqlContext(serverOptions, sql, inputProfile);
38612
38959
  return {
38613
- profileName,
38614
- normalizedSql: normalized.normalizedSql,
38615
- hasProfileSyntax: normalized.hasProfileSyntax,
38616
- appBindingByMappedApp: normalized.appBindingByMappedApp,
38617
- cacheContext: buildCacheContext(profileName, normalized.appBindingByMappedApp)
38960
+ profileName: sqlContext.profileName,
38961
+ normalizedSql: sqlContext.normalizedSql,
38962
+ hasProfileSyntax: sqlContext.hasProfileSyntax,
38963
+ appBindingByMappedApp: sqlContext.bindings,
38964
+ cacheContext: sqlContext.cacheContext,
38965
+ sqlContext,
38966
+ sourceSql: sql
38967
+ };
38968
+ }
38969
+ function toValidationBinding(mappedAppId, binding) {
38970
+ return binding.source === "logical" ? {
38971
+ source: binding.source,
38972
+ logicalName: binding.logicalName,
38973
+ mappedAppId,
38974
+ appId: binding.appId,
38975
+ profile: binding.profile
38976
+ } : {
38977
+ source: binding.source,
38978
+ mappedAppId,
38979
+ appId: binding.appId,
38980
+ profile: binding.profile
38618
38981
  };
38619
38982
  }
38983
+ function toExplainBindings(bindings) {
38984
+ return [...bindings.values()].map((binding) => binding.source === "logical" ? { source: binding.source, logicalName: binding.logicalName, appId: binding.appId, profile: binding.profile } : { source: binding.source, appId: binding.appId, profile: binding.profile });
38985
+ }
38620
38986
  function explainSql(sql) {
38621
38987
  return /^\s*EXPLAIN\b/i.test(sql) ? sql : `EXPLAIN ${sql}`;
38622
38988
  }
@@ -38740,15 +39106,17 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38740
39106
  const createRuntime = deps.createRuntime ?? createKsqlRuntime;
38741
39107
  const executeSql = deps.executeSql ?? execute;
38742
39108
  const executeBatchSql = deps.executeBatchSql ?? executeBatch;
39109
+ const validationContexts = /* @__PURE__ */ new WeakMap();
38743
39110
  async function validate(input) {
38744
39111
  const normalized = normalizeSqlForTool(serverOptions, input.sql, input.profile);
38745
- const statements = parseSqlStatements(normalized.normalizedSql);
38746
- const analysis = analyzeBatch(statements);
38747
- const appBindings = [...normalized.appBindingByMappedApp.entries()].map(([mappedAppId, binding]) => ({
38748
- mappedAppId,
38749
- appId: binding.appId,
38750
- profile: binding.profile
38751
- }));
39112
+ let analysis;
39113
+ try {
39114
+ const statements = parseSqlStatements(normalized.normalizedSql);
39115
+ analysis = analyzeBatch(statements);
39116
+ } catch (err) {
39117
+ throw restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
39118
+ }
39119
+ const appBindings = [...normalized.appBindingByMappedApp.entries()].map(([mappedAppId, binding]) => toValidationBinding(mappedAppId, binding));
38752
39120
  const statementValidations = analysis.statements.map((s2) => ({
38753
39121
  index: s2.index,
38754
39122
  statementType: s2.statementType,
@@ -38778,10 +39146,12 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38778
39146
  appBindings
38779
39147
  };
38780
39148
  if (analysis.statementCount > 1) {
38781
- return { ...common, batch: true };
39149
+ const result2 = { ...common, batch: true };
39150
+ validationContexts.set(result2, normalized.sqlContext);
39151
+ return result2;
38782
39152
  }
38783
39153
  const s = statementValidations[0];
38784
- return {
39154
+ const result = {
38785
39155
  ...common,
38786
39156
  batch: false,
38787
39157
  statementType: s.statementType,
@@ -38791,17 +39161,26 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38791
39161
  insertValuesCount: s.insertValuesCount,
38792
39162
  appIds: s.appIds
38793
39163
  };
39164
+ validationContexts.set(result, normalized.sqlContext);
39165
+ return result;
38794
39166
  }
38795
39167
  async function explain(input) {
38796
39168
  const normalized = normalizeSqlForTool(serverOptions, input.sql, input.profile);
38797
- const statements = parseSqlStatements(normalized.normalizedSql);
39169
+ const appBindings = toExplainBindings(normalized.appBindingByMappedApp);
39170
+ let statements;
39171
+ try {
39172
+ statements = parseSqlStatements(normalized.normalizedSql);
39173
+ } catch (err) {
39174
+ throw restoreSqlContextError(err, normalized.sourceSql, normalized.sqlContext);
39175
+ }
38798
39176
  if (statements.length > 1) {
38799
39177
  const plans = buildBatchExplainPlans(normalized.normalizedSql);
38800
39178
  return {
38801
39179
  ok: true,
38802
39180
  batch: true,
38803
39181
  statementCount: plans.statementCount,
38804
- statements: plans.statements
39182
+ statements: restoreSqlDiagnosticValue(plans.statements, normalized.appBindingByMappedApp),
39183
+ appBindings
38805
39184
  };
38806
39185
  }
38807
39186
  const result = await executeSql(explainSql(normalized.normalizedSql), noOpClient(), {
@@ -38810,16 +39189,20 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38810
39189
  if (result.type !== "SELECT") {
38811
39190
  throw new Error(`ArgumentError: EXPLAIN returned unexpected result type ${result.type}.`);
38812
39191
  }
38813
- return toSelectPayload(result);
39192
+ return restoreSqlDiagnosticValue(
39193
+ { ...toSelectPayload(result), appBindings },
39194
+ normalized.appBindingByMappedApp
39195
+ );
38814
39196
  }
38815
- async function query(input) {
38816
- const validation = await validate(input);
39197
+ async function query(input, validated) {
39198
+ const validation = validated ?? await validate(input);
38817
39199
  if (validation.batch) {
38818
39200
  if (validation.containsDml) {
38819
39201
  throw new Error("ArgumentError: batch contains DML statements. Use ksql_mutate.");
38820
39202
  }
38821
39203
  const runtime2 = await createRuntime(serverOptions, {
38822
39204
  sql: input.sql,
39205
+ sqlContext: validationContexts.get(validation),
38823
39206
  profile: input.profile,
38824
39207
  maxRecords: input.maxRecords,
38825
39208
  fetchParallel: input.fetchParallel,
@@ -38862,6 +39245,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38862
39245
  }
38863
39246
  const runtime = await createRuntime(serverOptions, {
38864
39247
  sql: input.sql,
39248
+ sqlContext: validationContexts.get(validation),
38865
39249
  profile: input.profile,
38866
39250
  maxRecords: input.maxRecords,
38867
39251
  fetchParallel: input.fetchParallel,
@@ -38907,6 +39291,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38907
39291
  const selectBasedDml = containsSelectBasedDml(validation.statements);
38908
39292
  const runtime = await createRuntime(serverOptions, {
38909
39293
  sql: input.sql,
39294
+ sqlContext: validationContexts.get(validation),
38910
39295
  profile: input.profile,
38911
39296
  // SELECT-based DML を含む場合は dmlMaxRows で読み取りを絞らない(案A。
38912
39297
  // resolveMutateRuntimeMaxRecords の doc コメント参照)
@@ -38951,9 +39336,9 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38951
39336
  }
38952
39337
  return { ...payload };
38953
39338
  }
38954
- async function mutate(input) {
39339
+ async function mutate(input, validated) {
38955
39340
  const dmlMaxRows = requireDmlApproval(input, "ksql_mutate");
38956
- const validation = await validate(input);
39341
+ const validation = validated ?? await validate(input);
38957
39342
  if (validation.batch) {
38958
39343
  return mutateBatch(input, validation, dmlMaxRows);
38959
39344
  }
@@ -38969,6 +39354,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
38969
39354
  const selectBasedDml = containsSelectBasedDml(validation.statements);
38970
39355
  const runtime = await createRuntime(serverOptions, {
38971
39356
  sql: input.sql,
39357
+ sqlContext: validationContexts.get(validation),
38972
39358
  profile: input.profile,
38973
39359
  // SELECT-based DML は dmlMaxRows で読み取りを絞らない(案A。
38974
39360
  // resolveMutateRuntimeMaxRecords の doc コメント参照)
@@ -39089,7 +39475,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
39089
39475
  fetchParallel: input.fetchParallel,
39090
39476
  onLimit: input.onLimit,
39091
39477
  timeout: input.timeout
39092
- });
39478
+ }, validation);
39093
39479
  return {
39094
39480
  ok: true,
39095
39481
  name: saved.name,
@@ -39105,7 +39491,7 @@ function createKsqlMcpTools(serverOptions, deps = {}) {
39105
39491
  dmlMaxRows,
39106
39492
  fetchParallel: input.fetchParallel,
39107
39493
  timeout: input.timeout
39108
- });
39494
+ }, validation);
39109
39495
  return {
39110
39496
  ok: true,
39111
39497
  name: saved.name,
@@ -39275,7 +39661,7 @@ Options:
39275
39661
  -h, --help Show help
39276
39662
  `);
39277
39663
  }
39278
- var SERVER_VERSION = true ? "1.12.1" : "0.0.0-dev";
39664
+ var SERVER_VERSION = true ? "1.13.2" : "0.0.0-dev";
39279
39665
  function createServer(args) {
39280
39666
  const server = new McpServer({
39281
39667
  name: "ksql-mcp",