@tulip-systems/drive 0.8.1 → 0.8.3

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 (141) hide show
  1. package/dist/client.d.mts +5 -0
  2. package/dist/client.mjs +6 -0
  3. package/dist/components/content.d.mts +13 -0
  4. package/dist/components/content.mjs +17 -0
  5. package/dist/components/context.client.d.mts +13 -0
  6. package/dist/components/dnd.client.d.mts +16 -0
  7. package/dist/components/dnd.client.mjs +29 -0
  8. package/dist/components/grid-card.client.d.mts +46 -0
  9. package/dist/components/grid-card.client.mjs +173 -0
  10. package/dist/components/grid.client.d.mts +53 -0
  11. package/dist/components/grid.client.mjs +53 -0
  12. package/dist/components/navigation/breadcrumbs.client.d.mts +25 -0
  13. package/dist/components/navigation/breadcrumbs.client.mjs +51 -0
  14. package/dist/components/navigation/header.client.d.mts +27 -0
  15. package/dist/components/navigation/header.client.mjs +40 -0
  16. package/dist/components/navigation/toolbar.client.d.mts +23 -0
  17. package/dist/components/navigation/toolbar.client.mjs +34 -0
  18. package/dist/components/navigation/view-switcher.client.d.mts +9 -0
  19. package/dist/components/navigation/view-switcher.client.mjs +35 -0
  20. package/dist/components/selection.client.d.mts +27 -0
  21. package/dist/components/selection.client.mjs +36 -0
  22. package/dist/components/view.client.d.mts +25 -0
  23. package/dist/components/view.client.mjs +43 -0
  24. package/dist/config/filters.mjs +14 -0
  25. package/dist/config/types.mjs +60 -0
  26. package/dist/google/client.d.mts +8 -0
  27. package/dist/google/client.mjs +9 -0
  28. package/dist/google/server.d.mts +3 -0
  29. package/dist/google/server.mjs +4 -0
  30. package/dist/google.d.mts +6 -0
  31. package/dist/google.mjs +7 -0
  32. package/dist/index.d.mts +7 -0
  33. package/dist/index.mjs +7 -0
  34. package/dist/lib/constants.d.mts +11 -0
  35. package/dist/lib/constants.mjs +20 -0
  36. package/dist/lib/contracts.d.mts +100 -0
  37. package/dist/lib/dto.d.mts +117 -0
  38. package/dist/lib/dto.mjs +57 -0
  39. package/dist/lib/helpers.d.mts +17 -0
  40. package/dist/lib/helpers.mjs +36 -0
  41. package/dist/lib/helpers.server.d.mts +13 -0
  42. package/dist/lib/helpers.server.mjs +15 -0
  43. package/dist/lib/search-params.d.mts +8 -0
  44. package/dist/lib/search-params.mjs +7 -0
  45. package/dist/lib/validators.d.mts +157 -0
  46. package/dist/lib/validators.mjs +65 -0
  47. package/dist/local/client.d.mts +13 -0
  48. package/dist/local/client.mjs +13 -0
  49. package/dist/local/server.d.mts +4 -0
  50. package/dist/local/server.mjs +5 -0
  51. package/dist/local.d.mts +8 -0
  52. package/dist/local.mjs +9 -0
  53. package/dist/providers/google/components/command-file-update.d.mts +21 -0
  54. package/dist/providers/google/components/command-file-update.mjs +51 -0
  55. package/dist/providers/google/components/command-folder-create.d.mts +21 -0
  56. package/dist/providers/google/components/command-folder-create.mjs +58 -0
  57. package/dist/providers/google/components/command-folder-update.d.mts +21 -0
  58. package/dist/providers/google/components/command-folder-update.mjs +51 -0
  59. package/dist/providers/google/components/content.client.d.mts +9 -0
  60. package/dist/providers/google/components/content.client.mjs +10 -0
  61. package/dist/providers/google/components/navigation.client.d.mts +15 -0
  62. package/dist/providers/google/components/navigation.client.mjs +17 -0
  63. package/dist/providers/google/components/provider.client.d.mts +32 -0
  64. package/dist/providers/google/components/provider.client.mjs +42 -0
  65. package/dist/providers/google/components/view.client.d.mts +40 -0
  66. package/dist/providers/google/components/view.client.mjs +96 -0
  67. package/dist/providers/google/config/columns-data.d.mts +7 -0
  68. package/dist/providers/google/config/columns-data.mjs +69 -0
  69. package/dist/providers/google/config/filters.d.mts +15 -0
  70. package/dist/providers/google/config/filters.mjs +7 -0
  71. package/dist/providers/google/lib/constants.mjs +12 -0
  72. package/dist/providers/google/lib/dto.d.mts +38 -0
  73. package/dist/providers/google/lib/dto.mjs +65 -0
  74. package/dist/providers/google/lib/helpers.mjs +38 -0
  75. package/dist/providers/google/lib/router.server.d.mts +611 -0
  76. package/dist/providers/google/lib/router.server.mjs +39 -0
  77. package/dist/providers/google/lib/search-params.d.mts +14 -0
  78. package/dist/providers/google/lib/search-params.mjs +11 -0
  79. package/dist/providers/google/lib/service.server.d.mts +185 -0
  80. package/dist/providers/google/lib/service.server.mjs +610 -0
  81. package/dist/providers/google/lib/validators.d.mts +302 -0
  82. package/dist/providers/google/lib/validators.mjs +58 -0
  83. package/dist/providers/local/components/command-file-update.d.mts +17 -0
  84. package/dist/providers/local/components/command-file-update.mjs +47 -0
  85. package/dist/providers/local/components/command-file-upload.d.mts +10 -0
  86. package/dist/providers/local/components/command-file-upload.mjs +34 -0
  87. package/dist/providers/local/components/command-folder-create.d.mts +17 -0
  88. package/dist/providers/local/components/command-folder-create.mjs +54 -0
  89. package/dist/providers/local/components/command-folder-update.d.mts +17 -0
  90. package/dist/providers/local/components/command-folder-update.mjs +47 -0
  91. package/dist/providers/local/components/content.client.d.mts +6 -0
  92. package/dist/providers/local/components/content.client.mjs +7 -0
  93. package/dist/providers/local/components/navigation.client.d.mts +15 -0
  94. package/dist/providers/local/components/navigation.client.mjs +17 -0
  95. package/dist/providers/local/components/provider.client.d.mts +39 -0
  96. package/dist/providers/local/components/provider.client.mjs +60 -0
  97. package/dist/providers/local/components/upload-zone-context.client.d.mts +37 -0
  98. package/dist/providers/local/components/upload-zone-context.client.mjs +22 -0
  99. package/dist/providers/local/components/upload-zone.client.d.mts +29 -0
  100. package/dist/providers/local/components/upload-zone.client.mjs +146 -0
  101. package/dist/providers/local/components/view.client.d.mts +31 -0
  102. package/dist/providers/local/components/view.client.mjs +90 -0
  103. package/dist/providers/local/config/columns-data.d.mts +7 -0
  104. package/dist/providers/local/config/columns-data.mjs +69 -0
  105. package/dist/providers/local/config/filters.d.mts +25 -0
  106. package/dist/providers/local/config/filters.mjs +14 -0
  107. package/dist/providers/local/lib/constants.d.mts +11 -0
  108. package/dist/providers/local/lib/constants.mjs +28 -0
  109. package/dist/providers/local/lib/helpers.d.mts +44 -0
  110. package/dist/providers/local/lib/helpers.mjs +109 -0
  111. package/dist/providers/local/lib/route-handler.server.d.mts +33 -0
  112. package/dist/providers/local/lib/route-handler.server.mjs +113 -0
  113. package/dist/providers/local/lib/router.server.d.mts +41647 -0
  114. package/dist/providers/local/lib/router.server.mjs +51 -0
  115. package/dist/providers/local/lib/schema.d.mts +1112 -0
  116. package/dist/providers/local/lib/schema.mjs +70 -0
  117. package/dist/providers/local/lib/search-params.d.mts +13 -0
  118. package/dist/providers/local/lib/search-params.mjs +8 -0
  119. package/dist/providers/local/lib/service.server.d.mts +488 -0
  120. package/dist/providers/local/lib/service.server.mjs +667 -0
  121. package/dist/providers/local/lib/upload.client.d.mts +61 -0
  122. package/dist/providers/local/lib/upload.client.mjs +99 -0
  123. package/dist/providers/local/lib/validators.d.mts +453 -0
  124. package/dist/providers/local/lib/validators.mjs +95 -0
  125. package/dist/server.d.mts +2 -0
  126. package/dist/server.mjs +3 -0
  127. package/package.json +45 -46
  128. package/src/components/grid-card.client.tsx +17 -12
  129. package/src/components/selection.client.tsx +1 -2
  130. package/src/config/types.tsx +1 -1
  131. package/src/providers/google/components/command-file-update.tsx +2 -2
  132. package/src/providers/google/components/command-folder-create.tsx +2 -2
  133. package/src/providers/google/components/command-folder-update.tsx +2 -2
  134. package/src/providers/google/components/view.client.tsx +1 -7
  135. package/src/providers/google/lib/helpers.ts +0 -9
  136. package/src/providers/google/lib/service.server.ts +1 -3
  137. package/src/providers/local/components/command-file-update.tsx +2 -2
  138. package/src/providers/local/components/command-folder-create.tsx +2 -2
  139. package/src/providers/local/components/command-folder-update.tsx +2 -2
  140. package/src/providers/local/components/upload-zone.client.tsx +1 -1
  141. package/src/providers/local/lib/helpers.ts +0 -1
