@aigne/afs-gcs 1.11.0-beta.6 → 1.11.0-beta.7

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/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AFSAccessMode, AFSBaseProvider, AFSDeleteResult, AFSEntry, AFSExecResult, AFSListResult, AFSModuleLoadParams, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult, RouteContext } from "@aigne/afs";
1
+ import { AFSAccessMode, AFSBaseProvider, AFSDeleteResult, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchResult, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult, RouteContext } from "@aigne/afs";
2
2
  import * as zod0 from "zod";
3
3
  import { z } from "zod";
4
4
 
@@ -140,7 +140,10 @@ declare class AFSGCS extends AFSBaseProvider {
140
140
  /**
141
141
  * Load from configuration file
142
142
  */
143
- static load(params: AFSModuleLoadParams): Promise<AFSGCS>;
143
+ static load({
144
+ basePath,
145
+ config
146
+ }?: AFSModuleLoadParams): Promise<AFSGCS>;
144
147
  /**
145
148
  * Build the full GCS key from a path
146
149
  */
@@ -201,46 +204,14 @@ declare class AFSGCS extends AFSBaseProvider {
201
204
  rewriteActionHandler(ctx: RouteContext<{
202
205
  path: string;
203
206
  }>, args: Record<string, unknown>): Promise<AFSExecResult>;
204
- /**
205
- * Generate a signed URL for downloading an object
206
- * @deprecated Use action /.actions/sign-download instead
207
- */
208
- getSignedDownloadUrl(path: string, options?: {
209
- expiresIn?: number;
210
- }): Promise<string>;
211
- /**
212
- * Generate a signed URL for uploading an object
213
- * @deprecated Use action /.actions/sign-upload instead
214
- */
215
- getSignedUploadUrl(path: string, options?: {
216
- expiresIn?: number;
217
- contentType?: string;
218
- }): Promise<string>;
219
- /**
220
- * List all versions (generations) of an object
221
- * @deprecated Use list on /:path/@versions instead
222
- */
223
- listVersions(path: string): Promise<Array<{
224
- generation: string;
225
- isLive: boolean;
226
- timeCreated?: Date;
227
- size: number;
228
- etag?: string;
229
- }>>;
230
- /**
231
- * Read a specific version (generation) of an object
232
- * @deprecated Use read on /:path/@versions/:generation instead
233
- */
234
- readVersion(path: string, generation: string): Promise<{
235
- content: string;
236
- metadata: Record<string, unknown>;
237
- }>;
238
- /**
239
- * Create a directory marker
240
- * @deprecated Use write with empty content instead
241
- */
242
- mkdir(path: string): Promise<void>;
207
+ explainHandler(ctx: RouteContext<{
208
+ path?: string;
209
+ }>): Promise<AFSExplainResult>;
210
+ searchHandler(ctx: RouteContext<{
211
+ path?: string;
212
+ }>, query: string): Promise<AFSSearchResult>;
213
+ readCapabilities(_ctx: RouteContext): Promise<AFSEntry>;
243
214
  }
244
215
  //#endregion
245
- export { AFSGCS, type AFSGCSOptions, type ParsedGCSUri };
216
+ export { AFSGCS, AFSGCS as default, type AFSGCSOptions, type ParsedGCSUri };
246
217
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/gcs-afs.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/gcs-afs.ts"],"mappings":";;;;;;AAUA;;UAAiB,aAAA;EAAa;EAE5B,IAAA;EAGA;EAAA,WAAA;EAMA;EAHA,MAAA;EASA;EANA,MAAA;EAYA;EATA,SAAA;EAaE;EAVF,UAAA;EAeA;EAZA,QAAA;EAYQ;EATR,WAAA;EAgE2B;EA7D3B,WAAA;IACE,WAAA;IACA,UAAA;EAAA;;EAIF,QAAA;AAAA;;;;UAuDe,YAAA;EACf,MAAA;EACA,MAAA;AAAA;;;;;;;;;;;;;;;;;;AAFF;;;;;;;;ACdA;cAAa,MAAA,SAAe,eAAA;EAAA,SACR,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,QAEtB,OAAA;EAAA,QACA,OAAA;EAAA,QACA,MAAA;EAAA,QACA,SAAA;EAAA,QACA,SAAA;cAEI,OAAA,EAAS,aAAA;;;;SA+Bd,MAAA,CAAA,GAAM,IAAA,CAAA,UAAA,MAAA,SAAA;2CA/BqB,IAAA,CAAA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAq8B1B;;;EAAA,OA/5BK,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAA2B,OAAA,CAAQ,MAAA;EAm9BP;;;EAAA,QAz8B5D,WAAA;EAkiCL;;;EAAA,QA1hCK,UAAA;EAnEkB;;;EAAA,QA2ElB,eAAA;EA1EU;;;EA2FlB,UAAA,CAAA;EASM,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EA7FzD;;;EAAA,QAgKM,iBAAA;EAoHR,mBAAA,CAAoB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EAgDlE,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,QAAA;EA8H1D,kBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;IAAc,UAAA;EAAA,KACjC,OAAA,CAAQ,QAAA;EA0CL,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;EAmJ3D,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EAuB3D,YAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,EAAS,oBAAA,GACR,OAAA,CAAQ,cAAA;EAiEL,aAAA,CAAc,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,eAAA;EA4C5D,kBAAA,CAAmB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EAgFjE,yBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EA0BL,uBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EA4BL,oBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EA+CL,oBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EAmDL,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,gBAAA;EAsF9D,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,KAAA,WACC,OAAA,CAAQ,eAAA;EAyCL,gBAAA,CAAiB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;AAAA"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { AFSBaseProvider, AFSError, AFSNotFoundError, Actions, Delete, List, Meta, Read, Stat, Write } from "@aigne/afs";
1
+ import { AFSBaseProvider, AFSError, AFSNotFoundError, Actions, Delete, Explain, List, Meta, Read, Search, Stat, Write } from "@aigne/afs";
2
2
  import { camelize, optionalize, zodParse } from "@aigne/afs/utils/zod";
3
+ import { joinURL } from "ufo";
3
4
  import { Storage } from "@google-cloud/storage";
4
5
  import { z } from "zod";
5
6
 
@@ -345,8 +346,8 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
345
346
  /**
346
347
  * Load from configuration file
347
348
  */
348
- static async load(params) {
349
- return new AFSGCS(zodParse(afsgcsOptionsSchema, params.parsed, { prefix: params.filepath }));
349
+ static async load({ basePath, config } = {}) {
350
+ return new AFSGCS(zodParse(afsgcsOptionsSchema, config, { prefix: basePath }));
350
351
  }
351
352
  /**
352
353
  * Build the full GCS key from a path
@@ -359,7 +360,8 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
359
360
  * Generate a unique ID for a GCS object
360
361
  */
361
362
  generateId(key) {
362
- return `gs://${this.options.bucket}/${key}`;
363
+ const cleanKey = key.replace(/^\/+/, "");
364
+ return `gs://${this.options.bucket}/${cleanKey}`;
363
365
  }
364
366
  /**
365
367
  * Invalidate caches for a given path
@@ -423,14 +425,12 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
423
425
  if (!dirPrefix) continue;
424
426
  const dirName = dirPrefix.slice(prefix.length).replace(/\/$/, "");
425
427
  if (!dirName) continue;
426
- const entryPath = basePath ? `${basePath}/${dirName}` : dirName;
427
- const normalizedPath = entryPath.startsWith("/") ? entryPath : `/${entryPath}`;
428
428
  childEntries.push({
429
429
  id: this.generateId(dirPrefix),
430
- path: normalizedPath,
431
- metadata: {
430
+ path: joinURL("/", basePath, dirName),
431
+ meta: {
432
432
  kind: "afs:node",
433
- childrenCount: void 0,
433
+ childrenCount: -1,
434
434
  platformRef: generatePlatformRef(bucketName, dirPrefix, true)
435
435
  }
436
436
  });
@@ -442,15 +442,13 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
442
442
  if (file.name.endsWith("/")) {
443
443
  const dirName = file.name.slice(prefix.length).replace(/\/$/, "");
444
444
  if (!dirName) continue;
445
- const entryPath$1 = basePath ? `${basePath}/${dirName}` : dirName;
446
- const normalizedPath$1 = entryPath$1.startsWith("/") ? entryPath$1 : `/${entryPath$1}`;
447
445
  childEntries.push({
448
446
  id: this.generateId(file.name),
449
- path: normalizedPath$1,
447
+ path: joinURL("/", basePath, dirName),
450
448
  updatedAt: file.metadata.updated ? new Date(file.metadata.updated) : void 0,
451
- metadata: {
449
+ meta: {
452
450
  kind: "afs:node",
453
- childrenCount: void 0,
451
+ childrenCount: -1,
454
452
  platformRef: generatePlatformRef(bucketName, file.name, true)
455
453
  }
456
454
  });
@@ -459,13 +457,11 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
459
457
  }
460
458
  const fileName = file.name.slice(prefix.length);
461
459
  if (!fileName || fileName.includes("/")) continue;
462
- const entryPath = basePath ? `${basePath}/${fileName}` : fileName;
463
- const normalizedPath = entryPath.startsWith("/") ? entryPath : `/${entryPath}`;
464
460
  childEntries.push({
465
461
  id: this.generateId(file.name),
466
- path: normalizedPath,
462
+ path: joinURL("/", basePath, fileName),
467
463
  updatedAt: file.metadata.updated ? new Date(file.metadata.updated) : void 0,
468
- metadata: {
464
+ meta: {
469
465
  size: file.metadata.size ? Number(file.metadata.size) : void 0,
470
466
  contentType: file.metadata.contentType,
471
467
  lastModified: file.metadata.updated,
@@ -479,36 +475,12 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
479
475
  const selfPath = basePath ? basePath.startsWith("/") ? basePath : `/${basePath}` : "/";
480
476
  if (childEntries.length === 0 && basePath) {
481
477
  const key = this.options.prefix ? `${this.options.prefix}/${basePath}` : basePath;
482
- const file = this.bucket.file(key);
483
- const [fileExists] = await file.exists();
484
- if (fileExists) {
485
- const [metadata] = await file.getMetadata();
486
- return { data: [{
487
- id: this.generateId(key),
488
- path: selfPath,
489
- updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
490
- metadata: {
491
- size: metadata.size ? Number(metadata.size) : void 0,
492
- contentType: metadata.contentType,
493
- lastModified: metadata.updated,
494
- etag: metadata.etag,
495
- childrenCount: 0,
496
- platformRef: generatePlatformRef(bucketName, key, false)
497
- }
498
- }] };
499
- }
478
+ const [fileExists] = await this.bucket.file(key).exists();
479
+ if (fileExists) return { data: [] };
500
480
  const [dirExists] = await this.bucket.file(`${key}/`).exists();
501
481
  if (!dirExists) throw new AFSNotFoundError(selfPath);
502
482
  }
503
- return { data: [{
504
- id: this.generateId(prefix || "/"),
505
- path: selfPath,
506
- metadata: {
507
- kind: "afs:node",
508
- childrenCount: childEntries.length,
509
- platformRef: generatePlatformRef(bucketName, prefix, true)
510
- }
511
- }, ...childEntries] };
483
+ return { data: childEntries };
512
484
  }
513
485
  async listVersionsHandler(ctx) {
514
486
  try {
@@ -523,13 +495,13 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
523
495
  if (versionFile.name !== key) continue;
524
496
  const generation = versionFile.metadata.generation;
525
497
  if (!generation) continue;
526
- const versionPath = `/${normalizedPath}/@versions/${generation}`;
498
+ const versionPath = joinURL("/", normalizedPath, "@versions", String(generation));
527
499
  const isLive = !versionFile.metadata.timeDeleted;
528
500
  entries.push({
529
501
  id: `${this.generateId(key)}:${generation}`,
530
502
  path: versionPath,
531
503
  updatedAt: versionFile.metadata.timeCreated ? new Date(versionFile.metadata.timeCreated) : void 0,
532
- metadata: {
504
+ meta: {
533
505
  generation,
534
506
  isLive,
535
507
  timeCreated: versionFile.metadata.timeCreated,
@@ -559,7 +531,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
559
531
  id: this.generateId("/"),
560
532
  path: "/",
561
533
  content: "",
562
- metadata: {
534
+ meta: {
563
535
  kind: "afs:node",
564
536
  childrenCount,
565
537
  platformRef: generatePlatformRef(bucketName, "", true)
@@ -575,9 +547,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
575
547
  path: normalizedOutputPath,
576
548
  content: "",
577
549
  updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
578
- metadata: {
550
+ meta: {
579
551
  kind: "afs:node",
580
- childrenCount: void 0,
552
+ childrenCount: -1,
581
553
  platformRef: generatePlatformRef(bucketName, key, true)
582
554
  }
583
555
  };
@@ -587,7 +559,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
587
559
  path: normalizedOutputPath,
588
560
  content: content.toString("utf-8"),
589
561
  updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
590
- metadata: {
562
+ meta: {
591
563
  size: metadata.size ? Number(metadata.size) : void 0,
592
564
  mimeType: metadata.contentType,
593
565
  contentType: metadata.contentType,
@@ -608,7 +580,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
608
580
  id: this.generateId(`${key}/`),
609
581
  path: normalizedOutputPath,
610
582
  content: "",
611
- metadata: {
583
+ meta: {
612
584
  kind: "afs:node",
613
585
  childrenCount,
614
586
  platformRef: generatePlatformRef(bucketName, key, true)
@@ -624,9 +596,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
624
596
  path: normalizedOutputPath,
625
597
  content: "",
626
598
  updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated) : void 0,
627
- metadata: {
599
+ meta: {
628
600
  kind: "afs:node",
629
- childrenCount: void 0,
601
+ childrenCount: -1,
630
602
  platformRef: generatePlatformRef(bucketName, key, true)
631
603
  }
632
604
  };
@@ -652,7 +624,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
652
624
  path: ctx.path,
653
625
  content: content.toString("utf-8"),
654
626
  updatedAt: metadata.timeCreated ? new Date(metadata.timeCreated) : void 0,
655
- metadata: {
627
+ meta: {
656
628
  size: metadata.size ? Number(metadata.size) : void 0,
657
629
  contentType: metadata.contentType,
658
630
  timeCreated: metadata.timeCreated,
@@ -681,7 +653,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
681
653
  return {
682
654
  id: this.generateId("/"),
683
655
  path: "/.meta",
684
- metadata: {
656
+ meta: {
685
657
  kind: "afs:node",
686
658
  childrenCount,
687
659
  platformRef: generatePlatformRef(bucketName, "", true)
@@ -694,7 +666,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
694
666
  if (cached) return {
695
667
  id: cached.id,
696
668
  path: ctx.path,
697
- metadata: cached.metadata
669
+ meta: cached.meta
698
670
  };
699
671
  }
700
672
  const file = this.bucket.file(key);
@@ -705,9 +677,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
705
677
  id: this.generateId(key),
706
678
  path: ctx.path,
707
679
  updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
708
- metadata: {
680
+ meta: {
709
681
  kind: "afs:node",
710
- childrenCount: void 0,
682
+ childrenCount: -1,
711
683
  platformRef: generatePlatformRef(bucketName, key, true)
712
684
  }
713
685
  };
@@ -715,7 +687,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
715
687
  id: this.generateId(key),
716
688
  path: ctx.path,
717
689
  updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
718
- metadata: {
690
+ meta: {
719
691
  kind: "afs:document",
720
692
  size: metadata.size ? Number(metadata.size) : void 0,
721
693
  contentType: metadata.contentType,
@@ -742,9 +714,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
742
714
  if (files.length > 0) return {
743
715
  id: this.generateId(`${key}/`),
744
716
  path: ctx.path,
745
- metadata: {
717
+ meta: {
746
718
  kind: "afs:node",
747
- childrenCount: void 0,
719
+ childrenCount: -1,
748
720
  platformRef: generatePlatformRef(bucketName, key, true)
749
721
  }
750
722
  };
@@ -756,9 +728,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
756
728
  id: this.generateId(`${key}/`),
757
729
  path: ctx.path,
758
730
  updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated) : void 0,
759
- metadata: {
731
+ meta: {
760
732
  kind: "afs:node",
761
- childrenCount: void 0,
733
+ childrenCount: -1,
762
734
  platformRef: generatePlatformRef(bucketName, key, true)
763
735
  }
764
736
  };
@@ -774,11 +746,11 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
774
746
  ...ctx,
775
747
  path: ctx.path.endsWith("/.meta") ? ctx.path : `${ctx.path}/.meta`
776
748
  });
749
+ const pathSegments = ctx.path.split("/").filter(Boolean);
777
750
  return { data: {
751
+ id: pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : "/",
778
752
  path: ctx.path,
779
- size: metaEntry.metadata?.size,
780
- childrenCount: metaEntry.metadata?.childrenCount,
781
- meta: metaEntry.metadata
753
+ meta: metaEntry.meta
782
754
  } };
783
755
  }
784
756
  async writeHandler(ctx, payload) {
@@ -791,7 +763,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
791
763
  else if (payload.content !== void 0) content = JSON.stringify(payload.content);
792
764
  else content = "";
793
765
  const saveOptions = { validation: false };
794
- if (payload.metadata?.mimeType || payload.metadata?.contentType) saveOptions.contentType = payload.metadata.mimeType ?? payload.metadata.contentType;
766
+ if (payload.meta?.mimeType || payload.meta?.contentType) saveOptions.contentType = payload.meta.mimeType ?? payload.meta.contentType;
795
767
  await file.save(content, saveOptions);
796
768
  const [metadata] = await file.getMetadata();
797
769
  const normalizedOutputPath = ctx.path.startsWith("/") ? ctx.path : `/${ctx.path}`;
@@ -801,11 +773,11 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
801
773
  path: normalizedOutputPath,
802
774
  content: payload.content,
803
775
  updatedAt: metadata.updated ? new Date(metadata.updated) : /* @__PURE__ */ new Date(),
804
- metadata: {
776
+ meta: {
805
777
  size: metadata.size ? Number(metadata.size) : 0,
806
778
  etag: metadata.etag,
807
779
  generation: metadata.generation,
808
- ...payload.metadata
780
+ ...payload.meta
809
781
  }
810
782
  } };
811
783
  } catch (error) {
@@ -834,42 +806,88 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
834
806
  }
