@h-ai/storage 0.1.0-alpha.13 → 0.1.0-alpha.16

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
@@ -51,43 +51,16 @@ await uploadWithPresignedUrl(uploadUrl, file)
51
51
 
52
52
  > 注意:`@h-ai/storage` 不内置 HTTP 路由;请在应用层自行实现签名 URL API。
53
53
 
54
- ## API 契约(`@h-ai/storage/api`)
54
+ ## HTTP API 契约
55
55
 
56
- 独立子路径 `@h-ai/storage/api` 导出所有存储端点的 Zod Schema 和端点契约定义,客户端和服务端共享同一份类型定义,编译时保证 I/O 一致性。
56
+ 公共 HTTP API 统一由 `@h-ai/api-contract` 提供,并由 `@h-ai/serv` 的 `createStorageProcedures({ storage })` 绑定到本模块能力。
57
57
 
58
58
  ```ts
59
- import { PresignPutInputSchema, storageEndpoints } from '@h-ai/storage/api'
59
+ import { apiClient } from '@h-ai/api-client'
60
60
 
61
- // 端点契约包含 6 个端点:
62
- // presignDownload / presignUpload / fileInfo / listFiles / deleteFile / deleteFiles
63
-
64
- // 客户端调用
65
- const { url } = await api.call(storageEndpoints.presignUpload, { key: 'avatar.png' })
66
-
67
- // 服务端路由
68
- export const POST = kit.fromContract(storageEndpoints.presignUpload, async (input) => {
69
- const result = await storage.presign.putUrl(input.key, input)
70
- return result.success ? result.data : kit.response.internalError(result.error.message)
71
- })
72
-
73
- // 独立使用 Schema 做校验
74
- const parsed = PresignPutInputSchema.safeParse(requestBody)
75
- if (!parsed.success) { /* 校验失败 */ }
61
+ const result = await apiClient.storage.presignedUrls.createUpload({ key: 'avatar.png' })
76
62
  ```
77
63
 
78
- **导出的 Schema:**
79
-
80
- - `FileMetadataSchema` — 文件元数据
81
- - `PresignGetInputSchema` / `PresignPutInputSchema` — 签名 URL 入参
82
- - `PresignUrlOutputSchema` — 签名 URL 出参
83
- - `ListFilesOutputSchema` — 文件列表出参
84
- - `DeleteFileInputSchema` / `DeleteFilesInputSchema` — 删除入参
85
- - `FileInfoInputSchema` — 文件信息入参
86
-
87
- **导出的类型(从 Schema 推导):**
88
-
89
- `PresignGetInput` / `PresignPutInput` / `PresignUrlOutput` / `ListFilesOutput` / `DeleteFileInput` / `DeleteFilesInput` / `FileInfoInput`
90
-
91
64
  ## API 概览
92
65
 
93
66
  - `storage.file`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/storage",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.13",
4
+ "version": "0.1.0-alpha.16",
5
5
  "description": "Hai Framework object storage module for S3-compatible and local backends.",
6
6
  "license": "Apache-2.0",
7
7
  "exports": {
@@ -14,10 +14,6 @@
14
14
  "./client": {
15
15
  "types": "./dist/client/index.d.ts",
16
16
  "import": "./dist/client/index.js"
17
- },
18
- "./api": {
19
- "types": "./dist/api/index.d.ts",
20
- "import": "./dist/api/index.js"
21
17
  }
22
18
  },
23
19
  "main": "./dist/node.js",
@@ -33,7 +29,7 @@
33
29
  "@aws-sdk/client-s3": "^3.723.0",
34
30
  "@aws-sdk/s3-request-presigner": "^3.723.0",
35
31
  "zod": "^4.3.6",
36
- "@h-ai/core": "0.1.0-alpha.13"
32
+ "@h-ai/core": "0.1.0-alpha.16"
37
33
  },
