@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 +13 -42
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +275 -148
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -4
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(
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;;;;;;;;
|
|
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(
|
|
349
|
-
return new AFSGCS(zodParse(afsgcsOptionsSchema,
|
|
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
|
-
|
|
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:
|
|
431
|
-
|
|
430
|
+
path: joinURL("/", basePath, dirName),
|
|
431
|
+
meta: {
|
|
432
432
|
kind: "afs:node",
|
|
433
|
-
childrenCount:
|
|
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:
|
|
447
|
+
path: joinURL("/", basePath, dirName),
|
|
450
448
|
updatedAt: file.metadata.updated ? new Date(file.metadata.updated) : void 0,
|
|
451
|
-
|
|
449
|
+
meta: {
|
|
452
450
|
kind: "afs:node",
|
|
453
|
-
childrenCount:
|
|
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:
|
|
462
|
+
path: joinURL("/", basePath, fileName),
|
|
467
463
|
updatedAt: file.metadata.updated ? new Date(file.metadata.updated) : void 0,
|
|
468
|
-
|
|
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
|
|
483
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
550
|
+
meta: {
|
|
579
551
|
kind: "afs:node",
|
|
580
|
-
childrenCount:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
599
|
+
meta: {
|
|
628
600
|
kind: "afs:node",
|
|
629
|
-
childrenCount:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
680
|
+
meta: {
|
|
709
681
|
kind: "afs:node",
|
|
710
|
-
childrenCount:
|
|
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
|
-
|
|
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
|
-
|
|
717
|
+
meta: {
|
|
746
718
|
kind: "afs:node",
|
|
747
|
-
childrenCount:
|
|
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
|
-
|
|
731
|
+
meta: {
|
|
760
732
|
kind: "afs:node",
|
|
761
|
-
childrenCount:
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
776
|
+
meta: {
|
|
805
777
|
size: metadata.size ? Number(metadata.size) : 0,
|
|
806
778
|
etag: metadata.etag,
|
|
807
779
|
generation: metadata.generation,
|
|
808
|
-
...payload.
|
|
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:
|
|
812
|
+
path: joinURL(ctx.path, "presign-download"),
|
|
842
813
|
summary: "Generate signed download URL",
|
|
843
|
-
|
|
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:
|
|
828
|
+
path: joinURL(ctx.path, "presign-upload"),
|
|
851
829
|
summary: "Generate signed upload URL",
|
|
852
|
-
|
|
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:
|
|
850
|
+
path: joinURL(ctx.path, "compose"),
|
|
860
851
|
summary: "Compose multiple objects into one",
|
|
861
|
-
|
|
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:
|
|
868
|
+
path: joinURL(ctx.path, "rewrite"),
|
|
869
869
|
summary: "Rewrite object to new location",
|
|
870
|
-
|
|
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
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
etag
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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
|
-
|
|
1026
|
-
|
|
1050
|
+
format: "markdown",
|
|
1051
|
+
content: lines.join("\n")
|
|
1027
1052
|
};
|
|
1028
1053
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
const
|
|
1035
|
-
const
|
|
1036
|
-
|
|
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
|
package/dist/index.mjs.map
CHANGED
|
@@ -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.
|
|
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.
|
|
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/
|
|
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",
|