@fruggr/zendesk-mcp-server 3.7.0 → 3.9.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/index.js CHANGED
@@ -7911,6 +7911,11 @@ const ConfigSchema = object({
7911
7911
  oauthMasterSecretFile: string().min(1).optional(),
7912
7912
  /** HTTP only: the grant store URI; defaults to a file in the config dir. */
7913
7913
  oauthStore: string().url().optional(),
7914
+ /**
7915
+ * HTTP only: where the admins' instructions, prompts and skills live
7916
+ * (`file://<dir>` or `memory://`); defaults to a folder in the config dir.
7917
+ */
7918
+ customizationStore: string().url().optional(),
7914
7919
  /** HTTP only: CIMD client ids added to the consent-skip allowlist. */
7915
7920
  oauthTrustedClients: array(string().url()).default([]),
7916
7921
  /** HTTP only: whether the built-in allowlist (claude.ai, ChatGPT) applies. */
@@ -7975,21 +7980,42 @@ const portEnv = (name) => {
7975
7980
  return env.value === void 0 ? void 0 : parsePort(env.value, env.name);
7976
7981
  };
7977
7982
  /**
7978
- * Split a raw `--brand-ids` / `ZENDESK_BRAND_IDS` value into its entries.
7979
- * Comma-separated, entries trimmed; an empty entry (from `a,,b`, a leading/
7980
- * trailing comma, or a whitespace-only value) is a misconfiguration, not an
7981
- * ignored blank: it fails at startup naming the knob. Format validation only —
7982
- * whether an id/subdomain exists is decided lazily, on first use (no token is
7983
- * available at startup on either transport).
7983
+ * Split a raw comma-separated value (`ZENDESK_BRAND_IDS`, `MCP_NAMESPACES`) into
7984
+ * its entries, trimmed. An empty entry (from `a,,b`, a leading/trailing comma,
7985
+ * or a whitespace-only value) is a misconfiguration, not an ignored blank: it
7986
+ * fails at startup naming the knob, unlike the lenient `splitList`.
7984
7987
  */
7988
+ const splitStrictList = (raw, emptyEntryMessage) => {
7989
+ const entries = raw.split(",").map((entry) => entry.trim());
7990
+ if (entries.some((entry) => entry.length === 0)) throw createStartupError(emptyEntryMessage, STARTUP_DOCS.environment);
7991
+ return entries;
7992
+ };
7985
7993
  const parseBrandIds = (raw) => {
7986
7994
  if (raw === void 0) return void 0;
7987
- const entries = raw.split(",").map((entry) => entry.trim());
7988
- if (entries.some((entry) => entry.length === 0)) throw createStartupError("Invalid --brand-ids / ZENDESK_BRAND_IDS value: empty entry. Expected a comma-separated list of brand ids or subdomains, or \"all\".", STARTUP_DOCS.environment);
7989
- const normalized = entries.map((entry) => entry.toLowerCase());
7995
+ const normalized = splitStrictList(raw, "Invalid --brand-ids / ZENDESK_BRAND_IDS value: empty entry. Expected a comma-separated list of brand ids or subdomains, or \"all\".").map((entry) => entry.toLowerCase());
7990
7996
  if (normalized.length > 1 && normalized.includes("all")) throw createStartupError("Invalid --brand-ids / ZENDESK_BRAND_IDS value: \"all\" cannot be combined with brand ids or subdomains.", STARTUP_DOCS.environment);
7991
7997
  return [...new Set(normalized)];
7992
7998
  };
7999
+ const ALL_NAMESPACES = "all";
8000
+ const expandAllNamespaces = (named, docs) => {
8001
+ const entries = [...new Set(named)];
8002
+ if (!entries.includes(ALL_NAMESPACES)) return entries;
8003
+ if (entries.length > 1) throw createStartupError("Invalid --namespace / MCP_NAMESPACES value: \"all\" cannot be combined with named namespaces.", docs);
8004
+ return [...Namespace.options];
8005
+ };
8006
+ /**
8007
+ * The namespaces the operator asked for, or undefined when neither
8008
+ * `--namespace` nor `MCP_NAMESPACES` is set. The flag wins and leaves the variable
8009
+ * unread. Entries stay strings: the `Namespace` enum in ConfigSchema rejects an
8010
+ * unknown one.
8011
+ */
8012
+ const resolveNamespaces = (cliValues) => {
8013
+ if (cliValues !== void 0) return expandAllNamespaces(cliValues, STARTUP_DOCS.cliReference);
8014
+ const raw = requireNonEmptyEnv("MCP_NAMESPACES");
8015
+ if (raw === void 0) return void 0;
8016
+ const entries = splitStrictList(raw, "Invalid MCP_NAMESPACES value: empty entry. Expected a comma-separated list of namespaces, or \"all\".");
8017
+ return expandAllNamespaces(entries, STARTUP_DOCS.environment);
8018
+ };
7993
8019
  const CLI_OPTIONS = {
7994
8020
  mode: { type: "string" },
7995
8021
  "brand-ids": { type: "string" },
@@ -8014,6 +8040,7 @@ const CLI_OPTIONS = {
8014
8040
  "callback-port": { type: "string" },
8015
8041
  "oauth-master-secret-file": { type: "string" },
8016
8042
  "oauth-store": { type: "string" },
8043
+ "customization-store": { type: "string" },
8017
8044
  "oauth-trusted-client": {
8018
8045
  type: "string",
8019
8046
  multiple: true
@@ -8045,6 +8072,7 @@ const FIELD_BY_FLAG = /* @__PURE__ */ new Map([
8045
8072
  ["cors-origin", "corsOrigins"],
8046
8073
  ["oauth-master-secret-file", "oauthMasterSecretFile"],
8047
8074
  ["oauth-store", "oauthStore"],
8075
+ ["customization-store", "customizationStore"],
8048
8076
  ["oauth-trusted-client", "oauthTrustedClients"],
8049
8077
  ["sla-reply-thresholds", "slaReplyThresholds"],
8050
8078
  ["sla-wait-thresholds", "slaWaitThresholds"],
@@ -8112,6 +8140,7 @@ const resolveOAuthServerSettings = (cli) => ({
8112
8140
  oauthMasterSecret: requireNonEmptyEnv("OAUTH_MASTER_SECRET"),
8113
8141
  oauthMasterSecretFile: cli.oauthMasterSecretFile ?? requireNonEmptyEnv("OAUTH_MASTER_SECRET_FILE"),
8114
8142
  oauthStore: cli.oauthStore ?? requireNonEmptyEnv("OAUTH_STORE"),
8143
+ customizationStore: cli.customizationStore ?? requireNonEmptyEnv("CUSTOMIZATION_STORE"),
8115
8144
  oauthTrustedClients: [...cli.oauthTrustedClients ?? [], ...splitList(readEnv("OAUTH_TRUSTED_CLIENTS").value)],
8116
8145
  defaultTrustedClients: cli.defaultTrustedClients
8117
8146
  });
@@ -8173,7 +8202,7 @@ const loadConfig = (argv = process.argv.slice(2)) => {
8173
8202
  const subdomain = cli.subdomain ?? requireNonEmptyEnv("ZENDESK_SUBDOMAIN") ?? "";
8174
8203
  const oauthClientId = requireNonEmptyEnv("ZENDESK_OAUTH_CLIENT_ID") ?? `${subdomain}_zendesk`;
8175
8204
  const mode = cli.tools?.length ? "all" : cli.mode ?? "namespace";
8176
- const namespaces = cli.namespaces ?? (cli.tools?.length ? [...Namespace.options] : void 0);
8205
+ const namespaces = resolveNamespaces(cli.namespaces) ?? (cli.tools?.length ? [...Namespace.options] : void 0);
8177
8206
  const callbackPort = cli.callbackPort ?? portEnv("OAUTH_CALLBACK_PORT");
8178
8207
  const brandIds = cli.brandIds ?? requireNonEmptyEnv("ZENDESK_BRAND_IDS");
8179
8208
  const hcResourceScheme = cli.hcResourceScheme ?? requireNonEmptyEnv("HC_RESOURCE_SCHEME");
@@ -8383,7 +8412,7 @@ const buildHelpCenterInstructions = (config) => {
8383
8412
  ].join("\n");
8384
8413
  };
8385
8414
  //#endregion
8386
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
8415
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/chunk-vYduTEsO.mjs
8387
8416
  var __create = Object.create;
8388
8417
  var __defProp = Object.defineProperty;
8389
8418
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -8415,7 +8444,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
8415
8444
  enumerable: true
8416
8445
  }) : target, mod));
8417
8446
  //#endregion
8418
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs
8447
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/dialects-CDpMbily.mjs
8419
8448
  /**
8420
8449
  * Canonical `$schema` URIs per supported dialect (http + https variants, trailing-`#` stripped).
8421
8450
  */
@@ -9875,6 +9904,8 @@ const ServerResultSchema = union([
9875
9904
  ListToolsResultSchema,
9876
9905
  SubscriptionsListenResultSchema
9877
9906
  ]);
9907
+ //#endregion
9908
+ //#region node_modules/.pnpm/@modelcontextprotocol+core@2.4.0/node_modules/@modelcontextprotocol/core/dist/auth-UMQCo-ad.mjs
9878
9909
  /**
9879
9910
  * Reusable URL validation that disallows `javascript:` scheme
9880
9911
  */
@@ -10078,7 +10109,7 @@ const OAuthTokenRevocationRequestSchema = object({
10078
10109
  token_type_hint: string().optional()
10079
10110
  }).strip();
10080
10111
  //#endregion
10081
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs
10112
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/src-DdvbAHrR.mjs
10082
10113
  /**
10083
10114
  * Cross-bundle `instanceof` support for the SDK error classes.
10084
10115
  *
@@ -14457,25 +14488,22 @@ const X_MCP_HEADER_KEY = "x-mcp-header";
14457
14488
  */
14458
14489
  const RFC9110_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
14459
14490
  /**
14460
- * JSON Schema `type` values the spec admits on an `x-mcp-header` property.
14461
- *
14462
- * The spec text names `integer`, `string`, `boolean` and explicitly excludes
14463
- * `number`. The published conformance referee at the pinned release ships its
14464
- * `http-custom-headers` scenario with two `type: "number"` `x-mcp-header`
14465
- * parameters and expects the client to mirror them, so `number` is accepted
14466
- * here so that the conformance gate passes; the discrepancy is tracked
14467
- * upstream. Everything else (`object`, `array`, `null`, absent) is rejected.
14491
+ * JSON Schema `type` values the spec admits on an `x-mcp-header` property:
14492
+ * `integer`, `string` and `boolean`. `number` is explicitly not permitted
14493
+ * (2026-07-28 Streamable HTTP, "Schema Extension"), so a tool that annotates a
14494
+ * `number`-typed property is invalid (the server still checks its headers).
14495
+ * Everything else (`object`, `array`, `null`, absent) is rejected too.
14468
14496
  */
14469
14497
  const PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
14470
14498
  "string",
14471
14499
  "integer",
14472
- "boolean",
14473
- "number"
14500
+ "boolean"
14474
14501
  ]);
14475
14502
  /**
14476
14503
  * Scan a tool's JSON-serialized `inputSchema` for `x-mcp-header` declarations
14477
14504
  * and validate every constraint the spec places on them. Returns either the
14478
- * collected declarations (possibly empty) or the first violated constraint.
14505
+ * collected declarations (possibly empty) or the first violated constraint
14506
+ * (with the declarations too, when `number`-typed declarations are the only fault).
14479
14507
  *
14480
14508
  * The walk descends through `properties` at any depth (the spec's "any nesting
14481
14509
  * depth" clause). The static-reachability MUST is enforced as a structural
@@ -14488,6 +14516,7 @@ const PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
14488
14516
  function scanXMcpHeaderDeclarations(inputSchema) {
14489
14517
  const declarations = [];
14490
14518
  const seenLower = /* @__PURE__ */ new Map();
14519
+ let numberFault;
14491
14520
  const visit = (node, path, reachable) => {
14492
14521
  if (node === null || typeof node !== "object") return void 0;
14493
14522
  const schema = node;
@@ -14497,7 +14526,11 @@ function scanXMcpHeaderDeclarations(inputSchema) {
14497
14526
  if (typeof raw !== "string" || raw.length === 0) return `${pathName(path)}: x-mcp-header MUST be a non-empty string`;
14498
14527
  if (!RFC9110_TOKEN.test(raw)) return `${pathName(path)}: x-mcp-header '${raw}' is not a valid RFC 9110 token (no spaces, control characters or HTTP delimiters)`;
14499
14528
  const type = typeof schema.type === "string" ? schema.type : void 0;
14500
- if (type === void 0 || !PERMITTED_X_MCP_HEADER_TYPES.has(type)) return `${pathName(path)}: x-mcp-header is only permitted on primitive-typed properties (string, integer, boolean); got ${type ?? "<none>"}`;
14529
+ if (type === void 0 || !PERMITTED_X_MCP_HEADER_TYPES.has(type)) {
14530
+ const fault$1 = `${pathName(path)}: x-mcp-header is only permitted on primitive-typed properties (string, integer, boolean); got ${type ?? "<none>"}`;
14531
+ if (type !== "number") return fault$1;
14532
+ numberFault ??= fault$1;
14533
+ }
14501
14534
  const lower = raw.toLowerCase();
14502
14535
  const prior = seenLower.get(lower);
14503
14536
  if (prior !== void 0) return `x-mcp-header '${raw}' is not case-insensitively unique (also declared as '${prior}')`;
@@ -14524,12 +14557,17 @@ function scanXMcpHeaderDeclarations(inputSchema) {
14524
14557
  }
14525
14558
  };
14526
14559
  const fault = visit(inputSchema, [], true);
14527
- return fault === void 0 ? {
14560
+ if (fault !== void 0) return {
14561
+ valid: false,
14562
+ reason: numberFault ?? fault
14563
+ };
14564
+ return numberFault === void 0 ? {
14528
14565
  valid: true,
14529
14566
  declarations
14530
14567
  } : {
14531
14568
  valid: false,
14532
- reason: fault
14569
+ reason: numberFault,
14570
+ declarations
14533
14571
  };
14534
14572
  }
14535
14573
  /**
@@ -14632,7 +14670,7 @@ function valueAtPath(root, path) {
14632
14670
  * A sentinel-carrying header whose payload is not canonical Base64 / valid
14633
14671
  * UTF-8 is rejected as invalid characters.
14634
14672
  *
14635
- * Integer-typed declarations are compared numerically (the spec's SHOULD —
14673
+ * Integer- and number-typed declarations are compared numerically (the spec's SHOULD —
14636
14674
  * `42.0` and `42` are equal); everything else is compared as decoded strings.
14637
14675
  *
14638
14676
  * Returns `undefined` when every check passes, or an
@@ -15202,22 +15240,26 @@ function standardSchemaToJsonSchema(schema, io = "input") {
15202
15240
  ...result
15203
15241
  };
15204
15242
  }
15243
+ const LOCAL_DEFINITION_REF = /^#\/(\$defs|definitions)\/([^/~%]+)$/;
15205
15244
  /**
15206
15245
  * A typeless JSON Schema root is "provably object-shaped" when either it carries object keywords
15207
15246
  * directly (`properties`/`patternProperties`/`additionalProperties`/`required`), or it is a
15208
15247
  * composition (`oneOf`/`anyOf`/`allOf`) whose every member is itself `type:'object'` or recursively
15209
- * provably object-shaped (e.g. a nested `discriminatedUnion`). `$ref` is not followed. Used to
15248
+ * provably object-shaped (e.g. a nested `discriminatedUnion`). A `$ref` is followed one hop, to an
15249
+ * entry of the root's `$defs`/`definitions` that is itself `type:'object'`. Used to
15210
15250
  * decide whether stamping `type:'object'` is safe (redundant-but-valid) versus self-contradictory.
15211
15251
  */
15212
- function isProvablyObjectShapedRoot(schema) {
15252
+ function isProvablyObjectShapedRoot(schema, root = schema) {
15213
15253
  if ("properties" in schema || "patternProperties" in schema || "additionalProperties" in schema || "required" in schema) return true;
15254
+ const ref = typeof schema.$ref === "string" && LOCAL_DEFINITION_REF.exec(schema.$ref);
15255
+ if (ref && root[ref[1]]?.[ref[2]]?.type === "object") return true;
15214
15256
  for (const key of [
15215
15257
  "oneOf",
15216
15258
  "anyOf",
15217
15259
  "allOf"
15218
15260
  ]) {
15219
15261
  const members = schema[key];
15220
- if (Array.isArray(members) && members.length > 0) return members.every((m) => m !== null && typeof m === "object" && (m.type === "object" || isProvablyObjectShapedRoot(m)));
15262
+ if (Array.isArray(members) && members.length > 0) return members.every((m) => m !== null && typeof m === "object" && (m.type === "object" || isProvablyObjectShapedRoot(m, "$id" in m ? {} : root)));
15221
15263
  }
15222
15264
  return false;
15223
15265
  }
@@ -17187,7 +17229,7 @@ function normalizeRawShapeSchema(schema) {
17187
17229
  return schema;
17188
17230
  }
17189
17231
  //#endregion
17190
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/ajvProvider-CEoC__sr.mjs
17232
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/ajvProvider-DyjnscPj.mjs
17191
17233
  var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
17192
17234
  Object.defineProperty(exports, "__esModule", { value: true });
17193
17235
  exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = void 0;
@@ -19865,6 +19907,28 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
19865
19907
  const isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
19866
19908
  /** @type {(value: string) => boolean} */
19867
19909
  const 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);
19910
+ /** @type {(value: string) => boolean} */
19911
+ const isPort = RegExp.prototype.test.bind(/^\d*$/u);
19912
+ /** @type {(value: string) => boolean} */
19913
+ const isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
19914
+ /** @type {(value: string) => boolean} */
19915
+ const isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
19916
+ /** @type {(value: string) => boolean} */
19917
+ const isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
19918
+ /** @type {(value: string) => boolean} */
19919
+ const isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
19920
+ /** @type {(value: string) => boolean} */
19921
+ const isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
19922
+ const BYTE_HEX = new Array(256);
19923
+ {
19924
+ const HEX_DIGITS = "0123456789ABCDEF";
19925
+ for (let i = 0; i < 256; i++) BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
19926
+ }
19927
+ function percentEncodeNonAscii(cp) {
19928
+ if (cp < 2048) return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
19929
+ if (cp < 65536) return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
19930
+ return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
19931
+ }
19868
19932
  /**
19869
19933
  * @param {Array<string>} input
19870
19934
  * @returns {string}
@@ -19887,119 +19951,162 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
19887
19951
  }
19888
19952
  return acc;
19889
19953
  }
19890
- /**
19891
- * @typedef {Object} GetIPV6Result
19892
- * @property {boolean} error - Indicates if there was an error parsing the IPv6 address.
19893
- * @property {string} address - The parsed IPv6 address.
19894
- * @property {string} [zone] - The zone identifier, if present.
19895
- */
19954
+ /** @type {(value: string) => boolean} */
19955
+ const isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
19956
+ /** @type {(value: string) => boolean} */
19957
+ const isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
19958
+ /** @type {(value: string) => boolean} */
19959
+ const isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
19896
19960
  /**
19897
19961
  * @param {string} value
19898
19962
  * @returns {boolean}
19899
19963
  */
19900
19964
  const nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
19901
19965
  /**
19902
- * @param {Array<string>} buffer
19966
+ * @param {string} zone
19903
19967
  * @returns {boolean}
19904
19968
  */
19905
- function consumeIsZone(buffer) {
19906
- buffer.length = 0;
19969
+ function isZoneIdentifier(zone) {
19970
+ if (zone.length === 0) return false;
19971
+ for (let i = 0; i < zone.length; i++) {
19972
+ if (isZoneCharacter(zone[i])) continue;
19973
+ if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
19974
+ i += 2;
19975
+ continue;
19976
+ }
19977
+ return false;
19978
+ }
19907
19979
  return true;
19908
19980
  }
19909
19981
  /**
19910
- * @param {Array<string>} buffer
19911
- * @param {Array<string>} address
19912
- * @param {GetIPV6Result} output
19913
- * @returns {boolean}
19982
+ * Compresses the longest run of zero hextets to "::" per RFC 5952. A run of a
19983
+ * single zero hextet is left uncompressed. On ties the leftmost run wins.
19984
+ *
19985
+ * @param {string[]} hextets
19986
+ * @returns {string}
19914
19987
  */
19915
- function consumeHextets(buffer, address, output) {
19916
- if (buffer.length) {
19917
- const hex = stringArrayToHexStripped(buffer);
19918
- if (hex !== "") address.push(hex);
19919
- else {
19920
- output.error = true;
19921
- return false;
19988
+ function compressIPv6ZeroRun(hextets) {
19989
+ let bestStart = -1;
19990
+ let bestLength = 0;
19991
+ let runStart = -1;
19992
+ let runLength = 0;
19993
+ for (let i = 0; i < hextets.length; i++) if (hextets[i] === "0") {
19994
+ if (runStart === -1) runStart = i;
19995
+ runLength++;
19996
+ if (runLength > bestLength) {
19997
+ bestLength = runLength;
19998
+ bestStart = runStart;
19922
19999
  }
19923
- buffer.length = 0;
20000
+ } else {
20001
+ runStart = -1;
20002
+ runLength = 0;
19924
20003
  }
19925
- return true;
20004
+ if (bestLength < 2) return hextets.join(":");
20005
+ const head = hextets.slice(0, bestStart).join(":");
20006
+ const tail = hextets.slice(bestStart + bestLength).join(":");
20007
+ return head + "::" + tail;
19926
20008
  }
19927
20009
  /**
20010
+ * Validates an IPv6 address against the alternatives in RFC 3986 section
20011
+ * 3.2.2 and returns the same address with leading hextet zeroes removed.
20012
+ * An embedded IPv4 address counts as two hextets and is only valid at the end.
20013
+ *
19928
20014
  * @param {string} input
19929
- * @returns {GetIPV6Result}
19930
- */
19931
- function getIPV6(input) {
19932
- let tokenCount = 0;
19933
- const output = {
19934
- error: false,
19935
- address: "",
19936
- zone: ""
19937
- };
19938
- /** @type {Array<string>} */
19939
- const address = [];
19940
- /** @type {Array<string>} */
19941
- const buffer = [];
19942
- let endipv6Encountered = false;
19943
- let endIpv6 = false;
19944
- let consume = consumeHextets;
19945
- for (let i = 0; i < input.length; i++) {
19946
- const cursor = input[i];
19947
- if (cursor === "[" || cursor === "]") continue;
19948
- if (cursor === ":") {
19949
- if (endipv6Encountered === true) endIpv6 = true;
19950
- if (!consume(buffer, address, output)) break;
19951
- if (++tokenCount > 7) {
19952
- output.error = true;
19953
- break;
19954
- }
19955
- if (i > 0 && input[i - 1] === ":") endipv6Encountered = true;
19956
- address.push(":");
19957
- continue;
19958
- } else if (cursor === "%") {
19959
- if (!consume(buffer, address, output)) break;
19960
- consume = consumeIsZone;
19961
- } else {
19962
- buffer.push(cursor);
20015
+ * @returns {string|undefined}
20016
+ */
20017
+ function normalizeIPv6Address(input) {
20018
+ const compression = input.indexOf("::");
20019
+ if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
20020
+ const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
20021
+ const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
20022
+ if (compression !== -1) {
20023
+ if (left.length === 1 && left[0] === "") left.length = 0;
20024
+ if (right.length === 1 && right[0] === "") right.length = 0;
20025
+ }
20026
+ const parts = left.concat(right);
20027
+ let hextetCount = 0;
20028
+ for (let i = 0; i < parts.length; i++) {
20029
+ const part = parts[i];
20030
+ if (part === "") return void 0;
20031
+ if (part.indexOf(".") !== -1) {
20032
+ if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
20033
+ hextetCount += 2;
19963
20034
  continue;
19964
20035
  }
20036
+ if (!isHextet(part)) return void 0;
20037
+ parts[i] = parseInt(part, 16).toString(16);
20038
+ hextetCount++;
19965
20039
  }
19966
- if (buffer.length) if (consume === consumeIsZone) output.zone = buffer.join("");
19967
- else if (endIpv6) address.push(buffer.join(""));
19968
- else address.push(stringArrayToHexStripped(buffer));
19969
- output.address = address.join("");
19970
- return output;
20040
+ if (compression === -1) {
20041
+ if (hextetCount !== 8) return void 0;
20042
+ return compressIPv6ZeroRun(parts);
20043
+ }
20044
+ if (hextetCount >= 8) return void 0;
20045
+ const expanded = parts.slice(0, left.length);
20046
+ for (let i = hextetCount; i < 8; i++) expanded.push("0");
20047
+ for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
20048
+ return compressIPv6ZeroRun(expanded);
19971
20049
  }
19972
20050
  /**
19973
20051
  * @typedef {Object} NormalizeIPv6Result
19974
20052
  * @property {string} host - The normalized host.
19975
20053
  * @property {string} [escapedHost] - The escaped host.
19976
20054
  * @property {boolean} isIPV6 - Indicates if the host is an IPv6 address.
20055
+ * @property {boolean} [isIPVFuture] - Indicates if the host is an IPvFuture literal.
20056
+ * @property {boolean} [error] - Indicates if a bracketed IP literal is malformed.
19977
20057
  */
19978
20058
  /**
20059
+ * Validates and normalizes a bracketed IP literal. Raw zone separators remain
20060
+ * accepted for backwards compatibility, while encoded separators and zone
20061
+ * contents follow RFC 6874.
20062
+ *
19979
20063
  * @param {string} host
19980
20064
  * @returns {NormalizeIPv6Result}
19981
20065
  */
19982
20066
  function normalizeIPv6(host) {
19983
- if (findToken(host, ":") < 2) return {
20067
+ const bracketed = host[0] === "[" && host[host.length - 1] === "]";
20068
+ if ((host[0] === "[" || host[host.length - 1] === "]") && !bracketed) return {
19984
20069
  host,
19985
- isIPV6: false
20070
+ isIPV6: false,
20071
+ error: true
19986
20072
  };
19987
- const ipv6 = getIPV6(host);
19988
- if (!ipv6.error) {
19989
- let newHost = ipv6.address;
19990
- let escapedHost = ipv6.address;
19991
- if (ipv6.zone) {
19992
- newHost += "%" + ipv6.zone;
19993
- escapedHost += "%25" + ipv6.zone;
19994
- }
20073
+ let input = bracketed ? host.slice(1, -1) : host;
20074
+ if (bracketed && isIPvFuture(input)) {
20075
+ input = input.toLowerCase();
19995
20076
  return {
19996
- host: newHost,
19997
- isIPV6: true,
19998
- escapedHost
20077
+ host: `[${input}]`,
20078
+ escapedHost: input,
20079
+ isIPV6: false,
20080
+ isIPVFuture: true
20081
+ };
20082
+ }
20083
+ if (findToken(input, ":") < 2) return {
20084
+ host,
20085
+ isIPV6: false,
20086
+ error: bracketed
20087
+ };
20088
+ let zoneIdentifier = "";
20089
+ const zoneSeparator = input.indexOf("%");
20090
+ if (zoneSeparator !== -1) {
20091
+ const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
20092
+ zoneIdentifier = input.slice(zoneSeparator + separatorLength);
20093
+ if (!isZoneIdentifier(zoneIdentifier)) return {
20094
+ host,
20095
+ isIPV6: false,
20096
+ error: true
19999
20097
  };
20000
- } else return {
20098
+ input = input.slice(0, zoneSeparator);
20099
+ }
20100
+ const address = normalizeIPv6Address(input);
20101
+ if (address === void 0) return {
20001
20102
  host,
20002
- isIPV6: false
20103
+ isIPV6: false,
20104
+ error: true
20105
+ };
20106
+ return {
20107
+ host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
20108
+ escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
20109
+ isIPV6: true
20003
20110
  };
20004
20111
  }
20005
20112
  /**
@@ -20087,19 +20194,264 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20087
20194
  return output.join("");
20088
20195
  }
20089
20196
  /**
20090
- * @param {import('../types/index').URIComponent} component
20091
- * @param {boolean} esc
20092
- * @returns {import('../types/index').URIComponent}
20197
+ * Re-escape RFC 3986 gen-delims that must not appear literally in the host.
20198
+ * After the URI regex parses, these characters cannot be literal in the host
20199
+ * field, so any that appear after decoding came from percent-encoding and
20200
+ * must be restored to prevent authority structure changes.
20201
+ *
20202
+ * @param {string} host
20203
+ * @param {boolean} isIP - true for IPv4/IPv6 hosts (skip colon re-escaping)
20204
+ * @returns {string}
20093
20205
  */
20094
- function normalizeComponentEncoding(component, esc) {
20095
- const func = esc !== true ? escape : unescape;
20096
- if (component.scheme !== void 0) component.scheme = func(component.scheme);
20097
- if (component.userinfo !== void 0) component.userinfo = func(component.userinfo);
20098
- if (component.host !== void 0) component.host = func(component.host);
20099
- if (component.path !== void 0) component.path = func(component.path);
20100
- if (component.query !== void 0) component.query = func(component.query);
20101
- if (component.fragment !== void 0) component.fragment = func(component.fragment);
20102
- return component;
20206
+ const HOST_DELIMS = {
20207
+ "@": "%40",
20208
+ "/": "%2F",
20209
+ "?": "%3F",
20210
+ "#": "%23",
20211
+ ":": "%3A"
20212
+ };
20213
+ const HOST_DELIM_RE = /[@/?#:]/g;
20214
+ const HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
20215
+ function reescapeHostDelimiters(host, isIP) {
20216
+ const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
20217
+ re.lastIndex = 0;
20218
+ return host.replace(re, (ch) => HOST_DELIMS[ch]);
20219
+ }
20220
+ /**
20221
+ * Normalizes percent escapes and optionally decodes only unreserved ASCII bytes.
20222
+ * Reserved delimiters such as `%2F` stay escaped; `%2E` is unreserved.
20223
+ *
20224
+ * @param {string} input
20225
+ * @param {boolean} [decodeUnreserved=false]
20226
+ * @returns {string}
20227
+ */
20228
+ function normalizePercentEncoding(input, decodeUnreserved = false) {
20229
+ if (input.indexOf("%") === -1) return input;
20230
+ let output = "";
20231
+ for (let i = 0; i < input.length; i++) {
20232
+ if (input[i] === "%" && i + 2 < input.length) {
20233
+ const hex = input.slice(i + 1, i + 3);
20234
+ if (isHexPair(hex)) {
20235
+ const normalizedHex = hex.toUpperCase();
20236
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
20237
+ if (decodeUnreserved && isUnreserved(decoded)) output += decoded;
20238
+ else output += "%" + normalizedHex;
20239
+ i += 2;
20240
+ continue;
20241
+ }
20242
+ }
20243
+ output += input[i];
20244
+ }
20245
+ return output;
20246
+ }
20247
+ /**
20248
+ * Normalizes path data without turning reserved escapes into live path syntax.
20249
+ * Valid escapes are uppercased, raw unsafe characters are escaped, and only
20250
+ * unreserved bytes that are not `.` are decoded.
20251
+ *
20252
+ * @param {string} input
20253
+ * @returns {string}
20254
+ */
20255
+ function normalizePathEncoding(input) {
20256
+ let output = "";
20257
+ for (let i = 0; i < input.length; i++) {
20258
+ const ch = input[i];
20259
+ if (ch === "%" && i + 2 < input.length) {
20260
+ const hex = input.slice(i + 1, i + 3);
20261
+ if (isHexPair(hex)) {
20262
+ const normalizedHex = hex.toUpperCase();
20263
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
20264
+ if (decoded !== "." && isUnreserved(decoded)) output += decoded;
20265
+ else output += "%" + normalizedHex;
20266
+ i += 2;
20267
+ continue;
20268
+ }
20269
+ }
20270
+ if (isPathCharacter(ch)) output += ch;
20271
+ else {
20272
+ const code = input.charCodeAt(i);
20273
+ if (code < 128) output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
20274
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
20275
+ else if (code <= 56319 && i + 1 < input.length) {
20276
+ const low = input.charCodeAt(i + 1);
20277
+ if (low >= 56320 && low <= 57343) {
20278
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
20279
+ i++;
20280
+ } else output += percentEncodeNonAscii(65533);
20281
+ } else output += percentEncodeNonAscii(65533);
20282
+ }
20283
+ }
20284
+ return output;
20285
+ }
20286
+ /**
20287
+ * Serializes a path without rewriting reserved data. Raw RFC 3986 path
20288
+ * characters remain literal, valid escapes are preserved and uppercased, and
20289
+ * everything else is UTF-8 percent-encoded. In a path-noscheme, a colon in the
20290
+ * first segment must be escaped so the result cannot be parsed as a scheme.
20291
+ *
20292
+ * @param {string} input
20293
+ * @param {boolean} [pathNoScheme=false]
20294
+ * @returns {string}
20295
+ */
20296
+ function serializePathEncoding(input, pathNoScheme = false) {
20297
+ let output = "";
20298
+ let firstSegment = pathNoScheme && input[0] !== "/";
20299
+ for (let i = 0; i < input.length; i++) {
20300
+ const ch = input[i];
20301
+ if (ch === "%" && i + 2 < input.length) {
20302
+ const hex = input.slice(i + 1, i + 3);
20303
+ if (isHexPair(hex)) {
20304
+ output += "%" + hex.toUpperCase();
20305
+ i += 2;
20306
+ continue;
20307
+ }
20308
+ }
20309
+ if (ch === "/") firstSegment = false;
20310
+ if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) output += ch;
20311
+ else {
20312
+ const code = input.charCodeAt(i);
20313
+ if (code < 128) output += BYTE_HEX[code];
20314
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
20315
+ else if (code <= 56319 && i + 1 < input.length) {
20316
+ const low = input.charCodeAt(i + 1);
20317
+ if (low >= 56320 && low <= 57343) {
20318
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
20319
+ i++;
20320
+ } else output += percentEncodeNonAscii(65533);
20321
+ } else output += percentEncodeNonAscii(65533);
20322
+ }
20323
+ }
20324
+ return output;
20325
+ }
20326
+ /**
20327
+ * Percent-encodes a URI component using its RFC 3986 literal character set.
20328
+ * Existing valid escapes are preserved and normalized to uppercase hex.
20329
+ *
20330
+ * @param {string} input
20331
+ * @param {(value: string) => boolean} isAllowed
20332
+ * @returns {string}
20333
+ */
20334
+ function encodeComponent(input, isAllowed) {
20335
+ let output = "";
20336
+ for (let i = 0; i < input.length; i++) {
20337
+ const ch = input[i];
20338
+ if (ch === "%" && i + 2 < input.length) {
20339
+ const hex = input.slice(i + 1, i + 3);
20340
+ if (isHexPair(hex)) {
20341
+ output += "%" + hex.toUpperCase();
20342
+ i += 2;
20343
+ continue;
20344
+ }
20345
+ }
20346
+ if (isAllowed(ch)) output += ch;
20347
+ else {
20348
+ const code = input.charCodeAt(i);
20349
+ if (code < 128) output += BYTE_HEX[code];
20350
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
20351
+ else if (code <= 56319 && i + 1 < input.length) {
20352
+ const low = input.charCodeAt(i + 1);
20353
+ if (low >= 56320 && low <= 57343) {
20354
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
20355
+ i++;
20356
+ } else output += percentEncodeNonAscii(65533);
20357
+ } else output += percentEncodeNonAscii(65533);
20358
+ }
20359
+ }
20360
+ return output;
20361
+ }
20362
+ /**
20363
+ * Encodes userinfo while preserving its RFC 3986 §3.2.1 literal characters.
20364
+ * In particular, authority delimiters such as `@`, `/`, `?`, and `#` are data.
20365
+ *
20366
+ * @param {string} input
20367
+ * @returns {string}
20368
+ */
20369
+ function encodeUserinfo(input) {
20370
+ return encodeComponent(input, isUserinfoCharacter);
20371
+ }
20372
+ /**
20373
+ * Encodes query data using the RFC 3986 §3.4 grammar. A literal `#` must be
20374
+ * escaped because it would otherwise begin the fragment component.
20375
+ *
20376
+ * @param {string} input
20377
+ * @returns {string}
20378
+ */
20379
+ function encodeQuery(input) {
20380
+ return encodeComponent(input, isQueryFragmentCharacter);
20381
+ }
20382
+ /**
20383
+ * Encodes fragment data using the RFC 3986 §3.5 grammar.
20384
+ *
20385
+ * @param {string} input
20386
+ * @returns {string}
20387
+ */
20388
+ function encodeFragment(input) {
20389
+ return encodeComponent(input, isQueryFragmentCharacter);
20390
+ }
20391
+ function isEscapeSafe(cp) {
20392
+ 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;
20393
+ }
20394
+ /**
20395
+ * Normalizes the percent-encoding of a query or fragment component.
20396
+ *
20397
+ * Like `normalizePathEncoding`, but uses the query/fragment character set
20398
+ * (which additionally allows `?`) and decodes `.` since it has no dot-segment
20399
+ * meaning outside of a path.
20400
+ *
20401
+ * @param {string} input
20402
+ * @returns {string}
20403
+ */
20404
+ function normalizeQueryFragmentEncoding(input) {
20405
+ let output = "";
20406
+ for (let i = 0; i < input.length; i++) {
20407
+ const ch = input[i];
20408
+ if (ch === "%" && i + 2 < input.length) {
20409
+ const hex = input.slice(i + 1, i + 3);
20410
+ if (isHexPair(hex)) {
20411
+ const normalizedHex = hex.toUpperCase();
20412
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
20413
+ if (isUnreserved(decoded)) output += decoded;
20414
+ else output += "%" + normalizedHex;
20415
+ i += 2;
20416
+ continue;
20417
+ }
20418
+ }
20419
+ if (isQueryFragmentCharacter(ch)) output += ch;
20420
+ else {
20421
+ const code = input.charCodeAt(i);
20422
+ if (code < 128) output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
20423
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
20424
+ else if (code <= 56319 && i + 1 < input.length) {
20425
+ const low = input.charCodeAt(i + 1);
20426
+ if (low >= 56320 && low <= 57343) {
20427
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
20428
+ i++;
20429
+ } else output += percentEncodeNonAscii(65533);
20430
+ } else output += percentEncodeNonAscii(65533);
20431
+ }
20432
+ }
20433
+ return output;
20434
+ }
20435
+ /**
20436
+ * Escapes a component while preserving existing valid percent escapes.
20437
+ *
20438
+ * @param {string} input
20439
+ * @returns {string}
20440
+ */
20441
+ function escapePreservingEscapes(input) {
20442
+ let output = "";
20443
+ for (let i = 0; i < input.length; i++) {
20444
+ if (input[i] === "%" && i + 2 < input.length) {
20445
+ const hex = input.slice(i + 1, i + 3);
20446
+ if (isHexPair(hex)) {
20447
+ output += "%" + hex.toUpperCase();
20448
+ i += 2;
20449
+ continue;
20450
+ }
20451
+ }
20452
+ output += escape(input[i]);
20453
+ }
20454
+ return output;
20103
20455
  }
20104
20456
  /**
20105
20457
  * @param {import('../types/index').URIComponent} component
@@ -20108,28 +20460,42 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20108
20460
  function recomposeAuthority(component) {
20109
20461
  const uriTokens = [];
20110
20462
  if (component.userinfo !== void 0) {
20111
- uriTokens.push(component.userinfo);
20463
+ uriTokens.push(encodeUserinfo(component.userinfo));
20112
20464
  uriTokens.push("@");
20113
20465
  }
20114
20466
  if (component.host !== void 0) {
20115
- let host = unescape(component.host);
20467
+ let host = component.host;
20116
20468
  if (!isIPv4(host)) {
20117
- const ipV6res = normalizeIPv6(host);
20118
- if (ipV6res.isIPV6 === true) host = `[${ipV6res.escapedHost}]`;
20119
- else host = component.host;
20469
+ let ipV6res = normalizeIPv6(host);
20470
+ if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
20471
+ host = normalizePercentEncoding(host, true);
20472
+ ipV6res = normalizeIPv6(host);
20473
+ }
20474
+ if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) host = `[${ipV6res.escapedHost}]`;
20475
+ else host = reescapeHostDelimiters(host, false);
20120
20476
  }
20121
20477
  uriTokens.push(host);
20122
20478
  }
20123
20479
  if (typeof component.port === "number" || typeof component.port === "string") {
20480
+ const port = String(component.port);
20481
+ if (!isPort(port)) throw new TypeError("URI port is malformed.");
20124
20482
  uriTokens.push(":");
20125
- uriTokens.push(String(component.port));
20483
+ uriTokens.push(port);
20126
20484
  }
20127
20485
  return uriTokens.length ? uriTokens.join("") : void 0;
20128
20486
  }
20129
20487
  module.exports = {
20130
20488
  nonSimpleDomain,
20131
20489
  recomposeAuthority,
20132
- normalizeComponentEncoding,
20490
+ reescapeHostDelimiters,
20491
+ normalizePercentEncoding,
20492
+ normalizePathEncoding,
20493
+ serializePathEncoding,
20494
+ normalizeQueryFragmentEncoding,
20495
+ encodeUserinfo,
20496
+ encodeQuery,
20497
+ encodeFragment,
20498
+ escapePreservingEscapes,
20133
20499
  removeDotSegments,
20134
20500
  isIPv4,
20135
20501
  isUUID,
@@ -20139,7 +20505,7 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20139
20505
  }));
20140
20506
  var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20141
20507
  const { isUUID } = require_utils();
20142
- const URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
20508
+ const URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
20143
20509
  const supportedSchemeNames = [
20144
20510
  "http",
20145
20511
  "https",
@@ -20210,9 +20576,10 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20210
20576
  wsComponent.secure = void 0;
20211
20577
  }
20212
20578
  if (wsComponent.resourceName) {
20213
- const [path, query] = wsComponent.resourceName.split("?");
20579
+ const queryIndex = wsComponent.resourceName.indexOf("?");
20580
+ const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
20214
20581
  wsComponent.path = path && path !== "/" ? path : void 0;
20215
- wsComponent.query = query;
20582
+ wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
20216
20583
  wsComponent.resourceName = void 0;
20217
20584
  }
20218
20585
  wsComponent.fragment = void 0;
@@ -20225,7 +20592,7 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20225
20592
  return urnComponent;
20226
20593
  }
20227
20594
  const matches = urnComponent.path.match(URN_REG);
20228
- if (matches) {
20595
+ if (matches && matches[0] === urnComponent.path) {
20229
20596
  const scheme = options.scheme || urnComponent.scheme || "urn";
20230
20597
  urnComponent.nid = matches[1].toLowerCase();
20231
20598
  urnComponent.nss = matches[2];
@@ -20319,8 +20686,19 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20319
20686
  };
20320
20687
  }));
20321
20688
  var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20322
- const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
20689
+ const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
20323
20690
  const { SCHEMES, getSchemeHandler } = require_schemes();
20691
+ const VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
20692
+ const MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
20693
+ /**
20694
+ * @param {string} scheme
20695
+ * @returns {string}
20696
+ */
20697
+ function decodeValidScheme(scheme) {
20698
+ const decodedScheme = unescape(String(scheme));
20699
+ if (!VALID_SCHEME.test(decodedScheme)) throw new TypeError(MALFORMED_SCHEME_ERROR);
20700
+ return decodedScheme;
20701
+ }
20324
20702
  /**
20325
20703
  * @template {import('./types/index').URIComponent|string} T
20326
20704
  * @param {T} uri
@@ -20328,7 +20706,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20328
20706
  * @returns {T}
20329
20707
  */
20330
20708
  function normalize(uri, options) {
20331
- if (typeof uri === "string") uri = serialize(parse(uri, options), options);
20709
+ if (typeof uri === "string") uri = normalizeString(uri, options);
20332
20710
  else if (typeof uri === "object") uri = parse(serialize(uri, options), options);
20333
20711
  return uri;
20334
20712
  }
@@ -20340,7 +20718,16 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20340
20718
  */
20341
20719
  function resolve(baseURI, relativeURI, options) {
20342
20720
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
20343
- const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
20721
+ const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed, malformedPercentEncoding: baseMalformedPercentEncoding, malformedSchemeSpecific: baseMalformedSchemeSpecific, malformedHost: baseMalformedHost, malformedScheme: baseMalformedScheme } = parseWithStatus(baseURI, schemelessOptions);
20722
+ const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed, malformedPercentEncoding: relativeMalformedPercentEncoding, malformedSchemeSpecific: relativeMalformedSchemeSpecific, malformedHost: relativeMalformedHost, malformedScheme: relativeMalformedScheme } = parseWithStatus(relativeURI, schemelessOptions);
20723
+ if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
20724
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
20725
+ const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
20726
+ const resolvedHost = resolved.host;
20727
+ const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
20728
+ canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
20729
+ const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
20730
+ if (resolved.error && !encodedASCIIHost) throw new Error(resolved.error);
20344
20731
  schemelessOptions.skipEscape = true;
20345
20732
  return serialize(resolved, schemelessOptions);
20346
20733
  }
@@ -20404,27 +20791,9 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20404
20791
  * @returns {boolean}
20405
20792
  */
20406
20793
  function equal(uriA, uriB, options) {
20407
- if (typeof uriA === "string") {
20408
- uriA = unescape(uriA);
20409
- uriA = serialize(normalizeComponentEncoding(parse(uriA, options), true), {
20410
- ...options,
20411
- skipEscape: true
20412
- });
20413
- } else if (typeof uriA === "object") uriA = serialize(normalizeComponentEncoding(uriA, true), {
20414
- ...options,
20415
- skipEscape: true
20416
- });
20417
- if (typeof uriB === "string") {
20418
- uriB = unescape(uriB);
20419
- uriB = serialize(normalizeComponentEncoding(parse(uriB, options), true), {
20420
- ...options,
20421
- skipEscape: true
20422
- });
20423
- } else if (typeof uriB === "object") uriB = serialize(normalizeComponentEncoding(uriB, true), {
20424
- ...options,
20425
- skipEscape: true
20426
- });
20427
- return uriA.toLowerCase() === uriB.toLowerCase();
20794
+ const normalizedA = normalizeComparableURI(uriA, options);
20795
+ const normalizedB = normalizeComparableURI(uriB, options);
20796
+ return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
20428
20797
  }
20429
20798
  /**
20430
20799
  * @param {Readonly<import('./types/index').URIComponent>} cmpts
@@ -20450,13 +20819,17 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20450
20819
  };
20451
20820
  const options = Object.assign({}, opts);
20452
20821
  const uriTokens = [];
20822
+ if (component.scheme) component.scheme = decodeValidScheme(component.scheme);
20453
20823
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
20454
20824
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
20455
- if (component.path !== void 0) if (!options.skipEscape) {
20456
- component.path = escape(component.path);
20457
- if (component.scheme !== void 0) component.path = component.path.split("%3A").join(":");
20458
- } else component.path = unescape(component.path);
20459
- if (options.reference !== "suffix" && component.scheme) uriTokens.push(component.scheme, ":");
20825
+ const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
20826
+ const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
20827
+ if (component.path !== void 0) if (!options.skipEscape) component.path = serializePathEncoding(component.path, pathNoScheme);
20828
+ else component.path = normalizePercentEncoding(component.path);
20829
+ if (options.reference !== "suffix" && component.scheme) {
20830
+ component.scheme = decodeValidScheme(component.scheme);
20831
+ uriTokens.push(component.scheme, ":");
20832
+ }
20460
20833
  const authority = recomposeAuthority(component);
20461
20834
  if (authority !== void 0) {
20462
20835
  if (options.reference !== "suffix") uriTokens.push("//");
@@ -20466,20 +20839,84 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20466
20839
  if (component.path !== void 0) {
20467
20840
  let s = component.path;
20468
20841
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) s = removeDotSegments(s);
20842
+ if (pathNoScheme) s = serializePathEncoding(s, true);
20469
20843
  if (authority === void 0 && s[0] === "/" && s[1] === "/") s = "/%2F" + s.slice(2);
20470
20844
  uriTokens.push(s);
20471
20845
  }
20472
- if (component.query !== void 0) uriTokens.push("?", component.query);
20473
- if (component.fragment !== void 0) uriTokens.push("#", component.fragment);
20846
+ if (component.query !== void 0) uriTokens.push("?", encodeQuery(component.query));
20847
+ if (component.fragment !== void 0) uriTokens.push("#", encodeFragment(component.fragment));
20474
20848
  return uriTokens.join("");
20475
20849
  }
20476
20850
  const URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
20851
+ const AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
20852
+ const AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
20853
+ /**
20854
+ * @param {import('./types/index').URIComponent} parsed
20855
+ * @param {RegExpMatchArray} matches
20856
+ * @returns {string|undefined}
20857
+ */
20858
+ function getParseError(parsed, matches) {
20859
+ if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") return "URI path must start with \"/\" when authority is present.";
20860
+ if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) return "URI port is malformed.";
20861
+ }
20862
+ /**
20863
+ * Checks percent syntax without decoding the represented octets. RFC 3986
20864
+ * percent-encoding is byte-oriented, so sequences such as `%FF` are valid even
20865
+ * though they are not independently valid UTF-8.
20866
+ *
20867
+ * @param {string|undefined} component
20868
+ * @returns {boolean}
20869
+ */
20870
+ function hasMalformedPercentEncoding(component) {
20871
+ if (component === void 0) return false;
20872
+ let percent = component.indexOf("%");
20873
+ while (percent !== -1) {
20874
+ if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) return true;
20875
+ percent = component.indexOf("%", percent + 3);
20876
+ }
20877
+ return false;
20878
+ }
20879
+ /**
20880
+ * Whether the host is a bracketed IP literal (RFC 3986 `IP-literal`).
20881
+ * An unterminated `[` is not a literal, so it must still be validated as a
20882
+ * reg-name instead of being waved through as an IP.
20883
+ *
20884
+ * @param {string} host
20885
+ * @returns {boolean}
20886
+ */
20887
+ function isIPLiteral(host) {
20888
+ return host[0] === "[" && host[host.length - 1] === "]";
20889
+ }
20890
+ /**
20891
+ * @param {RegExpMatchArray} matches
20892
+ * @returns {boolean}
20893
+ */
20894
+ function hasMalformedComponentPercentEncoding(matches) {
20895
+ const host = matches[4];
20896
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
20897
+ }
20898
+ /**
20899
+ * @param {import('./types/index').URIComponent} parsed
20900
+ * @param {import('./types/index').Options} options
20901
+ * @param {{ domainHost?: boolean, unicodeSupport?: boolean }|undefined} schemeHandler
20902
+ * @param {boolean} isIP
20903
+ * @returns {boolean} whether host conversion failed
20904
+ */
20905
+ function canonicalizeHost(parsed, options, schemeHandler, isIP) {
20906
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) try {
20907
+ parsed.host = new URL("http://" + parsed.host).hostname;
20908
+ } catch (e) {
20909
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
20910
+ return true;
20911
+ }
20912
+ return false;
20913
+ }
20477
20914
  /**
20478
20915
  * @param {string} uri
20479
20916
  * @param {import('./types/index').Options} [opts]
20480
- * @returns
20917
+ * @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
20481
20918
  */
20482
- function parse(uri, opts) {
20919
+ function parseWithStatus(uri, opts) {
20483
20920
  const options = Object.assign({}, opts);
20484
20921
  /** @type {import('./types/index').URIComponent} */
20485
20922
  const parsed = {
@@ -20491,9 +20928,34 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20491
20928
  query: void 0,
20492
20929
  fragment: void 0
20493
20930
  };
20931
+ let malformedAuthorityOrPort = false;
20932
+ let malformedPercentEncoding = false;
20933
+ let malformedSchemeSpecific = false;
20934
+ let malformedHost = false;
20935
+ let malformedIPLiteral = false;
20936
+ let malformedScheme = false;
20494
20937
  let isIP = false;
20495
20938
  if (options.reference === "suffix") if (options.scheme) uri = options.scheme + ":" + uri;
20496
20939
  else uri = "//" + uri;
20940
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
20941
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
20942
+ parsed.error = "URI authority must not contain a literal backslash.";
20943
+ malformedAuthorityOrPort = true;
20944
+ }
20945
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
20946
+ if (introducerMatch !== null) {
20947
+ const region = introducerMatch[1];
20948
+ const normalizedRegion = region.replace(/[\t\n\r]/g, "");
20949
+ if (normalizedRegion.length >= 2) {
20950
+ if (normalizedRegion.slice(0, 2) !== "//") {
20951
+ parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
20952
+ malformedAuthorityOrPort = true;
20953
+ } else if (region.length !== normalizedRegion.length) {
20954
+ parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
20955
+ malformedAuthorityOrPort = true;
20956
+ }
20957
+ }
20958
+ }
20497
20959
  const matches = uri.match(URI_PARSE);
20498
20960
  if (matches) {
20499
20961
  parsed.scheme = matches[1];
@@ -20503,11 +20965,33 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20503
20965
  parsed.path = matches[6] || "";
20504
20966
  parsed.query = matches[7];
20505
20967
  parsed.fragment = matches[8];
20968
+ if (parsed.scheme !== void 0) {
20969
+ const decodedScheme = unescape(parsed.scheme);
20970
+ if (VALID_SCHEME.test(decodedScheme)) parsed.scheme = decodedScheme.toLowerCase();
20971
+ else {
20972
+ parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
20973
+ malformedScheme = true;
20974
+ }
20975
+ }
20976
+ malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
20977
+ if (malformedPercentEncoding) parsed.error = parsed.error || "URI contains malformed percent-encoding.";
20506
20978
  if (isNaN(parsed.port)) parsed.port = matches[5];
20979
+ const parseError = getParseError(parsed, matches);
20980
+ if (parseError !== void 0) {
20981
+ parsed.error = parsed.error || parseError;
20982
+ malformedAuthorityOrPort = true;
20983
+ }
20507
20984
  if (parsed.host) if (isIPv4(parsed.host) === false) {
20985
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
20986
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
20508
20987
  const ipv6result = normalizeIPv6(parsed.host);
20509
- parsed.host = ipv6result.host.toLowerCase();
20510
- isIP = ipv6result.isIPV6;
20988
+ isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
20989
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
20990
+ parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
20991
+ if (malformedIPLiteral) {
20992
+ parsed.error = parsed.error || "URI host is malformed.";
20993
+ malformedAuthorityOrPort = true;
20994
+ }
20511
20995
  } else isIP = true;
20512
20996
  if (parsed.scheme === void 0 && parsed.userinfo === void 0 && parsed.host === void 0 && parsed.port === void 0 && parsed.query === void 0 && !parsed.path) parsed.reference = "same-document";
20513
20997
  else if (parsed.scheme === void 0) parsed.reference = "relative";
@@ -20515,53 +20999,107 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20515
20999
  else parsed.reference = "uri";
20516
21000
  if (options.reference && options.reference !== "suffix" && options.reference !== parsed.reference) parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
20517
21001
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
20518
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
20519
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) try {
20520
- parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
20521
- } catch (e) {
20522
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
20523
- }
21002
+ if (!malformedIPLiteral) malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
21003
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
21004
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
21005
+ if (!isIP) host = normalizePercentEncoding(host.toLowerCase());
21006
+ parsed.host = reescapeHostDelimiters(host, isIP);
20524
21007
  }
20525
21008
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
20526
- if (uri.indexOf("%") !== -1) {
20527
- if (parsed.scheme !== void 0) parsed.scheme = unescape(parsed.scheme);
20528
- if (parsed.host !== void 0) parsed.host = unescape(parsed.host);
20529
- }
20530
- if (parsed.path) parsed.path = escape(unescape(parsed.path));
20531
- if (parsed.fragment) parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
21009
+ if (parsed.path) parsed.path = normalizePathEncoding(parsed.path);
21010
+ if (parsed.query) parsed.query = normalizeQueryFragmentEncoding(parsed.query);
21011
+ if (parsed.fragment) parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
21012
+ }
21013
+ if (schemeHandler && schemeHandler.parse) {
21014
+ schemeHandler.parse(parsed, options);
21015
+ if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) malformedSchemeSpecific = true;
20532
21016
  }
20533
- if (schemeHandler && schemeHandler.parse) schemeHandler.parse(parsed, options);
20534
21017
  } else parsed.error = parsed.error || "URI can not be parsed.";
20535
- return parsed;
21018
+ return {
21019
+ parsed,
21020
+ malformedAuthorityOrPort,
21021
+ malformedPercentEncoding,
21022
+ malformedSchemeSpecific,
21023
+ malformedHost,
21024
+ malformedScheme
21025
+ };
20536
21026
  }
20537
- const fastUri = {
20538
- SCHEMES,
20539
- normalize,
20540
- resolve,
20541
- resolveComponent,
20542
- equal,
20543
- serialize,
20544
- parse
20545
- };
20546
- module.exports = fastUri;
20547
- module.exports.default = fastUri;
20548
- module.exports.fastUri = fastUri;
20549
- }));
20550
- var require_uri = /* @__PURE__ */ __commonJSMin(((exports) => {
20551
- Object.defineProperty(exports, "__esModule", { value: true });
20552
- const uri = require_fast_uri();
20553
- uri.code = "require(\"ajv/dist/runtime/uri\").default";
20554
- exports.default = uri;
20555
- }));
20556
- var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
20557
- Object.defineProperty(exports, "__esModule", { value: true });
20558
- exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0;
20559
- var validate_1 = require_validate();
20560
- Object.defineProperty(exports, "KeywordCxt", {
20561
- enumerable: true,
20562
- get: function() {
20563
- return validate_1.KeywordCxt;
20564
- }
21027
+ /**
21028
+ * @param {string} uri
21029
+ * @param {import('./types/index').Options} [opts]
21030
+ * @returns
21031
+ */
21032
+ function parse(uri, opts) {
21033
+ return parseWithStatus(uri, opts).parsed;
21034
+ }
21035
+ /**
21036
+ * @param {string} uri
21037
+ * @param {import('./types/index').Options} [opts]
21038
+ * @returns {string}
21039
+ */
21040
+ function normalizeString(uri, opts) {
21041
+ return normalizeStringWithStatus(uri, opts).normalized;
21042
+ }
21043
+ /**
21044
+ * @param {string} uri
21045
+ * @param {import('./types/index').Options} [opts]
21046
+ * @returns {{ normalized: string, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
21047
+ */
21048
+ function normalizeStringWithStatus(uri, opts) {
21049
+ const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
21050
+ return {
21051
+ normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
21052
+ malformedAuthorityOrPort,
21053
+ malformedPercentEncoding,
21054
+ malformedSchemeSpecific,
21055
+ malformedHost,
21056
+ malformedScheme
21057
+ };
21058
+ }
21059
+ /**
21060
+ * @param {import ('./types/index').URIComponent|string} uri
21061
+ * @param {import('./types/index').Options} [opts]
21062
+ * @returns {string|undefined}
21063
+ */
21064
+ function normalizeComparableURI(uri, opts) {
21065
+ if (typeof uri !== "string" && typeof uri !== "object") return;
21066
+ let value;
21067
+ try {
21068
+ value = typeof uri === "string" ? uri : serialize(uri, opts);
21069
+ } catch {
21070
+ return;
21071
+ }
21072
+ const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
21073
+ return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
21074
+ }
21075
+ const fastUri = {
21076
+ SCHEMES,
21077
+ normalize,
21078
+ resolve,
21079
+ resolveComponent,
21080
+ equal,
21081
+ serialize,
21082
+ parse
21083
+ };
21084
+ module.exports = fastUri;
21085
+ module.exports.default = fastUri;
21086
+ module.exports.fastUri = fastUri;
21087
+ }));
21088
+ var require_uri = /* @__PURE__ */ __commonJSMin(((exports) => {
21089
+ Object.defineProperty(exports, "__esModule", { value: true });
21090
+ const uri = require_fast_uri();
21091
+ uri.code = "require(\"ajv/dist/runtime/uri\").default";
21092
+ exports.default = uri;
21093
+ }));
21094
+ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
21095
+ Object.defineProperty(exports, "__esModule", { value: true });
21096
+ exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0;
21097
+ var validate_1 = require_validate();
21098
+ Object.defineProperty(exports, "KeywordCxt", {
21099
+ enumerable: true,
21100
+ get: function() {
21101
+ return validate_1.KeywordCxt;
21102
+ }
20565
21103
  });