835
807
  }
836
808
  async listActionsHandler(ctx) {
837
- const basePath = ctx.path.replace(/\/\.actions$/, "");
838
809
  return { data: [
839
810
  {
840
811
  id: "presign-download",
841
- path: `${basePath}/.actions/presign-download`,
812
+ path: joinURL(ctx.path, "presign-download"),
842
813
  summary: "Generate signed download URL",
843
- metadata: {
814
+ meta: {
844
815
  kind: "afs:executable",
845
- kinds: ["afs:executable", "afs:node"]
816
+ kinds: ["afs:executable", "afs:node"],
817
+ inputSchema: {
818
+ type: "object",
819
+ properties: { expiresIn: {
820
+ type: "number",
821
+ description: "Expiration in seconds (default: 3600, max: 604800)"
822
+ } }
823
+ }
846
824
  }
847
825
  },
848
826
  {
849
827
  id: "presign-upload",
850
- path: `${basePath}/.actions/presign-upload`,
828
+ path: joinURL(ctx.path, "presign-upload"),
851
829
  summary: "Generate signed upload URL",
852
- metadata: {
830
+ meta: {
853
831
  kind: "afs:executable",
854
- kinds: ["afs:executable", "afs:node"]
832
+ kinds: ["afs:executable", "afs:node"],
833
+ inputSchema: {
834
+ type: "object",
835
+ properties: {
836
+ expiresIn: {
837
+ type: "number",
838
+ description: "Expiration in seconds"
839
+ },
840
+ contentType: {
841
+ type: "string",
842
+ description: "Content-Type for upload"
843
+ }
844
+ }
845
+ }
855
846
  }
856
847
  },
857
848
  {
858
849
  id: "compose",
859
- path: `${basePath}/.actions/compose`,
850
+ path: joinURL(ctx.path, "compose"),
860
851
  summary: "Compose multiple objects into one",
861
- metadata: {
852
+ meta: {
862
853
  kind: "afs:executable",
863
- kinds: ["afs:executable", "afs:node"]
854
+ kinds: ["afs:executable", "afs:node"],
855
+ inputSchema: {
856
+ type: "object",
857
+ properties: { sources: {
858
+ type: "array",
859
+ description: "Array of source paths (2-32)",
860
+ items: { type: "string" }
861
+ } },
862
+ required: ["sources"]
863
+ }
864
864
  }
865
865
  },
866
866
  {
867
867
  id: "rewrite",
868
- path: `${basePath}/.actions/rewrite`,
868
+ path: joinURL(ctx.path, "rewrite"),
869
869
  summary: "Rewrite object to new location",
870
- metadata: {
870
+ meta: {
871
871
  kind: "afs:executable",
872
- kinds: ["afs:executable", "afs:node"]
872
+ kinds: ["afs:executable", "afs:node"],
873
+ inputSchema: {
874
+ type: "object",
875
+ properties: {
876
+ destination: {
877
+ type: "string",
878
+ description: "Destination path"
879
+ },
880
+ storageClass: {
881
+ type: "string",
882
+ description: "Target storage class"
883
+ },
884
+ contentType: {
885
+ type: "string",
886
+ description: "Override content type"
887
+ }
888
+ },
889
+ required: ["destination"]
890
+ }
873
891
  }
874
892
  }
875
893
  ] };
@@ -966,74 +984,180 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
966
984
  }
967
985
  };
968
986
  }
969
- /**
970
- * Generate a signed URL for downloading an object
971
- * @deprecated Use action /.actions/sign-download instead
972
- */
973
- async getSignedDownloadUrl(path, options) {
974
- return (await this.signDownloadActionHandler({
975
- path: `/${path}`,
976
- params: { path },
977
- options: {}
978
- }, { expiresIn: options?.expiresIn })).data.url;
979
- }
980
- /**
981
- * Generate a signed URL for uploading an object
982
- * @deprecated Use action /.actions/sign-upload instead
983
- */
984
- async getSignedUploadUrl(path, options) {
985
- return (await this.signUploadActionHandler({
986
- path: `/${path}`,
987
- params: { path },
988
- options: {}
989
- }, {
990
- expiresIn: options?.expiresIn,
991
- contentType: options?.contentType
992
- })).data.url;
993
- }
994
- /**
995
- * List all versions (generations) of an object
996
- * @deprecated Use list on /:path/@versions instead
997
- */
998
- async listVersions(path) {
999
- return (await this.listVersionsHandler({
1000
- path: `/${path}/@versions`,
1001
- params: { path },
1002
- options: {}
1003
- })).data.map((entry) => ({
1004
- generation: entry.metadata?.generation,
1005
- isLive: entry.metadata?.isLive,
1006
- timeCreated: entry.updatedAt,
1007
- size: entry.metadata?.size ?? 0,
1008
- etag: entry.metadata?.etag
1009
- }));
1010
- }
1011
- /**
1012
- * Read a specific version (generation) of an object
1013
- * @deprecated Use read on /:path/@versions/:generation instead
1014
- */
1015
- async readVersion(path, generation) {
1016
- const result = await this.readVersionHandler({
1017
- path: `/${path}/@versions/${generation}`,
1018
- params: {
1019
- path,
1020
- generation
1021
- },
1022
- options: {}
987
+ async explainHandler(ctx) {
988
+ const normalizedPath = (ctx.params.path ?? "").replace(/^\/+/, "").replace(/\/+$/, "");
989
+ if (!normalizedPath) {
990
+ const [files$1, , response$1] = await this.bucket.getFiles({
991
+ prefix: this.options.prefix ? `${this.options.prefix}/` : void 0,
992
+ delimiter: "/",
993
+ maxResults: 1e3
994
+ });
995
+ const objectCount$1 = files$1.length;
996
+ const prefixCount$1 = response$1?.prefixes?.length ?? 0;
997
+ const lines$1 = [];
998
+ lines$1.push(`# ${this.options.bucket}`);
999
+ lines$1.push("");
1000
+ lines$1.push(`- **Type**: GCS Bucket`);
1001
+ lines$1.push(`- **Bucket**: ${this.options.bucket}`);
1002
+ if (this.options.prefix) lines$1.push(`- **Prefix**: ${this.options.prefix}`);
1003
+ if (this.options.projectId) lines$1.push(`- **Project**: ${this.options.projectId}`);
1004
+ lines$1.push(`- **Access Mode**: ${this.accessMode}`);
1005
+ lines$1.push(`- **Top-level Objects**: ${objectCount$1}`);
1006
+ lines$1.push(`- **Top-level Prefixes**: ${prefixCount$1}`);
1007
+ return {
1008
+ format: "markdown",
1009
+ content: lines$1.join("\n")
1010
+ };
1011
+ }
1012
+ const key = this.buildGCSKey(normalizedPath);
1013
+ const file = this.bucket.file(key);
1014
+ const [exists] = await file.exists();
1015
+ if (exists) {
1016
+ const [metadata] = await file.getMetadata();
1017
+ const lines$1 = [];
1018
+ lines$1.push(`# ${normalizedPath}`);
1019
+ lines$1.push("");
1020
+ lines$1.push(`- **Type**: Object`);
1021
+ lines$1.push(`- **Key**: ${key}`);
1022
+ lines$1.push(`- **Size**: ${metadata.size ?? 0} bytes`);
1023
+ if (metadata.contentType) lines$1.push(`- **Content-Type**: ${metadata.contentType}`);
1024
+ if (metadata.storageClass) lines$1.push(`- **Storage Class**: ${metadata.storageClass}`);
1025
+ if (metadata.updated) lines$1.push(`- **Last Modified**: ${metadata.updated}`);
1026
+ if (metadata.etag) lines$1.push(`- **ETag**: ${metadata.etag}`);
1027
+ if (metadata.generation) lines$1.push(`- **Generation**: ${metadata.generation}`);
1028
+ return {
1029
+ format: "markdown",
1030
+ content: lines$1.join("\n")
1031
+ };
1032
+ }
1033
+ const prefix = key.endsWith("/") ? key : `${key}/`;
1034
+ const [files, , response] = await this.bucket.getFiles({
1035
+ prefix,
1036
+ delimiter: "/",
1037
+ maxResults: 1e3
1023
1038
  });
1039
+ const objectCount = files.length;
1040
+ const prefixCount = response?.prefixes?.length ?? 0;
1041
+ if (objectCount === 0 && prefixCount === 0) throw new AFSNotFoundError(`/${normalizedPath}`, `Path not found: ${normalizedPath}`);
1042
+ const lines = [];
1043
+ lines.push(`# ${normalizedPath}/`);
1044
+ lines.push("");
1045
+ lines.push(`- **Type**: Prefix (directory)`);
1046
+ lines.push(`- **Prefix**: ${prefix}`);
1047
+ lines.push(`- **Objects**: ${objectCount}`);
1048
+ lines.push(`- **Sub-prefixes**: ${prefixCount}`);
1024
1049
  return {
1025
- content: result.content,
1026
- metadata: result.metadata
1050
+ format: "markdown",
1051
+ content: lines.join("\n")
1027
1052
  };
1028
1053
  }
1029
- /**
1030
- * Create a directory marker
1031
- * @deprecated Use write with empty content instead
1032
- */
1033
- async mkdir(path) {
1034
- const key = this.buildGCSKey(path);
1035
- const dirKey = key.endsWith("/") ? key : `${key}/`;
1036
- await this.bucket.file(dirKey).save("", { contentType: "application/x-directory" });
1054
+ async searchHandler(ctx, query) {
1055
+ const { minimatch } = await import("minimatch");
1056
+ const normalizedPath = (ctx.params.path ?? "").replace(/^\/+/, "").replace(/\/+$/, "");
1057
+ const prefix = normalizedPath ? this.options.prefix ? `${this.options.prefix}/${normalizedPath}/` : `${normalizedPath}/` : this.options.prefix ? `${this.options.prefix}/` : "";
1058
+ const [files] = await this.bucket.getFiles({ prefix: prefix || void 0 });
1059
+ const results = [];
1060
+ for (const file of files) {
1061
+ const relativePath = prefix ? file.name.slice(prefix.length) : file.name;
1062
+ if (!relativePath) continue;
1063
+ if (minimatch(relativePath, query)) {
1064
+ const displayPath = joinURL("/", normalizedPath, relativePath);
1065
+ results.push({
1066
+ id: this.generateId(file.name),
1067
+ path: displayPath,
1068
+ meta: {
1069
+ size: file.metadata?.size ? Number(file.metadata.size) : void 0,
1070
+ contentType: file.metadata?.contentType,
1071
+ lastModified: file.metadata?.updated
1072
+ }
1073
+ });
1074
+ }
1075
+ }
1076
+ return { data: results };
1077
+ }
1078
+ async readCapabilities(_ctx) {
1079
+ const capabilities = {
1080
+ schemaVersion: 1,
1081
+ provider: "gcs",
1082
+ description: `GCS bucket: ${this.options.bucket}`,
1083
+ tools: [],
1084
+ operations: this.getOperationsDeclaration(),
1085
+ actions: [{
1086
+ description: "GCS object actions",
1087
+ catalog: [
1088
+ {
1089
+ name: "presign-download",
1090
+ description: "Generate a signed download URL",
1091
+ inputSchema: {
1092
+ type: "object",
1093
+ properties: { expiresIn: {
1094
+ type: "number",
1095
+ description: "Expiration in seconds (default: 3600, max: 604800)"
1096
+ } }
1097
+ }
1098
+ },
1099
+ {
1100
+ name: "presign-upload",
1101
+ description: "Generate a signed upload URL",
1102
+ inputSchema: {
1103
+ type: "object",
1104
+ properties: {
1105
+ expiresIn: {
1106
+ type: "number",
1107
+ description: "Expiration in seconds"
1108
+ },
1109
+ contentType: {
1110
+ type: "string",
1111
+ description: "Content-Type for upload"
1112
+ }
1113
+ }
1114
+ }
1115
+ },
1116
+ {
1117
+ name: "compose",
1118
+ description: "Compose multiple objects into one (max 32 sources)",
1119
+ inputSchema: {
1120
+ type: "object",
1121
+ properties: { sources: {
1122
+ type: "array",
1123
+ description: "Array of source paths (2-32)",
1124
+ items: { type: "string" }
1125
+ } },
1126
+ required: ["sources"]
1127
+ }
1128
+ },
1129
+ {
1130
+ name: "rewrite",
1131
+ description: "Copy/rewrite object to a new location",
1132
+ inputSchema: {
1133
+ type: "object",
1134
+ properties: {
1135
+ destination: {
1136
+ type: "string",
1137
+ description: "Destination path"
1138
+ },
1139
+ storageClass: {
1140
+ type: "string",
1141
+ description: "Target storage class"
1142
+ },
1143
+ contentType: {
1144
+ type: "string",
1145
+ description: "Override content type"
1146
+ }
1147
+ },
1148
+ required: ["destination"]
1149
+ }
1150
+ }
1151
+ ],
1152
+ discovery: { pathTemplate: "/{path}/.actions" }
1153
+ }]
1154
+ };
1155
+ return {
1156
+ id: ".capabilities",
1157
+ path: "/.meta/.capabilities",
1158
+ content: JSON.stringify(capabilities, null, 2),
1159
+ meta: { kind: "afs:capabilities" }
1160
+ };
1037
1161
  }
1038
1162
  };
1039
1163
  __decorate([List("/"), List("/:path*")], AFSGCS.prototype, "listHandler", null);
@@ -1049,7 +1173,10 @@ __decorate([Actions.Exec("/:path*", "presign-download")], AFSGCS.prototype, "sig
1049
1173
  __decorate([Actions.Exec("/:path*", "presign-upload")], AFSGCS.prototype, "signUploadActionHandler", null);
1050
1174
  __decorate([Actions.Exec("/:path*", "compose")], AFSGCS.prototype, "composeActionHandler", null);
1051
1175
  __decorate([Actions.Exec("/:path*", "rewrite")], AFSGCS.prototype, "rewriteActionHandler", null);
1176
+ __decorate([Explain("/"), Explain("/:path*")], AFSGCS.prototype, "explainHandler", null);
1177
+ __decorate([Search("/"), Search("/:path*")], AFSGCS.prototype, "searchHandler", null);
1178
+ __decorate([Read("/.meta/.capabilities")], AFSGCS.prototype, "readCapabilities", null);
1052
1179
 
1053
1180
  //#endregion
1054
- export { AFSGCS };
1181
+ export { AFSGCS, AFSGCS as default };
1055
1182
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["AFSError","entryPath","normalizedPath","files","apiResponse"],"sources":["../src/cache.ts","../src/client.ts","../src/errors.ts","../src/platform-ref.ts","../src/types.ts","../src/gcs-afs.ts"],"sourcesContent":["/**\n * GCS Response Caching\n *\n * LRU cache with TTL for GCS list and stat results.\n */\n\n/**\n * Cache entry with expiration\n */\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n}\n\n/**\n * LRU Cache with TTL support\n */\nexport class LRUCache<T> {\n private cache = new Map<string, CacheEntry<T>>();\n private maxSize: number;\n private defaultTtl: number;\n\n /**\n * Create a new LRU cache\n *\n * @param maxSize - Maximum number of entries (default: 1000)\n * @param defaultTtl - Default TTL in seconds (default: 60)\n */\n constructor(maxSize = 1000, defaultTtl = 60) {\n this.maxSize = maxSize;\n this.defaultTtl = defaultTtl;\n }\n\n /**\n * Get a value from the cache\n *\n * @param key - Cache key\n * @returns Cached value or undefined if not found/expired\n */\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n\n if (!entry) {\n return undefined;\n }\n\n // Check if expired\n if (Date.now() > entry.expiresAt) {\n this.cache.delete(key);\n return undefined;\n }\n\n // Move to end (most recently used)\n this.cache.delete(key);\n this.cache.set(key, entry);\n\n return entry.value;\n }\n\n /**\n * Set a value in the cache\n *\n * @param key - Cache key\n * @param value - Value to cache\n * @param ttl - TTL in seconds (optional, uses default)\n */\n set(key: string, value: T, ttl?: number): void {\n // Remove if already exists (to update position)\n if (this.cache.has(key)) {\n this.cache.delete(key);\n }\n\n // Evict oldest entries if at capacity\n while (this.cache.size >= this.maxSize) {\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n const expiresAt = Date.now() + (ttl ?? this.defaultTtl) * 1000;\n this.cache.set(key, { value, expiresAt });\n }\n\n /**\n * Delete a value from the cache\n *\n * @param key - Cache key\n */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /**\n * Delete all entries matching a prefix\n *\n * @param prefix - Key prefix to match\n */\n deleteByPrefix(prefix: string): void {\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key);\n }\n }\n }\n\n /**\n * Clear all entries\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the number of entries in the cache\n */\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * Prune expired entries\n */\n prune(): void {\n const now = Date.now();\n for (const [key, entry] of this.cache.entries()) {\n if (now > entry.expiresAt) {\n this.cache.delete(key);\n }\n }\n }\n}\n\n/**\n * Create a cache key from bucket, prefix, and path\n */\nexport function createCacheKey(\n bucket: string,\n prefix: string,\n path: string,\n suffix?: string,\n): string {\n const base = `${bucket}:${prefix}:${path}`;\n return suffix ? `${base}:${suffix}` : base;\n}\n","/**\n * GCS Client Factory\n *\n * Creates configured Google Cloud Storage clients.\n */\n\nimport { Storage, type StorageOptions } from \"@google-cloud/storage\";\nimport type { AFSGCSOptions } from \"./types.js\";\n\n/**\n * Create a GCS Storage client from options\n *\n * @param options - AFSGCS options\n * @returns Configured Storage client\n */\nexport function createGCSClient(options: AFSGCSOptions): Storage {\n const storageOptions: StorageOptions = {};\n\n // Project ID\n if (options.projectId) {\n storageOptions.projectId = options.projectId;\n }\n\n // Custom endpoint (for fake-gcs-server)\n if (options.endpoint) {\n storageOptions.apiEndpoint = options.endpoint;\n }\n\n // Key file authentication\n if (options.keyFilename) {\n storageOptions.keyFilename = options.keyFilename;\n }\n\n // Explicit credentials\n if (options.credentials) {\n storageOptions.credentials = {\n client_email: options.credentials.clientEmail,\n private_key: options.credentials.privateKey,\n };\n }\n\n return new Storage(storageOptions);\n}\n","/**\n * GCS Provider Error Handling\n *\n * Maps GCS SDK errors to AFS error types.\n */\n\n/**\n * AFS error codes\n */\nexport const AFSErrorCode = {\n ENTRY_NOT_FOUND: \"ENTRY_NOT_FOUND\",\n MODULE_NOT_FOUND: \"MODULE_NOT_FOUND\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n AUTH_ERROR: \"AUTH_ERROR\",\n RATE_LIMIT_EXCEEDED: \"RATE_LIMIT_EXCEEDED\",\n INVALID_OPERATION: \"INVALID_OPERATION\",\n ALREADY_EXISTS: \"ALREADY_EXISTS\",\n SERVICE_UNAVAILABLE: \"SERVICE_UNAVAILABLE\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport type AFSErrorCode = (typeof AFSErrorCode)[keyof typeof AFSErrorCode];\n\n/**\n * AFS Error class\n */\nexport class AFSError extends Error {\n readonly code: AFSErrorCode;\n readonly retryAfter?: number;\n\n constructor(code: AFSErrorCode, message: string, options?: { retryAfter?: number }) {\n super(message);\n this.name = \"AFSError\";\n this.code = code;\n this.retryAfter = options?.retryAfter;\n }\n}\n\n/**\n * Custom GCS error class for internal use\n */\nexport class GCSError extends Error {\n constructor(\n message: string,\n public readonly code: number,\n ) {\n super(message);\n this.name = \"GCSError\";\n }\n}\n\n/**\n * Map GCS HTTP status codes to AFS error codes\n */\nconst STATUS_TO_AFS_ERROR: Record<number, AFSErrorCode> = {\n 400: AFSErrorCode.INVALID_OPERATION,\n 401: AFSErrorCode.AUTH_ERROR,\n 403: AFSErrorCode.PERMISSION_DENIED,\n 404: AFSErrorCode.ENTRY_NOT_FOUND,\n 409: AFSErrorCode.ALREADY_EXISTS,\n 429: AFSErrorCode.RATE_LIMIT_EXCEEDED,\n 503: AFSErrorCode.SERVICE_UNAVAILABLE,\n};\n\n/**\n * Map GCS error to AFS error\n *\n * @param error - GCS SDK error or generic error\n * @returns AFSError with appropriate error code\n */\nexport function mapGCSError(error: unknown): AFSError {\n // Handle GCSError\n if (error instanceof GCSError) {\n const afsCode = STATUS_TO_AFS_ERROR[error.code] ?? AFSErrorCode.UNKNOWN;\n return new AFSError(afsCode, error.message);\n }\n\n // Handle GCS SDK ApiError format (has code property)\n if (typeof error === \"object\" && error !== null && \"code\" in error) {\n const err = error as { code: number; message?: string };\n const statusCode = typeof err.code === \"number\" ? err.code : 500;\n const message = err.message ?? \"Unknown GCS error\";\n const afsCode = STATUS_TO_AFS_ERROR[statusCode] ?? AFSErrorCode.UNKNOWN;\n return new AFSError(afsCode, message);\n }\n\n // Handle generic Error\n if (error instanceof Error) {\n return new AFSError(AFSErrorCode.UNKNOWN, error.message);\n }\n\n // Unknown error type\n return new AFSError(AFSErrorCode.UNKNOWN, String(error));\n}\n","/**\n * GCS Platform Reference\n *\n * Generates Google Cloud Console URLs for GCS objects and directories.\n */\n\n/**\n * Platform reference containing console URL\n */\nexport interface GCSPlatformRef {\n consoleUrl: string;\n}\n\n/**\n * Generate platform reference with GCP Console URL\n *\n * @param bucket - GCS bucket name\n * @param prefix - Object prefix/key (without leading slash)\n * @param isDirectory - Whether this is a directory (prefix)\n * @returns Platform reference with console URL\n */\nexport function generatePlatformRef(\n bucket: string,\n prefix: string,\n isDirectory: boolean,\n): GCSPlatformRef {\n // GCS Console URL format:\n // Objects: https://console.cloud.google.com/storage/browser/_details/{bucket}/{path}\n // Folders: https://console.cloud.google.com/storage/browser/{bucket}/{path}\n\n if (isDirectory) {\n // Directory URL\n const normalizedPrefix = prefix.endsWith(\"/\") ? prefix : prefix ? `${prefix}/` : \"\";\n return {\n consoleUrl: `https://console.cloud.google.com/storage/browser/${bucket}/${normalizedPrefix}`,\n };\n }\n\n // File URL\n const encodedPrefix = encodeURIComponent(prefix).replace(/%2F/g, \"/\");\n return {\n consoleUrl: `https://console.cloud.google.com/storage/browser/_details/${bucket}/${encodedPrefix}`,\n };\n}\n","/**\n * AFS GCS Provider Types\n */\n\nimport { camelize, optionalize } from \"@aigne/afs/utils/zod\";\nimport { z } from \"zod\";\n\n/**\n * Configuration options for AFSGCS\n */\nexport interface AFSGCSOptions {\n /** Module name (used as mount path segment) */\n name?: string;\n\n /** Module description */\n description?: string;\n\n /** GCS bucket name */\n bucket: string;\n\n /** Key prefix (optional) */\n prefix?: string;\n\n /** GCP project ID */\n projectId?: string;\n\n /** Access mode */\n accessMode?: \"readonly\" | \"readwrite\";\n\n /** Custom endpoint for GCS-compatible services (fake-gcs-server) */\n endpoint?: string;\n\n /** Path to service account key file */\n keyFilename?: string;\n\n /** Explicit credentials (for programmatic access) */\n credentials?: {\n clientEmail: string;\n privateKey: string;\n };\n\n /** Cache TTL in seconds (0 = no cache) */\n cacheTtl?: number;\n}\n\n/**\n * GCS bucket name validation regex\n * - 3-63 characters\n * - lowercase letters, numbers, hyphens, dots (but not consecutive dots)\n * - must start and end with letter or number\n */\nconst BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;\n\n/**\n * Additional validation for bucket names\n */\nfunction isValidBucketName(name: string): boolean {\n // Check basic regex\n if (!BUCKET_NAME_REGEX.test(name)) return false;\n\n // No consecutive dots\n if (name.includes(\"..\")) return false;\n\n // No underscores\n if (name.includes(\"_\")) return false;\n\n return true;\n}\n\n/**\n * Zod schema for options validation\n */\nexport const afsgcsOptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n description: optionalize(z.string()),\n bucket: z.string().refine(isValidBucketName, \"Invalid GCS bucket name\"),\n prefix: optionalize(z.string()),\n projectId: optionalize(z.string()),\n accessMode: optionalize(z.enum([\"readonly\", \"readwrite\"])),\n endpoint: optionalize(z.string().url()),\n keyFilename: optionalize(z.string()),\n credentials: optionalize(\n z.object({\n clientEmail: z.string(),\n privateKey: z.string(),\n }),\n ),\n cacheTtl: optionalize(z.number().int().min(0)),\n })\n .strict(),\n);\n\n/**\n * Parsed GCS URI\n */\nexport interface ParsedGCSUri {\n bucket: string;\n prefix: string;\n}\n","/**\n * AFS GCS Provider\n *\n * GCS provider using AFSBaseProvider decorator routing pattern.\n * Provides access to Google Cloud Storage through AFS.\n */\n\nimport {\n Actions,\n type AFSAccessMode,\n AFSBaseProvider,\n type AFSDeleteResult,\n type AFSEntry,\n AFSError,\n type AFSExecResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n type AFSStatResult,\n type AFSWriteEntryPayload,\n type AFSWriteResult,\n Delete,\n List,\n Meta,\n Read,\n type RouteContext,\n Stat,\n Write,\n} from \"@aigne/afs\";\nimport { zodParse } from \"@aigne/afs/utils/zod\";\nimport type { Bucket, Storage } from \"@google-cloud/storage\";\nimport { createCacheKey, LRUCache } from \"./cache.js\";\nimport { createGCSClient } from \"./client.js\";\nimport { mapGCSError } from \"./errors.js\";\nimport { generatePlatformRef } from \"./platform-ref.js\";\nimport { type AFSGCSOptions, afsgcsOptionsSchema } from \"./types.js\";\n\n/**\n * Default URL expiration time (1 hour)\n */\nconst DEFAULT_EXPIRES_IN = 3600;\n\n/**\n * Maximum expiration time (7 days)\n */\nconst MAX_EXPIRES_IN = 604800;\n\n/**\n * Maximum sources for compose operation\n */\nconst MAX_COMPOSE_SOURCES = 32;\n\n/**\n * AFSGCS Provider using Base Provider pattern\n *\n * Provides access to Google Cloud Storage through AFS.\n * Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions).\n *\n * @example\n * ```typescript\n * const gcs = new AFSGCS({\n * bucket: \"my-bucket\",\n * prefix: \"data\",\n * projectId: \"my-project\",\n * });\n *\n * // Mount to AFS\n * afs.mount(gcs);\n *\n * // List objects\n * const result = await afs.list(\"/modules/my-bucket/data\");\n *\n * // Read object\n * const content = await afs.read(\"/modules/my-bucket/data/file.json\");\n * ```\n */\nexport class AFSGCS extends AFSBaseProvider {\n override readonly name: string;\n override readonly description?: string;\n override readonly accessMode: AFSAccessMode;\n\n private options: Required<Pick<AFSGCSOptions, \"bucket\">> & AFSGCSOptions;\n private storage: Storage;\n private bucket: Bucket;\n private listCache?: LRUCache<AFSListResult>;\n private statCache?: LRUCache<AFSEntry>;\n\n constructor(options: AFSGCSOptions) {\n super();\n\n // Validate options\n const parsed = afsgcsOptionsSchema.parse(options);\n\n this.options = {\n ...parsed,\n bucket: parsed.bucket,\n prefix: parsed.prefix ?? \"\",\n accessMode: parsed.accessMode ?? \"readonly\",\n };\n\n this.name = parsed.name ?? parsed.bucket;\n this.description = parsed.description ?? `GCS bucket: ${parsed.bucket}`;\n this.accessMode = this.options.accessMode ?? \"readonly\";\n\n // Create GCS client\n this.storage = createGCSClient(this.options);\n this.bucket = this.storage.bucket(parsed.bucket);\n\n // Initialize caches if cacheTtl is set\n if (parsed.cacheTtl && parsed.cacheTtl > 0) {\n this.listCache = new LRUCache<AFSListResult>(1000, parsed.cacheTtl);\n this.statCache = new LRUCache<AFSEntry>(5000, parsed.cacheTtl);\n }\n }\n\n /**\n * Schema for configuration validation\n */\n static schema() {\n return afsgcsOptionsSchema;\n }\n\n /**\n * Load from configuration file\n */\n static async load(params: AFSModuleLoadParams): Promise<AFSGCS> {\n const options = zodParse(afsgcsOptionsSchema, params.parsed, { prefix: params.filepath });\n return new AFSGCS(options);\n }\n\n // ========== Helper Methods ==========\n\n /**\n * Build the full GCS key from a path\n */\n private buildGCSKey(path: string): string {\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n return this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n }\n\n /**\n * Generate a unique ID for a GCS object\n */\n private generateId(key: string): string {\n return `gs://${this.options.bucket}/${key}`;\n }\n\n /**\n * Invalidate caches for a given path\n */\n private invalidateCache(path: string): void {\n if (this.statCache) {\n const statKey = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", path);\n this.statCache.delete(statKey);\n }\n\n if (this.listCache) {\n // Invalidate list cache for parent directory\n const parentPath = path.split(\"/\").slice(0, -1).join(\"/\") || \"/\";\n const listPrefix = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", parentPath);\n this.listCache.deleteByPrefix(listPrefix);\n }\n }\n\n /**\n * Clear all caches\n */\n clearCache(): void {\n this.listCache?.clear();\n this.statCache?.clear();\n }\n\n // ========== List Operations ==========\n\n @List(\"/\")\n @List(\"/:path*\")\n async listHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSListResult> {\n try {\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n\n // Build the full GCS prefix\n const fullPrefix = this.options.prefix\n ? normalizedPath\n ? `${this.options.prefix}/${normalizedPath}/`\n : `${this.options.prefix}/`\n : normalizedPath\n ? `${normalizedPath}/`\n : \"\";\n\n const maxChildren = (ctx.options as { limit?: number } | undefined)?.limit ?? 1000;\n\n // Check cache first\n if (this.listCache) {\n const cacheKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n normalizedPath,\n JSON.stringify(ctx.options ?? {}),\n );\n const cached = this.listCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n }\n\n const result = await this.listWithDelimiter(fullPrefix, normalizedPath, maxChildren);\n\n // Cache the result\n if (this.listCache) {\n const cacheKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n normalizedPath,\n JSON.stringify(ctx.options ?? {}),\n );\n this.listCache.set(cacheKey, result);\n\n // Also populate stat cache from list results\n if (this.statCache) {\n for (const entry of result.data) {\n const statKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n entry.path,\n );\n this.statCache.set(statKey, entry);\n }\n }\n }\n\n return result;\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n /**\n * List with delimiter (single level)\n */\n private async listWithDelimiter(\n prefix: string,\n basePath: string,\n maxChildren: number,\n ): Promise<AFSListResult> {\n const childEntries: AFSEntry[] = [];\n const bucketName = this.options.bucket;\n\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix,\n delimiter: \"/\",\n maxResults: maxChildren,\n });\n\n // Add directories from prefixes\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n if (prefixes) {\n for (const dirPrefix of prefixes) {\n if (!dirPrefix) continue;\n\n // Extract directory name\n const dirName = dirPrefix.slice(prefix.length).replace(/\\/$/, \"\");\n if (!dirName) continue;\n\n const entryPath = basePath ? `${basePath}/${dirName}` : dirName;\n const normalizedPath = entryPath.startsWith(\"/\") ? entryPath : `/${entryPath}`;\n\n childEntries.push({\n id: this.generateId(dirPrefix),\n path: normalizedPath,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(bucketName, dirPrefix, true),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n // Add files from Contents\n for (const file of files) {\n if (!file.name) continue;\n\n // Skip the prefix itself (if it's a \"directory marker\")\n if (file.name === prefix) continue;\n\n // Handle directory markers (ending with /)\n if (file.name.endsWith(\"/\")) {\n const dirName = file.name.slice(prefix.length).replace(/\\/$/, \"\");\n if (!dirName) continue;\n\n const entryPath = basePath ? `${basePath}/${dirName}` : dirName;\n const normalizedPath = entryPath.startsWith(\"/\") ? entryPath : `/${entryPath}`;\n\n childEntries.push({\n id: this.generateId(file.name),\n path: normalizedPath,\n updatedAt: file.metadata.updated ? new Date(file.metadata.updated as string) : undefined,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(bucketName, file.name, true),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n continue;\n }\n\n // Extract file name\n const fileName = file.name.slice(prefix.length);\n if (!fileName || fileName.includes(\"/\")) continue;\n\n const entryPath = basePath ? `${basePath}/${fileName}` : fileName;\n const normalizedPath = entryPath.startsWith(\"/\") ? entryPath : `/${entryPath}`;\n\n childEntries.push({\n id: this.generateId(file.name),\n path: normalizedPath,\n updatedAt: file.metadata.updated ? new Date(file.metadata.updated as string) : undefined,\n metadata: {\n size: file.metadata.size ? Number(file.metadata.size) : undefined,\n contentType: file.metadata.contentType,\n lastModified: file.metadata.updated,\n etag: file.metadata.etag,\n storageClass: file.metadata.storageClass,\n platformRef: generatePlatformRef(bucketName, file.name, false),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n\n // Build result with self entry first\n const selfPath = basePath ? (basePath.startsWith(\"/\") ? basePath : `/${basePath}`) : \"/\";\n\n // For non-root paths with no children, check if the path exists\n if (childEntries.length === 0 && basePath) {\n // Check if this path exists as a file\n const key = this.options.prefix ? `${this.options.prefix}/${basePath}` : basePath;\n const file = this.bucket.file(key);\n const [fileExists] = await file.exists();\n\n if (fileExists) {\n // Path exists as an object (file)\n const [metadata] = await file.getMetadata();\n return {\n data: [\n {\n id: this.generateId(key),\n path: selfPath,\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n metadata: {\n size: metadata.size ? Number(metadata.size) : undefined,\n contentType: metadata.contentType,\n lastModified: metadata.updated,\n etag: metadata.etag,\n childrenCount: 0,\n platformRef: generatePlatformRef(bucketName, key, false),\n },\n },\n ],\n };\n }\n\n // Check for directory marker\n const dirMarker = this.bucket.file(`${key}/`);\n const [dirExists] = await dirMarker.exists();\n\n if (!dirExists) {\n // Neither file nor directory marker exists\n throw new AFSNotFoundError(selfPath);\n }\n }\n\n // Create self entry (the directory itself)\n const selfEntry: AFSEntry = {\n id: this.generateId(prefix || \"/\"),\n path: selfPath,\n metadata: {\n kind: \"afs:node\",\n childrenCount: childEntries.length,\n platformRef: generatePlatformRef(bucketName, prefix, true),\n },\n };\n\n return { data: [selfEntry, ...childEntries] };\n }\n\n // ========== Versioning List ==========\n\n @List(\"/:path*/@versions\")\n async listVersionsHandler(ctx: RouteContext<{ path: string }>): Promise<AFSListResult> {\n try {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n // Get all generations of the file\n const [files] = await this.bucket.getFiles({\n prefix: key,\n versions: true,\n });\n\n const entries: AFSEntry[] = [];\n\n for (const versionFile of files) {\n // Only include exact key matches\n if (versionFile.name !== key) continue;\n\n const generation = versionFile.metadata.generation;\n if (!generation) continue;\n\n const versionPath = `/${normalizedPath}/@versions/${generation}`;\n const isLive = !versionFile.metadata.timeDeleted;\n\n entries.push({\n id: `${this.generateId(key)}:${generation}`,\n path: versionPath,\n updatedAt: versionFile.metadata.timeCreated\n ? new Date(versionFile.metadata.timeCreated as string)\n : undefined,\n metadata: {\n generation: generation,\n isLive,\n timeCreated: versionFile.metadata.timeCreated,\n size: versionFile.metadata.size ? Number(versionFile.metadata.size) : 0,\n etag: versionFile.metadata.etag,\n },\n });\n }\n\n return { data: entries };\n } catch (error) {\n throw mapGCSError(error);\n }\n }\n\n // ========== Read Operations ==========\n\n @Read(\"/:path*\")\n async readHandler(ctx: RouteContext<{ path: string }>): Promise<AFSEntry> {\n try {\n const key = this.buildGCSKey(ctx.params.path);\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n const bucketName = this.options.bucket;\n\n // Handle root path - return bucket metadata with childrenCount\n if (!key) {\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n delimiter: \"/\",\n maxResults: 1000,\n });\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n const childrenCount = (prefixes?.length ?? 0) + files.length;\n\n return {\n id: this.generateId(\"/\"),\n path: \"/\",\n content: \"\",\n metadata: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(bucketName, \"\", true),\n },\n };\n }\n\n const file = this.bucket.file(key);\n\n // Check if file exists\n const [exists] = await file.exists();\n\n if (exists) {\n const [metadata] = await file.getMetadata();\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || metadata.contentType === \"application/x-directory\") {\n // Return directory info instead of throwing\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: \"\",\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Download content\n const [content] = await file.download();\n\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: content.toString(\"utf-8\"),\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n metadata: {\n size: metadata.size ? Number(metadata.size) : undefined,\n mimeType: metadata.contentType,\n contentType: metadata.contentType,\n lastModified: metadata.updated,\n etag: metadata.etag,\n },\n };\n }\n\n // File doesn't exist, check if it's a directory prefix\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix: `${key}/`,\n delimiter: \"/\",\n maxResults: 1000,\n });\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n const hasChildren = files.length > 0 || (prefixes && prefixes.length > 0);\n\n if (hasChildren) {\n // It's a directory - return directory info\n const childrenCount = (prefixes?.length ?? 0) + files.length;\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n metadata: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n const dirMarker = this.bucket.file(`${key}/`);\n const [dirExists] = await dirMarker.exists();\n\n if (dirExists) {\n const [dirMetadata] = await dirMarker.getMetadata();\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated as string) : undefined,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Neither file nor directory exists\n throw new AFSNotFoundError(`/${ctx.params.path}`);\n } catch (error) {\n if (error instanceof AFSError || error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Version Read ==========\n\n @Read(\"/:path*/@versions/:generation\")\n async readVersionHandler(\n ctx: RouteContext<{ path: string; generation: string }>,\n ): Promise<AFSEntry> {\n try {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n const generation = ctx.params.generation;\n\n const file = this.bucket.file(key, { generation: parseInt(generation, 10) });\n\n // Check if version exists\n const [exists] = await file.exists();\n if (!exists) {\n throw new AFSNotFoundError(`/${normalizedPath}/@versions/${generation}`);\n }\n\n const [content] = await file.download();\n const [metadata] = await file.getMetadata();\n\n return {\n id: `${this.generateId(key)}:${generation}`,\n path: ctx.path,\n content: content.toString(\"utf-8\"),\n updatedAt: metadata.timeCreated ? new Date(metadata.timeCreated as string) : undefined,\n metadata: {\n size: metadata.size ? Number(metadata.size) : undefined,\n contentType: metadata.contentType,\n timeCreated: metadata.timeCreated,\n etag: metadata.etag,\n generation: metadata.generation,\n },\n };\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Meta Operations ==========\n\n @Meta(\"/\")\n @Meta(\"/:path*\")\n async metaHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSEntry> {\n try {\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix\n ? normalizedPath\n ? `${this.options.prefix}/${normalizedPath}`\n : this.options.prefix\n : normalizedPath;\n\n const bucketName = this.options.bucket;\n\n // Root metadata\n if (!key) {\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n delimiter: \"/\",\n maxResults: 1000,\n });\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n const childrenCount = (prefixes?.length ?? 0) + files.length;\n\n return {\n id: this.generateId(\"/\"),\n path: \"/.meta\",\n metadata: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(bucketName, \"\", true),\n },\n };\n }\n\n // Check cache first\n if (this.statCache) {\n const cacheKey = createCacheKey(bucketName, this.options.prefix ?? \"\", path);\n const cached = this.statCache.get(cacheKey);\n if (cached) {\n return {\n id: cached.id,\n path: ctx.path,\n metadata: cached.metadata,\n };\n }\n }\n\n // Try to get the object directly (file case)\n const file = this.bucket.file(key);\n const [exists] = await file.exists();\n\n if (exists) {\n const [metadata] = await file.getMetadata();\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || metadata.contentType === \"application/x-directory\") {\n return {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n const result = {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n metadata: {\n kind: \"afs:document\",\n size: metadata.size ? Number(metadata.size) : undefined,\n contentType: metadata.contentType,\n lastModified: metadata.updated,\n etag: metadata.etag,\n storageClass: metadata.storageClass,\n // GCS-specific fields\n generation: metadata.generation,\n metageneration: metadata.metageneration,\n crc32c: metadata.crc32c,\n md5Hash: metadata.md5Hash,\n platformRef: generatePlatformRef(bucketName, key, false),\n },\n };\n\n // Cache the result\n if (this.statCache) {\n const cacheKey = createCacheKey(bucketName, this.options.prefix ?? \"\", path);\n this.statCache.set(cacheKey, result);\n }\n\n return result;\n }\n\n // File doesn't exist, check if it's a directory prefix\n const [files] = await this.bucket.getFiles({\n prefix: `${key}/`,\n maxResults: 1,\n });\n\n if (files.length > 0) {\n // There are objects under this prefix, so it's a directory\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n const dirMarker = this.bucket.file(`${key}/`);\n const [dirExists] = await dirMarker.exists();\n\n if (dirExists) {\n const [dirMetadata] = await dirMarker.getMetadata();\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated as string) : undefined,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Neither file nor directory exists\n throw new AFSNotFoundError(path.startsWith(\"/\") ? path : `/${path}`);\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Stat Operations ==========\n\n @Stat(\"/\")\n @Stat(\"/:path*\")\n async statHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSStatResult> {\n // Delegate to meta handler and convert to stat result\n const metaEntry = await this.metaHandler({\n ...ctx,\n path: ctx.path.endsWith(\"/.meta\") ? ctx.path : `${ctx.path}/.meta`,\n });\n\n return {\n data: {\n path: ctx.path,\n size: metaEntry.metadata?.size as number | undefined,\n childrenCount: metaEntry.metadata?.childrenCount as number | undefined,\n meta: metaEntry.metadata as Record<string, unknown>,\n },\n };\n }\n\n // ========== Write Operations ==========\n\n @Write(\"/:path*\")\n async writeHandler(\n ctx: RouteContext<{ path: string }>,\n payload: AFSWriteEntryPayload,\n ): Promise<AFSWriteResult> {\n try {\n const key = this.buildGCSKey(ctx.params.path);\n const file = this.bucket.file(key);\n\n // Prepare content\n let content: string | Buffer;\n if (typeof payload.content === \"string\") {\n content = payload.content;\n } else if (Buffer.isBuffer(payload.content)) {\n content = payload.content;\n } else if (payload.content !== undefined) {\n // JSON content\n content = JSON.stringify(payload.content);\n } else {\n content = \"\";\n }\n\n // Prepare save options\n // Disable validation for fake-gcs-server compatibility (it doesn't properly support CRC32C)\n const saveOptions: {\n contentType?: string;\n metadata?: Record<string, string>;\n validation?: boolean;\n } = {\n validation: false,\n };\n if (payload.metadata?.mimeType || payload.metadata?.contentType) {\n saveOptions.contentType =\n (payload.metadata.mimeType as string) ?? (payload.metadata.contentType as string);\n }\n\n // Write content\n await file.save(content, saveOptions);\n\n // Get updated metadata\n const [metadata] = await file.getMetadata();\n\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n\n // Invalidate caches for the written path\n this.invalidateCache(ctx.params.path);\n\n return {\n data: {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: payload.content,\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : new Date(),\n metadata: {\n size: metadata.size ? Number(metadata.size) : 0,\n etag: metadata.etag,\n generation: metadata.generation,\n ...payload.metadata,\n },\n },\n };\n } catch (error) {\n throw mapGCSError(error);\n }\n }\n\n // ========== Delete Operations ==========\n\n @Delete(\"/:path*\")\n async deleteHandler(ctx: RouteContext<{ path: string }>): Promise<AFSDeleteResult> {\n try {\n const key = this.buildGCSKey(ctx.params.path);\n const file = this.bucket.file(key);\n const [exists] = await file.exists();\n\n if (exists) {\n // It's a file, delete it directly\n await file.delete();\n\n // Invalidate caches for the deleted path\n this.invalidateCache(ctx.params.path);\n\n return {\n message: `Successfully deleted: ${ctx.params.path}`,\n };\n }\n\n // Check if it's a directory (has children)\n const [files] = await this.bucket.getFiles({\n prefix: `${key}/`,\n maxResults: 1,\n });\n\n if (files.length === 0) {\n throw new AFSNotFoundError(`/${ctx.params.path}`);\n }\n\n // Directory with children - need recursive option\n throw new AFSError(\n `Cannot delete non-empty directory: ${ctx.params.path}. Use recursive option.`,\n \"AFS_INVALID_OPERATION\",\n );\n } catch (error) {\n if (error instanceof AFSError || error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Action System ==========\n\n @Actions(\"/:path*\")\n async listActionsHandler(ctx: RouteContext<{ path: string }>): Promise<AFSListResult> {\n const basePath = ctx.path.replace(/\\/\\.actions$/, \"\");\n return {\n data: [\n {\n id: \"presign-download\",\n path: `${basePath}/.actions/presign-download`,\n summary: \"Generate signed download URL\",\n metadata: { kind: \"afs:executable\", kinds: [\"afs:executable\", \"afs:node\"] },\n },\n {\n id: \"presign-upload\",\n path: `${basePath}/.actions/presign-upload`,\n summary: \"Generate signed upload URL\",\n metadata: { kind: \"afs:executable\", kinds: [\"afs:executable\", \"afs:node\"] },\n },\n {\n id: \"compose\",\n path: `${basePath}/.actions/compose`,\n summary: \"Compose multiple objects into one\",\n metadata: { kind: \"afs:executable\", kinds: [\"afs:executable\", \"afs:node\"] },\n },\n {\n id: \"rewrite\",\n path: `${basePath}/.actions/rewrite`,\n summary: \"Rewrite object to new location\",\n metadata: { kind: \"afs:executable\", kinds: [\"afs:executable\", \"afs:node\"] },\n },\n ],\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-download\")\n async signDownloadActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n const file = this.bucket.file(key);\n\n let expiresIn = (args.expiresIn as number) ?? DEFAULT_EXPIRES_IN;\n if (expiresIn > MAX_EXPIRES_IN) expiresIn = MAX_EXPIRES_IN;\n if (expiresIn < 1) expiresIn = 1;\n\n const expiresAt = Date.now() + expiresIn * 1000;\n\n const [url] = await file.getSignedUrl({\n action: \"read\",\n expires: expiresAt,\n });\n\n return {\n success: true,\n data: {\n url,\n expiresAt: new Date(expiresAt).toISOString(),\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-upload\")\n async signUploadActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n const file = this.bucket.file(key);\n\n let expiresIn = (args.expiresIn as number) ?? DEFAULT_EXPIRES_IN;\n if (expiresIn > MAX_EXPIRES_IN) expiresIn = MAX_EXPIRES_IN;\n if (expiresIn < 1) expiresIn = 1;\n\n const expiresAt = Date.now() + expiresIn * 1000;\n const contentType = (args.contentType as string) ?? \"application/octet-stream\";\n\n const [url] = await file.getSignedUrl({\n action: \"write\",\n expires: expiresAt,\n contentType,\n });\n\n return {\n success: true,\n data: {\n url,\n expiresAt: new Date(expiresAt).toISOString(),\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"compose\")\n async composeActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const destinationKey = this.options.prefix\n ? `${this.options.prefix}/${normalizedPath}`\n : normalizedPath;\n\n const sources = args.sources as string[];\n if (!sources || !Array.isArray(sources) || sources.length < 2) {\n throw new AFSError(\"compose requires at least 2 source objects\", \"AFS_INVALID_ARGUMENT\");\n }\n\n if (sources.length > MAX_COMPOSE_SOURCES) {\n throw new AFSError(\n `compose supports maximum ${MAX_COMPOSE_SOURCES} sources`,\n \"AFS_INVALID_ARGUMENT\",\n );\n }\n\n // Build source file references\n const sourceFiles = sources.map((source) => {\n const sourcePath = source.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const sourceKey = this.options.prefix ? `${this.options.prefix}/${sourcePath}` : sourcePath;\n return this.bucket.file(sourceKey);\n });\n\n const destinationFile = this.bucket.file(destinationKey);\n\n // Compose the files using bucket.combine()\n await this.bucket.combine(sourceFiles, destinationFile);\n\n // Get metadata of the composed file\n const [metadata] = await destinationFile.getMetadata();\n\n // Invalidate cache for destination\n this.invalidateCache(ctx.params.path);\n\n return {\n success: true,\n data: {\n generation: metadata.generation,\n size: metadata.size ? Number(metadata.size) : 0,\n etag: metadata.etag,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"rewrite\")\n async rewriteActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const sourceKey = this.options.prefix\n ? `${this.options.prefix}/${normalizedPath}`\n : normalizedPath;\n\n const destination = args.destination as string;\n if (!destination) {\n throw new AFSError(\"rewrite requires destination path\", \"AFS_INVALID_ARGUMENT\");\n }\n\n const destPath = destination.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const destKey = this.options.prefix ? `${this.options.prefix}/${destPath}` : destPath;\n\n const sourceFile = this.bucket.file(sourceKey);\n const destFile = this.bucket.file(destKey);\n\n // Copy options\n const copyOptions: { contentType?: string; metadata?: Record<string, string> } = {};\n if (args.storageClass) {\n copyOptions.metadata = { storageClass: args.storageClass as string };\n }\n if (args.contentType) {\n copyOptions.contentType = args.contentType as string;\n }\n\n // Copy (rewrite) the file\n await sourceFile.copy(destFile, copyOptions);\n\n // Get metadata of the new file\n const [metadata] = await destFile.getMetadata();\n\n // Invalidate cache for destination\n this.invalidateCache(destPath);\n\n return {\n success: true,\n data: {\n destination: `/${destPath}`,\n generation: metadata.generation,\n size: metadata.size ? Number(metadata.size) : 0,\n etag: metadata.etag,\n storageClass: metadata.storageClass,\n },\n };\n }\n\n // ========== Legacy API compatibility (for migration) ==========\n\n /**\n * Generate a signed URL for downloading an object\n * @deprecated Use action /.actions/sign-download instead\n */\n async getSignedDownloadUrl(path: string, options?: { expiresIn?: number }): Promise<string> {\n const result = await this.signDownloadActionHandler(\n { path: `/${path}`, params: { path }, options: {} },\n { expiresIn: options?.expiresIn },\n );\n return result.data!.url as string;\n }\n\n /**\n * Generate a signed URL for uploading an object\n * @deprecated Use action /.actions/sign-upload instead\n */\n async getSignedUploadUrl(\n path: string,\n options?: { expiresIn?: number; contentType?: string },\n ): Promise<string> {\n const result = await this.signUploadActionHandler(\n { path: `/${path}`, params: { path }, options: {} },\n { expiresIn: options?.expiresIn, contentType: options?.contentType },\n );\n return result.data!.url as string;\n }\n\n /**\n * List all versions (generations) of an object\n * @deprecated Use list on /:path/@versions instead\n */\n async listVersions(path: string): Promise<\n Array<{\n generation: string;\n isLive: boolean;\n timeCreated?: Date;\n size: number;\n etag?: string;\n }>\n > {\n const result = await this.listVersionsHandler({\n path: `/${path}/@versions`,\n params: { path },\n options: {},\n });\n return result.data.map((entry) => ({\n generation: entry.metadata?.generation as string,\n isLive: entry.metadata?.isLive as boolean,\n timeCreated: entry.updatedAt,\n size: (entry.metadata?.size as number) ?? 0,\n etag: entry.metadata?.etag as string | undefined,\n }));\n }\n\n /**\n * Read a specific version (generation) of an object\n * @deprecated Use read on /:path/@versions/:generation instead\n */\n async readVersion(\n path: string,\n generation: string,\n ): Promise<{ content: string; metadata: Record<string, unknown> }> {\n const result = await this.readVersionHandler({\n path: `/${path}/@versions/${generation}`,\n params: { path, generation },\n options: {},\n });\n return {\n content: result.content as string,\n metadata: result.metadata as Record<string, unknown>,\n };\n }\n\n /**\n * Create a directory marker\n * @deprecated Use write with empty content instead\n */\n async mkdir(path: string): Promise<void> {\n const key = this.buildGCSKey(path);\n const dirKey = key.endsWith(\"/\") ? key : `${key}/`;\n const file = this.bucket.file(dirKey);\n\n await file.save(\"\", { contentType: \"application/x-directory\" });\n }\n}\n\n// Type check: AFSGCS should implement AFSModuleClass\nconst _typeCheck: AFSModuleClass<AFSGCS, AFSGCSOptions> = AFSGCS;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["AFSError","files","apiResponse","response","objectCount","prefixCount","lines"],"sources":["../src/cache.ts","../src/client.ts","../src/errors.ts","../src/platform-ref.ts","../src/types.ts","../src/gcs-afs.ts"],"sourcesContent":["/**\n * GCS Response Caching\n *\n * LRU cache with TTL for GCS list and stat results.\n */\n\n/**\n * Cache entry with expiration\n */\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n}\n\n/**\n * LRU Cache with TTL support\n */\nexport class LRUCache<T> {\n private cache = new Map<string, CacheEntry<T>>();\n private maxSize: number;\n private defaultTtl: number;\n\n /**\n * Create a new LRU cache\n *\n * @param maxSize - Maximum number of entries (default: 1000)\n * @param defaultTtl - Default TTL in seconds (default: 60)\n */\n constructor(maxSize = 1000, defaultTtl = 60) {\n this.maxSize = maxSize;\n this.defaultTtl = defaultTtl;\n }\n\n /**\n * Get a value from the cache\n *\n * @param key - Cache key\n * @returns Cached value or undefined if not found/expired\n */\n get(key: string): T | undefined {\n const entry = this.cache.get(key);\n\n if (!entry) {\n return undefined;\n }\n\n // Check if expired\n if (Date.now() > entry.expiresAt) {\n this.cache.delete(key);\n return undefined;\n }\n\n // Move to end (most recently used)\n this.cache.delete(key);\n this.cache.set(key, entry);\n\n return entry.value;\n }\n\n /**\n * Set a value in the cache\n *\n * @param key - Cache key\n * @param value - Value to cache\n * @param ttl - TTL in seconds (optional, uses default)\n */\n set(key: string, value: T, ttl?: number): void {\n // Remove if already exists (to update position)\n if (this.cache.has(key)) {\n this.cache.delete(key);\n }\n\n // Evict oldest entries if at capacity\n while (this.cache.size >= this.maxSize) {\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n const expiresAt = Date.now() + (ttl ?? this.defaultTtl) * 1000;\n this.cache.set(key, { value, expiresAt });\n }\n\n /**\n * Delete a value from the cache\n *\n * @param key - Cache key\n */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /**\n * Delete all entries matching a prefix\n *\n * @param prefix - Key prefix to match\n */\n deleteByPrefix(prefix: string): void {\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key);\n }\n }\n }\n\n /**\n * Clear all entries\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the number of entries in the cache\n */\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * Prune expired entries\n */\n prune(): void {\n const now = Date.now();\n for (const [key, entry] of this.cache.entries()) {\n if (now > entry.expiresAt) {\n this.cache.delete(key);\n }\n }\n }\n}\n\n/**\n * Create a cache key from bucket, prefix, and path\n */\nexport function createCacheKey(\n bucket: string,\n prefix: string,\n path: string,\n suffix?: string,\n): string {\n const base = `${bucket}:${prefix}:${path}`;\n return suffix ? `${base}:${suffix}` : base;\n}\n","/**\n * GCS Client Factory\n *\n * Creates configured Google Cloud Storage clients.\n */\n\nimport { Storage, type StorageOptions } from \"@google-cloud/storage\";\nimport type { AFSGCSOptions } from \"./types.js\";\n\n/**\n * Create a GCS Storage client from options\n *\n * @param options - AFSGCS options\n * @returns Configured Storage client\n */\nexport function createGCSClient(options: AFSGCSOptions): Storage {\n const storageOptions: StorageOptions = {};\n\n // Project ID\n if (options.projectId) {\n storageOptions.projectId = options.projectId;\n }\n\n // Custom endpoint (for fake-gcs-server)\n if (options.endpoint) {\n storageOptions.apiEndpoint = options.endpoint;\n }\n\n // Key file authentication\n if (options.keyFilename) {\n storageOptions.keyFilename = options.keyFilename;\n }\n\n // Explicit credentials\n if (options.credentials) {\n storageOptions.credentials = {\n client_email: options.credentials.clientEmail,\n private_key: options.credentials.privateKey,\n };\n }\n\n return new Storage(storageOptions);\n}\n","/**\n * GCS Provider Error Handling\n *\n * Maps GCS SDK errors to AFS error types.\n */\n\n/**\n * AFS error codes\n */\nexport const AFSErrorCode = {\n ENTRY_NOT_FOUND: \"ENTRY_NOT_FOUND\",\n MODULE_NOT_FOUND: \"MODULE_NOT_FOUND\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n AUTH_ERROR: \"AUTH_ERROR\",\n RATE_LIMIT_EXCEEDED: \"RATE_LIMIT_EXCEEDED\",\n INVALID_OPERATION: \"INVALID_OPERATION\",\n ALREADY_EXISTS: \"ALREADY_EXISTS\",\n SERVICE_UNAVAILABLE: \"SERVICE_UNAVAILABLE\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport type AFSErrorCode = (typeof AFSErrorCode)[keyof typeof AFSErrorCode];\n\n/**\n * AFS Error class\n */\nexport class AFSError extends Error {\n readonly code: AFSErrorCode;\n readonly retryAfter?: number;\n\n constructor(code: AFSErrorCode, message: string, options?: { retryAfter?: number }) {\n super(message);\n this.name = \"AFSError\";\n this.code = code;\n this.retryAfter = options?.retryAfter;\n }\n}\n\n/**\n * Custom GCS error class for internal use\n */\nexport class GCSError extends Error {\n constructor(\n message: string,\n public readonly code: number,\n ) {\n super(message);\n this.name = \"GCSError\";\n }\n}\n\n/**\n * Map GCS HTTP status codes to AFS error codes\n */\nconst STATUS_TO_AFS_ERROR: Record<number, AFSErrorCode> = {\n 400: AFSErrorCode.INVALID_OPERATION,\n 401: AFSErrorCode.AUTH_ERROR,\n 403: AFSErrorCode.PERMISSION_DENIED,\n 404: AFSErrorCode.ENTRY_NOT_FOUND,\n 409: AFSErrorCode.ALREADY_EXISTS,\n 429: AFSErrorCode.RATE_LIMIT_EXCEEDED,\n 503: AFSErrorCode.SERVICE_UNAVAILABLE,\n};\n\n/**\n * Map GCS error to AFS error\n *\n * @param error - GCS SDK error or generic error\n * @returns AFSError with appropriate error code\n */\nexport function mapGCSError(error: unknown): AFSError {\n // Handle GCSError\n if (error instanceof GCSError) {\n const afsCode = STATUS_TO_AFS_ERROR[error.code] ?? AFSErrorCode.UNKNOWN;\n return new AFSError(afsCode, error.message);\n }\n\n // Handle GCS SDK ApiError format (has code property)\n if (typeof error === \"object\" && error !== null && \"code\" in error) {\n const err = error as { code: number; message?: string };\n const statusCode = typeof err.code === \"number\" ? err.code : 500;\n const message = err.message ?? \"Unknown GCS error\";\n const afsCode = STATUS_TO_AFS_ERROR[statusCode] ?? AFSErrorCode.UNKNOWN;\n return new AFSError(afsCode, message);\n }\n\n // Handle generic Error\n if (error instanceof Error) {\n return new AFSError(AFSErrorCode.UNKNOWN, error.message);\n }\n\n // Unknown error type\n return new AFSError(AFSErrorCode.UNKNOWN, String(error));\n}\n","/**\n * GCS Platform Reference\n *\n * Generates Google Cloud Console URLs for GCS objects and directories.\n */\n\n/**\n * Platform reference containing console URL\n */\nexport interface GCSPlatformRef {\n consoleUrl: string;\n}\n\n/**\n * Generate platform reference with GCP Console URL\n *\n * @param bucket - GCS bucket name\n * @param prefix - Object prefix/key (without leading slash)\n * @param isDirectory - Whether this is a directory (prefix)\n * @returns Platform reference with console URL\n */\nexport function generatePlatformRef(\n bucket: string,\n prefix: string,\n isDirectory: boolean,\n): GCSPlatformRef {\n // GCS Console URL format:\n // Objects: https://console.cloud.google.com/storage/browser/_details/{bucket}/{path}\n // Folders: https://console.cloud.google.com/storage/browser/{bucket}/{path}\n\n if (isDirectory) {\n // Directory URL\n const normalizedPrefix = prefix.endsWith(\"/\") ? prefix : prefix ? `${prefix}/` : \"\";\n return {\n consoleUrl: `https://console.cloud.google.com/storage/browser/${bucket}/${normalizedPrefix}`,\n };\n }\n\n // File URL\n const encodedPrefix = encodeURIComponent(prefix).replace(/%2F/g, \"/\");\n return {\n consoleUrl: `https://console.cloud.google.com/storage/browser/_details/${bucket}/${encodedPrefix}`,\n };\n}\n","/**\n * AFS GCS Provider Types\n */\n\nimport { camelize, optionalize } from \"@aigne/afs/utils/zod\";\nimport { z } from \"zod\";\n\n/**\n * Configuration options for AFSGCS\n */\nexport interface AFSGCSOptions {\n /** Module name (used as mount path segment) */\n name?: string;\n\n /** Module description */\n description?: string;\n\n /** GCS bucket name */\n bucket: string;\n\n /** Key prefix (optional) */\n prefix?: string;\n\n /** GCP project ID */\n projectId?: string;\n\n /** Access mode */\n accessMode?: \"readonly\" | \"readwrite\";\n\n /** Custom endpoint for GCS-compatible services (fake-gcs-server) */\n endpoint?: string;\n\n /** Path to service account key file */\n keyFilename?: string;\n\n /** Explicit credentials (for programmatic access) */\n credentials?: {\n clientEmail: string;\n privateKey: string;\n };\n\n /** Cache TTL in seconds (0 = no cache) */\n cacheTtl?: number;\n}\n\n/**\n * GCS bucket name validation regex\n * - 3-63 characters\n * - lowercase letters, numbers, hyphens, dots (but not consecutive dots)\n * - must start and end with letter or number\n */\nconst BUCKET_NAME_REGEX = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;\n\n/**\n * Additional validation for bucket names\n */\nfunction isValidBucketName(name: string): boolean {\n // Check basic regex\n if (!BUCKET_NAME_REGEX.test(name)) return false;\n\n // No consecutive dots\n if (name.includes(\"..\")) return false;\n\n // No underscores\n if (name.includes(\"_\")) return false;\n\n return true;\n}\n\n/**\n * Zod schema for options validation\n */\nexport const afsgcsOptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n description: optionalize(z.string()),\n bucket: z.string().refine(isValidBucketName, \"Invalid GCS bucket name\"),\n prefix: optionalize(z.string()),\n projectId: optionalize(z.string()),\n accessMode: optionalize(z.enum([\"readonly\", \"readwrite\"])),\n endpoint: optionalize(z.string().url()),\n keyFilename: optionalize(z.string()),\n credentials: optionalize(\n z.object({\n clientEmail: z.string(),\n privateKey: z.string(),\n }),\n ),\n cacheTtl: optionalize(z.number().int().min(0)),\n })\n .strict(),\n);\n\n/**\n * Parsed GCS URI\n */\nexport interface ParsedGCSUri {\n bucket: string;\n prefix: string;\n}\n","/**\n * AFS GCS Provider\n *\n * GCS provider using AFSBaseProvider decorator routing pattern.\n * Provides access to Google Cloud Storage through AFS.\n */\n\nimport {\n Actions,\n type AFSAccessMode,\n AFSBaseProvider,\n type AFSDeleteResult,\n type AFSEntry,\n AFSError,\n type AFSExecResult,\n type AFSExplainResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n type AFSSearchResult,\n type AFSStatResult,\n type AFSWriteEntryPayload,\n type AFSWriteResult,\n type CapabilitiesManifest,\n Delete,\n Explain,\n List,\n Meta,\n Read,\n type RouteContext,\n Search,\n Stat,\n Write,\n} from \"@aigne/afs\";\nimport { zodParse } from \"@aigne/afs/utils/zod\";\nimport type { Bucket, Storage } from \"@google-cloud/storage\";\nimport { joinURL } from \"ufo\";\nimport { createCacheKey, LRUCache } from \"./cache.js\";\nimport { createGCSClient } from \"./client.js\";\nimport { mapGCSError } from \"./errors.js\";\nimport { generatePlatformRef } from \"./platform-ref.js\";\nimport { type AFSGCSOptions, afsgcsOptionsSchema } from \"./types.js\";\n\n/**\n * Default URL expiration time (1 hour)\n */\nconst DEFAULT_EXPIRES_IN = 3600;\n\n/**\n * Maximum expiration time (7 days)\n */\nconst MAX_EXPIRES_IN = 604800;\n\n/**\n * Maximum sources for compose operation\n */\nconst MAX_COMPOSE_SOURCES = 32;\n\n/**\n * AFSGCS Provider using Base Provider pattern\n *\n * Provides access to Google Cloud Storage through AFS.\n * Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions).\n *\n * @example\n * ```typescript\n * const gcs = new AFSGCS({\n * bucket: \"my-bucket\",\n * prefix: \"data\",\n * projectId: \"my-project\",\n * });\n *\n * // Mount to AFS\n * afs.mount(gcs);\n *\n * // List objects\n * const result = await afs.list(\"/modules/my-bucket/data\");\n *\n * // Read object\n * const content = await afs.read(\"/modules/my-bucket/data/file.json\");\n * ```\n */\nexport class AFSGCS extends AFSBaseProvider {\n override readonly name: string;\n override readonly description?: string;\n override readonly accessMode: AFSAccessMode;\n\n private options: Required<Pick<AFSGCSOptions, \"bucket\">> & AFSGCSOptions;\n private storage: Storage;\n private bucket: Bucket;\n private listCache?: LRUCache<AFSListResult>;\n private statCache?: LRUCache<AFSEntry>;\n\n constructor(options: AFSGCSOptions) {\n super();\n\n // Validate options\n const parsed = afsgcsOptionsSchema.parse(options);\n\n this.options = {\n ...parsed,\n bucket: parsed.bucket,\n prefix: parsed.prefix ?? \"\",\n accessMode: parsed.accessMode ?? \"readonly\",\n };\n\n this.name = parsed.name ?? parsed.bucket;\n this.description = parsed.description ?? `GCS bucket: ${parsed.bucket}`;\n this.accessMode = this.options.accessMode ?? \"readonly\";\n\n // Create GCS client\n this.storage = createGCSClient(this.options);\n this.bucket = this.storage.bucket(parsed.bucket);\n\n // Initialize caches if cacheTtl is set\n if (parsed.cacheTtl && parsed.cacheTtl > 0) {\n this.listCache = new LRUCache<AFSListResult>(1000, parsed.cacheTtl);\n this.statCache = new LRUCache<AFSEntry>(5000, parsed.cacheTtl);\n }\n }\n\n /**\n * Schema for configuration validation\n */\n static schema() {\n return afsgcsOptionsSchema;\n }\n\n /**\n * Load from configuration file\n */\n static async load({ basePath, config }: AFSModuleLoadParams = {}): Promise<AFSGCS> {\n const options = zodParse(afsgcsOptionsSchema, config, { prefix: basePath });\n return new AFSGCS(options);\n }\n\n // ========== Helper Methods ==========\n\n /**\n * Build the full GCS key from a path\n */\n private buildGCSKey(path: string): string {\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n return this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n }\n\n /**\n * Generate a unique ID for a GCS object\n */\n private generateId(key: string): string {\n const cleanKey = key.replace(/^\\/+/, \"\");\n return `gs://${this.options.bucket}/${cleanKey}`;\n }\n\n /**\n * Invalidate caches for a given path\n */\n private invalidateCache(path: string): void {\n if (this.statCache) {\n const statKey = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", path);\n this.statCache.delete(statKey);\n }\n\n if (this.listCache) {\n // Invalidate list cache for parent directory\n const parentPath = path.split(\"/\").slice(0, -1).join(\"/\") || \"/\";\n const listPrefix = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", parentPath);\n this.listCache.deleteByPrefix(listPrefix);\n }\n }\n\n /**\n * Clear all caches\n */\n clearCache(): void {\n this.listCache?.clear();\n this.statCache?.clear();\n }\n\n // ========== List Operations ==========\n\n @List(\"/\")\n @List(\"/:path*\")\n async listHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSListResult> {\n try {\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n\n // Build the full GCS prefix\n const fullPrefix = this.options.prefix\n ? normalizedPath\n ? `${this.options.prefix}/${normalizedPath}/`\n : `${this.options.prefix}/`\n : normalizedPath\n ? `${normalizedPath}/`\n : \"\";\n\n const maxChildren = (ctx.options as { limit?: number } | undefined)?.limit ?? 1000;\n\n // Check cache first\n if (this.listCache) {\n const cacheKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n normalizedPath,\n JSON.stringify(ctx.options ?? {}),\n );\n const cached = this.listCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n }\n\n const result = await this.listWithDelimiter(fullPrefix, normalizedPath, maxChildren);\n\n // Cache the result\n if (this.listCache) {\n const cacheKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n normalizedPath,\n JSON.stringify(ctx.options ?? {}),\n );\n this.listCache.set(cacheKey, result);\n\n // Also populate stat cache from list results\n if (this.statCache) {\n for (const entry of result.data) {\n const statKey = createCacheKey(\n this.options.bucket,\n this.options.prefix ?? \"\",\n entry.path,\n );\n this.statCache.set(statKey, entry);\n }\n }\n }\n\n return result;\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n /**\n * List with delimiter (single level)\n */\n private async listWithDelimiter(\n prefix: string,\n basePath: string,\n maxChildren: number,\n ): Promise<AFSListResult> {\n const childEntries: AFSEntry[] = [];\n const bucketName = this.options.bucket;\n\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix,\n delimiter: \"/\",\n maxResults: maxChildren,\n });\n\n // Add directories from prefixes\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n if (prefixes) {\n for (const dirPrefix of prefixes) {\n if (!dirPrefix) continue;\n\n // Extract directory name\n const dirName = dirPrefix.slice(prefix.length).replace(/\\/$/, \"\");\n if (!dirName) continue;\n\n childEntries.push({\n id: this.generateId(dirPrefix),\n path: joinURL(\"/\", basePath, dirName),\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(bucketName, dirPrefix, true),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n // Add files from Contents\n for (const file of files) {\n if (!file.name) continue;\n\n // Skip the prefix itself (if it's a \"directory marker\")\n if (file.name === prefix) continue;\n\n // Handle directory markers (ending with /)\n if (file.name.endsWith(\"/\")) {\n const dirName = file.name.slice(prefix.length).replace(/\\/$/, \"\");\n if (!dirName) continue;\n\n childEntries.push({\n id: this.generateId(file.name),\n path: joinURL(\"/\", basePath, dirName),\n updatedAt: file.metadata.updated ? new Date(file.metadata.updated as string) : undefined,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(bucketName, file.name, true),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n continue;\n }\n\n // Extract file name\n const fileName = file.name.slice(prefix.length);\n if (!fileName || fileName.includes(\"/\")) continue;\n\n childEntries.push({\n id: this.generateId(file.name),\n path: joinURL(\"/\", basePath, fileName),\n updatedAt: file.metadata.updated ? new Date(file.metadata.updated as string) : undefined,\n meta: {\n size: file.metadata.size ? Number(file.metadata.size) : undefined,\n contentType: file.metadata.contentType,\n lastModified: file.metadata.updated,\n etag: file.metadata.etag,\n storageClass: file.metadata.storageClass,\n platformRef: generatePlatformRef(bucketName, file.name, false),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n\n const selfPath = basePath ? (basePath.startsWith(\"/\") ? basePath : `/${basePath}`) : \"/\";\n\n // For non-root paths with no children, check if the path exists\n if (childEntries.length === 0 && basePath) {\n // Check if this path exists as a file\n const key = this.options.prefix ? `${this.options.prefix}/${basePath}` : basePath;\n const file = this.bucket.file(key);\n const [fileExists] = await file.exists();\n\n if (fileExists) {\n // Path exists as an object (file), not a directory - return empty children\n return { data: [] };\n }\n\n // Check for directory marker\n const dirMarker = this.bucket.file(`${key}/`);\n const [dirExists] = await dirMarker.exists();\n\n if (!dirExists) {\n // Neither file nor directory marker exists\n throw new AFSNotFoundError(selfPath);\n }\n }\n\n return { data: childEntries };\n }\n\n // ========== Versioning List ==========\n\n @List(\"/:path*/@versions\")\n async listVersionsHandler(ctx: RouteContext<{ path: string }>): Promise<AFSListResult> {\n try {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n // Get all generations of the file\n const [files] = await this.bucket.getFiles({\n prefix: key,\n versions: true,\n });\n\n const entries: AFSEntry[] = [];\n\n for (const versionFile of files) {\n // Only include exact key matches\n if (versionFile.name !== key) continue;\n\n const generation = versionFile.metadata.generation;\n if (!generation) continue;\n\n const versionPath = joinURL(\"/\", normalizedPath, \"@versions\", String(generation));\n const isLive = !versionFile.metadata.timeDeleted;\n\n entries.push({\n id: `${this.generateId(key)}:${generation}`,\n path: versionPath,\n updatedAt: versionFile.metadata.timeCreated\n ? new Date(versionFile.metadata.timeCreated as string)\n : undefined,\n meta: {\n generation: generation,\n isLive,\n timeCreated: versionFile.metadata.timeCreated,\n size: versionFile.metadata.size ? Number(versionFile.metadata.size) : 0,\n etag: versionFile.metadata.etag,\n },\n });\n }\n\n return { data: entries };\n } catch (error) {\n throw mapGCSError(error);\n }\n }\n\n // ========== Read Operations ==========\n\n @Read(\"/:path*\")\n async readHandler(ctx: RouteContext<{ path: string }>): Promise<AFSEntry> {\n try {\n const key = this.buildGCSKey(ctx.params.path);\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n const bucketName = this.options.bucket;\n\n // Handle root path - return bucket metadata with childrenCount\n if (!key) {\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n delimiter: \"/\",\n maxResults: 1000,\n });\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n const childrenCount = (prefixes?.length ?? 0) + files.length;\n\n return {\n id: this.generateId(\"/\"),\n path: \"/\",\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(bucketName, \"\", true),\n },\n };\n }\n\n const file = this.bucket.file(key);\n\n // Check if file exists\n const [exists] = await file.exists();\n\n if (exists) {\n const [metadata] = await file.getMetadata();\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || metadata.contentType === \"application/x-directory\") {\n // Return directory info instead of throwing\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: \"\",\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Download content\n const [content] = await file.download();\n\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: content.toString(\"utf-8\"),\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n meta: {\n size: metadata.size ? Number(metadata.size) : undefined,\n mimeType: metadata.contentType,\n contentType: metadata.contentType,\n lastModified: metadata.updated,\n etag: metadata.etag,\n },\n };\n }\n\n // File doesn't exist, check if it's a directory prefix\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix: `${key}/`,\n delimiter: \"/\",\n maxResults: 1000,\n });\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n const hasChildren = files.length > 0 || (prefixes && prefixes.length > 0);\n\n if (hasChildren) {\n // It's a directory - return directory info\n const childrenCount = (prefixes?.length ?? 0) + files.length;\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n const dirMarker = this.bucket.file(`${key}/`);\n const [dirExists] = await dirMarker.exists();\n\n if (dirExists) {\n const [dirMetadata] = await dirMarker.getMetadata();\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated as string) : undefined,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Neither file nor directory exists\n throw new AFSNotFoundError(`/${ctx.params.path}`);\n } catch (error) {\n if (error instanceof AFSError || error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Version Read ==========\n\n @Read(\"/:path*/@versions/:generation\")\n async readVersionHandler(\n ctx: RouteContext<{ path: string; generation: string }>,\n ): Promise<AFSEntry> {\n try {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n const generation = ctx.params.generation;\n\n const file = this.bucket.file(key, { generation: parseInt(generation, 10) });\n\n // Check if version exists\n const [exists] = await file.exists();\n if (!exists) {\n throw new AFSNotFoundError(`/${normalizedPath}/@versions/${generation}`);\n }\n\n const [content] = await file.download();\n const [metadata] = await file.getMetadata();\n\n return {\n id: `${this.generateId(key)}:${generation}`,\n path: ctx.path,\n content: content.toString(\"utf-8\"),\n updatedAt: metadata.timeCreated ? new Date(metadata.timeCreated as string) : undefined,\n meta: {\n size: metadata.size ? Number(metadata.size) : undefined,\n contentType: metadata.contentType,\n timeCreated: metadata.timeCreated,\n etag: metadata.etag,\n generation: metadata.generation,\n },\n };\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Meta Operations ==========\n\n @Meta(\"/\")\n @Meta(\"/:path*\")\n async metaHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSEntry> {\n try {\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix\n ? normalizedPath\n ? `${this.options.prefix}/${normalizedPath}`\n : this.options.prefix\n : normalizedPath;\n\n const bucketName = this.options.bucket;\n\n // Root metadata\n if (!key) {\n const [files, , apiResponse] = await this.bucket.getFiles({\n prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n delimiter: \"/\",\n maxResults: 1000,\n });\n const prefixes = (apiResponse as Record<string, unknown>)?.prefixes as string[] | undefined;\n const childrenCount = (prefixes?.length ?? 0) + files.length;\n\n return {\n id: this.generateId(\"/\"),\n path: \"/.meta\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(bucketName, \"\", true),\n },\n };\n }\n\n // Check cache first\n if (this.statCache) {\n const cacheKey = createCacheKey(bucketName, this.options.prefix ?? \"\", path);\n const cached = this.statCache.get(cacheKey);\n if (cached) {\n return {\n id: cached.id,\n path: ctx.path,\n meta: cached.meta,\n };\n }\n }\n\n // Try to get the object directly (file case)\n const file = this.bucket.file(key);\n const [exists] = await file.exists();\n\n if (exists) {\n const [metadata] = await file.getMetadata();\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || metadata.contentType === \"application/x-directory\") {\n return {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n const result = {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : undefined,\n meta: {\n kind: \"afs:document\",\n size: metadata.size ? Number(metadata.size) : undefined,\n contentType: metadata.contentType,\n lastModified: metadata.updated,\n etag: metadata.etag,\n storageClass: metadata.storageClass,\n // GCS-specific fields\n generation: metadata.generation,\n metageneration: metadata.metageneration,\n crc32c: metadata.crc32c,\n md5Hash: metadata.md5Hash,\n platformRef: generatePlatformRef(bucketName, key, false),\n },\n };\n\n // Cache the result\n if (this.statCache) {\n const cacheKey = createCacheKey(bucketName, this.options.prefix ?? \"\", path);\n this.statCache.set(cacheKey, result);\n }\n\n return result;\n }\n\n // File doesn't exist, check if it's a directory prefix\n const [files] = await this.bucket.getFiles({\n prefix: `${key}/`,\n maxResults: 1,\n });\n\n if (files.length > 0) {\n // There are objects under this prefix, so it's a directory\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n const dirMarker = this.bucket.file(`${key}/`);\n const [dirExists] = await dirMarker.exists();\n\n if (dirExists) {\n const [dirMetadata] = await dirMarker.getMetadata();\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated as string) : undefined,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(bucketName, key, true),\n },\n };\n }\n\n // Neither file nor directory exists\n throw new AFSNotFoundError(path.startsWith(\"/\") ? path : `/${path}`);\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Stat Operations ==========\n\n @Stat(\"/\")\n @Stat(\"/:path*\")\n async statHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSStatResult> {\n // Delegate to meta handler and convert to stat result\n const metaEntry = await this.metaHandler({\n ...ctx,\n path: ctx.path.endsWith(\"/.meta\") ? ctx.path : `${ctx.path}/.meta`,\n });\n\n // Extract id from path\n const pathSegments = ctx.path.split(\"/\").filter(Boolean);\n const id = pathSegments.length > 0 ? (pathSegments[pathSegments.length - 1] as string) : \"/\";\n\n return {\n data: {\n id,\n path: ctx.path,\n meta: metaEntry.meta as Record<string, unknown>,\n },\n };\n }\n\n // ========== Write Operations ==========\n\n @Write(\"/:path*\")\n async writeHandler(\n ctx: RouteContext<{ path: string }>,\n payload: AFSWriteEntryPayload,\n ): Promise<AFSWriteResult> {\n try {\n const key = this.buildGCSKey(ctx.params.path);\n const file = this.bucket.file(key);\n\n // Prepare content\n let content: string | Buffer;\n if (typeof payload.content === \"string\") {\n content = payload.content;\n } else if (Buffer.isBuffer(payload.content)) {\n content = payload.content;\n } else if (payload.content !== undefined) {\n // JSON content\n content = JSON.stringify(payload.content);\n } else {\n content = \"\";\n }\n\n // Prepare save options\n // Disable validation for fake-gcs-server compatibility (it doesn't properly support CRC32C)\n const saveOptions: {\n contentType?: string;\n metadata?: Record<string, string>;\n validation?: boolean;\n } = {\n validation: false,\n };\n if (payload.meta?.mimeType || payload.meta?.contentType) {\n saveOptions.contentType =\n (payload.meta.mimeType as string) ?? (payload.meta.contentType as string);\n }\n\n // Write content\n await file.save(content, saveOptions);\n\n // Get updated metadata\n const [metadata] = await file.getMetadata();\n\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n\n // Invalidate caches for the written path\n this.invalidateCache(ctx.params.path);\n\n return {\n data: {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: payload.content,\n updatedAt: metadata.updated ? new Date(metadata.updated as string) : new Date(),\n meta: {\n size: metadata.size ? Number(metadata.size) : 0,\n etag: metadata.etag,\n generation: metadata.generation,\n ...payload.meta,\n },\n },\n };\n } catch (error) {\n throw mapGCSError(error);\n }\n }\n\n // ========== Delete Operations ==========\n\n @Delete(\"/:path*\")\n async deleteHandler(ctx: RouteContext<{ path: string }>): Promise<AFSDeleteResult> {\n try {\n const key = this.buildGCSKey(ctx.params.path);\n const file = this.bucket.file(key);\n const [exists] = await file.exists();\n\n if (exists) {\n // It's a file, delete it directly\n await file.delete();\n\n // Invalidate caches for the deleted path\n this.invalidateCache(ctx.params.path);\n\n return {\n message: `Successfully deleted: ${ctx.params.path}`,\n };\n }\n\n // Check if it's a directory (has children)\n const [files] = await this.bucket.getFiles({\n prefix: `${key}/`,\n maxResults: 1,\n });\n\n if (files.length === 0) {\n throw new AFSNotFoundError(`/${ctx.params.path}`);\n }\n\n // Directory with children - need recursive option\n throw new AFSError(\n `Cannot delete non-empty directory: ${ctx.params.path}. Use recursive option.`,\n \"AFS_INVALID_OPERATION\",\n );\n } catch (error) {\n if (error instanceof AFSError || error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapGCSError(error);\n }\n }\n\n // ========== Action System ==========\n\n @Actions(\"/:path*\")\n async listActionsHandler(ctx: RouteContext<{ path: string }>): Promise<AFSListResult> {\n return {\n data: [\n {\n id: \"presign-download\",\n path: joinURL(ctx.path, \"presign-download\"),\n summary: \"Generate signed download URL\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: {\n type: \"number\",\n description: \"Expiration in seconds (default: 3600, max: 604800)\",\n },\n },\n },\n },\n },\n {\n id: \"presign-upload\",\n path: joinURL(ctx.path, \"presign-upload\"),\n summary: \"Generate signed upload URL\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: { type: \"number\", description: \"Expiration in seconds\" },\n contentType: { type: \"string\", description: \"Content-Type for upload\" },\n },\n },\n },\n },\n {\n id: \"compose\",\n path: joinURL(ctx.path, \"compose\"),\n summary: \"Compose multiple objects into one\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n sources: {\n type: \"array\",\n description: \"Array of source paths (2-32)\",\n items: { type: \"string\" },\n },\n },\n required: [\"sources\"],\n },\n },\n },\n {\n id: \"rewrite\",\n path: joinURL(ctx.path, \"rewrite\"),\n summary: \"Rewrite object to new location\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n destination: { type: \"string\", description: \"Destination path\" },\n storageClass: { type: \"string\", description: \"Target storage class\" },\n contentType: { type: \"string\", description: \"Override content type\" },\n },\n required: [\"destination\"],\n },\n },\n },\n ],\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-download\")\n async signDownloadActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n const file = this.bucket.file(key);\n\n let expiresIn = (args.expiresIn as number) ?? DEFAULT_EXPIRES_IN;\n if (expiresIn > MAX_EXPIRES_IN) expiresIn = MAX_EXPIRES_IN;\n if (expiresIn < 1) expiresIn = 1;\n\n const expiresAt = Date.now() + expiresIn * 1000;\n\n const [url] = await file.getSignedUrl({\n action: \"read\",\n expires: expiresAt,\n });\n\n return {\n success: true,\n data: {\n url,\n expiresAt: new Date(expiresAt).toISOString(),\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-upload\")\n async signUploadActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n const file = this.bucket.file(key);\n\n let expiresIn = (args.expiresIn as number) ?? DEFAULT_EXPIRES_IN;\n if (expiresIn > MAX_EXPIRES_IN) expiresIn = MAX_EXPIRES_IN;\n if (expiresIn < 1) expiresIn = 1;\n\n const expiresAt = Date.now() + expiresIn * 1000;\n const contentType = (args.contentType as string) ?? \"application/octet-stream\";\n\n const [url] = await file.getSignedUrl({\n action: \"write\",\n expires: expiresAt,\n contentType,\n });\n\n return {\n success: true,\n data: {\n url,\n expiresAt: new Date(expiresAt).toISOString(),\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"compose\")\n async composeActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const destinationKey = this.options.prefix\n ? `${this.options.prefix}/${normalizedPath}`\n : normalizedPath;\n\n const sources = args.sources as string[];\n if (!sources || !Array.isArray(sources) || sources.length < 2) {\n throw new AFSError(\"compose requires at least 2 source objects\", \"AFS_INVALID_ARGUMENT\");\n }\n\n if (sources.length > MAX_COMPOSE_SOURCES) {\n throw new AFSError(\n `compose supports maximum ${MAX_COMPOSE_SOURCES} sources`,\n \"AFS_INVALID_ARGUMENT\",\n );\n }\n\n // Build source file references\n const sourceFiles = sources.map((source) => {\n const sourcePath = source.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const sourceKey = this.options.prefix ? `${this.options.prefix}/${sourcePath}` : sourcePath;\n return this.bucket.file(sourceKey);\n });\n\n const destinationFile = this.bucket.file(destinationKey);\n\n // Compose the files using bucket.combine()\n await this.bucket.combine(sourceFiles, destinationFile);\n\n // Get metadata of the composed file\n const [metadata] = await destinationFile.getMetadata();\n\n // Invalidate cache for destination\n this.invalidateCache(ctx.params.path);\n\n return {\n success: true,\n data: {\n generation: metadata.generation,\n size: metadata.size ? Number(metadata.size) : 0,\n etag: metadata.etag,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"rewrite\")\n async rewriteActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const sourceKey = this.options.prefix\n ? `${this.options.prefix}/${normalizedPath}`\n : normalizedPath;\n\n const destination = args.destination as string;\n if (!destination) {\n throw new AFSError(\"rewrite requires destination path\", \"AFS_INVALID_ARGUMENT\");\n }\n\n const destPath = destination.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const destKey = this.options.prefix ? `${this.options.prefix}/${destPath}` : destPath;\n\n const sourceFile = this.bucket.file(sourceKey);\n const destFile = this.bucket.file(destKey);\n\n // Copy options\n const copyOptions: { contentType?: string; metadata?: Record<string, string> } = {};\n if (args.storageClass) {\n copyOptions.metadata = { storageClass: args.storageClass as string };\n }\n if (args.contentType) {\n copyOptions.contentType = args.contentType as string;\n }\n\n // Copy (rewrite) the file\n await sourceFile.copy(destFile, copyOptions);\n\n // Get metadata of the new file\n const [metadata] = await destFile.getMetadata();\n\n // Invalidate cache for destination\n this.invalidateCache(destPath);\n\n return {\n success: true,\n data: {\n destination: `/${destPath}`,\n generation: metadata.generation,\n size: metadata.size ? Number(metadata.size) : 0,\n etag: metadata.etag,\n storageClass: metadata.storageClass,\n },\n };\n }\n\n // ========== Explain Operations ==========\n\n @Explain(\"/\")\n @Explain(\"/:path*\")\n async explainHandler(ctx: RouteContext<{ path?: string }>): Promise<AFSExplainResult> {\n const normalizedPath = (ctx.params.path ?? \"\").replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n\n // Root explain\n if (!normalizedPath) {\n const [files, , response] = await this.bucket.getFiles({\n prefix: this.options.prefix ? `${this.options.prefix}/` : undefined,\n delimiter: \"/\",\n maxResults: 1000,\n });\n\n const objectCount = files.length;\n const prefixCount = (response as any)?.prefixes?.length ?? 0;\n\n const lines: string[] = [];\n lines.push(`# ${this.options.bucket}`);\n lines.push(\"\");\n lines.push(`- **Type**: GCS Bucket`);\n lines.push(`- **Bucket**: ${this.options.bucket}`);\n if (this.options.prefix) {\n lines.push(`- **Prefix**: ${this.options.prefix}`);\n }\n if (this.options.projectId) {\n lines.push(`- **Project**: ${this.options.projectId}`);\n }\n lines.push(`- **Access Mode**: ${this.accessMode}`);\n lines.push(`- **Top-level Objects**: ${objectCount}`);\n lines.push(`- **Top-level Prefixes**: ${prefixCount}`);\n\n return { format: \"markdown\", content: lines.join(\"\\n\") };\n }\n\n const key = this.buildGCSKey(normalizedPath);\n\n // Try as object first\n const file = this.bucket.file(key);\n const [exists] = await file.exists();\n\n if (exists) {\n const [metadata] = await file.getMetadata();\n\n const lines: string[] = [];\n lines.push(`# ${normalizedPath}`);\n lines.push(\"\");\n lines.push(`- **Type**: Object`);\n lines.push(`- **Key**: ${key}`);\n lines.push(`- **Size**: ${metadata.size ?? 0} bytes`);\n if (metadata.contentType) lines.push(`- **Content-Type**: ${metadata.contentType}`);\n if (metadata.storageClass) lines.push(`- **Storage Class**: ${metadata.storageClass}`);\n if (metadata.updated) lines.push(`- **Last Modified**: ${metadata.updated}`);\n if (metadata.etag) lines.push(`- **ETag**: ${metadata.etag}`);\n if (metadata.generation) lines.push(`- **Generation**: ${metadata.generation}`);\n\n return { format: \"markdown\", content: lines.join(\"\\n\") };\n }\n\n // Try as prefix (directory)\n const prefix = key.endsWith(\"/\") ? key : `${key}/`;\n const [files, , response] = await this.bucket.getFiles({\n prefix,\n delimiter: \"/\",\n maxResults: 1000,\n });\n\n const objectCount = files.length;\n const prefixCount = (response as any)?.prefixes?.length ?? 0;\n\n if (objectCount === 0 && prefixCount === 0) {\n throw new AFSNotFoundError(`/${normalizedPath}`, `Path not found: ${normalizedPath}`);\n }\n\n const lines: string[] = [];\n lines.push(`# ${normalizedPath}/`);\n lines.push(\"\");\n lines.push(`- **Type**: Prefix (directory)`);\n lines.push(`- **Prefix**: ${prefix}`);\n lines.push(`- **Objects**: ${objectCount}`);\n lines.push(`- **Sub-prefixes**: ${prefixCount}`);\n\n return { format: \"markdown\", content: lines.join(\"\\n\") };\n }\n\n // ========== Search Operations ==========\n\n @Search(\"/\")\n @Search(\"/:path*\")\n async searchHandler(\n ctx: RouteContext<{ path?: string }>,\n query: string,\n ): Promise<AFSSearchResult> {\n const { minimatch } = await import(\"minimatch\");\n const path = ctx.params.path ?? \"\";\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const prefix = normalizedPath\n ? this.options.prefix\n ? `${this.options.prefix}/${normalizedPath}/`\n : `${normalizedPath}/`\n : this.options.prefix\n ? `${this.options.prefix}/`\n : \"\";\n\n const [files] = await this.bucket.getFiles({\n prefix: prefix || undefined,\n });\n\n const results: AFSEntry[] = [];\n for (const file of files) {\n const relativePath = prefix ? file.name.slice(prefix.length) : file.name;\n if (!relativePath) continue;\n\n if (minimatch(relativePath, query)) {\n const displayPath = joinURL(\"/\", normalizedPath, relativePath);\n results.push({\n id: this.generateId(file.name),\n path: displayPath,\n meta: {\n size: file.metadata?.size ? Number(file.metadata.size) : undefined,\n contentType: file.metadata?.contentType,\n lastModified: file.metadata?.updated,\n },\n });\n }\n }\n\n return { data: results };\n }\n\n // ========== Capabilities ==========\n\n @Read(\"/.meta/.capabilities\")\n async readCapabilities(_ctx: RouteContext): Promise<AFSEntry> {\n const capabilities: CapabilitiesManifest = {\n schemaVersion: 1,\n provider: \"gcs\",\n description: `GCS bucket: ${this.options.bucket}`,\n tools: [],\n operations: this.getOperationsDeclaration(),\n actions: [\n {\n description: \"GCS object actions\",\n catalog: [\n {\n name: \"presign-download\",\n description: \"Generate a signed download URL\",\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: {\n type: \"number\",\n description: \"Expiration in seconds (default: 3600, max: 604800)\",\n },\n },\n },\n },\n {\n name: \"presign-upload\",\n description: \"Generate a signed upload URL\",\n inputSchema: {\n type: \"object\",\n properties: {\n expiresIn: { type: \"number\", description: \"Expiration in seconds\" },\n contentType: { type: \"string\", description: \"Content-Type for upload\" },\n },\n },\n },\n {\n name: \"compose\",\n description: \"Compose multiple objects into one (max 32 sources)\",\n inputSchema: {\n type: \"object\",\n properties: {\n sources: {\n type: \"array\",\n description: \"Array of source paths (2-32)\",\n items: { type: \"string\" },\n },\n },\n required: [\"sources\"],\n },\n },\n {\n name: \"rewrite\",\n description: \"Copy/rewrite object to a new location\",\n inputSchema: {\n type: \"object\",\n properties: {\n destination: { type: \"string\", description: \"Destination path\" },\n storageClass: { type: \"string\", description: \"Target storage class\" },\n contentType: { type: \"string\", description: \"Override content type\" },\n },\n required: [\"destination\"],\n },\n },\n ],\n discovery: { pathTemplate: \"/{path}/.actions\" },\n },\n ],\n };\n\n return {\n id: \".capabilities\",\n path: \"/.meta/.capabilities\",\n content: JSON.stringify(capabilities, null, 2),\n meta: { kind: \"afs:capabilities\" },\n };\n }\n}\n\n// Type check: AFSGCS should implement AFSModuleClass\nconst _typeCheck: AFSModuleClass<AFSGCS, AFSGCSOptions> = AFSGCS;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/afs-gcs",
3
- "version": "1.11.0-beta.6",
3
+ "version": "1.11.0-beta.7",
4
4
  "description": "AIGNE AFS module for Google Cloud Storage",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -34,8 +34,10 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@google-cloud/storage": "^7.16.0",
37
+ "minimatch": "^10.1.1",
38
+ "ufo": "^1.6.3",
37
39
  "zod": "^3.25.67",
38
- "@aigne/afs": "^1.11.0-beta.6"
40
+ "@aigne/afs": "^1.11.0-beta.7"
39
41
  },
40
42
  "devDependencies": {
41
43
  "@types/bun": "^1.3.6",
@@ -43,8 +45,9 @@
43
45
  "rimraf": "^6.1.2",
44
46
  "tsdown": "0.20.0-beta.3",
45
47
  "typescript": "5.9.2",
46
- "@aigne/scripts": "0.0.0",
47
- "@aigne/typescript-config": "0.0.0"
48
+ "@aigne/afs-testing": "1.11.0-beta.7",
49
+ "@aigne/typescript-config": "0.0.0",
50
+ "@aigne/scripts": "0.0.0"
48
51
  },
49
52
  "scripts": {
50
53
  "build": "tsdown",