@rex0220/kintone-sql-tools 1.12.1 → 1.13.1
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/README.md +46 -12
- package/dist-cli/ksql.js +764 -429
- package/dist-mcp/ksql-mcp.js +614 -236
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +4 -2
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
|
});
|
|
@@ -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
|
|
6871
|
-
return
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
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))
|
|
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
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
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
|
-
|
|
6952
|
-
appId: Number(sql.slice(digitStart, digitEnd)),
|
|
7422
|
+
const common = {
|
|
6953
7423
|
profile,
|
|
6954
7424
|
start,
|
|
6955
|
-
|
|
6956
|
-
|
|
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(
|
|
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(
|
|
7042
|
-
appBindingByMappedApp.set(mapped, {
|
|
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
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
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
|
-
|
|
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,99 @@ 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
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
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
|
|
7255
|
-
|
|
7256
|
-
|
|
7682
|
+
function toParseInput(sql) {
|
|
7683
|
+
try {
|
|
7684
|
+
return normalizeSqlAppProfiles(sql, "console").normalizedSql;
|
|
7685
|
+
} catch {
|
|
7686
|
+
return sql;
|
|
7687
|
+
}
|
|
7257
7688
|
}
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
}
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
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");
|
|
7276
|
-
}
|
|
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
|
-
}
|
|
7287
|
-
}
|
|
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}`);
|
|
7297
|
-
}
|
|
7298
|
-
throw new Error(`kintone API error ${res.status}: ${bodyText}`);
|
|
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 };
|
|
7299
7702
|
}
|
|
7300
|
-
if (
|
|
7301
|
-
|
|
7703
|
+
if (e instanceof ParseError) {
|
|
7704
|
+
return { kind: "fail", continuable: e.token.kind === "EOF" /* EOF */, message: e.message };
|
|
7302
7705
|
}
|
|
7303
|
-
|
|
7706
|
+
throw e;
|
|
7304
7707
|
}
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7708
|
+
}
|
|
7709
|
+
|
|
7710
|
+
// src/node/sqlDiagnostics.ts
|
|
7711
|
+
function restoreSqlDiagnosticValue(value, bindings) {
|
|
7712
|
+
if (typeof value === "string") {
|
|
7713
|
+
let restored = value;
|
|
7714
|
+
for (const binding of bindings.values()) {
|
|
7715
|
+
const internal = `APP${binding.mappedAppId}`;
|
|
7716
|
+
const display = binding.source === "logical" ? `LAPP_${binding.logicalName}@${binding.profile}` : `APP${binding.appId}@${binding.profile}`;
|
|
7717
|
+
restored = restored.split(`${internal} (${binding.mappedAppId})`).join(display).split(internal).join(display);
|
|
7718
|
+
}
|
|
7719
|
+
return restored;
|
|
7720
|
+
}
|
|
7721
|
+
if (Array.isArray(value)) {
|
|
7722
|
+
return value.map((item) => restoreSqlDiagnosticValue(item, bindings));
|
|
7723
|
+
}
|
|
7724
|
+
if (value !== null && typeof value === "object") {
|
|
7725
|
+
return Object.fromEntries(
|
|
7726
|
+
Object.entries(value).map(([key, item]) => [key, restoreSqlDiagnosticValue(item, bindings)])
|
|
7727
|
+
);
|
|
7314
7728
|
}
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7729
|
+
return value;
|
|
7730
|
+
}
|
|
7731
|
+
function restoreSqlContextError(err, sourceSql, context) {
|
|
7732
|
+
if (!(err instanceof Error)) return err;
|
|
7733
|
+
let message = err.message;
|
|
7734
|
+
for (const binding of context.bindings.values()) {
|
|
7735
|
+
if (binding.source === "logical") {
|
|
7736
|
+
message = message.split(`APP${binding.mappedAppId}`).join(`LAPP_${binding.logicalName}@${binding.profile}`);
|
|
7737
|
+
}
|
|
7323
7738
|
}
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
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
|
|
7739
|
+
const token = err.token;
|
|
7740
|
+
if (typeof token?.pos === "number") {
|
|
7741
|
+
const segment = context.rewriteSegments.find(
|
|
7742
|
+
(candidate) => token.pos >= candidate.normalizedStart && token.pos < candidate.normalizedEnd
|
|
7743
|
+
);
|
|
7744
|
+
if (segment) {
|
|
7745
|
+
const sourcePos = segment.sourceStart + Math.min(
|
|
7746
|
+
token.pos - segment.normalizedStart,
|
|
7747
|
+
Math.max(0, segment.sourceEnd - segment.sourceStart - 1)
|
|
7394
7748
|
);
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
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;
|
|
7749
|
+
message = message.replace(/(位置 \d+、トークン:/, `\uFF08\u4F4D\u7F6E ${sourcePos}\u3001\u30C8\u30FC\u30AF\u30F3:`);
|
|
7750
|
+
const originalRef = sourceSql.slice(segment.sourceStart, segment.sourceEnd);
|
|
7751
|
+
if (segment.bindingMappedAppId !== void 0 && originalRef) {
|
|
7752
|
+
message = message.replace(/(トークン: 「)[^」]*(」)/, `$1${originalRef}$2`);
|
|
7418
7753
|
}
|
|
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
7754
|
}
|
|
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
7755
|
}
|
|
7457
|
-
|
|
7756
|
+
err.message = message;
|
|
7757
|
+
return err;
|
|
7458
7758
|
}
|
|
7459
7759
|
|
|
7460
7760
|
// src/cli/index.ts
|
|
@@ -7835,7 +8135,7 @@ function loadConfig(configPath) {
|
|
|
7835
8135
|
const raw = (0, import_fs2.readFileSync)(configPath, "utf-8");
|
|
7836
8136
|
return JSON.parse(raw);
|
|
7837
8137
|
}
|
|
7838
|
-
function
|
|
8138
|
+
function resolveTokenValue2(raw) {
|
|
7839
8139
|
if (raw.startsWith("env:")) {
|
|
7840
8140
|
const envKey = raw.slice(4);
|
|
7841
8141
|
const envVal = process.env[envKey];
|
|
@@ -7866,12 +8166,12 @@ function envFormat(name) {
|
|
|
7866
8166
|
const v = envString2(name);
|
|
7867
8167
|
return normalizeOutputFormat(v);
|
|
7868
8168
|
}
|
|
7869
|
-
function
|
|
8169
|
+
function envOnLimit2(name) {
|
|
7870
8170
|
const v = envString2(name);
|
|
7871
8171
|
if (v === "error" || v === "truncate") return v;
|
|
7872
8172
|
return null;
|
|
7873
8173
|
}
|
|
7874
|
-
function
|
|
8174
|
+
function envAuth2(name) {
|
|
7875
8175
|
const v = envString2(name);
|
|
7876
8176
|
if (v === "token" || v === "userpass" || v === "auto") return v;
|
|
7877
8177
|
return null;
|
|
@@ -8296,10 +8596,10 @@ async function runWithArgvCapture(argv) {
|
|
|
8296
8596
|
process.stdout.write = original;
|
|
8297
8597
|
}
|
|
8298
8598
|
}
|
|
8299
|
-
async function confirmDmlInConsole(sql, opts, queue, defaultProfile = "dev") {
|
|
8599
|
+
async function confirmDmlInConsole(sql, opts, queue, defaultProfile = "dev", resolutionContext) {
|
|
8300
8600
|
if (!opts.allowDml || opts.yes || opts.dryRun) return true;
|
|
8301
8601
|
try {
|
|
8302
|
-
const normalized = normalizeSqlAppProfiles(sql, defaultProfile);
|
|
8602
|
+
const normalized = normalizeSqlAppProfiles(sql, defaultProfile, resolutionContext);
|
|
8303
8603
|
const statements = parseSqlStatements(normalized.normalizedSql);
|
|
8304
8604
|
if (statements.length > 1) {
|
|
8305
8605
|
const analysis = analyzeBatch(statements);
|
|
@@ -8387,6 +8687,14 @@ async function runConsole(base) {
|
|
|
8387
8687
|
let buffer = "";
|
|
8388
8688
|
let emptyPromptSigintArmed = false;
|
|
8389
8689
|
const queue = createConsoleEventQueue(rl);
|
|
8690
|
+
const consoleConfigPath = base.configPath ?? envString2("KSQL_CONFIG") ?? "./ksql.config.json";
|
|
8691
|
+
let consoleConfig = {};
|
|
8692
|
+
try {
|
|
8693
|
+
consoleConfig = validateKsqlConfig(loadConfig(consoleConfigPath));
|
|
8694
|
+
} catch {
|
|
8695
|
+
}
|
|
8696
|
+
const consoleResolutionContext = () => createAppResolutionContext(consoleConfig, profile ?? "dev");
|
|
8697
|
+
const formatConsoleProfiles = (sql) => formatResolvedAppProfiles(sql, profile ?? "dev", consoleResolutionContext());
|
|
8390
8698
|
process.stdout.write("kSQL Console (type :help)\n");
|
|
8391
8699
|
process.stdout.write(
|
|
8392
8700
|
[
|
|
@@ -8444,7 +8752,7 @@ async function runConsole(base) {
|
|
|
8444
8752
|
allowDml: base.allowDml,
|
|
8445
8753
|
yes: base.yes,
|
|
8446
8754
|
dryRun
|
|
8447
|
-
}, queue, profile ?? "dev");
|
|
8755
|
+
}, queue, profile ?? "dev", consoleResolutionContext());
|
|
8448
8756
|
if (!ok) {
|
|
8449
8757
|
process.stderr.write("DML was cancelled by user.\n");
|
|
8450
8758
|
continue;
|
|
@@ -8452,7 +8760,7 @@ async function runConsole(base) {
|
|
|
8452
8760
|
}
|
|
8453
8761
|
buffer = "";
|
|
8454
8762
|
lastSql = sql2;
|
|
8455
|
-
lastResolvedProfiles =
|
|
8763
|
+
lastResolvedProfiles = formatConsoleProfiles(sql2);
|
|
8456
8764
|
history.push(sql2);
|
|
8457
8765
|
appendHistory(sql2);
|
|
8458
8766
|
const { code: code2, stdout: stdout2 } = await runWithArgvCapture(buildReplExecArgvWithProfile(base, sql2, dryRun, format, profile));
|
|
@@ -8559,13 +8867,13 @@ async function runConsole(base) {
|
|
|
8559
8867
|
allowDml: base.allowDml,
|
|
8560
8868
|
yes: base.yes,
|
|
8561
8869
|
dryRun
|
|
8562
|
-
}, queue, profile ?? "dev");
|
|
8870
|
+
}, queue, profile ?? "dev", consoleResolutionContext());
|
|
8563
8871
|
if (!ok) {
|
|
8564
8872
|
process.stderr.write("DML was cancelled by user.\n");
|
|
8565
8873
|
continue;
|
|
8566
8874
|
}
|
|
8567
8875
|
lastSql = sql2;
|
|
8568
|
-
lastResolvedProfiles =
|
|
8876
|
+
lastResolvedProfiles = formatConsoleProfiles(sql2);
|
|
8569
8877
|
process.stdout.write(`rerun: ${sql2.replace(/\s+/g, " ").trim()}
|
|
8570
8878
|
`);
|
|
8571
8879
|
const { code: code2, stdout: stdout2 } = await runWithArgvCapture(buildReplExecArgvWithProfile(base, sql2, dryRun, format, profile));
|
|
@@ -8637,14 +8945,14 @@ async function runConsole(base) {
|
|
|
8637
8945
|
allowDml: base.allowDml,
|
|
8638
8946
|
yes: base.yes,
|
|
8639
8947
|
dryRun
|
|
8640
|
-
}, queue, profile ?? "dev");
|
|
8948
|
+
}, queue, profile ?? "dev", consoleResolutionContext());
|
|
8641
8949
|
if (!ok) {
|
|
8642
8950
|
process.stderr.write("DML was cancelled by user.\n");
|
|
8643
8951
|
continue;
|
|
8644
8952
|
}
|
|
8645
8953
|
}
|
|
8646
8954
|
lastSql = sql;
|
|
8647
|
-
lastResolvedProfiles =
|
|
8955
|
+
lastResolvedProfiles = formatConsoleProfiles(sql);
|
|
8648
8956
|
history.push(sql);
|
|
8649
8957
|
appendHistory(sql);
|
|
8650
8958
|
const { code, stdout } = await runWithArgvCapture(buildReplExecArgvWithProfile(base, sql, dryRun, format, profile));
|
|
@@ -8698,6 +9006,8 @@ async function run() {
|
|
|
8698
9006
|
const profileName = args.profile ?? envString2("KSQL_PROFILE") ?? config.defaultProfile ?? "dev";
|
|
8699
9007
|
const profile = config.profiles?.[profileName] ?? {};
|
|
8700
9008
|
let sql = null;
|
|
9009
|
+
let sourceSql = null;
|
|
9010
|
+
let sqlDiagnosticContext = null;
|
|
8701
9011
|
let hasProfileSyntax = false;
|
|
8702
9012
|
let appBindingByMappedApp = /* @__PURE__ */ new Map();
|
|
8703
9013
|
let parsedStmt = null;
|
|
@@ -8715,9 +9025,16 @@ async function run() {
|
|
|
8715
9025
|
process.stderr.write("ArgumentError: SQL is empty.\n");
|
|
8716
9026
|
return 2;
|
|
8717
9027
|
}
|
|
9028
|
+
sourceSql = sql;
|
|
8718
9029
|
try {
|
|
8719
|
-
const
|
|
9030
|
+
const validatedConfig = validateKsqlConfig(config);
|
|
9031
|
+
const resolutionContext = createAppResolutionContext(validatedConfig, profileName);
|
|
9032
|
+
const normalized = normalizeSqlAppProfiles(sql, profileName, resolutionContext);
|
|
9033
|
+
for (const binding of normalized.appBindingByMappedApp.values()) {
|
|
9034
|
+
if (binding.source === "physical") resolutionContext.assertPhysicalAppAllowed(binding.profile);
|
|
9035
|
+
}
|
|
8720
9036
|
sql = normalized.normalizedSql;
|
|
9037
|
+
sqlDiagnosticContext = normalized;
|
|
8721
9038
|
hasProfileSyntax = normalized.hasProfileSyntax;
|
|
8722
9039
|
appBindingByMappedApp = normalized.appBindingByMappedApp;
|
|
8723
9040
|
} catch (err) {
|
|
@@ -8746,14 +9063,18 @@ async function run() {
|
|
|
8746
9063
|
}
|
|
8747
9064
|
}
|
|
8748
9065
|
} catch (err) {
|
|
8749
|
-
|
|
9066
|
+
const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
|
|
9067
|
+
bindings: sqlDiagnosticContext.appBindingByMappedApp,
|
|
9068
|
+
rewriteSegments: sqlDiagnosticContext.rewriteSegments
|
|
9069
|
+
}) : err;
|
|
9070
|
+
process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
|
|
8750
9071
|
`);
|
|
8751
9072
|
return 1;
|
|
8752
9073
|
}
|
|
8753
9074
|
}
|
|
8754
9075
|
const maxRecords = args.maxRecords ?? envInt2("KSQL_MAX_RECORDS") ?? profile.query?.maxRecords ?? 500;
|
|
8755
9076
|
const fetchParallel = args.fetchParallel ?? envInt2("KSQL_FETCH_PARALLEL") ?? profile.query?.fetchParallel ?? 3;
|
|
8756
|
-
const onLimit = args.onLimit ??
|
|
9077
|
+
const onLimit = args.onLimit ?? envOnLimit2("KSQL_ON_LIMIT") ?? profile.query?.onLimit ?? "error";
|
|
8757
9078
|
const timeout = args.timeout ?? envInt2("KSQL_TIMEOUT") ?? profile.query?.timeout ?? 3e4;
|
|
8758
9079
|
const tempTableMaxRows = args.tempTableMaxRows ?? envInt2("KSQL_TEMP_TABLE_MAX_ROWS") ?? profile.query?.tempTableMaxRows ?? void 0;
|
|
8759
9080
|
if (!Number.isInteger(fetchParallel) || fetchParallel < 1 || fetchParallel > 10) {
|
|
@@ -8804,9 +9125,21 @@ async function run() {
|
|
|
8804
9125
|
return 2;
|
|
8805
9126
|
}
|
|
8806
9127
|
if (args.dryRun) {
|
|
8807
|
-
|
|
9128
|
+
let plans;
|
|
9129
|
+
try {
|
|
9130
|
+
plans = buildBatchExplainPlans(sql);
|
|
9131
|
+
} catch (err) {
|
|
9132
|
+
const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
|
|
9133
|
+
bindings: sqlDiagnosticContext.appBindingByMappedApp,
|
|
9134
|
+
rewriteSegments: sqlDiagnosticContext.rewriteSegments
|
|
9135
|
+
}) : err;
|
|
9136
|
+
process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
|
|
9137
|
+
`);
|
|
9138
|
+
return toExitCodeFromError(restored);
|
|
9139
|
+
}
|
|
8808
9140
|
const out = [];
|
|
8809
|
-
plans.statements.
|
|
9141
|
+
const restoredStatements = sqlDiagnosticContext ? restoreSqlDiagnosticValue(plans.statements, sqlDiagnosticContext.appBindingByMappedApp) : plans.statements;
|
|
9142
|
+
restoredStatements.forEach((p) => {
|
|
8810
9143
|
if (p.index > 0) out.push("");
|
|
8811
9144
|
out.push(`[${p.index + 1}] ${p.type}`);
|
|
8812
9145
|
out.push(...p.plan);
|
|
@@ -8872,7 +9205,7 @@ async function run() {
|
|
|
8872
9205
|
`);
|
|
8873
9206
|
return 3;
|
|
8874
9207
|
}
|
|
8875
|
-
const authReq = args.auth ??
|
|
9208
|
+
const authReq = args.auth ?? envAuth2("KSQL_AUTH") ?? p.auth ?? "auto";
|
|
8876
9209
|
const username = args.username ?? envString2("KSQL_USERNAME") ?? p.username ?? null;
|
|
8877
9210
|
const passwordFromEnvRef = p.passwordEnv ? envString2(p.passwordEnv) : null;
|
|
8878
9211
|
const password = args.password ?? envString2("KSQL_PASSWORD") ?? passwordFromEnvRef ?? p.password ?? null;
|
|
@@ -8900,25 +9233,22 @@ async function run() {
|
|
|
8900
9233
|
}
|
|
8901
9234
|
const mapFromConfigRaw = p.tokenMap ?? {};
|
|
8902
9235
|
const mapFromConfig = Object.fromEntries(
|
|
8903
|
-
Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k),
|
|
9236
|
+
Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k), resolveTokenValue2(String(v))])
|
|
8904
9237
|
);
|
|
8905
9238
|
const effectiveTokenMap = { ...mapFromConfig, ...mapFromEnv, ...mapFromFile, ...mapFromArg };
|
|
8906
9239
|
const assignedAppIds = appIds.filter((appId) => appProfileByApp.get(appId) === pName);
|
|
8907
|
-
const
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
}
|
|
8920
|
-
missingAppProfiles.push(`APP${realAppId}@${pName}`);
|
|
8921
|
-
}
|
|
9240
|
+
const resolvedTokens = resolveTokenByMappedApp({
|
|
9241
|
+
mappedAppIds: assignedAppIds,
|
|
9242
|
+
profileName: pName,
|
|
9243
|
+
bindings: appBindingByMappedApp,
|
|
9244
|
+
logicalBindingLabels: new Map(
|
|
9245
|
+
[...appBindingByMappedApp.values()].filter((b) => b.source === "logical").map((b) => [b.mappedAppId, `LAPP_${b.logicalName}@${b.profile}`])
|
|
9246
|
+
),
|
|
9247
|
+
effectiveTokenMap,
|
|
9248
|
+
singleToken
|
|
9249
|
+
});
|
|
9250
|
+
const tokenByApp = resolvedTokens.tokenByPhysicalApp;
|
|
9251
|
+
missingAppProfiles.push(...resolvedTokens.missing);
|
|
8922
9252
|
profileClientMap.set(pName, createNodeKintoneClient(baseUrl, {
|
|
8923
9253
|
guestSpaceId,
|
|
8924
9254
|
timeoutMs: timeout,
|
|
@@ -8932,7 +9262,7 @@ async function run() {
|
|
|
8932
9262
|
auth: {
|
|
8933
9263
|
type: "token",
|
|
8934
9264
|
resolveToken(appId) {
|
|
8935
|
-
const token = tokenByApp.get(appId)
|
|
9265
|
+
const token = tokenByApp.get(appId);
|
|
8936
9266
|
if (!token) throw new Error(`AuthError: token is not resolved for APP${appId}@${pName}.`);
|
|
8937
9267
|
return token;
|
|
8938
9268
|
}
|
|
@@ -8963,7 +9293,7 @@ async function run() {
|
|
|
8963
9293
|
`);
|
|
8964
9294
|
return 3;
|
|
8965
9295
|
}
|
|
8966
|
-
const authReq = args.auth ??
|
|
9296
|
+
const authReq = args.auth ?? envAuth2("KSQL_AUTH") ?? diagProfile.auth ?? "auto";
|
|
8967
9297
|
const username = args.username ?? envString2("KSQL_USERNAME") ?? diagProfile.username ?? null;
|
|
8968
9298
|
const passwordFromEnvRef = diagProfile.passwordEnv ? envString2(diagProfile.passwordEnv) : null;
|
|
8969
9299
|
const password = args.password ?? envString2("KSQL_PASSWORD") ?? passwordFromEnvRef ?? diagProfile.password ?? null;
|
|
@@ -8990,7 +9320,7 @@ async function run() {
|
|
|
8990
9320
|
} else {
|
|
8991
9321
|
const mapFromConfigRaw = diagProfile.tokenMap ?? {};
|
|
8992
9322
|
const mapFromConfig = Object.fromEntries(
|
|
8993
|
-
Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k),
|
|
9323
|
+
Object.entries(mapFromConfigRaw).map(([k, v]) => [normalizeAppKey(k), resolveTokenValue2(String(v))])
|
|
8994
9324
|
);
|
|
8995
9325
|
const effectiveTokenMap = { ...mapFromConfig, ...mapFromEnv, ...mapFromFile, ...mapFromArg };
|
|
8996
9326
|
const diagToken = effectiveTokenMap[`APP${defaultApp}`] ?? (singleToken && appIds.length <= 1 ? singleToken : null);
|
|
@@ -9008,7 +9338,7 @@ async function run() {
|
|
|
9008
9338
|
debug,
|
|
9009
9339
|
debugHeaders,
|
|
9010
9340
|
debugUrlOnly: args.debugUrl,
|
|
9011
|
-
auth: { type: "token", token:
|
|
9341
|
+
auth: { type: "token", token: resolveTokenValue2(diagToken) }
|
|
9012
9342
|
});
|
|
9013
9343
|
}
|
|
9014
9344
|
return 0;
|
|
@@ -9158,14 +9488,18 @@ query=${label}`);
|
|
|
9158
9488
|
if (shouldExitOnEmpty(args.dryRun, exitOnEmpty, result.rowCount)) return 1;
|
|
9159
9489
|
return 0;
|
|
9160
9490
|
} catch (err) {
|
|
9161
|
-
|
|
9162
|
-
|
|
9491
|
+
const restored = sourceSql && sqlDiagnosticContext ? restoreSqlContextError(err, sourceSql, {
|
|
9492
|
+
bindings: sqlDiagnosticContext.appBindingByMappedApp,
|
|
9493
|
+
rewriteSegments: sqlDiagnosticContext.rewriteSegments
|
|
9494
|
+
}) : err;
|
|
9495
|
+
if (restored instanceof OperationCancelledError) {
|
|
9496
|
+
process.stderr.write(`${restored.message}
|
|
9163
9497
|
`);
|
|
9164
9498
|
return 2;
|
|
9165
9499
|
}
|
|
9166
|
-
process.stderr.write(`${
|
|
9500
|
+
process.stderr.write(`${restored instanceof Error ? restored.message : String(restored)}
|
|
9167
9501
|
`);
|
|
9168
|
-
return toExitCodeFromError(
|
|
9502
|
+
return toExitCodeFromError(restored);
|
|
9169
9503
|
}
|
|
9170
9504
|
}
|
|
9171
9505
|
function isDirectCliRun() {
|
|
@@ -9192,6 +9526,7 @@ if (isDirectCliRun()) {
|
|
|
9192
9526
|
parseConsoleMetaCommand,
|
|
9193
9527
|
parseTokenFile,
|
|
9194
9528
|
parseTokenMap,
|
|
9529
|
+
runWithArgv,
|
|
9195
9530
|
shouldExitOnEmpty,
|
|
9196
9531
|
writeBatchOutput
|
|
9197
9532
|
});
|