@kmlckj/licos-platform-sdk 0.5.0 → 0.6.1

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,45 @@
1
1
  # @kmlckj/licos-platform-sdk
2
2
 
3
- LICOS 平台对象存储 SDK,兼容前端和后端调用。
3
+ LICOS platform SDK for server-side project runtime code.
4
4
 
5
- ## 后端模式
5
+ ## Runtime Configuration
6
6
 
7
- - `baseUrl` / `token` / `userId` / `projectId` 不传时,会回退读取 `LICOS_*` 环境变量
8
- - 后端默认读取 `LICOS_ORCHESTRATOR_API_BASE_URL`
9
- - 这些存储接口实际挂在调度器 `/api/v1/storage/*` `/api/v1/public/storage/*`
10
- - `uploadUser` / `uploadProject` 支持直接传本地文件路径
11
- - `download(url, outputPath)` 会把文件写到本地路径
12
- - 上传、预览、下载都要求鉴权;`download(...)` 会自动带上 `token` 或 `LICOS_USER_TOKEN`
7
+ Server-side helpers call platform Studio runtime APIs. Project code does not
8
+ pass API base URLs, project IDs, tokens, or environment scopes. The SDK loads
9
+ them from runtime environment variables injected by LICOS:
13
10
 
14
- ```js
15
- import { uploadProject, download } from '@kmlckj/licos-platform-sdk';
16
-
17
- const uploaded = await uploadProject('/tmp/report.pdf');
18
- await download(uploaded.download_url, '/tmp/report-downloaded.pdf');
19
- ```
11
+ - `LICOS_PLATFORM_API_BASE_URL`
12
+ - `LICOS_RUNTIME_TOKEN` or `LICOS_USER_TOKEN`
13
+ - `LICOS_PROJECT_ID` or `AGENT_PROJECT_ID`
14
+ - `LICOS_WORKSPACE_ID` or `AGENT_WORKSPACE_ID`
15
+ - `LICOS_PROJECT_ENV`, mapped internally to `dev` or `prod`
20
16
 
21
- ## 前端模式
17
+ ## Database
22
18
 
23
- - 默认走同源相对地址;也可以显式传 `baseUrl`
24
- - 上传支持 `File` / `Blob` / `ArrayBuffer` / TypedArray / `{ data, fileName, contentType }`
25
- - `download(url)` 返回 `Blob`
26
- - TypeScript 前端可显式导入浏览器入口 `@kmlckj/licos-platform-sdk/browser`,拿到浏览器专用类型
27
- - 预览 / 下载接口同样要求传 `token`
19
+ Database helpers call the runtime database data plane only. They do not expose
20
+ database control-plane or service deletion APIs.
28
21
 
29
22
  ```js
30
- import { uploadUser, download } from '@kmlckj/licos-platform-sdk';
31
-
32
- const uploaded = await uploadUser(fileInput.files[0], {
33
- token: userJwt,
34
- });
35
-
36
- const blob = await download(uploaded.download_url, { token: userJwt });
23
+ import { database } from '@kmlckj/licos-platform-sdk';
24
+
25
+ const rows = await database
26
+ .table('todos')
27
+ .select('id', 'title')
28
+ .eq('done', false)
29
+ .order('created_at', { desc: true })
30
+ .limit(10)
31
+ .execute();
37
32
  ```
38
33
 
39
- ```ts
40
- import { uploadUser, download } from '@kmlckj/licos-platform-sdk/browser';
34
+ ## Object Storage
41
35
 
42
- const uploaded = await uploadUser(fileInput.files![0], {
43
- token: userJwt,
44
- });
36
+ ```js
37
+ import { storage } from '@kmlckj/licos-platform-sdk';
45
38
 
