@ims-view/server 1.2.0 → 1.2.2
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 +20 -1
- package/lib/excel/excel-export.worker.cjs +70 -0
- package/lib/excel/excel-parse.worker.cjs +114 -0
- package/lib/server/src/app.module.js.map +1 -0
- package/lib/server/src/config/env.d.ts +31 -0
- package/lib/server/src/config/env.js +32 -0
- package/lib/server/src/config/env.js.map +1 -0
- package/lib/server/src/excel/dto/excel-task-id.dto.d.ts +19 -0
- package/lib/server/src/excel/dto/excel-task-id.dto.js +17 -0
- package/lib/server/src/excel/dto/excel-task-id.dto.js.map +1 -0
- package/lib/server/src/excel/dto/export-excel.dto.d.ts +67 -0
- package/lib/server/src/excel/dto/export-excel.dto.js +48 -0
- package/lib/server/src/excel/dto/export-excel.dto.js.map +1 -0
- package/lib/server/src/excel/excel-storage.service.d.ts +54 -0
- package/lib/server/src/excel/excel-storage.service.js +267 -0
- package/lib/server/src/excel/excel-storage.service.js.map +1 -0
- package/lib/server/src/excel/excel.controller.d.ts +17 -0
- package/lib/{excel → server/src/excel}/excel.controller.js +40 -11
- package/lib/server/src/excel/excel.controller.js.map +1 -0
- package/lib/server/src/excel/excel.converter.d.ts +15 -0
- package/lib/server/src/excel/excel.converter.js +53 -0
- package/lib/server/src/excel/excel.converter.js.map +1 -0
- package/lib/{excel → server/src/excel}/excel.module.js +3 -2
- package/lib/server/src/excel/excel.module.js.map +1 -0
- package/lib/server/src/excel/excel.service.d.ts +33 -0
- package/lib/server/src/excel/excel.service.js +390 -0
- package/lib/server/src/excel/excel.service.js.map +1 -0
- package/lib/server/src/excel/types.d.ts +6 -0
- package/lib/server/src/excel/types.js +44 -0
- package/lib/server/src/excel/types.js.map +1 -0
- package/lib/server/src/index.d.ts +12 -0
- package/lib/server/src/index.js +32 -0
- package/lib/server/src/index.js.map +1 -0
- package/lib/server/src/main.d.ts +1 -0
- package/lib/{main.js → server/src/main.js} +58 -2
- package/lib/server/src/main.js.map +1 -0
- package/lib/server/src/shared/create-zod-base-model.d.ts +29 -0
- package/lib/server/src/shared/create-zod-base-model.js +55 -0
- package/lib/server/src/shared/create-zod-base-model.js.map +1 -0
- package/lib/server/src/shared/index.d.ts +5 -0
- package/lib/server/src/shared/index.js +16 -0
- package/lib/server/src/shared/index.js.map +1 -0
- package/lib/server/src/shared/response-entity.d.ts +32 -0
- package/lib/server/src/shared/response-entity.js +74 -0
- package/lib/server/src/shared/response-entity.js.map +1 -0
- package/lib/server/src/shared/zod-exception.filter.d.ts +5 -0
- package/lib/server/src/shared/zod-exception.filter.js +25 -0
- package/lib/server/src/shared/zod-exception.filter.js.map +1 -0
- package/lib/server/src/shared/zod-validation.pipe.d.ts +4 -0
- package/lib/server/src/shared/zod-validation.pipe.js +25 -0
- package/lib/server/src/shared/zod-validation.pipe.js.map +1 -0
- package/lib/utils/src/excel/converter.d.ts +7 -0
- package/lib/utils/src/excel/converter.js +139 -0
- package/lib/utils/src/excel/converter.js.map +1 -0
- package/lib/utils/src/excel/exceljsChunked.d.ts +2 -0
- package/lib/utils/src/excel/exceljsChunked.js +16 -0
- package/lib/utils/src/excel/exceljsChunked.js.map +1 -0
- package/lib/utils/src/excel/exceljsDrawings.d.ts +2 -0
- package/lib/utils/src/excel/exceljsDrawings.js +8 -0
- package/lib/utils/src/excel/exceljsDrawings.js.map +1 -0
- package/lib/utils/src/excel/exceljsStyle.d.ts +2 -0
- package/lib/utils/src/excel/exceljsStyle.js +10 -0
- package/lib/utils/src/excel/exceljsStyle.js.map +1 -0
- package/lib/utils/src/excel/exportWorkerClient.d.ts +31 -0
- package/lib/utils/src/excel/exportWorkerClient.js +105 -0
- package/lib/utils/src/excel/exportWorkerClient.js.map +1 -0
- package/lib/utils/src/excel/index.d.ts +15 -0
- package/lib/utils/src/excel/index.js +57 -0
- package/lib/utils/src/excel/index.js.map +1 -0
- package/lib/utils/src/excel/luckyexcel.d.ts +13 -0
- package/lib/utils/src/excel/luckyexcel.js +211 -0
- package/lib/utils/src/excel/luckyexcel.js.map +1 -0
- package/lib/utils/src/excel/sheetjsConverter.d.ts +6 -0
- package/lib/utils/src/excel/sheetjsConverter.js +224 -0
- package/lib/utils/src/excel/sheetjsConverter.js.map +1 -0
- package/lib/utils/src/excel/types.d.ts +88 -0
- package/lib/utils/src/excel/types.js +11 -0
- package/lib/utils/src/excel/types.js.map +1 -0
- package/lib/utils/src/excel/workbookChunked.d.ts +13 -0
- package/lib/utils/src/excel/workbookChunked.js +152 -0
- package/lib/utils/src/excel/workbookChunked.js.map +1 -0
- package/package.json +12 -6
- package/lib/app.module.js.map +0 -1
- package/lib/excel/excel.controller.d.ts +0 -13
- package/lib/excel/excel.controller.js.map +0 -1
- package/lib/excel/excel.converter.d.ts +0 -2
- package/lib/excel/excel.converter.js +0 -11
- package/lib/excel/excel.converter.js.map +0 -1
- package/lib/excel/excel.module.js.map +0 -1
- package/lib/excel/excel.service.d.ts +0 -9
- package/lib/excel/excel.service.js +0 -33
- package/lib/excel/excel.service.js.map +0 -1
- package/lib/excel/types.d.ts +0 -6
- package/lib/excel/types.js +0 -6
- package/lib/excel/types.js.map +0 -1
- package/lib/index.d.ts +0 -7
- package/lib/index.js +0 -14
- package/lib/index.js.map +0 -1
- package/lib/main.d.ts +0 -1
- package/lib/main.js.map +0 -1
- /package/lib/{app.module.d.ts → server/src/app.module.d.ts} +0 -0
- /package/lib/{app.module.js → server/src/app.module.js} +0 -0
- /package/lib/{excel → server/src/excel}/excel.module.d.ts +0 -0
package/README.md
CHANGED
|
@@ -33,12 +33,16 @@ npx ims-view-server
|
|
|
33
33
|
IMS_SERVER_PORT=3010 npx ims-view-server
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
仓库内开发(默认热重载,改 `packages/server/src` / `packages/utils/src/excel` 会自动重启):
|
|
37
37
|
|
|
38
38
|
```shell
|
|
39
39
|
IMS_SERVER_PORT=3010 pnpm start:server
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
不需要热重载时:`pnpm --dir packages/server start:once`
|
|
43
|
+
|
|
44
|
+
请求体校验使用 Zod(`createZodBaseModel` + `ZodValidationPipe`),JSON 接口统一 `ResponseEntity`:`{ code, data, msg }`(`code === 0` 成功)。`POST /excel/export` 返回 xlsx 二进制,内部走 **ExcelJS `worker_threads`**(不堵 Nest 主线程)。
|
|
45
|
+
|
|
42
46
|
## 嵌入 Nest 应用
|
|
43
47
|
|
|
44
48
|
```ts
|
|
@@ -57,9 +61,24 @@ export class AppModule {}
|
|
|
57
61
|
| ---- | ---- | ---- |
|
|
58
62
|
| `IMS_SERVER_PORT` | 服务端口(优先) | `3010` |
|
|
59
63
|
| `PORT` | 兼容通用端口变量 | - |
|
|
64
|
+
| `IMS_SERVER_BODY_LIMIT` | JSON body 体积上限 | `50mb` |
|
|
65
|
+
| `IMS_SERVER_GZIP_THRESHOLD` | 响应 gzip 压缩阈值(字节) | `1024` |
|
|
66
|
+
| `IMS_EXCEL_UPLOAD_DIR` | 上传文件目录 | `os.tmpdir()/ims-excel-uploads` |
|
|
67
|
+
| `IMS_EXCEL_UPLOAD_TTL_MS` | 上传文件保留时长 | `3600000`(1h) |
|
|
68
|
+
| `IMS_EXCEL_UPLOAD_CLEANUP_MS` | 定时清理间隔 | `600000`(10min) |
|
|
69
|
+
| `IMS_EXCEL_PARSE_TIMEOUT_MS` | 单次解析 / 导出超时 | `600000`(10min) |
|
|
60
70
|
|
|
61
71
|
前端对接:`IMS_EXCHANGE_ENDPOINT=http://localhost:3010`
|
|
62
72
|
|
|
73
|
+
## 导入策略
|
|
74
|
+
|
|
75
|
+
参考 [CasualOffice/sheets large-file pipeline](https://github.com/CasualOffice/sheets/blob/main/docs/LARGE_FILE_PIPELINE.md):解析放 Worker,前端渐进挂载。
|
|
76
|
+
|
|
77
|
+
- 一律 **ExcelJS Worker** → meta + blocks(不堵 Nest,有进度日志)
|
|
78
|
+
- 目标:约 **10 万行级 xlsx,解析完成约 10s 内**
|
|
79
|
+
- 样式:默认映射(字体/填充/边框/对齐/数字格式/锁定 `lo` + `meta.styles` intern)
|
|
80
|
+
- 另支持:浮动图(`SHEET_DRAWING_PLUGIN`)、冻结窗格(`sheet.freeze`)
|
|
81
|
+
- **不做**:条件格式、数据验证、图表、EMF 等非位图
|
|
63
82
|
## API
|
|
64
83
|
|
|
65
84
|
见文档站:
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nest 导出 Worker(纯 CJS):LuckyExcel Univer→xlsx(与浏览器同一套),不堵 HTTP 主线程。
|
|
3
|
+
* workerData: { snapshotPath, outPath, fileName }
|
|
4
|
+
*/
|
|
5
|
+
const { parentPort, workerData } = require('worker_threads');
|
|
6
|
+
const { readFile, writeFile } = require('fs/promises');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { createRequire } = require('module');
|
|
9
|
+
|
|
10
|
+
const requireFromHere = createRequire(__filename);
|
|
11
|
+
|
|
12
|
+
const resolveUtilsExcel = () => {
|
|
13
|
+
const candidates = [
|
|
14
|
+
() => requireFromHere.resolve('@ims-view/utils'),
|
|
15
|
+
() =>
|
|
16
|
+
requireFromHere.resolve('@ims-view/utils', {
|
|
17
|
+
paths: [
|
|
18
|
+
path.resolve(__dirname, '../../..'),
|
|
19
|
+
path.resolve(__dirname, '../../../../node_modules'),
|
|
20
|
+
],
|
|
21
|
+
}),
|
|
22
|
+
];
|
|
23
|
+
for (const tryResolve of candidates) {
|
|
24
|
+
try {
|
|
25
|
+
const id = tryResolve();
|
|
26
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
27
|
+
return require(id);
|
|
28
|
+
} catch {
|
|
29
|
+
// continue
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
throw new Error('无法解析 @ims-view/utils,请先构建 packages/utils');
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const post = (msg) => parentPort && parentPort.postMessage(msg);
|
|
36
|
+
const log = (message) => post({ type: 'log', message });
|
|
37
|
+
|
|
38
|
+
(async () => {
|
|
39
|
+
const started = Date.now();
|
|
40
|
+
const { snapshotPath, outPath, fileName } = workerData || {};
|
|
41
|
+
if (!snapshotPath || !outPath) {
|
|
42
|
+
throw new Error('workerData 缺少 snapshotPath / outPath');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
log(`export worker start file=${fileName || path.basename(outPath)} engine=luckyexcel`);
|
|
46
|
+
const raw = await readFile(snapshotPath, 'utf8');
|
|
47
|
+
const snapshot = JSON.parse(raw);
|
|
48
|
+
const utils = resolveUtilsExcel();
|
|
49
|
+
const workbookDataToExcelBuffer = utils.workbookDataToExcelBuffer;
|
|
50
|
+
if (typeof workbookDataToExcelBuffer !== 'function') {
|
|
51
|
+
throw new Error('@ims-view/utils 未导出 workbookDataToExcelBuffer');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const buffer = await workbookDataToExcelBuffer(snapshot, fileName || path.basename(outPath));
|
|
55
|
+
await writeFile(outPath, buffer);
|
|
56
|
+
const costMs = Date.now() - started;
|
|
57
|
+
log(
|
|
58
|
+
`export worker done cost=${costMs}ms size=${(buffer.byteLength / 1024).toFixed(1)}KB`,
|
|
59
|
+
);
|
|
60
|
+
post({
|
|
61
|
+
type: 'done',
|
|
62
|
+
costMs,
|
|
63
|
+
size: buffer.byteLength,
|
|
64
|
+
outPath,
|
|
65
|
+
});
|
|
66
|
+
})().catch((error) => {
|
|
67
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
68
|
+
const stack = error instanceof Error ? error.stack : undefined;
|
|
69
|
+
post({ type: 'error', message, error: message, stack });
|
|
70
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker 入口(纯 CJS):调用 @ims-view/univer-import-excel 公共转换,本文件只负责读盘/写盘/进度上报。
|
|
3
|
+
* 与本地导入同一套 ExcelJS→Univer 映射(excelBufferToChunkedWorkbook)。
|
|
4
|
+
*/
|
|
5
|
+
const { parentPort, workerData } = require('worker_threads');
|
|
6
|
+
const { readFile, writeFile } = require('fs/promises');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { createRequire } = require('module');
|
|
9
|
+
|
|
10
|
+
const requireFromHere = createRequire(__filename);
|
|
11
|
+
|
|
12
|
+
const resolveImportExcel = () => {
|
|
13
|
+
const candidates = [
|
|
14
|
+
// monorepo 源码旁的构建产物(最稳)
|
|
15
|
+
() => path.resolve(__dirname, '../../../univer-import-excel/lib/index.js'),
|
|
16
|
+
() => requireFromHere.resolve('@ims-view/univer-import-excel'),
|
|
17
|
+
() =>
|
|
18
|
+
requireFromHere.resolve('@ims-view/univer-import-excel', {
|
|
19
|
+
paths: [
|
|
20
|
+
path.resolve(__dirname, '../../..'),
|
|
21
|
+
path.resolve(__dirname, '../../../../node_modules'),
|
|
22
|
+
],
|
|
23
|
+
}),
|
|
24
|
+
];
|
|
25
|
+
for (const tryResolve of candidates) {
|
|
26
|
+
try {
|
|
27
|
+
const id = tryResolve();
|
|
28
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
29
|
+
return require(id);
|
|
30
|
+
} catch {
|
|
31
|
+
// continue
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new Error('无法解析 @ims-view/univer-import-excel,请先在 packages/univer-import-excel 执行 pnpm build');
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
excelBufferToChunkedWorkbook,
|
|
39
|
+
chunkedBlockFileName,
|
|
40
|
+
chunkedMetaFileName,
|
|
41
|
+
} = resolveImportExcel();
|
|
42
|
+
|
|
43
|
+
const post = (msg) => parentPort && parentPort.postMessage(msg);
|
|
44
|
+
const log = (message) => post({ type: 'log', message });
|
|
45
|
+
|
|
46
|
+
(async () => {
|
|
47
|
+
const started = Date.now();
|
|
48
|
+
const {
|
|
49
|
+
id,
|
|
50
|
+
xlsxPath,
|
|
51
|
+
outDir: outDirRaw,
|
|
52
|
+
uploadDir,
|
|
53
|
+
fileName,
|
|
54
|
+
blockRowSize = 5000,
|
|
55
|
+
maxRows = 0,
|
|
56
|
+
includeStyles = true,
|
|
57
|
+
} = workerData || {};
|
|
58
|
+
const outDir = outDirRaw || uploadDir;
|
|
59
|
+
|
|
60
|
+
if (!id || !xlsxPath || !outDir) {
|
|
61
|
+
throw new Error('workerData 缺少 id / xlsxPath / outDir(uploadDir)');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
log(`worker start id=${id} file=${fileName || path.basename(xlsxPath)}`);
|
|
65
|
+
const buffer = await readFile(xlsxPath);
|
|
66
|
+
log(`read xlsx ${(buffer.byteLength / 1024 / 1024).toFixed(2)}MB cost=${Date.now() - started}ms`);
|
|
67
|
+
|
|
68
|
+
let emittedBlocks = 0;
|
|
69
|
+
|
|
70
|
+
const { meta } = await excelBufferToChunkedWorkbook(buffer, {
|
|
71
|
+
fileName: fileName || path.basename(xlsxPath),
|
|
72
|
+
blockRowSize,
|
|
73
|
+
maxRows,
|
|
74
|
+
includeStyles,
|
|
75
|
+
workbookId: `wb-chunked-${id}`,
|
|
76
|
+
onProgress: (progress) => {
|
|
77
|
+
post({
|
|
78
|
+
type: 'progress',
|
|
79
|
+
percent: progress.percent,
|
|
80
|
+
parsedBlocks: progress.parsedBlocks,
|
|
81
|
+
totalBlocks: progress.totalBlocks,
|
|
82
|
+
phase: progress.phase,
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
onBlock: async (block) => {
|
|
86
|
+
const name = chunkedBlockFileName(id, block.sheetIndex, block.blockIndex);
|
|
87
|
+
await writeFile(path.join(outDir, name), JSON.stringify(block), 'utf8');
|
|
88
|
+
emittedBlocks += 1;
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// onBlock 已写盘时 blocks 可能为空数组
|
|
93
|
+
const metaName = chunkedMetaFileName(id);
|
|
94
|
+
const metaPath = path.join(outDir, metaName);
|
|
95
|
+
await writeFile(metaPath, JSON.stringify(meta), 'utf8');
|
|
96
|
+
|
|
97
|
+
const costMs = Date.now() - started;
|
|
98
|
+
log(
|
|
99
|
+
`done id=${id} cost=${costMs}ms blocks=${emittedBlocks} styles=${Object.keys(meta.styles || {}).length} truncated=${Boolean(meta.truncated)}`,
|
|
100
|
+
);
|
|
101
|
+
post({
|
|
102
|
+
type: 'done',
|
|
103
|
+
costMs,
|
|
104
|
+
parsedBlocks: emittedBlocks,
|
|
105
|
+
totalBlocks: emittedBlocks,
|
|
106
|
+
truncated: Boolean(meta.truncated),
|
|
107
|
+
metaFile: metaName,
|
|
108
|
+
metaFileName: metaName,
|
|
109
|
+
});
|
|
110
|
+
})().catch((error) => {
|
|
111
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
112
|
+
const stack = error instanceof Error ? error.stack : undefined;
|
|
113
|
+
post({ type: 'error', message, error: message, stack });
|
|
114
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../../../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uDAAmD;AAK5C,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAHrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,SAAS,CAAG"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const serverEnvSchema: z.ZodObject<{
|
|
3
|
+
IMS_SERVER_PORT: z.ZodDefault<z.ZodNumber>;
|
|
4
|
+
PORT: z.ZodEffects<z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>, number | undefined, string | number | undefined>;
|
|
5
|
+
IMS_SERVER_BODY_LIMIT: z.ZodDefault<z.ZodString>;
|
|
6
|
+
IMS_SERVER_GZIP_THRESHOLD: z.ZodDefault<z.ZodNumber>;
|
|
7
|
+
IMS_EXCEL_UPLOAD_DIR: z.ZodOptional<z.ZodString>;
|
|
8
|
+
IMS_EXCEL_UPLOAD_TTL_MS: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
IMS_EXCEL_UPLOAD_CLEANUP_MS: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
IMS_EXCEL_PARSE_TIMEOUT_MS: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
IMS_EXCEL_UPLOAD_TTL_MS: number;
|
|
13
|
+
IMS_EXCEL_UPLOAD_CLEANUP_MS: number;
|
|
14
|
+
IMS_EXCEL_PARSE_TIMEOUT_MS: number;
|
|
15
|
+
IMS_SERVER_PORT: number;
|
|
16
|
+
IMS_SERVER_BODY_LIMIT: string;
|
|
17
|
+
IMS_SERVER_GZIP_THRESHOLD: number;
|
|
18
|
+
IMS_EXCEL_UPLOAD_DIR?: string | undefined;
|
|
19
|
+
PORT?: number | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
IMS_EXCEL_UPLOAD_DIR?: string | undefined;
|
|
22
|
+
IMS_EXCEL_UPLOAD_TTL_MS?: number | undefined;
|
|
23
|
+
IMS_EXCEL_UPLOAD_CLEANUP_MS?: number | undefined;
|
|
24
|
+
IMS_EXCEL_PARSE_TIMEOUT_MS?: number | undefined;
|
|
25
|
+
IMS_SERVER_PORT?: number | undefined;
|
|
26
|
+
PORT?: string | number | undefined;
|
|
27
|
+
IMS_SERVER_BODY_LIMIT?: string | undefined;
|
|
28
|
+
IMS_SERVER_GZIP_THRESHOLD?: number | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
export type ServerEnv = z.infer<typeof serverEnvSchema>;
|
|
31
|
+
export declare const loadServerEnv: (env?: NodeJS.ProcessEnv) => ServerEnv;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadServerEnv = exports.serverEnvSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
exports.serverEnvSchema = zod_1.z.object({
|
|
6
|
+
IMS_SERVER_PORT: zod_1.z.coerce.number().int().positive().default(3010),
|
|
7
|
+
PORT: zod_1.z
|
|
8
|
+
.union([zod_1.z.string(), zod_1.z.number()])
|
|
9
|
+
.optional()
|
|
10
|
+
.transform((value) => {
|
|
11
|
+
if (value === undefined || value === '')
|
|
12
|
+
return undefined;
|
|
13
|
+
const num = typeof value === 'number' ? value : Number(value);
|
|
14
|
+
return Number.isFinite(num) && num > 0 ? num : undefined;
|
|
15
|
+
}),
|
|
16
|
+
IMS_SERVER_BODY_LIMIT: zod_1.z.string().default('50mb'),
|
|
17
|
+
IMS_SERVER_GZIP_THRESHOLD: zod_1.z.coerce.number().int().nonnegative().default(1024),
|
|
18
|
+
IMS_EXCEL_UPLOAD_DIR: zod_1.z.string().optional(),
|
|
19
|
+
IMS_EXCEL_UPLOAD_TTL_MS: zod_1.z.coerce.number().int().positive().default(60 * 60 * 1000),
|
|
20
|
+
IMS_EXCEL_UPLOAD_CLEANUP_MS: zod_1.z.coerce.number().int().positive().default(10 * 60 * 1000),
|
|
21
|
+
IMS_EXCEL_PARSE_TIMEOUT_MS: zod_1.z.coerce.number().int().positive().default(10 * 60 * 1000),
|
|
22
|
+
});
|
|
23
|
+
const loadServerEnv = (env = process.env) => {
|
|
24
|
+
const result = exports.serverEnvSchema.safeParse(env);
|
|
25
|
+
if (!result.success) {
|
|
26
|
+
const message = result.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ');
|
|
27
|
+
throw new Error(`[@ims-view/server] 环境变量校验失败: ${message}`);
|
|
28
|
+
}
|
|
29
|
+
return result.data;
|
|
30
|
+
};
|
|
31
|
+
exports.loadServerEnv = loadServerEnv;
|
|
32
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../../../src/config/env.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAQX,QAAA,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,eAAe,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACjE,IAAI,EAAE,OAAC;SACJ,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC;SAC/B,QAAQ,EAAE;SACV,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QACnB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,CAAC,CAAC;IACJ,qBAAqB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACjD,yBAAyB,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9E,oBAAoB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3C,uBAAuB,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACnF,2BAA2B,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAEvF,0BAA0B,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CACvF,CAAC,CAAC;AAII,MAAM,aAAa,GAAG,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAa,EAAE;IAC/E,MAAM,MAAM,GAAG,uBAAe,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/F,MAAM,IAAI,KAAK,CAAC,gCAAgC,OAAO,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC,CAAC;AAPW,QAAA,aAAa,iBAOxB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const excelTaskIdSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodString;
|
|
4
|
+
}, "strip", z.ZodTypeAny, {
|
|
5
|
+
id: string;
|
|
6
|
+
}, {
|
|
7
|
+
id: string;
|
|
8
|
+
}>;
|
|
9
|
+
declare const ExcelTaskIdDto_base: import("../../shared/create-zod-base-model").ZodBaseModel<z.ZodObject<{
|
|
10
|
+
id: z.ZodString;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
id: string;
|
|
13
|
+
}, {
|
|
14
|
+
id: string;
|
|
15
|
+
}>>;
|
|
16
|
+
export declare class ExcelTaskIdDto extends ExcelTaskIdDto_base {
|
|
17
|
+
}
|
|
18
|
+
export type ExcelTaskIdPlain = z.infer<typeof excelTaskIdSchema>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExcelTaskIdDto = exports.excelTaskIdSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const create_zod_base_model_1 = require("../../shared/create-zod-base-model");
|
|
6
|
+
exports.excelTaskIdSchema = zod_1.z.object({
|
|
7
|
+
id: zod_1.z
|
|
8
|
+
.string()
|
|
9
|
+
.trim()
|
|
10
|
+
.min(1, '任务 id 不能为空')
|
|
11
|
+
.regex(/^[a-zA-Z0-9_-]+$/, '任务 id 格式无效')
|
|
12
|
+
.describe('解析任务 id'),
|
|
13
|
+
});
|
|
14
|
+
class ExcelTaskIdDto extends (0, create_zod_base_model_1.createZodBaseModel)(exports.excelTaskIdSchema) {
|
|
15
|
+
}
|
|
16
|
+
exports.ExcelTaskIdDto = ExcelTaskIdDto;
|
|
17
|
+
//# sourceMappingURL=excel-task-id.dto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excel-task-id.dto.js","sourceRoot":"","sources":["../../../../../src/excel/dto/excel-task-id.dto.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,8EAAwE;AAE3D,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,EAAE,EAAE,OAAC;SACF,MAAM,EAAE;SACR,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC;SACpB,KAAK,CAAC,kBAAkB,EAAE,YAAY,CAAC;SACvC,QAAQ,CAAC,SAAS,CAAC;CACvB,CAAC,CAAC;AAEH,MAAa,cAAe,SAAQ,IAAA,0CAAkB,EAAC,yBAAiB,CAAC;CAAG;AAA5E,wCAA4E"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const exportExcelSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
3
|
+
data: z.ZodOptional<z.ZodAny>;
|
|
4
|
+
fileName: z.ZodOptional<z.ZodString>;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
data?: any;
|
|
7
|
+
fileName?: string | undefined;
|
|
8
|
+
}, {
|
|
9
|
+
data?: any;
|
|
10
|
+
fileName?: string | undefined;
|
|
11
|
+
}>, {
|
|
12
|
+
data?: any;
|
|
13
|
+
fileName?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
data?: any;
|
|
16
|
+
fileName?: string | undefined;
|
|
17
|
+
}>, {
|
|
18
|
+
fileName: string | undefined;
|
|
19
|
+
data: z.objectOutputType<{
|
|
20
|
+
id: z.ZodOptional<z.ZodString>;
|
|
21
|
+
name: z.ZodOptional<z.ZodString>;
|
|
22
|
+
appVersion: z.ZodOptional<z.ZodString>;
|
|
23
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
24
|
+
sheetOrder: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
25
|
+
sheets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
26
|
+
styles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
27
|
+
resources: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
|
|
28
|
+
}, z.ZodTypeAny, "passthrough">;
|
|
29
|
+
}, {
|
|
30
|
+
data?: any;
|
|
31
|
+
fileName?: string | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
declare const ExportExcelDto_base: import("../../shared/create-zod-base-model").ZodBaseModel<z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
34
|
+
data: z.ZodOptional<z.ZodAny>;
|
|
35
|
+
fileName: z.ZodOptional<z.ZodString>;
|
|
36
|
+
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
data?: any;
|
|
38
|
+
fileName?: string | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
data?: any;
|
|
41
|
+
fileName?: string | undefined;
|
|
42
|
+
}>, {
|
|
43
|
+
data?: any;
|
|
44
|
+
fileName?: string | undefined;
|
|
45
|
+
}, {
|
|
46
|
+
data?: any;
|
|
47
|
+
fileName?: string | undefined;
|
|
48
|
+
}>, {
|
|
49
|
+
fileName: string | undefined;
|
|
50
|
+
data: z.objectOutputType<{
|
|
51
|
+
id: z.ZodOptional<z.ZodString>;
|
|
52
|
+
name: z.ZodOptional<z.ZodString>;
|
|
53
|
+
appVersion: z.ZodOptional<z.ZodString>;
|
|
54
|
+
locale: z.ZodOptional<z.ZodString>;
|
|
55
|
+
sheetOrder: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
56
|
+
sheets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
57
|
+
styles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
58
|
+
resources: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
|
|
59
|
+
}, z.ZodTypeAny, "passthrough">;
|
|
60
|
+
}, {
|
|
61
|
+
data?: any;
|
|
62
|
+
fileName?: string | undefined;
|
|
63
|
+
}>>;
|
|
64
|
+
export declare class ExportExcelDto extends ExportExcelDto_base {
|
|
65
|
+
}
|
|
66
|
+
export type ExportExcelPlain = z.infer<typeof exportExcelSchema>;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExportExcelDto = exports.exportExcelSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const create_zod_base_model_1 = require("../../shared/create-zod-base-model");
|
|
6
|
+
const workbookDataSchema = zod_1.z
|
|
7
|
+
.object({
|
|
8
|
+
id: zod_1.z.string().optional(),
|
|
9
|
+
name: zod_1.z.string().optional(),
|
|
10
|
+
appVersion: zod_1.z.string().optional(),
|
|
11
|
+
locale: zod_1.z.string().optional(),
|
|
12
|
+
sheetOrder: zod_1.z.array(zod_1.z.string()).optional(),
|
|
13
|
+
sheets: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
14
|
+
styles: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
15
|
+
resources: zod_1.z.array(zod_1.z.any()).optional(),
|
|
16
|
+
})
|
|
17
|
+
.passthrough();
|
|
18
|
+
exports.exportExcelSchema = zod_1.z
|
|
19
|
+
.object({
|
|
20
|
+
data: zod_1.z.any().optional().describe('工作簿 snapshot'),
|
|
21
|
+
fileName: zod_1.z.string().trim().min(1, '文件名不能为空').optional().describe('导出文件名'),
|
|
22
|
+
})
|
|
23
|
+
.superRefine((value, ctx) => {
|
|
24
|
+
if (value.data == null || typeof value.data !== 'object' || Array.isArray(value.data)) {
|
|
25
|
+
ctx.addIssue({
|
|
26
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
27
|
+
path: ['data'],
|
|
28
|
+
message: '缺少工作簿数据 data',
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const parsed = workbookDataSchema.safeParse(value.data);
|
|
33
|
+
if (!parsed.success) {
|
|
34
|
+
ctx.addIssue({
|
|
35
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
36
|
+
path: ['data'],
|
|
37
|
+
message: parsed.error.issues[0]?.message || 'data 格式无效',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.transform((value) => ({
|
|
42
|
+
fileName: value.fileName,
|
|
43
|
+
data: workbookDataSchema.parse(value.data),
|
|
44
|
+
}));
|
|
45
|
+
class ExportExcelDto extends (0, create_zod_base_model_1.createZodBaseModel)(exports.exportExcelSchema) {
|
|
46
|
+
}
|
|
47
|
+
exports.ExportExcelDto = ExportExcelDto;
|
|
48
|
+
//# sourceMappingURL=export-excel.dto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-excel.dto.js","sourceRoot":"","sources":["../../../../../src/excel/dto/export-excel.dto.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,8EAAwE;AAExE,MAAM,kBAAkB,GAAG,OAAC;KACzB,MAAM,CAAC;IACN,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC1C,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE;CACvC,CAAC;KACD,WAAW,EAAE,CAAC;AAGJ,QAAA,iBAAiB,GAAG,OAAC;KAC/B,MAAM,CAAC;IACN,IAAI,EAAE,OAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;IACjD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;CAC3E,CAAC;KACD,WAAW,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IAC1B,IAAI,KAAK,CAAC,IAAI,IAAI,IAAI,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtF,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,OAAO,EAAE,cAAc;SACxB,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,WAAW;SACxD,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC;KACD,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC,QAAQ;IACxB,IAAI,EAAE,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;CAC3C,CAAC,CAAC,CAAC;AAEN,MAAa,cAAe,SAAQ,IAAA,0CAAkB,EAAC,yBAAiB,CAAC;CAAG;AAA5E,wCAA4E"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
|
2
|
+
export declare const getExcelUploadDir: () => string;
|
|
3
|
+
export declare const EXCEL_STATIC_PREFIX = "/excel/static";
|
|
4
|
+
export declare const preserveUploadFileName: (raw?: string) => string;
|
|
5
|
+
export type StoredExcelFile = {
|
|
6
|
+
id: string;
|
|
7
|
+
fileName: string;
|
|
8
|
+
path: string;
|
|
9
|
+
size: number;
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
};
|
|
12
|
+
export type ExcelParseTaskStatus = 'pending' | 'done' | 'error';
|
|
13
|
+
export type ExcelParseMode = 'snapshot' | 'chunked';
|
|
14
|
+
export type ExcelParseTask = {
|
|
15
|
+
id: string;
|
|
16
|
+
status: ExcelParseTaskStatus;
|
|
17
|
+
mode?: ExcelParseMode;
|
|
18
|
+
fileName: string;
|
|
19
|
+
xlsxPath: string;
|
|
20
|
+
snapshotPath?: string;
|
|
21
|
+
metaPath?: string;
|
|
22
|
+
progress?: number;
|
|
23
|
+
parsedBlocks?: number;
|
|
24
|
+
totalBlocks?: number;
|
|
25
|
+
truncated?: boolean;
|
|
26
|
+
error?: string;
|
|
27
|
+
startedAt: number;
|
|
28
|
+
finishedAt?: number;
|
|
29
|
+
};
|
|
30
|
+
export declare class ExcelStorageService implements OnModuleInit, OnModuleDestroy {
|
|
31
|
+
private readonly logger;
|
|
32
|
+
private readonly uploadDir;
|
|
33
|
+
private cleanupTimer;
|
|
34
|
+
private readonly tasks;
|
|
35
|
+
onModuleInit(): Promise<void>;
|
|
36
|
+
onModuleDestroy(): void;
|
|
37
|
+
getUploadDir(): string;
|
|
38
|
+
getTask(id: string): ExcelParseTask | undefined;
|
|
39
|
+
getXlsxFullPath(id: string): string;
|
|
40
|
+
saveXlsx(buffer: Buffer, originalName: string): Promise<StoredExcelFile>;
|
|
41
|
+
setTaskMode(id: string, mode: ExcelParseMode): void;
|
|
42
|
+
updateTaskProgress(id: string, progress: number, parsedBlocks?: number, totalBlocks?: number): void;
|
|
43
|
+
saveSnapshot(id: string, workbookData: unknown): Promise<string>;
|
|
44
|
+
markTaskChunkedDone(id: string, metaFileName: string, extras?: {
|
|
45
|
+
parsedBlocks?: number;
|
|
46
|
+
totalBlocks?: number;
|
|
47
|
+
truncated?: boolean;
|
|
48
|
+
}): string;
|
|
49
|
+
markTaskError(id: string, error: string): void;
|
|
50
|
+
readSnapshot(id: string): Promise<unknown | null>;
|
|
51
|
+
private isManagedFile;
|
|
52
|
+
private idFromFileName;
|
|
53
|
+
cleanupExpired(): Promise<number>;
|
|
54
|
+
}
|