@kmlckj/licos-platform-sdk 0.5.0 → 0.6.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 CHANGED
@@ -1,47 +1,39 @@
1
1
  # @kmlckj/licos-platform-sdk
2
2
 
3
- LICOS 平台对象存储 SDK,兼容前端和后端调用。
3
+ LICOS 平台对象存储预览 / 下载 SDK,兼容前端和后端调用。
4
4
 
5
5
  ## 后端模式
6
6
 
7
- - `baseUrl` / `token` / `userId` / `projectId` 不传时,会回退读取 `LICOS_*` 环境变量
7
+ - `baseUrl` / `token` 不传时,会回退读取 `LICOS_*` 环境变量
8
8
  - 后端默认读取 `LICOS_ORCHESTRATOR_API_BASE_URL`
9
- - 这些存储接口实际挂在调度器 `/api/v1/storage/*` 和 `/api/v1/public/storage/*`
10
- - `uploadUser` / `uploadProject` 支持直接传本地文件路径
9
+ - 这些存储接口实际挂在调度器 `/api/v1/public/storage/*`
11
10
  - `download(url, outputPath)` 会把文件写到本地路径
12
- - 上传、预览、下载都要求鉴权;`download(...)` 会自动带上 `token` 或 `LICOS_USER_TOKEN`
11
+ - 预览 / 下载都要求鉴权;`download(...)` 会自动带上 `token` 或 `LICOS_RUNTIME_TOKEN`
13
12
 
14
13
  ```js
15
- import { uploadProject, download } from '@kmlckj/licos-platform-sdk';
14
+ import { download, downloadUrl } from '@kmlckj/licos-platform-sdk';
16
15
 
17
- const uploaded = await uploadProject('/tmp/report.pdf');
18
- await download(uploaded.download_url, '/tmp/report-downloaded.pdf');
16
+ const url = downloadUrl('demo-bucket', 'folder/report.pdf');
17
+ await download(url, '/tmp/report-downloaded.pdf');
19
18
  ```
20
19
 
21
20
  ## 前端模式
22
21
 
23
22
  - 默认走同源相对地址;也可以显式传 `baseUrl`
24
- - 上传支持 `File` / `Blob` / `ArrayBuffer` / TypedArray / `{ data, fileName, contentType }`
25
23
  - `download(url)` 返回 `Blob`
26
24
  - TypeScript 前端可显式导入浏览器入口 `@kmlckj/licos-platform-sdk/browser`,拿到浏览器专用类型
27
25
  - 预览 / 下载接口同样要求传 `token`
28
26
 
29
27
  ```js
30
- import { uploadUser, download } from '@kmlckj/licos-platform-sdk';
28
+ import { download, previewUrl } from '@kmlckj/licos-platform-sdk';
31
29
 
32
- const uploaded = await uploadUser(fileInput.files[0], {
33
- token: userJwt,
34
- });
35
-
36
- const blob = await download(uploaded.download_url, { token: userJwt });
30
+ const url = previewUrl('demo-bucket', 'images/demo.png');
31
+ const blob = await download(url, { token: runtimeToken });
37
32
  ```
38
33
 
