-
Notifications
You must be signed in to change notification settings - Fork 124
/
utils.js
156 lines (118 loc) · 4.2 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
'use strict'
/**
* Lightweight web framework for your serverless applications
* @author Jeremy Daly <jeremy@jeremydaly.com>
* @license MIT
*/
const QS = require('querystring') // Require the querystring library
const crypto = require('crypto') // Require Node.js crypto library
const { FileError } = require('./errors') // Require custom errors
const entityMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
'\'': '''
}
exports.escapeHtml = html => html.replace(/[&<>"']/g, s => entityMap[s])
// From encodeurl by Douglas Christopher Wilson
let ENCODE_CHARS_REGEXP = /(?:[^\x21\x25\x26-\x3B\x3D\x3F-\x5B\x5D\x5F\x61-\x7A\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g
let UNMATCHED_SURROGATE_PAIR_REGEXP = /(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g
let UNMATCHED_SURROGATE_PAIR_REPLACE = '$1\uFFFD$2'
exports.encodeUrl = url => String(url)
.replace(UNMATCHED_SURROGATE_PAIR_REGEXP, UNMATCHED_SURROGATE_PAIR_REPLACE)
.replace(ENCODE_CHARS_REGEXP, encodeURI)
const encodeBody = (body,serializer) => {
const encode = typeof serializer === 'function' ? serializer : JSON.stringify
return typeof body === 'object' ? encode(body) : (body && typeof body !== 'string' ? body.toString() : (body ? body : ''))
}
exports.encodeBody = encodeBody
exports.parsePath = path => {
return path ? path.trim().split('?')[0].replace(/^\/(.*?)(\/)*$/,'$1').split('/') : []
}
exports.parseBody = body => {
try {
return JSON.parse(body)
} catch(e) {
return body
}
}
// Parses auth values into known formats
const parseAuthValue = (type,value) => {
switch (type) {
case 'Basic': {
let creds = Buffer.from(value, 'base64').toString().split(':')
return { type, value, username: creds[0], password: creds[1] ? creds[1] : null }
}
case 'OAuth': {
let params = QS.parse(value.replace(/",\s*/g,'&').replace(/"/g,'').trim())
return Object.assign({ type, value }, params)
}
default: {
return { type, value }
}
}
}
exports.parseAuth = authStr => {
let auth = authStr && typeof authStr === 'string' ? authStr.split(' ') : []
return auth.length > 1 && ['Bearer','Basic','Digest','OAuth'].includes(auth[0]) ?
parseAuthValue(auth[0], auth.slice(1).join(' ').trim()) :
{ type: 'none', value: null }
}
const mimeMap = require('./mimemap.js') // MIME Map
exports.mimeLookup = (input,custom={}) => {
let type = input.trim().replace(/^\./,'')
// If it contains a slash, return unmodified
if (/.*\/.*/.test(type)) {
return input.trim()
} else {
// Lookup mime type
let mime = Object.assign(mimeMap,custom)[type]
return mime ? mime : false
}
}
const statusCodes = require('./statusCodes.js') // MIME Map
exports.statusLookup = status => {
return status in statusCodes ? statusCodes[status] : 'Unknown'
}
// Parses routes into readable array
const extractRoutes = (routes,table=[]) => {
for (let route in routes) {
if (/^__[A-Z]+$/.test(route)) {
table.push([route.replace(/^__/,''),routes[route].path])
} else {
extractRoutes(routes[route],table)
}
}
return table
}
exports.extractRoutes = extractRoutes
// Generate an Etag for the supplied value
exports.generateEtag = data =>
crypto.createHash('sha256').update(encodeBody(data)).digest('hex').substr(0,32)
// Check if valid S3 path
exports.isS3 = path => /^s3:\/\/.+\/.+/i.test(path)
// Parse S3 path
exports.parseS3 = path => {
if (!this.isS3(path)) throw new FileError('Invalid S3 path',{path})
let s3object = path.replace(/^s3:\/\//i,'').split('/')
return { Bucket: s3object.shift(), Key: s3object.join('/') }
}
// Deep Merge
exports.deepMerge = (a,b) => {
Object.keys(b).forEach(key => (key in a) ?
this.deepMerge(a[key],b[key]) : Object.assign(a,b) )
return a
}
// Concats values from an array to ',' separated string
exports.fromArray = val =>
val && val instanceof Array ? val.toString() : undefined
// Stringify multi-value headers
exports.stringifyHeaders = headers =>
Object.keys(headers)
.reduce((acc,key) =>
Object.assign(acc,{
// set-cookie cannot be concatenated with a comma
[key]: key === 'set-cookie' ? headers[key].slice(-1)[0] : headers[key].toString()
})
,{})