@techiev2/vajra 1.2.0 → 1.3.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/examples/api.js CHANGED
@@ -5,26 +5,39 @@ async function getUsers(query = {}) {
5
5
  return (await fetch(`https://jsonplaceholder.typicode.com/users?${encode(query)}`)).json()
6
6
  }
7
7
 
8
- const { get, post, use, start, setProperty, log } = Vajra.create();
8
+ const { get, post, put, use, start, setProperty, log } = Vajra.create();
9
9
 
10
10
  setProperty({ viewRoot: `${import.meta.dirname}/views` })
11
11
 
12
- use((req, res, next) => {
13
- log(`${req.method} ${req.url}`)
14
- next();
15
- });
12
+ // use((req, res, next) => {
13
+ // log(`${req.method} ${req.url}`)
14
+ // next();
15
+ // });
16
16
 
17
- get('/', (req, res) => {
17
+ get('/', ({ query = {} }, res) => {
18
+ const { version } = query
18
19
  res.cookie('session', 'abc');
19
20
  res.cookie('theme', 'dark');
20
21
  res.cookie('user', 'test');
21
- res.writeMessage('Hello from Vajra ⚡');
22
+ res.writeMessage(version ? `Hello from Vajra (v${version}) ⚡` : 'Hello from Vajra ⚡');
23
+ });
24
+
25
+ get('/query', ({ query, params }, res) => {
26
+ return res.json({ query, params, now: new Date().getTime() })
22
27
  });
23
28
 
24
29
  post('/upload', (req, res) => {
25
30
  res.json({ received: true, filesCount: req.files.length, files: req.files, body: req.body });
26
31
  });
27
32
 
33
+ post('/post', ({ body, query, params }, res) => {
34
+ return res.json({ query, params, body, now: new Date().getTime() })
35
+ })
36
+
37
+ put('/users/:id', ({ params, query, body }, res) => {
38
+ return res.json({ params, query, body, now: new Date().getTime() })
39
+ })
40
+
28
41
  start({ port: 4002 }, () => {
29
42
  console.log('Ready at http://localhost:4002');
30
43
  });
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createServer } from 'node:http'
2
- import { readFile, access } from 'fs/promises'
2
+ import { readFile, access } from 'node:fs/promises'
3
3
 
4
4
  const BLOCK_MATCHER=/{{\s*#\s*(?<grpStart>\w+)\s*}}\s*(?<block>.*?)\s*{{\s*\/\s*(?<grpEnd>\w+)\s*}}/gmsi; const INNER_BLOCK_MATCHER = /{\s*(.*?)\s*}/gmsi
5
5
  const LOOP_MATCHER=/({\s*\w+@\s*})/gmis
@@ -88,12 +88,11 @@ export default class Vajra {
88
88
  req.cookies = Object.fromEntries((req.headers.Cookie || req.headers.cookie || '').split(/;\s*/).map((k) => k.split('=')).map(([k, v]) => [k.trim(), decodeURIComponent(v).trim()]))
89
89
  })
90
90
  async function handleRoute() {
91
- let match_; const directHandler = (Vajra.#straightRoutes[req.url] || {})[req.method.toLowerCase()]
91
+ let _url = req.url.split('?')[0]; if (_url.endsWith('/')) _url = _url.split('/').slice(0, -1).join('/')
92
+ let match_; const directHandler = (Vajra.#straightRoutes[_url] || Vajra.#straightRoutes[`${_url}/`] || {})[req.method.toLowerCase()]
92
93
  if (directHandler) { try { await directHandler(req, res); if (!res.sent && !res.writableEnded) res.end() } catch (error) { return default_500(req, res, error) }; return }
93
94
  Object.entries(Vajra.#routes).map(([route, handler]) => {
94
- if (match_) { return }
95
- if (route === '/' && route !== req.url.split('?')[0]) { return } // FIXME: The case of a bare '/' is not handled right due to the pattern addition.
96
- const match = new RegExp(route).exec(req.url); if (!!match && handler[req.method.toLowerCase()]) { match_ = handler; Object.assign(req.params, match.groups); return }
95
+ if (match_) { return }; const match = new RegExp(route).exec(req.url); if (!!match && handler[req.method.toLowerCase()]) { match_ = handler; Object.assign(req.params, match.groups); return }
97
96
  })
98
97
  if (!match_) { return default_404(req, res) }
99
98
  if (!match_[req.method.toLowerCase()]) { return default_405(req, res) }
@@ -106,13 +105,11 @@ export default class Vajra {
106
105
  const paramMatcher = /.*?(?<param>\:[a-zA-Z]{1,})/g; let pathMatcherStr = path
107
106
  path.matchAll(paramMatcher).forEach(match => pathMatcherStr = pathMatcherStr.replace(match.groups.param, `{0,1}(?<${match.groups.param.slice(1)}>\\w{1,})`))
108
107
  if (path !== '/' && pathMatcherStr.endsWith('/')) { pathMatcherStr = pathMatcherStr.replace(/(\/)$/, '/?') }
109
- /*pathMatcherStr = `^${pathMatcherStr}$`; */Vajra.#routes[pathMatcherStr] = Object.assign(Vajra.#routes[pathMatcherStr] || {}, {[method]: handler})
110
- if (!paramMatcher.exec(path)?.groups) { Vajra.#straightRoutes[pathMatcherStr] = Object.assign(Vajra.#straightRoutes[pathMatcherStr] || {}, {[method]: handler}) }
111
- return defaults
108
+ if (!paramMatcher.exec(path)?.groups) { Vajra.#straightRoutes[pathMatcherStr] = Object.assign(Vajra.#straightRoutes[pathMatcherStr] || {}, { [method]: handler }); return }
109
+ Vajra.#routes[pathMatcherStr] = Object.assign(Vajra.#routes[pathMatcherStr] || {}, {[method]: handler}); return defaults
112
110
  }
113
111
  function use(fn) {
114
- if (typeof fn !== "function") { throw new Error(`${fn} is not a function. Can't use as middleware`) }
115
- Vajra.#middlewares.push(fn); return defaults
112
+ if (typeof fn !== "function") { throw new Error(`${fn} is not a function. Can't use as middleware`) }; Vajra.#middlewares.push(fn); return defaults
116
113
  }
117
114
  const defaults = Object.freeze(Object.assign({}, { use, setProperty, start, log }, Object.fromEntries('get__post__put__patch__delete__head__options'.split('__').map((method) => [method, (...args) => register(method, ...args)])))); return Object.assign({}, { start }, defaults)
118
115
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techiev2/vajra",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Blazing-fast, zero-dependency Node.js server with routing, middleware, multipart uploads, and templating. 111 lines · ~95k req/s · ~52 MB idle.",
5
5
  "keywords": [
6
6
  "http-server",
@@ -38,6 +38,6 @@
38
38
  "test": "tests"
39
39
  },
40
40
  "scripts": {
41
- "test": "tests/tests.js"
41
+ "test": "node --watch tests/tests.js"
42
42
  }
43
43
  }
package/tests/tests.js CHANGED
@@ -1,55 +1,58 @@
1
1
  import assert from 'node:assert';
2
2
  import { encode } from 'node:querystring';
3
+ import pkg from '../package.json' with {type: 'json'}
3
4
  import { suite, test } from 'node:test';
4
5
 
5
- async function getJSON(url, method = 'GET', body) {
6
+ async function getResponse(url, method = 'GET', body) {
6
7
  return (await fetch(url, !!body ?
7
8
  { method, headers: {'Content-Type': 'application/json'}, body: JSON.stringify(body) }
8
9
  : { method, headers: {'Content-Type': 'application/json'} }
9
- )).json()
10
+ ))
11
+ }
12
+
13
+ async function getJSON(url, method = 'GET', body) {
14
+ return (await getResponse(url, method, body)).json()
10
15
  }
11
16
 
12
- suite('Test HTTP API at port 4000', () => {
13
- const BASE_URL = 'http://localhost:4000'
17
+ suite('Test HTTP API at port 4002', () => {
18
+ const BASE_URL = 'http://localhost:4002'
14
19
  suite('Test HTTP GET', () => {
15
20
  test('Basic HTTP GET to respond with a now and empty query/params', async () => {
16
- const { now, query: res_query, params: res_params } = await getJSON(BASE_URL)
17
- assert.equal(!!now, true)
18
- assert.deepEqual(res_query, {})
19
- assert.deepEqual(res_params, {})
21
+ assert.deepEqual((await (await getResponse(BASE_URL)).text()), 'Hello from Vajra ⚡')
22
+ })
23
+ test('Basic HTTP GET to respond with custom message given version query param', async () => {
24
+ const { version } = pkg
25
+ assert.deepEqual((await (await getResponse(`${BASE_URL}?${encode({ version })}`)).text()), `Hello from Vajra (v${version}) ⚡`)
20
26
  })
21
27
  test('Basic HTTP GET to respond with a now, and query params', async () => {
22
28
  const query = { id: 1, user: 'test' }
23
- const { now, query: res_query, params: res_params } = await getJSON(`${BASE_URL}?${encode(query)}`)
29
+ const { now, query: res_query, params: res_params } = await getJSON(`${BASE_URL}/query?${encode(query)}`)
24
30
  assert.equal(!!now, true)
25
31
  assert.deepEqual(res_query, query)
26
32
  assert.deepEqual(res_params, {})
27
33
  })
28
34
  })
29
35
  suite('Test HTTP POST', () => {
30
- // test('Basic HTTP POST to respond with a now and body', async () => {
31
- // const query = { id: 1, user: 'test' }
32
- // const body = { name: 'user' }
33
- // const { now, query: res_query, body: res_body } = await getJSON(`${BASE_URL}`, 'POST', body)
34
- // assert.equal(!!now, true)
35
- // assert.deepEqual(res_query, query)
36
- // assert.deepEqual(res_body, body)
37
- // })
38
- test('Basic HTTP POST with a param shoudl return a 404', async () => {
39
- // const { now, query: res_query, body: res_body } =
36
+ test('Basic HTTP POST to respond with a now and body', async () => {
37
+ const body = { name: 'user' }
38
+ const json = await getJSON(`${BASE_URL}/post`, 'POST', body)
39
+ const { now, body: res_body } = json
40
+ assert.equal(!!now, true)
41
+ assert.deepEqual(res_body, body)
42
+ })
43
+ test('Basic HTTP POST with a param (unmapped route) should return a 404', async (done) => {
44
+ const query = { test: 'random' }
40
45
  const body = { name: 'test' }
41
- const json = await getJSON(`${BASE_URL}/users/1?test=random`, 'POST', body)
42
- console.log(json)
43
- // assert.equal(!!now, true)
44
- // assert.deepEqual(res_query, query)
45
- // assert.deepEqual(res_body, body)
46
+ const response = await getResponse(`${BASE_URL}/post/1/?${encode(query)}`, 'POST', body)
47
+ assert.equal(response.status, 404)
46
48
  })
47
49
  })
48
50
  suite('Test HTTP PUT', () => {
49
51
  test('Basic HTTP PUT to respond with a now, query, params, and body', async () => {
50
52
  const params = { id : 1 }
51
53
  const body = { name: 'user' }
52
- const { now, params: res_params, body: res_body} = await getJSON(`${BASE_URL}/users/${params.id}`, 'PUT', body)
54
+ const json = await getJSON(`${BASE_URL}/users/${params.id}`, 'PUT', body)
55
+ const { now, params: res_params, body: res_body} = json
53
56
  assert.equal(!!now, true)
54
57
  assert.deepEqual(res_params, params)
55
58
  assert.deepEqual(res_body, body)