@unisource/sdk 0.1.2 → 0.3.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/src/folders.ts ADDED
@@ -0,0 +1,97 @@
1
+ import { z } from 'zod';
2
+ import { nonEmptyString, positiveInt, FILES_MAX_LIMIT } 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
23
+ .object({
24
+ parent_id: nonEmptyString.nullable().optional(),
25
+ trashed: z.boolean().optional(),
26
+ is_trashed: z.boolean().optional(),
27
+ cursor: nonEmptyString.optional(),
28
+ limit: z.number().int().min(1).max(FILES_MAX_LIMIT).optional(),
29
+ })
30
+ .refine(
31
+ (v) => !(v.trashed !== undefined && v.is_trashed !== undefined),
32
+ { message: 'Use either trashed or is_trashed, not both' }
33
+ );
34
+ export type FolderListQuery = z.infer<typeof folderListQuerySchema>;
35
+
36
+ export const folderListResponseSchema = z.object({
37
+ items: z.array(folderSchema),
38
+ next_cursor: z.string().nullable(),
39
+ limit: positiveInt,
40
+ });
41
+ export type FolderListResponse = z.infer<typeof folderListResponseSchema>;
42
+
43
+ // ─── Create ───────────────────────────────────────────────────────────────────
44
+
45
+ export const folderCreateRequestSchema = z.object({
46
+ name: nonEmptyString,
47
+ parent_id: nonEmptyString.optional(),
48
+ color_tag: z.string().optional(),
49
+ });
50
+ export type FolderCreateRequest = z.infer<typeof folderCreateRequestSchema>;
51
+
52
+ export const folderCreateResponseSchema = z.object({
53
+ folder: folderSchema,
54
+ });
55
+ export type FolderCreateResponse = z.infer<typeof folderCreateResponseSchema>;
56
+
57
+ // ─── Update ───────────────────────────────────────────────────────────────────
58
+
59
+ export const folderUpdateRequestSchema = z
60
+ .object({
61
+ name: nonEmptyString.optional(),
62
+ color_tag: z.string().nullable().optional(),
63
+ })
64
+ .refine((v) => v.name !== undefined || v.color_tag !== undefined, {
65
+ message: 'At least one of name or color_tag must be provided',
66
+ });
67
+ export type FolderUpdateRequest = z.infer<typeof folderUpdateRequestSchema>;
68
+
69
+ export const folderUpdateResponseSchema = z.object({
70
+ folder: folderSchema,
71
+ });
72
+ export type FolderUpdateResponse = z.infer<typeof folderUpdateResponseSchema>;
73
+
74
+ // ─── Delete ───────────────────────────────────────────────────────────────────
75
+
76
+ export const folderDeleteResponseSchema = z.object({
77
+ success: z.literal(true),
78
+ id: nonEmptyString,
79
+ permanent: z.boolean(),
80
+ folders_deleted: z.number().int().nonnegative().optional(),
81
+ });
82
+ export type FolderDeleteResponse = z.infer<typeof folderDeleteResponseSchema>;
83
+
84
+ // ─── Single folder detail ─────────────────────────────────────────────────────
85
+
86
+ export const folderDetailResponseSchema = z.object({
87
+ folder: folderSchema,
88
+ });
89
+ export type FolderDetailResponse = z.infer<typeof folderDetailResponseSchema>;
90
+
91
+ // ─── Restore ──────────────────────────────────────────────────────────────────
92
+
93
+ export const folderRestoreResponseSchema = z.object({
94
+ success: z.literal(true),
95
+ id: nonEmptyString,
96
+ });
97
+ export type FolderRestoreResponse = z.infer<typeof folderRestoreResponseSchema>;
package/src/index.ts CHANGED
@@ -1,125 +1,198 @@
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
+ FILES_DEFAULT_LIMIT,
10
+ FILES_MAX_LIMIT,
11
+ } from './primitives';
12
+ export type { UploadDestination, UploadStatus, ApiError } from './primitives';
13
+
14
+
15
+ // ─── Upload ───────────────────────────────────────────────────────────────────
16
+ export {
17
+ uploadR2InitRequestSchema,
18
+ uploadR2InitResponseSchema,
19
+ uploadAppwriteInitRequestSchema,
20
+ uploadAppwriteInitResponseSchema,
21
+ uploadLifecycleRequestSchema,
22
+ uploadCompleteResponseSchema,
23
+ uploadFailResponseSchema,
24
+ uploadRecordSchema,
25
+ uploadsListResponseSchema,
26
+ uploadRecordDetailResponseSchema,
27
+ } from './uploads';
28
+ export type {
29
+ UploadR2InitRequest,
30
+ UploadR2InitResponse,
31
+ UploadAppwriteInitRequest,
32
+ UploadAppwriteInitResponse,
33
+ UploadLifecycleRequest,
34
+ UploadCompleteResponse,
35
+ UploadFailResponse,
36
+ UploadRecord,
37
+ UploadsListResponse,
38
+ UploadRecordDetailResponse,
39
+ } from './uploads';
40
+
41
+ // ─── File Records ─────────────────────────────────────────────────────────────
42
+ export {
43
+ fileRecordSchema,
44
+ fileRecordsListQuerySchema,
45
+ fileRecordsListResponseSchema,
46
+ fileRecordDetailResponseSchema,
47
+ fileMoveRequestSchema,
48
+ fileDownloadUrlResponseSchema,
49
+ fileDeleteResponseSchema,
50
+ fileRestoreResponseSchema,
51
+ fileUpdateRequestSchema,
52
+ fileUpdateResponseSchema,
53
+ } from './fileRecords';
54
+ export type {
55
+ FileRecord,
56
+ FileRecordsListQuery,
57
+ FileRecordsListResponse,
58
+ FileRecordDetailResponse,
59
+ FileMoveRequest,
60
+ FileDownloadUrlResponse,
61
+ FileDeleteResponse,
62
+ FileRestoreResponse,
63
+ FileUpdateRequest,
64
+ FileUpdateResponse,
65
+ } from './fileRecords';
66
+
67
+ // ─── Folders ─────────────────────────────────────────────────────────────────
68
+ export {
69
+ folderSchema,
70
+ folderListQuerySchema,
71
+ folderListResponseSchema,
72
+ folderDetailResponseSchema,
73
+ folderCreateRequestSchema,
74
+ folderCreateResponseSchema,
75
+ folderUpdateRequestSchema,
76
+ folderUpdateResponseSchema,
77
+ folderDeleteResponseSchema,
78
+ folderRestoreResponseSchema,
79
+ } from './folders';
80
+ export type {
81
+ Folder,
82
+ FolderListQuery,
83
+ FolderListResponse,
84
+ FolderDetailResponse,
85
+ FolderCreateRequest,
86
+ FolderCreateResponse,
87
+ FolderUpdateRequest,
88
+ FolderUpdateResponse,
89
+ FolderDeleteResponse,
90
+ FolderRestoreResponse,
91
+ } from './folders';
92
+
93
+ // ─── Services & Audit ─────────────────────────────────────────────────────────
94
+ export {
95
+ serviceSchema,
96
+ serviceDetailResponseSchema,
97
+ adminServiceUpdateRequestSchema,
98
+ adminServiceUpdateResponseSchema,
99
+ serviceUsageResponseSchema,
100
+ auditEventActionSchema,
101
+ auditEventSchema,
102
+ auditLogListQuerySchema,
103
+ auditLogListResponseSchema,
104
+ adminUserSchema,
105
+ adminUserListResponseSchema,
106
+ adminUserUpdateRequestSchema,
107
+ adminUserUpdateResponseSchema,
108
+ adminUserPasswordResetRequestSchema,
109
+ adminUserPasswordResetResponseSchema,
110
+ } from './services';
111
+ export type {
112
+ Service,
113
+ ServiceDetailResponse,
114
+ AdminServiceUpdateRequest,
115
+ AdminServiceUpdateResponse,
116
+ ServiceUsageResponse,
117
+ AuditEventAction,
118
+ AuditEvent,
119
+ AuditLogListQuery,
120
+ AuditLogListResponse,
121
+ AdminUser,
122
+ AdminUserListResponse,
123
+ AdminUserUpdateRequest,
124
+ AdminUserUpdateResponse,
125
+ AdminUserPasswordResetRequest,
126
+ AdminUserPasswordResetResponse,
127
+ } from './services';
128
+
129
+ // ─── Main Storage ─────────────────────────────────────────────────────────────
130
+ export * from './mainStorage';
131
+
132
+ // ─── Releases ───────────────────────────────────────────────────────────────
133
+ export {
134
+ releaseDTOSchema,
135
+ releaseUploadInitRequestSchema,
136
+ releaseUploadInitResponseSchema,
137
+ releaseUploadCompleteRequestSchema,
138
+ releaseUploadCompleteResponseSchema,
139
+ releaseUploadFailResponseSchema,
140
+ releasesListQuerySchema,
141
+ releasesListResponseSchema,
142
+ releaseUpdateRequestSchema,
143
+ releaseDeleteResponseSchema,
144
+ releaseSyncManifestSchema,
145
+ releaseSyncRequestSchema,
146
+ releaseSyncResultSchema,
147
+ releaseSyncResponseSchema,
148
+ } from './releases';
149
+ export type {
150
+ ReleaseDTO,
151
+ ReleaseUploadInitRequest,
152
+ ReleaseUploadInitResponse,
153
+ ReleaseUploadCompleteRequest,
154
+ ReleaseUploadCompleteResponse,
155
+ ReleaseUploadFailResponse,
156
+ ReleasesListQuery,
157
+ ReleasesListResponse,
158
+ ReleaseUpdateRequest,
159
+ ReleaseDeleteResponse,
160
+ ReleaseSyncManifest,
161
+ ReleaseSyncRequest,
162
+ ReleaseSyncResult,
163
+ ReleaseSyncResponse,
164
+ } from './releases';
165
+
166
+ // ─── Share Links ─────────────────────────────────────────────────────────────
167
+ export {
168
+ shareLinkSchema,
169
+ shareLinkCreateRequestSchema,
170
+ shareLinkUpdateRequestSchema,
171
+ shareLinkListResponseSchema,
172
+ shareLinkCreateResponseSchema,
173
+ shareLinkUpdateResponseSchema,
174
+ shareLinkDeleteResponseSchema,
175
+ publicFileAccessResponseSchema,
176
+ publicFileLockedResponseSchema,
177
+ } from './shareLinks';
178
+ export type {
179
+ ShareLink,
180
+ ShareLinkCreateRequest,
181
+ ShareLinkUpdateRequest,
182
+ ShareLinkListResponse,
183
+ ShareLinkCreateResponse,
184
+ ShareLinkUpdateResponse,
185
+ ShareLinkDeleteResponse,
186
+ PublicFileAccessResponse,
187
+ PublicFileLockedResponse,
188
+ } from './shareLinks';
189
+
190
+ // ─── HTTP Client ─────────────────────────────────────────────────────────────
191
+ export {
192
+ UnisourceClient,
193
+ UnisourceError,
194
+ UnisourceNetworkError,
195
+ getPublicFileInfo,
196
+ unlockPublicFile,
197
+ } from './client';
198
+ export type { UnisourceClientConfig } from './client';
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod';
2
+ import { nonEmptyString } from './primitives';
3
+ import { fileRecordSchema } from './fileRecords';
4
+
5
+ // ─── List ──────────────────────────────────────────────────────────────────────
6
+
7
+ export const mainStorageListQuerySchema = z.object({
8
+ limit: z.number().int().positive().optional(),
9
+ cursor: nonEmptyString.optional(),
10
+ });
11
+ export type MainStorageListQuery = z.infer<typeof mainStorageListQuerySchema>;
12
+
13
+ export const mainStorageListResponseSchema = z.object({
14
+ items: z.array(fileRecordSchema),
15
+ next_cursor: z.string().nullable(),
16
+ });
17
+ export type MainStorageListResponse = z.infer<typeof mainStorageListResponseSchema>;
18
+
19
+ // ─── Rename ────────────────────────────────────────────────────────────────────
20
+
21
+ export const mainStorageRenameRequestSchema = z.object({
22
+ filename: nonEmptyString,
23
+ });
24
+ export type MainStorageRenameRequest = z.infer<typeof mainStorageRenameRequestSchema>;
25
+
26
+ // ─── Delete ────────────────────────────────────────────────────────────────────
27
+
28
+ export const mainStorageDeleteResponseSchema = z.object({
29
+ success: z.boolean(),
30
+ file_id: nonEmptyString,
31
+ });
32
+ export type MainStorageDeleteResponse = z.infer<typeof mainStorageDeleteResponseSchema>;
33
+
34
+ // ─── Restore ───────────────────────────────────────────────────────────────────
35
+
36
+ export const mainStorageRestoreResponseSchema = z.object({
37
+ success: z.boolean(),
38
+ file_id: nonEmptyString,
39
+ });
40
+ export type MainStorageRestoreResponse = z.infer<typeof mainStorageRestoreResponseSchema>;
@@ -0,0 +1,30 @@
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
+ // ─── Pagination constants ─────────────────────────────────────────────────────
10
+
11
+ export const FILES_DEFAULT_LIMIT = 25;
12
+ export const FILES_MAX_LIMIT = 100;
13
+
14
+ // ─── Upload Destination ──────────────────────────────────────────────────────
15
+
16
+ export const uploadDestinationSchema = z.enum(['r2', 'appwrite']);
17
+ export type UploadDestination = z.infer<typeof uploadDestinationSchema>;
18
+
19
+ // ─── Upload Status ────────────────────────────────────────────────────────────
20
+
21
+ export const uploadStatusSchema = z.enum(['pending', 'completed', 'failed']);
22
+ export type UploadStatus = z.infer<typeof uploadStatusSchema>;
23
+
24
+ // ─── API Error ────────────────────────────────────────────────────────────────
25
+
26
+ export const apiErrorSchema = z.object({
27
+ error: nonEmptyString,
28
+ message: nonEmptyString,
29
+ });
30
+ export type ApiError = z.infer<typeof apiErrorSchema>;
@@ -0,0 +1,132 @@
1
+ import { z } from 'zod';
2
+ import { nonEmptyString, positiveInt, uploadStatusSchema } from './primitives';
3
+
4
+ const releaseIdSchema = z.string().trim().min(1).max(128);
5
+ const releaseNameSchema = z.string().trim().min(1).max(256);
6
+ const releaseFilenameSchema = z.string().trim().min(1).max(255);
7
+ const releaseTagsSchema = z.array(z.string().trim().min(1).max(64)).max(32);
8
+ const releaseNotesSchema = z.string().trim().max(10_000).nullable().optional();
9
+ const releaseR2KeySchema = z.string().trim().min(1).max(1024);
10
+
11
+ // ─── Release DTO ─────────────────────────────────────────────────────────────
12
+
13
+ export const releaseDTOSchema = z.object({
14
+ id: nonEmptyString,
15
+ service_id: nonEmptyString,
16
+ name: nonEmptyString,
17
+ size: z.number().int().nonnegative(),
18
+ r2_key: nonEmptyString,
19
+ tags: z.array(nonEmptyString),
20
+ notes: z.string().nullable(),
21
+ force_update: z.boolean(),
22
+ uploaded_by: nonEmptyString,
23
+ upload_status: uploadStatusSchema,
24
+ created_at: nonEmptyString,
25
+ });
26
+ export type ReleaseDTO = z.infer<typeof releaseDTOSchema>;
27
+
28
+ // ─── Upload Lifecycle ───────────────────────────────────────────────────────
29
+
30
+ export const releaseUploadInitRequestSchema = z.object({
31
+ name: releaseNameSchema,
32
+ filename: releaseFilenameSchema,
33
+ tags: releaseTagsSchema.optional().default([]),
34
+ notes: releaseNotesSchema,
35
+ force_update: z.boolean().optional().default(false),
36
+ });
37
+ export type ReleaseUploadInitRequest = z.input<typeof releaseUploadInitRequestSchema>;
38
+
39
+ export const releaseUploadInitResponseSchema = z.object({
40
+ release_id: nonEmptyString,
41
+ presigned_url: z.string().url(),
42
+ r2_key: nonEmptyString,
43
+ expires_at: positiveInt,
44
+ });
45
+ export type ReleaseUploadInitResponse = z.infer<typeof releaseUploadInitResponseSchema>;
46
+
47
+ export const releaseUploadCompleteRequestSchema = z.object({
48
+ release_id: releaseIdSchema,
49
+ size: z.number().int().nonnegative(),
50
+ });
51
+ export type ReleaseUploadCompleteRequest = z.infer<typeof releaseUploadCompleteRequestSchema>;
52
+
53
+ export const releaseUploadCompleteResponseSchema = z.object({
54
+ success: z.literal(true),
55
+ release_id: nonEmptyString,
56
+ status: z.literal('completed'),
57
+ });
58
+ export type ReleaseUploadCompleteResponse = z.infer<typeof releaseUploadCompleteResponseSchema>;
59
+
60
+ export const releaseUploadFailResponseSchema = z.object({
61
+ success: z.literal(true),
62
+ release_id: nonEmptyString,
63
+ status: z.literal('failed'),
64
+ });
65
+ export type ReleaseUploadFailResponse = z.infer<typeof releaseUploadFailResponseSchema>;
66
+
67
+ // ─── List ───────────────────────────────────────────────────────────────────
68
+
69
+ export const releasesListQuerySchema = z.object({
70
+ limit: z.number().int().positive().max(100).optional(),
71
+ cursor: nonEmptyString.optional(),
72
+ });
73
+ export type ReleasesListQuery = z.infer<typeof releasesListQuerySchema>;
74
+
75
+ export const releasesListResponseSchema = z.object({
76
+ items: z.array(releaseDTOSchema),
77
+ next_cursor: z.string().nullable(),
78
+ });
79
+ export type ReleasesListResponse = z.infer<typeof releasesListResponseSchema>;
80
+
81
+ // ─── Update ─────────────────────────────────────────────────────────────────
82
+
83
+ export const releaseUpdateRequestSchema = z
84
+ .object({
85
+ name: releaseNameSchema.optional(),
86
+ tags: releaseTagsSchema.optional(),
87
+ notes: releaseNotesSchema,
88
+ force_update: z.boolean().optional(),
89
+ })
90
+ .refine((body) => Object.values(body).some((value) => value !== undefined), {
91
+ message: 'At least one field must be provided',
92
+ });
93
+ export type ReleaseUpdateRequest = z.infer<typeof releaseUpdateRequestSchema>;
94
+
95
+ // ─── Delete ─────────────────────────────────────────────────────────────────
96
+
97
+ export const releaseDeleteResponseSchema = z.object({
98
+ success: z.literal(true),
99
+ release_id: nonEmptyString,
100
+ });
101
+ export type ReleaseDeleteResponse = z.infer<typeof releaseDeleteResponseSchema>;
102
+
103
+ // ─── Sync ───────────────────────────────────────────────────────────────────
104
+
105
+ export const releaseSyncManifestSchema = z.object({
106
+ id: releaseIdSchema.optional(),
107
+ name: releaseNameSchema,
108
+ r2_key: releaseR2KeySchema,
109
+ size: z.number().int().nonnegative(),
110
+ tags: releaseTagsSchema.optional().default([]),
111
+ notes: releaseNotesSchema,
112
+ force_update: z.boolean().optional().default(false),
113
+ });
114
+ export type ReleaseSyncManifest = z.input<typeof releaseSyncManifestSchema>;
115
+
116
+ export const releaseSyncRequestSchema = z.object({
117
+ releases: z.array(releaseSyncManifestSchema).min(1).max(100),
118
+ });
119
+ export type ReleaseSyncRequest = z.input<typeof releaseSyncRequestSchema>;
120
+
121
+ export const releaseSyncResultSchema = z.object({
122
+ release_id: nonEmptyString,
123
+ success: z.boolean(),
124
+ status: uploadStatusSchema,
125
+ });
126
+ export type ReleaseSyncResult = z.infer<typeof releaseSyncResultSchema>;
127
+
128
+ export const releaseSyncResponseSchema = z.object({
129
+ synced: z.number().int().nonnegative(),
130
+ results: z.array(releaseSyncResultSchema),
131
+ });
132
+ export type ReleaseSyncResponse = z.infer<typeof releaseSyncResponseSchema>;