@@ -0,0 +1,70 @@
1
+ import { imageVariants, nodeSubtypes } from "./constants.mjs";
2
+ import { baseColumns } from "@tulip-systems/core/config";
3
+ import { imageDispositions, storageAssets } from "@tulip-systems/core/storage";
4
+ import { relations } from "drizzle-orm";
5
+ import { boolean, pgEnum, pgTable, unique } from "drizzle-orm/pg-core";
6
+
7
+ //#region src/providers/local/lib/schema.ts
8
+ const nodeTypeEnum = pgEnum("node_types", ["file", "folder"]);
9
+ /**
10
+ * Node table
11
+ */
12
+ const nodes = pgTable("nodes", (t) => ({
13
+ ...baseColumns,
14
+ name: t.text().notNull(),
15
+ namespace: t.text().notNull().default("global"),
16
+ type: nodeTypeEnum(),
17
+ subtype: t.text({ enum: nodeSubtypes }).notNull().default("other"),
18
+ size: t.integer(),
19
+ contentType: t.varchar({ length: 255 }),
20
+ readonly: boolean().default(false),
21
+ hidden: boolean().default(false),
22
+ archivedAt: t.timestamp(),
23
+ parentId: t.uuid().references(() => nodes.id, { onDelete: "cascade" }),
24
+ assetId: t.uuid().references(() => storageAssets.id, { onDelete: "cascade" })
25
+ }));
26
+ const nodesRelations = relations(nodes, ({ one, many }) => ({
27
+ parent: one(nodes, {
28
+ fields: [nodes.parentId],
29
+ references: [nodes.id],
30
+ relationName: "parent"
31
+ }),
32
+ children: many(nodes, { relationName: "parent" }),
33
+ urls: many(nodePresignedUrls),
34
+ variants: many(nodeVariants)
35
+ }));
36
+ /**
37
+ * Node variants table
38
+ */
39
+ const nodeVariants = pgTable("node_variants", (t) => ({
40
+ ...baseColumns,
41
+ nodeId: t.uuid().notNull().references(() => nodes.id, { onDelete: "cascade" }),
42
+ assetId: t.uuid().notNull().references(() => storageAssets.id, { onDelete: "cascade" }),
43
+ variant: t.text({ enum: imageVariants }).notNull(),
44
+ width: t.integer().notNull()
45
+ }));
46
+ const nodeVariantsRelations = relations(nodeVariants, ({ one }) => ({ node: one(nodes, {
47
+ fields: [nodeVariants.nodeId],
48
+ references: [nodes.id],
49
+ relationName: "node"
50
+ }) }));
51
+ /**
52
+ * Node presigned urls table
53
+ */
54
+ const nodePresignedUrls = pgTable("node_presigned_urls", (t) => ({
55
+ ...baseColumns,
56
+ url: t.text().notNull().unique(),
57
+ variant: t.text({ enum: imageVariants }).notNull(),
58
+ disposition: t.text({ enum: imageDispositions }).notNull(),
59
+ expiresAt: t.timestamp().notNull(),
60
+ nodeId: t.uuid().notNull().references(() => nodes.id, { onDelete: "cascade" }),
61
+ variantId: t.uuid().references(() => nodeVariants.id, { onDelete: "set null" })
62
+ }), (t) => [unique("node_presigned_url_unique").on(t.nodeId, t.variant, t.disposition)]);
63
+ const nodePresignedUrlsRelations = relations(nodePresignedUrls, ({ one }) => ({ node: one(nodes, {
64
+ fields: [nodePresignedUrls.nodeId],
65
+ references: [nodes.id],
66
+ relationName: "node"
67
+ }) }));
68
+
69
+ //#endregion
70
+ export { nodePresignedUrls, nodePresignedUrlsRelations, nodeTypeEnum, nodeVariants, nodeVariantsRelations, nodes, nodesRelations };
@@ -0,0 +1,13 @@
1
+ import * as nuqs_server0 from "nuqs/server";
2
+
3
+ //#region src/providers/local/lib/search-params.d.ts
4
+ declare const localDriveTreeFilterSearchParams: {
5
+ readonly subtypes: nuqs_server0.SingleParserBuilder<("image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other")[] | null>;
6
+ readonly hidden: nuqs_server0.SingleParserBuilder<boolean | null>;
7
+ readonly types: nuqs_server0.SingleParserBuilder<("file" | "folder")[] | null>;
8
+ readonly contentTypes: nuqs_server0.SingleParserBuilder<string[] | null>;
9
+ readonly nodeIds: nuqs_server0.SingleParserBuilder<string[] | null>;
10
+ readonly isArchived: nuqs_server0.SingleParserBuilder<boolean | null>;
11
+ };
12
+ //#endregion
13
+ export { localDriveTreeFilterSearchParams };
@@ -0,0 +1,8 @@
1
+ import { localDriveFilters } from "../config/filters.mjs";
2
+ import { resolveFiltersSearchParams } from "@tulip-systems/core/data-tables";
3
+
4
+ //#region src/providers/local/lib/search-params.ts
5
+ const localDriveTreeFilterSearchParams = resolveFiltersSearchParams(localDriveFilters);
6
+
7
+ //#endregion
8
+ export { localDriveTreeFilterSearchParams };
@@ -0,0 +1,488 @@
1
+ import { DriveArchive, DriveDirectUpload, DriveNodeMutations, DrivePresignedUpload, DrivePreviewGenerator, DriveReader, DriveReadonly } from "../../../lib/contracts.mjs";
2
+ import { CreateLocalDriveFolderSchema, GetLocalDriveNodesByParentIdInput, GetLocalFileURLSchema, ListLocalDriveFlatSchema, ListLocalDriveTreeSchema, LocalDriveFileNode, LocalDriveNode, LocalDriveNodeChild, LocalDriveNodeWithAsset, LocalDriveNodeWithChildren, LocalNode, PresignLocalDriveFileInput, UpdateLocalDriveNodeInput, UploadLocalDriveFileSchema } from "./validators.mjs";
3
+ import { TDatabaseSchema } from "@tulip-systems/core/config";
4
+ import { ObjectBodyInput } from "@tulip-systems/core/storage";
5
+ import { TableQueryResponse } from "@tulip-systems/core/data-tables/server";
6
+ import { Database } from "@tulip-systems/core/database/server";
7
+ import { Storage } from "@tulip-systems/core/storage/server";
8
+
9
+ //#region src/providers/local/lib/service.server.d.ts
10
+ /**
11
+ * Drive Service Config
12
+ */
13
+ type LocalDriveConfig<TSchema extends TDatabaseSchema> = {
14
+ db: Database<TSchema>;
15
+ storage: Storage<TSchema>;
16
+ };
17
+ /**
18
+ * Drive Service
19
+ */
20
+ declare class LocalDrive<TSchema extends TDatabaseSchema> implements DriveReader<LocalDriveNode>, DriveNodeMutations<LocalDriveNode>, DriveDirectUpload<LocalDriveNode, ObjectBodyInput>, DrivePresignedUpload<LocalDriveFileNode>, DrivePreviewGenerator, DriveReadonly<LocalDriveNode>, DriveArchive<LocalDriveNode> {
21
+ #private;
22
+ storage: Storage<TSchema>;
23
+ constructor({
24
+ db,
25
+ storage
26
+ }: LocalDriveConfig<TSchema>);
27
+ /**
28
+ * Retrieves the node row for a given id.
29
+ *
30
+ * This is a thin lookup helper that returns the raw database result for the
31
+ * matching node. It does not load children, variants, or perform namespace
32
+ * validation.
33
+ *
34
+ * @param id - The node identifier.
35
+ * @returns A promise resolving to the matching node row as a single-item array,
36
+ * or an empty array when no node exists.
37
+ */
38
+ getNodeById(id: string): Promise<LocalDriveNode | null>;
39
+ /**
40
+ * Retrieves a node together with its direct children for a namespace.
41
+ *
42
+ * This is used by the drive UI when opening a node detail view that expects
43
+ * the immediate child nodes in the same payload.
44
+ *
45
+ * @param input - The target node id and namespace.
46
+ * @returns The node with its children when found, otherwise `null`.
47
+ */
48
+ getNodeWithChildren(input: {
49
+ id: string;
50
+ namespace: string;
51
+ }): Promise<LocalDriveNodeWithChildren | null>;
52
+ /**
53
+ * Lists nodes within a folder scope using the current table-query filters.
54
+ *
55
+ * Behavior:
56
+ * - Scopes results to a namespace
57
+ * - Resolves either root nodes (`parentId = null`) or a specific parent folder
58
+ * - Applies optional filters such as ids, types, hidden state, and archive state
59
+ * - Applies search and sorting through the shared table-query utilities
60
+ *
61
+ * @param input - Table query input containing drive filters, search, and sorting.
62
+ * @returns A promise resolving to the matching child nodes.
63
+ */
64
+ getNodesByParentId({
65
+ filters,
66
+ ...query
67
+ }: GetLocalDriveNodesByParentIdInput): Promise<LocalDriveNode[]>;
68
+ /**
69
+ * Lists tree-scoped nodes with pagination, search, and richer local filters.
70
+ *
71
+ * This query is intended for admin table views where additional local fields
72
+ * such as subtype and contentType are part of the filtering contract.
73
+ *
74
+ * @param input - Table query input with tree filters.
75
+ * @returns A paginated table-query response.
76
+ */
77
+ listTree({
78
+ filters,
79
+ ...query
80
+ }: ListLocalDriveTreeSchema): Promise<TableQueryResponse<LocalDriveNodeWithAsset>>;
81
+ /**
82
+ * Lists flat nodes with pagination, search, and richer local filters.
83
+ *
84
+ * Unlike `listTree`, this query ignores parent scoping and is intended for
85
+ * flat file-manager style views.
86
+ *
87
+ * @param input - Table query input with flat filters.
88
+ * @returns A paginated table-query response.
89
+ */
90
+ listFlat({
91
+ filters,
92
+ ...query
93
+ }: ListLocalDriveFlatSchema): Promise<TableQueryResponse<LocalDriveNodeWithAsset>>;
94
+ /**
95
+ * Resolves the ancestor chain for a folder or file node.
96
+ *
97
+ * The returned list starts with the root-most ancestor and ends with the
98
+ * requested node. This is primarily used for breadcrumb navigation.
99
+ *
100
+ * @param input - The node id and namespace to resolve.
101
+ * @returns A promise resolving to the ordered ancestor chain.
102
+ */
103
+ getFolderParents(input: {
104
+ id: string | null;
105
+ namespace: string;
106
+ }): Promise<(Pick<{
107
+ id: string;
108
+ createdAt: Date;
109
+ updatedAt: Date;
110
+ name: string;
111
+ type: "file" | "folder" | null;
112
+ readonly: boolean | null;
113
+ size: number | null;
114
+ contentType: string | null;
115
+ namespace: string;
116
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
117
+ hidden: boolean | null;
118
+ archivedAt: Date | null;
119
+ parentId: string | null;
120
+ assetId: string | null;
121
+ }, "id" | "name" | "parentId"> & {
122
+ depth: number;
123
+ })[]>;
124
+ /**
125
+ * Retrieves the structural subtree for one or more root node ids.
126
+ *
127
+ * The result includes the provided start nodes and all descendant nodes,
128
+ * returned as a lightweight shape containing only the fields needed for
129
+ * recursive drive operations such as delete, archive, and validation.
130
+ *
131
+ * Depth starts at `0` for the provided root nodes and increases for each
132
+ * descendant level. Duplicate paths are collapsed by keeping the minimum depth.
133
+ *
134
+ * @param ids - Root node ids to traverse from.
135
+ * @returns A promise resolving to the subtree rows for the provided nodes.
136
+ */
137
+ getNodeChildren(ids: string[]): Promise<LocalDriveNodeChild[]>;
138
+ /**
139
+ * Resolves an access URL for a file node and the requested variant.
140
+ *
141
+ * Behavior:
142
+ * - Rejects folder nodes because only file nodes can resolve a file URL
143
+ * - Reuses a cached URL when an unexpired entry exists for the requested
144
+ * variant and disposition
145
+ * - Resolves the requested preview variant asset when available
146
+ * - Falls back to the main file asset when the requested variant does not exist
147
+ * - Delegates final URL generation to the storage service
148
+ * - Stores the generated URL in the local cache table for future reuse
149
+ *
150
+ * @param node - The file node to resolve.
151
+ * @param options - URL options such as variant and content disposition.
152
+ * @returns A promise resolving to a temporary file URL.
153
+ */
154
+ getURL(node: LocalNode, options?: GetLocalFileURLSchema): Promise<string>;
155
+ /**
156
+ * Moves a node to a different parent folder.
157
+ *
158
+ * The target parent is validated before the move is persisted. Moving a node
159
+ * into itself or into one of its descendants is rejected to keep the tree
160
+ * structure valid.
161
+ *
162
+ * @param input - The node id and the target parent id.
163
+ * @returns A promise resolving to the updated node.
164
+ * @throws {ServerError} If the move is invalid.
165
+ */
166
+ moveNode(input: {
167
+ id: string;
168
+ parentId: string | null;
169
+ }): Promise<{
170
+ id: string;
171
+ createdAt: Date;
172
+ updatedAt: Date;
173
+ name: string;
174
+ namespace: string;
175
+ type: "file" | "folder";
176
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
177
+ size: number | null;
178
+ contentType: string | null;
179
+ readonly: boolean;
180
+ hidden: boolean;
181
+ archivedAt: Date | null;
182
+ parentId: string | null;
183
+ links: {
184
+ view: string | null;
185
+ download: string | null;
186
+ preview: string | null;
187
+ thumbnail: string | null;
188
+ };
189
+ availability: "ready" | "pending" | "failed";
190
+ provider: "local";
191
+ assetId: string | null;
192
+ }>;
193
+ /**
194
+ * Uploads a file through the storage service and creates the matching node.
195
+ *
196
+ * Behavior:
197
+ * - Validates the parent folder when `parentId` is provided
198
+ * - Uploads the file bytes through the storage service
199
+ * - Persists the created storage asset id on the node
200
+ * - Cleans up the uploaded asset when node creation fails
201
+ *
202
+ * @param input - File metadata and body.
203
+ * @returns A promise resolving to the created file node.
204
+ * @throws {ServerError} If validation or persistence fails.
205
+ */
206
+ uploadFile(input: UploadLocalDriveFileSchema & {
207
+ body: ObjectBodyInput;
208
+ }): Promise<{
209
+ id: string;
210
+ createdAt: Date;
211
+ updatedAt: Date;
212
+ name: string;
213
+ namespace: string;
214
+ type: "file" | "folder";
215
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
216
+ size: number | null;
217
+ contentType: string | null;
218
+ readonly: boolean;
219
+ hidden: boolean;
220
+ archivedAt: Date | null;
221
+ parentId: string | null;
222
+ links: {
223
+ view: string | null;
224
+ download: string | null;
225
+ preview: string | null;
226
+ thumbnail: string | null;
227
+ };
228
+ availability: "ready" | "pending" | "failed";
229
+ provider: "local";
230
+ assetId: string | null;
231
+ }>;
232
+ /**
233
+ * Creates a direct upload intent and the matching file node.
234
+ *
235
+ * Behavior:
236
+ * - Validates the parent folder when `parentId` is provided
237
+ * - Requests a presigned upload from the storage service
238
+ * - Creates the file node linked to the pending storage asset
239
+ * - Purges the pending asset when node creation fails
240
+ *
241
+ * @param input - File metadata used to create the upload intent.
242
+ * @returns The created asset intent, presigned URL, and file node.
243
+ * @throws {ServerError} If validation or node creation fails.
244
+ */
245
+ presignUpload(input: PresignLocalDriveFileInput): Promise<{
246
+ uploadId: string;
247
+ presignedUrl: string;
248
+ node: LocalDriveFileNode;
249
+ asset: {
250
+ id: string;
251
+ createdAt: Date;
252
+ updatedAt: Date;
253
+ name: string | null;
254
+ key: string;
255
+ metadata: unknown;
256
+ provider: "s3";
257
+ uploadId: string;
258
+ visibility: "private" | "public";
259
+ size: number | null;
260
+ contentType: string | null;
261
+ bucket: string;
262
+ status: "error" | "pending" | "ready";
263
+ etag: string | null;
264
+ uploadedAt: Date;
265
+ deletedAt: Date | null;
266
+ };
267
+ }>;
268
+ /**
269
+ * Finalizes a presigned upload and synchronizes the node metadata.
270
+ *
271
+ * Behavior:
272
+ * - Confirms the pending asset through the storage service
273
+ * - Finds the matching node by `assetId`
274
+ * - Synchronizes final asset metadata onto the node
275
+ * - Schedules preview generation for image files
276
+ *
277
+ * @param uploadId - The storage upload id returned by the presign step.
278
+ * @returns A promise resolving to the finalized file node.
279
+ * @throws {ServerError} If the asset or node cannot be resolved.
280
+ */
281
+ confirmUpload(uploadId: string): Promise<LocalDriveFileNode>;
282
+ /**
283
+ * Generates preview assets for an image node.
284
+ *
285
+ * The original file is loaded from storage, resized to the configured device
286
+ * widths, uploaded as separate storage assets, and linked through
287
+ * `node_variants`. Existing preview variants are skipped.
288
+ *
289
+ * @param input - The target file node identifier.
290
+ * @returns A promise that resolves once preview generation completes.
291
+ * @throws {ServerError} If the source node or asset cannot be resolved.
292
+ */
293
+ generatePreviews(input: {
294
+ id: string;
295
+ }): Promise<void>;
296
+ /**
297
+ * Creates a folder node in the drive tree.
298
+ *
299
+ * The parent folder is validated before the folder is inserted. Folders do
300
+ * not own a storage asset and are stored purely as drive metadata.
301
+ *
302
+ * @param input - Folder metadata.
303
+ * @returns A promise resolving to the created folder node.
304
+ * @throws {ServerError} If validation or insertion fails.
305
+ */
306
+ createFolder(input: CreateLocalDriveFolderSchema): Promise<{
307
+ id: string;
308
+ createdAt: Date;
309
+ updatedAt: Date;
310
+ name: string;
311
+ namespace: string;
312
+ type: "file" | "folder";
313
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
314
+ size: number | null;
315
+ contentType: string | null;
316
+ readonly: boolean;
317
+ hidden: boolean;
318
+ archivedAt: Date | null;
319
+ parentId: string | null;
320
+ links: {
321
+ view: string | null;
322
+ download: string | null;
323
+ preview: string | null;
324
+ thumbnail: string | null;
325
+ };
326
+ availability: "ready" | "pending" | "failed";
327
+ provider: "local";
328
+ assetId: string | null;
329
+ }>;
330
+ /**
331
+ * Updates mutable node metadata.
332
+ *
333
+ * Supported updates currently include renaming, reparenting, visibility flags,
334
+ * and archive state. Structural updates are validated before being persisted.
335
+ *
336
+ * @param id - The target node id.
337
+ * @param data - Partial node fields to update.
338
+ * @returns A promise resolving to the updated node.
339
+ * @throws {ServerError} If the node does not exist or the update is invalid.
340
+ */
341
+ updateNode(id: string, data: UpdateLocalDriveNodeInput): Promise<{
342
+ id: string;
343
+ createdAt: Date;
344
+ updatedAt: Date;
345
+ name: string;
346
+ namespace: string;
347
+ type: "file" | "folder";
348
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
349
+ size: number | null;
350
+ contentType: string | null;
351
+ readonly: boolean;
352
+ hidden: boolean;
353
+ archivedAt: Date | null;
354
+ parentId: string | null;
355
+ links: {
356
+ view: string | null;
357
+ download: string | null;
358
+ preview: string | null;
359
+ thumbnail: string | null;
360
+ };
361
+ availability: "ready" | "pending" | "failed";
362
+ provider: "local";
363
+ assetId: string | null;
364
+ }>;
365
+ /**
366
+ * Sets the readonly state for multiple nodes.
367
+ *
368
+ * This method is intended for bulk lock and unlock operations in the drive UI.
369
+ *
370
+ * @param ids - The node ids to update.
371
+ * @param readonly - The readonly state to apply.
372
+ * @returns A promise resolving to the updated nodes.
373
+ * @throws {ServerError} If no ids are provided or no matching nodes are found.
374
+ */
375
+ setReadonly(ids: string[], readonly: boolean): Promise<{
376
+ id: string;
377
+ createdAt: Date;
378
+ updatedAt: Date;
379
+ name: string;
380
+ namespace: string;
381
+ type: "file" | "folder";
382
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
383
+ size: number | null;
384
+ contentType: string | null;
385
+ readonly: boolean;
386
+ hidden: boolean;
387
+ archivedAt: Date | null;
388
+ parentId: string | null;
389
+ links: {
390
+ view: string | null;
391
+ download: string | null;
392
+ preview: string | null;
393
+ thumbnail: string | null;
394
+ };
395
+ availability: "ready" | "pending" | "failed";
396
+ provider: "local";
397
+ assetId: string | null;
398
+ }[]>;
399
+ /**
400
+ * Archives nodes by setting their archive timestamp.
401
+ *
402
+ * This is a drive-level archive state and does not delete the underlying
403
+ * storage assets.
404
+ *
405
+ * @param ids - The node ids to archive.
406
+ * @returns A promise resolving to the archived nodes.
407
+ * @throws {ServerError} If no ids are provided or no matching nodes are found.
408
+ */
409
+ archiveNodes(ids: string[]): Promise<{
410
+ id: string;
411
+ createdAt: Date;
412
+ updatedAt: Date;
413
+ name: string;
414
+ namespace: string;
415
+ type: "file" | "folder";
416
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
417
+ size: number | null;
418
+ contentType: string | null;
419
+ readonly: boolean;
420
+ hidden: boolean;
421
+ archivedAt: Date | null;
422
+ parentId: string | null;
423
+ links: {
424
+ view: string | null;
425
+ download: string | null;
426
+ preview: string | null;
427
+ thumbnail: string | null;
428
+ };
429
+ availability: "ready" | "pending" | "failed";
430
+ provider: "local";
431
+ assetId: string | null;
432
+ }[]>;
433
+ /**
434
+ * Restores archived nodes by clearing their archive timestamp.
435
+ *
436
+ * @param ids - The node ids to restore.
437
+ * @returns A promise resolving to the restored nodes.
438
+ * @throws {ServerError} If no ids are provided or no matching nodes are found.
439
+ */
440
+ restoreNodes(ids: string[]): Promise<{
441
+ id: string;
442
+ createdAt: Date;
443
+ updatedAt: Date;
444
+ name: string;
445
+ namespace: string;
446
+ type: "file" | "folder";
447
+ subtype: "image" | "document" | "spreadsheet" | "video" | "audio" | "archive" | "other";
448
+ size: number | null;
449
+ contentType: string | null;
450
+ readonly: boolean;
451
+ hidden: boolean;
452
+ archivedAt: Date | null;
453
+ parentId: string | null;
454
+ links: {
455
+ view: string | null;
456
+ download: string | null;
457
+ preview: string | null;
458
+ thumbnail: string | null;
459
+ };
460
+ availability: "ready" | "pending" | "failed";
461
+ provider: "local";
462
+ assetId: string | null;
463
+ }[]>;
464
+ /**
465
+ * Permanently deletes a node subtree and all linked assets.
466
+ *
467
+ * The subtree includes the provided node and all descendants. Associated file
468
+ * assets and generated preview assets are purged from storage after the nodes
469
+ * are removed from the drive catalog.
470
+ *
471
+ * @param id - The root node id to delete.
472
+ * @throws {ServerError} If the node does not exist.
473
+ */
474
+ deleteNode(id: string): Promise<void>;
475
+ /**
476
+ * Permanently deletes multiple node subtrees and their linked assets.
477
+ *
478
+ * Each provided id is treated as a subtree root. All descendant nodes are
479
+ * removed together with their main file assets and generated preview assets.
480
+ *
481
+ * @param ids - Root node ids to delete.
482
+ * @returns A promise that resolves when deletion completes.
483
+ * @throws {ServerError} If no ids are provided or no matching nodes are found.
484
+ */
485
+ deleteNodes(ids: string[]): Promise<void>;
486
+ }
487
+ //#endregion
488
+ export { LocalDrive, LocalDriveConfig };