@tulip-systems/drive 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.mts +5 -0
- package/dist/client.mjs +6 -0
- package/dist/components/content.d.mts +14 -0
- package/dist/components/content.d.mts.map +1 -0
- package/dist/components/content.mjs +18 -0
- package/dist/components/content.mjs.map +1 -0
- package/dist/components/context.client.d.mts +14 -0
- package/dist/components/context.client.d.mts.map +1 -0
- package/dist/components/dnd.client.d.mts +17 -0
- package/dist/components/dnd.client.d.mts.map +1 -0
- package/dist/components/dnd.client.mjs +30 -0
- package/dist/components/dnd.client.mjs.map +1 -0
- package/dist/components/grid-card.client.d.mts +43 -0
- package/dist/components/grid-card.client.d.mts.map +1 -0
- package/dist/components/grid-card.client.mjs +173 -0
- package/dist/components/grid-card.client.mjs.map +1 -0
- package/dist/components/grid.client.d.mts +54 -0
- package/dist/components/grid.client.d.mts.map +1 -0
- package/dist/components/grid.client.mjs +54 -0
- package/dist/components/grid.client.mjs.map +1 -0
- package/dist/components/navigation/breadcrumbs.client.d.mts +26 -0
- package/dist/components/navigation/breadcrumbs.client.d.mts.map +1 -0
- package/dist/components/navigation/breadcrumbs.client.mjs +52 -0
- package/dist/components/navigation/breadcrumbs.client.mjs.map +1 -0
- package/dist/components/navigation/header.client.d.mts +28 -0
- package/dist/components/navigation/header.client.d.mts.map +1 -0
- package/dist/components/navigation/header.client.mjs +41 -0
- package/dist/components/navigation/header.client.mjs.map +1 -0
- package/dist/components/navigation/toolbar.client.d.mts +24 -0
- package/dist/components/navigation/toolbar.client.d.mts.map +1 -0
- package/dist/components/navigation/toolbar.client.mjs +35 -0
- package/dist/components/navigation/toolbar.client.mjs.map +1 -0
- package/dist/components/navigation/view-switcher.client.d.mts +10 -0
- package/dist/components/navigation/view-switcher.client.d.mts.map +1 -0
- package/dist/components/navigation/view-switcher.client.mjs +36 -0
- package/dist/components/navigation/view-switcher.client.mjs.map +1 -0
- package/dist/components/selection.client.d.mts +28 -0
- package/dist/components/selection.client.d.mts.map +1 -0
- package/dist/components/selection.client.mjs +39 -0
- package/dist/components/selection.client.mjs.map +1 -0
- package/dist/components/view.client.d.mts +26 -0
- package/dist/components/view.client.d.mts.map +1 -0
- package/dist/components/view.client.mjs +44 -0
- package/dist/components/view.client.mjs.map +1 -0
- package/dist/config/filters.mjs +15 -0
- package/dist/config/filters.mjs.map +1 -0
- package/dist/config/types.mjs +61 -0
- package/dist/config/types.mjs.map +1 -0
- package/dist/google/client.d.mts +8 -0
- package/dist/google/client.mjs +9 -0
- package/dist/google/server.d.mts +3 -0
- package/dist/google/server.mjs +4 -0
- package/dist/google.d.mts +6 -0
- package/dist/google.mjs +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +7 -0
- package/dist/lib/constants.d.mts +12 -0
- package/dist/lib/constants.d.mts.map +1 -0
- package/dist/lib/constants.mjs +21 -0
- package/dist/lib/constants.mjs.map +1 -0
- package/dist/lib/contracts.d.mts +101 -0
- package/dist/lib/contracts.d.mts.map +1 -0
- package/dist/lib/dto.d.mts +118 -0
- package/dist/lib/dto.d.mts.map +1 -0
- package/dist/lib/dto.mjs +58 -0
- package/dist/lib/dto.mjs.map +1 -0
- package/dist/lib/helpers.d.mts +18 -0
- package/dist/lib/helpers.d.mts.map +1 -0
- package/dist/lib/helpers.mjs +37 -0
- package/dist/lib/helpers.mjs.map +1 -0
- package/dist/lib/helpers.server.d.mts +14 -0
- package/dist/lib/helpers.server.d.mts.map +1 -0
- package/dist/lib/helpers.server.mjs +16 -0
- package/dist/lib/helpers.server.mjs.map +1 -0
- package/dist/lib/search-params.d.mts +9 -0
- package/dist/lib/search-params.d.mts.map +1 -0
- package/dist/lib/search-params.mjs +8 -0
- package/dist/lib/search-params.mjs.map +1 -0
- package/dist/lib/validators.d.mts +158 -0
- package/dist/lib/validators.d.mts.map +1 -0
- package/dist/lib/validators.mjs +66 -0
- package/dist/lib/validators.mjs.map +1 -0
- package/dist/local/client.d.mts +13 -0
- package/dist/local/client.mjs +13 -0
- package/dist/local/server.d.mts +4 -0
- package/dist/local/server.mjs +5 -0
- package/dist/local.d.mts +8 -0
- package/dist/local.mjs +9 -0
- package/dist/providers/google/components/command-file-update.d.mts +22 -0
- package/dist/providers/google/components/command-file-update.d.mts.map +1 -0
- package/dist/providers/google/components/command-file-update.mjs +52 -0
- package/dist/providers/google/components/command-file-update.mjs.map +1 -0
- package/dist/providers/google/components/command-folder-create.d.mts +22 -0
- package/dist/providers/google/components/command-folder-create.d.mts.map +1 -0
- package/dist/providers/google/components/command-folder-create.mjs +59 -0
- package/dist/providers/google/components/command-folder-create.mjs.map +1 -0
- package/dist/providers/google/components/command-folder-update.d.mts +22 -0
- package/dist/providers/google/components/command-folder-update.d.mts.map +1 -0
- package/dist/providers/google/components/command-folder-update.mjs +52 -0
- package/dist/providers/google/components/command-folder-update.mjs.map +1 -0
- package/dist/providers/google/components/content.client.d.mts +10 -0
- package/dist/providers/google/components/content.client.d.mts.map +1 -0
- package/dist/providers/google/components/content.client.mjs +11 -0
- package/dist/providers/google/components/content.client.mjs.map +1 -0
- package/dist/providers/google/components/navigation.client.d.mts +16 -0
- package/dist/providers/google/components/navigation.client.d.mts.map +1 -0
- package/dist/providers/google/components/navigation.client.mjs +18 -0
- package/dist/providers/google/components/navigation.client.mjs.map +1 -0
- package/dist/providers/google/components/provider.client.d.mts +33 -0
- package/dist/providers/google/components/provider.client.d.mts.map +1 -0
- package/dist/providers/google/components/provider.client.mjs +43 -0
- package/dist/providers/google/components/provider.client.mjs.map +1 -0
- package/dist/providers/google/components/view.client.d.mts +41 -0
- package/dist/providers/google/components/view.client.d.mts.map +1 -0
- package/dist/providers/google/components/view.client.mjs +98 -0
- package/dist/providers/google/components/view.client.mjs.map +1 -0
- package/dist/providers/google/config/columns-data.d.mts +8 -0
- package/dist/providers/google/config/columns-data.d.mts.map +1 -0
- package/dist/providers/google/config/columns-data.mjs +70 -0
- package/dist/providers/google/config/columns-data.mjs.map +1 -0
- package/dist/providers/google/config/filters.d.mts +16 -0
- package/dist/providers/google/config/filters.d.mts.map +1 -0
- package/dist/providers/google/config/filters.mjs +8 -0
- package/dist/providers/google/config/filters.mjs.map +1 -0
- package/dist/providers/google/lib/constants.mjs +13 -0
- package/dist/providers/google/lib/constants.mjs.map +1 -0
- package/dist/providers/google/lib/dto.d.mts +39 -0
- package/dist/providers/google/lib/dto.d.mts.map +1 -0
- package/dist/providers/google/lib/dto.mjs +66 -0
- package/dist/providers/google/lib/dto.mjs.map +1 -0
- package/dist/providers/google/lib/helpers.mjs +46 -0
- package/dist/providers/google/lib/helpers.mjs.map +1 -0
- package/dist/providers/google/lib/router.server.d.mts +612 -0
- package/dist/providers/google/lib/router.server.d.mts.map +1 -0
- package/dist/providers/google/lib/router.server.mjs +40 -0
- package/dist/providers/google/lib/router.server.mjs.map +1 -0
- package/dist/providers/google/lib/search-params.d.mts +15 -0
- package/dist/providers/google/lib/search-params.d.mts.map +1 -0
- package/dist/providers/google/lib/search-params.mjs +12 -0
- package/dist/providers/google/lib/search-params.mjs.map +1 -0
- package/dist/providers/google/lib/service.server.d.mts +186 -0
- package/dist/providers/google/lib/service.server.d.mts.map +1 -0
- package/dist/providers/google/lib/service.server.mjs +613 -0
- package/dist/providers/google/lib/service.server.mjs.map +1 -0
- package/dist/providers/google/lib/validators.d.mts +303 -0
- package/dist/providers/google/lib/validators.d.mts.map +1 -0
- package/dist/providers/google/lib/validators.mjs +59 -0
- package/dist/providers/google/lib/validators.mjs.map +1 -0
- package/dist/providers/local/components/command-file-update.d.mts +18 -0
- package/dist/providers/local/components/command-file-update.d.mts.map +1 -0
- package/dist/providers/local/components/command-file-update.mjs +48 -0
- package/dist/providers/local/components/command-file-update.mjs.map +1 -0
- package/dist/providers/local/components/command-file-upload.d.mts +11 -0
- package/dist/providers/local/components/command-file-upload.d.mts.map +1 -0
- package/dist/providers/local/components/command-file-upload.mjs +35 -0
- package/dist/providers/local/components/command-file-upload.mjs.map +1 -0
- package/dist/providers/local/components/command-folder-create.d.mts +18 -0
- package/dist/providers/local/components/command-folder-create.d.mts.map +1 -0
- package/dist/providers/local/components/command-folder-create.mjs +55 -0
- package/dist/providers/local/components/command-folder-create.mjs.map +1 -0
- package/dist/providers/local/components/command-folder-update.d.mts +18 -0
- package/dist/providers/local/components/command-folder-update.d.mts.map +1 -0
- package/dist/providers/local/components/command-folder-update.mjs +48 -0
- package/dist/providers/local/components/command-folder-update.mjs.map +1 -0
- package/dist/providers/local/components/content.client.d.mts +7 -0
- package/dist/providers/local/components/content.client.d.mts.map +1 -0
- package/dist/providers/local/components/content.client.mjs +8 -0
- package/dist/providers/local/components/content.client.mjs.map +1 -0
- package/dist/providers/local/components/navigation.client.d.mts +16 -0
- package/dist/providers/local/components/navigation.client.d.mts.map +1 -0
- package/dist/providers/local/components/navigation.client.mjs +18 -0
- package/dist/providers/local/components/navigation.client.mjs.map +1 -0
- package/dist/providers/local/components/provider.client.d.mts +40 -0
- package/dist/providers/local/components/provider.client.d.mts.map +1 -0
- package/dist/providers/local/components/provider.client.mjs +61 -0
- package/dist/providers/local/components/provider.client.mjs.map +1 -0
- package/dist/providers/local/components/upload-zone-context.client.d.mts +38 -0
- package/dist/providers/local/components/upload-zone-context.client.d.mts.map +1 -0
- package/dist/providers/local/components/upload-zone-context.client.mjs +23 -0
- package/dist/providers/local/components/upload-zone-context.client.mjs.map +1 -0
- package/dist/providers/local/components/upload-zone.client.d.mts +30 -0
- package/dist/providers/local/components/upload-zone.client.d.mts.map +1 -0
- package/dist/providers/local/components/upload-zone.client.mjs +147 -0
- package/dist/providers/local/components/upload-zone.client.mjs.map +1 -0
- package/dist/providers/local/components/view.client.d.mts +32 -0
- package/dist/providers/local/components/view.client.d.mts.map +1 -0
- package/dist/providers/local/components/view.client.mjs +91 -0
- package/dist/providers/local/components/view.client.mjs.map +1 -0
- package/dist/providers/local/config/columns-data.d.mts +8 -0
- package/dist/providers/local/config/columns-data.d.mts.map +1 -0
- package/dist/providers/local/config/columns-data.mjs +70 -0
- package/dist/providers/local/config/columns-data.mjs.map +1 -0
- package/dist/providers/local/config/filters.d.mts +26 -0
- package/dist/providers/local/config/filters.d.mts.map +1 -0
- package/dist/providers/local/config/filters.mjs +15 -0
- package/dist/providers/local/config/filters.mjs.map +1 -0
- package/dist/providers/local/lib/constants.d.mts +12 -0
- package/dist/providers/local/lib/constants.d.mts.map +1 -0
- package/dist/providers/local/lib/constants.mjs +29 -0
- package/dist/providers/local/lib/constants.mjs.map +1 -0
- package/dist/providers/local/lib/helpers.d.mts +45 -0
- package/dist/providers/local/lib/helpers.d.mts.map +1 -0
- package/dist/providers/local/lib/helpers.mjs +110 -0
- package/dist/providers/local/lib/helpers.mjs.map +1 -0
- package/dist/providers/local/lib/route-handler.server.d.mts +34 -0
- package/dist/providers/local/lib/route-handler.server.d.mts.map +1 -0
- package/dist/providers/local/lib/route-handler.server.mjs +114 -0
- package/dist/providers/local/lib/route-handler.server.mjs.map +1 -0
- package/dist/providers/local/lib/router.server.d.mts +41648 -0
- package/dist/providers/local/lib/router.server.d.mts.map +1 -0
- package/dist/providers/local/lib/router.server.mjs +52 -0
- package/dist/providers/local/lib/router.server.mjs.map +1 -0
- package/dist/providers/local/lib/schema.d.mts +1113 -0
- package/dist/providers/local/lib/schema.d.mts.map +1 -0
- package/dist/providers/local/lib/schema.mjs +71 -0
- package/dist/providers/local/lib/schema.mjs.map +1 -0
- package/dist/providers/local/lib/search-params.d.mts +14 -0
- package/dist/providers/local/lib/search-params.d.mts.map +1 -0
- package/dist/providers/local/lib/search-params.mjs +9 -0
- package/dist/providers/local/lib/search-params.mjs.map +1 -0
- package/dist/providers/local/lib/service.server.d.mts +489 -0
- package/dist/providers/local/lib/service.server.d.mts.map +1 -0
- package/dist/providers/local/lib/service.server.mjs +668 -0
- package/dist/providers/local/lib/service.server.mjs.map +1 -0
- package/dist/providers/local/lib/upload.client.d.mts +62 -0
- package/dist/providers/local/lib/upload.client.d.mts.map +1 -0
- package/dist/providers/local/lib/upload.client.mjs +100 -0
- package/dist/providers/local/lib/upload.client.mjs.map +1 -0
- package/dist/providers/local/lib/validators.d.mts +454 -0
- package/dist/providers/local/lib/validators.d.mts.map +1 -0
- package/dist/providers/local/lib/validators.mjs +96 -0
- package/dist/providers/local/lib/validators.mjs.map +1 -0
- package/dist/server.d.mts +2 -0
- package/dist/server.mjs +3 -0
- package/package.json +3 -2
- package/src/providers/local/lib/helpers.ts +0 -1
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
import { GOOGLE_DRIVE_FOLDER_MIME_TYPE, GOOGLE_DRIVE_NODE_FIELDS } from "./constants.mjs";
|
|
2
|
+
import { GoogleDriveNodeDTO } from "./dto.mjs";
|
|
3
|
+
import { escapeGoogleDriveQueryValue, isGoogleNotFound, toGoogleDriveReadable } from "./helpers.mjs";
|
|
4
|
+
import { ServerError } from "@tulip-systems/core/router/server";
|
|
5
|
+
import { google } from "googleapis";
|
|
6
|
+
|
|
7
|
+
//#region src/providers/google/lib/service.server.ts
|
|
8
|
+
const DEFAULT_PAGE_SIZE = 10;
|
|
9
|
+
const CHILDREN_PAGE_SIZE = 1e3;
|
|
10
|
+
/**
|
|
11
|
+
* Google Drive provider.
|
|
12
|
+
*
|
|
13
|
+
* A single service instance can operate on multiple Google shared drives. The
|
|
14
|
+
* shared Drive `namespace` is the Google `driveId`, so namespace is supplied by
|
|
15
|
+
* method inputs rather than by the constructor.
|
|
16
|
+
*/
|
|
17
|
+
var GoogleDrive = class {
|
|
18
|
+
client;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.client = google.drive({
|
|
21
|
+
auth: config.auth,
|
|
22
|
+
version: "v3"
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fetches raw Google Drive metadata for a file or folder.
|
|
27
|
+
*
|
|
28
|
+
* Google Drive uses the same `files.get` endpoint for files and folders. We
|
|
29
|
+
* request only `GOOGLE_DRIVE_NODE_FIELDS` so DTO mapping receives a predictable
|
|
30
|
+
* shape and we avoid over-fetching provider metadata.
|
|
31
|
+
*
|
|
32
|
+
* A Google 404 is translated to `null` to match the public `getNodeById`
|
|
33
|
+
* contract. All other errors are rethrown because they represent real failures
|
|
34
|
+
* such as insufficient permissions, invalid credentials, quota limits, or API
|
|
35
|
+
* outages.
|
|
36
|
+
*/
|
|
37
|
+
async #getFile(id) {
|
|
38
|
+
try {
|
|
39
|
+
return (await this.client.files.get({
|
|
40
|
+
fileId: id,
|
|
41
|
+
supportsAllDrives: true,
|
|
42
|
+
fields: GOOGLE_DRIVE_NODE_FIELDS
|
|
43
|
+
})).data;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (isGoogleNotFound(error)) return null;
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Loads a single Google Drive file/folder by its Google file id.
|
|
51
|
+
*
|
|
52
|
+
* Returns `null` when Google reports the file does not exist or is not visible to
|
|
53
|
+
* the authenticated account. Other Google API errors are allowed to bubble up so
|
|
54
|
+
* callers do not accidentally treat permission, quota, or transport failures as
|
|
55
|
+
* missing data.
|
|
56
|
+
*
|
|
57
|
+
* The returned Google metadata is normalized into the shared Drive node shape by
|
|
58
|
+
* `#toNode`, which also validates that Google returned the minimum metadata we
|
|
59
|
+
* need to identify the node and its namespace.
|
|
60
|
+
*/
|
|
61
|
+
async getNodeById(id) {
|
|
62
|
+
const file = await this.#getFile(id);
|
|
63
|
+
return file ? this.#toNode(file) : null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Converts a table sort field into a Google Drive `orderBy` value.
|
|
67
|
+
*
|
|
68
|
+
* Google Drive only accepts a fixed set of field names in `files.list.orderBy`.
|
|
69
|
+
* The table layer uses provider-neutral names such as `createdAt` and
|
|
70
|
+
* `updatedAt`, so this method maps those aliases to Google's native field names.
|
|
71
|
+
*
|
|
72
|
+
* Unknown sort fields intentionally fall back to `modifiedTime desc` instead of
|
|
73
|
+
* being passed through, because Google returns a 400 for unsupported order fields.
|
|
74
|
+
*/
|
|
75
|
+
#orderBy(input) {
|
|
76
|
+
const direction = input.order === "asc" ? "" : " desc";
|
|
77
|
+
switch (input.sort) {
|
|
78
|
+
case "name": return `name${direction}`;
|
|
79
|
+
case "createdAt":
|
|
80
|
+
case "createdTime": return `createdTime${direction}`;
|
|
81
|
+
case "updatedAt":
|
|
82
|
+
case "modifiedTime": return `modifiedTime${direction}`;
|
|
83
|
+
default: return "modifiedTime desc";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Scopes results to a parent folder.
|
|
88
|
+
*
|
|
89
|
+
* Google Drive represents folder membership as `'<folderId>' in parents`.
|
|
90
|
+
* A `null` parent maps to the provider namespace/root drive id.
|
|
91
|
+
* An `undefined` parent means no parent filter should be applied.
|
|
92
|
+
*/
|
|
93
|
+
#parentQuery(input) {
|
|
94
|
+
if (input.parentId === void 0) return null;
|
|
95
|
+
return `'${escapeGoogleDriveQueryValue(input.parentId ?? input.namespace)}' in parents`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Filters results to a specific set of Google file ids.
|
|
99
|
+
*/
|
|
100
|
+
#idQuery(nodeIds) {
|
|
101
|
+
if (!nodeIds?.length) return null;
|
|
102
|
+
return `(${nodeIds.map((id) => `id = '${escapeGoogleDriveQueryValue(id)}'`).join(" or ")})`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Converts provider-neutral node types to Google Drive MIME-type filters.
|
|
106
|
+
*
|
|
107
|
+
* Google Drive folders are identified by their folder MIME type. Every other
|
|
108
|
+
* MIME type is treated as a file.
|
|
109
|
+
*/
|
|
110
|
+
#typeQuery(types) {
|
|
111
|
+
if (types?.length !== 1) return null;
|
|
112
|
+
return types[0] === "folder" ? `mimeType = '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}'` : `mimeType != '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}'`;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Filters files by MIME type.
|
|
116
|
+
*
|
|
117
|
+
* This only applies to files. Folder filtering should use `#typeQuery`.
|
|
118
|
+
*/
|
|
119
|
+
#contentTypeQuery(contentTypes) {
|
|
120
|
+
if (!contentTypes?.length) return null;
|
|
121
|
+
return `(${contentTypes.map((contentType) => `mimeType = '${escapeGoogleDriveQueryValue(contentType)}'`).join(" or ")})`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Filters archived/active nodes.
|
|
125
|
+
*
|
|
126
|
+
* Google Drive calls this state `trashed`; the shared Drive contract calls it
|
|
127
|
+
* archived/soft-deleted. By default we return active nodes only.
|
|
128
|
+
*/
|
|
129
|
+
#archiveQuery(isArchived) {
|
|
130
|
+
return `trashed = ${isArchived === true ? "true" : "false"}`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Applies a name search.
|
|
134
|
+
*
|
|
135
|
+
* Google Drive supports `name contains '<value>'`, which is a substring match.
|
|
136
|
+
*/
|
|
137
|
+
#searchQuery(search) {
|
|
138
|
+
if (!search) return null;
|
|
139
|
+
return `name contains '${escapeGoogleDriveQueryValue(search)}'`;
|
|
140
|
+
}
|
|
141
|
+
#buildQuery(input) {
|
|
142
|
+
return [
|
|
143
|
+
this.#parentQuery(input),
|
|
144
|
+
this.#idQuery(input.nodeIds),
|
|
145
|
+
this.#typeQuery(input.types),
|
|
146
|
+
this.#contentTypeQuery(input.contentTypes),
|
|
147
|
+
this.#archiveQuery(input.isArchived),
|
|
148
|
+
this.#searchQuery(input.search)
|
|
149
|
+
].filter((clause) => typeof clause === "string" && clause.length > 0).join(" and ");
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Lists the direct children of a Google Drive folder.
|
|
153
|
+
*
|
|
154
|
+
* Google Drive stores both files and folders behind the `files.list` endpoint.
|
|
155
|
+
* This helper translates our drive query input into a Google Drive search query
|
|
156
|
+
* and returns only the immediate children for the requested parent.
|
|
157
|
+
*
|
|
158
|
+
* The result is paginated by Google, so we keep requesting pages until all
|
|
159
|
+
* matching children are loaded. Recursive traversal is intentionally not handled
|
|
160
|
+
* here; callers that need full subtrees should use `getNodeChildren`.
|
|
161
|
+
*/
|
|
162
|
+
async #listChildren(input) {
|
|
163
|
+
const filters = input?.filters;
|
|
164
|
+
const files = [];
|
|
165
|
+
let pageToken;
|
|
166
|
+
do {
|
|
167
|
+
const result = await this.client.files.list({
|
|
168
|
+
pageToken,
|
|
169
|
+
pageSize: CHILDREN_PAGE_SIZE,
|
|
170
|
+
corpora: "drive",
|
|
171
|
+
driveId: input.filters.namespace,
|
|
172
|
+
includeItemsFromAllDrives: true,
|
|
173
|
+
supportsAllDrives: true,
|
|
174
|
+
q: this.#buildQuery({
|
|
175
|
+
namespace: input.filters.namespace,
|
|
176
|
+
parentId: input.filters.parentId,
|
|
177
|
+
nodeIds: filters?.nodeIds,
|
|
178
|
+
types: filters?.types,
|
|
179
|
+
contentTypes: filters?.contentTypes,
|
|
180
|
+
isArchived: filters?.isArchived,
|
|
181
|
+
search: input?.search
|
|
182
|
+
}),
|
|
183
|
+
orderBy: this.#orderBy(input),
|
|
184
|
+
fields: `nextPageToken, files(${GOOGLE_DRIVE_NODE_FIELDS})`
|
|
185
|
+
});
|
|
186
|
+
files.push(...result.data.files ?? []);
|
|
187
|
+
pageToken = result.data.nextPageToken ?? void 0;
|
|
188
|
+
} while (pageToken);
|
|
189
|
+
return files.map((file) => this.#toNode(file));
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Loads a node and its direct children within a namespace.
|
|
193
|
+
*
|
|
194
|
+
* Google Drive does not enforce our provider-neutral `namespace` at `files.get`
|
|
195
|
+
* time, so the node is fetched by id first and then checked against the requested
|
|
196
|
+
* namespace/shared drive. A missing node or namespace mismatch returns
|
|
197
|
+
* `null`, matching the shared Drive reader contract.
|
|
198
|
+
*
|
|
199
|
+
* Only direct children are loaded. Recursive traversal is handled separately by
|
|
200
|
+
* `getNodeChildren`.
|
|
201
|
+
*/
|
|
202
|
+
async getNodeWithChildren(input) {
|
|
203
|
+
const node = await this.getNodeById(input.id);
|
|
204
|
+
if (!node || node.namespace !== input.namespace) return null;
|
|
205
|
+
const children = await this.#listChildren({ filters: {
|
|
206
|
+
parentId: node.id,
|
|
207
|
+
namespace: node.namespace,
|
|
208
|
+
isArchived: false
|
|
209
|
+
} });
|
|
210
|
+
return {
|
|
211
|
+
...node,
|
|
212
|
+
children
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Lists the direct children of a parent folder.
|
|
217
|
+
*
|
|
218
|
+
* This is the Google Drive implementation of the shared Drive reader method. It
|
|
219
|
+
* delegates to `#listChildren`, which translates the provider-neutral filters,
|
|
220
|
+
* search, and sorting options into a Google Drive `files.list` query.
|
|
221
|
+
*
|
|
222
|
+
* Only immediate children are returned. Recursive traversal is handled by
|
|
223
|
+
* `getNodeChildren`.
|
|
224
|
+
*/
|
|
225
|
+
async getNodesByParentId(input) {
|
|
226
|
+
return this.#listChildren(input);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Resolves the Google Drive page token for a numeric table cursor.
|
|
230
|
+
*
|
|
231
|
+
* The shared table API uses numeric cursors (`0`, `1`, `2`, ...), while Google
|
|
232
|
+
* Drive pagination uses opaque `nextPageToken` values. To bridge that mismatch,
|
|
233
|
+
* we walk Google pages until we reach the requested table page.
|
|
234
|
+
*
|
|
235
|
+
* The intermediate requests only fetch file ids because their content is thrown
|
|
236
|
+
* away; we only need each response's `nextPageToken`.
|
|
237
|
+
*
|
|
238
|
+
* Returns `exhausted: true` when Google runs out of pages before the requested
|
|
239
|
+
* cursor is reached.
|
|
240
|
+
*/
|
|
241
|
+
async #pageCursorAt(input, remainingPages, pageToken) {
|
|
242
|
+
if (remainingPages === 0) return {
|
|
243
|
+
pageToken,
|
|
244
|
+
exhausted: false
|
|
245
|
+
};
|
|
246
|
+
const nextPageToken = (await this.client.files.list({
|
|
247
|
+
pageSize: input.limit,
|
|
248
|
+
pageToken,
|
|
249
|
+
corpora: "drive",
|
|
250
|
+
driveId: input.namespace,
|
|
251
|
+
includeItemsFromAllDrives: true,
|
|
252
|
+
supportsAllDrives: true,
|
|
253
|
+
q: input.q,
|
|
254
|
+
orderBy: input.orderBy,
|
|
255
|
+
fields: "nextPageToken, files(id)"
|
|
256
|
+
})).data.nextPageToken ?? void 0;
|
|
257
|
+
if (!nextPageToken) return { exhausted: true };
|
|
258
|
+
return this.#pageCursorAt(input, remainingPages - 1, nextPageToken);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Lists Google Drive nodes using the shared table pagination shape.
|
|
262
|
+
*
|
|
263
|
+
* The shared table API uses numeric cursors, but Google Drive uses opaque page
|
|
264
|
+
* tokens. Before fetching the requested page, `#pageCursorAt` advances through
|
|
265
|
+
* Google pages until it resolves the page token for the requested numeric cursor.
|
|
266
|
+
*
|
|
267
|
+
* Google Drive does not return a total row count for `files.list`, so pagination
|
|
268
|
+
* totals are estimated from the current cursor, returned page size, and whether
|
|
269
|
+
* Google reports another page. The estimate is only meant to support table/infinite
|
|
270
|
+
* pagination controls; it should not be treated as an exact item count.
|
|
271
|
+
*/
|
|
272
|
+
async #listNodes(input) {
|
|
273
|
+
const limit = input.limit ?? DEFAULT_PAGE_SIZE;
|
|
274
|
+
const cursor = input.cursor ?? 0;
|
|
275
|
+
const pageCursor = await this.#pageCursorAt({
|
|
276
|
+
...input,
|
|
277
|
+
limit
|
|
278
|
+
}, cursor);
|
|
279
|
+
if (pageCursor.exhausted) return {
|
|
280
|
+
data: [],
|
|
281
|
+
pagination: {
|
|
282
|
+
total: cursor * limit,
|
|
283
|
+
totalPages: Math.max(1, cursor),
|
|
284
|
+
limit,
|
|
285
|
+
hasNextPage: false,
|
|
286
|
+
hasPreviousPage: cursor > 0,
|
|
287
|
+
cursor,
|
|
288
|
+
nextCursor: null,
|
|
289
|
+
previousCursor: cursor > 0 ? cursor - 1 : null
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const result = await this.client.files.list({
|
|
293
|
+
pageSize: limit,
|
|
294
|
+
pageToken: pageCursor.pageToken,
|
|
295
|
+
corpora: "drive",
|
|
296
|
+
driveId: input.namespace,
|
|
297
|
+
includeItemsFromAllDrives: true,
|
|
298
|
+
supportsAllDrives: true,
|
|
299
|
+
q: input.q,
|
|
300
|
+
orderBy: input.orderBy,
|
|
301
|
+
fields: `nextPageToken, files(${GOOGLE_DRIVE_NODE_FIELDS})`
|
|
302
|
+
});
|
|
303
|
+
const data = result.data.files?.map((file) => this.#toNode(file)) ?? [];
|
|
304
|
+
const hasNextPage = result.data.nextPageToken != null;
|
|
305
|
+
const total = cursor * limit + data.length + (hasNextPage ? limit : 0);
|
|
306
|
+
return {
|
|
307
|
+
data,
|
|
308
|
+
pagination: {
|
|
309
|
+
total,
|
|
310
|
+
totalPages: Math.max(1, Math.ceil(total / limit)),
|
|
311
|
+
limit,
|
|
312
|
+
hasNextPage,
|
|
313
|
+
hasPreviousPage: cursor > 0,
|
|
314
|
+
cursor,
|
|
315
|
+
nextCursor: hasNextPage ? cursor + 1 : null,
|
|
316
|
+
previousCursor: cursor > 0 ? cursor - 1 : null
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Lists one page of Google Drive nodes for a tree/folder view.
|
|
322
|
+
*
|
|
323
|
+
* The public input uses the shared table-query shape: pagination, sorting,
|
|
324
|
+
* search, and drive filters. This method translates that input into the internal
|
|
325
|
+
* Google Drive list request shape used by `#listNodes`.
|
|
326
|
+
*
|
|
327
|
+
* Google Drive does not expose a native tree endpoint. This returns only the
|
|
328
|
+
* direct children for the requested `parentId`; recursive traversal is handled
|
|
329
|
+
* by `getNodeChildren`.
|
|
330
|
+
*/
|
|
331
|
+
async listTree(input) {
|
|
332
|
+
return this.#listNodes({
|
|
333
|
+
namespace: input.filters.namespace,
|
|
334
|
+
limit: input.limit,
|
|
335
|
+
cursor: input.cursor,
|
|
336
|
+
orderBy: this.#orderBy(input),
|
|
337
|
+
q: this.#buildQuery({
|
|
338
|
+
namespace: input.filters.namespace,
|
|
339
|
+
parentId: input.filters.parentId,
|
|
340
|
+
nodeIds: input.filters.nodeIds,
|
|
341
|
+
types: input.filters.types,
|
|
342
|
+
contentTypes: input.filters.contentTypes,
|
|
343
|
+
isArchived: input.filters.isArchived,
|
|
344
|
+
search: input.search
|
|
345
|
+
})
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Walks from a Google Drive node up through its parent chain.
|
|
350
|
+
*
|
|
351
|
+
* Google Drive only exposes direct parents, so breadcrumbs must be resolved one
|
|
352
|
+
* node at a time. The `seen` set prevents accidental infinite loops if Google
|
|
353
|
+
* ever returns cyclic or inconsistent parent metadata.
|
|
354
|
+
*
|
|
355
|
+
* `seen` is treated immutably so each recursive call receives its own traversal
|
|
356
|
+
* state instead of mutating caller-owned state.
|
|
357
|
+
*/
|
|
358
|
+
async #walkParents(currentId, namespace, depth = 0, seen = /* @__PURE__ */ new Set()) {
|
|
359
|
+
if (!currentId || seen.has(currentId)) return [];
|
|
360
|
+
const node = await this.getNodeById(currentId);
|
|
361
|
+
if (!node || node.namespace !== namespace) return [];
|
|
362
|
+
const nextSeen = new Set([...seen, node.id]);
|
|
363
|
+
return [{
|
|
364
|
+
id: node.id,
|
|
365
|
+
name: node.name,
|
|
366
|
+
parentId: node.parentId,
|
|
367
|
+
depth
|
|
368
|
+
}, ...await this.#walkParents(node.parentId, namespace, depth + 1, nextSeen)];
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Resolves the breadcrumb parent chain for a Google Drive node.
|
|
372
|
+
*
|
|
373
|
+
* A `null` id represents the namespace root and therefore has no parent chain.
|
|
374
|
+
* The returned list is ordered from the root-most ancestor to the requested
|
|
375
|
+
* node, matching breadcrumb display order.
|
|
376
|
+
*/
|
|
377
|
+
async getFolderParents(input) {
|
|
378
|
+
if (!input.id) return [];
|
|
379
|
+
return (await this.#walkParents(input.id, input.namespace)).toSorted((a, b) => Number(b.depth) - Number(a.depth));
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Walks a Google Drive subtree breadth-first.
|
|
383
|
+
*
|
|
384
|
+
* Google Drive has no recursive children endpoint, so folder descendants must be
|
|
385
|
+
* loaded one folder at a time. The returned rows include the starting nodes and
|
|
386
|
+
* all descendants with their relative depth.
|
|
387
|
+
*/
|
|
388
|
+
async #walkChildren(queue, seen = /* @__PURE__ */ new Set()) {
|
|
389
|
+
const [current, ...rest] = queue;
|
|
390
|
+
if (!current) return [];
|
|
391
|
+
if (seen.has(current.id)) return this.#walkChildren(rest, seen);
|
|
392
|
+
const node = await this.getNodeById(current.id);
|
|
393
|
+
if (!node) return this.#walkChildren(rest, seen);
|
|
394
|
+
const nextSeen = new Set([...seen, node.id]);
|
|
395
|
+
const currentChild = {
|
|
396
|
+
id: node.id,
|
|
397
|
+
type: node.type,
|
|
398
|
+
parentId: node.parentId,
|
|
399
|
+
depth: current.depth
|
|
400
|
+
};
|
|
401
|
+
if (node.type !== "folder") return [currentChild, ...await this.#walkChildren(rest, nextSeen)];
|
|
402
|
+
const children = await this.#listChildren({ filters: {
|
|
403
|
+
namespace: node.namespace,
|
|
404
|
+
parentId: node.id,
|
|
405
|
+
isArchived: false
|
|
406
|
+
} });
|
|
407
|
+
const nextQueue = [...rest, ...children.map((child) => ({
|
|
408
|
+
id: child.id,
|
|
409
|
+
depth: current.depth + 1
|
|
410
|
+
}))];
|
|
411
|
+
return [currentChild, ...await this.#walkChildren(nextQueue, nextSeen)];
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Resolves the subtree for one or more Google Drive nodes.
|
|
415
|
+
*/
|
|
416
|
+
async getNodeChildren(ids) {
|
|
417
|
+
const uniqueIds = Array.from(new Set(ids));
|
|
418
|
+
return this.#walkChildren(uniqueIds.map((id) => ({
|
|
419
|
+
id,
|
|
420
|
+
depth: 0
|
|
421
|
+
})));
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Creates a Google Drive folder in the requested parent.
|
|
425
|
+
*
|
|
426
|
+
* Google Drive represents folders as files with the special folder MIME type.
|
|
427
|
+
* Creating a folder therefore uses the same `files.create` endpoint as file
|
|
428
|
+
* uploads, but without a media body.
|
|
429
|
+
*
|
|
430
|
+
* A `null` or missing `parentId` means "create in the namespace root", where the
|
|
431
|
+
* namespace is the shared drive id/root folder used by this provider.
|
|
432
|
+
*/
|
|
433
|
+
async createFolder(input) {
|
|
434
|
+
const result = await this.client.files.create({
|
|
435
|
+
requestBody: {
|
|
436
|
+
name: input.name,
|
|
437
|
+
parents: [input.parentId ?? input.namespace],
|
|
438
|
+
mimeType: GOOGLE_DRIVE_FOLDER_MIME_TYPE
|
|
439
|
+
},
|
|
440
|
+
supportsAllDrives: true,
|
|
441
|
+
fields: GOOGLE_DRIVE_NODE_FIELDS
|
|
442
|
+
});
|
|
443
|
+
return this.#toNode(result.data);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Uploads a file body to Google Drive and returns the created node.
|
|
447
|
+
*
|
|
448
|
+
* Google Drive creates files through `files.create` with two parts:
|
|
449
|
+
* metadata in `requestBody` and the actual file content in `media.body`.
|
|
450
|
+
*
|
|
451
|
+
* A `null` or missing `parentId` means "upload to the namespace root", where the
|
|
452
|
+
* namespace is the Google shared drive/root id used by this provider.
|
|
453
|
+
*
|
|
454
|
+
* The input body can be a provider-neutral object body type. `toReadable`
|
|
455
|
+
* normalizes it into a Node.js readable stream because the Google API client
|
|
456
|
+
* expects stream-compatible media bodies.
|
|
457
|
+
*/
|
|
458
|
+
async uploadFile(input) {
|
|
459
|
+
const result = await this.client.files.create({
|
|
460
|
+
requestBody: {
|
|
461
|
+
name: input.name,
|
|
462
|
+
parents: [input.parentId ?? input.namespace],
|
|
463
|
+
mimeType: input.contentType ?? void 0
|
|
464
|
+
},
|
|
465
|
+
media: {
|
|
466
|
+
mimeType: input.contentType ?? void 0,
|
|
467
|
+
body: toGoogleDriveReadable(input.body)
|
|
468
|
+
},
|
|
469
|
+
supportsAllDrives: true,
|
|
470
|
+
fields: GOOGLE_DRIVE_NODE_FIELDS
|
|
471
|
+
});
|
|
472
|
+
return this.#toNode(result.data);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Updates mutable Google Drive node metadata.
|
|
476
|
+
*
|
|
477
|
+
* Google Drive uses `files.update` for metadata changes such as renaming and
|
|
478
|
+
* moving a file/folder to or from the trash. The shared Drive API calls this
|
|
479
|
+
* soft-delete state "archived"; Google Drive exposes it as `trashed`, so
|
|
480
|
+
* `data.isArchived` is translated directly to Google's `trashed` field.
|
|
481
|
+
*
|
|
482
|
+
* Parent changes are intentionally not handled here. Moving a node requires
|
|
483
|
+
* Google Drive's `addParents`/`removeParents` API and should go through
|
|
484
|
+
* `moveNode` instead.
|
|
485
|
+
*/
|
|
486
|
+
async updateNode(id, data) {
|
|
487
|
+
const result = await this.client.files.update({
|
|
488
|
+
fileId: id,
|
|
489
|
+
requestBody: {
|
|
490
|
+
name: data.name,
|
|
491
|
+
trashed: data.isArchived
|
|
492
|
+
},
|
|
493
|
+
supportsAllDrives: true,
|
|
494
|
+
fields: GOOGLE_DRIVE_NODE_FIELDS
|
|
495
|
+
});
|
|
496
|
+
return this.#toNode(result.data);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Moves a Google Drive node to another parent folder.
|
|
500
|
+
*
|
|
501
|
+
* Google Drive does not allow parent changes through the normal metadata
|
|
502
|
+
* `requestBody`. Moving requires `addParents` and `removeParents` on
|
|
503
|
+
* `files.update`, so this operation is intentionally separate from `updateNode`.
|
|
504
|
+
*
|
|
505
|
+
* A `null` parent means "move to the namespace root". For this provider, the
|
|
506
|
+
* namespace is the Google shared drive/root id.
|
|
507
|
+
*/
|
|
508
|
+
async moveNode(input) {
|
|
509
|
+
const node = await this.getNodeById(input.id);
|
|
510
|
+
if (!node) throw new ServerError("NOT_FOUND", { message: "Node not found" });
|
|
511
|
+
if (node.readonly) throw new ServerError("BAD_REQUEST", { message: "Node is readonly and cannot be changed" });
|
|
512
|
+
if (input.parentId === input.id) throw new ServerError("BAD_REQUEST", { message: "Node cannot be moved into itself" });
|
|
513
|
+
if (input.parentId) {
|
|
514
|
+
if ((await this.getNodeChildren([input.id])).some((child) => child.id === input.parentId)) throw new ServerError("BAD_REQUEST", { message: "Node cannot be moved into one of its descendants" });
|
|
515
|
+
}
|
|
516
|
+
const result = await this.client.files.update({
|
|
517
|
+
fileId: input.id,
|
|
518
|
+
addParents: input.parentId ?? node.namespace,
|
|
519
|
+
removeParents: node.googleParents.join(",") || void 0,
|
|
520
|
+
supportsAllDrives: true,
|
|
521
|
+
fields: GOOGLE_DRIVE_NODE_FIELDS
|
|
522
|
+
});
|
|
523
|
+
return this.#toNode(result.data);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Updates the Google Drive trash state for multiple nodes.
|
|
527
|
+
*
|
|
528
|
+
* Google Drive exposes archive/restore behavior through the `trashed` metadata
|
|
529
|
+
* field on `files.update`. Each node requires a separate API request, so updates
|
|
530
|
+
* are executed in parallel after duplicate ids are removed.
|
|
531
|
+
*/
|
|
532
|
+
async #setTrashed(ids, trashed) {
|
|
533
|
+
const uniqueIds = [...new Set(ids)];
|
|
534
|
+
return (await Promise.all(uniqueIds.map((id) => this.client.files.update({
|
|
535
|
+
fileId: id,
|
|
536
|
+
requestBody: { trashed },
|
|
537
|
+
supportsAllDrives: true,
|
|
538
|
+
fields: GOOGLE_DRIVE_NODE_FIELDS
|
|
539
|
+
})))).map((result) => this.#toNode(result.data));
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Moves Google Drive nodes to trash.
|
|
543
|
+
*
|
|
544
|
+
* The shared Drive API calls this "archive", while Google Drive calls the same
|
|
545
|
+
* state `trashed`. This is a soft-delete operation; nodes can be restored with
|
|
546
|
+
* `restoreNodes`.
|
|
547
|
+
*/
|
|
548
|
+
async archiveNodes(ids) {
|
|
549
|
+
return this.#setTrashed(ids, true);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Restores Google Drive nodes from trash.
|
|
553
|
+
*
|
|
554
|
+
* This reverses `archiveNodes` by setting Google Drive's `trashed` flag back to
|
|
555
|
+
* `false`.
|
|
556
|
+
*/
|
|
557
|
+
async restoreNodes(ids) {
|
|
558
|
+
return this.#setTrashed(ids, false);
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Permanently deletes a Google Drive node.
|
|
562
|
+
*
|
|
563
|
+
* This uses Google Drive `files.delete`, which removes the file/folder rather
|
|
564
|
+
* than moving it to trash. Soft deletion/trashing should go through the archive
|
|
565
|
+
* method instead.
|
|
566
|
+
*/
|
|
567
|
+
async deleteNode(id) {
|
|
568
|
+
await this.client.files.delete({
|
|
569
|
+
fileId: id,
|
|
570
|
+
supportsAllDrives: true
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Permanently deletes multiple Google Drive nodes.
|
|
575
|
+
*
|
|
576
|
+
* Duplicate ids are removed before issuing delete requests so the same Google
|
|
577
|
+
* file is not deleted more than once. Deletions are executed in parallel because
|
|
578
|
+
* Google Drive exposes deletion as one request per file id.
|
|
579
|
+
*
|
|
580
|
+
* This is a hard delete. Soft deletion/trashing should go through the archive
|
|
581
|
+
* method instead.
|
|
582
|
+
*/
|
|
583
|
+
async deleteNodes(ids) {
|
|
584
|
+
const uniqueIds = [...new Set(ids)];
|
|
585
|
+
await Promise.all(uniqueIds.map((id) => this.client.files.delete({
|
|
586
|
+
fileId: id,
|
|
587
|
+
supportsAllDrives: true
|
|
588
|
+
})));
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Converts raw Google Drive file metadata into the provider's normalized node DTO.
|
|
592
|
+
*
|
|
593
|
+
* Google Drive API responses are loosely typed and may omit fields that our
|
|
594
|
+
* drive abstraction requires, such as `id` or a namespace/root identifier. Those
|
|
595
|
+
* cases indicate an invalid provider response rather than a caller error, so
|
|
596
|
+
* they are normalized to an internal server error.
|
|
597
|
+
*
|
|
598
|
+
* Any unexpected mapping error is rethrown so it can be handled by the normal
|
|
599
|
+
* server error boundary/logging path.
|
|
600
|
+
*/
|
|
601
|
+
#toNode(file) {
|
|
602
|
+
try {
|
|
603
|
+
return GoogleDriveNodeDTO.create({ file });
|
|
604
|
+
} catch (error) {
|
|
605
|
+
if (error instanceof Error && ["Google Drive file has no id", "Google Drive file has no namespace"].includes(error.message)) throw new ServerError("INTERNAL_SERVER_ERROR", { message: error.message });
|
|
606
|
+
throw error;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
//#endregion
|
|
612
|
+
export { GoogleDrive };
|
|
613
|
+
//# sourceMappingURL=service.server.mjs.map
|