@aigne/afs-gcs 1.11.0-beta.6 → 1.11.0-beta.8
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 +43 -96
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +301 -150
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { AFSAccessMode, AFSBaseProvider, AFSDeleteResult, AFSEntry, AFSExecResult, AFSListResult, AFSModuleLoadParams, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult, RouteContext } from "@aigne/afs";
|
|
2
|
-
import * as zod0 from "zod";
|
|
1
|
+
import { AFSAccessMode, AFSBaseProvider, AFSDeleteResult, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchResult, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult, ProviderManifest, RouteContext } from "@aigne/afs";
|
|
3
2
|
import { z } from "zod";
|
|
4
3
|
|
|
5
4
|
//#region src/types.d.ts
|
|
@@ -73,74 +72,54 @@ declare class AFSGCS extends AFSBaseProvider {
|
|
|
73
72
|
private bucket;
|
|
74
73
|
private listCache?;
|
|
75
74
|
private statCache?;
|
|
76
|
-
constructor(options: AFSGCSOptions
|
|
75
|
+
constructor(options: AFSGCSOptions & {
|
|
76
|
+
uri?: string;
|
|
77
|
+
token?: string;
|
|
78
|
+
auth?: unknown;
|
|
79
|
+
});
|
|
77
80
|
/**
|
|
78
81
|
* Schema for configuration validation
|
|
79
82
|
*/
|
|
80
|
-
static schema():
|
|
81
|
-
name:
|
|
82
|
-
description:
|
|
83
|
-
bucket: zod0.ZodEffects<zod0.ZodString, string, string>;
|
|
84
|
-
prefix: zod0.ZodType<string | undefined, zod0.ZodTypeDef, string | undefined>;
|
|
85
|
-
projectId: zod0.ZodType<string | undefined, zod0.ZodTypeDef, string | undefined>;
|
|
86
|
-
accessMode: zod0.ZodType<"readonly" | "readwrite" | undefined, zod0.ZodTypeDef, "readonly" | "readwrite" | undefined>;
|
|
87
|
-
endpoint: zod0.ZodType<string | undefined, zod0.ZodTypeDef, string | undefined>;
|
|
88
|
-
keyFilename: zod0.ZodType<string | undefined, zod0.ZodTypeDef, string | undefined>;
|
|
89
|
-
credentials: zod0.ZodType<{
|
|
90
|
-
clientEmail: string;
|
|
91
|
-
privateKey: string;
|
|
92
|
-
} | undefined, zod0.ZodTypeDef, {
|
|
93
|
-
clientEmail: string;
|
|
94
|
-
privateKey: string;
|
|
95
|
-
} | undefined>;
|
|
96
|
-
cacheTtl: zod0.ZodType<number | undefined, zod0.ZodTypeDef, number | undefined>;
|
|
97
|
-
}, "strict", zod0.ZodTypeAny, {
|
|
98
|
-
bucket: string;
|
|
99
|
-
name?: string | undefined;
|
|
100
|
-
description?: string | undefined;
|
|
101
|
-
prefix?: string | undefined;
|
|
102
|
-
projectId?: string | undefined;
|
|
103
|
-
accessMode?: "readonly" | "readwrite" | undefined;
|
|
104
|
-
endpoint?: string | undefined;
|
|
105
|
-
keyFilename?: string | undefined;
|
|
106
|
-
credentials?: {
|
|
107
|
-
clientEmail: string;
|
|
108
|
-
privateKey: string;
|
|
109
|
-
} | undefined;
|
|
110
|
-
cacheTtl?: number | undefined;
|
|
111
|
-
}, {
|
|
83
|
+
static schema(): z.ZodType<{
|
|
84
|
+
name: string | undefined;
|
|
85
|
+
description: string | undefined;
|
|
112
86
|
bucket: string;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
keyFilename?: string | undefined;
|
|
120
|
-
credentials?: {
|
|
87
|
+
prefix: string | undefined;
|
|
88
|
+
projectId: string | undefined;
|
|
89
|
+
accessMode: "readonly" | "readwrite" | undefined;
|
|
90
|
+
endpoint: string | undefined;
|
|
91
|
+
keyFilename: string | undefined;
|
|
92
|
+
credentials: {
|
|
121
93
|
clientEmail: string;
|
|
122
94
|
privateKey: string;
|
|
123
95
|
} | undefined;
|
|
124
|
-
cacheTtl
|
|
125
|
-
}
|
|
96
|
+
cacheTtl: number | undefined;
|
|
97
|
+
}, unknown, z.core.$ZodTypeInternals<{
|
|
98
|
+
name: string | undefined;
|
|
99
|
+
description: string | undefined;
|
|
126
100
|
bucket: string;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
keyFilename?: string | undefined;
|
|
134
|
-
credentials?: {
|
|
101
|
+
prefix: string | undefined;
|
|
102
|
+
projectId: string | undefined;
|
|
103
|
+
accessMode: "readonly" | "readwrite" | undefined;
|
|
104
|
+
endpoint: string | undefined;
|
|
105
|
+
keyFilename: string | undefined;
|
|
106
|
+
credentials: {
|
|
135
107
|
clientEmail: string;
|
|
136
108
|
privateKey: string;
|
|
137
109
|
} | undefined;
|
|
138
|
-
cacheTtl
|
|
139
|
-
},
|
|
110
|
+
cacheTtl: number | undefined;
|
|
111
|
+
}, unknown>>;
|
|
112
|
+
/**
|
|
113
|
+
* Provider manifest for URI-based discovery
|
|
114
|
+
*/
|
|
115
|
+
static manifest(): ProviderManifest;
|
|
140
116
|
/**
|
|
141
117
|
* Load from configuration file
|
|
142
118
|
*/
|
|
143
|
-
static load(
|
|
119
|
+
static load({
|
|
120
|
+
basePath,
|
|
121
|
+
config
|
|
122
|
+
}?: AFSModuleLoadParams): Promise<AFSGCS>;
|
|
144
123
|
/**
|
|
145
124
|
* Build the full GCS key from a path
|
|
146
125
|
*/
|
|
@@ -201,46 +180,14 @@ declare class AFSGCS extends AFSBaseProvider {
|
|
|
201
180
|
rewriteActionHandler(ctx: RouteContext<{
|
|
202
181
|
path: string;
|
|
203
182
|
}>, 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>;
|
|
183
|
+
explainHandler(ctx: RouteContext<{
|
|
184
|
+
path?: string;
|
|
185
|
+
}>): Promise<AFSExplainResult>;
|
|
186
|
+
searchHandler(ctx: RouteContext<{
|
|
187
|
+
path?: string;
|
|
188
|
+
}>, query: string): Promise<AFSSearchResult>;
|
|
189
|
+
readCapabilities(_ctx: RouteContext): Promise<AFSEntry>;
|
|
243
190
|
}
|
|
244
191
|
//#endregion
|
|
245
|
-
export { AFSGCS, type AFSGCSOptions, type ParsedGCSUri };
|
|
192
|
+
export { AFSGCS, AFSGCS as default, type AFSGCSOptions, type ParsedGCSUri };
|
|
246
193
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/gcs-afs.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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 {
|
|
3
|
+
import { joinURL } from "ufo";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { Storage } from "@google-cloud/storage";
|
|
5
6
|
|
|
6
7
|
//#region src/cache.ts
|
|
7
8
|
/**
|
|
@@ -319,7 +320,8 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
319
320
|
statCache;
|
|
320
321
|
constructor(options) {
|
|
321
322
|
super();
|
|
322
|
-
const
|
|
323
|
+
const { uri: _uri, token: _token, auth: _auth, ...cleanOptions } = options;
|
|
324
|
+
const parsed = afsgcsOptionsSchema.parse(cleanOptions);
|
|
323
325
|
this.options = {
|
|
324
326
|
...parsed,
|
|
325
327
|
bucket: parsed.bucket,
|
|
@@ -343,10 +345,33 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
343
345
|
return afsgcsOptionsSchema;
|
|
344
346
|
}
|
|
345
347
|
/**
|
|
348
|
+
* Provider manifest for URI-based discovery
|
|
349
|
+
*/
|
|
350
|
+
static manifest() {
|
|
351
|
+
return {
|
|
352
|
+
name: "gcs",
|
|
353
|
+
description: "Google Cloud Storage",
|
|
354
|
+
uriTemplate: "gcs://{bucket}/{prefix+?}",
|
|
355
|
+
category: "cloud-storage",
|
|
356
|
+
schema: z.object({
|
|
357
|
+
bucket: z.string(),
|
|
358
|
+
prefix: z.string().optional(),
|
|
359
|
+
projectId: z.string().optional(),
|
|
360
|
+
keyFilename: z.string().optional()
|
|
361
|
+
}),
|
|
362
|
+
tags: [
|
|
363
|
+
"gcp",
|
|
364
|
+
"gcs",
|
|
365
|
+
"cloud",
|
|
366
|
+
"storage"
|
|
367
|
+
]
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
346
371
|
* Load from configuration file
|
|
347
372
|
*/
|
|
348
|
-
static async load(
|
|
349
|
-
return new AFSGCS(zodParse(afsgcsOptionsSchema,
|
|
373
|
+
static async load({ basePath, config } = {}) {
|
|
374
|
+
return new AFSGCS(zodParse(afsgcsOptionsSchema, config, { prefix: basePath }));
|
|
350
375
|
}
|
|
351
376
|
/**
|
|
352
377
|
* Build the full GCS key from a path
|
|
@@ -359,7 +384,8 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
359
384
|
* Generate a unique ID for a GCS object
|
|
360
385
|
*/
|
|
361
386
|
generateId(key) {
|
|
362
|
-
|
|
387
|
+
const cleanKey = key.replace(/^\/+/, "");
|
|
388
|
+
return `gcs://${this.options.bucket}/${cleanKey}`;
|
|
363
389
|
}
|
|
364
390
|
/**
|
|
365
391
|
* Invalidate caches for a given path
|
|
@@ -423,14 +449,12 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
423
449
|
if (!dirPrefix) continue;
|
|
424
450
|
const dirName = dirPrefix.slice(prefix.length).replace(/\/$/, "");
|
|
425
451
|
if (!dirName) continue;
|
|
426
|
-
const entryPath = basePath ? `${basePath}/${dirName}` : dirName;
|
|
427
|
-
const normalizedPath = entryPath.startsWith("/") ? entryPath : `/${entryPath}`;
|
|
428
452
|
childEntries.push({
|
|
429
453
|
id: this.generateId(dirPrefix),
|
|
430
|
-
path:
|
|
431
|
-
|
|
454
|
+
path: joinURL("/", basePath, dirName),
|
|
455
|
+
meta: {
|
|
432
456
|
kind: "afs:node",
|
|
433
|
-
childrenCount:
|
|
457
|
+
childrenCount: -1,
|
|
434
458
|
platformRef: generatePlatformRef(bucketName, dirPrefix, true)
|
|
435
459
|
}
|
|
436
460
|
});
|
|
@@ -442,15 +466,13 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
442
466
|
if (file.name.endsWith("/")) {
|
|
443
467
|
const dirName = file.name.slice(prefix.length).replace(/\/$/, "");
|
|
444
468
|
if (!dirName) continue;
|
|
445
|
-
const entryPath$1 = basePath ? `${basePath}/${dirName}` : dirName;
|
|
446
|
-
const normalizedPath$1 = entryPath$1.startsWith("/") ? entryPath$1 : `/${entryPath$1}`;
|
|
447
469
|
childEntries.push({
|
|
448
470
|
id: this.generateId(file.name),
|
|
449
|
-
path:
|
|
471
|
+
path: joinURL("/", basePath, dirName),
|
|
450
472
|
updatedAt: file.metadata.updated ? new Date(file.metadata.updated) : void 0,
|
|
451
|
-
|
|
473
|
+
meta: {
|
|
452
474
|
kind: "afs:node",
|
|
453
|
-
childrenCount:
|
|
475
|
+
childrenCount: -1,
|
|
454
476
|
platformRef: generatePlatformRef(bucketName, file.name, true)
|
|
455
477
|
}
|
|
456
478
|
});
|
|
@@ -459,13 +481,11 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
459
481
|
}
|
|
460
482
|
const fileName = file.name.slice(prefix.length);
|
|
461
483
|
if (!fileName || fileName.includes("/")) continue;
|
|
462
|
-
const entryPath = basePath ? `${basePath}/${fileName}` : fileName;
|
|
463
|
-
const normalizedPath = entryPath.startsWith("/") ? entryPath : `/${entryPath}`;
|
|
464
484
|
childEntries.push({
|
|
465
485
|
id: this.generateId(file.name),
|
|
466
|
-
path:
|
|
486
|
+
path: joinURL("/", basePath, fileName),
|
|
467
487
|
updatedAt: file.metadata.updated ? new Date(file.metadata.updated) : void 0,
|
|
468
|
-
|
|
488
|
+
meta: {
|
|
469
489
|
size: file.metadata.size ? Number(file.metadata.size) : void 0,
|
|
470
490
|
contentType: file.metadata.contentType,
|
|
471
491
|
lastModified: file.metadata.updated,
|
|
@@ -479,36 +499,12 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
479
499
|
const selfPath = basePath ? basePath.startsWith("/") ? basePath : `/${basePath}` : "/";
|
|
480
500
|
if (childEntries.length === 0 && basePath) {
|
|
481
501
|
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
|
-
}
|
|
502
|
+
const [fileExists] = await this.bucket.file(key).exists();
|
|
503
|
+
if (fileExists) return { data: [] };
|
|
500
504
|
const [dirExists] = await this.bucket.file(`${key}/`).exists();
|
|
501
505
|
if (!dirExists) throw new AFSNotFoundError(selfPath);
|
|
502
506
|
}
|
|
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] };
|
|
507
|
+
return { data: childEntries };
|
|
512
508
|
}
|
|
513
509
|
async listVersionsHandler(ctx) {
|
|
514
510
|
try {
|
|
@@ -523,13 +519,13 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
523
519
|
if (versionFile.name !== key) continue;
|
|
524
520
|
const generation = versionFile.metadata.generation;
|
|
525
521
|
if (!generation) continue;
|
|
526
|
-
const versionPath =
|
|
522
|
+
const versionPath = joinURL("/", normalizedPath, "@versions", String(generation));
|
|
527
523
|
const isLive = !versionFile.metadata.timeDeleted;
|
|
528
524
|
entries.push({
|
|
529
525
|
id: `${this.generateId(key)}:${generation}`,
|
|
530
526
|
path: versionPath,
|
|
531
527
|
updatedAt: versionFile.metadata.timeCreated ? new Date(versionFile.metadata.timeCreated) : void 0,
|
|
532
|
-
|
|
528
|
+
meta: {
|
|
533
529
|
generation,
|
|
534
530
|
isLive,
|
|
535
531
|
timeCreated: versionFile.metadata.timeCreated,
|
|
@@ -559,7 +555,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
559
555
|
id: this.generateId("/"),
|
|
560
556
|
path: "/",
|
|
561
557
|
content: "",
|
|
562
|
-
|
|
558
|
+
meta: {
|
|
563
559
|
kind: "afs:node",
|
|
564
560
|
childrenCount,
|
|
565
561
|
platformRef: generatePlatformRef(bucketName, "", true)
|
|
@@ -575,9 +571,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
575
571
|
path: normalizedOutputPath,
|
|
576
572
|
content: "",
|
|
577
573
|
updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
|
|
578
|
-
|
|
574
|
+
meta: {
|
|
579
575
|
kind: "afs:node",
|
|
580
|
-
childrenCount:
|
|
576
|
+
childrenCount: -1,
|
|
581
577
|
platformRef: generatePlatformRef(bucketName, key, true)
|
|
582
578
|
}
|
|
583
579
|
};
|
|
@@ -587,7 +583,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
587
583
|
path: normalizedOutputPath,
|
|
588
584
|
content: content.toString("utf-8"),
|
|
589
585
|
updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
|
|
590
|
-
|
|
586
|
+
meta: {
|
|
591
587
|
size: metadata.size ? Number(metadata.size) : void 0,
|
|
592
588
|
mimeType: metadata.contentType,
|
|
593
589
|
contentType: metadata.contentType,
|
|
@@ -608,7 +604,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
608
604
|
id: this.generateId(`${key}/`),
|
|
609
605
|
path: normalizedOutputPath,
|
|
610
606
|
content: "",
|
|
611
|
-
|
|
607
|
+
meta: {
|
|
612
608
|
kind: "afs:node",
|
|
613
609
|
childrenCount,
|
|
614
610
|
platformRef: generatePlatformRef(bucketName, key, true)
|
|
@@ -624,9 +620,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
624
620
|
path: normalizedOutputPath,
|
|
625
621
|
content: "",
|
|
626
622
|
updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated) : void 0,
|
|
627
|
-
|
|
623
|
+
meta: {
|
|
628
624
|
kind: "afs:node",
|
|
629
|
-
childrenCount:
|
|
625
|
+
childrenCount: -1,
|
|
630
626
|
platformRef: generatePlatformRef(bucketName, key, true)
|
|
631
627
|
}
|
|
632
628
|
};
|
|
@@ -652,7 +648,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
652
648
|
path: ctx.path,
|
|
653
649
|
content: content.toString("utf-8"),
|
|
654
650
|
updatedAt: metadata.timeCreated ? new Date(metadata.timeCreated) : void 0,
|
|
655
|
-
|
|
651
|
+
meta: {
|
|
656
652
|
size: metadata.size ? Number(metadata.size) : void 0,
|
|
657
653
|
contentType: metadata.contentType,
|
|
658
654
|
timeCreated: metadata.timeCreated,
|
|
@@ -681,7 +677,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
681
677
|
return {
|
|
682
678
|
id: this.generateId("/"),
|
|
683
679
|
path: "/.meta",
|
|
684
|
-
|
|
680
|
+
meta: {
|
|
685
681
|
kind: "afs:node",
|
|
686
682
|
childrenCount,
|
|
687
683
|
platformRef: generatePlatformRef(bucketName, "", true)
|
|
@@ -694,7 +690,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
694
690
|
if (cached) return {
|
|
695
691
|
id: cached.id,
|
|
696
692
|
path: ctx.path,
|
|
697
|
-
|
|
693
|
+
meta: cached.meta
|
|
698
694
|
};
|
|
699
695
|
}
|
|
700
696
|
const file = this.bucket.file(key);
|
|
@@ -705,9 +701,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
705
701
|
id: this.generateId(key),
|
|
706
702
|
path: ctx.path,
|
|
707
703
|
updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
|
|
708
|
-
|
|
704
|
+
meta: {
|
|
709
705
|
kind: "afs:node",
|
|
710
|
-
childrenCount:
|
|
706
|
+
childrenCount: -1,
|
|
711
707
|
platformRef: generatePlatformRef(bucketName, key, true)
|
|
712
708
|
}
|
|
713
709
|
};
|
|
@@ -715,7 +711,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
715
711
|
id: this.generateId(key),
|
|
716
712
|
path: ctx.path,
|
|
717
713
|
updatedAt: metadata.updated ? new Date(metadata.updated) : void 0,
|
|
718
|
-
|
|
714
|
+
meta: {
|
|
719
715
|
kind: "afs:document",
|
|
720
716
|
size: metadata.size ? Number(metadata.size) : void 0,
|
|
721
717
|
contentType: metadata.contentType,
|
|
@@ -742,9 +738,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
742
738
|
if (files.length > 0) return {
|
|
743
739
|
id: this.generateId(`${key}/`),
|
|
744
740
|
path: ctx.path,
|
|
745
|
-
|
|
741
|
+
meta: {
|
|
746
742
|
kind: "afs:node",
|
|
747
|
-
childrenCount:
|
|
743
|
+
childrenCount: -1,
|
|
748
744
|
platformRef: generatePlatformRef(bucketName, key, true)
|
|
749
745
|
}
|
|
750
746
|
};
|
|
@@ -756,9 +752,9 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
756
752
|
id: this.generateId(`${key}/`),
|
|
757
753
|
path: ctx.path,
|
|
758
754
|
updatedAt: dirMetadata.updated ? new Date(dirMetadata.updated) : void 0,
|
|
759
|
-
|
|
755
|
+
meta: {
|
|
760
756
|
kind: "afs:node",
|
|
761
|
-
childrenCount:
|
|
757
|
+
childrenCount: -1,
|
|
762
758
|
platformRef: generatePlatformRef(bucketName, key, true)
|
|
763
759
|
}
|
|
764
760
|
};
|
|
@@ -774,11 +770,11 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
774
770
|
...ctx,
|
|
775
771
|
path: ctx.path.endsWith("/.meta") ? ctx.path : `${ctx.path}/.meta`
|
|
776
772
|
});
|
|
773
|
+
const pathSegments = ctx.path.split("/").filter(Boolean);
|
|
777
774
|
return { data: {
|
|
775
|
+
id: pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : "/",
|
|
778
776
|
path: ctx.path,
|
|
779
|
-
|
|
780
|
-
childrenCount: metaEntry.metadata?.childrenCount,
|
|
781
|
-
meta: metaEntry.metadata
|
|
777
|
+
meta: metaEntry.meta
|
|
782
778
|
} };
|
|
783
779
|
}
|
|
784
780
|
async writeHandler(ctx, payload) {
|
|
@@ -791,7 +787,7 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
791
787
|
else if (payload.content !== void 0) content = JSON.stringify(payload.content);
|
|
792
788
|
else content = "";
|
|
793
789
|
const saveOptions = { validation: false };
|
|
794
|
-
if (payload.
|
|
790
|
+
if (payload.meta?.mimeType || payload.meta?.contentType) saveOptions.contentType = payload.meta.mimeType ?? payload.meta.contentType;
|
|
795
791
|
await file.save(content, saveOptions);
|
|
796
792
|
const [metadata] = await file.getMetadata();
|
|
797
793
|
const normalizedOutputPath = ctx.path.startsWith("/") ? ctx.path : `/${ctx.path}`;
|
|
@@ -801,11 +797,11 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
801
797
|
path: normalizedOutputPath,
|
|
802
798
|
content: payload.content,
|
|
803
799
|
updatedAt: metadata.updated ? new Date(metadata.updated) : /* @__PURE__ */ new Date(),
|
|
804
|
-
|
|
800
|
+
meta: {
|
|
805
801
|
size: metadata.size ? Number(metadata.size) : 0,
|
|
806
802
|
etag: metadata.etag,
|
|
807
803
|
generation: metadata.generation,
|
|
808
|
-
...payload.
|
|
804
|
+
...payload.meta
|
|
809
805
|
}
|
|
810
806
|
} };
|
|
811
807
|
} catch (error) {
|
|
@@ -834,42 +830,88 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
834
830
|
}
|
|
835
831
|
}
|
|
836
832
|
async listActionsHandler(ctx) {
|
|
837
|
-
const basePath = ctx.path.replace(/\/\.actions$/, "");
|
|
838
833
|
return { data: [
|
|
839
834
|
{
|
|
840
835
|
id: "presign-download",
|
|
841
|
-
path:
|
|
836
|
+
path: joinURL(ctx.path, "presign-download"),
|
|
842
837
|
summary: "Generate signed download URL",
|
|
843
|
-
|
|
838
|
+
meta: {
|
|
844
839
|
kind: "afs:executable",
|
|
845
|
-
kinds: ["afs:executable", "afs:node"]
|
|
840
|
+
kinds: ["afs:executable", "afs:node"],
|
|
841
|
+
inputSchema: {
|
|
842
|
+
type: "object",
|
|
843
|
+
properties: { expiresIn: {
|
|
844
|
+
type: "number",
|
|
845
|
+
description: "Expiration in seconds (default: 3600, max: 604800)"
|
|
846
|
+
} }
|
|
847
|
+
}
|
|
846
848
|
}
|
|
847
849
|
},
|
|
848
850
|
{
|
|
849
851
|
id: "presign-upload",
|
|
850
|
-
path:
|
|
852
|
+
path: joinURL(ctx.path, "presign-upload"),
|
|
851
853
|
summary: "Generate signed upload URL",
|
|
852
|
-
|
|
854
|
+
meta: {
|
|
853
855
|
kind: "afs:executable",
|
|
854
|
-
kinds: ["afs:executable", "afs:node"]
|
|
856
|
+
kinds: ["afs:executable", "afs:node"],
|
|
857
|
+
inputSchema: {
|
|
858
|
+
type: "object",
|
|
859
|
+
properties: {
|
|
860
|
+
expiresIn: {
|
|
861
|
+
type: "number",
|
|
862
|
+
description: "Expiration in seconds"
|
|
863
|
+
},
|
|
864
|
+
contentType: {
|
|
865
|
+
type: "string",
|
|
866
|
+
description: "Content-Type for upload"
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
855
870
|
}
|
|
856
871
|
},
|
|
857
872
|
{
|
|
858
873
|
id: "compose",
|
|
859
|
-
path:
|
|
874
|
+
path: joinURL(ctx.path, "compose"),
|
|
860
875
|
summary: "Compose multiple objects into one",
|
|
861
|
-
|
|
876
|
+
meta: {
|
|
862
877
|
kind: "afs:executable",
|
|
863
|
-
kinds: ["afs:executable", "afs:node"]
|
|
878
|
+
kinds: ["afs:executable", "afs:node"],
|
|
879
|
+
inputSchema: {
|
|
880
|
+
type: "object",
|
|
881
|
+
properties: { sources: {
|
|
882
|
+
type: "array",
|
|
883
|
+
description: "Array of source paths (2-32)",
|
|
884
|
+
items: { type: "string" }
|
|
885
|
+
} },
|
|
886
|
+
required: ["sources"]
|
|
887
|
+
}
|
|
864
888
|
}
|
|
865
889
|
},
|
|
866
890
|
{
|
|
867
891
|
id: "rewrite",
|
|
868
|
-
path:
|
|
892
|
+
path: joinURL(ctx.path, "rewrite"),
|
|
869
893
|
summary: "Rewrite object to new location",
|
|
870
|
-
|
|
894
|
+
meta: {
|
|
871
895
|
kind: "afs:executable",
|
|
872
|
-
kinds: ["afs:executable", "afs:node"]
|
|
896
|
+
kinds: ["afs:executable", "afs:node"],
|
|
897
|
+
inputSchema: {
|
|
898
|
+
type: "object",
|
|
899
|
+
properties: {
|
|
900
|
+
destination: {
|
|
901
|
+
type: "string",
|
|
902
|
+
description: "Destination path"
|
|
903
|
+
},
|
|
904
|
+
storageClass: {
|
|
905
|
+
type: "string",
|
|
906
|
+
description: "Target storage class"
|
|
907
|
+
},
|
|
908
|
+
contentType: {
|
|
909
|
+
type: "string",
|
|
910
|
+
description: "Override content type"
|
|
911
|
+
}
|
|
912
|
+
},
|
|
913
|
+
required: ["destination"]
|
|
914
|
+
}
|
|
873
915
|
}
|
|
874
916
|
}
|
|
875
917
|
] };
|
|
@@ -966,74 +1008,180 @@ var AFSGCS = class AFSGCS extends AFSBaseProvider {
|
|
|
966
1008
|
}
|
|
967
1009
|
};
|
|
968
1010
|
}
|
|
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: {}
|
|
1011
|
+
async explainHandler(ctx) {
|
|
1012
|
+
const normalizedPath = (ctx.params.path ?? "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
1013
|
+
if (!normalizedPath) {
|
|
1014
|
+
const [files$1, , response$1] = await this.bucket.getFiles({
|
|
1015
|
+
prefix: this.options.prefix ? `${this.options.prefix}/` : void 0,
|
|
1016
|
+
delimiter: "/",
|
|
1017
|
+
maxResults: 1e3
|
|
1018
|
+
});
|
|
1019
|
+
const objectCount$1 = files$1.length;
|
|
1020
|
+
const prefixCount$1 = response$1?.prefixes?.length ?? 0;
|
|
1021
|
+
const lines$1 = [];
|
|
1022
|
+
lines$1.push(`# ${this.options.bucket}`);
|
|
1023
|
+
lines$1.push("");
|
|
1024
|
+
lines$1.push(`- **Type**: GCS Bucket`);
|
|
1025
|
+
lines$1.push(`- **Bucket**: ${this.options.bucket}`);
|
|
1026
|
+
if (this.options.prefix) lines$1.push(`- **Prefix**: ${this.options.prefix}`);
|
|
1027
|
+
if (this.options.projectId) lines$1.push(`- **Project**: ${this.options.projectId}`);
|
|
1028
|
+
lines$1.push(`- **Access Mode**: ${this.accessMode}`);
|
|
1029
|
+
lines$1.push(`- **Top-level Objects**: ${objectCount$1}`);
|
|
1030
|
+
lines$1.push(`- **Top-level Prefixes**: ${prefixCount$1}`);
|
|
1031
|
+
return {
|
|
1032
|
+
format: "markdown",
|
|
1033
|
+
content: lines$1.join("\n")
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
const key = this.buildGCSKey(normalizedPath);
|
|
1037
|
+
const file = this.bucket.file(key);
|
|
1038
|
+
const [exists] = await file.exists();
|
|
1039
|
+
if (exists) {
|
|
1040
|
+
const [metadata] = await file.getMetadata();
|
|
1041
|
+
const lines$1 = [];
|
|
1042
|
+
lines$1.push(`# ${normalizedPath}`);
|
|
1043
|
+
lines$1.push("");
|
|
1044
|
+
lines$1.push(`- **Type**: Object`);
|
|
1045
|
+
lines$1.push(`- **Key**: ${key}`);
|
|
1046
|
+
lines$1.push(`- **Size**: ${metadata.size ?? 0} bytes`);
|
|
1047
|
+
if (metadata.contentType) lines$1.push(`- **Content-Type**: ${metadata.contentType}`);
|
|
1048
|
+
if (metadata.storageClass) lines$1.push(`- **Storage Class**: ${metadata.storageClass}`);
|
|
1049
|
+
if (metadata.updated) lines$1.push(`- **Last Modified**: ${metadata.updated}`);
|
|
1050
|
+
if (metadata.etag) lines$1.push(`- **ETag**: ${metadata.etag}`);
|
|
1051
|
+
if (metadata.generation) lines$1.push(`- **Generation**: ${metadata.generation}`);
|
|
1052
|
+
return {
|
|
1053
|
+
format: "markdown",
|
|
1054
|
+
content: lines$1.join("\n")
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
const prefix = key.endsWith("/") ? key : `${key}/`;
|
|
1058
|
+
const [files, , response] = await this.bucket.getFiles({
|
|
1059
|
+
prefix,
|
|
1060
|
+
delimiter: "/",
|
|
1061
|
+
maxResults: 1e3
|
|
1023
1062
|
});
|
|
1063
|
+
const objectCount = files.length;
|
|
1064
|
+
const prefixCount = response?.prefixes?.length ?? 0;
|
|
1065
|
+
if (objectCount === 0 && prefixCount === 0) throw new AFSNotFoundError(`/${normalizedPath}`, `Path not found: ${normalizedPath}`);
|
|
1066
|
+
const lines = [];
|
|
1067
|
+
lines.push(`# ${normalizedPath}/`);
|
|
1068
|
+
lines.push("");
|
|
1069
|
+
lines.push(`- **Type**: Prefix (directory)`);
|
|
1070
|
+
lines.push(`- **Prefix**: ${prefix}`);
|
|
1071
|
+
lines.push(`- **Objects**: ${objectCount}`);
|
|
1072
|
+
lines.push(`- **Sub-prefixes**: ${prefixCount}`);
|
|
1024
1073
|
return {
|
|
1025
|
-
|
|
1026
|
-
|
|
1074
|
+
format: "markdown",
|
|
1075
|
+
content: lines.join("\n")
|
|
1027
1076
|
};
|
|
1028
1077
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
const
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1078
|
+
async searchHandler(ctx, query) {
|
|
1079
|
+
const { minimatch } = await import("minimatch");
|
|
1080
|
+
const normalizedPath = (ctx.params.path ?? "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
1081
|
+
const prefix = normalizedPath ? this.options.prefix ? `${this.options.prefix}/${normalizedPath}/` : `${normalizedPath}/` : this.options.prefix ? `${this.options.prefix}/` : "";
|
|
1082
|
+
const [files] = await this.bucket.getFiles({ prefix: prefix || void 0 });
|
|
1083
|
+
const results = [];
|
|
1084
|
+
for (const file of files) {
|
|
1085
|
+
const relativePath = prefix ? file.name.slice(prefix.length) : file.name;
|
|
1086
|
+
if (!relativePath) continue;
|
|
1087
|
+
if (minimatch(relativePath, query)) {
|
|
1088
|
+
const displayPath = joinURL("/", normalizedPath, relativePath);
|
|
1089
|
+
results.push({
|
|
1090
|
+
id: this.generateId(file.name),
|
|
1091
|
+
path: displayPath,
|
|
1092
|
+
meta: {
|
|
1093
|
+
size: file.metadata?.size ? Number(file.metadata.size) : void 0,
|
|
1094
|
+
contentType: file.metadata?.contentType,
|
|
1095
|
+
lastModified: file.metadata?.updated
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return { data: results };
|
|
1101
|
+
}
|
|
1102
|
+
async readCapabilities(_ctx) {
|
|
1103
|
+
const capabilities = {
|
|
1104
|
+
schemaVersion: 1,
|
|
1105
|
+
provider: "gcs",
|
|
1106
|
+
description: `GCS bucket: ${this.options.bucket}`,
|
|
1107
|
+
tools: [],
|
|
1108
|
+
operations: this.getOperationsDeclaration(),
|
|
1109
|
+
actions: [{
|
|
1110
|
+
description: "GCS object actions",
|
|
1111
|
+
catalog: [
|
|
1112
|
+
{
|
|
1113
|
+
name: "presign-download",
|
|
1114
|
+
description: "Generate a signed download URL",
|
|
1115
|
+
inputSchema: {
|
|
1116
|
+
type: "object",
|
|
1117
|
+
properties: { expiresIn: {
|
|
1118
|
+
type: "number",
|
|
1119
|
+
description: "Expiration in seconds (default: 3600, max: 604800)"
|
|
1120
|
+
} }
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
name: "presign-upload",
|
|
1125
|
+
description: "Generate a signed upload URL",
|
|
1126
|
+
inputSchema: {
|
|
1127
|
+
type: "object",
|
|
1128
|
+
properties: {
|
|
1129
|
+
expiresIn: {
|
|
1130
|
+
type: "number",
|
|
1131
|
+
description: "Expiration in seconds"
|
|
1132
|
+
},
|
|
1133
|
+
contentType: {
|
|
1134
|
+
type: "string",
|
|
1135
|
+
description: "Content-Type for upload"
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
name: "compose",
|
|
1142
|
+
description: "Compose multiple objects into one (max 32 sources)",
|
|
1143
|
+
inputSchema: {
|
|
1144
|
+
type: "object",
|
|
1145
|
+
properties: { sources: {
|
|
1146
|
+
type: "array",
|
|
1147
|
+
description: "Array of source paths (2-32)",
|
|
1148
|
+
items: { type: "string" }
|
|
1149
|
+
} },
|
|
1150
|
+
required: ["sources"]
|
|
1151
|
+
}
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
name: "rewrite",
|
|
1155
|
+
description: "Copy/rewrite object to a new location",
|
|
1156
|
+
inputSchema: {
|
|
1157
|
+
type: "object",
|
|
1158
|
+
properties: {
|
|
1159
|
+
destination: {
|
|
1160
|
+
type: "string",
|
|
1161
|
+
description: "Destination path"
|
|
1162
|
+
},
|
|
1163
|
+
storageClass: {
|
|
1164
|
+
type: "string",
|
|
1165
|
+
description: "Target storage class"
|
|
1166
|
+
},
|
|
1167
|
+
contentType: {
|
|
1168
|
+
type: "string",
|
|
1169
|
+
description: "Override content type"
|
|
1170
|
+
}
|
|
1171
|
+
},
|
|
1172
|
+
required: ["destination"]
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
],
|
|
1176
|
+
discovery: { pathTemplate: "/{path}/.actions" }
|
|
1177
|
+
}]
|
|
1178
|
+
};
|
|
1179
|
+
return {
|
|
1180
|
+
id: ".capabilities",
|
|
1181
|
+
path: "/.meta/.capabilities",
|
|
1182
|
+
content: JSON.stringify(capabilities, null, 2),
|
|
1183
|
+
meta: { kind: "afs:capabilities" }
|
|
1184
|
+
};
|
|
1037
1185
|
}
|
|
1038
1186
|
};
|
|
1039
1187
|
__decorate([List("/"), List("/:path*")], AFSGCS.prototype, "listHandler", null);
|
|
@@ -1049,7 +1197,10 @@ __decorate([Actions.Exec("/:path*", "presign-download")], AFSGCS.prototype, "sig
|
|
|
1049
1197
|
__decorate([Actions.Exec("/:path*", "presign-upload")], AFSGCS.prototype, "signUploadActionHandler", null);
|
|
1050
1198
|
__decorate([Actions.Exec("/:path*", "compose")], AFSGCS.prototype, "composeActionHandler", null);
|
|
1051
1199
|
__decorate([Actions.Exec("/:path*", "rewrite")], AFSGCS.prototype, "rewriteActionHandler", null);
|
|
1200
|
+
__decorate([Explain("/"), Explain("/:path*")], AFSGCS.prototype, "explainHandler", null);
|
|
1201
|
+
__decorate([Search("/"), Search("/:path*")], AFSGCS.prototype, "searchHandler", null);
|
|
1202
|
+
__decorate([Read("/.meta/.capabilities")], AFSGCS.prototype, "readCapabilities", null);
|
|
1052
1203
|
|
|
1053
1204
|
//#endregion
|
|
1054
|
-
export { AFSGCS };
|
|
1205
|
+
export { AFSGCS, AFSGCS as default };
|
|
1055
1206
|
//# 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 type ProviderManifest,\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 { z } from \"zod\";\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 & { uri?: string; token?: string; auth?: unknown }) {\n super();\n\n // Strip registry-injected keys before strict schema validation\n const { uri: _uri, token: _token, auth: _auth, ...cleanOptions } = options as any;\n\n // Validate options\n const parsed = afsgcsOptionsSchema.parse(cleanOptions);\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 * Provider manifest for URI-based discovery\n */\n static manifest(): ProviderManifest {\n return {\n name: \"gcs\",\n description: \"Google Cloud Storage\",\n uriTemplate: \"gcs://{bucket}/{prefix+?}\",\n category: \"cloud-storage\",\n schema: z.object({\n bucket: z.string(),\n prefix: z.string().optional(),\n projectId: z.string().optional(),\n keyFilename: z.string().optional(),\n }),\n tags: [\"gcp\", \"gcs\", \"cloud\", \"storage\"],\n };\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 `gcs://${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.8",
|
|
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
|
-
"
|
|
38
|
-
"
|
|
37
|
+
"minimatch": "^10.1.1",
|
|
38
|
+
"ufo": "^1.6.3",
|
|
39
|
+
"zod": "^4.0.0",
|
|
40
|
+
"@aigne/afs": "^1.11.0-beta.8"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/bun": "^1.3.6",
|
|
@@ -43,6 +45,7 @@
|
|
|
43
45
|
"rimraf": "^6.1.2",
|
|
44
46
|
"tsdown": "0.20.0-beta.3",
|
|
45
47
|
"typescript": "5.9.2",
|
|
48
|
+
"@aigne/afs-testing": "1.11.0-beta.8",
|
|
46
49
|
"@aigne/scripts": "0.0.0",
|
|
47
50
|
"@aigne/typescript-config": "0.0.0"
|
|
48
51
|
},
|