39
34
  ```ts
40
- import { uploadUser, download } from '@kmlckj/licos-platform-sdk/browser';
41
-
42
- const uploaded = await uploadUser(fileInput.files![0], {
43
- token: userJwt,
44
- });
35
+ import { download, previewUrl } from '@kmlckj/licos-platform-sdk/browser';
45
36
 
46
- const blob = await download(uploaded.download_url, { token: userJwt });
37
+ const url = previewUrl('demo-bucket', 'images/demo.png');
38
+ const blob = await download(url, { token: runtimeToken });
47
39
  ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kmlckj/licos-platform-sdk",
3
- "version": "0.5.0",
4
- "description": "LICOS platform SDK for storage upload, download, and preview in browser and Node runtimes",
3
+ "version": "0.6.0",
4
+ "description": "LICOS platform SDK for storage download and preview in browser and Node runtimes",
5
5
  "author": "kmlckj",
6
6
  "type": "module",
7
7
  "main": "./src/index.js",
package/src/browser.d.ts CHANGED
@@ -1,48 +1,11 @@
1
- export interface UploadResult {
2
- bucket: string;
3
- object_key: string;
4
- file_name: string;
5
- content_type: string;
6
- size_bytes: number;
7
- preview_url: string;
8
- download_url: string;
9
- }
10
-
11
- export interface BlobLike {
12
- arrayBuffer(): Promise<ArrayBuffer>;
13
- readonly size?: number;
14
- readonly type?: string;
15
- readonly name?: string;
16
- }
17
-
18
- export interface UploadBinaryDescriptor {
19
- data?: ArrayBuffer | ArrayBufferView | BlobLike;
20
- file?: ArrayBuffer | ArrayBufferView | BlobLike;
21
- fileName?: string;
22
- contentType?: string;
23
- }
24
-
25
- export type UploadSource =
26
- | ArrayBuffer
27
- | ArrayBufferView
28
- | BlobLike
29
- | UploadBinaryDescriptor;
30
-
31
1
  export interface StorageClientOptions {
32
2
  baseUrl?: string;
33
3
  token?: string;
34
- userId?: string;
35
- projectId?: string;
36
4
  fetch?: typeof fetch;
37
5
  headers?: HeadersInit;
38
6
  credentials?: RequestCredentials;
39
7
  }
40
8
 
41
- export interface UploadProjectOptions extends StorageClientOptions {
42
- userId?: string;
43
- projectId?: string;
44
- }
45
-
46
9
  export class PlatformSdkError extends Error {
47
10
  status?: number;
48
11
  code?: number;
@@ -56,8 +19,6 @@ export class StorageClient {
56
19
  constructor(options?: StorageClientOptions);
57
20
  previewUrl(bucket: string, objectKey: string): string;
58
21
  downloadUrl(bucket: string, objectKey: string): string;
59
- uploadUser(file: UploadSource): Promise<UploadResult>;
60
- uploadProject(file: UploadSource, options?: UploadProjectOptions): Promise<UploadResult>;
61
22
  download(url: string): Promise<Blob>;
62
23
  }
63
24
 
@@ -71,12 +32,4 @@ export function downloadUrl(
71
32
  objectKey: string,
72
33
  options?: StorageClientOptions,
73
34
  ): string;
74
- export function uploadUser(
75
- file: UploadSource,
76
- options?: StorageClientOptions,
77
- ): Promise<UploadResult>;
78
- export function uploadProject(
79
- file: UploadSource,
80
- options?: UploadProjectOptions,
81
- ): Promise<UploadResult>;
82
35
  export function download(url: string, options?: StorageClientOptions): Promise<Blob>;
package/src/browser.js CHANGED
@@ -6,17 +6,12 @@ import {
6
6
  encodeObjectKey,
7
7
  encodeStorageSegment,
8
8
  normalizeBaseUrl,
9
- resolveProjectId,
10
- resolveUserId,
11
- uploadFile,
12
9
  } from './shared.js';
13
10
 
14
11
  export class StorageClient {
15
12
  constructor(options = {}) {
16
13
  this.baseUrl = options.baseUrl;
17
14
  this.token = options.token;
18
- this.userId = options.userId;
19
- this.projectId = options.projectId;
20
15
  this.fetchImpl = options.fetch ?? fetch;
21
16
  this.headers = options.headers;
22
17
  this.credentials = options.credentials;
@@ -31,31 +26,6 @@ export class StorageClient {
31
26
  return `${this.previewUrl(bucket, objectKey)}?download=1`;
32
27
  }
33
28
 
34
- async uploadUser(file) {
35
- const baseUrl = normalizeBaseUrl(this.baseUrl);
36
- return uploadFile(this.fetchImpl, `${baseUrl}/api/v1/storage/upload/user`, file, {
37
- token: this.token,
38
- headers: this.headers,
39
- credentials: this.credentials,
40
- });
41
- }
42
-
43
- async uploadProject(file, options = {}) {
44
- const baseUrl = normalizeBaseUrl(this.baseUrl);
45
- const userId = resolveUserId(options.userId ?? this.userId);
46
- const projectId = resolveProjectId(options.projectId ?? this.projectId);
47
- return uploadFile(
48
- this.fetchImpl,
49
- `${baseUrl}/api/v1/storage/upload/project/${encodeStorageSegment(userId)}/${encodeStorageSegment(projectId)}`,
50
- file,
51
- {
52
- token: this.token,
53
- headers: this.headers,
54
- credentials: this.credentials,
55
- },
56
- );
57
- }
58
-
59
29
  async download(url, outputPath) {
60
30
  if (outputPath !== undefined) {
61
31
  throw new ConfigurationError(
@@ -82,14 +52,6 @@ export function downloadUrl(bucket, objectKey, options = {}) {
82
52
  return defaultClient(options).downloadUrl(bucket, objectKey);
83
53
  }
84
54
 
85
- export async function uploadUser(file, options = {}) {
86
- return defaultClient(options).uploadUser(file);
87
- }
88
-
89
- export async function uploadProject(file, options = {}) {
90
- return defaultClient(options).uploadProject(file, options);
91
- }
92
-
93
55
  export async function download(url, outputPathOrOptions, maybeOptions = {}) {
94
56
  if (typeof outputPathOrOptions === 'string') {
95
57
  throw new ConfigurationError(
package/src/index.d.ts CHANGED
@@ -1,49 +1,11 @@
1
- export interface UploadResult {
2
- bucket: string;
3
- object_key: string;
4
- file_name: string;
5
- content_type: string;
6
- size_bytes: number;
7
- preview_url: string;
8
- download_url: string;
9
- }
10
-
11
- export interface BlobLike {
12
- arrayBuffer(): Promise<ArrayBuffer>;
13
- readonly size?: number;
14
- readonly type?: string;
15
- readonly name?: string;
16
- }
17
-
18
- export interface UploadBinaryDescriptor {
19
- data?: ArrayBuffer | ArrayBufferView | BlobLike;
20
- file?: ArrayBuffer | ArrayBufferView | BlobLike;
21
- fileName?: string;
22
- contentType?: string;
23
- }
24
-
25
- export type UploadSource =
26
- | string
27
- | ArrayBuffer
28
- | ArrayBufferView
29
- | BlobLike
30
- | UploadBinaryDescriptor;
31
-
32
1
  export interface StorageClientOptions {
33
2
  baseUrl?: string;
34
3
  token?: string;
35
- userId?: string;
36
- projectId?: string;
37
4
  fetch?: typeof fetch;
38
5
  headers?: HeadersInit;
39
6
  credentials?: RequestCredentials;
40
7
  }
41
8
 
42
- export interface UploadProjectOptions extends StorageClientOptions {
43
- userId?: string;
44
- projectId?: string;
45
- }
46
-
47
9
  export class PlatformSdkError extends Error {
48
10
  status?: number;
49
11
  code?: number;
@@ -57,8 +19,6 @@ export class StorageClient {
57
19
  constructor(options?: StorageClientOptions);
58
20
  previewUrl(bucket: string, objectKey: string): string;
59
21
  downloadUrl(bucket: string, objectKey: string): string;
60
- uploadUser(file: UploadSource): Promise<UploadResult>;
61
- uploadProject(file: UploadSource, options?: UploadProjectOptions): Promise<UploadResult>;
62
22
  download(url: string): Promise<Blob>;
63
23
  download(url: string, outputPath: string): Promise<string>;
64
24
  }
@@ -73,14 +33,6 @@ export function downloadUrl(
73
33
  objectKey: string,
74
34
  options?: StorageClientOptions,
75
35
  ): string;
76
- export function uploadUser(
77
- file: UploadSource,
78
- options?: StorageClientOptions,
79
- ): Promise<UploadResult>;
80
- export function uploadProject(
81
- file: UploadSource,
82
- options?: UploadProjectOptions,
83
- ): Promise<UploadResult>;
84
36
  export function download(url: string, options?: StorageClientOptions): Promise<Blob>;
85
37
  export function download(
86
38
  url: string,
package/src/index.js CHANGED
@@ -8,20 +8,13 @@ import {
8
8
  downloadBlob,
9
9
  encodeObjectKey,
10
10
  encodeStorageSegment,
11
- fileNameFromPath,
12
- guessContentType,
13
11
  normalizeBaseUrl,
14
- resolveProjectId,
15
- resolveUserId,
16
- uploadFile,
17
12
  } from './shared.js';
18
13
 
19
14
  export class StorageClient {
20
15
  constructor(options = {}) {
21
16
  this.baseUrl = options.baseUrl;
22
17
  this.token = options.token;
23
- this.userId = options.userId;
24
- this.projectId = options.projectId;
25
18
  this.fetchImpl = options.fetch ?? fetch;
26
19
  this.headers = options.headers;
27
20
  this.credentials = options.credentials;
@@ -36,21 +29,6 @@ export class StorageClient {
36
29
  return `${this.previewUrl(bucket, objectKey)}?download=1`;
37
30
  }
38
31
 
39
- async uploadUser(file) {
40
- const baseUrl = normalizeBaseUrl(this.baseUrl);
41
- return this.#upload(`${baseUrl}/api/v1/storage/upload/user`, file);
42
- }
43
-
44
- async uploadProject(file, options = {}) {
45
- const baseUrl = normalizeBaseUrl(this.baseUrl);
46
- const userId = resolveUserId(options.userId ?? this.userId);
47
- const projectId = resolveProjectId(options.projectId ?? this.projectId);
48
- return this.#upload(
49
- `${baseUrl}/api/v1/storage/upload/project/${encodeStorageSegment(userId)}/${encodeStorageSegment(projectId)}`,
50
- file,
51
- );
52
- }
53
-
54
32
  async download(url, outputPath) {
55
33
  const blob = await downloadBlob(this.fetchImpl, url, {
56
34
  token: this.token,
@@ -66,28 +44,6 @@ export class StorageClient {
66
44
  return outputPath;
67
45
  }
68
46
 
69
- async #upload(url, file) {
70
- return uploadFile(
71
- this.fetchImpl,
72
- url,
73
- file,
74
- {
75
- token: this.token,
76
- headers: this.headers,
77
- credentials: this.credentials,
78
- },
79
- async (filePath) => {
80
- const fileBuffer = await fs.readFile(filePath);
81
- const fileName = fileNameFromPath(filePath);
82
- const contentType = guessContentType(fileName);
83
- return {
84
- fileName,
85
- contentType,
86
- blob: new Blob([fileBuffer], { type: contentType }),
87
- };
88
- },
89
- );
90
- }
91
47
  }
92
48
 
93
49
  function defaultClient(options = {}) {
@@ -102,14 +58,6 @@ export function downloadUrl(bucket, objectKey, options = {}) {
102
58
  return defaultClient(options).downloadUrl(bucket, objectKey);
103
59
  }
104
60
 
105
- export async function uploadUser(filePath, options = {}) {
106
- return defaultClient(options).uploadUser(filePath);
107
- }
108
-
109
- export async function uploadProject(filePath, options = {}) {
110
- return defaultClient(options).uploadProject(filePath, options);
111
- }
112
-
113
61
  export async function download(url, outputPathOrOptions, maybeOptions = {}) {
114
62
  if (typeof outputPathOrOptions === 'string') {
115
63
  return defaultClient(maybeOptions).download(url, outputPathOrOptions);
package/src/shared.js CHANGED
@@ -23,14 +23,6 @@ function readEnv(name) {
23
23
  return value ? value : undefined;
24
24
  }
25
25
 
26
- export function requireEnv(name) {
27
- const value = readEnv(name);
28
- if (value) {
29
- return value;
30
- }
31
- throw new ConfigurationError(`Missing required environment variable: ${name}`);
32
- }
33
-
34
26
  export function normalizeBaseUrl(baseUrl) {
35
27
  if (typeof baseUrl === 'string') {
36
28
  return baseUrl.replace(/\/+$/, '');
@@ -50,14 +42,6 @@ export function normalizeBaseUrl(baseUrl) {
50
42
  );
51
43
  }
52
44
 
53
- export function resolveUserId(userId) {
54
- return userId ?? requireEnv('LICOS_USER_ID');
55
- }
56
-
57
- export function resolveProjectId(projectId) {
58
- return projectId ?? requireEnv('LICOS_PROJECT_ID');
59
- }
60
-
61
45
  export function encodeStorageSegment(value) {
62
46
  return encodeURIComponent(value);
63
47
  }
@@ -66,112 +50,6 @@ export function encodeObjectKey(value) {
66
50
  return value.split('/').map(encodeStorageSegment).join('/');
67
51
  }
68
52
 
69
- export function fileNameFromPath(filePath) {
70
- const parts = String(filePath).split(/[\\/]/);
71
- const fileName = parts[parts.length - 1];
72
- return fileName || 'upload.bin';
73
- }
74
-
75
- export function guessContentType(fileName) {
76
- const ext = /\.[^./\\]+$/.exec(String(fileName).toLowerCase())?.[0] ?? '';
77
- switch (ext) {
78
- case '.txt':
79
- return 'text/plain';
80
- case '.json':
81
- return 'application/json';
82
- case '.pdf':
83
- return 'application/pdf';
84
- case '.png':
85
- return 'image/png';
86
- case '.jpg':
87
- case '.jpeg':
88
- return 'image/jpeg';
89
- case '.gif':
90
- return 'image/gif';
91
- case '.webp':
92
- return 'image/webp';
93
- case '.svg':
94
- return 'image/svg+xml';
95
- case '.md':
96
- return 'text/markdown';
97
- default:
98
- return 'application/octet-stream';
99
- }
100
- }
101
-
102
- function isBlobLike(value) {
103
- return !!value && typeof value === 'object' && typeof value.arrayBuffer === 'function';
104
- }
105
-
106
- function isBinaryDescriptor(value) {
107
- if (!value || typeof value !== 'object' || isBlobLike(value)) {
108
- return false;
109
- }
110
- return 'data' in value || 'file' in value;
111
- }
112
-
113
- function toUint8Array(data) {
114
- if (data instanceof Uint8Array) {
115
- return data;
116
- }
117
- if (ArrayBuffer.isView(data)) {
118
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
119
- }
120
- return new Uint8Array(data);
121
- }
122
-
123
- async function normalizeBinaryPayload(data, overrides = {}) {
124
- if (isBlobLike(data)) {
125
- const fileName =
126
- overrides.fileName ??
127
- (typeof data.name === 'string' && data.name ? data.name : undefined) ??
128
- 'upload.bin';
129
- const contentType =
130
- overrides.contentType ??
131
- (typeof data.type === 'string' && data.type ? data.type : undefined) ??
132
- guessContentType(fileName);
133
- return {
134
- fileName,
135
- contentType,
136
- blob: new Blob([await data.arrayBuffer()], { type: contentType }),
137
- };
138
- }
139
-
140
- if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
141
- const fileName = overrides.fileName ?? 'upload.bin';
142
- const contentType = overrides.contentType ?? guessContentType(fileName);
143
- return {
144
- fileName,
145
- contentType,
146
- blob: new Blob([toUint8Array(data)], { type: contentType }),
147
- };
148
- }
149
-
150
- throw new ConfigurationError(
151
- 'Unsupported upload input. Use a file path string, Blob/File, ArrayBuffer, TypedArray, or { data, fileName }.',
152
- );
153
- }
154
-
155
- export async function normalizeUploadSource(source, readPathSource) {
156
- if (typeof source === 'string') {
157
- if (!readPathSource) {
158
- throw new ConfigurationError(
159
- 'Front-end mode does not support filesystem path uploads. Pass File, Blob, ArrayBuffer, TypedArray, or { data, fileName } instead.',
160
- );
161
- }
162
- return readPathSource(source);
163
- }
164
-
165
- if (isBinaryDescriptor(source)) {
166
- return normalizeBinaryPayload(source.data ?? source.file, {
167
- fileName: source.fileName,
168
- contentType: source.contentType,
169
- });
170
- }
171
-
172
- return normalizeBinaryPayload(source);
173
- }
174
-
175
53
  function headersFromInit(headersInit) {
176
54
  return new Headers(headersInit ?? undefined);
177
55
  }
@@ -179,12 +57,12 @@ function headersFromInit(headersInit) {
179
57
  export function buildRequestHeaders(options = {}, { authenticated = false } = {}) {
180
58
  const headers = headersFromInit(options.headers);
181
59
  if (authenticated && !headers.has('Authorization')) {
182
- const resolvedToken = options.token ?? readEnv('LICOS_USER_TOKEN');
60
+ const resolvedToken = options.token ?? readEnv('LICOS_RUNTIME_TOKEN');
183
61
  if (resolvedToken) {
184
62
  headers.set('Authorization', `Bearer ${resolvedToken}`);
185
63
  } else if (isNodeRuntime()) {
186
64
  throw new ConfigurationError(
187
- 'Missing authentication token. Pass options.token, provide an Authorization header, or set LICOS_USER_TOKEN.',
65
+ 'Missing authentication token. Pass options.token, provide an Authorization header, or set LICOS_RUNTIME_TOKEN.',
188
66
  );
189
67
  }
190
68
  }
@@ -199,27 +77,6 @@ export function buildRequestInit(options = {}, init = {}) {
199
77
  return requestInit;
200
78
  }
201
79
 
202
- export async function readJsonEnvelope(response) {
203
- let payload;
204
- try {
205
- payload = await response.json();
206
- } catch (error) {
207
- throw new ApiError('Platform API returned non-JSON response', {
208
- status: response.status,
209
- details: error instanceof Error ? error.message : String(error),
210
- });
211
- }
212
-
213
- if (!response.ok || payload?.code !== 0) {
214
- throw new ApiError(payload?.message ?? `Platform API failed: HTTP ${response.status}`, {
215
- status: response.status,
216
- code: payload?.code,
217
- details: payload?.data ?? payload,
218
- });
219
- }
220
- return payload.data;
221
- }
222
-
223
80
  export async function downloadBlob(fetchImpl, url, options = {}) {
224
81
  const response = await fetchImpl(
225
82
  url,
@@ -240,19 +97,3 @@ export async function downloadBlob(fetchImpl, url, options = {}) {
240
97
  type: response.headers.get('content-type') ?? undefined,
241
98
  });
242
99
  }
243
-
244
- export async function uploadFile(fetchImpl, url, source, options = {}, readPathSource) {
245
- const normalized = await normalizeUploadSource(source, readPathSource);
246
- const formData = new FormData();
247
- formData.append('file', normalized.blob, normalized.fileName);
248
-
249
- const response = await fetchImpl(
250
- url,
251
- buildRequestInit(options, {
252
- method: 'POST',
253
- headers: buildRequestHeaders(options, { authenticated: true }),
254
- body: formData,
255
- }),
256
- );
257
- return readJsonEnvelope(response);
258
- }