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/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(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
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 consumeIsZone(buffer) {
3149
- buffer.length = 0;
3150
- return true;
3151
- }
3152
- function consumeHextets(buffer, address, output) {
3153
- if (buffer.length) {
3154
- const hex3 = stringArrayToHexStripped(buffer);
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
- buffer.length = 0;
3178
+ return false;
3162
3179
  }
3163
3180
  return true;
3164
3181
  }
3165
- function getIPV6(input) {
3166
- let tokenCount = 0;
3167
- const output = { error: false, address: "", zone: "" };
3168
- const address = [];
3169
- const buffer = [];
3170
- let endipv6Encountered = false;
3171
- let endIpv6 = false;
3172
- let consume = consumeHextets;
3173
- for (let i2 = 0; i2 < input.length; i2++) {
3174
- const cursor = input[i2];
3175
- if (cursor === "[" || cursor === "]") {
3176
- continue;
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
- buffer.push(cursor);
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 (buffer.length) {
3205
- if (consume === consumeIsZone) {
3206
- output.zone = buffer.join("");
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
- output.address = address.join("");
3214
- return output;
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
- if (findToken(host, ":") < 2) {
3218
- return { host, isIPV6: false };
3219
- }
3220
- const ipv63 = getIPV6(host);
3221
- if (!ipv63.error) {
3222
- let newHost = ipv63.address;
3223
- let escapedHost = ipv63.address;
3224
- if (ipv63.zone) {
3225
- newHost += "%" + ipv63.zone;
3226
- escapedHost += "%25" + ipv63.zone;
3227
- }
3228
- return { host: newHost, isIPV6: true, escapedHost };
3229
- } else {
3230
- return { host, isIPV6: false };
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
- if (input[i2] === "%" && i2 + 2 < input.length) {
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(input[i2])) {
3365
- output += input[i2];
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
- output += escape(input[i2]);
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 = unescape(component.host);
3570
+ let host = component.host;
3395
3571
  if (!isIPv4(host)) {
3396
- const ipV6res = normalizeIPv6(host);
3397
- if (ipV6res.isIPV6 === true) {
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(String(component.port));
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 = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
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 [path, query] = wsComponent.resourceName.split("?");
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 = 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, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
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 { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3656
- const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3657
- if (baseMalformed || relativeMalformed) {
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.toLowerCase() === normalizedB.toLowerCase();
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 = escapePreservingEscapes(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
- parsed.host = ipv6result.host.toLowerCase();
3857
- isIP = ipv6result.isIPV6;
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 (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3876
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
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.scheme !== void 0) {
3887
- parsed.scheme = unescape(parsed.scheme);
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
- try {
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 === "string") {
3927
- const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
3928
- return malformedAuthorityOrPort ? void 0 : normalized;
4205
+ if (typeof uri !== "string" && typeof uri !== "object") {
4206
+ return void 0;
3929
4207
  }
3930
- if (typeof uri === "object") {
3931
- return serialize(uri, opts);
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 warn(e2) {
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 warn(`getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.`), t2;
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) : (warn(`Not a valid color format: "${e2}"`), [0, 0, 0]);
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
- warn(`Unrecognized drawing path operator: ${e2[n2 - 1]}`);
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 walk(e3) {
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) walk(t3);
34814
+ if (n3 !== null && t2.push({ str: n3 }), e3.children) for (let t3 of e3.children) walk2(t3);
34532
34815
  }
34533
- return walk(e2), n2;
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) && warn(`Timer is already running for ${e2}`), this.#e.set(e2, Date.now());
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) || warn(`Timer has not been started for ${e2}`), this.times.push({ name: e2, start: this.#e.get(e2), end: Date.now() }), this.#e.delete(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
- warn(e3), n2 = null;
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
- warn(`KeyboardManager: multiple keys in shortcut "${e2}"`);
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 && warn(`KeyboardManager: no key found in shortcut "${e2}"`), [t2, n2];
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
- warn(`paste: "${e3.message}".`);
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
- warn(`paste: "${e3.message}".`);
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
- warn(`Cannot load system font: ${e2.baseFontName}, installing it could help to improve PDF rendering.`), this.removeNativeFontFace(a2);
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 warn(`Failed to load font '${t3.family}': '${n2}'.`), e2.disableFontFace = true, n2;
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
- warn(`Load test font never loaded.`), t3();
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
- warn(`getPathGenerator - ignoring character: "${e3}".`);
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
- warn("LoopbackPort - cannot use an `aborted` signal.");
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) ? warn(`#createUrl: ignore "data:"-URL for performance reasons.`) : this.#e = updateUrlHash(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) && warn(`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;
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()) : warn(`Type3 character "${h2.operatorListId}" is not available.`);
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
- warn(`Dependent image isn't ready yet`);
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
- warn(`Dependent image isn't ready yet`);
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) : (warn(`Missing or invalid "Content-Range" header.`), n2.onError(0));
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 warn(`Optional content group not found: ${t3}`), true;
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 : (warn(`Optional content group not found: ${e2.id}`), true);
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 warn(`Optional content group not found: ${t2}`), true;
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 warn(`Optional content group not found: ${t2}`), true;
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 warn(`Optional content group not found: ${t2}`), true;
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 warn(`Optional content group not found: ${t2}`), true;
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 warn(`Unknown optional content policy ${e2.policy}.`), true;
40830
+ return warn2(`Unknown optional content policy ${e2.policy}.`), true;
40548
40831
  }
40549
- return warn(`Unknown group type ${e2.type}.`), true;
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
- warn(`Optional content group not found: ${e2}`);
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
- warn(`Ignoring additional textDivs for performance reasons.`), this.#n = true;
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 && warn("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;
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 ||= (warn(`Setting up fake worker.`), true), PDFWorker._setupFakeWorkerGlobal.then((e2) => {
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
- warn(`getRenderingIntent - invalid intent: ${e2}`);
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
- warn(`getRenderingIntent - invalid annotationMode: ${t2}`);
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
- warn(`Error during font loading: ${e3}`), this.commonObjs.resolve(t3, e3);
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 && warn("saveDocument called while `annotationStorage` is empty, please use the getData-method instead.");
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
- warn(`Unimplemented border style: beveled`);
42474
+ warn2(`Unimplemented border style: beveled`);
42192
42475
  break;
42193
42476
  case Po.INSET:
42194
- warn(`Unimplemented border style: inset`);
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
- warn(`_getElementsByName - element not allowed: ${i2}`);
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
- warn('_bindResetFormAction - "resetForm" action not supported, ensure that the `fieldObjects` parameter is provided.'), n2 || (e2.onclick = () => false);
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
- warn(`_bindResetFormAction - element not allowed: ${t3}`);
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 warn(`decompressSignature: ${e3}`), null;
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/index.js
70949
- function textResult(data) {
70950
- return {
70951
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
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 key = opts?.apiKey ?? readEnvVar("SPLITWISE_API_KEY");
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 textResult({
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 (id, first_name, last_name, email). Use the returned id when building custom expense splits.",
71480
- annotations: { readOnlyHint: true }
71481
- }, async () => {
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 textResult(data);
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 textResult(data);
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 textResult(data);
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
- }, async () => {
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 with their id, first_name, last_name, and email. Use this to resolve a friend's name to a user_id before adding them to a group or building a custom expense split.",
71619
- annotations: { readOnlyHint: true }
71620
- }, async () => {
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 = textResult({
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
- }, async () => {
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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 textResult(data);
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.2.3",
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,