46
- const blob = await download(uploaded.download_url, { token: userJwt });
39
+ await storage.createFolder('reports');
40
+ const file = await storage.uploadFile('/tmp/report.pdf');
41
+ const link = await storage.shareUrl(file.id);
47
42
  ```
43
+
44
+ The browser entry does not export object storage helpers because runtime tokens
45
+ must not be bundled into public frontend code.
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
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",
5
- "author": "kmlckj",
6
- "type": "module",
7
- "main": "./src/index.js",
8
- "browser": "./src/browser.js",
9
- "types": "./src/index.d.ts",
10
- "typesVersions": {
11
- "*": {
12
- "browser": [
13
- "src/browser.d.ts"
14
- ]
15
- }
16
- },
17
- "exports": {
18
- ".": {
19
- "browser": {
20
- "types": "./src/browser.d.ts",
21
- "default": "./src/browser.js"
22
- },
23
- "types": "./src/index.d.ts",
24
- "import": "./src/index.js",
25
- "default": "./src/index.js"
26
- },
27
- "./browser": {
28
- "types": "./src/browser.d.ts",
29
- "import": "./src/browser.js",
30
- "default": "./src/browser.js"
31
- }
32
- },
33
- "files": [
34
- "src",
35
- "README.md"
36
- ],
37
- "scripts": {
38
- "test": "node --test"
39
- },
40
- "license": "MIT",
41
- "publishConfig": {
42
- "access": "public",
43
- "registry": "https://registry.npmjs.org"
44
- }
45
- }
1
+ {
2
+ "name": "@kmlckj/licos-platform-sdk",
3
+ "version": "0.6.1",
4
+ "description": "LICOS platform SDK package shell for browser and Node runtimes",
5
+ "author": "kmlckj",
6
+ "type": "module",
7
+ "main": "./src/index.js",
8
+ "browser": "./src/browser.js",
9
+ "types": "./src/index.d.ts",
10
+ "typesVersions": {
11
+ "*": {
12
+ "browser": [
13
+ "src/browser.d.ts"
14
+ ]
15
+ }
16
+ },
17
+ "exports": {
18
+ ".": {
19
+ "browser": {
20
+ "types": "./src/browser.d.ts",
21
+ "default": "./src/browser.js"
22
+ },
23
+ "types": "./src/index.d.ts",
24
+ "import": "./src/index.js",
25
+ "default": "./src/index.js"
26
+ },
27
+ "./browser": {
28
+ "types": "./src/browser.d.ts",
29
+ "import": "./src/browser.js",
30
+ "default": "./src/browser.js"
31
+ }
32
+ },
33
+ "files": [
34
+ "src",
35
+ "README.md"
36
+ ],
37
+ "scripts": {
38
+ "test": "node --test"
39
+ },
40
+ "license": "MIT",
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "registry": "https://registry.npmjs.org"
44
+ }
45
+ }
package/src/browser.d.ts CHANGED
@@ -1,48 +1,3 @@
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
- export interface StorageClientOptions {
32
- baseUrl?: string;
33
- token?: string;
34
- userId?: string;
35
- projectId?: string;
36
- fetch?: typeof fetch;
37
- headers?: HeadersInit;
38
- credentials?: RequestCredentials;
39
- }
40
-
41
- export interface UploadProjectOptions extends StorageClientOptions {
42
- userId?: string;
43
- projectId?: string;
44
- }
45
-
46
1
  export class PlatformSdkError extends Error {
47
2
  status?: number;
48
3
  code?: number;
@@ -51,32 +6,3 @@ export class PlatformSdkError extends Error {
51
6
 
52
7
  export class ConfigurationError extends PlatformSdkError {}
53
8
  export class ApiError extends PlatformSdkError {}
54
-
55
- export class StorageClient {
56
- constructor(options?: StorageClientOptions);
57
- previewUrl(bucket: string, objectKey: string): string;
58
- downloadUrl(bucket: string, objectKey: string): string;
59
- uploadUser(file: UploadSource): Promise<UploadResult>;
60
- uploadProject(file: UploadSource, options?: UploadProjectOptions): Promise<UploadResult>;
61
- download(url: string): Promise<Blob>;
62
- }
63
-
64
- export function previewUrl(
65
- bucket: string,
66
- objectKey: string,
67
- options?: StorageClientOptions,
68
- ): string;
69
- export function downloadUrl(
70
- bucket: string,
71
- objectKey: string,
72
- options?: StorageClientOptions,
73
- ): 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
- export function download(url: string, options?: StorageClientOptions): Promise<Blob>;
package/src/browser.js CHANGED
@@ -1,102 +1 @@
1
- import {
2
- ApiError,
3
- ConfigurationError,
4
- PlatformSdkError,
5
- downloadBlob,
6
- encodeObjectKey,
7
- encodeStorageSegment,
8
- normalizeBaseUrl,
9
- resolveProjectId,
10
- resolveUserId,
11
- uploadFile,
12
- } from './shared.js';
13
-
14
- export class StorageClient {
15
- constructor(options = {}) {
16
- this.baseUrl = options.baseUrl;
17
- this.token = options.token;
18
- this.userId = options.userId;
19
- this.projectId = options.projectId;
20
- this.fetchImpl = options.fetch ?? fetch;
21
- this.headers = options.headers;
22
- this.credentials = options.credentials;
23
- }
24
-
25
- previewUrl(bucket, objectKey) {
26
- const baseUrl = normalizeBaseUrl(this.baseUrl);
27
- return `${baseUrl}/api/v1/public/storage/${encodeStorageSegment(bucket)}/${encodeObjectKey(objectKey)}`;
28
- }
29
-
30
- downloadUrl(bucket, objectKey) {
31
- return `${this.previewUrl(bucket, objectKey)}?download=1`;
32
- }
33
-
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
- async download(url, outputPath) {
60
- if (outputPath !== undefined) {
61
- throw new ConfigurationError(
62
- 'Front-end mode does not support downloading directly to a filesystem path. Omit outputPath to receive a Blob.',
63
- );
64
- }
65
- return downloadBlob(this.fetchImpl, url, {
66
- token: this.token,
67
- headers: this.headers,
68
- credentials: this.credentials,
69
- });
70
- }
71
- }
72
-
73
- function defaultClient(options = {}) {
74
- return new StorageClient(options);
75
- }
76
-
77
- export function previewUrl(bucket, objectKey, options = {}) {
78
- return defaultClient(options).previewUrl(bucket, objectKey);
79
- }
80
-
81
- export function downloadUrl(bucket, objectKey, options = {}) {
82
- return defaultClient(options).downloadUrl(bucket, objectKey);
83
- }
84
-
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
- export async function download(url, outputPathOrOptions, maybeOptions = {}) {
94
- if (typeof outputPathOrOptions === 'string') {
95
- throw new ConfigurationError(
96
- 'Front-end mode does not support downloading directly to a filesystem path. Omit outputPath to receive a Blob.',
97
- );
98
- }
99
- return defaultClient(outputPathOrOptions ?? maybeOptions).download(url);
100
- }
101
-
102
- export { PlatformSdkError, ConfigurationError, ApiError };
1
+ export { PlatformSdkError, ConfigurationError, ApiError } from './shared.js';
@@ -0,0 +1,255 @@
1
+ import { ApiError } from './shared.js';
2
+ import { authHeaders, runtimeConfig } from './runtime.js';
3
+
4
+ function databaseConfig() {
5
+ return runtimeConfig({ environmentOverrideEnv: 'LICOS_DATABASE_ENVIRONMENT' });
6
+ }
7
+
8
+ function databaseUrl(config, route) {
9
+ return `${config.baseUrl}/api/v1/studio/runtime/database/projects/${encodeURIComponent(config.projectId)}${route}`;
10
+ }
11
+
12
+ function compactObject(value) {
13
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined && item !== null));
14
+ }
15
+
16
+ function normalizeSelect(select) {
17
+ if (select === undefined || select === null) return undefined;
18
+ if (typeof select === 'string') {
19
+ return select.split(',').map((item) => item.trim()).filter(Boolean);
20
+ }
21
+ return Array.from(select).map((item) => String(item));
22
+ }
23
+
24
+ async function decodeResponse(response) {
25
+ const text = await response.text();
26
+ const payload = text ? JSON.parse(text) : null;
27
+ if (!response.ok) {
28
+ throw new ApiError(text || `platform database API returned ${response.status}`, {
29
+ status: response.status,
30
+ details: payload,
31
+ });
32
+ }
33
+ if (!payload || typeof payload !== 'object') return payload;
34
+ if ((payload.code !== undefined && payload.code !== 0) || payload.success === false) {
35
+ throw new ApiError(payload.message || 'platform database API failed', {
36
+ status: response.status,
37
+ code: typeof payload.code === 'number' ? payload.code : undefined,
38
+ details: payload,
39
+ });
40
+ }
41
+ return payload.data;
42
+ }
43
+
44
+ async function requestJson(route, body) {
45
+ const config = databaseConfig();
46
+ const payload = { environment: config.environment, ...compactObject(body) };
47
+ const response = await fetch(databaseUrl(config, route), {
48
+ method: 'POST',
49
+ headers: authHeaders(config),
50
+ body: JSON.stringify(payload),
51
+ });
52
+ return decodeResponse(response);
53
+ }
54
+
55
+ export async function query(table, options = {}) {
56
+ return requestJson('/query', {
57
+ table,
58
+ select: normalizeSelect(options.select),
59
+ filters: options.filters?.length ? options.filters : undefined,
60
+ orderBy: options.orderBy?.length ? options.orderBy : undefined,
61
+ range: options.range,
62
+ limit: options.limit,
63
+ offset: options.offset,
64
+ count: options.count,
65
+ head: options.head,
66
+ });
67
+ }
68
+
69
+ export async function single(table, options = {}) {
70
+ return requestJson('/single', {
71
+ table,
72
+ select: normalizeSelect(options.select),
73
+ filters: options.filters?.length ? options.filters : undefined,
74
+ orderBy: options.orderBy?.length ? options.orderBy : undefined,
75
+ mode: options.maybe ? 'maybe_single' : 'single',
76
+ });
77
+ }
78
+
79
+ export async function maybeSingle(table, options = {}) {
80
+ return single(table, { ...options, maybe: true });
81
+ }
82
+
83
+ export async function aggregate(table, aggregateName, options = {}) {
84
+ return requestJson('/aggregate', {
85
+ table,
86
+ filters: options.filters?.length ? options.filters : undefined,
87
+ aggregate: aggregateName,
88
+ field: options.field,
89
+ groupBy: options.groupBy?.length ? options.groupBy : undefined,
90
+ });
91
+ }
92
+
93
+ export async function insert(table, options = {}) {
94
+ return requestJson('/insert', {
95
+ table,
96
+ row: options.row,
97
+ rows: options.rows,
98
+ returning: options.returning ?? true,
99
+ });
100
+ }
101
+
102
+ export async function updateRows(table, options = {}) {
103
+ return requestJson('/update', {
104
+ table,
105
+ values: options.values,
106
+ filters: options.filters?.length ? options.filters : undefined,
107
+ updates: options.updates?.length ? options.updates : undefined,
108
+ returning: options.returning ?? true,
109
+ });
110
+ }
111
+
112
+ export async function deleteRows(table, options = {}) {
113
+ return requestJson('/delete', {
114
+ table,
115
+ filters: options.filters?.length ? options.filters : undefined,
116
+ deletes: options.deletes?.length ? options.deletes : undefined,
117
+ returning: options.returning ?? true,
118
+ });
119
+ }
120
+
121
+ export async function upsert(table, options = {}) {
122
+ return requestJson('/upsert', {
123
+ table,
124
+ row: options.row,
125
+ rows: options.rows,
126
+ conflictKeys: options.conflictKeys?.length ? options.conflictKeys : undefined,
127
+ returning: options.returning ?? true,
128
+ });
129
+ }
130
+
131
+ export async function transaction(operations) {
132
+ const config = databaseConfig();
133
+ const normalized = operations.map((operation) => ({
134
+ type: operation.type,
135
+ request: {
136
+ ...operation.request,
137
+ environment: operation.request?.environment || config.environment,
138
+ },
139
+ }));
140
+ return requestJson('/transaction', { operations: normalized });
141
+ }
142
+
143
+ export async function rpc(functionName, args = {}) {
144
+ return requestJson(`/rpc/${encodeURIComponent(functionName)}`, { args });
145
+ }
146
+
147
+ export function table(name) {
148
+ return new TableQuery(name);
149
+ }
150
+
151
+ class TableQuery {
152
+ constructor(name) {
153
+ this.name = name;
154
+ this._select = undefined;
155
+ this._filters = [];
156
+ this._orderBy = [];
157
+ this._range = undefined;
158
+ this._limit = undefined;
159
+ this._offset = undefined;
160
+ this._count = undefined;
161
+ this._head = undefined;
162
+ }
163
+
164
+ select(...fields) {
165
+ this._select = fields.length === 1 && fields[0].includes(',') ? normalizeSelect(fields[0]) : fields;
166
+ return this;
167
+ }
168
+
169
+ filter(field, op, value) {
170
+ this._filters.push({ field, op, value });
171
+ return this;
172
+ }
173
+
174
+ eq(field, value) { return this.filter(field, 'eq', value); }
175
+ neq(field, value) { return this.filter(field, 'neq', value); }
176
+ gt(field, value) { return this.filter(field, 'gt', value); }
177
+ gte(field, value) { return this.filter(field, 'gte', value); }
178
+ lt(field, value) { return this.filter(field, 'lt', value); }
179
+ lte(field, value) { return this.filter(field, 'lte', value); }
180
+ in(field, value) { return this.filter(field, 'in', value); }
181
+ notIn(field, value) { return this.filter(field, 'not_in', value); }
182
+ isNull(field) { return this.filter(field, 'is_null'); }
183
+ isNotNull(field) { return this.filter(field, 'is_not_null'); }
184
+ like(field, value) { return this.filter(field, 'like', value); }
185
+ ilike(field, value) { return this.filter(field, 'ilike', value); }
186
+
187
+ order(field, options = {}) {
188
+ this._orderBy.push({ field, direction: options.desc ? 'desc' : 'asc' });
189
+ return this;
190
+ }
191
+
192
+ range(from, to) {
193
+ this._range = { from, to };
194
+ return this;
195
+ }
196
+
197
+ limit(value) {
198
+ this._limit = value;
199
+ return this;
200
+ }
201
+
202
+ offset(value) {
203
+ this._offset = value;
204
+ return this;
205
+ }
206
+
207
+ withCount(options = {}) {
208
+ this._count = true;
209
+ this._head = Boolean(options.head);
210
+ return this;
211
+ }
212
+
213
+ execute() {
214
+ return query(this.name, {
215
+ select: this._select,
216
+ filters: this._filters,
217
+ orderBy: this._orderBy,
218
+ range: this._range,
219
+ limit: this._limit,
220
+ offset: this._offset,
221
+ count: this._count,
222
+ head: this._head,
223
+ });
224
+ }
225
+
226
+ single() {
227
+ return single(this.name, {
228
+ select: this._select,
229
+ filters: this._filters,
230
+ orderBy: this._orderBy,
231
+ });
232
+ }
233
+
234
+ maybeSingle() {
235
+ return maybeSingle(this.name, {
236
+ select: this._select,
237
+ filters: this._filters,
238
+ orderBy: this._orderBy,
239
+ });
240
+ }
241
+ }
242
+
243
+ export const database = {
244
+ query,
245
+ single,
246
+ maybeSingle,
247
+ aggregate,
248
+ insert,
249
+ updateRows,
250
+ deleteRows,
251
+ upsert,
252
+ transaction,
253
+ rpc,
254
+ table,
255
+ };