20566
21104
  var codegen_1 = require_codegen();
20567
21105
  Object.defineProperty(exports, "_", {
@@ -24322,7 +24860,7 @@ var AjvJsonSchemaValidator = class {
24322
24860
  };
24323
24861
  import_ajv.Ajv;
24324
24862
  //#endregion
24325
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/mcp-DIH4cS6P.mjs
24863
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/mcp-oHn3ureC.mjs
24326
24864
  const COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
24327
24865
  /**
24328
24866
  * Checks if a schema is completable (has completion metadata).
@@ -24344,240 +24882,195 @@ function armSseKeepAlive(intervalMs, onTick) {
24344
24882
  timer.unref?.();
24345
24883
  return timer;
24346
24884
  }
24885
+ let readClassification;
24347
24886
  /**
24348
- * A `ServerEventBus` backed by an in-process listener set.
24887
+ * The per-request micro-transport: a real, connected `Transport` whose whole
24888
+ * lifetime is one HTTP exchange. See the module documentation for the
24889
+ * response shapes it produces.
24349
24890
  *
24350
- * `publish()` delivers synchronously to the live listener set (a listener
24351
- * unsubscribing itself mid-dispatch is safe; the entry's listen-router
24352
- * listeners never unsubscribe peers). A throwing listener does not stop
24353
- * delivery to the others.
24891
+ * Connecting a `Server` binds the instance to the classified modern revision
24892
+ * when its `supportedProtocolVersions` lists it and the SDK implements it.
24354
24893
  */
24355
- var InMemoryServerEventBus = class {
24356
- _listeners = /* @__PURE__ */ new Set();
24894
+ var PerRequestHTTPServerTransport = class {
24895
+ static {
24896
+ readClassification = (transport) => transport._classification;
24897
+ }
24898
+ onclose;
24899
+ onerror;
24900
+ onmessage;
24901
+ _classification;
24902
+ _responseMode;
24903
+ _started = false;
24904
+ _used = false;
24905
+ _closed = false;
24906
+ _terminalDelivered = false;
24357
24907
  /**
24358
- * @param onerror - Optional callback for errors thrown by listeners
24359
- * during dispatch.
24908
+ * `true` only while the inbound message is being delivered synchronously
24909
+ * to the connected protocol layer. The pre-handler gates (the era
24910
+ * registry gate, the edge→instance handoff check, the missing-handler
24911
+ * rejection) answer inside this window; request handlers always run
24912
+ * after it (the protocol layer defers them to a microtask). An error
24913
+ * sent inside the window is therefore ladder-originated, and an error
24914
+ * sent after it is handler-produced.
24360
24915
  */
24361
- constructor(onerror) {
24362
- this.onerror = onerror;
24916
+ _dispatchWindowOpen = false;
24917
+ _requestId;
24918
+ _deferredResponse;
24919
+ _sse;
24920
+ _abortCleanup;
24921
+ _keepAliveMs;
24922
+ constructor(options) {
24923
+ this._classification = options.classification;
24924
+ this._responseMode = options.responseMode ?? "auto";
24925
+ this._keepAliveMs = options.keepAliveMs ?? 15e3;
24363
24926
  }
24364
- publish(event) {
24365
- for (const listener of this._listeners) try {
24366
- listener(event);
24367
- } catch (error) {
24368
- this.onerror?.(error instanceof Error ? error : new Error(String(error)));
24369
- }
24927
+ async start() {
24928
+ if (this._started) throw new Error("PerRequestHTTPServerTransport is already started");
24929
+ this._started = true;
24370
24930
  }
24371
- subscribe(listener) {
24372
- this._listeners.add(listener);
24373
- let live = true;
24374
- return () => {
24375
- if (!live) return;
24376
- live = false;
24377
- this._listeners.delete(listener);
24931
+ /**
24932
+ * Serves the single exchange: delivers the classified message to the
24933
+ * connected server instance and resolves with the HTTP response.
24934
+ *
24935
+ * Throws when called a second time (the transport is strictly
24936
+ * single-use), or before a server has been connected to the transport.
24937
+ * The returned promise rejects with a connection-closed error when the
24938
+ * transport is closed before a response was produced (for example because
24939
+ * the client disconnected).
24940
+ */
24941
+ async handleMessage(message, extra) {
24942
+ if (this._used) throw new Error("PerRequestHTTPServerTransport serves exactly one exchange; construct a new transport per request");
24943
+ if (!this._started || this.onmessage === void 0) throw new Error("PerRequestHTTPServerTransport is not connected: connect a server to this transport before handling a message");
24944
+ if (this._closed) throw new Error("PerRequestHTTPServerTransport is closed");
24945
+ this._used = true;
24946
+ const signal = extra?.request?.signal;
24947
+ if (signal?.aborted) {
24948
+ await this.close();
24949
+ throw new SdkError(SdkErrorCode.ConnectionClosed, "The request was aborted before it could be handled");
24950
+ }
24951
+ const messageExtra = {
24952
+ classification: this._classification,
24953
+ ...extra?.request !== void 0 && { request: extra.request },
24954
+ ...extra?.authInfo !== void 0 && { authInfo: extra.authInfo }
24378
24955
  };
24956
+ if (isJSONRPCRequest(message)) {
24957
+ this._requestId = message.id;
24958
+ let resolve;
24959
+ let reject;
24960
+ const promise = new Promise((promiseResolve, promiseReject) => {
24961
+ resolve = promiseResolve;
24962
+ reject = promiseReject;
24963
+ });
24964
+ this._deferredResponse = {
24965
+ promise,
24966
+ resolve,
24967
+ reject,
24968
+ settled: false
24969
+ };
24970
+ if (signal !== void 0) {
24971
+ const onAbort = () => void this.close();
24972
+ signal.addEventListener("abort", onAbort, { once: true });
24973
+ this._abortCleanup = () => signal.removeEventListener("abort", onAbort);
24974
+ }
24975
+ this._dispatchWindowOpen = true;
24976
+ try {
24977
+ this.onmessage(message, messageExtra);
24978
+ } finally {
24979
+ this._dispatchWindowOpen = false;
24980
+ }
24981
+ if (this._responseMode === "sse" && !this._closed && !this._deferredResponse.settled) this.upgradeToSse();
24982
+ return promise;
24983
+ }
24984
+ this.onmessage(message, messageExtra);
24985
+ return new Response(null, { status: 202 });
24379
24986
  }
24380
- /** The number of currently registered listeners (test/introspection only — the routers track capacity via their own open-subscription set). */
24381
- get listenerCount() {
24382
- return this._listeners.size;
24987
+ async send(message, options) {
24988
+ if (this._closed) return;
24989
+ const isResponse = isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message);
24990
+ const relatedId = isResponse ? message.id : options?.relatedRequestId;
24991
+ if (this._requestId === void 0 || relatedId === void 0 || relatedId !== this._requestId) {
24992
+ if (isResponse) this.onerror?.(/* @__PURE__ */ new Error(`Received a response for an unknown request id: ${String(message.id)}`));
24993
+ return;
24994
+ }
24995
+ if (isResponse) {
24996
+ if (this._terminalDelivered) return;
24997
+ this._terminalDelivered = true;
24998
+ const errorCode = isJSONRPCErrorResponse(message) ? message.error.code : void 0;
24999
+ const ladderStatus = errorCode !== void 0 && (this._dispatchWindowOpen || errorCode === ProtocolErrorCode.MissingRequiredClientCapability) ? LADDER_ERROR_HTTP_STATUS[errorCode] : void 0;
25000
+ if (ladderStatus !== void 0 && this._sse === void 0) {
25001
+ this.settleResponse(Response.json(message, {
25002
+ status: ladderStatus,
25003
+ headers: { "Content-Type": "application/json" }
25004
+ }));
25005
+ queueMicrotask(() => void this.close());
25006
+ return;
25007
+ }
25008
+ if (this._sse !== void 0 || this._responseMode === "sse") {
25009
+ if (this._sse === void 0) this.upgradeToSse();
25010
+ this.writeMessageFrame(message);
25011
+ this.finalizeStream();
25012
+ return;
25013
+ }
25014
+ this.settleResponse(Response.json(message, {
25015
+ status: 200,
25016
+ headers: { "Content-Type": "application/json" }
25017
+ }));
25018
+ queueMicrotask(() => void this.close());
25019
+ return;
25020
+ }
25021
+ if (this._responseMode === "json") return;
25022
+ if (this._sse === void 0) this.upgradeToSse();
25023
+ this.writeMessageFrame(message);
24383
25024
  }
24384
- };
24385
- /** Build a {@linkcode ServerNotifier} over a bus. */
24386
- function createServerNotifier(bus) {
24387
- return {
24388
- toolsChanged: () => bus.publish({ kind: "tools_list_changed" }),
24389
- promptsChanged: () => bus.publish({ kind: "prompts_list_changed" }),
24390
- resourcesChanged: () => bus.publish({ kind: "resources_list_changed" }),
24391
- resourceUpdated: (uri) => bus.publish({
24392
- kind: "resource_updated",
24393
- uri
24394
- })
24395
- };
24396
- }
24397
- /**
24398
- * Whether a `subscriptions/listen` filter accepts a given change event.
24399
- *
24400
- * Pure: no I/O, no mutation. The filter governs ONLY the four
24401
- * subscription-gated change types — non-gated notifications never reach the
24402
- * bus and are not modeled here.
24403
- *
24404
- * `resource_updated` matches only when `resourceSubscriptions` is present and
24405
- * contains the event's URI exactly (per the spec: "for these resource URIs").
24406
- */
24407
- function listenFilterAccepts(filter, event) {
24408
- switch (event.kind) {
24409
- case "tools_list_changed": return filter.toolsListChanged === true;
24410
- case "prompts_list_changed": return filter.promptsListChanged === true;
24411
- case "resources_list_changed": return filter.resourcesListChanged === true;
24412
- case "resource_updated": return filter.resourceSubscriptions !== void 0 && filter.resourceSubscriptions.includes(event.uri);
25025
+ /**
25026
+ * Writes an SSE comment frame (a keep-alive heartbeat). Dropped when the
25027
+ * exchange is not currently streaming.
25028
+ */
25029
+ writeCommentFrame(comment) {
25030
+ if (this._closed || this._sse === void 0 || this._sse.closed) return;
25031
+ const frame = comment.split("\n").map((line) => `: ${line}`).join("\n");
25032
+ this.writeFrame(`${frame}\n\n`);
24413
25033
  }
24414
- }
24415
- /**
24416
- * The honored subset of a requested filter: keeps only the fields the client
24417
- * explicitly opted in to (drops `false` and absent fields), narrowed against
24418
- * the server's declared capabilities when supplied. The serving entry sends
24419
- * this back in `notifications/subscriptions/acknowledged` so the ack reflects
24420
- * what the server can actually deliver.
24421
- *
24422
- * - `toolsListChanged` is honored only when `capabilities.tools.listChanged`
24423
- * is advertised; likewise `promptsListChanged` / `resourcesListChanged`.
24424
- * - `resourceSubscriptions` is honored only when
24425
- * `capabilities.resources.subscribe` is advertised.
24426
- *
24427
- * `capabilities` is optional on this pure helper for test convenience only —
24428
- * both wired routers REQUIRE capabilities at the call site (the HTTP router's
24429
- * `serve()` takes a required parameter; `StdioListenRouter.serve()` throws
24430
- * before `setServerCapabilities()` was called), so the fail-open
24431
- * `undefined → honor everything` branch is never reachable on a wired entry.
24432
- */
24433
- function honoredSubset(requested, capabilities) {
24434
- const honored = {};
24435
- const allow = (bit) => capabilities === void 0 || bit === true;
24436
- if (requested.toolsListChanged === true && allow(capabilities?.tools?.listChanged)) honored.toolsListChanged = true;
24437
- if (requested.promptsListChanged === true && allow(capabilities?.prompts?.listChanged)) honored.promptsListChanged = true;
24438
- if (requested.resourcesListChanged === true && allow(capabilities?.resources?.listChanged)) honored.resourcesListChanged = true;
24439
- if (requested.resourceSubscriptions !== void 0 && requested.resourceSubscriptions.length > 0 && allow(capabilities?.resources?.subscribe)) honored.resourceSubscriptions = [...requested.resourceSubscriptions];
24440
- return honored;
24441
- }
24442
- /** Map a {@linkcode ServerEvent} onto its wire notification `{method, params}`. */
24443
- function serverEventToNotification(event) {
24444
- switch (event.kind) {
24445
- case "tools_list_changed": return { method: "notifications/tools/list_changed" };
24446
- case "prompts_list_changed": return { method: "notifications/prompts/list_changed" };
24447
- case "resources_list_changed": return { method: "notifications/resources/list_changed" };
24448
- case "resource_updated": return {
24449
- method: "notifications/resources/updated",
24450
- params: { uri: event.uri }
24451
- };
24452
- }
24453
- }
24454
- /** Default capacity guard: refuse a new subscription when this many are already open. */
24455
- const DEFAULT_MAX_SUBSCRIPTIONS = 1024;
24456
- function jsonRpcError(id, code, message) {
24457
- return Response.json({
24458
- jsonrpc: "2.0",
24459
- error: {
24460
- code,
24461
- message
24462
- },
24463
- id
24464
- }, { status: 200 });
24465
- }
24466
- /** Stamp the subscription id onto a notification's `_meta`. Non-mutating. */
24467
- function stampSubscriptionId(notification, subscriptionId) {
24468
- return {
24469
- method: notification.method,
24470
- params: {
24471
- ...notification.params,
24472
- _meta: {
24473
- ...notification.params?._meta,
24474
- [SUBSCRIPTION_ID_META_KEY]: subscriptionId
24475
- }
25034
+ async close() {
25035
+ if (this._closed) return;
25036
+ this._closed = true;
25037
+ this._abortCleanup?.();
25038
+ this._abortCleanup = void 0;
25039
+ if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
25040
+ if (this._sse !== void 0 && !this._sse.closed) {
25041
+ this._sse.closed = true;
25042
+ try {
25043
+ this._sse.controller.close();
25044
+ } catch {}
24476
25045
  }
24477
- };
24478
- }
24479
- /**
24480
- * Read the requested filter off a `subscriptions/listen` request body.
24481
- * Returns the validated filter, or `undefined` when `params.notifications`
24482
- * is absent or fails the schema (the caller answers `-32602` — the spec
24483
- * marks `notifications` REQUIRED on the listen request).
24484
- */
24485
- function parseListenFilter(message) {
24486
- const outcome = codecForVersion(MODERN_WIRE_REVISION).validateRequest("subscriptions/listen", message);
24487
- return outcome.ok ? outcome.value.params?.notifications : void 0;
24488
- }
24489
- function createListenRouter(options) {
24490
- const { bus, onerror } = options;
24491
- const maxSubscriptions = options.maxSubscriptions ?? 1024;
24492
- const keepAliveMs = options.keepAliveMs ?? 15e3;
24493
- const open = /* @__PURE__ */ new Set();
24494
- function serve(message, signal, capabilities, serverInfo) {
24495
- if (open.size >= maxSubscriptions) {
24496
- onerror?.(/* @__PURE__ */ new Error(`subscriptions/listen refused: subscription limit reached (${maxSubscriptions})`));
24497
- return jsonRpcError(message.id, -32603, "Subscription limit reached");
25046
+ if (this._deferredResponse !== void 0 && !this._deferredResponse.settled) {
25047
+ this._deferredResponse.settled = true;
25048
+ this._deferredResponse.reject(new SdkError(SdkErrorCode.ConnectionClosed, "Connection closed before a response was produced"));
24498
25049
  }
24499
- const filter = parseListenFilter(message);
24500
- if (filter === void 0) return jsonRpcError(message.id, -32602, "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter");
24501
- const honored = honoredSubset(filter, capabilities);
24502
- const subscriptionId = message.id;
24503
- const encoder = new TextEncoder();
25050
+ this.onclose?.();
25051
+ }
25052
+ settleResponse(response) {
25053
+ if (this._deferredResponse === void 0 || this._deferredResponse.settled) return;
25054
+ this._deferredResponse.settled = true;
25055
+ this._deferredResponse.resolve(response);
25056
+ }
25057
+ upgradeToSse() {
24504
25058
  let controller;
24505
- let closed = false;
24506
- let unsubscribe;
24507
- let keepAliveTimer;
24508
- let abortCleanup;
24509
- const writeFrame = (frame) => {
24510
- if (closed) return;
24511
- try {
24512
- controller.enqueue(encoder.encode(frame));
24513
- } catch (error) {
24514
- onerror?.(error instanceof Error ? error : new Error(String(error)));
24515
- }
24516
- };
24517
- const writeNotification = (method, params) => {
24518
- writeFrame(`event: message\ndata: ${JSON.stringify({
24519
- jsonrpc: "2.0",
24520
- method,
24521
- params
24522
- })}\n\n`);
24523
- };
24524
- const teardown = (graceful) => {
24525
- if (closed) return;
24526
- if (graceful) writeFrame(`event: message\ndata: ${JSON.stringify({
24527
- jsonrpc: "2.0",
24528
- id: subscriptionId,
24529
- result: {
24530
- resultType: "complete",
24531
- _meta: {
24532
- [SUBSCRIPTION_ID_META_KEY]: subscriptionId,
24533
- [SERVER_INFO_META_KEY]: serverInfo
24534
- }
24535
- }
24536
- })}\n\n`);
24537
- closed = true;
24538
- try {
24539
- unsubscribe?.();
24540
- } catch (error) {
24541
- onerror?.(error instanceof Error ? error : new Error(String(error)));
24542
- }
24543
- if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
24544
- abortCleanup?.();
24545
- open.delete(teardown);
24546
- try {
24547
- controller.close();
24548
- } catch {}
24549
- };
24550
25059
  const readable = new ReadableStream({
24551
- start(streamController) {
25060
+ start: (streamController) => {
24552
25061
  controller = streamController;
24553
- const ack = stampSubscriptionId({
24554
- method: "notifications/subscriptions/acknowledged",
24555
- params: { notifications: honored }
24556
- }, subscriptionId);
24557
- writeNotification(ack.method, ack.params);
24558
- if (Object.keys(honored).length === 0) {
24559
- teardown(true);
24560
- return;
24561
- }
24562
- unsubscribe = bus.subscribe((event) => {
24563
- if (closed || !listenFilterAccepts(honored, event)) return;
24564
- const note = stampSubscriptionId(serverEventToNotification(event), subscriptionId);
24565
- writeNotification(note.method, note.params);
24566
- });
24567
- keepAliveTimer = armSseKeepAlive(keepAliveMs, () => writeFrame(": keepalive\n\n"));
24568
- open.add(teardown);
24569
25062
  },
24570
- cancel() {
24571
- teardown(false);
25063
+ cancel: () => {
25064
+ this.close();
24572
25065
  }
24573
25066
  });
24574
- if (signal !== void 0) if (signal.aborted) teardown(false);
24575
- else {
24576
- const onAbort = () => teardown(false);
24577
- signal.addEventListener("abort", onAbort, { once: true });
24578
- abortCleanup = () => signal.removeEventListener("abort", onAbort);
24579
- }
24580
- return new Response(readable, {
25067
+ this._sse = {
25068
+ controller,
25069
+ encoder: new TextEncoder(),
25070
+ closed: false
25071
+ };
25072
+ this._sse.keepAliveTimer = armSseKeepAlive(this._keepAliveMs, () => this.writeCommentFrame("keepalive"));
25073
+ this.settleResponse(new Response(readable, {
24581
25074
  status: 200,
24582
25075
  headers: {
24583
25076
  "Content-Type": "text/event-stream",
@@ -24585,167 +25078,33 @@ function createListenRouter(options) {
24585
25078
  Connection: "keep-alive",
24586
25079
  "X-Accel-Buffering": "no"
24587
25080
  }
24588
- });
25081
+ }));
24589
25082
  }
24590
- return {
24591
- serve,
24592
- closeAll() {
24593
- for (const teardown of open) teardown(true);
24594
- },
24595
- get openCount() {
24596
- return open.size;
25083
+ finalizeStream() {
25084
+ if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
25085
+ if (this._sse !== void 0 && !this._sse.closed) {
25086
+ this._sse.closed = true;
25087
+ try {
25088
+ this._sse.controller.close();
25089
+ } catch {}
24597
25090
  }
24598
- };
24599
- }
24600
- const CHANGE_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
24601
- "notifications/tools/list_changed",
24602
- "notifications/prompts/list_changed",
24603
- "notifications/resources/list_changed",
24604
- "notifications/resources/updated"
24605
- ]);
24606
- /**
24607
- * Per-connection listen state for the stdio entry. One instance is held by
24608
- * `serveStdio` for the connection lifetime; it routes inbound
24609
- * `subscriptions/listen` / `notifications/cancelled` and rewrites outbound
24610
- * change notifications onto the active subscriptions. No bus — the long-lived
24611
- * pinned instance's existing `send*ListChanged()` calls feed straight into
24612
- * `routeOutbound()`.
24613
- */
24614
- var StdioListenRouter = class {
24615
- /** Active subscriptions, keyed by the listen request's JSON-RPC id verbatim. */
24616
- _subs = /* @__PURE__ */ new Map();
24617
- /**
24618
- * The serving instance's declared capabilities. Filled in by the entry
24619
- * once the modern instance is constructed (the router is created before
24620
- * the instance exists), so the acknowledged filter is narrowed against
24621
- * what the server can actually deliver.
24622
- */
24623
- _serverCapabilities;
24624
- /**
24625
- * The serving instance's identity, stamped onto the graceful-close
24626
- * results' `_meta` (the spec's `SubscriptionsListenResultMetaObject` extends
24627
- * `ResultMetaObject`). Handed over together with the capabilities.
24628
- */
24629
- _serverInfo;
24630
- constructor(_maxSubscriptions = DEFAULT_MAX_SUBSCRIPTIONS, serverCapabilities, serverInfo) {
24631
- this._maxSubscriptions = _maxSubscriptions;
24632
- this._serverCapabilities = serverCapabilities;
24633
- this._serverInfo = serverInfo;
25091
+ queueMicrotask(() => void this.close());
24634
25092
  }
24635
- /**
24636
- * Record the serving instance's declared capabilities and identity once
24637
- * it has been constructed. Called by `serveStdio`'s connect path;
24638
- * subsequent `serve()` calls narrow the honored filter against the
24639
- * capabilities, and `teardownAll()` stamps the identity.
24640
- */
24641
- setServerCapabilities(capabilities, serverInfo) {
24642
- this._serverCapabilities = capabilities;
24643
- if (serverInfo !== void 0) this._serverInfo = serverInfo;
25093
+ writeMessageFrame(message) {
25094
+ this.writeFrame(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
24644
25095
  }
24645
- /** Whether `id` is an active listen subscription on this connection. */
24646
- has(id) {
24647
- return this._subs.has(id);
24648
- }
24649
- /**
24650
- * Serve one inbound `subscriptions/listen` request: registers the
24651
- * subscription and returns the stamped acknowledged notification (or, on
24652
- * capacity / params rejection, the in-band JSON-RPC error response).
24653
- *
24654
- * @throws when called before {@linkcode setServerCapabilities} (or the
24655
- * constructor) has supplied the serving instance's capabilities. Honoring a
24656
- * filter without knowing the server's advertised capabilities would fail
24657
- * open (deliver unadvertised types); the entry guarantees capabilities are
24658
- * set before any listen request is routed here.
24659
- */
24660
- serve(message) {
24661
- if (this._serverCapabilities === void 0) throw new Error("StdioListenRouter.serve() called before setServerCapabilities(); refusing to honor a filter without capabilities");
24662
- if (this._subs.size >= this._maxSubscriptions) return {
24663
- jsonrpc: "2.0",
24664
- id: message.id,
24665
- error: {
24666
- code: -32603,
24667
- message: "Subscription limit reached"
24668
- }
24669
- };
24670
- const filter = parseListenFilter(message);
24671
- if (filter === void 0) return {
24672
- jsonrpc: "2.0",
24673
- id: message.id,
24674
- error: {
24675
- code: -32602,
24676
- message: "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter"
24677
- }
24678
- };
24679
- const honored = honoredSubset(filter, this._serverCapabilities);
24680
- this._subs.set(message.id, honored);
24681
- return stampSubscriptionId({
24682
- method: "notifications/subscriptions/acknowledged",
24683
- params: { notifications: honored }
24684
- }, message.id);
24685
- }
24686
- /**
24687
- * Tear down one subscription (inbound `notifications/cancelled`). Returns
24688
- * `true` when a subscription was removed. After this call NOTHING further
24689
- * is delivered for that subscription id (the post-cancel hardening).
24690
- */
24691
- cancel(id) {
24692
- return this._subs.delete(id);
24693
- }
24694
- /**
24695
- * Route an outbound notification through the active subscriptions.
24696
- *
24697
- * - For a subscription-gated change notification, returns one stamped copy
24698
- * per subscription that opted in to it (an empty array means it is
24699
- * dropped — the modern era never delivers an un-requested change type).
24700
- * - For any other outbound message, returns `'passthrough'` (the entry
24701
- * forwards it as-is).
24702
- */
24703
- routeOutbound(message) {
24704
- if (!CHANGE_NOTIFICATION_METHODS.has(message.method)) return "passthrough";
24705
- const uriParam = message.params?.["uri"];
24706
- const uri = typeof uriParam === "string" ? uriParam : void 0;
24707
- const event = notificationToServerEvent(message.method, uri);
24708
- const out = [];
24709
- for (const [subscriptionId, filter] of this._subs) if (listenFilterAccepts(filter, event)) out.push(stampSubscriptionId({
24710
- method: message.method,
24711
- params: message.params ?? {}
24712
- }, subscriptionId));
24713
- return out;
24714
- }
24715
- /**
24716
- * Server-side graceful teardown of every active subscription: returns the
24717
- * empty `subscriptions/listen` JSON-RPC result for each subscription id —
24718
- * the spec's graceful-close signal, `_meta` carrying the subscription id
24719
- * and the serving instance's identity — for the entry to emit before
24720
- * closing the wire. Clears the set so nothing further is delivered.
24721
- */
24722
- teardownAll() {
24723
- const out = [];
24724
- for (const id of this._subs.keys()) out.push({
24725
- jsonrpc: "2.0",
24726
- id,
24727
- result: {
24728
- resultType: "complete",
24729
- _meta: {
24730
- [SUBSCRIPTION_ID_META_KEY]: id,
24731
- ...this._serverInfo !== void 0 && { ["io.modelcontextprotocol/serverInfo"]: this._serverInfo }
24732
- }
24733
- }
24734
- });
24735
- this._subs.clear();
24736
- return out;
25096
+ writeFrame(frame) {
25097
+ if (this._sse === void 0 || this._sse.closed) return;
25098
+ try {
25099
+ this._sse.controller.enqueue(this._sse.encoder.encode(frame));
25100
+ } catch (error) {
25101
+ this.onerror?.(/* @__PURE__ */ new Error(`Failed to write to the response stream: ${error}`));
25102
+ }
24737
25103
  }
24738
25104
  };
24739
- function notificationToServerEvent(method, uri) {
24740
- switch (method) {
24741
- case "notifications/tools/list_changed": return { kind: "tools_list_changed" };
24742
- case "notifications/prompts/list_changed": return { kind: "prompts_list_changed" };
24743
- case "notifications/resources/list_changed": return { kind: "resources_list_changed" };
24744
- default: return {
24745
- kind: "resource_updated",
24746
- uri: uri ?? ""
24747
- };
24748
- }
25105
+ /** Package-internal: the classification a per-request transport serves; `undefined` for any other transport. Not public API. */
25106
+ function perRequestClassificationOf(transport) {
25107
+ return transport instanceof PerRequestHTTPServerTransport ? readClassification(transport) : void 0;
24749
25108
  }
24750
25109
  function comparableResource(value) {
24751
25110
  const text = String(value);
@@ -25129,6 +25488,7 @@ var Server = class extends Protocol {
25129
25488
  _requestStateVerify;
25130
25489
  _inputRequiredServing;
25131
25490
  _legacyShim;
25491
+ _boundFromTransport = false;
25132
25492
  /** Lazily-built legacy shim; the loop lives in legacyInputRequiredShim.ts behind a narrow host contract. */
25133
25493
  _legacyInputRequiredShim() {
25134
25494
  return this._legacyShim ??= new LegacyInputRequiredShim({
@@ -25168,6 +25528,15 @@ var Server = class extends Protocol {
25168
25528
  /** Attaches to the given transport; rejects while this instance is connected to another one. */
25169
25529
  async connect(transport) {
25170
25530
  if (this.transport !== void 0) throw new SdkError(SdkErrorCode.AlreadyConnected, "Use a separate Server instance per connection: this instance is already connected to a transport. To connect it to a new transport, call close() first.");
25531
+ if (this._boundFromTransport) {
25532
+ this._boundFromTransport = false;
25533
+ this._negotiatedProtocolVersion = void 0;
25534
+ }
25535
+ const revision = perRequestClassificationOf(transport)?.revision;
25536
+ if (revision !== void 0 && !this._servedModernEra() && SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(revision) && modernProtocolVersions(this._supportedProtocolVersions).includes(revision)) {
25537
+ this._negotiatedProtocolVersion = revision;
25538
+ this._boundFromTransport = true;
25539
+ }
25171
25540
  await super.connect(transport);
25172
25541
  }
25173
25542
  /**
@@ -25478,8 +25847,8 @@ var Server = class extends Protocol {
25478
25847
  * @deprecated Read client identity from the per-request handler context instead: on
25479
25848
  * 2026-07-28 (per-request envelope) requests `ctx.mcpReq.envelope` carries the client's
25480
25849
  * declared capabilities, while on 2025-era connections this accessor keeps returning the
25481
- * `initialize`-scoped value. The accessor remains functional — instances serving the
25482
- * 2026-07-28 era are backfilled per request from the validated envelope.
25850
+ * `initialize`-scoped value. The accessor remains functional where `createMcpHandler`
25851
+ * serves the 2026-07-28 era: it backfills it per request from the validated envelope.
25483
25852
  */
25484
25853
  getClientCapabilities() {
25485
25854
  return this._clientCapabilities;
@@ -25490,8 +25859,8 @@ var Server = class extends Protocol {
25490
25859
  * @deprecated Read client identity from the per-request handler context instead: on
25491
25860
  * 2026-07-28 (per-request envelope) requests `ctx.mcpReq.envelope` carries the client's
25492
25861
  * name and version, while on 2025-era connections this accessor keeps returning the
25493
- * `initialize`-scoped value. The accessor remains functional — instances serving the
25494
- * 2026-07-28 era are backfilled per request from the validated envelope.
25862
+ * `initialize`-scoped value. The accessor remains functional where `createMcpHandler`
25863
+ * serves the 2026-07-28 era: it backfills it per request from the validated envelope.
25495
25864
  */
25496
25865
  getClientVersion() {
25497
25866
  return this._clientVersion;
@@ -26084,8 +26453,14 @@ var McpServer = class {
26084
26453
  if (updates.uri) this._registeredResources[updates.uri] = registeredResource;
26085
26454
  }
26086
26455
  if (updates.name !== void 0) registeredResource.name = updates.name;
26087
- if (updates.title !== void 0) registeredResource.title = updates.title;
26088
26456
  if (updates.metadata !== void 0) registeredResource.metadata = updates.metadata;
26457
+ if (updates.title !== void 0) {
26458
+ registeredResource.title = updates.title;
26459
+ registeredResource.metadata = {
26460
+ ...registeredResource.metadata,
26461
+ title: updates.title
26462
+ };
26463
+ }
26089
26464
  if (updates.callback !== void 0) registeredResource.readCallback = updates.callback;
26090
26465
  if (updates.scopeChallenge !== void 0) registeredResource.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
26091
26466
  if (updates.enabled !== void 0) registeredResource.enabled = updates.enabled;
@@ -26111,9 +26486,15 @@ var McpServer = class {
26111
26486
  delete this._registeredResourceTemplates[name];
26112
26487
  if (updates.name) this._registeredResourceTemplates[updates.name] = registeredResourceTemplate;
26113
26488
  }
26114
- if (updates.title !== void 0) registeredResourceTemplate.title = updates.title;
26115
26489
  if (updates.template !== void 0) registeredResourceTemplate.resourceTemplate = updates.template;
26116
26490
  if (updates.metadata !== void 0) registeredResourceTemplate.metadata = updates.metadata;
26491
+ if (updates.title !== void 0) {
26492
+ registeredResourceTemplate.title = updates.title;
26493
+ registeredResourceTemplate.metadata = {
26494
+ ...registeredResourceTemplate.metadata,
26495
+ title: updates.title
26496
+ };
26497
+ }
26117
26498
  if (updates.callback !== void 0) registeredResourceTemplate.readCallback = updates.callback;
26118
26499
  if (updates.scopeChallenge !== void 0) registeredResourceTemplate.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
26119
26500
  if (updates.enabled !== void 0) registeredResourceTemplate.enabled = updates.enabled;
@@ -26417,189 +26798,253 @@ function unwrapOptionalSchema(schema) {
26417
26798
  return schema.def?.innerType ?? schema;
26418
26799
  }
26419
26800
  //#endregion
26420
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/index.mjs
26801
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/listenRouter-Bartj63y.mjs
26421
26802
  /**
26422
- * The per-request micro-transport: a real, connected `Transport` whose whole
26423
- * lifetime is one HTTP exchange. See the module documentation for the
26424
- * response shapes it produces.
26803
+ * A `ServerEventBus` backed by an in-process listener set.
26804
+ *
26805
+ * `publish()` delivers synchronously to the live listener set (a listener
26806
+ * unsubscribing itself mid-dispatch is safe; the entry's listen-router
26807
+ * listeners never unsubscribe peers). A throwing listener does not stop
26808
+ * delivery to the others.
26425
26809
  */
26426
- var PerRequestHTTPServerTransport = class {
26427
- onclose;
26428
- onerror;
26429
- onmessage;
26430
- _classification;
26431
- _responseMode;
26432
- _started = false;
26433
- _used = false;
26434
- _closed = false;
26435
- _terminalDelivered = false;
26810
+ var InMemoryServerEventBus = class {
26811
+ _listeners = /* @__PURE__ */ new Set();
26436
26812
  /**
26437
- * `true` only while the inbound message is being delivered synchronously
26438
- * to the connected protocol layer. The pre-handler gates (the era
26439
- * registry gate, the edge→instance handoff check, the missing-handler
26440
- * rejection) answer inside this window; request handlers always run
26441
- * after it (the protocol layer defers them to a microtask). An error
26442
- * sent inside the window is therefore ladder-originated, and an error
26443
- * sent after it is handler-produced.
26813
+ * @param onerror - Optional callback for errors thrown by listeners
26814
+ * during dispatch.
26444
26815
  */
26445
- _dispatchWindowOpen = false;
26446
- _requestId;
26447
- _deferredResponse;
26448
- _sse;
26449
- _abortCleanup;
26450
- _keepAliveMs;
26451
- constructor(options) {
26452
- this._classification = options.classification;
26453
- this._responseMode = options.responseMode ?? "auto";
26454
- this._keepAliveMs = options.keepAliveMs ?? 15e3;
26455
- }
26456
- async start() {
26457
- if (this._started) throw new Error("PerRequestHTTPServerTransport is already started");
26458
- this._started = true;
26816
+ constructor(onerror) {
26817
+ this.onerror = onerror;
26459
26818
  }
26460
- /**
26461
- * Serves the single exchange: delivers the classified message to the
26462
- * connected server instance and resolves with the HTTP response.
26463
- *
26464
- * Throws when called a second time (the transport is strictly
26465
- * single-use), or before a server has been connected to the transport.
26466
- * The returned promise rejects with a connection-closed error when the
26467
- * transport is closed before a response was produced (for example because
26468
- * the client disconnected).
26469
- */
26470
- async handleMessage(message, extra) {
26471
- if (this._used) throw new Error("PerRequestHTTPServerTransport serves exactly one exchange; construct a new transport per request");
26472
- if (!this._started || this.onmessage === void 0) throw new Error("PerRequestHTTPServerTransport is not connected: connect a server to this transport before handling a message");
26473
- if (this._closed) throw new Error("PerRequestHTTPServerTransport is closed");
26474
- this._used = true;
26475
- const signal = extra?.request?.signal;
26476
- if (signal?.aborted) {
26477
- await this.close();
26478
- throw new SdkError(SdkErrorCode.ConnectionClosed, "The request was aborted before it could be handled");
26819
+ publish(event) {
26820
+ for (const listener of this._listeners) try {
26821
+ listener(event);
26822
+ } catch (error) {
26823
+ this.onerror?.(error instanceof Error ? error : new Error(String(error)));
26479
26824
  }
26480
- const messageExtra = {
26481
- classification: this._classification,
26482
- ...extra?.request !== void 0 && { request: extra.request },
26483
- ...extra?.authInfo !== void 0 && { authInfo: extra.authInfo }
26825
+ }
26826
+ subscribe(listener) {
26827
+ this._listeners.add(listener);
26828
+ let live = true;
26829
+ return () => {
26830
+ if (!live) return;
26831
+ live = false;
26832
+ this._listeners.delete(listener);
26484
26833
  };
26485
- if (isJSONRPCRequest(message)) {
26486
- this._requestId = message.id;
26487
- let resolve;
26488
- let reject;
26489
- const promise = new Promise((promiseResolve, promiseReject) => {
26490
- resolve = promiseResolve;
26491
- reject = promiseReject;
26492
- });
26493
- this._deferredResponse = {
26494
- promise,
26495
- resolve,
26496
- reject,
26497
- settled: false
26498
- };
26499
- if (signal !== void 0) {
26500
- const onAbort = () => void this.close();
26501
- signal.addEventListener("abort", onAbort, { once: true });
26502
- this._abortCleanup = () => signal.removeEventListener("abort", onAbort);
26834
+ }
26835
+ /** The number of currently registered listeners (test/introspection only — the routers track capacity via their own open-subscription set). */
26836
+ get listenerCount() {
26837
+ return this._listeners.size;
26838
+ }
26839
+ };
26840
+ /** Build a {@linkcode ServerNotifier} over a bus. */
26841
+ function createServerNotifier(bus) {
26842
+ return {
26843
+ toolsChanged: () => bus.publish({ kind: "tools_list_changed" }),
26844
+ promptsChanged: () => bus.publish({ kind: "prompts_list_changed" }),
26845
+ resourcesChanged: () => bus.publish({ kind: "resources_list_changed" }),
26846
+ resourceUpdated: (uri) => bus.publish({
26847
+ kind: "resource_updated",
26848
+ uri
26849
+ })
26850
+ };
26851
+ }
26852
+ /**
26853
+ * Whether a `subscriptions/listen` filter accepts a given change event.
26854
+ *
26855
+ * Pure: no I/O, no mutation. The filter governs ONLY the four
26856
+ * subscription-gated change types — non-gated notifications never reach the
26857
+ * bus and are not modeled here.
26858
+ *
26859
+ * `resource_updated` matches only when `resourceSubscriptions` is present and
26860
+ * contains the event's URI exactly (per the spec: "for these resource URIs").
26861
+ */
26862
+ function listenFilterAccepts(filter, event) {
26863
+ switch (event.kind) {
26864
+ case "tools_list_changed": return filter.toolsListChanged === true;
26865
+ case "prompts_list_changed": return filter.promptsListChanged === true;
26866
+ case "resources_list_changed": return filter.resourcesListChanged === true;
26867
+ case "resource_updated": return filter.resourceSubscriptions !== void 0 && filter.resourceSubscriptions.includes(event.uri);
26868
+ }
26869
+ }
26870
+ /**
26871
+ * The honored subset of a requested filter: keeps only the fields the client
26872
+ * explicitly opted in to (drops `false` and absent fields), narrowed against
26873
+ * the server's declared capabilities when supplied. The serving entry sends
26874
+ * this back in `notifications/subscriptions/acknowledged` so the ack reflects
26875
+ * what the server can actually deliver.
26876
+ *
26877
+ * - `toolsListChanged` is honored only when `capabilities.tools.listChanged`
26878
+ * is advertised; likewise `promptsListChanged` / `resourcesListChanged`.
26879
+ * - `resourceSubscriptions` is honored only when
26880
+ * `capabilities.resources.subscribe` is advertised.
26881
+ *
26882
+ * `capabilities` is optional on this pure helper for test convenience only —
26883
+ * both wired routers REQUIRE capabilities at the call site (the HTTP router's
26884
+ * `serve()` takes a required parameter; `StdioListenRouter.serve()` throws
26885
+ * before `setServerCapabilities()` was called), so the fail-open
26886
+ * `undefined → honor everything` branch is never reachable on a wired entry.
26887
+ */
26888
+ function honoredSubset(requested, capabilities) {
26889
+ const honored = {};
26890
+ const allow = (bit) => capabilities === void 0 || bit === true;
26891
+ if (requested.toolsListChanged === true && allow(capabilities?.tools?.listChanged)) honored.toolsListChanged = true;
26892
+ if (requested.promptsListChanged === true && allow(capabilities?.prompts?.listChanged)) honored.promptsListChanged = true;
26893
+ if (requested.resourcesListChanged === true && allow(capabilities?.resources?.listChanged)) honored.resourcesListChanged = true;
26894
+ if (requested.resourceSubscriptions !== void 0 && requested.resourceSubscriptions.length > 0 && allow(capabilities?.resources?.subscribe)) honored.resourceSubscriptions = [...requested.resourceSubscriptions];
26895
+ return honored;
26896
+ }
26897
+ /** Map a {@linkcode ServerEvent} onto its wire notification `{method, params}`. */
26898
+ function serverEventToNotification(event) {
26899
+ switch (event.kind) {
26900
+ case "tools_list_changed": return {
26901
+ method: "notifications/tools/list_changed",
26902
+ ...event.params !== void 0 && { params: event.params }
26903
+ };
26904
+ case "prompts_list_changed": return {
26905
+ method: "notifications/prompts/list_changed",
26906
+ ...event.params !== void 0 && { params: event.params }
26907
+ };
26908
+ case "resources_list_changed": return {
26909
+ method: "notifications/resources/list_changed",
26910
+ ...event.params !== void 0 && { params: event.params }
26911
+ };
26912
+ case "resource_updated": return {
26913
+ method: "notifications/resources/updated",
26914
+ params: {
26915
+ ...event.params,
26916
+ uri: event.uri
26503
26917
  }
26504
- this._dispatchWindowOpen = true;
26505
- try {
26506
- this.onmessage(message, messageExtra);
26507
- } finally {
26508
- this._dispatchWindowOpen = false;
26918
+ };
26919
+ }
26920
+ }
26921
+ /** Default capacity guard: refuse a new subscription when this many are already open. */
26922
+ const DEFAULT_MAX_SUBSCRIPTIONS = 1024;
26923
+ function jsonRpcError(id, code, message) {
26924
+ return Response.json({
26925
+ jsonrpc: "2.0",
26926
+ error: {
26927
+ code,
26928
+ message
26929
+ },
26930
+ id
26931
+ }, { status: 200 });
26932
+ }
26933
+ /** Stamp the subscription id onto a notification's `_meta`. Non-mutating. */
26934
+ function stampSubscriptionId(notification, subscriptionId) {
26935
+ return {
26936
+ method: notification.method,
26937
+ params: {
26938
+ ...notification.params,
26939
+ _meta: {
26940
+ ...notification.params?._meta,
26941
+ [SUBSCRIPTION_ID_META_KEY]: subscriptionId
26509
26942
  }
26510
- if (this._responseMode === "sse" && !this._closed && !this._deferredResponse.settled) this.upgradeToSse();
26511
- return promise;
26512
26943
  }
26513
- this.onmessage(message, messageExtra);
26514
- return new Response(null, { status: 202 });
26515
- }
26516
- async send(message, options) {
26517
- if (this._closed) return;
26518
- const isResponse = isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message);
26519
- const relatedId = isResponse ? message.id : options?.relatedRequestId;
26520
- if (this._requestId === void 0 || relatedId === void 0 || relatedId !== this._requestId) {
26521
- if (isResponse) this.onerror?.(/* @__PURE__ */ new Error(`Received a response for an unknown request id: ${String(message.id)}`));
26522
- return;
26944
+ };
26945
+ }
26946
+ /**
26947
+ * Read the requested filter off a `subscriptions/listen` request body.
26948
+ * Returns the validated filter, or `undefined` when `params.notifications`
26949
+ * is absent or fails the schema (the caller answers `-32602` — the spec
26950
+ * marks `notifications` REQUIRED on the listen request).
26951
+ */
26952
+ function parseListenFilter(message) {
26953
+ const outcome = codecForVersion(MODERN_WIRE_REVISION).validateRequest("subscriptions/listen", message);
26954
+ return outcome.ok ? outcome.value.params?.notifications : void 0;
26955
+ }
26956
+ function createListenRouter(options) {
26957
+ const { bus, onerror } = options;
26958
+ const maxSubscriptions = options.maxSubscriptions ?? 1024;
26959
+ const keepAliveMs = options.keepAliveMs ?? 15e3;
26960
+ const open = /* @__PURE__ */ new Set();
26961
+ function serve(message, signal, capabilities, serverInfo) {
26962
+ if (open.size >= maxSubscriptions) {
26963
+ onerror?.(/* @__PURE__ */ new Error(`subscriptions/listen refused: subscription limit reached (${maxSubscriptions})`));
26964
+ return jsonRpcError(message.id, -32603, "Subscription limit reached");
26523
26965
  }
26524
- if (isResponse) {
26525
- if (this._terminalDelivered) return;
26526
- this._terminalDelivered = true;
26527
- const errorCode = isJSONRPCErrorResponse(message) ? message.error.code : void 0;
26528
- const ladderStatus = errorCode !== void 0 && (this._dispatchWindowOpen || errorCode === ProtocolErrorCode.MissingRequiredClientCapability) ? LADDER_ERROR_HTTP_STATUS[errorCode] : void 0;
26529
- if (ladderStatus !== void 0 && this._sse === void 0) {
26530
- this.settleResponse(Response.json(message, {
26531
- status: ladderStatus,
26532
- headers: { "Content-Type": "application/json" }
26533
- }));
26534
- queueMicrotask(() => void this.close());
26535
- return;
26536
- }
26537
- if (this._sse !== void 0 || this._responseMode === "sse") {
26538
- if (this._sse === void 0) this.upgradeToSse();
26539
- this.writeMessageFrame(message);
26540
- this.finalizeStream();
26541
- return;
26966
+ const filter = parseListenFilter(message);
26967
+ if (filter === void 0) return jsonRpcError(message.id, -32602, "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter");
26968
+ const honored = honoredSubset(filter, capabilities);
26969
+ const subscriptionId = message.id;
26970
+ const encoder = new TextEncoder();
26971
+ let controller;
26972
+ let closed = false;
26973
+ let unsubscribe;
26974
+ let keepAliveTimer;
26975
+ let abortCleanup;
26976
+ const writeFrame = (frame) => {
26977
+ if (closed) return;
26978
+ try {
26979
+ controller.enqueue(encoder.encode(frame));
26980
+ } catch (error) {
26981
+ onerror?.(error instanceof Error ? error : new Error(String(error)));
26542
26982
  }
26543
- this.settleResponse(Response.json(message, {
26544
- status: 200,
26545
- headers: { "Content-Type": "application/json" }
26546
- }));
26547
- queueMicrotask(() => void this.close());
26548
- return;
26549
- }
26550
- if (this._responseMode === "json") return;
26551
- if (this._sse === void 0) this.upgradeToSse();
26552
- this.writeMessageFrame(message);
26553
- }
26554
- /**
26555
- * Writes an SSE comment frame (a keep-alive heartbeat). Dropped when the
26556
- * exchange is not currently streaming.
26557
- */
26558
- writeCommentFrame(comment) {
26559
- if (this._closed || this._sse === void 0 || this._sse.closed) return;
26560
- const frame = comment.split("\n").map((line) => `: ${line}`).join("\n");
26561
- this.writeFrame(`${frame}\n\n`);
26562
- }
26563
- async close() {
26564
- if (this._closed) return;
26565
- this._closed = true;
26566
- this._abortCleanup?.();
26567
- this._abortCleanup = void 0;
26568
- if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
26569
- if (this._sse !== void 0 && !this._sse.closed) {
26570
- this._sse.closed = true;
26983
+ };
26984
+ const writeNotification = (method, params) => {
26985
+ writeFrame(`event: message\ndata: ${JSON.stringify({
26986
+ jsonrpc: "2.0",
26987
+ method,
26988
+ params
26989
+ })}\n\n`);
26990
+ };
26991
+ const teardown = (graceful) => {
26992
+ if (closed) return;
26993
+ if (graceful) writeFrame(`event: message\ndata: ${JSON.stringify({
26994
+ jsonrpc: "2.0",
26995
+ id: subscriptionId,
26996
+ result: {
26997
+ resultType: "complete",
26998
+ _meta: {
26999
+ [SUBSCRIPTION_ID_META_KEY]: subscriptionId,
27000
+ [SERVER_INFO_META_KEY]: serverInfo
27001
+ }
27002
+ }
27003
+ })}\n\n`);
27004
+ closed = true;
26571
27005
  try {
26572
- this._sse.controller.close();
27006
+ unsubscribe?.();
27007
+ } catch (error) {
27008
+ onerror?.(error instanceof Error ? error : new Error(String(error)));
27009
+ }
27010
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
27011
+ abortCleanup?.();
27012
+ open.delete(teardown);
27013
+ try {
27014
+ controller.close();
26573
27015
  } catch {}
26574
- }
26575
- if (this._deferredResponse !== void 0 && !this._deferredResponse.settled) {
26576
- this._deferredResponse.settled = true;
26577
- this._deferredResponse.reject(new SdkError(SdkErrorCode.ConnectionClosed, "Connection closed before a response was produced"));
26578
- }
26579
- this.onclose?.();
26580
- }
26581
- settleResponse(response) {
26582
- if (this._deferredResponse === void 0 || this._deferredResponse.settled) return;
26583
- this._deferredResponse.settled = true;
26584
- this._deferredResponse.resolve(response);
26585
- }
26586
- upgradeToSse() {
26587
- let controller;
27016
+ };
26588
27017
  const readable = new ReadableStream({
26589
- start: (streamController) => {
27018
+ start(streamController) {
26590
27019
  controller = streamController;
27020
+ const ack = stampSubscriptionId({
27021
+ method: "notifications/subscriptions/acknowledged",
27022
+ params: { notifications: honored }
27023
+ }, subscriptionId);
27024
+ writeNotification(ack.method, ack.params);
27025
+ if (Object.keys(honored).length === 0) {
27026
+ teardown(true);
27027
+ return;
27028
+ }
27029
+ unsubscribe = bus.subscribe((event) => {
27030
+ if (closed || !listenFilterAccepts(honored, event)) return;
27031
+ const note = stampSubscriptionId(serverEventToNotification(event), subscriptionId);
27032
+ writeNotification(note.method, note.params);
27033
+ });
27034
+ keepAliveTimer = armSseKeepAlive(keepAliveMs, () => writeFrame(": keepalive\n\n"));
27035
+ open.add(teardown);
26591
27036
  },
26592
- cancel: () => {
26593
- this.close();
27037
+ cancel() {
27038
+ teardown(false);
26594
27039
  }
26595
27040
  });
26596
- this._sse = {
26597
- controller,
26598
- encoder: new TextEncoder(),
26599
- closed: false
26600
- };
26601
- this._sse.keepAliveTimer = armSseKeepAlive(this._keepAliveMs, () => this.writeCommentFrame("keepalive"));
26602
- this.settleResponse(new Response(readable, {
27041
+ if (signal !== void 0) if (signal.aborted) teardown(false);
27042
+ else {
27043
+ const onAbort = () => teardown(false);
27044
+ signal.addEventListener("abort", onAbort, { once: true });
27045
+ abortCleanup = () => signal.removeEventListener("abort", onAbort);
27046
+ }
27047
+ return new Response(readable, {
26603
27048
  status: 200,
26604
27049
  headers: {
26605
27050
  "Content-Type": "text/event-stream",
@@ -26607,30 +27052,170 @@ var PerRequestHTTPServerTransport = class {
26607
27052
  Connection: "keep-alive",
26608
27053
  "X-Accel-Buffering": "no"
26609
27054
  }
26610
- }));
27055
+ });
26611
27056
  }
26612
- finalizeStream() {
26613
- if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
26614
- if (this._sse !== void 0 && !this._sse.closed) {
26615
- this._sse.closed = true;
26616
- try {
26617
- this._sse.controller.close();
26618
- } catch {}
27057
+ return {
27058
+ serve,
27059
+ closeAll() {
27060
+ for (const teardown of open) teardown(true);
27061
+ },
27062
+ get openCount() {
27063
+ return open.size;
26619
27064
  }
26620
- queueMicrotask(() => void this.close());
27065
+ };
27066
+ }
27067
+ const CHANGE_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
27068
+ "notifications/tools/list_changed",
27069
+ "notifications/prompts/list_changed",
27070
+ "notifications/resources/list_changed",
27071
+ "notifications/resources/updated"
27072
+ ]);
27073
+ /**
27074
+ * Per-connection listen state for the stdio entry. One instance is held by
27075
+ * `serveStdio` for the connection lifetime; it routes inbound
27076
+ * `subscriptions/listen` / `notifications/cancelled` and rewrites outbound
27077
+ * change notifications onto the active subscriptions. No bus — the long-lived
27078
+ * pinned instance's existing `send*ListChanged()` calls feed straight into
27079
+ * `routeOutbound()`.
27080
+ */
27081
+ var StdioListenRouter = class {
27082
+ /** Active subscriptions, keyed by the listen request's JSON-RPC id verbatim. */
27083
+ _subs = /* @__PURE__ */ new Map();
27084
+ /**
27085
+ * The serving instance's declared capabilities. Filled in by the entry
27086
+ * once the modern instance is constructed (the router is created before
27087
+ * the instance exists), so the acknowledged filter is narrowed against
27088
+ * what the server can actually deliver.
27089
+ */
27090
+ _serverCapabilities;
27091
+ /**
27092
+ * The serving instance's identity, stamped onto the graceful-close
27093
+ * results' `_meta` (the spec's `SubscriptionsListenResultMetaObject` extends
27094
+ * `ResultMetaObject`). Handed over together with the capabilities.
27095
+ */
27096
+ _serverInfo;
27097
+ constructor(_maxSubscriptions = DEFAULT_MAX_SUBSCRIPTIONS, serverCapabilities, serverInfo) {
27098
+ this._maxSubscriptions = _maxSubscriptions;
27099
+ this._serverCapabilities = serverCapabilities;
27100
+ this._serverInfo = serverInfo;
26621
27101
  }
26622
- writeMessageFrame(message) {
26623
- this.writeFrame(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
27102
+ /**
27103
+ * Record the serving instance's declared capabilities and identity once
27104
+ * it has been constructed. Called by `serveStdio`'s connect path;
27105
+ * subsequent `serve()` calls narrow the honored filter against the
27106
+ * capabilities, and `teardownAll()` stamps the identity.
27107
+ */
27108
+ setServerCapabilities(capabilities, serverInfo) {
27109
+ this._serverCapabilities = capabilities;
27110
+ if (serverInfo !== void 0) this._serverInfo = serverInfo;
26624
27111
  }
26625
- writeFrame(frame) {
26626
- if (this._sse === void 0 || this._sse.closed) return;
26627
- try {
26628
- this._sse.controller.enqueue(this._sse.encoder.encode(frame));
26629
- } catch (error) {
26630
- this.onerror?.(/* @__PURE__ */ new Error(`Failed to write to the response stream: ${error}`));
26631
- }
27112
+ /** Whether `id` is an active listen subscription on this connection. */
27113
+ has(id) {
27114
+ return this._subs.has(id);
27115
+ }
27116
+ /**
27117
+ * Serve one inbound `subscriptions/listen` request: registers the
27118
+ * subscription and returns the stamped acknowledged notification (or, on
27119
+ * capacity / params rejection, the in-band JSON-RPC error response).
27120
+ *
27121
+ * @throws when called before {@linkcode setServerCapabilities} (or the
27122
+ * constructor) has supplied the serving instance's capabilities. Honoring a
27123
+ * filter without knowing the server's advertised capabilities would fail
27124
+ * open (deliver unadvertised types); the entry guarantees capabilities are
27125
+ * set before any listen request is routed here.
27126
+ */
27127
+ serve(message) {
27128
+ if (this._serverCapabilities === void 0) throw new Error("StdioListenRouter.serve() called before setServerCapabilities(); refusing to honor a filter without capabilities");
27129
+ if (this._subs.size >= this._maxSubscriptions) return {
27130
+ jsonrpc: "2.0",
27131
+ id: message.id,
27132
+ error: {
27133
+ code: -32603,
27134
+ message: "Subscription limit reached"
27135
+ }
27136
+ };
27137
+ const filter = parseListenFilter(message);
27138
+ if (filter === void 0) return {
27139
+ jsonrpc: "2.0",
27140
+ id: message.id,
27141
+ error: {
27142
+ code: -32602,
27143
+ message: "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter"
27144
+ }
27145
+ };
27146
+ const honored = honoredSubset(filter, this._serverCapabilities);
27147
+ this._subs.set(message.id, honored);
27148
+ return stampSubscriptionId({
27149
+ method: "notifications/subscriptions/acknowledged",
27150
+ params: { notifications: honored }
27151
+ }, message.id);
27152
+ }
27153
+ /**
27154
+ * Tear down one subscription (inbound `notifications/cancelled`). Returns
27155
+ * `true` when a subscription was removed. After this call NOTHING further
27156
+ * is delivered for that subscription id (the post-cancel hardening).
27157
+ */
27158
+ cancel(id) {
27159
+ return this._subs.delete(id);
27160
+ }
27161
+ /**
27162
+ * Route an outbound notification through the active subscriptions.
27163
+ *
27164
+ * - For a subscription-gated change notification, returns one stamped copy
27165
+ * per subscription that opted in to it (an empty array means it is
27166
+ * dropped — the modern era never delivers an un-requested change type).
27167
+ * - For any other outbound message, returns `'passthrough'` (the entry
27168
+ * forwards it as-is).
27169
+ */
27170
+ routeOutbound(message) {
27171
+ if (!CHANGE_NOTIFICATION_METHODS.has(message.method)) return "passthrough";
27172
+ const uriParam = message.params?.["uri"];
27173
+ const uri = typeof uriParam === "string" ? uriParam : void 0;
27174
+ const event = notificationToServerEvent(message.method, uri);
27175
+ const out = [];
27176
+ for (const [subscriptionId, filter] of this._subs) if (listenFilterAccepts(filter, event)) out.push(stampSubscriptionId({
27177
+ method: message.method,
27178
+ params: message.params ?? {}
27179
+ }, subscriptionId));
27180
+ return out;
27181
+ }
27182
+ /**
27183
+ * Server-side graceful teardown of every active subscription: returns the
27184
+ * empty `subscriptions/listen` JSON-RPC result for each subscription id —
27185
+ * the spec's graceful-close signal, `_meta` carrying the subscription id
27186
+ * and the serving instance's identity — for the entry to emit before
27187
+ * closing the wire. Clears the set so nothing further is delivered.
27188
+ */
27189
+ teardownAll() {
27190
+ const out = [];
27191
+ for (const id of this._subs.keys()) out.push({
27192
+ jsonrpc: "2.0",
27193
+ id,
27194
+ result: {
27195
+ resultType: "complete",
27196
+ _meta: {
27197
+ [SUBSCRIPTION_ID_META_KEY]: id,
27198
+ ...this._serverInfo !== void 0 && { ["io.modelcontextprotocol/serverInfo"]: this._serverInfo }
27199
+ }
27200
+ }
27201
+ });
27202
+ this._subs.clear();
27203
+ return out;
26632
27204
  }
26633
27205
  };
27206
+ function notificationToServerEvent(method, uri) {
27207
+ switch (method) {
27208
+ case "notifications/tools/list_changed": return { kind: "tools_list_changed" };
27209
+ case "notifications/prompts/list_changed": return { kind: "prompts_list_changed" };
27210
+ case "notifications/resources/list_changed": return { kind: "resources_list_changed" };
27211
+ default: return {
27212
+ kind: "resource_updated",
27213
+ uri: uri ?? ""
27214
+ };
27215
+ }
27216
+ }
27217
+ //#endregion
27218
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/index.mjs
26634
27219
  /**
26635
27220
  * Serves one classified inbound message on the given server instance and
26636
27221
  * returns the HTTP response for the exchange.
@@ -27430,7 +28015,10 @@ function jsonRpcErrorResponse(httpStatus, code, message, data, id = null) {
27430
28015
  ...data !== void 0 && { data }
27431
28016
  },
27432
28017
  id
27433
- }, { status: httpStatus });
28018
+ }, {
28019
+ status: httpStatus,
28020
+ ...httpStatus === 405 && { headers: { Allow: "POST" } }
28021
+ });
27434
28022
  }
27435
28023
  function rejectionResponse(rejection, id = null) {
27436
28024
  return jsonRpcErrorResponse(rejection.httpStatus, rejection.code, rejection.message, rejection.data, id);
@@ -27793,7 +28381,7 @@ function createMcpHandler(factory, options = {}) {
27793
28381
  const inputSchema = toolName === void 0 ? void 0 : product.toolInputSchemaJson(toolName);
27794
28382
  if (inputSchema !== void 0) {
27795
28383
  const scan = scanXMcpHeaderDeclarations(inputSchema);
27796
- if (scan.valid && scan.declarations.length > 0) {
28384
+ if (scan.declarations !== void 0 && scan.declarations.length > 0) {
27797
28385
  const rejection = validateMcpParamHeaders(scan.declarations, callParams?.arguments, request.headers);
27798
28386
  if (rejection !== void 0) {
27799
28387
  product.close().catch(reportError);
@@ -28419,7 +29007,7 @@ const memoisedUntilFailure = (load) => {
28419
29007
  * processes never touch an article, and it is the heaviest code in the bundle.
28420
29008
  * `tests/unit/module-graph.test.ts` keeps it off the static graph.
28421
29009
  */
28422
- const loadArticleFormat = memoisedUntilFailure(() => import("./article-sections-CzTODYsS.js"));
29010
+ const loadArticleFormat = memoisedUntilFailure(() => import("./article-sections-DA7_Jm2H.js"));
28423
29011
  //#endregion
28424
29012
  //#region src/utils/formatting.ts
28425
29013
  const truncateIfNeeded = (text, advice = "Use pagination or filters to reduce results.") => {
@@ -33761,9 +34349,9 @@ const registerSingleProxies = (registration, tools) => {
33761
34349
  * (`dev/reload.ts`) can keep this long-lived shell (and its transport/session)
33762
34350
  * alive while swapping the toolset underneath it via `registerToolset`.
33763
34351
  */
33764
- const createServerShell = (config, requestStateCodec = createRequestStateCodec({})) => {
34352
+ const createServerShell = (config, requestStateCodec = createRequestStateCodec({}), extraInstructions) => {
33765
34353
  const pkg = readPackageInfo();
33766
- const instructions = buildInstructions(config);
34354
+ const instructions = [buildInstructions(config), extraInstructions].filter((block) => block !== void 0).join("\n\n") || void 0;
33767
34355
  return new McpServer({
33768
34356
  name: pkg.name,
33769
34357
  version: pkg.version
@@ -34081,8 +34669,8 @@ const toolEnv = (config) => ({
34081
34669
  subdomain: config.subdomain,
34082
34670
  brandIds: config.brandIds
34083
34671
  });
34084
- const createMcpServer = (config, getToken, logger = silentLogger, onUnauthorized, { caches = createServerCaches(config, getToken, onUnauthorized), requestStateCodec, slaRuntime = createSlaRuntime(config), listens, getBackgroundToken, alertPolling = "server", tools = createAllTools(toolEnv(config)) } = {}) => {
34085
- const server = createServerShell(config, requestStateCodec);
34672
+ const createMcpServer = (config, getToken, logger = silentLogger, onUnauthorized, { caches = createServerCaches(config, getToken, onUnauthorized), requestStateCodec, slaRuntime = createSlaRuntime(config), listens, getBackgroundToken, alertPolling = "server", tools = createAllTools(toolEnv(config)), extraInstructions, decorate } = {}) => {
34673
+ const server = createServerShell(config, requestStateCodec, extraInstructions);
34086
34674
  const sla = createSlaService(slaSettings(config), slaRuntime, caches.resolveUserId);
34087
34675
  registerToolset(server, {
34088
34676
  config,
@@ -34104,9 +34692,12 @@ const createMcpServer = (config, getToken, logger = silentLogger, onUnauthorized
34104
34692
  installAlertSubscriptions(server, config, reader, logger, listens);
34105
34693
  }
34106
34694
  }
34695
+ decorate?.(server);
34107
34696
  return server;
34108
34697
  };
34109
- const defaultServerFactory = ({ config, getToken, logger, onUnauthorized, caches, tools, requestStateCodec, slaRuntime, listens, getBackgroundToken, alertPolling }) => createMcpServer(config, getToken, logger, onUnauthorized, {
34698
+ const defaultServerFactory = ({ config, getToken, logger, onUnauthorized, caches, tools, requestStateCodec, slaRuntime, listens, getBackgroundToken, alertPolling, extraInstructions, decorate }) => createMcpServer(config, getToken, logger, onUnauthorized, {
34699
+ extraInstructions,
34700
+ decorate,
34110
34701
  ...caches ? { caches } : {},
34111
34702
  ...tools ? { tools } : {},
34112
34703
  ...slaRuntime ? { slaRuntime } : {},
@@ -34116,7 +34707,7 @@ const defaultServerFactory = ({ config, getToken, logger, onUnauthorized, caches
34116
34707
  requestStateCodec
34117
34708
  });
34118
34709
  //#endregion
34119
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
34710
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
34120
34711
  /**
34121
34712
  * Marks a stdout `'error'` listener that a closed transport left attached purely to
34122
34713
  * swallow late write failures (see {@link StdioServerTransport.close}). A fresh
@@ -35146,7 +35737,7 @@ const installShutdown = (options) => {
35146
35737
  //#endregion
35147
35738
  //#region src/index.ts
35148
35739
  const buildStdioTokenStore = async (config, logger) => {
35149
- const { createTokenStore } = await import("./token-store-BqwB-30K.js");
35740
+ const { createTokenStore } = await import("./token-store-EwRwZiJO.js");
35150
35741
  return createTokenStore({
35151
35742
  subdomain: config.subdomain,
35152
35743
  oauthClientId: config.oauthClientId,
@@ -35196,7 +35787,7 @@ const main = async () => {
35196
35787
  logger,
35197
35788
  cleanup: async () => (await starting?.catch(() => void 0))?.close()
35198
35789
  });
35199
- const { startHttpTransport } = await import("./http-D-Xy9BWD.js");
35790
+ const { startHttpTransport } = await import("./http-DuRcQrOc.js");
35200
35791
  starting = startHttpTransport(config, logger);
35201
35792
  await starting;
35202
35793
  };
@@ -35206,4 +35797,4 @@ main().catch((error) => {
35206
35797
  process.exit(1);
35207
35798
  });
35208
35799
  //#endregion
35209
- export { STARTUP_DOCS as A, WebStandardStreamableHTTPServerTransport as C, OAuthError as D, verifyBearerToken as E, errnoCode as M, OAuthErrorCode as O, DEFAULT_MAX_REQUEST_BODY_SIZE as S, isLegacyRequest as T, zendeskGet as _, toolEnv as a, DEFAULT_CALLBACK_PORT as b, clearToken as c, resolveTokenPath as d, saveToken as f, createAllTools as g, silentLogger as h, defaultServerFactory as i, createStartupError as j, compareCodeUnits as k, configDir as l, createRequestStateCodec as m, listenedUris as n, watchGrantAlerts as o, signInRequiredError as p, createServerCaches as r, startSlaRuntime as s, createListenFeed as t, loadToken as u, REQUEST_TIMEOUT_MS as v, createMcpHandler as w, getOAuthUrls as x, deadlineSignal as y };
35800
+ export { _enum as $, ResourceTemplate as A, stampErrorBrands as B, DEFAULT_CALLBACK_PORT as C, createMcpHandler as D, WebStandardStreamableHTTPServerTransport as E, ProtocolErrorCode as F, ResultSchema as G, PaginatedRequestParamsSchema as H, ResourceNotFoundError as I, toStrictStandardSchema as J, buildInstructions as K, assertValidCacheHint as L, OAuthError as M, OAuthErrorCode as N, isLegacyRequest as O, ProtocolError as P, errnoCode as Q, brandedHasInstance as R, deadlineSignal as S, DEFAULT_MAX_REQUEST_BODY_SIZE as T, PaginatedResultSchema as U, BaseRequestParamsSchema as V, ResourceSchema as W, STARTUP_DOCS as X, compareCodeUnits as Y, createStartupError as Z, createAllTools as _, toolEnv as a, object as at, zendeskGet as b, clearToken as c, union as ct, resolveTokenPath as d, array as et, saveToken as f, silentLogger as g, readPackageInfo as h, defaultServerFactory as i, number$1 as it, verifyBearerToken as j, McpServer as k, configDir as l, unknown as lt, createRequestStateCodec as m, listenedUris as n, literal as nt, watchGrantAlerts as o, record as ot, signInRequiredError as p, Namespace as q, createServerCaches as r, looseObject as rt, startSlaRuntime as s, string as st, createListenFeed as t, boolean as tt, loadToken as u, createTopologyProvider as v, getOAuthUrls as w, REQUEST_TIMEOUT_MS as x, ZendeskApiError as y, isModernProtocolVersion as z };