@braidhq/source-loader-gdrive 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
- import type { IngestReport, SourceLoader, SourceLoaderContext, SyncReport } from '@braidhq/core';
2
- import type { AbsolutePath, LoaderKind, PluginId } from '@braidhq/schema';
1
+ import type { SourceLoaderContext, SourceLoaderPlugin } from '@braidhq/core';
3
2
  import { z } from 'zod';
4
- import { type FetchFn } from './driveClient.js';
3
+ import { type DriveFileMetadata, type FetchFn } from './driveClient.js';
5
4
  export declare const GoogleDriveLoaderConfig: z.ZodObject<{
6
5
  /**
7
6
  * Drive folder id (the long alphanumeric in the URL). Reject the alias
@@ -12,16 +11,32 @@ export declare const GoogleDriveLoaderConfig: z.ZodObject<{
12
11
  */
13
12
  folderId: z.ZodEffects<z.ZodString, string, string>;
14
13
  /**
15
- * Whether to follow subfolders recursively. Default true. Disable for
16
- * flat folder mirrors where nested folders should be skipped.
14
+ * Whether to traverse subfolders. Default true. Subfolder hierarchy
15
+ * is NOT preserved on disk; every matching doc lives in its own dir
16
+ * directly under `destination/`. Disable to limit to immediate
17
+ * children of `folderId`.
17
18
  */
18
19
  recursive: z.ZodDefault<z.ZodBoolean>;
20
+ /**
21
+ * Optional regex (string). When set, only docs whose *Drive title*
22
+ * matches this pattern are downloaded. Subfolders are still traversed.
23
+ */
24
+ include: z.ZodOptional<z.ZodString>;
25
+ /**
26
+ * Optional regex (string). When set, docs whose title matches are
27
+ * skipped. Evaluated after `include`, so exclude takes priority.
28
+ */
29
+ exclude: z.ZodOptional<z.ZodString>;
19
30
  }, "strip", z.ZodTypeAny, {
20
31
  folderId: string;
21
32
  recursive: boolean;
33
+ exclude?: string | undefined;
34
+ include?: string | undefined;
22
35
  }, {
23
36
  folderId: string;
37
+ exclude?: string | undefined;
24
38
  recursive?: boolean | undefined;
39
+ include?: string | undefined;
25
40
  }>;
26
41
  export type GoogleDriveLoaderConfig = z.infer<typeof GoogleDriveLoaderConfig>;
27
42
  export interface GoogleDriveLoaderDeps {
@@ -35,46 +50,29 @@ export interface GoogleDriveLoaderDeps {
35
50
  fetchFn?: FetchFn;
36
51
  }
37
52
  /**
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/`.
53
+ * Build a Google Drive source loader. Walks a Drive folder, exports every
54
+ * Google Doc inside as markdown, extracts inlined base64 images into
55
+ * sibling files, and lays everything out as:
41
56
  *
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.
57
+ * <destination>/
58
+ * <sanitised-doc-title>/
59
+ * index.md
60
+ * <image-label>.png (one per inlined image)
61
+ * <sanitised-doc-title-2>/
62
+ * index.md
63
+ * .braid-manifest.json (sync state)
64
+ *
65
+ * Drive folder hierarchy is NOT preserved on disk; every matched doc
66
+ * is flattened into a sibling directory under `destination/`. Two docs
67
+ * with the same sanitised title will collide; rename one in Drive to
68
+ * disambiguate.
69
+ *
70
+ * Out of scope (intentional): Google Sheets, Slides, Drawings, Forms,
71
+ * and standalone binaries. Mirrors redoc's PRD-focused workflow. If you
72
+ * need spreadsheets / slide decks, ingest them through a different
73
+ * source-loader plugin.
46
74
  */
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
- }
75
+ export declare function createGoogleDriveLoader(deps: GoogleDriveLoaderDeps): SourceLoaderPlugin;
76
+ /** Note: the helper used by DriveClient.exportDoc returns Buffer. */
77
+ export type { DriveFileMetadata, FetchFn };
80
78
  //# sourceMappingURL=GoogleDriveLoader.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"GoogleDriveLoader.d.ts","sourceRoot":"","sources":["../src/GoogleDriveLoader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAK5E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAe,KAAK,iBAAiB,EAAE,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAA;AA2BpF,eAAO,MAAM,uBAAuB;IAClC;;;;;;OAMG;;IAMH;;;;;OAKG;;IAEH;;;OAGG;;IAEH;;;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;AAaD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,qBAAqB,GAAG,kBAAkB,CAgHvF;AA8GD,qEAAqE;AACrE,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAA"}
