Skip to content

Commit

Permalink
feat: create @tinyhttp/jsonp
Browse files Browse the repository at this point in the history
  • Loading branch information
v1rtl committed Sep 28, 2020
1 parent 4087eed commit f21586e
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 5 deletions.
8 changes: 3 additions & 5 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"$schema": "https://unpkg.com/@changesets/config@1.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"linked": [],
"access": "restricted",
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": []
}
"updateInternalDependencies": "patch"
}
3 changes: 3 additions & 0 deletions packages/jsonp/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
pnpm-lock.yaml
rollup.config.js
17 changes: 17 additions & 0 deletions packages/jsonp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# @tinyhttp/jsonp

[![npm (scoped)](https://img.shields.io/npm/v/@tinyhttp/jsonp?style=flat-square)](https://npmjs.com/package/@tinyhttp/jsonp) [![npm](https://img.shields.io/npm/dt/@tinyhttp/jsonp?style=flat-square)](https://npmjs.com/package/@tinyhttp/jsonp) [![](https://img.shields.io/badge/website-visit-hotpink?style=flat-square)](https://tinyhttp.v1rtl.site/mw/jsonp)

JSONP middleware module. Helper package for `@tinyhttp/res`.

## Install

```sh
pnpm i @tinyhttp/jsonp
```

## Example

## License

MIT 漏 [v1rtl](https://v1rtl.site)
42 changes: 42 additions & 0 deletions packages/jsonp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "@tinyhttp/jsonp",
"version": "0.4.0",
"type": "module",
"description": "JSONP response middleware",
"homepage": "https://github.com/talentlessguy/tinyhttp#readme",
"repository": {
"type": "git",
"url": "https://github.com/talentlessguy/tinyhttp.git",
"directory": "packages/etag"
},
"main": "dist/index.cjs",
"types": "dist/index.d.ts",
"module": "dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./package.json": "./package.json",
"./": "./"
},
"keywords": [
"tinyhttp",
"node.js",
"web framework",
"web",
"backend",
"etag"
],
"engines": {
"node": ">=12.4.0"
},
"author": "v1rtl",
"license": "MIT",
"scripts": {
"build": "rollup -c ../../build/defaultConfig.js"
},
"dependencies": {
"@tinyhttp/app": "workspace:^0.4.3"
}
}
70 changes: 70 additions & 0 deletions packages/jsonp/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { Request, Response, App } from '@tinyhttp/app'

export type JSONPOptions = Partial<{
escape: boolean
replacer: (this: any, key: string, value: any) => any
spaces: string | number
callbackName: string
}>

function stringify(value: unknown, replacer: (this: any, key: string, value: any) => any, spaces: string | number, escape: boolean) {
// v8 checks arguments.length for optimizing simple call
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
let json = replacer || spaces ? JSON.stringify(value, replacer, spaces) : JSON.stringify(value)

if (escape) {
json = json.replace(/[<>&]/g, (c) => {
switch (c.charCodeAt(0)) {
case 0x3c:
return '\\u003c'
case 0x3e:
return '\\u003e'
case 0x26:
return '\\u0026'
default:
return c
}
})
}

return json
}

/**
* Send JSON response with JSONP callback support
* @param req Request
* @param res Response
* @param app App
*/
export const jsonp = (req: Request, res: Response, app: App) => (obj: unknown, opts?: JSONPOptions) => {
const val = obj

const { escape, replacer, spaces, callbackName } = opts

let body = stringify(val, replacer, spaces, escape)

let callback = req.query[app[callbackName]]

if (!res.get('Content-Type')) {
res.set('X-Content-Type-Options', 'nosniff')
res.set('Content-Type', 'application/json')
}

// jsonp
if (typeof callback === 'string' && callback.length !== 0) {
res.set('X-Content-Type-Options', 'nosniff')
res.set('Content-Type', 'text/javascript')

// restrict callback charset
callback = callback.replace(/[^[\]\w$.]/g, '')

// replace chars not allowed in JavaScript that are in JSON
body = body.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029')

// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
// the typeof check is just to reduce client error noise
body = '/**/ typeof ' + callback + " === 'function' && " + callback + '(' + body + ');'
}

return res.send(body)
}
3 changes: 3 additions & 0 deletions packages/jsonp/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.json"
}

0 comments on commit f21586e

Please sign in to comment.