@casys/mcp-erpnext 3.0.2 → 3.1.0-beta.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/README.md +72 -41
- package/mcp-erpnext.mjs +2301 -525
- package/package.json +1 -1
- package/ui-dist/chart-viewer/index.html +58 -99
- package/ui-dist/doc-viewer/index.html +116 -0
- package/ui-dist/doclist-viewer/index.html +44 -82
- package/ui-dist/funnel-viewer/index.html +42 -80
- package/ui-dist/invoice-viewer/index.html +42 -80
- package/ui-dist/kanban-viewer/index.html +42 -80
- package/ui-dist/kpi-viewer/index.html +42 -80
- package/ui-dist/stock-viewer/index.html +42 -80
package/mcp-erpnext.mjs
CHANGED
|
@@ -3117,7 +3117,25 @@ var require_utils2 = __commonJS({
|
|
|
3117
3117
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
3118
3118
|
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
3119
3119
|
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
3120
|
-
var isPathCharacter = RegExp.prototype.test.bind(/^[
|
|
3120
|
+
var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
|
|
3121
|
+
var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
|
|
3122
|
+
var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
|
|
3123
|
+
var BYTE_HEX = new Array(256);
|
|
3124
|
+
{
|
|
3125
|
+
const HEX_DIGITS = "0123456789ABCDEF";
|
|
3126
|
+
for (let i = 0; i < 256; i++) {
|
|
3127
|
+
BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
function percentEncodeNonAscii(cp) {
|
|
3131
|
+
if (cp < 2048) {
|
|
3132
|
+
return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
|
|
3133
|
+
}
|
|
3134
|
+
if (cp < 65536) {
|
|
3135
|
+
return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3136
|
+
}
|
|
3137
|
+
return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3138
|
+
}
|
|
3121
3139
|
function stringArrayToHexStripped(input) {
|
|
3122
3140
|
let acc = "";
|
|
3123
3141
|
let code = 0;
|
|
@@ -3142,91 +3160,105 @@ var require_utils2 = __commonJS({
|
|
|
3142
3160
|
}
|
|
3143
3161
|
return acc;
|
|
3144
3162
|
}
|
|
3163
|
+
var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
|
|
3164
|
+
var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
|
|
3165
|
+
var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
|
|
3145
3166
|
var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
3146
|
-
function
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
if (hex !== "") {
|
|
3154
|
-
address.push(hex);
|
|
3155
|
-
} else {
|
|
3156
|
-
output.error = true;
|
|
3157
|
-
return false;
|
|
3167
|
+
function isZoneIdentifier(zone) {
|
|
3168
|
+
if (zone.length === 0) return false;
|
|
3169
|
+
for (let i = 0; i < zone.length; i++) {
|
|
3170
|
+
if (isZoneCharacter(zone[i])) continue;
|
|
3171
|
+
if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
|
|
3172
|
+
i += 2;
|
|
3173
|
+
continue;
|
|
3158
3174
|
}
|
|
3159
|
-
|
|
3175
|
+
return false;
|
|
3160
3176
|
}
|
|
3161
3177
|
return true;
|
|
3162
3178
|
}
|
|
3163
|
-
function
|
|
3164
|
-
let
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
let
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
}
|
|
3176
|
-
if (cursor === ":") {
|
|
3177
|
-
if (endipv6Encountered === true) {
|
|
3178
|
-
endIpv6 = true;
|
|
3179
|
+
function compressIPv6ZeroRun(hextets) {
|
|
3180
|
+
let bestStart = -1;
|
|
3181
|
+
let bestLength = 0;
|
|
3182
|
+
let runStart = -1;
|
|
3183
|
+
let runLength = 0;
|
|
3184
|
+
for (let i = 0; i < hextets.length; i++) {
|
|
3185
|
+
if (hextets[i] === "0") {
|
|
3186
|
+
if (runStart === -1) runStart = i;
|
|
3187
|
+
runLength++;
|
|
3188
|
+
if (runLength > bestLength) {
|
|
3189
|
+
bestLength = runLength;
|
|
3190
|
+
bestStart = runStart;
|
|
3179
3191
|
}
|
|
3180
|
-
if (!consume(buffer, address, output)) {
|
|
3181
|
-
break;
|
|
3182
|
-
}
|
|
3183
|
-
if (++tokenCount > 7) {
|
|
3184
|
-
output.error = true;
|
|
3185
|
-
break;
|
|
3186
|
-
}
|
|
3187
|
-
if (i > 0 && input[i - 1] === ":") {
|
|
3188
|
-
endipv6Encountered = true;
|
|
3189
|
-
}
|
|
3190
|
-
address.push(":");
|
|
3191
|
-
continue;
|
|
3192
|
-
} else if (cursor === "%") {
|
|
3193
|
-
if (!consume(buffer, address, output)) {
|
|
3194
|
-
break;
|
|
3195
|
-
}
|
|
3196
|
-
consume = consumeIsZone;
|
|
3197
3192
|
} else {
|
|
3198
|
-
|
|
3193
|
+
runStart = -1;
|
|
3194
|
+
runLength = 0;
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
if (bestLength < 2) return hextets.join(":");
|
|
3198
|
+
const head = hextets.slice(0, bestStart).join(":");
|
|
3199
|
+
const tail = hextets.slice(bestStart + bestLength).join(":");
|
|
3200
|
+
return head + "::" + tail;
|
|
3201
|
+
}
|
|
3202
|
+
function normalizeIPv6Address(input) {
|
|
3203
|
+
const compression = input.indexOf("::");
|
|
3204
|
+
if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
|
|
3205
|
+
const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
|
|
3206
|
+
const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
|
|
3207
|
+
if (compression !== -1) {
|
|
3208
|
+
if (left.length === 1 && left[0] === "") left.length = 0;
|
|
3209
|
+
if (right.length === 1 && right[0] === "") right.length = 0;
|
|
3210
|
+
}
|
|
3211
|
+
const parts = left.concat(right);
|
|
3212
|
+
let hextetCount = 0;
|
|
3213
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3214
|
+
const part = parts[i];
|
|
3215
|
+
if (part === "") return void 0;
|
|
3216
|
+
if (part.indexOf(".") !== -1) {
|
|
3217
|
+
if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
|
|
3218
|
+
hextetCount += 2;
|
|
3199
3219
|
continue;
|
|
3200
3220
|
}
|
|
3221
|
+
if (!isHextet(part)) return void 0;
|
|
3222
|
+
parts[i] = parseInt(part, 16).toString(16);
|
|
3223
|
+
hextetCount++;
|
|
3201
3224
|
}
|
|
3202
|
-
if (
|
|
3203
|
-
if (
|
|
3204
|
-
|
|
3205
|
-
} else if (endIpv6) {
|
|
3206
|
-
address.push(buffer.join(""));
|
|
3207
|
-
} else {
|
|
3208
|
-
address.push(stringArrayToHexStripped(buffer));
|
|
3209
|
-
}
|
|
3225
|
+
if (compression === -1) {
|
|
3226
|
+
if (hextetCount !== 8) return void 0;
|
|
3227
|
+
return compressIPv6ZeroRun(parts);
|
|
3210
3228
|
}
|
|
3211
|
-
|
|
3212
|
-
|
|
3229
|
+
if (hextetCount >= 8) return void 0;
|
|
3230
|
+
const expanded = parts.slice(0, left.length);
|
|
3231
|
+
for (let i = hextetCount; i < 8; i++) expanded.push("0");
|
|
3232
|
+
for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
|
|
3233
|
+
return compressIPv6ZeroRun(expanded);
|
|
3213
3234
|
}
|
|
3214
3235
|
function normalizeIPv6(host) {
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
}
|
|
3218
|
-
|
|
3219
|
-
if (
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3236
|
+
const bracketed = host[0] === "[" && host[host.length - 1] === "]";
|
|
3237
|
+
const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
|
|
3238
|
+
if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
|
|
3239
|
+
let input = bracketed ? host.slice(1, -1) : host;
|
|
3240
|
+
if (bracketed && isIPvFuture(input)) {
|
|
3241
|
+
input = input.toLowerCase();
|
|
3242
|
+
return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
|
|
3243
|
+
}
|
|
3244
|
+
if (findToken(input, ":") < 2) {
|
|
3245
|
+
return { host, isIPV6: false, error: bracketed };
|
|
3246
|
+
}
|
|
3247
|
+
let zoneIdentifier = "";
|
|
3248
|
+
const zoneSeparator = input.indexOf("%");
|
|
3249
|
+
if (zoneSeparator !== -1) {
|
|
3250
|
+
const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
|
|
3251
|
+
zoneIdentifier = input.slice(zoneSeparator + separatorLength);
|
|
3252
|
+
if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
|
|
3253
|
+
input = input.slice(0, zoneSeparator);
|
|
3254
|
+
}
|
|
3255
|
+
const address = normalizeIPv6Address(input);
|
|
3256
|
+
if (address === void 0) return { host, isIPV6: false, error: true };
|
|
3257
|
+
return {
|
|
3258
|
+
host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
|
|
3259
|
+
escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
|
|
3260
|
+
isIPV6: true
|
|
3261
|
+
};
|
|
3230
3262
|
}
|
|
3231
3263
|
function findToken(str, token) {
|
|
3232
3264
|
let ind = 0;
|
|
@@ -3345,7 +3377,8 @@ var require_utils2 = __commonJS({
|
|
|
3345
3377
|
function normalizePathEncoding(input) {
|
|
3346
3378
|
let output = "";
|
|
3347
3379
|
for (let i = 0; i < input.length; i++) {
|
|
3348
|
-
|
|
3380
|
+
const ch = input[i];
|
|
3381
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3349
3382
|
const hex = input.slice(i + 1, i + 3);
|
|
3350
3383
|
if (isHexPair(hex)) {
|
|
3351
3384
|
const normalizedHex = hex.toUpperCase();
|
|
@@ -3359,10 +3392,152 @@ var require_utils2 = __commonJS({
|
|
|
3359
3392
|
continue;
|
|
3360
3393
|
}
|
|
3361
3394
|
}
|
|
3362
|
-
if (isPathCharacter(
|
|
3363
|
-
output +=
|
|
3395
|
+
if (isPathCharacter(ch)) {
|
|
3396
|
+
output += ch;
|
|
3397
|
+
} else {
|
|
3398
|
+
const code = input.charCodeAt(i);
|
|
3399
|
+
if (code < 128) {
|
|
3400
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3401
|
+
} else if (code < 55296 || code > 57343) {
|
|
3402
|
+
output += percentEncodeNonAscii(code);
|
|
3403
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3404
|
+
const low = input.charCodeAt(i + 1);
|
|
3405
|
+
if (low >= 56320 && low <= 57343) {
|
|
3406
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3407
|
+
i++;
|
|
3408
|
+
} else {
|
|
3409
|
+
output += percentEncodeNonAscii(65533);
|
|
3410
|
+
}
|
|
3411
|
+
} else {
|
|
3412
|
+
output += percentEncodeNonAscii(65533);
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
return output;
|
|
3417
|
+
}
|
|
3418
|
+
function serializePathEncoding(input, pathNoScheme = false) {
|
|
3419
|
+
let output = "";
|
|
3420
|
+
let firstSegment = pathNoScheme && input[0] !== "/";
|
|
3421
|
+
for (let i = 0; i < input.length; i++) {
|
|
3422
|
+
const ch = input[i];
|
|
3423
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3424
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3425
|
+
if (isHexPair(hex)) {
|
|
3426
|
+
output += "%" + hex.toUpperCase();
|
|
3427
|
+
i += 2;
|
|
3428
|
+
continue;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
if (ch === "/") {
|
|
3432
|
+
firstSegment = false;
|
|
3433
|
+
}
|
|
3434
|
+
if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
|
|
3435
|
+
output += ch;
|
|
3364
3436
|
} else {
|
|
3365
|
-
|
|
3437
|
+
const code = input.charCodeAt(i);
|
|
3438
|
+
if (code < 128) {
|
|
3439
|
+
output += BYTE_HEX[code];
|
|
3440
|
+
} else if (code < 55296 || code > 57343) {
|
|
3441
|
+
output += percentEncodeNonAscii(code);
|
|
3442
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3443
|
+
const low = input.charCodeAt(i + 1);
|
|
3444
|
+
if (low >= 56320 && low <= 57343) {
|
|
3445
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3446
|
+
i++;
|
|
3447
|
+
} else {
|
|
3448
|
+
output += percentEncodeNonAscii(65533);
|
|
3449
|
+
}
|
|
3450
|
+
} else {
|
|
3451
|
+
output += percentEncodeNonAscii(65533);
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
return output;
|
|
3456
|
+
}
|
|
3457
|
+
function encodeComponent(input, isAllowed) {
|
|
3458
|
+
let output = "";
|
|
3459
|
+
for (let i = 0; i < input.length; i++) {
|
|
3460
|
+
const ch = input[i];
|
|
3461
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3462
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3463
|
+
if (isHexPair(hex)) {
|
|
3464
|
+
output += "%" + hex.toUpperCase();
|
|
3465
|
+
i += 2;
|
|
3466
|
+
continue;
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
if (isAllowed(ch)) {
|
|
3470
|
+
output += ch;
|
|
3471
|
+
} else {
|
|
3472
|
+
const code = input.charCodeAt(i);
|
|
3473
|
+
if (code < 128) {
|
|
3474
|
+
output += BYTE_HEX[code];
|
|
3475
|
+
} else if (code < 55296 || code > 57343) {
|
|
3476
|
+
output += percentEncodeNonAscii(code);
|
|
3477
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3478
|
+
const low = input.charCodeAt(i + 1);
|
|
3479
|
+
if (low >= 56320 && low <= 57343) {
|
|
3480
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3481
|
+
i++;
|
|
3482
|
+
} else {
|
|
3483
|
+
output += percentEncodeNonAscii(65533);
|
|
3484
|
+
}
|
|
3485
|
+
} else {
|
|
3486
|
+
output += percentEncodeNonAscii(65533);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
return output;
|
|
3491
|
+
}
|
|
3492
|
+
function encodeUserinfo(input) {
|
|
3493
|
+
return encodeComponent(input, isUserinfoCharacter);
|
|
3494
|
+
}
|
|
3495
|
+
function encodeQuery(input) {
|
|
3496
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3497
|
+
}
|
|
3498
|
+
function encodeFragment(input) {
|
|
3499
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3500
|
+
}
|
|
3501
|
+
function isEscapeSafe(cp) {
|
|
3502
|
+
return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
|
|
3503
|
+
}
|
|
3504
|
+
function normalizeQueryFragmentEncoding(input) {
|
|
3505
|
+
let output = "";
|
|
3506
|
+
for (let i = 0; i < input.length; i++) {
|
|
3507
|
+
const ch = input[i];
|
|
3508
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
3509
|
+
const hex = input.slice(i + 1, i + 3);
|
|
3510
|
+
if (isHexPair(hex)) {
|
|
3511
|
+
const normalizedHex = hex.toUpperCase();
|
|
3512
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3513
|
+
if (isUnreserved(decoded)) {
|
|
3514
|
+
output += decoded;
|
|
3515
|
+
} else {
|
|
3516
|
+
output += "%" + normalizedHex;
|
|
3517
|
+
}
|
|
3518
|
+
i += 2;
|
|
3519
|
+
continue;
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
if (isQueryFragmentCharacter(ch)) {
|
|
3523
|
+
output += ch;
|
|
3524
|
+
} else {
|
|
3525
|
+
const code = input.charCodeAt(i);
|
|
3526
|
+
if (code < 128) {
|
|
3527
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
3528
|
+
} else if (code < 55296 || code > 57343) {
|
|
3529
|
+
output += percentEncodeNonAscii(code);
|
|
3530
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
3531
|
+
const low = input.charCodeAt(i + 1);
|
|
3532
|
+
if (low >= 56320 && low <= 57343) {
|
|
3533
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3534
|
+
i++;
|
|
3535
|
+
} else {
|
|
3536
|
+
output += percentEncodeNonAscii(65533);
|
|
3537
|
+
}
|
|
3538
|
+
} else {
|
|
3539
|
+
output += percentEncodeNonAscii(65533);
|
|
3540
|
+
}
|
|
3366
3541
|
}
|
|
3367
3542
|
}
|
|
3368
3543
|
return output;
|
|
@@ -3385,14 +3560,18 @@ var require_utils2 = __commonJS({
|
|
|
3385
3560
|
function recomposeAuthority(component) {
|
|
3386
3561
|
const uriTokens = [];
|
|
3387
3562
|
if (component.userinfo !== void 0) {
|
|
3388
|
-
uriTokens.push(component.userinfo);
|
|
3563
|
+
uriTokens.push(encodeUserinfo(component.userinfo));
|
|
3389
3564
|
uriTokens.push("@");
|
|
3390
3565
|
}
|
|
3391
3566
|
if (component.host !== void 0) {
|
|
3392
|
-
let host =
|
|
3567
|
+
let host = component.host;
|
|
3393
3568
|
if (!isIPv4(host)) {
|
|
3394
|
-
|
|
3395
|
-
if (ipV6res.isIPV6
|
|
3569
|
+
let ipV6res = normalizeIPv6(host);
|
|
3570
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
3571
|
+
host = normalizePercentEncoding(host, true);
|
|
3572
|
+
ipV6res = normalizeIPv6(host);
|
|
3573
|
+
}
|
|
3574
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
3396
3575
|
host = `[${ipV6res.escapedHost}]`;
|
|
3397
3576
|
} else {
|
|
3398
3577
|
host = reescapeHostDelimiters(host, false);
|
|
@@ -3412,6 +3591,11 @@ var require_utils2 = __commonJS({
|
|
|
3412
3591
|
reescapeHostDelimiters,
|
|
3413
3592
|
normalizePercentEncoding,
|
|
3414
3593
|
normalizePathEncoding,
|
|
3594
|
+
serializePathEncoding,
|
|
3595
|
+
normalizeQueryFragmentEncoding,
|
|
3596
|
+
encodeUserinfo,
|
|
3597
|
+
encodeQuery,
|
|
3598
|
+
encodeFragment,
|
|
3415
3599
|
escapePreservingEscapes,
|
|
3416
3600
|
removeDotSegments,
|
|
3417
3601
|
isIPv4,
|
|
@@ -3427,7 +3611,7 @@ var require_schemes2 = __commonJS({
|
|
|
3427
3611
|
"node_modules/fast-uri/lib/schemes.js"(exports, module) {
|
|
3428
3612
|
"use strict";
|
|
3429
3613
|
var { isUUID } = require_utils2();
|
|
3430
|
-
var URN_REG =
|
|
3614
|
+
var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
3431
3615
|
var supportedSchemeNames = (
|
|
3432
3616
|
/** @type {const} */
|
|
3433
3617
|
[
|
|
@@ -3488,9 +3672,10 @@ var require_schemes2 = __commonJS({
|
|
|
3488
3672
|
wsComponent.secure = void 0;
|
|
3489
3673
|
}
|
|
3490
3674
|
if (wsComponent.resourceName) {
|
|
3491
|
-
const
|
|
3675
|
+
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3676
|
+
const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3492
3677
|
wsComponent.path = path && path !== "/" ? path : void 0;
|
|
3493
|
-
wsComponent.query =
|
|
3678
|
+
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3494
3679
|
wsComponent.resourceName = void 0;
|
|
3495
3680
|
}
|
|
3496
3681
|
wsComponent.fragment = void 0;
|
|
@@ -3502,7 +3687,7 @@ var require_schemes2 = __commonJS({
|
|
|
3502
3687
|
return urnComponent;
|
|
3503
3688
|
}
|
|
3504
3689
|
const matches = urnComponent.path.match(URN_REG);
|
|
3505
|
-
if (matches) {
|
|
3690
|
+
if (matches && matches[0] === urnComponent.path) {
|
|
3506
3691
|
const scheme = options.scheme || urnComponent.scheme || "urn";
|
|
3507
3692
|
urnComponent.nid = matches[1].toLowerCase();
|
|
3508
3693
|
urnComponent.nss = matches[2];
|
|
@@ -3636,8 +3821,17 @@ var require_schemes2 = __commonJS({
|
|
|
3636
3821
|
var require_fast_uri2 = __commonJS({
|
|
3637
3822
|
"node_modules/fast-uri/index.js"(exports, module) {
|
|
3638
3823
|
"use strict";
|
|
3639
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding,
|
|
3824
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils2();
|
|
3640
3825
|
var { SCHEMES, getSchemeHandler } = require_schemes2();
|
|
3826
|
+
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
3827
|
+
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
3828
|
+
function decodeValidScheme(scheme) {
|
|
3829
|
+
const decodedScheme = unescape(String(scheme));
|
|
3830
|
+
if (!VALID_SCHEME.test(decodedScheme)) {
|
|
3831
|
+
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
3832
|
+
}
|
|
3833
|
+
return decodedScheme;
|
|
3834
|
+
}
|
|
3641
3835
|
function normalize2(uri, options) {
|
|
3642
3836
|
if (typeof uri === "string") {
|
|
3643
3837
|
uri = /** @type {T} */
|
|
@@ -3650,12 +3844,34 @@ var require_fast_uri2 = __commonJS({
|
|
|
3650
3844
|
}
|
|
3651
3845
|
function resolve(baseURI, relativeURI, options) {
|
|
3652
3846
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3653
|
-
const {
|
|
3654
|
-
|
|
3655
|
-
|
|
3847
|
+
const {
|
|
3848
|
+
parsed: baseParsed,
|
|
3849
|
+
malformedAuthorityOrPort: baseMalformed,
|
|
3850
|
+
malformedPercentEncoding: baseMalformedPercentEncoding,
|
|
3851
|
+
malformedSchemeSpecific: baseMalformedSchemeSpecific,
|
|
3852
|
+
malformedHost: baseMalformedHost,
|
|
3853
|
+
malformedScheme: baseMalformedScheme
|
|
3854
|
+
} = parseWithStatus(baseURI, schemelessOptions);
|
|
3855
|
+
const {
|
|
3856
|
+
parsed: relativeParsed,
|
|
3857
|
+
malformedAuthorityOrPort: relativeMalformed,
|
|
3858
|
+
malformedPercentEncoding: relativeMalformedPercentEncoding,
|
|
3859
|
+
malformedSchemeSpecific: relativeMalformedSchemeSpecific,
|
|
3860
|
+
malformedHost: relativeMalformedHost,
|
|
3861
|
+
malformedScheme: relativeMalformedScheme
|
|
3862
|
+
} = parseWithStatus(relativeURI, schemelessOptions);
|
|
3863
|
+
if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
|
|
3656
3864
|
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3657
3865
|
}
|
|
3658
3866
|
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3867
|
+
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
3868
|
+
const resolvedHost = resolved.host;
|
|
3869
|
+
const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
3870
|
+
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
3871
|
+
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
|
|
3872
|
+
if (resolved.error && !encodedASCIIHost) {
|
|
3873
|
+
throw new Error(resolved.error);
|
|
3874
|
+
}
|
|
3659
3875
|
schemelessOptions.skipEscape = true;
|
|
3660
3876
|
return serialize(resolved, schemelessOptions);
|
|
3661
3877
|
}
|
|
@@ -3715,7 +3931,7 @@ var require_fast_uri2 = __commonJS({
|
|
|
3715
3931
|
function equal(uriA, uriB, options) {
|
|
3716
3932
|
const normalizedA = normalizeComparableURI(uriA, options);
|
|
3717
3933
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
3718
|
-
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA
|
|
3934
|
+
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
|
|
3719
3935
|
}
|
|
3720
3936
|
function serialize(cmpts, opts) {
|
|
3721
3937
|
const component = {
|
|
@@ -3736,19 +3952,22 @@ var require_fast_uri2 = __commonJS({
|
|
|
3736
3952
|
};
|
|
3737
3953
|
const options = Object.assign({}, opts);
|
|
3738
3954
|
const uriTokens = [];
|
|
3955
|
+
if (component.scheme) {
|
|
3956
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
3957
|
+
}
|
|
3739
3958
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
3740
3959
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
3960
|
+
const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
|
|
3961
|
+
const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
|
|
3741
3962
|
if (component.path !== void 0) {
|
|
3742
3963
|
if (!options.skipEscape) {
|
|
3743
|
-
component.path =
|
|
3744
|
-
if (component.scheme !== void 0) {
|
|
3745
|
-
component.path = component.path.split("%3A").join(":");
|
|
3746
|
-
}
|
|
3964
|
+
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
3747
3965
|
} else {
|
|
3748
3966
|
component.path = normalizePercentEncoding(component.path);
|
|
3749
3967
|
}
|
|
3750
3968
|
}
|
|
3751
3969
|
if (options.reference !== "suffix" && component.scheme) {
|
|
3970
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
3752
3971
|
uriTokens.push(component.scheme, ":");
|
|
3753
3972
|
}
|
|
3754
3973
|
const authority = recomposeAuthority(component);
|
|
@@ -3766,16 +3985,19 @@ var require_fast_uri2 = __commonJS({
|
|
|
3766
3985
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
3767
3986
|
s = removeDotSegments(s);
|
|
3768
3987
|
}
|
|
3988
|
+
if (pathNoScheme) {
|
|
3989
|
+
s = serializePathEncoding(s, true);
|
|
3990
|
+
}
|
|
3769
3991
|
if (authority === void 0 && s[0] === "/" && s[1] === "/") {
|
|
3770
3992
|
s = "/%2F" + s.slice(2);
|
|
3771
3993
|
}
|
|
3772
3994
|
uriTokens.push(s);
|
|
3773
3995
|
}
|
|
3774
3996
|
if (component.query !== void 0) {
|
|
3775
|
-
uriTokens.push("?", component.query);
|
|
3997
|
+
uriTokens.push("?", encodeQuery(component.query));
|
|
3776
3998
|
}
|
|
3777
3999
|
if (component.fragment !== void 0) {
|
|
3778
|
-
uriTokens.push("#", component.fragment);
|
|
4000
|
+
uriTokens.push("#", encodeFragment(component.fragment));
|
|
3779
4001
|
}
|
|
3780
4002
|
return uriTokens.join("");
|
|
3781
4003
|
}
|
|
@@ -3791,6 +4013,32 @@ var require_fast_uri2 = __commonJS({
|
|
|
3791
4013
|
}
|
|
3792
4014
|
return void 0;
|
|
3793
4015
|
}
|
|
4016
|
+
function hasMalformedPercentEncoding(component) {
|
|
4017
|
+
if (component === void 0) return false;
|
|
4018
|
+
let percent = component.indexOf("%");
|
|
4019
|
+
while (percent !== -1) {
|
|
4020
|
+
if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
|
|
4021
|
+
return true;
|
|
4022
|
+
}
|
|
4023
|
+
percent = component.indexOf("%", percent + 3);
|
|
4024
|
+
}
|
|
4025
|
+
return false;
|
|
4026
|
+
}
|
|
4027
|
+
function hasMalformedComponentPercentEncoding(matches) {
|
|
4028
|
+
const host = matches[4];
|
|
4029
|
+
return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !(host[0] === "[" && host[host.length - 1] === "]") && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
|
|
4030
|
+
}
|
|
4031
|
+
function canonicalizeHost(parsed, options, schemeHandler, isIP) {
|
|
4032
|
+
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && parsed.host[0] !== "[" && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
4033
|
+
try {
|
|
4034
|
+
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
4035
|
+
} catch (e) {
|
|
4036
|
+
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
4037
|
+
return true;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
return false;
|
|
4041
|
+
}
|
|
3794
4042
|
function parseWithStatus(uri, opts) {
|
|
3795
4043
|
const options = Object.assign({}, opts);
|
|
3796
4044
|
const parsed = {
|
|
@@ -3803,6 +4051,11 @@ var require_fast_uri2 = __commonJS({
|
|
|
3803
4051
|
fragment: void 0
|
|
3804
4052
|
};
|
|
3805
4053
|
let malformedAuthorityOrPort = false;
|
|
4054
|
+
let malformedPercentEncoding = false;
|
|
4055
|
+
let malformedSchemeSpecific = false;
|
|
4056
|
+
let malformedHost = false;
|
|
4057
|
+
let malformedIPLiteral = false;
|
|
4058
|
+
let malformedScheme = false;
|
|
3806
4059
|
let isIP = false;
|
|
3807
4060
|
if (options.reference === "suffix") {
|
|
3808
4061
|
if (options.scheme) {
|
|
@@ -3839,6 +4092,19 @@ var require_fast_uri2 = __commonJS({
|
|
|
3839
4092
|
parsed.path = matches[6] || "";
|
|
3840
4093
|
parsed.query = matches[7];
|
|
3841
4094
|
parsed.fragment = matches[8];
|
|
4095
|
+
if (parsed.scheme !== void 0) {
|
|
4096
|
+
const decodedScheme = unescape(parsed.scheme);
|
|
4097
|
+
if (VALID_SCHEME.test(decodedScheme)) {
|
|
4098
|
+
parsed.scheme = decodedScheme.toLowerCase();
|
|
4099
|
+
} else {
|
|
4100
|
+
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
4101
|
+
malformedScheme = true;
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
|
|
4105
|
+
if (malformedPercentEncoding) {
|
|
4106
|
+
parsed.error = parsed.error || "URI contains malformed percent-encoding.";
|
|
4107
|
+
}
|
|
3842
4108
|
if (isNaN(parsed.port)) {
|
|
3843
4109
|
parsed.port = matches[5];
|
|
3844
4110
|
}
|
|
@@ -3850,9 +4116,15 @@ var require_fast_uri2 = __commonJS({
|
|
|
3850
4116
|
if (parsed.host) {
|
|
3851
4117
|
const ipv4result = isIPv4(parsed.host);
|
|
3852
4118
|
if (ipv4result === false) {
|
|
4119
|
+
const bracketedIPLiteral = parsed.host[0] === "[" && parsed.host[parsed.host.length - 1] === "]";
|
|
3853
4120
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
3854
|
-
|
|
3855
|
-
|
|
4121
|
+
isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
4122
|
+
malformedIPLiteral = bracketedIPLiteral && ipv6result.error === true;
|
|
4123
|
+
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
4124
|
+
if (malformedIPLiteral) {
|
|
4125
|
+
parsed.error = parsed.error || "URI host is malformed.";
|
|
4126
|
+
malformedAuthorityOrPort = true;
|
|
4127
|
+
}
|
|
3856
4128
|
} else {
|
|
3857
4129
|
isIP = true;
|
|
3858
4130
|
}
|
|
@@ -3870,42 +4142,34 @@ var require_fast_uri2 = __commonJS({
|
|
|
3870
4142
|
parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
|
|
3871
4143
|
}
|
|
3872
4144
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
3873
|
-
|
|
3874
|
-
if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
3875
|
-
try {
|
|
3876
|
-
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
3877
|
-
} catch (e) {
|
|
3878
|
-
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
3879
|
-
}
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
4145
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
3882
4146
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
3883
4147
|
if (uri.indexOf("%") !== -1) {
|
|
3884
|
-
if (parsed.
|
|
3885
|
-
parsed.
|
|
3886
|
-
|
|
3887
|
-
if (parsed.host !== void 0) {
|
|
3888
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
4148
|
+
if (parsed.host !== void 0 && !malformedIPLiteral) {
|
|
4149
|
+
const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
4150
|
+
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
3889
4151
|
}
|
|
3890
4152
|
}
|
|
3891
4153
|
if (parsed.path) {
|
|
3892
4154
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
3893
4155
|
}
|
|
4156
|
+
if (parsed.query) {
|
|
4157
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
4158
|
+
}
|
|
3894
4159
|
if (parsed.fragment) {
|
|
3895
|
-
|
|
3896
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
3897
|
-
} catch {
|
|
3898
|
-
parsed.error = parsed.error || "URI malformed";
|
|
3899
|
-
}
|
|
4160
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
3900
4161
|
}
|
|
3901
4162
|
}
|
|
3902
4163
|
if (schemeHandler && schemeHandler.parse) {
|
|
3903
4164
|
schemeHandler.parse(parsed, options);
|
|
4165
|
+
if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
|
|
4166
|
+
malformedSchemeSpecific = true;
|
|
4167
|
+
}
|
|
3904
4168
|
}
|
|
3905
4169
|
} else {
|
|
3906
4170
|
parsed.error = parsed.error || "URI can not be parsed.";
|
|
3907
4171
|
}
|
|
3908
|
-
return { parsed, malformedAuthorityOrPort };
|
|
4172
|
+
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
3909
4173
|
}
|
|
3910
4174
|
function parse3(uri, opts) {
|
|
3911
4175
|
return parseWithStatus(uri, opts).parsed;
|
|
@@ -3914,20 +4178,28 @@ var require_fast_uri2 = __commonJS({
|
|
|
3914
4178
|
return normalizeStringWithStatus(uri, opts).normalized;
|
|
3915
4179
|
}
|
|
3916
4180
|
function normalizeStringWithStatus(uri, opts) {
|
|
3917
|
-
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
4181
|
+
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
3918
4182
|
return {
|
|
3919
|
-
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
3920
|
-
malformedAuthorityOrPort
|
|
4183
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
|
|
4184
|
+
malformedAuthorityOrPort,
|
|
4185
|
+
malformedPercentEncoding,
|
|
4186
|
+
malformedSchemeSpecific,
|
|
4187
|
+
malformedHost,
|
|
4188
|
+
malformedScheme
|
|
3921
4189
|
};
|
|
3922
4190
|
}
|
|
3923
4191
|
function normalizeComparableURI(uri, opts) {
|
|
3924
|
-
if (typeof uri
|
|
3925
|
-
|
|
3926
|
-
return malformedAuthorityOrPort ? void 0 : normalized;
|
|
4192
|
+
if (typeof uri !== "string" && typeof uri !== "object") {
|
|
4193
|
+
return void 0;
|
|
3927
4194
|
}
|
|
3928
|
-
|
|
3929
|
-
|
|
4195
|
+
let value;
|
|
4196
|
+
try {
|
|
4197
|
+
value = typeof uri === "string" ? uri : serialize(uri, opts);
|
|
4198
|
+
} catch {
|
|
4199
|
+
return void 0;
|
|
3930
4200
|
}
|
|
4201
|
+
const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
|
|
4202
|
+
return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
|
|
3931
4203
|
}
|
|
3932
4204
|
var fastUri = {
|
|
3933
4205
|
SCHEMES,
|
|
@@ -37072,13 +37344,13 @@ var HonoRequest = class {
|
|
|
37072
37344
|
return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
|
|
37073
37345
|
}
|
|
37074
37346
|
#getDecodedParam(key) {
|
|
37075
|
-
const paramKey = this.#matchResult[0][this.routeIndex][1][key];
|
|
37347
|
+
const paramKey = this.#matchResult[0][this.routeIndex]?.[1][key];
|
|
37076
37348
|
const param = this.#getParamValue(paramKey);
|
|
37077
37349
|
return param && tryDecodeURIComponent(param);
|
|
37078
37350
|
}
|
|
37079
37351
|
#getAllDecodedParams() {
|
|
37080
37352
|
const decoded = {};
|
|
37081
|
-
const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
|
|
37353
|
+
const keys = Object.keys(this.#matchResult[0][this.routeIndex]?.[1] ?? {});
|
|
37082
37354
|
for (const key of keys) {
|
|
37083
37355
|
const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
|
|
37084
37356
|
if (value !== void 0) {
|
|
@@ -38165,6 +38437,9 @@ var Hono = class _Hono {
|
|
|
38165
38437
|
};
|
|
38166
38438
|
};
|
|
38167
38439
|
|
|
38440
|
+
// node_modules/hono/dist/router/utils.js
|
|
38441
|
+
var createNullObject = () => /* @__PURE__ */ Object.create(null);
|
|
38442
|
+
|
|
38168
38443
|
// node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
38169
38444
|
var emptyParam = [];
|
|
38170
38445
|
function match(method, path) {
|
|
@@ -38215,7 +38490,7 @@ var Node = class _Node {
|
|
|
38215
38490
|
// handler index of a dynamic path, or -1 for a static path terminal
|
|
38216
38491
|
#index;
|
|
38217
38492
|
#varIndex;
|
|
38218
|
-
#children =
|
|
38493
|
+
#children = createNullObject();
|
|
38219
38494
|
insert(tokens, index, paramMap, context2, isStatic) {
|
|
38220
38495
|
let node = this;
|
|
38221
38496
|
for (let i = 0, len = tokens.length; i < len; i++) {
|
|
@@ -38299,7 +38574,7 @@ var Trie = class {
|
|
|
38299
38574
|
#root = new Node();
|
|
38300
38575
|
#index = 0;
|
|
38301
38576
|
// dynamic path -> [handler index, param assoc]; static paths are not registered
|
|
38302
|
-
paths =
|
|
38577
|
+
paths = createNullObject();
|
|
38303
38578
|
insert(path, isStatic) {
|
|
38304
38579
|
if (isStatic) {
|
|
38305
38580
|
this.#root.insert(path.split(""), 0, [], this.#context, true);
|
|
@@ -38358,22 +38633,16 @@ var Trie = class {
|
|
|
38358
38633
|
};
|
|
38359
38634
|
|
|
38360
38635
|
// node_modules/hono/dist/router/reg-exp-router/router.js
|
|
38361
|
-
var wildcardRegExpCache =
|
|
38636
|
+
var wildcardRegExpCache = createNullObject();
|
|
38362
38637
|
function buildWildcardRegExp(path) {
|
|
38363
38638
|
return wildcardRegExpCache[path] ??= new RegExp(
|
|
38364
|
-
|
|
38365
|
-
|
|
38366
|
-
(
|
|
38639
|
+
`^${path.replace(
|
|
38640
|
+
/\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g,
|
|
38641
|
+
(match2, metaChar) => metaChar ? `\\${metaChar}` : match2 === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match2 === "*" ? ONLY_WILDCARD_REG_EXP_STR : `/:${LABEL_REG_EXP_STR}`
|
|
38367
38642
|
)}$`
|
|
38368
38643
|
);
|
|
38369
38644
|
}
|
|
38370
|
-
function clearWildcardRegExpCache() {
|
|
38371
|
-
wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
38372
|
-
}
|
|
38373
38645
|
function findMiddleware(middleware, path) {
|
|
38374
|
-
if (!middleware) {
|
|
38375
|
-
return void 0;
|
|
38376
|
-
}
|
|
38377
38646
|
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
|
|
38378
38647
|
if (buildWildcardRegExp(k).test(path)) {
|
|
38379
38648
|
return [...middleware[k]];
|
|
@@ -38387,8 +38656,8 @@ var RegExpRouter = class {
|
|
|
38387
38656
|
#routes;
|
|
38388
38657
|
#tries;
|
|
38389
38658
|
constructor() {
|
|
38390
|
-
this.#middleware = { [METHOD_NAME_ALL]:
|
|
38391
|
-
this.#routes = { [METHOD_NAME_ALL]:
|
|
38659
|
+
this.#middleware = { [METHOD_NAME_ALL]: createNullObject() };
|
|
38660
|
+
this.#routes = { [METHOD_NAME_ALL]: createNullObject() };
|
|
38392
38661
|
this.#tries = { [METHOD_NAME_ALL]: new Trie() };
|
|
38393
38662
|
}
|
|
38394
38663
|
#insertPath(method, path) {
|
|
@@ -38401,117 +38670,86 @@ var RegExpRouter = class {
|
|
|
38401
38670
|
add(method, path, handler) {
|
|
38402
38671
|
const middleware = this.#middleware;
|
|
38403
38672
|
const routes = this.#routes;
|
|
38404
|
-
if (!middleware
|
|
38673
|
+
if (!middleware) {
|
|
38405
38674
|
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
|
|
38406
38675
|
}
|
|
38407
38676
|
if (!middleware[method]) {
|
|
38408
38677
|
this.#tries[method] = new Trie();
|
|
38409
|
-
[middleware, routes]
|
|
38410
|
-
handlerMap[method] =
|
|
38411
|
-
|
|
38678
|
+
for (const handlerMap of [middleware, routes]) {
|
|
38679
|
+
handlerMap[method] = createNullObject();
|
|
38680
|
+
for (const p in handlerMap[METHOD_NAME_ALL]) {
|
|
38412
38681
|
handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
|
|
38413
38682
|
this.#insertPath(method, p);
|
|
38414
|
-
}
|
|
38415
|
-
}
|
|
38683
|
+
}
|
|
38684
|
+
}
|
|
38416
38685
|
}
|
|
38417
38686
|
if (path === "/*") {
|
|
38418
38687
|
path = "*";
|
|
38419
38688
|
}
|
|
38420
|
-
const
|
|
38689
|
+
const methods = method === METHOD_NAME_ALL ? Object.keys(middleware) : [method];
|
|
38421
38690
|
if (/\*$/.test(path)) {
|
|
38422
38691
|
const re2 = buildWildcardRegExp(path);
|
|
38423
|
-
|
|
38424
|
-
if (
|
|
38692
|
+
for (const m of methods) {
|
|
38693
|
+
if (!middleware[m][path]) {
|
|
38425
38694
|
this.#insertPath(m, path);
|
|
38426
38695
|
middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
|
|
38427
38696
|
}
|
|
38428
|
-
}
|
|
38429
|
-
|
|
38430
|
-
|
|
38431
|
-
|
|
38432
|
-
re2.test(p) &&
|
|
38433
|
-
}
|
|
38434
|
-
}
|
|
38435
|
-
});
|
|
38436
|
-
Object.keys(routes).forEach((m) => {
|
|
38437
|
-
if (method === METHOD_NAME_ALL || method === m) {
|
|
38438
|
-
Object.keys(routes[m]).forEach(
|
|
38439
|
-
(p) => re2.test(p) && routes[m][p].push([handler, paramCount])
|
|
38440
|
-
);
|
|
38697
|
+
}
|
|
38698
|
+
for (const handlerMap of [middleware, routes]) {
|
|
38699
|
+
for (const m of methods) {
|
|
38700
|
+
for (const p in handlerMap[m]) {
|
|
38701
|
+
re2.test(p) && handlerMap[m][p].push([handler, path]);
|
|
38702
|
+
}
|
|
38441
38703
|
}
|
|
38442
|
-
}
|
|
38704
|
+
}
|
|
38443
38705
|
return;
|
|
38444
38706
|
}
|
|
38445
38707
|
const paths = checkOptionalParameter(path) || [path];
|
|
38446
|
-
for (
|
|
38447
|
-
const
|
|
38448
|
-
|
|
38449
|
-
|
|
38450
|
-
|
|
38451
|
-
this.#insertPath(m, path2);
|
|
38452
|
-
routes[m][path2] = [
|
|
38453
|
-
...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
|
|
38454
|
-
];
|
|
38455
|
-
}
|
|
38456
|
-
routes[m][path2].push([handler, paramCount - len + i + 1]);
|
|
38708
|
+
for (const path2 of paths) {
|
|
38709
|
+
for (const m of methods) {
|
|
38710
|
+
if (!routes[m][path2]) {
|
|
38711
|
+
this.#insertPath(m, path2);
|
|
38712
|
+
routes[m][path2] = findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || [];
|
|
38457
38713
|
}
|
|
38458
|
-
|
|
38714
|
+
routes[m][path2].push([handler, path2]);
|
|
38715
|
+
}
|
|
38459
38716
|
}
|
|
38460
38717
|
}
|
|
38461
38718
|
match = match;
|
|
38462
38719
|
buildAllMatchers() {
|
|
38463
|
-
const matchers =
|
|
38464
|
-
Object.keys(this.#routes)
|
|
38465
|
-
matchers[method]
|
|
38466
|
-
}
|
|
38720
|
+
const matchers = createNullObject();
|
|
38721
|
+
for (const method of Object.keys(this.#routes)) {
|
|
38722
|
+
matchers[method] = this.#buildMatcher(method);
|
|
38723
|
+
}
|
|
38467
38724
|
this.#middleware = this.#routes = this.#tries = void 0;
|
|
38468
|
-
|
|
38725
|
+
wildcardRegExpCache = createNullObject();
|
|
38469
38726
|
return matchers;
|
|
38470
38727
|
}
|
|
38471
38728
|
#buildMatcher(method) {
|
|
38472
38729
|
const middleware = this.#middleware[method];
|
|
38473
38730
|
const routes = this.#routes[method];
|
|
38474
38731
|
const trie = this.#tries[method];
|
|
38475
|
-
const staticMap =
|
|
38732
|
+
const staticMap = createNullObject();
|
|
38476
38733
|
const handlerData = [];
|
|
38477
|
-
[
|
|
38734
|
+
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
38735
|
+
for (const r of [middleware, routes]) {
|
|
38478
38736
|
for (const path in r) {
|
|
38479
38737
|
const handlers = r[path];
|
|
38480
38738
|
const pathData = trie.paths[path];
|
|
38481
38739
|
if (!pathData) {
|
|
38482
|
-
staticMap[path] = [handlers.map(([h]) => [h,
|
|
38483
|
-
continue;
|
|
38484
|
-
}
|
|
38485
|
-
const paramAssoc = pathData[1];
|
|
38486
|
-
handlerData[pathData[0]] = handlers.map(([h, paramCount]) => {
|
|
38487
|
-
const paramIndexMap = /* @__PURE__ */ Object.create(null);
|
|
38488
|
-
paramCount -= 1;
|
|
38489
|
-
for (; paramCount >= 0; paramCount--) {
|
|
38490
|
-
const [key, value] = paramAssoc[paramCount];
|
|
38491
|
-
paramIndexMap[key] = value;
|
|
38492
|
-
}
|
|
38493
|
-
return [h, paramIndexMap];
|
|
38494
|
-
});
|
|
38495
|
-
}
|
|
38496
|
-
});
|
|
38497
|
-
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
38498
|
-
for (let i = 0, len = handlerData.length; i < len; i++) {
|
|
38499
|
-
for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
|
|
38500
|
-
const map = handlerData[i][j]?.[1];
|
|
38501
|
-
if (!map) {
|
|
38740
|
+
staticMap[path] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam];
|
|
38502
38741
|
continue;
|
|
38503
38742
|
}
|
|
38504
|
-
|
|
38505
|
-
|
|
38506
|
-
|
|
38507
|
-
|
|
38743
|
+
handlerData[pathData[0]] = handlers.map(([h, handlerPath]) => [
|
|
38744
|
+
h,
|
|
38745
|
+
trie.paths[handlerPath][1].reduceRight((map, [key], i) => {
|
|
38746
|
+
map[key] = paramReplacementMap[pathData[1][i][1]];
|
|
38747
|
+
return map;
|
|
38748
|
+
}, createNullObject())
|
|
38749
|
+
]);
|
|
38508
38750
|
}
|
|
38509
38751
|
}
|
|
38510
|
-
|
|
38511
|
-
for (const i in indexReplacementMap) {
|
|
38512
|
-
handlerMap[i] = handlerData[indexReplacementMap[i]];
|
|
38513
|
-
}
|
|
38514
|
-
return [regexp, handlerMap, staticMap];
|
|
38752
|
+
return [regexp, indexReplacementMap.map((i) => handlerData[i]), staticMap];
|
|
38515
38753
|
}
|
|
38516
38754
|
};
|
|
38517
38755
|
|
|
@@ -38571,11 +38809,11 @@ var SmartRouter = class {
|
|
|
38571
38809
|
};
|
|
38572
38810
|
|
|
38573
38811
|
// node_modules/hono/dist/router/trie-router/node.js
|
|
38574
|
-
var emptyParams =
|
|
38812
|
+
var emptyParams = createNullObject();
|
|
38575
38813
|
var order = 0;
|
|
38576
38814
|
var Node2 = class _Node2 {
|
|
38577
38815
|
#methods = [];
|
|
38578
|
-
#children =
|
|
38816
|
+
#children = createNullObject();
|
|
38579
38817
|
#patterns = [];
|
|
38580
38818
|
#pattern;
|
|
38581
38819
|
#params = emptyParams;
|
|
@@ -38612,7 +38850,7 @@ var Node2 = class _Node2 {
|
|
|
38612
38850
|
const m = node.#methods[i];
|
|
38613
38851
|
const handlerSet = m[method] || m[METHOD_NAME_ALL];
|
|
38614
38852
|
if (handlerSet) {
|
|
38615
|
-
handlerSet.params =
|
|
38853
|
+
handlerSet.params = createNullObject();
|
|
38616
38854
|
handlerSets.push(handlerSet);
|
|
38617
38855
|
for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) {
|
|
38618
38856
|
const key = handlerSet.possibleKeys[i2];
|
|
@@ -40550,33 +40788,11 @@ function isObject2(input) {
|
|
|
40550
40788
|
return false;
|
|
40551
40789
|
}
|
|
40552
40790
|
const prototype = Object.getPrototypeOf(input);
|
|
40553
|
-
|
|
40554
|
-
return true;
|
|
40555
|
-
}
|
|
40556
|
-
let proto = prototype;
|
|
40557
|
-
while (Object.getPrototypeOf(proto) !== null) {
|
|
40558
|
-
proto = Object.getPrototypeOf(proto);
|
|
40559
|
-
}
|
|
40560
|
-
return prototype === proto;
|
|
40791
|
+
return prototype === null || Object.getPrototypeOf(prototype) === null;
|
|
40561
40792
|
}
|
|
40562
|
-
function
|
|
40563
|
-
|
|
40564
|
-
for (const header of headers) {
|
|
40565
|
-
if (!header)
|
|
40566
|
-
continue;
|
|
40567
|
-
for (const parameter of Object.keys(header)) {
|
|
40568
|
-
if (parameters.has(parameter)) {
|
|
40569
|
-
return false;
|
|
40570
|
-
}
|
|
40571
|
-
parameters.add(parameter);
|
|
40572
|
-
}
|
|
40573
|
-
}
|
|
40574
|
-
return true;
|
|
40793
|
+
function isJwkSet(input) {
|
|
40794
|
+
return isObject2(input) && Array.isArray(input.keys) && Array.from(input.keys).every(isObject2);
|
|
40575
40795
|
}
|
|
40576
|
-
var isJWK = (key) => isObject2(key) && typeof key.kty === "string";
|
|
40577
|
-
var isPrivateJWK = (key) => key.kty !== "oct" && (key.kty === "AKP" && typeof key.priv === "string" || typeof key.d === "string");
|
|
40578
|
-
var isPublicJWK = (key) => key.kty !== "oct" && key.d === void 0 && key.priv === void 0;
|
|
40579
|
-
var isSecretJWK = (key) => key.kty === "oct" && typeof key.k === "string";
|
|
40580
40796
|
|
|
40581
40797
|
// node_modules/jose/dist/webapi/lib/helpers.js
|
|
40582
40798
|
var unprotected = Symbol();
|
|
@@ -40625,6 +40841,26 @@ async function jwkToKey(entry, jwk) {
|
|
|
40625
40841
|
return crypto.subtle.importKey("jwk", keyData, algorithm, jwk.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);
|
|
40626
40842
|
}
|
|
40627
40843
|
|
|
40844
|
+
// node_modules/jose/dist/webapi/lib/jwk_metadata.js
|
|
40845
|
+
function snapshotJwk(jwk) {
|
|
40846
|
+
return { __proto__: null, ...jwk };
|
|
40847
|
+
}
|
|
40848
|
+
function normalizeJwk(jwk) {
|
|
40849
|
+
const normalized = snapshotJwk(jwk);
|
|
40850
|
+
if (normalized.ext !== void 0 && typeof normalized.ext !== "boolean") {
|
|
40851
|
+
throw new TypeError('"ext" (Extractable) Parameter must be a boolean');
|
|
40852
|
+
}
|
|
40853
|
+
if (normalized.key_ops !== void 0) {
|
|
40854
|
+
const value = normalized.key_ops;
|
|
40855
|
+
const keyOps = Array.isArray(value) ? [...value] : void 0;
|
|
40856
|
+
if (!keyOps || keyOps.some((operation) => typeof operation !== "string") || new Set(keyOps).size !== keyOps.length) {
|
|
40857
|
+
throw new TypeError('"key_ops" (Key Operations) Parameter must be an array of unique strings');
|
|
40858
|
+
}
|
|
40859
|
+
normalized.key_ops = keyOps;
|
|
40860
|
+
}
|
|
40861
|
+
return normalized;
|
|
40862
|
+
}
|
|
40863
|
+
|
|
40628
40864
|
// node_modules/jose/dist/webapi/lib/key.js
|
|
40629
40865
|
var tag = (key) => key[Symbol.toStringTag];
|
|
40630
40866
|
var jwkMatchesOp = (entry, key, usage) => {
|
|
@@ -40650,12 +40886,17 @@ function checkKeyType(entry, key, usage) {
|
|
|
40650
40886
|
const privateKey = usage === "decrypt" || usage === "sign";
|
|
40651
40887
|
if (secret && key instanceof Uint8Array)
|
|
40652
40888
|
return [BYTES, key];
|
|
40653
|
-
if (
|
|
40654
|
-
|
|
40889
|
+
if (isObject2(key)) {
|
|
40890
|
+
const normalized = normalizeJwk(key);
|
|
40891
|
+
if (typeof normalized.kty !== "string") {
|
|
40892
|
+
throw new TypeError(secret ? withAlg(alg, key, "CryptoKey", "KeyObject", "JSON Web Key", "Uint8Array") : withAlg(alg, key, "CryptoKey", "KeyObject", "JSON Web Key"));
|
|
40893
|
+
}
|
|
40894
|
+
const valid = secret ? normalized.kty === "oct" && typeof normalized.k === "string" : normalized.kty !== "oct" && (privateKey ? normalized.kty === "AKP" && typeof normalized.priv === "string" || typeof normalized.d === "string" : normalized.d === void 0 && normalized.priv === void 0);
|
|
40895
|
+
if (!valid) {
|
|
40655
40896
|
throw new TypeError(secret ? `JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present` : `JSON Web Key for this operation must be a ${privateKey ? "private" : "public"} JWK`);
|
|
40656
40897
|
}
|
|
40657
|
-
jwkMatchesOp(entry,
|
|
40658
|
-
return [JWK, key];
|
|
40898
|
+
jwkMatchesOp(entry, normalized, usage);
|
|
40899
|
+
return [JWK, key, normalized];
|
|
40659
40900
|
}
|
|
40660
40901
|
if (!isKeyLike(key)) {
|
|
40661
40902
|
throw new TypeError(secret ? withAlg(alg, key, "CryptoKey", "KeyObject", "JSON Web Key", "Uint8Array") : withAlg(alg, key, "CryptoKey", "KeyObject", "JSON Web Key"));
|
|
@@ -40694,7 +40935,7 @@ function cached2(key, alg, value) {
|
|
|
40694
40935
|
if (entry) {
|
|
40695
40936
|
entry[alg] = value;
|
|
40696
40937
|
} else {
|
|
40697
|
-
cache.set(key, {
|
|
40938
|
+
cache.set(key, { [alg]: value });
|
|
40698
40939
|
}
|
|
40699
40940
|
}
|
|
40700
40941
|
return value ?? entry?.[alg];
|
|
@@ -40719,8 +40960,9 @@ async function prepareKey(entry, key, usage) {
|
|
|
40719
40960
|
return tagged[1];
|
|
40720
40961
|
case JWK: {
|
|
40721
40962
|
const key2 = tagged[1];
|
|
40722
|
-
|
|
40723
|
-
|
|
40963
|
+
const normalized = tagged[2];
|
|
40964
|
+
if (normalized.kty === "oct") {
|
|
40965
|
+
return decode2(normalized.k);
|
|
40724
40966
|
}
|
|
40725
40967
|
if (!Object.isFrozen(key2)) {
|
|
40726
40968
|
const { key_ops } = key2;
|
|
@@ -40728,7 +40970,7 @@ async function prepareKey(entry, key, usage) {
|
|
|
40728
40970
|
Object.freeze(key_ops);
|
|
40729
40971
|
Object.freeze(key2);
|
|
40730
40972
|
}
|
|
40731
|
-
return handleJWK(key2,
|
|
40973
|
+
return handleJWK(key2, normalized, entry);
|
|
40732
40974
|
}
|
|
40733
40975
|
case KEYOBJECT: {
|
|
40734
40976
|
const keyObject = tagged[1];
|
|
@@ -40787,6 +41029,16 @@ function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader,
|
|
|
40787
41029
|
}
|
|
40788
41030
|
return protectedHeader.crit;
|
|
40789
41031
|
}
|
|
41032
|
+
function validateB64(protectedHeader, extensions) {
|
|
41033
|
+
if (extensions.includes("b64")) {
|
|
41034
|
+
const b64 = protectedHeader.b64;
|
|
41035
|
+
if (typeof b64 !== "boolean") {
|
|
41036
|
+
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
41037
|
+
}
|
|
41038
|
+
return b64;
|
|
41039
|
+
}
|
|
41040
|
+
return true;
|
|
41041
|
+
}
|
|
40790
41042
|
|
|
40791
41043
|
// node_modules/jose/dist/webapi/lib/signing.js
|
|
40792
41044
|
async function getSigKey(entry, key, usage) {
|
|
@@ -40886,64 +41138,42 @@ function jwsAlgorithm(alg) {
|
|
|
40886
41138
|
function prepareVerify(options) {
|
|
40887
41139
|
return [options && validateAlgorithms("algorithms", options.algorithms), options?.crit];
|
|
40888
41140
|
}
|
|
40889
|
-
|
|
40890
|
-
|
|
40891
|
-
|
|
40892
|
-
|
|
40893
|
-
|
|
40894
|
-
|
|
40895
|
-
let joseHeader;
|
|
40896
|
-
if (header !== void 0) {
|
|
40897
|
-
if (!isDisjoint(parsedProt, header)) {
|
|
40898
|
-
throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
|
|
40899
|
-
}
|
|
40900
|
-
joseHeader = { ...parsedProt, ...header };
|
|
40901
|
-
} else {
|
|
40902
|
-
joseHeader = parsedProt;
|
|
40903
|
-
}
|
|
40904
|
-
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader);
|
|
40905
|
-
let b64 = true;
|
|
40906
|
-
if (extensions.includes("b64")) {
|
|
40907
|
-
b64 = parsedProt.b64;
|
|
40908
|
-
if (typeof b64 !== "boolean") {
|
|
40909
|
-
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
40910
|
-
}
|
|
40911
|
-
}
|
|
40912
|
-
const { alg } = joseHeader;
|
|
41141
|
+
function parseProtectedHeader(encodedProtected, parsedProtected = encodedProtected === void 0 ? {} : parseJoseHeader(encodedProtected, JWSInvalid, "JWS Protected Header is invalid")) {
|
|
41142
|
+
return parsedProtected;
|
|
41143
|
+
}
|
|
41144
|
+
function validateJwsHeaders(parsedProt, joseHeader, shared) {
|
|
41145
|
+
const b64 = validateB64(parsedProt, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader));
|
|
41146
|
+
const alg = joseHeader.alg;
|
|
40913
41147
|
if (typeof alg !== "string" || !alg) {
|
|
40914
41148
|
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
40915
41149
|
}
|
|
40916
41150
|
if (shared[0] && !shared[0].has(alg)) {
|
|
40917
41151
|
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
|
|
40918
41152
|
}
|
|
40919
|
-
|
|
40920
|
-
|
|
40921
|
-
|
|
40922
|
-
|
|
40923
|
-
|
|
40924
|
-
|
|
41153
|
+
return [b64, alg];
|
|
41154
|
+
}
|
|
41155
|
+
function encodeCompactUnencodedPayload(payload) {
|
|
41156
|
+
try {
|
|
41157
|
+
return encode2(payload);
|
|
41158
|
+
} catch {
|
|
41159
|
+
throw new JWSInvalid("JWS Compact Serialization payload must use only ASCII characters");
|
|
40925
41160
|
}
|
|
41161
|
+
}
|
|
41162
|
+
async function verifyPrepared(jws, shared, key, encodedProtected, parsedProt, alg, signingPayload) {
|
|
40926
41163
|
let resolvedKey = false;
|
|
40927
41164
|
if (typeof key === "function") {
|
|
40928
41165
|
key = await key(parsedProt, jws);
|
|
40929
41166
|
resolvedKey = true;
|
|
40930
41167
|
}
|
|
41168
|
+
const b64 = typeof signingPayload === "string";
|
|
40931
41169
|
const entry = jwsAlgorithm(alg);
|
|
40932
|
-
const data = concat(encodedProtected !== void 0 ? encode2(encodedProtected) : new Uint8Array(), encode2("."),
|
|
41170
|
+
const data = concat(encodedProtected !== void 0 ? encode2(encodedProtected) : new Uint8Array(), encode2("."), b64 ? shared[2] ??= encodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload);
|
|
40933
41171
|
const signature = decodeBase64url(jws.signature, "signature", JWSInvalid);
|
|
40934
41172
|
const k = await prepareKey(entry, key, "verify");
|
|
40935
|
-
|
|
40936
|
-
if (!verified) {
|
|
41173
|
+
if (!await verify(entry, k, signature, data)) {
|
|
40937
41174
|
throw new JWSSignatureVerificationFailed();
|
|
40938
41175
|
}
|
|
40939
|
-
|
|
40940
|
-
if (b64) {
|
|
40941
|
-
payload = decodeBase64url(inputPayload, "payload", JWSInvalid);
|
|
40942
|
-
} else if (typeof inputPayload === "string") {
|
|
40943
|
-
payload = encoder.encode(inputPayload);
|
|
40944
|
-
} else {
|
|
40945
|
-
payload = inputPayload;
|
|
40946
|
-
}
|
|
41176
|
+
const payload = b64 ? decodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload;
|
|
40947
41177
|
return [payload, parsedProt, b64, k, resolvedKey];
|
|
40948
41178
|
}
|
|
40949
41179
|
async function verifyCompact(jws, shared, key) {
|
|
@@ -40957,7 +41187,11 @@ async function verifyCompact(jws, shared, key) {
|
|
|
40957
41187
|
if (length !== 3) {
|
|
40958
41188
|
throw new JWSInvalid("Invalid Compact JWS");
|
|
40959
41189
|
}
|
|
40960
|
-
|
|
41190
|
+
const compactJws = { payload, protected: protectedHeader, signature };
|
|
41191
|
+
const parsedProt = parseProtectedHeader(protectedHeader);
|
|
41192
|
+
const [b64, alg] = validateJwsHeaders(parsedProt, parsedProt, shared);
|
|
41193
|
+
const signingPayload = b64 ? payload : encodeCompactUnencodedPayload(payload);
|
|
41194
|
+
return verifyPrepared(compactJws, shared, key, protectedHeader, parsedProt, alg, signingPayload);
|
|
40961
41195
|
}
|
|
40962
41196
|
|
|
40963
41197
|
// node_modules/jose/dist/webapi/lib/jwt_claims_set.js
|
|
@@ -40972,13 +41206,22 @@ var multipliers = {
|
|
|
40972
41206
|
};
|
|
40973
41207
|
var REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
|
|
40974
41208
|
var checkFailed = "check_failed";
|
|
41209
|
+
function invalidDuration() {
|
|
41210
|
+
throw new TypeError("Invalid time period format");
|
|
41211
|
+
}
|
|
40975
41212
|
function secs(str) {
|
|
41213
|
+
if (typeof str !== "string") {
|
|
41214
|
+
invalidDuration();
|
|
41215
|
+
}
|
|
40976
41216
|
const matched = REGEX.exec(str);
|
|
40977
41217
|
if (!matched || matched[4] && matched[1]) {
|
|
40978
|
-
|
|
41218
|
+
invalidDuration();
|
|
40979
41219
|
}
|
|
40980
41220
|
const value = parseFloat(matched[2]);
|
|
40981
41221
|
const numericDate = Math.round(value * multipliers[matched[3][0].toLowerCase()]);
|
|
41222
|
+
if (!Number.isFinite(numericDate)) {
|
|
41223
|
+
invalidDuration();
|
|
41224
|
+
}
|
|
40982
41225
|
if (matched[1] === "-" || matched[4] === "ago") {
|
|
40983
41226
|
return -numericDate;
|
|
40984
41227
|
}
|
|
@@ -40991,10 +41234,8 @@ function validateInput(label, input) {
|
|
|
40991
41234
|
return input;
|
|
40992
41235
|
}
|
|
40993
41236
|
var normalizeTyp = (value) => {
|
|
40994
|
-
|
|
40995
|
-
|
|
40996
|
-
}
|
|
40997
|
-
return `application/${value.toLowerCase()}`;
|
|
41237
|
+
const normalized = value.toLowerCase();
|
|
41238
|
+
return value.includes("/") ? normalized : `application/${normalized}`;
|
|
40998
41239
|
};
|
|
40999
41240
|
var checkAudiencePresence = (audPayload, audOption) => {
|
|
41000
41241
|
if (typeof audPayload === "string") {
|
|
@@ -41027,7 +41268,7 @@ function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
|
41027
41268
|
throw new JWTInvalid("JWT Claims Set must be a top-level JSON object");
|
|
41028
41269
|
}
|
|
41029
41270
|
const { typ } = options;
|
|
41030
|
-
if (typ && (typeof protectedHeader.typ !== "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
41271
|
+
if (typ !== void 0 && (typeof protectedHeader.typ !== "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
41031
41272
|
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, "typ", checkFailed);
|
|
41032
41273
|
}
|
|
41033
41274
|
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
|
|
@@ -41066,7 +41307,7 @@ function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
|
41066
41307
|
}
|
|
41067
41308
|
validateInput("clockTolerance option", tolerance);
|
|
41068
41309
|
const { currentDate } = options;
|
|
41069
|
-
const now = validateInput("currentDate option", epoch(currentDate
|
|
41310
|
+
const now = validateInput("currentDate option", epoch(currentDate === void 0 ? /* @__PURE__ */ new Date() : currentDate));
|
|
41070
41311
|
const iat = validateNumericDate(payload, "iat", maxTokenAge !== void 0);
|
|
41071
41312
|
const nbf = validateNumericDate(payload, "nbf");
|
|
41072
41313
|
if (nbf !== void 0) {
|
|
@@ -41082,11 +41323,11 @@ function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
|
41082
41323
|
}
|
|
41083
41324
|
if (maxTokenAge !== void 0) {
|
|
41084
41325
|
const age = now - iat;
|
|
41085
|
-
const max = typeof maxTokenAge === "number" ? maxTokenAge : secs(maxTokenAge);
|
|
41326
|
+
const max = validateInput("maxTokenAge option", typeof maxTokenAge === "number" ? maxTokenAge : secs(maxTokenAge));
|
|
41086
41327
|
if (age - tolerance > max) {
|
|
41087
41328
|
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, "iat", checkFailed);
|
|
41088
41329
|
}
|
|
41089
|
-
if (age <
|
|
41330
|
+
if (age < -tolerance) {
|
|
41090
41331
|
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, "iat", checkFailed);
|
|
41091
41332
|
}
|
|
41092
41333
|
}
|
|
@@ -41108,74 +41349,61 @@ async function jwtVerify(jwt, key, options) {
|
|
|
41108
41349
|
}
|
|
41109
41350
|
|
|
41110
41351
|
// node_modules/jose/dist/webapi/jwks/local.js
|
|
41111
|
-
function
|
|
41112
|
-
const
|
|
41113
|
-
|
|
41114
|
-
|
|
41115
|
-
}
|
|
41116
|
-
return entry;
|
|
41352
|
+
function isUsableJWK(jwk, entry, alg, kid) {
|
|
41353
|
+
const { kty, key_ops, ext, kid: jwkKid, alg: jwkAlg, use, crv } = snapshotJwk(jwk);
|
|
41354
|
+
const keyOps = Array.isArray(key_ops) ? [...key_ops] : key_ops;
|
|
41355
|
+
return (ext === void 0 || typeof ext === "boolean") && (keyOps === void 0 || Array.isArray(keyOps) && keyOps.every((operation, index) => typeof operation === "string" && keyOps.indexOf(operation) === index) && keyOps.includes("verify")) && entry.kty.includes(kty) && (kid === void 0 || typeof kid === "string" && kid === jwkKid) && (jwkAlg === void 0 ? kty !== "AKP" : alg === jwkAlg) && (use === void 0 || use === "sig") && (!entry.crv || crv === entry.crv);
|
|
41117
41356
|
}
|
|
41118
|
-
function
|
|
41119
|
-
|
|
41120
|
-
|
|
41357
|
+
async function importWithAlgCache(cache2, jwk, entry) {
|
|
41358
|
+
const cached3 = cache2.get(jwk) || cache2.set(jwk, {}).get(jwk);
|
|
41359
|
+
const { alg } = entry;
|
|
41360
|
+
if (cached3[alg] === void 0) {
|
|
41361
|
+
const key = await jwkToKey(entry, { ...jwk, alg, ext: true });
|
|
41362
|
+
if (key.type !== "public") {
|
|
41363
|
+
throw new JWKSInvalid("JSON Web Key Set members must be public keys");
|
|
41364
|
+
}
|
|
41365
|
+
cached3[alg] = key;
|
|
41121
41366
|
}
|
|
41122
|
-
|
|
41123
|
-
return Array.isArray(keys) && keys.every(isObject2);
|
|
41367
|
+
return cached3[alg];
|
|
41124
41368
|
}
|
|
41125
|
-
|
|
41126
|
-
|
|
41127
|
-
|
|
41128
|
-
|
|
41129
|
-
|
|
41130
|
-
throw new JWKSInvalid("JSON Web Key Set malformed");
|
|
41131
|
-
}
|
|
41132
|
-
this.#jwks = structuredClone(jwks);
|
|
41369
|
+
function createLocalJWKSet(jwks) {
|
|
41370
|
+
let snapshot;
|
|
41371
|
+
try {
|
|
41372
|
+
snapshot = structuredClone(jwks);
|
|
41373
|
+
} catch {
|
|
41133
41374
|
}
|
|
41134
|
-
|
|
41135
|
-
|
|
41375
|
+
if (!isJwkSet(snapshot)) {
|
|
41376
|
+
throw new JWKSInvalid("JSON Web Key Set malformed");
|
|
41136
41377
|
}
|
|
41137
|
-
|
|
41378
|
+
const cached3 = /* @__PURE__ */ new WeakMap();
|
|
41379
|
+
const localJWKSet = async (protectedHeader, token) => {
|
|
41138
41380
|
const { alg, kid } = { ...protectedHeader, ...token?.header };
|
|
41139
|
-
const entry =
|
|
41140
|
-
|
|
41381
|
+
const entry = typeof alg === "string" ? JWS[alg] : void 0;
|
|
41382
|
+
if (!entry || entry.secret) {
|
|
41383
|
+
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
41384
|
+
}
|
|
41385
|
+
const candidates = snapshot.keys.filter((jwk2) => isUsableJWK(jwk2, entry, alg, kid));
|
|
41141
41386
|
const { 0: jwk, length } = candidates;
|
|
41142
|
-
if (length
|
|
41387
|
+
if (!length) {
|
|
41143
41388
|
throw new JWKSNoMatchingKey();
|
|
41144
41389
|
}
|
|
41145
41390
|
if (length !== 1) {
|
|
41146
41391
|
const error2 = new JWKSMultipleMatchingKeys();
|
|
41147
|
-
const _cached = this.#cached;
|
|
41148
41392
|
error2[Symbol.asyncIterator] = async function* () {
|
|
41149
41393
|
for (const jwk2 of candidates) {
|
|
41150
41394
|
try {
|
|
41151
|
-
yield await importWithAlgCache(
|
|
41395
|
+
yield await importWithAlgCache(cached3, jwk2, entry);
|
|
41152
41396
|
} catch {
|
|
41153
41397
|
}
|
|
41154
41398
|
}
|
|
41155
41399
|
};
|
|
41156
41400
|
throw error2;
|
|
41157
41401
|
}
|
|
41158
|
-
return importWithAlgCache(
|
|
41159
|
-
}
|
|
41160
|
-
|
|
41161
|
-
|
|
41162
|
-
const cached3 = cache2.get(jwk) || cache2.set(jwk, { __proto__: null }).get(jwk);
|
|
41163
|
-
if (cached3[entry.alg] === void 0) {
|
|
41164
|
-
const key = await jwkToKey(entry, { ...jwk, alg: entry.alg, ext: true });
|
|
41165
|
-
if (key.type !== "public") {
|
|
41166
|
-
throw new JWKSInvalid("JSON Web Key Set members must be public keys");
|
|
41167
|
-
}
|
|
41168
|
-
cached3[entry.alg] = key;
|
|
41169
|
-
}
|
|
41170
|
-
return cached3[entry.alg];
|
|
41171
|
-
}
|
|
41172
|
-
function createLocalJWKSet(jwks) {
|
|
41173
|
-
const set = new LocalJWKSetImpl(jwks);
|
|
41174
|
-
const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
41175
|
-
Object.defineProperty(localJWKSet, "jwks", {
|
|
41176
|
-
value: () => structuredClone(set.jwks())
|
|
41402
|
+
return importWithAlgCache(cached3, jwk, entry);
|
|
41403
|
+
};
|
|
41404
|
+
return Object.defineProperty(localJWKSet, "jwks", {
|
|
41405
|
+
value: () => structuredClone(snapshot)
|
|
41177
41406
|
});
|
|
41178
|
-
return localJWKSet;
|
|
41179
41407
|
}
|
|
41180
41408
|
|
|
41181
41409
|
// node_modules/jose/dist/webapi/jwks/remote.js
|
|
@@ -41185,7 +41413,7 @@ function isCloudflareWorkers() {
|
|
|
41185
41413
|
var USER_AGENT;
|
|
41186
41414
|
if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
|
|
41187
41415
|
const NAME = "jose";
|
|
41188
|
-
const VERSION2 = "v6.2.
|
|
41416
|
+
const VERSION2 = "v6.2.10";
|
|
41189
41417
|
USER_AGENT = `${NAME}/${VERSION2}`;
|
|
41190
41418
|
}
|
|
41191
41419
|
var customFetch = Symbol();
|
|
@@ -41211,130 +41439,112 @@ async function fetchJwks(url2, headers, signal, fetchImpl = fetch) {
|
|
|
41211
41439
|
}
|
|
41212
41440
|
}
|
|
41213
41441
|
var jwksCache = Symbol();
|
|
41214
|
-
function
|
|
41215
|
-
|
|
41216
|
-
|
|
41217
|
-
|
|
41218
|
-
if (
|
|
41219
|
-
|
|
41220
|
-
}
|
|
41221
|
-
if (!("jwks" in input) || !isObject2(input.jwks) || !Array.isArray(input.jwks.keys) || !Array.prototype.every.call(input.jwks.keys, isObject2)) {
|
|
41222
|
-
return false;
|
|
41442
|
+
function isFreshFor(timestamp, duration3) {
|
|
41443
|
+
return Number.isFinite(timestamp) && Date.now() < timestamp + duration3;
|
|
41444
|
+
}
|
|
41445
|
+
function validateDuration(value, fallback, option) {
|
|
41446
|
+
if (Number.isNaN(value)) {
|
|
41447
|
+
throw new TypeError(`"${option}" option must not be NaN`);
|
|
41223
41448
|
}
|
|
41224
|
-
return
|
|
41449
|
+
return typeof value === "number" ? value : fallback;
|
|
41225
41450
|
}
|
|
41226
|
-
|
|
41227
|
-
|
|
41228
|
-
|
|
41229
|
-
|
|
41230
|
-
|
|
41231
|
-
|
|
41232
|
-
|
|
41233
|
-
|
|
41234
|
-
|
|
41235
|
-
|
|
41236
|
-
|
|
41237
|
-
|
|
41238
|
-
|
|
41239
|
-
|
|
41240
|
-
|
|
41241
|
-
|
|
41242
|
-
|
|
41243
|
-
|
|
41244
|
-
|
|
41245
|
-
|
|
41246
|
-
|
|
41247
|
-
|
|
41248
|
-
|
|
41249
|
-
|
|
41250
|
-
|
|
41251
|
-
|
|
41252
|
-
|
|
41253
|
-
|
|
41254
|
-
|
|
41255
|
-
|
|
41256
|
-
|
|
41257
|
-
|
|
41258
|
-
|
|
41259
|
-
|
|
41260
|
-
|
|
41261
|
-
|
|
41262
|
-
|
|
41263
|
-
|
|
41264
|
-
|
|
41265
|
-
|
|
41266
|
-
|
|
41267
|
-
|
|
41268
|
-
|
|
41269
|
-
|
|
41270
|
-
|
|
41271
|
-
|
|
41272
|
-
|
|
41273
|
-
|
|
41274
|
-
|
|
41275
|
-
|
|
41276
|
-
|
|
41277
|
-
|
|
41278
|
-
|
|
41279
|
-
|
|
41280
|
-
|
|
41281
|
-
|
|
41451
|
+
function createRemoteJWKSet(url2, options) {
|
|
41452
|
+
if (!(url2 instanceof URL)) {
|
|
41453
|
+
throw new TypeError("url must be an instance of URL");
|
|
41454
|
+
}
|
|
41455
|
+
const href = new URL(url2.href).href;
|
|
41456
|
+
const opts = options ?? {};
|
|
41457
|
+
const timeoutOption = opts.timeoutDuration;
|
|
41458
|
+
if (typeof timeoutOption === "number" && (!Number.isInteger(timeoutOption) || timeoutOption < 0)) {
|
|
41459
|
+
throw new TypeError('"timeoutDuration" option must be a non-negative integer');
|
|
41460
|
+
}
|
|
41461
|
+
const timeoutDuration = typeof timeoutOption === "number" ? timeoutOption : 5e3;
|
|
41462
|
+
const cooldownDuration = validateDuration(opts.cooldownDuration, 3e4, "cooldownDuration");
|
|
41463
|
+
const cacheMaxAge = validateDuration(opts.cacheMaxAge, 6e5, "cacheMaxAge");
|
|
41464
|
+
const headers = new Headers(opts.headers);
|
|
41465
|
+
if (USER_AGENT && !headers.has("User-Agent")) {
|
|
41466
|
+
headers.set("User-Agent", USER_AGENT);
|
|
41467
|
+
}
|
|
41468
|
+
if (!headers.has("accept")) {
|
|
41469
|
+
headers.set("accept", "application/json, application/jwk-set+json");
|
|
41470
|
+
}
|
|
41471
|
+
const fetchImpl = opts[customFetch];
|
|
41472
|
+
const cache2 = opts[jwksCache];
|
|
41473
|
+
let jwksTimestamp;
|
|
41474
|
+
let pendingFetch;
|
|
41475
|
+
let reloadSequence = 0;
|
|
41476
|
+
let appliedSequence = 0;
|
|
41477
|
+
let local;
|
|
41478
|
+
if (cache2 && typeof cache2 === "object") {
|
|
41479
|
+
const { uat, jwks } = cache2;
|
|
41480
|
+
if (isFreshFor(uat, cacheMaxAge) && isJwkSet(jwks)) {
|
|
41481
|
+
jwksTimestamp = uat;
|
|
41482
|
+
local = createLocalJWKSet(jwks);
|
|
41483
|
+
}
|
|
41484
|
+
}
|
|
41485
|
+
const reload = async () => {
|
|
41486
|
+
if (pendingFetch && isCloudflareWorkers()) {
|
|
41487
|
+
pendingFetch = void 0;
|
|
41488
|
+
}
|
|
41489
|
+
if (!pendingFetch) {
|
|
41490
|
+
const sequence = ++reloadSequence;
|
|
41491
|
+
const current = pendingFetch = fetchJwks(href, headers, AbortSignal.timeout(timeoutDuration), fetchImpl).then((json) => {
|
|
41492
|
+
const next = createLocalJWKSet(json);
|
|
41493
|
+
if (sequence <= appliedSequence) {
|
|
41494
|
+
return;
|
|
41495
|
+
}
|
|
41496
|
+
local = next;
|
|
41497
|
+
const updatedAt = Date.now();
|
|
41498
|
+
if (cache2) {
|
|
41499
|
+
cache2.uat = updatedAt;
|
|
41500
|
+
cache2.jwks = json;
|
|
41501
|
+
}
|
|
41502
|
+
jwksTimestamp = updatedAt;
|
|
41503
|
+
appliedSequence = sequence;
|
|
41504
|
+
}).finally(() => {
|
|
41505
|
+
if (pendingFetch === current) {
|
|
41506
|
+
pendingFetch = void 0;
|
|
41507
|
+
}
|
|
41508
|
+
});
|
|
41509
|
+
}
|
|
41510
|
+
await pendingFetch;
|
|
41511
|
+
};
|
|
41512
|
+
const remoteJWKSet = async (protectedHeader, token) => {
|
|
41513
|
+
if (!local || !isFreshFor(jwksTimestamp, cacheMaxAge)) {
|
|
41514
|
+
await reload();
|
|
41282
41515
|
}
|
|
41283
41516
|
try {
|
|
41284
|
-
return await
|
|
41517
|
+
return await local(protectedHeader, token);
|
|
41285
41518
|
} catch (err) {
|
|
41286
|
-
if (err instanceof JWKSNoMatchingKey) {
|
|
41287
|
-
|
|
41288
|
-
|
|
41289
|
-
return this.#local(protectedHeader, token);
|
|
41290
|
-
}
|
|
41519
|
+
if (err instanceof JWKSNoMatchingKey && !isFreshFor(jwksTimestamp, cooldownDuration)) {
|
|
41520
|
+
await reload();
|
|
41521
|
+
return local(protectedHeader, token);
|
|
41291
41522
|
}
|
|
41292
41523
|
throw err;
|
|
41293
41524
|
}
|
|
41294
|
-
}
|
|
41295
|
-
|
|
41296
|
-
if (this.#pendingFetch && isCloudflareWorkers()) {
|
|
41297
|
-
this.#pendingFetch = void 0;
|
|
41298
|
-
}
|
|
41299
|
-
this.#pendingFetch ||= fetchJwks(this.#url.href, this.#headers, AbortSignal.timeout(this.#timeoutDuration), this.#customFetch).then((json) => {
|
|
41300
|
-
this.#local = createLocalJWKSet(json);
|
|
41301
|
-
if (this.#cache) {
|
|
41302
|
-
this.#cache.uat = Date.now();
|
|
41303
|
-
this.#cache.jwks = json;
|
|
41304
|
-
}
|
|
41305
|
-
this.#jwksTimestamp = Date.now();
|
|
41306
|
-
}).finally(() => {
|
|
41307
|
-
this.#pendingFetch = void 0;
|
|
41308
|
-
});
|
|
41309
|
-
await this.#pendingFetch;
|
|
41310
|
-
}
|
|
41311
|
-
};
|
|
41312
|
-
function createRemoteJWKSet(url2, options) {
|
|
41313
|
-
const set = new RemoteJWKSetImpl(url2, options);
|
|
41314
|
-
const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
|
|
41315
|
-
Object.defineProperties(remoteJWKSet, {
|
|
41525
|
+
};
|
|
41526
|
+
return Object.defineProperties(remoteJWKSet, {
|
|
41316
41527
|
coolingDown: {
|
|
41317
|
-
get: () =>
|
|
41528
|
+
get: () => isFreshFor(jwksTimestamp, cooldownDuration),
|
|
41318
41529
|
enumerable: true
|
|
41319
41530
|
},
|
|
41320
41531
|
fresh: {
|
|
41321
|
-
get: () =>
|
|
41532
|
+
get: () => isFreshFor(jwksTimestamp, cacheMaxAge),
|
|
41322
41533
|
enumerable: true
|
|
41323
41534
|
},
|
|
41324
41535
|
reload: {
|
|
41325
|
-
value:
|
|
41536
|
+
value: reload,
|
|
41326
41537
|
enumerable: true
|
|
41327
41538
|
},
|
|
41328
41539
|
reloading: {
|
|
41329
|
-
get: () =>
|
|
41540
|
+
get: () => !!pendingFetch,
|
|
41330
41541
|
enumerable: true
|
|
41331
41542
|
},
|
|
41332
41543
|
jwks: {
|
|
41333
|
-
value: () =>
|
|
41544
|
+
value: () => local?.jwks(),
|
|
41334
41545
|
enumerable: true
|
|
41335
41546
|
}
|
|
41336
41547
|
});
|
|
41337
|
-
return remoteJWKSet;
|
|
41338
41548
|
}
|
|
41339
41549
|
|
|
41340
41550
|
// node_modules/@casys/mcp-server/src/auth/provider.ts
|
|
@@ -46975,6 +47185,7 @@ var viewer = (name) => ({
|
|
|
46975
47185
|
ui: { resourceUri: `ui://mcp-erpnext/${name}` }
|
|
46976
47186
|
});
|
|
46977
47187
|
var DOCLIST_META = viewer("doclist-viewer");
|
|
47188
|
+
var DOC_META = viewer("doc-viewer");
|
|
46978
47189
|
var INVOICE_META = viewer("invoice-viewer");
|
|
46979
47190
|
var STOCK_META = viewer("stock-viewer");
|
|
46980
47191
|
var CHART_META = viewer("chart-viewer");
|
|
@@ -47074,6 +47285,115 @@ function stableStringify(value) {
|
|
|
47074
47285
|
var DEFAULT_RETRY_STATUSES = [408, 429, 502, 503, 504];
|
|
47075
47286
|
var DEFAULT_RETRY_METHODS = ["GET"];
|
|
47076
47287
|
var DEFAULT_MAX_UPLOAD_BYTES = 10 * 1024 * 1024;
|
|
47288
|
+
var DEFAULT_MAX_DOWNLOAD_BYTES = 10 * 1024 * 1024;
|
|
47289
|
+
var MAX_ERROR_BODY_BYTES = 64 * 1024;
|
|
47290
|
+
var ResponseBodyLimitError = class extends Error {
|
|
47291
|
+
constructor(label, size, limit) {
|
|
47292
|
+
super(
|
|
47293
|
+
`[FrappeClient] ${label} size ${size} bytes exceeds the ${limit}-byte limit`
|
|
47294
|
+
);
|
|
47295
|
+
this.name = "ResponseBodyLimitError";
|
|
47296
|
+
}
|
|
47297
|
+
};
|
|
47298
|
+
async function readResponseBytes(response, maxBytes, label) {
|
|
47299
|
+
const declaredLength = response.headers.get("content-length")?.trim();
|
|
47300
|
+
if (declaredLength && /^\d+$/.test(declaredLength)) {
|
|
47301
|
+
const size = Number(declaredLength);
|
|
47302
|
+
if (size > maxBytes) {
|
|
47303
|
+
try {
|
|
47304
|
+
await response.body?.cancel();
|
|
47305
|
+
} catch {
|
|
47306
|
+
}
|
|
47307
|
+
throw new ResponseBodyLimitError(label, size, maxBytes);
|
|
47308
|
+
}
|
|
47309
|
+
}
|
|
47310
|
+
if (!response.body) return new Uint8Array();
|
|
47311
|
+
const reader = response.body.getReader();
|
|
47312
|
+
const chunks = [];
|
|
47313
|
+
let total = 0;
|
|
47314
|
+
try {
|
|
47315
|
+
while (true) {
|
|
47316
|
+
const { done, value } = await reader.read();
|
|
47317
|
+
if (done) break;
|
|
47318
|
+
total += value.byteLength;
|
|
47319
|
+
if (total > maxBytes) {
|
|
47320
|
+
try {
|
|
47321
|
+
await reader.cancel();
|
|
47322
|
+
} catch {
|
|
47323
|
+
}
|
|
47324
|
+
throw new ResponseBodyLimitError(label, total, maxBytes);
|
|
47325
|
+
}
|
|
47326
|
+
chunks.push(value);
|
|
47327
|
+
}
|
|
47328
|
+
} finally {
|
|
47329
|
+
reader.releaseLock();
|
|
47330
|
+
}
|
|
47331
|
+
const bytes = new Uint8Array(total);
|
|
47332
|
+
let offset = 0;
|
|
47333
|
+
for (const chunk of chunks) {
|
|
47334
|
+
bytes.set(chunk, offset);
|
|
47335
|
+
offset += chunk.byteLength;
|
|
47336
|
+
}
|
|
47337
|
+
return bytes;
|
|
47338
|
+
}
|
|
47339
|
+
function isUnsafeControl(character) {
|
|
47340
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
47341
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8294 && codePoint <= 8297;
|
|
47342
|
+
}
|
|
47343
|
+
function validateLocalFileUrl(fileUrl, isPrivate) {
|
|
47344
|
+
if (typeof fileUrl !== "string" || fileUrl.length === 0) {
|
|
47345
|
+
throw new Error("[FrappeClient] File.file_url must be a non-empty string");
|
|
47346
|
+
}
|
|
47347
|
+
if (fileUrl !== fileUrl.trim()) {
|
|
47348
|
+
throw new Error(
|
|
47349
|
+
"[FrappeClient] File.file_url must not contain whitespace padding"
|
|
47350
|
+
);
|
|
47351
|
+
}
|
|
47352
|
+
const expectedPrefix = isPrivate ? "/private/files/" : "/files/";
|
|
47353
|
+
let decoded = fileUrl;
|
|
47354
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
47355
|
+
if (Array.from(decoded).some(isUnsafeControl) || decoded.includes("\\") || decoded.includes("?") || decoded.includes("#")) {
|
|
47356
|
+
throw new Error(
|
|
47357
|
+
"[FrappeClient] File.file_url must be a local Frappe file path without query, fragment, NUL, or backslash"
|
|
47358
|
+
);
|
|
47359
|
+
}
|
|
47360
|
+
if (!decoded.startsWith(expectedPrefix)) {
|
|
47361
|
+
throw new Error(
|
|
47362
|
+
`[FrappeClient] File.file_url does not match is_private=${isPrivate ? 1 : 0}`
|
|
47363
|
+
);
|
|
47364
|
+
}
|
|
47365
|
+
const fileSegment = decoded.slice(expectedPrefix.length);
|
|
47366
|
+
if (!fileSegment || fileSegment === "." || fileSegment === ".." || fileSegment.includes("/")) {
|
|
47367
|
+
throw new Error(
|
|
47368
|
+
"[FrappeClient] File.file_url must contain exactly one safe file segment"
|
|
47369
|
+
);
|
|
47370
|
+
}
|
|
47371
|
+
let next;
|
|
47372
|
+
try {
|
|
47373
|
+
next = decodeURIComponent(decoded);
|
|
47374
|
+
} catch {
|
|
47375
|
+
throw new Error(
|
|
47376
|
+
"[FrappeClient] File.file_url contains malformed percent encoding"
|
|
47377
|
+
);
|
|
47378
|
+
}
|
|
47379
|
+
if (next === decoded) return fileUrl;
|
|
47380
|
+
decoded = next;
|
|
47381
|
+
}
|
|
47382
|
+
throw new Error(
|
|
47383
|
+
"[FrappeClient] File.file_url contains excessive nested percent encoding"
|
|
47384
|
+
);
|
|
47385
|
+
}
|
|
47386
|
+
function sanitizeDownloadFileName(raw2, fileId) {
|
|
47387
|
+
const fallbackId = fileId.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80) || "file";
|
|
47388
|
+
if (typeof raw2 !== "string") return `erpnext-${fallbackId}`;
|
|
47389
|
+
const normalized = Array.from(raw2.normalize("NFC")).filter((character) => !isUnsafeControl(character)).join("").replace(/[\\/:*?"<>|]/g, "_").trim().replace(/[. ]+$/g, "");
|
|
47390
|
+
const bounded = Array.from(normalized).slice(0, 255).join("");
|
|
47391
|
+
return !bounded || bounded === "." || bounded === ".." ? `erpnext-${fallbackId}` : bounded;
|
|
47392
|
+
}
|
|
47393
|
+
function normalizeMimeType(raw2) {
|
|
47394
|
+
const candidate = (raw2 ?? "").split(";", 1)[0].trim().toLowerCase();
|
|
47395
|
+
return /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/i.test(candidate) && candidate !== "application/unknown" ? candidate : "application/octet-stream";
|
|
47396
|
+
}
|
|
47077
47397
|
function decodeBase64File(contentBase64, maxBytes) {
|
|
47078
47398
|
if (contentBase64.length === 0) {
|
|
47079
47399
|
throw new Error("[FrappeClient] File content must not be empty");
|
|
@@ -47159,6 +47479,7 @@ var FrappeClient = class {
|
|
|
47159
47479
|
authHeader;
|
|
47160
47480
|
timeoutMs;
|
|
47161
47481
|
maxUploadBytes;
|
|
47482
|
+
maxDownloadBytes;
|
|
47162
47483
|
retries;
|
|
47163
47484
|
retryStatuses;
|
|
47164
47485
|
retryBackoffMs;
|
|
@@ -47174,6 +47495,12 @@ var FrappeClient = class {
|
|
|
47174
47495
|
"[FrappeClient] maxUploadBytes must be a positive integer"
|
|
47175
47496
|
);
|
|
47176
47497
|
}
|
|
47498
|
+
this.maxDownloadBytes = config2.maxDownloadBytes ?? DEFAULT_MAX_DOWNLOAD_BYTES;
|
|
47499
|
+
if (!Number.isInteger(this.maxDownloadBytes) || this.maxDownloadBytes <= 0) {
|
|
47500
|
+
throw new Error(
|
|
47501
|
+
"[FrappeClient] maxDownloadBytes must be a positive integer"
|
|
47502
|
+
);
|
|
47503
|
+
}
|
|
47177
47504
|
this.retries = config2.retries ?? 3;
|
|
47178
47505
|
this.retryStatuses = config2.retryStatuses ?? DEFAULT_RETRY_STATUSES;
|
|
47179
47506
|
this.retryBackoffMs = config2.retryBackoffMs ?? 200;
|
|
@@ -47288,6 +47615,152 @@ var FrappeClient = class {
|
|
|
47288
47615
|
}
|
|
47289
47616
|
return responseBody;
|
|
47290
47617
|
}
|
|
47618
|
+
buildSameOriginUrl(path) {
|
|
47619
|
+
if (!path.startsWith("/")) {
|
|
47620
|
+
throw new Error("[FrappeClient] Download path must be root-relative");
|
|
47621
|
+
}
|
|
47622
|
+
let base;
|
|
47623
|
+
let target;
|
|
47624
|
+
try {
|
|
47625
|
+
base = new URL(this.baseUrl);
|
|
47626
|
+
target = new URL(`${this.baseUrl}${path}`);
|
|
47627
|
+
} catch {
|
|
47628
|
+
throw new Error("[FrappeClient] baseUrl must be a valid absolute URL");
|
|
47629
|
+
}
|
|
47630
|
+
if (target.origin !== base.origin) {
|
|
47631
|
+
throw new Error("[FrappeClient] Refusing a cross-origin download");
|
|
47632
|
+
}
|
|
47633
|
+
return target.href;
|
|
47634
|
+
}
|
|
47635
|
+
async requestBinary(path) {
|
|
47636
|
+
let lastError;
|
|
47637
|
+
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
47638
|
+
try {
|
|
47639
|
+
return await this.requestBinaryOnce(path);
|
|
47640
|
+
} catch (err) {
|
|
47641
|
+
lastError = err;
|
|
47642
|
+
if (attempt === this.retries || !this.isRetryable(err, "GET")) {
|
|
47643
|
+
throw err;
|
|
47644
|
+
}
|
|
47645
|
+
const delay = this.computeBackoff(attempt, err);
|
|
47646
|
+
if (delay > 0) {
|
|
47647
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
47648
|
+
}
|
|
47649
|
+
}
|
|
47650
|
+
}
|
|
47651
|
+
throw lastError;
|
|
47652
|
+
}
|
|
47653
|
+
async requestBinaryOnce(path) {
|
|
47654
|
+
const url2 = this.buildSameOriginUrl(path);
|
|
47655
|
+
const controller = new AbortController();
|
|
47656
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
47657
|
+
let response;
|
|
47658
|
+
try {
|
|
47659
|
+
response = await fetch(url2, {
|
|
47660
|
+
method: "GET",
|
|
47661
|
+
headers: {
|
|
47662
|
+
"Authorization": this.authHeader,
|
|
47663
|
+
"Accept": "*/*",
|
|
47664
|
+
"Cache-Control": "no-store"
|
|
47665
|
+
},
|
|
47666
|
+
cache: "no-store",
|
|
47667
|
+
redirect: "manual",
|
|
47668
|
+
signal: controller.signal
|
|
47669
|
+
});
|
|
47670
|
+
} catch (err) {
|
|
47671
|
+
clearTimeout(timer);
|
|
47672
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
47673
|
+
throw new FrappeAPIError(
|
|
47674
|
+
`Request timed out after ${this.timeoutMs}ms: GET ${path}`,
|
|
47675
|
+
408,
|
|
47676
|
+
null
|
|
47677
|
+
);
|
|
47678
|
+
}
|
|
47679
|
+
throw new FrappeAPIError(
|
|
47680
|
+
`Network error on GET ${path}: ${err.message}`,
|
|
47681
|
+
0,
|
|
47682
|
+
null
|
|
47683
|
+
);
|
|
47684
|
+
}
|
|
47685
|
+
try {
|
|
47686
|
+
if (response.redirected || response.type === "opaqueredirect" || response.status >= 300 && response.status < 400) {
|
|
47687
|
+
try {
|
|
47688
|
+
await response.body?.cancel();
|
|
47689
|
+
} catch {
|
|
47690
|
+
}
|
|
47691
|
+
throw new FrappeAPIError(
|
|
47692
|
+
`GET ${path} refused an HTTP redirect`,
|
|
47693
|
+
response.status,
|
|
47694
|
+
null
|
|
47695
|
+
);
|
|
47696
|
+
}
|
|
47697
|
+
if (!response.ok) {
|
|
47698
|
+
let rawText;
|
|
47699
|
+
try {
|
|
47700
|
+
const errorBytes = await readResponseBytes(
|
|
47701
|
+
response,
|
|
47702
|
+
MAX_ERROR_BODY_BYTES,
|
|
47703
|
+
"Error response body"
|
|
47704
|
+
);
|
|
47705
|
+
rawText = new TextDecoder().decode(errorBytes);
|
|
47706
|
+
} catch (err) {
|
|
47707
|
+
if (!(err instanceof ResponseBodyLimitError)) throw err;
|
|
47708
|
+
rawText = err.message;
|
|
47709
|
+
}
|
|
47710
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
47711
|
+
let responseBody = rawText;
|
|
47712
|
+
if (contentType.includes("application/json") && rawText.length > 0) {
|
|
47713
|
+
try {
|
|
47714
|
+
responseBody = JSON.parse(rawText);
|
|
47715
|
+
} catch {
|
|
47716
|
+
responseBody = rawText;
|
|
47717
|
+
}
|
|
47718
|
+
}
|
|
47719
|
+
let message2 = response.statusText;
|
|
47720
|
+
if (typeof responseBody === "object" && responseBody !== null) {
|
|
47721
|
+
const body = responseBody;
|
|
47722
|
+
const baseMessage = body.message ?? body.exc_type ?? response.statusText;
|
|
47723
|
+
const serverDetails = extractServerMessages(body._server_messages);
|
|
47724
|
+
message2 = serverDetails ? `${baseMessage}: ${serverDetails}` : baseMessage;
|
|
47725
|
+
} else if (typeof responseBody === "string" && responseBody.length > 0) {
|
|
47726
|
+
message2 = responseBody.slice(0, 200);
|
|
47727
|
+
}
|
|
47728
|
+
throw new FrappeAPIError(
|
|
47729
|
+
`GET ${path} failed: ${message2}`,
|
|
47730
|
+
response.status,
|
|
47731
|
+
responseBody,
|
|
47732
|
+
parseRetryAfter(response.headers.get("retry-after"))
|
|
47733
|
+
);
|
|
47734
|
+
}
|
|
47735
|
+
const bytes = await readResponseBytes(
|
|
47736
|
+
response,
|
|
47737
|
+
this.maxDownloadBytes,
|
|
47738
|
+
"Downloaded file"
|
|
47739
|
+
);
|
|
47740
|
+
return {
|
|
47741
|
+
bytes,
|
|
47742
|
+
mimeType: normalizeMimeType(response.headers.get("content-type"))
|
|
47743
|
+
};
|
|
47744
|
+
} catch (err) {
|
|
47745
|
+
if (err instanceof FrappeAPIError || err instanceof ResponseBodyLimitError) {
|
|
47746
|
+
throw err;
|
|
47747
|
+
}
|
|
47748
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
47749
|
+
throw new FrappeAPIError(
|
|
47750
|
+
`Request timed out after ${this.timeoutMs}ms: GET ${path}`,
|
|
47751
|
+
408,
|
|
47752
|
+
null
|
|
47753
|
+
);
|
|
47754
|
+
}
|
|
47755
|
+
throw new FrappeAPIError(
|
|
47756
|
+
`Network error while reading GET ${path}: ${err.message}`,
|
|
47757
|
+
0,
|
|
47758
|
+
null
|
|
47759
|
+
);
|
|
47760
|
+
} finally {
|
|
47761
|
+
clearTimeout(timer);
|
|
47762
|
+
}
|
|
47763
|
+
}
|
|
47291
47764
|
// ── Resource CRUD ───────────────────────────────────────────────────────────
|
|
47292
47765
|
/**
|
|
47293
47766
|
* List documents of a DocType.
|
|
@@ -47409,6 +47882,73 @@ var FrappeClient = class {
|
|
|
47409
47882
|
);
|
|
47410
47883
|
this.invalidate(doctype, name);
|
|
47411
47884
|
}
|
|
47885
|
+
/**
|
|
47886
|
+
* Download one File row through Frappe's authenticated download handler.
|
|
47887
|
+
*
|
|
47888
|
+
* The caller supplies the expected attachment owner as a confused-deputy
|
|
47889
|
+
* guard. The File row is fetched fresh, its local path is validated, and the
|
|
47890
|
+
* response stream is stopped as soon as it exceeds `maxDownloadBytes`.
|
|
47891
|
+
*/
|
|
47892
|
+
async downloadFile(input) {
|
|
47893
|
+
const fileId = input.fileId.trim();
|
|
47894
|
+
const attachedToDoctype = input.attachedToDoctype.trim();
|
|
47895
|
+
const attachedToName = input.attachedToName.trim();
|
|
47896
|
+
if (!fileId) {
|
|
47897
|
+
throw new Error("[FrappeClient] fileId must not be empty");
|
|
47898
|
+
}
|
|
47899
|
+
if (!attachedToDoctype) {
|
|
47900
|
+
throw new Error("[FrappeClient] attachedToDoctype must not be empty");
|
|
47901
|
+
}
|
|
47902
|
+
if (!attachedToName) {
|
|
47903
|
+
throw new Error("[FrappeClient] attachedToName must not be empty");
|
|
47904
|
+
}
|
|
47905
|
+
const metadata = await this.request(
|
|
47906
|
+
"GET",
|
|
47907
|
+
`/api/resource/File/${encodeURIComponent(fileId)}`
|
|
47908
|
+
);
|
|
47909
|
+
const file = metadata.data;
|
|
47910
|
+
if (file.name !== fileId) {
|
|
47911
|
+
throw new Error("[FrappeClient] File response identity mismatch");
|
|
47912
|
+
}
|
|
47913
|
+
if (file.attached_to_doctype !== attachedToDoctype || file.attached_to_name !== attachedToName) {
|
|
47914
|
+
throw new Error(
|
|
47915
|
+
"[FrappeClient] File is not attached to the requested document"
|
|
47916
|
+
);
|
|
47917
|
+
}
|
|
47918
|
+
if (file.is_private !== 0 && file.is_private !== 1) {
|
|
47919
|
+
throw new Error("[FrappeClient] File.is_private must be 0 or 1");
|
|
47920
|
+
}
|
|
47921
|
+
const fileUrl = validateLocalFileUrl(
|
|
47922
|
+
file.file_url,
|
|
47923
|
+
file.is_private === 1
|
|
47924
|
+
);
|
|
47925
|
+
const fileSize = file.file_size;
|
|
47926
|
+
if (fileSize !== void 0 && fileSize !== null) {
|
|
47927
|
+
if (typeof fileSize !== "number" || !Number.isInteger(fileSize) || fileSize < 0) {
|
|
47928
|
+
throw new Error(
|
|
47929
|
+
"[FrappeClient] File.file_size must be a non-negative integer"
|
|
47930
|
+
);
|
|
47931
|
+
}
|
|
47932
|
+
if (fileSize > this.maxDownloadBytes) {
|
|
47933
|
+
throw new ResponseBodyLimitError(
|
|
47934
|
+
"File metadata",
|
|
47935
|
+
fileSize,
|
|
47936
|
+
this.maxDownloadBytes
|
|
47937
|
+
);
|
|
47938
|
+
}
|
|
47939
|
+
}
|
|
47940
|
+
const params = new URLSearchParams({ file_url: fileUrl });
|
|
47941
|
+
const download = await this.requestBinary(
|
|
47942
|
+
`/api/method/frappe.handler.download_file?${params.toString()}`
|
|
47943
|
+
);
|
|
47944
|
+
return {
|
|
47945
|
+
fileId,
|
|
47946
|
+
fileName: sanitizeDownloadFileName(file.file_name, fileId),
|
|
47947
|
+
mimeType: download.mimeType,
|
|
47948
|
+
bytes: download.bytes,
|
|
47949
|
+
isPrivate: file.is_private === 1
|
|
47950
|
+
};
|
|
47951
|
+
}
|
|
47412
47952
|
/**
|
|
47413
47953
|
* Upload file bytes and attach the native File document to another document.
|
|
47414
47954
|
* POST /api/method/upload_file
|
|
@@ -47479,6 +48019,7 @@ function getFrappeClient() {
|
|
|
47479
48019
|
const apiKey = env6("ERPNEXT_API_KEY");
|
|
47480
48020
|
const apiSecret = env6("ERPNEXT_API_SECRET");
|
|
47481
48021
|
const maxUploadBytesRaw = env6("ERPNEXT_MAX_UPLOAD_BYTES");
|
|
48022
|
+
const maxDownloadBytesRaw = env6("ERPNEXT_MAX_DOWNLOAD_BYTES");
|
|
47482
48023
|
if (!url2) {
|
|
47483
48024
|
throw new Error(
|
|
47484
48025
|
"[lib/erpnext] ERPNEXT_URL is required. Set it to your ERPNext instance URL, e.g. http://localhost:8000"
|
|
@@ -47494,7 +48035,8 @@ function getFrappeClient() {
|
|
|
47494
48035
|
apiKey,
|
|
47495
48036
|
apiSecret,
|
|
47496
48037
|
cache: getCache(),
|
|
47497
|
-
maxUploadBytes: maxUploadBytesRaw?.trim() ? Number(maxUploadBytesRaw) : void 0
|
|
48038
|
+
maxUploadBytes: maxUploadBytesRaw?.trim() ? Number(maxUploadBytesRaw) : void 0,
|
|
48039
|
+
maxDownloadBytes: maxDownloadBytesRaw?.trim() ? Number(maxDownloadBytesRaw) : void 0
|
|
47498
48040
|
});
|
|
47499
48041
|
return _client;
|
|
47500
48042
|
}
|
|
@@ -47626,6 +48168,14 @@ function roundedTotalFallbackWarning(original, patched) {
|
|
|
47626
48168
|
}
|
|
47627
48169
|
|
|
47628
48170
|
// src/tools/sales.ts
|
|
48171
|
+
function viewerDocument(value, doctype, name) {
|
|
48172
|
+
const record2 = typeof value === "object" && value !== null ? value : {};
|
|
48173
|
+
return {
|
|
48174
|
+
...record2,
|
|
48175
|
+
...typeof record2.name === "string" || !name ? {} : { name },
|
|
48176
|
+
doctype
|
|
48177
|
+
};
|
|
48178
|
+
}
|
|
47629
48179
|
function mapLineItems(items, options) {
|
|
47630
48180
|
if (!Array.isArray(items) || items.length === 0) {
|
|
47631
48181
|
throw new Error(
|
|
@@ -47711,6 +48261,7 @@ var salesTools = [
|
|
|
47711
48261
|
{
|
|
47712
48262
|
name: "erpnext_customer_get",
|
|
47713
48263
|
annotations: { readOnlyHint: true },
|
|
48264
|
+
_meta: DOC_META,
|
|
47714
48265
|
description: "Get a single ERPNext customer by name (ID). Returns all fields including contact details.",
|
|
47715
48266
|
category: "sales",
|
|
47716
48267
|
inputSchema: {
|
|
@@ -47725,7 +48276,7 @@ var salesTools = [
|
|
|
47725
48276
|
throw new Error("[erpnext_customer_get] 'name' is required");
|
|
47726
48277
|
}
|
|
47727
48278
|
const doc = await ctx.client.get("Customer", input.name);
|
|
47728
|
-
return { data: doc };
|
|
48279
|
+
return { data: { ...doc, doctype: "Customer" } };
|
|
47729
48280
|
}
|
|
47730
48281
|
},
|
|
47731
48282
|
{
|
|
@@ -47906,7 +48457,7 @@ var salesTools = [
|
|
|
47906
48457
|
throw new Error("[erpnext_sales_order_get] 'name' is required");
|
|
47907
48458
|
}
|
|
47908
48459
|
const doc = await ctx.client.get("Sales Order", input.name);
|
|
47909
|
-
return { data: doc };
|
|
48460
|
+
return { data: viewerDocument(doc, "Sales Order", input.name) };
|
|
47910
48461
|
}
|
|
47911
48462
|
},
|
|
47912
48463
|
{
|
|
@@ -47987,8 +48538,12 @@ var salesTools = [
|
|
|
47987
48538
|
}
|
|
47988
48539
|
const doc = await ctx.client.create("Sales Order", data);
|
|
47989
48540
|
return {
|
|
47990
|
-
data: doc,
|
|
47991
|
-
message: `Sales Order ${doc.name} created successfully
|
|
48541
|
+
data: viewerDocument(doc, "Sales Order"),
|
|
48542
|
+
message: `Sales Order ${doc.name} created successfully`,
|
|
48543
|
+
refreshRequest: {
|
|
48544
|
+
toolName: "erpnext_sales_order_get",
|
|
48545
|
+
arguments: { name: doc.name }
|
|
48546
|
+
}
|
|
47992
48547
|
};
|
|
47993
48548
|
}
|
|
47994
48549
|
},
|
|
@@ -48078,8 +48633,16 @@ var salesTools = [
|
|
|
48078
48633
|
ctx.client.invalidate("Sales Order", input.name);
|
|
48079
48634
|
const warnings = roundedTotalFallbackWarning(docWithDoctype, patchedDoc);
|
|
48080
48635
|
return {
|
|
48081
|
-
data:
|
|
48636
|
+
data: viewerDocument(
|
|
48637
|
+
result,
|
|
48638
|
+
"Sales Order",
|
|
48639
|
+
input.name
|
|
48640
|
+
),
|
|
48082
48641
|
message: `Sales Order ${input.name} submitted successfully`,
|
|
48642
|
+
refreshRequest: {
|
|
48643
|
+
toolName: "erpnext_sales_order_get",
|
|
48644
|
+
arguments: { name: input.name }
|
|
48645
|
+
},
|
|
48083
48646
|
...warnings.length > 0 ? { warnings } : {}
|
|
48084
48647
|
};
|
|
48085
48648
|
}
|
|
@@ -48205,7 +48768,11 @@ var salesTools = [
|
|
|
48205
48768
|
}
|
|
48206
48769
|
const doc = await ctx.client.get("Sales Invoice", input.name);
|
|
48207
48770
|
return {
|
|
48208
|
-
data:
|
|
48771
|
+
data: viewerDocument(
|
|
48772
|
+
doc,
|
|
48773
|
+
"Sales Invoice",
|
|
48774
|
+
input.name
|
|
48775
|
+
),
|
|
48209
48776
|
_meta: INVOICE_META
|
|
48210
48777
|
};
|
|
48211
48778
|
}
|
|
@@ -48291,9 +48858,13 @@ var salesTools = [
|
|
|
48291
48858
|
}
|
|
48292
48859
|
const doc = await ctx.client.create("Sales Invoice", data);
|
|
48293
48860
|
return {
|
|
48294
|
-
data: doc,
|
|
48861
|
+
data: viewerDocument(doc, "Sales Invoice"),
|
|
48295
48862
|
message: `Sales Invoice ${doc.name} created successfully`,
|
|
48296
|
-
_meta: INVOICE_META
|
|
48863
|
+
_meta: INVOICE_META,
|
|
48864
|
+
refreshRequest: {
|
|
48865
|
+
toolName: "erpnext_sales_invoice_get",
|
|
48866
|
+
arguments: { name: doc.name }
|
|
48867
|
+
}
|
|
48297
48868
|
};
|
|
48298
48869
|
}
|
|
48299
48870
|
},
|
|
@@ -48327,9 +48898,17 @@ var salesTools = [
|
|
|
48327
48898
|
ctx.client.invalidate("Sales Invoice", input.name);
|
|
48328
48899
|
const warnings = roundedTotalFallbackWarning(docWithDoctype, patchedDoc);
|
|
48329
48900
|
return {
|
|
48330
|
-
data:
|
|
48901
|
+
data: viewerDocument(
|
|
48902
|
+
result,
|
|
48903
|
+
"Sales Invoice",
|
|
48904
|
+
input.name
|
|
48905
|
+
),
|
|
48331
48906
|
message: `Sales Invoice ${input.name} submitted successfully`,
|
|
48332
48907
|
_meta: INVOICE_META,
|
|
48908
|
+
refreshRequest: {
|
|
48909
|
+
toolName: "erpnext_sales_invoice_get",
|
|
48910
|
+
arguments: { name: input.name }
|
|
48911
|
+
},
|
|
48333
48912
|
...warnings.length > 0 ? { warnings } : {}
|
|
48334
48913
|
};
|
|
48335
48914
|
}
|
|
@@ -48435,7 +49014,7 @@ var salesTools = [
|
|
|
48435
49014
|
throw new Error("[erpnext_quotation_get] 'name' is required");
|
|
48436
49015
|
}
|
|
48437
49016
|
const doc = await ctx.client.get("Quotation", input.name);
|
|
48438
|
-
return { data: doc };
|
|
49017
|
+
return { data: viewerDocument(doc, "Quotation", input.name) };
|
|
48439
49018
|
}
|
|
48440
49019
|
},
|
|
48441
49020
|
{
|
|
@@ -48529,8 +49108,12 @@ var salesTools = [
|
|
|
48529
49108
|
}
|
|
48530
49109
|
const doc = await ctx.client.create("Quotation", data);
|
|
48531
49110
|
return {
|
|
48532
|
-
data: doc,
|
|
48533
|
-
message: `Quotation ${doc.name} created successfully
|
|
49111
|
+
data: viewerDocument(doc, "Quotation"),
|
|
49112
|
+
message: `Quotation ${doc.name} created successfully`,
|
|
49113
|
+
refreshRequest: {
|
|
49114
|
+
toolName: "erpnext_quotation_get",
|
|
49115
|
+
arguments: { name: doc.name }
|
|
49116
|
+
}
|
|
48534
49117
|
};
|
|
48535
49118
|
}
|
|
48536
49119
|
}
|
|
@@ -48601,6 +49184,7 @@ var inventoryTools = [
|
|
|
48601
49184
|
{
|
|
48602
49185
|
name: "erpnext_item_get",
|
|
48603
49186
|
annotations: { readOnlyHint: true },
|
|
49187
|
+
_meta: DOC_META,
|
|
48604
49188
|
description: "Get a single ERPNext Item by name/item_code. Returns all fields including pricing and stock details.",
|
|
48605
49189
|
category: "inventory",
|
|
48606
49190
|
inputSchema: {
|
|
@@ -48615,7 +49199,7 @@ var inventoryTools = [
|
|
|
48615
49199
|
throw new Error("[erpnext_item_get] 'name' is required");
|
|
48616
49200
|
}
|
|
48617
49201
|
const doc = await ctx.client.get("Item", input.name);
|
|
48618
|
-
return { data: doc };
|
|
49202
|
+
return { data: { ...doc, doctype: "Item" } };
|
|
48619
49203
|
}
|
|
48620
49204
|
},
|
|
48621
49205
|
{
|
|
@@ -48816,12 +49400,16 @@ var inventoryTools = [
|
|
|
48816
49400
|
name: "erpnext_stock_entry_list",
|
|
48817
49401
|
annotations: { readOnlyHint: true },
|
|
48818
49402
|
_meta: DOCLIST_META,
|
|
48819
|
-
description: "List Stock Entries (material transfers, receipts, issues). Fields: name, stock_entry_type, posting_date, from_warehouse, to_warehouse, total_amount. Filterable by stock_entry_type, date range.",
|
|
49403
|
+
description: "List Stock Entries (material transfers, receipts, issues). Fields: name, stock_entry_type, posting_date, from_warehouse, to_warehouse, total_amount. Filterable by item_code, stock_entry_type, date range.",
|
|
48820
49404
|
category: "inventory",
|
|
48821
49405
|
inputSchema: {
|
|
48822
49406
|
type: "object",
|
|
48823
49407
|
properties: {
|
|
48824
49408
|
limit: { type: "number", description: "Max results (default 20)" },
|
|
49409
|
+
item_code: {
|
|
49410
|
+
type: "string",
|
|
49411
|
+
description: "Exact item code present in a Stock Entry line"
|
|
49412
|
+
},
|
|
48825
49413
|
stock_entry_type: {
|
|
48826
49414
|
type: "string",
|
|
48827
49415
|
description: "Filter by type (Material Issue, Material Receipt, Material Transfer, etc.)"
|
|
@@ -48836,6 +49424,14 @@ var inventoryTools = [
|
|
|
48836
49424
|
handler: async (input, ctx) => {
|
|
48837
49425
|
const limit = input.limit ?? 20;
|
|
48838
49426
|
const filters = [];
|
|
49427
|
+
if (input.item_code) {
|
|
49428
|
+
filters.push([
|
|
49429
|
+
"Stock Entry Detail",
|
|
49430
|
+
"item_code",
|
|
49431
|
+
"=",
|
|
49432
|
+
input.item_code
|
|
49433
|
+
]);
|
|
49434
|
+
}
|
|
48839
49435
|
if (input.stock_entry_type) {
|
|
48840
49436
|
filters.push([
|
|
48841
49437
|
"stock_entry_type",
|
|
@@ -48873,6 +49469,7 @@ var inventoryTools = [
|
|
|
48873
49469
|
{
|
|
48874
49470
|
name: "erpnext_stock_entry_get",
|
|
48875
49471
|
annotations: { readOnlyHint: true },
|
|
49472
|
+
_meta: DOC_META,
|
|
48876
49473
|
description: "Get a single Stock Entry by name. Returns full document with item details.",
|
|
48877
49474
|
category: "inventory",
|
|
48878
49475
|
inputSchema: {
|
|
@@ -48890,7 +49487,7 @@ var inventoryTools = [
|
|
|
48890
49487
|
throw new Error("[erpnext_stock_entry_get] 'name' is required");
|
|
48891
49488
|
}
|
|
48892
49489
|
const doc = await ctx.client.get("Stock Entry", input.name);
|
|
48893
|
-
return { data: doc };
|
|
49490
|
+
return { data: { ...doc, doctype: "Stock Entry" } };
|
|
48894
49491
|
}
|
|
48895
49492
|
},
|
|
48896
49493
|
{
|
|
@@ -49085,6 +49682,7 @@ var accountingTools = [
|
|
|
49085
49682
|
{
|
|
49086
49683
|
name: "erpnext_journal_entry_get",
|
|
49087
49684
|
annotations: { readOnlyHint: true },
|
|
49685
|
+
_meta: DOC_META,
|
|
49088
49686
|
description: "Get a single Journal Entry by name (e.g. JV-00001). Returns full document with accounts.",
|
|
49089
49687
|
category: "accounting",
|
|
49090
49688
|
inputSchema: {
|
|
@@ -49102,7 +49700,7 @@ var accountingTools = [
|
|
|
49102
49700
|
throw new Error("[erpnext_journal_entry_get] 'name' is required");
|
|
49103
49701
|
}
|
|
49104
49702
|
const doc = await ctx.client.get("Journal Entry", input.name);
|
|
49105
|
-
return { data: doc };
|
|
49703
|
+
return { data: { ...doc, doctype: "Journal Entry" } };
|
|
49106
49704
|
}
|
|
49107
49705
|
},
|
|
49108
49706
|
// ── Payment Entries ───────────────────────────────────────────────────────
|
|
@@ -49190,6 +49788,7 @@ var accountingTools = [
|
|
|
49190
49788
|
{
|
|
49191
49789
|
name: "erpnext_payment_entry_get",
|
|
49192
49790
|
annotations: { readOnlyHint: true },
|
|
49791
|
+
_meta: DOC_META,
|
|
49193
49792
|
description: "Get a single Payment Entry by name (e.g. PE-00001). Returns full document including references.",
|
|
49194
49793
|
category: "accounting",
|
|
49195
49794
|
inputSchema: {
|
|
@@ -49207,7 +49806,7 @@ var accountingTools = [
|
|
|
49207
49806
|
throw new Error("[erpnext_payment_entry_get] 'name' is required");
|
|
49208
49807
|
}
|
|
49209
49808
|
const doc = await ctx.client.get("Payment Entry", input.name);
|
|
49210
|
-
return { data: doc };
|
|
49809
|
+
return { data: { ...doc, doctype: "Payment Entry" } };
|
|
49211
49810
|
}
|
|
49212
49811
|
},
|
|
49213
49812
|
{
|
|
@@ -49333,6 +49932,7 @@ var hrTools = [
|
|
|
49333
49932
|
{
|
|
49334
49933
|
name: "erpnext_employee_get",
|
|
49335
49934
|
annotations: { readOnlyHint: true },
|
|
49935
|
+
_meta: DOC_META,
|
|
49336
49936
|
description: "Get a single Employee by name/ID (e.g. HR-EMP-00001). Returns all fields.",
|
|
49337
49937
|
category: "hr",
|
|
49338
49938
|
inputSchema: {
|
|
@@ -49357,7 +49957,7 @@ var hrTools = [
|
|
|
49357
49957
|
{ allowPartialMatch: false, inputPath: "name" }
|
|
49358
49958
|
);
|
|
49359
49959
|
const doc = await ctx.client.get("Employee", employeeId);
|
|
49360
|
-
return { data: doc };
|
|
49960
|
+
return { data: { ...doc, doctype: "Employee" } };
|
|
49361
49961
|
}
|
|
49362
49962
|
},
|
|
49363
49963
|
// ── Attendance ────────────────────────────────────────────────────────────
|
|
@@ -49508,6 +50108,7 @@ var hrTools = [
|
|
|
49508
50108
|
{
|
|
49509
50109
|
name: "erpnext_leave_application_get",
|
|
49510
50110
|
annotations: { readOnlyHint: true },
|
|
50111
|
+
_meta: DOC_META,
|
|
49511
50112
|
description: "Get a single Leave Application by name. Returns full document.",
|
|
49512
50113
|
category: "hr",
|
|
49513
50114
|
inputSchema: {
|
|
@@ -49525,7 +50126,7 @@ var hrTools = [
|
|
|
49525
50126
|
"Leave Application",
|
|
49526
50127
|
input.name
|
|
49527
50128
|
);
|
|
49528
|
-
return { data: doc };
|
|
50129
|
+
return { data: { ...doc, doctype: "Leave Application" } };
|
|
49529
50130
|
}
|
|
49530
50131
|
},
|
|
49531
50132
|
{
|
|
@@ -49671,6 +50272,7 @@ var hrTools = [
|
|
|
49671
50272
|
{
|
|
49672
50273
|
name: "erpnext_salary_slip_get",
|
|
49673
50274
|
annotations: { readOnlyHint: true },
|
|
50275
|
+
_meta: DOC_META,
|
|
49674
50276
|
description: "Get a single Salary Slip by name/ID. Returns all fields including earnings and deductions.",
|
|
49675
50277
|
category: "hr",
|
|
49676
50278
|
inputSchema: {
|
|
@@ -49688,7 +50290,7 @@ var hrTools = [
|
|
|
49688
50290
|
throw new Error("[erpnext_salary_slip_get] 'name' is required");
|
|
49689
50291
|
}
|
|
49690
50292
|
const doc = await ctx.client.get("Salary Slip", input.name);
|
|
49691
|
-
return { data: doc };
|
|
50293
|
+
return { data: { ...doc, doctype: "Salary Slip" } };
|
|
49692
50294
|
}
|
|
49693
50295
|
},
|
|
49694
50296
|
// ── Payroll Entries ───────────────────────────────────────────────────────
|
|
@@ -50179,6 +50781,7 @@ var projectTools = [
|
|
|
50179
50781
|
{
|
|
50180
50782
|
name: "erpnext_project_get",
|
|
50181
50783
|
annotations: { readOnlyHint: true },
|
|
50784
|
+
_meta: DOC_META,
|
|
50182
50785
|
description: "Get a single Project by name. Returns full document including tasks summary.",
|
|
50183
50786
|
category: "project",
|
|
50184
50787
|
inputSchema: {
|
|
@@ -50193,7 +50796,7 @@ var projectTools = [
|
|
|
50193
50796
|
throw new Error("[erpnext_project_get] 'name' is required");
|
|
50194
50797
|
}
|
|
50195
50798
|
const doc = await ctx.client.get("Project", input.name);
|
|
50196
|
-
return { data: doc };
|
|
50799
|
+
return { data: { ...doc, doctype: "Project" } };
|
|
50197
50800
|
}
|
|
50198
50801
|
},
|
|
50199
50802
|
// ── Tasks ─────────────────────────────────────────────────────────────────
|
|
@@ -50358,6 +50961,7 @@ var projectTools = [
|
|
|
50358
50961
|
{
|
|
50359
50962
|
name: "erpnext_task_get",
|
|
50360
50963
|
annotations: { readOnlyHint: true },
|
|
50964
|
+
_meta: DOC_META,
|
|
50361
50965
|
description: "Get a single Task by name. Returns full document including description and dependencies.",
|
|
50362
50966
|
category: "project",
|
|
50363
50967
|
inputSchema: {
|
|
@@ -50372,7 +50976,7 @@ var projectTools = [
|
|
|
50372
50976
|
throw new Error("[erpnext_task_get] 'name' is required");
|
|
50373
50977
|
}
|
|
50374
50978
|
const doc = await ctx.client.get("Task", input.name);
|
|
50375
|
-
return { data: doc };
|
|
50979
|
+
return { data: { ...doc, doctype: "Task" } };
|
|
50376
50980
|
}
|
|
50377
50981
|
},
|
|
50378
50982
|
{
|
|
@@ -50548,6 +51152,7 @@ var projectTools = [
|
|
|
50548
51152
|
{
|
|
50549
51153
|
name: "erpnext_timesheet_get",
|
|
50550
51154
|
annotations: { readOnlyHint: true },
|
|
51155
|
+
_meta: DOC_META,
|
|
50551
51156
|
description: "Get a single Timesheet by name. Returns full document with time log details.",
|
|
50552
51157
|
category: "project",
|
|
50553
51158
|
inputSchema: {
|
|
@@ -50562,7 +51167,7 @@ var projectTools = [
|
|
|
50562
51167
|
throw new Error("[erpnext_timesheet_get] 'name' is required");
|
|
50563
51168
|
}
|
|
50564
51169
|
const doc = await ctx.client.get("Timesheet", input.name);
|
|
50565
|
-
return { data: doc };
|
|
51170
|
+
return { data: { ...doc, doctype: "Timesheet" } };
|
|
50566
51171
|
}
|
|
50567
51172
|
},
|
|
50568
51173
|
{
|
|
@@ -50684,6 +51289,7 @@ var purchasingTools = [
|
|
|
50684
51289
|
{
|
|
50685
51290
|
name: "erpnext_supplier_get",
|
|
50686
51291
|
annotations: { readOnlyHint: true },
|
|
51292
|
+
_meta: DOC_META,
|
|
50687
51293
|
description: "Get a single ERPNext supplier by name (ID). Returns all fields including contact details.",
|
|
50688
51294
|
category: "purchasing",
|
|
50689
51295
|
inputSchema: {
|
|
@@ -50698,7 +51304,7 @@ var purchasingTools = [
|
|
|
50698
51304
|
throw new Error("[erpnext_supplier_get] 'name' is required");
|
|
50699
51305
|
}
|
|
50700
51306
|
const doc = await ctx.client.get("Supplier", input.name);
|
|
50701
|
-
return { data: doc };
|
|
51307
|
+
return { data: { ...doc, doctype: "Supplier" } };
|
|
50702
51308
|
}
|
|
50703
51309
|
},
|
|
50704
51310
|
{
|
|
@@ -50827,6 +51433,7 @@ var purchasingTools = [
|
|
|
50827
51433
|
{
|
|
50828
51434
|
name: "erpnext_purchase_order_get",
|
|
50829
51435
|
annotations: { readOnlyHint: true },
|
|
51436
|
+
_meta: DOC_META,
|
|
50830
51437
|
description: "Get a single Purchase Order by name (e.g. PO-00001). Returns full document with line items.",
|
|
50831
51438
|
category: "purchasing",
|
|
50832
51439
|
inputSchema: {
|
|
@@ -50844,7 +51451,7 @@ var purchasingTools = [
|
|
|
50844
51451
|
throw new Error("[erpnext_purchase_order_get] 'name' is required");
|
|
50845
51452
|
}
|
|
50846
51453
|
const doc = await ctx.client.get("Purchase Order", input.name);
|
|
50847
|
-
return { data: doc };
|
|
51454
|
+
return { data: { ...doc, doctype: "Purchase Order" } };
|
|
50848
51455
|
}
|
|
50849
51456
|
},
|
|
50850
51457
|
{
|
|
@@ -50995,6 +51602,7 @@ var purchasingTools = [
|
|
|
50995
51602
|
{
|
|
50996
51603
|
name: "erpnext_purchase_invoice_get",
|
|
50997
51604
|
annotations: { readOnlyHint: true },
|
|
51605
|
+
_meta: DOC_META,
|
|
50998
51606
|
description: "Get a single Purchase Invoice by name (e.g. PINV-00001). Returns full document with line items.",
|
|
50999
51607
|
category: "purchasing",
|
|
51000
51608
|
inputSchema: {
|
|
@@ -51015,7 +51623,7 @@ var purchasingTools = [
|
|
|
51015
51623
|
"Purchase Invoice",
|
|
51016
51624
|
input.name
|
|
51017
51625
|
);
|
|
51018
|
-
return { data: doc };
|
|
51626
|
+
return { data: { ...doc, doctype: "Purchase Invoice" } };
|
|
51019
51627
|
}
|
|
51020
51628
|
},
|
|
51021
51629
|
// ── Purchase Receipts ─────────────────────────────────────────────────────
|
|
@@ -51089,6 +51697,7 @@ var purchasingTools = [
|
|
|
51089
51697
|
{
|
|
51090
51698
|
name: "erpnext_purchase_receipt_get",
|
|
51091
51699
|
annotations: { readOnlyHint: true },
|
|
51700
|
+
_meta: DOC_META,
|
|
51092
51701
|
description: "Get a single Purchase Receipt by name (e.g. MAT-PRE-00001). Returns full document with received items.",
|
|
51093
51702
|
category: "purchasing",
|
|
51094
51703
|
inputSchema: {
|
|
@@ -51109,7 +51718,7 @@ var purchasingTools = [
|
|
|
51109
51718
|
"Purchase Receipt",
|
|
51110
51719
|
input.name
|
|
51111
51720
|
);
|
|
51112
|
-
return { data: doc };
|
|
51721
|
+
return { data: { ...doc, doctype: "Purchase Receipt" } };
|
|
51113
51722
|
}
|
|
51114
51723
|
},
|
|
51115
51724
|
// ── Supplier Quotations ───────────────────────────────────────────────────
|
|
@@ -51254,6 +51863,7 @@ var deliveryTools = [
|
|
|
51254
51863
|
{
|
|
51255
51864
|
name: "erpnext_delivery_note_get",
|
|
51256
51865
|
annotations: { readOnlyHint: true },
|
|
51866
|
+
_meta: DOC_META,
|
|
51257
51867
|
description: "Get a single Delivery Note by name (e.g. MAT-DN-00001). Returns full document with delivered items.",
|
|
51258
51868
|
category: "delivery",
|
|
51259
51869
|
inputSchema: {
|
|
@@ -51271,7 +51881,7 @@ var deliveryTools = [
|
|
|
51271
51881
|
throw new Error("[erpnext_delivery_note_get] 'name' is required");
|
|
51272
51882
|
}
|
|
51273
51883
|
const doc = await ctx.client.get("Delivery Note", input.name);
|
|
51274
|
-
return { data: doc };
|
|
51884
|
+
return { data: { ...doc, doctype: "Delivery Note" } };
|
|
51275
51885
|
}
|
|
51276
51886
|
},
|
|
51277
51887
|
{
|
|
@@ -51404,6 +52014,7 @@ var deliveryTools = [
|
|
|
51404
52014
|
{
|
|
51405
52015
|
name: "erpnext_shipment_get",
|
|
51406
52016
|
annotations: { readOnlyHint: true },
|
|
52017
|
+
_meta: DOC_META,
|
|
51407
52018
|
description: "Get a single Shipment by name. Returns full shipment details including parcels.",
|
|
51408
52019
|
category: "delivery",
|
|
51409
52020
|
inputSchema: {
|
|
@@ -51418,7 +52029,7 @@ var deliveryTools = [
|
|
|
51418
52029
|
throw new Error("[erpnext_shipment_get] 'name' is required");
|
|
51419
52030
|
}
|
|
51420
52031
|
const doc = await ctx.client.get("Shipment", input.name);
|
|
51421
|
-
return { data: doc };
|
|
52032
|
+
return { data: { ...doc, doctype: "Shipment" } };
|
|
51422
52033
|
}
|
|
51423
52034
|
}
|
|
51424
52035
|
];
|
|
@@ -51498,6 +52109,7 @@ var manufacturingTools = [
|
|
|
51498
52109
|
{
|
|
51499
52110
|
name: "erpnext_bom_get",
|
|
51500
52111
|
annotations: { readOnlyHint: true },
|
|
52112
|
+
_meta: DOC_META,
|
|
51501
52113
|
description: "Get a single BOM by name (e.g. BOM-ITEM-00001). Returns full document with raw materials and operations.",
|
|
51502
52114
|
category: "manufacturing",
|
|
51503
52115
|
inputSchema: {
|
|
@@ -51512,7 +52124,7 @@ var manufacturingTools = [
|
|
|
51512
52124
|
throw new Error("[erpnext_bom_get] 'name' is required");
|
|
51513
52125
|
}
|
|
51514
52126
|
const doc = await ctx.client.get("BOM", input.name);
|
|
51515
|
-
return { data: doc };
|
|
52127
|
+
return { data: { ...doc, doctype: "BOM" } };
|
|
51516
52128
|
}
|
|
51517
52129
|
},
|
|
51518
52130
|
// ── Work Orders ───────────────────────────────────────────────────────────
|
|
@@ -51590,6 +52202,7 @@ var manufacturingTools = [
|
|
|
51590
52202
|
{
|
|
51591
52203
|
name: "erpnext_work_order_get",
|
|
51592
52204
|
annotations: { readOnlyHint: true },
|
|
52205
|
+
_meta: DOC_META,
|
|
51593
52206
|
description: "Get a single Work Order by name (e.g. MFG-WO-00001). Returns full document with operations and materials.",
|
|
51594
52207
|
category: "manufacturing",
|
|
51595
52208
|
inputSchema: {
|
|
@@ -51607,7 +52220,7 @@ var manufacturingTools = [
|
|
|
51607
52220
|
throw new Error("[erpnext_work_order_get] 'name' is required");
|
|
51608
52221
|
}
|
|
51609
52222
|
const doc = await ctx.client.get("Work Order", input.name);
|
|
51610
|
-
return { data: doc };
|
|
52223
|
+
return { data: { ...doc, doctype: "Work Order" } };
|
|
51611
52224
|
}
|
|
51612
52225
|
},
|
|
51613
52226
|
{
|
|
@@ -51723,6 +52336,7 @@ var manufacturingTools = [
|
|
|
51723
52336
|
{
|
|
51724
52337
|
name: "erpnext_job_card_get",
|
|
51725
52338
|
annotations: { readOnlyHint: true },
|
|
52339
|
+
_meta: DOC_META,
|
|
51726
52340
|
description: "Get a single Job Card by name. Returns full document with time logs and material transfers.",
|
|
51727
52341
|
category: "manufacturing",
|
|
51728
52342
|
inputSchema: {
|
|
@@ -51737,7 +52351,7 @@ var manufacturingTools = [
|
|
|
51737
52351
|
throw new Error("[erpnext_job_card_get] 'name' is required");
|
|
51738
52352
|
}
|
|
51739
52353
|
const doc = await ctx.client.get("Job Card", input.name);
|
|
51740
|
-
return { data: doc };
|
|
52354
|
+
return { data: { ...doc, doctype: "Job Card" } };
|
|
51741
52355
|
}
|
|
51742
52356
|
}
|
|
51743
52357
|
];
|
|
@@ -51799,6 +52413,7 @@ var crmTools = [
|
|
|
51799
52413
|
{
|
|
51800
52414
|
name: "erpnext_lead_get",
|
|
51801
52415
|
annotations: { readOnlyHint: true },
|
|
52416
|
+
_meta: DOC_META,
|
|
51802
52417
|
description: "Get a single CRM Lead by name. Returns all lead details including contact info.",
|
|
51803
52418
|
category: "crm",
|
|
51804
52419
|
inputSchema: {
|
|
@@ -51813,7 +52428,7 @@ var crmTools = [
|
|
|
51813
52428
|
throw new Error("[erpnext_lead_get] 'name' is required");
|
|
51814
52429
|
}
|
|
51815
52430
|
const doc = await ctx.client.get("Lead", input.name);
|
|
51816
|
-
return { data: doc };
|
|
52431
|
+
return { data: { ...doc, doctype: "Lead" } };
|
|
51817
52432
|
}
|
|
51818
52433
|
},
|
|
51819
52434
|
{
|
|
@@ -51945,6 +52560,7 @@ var crmTools = [
|
|
|
51945
52560
|
{
|
|
51946
52561
|
name: "erpnext_opportunity_get",
|
|
51947
52562
|
annotations: { readOnlyHint: true },
|
|
52563
|
+
_meta: DOC_META,
|
|
51948
52564
|
description: "Get a single CRM Opportunity by name. Returns full details including items and competitors.",
|
|
51949
52565
|
category: "crm",
|
|
51950
52566
|
inputSchema: {
|
|
@@ -51959,7 +52575,7 @@ var crmTools = [
|
|
|
51959
52575
|
throw new Error("[erpnext_opportunity_get] 'name' is required");
|
|
51960
52576
|
}
|
|
51961
52577
|
const doc = await ctx.client.get("Opportunity", input.name);
|
|
51962
|
-
return { data: doc };
|
|
52578
|
+
return { data: { ...doc, doctype: "Opportunity" } };
|
|
51963
52579
|
}
|
|
51964
52580
|
},
|
|
51965
52581
|
// ── Contacts ──────────────────────────────────────────────────────────────
|
|
@@ -52014,6 +52630,7 @@ var crmTools = [
|
|
|
52014
52630
|
{
|
|
52015
52631
|
name: "erpnext_contact_get",
|
|
52016
52632
|
annotations: { readOnlyHint: true },
|
|
52633
|
+
_meta: DOC_META,
|
|
52017
52634
|
description: "Get a single Contact by name. Returns all contact details.",
|
|
52018
52635
|
category: "crm",
|
|
52019
52636
|
inputSchema: {
|
|
@@ -52028,7 +52645,7 @@ var crmTools = [
|
|
|
52028
52645
|
throw new Error("[erpnext_contact_get] 'name' is required");
|
|
52029
52646
|
}
|
|
52030
52647
|
const doc = await ctx.client.get("Contact", input.name);
|
|
52031
|
-
return { data: doc };
|
|
52648
|
+
return { data: { ...doc, doctype: "Contact" } };
|
|
52032
52649
|
}
|
|
52033
52650
|
},
|
|
52034
52651
|
// ── Campaigns ─────────────────────────────────────────────────────────────
|
|
@@ -52178,6 +52795,7 @@ var assetsTools = [
|
|
|
52178
52795
|
{
|
|
52179
52796
|
name: "erpnext_asset_get",
|
|
52180
52797
|
annotations: { readOnlyHint: true },
|
|
52798
|
+
_meta: DOC_META,
|
|
52181
52799
|
description: "Get a single Asset by name. Returns full details including depreciation schedule and maintenance logs.",
|
|
52182
52800
|
category: "assets",
|
|
52183
52801
|
inputSchema: {
|
|
@@ -52192,7 +52810,7 @@ var assetsTools = [
|
|
|
52192
52810
|
throw new Error("[erpnext_asset_get] 'name' is required");
|
|
52193
52811
|
}
|
|
52194
52812
|
const doc = await ctx.client.get("Asset", input.name);
|
|
52195
|
-
return { data: doc };
|
|
52813
|
+
return { data: { ...doc, doctype: "Asset" } };
|
|
52196
52814
|
}
|
|
52197
52815
|
},
|
|
52198
52816
|
{
|
|
@@ -52328,6 +52946,7 @@ var assetsTools = [
|
|
|
52328
52946
|
{
|
|
52329
52947
|
name: "erpnext_asset_movement_get",
|
|
52330
52948
|
annotations: { readOnlyHint: true },
|
|
52949
|
+
_meta: DOC_META,
|
|
52331
52950
|
description: "Get a single Asset Movement by name. Returns full details including assets moved.",
|
|
52332
52951
|
category: "assets",
|
|
52333
52952
|
inputSchema: {
|
|
@@ -52342,7 +52961,7 @@ var assetsTools = [
|
|
|
52342
52961
|
throw new Error("[erpnext_asset_movement_get] 'name' is required");
|
|
52343
52962
|
}
|
|
52344
52963
|
const doc = await ctx.client.get("Asset Movement", input.name);
|
|
52345
|
-
return { data: doc };
|
|
52964
|
+
return { data: { ...doc, doctype: "Asset Movement" } };
|
|
52346
52965
|
}
|
|
52347
52966
|
},
|
|
52348
52967
|
// ── Asset Maintenance ─────────────────────────────────────────────────────
|
|
@@ -52399,6 +53018,7 @@ var assetsTools = [
|
|
|
52399
53018
|
{
|
|
52400
53019
|
name: "erpnext_asset_maintenance_get",
|
|
52401
53020
|
annotations: { readOnlyHint: true },
|
|
53021
|
+
_meta: DOC_META,
|
|
52402
53022
|
description: "Get a single Asset Maintenance record by name. Returns full details including maintenance tasks.",
|
|
52403
53023
|
category: "assets",
|
|
52404
53024
|
inputSchema: {
|
|
@@ -52416,7 +53036,7 @@ var assetsTools = [
|
|
|
52416
53036
|
"Asset Maintenance",
|
|
52417
53037
|
input.name
|
|
52418
53038
|
);
|
|
52419
|
-
return { data: doc };
|
|
53039
|
+
return { data: { ...doc, doctype: "Asset Maintenance" } };
|
|
52420
53040
|
}
|
|
52421
53041
|
},
|
|
52422
53042
|
// ── Asset Categories ──────────────────────────────────────────────────────
|
|
@@ -52451,8 +53071,161 @@ var assetsTools = [
|
|
|
52451
53071
|
];
|
|
52452
53072
|
|
|
52453
53073
|
// src/tools/operations.ts
|
|
53074
|
+
function bytesToBase64(bytes) {
|
|
53075
|
+
const chunkSize = 32 * 1024;
|
|
53076
|
+
let binary = "";
|
|
53077
|
+
for (let offset = 0; offset < bytes.byteLength; offset += chunkSize) {
|
|
53078
|
+
binary += String.fromCharCode(
|
|
53079
|
+
...bytes.subarray(offset, Math.min(offset + chunkSize, bytes.byteLength))
|
|
53080
|
+
);
|
|
53081
|
+
}
|
|
53082
|
+
return btoa(binary);
|
|
53083
|
+
}
|
|
52454
53084
|
var operationsTools = [
|
|
52455
53085
|
// ── File Attachments ───────────────────────────────────────────────────────
|
|
53086
|
+
{
|
|
53087
|
+
name: "erpnext_file_list",
|
|
53088
|
+
annotations: { readOnlyHint: true },
|
|
53089
|
+
description: "List the files attached to an ERPNext document. Returns name, size, privacy and URL for each attachment. Pairs with erpnext_file_upload: that one attaches, this one reads back.",
|
|
53090
|
+
category: "operations",
|
|
53091
|
+
inputSchema: {
|
|
53092
|
+
type: "object",
|
|
53093
|
+
properties: {
|
|
53094
|
+
attached_to_doctype: {
|
|
53095
|
+
type: "string",
|
|
53096
|
+
description: "DocType of the document whose attachments to list.",
|
|
53097
|
+
minLength: 1
|
|
53098
|
+
},
|
|
53099
|
+
attached_to_name: {
|
|
53100
|
+
type: "string",
|
|
53101
|
+
description: "Name/ID of the document whose attachments to list.",
|
|
53102
|
+
minLength: 1
|
|
53103
|
+
},
|
|
53104
|
+
limit: {
|
|
53105
|
+
type: "number",
|
|
53106
|
+
description: "Maximum number of files to return. Defaults to 50.",
|
|
53107
|
+
minimum: 1,
|
|
53108
|
+
maximum: 500
|
|
53109
|
+
}
|
|
53110
|
+
},
|
|
53111
|
+
required: ["attached_to_doctype", "attached_to_name"]
|
|
53112
|
+
},
|
|
53113
|
+
handler: async (input, ctx) => {
|
|
53114
|
+
for (const field of ["attached_to_doctype", "attached_to_name"]) {
|
|
53115
|
+
if (typeof input[field] !== "string" || !input[field].trim()) {
|
|
53116
|
+
throw new Error(
|
|
53117
|
+
`[erpnext_file_list] '${field}' must be a non-empty string`
|
|
53118
|
+
);
|
|
53119
|
+
}
|
|
53120
|
+
}
|
|
53121
|
+
if (input.limit !== void 0 && (typeof input.limit !== "number" || !Number.isInteger(input.limit) || input.limit < 1 || input.limit > 500)) {
|
|
53122
|
+
throw new Error(
|
|
53123
|
+
"[erpnext_file_list] 'limit' must be an integer between 1 and 500"
|
|
53124
|
+
);
|
|
53125
|
+
}
|
|
53126
|
+
const files = await ctx.client.list("File", {
|
|
53127
|
+
fields: [
|
|
53128
|
+
"name",
|
|
53129
|
+
"file_name",
|
|
53130
|
+
"file_url",
|
|
53131
|
+
"file_size",
|
|
53132
|
+
"is_private",
|
|
53133
|
+
"attached_to_field",
|
|
53134
|
+
"creation",
|
|
53135
|
+
"modified",
|
|
53136
|
+
"owner"
|
|
53137
|
+
],
|
|
53138
|
+
filters: [
|
|
53139
|
+
["attached_to_doctype", "=", input.attached_to_doctype],
|
|
53140
|
+
["attached_to_name", "=", input.attached_to_name]
|
|
53141
|
+
],
|
|
53142
|
+
order_by: "creation desc",
|
|
53143
|
+
limit: input.limit ?? 50
|
|
53144
|
+
});
|
|
53145
|
+
return {
|
|
53146
|
+
count: files.length,
|
|
53147
|
+
data: files.map((file) => ({
|
|
53148
|
+
name: file.name,
|
|
53149
|
+
file_name: file.file_name,
|
|
53150
|
+
file_url: file.file_url,
|
|
53151
|
+
file_size: file.file_size ?? null,
|
|
53152
|
+
is_private: file.is_private === 1,
|
|
53153
|
+
attached_to_field: file.attached_to_field ?? null,
|
|
53154
|
+
creation: file.creation,
|
|
53155
|
+
modified: file.modified,
|
|
53156
|
+
owner: file.owner
|
|
53157
|
+
}))
|
|
53158
|
+
};
|
|
53159
|
+
}
|
|
53160
|
+
},
|
|
53161
|
+
{
|
|
53162
|
+
name: "erpnext_file_download",
|
|
53163
|
+
annotations: { readOnlyHint: true },
|
|
53164
|
+
_meta: {
|
|
53165
|
+
ui: {
|
|
53166
|
+
resourceUri: DOC_META.ui.resourceUri,
|
|
53167
|
+
visibility: ["app"]
|
|
53168
|
+
}
|
|
53169
|
+
},
|
|
53170
|
+
description: "Download one ERPNext attachment for the document viewer. The tool accepts a File ID, verifies its document attachment, and returns one embedded binary resource.",
|
|
53171
|
+
category: "operations",
|
|
53172
|
+
inputSchema: {
|
|
53173
|
+
type: "object",
|
|
53174
|
+
properties: {
|
|
53175
|
+
file_id: {
|
|
53176
|
+
type: "string",
|
|
53177
|
+
description: "Native ERPNext File.name identifier, never a URL.",
|
|
53178
|
+
minLength: 1
|
|
53179
|
+
},
|
|
53180
|
+
attached_to_doctype: {
|
|
53181
|
+
type: "string",
|
|
53182
|
+
description: "Expected parent document DocType.",
|
|
53183
|
+
minLength: 1
|
|
53184
|
+
},
|
|
53185
|
+
attached_to_name: {
|
|
53186
|
+
type: "string",
|
|
53187
|
+
description: "Expected parent document name/ID.",
|
|
53188
|
+
minLength: 1
|
|
53189
|
+
}
|
|
53190
|
+
},
|
|
53191
|
+
required: ["file_id", "attached_to_doctype", "attached_to_name"],
|
|
53192
|
+
additionalProperties: false
|
|
53193
|
+
},
|
|
53194
|
+
handler: async (input, ctx) => {
|
|
53195
|
+
for (const field of [
|
|
53196
|
+
"file_id",
|
|
53197
|
+
"attached_to_doctype",
|
|
53198
|
+
"attached_to_name"
|
|
53199
|
+
]) {
|
|
53200
|
+
if (typeof input[field] !== "string" || !input[field].trim()) {
|
|
53201
|
+
throw new Error(
|
|
53202
|
+
`[erpnext_file_download] '${field}' must be a non-empty string`
|
|
53203
|
+
);
|
|
53204
|
+
}
|
|
53205
|
+
}
|
|
53206
|
+
const file = await ctx.client.downloadFile({
|
|
53207
|
+
fileId: input.file_id.trim(),
|
|
53208
|
+
attachedToDoctype: input.attached_to_doctype.trim(),
|
|
53209
|
+
attachedToName: input.attached_to_name.trim()
|
|
53210
|
+
});
|
|
53211
|
+
return {
|
|
53212
|
+
content: [
|
|
53213
|
+
{
|
|
53214
|
+
type: "text",
|
|
53215
|
+
text: `Prepared ${file.fileName} for download (${file.bytes.byteLength} bytes).`
|
|
53216
|
+
},
|
|
53217
|
+
{
|
|
53218
|
+
type: "resource",
|
|
53219
|
+
resource: {
|
|
53220
|
+
uri: `file:///${encodeURIComponent(file.fileName)}`,
|
|
53221
|
+
mimeType: file.mimeType,
|
|
53222
|
+
blob: bytesToBase64(file.bytes)
|
|
53223
|
+
}
|
|
53224
|
+
}
|
|
53225
|
+
]
|
|
53226
|
+
};
|
|
53227
|
+
}
|
|
53228
|
+
},
|
|
52456
53229
|
{
|
|
52457
53230
|
name: "erpnext_file_upload",
|
|
52458
53231
|
annotations: { destructiveHint: true },
|
|
@@ -52754,6 +53527,7 @@ var operationsTools = [
|
|
|
52754
53527
|
{
|
|
52755
53528
|
name: "erpnext_doc_get",
|
|
52756
53529
|
annotations: { readOnlyHint: true },
|
|
53530
|
+
_meta: DOC_META,
|
|
52757
53531
|
description: "Get any ERPNext document by DocType and name. Useful for DocTypes not covered by dedicated tools. Returns the full document with all fields.",
|
|
52758
53532
|
category: "operations",
|
|
52759
53533
|
inputSchema: {
|
|
@@ -52771,17 +53545,18 @@ var operationsTools = [
|
|
|
52771
53545
|
required: ["doctype", "name"]
|
|
52772
53546
|
},
|
|
52773
53547
|
handler: async (input, ctx) => {
|
|
52774
|
-
if (!input.doctype) {
|
|
53548
|
+
if (typeof input.doctype !== "string" || !input.doctype.trim()) {
|
|
52775
53549
|
throw new Error("[erpnext_doc_get] 'doctype' is required");
|
|
52776
53550
|
}
|
|
52777
|
-
if (!input.name) {
|
|
53551
|
+
if (typeof input.name !== "string" || !input.name.trim()) {
|
|
52778
53552
|
throw new Error("[erpnext_doc_get] 'name' is required");
|
|
52779
53553
|
}
|
|
53554
|
+
const doctype = input.doctype.trim();
|
|
52780
53555
|
const doc = await ctx.client.get(
|
|
52781
|
-
|
|
52782
|
-
input.name
|
|
53556
|
+
doctype,
|
|
53557
|
+
input.name.trim()
|
|
52783
53558
|
);
|
|
52784
|
-
return { data: doc };
|
|
53559
|
+
return { data: { ...doc, doctype } };
|
|
52785
53560
|
}
|
|
52786
53561
|
},
|
|
52787
53562
|
// ── Generic List ──────────────────────────────────────────────────────────
|
|
@@ -52805,10 +53580,52 @@ var operationsTools = [
|
|
|
52805
53580
|
},
|
|
52806
53581
|
filters: {
|
|
52807
53582
|
type: "array",
|
|
52808
|
-
description: 'Frappe filters as array of [fieldname, operator, value] tuples. Example: [["status","=","Open"],["company","=","Acme"]]',
|
|
53583
|
+
description: 'Frappe filters as array of [fieldname, operator, value] tuples, or [child doctype, fieldname, operator, value] to filter on a child table. Values may be strings, numbers, booleans, null, or string/number arrays for in/not in. Example: [["status","=","Open"],["company","=","Acme"]]',
|
|
52809
53584
|
items: {
|
|
52810
53585
|
type: "array",
|
|
52811
|
-
|
|
53586
|
+
anyOf: [
|
|
53587
|
+
{
|
|
53588
|
+
prefixItems: [
|
|
53589
|
+
{ type: "string", minLength: 1 },
|
|
53590
|
+
{ type: "string", minLength: 1 },
|
|
53591
|
+
{
|
|
53592
|
+
oneOf: [
|
|
53593
|
+
{ type: ["string", "number", "boolean", "null"] },
|
|
53594
|
+
{
|
|
53595
|
+
type: "array",
|
|
53596
|
+
items: { type: ["string", "number"] }
|
|
53597
|
+
}
|
|
53598
|
+
]
|
|
53599
|
+
}
|
|
53600
|
+
],
|
|
53601
|
+
minItems: 3,
|
|
53602
|
+
maxItems: 3
|
|
53603
|
+
},
|
|
53604
|
+
{
|
|
53605
|
+
prefixItems: [
|
|
53606
|
+
{ type: "string", minLength: 1 },
|
|
53607
|
+
{ type: "string", minLength: 1 },
|
|
53608
|
+
{ type: "string", minLength: 1 },
|
|
53609
|
+
{
|
|
53610
|
+
oneOf: [
|
|
53611
|
+
{ type: ["string", "number", "boolean", "null"] },
|
|
53612
|
+
{
|
|
53613
|
+
type: "array",
|
|
53614
|
+
items: { type: ["string", "number"] }
|
|
53615
|
+
}
|
|
53616
|
+
]
|
|
53617
|
+
}
|
|
53618
|
+
],
|
|
53619
|
+
minItems: 4,
|
|
53620
|
+
maxItems: 4
|
|
53621
|
+
},
|
|
53622
|
+
{
|
|
53623
|
+
// 3.0.x advertised arbitrary string arrays. Keep accepting
|
|
53624
|
+
// that legacy surface while describing real 3/4-part Frappe
|
|
53625
|
+
// tuples precisely for modern clients.
|
|
53626
|
+
items: { type: "string" }
|
|
53627
|
+
}
|
|
53628
|
+
]
|
|
52812
53629
|
}
|
|
52813
53630
|
},
|
|
52814
53631
|
limit: { type: "number", description: "Max results (default 20)" },
|
|
@@ -53215,7 +54032,6 @@ var analyticsTools = [
|
|
|
53215
54032
|
const invoices2 = await ctx.client.list("Sales Invoice", {
|
|
53216
54033
|
fields: ["name", "status", "grand_total"],
|
|
53217
54034
|
filters: [["docstatus", "!=", 2]],
|
|
53218
|
-
// exclude cancelled
|
|
53219
54035
|
limit: 500,
|
|
53220
54036
|
order_by: "modified desc"
|
|
53221
54037
|
});
|
|
@@ -53551,6 +54367,7 @@ var analyticsTools = [
|
|
|
53551
54367
|
color: "#fbbf24",
|
|
53552
54368
|
type: "line",
|
|
53553
54369
|
yAxisId: "right",
|
|
54370
|
+
unit: "orders",
|
|
53554
54371
|
showDots: true
|
|
53555
54372
|
}
|
|
53556
54373
|
],
|
|
@@ -54006,7 +54823,9 @@ var analyticsTools = [
|
|
|
54006
54823
|
category: "analytics",
|
|
54007
54824
|
inputSchema: { type: "object", properties: {} },
|
|
54008
54825
|
handler: async (_input, ctx) => {
|
|
54009
|
-
const
|
|
54826
|
+
const now = /* @__PURE__ */ new Date();
|
|
54827
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
54828
|
+
const today = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
|
|
54010
54829
|
const invoices = await ctx.client.list("Sales Invoice", {
|
|
54011
54830
|
fields: ["outstanding_amount", "due_date"],
|
|
54012
54831
|
filters: [
|
|
@@ -55616,46 +56435,190 @@ var DOCTYPE_GET_TOOLS = {
|
|
|
55616
56435
|
};
|
|
55617
56436
|
var DOCTYPE_SEND_MESSAGE_HINTS = {
|
|
55618
56437
|
"Customer": [
|
|
55619
|
-
{
|
|
55620
|
-
|
|
56438
|
+
{
|
|
56439
|
+
key: "orders",
|
|
56440
|
+
label: "Orders",
|
|
56441
|
+
message: "Show sales orders for customer {id}",
|
|
56442
|
+
tool: "erpnext_sales_order_list",
|
|
56443
|
+
args: { customer: "{id}", limit: 20 }
|
|
56444
|
+
},
|
|
56445
|
+
{
|
|
56446
|
+
key: "invoices",
|
|
56447
|
+
label: "Invoices",
|
|
56448
|
+
message: "Show sales invoices for customer {id}",
|
|
56449
|
+
tool: "erpnext_sales_invoice_list",
|
|
56450
|
+
args: { customer: "{id}", limit: 20 }
|
|
56451
|
+
}
|
|
55621
56452
|
],
|
|
55622
56453
|
"Sales Order": [
|
|
55623
|
-
{
|
|
55624
|
-
|
|
56454
|
+
{
|
|
56455
|
+
key: "invoice",
|
|
56456
|
+
label: "Invoice",
|
|
56457
|
+
message: "Show invoices linked to sales order {id}",
|
|
56458
|
+
tool: "erpnext_doc_list",
|
|
56459
|
+
args: {
|
|
56460
|
+
doctype: "Sales Invoice",
|
|
56461
|
+
fields: ["name", "customer", "posting_date", "status", "grand_total"],
|
|
56462
|
+
filters: [["Sales Invoice Item", "sales_order", "=", "{id}"]],
|
|
56463
|
+
limit: 20
|
|
56464
|
+
}
|
|
56465
|
+
},
|
|
56466
|
+
{
|
|
56467
|
+
key: "delivery",
|
|
56468
|
+
label: "Delivery",
|
|
56469
|
+
message: "Show delivery notes for sales order {id}",
|
|
56470
|
+
tool: "erpnext_doc_list",
|
|
56471
|
+
args: {
|
|
56472
|
+
doctype: "Delivery Note",
|
|
56473
|
+
fields: ["name", "customer", "posting_date", "status"],
|
|
56474
|
+
filters: [["Delivery Note Item", "against_sales_order", "=", "{id}"]],
|
|
56475
|
+
limit: 20
|
|
56476
|
+
}
|
|
56477
|
+
}
|
|
55625
56478
|
],
|
|
55626
56479
|
"Sales Invoice": [
|
|
55627
|
-
{
|
|
56480
|
+
{
|
|
56481
|
+
key: "payments",
|
|
56482
|
+
label: "Payments",
|
|
56483
|
+
message: "Show payment entries for invoice {id}",
|
|
56484
|
+
tool: "erpnext_doc_list",
|
|
56485
|
+
args: {
|
|
56486
|
+
doctype: "Payment Entry",
|
|
56487
|
+
fields: [
|
|
56488
|
+
"name",
|
|
56489
|
+
"posting_date",
|
|
56490
|
+
"paid_amount",
|
|
56491
|
+
"mode_of_payment",
|
|
56492
|
+
"docstatus"
|
|
56493
|
+
],
|
|
56494
|
+
filters: [["Payment Entry Reference", "reference_name", "=", "{id}"]],
|
|
56495
|
+
limit: 20
|
|
56496
|
+
}
|
|
56497
|
+
}
|
|
55628
56498
|
],
|
|
55629
56499
|
"Item": [
|
|
55630
|
-
{
|
|
55631
|
-
|
|
56500
|
+
{
|
|
56501
|
+
key: "stock",
|
|
56502
|
+
label: "Stock",
|
|
56503
|
+
message: "Show stock balance for item {id}",
|
|
56504
|
+
tool: "erpnext_stock_balance",
|
|
56505
|
+
args: { item_code: "{id}" }
|
|
56506
|
+
},
|
|
56507
|
+
{
|
|
56508
|
+
key: "orders",
|
|
56509
|
+
label: "Orders",
|
|
56510
|
+
message: "Show sales orders containing item {id}",
|
|
56511
|
+
tool: "erpnext_doc_list",
|
|
56512
|
+
args: {
|
|
56513
|
+
doctype: "Sales Order",
|
|
56514
|
+
fields: ["name", "customer", "transaction_date", "status"],
|
|
56515
|
+
filters: [["Sales Order Item", "item_code", "=", "{id}"]],
|
|
56516
|
+
limit: 20
|
|
56517
|
+
}
|
|
56518
|
+
}
|
|
55632
56519
|
],
|
|
55633
56520
|
"Supplier": [
|
|
55634
|
-
{
|
|
55635
|
-
|
|
56521
|
+
{
|
|
56522
|
+
key: "po",
|
|
56523
|
+
label: "PO",
|
|
56524
|
+
message: "Show purchase orders for supplier {id}",
|
|
56525
|
+
tool: "erpnext_purchase_order_list",
|
|
56526
|
+
args: { supplier: "{id}", limit: 20 }
|
|
56527
|
+
},
|
|
56528
|
+
{
|
|
56529
|
+
key: "invoices",
|
|
56530
|
+
label: "Invoices",
|
|
56531
|
+
message: "Show purchase invoices for supplier {id}",
|
|
56532
|
+
tool: "erpnext_purchase_invoice_list",
|
|
56533
|
+
args: { supplier: "{id}", limit: 20 }
|
|
56534
|
+
}
|
|
55636
56535
|
],
|
|
55637
56536
|
"Purchase Order": [
|
|
55638
|
-
{
|
|
55639
|
-
|
|
56537
|
+
{
|
|
56538
|
+
key: "receipt",
|
|
56539
|
+
label: "Receipt",
|
|
56540
|
+
message: "Show purchase receipts for order {id}",
|
|
56541
|
+
tool: "erpnext_doc_list",
|
|
56542
|
+
args: {
|
|
56543
|
+
doctype: "Purchase Receipt",
|
|
56544
|
+
fields: ["name", "supplier", "posting_date", "status"],
|
|
56545
|
+
filters: [["Purchase Receipt Item", "purchase_order", "=", "{id}"]],
|
|
56546
|
+
limit: 20
|
|
56547
|
+
}
|
|
56548
|
+
},
|
|
56549
|
+
{
|
|
56550
|
+
key: "invoice",
|
|
56551
|
+
label: "Invoice",
|
|
56552
|
+
message: "Show purchase invoices for order {id}",
|
|
56553
|
+
tool: "erpnext_doc_list",
|
|
56554
|
+
args: {
|
|
56555
|
+
doctype: "Purchase Invoice",
|
|
56556
|
+
fields: ["name", "supplier", "posting_date", "status", "grand_total"],
|
|
56557
|
+
filters: [["Purchase Invoice Item", "purchase_order", "=", "{id}"]],
|
|
56558
|
+
limit: 20
|
|
56559
|
+
}
|
|
56560
|
+
}
|
|
55640
56561
|
],
|
|
55641
56562
|
"Employee": [
|
|
55642
|
-
{
|
|
55643
|
-
|
|
56563
|
+
{
|
|
56564
|
+
key: "attendance",
|
|
56565
|
+
label: "Attendance",
|
|
56566
|
+
message: "Show attendance for employee {id}",
|
|
56567
|
+
tool: "erpnext_attendance_list",
|
|
56568
|
+
args: { employee: "{id}", limit: 20 }
|
|
56569
|
+
},
|
|
56570
|
+
{
|
|
56571
|
+
key: "leaves",
|
|
56572
|
+
label: "Leaves",
|
|
56573
|
+
message: "Show leave applications for employee {id}",
|
|
56574
|
+
tool: "erpnext_leave_application_list",
|
|
56575
|
+
args: { employee: "{id}", limit: 20 }
|
|
56576
|
+
}
|
|
55644
56577
|
],
|
|
55645
56578
|
"Project": [
|
|
55646
|
-
{
|
|
55647
|
-
|
|
56579
|
+
{
|
|
56580
|
+
key: "tasks",
|
|
56581
|
+
label: "Tasks",
|
|
56582
|
+
message: "Show tasks for project {id}",
|
|
56583
|
+
tool: "erpnext_task_list",
|
|
56584
|
+
args: { project: "{id}", limit: 20 }
|
|
56585
|
+
},
|
|
56586
|
+
{
|
|
56587
|
+
key: "timesheets",
|
|
56588
|
+
label: "Timesheets",
|
|
56589
|
+
message: "Show timesheets for project {id}",
|
|
56590
|
+
tool: "erpnext_timesheet_list",
|
|
56591
|
+
args: { project: "{id}", limit: 20 }
|
|
56592
|
+
}
|
|
55648
56593
|
],
|
|
55649
56594
|
"Task": [
|
|
55650
|
-
{
|
|
56595
|
+
{
|
|
56596
|
+
key: "timesheets",
|
|
56597
|
+
label: "Timesheets",
|
|
56598
|
+
message: "Show timesheets for task {id}",
|
|
56599
|
+
tool: "erpnext_doc_list",
|
|
56600
|
+
args: {
|
|
56601
|
+
doctype: "Timesheet",
|
|
56602
|
+
fields: ["name", "employee", "start_date", "total_hours", "status"],
|
|
56603
|
+
filters: [["Timesheet Detail", "task", "=", "{id}"]],
|
|
56604
|
+
limit: 20
|
|
56605
|
+
}
|
|
56606
|
+
}
|
|
55651
56607
|
],
|
|
55652
56608
|
"Lead": [
|
|
55653
|
-
{
|
|
56609
|
+
{
|
|
56610
|
+
key: "opportunities",
|
|
56611
|
+
label: "Opportunities",
|
|
56612
|
+
message: "Show opportunities for lead {id}",
|
|
56613
|
+
tool: "erpnext_opportunity_list",
|
|
56614
|
+
// Le handler exige opportunity_from avec party_name.
|
|
56615
|
+
args: { opportunity_from: "Lead", party_name: "{id}", limit: 20 }
|
|
56616
|
+
}
|
|
55654
56617
|
]
|
|
55655
56618
|
};
|
|
55656
56619
|
var KPI_DRILL_DOWN = {
|
|
55657
56620
|
"erpnext_kpi_revenue": {
|
|
55658
|
-
_drillDown: "Show all sales
|
|
56621
|
+
_drillDown: "Show all sales orders for this month",
|
|
55659
56622
|
_trendDrillDown: "Show revenue trend chart for the last 12 months"
|
|
55660
56623
|
},
|
|
55661
56624
|
"erpnext_kpi_outstanding": {
|
|
@@ -55667,7 +56630,7 @@ var KPI_DRILL_DOWN = {
|
|
|
55667
56630
|
_trendDrillDown: "Show order breakdown chart for the last 6 months"
|
|
55668
56631
|
},
|
|
55669
56632
|
"erpnext_kpi_gross_margin": {
|
|
55670
|
-
_drillDown: "Show
|
|
56633
|
+
_drillDown: "Show the non-cancelled Sales Order Items and Bin valuation rates used to estimate this gross margin",
|
|
55671
56634
|
_trendDrillDown: "Show profit and loss chart for the last 12 months"
|
|
55672
56635
|
},
|
|
55673
56636
|
"erpnext_kpi_overdue": {
|
|
@@ -55678,12 +56641,521 @@ var KPI_DRILL_DOWN = {
|
|
|
55678
56641
|
var CHART_DRILL_DOWN = {
|
|
55679
56642
|
"erpnext_sales_chart": "Show sales invoices for {label}",
|
|
55680
56643
|
"erpnext_stock_chart": "Show stock balance for item {label}",
|
|
55681
|
-
"erpnext_revenue_trend": "Show sales
|
|
56644
|
+
"erpnext_revenue_trend": "Show sales orders for month {label}",
|
|
55682
56645
|
"erpnext_order_breakdown": "Show sales orders for {label}",
|
|
55683
|
-
"erpnext_ar_aging": "Show
|
|
56646
|
+
"erpnext_ar_aging": "Show outstanding sales invoices for customer {label}",
|
|
55684
56647
|
"erpnext_gross_profit": "Show gross profit details for {label}",
|
|
55685
|
-
"erpnext_profit_loss": "Show
|
|
56648
|
+
"erpnext_profit_loss": "Show submitted sales and purchase orders for month {label}"
|
|
56649
|
+
};
|
|
56650
|
+
var INVOICE_ITEM_HINTS = [
|
|
56651
|
+
{
|
|
56652
|
+
key: "item",
|
|
56653
|
+
label: "Item",
|
|
56654
|
+
message: "Show item {item}",
|
|
56655
|
+
tool: "erpnext_item_get",
|
|
56656
|
+
args: { name: "{item}" },
|
|
56657
|
+
kind: "record"
|
|
56658
|
+
},
|
|
56659
|
+
{
|
|
56660
|
+
key: "stock",
|
|
56661
|
+
label: "Stock",
|
|
56662
|
+
message: "Show stock balance for item {item}",
|
|
56663
|
+
tool: "erpnext_stock_balance",
|
|
56664
|
+
args: { item_code: "{item}", limit: 50 },
|
|
56665
|
+
kind: "list"
|
|
56666
|
+
}
|
|
56667
|
+
];
|
|
56668
|
+
var INVOICE_HINTS = {
|
|
56669
|
+
"Sales Invoice": [
|
|
56670
|
+
{
|
|
56671
|
+
key: "payments",
|
|
56672
|
+
label: "Payments",
|
|
56673
|
+
message: "Show payment entries for invoice {id}",
|
|
56674
|
+
tool: "erpnext_doc_list",
|
|
56675
|
+
args: {
|
|
56676
|
+
doctype: "Payment Entry",
|
|
56677
|
+
fields: [
|
|
56678
|
+
"name",
|
|
56679
|
+
"posting_date",
|
|
56680
|
+
"paid_amount",
|
|
56681
|
+
"mode_of_payment",
|
|
56682
|
+
"docstatus"
|
|
56683
|
+
],
|
|
56684
|
+
filters: [["Payment Entry Reference", "reference_name", "=", "{id}"]],
|
|
56685
|
+
limit: 20
|
|
56686
|
+
},
|
|
56687
|
+
kind: "list"
|
|
56688
|
+
},
|
|
56689
|
+
{
|
|
56690
|
+
key: "customer",
|
|
56691
|
+
label: "Customer",
|
|
56692
|
+
message: "Show customer {party}",
|
|
56693
|
+
tool: "erpnext_customer_get",
|
|
56694
|
+
args: { name: "{party}" },
|
|
56695
|
+
kind: "record"
|
|
56696
|
+
},
|
|
56697
|
+
...INVOICE_ITEM_HINTS
|
|
56698
|
+
],
|
|
56699
|
+
"Purchase Invoice": [
|
|
56700
|
+
{
|
|
56701
|
+
key: "payments",
|
|
56702
|
+
label: "Payments",
|
|
56703
|
+
message: "Show payment entries for invoice {id}",
|
|
56704
|
+
tool: "erpnext_doc_list",
|
|
56705
|
+
args: {
|
|
56706
|
+
doctype: "Payment Entry",
|
|
56707
|
+
fields: [
|
|
56708
|
+
"name",
|
|
56709
|
+
"posting_date",
|
|
56710
|
+
"paid_amount",
|
|
56711
|
+
"mode_of_payment",
|
|
56712
|
+
"docstatus"
|
|
56713
|
+
],
|
|
56714
|
+
filters: [["Payment Entry Reference", "reference_name", "=", "{id}"]],
|
|
56715
|
+
limit: 20
|
|
56716
|
+
},
|
|
56717
|
+
kind: "list"
|
|
56718
|
+
},
|
|
56719
|
+
{
|
|
56720
|
+
key: "supplier",
|
|
56721
|
+
label: "Supplier",
|
|
56722
|
+
message: "Show supplier {party}",
|
|
56723
|
+
tool: "erpnext_supplier_get",
|
|
56724
|
+
args: { name: "{party}" },
|
|
56725
|
+
kind: "record"
|
|
56726
|
+
},
|
|
56727
|
+
...INVOICE_ITEM_HINTS
|
|
56728
|
+
]
|
|
56729
|
+
};
|
|
56730
|
+
var STOCK_HINTS = [
|
|
56731
|
+
{
|
|
56732
|
+
key: "item",
|
|
56733
|
+
label: "Item",
|
|
56734
|
+
message: "Show item {id}",
|
|
56735
|
+
tool: "erpnext_item_get",
|
|
56736
|
+
args: { name: "{id}" },
|
|
56737
|
+
kind: "record"
|
|
56738
|
+
},
|
|
56739
|
+
{
|
|
56740
|
+
key: "movements",
|
|
56741
|
+
label: "Stock entries",
|
|
56742
|
+
message: "Show stock entries for item {id}",
|
|
56743
|
+
tool: "erpnext_doc_list",
|
|
56744
|
+
args: {
|
|
56745
|
+
doctype: "Stock Entry",
|
|
56746
|
+
fields: ["name", "posting_date", "stock_entry_type", "docstatus"],
|
|
56747
|
+
filters: [["Stock Entry Detail", "item_code", "=", "{id}"]],
|
|
56748
|
+
limit: 20
|
|
56749
|
+
},
|
|
56750
|
+
kind: "list"
|
|
56751
|
+
},
|
|
56752
|
+
{
|
|
56753
|
+
key: "warehouse",
|
|
56754
|
+
label: "Warehouse stock",
|
|
56755
|
+
message: "Show stock chart for warehouse {warehouse}",
|
|
56756
|
+
tool: "erpnext_stock_chart",
|
|
56757
|
+
args: { warehouse: "{warehouse}", limit: 10 },
|
|
56758
|
+
kind: "chart"
|
|
56759
|
+
}
|
|
56760
|
+
];
|
|
56761
|
+
function monthRange(now) {
|
|
56762
|
+
const y = now.getFullYear();
|
|
56763
|
+
const m = now.getMonth();
|
|
56764
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
56765
|
+
const last = new Date(y, m + 1, 0).getDate();
|
|
56766
|
+
return {
|
|
56767
|
+
from: `${y}-${pad(m + 1)}-01`,
|
|
56768
|
+
to: `${y}-${pad(m + 1)}-${pad(last)}`,
|
|
56769
|
+
today: `${y}-${pad(m + 1)}-${pad(now.getDate())}`
|
|
56770
|
+
};
|
|
56771
|
+
}
|
|
56772
|
+
var INVOICE_LIST_FIELDS = [
|
|
56773
|
+
"name",
|
|
56774
|
+
"customer",
|
|
56775
|
+
"posting_date",
|
|
56776
|
+
"due_date",
|
|
56777
|
+
"status",
|
|
56778
|
+
"outstanding_amount"
|
|
56779
|
+
];
|
|
56780
|
+
var KPI_JUMPS = {
|
|
56781
|
+
"erpnext_kpi_revenue": (r) => ({
|
|
56782
|
+
number: {
|
|
56783
|
+
key: "sales_orders_month",
|
|
56784
|
+
label: "Sales orders this month",
|
|
56785
|
+
tool: "erpnext_doc_list",
|
|
56786
|
+
args: {
|
|
56787
|
+
doctype: "Sales Order",
|
|
56788
|
+
fields: [
|
|
56789
|
+
"name",
|
|
56790
|
+
"customer",
|
|
56791
|
+
"transaction_date",
|
|
56792
|
+
"status",
|
|
56793
|
+
"grand_total"
|
|
56794
|
+
],
|
|
56795
|
+
filters: [
|
|
56796
|
+
["transaction_date", ">=", r.from],
|
|
56797
|
+
["transaction_date", "<=", r.to],
|
|
56798
|
+
// comme le KPI : les commandes annulées ne comptent pas
|
|
56799
|
+
["docstatus", "<", 2]
|
|
56800
|
+
],
|
|
56801
|
+
limit: 20
|
|
56802
|
+
},
|
|
56803
|
+
kind: "list"
|
|
56804
|
+
},
|
|
56805
|
+
trend: {
|
|
56806
|
+
key: "revenue_trend",
|
|
56807
|
+
label: "Revenue trend",
|
|
56808
|
+
tool: "erpnext_revenue_trend",
|
|
56809
|
+
args: { months: 12 },
|
|
56810
|
+
kind: "chart"
|
|
56811
|
+
}
|
|
56812
|
+
}),
|
|
56813
|
+
"erpnext_kpi_outstanding": () => ({
|
|
56814
|
+
number: {
|
|
56815
|
+
key: "unpaid_invoices",
|
|
56816
|
+
label: "Unpaid invoices",
|
|
56817
|
+
tool: "erpnext_doc_list",
|
|
56818
|
+
args: {
|
|
56819
|
+
doctype: "Sales Invoice",
|
|
56820
|
+
fields: INVOICE_LIST_FIELDS,
|
|
56821
|
+
filters: [["outstanding_amount", ">", 0], ["docstatus", "=", 1]],
|
|
56822
|
+
limit: 20
|
|
56823
|
+
},
|
|
56824
|
+
kind: "list"
|
|
56825
|
+
},
|
|
56826
|
+
trend: {
|
|
56827
|
+
key: "receivables_aging",
|
|
56828
|
+
label: "Receivables aging",
|
|
56829
|
+
tool: "erpnext_ar_aging",
|
|
56830
|
+
args: {},
|
|
56831
|
+
kind: "chart"
|
|
56832
|
+
}
|
|
56833
|
+
}),
|
|
56834
|
+
"erpnext_kpi_orders": (r) => ({
|
|
56835
|
+
number: {
|
|
56836
|
+
key: "sales_orders_month",
|
|
56837
|
+
label: "Sales orders this month",
|
|
56838
|
+
tool: "erpnext_doc_list",
|
|
56839
|
+
args: {
|
|
56840
|
+
doctype: "Sales Order",
|
|
56841
|
+
fields: [
|
|
56842
|
+
"name",
|
|
56843
|
+
"customer",
|
|
56844
|
+
"transaction_date",
|
|
56845
|
+
"status",
|
|
56846
|
+
"grand_total"
|
|
56847
|
+
],
|
|
56848
|
+
filters: [
|
|
56849
|
+
["transaction_date", ">=", r.from],
|
|
56850
|
+
["transaction_date", "<=", r.to],
|
|
56851
|
+
// comme le KPI : les commandes annulées ne comptent pas
|
|
56852
|
+
["docstatus", "<", 2]
|
|
56853
|
+
],
|
|
56854
|
+
limit: 20
|
|
56855
|
+
},
|
|
56856
|
+
kind: "list"
|
|
56857
|
+
},
|
|
56858
|
+
trend: {
|
|
56859
|
+
key: "order_breakdown",
|
|
56860
|
+
label: "Order breakdown",
|
|
56861
|
+
tool: "erpnext_order_breakdown",
|
|
56862
|
+
args: {},
|
|
56863
|
+
kind: "chart"
|
|
56864
|
+
}
|
|
56865
|
+
}),
|
|
56866
|
+
"erpnext_kpi_gross_margin": () => ({
|
|
56867
|
+
trend: {
|
|
56868
|
+
key: "profit_loss",
|
|
56869
|
+
label: "Profit and loss",
|
|
56870
|
+
tool: "erpnext_profit_loss",
|
|
56871
|
+
args: { months: 12 },
|
|
56872
|
+
kind: "chart"
|
|
56873
|
+
}
|
|
56874
|
+
}),
|
|
56875
|
+
"erpnext_kpi_overdue": (r) => ({
|
|
56876
|
+
number: {
|
|
56877
|
+
key: "overdue_invoices",
|
|
56878
|
+
label: "Overdue invoices",
|
|
56879
|
+
tool: "erpnext_doc_list",
|
|
56880
|
+
args: {
|
|
56881
|
+
doctype: "Sales Invoice",
|
|
56882
|
+
fields: INVOICE_LIST_FIELDS,
|
|
56883
|
+
filters: [
|
|
56884
|
+
["due_date", "<", r.today],
|
|
56885
|
+
["outstanding_amount", ">", 0],
|
|
56886
|
+
["docstatus", "=", 1]
|
|
56887
|
+
],
|
|
56888
|
+
limit: 20
|
|
56889
|
+
},
|
|
56890
|
+
kind: "list"
|
|
56891
|
+
},
|
|
56892
|
+
trend: {
|
|
56893
|
+
key: "receivables_aging",
|
|
56894
|
+
label: "Receivables aging",
|
|
56895
|
+
tool: "erpnext_ar_aging",
|
|
56896
|
+
args: {},
|
|
56897
|
+
kind: "chart"
|
|
56898
|
+
}
|
|
56899
|
+
})
|
|
56900
|
+
};
|
|
56901
|
+
var FUNNEL_LIST_FIELDS = {
|
|
56902
|
+
"Leads": [
|
|
56903
|
+
"name",
|
|
56904
|
+
"lead_name",
|
|
56905
|
+
"company_name",
|
|
56906
|
+
"status",
|
|
56907
|
+
"lead_owner",
|
|
56908
|
+
"creation"
|
|
56909
|
+
],
|
|
56910
|
+
"Opportunities": [
|
|
56911
|
+
"name",
|
|
56912
|
+
"opportunity_from",
|
|
56913
|
+
"party_name",
|
|
56914
|
+
"status",
|
|
56915
|
+
"opportunity_amount",
|
|
56916
|
+
"currency",
|
|
56917
|
+
"probability",
|
|
56918
|
+
"opportunity_owner",
|
|
56919
|
+
"transaction_date"
|
|
56920
|
+
],
|
|
56921
|
+
"Quotations": [
|
|
56922
|
+
"name",
|
|
56923
|
+
"party_name",
|
|
56924
|
+
"transaction_date",
|
|
56925
|
+
"status",
|
|
56926
|
+
"grand_total"
|
|
56927
|
+
],
|
|
56928
|
+
"Orders": [
|
|
56929
|
+
"name",
|
|
56930
|
+
"customer",
|
|
56931
|
+
"transaction_date",
|
|
56932
|
+
"status",
|
|
56933
|
+
"grand_total"
|
|
56934
|
+
]
|
|
56935
|
+
};
|
|
56936
|
+
var FUNNEL_STAGE_JUMPS = {
|
|
56937
|
+
"Leads": {
|
|
56938
|
+
key: "leads",
|
|
56939
|
+
label: "Leads",
|
|
56940
|
+
tool: "erpnext_lead_list",
|
|
56941
|
+
args: { limit: 20 },
|
|
56942
|
+
kind: "list"
|
|
56943
|
+
},
|
|
56944
|
+
"Opportunities": {
|
|
56945
|
+
key: "opportunities",
|
|
56946
|
+
label: "Opportunities",
|
|
56947
|
+
tool: "erpnext_opportunity_list",
|
|
56948
|
+
args: { limit: 20 },
|
|
56949
|
+
kind: "list"
|
|
56950
|
+
},
|
|
56951
|
+
"Quotations": {
|
|
56952
|
+
key: "quotations",
|
|
56953
|
+
label: "Quotations",
|
|
56954
|
+
tool: "erpnext_doc_list",
|
|
56955
|
+
args: {
|
|
56956
|
+
doctype: "Quotation",
|
|
56957
|
+
fields: FUNNEL_LIST_FIELDS.Quotations,
|
|
56958
|
+
filters: [["docstatus", "!=", 2]],
|
|
56959
|
+
limit: 20
|
|
56960
|
+
},
|
|
56961
|
+
kind: "list"
|
|
56962
|
+
},
|
|
56963
|
+
"Orders": {
|
|
56964
|
+
key: "sales_orders",
|
|
56965
|
+
label: "Sales orders",
|
|
56966
|
+
tool: "erpnext_doc_list",
|
|
56967
|
+
args: {
|
|
56968
|
+
doctype: "Sales Order",
|
|
56969
|
+
fields: FUNNEL_LIST_FIELDS.Orders,
|
|
56970
|
+
filters: [["docstatus", "!=", 2]],
|
|
56971
|
+
limit: 20
|
|
56972
|
+
},
|
|
56973
|
+
kind: "list"
|
|
56974
|
+
}
|
|
55686
56975
|
};
|
|
56976
|
+
function funnelPeriodStart(period, now) {
|
|
56977
|
+
const year = now.getFullYear();
|
|
56978
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
56979
|
+
if (period === "this_month") {
|
|
56980
|
+
return `${year}-${pad(now.getMonth() + 1)}-01`;
|
|
56981
|
+
}
|
|
56982
|
+
if (period === "this_quarter") {
|
|
56983
|
+
const firstMonth = Math.floor(now.getMonth() / 3) * 3 + 1;
|
|
56984
|
+
return `${year}-${pad(firstMonth)}-01`;
|
|
56985
|
+
}
|
|
56986
|
+
if (period === "this_year") return `${year}-01-01`;
|
|
56987
|
+
return void 0;
|
|
56988
|
+
}
|
|
56989
|
+
function funnelStageJumps(args, now) {
|
|
56990
|
+
const since = funnelPeriodStart(args.period, now);
|
|
56991
|
+
if (!since) return FUNNEL_STAGE_JUMPS;
|
|
56992
|
+
const scopedJump = (stage, doctype, dateField) => {
|
|
56993
|
+
const base = FUNNEL_STAGE_JUMPS[stage];
|
|
56994
|
+
const baseFilters = Array.isArray(base.args.filters) ? base.args.filters : [];
|
|
56995
|
+
return {
|
|
56996
|
+
...base,
|
|
56997
|
+
tool: "erpnext_doc_list",
|
|
56998
|
+
args: {
|
|
56999
|
+
doctype,
|
|
57000
|
+
fields: FUNNEL_LIST_FIELDS[stage],
|
|
57001
|
+
filters: [[dateField, ">=", since], ...baseFilters],
|
|
57002
|
+
limit: 20
|
|
57003
|
+
}
|
|
57004
|
+
};
|
|
57005
|
+
};
|
|
57006
|
+
return {
|
|
57007
|
+
"Leads": scopedJump("Leads", "Lead", "creation"),
|
|
57008
|
+
"Opportunities": scopedJump(
|
|
57009
|
+
"Opportunities",
|
|
57010
|
+
"Opportunity",
|
|
57011
|
+
"transaction_date"
|
|
57012
|
+
),
|
|
57013
|
+
"Quotations": scopedJump(
|
|
57014
|
+
"Quotations",
|
|
57015
|
+
"Quotation",
|
|
57016
|
+
"transaction_date"
|
|
57017
|
+
),
|
|
57018
|
+
"Orders": scopedJump("Orders", "Sales Order", "transaction_date")
|
|
57019
|
+
};
|
|
57020
|
+
}
|
|
57021
|
+
var SALES_ORDER_LIST_FIELDS = [
|
|
57022
|
+
"name",
|
|
57023
|
+
"customer",
|
|
57024
|
+
"transaction_date",
|
|
57025
|
+
"status",
|
|
57026
|
+
"grand_total"
|
|
57027
|
+
];
|
|
57028
|
+
var PURCHASE_ORDER_LIST_FIELDS = [
|
|
57029
|
+
"name",
|
|
57030
|
+
"supplier",
|
|
57031
|
+
"transaction_date",
|
|
57032
|
+
"status",
|
|
57033
|
+
"grand_total"
|
|
57034
|
+
];
|
|
57035
|
+
function monthLabel(d) {
|
|
57036
|
+
return `${d.toLocaleString("en", { month: "short" })} ${d.getFullYear().toString().slice(2)}`;
|
|
57037
|
+
}
|
|
57038
|
+
function monthBuckets(monthsBack, now) {
|
|
57039
|
+
const buckets = [];
|
|
57040
|
+
for (let m = 0; m < monthsBack; m++) {
|
|
57041
|
+
const d = new Date(
|
|
57042
|
+
now.getFullYear(),
|
|
57043
|
+
now.getMonth() - monthsBack + 1 + m,
|
|
57044
|
+
1
|
|
57045
|
+
);
|
|
57046
|
+
buckets.push({ label: monthLabel(d), range: monthRange(d) });
|
|
57047
|
+
}
|
|
57048
|
+
return buckets;
|
|
57049
|
+
}
|
|
57050
|
+
var docList = (label, doctype, fields, filters) => ({
|
|
57051
|
+
label,
|
|
57052
|
+
tool: "erpnext_doc_list",
|
|
57053
|
+
args: { doctype, fields, filters, limit: 20 },
|
|
57054
|
+
kind: "list"
|
|
57055
|
+
});
|
|
57056
|
+
function chartPointJumps(toolName, args, labels, now) {
|
|
57057
|
+
const jumps = {};
|
|
57058
|
+
const byLabel = (make) => {
|
|
57059
|
+
for (const label of labels) {
|
|
57060
|
+
if (label === "Unknown") continue;
|
|
57061
|
+
jumps[label] = make(label);
|
|
57062
|
+
}
|
|
57063
|
+
};
|
|
57064
|
+
switch (toolName) {
|
|
57065
|
+
case "erpnext_revenue_trend": {
|
|
57066
|
+
const months = Number(args.months ?? 6);
|
|
57067
|
+
for (const { label, range } of monthBuckets(months, now)) {
|
|
57068
|
+
if (!labels.includes(label)) continue;
|
|
57069
|
+
jumps[label] = docList(label, "Sales Order", SALES_ORDER_LIST_FIELDS, [
|
|
57070
|
+
["transaction_date", ">=", range.from],
|
|
57071
|
+
["transaction_date", "<=", range.to],
|
|
57072
|
+
["docstatus", "<", 2]
|
|
57073
|
+
]);
|
|
57074
|
+
}
|
|
57075
|
+
break;
|
|
57076
|
+
}
|
|
57077
|
+
case "erpnext_sales_chart": {
|
|
57078
|
+
const groupBy = String(args.group_by ?? "customer");
|
|
57079
|
+
byLabel((label) => {
|
|
57080
|
+
const filters = [
|
|
57081
|
+
groupBy === "item" ? ["Sales Invoice Item", "item_name", "=", label] : groupBy === "status" ? ["status", "=", label] : ["customer_name", "=", label]
|
|
57082
|
+
];
|
|
57083
|
+
if (groupBy === "status") {
|
|
57084
|
+
filters.push(["docstatus", "!=", 2]);
|
|
57085
|
+
} else if (groupBy === "item" || args.include_drafts !== true) {
|
|
57086
|
+
filters.push(["docstatus", "=", 1]);
|
|
57087
|
+
}
|
|
57088
|
+
return docList(label, "Sales Invoice", INVOICE_LIST_FIELDS, filters);
|
|
57089
|
+
});
|
|
57090
|
+
break;
|
|
57091
|
+
}
|
|
57092
|
+
case "erpnext_order_breakdown":
|
|
57093
|
+
case "erpnext_revenue_vs_orders":
|
|
57094
|
+
byLabel(
|
|
57095
|
+
(label) => docList(label, "Sales Order", SALES_ORDER_LIST_FIELDS, [
|
|
57096
|
+
["customer_name", "=", label],
|
|
57097
|
+
["docstatus", "<", 2]
|
|
57098
|
+
])
|
|
57099
|
+
);
|
|
57100
|
+
break;
|
|
57101
|
+
case "erpnext_ar_aging":
|
|
57102
|
+
case "erpnext_profit_loss":
|
|
57103
|
+
return void 0;
|
|
57104
|
+
case "erpnext_gross_profit": {
|
|
57105
|
+
const groupBy = String(args.group_by ?? "item");
|
|
57106
|
+
byLabel(
|
|
57107
|
+
(label) => docList(label, "Sales Invoice", INVOICE_LIST_FIELDS, [
|
|
57108
|
+
groupBy === "customer" ? ["customer_name", "=", label] : ["Sales Invoice Item", "item_name", "=", label],
|
|
57109
|
+
["docstatus", "=", 1]
|
|
57110
|
+
])
|
|
57111
|
+
);
|
|
57112
|
+
break;
|
|
57113
|
+
}
|
|
57114
|
+
case "erpnext_stock_chart":
|
|
57115
|
+
byLabel((label) => ({
|
|
57116
|
+
label,
|
|
57117
|
+
tool: "erpnext_stock_balance",
|
|
57118
|
+
args: {
|
|
57119
|
+
item_code: label,
|
|
57120
|
+
...typeof args.warehouse === "string" ? { warehouse: args.warehouse } : {},
|
|
57121
|
+
limit: 50
|
|
57122
|
+
},
|
|
57123
|
+
kind: "list"
|
|
57124
|
+
}));
|
|
57125
|
+
break;
|
|
57126
|
+
default:
|
|
57127
|
+
return void 0;
|
|
57128
|
+
}
|
|
57129
|
+
return Object.keys(jumps).length > 0 ? jumps : void 0;
|
|
57130
|
+
}
|
|
57131
|
+
function chartSeriesPointJumps(toolName, args, labels, now) {
|
|
57132
|
+
if (toolName !== "erpnext_profit_loss") return void 0;
|
|
57133
|
+
const jumps = {};
|
|
57134
|
+
const months = Number(args.months ?? 6);
|
|
57135
|
+
for (const { label, range } of monthBuckets(months, now)) {
|
|
57136
|
+
if (!labels.includes(label)) continue;
|
|
57137
|
+
const filters = [
|
|
57138
|
+
["transaction_date", ">=", range.from],
|
|
57139
|
+
["transaction_date", "<=", range.to],
|
|
57140
|
+
["docstatus", "=", 1]
|
|
57141
|
+
];
|
|
57142
|
+
jumps[label] = {
|
|
57143
|
+
"Income": docList(
|
|
57144
|
+
`${label} \xB7 Income`,
|
|
57145
|
+
"Sales Order",
|
|
57146
|
+
SALES_ORDER_LIST_FIELDS,
|
|
57147
|
+
filters
|
|
57148
|
+
),
|
|
57149
|
+
"Expenses": docList(
|
|
57150
|
+
`${label} \xB7 Expenses`,
|
|
57151
|
+
"Purchase Order",
|
|
57152
|
+
PURCHASE_ORDER_LIST_FIELDS,
|
|
57153
|
+
filters
|
|
57154
|
+
)
|
|
57155
|
+
};
|
|
57156
|
+
}
|
|
57157
|
+
return Object.keys(jumps).length > 0 ? jumps : void 0;
|
|
57158
|
+
}
|
|
55687
57159
|
function isChartViewer(result) {
|
|
55688
57160
|
const uri = result._meta?.ui?.resourceUri;
|
|
55689
57161
|
return uri === "ui://mcp-erpnext/chart-viewer";
|
|
@@ -55692,6 +57164,28 @@ function isKpiViewer(result) {
|
|
|
55692
57164
|
const uri = result._meta?.ui?.resourceUri;
|
|
55693
57165
|
return uri === "ui://mcp-erpnext/kpi-viewer";
|
|
55694
57166
|
}
|
|
57167
|
+
function isFunnelViewer(result) {
|
|
57168
|
+
const uri = result._meta?.ui?.resourceUri;
|
|
57169
|
+
return uri === "ui://mcp-erpnext/funnel-viewer";
|
|
57170
|
+
}
|
|
57171
|
+
function resultDoctype(result) {
|
|
57172
|
+
if (typeof result.doctype === "string") return result.doctype;
|
|
57173
|
+
const data = result.data;
|
|
57174
|
+
if (isRecord7(data) && typeof data.doctype === "string") return data.doctype;
|
|
57175
|
+
return void 0;
|
|
57176
|
+
}
|
|
57177
|
+
function isInvoiceViewer(result) {
|
|
57178
|
+
const uri = result._meta?.ui?.resourceUri;
|
|
57179
|
+
return uri === "ui://mcp-erpnext/invoice-viewer";
|
|
57180
|
+
}
|
|
57181
|
+
function isStockViewer(result) {
|
|
57182
|
+
const uri = result._meta?.ui?.resourceUri;
|
|
57183
|
+
return uri === "ui://mcp-erpnext/stock-viewer";
|
|
57184
|
+
}
|
|
57185
|
+
function isKanbanViewer(result) {
|
|
57186
|
+
const uri = result._meta?.ui?.resourceUri;
|
|
57187
|
+
return uri === "ui://mcp-erpnext/kanban-viewer";
|
|
57188
|
+
}
|
|
55695
57189
|
function isDoclistResult(result) {
|
|
55696
57190
|
return typeof result.doctype === "string" && Array.isArray(result.data);
|
|
55697
57191
|
}
|
|
@@ -55699,7 +57193,193 @@ function isDoclistViewer(result) {
|
|
|
55699
57193
|
const uri = result._meta?.ui?.resourceUri;
|
|
55700
57194
|
return uri === "ui://mcp-erpnext/doclist-viewer";
|
|
55701
57195
|
}
|
|
55702
|
-
function
|
|
57196
|
+
function isDocViewer(result) {
|
|
57197
|
+
const uri = result._meta?.ui?.resourceUri;
|
|
57198
|
+
return uri === "ui://mcp-erpnext/doc-viewer";
|
|
57199
|
+
}
|
|
57200
|
+
var VIEWER_TOOL_CANDIDATES = {
|
|
57201
|
+
"ui://mcp-erpnext/invoice-viewer": [
|
|
57202
|
+
"erpnext_item_get",
|
|
57203
|
+
"erpnext_stock_balance",
|
|
57204
|
+
"erpnext_file_list",
|
|
57205
|
+
"erpnext_file_upload",
|
|
57206
|
+
"erpnext_file_download"
|
|
57207
|
+
],
|
|
57208
|
+
"ui://mcp-erpnext/doclist-viewer": [],
|
|
57209
|
+
"ui://mcp-erpnext/doc-viewer": [
|
|
57210
|
+
"erpnext_file_list",
|
|
57211
|
+
"erpnext_file_upload",
|
|
57212
|
+
"erpnext_file_download"
|
|
57213
|
+
],
|
|
57214
|
+
"ui://mcp-erpnext/kanban-viewer": [
|
|
57215
|
+
"erpnext_doc_get",
|
|
57216
|
+
"erpnext_doc_update",
|
|
57217
|
+
"erpnext_user_list",
|
|
57218
|
+
"erpnext_doc_assign",
|
|
57219
|
+
"erpnext_doc_unassign"
|
|
57220
|
+
],
|
|
57221
|
+
"ui://mcp-erpnext/stock-viewer": [
|
|
57222
|
+
"erpnext_item_get",
|
|
57223
|
+
"erpnext_stock_entry_list"
|
|
57224
|
+
]
|
|
57225
|
+
};
|
|
57226
|
+
var SUBMITTABLE_VIEWER_DOCTYPES = /* @__PURE__ */ new Set([
|
|
57227
|
+
"Quotation",
|
|
57228
|
+
"Sales Order",
|
|
57229
|
+
"Delivery Note",
|
|
57230
|
+
"Sales Invoice",
|
|
57231
|
+
"Purchase Order",
|
|
57232
|
+
"Purchase Receipt",
|
|
57233
|
+
"Purchase Invoice",
|
|
57234
|
+
"Stock Entry",
|
|
57235
|
+
"Journal Entry",
|
|
57236
|
+
"Payment Entry",
|
|
57237
|
+
"Timesheet",
|
|
57238
|
+
"Leave Application",
|
|
57239
|
+
"Salary Slip",
|
|
57240
|
+
"BOM",
|
|
57241
|
+
"Work Order",
|
|
57242
|
+
"Job Card",
|
|
57243
|
+
"Asset",
|
|
57244
|
+
"Asset Movement",
|
|
57245
|
+
"Shipment"
|
|
57246
|
+
]);
|
|
57247
|
+
var INVOICE_DEDICATED_MUTATION_TOOLS = {
|
|
57248
|
+
"Sales Order": [
|
|
57249
|
+
"erpnext_sales_order_submit",
|
|
57250
|
+
"erpnext_sales_order_cancel"
|
|
57251
|
+
],
|
|
57252
|
+
"Sales Invoice": ["erpnext_sales_invoice_submit"]
|
|
57253
|
+
};
|
|
57254
|
+
function addDoctypeMutationCandidates(target, uri, doctype) {
|
|
57255
|
+
if (!doctype || !SUBMITTABLE_VIEWER_DOCTYPES.has(doctype)) return;
|
|
57256
|
+
if (uri === "ui://mcp-erpnext/invoice-viewer" || uri === "ui://mcp-erpnext/doclist-viewer" || uri === "ui://mcp-erpnext/doc-viewer") {
|
|
57257
|
+
target.add("erpnext_doc_submit");
|
|
57258
|
+
target.add("erpnext_doc_cancel");
|
|
57259
|
+
}
|
|
57260
|
+
if (uri === "ui://mcp-erpnext/invoice-viewer") {
|
|
57261
|
+
for (const tool of INVOICE_DEDICATED_MUTATION_TOOLS[doctype] ?? []) {
|
|
57262
|
+
target.add(tool);
|
|
57263
|
+
}
|
|
57264
|
+
}
|
|
57265
|
+
}
|
|
57266
|
+
function addJumpTools(target, jumps) {
|
|
57267
|
+
if (!jumps) return;
|
|
57268
|
+
for (const jump of Object.values(jumps)) target.add(jump.tool);
|
|
57269
|
+
}
|
|
57270
|
+
function addSeriesJumpTools(target, jumps) {
|
|
57271
|
+
if (!jumps) return;
|
|
57272
|
+
for (const seriesJumps of Object.values(jumps)) {
|
|
57273
|
+
addJumpTools(target, seriesJumps);
|
|
57274
|
+
}
|
|
57275
|
+
}
|
|
57276
|
+
function availableViewerToolNames(result, availableToolNames) {
|
|
57277
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
57278
|
+
const uri = result._meta?.ui?.resourceUri;
|
|
57279
|
+
for (const name of uri ? VIEWER_TOOL_CANDIDATES[uri] ?? [] : []) {
|
|
57280
|
+
candidates.add(name);
|
|
57281
|
+
}
|
|
57282
|
+
addDoctypeMutationCandidates(candidates, uri, resultDoctype(result));
|
|
57283
|
+
if (result.refreshRequest) candidates.add(result.refreshRequest.toolName);
|
|
57284
|
+
if (result._rowAction) candidates.add(result._rowAction.toolName);
|
|
57285
|
+
for (const hint of result._sendMessageHints ?? []) {
|
|
57286
|
+
if (hint.tool) candidates.add(hint.tool);
|
|
57287
|
+
}
|
|
57288
|
+
addJumpTools(
|
|
57289
|
+
candidates,
|
|
57290
|
+
result._jumps ? Object.fromEntries(
|
|
57291
|
+
Object.entries(result._jumps).filter(
|
|
57292
|
+
(entry) => entry[1] !== void 0
|
|
57293
|
+
)
|
|
57294
|
+
) : void 0
|
|
57295
|
+
);
|
|
57296
|
+
addJumpTools(candidates, result._stageJumps);
|
|
57297
|
+
addJumpTools(candidates, result._pointJumps);
|
|
57298
|
+
addSeriesJumpTools(candidates, result._seriesPointJumps);
|
|
57299
|
+
if (typeof result.moveToolName === "string") {
|
|
57300
|
+
candidates.add(result.moveToolName);
|
|
57301
|
+
}
|
|
57302
|
+
return [...candidates].filter((name) => availableToolNames.has(name)).sort();
|
|
57303
|
+
}
|
|
57304
|
+
function filterJumpMap(jumps, availableToolNames) {
|
|
57305
|
+
const available = Object.fromEntries(
|
|
57306
|
+
Object.entries(jumps).filter(
|
|
57307
|
+
([, jump]) => availableToolNames.has(jump.tool)
|
|
57308
|
+
)
|
|
57309
|
+
);
|
|
57310
|
+
return Object.keys(available).length > 0 ? available : void 0;
|
|
57311
|
+
}
|
|
57312
|
+
function filterSeriesJumpMap(jumps, availableToolNames) {
|
|
57313
|
+
const available = {};
|
|
57314
|
+
for (const [label, seriesJumps] of Object.entries(jumps)) {
|
|
57315
|
+
const filtered = filterJumpMap(seriesJumps, availableToolNames);
|
|
57316
|
+
if (filtered) available[label] = filtered;
|
|
57317
|
+
}
|
|
57318
|
+
return Object.keys(available).length > 0 ? available : void 0;
|
|
57319
|
+
}
|
|
57320
|
+
function filterNavJumpsByAvailableTools(result, availableToolNames) {
|
|
57321
|
+
if (!availableToolNames) return result;
|
|
57322
|
+
const filtered = { ...result };
|
|
57323
|
+
if (result._jumps) {
|
|
57324
|
+
const jumps = filterJumpMap(
|
|
57325
|
+
Object.fromEntries(
|
|
57326
|
+
Object.entries(result._jumps).filter(
|
|
57327
|
+
(entry) => entry[1] !== void 0
|
|
57328
|
+
)
|
|
57329
|
+
),
|
|
57330
|
+
availableToolNames
|
|
57331
|
+
);
|
|
57332
|
+
if (jumps) filtered._jumps = jumps;
|
|
57333
|
+
else delete filtered._jumps;
|
|
57334
|
+
}
|
|
57335
|
+
if (result._stageJumps) {
|
|
57336
|
+
const jumps = filterJumpMap(result._stageJumps, availableToolNames);
|
|
57337
|
+
if (jumps) filtered._stageJumps = jumps;
|
|
57338
|
+
else delete filtered._stageJumps;
|
|
57339
|
+
}
|
|
57340
|
+
if (result._pointJumps) {
|
|
57341
|
+
const jumps = filterJumpMap(result._pointJumps, availableToolNames);
|
|
57342
|
+
if (jumps) filtered._pointJumps = jumps;
|
|
57343
|
+
else delete filtered._pointJumps;
|
|
57344
|
+
}
|
|
57345
|
+
if (result._seriesPointJumps) {
|
|
57346
|
+
const jumps = filterSeriesJumpMap(
|
|
57347
|
+
result._seriesPointJumps,
|
|
57348
|
+
availableToolNames
|
|
57349
|
+
);
|
|
57350
|
+
if (jumps) filtered._seriesPointJumps = jumps;
|
|
57351
|
+
else delete filtered._seriesPointJumps;
|
|
57352
|
+
}
|
|
57353
|
+
if (result._rowAction && !availableToolNames.has(result._rowAction.toolName)) {
|
|
57354
|
+
const doctype = resultDoctype(result);
|
|
57355
|
+
if (doctype && availableToolNames.has("erpnext_doc_get")) {
|
|
57356
|
+
filtered._rowAction = {
|
|
57357
|
+
toolName: "erpnext_doc_get",
|
|
57358
|
+
idField: "name",
|
|
57359
|
+
argName: "name",
|
|
57360
|
+
extraArgs: { doctype }
|
|
57361
|
+
};
|
|
57362
|
+
} else {
|
|
57363
|
+
delete filtered._rowAction;
|
|
57364
|
+
}
|
|
57365
|
+
}
|
|
57366
|
+
if (result._sendMessageHints) {
|
|
57367
|
+
filtered._sendMessageHints = result._sendMessageHints.map((hint) => {
|
|
57368
|
+
if (!hint.tool || availableToolNames.has(hint.tool)) return hint;
|
|
57369
|
+
return { key: hint.key, label: hint.label, message: hint.message };
|
|
57370
|
+
});
|
|
57371
|
+
}
|
|
57372
|
+
filtered._availableTools = availableViewerToolNames(
|
|
57373
|
+
filtered,
|
|
57374
|
+
availableToolNames
|
|
57375
|
+
);
|
|
57376
|
+
return filtered;
|
|
57377
|
+
}
|
|
57378
|
+
function withViewerToolCapabilities(result, availableToolNames) {
|
|
57379
|
+
if (!isRecord7(result) || !hasUiResource(result)) return result;
|
|
57380
|
+
return filterNavJumpsByAvailableTools(result, availableToolNames);
|
|
57381
|
+
}
|
|
57382
|
+
function withUiRefreshRequest(result, toolName, args, now = /* @__PURE__ */ new Date(), availableToolNames) {
|
|
55703
57383
|
if (!isRecord7(result) || !hasUiResource(result)) {
|
|
55704
57384
|
return result;
|
|
55705
57385
|
}
|
|
@@ -55749,10 +57429,92 @@ function withUiRefreshRequest(result, toolName, args) {
|
|
|
55749
57429
|
}
|
|
55750
57430
|
}
|
|
55751
57431
|
}
|
|
55752
|
-
|
|
57432
|
+
if (isKpiViewer(enriched) && !enriched._jumps) {
|
|
57433
|
+
const jumps = KPI_JUMPS[toolName];
|
|
57434
|
+
if (jumps) enriched._jumps = jumps(monthRange(now));
|
|
57435
|
+
}
|
|
57436
|
+
if (isFunnelViewer(enriched) && !enriched._stageJumps) {
|
|
57437
|
+
enriched._stageJumps = funnelStageJumps(args, now);
|
|
57438
|
+
}
|
|
57439
|
+
if (isChartViewer(enriched) && Array.isArray(enriched.labels)) {
|
|
57440
|
+
const labels = enriched.labels.map(String);
|
|
57441
|
+
if (!enriched._pointJumps) {
|
|
57442
|
+
const jumps = chartPointJumps(toolName, args, labels, now);
|
|
57443
|
+
if (jumps) enriched._pointJumps = jumps;
|
|
57444
|
+
}
|
|
57445
|
+
if (!enriched._seriesPointJumps) {
|
|
57446
|
+
const jumps = chartSeriesPointJumps(toolName, args, labels, now);
|
|
57447
|
+
if (jumps) enriched._seriesPointJumps = jumps;
|
|
57448
|
+
}
|
|
57449
|
+
}
|
|
57450
|
+
if (!enriched._sendMessageHints) {
|
|
57451
|
+
const doctype = resultDoctype(enriched);
|
|
57452
|
+
if (isInvoiceViewer(enriched) && doctype) {
|
|
57453
|
+
const hints = INVOICE_HINTS[doctype];
|
|
57454
|
+
if (hints) enriched._sendMessageHints = hints;
|
|
57455
|
+
} else if (isStockViewer(enriched)) {
|
|
57456
|
+
enriched._sendMessageHints = STOCK_HINTS;
|
|
57457
|
+
} else if ((isKanbanViewer(enriched) || isDocViewer(enriched)) && doctype) {
|
|
57458
|
+
const hints = DOCTYPE_SEND_MESSAGE_HINTS[doctype];
|
|
57459
|
+
if (hints) enriched._sendMessageHints = hints;
|
|
57460
|
+
}
|
|
57461
|
+
}
|
|
57462
|
+
return filterNavJumpsByAvailableTools(enriched, availableToolNames);
|
|
55753
57463
|
}
|
|
55754
57464
|
|
|
55755
57465
|
// src/client.ts
|
|
57466
|
+
function isRecord8(value) {
|
|
57467
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
57468
|
+
}
|
|
57469
|
+
function isSingleEmbeddedBlobResult(value) {
|
|
57470
|
+
if (!isRecord8(value) || !Array.isArray(value.content)) return false;
|
|
57471
|
+
if (Object.keys(value).some((key) => key !== "content")) return false;
|
|
57472
|
+
if (value.content.length !== 2) return false;
|
|
57473
|
+
const [summary, embedded] = value.content;
|
|
57474
|
+
if (!isRecord8(summary) || summary.type !== "text" || typeof summary.text !== "string") {
|
|
57475
|
+
return false;
|
|
57476
|
+
}
|
|
57477
|
+
if (!isRecord8(embedded) || embedded.type !== "resource") return false;
|
|
57478
|
+
if (!isRecord8(embedded.resource)) return false;
|
|
57479
|
+
const resource = embedded.resource;
|
|
57480
|
+
return typeof resource.uri === "string" && resource.uri.startsWith("file:///") && typeof resource.mimeType === "string" && typeof resource.blob === "string";
|
|
57481
|
+
}
|
|
57482
|
+
function withSafeUiRefresh(result, tool, args, availableToolNames) {
|
|
57483
|
+
const readOnly = tool.annotations?.readOnlyHint === true;
|
|
57484
|
+
if (!isRecord8(result)) {
|
|
57485
|
+
return result;
|
|
57486
|
+
}
|
|
57487
|
+
const resultMeta = isRecord8(result._meta) ? result._meta : {};
|
|
57488
|
+
const declaredUi = tool._meta?.ui;
|
|
57489
|
+
const viewerResult = declaredUi && !isRecord8(resultMeta.ui) ? {
|
|
57490
|
+
...result,
|
|
57491
|
+
_meta: { ...resultMeta, ...tool._meta, ui: declaredUi }
|
|
57492
|
+
} : result;
|
|
57493
|
+
const request = isRecord8(viewerResult.refreshRequest) ? viewerResult.refreshRequest : null;
|
|
57494
|
+
const target = request && typeof request.toolName === "string" ? getToolByName(request.toolName) : void 0;
|
|
57495
|
+
const hasSafeExplicitRefresh = request !== null && typeof request.toolName === "string" && availableToolNames.has(request.toolName) && isRecord8(request.arguments) && (request.toolName === tool.name ? readOnly : target?.annotations?.readOnlyHint === true);
|
|
57496
|
+
if (hasSafeExplicitRefresh) {
|
|
57497
|
+
return withUiRefreshRequest(
|
|
57498
|
+
viewerResult,
|
|
57499
|
+
tool.name,
|
|
57500
|
+
args,
|
|
57501
|
+
/* @__PURE__ */ new Date(),
|
|
57502
|
+
availableToolNames
|
|
57503
|
+
);
|
|
57504
|
+
}
|
|
57505
|
+
let sanitized = viewerResult;
|
|
57506
|
+
if ("refreshRequest" in viewerResult) {
|
|
57507
|
+
sanitized = { ...viewerResult };
|
|
57508
|
+
delete sanitized.refreshRequest;
|
|
57509
|
+
}
|
|
57510
|
+
return readOnly ? withUiRefreshRequest(
|
|
57511
|
+
sanitized,
|
|
57512
|
+
tool.name,
|
|
57513
|
+
args,
|
|
57514
|
+
/* @__PURE__ */ new Date(),
|
|
57515
|
+
availableToolNames
|
|
57516
|
+
) : withViewerToolCapabilities(sanitized, availableToolNames);
|
|
57517
|
+
}
|
|
55756
57518
|
var ErpNextToolsClient = class {
|
|
55757
57519
|
tools;
|
|
55758
57520
|
enableLinkDisambiguation;
|
|
@@ -55793,6 +57555,7 @@ var ErpNextToolsClient = class {
|
|
|
55793
57555
|
*/
|
|
55794
57556
|
buildHandlersMap() {
|
|
55795
57557
|
const handlers = /* @__PURE__ */ new Map();
|
|
57558
|
+
const availableToolNames = new Set(this.tools.map((tool) => tool.name));
|
|
55796
57559
|
for (const tool of this.tools) {
|
|
55797
57560
|
const toolMeta = tool._meta;
|
|
55798
57561
|
handlers.set(tool.name, async (args, mcpContext) => {
|
|
@@ -55815,10 +57578,14 @@ var ErpNextToolsClient = class {
|
|
|
55815
57578
|
if (execution.result !== null && typeof execution.result === "object" && !Array.isArray(execution.result) && execution.result.resultType === "input_required") {
|
|
55816
57579
|
return execution.result;
|
|
55817
57580
|
}
|
|
55818
|
-
|
|
57581
|
+
if (tool.name === "erpnext_file_download" && isSingleEmbeddedBlobResult(execution.result)) {
|
|
57582
|
+
return execution.result;
|
|
57583
|
+
}
|
|
57584
|
+
const result = withSafeUiRefresh(
|
|
55819
57585
|
execution.result,
|
|
55820
|
-
tool
|
|
55821
|
-
execution.args
|
|
57586
|
+
tool,
|
|
57587
|
+
execution.args,
|
|
57588
|
+
availableToolNames
|
|
55822
57589
|
);
|
|
55823
57590
|
const r = result !== null && typeof result === "object" && !Array.isArray(result) ? result : null;
|
|
55824
57591
|
const resultUi = r?._meta && typeof r._meta === "object" && r._meta.ui;
|
|
@@ -55845,7 +57612,15 @@ var ErpNextToolsClient = class {
|
|
|
55845
57612
|
}
|
|
55846
57613
|
const client = getFrappeClient();
|
|
55847
57614
|
const result = await tool.handler(args, { client });
|
|
55848
|
-
|
|
57615
|
+
if (tool.name === "erpnext_file_download" && isSingleEmbeddedBlobResult(result)) {
|
|
57616
|
+
return result;
|
|
57617
|
+
}
|
|
57618
|
+
return withSafeUiRefresh(
|
|
57619
|
+
result,
|
|
57620
|
+
tool,
|
|
57621
|
+
args,
|
|
57622
|
+
new Set(this.tools.map((candidate) => candidate.name))
|
|
57623
|
+
);
|
|
55849
57624
|
}
|
|
55850
57625
|
/** Get tool count */
|
|
55851
57626
|
get count() {
|
|
@@ -55859,6 +57634,7 @@ var UI_VIEWERS = [
|
|
|
55859
57634
|
"invoice-viewer",
|
|
55860
57635
|
"stock-viewer",
|
|
55861
57636
|
"doclist-viewer",
|
|
57637
|
+
"doc-viewer",
|
|
55862
57638
|
"chart-viewer",
|
|
55863
57639
|
"kpi-viewer",
|
|
55864
57640
|
"funnel-viewer",
|
|
@@ -56102,7 +57878,7 @@ async function main() {
|
|
|
56102
57878
|
);
|
|
56103
57879
|
const server = new McpApp({
|
|
56104
57880
|
name: "mcp-erpnext",
|
|
56105
|
-
version: "3.0.2",
|
|
57881
|
+
version: "3.1.0-beta.2",
|
|
56106
57882
|
transport: "stateless",
|
|
56107
57883
|
cache: {
|
|
56108
57884
|
ttlMs: 36e5,
|