@@ -1,20 +1,31 @@
1
+ import { Buffer } from 'node:buffer';
1
2
  import { mkdir, rm, writeFile } from 'node:fs/promises';
2
3
  import { join } from 'node:path';
4
+ import { defineSourceLoader } from '@braidhq/sdk';
3
5
  import { z } from 'zod';
4
6
  import { DriveClient } from './driveClient.js';
7
+ import { readManifest, writeManifest } from './Manifest.js';
5
8
  const FOLDER_MIME = 'application/vnd.google-apps.folder';
9
+ const DOC_MIME = 'application/vnd.google-apps.document';
6
10
  /**
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
+ * Drive auto-creates duplicates named "Copy of …" (English) or "…的副本"
12
+ * (Chinese) when users use the Make a Copy menu. They almost always carry
13
+ * stale content that would conflict with the canonical doc, so we skip
14
+ * them silently. Same rule as redoc.
11
15
  */
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
- };
16
+ const COPY_PATTERNS = [/^Copy of /, /的副本$/];
17
+ /**
18
+ * Drive's `text/markdown` export inlines images as base64 data URIs in
19
+ * reference-style link form:
20
+ *
21
+ * [image0]: <data:image/png;base64,iVBORw0KG…>
22
+ *
23
+ * Leaving those base64 blobs in the markdown bloats the file by ~33 % per
24
+ * image and is unreadable for both humans and LLMs. This regex matches one
25
+ * such reference line so we can extract the image to its own file and
26
+ * rewrite the link to a relative path. Same approach as redoc.
27
+ */
28
+ const INLINE_IMAGE_RE = /^\[([^\]]+)\]:\s*<data:image\/([\w+]+);base64,([^>]+)>$/gm;
18
29
  export const GoogleDriveLoaderConfig = z.object({
19
30
  /**
20
31
  * Drive folder id (the long alphanumeric in the URL). Reject the alias
@@ -29,97 +40,246 @@ export const GoogleDriveLoaderConfig = z.object({
29
40
  message: 'folderId "root" refers to the entire My Drive and is rejected. Create a dedicated subfolder and use its id instead.',
30
41
  }),
31
42
  /**
32
- * Whether to follow subfolders recursively. Default true. Disable for
33
- * flat folder mirrors where nested folders should be skipped.
43
+ * Whether to traverse subfolders. Default true. Subfolder hierarchy
44
+ * is NOT preserved on disk; every matching doc lives in its own dir
45
+ * directly under `destination/`. Disable to limit to immediate
46
+ * children of `folderId`.
34
47
  */
35
48
  recursive: z.boolean().default(true),
49
+ /**
50
+ * Optional regex (string). When set, only docs whose *Drive title*
51
+ * matches this pattern are downloaded. Subfolders are still traversed.
52
+ */
53
+ include: z.string().optional(),
54
+ /**
55
+ * Optional regex (string). When set, docs whose title matches are
56
+ * skipped. Evaluated after `include`, so exclude takes priority.
57
+ */
58
+ exclude: z.string().optional(),
36
59
  });
