@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.
package/dist-cli/ksql.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  parseConsoleMetaCommand: () => parseConsoleMetaCommand,
35
35
  parseTokenFile: () => parseTokenFile,
36
36
  parseTokenMap: () => parseTokenMap,
37
+ runWithArgv: () => runWithArgv,
37
38
  shouldExitOnEmpty: () => shouldExitOnEmpty,
38
39
  writeBatchOutput: () => writeBatchOutput
39
40
  });
@@ -6539,7 +6540,7 @@ function buildInsertPlan(stmt, label) {
6539
6540
  const lines = [];
6540
6541
  if (label) lines.push(label);
6541
6542
  lines.push(` [INSERT]`);
6542
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
6543
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
6543
6544
  lines.push(` records: ${totalRows} \u4EF6\uFF08\u30D0\u30C3\u30C1 ${batchCount} \u56DE \xD7 \u6700\u5927 100 \u4EF6\uFF09`);
6544
6545
  lines.push(` api: POST /k/v1/records.json \xD7 ${batchCount}`);
6545
6546
  lines.push(` fields: ${stmt.fields.join(", ")}`);
@@ -6549,7 +6550,7 @@ function buildInsertSelectPlan(stmt, label) {
6549
6550
  const lines = [];
6550
6551
  if (label) lines.push(label);
6551
6552
  lines.push(` [INSERT SELECT]`);
6552
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
6553
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
6553
6554
  lines.push(` fields: ${stmt.fields.join(", ")}`);
6554
6555
  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`);
6555
6556
  lines.push("");
@@ -6562,7 +6563,7 @@ function buildUpdatePlan(stmt, label) {
6562
6563
  const lines = [];
6563
6564
  if (label) lines.push(label);
6564
6565
  lines.push(` [UPDATE]`);
6565
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
6566
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
6566
6567
  lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
6567
6568
  lines.push(` api: GET /k/v1/records.json \u2192 PUT /k/v1/records.json`);
6568
6569
  const setTypes = [];
@@ -6592,7 +6593,7 @@ function buildDeletePlan(stmt, label) {
6592
6593
  const lines = [];
6593
6594
  if (label) lines.push(label);
6594
6595
  lines.push(` [DELETE]`);
6595
- lines.push(` app: APP${stmt.appId} (${stmt.appId})`);
6596
+ lines.push(` target: APP${stmt.appId} (${stmt.appId})`);
6596
6597
  lines.push(` kintone query: ${safeWhereToKintone(stmt.where)}`);
6597
6598
  lines.push(` api: GET /k/v1/records.json \u2192 DELETE /k/v1/records.json`);
6598
6599
  return lines;
@@ -6603,7 +6604,7 @@ function buildUpsertPlan(stmt, label) {
6603
6604
  return [
6604
6605
  ...label ? [label] : [],
6605
6606
  ` [UPSERT]`,
6606
- ` app: APP${stmt.appId} (${stmt.appId})`,
6607
+ ` target: APP${stmt.appId} (${stmt.appId})`,
6607
6608
  ` records: ${totalRows} \u4EF6\uFF08\u30D0\u30C3\u30C1 ${batchCount} \u56DE \xD7 \u6700\u5927 100 \u4EF6\uFF09`,
6608
6609
  ` key fields: ${stmt.keyFields.join(", ")}`,
6609
6610
  ` fields: ${stmt.fields.join(", ")}`,
@@ -6614,7 +6615,7 @@ function buildUpsertSelectPlan(stmt, label) {
6614
6615
  const lines = [
6615
6616
  ...label ? [label] : [],
6616
6617
  ` [UPSERT SELECT]`,
6617
- ` app: APP${stmt.appId} (${stmt.appId})`,
6618
+ ` target: APP${stmt.appId} (${stmt.appId})`,
6618
6619
  ` key fields: ${stmt.keyFields.join(", ")}`,
6619
6620
  ` fields: ${stmt.fields.join(", ")}`,
6620
6621
  ` 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`,
@@ -6630,7 +6631,7 @@ function buildReorderPlan(stmt, label) {
6630
6631
  const lines = [
6631
6632
  ...label ? [label] : [],
6632
6633
  ` [REORDER]`,
6633
- ` app: APP${stmt.appId} (${stmt.appId})`,
6634
+ ` target: APP${stmt.appId} (${stmt.appId})`,
6634
6635
  ` table: ${target}`,
6635
6636
  ` scope: ${scope}`,
6636
6637
  ` by: ${byStr}`,
@@ -6849,6 +6850,114 @@ function buildBatchEnvelope(batch, options = {}) {
6849
6850
  }
6850
6851
 
6851
6852
  // src/node/config.ts
6853
+ var LOGICAL_APP_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
6854
+ var PHYSICAL_APP_KEY_RE = /^APP\d+$/i;
6855
+ var NUMERIC_APP_KEY_RE = /^\d+$/;
6856
+ var LOGICAL_SQL_KEY_RE = /^LAPP_/i;
6857
+ function argumentError(message) {
6858
+ return new Error(`ArgumentError: ${message}`);
6859
+ }
6860
+ function normalizeLogicalApps(profileName, value) {
6861
+ if (value === void 0) return void 0;
6862
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
6863
+ throw argumentError(`logicalApps for profile "${profileName}" must be an object.`);
6864
+ }
6865
+ const normalized = {};
6866
+ const physicalIdOwners = /* @__PURE__ */ new Map();
6867
+ for (const [rawName, rawAppId] of Object.entries(value)) {
6868
+ if (PHYSICAL_APP_KEY_RE.test(rawName) || NUMERIC_APP_KEY_RE.test(rawName) || LOGICAL_SQL_KEY_RE.test(rawName)) {
6869
+ throw argumentError(
6870
+ `logical app key "${rawName}" in profile "${profileName}" must be a logical name without APP, numeric, or LAPP_ syntax.`
6871
+ );
6872
+ }
6873
+ if (!LOGICAL_APP_NAME_RE.test(rawName)) {
6874
+ throw argumentError(
6875
+ `logical app key "${rawName}" in profile "${profileName}" must match [A-Z][A-Z0-9_]{0,63}.`
6876
+ );
6877
+ }
6878
+ const logicalName = rawName.toUpperCase();
6879
+ if (Object.prototype.hasOwnProperty.call(normalized, logicalName)) {
6880
+ throw argumentError(
6881
+ `logical app name "${logicalName}" is duplicated after case normalization in profile "${profileName}".`
6882
+ );
6883
+ }
6884
+ if (typeof rawAppId !== "number" || !Number.isSafeInteger(rawAppId) || rawAppId <= 0) {
6885
+ throw argumentError(
6886
+ `physical app ID for logical app "${logicalName}" in profile "${profileName}" must be a positive safe integer.`
6887
+ );
6888
+ }
6889
+ const existingName = physicalIdOwners.get(rawAppId);
6890
+ if (existingName !== void 0) {
6891
+ throw argumentError(
6892
+ `logical apps "${existingName}" and "${logicalName}" in profile "${profileName}" map to the same physical app ID ${rawAppId}; physical app aliases are not supported yet.`
6893
+ );
6894
+ }
6895
+ physicalIdOwners.set(rawAppId, logicalName);
6896
+ normalized[logicalName] = rawAppId;
6897
+ }
6898
+ return normalized;
6899
+ }
6900
+ function validateKsqlConfig(config) {
6901
+ if (config === null || typeof config !== "object" || Array.isArray(config)) {
6902
+ throw argumentError("config must be an object.");
6903
+ }
6904
+ if (config.profiles === void 0) return config;
6905
+ if (config.profiles === null || typeof config.profiles !== "object" || Array.isArray(config.profiles)) {
6906
+ throw argumentError("profiles must be an object.");
6907
+ }
6908
+ for (const [profileName, profile] of Object.entries(config.profiles)) {
6909
+ if (profile === null || typeof profile !== "object" || Array.isArray(profile)) {
6910
+ throw argumentError(`profile "${profileName}" must be an object.`);
6911
+ }
6912
+ if (profile.allowPhysicalAppRefs !== void 0 && typeof profile.allowPhysicalAppRefs !== "boolean") {
6913
+ throw argumentError(`allowPhysicalAppRefs for profile "${profileName}" must be boolean.`);
6914
+ }
6915
+ const logicalApps = normalizeLogicalApps(profileName, profile.logicalApps);
6916
+ if (logicalApps !== void 0) profile.logicalApps = logicalApps;
6917
+ }
6918
+ return config;
6919
+ }
6920
+ function createAppResolutionContext(config, defaultProfile) {
6921
+ const profiles = Object.fromEntries(
6922
+ Object.entries(config.profiles ?? {}).map(([name, profile]) => [
6923
+ name,
6924
+ {
6925
+ logicalApps: profile.logicalApps === void 0 ? void 0 : { ...profile.logicalApps },
6926
+ allowPhysicalAppRefs: profile.allowPhysicalAppRefs
6927
+ }
6928
+ ])
6929
+ );
6930
+ const implicitDefaultProfile = {};
6931
+ function requireProfile(profileName) {
6932
+ const profile = profiles[profileName];
6933
+ if (!profile && profileName === defaultProfile) return implicitDefaultProfile;
6934
+ if (!profile) throw argumentError(`profile "${profileName}" is not defined.`);
6935
+ return profile;
6936
+ }
6937
+ return {
6938
+ resolveLogicalApp(name, profile) {
6939
+ if (!LOGICAL_APP_NAME_RE.test(name)) {
6940
+ throw argumentError(`logical app name "${name}" must match [A-Z][A-Z0-9_]{0,63}.`);
6941
+ }
6942
+ const profileName = profile || defaultProfile;
6943
+ const logicalName = name.toUpperCase();
6944
+ const appId = requireProfile(profileName).logicalApps?.[logicalName];
6945
+ if (appId === void 0) {
6946
+ throw argumentError(`logical app LAPP_${logicalName}@${profileName} is not defined.`);
6947
+ }
6948
+ return appId;
6949
+ },
6950
+ assertPhysicalAppAllowed(profile) {
6951
+ const profileName = profile || defaultProfile;
6952
+ if (!profiles[profileName]) return;
6953
+ if (requireProfile(profileName).allowPhysicalAppRefs === false) {
6954
+ throw argumentError(
6955
+ `physical app references are not allowed for profile "${profileName}"; use LAPP_<NAME>.`
6956
+ );
6957
+ }
6958
+ }
6959
+ };
6960
+ }
6852
6961
  function envString(name) {
6853
6962
  const v = process.env[name];
6854
6963
  return v && v.trim() ? v : null;
@@ -6860,20 +6969,354 @@ function envInt(name) {
6860
6969
  if (!Number.isInteger(n) || n <= 0) return null;
6861
6970
  return n;
6862
6971
  }
6863
- function envNonNegativeInt(name) {
6864
- const v = envString(name);
6865
- if (v === null) return null;
6866
- const n = Number(v);
6867
- if (!Number.isInteger(n) || n < 0) return null;
6868
- return n;
6972
+ function envNonNegativeInt(name) {
6973
+ const v = envString(name);
6974
+ if (v === null) return null;
6975
+ const n = Number(v);
6976
+ if (!Number.isInteger(n) || n < 0) return null;
6977
+ return n;
6978
+ }
6979
+ function resolveRequestGateOptions(base) {
6980
+ return {
6981
+ ...base,
6982
+ maxConcurrent: envInt("KSQL_MAX_CONCURRENT") ?? base.maxConcurrent,
6983
+ // KSQL_RETRY=0(リトライ無効)は有効値のため envNonNegativeInt で読む
6984
+ maxRetries: envNonNegativeInt("KSQL_RETRY") ?? base.maxRetries
6985
+ };
6986
+ }
6987
+ function resolveTokenValue(raw) {
6988
+ if (raw.startsWith("env:")) {
6989
+ const envKey = raw.slice(4);
6990
+ const envVal = process.env[envKey];
6991
+ if (!envVal) throw new Error(`AuthError: environment variable "${envKey}" is not set.`);
6992
+ return envVal;
6993
+ }
6994
+ return raw;
6995
+ }
6996
+
6997
+ // src/api/requestGate.ts
6998
+ var DEFAULT_MAX_CONCURRENT = 10;
6999
+ var DEFAULT_MAX_RETRIES = 3;
7000
+ var DEFAULT_BASE_DELAY_MS = 500;
7001
+ var DEFAULT_MAX_DELAY_MS = 8e3;
7002
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
7003
+ function isRetryableError(err) {
7004
+ if (!(err instanceof Error)) return false;
7005
+ const status = err.message.match(/^kintone API error (\d{3}):/);
7006
+ if (status) return RETRYABLE_STATUSES.has(Number(status[1]));
7007
+ if (err.name === "AbortError" || err.name === "TimeoutError") return true;
7008
+ if (/fetch failed/i.test(err.message)) return true;
7009
+ return false;
7010
+ }
7011
+ var RequestGate = class {
7012
+ constructor(options = {}) {
7013
+ this.active = 0;
7014
+ this.waiters = [];
7015
+ this.maxConcurrent = clampInt(options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT, 1, 50);
7016
+ this.maxRetries = clampInt(options.maxRetries ?? DEFAULT_MAX_RETRIES, 0, 10);
7017
+ this.baseDelayMs = clampInt(options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS, 1, 6e4);
7018
+ this.maxDelayMs = Math.max(
7019
+ clampInt(options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS, 1, 6e5),
7020
+ this.baseDelayMs
7021
+ );
7022
+ this.sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
7023
+ this.random = options.random ?? Math.random;
7024
+ }
7025
+ /** 現在の同時実行数(テスト・診断用) */
7026
+ get activeCount() {
7027
+ return this.active;
7028
+ }
7029
+ get limit() {
7030
+ return this.maxConcurrent;
7031
+ }
7032
+ /** 解決済みの GET リトライ回数(テスト・診断用) */
7033
+ get retries() {
7034
+ return this.maxRetries;
7035
+ }
7036
+ /** 解決済みのバックオフ初期値ミリ秒(テスト・診断用) */
7037
+ get retryBaseDelayMs() {
7038
+ return this.baseDelayMs;
7039
+ }
7040
+ /** 解決済みのバックオフ上限ミリ秒(テスト・診断用) */
7041
+ get retryMaxDelayMs() {
7042
+ return this.maxDelayMs;
7043
+ }
7044
+ /** GET 系: セマフォ + リトライ付きで実行する */
7045
+ async runReadOnly(fn) {
7046
+ let attempt = 0;
7047
+ while (true) {
7048
+ try {
7049
+ return await this.withSlot(fn);
7050
+ } catch (err) {
7051
+ if (attempt >= this.maxRetries || !isRetryableError(err)) throw err;
7052
+ await this.sleep(this.backoffDelay(attempt));
7053
+ attempt += 1;
7054
+ }
7055
+ }
7056
+ }
7057
+ /** 書き込み系: セマフォのみ(リトライしない — 二重実行防止) */
7058
+ async runMutation(fn) {
7059
+ return this.withSlot(fn);
7060
+ }
7061
+ async withSlot(fn) {
7062
+ await this.acquire();
7063
+ try {
7064
+ return await fn();
7065
+ } finally {
7066
+ this.release();
7067
+ }
7068
+ }
7069
+ async acquire() {
7070
+ if (this.active < this.maxConcurrent) {
7071
+ this.active += 1;
7072
+ return;
7073
+ }
7074
+ await new Promise((resolve2) => this.waiters.push(resolve2));
7075
+ this.active += 1;
7076
+ }
7077
+ release() {
7078
+ this.active -= 1;
7079
+ const next = this.waiters.shift();
7080
+ if (next) next();
7081
+ }
7082
+ /** 指数バックオフ + ジッタ(attempt: 0 始まり) */
7083
+ backoffDelay(attempt) {
7084
+ const base = Math.min(this.baseDelayMs * 2 ** attempt, this.maxDelayMs);
7085
+ const jitter = 1 + (this.random() - 0.5) * 0.5;
7086
+ return Math.round(base * jitter);
7087
+ }
7088
+ };
7089
+ function withRequestGate(client, gate) {
7090
+ return {
7091
+ getRecords: (params) => gate.runReadOnly(() => client.getRecords(params)),
7092
+ getApps: () => gate.runReadOnly(() => client.getApps()),
7093
+ getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
7094
+ postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
7095
+ putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
7096
+ deleteRecords: (params) => gate.runMutation(() => client.deleteRecords(params))
7097
+ };
7098
+ }
7099
+ var globalGate = null;
7100
+ function getGlobalRequestGate(options) {
7101
+ if (globalGate === null) {
7102
+ globalGate = new RequestGate(
7103
+ typeof options === "number" ? { maxConcurrent: options } : options ?? {}
7104
+ );
7105
+ }
7106
+ return globalGate;
7107
+ }
7108
+ function clampInt(v, min, max) {
7109
+ if (!Number.isFinite(v)) return min;
7110
+ return Math.max(min, Math.min(max, Math.trunc(v)));
7111
+ }
7112
+
7113
+ // src/cli/nodeKintoneClient.ts
7114
+ function createNodeKintoneClient(baseUrl, tokenResolver) {
7115
+ const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
7116
+ const apiBasePath = tokenResolver.guestSpaceId && tokenResolver.guestSpaceId > 0 ? `/k/guest/${tokenResolver.guestSpaceId}/v1` : "/k/v1";
7117
+ async function requestJson(path, init, appIdForToken) {
7118
+ const headers = new Headers(init.headers ?? {});
7119
+ if (tokenResolver.auth.type === "token") {
7120
+ headers.set("X-Cybozu-API-Token", tokenResolver.auth.resolveToken(appIdForToken));
7121
+ } else {
7122
+ const credentials = `${tokenResolver.auth.username}:${tokenResolver.auth.password}`;
7123
+ const encoded = Buffer.from(credentials, "utf-8").toString("base64");
7124
+ headers.set("X-Cybozu-Authorization", encoded);
7125
+ }
7126
+ headers.set("Accept", "application/json");
7127
+ const method = String(init.method ?? "GET").toUpperCase();
7128
+ if (method !== "GET" && method !== "HEAD") {
7129
+ headers.set("Content-Type", "application/json");
7130
+ }
7131
+ const timeoutMs = tokenResolver.timeoutMs ?? 3e4;
7132
+ const url = `${normalizedBaseUrl}${path}`;
7133
+ if (tokenResolver.debug) {
7134
+ tokenResolver.log?.(`[debug] request ${String(init.method ?? "GET")} ${url}`);
7135
+ if (tokenResolver.debugHeaders) {
7136
+ const authHeader = headers.get("X-Cybozu-API-Token") ? "X-Cybozu-API-Token=***" : headers.get("X-Cybozu-Authorization") ? "X-Cybozu-Authorization=***" : "Auth=(none)";
7137
+ tokenResolver.log?.(
7138
+ `[debug] request-headers ${authHeader} Content-Type=${headers.get("Content-Type") ?? "(none)"} Accept=${headers.get("Accept") ?? "(none)"}`
7139
+ );
7140
+ }
7141
+ }
7142
+ const controller = new AbortController();
7143
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
7144
+ timeout.unref?.();
7145
+ let res;
7146
+ try {
7147
+ res = await fetch(url, {
7148
+ ...init,
7149
+ headers,
7150
+ signal: controller.signal
7151
+ });
7152
+ } finally {
7153
+ clearTimeout(timeout);
7154
+ }
7155
+ if (!res.ok) {
7156
+ const bodyText = await res.text();
7157
+ if (tokenResolver.debug) {
7158
+ tokenResolver.log?.(`[debug] response status=${res.status} body=${bodyText}`);
7159
+ }
7160
+ throw new Error(`kintone API error ${res.status}: ${bodyText}`);
7161
+ }
7162
+ if (tokenResolver.debug) {
7163
+ tokenResolver.log?.(`[debug] response status=${res.status}`);
7164
+ }
7165
+ return await res.json();
7166
+ }
7167
+ function shouldRetryWithRecordNumberOrder(path, bodyText) {
7168
+ if (!path.includes("/v1/records.json?")) return false;
7169
+ if (!bodyText.includes('"code":"CB_IL02"')) return false;
7170
+ const queryPart = path.split("query=")[1] ?? "";
7171
+ const query = decodeURIComponent(queryPart.split("&")[0] ?? "");
7172
+ if (!query.includes("limit")) return false;
7173
+ if (!query.includes("offset")) return false;
7174
+ if (query.toLowerCase().includes("order by")) return false;
7175
+ return true;
7176
+ }
7177
+ function rewriteQueryWithRecordNumberOrder(path) {
7178
+ const [base, rest] = path.split("query=");
7179
+ if (!rest) return path;
7180
+ const [encodedQuery, ...tail] = rest.split("&");
7181
+ const query = decodeURIComponent(encodedQuery ?? "");
7182
+ const rewritten = `order by \u30EC\u30B3\u30FC\u30C9\u756A\u53F7 asc ${query}`.trim();
7183
+ const nextQuery = encodeURIComponent(rewritten);
7184
+ return `${base}query=${nextQuery}${tail.length > 0 ? `&${tail.join("&")}` : ""}`;
7185
+ }
7186
+ return {
7187
+ async getRecords(params) {
7188
+ const queryPart = `query=${encodeURIComponent(params.query)}`;
7189
+ const appPart = `app=${encodeURIComponent(String(params.app))}`;
7190
+ const fieldParts = params.fields.map((f) => `fields[]=${encodeURIComponent(f)}`);
7191
+ const qs = [appPart, queryPart, ...fieldParts].join("&");
7192
+ if (tokenResolver.debug) {
7193
+ tokenResolver.log?.(
7194
+ `[debug] getRecords app=${params.app} query="${params.query}" fields=${params.fields.length > 0 ? params.fields.join(",") : "(all)"} auth=${tokenResolver.auth.type}`
7195
+ );
7196
+ }
7197
+ const path = `${apiBasePath}/records.json?${qs}`;
7198
+ try {
7199
+ return await requestJson(
7200
+ path,
7201
+ { method: "GET" },
7202
+ params.app
7203
+ );
7204
+ } catch (err) {
7205
+ const msg = err instanceof Error ? err.message : String(err);
7206
+ if (!shouldRetryWithRecordNumberOrder(path, msg)) throw err;
7207
+ const retryPath = rewriteQueryWithRecordNumberOrder(path);
7208
+ if (tokenResolver.debug) {
7209
+ tokenResolver.log?.("[debug] retry with fallback query order by \u30EC\u30B3\u30FC\u30C9\u756A\u53F7 asc");
7210
+ }
7211
+ return await requestJson(
7212
+ retryPath,
7213
+ { method: "GET" },
7214
+ params.app
7215
+ );
7216
+ }
7217
+ },
7218
+ async postRecords(_params) {
7219
+ const res = await requestJson(
7220
+ `${apiBasePath}/records.json`,
7221
+ {
7222
+ method: "POST",
7223
+ body: JSON.stringify({
7224
+ app: _params.app,
7225
+ records: _params.records
7226
+ })
7227
+ },
7228
+ _params.app
7229
+ );
7230
+ return { ids: res.ids };
7231
+ },
7232
+ async putRecords(_params) {
7233
+ await requestJson(
7234
+ `${apiBasePath}/records.json`,
7235
+ {
7236
+ method: "PUT",
7237
+ body: JSON.stringify({
7238
+ app: _params.app,
7239
+ records: _params.records
7240
+ })
7241
+ },
7242
+ _params.app
7243
+ );
7244
+ },
7245
+ async deleteRecords(_params) {
7246
+ await requestJson(
7247
+ `${apiBasePath}/records.json`,
7248
+ {
7249
+ method: "DELETE",
7250
+ body: JSON.stringify({
7251
+ app: _params.app,
7252
+ ids: _params.ids
7253
+ })
7254
+ },
7255
+ _params.app
7256
+ );
7257
+ },
7258
+ async getApps() {
7259
+ const PAGE = 100;
7260
+ const all = [];
7261
+ let offset = 0;
7262
+ while (true) {
7263
+ const qs = new URLSearchParams();
7264
+ qs.set("limit", String(PAGE));
7265
+ qs.set("offset", String(offset));
7266
+ const res = await requestJson(
7267
+ `${apiBasePath}/apps.json?${qs.toString()}`,
7268
+ { method: "GET" },
7269
+ 0
7270
+ );
7271
+ for (const app of res.apps) {
7272
+ all.push({
7273
+ appId: Number(app.appId),
7274
+ name: app.name,
7275
+ description: app.description
7276
+ });
7277
+ }
7278
+ if (res.apps.length < PAGE) break;
7279
+ offset += PAGE;
7280
+ }
7281
+ return all;
7282
+ },
7283
+ async getFields(appId) {
7284
+ const qs = new URLSearchParams();
7285
+ qs.set("app", String(appId));
7286
+ const res = await requestJson(
7287
+ `${apiBasePath}/app/form/fields.json?${qs.toString()}`,
7288
+ { method: "GET" },
7289
+ appId
7290
+ );
7291
+ return Object.values(res.properties).map((f) => ({
7292
+ code: f.code,
7293
+ label: f.label,
7294
+ fieldType: f.type,
7295
+ optionOrder: toOptionOrderMap(f.options),
7296
+ sortKind: detectSortKind(f.type, f.format)
7297
+ }));
7298
+ }
7299
+ };
7300
+ }
7301
+ function toOptionOrderMap(options) {
7302
+ if (!options || typeof options !== "object") return void 0;
7303
+ const order = {};
7304
+ let hasAny = false;
7305
+ for (const [label, meta] of Object.entries(options)) {
7306
+ const n = Number(meta?.index);
7307
+ if (!Number.isFinite(n)) continue;
7308
+ order[label] = n;
7309
+ hasAny = true;
7310
+ }
7311
+ return hasAny ? order : void 0;
6869
7312
  }
6870
- function resolveRequestGateOptions(base) {
6871
- return {
6872
- ...base,
6873
- maxConcurrent: envInt("KSQL_MAX_CONCURRENT") ?? base.maxConcurrent,
6874
- // KSQL_RETRY=0(リトライ無効)は有効値のため envNonNegativeInt で読む
6875
- maxRetries: envNonNegativeInt("KSQL_RETRY") ?? base.maxRetries
6876
- };
7313
+ function detectSortKind(fieldType, calcFormat) {
7314
+ if (fieldType === "NUMBER" || fieldType === "RECORD_NUMBER") return "number";
7315
+ if (fieldType === "CALC") {
7316
+ if (calcFormat === "NUMBER" || calcFormat === "NUMBER_DIGIT") return "number";
7317
+ return "string";
7318
+ }
7319
+ return void 0;
6877
7320
  }
6878
7321
 
6879
7322
  // src/node/appProfiles.ts
@@ -6908,7 +7351,9 @@ function normalizeAppKey(v) {
6908
7351
  }
6909
7352
  function extractAppIds(sql) {
6910
7353
  const out = /* @__PURE__ */ new Set();
6911
- for (const t of collectAppProfileTokens(sql)) out.add(t.appId);
7354
+ for (const t of collectAppProfileTokens(sql)) {
7355
+ if (t.source === "physical") out.add(t.appId);
7356
+ }
6912
7357
  return [...out];
6913
7358
  }
6914
7359
  function isSqlIdentContinue(ch) {
@@ -6921,16 +7366,42 @@ function isProfileNameChar(ch) {
6921
7366
  const cp = ch.codePointAt(0);
6922
7367
  return cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp >= 48 && cp <= 57 || ch === "_" || ch === "-" || ch === "." || ch === "$";
6923
7368
  }
7369
+ function isAsciiLogicalNameStart(ch) {
7370
+ return /^[A-Za-z]$/.test(ch);
7371
+ }
7372
+ function isAsciiLogicalNameContinue(ch) {
7373
+ return /^[A-Za-z0-9_]$/.test(ch);
7374
+ }
6924
7375
  function tryParseAppProfileToken(sql, start) {
6925
- const head = sql.slice(start, start + 3);
6926
- if (head.toUpperCase() !== "APP") return null;
6927
7376
  const prev = start > 0 ? sql[start - 1] : "";
6928
7377
  if (isSqlIdentContinue(prev)) return null;
6929
- let i = start + 3;
6930
- const digitStart = i;
6931
- while (i < sql.length && /[0-9]/.test(sql[i])) i++;
6932
- const digitEnd = i;
6933
- if (digitEnd === digitStart) return null;
7378
+ let source;
7379
+ let appId;
7380
+ let logicalName;
7381
+ let referenceValueStart;
7382
+ let referenceValueEnd;
7383
+ let i;
7384
+ if (sql.slice(start, start + 5).toUpperCase() === "LAPP_") {
7385
+ source = "logical";
7386
+ i = start + 5;
7387
+ referenceValueStart = i;
7388
+ if (!isAsciiLogicalNameStart(sql[i] ?? "")) return null;
7389
+ i++;
7390
+ while (i < sql.length && isAsciiLogicalNameContinue(sql[i])) i++;
7391
+ referenceValueEnd = i;
7392
+ if (referenceValueEnd - referenceValueStart > 64) return null;
7393
+ logicalName = sql.slice(referenceValueStart, referenceValueEnd).toUpperCase();
7394
+ } else if (sql.slice(start, start + 3).toUpperCase() === "APP") {
7395
+ source = "physical";
7396
+ i = start + 3;
7397
+ referenceValueStart = i;
7398
+ while (i < sql.length && /[0-9]/.test(sql[i])) i++;
7399
+ referenceValueEnd = i;
7400
+ if (referenceValueEnd === referenceValueStart) return null;
7401
+ appId = Number(sql.slice(referenceValueStart, referenceValueEnd));
7402
+ } else {
7403
+ return null;
7404
+ }
6934
7405
  if (sql[i] === "$") {
6935
7406
  i++;
6936
7407
  const subStart = i;
@@ -6948,15 +7419,15 @@ function tryParseAppProfileToken(sql, start) {
6948
7419
  }
6949
7420
  const next = i < sql.length ? sql[i] : "";
6950
7421
  if (isSqlIdentContinue(next)) return null;
6951
- return {
6952
- appId: Number(sql.slice(digitStart, digitEnd)),
7422
+ const common = {
6953
7423
  profile,
6954
7424
  start,
6955
- digitStart,
6956
- digitEnd,
7425
+ referenceValueStart,
7426
+ referenceValueEnd,
6957
7427
  appEnd,
6958
7428
  fullEnd: i
6959
7429
  };
7430
+ return source === "physical" ? { ...common, source, appId } : { ...common, source, logicalName };
6960
7431
  }
6961
7432
  function collectAppProfileTokens(sql) {
6962
7433
  const tokens = [];
@@ -7016,12 +7487,13 @@ function nextVirtualAppId(used) {
7016
7487
  used.add(id);
7017
7488
  return id;
7018
7489
  }
7019
- function normalizeSqlAppProfiles(sql, defaultProfile = "dev") {
7490
+ function normalizeSqlAppProfiles(sql, defaultProfile = "dev", resolutionContext) {
7020
7491
  const tokens = collectAppProfileTokens(sql);
7021
7492
  const hasProfileSyntax = tokens.some((t) => t.profile !== null);
7022
7493
  const profilesByApp = /* @__PURE__ */ new Map();
7023
7494
  const normalizedProfile = (profile) => profile ?? defaultProfile;
7024
7495
  for (const t of tokens) {
7496
+ if (t.source !== "physical") continue;
7025
7497
  const p = normalizedProfile(t.profile);
7026
7498
  let set = profilesByApp.get(t.appId);
7027
7499
  if (!set) {
@@ -7030,7 +7502,24 @@ function normalizeSqlAppProfiles(sql, defaultProfile = "dev") {
7030
7502
  }
7031
7503
  set.add(p.toLowerCase());
7032
7504
  }
7033
- const usedAppIds = new Set(tokens.map((t) => t.appId));
7505
+ const usedAppIds = new Set(
7506
+ tokens.filter((t) => t.source === "physical").map((t) => t.appId)
7507
+ );
7508
+ const resolvedLogicalApps = /* @__PURE__ */ new Map();
7509
+ for (const t of tokens) {
7510
+ if (t.source !== "logical") continue;
7511
+ const pLower = normalizedProfile(t.profile).toLowerCase();
7512
+ const logicalKey = `logical:${t.logicalName}@${pLower}`;
7513
+ if (resolvedLogicalApps.has(logicalKey)) continue;
7514
+ if (!resolutionContext) {
7515
+ throw new Error(
7516
+ `ArgumentError: logical app LAPP_${t.logicalName}@${pLower} requires logicalApps configuration.`
7517
+ );
7518
+ }
7519
+ const resolvedAppId = resolutionContext.resolveLogicalApp(t.logicalName, pLower);
7520
+ resolvedLogicalApps.set(logicalKey, resolvedAppId);
7521
+ usedAppIds.add(resolvedAppId);
7522
+ }
7034
7523
  const pairToMapped = /* @__PURE__ */ new Map();
7035
7524
  const appBindingByMappedApp = /* @__PURE__ */ new Map();
7036
7525
  for (const [appId, pSet] of profilesByApp.entries()) {
@@ -7038,39 +7527,117 @@ function normalizeSqlAppProfiles(sql, defaultProfile = "dev") {
7038
7527
  if (profiles.length <= 1) continue;
7039
7528
  for (const pLower of profiles) {
7040
7529
  const mapped = nextVirtualAppId(usedAppIds);
7041
- pairToMapped.set(`${appId}@${pLower}`, mapped);
7042
- appBindingByMappedApp.set(mapped, { appId, profile: pLower });
7530
+ pairToMapped.set(`physical:${appId}@${pLower}`, mapped);
7531
+ appBindingByMappedApp.set(mapped, {
7532
+ source: "physical",
7533
+ mappedAppId: mapped,
7534
+ appId,
7535
+ profile: pLower
7536
+ });
7043
7537
  }
7044
7538
  }
7045
7539
  const out = [];
7540
+ const rewriteSegments = [];
7541
+ let normalizedLength = 0;
7046
7542
  let cursor = 0;
7543
+ const appendSegment = (text, sourceStart, sourceEnd, bindingMappedAppId) => {
7544
+ if (!text && sourceStart === sourceEnd) return;
7545
+ const normalizedStart = normalizedLength;
7546
+ out.push(text);
7547
+ normalizedLength += text.length;
7548
+ rewriteSegments.push({
7549
+ normalizedStart,
7550
+ normalizedEnd: normalizedLength,
7551
+ sourceStart,
7552
+ sourceEnd,
7553
+ ...bindingMappedAppId === void 0 ? {} : { bindingMappedAppId }
7554
+ });
7555
+ };
7047
7556
  for (const t of tokens) {
7048
7557
  const p = normalizedProfile(t.profile);
7049
7558
  const pLower = p.toLowerCase();
7050
- const mapped = pairToMapped.get(`${t.appId}@${pLower}`) ?? t.appId;
7051
- appBindingByMappedApp.set(mapped, { appId: t.appId, profile: pLower });
7052
- out.push(sql.slice(cursor, t.start));
7053
- out.push(sql.slice(t.start, t.digitStart));
7054
- out.push(String(mapped));
7055
- out.push(sql.slice(t.digitEnd, t.appEnd));
7559
+ let binding;
7560
+ if (t.source === "physical") {
7561
+ const mapped = pairToMapped.get(`physical:${t.appId}@${pLower}`) ?? t.appId;
7562
+ binding = {
7563
+ source: "physical",
7564
+ mappedAppId: mapped,
7565
+ appId: t.appId,
7566
+ profile: pLower
7567
+ };
7568
+ } else {
7569
+ const logicalKey = `logical:${t.logicalName}@${pLower}`;
7570
+ let mapped = pairToMapped.get(logicalKey);
7571
+ if (mapped === void 0) {
7572
+ mapped = nextVirtualAppId(usedAppIds);
7573
+ pairToMapped.set(logicalKey, mapped);
7574
+ }
7575
+ binding = {
7576
+ source: "logical",
7577
+ logicalName: t.logicalName,
7578
+ mappedAppId: mapped,
7579
+ appId: resolvedLogicalApps.get(logicalKey),
7580
+ profile: pLower
7581
+ };
7582
+ }
7583
+ appBindingByMappedApp.set(binding.mappedAppId, binding);
7584
+ appendSegment(sql.slice(cursor, t.start), cursor, t.start);
7585
+ const subtableSuffix = sql.slice(t.referenceValueEnd, t.appEnd);
7586
+ const normalizedReference = t.source === "physical" ? `${sql.slice(t.start, t.referenceValueStart)}${binding.mappedAppId}${subtableSuffix}` : `APP${binding.mappedAppId}${subtableSuffix}`;
7587
+ appendSegment(
7588
+ normalizedReference,
7589
+ t.start,
7590
+ t.fullEnd,
7591
+ binding.mappedAppId
7592
+ );
7056
7593
  cursor = t.fullEnd;
7057
7594
  }
7058
- out.push(sql.slice(cursor));
7595
+ appendSegment(sql.slice(cursor), cursor, sql.length);
7059
7596
  return {
7060
7597
  normalizedSql: out.join(""),
7061
7598
  hasProfileSyntax,
7062
- appBindingByMappedApp
7599
+ appBindingByMappedApp,
7600
+ rewriteSegments
7063
7601
  };
7064
7602
  }
7065
7603
  function buildCacheContext(defaultProfile, appBindingByMappedApp) {
7066
7604
  if (appBindingByMappedApp.size === 0) return `default:${defaultProfile.toLowerCase()}`;
7067
- const pairs = [...appBindingByMappedApp.entries()].sort((a, b) => a[0] - b[0]).map(([mappedAppId, b]) => `M${mappedAppId}=APP${b.appId}@${b.profile}`);
7605
+ 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}`);
7068
7606
  return `apps:${pairs.join(",")}`;
7069
7607
  }
