splitwise-mcp 2.2.3 → 2.4.0
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -0
- package/dist/bundle.js +887 -222
- package/dist/client.js +20 -1
- package/dist/index.js +3 -1
- package/dist/project.js +184 -0
- package/dist/tools/_confirm.js +2 -2
- package/dist/tools/expenses.js +11 -8
- package/dist/tools/friends.js +10 -6
- package/dist/tools/groups.js +15 -10
- package/dist/tools/healthcheck.js +28 -0
- package/dist/tools/receipts.js +2 -2
- package/dist/tools/user.js +13 -8
- package/dist/tools/utilities.js +14 -9
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -3117,9 +3117,28 @@ var require_utils = __commonJS({
|
|
|
3117
3117
|
"use strict";
|
|
3118
3118
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
3119
3119
|
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);
|
|
3120
|
+
var isPort = RegExp.prototype.test.bind(/^\d*$/u);
|
|
3120
3121
|
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
3121
3122
|
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
3122
|
-
var isPathCharacter = RegExp.prototype.test.bind(/^[
|
|
3123
|
+
var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
|
|
3124
|
+
var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
|
|
3125
|
+
var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
|
|
3126
|
+
var BYTE_HEX = new Array(256);
|
|
3127
|
+
{
|
|
3128
|
+
const HEX_DIGITS = "0123456789ABCDEF";
|
|
3129
|
+
for (let i2 = 0; i2 < 256; i2++) {
|
|
3130
|
+
BYTE_HEX[i2] = "%" + HEX_DIGITS[i2 >> 4] + HEX_DIGITS[i2 & 15];
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
function percentEncodeNonAscii(cp) {
|
|
3134
|
+
if (cp < 2048) {
|
|
3135
|
+
return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
|
|
3136
|
+
}
|
|
3137
|
+
if (cp < 65536) {
|
|
3138
|
+
return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3139
|
+
}
|
|
3140
|
+
return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
3141
|
+
}
|
|
3123
3142
|
function stringArrayToHexStripped(input) {
|
|
3124
3143
|
let acc = "";
|
|
3125
3144
|
let code = 0;
|
|
@@ -3144,91 +3163,105 @@ var require_utils = __commonJS({
|
|
|
3144
3163
|
}
|
|
3145
3164
|
return acc;
|
|
3146
3165
|
}
|
|
3166
|
+
var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
|
|
3167
|
+
var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
|
|
3168
|
+
var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
|
|
3147
3169
|
var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
3148
|
-
function
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
if (hex3 !== "") {
|
|
3156
|
-
address.push(hex3);
|
|
3157
|
-
} else {
|
|
3158
|
-
output.error = true;
|
|
3159
|
-
return false;
|
|
3170
|
+
function isZoneIdentifier(zone) {
|
|
3171
|
+
if (zone.length === 0) return false;
|
|
3172
|
+
for (let i2 = 0; i2 < zone.length; i2++) {
|
|
3173
|
+
if (isZoneCharacter(zone[i2])) continue;
|
|
3174
|
+
if (zone[i2] === "%" && i2 + 2 < zone.length && isHexPair(zone.slice(i2 + 1, i2 + 3))) {
|
|
3175
|
+
i2 += 2;
|
|
3176
|
+
continue;
|
|
3160
3177
|
}
|
|
3161
|
-
|
|
3178
|
+
return false;
|
|
3162
3179
|
}
|
|
3163
3180
|
return true;
|
|
3164
3181
|
}
|
|
3165
|
-
function
|
|
3166
|
-
let
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
let
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
}
|
|
3178
|
-
if (cursor === ":") {
|
|
3179
|
-
if (endipv6Encountered === true) {
|
|
3180
|
-
endIpv6 = true;
|
|
3181
|
-
}
|
|
3182
|
-
if (!consume(buffer, address, output)) {
|
|
3183
|
-
break;
|
|
3182
|
+
function compressIPv6ZeroRun(hextets) {
|
|
3183
|
+
let bestStart = -1;
|
|
3184
|
+
let bestLength = 0;
|
|
3185
|
+
let runStart = -1;
|
|
3186
|
+
let runLength = 0;
|
|
3187
|
+
for (let i2 = 0; i2 < hextets.length; i2++) {
|
|
3188
|
+
if (hextets[i2] === "0") {
|
|
3189
|
+
if (runStart === -1) runStart = i2;
|
|
3190
|
+
runLength++;
|
|
3191
|
+
if (runLength > bestLength) {
|
|
3192
|
+
bestLength = runLength;
|
|
3193
|
+
bestStart = runStart;
|
|
3184
3194
|
}
|
|
3185
|
-
if (++tokenCount > 7) {
|
|
3186
|
-
output.error = true;
|
|
3187
|
-
break;
|
|
3188
|
-
}
|
|
3189
|
-
if (i2 > 0 && input[i2 - 1] === ":") {
|
|
3190
|
-
endipv6Encountered = true;
|
|
3191
|
-
}
|
|
3192
|
-
address.push(":");
|
|
3193
|
-
continue;
|
|
3194
|
-
} else if (cursor === "%") {
|
|
3195
|
-
if (!consume(buffer, address, output)) {
|
|
3196
|
-
break;
|
|
3197
|
-
}
|
|
3198
|
-
consume = consumeIsZone;
|
|
3199
3195
|
} else {
|
|
3200
|
-
|
|
3196
|
+
runStart = -1;
|
|
3197
|
+
runLength = 0;
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
if (bestLength < 2) return hextets.join(":");
|
|
3201
|
+
const head = hextets.slice(0, bestStart).join(":");
|
|
3202
|
+
const tail = hextets.slice(bestStart + bestLength).join(":");
|
|
3203
|
+
return head + "::" + tail;
|
|
3204
|
+
}
|
|
3205
|
+
function normalizeIPv6Address(input) {
|
|
3206
|
+
const compression = input.indexOf("::");
|
|
3207
|
+
if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
|
|
3208
|
+
const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
|
|
3209
|
+
const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
|
|
3210
|
+
if (compression !== -1) {
|
|
3211
|
+
if (left.length === 1 && left[0] === "") left.length = 0;
|
|
3212
|
+
if (right.length === 1 && right[0] === "") right.length = 0;
|
|
3213
|
+
}
|
|
3214
|
+
const parts = left.concat(right);
|
|
3215
|
+
let hextetCount = 0;
|
|
3216
|
+
for (let i2 = 0; i2 < parts.length; i2++) {
|
|
3217
|
+
const part = parts[i2];
|
|
3218
|
+
if (part === "") return void 0;
|
|
3219
|
+
if (part.indexOf(".") !== -1) {
|
|
3220
|
+
if (i2 !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
|
|
3221
|
+
hextetCount += 2;
|
|
3201
3222
|
continue;
|
|
3202
3223
|
}
|
|
3224
|
+
if (!isHextet(part)) return void 0;
|
|
3225
|
+
parts[i2] = parseInt(part, 16).toString(16);
|
|
3226
|
+
hextetCount++;
|
|
3203
3227
|
}
|
|
3204
|
-
if (
|
|
3205
|
-
if (
|
|
3206
|
-
|
|
3207
|
-
} else if (endIpv6) {
|
|
3208
|
-
address.push(buffer.join(""));
|
|
3209
|
-
} else {
|
|
3210
|
-
address.push(stringArrayToHexStripped(buffer));
|
|
3211
|
-
}
|
|
3228
|
+
if (compression === -1) {
|
|
3229
|
+
if (hextetCount !== 8) return void 0;
|
|
3230
|
+
return compressIPv6ZeroRun(parts);
|
|
3212
3231
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3232
|
+
if (hextetCount >= 8) return void 0;
|
|
3233
|
+
const expanded = parts.slice(0, left.length);
|
|
3234
|
+
for (let i2 = hextetCount; i2 < 8; i2++) expanded.push("0");
|
|
3235
|
+
for (let i2 = left.length; i2 < parts.length; i2++) expanded.push(parts[i2]);
|
|
3236
|
+
return compressIPv6ZeroRun(expanded);
|
|
3215
3237
|
}
|
|
3216
3238
|
function normalizeIPv6(host) {
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
}
|
|
3220
|
-
|
|
3221
|
-
if (
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3239
|
+
const bracketed = host[0] === "[" && host[host.length - 1] === "]";
|
|
3240
|
+
const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
|
|
3241
|
+
if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
|
|
3242
|
+
let input = bracketed ? host.slice(1, -1) : host;
|
|
3243
|
+
if (bracketed && isIPvFuture(input)) {
|
|
3244
|
+
input = input.toLowerCase();
|
|
3245
|
+
return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
|
|
3246
|
+
}
|
|
3247
|
+
if (findToken(input, ":") < 2) {
|
|
3248
|
+
return { host, isIPV6: false, error: bracketed };
|
|
3249
|
+
}
|
|
3250
|
+
let zoneIdentifier = "";
|
|
3251
|
+
const zoneSeparator = input.indexOf("%");
|
|
3252
|
+
if (zoneSeparator !== -1) {
|
|
3253
|
+
const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
|
|
3254
|
+
zoneIdentifier = input.slice(zoneSeparator + separatorLength);
|
|
3255
|
+
if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
|
|
3256
|
+
input = input.slice(0, zoneSeparator);
|
|
3257
|
+
}
|
|
3258
|
+
const address = normalizeIPv6Address(input);
|
|
3259
|
+
if (address === void 0) return { host, isIPV6: false, error: true };
|
|
3260
|
+
return {
|
|
3261
|
+
host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
|
|
3262
|
+
escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
|
|
3263
|
+
isIPV6: true
|
|
3264
|
+
};
|
|
3232
3265
|
}
|
|
3233
3266
|
function findToken(str, token) {
|
|
3234
3267
|
let ind = 0;
|
|
@@ -3347,7 +3380,8 @@ var require_utils = __commonJS({
|
|
|
3347
3380
|
function normalizePathEncoding(input) {
|
|
3348
3381
|
let output = "";
|
|
3349
3382
|
for (let i2 = 0; i2 < input.length; i2++) {
|
|
3350
|
-
|
|
3383
|
+
const ch2 = input[i2];
|
|
3384
|
+
if (ch2 === "%" && i2 + 2 < input.length) {
|
|
3351
3385
|
const hex3 = input.slice(i2 + 1, i2 + 3);
|
|
3352
3386
|
if (isHexPair(hex3)) {
|
|
3353
3387
|
const normalizedHex = hex3.toUpperCase();
|
|
@@ -3361,10 +3395,152 @@ var require_utils = __commonJS({
|
|
|
3361
3395
|
continue;
|
|
3362
3396
|
}
|
|
3363
3397
|
}
|
|
3364
|
-
if (isPathCharacter(
|
|
3365
|
-
output +=
|
|
3398
|
+
if (isPathCharacter(ch2)) {
|
|
3399
|
+
output += ch2;
|
|
3400
|
+
} else {
|
|
3401
|
+
const code = input.charCodeAt(i2);
|
|
3402
|
+
if (code < 128) {
|
|
3403
|
+
output += isEscapeSafe(code) ? ch2 : BYTE_HEX[code];
|
|
3404
|
+
} else if (code < 55296 || code > 57343) {
|
|
3405
|
+
output += percentEncodeNonAscii(code);
|
|
3406
|
+
} else if (code <= 56319 && i2 + 1 < input.length) {
|
|
3407
|
+
const low = input.charCodeAt(i2 + 1);
|
|
3408
|
+
if (low >= 56320 && low <= 57343) {
|
|
3409
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3410
|
+
i2++;
|
|
3411
|
+
} else {
|
|
3412
|
+
output += percentEncodeNonAscii(65533);
|
|
3413
|
+
}
|
|
3414
|
+
} else {
|
|
3415
|
+
output += percentEncodeNonAscii(65533);
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
return output;
|
|
3420
|
+
}
|
|
3421
|
+
function serializePathEncoding(input, pathNoScheme = false) {
|
|
3422
|
+
let output = "";
|
|
3423
|
+
let firstSegment = pathNoScheme && input[0] !== "/";
|
|
3424
|
+
for (let i2 = 0; i2 < input.length; i2++) {
|
|
3425
|
+
const ch2 = input[i2];
|
|
3426
|
+
if (ch2 === "%" && i2 + 2 < input.length) {
|
|
3427
|
+
const hex3 = input.slice(i2 + 1, i2 + 3);
|
|
3428
|
+
if (isHexPair(hex3)) {
|
|
3429
|
+
output += "%" + hex3.toUpperCase();
|
|
3430
|
+
i2 += 2;
|
|
3431
|
+
continue;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
if (ch2 === "/") {
|
|
3435
|
+
firstSegment = false;
|
|
3436
|
+
}
|
|
3437
|
+
if (isPathCharacter(ch2) && (ch2 !== ":" || !firstSegment)) {
|
|
3438
|
+
output += ch2;
|
|
3439
|
+
} else {
|
|
3440
|
+
const code = input.charCodeAt(i2);
|
|
3441
|
+
if (code < 128) {
|
|
3442
|
+
output += BYTE_HEX[code];
|
|
3443
|
+
} else if (code < 55296 || code > 57343) {
|
|
3444
|
+
output += percentEncodeNonAscii(code);
|
|
3445
|
+
} else if (code <= 56319 && i2 + 1 < input.length) {
|
|
3446
|
+
const low = input.charCodeAt(i2 + 1);
|
|
3447
|
+
if (low >= 56320 && low <= 57343) {
|
|
3448
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3449
|
+
i2++;
|
|
3450
|
+
} else {
|
|
3451
|
+
output += percentEncodeNonAscii(65533);
|
|
3452
|
+
}
|
|
3453
|
+
} else {
|
|
3454
|
+
output += percentEncodeNonAscii(65533);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
return output;
|
|
3459
|
+
}
|
|
3460
|
+
function encodeComponent(input, isAllowed) {
|
|
3461
|
+
let output = "";
|
|
3462
|
+
for (let i2 = 0; i2 < input.length; i2++) {
|
|
3463
|
+
const ch2 = input[i2];
|
|
3464
|
+
if (ch2 === "%" && i2 + 2 < input.length) {
|
|
3465
|
+
const hex3 = input.slice(i2 + 1, i2 + 3);
|
|
3466
|
+
if (isHexPair(hex3)) {
|
|
3467
|
+
output += "%" + hex3.toUpperCase();
|
|
3468
|
+
i2 += 2;
|
|
3469
|
+
continue;
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
if (isAllowed(ch2)) {
|
|
3473
|
+
output += ch2;
|
|
3474
|
+
} else {
|
|
3475
|
+
const code = input.charCodeAt(i2);
|
|
3476
|
+
if (code < 128) {
|
|
3477
|
+
output += BYTE_HEX[code];
|
|
3478
|
+
} else if (code < 55296 || code > 57343) {
|
|
3479
|
+
output += percentEncodeNonAscii(code);
|
|
3480
|
+
} else if (code <= 56319 && i2 + 1 < input.length) {
|
|
3481
|
+
const low = input.charCodeAt(i2 + 1);
|
|
3482
|
+
if (low >= 56320 && low <= 57343) {
|
|
3483
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3484
|
+
i2++;
|
|
3485
|
+
} else {
|
|
3486
|
+
output += percentEncodeNonAscii(65533);
|
|
3487
|
+
}
|
|
3488
|
+
} else {
|
|
3489
|
+
output += percentEncodeNonAscii(65533);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
return output;
|
|
3494
|
+
}
|
|
3495
|
+
function encodeUserinfo(input) {
|
|
3496
|
+
return encodeComponent(input, isUserinfoCharacter);
|
|
3497
|
+
}
|
|
3498
|
+
function encodeQuery(input) {
|
|
3499
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3500
|
+
}
|
|
3501
|
+
function encodeFragment(input) {
|
|
3502
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
3503
|
+
}
|
|
3504
|
+
function isEscapeSafe(cp) {
|
|
3505
|
+
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;
|
|
3506
|
+
}
|
|
3507
|
+
function normalizeQueryFragmentEncoding(input) {
|
|
3508
|
+
let output = "";
|
|
3509
|
+
for (let i2 = 0; i2 < input.length; i2++) {
|
|
3510
|
+
const ch2 = input[i2];
|
|
3511
|
+
if (ch2 === "%" && i2 + 2 < input.length) {
|
|
3512
|
+
const hex3 = input.slice(i2 + 1, i2 + 3);
|
|
3513
|
+
if (isHexPair(hex3)) {
|
|
3514
|
+
const normalizedHex = hex3.toUpperCase();
|
|
3515
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3516
|
+
if (isUnreserved(decoded)) {
|
|
3517
|
+
output += decoded;
|
|
3518
|
+
} else {
|
|
3519
|
+
output += "%" + normalizedHex;
|
|
3520
|
+
}
|
|
3521
|
+
i2 += 2;
|
|
3522
|
+
continue;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
if (isQueryFragmentCharacter(ch2)) {
|
|
3526
|
+
output += ch2;
|
|
3366
3527
|
} else {
|
|
3367
|
-
|
|
3528
|
+
const code = input.charCodeAt(i2);
|
|
3529
|
+
if (code < 128) {
|
|
3530
|
+
output += isEscapeSafe(code) ? ch2 : BYTE_HEX[code];
|
|
3531
|
+
} else if (code < 55296 || code > 57343) {
|
|
3532
|
+
output += percentEncodeNonAscii(code);
|
|
3533
|
+
} else if (code <= 56319 && i2 + 1 < input.length) {
|
|
3534
|
+
const low = input.charCodeAt(i2 + 1);
|
|
3535
|
+
if (low >= 56320 && low <= 57343) {
|
|
3536
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
3537
|
+
i2++;
|
|
3538
|
+
} else {
|
|
3539
|
+
output += percentEncodeNonAscii(65533);
|
|
3540
|
+
}
|
|
3541
|
+
} else {
|
|
3542
|
+
output += percentEncodeNonAscii(65533);
|
|
3543
|
+
}
|
|
3368
3544
|
}
|
|
3369
3545
|
}
|
|
3370
3546
|
return output;
|
|
@@ -3387,14 +3563,18 @@ var require_utils = __commonJS({
|
|
|
3387
3563
|
function recomposeAuthority(component) {
|
|
3388
3564
|
const uriTokens = [];
|
|
3389
3565
|
if (component.userinfo !== void 0) {
|
|
3390
|
-
uriTokens.push(component.userinfo);
|
|
3566
|
+
uriTokens.push(encodeUserinfo(component.userinfo));
|
|
3391
3567
|
uriTokens.push("@");
|
|
3392
3568
|
}
|
|
3393
3569
|
if (component.host !== void 0) {
|
|
3394
|
-
let host =
|
|
3570
|
+
let host = component.host;
|
|
3395
3571
|
if (!isIPv4(host)) {
|
|
3396
|
-
|
|
3397
|
-
if (ipV6res.isIPV6
|
|
3572
|
+
let ipV6res = normalizeIPv6(host);
|
|
3573
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
3574
|
+
host = normalizePercentEncoding(host, true);
|
|
3575
|
+
ipV6res = normalizeIPv6(host);
|
|
3576
|
+
}
|
|
3577
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
3398
3578
|
host = `[${ipV6res.escapedHost}]`;
|
|
3399
3579
|
} else {
|
|
3400
3580
|
host = reescapeHostDelimiters(host, false);
|
|
@@ -3403,8 +3583,12 @@ var require_utils = __commonJS({
|
|
|
3403
3583
|
uriTokens.push(host);
|
|
3404
3584
|
}
|
|
3405
3585
|
if (typeof component.port === "number" || typeof component.port === "string") {
|
|
3586
|
+
const port = String(component.port);
|
|
3587
|
+
if (!isPort(port)) {
|
|
3588
|
+
throw new TypeError("URI port is malformed.");
|
|
3589
|
+
}
|
|
3406
3590
|
uriTokens.push(":");
|
|
3407
|
-
uriTokens.push(
|
|
3591
|
+
uriTokens.push(port);
|
|
3408
3592
|
}
|
|
3409
3593
|
return uriTokens.length ? uriTokens.join("") : void 0;
|
|
3410
3594
|
}
|
|
@@ -3414,6 +3598,11 @@ var require_utils = __commonJS({
|
|
|
3414
3598
|
reescapeHostDelimiters,
|
|
3415
3599
|
normalizePercentEncoding,
|
|
3416
3600
|
normalizePathEncoding,
|
|
3601
|
+
serializePathEncoding,
|
|
3602
|
+
normalizeQueryFragmentEncoding,
|
|
3603
|
+
encodeUserinfo,
|
|
3604
|
+
encodeQuery,
|
|
3605
|
+
encodeFragment,
|
|
3417
3606
|
escapePreservingEscapes,
|
|
3418
3607
|
removeDotSegments,
|
|
3419
3608
|
isIPv4,
|
|
@@ -3429,7 +3618,7 @@ var require_schemes = __commonJS({
|
|
|
3429
3618
|
"node_modules/fast-uri/lib/schemes.js"(exports, module) {
|
|
3430
3619
|
"use strict";
|
|
3431
3620
|
var { isUUID } = require_utils();
|
|
3432
|
-
var URN_REG =
|
|
3621
|
+
var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
3433
3622
|
var supportedSchemeNames = (
|
|
3434
3623
|
/** @type {const} */
|
|
3435
3624
|
[
|
|
@@ -3490,9 +3679,10 @@ var require_schemes = __commonJS({
|
|
|
3490
3679
|
wsComponent.secure = void 0;
|
|
3491
3680
|
}
|
|
3492
3681
|
if (wsComponent.resourceName) {
|
|
3493
|
-
const
|
|
3682
|
+
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3683
|
+
const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3494
3684
|
wsComponent.path = path && path !== "/" ? path : void 0;
|
|
3495
|
-
wsComponent.query =
|
|
3685
|
+
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3496
3686
|
wsComponent.resourceName = void 0;
|
|
3497
3687
|
}
|
|
3498
3688
|
wsComponent.fragment = void 0;
|
|
@@ -3504,7 +3694,7 @@ var require_schemes = __commonJS({
|
|
|
3504
3694
|
return urnComponent;
|
|
3505
3695
|
}
|
|
3506
3696
|
const matches = urnComponent.path.match(URN_REG);
|
|
3507
|
-
if (matches) {
|
|
3697
|
+
if (matches && matches[0] === urnComponent.path) {
|
|
3508
3698
|
const scheme = options.scheme || urnComponent.scheme || "urn";
|
|
3509
3699
|
urnComponent.nid = matches[1].toLowerCase();
|
|
3510
3700
|
urnComponent.nss = matches[2];
|
|
@@ -3638,8 +3828,17 @@ var require_schemes = __commonJS({
|
|
|
3638
3828
|
var require_fast_uri = __commonJS({
|
|
3639
3829
|
"node_modules/fast-uri/index.js"(exports, module) {
|
|
3640
3830
|
"use strict";
|
|
3641
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding,
|
|
3831
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3642
3832
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
3833
|
+
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
3834
|
+
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
3835
|
+
function decodeValidScheme(scheme) {
|
|
3836
|
+
const decodedScheme = unescape(String(scheme));
|
|
3837
|
+
if (!VALID_SCHEME.test(decodedScheme)) {
|
|
3838
|
+
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
3839
|
+
}
|
|
3840
|
+
return decodedScheme;
|
|
3841
|
+
}
|
|
3643
3842
|
function normalize(uri, options) {
|
|
3644
3843
|
if (typeof uri === "string") {
|
|
3645
3844
|
uri = /** @type {T} */
|
|
@@ -3652,12 +3851,34 @@ var require_fast_uri = __commonJS({
|
|
|
3652
3851
|
}
|
|
3653
3852
|
function resolve2(baseURI, relativeURI, options) {
|
|
3654
3853
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3655
|
-
const {
|
|
3656
|
-
|
|
3657
|
-
|
|
3854
|
+
const {
|
|
3855
|
+
parsed: baseParsed,
|
|
3856
|
+
malformedAuthorityOrPort: baseMalformed,
|
|
3857
|
+
malformedPercentEncoding: baseMalformedPercentEncoding,
|
|
3858
|
+
malformedSchemeSpecific: baseMalformedSchemeSpecific,
|
|
3859
|
+
malformedHost: baseMalformedHost,
|
|
3860
|
+
malformedScheme: baseMalformedScheme
|
|
3861
|
+
} = parseWithStatus(baseURI, schemelessOptions);
|
|
3862
|
+
const {
|
|
3863
|
+
parsed: relativeParsed,
|
|
3864
|
+
malformedAuthorityOrPort: relativeMalformed,
|
|
3865
|
+
malformedPercentEncoding: relativeMalformedPercentEncoding,
|
|
3866
|
+
malformedSchemeSpecific: relativeMalformedSchemeSpecific,
|
|
3867
|
+
malformedHost: relativeMalformedHost,
|
|
3868
|
+
malformedScheme: relativeMalformedScheme
|
|
3869
|
+
} = parseWithStatus(relativeURI, schemelessOptions);
|
|
3870
|
+
if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
|
|
3658
3871
|
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3659
3872
|
}
|
|
3660
3873
|
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3874
|
+
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
3875
|
+
const resolvedHost = resolved.host;
|
|
3876
|
+
const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
3877
|
+
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
3878
|
+
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
|
|
3879
|
+
if (resolved.error && !encodedASCIIHost) {
|
|
3880
|
+
throw new Error(resolved.error);
|
|
3881
|
+
}
|
|
3661
3882
|
schemelessOptions.skipEscape = true;
|
|
3662
3883
|
return serialize(resolved, schemelessOptions);
|
|
3663
3884
|
}
|
|
@@ -3717,7 +3938,7 @@ var require_fast_uri = __commonJS({
|
|
|
3717
3938
|
function equal(uriA, uriB, options) {
|
|
3718
3939
|
const normalizedA = normalizeComparableURI(uriA, options);
|
|
3719
3940
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
3720
|
-
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA
|
|
3941
|
+
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
|
|
3721
3942
|
}
|
|
3722
3943
|
function serialize(cmpts, opts) {
|
|
3723
3944
|
const component = {
|
|
@@ -3738,19 +3959,22 @@ var require_fast_uri = __commonJS({
|
|
|
3738
3959
|
};
|
|
3739
3960
|
const options = Object.assign({}, opts);
|
|
3740
3961
|
const uriTokens = [];
|
|
3962
|
+
if (component.scheme) {
|
|
3963
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
3964
|
+
}
|
|
3741
3965
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
3742
3966
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
3967
|
+
const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
|
|
3968
|
+
const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
|
|
3743
3969
|
if (component.path !== void 0) {
|
|
3744
3970
|
if (!options.skipEscape) {
|
|
3745
|
-
component.path =
|
|
3746
|
-
if (component.scheme !== void 0) {
|
|
3747
|
-
component.path = component.path.split("%3A").join(":");
|
|
3748
|
-
}
|
|
3971
|
+
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
3749
3972
|
} else {
|
|
3750
3973
|
component.path = normalizePercentEncoding(component.path);
|
|
3751
3974
|
}
|
|
3752
3975
|
}
|
|
3753
3976
|
if (options.reference !== "suffix" && component.scheme) {
|
|
3977
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
3754
3978
|
uriTokens.push(component.scheme, ":");
|
|
3755
3979
|
}
|
|
3756
3980
|
const authority = recomposeAuthority(component);
|
|
@@ -3768,16 +3992,19 @@ var require_fast_uri = __commonJS({
|
|
|
3768
3992
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
3769
3993
|
s2 = removeDotSegments(s2);
|
|
3770
3994
|
}
|
|
3995
|
+
if (pathNoScheme) {
|
|
3996
|
+
s2 = serializePathEncoding(s2, true);
|
|
3997
|
+
}
|
|
3771
3998
|
if (authority === void 0 && s2[0] === "/" && s2[1] === "/") {
|
|
3772
3999
|
s2 = "/%2F" + s2.slice(2);
|
|
3773
4000
|
}
|
|
3774
4001
|
uriTokens.push(s2);
|
|
3775
4002
|
}
|
|
3776
4003
|
if (component.query !== void 0) {
|
|
3777
|
-
uriTokens.push("?", component.query);
|
|
4004
|
+
uriTokens.push("?", encodeQuery(component.query));
|
|
3778
4005
|
}
|
|
3779
4006
|
if (component.fragment !== void 0) {
|
|
3780
|
-
uriTokens.push("#", component.fragment);
|
|
4007
|
+
uriTokens.push("#", encodeFragment(component.fragment));
|
|
3781
4008
|
}
|
|
3782
4009
|
return uriTokens.join("");
|
|
3783
4010
|
}
|
|
@@ -3793,6 +4020,35 @@ var require_fast_uri = __commonJS({
|
|
|
3793
4020
|
}
|
|
3794
4021
|
return void 0;
|
|
3795
4022
|
}
|
|
4023
|
+
function hasMalformedPercentEncoding(component) {
|
|
4024
|
+
if (component === void 0) return false;
|
|
4025
|
+
let percent = component.indexOf("%");
|
|
4026
|
+
while (percent !== -1) {
|
|
4027
|
+
if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
|
|
4028
|
+
return true;
|
|
4029
|
+
}
|
|
4030
|
+
percent = component.indexOf("%", percent + 3);
|
|
4031
|
+
}
|
|
4032
|
+
return false;
|
|
4033
|
+
}
|
|
4034
|
+
function isIPLiteral(host) {
|
|
4035
|
+
return host[0] === "[" && host[host.length - 1] === "]";
|
|
4036
|
+
}
|
|
4037
|
+
function hasMalformedComponentPercentEncoding(matches) {
|
|
4038
|
+
const host = matches[4];
|
|
4039
|
+
return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
|
|
4040
|
+
}
|
|
4041
|
+
function canonicalizeHost(parsed, options, schemeHandler, isIP) {
|
|
4042
|
+
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
4043
|
+
try {
|
|
4044
|
+
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
4045
|
+
} catch (e2) {
|
|
4046
|
+
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e2;
|
|
4047
|
+
return true;
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
return false;
|
|
4051
|
+
}
|
|
3796
4052
|
function parseWithStatus(uri, opts) {
|
|
3797
4053
|
const options = Object.assign({}, opts);
|
|
3798
4054
|
const parsed = {
|
|
@@ -3805,6 +4061,11 @@ var require_fast_uri = __commonJS({
|
|
|
3805
4061
|
fragment: void 0
|
|
3806
4062
|
};
|
|
3807
4063
|
let malformedAuthorityOrPort = false;
|
|
4064
|
+
let malformedPercentEncoding = false;
|
|
4065
|
+
let malformedSchemeSpecific = false;
|
|
4066
|
+
let malformedHost = false;
|
|
4067
|
+
let malformedIPLiteral = false;
|
|
4068
|
+
let malformedScheme = false;
|
|
3808
4069
|
let isIP = false;
|
|
3809
4070
|
if (options.reference === "suffix") {
|
|
3810
4071
|
if (options.scheme) {
|
|
@@ -3841,6 +4102,19 @@ var require_fast_uri = __commonJS({
|
|
|
3841
4102
|
parsed.path = matches[6] || "";
|
|
3842
4103
|
parsed.query = matches[7];
|
|
3843
4104
|
parsed.fragment = matches[8];
|
|
4105
|
+
if (parsed.scheme !== void 0) {
|
|
4106
|
+
const decodedScheme = unescape(parsed.scheme);
|
|
4107
|
+
if (VALID_SCHEME.test(decodedScheme)) {
|
|
4108
|
+
parsed.scheme = decodedScheme.toLowerCase();
|
|
4109
|
+
} else {
|
|
4110
|
+
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
4111
|
+
malformedScheme = true;
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
|
|
4115
|
+
if (malformedPercentEncoding) {
|
|
4116
|
+
parsed.error = parsed.error || "URI contains malformed percent-encoding.";
|
|
4117
|
+
}
|
|
3844
4118
|
if (isNaN(parsed.port)) {
|
|
3845
4119
|
parsed.port = matches[5];
|
|
3846
4120
|
}
|
|
@@ -3852,9 +4126,16 @@ var require_fast_uri = __commonJS({
|
|
|
3852
4126
|
if (parsed.host) {
|
|
3853
4127
|
const ipv4result = isIPv4(parsed.host);
|
|
3854
4128
|
if (ipv4result === false) {
|
|
4129
|
+
const bracketedIPLiteral = isIPLiteral(parsed.host);
|
|
4130
|
+
const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
|
|
3855
4131
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
3856
|
-
|
|
3857
|
-
|
|
4132
|
+
isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
4133
|
+
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
4134
|
+
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
4135
|
+
if (malformedIPLiteral) {
|
|
4136
|
+
parsed.error = parsed.error || "URI host is malformed.";
|
|
4137
|
+
malformedAuthorityOrPort = true;
|
|
4138
|
+
}
|
|
3858
4139
|
} else {
|
|
3859
4140
|
isIP = true;
|
|
3860
4141
|
}
|
|
@@ -3872,42 +4153,36 @@ var require_fast_uri = __commonJS({
|
|
|
3872
4153
|
parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
|
|
3873
4154
|
}
|
|
3874
4155
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
3875
|
-
if (!
|
|
3876
|
-
|
|
3877
|
-
try {
|
|
3878
|
-
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
3879
|
-
} catch (e2) {
|
|
3880
|
-
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e2;
|
|
3881
|
-
}
|
|
3882
|
-
}
|
|
4156
|
+
if (!malformedIPLiteral) {
|
|
4157
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
3883
4158
|
}
|
|
3884
4159
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
3885
4160
|
if (uri.indexOf("%") !== -1) {
|
|
3886
|
-
if (parsed.
|
|
3887
|
-
parsed.
|
|
3888
|
-
|
|
3889
|
-
if (parsed.host !== void 0) {
|
|
3890
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
4161
|
+
if (parsed.host !== void 0 && !malformedIPLiteral) {
|
|
4162
|
+
const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
4163
|
+
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
3891
4164
|
}
|
|
3892
4165
|
}
|
|
3893
4166
|
if (parsed.path) {
|
|
3894
4167
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
3895
4168
|
}
|
|
4169
|
+
if (parsed.query) {
|
|
4170
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
4171
|
+
}
|
|
3896
4172
|
if (parsed.fragment) {
|
|
3897
|
-
|
|
3898
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
3899
|
-
} catch {
|
|
3900
|
-
parsed.error = parsed.error || "URI malformed";
|
|
3901
|
-
}
|
|
4173
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
3902
4174
|
}
|
|
3903
4175
|
}
|
|
3904
4176
|
if (schemeHandler && schemeHandler.parse) {
|
|
3905
4177
|
schemeHandler.parse(parsed, options);
|
|
4178
|
+
if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
|
|
4179
|
+
malformedSchemeSpecific = true;
|
|
4180
|
+
}
|
|
3906
4181
|
}
|
|
3907
4182
|
} else {
|
|
3908
4183
|
parsed.error = parsed.error || "URI can not be parsed.";
|
|
3909
4184
|
}
|
|
3910
|
-
return { parsed, malformedAuthorityOrPort };
|
|
4185
|
+
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
3911
4186
|
}
|
|
3912
4187
|
function parse3(uri, opts) {
|
|
3913
4188
|
return parseWithStatus(uri, opts).parsed;
|
|
@@ -3916,20 +4191,28 @@ var require_fast_uri = __commonJS({
|
|
|
3916
4191
|
return normalizeStringWithStatus(uri, opts).normalized;
|
|
3917
4192
|
}
|
|
3918
4193
|
function normalizeStringWithStatus(uri, opts) {
|
|
3919
|
-
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
4194
|
+
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
3920
4195
|
return {
|
|
3921
|
-
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
3922
|
-
malformedAuthorityOrPort
|
|
4196
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
|
|
4197
|
+
malformedAuthorityOrPort,
|
|
4198
|
+
malformedPercentEncoding,
|
|
4199
|
+
malformedSchemeSpecific,
|
|
4200
|
+
malformedHost,
|
|
4201
|
+
malformedScheme
|
|
3923
4202
|
};
|
|
3924
4203
|
}
|
|
3925
4204
|
function normalizeComparableURI(uri, opts) {
|
|
3926
|
-
if (typeof uri
|
|
3927
|
-
|
|
3928
|
-
return malformedAuthorityOrPort ? void 0 : normalized;
|
|
4205
|
+
if (typeof uri !== "string" && typeof uri !== "object") {
|
|
4206
|
+
return void 0;
|
|
3929
4207
|
}
|
|
3930
|
-
|
|
3931
|
-
|
|
4208
|
+
let value;
|
|
4209
|
+
try {
|
|
4210
|
+
value = typeof uri === "string" ? uri : serialize(uri, opts);
|
|
4211
|
+
} catch {
|
|
4212
|
+
return void 0;
|
|
3932
4213
|
}
|
|
4214
|
+
const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
|
|
4215
|
+
return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
|
|
3933
4216
|
}
|
|
3934
4217
|
var fastUri = {
|
|
3935
4218
|
SCHEMES,
|
|
@@ -10599,7 +10882,7 @@ function getVerbosityLevel() {
|
|
|
10599
10882
|
function info(e2) {
|
|
10600
10883
|
zo >= Fo.INFOS && console.info(`Info: ${e2}`);
|
|
10601
10884
|
}
|
|
10602
|
-
function
|
|
10885
|
+
function warn2(e2) {
|
|
10603
10886
|
zo >= Fo.WARNINGS && console.warn(`Warning: ${e2}`);
|
|
10604
10887
|
}
|
|
10605
10888
|
function unreachable(e2) {
|
|
@@ -10747,7 +11030,7 @@ function getFilenameFromUrl(e2) {
|
|
|
10747
11030
|
}
|
|
10748
11031
|
function getPdfFilenameFromUrl(e2, t2 = `document.pdf`) {
|
|
10749
11032
|
if (typeof e2 != `string`) return t2;
|
|
10750
|
-
if (isDataScheme(e2)) return
|
|
11033
|
+
if (isDataScheme(e2)) return warn2(`getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.`), t2;
|
|
10751
11034
|
let getURL = (e3) => {
|
|
10752
11035
|
try {
|
|
10753
11036
|
return new URL(e3);
|
|
@@ -10815,7 +11098,7 @@ function getRGBA(e2) {
|
|
|
10815
11098
|
}
|
|
10816
11099
|
function getRGB(e2) {
|
|
10817
11100
|
let t2 = getRGBA(e2);
|
|
10818
|
-
return t2 ? t2.slice(0, 3) : (
|
|
11101
|
+
return t2 ? t2.slice(0, 3) : (warn2(`Not a valid color format: "${e2}"`), [0, 0, 0]);
|
|
10819
11102
|
}
|
|
10820
11103
|
function getColorValues(e2) {
|
|
10821
11104
|
let t2 = document.createElement(`span`);
|
|
@@ -10952,7 +11235,7 @@ function makePathFromDrawOPS(e2) {
|
|
|
10952
11235
|
t2.closePath();
|
|
10953
11236
|
break;
|
|
10954
11237
|
default:
|
|
10955
|
-
|
|
11238
|
+
warn2(`Unrecognized drawing path operator: ${e2[n2 - 1]}`);
|
|
10956
11239
|
break;
|
|
10957
11240
|
}
|
|
10958
11241
|
return t2;
|
|
@@ -34522,15 +34805,15 @@ var init_pdfjs = __esm({
|
|
|
34522
34805
|
Wo = class XfaText {
|
|
34523
34806
|
static textContent(e2) {
|
|
34524
34807
|
let t2 = [], n2 = { items: t2, styles: /* @__PURE__ */ Object.create(null) };
|
|
34525
|
-
function
|
|
34808
|
+
function walk2(e3) {
|
|
34526
34809
|
if (!e3) return;
|
|
34527
34810
|
let n3 = null, r2 = e3.name;
|
|
34528
34811
|
if (r2 === `#text`) n3 = e3.value;
|
|
34529
34812
|
else if (XfaText.shouldBuildText(r2)) e3?.attributes?.textContent ? n3 = e3.attributes.textContent : e3.value && (n3 = e3.value);
|
|
34530
34813
|
else return;
|
|
34531
|
-
if (n3 !== null && t2.push({ str: n3 }), e3.children) for (let t3 of e3.children)
|
|
34814
|
+
if (n3 !== null && t2.push({ str: n3 }), e3.children) for (let t3 of e3.children) walk2(t3);
|
|
34532
34815
|
}
|
|
34533
|
-
return
|
|
34816
|
+
return walk2(e2), n2;
|
|
34534
34817
|
}
|
|
34535
34818
|
static shouldBuildText(e2) {
|
|
34536
34819
|
return e2 !== `textarea` && e2 !== `input` && e2 !== `option` && e2 !== `select`;
|
|
@@ -34659,10 +34942,10 @@ var init_pdfjs = __esm({
|
|
|
34659
34942
|
#e = /* @__PURE__ */ new Map();
|
|
34660
34943
|
times = [];
|
|
34661
34944
|
time(e2) {
|
|
34662
|
-
this.#e.has(e2) &&
|
|
34945
|
+
this.#e.has(e2) && warn2(`Timer is already running for ${e2}`), this.#e.set(e2, Date.now());
|
|
34663
34946
|
}
|
|
34664
34947
|
timeEnd(e2) {
|
|
34665
|
-
this.#e.has(e2) ||
|
|
34948
|
+
this.#e.has(e2) || warn2(`Timer has not been started for ${e2}`), this.times.push({ name: e2, start: this.#e.get(e2), end: Date.now() }), this.#e.delete(e2);
|
|
34666
34949
|
}
|
|
34667
34950
|
toString() {
|
|
34668
34951
|
let e2 = Math.max(...this.times.map((e3) => e3.name.length));
|
|
@@ -34987,7 +35270,7 @@ var init_pdfjs = __esm({
|
|
|
34987
35270
|
} else n2.bitmap = await createImageBitmap(e3);
|
|
34988
35271
|
n2.refCounter = 1;
|
|
34989
35272
|
} catch (e3) {
|
|
34990
|
-
|
|
35273
|
+
warn2(e3), n2 = null;
|
|
34991
35274
|
}
|
|
34992
35275
|
return this.#n.set(e2, n2), n2 && this.#n.set(n2.id, n2), n2;
|
|
34993
35276
|
}
|
|
@@ -35126,12 +35409,12 @@ var init_pdfjs = __esm({
|
|
|
35126
35409
|
continue;
|
|
35127
35410
|
}
|
|
35128
35411
|
if (t2 !== null) {
|
|
35129
|
-
|
|
35412
|
+
warn2(`KeyboardManager: multiple keys in shortcut "${e2}"`);
|
|
35130
35413
|
break;
|
|
35131
35414
|
}
|
|
35132
35415
|
t2 = i2 === `SPACE` ? ` ` : r2;
|
|
35133
35416
|
}
|
|
35134
|
-
return t2 === null &&
|
|
35417
|
+
return t2 === null && warn2(`KeyboardManager: no key found in shortcut "${e2}"`), [t2, n2];
|
|
35135
35418
|
}
|
|
35136
35419
|
static #t(e2) {
|
|
35137
35420
|
let t2 = /^(?:Key([A-Z])|(?:Digit|Numpad)(\d))$/.exec(e2);
|
|
@@ -35562,7 +35845,7 @@ var init_pdfjs = __esm({
|
|
|
35562
35845
|
try {
|
|
35563
35846
|
n2 = JSON.parse(n2);
|
|
35564
35847
|
} catch (e3) {
|
|
35565
|
-
|
|
35848
|
+
warn2(`paste: "${e3.message}".`);
|
|
35566
35849
|
return;
|
|
35567
35850
|
}
|
|
35568
35851
|
if (!Array.isArray(n2)) return;
|
|
@@ -35583,7 +35866,7 @@ var init_pdfjs = __esm({
|
|
|
35583
35866
|
};
|
|
35584
35867
|
this.addCommands({ cmd, undo, mustExec: true });
|
|
35585
35868
|
} catch (e3) {
|
|
35586
|
-
|
|
35869
|
+
warn2(`paste: "${e3.message}".`);
|
|
35587
35870
|
}
|
|
35588
35871
|
}
|
|
35589
35872
|
keydown(e2) {
|
|
@@ -37788,7 +38071,7 @@ var init_pdfjs = __esm({
|
|
|
37788
38071
|
try {
|
|
37789
38072
|
await a2.load(), this.#e.add(t3), n2?.(e2);
|
|
37790
38073
|
} catch {
|
|
37791
|
-
|
|
38074
|
+
warn2(`Cannot load system font: ${e2.baseFontName}, installing it could help to improve PDF rendering.`), this.removeNativeFontFace(a2);
|
|
37792
38075
|
}
|
|
37793
38076
|
return;
|
|
37794
38077
|
}
|
|
@@ -37808,7 +38091,7 @@ var init_pdfjs = __esm({
|
|
|
37808
38091
|
try {
|
|
37809
38092
|
await t3.loaded;
|
|
37810
38093
|
} catch (n2) {
|
|
37811
|
-
throw
|
|
38094
|
+
throw warn2(`Failed to load font '${t3.family}': '${n2}'.`), e2.disableFontFace = true, n2;
|
|
37812
38095
|
}
|
|
37813
38096
|
}
|
|
37814
38097
|
return;
|
|
@@ -37859,7 +38142,7 @@ var init_pdfjs = __esm({
|
|
|
37859
38142
|
let a2 = i2.getContext(`2d`), o2 = 0;
|
|
37860
38143
|
function isFontReady(e3, t3) {
|
|
37861
38144
|
if (++o2 > 30) {
|
|
37862
|
-
|
|
38145
|
+
warn2(`Load test font never loaded.`), t3();
|
|
37863
38146
|
return;
|
|
37864
38147
|
}
|
|
37865
38148
|
if (a2.font = `30px ` + e3, a2.fillText(`.`, 0, 20), a2.getImageData(0, 0, 1, 1).data[3] > 0) {
|
|
@@ -37918,7 +38201,7 @@ var init_pdfjs = __esm({
|
|
|
37918
38201
|
try {
|
|
37919
38202
|
r2 = e2.get(n2);
|
|
37920
38203
|
} catch (e3) {
|
|
37921
|
-
|
|
38204
|
+
warn2(`getPathGenerator - ignoring character: "${e3}".`);
|
|
37922
38205
|
}
|
|
37923
38206
|
let i2 = makePathFromDrawOPS(r2?.path);
|
|
37924
38207
|
return this.fontExtraProperties || e2.delete(n2), this.compiledGlyphs[t2] = i2;
|
|
@@ -38275,7 +38558,7 @@ var init_pdfjs = __esm({
|
|
|
38275
38558
|
if (n2?.signal instanceof AbortSignal) {
|
|
38276
38559
|
let { signal: r2 } = n2;
|
|
38277
38560
|
if (r2.aborted) {
|
|
38278
|
-
|
|
38561
|
+
warn2("LoopbackPort - cannot use an `aborted` signal.");
|
|
38279
38562
|
return;
|
|
38280
38563
|
}
|
|
38281
38564
|
let onAbort = () => this.removeEventListener(e2, t2);
|
|
@@ -38567,7 +38850,7 @@ var init_pdfjs = __esm({
|
|
|
38567
38850
|
if (this.#e === void 0) {
|
|
38568
38851
|
this.#e = ``;
|
|
38569
38852
|
let e3 = this.#i.URL;
|
|
38570
|
-
e3 !== this.#i.baseURI && (isDataScheme(e3) ?
|
|
38853
|
+
e3 !== this.#i.baseURI && (isDataScheme(e3) ? warn2(`#createUrl: ignore "data:"-URL for performance reasons.`) : this.#e = updateUrlHash(e3, ``));
|
|
38571
38854
|
}
|
|
38572
38855
|
return `url(${this.#e}#${e2})`;
|
|
38573
38856
|
}
|
|
@@ -39508,7 +39791,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
39508
39791
|
this.dependencyTracker?.recordSimpleData(`font`, e2).recordSimpleDataFromNamed(`fontObj`, t2, e2);
|
|
39509
39792
|
let r2 = this.commonObjs.get(t2), i2 = this.current;
|
|
39510
39793
|
if (!r2) throw Error(`Can't find font for ${t2}`);
|
|
39511
|
-
if (i2.fontMatrix = r2.fontMatrix || xo, (i2.fontMatrix[0] === 0 || i2.fontMatrix[3] === 0) &&
|
|
39794
|
+
if (i2.fontMatrix = r2.fontMatrix || xo, (i2.fontMatrix[0] === 0 || i2.fontMatrix[3] === 0) && warn2(`Invalid font matrix for font ` + t2), n2 < 0 ? (n2 = -n2, i2.fontDirection = -1) : i2.fontDirection = 1, this.current.font = r2, this.current.fontSize = n2, r2.isType3Font) return;
|
|
39512
39795
|
let a2 = r2.loadedName || `sans-serif`, o2 = r2.systemFontInfo?.css || `"${a2}", ${r2.fallbackName}`, s2 = `normal`;
|
|
39513
39796
|
r2.black ? s2 = `900` : r2.bold && (s2 = `bold`);
|
|
39514
39797
|
let c2 = r2.italic ? `italic` : `normal`, l2 = MathClamp(n2, 16, 100);
|
|
@@ -39650,7 +39933,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
39650
39933
|
continue;
|
|
39651
39934
|
}
|
|
39652
39935
|
let e3 = (h2.isSpace ? l2 : 0) + c2, o3 = i2.charProcOperatorList[h2.operatorListId];
|
|
39653
|
-
o3 ? this.contentVisible && (this.save(), o3.fnArray[0] === Io.setCharWidth && (r2.fillAlpha = r2.strokeAlpha = 1, n2.globalAlpha = 1), n2.scale(a2, a2), n2.transform(...d2), this.executeOperatorList(o3), this.restore()) :
|
|
39936
|
+
o3 ? this.contentVisible && (this.save(), o3.fnArray[0] === Io.setCharWidth && (r2.fillAlpha = r2.strokeAlpha = 1, n2.globalAlpha = 1), n2.scale(a2, a2), n2.transform(...d2), this.executeOperatorList(o3), this.restore()) : warn2(`Type3 character "${h2.operatorListId}" is not available.`);
|
|
39654
39937
|
let f3 = [h2.width, 0];
|
|
39655
39938
|
Util.applyTransform(f3, d2), g2 = f3[0] * a2 + e3, n2.translate(g2, 0), r2.x += g2 * u2;
|
|
39656
39939
|
}
|
|
@@ -39870,7 +40153,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
39870
40153
|
if (!this.contentVisible) return;
|
|
39871
40154
|
let n2 = this.getObject(e2, t2);
|
|
39872
40155
|
if (!n2) {
|
|
39873
|
-
|
|
40156
|
+
warn2(`Dependent image isn't ready yet`);
|
|
39874
40157
|
return;
|
|
39875
40158
|
}
|
|
39876
40159
|
this.paintInlineImageXObject(e2, n2);
|
|
@@ -39879,7 +40162,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
39879
40162
|
if (!this.contentVisible) return;
|
|
39880
40163
|
let a2 = this.getObject(e2, t2);
|
|
39881
40164
|
if (!a2) {
|
|
39882
|
-
|
|
40165
|
+
warn2(`Dependent image isn't ready yet`);
|
|
39883
40166
|
return;
|
|
39884
40167
|
}
|
|
39885
40168
|
let o2 = a2.width, s2 = a2.height, c2 = [];
|
|
@@ -40267,7 +40550,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
40267
40550
|
let i2 = network_getArrayBuffer(e2.response);
|
|
40268
40551
|
if (r2 === 206) {
|
|
40269
40552
|
let t3 = e2.getResponseHeader(`Content-Range`);
|
|
40270
|
-
/bytes \d+-\d+\/\d+/.test(t3) ? n2.onDone(i2) : (
|
|
40553
|
+
/bytes \d+-\d+\/\d+/.test(t3) ? n2.onDone(i2) : (warn2(`Missing or invalid "Content-Range" header.`), n2.onError(0));
|
|
40271
40554
|
} else i2 ? n2.onDone(i2) : n2.onError(e2.status);
|
|
40272
40555
|
}
|
|
40273
40556
|
_abortRequest(e2) {
|
|
@@ -40497,7 +40780,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
40497
40780
|
let t3 = e2[r2], i2;
|
|
40498
40781
|
if (Array.isArray(t3)) i2 = this.#a(t3);
|
|
40499
40782
|
else if (this.#t.has(t3)) i2 = this.#t.get(t3).visible;
|
|
40500
|
-
else return
|
|
40783
|
+
else return warn2(`Optional content group not found: ${t3}`), true;
|
|
40501
40784
|
switch (n2) {
|
|
40502
40785
|
case `And`:
|
|
40503
40786
|
if (!i2) return false;
|
|
@@ -40516,42 +40799,42 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
40516
40799
|
isVisible(e2) {
|
|
40517
40800
|
if (this.#t.size === 0) return true;
|
|
40518
40801
|
if (!e2) return info(`Optional content group not defined.`), true;
|
|
40519
|
-
if (e2.type === `OCG`) return this.#t.has(e2.id) ? this.#t.get(e2.id).visible : (
|
|
40802
|
+
if (e2.type === `OCG`) return this.#t.has(e2.id) ? this.#t.get(e2.id).visible : (warn2(`Optional content group not found: ${e2.id}`), true);
|
|
40520
40803
|
if (e2.type === `OCMD`) {
|
|
40521
40804
|
if (e2.expression) return this.#a(e2.expression);
|
|
40522
40805
|
if (!e2.policy || e2.policy === `AnyOn`) {
|
|
40523
40806
|
for (let t2 of e2.ids) {
|
|
40524
|
-
if (!this.#t.has(t2)) return
|
|
40807
|
+
if (!this.#t.has(t2)) return warn2(`Optional content group not found: ${t2}`), true;
|
|
40525
40808
|
if (this.#t.get(t2).visible) return true;
|
|
40526
40809
|
}
|
|
40527
40810
|
return false;
|
|
40528
40811
|
} else if (e2.policy === `AllOn`) {
|
|
40529
40812
|
for (let t2 of e2.ids) {
|
|
40530
|
-
if (!this.#t.has(t2)) return
|
|
40813
|
+
if (!this.#t.has(t2)) return warn2(`Optional content group not found: ${t2}`), true;
|
|
40531
40814
|
if (!this.#t.get(t2).visible) return false;
|
|
40532
40815
|
}
|
|
40533
40816
|
return true;
|
|
40534
40817
|
} else if (e2.policy === `AnyOff`) {
|
|
40535
40818
|
for (let t2 of e2.ids) {
|
|
40536
|
-
if (!this.#t.has(t2)) return
|
|
40819
|
+
if (!this.#t.has(t2)) return warn2(`Optional content group not found: ${t2}`), true;
|
|
40537
40820
|
if (!this.#t.get(t2).visible) return true;
|
|
40538
40821
|
}
|
|
40539
40822
|
return false;
|
|
40540
40823
|
} else if (e2.policy === `AllOff`) {
|
|
40541
40824
|
for (let t2 of e2.ids) {
|
|
40542
|
-
if (!this.#t.has(t2)) return
|
|
40825
|
+
if (!this.#t.has(t2)) return warn2(`Optional content group not found: ${t2}`), true;
|
|
40543
40826
|
if (this.#t.get(t2).visible) return false;
|
|
40544
40827
|
}
|
|
40545
40828
|
return true;
|
|
40546
40829
|
}
|
|
40547
|
-
return
|
|
40830
|
+
return warn2(`Unknown optional content policy ${e2.policy}.`), true;
|
|
40548
40831
|
}
|
|
40549
|
-
return
|
|
40832
|
+
return warn2(`Unknown group type ${e2.type}.`), true;
|
|
40550
40833
|
}
|
|
40551
40834
|
setVisibility(e2, t2 = true, n2 = true) {
|
|
40552
40835
|
let r2 = this.#t.get(e2);
|
|
40553
40836
|
if (!r2) {
|
|
40554
|
-
|
|
40837
|
+
warn2(`Optional content group not found: ${e2}`);
|
|
40555
40838
|
return;
|
|
40556
40839
|
}
|
|
40557
40840
|
if (n2 && t2 && r2.rbGroups.length) for (let t3 of r2.rbGroups) for (let n3 of t3) n3 !== e2 && this.#t.get(n3)?._setVisible(ks, false, true);
|
|
@@ -40847,7 +41130,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
40847
41130
|
let t2 = this.#g, n2 = this.#m;
|
|
40848
41131
|
for (let r2 of e2) {
|
|
40849
41132
|
if (t2.length > 1e5) {
|
|
40850
|
-
|
|
41133
|
+
warn2(`Ignoring additional textDivs for performance reasons.`), this.#n = true;
|
|
40851
41134
|
return;
|
|
40852
41135
|
}
|
|
40853
41136
|
if (r2.str === void 0) {
|
|
@@ -40924,7 +41207,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
40924
41207
|
let a2 = i2.measureText(``), o2 = a2.fontBoundingBoxAscent, s2 = Math.abs(a2.fontBoundingBoxDescent);
|
|
40925
41208
|
i2.canvas.width = i2.canvas.height = 0;
|
|
40926
41209
|
let c2 = 0.8;
|
|
40927
|
-
return o2 ? c2 = o2 / (o2 + s2) : (FeatureTest.platform.isFirefox &&
|
|
41210
|
+
return o2 ? c2 = o2 / (o2 + s2) : (FeatureTest.platform.isFirefox && warn2("Enable the `dom.textMetrics.fontBoundingBox.enabled` preference in `about:config` to improve TextLayer rendering."), t2.ascent ? c2 = t2.ascent : t2.descent && (c2 = 1 + t2.descent)), this.#y.set(e2, c2), c2;
|
|
40928
41211
|
}
|
|
40929
41212
|
};
|
|
40930
41213
|
Ns = class PDFDocumentLoadingTask {
|
|
@@ -41358,7 +41641,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
41358
41641
|
this.#u();
|
|
41359
41642
|
}
|
|
41360
41643
|
#u() {
|
|
41361
|
-
PDFWorker.#a ||= (
|
|
41644
|
+
PDFWorker.#a ||= (warn2(`Setting up fake worker.`), true), PDFWorker._setupFakeWorkerGlobal.then((e2) => {
|
|
41362
41645
|
if (this.destroyed) {
|
|
41363
41646
|
this.#e.reject(Error(`Worker was destroyed`));
|
|
41364
41647
|
return;
|
|
@@ -41435,7 +41718,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
41435
41718
|
a2 = wo.PRINT;
|
|
41436
41719
|
break;
|
|
41437
41720
|
default:
|
|
41438
|
-
|
|
41721
|
+
warn2(`getRenderingIntent - invalid intent: ${e2}`);
|
|
41439
41722
|
}
|
|
41440
41723
|
let s2 = a2 & wo.PRINT && n2 instanceof PrintAnnotationStorage ? n2 : this.annotationStorage;
|
|
41441
41724
|
switch (t2) {
|
|
@@ -41451,7 +41734,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
41451
41734
|
a2 += wo.ANNOTATIONS_STORAGE, o2 = s2.serializable;
|
|
41452
41735
|
break;
|
|
41453
41736
|
default:
|
|
41454
|
-
|
|
41737
|
+
warn2(`getRenderingIntent - invalid annotationMode: ${t2}`);
|
|
41455
41738
|
}
|
|
41456
41739
|
r2 && (a2 += wo.IS_EDITING), i2 && (a2 += wo.OPLIST);
|
|
41457
41740
|
let { ids: c2, hash: l2 } = s2.modifiedIds, u2 = [a2, o2.hash, l2];
|
|
@@ -41538,7 +41821,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
41538
41821
|
case `Font`:
|
|
41539
41822
|
if (`error` in r2) {
|
|
41540
41823
|
let e3 = r2.error;
|
|
41541
|
-
|
|
41824
|
+
warn2(`Error during font loading: ${e3}`), this.commonObjs.resolve(t3, e3);
|
|
41542
41825
|
break;
|
|
41543
41826
|
}
|
|
41544
41827
|
let i2 = new FontFaceObject(new FontInfo(r2), this._params.pdfBug && globalThis.FontInspector?.enabled ? (e3, t4) => globalThis.FontInspector.fontAdded(e3, t4) : null, r2.charProcOperatorList, r2.extra);
|
|
@@ -41599,7 +41882,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
41599
41882
|
return this.messageHandler.sendWithPromise(`GetData`, null);
|
|
41600
41883
|
}
|
|
41601
41884
|
saveDocument() {
|
|
41602
|
-
this.annotationStorage.size <= 0 &&
|
|
41885
|
+
this.annotationStorage.size <= 0 && warn2("saveDocument called while `annotationStorage` is empty, please use the getData-method instead.");
|
|
41603
41886
|
let { map: e2, transfer: t2 } = this.annotationStorage.serializable;
|
|
41604
41887
|
return this.messageHandler.sendWithPromise(`SaveDocument`, { isPureXfa: !!this._htmlForXfa, numPages: this._numPages, annotationStorage: e2, filename: this.#e?.filename ?? null }, t2).finally(() => {
|
|
41605
41888
|
this.annotationStorage.resetModified();
|
|
@@ -42188,10 +42471,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
42188
42471
|
a2.borderStyle = `dashed`;
|
|
42189
42472
|
break;
|
|
42190
42473
|
case Po.BEVELED:
|
|
42191
|
-
|
|
42474
|
+
warn2(`Unimplemented border style: beveled`);
|
|
42192
42475
|
break;
|
|
42193
42476
|
case Po.INSET:
|
|
42194
|
-
|
|
42477
|
+
warn2(`Unimplemented border style: inset`);
|
|
42195
42478
|
break;
|
|
42196
42479
|
case Po.UNDERLINE:
|
|
42197
42480
|
a2.borderBottomStyle = `solid`;
|
|
@@ -42311,7 +42594,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
42311
42594
|
if (e3 === -1 || i2 === t2) continue;
|
|
42312
42595
|
let r3 = typeof a2 == `string` ? a2 : null, o2 = document.querySelector(`[data-element-id="${i2}"]`);
|
|
42313
42596
|
if (o2 && !Hs.has(o2)) {
|
|
42314
|
-
|
|
42597
|
+
warn2(`_getElementsByName - element not allowed: ${i2}`);
|
|
42315
42598
|
continue;
|
|
42316
42599
|
}
|
|
42317
42600
|
n2.push({ id: i2, exportValue: r3, domElement: o2 });
|
|
@@ -42431,7 +42714,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
42431
42714
|
_bindResetFormAction(e2, t2) {
|
|
42432
42715
|
let n2 = e2.onclick;
|
|
42433
42716
|
if (n2 || (e2.href = this.linkService.getAnchorUrl(``)), this.#e(), !this._fieldObjects) {
|
|
42434
|
-
|
|
42717
|
+
warn2('_bindResetFormAction - "resetForm" action not supported, ensure that the `fieldObjects` parameter is provided.'), n2 || (e2.onclick = () => false);
|
|
42435
42718
|
return;
|
|
42436
42719
|
}
|
|
42437
42720
|
e2.onclick = () => {
|
|
@@ -42472,7 +42755,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
42472
42755
|
let n3 = document.querySelector(`[data-element-id="${t3}"]`);
|
|
42473
42756
|
if (n3) {
|
|
42474
42757
|
if (!Hs.has(n3)) {
|
|
42475
|
-
|
|
42758
|
+
warn2(`_bindResetFormAction - element not allowed: ${t3}`);
|
|
42476
42759
|
continue;
|
|
42477
42760
|
}
|
|
42478
42761
|
n3.dispatchEvent(new Event(`resetform`));
|
|
@@ -45413,7 +45696,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
45413
45696
|
}
|
|
45414
45697
|
return { areContours: d2, thickness: f2, outlines: h2, width: l2, height: u2 };
|
|
45415
45698
|
} catch (e3) {
|
|
45416
|
-
return
|
|
45699
|
+
return warn2(`decompressSignature: ${e3}`), null;
|
|
45417
45700
|
}
|
|
45418
45701
|
}
|
|
45419
45702
|
};
|
|
@@ -70944,13 +71227,104 @@ function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
|
|
|
70944
71227
|
return redacted;
|
|
70945
71228
|
return `${redacted.slice(0, max)}\u2026 [truncated]`;
|
|
70946
71229
|
}
|
|
71230
|
+
function messageOf(err) {
|
|
71231
|
+
if (err instanceof Error)
|
|
71232
|
+
return err.message;
|
|
71233
|
+
return String(err);
|
|
71234
|
+
}
|
|
70947
71235
|
|
|
70948
|
-
// node_modules/@chrischall/mcp-utils/dist/response/
|
|
70949
|
-
|
|
70950
|
-
|
|
70951
|
-
|
|
70952
|
-
|
|
71236
|
+
// node_modules/@chrischall/mcp-utils/dist/response/view.js
|
|
71237
|
+
var VIEWS = ["compact", "full", "raw"];
|
|
71238
|
+
var DEFAULT_VIEW = "compact";
|
|
71239
|
+
var BLURB = {
|
|
71240
|
+
compact: '"compact" (default) drops fields the response already carries elsewhere',
|
|
71241
|
+
full: '"full" returns every field this server understands',
|
|
71242
|
+
raw: '"raw" returns the upstream payload unprojected'
|
|
71243
|
+
};
|
|
71244
|
+
function viewParam(honoured, opts = {}) {
|
|
71245
|
+
if (honoured.length < 2) {
|
|
71246
|
+
throw new Error("viewParam needs at least two rungs: a parameter offering one value decides nothing");
|
|
71247
|
+
}
|
|
71248
|
+
if (!honoured.includes("compact")) {
|
|
71249
|
+
throw new Error('viewParam must offer "compact": a tool with no cheap rung has nothing to default to');
|
|
71250
|
+
}
|
|
71251
|
+
const ordered = VIEWS.filter((v2) => honoured.includes(v2));
|
|
71252
|
+
const sentence = `Response shape: ${ordered.map((v2) => BLURB[v2]).join("; ")}.`;
|
|
71253
|
+
return external_exports.enum(Object.fromEntries(ordered.map((v2) => [v2, v2]))).optional().describe(opts.note ? `${sentence} ${opts.note}` : sentence);
|
|
71254
|
+
}
|
|
71255
|
+
function resolveView(value, honoured) {
|
|
71256
|
+
return value !== void 0 && honoured.includes(value) ? value : DEFAULT_VIEW;
|
|
71257
|
+
}
|
|
71258
|
+
function minifiedResult(data) {
|
|
71259
|
+
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
70953
71260
|
}
|
|
71261
|
+
function projectOrRaw(value, project, opts) {
|
|
71262
|
+
try {
|
|
71263
|
+
const projected = project(value);
|
|
71264
|
+
if (projected === void 0) {
|
|
71265
|
+
warn(opts, "the projection produced nothing");
|
|
71266
|
+
return value;
|
|
71267
|
+
}
|
|
71268
|
+
return projected;
|
|
71269
|
+
} catch (error51) {
|
|
71270
|
+
warn(opts, error51 instanceof Error ? error51.message : String(error51));
|
|
71271
|
+
return value;
|
|
71272
|
+
}
|
|
71273
|
+
}
|
|
71274
|
+
function warn(opts, why) {
|
|
71275
|
+
console.error(`[${opts.label}] WARNING: could not project ${opts.context} (${why}); returning the unprojected payload. The upstream shape has probably changed \u2014 the projection needs updating.`);
|
|
71276
|
+
}
|
|
71277
|
+
|
|
71278
|
+
// node_modules/@chrischall/mcp-utils/dist/response/media.js
|
|
71279
|
+
var MEDIA_NOUN = "(?:avatar|picture|photo|thumbnail|thumb|image|icon|banner|profile_pic(?:ture)?|logo)";
|
|
71280
|
+
var MEDIA_QUALIFIER = "(?:primary|secondary|main|default|cover|hero|profile|master|rendered|small|medium|large|full|original|tall)";
|
|
71281
|
+
var MEDIA_KEY = new RegExp(`^(?:(?:${MEDIA_QUALIFIER}|${MEDIA_NOUN})[_-]?)?${MEDIA_NOUN}s?(?:[_-]?(?:link|uri|url|src)s?)?$`, "i");
|
|
71282
|
+
var MEDIA_URL = /^https?:\/\/[^\s]+?\.(png|jpe?g|gif|webp|svg|avif|bmp|ico)([?#]|$)/i;
|
|
71283
|
+
function stripMediaUrls(value, opts = {}) {
|
|
71284
|
+
const keep = normalizeRules(opts.keep ?? []);
|
|
71285
|
+
const drop = normalizeRules(opts.drop ?? []);
|
|
71286
|
+
return walk(value, keep, drop);
|
|
71287
|
+
}
|
|
71288
|
+
function normalizeRules(rules) {
|
|
71289
|
+
return rules.map((rule) => typeof rule === "string" ? rule.toLowerCase() : new RegExp(rule.source, rule.flags));
|
|
71290
|
+
}
|
|
71291
|
+
function matchesRule(key, rules) {
|
|
71292
|
+
const lower = key.toLowerCase();
|
|
71293
|
+
for (const rule of rules) {
|
|
71294
|
+
if (typeof rule === "string") {
|
|
71295
|
+
if (rule === lower)
|
|
71296
|
+
return true;
|
|
71297
|
+
continue;
|
|
71298
|
+
}
|
|
71299
|
+
rule.lastIndex = 0;
|
|
71300
|
+
if (rule.test(key))
|
|
71301
|
+
return true;
|
|
71302
|
+
}
|
|
71303
|
+
return false;
|
|
71304
|
+
}
|
|
71305
|
+
function walk(value, keep, drop) {
|
|
71306
|
+
if (Array.isArray(value))
|
|
71307
|
+
return value.map((v2) => walk(v2, keep, drop));
|
|
71308
|
+
if (value === null || typeof value !== "object")
|
|
71309
|
+
return value;
|
|
71310
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null)
|
|
71311
|
+
return value;
|
|
71312
|
+
const out = {};
|
|
71313
|
+
for (const [key, v2] of Object.entries(value)) {
|
|
71314
|
+
if (matchesRule(key, keep)) {
|
|
71315
|
+
out[key] = v2;
|
|
71316
|
+
continue;
|
|
71317
|
+
}
|
|
71318
|
+
if (MEDIA_KEY.test(key) || matchesRule(key, drop))
|
|
71319
|
+
continue;
|
|
71320
|
+
if (typeof v2 === "string" && MEDIA_URL.test(v2))
|
|
71321
|
+
continue;
|
|
71322
|
+
out[key] = walk(v2, keep, drop);
|
|
71323
|
+
}
|
|
71324
|
+
return out;
|
|
71325
|
+
}
|
|
71326
|
+
|
|
71327
|
+
// node_modules/@chrischall/mcp-utils/dist/response/index.js
|
|
70954
71328
|
function imageResult(base643, mimeType) {
|
|
70955
71329
|
return {
|
|
70956
71330
|
content: [{ type: "image", data: base643, mimeType }]
|
|
@@ -71380,6 +71754,8 @@ function redactAssetQuery(err, search) {
|
|
|
71380
71754
|
}
|
|
71381
71755
|
var SplitwiseClient = class {
|
|
71382
71756
|
apiKey;
|
|
71757
|
+
/** Which source supplied the API key — a LABEL, never the value. */
|
|
71758
|
+
credentialSource;
|
|
71383
71759
|
configError;
|
|
71384
71760
|
api;
|
|
71385
71761
|
/** Per-origin clients for asset hosts, built on first use by `fetchAsset`. */
|
|
@@ -71395,7 +71771,9 @@ var SplitwiseClient = class {
|
|
|
71395
71771
|
* byte-for-byte identical behaviour.
|
|
71396
71772
|
*/
|
|
71397
71773
|
constructor(opts) {
|
|
71398
|
-
const
|
|
71774
|
+
const injected = opts?.apiKey;
|
|
71775
|
+
const key = injected ?? readEnvVar("SPLITWISE_API_KEY");
|
|
71776
|
+
this.credentialSource = !key ? null : injected !== void 0 ? "injected" : "env";
|
|
71399
71777
|
if (!key) {
|
|
71400
71778
|
this.apiKey = null;
|
|
71401
71779
|
this.configError = new Error("SPLITWISE_API_KEY environment variable is required");
|
|
@@ -71405,6 +71783,14 @@ var SplitwiseClient = class {
|
|
|
71405
71783
|
}
|
|
71406
71784
|
this.api = this.buildApiClient(BASE_URL, true);
|
|
71407
71785
|
}
|
|
71786
|
+
/**
|
|
71787
|
+
* Which source supplied the API key, for `sw_healthcheck`. Returns a LABEL
|
|
71788
|
+
* or `null` — deliberately never the key, since the healthcheck result is
|
|
71789
|
+
* the thing people paste into a chat when something is broken.
|
|
71790
|
+
*/
|
|
71791
|
+
describeCredential() {
|
|
71792
|
+
return { source: this.credentialSource };
|
|
71793
|
+
}
|
|
71408
71794
|
/**
|
|
71409
71795
|
* One API client per origin, sharing the fleet's retry/timeout/error policy.
|
|
71410
71796
|
* `authenticated` decides whether the API key is attached: `getToken` defers
|
|
@@ -71460,10 +71846,117 @@ var SplitwiseClient = class {
|
|
|
71460
71846
|
};
|
|
71461
71847
|
var client = new SplitwiseClient();
|
|
71462
71848
|
|
|
71849
|
+
// src/project.ts
|
|
71850
|
+
var SW_VIEWS = ["compact", "full"];
|
|
71851
|
+
var LABEL = "splitwise-mcp";
|
|
71852
|
+
function asDict(v2) {
|
|
71853
|
+
return v2 !== null && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
|
|
71854
|
+
}
|
|
71855
|
+
function asList(v2) {
|
|
71856
|
+
return Array.isArray(v2) ? v2 : [];
|
|
71857
|
+
}
|
|
71858
|
+
function nameOf(user) {
|
|
71859
|
+
if (!user) return void 0;
|
|
71860
|
+
const parts = [user.first_name, user.last_name].filter((p2) => typeof p2 === "string" && p2 !== "");
|
|
71861
|
+
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
71862
|
+
}
|
|
71863
|
+
var PERSON_VIEW_NOTE = 'compact returns {id, name, email, registration_status, balance} per person \u2014 `name` is first_name + last_name joined, so the separate fields are on "full" only; "full" returns Splitwise\'s whole record.';
|
|
71864
|
+
function compactPerson(raw) {
|
|
71865
|
+
const u2 = asDict(raw) ?? {};
|
|
71866
|
+
return pruneUndefined({
|
|
71867
|
+
id: u2.id,
|
|
71868
|
+
name: nameOf(u2),
|
|
71869
|
+
email: u2.email,
|
|
71870
|
+
registration_status: u2.registration_status,
|
|
71871
|
+
balance: asList(u2.balance).length > 0 ? u2.balance : void 0
|
|
71872
|
+
});
|
|
71873
|
+
}
|
|
71874
|
+
function compactGroup(raw) {
|
|
71875
|
+
const g2 = asDict(raw) ?? {};
|
|
71876
|
+
return pruneUndefined({
|
|
71877
|
+
id: g2.id,
|
|
71878
|
+
name: g2.name,
|
|
71879
|
+
group_type: g2.group_type,
|
|
71880
|
+
updated_at: g2.updated_at,
|
|
71881
|
+
members: asList(g2.members).map(compactPerson),
|
|
71882
|
+
simplified_debts: asList(g2.simplified_debts).length > 0 ? g2.simplified_debts : void 0,
|
|
71883
|
+
invite_link: g2.invite_link
|
|
71884
|
+
});
|
|
71885
|
+
}
|
|
71886
|
+
function compactExpense(raw) {
|
|
71887
|
+
const e2 = asDict(raw) ?? {};
|
|
71888
|
+
const receipt = asDict(e2.receipt);
|
|
71889
|
+
const original = receipt?.original;
|
|
71890
|
+
return pruneUndefined({
|
|
71891
|
+
id: e2.id,
|
|
71892
|
+
group_id: e2.group_id,
|
|
71893
|
+
description: e2.description,
|
|
71894
|
+
details: e2.details,
|
|
71895
|
+
cost: e2.cost,
|
|
71896
|
+
currency_code: e2.currency_code,
|
|
71897
|
+
date: e2.date,
|
|
71898
|
+
category: asDict(e2.category)?.name,
|
|
71899
|
+
payment: e2.payment === true ? true : void 0,
|
|
71900
|
+
created_by: nameOf(asDict(e2.created_by)),
|
|
71901
|
+
updated_at: e2.updated_at,
|
|
71902
|
+
// Kept only when set. A deleted expense still comes back from the list
|
|
71903
|
+
// endpoint, and a caller that cannot see `deleted_at` will count it.
|
|
71904
|
+
deleted_at: e2.deleted_at ?? void 0,
|
|
71905
|
+
comments_count: e2.comments_count,
|
|
71906
|
+
repayments: e2.repayments,
|
|
71907
|
+
users: asList(e2.users).map((entry) => {
|
|
71908
|
+
const u2 = asDict(entry) ?? {};
|
|
71909
|
+
return {
|
|
71910
|
+
id: u2.user_id ?? asDict(u2.user)?.id,
|
|
71911
|
+
name: nameOf(asDict(u2.user)),
|
|
71912
|
+
paid_share: u2.paid_share,
|
|
71913
|
+
owed_share: u2.owed_share,
|
|
71914
|
+
net_balance: u2.net_balance
|
|
71915
|
+
};
|
|
71916
|
+
}),
|
|
71917
|
+
// A receipt is evidence; `sw_get_receipt` is how a caller fetches it.
|
|
71918
|
+
has_receipt: typeof original === "string" ? true : void 0
|
|
71919
|
+
});
|
|
71920
|
+
}
|
|
71921
|
+
function projectList(view, payload, key, map2, context) {
|
|
71922
|
+
if (view !== "compact") return payload;
|
|
71923
|
+
return projectOrRaw(
|
|
71924
|
+
payload,
|
|
71925
|
+
(p2) => {
|
|
71926
|
+
const box = asDict(p2);
|
|
71927
|
+
const items = box?.[key];
|
|
71928
|
+
if (!box || !Array.isArray(items)) throw new Error(`expected a ${key}[] array`);
|
|
71929
|
+
return { ...box, [key]: items.map(map2) };
|
|
71930
|
+
},
|
|
71931
|
+
{ label: LABEL, context }
|
|
71932
|
+
);
|
|
71933
|
+
}
|
|
71934
|
+
var viewGroups = (view, p2) => projectList(view, p2, "groups", compactGroup, "GET /get_groups");
|
|
71935
|
+
var viewFriends = (view, p2) => projectList(view, p2, "friends", compactPerson, "GET /get_friends");
|
|
71936
|
+
var viewExpenses = (view, p2) => projectList(view, p2, "expenses", compactExpense, "GET /get_expenses");
|
|
71937
|
+
function projectOne(view, payload, key, map2, context) {
|
|
71938
|
+
if (view !== "compact") return payload;
|
|
71939
|
+
return projectOrRaw(
|
|
71940
|
+
payload,
|
|
71941
|
+
(p2) => {
|
|
71942
|
+
const box = asDict(p2);
|
|
71943
|
+
if (!box || box[key] === void 0) throw new Error(`expected a ${key} object`);
|
|
71944
|
+
return { ...box, [key]: map2(box[key]) };
|
|
71945
|
+
},
|
|
71946
|
+
{ label: LABEL, context }
|
|
71947
|
+
);
|
|
71948
|
+
}
|
|
71949
|
+
var viewGroup = (view, p2) => projectOne(view, p2, "group", compactGroup, "GET /get_group/{id}");
|
|
71950
|
+
var viewExpense = (view, p2) => projectOne(view, p2, "expense", compactExpense, "GET /get_expense/{id}");
|
|
71951
|
+
var viewUser = (view, p2) => projectOne(view, p2, "user", compactPerson, "GET /get_user/{id}");
|
|
71952
|
+
function viewGeneric(view, payload) {
|
|
71953
|
+
return view === "compact" ? stripMediaUrls(payload) : payload;
|
|
71954
|
+
}
|
|
71955
|
+
|
|
71463
71956
|
// src/tools/_confirm.ts
|
|
71464
71957
|
function previewUnlessConfirmed(confirm, action, method, path, body) {
|
|
71465
71958
|
if (confirm === true) return null;
|
|
71466
|
-
return
|
|
71959
|
+
return minifiedResult({
|
|
71467
71960
|
dryRun: true,
|
|
71468
71961
|
action,
|
|
71469
71962
|
method,
|
|
@@ -71476,21 +71969,25 @@ function previewUnlessConfirmed(confirm, action, method, path, body) {
|
|
|
71476
71969
|
// src/tools/user.ts
|
|
71477
71970
|
function registerUserTools(server, client2) {
|
|
71478
71971
|
server.registerTool("sw_get_current_user", {
|
|
71479
|
-
description: "Get the authenticated Splitwise user's profile
|
|
71480
|
-
annotations: { readOnlyHint: true }
|
|
71481
|
-
|
|
71972
|
+
description: "Get the authenticated Splitwise user's profile. Use the returned id when building custom expense splits. The compact default returns id, name (first_name + last_name joined), email and registration_status; pass view:'full' for Splitwise's raw record, which keeps first_name and last_name separate \u2014 the form sw_update_user takes.",
|
|
71973
|
+
annotations: { readOnlyHint: true },
|
|
71974
|
+
inputSchema: {
|
|
71975
|
+
view: viewParam(SW_VIEWS, { note: PERSON_VIEW_NOTE })
|
|
71976
|
+
}
|
|
71977
|
+
}, async ({ view }) => {
|
|
71482
71978
|
const data = await client2.request("GET", "/get_current_user");
|
|
71483
|
-
return
|
|
71979
|
+
return minifiedResult(viewUser(resolveView(view, SW_VIEWS), data));
|
|
71484
71980
|
});
|
|
71485
71981
|
server.registerTool("sw_get_user", {
|
|
71486
|
-
description: "Get another Splitwise user's profile by id.",
|
|
71982
|
+
description: "Get another Splitwise user's profile by id. Same shape as sw_get_current_user: the compact default merges first_name/last_name into name, and view:'full' keeps them separate.",
|
|
71487
71983
|
annotations: { readOnlyHint: true },
|
|
71488
71984
|
inputSchema: {
|
|
71985
|
+
view: viewParam(SW_VIEWS, { note: PERSON_VIEW_NOTE }),
|
|
71489
71986
|
id: external_exports.number().describe("User ID")
|
|
71490
71987
|
}
|
|
71491
|
-
}, async ({ id }) => {
|
|
71988
|
+
}, async ({ id, view }) => {
|
|
71492
71989
|
const data = await client2.request("GET", `/get_user/${id}`);
|
|
71493
|
-
return
|
|
71990
|
+
return minifiedResult(viewUser(resolveView(view, SW_VIEWS), data));
|
|
71494
71991
|
});
|
|
71495
71992
|
server.registerTool("sw_update_user", {
|
|
71496
71993
|
description: "Update the current user's profile fields. id must be the current user's id.",
|
|
@@ -71510,7 +72007,161 @@ function registerUserTools(server, client2) {
|
|
|
71510
72007
|
const gate = previewUnlessConfirmed(confirm, `Update current Splitwise user ${id} profile`, "POST", `/update_user/${id}`, previewBody);
|
|
71511
72008
|
if (gate) return gate;
|
|
71512
72009
|
const data = await client2.request("POST", `/update_user/${id}`, body);
|
|
71513
|
-
return
|
|
72010
|
+
return minifiedResult(data);
|
|
72011
|
+
});
|
|
72012
|
+
}
|
|
72013
|
+
|
|
72014
|
+
// node_modules/@chrischall/mcp-utils/dist/healthcheck/index.js
|
|
72015
|
+
function statusOf(err) {
|
|
72016
|
+
if (typeof err !== "object" || err === null)
|
|
72017
|
+
return void 0;
|
|
72018
|
+
const s2 = err.status ?? err.statusCode;
|
|
72019
|
+
return typeof s2 === "number" ? s2 : void 0;
|
|
72020
|
+
}
|
|
72021
|
+
var CREDENTIAL_ARMS = /* @__PURE__ */ new Set([
|
|
72022
|
+
"ok",
|
|
72023
|
+
"no_credential",
|
|
72024
|
+
"credential_rejected",
|
|
72025
|
+
"timeout",
|
|
72026
|
+
"http",
|
|
72027
|
+
"transport",
|
|
72028
|
+
"unknown"
|
|
72029
|
+
]);
|
|
72030
|
+
function isArm(kind) {
|
|
72031
|
+
return kind !== void 0 && CREDENTIAL_ARMS.has(kind);
|
|
72032
|
+
}
|
|
72033
|
+
function credentialHint(arm, prefix, hostLabel, source) {
|
|
72034
|
+
switch (arm) {
|
|
72035
|
+
case "ok":
|
|
72036
|
+
return `Credential from '${source}' works: ${hostLabel} accepted an authenticated request. If a real tool still fails, the problem is that tool, not auth.`;
|
|
72037
|
+
case "no_credential":
|
|
72038
|
+
return `No credential resolved. Nothing was available to authenticate with \u2014 sign in and reconnect the connector so ${prefix} receives a token, or set the documented environment variable.`;
|
|
72039
|
+
case "credential_rejected":
|
|
72040
|
+
return `${hostLabel} rejected the credential from '${source}'. It is present but no longer valid \u2014 most often expired or revoked upstream. Re-authenticate and reconnect; retrying will not fix it.`;
|
|
72041
|
+
case "timeout":
|
|
72042
|
+
return `The credential from '${source}' resolved, but ${hostLabel} did not answer in time. Usually transient \u2014 retry. If it persists, ${hostLabel} is slow or unreachable from here.`;
|
|
72043
|
+
case "http":
|
|
72044
|
+
return `${hostLabel} answered with an error status that is not an auth rejection. That is USUALLY a ${hostLabel}-side problem rather than an auth one \u2014 but a 404 here more often means the probe path is wrong than that ${hostLabel} is broken, so check error.message and probe.url before concluding anything about the credential.`;
|
|
72045
|
+
case "transport":
|
|
72046
|
+
return `Could not reach ${hostLabel} at all. Check network egress; the credential itself was never judged.`;
|
|
72047
|
+
default:
|
|
72048
|
+
return `Unexpected failure \u2014 see error.message.`;
|
|
72049
|
+
}
|
|
72050
|
+
}
|
|
72051
|
+
function registerCredentialHealthcheckTool(args) {
|
|
72052
|
+
const { server, prefix, hostLabel, probePath, resolveCredential, probeFn, classifyThrown, hints } = args;
|
|
72053
|
+
const probeUrl = probePath ? `https://${hostLabel}${probePath}` : void 0;
|
|
72054
|
+
server.registerTool(`${prefix}_healthcheck`, {
|
|
72055
|
+
title: "Verify credentials and upstream reachability",
|
|
72056
|
+
description: `Resolves the credential the way real tools do, then makes one authenticated request to ${hostLabel}. Reports which source supplied the credential, whether ${hostLabel} accepted it, the round-trip time, and a plain-English hint distinguishing 'no credential' from 'credential rejected' from 'a ${hostLabel}-side problem'. Call this when a real tool fails and you want to know which hop broke. Read-only; never returns the credential itself.`,
|
|
72057
|
+
annotations: {
|
|
72058
|
+
title: "Verify credentials and upstream reachability",
|
|
72059
|
+
readOnlyHint: true,
|
|
72060
|
+
idempotentHint: true,
|
|
72061
|
+
openWorldHint: true
|
|
72062
|
+
},
|
|
72063
|
+
inputSchema: {}
|
|
72064
|
+
}, async () => {
|
|
72065
|
+
let probeStarted = 0;
|
|
72066
|
+
let state;
|
|
72067
|
+
try {
|
|
72068
|
+
state = await resolveCredential();
|
|
72069
|
+
} catch (e2) {
|
|
72070
|
+
const classified = classifyThrown?.(e2);
|
|
72071
|
+
const result2 = {
|
|
72072
|
+
ok: false,
|
|
72073
|
+
// Still false, and still no source: a classification explains WHY
|
|
72074
|
+
// nothing resolved, it does not invent a credential that did.
|
|
72075
|
+
credential: { source: null, resolved: false },
|
|
72076
|
+
// No `url`: nothing was probed, and naming one implies it was tried.
|
|
72077
|
+
probe: { elapsed_ms: 0 },
|
|
72078
|
+
error: {
|
|
72079
|
+
kind: classified?.kind ?? "no_credential",
|
|
72080
|
+
message: truncateErrorMessage(messageOf(e2)),
|
|
72081
|
+
...classified?.detail !== void 0 ? { detail: classified.detail } : {}
|
|
72082
|
+
},
|
|
72083
|
+
// The hint must follow the KIND beside it. Falling back to
|
|
72084
|
+
// `no_credential`'s copy under a classified kind would state a cause
|
|
72085
|
+
// the kind contradicts — the same disagreement this path exists to
|
|
72086
|
+
// remove. So: an inline hint wins; else the classified arm's own
|
|
72087
|
+
// copy (consumer override first); else, for a kind this module has
|
|
72088
|
+
// no copy for, the neutral `unknown` text rather than one that
|
|
72089
|
+
// asserts a cause; else the unclassified `no_credential` default.
|
|
72090
|
+
hint: classified?.hint ?? (isArm(classified?.kind) ? hints?.[classified.kind] ?? credentialHint(classified.kind, prefix, hostLabel, null) : classified !== void 0 ? credentialHint("unknown", prefix, hostLabel, null) : hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null))
|
|
72091
|
+
};
|
|
72092
|
+
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
72093
|
+
}
|
|
72094
|
+
const credential = {
|
|
72095
|
+
source: state.source,
|
|
72096
|
+
resolved: state.source !== null,
|
|
72097
|
+
...state.detail !== void 0 ? { detail: state.detail } : {}
|
|
72098
|
+
};
|
|
72099
|
+
if (!credential.resolved) {
|
|
72100
|
+
const result2 = {
|
|
72101
|
+
ok: false,
|
|
72102
|
+
credential,
|
|
72103
|
+
probe: { elapsed_ms: 0 },
|
|
72104
|
+
error: { kind: "no_credential", message: "no credential source resolved" },
|
|
72105
|
+
hint: hints?.no_credential ?? credentialHint("no_credential", prefix, hostLabel, null)
|
|
72106
|
+
};
|
|
72107
|
+
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
72108
|
+
}
|
|
72109
|
+
let arm = "ok";
|
|
72110
|
+
let error51;
|
|
72111
|
+
let status;
|
|
72112
|
+
let customHint;
|
|
72113
|
+
probeStarted = Date.now();
|
|
72114
|
+
try {
|
|
72115
|
+
await probeFn();
|
|
72116
|
+
} catch (e2) {
|
|
72117
|
+
status = statusOf(e2);
|
|
72118
|
+
const aborted2 = e2 instanceof Error && e2.name === "AbortError";
|
|
72119
|
+
arm = status === 401 || status === 403 ? "credential_rejected" : status !== void 0 ? "http" : aborted2 || /timeout|timed out|ETIMEDOUT/i.test(messageOf(e2)) ? "timeout" : /fetch failed|ENOTFOUND|ECONNREFUSED|ECONNRESET|network/i.test(messageOf(e2)) ? "transport" : "unknown";
|
|
72120
|
+
let kind = arm;
|
|
72121
|
+
let detail;
|
|
72122
|
+
const custom2 = classifyThrown?.(e2);
|
|
72123
|
+
if (custom2) {
|
|
72124
|
+
kind = custom2.kind;
|
|
72125
|
+
customHint = custom2.hint;
|
|
72126
|
+
detail = custom2.detail;
|
|
72127
|
+
}
|
|
72128
|
+
error51 = {
|
|
72129
|
+
kind,
|
|
72130
|
+
// Redacted AND bounded before it reaches the result: an upstream
|
|
72131
|
+
// failure routinely quotes what it was sent, and a healthcheck is
|
|
72132
|
+
// the tool people paste into a chat when something is broken.
|
|
72133
|
+
message: truncateErrorMessage(messageOf(e2)),
|
|
72134
|
+
...detail !== void 0 ? { detail } : {}
|
|
72135
|
+
};
|
|
72136
|
+
}
|
|
72137
|
+
const result = {
|
|
72138
|
+
ok: error51 === void 0,
|
|
72139
|
+
credential,
|
|
72140
|
+
probe: {
|
|
72141
|
+
...probeUrl ? { url: probeUrl } : {},
|
|
72142
|
+
elapsed_ms: Date.now() - probeStarted,
|
|
72143
|
+
...status !== void 0 ? { status } : {}
|
|
72144
|
+
},
|
|
72145
|
+
...error51 ? { error: error51 } : {},
|
|
72146
|
+
hint: customHint ?? hints?.[arm] ?? credentialHint(arm, prefix, hostLabel, state.source)
|
|
72147
|
+
};
|
|
72148
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
72149
|
+
});
|
|
72150
|
+
}
|
|
72151
|
+
|
|
72152
|
+
// src/tools/healthcheck.ts
|
|
72153
|
+
function registerHealthcheckTools(server, client2) {
|
|
72154
|
+
registerCredentialHealthcheckTool({
|
|
72155
|
+
server,
|
|
72156
|
+
prefix: "sw",
|
|
72157
|
+
hostLabel: "secure.splitwise.com",
|
|
72158
|
+
probePath: "/api/v3.0/get_current_user",
|
|
72159
|
+
resolveCredential: async () => client2.describeCredential(),
|
|
72160
|
+
probeFn: () => client2.request("GET", "/get_current_user"),
|
|
72161
|
+
hints: {
|
|
72162
|
+
no_credential: "No Splitwise API key resolved. Set SPLITWISE_API_KEY, or reconnect the connector so it supplies one.",
|
|
72163
|
+
credential_rejected: "Splitwise rejected the API key. Generate a new one at https://secure.splitwise.com/apps and update SPLITWISE_API_KEY (or reconnect the connector)."
|
|
72164
|
+
}
|
|
71514
72165
|
});
|
|
71515
72166
|
}
|
|
71516
72167
|
|
|
@@ -71518,20 +72169,24 @@ function registerUserTools(server, client2) {
|
|
|
71518
72169
|
function registerGroupTools(server, client2) {
|
|
71519
72170
|
server.registerTool("sw_list_groups", {
|
|
71520
72171
|
description: "List all Splitwise groups the current user belongs to. Returns id, name, and members for each group. Use this to resolve a group name to its id.",
|
|
71521
|
-
annotations: { readOnlyHint: true }
|
|
71522
|
-
|
|
72172
|
+
annotations: { readOnlyHint: true },
|
|
72173
|
+
inputSchema: {
|
|
72174
|
+
view: viewParam(SW_VIEWS, { note: `compact drops the avatar/cover-photo URLs (60% of a live 51-group response, which does not fit in a tool result at all) and the whiteboard/reminder settings; "full" returns Splitwise's whole records.` })
|
|
72175
|
+
}
|
|
72176
|
+
}, async ({ view }) => {
|
|
71523
72177
|
const data = await client2.request("GET", "/get_groups");
|
|
71524
|
-
return
|
|
72178
|
+
return minifiedResult(viewGroups(resolveView(view, SW_VIEWS), data));
|
|
71525
72179
|
});
|
|
71526
72180
|
server.registerTool("sw_get_group", {
|
|
71527
72181
|
description: "Get details of a single Splitwise group including all members and balances.",
|
|
71528
72182
|
annotations: { readOnlyHint: true },
|
|
71529
72183
|
inputSchema: {
|
|
72184
|
+
view: viewParam(SW_VIEWS, { note: `compact drops the avatar/cover-photo URLs (60% of a live 51-group response, which does not fit in a tool result at all) and the whiteboard/reminder settings; "full" returns Splitwise's whole records.` }),
|
|
71530
72185
|
id: external_exports.number().describe("Group ID")
|
|
71531
72186
|
}
|
|
71532
|
-
}, async ({ id }) => {
|
|
72187
|
+
}, async ({ id, view }) => {
|
|
71533
72188
|
const data = await client2.request("GET", `/get_group/${id}`);
|
|
71534
|
-
return
|
|
72189
|
+
return minifiedResult(viewGroup(resolveView(view, SW_VIEWS), data));
|
|
71535
72190
|
});
|
|
71536
72191
|
server.registerTool("sw_create_group", {
|
|
71537
72192
|
description: "Create a new Splitwise group.",
|
|
@@ -71547,7 +72202,7 @@ function registerGroupTools(server, client2) {
|
|
|
71547
72202
|
const gate = previewUnlessConfirmed(confirm, `Create Splitwise group "${name}"`, "POST", "/create_group", body);
|
|
71548
72203
|
if (gate) return gate;
|
|
71549
72204
|
const data = await client2.request("POST", "/create_group", body);
|
|
71550
|
-
return
|
|
72205
|
+
return minifiedResult(data);
|
|
71551
72206
|
});
|
|
71552
72207
|
server.registerTool("sw_add_user_to_group", {
|
|
71553
72208
|
description: "Add a user to a Splitwise group. Provide user_id (preferred, use sw_list_friends to resolve a name) or first_name + last_name + email to invite by email.",
|
|
@@ -71572,7 +72227,7 @@ function registerGroupTools(server, client2) {
|
|
|
71572
72227
|
const gate = previewUnlessConfirmed(confirm, `Add a user to Splitwise group ${group_id} (may send an invite email)`, "POST", "/add_user_to_group", body);
|
|
71573
72228
|
if (gate) return gate;
|
|
71574
72229
|
const data = await client2.request("POST", "/add_user_to_group", body);
|
|
71575
|
-
return
|
|
72230
|
+
return minifiedResult(data);
|
|
71576
72231
|
});
|
|
71577
72232
|
server.registerTool("sw_remove_user_from_group", {
|
|
71578
72233
|
description: "Remove a user from a Splitwise group. Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it removes the user.",
|
|
@@ -71586,7 +72241,7 @@ function registerGroupTools(server, client2) {
|
|
|
71586
72241
|
const gate = previewUnlessConfirmed(confirm, `Remove user ${user_id} from Splitwise group ${group_id}`, "POST", "/remove_user_from_group", { group_id, user_id });
|
|
71587
72242
|
if (gate) return gate;
|
|
71588
72243
|
const data = await client2.request("POST", "/remove_user_from_group", { group_id, user_id });
|
|
71589
|
-
return
|
|
72244
|
+
return minifiedResult(data);
|
|
71590
72245
|
});
|
|
71591
72246
|
server.registerTool("sw_delete_group", {
|
|
71592
72247
|
description: "Soft-delete a Splitwise group. Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it deletes.",
|
|
@@ -71599,7 +72254,7 @@ function registerGroupTools(server, client2) {
|
|
|
71599
72254
|
const gate = previewUnlessConfirmed(confirm, `Soft-delete Splitwise group ${id}`, "POST", `/delete_group/${id}`);
|
|
71600
72255
|
if (gate) return gate;
|
|
71601
72256
|
const data = await client2.request("POST", `/delete_group/${id}`);
|
|
71602
|
-
return
|
|
72257
|
+
return minifiedResult(data);
|
|
71603
72258
|
});
|
|
71604
72259
|
server.registerTool("sw_undelete_group", {
|
|
71605
72260
|
description: "Restore a soft-deleted Splitwise group.",
|
|
@@ -71608,18 +72263,21 @@ function registerGroupTools(server, client2) {
|
|
|
71608
72263
|
}
|
|
71609
72264
|
}, async ({ id }) => {
|
|
71610
72265
|
const data = await client2.request("POST", `/undelete_group/${id}`);
|
|
71611
|
-
return
|
|
72266
|
+
return minifiedResult(data);
|
|
71612
72267
|
});
|
|
71613
72268
|
}
|
|
71614
72269
|
|
|
71615
72270
|
// src/tools/friends.ts
|
|
71616
72271
|
function registerFriendTools(server, client2) {
|
|
71617
72272
|
server.registerTool("sw_list_friends", {
|
|
71618
|
-
description: "List all Splitwise friends
|
|
71619
|
-
annotations: { readOnlyHint: true }
|
|
71620
|
-
|
|
72273
|
+
description: "List all Splitwise friends. Use this to resolve a friend's name to a user_id before adding them to a group or building a custom expense split. The compact default returns id, name (first_name + last_name joined), email, registration_status and any non-empty balance per friend; pass view:'full' for Splitwise's raw records, which keep first_name and last_name separate.",
|
|
72274
|
+
annotations: { readOnlyHint: true },
|
|
72275
|
+
inputSchema: {
|
|
72276
|
+
view: viewParam(SW_VIEWS, { note: PERSON_VIEW_NOTE })
|
|
72277
|
+
}
|
|
72278
|
+
}, async ({ view }) => {
|
|
71621
72279
|
const data = await client2.request("GET", "/get_friends");
|
|
71622
|
-
return
|
|
72280
|
+
return minifiedResult(viewFriends(resolveView(view, SW_VIEWS), data));
|
|
71623
72281
|
});
|
|
71624
72282
|
server.registerTool("sw_create_friend", {
|
|
71625
72283
|
description: "Add a Splitwise friend by email (sends them an invite). Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it adds the friend.",
|
|
@@ -71635,7 +72293,7 @@ function registerFriendTools(server, client2) {
|
|
|
71635
72293
|
const gate = previewUnlessConfirmed(confirm, `Add ${user_email} as a Splitwise friend`, "POST", "/create_friend", body);
|
|
71636
72294
|
if (gate) return gate;
|
|
71637
72295
|
const data = await client2.request("POST", "/create_friend", body);
|
|
71638
|
-
return
|
|
72296
|
+
return minifiedResult(data);
|
|
71639
72297
|
});
|
|
71640
72298
|
server.registerTool("sw_delete_friend", {
|
|
71641
72299
|
description: "Remove a Splitwise friendship by user id. Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it removes the friendship.",
|
|
@@ -71648,7 +72306,7 @@ function registerFriendTools(server, client2) {
|
|
|
71648
72306
|
const gate = previewUnlessConfirmed(confirm, `Remove Splitwise friendship with user ${id}`, "POST", `/delete_friend/${id}`);
|
|
71649
72307
|
if (gate) return gate;
|
|
71650
72308
|
const data = await client2.request("POST", `/delete_friend/${id}`);
|
|
71651
|
-
return
|
|
72309
|
+
return minifiedResult(data);
|
|
71652
72310
|
});
|
|
71653
72311
|
}
|
|
71654
72312
|
|
|
@@ -71685,6 +72343,7 @@ function registerExpenseTools(server, client2) {
|
|
|
71685
72343
|
description: "List or search Splitwise expenses. All filters are optional. Use group_id to filter by group, dated_after/dated_before for date ranges.",
|
|
71686
72344
|
annotations: { readOnlyHint: true },
|
|
71687
72345
|
inputSchema: {
|
|
72346
|
+
view: viewParam(SW_VIEWS, { note: `compact keeps the share breakdown, repayments and receipt presence and drops the avatars and the repeat/reminder/transaction block; "full" returns Splitwise's whole records.` }),
|
|
71688
72347
|
group_id: external_exports.number().describe("Only expenses in this group").optional(),
|
|
71689
72348
|
friend_id: external_exports.number().describe("Only expenses with this friend").optional(),
|
|
71690
72349
|
dated_after: external_exports.string().describe("ISO 8601 date \u2014 only expenses on or after this date").optional(),
|
|
@@ -71706,17 +72365,18 @@ function registerExpenseTools(server, client2) {
|
|
|
71706
72365
|
offset: args.offset
|
|
71707
72366
|
});
|
|
71708
72367
|
const data = await client2.request("GET", `/get_expenses${qs2}`);
|
|
71709
|
-
return
|
|
72368
|
+
return minifiedResult(viewExpenses(resolveView(args.view, SW_VIEWS), data));
|
|
71710
72369
|
});
|
|
71711
72370
|
server.registerTool("sw_get_expense", {
|
|
71712
72371
|
description: "Get full details of a single Splitwise expense by id.",
|
|
71713
72372
|
annotations: { readOnlyHint: true },
|
|
71714
72373
|
inputSchema: {
|
|
72374
|
+
view: viewParam(SW_VIEWS, { note: `compact keeps the share breakdown, repayments and receipt presence and drops the avatars and the repeat/reminder/transaction block; "full" returns Splitwise's whole records.` }),
|
|
71715
72375
|
id: external_exports.number().describe("Expense ID")
|
|
71716
72376
|
}
|
|
71717
|
-
}, async ({ id }) => {
|
|
72377
|
+
}, async ({ id, view }) => {
|
|
71718
72378
|
const data = await client2.request("GET", `/get_expense/${id}`);
|
|
71719
|
-
return
|
|
72379
|
+
return minifiedResult(viewExpense(resolveView(view, SW_VIEWS), data));
|
|
71720
72380
|
});
|
|
71721
72381
|
server.registerTool("sw_create_expense", {
|
|
71722
72382
|
description: 'Create a Splitwise expense. Use split_equally:true to split evenly among group members, or provide a users array for custom per-person splits (paid_share and owed_share as decimal strings like "25.00"). cost must be a decimal string. Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it creates the expense.',
|
|
@@ -71737,7 +72397,7 @@ function registerExpenseTools(server, client2) {
|
|
|
71737
72397
|
const gate = previewUnlessConfirmed(args.confirm, `Create a Splitwise expense "${args.description}" (${args.cost}) \u2014 notifies group members`, "POST", "/create_expense", body);
|
|
71738
72398
|
if (gate) return gate;
|
|
71739
72399
|
const data = await client2.request("POST", "/create_expense", body);
|
|
71740
|
-
return
|
|
72400
|
+
return minifiedResult(data);
|
|
71741
72401
|
});
|
|
71742
72402
|
server.registerTool("sw_update_expense", {
|
|
71743
72403
|
description: "Edit an existing Splitwise expense. Provide expense_id and any fields to change. For custom split updates, the full users array must be provided (the API replaces the entire split). Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it updates the expense.",
|
|
@@ -71759,7 +72419,7 @@ function registerExpenseTools(server, client2) {
|
|
|
71759
72419
|
const gate = previewUnlessConfirmed(args.confirm, `Update Splitwise expense ${expense_id} \u2014 notifies group members`, "POST", `/update_expense/${expense_id}`, body);
|
|
71760
72420
|
if (gate) return gate;
|
|
71761
72421
|
const data = await client2.request("POST", `/update_expense/${expense_id}`, body);
|
|
71762
|
-
return
|
|
72422
|
+
return minifiedResult(data);
|
|
71763
72423
|
});
|
|
71764
72424
|
server.registerTool("sw_delete_expense", {
|
|
71765
72425
|
description: "Soft-delete a Splitwise expense by id. Returns {success: true} on success. Use sw_undelete_expense to restore. Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it deletes.",
|
|
@@ -71772,7 +72432,7 @@ function registerExpenseTools(server, client2) {
|
|
|
71772
72432
|
const gate = previewUnlessConfirmed(confirm, `Soft-delete Splitwise expense ${id}`, "POST", `/delete_expense/${id}`);
|
|
71773
72433
|
if (gate) return gate;
|
|
71774
72434
|
const data = await client2.request("POST", `/delete_expense/${id}`);
|
|
71775
|
-
return
|
|
72435
|
+
return minifiedResult(data);
|
|
71776
72436
|
});
|
|
71777
72437
|
server.registerTool("sw_undelete_expense", {
|
|
71778
72438
|
description: "Restore a soft-deleted Splitwise expense.",
|
|
@@ -71781,7 +72441,7 @@ function registerExpenseTools(server, client2) {
|
|
|
71781
72441
|
}
|
|
71782
72442
|
}, async ({ id }) => {
|
|
71783
72443
|
const data = await client2.request("POST", `/undelete_expense/${id}`);
|
|
71784
|
-
return
|
|
72444
|
+
return minifiedResult(data);
|
|
71785
72445
|
});
|
|
71786
72446
|
}
|
|
71787
72447
|
|
|
@@ -71916,7 +72576,7 @@ function registerReceiptTools(server, client2) {
|
|
|
71916
72576
|
].join(" ")
|
|
71917
72577
|
);
|
|
71918
72578
|
}
|
|
71919
|
-
const result =
|
|
72579
|
+
const result = minifiedResult({
|
|
71920
72580
|
expense_id: id,
|
|
71921
72581
|
size: served,
|
|
71922
72582
|
...served !== wanted ? { requested_size: wanted, note: `Expense ${id} has no ${wanted} rendition; fetched ${served}.` } : {},
|
|
@@ -71949,34 +72609,38 @@ function registerReceiptTools(server, client2) {
|
|
|
71949
72609
|
function registerUtilityTools(server, client2) {
|
|
71950
72610
|
server.registerTool("sw_get_notifications", {
|
|
71951
72611
|
description: "Get recent Splitwise activity notifications for the current user.",
|
|
71952
|
-
annotations: { readOnlyHint: true }
|
|
71953
|
-
|
|
72612
|
+
annotations: { readOnlyHint: true },
|
|
72613
|
+
inputSchema: {
|
|
72614
|
+
view: viewParam(SW_VIEWS, { note: `compact drops the avatar URLs; "full" returns Splitwise's whole record.` })
|
|
72615
|
+
}
|
|
72616
|
+
}, async ({ view }) => {
|
|
71954
72617
|
const data = await client2.request("GET", "/get_notifications");
|
|
71955
|
-
return
|
|
72618
|
+
return minifiedResult(viewGeneric(resolveView(view, SW_VIEWS), data));
|
|
71956
72619
|
});
|
|
71957
72620
|
server.registerTool("sw_get_categories", {
|
|
71958
72621
|
description: "Get the hierarchical list of Splitwise expense categories. Use the returned id as category_id when creating expenses.",
|
|
71959
72622
|
annotations: { readOnlyHint: true }
|
|
71960
72623
|
}, async () => {
|
|
71961
72624
|
const data = await client2.request("GET", "/get_categories");
|
|
71962
|
-
return
|
|
72625
|
+
return minifiedResult(data);
|
|
71963
72626
|
});
|
|
71964
72627
|
server.registerTool("sw_get_currencies", {
|
|
71965
72628
|
description: "Get all Splitwise-supported currency codes and units. Use the currency_code value when creating expenses in non-default currencies.",
|
|
71966
72629
|
annotations: { readOnlyHint: true }
|
|
71967
72630
|
}, async () => {
|
|
71968
72631
|
const data = await client2.request("GET", "/get_currencies");
|
|
71969
|
-
return
|
|
72632
|
+
return minifiedResult(data);
|
|
71970
72633
|
});
|
|
71971
72634
|
server.registerTool("sw_get_comments", {
|
|
71972
72635
|
description: "Get all comments on a Splitwise expense.",
|
|
71973
72636
|
annotations: { readOnlyHint: true },
|
|
71974
72637
|
inputSchema: {
|
|
72638
|
+
view: viewParam(SW_VIEWS, { note: `compact drops the avatar URLs; "full" returns Splitwise's whole record.` }),
|
|
71975
72639
|
expense_id: external_exports.number().describe("Expense ID to get comments for")
|
|
71976
72640
|
}
|
|
71977
|
-
}, async ({ expense_id }) => {
|
|
72641
|
+
}, async ({ expense_id, view }) => {
|
|
71978
72642
|
const data = await client2.request("GET", `/get_comments${buildQueryString({ expense_id })}`);
|
|
71979
|
-
return
|
|
72643
|
+
return minifiedResult(viewGeneric(resolveView(view, SW_VIEWS), data));
|
|
71980
72644
|
});
|
|
71981
72645
|
server.registerTool("sw_create_comment", {
|
|
71982
72646
|
description: "Add a comment to a Splitwise expense (visible to other participants). Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it posts.",
|
|
@@ -71990,7 +72654,7 @@ function registerUtilityTools(server, client2) {
|
|
|
71990
72654
|
const gate = previewUnlessConfirmed(confirm, `Comment on Splitwise expense ${expense_id} (visible to participants)`, "POST", "/create_comment", { expense_id, content });
|
|
71991
72655
|
if (gate) return gate;
|
|
71992
72656
|
const data = await client2.request("POST", "/create_comment", { expense_id, content });
|
|
71993
|
-
return
|
|
72657
|
+
return minifiedResult(data);
|
|
71994
72658
|
});
|
|
71995
72659
|
server.registerTool("sw_delete_comment", {
|
|
71996
72660
|
description: "Delete a comment by id. Without confirm:true this returns a dry-run preview and makes NO network call; with confirm:true it deletes.",
|
|
@@ -72003,19 +72667,20 @@ function registerUtilityTools(server, client2) {
|
|
|
72003
72667
|
const gate = previewUnlessConfirmed(confirm, `Delete Splitwise comment ${id}`, "POST", `/delete_comment/${id}`);
|
|
72004
72668
|
if (gate) return gate;
|
|
72005
72669
|
const data = await client2.request("POST", `/delete_comment/${id}`);
|
|
72006
|
-
return
|
|
72670
|
+
return minifiedResult(data);
|
|
72007
72671
|
});
|
|
72008
72672
|
}
|
|
72009
72673
|
|
|
72010
72674
|
// src/index.ts
|
|
72011
72675
|
await runMcp({
|
|
72012
72676
|
name: "splitwise-mcp",
|
|
72013
|
-
version: "2.
|
|
72677
|
+
version: "2.4.0",
|
|
72014
72678
|
// x-release-please-version
|
|
72015
72679
|
deps: client,
|
|
72016
72680
|
banner: "[splitwise-mcp] This project was developed and is maintained by AI (Claude Sonnet 4.6). Use at your own discretion.",
|
|
72017
72681
|
tools: [
|
|
72018
72682
|
registerUserTools,
|
|
72683
|
+
registerHealthcheckTools,
|
|
72019
72684
|
registerGroupTools,
|
|
72020
72685
|
registerFriendTools,
|
|
72021
72686
|
registerExpenseTools,
|