tezign-ai-appstore-sdk 0.1.0 → 0.1.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.
Files changed (38) hide show
  1. package/README.md +131 -9
  2. package/dist/index.d.ts +12 -77
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -195
  5. package/dist/index.js.map +1 -1
  6. package/dist/storage/field-permission.d.ts +33 -0
  7. package/dist/storage/field-permission.d.ts.map +1 -0
  8. package/dist/storage/field-permission.js +138 -0
  9. package/dist/storage/field-permission.js.map +1 -0
  10. package/dist/storage/migration.d.ts +72 -0
  11. package/dist/storage/migration.d.ts.map +1 -0
  12. package/dist/storage/migration.js +227 -0
  13. package/dist/storage/migration.js.map +1 -0
  14. package/dist/storage/schema-client.d.ts +128 -0
  15. package/dist/storage/schema-client.d.ts.map +1 -0
  16. package/dist/storage/schema-client.js +468 -0
  17. package/dist/storage/schema-client.js.map +1 -0
  18. package/dist/storage/schema.d.ts +645 -0
  19. package/dist/storage/schema.d.ts.map +1 -0
  20. package/dist/storage/schema.js +750 -0
  21. package/dist/storage/schema.js.map +1 -0
  22. package/dist/storage/storage-types.d.ts +191 -0
  23. package/dist/storage/storage-types.d.ts.map +1 -0
  24. package/dist/storage/storage-types.js +2 -0
  25. package/dist/storage/storage-types.js.map +1 -0
  26. package/dist/storage/storage.d.ts +268 -0
  27. package/dist/storage/storage.d.ts.map +1 -0
  28. package/dist/storage/storage.js +440 -0
  29. package/dist/storage/storage.js.map +1 -0
  30. package/dist/storage/typed-storage.d.ts +311 -0
  31. package/dist/storage/typed-storage.d.ts.map +1 -0
  32. package/dist/storage/typed-storage.js +490 -0
  33. package/dist/storage/typed-storage.js.map +1 -0
  34. package/dist/uploader/index.d.ts +78 -0
  35. package/dist/uploader/index.d.ts.map +1 -0
  36. package/dist/uploader/index.js +196 -0
  37. package/dist/uploader/index.js.map +1 -0
  38. package/package.json +11 -3
package/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # tezign-ai-appstore-sdk
2
2
 
3
- 浏览器端上传 SDK:调用平台的 uploads API 创建文件记录,获取最小权限 STS 临时凭证,然后使用 `ali-oss` 在浏览器侧执行 `multipartUpload` 上传,并返回你们的 CDN 永久公网链接。
3
+ AI 应用商店 SDK:提供文件上传和结构化数据存储功能。
4
4
 
5
- ## 使用方式(浏览器)
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install tezign-ai-appstore-sdk
9
+ ```
10
+
11
+ ## 快速开始
12
+
13
+ ### 文件上传
6
14
 
7
15
  ```ts
8
16
  import { createOssUploadClient } from 'tezign-ai-appstore-sdk'
@@ -10,21 +18,135 @@ import { createOssUploadClient } from 'tezign-ai-appstore-sdk'
10
18
  const client = createOssUploadClient({
11
19
  baseUrl: 'https://platform.example.com',
12
20
  projectId: 'your-project-id',
13
- // 第三方 Supabase:由业务方获取 access_token 后提供给 SDK
14
- getAccessToken: async () => supabase.auth.getSession().then((r) => r.data.session?.access_token ?? null),
21
+ getAccessToken: async () => 'your-token',
15
22
  })
16
23
 
