@braidhq/source-loader-gdrive 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mroops0111
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,80 @@
1
+ import type { IngestReport, SourceLoader, SourceLoaderContext, SyncReport } from '@braidhq/core';
2
+ import type { AbsolutePath, LoaderKind, PluginId } from '@braidhq/schema';
3
+ import { z } from 'zod';
4
+ import { type FetchFn } from './driveClient.js';
5
+ export declare const GoogleDriveLoaderConfig: z.ZodObject<{
6
+ /**
7
+ * Drive folder id (the long alphanumeric in the URL). Reject the alias
8
+ * `root` outright: it expands to the user's entire My Drive and would
9
+ * mirror every file they own, which is almost never what someone wants
10
+ * from an "intent docs" source and easily costs gigabytes + minutes.
11
+ * Make people pick a specific subfolder.
12
+ */
13
+ folderId: z.ZodEffects<z.ZodString, string, string>;
14
+ /**
15
+ * Whether to follow subfolders recursively. Default true. Disable for
16
+ * flat folder mirrors where nested folders should be skipped.
17
+ */
18
+ recursive: z.ZodDefault<z.ZodBoolean>;
19
+ }, "strip", z.ZodTypeAny, {
20
+ folderId: string;
21
+ recursive: boolean;
22
+ }, {
23
+ folderId: string;
24
+ recursive?: boolean | undefined;
25
+ }>;
26
+ export type GoogleDriveLoaderConfig = z.infer<typeof GoogleDriveLoaderConfig>;
27
+ export interface GoogleDriveLoaderDeps {
28
+ /**
29
+ * Resolve a fresh access token for a given `(workspaceId, sourceId)`.
30
+ * The composition root implements this against its OAuth refresh-token
31
+ * store; tests can return a static token regardless of context.
32
+ */
33
+ resolveAccessToken: (context: SourceLoaderContext) => Promise<string>;
34
+ /** Inject for tests. Real callers use globalThis.fetch. */
35
+ fetchFn?: FetchFn;
36
+ }
37
+ /**
38
+ * Google Drive source loader. Walks a Drive folder, exports Google-native
39
+ * docs to text/markdown/csv/pdf depending on type, downloads binaries
40
+ * (images, PDFs) as-is, and mirrors the folder layout under `destination/`.
41
+ *
42
+ * Loader owns destination contents: it `rm -rf`'s `destination` on each
43
+ * `ingest` so previously-fetched files don't linger. `sync` re-walks and
44
+ * compares modifiedTime per file; reports `changed = true` if any file
45
+ * was added / updated / removed.
46
+ */
47
+ export declare class GoogleDriveLoader implements SourceLoader {
48
+ private readonly deps;
49
+ readonly id: PluginId;
50
+ readonly type: "source-loader";
51
+ readonly kind: LoaderKind;
52
+ readonly configSchema: z.ZodObject<{
53
+ /**
54
+ * Drive folder id (the long alphanumeric in the URL). Reject the alias
55
+ * `root` outright: it expands to the user's entire My Drive and would
56
+ * mirror every file they own, which is almost never what someone wants
57
+ * from an "intent docs" source and easily costs gigabytes + minutes.
58
+ * Make people pick a specific subfolder.
59
+ */
60
+ folderId: z.ZodEffects<z.ZodString, string, string>;
61
+ /**
62
+ * Whether to follow subfolders recursively. Default true. Disable for
63
+ * flat folder mirrors where nested folders should be skipped.
64
+ */
65
+ recursive: z.ZodDefault<z.ZodBoolean>;
66
+ }, "strip", z.ZodTypeAny, {
67
+ folderId: string;
68
+ recursive: boolean;
69
+ }, {
70
+ folderId: string;
71
+ recursive?: boolean | undefined;
72
+ }>;
73
+ constructor(deps: GoogleDriveLoaderDeps);
74
+ ingest(rawConfig: unknown, destination: AbsolutePath, context: SourceLoaderContext): Promise<IngestReport>;
75
+ sync(rawConfig: unknown, destination: AbsolutePath, context: SourceLoaderContext): Promise<SyncReport>;
76
+ private client;
77
+ private walk;
78
+ private writeOneFile;
79
+ }
80
+ //# sourceMappingURL=GoogleDriveLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleDriveLoader.d.ts","sourceRoot":"","sources":["../src/GoogleDriveLoader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAChG,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAGzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAuC,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAiBpF,eAAO,MAAM,uBAAuB;IAClC;;;;;;OAMG;;IAMH;;;OAGG;;;;;;;;EAEH,CAAA;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAE7E,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACrE,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;GASG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IAMxC,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,QAAQ,CAAC,EAAE,EAA6B,QAAQ,CAAA;IAChD,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAS;IACxC,QAAQ,CAAC,IAAI,EAAe,UAAU,CAAA;IACtC,QAAQ,CAAC,YAAY;QA7CrB;;;;;;WAMG;;QAMH;;;WAGG;;;;;;;;OA8B4C;gBAElB,IAAI,EAAE,qBAAqB;IAElD,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC;IAa1G,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC;YAa9F,MAAM;YAKN,IAAI;YAyBJ,YAAY;CAoB3B"}
@@ -0,0 +1,125 @@
1
+ import { mkdir, rm, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { DriveClient } from './driveClient.js';
5
+ const FOLDER_MIME = 'application/vnd.google-apps.folder';
6
+ /**
7
+ * Map of Google-native mime types to (Drive export format, filename
8
+ * extension we write to disk). Skip everything else: only Drive
9
+ * "applications" need exporting. Regular binaries (images, PDFs, etc.)
10
+ * go through `downloadFile`.
11
+ */
12
+ const GOOGLE_NATIVE_EXPORT = {
13
+ 'application/vnd.google-apps.document': { mimeType: 'text/markdown', extension: '.md' },
14
+ 'application/vnd.google-apps.spreadsheet': { mimeType: 'text/csv', extension: '.csv' },
15
+ 'application/vnd.google-apps.presentation': { mimeType: 'application/pdf', extension: '.pdf' },
16
+ 'application/vnd.google-apps.drawing': { mimeType: 'image/png', extension: '.png' },
17
+ };
18
+ export const GoogleDriveLoaderConfig = z.object({
19
+ /**
20
+ * Drive folder id (the long alphanumeric in the URL). Reject the alias
21
+ * `root` outright: it expands to the user's entire My Drive and would
22
+ * mirror every file they own, which is almost never what someone wants
23
+ * from an "intent docs" source and easily costs gigabytes + minutes.
24
+ * Make people pick a specific subfolder.
25
+ */
26
+ folderId: z.string()
27
+ .min(1)
28
+ .refine(value => value !== 'root', {
29
+ message: 'folderId "root" refers to the entire My Drive and is rejected. Create a dedicated subfolder and use its id instead.',
30
+ }),
31
+ /**
32
+ * Whether to follow subfolders recursively. Default true. Disable for
33
+ * flat folder mirrors where nested folders should be skipped.
34
+ */
35
+ recursive: z.boolean().default(true),
36
+ });
37
+ /**
38
+ * Google Drive source loader. Walks a Drive folder, exports Google-native
39
+ * docs to text/markdown/csv/pdf depending on type, downloads binaries
40
+ * (images, PDFs) as-is, and mirrors the folder layout under `destination/`.
41
+ *
42
+ * Loader owns destination contents: it `rm -rf`'s `destination` on each
43
+ * `ingest` so previously-fetched files don't linger. `sync` re-walks and
44
+ * compares modifiedTime per file; reports `changed = true` if any file
45
+ * was added / updated / removed.
46
+ */
47
+ export class GoogleDriveLoader {
48
+ deps;
49
+ id = 'source-loader-gdrive';
50
+ type = 'source-loader';
51
+ kind = 'gdrive';
52
+ configSchema = GoogleDriveLoaderConfig;
53
+ constructor(deps) {
54
+ this.deps = deps;
55
+ }
56
+ async ingest(rawConfig, destination, context) {
57
+ const config = GoogleDriveLoaderConfig.parse(rawConfig);
58
+ await rm(destination, { recursive: true, force: true });
59
+ await mkdir(destination, { recursive: true });
60
+ const client = await this.client(context);
61
+ const result = await this.walk(client, config.folderId, destination, { recursive: config.recursive });
62
+ return {
63
+ localPath: destination,
64
+ metadata: { folderId: config.folderId, fileCount: result.fileCount },
65
+ fetchedAt: new Date().toISOString(),
66
+ };
67
+ }
68
+ async sync(rawConfig, destination, context) {
69
+ // v0.1: incremental sync needs per-file mtime cache. Until we add
70
+ // that, sync is "blow away + re-ingest". Always safe, just slow.
71
+ const config = GoogleDriveLoaderConfig.parse(rawConfig);
72
+ const before = Date.now();
73
+ await this.ingest(config, destination, context);
74
+ return {
75
+ changed: true,
76
+ metadata: { folderId: config.folderId, syncedAt: new Date(before).toISOString() },
77
+ fetchedAt: new Date().toISOString(),
78
+ };
79
+ }
80
+ async client(context) {
81
+ const token = await this.deps.resolveAccessToken(context);
82
+ return new DriveClient(token, this.deps.fetchFn);
83
+ }
84
+ async walk(client, folderId, destination, options) {
85
+ let fileCount = 0;
86
+ const children = await client.listChildren(folderId);
87
+ for (const child of children) {
88
+ if (child.mimeType === FOLDER_MIME) {
89
+ if (!options.recursive)
90
+ continue;
91
+ const subDest = join(destination, sanitiseName(child.name));
92
+ await mkdir(subDest, { recursive: true });
93
+ const nested = await this.walk(client, child.id, subDest, options);
94
+ fileCount += nested.fileCount;
95
+ continue;
96
+ }
97
+ const written = await this.writeOneFile(client, child, destination);
98
+ if (written)
99
+ fileCount++;
100
+ }
101
+ return { fileCount };
102
+ }
103
+ async writeOneFile(client, file, destination) {
104
+ const exportSpec = GOOGLE_NATIVE_EXPORT[file.mimeType];
105
+ if (exportSpec) {
106
+ const bytes = await client.exportDoc(file.id, exportSpec.mimeType);
107
+ const filename = `${sanitiseName(file.name)}${exportSpec.extension}`;
108
+ await writeFile(join(destination, filename), bytes);
109
+ return true;
110
+ }
111
+ // Skip native Drive types we don't have an export for (e.g. forms).
112
+ if (file.mimeType.startsWith('application/vnd.google-apps.'))
113
+ return false;
114
+ // Regular binary: download as-is, preserve the name.
115
+ const bytes = await client.downloadFile(file.id);
116
+ await writeFile(join(destination, sanitiseName(file.name)), bytes);
117
+ return true;
118
+ }
119
+ }
120
+ function sanitiseName(name) {
121
+ // Replace path separators and control chars; leave the rest alone so
122
+ // human-readable filenames survive ('My Doc — v3.md').
123
+ return name.replace(/[/\\]/g, '_');
124
+ }
125
+ //# sourceMappingURL=GoogleDriveLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleDriveLoader.js","sourceRoot":"","sources":["../src/GoogleDriveLoader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { Buffer } from 'node:buffer';
2
+ /**
3
+ * Tiny Google Drive v3 client wrapper. We only use a few endpoints and
4
+ * don't need the full `googleapis` package (which ships > 1 MB of types).
5
+ *
6
+ * Inject `fetchFn` for tests; real callers use globalThis.fetch.
7
+ */
8
+ export type FetchFn = typeof globalThis.fetch;
9
+ export interface DriveFileMetadata {
10
+ readonly id: string;
11
+ readonly name: string;
12
+ readonly mimeType: string;
13
+ readonly modifiedTime: string;
14
+ readonly parents?: readonly string[];
15
+ }
16
+ export declare class DriveClient {
17
+ private readonly accessToken;
18
+ private readonly fetchFn;
19
+ constructor(accessToken: string, fetchFn?: FetchFn);
20
+ /** List immediate children of a folder. Paginated. */
21
+ listChildren(folderId: string): Promise<readonly DriveFileMetadata[]>;
22
+ /** Export a Google native doc (gdoc / gsheet / gslides) to a chosen format. */
23
+ exportDoc(fileId: string, mimeType: string): Promise<Buffer>;
24
+ /** Download a binary file (image, PDF, …) as-is. */
25
+ downloadFile(fileId: string): Promise<Buffer>;
26
+ private authHeader;
27
+ }
28
+ //# sourceMappingURL=driveClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driveClient.d.ts","sourceRoot":"","sources":["../src/driveClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAMpC;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,UAAU,CAAC,KAAK,CAAA;AAE7C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,OAA0B;IAGtD,sDAAsD;IAChD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC;IAsB3E,+EAA+E;IACzE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQlE,oDAAoD;IAC9C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQnD,OAAO,CAAC,UAAU;CAGnB"}
@@ -0,0 +1,58 @@
1
+ import { Buffer } from 'node:buffer';
2
+ function globalBuffer() {
3
+ return Buffer;
4
+ }
5
+ export class DriveClient {
6
+ accessToken;
7
+ fetchFn;
8
+ constructor(accessToken, fetchFn = globalThis.fetch) {
9
+ this.accessToken = accessToken;
10
+ this.fetchFn = fetchFn;
11
+ }
12
+ /** List immediate children of a folder. Paginated. */
13
+ async listChildren(folderId) {
14
+ const items = [];
15
+ let pageToken;
16
+ do {
17
+ const params = new URLSearchParams({
18
+ q: `'${folderId}' in parents and trashed=false`,
19
+ fields: 'nextPageToken,files(id,name,mimeType,modifiedTime,parents)',
20
+ pageSize: '1000',
21
+ });
22
+ if (pageToken)
23
+ params.set('pageToken', pageToken);
24
+ const url = `https://www.googleapis.com/drive/v3/files?${params.toString()}`;
25
+ const response = await this.fetchFn(url, { headers: this.authHeader() });
26
+ if (!response.ok)
27
+ throw await driveError(response, `listChildren(${folderId})`);
28
+ const data = await response.json();
29
+ items.push(...data.files);
30
+ pageToken = data.nextPageToken;
31
+ } while (pageToken);
32
+ return items;
33
+ }
34
+ /** Export a Google native doc (gdoc / gsheet / gslides) to a chosen format. */
35
+ async exportDoc(fileId, mimeType) {
36
+ const url = `https://www.googleapis.com/drive/v3/files/${fileId}/export?${new URLSearchParams({ mimeType })}`;
37
+ const response = await this.fetchFn(url, { headers: this.authHeader() });
38
+ if (!response.ok)
39
+ throw await driveError(response, `exportDoc(${fileId},${mimeType})`);
40
+ return globalBuffer().from(await response.arrayBuffer());
41
+ }
42
+ /** Download a binary file (image, PDF, …) as-is. */
43
+ async downloadFile(fileId) {
44
+ const url = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`;
45
+ const response = await this.fetchFn(url, { headers: this.authHeader() });
46
+ if (!response.ok)
47
+ throw await driveError(response, `downloadFile(${fileId})`);
48
+ return globalBuffer().from(await response.arrayBuffer());
49
+ }
50
+ authHeader() {
51
+ return { Authorization: `Bearer ${this.accessToken}` };
52
+ }
53
+ }
54
+ async function driveError(response, op) {
55
+ const text = await response.text().catch(() => '');
56
+ return new Error(`Drive ${op} failed: ${response.status} ${response.statusText} ${text}`);
57
+ }
58
+ //# sourceMappingURL=driveClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driveClient.js","sourceRoot":"","sources":["../src/driveClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from './driveClient.js';
2
+ export * from './GoogleDriveLoader.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,wBAAwB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './driveClient.js';
2
+ export * from './GoogleDriveLoader.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,wBAAwB,CAAA"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@braidhq/source-loader-gdrive",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "Google Drive source-loader plugin: download text + images from a Drive folder into a workspace source path.",
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "README.md",
17
+ "dist"
18
+ ],
19
+ "dependencies": {
20
+ "zod": "^3.24.0",
21
+ "@braidhq/core": "0.0.1",
22
+ "@braidhq/schema": "0.0.1"
23
+ },
24
+ "devDependencies": {
25
+ "typescript": "^5.7.0",
26
+ "vitest": "^2.1.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc -p tsconfig.json",
33
+ "typecheck": "tsc -p tsconfig.json --noEmit",
34
+ "test": "vitest run",
35
+ "clean": "rm -rf dist .turbo *.tsbuildinfo coverage"
36
+ }
37
+ }