1"use strict";(self.webpackChunk=self.webpackChunk||[]).push([["14481"],{79264(e,s,n){n.r(s),n.d(s,{metadata:()=>r,default:()=>u,frontMatter:()=>l,contentTitle:()=>c,toc:()=>h,assets:()=>d});var r=JSON.parse('{"id":"guides/persisted-queries","title":"Persisted Queries","description":"Relay guide to persisted queries","source":"@site/versioned_docs/version-v15.0.0/guides/persisted-queries.md","sourceDirName":"guides","slug":"/guides/persisted-queries/","permalink":"/docs/v15.0.0/guides/persisted-queries/","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/relay/tree/main/website/versioned_docs/version-v15.0.0/guides/persisted-queries.md","tags":[],"version":"v15.0.0","frontMatter":{"id":"persisted-queries","title":"Persisted Queries","slug":"/guides/persisted-queries/","description":"Relay guide to persisted queries","keywords":["persisted"]},"sidebar":"docs","previous":{"title":"Type Emission","permalink":"/docs/v15.0.0/guides/type-emission/"},"next":{"title":"Network Layer","permalink":"/docs/v15.0.0/guides/network-layer/"}}'),t=n(74848),i=n(28453),o=n(86898),a=n(17352);let l={id:"persisted-queries",title:"Persisted Queries",slug:"/guides/persisted-queries/",description:"Relay guide to persisted queries",keywords:["persisted"]},c,d={},h=[{value:"Usage on the client",id:"usage-on-the-client",level:2},{value:"The <code>persistConfig</code> option",id:"the-persistconfig-option",level:3},{value:"Local Persisted Queries",id:"local-persisted-queries",level:3},{value:"Tradeoffs",id:"tradeoffs",level:4},{value:"Example implemetation of <code>relayLocalPersisting.js</code>",id:"example-implemetation-of-relaylocalpersistingjs",level:3},{value:"Network layer changes",id:"network-layer-changes",level:3},{value:"Executing Persisted Queries on the Server",id:"executing-persisted-queries-on-the-server",level:2},{value:"Compile time push",id:"compile-time-push",level:3},{value:"Run time push",id:"run-time-push",level:3},{value:"Simple server example",id:"simple-server-example",level:3},{value:"Using <code>persistConfig</code> and <code>--watch</code>",id:"using-persistconfig-and---watch",level:2}];function p(e){let s={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",h4:"h4",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,i.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(a.FbInternalOnly,{children:(0,t.jsxs)(s.blockquote,{children:["\n",(0,t.jsxs)(s.p,{children:["Persistence is handled by the ",(0,t.jsx)(s.code,{children:"relay"})," command for you. You likely do not need to worry about the contents of this guide."]}),"\n"]})}),"\n",(0,t.jsx)(s.p,{children:"The relay compiler supports persisted queries. This is useful because:"}),"\n",(0,t.jsxs)(s.ul,{children:["\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsx)(s.p,{children:"The client operation text becomes just an md5 hash which is usually shorter than the real\nquery string. This saves upload bytes from the client to the server."}),"\n"]}),"\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsx)(s.p,{children:"The server can now allowlist queries which improves security by restricting the operations\nthat can be executed by a client."}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(a.OssOnly,{children:[(0,t.jsx)(s.h2,{id:"usage-on-the-client",children:"Usage on the client"}),(0,t.jsxs)(s.h3,{id:"the-persistconfig-option",children:["The ",(0,t.jsx)(s.code,{children:"persistConfig"})," option"]}),(0,t.jsxs)(s.p,{children:["In your relay configiration section in ",(0,t.jsx)(s.code,{children:"package.json"}),' you\'ll need specify\n"persistConfig".']}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:'"scripts": {\n "relay": "relay-compiler",\n "relay-persisting": "node relayLocalPersisting.js"\n},\n"relay": {\n "src": "./src",\n "schema": "./schema.graphql",\n "persistConfig": {\n "url": "http://localhost:2999",\n "params": {}\n }\n}\n'})}),(0,t.jsxs)(s.p,{children:["Specifiying ",(0,t.jsx)(s.code,{children:"persistConfig"})," in the config will do the following:"]}),(0,t.jsxs)(s.ol,{children:["\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsx)(s.p,{children:"It converts all query and mutation operation texts to md5 hashes."}),"\n",(0,t.jsxs)(s.p,{children:["For example without ",(0,t.jsx)(s.code,{children:"persist
1Config"}),", a generated ",(0,t.jsx)(s.code,{children:"ConcreteRequest"})," might look\nlike below:"]}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-javascript",children:'const node/*: ConcreteRequest*/ = (function(){\n//... excluded for brevity\nreturn {\n "kind": "Request",\n "operationKind": "query",\n "name": "TodoItemRefetchQuery",\n "id": null, // NOTE: id is null\n "text": "query TodoItemRefetchQuery(\\n $itemID: ID!\\n) {\\n node(id: $itemID) {\\n ...TodoItem_item_2FOrhs\\n }\\n}\\n\\nfragment TodoItem_item_2FOrhs on Todo {\\n text\\n isComplete\\n}\\n",\n //... excluded for brevity\n};\n})();\n\n'})}),"\n",(0,t.jsxs)(s.p,{children:["With ",(0,t.jsx)(s.code,{children:"persistConfig"})," this becomes:"]}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-javascript",children:'const node/*: ConcreteRequest*/ = (function(){\n//... excluded for brevity\nreturn {\n "kind": "Request",\n "operationKind": "query",\n "name": "TodoItemRefetchQuery",\
1n "id": "3be4abb81fa595e25eb725b2c6a87508", // NOTE: id is now an md5 hash\n // of the query text\n "text": null, // NOTE: text is null now\n //... excluded for brevity\n};\n})();\n\n'})}),"\n"]}),"\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsxs)(s.p,{children:["It will send an HTTP POST request with a ",(0,t.jsx)(s.code,{children:"text"})," parameter to the\nspecified ",(0,t.jsx)(s.code,{children:"url"}),".\nYou can also add additional request body parameters via the ",(0,t.jsx)(s.code,{children:"params"})," option."]}),"\n"]}),"\n"]}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:'"scripts": {\n "relay": "relay-compiler"\n},\n"relay": {\n "src": "./src",\n "schema": "./schema.graphql",\n "persistConfig": {\n "url": "http://localhost:2999",\n "params": {}\n }\n}\n'})}),(0,t.jsx)(s.h3,{id:"local-persisted-queries",children:"Local Persisted Queries"}),(0,t.jsxs)(s.p,{children:["With the following config, you can generate a local JSON file which contains a map of ",(0,t.jsx)(s.code,{children:"operation_id => full operation text"}),"."]}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:'"scripts": {\n "relay": "relay-compiler"\n},\n"relay": {\n "src": "./src",\n "schema": "./schema.graphql",\n "persistConfig": {\n "file": "./persisted_queries.json",\n "algorithm": "MD5" // this can be one of MD5, SHA256, SHA1\n }\n}\n'})}),(0,t.jsxs)(s.p,{children:["Ideally, you'll take this file and ship it to your server at deploy time so your server knows about all the queries it could possibly receive. If you don't want to do that, you'll have to implement the ",(0,t.jsx)(s.a,{href:"https://www.apollographql.com/docs/apollo-server/performance/apq/",children:"Automatic Persisted Queries handshake"}),"."]}),(0,t.jsx)(s.h4,{id:"tradeoffs",children:"Tradeoffs"}),(0,t.jsxs)(s.ul,{children:["\n",(0,t.jsx)(s.li,{children:"\u2705 If your server's persisted query datastore gets wiped, you can recover automatically through your client's requests."}),"\n",(0,t.jsx)(s.li,{children:"\u274C When there's a cache miss, it'll cost you an extra round trip to the server."}),"\n",(0,t.jsxs)(s.li,{children:["\u274C You'll have to ship your ",(0,t.jsx)(s.code,{children:"persisted_queries.json"})," file to the browser which will increase your bundle size."]}),"\n"]}),(0,t.jsxs)(s.h3,{id:"example-implemetation-of-relaylocalpersistingjs",children:["Example implemetation of ",(0,t.jsx)(s.code,{children:"relayLocalPersisting.js"})]}),(0,t.jsxs)(s.p,{children:["Here's an example of a simple persist server that will save query text to the ",(0,t.jsx)(s.code,{children:"queryMap.json"})," file."]}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-javascript",children:"const http = require('http');\nconst crypto = require('crypto');\nconst fs = require('fs');\n\nfunction md5(input) {\n return crypto.createHash('md5').update(input).digest('hex');\n}\n\nclass QueryMap {\n constructor(fileMapName) {\n this._fileMapName = fileMapName;\n this._queryMap = new Map(JSON.parse(fs.readFileSync(this._fileMapName)));\n }\n\n _flush() {\n const data = JSON.stringify(Array.from(this._queryMap.entries()));\n fs.writeFileSync(this._fileMapName, data);\n }\n\n saveQuery(text) {\n const id = md5(text);\n this._queryMap.set(id, text);\n this._flush();\n return id;\n }\n}\n\nconst queryMap = new QueryMap('./queryMap.json');\n\nasync function requestListener(req, res) {\n if (req.method === 'POST') {\n const buffers = [];\n for await (const chunk of req) {\n buffers.push(chunk);\n }\n const data = Buffer.concat(buffers).toString();\n res.writeHead(200, {\n 'Content-Type': 'application/json'\n });\n try {\n if (req.headers['content-type'] !== 'application/x-www-form-urlencoded') {\n throw new Error(\n 'Only \"application/x-www-form-urlencoded\" requests are supported.'\n );\n }\n const text = new URLSearchParams(data).get('text');\n if (text == null) {\n throw new Error('Expected to have `text` parameter in the POST.');\n }\n const id = queryMap.saveQuery(text);\n res.end(JSON.stringify({\"id\": id}));\n } catch (e) {\n console.error(e);\n res.writeHead(400);\n res.end(`Unable to save query: ${e}.`);\n }\n } else {\n res.writeHead(400);\n res.end(\"Request is not supported.\")\n }\n}\n\nconst PORT = 2999;\nconst server = http.createServer(requestListener);\nserver.listen(PORT);\n\nconsole.log(`Relay persisting server listening on ${PORT} port.`);\n"})}),(0,t.jsxs)(s.p,{children:["The example above writes the complete query map file to ",(0,t.jsx)(s.code,{children:"./queryMap.json"}),".\nTo use this, you'll need to update ",(0,t.jsx)(s.code,{children:"package.json"}),":"]}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:'"scripts": {\n "persist-server": "node ./relayLocalPersisting.js",\n "relay": "relay-compiler"\n}\n'})})]}),"\n",(0,t.jsx)(s.h3,{id:"network-layer-changes",children:"Network layer changes"}),"\n",(0,t.jsxs)(s.p,{children:["You'll need to modify your network layer fetch implementation to pass an ID parameter in the POST body (e.g., ",(0,t.jsx)(s.code,{children:"doc_id"}),") instead of a query parameter:"]}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-javascript",children:"function fetchQuery(operation, variables) {\n return fetch('/graphql', {\n method: 'POST',\n headers: {\n 'content-type': 'application/json'\n },\n body: JSON.stringify({\n doc_id: operation.id, // NOTE: pass md5 hash to the server\n // query: operation.text, // this is now obsolete because text is null\n variables,\n }),\n }).then(response => {\n return response.json();\n });\n}\n"})}),"\n",(0,t.jsx)(s.h2,{id:"executing-persisted-queries-on-the-server",children:"Executing Persisted Quer
1ies on the Server"}),"\n",(0,t.jsx)(a.FbInternalOnly,{children:(0,t.jsxs)(s.p,{children:["Your server should then look up the query referenced by ",(0,t.jsx)(s.code,{children:"doc_id"})," when responding to this request."]})}),"\n",(0,t.jsxs)(a.OssOnly,{children:[(0,t.jsxs)(s.p,{children:["To execute client requests that send persisted queries instead of query text, your server will need to be able\nto lookup the query text corresponding to each ID. Typically this will involve saving the output of the ",(0,t.jsx)(s.code,{children:"queryMap.json"})," JSON file to a database or some other storage mechanism, and retrieving the corresponding text for the ID specified by a client."]}),(0,t.jsxs)(s.p,{children:["Additionally, your implementation of ",(0,t.jsx)(s.code,{children:"relayLocalPersisting.js"})," could directly save queries to the database or other storage."]}),(0,t.jsx)(s.p,{children:"For universal applications where the client and server code are in one project, this is not an issue since you can place\nthe query map file in a common location accessible to both the client and the server."}),(0,t.jsx)(s.h3,{id:"compile-time-push",children:"Compile time push"}),(0,t.jsx)(s.p,{children:"For applications where the client and server projects are separate, one option is to have an additional npm run script\nto push the query map at compile time to a location accessible by your server:"}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-javascript",children:'"scripts": {\n "push-queries": "node ./pushQueries.js",\n "persist-server": "node ./relayLocalPersisting.js",\n "relay": "relay-compiler && npm run push-queries"\n}\n'})}),(0,t.jsxs)(s.p,{children:["Some possibilities of what you can do in ",(0,t.jsx)(s.code,{children:"./pushQueries.js"}),":"]}),(0,t.jsxs)(s.ul,{children:["\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.code,{children:"git push"})," to your server repo."]}),"\n"]}),"\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsx)(s.p,{children:"Save the query maps to a database."}),"\n"]}),"\n"]}),(0,t.jsx)(s.h3,{id:"run-time-push",children:"Run time push"}),(0,t.jsx)(s.p,{children:"A second more complex option is to push your query maps to the server at runtime, without the server knowing the query IDs at the start.\nThe client optimistically sends a query ID to the server, which does not have the query map. The server then in turn requests\nfor the full query text from the client so it can cache the query map for subsequent requests. This is a more complex approach\nrequiring the client and server to interact to exchange the query maps."}),(0,t.jsx)(s.h3,{id:"simple-server-example",children:"Simple server example"}),(0,t.jsx)(s.p,{children:"Once your server has access to the query map, you can perform the mapping. The solution varies depen
1ding on the server and\ndatabase technologies you use, so we'll just cover the most common and basic example here."}),(0,t.jsxs)(s.p,{children:["If you use ",(0,t.jsx)(s.code,{children:"express-graphql"})," and have access to the query map file, you can import it directly and\nperform the matching using the ",(0,t.jsx)(s.code,{children:"persistedQueries"})," middleware from ",(0,t.jsx)(s.a,{href:"https://github.com/kyarik/express-graphql-persisted-queries",children:"express-graphql-persisted-queries"}),"."]}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-javascript",children:"import express from 'express';\nimport {graphqlHTTP} from 'express-graphql';\nimport {persistedQueries} from 'express-graphql-persisted-queries';\nimport queryMap from './path/to/queryMap.json';\n\nconst app = express();\n\napp.use(\n '/graphql',\n persistedQueries({\n queryMap,\n queryIdKey: 'doc_id',\n }),\n graphqlHTTP({schema}),\n);\n"})}),(0,t.jsxs)(s.h2,{id:"using-persistconfig-and---watch",children:["Using ",(0,t.jsx)(s.code,{children:"persistConfig"})," and ",(0,t.jsx)(s.code,{children:"--watch"})]}),(0,t.jsxs)(s.p,{children:["It is possible to continuously generate the query map files by using the ",(0,t.jsx)(s.code,{children:"persistConfig"})," and ",(0,t.jsx)(s.code,{children:"--watch"})," options simultaneously.\nThis only makes sense for universal applications i.e. if your client and server code are in a single project\nand you run them both together on localhost during development. Furthermore, in order for the server to pick up changes\nto the ",(0,t.jsx)(s.code,{children:"queryMap.json"}),", you'll need to have server side hot-reloading set up. The details on how to set this up\nare out of the scope of this document."]})]}),"\n",(0,t.jsx)(o.A,{})]})}function u(e={}){let{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,t.jsx)(s,{...e,children:(0,t.jsx)(p,{...e})}):p(e)}},86898(e,s,n){n.d(s,{A:()=>m});var r=n(74848),t=n(95310),i=n(17352),o=n(96540);function a(){let e=window.encodeURI(JSON.stringify({title:"Feedback about "+window.location.pathname,description:"**!!! Required !!!**\n\nPlease modify the task description to let us know how the docs can be improved.\n\n**Please do not ask support questions via this form! Instead, ask in fburl.com/relay_support**",tag_ids:{add:[0xac96423e5b680,0x64079768ac750]}}));window.open("https://www.internalfb.com/tasks/?n="+e)}function l({children:e}){return(0,r.jsxs)("div",{className:"docsRating",id:"docsRating",children:[(0,r.jsx)("hr",{}),e]})}let c=()=>{let[e,s]=o.useState(!1),n=e=>{s(!0),window.ga&&window.ga("send",{hitType:"event",eventCategory:"button",eventAction:"feedback",eventValue:e})};return e?"Thank you for letting us know!":(0,r.jsxs)(r.Fragment,{children:["Is this page useful?",(0,r.jsx)("svg",{className:"i_thumbsup",alt:"Like",id:"docsRating-like",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 81.13 89.76",onClick:()=>n(1),children:(0,r.jsx)("path",{d:"M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z"})}),(0,r.jsx)("svg",{className:"i_thumbsdown",alt:"Dislike",id:"docsRating-dislike",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 81.13 89.76",onClick:()=>n(0),children:(0,r.jsx)("path",{d:"M22.9 6a18.57 18.57 0 002.67 8.4 25.72 25.72 0 008.65 7.66c3.86 2 8.67 7.13 13.51 11 3.86 3.11 8.57 7.11 11.54 8.45s13.59.26 14.64 1.17c1.88 1.63 1.55 9-.11 15.25-1.61 5.86-5.96 10.55-6.48 16.86-.4 4.83-2.7 4.88-10.93 4.88h-1.35c-3.82 0-8.24 2.93-12.92 3.62a68 68 0 01-9.73.5c-3.57 0-7.86-.08-13.25-.08-3.56 0-4.71-1.83-4.71-4.48h8.42a3.51 3.51 0 000-7H12.28a2.89 2.89 0 01-2.88-2.88 1.91 1.91 0 01.77-1.78h16.46a3.51 3.51 0 000-7H12.29c-3.21 0-4.84-1.83-4.84-4a6.41 6.41 0 011.17-3.78h19.06a3.5 3.5 0 100-7H9.75A3.51 3.51 0 016 42.27a3.45 3.45 0 013.75-3.48h13.11c5.61 0 7.71-3 5.71-5.52-4.43-4.74-10.84-12.62-11-18.71-.15-6.51 2.6-7.83 5.36-8.56m0-6a6.18 6.18 0 00-1.53.2c-6.69 1.77-10 6.65-9.82 14.5.08 5.09 2.99 11.18 8.52 18.09H9.74a9.52 9.52 0 00-6.23 16.9 12.52 12.52 0 00-2.07 6.84 9.64 9.64 0 003.65 7.7 7.85 7.85 0 00-1.7 5.13 8.9 8.9 0 005.3 8.13 6 6 0 00-.26 1.76c0 6.37 4.2 10.48 10.71 10.48h13.25a73.75 73.75 0 0010.6-.56 35.89 35.89 0 007.58-2.18 17.83 17.83 0 014.48-1.34h1.35c4.69 0 7.79 0 10.5-1 3.85-1.44 6-4.59 6.41-9.38.2-2.46 1.42-4.85 2.84-7.62a41.3 41.3 0 003.42-8.13 48 48 0 001.59-10.79c.1-5.13-1-8.48-3.35-10.55-2.16-1.87-4.64-1.87-9.6-1.88a46.86 46.86 0 01-6.64-.29c-1.92-.94-5.72-4-8.51-6.3l-1.58-1.28c-1.6-1.3-3.27-2.79-4.87-4.23-3.33-3-6.47-5.79-9.61-7.45a20.2 20.2 0 01-6.43-5.53 12.44 12.44 0 01-1.72-5.36 6 6 0 00-6-5.86z"})})]})},d=()=>(0,r.jsxs)("p",{children:["Let us know how these docs can be improved by",(0,r.jsx)("a",{className:"button",role:"button",tabIndex:0,onClick:a,children:"Filing a task"})]}),h=()=>(0,r.jsxs)("p",{children:["Help us make the site even better by"," ",(0,r.jsx)(t.default,{to:"https://www.surveymonkey.com/r/FYC9TCJ",children:"answering a few quick questions"}),"."]}),p=()=>(0,r.jsxs)(l,{children:[(0,r.jsx)(d,{}),(0,r.jsx)(c,{}),(0,r.jsx)(h,{})]}),u=()=>(0,r.jsxs)(l,{children:[(0,r.jsx)(c,{}),(0,r.jsx)(h,{})]}),m=()=>(0,i.fbContent)({internal:(0,r.jsx)(p,{}),external:(0,r.jsx)(u,{})})},28453(e,s,n){n.d(s,{R:()=>o,x:()=>a});var r=n(96540);let t={},i=r.createContext(t);function o(e){let s=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function a(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),r.createElement(i.Provider,{value:s},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.