7070
- function formatResolvedAppProfiles(sql, defaultProfile) {
7071
- const parsed = normalizeSqlAppProfiles(sql, defaultProfile);
7608
+ function formatResolvedAppProfiles(sql, defaultProfile, resolutionContext) {
7609
+ const parsed = normalizeSqlAppProfiles(sql, defaultProfile, resolutionContext);
7072
7610
  if (parsed.appBindingByMappedApp.size === 0) return "(none)";
7073
- return [...parsed.appBindingByMappedApp.values()].map((b) => `APP${b.appId}->${b.profile}`).join(", ");
7611
+ return [...parsed.appBindingByMappedApp.values()].map((b) => b.source === "logical" ? `LAPP_${b.logicalName}@${b.profile}->APP${b.appId}@${b.profile}` : `APP${b.appId}->${b.profile}`).join(", ");
7612
+ }
7613
+
7614
+ // src/node/runtime.ts
7615
+ function resolveTokenByMappedApp(args) {
7616
+ const tokenByMappedApp = /* @__PURE__ */ new Map();
7617
+ const tokenByPhysicalApp = /* @__PURE__ */ new Map();
7618
+ const missing = [];
7619
+ for (const mappedAppId of args.mappedAppIds) {
7620
+ const binding = args.bindings.get(mappedAppId);
7621
+ if (!binding && args.logicalBindingLabels.has(mappedAppId)) {
7622
+ throw new Error(`InternalError: binding is missing for logical app ${args.logicalBindingLabels.get(mappedAppId)}.`);
7623
+ }
7624
+ const appId = binding?.appId ?? mappedAppId;
7625
+ const profile = binding?.profile ?? args.profileName;
7626
+ const fromMap = args.effectiveTokenMap[`APP${appId}`];
7627
+ if (fromMap) {
7628
+ const token = resolveTokenValue(fromMap);
7629
+ tokenByMappedApp.set(mappedAppId, token);
7630
+ tokenByPhysicalApp.set(appId, token);
7631
+ continue;
7632
+ }
7633
+ if (binding?.source !== "logical" && args.mappedAppIds.length === 1 && args.singleToken) {
7634
+ tokenByMappedApp.set(mappedAppId, args.singleToken);
7635
+ tokenByPhysicalApp.set(appId, args.singleToken);
7636
+ continue;
7637
+ }
7638
+ missing.push(binding?.source === "logical" ? `LAPP_${binding.logicalName} (APP${appId})@${profile}` : `APP${appId}@${profile}`);
7639
+ }
7640
+ return { tokenByMappedApp, tokenByPhysicalApp, missing };
7074
7641
  }
7075
7642
 
7076
7643
  // src/cli/consoleInput.ts
@@ -7095,366 +7662,107 @@ function decideRun(buffer) {
7095
7662
  if (buffer.trim().length === 0) {
7096
7663
  return { kind: "error", message: "ArgumentError: input buffer is empty (nothing to :run)" };
7097
7664
  }
7098
- const parsed = tryParseStatements(buffer);
7099
- if (parsed.kind === "fail") return { kind: "error", message: parsed.message };
7100
- return { kind: "execute-batch", sql: buffer };
7101
- }
7102
- function isBatchConstruction(buffer) {
7103
- return /^create\s+temp\s+table\b/i.test(stripLeadingCommentsAndWs(buffer));
7104
- }
7105
- function stripLeadingCommentsAndWs(sql) {
7106
- let s = sql;
7107
- while (true) {
7108
- const before = s;
7109
- s = s.replace(/^\s+/, "");
7110
- s = s.replace(/^--[^\n]*(\n|$)/, "");
7111
- s = s.replace(/^\/\*[\s\S]*?\*\//, "");
7112
- if (s === before) return s;
7113
- }
7114
- }
7115
- function toParseInput(sql) {
7116
- try {
7117
- return normalizeSqlAppProfiles(sql, "console").normalizedSql;
7118
- } catch {
7119
- return sql;
7120
- }
7121
- }
7122
- function tryParseStatements(sql) {
7123
- try {
7124
- const stmts = new Parser(new Lexer(toParseInput(sql)).tokenize()).parseStatements();
7125
- return {
7126
- kind: "ok",
7127
- count: stmts.length,
7128
- hasTempTable: stmts.some(
7129
- (s) => s.type === "CREATE_TEMP_TABLE" || s.type === "DROP_TEMP_TABLE"
7130
- )
7131
- };
7132
- } catch (e) {
7133
- if (e instanceof LexError) {
7134
- return { kind: "fail", continuable: e.unterminated, message: e.message };
7135
- }
7136
- if (e instanceof ParseError) {
7137
- return { kind: "fail", continuable: e.token.kind === "EOF" /* EOF */, message: e.message };
7138
- }
7139
- throw e;
7140
- }
7141
- }
7142
-
7143
- // src/api/requestGate.ts
7144
- var DEFAULT_MAX_CONCURRENT = 10;
7145
- var DEFAULT_MAX_RETRIES = 3;
7146
- var DEFAULT_BASE_DELAY_MS = 500;
7147
- var DEFAULT_MAX_DELAY_MS = 8e3;
7148
- var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
7149
- function isRetryableError(err) {
7150
- if (!(err instanceof Error)) return false;
7151
- const status = err.message.match(/^kintone API error (\d{3}):/);
7152
- if (status) return RETRYABLE_STATUSES.has(Number(status[1]));
7153
- if (err.name === "AbortError" || err.name === "TimeoutError") return true;
7154
- if (/fetch failed/i.test(err.message)) return true;
7155
- return false;
7156
- }
7157
- var RequestGate = class {
7158
- constructor(options = {}) {
7159
- this.active = 0;
7160
- this.waiters = [];
7161
- this.maxConcurrent = clampInt(options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT, 1, 50);
7162
- this.maxRetries = clampInt(options.maxRetries ?? DEFAULT_MAX_RETRIES, 0, 10);
7163
- this.baseDelayMs = clampInt(options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS, 1, 6e4);
7164
- this.maxDelayMs = Math.max(
7165
- clampInt(options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS, 1, 6e5),
7166
- this.baseDelayMs
7167
- );
7168
- this.sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
7169
- this.random = options.random ?? Math.random;
7170
- }
7171
- /** 現在の同時実行数(テスト・診断用) */
7172
- get activeCount() {
7173
- return this.active;
7174
- }
7175
- get limit() {
7176
- return this.maxConcurrent;
7177
- }
7178
- /** 解決済みの GET リトライ回数(テスト・診断用) */
7179
- get retries() {
7180
- return this.maxRetries;
7181
- }
7182
- /** 解決済みのバックオフ初期値ミリ秒(テスト・診断用) */
7183
- get retryBaseDelayMs() {
7184
- return this.baseDelayMs;
7185
- }
7186
- /** 解決済みのバックオフ上限ミリ秒(テスト・診断用) */
7187
- get retryMaxDelayMs() {
7188
- return this.maxDelayMs;
7189
- }
7190
- /** GET 系: セマフォ + リトライ付きで実行する */
7191
- async runReadOnly(fn) {
7192
- let attempt = 0;
7193
- while (true) {
7194
- try {
7195
- return await this.withSlot(fn);
7196
- } catch (err) {
7197
- if (attempt >= this.maxRetries || !isRetryableError(err)) throw err;
7198
- await this.sleep(this.backoffDelay(attempt));
7199
- attempt += 1;
7200
- }
7201
- }
7202
- }
7203
- /** 書き込み系: セマフォのみ(リトライしない — 二重実行防止) */
7204
- async runMutation(fn) {
7205
- return this.withSlot(fn);
7206
- }
7207
- async withSlot(fn) {
7208
- await this.acquire();
7209
- try {
7210
- return await fn();
7211
- } finally {
7212
- this.release();
7213
- }
7214
- }
7215
- async acquire() {
7216
- if (this.active < this.maxConcurrent) {
7217
- this.active += 1;
7218
- return;
7219
- }
7220
- await new Promise((resolve2) => this.waiters.push(resolve2));
7221
- this.active += 1;
7222
- }
7223
- release() {
7224
- this.active -= 1;
7225
- const next = this.waiters.shift();
7226
- if (next) next();
7227
- }
7228
- /** 指数バックオフ + ジッタ(attempt: 0 始まり) */
7229
- backoffDelay(attempt) {
7230
- const base = Math.min(this.baseDelayMs * 2 ** attempt, this.maxDelayMs);
7231
- const jitter = 1 + (this.random() - 0.5) * 0.5;
7232
- return Math.round(base * jitter);
7233
- }
7234
- };
7235
- function withRequestGate(client, gate) {
7236
- return {
7237
- getRecords: (params) => gate.runReadOnly(() => client.getRecords(params)),
7238
- getApps: () => gate.runReadOnly(() => client.getApps()),
7239
- getFields: (appId) => gate.runReadOnly(() => client.getFields(appId)),
7240
- postRecords: (params) => gate.runMutation(() => client.postRecords(params)),
7241
- putRecords: (params) => gate.runMutation(() => client.putRecords(params)),
7242
- deleteRecords: (params) => gate.runMutation(() => client.deleteRecords(params))
7243
- };
7665
+ const parsed = tryParseStatements(buffer);
7666
+ if (parsed.kind === "fail") return { kind: "error", message: parsed.message };
7667
+ return { kind: "execute-batch", sql: buffer };
7244
7668
  }
7245
- var globalGate = null;
7246
- function getGlobalRequestGate(options) {
7247
- if (globalGate === null) {
7248
- globalGate = new RequestGate(
7249
- typeof options === "number" ? { maxConcurrent: options } : options ?? {}
7250
- );
7669
+ function isBatchConstruction(buffer) {
7670
+ return /^create\s+temp\s+table\b/i.test(stripLeadingCommentsAndWs(buffer));
7671
+ }
7672
+ function stripLeadingCommentsAndWs(sql) {
7673
+ let s = sql;
7674
+ while (true) {
7675
+ const before = s;
7676
+ s = s.replace(/^\s+/, "");
7677
+ s = s.replace(/^--[^\n]*(\n|$)/, "");
7678
+ s = s.replace(/^\/\*[\s\S]*?\*\//, "");
7679
+ if (s === before) return s;
7251
7680
  }
7252
- return globalGate;
7253
7681
  }
7254
- function clampInt(v, min, max) {
7255
- if (!Number.isFinite(v)) return min;
7256
- return Math.max(min, Math.min(max, Math.trunc(v)));
7682
+ function toParseInput(sql) {
7683
+ try {
7684
+ return normalizeSqlAppProfiles(sql, "console").normalizedSql;
7685
+ } catch {
7686
+ return sql;
7687
+ }
7257
7688
  }
7258
-
7259
- // src/cli/nodeKintoneClient.ts
7260
- function createNodeKintoneClient(baseUrl, tokenResolver) {
7261
- const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
7262
- const apiBasePath = tokenResolver.guestSpaceId && tokenResolver.guestSpaceId > 0 ? `/k/guest/${tokenResolver.guestSpaceId}/v1` : "/k/v1";
7263
- async function requestJson(path, init, appIdForToken) {
7264
- const headers = new Headers(init.headers ?? {});
7265
- if (tokenResolver.auth.type === "token") {
7266
- headers.set("X-Cybozu-API-Token", tokenResolver.auth.resolveToken(appIdForToken));
7267
- } else {
7268
- const credentials = `${tokenResolver.auth.username}:${tokenResolver.auth.password}`;
7269
- const encoded = Buffer.from(credentials, "utf-8").toString("base64");
7270
- headers.set("X-Cybozu-Authorization", encoded);
7271
- }
7272
- headers.set("Accept", "application/json");
7273
- const method = String(init.method ?? "GET").toUpperCase();
7274
- if (method !== "GET" && method !== "HEAD") {
7275
- headers.set("Content-Type", "application/json");
7689
+ function tryParseStatements(sql) {
7690
+ try {
7691
+ const stmts = new Parser(new Lexer(toParseInput(sql)).tokenize()).parseStatements();
7692
+ return {
7693
+ kind: "ok",
7694
+ count: stmts.length,
7695
+ hasTempTable: stmts.some(
7696
+ (s) => s.type === "CREATE_TEMP_TABLE" || s.type === "DROP_TEMP_TABLE"
7697
+ )
7698
+ };
7699
+ } catch (e) {
7700
+ if (e instanceof LexError) {
7701
+ return { kind: "fail", continuable: e.unterminated, message: e.message };
7276
7702
  }
7277
- const timeoutMs = tokenResolver.timeoutMs ?? 3e4;
7278
- const url = `${normalizedBaseUrl}${path}`;
7279
- if (tokenResolver.debug) {
7280
- tokenResolver.log?.(`[debug] request ${String(init.method ?? "GET")} ${url}`);
7281
- if (tokenResolver.debugHeaders) {
7282
- const authHeader = headers.get("X-Cybozu-API-Token") ? "X-Cybozu-API-Token=***" : headers.get("X-Cybozu-Authorization") ? "X-Cybozu-Authorization=***" : "Auth=(none)";
7283
- tokenResolver.log?.(
7284
- `[debug] request-headers ${authHeader} Content-Type=${headers.get("Content-Type") ?? "(none)"} Accept=${headers.get("Accept") ?? "(none)"}`
7285
- );
7286
- }
7703
+ if (e instanceof ParseError) {
7704
+ return { kind: "fail", continuable: e.token.kind === "EOF" /* EOF */, message: e.message };
7287
7705
  }
7288
- const res = await fetch(url, {
7289
- ...init,
7290
- headers,
7291
- signal: AbortSignal.timeout(timeoutMs)
7292
- });
7293
- if (!res.ok) {
7294
- const bodyText = await res.text();
7295
- if (tokenResolver.debug) {
7296
- tokenResolver.log?.(`[debug] response status=${res.status} body=${bodyText}`);
7706
+ throw e;
7707
+ }
7708
+ }
7709
+
7710
+ // src/node/sqlDiagnostics.ts
7711
+ function restoreSqlDiagnosticValue(value, bindings) {
7712
+ if (typeof value === "string") {
7713
+ const dmlTarget = value.match(/^(\s*target:\s*)APP(\d+) \((\d+)\)\s*$/);
7714
+ if (dmlTarget && dmlTarget[2] === dmlTarget[3]) {
7715
+ const binding = bindings.get(Number(dmlTarget[2]));
7716
+ if (binding) {
7717
+ const target = binding.source === "logical" ? `LAPP_${binding.logicalName} -> APP${binding.appId}@${binding.profile}` : `APP${binding.appId}@${binding.profile}`;
7718
+ return `${dmlTarget[1]}${target}`;
7297
7719
  }
7298
- throw new Error(`kintone API error ${res.status}: ${bodyText}`);
7299
7720
  }
7300
- if (tokenResolver.debug) {
7301
- tokenResolver.log?.(`[debug] response status=${res.status}`);
7721
+ let restored = value;
7722
+ for (const binding of bindings.values()) {
7723
+ const internal = `APP${binding.mappedAppId}`;
7724
+ const display = binding.source === "logical" ? `LAPP_${binding.logicalName}@${binding.profile}` : `APP${binding.appId}@${binding.profile}`;
7725
+ restored = restored.split(`${internal} (${binding.mappedAppId})`).join(display).split(internal).join(display);
7302
7726
  }
7303
- return await res.json();
7727
+ return restored;
7304
7728
  }
7305
- function shouldRetryWithRecordNumberOrder(path, bodyText) {
7306
- if (!path.includes("/v1/records.json?")) return false;
7307
- if (!bodyText.includes('"code":"CB_IL02"')) return false;
7308
- const queryPart = path.split("query=")[1] ?? "";
7309
- const query = decodeURIComponent(queryPart.split("&")[0] ?? "");
7310
- if (!query.includes("limit")) return false;
7311
- if (!query.includes("offset")) return false;
7312
- if (query.toLowerCase().includes("order by")) return false;
7313
- return true;
7729
+ if (Array.isArray(value)) {
7730
+ return value.map((item) => restoreSqlDiagnosticValue(item, bindings));
7314
7731
  }
7315
- function rewriteQueryWithRecordNumberOrder(path) {
7316
- const [base, rest] = path.split("query=");
7317
- if (!rest) return path;
7318
- const [encodedQuery, ...tail] = rest.split("&");
7319
- const query = decodeURIComponent(encodedQuery ?? "");
7320
- const rewritten = `order by \u30EC\u30B3\u30FC\u30C9\u756A\u53F7 asc ${query}`.trim();
7321
- const nextQuery = encodeURIComponent(rewritten);
7322
- return `${base}query=${nextQuery}${tail.length > 0 ? `&${tail.join("&")}` : ""}`;
7732
+ if (value !== null && typeof value === "object") {
7733
+ return Object.fromEntries(
7734
+ Object.entries(value).map(([key, item]) => [key, restoreSqlDiagnosticValue(item, bindings)])
7735
+ );
7323
7736
  }
7324
- return {
7325
- async getRecords(params) {
7326
- const queryPart = `query=${encodeURIComponent(params.query)}`;
7327
- const appPart = `app=${encodeURIComponent(String(params.app))}`;
7328
- const fieldParts = params.fields.map((f) => `fields[]=${encodeURIComponent(f)}`);
7329
- const qs = [appPart, queryPart, ...fieldParts].join("&");
7330
- if (tokenResolver.debug) {
7331
- tokenResolver.log?.(
7332
- `[debug] getRecords app=${params.app} query="${params.query}" fields=${params.fields.length > 0 ? params.fields.join(",") : "(all)"} auth=${tokenResolver.auth.type}`
7333
- );
7334
- }
7335
- const path = `${apiBasePath}/records.json?${qs}`;
7336
- try {
7337
- return await requestJson(
7338
- path,
7339
- { method: "GET" },
7340
- params.app
7341
- );
7342
- } catch (err) {
7343
- const msg = err instanceof Error ? err.message : String(err);
7344
- if (!shouldRetryWithRecordNumberOrder(path, msg)) throw err;
7345
- const retryPath = rewriteQueryWithRecordNumberOrder(path);
7346
- if (tokenResolver.debug) {
7347
- tokenResolver.log?.("[debug] retry with fallback query order by \u30EC\u30B3\u30FC\u30C9\u756A\u53F7 asc");
7348
- }
7349
- return await requestJson(
7350
- retryPath,
7351
- { method: "GET" },
7352
- params.app
7353
- );
7354
- }
7355
- },
7356
- async postRecords(_params) {
7357
- const res = await requestJson(
7358
- `${apiBasePath}/records.json`,
7359
- {
7360
- method: "POST",
7361
- body: JSON.stringify({
7362
- app: _params.app,
7363
- records: _params.records
7364
- })
7365
- },
7366
- _params.app
7367
- );
7368
- return { ids: res.ids };
7369
- },
7370
- async putRecords(_params) {
7371
- await requestJson(
7372
- `${apiBasePath}/records.json`,
7373
- {
7374
- method: "PUT",
7375
- body: JSON.stringify({
7376
- app: _params.app,
7377
- records: _params.records
7378
- })
7379
- },
7380
- _params.app
7381
- );
7382
- },
7383
- async deleteRecords(_params) {
7384
- await requestJson(
7385
- `${apiBasePath}/records.json`,
7386
- {
7387
- method: "DELETE",
7388
- body: JSON.stringify({
7389
- app: _params.app,
7390
- ids: _params.ids
7391
- })
7392
- },
7393
- _params.app
7737
+ return value;
7738
+ }
7739
+ function restoreSqlContextError(err, sourceSql, context) {
7740
+ if (!(err instanceof Error)) return err;
7741
+ let message = err.message;
7742
+ for (const binding of context.bindings.values()) {
7743
+ if (binding.source === "logical") {
7744
+ message = message.split(`APP${binding.mappedAppId}`).join(`LAPP_${binding.logicalName}@${binding.profile}`);
7745
+ }
7746
+ }
7747
+ const token = err.token;
7748
+ if (typeof token?.pos === "number") {
7749
+ const segment = context.rewriteSegments.find(
7750
+ (candidate) => token.pos >= candidate.normalizedStart && token.pos < candidate.normalizedEnd
7751
+ );
7752
+ if (segment) {
7753
+ const sourcePos = segment.sourceStart + Math.min(
7754
+ token.pos - segment.normalizedStart,
7755
+ Math.max(0, segment.sourceEnd - segment.sourceStart - 1)
7394
7756
  );
7395
- },
7396
- async getApps() {
7397
- const PAGE = 100;
7398
- const all = [];
7399
- let offset = 0;
7400
- while (true) {
7401
- const qs = new URLSearchParams();
7402
- qs.set("limit", String(PAGE));
7403
- qs.set("offset", String(offset));
7404
- const res = await requestJson(
7405
- `${apiBasePath}/apps.json?${qs.toString()}`,
7406
- { method: "GET" },
7407
- 0
7408
- );
7409
- for (const app of res.apps) {
7410
- all.push({
7411
- appId: Number(app.appId),
7412
- name: app.name,
7413
- description: app.description
7414
- });
7415
- }
7416
- if (res.apps.length < PAGE) break;
7417
- offset += PAGE;
7757
+ message = message.replace(/(位置 \d+、トークン:/, `\uFF08\u4F4D\u7F6E ${sourcePos}\u3001\u30C8\u30FC\u30AF\u30F3:`);
7758
+ const originalRef = sourceSql.slice(segment.sourceStart, segment.sourceEnd);
7759
+ if (segment.bindingMappedAppId !== void 0 && originalRef) {
7760
+ message = message.replace(/(トークン: 「)[^」]*(」)/, `$1${originalRef}$2`);
7418
7761
  }
7419
- return all;
7420
- },
7421
- async getFields(appId) {
7422
- const qs = new URLSearchParams();
7423
- qs.set("app", String(appId));
7424
- const res = await requestJson(
7425
- `${apiBasePath}/app/form/fields.json?${qs.toString()}`,
7426
- { method: "GET" },
7427
- appId
7428
- );
7429
- return Object.values(res.properties).map((f) => ({
7430
- code: f.code,
7431
- label: f.label,
7432
- fieldType: f.type,
7433
- optionOrder: toOptionOrderMap(f.options),
7434
- sortKind: detectSortKind(f.type, f.format)
7435
- }));
7436
7762
  }
7437
- };
7438
- }
7439
- function toOptionOrderMap(options) {
7440
- if (!options || typeof options !== "object") return void 0;
7441
- const order = {};
7442
- let hasAny = false;
7443
- for (const [label, meta] of Object.entries(options)) {
7444
- const n = Number(meta?.index);
7445
- if (!Number.isFinite(n)) continue;
7446
- order[label] = n;
7447
- hasAny = true;
7448
- }
7449
- return hasAny ? order : void 0;
7450
- }
7451
- function detectSortKind(fieldType, calcFormat) {
7452
- if (fieldType === "NUMBER" || fieldType === "RECORD_NUMBER") return "number";
7453
- if (fieldType === "CALC") {
7454
- if (calcFormat === "NUMBER" || calcFormat === "NUMBER_DIGIT") return "number";
7455
- return "string";
7456
7763
  }
7457
- return void 0;
7764
+ err.message = message;
7765
+ return err;
7458
7766
  }
7459
7767
 
7460
7768
  // src/cli/index.ts
@@ -7835,7 +8143,7 @@ function loadConfig(configPath) {
7835
8143
  const raw = (0, import_fs2.readFileSync)(configPath, "utf-8");
7836
8144
  return JSON.parse(raw);
7837
8145
  }
7838
- function resolveTokenValue(raw) {
8146
+ function resolveTokenValue2(raw) {
7839
8147
  if (raw.startsWith("env:")) {
7840
8148
  const envKey = raw.slice(4);
7841
8149
  const envVal = process.env[envKey];
@@ -7866,12 +8174,12 @@ function envFormat(name) {
7866
8174
  const v = envString2(name);
7867
8175
  return normalizeOutputFormat(v);
7868
8176
  }
7869
- function envOnLimit(name) {
8177
+ function envOnLimit2(name) {
7870
8178
  const v = envString2(name);
7871
8179
  if (v === "error" || v === "truncate") return v;
7872
8180
  return null;
7873
8181
  }
7874
- function envAuth(name) {
8182
+ function envAuth2(name) {
7875
8183
  const v = envString2(name);
7876
8184
  if (v === "token" || v === "userpass" || v === "auto") return v;
7877
8185
  return null;
@@ -8296,10 +8604,10 @@ async function runWithArgvCapture(argv) {
8296
8604
  process.stdout.write = original;
8297
8605
  }
8298
8606
  }
8299
- async function confirmDmlInConsole(sql, opts, queue, defaultProfile = "dev") {
8607
+ async function confirmDmlInConsole(sql, opts, queue, defaultProfile = "dev", resolutionContext) {
8300
8608
  if (!opts.allowDml || opts.yes || opts.dryRun) return true;
8301
8609
  try {
8302
- const normalized = normalizeSqlAppProfiles(sql, defaultProfile);
8610
+ const normalized = normalizeSqlAppProfiles(sql, defaultProfile, resolutionContext);
8303
8611
  const statements = parseSqlStatements(normalized.normalizedSql);
8304
8612
  if (statements.length > 1) {
8305
8613
  const analysis = analyzeBatch(statements);
@@ -8387,6 +8695,14 @@ async function runConsole(base) {
8387
8695
  let buffer = "";
8388
8696
  let emptyPromptSigintArmed = false;
8389
8697
  const queue = createConsoleEventQueue(rl);
8698
+ const consoleConfigPath = base.configPath ?? envString2("KSQL_CONFIG") ?? "./ksql.config.json";
8699
+ let consoleConfig = {};
8700
+ try {
8701
+ consoleConfig = validateKsqlConfig(loadConfig(consoleConfigPath));
8702
+ } catch {
8703
+ }
8704
+ const consoleResolutionContext = () => createAppResolutionContext(consoleConfig, profile ?? "dev");
8705
+ const formatConsoleProfiles = (sql) => formatResolvedAppProfiles(sql, profile ?? "dev", consoleResolutionContext());
8390
8706
  process.stdout.write("kSQL Console (type :help)\n");
8391
8707
  process.stdout.write(
8392
8708
  [
@@ -8444,7 +8760,7 @@ async function runConsole(base) {
8444
8760
  allowDml: base.allowDml,
8445
8761
  yes: base.yes,
8446
8762
  dryRun
8447
- }, queue, profile ?? "dev");
8763
+ }, queue, profile ?? "dev", consoleResolutionContext());
8448
8764
  if (!ok) {
8449
8765
  process.stderr.write("DML was cancelled by user.\n");
8450
8766
  continue;
@@ -8452,7 +8768,7 @@ async function runConsole(base) {
8452
8768
  }
8453
8769
  buffer = "";
8454
8770
  lastSql = sql2;
8455
- lastResolvedProfiles = formatResolvedAppProfiles(sql2, profile ?? "dev");
8771
+ lastResolvedProfiles = formatConsoleProfiles(sql2);
8456
8772
  history.push(sql2);
8457
8773
  appendHistory(sql2);
8458
8774
  const { code: code2, stdout: stdout2 } = await runWithArgvCapture(buildReplExecArgvWithProfile(base, sql2, dryRun, format, profile));
@@ -8559,13 +8875,13 @@ async function runConsole(base) {
8559
8875
  allowDml: base.allowDml,
8560
8876
  yes: base.yes,
8561
8877
  dryRun
8562
- }, queue, profile ?? "dev");
8878
+ }, queue, profile ?? "dev", consoleResolutionContext());
8563
8879
  if (!ok) {
8564
8880
  process.stderr.write("DML was cancelled by user.\n");
8565
8881
  continue;
8566
8882
  }
8567
8883
  lastSql = sql2;
8568
- lastResolvedProfiles = formatResolvedAppProfiles(sql2, profile ?? "dev");
8884
+ lastResolvedProfiles = formatConsoleProfiles(sql2);
8569
8885
  process.stdout.write(`rerun: ${sql2.replace(/\s+/g, " ").trim()}
8570
8886
  `);
8571
8887
  const { code: code2, stdout: stdout2 } = await runWithArgvCapture(buildReplExecArgvWithProfile(base, sql2, dryRun, format, profile));
@@ -8637,14 +8953,14 @@ async function runConsole(base) {
8637
8953
  allowDml: base.allowDml,
8638
8954
  yes: base.yes,
8639
8955
  dryRun
8640
- }, queue, profile ?? "dev");
8956
+ }, queue, profile ?? "dev", consoleResolutionContext());
8641
8957
  if (!ok) {
8642
8958
  process.stderr.write("DML was cancelled by user.\n");
8643
8959
  continue;
8644
8960
  }
8645
8961
  }
8646
8962
  lastSql = sql;
8647
- lastResolvedProfiles = formatResolvedAppProfiles(sql, profile ?? "dev");
8963
+ lastResolvedProfiles = formatConsoleProfiles(sql);
8648
8964
  history.push(sql);
8649
8965
  appendHistory(sql);
8650
8966
  const { code, stdout } = await runWithArgvCapture(buildReplExecArgvWithProfile(base, sql, dryRun, format, profile));
@@ -8698,6 +9014,8 @@ async function run() {
8698
9014
  const profileName = args.profile ?? envString2("KSQL_PROFILE") ?? config.defaultProfile ?? "dev";
8699
9015
  const profile = config.profiles?.[profileName] ?? {};
8700
9016
  let sql = null;
9017
+ let sourceSql = null;
9018
+ let sqlDiagnosticContext = null;
8701
9019
  let hasProfileSyntax = false;
8702
9020
  let appBindingByMappedApp = /* @__PURE__ */ new Map();
8703
9021
  let parsedStmt = null;
@@ -8715,9 +9033,16 @@ async function run() {
8715
9033
  process.stderr.write("ArgumentError: SQL is empty.\n");
8716
9034
  return 2;
8717
9035
  }
9036
+ sourceSql = sql;
8718
9037
  try {
8719
- const normalized = normalizeSqlAppProfiles(sql, profileName);
9038
+ const validatedConfig = validateKsqlConfig(config);
9039
+ const resolutionContext = createAppResolutionContext(validatedConfig, profileName);
9040
+ const normalized = normalizeSqlAppProfiles(sql, profileName, resolutionContext);
9041
+ for (const binding of normalized.appBindingByMappedApp.values()) {
9042
+ if (binding.source === "physical") resolutionContext.assertPhysicalAppAllowed(binding.profile);
9043
+ }
8720
9044
  sql = normalized.normalizedSql;
9045
+ sqlDiagnosticContext = normalized;
8721
9046
  hasProfileSyntax = normalized.hasProfileSyntax;
8722
9047
  appBindingByMappedApp = normalized.appBindingByMappedApp;
8723
9048
  } catch (err) {
@@ -8746,14 +9071,18 @@ async function run() {
8746
9071
  }
8747
9072
  }
8748
9073
  } catch (err) {
8749
- process.stderr.write(`${err instanceof Error ? err.message : String(err)}
9074
+ const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
9075
+ bindings: sqlDiagnosticContext.appBindingByMappedApp,
9076
+ rewriteSegments: sqlDiagnosticContext.rewriteSegments
9077
+ }) : err;
9078
+ process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
8750
9079
  `);
8751
9080
  return 1;
8752
9081
  }
8753
9082
  }
8754
9083
  const maxRecords = args.maxRecords ?? envInt2("KSQL_MAX_RECORDS") ?? profile.query?.maxRecords ?? 500;
8755
9084
  const fetchParallel = args.fetchParallel ?? envInt2("KSQL_FETCH_PARALLEL") ?? profile.query?.fetchParallel ?? 3;
8756
- const onLimit = args.onLimit ?? envOnLimit("KSQL_ON_LIMIT") ?? profile.query?.onLimit ?? "error";
9085
+ const onLimit = args.onLimit ?? envOnLimit2("KSQL_ON_LIMIT") ?? profile.query?.onLimit ?? "error";
8757
9086
  const timeout = args.timeout ?? envInt2("KSQL_TIMEOUT") ?? profile.query?.timeout ?? 3e4;
8758
9087
  const tempTableMaxRows = args.tempTableMaxRows ?? envInt2("KSQL_TEMP_TABLE_MAX_ROWS") ?? profile.query?.tempTableMaxRows ?? void 0;
8759
9088
  if (!Number.isInteger(fetchParallel) || fetchParallel < 1 || fetchParallel > 10) {
@@ -8804,9 +9133,21 @@ async function run() {
8804
9133
  return 2;
8805
9134
  }
8806
9135
  if (args.dryRun) {
8807
- const plans = buildBatchExplainPlans(sql);
9136
+ let plans;
9137
+ try {
9138
+ plans = buildBatchExplainPlans(sql);
9139
+ } catch (err) {
9140
+ const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
9141
+ bindings: sqlDiagnosticContext.appBindingByMappedApp,
9142
+ rewriteSegments: sqlDiagnosticContext.rewriteSegments
9143
+ }) : err;
9144
+ process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
9145
+ `);
9146
+ return toExitCodeFromError(restored);
9147
+ }
8808
9148
  const out = [];
8809
- plans.statements.forEach((p) => {
9149
+ const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
9150
+ restoredStatements.forEach((p) => {
8810
9151
  if (p.index > 0) out.push("");
8811
9152
  out.push(`[${p.index + 1}] ${p.type}`);
8812
9153
  out.push(...p.plan);
@@ -8872,7 +9213,7 @@ async function run() {
8872
9213
  `);
8873
9214
  return 3;
8874
9215
  }
8875
- const authReq = args.auth ?? envAuth("KSQL_AUTH") ?? p.auth ?? "auto";
9216
+ const authReq = args.auth ?? envAuth2("KSQL_AUTH") ?? p.auth ?? "auto";
8876
9217
  const username = args.username ?? envString2("KSQL_USERNAME") ?? p.username ?? null;
8877
9218
  const passwordFromEnvRef = p.passwordEnv ? envString2(p.passwordEnv) : null;
8878
9219
  const password = args.password ?? envString2("KSQL_PASSWORD") ?? passwordFromEnvRef ?? p.password ?? null;
@@ -8900,25 +9241,22 @@ async function run() {
8900
9241
  }
8901
9242
  const mapFromConfigRaw = p.tokenMap ?? {};
8902
9243
  const mapFromConfig = Object.fromEntries(
8903
- Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k), resolveTokenValue(String(v))])
9244
+ Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k), resolveTokenValue2(String(v))])
8904
9245
  );
8905
9246
  const effectiveTokenMap = { ...mapFromConfig, ...mapFromEnv, ...mapFromFile, ...mapFromArg };
8906
9247
  const assignedAppIds = appIds.filter((appId) => appProfileByApp.get(appId) === pName);
8907
- const tokenByApp = /* @__PURE__ */ new Map();
8908
- for (const mappedAppId of assignedAppIds) {
8909
- const realAppId = appBindingByMappedApp.get(mappedAppId)?.appId ?? mappedAppId;
8910
- const key = `APP${realAppId}`;
8911
- const fromMap = effectiveTokenMap[key];
8912
- if (fromMap) {
8913
- tokenByApp.set(mappedAppId, resolveTokenValue(fromMap));
8914
- continue;
8915
- }
8916
- if (assignedAppIds.length === 1 && singleToken) {
8917
- tokenByApp.set(mappedAppId, singleToken);
8918
- continue;
8919
- }
8920
- missingAppProfiles.push(`APP${realAppId}@${pName}`);
8921
- }
9248
+ const resolvedTokens = resolveTokenByMappedApp({
9249
+ mappedAppIds: assignedAppIds,
9250
+ profileName: pName,
9251
+ bindings: appBindingByMappedApp,
9252
+ logicalBindingLabels: new Map(
9253
+ [...appBindingByMappedApp.values()].filter((b) => b.source === "logical").map((b) => [b.mappedAppId, `LAPP_${b.logicalName}@${b.profile}`])
9254
+ ),
9255
+ effectiveTokenMap,
9256
+ singleToken
9257
+ });
9258
+ const tokenByApp = resolvedTokens.tokenByPhysicalApp;
9259
+ missingAppProfiles.push(...resolvedTokens.missing);
8922
9260
  profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
8923
9261
  guestSpaceId,
8924
9262
  timeoutMs: timeout,
@@ -8932,7 +9270,7 @@ async function run() {
8932
9270
  auth: {
8933
9271
  type: "token",
8934
9272
  resolveToken(appId) {
8935
- const token = tokenByApp.get(appId) ?? tokenByApp.get(assignedAppIds[0]);
9273
+ const token = tokenByApp.get(appId);
8936
9274
  if (!token) throw new Error(`AuthError: token is not resolved for APP${appId}@${pName}.`);
8937
9275
  return token;
8938
9276
  }
@@ -8963,7 +9301,7 @@ async function run() {
8963
9301
  `);
8964
9302
  return 3;
8965
9303
  }
8966
- const authReq = args.auth ?? envAuth("KSQL_AUTH") ?? diagProfile.auth ?? "auto";
9304
+ const authReq = args.auth ?? envAuth2("KSQL_AUTH") ?? diagProfile.auth ?? "auto";
8967
9305
  const username = args.username ?? envString2("KSQL_USERNAME") ?? diagProfile.username ?? null;
8968
9306
  const passwordFromEnvRef = diagProfile.passwordEnv ? envString2(diagProfile.passwordEnv) : null;
8969
9307
  const password = args.password ?? envString2("KSQL_PASSWORD") ?? passwordFromEnvRef ?? diagProfile.password ?? null;
@@ -8990,7 +9328,7 @@ async function run() {
8990
9328
  } else {
8991
9329
  const mapFromConfigRaw = diagProfile.tokenMap ?? {};
8992
9330
  const mapFromConfig = Object.fromEntries(
8993
- Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k), resolveTokenValue(String(v))])
9331
+ Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k), resolveTokenValue2(String(v))])
8994
9332
  );
8995
9333
  const effectiveTokenMap = { ...mapFromConfig, ...mapFromEnv, ...mapFromFile, ...mapFromArg };
8996
9334
  const diagToken = effectiveTokenMap[`APP${defaultApp}`] ?? (singleToken && appIds.length <= 1 ? singleToken : null);
@@ -9008,7 +9346,7 @@ async function run() {
9008
9346
  debug,
9009
9347
  debugHeaders,
9010
9348
  debugUrlOnly: args.debugUrl,
9011
- auth: { type: "token", token: resolveTokenValue(diagToken) }
9349
+ auth: { type: "token", token: resolveTokenValue2(diagToken) }
9012
9350
  });
9013
9351
  }
9014
9352
  return 0;
@@ -9123,13 +9461,16 @@ query=${label}`);
9123
9461
  });
9124
9462
  return writeBatchOutput(batchResult, { format, noHeader, pretty, displayOptions, outputPath, quiet });
9125
9463
  }
9126
- const result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, { maxRecords, onLimitReached: onLimit, cacheContext }) : await execute(sql, client, {
9464
+ let result = args.dryRun ? await execute(`EXPLAIN ${sql}`, client, { maxRecords, onLimitReached: onLimit, cacheContext }) : await execute(sql, client, {
9127
9465
  maxRecords,
9128
9466
  fetchParallel,
9129
9467
  onLimitReached: onLimit,
9130
9468
  confirm: isDmlStatement ? confirm : void 0,
9131
9469
  cacheContext
9132
9470
  });
9471
+ if (args.dryRun && sqlDiagnosticContext) {
9472
+ result = restoreSqlDiagnosticValue(result, sqlDiagnosticContext.appBindingByMappedApp);
9473
+ }
9133
9474
  if (result.type === "ASSERT") {
9134
9475
  const output2 = buildAssertOutput(result, format, pretty);
9135
9476
  if (outputPath) (0, import_fs2.writeFileSync)(outputPath, `${output2}
@@ -9158,14 +9499,18 @@ query=${label}`);
9158
9499
  if (shouldExitOnEmpty(args.dryRun, exitOnEmpty, result.rowCount)) return 1;
9159
9500
  return 0;
9160
9501
  } catch (err) {
9161
- if (err instanceof OperationCancelledError) {
9162
- process.stderr.write(`${err.message}
9502
+ const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
9503
+ bindings: sqlDiagnosticContext.appBindingByMappedApp,
9504
+ rewriteSegments: sqlDiagnosticContext.rewriteSegments
9505
+ }) : err;
9506
+ if (restored instanceof OperationCancelledError) {
9507
+ process.stderr.write(`${restored.message}
9163
9508
  `);
9164
9509
  return 2;
9165
9510
  }
9166
- process.stderr.write(`${err instanceof Error ? err.message : String(err)}
9511
+ process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
9167
9512
  `);
9168
- return toExitCodeFromError(err);
9513
+ return toExitCodeFromError(restored);
9169
9514
  }
9170
9515
  }
9171
9516
  function isDirectCliRun() {
@@ -9192,6 +9537,7 @@ if (isDirectCliRun()) {
9192
9537
  parseConsoleMetaCommand,
9193
9538
  parseTokenFile,
9194
9539
  parseTokenMap,
9540
+ runWithArgv,
9195
9541
  shouldExitOnEmpty,
9196
9542
  writeBatchOutput
9197
9543
  });