Skip to content

Commit fe517c6

Browse files
author
Dmitry Dutikov
committed
Initial implementation
1 parent bc1d780 commit fe517c6

File tree

11 files changed

+5079
-0
lines changed

11 files changed

+5079
-0
lines changed

.github/workflows/npm-publish.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3+
4+
name: Node.js Package
5+
6+
on:
7+
workflow_dispatch:
8+
release:
9+
types: [created]
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v2
16+
- uses: actions/setup-node@v2
17+
with:
18+
node-version: 16
19+
- run: npm ci
20+
- run: npm test
21+
22+
publish-npm:
23+
needs: build
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v2
27+
- uses: actions/setup-node@v2
28+
with:
29+
node-version: 16
30+
registry-url: https://registry.npmjs.org/
31+
- run: npm ci
32+
- run: npm publish
33+
env:
34+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
.idea

.npmignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
test
3+
.github
4+
.gitignore

README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# SuperAgent JSON22 Plugin
2+
SuperAgent plugin providing support to [JSON22](https://github.com/dancecoder/json22#readme) data format in your applications.
3+
4+
## Features
5+
* Ready to use [SuperAgent](https://visionmedia.github.io/superagent/) plugin
6+
* Parse [JSON22](https://github.com/dancecoder/json22#readme) body content
7+
* Serialize data to JSON22
8+
* Can be used with [SuperTest](https://github.com/visionmedia/supertest#readme)
9+
* Both CJS/ESM modules support
10+
11+
## Installation
12+
```shell
13+
npm install superagent-json22
14+
```
15+
16+
Add plugin to a request
17+
```javascript
18+
import superagent from 'superagent';
19+
import { json22Plugin } from 'superagent-json22';
20+
21+
async function sendData() {
22+
try {
23+
return await superagent
24+
.post('https://example.com/api/data')
25+
.use(json22Plugin())
26+
.send(data);
27+
} catch (e) {
28+
console.error(e);
29+
}
30+
}
31+
```
32+
33+
Old-fashioned javascript imports
34+
```javascript
35+
const superagent = require('superagent');
36+
const { json22Plugin } = require('superagent-json22');
37+
```
38+
39+
## Options
40+
41+
Both stringify and parse methods of JSON22 accepts options. You may be interested to define such options at global level as well as with isolated client instance.
42+
43+
`Json22RequestInterceptor` accepts the next options structure
44+
45+
```typescript
46+
interface Json22SuperagentPluginOptions {
47+
json22ParseOptions?: Json22ParseOptions;
48+
json22StringifyOptions?: Json22StringifyOptions;
49+
}
50+
```
51+
See also `Json22ParseOptions` and `Json22StringifyOptions` at [JSON22 API description](https://github.com/dancecoder/json22#api)
52+
53+
### Define options on each request
54+
```javascript
55+
import superagent from 'superagent';
56+
import { json22Plugin } from 'superagent-json22';
57+
import { TypedModel } from './models/typed-model.js';
58+
59+
async function sendData() {
60+
try {
61+
return await superagent
62+
.post('https://example.com/api/data')
63+
.use(json22Plugin({
64+
json22ParseOptions: { context: { TypedModel } },
65+
}))
66+
.send(data);
67+
} catch (e) {
68+
console.error(e);
69+
}
70+
}
71+
```
72+
73+
### Define an agent instance plugin options
74+
```javascript
75+
import superagent from 'superagent';
76+
import { json22Plugin } from 'superagent-json22';
77+
import { TypedModel } from './models/typed-model.js';
78+
79+
const agent = superagent.agent().use(json22Plugin({
80+
json22ParseOptions: { context: { TypedModel } },
81+
}));
82+
83+
async function sendData() {
84+
try {
85+
return await agent.post('https://example.com/api/data').send(data);
86+
} catch (e) {
87+
console.error(e);
88+
}
89+
}
90+
```
91+
92+
## Using with SuperTest
93+
SuperTest uses SuperAgent under the hood. SuperAgent isn't well isolated by SuperTest,
94+
so you can easily use any SuperAgent plugin including superagent-json22. Here is the example.
95+
```javascript
96+
import supertest from 'supertest';
97+
import { appFactory } from '../lib/app/appFatory.js';
98+
import { TypedModel } from '../lib/models/typed-model.js';
99+
100+
suite('Response tests', () => {
101+
102+
test('supertest with plugin', (done) => {
103+
const app = appFactory();
104+
supertest(app)
105+
.post('/')
106+
.use(json22Plugin({ json22ParseOptions: { context: { TypedModel } }}))
107+
.send(data)
108+
.expect(200)
109+
.then(resp => done())
110+
.catch(done);
111+
});
112+
113+
});
114+
```

index.cjs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
MIT License
3+
4+
Copyright (c) 2022 Dmitry Dutikov
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
*/
24+
const { JSON22 } = require('json22');
25+
26+
const METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH'];
27+
28+
module.exports = { json22Plugin };
29+
30+
/**
31+
* @param {Json22SuperagentPluginOptions} [options]
32+
* @return {request.Plugin}
33+
* */
34+
function json22Plugin(options) {
35+
return requestPlugin.bind(options ?? {});
36+
}
37+
38+
/**
39+
* @this Json22SuperagentPluginOptions
40+
* @param {SuperAgentRequest} request
41+
**/
42+
function requestPlugin(request) {
43+
if (METHODS_WITH_BODY.indexOf(request.method) > -1) {
44+
request.type(JSON22.mimeType);
45+
request.serialize(data => JSON22.stringify(data, this.json22StringifyOptions));
46+
}
47+
request.accept(JSON22.mimeType);
48+
request.buffer(true); // required for parsing in node environment
49+
request.parse((textOrResp, cb) => {
50+
if (cb == null) {
51+
return JSON22.parse(textOrResp, this.json22ParseOptions);
52+
} else {
53+
json22Parse(textOrResp, cb, this.json22ParseOptions);
54+
}
55+
});
56+
}
57+
58+
59+
function json22Parse(res, fn, json22ParseOptions) {
60+
const chunks = [];
61+
res.setEncoding('utf8');
62+
res.on('data', chunk => chunks.push(chunk));
63+
res.on('end', function () {
64+
try {
65+
const text = chunks.join('');
66+
const body = JSON22.parse(text, json22ParseOptions);
67+
res.text = text
68+
fn(undefined, body);
69+
} catch (e) {
70+
e.rawResponse = chunks.join('');
71+
e.statusCode = res.statusCode;
72+
fn(e);
73+
}
74+
});
75+
}

index.d.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
MIT License
3+
4+
Copyright (c) 2022 Dmitry Dutikov
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
*/
24+
import { Json22ParseOptions, Json22StringifyOptions } from 'json22';
25+
26+
export interface Json22SuperagentPluginOptions {
27+
json22ParseOptions?: Json22ParseOptions;
28+
json22StringifyOptions?: Json22StringifyOptions;
29+
}
30+
31+
export declare function json22Plugin(options?: Json22SuperagentPluginOptions);

index.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
MIT License
3+
4+
Copyright (c) 2022 Dmitry Dutikov
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
*/
24+
import { JSON22 } from 'json22';
25+
26+
const METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH'];
27+
28+
/**
29+
* @param {Json22SuperagentPluginOptions} [options]
30+
* @return {request.Plugin}
31+
* */
32+
export function json22Plugin(options) {
33+
return requestPlugin.bind(options ?? {});
34+
}
35+
36+
/**
37+
* @this Json22SuperagentPluginOptions
38+
* @param {SuperAgentRequest} request
39+
**/
40+
function requestPlugin(request) {
41+
if (METHODS_WITH_BODY.indexOf(request.method) > -1) {
42+
request.type(JSON22.mimeType);
43+
request.serialize(data => JSON22.stringify(data, this.json22StringifyOptions));
44+
}
45+
request.accept(JSON22.mimeType);
46+
request.buffer(true); // required for parsing in node environment
47+
request.parse((textOrResp, cb) => {
48+
if (cb == null) {
49+
return JSON22.parse(textOrResp, this.json22ParseOptions);
50+
} else {
51+
json22Parse(textOrResp, cb, this.json22ParseOptions);
52+
}
53+
});
54+
}
55+
56+
57+
function json22Parse(res, fn, json22ParseOptions) {
58+
const chunks = [];
59+
res.setEncoding('utf8');
60+
res.on('data', chunk => chunks.push(chunk));
61+
res.on('end', function () {
62+
try {
63+
const text = chunks.join('');
64+
const body = JSON22.parse(text, json22ParseOptions);
65+
res.text = text
66+
fn(undefined, body);
67+
} catch (e) {
68+
e.rawResponse = chunks.join('');
69+
e.statusCode = res.statusCode;
70+
fn(e);
71+
}
72+
});
73+
}

0 commit comments

Comments
 (0)