@unisource/sdk 0.1.2 → 0.2.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.
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import { nonEmptyString, positiveInt, uploadDestinationSchema } from './primitives';
3
+
4
+ // ─── User-facing file record ──────────────────────────────────────────────────
5
+
6
+ /**
7
+ * A confirmed file record owned by a user.
8
+ * Internal fields (storage_key, bucket) are intentionally excluded from the public API.
9
+ */
10
+ export const fileRecordSchema = z.object({
11
+ id: nonEmptyString,
12
+ service_id: nonEmptyString,
13
+ user_id: nonEmptyString,
14
+ folder_id: nonEmptyString.nullable(),
15
+ upload_id: nonEmptyString.nullable(),
16
+ filename: nonEmptyString,
17
+ size: positiveInt,
18
+ mime_type: nonEmptyString,
19
+ storage_destination: uploadDestinationSchema,
20
+ is_trashed: z.boolean(),
21
+ trashed_at: positiveInt.nullable(),
22
+ created_at: positiveInt,
23
+ updated_at: positiveInt,
24
+ });
25
+ export type FileRecord = z.infer<typeof fileRecordSchema>;
26
+
27
+ // ─── List ─────────────────────────────────────────────────────────────────────
28
+
29
+ export const FILES_DEFAULT_LIMIT = 25;
30
+ export const FILES_MAX_LIMIT = 100;
31
+
32
+ export const fileRecordsListQuerySchema = z.object({
33
+ folder_id: nonEmptyString.nullable().optional(),
34
+ is_trashed: z.boolean().optional(),
35
+ cursor: nonEmptyString.optional(),
36
+ limit: z.number().int().min(1).max(FILES_MAX_LIMIT).optional(),
37
+ });
38
+ export type FileRecordsListQuery = z.infer<typeof fileRecordsListQuerySchema>;
39
+
40
+ export const fileRecordsListResponseSchema = z.object({
41
+ items: z.array(fileRecordSchema),
42
+ next_cursor: z.string().nullable(),
43
+ limit: positiveInt,
44
+ });
45
+ export type FileRecordsListResponse = z.infer<typeof fileRecordsListResponseSchema>;
46
+
47
+ // ─── Single file detail ───────────────────────────────────────────────────────
48
+
49
+ export const fileRecordDetailResponseSchema = z.object({
50
+ file: fileRecordSchema,
51
+ });
52
+ export type FileRecordDetailResponse = z.infer<typeof fileRecordDetailResponseSchema>;
53
+
54
+ // ─── Move ────────────────────────────────────────────────────────────────────
55
+
56
+ export const fileMoveRequestSchema = z.object({
57
+ /** null = move to root, undefined = keep unchanged */
58
+ folder_id: nonEmptyString.nullable().optional(),
59
+ });
60
+ export type FileMoveRequest = z.infer<typeof fileMoveRequestSchema>;
61
+
62
+ // ─── Download URL ─────────────────────────────────────────────────────────────
63
+
64
+ export const fileDownloadUrlResponseSchema = z.object({
65
+ upload_id: nonEmptyString,
66
+ destination: uploadDestinationSchema,
67
+ download_url: z.string().url(),
68
+ expires_at: positiveInt,
69
+ });
70
+ export type FileDownloadUrlResponse = z.infer<typeof fileDownloadUrlResponseSchema>;
71
+
72
+ // ─── Delete ───────────────────────────────────────────────────────────────────
73
+
74
+ export const fileDeleteResponseSchema = z.object({
75
+ success: z.literal(true),
76
+ id: nonEmptyString,
77
+ permanent: z.boolean(),
78
+ });
79
+ export type FileDeleteResponse = z.infer<typeof fileDeleteResponseSchema>;
80
+
81
+ // ─── Restore ──────────────────────────────────────────────────────────────────
82
+
83
+ export const fileRestoreResponseSchema = z.object({
84
+ success: z.literal(true),
85
+ id: nonEmptyString,
86
+ });
87
+ export type FileRestoreResponse = z.infer<typeof fileRestoreResponseSchema>;
package/src/folders.ts ADDED
@@ -0,0 +1,84 @@
1
+ import { z } from 'zod';
2
+ import { nonEmptyString, positiveInt } from './primitives';
3
+
4
+ // ─── Folder record ────────────────────────────────────────────────────────────
5
+
6
+ export const folderSchema = z.object({
7
+ id: nonEmptyString,
8
+ service_id: nonEmptyString,
9
+ user_id: nonEmptyString,
10
+ parent_id: nonEmptyString.nullable(),
11
+ name: nonEmptyString,
12
+ color_tag: z.string().nullable(),
13
+ is_trashed: z.boolean(),
14
+ trashed_at: positiveInt.nullable(),
15
+ created_at: positiveInt,
16
+ updated_at: positiveInt,
17
+ });
18
+ export type Folder = z.infer<typeof folderSchema>;
19
+
20
+ // ─── List ─────────────────────────────────────────────────────────────────────
21
+
22
+ export const folderListQuerySchema = z.object({
23
+ parent_id: nonEmptyString.nullable().optional(),
24
+ is_trashed: z.boolean().optional(),
25
+ cursor: nonEmptyString.optional(),
26
+ limit: z.number().int().min(1).max(100).optional(),
27
+ });
28
+ export type FolderListQuery = z.infer<typeof folderListQuerySchema>;
29
+
30
+ export const folderListResponseSchema = z.object({
31
+ items: z.array(folderSchema),
32
+ next_cursor: z.string().nullable(),
33
+ limit: positiveInt,
34
+ });
35
+ export type FolderListResponse = z.infer<typeof folderListResponseSchema>;
36
+
37
+ // ─── Create ───────────────────────────────────────────────────────────────────
38
+
39
+ export const folderCreateRequestSchema = z.object({
40
+ name: nonEmptyString,
41
+ parent_id: nonEmptyString.optional(),
42
+ color_tag: z.string().optional(),
43
+ });
44
+ export type FolderCreateRequest = z.infer<typeof folderCreateRequestSchema>;
45
+
46
+ export const folderCreateResponseSchema = z.object({
47
+ folder: folderSchema,
48
+ });
49
+ export type FolderCreateResponse = z.infer<typeof folderCreateResponseSchema>;
50
+
51
+ // ─── Update ───────────────────────────────────────────────────────────────────
52
+
53
+ export const folderUpdateRequestSchema = z
54
+ .object({
55
+ name: nonEmptyString.optional(),
56
+ color_tag: z.string().nullable().optional(),
57
+ })
58
+ .refine((v) => v.name !== undefined || v.color_tag !== undefined, {
59
+ message: 'At least one of name or color_tag must be provided',
60
+ });
61
+ export type FolderUpdateRequest = z.infer<typeof folderUpdateRequestSchema>;
62
+
63
+ export const folderUpdateResponseSchema = z.object({
64
+ folder: folderSchema,
65
+ });
66
+ export type FolderUpdateResponse = z.infer<typeof folderUpdateResponseSchema>;
67
+
68
+ // ─── Delete ───────────────────────────────────────────────────────────────────
69
+
70
+ export const folderDeleteResponseSchema = z.object({
71
+ success: z.literal(true),
72
+ id: nonEmptyString,
73
+ permanent: z.boolean(),
74
+ folders_deleted: z.number().int().nonnegative().optional(),
75
+ });
76
+ export type FolderDeleteResponse = z.infer<typeof folderDeleteResponseSchema>;
77
+
78
+ // ─── Restore ──────────────────────────────────────────────────────────────────
79
+
80
+ export const folderRestoreResponseSchema = z.object({
81
+ success: z.literal(true),
82
+ id: nonEmptyString,
83
+ });
84
+ export type FolderRestoreResponse = z.infer<typeof folderRestoreResponseSchema>;
package/src/index.ts CHANGED
@@ -1,125 +1,106 @@
1
- import { z } from 'zod';
2
-
3
- export const FILES_DEFAULT_LIMIT = 25;
4
- export const FILES_MAX_LIMIT = 100;
5
-
6
- const nonEmptyStringSchema = z.string().trim().min(1);
7
- const positiveIntegerSchema = z.number().int().positive();
8
-
9
- export const uploadDestinationSchema = z.enum(['r2', 'appwrite']);
10
- export type UploadDestination = z.infer<typeof uploadDestinationSchema>;
11
-
12
- export const uploadStatusSchema = z.enum(['pending', 'completed', 'failed']);
13
- export type UploadStatus = z.infer<typeof uploadStatusSchema>;
14
-
15
- export const apiErrorSchema = z.object({
16
- error: nonEmptyStringSchema,
17
- message: nonEmptyStringSchema,
18
- });
19
- export type ApiError = z.infer<typeof apiErrorSchema>;
20
-
21
- export const uploadR2InitRequestSchema = z.object({
22
- filename: nonEmptyStringSchema,
23
- size: positiveIntegerSchema,
24
- mime_type: nonEmptyStringSchema,
25
- bucket: nonEmptyStringSchema.optional(),
26
- });
27
- export type UploadR2InitRequest = z.infer<typeof uploadR2InitRequestSchema>;
28
-
29
- export const uploadR2InitResponseSchema = z.object({
30
- upload_id: nonEmptyStringSchema,
31
- destination: z.literal('r2'),
32
- presigned_url: z.string().url(),
33
- storage_key: nonEmptyStringSchema,
34
- bucket: nonEmptyStringSchema,
35
- expires_at: positiveIntegerSchema,
36
- });
37
- export type UploadR2InitResponse = z.infer<typeof uploadR2InitResponseSchema>;
38
-
39
- export const uploadAppwriteInitRequestSchema = z.object({
40
- filename: nonEmptyStringSchema,
41
- size: positiveIntegerSchema,
42
- mime_type: nonEmptyStringSchema,
43
- });
44
- export type UploadAppwriteInitRequest = z.infer<typeof uploadAppwriteInitRequestSchema>;
45
-
46
- export const uploadAppwriteInitResponseSchema = z.object({
47
- upload_id: nonEmptyStringSchema,
48
- destination: z.literal('appwrite'),
49
- appwrite_endpoint: z.string().url(),
50
- appwrite_project_id: nonEmptyStringSchema,
51
- appwrite_bucket_id: nonEmptyStringSchema,
52
- file_id: nonEmptyStringSchema,
53
- expires_at: positiveIntegerSchema,
54
- });
55
- export type UploadAppwriteInitResponse = z.infer<typeof uploadAppwriteInitResponseSchema>;
56
-
57
- export const uploadLifecycleRequestSchema = z.object({
58
- upload_id: nonEmptyStringSchema,
59
- });
60
- export type UploadLifecycleRequest = z.infer<typeof uploadLifecycleRequestSchema>;
61
-
62
- export const uploadCompleteResponseSchema = z.object({
63
- success: z.literal(true),
64
- upload_id: nonEmptyStringSchema,
65
- status: z.literal('completed'),
66
- });
67
- export type UploadCompleteResponse = z.infer<typeof uploadCompleteResponseSchema>;
68
-
69
- export const uploadFailResponseSchema = z.object({
70
- success: z.literal(true),
71
- upload_id: nonEmptyStringSchema,
72
- status: z.literal('failed'),
73
- });
74
- export type UploadFailResponse = z.infer<typeof uploadFailResponseSchema>;
75
-
76
- export const fileRecordSchema = z.object({
77
- id: nonEmptyStringSchema,
78
- filename: nonEmptyStringSchema,
79
- size: positiveIntegerSchema,
80
- mime_type: nonEmptyStringSchema,
81
- destination: uploadDestinationSchema,
82
- storage_key: nonEmptyStringSchema,
83
- bucket: nonEmptyStringSchema,
84
- status: uploadStatusSchema,
85
- expires_at: positiveIntegerSchema,
86
- created_at: positiveIntegerSchema,
87
- updated_at: positiveIntegerSchema,
88
- });
89
- export type FileRecord = z.infer<typeof fileRecordSchema>;
90
-
91
- export const filesListQuerySchema = z.object({
92
- limit: z.number().int().min(1).max(FILES_MAX_LIMIT).optional(),
93
- cursor: nonEmptyStringSchema.optional(),
94
- destination: uploadDestinationSchema.optional(),
95
- status: uploadStatusSchema.optional(),
96
- });
97
- export type FilesListQuery = z.infer<typeof filesListQuerySchema>;
98
-
99
- export const filesListResponseSchema = z.object({
100
- items: z.array(fileRecordSchema),
101
- next_cursor: z.string().nullable(),
102
- limit: z.number().int().min(1).max(FILES_MAX_LIMIT),
103
- });
104
- export type FilesListResponse = z.infer<typeof filesListResponseSchema>;
105
-
106
- export const fileDetailsResponseSchema = z.object({
107
- file: fileRecordSchema,
108
- });
109
- export type FileDetailsResponse = z.infer<typeof fileDetailsResponseSchema>;
110
-
111
- export const fileDownloadUrlResponseSchema = z.object({
112
- upload_id: nonEmptyStringSchema,
113
- destination: uploadDestinationSchema,
114
- download_url: z.string().url(),
115
- expires_at: positiveIntegerSchema,
116
- });
117
- export type FileDownloadUrlResponse = z.infer<typeof fileDownloadUrlResponseSchema>;
118
-
119
- export const fileDeleteResponseSchema = z.object({
120
- success: z.literal(true),
121
- upload_id: nonEmptyStringSchema,
122
- destination: uploadDestinationSchema,
123
- storage_not_found: z.boolean(),
124
- });
125
- export type FileDeleteResponse = z.infer<typeof fileDeleteResponseSchema>;
1
+ // ─── Primitives ───────────────────────────────────────────────────────────────
2
+ export {
3
+ nonEmptyString,
4
+ positiveInt,
5
+ unixTimestamp,
6
+ uploadDestinationSchema,
7
+ uploadStatusSchema,
8
+ apiErrorSchema,
9
+ } from './primitives';
10
+ export type { UploadDestination, UploadStatus, ApiError } from './primitives';
11
+
12
+ // ─── Upload ───────────────────────────────────────────────────────────────────
13
+ export {
14
+ FILES_DEFAULT_LIMIT,
15
+ FILES_MAX_LIMIT,
16
+ uploadR2InitRequestSchema,
17
+ uploadR2InitResponseSchema,
18
+ uploadAppwriteInitRequestSchema,
19
+ uploadAppwriteInitResponseSchema,
20
+ uploadLifecycleRequestSchema,
21
+ uploadCompleteResponseSchema,
22
+ uploadFailResponseSchema,
23
+ uploadRecordSchema,
24
+ uploadsListResponseSchema,
25
+ } from './uploads';
26
+ export type {
27
+ UploadR2InitRequest,
28
+ UploadR2InitResponse,
29
+ UploadAppwriteInitRequest,
30
+ UploadAppwriteInitResponse,
31
+ UploadLifecycleRequest,
32
+ UploadCompleteResponse,
33
+ UploadFailResponse,
34
+ UploadRecord,
35
+ UploadsListResponse,
36
+ } from './uploads';
37
+
38
+ // ─── File Records ─────────────────────────────────────────────────────────────
39
+ export {
40
+ fileRecordSchema,
41
+ fileRecordsListQuerySchema,
42
+ fileRecordsListResponseSchema,
43
+ fileRecordDetailResponseSchema,
44
+ fileMoveRequestSchema,
45
+ fileDownloadUrlResponseSchema,
46
+ fileDeleteResponseSchema,
47
+ fileRestoreResponseSchema,
48
+ } from './fileRecords';
49
+ export type {
50
+ FileRecord,
51
+ FileRecordsListQuery,
52
+ FileRecordsListResponse,
53
+ FileRecordDetailResponse,
54
+ FileMoveRequest,
55
+ FileDownloadUrlResponse,
56
+ FileDeleteResponse,
57
+ FileRestoreResponse,
58
+ } from './fileRecords';
59
+
60
+ // ─── Folders ─────────────────────────────────────────────────────────────────
61
+ export {
62
+ folderSchema,
63
+ folderListQuerySchema,
64
+ folderListResponseSchema,
65
+ folderCreateRequestSchema,
66
+ folderCreateResponseSchema,
67
+ folderUpdateRequestSchema,
68
+ folderUpdateResponseSchema,
69
+ folderDeleteResponseSchema,
70
+ folderRestoreResponseSchema,
71
+ } from './folders';
72
+ export type {
73
+ Folder,
74
+ FolderListQuery,
75
+ FolderListResponse,
76
+ FolderCreateRequest,
77
+ FolderCreateResponse,
78
+ FolderUpdateRequest,
79
+ FolderUpdateResponse,
80
+ FolderDeleteResponse,
81
+ FolderRestoreResponse,
82
+ } from './folders';
83
+
84
+ // ─── Services & Audit ─────────────────────────────────────────────────────────
85
+ export {
86
+ serviceSchema,
87
+ serviceDetailResponseSchema,
88
+ serviceUsageResponseSchema,
89
+ auditEventActionSchema,
90
+ auditEventSchema,
91
+ auditLogListQuerySchema,
92
+ auditLogListResponseSchema,
93
+ } from './services';
94
+ export type {
95
+ Service,
96
+ ServiceDetailResponse,
97
+ ServiceUsageResponse,
98
+ AuditEventAction,
99
+ AuditEvent,
100
+ AuditLogListQuery,
101
+ AuditLogListResponse,
102
+ } from './services';
103
+
104
+ // ─── HTTP Client ─────────────────────────────────────────────────────────────
105
+ export { UnisourceClient, UnisourceError, UnisourceNetworkError } from './client';
106
+ export type { UnisourceClientConfig } from './client';
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+
3
+ // ─── Primitives ─────────────────────────────────────────────────────────────
4
+
5
+ export const nonEmptyString = z.string().trim().min(1);
6
+ export const positiveInt = z.number().int().positive();
7
+ export const unixTimestamp = z.number().int().nonnegative();
8
+
9
+ // ─── Upload Destination ──────────────────────────────────────────────────────
10
+
11
+ export const uploadDestinationSchema = z.enum(['r2', 'appwrite']);
12
+ export type UploadDestination = z.infer<typeof uploadDestinationSchema>;
13
+
14
+ // ─── Upload Status ────────────────────────────────────────────────────────────
15
+
16
+ export const uploadStatusSchema = z.enum(['pending', 'completed', 'failed']);
17
+ export type UploadStatus = z.infer<typeof uploadStatusSchema>;
18
+
19
+ // ─── API Error ────────────────────────────────────────────────────────────────
20
+
21
+ export const apiErrorSchema = z.object({
22
+ error: nonEmptyString,
23
+ message: nonEmptyString,
24
+ });
25
+ export type ApiError = z.infer<typeof apiErrorSchema>;
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import { nonEmptyString, positiveInt, unixTimestamp } from './primitives';
3
+
4
+ // ─── Service record ───────────────────────────────────────────────────────────
5
+
6
+ /** Public service info returned to admins. Never exposes secrets. */
7
+ export const serviceSchema = z.object({
8
+ id: nonEmptyString,
9
+ name: nonEmptyString,
10
+ max_storage_bytes: positiveInt,
11
+ current_used_bytes: z.number().int().nonnegative(),
12
+ max_file_size_bytes: positiveInt,
13
+ created_at: unixTimestamp,
14
+ });
15
+ export type Service = z.infer<typeof serviceSchema>;
16
+
17
+ export const serviceDetailResponseSchema = z.object({
18
+ service: serviceSchema,
19
+ });
20
+ export type ServiceDetailResponse = z.infer<typeof serviceDetailResponseSchema>;
21
+
22
+ // ─── Service usage summary ────────────────────────────────────────────────────
23
+
24
+ export const serviceUsageResponseSchema = z.object({
25
+ service_id: nonEmptyString,
26
+ max_storage_bytes: positiveInt,
27
+ current_used_bytes: z.number().int().nonnegative(),
28
+ used_percent: z.number().min(0).max(100),
29
+ });
30
+ export type ServiceUsageResponse = z.infer<typeof serviceUsageResponseSchema>;
31
+
32
+ // ─── Audit log event ──────────────────────────────────────────────────────────
33
+
34
+ export const auditEventActionSchema = z.enum([
35
+ 'upload_completed',
36
+ 'file_deleted',
37
+ 'folder_deleted',
38
+ 'quota_exceeded',
39
+ ]);
40
+ export type AuditEventAction = z.infer<typeof auditEventActionSchema>;
41
+
42
+ export const auditEventSchema = z.object({
43
+ id: nonEmptyString,
44
+ service_id: nonEmptyString,
45
+ user_id: nonEmptyString,
46
+ action: auditEventActionSchema,
47
+ resource_type: z.enum(['file', 'folder', 'service']),
48
+ resource_id: nonEmptyString,
49
+ metadata: z.record(z.string(), z.unknown()).nullable(),
50
+ ip_address: z.string().nullable(),
51
+ created_at: unixTimestamp,
52
+ });
53
+ export type AuditEvent = z.infer<typeof auditEventSchema>;
54
+
55
+ export const auditLogListQuerySchema = z.object({
56
+ user_id: nonEmptyString.optional(),
57
+ action: auditEventActionSchema.optional(),
58
+ resource_type: z.enum(['file', 'folder', 'service']).optional(),
59
+ cursor: nonEmptyString.optional(),
60
+ limit: z.number().int().min(1).max(200).optional(),
61
+ });
62
+ export type AuditLogListQuery = z.infer<typeof auditLogListQuerySchema>;
63
+
64
+ export const auditLogListResponseSchema = z.object({
65
+ items: z.array(auditEventSchema),
66
+ next_cursor: z.string().nullable(),
67
+ limit: positiveInt,
68
+ });
69
+ export type AuditLogListResponse = z.infer<typeof auditLogListResponseSchema>;
@@ -0,0 +1,4 @@
1
+ import { z } from 'zod';
2
+
3
+ export const uploadDestinationSchema = z.enum(['r2', 'appwrite']);
4
+ export type UploadDestination = z.infer<typeof uploadDestinationSchema>;
package/src/uploads.ts ADDED
@@ -0,0 +1,90 @@
1
+ import { z } from 'zod';
2
+ import { nonEmptyString, positiveInt, uploadDestinationSchema } from './primitives';
3
+
4
+ // ─── Init: R2 ────────────────────────────────────────────────────────────────
5
+
6
+ export const uploadR2InitRequestSchema = z.object({
7
+ filename: nonEmptyString,
8
+ size: positiveInt,
9
+ mime_type: nonEmptyString,
10
+ });
11
+ export type UploadR2InitRequest = z.infer<typeof uploadR2InitRequestSchema>;
12
+
13
+ export const uploadR2InitResponseSchema = z.object({
14
+ upload_id: nonEmptyString,
15
+ destination: z.literal('r2'),
16
+ presigned_url: z.string().url(),
17
+ storage_key: nonEmptyString,
18
+ bucket: nonEmptyString,
19
+ expires_at: positiveInt,
20
+ });
21
+ export type UploadR2InitResponse = z.infer<typeof uploadR2InitResponseSchema>;
22
+
23
+ // ─── Init: Appwrite ───────────────────────────────────────────────────────────
24
+
25
+ export const uploadAppwriteInitRequestSchema = z.object({
26
+ filename: nonEmptyString,
27
+ size: positiveInt,
28
+ mime_type: nonEmptyString,
29
+ });
30
+ export type UploadAppwriteInitRequest = z.infer<typeof uploadAppwriteInitRequestSchema>;
31
+
32
+ export const uploadAppwriteInitResponseSchema = z.object({
33
+ upload_id: nonEmptyString,
34
+ destination: z.literal('appwrite'),
35
+ appwrite_endpoint: z.string().url(),
36
+ appwrite_project_id: nonEmptyString,
37
+ appwrite_bucket_id: nonEmptyString,
38
+ file_id: nonEmptyString,
39
+ expires_at: positiveInt,
40
+ });
41
+ export type UploadAppwriteInitResponse = z.infer<typeof uploadAppwriteInitResponseSchema>;
42
+
43
+ // ─── Lifecycle ────────────────────────────────────────────────────────────────
44
+
45
+ export const uploadLifecycleRequestSchema = z.object({
46
+ upload_id: nonEmptyString,
47
+ });
48
+ export type UploadLifecycleRequest = z.infer<typeof uploadLifecycleRequestSchema>;
49
+
50
+ export const uploadCompleteResponseSchema = z.object({
51
+ success: z.literal(true),
52
+ upload_id: nonEmptyString,
53
+ status: z.literal('completed'),
54
+ });
55
+ export type UploadCompleteResponse = z.infer<typeof uploadCompleteResponseSchema>;
56
+
57
+ export const uploadFailResponseSchema = z.object({
58
+ success: z.literal(true),
59
+ upload_id: nonEmptyString,
60
+ status: z.literal('failed'),
61
+ });
62
+ export type UploadFailResponse = z.infer<typeof uploadFailResponseSchema>;
63
+
64
+ // ─── Admin: list uploads ──────────────────────────────────────────────────────
65
+
66
+ export const FILES_DEFAULT_LIMIT = 25;
67
+ export const FILES_MAX_LIMIT = 100;
68
+
69
+ /** Raw upload record — returned by admin `/files` endpoints. */
70
+ export const uploadRecordSchema = z.object({
71
+ id: nonEmptyString,
72
+ service_id: nonEmptyString,
73
+ user_id: nonEmptyString.nullable(),
74
+ filename: nonEmptyString,
75
+ size: positiveInt,
76
+ mime_type: nonEmptyString,
77
+ destination: uploadDestinationSchema,
78
+ status: z.enum(['pending', 'completed', 'failed']),
79
+ expires_at: positiveInt,
80
+ created_at: positiveInt,
81
+ updated_at: positiveInt,
82
+ });
83
+ export type UploadRecord = z.infer<typeof uploadRecordSchema>;
84
+
85
+ export const uploadsListResponseSchema = z.object({
86
+ items: z.array(uploadRecordSchema),
87
+ next_cursor: z.string().nullable(),
88
+ limit: positiveInt,
89
+ });
90
+ export type UploadsListResponse = z.infer<typeof uploadsListResponseSchema>;