UNPKG

9.52 kBJavaScriptView Raw
1"use strict";
2const isObject = (x) => x && (typeof x === 'object' || typeof x === 'function');
3const http = require('http');
4
5
6/*******************************************************************************\
7** **
8** **
9** ================================================== **
10** ======== npm package: good-request ======== **
11** ================================================== ** **
12** **
13** New 7/31 version, first published version. **
14** "He was ten feet short; he RAN down the ramp. He RAAAN!" **
15** **
16** nice-request, neat-request, and my-request are already taken. **
17** better-request better back down. **
18** **
19** Based on the 2020-07-31 example documented at: **
20** https://nodejs.org/api/http.html#http_http_request_options_callback **
21** **
22** By wv-coder. **
23** **
24** **
25** ==== API ==== **
26** **
27** Functions provided: **
28** - basicPost (urlString, ct, reqText, callback) **
29** - retryingPost (urlString, ct, reqText, nRetries, callback) **
30** - retryingJsonPost (urlString, reqObject, nRetries, callback) **
31** **
32** Callback provides an object with some of the following properties: **
33** - received: true | undefined **
34** - statusCode: response status integer | undefined **
35** - text: response body string | undefined **
36** - error: error object | undefined **
37** - errorsRetried: array of error objects | undefined **
38** - object: response body jso | undefined **
39** **
40** **
41\*******************************************************************************/
42
43
44const basicPost =
45exports. basicPost =
46function basicPost (urlString, ct, reqText, callback) {
47 const url = new URL(urlString);
48 if (typeof reqText !== 'string') reqText = `${reqText}`;
49 const options = {
50 hostname: url.hostname,
51 port: parseInt(url.port),
52 path: url.pathname,
53 method: 'POST',
54 headers: {
55 'Content-Type': ct || 'text/plain',
56 'Content-Length': Buffer.byteLength(reqText),
57 },
58 };
59 const req = http.request(options, (res) => {
60 const resultTextChunks = [];
61 res.setEncoding('utf8');
62 res.on('data', (chunk) => {
63 resultTextChunks.push(chunk);
64 });
65 res.on('end', () => {
66 callback({
67 received: true,
68 statusCode: res.statusCode,
69 text: resultTextChunks.join(''),
70 });
71 });
72 });
73 req.on('error', (e) => {
74 callback({ error: e });
75 });
76 req.write(reqText);
77 req.end();
78};
79
80
81const retryingPost =
82exports. retryingPost =
83function retryingPost (urlString, ct, reqText, nRetries, callback) {
84 const errorsRetried = [];
85 const innerCallback = (result) => {
86 nRetries--;
87 if (result.error && nRetries > 0) {
88 errorsRetried.push(result.error);
89 basicPost(urlString, ct, reqText, innerCallback);
90 } else {
91 result.errorsRetried = errorsRetried;
92 callback(result);
93 }
94 };
95 return basicPost(urlString, ct, reqText, innerCallback);
96};
97
98
99const retryingJsonPost =
100exports. retryingJsonPost =
101function retryingJsonPost (urlString, reqObject, nRetries, callback) {
102 const reqText = `${JSON.stringify(reqObject, undefined, 2)}\n`;
103 const innerCallback = (result) => {
104 try {
105 result.object = JSON.parse(result.text);
106 } catch (jsonParseEx) {
107 if (!result.error) result.error = jsonParseEx;
108 }
109 callback(result);
110 };
111 return retryingPost(urlString, 'application/json', reqText, nRetries, innerCallback);
112};
113
114
115/*************************************************************\
116** **
117** ==== HTML Generator ==== **
118** 9/14/2020 AV **
119** **
120** Many times in the past I tried to make this module. **
121** I think I understand well enough to do so now. **
122** **
123** Google.com will be the default model, **
124** to default to power while avoiding questions. **
125** **
126** It's not perfect, nor do I intend it to be yet. **
127** **
128\*************************************************************/
129
130
131const collapseStr =
132exports. collapseStr =
133function collapseStr (x) {
134 return x ? (Array.isArray(x) ? x.map(collapseStr).join('') : '' + x) : '';
135};
136
137
138const makeHtml =
139exports. makeHtml =
140function makeHtml ({ lang, fixed, title, style, head, body, mainDiv, script }) {
141
142 const bits = [];
143
144 // Just as Google does, I use a lowercase DTD, because in theory it might compress better.
145 bits.push('<!doctype html>');
146
147 // Due to unfortunate browser implementation, it turns out lang="en" is the only neutral option,
148 // because omitting lang="en" has come to mean "use machine learning to guess a random odd language and apply translation",
149 // and the only way to avoid that annoyance is to say lang="en" regardless.
150 bits.push(`<html lang="${lang || 'en'}">`);
151
152 // Once again, it might compress better as lowercase.
153 bits.push('<meta charset="utf-8">');
154
155 bits.push(`<meta name="viewport" content="width=device-width, initial-scale=1${
156 fixed ? ', minimum-scale=1, maximum-scale=1, user-scalable=no' : ''
157 }">`);
158
159 if (title) bits.push(`<title>${title}</title>`);
160
161 if (style) {
162 if (isObject(style) && style.src) {
163 const each = Array.isArray(style.src) ? style.src : [style.src];
164 for (let s of each) bits.push(`<link href="${s}" rel="stylesheet">`);
165 } else bits.push(`<style>${collapseStr(style)}</style>`);
166 }
167
168 if (head) bits.push(collapseStr(head));
169
170 bits.push('</head>');
171
172 bits.push('<body>');
173
174 if (body) bits.push(collapseStr(body));
175
176 if (mainDiv) bits.push(`<div id="main">${collapseStr(mainDiv)}</div>`);
177
178 if (script) {
179 if (isObject(script) && script.src) {
180 const each = Array.isArray(script.src) ? script.src : [script.src];
181 for (let s of each) bits.push(`<script src="${s}"></script>`);
182 } else bits.push(`<script>${collapseStr(script)}</script>`);
183 }
184
185 bits.push('</body>');
186
187 bits.push('</html>');
188
189 //TODO: Check "viewport". Test every path. Compare with old 'good-request'. Collapse with previous versions of 'makeHtml'.
190
191 return bits.join('');
192};
193
194
195/********************************\
196** **
197** gss | good-string-sort **
198** \********************************************\
199** **
200** This function correctly sorts strings according to Unicode Codepoints. **
201** For example: **
202** ['some', 'strings', 'in here.'].sort(gss); **
203** **
204\****************************************************************************/
205
206
207const goodStringSort =
208exports. goodStringSort =
209function goodStringSort (a, b) {
210 if (!(typeof a === 'string' && typeof b === 'string'))
211 throw new Error('good-string-sort: Expects a pair of string arguments.');
212 const codepointsA = [...a].map((c) => c.codePointAt());
213 const codepointsB = [...b].map((c) => c.codePointAt());
214 // Note that the length of the codepoint-arrays DIFFERS from the length of the raw strings due to SURROGATE PAIRS.
215 const l = Math.max(codepointsA.length, codepointsB.length);
216 for (let i = 0; i < l; i++) {
217 let sortCharA = codepointsA[i]; if (sortCharA === undefined) sortCharA = -1;
218 let sortCharB = codepointsB[i]; if (sortCharB === undefined) sortCharB = -1;
219 if (sortCharA > sortCharB) return 1;
220 else if (sortCharA < sortCharB) return -1;
221 }
222 return 0;
223};