utilitas 1999.1.12 → 1999.1.13
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/README.md +14 -44
- package/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/index.mjs +4 -6
- package/lib/alan.mjs +15 -20
- package/lib/bot.mjs +1 -1
- package/lib/manifest.mjs +1 -1
- package/lib/network.mjs +1 -1
- package/lib/speech.mjs +1 -1
- package/lib/storage.mjs +22 -7
- package/lib/utilitas.mjs +4 -2
- package/lib/web.mjs +269 -6
- package/package.json +1 -1
- package/lib/shekel.mjs +0 -24
- package/lib/shot.mjs +0 -195
package/lib/shot.mjs
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { fileTypeFromBuffer } from 'file-type';
|
|
2
|
-
import { promises as fs } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { sha256 } from './encryption.mjs';
|
|
5
|
-
import { distillHtml } from './web.mjs';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
ensureInt,
|
|
9
|
-
ensureString, extract, ignoreErrFunc, inBrowser, parseJson, parseVersion,
|
|
10
|
-
throwError, which
|
|
11
|
-
} from './utilitas.mjs';
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
encodeBase64DataURL, exists, mapFilename, readJson, touchPath, writeJson
|
|
15
|
-
} from './storage.mjs';
|
|
16
|
-
|
|
17
|
-
const TMPDIR = process.env.TMPDIR ? join(process.env.TMPDIR, 'shot') : null;
|
|
18
|
-
const buf2utf = buf => buf.toString('utf8');
|
|
19
|
-
const [_JSON, _PARSED] = ['JSON', 'PARSED'];
|
|
20
|
-
const getJson = async (u, o) => await get(u, { encode: _JSON, ...o || {} });
|
|
21
|
-
const getParsedHtml = async (u, o) => await get(u, { encode: _PARSED, ...o || {} });
|
|
22
|
-
const checkSearch = () => googleApiKey && googleCx;
|
|
23
|
-
|
|
24
|
-
let googleApiKey, googleCx;
|
|
25
|
-
|
|
26
|
-
const defFetchOpt = {
|
|
27
|
-
redirect: 'follow', follow: 3, timeout: 1000 * 10, headers: {
|
|
28
|
-
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) '
|
|
29
|
-
+ 'AppleWebKit/605.1.15 (KHTML, like Gecko) '
|
|
30
|
-
+ 'Version/17.0 Safari/605.1.15',
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const getVersionOnNpm = async (packName) => {
|
|
35
|
-
assert(packName, 'Package name is required.', 400);
|
|
36
|
-
const url = `https://registry.npmjs.org/-/package/${packName}/dist-tags`;
|
|
37
|
-
const rp = (await get(url, { encode: _JSON }))?.content;
|
|
38
|
-
assert(rp, 'Error fetching package info.', 500);
|
|
39
|
-
assert(rp !== 'Not Found' && rp.latest, 'Package not found.', 404);
|
|
40
|
-
return parseVersion(rp.latest);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const checkVersion = async (pack) => {
|
|
44
|
-
const objPack = await which(pack);
|
|
45
|
-
const curVersion = objPack.versionNormalized;
|
|
46
|
-
const newVersion = await getVersionOnNpm(objPack.name);
|
|
47
|
-
return {
|
|
48
|
-
name: objPack.name, curVersion, newVersion,
|
|
49
|
-
updateAvailable: newVersion.normalized > curVersion.normalized,
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const getCurrentIp = async (options) => {
|
|
54
|
-
const resp = await get(
|
|
55
|
-
'https://ifconfig.me/all.json', { encode: _JSON, ...options || {} }
|
|
56
|
-
);
|
|
57
|
-
assert(resp?.content?.ip_addr, 'Error detecting IP address.', 500);
|
|
58
|
-
return options?.raw ? resp : resp.content.ip_addr;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const getCurrentPosition = async () => {
|
|
62
|
-
const url = 'https://geolocation-db.com/json/';
|
|
63
|
-
const rp = await fetch(url).then(res => res.json());
|
|
64
|
-
assert(rp, 'Network is unreachable.', 500);
|
|
65
|
-
assert(rp.country_code, 'Error detecting geolocation.', 500);
|
|
66
|
-
return rp;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const get = async (url, options) => {
|
|
70
|
-
assert(url, 'URL is required.', 400);
|
|
71
|
-
options = options || {};
|
|
72
|
-
options.encode = ensureString(options.encode, { case: 'UP' });
|
|
73
|
-
const urlHash = inBrowser() ? null : sha256(url);
|
|
74
|
-
const tmp = urlHash ? (options.cache?.tmp || TMPDIR) : null;
|
|
75
|
-
const base = tmp ? join(tmp, mapFilename(urlHash)) : null;
|
|
76
|
-
const [cacheMeta, cacheCont] = base ? ['meta', 'content'].map(
|
|
77
|
-
x => join(base, `${urlHash}.${x}`)
|
|
78
|
-
) : [];
|
|
79
|
-
if (options?.fuzzy && await exists(cacheMeta) && await exists(cacheCont)) {
|
|
80
|
-
return { cache: { meta: cacheMeta, content: cacheCont } };
|
|
81
|
-
}
|
|
82
|
-
const meta = options?.refresh || !base ? null : await readJson(cacheMeta);
|
|
83
|
-
const cache = options?.refresh || !base ? null : await ignoreErrFunc(
|
|
84
|
-
() => fs.readFile(cacheCont)
|
|
85
|
-
);
|
|
86
|
-
const headers = meta?.responseHeaders && cache ? {
|
|
87
|
-
'cache-control': 'max-age=0',
|
|
88
|
-
'if-modified-since': meta.responseHeaders['last-modified'] || '',
|
|
89
|
-
'if-none-match': meta.responseHeaders['etag'] || '',
|
|
90
|
-
} : {};
|
|
91
|
-
let [timer, r, responseHeaders] = [null, null, {}];
|
|
92
|
-
const fetchOptions = {
|
|
93
|
-
...defFetchOpt, headers: { ...defFetchOpt.headers, ...headers },
|
|
94
|
-
...options.fetch || {}
|
|
95
|
-
};
|
|
96
|
-
if (options.timeout) {
|
|
97
|
-
const controller = new AbortController();
|
|
98
|
-
fetchOptions.signal = controller.signal;
|
|
99
|
-
timer = setTimeout(() => controller.abort(), options.timeout);
|
|
100
|
-
}
|
|
101
|
-
try { r = await fetch(url, fetchOptions); } catch (e) {
|
|
102
|
-
throwError(e.message.includes('aborted') ? 'Timed out.' : e.message, 500);
|
|
103
|
-
}
|
|
104
|
-
timer && clearTimeout(timer);
|
|
105
|
-
(r.status === 304) && (r.arrayBuffer = async () => cache);
|
|
106
|
-
const [htpMime, buffer] = [r.headers.get('content-type'), Buffer.from(await r.arrayBuffer())];
|
|
107
|
-
if (r.headers?.raw) { responseHeaders = r.headers.raw(); }
|
|
108
|
-
else { for (const [k, v] of r.headers.entries()) { responseHeaders[k] = v; } }
|
|
109
|
-
const bufMime = await ignoreErrFunc(async () => {
|
|
110
|
-
extract(await fileTypeFromBuffer(buffer), 'mime');
|
|
111
|
-
});
|
|
112
|
-
const mimeType = bufMime || htpMime;
|
|
113
|
-
const length = buffer.length;
|
|
114
|
-
let content;
|
|
115
|
-
if (!options?.fuzzy) {
|
|
116
|
-
switch (options.encode) {
|
|
117
|
-
case 'BUFFER':
|
|
118
|
-
content = buffer;
|
|
119
|
-
break;
|
|
120
|
-
case 'BASE64':
|
|
121
|
-
content = buffer.toString(options.encode);
|
|
122
|
-
break;
|
|
123
|
-
case 'BASE64_DATA_URL':
|
|
124
|
-
content = await encodeBase64DataURL(mimeType, buffer);
|
|
125
|
-
break;
|
|
126
|
-
case _JSON:
|
|
127
|
-
content = parseJson(buf2utf(buffer), null);
|
|
128
|
-
break;
|
|
129
|
-
case _PARSED:
|
|
130
|
-
content = await distillHtml(buf2utf(buffer));
|
|
131
|
-
break;
|
|
132
|
-
default:
|
|
133
|
-
assert(!options.encode, 'Invalid encoding.', 400);
|
|
134
|
-
case 'TEXT':
|
|
135
|
-
content = buf2utf(buffer);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
base && !cache && length && r.status === 200
|
|
139
|
-
&& await ignoreErrFunc(async () => {
|
|
140
|
-
return {
|
|
141
|
-
touch: await touchPath(base),
|
|
142
|
-
content: await fs.writeFile(cacheCont, buffer),
|
|
143
|
-
meta: await writeJson(cacheMeta, {
|
|
144
|
-
url, requestHeaders: headers, responseHeaders,
|
|
145
|
-
}),
|
|
146
|
-
};
|
|
147
|
-
});
|
|
148
|
-
return {
|
|
149
|
-
statusCode: r.status, statusText: r.statusText, length, mimeType,
|
|
150
|
-
content, headers: responseHeaders, response: r,
|
|
151
|
-
cache: r.status >= 200 && r.status < 400 ? { meta: cacheMeta, content: cacheCont } : null,
|
|
152
|
-
};
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const initSearch = async (options) => {
|
|
156
|
-
assert(
|
|
157
|
-
options?.apiKey && options?.cx, 'Invalid search options.'
|
|
158
|
-
);
|
|
159
|
-
return [googleApiKey, googleCx] = [options.apiKey, options.cx];
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const search = async (query, options) => {
|
|
163
|
-
const [key, cx, min, max]
|
|
164
|
-
= [options?.apiKey || googleApiKey, options?.cx || googleCx, 1, 10];
|
|
165
|
-
assert(query, 'Query is required.');
|
|
166
|
-
assert(key, 'API key is required.');
|
|
167
|
-
assert(cx, 'CX is required.');
|
|
168
|
-
const num = ensureInt(options?.num || max, { min, max });
|
|
169
|
-
const start = ensureInt(options?.start || min, { min });
|
|
170
|
-
assert(start + num <= 100, 'Reached maximum search limit.');
|
|
171
|
-
const url = 'https://www.googleapis.com/customsearch/v1'
|
|
172
|
-
+ `?key=${encodeURIComponent(key)}&cx=${encodeURIComponent(cx)}`
|
|
173
|
-
+ `&q=${encodeURIComponent(query)}&num=${num}&start=${start}`
|
|
174
|
-
+ (options?.image ? `&searchType=image` : '');
|
|
175
|
-
const resp = await get(url, { encode: _JSON, ...options || {} });
|
|
176
|
-
return options?.raw ? resp.content : {
|
|
177
|
-
totalResults: resp?.content?.searchInformation?.totalResults || 0,
|
|
178
|
-
startIndex: resp?.content?.queries?.request?.[0]?.startIndex || 1,
|
|
179
|
-
items: resp?.content?.items.map(x => ({
|
|
180
|
-
title: x.title, link: options?.image ? null : x.link,
|
|
181
|
-
snippet: x.snippet,
|
|
182
|
-
image: (options?.image ? x.link : x.pagemap?.cse_image?.[0]?.src) || null,
|
|
183
|
-
})),
|
|
184
|
-
};
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
export default get;
|
|
188
|
-
export {
|
|
189
|
-
checkSearch, checkVersion,
|
|
190
|
-
get,
|
|
191
|
-
getCurrentIp, getCurrentPosition,
|
|
192
|
-
getJson,
|
|
193
|
-
getParsedHtml,
|
|
194
|
-
getVersionOnNpm, initSearch, search
|
|
195
|
-
};
|