swpp-backends 2.3.12 → 3.0.0-alpha.0
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 +1 -38
- package/dist/index.js +5 -38
- package/dist/swpp/FileParser.js +85 -0
- package/dist/swpp/JsonBuilder.js +232 -0
- package/dist/swpp/NetworkFileHandler.js +68 -0
- package/dist/swpp/ResourcesScanner.js +246 -0
- package/dist/swpp/SwCompiler.js +62 -0
- package/dist/swpp/config/ConfigLoader.js +185 -0
- package/dist/swpp/database/CompilationEnv.js +280 -0
- package/dist/swpp/database/CrossDepCode.js +76 -0
- package/dist/swpp/database/CrossEnv.js +70 -0
- package/dist/swpp/database/KeyValueDatabase.js +76 -0
- package/dist/swpp/database/RuntimeCoreCode.js +146 -0
- package/dist/swpp/database/RuntimeDepCode.js +316 -0
- package/dist/swpp/database/RuntimeEventCode.js +54 -0
- package/dist/swpp/database/RuntimeKeyValueDatabase.js +11 -0
- package/dist/swpp/untils.js +256 -0
- package/package.json +2 -5
- package/types/FileAnalyzer.d.ts +2 -2
- package/types/Utils.d.ts +1 -2
- package/types/browser/ServiceWorkerRuntimeTypes.d.ts +2 -0
- package/types/index.d.ts +2 -64
- package/types/swpp/FileParser.d.ts +67 -0
- package/types/swpp/JsonBuilder.d.ts +28 -0
- package/types/swpp/NetworkFileHandler.d.ts +28 -0
- package/types/swpp/ResourcesScanner.d.ts +67 -0
- package/types/swpp/RuntimeEnv.d.ts +39 -0
- package/types/swpp/SwCodeInject.d.ts +17 -0
- package/types/swpp/SwCompiler.d.ts +47 -0
- package/types/swpp/config/ConfigLoader.d.ts +210 -0
- package/types/swpp/database/CompilationEnv.d.ts +33 -0
- package/types/swpp/database/CrossDepCode.d.ts +30 -0
- package/types/swpp/database/CrossEnv.d.ts +25 -0
- package/types/swpp/database/KeyValueDatabase.d.ts +53 -0
- package/types/swpp/database/RuntimeCoreCode.d.ts +29 -0
- package/types/swpp/database/RuntimeDepCode.d.ts +104 -0
- package/types/swpp/database/RuntimeEnv.d.ts +5 -0
- package/types/swpp/database/RuntimeEventCode.d.ts +25 -0
- package/types/swpp/database/RuntimeKeyValueDatabase.d.ts +7 -0
- package/types/swpp/untils.d.ts +89 -0
- package/dist/DomBuilder.js +0 -32
- package/dist/FileAnalyzer.js +0 -496
- package/dist/ServiceWorkerBuilder.js +0 -100
- package/dist/SwppConfig.js +0 -54
- package/dist/SwppRules.js +0 -85
- package/dist/UpdateJsonBuilder.js +0 -320
- package/dist/Utils.js +0 -227
- package/dist/Variant.js +0 -111
- package/dist/VersionAnalyzer.js +0 -67
- package/dist/browser/ServiceWorkerRuntimeTypes.js +0 -1
- package/dist/resources/sw-template.js +0 -348
package/README.md
CHANGED
|
@@ -2,41 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
## 欢迎使用 SwppBackends
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
swpp 的全拼为“Service Worker Plus Plus”(或“Service Worker++”),但是其与已有的插件“hexo-service-worker”并没有关系,插件中所有代码均为我个人开发,这一点请不要误解。
|
|
8
|
-
|
|
9
|
-
swpp 生成的 SW 与其它插件的对比:
|
|
10
|
-
|
|
11
|
-
| | swpp | hexo-offline |
|
|
12
|
-
|:---------------:|:-------------:|:------------:|
|
|
13
|
-
| 本地缓存 | ✔️ | ✔️ |
|
|
14
|
-
| 缓存增量更新 | ✔️ | ❌ |
|
|
15
|
-
| 缓存过期时间 | ❌ | ✔️ |
|
|
16
|
-
| 缓存大小限制 | ❌ | ✔️ |
|
|
17
|
-
| 预缓存 | ❌<sup>1</sup> | ✔️ |
|
|
18
|
-
| Request 篡改 | ✔️ | ❌ |
|
|
19
|
-
| URL 竞速 | ✔️ | ❌ |
|
|
20
|
-
| 备用 URL | ✔️ | ❌ |
|
|
21
|
-
| 204 阻塞响应 | ✔️ | ❌ |
|
|
22
|
-
| 逃生门 | ✔️ | ❌ |
|
|
23
|
-
| 请求合并 | ✔️ | ❌ |
|
|
24
|
-
| 跨平台<sup>2</sup> | ✔️ | ❌ |
|
|
25
|
-
| 高度自由 | ✔️ | ❌ |
|
|
26
|
-
| 更新 | 非常频繁 | 超过两年没有更新 |
|
|
27
|
-
|
|
28
|
-
+ ✔️:支持
|
|
29
|
-
+ ❌:不支持
|
|
30
|
-
|
|
31
|
-
1. 预缓存可以在前端实现,SW 实现这个功能容易拖延注册时间
|
|
32
|
-
2. 跨平台是指跨越框架(可在 NodeJS 平台中的任意框架下工作)
|
|
33
|
-
|
|
34
|
-
目前支持的平台:
|
|
35
|
-
|
|
36
|
-
| 平台 | 插件名 | 文档 | 作者 |
|
|
37
|
-
|:----:|:-----------:|:---------------------------------------------------------:|:-----------------------:|
|
|
38
|
-
| hexo | `hexo-swpp` | [github](https://github.com/EmptyDreams/hexo-swpp#readme) | [空梦](https://kmar.top/) |
|
|
39
|
-
|
|
40
|
-
如果你为某一个平台做了适配,可以在 gh 上发布 issue 或者在文档页面发布评论~
|
|
41
|
-
|
|
42
|
-
文档:[Swpp Backends 官方文档 | 山岳库博](https://kmar.top/posts/b70ec88f/)
|
|
5
|
+
本分支是 swpp 的 V3 分支,V3 将重构包括 sw.js 在内的所有代码,目前仍在开发中。
|
package/dist/index.js
CHANGED
|
@@ -1,40 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const Variant_1 = require("./Variant");
|
|
9
|
-
const VersionAnalyzer_1 = require("./VersionAnalyzer");
|
|
10
|
-
const DomBuilder_1 = require("./DomBuilder");
|
|
11
|
-
// noinspection JSUnusedGlobalSymbols
|
|
12
|
-
exports.default = {
|
|
13
|
-
version: require('../package.json').version,
|
|
14
|
-
cache: {
|
|
15
|
-
readEjectData: Utils_1.readEjectData, readUpdateJson: Variant_1.readUpdateJson,
|
|
16
|
-
readRules: Variant_1.readRules, readMergeVersionMap: Variant_1.readMergeVersionMap,
|
|
17
|
-
readOldVersionJson: Variant_1.readOldVersionJson, readNewVersionJson: Variant_1.readNewVersionJson,
|
|
18
|
-
readAnalyzeResult: Variant_1.readAnalyzeResult
|
|
19
|
-
},
|
|
20
|
-
builder: {
|
|
21
|
-
buildServiceWorker: ServiceWorkerBuilder_1.buildServiceWorker,
|
|
22
|
-
buildDomJs: DomBuilder_1.buildDomJs,
|
|
23
|
-
buildVersionJson: FileAnalyzer_1.buildVersionJson,
|
|
24
|
-
buildUpdateJson: UpdateJsonBuilder_1.buildUpdateJson,
|
|
25
|
-
calcEjectValues: Utils_1.calcEjectValues,
|
|
26
|
-
analyzeVersion: VersionAnalyzer_1.analyzeVersion
|
|
27
|
-
},
|
|
28
|
-
loader: {
|
|
29
|
-
loadRules: SwppRules_1.loadRules, loadUpdateJson: UpdateJsonBuilder_1.loadUpdateJson, loadVersionJson: FileAnalyzer_1.loadVersionJson
|
|
30
|
-
},
|
|
31
|
-
event: {
|
|
32
|
-
addRulesMapEvent: SwppRules_1.addRulesMapEvent, refreshUrl: VersionAnalyzer_1.refreshUrl, submitChange: UpdateJsonBuilder_1.submitChange, submitCacheInfo: FileAnalyzer_1.submitCacheInfo, submitExternalUrl: FileAnalyzer_1.submitExternalUrl, registryFileHandler: FileAnalyzer_1.registryFileHandler
|
|
33
|
-
},
|
|
34
|
-
utils: {
|
|
35
|
-
getSource: Utils_1.getSource, getShorthand: UpdateJsonBuilder_1.getShorthand, findCache: FileAnalyzer_1.findCache,
|
|
36
|
-
fetchFile: Utils_1.fetchFile, replaceDevRequest: Utils_1.replaceDevRequest, replaceRequest: FileAnalyzer_1.replaceRequest,
|
|
37
|
-
isStable: FileAnalyzer_1.isStable, isExclude: FileAnalyzer_1.isExclude, findFileHandler: FileAnalyzer_1.findFileHandler,
|
|
38
|
-
eachAllLinkInUrl: FileAnalyzer_1.eachAllLinkInUrl, deepFreeze: Utils_1.deepFreeze, writeVariant: Variant_1.writeVariant, readVariant: Variant_1.readVariant, deleteVariant: Variant_1.deleteVariant
|
|
39
|
-
}
|
|
40
|
-
};
|
|
3
|
+
exports.version = void 0;
|
|
4
|
+
const untils_1 = require("./swpp/untils");
|
|
5
|
+
/** 版本号 */
|
|
6
|
+
exports.version = require('../package.json').version;
|
|
7
|
+
untils_1.utils.printInfo('INDEX', `欢迎使用 swpp@${exports.version}`);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildFileParser = exports.FileParserRegistry = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const untils_1 = require("./untils");
|
|
9
|
+
class FileParserRegistry {
|
|
10
|
+
constructor(compilation, obj = {}) {
|
|
11
|
+
this.compilation = compilation;
|
|
12
|
+
this.map = new Map();
|
|
13
|
+
for (let key in obj) {
|
|
14
|
+
this.map.set(key, obj[key]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
registry(type, parser) {
|
|
18
|
+
this.map.set(type, parser);
|
|
19
|
+
}
|
|
20
|
+
/** 判断是否支持指定类型 */
|
|
21
|
+
containsType(type) {
|
|
22
|
+
return this.map.has(type);
|
|
23
|
+
}
|
|
24
|
+
/** 解析本地文件 */
|
|
25
|
+
async parserLocalFile(path) {
|
|
26
|
+
const parser = this.map.get(path_1.default.extname(path));
|
|
27
|
+
if (!parser)
|
|
28
|
+
return new Set();
|
|
29
|
+
const content = await parser.readFromLocal(this.compilation, path);
|
|
30
|
+
return await parser.extractUrls(this.compilation, content);
|
|
31
|
+
}
|
|
32
|
+
/** 解析网络文件 */
|
|
33
|
+
async parserNetworkFile(response, callback) {
|
|
34
|
+
const fileHandler = this.compilation.compilationEnv.read('FETCH_NETWORK_FILE');
|
|
35
|
+
const contentType = fileHandler.getUrlContentType(response.url, response);
|
|
36
|
+
const parser = this.map.get(contentType);
|
|
37
|
+
if (!parser)
|
|
38
|
+
return new Set();
|
|
39
|
+
const content = await parser.readFromNetwork(this.compilation, response);
|
|
40
|
+
if (callback)
|
|
41
|
+
await callback(content);
|
|
42
|
+
return await parser.extractUrls(this.compilation, content);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 解析指定的 URL
|
|
46
|
+
* @param url 链接
|
|
47
|
+
* @param isCached 该链接指向的资源是否需要缓存
|
|
48
|
+
*/
|
|
49
|
+
async parserUrlFile(url, isCached) {
|
|
50
|
+
const fileHandler = this.compilation.compilationEnv.read('FETCH_NETWORK_FILE');
|
|
51
|
+
const contentType = fileHandler.getUrlContentType(url);
|
|
52
|
+
if (!contentType && !isCached)
|
|
53
|
+
return { file: url, mark: '', urls: new Set() };
|
|
54
|
+
const parser = this.map.get(contentType);
|
|
55
|
+
if (parser?.calcUrl) {
|
|
56
|
+
const result = await parser.calcUrl(url);
|
|
57
|
+
if (result)
|
|
58
|
+
return {
|
|
59
|
+
file: url,
|
|
60
|
+
...result
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const fetcher = this.compilation.compilationEnv.read('FETCH_NETWORK_FILE');
|
|
64
|
+
const urls = new Set();
|
|
65
|
+
let mark = '';
|
|
66
|
+
await fetcher.fetch(url)
|
|
67
|
+
.then(response => this.parserNetworkFile(response, content => {
|
|
68
|
+
mark = untils_1.utils.calcHash(content);
|
|
69
|
+
}))
|
|
70
|
+
.then(urls => urls.forEach(it => urls.add(it)));
|
|
71
|
+
return { file: url, mark, urls };
|
|
72
|
+
}
|
|
73
|
+
/** 解析指定类型的文件内容 */
|
|
74
|
+
async parserContent(type, content) {
|
|
75
|
+
const parser = this.map.get(type);
|
|
76
|
+
if (!parser)
|
|
77
|
+
return new Set();
|
|
78
|
+
return await parser.extractUrls(this.compilation, content);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.FileParserRegistry = FileParserRegistry;
|
|
82
|
+
function buildFileParser(parser) {
|
|
83
|
+
return parser;
|
|
84
|
+
}
|
|
85
|
+
exports.buildFileParser = buildFileParser;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JsonBuilder = void 0;
|
|
4
|
+
const untils_1 = require("./untils");
|
|
5
|
+
class JsonBuilder {
|
|
6
|
+
constructor(compilation, urls, headers = new Map(), map = new Map()) {
|
|
7
|
+
this.compilation = compilation;
|
|
8
|
+
this.urls = urls;
|
|
9
|
+
this.headers = headers;
|
|
10
|
+
this.map = map;
|
|
11
|
+
}
|
|
12
|
+
update(key, value) {
|
|
13
|
+
this.map.set(key, value);
|
|
14
|
+
}
|
|
15
|
+
putHeader(key, value) {
|
|
16
|
+
this.headers.set(key, value);
|
|
17
|
+
}
|
|
18
|
+
async buildJson() {
|
|
19
|
+
const json = await this.compilation.compilationEnv.read('VERSION_FILE')();
|
|
20
|
+
if (json.info.length == 0) {
|
|
21
|
+
json.info.push({ version: 1 });
|
|
22
|
+
return json;
|
|
23
|
+
}
|
|
24
|
+
const newChange = createUpdateChangeExps(this.urls, this.map.values());
|
|
25
|
+
json.info.unshift({
|
|
26
|
+
version: json.info[0].version + 1,
|
|
27
|
+
change: [newChange]
|
|
28
|
+
});
|
|
29
|
+
this.zipJson(json);
|
|
30
|
+
this.limitJson(json);
|
|
31
|
+
return json;
|
|
32
|
+
}
|
|
33
|
+
zipJson(json) {
|
|
34
|
+
const matchUpdateRule = this.compilation.crossDep.read('matchUpdateRule');
|
|
35
|
+
const htmlMatcher = matchUpdateRule.runOnNode({ flag: 'html' });
|
|
36
|
+
// 打散新版本的所有规则
|
|
37
|
+
json.info[0].change = json.info[0].change?.flatMap(exp => {
|
|
38
|
+
if (!Array.isArray(exp.value))
|
|
39
|
+
return [exp];
|
|
40
|
+
return exp.value.map(it => ({
|
|
41
|
+
flag: exp.flag, value: it
|
|
42
|
+
}));
|
|
43
|
+
});
|
|
44
|
+
// 压缩第一个版本的内容
|
|
45
|
+
const indexes = (() => {
|
|
46
|
+
const change = json.info[0].change;
|
|
47
|
+
if (!change)
|
|
48
|
+
return new Set();
|
|
49
|
+
let htmlCount = 0;
|
|
50
|
+
const indexes = new Set();
|
|
51
|
+
// 统计每个表达式匹配的资源及刷新的 HTML 总量
|
|
52
|
+
const indexesArray = change.map(exp => {
|
|
53
|
+
const matcher = matchUpdateRule.runOnNode(exp);
|
|
54
|
+
const result = new Set();
|
|
55
|
+
untils_1.utils.findValueInIterable(this.urls, it => !!matcher(it))
|
|
56
|
+
.forEach(item => {
|
|
57
|
+
indexes.add(item.index);
|
|
58
|
+
result.add(item.index);
|
|
59
|
+
if (htmlMatcher(item.value))
|
|
60
|
+
++htmlCount;
|
|
61
|
+
});
|
|
62
|
+
return result;
|
|
63
|
+
});
|
|
64
|
+
// 如果 HTML 更新数量超过阈值,则直接清除所有 HTML 的缓存
|
|
65
|
+
if (htmlCount > 0) {
|
|
66
|
+
const htmlLimit = this.compilation.compilationEnv.read('JSON_HTML_LIMIT');
|
|
67
|
+
if (htmlLimit > 0 && htmlCount > htmlLimit) {
|
|
68
|
+
change.unshift({ flag: 'html' });
|
|
69
|
+
const indexes = new Set();
|
|
70
|
+
untils_1.utils.findValueInIterable(this.urls, it => !!htmlMatcher(it))
|
|
71
|
+
.forEach(({ index }) => indexes.add(index));
|
|
72
|
+
indexesArray.unshift(indexes);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// 分析哪些表达式是冗余的
|
|
76
|
+
const invalidIndex = new Array(indexesArray.length);
|
|
77
|
+
for (let i = 0; i < indexesArray.length; i++) {
|
|
78
|
+
if (invalidIndex[i])
|
|
79
|
+
continue;
|
|
80
|
+
const parent = indexesArray[0];
|
|
81
|
+
o: for (let k = 0; k < indexesArray.length; k++) {
|
|
82
|
+
if (i == k || invalidIndex[k])
|
|
83
|
+
continue;
|
|
84
|
+
for (let item of indexesArray[k]) {
|
|
85
|
+
if (!parent.has(item)) {
|
|
86
|
+
continue o;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
invalidIndex[k] = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// 生成新的表达式
|
|
93
|
+
const validExp = new Map();
|
|
94
|
+
for (let i = 0; i < invalidIndex.length; i++) {
|
|
95
|
+
if (invalidIndex[i])
|
|
96
|
+
continue;
|
|
97
|
+
const oldExpList = validExp.get(change[i].flag);
|
|
98
|
+
const expList = oldExpList ?? [];
|
|
99
|
+
if (change[i].value) {
|
|
100
|
+
console.assert(typeof change[i].value == 'string', `change[${i}].value = ${change[i].value} 应当为字符串`);
|
|
101
|
+
expList.push(change[i].value);
|
|
102
|
+
}
|
|
103
|
+
if (!oldExpList)
|
|
104
|
+
validExp.set(change[i].flag, expList);
|
|
105
|
+
}
|
|
106
|
+
const newChange = json.info[0].change = [];
|
|
107
|
+
validExp.forEach((value, flag) => {
|
|
108
|
+
switch (value.length) {
|
|
109
|
+
case 0:
|
|
110
|
+
newChange.push({ flag });
|
|
111
|
+
break;
|
|
112
|
+
case 1:
|
|
113
|
+
newChange.push({ flag, value: value[0] });
|
|
114
|
+
break;
|
|
115
|
+
default:
|
|
116
|
+
newChange.push({ flag, value });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return indexes;
|
|
120
|
+
})();
|
|
121
|
+
// 移除后续表达式中冗余的内容
|
|
122
|
+
if (indexes.size == 0)
|
|
123
|
+
return;
|
|
124
|
+
for (let i = 1; i < json.info.length; i++) {
|
|
125
|
+
const changes = json.info[i].change;
|
|
126
|
+
if (!changes)
|
|
127
|
+
continue;
|
|
128
|
+
for (let k = changes.length - 1; k >= 0; k--) {
|
|
129
|
+
const change = json.info[i].change[k];
|
|
130
|
+
const values = change.value ? (Array.isArray(change.value) ? change.value : [change.value]) : [];
|
|
131
|
+
const tmpChange = {
|
|
132
|
+
flag: change.flag,
|
|
133
|
+
value: ''
|
|
134
|
+
};
|
|
135
|
+
for (let j = values.length - 1; j >= 0; j--) {
|
|
136
|
+
tmpChange.value = values[j];
|
|
137
|
+
const matcher = matchUpdateRule.runOnNode(tmpChange);
|
|
138
|
+
const matchIndex = untils_1.utils.findValueInIterable(this.urls, url => !!matcher(url));
|
|
139
|
+
if (matchIndex.every(it => indexes.has(it.index))) {
|
|
140
|
+
values.splice(j, 1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (values.length == 0)
|
|
144
|
+
delete json.info[i].change;
|
|
145
|
+
else if (values.length == 1)
|
|
146
|
+
change.value = values[0];
|
|
147
|
+
else
|
|
148
|
+
change.value = values;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
limitJson(json) {
|
|
153
|
+
const lengthLimit = this.compilation.compilationEnv.read('VERSION_LENGTH_LIMIT');
|
|
154
|
+
if (lengthLimit == 0)
|
|
155
|
+
return;
|
|
156
|
+
let sum = 0;
|
|
157
|
+
for (let i = 0; i < json.info.length; i++) {
|
|
158
|
+
sum += JSON.stringify(json.info[i]).length;
|
|
159
|
+
if (sum > lengthLimit) {
|
|
160
|
+
if (i == 0)
|
|
161
|
+
json.info = [{ version: json.info[0].version }];
|
|
162
|
+
else
|
|
163
|
+
json.info.splice(i);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.JsonBuilder = JsonBuilder;
|
|
170
|
+
/**
|
|
171
|
+
* 构建更新表达式
|
|
172
|
+
*
|
|
173
|
+
* 具体实现为使用字典树构建最优后缀匹配表达式,时间复杂度 O(N + M)
|
|
174
|
+
*/
|
|
175
|
+
function createUpdateChangeExps(urls, refresh) {
|
|
176
|
+
function newNode() {
|
|
177
|
+
return {
|
|
178
|
+
next: new Array(128),
|
|
179
|
+
flag: false,
|
|
180
|
+
isEnd: false
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const head = newNode();
|
|
184
|
+
const insert = (content, flag) => {
|
|
185
|
+
let cur = head;
|
|
186
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
187
|
+
const index = content.charCodeAt(i);
|
|
188
|
+
if (cur.next[index]) {
|
|
189
|
+
cur = cur.next[index];
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
cur = cur.next[index] = newNode();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
cur.flag = flag;
|
|
196
|
+
cur.isEnd = true;
|
|
197
|
+
};
|
|
198
|
+
urls.forEach(it => insert(it, false));
|
|
199
|
+
for (let item of refresh) {
|
|
200
|
+
insert(item, true);
|
|
201
|
+
}
|
|
202
|
+
function dfs(node) {
|
|
203
|
+
if (node.isEnd)
|
|
204
|
+
return;
|
|
205
|
+
node.flag = true;
|
|
206
|
+
for (let next of node.next) {
|
|
207
|
+
if (next) {
|
|
208
|
+
dfs(next);
|
|
209
|
+
node.flag = node.flag && next.flag;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
dfs(head);
|
|
214
|
+
let dfs2S = [];
|
|
215
|
+
const result = [];
|
|
216
|
+
function dfs2(node) {
|
|
217
|
+
if (node.flag) {
|
|
218
|
+
result.push(dfs2S.reduceRight((a, b) => a + b, ''));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
for (let i = 0; i < node.next.length; i++) {
|
|
222
|
+
const next = node.next[i];
|
|
223
|
+
if (next) {
|
|
224
|
+
dfs2S.push(String.fromCharCode(i));
|
|
225
|
+
dfs2(next);
|
|
226
|
+
dfs2S.pop();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
dfs2(head);
|
|
231
|
+
return { flag: 'suf', value: result };
|
|
232
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FiniteConcurrencyFetcher = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
/** 支持并发控制的网络文件拉取工具 */
|
|
9
|
+
class FiniteConcurrencyFetcher {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.fetchingCount = 0;
|
|
12
|
+
this.waitList = [];
|
|
13
|
+
this.limit = 100;
|
|
14
|
+
this.referer = 'swpp-backends';
|
|
15
|
+
this.userAgent = 'swpp-backends';
|
|
16
|
+
this.headers = {};
|
|
17
|
+
}
|
|
18
|
+
fetch(request) {
|
|
19
|
+
if (this.fetchingCount !== this.limit) {
|
|
20
|
+
return this.createFetchTask(request);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
this.waitList.push({ request, resolve, reject });
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async createFetchTask(url) {
|
|
29
|
+
++this.fetchingCount;
|
|
30
|
+
try {
|
|
31
|
+
return await fetch(url, {
|
|
32
|
+
referrer: this.referer,
|
|
33
|
+
headers: {
|
|
34
|
+
...this.headers,
|
|
35
|
+
'User-Agent': this.userAgent
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
--this.fetchingCount;
|
|
41
|
+
if (this.waitList.length !== 0) {
|
|
42
|
+
const item = this.waitList.pop();
|
|
43
|
+
this.createFetchTask(item.request)
|
|
44
|
+
.then(response => item.resolve(response))
|
|
45
|
+
.catch(err => item.reject(err));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
getUrlContentType(url, response) {
|
|
50
|
+
let contentType;
|
|
51
|
+
if (url.endsWith('/')) {
|
|
52
|
+
contentType = 'html';
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
contentType = path_1.default.extname(url);
|
|
56
|
+
}
|
|
57
|
+
if (!contentType) {
|
|
58
|
+
if (response)
|
|
59
|
+
contentType = response.headers.get('content-type') ?? '';
|
|
60
|
+
if (contentType.startsWith('text/'))
|
|
61
|
+
contentType = contentType.substring(5);
|
|
62
|
+
if (contentType === 'javascript')
|
|
63
|
+
contentType = 'script';
|
|
64
|
+
}
|
|
65
|
+
return contentType;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.FiniteConcurrencyFetcher = FiniteConcurrencyFetcher;
|