37
60
  /**
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/`.
61
+ * Build a Google Drive source loader. Walks a Drive folder, exports every
62
+ * Google Doc inside as markdown, extracts inlined base64 images into
63
+ * sibling files, and lays everything out as:
64
+ *
65
+ * <destination>/
66
+ * <sanitised-doc-title>/
67
+ * index.md
68
+ * <image-label>.png (one per inlined image)
69
+ * <sanitised-doc-title-2>/
70
+ * index.md
71
+ * .braid-manifest.json (sync state)
41
72
  *
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.
73
+ * Drive folder hierarchy is NOT preserved on disk; every matched doc
74
+ * is flattened into a sibling directory under `destination/`. Two docs
75
+ * with the same sanitised title will collide; rename one in Drive to
76
+ * disambiguate.
77
+ *
78
+ * Out of scope (intentional): Google Sheets, Slides, Drawings, Forms,
79
+ * and standalone binaries. Mirrors redoc's PRD-focused workflow. If you
80
+ * need spreadsheets / slide decks, ingest them through a different
81
+ * source-loader plugin.
46
82
  */
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;
83
+ export function createGoogleDriveLoader(deps) {
84
+ return defineSourceLoader({
85
+ kind: 'gdrive',
86
+ configSchema: GoogleDriveLoaderConfig,
87
+ ingest: async (config, destination, context) => {
88
+ await rm(destination, { recursive: true, force: true });
89
+ await mkdir(destination, { recursive: true });
90
+ const client = await buildClient(deps, context);
91
+ const candidates = await walk(client, config);
92
+ const manifest = {
93
+ folderId: config.folderId,
94
+ include: config.include,
95
+ exclude: config.exclude,
96
+ files: {},
97
+ };
98
+ for (const doc of candidates) {
99
+ await downloadOne(client, doc, destination);
100
+ manifest.files[doc.id] = entryOf(doc);
101
+ }
102
+ await writeManifest(destination, manifest);
103
+ return {
104
+ localPath: destination,
105
+ metadata: { folderId: config.folderId, fileCount: candidates.length },
106
+ fetchedAt: new Date().toISOString(),
107
+ };
108
+ },
109
+ sync: async (config, destination, context) => {
110
+ const cached = await readManifest(destination);
111
+ if (!cached) {
112
+ // No manifest yet (first sync after upgrade / cache wiped). Fall
113
+ // back to a clean ingest so we end up in a known-good state.
114
+ const client = await buildClient(deps, context);
115
+ await rm(destination, { recursive: true, force: true });
116
+ await mkdir(destination, { recursive: true });
117
+ const candidates = await walk(client, config);
118
+ const manifest = {
119
+ folderId: config.folderId,
120
+ include: config.include,
121
+ exclude: config.exclude,
122
+ files: {},
123
+ };
124
+ for (const doc of candidates) {
125
+ await downloadOne(client, doc, destination);
126
+ manifest.files[doc.id] = entryOf(doc);
127
+ }
128
+ await writeManifest(destination, manifest);
129
+ return {
130
+ changed: true,
131
+ added: candidates.length,
132
+ updated: 0,
133
+ removed: 0,
134
+ unchanged: 0,
135
+ metadata: { folderId: config.folderId, fileCount: candidates.length },
136
+ fetchedAt: new Date().toISOString(),
137
+ };
138
+ }
139
+ const client = await buildClient(deps, context);
140
+ const candidates = await walk(client, config);
141
+ const seen = new Set();
142
+ let added = 0;
143
+ let updated = 0;
144
+ let unchanged = 0;
145
+ const next = {
146
+ folderId: config.folderId,
147
+ include: config.include,
148
+ exclude: config.exclude,
149
+ files: {},
150
+ };
151
+ for (const doc of candidates) {
152
+ seen.add(doc.id);
153
+ const prior = cached.files[doc.id];
154
+ if (!prior) {
155
+ await downloadOne(client, doc, destination);
156
+ added++;
157
+ }
158
+ else if (prior.localDir !== doc.localDir || prior.modifiedTime !== doc.modifiedTime) {
159
+ // Either content updated or the doc was renamed in Drive. Both
160
+ // cases: re-download to the new dir, then rm the old dir.
161
+ if (prior.localDir !== doc.localDir)
162
+ await rm(join(destination, prior.localDir), { recursive: true, force: true });
163
+ await downloadOne(client, doc, destination);
164
+ updated++;
165
+ }
166
+ else {
167
+ unchanged++;
168
+ }
169
+ next.files[doc.id] = entryOf(doc);
170
+ }
171
+ let removed = 0;
172
+ for (const [id, entry] of Object.entries(cached.files)) {
173
+ if (seen.has(id))
174
+ continue;
175
+ await rm(join(destination, entry.localDir), { recursive: true, force: true });
176
+ removed++;
177
+ }
178
+ await writeManifest(destination, next);
179
+ return {
180
+ changed: added + updated + removed > 0,
181
+ added,
182
+ updated,
183
+ removed,
184
+ unchanged,
185
+ metadata: { folderId: config.folderId },
186
+ fetchedAt: new Date().toISOString(),
187
+ };
188
+ },
189
+ });
190
+ }
191
+ async function buildClient(deps, context) {
192
+ const token = await deps.resolveAccessToken(context);
193
+ return new DriveClient(token, deps.fetchFn);
194
+ }
195
+ async function walk(client, config) {
196
+ const includeRe = compileRegex(config.include, 'include');
197
+ const excludeRe = compileRegex(config.exclude, 'exclude');
198
+ const out = [];
199
+ const seenDirs = new Map(); // localDir -> first doc id, to detect collisions
200
+ const visit = async (folderId) => {
86
201
  const children = await client.listChildren(folderId);
87
202
  for (const child of children) {
88
203
  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;
204
+ if (config.recursive)
205
+ await visit(child.id);
95
206
  continue;
96
207
  }
97
- const written = await this.writeOneFile(client, child, destination);
98
- if (written)
99
- fileCount++;
208
+ if (child.mimeType !== DOC_MIME)
209
+ continue;
210
+ if (COPY_PATTERNS.some(re => re.test(child.name)))
211
+ continue;
212
+ if (includeRe && !includeRe.test(child.name))
213
+ continue;
214
+ if (excludeRe && excludeRe.test(child.name))
215
+ continue;
216
+ const localDir = sanitiseName(child.name);
217
+ const collides = seenDirs.get(localDir);
218
+ if (collides && collides !== child.id) {
219
+ // Two distinct Drive docs sanitise to the same dir name. Keep
220
+ // the first one we walked into; skipping the second is safer
221
+ // than silently overwriting. User can rename in Drive.
222
+ continue;
223
+ }
224
+ seenDirs.set(localDir, child.id);
225
+ out.push({
226
+ id: child.id,
227
+ title: child.name,
228
+ modifiedTime: child.modifiedTime,
229
+ localDir,
230
+ });
100
231
  }
101
- return { fileCount };
232
+ };
233
+ await visit(config.folderId);
234
+ return out;
235
+ }
236
+ async function downloadOne(client, doc, destination) {
237
+ const docDir = join(destination, doc.localDir);
238
+ // Clean any prior content for this doc so removed images don't linger.
239
+ await rm(docDir, { recursive: true, force: true });
240
+ await mkdir(docDir, { recursive: true });
241
+ const bytes = await client.exportDoc(doc.id, 'text/markdown');
242
+ const markdown = bytes.toString('utf-8');
243
+ const rewritten = await extractInlineImages(markdown, docDir);
244
+ await writeFile(join(docDir, 'index.md'), rewritten, 'utf-8');
245
+ }
246
+ /**
247
+ * Pull every `[label]: <data:image/ext;base64,...>` reference out of the
248
+ * markdown, write the decoded bytes as `<label>.<ext>` in `docDir`, and
249
+ * rewrite the reference to point at the new local file. Returns the
250
+ * cleaned markdown. Matches redoc's behaviour.
251
+ */
252
+ async function extractInlineImages(markdown, docDir) {
253
+ const writes = [];
254
+ const rewritten = markdown.replace(INLINE_IMAGE_RE, (_match, label, ext, data) => {
255
+ const filename = `${sanitiseName(label)}.${ext}`;
256
+ writes.push(writeFile(join(docDir, filename), Buffer.from(data, 'base64')));
257
+ return `[${label}]: <${filename}>`;
258
+ });
259
+ await Promise.all(writes);
260
+ return rewritten;
261
+ }
262
+ function compileRegex(pattern, field) {
263
+ if (!pattern)
264
+ return undefined;
265
+ try {
266
+ return new RegExp(pattern);
102
267
  }
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;
268
+ catch (err) {
269
+ throw new Error(`googleDriveLoader: ${field} is not a valid regex (${err.message})`);
118
270
  }
119
271
  }
