@lark-apaas/file-service 0.0.1-alpha.1 → 0.0.1-alpha.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/dist/index.cjs +173 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +68 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/package.json +1 -4
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
|
|
6
|
+
// src/utils/lru-cache.ts
|
|
7
|
+
var LRUCache = class {
|
|
8
|
+
static {
|
|
9
|
+
__name(this, "LRUCache");
|
|
10
|
+
}
|
|
11
|
+
capacity;
|
|
12
|
+
cache;
|
|
13
|
+
constructor(capacity) {
|
|
14
|
+
if (capacity <= 0) {
|
|
15
|
+
throw new Error("Capacity must be greater than 0");
|
|
16
|
+
}
|
|
17
|
+
this.capacity = capacity;
|
|
18
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 获取缓存值,如果存在则将其移动到最近使用的位置。
|
|
22
|
+
*/
|
|
23
|
+
get(key) {
|
|
24
|
+
if (!this.cache.has(key)) {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
const value = this.cache.get(key);
|
|
28
|
+
this.cache.delete(key);
|
|
29
|
+
this.cache.set(key, value);
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 存入缓存值。如果超过容量,则删除最久未使用的项。
|
|
34
|
+
*/
|
|
35
|
+
put(key, value) {
|
|
36
|
+
if (this.cache.has(key)) {
|
|
37
|
+
this.cache.delete(key);
|
|
38
|
+
} else if (this.cache.size >= this.capacity) {
|
|
39
|
+
const firstKey = this.cache.keys().next().value;
|
|
40
|
+
if (firstKey !== void 0) {
|
|
41
|
+
this.cache.delete(firstKey);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
this.cache.set(key, value);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 检查是否包含某个键。不影响使用顺序。
|
|
48
|
+
*/
|
|
49
|
+
has(key) {
|
|
50
|
+
return this.cache.has(key);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 删除指定键。
|
|
54
|
+
*/
|
|
55
|
+
delete(key) {
|
|
56
|
+
return this.cache.delete(key);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 清空缓存。
|
|
60
|
+
*/
|
|
61
|
+
clear() {
|
|
62
|
+
this.cache.clear();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 获取当前缓存大小。
|
|
66
|
+
*/
|
|
67
|
+
get size() {
|
|
68
|
+
return this.cache.size;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/const.ts
|
|
73
|
+
var THIRTY_DAYS_IN_SECONDS = 2592e3;
|
|
74
|
+
|
|
75
|
+
// src/utils/helpers.ts
|
|
76
|
+
var isValidDownloadUrl = /* @__PURE__ */ __name((url) => {
|
|
77
|
+
return /^\/spark\/app\/app_[\w]+\/runtime\/api\/v1\/storage/.test(url);
|
|
78
|
+
}, "isValidDownloadUrl");
|
|
79
|
+
var maybeRetrieveFilePathFromDownloadUrl = /* @__PURE__ */ __name((path) => {
|
|
80
|
+
if (!isValidDownloadUrl(path)) {
|
|
81
|
+
return path;
|
|
82
|
+
}
|
|
83
|
+
const objectPath = "/object/";
|
|
84
|
+
const objectIndex = path.indexOf(objectPath);
|
|
85
|
+
if (objectIndex === -1) {
|
|
86
|
+
return "";
|
|
87
|
+
}
|
|
88
|
+
const afterObject = path.slice(objectIndex + objectPath.length);
|
|
89
|
+
const bucketEndIndex = afterObject.indexOf("/");
|
|
90
|
+
if (bucketEndIndex !== -1) {
|
|
91
|
+
const result = afterObject.slice(bucketEndIndex + 1);
|
|
92
|
+
return decodeURIComponent(result);
|
|
93
|
+
}
|
|
94
|
+
return "";
|
|
95
|
+
}, "maybeRetrieveFilePathFromDownloadUrl");
|
|
96
|
+
var parseFilePath = /* @__PURE__ */ __name((filePath) => {
|
|
97
|
+
const path = maybeRetrieveFilePathFromDownloadUrl(filePath);
|
|
98
|
+
return path.replace(/^\/+/, "");
|
|
99
|
+
}, "parseFilePath");
|
|
100
|
+
var toValidExpiresIn = /* @__PURE__ */ __name((expiresIn) => {
|
|
101
|
+
if (expiresIn < 1) {
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
if (expiresIn > THIRTY_DAYS_IN_SECONDS) {
|
|
105
|
+
return THIRTY_DAYS_IN_SECONDS;
|
|
106
|
+
}
|
|
107
|
+
return expiresIn;
|
|
108
|
+
}, "toValidExpiresIn");
|
|
109
|
+
|
|
110
|
+
// src/core/file-service.ts
|
|
111
|
+
var FileServiceCore = class {
|
|
112
|
+
static {
|
|
113
|
+
__name(this, "FileServiceCore");
|
|
114
|
+
}
|
|
115
|
+
httpClient;
|
|
116
|
+
cache = new LRUCache(1e3);
|
|
117
|
+
constructor(httpClient) {
|
|
118
|
+
this.httpClient = httpClient;
|
|
119
|
+
}
|
|
120
|
+
// async upload(file: FileBody, options?: UploadOptions) {
|
|
121
|
+
// }
|
|
122
|
+
async createSignedUrl(params) {
|
|
123
|
+
const { appId, filePath, expiresIn } = params;
|
|
124
|
+
const path = parseFilePath(filePath);
|
|
125
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
126
|
+
return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/object/sign/${bucketId}/${path}`, {
|
|
127
|
+
expires_in: toValidExpiresIn(expiresIn)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async list(params) {
|
|
131
|
+
const { appId, prefix, searchOptions } = params;
|
|
132
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
133
|
+
return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/list`, {
|
|
134
|
+
...searchOptions,
|
|
135
|
+
prefix
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async delete(filePaths, appId) {
|
|
139
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
140
|
+
return this.httpClient.delete(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/delete`, {
|
|
141
|
+
filePaths: filePaths.map((filePath) => parseFilePath(filePath))
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async getFileMetadata(filePath, appId) {
|
|
145
|
+
const path = parseFilePath(filePath);
|
|
146
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
147
|
+
return this.httpClient.get(`/api/v1/storage/inner/app/${appId}/object/${bucketId}/head/${path}`);
|
|
148
|
+
}
|
|
149
|
+
async getDefaultBucket(appId) {
|
|
150
|
+
return this.fetchBucket(appId);
|
|
151
|
+
}
|
|
152
|
+
async fetchBucket(appId) {
|
|
153
|
+
const cachedBucket = this.cache.get(appId);
|
|
154
|
+
if (cachedBucket) {
|
|
155
|
+
return cachedBucket;
|
|
156
|
+
}
|
|
157
|
+
const res = await this.httpClient.get(`/b/${appId}/get_published_v2`);
|
|
158
|
+
if (res.status !== 200) {
|
|
159
|
+
throw new Error(`Failed to get app info, status: ${res.status}`);
|
|
160
|
+
}
|
|
161
|
+
const data = res.json();
|
|
162
|
+
if (data?.bucket) {
|
|
163
|
+
this.cache.put(appId, data.bucket);
|
|
164
|
+
return data.bucket;
|
|
165
|
+
} else {
|
|
166
|
+
throw new Error(`No bucket found from remote for appId ${appId}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
exports.FileService = FileServiceCore;
|
|
172
|
+
//# sourceMappingURL=index.cjs.map
|
|
173
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/lru-cache.ts","../src/const.ts","../src/utils/helpers.ts","../src/core/file-service.ts"],"names":["LRUCache","capacity","cache","Error","Map","get","key","has","undefined","value","delete","set","put","size","firstKey","keys","next","clear","THIRTY_DAYS_IN_SECONDS","isValidDownloadUrl","url","test","maybeRetrieveFilePathFromDownloadUrl","path","objectPath","objectIndex","indexOf","afterObject","slice","length","bucketEndIndex","result","decodeURIComponent","parseFilePath","filePath","replace","toValidExpiresIn","expiresIn","FileServiceCore","httpClient","createSignedUrl","params","appId","bucketId","getDefaultBucket","post","expires_in","list","prefix","searchOptions","filePaths","map","getFileMetadata","fetchBucket","cachedBucket","res","status","data","json","bucket"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 一个简单的 LRU (最近最少使用) 缓存实现。\n * 利用 JavaScript Map 维护插入顺序的特性。\n */\nexport class LRUCache<K, V> {\n private capacity: number;\n private cache: Map<K, V>;\n\n constructor(capacity: number) {\n if (capacity <= 0) {\n throw new Error('Capacity must be greater than 0');\n }\n this.capacity = capacity;\n this.cache = new Map<K, V>();\n }\n\n /**\n * 获取缓存值,如果存在则将其移动到最近使用的位置。\n */\n get(key: K): V | undefined {\n if (!this.cache.has(key)) {\n return undefined;\n }\n\n // 将访问的键移到末尾(表示最近使用)\n const value = this.cache.get(key)!;\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n\n /**\n * 存入缓存值。如果超过容量,则删除最久未使用的项。\n */\n put(key: K, value: V): void {\n if (this.cache.has(key)) {\n // 如果已存在,先删除,后面重新 set 会移到末尾\n this.cache.delete(key);\n } else if (this.cache.size >= this.capacity) {\n // 达到容量上限,删除第一个(最久未使用)\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * 检查是否包含某个键。不影响使用顺序。\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * 删除指定键。\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * 清空缓存。\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * 获取当前缓存大小。\n */\n get size(): number {\n return this.cache.size;\n }\n}\n","export const THIRTY_DAYS_IN_SECONDS = 2592000;\n\n","import { THIRTY_DAYS_IN_SECONDS } from '../const';\n\nconst isValidDownloadUrl = (url: string) => {\n return /^\\/spark\\/app\\/app_[\\w]+\\/runtime\\/api\\/v1\\/storage/.test(url);\n}\n\nconst maybeRetrieveFilePathFromDownloadUrl = (path: string) => {\n if (!isValidDownloadUrl(path)) {\n return path;\n }\n\n const objectPath = '/object/';\n const objectIndex = path.indexOf(objectPath);\n if (objectIndex === -1) {\n return '';\n }\n\n const afterObject = path.slice(objectIndex + objectPath.length);\n const bucketEndIndex = afterObject.indexOf('/');\n\n if (bucketEndIndex !== -1) {\n const result = afterObject.slice(bucketEndIndex + 1);\n return decodeURIComponent(result);\n }\n\n return '';\n}\n\nexport const parseFilePath = (filePath: string) => {\n const path = maybeRetrieveFilePathFromDownloadUrl(filePath);\n return path.replace(/^\\/+/, '');\n}\n\nexport const toValidExpiresIn = (expiresIn: number) => {\n if (expiresIn < 1) {\n return 1;\n }\n if (expiresIn > THIRTY_DAYS_IN_SECONDS) {\n return THIRTY_DAYS_IN_SECONDS;\n }\n return expiresIn;\n};","import { LRUCache } from '../utils/lru-cache';\nimport { parseFilePath, toValidExpiresIn } from \"../utils/helpers\";\n\nimport type { HttpClient, SearchOptions } from '../types';\n\nexport class FileServiceCore {\n private readonly cache = new LRUCache<string, string>(1000);\n\n constructor(\n private readonly httpClient: HttpClient,\n ) {}\n\n // async upload(file: FileBody, options?: UploadOptions) {\n\n // }\n\n async createSignedUrl(params: { appId: string; filePath: string; expiresIn: number }) {\n const { appId, filePath, expiresIn } = params;\n const path = parseFilePath(filePath);\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/object/sign/${bucketId}/${path}`, {\n expires_in: toValidExpiresIn(expiresIn),\n });\n }\n\n async list(params: { appId: string; prefix: string; searchOptions?: SearchOptions}) {\n const { appId, prefix, searchOptions } = params;\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/list`, {\n ...searchOptions,\n prefix,\n });\n }\n\n async delete(filePaths: string[], appId: string) {\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.delete(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/delete`, {\n filePaths: filePaths.map((filePath) => parseFilePath(filePath)),\n });\n }\n\n async getFileMetadata(filePath: string, appId: string) {\n const path = parseFilePath(filePath);\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.get(`/api/v1/storage/inner/app/${appId}/object/${bucketId}/head/${path}`);\n }\n\n \n async getDefaultBucket(appId: string){\n return this.fetchBucket(appId);\n }\n\n private async fetchBucket(appId: string): Promise<string> {\n const cachedBucket = this.cache.get(appId);\n if (cachedBucket) {\n return cachedBucket;\n }\n\n const res = await this.httpClient.get(`/b/${appId}/get_published_v2`);\n if (res.status !== 200) {\n throw new Error(`Failed to get app info, status: ${res.status}`);\n }\n const data = res.json() as { bucket?: string };\n \n if (data?.bucket) {\n this.cache.put(appId, data.bucket);\n return data.bucket;\n } else {\n throw new Error(`No bucket found from remote for appId ${appId}`);\n }\n }\n}"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
type FileBody = ArrayBuffer | ArrayBufferView | Blob | Buffer | File | NodeJS.ReadableStream | ReadableStream<Uint8Array> | string;
|
|
2
|
+
interface SortBy {
|
|
3
|
+
column?: string;
|
|
4
|
+
order?: string;
|
|
5
|
+
}
|
|
6
|
+
interface SearchOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The number of files you want to be returned.
|
|
9
|
+
* @default 100
|
|
10
|
+
*/
|
|
11
|
+
maxKeys?: number;
|
|
12
|
+
/**
|
|
13
|
+
* The starting file token.
|
|
14
|
+
*/
|
|
15
|
+
continuationToken?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The column to sort by. Can be any column inside a FileObject.
|
|
18
|
+
*/
|
|
19
|
+
sortBy?: SortBy;
|
|
20
|
+
}
|
|
21
|
+
interface HttpClient {
|
|
22
|
+
/**
|
|
23
|
+
* 发送 HTTP 请求
|
|
24
|
+
*/
|
|
25
|
+
request(config: any): Promise<Response>;
|
|
26
|
+
/**
|
|
27
|
+
* 发送 GET 请求
|
|
28
|
+
*/
|
|
29
|
+
get(url: string, config?: any): Promise<Response>;
|
|
30
|
+
/**
|
|
31
|
+
* 发送 POST 请求
|
|
32
|
+
*/
|
|
33
|
+
post(url: string, data?: any, config?: any): Promise<Response>;
|
|
34
|
+
/**
|
|
35
|
+
* 发送 PUT 请求
|
|
36
|
+
*/
|
|
37
|
+
put(url: string, data?: any, config?: any): Promise<Response>;
|
|
38
|
+
/**
|
|
39
|
+
* 发送 PATCH 请求
|
|
40
|
+
*/
|
|
41
|
+
patch(url: string, data?: any, config?: any): Promise<Response>;
|
|
42
|
+
/**
|
|
43
|
+
* 发送 DELETE 请求
|
|
44
|
+
*/
|
|
45
|
+
delete(url: string, config?: any): Promise<Response>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
declare class FileServiceCore {
|
|
49
|
+
private readonly httpClient;
|
|
50
|
+
private readonly cache;
|
|
51
|
+
constructor(httpClient: HttpClient);
|
|
52
|
+
createSignedUrl(params: {
|
|
53
|
+
appId: string;
|
|
54
|
+
filePath: string;
|
|
55
|
+
expiresIn: number;
|
|
56
|
+
}): Promise<Response>;
|
|
57
|
+
list(params: {
|
|
58
|
+
appId: string;
|
|
59
|
+
prefix: string;
|
|
60
|
+
searchOptions?: SearchOptions;
|
|
61
|
+
}): Promise<Response>;
|
|
62
|
+
delete(filePaths: string[], appId: string): Promise<Response>;
|
|
63
|
+
getFileMetadata(filePath: string, appId: string): Promise<Response>;
|
|
64
|
+
getDefaultBucket(appId: string): Promise<string>;
|
|
65
|
+
private fetchBucket;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { type FileBody, FileServiceCore as FileService, type HttpClient, type SearchOptions, type SortBy };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
type FileBody = ArrayBuffer | ArrayBufferView | Blob | Buffer | File | NodeJS.ReadableStream | ReadableStream<Uint8Array> | string;
|
|
2
|
+
interface SortBy {
|
|
3
|
+
column?: string;
|
|
4
|
+
order?: string;
|
|
5
|
+
}
|
|
6
|
+
interface SearchOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The number of files you want to be returned.
|
|
9
|
+
* @default 100
|
|
10
|
+
*/
|
|
11
|
+
maxKeys?: number;
|
|
12
|
+
/**
|
|
13
|
+
* The starting file token.
|
|
14
|
+
*/
|
|
15
|
+
continuationToken?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The column to sort by. Can be any column inside a FileObject.
|
|
18
|
+
*/
|
|
19
|
+
sortBy?: SortBy;
|
|
20
|
+
}
|
|
21
|
+
interface HttpClient {
|
|
22
|
+
/**
|
|
23
|
+
* 发送 HTTP 请求
|
|
24
|
+
*/
|
|
25
|
+
request(config: any): Promise<Response>;
|
|
26
|
+
/**
|
|
27
|
+
* 发送 GET 请求
|
|
28
|
+
*/
|
|
29
|
+
get(url: string, config?: any): Promise<Response>;
|
|
30
|
+
/**
|
|
31
|
+
* 发送 POST 请求
|
|
32
|
+
*/
|
|
33
|
+
post(url: string, data?: any, config?: any): Promise<Response>;
|
|
34
|
+
/**
|
|
35
|
+
* 发送 PUT 请求
|
|
36
|
+
*/
|
|
37
|
+
put(url: string, data?: any, config?: any): Promise<Response>;
|
|
38
|
+
/**
|
|
39
|
+
* 发送 PATCH 请求
|
|
40
|
+
*/
|
|
41
|
+
patch(url: string, data?: any, config?: any): Promise<Response>;
|
|
42
|
+
/**
|
|
43
|
+
* 发送 DELETE 请求
|
|
44
|
+
*/
|
|
45
|
+
delete(url: string, config?: any): Promise<Response>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
declare class FileServiceCore {
|
|
49
|
+
private readonly httpClient;
|
|
50
|
+
private readonly cache;
|
|
51
|
+
constructor(httpClient: HttpClient);
|
|
52
|
+
createSignedUrl(params: {
|
|
53
|
+
appId: string;
|
|
54
|
+
filePath: string;
|
|
55
|
+
expiresIn: number;
|
|
56
|
+
}): Promise<Response>;
|
|
57
|
+
list(params: {
|
|
58
|
+
appId: string;
|
|
59
|
+
prefix: string;
|
|
60
|
+
searchOptions?: SearchOptions;
|
|
61
|
+
}): Promise<Response>;
|
|
62
|
+
delete(filePaths: string[], appId: string): Promise<Response>;
|
|
63
|
+
getFileMetadata(filePath: string, appId: string): Promise<Response>;
|
|
64
|
+
getDefaultBucket(appId: string): Promise<string>;
|
|
65
|
+
private fetchBucket;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { type FileBody, FileServiceCore as FileService, type HttpClient, type SearchOptions, type SortBy };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/utils/lru-cache.ts
|
|
5
|
+
var LRUCache = class {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "LRUCache");
|
|
8
|
+
}
|
|
9
|
+
capacity;
|
|
10
|
+
cache;
|
|
11
|
+
constructor(capacity) {
|
|
12
|
+
if (capacity <= 0) {
|
|
13
|
+
throw new Error("Capacity must be greater than 0");
|
|
14
|
+
}
|
|
15
|
+
this.capacity = capacity;
|
|
16
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 获取缓存值,如果存在则将其移动到最近使用的位置。
|
|
20
|
+
*/
|
|
21
|
+
get(key) {
|
|
22
|
+
if (!this.cache.has(key)) {
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
25
|
+
const value = this.cache.get(key);
|
|
26
|
+
this.cache.delete(key);
|
|
27
|
+
this.cache.set(key, value);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 存入缓存值。如果超过容量,则删除最久未使用的项。
|
|
32
|
+
*/
|
|
33
|
+
put(key, value) {
|
|
34
|
+
if (this.cache.has(key)) {
|
|
35
|
+
this.cache.delete(key);
|
|
36
|
+
} else if (this.cache.size >= this.capacity) {
|
|
37
|
+
const firstKey = this.cache.keys().next().value;
|
|
38
|
+
if (firstKey !== void 0) {
|
|
39
|
+
this.cache.delete(firstKey);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.cache.set(key, value);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 检查是否包含某个键。不影响使用顺序。
|
|
46
|
+
*/
|
|
47
|
+
has(key) {
|
|
48
|
+
return this.cache.has(key);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 删除指定键。
|
|
52
|
+
*/
|
|
53
|
+
delete(key) {
|
|
54
|
+
return this.cache.delete(key);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 清空缓存。
|
|
58
|
+
*/
|
|
59
|
+
clear() {
|
|
60
|
+
this.cache.clear();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 获取当前缓存大小。
|
|
64
|
+
*/
|
|
65
|
+
get size() {
|
|
66
|
+
return this.cache.size;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/const.ts
|
|
71
|
+
var THIRTY_DAYS_IN_SECONDS = 2592e3;
|
|
72
|
+
|
|
73
|
+
// src/utils/helpers.ts
|
|
74
|
+
var isValidDownloadUrl = /* @__PURE__ */ __name((url) => {
|
|
75
|
+
return /^\/spark\/app\/app_[\w]+\/runtime\/api\/v1\/storage/.test(url);
|
|
76
|
+
}, "isValidDownloadUrl");
|
|
77
|
+
var maybeRetrieveFilePathFromDownloadUrl = /* @__PURE__ */ __name((path) => {
|
|
78
|
+
if (!isValidDownloadUrl(path)) {
|
|
79
|
+
return path;
|
|
80
|
+
}
|
|
81
|
+
const objectPath = "/object/";
|
|
82
|
+
const objectIndex = path.indexOf(objectPath);
|
|
83
|
+
if (objectIndex === -1) {
|
|
84
|
+
return "";
|
|
85
|
+
}
|
|
86
|
+
const afterObject = path.slice(objectIndex + objectPath.length);
|
|
87
|
+
const bucketEndIndex = afterObject.indexOf("/");
|
|
88
|
+
if (bucketEndIndex !== -1) {
|
|
89
|
+
const result = afterObject.slice(bucketEndIndex + 1);
|
|
90
|
+
return decodeURIComponent(result);
|
|
91
|
+
}
|
|
92
|
+
return "";
|
|
93
|
+
}, "maybeRetrieveFilePathFromDownloadUrl");
|
|
94
|
+
var parseFilePath = /* @__PURE__ */ __name((filePath) => {
|
|
95
|
+
const path = maybeRetrieveFilePathFromDownloadUrl(filePath);
|
|
96
|
+
return path.replace(/^\/+/, "");
|
|
97
|
+
}, "parseFilePath");
|
|
98
|
+
var toValidExpiresIn = /* @__PURE__ */ __name((expiresIn) => {
|
|
99
|
+
if (expiresIn < 1) {
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
if (expiresIn > THIRTY_DAYS_IN_SECONDS) {
|
|
103
|
+
return THIRTY_DAYS_IN_SECONDS;
|
|
104
|
+
}
|
|
105
|
+
return expiresIn;
|
|
106
|
+
}, "toValidExpiresIn");
|
|
107
|
+
|
|
108
|
+
// src/core/file-service.ts
|
|
109
|
+
var FileServiceCore = class {
|
|
110
|
+
static {
|
|
111
|
+
__name(this, "FileServiceCore");
|
|
112
|
+
}
|
|
113
|
+
httpClient;
|
|
114
|
+
cache = new LRUCache(1e3);
|
|
115
|
+
constructor(httpClient) {
|
|
116
|
+
this.httpClient = httpClient;
|
|
117
|
+
}
|
|
118
|
+
// async upload(file: FileBody, options?: UploadOptions) {
|
|
119
|
+
// }
|
|
120
|
+
async createSignedUrl(params) {
|
|
121
|
+
const { appId, filePath, expiresIn } = params;
|
|
122
|
+
const path = parseFilePath(filePath);
|
|
123
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
124
|
+
return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/object/sign/${bucketId}/${path}`, {
|
|
125
|
+
expires_in: toValidExpiresIn(expiresIn)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async list(params) {
|
|
129
|
+
const { appId, prefix, searchOptions } = params;
|
|
130
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
131
|
+
return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/list`, {
|
|
132
|
+
...searchOptions,
|
|
133
|
+
prefix
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async delete(filePaths, appId) {
|
|
137
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
138
|
+
return this.httpClient.delete(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/delete`, {
|
|
139
|
+
filePaths: filePaths.map((filePath) => parseFilePath(filePath))
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
async getFileMetadata(filePath, appId) {
|
|
143
|
+
const path = parseFilePath(filePath);
|
|
144
|
+
const bucketId = await this.getDefaultBucket(appId);
|
|
145
|
+
return this.httpClient.get(`/api/v1/storage/inner/app/${appId}/object/${bucketId}/head/${path}`);
|
|
146
|
+
}
|
|
147
|
+
async getDefaultBucket(appId) {
|
|
148
|
+
return this.fetchBucket(appId);
|
|
149
|
+
}
|
|
150
|
+
async fetchBucket(appId) {
|
|
151
|
+
const cachedBucket = this.cache.get(appId);
|
|
152
|
+
if (cachedBucket) {
|
|
153
|
+
return cachedBucket;
|
|
154
|
+
}
|
|
155
|
+
const res = await this.httpClient.get(`/b/${appId}/get_published_v2`);
|
|
156
|
+
if (res.status !== 200) {
|
|
157
|
+
throw new Error(`Failed to get app info, status: ${res.status}`);
|
|
158
|
+
}
|
|
159
|
+
const data = res.json();
|
|
160
|
+
if (data?.bucket) {
|
|
161
|
+
this.cache.put(appId, data.bucket);
|
|
162
|
+
return data.bucket;
|
|
163
|
+
} else {
|
|
164
|
+
throw new Error(`No bucket found from remote for appId ${appId}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export { FileServiceCore as FileService };
|
|
170
|
+
//# sourceMappingURL=index.js.map
|
|
171
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/lru-cache.ts","../src/const.ts","../src/utils/helpers.ts","../src/core/file-service.ts"],"names":["LRUCache","capacity","cache","Error","Map","get","key","has","undefined","value","delete","set","put","size","firstKey","keys","next","clear","THIRTY_DAYS_IN_SECONDS","isValidDownloadUrl","url","test","maybeRetrieveFilePathFromDownloadUrl","path","objectPath","objectIndex","indexOf","afterObject","slice","length","bucketEndIndex","result","decodeURIComponent","parseFilePath","filePath","replace","toValidExpiresIn","expiresIn","FileServiceCore","httpClient","createSignedUrl","params","appId","bucketId","getDefaultBucket","post","expires_in","list","prefix","searchOptions","filePaths","map","getFileMetadata","fetchBucket","cachedBucket","res","status","data","json","bucket"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 一个简单的 LRU (最近最少使用) 缓存实现。\n * 利用 JavaScript Map 维护插入顺序的特性。\n */\nexport class LRUCache<K, V> {\n private capacity: number;\n private cache: Map<K, V>;\n\n constructor(capacity: number) {\n if (capacity <= 0) {\n throw new Error('Capacity must be greater than 0');\n }\n this.capacity = capacity;\n this.cache = new Map<K, V>();\n }\n\n /**\n * 获取缓存值,如果存在则将其移动到最近使用的位置。\n */\n get(key: K): V | undefined {\n if (!this.cache.has(key)) {\n return undefined;\n }\n\n // 将访问的键移到末尾(表示最近使用)\n const value = this.cache.get(key)!;\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n\n /**\n * 存入缓存值。如果超过容量,则删除最久未使用的项。\n */\n put(key: K, value: V): void {\n if (this.cache.has(key)) {\n // 如果已存在,先删除,后面重新 set 会移到末尾\n this.cache.delete(key);\n } else if (this.cache.size >= this.capacity) {\n // 达到容量上限,删除第一个(最久未使用)\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * 检查是否包含某个键。不影响使用顺序。\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * 删除指定键。\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * 清空缓存。\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * 获取当前缓存大小。\n */\n get size(): number {\n return this.cache.size;\n }\n}\n","export const THIRTY_DAYS_IN_SECONDS = 2592000;\n\n","import { THIRTY_DAYS_IN_SECONDS } from '../const';\n\nconst isValidDownloadUrl = (url: string) => {\n return /^\\/spark\\/app\\/app_[\\w]+\\/runtime\\/api\\/v1\\/storage/.test(url);\n}\n\nconst maybeRetrieveFilePathFromDownloadUrl = (path: string) => {\n if (!isValidDownloadUrl(path)) {\n return path;\n }\n\n const objectPath = '/object/';\n const objectIndex = path.indexOf(objectPath);\n if (objectIndex === -1) {\n return '';\n }\n\n const afterObject = path.slice(objectIndex + objectPath.length);\n const bucketEndIndex = afterObject.indexOf('/');\n\n if (bucketEndIndex !== -1) {\n const result = afterObject.slice(bucketEndIndex + 1);\n return decodeURIComponent(result);\n }\n\n return '';\n}\n\nexport const parseFilePath = (filePath: string) => {\n const path = maybeRetrieveFilePathFromDownloadUrl(filePath);\n return path.replace(/^\\/+/, '');\n}\n\nexport const toValidExpiresIn = (expiresIn: number) => {\n if (expiresIn < 1) {\n return 1;\n }\n if (expiresIn > THIRTY_DAYS_IN_SECONDS) {\n return THIRTY_DAYS_IN_SECONDS;\n }\n return expiresIn;\n};","import { LRUCache } from '../utils/lru-cache';\nimport { parseFilePath, toValidExpiresIn } from \"../utils/helpers\";\n\nimport type { HttpClient, SearchOptions } from '../types';\n\nexport class FileServiceCore {\n private readonly cache = new LRUCache<string, string>(1000);\n\n constructor(\n private readonly httpClient: HttpClient,\n ) {}\n\n // async upload(file: FileBody, options?: UploadOptions) {\n\n // }\n\n async createSignedUrl(params: { appId: string; filePath: string; expiresIn: number }) {\n const { appId, filePath, expiresIn } = params;\n const path = parseFilePath(filePath);\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/object/sign/${bucketId}/${path}`, {\n expires_in: toValidExpiresIn(expiresIn),\n });\n }\n\n async list(params: { appId: string; prefix: string; searchOptions?: SearchOptions}) {\n const { appId, prefix, searchOptions } = params;\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.post(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/list`, {\n ...searchOptions,\n prefix,\n });\n }\n\n async delete(filePaths: string[], appId: string) {\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.delete(`/api/v1/storage/inner/app/${appId}/bucket/${bucketId}/delete`, {\n filePaths: filePaths.map((filePath) => parseFilePath(filePath)),\n });\n }\n\n async getFileMetadata(filePath: string, appId: string) {\n const path = parseFilePath(filePath);\n const bucketId = await this.getDefaultBucket(appId);\n\n return this.httpClient.get(`/api/v1/storage/inner/app/${appId}/object/${bucketId}/head/${path}`);\n }\n\n \n async getDefaultBucket(appId: string){\n return this.fetchBucket(appId);\n }\n\n private async fetchBucket(appId: string): Promise<string> {\n const cachedBucket = this.cache.get(appId);\n if (cachedBucket) {\n return cachedBucket;\n }\n\n const res = await this.httpClient.get(`/b/${appId}/get_published_v2`);\n if (res.status !== 200) {\n throw new Error(`Failed to get app info, status: ${res.status}`);\n }\n const data = res.json() as { bucket?: string };\n \n if (data?.bucket) {\n this.cache.put(appId, data.bucket);\n return data.bucket;\n } else {\n throw new Error(`No bucket found from remote for appId ${appId}`);\n }\n }\n}"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/file-service",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.2",
|
|
4
4
|
"description": "File Service Core Package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,9 +36,6 @@
|
|
|
36
36
|
"lint": "echo 'ESLint skipped for TypeScript files'",
|
|
37
37
|
"lint:fix": "eslint src --ext .ts --fix"
|
|
38
38
|
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"@lark-apaas/nestjs-common": "0.1.2"
|
|
41
|
-
},
|
|
42
39
|
"devDependencies": {
|
|
43
40
|
"tsup": "^8.0.0",
|
|
44
41
|
"typescript": "^5.2.0"
|