17
24
  const result = await client.upload({
18
25
  file,
19
- onProgress: (p) => console.log(p.loaded, p.total),
26
+ onProgress: (p) => console.log(`${p.loaded}/${p.total}`),
27
+ })
28
+
29
+ console.log(result.publicUrl)
30
+ ```
31
+
32
+ ### 结构化数据存储
33
+
34
+ ```ts
35
+ import { createSchemaClient, createSchema } from 'tezign-ai-appstore-sdk'
36
+
37
+ // 定义数据模型
38
+ const postSchema = createSchema('posts')
39
+ .string('title', { required: true, filterable: true })
40
+ .string('content', { required: true })
41
+ .string('status', { enum: ['draft', 'published'], filterable: true })
42
+ .setVersion(1)
43
+ .build()
44
+
45
+ // 创建客户端(同步返回,自动初始化 + 自动迁移)
46
+ const db = createSchemaClient({ posts: postSchema }, storageClient)
47
+
48
+ // 数据操作(类型完全推导,自动等待初始化完成)
49
+ const post = await db.posts.create('post-1', {
50
+ title: 'Hello',
51
+ content: 'World',
52
+ status: 'published',
53
+ })
54
+ ```
55
+
56
+ ## 核心概念
57
+
58
+ **统一入口**:所有数据建模和操作通过 `createSchemaClient` 完成。
59
+
60
+ 1. **定义 Schema** - 使用 `createSchema` 定义数据模型
61
+ 2. **创建客户端** - 使用 `createSchemaClient` 自动完成初始化、迁移和客户端创建
62
+ 3. **数据操作** - 通过返回的客户端进行类型安全的数据操作
63
+
64
+ ## Schema 定义
65
+
66
+ ```ts
67
+ const postSchema = createSchema('posts')
68
+ .string('title', { required: true, filterable: true })
69
+ .string('content', { required: true })
70
+ .number('viewCount', { default: 0 })
71
+ .setVersion(1)
72
+ .setPolicy({
73
+ createSubjectsByScope: {
74
+ members: ['project:member'],
75
+ },
76
+ })
77
+ .build()
78
+ ```
79
+
80
+ **字段类型**:`.string()` `.number()` `.boolean()` `.datetime()` `.json()`
81
+
82
+ **字段选项**:
83
+ - `required` - 是否必填
84
+ - `default` - 默认值
85
+ - `filterable` - 是否可过滤
86
+ - `sortable` - 是否可排序
87
+ - `previousName` - 声明之前的字段名(用于字段更名)
88
+
89
+ ## 自动迁移
90
+
91
+ `createSchemaClient` 自动检测并应用 Schema 变更:
92
+
93
+ ```ts
94
+ // 字段更名
95
+ const postSchemaV2 = createSchema('posts')
96
+ .string('title', { required: true })
97
+ .string('authorId', {
98
+ filterable: true,
99
+ previousName: 'author' // 声明更名
100
+ })
101
+ .setVersion(2)
102
+ .build()
103
+
104
+ const db = createSchemaClient({ posts: postSchemaV2 }, storageClient)
105
+ ```
106
+
107
+ **预览变更**:
108
+
109
+ ```ts
110
+ const db = createSchemaClient({ posts: postSchema }, storageClient, {
111
+ migrateOptions: {
112
+ dryRun: true,
113
+ onChangesDetected: (namespace, changes) => {
114
+ console.log(changes)
115
+ }
116
+ }
117
+ })
118
+ ```
119
+
120
+ ## 数据操作
121
+
122
+ ```ts
123
+ // 创建
124
+ const post = await db.posts.create('post-1', { title: 'Hello' })
125
+
126
+ // 获取
127
+ const post = await db.posts.get('post-1')
128
+
129
+ // 更新
130
+ await db.posts.update('post-1', { title: 'Updated' }, post.version)
131
+
132
+ // 删除
133
+ await db.posts.delete('post-1')
134
+
135
+ // 查询
136
+ const result = await db.posts.findMany({
137
+ filter: { field: 'status', op: 'eq', value: 'published' },
138
+ sort: { field: '__sys.createdAt', direction: 'desc' },
139
+ limit: 20,
20
140
  })
21
141
 