272
+ function entryOf(doc) {
273
+ return {
274
+ localDir: doc.localDir,
275
+ modifiedTime: doc.modifiedTime,
276
+ title: doc.title,
277
+ };
278
+ }
120
279
  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, '_');
280
+ // Replace path separators, control chars, and common shell-hostile
281
+ // characters with `_`. Spaces stay so human-readable titles survive
282
+ // ("Roadmap Q3 2026" → "Roadmap Q3 2026").
283
+ return name.replace(/[/\\:*?"<>|]/g, '_');
124
284
  }
125
285
  //# sourceMappingURL=GoogleDriveLoader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GoogleDriveLoader.js","sourceRoot":"","sources":["../src/GoogleDriveLoader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GoogleDriveLoader.js","sourceRoot":"","sources":["../src/GoogleDriveLoader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** Filename written inside `destination` to track per-file sync state. */
2
+ export declare const MANIFEST_FILENAME = ".braid-manifest.json";
3
+ export interface ManifestEntry {
4
+ /** Posix relative dir under `destination`: the per-doc package. */
5
+ localDir: string;
6
+ modifiedTime: string;
7
+ /** Drive title at the time of the last sync, for debugging only. */
8
+ title: string;
9
+ }
10
+ export interface Manifest {
11
+ folderId: string;
12
+ include: string | undefined;
13
+ exclude: string | undefined;
14
+ files: Record<string, ManifestEntry>;
15
+ }
16
+ /**
17
+ * Read the manifest file from `destination`. Returns `undefined` if the
18
+ * file is missing or unparseable; callers should fall back to a clean
19
+ * ingest in that case (no manifest = no incremental state to diff against).
20
+ */
21
+ export declare function readManifest(destination: string): Promise<Manifest | undefined>;
22
+ export declare function writeManifest(destination: string, manifest: Manifest): Promise<void>;
23
+ //# sourceMappingURL=Manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Manifest.d.ts","sourceRoot":"","sources":["../src/Manifest.ts"],"names":[],"mappings":"AAGA,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,yBAAyB,CAAA;AAEvD,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CACrC;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAQrF;AAED,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1F"}
@@ -0,0 +1,22 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ /** Filename written inside `destination` to track per-file sync state. */
4
+ export const MANIFEST_FILENAME = '.braid-manifest.json';
5
+ /**
6
+ * Read the manifest file from `destination`. Returns `undefined` if the
7
+ * file is missing or unparseable; callers should fall back to a clean
8
+ * ingest in that case (no manifest = no incremental state to diff against).
9
+ */
10
+ export async function readManifest(destination) {
11
+ try {
12
+ const raw = await readFile(join(destination, MANIFEST_FILENAME), 'utf-8');
13
+ return JSON.parse(raw);
14
+ }
15
+ catch {
16
+ return undefined;
17
+ }
18
+ }
19
+ export async function writeManifest(destination, manifest) {
20
+ await writeFile(join(destination, MANIFEST_FILENAME), `${JSON.stringify(manifest, null, 2)}\n`, 'utf-8');
21
+ }
22
+ //# sourceMappingURL=Manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Manifest.js","sourceRoot":"","sources":["../src/Manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAA;AAiBvD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB;IACpD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAAA;QACzE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAA;IACpC,CAAC;IACD,MAAM,CAAC;QACL,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,WAAmB,EAAE,QAAkB;IACzE,MAAM,SAAS,CACb,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EACpC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EACxC,OAAO,CACR,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,37 +1,49 @@
1
1
  {
2
2
  "name": "@braidhq/source-loader-gdrive",
3
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.",
4
+ "version": "0.1.0",
5
+ "description": "Google Drive source-loader plugin for Braid. Export docs from Google Drive, requires OAuth on first use.",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/mroops0111/braid.git",
10
+ "directory": "packages/source-loader-gdrive"
11
+ },
7
12
  "exports": {
8
- ".": {
9
- "types": "./dist/index.d.ts",
10
- "default": "./dist/index.js"
11
- }
13
+ ".": "./src/index.ts"
12
14
  },
13
- "main": "./dist/index.js",
14
- "types": "./dist/index.d.ts",
15
+ "main": "./src/index.ts",
16
+ "types": "./src/index.ts",
15
17
  "files": [
16
18
  "README.md",
17
19
  "dist"
18
20
  ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json",
24
+ "test": "vitest run",
25
+ "clean": "rm -rf dist .turbo *.tsbuildinfo coverage"
26
+ },
19
27
  "dependencies": {
20
- "zod": "^3.24.0",
21
- "@braidhq/core": "0.0.1",
22
- "@braidhq/schema": "0.0.1"
28
+ "@braidhq/core": "workspace:*",
29
+ "@braidhq/schema": "workspace:*",
30
+ "@braidhq/sdk": "workspace:*",
31
+ "zod": "^3.24.0"
23
32
  },
24
33
  "devDependencies": {
25
- "typescript": "^5.7.0",
26
- "vitest": "^2.1.0"
34
+ "@braidhq/test-utils": "workspace:*",
35
+ "typescript": "^6.0.3",
36
+ "vitest": "^4.1.0"
27
37
  },
28
38
  "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"
39
+ "access": "public",
40
+ "main": "./dist/index.js",
41
+ "types": "./dist/index.d.ts",
42
+ "exports": {
43
+ ".": {
44
+ "types": "./dist/index.d.ts",
45
+ "default": "./dist/index.js"
46
+ }
47
+ }
36
48
  }
37
- }
49
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
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.