@cloud-cli/d0 1.0.3 → 1.2.0

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/.dockerignore ADDED
@@ -0,0 +1 @@
1
+ .github/
package/Dockerfile ADDED
@@ -0,0 +1,4 @@
1
+ FROM ghcr.io/cloud-cli/node:latest
2
+ COPY --chown=node:node . /home/app
3
+ RUN cd /home/app && npm i && npm run build && rm -r src/
4
+ ENV NODE_ENV=production
package/README.md CHANGED
@@ -7,19 +7,26 @@ SQLite server over HTTP
7
7
  **POST /query**
8
8
 
9
9
  Run a prepared SQLite statement.
10
- Send a JSON with two properties: `s` for statement and `d` for data (optional)
10
+ Accepts a JSON with these properties:
11
+
12
+ | Property | Description | Required |
13
+ |-|:-:|-|
14
+ |`s` | string with the statement | **yes** |
15
+ |`d` | data to bind on a statement | no |
16
+ |`m` | method to execute: `all`, `run` or `get`. Run is the default | no |
11
17
 
12
18
  ```js
13
- // using fetch
19
+ // select all items using fetch
14
20
  fetch('https://db.example.com/query', {
15
21
  method: 'POST',
16
22
  body: JSON.stringify({
17
23
  s: 'SELECT * FROM user WHERE id = ?',
18
- d: [123]
24
+ d: [123],
25
+ m: 'all',
19
26
  });
20
27
  });
21
28
 
22
- // using the server-provided library
29
+ // select using the server-provided library
23
30
  import db from 'https://db.example.com/index.mjs';
24
31
 
25
32
  const user = await db.query('SELECT * FROM user WHERE id = ?', [123]);
package/client.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  const baseURL = 'https://__API_URL__';
2
2
 
3
3
  export default {
4
- async query(statement, data) {
4
+ async query(statement, data, method = 'run') {
5
5
  const req = await fetch(new URL('/query', baseURL), {
6
6
  method: 'POST',
7
7
  body: JSON.stringify({
8
8
  s: statement,
9
9
  d: data || null,
10
+ m: method,
10
11
  }),
11
12
  });
12
13
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,13 @@
1
- /// <reference types="node" />
2
1
  import { IncomingMessage, ServerResponse } from 'node:http';
3
2
  import { Database } from 'better-sqlite3';
3
+ type Q = {
4
+ db: Database;
5
+ request: IncomingMessage;
6
+ response: ServerResponse;
7
+ args: Record<string, string>;
8
+ };
4
9
  export declare function getDatabase(): Database;
5
10
  export declare function serve(): import("http").Server<typeof IncomingMessage, typeof ServerResponse>;
11
+ export declare function handleRequest(request: any, response: any, db: any): Promise<void>;
12
+ export declare function onQuery({ db, request, response }: Q): Promise<void>;
13
+ export {};
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createServer } from 'node:http';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import SQLite from 'better-sqlite3';
4
+ const methods = ['all', 'run', 'get'];
4
5
  export function getDatabase() {
5
6
  const db = new SQLite(process.env.SQLITE_DB_PATH || './db.sqlite3');
6
7
  db.pragma('journal_mode = WAL');
@@ -8,33 +9,40 @@ export function getDatabase() {
8
9
  }
9
10
  export function serve() {
10
11
  const db = getDatabase();
11
- const server = createServer((request, response) => {
12
- const url = new URL(request.url, 'http://localhost');
13
- const route = `${request.method} ${url.pathname}`.trim();
14
- const args = Object.fromEntries(url.searchParams.entries());
15
- const q = { db, request, response, args };
16
- switch (route) {
17
- case 'GET /index.mjs':
18
- return onEsModule(q);
19
- case 'POST /query':
20
- return onQuery(q);
21
- default:
22
- response.writeHead(404).end();
23
- }
24
- });
12
+ const server = createServer((req, res) => handleRequest(req, res, db));
25
13
  server.listen(process.env.PORT);
26
14
  return server;
27
15
  }
28
- async function onQuery({ db, request, response }) {
29
- const query = await readStream(request);
16
+ export function handleRequest(request, response, db) {
17
+ const url = new URL(request.url, 'http://localhost');
18
+ const route = `${request.method} ${url.pathname}`.trim();
19
+ const args = Object.fromEntries(url.searchParams.entries());
20
+ const q = { db, request, response, args };
21
+ switch (route) {
22
+ case 'GET /index.mjs':
23
+ return onEsModule(q);
24
+ case 'POST /query':
25
+ return onQuery(q);
26
+ default:
27
+ response.writeHead(404).end();
28
+ }
29
+ }
30
+ export async function onQuery({ db, request, response }) {
31
+ const query = Buffer.concat(await request.toArray());
30
32
  if (!query.length) {
31
33
  response.writeHead(400).end();
32
34
  return;
33
35
  }
34
36
  try {
35
- const { s, d } = JSON.parse(query);
37
+ const { s = '', d, m = 'run' } = JSON.parse(query.toString('utf-8'));
38
+ if (!s.trim()) {
39
+ throw new Error('Invalid statement.');
40
+ }
41
+ if (!methods.includes(m)) {
42
+ throw new Error('Invalid method. Must be one of ' + methods.join(', '));
43
+ }
36
44
  const runner = db.prepare(s);
37
- const result = d ? runner.run(d) : runner.run();
45
+ const result = d ? runner[m](d) : runner[m]();
38
46
  response.end(JSON.stringify(result));
39
47
  }
40
48
  catch (error) {
@@ -59,11 +67,3 @@ async function onEsModule({ request, response }) {
59
67
  })
60
68
  .end(code.replace('__API_URL__', hostname));
61
69
  }
62
- function readStream(stream) {
63
- return new Promise((resolve, reject) => {
64
- const all = [];
65
- stream.on('data', (c) => all.push(c));
66
- stream.on('end', () => resolve(Buffer.concat(all).toString('utf8')));
67
- stream.on('error', reject);
68
- });
69
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-cli/d0",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",
@@ -18,7 +18,7 @@
18
18
  "access": "public"
19
19
  },
20
20
  "dependencies": {
21
- "better-sqlite3": "^9.4.3"
21
+ "better-sqlite3": "^11.0.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@cloud-cli/prettier-config": "^1.0.0",