@redocly/realm 0.136.0-next.13 → 0.136.0-next.15
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/CHANGELOG.md +33 -0
- package/dist/constants/api.d.ts +1 -0
- package/dist/constants/api.js +1 -1
- package/dist/server/constants/common.d.ts +1 -0
- package/dist/server/constants/common.js +1 -1
- package/dist/server/plugins/mcp/audit/audit-log.d.ts +36 -0
- package/dist/server/plugins/mcp/audit/audit-log.js +1 -0
- package/dist/server/plugins/mcp/audit/constants.d.ts +6 -0
- package/dist/server/plugins/mcp/audit/constants.js +1 -0
- package/dist/server/plugins/mcp/audit/secret-patterns.d.ts +2 -0
- package/dist/server/plugins/mcp/audit/secret-patterns.js +1 -0
- package/dist/server/plugins/mcp/codemode/build-tool-list.d.ts +12 -0
- package/dist/server/plugins/mcp/codemode/build-tool-list.js +2 -0
- package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +4 -4
- package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +1 -0
- package/dist/server/plugins/mcp/codemode/capabilities/utils.js +3 -3
- package/dist/server/plugins/mcp/codemode/code-execution-audit.d.ts +17 -0
- package/dist/server/plugins/mcp/codemode/code-execution-audit.js +1 -0
- package/dist/server/plugins/mcp/codemode/prompts.d.ts +6 -3
- package/dist/server/plugins/mcp/codemode/prompts.js +7 -4
- package/dist/server/plugins/mcp/codemode/sandbox/create-sandbox-setup.js +1 -1
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/fetch-shim.d.ts +6 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/fetch-shim.js +54 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/form-data.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/form-data.js +17 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.js +2 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url-search-params.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url-search-params.js +64 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url.d.ts +3 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/url.js +239 -0
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/tool-errors.d.ts +4 -0
- package/dist/server/plugins/mcp/codemode/tools/tool-errors.js +1 -0
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-endpoint-info.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/utils.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +3 -3
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.d.ts +24 -0
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-audit.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/fetch/fetch-bridge.js +1 -1
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +12 -8
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.d.ts +1 -0
- package/dist/server/plugins/mcp/servers/mcp-server.js +2 -2
- package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
- package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.d.ts +11 -0
- package/dist/server/plugins/mcp/workers/mcp-tool-telemetry.js +1 -1
- package/dist/server/tools/notifiers/formatter.d.ts +1 -0
- package/dist/server/tools/notifiers/formatter.js +3 -3
- package/dist/server/tools/notifiers/logger.d.ts +1 -0
- package/dist/server/tools/notifiers/logger.js +2 -2
- package/dist/server/web-server/routes/ask-ai.d.ts +5 -0
- package/dist/server/web-server/routes/ask-ai.js +1 -1
- package/dist/server/web-server/routes/index.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
- package/package.json +12 -12
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills.d.ts +0 -2
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills.js +0 -99
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @redocly/realm
|
|
2
2
|
|
|
3
|
+
## 0.136.0-next.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1f5d5014e53: Fixed an issue where sidebar items configured with `separatorLine: true` drew their line above the item instead of below it.
|
|
8
|
+
- e0f58876734: Fixed credential handling in the gateway MCP for clients without URL-mode elicitation.
|
|
9
|
+
Instructions provide correct links, credential errors are flagged, and credential pages display link validity duration.
|
|
10
|
+
- Updated dependencies [1f5d5014e53]
|
|
11
|
+
- @redocly/theme@0.68.0-next.9
|
|
12
|
+
- @redocly/api-docs@0.1.0-next.7
|
|
13
|
+
- @redocly/asyncapi-docs@1.13.0-next.13
|
|
14
|
+
- @redocly/graphql-docs@1.13.0-next.13
|
|
15
|
+
- @redocly/openapi-docs@3.24.0-next.13
|
|
16
|
+
- @redocly/portal-plugin-mock-server@0.21.0-next.13
|
|
17
|
+
|
|
18
|
+
## 0.136.0-next.14
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- c6e9480ee86: Added auditing to the experimental gateway MCP.
|
|
23
|
+
- 7c05ebc08b6: Fixed endpoint details returned by the Docs MCP server to correctly display documentation for individual endpoints.
|
|
24
|
+
- 330a3197c9e: Fixed security vulnerabilities `CVE-2026-67214` and `CVE-2026-67213` by upgrading `nanoid` to version `5.1.16`.
|
|
25
|
+
- 330a3197c9e: Fixed security vulnerability `GHSA-5p4m-2wfm-xmqj` by upgrading `js-yaml` to version `4.3.1`.
|
|
26
|
+
- Updated dependencies [c6e9480ee86]
|
|
27
|
+
- Updated dependencies [330a3197c9e]
|
|
28
|
+
- @redocly/realm-asyncapi-sdk@0.14.0-next.4
|
|
29
|
+
- @redocly/api-docs@0.1.0-next.6
|
|
30
|
+
- @redocly/openapi-docs@3.24.0-next.12
|
|
31
|
+
- @redocly/theme@0.68.0-next.8
|
|
32
|
+
- @redocly/portal-plugin-mock-server@0.21.0-next.12
|
|
33
|
+
- @redocly/asyncapi-docs@1.13.0-next.12
|
|
34
|
+
- @redocly/graphql-docs@1.13.0-next.12
|
|
35
|
+
|
|
3
36
|
## 0.136.0-next.13
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
package/dist/constants/api.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export declare const ServerRoutes: {
|
|
|
34
34
|
readonly HEALTH: '/_health';
|
|
35
35
|
readonly ASK_AI: '/_ask-ai';
|
|
36
36
|
readonly AI_ASSISTANT_ESCALATIONS: '/_ai-assistant-escalations';
|
|
37
|
+
readonly AI_ASSISTANT_FEEDBACK: '/_ai-assistant-feedback';
|
|
37
38
|
readonly CATALOG_ENTITIES: "/catalog-entities/:entityId?";
|
|
38
39
|
readonly CATALOG_ENTITIES_RELATIONS: "/catalog-entities-relations/:entityRelationId?";
|
|
39
40
|
readonly BFF_CATALOG_ENTITIES: "/bff/catalog-entities/:entityKey?";
|
package/dist/constants/api.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const _="_auth",e="entityKey",t="entityId",A="entityRelationId",T={CORS_PROXY:"/_api/cors",INFO:"/_info",EJECT_COMPONENT:"/eject-component/:componentName",SHARED_PAGE_DATA:"/page-data/shared/*",PAGE_DATA:"/page-data/*",APP_DATA:"/app-data.json",AUTHORIZATION:"/authorize",OIDC_CALLBACK:`/${_}/oidc`,SAML_CALLBACK:`/${_}/saml2`,REDOCLY_CALLBACK:`/${_}/redocly`,REPLAY_OAUTH2_CALLBACK:`/${_}/replay-oauth2`,REDOCLY_LOGIN_CALLBACK:`/${_}/redocly/redocly-login`,REDOCLY_TOKEN_LOGIN:`/${_}/redocly-token-login`,SEARCH:"/_search",SEARCH_FACETS:"/_search-facets",SEMANTIC_SEARCH:"/_semantic-search",LOGOUT:"/logout",POST_LOGOUT:"/post-logout",LOGIN:"/login",IDP_LOGIN:`/${_}/idp-login`,INVITE:"/invite/:code",RESOLVE_ROUTE_BY_PATH:"/resolve-route-by-path",RESOLVE_ROUTES_BY_PATHS:"/resolve-routes-by-paths",RESOLVE_ROUTE_BY_SLUG:"/resolve-route-by-slug",GET_ROUTES_BY_LINE:"/get-routes-by-line",FEEDBACK:"/feedback",TELEMETRY:"/_events",OTEL_TRACES:"/_otel/v1/traces",HEALTH:"/_health",ASK_AI:"/_ask-ai",AI_ASSISTANT_ESCALATIONS:"/_ai-assistant-escalations",CATALOG_ENTITIES:`/catalog-entities/:${t}?`,CATALOG_ENTITIES_RELATIONS:`/catalog-entities-relations/:${A}?`,BFF_CATALOG_ENTITIES:`/bff/catalog-entities/:${e}?`,BFF_CATALOG_RELATED_ENTITIES:`/bff/catalog-related-entities/:${e}`,BFF_CATALOG_REVISIONS:"/bff/catalog-revisions",MCP_OAUTH_AUTHORIZATION_SERVER:"/.well-known/oauth-authorization-server",MCP_OAUTH_PROTECTED_RESOURCE:"/.well-known/oauth-protected-resource",MCP_DYNAMIC_CLIENT_REGISTRATION:"/_mcp/register",MCP_AUTHORIZATION:"/_mcp/oauth2/auth",MCP_TOKEN_PORTAL:"/_mcp/oauth2/token-portal",MCP_CALLBACK:"/_mcp/oauth/callback",SCORECARDS:"/scorecards"};export{_ as AUTH_SEGMENT,t as CATALOG_ENTITY_ID,e as CATALOG_ENTITY_KEY,A as CATALOG_ENTITY_RELATION_ID,T as ServerRoutes};
|
|
1
|
+
const _="_auth",e="entityKey",t="entityId",A="entityRelationId",T={CORS_PROXY:"/_api/cors",INFO:"/_info",EJECT_COMPONENT:"/eject-component/:componentName",SHARED_PAGE_DATA:"/page-data/shared/*",PAGE_DATA:"/page-data/*",APP_DATA:"/app-data.json",AUTHORIZATION:"/authorize",OIDC_CALLBACK:`/${_}/oidc`,SAML_CALLBACK:`/${_}/saml2`,REDOCLY_CALLBACK:`/${_}/redocly`,REPLAY_OAUTH2_CALLBACK:`/${_}/replay-oauth2`,REDOCLY_LOGIN_CALLBACK:`/${_}/redocly/redocly-login`,REDOCLY_TOKEN_LOGIN:`/${_}/redocly-token-login`,SEARCH:"/_search",SEARCH_FACETS:"/_search-facets",SEMANTIC_SEARCH:"/_semantic-search",LOGOUT:"/logout",POST_LOGOUT:"/post-logout",LOGIN:"/login",IDP_LOGIN:`/${_}/idp-login`,INVITE:"/invite/:code",RESOLVE_ROUTE_BY_PATH:"/resolve-route-by-path",RESOLVE_ROUTES_BY_PATHS:"/resolve-routes-by-paths",RESOLVE_ROUTE_BY_SLUG:"/resolve-route-by-slug",GET_ROUTES_BY_LINE:"/get-routes-by-line",FEEDBACK:"/feedback",TELEMETRY:"/_events",OTEL_TRACES:"/_otel/v1/traces",HEALTH:"/_health",ASK_AI:"/_ask-ai",AI_ASSISTANT_ESCALATIONS:"/_ai-assistant-escalations",AI_ASSISTANT_FEEDBACK:"/_ai-assistant-feedback",CATALOG_ENTITIES:`/catalog-entities/:${t}?`,CATALOG_ENTITIES_RELATIONS:`/catalog-entities-relations/:${A}?`,BFF_CATALOG_ENTITIES:`/bff/catalog-entities/:${e}?`,BFF_CATALOG_RELATED_ENTITIES:`/bff/catalog-related-entities/:${e}`,BFF_CATALOG_REVISIONS:"/bff/catalog-revisions",MCP_OAUTH_AUTHORIZATION_SERVER:"/.well-known/oauth-authorization-server",MCP_OAUTH_PROTECTED_RESOURCE:"/.well-known/oauth-protected-resource",MCP_DYNAMIC_CLIENT_REGISTRATION:"/_mcp/register",MCP_AUTHORIZATION:"/_mcp/oauth2/auth",MCP_TOKEN_PORTAL:"/_mcp/oauth2/token-portal",MCP_CALLBACK:"/_mcp/oauth/callback",SCORECARDS:"/scorecards"};export{_ as AUTH_SEGMENT,t as CATALOG_ENTITY_ID,e as CATALOG_ENTITY_KEY,A as CATALOG_ENTITY_RELATION_ID,T as ServerRoutes};
|
|
@@ -32,6 +32,7 @@ export declare const ENTITLEMENTS_JWKS_URL: string;
|
|
|
32
32
|
export declare const FEEDBACK_API_URL: string;
|
|
33
33
|
export declare const ASK_AI_API_URL: string;
|
|
34
34
|
export declare const AI_ASSISTANT_ESCALATIONS_API_URL: string;
|
|
35
|
+
export declare const AI_ASSISTANT_FEEDBACK_API_URL: string;
|
|
35
36
|
export declare const SEMANTIC_SEARCH_API_URL: string;
|
|
36
37
|
export declare const CACHE_CONTROL_NO_STORE_HEADER_VALUE = "no-store";
|
|
37
38
|
export declare const DEFAULT_IMMUTABLE_CACHE_MAX_AGE: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ServerRoutes as
|
|
1
|
+
import{ServerRoutes as r}from"../../constants/api.js";import{USER_THEME_ALIAS as L}from"../../constants/common.js";import{envConfig as _}from"../config/env-config.js";const N="runtime",x="/assets",a="/_spec",U="/_bundle",M="static",P="redocly:::linkOriginal",l="@",i="default",m=new RegExp("((?!@i18n\\/|@l10n\\/)\\/?@.*?(?=\\/))","g"),F="@redocly/theme/plugin.js",H="styles.css",d=["@theme/plugin.js","@theme/plugin.cjs","@theme/plugin.mjs"],u=_.JWT_SECRET_KEY||"test_secret_key",G=3600,K=1440*60,c="@i18n",S="@l10n",Y=[L,c,S],B="https://redocly.com/auth/aud",g="catalog.json",h="scorecard.json",f="page-count.json",k="sitemap.xml",J="llms.txt",o=_.PROJECT_ID,e=_.PROJECT_SLUG,R=_.ORGANIZATION_ID,s=_.ORG_SLUG,t=_.BH_API_URL,n=t&&s&&e?`${t}/orgs/${s}/projects/${e}`:void 0,E=t&&R&&o?`${t}/orgs/${R}/projects/${o}`:void 0,p=t?`${t}/entitlements/.well-known/jwks`:"https://app.cloud.redocly.com/api/entitlements/.well-known/jwks",W=_.ENTITLEMENTS_JWKS_CDN_URL||p,$=n?`${n}/feedback`:"",j=E?`${E}/ai-agentic-conversations`:"",w=E?`${E}/ai-assistant-escalations`:"",V=E?`${E}/ai-assistant-feedback`:"",X=E?`${E}/semantic-search`:"",v="no-store",y=365*24*60*60,b="X-Redocly-Cache-Control",Z="no-cache",q="public, s-maxage=86400, max-age=10, must-revalidate",z=_.AUTH_URL,Q="Developer Documentation",T=_.REDOCLY_SSR_RENDER_MODE==="worker"?"worker":"main",__=T==="main"?0:_.REDOCLY_SSR_WORKERS_MIN!=null?Number(_.REDOCLY_SSR_WORKERS_MIN):2,E_=T==="main"?1:_.REDOCLY_SSR_WORKERS_MAX!=null?Number(_.REDOCLY_SSR_WORKERS_MAX):5,t_="-----BEGIN CERTIFICATE-----",o_="-----END CERTIFICATE-----",e_=!_.isDevelopMode&&_.REDOCLY_TELEMETRY!=="off"||_.REDOCLY_TELEMETRY==="on",R_="http://localhost:4318/v1/traces",s_=[r.HEALTH],A=_.SERVER_EDITOR_APP_URL||"http://127.0.0.1:3000",n_=_.REDOCLY_CORS_ORIGINS?_.REDOCLY_CORS_ORIGINS.split(",").map(I=>I.trim()).filter(Boolean).concat(A):[A],T_="markdown/partials",A_="markdown/partials-deps";export{w as AI_ASSISTANT_ESCALATIONS_API_URL,V as AI_ASSISTANT_FEEDBACK_API_URL,n_ as ALLOWED_CORS_ORIGINS,B as ALTERNATIVE_AUD_CLAIM_NAME,j as ASK_AI_API_URL,z as AUTH_URL,t_ as BEGIN_CERTIFICATE_DELIMITER,n as BH_API_PROJECTS_URL,E as BH_API_PROJECTS_URL_V2,v as CACHE_CONTROL_NO_STORE_HEADER_VALUE,Z as CACHE_CONTROL_PUBLIC_HTML,g as CATALOG_OUTPUT_FILE_NAME,y as DEFAULT_IMMUTABLE_CACHE_MAX_AGE,d as DEFAULT_PROJECT_PLUGIN_PATHS,F as DEFAULT_THEME_PLUGIN_PATH,Q as DEFAULT_TITLE,i as DEFAULT_VERSION_NAME,a as DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER,o_ as END_CERTIFICATE_DELIMITER,W as ENTITLEMENTS_JWKS_URL,$ as FEEDBACK_API_URL,c as I18N_DIR_NAME,u as JWT_SECRET_KEY,S as L10N_DIR_NAME,P as LINK_ORIGINAL_ATTR_NAME,J as LLMS_TXT_FILE_NAME,T_ as MARKDOC_PARTIALS_DATA_KEY,A_ as MARKDOC_PARTIALS_DEPS_KEY,G as MCP_ACCESS_TOKEN_TTL_SEC,K as MCP_REFRESH_SESSION_LIFETIME_SEC,R as ORG_ID,s as ORG_SLUG,s_ as OTEL_EXCLUDED_ROUTES,R_ as OTEL_TRACES_DEV_URL,f as PAGE_COUNT_OUTPUT_FILE_NAME,o as PROJECT_ID,e as PROJECT_SLUG,U as PUBLIC_API_DEFINITIONS_FOLDER,x as PUBLIC_ASSETS_FOLDER,M as PUBLIC_STATIC_FOLDER,Y as RESERVED_DIR_NAMES,N as RUNTIME_RESOURCES_DIR,h as SCORECARD_OUTPUT_FILE_NAME,X as SEMANTIC_SEARCH_API_URL,A as SERVER_EDITOR_APP_URL,k as SITEMAP_FILE_NAME,T as SSR_RENDER_MODE,E_ as SSR_WORKERS_MAX,__ as SSR_WORKERS_MIN,H as STYLES_FILE,e_ as TELEMETRY_ENABLED,m as VERSIONED_FOLDER_REGEXP_MATCH,l as VERSION_SEPARATOR,b as X_REDOCLY_CACHE_CONTROL_HEADER,q as X_REDOCLY_CACHE_CONTROL_PUBLIC_HTML};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export { AUDIT_LOG_PREFIX } from './constants.js';
|
|
2
|
+
export declare const AUDIT_FIELD_MAX_CHARS = 4096;
|
|
3
|
+
export type AuditField = string | number | boolean | undefined;
|
|
4
|
+
export declare function auditSha256(value: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Cuts at the last whitespace inside the budget rather than mid-token, so a secret is never split
|
|
7
|
+
* in half before the patterns get a look at it — half a token matches nothing and would survive.
|
|
8
|
+
*/
|
|
9
|
+
export declare function truncateAuditField(value: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Redact an input payload (code, request body, tool arguments) down to something loggable.
|
|
12
|
+
* Truncation runs first so the patterns only ever see whole tokens, and so the 14 regexes run
|
|
13
|
+
* over at most `AUDIT_FIELD_MAX_CHARS` instead of a multi-megabyte body.
|
|
14
|
+
*/
|
|
15
|
+
export declare function auditInput(value: string, injectedSecretValues?: readonly string[]): string;
|
|
16
|
+
/**
|
|
17
|
+
* Outputs are never recorded — an upstream response or tool result can hold credentials nobody
|
|
18
|
+
* can pattern-match. The digest still lets a replayed run be compared against the original.
|
|
19
|
+
*/
|
|
20
|
+
export declare function auditOutputDigest(value: string): {
|
|
21
|
+
sha256: string;
|
|
22
|
+
bytes: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Header values never reach the log. Redacting a fixed list of sensitive names would not be
|
|
26
|
+
* enough — sandbox code picks its own header names, so any allow-list leaks the creative ones.
|
|
27
|
+
*/
|
|
28
|
+
export declare function auditHeaderNames(headers: Record<string, string> | undefined): string;
|
|
29
|
+
/** Full URL with its query string, credential-shaped parameter values replaced. */
|
|
30
|
+
export declare function auditUrl(rawUrl: string, injectedSecretValues?: readonly string[]): string;
|
|
31
|
+
/**
|
|
32
|
+
* Values are JSON-encoded so a multi-line payload (submitted code, an assembled prompt) stays on
|
|
33
|
+
* one log line, which is what makes one record one Grafana row.
|
|
34
|
+
*/
|
|
35
|
+
export declare function logAuditRecord(action: string, fields: Record<string, AuditField>): void;
|
|
36
|
+
//# sourceMappingURL=audit-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as a}from"node:crypto";import{logger as c}from"../../../tools/notifiers/logger.js";import{AUDIT_LOG_PREFIX as u}from"./constants.js";import{redactSecretsFromString as f}from"../gateway-mcp/fetch/redact.js";import{redactSecretShapes as p}from"./secret-patterns.js";import{AUDIT_LOG_PREFIX as I}from"./constants.js";const s=4096,d="redacted",m=new Set(["access_token","api-key","api_key","apikey","auth","code","id_token","key","password","refresh_token","secret","session","sig","signature","token","x-amz-credential","x-amz-security-token","x-amz-signature"]);function h(t){return a("sha256").update(t,"utf8").digest("hex")}function g(t){if(t.length<=s)return t;const e=t.slice(0,s),r=e.search(/\s\S*$/),n=r>0?e.slice(0,r):e;return`${n}\u2026[truncated: kept ${n.length} of ${t.length} chars]`}function i(t,e=[]){return p(f(g(t),e))}function S(t){return{sha256:h(t),bytes:Buffer.byteLength(t,"utf8")}}function $(t){return Object.keys(t??{}).map(e=>e.toLowerCase()).sort().join(",")}function E(t,e=[]){let r;try{r=new URL(t)}catch{return i(t,e)}for(const n of[...r.searchParams.keys()])m.has(n.toLowerCase())&&r.searchParams.set(n,d);return r.username="",r.password="",i(r.href,e)}function R(t,e){const r=Object.entries(e).filter(([,n])=>n!==void 0&&n!=="").map(([n,o])=>typeof o=="string"?`${n}=${JSON.stringify(o)}`:`${n}=${String(o)}`).join(" ");try{c.infoPreSanitized(`${u} ${t} ${r}`)}catch{}}export{s as AUDIT_FIELD_MAX_CHARS,I as AUDIT_LOG_PREFIX,$ as auditHeaderNames,i as auditInput,S as auditOutputDigest,h as auditSha256,E as auditUrl,R as logAuditRecord,g as truncateAuditField};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t="[mcp-audit]";export{t as AUDIT_LOG_PREFIX};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const n=[{name:"aws_access_key_id",re:/\bAKIA[0-9A-Z]{16}\b/g},{name:"aws_temp_key",re:/\bASIA[0-9A-Z]{16}\b/g},{name:"github_token",re:/\bgh[pousr]_[A-Za-z0-9_]{20,255}\b/g},{name:"github_fine_grained",re:/\bgithub_pat_[A-Za-z0-9_]{60,255}\b/g},{name:"jwt",re:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g},{name:"pem_private_key",re:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g},{name:"google_api_key",re:/\bAIza[0-9A-Za-z_-]{35}\b/g},{name:"slack_token",re:/\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g},{name:"bearer_token",re:/(?<=[Aa]uthorization\s*[:=]\s*["']?[Bb]earer\s+)[A-Za-z0-9._\-+/=]{20,}/g},{name:"anthropic_key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}\b/g},{name:"openai_key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}\b/g},{name:"stripe_key",re:/\b(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{20,}\b/g},{name:"sendgrid_key",re:/\bSG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}\b/g},{name:"basic_auth_url",re:/\b([a-z][a-z0-9+\-.]*:\/\/)[^\s/:@]+:[^\s/@]+@/gi}];function t(e){if(!e)return e;let a=e;for(const{name:r,re:_}of n)a=a.replace(_,(A,b)=>r==="basic_auth_url"?`${b??""}[REDACTED:${r}]@`:`[REDACTED:${r}]`);return a}export{t as redactSecretShapes};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { McpToolSchema } from '../../../types';
|
|
2
|
+
import type { SandboxCapability } from './capabilities/types.js';
|
|
3
|
+
/** One line of the tool list: the sandbox-facing tool name plus a one-line purpose. */
|
|
4
|
+
export type ToolListEntry = {
|
|
5
|
+
name: string;
|
|
6
|
+
purpose?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Maps MCP tool schemas and active capabilities to tool-list entries (sandbox names). */
|
|
9
|
+
export declare function toToolListEntries(tools: McpToolSchema[], capabilities: SandboxCapability[]): ToolListEntry[];
|
|
10
|
+
/** Renders one `- name — purpose` line per entry within `maxChars`. */
|
|
11
|
+
export declare function buildToolList(entries: ToolListEntry[], maxChars: number): string;
|
|
12
|
+
//# sourceMappingURL=build-tool-list.d.ts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{sanitizeToolName as s}from"./build-tool-type-declarations.js";const l=60;function a(e,t){return[...e.map(n=>({name:s(n.name),purpose:n.description})),...t.map(n=>({name:s(n.id),purpose:n.purpose}))]}function $(e,t){if(e.length===0||t<=0)return"";const o=e.map(({name:r,purpose:f})=>{const i=(f??"").replace(/\s+/g," ").trim();if(!i)return`- ${r}`;const m=i.length>l?`${i.slice(0,l-1)}\u2026`:i;return`- ${r} \u2014 ${m}`}).join(`
|
|
2
|
+
`);if(o.length<=t)return o;const u=e.map(r=>r.name),p=u.join(", ");return p.length<=t?p:h(u,t)}function h(e,t){const n=[];for(const o of e){if([...n,o].join(", ").length+c(e.length-n.length-1).length>t)break;n.push(o)}return n.length>0?`${n.join(", ")}${c(e.length-n.length)}`:""}function c(e){return e>0?` \u2026 (+${e} more)`:""}export{$ as buildToolList,a as toToolListEntries};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getCallableApis as
|
|
2
|
-
`),c=["Example -
|
|
3
|
-
`);function
|
|
4
|
-
`)}};export{l as FETCH_DECLARATION,
|
|
1
|
+
import{getCallableApis as s,isFetchCapable as o}from"../../../gateway-mcp/fetch-capability.js";import{FETCH_TOOL_NAME as i}from"../../constants.js";const l=["```ts","/**"," * Call a documented API. Only the callable APIs' documented hosts are reachable."," * Responses cap at 1 MiB and redirects are not followed."," */","declare function fetch(url: string | URL, init?: {"," method?: string;"," /** `content-type` is set automatically for URLSearchParams/FormData unless you set one. */"," headers?: Record<string, string>;"," /** string \xB7 URLSearchParams (\u2192 application/x-www-form-urlencoded) \xB7 FormData (\u2192 multipart/form-data) */"," body?: string | URLSearchParams | FormData;","}): Promise<{"," status: number;"," statusText: string;"," ok: boolean;"," headers: { get(name: string): string | null };"," json(): Promise<unknown>;"," text(): Promise<string>;","}>;","```"].join(`
|
|
2
|
+
`),c=["Example - list the paid invoices of a billing API:","","```ts","const { items } = await tools.listApis({ filter: 'billing' });","const { endpoints } = await tools.getEndpoints({ name: items[0].name });","const ep = endpoints.find((e) => e.method === 'GET' && e.path.includes('invoices'));","const info = await tools.getEndpointInfo({ name: items[0].name, path: ep.path, method: ep.method });","const url = new URL((info.servers?.[0]?.url ?? '') + ep.path);","// Every name below comes from `info.endpoint.parameters` - read them first, never guess them.","url.searchParams.set('filter', 'status:paid');","url.searchParams.set('sort', '-createdTime');","url.searchParams.set('limit', '100');","try {"," const res = await fetch(url, { method: ep.method });"," return res.ok ? await res.json() : { status: res.status, error: await res.text() };","} catch (error) {"," return { error: error instanceof Error ? error.message : String(error), endpointId: info };","}","```"].join(`
|
|
3
|
+
`);function m(a){const t=new Map;for(const{name:e,version:r}of a){if(!e)continue;const n=t.get(e)??[];r&&n.push(r),t.set(e,n)}return[...t.entries()].map(([e,r])=>r.length>1?`${e} (${r.map(n=>`v${n}`).join(", ")})`:e).join(", ")}const h={id:i,purpose:"Call the documented APIs over HTTP",isActive:o,annotations:{readOnlyHint:!1,destructiveHint:!0,openWorldHint:!0},renderSection(a){const t=m(s(a));return["## `fetch` \u2014 call the documented APIs","",l,"","Always call `getEndpointInfo` before `fetch` for the exact URL, required parameters, and body shape. Put filtering, sorting, and paging in the query parameters, and keep sandbox JS for what the endpoint cannot express. Never guess.","",`Callable APIs (their documented hosts are reachable via \`fetch\`): ${t}`,"",c].join(`
|
|
4
|
+
`)}};export{l as FETCH_DECLARATION,h as fetchCapability};
|
|
@@ -3,6 +3,7 @@ import type { McpToolContext } from '../../types.js';
|
|
|
3
3
|
/** A capability is a per-request-gated global the code-mode sandbox exposes beyond the schema-driven `tools.*` surface. */
|
|
4
4
|
export type SandboxCapability = {
|
|
5
5
|
id: string;
|
|
6
|
+
purpose: string;
|
|
6
7
|
/** Whether this capability is live for THIS request (RBAC/config/flag-dependent). */
|
|
7
8
|
isActive(context: McpToolContext): boolean;
|
|
8
9
|
/** How this capability changes `execute`'s MCP annotations when active. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{buildToolTypeDeclarations as i}from"../build-tool-type-declarations.js";import{EXECUTE_BASE_ANNOTATIONS as
|
|
1
|
+
import{buildToolTypeDeclarations as i}from"../build-tool-type-declarations.js";import{EXECUTE_BASE_ANNOTATIONS as E}from"../tools/execute-schema.js";import{EXECUTE_ARGS_NOTE as p,PLAIN_EXECUTE_EXAMPLE as s,USAGE_NOTES as u}from"../prompts.js";import{FETCH_TOOL_NAME as c}from"../constants.js";import{ALL_CAPABILITIES as A}from"./registry.js";function S(o){return A.filter(n=>n.isActive(o))}function l(o){return o.reduce((n,e)=>({...n,...e.annotations}),{...E})}function N(o,n,e){const t=[];o.length>0&&t.push(`## Sandbox tools
|
|
2
2
|
|
|
3
3
|
${i(o)}
|
|
4
4
|
|
|
5
|
-
${
|
|
5
|
+
${u}`),t.push(p),o.length>0&&!n.some(r=>r.id===c)&&t.push(s);for(const r of n)t.push(r.renderSection(e));return t.join(`
|
|
6
6
|
|
|
7
|
-
`)}export{
|
|
7
|
+
`)}export{l as composeExecuteAnnotations,S as getActiveCapabilities,N as renderDescribeToolsOutput};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CodeAudit } from './code-audit.js';
|
|
2
|
+
type CodeExecutionAuditRecord = {
|
|
3
|
+
audit: CodeAudit;
|
|
4
|
+
code: string;
|
|
5
|
+
description: string;
|
|
6
|
+
sessionHandle?: string;
|
|
7
|
+
toolNames: readonly string[];
|
|
8
|
+
fetchBudget?: number;
|
|
9
|
+
fetchCapable: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Full sandbox input for one code mode `execute`, keyed by the same `code_sha256` the
|
|
13
|
+
* `mcpServer.codeExecuted` telemetry event carries, so a Grafana row resolves to this line.
|
|
14
|
+
*/
|
|
15
|
+
export declare function logCodeExecutionAudit({ audit, code, description, sessionHandle, toolNames, fetchBudget, fetchCapable, }: CodeExecutionAuditRecord): void;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=code-execution-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{auditInput as e,logAuditRecord as l}from"../audit/audit-log.js";function h({audit:o,code:t,description:c,sessionHandle:n,toolNames:d,fetchBudget:i,fetchCapable:s}){l("Record code execution input",{code_sha256:o.sha256,code_length:o.length,flags:o.flags,session_handle:n,description:e(c),tools:[...d].sort().join(","),fetch_capable:s,fetch_budget:i,code:e(t)})}export{h as logCodeExecutionAudit};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { type ToolListEntry } from './build-tool-list.js';
|
|
2
|
+
export declare const DESCRIPTION_CHAR_LIMIT = 2048;
|
|
1
3
|
export declare const EXECUTE_ARGS_NOTE = "Both `execute` arguments are required: `code` (the JavaScript to run; return a value to receive it) and `description` (a short human-readable summary of what the code does).";
|
|
2
|
-
export declare function buildExecuteDescription(hasFetch: boolean): string;
|
|
4
|
+
export declare function buildExecuteDescription(hasFetch: boolean, toolList?: ToolListEntry[]): string;
|
|
3
5
|
export declare const DESCRIBE_TOOLS_DESCRIPTION: string;
|
|
4
|
-
export declare function buildCodeModeInstructions(hasFetch: boolean): string;
|
|
5
|
-
export declare const
|
|
6
|
+
export declare function buildCodeModeInstructions(hasFetch: boolean, toolList?: ToolListEntry[]): string;
|
|
7
|
+
export declare const PLAIN_EXECUTE_EXAMPLE: string;
|
|
8
|
+
export declare const USAGE_NOTES: string;
|
|
6
9
|
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
const
|
|
2
|
-
`)}const
|
|
3
|
-
`);
|
|
4
|
-
`)
|
|
1
|
+
import{buildToolList as i}from"./build-tool-list.js";const l=2048,c="Both `execute` arguments are required: `code` (the JavaScript to run; return a value to receive it) and `description` (a short human-readable summary of what the code does).",n="Available tools (exact signatures come from `describe-tools`):";function a(e,t){if(t.length===0)return e;const o=l-e.length-n.length-3,s=i(t,o);return s?[e,"",n,s].join(`
|
|
2
|
+
`):e}function h(e,t=[]){const o=["Run JavaScript in a sandbox against the documentation tools on the global `tools` object.",e?"The extra globals are `tools`, `fetch`, `URL`, `URLSearchParams`, and `FormData` - there is no `console`, `setTimeout`, `require`, or Node.js API; all other I/O goes through `tools`.":"The only extra global is `tools` - there is no `fetch`, `console`, `setTimeout`, `require`, or Node.js API; all I/O goes through `tools`.","","Workflow - minimize round-trips; one `describe-tools` call plus one `execute` call covers most requests:","1. Analyze the request and decide which sandbox tools it needs.","2. Call `describe-tools` with those names to get their exact TypeScript signatures - inputs, result shapes, and a worked example. NEVER call `execute` with guessed method names, parameter names, enum values, or result field names: read fields off the declared return types, not from assumptions.","3. Batch everything into a SINGLE script: chain tool calls, pass one call's output into the next, process results in plain JavaScript, and `return` only what the request needs."].join(`
|
|
3
|
+
`);return a(o,t)}const d=["Get the exact TypeScript signatures of the sandbox tools available to `execute`.","Analyze the user request first, then pass `tools` with only the tool names the request needs (every name is listed in this input schema / description); omit `tools` only when you cannot tell which tools apply.","Always call this before your first `execute` and never guess signatures. Request every tool the task may need in ONE call instead of calling this repeatedly."].join(`
|
|
4
|
+
`);function u(e,t=[]){const o=["This server exposes documentation tools (API discovery, endpoint details, security schemes, full-text search) through two tools:","- `describe-tools` returns the exact TypeScript signatures of the sandbox tools.","- `execute` runs JavaScript that calls those tools on the global `tools` object.","Workflow: analyze the request, call `describe-tools` once with only the tool names it needs, then batch everything into a single `execute` script. Minimize round-trips and never guess tool signatures.",...e?["The sandbox also has a global `fetch` that can call the documented APIs (allow-listed hosts only). Include `fetch` in your `describe-tools` call to get its contract and the list of callable APIs."]:[]].join(`
|
|
5
|
+
`);return a(o,t)}const p=["Example - list the write endpoints of an API:","","```ts","const { items } = await tools.listApis({ filter: 'billing' });","const { endpoints } = await tools.getEndpoints({ name: items[0].name });","const writes = endpoints.filter((e) => e.method !== 'GET');","return { api: items[0].name, writes: writes.map((e) => `${e.method} ${e.path}`) };","```"].join(`
|
|
6
|
+
`),m=["Types named `OpenApi\u2026` are the standard OpenAPI Specification objects.","Payload samples live on a media type object (`requestBody.content[mediaType]`, `responses[code].content[mediaType]`): `.example` holds a single value, `.examples[name].value` a named map, and some descriptions only set `.schema.example`. Read each before concluding a sample is absent."].join(`
|
|
7
|
+
`);export{d as DESCRIBE_TOOLS_DESCRIPTION,l as DESCRIPTION_CHAR_LIMIT,c as EXECUTE_ARGS_NOTE,p as PLAIN_EXECUTE_EXAMPLE,m as USAGE_NOTES,u as buildCodeModeInstructions,h as buildExecuteDescription};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{setupHostFunctions as a}from"../../../../sandbox/utils/setup-host-functions.js";import{evalGuestCode as p}from"../../../../sandbox/utils/eval-guest-code.js";import{SANDBOX_FETCH_SETUP as i}from"./polyfills.js";import{FETCH_BRIDGE_FUNCTION_NAME as u,FETCH_BRIDGE_GLOBAL_NAME as c,TOOLS_BRIDGE_GLOBAL_NAME as n}from"../constants.js";const m=100,f=50;function T(r,t){return(e,l)=>{const s=[a(e,r,{globalName:n,budget:l,maxCalls:m,messages:{outputTooLarge:o=>`Tool output is too large (${o} bytes); request narrower data instead`,callLimitExceeded:o=>`Exceeded the limit of ${o} tool calls`}})];return t&&s.push(a(e,{[u]:o=>t(o)},{globalName:c,budget:l,maxCalls:f,messages:{outputTooLarge:o=>`Response is too large (${o} bytes); request less data`,callLimitExceeded:o=>`Exceeded the limit of ${o} fetch calls`}})),p(e,E(!!t)),()=>{for(const o of s)o()}}}function E(r){const t=r?i:"",e=`globalThis[${JSON.stringify(n)}]`;return`(() => {${t}
|
|
1
|
+
import{setupHostFunctions as a}from"../../../../sandbox/utils/setup-host-functions.js";import{evalGuestCode as p}from"../../../../sandbox/utils/eval-guest-code.js";import{SANDBOX_FETCH_SETUP as i}from"./polyfills/index.js";import{FETCH_BRIDGE_FUNCTION_NAME as u,FETCH_BRIDGE_GLOBAL_NAME as c,TOOLS_BRIDGE_GLOBAL_NAME as n}from"../constants.js";const m=100,f=50;function T(r,t){return(e,l)=>{const s=[a(e,r,{globalName:n,budget:l,maxCalls:m,messages:{outputTooLarge:o=>`Tool output is too large (${o} bytes); request narrower data instead`,callLimitExceeded:o=>`Exceeded the limit of ${o} tool calls`}})];return t&&s.push(a(e,{[u]:o=>t(o)},{globalName:c,budget:l,maxCalls:f,messages:{outputTooLarge:o=>`Response is too large (${o} bytes); request less data`,callLimitExceeded:o=>`Exceeded the limit of ${o} fetch calls`}})),p(e,E(!!t)),()=>{for(const o of s)o()}}}function E(r){const t=r?i:"",e=`globalThis[${JSON.stringify(n)}]`;return`(() => {${t}
|
|
2
2
|
const tools = ${e};
|
|
3
3
|
${e} = new Proxy(tools, {
|
|
4
4
|
get(target, prop) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fetch`: not a polyfill but a bridge. It takes the host rail off the global object so guest
|
|
3
|
+
* code cannot reach it directly, encodes the body, and hands the host a plain request.
|
|
4
|
+
*/
|
|
5
|
+
export declare const FETCH_SHIM = "\n const hostFetch = globalThis.__fetchBridge.fetch;\n delete globalThis.__fetchBridge;\n // RFC 7578: a field name travels with its quote and line breaks escaped, so a name cannot close its own header and open a part the caller did not write.\n const escapeFieldName = (name) =>\n String(name).replace(\n /[\"\\r\\n]/g,\n (char) => '%' + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0'),\n );\n globalThis.fetch = (url, init = {}) => {\n let body;\n let autoContentType;\n if (init.body instanceof URLSearchParams) {\n body = init.body.toString();\n autoContentType = 'application/x-www-form-urlencoded';\n } else if (init.body instanceof FormData) {\n const boundary = '----FormBoundary' + Math.random().toString(36).slice(2);\n body = init.body._fields\n .map(([k, v]) =>\n '--' + boundary + '\\r\\nContent-Disposition: form-data; name=\"' + escapeFieldName(k) + '\"\\r\\n\\r\\n' + v,\n )\n .join('\\r\\n') + '\\r\\n--' + boundary + '--';\n autoContentType = 'multipart/form-data; boundary=' + boundary;\n } else if (init.body != null && typeof init.body !== 'string') {\n throw new TypeError('fetch body must be a string, URLSearchParams, or FormData');\n } else {\n body = init.body ?? undefined;\n }\n const callerHeaders = init.headers;\n const hasContentType = callerHeaders != null && Object.keys(callerHeaders).some(\n (k) => k.toLowerCase() === 'content-type',\n );\n const headers = autoContentType && !hasContentType\n ? Object.assign({ 'content-type': autoContentType }, callerHeaders)\n : callerHeaders;\n return hostFetch({ url: String(url), method: init.method, headers, body })\n .then((res) => {\n const resHeaders = res.headers || {};\n return {\n status: res.status,\n statusText: res.statusText,\n ok: res.ok,\n headers: {\n get: (name) => {\n const value = resHeaders[String(name).toLowerCase()];\n return value === undefined ? null : value;\n },\n },\n json: () => Promise.resolve(JSON.parse(res.bodyText)),\n text: () => Promise.resolve(res.bodyText),\n };\n });\n };\n";
|
|
6
|
+
//# sourceMappingURL=fetch-shim.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import{FETCH_BRIDGE_FUNCTION_NAME as t,FETCH_BRIDGE_GLOBAL_NAME as e}from"../../constants.js";const n=`
|
|
2
|
+
const hostFetch = globalThis.${e}.${t};
|
|
3
|
+
delete globalThis.${e};
|
|
4
|
+
// RFC 7578: a field name travels with its quote and line breaks escaped, so a name cannot close its own header and open a part the caller did not write.
|
|
5
|
+
const escapeFieldName = (name) =>
|
|
6
|
+
String(name).replace(
|
|
7
|
+
/["\\r\\n]/g,
|
|
8
|
+
(char) => '%' + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0'),
|
|
9
|
+
);
|
|
10
|
+
globalThis.fetch = (url, init = {}) => {
|
|
11
|
+
let body;
|
|
12
|
+
let autoContentType;
|
|
13
|
+
if (init.body instanceof URLSearchParams) {
|
|
14
|
+
body = init.body.toString();
|
|
15
|
+
autoContentType = 'application/x-www-form-urlencoded';
|
|
16
|
+
} else if (init.body instanceof FormData) {
|
|
17
|
+
const boundary = '----FormBoundary' + Math.random().toString(36).slice(2);
|
|
18
|
+
body = init.body._fields
|
|
19
|
+
.map(([k, v]) =>
|
|
20
|
+
'--' + boundary + '\\r\\nContent-Disposition: form-data; name="' + escapeFieldName(k) + '"\\r\\n\\r\\n' + v,
|
|
21
|
+
)
|
|
22
|
+
.join('\\r\\n') + '\\r\\n--' + boundary + '--';
|
|
23
|
+
autoContentType = 'multipart/form-data; boundary=' + boundary;
|
|
24
|
+
} else if (init.body != null && typeof init.body !== 'string') {
|
|
25
|
+
throw new TypeError('fetch body must be a string, URLSearchParams, or FormData');
|
|
26
|
+
} else {
|
|
27
|
+
body = init.body ?? undefined;
|
|
28
|
+
}
|
|
29
|
+
const callerHeaders = init.headers;
|
|
30
|
+
const hasContentType = callerHeaders != null && Object.keys(callerHeaders).some(
|
|
31
|
+
(k) => k.toLowerCase() === 'content-type',
|
|
32
|
+
);
|
|
33
|
+
const headers = autoContentType && !hasContentType
|
|
34
|
+
? Object.assign({ 'content-type': autoContentType }, callerHeaders)
|
|
35
|
+
: callerHeaders;
|
|
36
|
+
return hostFetch({ url: String(url), method: init.method, headers, body })
|
|
37
|
+
.then((res) => {
|
|
38
|
+
const resHeaders = res.headers || {};
|
|
39
|
+
return {
|
|
40
|
+
status: res.status,
|
|
41
|
+
statusText: res.statusText,
|
|
42
|
+
ok: res.ok,
|
|
43
|
+
headers: {
|
|
44
|
+
get: (name) => {
|
|
45
|
+
const value = resHeaders[String(name).toLowerCase()];
|
|
46
|
+
return value === undefined ? null : value;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
json: () => Promise.resolve(JSON.parse(res.bodyText)),
|
|
50
|
+
text: () => Promise.resolve(res.bodyText),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
`;export{n as FETCH_SHIM};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** `FormData`: the multipart body the fetch shim serializes. */
|
|
2
|
+
export declare const FORM_DATA_POLYFILL = "\n const FormData = class FormData {\n constructor() { this._fields = []; }\n append(key, value) { this._fields.push([String(key), String(value)]); }\n set(key, value) {\n const k = String(key), v = String(value);\n this._fields = this._fields.filter(([fk]) => fk !== k);\n this._fields.push([k, v]);\n }\n get(key) { return (this._fields.find(([k]) => k === String(key)) ?? [undefined, null])[1]; }\n has(key) { return this._fields.some(([k]) => k === String(key)); }\n delete(key) { this._fields = this._fields.filter(([k]) => k !== String(key)); }\n entries() { return this._fields.values(); }\n [Symbol.iterator]() { return this._fields.values(); }\n };\n globalThis.FormData = FormData;\n";
|
|
3
|
+
//# sourceMappingURL=form-data.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const e=`
|
|
2
|
+
const FormData = class FormData {
|
|
3
|
+
constructor() { this._fields = []; }
|
|
4
|
+
append(key, value) { this._fields.push([String(key), String(value)]); }
|
|
5
|
+
set(key, value) {
|
|
6
|
+
const k = String(key), v = String(value);
|
|
7
|
+
this._fields = this._fields.filter(([fk]) => fk !== k);
|
|
8
|
+
this._fields.push([k, v]);
|
|
9
|
+
}
|
|
10
|
+
get(key) { return (this._fields.find(([k]) => k === String(key)) ?? [undefined, null])[1]; }
|
|
11
|
+
has(key) { return this._fields.some(([k]) => k === String(key)); }
|
|
12
|
+
delete(key) { this._fields = this._fields.filter(([k]) => k !== String(key)); }
|
|
13
|
+
entries() { return this._fields.values(); }
|
|
14
|
+
[Symbol.iterator]() { return this._fields.values(); }
|
|
15
|
+
};
|
|
16
|
+
globalThis.FormData = FormData;
|
|
17
|
+
`;export{e as FORM_DATA_POLYFILL};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** `URLSearchParams`: query-string parsing and serialization, and the store behind `URL.searchParams`. */
|
|
2
|
+
export declare const URL_SEARCH_PARAMS_POLYFILL = "\n // application/x-www-form-urlencoded reads '+' as a space, and leaves an escape it cannot read alone.\n // Decoding one well-formed run at a time is what keeps a stray '%' \u2014 '?discount=50%', a half-encoded cursor \u2014 from throwing URIError and ending the run.\n const decodeFormPart = (value) =>\n value.replace(/\\+/g, ' ').replace(/(?:%[0-9A-Fa-f]{2})+/g, (escaped) => {\n try { return decodeURIComponent(escaped); } catch (error) { return escaped; }\n });\n\n // The same rules in reverse: everything but alphanumerics and * - . _ is percent-encoded, and a space is '+'. encodeURIComponent keeps !'()~ literal, so those are encoded after it runs.\n const encodeFormPart = (value) =>\n encodeURIComponent(String(value))\n .replace(/%20/g, '+')\n .replace(/[!'()~]/g, (char) => '%' + char.charCodeAt(0).toString(16).toUpperCase());\n\n const URLSearchParams = class URLSearchParams {\n constructor(init) {\n this._params = [];\n if (init == null) return;\n if (typeof init === 'string') {\n const str = init.startsWith('?') ? init.slice(1) : init;\n for (const pair of (str ? str.split('&') : [])) {\n if (pair === '') continue;\n const eq = pair.indexOf('=');\n const key = eq >= 0 ? pair.slice(0, eq) : pair;\n const value = eq >= 0 ? pair.slice(eq + 1) : '';\n this._params.push([decodeFormPart(key), decodeFormPart(value)]);\n }\n } else if (typeof init[Symbol.iterator] === 'function') {\n // [['a', '1'], ['b', '2']] and another URLSearchParams both arrive here.\n for (const [key, value] of init) this._params.push([String(key), String(value)]);\n } else if (typeof init === 'object') {\n for (const [key, value] of Object.entries(init)) this._params.push([key, String(value)]);\n }\n }\n get size() { return this._params.length; }\n append(key, value) { this._params.push([String(key), String(value)]); }\n set(key, value) {\n const k = String(key), v = String(value);\n const at = this._params.findIndex(([pk]) => pk === k);\n if (at === -1) { this._params.push([k, v]); return; }\n // Overwrite where the key already sat, so an edit does not reorder the query the server sent.\n this._params[at] = [k, v];\n this._params = this._params.filter(([pk], index) => pk !== k || index === at);\n }\n get(key) { return (this._params.find(([k]) => k === String(key)) ?? [undefined, null])[1]; }\n getAll(key) { return this._params.filter(([k]) => k === String(key)).map(([, v]) => v); }\n has(key) { return this._params.some(([k]) => k === String(key)); }\n delete(key) { this._params = this._params.filter(([k]) => k !== String(key)); }\n forEach(callback, thisArg) {\n for (const [k, v] of [...this._params]) callback.call(thisArg, v, k, this);\n }\n keys() { return this._params.map(([k]) => k).values(); }\n values() { return this._params.map(([, v]) => v).values(); }\n entries() { return this._params.map(([k, v]) => [k, v]).values(); }\n sort() { this._params.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)); }\n [Symbol.iterator]() { return this.entries(); }\n toString() {\n return this._params\n .map(([k, v]) => encodeFormPart(k) + '=' + encodeFormPart(v))\n .join('&');\n }\n };\n globalThis.URLSearchParams = URLSearchParams;\n";
|
|
3
|
+
//# sourceMappingURL=url-search-params.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const e=`
|
|
2
|
+
// application/x-www-form-urlencoded reads '+' as a space, and leaves an escape it cannot read alone.
|
|
3
|
+
// Decoding one well-formed run at a time is what keeps a stray '%' \u2014 '?discount=50%', a half-encoded cursor \u2014 from throwing URIError and ending the run.
|
|
4
|
+
const decodeFormPart = (value) =>
|
|
5
|
+
value.replace(/\\+/g, ' ').replace(/(?:%[0-9A-Fa-f]{2})+/g, (escaped) => {
|
|
6
|
+
try { return decodeURIComponent(escaped); } catch (error) { return escaped; }
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// The same rules in reverse: everything but alphanumerics and * - . _ is percent-encoded, and a space is '+'. encodeURIComponent keeps !'()~ literal, so those are encoded after it runs.
|
|
10
|
+
const encodeFormPart = (value) =>
|
|
11
|
+
encodeURIComponent(String(value))
|
|
12
|
+
.replace(/%20/g, '+')
|
|
13
|
+
.replace(/[!'()~]/g, (char) => '%' + char.charCodeAt(0).toString(16).toUpperCase());
|
|
14
|
+
|
|
15
|
+
const URLSearchParams = class URLSearchParams {
|
|
16
|
+
constructor(init) {
|
|
17
|
+
this._params = [];
|
|
18
|
+
if (init == null) return;
|
|
19
|
+
if (typeof init === 'string') {
|
|
20
|
+
const str = init.startsWith('?') ? init.slice(1) : init;
|
|
21
|
+
for (const pair of (str ? str.split('&') : [])) {
|
|
22
|
+
if (pair === '') continue;
|
|
23
|
+
const eq = pair.indexOf('=');
|
|
24
|
+
const key = eq >= 0 ? pair.slice(0, eq) : pair;
|
|
25
|
+
const value = eq >= 0 ? pair.slice(eq + 1) : '';
|
|
26
|
+
this._params.push([decodeFormPart(key), decodeFormPart(value)]);
|
|
27
|
+
}
|
|
28
|
+
} else if (typeof init[Symbol.iterator] === 'function') {
|
|
29
|
+
// [['a', '1'], ['b', '2']] and another URLSearchParams both arrive here.
|
|
30
|
+
for (const [key, value] of init) this._params.push([String(key), String(value)]);
|
|
31
|
+
} else if (typeof init === 'object') {
|
|
32
|
+
for (const [key, value] of Object.entries(init)) this._params.push([key, String(value)]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
get size() { return this._params.length; }
|
|
36
|
+
append(key, value) { this._params.push([String(key), String(value)]); }
|
|
37
|
+
set(key, value) {
|
|
38
|
+
const k = String(key), v = String(value);
|
|
39
|
+
const at = this._params.findIndex(([pk]) => pk === k);
|
|
40
|
+
if (at === -1) { this._params.push([k, v]); return; }
|
|
41
|
+
// Overwrite where the key already sat, so an edit does not reorder the query the server sent.
|
|
42
|
+
this._params[at] = [k, v];
|
|
43
|
+
this._params = this._params.filter(([pk], index) => pk !== k || index === at);
|
|
44
|
+
}
|
|
45
|
+
get(key) { return (this._params.find(([k]) => k === String(key)) ?? [undefined, null])[1]; }
|
|
46
|
+
getAll(key) { return this._params.filter(([k]) => k === String(key)).map(([, v]) => v); }
|
|
47
|
+
has(key) { return this._params.some(([k]) => k === String(key)); }
|
|
48
|
+
delete(key) { this._params = this._params.filter(([k]) => k !== String(key)); }
|
|
49
|
+
forEach(callback, thisArg) {
|
|
50
|
+
for (const [k, v] of [...this._params]) callback.call(thisArg, v, k, this);
|
|
51
|
+
}
|
|
52
|
+
keys() { return this._params.map(([k]) => k).values(); }
|
|
53
|
+
values() { return this._params.map(([, v]) => v).values(); }
|
|
54
|
+
entries() { return this._params.map(([k, v]) => [k, v]).values(); }
|
|
55
|
+
sort() { this._params.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)); }
|
|
56
|
+
[Symbol.iterator]() { return this.entries(); }
|
|
57
|
+
toString() {
|
|
58
|
+
return this._params
|
|
59
|
+
.map(([k, v]) => encodeFormPart(k) + '=' + encodeFormPart(v))
|
|
60
|
+
.join('&');
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
globalThis.URLSearchParams = URLSearchParams;
|
|
64
|
+
`;export{e as URL_SEARCH_PARAMS_POLYFILL};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** `URL`: parses and resolves a url string, and exposes its parts. */
|
|
2
|
+
export declare const URL_POLYFILL = "\n // Collapses '.' and '..'. A backslash separates path segments, the way the host parser reads\n // one, so it is folded into '/' first \u2014 otherwise the two disagree about where the host name\n // ends in 'https://a.example\\@b.example/', and guest code reads a host the gateway never calls.\n const normalizePath = (rawPath) => {\n const path = rawPath.replace(/\\\\/g, '/');\n const out = [];\n for (const segment of path.split('/')) {\n if (segment === '.') continue;\n if (segment === '..') { if (out.length > 1) out.pop(); continue; }\n out.push(segment);\n }\n let joined = out.join('/');\n if ((path.endsWith('/.') || path.endsWith('/..')) && !joined.endsWith('/')) joined += '/';\n return joined;\n };\n\n const DEFAULT_PORTS = { 'http:': '80', 'https:': '443', 'ws:': '80', 'wss:': '443', 'ftp:': '21' };\n\n // Percent-encode what cannot travel literally in a url, so a hand-assembled path or query\n // reaches the host parser as the caller meant it. Non-ASCII goes out as UTF-8, in runs, so a\n // surrogate pair is encoded as the one character it stands for.\n const encodeLiterals = (value) =>\n value.replace(/[\\s\"<>\\`{}\\\\^]|[^\\x00-\\x7F]+/g, (chunk) => {\n try { return encodeURIComponent(chunk); } catch (error) { return chunk; }\n });\n\n // A non-ASCII host name is lower-cased but not punycoded \u2014 that needs an IDNA table this sandbox\n // does not carry. The host re-parses the url before it fetches, so the request still goes to the\n // punycoded name; only what guest code reads back differs.\n const parseHostPort = (hostport, scheme) => {\n let hostname = hostport;\n let port = '';\n if (hostport.startsWith('[')) {\n const close = hostport.indexOf(']');\n hostname = hostport.slice(0, close + 1);\n const after = hostport.slice(close + 1);\n if (after.startsWith(':')) port = after.slice(1);\n } else {\n const colon = hostport.lastIndexOf(':');\n if (colon !== -1) { hostname = hostport.slice(0, colon); port = hostport.slice(colon + 1); }\n }\n if (port !== '' && !/^[0-9]+$/.test(port)) return null;\n if (hostname === '') return null;\n return {\n hostname: hostname.toLowerCase(),\n port: DEFAULT_PORTS[scheme] === port ? '' : port,\n // A host written without a port leaves the port alone, rather than clearing it.\n hasPort: port !== '',\n };\n };\n\n const ABSOLUTE_RE = /^([A-Za-z][A-Za-z0-9+\\-.]*):(\\/\\/)?([\\s\\S]*)$/;\n\n const parseAuthorityUrl = (scheme, rest) => {\n const cut = rest.search(/[/\\\\?#]/);\n const authority = cut === -1 ? rest : rest.slice(0, cut);\n let tail = cut === -1 ? '' : rest.slice(cut);\n let username = '';\n let password = '';\n let hostport = authority;\n const at = authority.lastIndexOf('@');\n if (at !== -1) {\n const credentials = authority.slice(0, at);\n hostport = authority.slice(at + 1);\n const colon = credentials.indexOf(':');\n username = colon === -1 ? credentials : credentials.slice(0, colon);\n password = colon === -1 ? '' : credentials.slice(colon + 1);\n }\n const hostPort = parseHostPort(hostport, scheme);\n if (!hostPort) return null;\n let hash = '';\n const hashAt = tail.indexOf('#');\n if (hashAt !== -1) { hash = tail.slice(hashAt); tail = tail.slice(0, hashAt); }\n let search = '';\n const queryAt = tail.indexOf('?');\n if (queryAt !== -1) { search = tail.slice(queryAt); tail = tail.slice(0, queryAt); }\n let pathname = tail === '' ? '/' : tail;\n if (!/^[/\\\\]/.test(pathname)) pathname = '/' + pathname;\n return {\n protocol: scheme,\n username,\n password,\n hostname: hostPort.hostname,\n port: hostPort.port,\n pathname: encodeLiterals(normalizePath(pathname)),\n search: encodeLiterals(search === '?' ? '' : search),\n hash: encodeLiterals(hash === '#' ? '' : hash),\n };\n };\n\n const parseUrl = (input, base) => {\n // A tab or newline inside a url is dropped, not encoded \u2014 the host parser does the same, and\n // a url copied out of a wrapped response body is the usual way one gets in here.\n const str = String(input).trim().replace(/[\\t\\n\\r]/g, '');\n const matched = ABSOLUTE_RE.exec(str);\n // A scheme with no `//` (mailto:, data:) is not a fetch target, so it is not resolved.\n if (matched) return matched[2] ? parseAuthorityUrl(matched[1].toLowerCase() + ':', matched[3]) : null;\n if (base === undefined) return null;\n const resolved = parseUrl(base, undefined);\n if (!resolved) return null;\n if (/^[/\\\\]{2}/.test(str)) return parseAuthorityUrl(resolved.protocol, str.slice(2));\n const parts = { ...resolved };\n if (str.startsWith('#')) {\n parts.hash = encodeLiterals(str === '#' ? '' : str);\n return parts;\n }\n let rest = str;\n let hash = '';\n const hashAt = rest.indexOf('#');\n if (hashAt !== -1) { hash = rest.slice(hashAt); rest = rest.slice(0, hashAt); }\n let search = '';\n const queryAt = rest.indexOf('?');\n if (queryAt !== -1) { search = rest.slice(queryAt); rest = rest.slice(0, queryAt); }\n if (rest !== '') {\n const absolute = /^[/\\\\]/.test(rest)\n ? rest\n : resolved.pathname.slice(0, resolved.pathname.lastIndexOf('/') + 1) + rest;\n parts.pathname = encodeLiterals(normalizePath(absolute));\n }\n // An empty reference with no query of its own keeps the base query (RFC 3986 section 5.3).\n parts.search = rest === '' && search === '' ? resolved.search : encodeLiterals(search === '?' ? '' : search);\n parts.hash = encodeLiterals(hash === '#' ? '' : hash);\n return parts;\n };\n\n const URL = class URL {\n constructor(input, base) {\n const parsed = parseUrl(input, base === undefined ? undefined : String(base));\n if (!parsed) throw new TypeError('Invalid URL: ' + String(input));\n this._parts = parsed;\n this._params = undefined;\n this._edited = false;\n }\n // The query stays exactly as it arrived until searchParams edits it, so reading a url apart\n // and putting it back together does not re-encode what the server sent.\n _applyParams() {\n if (this._edited && this._params) {\n const query = this._params.toString();\n this._parts.search = query ? '?' + query : '';\n this._edited = false;\n }\n }\n // Every part a caller can read, it can also write. Guest code runs sloppy, so a missing\n // setter would swallow `url.host = \u2026` and fetch the address the caller meant to replace.\n get protocol() { return this._parts.protocol; }\n set protocol(value) {\n const next = String(value).toLowerCase().replace(/:$/, '') + ':';\n // Only a scheme that carries an authority can replace one, the way the host parser reads it.\n if (DEFAULT_PORTS[next] !== undefined) this._parts.protocol = next;\n }\n get username() { return this._parts.username; }\n set username(value) { this._parts.username = String(value); }\n get password() { return this._parts.password; }\n set password(value) { this._parts.password = String(value); }\n get hostname() { return this._parts.hostname; }\n set hostname(value) { this._parts.hostname = String(value).toLowerCase(); }\n get port() { return this._parts.port; }\n set port(value) {\n const port = String(value);\n if (port !== '' && !/^[0-9]+$/.test(port)) return;\n this._parts.port = DEFAULT_PORTS[this._parts.protocol] === port ? '' : port;\n }\n get host() {\n return this._parts.port ? this._parts.hostname + ':' + this._parts.port : this._parts.hostname;\n }\n set host(value) {\n const parsed = parseHostPort(String(value), this._parts.protocol);\n if (!parsed) return;\n this._parts.hostname = parsed.hostname;\n if (parsed.hasPort) this._parts.port = parsed.port;\n }\n get origin() { return this._parts.protocol + '//' + this.host; }\n get pathname() { return this._parts.pathname; }\n set pathname(value) {\n const path = String(value);\n this._parts.pathname = encodeLiterals(path.startsWith('/') ? path : '/' + path);\n }\n get search() {\n this._applyParams();\n return this._parts.search;\n }\n set search(value) {\n const query = String(value);\n const body = query.startsWith('?') ? query.slice(1) : query;\n this._parts.search = body ? encodeLiterals('?' + body) : '';\n this._params = undefined;\n this._edited = false;\n }\n get searchParams() {\n if (!this._params) {\n const params = new URLSearchParams(this._parts.search);\n const url = this;\n // Every method that can change the parameters has to mark the query for re-serialization.\n for (const method of ['append', 'set', 'delete', 'sort']) {\n const original = params[method];\n params[method] = function (...args) {\n url._edited = true;\n return original.apply(params, args);\n };\n }\n this._params = params;\n }\n return this._params;\n }\n get hash() { return this._parts.hash; }\n set hash(value) {\n const fragment = String(value);\n this._parts.hash = fragment === ''\n ? ''\n : encodeLiterals(fragment.startsWith('#') ? fragment : '#' + fragment);\n }\n get href() {\n this._applyParams();\n const credentials = this._parts.username\n ? this._parts.username + (this._parts.password ? ':' + this._parts.password : '') + '@'\n : '';\n return this._parts.protocol + '//' + credentials + this.host +\n this._parts.pathname + this._parts.search + this._parts.hash;\n }\n set href(value) {\n const parsed = parseUrl(value, undefined);\n if (!parsed) throw new TypeError('Invalid URL: ' + String(value));\n this._parts = parsed;\n this._params = undefined;\n this._edited = false;\n }\n toString() { return this.href; }\n toJSON() { return this.href; }\n static canParse(input, base) {\n return parseUrl(input, base === undefined ? undefined : String(base)) !== null;\n }\n static parse(input, base) {\n const parsed = parseUrl(input, base === undefined ? undefined : String(base));\n return parsed ? new URL(input, base) : null;\n }\n };\n globalThis.URL = URL;\n";
|
|
3
|
+
//# sourceMappingURL=url.d.ts.map
|