graphjin 3.0.31 → 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # GraphJin, A New Kind of ORM
1
+ # GraphJin - A Compiler to Connect AI to Your Databases
2
2
 
3
3
  [![Apache 2.0](https://img.shields.io/github/license/dosco/graphjin.svg?style=for-the-badge)](https://github.com/dosco/graphjin/blob/master/LICENSE)
4
4
  [![NPM Package](https://img.shields.io/npm/v/graphjin?style=for-the-badge)](https://www.npmjs.com/package/graphjin)
@@ -7,353 +7,227 @@
7
7
  [![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/dosco/graphjin/core/v3)
8
8
  [![GoReport](https://goreportcard.com/badge/github.com/gojp/goreportcard?style=for-the-badge)](https://goreportcard.com/report/github.com/dosco/graphjin/core/v3)
9
9
 
10
- ## Build APIs in 5 minutes not weeks
10
+ Point GraphJin at any database and AI assistants can query it instantly. Auto-discovers your schema, understands relationships, compiles to optimized SQL. No configuration required.
11
11
 
12
- Just use a simple GraphQL query to define your API and GraphJin automagically converts it into SQL and fetches the data you need. Build your backend APIs **100X** faster. Works with **NodeJS** and **GO**. Supports several databases, **Postgres**, **MySQL**, **Yugabyte**, **AWS Aurora/RDS** and **Google Cloud SQL**
12
+ Works with PostgreSQL, MySQL, MongoDB, SQLite, Oracle, MSSQL - and models from Claude/GPT-4 to local 7B models.
13
13
 
14
- The following GraphQL query fetches a list of products, their owners, and other category information, including a cursor for retrieving more products.
15
- GraphJin will do auto-discovery of your database schema and relationships and generate the most efficient single SQL query to fetch all this data including a cursor to fetch the next 20 item. You don't have to do a single thing besides write the GraphQL query.
14
+ ## Installation
16
15
 
17
- ```graphql
18
- query getProducts {
19
- products(
20
- # returns only 20 items
21
- limit: 20
22
-
23
- # orders the response items by highest price
24
- order_by: { price: desc }
25
-
26
- # only items with a price >= 20 and < 50 are returned
27
- where: { price: { and: { greater_or_equals: 20, lt: 50 } } }
28
- ) {
29
- id
30
- name
31
- price
32
-
33
- # also fetch the owner of the product
34
- owner {
35
- full_name
36
- picture: avatar
37
- email
38
-
39
- # and the categories the owner has products under
40
- category_counts(limit: 3) {
41
- count
42
- category {
43
- name
44
- }
45
- }
46
- }
47
-
48
- # and the categories of the product itself
49
- category(limit: 3) {
50
- id
51
- name
52
- }
53
- }
54
- # also return a cursor that we can use to fetch the next
55
- # batch of products
56
- products_cursor
57
- }
16
+ **npm (all platforms)**
17
+ ```bash
18
+ npm install -g graphjin
58
19
  ```
59
20
 
60
- **Note**: The corresponding SQL for creating the table (POSTGRES)
61
-
62
- ```sql
63
- CREATE TABLE products (
64
- id SERIAL NOT NULL,
65
- name TEXT NOT NULL,
66
- price INTEGER NOT NULL,
67
- owner_id INTEGER NOT NULL,
68
- category_id INTEGER NOT NULL,
69
- PRIMARY KEY(id)
70
- );
71
- ```
72
-
73
- ## Secure out of the box
74
-
75
- In production all queries are always read from locally saved copies not from what the client sends hence clients cannot modify the query. This makes
76
- GraphJin very secure as its similar to building APIs by hand. The idea that GraphQL means that clients can change the query as they wish **does not** apply to GraphJin
21
+ **macOS (Homebrew)**
22
+ ```bash
23
+ brew install dosco/graphjin/graphjin
24
+ ```
77
25
 
78
- ## Great Documentation
26
+ **Windows (Scoop)**
27
+ ```bash
28
+ scoop bucket add graphjin https://github.com/dosco/graphjin-scoop
29
+ scoop install graphjin
30
+ ```
79
31
 
80
- Detailed docs on GraphQL syntax, usecases, JS and GO code examples and it's actively updated.
32
+ **Linux**
81
33
 
82
- ## [![Docs](https://img.shields.io/badge/Docs-graphjin.com-red?style=for-the-badge)](https://graphjin.com)
34
+ Download .deb/.rpm from [releases](https://github.com/dosco/graphjin/releases)
83
35
 
84
- ## [![Example Code](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge&logo=go&label=Example+Code)](https://pkg.go.dev/github.com/dosco/graphjin/tests/v3)
36
+ **Docker**
37
+ ```bash
38
+ docker pull dosco/graphjin
39
+ ```
85
40
 
86
- ## Use with NodeJS
41
+ ## Try It Now
87
42
 
88
- GraphJin allows you to use GraphQL and the full power of GraphJin to access to create instant APIs without writing and maintaining lines and lines of database code. GraphJin NodeJS currently only supports Postgres compatible databases working on adding MySQL support as well. Example app in [/examples/nodejs](https://github.com/dosco/graphjin/tree/master/examples/nodejs)
43
+ ```bash
44
+ # With Claude Desktop - run the demo
45
+ graphjin mcp demo --path examples/webshop/config
46
+ graphjin mcp demo info # Copy output to Claude Desktop config
89
47
 
90
- ```console
91
- npm install graphjin
48
+ # With your own database
49
+ graphjin mcp --path /path/to/config
50
+ graphjin mcp info # Copy output to Claude Desktop config
92
51
  ```
93
52
 
94
- ```javascript
95
- import graphjin from "graphjin";
96
- import express from "express";
97
- import http from "http";
98
- import pg from "pg";
99
-
100
- const { Client } = pg;
101
- const db = new Client({
102
- host: "localhost",
103
- port: 5432,
104
- user: "postgres",
105
- password: "postgres",
106
- database: "appdb-development",
107
- });
108
-
109
- await db.connect();
110
-
111
- // config can either be a file (eg. `dev.yml`) or an object
112
- // const config = { production: true, default_limit: 50 };
113
-
114
- var gj = await graphjin("./config", "dev.yml", db);
115
- var app = express();
116
- var server = http.createServer(app);
117
-
118
- // subscriptions allow you to have a callback function triggerd
119
- // automatically when data in your database changes
120
- const res1 = await gj.subscribe(
121
- "subscription getUpdatedUser { users(id: $userID) { id email } }",
122
- null,
123
- { userID: 2 }
124
- );
125
-
126
- res1.data(function (res) {
127
- console.log(">", res.data());
128
- });
129
-
130
- // queries allow you to use graphql to query and update your database
131
- app.get("/", async function (req, resp) {
132
- const res2 = await gj.query(
133
- "query getUser { users(id: $id) { id email } }",
134
- { id: 1 },
135
- { userID: 1 }
136
- );
137
-
138
- resp.send(res2.data());
139
- });
140
-
141
- server.listen(3000);
142
- console.log("Express server started on port %s", server.address().port);
143
- ```
53
+ Within minutes, ask Claude: "What products do we have?" or "Show me orders from last week"
144
54
 
145
- ## Use with GO
55
+ ## How It Works
146
56
 
147
- You can use GraphJin as a library within your own code. The [serv](https://pkg.go.dev/github.com/dosco/graphjin/serv/v3) package exposes the entirely GraphJin standlone service as a library while the [core](https://pkg.go.dev/github.com/dosco/graphjin/core/v3) package exposes just the GraphJin compiler. The [Go docs](https://pkg.go.dev/github.com/dosco/graphjin/tests/v3#pkg-examples) are filled with examples on how to use GraphJin within your own apps as a sort of alternative to using ORM packages. GraphJin allows you to use GraphQL and the full power of GraphJin to access your data instead of a limiting ORM.
57
+ 1. **Connects to database** - Reads your schema automatically
58
+ 2. **Discovers relationships** - Foreign keys become navigable joins
59
+ 3. **Exposes MCP tools** - Teach any LLM the query syntax
60
+ 4. **Compiles to SQL** - Every request becomes a single optimized query
148
61
 
149
- ### Use GraphJin Core
62
+ No resolvers. No ORM. No N+1 queries. Just point and query.
150
63
 
151
- ```console
152
- go get github.com/dosco/graphjin/core/v3
153
- ```
64
+ ## What AI Can Do
154
65
 
155
- ```golang
156
- package main
157
-
158
- import (
159
- "context"
160
- "database/sql"
161
- "log"
162
- "net/http"
163
-
164
- "github.com/dosco/graphjin/core"
165
- "github.com/go-chi/chi/v5"
166
- _ "github.com/jackc/pgx/v5/stdlib"
167
- )
168
-
169
- func main() {
170
- db, err := sql.Open("pgx", "postgres://postgres:@localhost:5432/exampledb?sslmode=disable")
171
- if err != nil {
172
- log.Fatal(err)
173
- }
174
-
175
- gj, err := core.NewGraphJin(nil, db)
176
- if err != nil {
177
- log.Fatal(err)
178
- }
179
-
180
- query := `
181
- query getPosts {
182
- posts {
183
- id
184
- title
185
- }
186
- posts_cursor
187
- }`
188
-
189
- router := chi.NewRouter()
190
- router.Get("/", func(w http.ResponseWriter, request *http.Request) {
191
- context := context.WithValue(request.Context(), core.UserIDKey, 1)
192
- res, err := gj.GraphQL(context, query, nil, nil)
193
- if err != nil {
194
- log.Fatal(err)
195
- return
196
- }
197
- w.Write(res.Data)
198
- })
199
-
200
- log.Println("Go server started on port 3000")
201
- http.ListenAndServe(":3000", router)
66
+ **Simple queries with filters:**
67
+ ```graphql
68
+ { products(where: { price: { gt: 50 } }, limit: 10) { id name price } }
69
+ ```
202
70
 
71
+ **Nested relationships:**
72
+ ```graphql
73
+ {
74
+ orders(limit: 5) {
75
+ id total
76
+ customer { name email }
77
+ items { quantity product { name category { name } } }
78
+ }
203
79
  }
204
-
205
80
  ```
206
81
 
207
- ### Use GraphJin Service
208
-
209
- ```golang
210
- import (
211
- "github.com/dosco/graphjin/serv/v2"
212
- )
82
+ **Aggregations:**
83
+ ```graphql
84
+ { products { count_id sum_price avg_price } }
85
+ ```
213
86
 
214
- gj, err := serv.NewGraphJinService(conf, opt...)
215
- if err != nil {
216
- return err
87
+ **Mutations:**
88
+ ```graphql
89
+ mutation {
90
+ products(insert: { name: "New Product", price: 29.99 }) { id }
217
91
  }
92
+ ```
218
93
 
219
- if err := gj.Start(); err != nil {
220
- return err
94
+ **Spatial queries:**
95
+ ```graphql
96
+ {
97
+ stores(where: { location: { st_dwithin: { point: [-122.4, 37.7], distance: 1000 } } }) {
98
+ name address
99
+ }
221
100
  }
222
-
223
- // if err := gj.Attach(chiRouter); err != nil {
224
- // return err
225
- // }
226
101
  ```
227
102
 
228
- ## Standalone Service
229
-
230
- ### Quick install
103
+ ## Real-time Subscriptions
231
104
 
232
- ```
233
- # Mac (Homebrew)
234
- brew install dosco/graphjin/graphjin
105
+ Get live updates when your data changes. GraphJin handles thousands of concurrent subscribers with a single database query - not one per subscriber.
235
106
 
236
- # Ubuntu (Snap)
237
- sudo snap install --classic graphjin
107
+ ```graphql
108
+ subscription {
109
+ orders(where: { user_id: { eq: $user_id } }) {
110
+ id total status
111
+ items { product { name } }
112
+ }
113
+ }
238
114
  ```
239
115
 
240
- Debian and Redhat ([releases](https://github.com/dosco/graphjin/releases))
241
- Download the .deb or .rpm from the releases page and install with dpkg -i and rpm -i respectively.
116
+ **Why it's efficient:**
117
+ - Traditional approach: 1,000 subscribers = 1,000 database queries
118
+ - GraphJin: 1,000 subscribers = 1 optimized batch query
119
+ - Automatic change detection - updates only sent when data actually changes
120
+ - Built-in cursor pagination for feeds and infinite scroll
242
121
 
243
- ### Quickly create and deploy new apps
122
+ Works from Node.js, Go, or any WebSocket client.
244
123
 
245
- ```bash
246
- graphjin new <app_name>
247
- ```
124
+ ## MCP Tools
248
125
 
249
- ### Instantly deploy new versions
126
+ GraphJin exposes 15 tools that guide AI models to write valid queries. Key tools: `list_tables` and `describe_table` for schema discovery, `get_query_syntax` for learning the DSL, `execute_graphql` for running queries, and `execute_saved_query` for production-approved queries. Prompts like `write_query` and `fix_query_error` help models construct and debug queries.
250
127
 
251
- ```bash
252
- # Deploy a new config
253
- graphjin deploy --host=https://your-server.com --secret="your-secret-key"
128
+ ## Database Support
254
129
 
255
- # Rollback the last deployment
256
- graphjin deploy rollback --host=https://your-server.com --secret="your-secret-key"
257
- ```
130
+ | Database | Queries | Mutations | Subscriptions | Full-Text | GIS |
131
+ |----------|---------|-----------|---------------|-----------|-----|
132
+ | PostgreSQL | Yes | Yes | Yes | Yes | PostGIS |
133
+ | MySQL | Yes | Yes | Yes | Yes | 8.0+ |
134
+ | MariaDB | Yes | Yes | Yes | Yes | Yes |
135
+ | MSSQL | Yes | Yes | Yes | No | Yes |
136
+ | Oracle | Yes | Yes | Yes | No | Yes |
137
+ | SQLite | Yes | Yes | Yes | FTS5 | SpatiaLite |
138
+ | MongoDB | Yes | Yes | Yes | Yes | Yes |
139
+ | CockroachDB | Yes | Yes | Yes | Yes | No |
258
140
 
259
- ### Secrets Management
141
+ Also works with AWS Aurora/RDS, Google Cloud SQL, and YugabyteDB.
260
142
 
261
- ```bash
262
- # Secure save secrets like database passwords and JWT secret keys
263
- graphjin secrets
264
- ```
143
+ ## Production Security
265
144
 
266
- ### Database Management
145
+ **Query allow-lists** - In production, only saved queries can run. AI models call `execute_saved_query` with pre-approved queries. No arbitrary SQL injection possible.
267
146
 
268
- ```bash
269
- # Create, Migrate and Seed your database
270
- graphjin db
147
+ **Role-based access** - Different roles see different data:
148
+ ```yaml
149
+ roles:
150
+ user:
151
+ tables:
152
+ - name: orders
153
+ query:
154
+ filters: ["{ user_id: { eq: $user_id } }"]
271
155
  ```
272
156
 
273
- ## Built in Web-UI to help craft GraphQL queries
274
-
275
- ![graphjin-screenshot-final](https://user-images.githubusercontent.com/832235/108806955-1c363180-7571-11eb-8bfa-488ece2e51ae.png)
276
-
277
- ## Support the Project
157
+ **JWT authentication** - Supports Auth0, Firebase, JWKS endpoints.
278
158
 
279
- GraphJin is an open source project made possible by the support of awesome backers. It has collectively saved teams 1000's of hours dev. time and allowing them to focus on their product and be 100x more productive. If your team uses it please consider becoming a sponsor.
159
+ **Response caching** - Redis with in-memory fallback. Automatic cache invalidation.
280
160
 
281
- <div float="left">
282
- <a href="https://42papers.com">
283
- <img src="https://user-images.githubusercontent.com/832235/135753560-39e34be6-5734-440a-98e7-f7e160c2efb5.png" width="75" target="_blank">
284
- </a>
285
- <a href="https://www.exo.com.ar/">
286
- <img src="https://user-images.githubusercontent.com/832235/112428182-259def80-8d11-11eb-88b8-ccef9206b535.png" width="100" target="_blank">
287
- </a>
288
- </div>
161
+ ## CLI Reference
289
162
 
290
- ## About GraphJin
163
+ ```bash
164
+ # Run MCP server (stdio mode for Claude Desktop)
165
+ graphjin mcp --path /path/to/config
291
166
 
292
- After working on several products through my career I found that we spend way too much time on building API backends. Most APIs also need constant updating, and this costs time and money.
167
+ # Show Claude Desktop config JSON
168
+ graphjin mcp info
293
169
 
294
- It's always the same thing, figure out what the UI needs then build an endpoint for it. Most API code involves struggling with an ORM to query a database and mangle the data into a shape that the UI expects to see.
170
+ # Run with temporary database container
171
+ graphjin mcp demo --path examples/webshop/config
295
172
 
296
- I didn't want to write this code anymore, I wanted the computer to do it. Enter GraphQL, to me it sounded great, but it still required me to write all the same database query code.
173
+ # Show demo mode config JSON
174
+ graphjin mcp demo info
297
175
 
298
- Having worked with compilers before I saw this as a compiler problem. Why not build a compiler that converts GraphQL to highly efficient SQL.
176
+ # Keep data between restarts
177
+ graphjin mcp demo --persist
299
178
 
300
- This compiler is what sits at the heart of GraphJin, with layers of useful functionality around it like authentication, remote joins, rails integration, database migrations, and everything else needed for you to build production-ready apps with it.
179
+ # Override database type
180
+ graphjin mcp demo --db mysql
301
181
 
302
- ## Better APIs Faster!
182
+ # Set auth context
183
+ graphjin mcp --user-id admin --user-role admin
184
+ ```
303
185
 
304
- Lets take for example a simple blog app. You'll probably need the following APIs user management, posts, comments, votes. Each of these areas need apis for listing, creating, updating, deleting. Off the top of my head thats like 12 APIs if not more. This is just for managing things for rendering the blog posts, home page, profile page you probably need many more view apis that fetch a whole bunch of things at the same time. This is a lot and we're still talking something simple like a basic blogging app. All these APIs have to be coded up by someone and then the code maintained, updated, made secure, fast, etc. We are talking weeks to months of work if not more. Also remember your mobile and web developers have to wait around till this is all done.
186
+ **Claude Desktop config** (`claude_desktop_config.json`):
187
+ ```json
188
+ {
189
+ "mcpServers": {
190
+ "my-database": {
191
+ "command": "graphjin",
192
+ "args": ["mcp", "--path", "/path/to/config"],
193
+ }
194
+ }
195
+ }
196
+ ```
305
197
 
306
- With GraphJin your web and mobile developers can start building instantly. All they have to do is just build the GraphQL queries they need and GraphJin fetches the data. Nothing to maintain no backend API code, its secure, lighting fast and has tons of useful features like subscriptions, rate limiting, etc built-in. With GraphJin your building APIs in minutes not days.
198
+ ## Also a GraphQL API
307
199
 
308
- ## Highlevel
200
+ GraphJin works as a traditional API too - use it from Go or as a standalone service.
309
201
 
310
- - Works with Postgres, MySQL8, YugabyteDB
311
- - Also works with Amazon Aurora/RDS and Google Cloud SQL
312
- - Supports REST, GraphQL and Websocket APIs
202
+ ### Go
203
+ ```bash
204
+ go get github.com/dosco/graphjin/core/v3
205
+ ```
206
+ ```go
207
+ db, _ := sql.Open("pgx", "postgres://localhost/myapp")
208
+ gj, _ := core.NewGraphJin(nil, db)
209
+ res, _ := gj.GraphQL(ctx, `{ users { id email } }`, nil, nil)
210
+ ```
313
211
 
314
- ## More Features
212
+ ### Standalone Service
213
+ ```bash
214
+ brew install dosco/graphjin/graphjin # Mac
215
+ graphjin new myapp && cd myapp
216
+ graphjin serve
217
+ ```
315
218
 
316
- - Complex nested queries and mutations
317
- - Realtime updates with subscriptions
318
- - Add custom business logic in Javascript
319
- - Build infinite scroll, feeds, nested comments, etc
320
- - Add data validations on insert or update
321
- - Auto learns database tables and relationships
322
- - Role and Attribute-based access control
323
- - Cursor-based efficient pagination
324
- - Full-text search and aggregations
325
- - Automatic persisted queries
326
- - JWT tokens supported (Auth0, JWKS, Firebase, etc)
327
- - Join database queries with remote REST APIs
328
- - Also works with existing Ruby-On-Rails apps
329
- - Rails authentication supported (Redis, Memcache, Cookie)
330
- - A simple config file
331
- - High performance Go codebase
332
- - Tiny docker image and low memory requirements
333
- - Fuzz tested for security
334
- - Database migrations tool
335
- - Database seeding tool
336
- - OpenCensus Support: Zipkin, Prometheus, X-Ray, Stackdriver
337
- - API Rate Limiting
338
- - Highly scalable and fast
339
- - Instant Hot-deploy and rollbacks
340
- - Add Custom resolvers
219
+ Built-in web UI at `http://localhost:8080` for query development.
341
220
 
342
221
  ## Documentation
343
222
 
344
- [Quick Start](https://graphjin.com/posts/start)
345
-
346
- [Documentation](https://graphjin.com)
347
-
348
- [GraphJin GO Examples](https://pkg.go.dev/github.com/dosco/graphjin/core#pkg-examples)
349
-
350
- ## Reach out
351
-
352
- We're happy to help you leverage GraphJin reach out if you have questions
223
+ - [Quick Start](https://graphjin.com/posts/start)
224
+ - [Full Documentation](https://graphjin.com)
225
+ - [Feature Reference](docs/FEATURES.md)
226
+ - [Go Examples](https://pkg.go.dev/github.com/dosco/graphjin/core#pkg-examples)
353
227
 
354
- [twitter/dosco](https://twitter.com/dosco)
228
+ ## Get in Touch
355
229
 
356
- [discord/graphjin](https://discord.gg/6pSWCTZ) (Chat)
230
+ [Twitter @dosco](https://twitter.com/dosco) | [Discord](https://discord.gg/6pSWCTZ)
357
231
 
358
232
  ## License
359
233
 
package/package.json CHANGED
@@ -1,27 +1,29 @@
1
1
  {
2
2
  "name": "graphjin",
3
- "version": "3.0.31",
4
- "description": "GraphJin - Build APIs in 5 minutes with GraphQL",
5
- "type": "module",
6
- "main": "./wasm/js/graphjin.js",
3
+ "version": "3.2.3",
4
+ "description": "GraphJin CLI - Build APIs in 5 minutes with GraphQL",
5
+ "bin": {
6
+ "graphjin": "bin/graphjin.js"
7
+ },
7
8
  "files": [
8
- "./wasm/config",
9
- "./wasm/runtime",
10
- "./wasm/graphjin.wasm",
11
- "./wasm/js/*.js"
9
+ "./bin"
12
10
  ],
13
11
  "scripts": {
14
- "postinstall": "node ./wasm/js/install.js"
12
+ "postinstall": "node ./bin/install.js"
15
13
  },
16
14
  "repository": {
17
15
  "type": "git",
18
- "url": "https://github.com/dosco/graphjin.git"
16
+ "url": "git+https://github.com/dosco/graphjin.git"
19
17
  },
20
18
  "keywords": [
19
+ "graphql",
20
+ "sql",
21
+ "database",
22
+ "api",
23
+ "cli",
21
24
  "postgres",
22
25
  "mysql",
23
- "graphql",
24
- "sql"
26
+ "mcp"
25
27
  ],
26
28
  "author": "Vikram <https://twitter.com/dosco>",
27
29
  "license": "Apache-2.0",
@@ -30,6 +32,6 @@
30
32
  },
31
33
  "homepage": "https://graphjin.com",
32
34
  "dependencies": {
33
- "fs-extra": "^11.1.0"
35
+ "tar": "^7.0.0"
34
36
  }
35
37
  }
@@ -1,128 +0,0 @@
1
- # When production mode is 'true' only queries
2
- # from the allow list are permitted.
3
- production: false
4
-
5
- # Secret key for general encryption operations like
6
- # encrypting the cursor data
7
- secret_key: supercalifajalistics
8
-
9
- # Subscriptions poll the database to query for updates
10
- # this sets the duration (in seconds) between requests.
11
- # Defaults to 5 seconds
12
- subs_poll_every_seconds: 5
13
-
14
- # Default limit value to be used on queries and as the max
15
- # limit on all queries where a limit is defined as a query variable.
16
- # Defaults to 20
17
- default_limit: 20
18
-
19
- # Disables all aggregation functions like count, sum, etc
20
- # disable_agg_functions: false
21
-
22
- # Disables all functions like count, length, etc
23
- # disable_functions: false
24
-
25
- # Enables using camel case terms in GraphQL which are converted
26
- # to snake case in SQL
27
- # enable_camelcase: false
28
-
29
- # Set session variable "user.id" to the user id
30
- # Enable this if you need the user id in triggers, etc
31
- # Note: This will not work with subscriptions
32
- set_user_id: false
33
-
34
- # DefaultBlock ensures that in anonymous mode (role 'anon') all tables
35
- # are blocked from queries and mutations. To open access to tables in
36
- # anonymous mode they have to be added to the 'anon' role config.
37
- default_block: false
38
-
39
- # Define additional variables here to be used with filters
40
- # Variables used require a type suffix eg. $user_id:bigint
41
- variables:
42
- #admin_account_id: "5"
43
- admin_account_id: "sql:select id from users where admin = true limit 1"
44
-
45
- # Define variables set to values extracted from http headers
46
- header_variables:
47
- remote_ip: "X-Forwarded-For"
48
-
49
- # Field and table names that you wish to block
50
- blocklist:
51
- - ar_internal_metadata
52
- - schema_migrations
53
- - secret
54
- - password
55
- - encrypted
56
- - token
57
-
58
- # resolvers:
59
- # - name: payments
60
- # type: remote_api
61
- # table: customers
62
- # column: stripe_id
63
- # json_path: data
64
- # debug: false
65
- # url: http://payments/payments/$id
66
- # pass_headers:
67
- # - cookie
68
- # set_headers:
69
- # - name: Host
70
- # value: 0.0.0.0
71
- # # - name: Authorization
72
- # # value: Bearer <stripe_api_key>
73
-
74
- tables:
75
- - # You can create new fields that have a
76
- # real db table backing them
77
- name: me
78
- table: users
79
-
80
- - name: users
81
- order_by:
82
- new_users: ["created_at desc", "id asc"]
83
- id: ["id asc"]
84
-
85
- # Variables used require a type suffix eg. $user_id:bigint
86
- #roles_query: "SELECT * FROM users WHERE id = $user_id:bigint"
87
-
88
- roles:
89
- # if `auth.type` is set to a valid auth type then
90
- # all tables are blocked for the anon role unless
91
- # added to the role like below.
92
- # - name: anon
93
- # tables:
94
- # - name: users
95
- # query:
96
- # limit: 10
97
-
98
- - name: user
99
- tables:
100
- - name: me
101
- query:
102
- filters: ["{ id: { _eq: $user_id } }"]
103
-
104
- # - name: products
105
- # query:
106
- # limit: 50
107
- # filters: ["{ user_id: { eq: $user_id } }"]
108
- # disable_functions: false
109
-
110
- # insert:
111
- # filters: ["{ user_id: { eq: $user_id } }"]
112
- # presets:
113
- # - user_id: "$user_id"
114
- # - created_at: "now"
115
-
116
- # update:
117
- # filters: ["{ user_id: { eq: $user_id } }"]
118
- # presets:
119
- # - updated_at: "now"
120
-
121
- # delete:
122
- # block: true
123
-
124
- # - name: admin
125
- # match: id = 1000
126
- # tables:
127
- # - name: users
128
- # filters: []
@@ -1,21 +0,0 @@
1
- # Inherit config from this other config file
2
- # so I only need to overwrite some values
3
- inherits: dev
4
-
5
- # When production mode is 'true' only queries from the queries
6
- # folder can be used.
7
- production: true
8
-
9
- # Secret key for general encryption operations like
10
- # encrypting the cursor data
11
- secret_key: supercalifajalistics
12
-
13
- # Subscriptions poll the database to query for updates
14
- # this sets the duration (in seconds) between requests.
15
- # Defaults to 5 seconds
16
- subs_poll_every_seconds: 5
17
-
18
- # Default limit value to be used on queries and as the max
19
- # limit on all queries where a limit is defined as a query variable.
20
- # Defaults to 20
21
- default_limit: 20
Binary file
@@ -1,12 +0,0 @@
1
- import crypto from "crypto"
2
-
3
- if (typeof globalThis.crypto === 'undefined') {
4
- Object.defineProperty(globalThis, 'crypto', {
5
- value: {
6
- getRandomValues: function(buf) { return crypto.randomFillSync(buf);}
7
- },
8
- enumerable: false,
9
- configurable: true,
10
- writable: true,
11
- });
12
- }
@@ -1,27 +0,0 @@
1
- import "./globals.js"
2
- import * as _ from "./wasm_exec.js";
3
-
4
- import fs from "fs"
5
-
6
- import { fileURLToPath } from 'url';
7
- import { dirname, join } from 'path';
8
-
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = dirname(__filename);
11
-
12
- const go = new Go();
13
- const f = fs.readFileSync(join(__dirname,"../graphjin.wasm"));
14
- const inst = await WebAssembly.instantiate(f, go.importObject);
15
- go.run(inst.instance);
16
-
17
- // TODO: Use NODE_ENV to set production mode
18
-
19
- export default async function(configPath, config, db) {
20
- if (typeof config === 'string') {
21
- const conf = {value: config, isFile: true}
22
- return await createGraphJin(configPath, conf, db, fs)
23
- } else {
24
- const conf = {value: JSON.stringify(config), isFile: false}
25
- return await createGraphJin(configPath, conf, db, fs)
26
- }
27
- }
@@ -1,26 +0,0 @@
1
- import fs from "fs-extra";
2
-
3
- import { fileURLToPath } from "url";
4
- import { basename, dirname, join } from "path";
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = dirname(__filename);
8
-
9
- const src = join(__dirname, "../config");
10
- const dst = process.env.INIT_CWD;
11
-
12
- const newDst = join(dst, basename(src));
13
-
14
- const opt = {
15
- overwrite: false,
16
- errorOnExist: false,
17
- dereference: true,
18
- };
19
-
20
- try {
21
- await fs.emptyDir(newDst);
22
- await fs.copy(src, newDst, opt);
23
- } catch (err) {
24
- console.error(err);
25
- process.exit(1);
26
- }
@@ -1,561 +0,0 @@
1
- // Copyright 2018 The Go Authors. All rights reserved.
2
- // Use of this source code is governed by a BSD-style
3
- // license that can be found in the LICENSE file.
4
-
5
- "use strict";
6
-
7
- (() => {
8
- const enosys = () => {
9
- const err = new Error("not implemented");
10
- err.code = "ENOSYS";
11
- return err;
12
- };
13
-
14
- if (!globalThis.fs) {
15
- let outputBuf = "";
16
- globalThis.fs = {
17
- constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
18
- writeSync(fd, buf) {
19
- outputBuf += decoder.decode(buf);
20
- const nl = outputBuf.lastIndexOf("\n");
21
- if (nl != -1) {
22
- console.log(outputBuf.substring(0, nl));
23
- outputBuf = outputBuf.substring(nl + 1);
24
- }
25
- return buf.length;
26
- },
27
- write(fd, buf, offset, length, position, callback) {
28
- if (offset !== 0 || length !== buf.length || position !== null) {
29
- callback(enosys());
30
- return;
31
- }
32
- const n = this.writeSync(fd, buf);
33
- callback(null, n);
34
- },
35
- chmod(path, mode, callback) { callback(enosys()); },
36
- chown(path, uid, gid, callback) { callback(enosys()); },
37
- close(fd, callback) { callback(enosys()); },
38
- fchmod(fd, mode, callback) { callback(enosys()); },
39
- fchown(fd, uid, gid, callback) { callback(enosys()); },
40
- fstat(fd, callback) { callback(enosys()); },
41
- fsync(fd, callback) { callback(null); },
42
- ftruncate(fd, length, callback) { callback(enosys()); },
43
- lchown(path, uid, gid, callback) { callback(enosys()); },
44
- link(path, link, callback) { callback(enosys()); },
45
- lstat(path, callback) { callback(enosys()); },
46
- mkdir(path, perm, callback) { callback(enosys()); },
47
- open(path, flags, mode, callback) { callback(enosys()); },
48
- read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
49
- readdir(path, callback) { callback(enosys()); },
50
- readlink(path, callback) { callback(enosys()); },
51
- rename(from, to, callback) { callback(enosys()); },
52
- rmdir(path, callback) { callback(enosys()); },
53
- stat(path, callback) { callback(enosys()); },
54
- symlink(path, link, callback) { callback(enosys()); },
55
- truncate(path, length, callback) { callback(enosys()); },
56
- unlink(path, callback) { callback(enosys()); },
57
- utimes(path, atime, mtime, callback) { callback(enosys()); },
58
- };
59
- }
60
-
61
- if (!globalThis.process) {
62
- globalThis.process = {
63
- getuid() { return -1; },
64
- getgid() { return -1; },
65
- geteuid() { return -1; },
66
- getegid() { return -1; },
67
- getgroups() { throw enosys(); },
68
- pid: -1,
69
- ppid: -1,
70
- umask() { throw enosys(); },
71
- cwd() { throw enosys(); },
72
- chdir() { throw enosys(); },
73
- }
74
- }
75
-
76
- if (!globalThis.crypto) {
77
- throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
78
- }
79
-
80
- if (!globalThis.performance) {
81
- throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
82
- }
83
-
84
- if (!globalThis.TextEncoder) {
85
- throw new Error("globalThis.TextEncoder is not available, polyfill required");
86
- }
87
-
88
- if (!globalThis.TextDecoder) {
89
- throw new Error("globalThis.TextDecoder is not available, polyfill required");
90
- }
91
-
92
- const encoder = new TextEncoder("utf-8");
93
- const decoder = new TextDecoder("utf-8");
94
-
95
- globalThis.Go = class {
96
- constructor() {
97
- this.argv = ["js"];
98
- this.env = {};
99
- this.exit = (code) => {
100
- if (code !== 0) {
101
- console.warn("exit code:", code);
102
- }
103
- };
104
- this._exitPromise = new Promise((resolve) => {
105
- this._resolveExitPromise = resolve;
106
- });
107
- this._pendingEvent = null;
108
- this._scheduledTimeouts = new Map();
109
- this._nextCallbackTimeoutID = 1;
110
-
111
- const setInt64 = (addr, v) => {
112
- this.mem.setUint32(addr + 0, v, true);
113
- this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
114
- }
115
-
116
- const setInt32 = (addr, v) => {
117
- this.mem.setUint32(addr + 0, v, true);
118
- }
119
-
120
- const getInt64 = (addr) => {
121
- const low = this.mem.getUint32(addr + 0, true);
122
- const high = this.mem.getInt32(addr + 4, true);
123
- return low + high * 4294967296;
124
- }
125
-
126
- const loadValue = (addr) => {
127
- const f = this.mem.getFloat64(addr, true);
128
- if (f === 0) {
129
- return undefined;
130
- }
131
- if (!isNaN(f)) {
132
- return f;
133
- }
134
-
135
- const id = this.mem.getUint32(addr, true);
136
- return this._values[id];
137
- }
138
-
139
- const storeValue = (addr, v) => {
140
- const nanHead = 0x7FF80000;
141
-
142
- if (typeof v === "number" && v !== 0) {
143
- if (isNaN(v)) {
144
- this.mem.setUint32(addr + 4, nanHead, true);
145
- this.mem.setUint32(addr, 0, true);
146
- return;
147
- }
148
- this.mem.setFloat64(addr, v, true);
149
- return;
150
- }
151
-
152
- if (v === undefined) {
153
- this.mem.setFloat64(addr, 0, true);
154
- return;
155
- }
156
-
157
- let id = this._ids.get(v);
158
- if (id === undefined) {
159
- id = this._idPool.pop();
160
- if (id === undefined) {
161
- id = this._values.length;
162
- }
163
- this._values[id] = v;
164
- this._goRefCounts[id] = 0;
165
- this._ids.set(v, id);
166
- }
167
- this._goRefCounts[id]++;
168
- let typeFlag = 0;
169
- switch (typeof v) {
170
- case "object":
171
- if (v !== null) {
172
- typeFlag = 1;
173
- }
174
- break;
175
- case "string":
176
- typeFlag = 2;
177
- break;
178
- case "symbol":
179
- typeFlag = 3;
180
- break;
181
- case "function":
182
- typeFlag = 4;
183
- break;
184
- }
185
- this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
186
- this.mem.setUint32(addr, id, true);
187
- }
188
-
189
- const loadSlice = (addr) => {
190
- const array = getInt64(addr + 0);
191
- const len = getInt64(addr + 8);
192
- return new Uint8Array(this._inst.exports.mem.buffer, array, len);
193
- }
194
-
195
- const loadSliceOfValues = (addr) => {
196
- const array = getInt64(addr + 0);
197
- const len = getInt64(addr + 8);
198
- const a = new Array(len);
199
- for (let i = 0; i < len; i++) {
200
- a[i] = loadValue(array + i * 8);
201
- }
202
- return a;
203
- }
204
-
205
- const loadString = (addr) => {
206
- const saddr = getInt64(addr + 0);
207
- const len = getInt64(addr + 8);
208
- return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
209
- }
210
-
211
- const timeOrigin = Date.now() - performance.now();
212
- this.importObject = {
213
- _gotest: {
214
- add: (a, b) => a + b,
215
- },
216
- gojs: {
217
- // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
218
- // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
219
- // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
220
- // This changes the SP, thus we have to update the SP used by the imported function.
221
-
222
- // func wasmExit(code int32)
223
- "runtime.wasmExit": (sp) => {
224
- sp >>>= 0;
225
- const code = this.mem.getInt32(sp + 8, true);
226
- this.exited = true;
227
- delete this._inst;
228
- delete this._values;
229
- delete this._goRefCounts;
230
- delete this._ids;
231
- delete this._idPool;
232
- this.exit(code);
233
- },
234
-
235
- // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
236
- "runtime.wasmWrite": (sp) => {
237
- sp >>>= 0;
238
- const fd = getInt64(sp + 8);
239
- const p = getInt64(sp + 16);
240
- const n = this.mem.getInt32(sp + 24, true);
241
- fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
242
- },
243
-
244
- // func resetMemoryDataView()
245
- "runtime.resetMemoryDataView": (sp) => {
246
- sp >>>= 0;
247
- this.mem = new DataView(this._inst.exports.mem.buffer);
248
- },
249
-
250
- // func nanotime1() int64
251
- "runtime.nanotime1": (sp) => {
252
- sp >>>= 0;
253
- setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
254
- },
255
-
256
- // func walltime() (sec int64, nsec int32)
257
- "runtime.walltime": (sp) => {
258
- sp >>>= 0;
259
- const msec = (new Date).getTime();
260
- setInt64(sp + 8, msec / 1000);
261
- this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
262
- },
263
-
264
- // func scheduleTimeoutEvent(delay int64) int32
265
- "runtime.scheduleTimeoutEvent": (sp) => {
266
- sp >>>= 0;
267
- const id = this._nextCallbackTimeoutID;
268
- this._nextCallbackTimeoutID++;
269
- this._scheduledTimeouts.set(id, setTimeout(
270
- () => {
271
- this._resume();
272
- while (this._scheduledTimeouts.has(id)) {
273
- // for some reason Go failed to register the timeout event, log and try again
274
- // (temporary workaround for https://github.com/golang/go/issues/28975)
275
- console.warn("scheduleTimeoutEvent: missed timeout event");
276
- this._resume();
277
- }
278
- },
279
- getInt64(sp + 8),
280
- ));
281
- this.mem.setInt32(sp + 16, id, true);
282
- },
283
-
284
- // func clearTimeoutEvent(id int32)
285
- "runtime.clearTimeoutEvent": (sp) => {
286
- sp >>>= 0;
287
- const id = this.mem.getInt32(sp + 8, true);
288
- clearTimeout(this._scheduledTimeouts.get(id));
289
- this._scheduledTimeouts.delete(id);
290
- },
291
-
292
- // func getRandomData(r []byte)
293
- "runtime.getRandomData": (sp) => {
294
- sp >>>= 0;
295
- crypto.getRandomValues(loadSlice(sp + 8));
296
- },
297
-
298
- // func finalizeRef(v ref)
299
- "syscall/js.finalizeRef": (sp) => {
300
- sp >>>= 0;
301
- const id = this.mem.getUint32(sp + 8, true);
302
- this._goRefCounts[id]--;
303
- if (this._goRefCounts[id] === 0) {
304
- const v = this._values[id];
305
- this._values[id] = null;
306
- this._ids.delete(v);
307
- this._idPool.push(id);
308
- }
309
- },
310
-
311
- // func stringVal(value string) ref
312
- "syscall/js.stringVal": (sp) => {
313
- sp >>>= 0;
314
- storeValue(sp + 24, loadString(sp + 8));
315
- },
316
-
317
- // func valueGet(v ref, p string) ref
318
- "syscall/js.valueGet": (sp) => {
319
- sp >>>= 0;
320
- const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
321
- sp = this._inst.exports.getsp() >>> 0; // see comment above
322
- storeValue(sp + 32, result);
323
- },
324
-
325
- // func valueSet(v ref, p string, x ref)
326
- "syscall/js.valueSet": (sp) => {
327
- sp >>>= 0;
328
- Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
329
- },
330
-
331
- // func valueDelete(v ref, p string)
332
- "syscall/js.valueDelete": (sp) => {
333
- sp >>>= 0;
334
- Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
335
- },
336
-
337
- // func valueIndex(v ref, i int) ref
338
- "syscall/js.valueIndex": (sp) => {
339
- sp >>>= 0;
340
- storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
341
- },
342
-
343
- // valueSetIndex(v ref, i int, x ref)
344
- "syscall/js.valueSetIndex": (sp) => {
345
- sp >>>= 0;
346
- Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
347
- },
348
-
349
- // func valueCall(v ref, m string, args []ref) (ref, bool)
350
- "syscall/js.valueCall": (sp) => {
351
- sp >>>= 0;
352
- try {
353
- const v = loadValue(sp + 8);
354
- const m = Reflect.get(v, loadString(sp + 16));
355
- const args = loadSliceOfValues(sp + 32);
356
- const result = Reflect.apply(m, v, args);
357
- sp = this._inst.exports.getsp() >>> 0; // see comment above
358
- storeValue(sp + 56, result);
359
- this.mem.setUint8(sp + 64, 1);
360
- } catch (err) {
361
- sp = this._inst.exports.getsp() >>> 0; // see comment above
362
- storeValue(sp + 56, err);
363
- this.mem.setUint8(sp + 64, 0);
364
- }
365
- },
366
-
367
- // func valueInvoke(v ref, args []ref) (ref, bool)
368
- "syscall/js.valueInvoke": (sp) => {
369
- sp >>>= 0;
370
- try {
371
- const v = loadValue(sp + 8);
372
- const args = loadSliceOfValues(sp + 16);
373
- const result = Reflect.apply(v, undefined, args);
374
- sp = this._inst.exports.getsp() >>> 0; // see comment above
375
- storeValue(sp + 40, result);
376
- this.mem.setUint8(sp + 48, 1);
377
- } catch (err) {
378
- sp = this._inst.exports.getsp() >>> 0; // see comment above
379
- storeValue(sp + 40, err);
380
- this.mem.setUint8(sp + 48, 0);
381
- }
382
- },
383
-
384
- // func valueNew(v ref, args []ref) (ref, bool)
385
- "syscall/js.valueNew": (sp) => {
386
- sp >>>= 0;
387
- try {
388
- const v = loadValue(sp + 8);
389
- const args = loadSliceOfValues(sp + 16);
390
- const result = Reflect.construct(v, args);
391
- sp = this._inst.exports.getsp() >>> 0; // see comment above
392
- storeValue(sp + 40, result);
393
- this.mem.setUint8(sp + 48, 1);
394
- } catch (err) {
395
- sp = this._inst.exports.getsp() >>> 0; // see comment above
396
- storeValue(sp + 40, err);
397
- this.mem.setUint8(sp + 48, 0);
398
- }
399
- },
400
-
401
- // func valueLength(v ref) int
402
- "syscall/js.valueLength": (sp) => {
403
- sp >>>= 0;
404
- setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
405
- },
406
-
407
- // valuePrepareString(v ref) (ref, int)
408
- "syscall/js.valuePrepareString": (sp) => {
409
- sp >>>= 0;
410
- const str = encoder.encode(String(loadValue(sp + 8)));
411
- storeValue(sp + 16, str);
412
- setInt64(sp + 24, str.length);
413
- },
414
-
415
- // valueLoadString(v ref, b []byte)
416
- "syscall/js.valueLoadString": (sp) => {
417
- sp >>>= 0;
418
- const str = loadValue(sp + 8);
419
- loadSlice(sp + 16).set(str);
420
- },
421
-
422
- // func valueInstanceOf(v ref, t ref) bool
423
- "syscall/js.valueInstanceOf": (sp) => {
424
- sp >>>= 0;
425
- this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
426
- },
427
-
428
- // func copyBytesToGo(dst []byte, src ref) (int, bool)
429
- "syscall/js.copyBytesToGo": (sp) => {
430
- sp >>>= 0;
431
- const dst = loadSlice(sp + 8);
432
- const src = loadValue(sp + 32);
433
- if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
434
- this.mem.setUint8(sp + 48, 0);
435
- return;
436
- }
437
- const toCopy = src.subarray(0, dst.length);
438
- dst.set(toCopy);
439
- setInt64(sp + 40, toCopy.length);
440
- this.mem.setUint8(sp + 48, 1);
441
- },
442
-
443
- // func copyBytesToJS(dst ref, src []byte) (int, bool)
444
- "syscall/js.copyBytesToJS": (sp) => {
445
- sp >>>= 0;
446
- const dst = loadValue(sp + 8);
447
- const src = loadSlice(sp + 16);
448
- if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
449
- this.mem.setUint8(sp + 48, 0);
450
- return;
451
- }
452
- const toCopy = src.subarray(0, dst.length);
453
- dst.set(toCopy);
454
- setInt64(sp + 40, toCopy.length);
455
- this.mem.setUint8(sp + 48, 1);
456
- },
457
-
458
- "debug": (value) => {
459
- console.log(value);
460
- },
461
- }
462
- };
463
- }
464
-
465
- async run(instance) {
466
- if (!(instance instanceof WebAssembly.Instance)) {
467
- throw new Error("Go.run: WebAssembly.Instance expected");
468
- }
469
- this._inst = instance;
470
- this.mem = new DataView(this._inst.exports.mem.buffer);
471
- this._values = [ // JS values that Go currently has references to, indexed by reference id
472
- NaN,
473
- 0,
474
- null,
475
- true,
476
- false,
477
- globalThis,
478
- this,
479
- ];
480
- this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
481
- this._ids = new Map([ // mapping from JS values to reference ids
482
- [0, 1],
483
- [null, 2],
484
- [true, 3],
485
- [false, 4],
486
- [globalThis, 5],
487
- [this, 6],
488
- ]);
489
- this._idPool = []; // unused ids that have been garbage collected
490
- this.exited = false; // whether the Go program has exited
491
-
492
- // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
493
- let offset = 4096;
494
-
495
- const strPtr = (str) => {
496
- const ptr = offset;
497
- const bytes = encoder.encode(str + "\0");
498
- new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
499
- offset += bytes.length;
500
- if (offset % 8 !== 0) {
501
- offset += 8 - (offset % 8);
502
- }
503
- return ptr;
504
- };
505
-
506
- const argc = this.argv.length;
507
-
508
- const argvPtrs = [];
509
- this.argv.forEach((arg) => {
510
- argvPtrs.push(strPtr(arg));
511
- });
512
- argvPtrs.push(0);
513
-
514
- const keys = Object.keys(this.env).sort();
515
- keys.forEach((key) => {
516
- argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
517
- });
518
- argvPtrs.push(0);
519
-
520
- const argv = offset;
521
- argvPtrs.forEach((ptr) => {
522
- this.mem.setUint32(offset, ptr, true);
523
- this.mem.setUint32(offset + 4, 0, true);
524
- offset += 8;
525
- });
526
-
527
- // The linker guarantees global data starts from at least wasmMinDataAddr.
528
- // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
529
- const wasmMinDataAddr = 4096 + 8192;
530
- if (offset >= wasmMinDataAddr) {
531
- throw new Error("total length of command line and environment variables exceeds limit");
532
- }
533
-
534
- this._inst.exports.run(argc, argv);
535
- if (this.exited) {
536
- this._resolveExitPromise();
537
- }
538
- await this._exitPromise;
539
- }
540
-
541
- _resume() {
542
- if (this.exited) {
543
- throw new Error("Go program has already exited");
544
- }
545
- this._inst.exports.resume();
546
- if (this.exited) {
547
- this._resolveExitPromise();
548
- }
549
- }
550
-
551
- _makeFuncWrapper(id) {
552
- const go = this;
553
- return function () {
554
- const event = { id: id, this: this, args: arguments };
555
- go._pendingEvent = event;
556
- go._resume();
557
- return event.result;
558
- };
559
- }
560
- }
561
- })();