38
34
  "devDependencies": {
39
35
  "@types/node": "^25.1.0",
@@ -1,167 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- /**
4
- * @h-ai/storage/api — 存储 API 端点契约定义
5
- *
6
- * 所有 storage 模块的 API 端点(path + method + schema),
7
- * 客户端和服务端都从此处引用,编译时保证一致性。
8
- * @module storage-api-contract
9
- */
10
-
11
- interface EndpointDef<TInput = unknown, TOutput = unknown> {
12
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
13
- path: string;
14
- input: z.ZodType<TInput>;
15
- output: z.ZodType<TOutput>;
16
- requireAuth?: boolean;
17
- meta?: {
18
- summary?: string;
19
- tags?: string[];
20
- };
21
- }
22
- /**
23
- * storage 所有 API 端点
24
- *
25
- * @example
26
- * ```ts
27
- * // 客户端
28
- * import { storageEndpoints } from '@h-ai/storage/api'
29
- * const { url } = await api.call(storageEndpoints.presignUpload, { key: 'avatar.png' })
30
- *
31
- * // 服务端
32
- * export const POST = kit.fromContract(storageEndpoints.presignUpload, async (input) => {
33
- * const result = await storage.presign.putUrl(input.key, input)
34
- * return result.success ? result.data : kit.response.internalError(result.error.message)
35
- * })
36
- * ```
37
- */
38
- declare const storageEndpoints: {
39
- /** 获取下载签名 URL */
40
- readonly presignDownload: EndpointDef<{
41
- key: string;
42
- expiresIn?: number | undefined;
43
- }, {
44
- url: string;
45
- key: string;
46
- expiresAt?: Date | undefined;
47
- }>;
48
- /** 获取上传签名 URL */
49
- readonly presignUpload: EndpointDef<{
50
- key: string;
51
- contentType?: string | undefined;
52
- contentLength?: number | undefined;
53
- expiresIn?: number | undefined;
54
- }, {
55
- url: string;
56
- key: string;
57
- expiresAt?: Date | undefined;
58
- }>;
59
- /** 获取文件元数据 */
60
- readonly fileInfo: EndpointDef<{
61
- key: string;
62
- }, {
63
- key: string;
64
- size: number;
65
- contentType: string;
66
- lastModified: Date;
67
- etag?: string | undefined;
68
- metadata?: Record<string, string> | undefined;
69
- }>;
70
- /** 列出文件 */
71
- readonly listFiles: EndpointDef<{
72
- prefix?: string | undefined;
73
- maxKeys?: number | undefined;
74
- continuationToken?: string | undefined;
75
- delimiter?: string | undefined;
76
- }, {
77
- files: {
78
- key: string;
79
- size: number;
80
- contentType: string;
81
- lastModified: Date;
82
- etag?: string | undefined;
83
- metadata?: Record<string, string> | undefined;
84
- }[];
85
- commonPrefixes: string[];
86
- isTruncated: boolean;
87
- nextContinuationToken?: string | undefined;
88
- }>;
89
- /** 删除单个文件 */
90
- readonly deleteFile: EndpointDef<{
91
- key: string;
92
- }, void>;
93
- /** 批量删除文件 */
94
- readonly deleteFiles: EndpointDef<{
95
- keys: string[];
96
- }, void>;
97
- };
98
-
99
- /**
100
- * @h-ai/storage/api — 存储 API 契约 Schema
101
- *
102
- * 入参/出参 Schema,客户端和服务端共享的唯一真相源。
103
- * @module storage-api-schemas
104
- */
105
-
106
- /** 文件元数据 Schema */
107
- declare const FileMetadataSchema: z.ZodObject<{
108
- key: z.ZodString;
109
- size: z.ZodNumber;
110
- contentType: z.ZodString;
111
- lastModified: z.ZodCoercedDate<unknown>;
112
- etag: z.ZodOptional<z.ZodString>;
113
- metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
114
- }, z.core.$strip>;
115
- /** 获取下载签名 URL 入参 */
116
- declare const PresignGetInputSchema: z.ZodObject<{
117
- key: z.ZodString;
118
- expiresIn: z.ZodOptional<z.ZodNumber>;
119
- }, z.core.$strip>;
120
- /** 获取上传签名 URL 入参 */
121
- declare const PresignPutInputSchema: z.ZodObject<{
122
- key: z.ZodString;
123
- contentType: z.ZodOptional<z.ZodString>;
124
- contentLength: z.ZodOptional<z.ZodNumber>;
125
- expiresIn: z.ZodOptional<z.ZodNumber>;
126
- }, z.core.$strip>;
127
- /** 签名 URL 出参 */
128
- declare const PresignUrlOutputSchema: z.ZodObject<{
129
- url: z.ZodString;
130
- key: z.ZodString;
131
- expiresAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
132
- }, z.core.$strip>;
133
- /** 文件列表出参 */
134
- declare const ListFilesOutputSchema: z.ZodObject<{
135
- files: z.ZodArray<z.ZodObject<{
136
- key: z.ZodString;
137
- size: z.ZodNumber;
138
- contentType: z.ZodString;
139
- lastModified: z.ZodCoercedDate<unknown>;
140
- etag: z.ZodOptional<z.ZodString>;
141
- metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
142
- }, z.core.$strip>>;
143
- commonPrefixes: z.ZodArray<z.ZodString>;
144
- nextContinuationToken: z.ZodOptional<z.ZodString>;
145
- isTruncated: z.ZodBoolean;
146
- }, z.core.$strip>;
147
- /** 删除文件入参 */
148
- declare const DeleteFileInputSchema: z.ZodObject<{
149
- key: z.ZodString;
150
- }, z.core.$strip>;
151
- /** 批量删除文件入参 */
152
- declare const DeleteFilesInputSchema: z.ZodObject<{
153
- keys: z.ZodArray<z.ZodString>;
154
- }, z.core.$strip>;
155
- /** 获取文件信息入参 */
156
- declare const FileInfoInputSchema: z.ZodObject<{
157
- key: z.ZodString;
158
- }, z.core.$strip>;
159
- type PresignGetInput = z.infer<typeof PresignGetInputSchema>;
160
- type PresignPutInput = z.infer<typeof PresignPutInputSchema>;
161
- type PresignUrlOutput = z.infer<typeof PresignUrlOutputSchema>;
162
- type ListFilesOutput = z.infer<typeof ListFilesOutputSchema>;
163
- type DeleteFileInput = z.infer<typeof DeleteFileInputSchema>;
164
- type DeleteFilesInput = z.infer<typeof DeleteFilesInputSchema>;
165
- type FileInfoInput = z.infer<typeof FileInfoInputSchema>;
166
-
167
- export { type DeleteFileInput, DeleteFileInputSchema, type DeleteFilesInput, DeleteFilesInputSchema, type FileInfoInput, FileInfoInputSchema, FileMetadataSchema, type ListFilesOutput, ListFilesOutputSchema, type PresignGetInput, PresignGetInputSchema, type PresignPutInput, PresignPutInputSchema, type PresignUrlOutput, PresignUrlOutputSchema, storageEndpoints };
package/dist/api/index.js DELETED
@@ -1,105 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- // src/api/storage-api-contract.ts
4
- var FileMetadataSchema = z.object({
5
- key: z.string(),
6
- size: z.number(),
7
- contentType: z.string(),
8
- lastModified: z.coerce.date(),
9
- etag: z.string().optional(),
10
- metadata: z.record(z.string(), z.string()).optional()
11
- });
12
- var PresignGetInputSchema = z.object({
13
- key: z.string().min(1),
14
- expiresIn: z.number().int().min(1).optional()
15
- });
16
- var PresignPutInputSchema = z.object({
17
- key: z.string().min(1),
18
- contentType: z.string().optional(),
19
- contentLength: z.number().int().min(1).optional(),
20
- expiresIn: z.number().int().min(1).optional()
21
- });
22
- var PresignUrlOutputSchema = z.object({
23
- url: z.string(),
24
- key: z.string(),
25
- expiresAt: z.coerce.date().optional()
26
- });
27
- var ListFilesOutputSchema = z.object({
28
- files: z.array(FileMetadataSchema),
29
- commonPrefixes: z.array(z.string()),
30
- nextContinuationToken: z.string().optional(),
31
- isTruncated: z.boolean()
32
- });
33
- var DeleteFileInputSchema = z.object({
34
- key: z.string().min(1)
35
- });
36
- var DeleteFilesInputSchema = z.object({
37
- keys: z.array(z.string().min(1)).min(1)
38
- });
39
- var FileInfoInputSchema = z.object({
40
- key: z.string().min(1)
41
- });
42
-
43
- // src/api/storage-api-contract.ts
44
- function defineEndpoint(def) {
45
- return def;
46
- }
47
- var storageEndpoints = {
48
- /** 获取下载签名 URL */
49
- presignDownload: defineEndpoint({
50
- method: "POST",
51
- path: "/storage/presign/download",
52
- input: PresignGetInputSchema,
53
- output: PresignUrlOutputSchema,
54
- meta: { summary: "Get presigned download URL", tags: ["storage"] }
55
- }),
56
- /** 获取上传签名 URL */
57
- presignUpload: defineEndpoint({
58
- method: "POST",
59
- path: "/storage/presign/upload",
60
- input: PresignPutInputSchema,
61
- output: PresignUrlOutputSchema,
62
- meta: { summary: "Get presigned upload URL", tags: ["storage"] }
63
- }),
64
- /** 获取文件元数据 */
65
- fileInfo: defineEndpoint({
66
- method: "POST",
67
- path: "/storage/file/info",
68
- input: FileInfoInputSchema,
69
- output: FileMetadataSchema,
70
- meta: { summary: "Get file metadata", tags: ["storage"] }
71
- }),
72
- /** 列出文件 */
73
- listFiles: defineEndpoint({
74
- method: "GET",
75
- path: "/storage/files",
76
- input: z.object({
77
- prefix: z.string().optional(),
78
- maxKeys: z.coerce.number().int().min(1).max(1e3).optional(),
79
- continuationToken: z.string().optional(),
80
- delimiter: z.string().optional()
81
- }),
82
- output: ListFilesOutputSchema,
83
- meta: { summary: "List files by prefix", tags: ["storage"] }
84
- }),
85
- /** 删除单个文件 */
86
- deleteFile: defineEndpoint({
87
- method: "POST",
88
- path: "/storage/file/delete",
89
- input: DeleteFileInputSchema,
90
- output: z.void(),
91
- meta: { summary: "Delete a file", tags: ["storage"] }
92
- }),
93
- /** 批量删除文件 */
94
- deleteFiles: defineEndpoint({
95
- method: "POST",
96
- path: "/storage/files/delete",
97
- input: DeleteFilesInputSchema,
98
- output: z.void(),
99
- meta: { summary: "Delete multiple files", tags: ["storage"] }
100
- })
101
- };
102
-
103
- export { DeleteFileInputSchema, DeleteFilesInputSchema, FileInfoInputSchema, FileMetadataSchema, ListFilesOutputSchema, PresignGetInputSchema, PresignPutInputSchema, PresignUrlOutputSchema, storageEndpoints };
104
- //# sourceMappingURL=index.js.map
105
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/api/storage-api-schemas.ts","../../src/api/storage-api-contract.ts"],"names":["z"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @h-ai/storage/api — 存储 API 契约 Schema\n *\n * 入参/出参 Schema,客户端和服务端共享的唯一真相源。\n * @module storage-api-schemas\n */\n\nimport { z } from 'zod'\n\n// ─── 文件元数据 ───\n\n/** 文件元数据 Schema */\nexport const FileMetadataSchema = z.object({\n key: z.string(),\n size: z.number(),\n contentType: z.string(),\n lastModified: z.coerce.date(),\n etag: z.string().optional(),\n metadata: z.record(z.string(), z.string()).optional(),\n})\n\n// ─── 签名 URL ───\n\n/** 获取下载签名 URL 入参 */\nexport const PresignGetInputSchema = z.object({\n key: z.string().min(1),\n expiresIn: z.number().int().min(1).optional(),\n})\n\n/** 获取上传签名 URL 入参 */\nexport const PresignPutInputSchema = z.object({\n key: z.string().min(1),\n contentType: z.string().optional(),\n contentLength: z.number().int().min(1).optional(),\n expiresIn: z.number().int().min(1).optional(),\n})\n\n/** 签名 URL 出参 */\nexport const PresignUrlOutputSchema = z.object({\n url: z.string(),\n key: z.string(),\n expiresAt: z.coerce.date().optional(),\n})\n\n// ─── 列表 ───\n\n/** 文件列表出参 */\nexport const ListFilesOutputSchema = z.object({\n files: z.array(FileMetadataSchema),\n commonPrefixes: z.array(z.string()),\n nextContinuationToken: z.string().optional(),\n isTruncated: z.boolean(),\n})\n\n// ─── 删除 ───\n\n/** 删除文件入参 */\nexport const DeleteFileInputSchema = z.object({\n key: z.string().min(1),\n})\n\n/** 批量删除文件入参 */\nexport const DeleteFilesInputSchema = z.object({\n keys: z.array(z.string().min(1)).min(1),\n})\n\n// ─── 文件信息 ───\n\n/** 获取文件信息入参 */\nexport const FileInfoInputSchema = z.object({\n key: z.string().min(1),\n})\n\n// ─── 推导类型 ───\n\nexport type PresignGetInput = z.infer<typeof PresignGetInputSchema>\nexport type PresignPutInput = z.infer<typeof PresignPutInputSchema>\nexport type PresignUrlOutput = z.infer<typeof PresignUrlOutputSchema>\nexport type ListFilesOutput = z.infer<typeof ListFilesOutputSchema>\nexport type DeleteFileInput = z.infer<typeof DeleteFileInputSchema>\nexport type DeleteFilesInput = z.infer<typeof DeleteFilesInputSchema>\nexport type FileInfoInput = z.infer<typeof FileInfoInputSchema>\n","/**\n * @h-ai/storage/api — 存储 API 端点契约定义\n *\n * 所有 storage 模块的 API 端点(path + method + schema),\n * 客户端和服务端都从此处引用,编译时保证一致性。\n * @module storage-api-contract\n */\n\nimport { z } from 'zod'\nimport {\n DeleteFileInputSchema,\n DeleteFilesInputSchema,\n FileInfoInputSchema,\n FileMetadataSchema,\n ListFilesOutputSchema,\n PresignGetInputSchema,\n PresignPutInputSchema,\n PresignUrlOutputSchema,\n} from './storage-api-schemas.js'\n\n// ─── 端点定义辅助(内联,避免对 @h-ai/api-client 的循环依赖) ───\n\ninterface EndpointDef<TInput = unknown, TOutput = unknown> {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'\n path: string\n input: z.ZodType<TInput>\n output: z.ZodType<TOutput>\n requireAuth?: boolean\n meta?: { summary?: string, tags?: string[] }\n}\n\nfunction defineEndpoint<TInput, TOutput>(def: EndpointDef<TInput, TOutput>): EndpointDef<TInput, TOutput> {\n return def\n}\n\n// ─── storage API 端点 ───\n\n/**\n * storage 所有 API 端点\n *\n * @example\n * ```ts\n * // 客户端\n * import { storageEndpoints } from '@h-ai/storage/api'\n * const { url } = await api.call(storageEndpoints.presignUpload, { key: 'avatar.png' })\n *\n * // 服务端\n * export const POST = kit.fromContract(storageEndpoints.presignUpload, async (input) => {\n * const result = await storage.presign.putUrl(input.key, input)\n * return result.success ? result.data : kit.response.internalError(result.error.message)\n * })\n * ```\n */\nexport const storageEndpoints = {\n /** 获取下载签名 URL */\n presignDownload: defineEndpoint({\n method: 'POST',\n path: '/storage/presign/download',\n input: PresignGetInputSchema,\n output: PresignUrlOutputSchema,\n meta: { summary: 'Get presigned download URL', tags: ['storage'] },\n }),\n\n /** 获取上传签名 URL */\n presignUpload: defineEndpoint({\n method: 'POST',\n path: '/storage/presign/upload',\n input: PresignPutInputSchema,\n output: PresignUrlOutputSchema,\n meta: { summary: 'Get presigned upload URL', tags: ['storage'] },\n }),\n\n /** 获取文件元数据 */\n fileInfo: defineEndpoint({\n method: 'POST',\n path: '/storage/file/info',\n input: FileInfoInputSchema,\n output: FileMetadataSchema,\n meta: { summary: 'Get file metadata', tags: ['storage'] },\n }),\n\n /** 列出文件 */\n listFiles: defineEndpoint({\n method: 'GET',\n path: '/storage/files',\n input: z.object({\n prefix: z.string().optional(),\n maxKeys: z.coerce.number().int().min(1).max(1000).optional(),\n continuationToken: z.string().optional(),\n delimiter: z.string().optional(),\n }),\n output: ListFilesOutputSchema,\n meta: { summary: 'List files by prefix', tags: ['storage'] },\n }),\n\n /** 删除单个文件 */\n deleteFile: defineEndpoint({\n method: 'POST',\n path: '/storage/file/delete',\n input: DeleteFileInputSchema,\n output: z.void(),\n meta: { summary: 'Delete a file', tags: ['storage'] },\n }),\n\n /** 批量删除文件 */\n deleteFiles: defineEndpoint({\n method: 'POST',\n path: '/storage/files/delete',\n input: DeleteFilesInputSchema,\n output: z.void(),\n meta: { summary: 'Delete multiple files', tags: ['storage'] },\n }),\n} as const\n"]}