[{"data":1,"prerenderedAt":6514},["ShallowReactive",2],{"navigation_docs_en":3,"-en-developer-extensions-data-connectors-data-source-mapping":445,"-en-developer-extensions-data-connectors-data-source-mapping-surround":6509},[4,30,72,132,260,436],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":6},"Getting Started",false,"\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,25],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F2.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Quickstart","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F3.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Use Cases","\u002Fen\u002Fgetting-started\u002Fuse-cases","en\u002F1.getting-started\u002F4.use-cases","i-lucide-layers",{"title":26,"path":27,"stem":28,"icon":29},"Concepts","\u002Fen\u002Fgetting-started\u002Fconcepts","en\u002F1.getting-started\u002F5.concepts","i-lucide-book",{"title":26,"path":31,"stem":32,"children":33,"page":6},"\u002Fen\u002Fconcepts","en\u002F2.concepts",[34,38,43,48,53,58,63,68],{"title":35,"path":36,"stem":37,"icon":14},"Organization","\u002Fen\u002Fconcepts\u002Forganization","en\u002F2.concepts\u002F1.organization",{"title":39,"path":40,"stem":41,"icon":42},"Data Model","\u002Fen\u002Fconcepts\u002Fdata-model","en\u002F2.concepts\u002F2.data-model","i-lucide-database",{"title":44,"path":45,"stem":46,"icon":47},"Access Control","\u002Fen\u002Fconcepts\u002Faccess-permissions","en\u002F2.concepts\u002F3.access-permissions","i-lucide-user-key",{"title":49,"path":50,"stem":51,"icon":52},"Audit Logs","\u002Fen\u002Fconcepts\u002Faudit-logs","en\u002F2.concepts\u002F4.audit-logs","i-lucide-scroll-text",{"title":54,"path":55,"stem":56,"icon":57},"Introspection","\u002Fen\u002Fconcepts\u002Fintrospection","en\u002F2.concepts\u002F6.introspection","i-lucide-database-search",{"title":59,"path":60,"stem":61,"icon":62},"Query Engine","\u002Fen\u002Fconcepts\u002Fquery-engine","en\u002F2.concepts\u002F7.query-engine","i-lucide-workflow",{"title":64,"path":65,"stem":66,"icon":67},"AI","\u002Fen\u002Fconcepts\u002Fai","en\u002F2.concepts\u002F8.ai","i-lucide-sparkles",{"title":69,"path":70,"stem":71,"icon":42},"Caching","\u002Fen\u002Fconcepts\u002Fcaching","en\u002F2.concepts\u002F9.caching",{"title":73,"path":74,"stem":75,"children":76,"page":6},"Guides","\u002Fen\u002Fguides","en\u002F4.guides",[77,82,87,92,97,102,107,112],{"title":78,"path":79,"stem":80,"icon":81},"REST API Quickstart","\u002Fen\u002Fguides\u002Frest-api","en\u002F4.guides\u002F1.rest-api","i-lucide-plug",{"title":83,"path":84,"stem":85,"icon":86},"SDK Quickstart","\u002Fen\u002Fguides\u002Fsdk","en\u002F4.guides\u002F2.sdk","i-lucide-terminal",{"title":88,"path":89,"stem":90,"icon":91},"Configure SSO","\u002Fen\u002Fguides\u002Fconfigure-sso","en\u002F4.guides\u002F3.configure-sso","i-lucide-key-round",{"title":93,"path":94,"stem":95,"icon":96},"Configure MCP","\u002Fen\u002Fguides\u002Fmcp","en\u002F4.guides\u002F4.mcp","i-lucide-brain-cog",{"title":98,"path":99,"stem":100,"icon":101},"Customize Content Space","\u002Fen\u002Fguides\u002Fcustomize-content-space","en\u002F4.guides\u002F5.customize-content-space","i-lucide-columns-3-cog",{"title":103,"path":104,"stem":105,"icon":106},"Build an Application with AI Assistance","\u002Fen\u002Fguides\u002Fbuild-an-application","en\u002F4.guides\u002F6.build-an-application","i-lucide-route",{"title":108,"path":109,"stem":110,"icon":111},"Horizontal Scaling","\u002Fen\u002Fguides\u002Fhorizontal-scaling","en\u002F4.guides\u002F7.horizontal-scaling","i-lucide-network",{"title":113,"icon":114,"path":115,"stem":116,"children":117,"page":6},"Data Connectors","i-lucide-cable","\u002Fen\u002Fguides\u002Fdata-connectors","en\u002F4.guides\u002F8.data-connectors",[118,122,127],{"title":16,"path":119,"stem":120,"icon":121},"\u002Fen\u002Fguides\u002Fdata-connectors\u002Fquickstart","en\u002F4.guides\u002F8.data-connectors\u002F1.quickstart","i-lucide-rocket",{"title":123,"path":124,"stem":125,"icon":126},"Build a Stripe Connector","\u002Fen\u002Fguides\u002Fdata-connectors\u002Fstripe","en\u002F4.guides\u002F8.data-connectors\u002F2.stripe","i-lucide-hammer",{"title":128,"path":129,"stem":130,"icon":131},"Build with AI Agents","\u002Fen\u002Fguides\u002Fdata-connectors\u002Fai-agents","en\u002F4.guides\u002F8.data-connectors\u002F3.ai-agents","i-lucide-bot",{"title":133,"path":134,"stem":135,"children":136,"page":6},"Developer","\u002Fen\u002Fdeveloper","en\u002F5.developer",[137,191,215,220],{"title":138,"path":139,"stem":140,"children":141,"page":6},"Data Access","\u002Fen\u002Fdeveloper\u002Fapi","en\u002F5.developer\u002F1.api",[142,147,152,157,162,167,172,177,182,186],{"title":143,"path":144,"stem":145,"icon":146},"Overview","\u002Fen\u002Fdeveloper\u002Fapi\u002Foverview","en\u002F5.developer\u002F1.api\u002F1.overview","i-lucide-globe",{"title":148,"path":149,"stem":150,"icon":151},"Errors","\u002Fen\u002Fdeveloper\u002Fapi\u002Ferrors","en\u002F5.developer\u002F1.api\u002F10.errors","i-lucide-alert-triangle",{"title":153,"path":154,"stem":155,"icon":156},"Authentication","\u002Fen\u002Fdeveloper\u002Fapi\u002Fauthentication","en\u002F5.developer\u002F1.api\u002F2.authentication","i-lucide-lock",{"title":158,"path":159,"stem":160,"icon":161},"Reading Data","\u002Fen\u002Fdeveloper\u002Fapi\u002Freading-data","en\u002F5.developer\u002F1.api\u002F3.reading-data","i-lucide-book-open",{"title":163,"path":164,"stem":165,"icon":166},"Writing Data","\u002Fen\u002Fdeveloper\u002Fapi\u002Fwriting-data","en\u002F5.developer\u002F1.api\u002F4.writing-data","i-lucide-pencil",{"title":168,"path":169,"stem":170,"icon":171},"Filtering","\u002Fen\u002Fdeveloper\u002Fapi\u002Ffiltering","en\u002F5.developer\u002F1.api\u002F5.filtering","i-lucide-filter",{"title":173,"path":174,"stem":175,"icon":176},"Field Selection","\u002Fen\u002Fdeveloper\u002Fapi\u002Ffield-selection","en\u002F5.developer\u002F1.api\u002F6.field-selection","i-lucide-list-checks",{"title":178,"path":179,"stem":180,"icon":181},"Sorting & Pagination","\u002Fen\u002Fdeveloper\u002Fapi\u002Fsorting-pagination","en\u002F5.developer\u002F1.api\u002F7.sorting-pagination","i-lucide-arrow-up-down",{"title":183,"path":184,"stem":185,"icon":111},"Relational Data","\u002Fen\u002Fdeveloper\u002Fapi\u002Frelational-data","en\u002F5.developer\u002F1.api\u002F8.relational-data",{"title":187,"path":188,"stem":189,"icon":190},"Files & Assets","\u002Fen\u002Fdeveloper\u002Fapi\u002Ffiles","en\u002F5.developer\u002F1.api\u002F9.files","i-lucide-file-up",{"title":192,"path":193,"stem":194,"children":195,"page":6},"Client SDK","\u002Fen\u002Fdeveloper\u002Fsdk","en\u002F5.developer\u002F2.sdk",[196,200,205,210],{"title":197,"path":198,"stem":199,"icon":19},"Installation","\u002Fen\u002Fdeveloper\u002Fsdk\u002Finstallation","en\u002F5.developer\u002F2.sdk\u002F1.installation",{"title":201,"path":202,"stem":203,"icon":204},"Client Setup","\u002Fen\u002Fdeveloper\u002Fsdk\u002Fclient-setup","en\u002F5.developer\u002F2.sdk\u002F2.client-setup","i-lucide-settings",{"title":206,"path":207,"stem":208,"icon":209},"Type System","\u002Fen\u002Fdeveloper\u002Fsdk\u002Ftype-system","en\u002F5.developer\u002F2.sdk\u002F3.type-system","i-lucide-braces",{"title":211,"path":212,"stem":213,"icon":214},"Advanced","\u002Fen\u002Fdeveloper\u002Fsdk\u002Fadvanced","en\u002F5.developer\u002F2.sdk\u002F5.advanced","i-lucide-puzzle",{"title":216,"path":217,"stem":218,"icon":219},"Application Users","\u002Fen\u002Fdeveloper\u002Fapplication-users","en\u002F5.developer\u002F3.application-users","i-lucide-users",{"title":221,"path":222,"stem":223,"children":224,"icon":214},"Extensions","\u002Fen\u002Fdeveloper\u002Fextensions","en\u002F5.developer\u002F3.extensions\u002Findex",[225,226,231],{"title":143,"path":222,"stem":223,"icon":214},{"title":227,"path":228,"stem":229,"icon":230},"Install and Manage Extensions","\u002Fen\u002Fdeveloper\u002Fextensions\u002Finstall","en\u002F5.developer\u002F3.extensions\u002F1.install","i-lucide-package-plus",{"title":113,"path":232,"stem":233,"children":234,"icon":114},"\u002Fen\u002Fdeveloper\u002Fextensions\u002Fdata-connectors","en\u002F5.developer\u002F3.extensions\u002F2.data-connectors\u002Findex",[235,236,241,245,250,255],{"title":143,"path":232,"stem":233,"icon":114},{"title":237,"path":238,"stem":239,"icon":240},"How Data Connectors Run","\u002Fen\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Fruntime","en\u002F5.developer\u002F3.extensions\u002F2.data-connectors\u002F2.runtime","i-lucide-cpu",{"title":242,"path":243,"stem":244,"icon":42},"Data Sources","\u002Fen\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Fdata-sources","en\u002F5.developer\u002F3.extensions\u002F2.data-connectors\u002F3.data-sources",{"title":246,"path":247,"stem":248,"icon":249},"Map an External Data Source","\u002Fen\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Fdata-source-mapping","en\u002F5.developer\u002F3.extensions\u002F2.data-connectors\u002F4.data-source-mapping","i-lucide-map",{"title":251,"path":252,"stem":253,"icon":254},"Handle Errors","\u002Fen\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Ferrors","en\u002F5.developer\u002F3.extensions\u002F2.data-connectors\u002F5.errors","i-lucide-shield-alert",{"title":256,"path":257,"stem":258,"icon":259},"Test a Data Connector","\u002Fen\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Ftesting","en\u002F5.developer\u002F3.extensions\u002F2.data-connectors\u002F6.testing","i-lucide-flask-conical",{"title":261,"path":262,"stem":263,"children":264,"page":6},"Reference","\u002Fen\u002Freference","en\u002F6.reference",[265,269,274,279,295,299,304,340,369,374],{"title":266,"path":267,"stem":268,"icon":204},"Configuration","\u002Fen\u002Freference\u002Fenvironment-variables","en\u002F6.reference\u002F1.environment-variables",{"title":270,"path":271,"stem":272,"icon":273},"System Requirements","\u002Fen\u002Freference\u002Fsystem-requirements","en\u002F6.reference\u002F2.system-requirements","i-lucide-server",{"title":275,"path":276,"stem":277,"icon":278},"Licensing","\u002Fen\u002Freference\u002Flicensing","en\u002F6.reference\u002F3.licensing","i-lucide-gem",{"title":280,"path":281,"stem":282,"children":283,"icon":81},"Connectors","\u002Fen\u002Freference\u002Fconnectors","en\u002F6.reference\u002F4.connectors\u002Findex",[284,285,290],{"title":280,"path":281,"stem":282,"icon":81},{"title":286,"path":287,"stem":288,"icon":289},"PostgreSQL and Supabase","\u002Fen\u002Freference\u002Fconnectors\u002Fpostgresql-supabase","en\u002F6.reference\u002F4.connectors\u002F1.postgresql-supabase","i-simple-icons-postgresql",{"title":291,"path":292,"stem":293,"icon":294},"MySQL and MariaDB","\u002Fen\u002Freference\u002Fconnectors\u002Fmysql-mariadb","en\u002F6.reference\u002F4.connectors\u002F2.mysql-mariadb","i-simple-icons-mysql",{"title":296,"path":297,"stem":298,"icon":209},"Data Types Representation","\u002Fen\u002Freference\u002Fdata-types","en\u002F6.reference\u002F5.data-types",{"title":300,"path":301,"stem":302,"icon":303},"Permissions Reference","\u002Fen\u002Freference\u002Fpermissions","en\u002F6.reference\u002F6.permissions","i-lucide-shield",{"title":305,"path":306,"stem":307,"children":308,"page":6},"API Reference","\u002Fen\u002Freference\u002Fapi-reference","en\u002F6.reference\u002F7.api-reference",[309,313,318],{"title":310,"path":311,"stem":312,"icon":273},"System Endpoints","\u002Fen\u002Freference\u002Fapi-reference\u002Fsystem-endpoints","en\u002F6.reference\u002F7.api-reference\u002F1.system-endpoints",{"title":314,"path":315,"stem":316,"icon":317},"OpenAPI Spec","\u002Fen\u002Freference\u002Fapi-reference\u002Fopenapi-spec","en\u002F6.reference\u002F7.api-reference\u002F2.openapi-spec","i-lucide-file-code",{"title":319,"path":320,"stem":321,"children":322,"icon":324},"Schema Migration","\u002Fen\u002Freference\u002Fapi-reference\u002Fschema-migration","en\u002F6.reference\u002F7.api-reference\u002F3.schema-migration\u002Findex",[323,325,330,335],{"title":319,"path":320,"stem":321,"icon":324},"i-lucide-database-zap",{"title":326,"path":327,"stem":328,"icon":329},"Apply Migrations","\u002Fen\u002Freference\u002Fapi-reference\u002Fschema-migration\u002Fapply-migrations","en\u002F6.reference\u002F7.api-reference\u002F3.schema-migration\u002F1.apply-migrations","i-lucide-upload",{"title":331,"path":332,"stem":333,"icon":334},"Import Changes","\u002Fen\u002Freference\u002Fapi-reference\u002Fschema-migration\u002Fimport-changes","en\u002F6.reference\u002F7.api-reference\u002F3.schema-migration\u002F2.import-changes","i-lucide-refresh-cw",{"title":336,"path":337,"stem":338,"icon":339},"Operation Reference","\u002Fen\u002Freference\u002Fapi-reference\u002Fschema-migration\u002Foperation-reference","en\u002F6.reference\u002F7.api-reference\u002F3.schema-migration\u002F3.operation-reference","i-lucide-list-tree",{"title":341,"path":342,"stem":343,"children":344,"page":6},"Design Considerations","\u002Fen\u002Freference\u002Fdesign-considerations","en\u002F6.reference\u002F8.design-considerations",[345,349,354,359,364],{"title":143,"path":346,"stem":347,"icon":348},"\u002Fen\u002Freference\u002Fdesign-considerations\u002Foverview","en\u002F6.reference\u002F8.design-considerations\u002F1.overview","i-lucide-compass",{"title":350,"path":351,"stem":352,"icon":353},"Instance Configuration","\u002Fen\u002Freference\u002Fdesign-considerations\u002Finstance-configuration-related-problems","en\u002F6.reference\u002F8.design-considerations\u002F3.instance-configuration-related-problems","i-lucide-wrench",{"title":355,"path":356,"stem":357,"icon":358},"Overly Permissive Public Access","\u002Fen\u002Freference\u002Fdesign-considerations\u002Foverly-permissive","en\u002F6.reference\u002F8.design-considerations\u002F4.overly-permissive","i-lucide-shield-off",{"title":360,"path":361,"stem":362,"icon":363},"No Aggregates","\u002Fen\u002Freference\u002Fdesign-considerations\u002Fno-aggregates","en\u002F6.reference\u002F8.design-considerations\u002F5.no-aggregates","i-lucide-sigma",{"title":365,"path":366,"stem":367,"icon":368},"Cross-Data-Source Referential Integrity","\u002Fen\u002Freference\u002Fdesign-considerations\u002Fcross-data-source-referential-integrity","en\u002F6.reference\u002F8.design-considerations\u002F6.cross-data-source-referential-integrity","i-lucide-unlink",{"title":370,"path":371,"stem":372,"icon":373},"CLI","\u002Fen\u002Freference\u002Fcli","en\u002F6.reference\u002F9.cli","i-lucide-square-terminal",{"title":221,"path":375,"stem":376,"children":377,"page":6},"\u002Fen\u002Freference\u002Fextensions","en\u002F6.reference\u002F9.extensions",[378,383,386,405],{"title":379,"path":380,"stem":381,"icon":382},"Extension Config and Manifest","\u002Fen\u002Freference\u002Fextensions\u002Fmanifest","en\u002F6.reference\u002F9.extensions\u002F1.manifest","i-lucide-file-json",{"title":370,"path":384,"stem":385,"icon":86},"\u002Fen\u002Freference\u002Fextensions\u002Fcli","en\u002F6.reference\u002F9.extensions\u002F2.cli",{"title":387,"path":388,"stem":389,"children":390,"page":6},"Extension Kit","\u002Fen\u002Freference\u002Fextensions\u002Fextension-kit","en\u002F6.reference\u002F9.extensions\u002F3.extension-kit",[391,395,400],{"title":392,"path":393,"stem":394,"icon":81},"Data Connector","\u002Fen\u002Freference\u002Fextensions\u002Fextension-kit\u002Fdata-connector","en\u002F6.reference\u002F9.extensions\u002F3.extension-kit\u002F1.data-connector",{"title":396,"path":397,"stem":398,"icon":399},"Protocol","\u002Fen\u002Freference\u002Fextensions\u002Fextension-kit\u002Fprotocol","en\u002F6.reference\u002F9.extensions\u002F3.extension-kit\u002F2.protocol","i-lucide-arrow-left-right",{"title":401,"path":402,"stem":403,"icon":404},"Schema Helpers","\u002Fen\u002Freference\u002Fextensions\u002Fextension-kit\u002Fexperimental","en\u002F6.reference\u002F9.extensions\u002F3.extension-kit\u002F3.experimental","i-lucide-blocks",{"title":113,"path":406,"stem":407,"children":408,"page":6},"\u002Fen\u002Freference\u002Fextensions\u002Fdata-connectors","en\u002F6.reference\u002F9.extensions\u002F4.data-connectors",[409,413,417,421,426,431],{"title":410,"path":411,"stem":412,"icon":81},"Data Connector API","\u002Fen\u002Freference\u002Fextensions\u002Fdata-connectors\u002Fapi","en\u002F6.reference\u002F9.extensions\u002F4.data-connectors\u002F1.api",{"title":414,"path":415,"stem":416,"icon":176},"Operations and Operators","\u002Fen\u002Freference\u002Fextensions\u002Fdata-connectors\u002Foperations","en\u002F6.reference\u002F9.extensions\u002F4.data-connectors\u002F2.operations",{"title":148,"path":418,"stem":419,"icon":420},"\u002Fen\u002Freference\u002Fextensions\u002Fdata-connectors\u002Ferrors","en\u002F6.reference\u002F9.extensions\u002F4.data-connectors\u002F3.errors","i-lucide-triangle-alert",{"title":422,"path":423,"stem":424,"icon":425},"Runtime Limits","\u002Fen\u002Freference\u002Fextensions\u002Fdata-connectors\u002Flimits","en\u002F6.reference\u002F9.extensions\u002F4.data-connectors\u002F4.limits","i-lucide-gauge",{"title":427,"path":428,"stem":429,"icon":430},"Limitations","\u002Fen\u002Freference\u002Fextensions\u002Fdata-connectors\u002Flimitations","en\u002F6.reference\u002F9.extensions\u002F4.data-connectors\u002F5.limitations","i-lucide-ban",{"title":432,"path":433,"stem":434,"icon":435},"Network Permissions","\u002Fen\u002Freference\u002Fextensions\u002Fdata-connectors\u002Fpermissions","en\u002F6.reference\u002F9.extensions\u002F4.data-connectors\u002F6.permissions","i-lucide-shield-check",{"title":437,"icon":121,"path":438,"stem":439,"children":440,"page":6},"Release Notes","\u002Fen\u002Frelease-notes","en\u002F7.release-notes",[441],{"title":442,"path":443,"stem":444,"icon":121},"Changelog","\u002Fen\u002Frelease-notes\u002Fchangelog","en\u002F7.release-notes\u002F1.changelog",{"id":446,"title":246,"body":447,"description":6502,"extension":6503,"links":6504,"meta":6505,"navigation":6506,"path":247,"seo":6507,"stem":248,"__hash__":6508},"docs_en\u002Fen\u002F5.developer\u002F3.extensions\u002F2.data-connectors\u002F4.data-source-mapping.md",{"type":448,"value":449,"toc":6473},"minimark",[450,453,457,481,484,487,579,583,590,797,803,807,810,816,985,994,1001,1005,1008,1045,1050,1070,2660,2826,2847,2850,2854,2857,2913,2916,2967,2970,2975,2979,3066,3070,3082,3085,3175,3185,3191,3200,3230,3234,3245,3249,3261,3315,3319,3330,3345,3351,3355,3367,3373,3377,3380,4224,4227,4290,4293,4296,4344,4356,4365,4369,4400,4406,4410,4413,4449,4456,4460,4472,4489,4869,4881,4884,4985,4989,5089,5092,5121,5124,5128,5132,5135,5186,5190,5193,5324,5328,5343,6239,6247,6251,6264,6428,6434,6438,6469],[451,452],"preview",{},[454,455,143],"h2",{"id":456},"overview",[458,459,460,461,465,466,469,470,475,476,480],"p",{},"Use this page to design a ",[462,463,464],"strong",{},"data connector extension"," for a new ",[462,467,468],{},"external data source"," (an API, a service, or a database). The connector exposes it as ",[471,472,474],"a",{"href":473},"\u002Fconcepts\u002Fdata-model#collections","collections"," in a Monospace ",[471,477,479],{"href":478},"\u002Fconcepts\u002Forganization#glossary","workspace",", and each item in a collection is one object from the external data source.",[458,482,483],{},"Work through the sections in order: probe the external data source, write a capability table, choose a read path for its query grammar, then handle paging, keys, writes, values and errors. You end with a declaration that offers only what the external data source can answer, and a read path that serves all of it.",[458,485,486],{},"Two connectors illustrate the patterns:",[488,489,490,506],"table",{},[491,492,493],"thead",{},[494,495,496,500,503],"tr",{},[497,498,499],"th",{},"Connector",[497,501,502],{},"Query grammar",[497,504,505],{},"Used in",[507,508,509,553],"tbody",{},[494,510,511,524,530],{},[512,513,514,515,519,520,523],"td",{},"Art Institute of Chicago (",[516,517,518],"code",{},"artworks",", ",[516,521,522],{},"artists",")",[512,525,526,529],{},[462,527,528],{},"Rich",": an Elasticsearch query body on one search endpoint",[512,531,532,533,537,538,519,541,544,545,548,549,552],{},"This page. The ",[471,534,536],{"href":535},"\u002Fguides\u002Fdata-connectors\u002Fquickstart","Data Connector Quickstart"," builds a trimmed version that declares only ",[516,539,540],{},"equals",[516,542,543],{},"in"," and ",[516,546,547],{},"and",", a sort on ",[516,550,551],{},"id"," only, and no count",[494,554,555,567,573],{},[512,556,557,558,519,561,519,564,523],{},"Stripe test mode (",[516,559,560],{},"customers",[516,562,563],{},"products",[516,565,566],{},"prices",[512,568,569,572],{},[462,570,571],{},"Weak",": list endpoints with a few exact parameters",[512,574,575],{},[471,576,578],{"href":577},"\u002Fguides\u002Fdata-connectors\u002Fstripe","Build a Stripe Data Connector",[454,580,582],{"id":581},"probe-the-external-data-source","Probe the External Data Source",[458,584,585,586,589],{},"An API's reference is wrong in both directions. Stripe's list endpoints accept an ",[516,587,588],{},"offset"," parameter its reference never mentions, and the Art Institute's search stops at 1,000 results, not the 10,000 its documentation states. Call every capability you might rely on, and record what comes back.",[488,591,592,605],{},[491,593,594],{},[494,595,596,599,602],{},[497,597,598],{},"Area",[497,600,601],{},"What to probe",[497,603,604],{},"Example finding",[507,606,607,618,629,644,661,680,709,724,739,754,765,780],{},[494,608,609,612,615],{},[512,610,611],{},"Metadata",[512,613,614],{},"How collections, fields, keys and references are described. Without a metadata API, the schema is static",[512,616,617],{},"The Stripe connector declares its schema statically, and maps each declared field to its place in a Stripe object",[494,619,620,623,626],{},[512,621,622],{},"Filters",[512,624,625],{},"Each parameter with a value that must match nothing. Items coming back mean the parameter is ignored",[512,627,628],{},"Stripe rejects unknown parameters with an error instead of ignoring them",[494,630,631,634,637],{},[512,632,633],{},"Exactness",[512,635,636],{},"Whether a filter matches exactly or returns a superset, and at which precision ranges compare",[512,638,639,640,643],{},"Stripe's ",[516,641,642],{},"email"," filter is exact and case-sensitive. The Art Institute's title keywords are lowercased, so a term query returns a superset of an exact match",[494,645,646,649,652],{},[512,647,648],{},"Unknown values",[512,650,651],{},"An unknown enum value or a reference to a missing object: empty result or error",[512,653,654,544,657,660],{},[516,655,656],{},"products?active=maybe",[516,658,659],{},"prices?product=prod_does_not_exist"," both fail on Stripe",[494,662,663,666,669],{},[512,664,665],{},"Nulls",[512,667,668],{},"How the external data source tests for a missing value, and whether its negations include nulls",[512,670,671,672,675,676,679],{},"Elasticsearch ",[516,673,674],{},"must_not"," includes documents where the field is missing, which SQL ",[516,677,678],{},"NOT"," excludes",[494,681,682,685,688],{},[512,683,684],{},"Sort",[512,686,687],{},"Which fields sort in which directions, and where nulls land",[512,689,690,691,694,695,544,697,700,701,704,705,708],{},"Stripe lists only newest ",[516,692,693],{},"created"," first. The Art Institute sorts ",[516,696,551],{},[516,698,699],{},"dateStart"," both ways, with nulls placed by ",[516,702,703],{},"missing",". Its ",[516,706,707],{},"title"," sort uses the lowercased keyword",[494,710,711,714,717],{},[512,712,713],{},"Paging",[512,715,716],{},"Offset, cursor or both; the largest page; any maximum depth",[512,718,719,720,723],{},"Stripe caps a page at 100 without an error. The Art Institute refuses ",[516,721,722],{},"size"," above 100 and any request past result 1,000",[494,725,726,729,732],{},[512,727,728],{},"Batches",[512,730,731],{},"Whether a lookup by several ids keeps order and reports missing ids",[512,733,734,735,738],{},"The Art Institute's ",[516,736,737],{},"ids"," parameter reorders results and drops missing ids without notice",[494,740,741,744,747],{},[512,742,743],{},"Counts",[512,745,746],{},"Whether the external data source returns an exact total",[512,748,749,750,753],{},"The Art Institute returns ",[516,751,752],{},"pagination.total",". Stripe's list endpoints return no total",[494,755,756,759,762],{},[512,757,758],{},"Writes",[512,760,761],{},"What can be created, updated and deleted, what each call returns, and what can never be deleted",[512,763,764],{},"Stripe answers a delete with a tombstone, can't delete prices, and refuses to delete a product that has prices",[494,766,767,770,773],{},[512,768,769],{},"Limits",[512,771,772],{},"Rate limits, what a test run costs against them, and whether you can pin an API version",[512,774,775,776,779],{},"Stripe pins versions with ",[516,777,778],{},"Stripe-Version",". The Art Institute's documentation states 60 requests per minute per IP without a key",[494,781,782,784,787],{},[512,783,148],{},[512,785,786],{},"What a bad credential, a missing object and invalid input return",[512,788,789,790,793,794],{},"Stripe answers a wrong key with ",[516,791,792],{},"401"," and a missing object with ",[516,795,796],{},"resource_missing",[458,798,799,800,802],{},"Without credentials, read the vendor's OpenAPI or GraphQL schema and probe the unauthenticated endpoints, including the ",[516,801,792],{}," path.",[454,804,806],{"id":805},"write-a-capability-table","Write a Capability Table",[458,808,809],{},"List every filter operator, sort, paging argument and count you might offer callers, and name the request to the external data source that answers each one. If nothing answers it, don't offer it.",[458,811,812,813,815],{},"This excerpt is the Art Institute connector's table for ",[516,814,518],{},":",[488,817,818,831],{},[491,819,820],{},[494,821,822,825,828],{},[497,823,824],{},"Offered",[497,826,827],{},"Request to the Art Institute API",[497,829,830],{},"Evidence",[507,832,833,861,884,916,942,968],{},[494,834,835,844,856],{},[512,836,837,839,840,519,842],{},[516,838,551],{},": ",[516,841,540],{},[516,843,543],{},[512,845,846,849,850,853,854],{},[516,847,848],{},"term"," \u002F ",[516,851,852],{},"terms"," on ",[516,855,551],{},[512,857,858,860],{},[516,859,852],{}," on 3 ids, one missing, returns exactly the 2 that exist",[494,862,863,872,881],{},[512,864,865,839,868,519,870],{},[516,866,867],{},"artistId",[516,869,540],{},[516,871,543],{},[512,873,874,849,876,853,878],{},[516,875,848],{},[516,877,852],{},[516,879,880],{},"artist_id",[512,882,883],{},"Count matches the Art Institute API's own count for the same artist",[494,885,886,895,907],{},[512,887,888,839,890,519,892,894],{},[516,889,699],{},[516,891,540],{},[516,893,543],{},", ranges",[512,896,897,849,899,849,901,853,904],{},[516,898,848],{},[516,900,852],{},[516,902,903],{},"range",[516,905,906],{},"date_start",[512,908,909,544,912,915],{},[516,910,911],{},"gt 1884",[516,913,914],{},"gte 1885"," return the same total on this integer field",[494,917,918,925,932],{},[512,919,920,839,923],{},[516,921,922],{},"isPublicDomain",[516,924,540],{},[512,926,927,853,929],{},[516,928,848],{},[516,930,931],{},"is_public_domain",[512,933,934,937,938,941],{},[516,935,936],{},"true"," plus ",[516,939,940],{},"false"," equals the unfiltered total",[494,943,944,952,961],{},[512,945,946,947,519,949,951],{},"sort ",[516,948,551],{},[516,950,699],{},", both directions",[512,953,954,957,958,960],{},[516,955,956],{},"sort"," with ",[516,959,703],{}," for nulls",[512,962,963,964,967],{},"Nulls first or last as asked. Without ",[516,965,966],{},"nulls",", last in both directions, where the search API puts them by default",[494,969,970,975,981],{},[512,971,972],{},[516,973,974],{},"meta.totalCount",[512,976,977,978],{},"the same query with ",[516,979,980],{},"size: 0",[512,982,983],{},[516,984,752],{},[458,986,987,988,990,991,993],{},"Fields the Art Institute API can't match exactly stay out of the table. The Art Institute connector declares no operator on ",[516,989,707],{},", because its keyword matching is case-insensitive and ",[516,992,540],{}," would return a superset.",[458,995,996,997,1000],{},"Answering a filter by fetching everything and filtering in memory turns one Studio click into a scan of the whole external data source. Leave it out of the declaration instead. The declaration rules, and which operators each field type allows, are in ",[471,998,414],{"href":999},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Foperations",".",[454,1002,1004],{"id":1003},"match-the-read-path-to-the-grammar","Match the Read Path to the Grammar",[458,1006,1007],{},"The external data source's query grammar decides the shape of your read path.",[488,1009,1010,1023],{},[491,1011,1012],{},[494,1013,1014,1017,1020],{},[497,1015,1016],{},"Grammar",[497,1018,1019],{},"The external data source accepts",[497,1021,1022],{},"Your connector",[507,1024,1025,1035],{},[494,1026,1027,1029,1032],{},[512,1028,528],{},[512,1030,1031],{},"A filter language of its own (e.g., Elasticsearch, GraphQL filters, a query DSL)",[512,1033,1034],{},"Translates the filter tree structurally into one query per read",[494,1036,1037,1039,1042],{},[512,1038,571],{},[512,1040,1041],{},"A few list parameters with fixed meanings",[512,1043,1044],{},"Plans the filter into a union of exact requests to the external data source, or refuses it",[1046,1047,1049],"h3",{"id":1048},"translate-a-rich-grammar","Translate a Rich Grammar",[458,1051,1052,1053,1056,1057,1059,1060,1063,1064,1066,1067,1069],{},"The Art Institute connector maps each filter node to an Elasticsearch clause. Every read sends the translated query, in pages of up to 100 items, after a count request when the read reaches past result 1,000. The snippets on this page are excerpts from the full example sources. A file's first excerpt starts with its imports, and later excerpts of the same file continue it; ",[516,1054,1055],{},"\u002F\u002F …"," marks the lines an excerpt leaves out. The ",[471,1058,536],{"href":535}," builds a trimmed Art Institute connector: it declares no ranges, ",[516,1061,1062],{},"or",", or counts, sorts only on ",[516,1065,551],{},", and leaves out the declaration and value checks. ",[471,1068,578],{"href":577}," walks through the Stripe connector:",[1071,1072,1073],"accordion",{},[1074,1075,1077],"accordion-item",{"label":1076},"src\u002Fdata-connectors\u002Fartic\u002Fsearch.ts (toQuery, requireDeclared)",[1078,1079,1085],"pre",{"className":1080,"code":1081,"filename":1082,"language":1083,"meta":1084,"style":1084},"language-ts shiki shiki-themes monospace-light monospace-light monospace-dark","import type {\n    DeclaredOperator,\n    FilterContract,\n    FilterSerialized,\n    LogicalOperator,\n    MonospaceObject,\n    PrimitiveFieldType,\n    QueryResultFor,\n    QueryResultSerialized,\n    ReadArguments,\n    ReadManyOperationSerialized,\n    ReadOneOperationSerialized,\n    SortItemSerialized,\n    Value,\n} from '@monospace\u002Fextension-kit\u002Fdata-connector';\nimport { ConnectionFailed, QueryRejected, RateLimited } from '@monospace\u002Fextension-kit\u002Fdata-connector';\nimport { schema } from '.\u002Fschema';\n\u002F\u002F …\n\u002F**\n * Translates a filter into an Elasticsearch query. A field or operator `contract` doesn't declare,\n * anywhere in the filter, is refused with a plain error, never dropped: it can be a permission rule.\n * A shape the API can't answer, such as an `or` of permission rules on a collection that doesn't\n * declare `or`, is a `QueryRejected`.\n *\u002F\nfunction toQuery(filter: FilterSerialized, contract: FilterContract | undefined): SearchBody {\n    requireDeclared(filter, contract);\n    return translate(filter, contract);\n}\n\nfunction translate(filter: FilterSerialized, contract: FilterContract | undefined): SearchBody {\n    if (filter.and !== undefined) {\n        requireLogical(contract, 'and');\n        return { bool: { filter: filter.and.map((child) => translate(child, contract)) } };\n    }\n\n    if (filter.or !== undefined) {\n        requireLogical(contract, 'or');\n        return { bool: { should: filter.or.map((child) => translate(child, contract)), minimum_should_match: 1 } };\n    }\n\n    if (filter.not !== undefined) {\n        \u002F\u002F Nothing declares `not`, so Engine offers no negation, and no `_null`, which needs it.\n        throw new QueryRejected('The Art Institute connector can\\'t negate a filter.');\n    }\n\n    if (filter.in !== undefined) {\n        if (filter.in.field === undefined || !Array.isArray(filter.values)) throw new QueryRejected('`in` needs a field and a list of values.');\n        \u002F\u002F The search API refuses `null` in `terms`, and `terms` never matches a missing value. So null\n        \u002F\u002F members are left out, and `in` never matches an item without a value.\n        return { terms: { [apiFieldName(filter.in.field)]: filter.values.filter((value) => value !== null) } };\n    }\n\n    const { field } = filter.left;\n    const { value } = filter.right;\n\n    if (field === undefined || value === undefined) {\n        throw new QueryRejected('The Art Institute connector needs a field on the left of a comparison and a value on the right.');\n    }\n\n    const name = apiFieldName(field);\n\n    if (value === null) {\n        \u002F\u002F `field = null` means \"is null\": the search API's `exists` is false for a field without a value.\n        if (filter.cmp === 'equals') return { bool: { must_not: { exists: { field: name } } } };\n        \u002F\u002F Any other comparison with null is unknown in SQL, so it matches nothing.\n        return { match_none: {} };\n    }\n\n    switch (filter.cmp) {\n        case 'equals': return { term: { [name]: value } };\n        case 'lessThan': return { range: { [name]: { lt: value } } };\n        case 'lessThanOrEquals': return { range: { [name]: { lte: value } } };\n        case 'greaterThan': return { range: { [name]: { gt: value } } };\n        case 'greaterThanOrEquals': return { range: { [name]: { gte: value } } };\n        default: throw new QueryRejected(`The Art Institute connector can't translate \\`${filter.cmp}\\`.`);\n    }\n}\n\u002F\u002F …\n\u002F** Checks every comparison in the filter, however deeply nested, before any of it is translated. *\u002F\nfunction requireDeclared(filter: FilterSerialized, contract: FilterContract | undefined): void {\n    if (filter.cmp !== undefined) requireOperator(contract, filter.left.field, filter.cmp);\n    if (filter.in !== undefined) requireOperator(contract, filter.in.field, 'in');\n    for (const child of filter.and ?? filter.or ?? (filter.not === undefined ? [] : [filter.not])) requireDeclared(child, contract);\n}\n\n\u002F** Refuses `operator` on `field` unless `contract` declares it. A value in the field's place is a shape `translate` refuses. *\u002F\nfunction requireOperator(contract: FilterContract | undefined, field: string | undefined, operator: DeclaredOperator): void {\n    if (field === undefined) return;\n    const operators = contract?.fields[field]?.operators;\n\n    if (operators === undefined || !Object.hasOwn(operators, operator)) {\n        throw new Error(`The Art Institute connector can't filter \\`${field}\\` with \\`${operator}\\`.`);\n    }\n}\n","src\u002Fdata-connectors\u002Fartic\u002Fsearch.ts","ts","",[516,1086,1087,1103,1113,1121,1129,1137,1145,1153,1161,1169,1177,1185,1193,1201,1209,1225,1255,1274,1281,1287,1293,1299,1305,1311,1317,1360,1379,1398,1404,1411,1442,1465,1482,1527,1533,1538,1557,1573,1618,1623,1628,1648,1654,1679,1684,1689,1708,1772,1778,1784,1840,1845,1850,1875,1897,1902,1925,1941,1946,1951,1971,1976,1991,1997,2030,2036,2044,2049,2054,2070,2094,2119,2142,2165,2188,2229,2234,2239,2244,2250,2283,2331,2375,2454,2459,2464,2470,2516,2535,2567,2572,2609,2650,2655],{"__ignoreMap":1084},[1088,1089,1092,1096,1099],"span",{"class":1090,"line":1091},"line",1,[1088,1093,1095],{"class":1094},"sLVBU","import",[1088,1097,1098],{"class":1094}," type",[1088,1100,1102],{"class":1101},"sTMul"," {\n",[1088,1104,1106,1110],{"class":1090,"line":1105},2,[1088,1107,1109],{"class":1108},"sNJ9c","    DeclaredOperator",[1088,1111,1112],{"class":1101},",\n",[1088,1114,1116,1119],{"class":1090,"line":1115},3,[1088,1117,1118],{"class":1108},"    FilterContract",[1088,1120,1112],{"class":1101},[1088,1122,1124,1127],{"class":1090,"line":1123},4,[1088,1125,1126],{"class":1108},"    FilterSerialized",[1088,1128,1112],{"class":1101},[1088,1130,1132,1135],{"class":1090,"line":1131},5,[1088,1133,1134],{"class":1108},"    LogicalOperator",[1088,1136,1112],{"class":1101},[1088,1138,1140,1143],{"class":1090,"line":1139},6,[1088,1141,1142],{"class":1108},"    MonospaceObject",[1088,1144,1112],{"class":1101},[1088,1146,1148,1151],{"class":1090,"line":1147},7,[1088,1149,1150],{"class":1108},"    PrimitiveFieldType",[1088,1152,1112],{"class":1101},[1088,1154,1156,1159],{"class":1090,"line":1155},8,[1088,1157,1158],{"class":1108},"    QueryResultFor",[1088,1160,1112],{"class":1101},[1088,1162,1164,1167],{"class":1090,"line":1163},9,[1088,1165,1166],{"class":1108},"    QueryResultSerialized",[1088,1168,1112],{"class":1101},[1088,1170,1172,1175],{"class":1090,"line":1171},10,[1088,1173,1174],{"class":1108},"    ReadArguments",[1088,1176,1112],{"class":1101},[1088,1178,1180,1183],{"class":1090,"line":1179},11,[1088,1181,1182],{"class":1108},"    ReadManyOperationSerialized",[1088,1184,1112],{"class":1101},[1088,1186,1188,1191],{"class":1090,"line":1187},12,[1088,1189,1190],{"class":1108},"    ReadOneOperationSerialized",[1088,1192,1112],{"class":1101},[1088,1194,1196,1199],{"class":1090,"line":1195},13,[1088,1197,1198],{"class":1108},"    SortItemSerialized",[1088,1200,1112],{"class":1101},[1088,1202,1204,1207],{"class":1090,"line":1203},14,[1088,1205,1206],{"class":1108},"    Value",[1088,1208,1112],{"class":1101},[1088,1210,1212,1215,1218,1222],{"class":1090,"line":1211},15,[1088,1213,1214],{"class":1101},"} ",[1088,1216,1217],{"class":1094},"from",[1088,1219,1221],{"class":1220},"suKVh"," '@monospace\u002Fextension-kit\u002Fdata-connector'",[1088,1223,1224],{"class":1101},";\n",[1088,1226,1228,1230,1233,1236,1238,1241,1243,1246,1249,1251,1253],{"class":1090,"line":1227},16,[1088,1229,1095],{"class":1094},[1088,1231,1232],{"class":1101}," { ",[1088,1234,1235],{"class":1108},"ConnectionFailed",[1088,1237,519],{"class":1101},[1088,1239,1240],{"class":1108},"QueryRejected",[1088,1242,519],{"class":1101},[1088,1244,1245],{"class":1108},"RateLimited",[1088,1247,1248],{"class":1101}," } ",[1088,1250,1217],{"class":1094},[1088,1252,1221],{"class":1220},[1088,1254,1224],{"class":1101},[1088,1256,1258,1260,1262,1265,1267,1269,1272],{"class":1090,"line":1257},17,[1088,1259,1095],{"class":1094},[1088,1261,1232],{"class":1101},[1088,1263,1264],{"class":1108},"schema",[1088,1266,1248],{"class":1101},[1088,1268,1217],{"class":1094},[1088,1270,1271],{"class":1220}," '.\u002Fschema'",[1088,1273,1224],{"class":1101},[1088,1275,1277],{"class":1090,"line":1276},18,[1088,1278,1280],{"class":1279},"seNfP","\u002F\u002F …\n",[1088,1282,1284],{"class":1090,"line":1283},19,[1088,1285,1286],{"class":1279},"\u002F**\n",[1088,1288,1290],{"class":1090,"line":1289},20,[1088,1291,1292],{"class":1279}," * Translates a filter into an Elasticsearch query. A field or operator `contract` doesn't declare,\n",[1088,1294,1296],{"class":1090,"line":1295},21,[1088,1297,1298],{"class":1279}," * anywhere in the filter, is refused with a plain error, never dropped: it can be a permission rule.\n",[1088,1300,1302],{"class":1090,"line":1301},22,[1088,1303,1304],{"class":1279}," * A shape the API can't answer, such as an `or` of permission rules on a collection that doesn't\n",[1088,1306,1308],{"class":1090,"line":1307},23,[1088,1309,1310],{"class":1279}," * declare `or`, is a `QueryRejected`.\n",[1088,1312,1314],{"class":1090,"line":1313},24,[1088,1315,1316],{"class":1279}," *\u002F\n",[1088,1318,1320,1323,1327,1330,1333,1336,1338,1341,1344,1347,1351,1353,1355,1358],{"class":1090,"line":1319},25,[1088,1321,1322],{"class":1094},"function",[1088,1324,1326],{"class":1325},"ssBNi"," toQuery",[1088,1328,1329],{"class":1101},"(",[1088,1331,1332],{"class":1094},"filter:",[1088,1334,1335],{"class":1325}," FilterSerialized",[1088,1337,519],{"class":1101},[1088,1339,1340],{"class":1094},"contract:",[1088,1342,1343],{"class":1325}," FilterContract",[1088,1345,1346],{"class":1094}," |",[1088,1348,1350],{"class":1349},"snHjA"," undefined",[1088,1352,523],{"class":1101},[1088,1354,815],{"class":1094},[1088,1356,1357],{"class":1325}," SearchBody",[1088,1359,1102],{"class":1101},[1088,1361,1363,1366,1368,1371,1373,1376],{"class":1090,"line":1362},26,[1088,1364,1365],{"class":1325},"    requireDeclared",[1088,1367,1329],{"class":1101},[1088,1369,1370],{"class":1108},"filter",[1088,1372,519],{"class":1101},[1088,1374,1375],{"class":1108},"contract",[1088,1377,1378],{"class":1101},");\n",[1088,1380,1382,1385,1388,1390,1392,1394,1396],{"class":1090,"line":1381},27,[1088,1383,1384],{"class":1094},"    return",[1088,1386,1387],{"class":1325}," translate",[1088,1389,1329],{"class":1101},[1088,1391,1370],{"class":1108},[1088,1393,519],{"class":1101},[1088,1395,1375],{"class":1108},[1088,1397,1378],{"class":1101},[1088,1399,1401],{"class":1090,"line":1400},28,[1088,1402,1403],{"class":1101},"}\n",[1088,1405,1407],{"class":1090,"line":1406},29,[1088,1408,1410],{"emptyLinePlaceholder":1409},true,"\n",[1088,1412,1414,1416,1418,1420,1422,1424,1426,1428,1430,1432,1434,1436,1438,1440],{"class":1090,"line":1413},30,[1088,1415,1322],{"class":1094},[1088,1417,1387],{"class":1325},[1088,1419,1329],{"class":1101},[1088,1421,1332],{"class":1094},[1088,1423,1335],{"class":1325},[1088,1425,519],{"class":1101},[1088,1427,1340],{"class":1094},[1088,1429,1343],{"class":1325},[1088,1431,1346],{"class":1094},[1088,1433,1350],{"class":1349},[1088,1435,523],{"class":1101},[1088,1437,815],{"class":1094},[1088,1439,1357],{"class":1325},[1088,1441,1102],{"class":1101},[1088,1443,1445,1448,1451,1453,1455,1457,1460,1462],{"class":1090,"line":1444},31,[1088,1446,1447],{"class":1094},"    if",[1088,1449,1450],{"class":1101}," (",[1088,1452,1370],{"class":1108},[1088,1454,1000],{"class":1101},[1088,1456,547],{"class":1108},[1088,1458,1459],{"class":1094}," !==",[1088,1461,1350],{"class":1325},[1088,1463,1464],{"class":1101},") {\n",[1088,1466,1468,1471,1473,1475,1477,1480],{"class":1090,"line":1467},32,[1088,1469,1470],{"class":1325},"        requireLogical",[1088,1472,1329],{"class":1101},[1088,1474,1375],{"class":1108},[1088,1476,519],{"class":1101},[1088,1478,1479],{"class":1220},"'and'",[1088,1481,1378],{"class":1101},[1088,1483,1485,1488,1491,1493,1495,1497,1499,1502,1505,1508,1511,1514,1516,1518,1520,1522,1524],{"class":1090,"line":1484},33,[1088,1486,1487],{"class":1094},"        return",[1088,1489,1490],{"class":1101}," { bool: { filter: ",[1088,1492,1370],{"class":1108},[1088,1494,1000],{"class":1101},[1088,1496,547],{"class":1108},[1088,1498,1000],{"class":1101},[1088,1500,1501],{"class":1325},"map",[1088,1503,1504],{"class":1101},"((",[1088,1506,1507],{"class":1094},"child",[1088,1509,1510],{"class":1101},") ",[1088,1512,1513],{"class":1094},"=>",[1088,1515,1387],{"class":1325},[1088,1517,1329],{"class":1101},[1088,1519,1507],{"class":1108},[1088,1521,519],{"class":1101},[1088,1523,1375],{"class":1108},[1088,1525,1526],{"class":1101},")) } };\n",[1088,1528,1530],{"class":1090,"line":1529},34,[1088,1531,1532],{"class":1101},"    }\n",[1088,1534,1536],{"class":1090,"line":1535},35,[1088,1537,1410],{"emptyLinePlaceholder":1409},[1088,1539,1541,1543,1545,1547,1549,1551,1553,1555],{"class":1090,"line":1540},36,[1088,1542,1447],{"class":1094},[1088,1544,1450],{"class":1101},[1088,1546,1370],{"class":1108},[1088,1548,1000],{"class":1101},[1088,1550,1062],{"class":1108},[1088,1552,1459],{"class":1094},[1088,1554,1350],{"class":1325},[1088,1556,1464],{"class":1101},[1088,1558,1560,1562,1564,1566,1568,1571],{"class":1090,"line":1559},37,[1088,1561,1470],{"class":1325},[1088,1563,1329],{"class":1101},[1088,1565,1375],{"class":1108},[1088,1567,519],{"class":1101},[1088,1569,1570],{"class":1220},"'or'",[1088,1572,1378],{"class":1101},[1088,1574,1576,1578,1581,1583,1585,1587,1589,1591,1593,1595,1597,1599,1601,1603,1605,1607,1609,1612,1615],{"class":1090,"line":1575},38,[1088,1577,1487],{"class":1094},[1088,1579,1580],{"class":1101}," { bool: { should: ",[1088,1582,1370],{"class":1108},[1088,1584,1000],{"class":1101},[1088,1586,1062],{"class":1108},[1088,1588,1000],{"class":1101},[1088,1590,1501],{"class":1325},[1088,1592,1504],{"class":1101},[1088,1594,1507],{"class":1094},[1088,1596,1510],{"class":1101},[1088,1598,1513],{"class":1094},[1088,1600,1387],{"class":1325},[1088,1602,1329],{"class":1101},[1088,1604,1507],{"class":1108},[1088,1606,519],{"class":1101},[1088,1608,1375],{"class":1108},[1088,1610,1611],{"class":1101},")), minimum_should_match: ",[1088,1613,1614],{"class":1325},"1",[1088,1616,1617],{"class":1101}," } };\n",[1088,1619,1621],{"class":1090,"line":1620},39,[1088,1622,1532],{"class":1101},[1088,1624,1626],{"class":1090,"line":1625},40,[1088,1627,1410],{"emptyLinePlaceholder":1409},[1088,1629,1631,1633,1635,1637,1639,1642,1644,1646],{"class":1090,"line":1630},41,[1088,1632,1447],{"class":1094},[1088,1634,1450],{"class":1101},[1088,1636,1370],{"class":1108},[1088,1638,1000],{"class":1101},[1088,1640,1641],{"class":1108},"not",[1088,1643,1459],{"class":1094},[1088,1645,1350],{"class":1325},[1088,1647,1464],{"class":1101},[1088,1649,1651],{"class":1090,"line":1650},42,[1088,1652,1653],{"class":1279},"        \u002F\u002F Nothing declares `not`, so Engine offers no negation, and no `_null`, which needs it.\n",[1088,1655,1657,1660,1663,1666,1668,1671,1674,1677],{"class":1090,"line":1656},43,[1088,1658,1659],{"class":1094},"        throw",[1088,1661,1662],{"class":1094}," new",[1088,1664,1665],{"class":1325}," QueryRejected",[1088,1667,1329],{"class":1101},[1088,1669,1670],{"class":1220},"'The Art Institute connector can",[1088,1672,1673],{"class":1325},"\\'",[1088,1675,1676],{"class":1220},"t negate a filter.'",[1088,1678,1378],{"class":1101},[1088,1680,1682],{"class":1090,"line":1681},44,[1088,1683,1532],{"class":1101},[1088,1685,1687],{"class":1090,"line":1686},45,[1088,1688,1410],{"emptyLinePlaceholder":1409},[1088,1690,1692,1694,1696,1698,1700,1702,1704,1706],{"class":1090,"line":1691},46,[1088,1693,1447],{"class":1094},[1088,1695,1450],{"class":1101},[1088,1697,1370],{"class":1108},[1088,1699,1000],{"class":1101},[1088,1701,543],{"class":1108},[1088,1703,1459],{"class":1094},[1088,1705,1350],{"class":1325},[1088,1707,1464],{"class":1101},[1088,1709,1711,1714,1716,1718,1720,1722,1724,1727,1730,1732,1735,1738,1741,1743,1746,1748,1750,1752,1755,1758,1761,1763,1765,1767,1770],{"class":1090,"line":1710},47,[1088,1712,1713],{"class":1094},"        if",[1088,1715,1450],{"class":1101},[1088,1717,1370],{"class":1108},[1088,1719,1000],{"class":1101},[1088,1721,543],{"class":1108},[1088,1723,1000],{"class":1101},[1088,1725,1726],{"class":1108},"field",[1088,1728,1729],{"class":1094}," ===",[1088,1731,1350],{"class":1325},[1088,1733,1734],{"class":1094}," ||",[1088,1736,1737],{"class":1094}," !",[1088,1739,1740],{"class":1108},"Array",[1088,1742,1000],{"class":1101},[1088,1744,1745],{"class":1325},"isArray",[1088,1747,1329],{"class":1101},[1088,1749,1370],{"class":1108},[1088,1751,1000],{"class":1101},[1088,1753,1754],{"class":1108},"values",[1088,1756,1757],{"class":1101},")) ",[1088,1759,1760],{"class":1094},"throw",[1088,1762,1662],{"class":1094},[1088,1764,1665],{"class":1325},[1088,1766,1329],{"class":1101},[1088,1768,1769],{"class":1220},"'`in` needs a field and a list of values.'",[1088,1771,1378],{"class":1101},[1088,1773,1775],{"class":1090,"line":1774},48,[1088,1776,1777],{"class":1279},"        \u002F\u002F The search API refuses `null` in `terms`, and `terms` never matches a missing value. So null\n",[1088,1779,1781],{"class":1090,"line":1780},49,[1088,1782,1783],{"class":1279},"        \u002F\u002F members are left out, and `in` never matches an item without a value.\n",[1088,1785,1787,1789,1792,1795,1797,1799,1801,1803,1805,1807,1810,1812,1814,1816,1818,1820,1822,1825,1827,1829,1832,1834,1837],{"class":1090,"line":1786},50,[1088,1788,1487],{"class":1094},[1088,1790,1791],{"class":1101}," { terms: { [",[1088,1793,1794],{"class":1325},"apiFieldName",[1088,1796,1329],{"class":1101},[1088,1798,1370],{"class":1108},[1088,1800,1000],{"class":1101},[1088,1802,543],{"class":1108},[1088,1804,1000],{"class":1101},[1088,1806,1726],{"class":1108},[1088,1808,1809],{"class":1101},")]: ",[1088,1811,1370],{"class":1108},[1088,1813,1000],{"class":1101},[1088,1815,1754],{"class":1108},[1088,1817,1000],{"class":1101},[1088,1819,1370],{"class":1325},[1088,1821,1504],{"class":1101},[1088,1823,1824],{"class":1094},"value",[1088,1826,1510],{"class":1101},[1088,1828,1513],{"class":1094},[1088,1830,1831],{"class":1108}," value",[1088,1833,1459],{"class":1094},[1088,1835,1836],{"class":1325}," null",[1088,1838,1839],{"class":1101},") } };\n",[1088,1841,1843],{"class":1090,"line":1842},51,[1088,1844,1532],{"class":1101},[1088,1846,1848],{"class":1090,"line":1847},52,[1088,1849,1410],{"emptyLinePlaceholder":1409},[1088,1851,1853,1856,1858,1860,1862,1865,1868,1870,1873],{"class":1090,"line":1852},53,[1088,1854,1855],{"class":1094},"    const",[1088,1857,1232],{"class":1101},[1088,1859,1726],{"class":1325},[1088,1861,1248],{"class":1101},[1088,1863,1864],{"class":1094},"=",[1088,1866,1867],{"class":1108}," filter",[1088,1869,1000],{"class":1101},[1088,1871,1872],{"class":1108},"left",[1088,1874,1224],{"class":1101},[1088,1876,1878,1880,1882,1884,1886,1888,1890,1892,1895],{"class":1090,"line":1877},54,[1088,1879,1855],{"class":1094},[1088,1881,1232],{"class":1101},[1088,1883,1824],{"class":1325},[1088,1885,1248],{"class":1101},[1088,1887,1864],{"class":1094},[1088,1889,1867],{"class":1108},[1088,1891,1000],{"class":1101},[1088,1893,1894],{"class":1108},"right",[1088,1896,1224],{"class":1101},[1088,1898,1900],{"class":1090,"line":1899},55,[1088,1901,1410],{"emptyLinePlaceholder":1409},[1088,1903,1905,1907,1909,1911,1913,1915,1917,1919,1921,1923],{"class":1090,"line":1904},56,[1088,1906,1447],{"class":1094},[1088,1908,1450],{"class":1101},[1088,1910,1726],{"class":1108},[1088,1912,1729],{"class":1094},[1088,1914,1350],{"class":1325},[1088,1916,1734],{"class":1094},[1088,1918,1831],{"class":1108},[1088,1920,1729],{"class":1094},[1088,1922,1350],{"class":1325},[1088,1924,1464],{"class":1101},[1088,1926,1928,1930,1932,1934,1936,1939],{"class":1090,"line":1927},57,[1088,1929,1659],{"class":1094},[1088,1931,1662],{"class":1094},[1088,1933,1665],{"class":1325},[1088,1935,1329],{"class":1101},[1088,1937,1938],{"class":1220},"'The Art Institute connector needs a field on the left of a comparison and a value on the right.'",[1088,1940,1378],{"class":1101},[1088,1942,1944],{"class":1090,"line":1943},58,[1088,1945,1532],{"class":1101},[1088,1947,1949],{"class":1090,"line":1948},59,[1088,1950,1410],{"emptyLinePlaceholder":1409},[1088,1952,1954,1956,1959,1962,1965,1967,1969],{"class":1090,"line":1953},60,[1088,1955,1855],{"class":1094},[1088,1957,1958],{"class":1325}," name",[1088,1960,1961],{"class":1094}," =",[1088,1963,1964],{"class":1325}," apiFieldName",[1088,1966,1329],{"class":1101},[1088,1968,1726],{"class":1108},[1088,1970,1378],{"class":1101},[1088,1972,1974],{"class":1090,"line":1973},61,[1088,1975,1410],{"emptyLinePlaceholder":1409},[1088,1977,1979,1981,1983,1985,1987,1989],{"class":1090,"line":1978},62,[1088,1980,1447],{"class":1094},[1088,1982,1450],{"class":1101},[1088,1984,1824],{"class":1108},[1088,1986,1729],{"class":1094},[1088,1988,1836],{"class":1325},[1088,1990,1464],{"class":1101},[1088,1992,1994],{"class":1090,"line":1993},63,[1088,1995,1996],{"class":1279},"        \u002F\u002F `field = null` means \"is null\": the search API's `exists` is false for a field without a value.\n",[1088,1998,2000,2002,2004,2006,2008,2011,2013,2016,2018,2021,2024,2027],{"class":1090,"line":1999},64,[1088,2001,1713],{"class":1094},[1088,2003,1450],{"class":1101},[1088,2005,1370],{"class":1108},[1088,2007,1000],{"class":1101},[1088,2009,2010],{"class":1108},"cmp",[1088,2012,1729],{"class":1094},[1088,2014,2015],{"class":1220}," 'equals'",[1088,2017,1510],{"class":1101},[1088,2019,2020],{"class":1094},"return",[1088,2022,2023],{"class":1101}," { bool: { must_not: { exists: { field: ",[1088,2025,2026],{"class":1108},"name",[1088,2028,2029],{"class":1101}," } } } };\n",[1088,2031,2033],{"class":1090,"line":2032},65,[1088,2034,2035],{"class":1279},"        \u002F\u002F Any other comparison with null is unknown in SQL, so it matches nothing.\n",[1088,2037,2039,2041],{"class":1090,"line":2038},66,[1088,2040,1487],{"class":1094},[1088,2042,2043],{"class":1101}," { match_none: {} };\n",[1088,2045,2047],{"class":1090,"line":2046},67,[1088,2048,1532],{"class":1101},[1088,2050,2052],{"class":1090,"line":2051},68,[1088,2053,1410],{"emptyLinePlaceholder":1409},[1088,2055,2057,2060,2062,2064,2066,2068],{"class":1090,"line":2056},69,[1088,2058,2059],{"class":1094},"    switch",[1088,2061,1450],{"class":1101},[1088,2063,1370],{"class":1108},[1088,2065,1000],{"class":1101},[1088,2067,2010],{"class":1108},[1088,2069,1464],{"class":1101},[1088,2071,2073,2076,2078,2080,2082,2085,2087,2090,2092],{"class":1090,"line":2072},70,[1088,2074,2075],{"class":1094},"        case",[1088,2077,2015],{"class":1220},[1088,2079,839],{"class":1101},[1088,2081,2020],{"class":1094},[1088,2083,2084],{"class":1101}," { term: { [",[1088,2086,2026],{"class":1108},[1088,2088,2089],{"class":1101},"]: ",[1088,2091,1824],{"class":1108},[1088,2093,1617],{"class":1101},[1088,2095,2097,2099,2102,2104,2106,2109,2111,2114,2116],{"class":1090,"line":2096},71,[1088,2098,2075],{"class":1094},[1088,2100,2101],{"class":1220}," 'lessThan'",[1088,2103,839],{"class":1101},[1088,2105,2020],{"class":1094},[1088,2107,2108],{"class":1101}," { range: { [",[1088,2110,2026],{"class":1108},[1088,2112,2113],{"class":1101},"]: { lt: ",[1088,2115,1824],{"class":1108},[1088,2117,2118],{"class":1101}," } } };\n",[1088,2120,2122,2124,2127,2129,2131,2133,2135,2138,2140],{"class":1090,"line":2121},72,[1088,2123,2075],{"class":1094},[1088,2125,2126],{"class":1220}," 'lessThanOrEquals'",[1088,2128,839],{"class":1101},[1088,2130,2020],{"class":1094},[1088,2132,2108],{"class":1101},[1088,2134,2026],{"class":1108},[1088,2136,2137],{"class":1101},"]: { lte: ",[1088,2139,1824],{"class":1108},[1088,2141,2118],{"class":1101},[1088,2143,2145,2147,2150,2152,2154,2156,2158,2161,2163],{"class":1090,"line":2144},73,[1088,2146,2075],{"class":1094},[1088,2148,2149],{"class":1220}," 'greaterThan'",[1088,2151,839],{"class":1101},[1088,2153,2020],{"class":1094},[1088,2155,2108],{"class":1101},[1088,2157,2026],{"class":1108},[1088,2159,2160],{"class":1101},"]: { gt: ",[1088,2162,1824],{"class":1108},[1088,2164,2118],{"class":1101},[1088,2166,2168,2170,2173,2175,2177,2179,2181,2184,2186],{"class":1090,"line":2167},74,[1088,2169,2075],{"class":1094},[1088,2171,2172],{"class":1220}," 'greaterThanOrEquals'",[1088,2174,839],{"class":1101},[1088,2176,2020],{"class":1094},[1088,2178,2108],{"class":1101},[1088,2180,2026],{"class":1108},[1088,2182,2183],{"class":1101},"]: { gte: ",[1088,2185,1824],{"class":1108},[1088,2187,2118],{"class":1101},[1088,2189,2191,2194,2196,2198,2200,2202,2204,2207,2210,2213,2215,2217,2219,2222,2224,2227],{"class":1090,"line":2190},75,[1088,2192,2193],{"class":1094},"        default",[1088,2195,839],{"class":1101},[1088,2197,1760],{"class":1094},[1088,2199,1662],{"class":1094},[1088,2201,1665],{"class":1325},[1088,2203,1329],{"class":1101},[1088,2205,2206],{"class":1220},"`The Art Institute connector can't translate ",[1088,2208,2209],{"class":1325},"\\`",[1088,2211,2212],{"class":1220},"${",[1088,2214,1370],{"class":1108},[1088,2216,1000],{"class":1220},[1088,2218,2010],{"class":1108},[1088,2220,2221],{"class":1220},"}",[1088,2223,2209],{"class":1325},[1088,2225,2226],{"class":1220},".`",[1088,2228,1378],{"class":1101},[1088,2230,2232],{"class":1090,"line":2231},76,[1088,2233,1532],{"class":1101},[1088,2235,2237],{"class":1090,"line":2236},77,[1088,2238,1403],{"class":1101},[1088,2240,2242],{"class":1090,"line":2241},78,[1088,2243,1280],{"class":1279},[1088,2245,2247],{"class":1090,"line":2246},79,[1088,2248,2249],{"class":1279},"\u002F** Checks every comparison in the filter, however deeply nested, before any of it is translated. *\u002F\n",[1088,2251,2253,2255,2258,2260,2262,2264,2266,2268,2270,2272,2274,2276,2278,2281],{"class":1090,"line":2252},80,[1088,2254,1322],{"class":1094},[1088,2256,2257],{"class":1325}," requireDeclared",[1088,2259,1329],{"class":1101},[1088,2261,1332],{"class":1094},[1088,2263,1335],{"class":1325},[1088,2265,519],{"class":1101},[1088,2267,1340],{"class":1094},[1088,2269,1343],{"class":1325},[1088,2271,1346],{"class":1094},[1088,2273,1350],{"class":1349},[1088,2275,523],{"class":1101},[1088,2277,815],{"class":1094},[1088,2279,2280],{"class":1349}," void",[1088,2282,1102],{"class":1101},[1088,2284,2286,2288,2290,2292,2294,2296,2298,2300,2302,2305,2307,2309,2311,2313,2315,2317,2319,2321,2323,2325,2327,2329],{"class":1090,"line":2285},81,[1088,2287,1447],{"class":1094},[1088,2289,1450],{"class":1101},[1088,2291,1370],{"class":1108},[1088,2293,1000],{"class":1101},[1088,2295,2010],{"class":1108},[1088,2297,1459],{"class":1094},[1088,2299,1350],{"class":1325},[1088,2301,1510],{"class":1101},[1088,2303,2304],{"class":1325},"requireOperator",[1088,2306,1329],{"class":1101},[1088,2308,1375],{"class":1108},[1088,2310,519],{"class":1101},[1088,2312,1370],{"class":1108},[1088,2314,1000],{"class":1101},[1088,2316,1872],{"class":1108},[1088,2318,1000],{"class":1101},[1088,2320,1726],{"class":1108},[1088,2322,519],{"class":1101},[1088,2324,1370],{"class":1108},[1088,2326,1000],{"class":1101},[1088,2328,2010],{"class":1108},[1088,2330,1378],{"class":1101},[1088,2332,2334,2336,2338,2340,2342,2344,2346,2348,2350,2352,2354,2356,2358,2360,2362,2364,2366,2368,2370,2373],{"class":1090,"line":2333},82,[1088,2335,1447],{"class":1094},[1088,2337,1450],{"class":1101},[1088,2339,1370],{"class":1108},[1088,2341,1000],{"class":1101},[1088,2343,543],{"class":1108},[1088,2345,1459],{"class":1094},[1088,2347,1350],{"class":1325},[1088,2349,1510],{"class":1101},[1088,2351,2304],{"class":1325},[1088,2353,1329],{"class":1101},[1088,2355,1375],{"class":1108},[1088,2357,519],{"class":1101},[1088,2359,1370],{"class":1108},[1088,2361,1000],{"class":1101},[1088,2363,543],{"class":1108},[1088,2365,1000],{"class":1101},[1088,2367,1726],{"class":1108},[1088,2369,519],{"class":1101},[1088,2371,2372],{"class":1220},"'in'",[1088,2374,1378],{"class":1101},[1088,2376,2378,2381,2383,2386,2389,2392,2394,2396,2398,2401,2403,2405,2407,2409,2411,2413,2415,2417,2419,2421,2424,2427,2429,2432,2434,2436,2438,2441,2444,2446,2448,2450,2452],{"class":1090,"line":2377},83,[1088,2379,2380],{"class":1094},"    for",[1088,2382,1450],{"class":1101},[1088,2384,2385],{"class":1094},"const",[1088,2387,2388],{"class":1325}," child",[1088,2390,2391],{"class":1094}," of",[1088,2393,1867],{"class":1108},[1088,2395,1000],{"class":1101},[1088,2397,547],{"class":1108},[1088,2399,2400],{"class":1094}," ??",[1088,2402,1867],{"class":1108},[1088,2404,1000],{"class":1101},[1088,2406,1062],{"class":1108},[1088,2408,2400],{"class":1094},[1088,2410,1450],{"class":1101},[1088,2412,1370],{"class":1108},[1088,2414,1000],{"class":1101},[1088,2416,1641],{"class":1108},[1088,2418,1729],{"class":1094},[1088,2420,1350],{"class":1325},[1088,2422,2423],{"class":1094}," ?",[1088,2425,2426],{"class":1101}," [] ",[1088,2428,815],{"class":1094},[1088,2430,2431],{"class":1101}," [",[1088,2433,1370],{"class":1108},[1088,2435,1000],{"class":1101},[1088,2437,1641],{"class":1108},[1088,2439,2440],{"class":1101},"])) ",[1088,2442,2443],{"class":1325},"requireDeclared",[1088,2445,1329],{"class":1101},[1088,2447,1507],{"class":1108},[1088,2449,519],{"class":1101},[1088,2451,1375],{"class":1108},[1088,2453,1378],{"class":1101},[1088,2455,2457],{"class":1090,"line":2456},84,[1088,2458,1403],{"class":1101},[1088,2460,2462],{"class":1090,"line":2461},85,[1088,2463,1410],{"emptyLinePlaceholder":1409},[1088,2465,2467],{"class":1090,"line":2466},86,[1088,2468,2469],{"class":1279},"\u002F** Refuses `operator` on `field` unless `contract` declares it. A value in the field's place is a shape `translate` refuses. *\u002F\n",[1088,2471,2473,2475,2478,2480,2482,2484,2486,2488,2490,2493,2496,2498,2500,2502,2505,2508,2510,2512,2514],{"class":1090,"line":2472},87,[1088,2474,1322],{"class":1094},[1088,2476,2477],{"class":1325}," requireOperator",[1088,2479,1329],{"class":1101},[1088,2481,1340],{"class":1094},[1088,2483,1343],{"class":1325},[1088,2485,1346],{"class":1094},[1088,2487,1350],{"class":1349},[1088,2489,519],{"class":1101},[1088,2491,2492],{"class":1094},"field:",[1088,2494,2495],{"class":1349}," string",[1088,2497,1346],{"class":1094},[1088,2499,1350],{"class":1349},[1088,2501,519],{"class":1101},[1088,2503,2504],{"class":1094},"operator:",[1088,2506,2507],{"class":1325}," DeclaredOperator",[1088,2509,523],{"class":1101},[1088,2511,815],{"class":1094},[1088,2513,2280],{"class":1349},[1088,2515,1102],{"class":1101},[1088,2517,2519,2521,2523,2525,2527,2529,2531,2533],{"class":1090,"line":2518},88,[1088,2520,1447],{"class":1094},[1088,2522,1450],{"class":1101},[1088,2524,1726],{"class":1108},[1088,2526,1729],{"class":1094},[1088,2528,1350],{"class":1325},[1088,2530,1510],{"class":1101},[1088,2532,2020],{"class":1094},[1088,2534,1224],{"class":1101},[1088,2536,2538,2540,2543,2545,2548,2551,2554,2557,2559,2562,2565],{"class":1090,"line":2537},89,[1088,2539,1855],{"class":1094},[1088,2541,2542],{"class":1325}," operators",[1088,2544,1961],{"class":1094},[1088,2546,2547],{"class":1108}," contract",[1088,2549,2550],{"class":1101},"?.",[1088,2552,2553],{"class":1108},"fields",[1088,2555,2556],{"class":1101},"[",[1088,2558,1726],{"class":1108},[1088,2560,2561],{"class":1101},"]?.",[1088,2563,2564],{"class":1108},"operators",[1088,2566,1224],{"class":1101},[1088,2568,2570],{"class":1090,"line":2569},90,[1088,2571,1410],{"emptyLinePlaceholder":1409},[1088,2573,2575,2577,2579,2581,2583,2585,2587,2589,2592,2594,2597,2599,2601,2603,2606],{"class":1090,"line":2574},91,[1088,2576,1447],{"class":1094},[1088,2578,1450],{"class":1101},[1088,2580,2564],{"class":1108},[1088,2582,1729],{"class":1094},[1088,2584,1350],{"class":1325},[1088,2586,1734],{"class":1094},[1088,2588,1737],{"class":1094},[1088,2590,2591],{"class":1108},"Object",[1088,2593,1000],{"class":1101},[1088,2595,2596],{"class":1325},"hasOwn",[1088,2598,1329],{"class":1101},[1088,2600,2564],{"class":1108},[1088,2602,519],{"class":1101},[1088,2604,2605],{"class":1108},"operator",[1088,2607,2608],{"class":1101},")) {\n",[1088,2610,2612,2614,2616,2619,2621,2624,2626,2628,2630,2632,2634,2636,2638,2640,2642,2644,2646,2648],{"class":1090,"line":2611},92,[1088,2613,1659],{"class":1094},[1088,2615,1662],{"class":1094},[1088,2617,2618],{"class":1325}," Error",[1088,2620,1329],{"class":1101},[1088,2622,2623],{"class":1220},"`The Art Institute connector can't filter ",[1088,2625,2209],{"class":1325},[1088,2627,2212],{"class":1220},[1088,2629,1726],{"class":1108},[1088,2631,2221],{"class":1220},[1088,2633,2209],{"class":1325},[1088,2635,957],{"class":1220},[1088,2637,2209],{"class":1325},[1088,2639,2212],{"class":1220},[1088,2641,2605],{"class":1108},[1088,2643,2221],{"class":1220},[1088,2645,2209],{"class":1325},[1088,2647,2226],{"class":1220},[1088,2649,1378],{"class":1101},[1088,2651,2653],{"class":1090,"line":2652},93,[1088,2654,1532],{"class":1101},[1088,2656,2658],{"class":1090,"line":2657},94,[1088,2659,1403],{"class":1101},[2661,2662,2663,2692,2710,2729,2747,2761,2773,2796],"ul",{},[2664,2665,2666,2669,2670,2672,2673,2675,2676,2679,2680,957,2682,2685,2686,2689,2690,1000],"li",{},[462,2667,2668],{},"Only what the collection declares is translated."," ",[516,2671,2443],{}," checks every comparison in the filter, however deeply nested, against the connector's own declaration. It checks the field on the left of each comparison and the field of each ",[516,2674,543],{},". An undeclared field or operator there is a plain ",[516,2677,2678],{},"Error",", thrown before anything is translated or sent (e.g., \"The Art Institute connector can't filter ",[516,2681,867],{},[516,2683,2684],{},"lessThan",".\"). A field on the right of a comparison is a shape ",[516,2687,2688],{},"translate"," refuses with ",[516,2691,1240],{},[2664,2693,2694,2697,2698,2700,2701,2705,2706,1000],{},[462,2695,2696],{},"Engine doesn't check comparisons for you."," Engine checks a caller's request against the declaration it stored for the data source, but sends its own filters unchecked. An undeclared comparison reaches ",[516,2699,2443],{},", for example, when that stored declaration is older than the code, after an ",[471,2702,2704],{"href":2703},"\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Fdata-sources#update-data-sources-after-an-extension-update","extension update"," that narrowed the declaration without a schema refresh. On a collection with writes, a permission rule's condition can also bring one. See ",[471,2707,2709],{"href":2708},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Fapi#handle-requests-you-didnt-declare","Handle Requests You Didn't Declare",[2664,2711,2712,2715,2716,2718,2719,2722,2723,2725,2726,2728],{},[462,2713,2714],{},"Refuse, never drop."," A comparison you skip can be a permission rule, and dropping it widens what the caller reads. No class fits an undeclared comparison, and Engine reports the plain ",[516,2717,2678],{}," as a failed query. ",[516,2720,2721],{},"requireLogical"," throws ",[516,2724,1240],{}," instead, because Engine composes ",[516,2727,1062],{}," across permission rules whatever the collection declares.",[2664,2730,2731,2669,2738,544,2741,957,2744,1000],{},[462,2732,2733,544,2735,2737],{},[516,2734,547],{},[516,2736,1062],{}," map directly.",[516,2739,2740],{},"bool.filter",[516,2742,2743],{},"bool.should",[516,2745,2746],{},"minimum_should_match: 1",[2664,2748,2749,2757,2758,1000],{},[462,2750,2751,957,2753,2756],{},[516,2752,540],{},[516,2754,2755],{},"null"," needs a null test."," It means \"is null\", never a literal. The connector sends ",[516,2759,2760],{},"must_not exists",[2664,2762,2763,2769,2770,1000],{},[462,2764,2765,2766,2768],{},"Other comparisons with ",[516,2767,2755],{}," match nothing."," In SQL they're unknown, so the connector sends ",[516,2771,2772],{},"match_none",[2664,2774,2775,2783,2784,2786,2787,2789,2790,2792,2793,2795],{},[462,2776,2777,2779,2780,2782],{},[516,2778,543],{}," leaves ",[516,2781,2755],{}," members out."," The search API refuses ",[516,2785,2755],{}," in ",[516,2788,852],{},", and ",[516,2791,852],{}," never matches a missing value. So ",[516,2794,543],{}," never matches an item without a value.",[2664,2797,2798,2803,2804,2806,2807,2809,2810,2812,2813,2816,2817,2819,2820,2822,2823,2825],{},[462,2799,2800,2802],{},[516,2801,1641],{}," is rejected."," The connector doesn't declare ",[516,2805,1641],{},", because Elasticsearch's ",[516,2808,674],{}," selects missing values that SQL ",[516,2811,678],{}," excludes. So Engine offers callers no negation, and no ",[516,2814,2815],{},"_null",", which needs ",[516,2818,1641],{},". A ",[516,2821,1641],{}," that still arrives is a ",[516,2824,1240],{}," that says so: \"The Art Institute connector can't negate a filter.\"",[458,2827,2828,2829,2831,2832,2834,2835,519,2837,2839,2840,2834,2843,2846],{},"When an external data source has no general negation, push ",[516,2830,1641],{}," down to the leaves (",[516,2833,547],{}," becomes ",[516,2836,1062],{},[516,2838,540],{}," becomes not-equals, ",[516,2841,2842],{},"\u003C",[516,2844,2845],{},">=","). Refuse a leaf with no complement.",[458,2848,2849],{},"How a negated leaf treats a null field depends on the semantics you implement: your connector defines its own null handling. Under SQL three-valued logic, which the example connectors follow, a negated comparison never matches a null field. There, add an explicit \"is not null\" next to each negated comparison on a nullable field. Document the rules you choose, because callers can't read them from the declaration.",[1046,2851,2853],{"id":2852},"plan-a-weak-grammar","Plan a Weak Grammar",[458,2855,2856],{},"The Stripe connector plans each filter into Stripe requests before it sends any:",[2661,2858,2859,2867,2877,2888],{},[2664,2860,2861,2866],{},[462,2862,2863,2865],{},[516,2864,547],{}," intersects"," the allowed values and ranges of its children.",[2664,2868,2869,2876],{},[462,2870,2871,544,2873,2875],{},[516,2872,543],{},[516,2874,1062],{}," fan out"," into one list request per value or branch. A filter that plans more than 250 requests is refused. The bound counts planned requests, not HTTP requests, since one planned list can take several pages. It's checked while the combinations expand, before duplicates merge, so a filter can be refused even when fewer distinct requests would remain.",[2664,2878,2879,2882,2883,2887],{},[462,2880,2881],{},"An id constraint becomes a retrieve by id,"," one request per id. Top-level ids bypass the 250 bound; the read budget in ",[471,2884,2886],{"href":2885},"#bound-unbounded-reads","Bound Unbounded Reads"," limits them instead.",[2664,2889,2890,2893,2894,2896,2897,2899,2900,2903,2904,2906,2907,2909,2910,2912],{},[462,2891,2892],{},"Anything else is refused."," A field or operator the collection doesn't declare is a plain ",[516,2895,2678],{},". A permission rule's condition or an extension update without a schema refresh can send one. A declared shape Stripe can't express, such as ",[516,2898,1641],{}," or ",[516,2901,2902],{},"field = null",", is a ",[516,2905,1240],{},": \"Stripe can't filter ",[516,2908,560],{}," by ",[516,2911,642],{}," being null without reading every item.\"",[458,2914,2915],{},"The plan then runs in one of two shapes:",[488,2917,2918,2930],{},[491,2919,2920],{},[494,2921,2922,2925,2928],{},[497,2923,2924],{},"Plan",[497,2926,2927],{},"When",[497,2929,713],{},[507,2931,2932,2952],{},[494,2933,2934,2937,2940],{},[512,2935,2936],{},"Page",[512,2938,2939],{},"The filter is exactly one list request",[512,2941,2942,544,2944,2947,2948,2951],{},[516,2943,588],{},[516,2945,2946],{},"limit"," pass through to Stripe. Engine's per-parent relation reads (",[516,2949,2950],{},"in: [key]",") land here",[494,2953,2954,2957,2960],{},[512,2955,2956],{},"Collect",[512,2958,2959],{},"Several list requests or retrieves by id",[512,2961,2962,2963,2966],{},"Each list request reads ",[516,2964,2965],{},"offset + limit"," items, plus any created in the same second as the last one, and each retrieve fetches its ids. The connector drops duplicate ids, merges the rest in the declared order, re-checks the filter, and slices",[458,2968,2969],{},"The two plans can order items created in the same second differently. The page plan keeps Stripe's own order for such ties, which Stripe doesn't document. The collect plan breaks them by id.",[458,2971,2972,2974],{},[471,2973,578],{"href":577}," walks through the key parts of this code, and its downloadable project has all of it.",[1046,2976,2978],{"id":2977},"apply-these-rules-to-both-grammars","Apply These Rules to Both Grammars",[2661,2980,2981,2995,3001,3024,3034,3044,3050],{},[2664,2982,2983,2986,2987,2990,2991,2994],{},[462,2984,2985],{},"Read both operands."," Engine can send ",[516,2988,2989],{},"value \u003C field"," as well as ",[516,2992,2993],{},"field > value",". Reverse the ordered comparisons, and refuse string operators on a reversed pair.",[2664,2996,2997,3000],{},[462,2998,2999],{},"Refuse field-to-field comparisons"," unless the external data source expresses them. Never send a field name as a literal.",[2664,3002,3003,3006,3007,3009,3010,2834,3013,3016,3017,3019,3020,3023],{},[462,3004,3005],{},"Convert ranges to the external data source's precision, exactly."," Stripe stores ",[516,3008,693],{}," in whole seconds, so ",[516,3011,3012],{},"> t",[516,3014,3015],{},"created[gte]=floor(t)+1",". Parse the timestamp strictly and keep every fractional digit. ",[516,3018,2845],{}," a timestamp one microsecond past a whole second (",[516,3021,3022],{},"…:37.000001Z",") must move the bound to the next second, and a parser that stops at the millisecond keeps it on the same one.",[2664,3025,3026,3029,3030,3033],{},[462,3027,3028],{},"Decide out-of-domain values locally."," Stripe rejects ",[516,3031,3032],{},"active=maybe"," with an error. The Stripe connector never sends it, because no item can hold that value.",[2664,3035,3036,3039,3040,3043],{},[462,3037,3038],{},"Never turn \"no match\" into an error."," Stripe answers ",[516,3041,3042],{},"prices?product="," with a missing product as an error. The Stripe connector answers it with no items.",[2664,3045,3046,3049],{},[462,3047,3048],{},"Refuse sort options you can't honor,"," such as explicit null placement on an external data source that has none, rather than ignoring them.",[2664,3051,3052,3058,3059,3061,3062,3065],{},[462,3053,3054,3055,3057],{},"Keep a fixed null placement when ",[516,3056,966],{}," is absent."," Engine sends ",[516,3060,966],{}," only when the caller sets it. Without it, your connector picks the placement. Pick one, such as where the external data source puts them by default, and keep it stable across pages. The Art Institute connector sends ",[516,3063,3064],{},"missing: '_last'"," in both directions, where the search API puts items without a value by default.",[454,3067,3069],{"id":3068},"re-check-every-item","Re-Check Every Item",[458,3071,3072,3073,3075,3076,3078,3079,3081],{},"Run the full filter over every item the external data source returns, unless the request is exact by construction. This residual check makes a superset parameter correct when you page over the items that pass it: skip the caller's ",[516,3074,588],{}," among those items, then keep reading until ",[516,3077,2946],{}," of them pass. Never forward the caller's ",[516,3080,588],{}," to a request that returns a superset, because the items it skips aren't all matches. The check can't recover an item the request excluded, so pushdown must never drop a match.",[458,3083,3084],{},"The Stripe connector evaluates with SQL three-valued logic. That's the examples' choice, not a kit rule:",[488,3086,3087,3097],{},[491,3088,3089],{},[494,3090,3091,3094],{},[497,3092,3093],{},"Case",[497,3095,3096],{},"Result",[507,3098,3099,3109,3122,3131,3147,3157,3167],{},[494,3100,3101,3106],{},[512,3102,3103,3104],{},"A comparison with ",[516,3105,2755],{},[512,3107,3108],{},"Unknown",[494,3110,3111,3119],{},[512,3112,3113,3115,3116,3118],{},[516,3114,540],{}," with a ",[516,3117,2755],{}," literal",[512,3120,3121],{},"True when the field is null. This is how Engine sends \"is null\"",[494,3123,3124,3129],{},[512,3125,3126,3128],{},[516,3127,1641],{}," of unknown",[512,3130,3108],{},[494,3132,3133,3141],{},[512,3134,3135,3137,3138,3140],{},[516,3136,543],{}," with no matching member and a ",[516,3139,2755],{}," member",[512,3142,3143,3144,3146],{},"Unknown, so ",[516,3145,1641],{}," around it stays unknown",[494,3148,3149,3154],{},[512,3150,3151,3153],{},[516,3152,547],{}," with any false child",[512,3155,3156],{},"False; otherwise unknown if any child is unknown",[494,3158,3159,3164],{},[512,3160,3161,3163],{},[516,3162,1062],{}," with any true child",[512,3165,3166],{},"True; otherwise unknown if any child is unknown",[494,3168,3169,3172],{},[512,3170,3171],{},"The whole filter",[512,3173,3174],{},"Selects the item only when true",[458,3176,3177,3178,2786,3181,3184],{},"The Stripe connector's ",[516,3179,3180],{},"matches",[516,3182,3183],{},"filter.ts"," implements these rules, and compares timestamps as nanoseconds. The kit has no filter evaluator, so a connector that re-checks items implements the rules it chooses.",[458,3186,3187,3188,3190],{},"Each Stripe list parameter the connector sends matches exactly, and its ",[516,3189,693],{}," bounds are computed from the exact timestamp. So a single Stripe list request skips the check, while retrieves by id and merged fan-outs go through it. The Art Institute connector translates every filter it accepts into search API queries, so it runs no re-check. It refuses a comparison with the value on the left instead of reversing it.",[458,3192,3193,3194,3196,3197,3199],{},"The check also serves filters Engine composes that the external data source can't express, such as ",[516,3195,1641],{}," around an update's field rules and ",[516,3198,1062],{}," across permission rules. Where ids bound the candidates, retrieve them and evaluate the rest in memory. Everywhere else, refuse rather than scan.",[3201,3202,3203,3204,3206,3207,3209,3210,839,3212,3215,3216,957,3218,2789,3220,3223,3224,3226,3227,3229],"note",{},"Engine offers ",[516,3205,2815],{}," only on a nullable field whose filter declares ",[516,3208,540],{},", in a member whose filter also declares ",[516,3211,1641],{},[516,3213,3214],{},"_null: true"," arrives as ",[516,3217,540],{},[516,3219,2755],{},[516,3221,3222],{},"_null: false"," as ",[516,3225,1641],{}," around it. Declare ",[516,3228,1641],{}," only if you can answer both, with the external data source's null test or in memory over a bounded set.",[454,3231,3233],{"id":3232},"page-through-results","Page Through Results",[458,3235,3236,3237,544,3239,3241,3242,1000],{},"Callers page with ",[516,3238,2946],{},[516,3240,588],{}," only, and a connector can't declare a maximum for either. See ",[471,3243,713],{"href":3244},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Flimitations#paging",[1046,3246,3248],{"id":3247},"fill-the-limit","Fill the Limit",[458,3250,3251,3252,3254,3255,3257,3258,3260],{},"Engine reads a page shorter than ",[516,3253,2946],{}," as the end of the data. When the external data source's pages are smaller than ",[516,3256,2946],{},", keep requesting until you reach ",[516,3259,2946],{}," or it runs out, and size the last request to what remains.",[488,3262,3263,3278],{},[491,3264,3265],{},[494,3266,3267,3269,3272],{},[497,3268,499],{},[497,3270,3271],{},"Page size",[497,3273,3274,3277],{},[516,3275,3276],{},"limit: 150"," costs",[507,3279,3280,3294],{},[494,3281,3282,3285,3288],{},[512,3283,3284],{},"Stripe",[512,3286,3287],{},"100, capped without an error",[512,3289,3290,3291],{},"A page of 100, then 50 via ",[516,3292,3293],{},"starting_after",[494,3295,3296,3299,3306],{},[512,3297,3298],{},"Art Institute",[512,3300,3301,3302,3305],{},"100; ",[516,3303,3304],{},"size: 101"," is refused",[512,3307,3308,3311,3312],{},[516,3309,3310],{},"from: 0, size: 100",", then ",[516,3313,3314],{},"from: 100, size: 50",[1046,3316,3318],{"id":3317},"keep-the-order-stable","Keep the Order Stable",[458,3320,3321,3322,3324,3325,3329],{},"Engine adds no sort of its own, so a request without a caller sort arrives without ",[516,3323,956],{},". Close every sort with the ",[471,3326,3328],{"href":3327},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Foperations#use-keys-and-the-stable-id","stable ID",", so pages over unchanged data never overlap. Items created, deleted or edited between two page requests can still shift the pages.",[458,3331,3332,3333,3335,3336,3338,3339,3341,3342,3344],{},"The Art Institute connector appends ",[516,3334,551],{}," ascending unless ",[516,3337,551],{}," is already sorted. The Stripe connector breaks ",[516,3340,693],{}," ties by ",[516,3343,551],{}," when it merges requests.",[458,3346,3347,3348,3350],{},"Break ties before anything cuts a result short. When you merge several requests that were each cut at ",[516,3349,2965],{},", a tie-break applied only after the merge doesn't restore one global order: items that share a sort value at a cut can land on different pages. The Stripe connector avoids this: in a merge, it reads each list on through every item created in the same second as the last one, so every tied candidate reaches the tie-break.",[1046,3352,3354],{"id":3353},"serve-offset-over-cursors","Serve Offset over Cursors",[458,3356,3357,3358,3360,3361,3363,3364,3366],{},"An external data source can offer cursor paging only. Stripe's API reference documents only cursor paging, but Stripe's list endpoints also accept an undocumented ",[516,3359,588],{}," parameter. The Stripe connector uses it to skip to the caller's ",[516,3362,588],{}," on the first request, and follows ",[516,3365,3293],{}," after that.",[458,3368,3369,3370,3372],{},"On an external data source with cursors only, walk pages and discard items until you reach ",[516,3371,588],{},". The cost grows with the page number, so cap the walk.",[1046,3374,3376],{"id":3375},"refuse-past-a-search-window","Refuse Past a Search Window",[458,3378,3379],{},"The Art Institute's search refuses any request that reaches past its 1,000th result. The contract can't declare that ceiling, so the connector checks it before reading any data:",[1071,3381,3382],{},[1074,3383,3385],{"label":3384},"src\u002Fdata-connectors\u002Fartic\u002Fsearch.ts (readMany)",[1078,3386,3388],{"className":1080,"code":3387,"filename":1082,"language":1083,"meta":1084,"style":1084},"const BASE_URL = 'https:\u002F\u002Fapi.artic.edu\u002Fapi\u002Fv1';\n\u002F** Engine sets no deadline on `query` or `testConnection`, so every request carries its own. *\u002F\nconst TIMEOUT_MS = 10_000;\n\u002F** The search API returns at most 100 items per request... *\u002F\nconst PAGE_SIZE = 100;\n\u002F** ...and refuses any request that reaches past its 1,000th result. *\u002F\nconst SEARCH_WINDOW = 1000;\n\n\u002F** A request body for the search API, which takes Elasticsearch syntax. *\u002F\ntype SearchBody = Record\u003Cstring, unknown>;\n\u002F\u002F …\n\u002F** Answers `readMany`. The response field is `records`; each entry is one item. *\u002F\nexport async function readMany(query: ReadManyOperationSerialized): Promise\u003CQueryResultSerialized> {\n    const { read, types } = declaration(query.collection);\n    \u002F\u002F Translate everything first, so an undeclared filter or sort is refused before any request.\n    const base: SearchBody = query.filter === undefined ? {} : { query: toQuery(query.filter, read.filter) };\n    const sort = toSort(query.sort ?? [], read.sort);\n\n    \u002F\u002F A count read answers the number of matches and ignores `limit` and `offset`.\n    if (query.count === true) return { totalCount: await count(query.collection, base) };\n\n    const offset = query.offset ?? 0;\n    let end = query.limit === undefined ? Infinity : offset + query.limit;\n\n    \u002F\u002F The search API refuses any request past its 1,000th result. A read that reaches past it is\n    \u002F\u002F still safe when fewer items match, so count the matches first and stop at the last one.\n    if (end > SEARCH_WINDOW) {\n        end = Math.min(end, await count(query.collection, base));\n\n        if (end > SEARCH_WINDOW) {\n            \u002F\u002F A valid read the API can't serve: the caller can change it, so the message says how.\n            throw new QueryRejected(`The Art Institute search API returns only the first ${SEARCH_WINDOW} results. Narrow the filter or read an earlier page.`);\n        }\n    }\n\n    const select = query.select ?? [];\n    const body = { ...base, sort, fields: select.map((name) => apiFieldName(name)) };\n    const records: MonospaceObject[] = [];\n\n    for (let from = offset; from \u003C end; from += PAGE_SIZE) {\n        const size = Math.min(PAGE_SIZE, end - from);\n        const page = await search(query.collection, { ...body, from, size });\n        records.push(...page.data.map((apiItem) => toItem(apiItem, select, types)));\n        if (page.data.length \u003C size) break;\n    }\n\n    return { records };\n}\n",[516,3389,3390,3404,3409,3423,3428,3442,3447,3461,3465,3470,3495,3499,3504,3541,3574,3579,3635,3668,3672,3677,3723,3727,3749,3790,3794,3799,3804,3820,3860,3864,3878,3883,3905,3910,3914,3918,3939,3985,4004,4008,4045,4077,4119,4172,4200,4204,4208,4220],{"__ignoreMap":1084},[1088,3391,3392,3394,3397,3399,3402],{"class":1090,"line":1091},[1088,3393,2385],{"class":1094},[1088,3395,3396],{"class":1325}," BASE_URL",[1088,3398,1961],{"class":1094},[1088,3400,3401],{"class":1220}," 'https:\u002F\u002Fapi.artic.edu\u002Fapi\u002Fv1'",[1088,3403,1224],{"class":1101},[1088,3405,3406],{"class":1090,"line":1105},[1088,3407,3408],{"class":1279},"\u002F** Engine sets no deadline on `query` or `testConnection`, so every request carries its own. *\u002F\n",[1088,3410,3411,3413,3416,3418,3421],{"class":1090,"line":1115},[1088,3412,2385],{"class":1094},[1088,3414,3415],{"class":1325}," TIMEOUT_MS",[1088,3417,1961],{"class":1094},[1088,3419,3420],{"class":1325}," 10_000",[1088,3422,1224],{"class":1101},[1088,3424,3425],{"class":1090,"line":1123},[1088,3426,3427],{"class":1279},"\u002F** The search API returns at most 100 items per request... *\u002F\n",[1088,3429,3430,3432,3435,3437,3440],{"class":1090,"line":1131},[1088,3431,2385],{"class":1094},[1088,3433,3434],{"class":1325}," PAGE_SIZE",[1088,3436,1961],{"class":1094},[1088,3438,3439],{"class":1325}," 100",[1088,3441,1224],{"class":1101},[1088,3443,3444],{"class":1090,"line":1139},[1088,3445,3446],{"class":1279},"\u002F** ...and refuses any request that reaches past its 1,000th result. *\u002F\n",[1088,3448,3449,3451,3454,3456,3459],{"class":1090,"line":1147},[1088,3450,2385],{"class":1094},[1088,3452,3453],{"class":1325}," SEARCH_WINDOW",[1088,3455,1961],{"class":1094},[1088,3457,3458],{"class":1325}," 1000",[1088,3460,1224],{"class":1101},[1088,3462,3463],{"class":1090,"line":1155},[1088,3464,1410],{"emptyLinePlaceholder":1409},[1088,3466,3467],{"class":1090,"line":1163},[1088,3468,3469],{"class":1279},"\u002F** A request body for the search API, which takes Elasticsearch syntax. *\u002F\n",[1088,3471,3472,3475,3477,3479,3482,3484,3487,3489,3492],{"class":1090,"line":1171},[1088,3473,3474],{"class":1094},"type",[1088,3476,1357],{"class":1325},[1088,3478,1961],{"class":1094},[1088,3480,3481],{"class":1325}," Record",[1088,3483,2842],{"class":1101},[1088,3485,3486],{"class":1349},"string",[1088,3488,519],{"class":1101},[1088,3490,3491],{"class":1349},"unknown",[1088,3493,3494],{"class":1101},">;\n",[1088,3496,3497],{"class":1090,"line":1179},[1088,3498,1280],{"class":1279},[1088,3500,3501],{"class":1090,"line":1187},[1088,3502,3503],{"class":1279},"\u002F** Answers `readMany`. The response field is `records`; each entry is one item. *\u002F\n",[1088,3505,3506,3509,3512,3515,3518,3520,3523,3526,3528,3530,3533,3535,3538],{"class":1090,"line":1195},[1088,3507,3508],{"class":1094},"export",[1088,3510,3511],{"class":1094}," async",[1088,3513,3514],{"class":1094}," function",[1088,3516,3517],{"class":1325}," readMany",[1088,3519,1329],{"class":1101},[1088,3521,3522],{"class":1094},"query:",[1088,3524,3525],{"class":1325}," ReadManyOperationSerialized",[1088,3527,523],{"class":1101},[1088,3529,815],{"class":1094},[1088,3531,3532],{"class":1325}," Promise",[1088,3534,2842],{"class":1101},[1088,3536,3537],{"class":1325},"QueryResultSerialized",[1088,3539,3540],{"class":1101},"> {\n",[1088,3542,3543,3545,3547,3550,3552,3555,3557,3559,3562,3564,3567,3569,3572],{"class":1090,"line":1203},[1088,3544,1855],{"class":1094},[1088,3546,1232],{"class":1101},[1088,3548,3549],{"class":1325},"read",[1088,3551,519],{"class":1101},[1088,3553,3554],{"class":1325},"types",[1088,3556,1248],{"class":1101},[1088,3558,1864],{"class":1094},[1088,3560,3561],{"class":1325}," declaration",[1088,3563,1329],{"class":1101},[1088,3565,3566],{"class":1108},"query",[1088,3568,1000],{"class":1101},[1088,3570,3571],{"class":1108},"collection",[1088,3573,1378],{"class":1101},[1088,3575,3576],{"class":1090,"line":1211},[1088,3577,3578],{"class":1279},"    \u002F\u002F Translate everything first, so an undeclared filter or sort is refused before any request.\n",[1088,3580,3581,3583,3586,3588,3590,3592,3595,3597,3599,3601,3603,3605,3608,3610,3613,3616,3618,3620,3622,3624,3626,3628,3630,3632],{"class":1090,"line":1227},[1088,3582,1855],{"class":1094},[1088,3584,3585],{"class":1325}," base",[1088,3587,815],{"class":1094},[1088,3589,1357],{"class":1325},[1088,3591,1961],{"class":1094},[1088,3593,3594],{"class":1108}," query",[1088,3596,1000],{"class":1101},[1088,3598,1370],{"class":1108},[1088,3600,1729],{"class":1094},[1088,3602,1350],{"class":1325},[1088,3604,2423],{"class":1094},[1088,3606,3607],{"class":1101}," {} ",[1088,3609,815],{"class":1094},[1088,3611,3612],{"class":1101}," { query: ",[1088,3614,3615],{"class":1325},"toQuery",[1088,3617,1329],{"class":1101},[1088,3619,3566],{"class":1108},[1088,3621,1000],{"class":1101},[1088,3623,1370],{"class":1108},[1088,3625,519],{"class":1101},[1088,3627,3549],{"class":1108},[1088,3629,1000],{"class":1101},[1088,3631,1370],{"class":1108},[1088,3633,3634],{"class":1101},") };\n",[1088,3636,3637,3639,3642,3644,3647,3649,3651,3653,3655,3657,3660,3662,3664,3666],{"class":1090,"line":1257},[1088,3638,1855],{"class":1094},[1088,3640,3641],{"class":1325}," sort",[1088,3643,1961],{"class":1094},[1088,3645,3646],{"class":1325}," toSort",[1088,3648,1329],{"class":1101},[1088,3650,3566],{"class":1108},[1088,3652,1000],{"class":1101},[1088,3654,956],{"class":1108},[1088,3656,2400],{"class":1094},[1088,3658,3659],{"class":1101}," [], ",[1088,3661,3549],{"class":1108},[1088,3663,1000],{"class":1101},[1088,3665,956],{"class":1108},[1088,3667,1378],{"class":1101},[1088,3669,3670],{"class":1090,"line":1276},[1088,3671,1410],{"emptyLinePlaceholder":1409},[1088,3673,3674],{"class":1090,"line":1283},[1088,3675,3676],{"class":1279},"    \u002F\u002F A count read answers the number of matches and ignores `limit` and `offset`.\n",[1088,3678,3679,3681,3683,3685,3687,3690,3692,3695,3697,3699,3702,3705,3708,3710,3712,3714,3716,3718,3721],{"class":1090,"line":1289},[1088,3680,1447],{"class":1094},[1088,3682,1450],{"class":1101},[1088,3684,3566],{"class":1108},[1088,3686,1000],{"class":1101},[1088,3688,3689],{"class":1108},"count",[1088,3691,1729],{"class":1094},[1088,3693,3694],{"class":1325}," true",[1088,3696,1510],{"class":1101},[1088,3698,2020],{"class":1094},[1088,3700,3701],{"class":1101}," { totalCount: ",[1088,3703,3704],{"class":1094},"await",[1088,3706,3707],{"class":1325}," count",[1088,3709,1329],{"class":1101},[1088,3711,3566],{"class":1108},[1088,3713,1000],{"class":1101},[1088,3715,3571],{"class":1108},[1088,3717,519],{"class":1101},[1088,3719,3720],{"class":1108},"base",[1088,3722,3634],{"class":1101},[1088,3724,3725],{"class":1090,"line":1295},[1088,3726,1410],{"emptyLinePlaceholder":1409},[1088,3728,3729,3731,3734,3736,3738,3740,3742,3744,3747],{"class":1090,"line":1301},[1088,3730,1855],{"class":1094},[1088,3732,3733],{"class":1325}," offset",[1088,3735,1961],{"class":1094},[1088,3737,3594],{"class":1108},[1088,3739,1000],{"class":1101},[1088,3741,588],{"class":1108},[1088,3743,2400],{"class":1094},[1088,3745,3746],{"class":1325}," 0",[1088,3748,1224],{"class":1101},[1088,3750,3751,3754,3757,3759,3761,3763,3765,3767,3769,3771,3774,3777,3779,3782,3784,3786,3788],{"class":1090,"line":1307},[1088,3752,3753],{"class":1094},"    let",[1088,3755,3756],{"class":1108}," end",[1088,3758,1961],{"class":1094},[1088,3760,3594],{"class":1108},[1088,3762,1000],{"class":1101},[1088,3764,2946],{"class":1108},[1088,3766,1729],{"class":1094},[1088,3768,1350],{"class":1325},[1088,3770,2423],{"class":1094},[1088,3772,3773],{"class":1325}," Infinity",[1088,3775,3776],{"class":1094}," :",[1088,3778,3733],{"class":1108},[1088,3780,3781],{"class":1094}," +",[1088,3783,3594],{"class":1108},[1088,3785,1000],{"class":1101},[1088,3787,2946],{"class":1108},[1088,3789,1224],{"class":1101},[1088,3791,3792],{"class":1090,"line":1313},[1088,3793,1410],{"emptyLinePlaceholder":1409},[1088,3795,3796],{"class":1090,"line":1319},[1088,3797,3798],{"class":1279},"    \u002F\u002F The search API refuses any request past its 1,000th result. A read that reaches past it is\n",[1088,3800,3801],{"class":1090,"line":1362},[1088,3802,3803],{"class":1279},"    \u002F\u002F still safe when fewer items match, so count the matches first and stop at the last one.\n",[1088,3805,3806,3808,3810,3813,3816,3818],{"class":1090,"line":1381},[1088,3807,1447],{"class":1094},[1088,3809,1450],{"class":1101},[1088,3811,3812],{"class":1108},"end",[1088,3814,3815],{"class":1094}," >",[1088,3817,3453],{"class":1325},[1088,3819,1464],{"class":1101},[1088,3821,3822,3825,3827,3830,3832,3835,3837,3839,3841,3843,3845,3847,3849,3851,3853,3855,3857],{"class":1090,"line":1400},[1088,3823,3824],{"class":1108},"        end",[1088,3826,1961],{"class":1094},[1088,3828,3829],{"class":1108}," Math",[1088,3831,1000],{"class":1101},[1088,3833,3834],{"class":1325},"min",[1088,3836,1329],{"class":1101},[1088,3838,3812],{"class":1108},[1088,3840,519],{"class":1101},[1088,3842,3704],{"class":1094},[1088,3844,3707],{"class":1325},[1088,3846,1329],{"class":1101},[1088,3848,3566],{"class":1108},[1088,3850,1000],{"class":1101},[1088,3852,3571],{"class":1108},[1088,3854,519],{"class":1101},[1088,3856,3720],{"class":1108},[1088,3858,3859],{"class":1101},"));\n",[1088,3861,3862],{"class":1090,"line":1406},[1088,3863,1410],{"emptyLinePlaceholder":1409},[1088,3865,3866,3868,3870,3872,3874,3876],{"class":1090,"line":1413},[1088,3867,1713],{"class":1094},[1088,3869,1450],{"class":1101},[1088,3871,3812],{"class":1108},[1088,3873,3815],{"class":1094},[1088,3875,3453],{"class":1325},[1088,3877,1464],{"class":1101},[1088,3879,3880],{"class":1090,"line":1444},[1088,3881,3882],{"class":1279},"            \u002F\u002F A valid read the API can't serve: the caller can change it, so the message says how.\n",[1088,3884,3885,3888,3890,3892,3894,3897,3900,3903],{"class":1090,"line":1467},[1088,3886,3887],{"class":1094},"            throw",[1088,3889,1662],{"class":1094},[1088,3891,1665],{"class":1325},[1088,3893,1329],{"class":1101},[1088,3895,3896],{"class":1220},"`The Art Institute search API returns only the first ${",[1088,3898,3899],{"class":1325},"SEARCH_WINDOW",[1088,3901,3902],{"class":1220},"} results. Narrow the filter or read an earlier page.`",[1088,3904,1378],{"class":1101},[1088,3906,3907],{"class":1090,"line":1484},[1088,3908,3909],{"class":1101},"        }\n",[1088,3911,3912],{"class":1090,"line":1529},[1088,3913,1532],{"class":1101},[1088,3915,3916],{"class":1090,"line":1535},[1088,3917,1410],{"emptyLinePlaceholder":1409},[1088,3919,3920,3922,3925,3927,3929,3931,3934,3936],{"class":1090,"line":1540},[1088,3921,1855],{"class":1094},[1088,3923,3924],{"class":1325}," select",[1088,3926,1961],{"class":1094},[1088,3928,3594],{"class":1108},[1088,3930,1000],{"class":1101},[1088,3932,3933],{"class":1108},"select",[1088,3935,2400],{"class":1094},[1088,3937,3938],{"class":1101}," [];\n",[1088,3940,3941,3943,3946,3948,3950,3953,3955,3957,3959,3962,3964,3966,3968,3970,3972,3974,3976,3978,3980,3982],{"class":1090,"line":1559},[1088,3942,1855],{"class":1094},[1088,3944,3945],{"class":1325}," body",[1088,3947,1961],{"class":1094},[1088,3949,1232],{"class":1101},[1088,3951,3952],{"class":1094},"...",[1088,3954,3720],{"class":1108},[1088,3956,519],{"class":1101},[1088,3958,956],{"class":1108},[1088,3960,3961],{"class":1101},", fields: ",[1088,3963,3933],{"class":1108},[1088,3965,1000],{"class":1101},[1088,3967,1501],{"class":1325},[1088,3969,1504],{"class":1101},[1088,3971,2026],{"class":1094},[1088,3973,1510],{"class":1101},[1088,3975,1513],{"class":1094},[1088,3977,1964],{"class":1325},[1088,3979,1329],{"class":1101},[1088,3981,2026],{"class":1108},[1088,3983,3984],{"class":1101},")) };\n",[1088,3986,3987,3989,3992,3994,3997,4000,4002],{"class":1090,"line":1575},[1088,3988,1855],{"class":1094},[1088,3990,3991],{"class":1325}," records",[1088,3993,815],{"class":1094},[1088,3995,3996],{"class":1325}," MonospaceObject",[1088,3998,3999],{"class":1101},"[] ",[1088,4001,1864],{"class":1094},[1088,4003,3938],{"class":1101},[1088,4005,4006],{"class":1090,"line":1620},[1088,4007,1410],{"emptyLinePlaceholder":1409},[1088,4009,4010,4012,4014,4017,4020,4022,4024,4027,4029,4032,4034,4036,4038,4041,4043],{"class":1090,"line":1625},[1088,4011,2380],{"class":1094},[1088,4013,1450],{"class":1101},[1088,4015,4016],{"class":1094},"let",[1088,4018,4019],{"class":1108}," from",[1088,4021,1961],{"class":1094},[1088,4023,3733],{"class":1108},[1088,4025,4026],{"class":1101},"; ",[1088,4028,1217],{"class":1108},[1088,4030,4031],{"class":1094}," \u003C",[1088,4033,3756],{"class":1108},[1088,4035,4026],{"class":1101},[1088,4037,1217],{"class":1108},[1088,4039,4040],{"class":1094}," +=",[1088,4042,3434],{"class":1325},[1088,4044,1464],{"class":1101},[1088,4046,4047,4050,4053,4055,4057,4059,4061,4063,4066,4068,4070,4073,4075],{"class":1090,"line":1630},[1088,4048,4049],{"class":1094},"        const",[1088,4051,4052],{"class":1325}," size",[1088,4054,1961],{"class":1094},[1088,4056,3829],{"class":1108},[1088,4058,1000],{"class":1101},[1088,4060,3834],{"class":1325},[1088,4062,1329],{"class":1101},[1088,4064,4065],{"class":1325},"PAGE_SIZE",[1088,4067,519],{"class":1101},[1088,4069,3812],{"class":1108},[1088,4071,4072],{"class":1094}," -",[1088,4074,4019],{"class":1108},[1088,4076,1378],{"class":1101},[1088,4078,4079,4081,4084,4086,4089,4092,4094,4096,4098,4100,4103,4105,4108,4110,4112,4114,4116],{"class":1090,"line":1650},[1088,4080,4049],{"class":1094},[1088,4082,4083],{"class":1325}," page",[1088,4085,1961],{"class":1094},[1088,4087,4088],{"class":1094}," await",[1088,4090,4091],{"class":1325}," search",[1088,4093,1329],{"class":1101},[1088,4095,3566],{"class":1108},[1088,4097,1000],{"class":1101},[1088,4099,3571],{"class":1108},[1088,4101,4102],{"class":1101},", { ",[1088,4104,3952],{"class":1094},[1088,4106,4107],{"class":1108},"body",[1088,4109,519],{"class":1101},[1088,4111,1217],{"class":1108},[1088,4113,519],{"class":1101},[1088,4115,722],{"class":1108},[1088,4117,4118],{"class":1101}," });\n",[1088,4120,4121,4124,4126,4129,4131,4133,4136,4138,4141,4143,4145,4147,4150,4152,4154,4157,4159,4161,4163,4165,4167,4169],{"class":1090,"line":1656},[1088,4122,4123],{"class":1108},"        records",[1088,4125,1000],{"class":1101},[1088,4127,4128],{"class":1325},"push",[1088,4130,1329],{"class":1101},[1088,4132,3952],{"class":1094},[1088,4134,4135],{"class":1108},"page",[1088,4137,1000],{"class":1101},[1088,4139,4140],{"class":1108},"data",[1088,4142,1000],{"class":1101},[1088,4144,1501],{"class":1325},[1088,4146,1504],{"class":1101},[1088,4148,4149],{"class":1094},"apiItem",[1088,4151,1510],{"class":1101},[1088,4153,1513],{"class":1094},[1088,4155,4156],{"class":1325}," toItem",[1088,4158,1329],{"class":1101},[1088,4160,4149],{"class":1108},[1088,4162,519],{"class":1101},[1088,4164,3933],{"class":1108},[1088,4166,519],{"class":1101},[1088,4168,3554],{"class":1108},[1088,4170,4171],{"class":1101},")));\n",[1088,4173,4174,4176,4178,4180,4182,4184,4186,4189,4191,4193,4195,4198],{"class":1090,"line":1681},[1088,4175,1713],{"class":1094},[1088,4177,1450],{"class":1101},[1088,4179,4135],{"class":1108},[1088,4181,1000],{"class":1101},[1088,4183,4140],{"class":1108},[1088,4185,1000],{"class":1101},[1088,4187,4188],{"class":1349},"length",[1088,4190,4031],{"class":1094},[1088,4192,4052],{"class":1108},[1088,4194,1510],{"class":1101},[1088,4196,4197],{"class":1094},"break",[1088,4199,1224],{"class":1101},[1088,4201,4202],{"class":1090,"line":1686},[1088,4203,1532],{"class":1101},[1088,4205,4206],{"class":1090,"line":1691},[1088,4207,1410],{"emptyLinePlaceholder":1409},[1088,4209,4210,4212,4214,4217],{"class":1090,"line":1710},[1088,4211,1384],{"class":1094},[1088,4213,1232],{"class":1101},[1088,4215,4216],{"class":1108},"records",[1088,4218,4219],{"class":1101}," };\n",[1088,4221,4222],{"class":1090,"line":1774},[1088,4223,1403],{"class":1101},[458,4225,4226],{},"The connector translates the filter and the sort before the count, so an undeclared sort on an unbounded read is refused before any request. It refuses only a read that needs a result past the window. When the read reaches past result 1,000, it first asks for the total and clamps the end to it:",[488,4228,4229,4238],{},[491,4230,4231],{},[494,4232,4233,4236],{},[497,4234,4235],{},"Request",[497,4237,3096],{},[507,4239,4240,4250,4261,4271,4280],{},[494,4241,4242,4247],{},[512,4243,4244],{},[516,4245,4246],{},"offset=995&limit=5",[512,4248,4249],{},"Served",[494,4251,4252,4258],{},[512,4253,4254,4257],{},[516,4255,4256],{},"offset=995&limit=10",", unfiltered",[512,4259,4260],{},"Refused after one count request",[494,4262,4263,4268],{},[512,4264,4265,4257],{},[516,4266,4267],{},"limit=-1",[512,4269,4270],{},"Refused",[494,4272,4273,4278],{},[512,4274,4275,4277],{},[516,4276,4267],{}," on a filter with 13 matches",[512,4279,4249],{},[494,4281,4282,4287],{},[512,4283,4284,4277],{},[516,4285,4286],{},"offset=5000",[512,4288,4289],{},"An empty page, not a refusal",[458,4291,4292],{},"Never return a silently shortened page. A caller can't tell it from the end of the data.",[1046,4294,2886],{"id":4295},"bound-unbounded-reads",[458,4297,4298,4299,4301,4302,2899,4304,4307,4308,4311,4312,4314,4315,4319,4320,4322,4323,4325,4326,4328,4329,4331,4332,2899,4334,4336,4337,4339,4340,1000],{},"Some reads arrive with no ",[516,4300,2946],{},": a caller's ",[516,4303,4267],{},[516,4305,4306],{},"limit=0"," when the ",[462,4309,4310],{},"instance"," (your Monospace deployment) sets no maximum page size, and the batched ",[516,4313,543],{}," read Engine sends to resolve a ",[471,4316,4318],{"href":4317},"\u002Fconcepts\u002Fdata-model#relations","to-one relation",", which returns a single related item. A to-many relation, which returns several, arrives as one read per parent with a ",[516,4321,2946],{}," by default. It arrives as one batched read without ",[516,4324,2946],{}," only when the include resolves to no ",[516,4327,2946],{}," and no ",[516,4330,588],{},": the include sets ",[516,4333,4267],{},[516,4335,4306],{}," on an instance without a maximum, or sets no ",[516,4338,2946],{}," on an instance that unsets its default. See ",[471,4341,4343],{"href":4342},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Fapi#relation-reads","Relation Reads",[458,4345,4346,4347,4350,4351,2669,4353,4355],{},"Put a budget on what one read may fetch, and refuse when it runs out. The Stripe connector charges each object it takes from a list, and each id it retrieves, against ",[516,4348,4349],{},"maxItemsPerQuery"," (5,000 by default, configurable per data source). It refuses once the budget runs out, and an ",[516,4352,551],{},[516,4354,543],{}," filter over the budget is refused before its first request. Its keyed operations retrieve one id and skip the budget.",[458,4357,4358,4359,4361,4362,4364],{},"A budget like this bounds one read, not one caller query. A to-many include that resolves to a ",[516,4360,2946],{}," or an ",[516,4363,588],{},", as it does by default, arrives as one read per parent, so a single caller query can spend the budget once per parent. It also doesn't cap network traffic exactly. The connector charges objects as it takes them from a page it has already fetched, so the request that runs the budget out can bring a whole page of up to 100 objects. Concurrent list requests each bring their own page.",[1046,4366,4368],{"id":4367},"offer-counts-only-when-the-external-data-source-counts","Offer Counts Only When the External Data Source Counts",[458,4370,4371,4372,4374,4375,957,4378,519,4381,4328,4384,4386,4387,4390,4391,544,4393,4395,4396,1000],{},"Declare ",[516,4373,974],{}," only if the external data source returns an exact total. A count read arrives as ",[516,4376,4377],{},"readMany",[516,4379,4380],{},"count: true",[516,4382,4383],{},"limit: 0",[516,4385,3933],{},". Answer with ",[516,4388,4389],{},"totalCount"," for the whole filter, ignoring ",[516,4392,2946],{},[516,4394,588],{},", and return no items. See ",[471,4397,4399],{"href":4398},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Fapi#count-reads","Count Reads",[458,4401,4402,4403,4405],{},"The Art Institute connector answers a count read with the same query at ",[516,4404,980],{},". Stripe's list endpoints return no total, so the Stripe connector doesn't declare it and refuses a count read.",[454,4407,4409],{"id":4408},"refuse-reads-that-lack-a-required-filter","Refuse Reads That Lack a Required Filter",[458,4411,4412],{},"Some lists in an external data source can't be read unfiltered, such as messages that need a channel or items that need a parent. The contract can't declare a required filter, so callers and Studio still offer the unfiltered read.",[2661,4414,4415,4424,4433,4443],{},[2664,4416,4417,4420,4421,4423],{},[462,4418,4419],{},"Refuse early."," Check the filter before any request to the external data source, and throw ",[516,4422,1240],{}," when the required constraint is missing. The request is valid by the declaration, and the caller can fix it.",[2664,4425,4426,4429,4430,4432],{},[462,4427,4428],{},"Check every branch."," An ",[516,4431,1062],{}," whose branches don't all carry the constraint is unfiltered too.",[2664,4434,4435,4438,4439,4442],{},[462,4436,4437],{},"Name the fix."," Put the missing filter in the message (e.g., \"Filter messages by ",[516,4440,4441],{},"channelId",".\"). The message is the only explanation the caller sees.",[2664,4444,4445,4448],{},[462,4446,4447],{},"Document it"," in your connector's README, next to the capability table.",[458,4450,4451,4452,1000],{},"The same applies to bounds the external data source enforces, such as a maximum date range. See ",[471,4453,4455],{"href":4454},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Flimitations#required-filters","Required Filters",[454,4457,4459],{"id":4458},"answer-keyed-operations","Answer Keyed Operations",[458,4461,4462,519,4465,544,4468,4471],{},[516,4463,4464],{},"readOne",[516,4466,4467],{},"updateOne",[516,4469,4470],{},"deleteOne"," address one item by its key, plus an optional guard filter. Two implementations work.",[458,4473,4474,4477,4478,4481,4482,4485,4486,815],{},[462,4475,4476],{},"Route the key and guard through the read path."," When your read path already evaluates filters, combine the key and the guard into one filter. The Art Institute connector reads with ",[516,4479,4480],{},"limit: 1",", after checking the key's shape, and answers the item it finds as ",[516,4483,4484],{},"record",", or ",[516,4487,4488],{},"record: null",[1078,4490,4492],{"className":1080,"code":4491,"filename":1082,"language":1083,"meta":1084,"style":1084},"\u002F**\n * Answers `readOne` through the read path: the key, plus the optional guard, at most one item. When\n * nothing matches, it answers `record: null`, which callers see as 404.\n *\u002F\nexport async function readOne(query: ReadOneOperationSerialized): Promise\u003CQueryResultFor\u003C'readOne'>> {\n    const id = query.key.id;\n\n    if (Object.keys(query.key).length !== 1 || id === undefined) {\n        throw new Error(`\\`${query.collection}\\` keys are a single \\`id\\`.`);\n    }\n\n    \u002F\u002F An id that isn't an integer can't name an item: no item, without asking the API.\n    if (typeof id !== 'number' || !Number.isSafeInteger(id)) return { record: null };\n\n    const key: FilterSerialized = { cmp: 'equals', left: { field: 'id' }, right: { value: id } };\n    const filter = query.filter === undefined ? key : { and: [key, query.filter] };\n    const { records = [] } = await readMany({ op: 'readMany', collection: query.collection, select: query.select, filter, limit: 1 });\n    return { record: records[0] ?? null };\n}\n",[516,4493,4494,4498,4503,4508,4512,4549,4571,4575,4616,4656,4660,4664,4669,4712,4716,4748,4788,4842,4865],{"__ignoreMap":1084},[1088,4495,4496],{"class":1090,"line":1091},[1088,4497,1286],{"class":1279},[1088,4499,4500],{"class":1090,"line":1105},[1088,4501,4502],{"class":1279}," * Answers `readOne` through the read path: the key, plus the optional guard, at most one item. When\n",[1088,4504,4505],{"class":1090,"line":1115},[1088,4506,4507],{"class":1279}," * nothing matches, it answers `record: null`, which callers see as 404.\n",[1088,4509,4510],{"class":1090,"line":1123},[1088,4511,1316],{"class":1279},[1088,4513,4514,4516,4518,4520,4523,4525,4527,4530,4532,4534,4536,4538,4541,4543,4546],{"class":1090,"line":1131},[1088,4515,3508],{"class":1094},[1088,4517,3511],{"class":1094},[1088,4519,3514],{"class":1094},[1088,4521,4522],{"class":1325}," readOne",[1088,4524,1329],{"class":1101},[1088,4526,3522],{"class":1094},[1088,4528,4529],{"class":1325}," ReadOneOperationSerialized",[1088,4531,523],{"class":1101},[1088,4533,815],{"class":1094},[1088,4535,3532],{"class":1325},[1088,4537,2842],{"class":1101},[1088,4539,4540],{"class":1325},"QueryResultFor",[1088,4542,2842],{"class":1101},[1088,4544,4545],{"class":1220},"'readOne'",[1088,4547,4548],{"class":1101},">> {\n",[1088,4550,4551,4553,4556,4558,4560,4562,4565,4567,4569],{"class":1090,"line":1139},[1088,4552,1855],{"class":1094},[1088,4554,4555],{"class":1325}," id",[1088,4557,1961],{"class":1094},[1088,4559,3594],{"class":1108},[1088,4561,1000],{"class":1101},[1088,4563,4564],{"class":1108},"key",[1088,4566,1000],{"class":1101},[1088,4568,551],{"class":1108},[1088,4570,1224],{"class":1101},[1088,4572,4573],{"class":1090,"line":1147},[1088,4574,1410],{"emptyLinePlaceholder":1409},[1088,4576,4577,4579,4581,4583,4585,4588,4590,4592,4594,4596,4599,4601,4603,4606,4608,4610,4612,4614],{"class":1090,"line":1155},[1088,4578,1447],{"class":1094},[1088,4580,1450],{"class":1101},[1088,4582,2591],{"class":1108},[1088,4584,1000],{"class":1101},[1088,4586,4587],{"class":1325},"keys",[1088,4589,1329],{"class":1101},[1088,4591,3566],{"class":1108},[1088,4593,1000],{"class":1101},[1088,4595,4564],{"class":1108},[1088,4597,4598],{"class":1101},").",[1088,4600,4188],{"class":1349},[1088,4602,1459],{"class":1094},[1088,4604,4605],{"class":1325}," 1",[1088,4607,1734],{"class":1094},[1088,4609,4555],{"class":1108},[1088,4611,1729],{"class":1094},[1088,4613,1350],{"class":1325},[1088,4615,1464],{"class":1101},[1088,4617,4618,4620,4622,4624,4626,4629,4631,4633,4635,4637,4639,4641,4643,4646,4648,4650,4652,4654],{"class":1090,"line":1163},[1088,4619,1659],{"class":1094},[1088,4621,1662],{"class":1094},[1088,4623,2618],{"class":1325},[1088,4625,1329],{"class":1101},[1088,4627,4628],{"class":1220},"`",[1088,4630,2209],{"class":1325},[1088,4632,2212],{"class":1220},[1088,4634,3566],{"class":1108},[1088,4636,1000],{"class":1220},[1088,4638,3571],{"class":1108},[1088,4640,2221],{"class":1220},[1088,4642,2209],{"class":1325},[1088,4644,4645],{"class":1220}," keys are a single ",[1088,4647,2209],{"class":1325},[1088,4649,551],{"class":1220},[1088,4651,2209],{"class":1325},[1088,4653,2226],{"class":1220},[1088,4655,1378],{"class":1101},[1088,4657,4658],{"class":1090,"line":1171},[1088,4659,1532],{"class":1101},[1088,4661,4662],{"class":1090,"line":1179},[1088,4663,1410],{"emptyLinePlaceholder":1409},[1088,4665,4666],{"class":1090,"line":1187},[1088,4667,4668],{"class":1279},"    \u002F\u002F An id that isn't an integer can't name an item: no item, without asking the API.\n",[1088,4670,4671,4673,4675,4678,4680,4682,4685,4687,4689,4692,4694,4697,4699,4701,4703,4705,4708,4710],{"class":1090,"line":1195},[1088,4672,1447],{"class":1094},[1088,4674,1450],{"class":1101},[1088,4676,4677],{"class":1094},"typeof",[1088,4679,4555],{"class":1108},[1088,4681,1459],{"class":1094},[1088,4683,4684],{"class":1220}," 'number'",[1088,4686,1734],{"class":1094},[1088,4688,1737],{"class":1094},[1088,4690,4691],{"class":1108},"Number",[1088,4693,1000],{"class":1101},[1088,4695,4696],{"class":1325},"isSafeInteger",[1088,4698,1329],{"class":1101},[1088,4700,551],{"class":1108},[1088,4702,1757],{"class":1101},[1088,4704,2020],{"class":1094},[1088,4706,4707],{"class":1101}," { record: ",[1088,4709,2755],{"class":1325},[1088,4711,4219],{"class":1101},[1088,4713,4714],{"class":1090,"line":1203},[1088,4715,1410],{"emptyLinePlaceholder":1409},[1088,4717,4718,4720,4723,4725,4727,4729,4732,4735,4738,4741,4744,4746],{"class":1090,"line":1211},[1088,4719,1855],{"class":1094},[1088,4721,4722],{"class":1325}," key",[1088,4724,815],{"class":1094},[1088,4726,1335],{"class":1325},[1088,4728,1961],{"class":1094},[1088,4730,4731],{"class":1101}," { cmp: ",[1088,4733,4734],{"class":1220},"'equals'",[1088,4736,4737],{"class":1101},", left: { field: ",[1088,4739,4740],{"class":1220},"'id'",[1088,4742,4743],{"class":1101}," }, right: { value: ",[1088,4745,551],{"class":1108},[1088,4747,1617],{"class":1101},[1088,4749,4750,4752,4754,4756,4758,4760,4762,4764,4766,4768,4770,4772,4775,4777,4779,4781,4783,4785],{"class":1090,"line":1227},[1088,4751,1855],{"class":1094},[1088,4753,1867],{"class":1325},[1088,4755,1961],{"class":1094},[1088,4757,3594],{"class":1108},[1088,4759,1000],{"class":1101},[1088,4761,1370],{"class":1108},[1088,4763,1729],{"class":1094},[1088,4765,1350],{"class":1325},[1088,4767,2423],{"class":1094},[1088,4769,4722],{"class":1108},[1088,4771,3776],{"class":1094},[1088,4773,4774],{"class":1101}," { and: [",[1088,4776,4564],{"class":1108},[1088,4778,519],{"class":1101},[1088,4780,3566],{"class":1108},[1088,4782,1000],{"class":1101},[1088,4784,1370],{"class":1108},[1088,4786,4787],{"class":1101},"] };\n",[1088,4789,4790,4792,4794,4796,4798,4801,4803,4805,4807,4810,4813,4816,4818,4820,4822,4825,4827,4829,4831,4833,4835,4838,4840],{"class":1090,"line":1257},[1088,4791,1855],{"class":1094},[1088,4793,1232],{"class":1101},[1088,4795,4216],{"class":1325},[1088,4797,1961],{"class":1094},[1088,4799,4800],{"class":1101}," [] } ",[1088,4802,1864],{"class":1094},[1088,4804,4088],{"class":1094},[1088,4806,3517],{"class":1325},[1088,4808,4809],{"class":1101},"({ op: ",[1088,4811,4812],{"class":1220},"'readMany'",[1088,4814,4815],{"class":1101},", collection: ",[1088,4817,3566],{"class":1108},[1088,4819,1000],{"class":1101},[1088,4821,3571],{"class":1108},[1088,4823,4824],{"class":1101},", select: ",[1088,4826,3566],{"class":1108},[1088,4828,1000],{"class":1101},[1088,4830,3933],{"class":1108},[1088,4832,519],{"class":1101},[1088,4834,1370],{"class":1108},[1088,4836,4837],{"class":1101},", limit: ",[1088,4839,1614],{"class":1325},[1088,4841,4118],{"class":1101},[1088,4843,4844,4846,4848,4850,4852,4855,4858,4861,4863],{"class":1090,"line":1276},[1088,4845,1384],{"class":1094},[1088,4847,4707],{"class":1101},[1088,4849,4216],{"class":1108},[1088,4851,2556],{"class":1101},[1088,4853,4854],{"class":1325},"0",[1088,4856,4857],{"class":1101},"] ",[1088,4859,4860],{"class":1094},"??",[1088,4862,1836],{"class":1325},[1088,4864,4219],{"class":1101},[1088,4866,4867],{"class":1090,"line":1283},[1088,4868,1403],{"class":1101},[458,4870,4871,4874,4875,4878,4879,1000],{},[462,4872,4873],{},"Retrieve by key, then check the guard in memory."," When the external data source has a get-by-id endpoint, fetch the item and evaluate the guard on it. The Stripe connector's ",[516,4876,4877],{},"findByKey"," works this way; see ",[471,4880,578],{"href":577},[458,4882,4883],{},"Either way:",[2661,4885,4886,4902,4908,4922,4937,4950,4975],{},[2664,4887,4888,4893,4894,4897,4898,4901],{},[462,4889,4890,4891,1000],{},"A miss answers ",[516,4892,4488],{}," An unknown key, a deleted item and a guard miss all answer ",[516,4895,4896],{},"{ record: null }",", and change nothing. A miss isn't an error, so don't throw. Engine answers the caller with ",[516,4899,4900],{},"404"," \"No record found\".",[2664,4903,4904,4907],{},[462,4905,4906],{},"Check the guard before any write."," Read the item, evaluate the guard, then write only what you read.",[2664,4909,4910,4913,4914,4916,4917,4645,4919,4921],{},[462,4911,4912],{},"Check the key's shape."," The key has one entry, the primary-key field. Engine sends only the declared key, so refuse anything else with a plain ",[516,4915,2678],{}," (e.g., \"",[516,4918,518],{},[516,4920,551],{},".\").",[2664,4923,4924,4927,4928,4930,4931,544,4934,1000],{},[462,4925,4926],{},"Decide an out-of-domain key locally."," Answer it as a miss, without a request. The Art Institute connector answers ",[516,4929,4488],{}," for an id that isn't a safe integer. The Stripe connector does the same for an id that isn't a string of letters, digits, ",[516,4932,4933],{},"_",[516,4935,4936],{},"-",[2664,4938,4939,4946,4947,1000],{},[462,4940,4941,4942,4945],{},"Read an ",[516,4943,4944],{},"int64"," key as a string."," It arrives as a string, so never test it with ",[516,4948,4949],{},"typeof key === 'number'",[2664,4951,4952,4960,4961,4964,4965,4968,4969,4972,4973,1000],{},[462,4953,4954,4955,4957,4958,1000],{},"Answer ",[516,4956,4484],{},", never ",[516,4959,4216],{}," Engine fails a keyed operation that answers ",[516,4962,4963],{},"{ records: [...] }",", even with one item. It checks after your connector has run, so a write it made stays applied. Type the function's answer as ",[516,4966,4967],{},"QueryResultFor\u003C'readOne'>"," (or the operation you serve), and ",[516,4970,4971],{},"tsc"," refuses ",[516,4974,4216],{},[2664,4976,4977,4980,4981,4984],{},[462,4978,4979],{},"Treat tombstones as missing."," Stripe still returns a deleted customer, with ",[516,4982,4983],{},"deleted: true",", and the Stripe connector treats it as a miss.",[454,4986,4988],{"id":4987},"write-safely","Write Safely",[2661,4990,4991,5023,5046,5056,5062,5068,5074,5080],{},[2664,4992,4993,4996,4997,4999,5000,5004,5005,5008,5009,544,5012,5015,5016,5018,5019,1000],{},[462,4994,4995],{},"Declare what your writes return."," When your connector answers each kind of write with the items it wrote, carrying every field in ",[516,4998,3933],{},", declare the matching ",[471,5001,5003],{"href":5002},"\u002Freference\u002Fextensions\u002Fmanifest#capabilities","query capability",". The capabilities apply to every collection the entry serves. The Stripe connector declares all three. Without a capability, Engine makes extra calls by stable ID: it asks ",[516,5006,5007],{},"create"," for the stable IDs and reads the created items back, and it sends updates and deletes through ",[516,5010,5011],{},"updateMany",[516,5013,5014],{},"deleteMany",", between ",[516,5017,4377],{}," calls. That needs a stable ID and declarations that carry those calls. See ",[471,5020,5022],{"href":5021},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Foperations#declare-query-capabilities","Declare Query Capabilities",[2664,5024,5025,5028,5029,5031,5032,5034,5035,5038,5039,5042,5043,5045],{},[462,5026,5027],{},"Return the selected fields."," Answer every write with the items it wrote, carrying every field in ",[516,5030,3933],{},", with or without a capability. The capabilities change what Engine selects: a ",[516,5033,5007],{}," without ",[516,5036,5037],{},"insertReturning"," selects only the stable ID, and a write with ",[516,5040,5041],{},"select: []"," can answer with no ",[516,5044,4216],{},". When the external data source returns nothing useful (Stripe answers a delete with a tombstone), read the selected fields first, then write.",[2664,5047,5048,5051,5052,544,5054,1000],{},[462,5049,5050],{},"Write by id when the external data source can't write by filter."," Resolve the filter with your own read, then write each id. The Stripe connector does this for ",[516,5053,5011],{},[516,5055,5014],{},[2664,5057,5058,5061],{},[462,5059,5060],{},"Accept that writes aren't atomic."," Engine runs no transaction across connector writes. A failure partway leaves earlier writes applied, and a concurrent writer can change an item between your read and your write. Document it.",[2664,5063,5064,5067],{},[462,5065,5066],{},"Treat a failed request's outcome as unknown."," A write that times out, or whose answer can't be read, can still have been applied. A count of acknowledged writes is a lower bound, so reconcile by reading, or use idempotency keys where the external data source offers them.",[2664,5069,5070,5073],{},[462,5071,5072],{},"Stop at the first failure, and say how far you got."," In the Stripe connector's fan-outs, no new write starts after a failure; the writes already in flight finish, then the error is thrown. For a write of several items, the message says how many were written and keeps the error's class: \"Deleted 3 of 4 products before this error: This product cannot be deleted because it has one or more user-created prices.\"",[2664,5075,5076,5079],{},[462,5077,5078],{},"Bound concurrency."," The Stripe connector runs each fan-out with at most four concurrent requests. That caps concurrency, not requests per second, and the cap is per fan-out, so nested fan-outs and concurrent queries add up. Staying under a requests-per-second limit needs pacing of its own.",[2664,5081,5082,5085,5086,1000],{},[462,5083,5084],{},"Map refusals from the external data source to constraint errors."," Stripe refuses to delete a product that has prices. The Stripe connector reports it as ",[516,5087,5088],{},"ForeignKeyConstraintViolation",[458,5090,5091],{},"Defaults protect data the caller didn't mean to touch:",[2661,5093,5094,5104,5112],{},[2664,5095,5096,5099,5100,5103],{},[462,5097,5098],{},"Default writes off for credentials that reach production data."," The Stripe connector's ",[516,5101,5102],{},"enableWrites"," defaults to on only for test-mode keys. With writes off, it declares a read-only schema, so Engine refuses writes before they reach the connector.",[2664,5105,5106,5109,5110,1000],{},[462,5107,5108],{},"Give side effects their own switch."," When creating an item sends something (an email, an SMS, a charge), gate it behind a configuration option separate from ",[516,5111,5102],{},[2664,5113,5114,5117,5118,1000],{},[462,5115,5116],{},"Don't repeat side effects on retry."," If the external data source supports idempotency keys, send one per item and reuse it when you retry after a network error or a ",[516,5119,5120],{},"5xx",[458,5122,5123],{},"The Stripe example keeps its request path small: it sends no idempotency keys and doesn't retry. Every request has a 10-second deadline. Add idempotency keys and retries where your external data source needs them.",[454,5125,5127],{"id":5126},"map-values-types-and-keys","Map Values, Types and Keys",[1046,5129,5131],{"id":5130},"declare-a-static-schema","Declare a Static Schema",[458,5133,5134],{},"When the external data source has no metadata API, declare the schema in code.",[2661,5136,5137,5164,5174,5180],{},[2664,5138,5139,5142,5143,957,5146,5149,5150,5153,5154,5156,5157,5159,5160,5163],{},[462,5140,5141],{},"Declare each collection statically."," Write the ",[516,5144,5145],{},"operations",[516,5147,5148],{},"fields.all()"," for every field, or ",[516,5151,5152],{},"fieldsOf(fields).pick(...)"," for a list several members share, so ",[516,5155,4971],{}," catches a typo. A field is writable exactly when a ",[516,5158,4140],{}," list names it; the kit derives the read-only flags. The Stripe connector's ",[516,5161,5162],{},"collections.ts"," does this.",[2664,5165,5166,5169,5170,5173],{},[462,5167,5168],{},"Keep the mapping next to the declaration."," Key it by the declared field names, so a typo there is a type error too. The Stripe connector's ",[516,5171,5172],{},"resources.ts"," holds each field's path in the Stripe object, its form key and its list parameter, and reads what the field can do from the declaration.",[2664,5175,5176,5179],{},[462,5177,5178],{},"Pin the API version"," in every request, and change the pinned version and the field paths together.",[2664,5181,5182,5185],{},[462,5183,5184],{},"Check for drift."," Test that every declared field reads a value at its path on a live object.",[1046,5187,5189],{"id":5188},"map-types-from-metadata","Map Types from Metadata",[458,5191,5192],{},"When the external data source describes its schema, derive the collections from it at introspection.",[2661,5194,5195,5227,5257,5263,5269],{},[2664,5196,5197,5200,5201,5204,5205,5208,5209,5212,5213,5216,5217,2899,5220,5222,5223,1000],{},[462,5198,5199],{},"Map each type the external data source reports to a field type,"," and degrade honestly. A decimal becomes ",[516,5202,5203],{},"decimal",", returned as a string: ",[516,5206,5207],{},"field.decimal({ precision, scale })"," when the external data source declares both, and ",[516,5210,5211],{},"field.decimal()"," when it declares neither. An unknown type becomes ",[516,5214,5215],{},"unsupported",", which no filter, ",[516,5218,5219],{},"output",[516,5221,4140],{}," list can name, so callers can't read it. See ",[471,5224,5226],{"href":5225},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Foperations#match-operators-to-field-types","Match Operators to Field Types",[2664,5228,5229,5232,5233,5235,5236,5239,5240,5243,5244,5246,5247,544,5249,5252,5253,5256],{},[462,5230,5231],{},"Check value ranges before you pick a type."," The Art Institute's ",[516,5234,906],{}," isn't limited to plausible years: it holds values such as ",[516,5237,5238],{},"-1824528578",", which still fits ",[516,5241,5242],{},"int32",". The connector declares its ids and years as ",[516,5245,5242],{},", so they travel as JSON numbers in data, filters, keys and REST responses. Engine sends ",[516,5248,4944],{},[516,5250,5251],{},"uint64"," values as decimal strings in item data, filter values and keys, accepts a string or a number back, and returns them to callers as strings. Return values outside JavaScript's safe-integer range as decimal strings. That keeps answers exact only if you read them exactly: ",[516,5254,5255],{},"JSON.parse"," has already rounded such a number, so parse the external data source's response in a way that keeps the digits, or fail on the value.",[2664,5258,5259,5262],{},[462,5260,5261],{},"Derive nullability from data"," where the metadata is silent. Count items where the field is missing. A field with missing values must be nullable, but finding none doesn't prove future values are present. Declare a field non-nullable only where the external data source guarantees a value.",[2664,5264,5265,5268],{},[462,5266,5267],{},"Declare only keys you can prove."," Declare a primary or unique index only where the external data source guarantees it. Keyed operations need a single-field primary key. When the external data source identifies an item by several values, expose one string field that combines them as the primary key, and split it again in the connector; an index over several fields doesn't enable keyed operations.",[2664,5270,5271,5274,5275,5277,5278,544,5280,5282,5283,519,5285,957,5287,544,5289,5291,5292,5294,5295,5297,5298,5300,5301,544,5303,5305,5306,5308,5309,5312,5313,5315,5316,544,5320,1000],{},[462,5272,5273],{},"Declare relations with key fields of the same type."," Engine offers each direction only when the target collection can filter by the key and its members list the key fields in ",[516,5276,5219],{},". For a single key field, the filter needs ",[516,5279,543],{},[516,5281,547],{},"; without ",[516,5284,543],{},[516,5286,540],{},[516,5288,547],{},[516,5290,1062],{}," also works, and Engine then sends one ",[516,5293,540],{}," per key value joined with ",[516,5296,1062],{},". For a key of several fields, it needs ",[516,5299,540],{}," on each field, with ",[516,5302,547],{},[516,5304,1062],{},". So one direction can stay available when the other can't. A key whose type can't take ",[516,5307,543],{}," (e.g., a single ",[516,5310,5311],{},"boolean"," field), or that contains an ",[516,5314,5215],{}," field, is exempt from the filter requirement, and Engine sends its relation reads without a declaration; see ",[471,5317,5319],{"href":5318},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Foperations#rules-for-engines-own-calls","Rules for Engine's Own Calls",[471,5321,5323],{"href":5322},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Foperations#resolve-relations","Resolve Relations",[1046,5325,5327],{"id":5326},"normalize-values","Normalize Values",[458,5329,5330,5331,5333,5334,5336,5337,5339,5340,5342],{},"Return every value in the encoding its declared type expects, and project exactly the fields in ",[516,5332,3933],{},". Carry every selected field: use an explicit ",[516,5335,2755],{}," for a missing value, because a missing field fails the request. Check each value against its field's type and nullability as you convert it, so a changed API fails in your connector instead of with a type error in Engine. Engine doesn't check nullability at all: it accepts ",[516,5338,2755],{}," for a non-nullable field. Throw a plain ",[516,5341,2678],{}," for it: the caller can't fix a changed API, and Engine reports it as a failed query. The Stripe connector fails on a missing value for a field that isn't nullable, and converts Unix-second timestamps to RFC 3339, failing on a number too large for a date:",[1078,5344,5347],{"className":1080,"code":5345,"filename":5346,"language":1083,"meta":1084,"style":1084},"import type { MonospaceObject, Value } from '@monospace\u002Fextension-kit\u002Fdata-connector';\nimport type { StripeObject } from '.\u002Fclient';\nimport type { FieldSpec, ResourceSpec } from '.\u002Fresources';\nimport { QueryRejected } from '@monospace\u002Fextension-kit\u002Fdata-connector';\nimport { isObject } from '.\u002Fclient';\nimport { apiFieldName } from '.\u002Fresources';\n\n\u002F** An item, with the Stripe id the write code addresses its object by. *\u002F\nexport type Item = MonospaceObject & { readonly id: string };\n\n\u002F** Converts a Stripe object to an item with every declared field, each checked against its kind. *\u002F\nexport function toItem(resource: ResourceSpec, object: StripeObject): Item {\n    const item: MonospaceObject = {};\n    for (const field of resource.fields) item[field.name] = toValue(field, readPath(object, field.path ?? [apiFieldName(field.name)]));\n    return { ...item, id: object.id };\n}\n\u002F\u002F …\nfunction readPath(object: StripeObject, path: readonly string[]): unknown {\n    let value: unknown = object;\n\n    for (const key of path) {\n        if (!isObject(value)) return undefined;\n        value = value[key];\n    }\n\n    return value;\n}\n\nfunction toValue(field: FieldSpec, raw: unknown): Value {\n    if (raw === undefined || raw === null) {\n        if (field.isNullable) return null;\n        throw new Error(`Stripe returned no value for \\`${field.name}\\`.`);\n    }\n\n    switch (field.kind) {\n        case 'id':\n        case 'string':\n            if (typeof raw === 'string') return raw;\n            break;\n        case 'int64':\n            if (typeof raw === 'number' && Number.isSafeInteger(raw)) return raw;\n            break;\n        case 'boolean':\n            if (typeof raw === 'boolean') return raw;\n            break;\n        case 'timestamp': {\n            \u002F\u002F Stripe sends Unix seconds. Engine reads RFC 3339. A number outside the range `Date` can\n            \u002F\u002F hold makes an invalid date, whose `toISOString` would throw a plain `RangeError`.\n            const date = typeof raw === 'number' ? new Date(raw * 1000) : undefined;\n            if (date !== undefined && !Number.isNaN(date.getTime())) return date.toISOString();\n            break;\n        }\n    }\n\n    \u002F\u002F A changed API, not something the caller can fix: a plain error, which Engine reports as a failed query.\n    throw new Error(`Stripe returned an unexpected value for \\`${field.name}\\`.`);\n}\n","src\u002Fdata-connectors\u002Fstripe\u002Fitems.ts",[516,5348,5349,5373,5393,5418,5434,5451,5467,5471,5476,5504,5508,5513,5545,5561,5641,5662,5666,5670,5703,5720,5724,5741,5764,5780,5784,5788,5796,5800,5804,5833,5857,5878,5909,5913,5917,5932,5942,5951,5974,5981,5990,6026,6032,6041,6063,6069,6079,6084,6089,6132,6180,6186,6190,6194,6198,6203,6235],{"__ignoreMap":1084},[1088,5350,5351,5353,5355,5357,5360,5362,5365,5367,5369,5371],{"class":1090,"line":1091},[1088,5352,1095],{"class":1094},[1088,5354,1098],{"class":1094},[1088,5356,1232],{"class":1101},[1088,5358,5359],{"class":1108},"MonospaceObject",[1088,5361,519],{"class":1101},[1088,5363,5364],{"class":1108},"Value",[1088,5366,1248],{"class":1101},[1088,5368,1217],{"class":1094},[1088,5370,1221],{"class":1220},[1088,5372,1224],{"class":1101},[1088,5374,5375,5377,5379,5381,5384,5386,5388,5391],{"class":1090,"line":1105},[1088,5376,1095],{"class":1094},[1088,5378,1098],{"class":1094},[1088,5380,1232],{"class":1101},[1088,5382,5383],{"class":1108},"StripeObject",[1088,5385,1248],{"class":1101},[1088,5387,1217],{"class":1094},[1088,5389,5390],{"class":1220}," '.\u002Fclient'",[1088,5392,1224],{"class":1101},[1088,5394,5395,5397,5399,5401,5404,5406,5409,5411,5413,5416],{"class":1090,"line":1115},[1088,5396,1095],{"class":1094},[1088,5398,1098],{"class":1094},[1088,5400,1232],{"class":1101},[1088,5402,5403],{"class":1108},"FieldSpec",[1088,5405,519],{"class":1101},[1088,5407,5408],{"class":1108},"ResourceSpec",[1088,5410,1248],{"class":1101},[1088,5412,1217],{"class":1094},[1088,5414,5415],{"class":1220}," '.\u002Fresources'",[1088,5417,1224],{"class":1101},[1088,5419,5420,5422,5424,5426,5428,5430,5432],{"class":1090,"line":1123},[1088,5421,1095],{"class":1094},[1088,5423,1232],{"class":1101},[1088,5425,1240],{"class":1108},[1088,5427,1248],{"class":1101},[1088,5429,1217],{"class":1094},[1088,5431,1221],{"class":1220},[1088,5433,1224],{"class":1101},[1088,5435,5436,5438,5440,5443,5445,5447,5449],{"class":1090,"line":1131},[1088,5437,1095],{"class":1094},[1088,5439,1232],{"class":1101},[1088,5441,5442],{"class":1108},"isObject",[1088,5444,1248],{"class":1101},[1088,5446,1217],{"class":1094},[1088,5448,5390],{"class":1220},[1088,5450,1224],{"class":1101},[1088,5452,5453,5455,5457,5459,5461,5463,5465],{"class":1090,"line":1139},[1088,5454,1095],{"class":1094},[1088,5456,1232],{"class":1101},[1088,5458,1794],{"class":1108},[1088,5460,1248],{"class":1101},[1088,5462,1217],{"class":1094},[1088,5464,5415],{"class":1220},[1088,5466,1224],{"class":1101},[1088,5468,5469],{"class":1090,"line":1147},[1088,5470,1410],{"emptyLinePlaceholder":1409},[1088,5472,5473],{"class":1090,"line":1155},[1088,5474,5475],{"class":1279},"\u002F** An item, with the Stripe id the write code addresses its object by. *\u002F\n",[1088,5477,5478,5480,5482,5485,5487,5489,5492,5494,5497,5500,5502],{"class":1090,"line":1163},[1088,5479,3508],{"class":1094},[1088,5481,1098],{"class":1094},[1088,5483,5484],{"class":1325}," Item",[1088,5486,1961],{"class":1094},[1088,5488,3996],{"class":1325},[1088,5490,5491],{"class":1094}," &",[1088,5493,1232],{"class":1101},[1088,5495,5496],{"class":1094},"readonly",[1088,5498,5499],{"class":1094}," id:",[1088,5501,2495],{"class":1349},[1088,5503,4219],{"class":1101},[1088,5505,5506],{"class":1090,"line":1171},[1088,5507,1410],{"emptyLinePlaceholder":1409},[1088,5509,5510],{"class":1090,"line":1179},[1088,5511,5512],{"class":1279},"\u002F** Converts a Stripe object to an item with every declared field, each checked against its kind. *\u002F\n",[1088,5514,5515,5517,5519,5521,5523,5526,5529,5531,5534,5537,5539,5541,5543],{"class":1090,"line":1187},[1088,5516,3508],{"class":1094},[1088,5518,3514],{"class":1094},[1088,5520,4156],{"class":1325},[1088,5522,1329],{"class":1101},[1088,5524,5525],{"class":1094},"resource:",[1088,5527,5528],{"class":1325}," ResourceSpec",[1088,5530,519],{"class":1101},[1088,5532,5533],{"class":1094},"object:",[1088,5535,5536],{"class":1325}," StripeObject",[1088,5538,523],{"class":1101},[1088,5540,815],{"class":1094},[1088,5542,5484],{"class":1325},[1088,5544,1102],{"class":1101},[1088,5546,5547,5549,5552,5554,5556,5558],{"class":1090,"line":1195},[1088,5548,1855],{"class":1094},[1088,5550,5551],{"class":1325}," item",[1088,5553,815],{"class":1094},[1088,5555,3996],{"class":1325},[1088,5557,1961],{"class":1094},[1088,5559,5560],{"class":1101}," {};\n",[1088,5562,5563,5565,5567,5569,5572,5574,5577,5579,5581,5583,5586,5588,5590,5592,5594,5596,5598,5601,5603,5605,5607,5610,5612,5615,5617,5619,5621,5624,5626,5628,5630,5632,5634,5636,5638],{"class":1090,"line":1203},[1088,5564,2380],{"class":1094},[1088,5566,1450],{"class":1101},[1088,5568,2385],{"class":1094},[1088,5570,5571],{"class":1325}," field",[1088,5573,2391],{"class":1094},[1088,5575,5576],{"class":1108}," resource",[1088,5578,1000],{"class":1101},[1088,5580,2553],{"class":1108},[1088,5582,1510],{"class":1101},[1088,5584,5585],{"class":1108},"item",[1088,5587,2556],{"class":1101},[1088,5589,1726],{"class":1108},[1088,5591,1000],{"class":1101},[1088,5593,2026],{"class":1108},[1088,5595,4857],{"class":1101},[1088,5597,1864],{"class":1094},[1088,5599,5600],{"class":1325}," toValue",[1088,5602,1329],{"class":1101},[1088,5604,1726],{"class":1108},[1088,5606,519],{"class":1101},[1088,5608,5609],{"class":1325},"readPath",[1088,5611,1329],{"class":1101},[1088,5613,5614],{"class":1108},"object",[1088,5616,519],{"class":1101},[1088,5618,1726],{"class":1108},[1088,5620,1000],{"class":1101},[1088,5622,5623],{"class":1108},"path",[1088,5625,2400],{"class":1094},[1088,5627,2431],{"class":1101},[1088,5629,1794],{"class":1325},[1088,5631,1329],{"class":1101},[1088,5633,1726],{"class":1108},[1088,5635,1000],{"class":1101},[1088,5637,2026],{"class":1108},[1088,5639,5640],{"class":1101},")]));\n",[1088,5642,5643,5645,5647,5649,5651,5654,5656,5658,5660],{"class":1090,"line":1211},[1088,5644,1384],{"class":1094},[1088,5646,1232],{"class":1101},[1088,5648,3952],{"class":1094},[1088,5650,5585],{"class":1108},[1088,5652,5653],{"class":1101},", id: ",[1088,5655,5614],{"class":1108},[1088,5657,1000],{"class":1101},[1088,5659,551],{"class":1108},[1088,5661,4219],{"class":1101},[1088,5663,5664],{"class":1090,"line":1227},[1088,5665,1403],{"class":1101},[1088,5667,5668],{"class":1090,"line":1257},[1088,5669,1280],{"class":1279},[1088,5671,5672,5674,5677,5679,5681,5683,5685,5688,5691,5693,5696,5698,5701],{"class":1090,"line":1276},[1088,5673,1322],{"class":1094},[1088,5675,5676],{"class":1325}," readPath",[1088,5678,1329],{"class":1101},[1088,5680,5533],{"class":1094},[1088,5682,5536],{"class":1325},[1088,5684,519],{"class":1101},[1088,5686,5687],{"class":1094},"path:",[1088,5689,5690],{"class":1094}," readonly",[1088,5692,2495],{"class":1349},[1088,5694,5695],{"class":1101},"[])",[1088,5697,815],{"class":1094},[1088,5699,5700],{"class":1349}," unknown",[1088,5702,1102],{"class":1101},[1088,5704,5705,5707,5709,5711,5713,5715,5718],{"class":1090,"line":1283},[1088,5706,3753],{"class":1094},[1088,5708,1831],{"class":1108},[1088,5710,815],{"class":1094},[1088,5712,5700],{"class":1349},[1088,5714,1961],{"class":1094},[1088,5716,5717],{"class":1108}," object",[1088,5719,1224],{"class":1101},[1088,5721,5722],{"class":1090,"line":1289},[1088,5723,1410],{"emptyLinePlaceholder":1409},[1088,5725,5726,5728,5730,5732,5734,5736,5739],{"class":1090,"line":1295},[1088,5727,2380],{"class":1094},[1088,5729,1450],{"class":1101},[1088,5731,2385],{"class":1094},[1088,5733,4722],{"class":1325},[1088,5735,2391],{"class":1094},[1088,5737,5738],{"class":1108}," path",[1088,5740,1464],{"class":1101},[1088,5742,5743,5745,5747,5750,5752,5754,5756,5758,5760,5762],{"class":1090,"line":1301},[1088,5744,1713],{"class":1094},[1088,5746,1450],{"class":1101},[1088,5748,5749],{"class":1094},"!",[1088,5751,5442],{"class":1325},[1088,5753,1329],{"class":1101},[1088,5755,1824],{"class":1108},[1088,5757,1757],{"class":1101},[1088,5759,2020],{"class":1094},[1088,5761,1350],{"class":1325},[1088,5763,1224],{"class":1101},[1088,5765,5766,5769,5771,5773,5775,5777],{"class":1090,"line":1307},[1088,5767,5768],{"class":1108},"        value",[1088,5770,1961],{"class":1094},[1088,5772,1831],{"class":1108},[1088,5774,2556],{"class":1101},[1088,5776,4564],{"class":1108},[1088,5778,5779],{"class":1101},"];\n",[1088,5781,5782],{"class":1090,"line":1313},[1088,5783,1532],{"class":1101},[1088,5785,5786],{"class":1090,"line":1319},[1088,5787,1410],{"emptyLinePlaceholder":1409},[1088,5789,5790,5792,5794],{"class":1090,"line":1362},[1088,5791,1384],{"class":1094},[1088,5793,1831],{"class":1108},[1088,5795,1224],{"class":1101},[1088,5797,5798],{"class":1090,"line":1381},[1088,5799,1403],{"class":1101},[1088,5801,5802],{"class":1090,"line":1400},[1088,5803,1410],{"emptyLinePlaceholder":1409},[1088,5805,5806,5808,5810,5812,5814,5817,5819,5822,5824,5826,5828,5831],{"class":1090,"line":1406},[1088,5807,1322],{"class":1094},[1088,5809,5600],{"class":1325},[1088,5811,1329],{"class":1101},[1088,5813,2492],{"class":1094},[1088,5815,5816],{"class":1325}," FieldSpec",[1088,5818,519],{"class":1101},[1088,5820,5821],{"class":1094},"raw:",[1088,5823,5700],{"class":1349},[1088,5825,523],{"class":1101},[1088,5827,815],{"class":1094},[1088,5829,5830],{"class":1325}," Value",[1088,5832,1102],{"class":1101},[1088,5834,5835,5837,5839,5842,5844,5846,5848,5851,5853,5855],{"class":1090,"line":1413},[1088,5836,1447],{"class":1094},[1088,5838,1450],{"class":1101},[1088,5840,5841],{"class":1108},"raw",[1088,5843,1729],{"class":1094},[1088,5845,1350],{"class":1325},[1088,5847,1734],{"class":1094},[1088,5849,5850],{"class":1108}," raw",[1088,5852,1729],{"class":1094},[1088,5854,1836],{"class":1325},[1088,5856,1464],{"class":1101},[1088,5858,5859,5861,5863,5865,5867,5870,5872,5874,5876],{"class":1090,"line":1444},[1088,5860,1713],{"class":1094},[1088,5862,1450],{"class":1101},[1088,5864,1726],{"class":1108},[1088,5866,1000],{"class":1101},[1088,5868,5869],{"class":1108},"isNullable",[1088,5871,1510],{"class":1101},[1088,5873,2020],{"class":1094},[1088,5875,1836],{"class":1325},[1088,5877,1224],{"class":1101},[1088,5879,5880,5882,5884,5886,5888,5891,5893,5895,5897,5899,5901,5903,5905,5907],{"class":1090,"line":1467},[1088,5881,1659],{"class":1094},[1088,5883,1662],{"class":1094},[1088,5885,2618],{"class":1325},[1088,5887,1329],{"class":1101},[1088,5889,5890],{"class":1220},"`Stripe returned no value for ",[1088,5892,2209],{"class":1325},[1088,5894,2212],{"class":1220},[1088,5896,1726],{"class":1108},[1088,5898,1000],{"class":1220},[1088,5900,2026],{"class":1108},[1088,5902,2221],{"class":1220},[1088,5904,2209],{"class":1325},[1088,5906,2226],{"class":1220},[1088,5908,1378],{"class":1101},[1088,5910,5911],{"class":1090,"line":1484},[1088,5912,1532],{"class":1101},[1088,5914,5915],{"class":1090,"line":1529},[1088,5916,1410],{"emptyLinePlaceholder":1409},[1088,5918,5919,5921,5923,5925,5927,5930],{"class":1090,"line":1535},[1088,5920,2059],{"class":1094},[1088,5922,1450],{"class":1101},[1088,5924,1726],{"class":1108},[1088,5926,1000],{"class":1101},[1088,5928,5929],{"class":1108},"kind",[1088,5931,1464],{"class":1101},[1088,5933,5934,5936,5939],{"class":1090,"line":1540},[1088,5935,2075],{"class":1094},[1088,5937,5938],{"class":1220}," 'id'",[1088,5940,5941],{"class":1101},":\n",[1088,5943,5944,5946,5949],{"class":1090,"line":1559},[1088,5945,2075],{"class":1094},[1088,5947,5948],{"class":1220}," 'string'",[1088,5950,5941],{"class":1101},[1088,5952,5953,5956,5958,5960,5962,5964,5966,5968,5970,5972],{"class":1090,"line":1575},[1088,5954,5955],{"class":1094},"            if",[1088,5957,1450],{"class":1101},[1088,5959,4677],{"class":1094},[1088,5961,5850],{"class":1108},[1088,5963,1729],{"class":1094},[1088,5965,5948],{"class":1220},[1088,5967,1510],{"class":1101},[1088,5969,2020],{"class":1094},[1088,5971,5850],{"class":1108},[1088,5973,1224],{"class":1101},[1088,5975,5976,5979],{"class":1090,"line":1620},[1088,5977,5978],{"class":1094},"            break",[1088,5980,1224],{"class":1101},[1088,5982,5983,5985,5988],{"class":1090,"line":1625},[1088,5984,2075],{"class":1094},[1088,5986,5987],{"class":1220}," 'int64'",[1088,5989,5941],{"class":1101},[1088,5991,5992,5994,5996,5998,6000,6002,6004,6007,6010,6012,6014,6016,6018,6020,6022,6024],{"class":1090,"line":1630},[1088,5993,5955],{"class":1094},[1088,5995,1450],{"class":1101},[1088,5997,4677],{"class":1094},[1088,5999,5850],{"class":1108},[1088,6001,1729],{"class":1094},[1088,6003,4684],{"class":1220},[1088,6005,6006],{"class":1094}," &&",[1088,6008,6009],{"class":1108}," Number",[1088,6011,1000],{"class":1101},[1088,6013,4696],{"class":1325},[1088,6015,1329],{"class":1101},[1088,6017,5841],{"class":1108},[1088,6019,1757],{"class":1101},[1088,6021,2020],{"class":1094},[1088,6023,5850],{"class":1108},[1088,6025,1224],{"class":1101},[1088,6027,6028,6030],{"class":1090,"line":1650},[1088,6029,5978],{"class":1094},[1088,6031,1224],{"class":1101},[1088,6033,6034,6036,6039],{"class":1090,"line":1656},[1088,6035,2075],{"class":1094},[1088,6037,6038],{"class":1220}," 'boolean'",[1088,6040,5941],{"class":1101},[1088,6042,6043,6045,6047,6049,6051,6053,6055,6057,6059,6061],{"class":1090,"line":1681},[1088,6044,5955],{"class":1094},[1088,6046,1450],{"class":1101},[1088,6048,4677],{"class":1094},[1088,6050,5850],{"class":1108},[1088,6052,1729],{"class":1094},[1088,6054,6038],{"class":1220},[1088,6056,1510],{"class":1101},[1088,6058,2020],{"class":1094},[1088,6060,5850],{"class":1108},[1088,6062,1224],{"class":1101},[1088,6064,6065,6067],{"class":1090,"line":1686},[1088,6066,5978],{"class":1094},[1088,6068,1224],{"class":1101},[1088,6070,6071,6073,6076],{"class":1090,"line":1691},[1088,6072,2075],{"class":1094},[1088,6074,6075],{"class":1220}," 'timestamp'",[1088,6077,6078],{"class":1101},": {\n",[1088,6080,6081],{"class":1090,"line":1710},[1088,6082,6083],{"class":1279},"            \u002F\u002F Stripe sends Unix seconds. Engine reads RFC 3339. A number outside the range `Date` can\n",[1088,6085,6086],{"class":1090,"line":1774},[1088,6087,6088],{"class":1279},"            \u002F\u002F hold makes an invalid date, whose `toISOString` would throw a plain `RangeError`.\n",[1088,6090,6091,6094,6097,6099,6102,6104,6106,6108,6110,6112,6115,6117,6119,6122,6124,6126,6128,6130],{"class":1090,"line":1780},[1088,6092,6093],{"class":1094},"            const",[1088,6095,6096],{"class":1325}," date",[1088,6098,1961],{"class":1094},[1088,6100,6101],{"class":1094}," typeof",[1088,6103,5850],{"class":1108},[1088,6105,1729],{"class":1094},[1088,6107,4684],{"class":1220},[1088,6109,2423],{"class":1094},[1088,6111,1662],{"class":1094},[1088,6113,6114],{"class":1325}," Date",[1088,6116,1329],{"class":1101},[1088,6118,5841],{"class":1108},[1088,6120,6121],{"class":1094}," *",[1088,6123,3458],{"class":1325},[1088,6125,1510],{"class":1101},[1088,6127,815],{"class":1094},[1088,6129,1350],{"class":1325},[1088,6131,1224],{"class":1101},[1088,6133,6134,6136,6138,6141,6143,6145,6147,6149,6151,6153,6156,6158,6160,6162,6165,6168,6170,6172,6174,6177],{"class":1090,"line":1786},[1088,6135,5955],{"class":1094},[1088,6137,1450],{"class":1101},[1088,6139,6140],{"class":1108},"date",[1088,6142,1459],{"class":1094},[1088,6144,1350],{"class":1325},[1088,6146,6006],{"class":1094},[1088,6148,1737],{"class":1094},[1088,6150,4691],{"class":1108},[1088,6152,1000],{"class":1101},[1088,6154,6155],{"class":1325},"isNaN",[1088,6157,1329],{"class":1101},[1088,6159,6140],{"class":1108},[1088,6161,1000],{"class":1101},[1088,6163,6164],{"class":1325},"getTime",[1088,6166,6167],{"class":1101},"())) ",[1088,6169,2020],{"class":1094},[1088,6171,6096],{"class":1108},[1088,6173,1000],{"class":1101},[1088,6175,6176],{"class":1325},"toISOString",[1088,6178,6179],{"class":1101},"();\n",[1088,6181,6182,6184],{"class":1090,"line":1842},[1088,6183,5978],{"class":1094},[1088,6185,1224],{"class":1101},[1088,6187,6188],{"class":1090,"line":1847},[1088,6189,3909],{"class":1101},[1088,6191,6192],{"class":1090,"line":1852},[1088,6193,1532],{"class":1101},[1088,6195,6196],{"class":1090,"line":1877},[1088,6197,1410],{"emptyLinePlaceholder":1409},[1088,6199,6200],{"class":1090,"line":1899},[1088,6201,6202],{"class":1279},"    \u002F\u002F A changed API, not something the caller can fix: a plain error, which Engine reports as a failed query.\n",[1088,6204,6205,6208,6210,6212,6214,6217,6219,6221,6223,6225,6227,6229,6231,6233],{"class":1090,"line":1904},[1088,6206,6207],{"class":1094},"    throw",[1088,6209,1662],{"class":1094},[1088,6211,2618],{"class":1325},[1088,6213,1329],{"class":1101},[1088,6215,6216],{"class":1220},"`Stripe returned an unexpected value for ",[1088,6218,2209],{"class":1325},[1088,6220,2212],{"class":1220},[1088,6222,1726],{"class":1108},[1088,6224,1000],{"class":1220},[1088,6226,2026],{"class":1108},[1088,6228,2221],{"class":1220},[1088,6230,2209],{"class":1325},[1088,6232,2226],{"class":1220},[1088,6234,1378],{"class":1101},[1088,6236,6237],{"class":1090,"line":1927},[1088,6238,1403],{"class":1101},[458,6240,6241,6242,6246],{},"See ",[471,6243,6245],{"href":6244},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Fapi#field-types","Field Types"," for each type's encoding.",[454,6248,6250],{"id":6249},"map-errors-precisely","Map Errors Precisely",[458,6252,6253,6254,6258,6259,6263],{},"The error class you throw decides what the caller sees. See ",[471,6255,6257],{"href":6256},"\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Ferrors","Handle Data Connector Errors"," for the full mapping and ",[471,6260,6262],{"href":6261},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Ferrors","Data Connector Errors"," for each class.",[2661,6265,6266,6278,6301,6322,6341,6363,6380,6393,6402,6412],{},[2664,6267,6268,6271,6272,6274,6275,6277],{},[462,6269,6270],{},"Only a vendor-shaped \"not found\" is a miss."," Classify by the external data source's error body, not the status alone. A bare ",[516,6273,4900],{}," from a wrong base URL stays a plain ",[516,6276,2678],{},", or a misconfigured data source reads as empty.",[2664,6279,6280,6283,6284,6286,6287,6290,6291,6294,6295,6297,6298,6300],{},[462,6281,6282],{},"Handle a miss where it means no match."," The Stripe connector maps ",[516,6285,796],{}," by its error code. It reads ",[516,6288,6289],{},"param"," to tell a missing object from a missing reference. Its ",[516,6292,6293],{},"retrieve"," answers a ",[516,6296,4900],{}," with that code as no object: a keyed read answers ",[516,6299,4488],{},", and a read by id leaves the item out. A list filtered by a missing referenced object answers no items.",[2664,6302,6303,6308,6309,6311,6312,6315,6316,6318,6319,6321],{},[462,6304,6305,6306,1000],{},"A missing object elsewhere is a plain ",[516,6307,2678],{}," No kit class reports a missing item. A ",[516,6310,796],{}," error that reaches the Stripe client's classifier becomes ",[516,6313,6314],{},"StripeObjectMissing",", the connector's own subclass of ",[516,6317,2678],{},". Its keyed writes answer it as ",[516,6320,4488],{},". In a bulk write or on a later page, callers see a failed query.",[2664,6323,6324,6327,6328,6330,6331,6334,6335,957,6337,6340],{},[462,6325,6326],{},"A missing reference on a write is a constraint violation."," When a write names an item that doesn't exist, throw ",[516,6329,5088],{},". The caller then gets ",[516,6332,6333],{},"422"," instead of a failed query. Stripe answers a price create with a missing product as ",[516,6336,796],{},[516,6338,6339],{},"param: product",", and the Stripe connector maps it that way.",[2664,6342,6343,6349,6350,544,6352,6355,6356,6358,6359,6362],{},[462,6344,6345,6346,1000],{},"A rejected credential is ",[516,6347,6348],{},"AccessDenied"," Only someone with access to the external data source's account can fix it. The Stripe connector maps ",[516,6351,792],{},[516,6353,6354],{},"403"," to ",[516,6357,6348],{},", so a wrong key fails the connection test with a clear message. ",[516,6360,6361],{},"InvalidConfiguration"," is for a value that's wrong before it's ever sent.",[2664,6364,6365,6368,6369,6371,6372,6375,6376,6379],{},[462,6366,6367],{},"Put the retry hint in the message."," Besides the error's class, the message is all the caller gets. Both example connectors throw ",[516,6370,1245],{}," for a ",[516,6373,6374],{},"429",", and append \"Retry after 2 seconds.\" when ",[516,6377,6378],{},"Retry-After"," is a whole number of seconds, ignoring an HTTP-date. Nothing retries on it, so retry inside the connector where the request is safe to repeat.",[2664,6381,6382,6387,6388,6390,6391,1000],{},[462,6383,6384,6385,1000],{},"Server errors and timeouts are ",[516,6386,1235],{}," A ",[516,6389,5120],{}," means the external data source failed to serve this request, and a timeout means it didn't answer in time. Both examples treat either as an outage: a later request may work, so a caller can retry where the request is safe to repeat. Any other failed status the connector has no rule for is a plain ",[516,6392,2678],{},[2664,6394,6395,6401],{},[462,6396,6397,6400],{},[516,6398,6399],{},"testConnection"," authenticates."," Make it an authenticated read, so a wrong credential fails the test instead of the first query.",[2664,6403,6404,6407,6408,6411],{},[462,6405,6406],{},"Keep secrets out of messages."," The message is the only text of an error that reaches the caller. Stripe's error bodies include a URL that contains the account id, and its message for a rejected key echoes part of the key. The Stripe connector uses only Stripe's ",[516,6409,6410],{},"message"," field, and a fixed message for a rejected key. The Art Institute connector puts no response body into a message.",[2664,6413,6414,6417,6418,6355,6421,6424,6425,1000],{},[462,6415,6416],{},"Give every request a deadline."," Engine sets none. Both example connectors pass ",[516,6419,6420],{},"AbortSignal.timeout(10_000)",[516,6422,6423],{},"fetch"," and read the body inside the same ",[516,6426,6427],{},"try",[458,6429,6430,6433],{},[516,6431,6432],{},"console"," output from your connector lands in the Engine log at info level, tagged with the data source and the connector. Log the request you sent to the external data source when a mapping surprises you.",[454,6435,6437],{"id":6436},"see-also","See Also",[2661,6439,6440,6445,6450,6456,6463],{},[2664,6441,6442,6444],{},[471,6443,578],{"href":577},": the Stripe connector, step by step",[2664,6446,6447,6449],{},[471,6448,414],{"href":999},": what a collection can declare, and the rules Engine enforces",[2664,6451,6452,6455],{},[471,6453,410],{"href":6454},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Fapi",": the query wire format and the shapes Engine sends on its own",[2664,6457,6458,6462],{},[471,6459,6461],{"href":6460},"\u002Freference\u002Fextensions\u002Fdata-connectors\u002Flimitations","Data Connector Limitations",": what the contract can't express",[2664,6464,6465,6468],{},[471,6466,256],{"href":6467},"\u002Fdeveloper\u002Fextensions\u002Fdata-connectors\u002Ftesting",": check every mapping against the external data source",[6470,6471,6472],"style",{},"html pre.shiki code .sLVBU, html code.shiki .sLVBU{--shiki-light:#0891B2;--shiki-default:#0891B2;--shiki-dark:#22D3EE}html pre.shiki code .sTMul, html code.shiki .sTMul{--shiki-light:#1F2937;--shiki-default:#1F2937;--shiki-dark:#94A3B8}html pre.shiki code .sNJ9c, html code.shiki .sNJ9c{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E2E8F0}html pre.shiki code .suKVh, html code.shiki .suKVh{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#F8FAFC}html pre.shiki code .seNfP, html code.shiki .seNfP{--shiki-light:#64748B;--shiki-default:#64748B;--shiki-dark:#94A3B8}html pre.shiki code .ssBNi, html code.shiki .ssBNi{--shiki-light:#1659E9;--shiki-default:#1659E9;--shiki-dark:#94B3F5}html pre.shiki code .snHjA, html code.shiki .snHjA{--shiki-light:#124BC4;--shiki-default:#124BC4;--shiki-dark:#5B8EF4}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":1084,"searchDepth":1105,"depth":1105,"links":6474},[6475,6476,6477,6478,6483,6484,6492,6493,6494,6495,6500,6501],{"id":456,"depth":1105,"text":143},{"id":581,"depth":1105,"text":582},{"id":805,"depth":1105,"text":806},{"id":1003,"depth":1105,"text":1004,"children":6479},[6480,6481,6482],{"id":1048,"depth":1115,"text":1049},{"id":2852,"depth":1115,"text":2853},{"id":2977,"depth":1115,"text":2978},{"id":3068,"depth":1105,"text":3069},{"id":3232,"depth":1105,"text":3233,"children":6485},[6486,6487,6488,6489,6490,6491],{"id":3247,"depth":1115,"text":3248},{"id":3317,"depth":1115,"text":3318},{"id":3353,"depth":1115,"text":3354},{"id":3375,"depth":1115,"text":3376},{"id":4295,"depth":1115,"text":2886},{"id":4367,"depth":1115,"text":4368},{"id":4408,"depth":1105,"text":4409},{"id":4458,"depth":1105,"text":4459},{"id":4987,"depth":1105,"text":4988},{"id":5126,"depth":1105,"text":5127,"children":6496},[6497,6498,6499],{"id":5130,"depth":1115,"text":5131},{"id":5188,"depth":1115,"text":5189},{"id":5326,"depth":1115,"text":5327},{"id":6249,"depth":1105,"text":6250},{"id":6436,"depth":1105,"text":6437},"Translate an external data source's filters, paging, keys, writes and errors onto the data connector contract, with examples from a rich and a weak grammar.","md",null,{},{"icon":249},{"title":246,"description":6502},"WmlAploSpBxNjEKSAJr4iNW9GJ0NTUkoh8J_Bpg7AV0",[6510,6512],{"title":242,"path":243,"stem":244,"description":6511,"icon":42,"children":-1},"Learn how to create, update, recreate, and delete the data sources that use a data connector extension.",{"title":251,"path":252,"stem":253,"description":6513,"icon":254,"children":-1},"Map each failure of an external data source to the right error class, refuse requests you can't serve, and keep secrets out of what callers see.",1790840121671]