├── .github └── workflows │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── cli ├── Cli.res ├── CliUtils.res ├── EdgeDbGenerator.res ├── EdgeDbGenerator__Utils.res └── UnusedSelections.res ├── dbTestProject ├── dbschema │ ├── default.esdl │ └── migrations │ │ ├── 00001.edgeql │ │ ├── 00002.edgeql │ │ └── 00003.edgeql ├── edgedb.toml ├── package-lock.json ├── package.json ├── rescript.json ├── src │ ├── Main.res │ ├── Movies.res │ └── __generated__ │ │ └── Movies__edgeql.res └── test │ ├── TestFramework.res │ ├── TestProject.test.res │ └── __snapshots__ │ └── TestProject.test.mjs.snap ├── package-lock.json ├── package.json ├── rescript.json ├── src └── EdgeDB.res ├── test ├── GeneratorTests.test.res ├── TestFramework.res ├── UnusedSelectionsTests.test.res └── __snapshots__ │ ├── EdgeDbGeneratorTests.test.mjs.snap │ ├── GeneratorTests.test.mjs.snap │ └── UnusedSelectionsTests.test.mjs.snap └── vscode-extension ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── images └── icon.png ├── package-lock.json ├── package.json ├── snippets.json └── src ├── extension.ts └── tsconfig.json /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [18.x] 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | 26 | - name: Setup Bun 27 | uses: oven-sh/setup-bun@v1 28 | with: 29 | bun-version: latest 30 | 31 | - name: Install dependencies 32 | run: npm ci 33 | 34 | - name: Build project 35 | run: npm run build 36 | 37 | - name: Run tests 38 | run: npm test 39 | 40 | - name: Run bundled CLI 41 | run: ./dist/Cli.js --help 42 | 43 | - name: Setup EdgeDB 44 | uses: edgedb/setup-edgedb@v1 45 | with: 46 | instance-name: "dbTestProject" 47 | 48 | - name: Init EdgDB 49 | working-directory: ./dbTestProject 50 | run: edgedb project init --non-interactive 51 | 52 | - name: Setup and build DB test project 53 | working-directory: ./dbTestProject 54 | run: npm ci && npm run build:rescript && npm run build:edgedb 55 | 56 | - name: Run DB tests 57 | working-directory: ./dbTestProject 58 | run: npm test 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | dist 4 | *.mjs 5 | .DS_Store -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # master 2 | 3 | - Generate response types for primitve return types (bool, string, int, etc). 4 | 5 | # 0.7.0 6 | 7 | - Depend on ReScript `>=11.1.0` and latest `Core`. 8 | 9 | # 0.6.1 10 | 11 | - Fix issue with unused selections CLI. 12 | 13 | # 0.6.0 14 | 15 | - Up peer dependencies to support ReScript `11.1.0-rc.2` and Core `1.0.0`. 16 | 17 | # 0.5.0 18 | 19 | - Add `extract` and `ui-url` commands to CLI. 20 | - Emit `@live` annotations for relevant things in generated files. 21 | - Latest `rescript-embed-lang`. 22 | - Depend on ReScript v11. 23 | 24 | # 0.4.0 25 | 26 | - Latest `rescript-embed-lang`. 27 | - VSCode extension with errors in line. 28 | 29 | # 0.3.1 30 | 31 | - Fix bug with not findings `let` bindings in nested modules. 32 | 33 | # 0.3.0 34 | 35 | - Don't pick up %edgeql in single line comments. 36 | - Support more complex types in type generation. 37 | 38 | # 0.2.0 39 | 40 | - Move to use `rescript-embed-lang` utils for building the CLI. 41 | 42 | # 0.1.1 43 | 44 | - Minify output of Cli artifact. 45 | - Fix so that only EdgeDB related files are handled in the generated directory. 46 | 47 | # 0.1.0 48 | 49 | First release! 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rescript-edgedb 2 | 3 | Use EdgeDB fully type safe in ReScript. Embed EdgeQL right in your ReScript source code. 4 | 5 | ## Getting started 6 | 7 | > There's a dedicated VSCode extension for `rescript-edgedb` that will give you a few goodies like in-editor errors for your ReScript EdgeQL queries. Install from here: https://marketplace.visualstudio.com/items?itemName=GabrielNordeborn.vscode-rescript-edgedb. Read more about [its capabilities here](#vscode-extension). 8 | 9 | ```bash 10 | npm i rescript-edgedb rescript-embed-lang 11 | ``` 12 | 13 | Also make sure you have `@rescript/core >= 1.3.0`. It's required from `rescript-edgedb >=0.8.0`. 14 | 15 | Setup your `bsconfig.json`/`rescript.json`: 16 | 17 | ```json 18 | "bs-dependencies": ["@rescript/core", "rescript-edgedb"] 19 | "ppx-flags": ["rescript-embed-lang/ppx"] 20 | ``` 21 | 22 | The `rescript-edgedb` watcher needs to run for your EdgeQL to be compiled. Set up scripts in your `package.json` to do that: 23 | 24 | ```json 25 | "scripts": { 26 | "build:edgedb": "rescript-edgedb generate --output ./src/__generated__ --src ./src", 27 | "watch:edgedb": "npm run build:edgedb -- --watch" 28 | } 29 | ``` 30 | 31 | > The CLI will walk upwards looking for `edgedb.toml` in order to find how to connect to your database. So, you don't need to give the CLI any details on how to connect your database (although you can if you want). 32 | 33 | - `--src` should point to the root directory where you have ReScript files that contain `%edgeql` tags you want compiled. This directory will be searched (and watched) recursively. 34 | - `--output` should point to the directory where you want all _generated_ files to end up. This needs to be a directory that's part of your ReScript project, so the ReScript compiler can pick them up. 35 | 36 | ### Writing queries 37 | 38 | Finally, after starting `npm run watch:edgedb` and ensuring the console output looks fine, we can write our first EdgeQL query: 39 | 40 | ```rescript 41 | // Movies.res 42 | let findMovies = %edgeql(` 43 | # @name findMovies 44 | select Movie { 45 | title, 46 | status, 47 | actors: { 48 | name, 49 | age 50 | } 51 | } filter 52 | .title = $movieTitle`) 53 | ``` 54 | 55 | Executing the query is done by passing it the `client`, and arguments if it takes any. 56 | 57 | ```rescript 58 | let findMovies = %edgeql(` 59 | # @name findMovies 60 | select Movie { 61 | title, 62 | status, 63 | actors: { 64 | name, 65 | age 66 | } 67 | } filter 68 | .title = $movieTitle`) 69 | 70 | // array 71 | let movies = await client->findMovies({ 72 | movieTitle: title, 73 | }) 74 | ``` 75 | 76 | There's just one thing to notice in relation to regular EdgeQL - we require you to put a comment at the top of your query with a `@name` annotation, naming the query. This is because we need to be able to discern which query is which in the current ReScript file, since you can put as many queries as you want in the same ReScript file. 77 | 78 | ### `let` binding or `module` 79 | 80 | EdgeQL can be written both as a `let` binding like above, or as a `module` binding: 81 | 82 | ```rescript 83 | // Movies.res 84 | let findMovies = %edgeql(` 85 | # @name findMovies 86 | select Movie { 87 | title, 88 | status, 89 | actors: { 90 | name, 91 | age 92 | } 93 | } filter 94 | .title = $movieTitle 95 | `) 96 | 97 | let movies = await client->findMovies({ 98 | movieTitle: "Jalla Jalla", 99 | }) 100 | 101 | module DeleteMovie = %edgeql(` 102 | # @name deleteMovie 103 | delete Movie filter 104 | .title = $movieTitle 105 | `) 106 | 107 | let _maybeDeletedMovie = await client->DeleteMovie.query({ 108 | title: "Jalla Jalla" 109 | }) 110 | ``` 111 | 112 | The _only_ difference between these two is that the `let` binding gives you access to the generated `query` (which you use to execute the query) directly, whereas the `module` binding gives you access to the _entire_ generated module for the EdgeQL you write. This includes `query` (like with the `let` binding), the generated types for all of the query contents (args and response), and an extra `transaction` function that you can use in transactions. More about transactions below. 113 | 114 | `let` binding style EdgeQL is the thing you'll use most of all - it's to the point, and can be defined anywhere. `module` bindings need to be at the top level. But, sometimes you need it. 115 | 116 | > We might consider adding a "transaction mode" to the `let` binding as well in the future. Defining the queries inline is very powerful, and forcing you to define things at the top level because of `module` isn't the best DX at all times. 117 | 118 | ### Using transactions 119 | 120 | There's a `transaction` function emitted for each EdgeQL query. You can use that to do your operation in a transaction: 121 | 122 | ```rescript 123 | let client = EdgeDB.Client.make() 124 | 125 | // Remember to define your EdgeQL using a module, so you get easy access to all generated functions. 126 | module InsertMovie = %edgeql(` 127 | # @name insertMovie 128 | insert Movie { 129 | title := $title, 130 | status := $status 131 | }`) 132 | 133 | await client->EdgeDB.Client.transaction(async tx => { 134 | await tx->InsertMovie.transaction({ 135 | title: "Jalla Jalla", 136 | status: #Published 137 | }) 138 | }) 139 | ``` 140 | 141 | ### Cardinality 142 | 143 | > Cardinality = how many results are returned from your query. 144 | 145 | EdgeDB and `rescript-edgedb` automatically manages the cardinality of each query for you. That means that you can always trust the return types of your query. For example, adding `limit 1` to the `findMovies` query above would make the return types `option` instead of `array`. 146 | 147 | Similarily, you can design the query so that it expects there to always be exactly 1 response, and error if that's not the case. In that case, the return type would be `result`. 148 | 149 | Here's a complete list of the responses your EdgeQL queries can produce: 150 | 151 | - `void` - Nothing. No results are returned at all. 152 | - `array` - Many. A list of all results. 153 | - `option` - Maybe one. 154 | - `result` Exactly one. Or an error. 155 | 156 | ## The CLI 157 | 158 | You can get a full list of supported CLI commands by running `npx rescript-edgedb --help`. More documentation on the exact parameters available is coming. 159 | 160 | ## So, how does it work? 161 | 162 | `rescript-edgedb` consists of 2 parts: 163 | 164 | 1. A code generator that generates ReScript from your EdgeQL. 165 | 2. A PPX transform that swaps your `%edgeql` tag to its corresponding generated code, via [`rescript-embed-lang`](https://github.com/zth/rescript-embed-lang). 166 | 167 | Take this query as an example: 168 | 169 | ```rescript 170 | // Movies.res 171 | let findMovies = %edgeql(` 172 | # @name findMovies 173 | select Movie { 174 | title, 175 | status, 176 | actors: { 177 | name, 178 | age 179 | } 180 | } filter 181 | .title = $movieTitle`) 182 | ``` 183 | 184 | The `rescript-edgedb` tooling finds this `%edgeql` tag, and generates a file called `Movies__edgeql.res` from it, using code generation leveraging the official EdgeDB type generation tooling. That file will contain generated code and types for the `findMovies` query: 185 | 186 | ```rescript 187 | // @sourceHash 18807b4839373ee493a3aaab68766f53 188 | // @generated This file is generated automatically by rescript-edgedb, do not edit manually 189 | module FindMoviesQuery = { 190 | let queryText = `select Movie { 191 | title, 192 | status, 193 | actors: { 194 | name, 195 | age 196 | } 197 | } filter 198 | .title = $movieTitle` 199 | 200 | type args = { 201 | movieTitle: string, 202 | } 203 | 204 | type response_actors = { 205 | name: string, 206 | age: Null.t, 207 | } 208 | 209 | type response = { 210 | title: string, 211 | status: [#Published | #Unpublished], 212 | actors: array, 213 | } 214 | 215 | let query = (client: EdgeDB.Client.t, args: args): promise> => { 216 | client->EdgeDB.QueryHelpers.many(queryText, ~args) 217 | } 218 | 219 | let transaction = (transaction: EdgeDB.Transaction.t, args: args): promise> => { 220 | transaction->EdgeDB.TransactionHelpers.many(queryText, ~args) 221 | } 222 | } 223 | ``` 224 | 225 | Thanks to `rescript-embed-lang`, you don't have to think about that generated file at all. It's automatically managed for you. 226 | 227 | ## VSCode extension 228 | 229 | `rescript-edgedb` comes with a dedicated [VSCode extension](https://marketplace.visualstudio.com/items?itemName=GabrielNordeborn.vscode-rescript-edgedb) designed to enhance the experience of using ReScript and EdgeDB together. Below is a list of how you use it, and what it can do. 230 | 231 | > NOTE: Make sure you install the [official EdgeDB extension](https://marketplace.visualstudio.com/items?itemName=magicstack.edgedb) as well, so you get syntax highlighting and more. 232 | 233 | ### Snippets 234 | 235 | Snippets for easily adding new `%edgeql` blocks are included: 236 | 237 | ![snippets](https://github.com/zth/rescript-edgedb/assets/1457626/8dc1c54b-470d-4dee-9598-e26d35632286) 238 | 239 | These appear as soon as you start writing `%edgeql` in a ReScript file. 240 | 241 | ### In editor error messages 242 | 243 | Any errors for your EdgeQL queries will show directly in your ReScript files that define them: 244 | 245 | ![in-editor-errors](https://github.com/zth/rescript-edgedb/assets/1457626/19f6bf01-5648-4354-b510-881ed9b05c3a) 246 | 247 | ### Easily edit queries in the dedicated EdgeDB UI 248 | 249 | You can easily open the local EdgeDB UI, edit your query in there (including running it, etc), and then insert the modified query back: 250 | 251 | ![open-in-edgedb-ui](https://github.com/zth/rescript-edgedb/assets/1457626/e4dca50c-de60-4a78-8de9-f195a2cfd88d) 252 | 253 | It works like this: 254 | 255 | 1. Put the cursor in the query you want to edit. 256 | 2. Activate code actions. 257 | 3. Select the code action for opening the EdgeDB UI and copying the query. 258 | 4. The local EdgeDB query editor UI will now open in your browser, and the EdgeQL query you had your cursor in will be copied to your clipboard. 259 | 5. Paste the query into the query editor and make the edits you want. 260 | 6. Copy the entire query text and go back to VSCode and the file which has your query. 261 | 7. Activate code actions again and select the code action for inserting your modified query. 262 | 8. Done! 263 | 264 | ## FAQ 265 | 266 | **Should I check the generated files into source control?** 267 | Yes, you should. This ensures building the project doesn't _have_ to rely on a running EdgeDB instance (which the code generation tooling requires). 268 | 269 | ## Contributing 270 | 271 | `rescript-edgedb` leverages Bun for local development, including running tests. 272 | -------------------------------------------------------------------------------- /cli/Cli.res: -------------------------------------------------------------------------------- 1 | @@directive("#!/usr/bin/env node") 2 | 3 | module Path = NodeJs.Path 4 | 5 | @module("edgedb/dist/conUtils.js") 6 | external validTlsSecurityValues: array = "validTlsSecurityValues" 7 | 8 | @module("edgedb/dist/conUtils.js") 9 | external parseConnectArguments: EdgeDB.Client.connectConfig => promise< 10 | EdgeDB.Client.normalizedConnectConfig, 11 | > = "parseConnectArguments" 12 | 13 | let adapter = EdgeDbGenerator__Utils.adapter 14 | module EdgeDbUtils = EdgeDbGenerator__Utils 15 | 16 | let usage = `Usage: 17 | generate | Generates all EdgeDB code. 18 | [--output ] | Where to emit all generated files. 19 | [--src ] | The source folder for where to look for ReScript files. 20 | [--watch] | Runs this command in watch mode. 21 | 22 | unused-selections | Check if we there are unused selections in your EdgeQL queries. 23 | [--ci] | Run in CI mode. 24 | 25 | ui-url | Get the URL for opening the local EdgeDB UI. 26 | 27 | extract | Extract all %edgeql tags in file at .` 28 | 29 | type config = {client: EdgeDB.Client.t} 30 | 31 | type errorInFile = { 32 | startLoc: RescriptEmbedLang.loc, 33 | endLoc: RescriptEmbedLang.loc, 34 | errorMessage: string, 35 | } 36 | 37 | let isInConfigDir = async () => { 38 | let bsconfig = Path.resolve([adapter.process.cwd(), "bsconfig.json"]) 39 | let rescriptJson = Path.resolve([adapter.process.cwd(), "rescript.json"]) 40 | 41 | try { 42 | await adapter.fs.access(bsconfig) 43 | true 44 | } catch { 45 | | Exn.Error(_) => 46 | try { 47 | await adapter.fs.access(rescriptJson) 48 | true 49 | } catch { 50 | | Exn.Error(_) => false 51 | } 52 | } 53 | } 54 | 55 | let main = async () => { 56 | let errors = Map.make() 57 | 58 | let syncErrors = async () => { 59 | let isInConfigDir = await isInConfigDir() 60 | 61 | if isInConfigDir { 62 | let errorsFile = Path.resolve([adapter.process.cwd(), "lib", "bs", ".generator.edgeql.log"]) 63 | NodeJs.Fs.writeFileSync( 64 | errorsFile, 65 | errors 66 | ->Map.entries 67 | ->Iterator.toArray 68 | ->Array.map(((filePath, errorMap)) => 69 | { 70 | "filePath": filePath, 71 | "errors": errorMap->Dict.valuesToArray, 72 | } 73 | ) 74 | ->Array.reduce(Dict.make(), (dict, curr) => { 75 | dict->Dict.set(curr["filePath"], curr["errors"]) 76 | dict 77 | }) 78 | ->JSON.stringifyAny 79 | ->Option.getOr("") 80 | ->NodeJs.Buffer.fromString, 81 | ) 82 | } 83 | } 84 | 85 | let pruneErrorsForModuleInFile = (~path, ~moduleName) => 86 | switch errors->Map.get(path) { 87 | | None => () 88 | | Some(fileErrors) => 89 | switch moduleName { 90 | | None => () 91 | | Some(moduleName) => fileErrors->Dict.delete(moduleName) 92 | } 93 | } 94 | 95 | let setErrorForModuleInFile = (~path, ~error: errorInFile, ~moduleName) => 96 | switch moduleName { 97 | | Some(moduleName) => 98 | switch errors->Map.get(path) { 99 | | None => 100 | let fileErrors = Dict.fromArray([(moduleName, error)]) 101 | errors->Map.set(path, fileErrors) 102 | | Some(fileErrors) => fileErrors->Dict.set(moduleName, error) 103 | } 104 | | None => () 105 | } 106 | 107 | let emitter = RescriptEmbedLang.make( 108 | ~extensionPattern=FirstClass("edgeql"), 109 | ~cliHelpText=usage, 110 | ~setup=async ({args}) => { 111 | let howToGetPassword = switch ( 112 | args->RescriptEmbedLang.CliArgs.hasArg("--password"), 113 | args->RescriptEmbedLang.CliArgs.hasArg("--password-from-stdin"), 114 | ) { 115 | | (true, true) => panic(`Cannot use both --password and --password-from-stdin options`) 116 | | (true, false) => Some(#PromptPassword) 117 | | (false, true) => Some(#FromStdin) 118 | | (false, false) => None 119 | } 120 | 121 | let options: EdgeDB.Client.connectOptions = { 122 | concurrency: 5, 123 | dsn: ?args->RescriptEmbedLang.CliArgs.getArgValue(["-I", "--instance", "--dsn"]), 124 | credentialsFile: ?args->RescriptEmbedLang.CliArgs.getArgValue(["--credentials-file"]), 125 | host: ?args->RescriptEmbedLang.CliArgs.getArgValue(["-H", "--host"]), 126 | port: ?( 127 | args 128 | ->RescriptEmbedLang.CliArgs.getArgValue(["-P", "--port"]) 129 | ->Option.flatMap(port => Int.fromString(port)) 130 | ), 131 | database: ?args->RescriptEmbedLang.CliArgs.getArgValue(["-d", "--database"]), 132 | user: ?args->RescriptEmbedLang.CliArgs.getArgValue(["-u", "--user"]), 133 | tlsCAFile: ?args->RescriptEmbedLang.CliArgs.getArgValue(["--tls-ca-file"]), 134 | tlsSecurity: ?switch args->RescriptEmbedLang.CliArgs.getArgValue(["--tls-security"]) { 135 | | Some(tlsSec) => 136 | if !(validTlsSecurityValues->Array.includes(tlsSec)) { 137 | panic( 138 | `Invalid value for --tls-security. Must be one of: ${validTlsSecurityValues 139 | ->Array.map(x => `"${x}"`) 140 | ->Array.join(" | ")}`, 141 | ) 142 | } else { 143 | switch tlsSec { 144 | | "insecure" => Some(Insecure) 145 | | "no_host_verification" => Some(NoHostVerification) 146 | | "strict" => Some(Strict) 147 | | "default" => Some(Default) 148 | | _ => None 149 | } 150 | } 151 | 152 | | None => None 153 | }, 154 | } 155 | 156 | let client = EdgeDB.Client.make( 157 | ~options={ 158 | ...options, 159 | password: ?switch howToGetPassword { 160 | | None => None 161 | | Some(#PromptPassword) => 162 | let username = ( 163 | await parseConnectArguments({ 164 | ...(options :> EdgeDB.Client.connectConfig), 165 | password: "", 166 | }) 167 | ).connectionParams.user 168 | Some(await EdgeDbUtils.promptForPassword(username)) 169 | | Some(#FromStdin) => Some(await EdgeDbUtils.readPasswordFromStdin()) 170 | }, 171 | }, 172 | ) 173 | 174 | {client: client} 175 | }, 176 | ~handleOtherCommand=async ({args, command, config}) => { 177 | switch command { 178 | | "ui-url" => 179 | let getNormalizedConfig: EdgeDB.Client.t => promise< 180 | 'a, 181 | > = %raw(`async function getNormalizedConfig(client) { 182 | return (await client.pool._getNormalizedConnectConfig()).connectionParams 183 | }`) 184 | 185 | let connectionConfig = await getNormalizedConfig(config.client) 186 | 187 | let url = `http://${connectionConfig["address"]->Array.join( 188 | ":", 189 | )}/ui/${connectionConfig["database"]}` 190 | url->JSON.stringifyAny->Console.log 191 | | "unused-selections" => 192 | let ci = args->RescriptEmbedLang.CliArgs.hasArg("--ci") 193 | 194 | if !(await isInConfigDir()) { 195 | Console.error(`Could not find bsconfig.json or rescript.json. This command needs to run in the same directory as bsconfig.json or rescript.json.`) 196 | adapter.process->EdgeDbGenerator__Utils.Adapter.exit(1) 197 | } 198 | 199 | Console.log("Analyzing project... (this might take a while)") 200 | 201 | let results = await UnusedSelections.readReanalyzeOutput() 202 | 203 | UnusedSelections.reportResults(results) 204 | 205 | if ci && results->Array.length > 0 { 206 | adapter.process->EdgeDbGenerator__Utils.Adapter.exit(1) 207 | } 208 | | _ => () 209 | } 210 | }, 211 | ~generate=async ({config, content, path, location}) => { 212 | open EdgeDbGenerator 213 | 214 | let moduleName = 215 | content 216 | ->String.split("# @name ") 217 | ->Array.get(1) 218 | ->Option.getOr("") 219 | ->String.split(" ") 220 | ->Array.get(0) 221 | ->Option.map(EdgeDbGenerator__Utils.capitalizeString) 222 | ->Option.map(String.trim) 223 | 224 | let types = switch await config.client->AnalyzeQuery.analyzeQuery(content, ~path="") { 225 | | Ok(types) => 226 | pruneErrorsForModuleInFile(~path, ~moduleName) 227 | types 228 | | Error(errorMessage) => 229 | let error = Utils.Errors.extractFromString( 230 | errorMessage, 231 | ~startLoc=(location.start :> Utils.Errors.loc), 232 | ) 233 | 234 | switch error { 235 | | None => 236 | /* Set error loc to generic error */ 237 | let lines = content->String.split("\n") 238 | let lineCount = ref(0) 239 | let break = ref(false) 240 | let loc = ref(None) 241 | while !break.contents { 242 | let currentLineCount = lineCount.contents 243 | lineCount := currentLineCount + 1 244 | let line = lines->Array.get(currentLineCount)->Option.getOr("") 245 | if line->String.includes("@name") { 246 | switch line->String.split("# @name ") { 247 | | [before, _after] => 248 | let col = before->String.length + "# @name "->String.length 249 | loc := 250 | Some(( 251 | { 252 | Utils.Errors.line: currentLineCount, 253 | col, 254 | }, 255 | { 256 | Utils.Errors.line: currentLineCount, 257 | col: line->String.length, 258 | }, 259 | )) 260 | | _ => () 261 | } 262 | } 263 | 264 | if lineCount.contents > lines->Array.length { 265 | break := true 266 | } 267 | } 268 | 269 | switch loc.contents { 270 | | None => () 271 | | Some((startLoc, endLoc)) => 272 | setErrorForModuleInFile( 273 | ~path, 274 | ~error={ 275 | errorMessage, 276 | startLoc: { 277 | line: startLoc.line + location.start.line, 278 | col: startLoc.col, 279 | }, 280 | endLoc: { 281 | line: endLoc.line + location.start.line, 282 | col: endLoc.col, 283 | }, 284 | }, 285 | ~moduleName, 286 | ) 287 | } 288 | | Some(error) => 289 | setErrorForModuleInFile( 290 | ~path, 291 | ~error={ 292 | errorMessage: error.text, 293 | startLoc: { 294 | line: error.start.line, 295 | col: error.start.col, 296 | }, 297 | endLoc: { 298 | line: error.end.line, 299 | col: error.end.col, 300 | }, 301 | }, 302 | ~moduleName, 303 | ) 304 | } 305 | 306 | let distinctTypes = Set.make() 307 | distinctTypes->Set.add("type lookAboveForDetails = this_query_has_EdgeQL_errors") 308 | { 309 | args: "unit", 310 | result: "", 311 | cardinality: ONE, 312 | query: `/*\n${errorMessage}\n*/`, 313 | distinctTypes, 314 | } 315 | } 316 | 317 | let fileOutput = [] 318 | let (method, returnType, extraInFnArgs, extraInFnApply) = switch types.cardinality { 319 | | ONE => ( 320 | "singleRequired", 321 | "promise>", 322 | "", 323 | "", 324 | ) 325 | | AT_MOST_ONE => ("single", "promise>", ", ~onError=?", ", ~onError?") 326 | | _ => ("many", "promise>", "", "") 327 | } 328 | let hasArgs = types.args !== "null" 329 | let queryText = types.query->String.trim->String.replaceRegExp(%re("/`/g"), "\\`") 330 | fileOutput->Array.push( 331 | `let queryText = \`${queryText}\` 332 | 333 | ${types.distinctTypes->Set.values->Iterator.toArray->Array.join("\n\n")} 334 | 335 | @live 336 | let query = (client: EdgeDB.Client.t${hasArgs 337 | ? `, args: args` 338 | : ""}${extraInFnArgs}): ${returnType} => { 339 | client->EdgeDB.QueryHelpers.${method}(queryText${hasArgs ? ", ~args" : ""}${extraInFnApply}) 340 | } 341 | 342 | @live 343 | let transaction = (transaction: EdgeDB.Transaction.t${hasArgs 344 | ? `, args: args` 345 | : ""}${extraInFnArgs}): ${returnType} => { 346 | transaction->EdgeDB.TransactionHelpers.${method}(queryText${hasArgs 347 | ? ", ~args" 348 | : ""}${extraInFnApply}) 349 | }`, 350 | ) 351 | let content = fileOutput->Array.join("") 352 | 353 | // Sync errors 354 | syncErrors()->Promise.done 355 | 356 | switch moduleName { 357 | | Some(moduleName) => Ok({RescriptEmbedLang.WithModuleName({content, moduleName})}) 358 | | None => Error("Could not find query name.") 359 | } 360 | }, 361 | ~onWatch=async ({config, runGeneration, debug}) => { 362 | let migrationsCount = ref(-1) 363 | 364 | let checkForSchemaChanges = async () => { 365 | try { 366 | debug("[schema change detection] Polling for schema changes...") 367 | let migrations = 368 | (await config.client 369 | ->EdgeDB.QueryHelpers.single("select count(schema::Migration)")) 370 | ->Option.getOr(-1) 371 | 372 | debug(`[schema change detection] Found ${migrations->Int.toString} migrations`) 373 | let currentMigrationsCount = migrationsCount.contents 374 | migrationsCount := migrations 375 | 376 | if currentMigrationsCount > -1 && migrations !== migrationsCount.contents { 377 | Console.log("Detected changes to EdgeDB schema. Regenerating queries...") 378 | migrationsCount := migrations 379 | await runGeneration() 380 | } 381 | } catch { 382 | | Exn.Error(_) => () 383 | } 384 | } 385 | 386 | setInterval(() => { 387 | checkForSchemaChanges()->Promise.done 388 | }, 5000)->ignore 389 | }, 390 | ) 391 | 392 | RescriptEmbedLang.runCli(emitter) 393 | } 394 | 395 | main()->Promise.done 396 | -------------------------------------------------------------------------------- /cli/CliUtils.res: -------------------------------------------------------------------------------- 1 | let colorRed = str => `\x1b[31m${str}\x1b[0m` 2 | -------------------------------------------------------------------------------- /cli/EdgeDbGenerator.res: -------------------------------------------------------------------------------- 1 | /* Most things in here are (loosely) ported from the existing `edgedb-js` tooling to ReScript. */ 2 | module Utils = EdgeDbGenerator__Utils 3 | module Fs = NodeJs.Fs 4 | module Path = NodeJs.Path 5 | module Process = NodeJs.Process 6 | 7 | @send 8 | external matchAll: (string, RegExp.t) => Iterator.t> = "matchAll" 9 | 10 | let toReScriptPropName = name => 11 | switch RescriptEmbedLang.CodegenUtils.toReScriptSafeName(name) { 12 | | NeedsAnnotation({actualName, safeName}) => `@as("${actualName}") ${safeName}` 13 | | Safe(safeName) => safeName 14 | } 15 | 16 | @live 17 | type cardinality = 18 | | @as(0x6e) NO_RESULT 19 | | @as(0x6f) AT_MOST_ONE 20 | | @as(0x41) ONE 21 | | @as(0x6d) MANY 22 | | @as(0x4d) AT_LEAST_ONE 23 | 24 | let generateSetType = (typ: string, cardinality: cardinality): string => { 25 | switch cardinality { 26 | | AT_LEAST_ONE 27 | | MANY => 28 | `array<${typ}>` 29 | | ONE => typ 30 | | AT_MOST_ONE => `Null.t<${typ}>` 31 | | NO_RESULT => panic(`Unexpected cardinality: ${(cardinality :> int)->Int.toString}`) 32 | } 33 | } 34 | 35 | module Codec = { 36 | type t 37 | 38 | @live 39 | type objectFieldInfo = { 40 | name: string, 41 | implicit: bool, 42 | linkprop: bool, 43 | cardinality: cardinality, 44 | } 45 | 46 | @unboxed @live 47 | type codecKind = 48 | | @as("array") Array 49 | | @as("tuple") Tuple 50 | | @as("namedtuple") NamedTuple 51 | | @as("object") Object 52 | | @as("set") Set 53 | | @as("scalar") Scalar 54 | | @as("sparse_object") SparseObject 55 | | @as("range") Range 56 | | FutureAddedCodec(string) 57 | 58 | @get external tsType: t => string = "tsType" 59 | @get external values: t => array = "values" 60 | @send external getSubcodecs: t => array = "getSubcodecs" 61 | @send external getFields: t => array = "getFields" 62 | @send external getNames: t => array = "getNames" 63 | @send external getKind: t => codecKind = "getKind" 64 | 65 | @module("edgedb/dist/codecs/codecs.js") 66 | external nullCodec: t = "NullCodec" 67 | 68 | @module("edgedb/dist/codecs/ifaces.js") 69 | external scalarCodec: t = "ScalarCodec" 70 | 71 | @module("edgedb/dist/codecs/enum.js") 72 | external enumCodec: t = "EnumCodec" 73 | 74 | @module("edgedb/dist/codecs/numbers.js") 75 | external int16Codec: t = "Int16Codec" 76 | 77 | @module("edgedb/dist/codecs/numbers.js") 78 | external int32Codec: t = "Int32Codec" 79 | 80 | @module("edgedb/dist/codecs/json.js") 81 | external jsonCodec: t = "JSONCodec" 82 | 83 | @module("edgedb/dist/codecs/numerics.js") 84 | external bigintCodec: t = "BigIntCodec" 85 | 86 | @module("edgedb/dist/codecs/object.js") 87 | external objectCodec: t = "ObjectCodec" 88 | 89 | @module("edgedb/dist/codecs/namedtuple.js") 90 | external namedTupleCodec: t = "NamedTupleCodec" 91 | 92 | @module("edgedb/dist/codecs/array.js") 93 | external arrayCodec: t = "ArrayCodec" 94 | 95 | @module("edgedb/dist/codecs/tuple.js") 96 | external tupleCodec: t = "TupleCodec" 97 | 98 | @module("edgedb/dist/codecs/range.js") 99 | external rangeCodec: t = "RangeCodec" 100 | 101 | @module("edgedb/dist/codecs/set.js") 102 | external setCodec: t = "SetCodec" 103 | 104 | let is: (t, t) => bool = %raw(`function instanceOf(a, b) { 105 | return a instanceof b 106 | }`) 107 | } 108 | 109 | module QueryType = { 110 | type t = { 111 | args: string, 112 | result: string, 113 | cardinality: cardinality, 114 | query: string, 115 | distinctTypes: Set.t, 116 | } 117 | } 118 | 119 | module AnalyzeQuery = { 120 | type parseResult = ( 121 | cardinality, 122 | Codec.t, 123 | Codec.t, 124 | float, 125 | Null.t, 126 | Null.t, 127 | ) 128 | 129 | module BaseClientPool = { 130 | module Connection = { 131 | type sessionDefaults 132 | type session 133 | @module("edgedb/dist/options.js") 134 | external session: session = "Session" 135 | 136 | @send external getSessionDefaults: session => sessionDefaults = "defaults" 137 | 138 | // Manually mapped from `ord` fn in driver/src/primitives of the edgedb packages 139 | @live 140 | type outputFormat = | @as(98) BINARY | @as(106) JSON | @as(110) NONE 141 | type t 142 | @send 143 | external _parse: ( 144 | t, 145 | string, 146 | outputFormat, 147 | cardinality, 148 | sessionDefaults, 149 | ) => promise = "_parse" 150 | } 151 | type defaultOptions 152 | 153 | @module("edgedb/dist/options.js") @new 154 | external getDefaultOptions: unit => defaultOptions = "Options" 155 | 156 | type t 157 | module Holder = { 158 | type t 159 | @send external _getConnection: t => promise = "_getConnection" 160 | @send external release: t => promise = "release" 161 | } 162 | 163 | @send external acquireHolder: (t, defaultOptions) => promise = "acquireHolder" 164 | } 165 | 166 | @get external getPool: EdgeDB.Client.t => BaseClientPool.t = "pool" 167 | 168 | module WalkCodec = { 169 | type ctx = { 170 | optionalNulls: bool, 171 | distinctTypes: Set.t, 172 | currentPath: array, 173 | } 174 | 175 | let polyVariantNameNeedsEscapingRegex: RegExp.t = %re("/^[a-zA-Z0-9_]+$/") 176 | 177 | let rec generateRecord = ( 178 | ~fields: array, 179 | ~subCodecs: array, 180 | ~ctx: ctx, 181 | ) => { 182 | open Codec 183 | 184 | let annotations = if ctx.currentPath->Array.at(0) === Some("args") { 185 | "@live \n" 186 | } else { 187 | "" 188 | } 189 | 190 | let name = Utils.pathToName(ctx.currentPath) 191 | let recordDef = `${annotations}type ${name} = {\n${fields 192 | ->Array.mapWithIndex((field, i) => { 193 | let subCodec = switch (subCodecs->Array.getUnsafe(i), field.cardinality) { 194 | | (subCodec, ONE | NO_RESULT | AT_MOST_ONE) if subCodec->is(setCodec) => 195 | panic("subcodec is SetCodec, but upper cardinality is one") 196 | | (subCodec, _) if subCodec->is(setCodec) => subCodec->getSubcodecs->Array.getUnsafe(0) 197 | | (subCodec, _) => subCodec 198 | } 199 | ` ${toReScriptPropName(field.name)}${if ( 200 | ctx.optionalNulls && field.cardinality === AT_MOST_ONE 201 | ) { 202 | "?" 203 | } else { 204 | "" 205 | }}: ${walkCodec( 206 | subCodec, 207 | { 208 | ...ctx, 209 | currentPath: { 210 | let newPath = ctx.currentPath->Array.copy 211 | newPath->Array.push(field.name) 212 | newPath 213 | }, 214 | }, 215 | )->generateSetType(field.cardinality)},` 216 | }) 217 | ->Array.join("\n")}\n}` 218 | 219 | ctx.distinctTypes->Set.add(recordDef) 220 | name 221 | } 222 | and generatePrimitive = (~codec: Codec.t, ~ctx: ctx) => { 223 | let name = Utils.pathToName(ctx.currentPath) 224 | let annotations = if ctx.currentPath->Array.at(0) === Some("args") { 225 | "@live \n" 226 | } else { 227 | "" 228 | } 229 | ctx.distinctTypes->Set.add(`${annotations}type ${name} = ${codec->walkCodec(ctx)}`) 230 | } 231 | and walkCodec = (codec: Codec.t, ctx: ctx) => { 232 | open Codec 233 | if codec->is(nullCodec) { 234 | "null" 235 | } else if codec->is(scalarCodec) { 236 | if codec->is(enumCodec) { 237 | `[${codec 238 | ->values 239 | ->Array.map(v => { 240 | let name = polyVariantNameNeedsEscapingRegex->RegExp.test(v) ? v : `"${v}"` 241 | `#${name}` 242 | }) 243 | ->Array.join(" | ")}]` 244 | } else if codec->is(int16Codec) || codec->is(int32Codec) { 245 | "int" 246 | } else if codec->is(bigintCodec) { 247 | "bigint" 248 | } else if codec->is(jsonCodec) { 249 | "JSON.t" 250 | } else { 251 | switch codec->tsType { 252 | | "number" => "float" 253 | | "boolean" => "bool" 254 | | "Date" => "Date.t" 255 | | ("LocalDateTime" 256 | | "LocalDate" 257 | | "RelativeDuration" 258 | | "ConfigMemory" 259 | | "Duration" 260 | | "DateDuration" 261 | | "LocalTime") as dataType => 262 | `EdgeDB.DataTypes.${dataType}.t` 263 | | tsType if tsType->String.charAt(0)->String.toUpperCase === tsType->String.charAt(0) => 264 | `${tsType}.t` 265 | | tsType => tsType 266 | } 267 | } 268 | } else if codec->is(objectCodec) || codec->is(namedTupleCodec) { 269 | let fields = if codec->is(objectCodec) { 270 | codec->getFields 271 | } else { 272 | codec 273 | ->getNames 274 | ->Array.map(name => { 275 | name, 276 | cardinality: ONE, 277 | implicit: false, 278 | linkprop: false, 279 | }) 280 | } 281 | let subCodecs = codec->getSubcodecs 282 | generateRecord(~fields, ~subCodecs, ~ctx) 283 | } else if codec->is(arrayCodec) { 284 | `array<${walkCodec(codec->getSubcodecs->Array.getUnsafe(0), ctx)}>` 285 | } else if codec->is(tupleCodec) { 286 | `(${codec 287 | ->getSubcodecs 288 | ->Array.map(subCodec => walkCodec(subCodec, ctx)) 289 | ->Array.join(", ")})` 290 | } else if codec->is(rangeCodec) { 291 | let subCodec = codec->getSubcodecs->Array.getUnsafe(0) 292 | if !(subCodec->is(scalarCodec)) { 293 | panic("expected range subtype to be scalar type") 294 | } else { 295 | `EdgeDB.Range.t<${subCodec->tsType}>` 296 | } 297 | } else { 298 | panic(`Unexpected codec kind: ${String.make(codec->getKind)}}`) 299 | } 300 | } 301 | } 302 | 303 | let analyzeQuery = async (client: EdgeDB.Client.t, query: string, ~path): result< 304 | QueryType.t, 305 | string, 306 | > => { 307 | let pool = client->getPool 308 | let holder = await pool->BaseClientPool.acquireHolder(BaseClientPool.getDefaultOptions()) 309 | let errorMessage = ref(None) 310 | 311 | let parseResult = try { 312 | let cxn = await holder->BaseClientPool.Holder._getConnection 313 | let parseResult = 314 | await cxn->BaseClientPool.Connection._parse( 315 | query, 316 | BINARY, 317 | MANY, 318 | BaseClientPool.Connection.session->BaseClientPool.Connection.getSessionDefaults, 319 | ) 320 | await holder->BaseClientPool.Holder.release 321 | Some(parseResult) 322 | } catch { 323 | | Exn.Error(err) => 324 | errorMessage := err->Exn.message 325 | Console.error( 326 | `${CliUtils.colorRed("ERROR in file")}: ${path}:\n${errorMessage.contents->Option.getOr( 327 | "-", 328 | )}`, 329 | ) 330 | await holder->BaseClientPool.Holder.release 331 | None 332 | } 333 | 334 | switch parseResult { 335 | | Some((cardinality, inCodec, outCodec, _, _, _)) => 336 | let distinctTypes = Set.make() 337 | let args = WalkCodec.walkCodec( 338 | inCodec, 339 | { 340 | currentPath: ["args"], 341 | distinctTypes, 342 | optionalNulls: true, 343 | }, 344 | ) 345 | let result = WalkCodec.walkCodec( 346 | outCodec, 347 | { 348 | optionalNulls: false, 349 | currentPath: ["response"], 350 | distinctTypes, 351 | }, 352 | )->generateSetType(cardinality) 353 | // walkCodec only handles object codec and named tuple codec. 354 | // If distinctTypes is empty, or it is missing a response type, 355 | // then we know we are working with a primitive return type. 356 | if ( 357 | distinctTypes->Set.size === 0 || 358 | !( 359 | distinctTypes 360 | ->Set.values 361 | ->Iterator.toArrayWithMapper(t => String.includes(t, "response")) 362 | ->Array.includes(true) 363 | ) 364 | ) { 365 | WalkCodec.generatePrimitive( 366 | ~codec=outCodec, 367 | ~ctx={optionalNulls: false, currentPath: ["response"], distinctTypes}, 368 | ) 369 | } 370 | Ok({ 371 | result, 372 | args, 373 | cardinality, 374 | query, 375 | distinctTypes, 376 | }) 377 | | _ => 378 | Error( 379 | errorMessage.contents->Option.getOr( 380 | "This query has an unknown EdgeDB error. Please check the query and recompile.", 381 | ), 382 | ) 383 | } 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /cli/EdgeDbGenerator__Utils.res: -------------------------------------------------------------------------------- 1 | module Adapter = { 2 | // This is a big mess and should be cleaned up at some point 3 | 4 | type stdin = {isTTY: bool} 5 | @send external onData: (stdin, @as("data") _, string => unit) => unit = "on" 6 | @send external onLine: (stdin, @as("line") _, string => unit) => unit = "on" 7 | @send external onError: (stdin, @as("error") _, string => unit) => unit = "on" 8 | @send external onEnd: (stdin, @as("end") _, unit => unit) => unit = "on" 9 | @send external onClose: (stdin, @as("close") _, unit => unit) => unit = "on" 10 | type stdout = {isTTY: bool} 11 | type process = {cwd: unit => string, stdin: stdin, stdout: stdout} 12 | @send external exit: (process, int) => 'any = "exit" 13 | 14 | type fs = {access: string => promise} 15 | type inputParams = {@dead silent?: bool} 16 | type t = { 17 | process: process, 18 | fs: fs, 19 | input: (string, ~params: inputParams=?) => promise, 20 | } 21 | } 22 | 23 | @module("edgedb") external adapter: Adapter.t = "adapter" 24 | 25 | let isTTY = () => { 26 | adapter.process.stdin.isTTY && adapter.process.stdout.isTTY 27 | } 28 | 29 | let promptForPassword = async (username: string) => { 30 | if !isTTY() { 31 | panic( 32 | `Cannot use --password option in non-interactive mode. ` ++ `To read password from stdin use the --password-from-stdin option.`, 33 | ) 34 | } 35 | 36 | await adapter.input(`Password for '${username}': `, ~params={silent: true}) 37 | } 38 | 39 | let readPasswordFromStdin = () => { 40 | if adapter.process.stdin.isTTY { 41 | panic(`Cannot read password from stdin: stdin is a TTY.`) 42 | } 43 | 44 | Promise.make((resolve, _reject) => { 45 | let data = ref("") 46 | adapter.process.stdin->Adapter.onData(chunk => data := data.contents ++ chunk) 47 | adapter.process.stdin->Adapter.onEnd(() => resolve(data.contents->String.trimEnd)) 48 | }) 49 | } 50 | 51 | let capitalizeString = str => 52 | `${str->String.slice(~start=0, ~end=1)->String.toUpperCase}${str->String.sliceToEnd(~start=1)}` 53 | 54 | let uncapitalizeString = str => 55 | `${str->String.slice(~start=0, ~end=1)->String.toLowerCase}${str->String.sliceToEnd(~start=1)}` 56 | 57 | let pathToName = path => { 58 | let name = path->Array.join("__") 59 | 60 | // Make valid ReScript record name. 61 | uncapitalizeString(name) 62 | } 63 | 64 | module Errors = { 65 | type loc = { 66 | line: int, 67 | col: int, 68 | } 69 | 70 | type error = { 71 | start: loc, 72 | end: loc, 73 | text: string, 74 | } 75 | 76 | let extractFromString = (error, ~startLoc: loc): option => { 77 | let lines = error->String.split("\n") 78 | let text = [] 79 | let codeText = [] 80 | 81 | lines->Array.forEach(line => { 82 | let trimmed = line->String.trim 83 | let isCodeText = 84 | trimmed->String.startsWith("|") || 85 | (line->String.startsWith(" ") && trimmed->String.includes(" | ")) 86 | 87 | if isCodeText { 88 | codeText->Array.push(line) 89 | } else { 90 | text->Array.push(line) 91 | } 92 | }) 93 | 94 | let lineWithHighlight = codeText->Array.at(-1) 95 | let lineWithQueryLineNum = codeText->Array.at(-2) 96 | 97 | let line = switch lineWithQueryLineNum { 98 | | None => None 99 | | Some(line) => 100 | line 101 | ->String.trim 102 | ->String.split("|") 103 | ->Array.get(0) 104 | ->Option.flatMap(l => l->String.trim->Int.fromString) 105 | ->Option.map(l => 106 | /* EdgeDB error lines are not 0 based, so account for that */ 107 | l + startLoc.line - 1 108 | ) 109 | } 110 | 111 | switch (line, lineWithHighlight) { 112 | | (Some(line), Some(content)) => 113 | switch content->String.split("|") { 114 | | [_, highlightRow] => 115 | let colStart = 116 | highlightRow->String.length - highlightRow->String.trimStart->String.length - 1 117 | let colEnd = 118 | colStart + 119 | highlightRow 120 | ->String.split("") 121 | ->Array.filter(v => v === "^") 122 | ->Array.length 123 | 124 | Some({ 125 | text: text->Array.filter(l => l->String.trim !== "")->Array.join("\n"), 126 | start: { 127 | line, 128 | col: colStart, 129 | }, 130 | end: { 131 | line, 132 | col: colEnd, 133 | }, 134 | }) 135 | | _ => None 136 | } 137 | | _ => None 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /cli/UnusedSelections.res: -------------------------------------------------------------------------------- 1 | let adapter = EdgeDbGenerator__Utils.adapter 2 | 3 | let extractFileNameRegExp = %re("/\/([^\/]*?)__edgeql\.res/") 4 | 5 | let extractFileName = line => { 6 | switch line->String.trim->String.match(extractFileNameRegExp) { 7 | | Some([_, fileName]) => Some(fileName) 8 | | _ => None 9 | } 10 | } 11 | 12 | type extractedLineInfo = { 13 | queryName: string, 14 | recordPath: option>, 15 | fieldName: string, 16 | } 17 | let extractLineInfo = line => { 18 | switch line->String.trim->String.split(" ")->List.fromArray { 19 | | list{info, ...rest} => 20 | let restText = rest->List.toArray->Array.join(" ")->String.trim 21 | switch (info->String.split("."), restText) { 22 | | ([queryName, recordName, fieldName], "is a record label never used to read a value") 23 | if !(recordName->String.startsWith("args")) => 24 | Some({ 25 | queryName, 26 | fieldName, 27 | recordPath: switch recordName->String.split("__")->Array.sliceToEnd(~start=1) { 28 | | [] => None 29 | | path => Some(path) 30 | }, 31 | }) 32 | | _ => None 33 | } 34 | | _ => None 35 | } 36 | } 37 | 38 | let extractFromReanalyzeOutput = (output: string) => { 39 | output 40 | ->String.split("\n\n") 41 | ->Array.filterMap(text => { 42 | if text->String.includes("__edgeql.res") { 43 | let lines = text->String.split("\n") 44 | let index = ref(0) 45 | lines->Array.findMap(line => { 46 | let currentIndex = index.contents 47 | index := currentIndex + 1 48 | 49 | switch (line->String.includes("__edgeql.res"), lines[currentIndex + 1]) { 50 | | (true, Some(nextLine)) => 51 | switch (extractFileName(line), extractLineInfo(nextLine)) { 52 | | (Some(fileName), Some(fileInfo)) => Some((fileName, fileInfo)) 53 | | _ => None 54 | } 55 | | _ => None 56 | } 57 | }) 58 | } else { 59 | None 60 | } 61 | }) 62 | } 63 | 64 | @module("child_process") 65 | external childProcess: 'a = "default" 66 | 67 | let readReanalyzeOutput = () => { 68 | Promise.make((resolve, reject) => { 69 | let p = childProcess["spawn"]("npx", ["--yes", "@rescript/tools", "reanalyze", "-dce"]) 70 | 71 | switch p["stdout"]->Nullable.toOption { 72 | | None => 73 | Console.error("Something went wrong") 74 | reject() 75 | adapter.process->EdgeDbGenerator__Utils.Adapter.exit(1) 76 | | Some(stdout) => 77 | let data = ref("") 78 | stdout->EdgeDbGenerator__Utils.Adapter.onData(d => { 79 | data := data.contents ++ d 80 | }) 81 | switch p["stderr"]->Nullable.toOption { 82 | | None => () 83 | | Some(stderr) => 84 | stderr->EdgeDbGenerator__Utils.Adapter.onData(e => { 85 | if e->String.includes("End_of_file") { 86 | Console.error(`Something went wrong trying to analyze the ReScript project. Try cleaning your ReScript project and rebuilding it from scratch before trying again.`) 87 | } 88 | reject() 89 | adapter.process->EdgeDbGenerator__Utils.Adapter.exit(1) 90 | }) 91 | } 92 | stdout->EdgeDbGenerator__Utils.Adapter.onClose(() => { 93 | resolve(data.contents->extractFromReanalyzeOutput) 94 | }) 95 | } 96 | }) 97 | } 98 | 99 | let reportResults = results => { 100 | if results->Array.length === 0 { 101 | Console.log("No unused selections found. Great job!") 102 | } else { 103 | Console.log( 104 | `\n${CliUtils.colorRed("✘")} Found ${results 105 | ->Array.length 106 | ->Int.toString} unused selections.`, 107 | ) 108 | let byFile = Dict.make() 109 | results->Array.forEach(((queryName, fileInfo)) => { 110 | switch byFile->Dict.get(queryName) { 111 | | None => byFile->Dict.set(queryName, [fileInfo]) 112 | | Some(item) => item->Array.push(fileInfo) 113 | } 114 | }) 115 | byFile 116 | ->Dict.toArray 117 | ->Array.forEach(((fileName, fileInfos)) => { 118 | Console.log(`\nFile "${fileName}.res":`) 119 | let byQuery = Dict.make() 120 | fileInfos->Array.forEach(fileInfo => { 121 | switch byQuery->Dict.get(fileInfo.queryName) { 122 | | None => byQuery->Dict.set(fileInfo.queryName, [fileInfo]) 123 | | Some(item) => item->Array.push(fileInfo) 124 | } 125 | }) 126 | byQuery 127 | ->Dict.toArray 128 | ->Array.forEach(((queryName, fileInfo)) => { 129 | Console.log(` In query "${queryName}":`) 130 | fileInfo->Array.forEach( 131 | fileInfo => { 132 | let contextMessage = switch fileInfo.recordPath { 133 | | None | Some([]) => "" 134 | | Some(path) => `${path->Array.join(".")}` 135 | } 136 | Console.log(` - ${contextMessage}.${fileInfo.fieldName}`) 137 | }, 138 | ) 139 | }) 140 | }) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /dbTestProject/dbschema/default.esdl: -------------------------------------------------------------------------------- 1 | module default { 2 | scalar type PublishStatus extending enum; 3 | 4 | type Pet { 5 | required property name -> str; 6 | property nickname -> str; 7 | property paws -> int32; 8 | } 9 | 10 | type Person { 11 | required property name -> str; 12 | property age -> int32; 13 | multi link pets -> Pet; 14 | link typesDump -> TypesDump; 15 | } 16 | 17 | type TypesDump { 18 | property date -> datetime; 19 | property localDateTime -> cal::local_datetime; 20 | property localDate -> cal::local_date; 21 | property relativeDuration -> cal::relative_duration; 22 | property duration -> duration; 23 | property dateDuration -> cal::date_duration; 24 | property localTime -> cal::local_time; 25 | property json -> json; 26 | 27 | } 28 | 29 | type Movie { 30 | required property title -> str; 31 | required property status -> PublishStatus; 32 | multi link actors -> Person; 33 | } 34 | }; -------------------------------------------------------------------------------- /dbTestProject/dbschema/migrations/00001.edgeql: -------------------------------------------------------------------------------- 1 | CREATE MIGRATION m1glcdxeyvhdseptas3yrpq2bt44ob76hfqnenri7clygayy76n3sa 2 | ONTO initial 3 | { 4 | CREATE TYPE default::Pet { 5 | CREATE REQUIRED PROPERTY name: std::str; 6 | CREATE PROPERTY paws: std::int32; 7 | }; 8 | CREATE TYPE default::Person { 9 | CREATE MULTI LINK pets: default::Pet; 10 | CREATE PROPERTY age: std::int32; 11 | CREATE REQUIRED PROPERTY name: std::str; 12 | }; 13 | CREATE SCALAR TYPE default::PublishStatus EXTENDING enum; 14 | CREATE TYPE default::Movie { 15 | CREATE MULTI LINK actors: default::Person; 16 | CREATE REQUIRED PROPERTY status: default::PublishStatus; 17 | CREATE REQUIRED PROPERTY title: std::str; 18 | }; 19 | 20 | 21 | }; 22 | -------------------------------------------------------------------------------- /dbTestProject/dbschema/migrations/00002.edgeql: -------------------------------------------------------------------------------- 1 | CREATE MIGRATION m1i6q7vybrrde2mfclrnfjrvvgsuxpkm4yptsr6suwqh2i6iii4zia 2 | ONTO m1glcdxeyvhdseptas3yrpq2bt44ob76hfqnenri7clygayy76n3sa 3 | { 4 | ALTER TYPE default::Pet { 5 | CREATE PROPERTY nickname: std::str; 6 | }; 7 | 8 | INSERT Person { 9 | name := 'John Doe', 10 | age := 30, 11 | pets := { 12 | (INSERT Pet { 13 | name := 'Rex', 14 | paws := 4 15 | }), 16 | (INSERT Pet { 17 | name := 'Fluffy', 18 | paws := 4 19 | }) 20 | } 21 | }; 22 | 23 | INSERT Person { 24 | name := 'Jane Smith', 25 | age := 28, 26 | pets := { 27 | (INSERT Pet { 28 | name := 'Bella', 29 | paws := 4 30 | }) 31 | } 32 | }; 33 | 34 | INSERT Person { 35 | name := 'Bob Johnson', 36 | age := 35, 37 | pets := { 38 | (INSERT Pet { 39 | name := 'Max', 40 | paws := 4 41 | }) 42 | } 43 | }; 44 | 45 | INSERT Movie { 46 | title := 'The Great Adventure', 47 | status := 'Published', 48 | actors := { 49 | (SELECT Person FILTER .name = 'John Doe') 50 | } 51 | }; 52 | 53 | INSERT Movie { 54 | title := 'The Mystery', 55 | status := 'Published', 56 | actors := { 57 | (SELECT Person FILTER .name = 'Jane Smith') 58 | } 59 | }; 60 | 61 | INSERT Movie { 62 | title := 'The Thriller', 63 | status := 'Published', 64 | actors := { 65 | (SELECT Person) 66 | } 67 | }; 68 | }; 69 | -------------------------------------------------------------------------------- /dbTestProject/dbschema/migrations/00003.edgeql: -------------------------------------------------------------------------------- 1 | CREATE MIGRATION m1x7evrp4v4ervukqqmdu4rjwiakf45iw2q7nazyumw77ceb24g6wq 2 | ONTO m1i6q7vybrrde2mfclrnfjrvvgsuxpkm4yptsr6suwqh2i6iii4zia 3 | { 4 | CREATE TYPE default::TypesDump { 5 | CREATE PROPERTY date: std::datetime; 6 | CREATE PROPERTY dateDuration: cal::date_duration; 7 | CREATE PROPERTY duration: std::duration; 8 | CREATE PROPERTY json: std::json; 9 | CREATE PROPERTY localDate: cal::local_date; 10 | CREATE PROPERTY localDateTime: cal::local_datetime; 11 | CREATE PROPERTY localTime: cal::local_time; 12 | CREATE PROPERTY relativeDuration: cal::relative_duration; 13 | }; 14 | ALTER TYPE default::Person { 15 | CREATE LINK typesDump: default::TypesDump; 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /dbTestProject/edgedb.toml: -------------------------------------------------------------------------------- 1 | [edgedb] 2 | server-version = "3.4" 3 | -------------------------------------------------------------------------------- /dbTestProject/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rescript", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "rescript", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@rescript/core": "1.3.0", 13 | "rescript": "^11.1.0", 14 | "rescript-edgedb": "^0.7.0", 15 | "rescript-embed-lang": "0.4.0" 16 | } 17 | }, 18 | "node_modules/@nodelib/fs.scandir": { 19 | "version": "2.1.5", 20 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 21 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 22 | "dependencies": { 23 | "@nodelib/fs.stat": "2.0.5", 24 | "run-parallel": "^1.1.9" 25 | }, 26 | "engines": { 27 | "node": ">= 8" 28 | } 29 | }, 30 | "node_modules/@nodelib/fs.stat": { 31 | "version": "2.0.5", 32 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 33 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 34 | "engines": { 35 | "node": ">= 8" 36 | } 37 | }, 38 | "node_modules/@nodelib/fs.walk": { 39 | "version": "1.2.8", 40 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 41 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 42 | "dependencies": { 43 | "@nodelib/fs.scandir": "2.1.5", 44 | "fastq": "^1.6.0" 45 | }, 46 | "engines": { 47 | "node": ">= 8" 48 | } 49 | }, 50 | "node_modules/@rescript/core": { 51 | "version": "1.3.0", 52 | "resolved": "https://registry.npmjs.org/@rescript/core/-/core-1.3.0.tgz", 53 | "integrity": "sha512-wNZOZ63sYcaIYZCmTZeIPCeLa3HCGgPbIOR8zjyNkoBYUlxNV8Nb2ZyqlXR5Mb9ttvv8fTV56JbKhyVEZEYo8g==", 54 | "peerDependencies": { 55 | "rescript": "^11.1.0-rc.7" 56 | } 57 | }, 58 | "node_modules/anymatch": { 59 | "version": "3.1.3", 60 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 61 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 62 | "dependencies": { 63 | "normalize-path": "^3.0.0", 64 | "picomatch": "^2.0.4" 65 | }, 66 | "engines": { 67 | "node": ">= 8" 68 | } 69 | }, 70 | "node_modules/binary-extensions": { 71 | "version": "2.2.0", 72 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 73 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 74 | "engines": { 75 | "node": ">=8" 76 | } 77 | }, 78 | "node_modules/braces": { 79 | "version": "3.0.2", 80 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 81 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 82 | "dependencies": { 83 | "fill-range": "^7.0.1" 84 | }, 85 | "engines": { 86 | "node": ">=8" 87 | } 88 | }, 89 | "node_modules/chokidar": { 90 | "version": "3.5.3", 91 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 92 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 93 | "funding": [ 94 | { 95 | "type": "individual", 96 | "url": "https://paulmillr.com/funding/" 97 | } 98 | ], 99 | "dependencies": { 100 | "anymatch": "~3.1.2", 101 | "braces": "~3.0.2", 102 | "glob-parent": "~5.1.2", 103 | "is-binary-path": "~2.1.0", 104 | "is-glob": "~4.0.1", 105 | "normalize-path": "~3.0.0", 106 | "readdirp": "~3.6.0" 107 | }, 108 | "engines": { 109 | "node": ">= 8.10.0" 110 | }, 111 | "optionalDependencies": { 112 | "fsevents": "~2.3.2" 113 | } 114 | }, 115 | "node_modules/edgedb": { 116 | "version": "1.4.1", 117 | "resolved": "https://registry.npmjs.org/edgedb/-/edgedb-1.4.1.tgz", 118 | "integrity": "sha512-GMLeDcTR3lSzpIQCLM6DpcHrVre+nAICA091c0Jfpkd/RANaV7+RSEnIBceDg2rHQpYdCEGW3swULaoURsAQzg==", 119 | "peer": true, 120 | "bin": { 121 | "edgeql-js": "dist/cli.js" 122 | }, 123 | "engines": { 124 | "node": ">= 12.0.0" 125 | } 126 | }, 127 | "node_modules/fast-glob": { 128 | "version": "3.3.2", 129 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 130 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 131 | "dependencies": { 132 | "@nodelib/fs.stat": "^2.0.2", 133 | "@nodelib/fs.walk": "^1.2.3", 134 | "glob-parent": "^5.1.2", 135 | "merge2": "^1.3.0", 136 | "micromatch": "^4.0.4" 137 | }, 138 | "engines": { 139 | "node": ">=8.6.0" 140 | } 141 | }, 142 | "node_modules/fastq": { 143 | "version": "1.16.0", 144 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", 145 | "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", 146 | "dependencies": { 147 | "reusify": "^1.0.4" 148 | } 149 | }, 150 | "node_modules/fill-range": { 151 | "version": "7.0.1", 152 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 153 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 154 | "dependencies": { 155 | "to-regex-range": "^5.0.1" 156 | }, 157 | "engines": { 158 | "node": ">=8" 159 | } 160 | }, 161 | "node_modules/fsevents": { 162 | "version": "2.3.3", 163 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 164 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 165 | "hasInstallScript": true, 166 | "optional": true, 167 | "os": [ 168 | "darwin" 169 | ], 170 | "engines": { 171 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 172 | } 173 | }, 174 | "node_modules/glob-parent": { 175 | "version": "5.1.2", 176 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 177 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 178 | "dependencies": { 179 | "is-glob": "^4.0.1" 180 | }, 181 | "engines": { 182 | "node": ">= 6" 183 | } 184 | }, 185 | "node_modules/is-binary-path": { 186 | "version": "2.1.0", 187 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 188 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 189 | "dependencies": { 190 | "binary-extensions": "^2.0.0" 191 | }, 192 | "engines": { 193 | "node": ">=8" 194 | } 195 | }, 196 | "node_modules/is-extglob": { 197 | "version": "2.1.1", 198 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 199 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 200 | "engines": { 201 | "node": ">=0.10.0" 202 | } 203 | }, 204 | "node_modules/is-glob": { 205 | "version": "4.0.3", 206 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 207 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 208 | "dependencies": { 209 | "is-extglob": "^2.1.1" 210 | }, 211 | "engines": { 212 | "node": ">=0.10.0" 213 | } 214 | }, 215 | "node_modules/is-number": { 216 | "version": "7.0.0", 217 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 218 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 219 | "engines": { 220 | "node": ">=0.12.0" 221 | } 222 | }, 223 | "node_modules/merge2": { 224 | "version": "1.4.1", 225 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 226 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 227 | "engines": { 228 | "node": ">= 8" 229 | } 230 | }, 231 | "node_modules/micromatch": { 232 | "version": "4.0.5", 233 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 234 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 235 | "dependencies": { 236 | "braces": "^3.0.2", 237 | "picomatch": "^2.3.1" 238 | }, 239 | "engines": { 240 | "node": ">=8.6" 241 | } 242 | }, 243 | "node_modules/normalize-path": { 244 | "version": "3.0.0", 245 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 246 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 247 | "engines": { 248 | "node": ">=0.10.0" 249 | } 250 | }, 251 | "node_modules/picomatch": { 252 | "version": "2.3.1", 253 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 254 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 255 | "engines": { 256 | "node": ">=8.6" 257 | }, 258 | "funding": { 259 | "url": "https://github.com/sponsors/jonschlinkert" 260 | } 261 | }, 262 | "node_modules/queue-microtask": { 263 | "version": "1.2.3", 264 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 265 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 266 | "funding": [ 267 | { 268 | "type": "github", 269 | "url": "https://github.com/sponsors/feross" 270 | }, 271 | { 272 | "type": "patreon", 273 | "url": "https://www.patreon.com/feross" 274 | }, 275 | { 276 | "type": "consulting", 277 | "url": "https://feross.org/support" 278 | } 279 | ] 280 | }, 281 | "node_modules/readdirp": { 282 | "version": "3.6.0", 283 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 284 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 285 | "dependencies": { 286 | "picomatch": "^2.2.1" 287 | }, 288 | "engines": { 289 | "node": ">=8.10.0" 290 | } 291 | }, 292 | "node_modules/rescript": { 293 | "version": "11.1.0", 294 | "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.0.tgz", 295 | "integrity": "sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==", 296 | "hasInstallScript": true, 297 | "bin": { 298 | "bsc": "bsc", 299 | "bstracing": "lib/bstracing", 300 | "rescript": "rescript" 301 | }, 302 | "engines": { 303 | "node": ">=10" 304 | } 305 | }, 306 | "node_modules/rescript-edgedb": { 307 | "version": "0.7.0", 308 | "resolved": "https://registry.npmjs.org/rescript-edgedb/-/rescript-edgedb-0.7.0.tgz", 309 | "integrity": "sha512-w1q6528iO0YKiP5CD7QyD5gcLqxahqHzhHoNxk7jfHgN4oVKlivUvJLgNu7Uzp6XSwlvEDWhqdW2NHY4Sq5+cQ==", 310 | "dependencies": { 311 | "chokidar": "^3.5.3", 312 | "fast-glob": "^3.3.1" 313 | }, 314 | "bin": { 315 | "rescript-edgedb": "dist/Cli.js" 316 | }, 317 | "peerDependencies": { 318 | "@rescript/core": ">= 1.3.0", 319 | "edgedb": ">= 1.3.6", 320 | "rescript": "^11.1.0", 321 | "rescript-embed-lang": ">= 0.4.0" 322 | } 323 | }, 324 | "node_modules/rescript-embed-lang": { 325 | "version": "0.4.0", 326 | "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.4.0.tgz", 327 | "integrity": "sha512-TEUfLW5zwOXpx3qvmzSqOinWjBbI0izTvGRllZ39hX/WeSZCZgf4ka7XDQ/chpRFHLMBVDJqJET7beyepF8Kow==", 328 | "hasInstallScript": true 329 | }, 330 | "node_modules/reusify": { 331 | "version": "1.0.4", 332 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 333 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 334 | "engines": { 335 | "iojs": ">=1.0.0", 336 | "node": ">=0.10.0" 337 | } 338 | }, 339 | "node_modules/run-parallel": { 340 | "version": "1.2.0", 341 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 342 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 343 | "funding": [ 344 | { 345 | "type": "github", 346 | "url": "https://github.com/sponsors/feross" 347 | }, 348 | { 349 | "type": "patreon", 350 | "url": "https://www.patreon.com/feross" 351 | }, 352 | { 353 | "type": "consulting", 354 | "url": "https://feross.org/support" 355 | } 356 | ], 357 | "dependencies": { 358 | "queue-microtask": "^1.2.2" 359 | } 360 | }, 361 | "node_modules/to-regex-range": { 362 | "version": "5.0.1", 363 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 364 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 365 | "dependencies": { 366 | "is-number": "^7.0.0" 367 | }, 368 | "engines": { 369 | "node": ">=8.0" 370 | } 371 | } 372 | }, 373 | "dependencies": { 374 | "@nodelib/fs.scandir": { 375 | "version": "2.1.5", 376 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 377 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 378 | "requires": { 379 | "@nodelib/fs.stat": "2.0.5", 380 | "run-parallel": "^1.1.9" 381 | } 382 | }, 383 | "@nodelib/fs.stat": { 384 | "version": "2.0.5", 385 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 386 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" 387 | }, 388 | "@nodelib/fs.walk": { 389 | "version": "1.2.8", 390 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 391 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 392 | "requires": { 393 | "@nodelib/fs.scandir": "2.1.5", 394 | "fastq": "^1.6.0" 395 | } 396 | }, 397 | "@rescript/core": { 398 | "version": "1.3.0", 399 | "resolved": "https://registry.npmjs.org/@rescript/core/-/core-1.3.0.tgz", 400 | "integrity": "sha512-wNZOZ63sYcaIYZCmTZeIPCeLa3HCGgPbIOR8zjyNkoBYUlxNV8Nb2ZyqlXR5Mb9ttvv8fTV56JbKhyVEZEYo8g==", 401 | "requires": {} 402 | }, 403 | "anymatch": { 404 | "version": "3.1.3", 405 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 406 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 407 | "requires": { 408 | "normalize-path": "^3.0.0", 409 | "picomatch": "^2.0.4" 410 | } 411 | }, 412 | "binary-extensions": { 413 | "version": "2.2.0", 414 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 415 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 416 | }, 417 | "braces": { 418 | "version": "3.0.2", 419 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 420 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 421 | "requires": { 422 | "fill-range": "^7.0.1" 423 | } 424 | }, 425 | "chokidar": { 426 | "version": "3.5.3", 427 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 428 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 429 | "requires": { 430 | "anymatch": "~3.1.2", 431 | "braces": "~3.0.2", 432 | "fsevents": "~2.3.2", 433 | "glob-parent": "~5.1.2", 434 | "is-binary-path": "~2.1.0", 435 | "is-glob": "~4.0.1", 436 | "normalize-path": "~3.0.0", 437 | "readdirp": "~3.6.0" 438 | } 439 | }, 440 | "edgedb": { 441 | "version": "1.4.1", 442 | "resolved": "https://registry.npmjs.org/edgedb/-/edgedb-1.4.1.tgz", 443 | "integrity": "sha512-GMLeDcTR3lSzpIQCLM6DpcHrVre+nAICA091c0Jfpkd/RANaV7+RSEnIBceDg2rHQpYdCEGW3swULaoURsAQzg==", 444 | "peer": true 445 | }, 446 | "fast-glob": { 447 | "version": "3.3.2", 448 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 449 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 450 | "requires": { 451 | "@nodelib/fs.stat": "^2.0.2", 452 | "@nodelib/fs.walk": "^1.2.3", 453 | "glob-parent": "^5.1.2", 454 | "merge2": "^1.3.0", 455 | "micromatch": "^4.0.4" 456 | } 457 | }, 458 | "fastq": { 459 | "version": "1.16.0", 460 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", 461 | "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", 462 | "requires": { 463 | "reusify": "^1.0.4" 464 | } 465 | }, 466 | "fill-range": { 467 | "version": "7.0.1", 468 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 469 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 470 | "requires": { 471 | "to-regex-range": "^5.0.1" 472 | } 473 | }, 474 | "fsevents": { 475 | "version": "2.3.3", 476 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 477 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 478 | "optional": true 479 | }, 480 | "glob-parent": { 481 | "version": "5.1.2", 482 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 483 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 484 | "requires": { 485 | "is-glob": "^4.0.1" 486 | } 487 | }, 488 | "is-binary-path": { 489 | "version": "2.1.0", 490 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 491 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 492 | "requires": { 493 | "binary-extensions": "^2.0.0" 494 | } 495 | }, 496 | "is-extglob": { 497 | "version": "2.1.1", 498 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 499 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" 500 | }, 501 | "is-glob": { 502 | "version": "4.0.3", 503 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 504 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 505 | "requires": { 506 | "is-extglob": "^2.1.1" 507 | } 508 | }, 509 | "is-number": { 510 | "version": "7.0.0", 511 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 512 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 513 | }, 514 | "merge2": { 515 | "version": "1.4.1", 516 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 517 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" 518 | }, 519 | "micromatch": { 520 | "version": "4.0.5", 521 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 522 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 523 | "requires": { 524 | "braces": "^3.0.2", 525 | "picomatch": "^2.3.1" 526 | } 527 | }, 528 | "normalize-path": { 529 | "version": "3.0.0", 530 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 531 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 532 | }, 533 | "picomatch": { 534 | "version": "2.3.1", 535 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 536 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" 537 | }, 538 | "queue-microtask": { 539 | "version": "1.2.3", 540 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 541 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" 542 | }, 543 | "readdirp": { 544 | "version": "3.6.0", 545 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 546 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 547 | "requires": { 548 | "picomatch": "^2.2.1" 549 | } 550 | }, 551 | "rescript": { 552 | "version": "11.1.0", 553 | "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.0.tgz", 554 | "integrity": "sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==" 555 | }, 556 | "rescript-edgedb": { 557 | "version": "0.7.0", 558 | "resolved": "https://registry.npmjs.org/rescript-edgedb/-/rescript-edgedb-0.7.0.tgz", 559 | "integrity": "sha512-w1q6528iO0YKiP5CD7QyD5gcLqxahqHzhHoNxk7jfHgN4oVKlivUvJLgNu7Uzp6XSwlvEDWhqdW2NHY4Sq5+cQ==", 560 | "requires": { 561 | "chokidar": "^3.5.3", 562 | "fast-glob": "^3.3.1" 563 | } 564 | }, 565 | "rescript-embed-lang": { 566 | "version": "0.4.0", 567 | "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.4.0.tgz", 568 | "integrity": "sha512-TEUfLW5zwOXpx3qvmzSqOinWjBbI0izTvGRllZ39hX/WeSZCZgf4ka7XDQ/chpRFHLMBVDJqJET7beyepF8Kow==" 569 | }, 570 | "reusify": { 571 | "version": "1.0.4", 572 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 573 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" 574 | }, 575 | "run-parallel": { 576 | "version": "1.2.0", 577 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 578 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 579 | "requires": { 580 | "queue-microtask": "^1.2.2" 581 | } 582 | }, 583 | "to-regex-range": { 584 | "version": "5.0.1", 585 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 586 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 587 | "requires": { 588 | "is-number": "^7.0.0" 589 | } 590 | } 591 | } 592 | } 593 | -------------------------------------------------------------------------------- /dbTestProject/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rescript", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "bun test test/", 8 | "build": "npm run build:edgedb && npm run build:rescript", 9 | "build:rescript": "rescript build -with-deps", 10 | "build:edgedb": "../dist/Cli.js generate --output ./src/__generated__ --src ./src", 11 | "watch:edgedb": "npm run build:edgedb -- --watch" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@rescript/core": "1.3.0", 18 | "rescript": "^11.1.0", 19 | "rescript-edgedb": "^0.7.0", 20 | "rescript-embed-lang": "0.4.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dbTestProject/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "res", 3 | "uncurried": true, 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "test", 11 | "type": "dev" 12 | }, 13 | { 14 | "dir": "../src" 15 | } 16 | ], 17 | "package-specs": { 18 | "module": "esmodule", 19 | "in-source": true 20 | }, 21 | "suffix": ".mjs", 22 | "bs-dependencies": ["@rescript/core", "rescript-edgedb"], 23 | "bsc-flags": ["-open RescriptCore"], 24 | "ppx-flags": ["rescript-embed-lang/ppx"] 25 | } 26 | -------------------------------------------------------------------------------- /dbTestProject/src/Main.res: -------------------------------------------------------------------------------- 1 | // This file is just for marking fields as used, so we can try out the unused selections CLI. 2 | let client = EdgeDB.Client.make() 3 | 4 | let movies = await client->Movies.allMovies 5 | 6 | let _ = movies->Array.forEach(movie => { 7 | let _id = movie.id 8 | let _actors = movie.actors->Array.forEach(actor => { 9 | let _id = actor.id 10 | let _name = actor.name 11 | }) 12 | }) 13 | 14 | let moviesNested = await client->Movies.Nested.query 15 | 16 | let _ = moviesNested->Array.forEach(movie => { 17 | let _id = movie.id 18 | let _title = movie.title 19 | let _actors = movie.actors->Array.forEach(actor => { 20 | let _id = actor.id 21 | let _name = actor.name 22 | let _numberOfPets = actor.numberOfPets 23 | }) 24 | }) 25 | 26 | let singleMovie = await client->Movies.movieByTitle(~title="The Great Adventure") 27 | 28 | let _ = switch singleMovie { 29 | | Some({ 30 | title, 31 | actors: [ 32 | { 33 | id, 34 | numberOfPets, 35 | typesDump: Value({ 36 | date, 37 | localDateTime, 38 | localDate, 39 | relativeDuration, 40 | duration, 41 | dateDuration, 42 | localTime, 43 | json, 44 | }), 45 | }, 46 | ], 47 | }) => 48 | let _id = id 49 | let _title = title 50 | let _numberOfPets = numberOfPets 51 | Console.log(( 52 | date, 53 | localDateTime, 54 | localDate, 55 | relativeDuration, 56 | duration, 57 | dateDuration, 58 | localTime, 59 | json, 60 | )) 61 | | _ => () 62 | } 63 | -------------------------------------------------------------------------------- /dbTestProject/src/Movies.res: -------------------------------------------------------------------------------- 1 | let allMovies = client => { 2 | let query = %edgeql(` 3 | # @name allMovies 4 | select Movie { 5 | id, 6 | title, 7 | actors: { 8 | id, 9 | name, 10 | numberOfPets := count(.pets) 11 | } 12 | } order by .title 13 | `) 14 | 15 | // let query = %edgeql(` 16 | //# @name allMovies2 17 | //select Movie { 18 | // id 19 | //} order by .title 20 | //`) 21 | 22 | client->query 23 | } 24 | 25 | let countAllMovies = client => { 26 | let query = %edgeql(` 27 | # @name countAllMovies 28 | select count(Movie); 29 | `) 30 | 31 | client->query 32 | } 33 | 34 | let countAllMoviesWithParam = (client, ~title) => { 35 | let query = %edgeql(` 36 | # @name countAllMoviesWithParam 37 | select count(Movie filter .title = $title); 38 | `) 39 | 40 | client->query({title: title}) 41 | } 42 | 43 | let testBool = client => { 44 | let query = %edgeql(` 45 | # @name testBool 46 | select '!' IN {'hello', 'world'}; 47 | `) 48 | 49 | client->query 50 | } 51 | 52 | let testString = client => { 53 | let query = %edgeql(` 54 | # @name testString 55 | select r'A raw \n string'; 56 | `) 57 | 58 | client->query 59 | } 60 | 61 | module Nested = { 62 | let query = %edgeql(` 63 | # @name allMoviesNested 64 | select Movie { 65 | id, 66 | title, 67 | actors: { 68 | id, 69 | name, 70 | numberOfPets := count(.pets) 71 | } 72 | } order by .title 73 | `) 74 | } 75 | 76 | let movieByTitle = (client, ~title) => { 77 | let query = %edgeql(` 78 | # @name movieByTitle 79 | select Movie { 80 | id, 81 | title, 82 | actors: { 83 | id, 84 | name, 85 | numberOfPets := count(.pets), 86 | typesDump: { 87 | date, 88 | localDateTime, 89 | localDate, 90 | relativeDuration, 91 | duration, 92 | dateDuration, 93 | localTime, 94 | json 95 | } 96 | } 97 | } 98 | filter .title = $title 99 | limit 1 100 | `) 101 | 102 | client->query({ 103 | title: title, 104 | }) 105 | } 106 | 107 | module AddActor = %edgeql(` 108 | # @name AddActor 109 | insert Person { 110 | name := $name 111 | } 112 | `) 113 | 114 | let addActor = AddActor.transaction 115 | 116 | module RemoveActor = %edgeql(` 117 | # @name RemoveActor 118 | delete Person filter .id = $id 119 | `) 120 | 121 | let removeActor = RemoveActor.transaction 122 | -------------------------------------------------------------------------------- /dbTestProject/src/__generated__/Movies__edgeql.res: -------------------------------------------------------------------------------- 1 | // @sourceHash 8b3b5cd8dbf0f74596f7a74af8554e12 2 | 3 | module AllMovies = { 4 | let queryText = `# @name allMovies 5 | select Movie { 6 | id, 7 | title, 8 | actors: { 9 | id, 10 | name, 11 | numberOfPets := count(.pets) 12 | } 13 | } order by .title` 14 | 15 | type response__actors = { 16 | id: string, 17 | name: string, 18 | numberOfPets: float, 19 | } 20 | 21 | type response = { 22 | id: string, 23 | title: string, 24 | actors: array, 25 | } 26 | 27 | @live 28 | let query = (client: EdgeDB.Client.t): promise> => { 29 | client->EdgeDB.QueryHelpers.many(queryText) 30 | } 31 | 32 | @live 33 | let transaction = (transaction: EdgeDB.Transaction.t): promise> => { 34 | transaction->EdgeDB.TransactionHelpers.many(queryText) 35 | } 36 | } 37 | 38 | module CountAllMovies = { 39 | let queryText = `# @name countAllMovies 40 | select count(Movie);` 41 | 42 | type response = float 43 | 44 | @live 45 | let query = (client: EdgeDB.Client.t): promise> => { 46 | client->EdgeDB.QueryHelpers.singleRequired(queryText) 47 | } 48 | 49 | @live 50 | let transaction = (transaction: EdgeDB.Transaction.t): promise> => { 51 | transaction->EdgeDB.TransactionHelpers.singleRequired(queryText) 52 | } 53 | } 54 | 55 | module CountAllMoviesWithParam = { 56 | let queryText = `# @name countAllMoviesWithParam 57 | select count(Movie filter .title = $title);` 58 | 59 | @live 60 | type args = { 61 | title: string, 62 | } 63 | 64 | type response = float 65 | 66 | @live 67 | let query = (client: EdgeDB.Client.t, args: args): promise> => { 68 | client->EdgeDB.QueryHelpers.singleRequired(queryText, ~args) 69 | } 70 | 71 | @live 72 | let transaction = (transaction: EdgeDB.Transaction.t, args: args): promise> => { 73 | transaction->EdgeDB.TransactionHelpers.singleRequired(queryText, ~args) 74 | } 75 | } 76 | 77 | module TestBool = { 78 | let queryText = `# @name testBool 79 | select '!' IN {'hello', 'world'};` 80 | 81 | type response = bool 82 | 83 | @live 84 | let query = (client: EdgeDB.Client.t): promise> => { 85 | client->EdgeDB.QueryHelpers.singleRequired(queryText) 86 | } 87 | 88 | @live 89 | let transaction = (transaction: EdgeDB.Transaction.t): promise> => { 90 | transaction->EdgeDB.TransactionHelpers.singleRequired(queryText) 91 | } 92 | } 93 | 94 | module TestString = { 95 | let queryText = `# @name testString 96 | select r'A raw \n string';` 97 | 98 | type response = string 99 | 100 | @live 101 | let query = (client: EdgeDB.Client.t): promise> => { 102 | client->EdgeDB.QueryHelpers.singleRequired(queryText) 103 | } 104 | 105 | @live 106 | let transaction = (transaction: EdgeDB.Transaction.t): promise> => { 107 | transaction->EdgeDB.TransactionHelpers.singleRequired(queryText) 108 | } 109 | } 110 | 111 | module AllMoviesNested = { 112 | let queryText = `# @name allMoviesNested 113 | select Movie { 114 | id, 115 | title, 116 | actors: { 117 | id, 118 | name, 119 | numberOfPets := count(.pets) 120 | } 121 | } order by .title` 122 | 123 | type response__actors = { 124 | id: string, 125 | name: string, 126 | numberOfPets: float, 127 | } 128 | 129 | type response = { 130 | id: string, 131 | title: string, 132 | actors: array, 133 | } 134 | 135 | @live 136 | let query = (client: EdgeDB.Client.t): promise> => { 137 | client->EdgeDB.QueryHelpers.many(queryText) 138 | } 139 | 140 | @live 141 | let transaction = (transaction: EdgeDB.Transaction.t): promise> => { 142 | transaction->EdgeDB.TransactionHelpers.many(queryText) 143 | } 144 | } 145 | 146 | module MovieByTitle = { 147 | let queryText = `# @name movieByTitle 148 | select Movie { 149 | id, 150 | title, 151 | actors: { 152 | id, 153 | name, 154 | numberOfPets := count(.pets), 155 | typesDump: { 156 | date, 157 | localDateTime, 158 | localDate, 159 | relativeDuration, 160 | duration, 161 | dateDuration, 162 | localTime, 163 | json 164 | } 165 | } 166 | } 167 | filter .title = $title 168 | limit 1` 169 | 170 | @live 171 | type args = { 172 | title: string, 173 | } 174 | 175 | type response__actors__typesDump = { 176 | date: Null.t, 177 | localDateTime: Null.t, 178 | localDate: Null.t, 179 | relativeDuration: Null.t, 180 | duration: Null.t, 181 | dateDuration: Null.t, 182 | localTime: Null.t, 183 | json: Null.t, 184 | } 185 | 186 | type response__actors = { 187 | id: string, 188 | name: string, 189 | numberOfPets: float, 190 | typesDump: Null.t, 191 | } 192 | 193 | type response = { 194 | id: string, 195 | title: string, 196 | actors: array, 197 | } 198 | 199 | @live 200 | let query = (client: EdgeDB.Client.t, args: args, ~onError=?): promise> => { 201 | client->EdgeDB.QueryHelpers.single(queryText, ~args, ~onError?) 202 | } 203 | 204 | @live 205 | let transaction = (transaction: EdgeDB.Transaction.t, args: args, ~onError=?): promise> => { 206 | transaction->EdgeDB.TransactionHelpers.single(queryText, ~args, ~onError?) 207 | } 208 | } 209 | 210 | module AddActor = { 211 | let queryText = `# @name AddActor 212 | insert Person { 213 | name := $name 214 | }` 215 | 216 | @live 217 | type args = { 218 | name: string, 219 | } 220 | 221 | type response = { 222 | id: string, 223 | } 224 | 225 | @live 226 | let query = (client: EdgeDB.Client.t, args: args): promise> => { 227 | client->EdgeDB.QueryHelpers.singleRequired(queryText, ~args) 228 | } 229 | 230 | @live 231 | let transaction = (transaction: EdgeDB.Transaction.t, args: args): promise> => { 232 | transaction->EdgeDB.TransactionHelpers.singleRequired(queryText, ~args) 233 | } 234 | } 235 | 236 | module RemoveActor = { 237 | let queryText = `# @name RemoveActor 238 | delete Person filter .id = $id` 239 | 240 | @live 241 | type args = { 242 | id: string, 243 | } 244 | 245 | type response = { 246 | id: string, 247 | } 248 | 249 | @live 250 | let query = (client: EdgeDB.Client.t, args: args, ~onError=?): promise> => { 251 | client->EdgeDB.QueryHelpers.single(queryText, ~args, ~onError?) 252 | } 253 | 254 | @live 255 | let transaction = (transaction: EdgeDB.Transaction.t, args: args, ~onError=?): promise> => { 256 | transaction->EdgeDB.TransactionHelpers.single(queryText, ~args, ~onError?) 257 | } 258 | } -------------------------------------------------------------------------------- /dbTestProject/test/TestFramework.res: -------------------------------------------------------------------------------- 1 | @module("bun:test") external test: (string, unit => unit) => unit = "test" 2 | @module("bun:test") external describe: (string, unit => unit) => unit = "describe" 3 | @module("bun:test") external testAsync: (string, unit => promise) => unit = "test" 4 | 5 | module Expect = { 6 | type t 7 | @send external toBe: (t, 'value) => unit = "toBe" 8 | @send external toEqual: (t, 'value) => unit = "toEqual" 9 | @send external toMatchSnapshot: t => unit = "toMatchSnapshot" 10 | } 11 | 12 | @module("bun:test") external expect: 'result => Expect.t = "expect" 13 | 14 | @module("bun:test") external afterAllAsync: (unit => promise) => unit = "afterAll" 15 | -------------------------------------------------------------------------------- /dbTestProject/test/TestProject.test.res: -------------------------------------------------------------------------------- 1 | open TestFramework 2 | 3 | let client = EdgeDB.Client.make() 4 | 5 | external spawnSync: array => 'a = "Bun.spawnSync" 6 | 7 | afterAllAsync(async () => { 8 | await client->EdgeDB.Client.close 9 | }) 10 | 11 | let removeIds = JSON.Replacer( 12 | (key, value) => { 13 | switch (key, value) { 14 | | ("id", Js.Json.String(_)) => Js.Json.String("") 15 | | _ => value 16 | } 17 | }, 18 | ) 19 | 20 | describe("fetching data", () => { 21 | testAsync("fetching movies", async () => { 22 | let movies = await client->Movies.allMovies 23 | let movies = movies->JSON.stringifyAny(~replacer=removeIds, ~space=2)->Option.getOr("") 24 | expect(movies)->Expect.toMatchSnapshot 25 | }) 26 | 27 | testAsync("counting movies", async () => { 28 | let count = await client->Movies.countAllMovies 29 | expect(count)->Expect.toMatchSnapshot 30 | }) 31 | 32 | testAsync("counting movies with param", async () => { 33 | let count = await client->Movies.countAllMoviesWithParam(~title="The Great Adventure") 34 | expect(count)->Expect.toMatchSnapshot 35 | }) 36 | 37 | testAsync("test bool", async () => { 38 | let bool = await client->Movies.testBool 39 | expect(bool)->Expect.toMatchSnapshot 40 | }) 41 | 42 | testAsync("test string", async () => { 43 | let string = await client->Movies.testString 44 | expect(string)->Expect.toMatchSnapshot 45 | }) 46 | 47 | testAsync("fetching single movie", async () => { 48 | let movie = await client->Movies.movieByTitle(~title="The Great Adventure") 49 | let movie = movie->JSON.stringifyAny(~replacer=removeIds, ~space=2)->Option.getOr("") 50 | expect(movie)->Expect.toMatchSnapshot 51 | }) 52 | 53 | testAsync("fetching non-existing movie", async () => { 54 | expect( 55 | await client->Movies.movieByTitle(~title="The Great Adventure 2"), 56 | )->Expect.toMatchSnapshot 57 | }) 58 | 59 | testAsync("running in a transaction", async () => { 60 | let res = await client->EdgeDB.Client.transaction( 61 | async transaction => { 62 | await transaction->Movies.addActor({name: "Bruce Willis"}) 63 | }, 64 | ) 65 | 66 | expect( 67 | switch res { 68 | | Ok({id}) => 69 | let removed = await client->EdgeDB.Client.transaction( 70 | async transaction => { 71 | await transaction->Movies.removeActor({id: id}) 72 | }, 73 | ) 74 | switch removed { 75 | | Some({id}) => 76 | // Just for the unused CLI output 77 | let _id = id 78 | | None => () 79 | } 80 | id->String.length > 2 81 | | Error(_) => false 82 | }, 83 | )->Expect.toBe(true) 84 | }) 85 | }) 86 | 87 | test("run unused selections CLI", () => { 88 | let res = spawnSync(["npx", "rescript-edgedb", "unused-selections"]) 89 | expect(res["stdout"]["toString"]())->Expect.toMatchSnapshot 90 | }) 91 | -------------------------------------------------------------------------------- /dbTestProject/test/__snapshots__/TestProject.test.mjs.snap: -------------------------------------------------------------------------------- 1 | // Bun Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`fetching data fetching movies 1`] = ` 4 | "[ 5 | { 6 | "id": "", 7 | "title": "The Great Adventure", 8 | "actors": [ 9 | { 10 | "id": "", 11 | "name": "John Doe", 12 | "numberOfPets": 2 13 | } 14 | ] 15 | }, 16 | { 17 | "id": "", 18 | "title": "The Mystery", 19 | "actors": [ 20 | { 21 | "id": "", 22 | "name": "Jane Smith", 23 | "numberOfPets": 1 24 | } 25 | ] 26 | }, 27 | { 28 | "id": "", 29 | "title": "The Thriller", 30 | "actors": [ 31 | { 32 | "id": "", 33 | "name": "John Doe", 34 | "numberOfPets": 2 35 | }, 36 | { 37 | "id": "", 38 | "name": "Jane Smith", 39 | "numberOfPets": 1 40 | }, 41 | { 42 | "id": "", 43 | "name": "Bob Johnson", 44 | "numberOfPets": 1 45 | } 46 | ] 47 | } 48 | ]" 49 | `; 50 | 51 | exports[`fetching data fetching single movie 1`] = ` 52 | "{ 53 | "id": "", 54 | "title": "The Great Adventure", 55 | "actors": [ 56 | { 57 | "id": "", 58 | "name": "John Doe", 59 | "numberOfPets": 2, 60 | "typesDump": null 61 | } 62 | ] 63 | }" 64 | `; 65 | 66 | exports[`fetching data fetching non-existing movie 1`] = `undefined`; 67 | 68 | exports[`run unused selections CLI 1`] = ` 69 | "Analyzing project... (this might take a while) 70 | 71 | ✘ Found 4 unused selections. 72 | 73 | File "Movies.res": 74 | In query "AllMovies": 75 | - actors.numberOfPets 76 | - .title 77 | In query "MovieByTitle": 78 | - actors.name 79 | - .id 80 | " 81 | `; 82 | 83 | exports[`fetching data counting movies 1`] = ` 84 | { 85 | "TAG": "Ok", 86 | "_0": 3, 87 | } 88 | `; 89 | 90 | exports[`fetching data counting movies with param 1`] = ` 91 | { 92 | "TAG": "Ok", 93 | "_0": 1, 94 | } 95 | `; 96 | 97 | exports[`fetching data running in a transaction 1`] = `undefined`; 98 | 99 | exports[`fetching data test bool 1`] = ` 100 | { 101 | "TAG": "Ok", 102 | "_0": false, 103 | } 104 | `; 105 | 106 | exports[`fetching data test string 1`] = ` 107 | { 108 | "TAG": "Ok", 109 | "_0": 110 | "A raw 111 | string" 112 | , 113 | } 114 | `; 115 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rescript-edgedb", 3 | "version": "0.7.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "rescript-edgedb", 9 | "version": "0.7.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "chokidar": "^3.5.3", 13 | "fast-glob": "^3.3.1" 14 | }, 15 | "bin": { 16 | "rescript-edgedb": "dist/Cli.js" 17 | }, 18 | "devDependencies": { 19 | "@rescript/core": "1.3.0", 20 | "esbuild": "^0.19.3", 21 | "rescript": "^11.1.0", 22 | "rescript-embed-lang": "0.4.0", 23 | "rescript-nodejs": "^16.1.0" 24 | }, 25 | "peerDependencies": { 26 | "@rescript/core": ">= 1.3.0", 27 | "edgedb": ">= 1.3.6", 28 | "rescript": "^11.1.0", 29 | "rescript-embed-lang": ">= 0.4.0" 30 | } 31 | }, 32 | "node_modules/@esbuild/android-arm": { 33 | "version": "0.19.3", 34 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", 35 | "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", 36 | "cpu": [ 37 | "arm" 38 | ], 39 | "dev": true, 40 | "optional": true, 41 | "os": [ 42 | "android" 43 | ], 44 | "engines": { 45 | "node": ">=12" 46 | } 47 | }, 48 | "node_modules/@esbuild/android-arm64": { 49 | "version": "0.19.3", 50 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", 51 | "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", 52 | "cpu": [ 53 | "arm64" 54 | ], 55 | "dev": true, 56 | "optional": true, 57 | "os": [ 58 | "android" 59 | ], 60 | "engines": { 61 | "node": ">=12" 62 | } 63 | }, 64 | "node_modules/@esbuild/android-x64": { 65 | "version": "0.19.3", 66 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", 67 | "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", 68 | "cpu": [ 69 | "x64" 70 | ], 71 | "dev": true, 72 | "optional": true, 73 | "os": [ 74 | "android" 75 | ], 76 | "engines": { 77 | "node": ">=12" 78 | } 79 | }, 80 | "node_modules/@esbuild/darwin-arm64": { 81 | "version": "0.19.3", 82 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", 83 | "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", 84 | "cpu": [ 85 | "arm64" 86 | ], 87 | "dev": true, 88 | "optional": true, 89 | "os": [ 90 | "darwin" 91 | ], 92 | "engines": { 93 | "node": ">=12" 94 | } 95 | }, 96 | "node_modules/@esbuild/darwin-x64": { 97 | "version": "0.19.3", 98 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", 99 | "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", 100 | "cpu": [ 101 | "x64" 102 | ], 103 | "dev": true, 104 | "optional": true, 105 | "os": [ 106 | "darwin" 107 | ], 108 | "engines": { 109 | "node": ">=12" 110 | } 111 | }, 112 | "node_modules/@esbuild/freebsd-arm64": { 113 | "version": "0.19.3", 114 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", 115 | "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", 116 | "cpu": [ 117 | "arm64" 118 | ], 119 | "dev": true, 120 | "optional": true, 121 | "os": [ 122 | "freebsd" 123 | ], 124 | "engines": { 125 | "node": ">=12" 126 | } 127 | }, 128 | "node_modules/@esbuild/freebsd-x64": { 129 | "version": "0.19.3", 130 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", 131 | "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", 132 | "cpu": [ 133 | "x64" 134 | ], 135 | "dev": true, 136 | "optional": true, 137 | "os": [ 138 | "freebsd" 139 | ], 140 | "engines": { 141 | "node": ">=12" 142 | } 143 | }, 144 | "node_modules/@esbuild/linux-arm": { 145 | "version": "0.19.3", 146 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", 147 | "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", 148 | "cpu": [ 149 | "arm" 150 | ], 151 | "dev": true, 152 | "optional": true, 153 | "os": [ 154 | "linux" 155 | ], 156 | "engines": { 157 | "node": ">=12" 158 | } 159 | }, 160 | "node_modules/@esbuild/linux-arm64": { 161 | "version": "0.19.3", 162 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", 163 | "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", 164 | "cpu": [ 165 | "arm64" 166 | ], 167 | "dev": true, 168 | "optional": true, 169 | "os": [ 170 | "linux" 171 | ], 172 | "engines": { 173 | "node": ">=12" 174 | } 175 | }, 176 | "node_modules/@esbuild/linux-ia32": { 177 | "version": "0.19.3", 178 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", 179 | "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", 180 | "cpu": [ 181 | "ia32" 182 | ], 183 | "dev": true, 184 | "optional": true, 185 | "os": [ 186 | "linux" 187 | ], 188 | "engines": { 189 | "node": ">=12" 190 | } 191 | }, 192 | "node_modules/@esbuild/linux-loong64": { 193 | "version": "0.19.3", 194 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", 195 | "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", 196 | "cpu": [ 197 | "loong64" 198 | ], 199 | "dev": true, 200 | "optional": true, 201 | "os": [ 202 | "linux" 203 | ], 204 | "engines": { 205 | "node": ">=12" 206 | } 207 | }, 208 | "node_modules/@esbuild/linux-mips64el": { 209 | "version": "0.19.3", 210 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", 211 | "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", 212 | "cpu": [ 213 | "mips64el" 214 | ], 215 | "dev": true, 216 | "optional": true, 217 | "os": [ 218 | "linux" 219 | ], 220 | "engines": { 221 | "node": ">=12" 222 | } 223 | }, 224 | "node_modules/@esbuild/linux-ppc64": { 225 | "version": "0.19.3", 226 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", 227 | "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", 228 | "cpu": [ 229 | "ppc64" 230 | ], 231 | "dev": true, 232 | "optional": true, 233 | "os": [ 234 | "linux" 235 | ], 236 | "engines": { 237 | "node": ">=12" 238 | } 239 | }, 240 | "node_modules/@esbuild/linux-riscv64": { 241 | "version": "0.19.3", 242 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", 243 | "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", 244 | "cpu": [ 245 | "riscv64" 246 | ], 247 | "dev": true, 248 | "optional": true, 249 | "os": [ 250 | "linux" 251 | ], 252 | "engines": { 253 | "node": ">=12" 254 | } 255 | }, 256 | "node_modules/@esbuild/linux-s390x": { 257 | "version": "0.19.3", 258 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", 259 | "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", 260 | "cpu": [ 261 | "s390x" 262 | ], 263 | "dev": true, 264 | "optional": true, 265 | "os": [ 266 | "linux" 267 | ], 268 | "engines": { 269 | "node": ">=12" 270 | } 271 | }, 272 | "node_modules/@esbuild/linux-x64": { 273 | "version": "0.19.3", 274 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", 275 | "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", 276 | "cpu": [ 277 | "x64" 278 | ], 279 | "dev": true, 280 | "optional": true, 281 | "os": [ 282 | "linux" 283 | ], 284 | "engines": { 285 | "node": ">=12" 286 | } 287 | }, 288 | "node_modules/@esbuild/netbsd-x64": { 289 | "version": "0.19.3", 290 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", 291 | "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", 292 | "cpu": [ 293 | "x64" 294 | ], 295 | "dev": true, 296 | "optional": true, 297 | "os": [ 298 | "netbsd" 299 | ], 300 | "engines": { 301 | "node": ">=12" 302 | } 303 | }, 304 | "node_modules/@esbuild/openbsd-x64": { 305 | "version": "0.19.3", 306 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", 307 | "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", 308 | "cpu": [ 309 | "x64" 310 | ], 311 | "dev": true, 312 | "optional": true, 313 | "os": [ 314 | "openbsd" 315 | ], 316 | "engines": { 317 | "node": ">=12" 318 | } 319 | }, 320 | "node_modules/@esbuild/sunos-x64": { 321 | "version": "0.19.3", 322 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", 323 | "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", 324 | "cpu": [ 325 | "x64" 326 | ], 327 | "dev": true, 328 | "optional": true, 329 | "os": [ 330 | "sunos" 331 | ], 332 | "engines": { 333 | "node": ">=12" 334 | } 335 | }, 336 | "node_modules/@esbuild/win32-arm64": { 337 | "version": "0.19.3", 338 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", 339 | "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", 340 | "cpu": [ 341 | "arm64" 342 | ], 343 | "dev": true, 344 | "optional": true, 345 | "os": [ 346 | "win32" 347 | ], 348 | "engines": { 349 | "node": ">=12" 350 | } 351 | }, 352 | "node_modules/@esbuild/win32-ia32": { 353 | "version": "0.19.3", 354 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", 355 | "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", 356 | "cpu": [ 357 | "ia32" 358 | ], 359 | "dev": true, 360 | "optional": true, 361 | "os": [ 362 | "win32" 363 | ], 364 | "engines": { 365 | "node": ">=12" 366 | } 367 | }, 368 | "node_modules/@esbuild/win32-x64": { 369 | "version": "0.19.3", 370 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", 371 | "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", 372 | "cpu": [ 373 | "x64" 374 | ], 375 | "dev": true, 376 | "optional": true, 377 | "os": [ 378 | "win32" 379 | ], 380 | "engines": { 381 | "node": ">=12" 382 | } 383 | }, 384 | "node_modules/@nodelib/fs.scandir": { 385 | "version": "2.1.5", 386 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 387 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 388 | "dependencies": { 389 | "@nodelib/fs.stat": "2.0.5", 390 | "run-parallel": "^1.1.9" 391 | }, 392 | "engines": { 393 | "node": ">= 8" 394 | } 395 | }, 396 | "node_modules/@nodelib/fs.stat": { 397 | "version": "2.0.5", 398 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 399 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 400 | "engines": { 401 | "node": ">= 8" 402 | } 403 | }, 404 | "node_modules/@nodelib/fs.walk": { 405 | "version": "1.2.8", 406 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 407 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 408 | "dependencies": { 409 | "@nodelib/fs.scandir": "2.1.5", 410 | "fastq": "^1.6.0" 411 | }, 412 | "engines": { 413 | "node": ">= 8" 414 | } 415 | }, 416 | "node_modules/@rescript/core": { 417 | "version": "1.3.0", 418 | "resolved": "https://registry.npmjs.org/@rescript/core/-/core-1.3.0.tgz", 419 | "integrity": "sha512-wNZOZ63sYcaIYZCmTZeIPCeLa3HCGgPbIOR8zjyNkoBYUlxNV8Nb2ZyqlXR5Mb9ttvv8fTV56JbKhyVEZEYo8g==", 420 | "dev": true, 421 | "peerDependencies": { 422 | "rescript": "^11.1.0-rc.7" 423 | } 424 | }, 425 | "node_modules/anymatch": { 426 | "version": "3.1.3", 427 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 428 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 429 | "dependencies": { 430 | "normalize-path": "^3.0.0", 431 | "picomatch": "^2.0.4" 432 | }, 433 | "engines": { 434 | "node": ">= 8" 435 | } 436 | }, 437 | "node_modules/binary-extensions": { 438 | "version": "2.2.0", 439 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 440 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 441 | "engines": { 442 | "node": ">=8" 443 | } 444 | }, 445 | "node_modules/braces": { 446 | "version": "3.0.2", 447 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 448 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 449 | "dependencies": { 450 | "fill-range": "^7.0.1" 451 | }, 452 | "engines": { 453 | "node": ">=8" 454 | } 455 | }, 456 | "node_modules/chokidar": { 457 | "version": "3.5.3", 458 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 459 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 460 | "funding": [ 461 | { 462 | "type": "individual", 463 | "url": "https://paulmillr.com/funding/" 464 | } 465 | ], 466 | "dependencies": { 467 | "anymatch": "~3.1.2", 468 | "braces": "~3.0.2", 469 | "glob-parent": "~5.1.2", 470 | "is-binary-path": "~2.1.0", 471 | "is-glob": "~4.0.1", 472 | "normalize-path": "~3.0.0", 473 | "readdirp": "~3.6.0" 474 | }, 475 | "engines": { 476 | "node": ">= 8.10.0" 477 | }, 478 | "optionalDependencies": { 479 | "fsevents": "~2.3.2" 480 | } 481 | }, 482 | "node_modules/edgedb": { 483 | "version": "1.3.6", 484 | "resolved": "https://registry.npmjs.org/edgedb/-/edgedb-1.3.6.tgz", 485 | "integrity": "sha512-0eIBj/ClNYHkcxPyCtwGIZ4fC0RIWkI6CEntk8mPSVhcw6+32+o5pTCeb/tRwo9c56qVqJpiYR7Mqu+nZondNA==", 486 | "peer": true, 487 | "bin": { 488 | "edgeql-js": "dist/cli.js" 489 | }, 490 | "engines": { 491 | "node": ">= 12.0.0" 492 | } 493 | }, 494 | "node_modules/esbuild": { 495 | "version": "0.19.3", 496 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", 497 | "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", 498 | "dev": true, 499 | "hasInstallScript": true, 500 | "bin": { 501 | "esbuild": "bin/esbuild" 502 | }, 503 | "engines": { 504 | "node": ">=12" 505 | }, 506 | "optionalDependencies": { 507 | "@esbuild/android-arm": "0.19.3", 508 | "@esbuild/android-arm64": "0.19.3", 509 | "@esbuild/android-x64": "0.19.3", 510 | "@esbuild/darwin-arm64": "0.19.3", 511 | "@esbuild/darwin-x64": "0.19.3", 512 | "@esbuild/freebsd-arm64": "0.19.3", 513 | "@esbuild/freebsd-x64": "0.19.3", 514 | "@esbuild/linux-arm": "0.19.3", 515 | "@esbuild/linux-arm64": "0.19.3", 516 | "@esbuild/linux-ia32": "0.19.3", 517 | "@esbuild/linux-loong64": "0.19.3", 518 | "@esbuild/linux-mips64el": "0.19.3", 519 | "@esbuild/linux-ppc64": "0.19.3", 520 | "@esbuild/linux-riscv64": "0.19.3", 521 | "@esbuild/linux-s390x": "0.19.3", 522 | "@esbuild/linux-x64": "0.19.3", 523 | "@esbuild/netbsd-x64": "0.19.3", 524 | "@esbuild/openbsd-x64": "0.19.3", 525 | "@esbuild/sunos-x64": "0.19.3", 526 | "@esbuild/win32-arm64": "0.19.3", 527 | "@esbuild/win32-ia32": "0.19.3", 528 | "@esbuild/win32-x64": "0.19.3" 529 | } 530 | }, 531 | "node_modules/fast-glob": { 532 | "version": "3.3.1", 533 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", 534 | "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", 535 | "dependencies": { 536 | "@nodelib/fs.stat": "^2.0.2", 537 | "@nodelib/fs.walk": "^1.2.3", 538 | "glob-parent": "^5.1.2", 539 | "merge2": "^1.3.0", 540 | "micromatch": "^4.0.4" 541 | }, 542 | "engines": { 543 | "node": ">=8.6.0" 544 | } 545 | }, 546 | "node_modules/fastq": { 547 | "version": "1.15.0", 548 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", 549 | "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", 550 | "dependencies": { 551 | "reusify": "^1.0.4" 552 | } 553 | }, 554 | "node_modules/fill-range": { 555 | "version": "7.0.1", 556 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 557 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 558 | "dependencies": { 559 | "to-regex-range": "^5.0.1" 560 | }, 561 | "engines": { 562 | "node": ">=8" 563 | } 564 | }, 565 | "node_modules/fsevents": { 566 | "version": "2.3.3", 567 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 568 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 569 | "hasInstallScript": true, 570 | "optional": true, 571 | "os": [ 572 | "darwin" 573 | ], 574 | "engines": { 575 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 576 | } 577 | }, 578 | "node_modules/glob-parent": { 579 | "version": "5.1.2", 580 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 581 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 582 | "dependencies": { 583 | "is-glob": "^4.0.1" 584 | }, 585 | "engines": { 586 | "node": ">= 6" 587 | } 588 | }, 589 | "node_modules/is-binary-path": { 590 | "version": "2.1.0", 591 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 592 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 593 | "dependencies": { 594 | "binary-extensions": "^2.0.0" 595 | }, 596 | "engines": { 597 | "node": ">=8" 598 | } 599 | }, 600 | "node_modules/is-extglob": { 601 | "version": "2.1.1", 602 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 603 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 604 | "engines": { 605 | "node": ">=0.10.0" 606 | } 607 | }, 608 | "node_modules/is-glob": { 609 | "version": "4.0.3", 610 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 611 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 612 | "dependencies": { 613 | "is-extglob": "^2.1.1" 614 | }, 615 | "engines": { 616 | "node": ">=0.10.0" 617 | } 618 | }, 619 | "node_modules/is-number": { 620 | "version": "7.0.0", 621 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 622 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 623 | "engines": { 624 | "node": ">=0.12.0" 625 | } 626 | }, 627 | "node_modules/merge2": { 628 | "version": "1.4.1", 629 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 630 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 631 | "engines": { 632 | "node": ">= 8" 633 | } 634 | }, 635 | "node_modules/micromatch": { 636 | "version": "4.0.5", 637 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 638 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 639 | "dependencies": { 640 | "braces": "^3.0.2", 641 | "picomatch": "^2.3.1" 642 | }, 643 | "engines": { 644 | "node": ">=8.6" 645 | } 646 | }, 647 | "node_modules/normalize-path": { 648 | "version": "3.0.0", 649 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 650 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 651 | "engines": { 652 | "node": ">=0.10.0" 653 | } 654 | }, 655 | "node_modules/picomatch": { 656 | "version": "2.3.1", 657 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 658 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 659 | "engines": { 660 | "node": ">=8.6" 661 | }, 662 | "funding": { 663 | "url": "https://github.com/sponsors/jonschlinkert" 664 | } 665 | }, 666 | "node_modules/queue-microtask": { 667 | "version": "1.2.3", 668 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 669 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 670 | "funding": [ 671 | { 672 | "type": "github", 673 | "url": "https://github.com/sponsors/feross" 674 | }, 675 | { 676 | "type": "patreon", 677 | "url": "https://www.patreon.com/feross" 678 | }, 679 | { 680 | "type": "consulting", 681 | "url": "https://feross.org/support" 682 | } 683 | ] 684 | }, 685 | "node_modules/readdirp": { 686 | "version": "3.6.0", 687 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 688 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 689 | "dependencies": { 690 | "picomatch": "^2.2.1" 691 | }, 692 | "engines": { 693 | "node": ">=8.10.0" 694 | } 695 | }, 696 | "node_modules/rescript": { 697 | "version": "11.1.0", 698 | "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.0.tgz", 699 | "integrity": "sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==", 700 | "dev": true, 701 | "hasInstallScript": true, 702 | "bin": { 703 | "bsc": "bsc", 704 | "bstracing": "lib/bstracing", 705 | "rescript": "rescript" 706 | }, 707 | "engines": { 708 | "node": ">=10" 709 | } 710 | }, 711 | "node_modules/rescript-embed-lang": { 712 | "version": "0.4.0", 713 | "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.4.0.tgz", 714 | "integrity": "sha512-TEUfLW5zwOXpx3qvmzSqOinWjBbI0izTvGRllZ39hX/WeSZCZgf4ka7XDQ/chpRFHLMBVDJqJET7beyepF8Kow==", 715 | "dev": true, 716 | "hasInstallScript": true 717 | }, 718 | "node_modules/rescript-nodejs": { 719 | "version": "16.1.0", 720 | "resolved": "https://registry.npmjs.org/rescript-nodejs/-/rescript-nodejs-16.1.0.tgz", 721 | "integrity": "sha512-RyXGIEsb8UhuShf5PwKcTkYNPz+cPQ0CZq74lbYCbCa5YFidbmiIWpQhCMtpsgP1PkLClhKGDkfZfmwwNOil4Q==", 722 | "dev": true 723 | }, 724 | "node_modules/reusify": { 725 | "version": "1.0.4", 726 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 727 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 728 | "engines": { 729 | "iojs": ">=1.0.0", 730 | "node": ">=0.10.0" 731 | } 732 | }, 733 | "node_modules/run-parallel": { 734 | "version": "1.2.0", 735 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 736 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 737 | "funding": [ 738 | { 739 | "type": "github", 740 | "url": "https://github.com/sponsors/feross" 741 | }, 742 | { 743 | "type": "patreon", 744 | "url": "https://www.patreon.com/feross" 745 | }, 746 | { 747 | "type": "consulting", 748 | "url": "https://feross.org/support" 749 | } 750 | ], 751 | "dependencies": { 752 | "queue-microtask": "^1.2.2" 753 | } 754 | }, 755 | "node_modules/to-regex-range": { 756 | "version": "5.0.1", 757 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 758 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 759 | "dependencies": { 760 | "is-number": "^7.0.0" 761 | }, 762 | "engines": { 763 | "node": ">=8.0" 764 | } 765 | } 766 | }, 767 | "dependencies": { 768 | "@esbuild/android-arm": { 769 | "version": "0.19.3", 770 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.3.tgz", 771 | "integrity": "sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==", 772 | "dev": true, 773 | "optional": true 774 | }, 775 | "@esbuild/android-arm64": { 776 | "version": "0.19.3", 777 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz", 778 | "integrity": "sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==", 779 | "dev": true, 780 | "optional": true 781 | }, 782 | "@esbuild/android-x64": { 783 | "version": "0.19.3", 784 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.3.tgz", 785 | "integrity": "sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==", 786 | "dev": true, 787 | "optional": true 788 | }, 789 | "@esbuild/darwin-arm64": { 790 | "version": "0.19.3", 791 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz", 792 | "integrity": "sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==", 793 | "dev": true, 794 | "optional": true 795 | }, 796 | "@esbuild/darwin-x64": { 797 | "version": "0.19.3", 798 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz", 799 | "integrity": "sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==", 800 | "dev": true, 801 | "optional": true 802 | }, 803 | "@esbuild/freebsd-arm64": { 804 | "version": "0.19.3", 805 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz", 806 | "integrity": "sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==", 807 | "dev": true, 808 | "optional": true 809 | }, 810 | "@esbuild/freebsd-x64": { 811 | "version": "0.19.3", 812 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz", 813 | "integrity": "sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==", 814 | "dev": true, 815 | "optional": true 816 | }, 817 | "@esbuild/linux-arm": { 818 | "version": "0.19.3", 819 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz", 820 | "integrity": "sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==", 821 | "dev": true, 822 | "optional": true 823 | }, 824 | "@esbuild/linux-arm64": { 825 | "version": "0.19.3", 826 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz", 827 | "integrity": "sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==", 828 | "dev": true, 829 | "optional": true 830 | }, 831 | "@esbuild/linux-ia32": { 832 | "version": "0.19.3", 833 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz", 834 | "integrity": "sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==", 835 | "dev": true, 836 | "optional": true 837 | }, 838 | "@esbuild/linux-loong64": { 839 | "version": "0.19.3", 840 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz", 841 | "integrity": "sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==", 842 | "dev": true, 843 | "optional": true 844 | }, 845 | "@esbuild/linux-mips64el": { 846 | "version": "0.19.3", 847 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz", 848 | "integrity": "sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==", 849 | "dev": true, 850 | "optional": true 851 | }, 852 | "@esbuild/linux-ppc64": { 853 | "version": "0.19.3", 854 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz", 855 | "integrity": "sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==", 856 | "dev": true, 857 | "optional": true 858 | }, 859 | "@esbuild/linux-riscv64": { 860 | "version": "0.19.3", 861 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz", 862 | "integrity": "sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==", 863 | "dev": true, 864 | "optional": true 865 | }, 866 | "@esbuild/linux-s390x": { 867 | "version": "0.19.3", 868 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz", 869 | "integrity": "sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==", 870 | "dev": true, 871 | "optional": true 872 | }, 873 | "@esbuild/linux-x64": { 874 | "version": "0.19.3", 875 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz", 876 | "integrity": "sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==", 877 | "dev": true, 878 | "optional": true 879 | }, 880 | "@esbuild/netbsd-x64": { 881 | "version": "0.19.3", 882 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz", 883 | "integrity": "sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==", 884 | "dev": true, 885 | "optional": true 886 | }, 887 | "@esbuild/openbsd-x64": { 888 | "version": "0.19.3", 889 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz", 890 | "integrity": "sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==", 891 | "dev": true, 892 | "optional": true 893 | }, 894 | "@esbuild/sunos-x64": { 895 | "version": "0.19.3", 896 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz", 897 | "integrity": "sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==", 898 | "dev": true, 899 | "optional": true 900 | }, 901 | "@esbuild/win32-arm64": { 902 | "version": "0.19.3", 903 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz", 904 | "integrity": "sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==", 905 | "dev": true, 906 | "optional": true 907 | }, 908 | "@esbuild/win32-ia32": { 909 | "version": "0.19.3", 910 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz", 911 | "integrity": "sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==", 912 | "dev": true, 913 | "optional": true 914 | }, 915 | "@esbuild/win32-x64": { 916 | "version": "0.19.3", 917 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz", 918 | "integrity": "sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==", 919 | "dev": true, 920 | "optional": true 921 | }, 922 | "@nodelib/fs.scandir": { 923 | "version": "2.1.5", 924 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 925 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 926 | "requires": { 927 | "@nodelib/fs.stat": "2.0.5", 928 | "run-parallel": "^1.1.9" 929 | } 930 | }, 931 | "@nodelib/fs.stat": { 932 | "version": "2.0.5", 933 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 934 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" 935 | }, 936 | "@nodelib/fs.walk": { 937 | "version": "1.2.8", 938 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 939 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 940 | "requires": { 941 | "@nodelib/fs.scandir": "2.1.5", 942 | "fastq": "^1.6.0" 943 | } 944 | }, 945 | "@rescript/core": { 946 | "version": "1.3.0", 947 | "resolved": "https://registry.npmjs.org/@rescript/core/-/core-1.3.0.tgz", 948 | "integrity": "sha512-wNZOZ63sYcaIYZCmTZeIPCeLa3HCGgPbIOR8zjyNkoBYUlxNV8Nb2ZyqlXR5Mb9ttvv8fTV56JbKhyVEZEYo8g==", 949 | "dev": true, 950 | "requires": {} 951 | }, 952 | "anymatch": { 953 | "version": "3.1.3", 954 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 955 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 956 | "requires": { 957 | "normalize-path": "^3.0.0", 958 | "picomatch": "^2.0.4" 959 | } 960 | }, 961 | "binary-extensions": { 962 | "version": "2.2.0", 963 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 964 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 965 | }, 966 | "braces": { 967 | "version": "3.0.2", 968 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 969 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 970 | "requires": { 971 | "fill-range": "^7.0.1" 972 | } 973 | }, 974 | "chokidar": { 975 | "version": "3.5.3", 976 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 977 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 978 | "requires": { 979 | "anymatch": "~3.1.2", 980 | "braces": "~3.0.2", 981 | "fsevents": "~2.3.2", 982 | "glob-parent": "~5.1.2", 983 | "is-binary-path": "~2.1.0", 984 | "is-glob": "~4.0.1", 985 | "normalize-path": "~3.0.0", 986 | "readdirp": "~3.6.0" 987 | } 988 | }, 989 | "edgedb": { 990 | "version": "1.3.6", 991 | "resolved": "https://registry.npmjs.org/edgedb/-/edgedb-1.3.6.tgz", 992 | "integrity": "sha512-0eIBj/ClNYHkcxPyCtwGIZ4fC0RIWkI6CEntk8mPSVhcw6+32+o5pTCeb/tRwo9c56qVqJpiYR7Mqu+nZondNA==", 993 | "peer": true 994 | }, 995 | "esbuild": { 996 | "version": "0.19.3", 997 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.3.tgz", 998 | "integrity": "sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==", 999 | "dev": true, 1000 | "requires": { 1001 | "@esbuild/android-arm": "0.19.3", 1002 | "@esbuild/android-arm64": "0.19.3", 1003 | "@esbuild/android-x64": "0.19.3", 1004 | "@esbuild/darwin-arm64": "0.19.3", 1005 | "@esbuild/darwin-x64": "0.19.3", 1006 | "@esbuild/freebsd-arm64": "0.19.3", 1007 | "@esbuild/freebsd-x64": "0.19.3", 1008 | "@esbuild/linux-arm": "0.19.3", 1009 | "@esbuild/linux-arm64": "0.19.3", 1010 | "@esbuild/linux-ia32": "0.19.3", 1011 | "@esbuild/linux-loong64": "0.19.3", 1012 | "@esbuild/linux-mips64el": "0.19.3", 1013 | "@esbuild/linux-ppc64": "0.19.3", 1014 | "@esbuild/linux-riscv64": "0.19.3", 1015 | "@esbuild/linux-s390x": "0.19.3", 1016 | "@esbuild/linux-x64": "0.19.3", 1017 | "@esbuild/netbsd-x64": "0.19.3", 1018 | "@esbuild/openbsd-x64": "0.19.3", 1019 | "@esbuild/sunos-x64": "0.19.3", 1020 | "@esbuild/win32-arm64": "0.19.3", 1021 | "@esbuild/win32-ia32": "0.19.3", 1022 | "@esbuild/win32-x64": "0.19.3" 1023 | } 1024 | }, 1025 | "fast-glob": { 1026 | "version": "3.3.1", 1027 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", 1028 | "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", 1029 | "requires": { 1030 | "@nodelib/fs.stat": "^2.0.2", 1031 | "@nodelib/fs.walk": "^1.2.3", 1032 | "glob-parent": "^5.1.2", 1033 | "merge2": "^1.3.0", 1034 | "micromatch": "^4.0.4" 1035 | } 1036 | }, 1037 | "fastq": { 1038 | "version": "1.15.0", 1039 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", 1040 | "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", 1041 | "requires": { 1042 | "reusify": "^1.0.4" 1043 | } 1044 | }, 1045 | "fill-range": { 1046 | "version": "7.0.1", 1047 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1048 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1049 | "requires": { 1050 | "to-regex-range": "^5.0.1" 1051 | } 1052 | }, 1053 | "fsevents": { 1054 | "version": "2.3.3", 1055 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1056 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1057 | "optional": true 1058 | }, 1059 | "glob-parent": { 1060 | "version": "5.1.2", 1061 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1062 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1063 | "requires": { 1064 | "is-glob": "^4.0.1" 1065 | } 1066 | }, 1067 | "is-binary-path": { 1068 | "version": "2.1.0", 1069 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1070 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1071 | "requires": { 1072 | "binary-extensions": "^2.0.0" 1073 | } 1074 | }, 1075 | "is-extglob": { 1076 | "version": "2.1.1", 1077 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1078 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" 1079 | }, 1080 | "is-glob": { 1081 | "version": "4.0.3", 1082 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1083 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1084 | "requires": { 1085 | "is-extglob": "^2.1.1" 1086 | } 1087 | }, 1088 | "is-number": { 1089 | "version": "7.0.0", 1090 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1091 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 1092 | }, 1093 | "merge2": { 1094 | "version": "1.4.1", 1095 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1096 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" 1097 | }, 1098 | "micromatch": { 1099 | "version": "4.0.5", 1100 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 1101 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 1102 | "requires": { 1103 | "braces": "^3.0.2", 1104 | "picomatch": "^2.3.1" 1105 | } 1106 | }, 1107 | "normalize-path": { 1108 | "version": "3.0.0", 1109 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1110 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 1111 | }, 1112 | "picomatch": { 1113 | "version": "2.3.1", 1114 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1115 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" 1116 | }, 1117 | "queue-microtask": { 1118 | "version": "1.2.3", 1119 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1120 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" 1121 | }, 1122 | "readdirp": { 1123 | "version": "3.6.0", 1124 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1125 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1126 | "requires": { 1127 | "picomatch": "^2.2.1" 1128 | } 1129 | }, 1130 | "rescript": { 1131 | "version": "11.1.0", 1132 | "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.1.0.tgz", 1133 | "integrity": "sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==", 1134 | "dev": true 1135 | }, 1136 | "rescript-embed-lang": { 1137 | "version": "0.4.0", 1138 | "resolved": "https://registry.npmjs.org/rescript-embed-lang/-/rescript-embed-lang-0.4.0.tgz", 1139 | "integrity": "sha512-TEUfLW5zwOXpx3qvmzSqOinWjBbI0izTvGRllZ39hX/WeSZCZgf4ka7XDQ/chpRFHLMBVDJqJET7beyepF8Kow==", 1140 | "dev": true 1141 | }, 1142 | "rescript-nodejs": { 1143 | "version": "16.1.0", 1144 | "resolved": "https://registry.npmjs.org/rescript-nodejs/-/rescript-nodejs-16.1.0.tgz", 1145 | "integrity": "sha512-RyXGIEsb8UhuShf5PwKcTkYNPz+cPQ0CZq74lbYCbCa5YFidbmiIWpQhCMtpsgP1PkLClhKGDkfZfmwwNOil4Q==", 1146 | "dev": true 1147 | }, 1148 | "reusify": { 1149 | "version": "1.0.4", 1150 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1151 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" 1152 | }, 1153 | "run-parallel": { 1154 | "version": "1.2.0", 1155 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1156 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1157 | "requires": { 1158 | "queue-microtask": "^1.2.2" 1159 | } 1160 | }, 1161 | "to-regex-range": { 1162 | "version": "5.0.1", 1163 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1164 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1165 | "requires": { 1166 | "is-number": "^7.0.0" 1167 | } 1168 | } 1169 | } 1170 | } 1171 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rescript-edgedb", 3 | "version": "0.7.0", 4 | "description": "Use EdgeDB in ReScript.", 5 | "main": "src/EdgeDB.mjs", 6 | "bin": "dist/Cli.js", 7 | "scripts": { 8 | "test": "bun test test/*.test.mjs", 9 | "build:res": "rescript", 10 | "build": "npm run build:res && esbuild --external:edgedb --external:chokidar --external:fast-glob --platform=node --bundle cli/Cli.mjs --outfile=dist/Cli.js --minify --tree-shaking", 11 | "prepublish": "npm run build" 12 | }, 13 | "sideEffects": false, 14 | "keywords": [ 15 | "rescript", 16 | "edgedb" 17 | ], 18 | "files": [ 19 | "README.md", 20 | "CHANGELOG.md", 21 | "rescript.json", 22 | "dist", 23 | "src" 24 | ], 25 | "repository": "https://github.com/zth/rescript-edgedb", 26 | "author": "Gabriel Nordeborn", 27 | "license": "MIT", 28 | "dependencies": { 29 | "chokidar": "^3.5.3", 30 | "fast-glob": "^3.3.1" 31 | }, 32 | "devDependencies": { 33 | "@rescript/core": "1.3.0", 34 | "esbuild": "^0.19.3", 35 | "rescript": "^11.1.0", 36 | "rescript-embed-lang": "0.4.0", 37 | "rescript-nodejs": "^16.1.0" 38 | }, 39 | "peerDependencies": { 40 | "@rescript/core": ">= 1.3.0", 41 | "edgedb": ">= 1.3.6", 42 | "rescript": "^11.1.0", 43 | "rescript-embed-lang": ">= 0.4.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rescript-edgedb", 3 | "uncurried": true, 4 | "sources": [ 5 | { 6 | "dir": "src" 7 | }, 8 | { 9 | "dir": "cli", 10 | "type": "dev" 11 | }, 12 | { 13 | "dir": "test", 14 | "type": "dev" 15 | } 16 | ], 17 | "package-specs": { 18 | "module": "esmodule", 19 | "in-source": true 20 | }, 21 | "suffix": ".mjs", 22 | "bs-dependencies": ["@rescript/core"], 23 | "bs-dev-dependencies": ["rescript-nodejs", "rescript-embed-lang"], 24 | "bsc-flags": ["-open RescriptCore"] 25 | } 26 | -------------------------------------------------------------------------------- /src/EdgeDB.res: -------------------------------------------------------------------------------- 1 | module Transaction = { 2 | type t 3 | 4 | /**To execute a query without retrieving a result, use the `execute` method. This is especially useful for mutations, where there’s often no need for the query to return a value. 5 | 6 | await client->EdgeDB.Client.execute(`insert Movie { 7 | title := "Avengers: Endgame" 8 | };`) */ 9 | @send 10 | external execute: (t, string, ~args: 'args=?) => promise = "execute" 11 | 12 | /**The `query` method always returns an array of results. It places no constraints on cardinality. 13 | 14 | ```rescript 15 | await client->EdgeDB.Client.query(`select 2 + 2;`) // [4] 16 | await client->EdgeDB.Client.query(`select [1, 2, 3];`) // [[1, 2, 3]] 17 | await client->EdgeDB.Client.query(`select {};`) // [] 18 | await client->EdgeDB.Client.query(`select {1, 2, 3};`) // [1, 2, 3] 19 | ``` 20 | */ 21 | @send 22 | external query: (t, string, ~args: 'args=?) => promise> = "query" 23 | 24 | /**Same as `query`, but returns result as stringified JSON. */ 25 | @send 26 | external queryJSON: (t, string, ~args: 'args=?) => promise = "queryJSON" 27 | 28 | /**If you know your query will only return a single element, you can tell EdgeDB to expect a singleton result by using the `querySingle` method. This is intended for queries that return zero or one elements. If the query returns a set with more than one elements, the Client will throw a runtime error. 29 | 30 | Note that if you’re selecting an array or tuple, the returned value may still be an array. 31 | 32 | ```rescript 33 | await client->EdgeDB.Client.querySingle(`select 2 + 2;`) // 4 34 | await client->EdgeDB.Client.querySingle(`select [1, 2, 3];`) // [1, 2, 3] 35 | await client->EdgeDB.Client.querySingle(`select {};`) // null 36 | await client->EdgeDB.Client.querySingle(`select {1, 2, 3};`) // Error 37 | ``` 38 | */ 39 | @send 40 | external querySingle: (t, string, ~args: 'args=?) => promise> = "querySingle" 41 | 42 | /**Same as `querySingle`, but returns result as stringified JSON. */ 43 | @send 44 | external querySingleJSON: (t, string, ~args: 'args=?) => promise = "querySingleJSON" 45 | 46 | /**Use `queryRequiredSingle` for queries that return exactly one element. If the query returns an empty set or a set with multiple elements, the Client will throw a runtime error. 47 | 48 | await client->EdgeDB.Client.queryRequiredSingle(`select 2 + 2;`) // 4 49 | await client->EdgeDB.Client.queryRequiredSingle(`select [1, 2, 3];`) // [1, 2, 3] 50 | await client->EdgeDB.Client.queryRequiredSingle(`select {};`) // Error 51 | await client->EdgeDB.Client.queryRequiredSingle(`select {1, 2, 3};`) // Error 52 | */ 53 | @send 54 | external queryRequiredSingle: (t, string, ~args: 'args=?) => promise<'result> = 55 | "queryRequiredSingle" 56 | 57 | /**Same as `queryRequiredSingle`, but returns result as stringified JSON. */ 58 | @send 59 | external queryRequiredSingleJSON: (t, string, ~args: 'args=?) => promise = 60 | "queryRequiredSingleJSON" 61 | } 62 | 63 | module Session = { 64 | type t 65 | } 66 | 67 | /**A JavaScript representation of an EdgeDB duration value. This class attempts to conform to the TC39 Temporal Proposal Duration type as closely as possible. 68 | 69 | No arguments may be infinite and all must have the same sign. Any non-integer arguments will be rounded towards zero. 70 | 71 | Read more: https://www.edgedb.com/docs/clients/js/reference#duration*/ 72 | module Duration = { 73 | @live 74 | type durationLike = { 75 | years?: float, 76 | months?: float, 77 | weeks?: float, 78 | days?: float, 79 | hours?: float, 80 | minutes?: float, 81 | seconds?: float, 82 | milliseconds?: float, 83 | microseconds?: float, 84 | nanoseconds?: float, 85 | } 86 | 87 | type t 88 | @send external from: durationLike => t = "from" 89 | @send external fromDuration: t => t = "from" 90 | @send external fromString: string => t = "from" 91 | } 92 | 93 | /**A client represents a connection to your database and provides methods for executing queries. 94 | 95 | In actuality, the client maintains a pool of connections under the hood. When your server is under load, queries will be run in parallel across many connections, instead of being bottlenecked by a single connection. 96 | 97 | To create a client: 98 | 99 | ```rescript 100 | let client = EdgeDB.Client.make() 101 | ``` 102 | 103 | Read more: https://www.edgedb.com/docs/clients/js/driver#client*/ 104 | module Client = { 105 | type t 106 | 107 | type tlsSecurity = 108 | | @as("insecure") Insecure 109 | | @as("no_host_verification") NoHostVerification 110 | | @as("strict") Strict 111 | | @as("default") Default 112 | 113 | @live 114 | type connectConfig = { 115 | dsn?: string, 116 | instanceName?: string, 117 | credentials?: string, 118 | credentialsFile?: string, 119 | host?: string, 120 | port?: int, 121 | database?: string, 122 | user?: string, 123 | password?: string, 124 | secretKey?: string, 125 | serverSettings?: unknown, 126 | tlsCA?: string, 127 | tlsCAFile?: string, 128 | tlsSecurity?: tlsSecurity, 129 | timeout?: int, 130 | waitUntilAvailable?: Duration.t, 131 | logging?: bool, 132 | } 133 | @live 134 | type clientOptions = {concurrency?: int} 135 | type connectOptions = {...clientOptions, ...connectConfig} 136 | 137 | @live 138 | type resolvedConnectConfig = { 139 | address: (string, float), 140 | database: string, 141 | user: string, 142 | password: option, 143 | secretKey: option, 144 | cloudProfile: string, 145 | tlsSecurity: tlsSecurity, 146 | waitUntilAvailable: float, 147 | } 148 | 149 | @live 150 | type partiallyNormalizedConfig = { 151 | connectionParams: resolvedConnectConfig, 152 | inProject: unit => promise, 153 | fromProject: bool, 154 | fromEnv: bool, 155 | } 156 | 157 | @live 158 | type normalizedConnectConfig = { 159 | ...partiallyNormalizedConfig, 160 | connectTimeout?: float, 161 | logging: bool, 162 | } 163 | 164 | @module("edgedb") external make: (~options: connectOptions=?) => t = "createClient" 165 | 166 | @unboxed @live 167 | type isolationLevel = | @as("SERIALIZABLE") Serializable 168 | 169 | @live 170 | type simpleTransactionOptions = { 171 | isolation?: isolationLevel, 172 | readonly?: bool, 173 | deferrable?: bool, 174 | } 175 | 176 | @send 177 | external withTransactionOptions: (t, simpleTransactionOptions) => t = "withTransactionOptions" 178 | 179 | @live 180 | type simpleRetryOptions = { 181 | attempts?: int, 182 | /** (attempt: int) => float */ 183 | backoff?: int => float, 184 | } 185 | 186 | @send 187 | external withRetryOptions: (t, simpleRetryOptions) => t = "withRetryOptions" 188 | 189 | @send 190 | external withSession: (t, Session.t) => t = "withSession" 191 | 192 | @send 193 | external withModuleAliases: (t, Dict.t) => t = "withModuleAliases" 194 | 195 | @live 196 | type allowBareDdl = AlwaysAllow | NeverAllow 197 | 198 | @live 199 | type simpleConfig = { 200 | @as("session_idle_transaction_timeout") sessionIdleTransactionTimeout?: Duration.t, 201 | @as("query_execution_timeout") queryExecutionTimeout?: Duration.t, 202 | @as("allow_bare_ddl") allowBareDdl?: allowBareDdl, 203 | @as("allow_dml_in_functions") allowDmlInFunctions?: bool, 204 | @as("allow_user_specified_id") allowUserSpecifiedId?: bool, 205 | @as("apply_access_policies") applyAccessPolicies?: bool, 206 | } 207 | 208 | @send 209 | external withConfig: (t, simpleConfig) => t = "withConfig" 210 | 211 | @send 212 | external withGlobals: (t, Dict.t) => t = "withGlobals" 213 | 214 | /**If you want to explicitly ensure that the client is connected without running a query, use the `ensureConnected()` method.*/ 215 | @send 216 | external ensureConnected: t => promise = "ensureConnected" 217 | 218 | @send 219 | external isClosed: t => bool = "isClosed" 220 | 221 | @send 222 | external close: t => promise = "close" 223 | 224 | @send 225 | external terminate: t => unit = "terminate" 226 | 227 | /* Execution */ 228 | 229 | @send external execute: (t, string, ~args: 'args=?) => promise = "execute" 230 | 231 | @send external query: (t, string, ~args: 'args=?) => promise> = "query" 232 | @send external queryJSON: (t, string, ~args: 'args=?) => promise = "queryJSON" 233 | 234 | @send 235 | external querySingle: (t, string, ~args: 'args=?) => promise> = "querySingle" 236 | @send 237 | external querySingleJSON: (t, string, ~args: 'args=?) => promise = "querySingleJSON" 238 | 239 | @send 240 | external queryRequiredSingle: (t, string, ~args: 'args=?) => promise<'result> = 241 | "queryRequiredSingle" 242 | 243 | @send 244 | external queryRequiredSingleJSON: (t, string, ~args: 'args=?) => promise = 245 | "queryRequiredSingleJSON" 246 | 247 | @send 248 | external transaction: (t, Transaction.t => promise<'result>) => promise<'result> = "transaction" 249 | } 250 | 251 | module Error = { 252 | @live 253 | type binaryProtocolError = 254 | | @as(0x03_01_00_00) BinaryProtocolError 255 | | @as(0x03_01_00_01) UnsupportedProtocolVersionError 256 | | @as(0x03_01_00_02) TypeSpecNotFoundError 257 | | @as(0x03_01_00_03) UnexpectedMessageError 258 | 259 | @live 260 | type inputDataError = 261 | | @as(0x03_02_00_00) InputDataError 262 | | @as(0x03_02_01_00) ParameterTypeMismatchError 263 | | @as(0x03_02_02_00) StateMismatchError 264 | 265 | @live 266 | type capabilityError = 267 | | @as(0x03_04_00_00) CapabilityError 268 | | @as(0x03_04_01_00) UnsupportedCapabilityError 269 | | @as(0x03_04_02_00) DisabledCapabilityError 270 | 271 | @live 272 | type protocolError = 273 | | @as(0x03_00_00_00) ProtocolError 274 | | @as(0x03_03_00_00) ResultCardinalityMismatchError 275 | | ...binaryProtocolError 276 | | ...inputDataError 277 | | ...capabilityError 278 | 279 | @live 280 | type invalidSyntaxError = 281 | | @as(0x04_01_00_00) InvalidSyntaxError 282 | | @as(0x04_01_01_00) EdgeQLSyntaxError 283 | | @as(0x04_01_02_00) SchemaSyntaxError 284 | | @as(0x04_01_03_00) GraphQLSyntaxError 285 | 286 | @live 287 | type invalidTargetError = 288 | | @as(0x04_02_01_00) InvalidTargetError 289 | | @as(0x04_02_01_01) InvalidLinkTargetError 290 | | @as(0x04_02_01_02) InvalidPropertyTargetError 291 | 292 | @live 293 | type invalidTypeError = | @as(0x04_02_00_00) InvalidTypeError | ...invalidTargetError 294 | 295 | @live 296 | type invalidReferenceError = 297 | | @as(0x04_03_00_00) InvalidReferenceError 298 | | @as(0x04_03_00_01) UnknownModuleError 299 | | @as(0x04_03_00_02) UnknownLinkError 300 | | @as(0x04_03_00_03) UnknownPropertyError 301 | | @as(0x04_03_00_04) UnknownUserError 302 | | @as(0x04_03_00_05) UnknownDatabaseError 303 | | @as(0x04_03_00_06) UnknownParameterError 304 | 305 | @live 306 | type invalidDefinitionError = 307 | | @as(0x04_05_01_00) InvalidDefinitionError 308 | | @as(0x04_05_01_01) InvalidModuleDefinitionError 309 | | @as(0x04_05_01_02) InvalidLinkDefinitionError 310 | | @as(0x04_05_01_03) InvalidPropertyDefinitionError 311 | | @as(0x04_05_01_04) InvalidUserDefinitionError 312 | | @as(0x04_05_01_05) InvalidDatabaseDefinitionError 313 | | @as(0x04_05_01_06) InvalidOperatorDefinitionError 314 | | @as(0x04_05_01_07) InvalidAliasDefinitionError 315 | | @as(0x04_05_01_08) InvalidFunctionDefinitionError 316 | | @as(0x04_05_01_09) InvalidConstraintDefinitionError 317 | | @as(0x04_05_01_0a) InvalidCastDefinitionError 318 | 319 | @live 320 | type duplicateDefinitionError = 321 | | @as(0x04_05_02_00) DuplicateDefinitionError 322 | | @as(0x04_05_02_01) DuplicateModuleDefinitionError 323 | | @as(0x04_05_02_02) DuplicateLinkDefinitionError 324 | | @as(0x04_05_02_03) DuplicatePropertyDefinitionError 325 | | @as(0x04_05_02_04) DuplicateUserDefinitionError 326 | | @as(0x04_05_02_05) DuplicateDatabaseDefinitionError 327 | | @as(0x04_05_02_06) DuplicateOperatorDefinitionError 328 | | @as(0x04_05_02_07) DuplicateViewDefinitionError 329 | | @as(0x04_05_02_08) DuplicateFunctionDefinitionError 330 | | @as(0x04_05_02_09) DuplicateConstraintDefinitionError 331 | | @as(0x04_05_02_0a) DuplicateCastDefinitionError 332 | | @as(0x04_05_02_0b) DuplicateMigrationError 333 | 334 | @live 335 | type schemaDefinitionError = 336 | | @as(0x04_05_00_00) SchemaDefinitionError 337 | | ...invalidDefinitionError 338 | | ...duplicateDefinitionError 339 | 340 | @live 341 | type transactionTimeoutError = 342 | | @as(0x04_06_0a_00) TransactionTimeoutError | @as(0x04_06_0a_01) IdleTransactionTimeoutError 343 | 344 | @live 345 | type sessionTimeoutError = 346 | | @as(0x04_06_00_00) SessionTimeoutError 347 | | @as(0x04_06_01_00) IdleSessionTimeoutError 348 | | @as(0x04_06_02_00) QueryTimeoutError 349 | | ...transactionTimeoutError 350 | 351 | @live 352 | type queryError = 353 | | @as(0x04_00_00_00) QueryError 354 | | @as(0x04_04_00_00) SchemaError 355 | | ...invalidSyntaxError 356 | | ...invalidTypeError 357 | | ...invalidReferenceError 358 | | ...schemaDefinitionError 359 | | ...sessionTimeoutError 360 | 361 | @live 362 | type invalidValueError = 363 | | @as(0x05_01_00_00) InvalidValueError 364 | | @as(0x05_01_00_01) DivisionByZeroError 365 | | @as(0x05_01_00_02) NumericOutOfRangeError 366 | | @as(0x05_01_00_03) AccessPolicyError 367 | | @as(0x05_01_00_04) QueryAssertionError 368 | 369 | @live 370 | type integrityError = 371 | | @as(0x05_02_00_00) IntegrityError 372 | | @as(0x05_02_00_01) ConstraintViolationError 373 | | @as(0x05_02_00_02) CardinalityViolationError 374 | | @as(0x05_02_00_03) MissingRequiredError 375 | 376 | @live 377 | type transactionConflictError = 378 | | @as(0x05_03_01_00) TransactionConflictError 379 | | @as(0x05_03_01_01) TransactionSerializationError 380 | | @as(0x05_03_01_02) TransactionDeadlockError 381 | 382 | @live 383 | type transactionError = | @as(0x05_03_00_00) TransactionError | ...transactionConflictError 384 | 385 | @live 386 | type executionError = 387 | | @as(0x05_00_00_00) ExecutionError 388 | | @as(0x05_04_00_00) WatchError 389 | | ...invalidValueError 390 | | ...integrityError 391 | | ...transactionError 392 | 393 | @live 394 | type accessError = | @as(0x07_00_00_00) AccessError | @as(0x07_01_00_00) AuthenticationError 395 | 396 | @live 397 | type availabilityError = 398 | | @as(0x08_00_00_00) AvailabilityError | @as(0x08_00_00_01) BackendUnavailableError 399 | 400 | @live 401 | type backendError = 402 | | @as(0x09_00_00_00) BackendError | @as(0x09_00_01_00) UnsupportedBackendFeatureError 403 | 404 | @live 405 | type logMessage = | @as(0xf0_00_00_00) LogMessage | @as(0xf0_01_00_00) WarningMessage 406 | 407 | @live 408 | type clientConnectionFailedError = 409 | | @as(0xff_01_01_00) ClientConnectionFailedError 410 | | @as(0xff_01_01_01) ClientConnectionFailedTemporarilyError 411 | 412 | @live 413 | type clientConnectionError = 414 | | ...clientConnectionFailedError 415 | | @as(0xff_01_00_00) ClientConnectionError 416 | | @as(0xff_01_02_00) ClientConnectionTimeoutError 417 | | @as(0xff_01_03_00) ClientConnectionClosedError 418 | 419 | @live 420 | type queryArgumentError = 421 | | @as(0xff_02_01_00) QueryArgumentError 422 | | @as(0xff_02_01_01) MissingArgumentError 423 | | @as(0xff_02_01_02) UnknownArgumentError 424 | | @as(0xff_02_01_03) InvalidArgumentError 425 | 426 | @live 427 | type interfaceError = | @as(0xff_02_00_00) InterfaceError | ...queryArgumentError 428 | 429 | @live 430 | type clientError = 431 | | @as(0xff_00_00_00) ClientError 432 | | @as(0xff_03_00_00) NoDataError 433 | | @as(0xff_04_00_00) InternalClientError 434 | | ...clientConnectionError 435 | | ...interfaceError 436 | 437 | @live 438 | type t = 439 | | @as(0x01_00_00_00) InternalServerError 440 | | @as(0x02_00_00_00) UnsupportedFeatureError 441 | | @as(0x06_00_00_00) ConfigurationError 442 | | ...queryError 443 | | ...executionError 444 | | ...accessError 445 | | ...availabilityError 446 | | ...backendError 447 | | ...logMessage 448 | | ...clientError 449 | | ...protocolError 450 | 451 | @live 452 | type edgeDbErrorClass = {code: t} 453 | 454 | type errorFromOperation = EdgeDbError(edgeDbErrorClass) | GenericError(Exn.t) 455 | 456 | type edgeDbError 457 | 458 | @module("edgedb") external edgeDbError: edgeDbError = "EdgeDBError" 459 | 460 | let instanceOf: ('a, 'b) => bool = %raw(`function instanceOf(a, b) { 461 | return a instanceof b 462 | }`) 463 | 464 | let fromExn = (exn: Exn.t): errorFromOperation => { 465 | if exn->instanceOf(edgeDbError) { 466 | EdgeDbError(Obj.magic(exn)) 467 | } else { 468 | GenericError(exn) 469 | } 470 | } 471 | } 472 | 473 | module QueryHelpers = { 474 | /** Returns all found items as an array. */ 475 | @live 476 | let many = (client, query, ~args=?) => Client.query(client, query, ~args) 477 | 478 | /** Returns a single item, if one was found. */ 479 | let single = async (client, query, ~args=?, ~onError=?) => 480 | switch await Client.querySingle(client, query, ~args) { 481 | | Value(v) => Some(v) 482 | | Null => None 483 | | exception Exn.Error(err) => 484 | switch onError { 485 | | None => () 486 | | Some(onError) => onError(err->Error.fromExn) 487 | } 488 | None 489 | } 490 | 491 | /** Assumes exactly one item is going to be found, and errors if that's not the case. */ 492 | @live 493 | let singleRequired = async (client, query, ~args=?) => 494 | switch await Client.queryRequiredSingle(client, query, ~args) { 495 | | v => Ok(v) 496 | | exception Exn.Error(err) => Error(err->Error.fromExn) 497 | } 498 | } 499 | 500 | module TransactionHelpers = { 501 | /** Returns all found items as an array. */ 502 | @live 503 | let many = (client, query, ~args=?) => Transaction.query(client, query, ~args) 504 | 505 | /** Returns a single item, if one was found. */ 506 | @live 507 | let single = async (client, query, ~args=?, ~onError=?) => 508 | switch await Transaction.querySingle(client, query, ~args) { 509 | | Value(v) => Some(v) 510 | | Null => None 511 | | exception Exn.Error(err) => 512 | switch onError { 513 | | None => () 514 | | Some(onError) => onError(err->Error.fromExn) 515 | } 516 | None 517 | } 518 | 519 | /** Assumes exactly one item is going to be found, and errors if that's not the case. */ 520 | @live 521 | let singleRequired = async (client, query, ~args=?) => 522 | switch await Transaction.queryRequiredSingle(client, query, ~args) { 523 | | v => Ok(v) 524 | | exception Exn.Error(err) => Error(err->Error.fromExn) 525 | } 526 | } 527 | 528 | module DataTypes = { 529 | module LocalDate = { 530 | type months = 531 | | @as(1) January 532 | | @as(2) February 533 | | @as(3) March 534 | | @as(4) April 535 | | @as(5) May 536 | | @as(6) June 537 | | @as(7) July 538 | | @as(8) August 539 | | @as(9) September 540 | | @as(10) October 541 | | @as(11) November 542 | | @as(12) December 543 | 544 | type dayOfWeek = 545 | | @as(1) Monday 546 | | @as(2) Tuesday 547 | | @as(3) Wednesday 548 | | @as(4) Thursday 549 | | @as(5) Friday 550 | | @as(6) Saturday 551 | | @as(7) Sunday 552 | 553 | type t 554 | 555 | @get external year: t => int = "year" 556 | @get external months: t => months = "month" 557 | @get external day: t => int = "day" 558 | @get external dayOfWeek: t => dayOfWeek = "dayOfWeek" 559 | @get external dayOfYear: t => int = "dayOfYear" 560 | @get external daysInWeek: t => int = "daysInWeek" 561 | @get external daysInMonth: t => int = "daysInMonth" 562 | @get external daysInYear: t => int = "daysInYear" 563 | @get external monthsInYear: t => int = "monthsInYear" 564 | @get external inLeapYear: t => bool = "inLeapYear" 565 | @send external toString: t => string = "toString" 566 | @send external toJSON: t => string = "toJSON" 567 | } 568 | 569 | module LocalDateTime = { 570 | type t 571 | 572 | @get external year: t => int = "year" 573 | @get external months: t => LocalDate.months = "month" 574 | @get external day: t => int = "day" 575 | @get external dayOfWeek: t => LocalDate.dayOfWeek = "dayOfWeek" 576 | @get external dayOfYear: t => int = "dayOfYear" 577 | @get external daysInWeek: t => int = "daysInWeek" 578 | @get external daysInMonth: t => int = "daysInMonth" 579 | @get external daysInYear: t => int = "daysInYear" 580 | @get external monthsInYear: t => int = "monthsInYear" 581 | @get external inLeapYear: t => bool = "inLeapYear" 582 | 583 | @get external hour: t => int = "hour" 584 | @get external minute: t => int = "minute" 585 | @get external second: t => int = "second" 586 | @get external millisecond: t => int = "millisecond" 587 | @get external microsecond: t => int = "microsecond" 588 | @get external nanosecond: t => int = "nanosecond" 589 | 590 | @send external toString: t => string = "toString" 591 | @send external toJSON: t => string = "toJSON" 592 | } 593 | 594 | module LocalTime = { 595 | type t 596 | 597 | @get external hour: t => int = "hour" 598 | @get external minute: t => int = "minute" 599 | @get external second: t => int = "second" 600 | @get external millisecond: t => int = "millisecond" 601 | @get external microsecond: t => int = "microsecond" 602 | @get external nanosecond: t => int = "nanosecond" 603 | 604 | @send external toString: t => string = "toString" 605 | @send external toJSON: t => string = "toJSON" 606 | } 607 | 608 | module Duration = { 609 | type t 610 | 611 | @get external years: t => float = "years" 612 | @get external months: t => float = "months" 613 | @get external weeks: t => float = "weeks" 614 | @get external days: t => float = "days" 615 | @get external hours: t => float = "hours" 616 | @get external minutes: t => float = "minutes" 617 | @get external seconds: t => float = "seconds" 618 | @get external milliseconds: t => float = "milliseconds" 619 | @get external microseconds: t => float = "microseconds" 620 | @get external sign: t => float = "sign" 621 | @get external blank: t => bool = "blank" 622 | 623 | @send external toString: t => string = "toString" 624 | @send external toJSON: t => string = "toJSON" 625 | } 626 | 627 | module DateDuration = { 628 | type t 629 | 630 | @get external years: t => float = "years" 631 | @get external months: t => float = "months" 632 | @get external weeks: t => float = "weeks" 633 | @get external days: t => float = "days" 634 | 635 | @send external toString: t => string = "toString" 636 | @send external toJSON: t => string = "toJSON" 637 | } 638 | 639 | module RelativeDuration = { 640 | type t 641 | 642 | @get external years: t => float = "years" 643 | @get external months: t => float = "months" 644 | @get external weeks: t => float = "weeks" 645 | @get external days: t => float = "days" 646 | @get external hours: t => float = "hours" 647 | @get external minutes: t => float = "minutes" 648 | @get external seconds: t => float = "seconds" 649 | @get external milliseconds: t => float = "milliseconds" 650 | @get external microseconds: t => float = "microseconds" 651 | 652 | @send external toString: t => string = "toString" 653 | @send external toJSON: t => string = "toJSON" 654 | } 655 | 656 | module ConfigMemory = { 657 | type t 658 | 659 | @get external bytes: t => float = "bytes" 660 | @get external bytesBigInt: t => bigint = "bytesBigInt" 661 | @get external kibibytes: t => float = "kibibytes" 662 | @get external mebibytes: t => float = "mebibytes" 663 | @get external gibibytes: t => float = "gibibytes" 664 | @get external tebibytes: t => float = "tebibytes" 665 | @get external pebibytes: t => float = "pebibytes" 666 | @send external toString: t => string = "toString" 667 | } 668 | } 669 | -------------------------------------------------------------------------------- /test/GeneratorTests.test.res: -------------------------------------------------------------------------------- 1 | open TestFramework 2 | 3 | module Errors = { 4 | let unexpectedTokenWithHint = `Unexpected token: 5 | | 6 | 6 | actors: { 7 | | ^^^^^^ 8 | Hint: It appears that a ',' is missing in a shape before 'actors'` 9 | 10 | let typeOrAliasDoesNotExist = `object type or alias 'default::a' does not exist 11 | | 12 | 11 | } order by a.title 13 | | ^ 14 | Hint: did you mean '.id'?` 15 | 16 | let objectTypeHasNoProp = `object type 'default::Movie' has no link or property 'TITLE' 17 | | 18 | 11 | } order by .TITLE 19 | | ^^^^^^ 20 | ` 21 | 22 | let unexpected = `Unexpected 'orderaaaaa' 23 | | 24 | 11 | } orderaaaaa 25 | | ^^^^^^^^^^ 26 | ` 27 | } 28 | 29 | describe("extracting errors", () => { 30 | test("Extracting errors #unexpectedWithTokenHint", () => { 31 | let error = EdgeDbGenerator__Utils.Errors.extractFromString( 32 | Errors.unexpectedTokenWithHint, 33 | ~startLoc={line: 10, col: 12}, 34 | ) 35 | expect(error)->Expect.toMatchSnapshot 36 | }) 37 | 38 | test("Extracting errors #typeOrAliasDoesNotExist", () => { 39 | let error = EdgeDbGenerator__Utils.Errors.extractFromString( 40 | Errors.typeOrAliasDoesNotExist, 41 | ~startLoc={line: 10, col: 12}, 42 | ) 43 | expect(error)->Expect.toMatchSnapshot 44 | }) 45 | 46 | test("Extracting errors #objectTypeHasNoProp", () => { 47 | let error = EdgeDbGenerator__Utils.Errors.extractFromString( 48 | Errors.objectTypeHasNoProp, 49 | ~startLoc={line: 10, col: 12}, 50 | ) 51 | expect(error)->Expect.toMatchSnapshot 52 | }) 53 | 54 | test("Extracting errors #unexpected", () => { 55 | let error = EdgeDbGenerator__Utils.Errors.extractFromString( 56 | Errors.unexpected, 57 | ~startLoc={line: 10, col: 12}, 58 | ) 59 | expect(error)->Expect.toMatchSnapshot 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /test/TestFramework.res: -------------------------------------------------------------------------------- 1 | @module("bun:test") external test: (string, unit => unit) => unit = "test" 2 | @module("bun:test") external describe: (string, unit => unit) => unit = "describe" 3 | @module("bun:test") external testAsync: (string, unit => promise) => unit = "test" 4 | 5 | module Expect = { 6 | type t 7 | @send external toBe: (t, 'value) => unit = "toBe" 8 | @send external toEqual: (t, 'value) => unit = "toEqual" 9 | @send external toMatchSnapshot: t => unit = "toMatchSnapshot" 10 | } 11 | 12 | @module("bun:test") external expect: 'result => Expect.t = "expect" 13 | 14 | @module("bun:test") external afterAllAsync: (unit => promise) => unit = "afterAll" 15 | -------------------------------------------------------------------------------- /test/UnusedSelectionsTests.test.res: -------------------------------------------------------------------------------- 1 | open TestFramework 2 | open UnusedSelections 3 | 4 | let sampleReanalyzeOutput = ` 5 | Warning Dead Type 6 | /Users/zth/OSS/rescript-edgedb/dbTestProject/src/__generated__/Movies__edgeql.res:18:5-23 7 | AllMovies.response__actors.numberOfPets is a record label never used to read a value 8 | <-- line 18 9 | @dead("AllMovies.response__actors.numberOfPets") numberOfPets: float, 10 | 11 | Warning Dead Type 12 | /Users/zth/OSS/rescript-edgedb/dbTestProject/src/__generated__/Movies__edgeql.res:23:5-17 13 | AllMovies.response.title is a record label never used to read a value 14 | <-- line 23 15 | @dead("AllMovies.response.title") title: string, 16 | 17 | Warning Dead Type 18 | /Users/zth/OSS/rescript-edgedb/dbTestProject/src/__generated__/Movies__edgeql.res:115:5-16 19 | MovieByTitle.response__actors.name is a record label never used to read a value 20 | <-- line 115 21 | @dead("MovieByTitle.response__actors.name") name: string, 22 | 23 | Warning Dead Type 24 | /Users/zth/OSS/rescript-edgedb/dbTestProject/src/__generated__/Movies__edgeql.res:121:5-14 25 | MovieByTitle.response.id is a record label never used to read a value 26 | <-- line 121 27 | @dead("MovieByTitle.response.id") id: string, 28 | 29 | Warning Dead Value 30 | /Users/zth/OSS/rescript-edgedb/dbTestProject/test/TestProject.test.res:55:11-22 31 | _id is never used 32 | <-- line 55 33 | @dead("_id") let _id = id 34 | ` 35 | 36 | describe("extracting filenames", () => { 37 | test("it extracts file names", () => { 38 | expect( 39 | extractFileName(` /Users/zth/OSS/edgedb-rescript/rescript/__generated__/QueryFile__edgeql.res, line 14, characters 4-22`), 40 | )->Expect.toBe(Some("QueryFile")) 41 | }) 42 | 43 | test("it doesnt extract unless filename is from EdgeDB", () => { 44 | expect( 45 | extractFileName(` File "/Users/zth/OSS/edgedb-rescript/rescript/__generated__/QueryFile.res", line 14, characters 4-22`), 46 | )->Expect.toBe(None) 47 | }) 48 | }) 49 | 50 | describe("extracting line info", () => { 51 | test("it extracts line info for arg", () => { 52 | expect( 53 | extractLineInfo( 54 | " FindMovieQuery.response.status is a record label never used to read a value", 55 | ), 56 | )->Expect.toEqual( 57 | Some({ 58 | queryName: "FindMovieQuery", 59 | fieldName: "status", 60 | recordPath: None, 61 | }), 62 | ) 63 | }) 64 | 65 | test("it handles record paths", () => { 66 | expect( 67 | extractLineInfo( 68 | " FindMovieQuery.response__actor.status is a record label never used to read a value", 69 | ), 70 | )->Expect.toEqual( 71 | Some({ 72 | queryName: "FindMovieQuery", 73 | fieldName: "status", 74 | recordPath: Some(["actor"]), 75 | }), 76 | ) 77 | }) 78 | }) 79 | 80 | test("it can extract results", () => { 81 | expect(UnusedSelections.extractFromReanalyzeOutput(sampleReanalyzeOutput))->Expect.toMatchSnapshot 82 | }) 83 | -------------------------------------------------------------------------------- /test/__snapshots__/EdgeDbGeneratorTests.test.mjs.snap: -------------------------------------------------------------------------------- 1 | // Bun Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`extracting queries from ReScript documents it can extract from docs #1 1`] = ` 4 | "select Movie { 5 | title, 6 | status, 7 | actors: { 8 | name, 9 | age 10 | } 11 | } filter 12 | .title = $movieTitle" 13 | `; 14 | 15 | exports[`extracting queries from ReScript documents it can extract from docs #2 1`] = ` 16 | "select Movie { 17 | title, 18 | status, 19 | actors: { 20 | name, 21 | age 22 | } 23 | } filter 24 | .title = $movieTitle" 25 | `; 26 | 27 | exports[`extracting queries from ReScript documents it can extract from docs #2 2`] = ` 28 | "select Movie { 29 | title, 30 | status, 31 | actors: { 32 | name, 33 | age 34 | } 35 | } filter 36 | .title = $movieTitle" 37 | `; 38 | 39 | exports[`extracting queries from ReScript documents it can extract from docs #2 3`] = ` 40 | "select User { 41 | name 42 | } filter 43 | .id = $userId" 44 | `; 45 | 46 | exports[`generate file 1`] = ` 47 | { 48 | "contents": 49 | "// @sourceHash b71c2e4fb6a0ba65311c5fce81401d7a 50 | module FindMovie = { 51 | let queryText = \`select Movie { 52 | title, 53 | status, 54 | actors: { 55 | name, 56 | age 57 | } 58 | } filter 59 | .title = $movieTitle\` 60 | 61 | type args = {movieTitle: string} 62 | 63 | type response = {title: string, status: [#Published | #Unpublished]} 64 | 65 | let query = (client: EdgeDB.Client.t, args: args): promise> => { 66 | client->EdgeDB.QueryHelpers.singleRequired(queryText, ~args) 67 | } 68 | 69 | let transaction = (transaction: EdgeDB.Transaction.t, args: args): promise> => { 70 | transaction->EdgeDB.TransactionHelpers.singleRequired(queryText, ~args) 71 | } 72 | } 73 | 74 | " 75 | , 76 | "hash": "b71c2e4fb6a0ba65311c5fce81401d7a", 77 | "path": "SomeQueryFile__edgeDb.res", 78 | } 79 | `; 80 | -------------------------------------------------------------------------------- /test/__snapshots__/GeneratorTests.test.mjs.snap: -------------------------------------------------------------------------------- 1 | // Bun Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`extracting errors Extracting errors #unexpectedWithTokenHint 1`] = ` 4 | { 5 | "end": { 6 | "col": 12, 7 | "line": 15, 8 | }, 9 | "start": { 10 | "col": 6, 11 | "line": 15, 12 | }, 13 | "text": 14 | "Unexpected token: 15 | Hint: It appears that a ',' is missing in a shape before 'actors'" 16 | , 17 | } 18 | `; 19 | 20 | exports[`extracting errors Extracting errors #typeOrAliasDoesNotExist 1`] = ` 21 | { 22 | "end": { 23 | "col": 16, 24 | "line": 20, 25 | }, 26 | "start": { 27 | "col": 15, 28 | "line": 20, 29 | }, 30 | "text": 31 | "object type or alias 'default::a' does not exist 32 | Hint: did you mean '.id'?" 33 | , 34 | } 35 | `; 36 | 37 | exports[`extracting errors Extracting errors #objectTypeHasNoProp 1`] = ` 38 | { 39 | "end": { 40 | "col": 21, 41 | "line": 20, 42 | }, 43 | "start": { 44 | "col": 15, 45 | "line": 20, 46 | }, 47 | "text": "object type 'default::Movie' has no link or property 'TITLE'", 48 | } 49 | `; 50 | 51 | exports[`extracting errors Extracting errors #unexpected 1`] = ` 52 | { 53 | "end": { 54 | "col": 16, 55 | "line": 20, 56 | }, 57 | "start": { 58 | "col": 6, 59 | "line": 20, 60 | }, 61 | "text": "Unexpected 'orderaaaaa'", 62 | } 63 | `; 64 | -------------------------------------------------------------------------------- /test/__snapshots__/UnusedSelectionsTests.test.mjs.snap: -------------------------------------------------------------------------------- 1 | // Bun Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`it can extract results 1`] = ` 4 | [ 5 | [ 6 | "Movies", 7 | { 8 | "fieldName": "numberOfPets", 9 | "queryName": "AllMovies", 10 | "recordPath": [ 11 | "actors", 12 | ], 13 | }, 14 | ], 15 | [ 16 | "Movies", 17 | { 18 | "fieldName": "title", 19 | "queryName": "AllMovies", 20 | "recordPath": undefined, 21 | }, 22 | ], 23 | [ 24 | "Movies", 25 | { 26 | "fieldName": "name", 27 | "queryName": "MovieByTitle", 28 | "recordPath": [ 29 | "actors", 30 | ], 31 | }, 32 | ], 33 | [ 34 | "Movies", 35 | { 36 | "fieldName": "id", 37 | "queryName": "MovieByTitle", 38 | "recordPath": undefined, 39 | }, 40 | ], 41 | ] 42 | `; 43 | -------------------------------------------------------------------------------- /vscode-extension/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | build -------------------------------------------------------------------------------- /vscode-extension/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # main 2 | 3 | # 0.3.2 4 | 5 | - Add readme. 6 | 7 | # 0.3.1 8 | 9 | - Tidy up query pasting. 10 | 11 | # 0.3.0 12 | 13 | - Add snippets for inserting `%edgeql` blocks. 14 | 15 | # 0.2.0 16 | 17 | - Add code action for opening queries in the local EdgeDB UI and pasting them back. 18 | -------------------------------------------------------------------------------- /vscode-extension/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 17 | THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /vscode-extension/README.md: -------------------------------------------------------------------------------- 1 | # vscode-rescript-edgedb 2 | 3 | `rescript-edgedb` comes with this dedicated [VSCode extension](https://marketplace.visualstudio.com/items?itemName=GabrielNordeborn.vscode-rescript-edgedb) designed to enhance the experience of using ReScript and EdgeDB together. Below is a list of how you use it, and what it can do. 4 | 5 | > NOTE: Make sure you install the [official EdgeDB extension](https://marketplace.visualstudio.com/items?itemName=magicstack.edgedb) as well, so you get syntax highlighting and more. 6 | 7 | ### Snippets 8 | 9 | Snippets for easily adding new `%edgeql` blocks are included: 10 | 11 | ![snippets](https://github.com/zth/rescript-edgedb/assets/1457626/8dc1c54b-470d-4dee-9598-e26d35632286) 12 | 13 | These appear as soon as you start writing `%edgeql` in a ReScript file. 14 | 15 | ### In editor error messages 16 | 17 | Any errors for your EdgeQL queries will show directly in your ReScript files that define them: 18 | 19 | ![in-editor-errors](https://github.com/zth/rescript-edgedb/assets/1457626/19f6bf01-5648-4354-b510-881ed9b05c3a) 20 | 21 | ### Easily edit queries in the dedicated EdgeDB UI 22 | 23 | You can easily open the local EdgeDB UI, edit your query in there (including running it, etc), and then insert the modified query back: 24 | 25 | ![open-in-edgedb-ui](https://github.com/zth/rescript-edgedb/assets/1457626/e4dca50c-de60-4a78-8de9-f195a2cfd88d) 26 | 27 | It works like this: 28 | 29 | 1. Put the cursor in the query you want to edit. 30 | 2. Activate code actions. 31 | 3. Select the code action for opening the EdgeDB UI and copying the query. 32 | 4. The local EdgeDB query editor UI will now open in your browser, and the EdgeQL query you had your cursor in will be copied to your clipboard. 33 | 5. Paste the query into the query editor and make the edits you want. 34 | 6. Copy the entire query text and go back to VSCode and the file which has your query. 35 | 7. Activate code actions again and select the code action for inserting your modified query. 36 | 8. Done! 37 | -------------------------------------------------------------------------------- /vscode-extension/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zth/rescript-edgedb/04d64686783c4ef40541eccc1765ded4725e329d/vscode-extension/images/icon.png -------------------------------------------------------------------------------- /vscode-extension/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-rescript-edgedb", 3 | "version": "0.3.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "vscode-rescript-edgedb", 9 | "version": "0.3.2", 10 | "license": "MIT", 11 | "dependencies": { 12 | "dotenv": "^16.3.2", 13 | "typescript": "5.0.4" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "^18.15.11", 17 | "@types/vscode": "^1.77.0", 18 | "esbuild": "^0.17.15" 19 | }, 20 | "engines": { 21 | "vscode": "^1.77.0" 22 | } 23 | }, 24 | "node_modules/@esbuild/darwin-arm64": { 25 | "version": "0.17.15", 26 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.15.tgz", 27 | "integrity": "sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==", 28 | "cpu": [ 29 | "arm64" 30 | ], 31 | "dev": true, 32 | "optional": true, 33 | "os": [ 34 | "darwin" 35 | ], 36 | "engines": { 37 | "node": ">=12" 38 | } 39 | }, 40 | "node_modules/@types/node": { 41 | "version": "18.15.11", 42 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", 43 | "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", 44 | "dev": true 45 | }, 46 | "node_modules/@types/vscode": { 47 | "version": "1.77.0", 48 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.77.0.tgz", 49 | "integrity": "sha512-MWFN5R7a33n8eJZJmdVlifjig3LWUNRrPeO1xemIcZ0ae0TEQuRc7G2xV0LUX78RZFECY1plYBn+dP/Acc3L0Q==", 50 | "dev": true 51 | }, 52 | "node_modules/dotenv": { 53 | "version": "16.3.2", 54 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz", 55 | "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==", 56 | "engines": { 57 | "node": ">=12" 58 | }, 59 | "funding": { 60 | "url": "https://github.com/motdotla/dotenv?sponsor=1" 61 | } 62 | }, 63 | "node_modules/esbuild": { 64 | "version": "0.17.15", 65 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.15.tgz", 66 | "integrity": "sha512-LBUV2VsUIc/iD9ME75qhT4aJj0r75abCVS0jakhFzOtR7TQsqQA5w0tZ+KTKnwl3kXE0MhskNdHDh/I5aCR1Zw==", 67 | "dev": true, 68 | "hasInstallScript": true, 69 | "bin": { 70 | "esbuild": "bin/esbuild" 71 | }, 72 | "engines": { 73 | "node": ">=12" 74 | }, 75 | "optionalDependencies": { 76 | "@esbuild/android-arm": "0.17.15", 77 | "@esbuild/android-arm64": "0.17.15", 78 | "@esbuild/android-x64": "0.17.15", 79 | "@esbuild/darwin-arm64": "0.17.15", 80 | "@esbuild/darwin-x64": "0.17.15", 81 | "@esbuild/freebsd-arm64": "0.17.15", 82 | "@esbuild/freebsd-x64": "0.17.15", 83 | "@esbuild/linux-arm": "0.17.15", 84 | "@esbuild/linux-arm64": "0.17.15", 85 | "@esbuild/linux-ia32": "0.17.15", 86 | "@esbuild/linux-loong64": "0.17.15", 87 | "@esbuild/linux-mips64el": "0.17.15", 88 | "@esbuild/linux-ppc64": "0.17.15", 89 | "@esbuild/linux-riscv64": "0.17.15", 90 | "@esbuild/linux-s390x": "0.17.15", 91 | "@esbuild/linux-x64": "0.17.15", 92 | "@esbuild/netbsd-x64": "0.17.15", 93 | "@esbuild/openbsd-x64": "0.17.15", 94 | "@esbuild/sunos-x64": "0.17.15", 95 | "@esbuild/win32-arm64": "0.17.15", 96 | "@esbuild/win32-ia32": "0.17.15", 97 | "@esbuild/win32-x64": "0.17.15" 98 | } 99 | }, 100 | "node_modules/typescript": { 101 | "version": "5.0.4", 102 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", 103 | "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", 104 | "bin": { 105 | "tsc": "bin/tsc", 106 | "tsserver": "bin/tsserver" 107 | }, 108 | "engines": { 109 | "node": ">=12.20" 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /vscode-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-rescript-edgedb", 3 | "description": "Improve quality-of-life of using EdgeDB in ReScript with VSCode.", 4 | "version": "0.3.2", 5 | "main": "./build/extension.js", 6 | "engines": { 7 | "vscode": "^1.77.0" 8 | }, 9 | "scripts": { 10 | "vscode:prepublish": "yarn build", 11 | "build:watch": "tsc -w", 12 | "build:extension": "esbuild ./src/extension.ts --bundle --outfile=build/extension.js --external:vscode --format=cjs --platform=node --sourcemap", 13 | "build:clean": "rm -rf build", 14 | "build": "yarn build:clean && yarn build:extension" 15 | }, 16 | "author": "Gabriel Nordeborn ", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/zth/rescript-edgedb" 20 | }, 21 | "publisher": "GabrielNordeborn", 22 | "activationEvents": [ 23 | "workspaceContains:**/rescript.json", 24 | "workspaceContains:**/bsconfig.json" 25 | ], 26 | "categories": [ 27 | "Other" 28 | ], 29 | "icon": "images/icon.png", 30 | "contributes": { 31 | "snippets": [ 32 | { 33 | "language": "rescript", 34 | "path": "./snippets.json" 35 | } 36 | ] 37 | }, 38 | "galleryBanner": { 39 | "color": "#171E26", 40 | "theme": "dark" 41 | }, 42 | "license": "MIT", 43 | "dependencies": { 44 | "dotenv": "^16.3.2", 45 | "typescript": "5.0.4" 46 | }, 47 | "devDependencies": { 48 | "@types/node": "^18.15.11", 49 | "@types/vscode": "^1.77.0", 50 | "esbuild": "^0.17.15" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vscode-extension/snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "Insert EdgeQL query block": { 3 | "prefix": "%edgeql", 4 | "body": [ 5 | "let query = %edgeql(`", 6 | " # @name ${TM_FILENAME_BASE}Query", 7 | " $0", 8 | "`)" 9 | ], 10 | "description": "Insert EdgeQL query block" 11 | }, 12 | "Insert EdgeQL query block (as module)": { 13 | "prefix": "%edgeql", 14 | "body": [ 15 | "module Query = %edgeql(`", 16 | " # @name ${TM_FILENAME_BASE}Query", 17 | " $0", 18 | "`)" 19 | ], 20 | "description": "Insert EdgeQL query block (as module)" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vscode-extension/src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as childProcess from "child_process"; 2 | import * as os from "os"; 3 | import * as fs from "fs"; 4 | import * as path from "path"; 5 | import * as dotenv from "dotenv"; 6 | import { 7 | ExtensionContext, 8 | workspace, 9 | languages, 10 | Uri, 11 | Diagnostic, 12 | DiagnosticSeverity, 13 | Range, 14 | Position, 15 | TextDocument, 16 | window, 17 | commands, 18 | env, 19 | CodeAction, 20 | Command, 21 | WorkspaceEdit, 22 | } from "vscode"; 23 | 24 | let tempFilePrefix = "rescript_edgedb_" + process.pid + "_"; 25 | let tempFileId = 0; 26 | 27 | type loc = { line: number; col: number }; 28 | 29 | function createFileInTempDir() { 30 | let tempFileName = tempFilePrefix + tempFileId + ".res"; 31 | tempFileId = tempFileId + 1; 32 | return path.join(os.tmpdir(), tempFileName); 33 | } 34 | 35 | function findProjectPackageJsonRoot(source: string): null | string { 36 | let dir = path.dirname(source); 37 | if (fs.existsSync(path.join(dir, "package.json"))) { 38 | return dir; 39 | } else { 40 | if (dir === source) { 41 | // reached top 42 | return null; 43 | } else { 44 | return findProjectPackageJsonRoot(dir); 45 | } 46 | } 47 | } 48 | 49 | async function setupErrorLogWatcher(context: ExtensionContext) { 50 | const rootFiles = await workspace.findFiles( 51 | "**/{bsconfig,rescript}.json", 52 | "**/node_modules/**", 53 | 10 54 | ); 55 | 56 | const watchers = rootFiles 57 | .reduce((uniquePaths: Array, filePath) => { 58 | const dir = path.resolve(path.dirname(filePath.fsPath), "lib/bs"); 59 | 60 | if (!uniquePaths.includes(dir)) { 61 | uniquePaths.push(dir); 62 | } 63 | 64 | return uniquePaths; 65 | }, []) 66 | .map((dir) => { 67 | const watcher = workspace.createFileSystemWatcher( 68 | `${dir}/.generator.edgeql.log` 69 | ); 70 | 71 | const diagnostics = languages.createDiagnosticCollection("edgedb"); 72 | 73 | function syncDiagnostics() { 74 | try { 75 | const contents = fs.readFileSync( 76 | path.resolve(dir, ".generator.edgeql.log"), 77 | "utf-8" 78 | ); 79 | const parsed: Record< 80 | string, 81 | Array<{ 82 | startLoc: loc; 83 | endLoc: loc; 84 | errorMessage: string; 85 | }> 86 | > = JSON.parse(contents); 87 | 88 | // Clear diagnostics no longer present 89 | diagnostics.forEach((uri, _) => { 90 | const inParsed = parsed[uri.fsPath]; 91 | if (inParsed?.length === 0) { 92 | diagnostics.delete(uri); 93 | } 94 | }); 95 | 96 | Object.entries(parsed).forEach(([filePath, errors]) => { 97 | if (errors.length > 0) { 98 | diagnostics.set( 99 | Uri.parse(filePath), 100 | errors.map( 101 | (err) => 102 | new Diagnostic( 103 | new Range( 104 | new Position(err.startLoc.line, err.startLoc.col), 105 | new Position(err.endLoc.line, err.endLoc.col) 106 | ), 107 | err.errorMessage, 108 | DiagnosticSeverity.Error 109 | ) 110 | ) 111 | ); 112 | } 113 | }); 114 | } catch (e) { 115 | diagnostics.clear(); 116 | console.error(e); 117 | } 118 | } 119 | 120 | watcher.onDidChange((_) => { 121 | syncDiagnostics(); 122 | }); 123 | 124 | watcher.onDidCreate((_) => { 125 | syncDiagnostics(); 126 | }); 127 | 128 | watcher.onDidDelete((_) => { 129 | diagnostics.clear(); 130 | }); 131 | 132 | // Initial sync 133 | syncDiagnostics(); 134 | 135 | return { 136 | watcher, 137 | diagnostics, 138 | }; 139 | }); 140 | 141 | context.subscriptions.push( 142 | ...watchers.map(({ watcher }) => ({ 143 | dispose: () => watcher.dispose(), 144 | })) 145 | ); 146 | } 147 | 148 | type dataFromFile = { 149 | content: string; 150 | start: loc; 151 | end: loc; 152 | tag: string; 153 | }; 154 | 155 | const edgeqlContent: Map = new Map(); 156 | const waitingForPastes: Map< 157 | string, 158 | { 159 | queryName: string; 160 | offset: number; 161 | start: loc; 162 | end: loc; 163 | } 164 | > = new Map(); 165 | 166 | function callRescriptEdgeDBCli(command: string[], cwd: string) { 167 | const dotEnvLoc = path.join(cwd, ".env"); 168 | const dotEnvExists = fs.existsSync(dotEnvLoc); 169 | const env = dotEnvExists ? dotenv.config({ path: dotEnvLoc }) : null; 170 | 171 | return childProcess.execFileSync( 172 | "./node_modules/.bin/rescript-edgedb", 173 | command, 174 | { 175 | cwd: cwd, 176 | env: { 177 | ...process.env, 178 | ...env?.parsed, 179 | }, 180 | } 181 | ); 182 | } 183 | 184 | function updateContent(e: TextDocument) { 185 | if (e.languageId === "rescript") { 186 | const text = e.getText(); 187 | const cwd = findProjectPackageJsonRoot(e.fileName); 188 | if (cwd != null && text.includes("%edgeql(")) { 189 | const tempFile = createFileInTempDir(); 190 | fs.writeFileSync(tempFile, text); 191 | try { 192 | const dataFromCli = callRescriptEdgeDBCli(["extract", tempFile], cwd); 193 | const data: dataFromFile[] = JSON.parse(dataFromCli.toString()); 194 | edgeqlContent.set(e.fileName, data); 195 | } catch (e) { 196 | console.error(e); 197 | } finally { 198 | fs.rmSync(tempFile); 199 | } 200 | } else { 201 | edgeqlContent.delete(e.fileName); 202 | } 203 | } 204 | } 205 | 206 | function extractQueryName(str: string): string | undefined { 207 | return str.trim().split("\n")[0]?.split("@name ")[1]?.trim(); 208 | } 209 | 210 | async function setupDocumentWatchers(context: ExtensionContext) { 211 | context.subscriptions.push( 212 | workspace.onDidOpenTextDocument((e) => { 213 | updateContent(e); 214 | }) 215 | ); 216 | context.subscriptions.push( 217 | workspace.onDidCloseTextDocument((e) => { 218 | edgeqlContent.delete(e.fileName); 219 | waitingForPastes.delete(e.fileName); 220 | }) 221 | ); 222 | context.subscriptions.push( 223 | workspace.onDidChangeTextDocument((e) => { 224 | updateContent(e.document); 225 | }) 226 | ); 227 | } 228 | 229 | export async function activate(context: ExtensionContext) { 230 | let currentWorkspacePath = workspace.workspaceFolders?.[0].uri.fsPath; 231 | if (currentWorkspacePath == null) throw new Error("Init failed."); 232 | 233 | await Promise.all([ 234 | setupErrorLogWatcher(context), 235 | setupDocumentWatchers(context), 236 | ]); 237 | 238 | languages.registerCompletionItemProvider( 239 | { 240 | language: "rescript", 241 | }, 242 | { 243 | provideCompletionItems(d, p) { 244 | const pkgJson = findProjectPackageJsonRoot(d.fileName); 245 | return []; 246 | }, 247 | } 248 | ); 249 | 250 | context.subscriptions.push( 251 | commands.registerCommand( 252 | "vscode-rescript-edgedb-open-ui", 253 | async ( 254 | query: string, 255 | cwd: string, 256 | fileName: string, 257 | start: loc, 258 | end: loc, 259 | copyToClipboard: boolean 260 | ) => { 261 | const url = JSON.parse( 262 | callRescriptEdgeDBCli(["ui-url"], cwd).toString() 263 | ); 264 | const lines = query.trim().split("\n"); 265 | // First line has the comment with the query name, so the second line will have the offset 266 | const offset = lines[1]?.match(/^\s*/)?.[0].length ?? 2; 267 | const offsetAsStr = Array.from({ length: offset }) 268 | .map((_) => " ") 269 | .join(""); 270 | 271 | const queryText = lines 272 | .map((l) => { 273 | const leadingWhitespace = l.slice(0, offset); 274 | if (leadingWhitespace === offsetAsStr) { 275 | return l.slice(offset); 276 | } 277 | 278 | return l; 279 | }) 280 | .join("\n"); 281 | 282 | if (copyToClipboard) { 283 | await env.clipboard.writeText(queryText); 284 | 285 | const queryName = extractQueryName(queryText); 286 | if (queryName != null) { 287 | waitingForPastes.set(fileName, { 288 | start, 289 | end, 290 | offset, 291 | queryName: "", 292 | }); 293 | } 294 | window.showInformationMessage( 295 | "The EdgeQL query was copied to the clipboard." 296 | ); 297 | } 298 | 299 | const _didOpen = await env.openExternal(Uri.parse(`${url}/editor`)); 300 | } 301 | ) 302 | ); 303 | 304 | context.subscriptions.push( 305 | commands.registerCommand( 306 | "vscode-rescript-edgedb-paste-edited-query", 307 | async ( 308 | documentUri: string, 309 | start: loc, 310 | end: loc, 311 | textToInsert: string, 312 | fileName: string 313 | ) => { 314 | const edit = new WorkspaceEdit(); 315 | 316 | edit.replace( 317 | Uri.parse(documentUri), 318 | new Range( 319 | new Position(start.line, start.col), 320 | new Position(end.line, end.col) 321 | ), 322 | textToInsert 323 | ); 324 | 325 | workspace.applyEdit(edit); 326 | 327 | waitingForPastes.delete(fileName); 328 | } 329 | ) 330 | ); 331 | 332 | context.subscriptions.push( 333 | languages.registerCodeActionsProvider( 334 | { 335 | language: "rescript", 336 | }, 337 | { 338 | async provideCodeActions(document, range, _context, _token) { 339 | const cwd = findProjectPackageJsonRoot(document.fileName); 340 | const contentInFile = edgeqlContent.get(document.fileName) ?? []; 341 | const waitingForPaste = waitingForPastes.get(document.fileName); 342 | 343 | const results: (CodeAction | Command)[] = []; 344 | 345 | // Check for active pastes 346 | if (waitingForPaste != null) { 347 | const startPos = new Position( 348 | waitingForPaste.start.line, 349 | waitingForPaste.start.col 350 | ); 351 | 352 | const endPos = new Position( 353 | waitingForPaste.end.line, 354 | waitingForPaste.end.col 355 | ); 356 | 357 | const targetToPaste = contentInFile.find((c) => { 358 | const start = new Position(c.start.line, c.start.col); 359 | const end = new Position(c.end.line, c.end.col); 360 | return ( 361 | startPos.isAfterOrEqual(start) && endPos.isBeforeOrEqual(end) 362 | ); 363 | }); 364 | 365 | if (targetToPaste != null) { 366 | const clipboardContents = (await env.clipboard.readText()).trim(); 367 | const queryName = extractQueryName(clipboardContents); 368 | const targetToPasteContent = targetToPaste.content.trim(); 369 | 370 | if ( 371 | queryName != null && 372 | queryName === extractQueryName(targetToPasteContent) && 373 | clipboardContents !== targetToPasteContent 374 | ) { 375 | const offsetAsStr = Array.from({ 376 | length: waitingForPaste.offset, 377 | }) 378 | .map((_) => " ") 379 | .join(""); 380 | 381 | const textToInsert = [ 382 | "", 383 | ...clipboardContents.split("\n").map((l) => offsetAsStr + l), 384 | Array.from({ 385 | length: Math.max(0, waitingForPaste.offset - 2), 386 | }) 387 | .map((_) => " ") 388 | .join(""), 389 | ].join("\n"); 390 | 391 | results.push({ 392 | title: `Insert modified EdgeQL query "${queryName}"`, 393 | command: "vscode-rescript-edgedb-paste-edited-query", 394 | arguments: [ 395 | document.uri.toString(), 396 | targetToPaste.start, 397 | targetToPaste.end, 398 | textToInsert, 399 | document.fileName, 400 | ], 401 | }); 402 | } 403 | } 404 | } 405 | 406 | const targetWithCursor = contentInFile.find((c) => { 407 | const start = new Position(c.start.line, c.start.col); 408 | const end = new Position(c.end.line, c.end.col); 409 | return ( 410 | range.start.isAfterOrEqual(start) && 411 | range.end.isBeforeOrEqual(end) 412 | ); 413 | }); 414 | 415 | if (targetWithCursor != null && cwd != null) { 416 | results.push({ 417 | title: 418 | "Open EdgeDB UI query editor (+ copy query to the clipboard)", 419 | command: "vscode-rescript-edgedb-open-ui", 420 | arguments: [ 421 | targetWithCursor.content, 422 | cwd, 423 | document.fileName, 424 | targetWithCursor.start, 425 | targetWithCursor.end, 426 | true, 427 | ], 428 | tooltip: "The EdgeQL query will be copied to the clipboard.", 429 | }); 430 | results.push({ 431 | title: 432 | "Open EdgeDB UI query editor (without copying the query to the clipboard)", 433 | command: "vscode-rescript-edgedb-open-ui", 434 | arguments: [ 435 | targetWithCursor.content, 436 | cwd, 437 | document.fileName, 438 | targetWithCursor.start, 439 | targetWithCursor.end, 440 | false, 441 | ], 442 | }); 443 | } 444 | 445 | return results; 446 | }, 447 | } 448 | ) 449 | ); 450 | } 451 | 452 | export function deactivate() {} 453 | -------------------------------------------------------------------------------- /vscode-extension/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "esnext", 5 | "outDir": "build", 6 | "moduleResolution": "node", 7 | "lib": ["dom", "es2017", "esnext"], 8 | "types": ["node", "jest"], 9 | "sourceMap": false, 10 | "esModuleInterop": true, 11 | "removeComments": true, 12 | "noImplicitAny": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "strict": true 16 | }, 17 | "include": ["src"] 18 | } 19 | --------------------------------------------------------------------------------