{"id":35236157,"url":"https://github.com/fastly/compute-js-esi","last_synced_at":"2025-12-30T04:01:38.157Z","repository":{"id":213143426,"uuid":"720837260","full_name":"fastly/compute-js-esi","owner":"fastly","description":"An implementation of ESI (Edge Side Includes) for Fastly Compute JavaScript","archived":false,"fork":false,"pushed_at":"2025-09-05T06:32:25.000Z","size":270,"stargazers_count":4,"open_issues_count":1,"forks_count":1,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-11-17T18:28:55.550Z","etag":null,"topics":["compute-library","fastly-oss-tier2"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fastly.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-11-19T18:45:02.000Z","updated_at":"2025-09-05T06:32:29.000Z","dependencies_parsed_at":"2023-12-18T21:14:12.012Z","dependency_job_id":"341db0ba-7072-4e9b-b703-790229f79b43","html_url":"https://github.com/fastly/compute-js-esi","commit_stats":{"total_commits":11,"total_committers":1,"mean_commits":11.0,"dds":0.0,"last_synced_commit":"9a630b18ca1d4eccf28bc2ad61199ae291fe1f83"},"previous_names":["fastly/compute-js-esi"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/fastly/compute-js-esi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Fcompute-js-esi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Fcompute-js-esi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Fcompute-js-esi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Fcompute-js-esi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fastly","download_url":"https://codeload.github.com/fastly/compute-js-esi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastly%2Fcompute-js-esi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27295506,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-28T02:00:06.623Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compute-library","fastly-oss-tier2"],"created_at":"2025-12-30T04:00:35.858Z","updated_at":"2025-12-30T04:01:38.152Z","avatar_url":"https://github.com/fastly.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Edge Side Includes (ESI) for Fastly Compute JavaScript\n\nRun [Edge Side Includes (ESI)](https://www.w3.org/TR/esi-lang/) at the edge in your Fastly Compute JavaScript application.\n\n## Usage\n\n```\nnpm install @fastly/esi\n```\n\nThis is designed to be very easy to use:\n\n```javascript\n/// \u003creference types=\"@fastly/js-compute\" /\u003e\nimport { EsiTransformStream } from \"@fastly/esi\";\n\naddEventListener(\"fetch\", (event) =\u003e event.respondWith(handleRequest(event)));\n\nasync function handleRequest(event) {\n  const req = event.request;\n\n  // Perform a backend request\n  const url = new URL(req.url);\n  const headers = new Headers(req.headers);\n\n  const beresp = await fetch(url, {\n    headers,\n    backend: 'origin_0'\n  });\n\n  // Initialize the ESI Transformation\n  const esiTransformStream = new EsiTransformStream(url, headers, {\n    fetch(input, init) {\n      return fetch(input, { ...init, backend: 'origin_0' });\n    },\n  });\n  \n  // Simply use pipeThrough\n  const transformedResponse = beresp.body.pipeThrough(esiTransformStream); \n\n  return new Response(transformedResponse, {\n    status: beresp.status,\n    headers: beresp.headers,\n  });\n}\n```\n\n\u003e NOTE: `@fastly/esi` is provided as a Fastly Labs product. Visit the [Fastly Labs](https://www.fastlylabs.com/) site for terms of use.\n\n## API\n\n### `EsiTransformStream` constructor\n\n```javascript\nconst esiTransformStream = new EsiTransformStream(url, headers, options);\n```\n\nInitializes the ESI transformation stream using the following values. These values are used in handling\n`esi:include` tags as well as providing values for ESI variables.\n\nFor predictable results, pass the same values as you used to make the backend request.\nIn most cases, the only value you need to pass for `options` is `fetch` \u0026mdash; to provide the\n`backend` used by `fetch()` resulting from `esi:include` tags.\n\n* `url` - the absolute URL where the resource was fetched from, used to resolve relative URLs, as well\n    as used by ESI variables.\n* `headers` - the headers, which will be sent along with the subrequests caused by `esi:include`, as\n    well as used by ESI variables. \n* `options` - an object with the following keys:\n\n  * `fetch(input, init)` (optional)\n    \n    The `fetch()` function called whenever an `esi:include` tag is encountered. If not specified, the\n    global `fetch()` function is used.\n      - It will be called with the resolved, absolute URL of the resource being requested, along with\n        the `headers` passed in to the constructor, with the exception that if the resource is being\n        requested from a different host, then the `host` header is set to that of the resource being\n        requested.\n      - A common use of providing this function in Fastly Compute is to add the `backend` value as\n        a fetch is being made. This is not needed when you're using dynamic backends.\n        ```javascript\n          const esiTransformStream = new EsiTransformStream(url, headers, {\n            fetch(input, init) {\n              return fetch(input, { ...init, backend: 'origin_0' });\n            },\n          });\n        ```\n\n  * `processIncludeResponse(esiIncludeResult)` (optional)\n  \n    A function called after a `fetch()` from `esi:include` has succeeded. It is called with an object\n    that contains the following keys:\n      - `url` - the resolved, absolute URL of the document obtained by the `esi:include` tag.\n      - `headers` - the HTTP headers used when fetching the resource requested by the `esi:include` tag.\n        \u003e Note: The above values are as they were when the `fetch` function was called.\n        If you provided an override `fetch` function that caused the resource to be obtained\n        from another location, or using modified headers, those are not reflected in the above values.\n      - `response` - the `Response` object returned by the `fetch()` call.\n\n    This function is expected to return a `string`, or a `Promise` resolving to a `string`, which\n    is used by the transformer to replace the entire `esi:include` tag in the stream.\n    This value is optional, and should only be used in advanced cases, with care.\n\n    \u003e The default functionality recursively passes the response through another `EsiTransformationStream`\n    such that templates may call into additional templates.\n   \n  * `handleIncludeError(xmlElement)` (optional)\n\n    A function called when resources requested by both the `src` and `alt` (if provided) values of an\n    `esi:include` tag have been tried, and have resulted in errors. The function can return a `string`\n    or a `Promise` that resolves to a `string` that will be used to replace the entire `esi:include`\n    tag, or `null`.\n\n    \u003e If this is not specified, or if this returns `null` or a `Promise` that resolves to `null`, then\n    the default behavior is to throw an `EsiIncludeError` error.\n\n  * `esiPrefix` (optional, advanced)\n\n    A string value or `null`, used to specify a default XML prefix identifier to interpret as an ESI tag.\n    If you wish to suppress this functionality altogether, set this value to `null`.\n    See [Custom Namespace Prefix](#custom-namespace-prefix) below for details.\n\n    \u003e The default value is `'esi'`.\n\n## Notes\n\n### Supported tags\n\nAt the current time, the following tags are supported as [described in the specification](https://www.w3.org/TR/esi-lang/):\n\n* esi:include\n* esi:comment\n* esi:remove\n* esi:try / esi:attempt / esi:except\n* esi:choose / esi:when / esi:otherwise\n* esi:vars\n\nESI Variables are supported in the attributes of ESI tags.\nESI Expressions are supported in the `test` attribute of `esi:when`.\n\nAdditionally, the \u0026lt;!--esi ...--\u0026gt; comment is supported.\n\nThe following tags are not supported:\n\n* esi:inline\n\n### Errors\n\nIf an error from `esi:include` is not handled, or you handle it and return `null` (via the\n`handleIncludeError` option), the stream will throw a `EsiIncludeError`. Note that if this happens\non a stream that is being read by `event.respondWith()`, then the platform has already sent the\nstatus code and headers, and is already streaming to the client. Therefore, it is too late to\nrespond to this error or send alternate status codes or headers.\n\nIf you wish to return an alternate status code, then you must stream the entire response to memory,\nmake sure there are no errors, and then return that buffer.\n\n```javascript\n  const value = new Response(beresp.body.pipeThrough(esiTransformStream));\n\n  let buffer;\n  try {\n    buffer = await value.arrayBuffer();\n  } catch(ex) {\n    if(!(ex instanceof EsiIncludeError)) {\n      throw ex;\n    }\n\n    return new Response(\n      'esi:include error',\n      {\n        status: 500,\n      },\n    );\n  }\n\n  return new Response(\n    buffer,\n    {\n      status: beresp.status,\n      headers: beresp.headers,\n    },\n  );\n```\n\nBecause this could result in a longer TTFB, it is ideal to handle the errors from `esi:include`.\n\n### Backend requests\n\nKeep in mind that `esi:include` tags will cause a backend request, so [they are subject to\nconstraints](https://developer.fastly.com/learning/compute/#limitations-and-constraints)\nat the platform level.\n\n### Max inclusion depth\n\nThe templates that are fetched by `esi:include` tags can recursively include additional `esi:include` tags.\nThis library imposes a limit of 10 levels deep, after which an `EsiIncludeError` will be thrown.\n\n### XML Namespacing\n\nESI tags are defined as XML tags that live in the `http://www.edge-delivery.org/esi/1.0` namespace.\nThis means that formally, the namespace needs to be declared on the tag itself or on a parent element, e.g.:\n\n```html\n\u003cesi:include src=\"/bar\" xmlns:esi=\"http://www.edge-delivery.org/esi/1.0\"/\u003e\n```\nor:\n```html\n\u003chtml xmlns:esi=\"http://www.edge-delivery.org/esi/1.0\"\u003e\n  \u003cesi:include src=\"/bar\" /\u003e\n\u003c/html\u003e\n```\n\nHowever, implementations of ESI have had a history of not explicitly requiring this declaration. Additionally,\nbecause of the way we must handle [XML tags in HTML](#xml-tags-in-html), it's not always possible to reliably find the\nparent of an XML tag. For these reasons, this library implies this declaration by default and makes it available\neverywhere.\n\n```html\n\u003c!-- Works, because xmlns:esi=\"http://www.edge-delivery.org/esi/1.0\" is available by default. --\u003e\n\u003cesi:include src=\"/bar\" /\u003e\n```\n\n### Custom Namespace Prefix\n\nIf you'd like to make ESI available under a prefix other than `esi:` instead, provide it as the `esiPrefix` value when\nconstructing `EsiTransformStream`. This may be useful if you are using this library in conjunction with other\nprocessing that uses the `'esi'` prefix for its own use.\n\nFor example, you may set up your transform stream like this:\n```javascript\nconst esiTransformStream = new EsiTransformStream(url, headers, {\n  esiPrefix: 'my-esi'\n});\n```\n\nThen, if you have a document like this, its various tags will be handled as described:\n```html\n\u003c!-- EsiTransformStream knows about my-esi, so this is treated as an ESI include --\u003e\n\u003cmy-esi:include src=\"/foo\" /\u003e\n\u003c!-- EsiTransform does not know about esi, so this is not handled --\u003e  \n\u003cesi:include src=\"/bar\" /\u003e\n\u003c!-- Attribute on the tag explicitly sets namespace, so this is treated as an ESI include --\u003e\n\u003cesi:include src=\"/bar\" xmlns:esi=\"http://www.edge-delivery.org/esi/1.0\" /\u003e\n```\n\nIf you wish to disable the automatic prefix declaration altogether, it's also possible to set the value to `null`:\n```javascript\nconst esiTransformStream = new EsiTransformStream(url, headers, {\n  esiPrefix: null\n});\n```\n\n\u003e Note: If you do this, then `EsiTransformStream` will not know about any ESI namespaces, so you will need to specify\n\u003e the namespace in your document.\n\n### XML tags in HTML\n\nESI tags are [defined as an XML-based language](https://www.w3.org/TR/esi-lang/). This means they must follow\nthe rules of XML, such as attribute quoting and matching closing tags.  However, this library is designed to\nwork in an HTML context, so it operates in the following way with respect to HTML and XML tags:\n\n* HTML and XML tags that appear with the default namespace (no XML prefix) are treated as plain text by the\n  transformation. This includes the opening tag and any attributes, as well as any closing tags.\n\n* Tags that appear with an XML prefix are classified as XML and must meet the rules of XML. Then:\n    * Tags that belong to the ESI namespace are handled and processed by this library.\n    * Tags that belong to other namespaces are passed through without processing.\n\nIf the text content of either type of tag contain any nested tags, then they are also processed according to the same\nrules, that is, only tags that appear with an XML prefix are treated as XML.\n\nThere are several reasons we do this:\n* HTML employs a looser set of rules. For example, HTML defines tags that do not require closing tags (such as\n  `input`), and tags whose closing tags may be implied (such as `p`). This makes it very challenging to quickly and\n  accurately determine the structure of the HTML document.\n* Some ESI templates are authored with partial HTML elements. For example, a \"header\" template may contain the\n  opening tag of an HTML element, whose matching closing tag exists in a \"footer\" template. To be compatible with such\n  scenarios, we do not enforce XML rules on HTML tags.\n\nThis has a few additional implications relating to advanced namespace use:\n\n* This library won't find ESI tags from the default namespace, even when it's set as the default namespace.\n  For example, while formally valid, the following won't work in this library:\n\n```html\n\u003cdiv\u003e\n  \u003c!-- include tag in default namespace will be ignored. --\u003e\n  \u003cinclude src=\"/bar\" xmlns=\"http://www.edge-delivery.org/esi/1.0\"/\u003e\n\u003c/div\u003e\n```\n\n* If you're declaring `xmlns:\u003cprefix\u003e` yourself, that declaration will be ignored if it's placed on a tag\n  from the default namespace. For example, while formally valid, the following won't work in this library:\n\n```html\n\u003c!-- xmlns:esi is ignored because it's on div which is in the default namespace. --\u003e\n\u003cdiv xmlns:esi=\"http://www.edge-delivery.org/esi/1.0\"\u003e\n  \u003cesi:include src=\"/foo\" /\u003e \u003c!-- Won't be recognized! --\u003e\n\u003c/div\u003e\n```\n\nIn both of the cases above, do this instead:\n```html\n\u003cdiv\u003e\n  \u003cesi:include src=\"/foo\" xmlns:esi=\"http://www.edge-delivery.org/esi/1.0\" /\u003e\n\u003c/div\u003e\n```\n\n## Issues\n\nIf you encounter any non-security-related bug or unexpected behavior, please [file an issue][bug]\nusing the bug report template.\n\n[bug]: https://github.com/fastly/compute-js-esi/issues/new?labels=bug\n\n### Security issues\n\nPlease see our [SECURITY.md](./SECURITY.md) for guidance on reporting security-related issues.\n\n## License\n\n[MIT](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastly%2Fcompute-js-esi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffastly%2Fcompute-js-esi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastly%2Fcompute-js-esi/lists"}