22
- console.log(result.publicUrl) // https://{OSS_CDN_DOMAIN}/{objectKey}
142
+ // 计数
143
+ const count = await db.posts.count()
23
144
  ```
24
145
 
25
- 未提供 `getAccessToken` 时,SDK 默认使用 cookie(`credentials: 'include'`)。
146
+ ## 完整文档
26
147
 
27
- ## API 文档
148
+ [API.md](./API.md)。
28
149
 
29
- `API.md`。
150
+ ## License
30
151
 
152
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,78 +1,13 @@
1
- export type UploadProgress = {
2
- loaded: number;
3
- total: number;
4
- };
5
- export type CreateOssUploadClientOptions = {
6
- /**
7
- * Base URL(不带末尾 /)
8
- * - 不传时默认使用 window.location.origin(同域部署场景)
9
- */
10
- baseUrl?: string;
11
- /**
12
- * 固定 projectId(多项目场景可按项目创建 client)
13
- * - 不传时由服务端通过 host 解析
14
- */
15
- projectId?: string;
16
- /**
17
- * 返回 Bearer token(第三方 Supabase 的 access_token)
18
- * - 返回 null 表示不使用 Authorization(可用于同域 cookie 场景)
19
- */
20
- getAccessToken?: () => Promise<string | null>;
21
- };
22
- export type PrepareUploadInput = {
23
- file: File;
24
- };
25
- export type PrepareUploadResult = {
26
- uploadToken: string;
27
- objectKey: string;
28
- publicUrl: string;
29
- };
30
- export type CompleteUploadResult = {
31
- id: string;
32
- objectKey: string;
33
- filename: string;
34
- contentType: string | null;
35
- size: number | null;
36
- status: string;
37
- createdAt: string;
38
- publicUrl: string;
39
- };
40
- export type UploadTokenRef = {
41
- uploadToken: string;
42
- };
43
- export type UploadListItem = {
44
- id: string;
45
- objectKey: string;
46
- filename: string;
47
- contentType: string | null;
48
- size: number | null;
49
- status: string;
50
- createdAt: string;
51
- publicUrl: string | null;
52
- };
53
- export type UploadListResult = {
54
- page: number;
55
- pageSize: number;
56
- total: number;
57
- items: UploadListItem[];
58
- };
59
- export type UploadListOptions = {
60
- page?: number;
61
- pageSize?: number;
62
- };
63
- export type UploadInput = PrepareUploadInput & {
64
- onProgress?: (p: UploadProgress) => void;
65
- /** ali-oss multipart 并发数(默认 3) */
66
- concurrency?: number;
67
- };
68
- export declare function createOssUploadClient(options: CreateOssUploadClientOptions): {
69
- upload: (input: UploadInput) => Promise<CompleteUploadResult>;
70
- list: (options?: UploadListOptions) => Promise<{
71
- success: true;
72
- data: UploadListResult;
73
- }>;
74
- remove: (fileId: string) => Promise<{
75
- success: true;
76
- }>;
77
- };
1
+ export { createStorageClient, StorageClient, StorageError } from './storage/storage.js';
2
+ export type { StorageItem, Filter, Sort, StorageIndexDefinition, NamespacePolicy, PermissionResource, PermissionAction, CheckPermissionRequest, CheckPermissionResponse, } from './storage/storage-types.js';
3
+ export { createSchema, SchemaBuilder, OWNER_ID, subject, buildAclConfig, } from './storage/schema.js';
4
+ export type { FieldDef, FieldPermission, StringFieldDef, NumberFieldDef, BooleanFieldDef, DateTimeFieldDef, JsonFieldDef, FieldsDefinition, SchemaDefinition, IndexConfig, AccessConfig, InferSchemaType, CreateSchemaType, UpdateSchemaType, SchemaAclConfig, AclConfig, } from './storage/schema.js';
5
+ export type { TypedStorageClient } from './storage/typed-storage.js';
6
+ export { createTypedStorage } from './storage/typed-storage.js';
7
+ export { createFieldPermissionChecker } from './storage/field-permission.js';
8
+ export type { FieldPermissionContext, FieldPermissionCheck } from './storage/field-permission.js';
9
+ export { createSchemaClient, defineSchemas, PermissionError, UserCancelledError, SchemaClient, } from './storage/schema-client.js';
10
+ export type { SchemaDefinitions, InferSchemaTypes, CreateSchemaClientOptions, MigrateOptions, MigrationChange, MigrationResult, ConfirmDialogOptions, ConfirmDialogResult, ConfirmDialogFunction, } from './storage/schema-client.js';
11
+ export { createOssUploadClient } from './uploader/index.js';
12
+ export type { CreateOssUploadClientOptions, CompleteUploadResult, UploadListItem, UploadListResult, } from './uploader/index.js';
78
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAOD,MAAM,MAAM,4BAA4B,GAAG;IACzC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAC9C,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,IAAI,CAAA;CACX,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAEhC,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpD,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,cAAc,EAAE,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,kBAAkB,GAAG;IAC7C,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAA;IACxC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAuHD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B;oBAmH5C,WAAW,KAAG,OAAO,CAAC,oBAAoB,CAAC;qBAK1C,iBAAiB,KAAG,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,gBAAgB,CAAA;KAAE,CAAC;qBAetE,MAAM,KAAG,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,CAAC;EAgBlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACvF,YAAY,EACV,WAAW,EACX,MAAM,EACN,IAAI,EACJ,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,OAAO,EACP,cAAc,GACf,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,QAAQ,EACR,eAAe,EACf,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,SAAS,GACV,MAAM,qBAAqB,CAAA;AAE5B,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAE/D,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAC5E,YAAY,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AAEjG,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,YAAY,GACb,MAAM,4BAA4B,CAAA;AACnC,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,YAAY,EACV,4BAA4B,EAC5B,oBAAoB,EACpB,cAAc,EACd,gBAAgB,GACjB,MAAM,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -1,196 +1,7 @@
1
- function trimTrailingSlash(url) {
2
- const v = url || '';
3
- return v.endsWith('/') ? v.slice(0, -1) : v;
4
- }
5
- function isRecord(value) {
6
- return typeof value === 'object' && value !== null;
7
- }
8
- function isApiSuccess(value) {
9
- return isRecord(value) && value.success === true && 'data' in value;
10
- }
11
- function isApiOk(value) {
12
- return isRecord(value) && value.success === true;
13
- }
14
- function getErrorMessage(value) {
15
- if (!isRecord(value))
16
- return null;
17
- const error = value.error;
18
- if (typeof error === 'string' && error.trim())
19
- return error;
20
- const message = value.message;
21
- if (typeof message === 'string' && message.trim())
22
- return message;
23
- return null;
24
- }
25
- async function fetchJson(fetchImpl, url, init) {
26
- const res = await fetchImpl(url, init);
27
- const text = await res.text().catch(() => '');
28
- let json = null;
29
- try {
30
- json = text ? JSON.parse(text) : null;
31
- }
32
- catch {
33
- // ignore
34
- }
35
- if (!res.ok) {
36
- const msg = getErrorMessage(json) || res.statusText || 'Request failed';
37
- throw new Error(`${res.status} ${msg}`);
38
- }
39
- return json;
40
- }
41
- async function fetchApiSuccess(fetchImpl, url, init) {
42
- const json = await fetchJson(fetchImpl, url, init);
43
- const error = getErrorMessage(json);
44
- if (error)
45
- throw new Error(error);
46
- if (isApiSuccess(json)) {
47
- return json;
48
- }
49
- throw new Error('响应格式无效');
50
- }
51
- async function fetchApiData(fetchImpl, url, init) {
52
- const result = await fetchApiSuccess(fetchImpl, url, init);
53
- return result.data;
54
- }
55
- async function fetchApiOk(fetchImpl, url, init) {
56
- const json = await fetchJson(fetchImpl, url, init);
57
- const error = getErrorMessage(json);
58
- if (error)
59
- throw new Error(error);
60
- if (isApiOk(json)) {
61
- return json;
62
- }
63
- throw new Error('响应格式无效');
64
- }
65
- function resolveAliOssConstructor(mod) {
66
- const candidate = isRecord(mod) && 'default' in mod ? mod.default : mod;
67
- if (typeof candidate !== 'function') {
68
- throw new Error('ali-oss 模块加载失败');
69
- }
70
- return candidate;
71
- }
72
- function resolveUploadRef(prepared) {
73
- return { uploadToken: prepared.uploadToken };
74
- }
75
- export function createOssUploadClient(options) {
76
- const fetchImpl = fetch;
77
- const baseUrl = trimTrailingSlash(options.baseUrl ||
78
- (typeof window !== 'undefined' ? window.location.origin : ''));
79
- const credentials = options.getAccessToken ? 'omit' : 'include';
80
- const projectId = options.projectId;
81
- if (!baseUrl) {
82
- throw new Error('baseUrl is required in non-browser environments');
83
- }
84
- async function buildAuthHeaders() {
85
- const token = options.getAccessToken ? await options.getAccessToken() : null;
86
- return token ? { Authorization: `Bearer ${token}` } : {};
87
- }
88
- async function prepare(input) {
89
- const headers = await buildAuthHeaders();
90
- const url = `${baseUrl}/api/uploads/prepare`;
91
- const data = await fetchApiData(fetchImpl, url, {
92
- method: 'POST',
93
- headers: {
94
- ...headers,
95
- 'Content-Type': 'application/json',
96
- },
97
- credentials,
98
- body: JSON.stringify({
99
- projectId,
100
- filename: input.file.name,
101
- size: input.file.size,
102
- contentType: input.file.type || 'application/octet-stream',
103
- }),
104
- });
105
- return data;
106
- }
107
- async function complete(input) {
108
- const headers = await buildAuthHeaders();
109
- const url = `${baseUrl}/api/uploads/complete`;
110
- const data = await fetchApiData(fetchImpl, url, {
111
- method: 'POST',
112
- headers: {
113
- ...headers,
114
- 'Content-Type': 'application/json',
115
- },
116
- credentials,
117
- body: JSON.stringify({ ...input }),
118
- });
119
- return data;
120
- }
121
- async function getSts(input) {
122
- const headers = await buildAuthHeaders();
123
- const url = `${baseUrl}/api/uploads/sts`;
124
- const data = await fetchApiData(fetchImpl, url, {
125
- method: 'POST',
126
- headers: {
127
- ...headers,
128
- 'Content-Type': 'application/json',
129
- },
130
- credentials,
131
- body: JSON.stringify({ ...input }),
132
- });
133
- return data;
134
- }
135
- async function ossUploadViaAliOss(input, prepared) {
136
- const uploadRef = resolveUploadRef(prepared);
137
- const sts = await getSts(uploadRef);
138
- // ali-oss 为 CJS/ESM 混合导出,这里做一次兼容处理
139
- const OSSMod = (await import('ali-oss'));
140
- const OSS = resolveAliOssConstructor(OSSMod);
141
- const client = new OSS({
142
- region: sts.region,
143
- bucket: sts.bucket,
144
- accessKeyId: sts.credentials.accessKeyId,
145
- accessKeySecret: sts.credentials.accessKeySecret,
146
- stsToken: sts.credentials.securityToken,
147
- secure: true,
148
- });
149
- const parallel = Math.max(1, Math.min(8, input.concurrency ?? 3));
150
- await client.multipartUpload(sts.objectKey, input.file, {
151
- parallel,
152
- progress: async (p) => {
153
- if (!input.onProgress)
154
- return;
155
- input.onProgress({ loaded: Math.round(p * input.file.size), total: input.file.size });
156
- },
157
- });
158
- return await complete(uploadRef);
159
- }
160
- async function upload(input) {
161
- const prepared = await prepare({ file: input.file });
162
- return await ossUploadViaAliOss(input, prepared);
163
- }
164
- async function list(options) {
165
- const headers = await buildAuthHeaders();
166
- const urlObj = new URL(`${baseUrl}/api/uploads`);
167
- if (projectId)
168
- urlObj.searchParams.set('projectId', projectId);
169
- if (options?.page)
170
- urlObj.searchParams.set('page', String(options.page));
171
- if (options?.pageSize)
172
- urlObj.searchParams.set('pageSize', String(options.pageSize));
173
- return await fetchApiSuccess(fetchImpl, urlObj.toString(), {
174
- method: 'GET',
175
- headers,
176
- credentials,
177
- });
178
- }
179
- async function remove(fileId) {
180
- const headers = await buildAuthHeaders();
181
- const urlObj = new URL(`${baseUrl}/api/uploads/${encodeURIComponent(fileId)}`);
182
- if (projectId)
183
- urlObj.searchParams.set('projectId', projectId);
184
- return await fetchApiOk(fetchImpl, urlObj.toString(), {
185
- method: 'DELETE',
186
- headers,
187
- credentials,
188
- });
189
- }
190
- return {
191
- upload,
192
- list,
193
- remove,
194
- };
195
- }
1
+ export { createStorageClient, StorageClient, StorageError } from './storage/storage.js';
2
+ export { createSchema, SchemaBuilder, OWNER_ID, subject, buildAclConfig, } from './storage/schema.js';
3
+ export { createTypedStorage } from './storage/typed-storage.js';
4
+ export { createFieldPermissionChecker } from './storage/field-permission.js';
5
+ export { createSchemaClient, defineSchemas, PermissionError, UserCancelledError, SchemaClient, } from './storage/schema-client.js';
6
+ export { createOssUploadClient } from './uploader/index.js';
196
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAavF,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,OAAO,EACP,cAAc,GACf,MAAM,qBAAqB,CAAA;AAqB5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAE/D,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAG5E,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,YAAY,GACb,MAAM,4BAA4B,CAAA;AAanC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,33 @@
1
+ import type { SchemaDefinition, FieldsDefinition } from './schema.js';
2
+ export interface FieldPermissionContext {
3
+ userId?: string;
4
+ role?: string;
5
+ isOwner?: boolean;
6
+ }
7
+ export interface FieldPermissionCheck {
8
+ allowed: boolean;
9
+ forbiddenFields: string[];
10
+ reason?: string;
11
+ }
12
+ export declare class FieldPermissionChecker<TFields extends FieldsDefinition> {
13
+ private schema;
14
+ constructor(schema: SchemaDefinition<TFields>);
15
+ /**
16
+ * 检查创建操作是否有权限修改指定字段
17
+ */
18
+ checkCreate(data: Record<string, unknown>, context: FieldPermissionContext): FieldPermissionCheck;
19
+ /**
20
+ * 检查更新操作是否有权限修改指定字段
21
+ */
22
+ checkUpdate(patch: Record<string, unknown>, existingData: Record<string, unknown>, context: FieldPermissionContext): FieldPermissionCheck;
23
+ /**
24
+ * 过滤返回数据中的隐藏字段
25
+ */
26
+ filterHiddenFields(data: Record<string, unknown>): Record<string, unknown>;
27
+ /**
28
+ * 获取可编辑的字段列表
29
+ */
30
+ getEditableFields(context: FieldPermissionContext): string[];
31
+ }
32
+ export declare function createFieldPermissionChecker<TFields extends FieldsDefinition>(schema: SchemaDefinition<TFields>): FieldPermissionChecker<TFields>;
33
+ //# sourceMappingURL=field-permission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-permission.d.ts","sourceRoot":"","sources":["../../src/storage/field-permission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAmB,MAAM,aAAa,CAAA;AAEtF,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,qBAAa,sBAAsB,CAAC,OAAO,SAAS,gBAAgB;IACtD,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC;IAErD;;OAEG;IACH,WAAW,CACT,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,sBAAsB,GAC9B,oBAAoB;IAyBvB;;OAEG;IACH,WAAW,CACT,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,OAAO,EAAE,sBAAsB,GAC9B,oBAAoB;IAkDvB;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAuB1E;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,EAAE;CA+B7D;AAED,wBAAgB,4BAA4B,CAAC,OAAO,SAAS,gBAAgB,EAC3E,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAChC,sBAAsB,CAAC,OAAO,CAAC,CAEjC"}
@@ -0,0 +1,138 @@
1
+ export class FieldPermissionChecker {
2
+ schema;
3
+ constructor(schema) {
4
+ this.schema = schema;
5
+ }
6
+ /**
7
+ * 检查创建操作是否有权限修改指定字段
8
+ */
9
+ checkCreate(data, context) {
10
+ const forbiddenFields = [];
11
+ for (const [fieldName, value] of Object.entries(data)) {
12
+ const fieldDef = this.schema.fields[fieldName];
13
+ if (!fieldDef)
14
+ continue;
15
+ const permission = fieldDef.permission;
16
+ if (!permission)
17
+ continue;
18
+ // 检查 internal 字段
19
+ if (permission.internal && context.role !== 'admin') {
20
+ forbiddenFields.push(fieldName);
21
+ }
22
+ }
23
+ return {
24
+ allowed: forbiddenFields.length === 0,
25
+ forbiddenFields,
26
+ reason: forbiddenFields.length > 0
27
+ ? `Cannot create internal fields: ${forbiddenFields.join(', ')}`
28
+ : undefined,
29
+ };
30
+ }
31
+ /**
32
+ * 检查更新操作是否有权限修改指定字段
33
+ */
34
+ checkUpdate(patch, existingData, context) {
35
+ const forbiddenFields = [];
36
+ const reasons = [];
37
+ for (const [fieldName, value] of Object.entries(patch)) {
38
+ const fieldDef = this.schema.fields[fieldName];
39
+ if (!fieldDef)
40
+ continue;
41
+ const permission = fieldDef.permission;
42
+ if (!permission)
43
+ continue;
44
+ // 检查 immutable 字段
45
+ if (permission.immutable) {
46
+ // 检查是否真的在修改(值发生变化)
47
+ if (JSON.stringify(existingData[fieldName]) !== JSON.stringify(value)) {
48
+ forbiddenFields.push(fieldName);
49
+ reasons.push(`Field "${fieldName}" is immutable`);
50
+ }
51
+ continue;
52
+ }
53
+ // 检查 internal 字段
54
+ if (permission.internal && context.role !== 'admin') {
55
+ forbiddenFields.push(fieldName);
56
+ reasons.push(`Field "${fieldName}" is internal`);
57
+ continue;
58
+ }
59
+ // 检查 editableBy 限制
60
+ if (permission.editableBy && permission.editableBy.length > 0) {
61
+ const canEdit = permission.editableBy.some(role => {
62
+ if (role === 'owner')
63
+ return context.isOwner;
64
+ if (role === 'admin')
65
+ return context.role === 'admin' || context.role === 'owner';
66
+ return context.role === role;
67
+ });
68
+ if (!canEdit) {
69
+ forbiddenFields.push(fieldName);
70
+ reasons.push(`Field "${fieldName}" can only be edited by: ${permission.editableBy.join(', ')}`);
71
+ }
72
+ }
73
+ }
74
+ return {
75
+ allowed: forbiddenFields.length === 0,
76
+ forbiddenFields,
77
+ reason: reasons.length > 0 ? reasons.join('; ') : undefined,
78
+ };
79
+ }
80
+ /**
81
+ * 过滤返回数据中的隐藏字段
82
+ */
83
+ filterHiddenFields(data) {
84
+ const result = {};
85
+ for (const [fieldName, value] of Object.entries(data)) {
86
+ const fieldDef = this.schema.fields[fieldName];
87
+ if (!fieldDef) {
88
+ // 保留未知字段(可能是系统字段如 __sys)
89
+ result[fieldName] = value;
90
+ continue;
91
+ }
92
+ const permission = fieldDef.permission;
93
+ if (permission?.hidden || permission?.internal) {
94
+ // 跳过隐藏/内部字段
95
+ continue;
96
+ }
97
+ result[fieldName] = value;
98
+ }
99
+ return result;
100
+ }
101
+ /**
102
+ * 获取可编辑的字段列表
103
+ */
104
+ getEditableFields(context) {
105
+ const editable = [];
106
+ for (const [fieldName, fieldDef] of Object.entries(this.schema.fields)) {
107
+ const permission = fieldDef.permission;
108
+ if (!permission) {
109
+ editable.push(fieldName);
110
+ continue;
111
+ }
112
+ // internal 字段不可编辑
113
+ if (permission.internal)
114
+ continue;
115
+ // immutable 字段不可编辑
116
+ if (permission.immutable)
117
+ continue;
118
+ // 检查 editableBy
119
+ if (permission.editableBy && permission.editableBy.length > 0) {
120
+ const canEdit = permission.editableBy.some(role => {
121
+ if (role === 'owner')
122
+ return context.isOwner;
123
+ if (role === 'admin')
124
+ return context.role === 'admin' || context.role === 'owner';
125
+ return context.role === role;
126
+ });
127
+ if (!canEdit)
128
+ continue;
129
+ }
130
+ editable.push(fieldName);
131
+ }
132
+ return editable;
133
+ }
134
+ }
135
+ export function createFieldPermissionChecker(schema) {
136
+ return new FieldPermissionChecker(schema);
137
+ }
138
+ //# sourceMappingURL=field-permission.js.map