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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AFSAccessMode, AFSBaseProvider, AFSDeleteResult, AFSEntry, AFSExecResult, AFSListResult, AFSModuleLoadParams, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult, RouteContext } from "@aigne/afs";
1
+ import { AFSAccessMode, AFSBaseProvider, AFSDeleteResult, AFSEntry, AFSExecResult, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchResult, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult, RouteContext } from "@aigne/afs";
2
2
  import { S3Client } from "@aws-sdk/client-s3";
3
3
  import * as zod0 from "zod";
4
4
  import { z } from "zod";
@@ -157,7 +157,10 @@ declare class AFSS3 extends AFSBaseProvider {
157
157
  /**
158
158
  * Load from configuration file
159
159
  */
160
- static load(params: AFSModuleLoadParams): Promise<AFSS3>;
160
+ static load({
161
+ basePath,
162
+ config
163
+ }?: AFSModuleLoadParams): Promise<AFSS3>;
161
164
  /**
162
165
  * Build the full S3 key from a path
163
166
  */
@@ -215,68 +218,14 @@ declare class AFSS3 extends AFSBaseProvider {
215
218
  presignUploadActionHandler(ctx: RouteContext<{
216
219
  path: string;
217
220
  }>, args: Record<string, unknown>): Promise<AFSExecResult>;
218
- /**
219
- * Generate a presigned URL for downloading an object
220
- * @deprecated Use action /.actions/presign-download instead
221
- */
222
- getPresignedDownloadUrl(path: string, options?: {
223
- expiresIn?: number;
224
- }): Promise<string>;
225
- /**
226
- * Generate a presigned URL for uploading an object
227
- * @deprecated Use action /.actions/presign-upload instead
228
- */
229
- getPresignedUploadUrl(path: string, options?: {
230
- expiresIn?: number;
231
- contentType?: string;
232
- }): Promise<string>;
233
- /**
234
- * List all versions of an object
235
- * @deprecated Use list on /:path/@versions instead
236
- */
237
- listVersions(path: string): Promise<Array<{
238
- versionId: string;
239
- isLatest: boolean;
240
- lastModified?: Date;
241
- size: number;
242
- etag?: string;
243
- }>>;
244
- /**
245
- * Read a specific version of an object
246
- * @deprecated Use read on /:path/@versions/:versionId instead
247
- */
248
- readVersion(path: string, versionId: string): Promise<{
249
- content: string;
250
- metadata: Record<string, unknown>;
251
- }>;
252
- /**
253
- * Run a SQL-like query on a CSV/JSON/Parquet file (S3 Select)
254
- * @deprecated Use action /.actions/select instead
255
- */
256
- select(path: string, query: string, options?: {
257
- inputFormat?: "CSV" | "JSON" | "Parquet";
258
- csv?: {
259
- fieldDelimiter?: string;
260
- recordDelimiter?: string;
261
- fileHeaderInfo?: "USE" | "IGNORE" | "NONE";
262
- };
263
- json?: {
264
- type?: "DOCUMENT" | "LINES";
265
- };
266
- }): Promise<{
267
- records: unknown[];
268
- stats?: {
269
- bytesScanned: number;
270
- bytesProcessed: number;
271
- bytesReturned: number;
272
- };
273
- }>;
274
- /**
275
- * Create a directory marker
276
- * @deprecated Use write with empty content instead
277
- */
278
- mkdir(path: string): Promise<void>;
221
+ explainHandler(ctx: RouteContext<{
222
+ path?: string;
223
+ }>): Promise<AFSExplainResult>;
224
+ searchHandler(ctx: RouteContext<{
225
+ path?: string;
226
+ }>, query: string): Promise<AFSSearchResult>;
227
+ readCapabilities(_ctx: RouteContext): Promise<AFSEntry>;
279
228
  }
280
229
  //#endregion
281
- export { AFSS3, type AFSS3Options, type ParsedS3Uri };
230
+ export { AFSS3, AFSS3 as default, type AFSS3Options, type ParsedS3Uri };
282
231
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/s3-afs.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/s3-afs.ts"],"mappings":";;;;;;;AAWA;;UAAiB,YAAA;EA0CE;EAxCjB,IAAA;EAGA;EAAA,WAAA;EAMA;EAHA,MAAA;EASA;EANA,MAAA;EAYA;EATA,MAAA;EAeA;EAZA,UAAA;EAcE;EAXF,QAAA;EAgBA;EAbA,cAAA;EAmBS;EAhBT,OAAA;EAgBiB;EAbjB,WAAA;IACE,WAAA;IACA,eAAA;IACA,YAAA;EAAA;;EAIF,QAAA;;AC0CF;;;EDpCE,MAAA,GAAS,QAAA;AAAA;;;;UAyCM,WAAA;EACf,MAAA;EACA,MAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;AAFF;;;;cCLa,KAAA,SAAc,eAAA;EAAA,SACP,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,QAEtB,OAAA;EAAA,QACA,MAAA;EAAA,QACA,SAAA;EAAA,QACA,SAAA;cAEI,OAAA,EAAS,YAAA;EAAY;;;EAAA,OAiC1B,MAAA,CAAA,GAAM,IAAA,CAAA,UAAA,MAAA,SAAA;2CAjCoB,IAAA,CAAA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+jC1B;;;EAAA,OAvhCM,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAA2B,OAAA,CAAQ,KAAA;EAklCvB;;;EAAA,QAxkC5C,UAAA;EA5DgC;;;EAAA,QAoEhC,UAAA;EAjEU;;;EAAA,QAyEV,eAAA;EArEA;;;EAsFR,UAAA,CAAA;EASM,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EA3DpD;;;EAAA,QAiIC,iBAAA;EAkIR,mBAAA,CAAoB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EA4ClE,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,QAAA;EA8J1D,kBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;IAAc,SAAA;EAAA,KACjC,OAAA,CAAQ,QAAA;EAqCL,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;EAyK3D,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EAuB3D,YAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,EAAS,oBAAA,GACR,OAAA,CAAQ,cAAA;EA2EL,aAAA,CAAc,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,eAAA;EA0C5D,kBAAA,CAAmB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EA4DjE,mBAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EAwBL,4BAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EA0BL,0BAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,IAAA,EAAM,MAAA,oBACL,OAAA,CAAQ,aAAA;EA8BL,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,gBAAA;EA4F9D,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,KAAA,WACC,OAAA,CAAQ,eAAA;EAyDL,gBAAA,CAAiB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;AAAA"}
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
3
  import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, PutObjectCommand, S3Client, SelectObjectContentCommand, UploadPartCommand } from "@aws-sdk/client-s3";
4
4
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
5
+ import { joinURL } from "ufo";
5
6
  import { z } from "zod";
6
7
 
7
8
  //#region src/cache.ts
@@ -549,8 +550,8 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
549
550
  /**
550
551
  * Load from configuration file
551
552
  */
552
- static async load(params) {
553
- return new AFSS3(zodParse(afss3OptionsSchema, params.parsed, { prefix: params.filepath }));
553
+ static async load({ basePath, config } = {}) {
554
+ return new AFSS3(zodParse(afss3OptionsSchema, config, { prefix: basePath }));
554
555
  }
555
556
  /**
556
557
  * Build the full S3 key from a path
@@ -563,7 +564,8 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
563
564
  * Generate a unique ID for an S3 object
564
565
  */
565
566
  generateId(key) {
566
- return `s3://${this.options.bucket}/${key}`;
567
+ const cleanKey = key.replace(/^\/+/, "");
568
+ return `s3://${this.options.bucket}/${cleanKey}`;
567
569
  }
568
570
  /**
569
571
  * Invalidate caches for a given path
@@ -630,13 +632,12 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
630
632
  if (!commonPrefix.Prefix) continue;
631
633
  const dirName = commonPrefix.Prefix.slice(prefix.length).replace(/\/$/, "");
632
634
  if (!dirName) continue;
633
- const entryPath = basePath ? `${basePath}/${dirName}` : dirName;
634
- const normalizedPath = entryPath.startsWith("/") ? entryPath : `/${entryPath}`;
635
+ const normalizedPath = joinURL("/", basePath, dirName);
635
636
  childEntries.push({
636
637
  id: this.generateId(commonPrefix.Prefix),
637
638
  path: normalizedPath,
638
- metadata: {
639
- childrenCount: 0,
639
+ meta: {
640
+ childrenCount: -1,
640
641
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, commonPrefix.Prefix, true)
641
642
  }
642
643
  });
@@ -647,13 +648,12 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
647
648
  if (object.Key === prefix) continue;
648
649
  const fileName = object.Key.slice(prefix.length);
649
650
  if (!fileName || fileName.includes("/")) continue;
650
- const entryPath = basePath ? `${basePath}/${fileName}` : fileName;
651
- const normalizedPath = entryPath.startsWith("/") ? entryPath : `/${entryPath}`;
651
+ const normalizedPath = joinURL("/", basePath, fileName);
652
652
  childEntries.push({
653
653
  id: this.generateId(object.Key),
654
654
  path: normalizedPath,
655
655
  updatedAt: object.LastModified,
656
- metadata: {
656
+ meta: {
657
657
  size: object.Size,
658
658
  lastModified: object.LastModified?.toISOString(),
659
659
  etag: object.ETag?.replace(/"/g, ""),
@@ -665,7 +665,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
665
665
  }
666
666
  continuationToken = response.IsTruncated ? response.NextContinuationToken : void 0;
667
667
  } while (continuationToken && childEntries.length < maxChildren);
668
- const selfPath = basePath ? `/${basePath}` : "/";
668
+ const selfPath = basePath ? joinURL("/", basePath) : "/";
669
669
  if (childEntries.length === 0 && basePath) {
670
670
  const key = this.options.prefix ? `${this.options.prefix}/${basePath}` : basePath;
671
671
  try {
@@ -673,20 +673,8 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
673
673
  Bucket: this.options.bucket,
674
674
  Key: key
675
675
  });
676
- const headResponse = await this.client.send(headCommand);
677
- return { data: [{
678
- id: this.generateId(key),
679
- path: selfPath,
680
- updatedAt: headResponse.LastModified,
681
- metadata: {
682
- size: headResponse.ContentLength,
683
- contentType: headResponse.ContentType,
684
- lastModified: headResponse.LastModified?.toISOString(),
685
- etag: headResponse.ETag?.replace(/"/g, ""),
686
- childrenCount: 0,
687
- platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, false)
688
- }
689
- }] };
676
+ await this.client.send(headCommand);
677
+ return { data: [] };
690
678
  } catch (error) {
691
679
  if (error?.name === "NotFound" || error?.$metadata?.httpStatusCode === 404) try {
692
680
  const dirMarkerCommand = new HeadObjectCommand({
@@ -700,15 +688,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
700
688
  else throw error;
701
689
  }
702
690
  }
703
- return { data: [{
704
- id: this.generateId(prefix || "/"),
705
- path: selfPath,
706
- metadata: {
707
- kind: "afs:node",
708
- childrenCount: childEntries.length,
709
- platformRef: generatePlatformRef(this.options.bucket, this.options.region, prefix, true)
710
- }
711
- }, ...childEntries] };
691
+ return { data: childEntries };
712
692
  }
713
693
  async listVersionsHandler(ctx) {
714
694
  try {
@@ -723,12 +703,12 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
723
703
  const entries = [];
724
704
  if (response.Versions) {
725
705
  for (const version of response.Versions) if (version.Key === key && version.VersionId) {
726
- const versionPath = `/${normalizedPath}/@versions/${version.VersionId}`;
706
+ const versionPath = joinURL("/", normalizedPath, "@versions", version.VersionId);
727
707
  entries.push({
728
708
  id: `${this.generateId(key)}:${version.VersionId}`,
729
709
  path: versionPath,
730
710
  updatedAt: version.LastModified,
731
- metadata: {
711
+ meta: {
732
712
  versionId: version.VersionId,
733
713
  isLatest: version.IsLatest ?? false,
734
714
  lastModified: version.LastModified?.toISOString(),
@@ -760,7 +740,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
760
740
  id: this.generateId("/"),
761
741
  path: "/",
762
742
  content: "",
763
- metadata: {
743
+ meta: {
764
744
  kind: "afs:node",
765
745
  childrenCount,
766
746
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, "", true)
@@ -778,9 +758,9 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
778
758
  path: normalizedOutputPath,
779
759
  content: "",
780
760
  updatedAt: response.LastModified,
781
- metadata: {
761
+ meta: {
782
762
  kind: "afs:node",
783
- childrenCount: void 0,
763
+ childrenCount: -1,
784
764
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true)
785
765
  }
786
766
  };
@@ -794,7 +774,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
794
774
  path: normalizedOutputPath,
795
775
  content,
796
776
  updatedAt: response.LastModified,
797
- metadata: {
777
+ meta: {
798
778
  size: response.ContentLength,
799
779
  mimeType: response.ContentType,
800
780
  contentType: response.ContentType,
@@ -819,7 +799,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
819
799
  id: this.generateId(`${key}/`),
820
800
  path: normalizedOutputPath,
821
801
  content: "",
822
- metadata: {
802
+ meta: {
823
803
  kind: "afs:node",
824
804
  childrenCount,
825
805
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true)
@@ -836,7 +816,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
836
816
  id: this.generateId(`${key}/`),
837
817
  path: normalizedOutputPath,
838
818
  content: "",
839
- metadata: {
819
+ meta: {
840
820
  kind: "afs:node",
841
821
  childrenCount: 0,
842
822
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true)
@@ -869,7 +849,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
869
849
  path: ctx.path,
870
850
  content,
871
851
  updatedAt: response.LastModified,
872
- metadata: {
852
+ meta: {
873
853
  size: response.ContentLength,
874
854
  contentType: response.ContentType,
875
855
  lastModified: response.LastModified?.toISOString(),
@@ -898,7 +878,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
898
878
  return {
899
879
  id: this.generateId("/"),
900
880
  path: "/.meta",
901
- metadata: {
881
+ meta: {
902
882
  kind: "afs:node",
903
883
  childrenCount,
904
884
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, "", true)
@@ -911,7 +891,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
911
891
  if (cached) return {
912
892
  id: cached.id,
913
893
  path: ctx.path,
914
- metadata: cached.metadata
894
+ meta: cached.meta
915
895
  };
916
896
  }
917
897
  try {
@@ -924,9 +904,9 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
924
904
  id: this.generateId(key),
925
905
  path: ctx.path,
926
906
  updatedAt: response.LastModified,
927
- metadata: {
907
+ meta: {
928
908
  kind: "afs:node",
929
- childrenCount: void 0,
909
+ childrenCount: -1,
930
910
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true)
931
911
  }
932
912
  };
@@ -934,7 +914,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
934
914
  id: this.generateId(key),
935
915
  path: ctx.path,
936
916
  updatedAt: response.LastModified,
937
- metadata: {
917
+ meta: {
938
918
  kind: "afs:document",
939
919
  size: response.ContentLength,
940
920
  contentType: response.ContentType,
@@ -960,9 +940,9 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
960
940
  if (listResponse.Contents && listResponse.Contents.length > 0) return {
961
941
  id: this.generateId(`${key}/`),
962
942
  path: ctx.path,
963
- metadata: {
943
+ meta: {
964
944
  kind: "afs:node",
965
- childrenCount: void 0,
945
+ childrenCount: -1,
966
946
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true)
967
947
  }
968
948
  };
@@ -976,9 +956,9 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
976
956
  id: this.generateId(`${key}/`),
977
957
  path: ctx.path,
978
958
  updatedAt: markerResponse.LastModified,
979
- metadata: {
959
+ meta: {
980
960
  kind: "afs:node",
981
- childrenCount: void 0,
961
+ childrenCount: -1,
982
962
  platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true)
983
963
  }
984
964
  };
@@ -998,11 +978,11 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
998
978
  ...ctx,
999
979
  path: ctx.path.endsWith("/.meta") ? ctx.path : `${ctx.path}/.meta`
1000
980
  });
981
+ const pathSegments = ctx.path.split("/").filter(Boolean);
1001
982
  return { data: {
983
+ id: pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : "/",
1002
984
  path: ctx.path,
1003
- size: metaEntry.metadata?.size,
1004
- childrenCount: metaEntry.metadata?.childrenCount,
1005
- meta: metaEntry.metadata
985
+ meta: metaEntry.meta
1006
986
  } };
1007
987
  }
1008
988
  async writeHandler(ctx, payload) {
@@ -1013,7 +993,7 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
1013
993
  else if (Buffer.isBuffer(payload.content)) body = payload.content;
1014
994
  else if (payload.content !== void 0) body = Buffer.from(JSON.stringify(payload.content), "utf-8");
1015
995
  else body = Buffer.from("");
1016
- const contentType = payload.metadata?.mimeType ?? payload.metadata?.contentType ?? "application/octet-stream";
996
+ const contentType = payload.meta?.mimeType ?? payload.meta?.contentType ?? "application/octet-stream";
1017
997
  let etag;
1018
998
  let versionId;
1019
999
  if (shouldUseMultipart(body.length)) {
@@ -1037,11 +1017,11 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
1037
1017
  id: this.generateId(key),
1038
1018
  path: normalizedOutputPath,
1039
1019
  content: payload.content,
1040
- metadata: {
1020
+ meta: {
1041
1021
  size: body.length,
1042
1022
  etag,
1043
1023
  versionId,
1044
- ...payload.metadata
1024
+ ...payload.meta
1045
1025
  }
1046
1026
  } };
1047
1027
  } catch (error) {
@@ -1074,33 +1054,70 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
1074
1054
  }
1075
1055
  }
1076
1056
  async listActionsHandler(ctx) {
1077
- const basePath = ctx.path.replace(/\/\.actions$/, "");
1078
1057
  return { data: [
1079
1058
  {
1080
1059
  id: "select",
1081
- path: `${basePath}/.actions/select`,
1060
+ path: joinURL(ctx.path, ".actions", "select"),
1082
1061
  summary: "Query with S3 Select",
1083
- metadata: {
1062
+ meta: {
1084
1063
  kind: "afs:executable",
1085
- kinds: ["afs:executable", "afs:node"]
1064
+ kinds: ["afs:executable", "afs:node"],
1065
+ inputSchema: {
1066
+ type: "object",
1067
+ properties: {
1068
+ expression: {
1069
+ type: "string",
1070
+ description: "SQL expression"
1071
+ },
1072
+ inputFormat: {
1073
+ type: "string",
1074
+ description: "CSV | JSON | Parquet"
1075
+ },
1076
+ outputFormat: {
1077
+ type: "string",
1078
+ description: "CSV | JSON"
1079
+ }
1080
+ },
1081
+ required: ["expression"]
1082
+ }
1086
1083
  }
1087
1084
  },
1088
1085
  {
1089
1086
  id: "presign-download",
1090
- path: `${basePath}/.actions/presign-download`,
1087
+ path: joinURL(ctx.path, ".actions", "presign-download"),
1091
1088
  summary: "Generate download URL",
1092
- metadata: {
1089
+ meta: {
1093
1090
  kind: "afs:executable",
1094
- kinds: ["afs:executable", "afs:node"]
1091
+ kinds: ["afs:executable", "afs:node"],
1092
+ inputSchema: {
1093
+ type: "object",
1094
+ properties: { expiresIn: {
1095
+ type: "number",
1096
+ description: "Expiration in seconds (default: 3600, max: 604800)"
1097
+ } }
1098
+ }
1095
1099
  }
1096
1100
  },
1097
1101
  {
1098
1102
  id: "presign-upload",
1099
- path: `${basePath}/.actions/presign-upload`,
1103
+ path: joinURL(ctx.path, ".actions", "presign-upload"),
1100
1104
  summary: "Generate upload URL",
1101
- metadata: {
1105
+ meta: {
1102
1106
  kind: "afs:executable",
1103
- kinds: ["afs:executable", "afs:node"]
1107
+ kinds: ["afs:executable", "afs:node"],
1108
+ inputSchema: {
1109
+ type: "object",
1110
+ properties: {
1111
+ expiresIn: {
1112
+ type: "number",
1113
+ description: "Expiration in seconds"
1114
+ },
1115
+ contentType: {
1116
+ type: "string",
1117
+ description: "Content-Type for upload"
1118
+ }
1119
+ }
1120
+ }
1104
1121
  }
1105
1122
  }
1106
1123
  ] };
@@ -1159,87 +1176,179 @@ var AFSS3 = class AFSS3 extends AFSBaseProvider {
1159
1176
  }
1160
1177
  };
1161
1178
  }
1162
- /**
1163
- * Generate a presigned URL for downloading an object
1164
- * @deprecated Use action /.actions/presign-download instead
1165
- */
1166
- async getPresignedDownloadUrl(path, options) {
1167
- return (await this.presignDownloadActionHandler({
1168
- path: `/${path}`,
1169
- params: { path },
1170
- options: {}
1171
- }, { expiresIn: options?.expiresIn })).data.url;
1172
- }
1173
- /**
1174
- * Generate a presigned URL for uploading an object
1175
- * @deprecated Use action /.actions/presign-upload instead
1176
- */
1177
- async getPresignedUploadUrl(path, options) {
1178
- return (await this.presignUploadActionHandler({
1179
- path: `/${path}`,
1180
- params: { path },
1181
- options: {}
1182
- }, {
1183
- expiresIn: options?.expiresIn,
1184
- contentType: options?.contentType
1185
- })).data.url;
1186
- }
1187
- /**
1188
- * List all versions of an object
1189
- * @deprecated Use list on /:path/@versions instead
1190
- */
1191
- async listVersions(path) {
1192
- return (await this.listVersionsHandler({
1193
- path: `/${path}/@versions`,
1194
- params: { path },
1195
- options: {}
1196
- })).data.map((entry) => ({
1197
- versionId: entry.metadata?.versionId,
1198
- isLatest: entry.metadata?.isLatest,
1199
- lastModified: entry.updatedAt,
1200
- size: entry.metadata?.size ?? 0,
1201
- etag: entry.metadata?.etag
1179
+ async explainHandler(ctx) {
1180
+ const normalizedPath = (ctx.params.path ?? "").replace(/^\/+/, "").replace(/\/+$/, "");
1181
+ if (!normalizedPath) {
1182
+ const response$1 = await this.client.send(new ListObjectsV2Command({
1183
+ Bucket: this.options.bucket,
1184
+ Prefix: this.options.prefix ? `${this.options.prefix}/` : void 0,
1185
+ Delimiter: "/",
1186
+ MaxKeys: 1e3
1187
+ }));
1188
+ const objectCount$1 = response$1.Contents?.length ?? 0;
1189
+ const prefixCount$1 = response$1.CommonPrefixes?.length ?? 0;
1190
+ const lines$1 = [];
1191
+ lines$1.push(`# ${this.options.bucket}`);
1192
+ lines$1.push("");
1193
+ lines$1.push(`- **Type**: S3 Bucket`);
1194
+ lines$1.push(`- **Bucket**: ${this.options.bucket}`);
1195
+ if (this.options.prefix) lines$1.push(`- **Prefix**: ${this.options.prefix}`);
1196
+ if (this.options.region) lines$1.push(`- **Region**: ${this.options.region}`);
1197
+ lines$1.push(`- **Access Mode**: ${this.accessMode}`);
1198
+ lines$1.push(`- **Top-level Objects**: ${objectCount$1}`);
1199
+ lines$1.push(`- **Top-level Prefixes**: ${prefixCount$1}`);
1200
+ return {
1201
+ format: "markdown",
1202
+ content: lines$1.join("\n")
1203
+ };
1204
+ }
1205
+ const key = this.buildS3Key(normalizedPath);
1206
+ try {
1207
+ const head = await this.client.send(new HeadObjectCommand({
1208
+ Bucket: this.options.bucket,
1209
+ Key: key
1210
+ }));
1211
+ const lines$1 = [];
1212
+ lines$1.push(`# ${normalizedPath}`);
1213
+ lines$1.push("");
1214
+ lines$1.push(`- **Type**: Object`);
1215
+ lines$1.push(`- **Key**: ${key}`);
1216
+ lines$1.push(`- **Size**: ${head.ContentLength ?? 0} bytes`);
1217
+ if (head.ContentType) lines$1.push(`- **Content-Type**: ${head.ContentType}`);
1218
+ if (head.StorageClass) lines$1.push(`- **Storage Class**: ${head.StorageClass}`);
1219
+ if (head.LastModified) lines$1.push(`- **Last Modified**: ${head.LastModified.toISOString()}`);
1220
+ if (head.ETag) lines$1.push(`- **ETag**: ${head.ETag}`);
1221
+ return {
1222
+ format: "markdown",
1223
+ content: lines$1.join("\n")
1224
+ };
1225
+ } catch {}
1226
+ const prefix = key.endsWith("/") ? key : `${key}/`;
1227
+ const response = await this.client.send(new ListObjectsV2Command({
1228
+ Bucket: this.options.bucket,
1229
+ Prefix: prefix,
1230
+ Delimiter: "/",
1231
+ MaxKeys: 1e3
1202
1232
  }));
1203
- }
1204
- /**
1205
- * Read a specific version of an object
1206
- * @deprecated Use read on /:path/@versions/:versionId instead
1207
- */
1208
- async readVersion(path, versionId) {
1209
- const result = await this.readVersionHandler({
1210
- path: `/${path}/@versions/${versionId}`,
1211
- params: {
1212
- path,
1213
- versionId
1214
- },
1215
- options: {}
1216
- });
1233
+ const objectCount = response.Contents?.length ?? 0;
1234
+ const prefixCount = response.CommonPrefixes?.length ?? 0;
1235
+ if (objectCount === 0 && prefixCount === 0) throw new AFSNotFoundError(`/${normalizedPath}`, `Path not found: ${normalizedPath}`);
1236
+ const lines = [];
1237
+ lines.push(`# ${normalizedPath}/`);
1238
+ lines.push("");
1239
+ lines.push(`- **Type**: Prefix (directory)`);
1240
+ lines.push(`- **Prefix**: ${prefix}`);
1241
+ lines.push(`- **Objects**: ${objectCount}`);
1242
+ lines.push(`- **Sub-prefixes**: ${prefixCount}`);
1217
1243
  return {
1218
- content: result.content,
1219
- metadata: result.metadata
1244
+ format: "markdown",
1245
+ content: lines.join("\n")
1220
1246
  };
1221
1247
  }
1222
- /**
1223
- * Run a SQL-like query on a CSV/JSON/Parquet file (S3 Select)
1224
- * @deprecated Use action /.actions/select instead
1225
- */
1226
- async select(path, query, options) {
1227
- return selectQuery(this.client, this.options.bucket, this.options.prefix ?? "", path, query, options);
1248
+ async searchHandler(ctx, query) {
1249
+ const { minimatch } = await import("minimatch");
1250
+ const normalizedPath = (ctx.params.path ?? "").replace(/^\/+/, "").replace(/\/+$/, "");
1251
+ const prefix = normalizedPath ? this.options.prefix ? `${this.options.prefix}/${normalizedPath}/` : `${normalizedPath}/` : this.options.prefix ? `${this.options.prefix}/` : "";
1252
+ const results = [];
1253
+ let continuationToken;
1254
+ do {
1255
+ const response = await this.client.send(new ListObjectsV2Command({
1256
+ Bucket: this.options.bucket,
1257
+ Prefix: prefix || void 0,
1258
+ ContinuationToken: continuationToken,
1259
+ MaxKeys: 1e3
1260
+ }));
1261
+ for (const obj of response.Contents ?? []) {
1262
+ if (!obj.Key) continue;
1263
+ const relativePath = prefix ? obj.Key.slice(prefix.length) : obj.Key;
1264
+ if (!relativePath) continue;
1265
+ if (minimatch(relativePath, query)) {
1266
+ const displayPath = joinURL("/", normalizedPath, relativePath);
1267
+ results.push({
1268
+ id: this.generateId(obj.Key),
1269
+ path: displayPath,
1270
+ meta: {
1271
+ size: obj.Size,
1272
+ lastModified: obj.LastModified?.toISOString(),
1273
+ etag: obj.ETag?.replace(/"/g, "")
1274
+ }
1275
+ });
1276
+ }
1277
+ }
1278
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : void 0;
1279
+ } while (continuationToken);
1280
+ return { data: results };
1228
1281
  }
1229
- /**
1230
- * Create a directory marker
1231
- * @deprecated Use write with empty content instead
1232
- */
1233
- async mkdir(path) {
1234
- const key = this.buildS3Key(path);
1235
- const dirKey = key.endsWith("/") ? key : `${key}/`;
1236
- const command = new PutObjectCommand({
1237
- Bucket: this.options.bucket,
1238
- Key: dirKey,
1239
- Body: Buffer.from(""),
1240
- ContentType: "application/x-directory"
1241
- });
1242
- await this.client.send(command);
1282
+ async readCapabilities(_ctx) {
1283
+ const capabilities = {
1284
+ schemaVersion: 1,
1285
+ provider: "s3",
1286
+ description: `S3 bucket: ${this.options.bucket}`,
1287
+ tools: [],
1288
+ operations: this.getOperationsDeclaration(),
1289
+ actions: [{
1290
+ description: "S3 object actions",
1291
+ catalog: [
1292
+ {
1293
+ name: "select",
1294
+ description: "Query object contents using S3 Select (CSV/JSON/Parquet)",
1295
+ inputSchema: {
1296
+ type: "object",
1297
+ properties: {
1298
+ expression: {
1299
+ type: "string",
1300
+ description: "SQL expression"
1301
+ },
1302
+ inputFormat: {
1303
+ type: "string",
1304
+ description: "CSV | JSON | Parquet"
1305
+ },
1306
+ outputFormat: {
1307
+ type: "string",
1308
+ description: "CSV | JSON"
1309
+ }
1310
+ },
1311
+ required: ["expression"]
1312
+ }
1313
+ },
1314
+ {
1315
+ name: "presign-download",
1316
+ description: "Generate a pre-signed download URL",
1317
+ inputSchema: {
1318
+ type: "object",
1319
+ properties: { expiresIn: {
1320
+ type: "number",
1321
+ description: "Expiration in seconds (default: 3600, max: 604800)"
1322
+ } }
1323
+ }
1324
+ },
1325
+ {
1326
+ name: "presign-upload",
1327
+ description: "Generate a pre-signed upload URL",
1328
+ inputSchema: {
1329
+ type: "object",
1330
+ properties: {
1331
+ expiresIn: {
1332
+ type: "number",
1333
+ description: "Expiration in seconds"
1334
+ },
1335
+ contentType: {
1336
+ type: "string",
1337
+ description: "Content-Type for upload"
1338
+ }
1339
+ }
1340
+ }
1341
+ }
1342
+ ],
1343
+ discovery: { pathTemplate: "/{path}/.actions" }
1344
+ }]
1345
+ };
1346
+ return {
1347
+ id: ".capabilities",
1348
+ path: "/.meta/.capabilities",
1349
+ content: JSON.stringify(capabilities, null, 2),
1350
+ meta: { kind: "afs:capabilities" }
1351
+ };
1243
1352
  }
1244
1353
  };
1245
1354
  __decorate([List("/"), List("/:path*")], AFSS3.prototype, "listHandler", null);
@@ -1254,7 +1363,10 @@ __decorate([Actions("/:path*")], AFSS3.prototype, "listActionsHandler", null);
1254
1363
  __decorate([Actions.Exec("/:path*", "select")], AFSS3.prototype, "selectActionHandler", null);
1255
1364
  __decorate([Actions.Exec("/:path*", "presign-download")], AFSS3.prototype, "presignDownloadActionHandler", null);
1256
1365
  __decorate([Actions.Exec("/:path*", "presign-upload")], AFSS3.prototype, "presignUploadActionHandler", null);
1366
+ __decorate([Explain("/"), Explain("/:path*")], AFSS3.prototype, "explainHandler", null);
1367
+ __decorate([Search("/"), Search("/:path*")], AFSS3.prototype, "searchHandler", null);
1368
+ __decorate([Read("/.meta/.capabilities")], AFSS3.prototype, "readCapabilities", null);
1257
1369
 
1258
1370
  //#endregion
1259
- export { AFSS3 };
1371
+ export { AFSS3, AFSS3 as default };
1260
1372
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["AFSError"],"sources":["../src/cache.ts","../src/client.ts","../src/errors.ts","../src/operations/multipart.ts","../src/operations/select.ts","../src/platform-ref.ts","../src/types.ts","../src/s3-afs.ts"],"sourcesContent":["/**\n * S3 Response Caching (Phase 4)\n *\n * LRU cache with TTL for S3 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 * S3 Client Factory\n *\n * Creates and configures AWS S3 clients.\n */\n\nimport { S3Client, type S3ClientConfig } from \"@aws-sdk/client-s3\";\nimport type { AFSS3Options } from \"./types.js\";\n\n/**\n * Create an S3 client with the given options\n *\n * Uses AWS SDK's default credential chain:\n * 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)\n * 2. Shared credentials file (~/.aws/credentials)\n * 3. IAM role (EC2/ECS/Lambda)\n * 4. SSO credentials\n *\n * @param options - S3 provider options\n * @returns Configured S3 client\n */\nexport function createS3Client(options: AFSS3Options): S3Client {\n const config: S3ClientConfig = {};\n\n // Region configuration\n // For S3-compatible services (MinIO, R2, B2), default to \"us-east-1\" if no region specified\n if (options.region) {\n config.region = options.region;\n } else if (options.endpoint) {\n config.region = \"us-east-1\";\n }\n\n // Custom endpoint for S3-compatible services\n if (options.endpoint) {\n config.endpoint = options.endpoint;\n }\n\n // Force path-style URLs (needed for MinIO, some S3-compatible services)\n if (options.forcePathStyle) {\n config.forcePathStyle = true;\n }\n\n // Explicit credentials (for testing or non-AWS environments)\n if (options.credentials) {\n config.credentials = {\n accessKeyId: options.credentials.accessKeyId,\n secretAccessKey: options.credentials.secretAccessKey,\n sessionToken: options.credentials.sessionToken,\n };\n }\n\n return new S3Client(config);\n}\n","/**\n * S3 Error Handling\n *\n * Maps AWS S3 errors to AFS errors.\n */\n\nimport { AFSNotFoundError } from \"@aigne/afs\";\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_LIMITED: \"RATE_LIMITED\",\n TYPE_MISMATCH: \"TYPE_MISMATCH\",\n INTERNAL_ERROR: \"INTERNAL_ERROR\",\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 * Map S3 error to AFS error\n *\n * @param error - AWS S3 error\n * @param path - Optional path for AFSNotFoundError (with leading slash)\n * @returns AFS error (or throws the original error if it's already an AFS error)\n */\nexport function mapS3Error(error: unknown, path?: string): AFSError | AFSNotFoundError | never {\n // Pass through AFS errors unchanged (check by name since different module instances)\n if (error && typeof error === \"object\" && \"name\" in error) {\n const errorObj = error as { name?: string; code?: string };\n if (\n errorObj.name === \"AFSNotFoundError\" ||\n errorObj.name === \"AFSError\" ||\n errorObj.code === \"AFS_NOT_FOUND\"\n ) {\n throw error;\n }\n }\n\n // Handle AWS SDK errors\n if (error && typeof error === \"object\" && \"name\" in error) {\n const awsError = error as {\n name: string;\n message?: string;\n $metadata?: { httpStatusCode?: number };\n };\n const message = awsError.message ?? \"Unknown S3 error\";\n\n switch (awsError.name) {\n case \"NoSuchBucket\":\n return new AFSError(AFSErrorCode.MODULE_NOT_FOUND, `Bucket not found: ${message}`);\n\n case \"NoSuchKey\":\n case \"NotFound\":\n if (path) {\n return new AFSNotFoundError(path);\n }\n return new AFSError(AFSErrorCode.ENTRY_NOT_FOUND, `Object not found: ${message}`);\n\n case \"AccessDenied\":\n case \"Forbidden\":\n return new AFSError(AFSErrorCode.PERMISSION_DENIED, `Access denied: ${message}`);\n\n case \"InvalidAccessKeyId\":\n case \"SignatureDoesNotMatch\":\n case \"ExpiredToken\":\n case \"TokenRefreshRequired\":\n return new AFSError(AFSErrorCode.AUTH_ERROR, `Authentication failed: ${message}`);\n\n case \"SlowDown\":\n case \"ServiceUnavailable\":\n return new AFSError(AFSErrorCode.RATE_LIMITED, `Rate limited: ${message}`, {\n retryAfter: 1000,\n });\n\n default: {\n // Check HTTP status code\n const statusCode = awsError.$metadata?.httpStatusCode;\n if (statusCode === 404) {\n if (path) {\n return new AFSNotFoundError(path);\n }\n return new AFSError(AFSErrorCode.ENTRY_NOT_FOUND, message);\n }\n if (statusCode === 403) {\n return new AFSError(AFSErrorCode.PERMISSION_DENIED, message);\n }\n if (statusCode === 401) {\n return new AFSError(AFSErrorCode.AUTH_ERROR, message);\n }\n if (statusCode === 429 || statusCode === 503) {\n return new AFSError(AFSErrorCode.RATE_LIMITED, message, { retryAfter: 1000 });\n }\n\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, message);\n }\n }\n }\n\n // Handle generic errors\n if (error instanceof Error) {\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, error.message);\n }\n\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, String(error));\n}\n","/**\n * S3 Multipart Upload (Phase 2)\n *\n * Handles multipart upload for large files (>5GB).\n * S3 requires multipart upload for objects larger than 5GB.\n */\n\nimport {\n AbortMultipartUploadCommand,\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n type S3Client,\n UploadPartCommand,\n} from \"@aws-sdk/client-s3\";\nimport { mapS3Error } from \"../errors.js\";\n\n/**\n * Minimum part size (5MB) - AWS S3 requirement\n */\nconst MIN_PART_SIZE = 5 * 1024 * 1024;\n\n/**\n * Maximum part size (5GB) - AWS S3 requirement\n */\nconst MAX_PART_SIZE = 5 * 1024 * 1024 * 1024;\n\n/**\n * Threshold for switching to multipart upload (100MB)\n * Files larger than this will use multipart upload\n */\nexport const MULTIPART_THRESHOLD = 100 * 1024 * 1024;\n\n/**\n * Default part size (10MB)\n */\nconst DEFAULT_PART_SIZE = 10 * 1024 * 1024;\n\n/**\n * Maximum number of parts (10,000) - AWS S3 requirement\n */\nconst MAX_PARTS = 10000;\n\ninterface MultipartUploadOptions {\n /** Part size in bytes (default: 10MB) */\n partSize?: number;\n /** Content type */\n contentType?: string;\n /** Metadata to attach */\n metadata?: Record<string, string>;\n}\n\ninterface MultipartUploadResult {\n /** ETag of the completed upload */\n etag: string;\n /** Version ID if versioning is enabled */\n versionId?: string;\n}\n\n/**\n * Calculate optimal part size based on file size\n */\nfunction calculatePartSize(fileSize: number, requestedPartSize?: number): number {\n let partSize = requestedPartSize ?? DEFAULT_PART_SIZE;\n\n // Ensure minimum part size\n if (partSize < MIN_PART_SIZE) {\n partSize = MIN_PART_SIZE;\n }\n\n // Ensure maximum part size\n if (partSize > MAX_PART_SIZE) {\n partSize = MAX_PART_SIZE;\n }\n\n // If file is too large for current part size, increase it\n const partsNeeded = Math.ceil(fileSize / partSize);\n if (partsNeeded > MAX_PARTS) {\n // Calculate minimum part size needed to fit in MAX_PARTS\n partSize = Math.ceil(fileSize / MAX_PARTS);\n // Round up to next MB for efficiency\n partSize = Math.ceil(partSize / (1024 * 1024)) * 1024 * 1024;\n }\n\n return partSize;\n}\n\n/**\n * Upload a large file using multipart upload\n *\n * @param client - S3 client\n * @param bucket - Bucket name\n * @param key - Object key\n * @param data - File content as Buffer\n * @param options - Upload options\n * @returns Upload result with ETag\n */\nexport async function multipartUpload(\n client: S3Client,\n bucket: string,\n key: string,\n data: Buffer,\n options?: MultipartUploadOptions,\n): Promise<MultipartUploadResult> {\n const fileSize = data.length;\n const partSize = calculatePartSize(fileSize, options?.partSize);\n\n // Initiate multipart upload\n const createCommand = new CreateMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n ContentType: options?.contentType ?? \"application/octet-stream\",\n Metadata: options?.metadata,\n });\n\n const createResponse = await client.send(createCommand);\n const uploadId = createResponse.UploadId;\n\n if (!uploadId) {\n throw new Error(\"Failed to initiate multipart upload: no uploadId returned\");\n }\n\n const parts: { ETag: string; PartNumber: number }[] = [];\n\n try {\n // Upload parts\n const totalParts = Math.ceil(fileSize / partSize);\n\n for (let partNumber = 1; partNumber <= totalParts; partNumber++) {\n const start = (partNumber - 1) * partSize;\n const end = Math.min(start + partSize, fileSize);\n const partData = data.subarray(start, end);\n\n const uploadPartCommand = new UploadPartCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n PartNumber: partNumber,\n Body: partData,\n });\n\n const uploadPartResponse = await client.send(uploadPartCommand);\n\n if (!uploadPartResponse.ETag) {\n throw new Error(`Failed to upload part ${partNumber}: no ETag returned`);\n }\n\n parts.push({\n ETag: uploadPartResponse.ETag,\n PartNumber: partNumber,\n });\n }\n\n // Complete multipart upload\n const completeCommand = new CompleteMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n MultipartUpload: {\n Parts: parts,\n },\n });\n\n const completeResponse = await client.send(completeCommand);\n\n return {\n etag: completeResponse.ETag?.replace(/\"/g, \"\") ?? \"\",\n versionId: completeResponse.VersionId,\n };\n } catch (error) {\n // Abort the multipart upload on failure\n try {\n const abortCommand = new AbortMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n });\n await client.send(abortCommand);\n } catch {\n // Ignore abort errors\n }\n\n throw mapS3Error(error);\n }\n}\n\n/**\n * Check if a file should use multipart upload\n */\nexport function shouldUseMultipart(size: number): boolean {\n return size >= MULTIPART_THRESHOLD;\n}\n","/**\n * S3 Select Support (Phase 3)\n *\n * Runs SQL-like queries on CSV and JSON files stored in S3.\n * This enables server-side filtering, reducing data transfer.\n */\n\nimport {\n type S3Client,\n SelectObjectContentCommand,\n type SelectObjectContentEventStream,\n} from \"@aws-sdk/client-s3\";\nimport { mapS3Error } from \"../errors.js\";\n\n/**\n * Input serialization format\n */\nexport type InputFormat = \"CSV\" | \"JSON\" | \"Parquet\";\n\n/**\n * Select query options\n */\nexport interface SelectOptions {\n /** Input format (default: auto-detected from extension) */\n inputFormat?: InputFormat;\n /** CSV options */\n csv?: {\n /** Field delimiter (default: ,) */\n fieldDelimiter?: string;\n /** Record delimiter (default: \\n) */\n recordDelimiter?: string;\n /** Whether first row is header (default: true) */\n fileHeaderInfo?: \"USE\" | \"IGNORE\" | \"NONE\";\n /** Quote character (default: \") */\n quoteCharacter?: string;\n /** Comment character */\n comments?: string;\n };\n /** JSON options */\n json?: {\n /** JSON document type */\n type?: \"DOCUMENT\" | \"LINES\";\n };\n /** Output format (default: JSON) */\n outputFormat?: \"CSV\" | \"JSON\";\n}\n\n/**\n * Select query result\n */\nexport interface SelectResult {\n /** Query results as array of records */\n records: unknown[];\n /** Stats about the query */\n stats?: {\n bytesScanned: number;\n bytesProcessed: number;\n bytesReturned: number;\n };\n}\n\n/**\n * Detect input format from file extension\n */\nfunction detectInputFormat(path: string): InputFormat {\n const ext = path.toLowerCase().split(\".\").pop();\n switch (ext) {\n case \"csv\":\n case \"tsv\":\n return \"CSV\";\n case \"json\":\n case \"jsonl\":\n case \"ndjson\":\n return \"JSON\";\n case \"parquet\":\n return \"Parquet\";\n default:\n return \"JSON\";\n }\n}\n\n/**\n * Run a SQL-like query on an S3 object\n *\n * @param client - S3 client\n * @param bucket - Bucket name\n * @param mountPrefix - Mount prefix from options\n * @param path - Path relative to mount point\n * @param query - SQL query (e.g., \"SELECT * FROM s3object WHERE age > 21\")\n * @param options - Select options\n * @returns Query results\n */\nexport async function selectQuery(\n client: S3Client,\n bucket: string,\n mountPrefix: string,\n path: string,\n query: string,\n options?: SelectOptions,\n): Promise<SelectResult> {\n try {\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = mountPrefix ? `${mountPrefix}/${normalizedPath}` : normalizedPath;\n\n const inputFormat = options?.inputFormat ?? detectInputFormat(path);\n const outputFormat = options?.outputFormat ?? \"JSON\";\n\n // Build input serialization based on format\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const inputSerialization: Record<string, any> = {};\n\n if (inputFormat === \"CSV\") {\n inputSerialization.CSV = {\n FieldDelimiter: options?.csv?.fieldDelimiter ?? \",\",\n RecordDelimiter: options?.csv?.recordDelimiter ?? \"\\n\",\n FileHeaderInfo: options?.csv?.fileHeaderInfo ?? \"USE\",\n QuoteCharacter: options?.csv?.quoteCharacter ?? '\"',\n Comments: options?.csv?.comments,\n };\n } else if (inputFormat === \"JSON\") {\n inputSerialization.JSON = {\n Type: options?.json?.type ?? \"DOCUMENT\",\n };\n } else if (inputFormat === \"Parquet\") {\n inputSerialization.Parquet = {};\n }\n\n // Build output serialization\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const outputSerialization: Record<string, any> = {};\n\n if (outputFormat === \"JSON\") {\n outputSerialization.JSON = {};\n } else {\n outputSerialization.CSV = {};\n }\n\n const command = new SelectObjectContentCommand({\n Bucket: bucket,\n Key: key,\n ExpressionType: \"SQL\",\n Expression: query,\n InputSerialization: inputSerialization,\n OutputSerialization: outputSerialization,\n });\n\n const response = await client.send(command);\n\n // Process the event stream\n const records: unknown[] = [];\n let stats: SelectResult[\"stats\"] | undefined;\n\n if (response.Payload) {\n for await (const event of response.Payload as AsyncIterable<SelectObjectContentEventStream>) {\n if (event.Records?.Payload) {\n // Parse records from payload\n const text = new TextDecoder().decode(event.Records.Payload);\n // Split by newline and parse each line as JSON\n const lines = text.split(\"\\n\").filter((line) => line.trim());\n for (const line of lines) {\n try {\n records.push(JSON.parse(line));\n } catch {\n // If not valid JSON, just push as string\n records.push(line);\n }\n }\n }\n if (event.Stats?.Details) {\n stats = {\n bytesScanned: Number(event.Stats.Details.BytesScanned ?? 0),\n bytesProcessed: Number(event.Stats.Details.BytesProcessed ?? 0),\n bytesReturned: Number(event.Stats.Details.BytesReturned ?? 0),\n };\n }\n }\n }\n\n return { records, stats };\n } catch (error) {\n throw mapS3Error(error);\n }\n}\n","/**\n * S3 Platform Reference\n *\n * Generates AWS Console URLs for S3 objects and directories.\n */\n\n/**\n * Platform reference containing console URL\n */\nexport interface S3PlatformRef {\n consoleUrl: string;\n}\n\n/**\n * Generate platform reference with AWS Console URL\n *\n * @param bucket - S3 bucket name\n * @param region - AWS region (defaults to us-east-1)\n * @param key - S3 object 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 region: string | undefined,\n key: string,\n isDirectory: boolean,\n): S3PlatformRef {\n const effectiveRegion = region || \"us-east-1\";\n\n if (isDirectory) {\n // Directory URL: https://s3.console.aws.amazon.com/s3/buckets/{bucket}?region={region}&prefix={prefix}/\n const normalizedPrefix = key.endsWith(\"/\") ? key : key ? `${key}/` : \"\";\n return {\n consoleUrl: `https://s3.console.aws.amazon.com/s3/buckets/${bucket}?region=${effectiveRegion}&prefix=${normalizedPrefix}`,\n };\n }\n\n // File URL: https://s3.console.aws.amazon.com/s3/object/{bucket}?region={region}&prefix={key}\n const encodedKey = encodeURIComponent(key).replace(/%2F/g, \"/\");\n return {\n consoleUrl: `https://s3.console.aws.amazon.com/s3/object/${bucket}?region=${effectiveRegion}&prefix=${encodedKey}`,\n };\n}\n","/**\n * AFS S3 Provider Types\n */\n\nimport { camelize, optionalize } from \"@aigne/afs/utils/zod\";\nimport type { S3Client } from \"@aws-sdk/client-s3\";\nimport { z } from \"zod\";\n\n/**\n * Configuration options for AFSS3\n */\nexport interface AFSS3Options {\n /** Module name (used as mount path segment) */\n name?: string;\n\n /** Module description */\n description?: string;\n\n /** S3 bucket name */\n bucket: string;\n\n /** Key prefix (optional) */\n prefix?: string;\n\n /** AWS region (defaults to environment) */\n region?: string;\n\n /** Access mode */\n accessMode?: \"readonly\" | \"readwrite\";\n\n /** Custom endpoint for S3-compatible services (MinIO, R2, B2) */\n endpoint?: string;\n\n /** Force path-style URLs (needed for some S3-compatible services) */\n forcePathStyle?: boolean;\n\n /** AWS credentials profile name */\n profile?: string;\n\n /** Explicit credentials (for testing or non-AWS environments) */\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n\n /** Cache TTL in seconds (0 = no cache, Phase 4) */\n cacheTtl?: number;\n\n /**\n * Pre-configured S3 client (for testing or advanced use cases)\n * If provided, endpoint/region/credentials/profile options are ignored.\n */\n client?: S3Client;\n}\n\n/**\n * S3 bucket name validation regex\n * - 3-63 characters\n * - lowercase letters, numbers, hyphens, 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 * Zod schema for options validation\n */\nexport const afss3OptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n description: optionalize(z.string()),\n bucket: z.string().regex(BUCKET_NAME_REGEX, \"Invalid S3 bucket name\"),\n prefix: optionalize(z.string()),\n region: optionalize(z.string()),\n accessMode: optionalize(z.enum([\"readonly\", \"readwrite\"])),\n endpoint: optionalize(z.string().url()),\n forcePathStyle: optionalize(z.boolean()),\n profile: optionalize(z.string()),\n credentials: optionalize(\n z.object({\n accessKeyId: z.string(),\n secretAccessKey: z.string(),\n sessionToken: optionalize(z.string()),\n }),\n ),\n cacheTtl: optionalize(z.number().int().min(0)),\n })\n .strict(),\n);\n\n/**\n * Parsed S3 URI\n */\nexport interface ParsedS3Uri {\n bucket: string;\n prefix: string;\n}\n","/**\n * AFS S3 Provider\n *\n * S3 provider using AFSBaseProvider decorator routing pattern.\n * Provides access to AWS S3 and S3-compatible storage (MinIO, R2, B2).\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 {\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n ListObjectVersionsCommand,\n PutObjectCommand,\n type S3Client,\n} from \"@aws-sdk/client-s3\";\nimport { getSignedUrl } from \"@aws-sdk/s3-request-presigner\";\nimport { createCacheKey, LRUCache } from \"./cache.js\";\nimport { createS3Client } from \"./client.js\";\nimport { mapS3Error } from \"./errors.js\";\nimport { multipartUpload, shouldUseMultipart } from \"./operations/multipart.js\";\nimport { selectQuery } from \"./operations/select.js\";\nimport { generatePlatformRef } from \"./platform-ref.js\";\nimport { type AFSS3Options, afss3OptionsSchema } 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 * AFSS3 Provider using Base Provider pattern\n *\n * Provides access to AWS S3 and S3-compatible storage through AFS.\n * Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions).\n *\n * @example\n * ```typescript\n * const s3 = new AFSS3({\n * bucket: \"my-bucket\",\n * prefix: \"data\",\n * region: \"us-east-1\",\n * });\n *\n * // Mount to AFS\n * afs.mount(s3);\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 AFSS3 extends AFSBaseProvider {\n override readonly name: string;\n override readonly description?: string;\n override readonly accessMode: AFSAccessMode;\n\n private options: Required<Pick<AFSS3Options, \"bucket\">> & AFSS3Options;\n private client: S3Client;\n private listCache?: LRUCache<AFSListResult>;\n private statCache?: LRUCache<AFSEntry>;\n\n constructor(options: AFSS3Options) {\n super();\n\n // Extract client before validation (not serializable)\n const { client, ...restOptions } = options;\n\n // Validate options (excluding client)\n const parsed = afss3OptionsSchema.parse(restOptions);\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 ?? `S3 bucket: ${parsed.bucket}`;\n this.accessMode = this.options.accessMode ?? \"readonly\";\n\n // Use provided client or create one\n this.client = client ?? createS3Client(this.options);\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 afss3OptionsSchema;\n }\n\n /**\n * Load from configuration file\n */\n static async load(params: AFSModuleLoadParams): Promise<AFSS3> {\n const options = zodParse(afss3OptionsSchema, params.parsed, { prefix: params.filepath });\n return new AFSS3(options);\n }\n\n // ========== Helper Methods ==========\n\n /**\n * Build the full S3 key from a path\n */\n private buildS3Key(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 an S3 object\n */\n private generateId(key: string): string {\n return `s3://${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 S3 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 opts = ctx.options as { limit?: number; maxChildren?: number } | undefined;\n const maxChildren = opts?.limit ?? opts?.maxChildren ?? 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 const normalizedPath = ctx.params.path\n ? ctx.params.path.startsWith(\"/\")\n ? ctx.params.path\n : `/${ctx.params.path}`\n : \"/\";\n throw mapS3Error(error, normalizedPath);\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 let continuationToken: string | undefined;\n\n do {\n const command = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: prefix,\n Delimiter: \"/\",\n MaxKeys: Math.min(maxChildren - childEntries.length, 1000),\n ContinuationToken: continuationToken,\n });\n\n const response = await this.client.send(command);\n\n // Add directories from CommonPrefixes\n if (response.CommonPrefixes) {\n for (const commonPrefix of response.CommonPrefixes) {\n if (!commonPrefix.Prefix) continue;\n\n // Extract directory name\n const dirName = commonPrefix.Prefix.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(commonPrefix.Prefix),\n path: normalizedPath,\n metadata: {\n childrenCount: 0,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n commonPrefix.Prefix,\n true,\n ),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n // Add files from Contents\n if (response.Contents) {\n for (const object of response.Contents) {\n if (!object.Key) continue;\n\n // Skip the prefix itself (if it's a \"directory marker\")\n if (object.Key === prefix) continue;\n\n // Extract file name\n const fileName = object.Key.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(object.Key),\n path: normalizedPath,\n updatedAt: object.LastModified,\n metadata: {\n size: object.Size,\n lastModified: object.LastModified?.toISOString(),\n etag: object.ETag?.replace(/\"/g, \"\"),\n storageClass: object.StorageClass,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n object.Key,\n false,\n ),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;\n } while (continuationToken && childEntries.length < maxChildren);\n\n // Build result with self entry first\n const selfPath = 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 or directory marker\n const key = this.options.prefix ? `${this.options.prefix}/${basePath}` : basePath;\n\n try {\n const headCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n const headResponse = await this.client.send(headCommand);\n\n // Path exists as an object (file)\n return {\n data: [\n {\n id: this.generateId(key),\n path: selfPath,\n updatedAt: headResponse.LastModified,\n metadata: {\n size: headResponse.ContentLength,\n contentType: headResponse.ContentType,\n lastModified: headResponse.LastModified?.toISOString(),\n etag: headResponse.ETag?.replace(/\"/g, \"\"),\n childrenCount: 0,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n false,\n ),\n },\n },\n ],\n };\n } catch (error: any) {\n // Also check for directory marker\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n try {\n const dirMarkerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n await this.client.send(dirMarkerCommand);\n // Directory marker exists but is empty\n } catch {\n // Neither file nor directory marker exists\n throw new AFSNotFoundError(selfPath);\n }\n } else {\n throw error;\n }\n }\n }\n\n // Create self entry\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(this.options.bucket, this.options.region, 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 const command = new ListObjectVersionsCommand({\n Bucket: this.options.bucket,\n Prefix: key,\n MaxKeys: 1000,\n });\n\n const response = await this.client.send(command);\n const entries: AFSEntry[] = [];\n\n if (response.Versions) {\n for (const version of response.Versions) {\n // Only include exact key matches\n if (version.Key === key && version.VersionId) {\n const versionPath = `/${normalizedPath}/@versions/${version.VersionId}`;\n entries.push({\n id: `${this.generateId(key)}:${version.VersionId}`,\n path: versionPath,\n updatedAt: version.LastModified,\n metadata: {\n versionId: version.VersionId,\n isLatest: version.IsLatest ?? false,\n lastModified: version.LastModified?.toISOString(),\n size: version.Size ?? 0,\n etag: version.ETag?.replace(/\"/g, \"\"),\n },\n });\n }\n }\n }\n\n return { data: entries };\n } catch (error) {\n throw mapS3Error(error, `/${ctx.params.path}/@versions`);\n }\n }\n\n // ========== Read Operations ==========\n\n @Read(\"/:path*\")\n async readHandler(ctx: RouteContext<{ path: string }>): Promise<AFSEntry> {\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n\n try {\n const key = this.buildS3Key(ctx.params.path);\n\n // Handle root path - return bucket metadata with childrenCount\n if (!key) {\n // Count children in root\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n const listResponse = await this.client.send(listCommand);\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n\n return {\n id: this.generateId(\"/\"),\n path: \"/\",\n content: \"\",\n metadata: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, \"\", true),\n },\n };\n }\n\n // Try to get the object directly (file case)\n try {\n const command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const response = await this.client.send(command);\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || response.ContentType === \"application/x-directory\") {\n // This is a directory marker - return directory info\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: \"\",\n updatedAt: response.LastModified,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true),\n },\n };\n }\n\n // Read body as string\n let content: string;\n if (response.Body) {\n const bytes = await response.Body.transformToByteArray();\n content = new TextDecoder().decode(bytes);\n } else {\n content = \"\";\n }\n\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content,\n updatedAt: response.LastModified,\n metadata: {\n size: response.ContentLength,\n mimeType: response.ContentType,\n contentType: response.ContentType,\n contentLength: response.ContentLength,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n contentRange: response.ContentRange,\n },\n };\n } catch (error: any) {\n // If 404, check if it's a directory prefix\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n // Check if there are objects with this prefix (it's a directory)\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: `${key}/`,\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n\n const listResponse = await this.client.send(listCommand);\n const hasChildren =\n (listResponse.Contents && listResponse.Contents.length > 0) ||\n (listResponse.CommonPrefixes && listResponse.CommonPrefixes.length > 0);\n\n if (hasChildren) {\n // It's a directory - return directory info\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n metadata: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n try {\n const markerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n await this.client.send(markerCommand);\n // Directory marker exists\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n metadata: {\n kind: \"afs:node\",\n childrenCount: 0,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n } catch {\n // Neither file nor directory exists\n throw new AFSNotFoundError(normalizedOutputPath);\n }\n }\n throw error;\n }\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapS3Error(error, normalizedOutputPath);\n }\n }\n\n // ========== Version Read ==========\n\n @Read(\"/:path*/@versions/:versionId\")\n async readVersionHandler(\n ctx: RouteContext<{ path: string; versionId: 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\n const command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n VersionId: ctx.params.versionId,\n });\n\n const response = await this.client.send(command);\n\n const content = response.Body ? await response.Body.transformToString() : \"\";\n\n return {\n id: `${this.generateId(key)}:${ctx.params.versionId}`,\n path: ctx.path,\n content,\n updatedAt: response.LastModified,\n metadata: {\n size: response.ContentLength,\n contentType: response.ContentType,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n versionId: response.VersionId,\n },\n };\n } catch (error) {\n throw mapS3Error(error, `/${ctx.params.path}/@versions/${ctx.params.versionId}`);\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 // Root metadata - count children\n if (!key) {\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n const listResponse = await this.client.send(listCommand);\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n\n return {\n id: this.generateId(\"/\"),\n path: \"/.meta\",\n metadata: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, \"\", true),\n },\n };\n }\n\n // Check cache first\n if (this.statCache) {\n const cacheKey = createCacheKey(this.options.bucket, 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 try {\n const command = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const response = await this.client.send(command);\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || response.ContentType === \"application/x-directory\") {\n return {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: response.LastModified,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true),\n },\n };\n }\n\n const result = {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: response.LastModified,\n metadata: {\n kind: \"afs:document\",\n size: response.ContentLength,\n contentType: response.ContentType,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n storageClass: response.StorageClass,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, false),\n },\n };\n\n // Cache the result\n if (this.statCache) {\n const cacheKey = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", path);\n this.statCache.set(cacheKey, result);\n }\n\n return result;\n } catch (error: any) {\n // If 404, check if it's a directory prefix\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n // Check if there are objects with this prefix\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: `${key}/`,\n MaxKeys: 1,\n });\n\n const listResponse = await this.client.send(listCommand);\n\n if (listResponse.Contents && listResponse.Contents.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(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n try {\n const markerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n const markerResponse = await this.client.send(markerCommand);\n\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n updatedAt: markerResponse.LastModified,\n metadata: {\n kind: \"afs:node\",\n childrenCount: undefined,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n } catch {\n // Neither file nor directory exists - ensure path has leading slash\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n throw new AFSNotFoundError(normalizedPath);\n }\n }\n\n throw error;\n }\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n // Ensure path has leading slash for mapS3Error\n const normalizedPath = (ctx.params.path ?? \"\").startsWith(\"/\")\n ? (ctx.params.path ?? \"/\")\n : `/${ctx.params.path ?? \"\"}`;\n throw mapS3Error(error, normalizedPath);\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.buildS3Key(ctx.params.path);\n\n // Prepare content\n let body: Buffer;\n if (typeof payload.content === \"string\") {\n body = Buffer.from(payload.content, \"utf-8\");\n } else if (Buffer.isBuffer(payload.content)) {\n body = payload.content;\n } else if (payload.content !== undefined) {\n // JSON content\n body = Buffer.from(JSON.stringify(payload.content), \"utf-8\");\n } else {\n body = Buffer.from(\"\");\n }\n\n // Determine content type from metadata or default\n const contentType =\n (payload.metadata?.mimeType as string) ??\n (payload.metadata?.contentType as string) ??\n \"application/octet-stream\";\n\n let etag: string | undefined;\n let versionId: string | undefined;\n\n // Use multipart upload for large files\n if (shouldUseMultipart(body.length)) {\n const result = await multipartUpload(this.client, this.options.bucket, key, body, {\n contentType,\n });\n etag = result.etag;\n versionId = result.versionId;\n } else {\n const command = new PutObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n });\n\n const response = await this.client.send(command);\n etag = response.ETag?.replace(/\"/g, \"\");\n versionId = response.VersionId;\n }\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 metadata: {\n size: body.length,\n etag,\n versionId,\n ...payload.metadata,\n },\n },\n };\n } catch (error) {\n const normalizedPath = ctx.params.path.startsWith(\"/\")\n ? ctx.params.path\n : `/${ctx.params.path}`;\n throw mapS3Error(error, normalizedPath);\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.buildS3Key(ctx.params.path);\n\n // Check if the object exists first (S3 DeleteObject is idempotent and doesn't error on missing objects)\n try {\n const headCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n await this.client.send(headCommand);\n } catch (error: any) {\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n throw new AFSNotFoundError(`/${ctx.params.path}`);\n }\n throw error;\n }\n\n const command = new DeleteObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n await this.client.send(command);\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 } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapS3Error(error, `/${ctx.params.path}`);\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: \"select\",\n path: `${basePath}/.actions/select`,\n summary: \"Query with S3 Select\",\n metadata: { kind: \"afs:executable\", kinds: [\"afs:executable\", \"afs:node\"] },\n },\n {\n id: \"presign-download\",\n path: `${basePath}/.actions/presign-download`,\n summary: \"Generate 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 upload URL\",\n metadata: { kind: \"afs:executable\", kinds: [\"afs:executable\", \"afs:node\"] },\n },\n ],\n };\n }\n\n @Actions.Exec(\"/:path*\", \"select\")\n async selectActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const expression = args.expression as string;\n if (!expression) {\n throw new AFSError(\"Missing required argument: expression\", \"AFS_INVALID_ARGUMENT\");\n }\n\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n const result = await selectQuery(this.client, this.options.bucket, \"\", key, expression, {\n inputFormat: args.inputFormat as \"CSV\" | \"JSON\" | \"Parquet\" | undefined,\n outputFormat: args.outputFormat as \"CSV\" | \"JSON\" | undefined,\n });\n\n return {\n success: true,\n data: {\n records: result.records,\n stats: result.stats,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-download\")\n async presignDownloadActionHandler(\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\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 command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const url = await getSignedUrl(this.client, command, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();\n\n return {\n success: true,\n data: {\n url,\n expiresAt,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-upload\")\n async presignUploadActionHandler(\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\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 command = new PutObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n ContentType: (args.contentType as string) ?? \"application/octet-stream\",\n });\n\n const url = await getSignedUrl(this.client, command, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();\n\n return {\n success: true,\n data: {\n url,\n expiresAt,\n },\n };\n }\n\n // ========== Legacy API compatibility (for migration) ==========\n\n /**\n * Generate a presigned URL for downloading an object\n * @deprecated Use action /.actions/presign-download instead\n */\n async getPresignedDownloadUrl(path: string, options?: { expiresIn?: number }): Promise<string> {\n const result = await this.presignDownloadActionHandler(\n { path: `/${path}`, params: { path }, options: {} },\n { expiresIn: options?.expiresIn },\n );\n return result.data!.url as string;\n }\n\n /**\n * Generate a presigned URL for uploading an object\n * @deprecated Use action /.actions/presign-upload instead\n */\n async getPresignedUploadUrl(\n path: string,\n options?: { expiresIn?: number; contentType?: string },\n ): Promise<string> {\n const result = await this.presignUploadActionHandler(\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 of an object\n * @deprecated Use list on /:path/@versions instead\n */\n async listVersions(path: string): Promise<\n Array<{\n versionId: string;\n isLatest: boolean;\n lastModified?: 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 versionId: entry.metadata?.versionId as string,\n isLatest: entry.metadata?.isLatest as boolean,\n lastModified: 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 of an object\n * @deprecated Use read on /:path/@versions/:versionId instead\n */\n async readVersion(\n path: string,\n versionId: string,\n ): Promise<{ content: string; metadata: Record<string, unknown> }> {\n const result = await this.readVersionHandler({\n path: `/${path}/@versions/${versionId}`,\n params: { path, versionId },\n options: {},\n });\n return {\n content: result.content as string,\n metadata: result.metadata as Record<string, unknown>,\n };\n }\n\n /**\n * Run a SQL-like query on a CSV/JSON/Parquet file (S3 Select)\n * @deprecated Use action /.actions/select instead\n */\n async select(\n path: string,\n query: string,\n options?: {\n inputFormat?: \"CSV\" | \"JSON\" | \"Parquet\";\n csv?: {\n fieldDelimiter?: string;\n recordDelimiter?: string;\n fileHeaderInfo?: \"USE\" | \"IGNORE\" | \"NONE\";\n };\n json?: {\n type?: \"DOCUMENT\" | \"LINES\";\n };\n },\n ): Promise<{\n records: unknown[];\n stats?: { bytesScanned: number; bytesProcessed: number; bytesReturned: number };\n }> {\n return selectQuery(\n this.client,\n this.options.bucket,\n this.options.prefix ?? \"\",\n path,\n query,\n options,\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.buildS3Key(path);\n const dirKey = key.endsWith(\"/\") ? key : `${key}/`;\n\n const command = new PutObjectCommand({\n Bucket: this.options.bucket,\n Key: dirKey,\n Body: Buffer.from(\"\"),\n ContentType: \"application/x-directory\",\n });\n\n await this.client.send(command);\n }\n}\n\n// Type check: AFSS3 should implement AFSModuleClass\nconst _typeCheck: AFSModuleClass<AFSS3, AFSS3Options> = AFSS3;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["AFSError","response","objectCount","prefixCount","lines"],"sources":["../src/cache.ts","../src/client.ts","../src/errors.ts","../src/operations/multipart.ts","../src/operations/select.ts","../src/platform-ref.ts","../src/types.ts","../src/s3-afs.ts"],"sourcesContent":["/**\n * S3 Response Caching (Phase 4)\n *\n * LRU cache with TTL for S3 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 * S3 Client Factory\n *\n * Creates and configures AWS S3 clients.\n */\n\nimport { S3Client, type S3ClientConfig } from \"@aws-sdk/client-s3\";\nimport type { AFSS3Options } from \"./types.js\";\n\n/**\n * Create an S3 client with the given options\n *\n * Uses AWS SDK's default credential chain:\n * 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)\n * 2. Shared credentials file (~/.aws/credentials)\n * 3. IAM role (EC2/ECS/Lambda)\n * 4. SSO credentials\n *\n * @param options - S3 provider options\n * @returns Configured S3 client\n */\nexport function createS3Client(options: AFSS3Options): S3Client {\n const config: S3ClientConfig = {};\n\n // Region configuration\n // For S3-compatible services (MinIO, R2, B2), default to \"us-east-1\" if no region specified\n if (options.region) {\n config.region = options.region;\n } else if (options.endpoint) {\n config.region = \"us-east-1\";\n }\n\n // Custom endpoint for S3-compatible services\n if (options.endpoint) {\n config.endpoint = options.endpoint;\n }\n\n // Force path-style URLs (needed for MinIO, some S3-compatible services)\n if (options.forcePathStyle) {\n config.forcePathStyle = true;\n }\n\n // Explicit credentials (for testing or non-AWS environments)\n if (options.credentials) {\n config.credentials = {\n accessKeyId: options.credentials.accessKeyId,\n secretAccessKey: options.credentials.secretAccessKey,\n sessionToken: options.credentials.sessionToken,\n };\n }\n\n return new S3Client(config);\n}\n","/**\n * S3 Error Handling\n *\n * Maps AWS S3 errors to AFS errors.\n */\n\nimport { AFSNotFoundError } from \"@aigne/afs\";\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_LIMITED: \"RATE_LIMITED\",\n TYPE_MISMATCH: \"TYPE_MISMATCH\",\n INTERNAL_ERROR: \"INTERNAL_ERROR\",\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 * Map S3 error to AFS error\n *\n * @param error - AWS S3 error\n * @param path - Optional path for AFSNotFoundError (with leading slash)\n * @returns AFS error (or throws the original error if it's already an AFS error)\n */\nexport function mapS3Error(error: unknown, path?: string): AFSError | AFSNotFoundError | never {\n // Pass through AFS errors unchanged (check by name since different module instances)\n if (error && typeof error === \"object\" && \"name\" in error) {\n const errorObj = error as { name?: string; code?: string };\n if (\n errorObj.name === \"AFSNotFoundError\" ||\n errorObj.name === \"AFSError\" ||\n errorObj.code === \"AFS_NOT_FOUND\"\n ) {\n throw error;\n }\n }\n\n // Handle AWS SDK errors\n if (error && typeof error === \"object\" && \"name\" in error) {\n const awsError = error as {\n name: string;\n message?: string;\n $metadata?: { httpStatusCode?: number };\n };\n const message = awsError.message ?? \"Unknown S3 error\";\n\n switch (awsError.name) {\n case \"NoSuchBucket\":\n return new AFSError(AFSErrorCode.MODULE_NOT_FOUND, `Bucket not found: ${message}`);\n\n case \"NoSuchKey\":\n case \"NotFound\":\n if (path) {\n return new AFSNotFoundError(path);\n }\n return new AFSError(AFSErrorCode.ENTRY_NOT_FOUND, `Object not found: ${message}`);\n\n case \"AccessDenied\":\n case \"Forbidden\":\n return new AFSError(AFSErrorCode.PERMISSION_DENIED, `Access denied: ${message}`);\n\n case \"InvalidAccessKeyId\":\n case \"SignatureDoesNotMatch\":\n case \"ExpiredToken\":\n case \"TokenRefreshRequired\":\n return new AFSError(AFSErrorCode.AUTH_ERROR, `Authentication failed: ${message}`);\n\n case \"SlowDown\":\n case \"ServiceUnavailable\":\n return new AFSError(AFSErrorCode.RATE_LIMITED, `Rate limited: ${message}`, {\n retryAfter: 1000,\n });\n\n default: {\n // Check HTTP status code\n const statusCode = awsError.$metadata?.httpStatusCode;\n if (statusCode === 404) {\n if (path) {\n return new AFSNotFoundError(path);\n }\n return new AFSError(AFSErrorCode.ENTRY_NOT_FOUND, message);\n }\n if (statusCode === 403) {\n return new AFSError(AFSErrorCode.PERMISSION_DENIED, message);\n }\n if (statusCode === 401) {\n return new AFSError(AFSErrorCode.AUTH_ERROR, message);\n }\n if (statusCode === 429 || statusCode === 503) {\n return new AFSError(AFSErrorCode.RATE_LIMITED, message, { retryAfter: 1000 });\n }\n\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, message);\n }\n }\n }\n\n // Handle generic errors\n if (error instanceof Error) {\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, error.message);\n }\n\n return new AFSError(AFSErrorCode.INTERNAL_ERROR, String(error));\n}\n","/**\n * S3 Multipart Upload (Phase 2)\n *\n * Handles multipart upload for large files (>5GB).\n * S3 requires multipart upload for objects larger than 5GB.\n */\n\nimport {\n AbortMultipartUploadCommand,\n CompleteMultipartUploadCommand,\n CreateMultipartUploadCommand,\n type S3Client,\n UploadPartCommand,\n} from \"@aws-sdk/client-s3\";\nimport { mapS3Error } from \"../errors.js\";\n\n/**\n * Minimum part size (5MB) - AWS S3 requirement\n */\nconst MIN_PART_SIZE = 5 * 1024 * 1024;\n\n/**\n * Maximum part size (5GB) - AWS S3 requirement\n */\nconst MAX_PART_SIZE = 5 * 1024 * 1024 * 1024;\n\n/**\n * Threshold for switching to multipart upload (100MB)\n * Files larger than this will use multipart upload\n */\nexport const MULTIPART_THRESHOLD = 100 * 1024 * 1024;\n\n/**\n * Default part size (10MB)\n */\nconst DEFAULT_PART_SIZE = 10 * 1024 * 1024;\n\n/**\n * Maximum number of parts (10,000) - AWS S3 requirement\n */\nconst MAX_PARTS = 10000;\n\ninterface MultipartUploadOptions {\n /** Part size in bytes (default: 10MB) */\n partSize?: number;\n /** Content type */\n contentType?: string;\n /** Metadata to attach */\n metadata?: Record<string, string>;\n}\n\ninterface MultipartUploadResult {\n /** ETag of the completed upload */\n etag: string;\n /** Version ID if versioning is enabled */\n versionId?: string;\n}\n\n/**\n * Calculate optimal part size based on file size\n */\nfunction calculatePartSize(fileSize: number, requestedPartSize?: number): number {\n let partSize = requestedPartSize ?? DEFAULT_PART_SIZE;\n\n // Ensure minimum part size\n if (partSize < MIN_PART_SIZE) {\n partSize = MIN_PART_SIZE;\n }\n\n // Ensure maximum part size\n if (partSize > MAX_PART_SIZE) {\n partSize = MAX_PART_SIZE;\n }\n\n // If file is too large for current part size, increase it\n const partsNeeded = Math.ceil(fileSize / partSize);\n if (partsNeeded > MAX_PARTS) {\n // Calculate minimum part size needed to fit in MAX_PARTS\n partSize = Math.ceil(fileSize / MAX_PARTS);\n // Round up to next MB for efficiency\n partSize = Math.ceil(partSize / (1024 * 1024)) * 1024 * 1024;\n }\n\n return partSize;\n}\n\n/**\n * Upload a large file using multipart upload\n *\n * @param client - S3 client\n * @param bucket - Bucket name\n * @param key - Object key\n * @param data - File content as Buffer\n * @param options - Upload options\n * @returns Upload result with ETag\n */\nexport async function multipartUpload(\n client: S3Client,\n bucket: string,\n key: string,\n data: Buffer,\n options?: MultipartUploadOptions,\n): Promise<MultipartUploadResult> {\n const fileSize = data.length;\n const partSize = calculatePartSize(fileSize, options?.partSize);\n\n // Initiate multipart upload\n const createCommand = new CreateMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n ContentType: options?.contentType ?? \"application/octet-stream\",\n Metadata: options?.metadata,\n });\n\n const createResponse = await client.send(createCommand);\n const uploadId = createResponse.UploadId;\n\n if (!uploadId) {\n throw new Error(\"Failed to initiate multipart upload: no uploadId returned\");\n }\n\n const parts: { ETag: string; PartNumber: number }[] = [];\n\n try {\n // Upload parts\n const totalParts = Math.ceil(fileSize / partSize);\n\n for (let partNumber = 1; partNumber <= totalParts; partNumber++) {\n const start = (partNumber - 1) * partSize;\n const end = Math.min(start + partSize, fileSize);\n const partData = data.subarray(start, end);\n\n const uploadPartCommand = new UploadPartCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n PartNumber: partNumber,\n Body: partData,\n });\n\n const uploadPartResponse = await client.send(uploadPartCommand);\n\n if (!uploadPartResponse.ETag) {\n throw new Error(`Failed to upload part ${partNumber}: no ETag returned`);\n }\n\n parts.push({\n ETag: uploadPartResponse.ETag,\n PartNumber: partNumber,\n });\n }\n\n // Complete multipart upload\n const completeCommand = new CompleteMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n MultipartUpload: {\n Parts: parts,\n },\n });\n\n const completeResponse = await client.send(completeCommand);\n\n return {\n etag: completeResponse.ETag?.replace(/\"/g, \"\") ?? \"\",\n versionId: completeResponse.VersionId,\n };\n } catch (error) {\n // Abort the multipart upload on failure\n try {\n const abortCommand = new AbortMultipartUploadCommand({\n Bucket: bucket,\n Key: key,\n UploadId: uploadId,\n });\n await client.send(abortCommand);\n } catch {\n // Ignore abort errors\n }\n\n throw mapS3Error(error);\n }\n}\n\n/**\n * Check if a file should use multipart upload\n */\nexport function shouldUseMultipart(size: number): boolean {\n return size >= MULTIPART_THRESHOLD;\n}\n","/**\n * S3 Select Support (Phase 3)\n *\n * Runs SQL-like queries on CSV and JSON files stored in S3.\n * This enables server-side filtering, reducing data transfer.\n */\n\nimport {\n type S3Client,\n SelectObjectContentCommand,\n type SelectObjectContentEventStream,\n} from \"@aws-sdk/client-s3\";\nimport { mapS3Error } from \"../errors.js\";\n\n/**\n * Input serialization format\n */\nexport type InputFormat = \"CSV\" | \"JSON\" | \"Parquet\";\n\n/**\n * Select query options\n */\nexport interface SelectOptions {\n /** Input format (default: auto-detected from extension) */\n inputFormat?: InputFormat;\n /** CSV options */\n csv?: {\n /** Field delimiter (default: ,) */\n fieldDelimiter?: string;\n /** Record delimiter (default: \\n) */\n recordDelimiter?: string;\n /** Whether first row is header (default: true) */\n fileHeaderInfo?: \"USE\" | \"IGNORE\" | \"NONE\";\n /** Quote character (default: \") */\n quoteCharacter?: string;\n /** Comment character */\n comments?: string;\n };\n /** JSON options */\n json?: {\n /** JSON document type */\n type?: \"DOCUMENT\" | \"LINES\";\n };\n /** Output format (default: JSON) */\n outputFormat?: \"CSV\" | \"JSON\";\n}\n\n/**\n * Select query result\n */\nexport interface SelectResult {\n /** Query results as array of records */\n records: unknown[];\n /** Stats about the query */\n stats?: {\n bytesScanned: number;\n bytesProcessed: number;\n bytesReturned: number;\n };\n}\n\n/**\n * Detect input format from file extension\n */\nfunction detectInputFormat(path: string): InputFormat {\n const ext = path.toLowerCase().split(\".\").pop();\n switch (ext) {\n case \"csv\":\n case \"tsv\":\n return \"CSV\";\n case \"json\":\n case \"jsonl\":\n case \"ndjson\":\n return \"JSON\";\n case \"parquet\":\n return \"Parquet\";\n default:\n return \"JSON\";\n }\n}\n\n/**\n * Run a SQL-like query on an S3 object\n *\n * @param client - S3 client\n * @param bucket - Bucket name\n * @param mountPrefix - Mount prefix from options\n * @param path - Path relative to mount point\n * @param query - SQL query (e.g., \"SELECT * FROM s3object WHERE age > 21\")\n * @param options - Select options\n * @returns Query results\n */\nexport async function selectQuery(\n client: S3Client,\n bucket: string,\n mountPrefix: string,\n path: string,\n query: string,\n options?: SelectOptions,\n): Promise<SelectResult> {\n try {\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = mountPrefix ? `${mountPrefix}/${normalizedPath}` : normalizedPath;\n\n const inputFormat = options?.inputFormat ?? detectInputFormat(path);\n const outputFormat = options?.outputFormat ?? \"JSON\";\n\n // Build input serialization based on format\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const inputSerialization: Record<string, any> = {};\n\n if (inputFormat === \"CSV\") {\n inputSerialization.CSV = {\n FieldDelimiter: options?.csv?.fieldDelimiter ?? \",\",\n RecordDelimiter: options?.csv?.recordDelimiter ?? \"\\n\",\n FileHeaderInfo: options?.csv?.fileHeaderInfo ?? \"USE\",\n QuoteCharacter: options?.csv?.quoteCharacter ?? '\"',\n Comments: options?.csv?.comments,\n };\n } else if (inputFormat === \"JSON\") {\n inputSerialization.JSON = {\n Type: options?.json?.type ?? \"DOCUMENT\",\n };\n } else if (inputFormat === \"Parquet\") {\n inputSerialization.Parquet = {};\n }\n\n // Build output serialization\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const outputSerialization: Record<string, any> = {};\n\n if (outputFormat === \"JSON\") {\n outputSerialization.JSON = {};\n } else {\n outputSerialization.CSV = {};\n }\n\n const command = new SelectObjectContentCommand({\n Bucket: bucket,\n Key: key,\n ExpressionType: \"SQL\",\n Expression: query,\n InputSerialization: inputSerialization,\n OutputSerialization: outputSerialization,\n });\n\n const response = await client.send(command);\n\n // Process the event stream\n const records: unknown[] = [];\n let stats: SelectResult[\"stats\"] | undefined;\n\n if (response.Payload) {\n for await (const event of response.Payload as AsyncIterable<SelectObjectContentEventStream>) {\n if (event.Records?.Payload) {\n // Parse records from payload\n const text = new TextDecoder().decode(event.Records.Payload);\n // Split by newline and parse each line as JSON\n const lines = text.split(\"\\n\").filter((line) => line.trim());\n for (const line of lines) {\n try {\n records.push(JSON.parse(line));\n } catch {\n // If not valid JSON, just push as string\n records.push(line);\n }\n }\n }\n if (event.Stats?.Details) {\n stats = {\n bytesScanned: Number(event.Stats.Details.BytesScanned ?? 0),\n bytesProcessed: Number(event.Stats.Details.BytesProcessed ?? 0),\n bytesReturned: Number(event.Stats.Details.BytesReturned ?? 0),\n };\n }\n }\n }\n\n return { records, stats };\n } catch (error) {\n throw mapS3Error(error);\n }\n}\n","/**\n * S3 Platform Reference\n *\n * Generates AWS Console URLs for S3 objects and directories.\n */\n\n/**\n * Platform reference containing console URL\n */\nexport interface S3PlatformRef {\n consoleUrl: string;\n}\n\n/**\n * Generate platform reference with AWS Console URL\n *\n * @param bucket - S3 bucket name\n * @param region - AWS region (defaults to us-east-1)\n * @param key - S3 object 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 region: string | undefined,\n key: string,\n isDirectory: boolean,\n): S3PlatformRef {\n const effectiveRegion = region || \"us-east-1\";\n\n if (isDirectory) {\n // Directory URL: https://s3.console.aws.amazon.com/s3/buckets/{bucket}?region={region}&prefix={prefix}/\n const normalizedPrefix = key.endsWith(\"/\") ? key : key ? `${key}/` : \"\";\n return {\n consoleUrl: `https://s3.console.aws.amazon.com/s3/buckets/${bucket}?region=${effectiveRegion}&prefix=${normalizedPrefix}`,\n };\n }\n\n // File URL: https://s3.console.aws.amazon.com/s3/object/{bucket}?region={region}&prefix={key}\n const encodedKey = encodeURIComponent(key).replace(/%2F/g, \"/\");\n return {\n consoleUrl: `https://s3.console.aws.amazon.com/s3/object/${bucket}?region=${effectiveRegion}&prefix=${encodedKey}`,\n };\n}\n","/**\n * AFS S3 Provider Types\n */\n\nimport { camelize, optionalize } from \"@aigne/afs/utils/zod\";\nimport type { S3Client } from \"@aws-sdk/client-s3\";\nimport { z } from \"zod\";\n\n/**\n * Configuration options for AFSS3\n */\nexport interface AFSS3Options {\n /** Module name (used as mount path segment) */\n name?: string;\n\n /** Module description */\n description?: string;\n\n /** S3 bucket name */\n bucket: string;\n\n /** Key prefix (optional) */\n prefix?: string;\n\n /** AWS region (defaults to environment) */\n region?: string;\n\n /** Access mode */\n accessMode?: \"readonly\" | \"readwrite\";\n\n /** Custom endpoint for S3-compatible services (MinIO, R2, B2) */\n endpoint?: string;\n\n /** Force path-style URLs (needed for some S3-compatible services) */\n forcePathStyle?: boolean;\n\n /** AWS credentials profile name */\n profile?: string;\n\n /** Explicit credentials (for testing or non-AWS environments) */\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n\n /** Cache TTL in seconds (0 = no cache, Phase 4) */\n cacheTtl?: number;\n\n /**\n * Pre-configured S3 client (for testing or advanced use cases)\n * If provided, endpoint/region/credentials/profile options are ignored.\n */\n client?: S3Client;\n}\n\n/**\n * S3 bucket name validation regex\n * - 3-63 characters\n * - lowercase letters, numbers, hyphens, 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 * Zod schema for options validation\n */\nexport const afss3OptionsSchema = camelize(\n z\n .object({\n name: optionalize(z.string()),\n description: optionalize(z.string()),\n bucket: z.string().regex(BUCKET_NAME_REGEX, \"Invalid S3 bucket name\"),\n prefix: optionalize(z.string()),\n region: optionalize(z.string()),\n accessMode: optionalize(z.enum([\"readonly\", \"readwrite\"])),\n endpoint: optionalize(z.string().url()),\n forcePathStyle: optionalize(z.boolean()),\n profile: optionalize(z.string()),\n credentials: optionalize(\n z.object({\n accessKeyId: z.string(),\n secretAccessKey: z.string(),\n sessionToken: optionalize(z.string()),\n }),\n ),\n cacheTtl: optionalize(z.number().int().min(0)),\n })\n .strict(),\n);\n\n/**\n * Parsed S3 URI\n */\nexport interface ParsedS3Uri {\n bucket: string;\n prefix: string;\n}\n","/**\n * AFS S3 Provider\n *\n * S3 provider using AFSBaseProvider decorator routing pattern.\n * Provides access to AWS S3 and S3-compatible storage (MinIO, R2, B2).\n */\n\nimport {\n Actions,\n type AFSAccessMode,\n AFSBaseProvider,\n type AFSDeleteResult,\n type AFSEntry,\n AFSError,\n type AFSExecResult,\n type AFSExplainResult,\n type AFSListResult,\n type AFSModuleClass,\n type AFSModuleLoadParams,\n AFSNotFoundError,\n type AFSSearchResult,\n type AFSStatResult,\n type AFSWriteEntryPayload,\n type AFSWriteResult,\n type CapabilitiesManifest,\n Delete,\n Explain,\n List,\n Meta,\n Read,\n type RouteContext,\n Search,\n Stat,\n Write,\n} from \"@aigne/afs\";\nimport { zodParse } from \"@aigne/afs/utils/zod\";\nimport {\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n ListObjectVersionsCommand,\n PutObjectCommand,\n type S3Client,\n} from \"@aws-sdk/client-s3\";\nimport { getSignedUrl } from \"@aws-sdk/s3-request-presigner\";\nimport { joinURL } from \"ufo\";\nimport { createCacheKey, LRUCache } from \"./cache.js\";\nimport { createS3Client } from \"./client.js\";\nimport { mapS3Error } from \"./errors.js\";\nimport { multipartUpload, shouldUseMultipart } from \"./operations/multipart.js\";\nimport { selectQuery } from \"./operations/select.js\";\nimport { generatePlatformRef } from \"./platform-ref.js\";\nimport { type AFSS3Options, afss3OptionsSchema } 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 * AFSS3 Provider using Base Provider pattern\n *\n * Provides access to AWS S3 and S3-compatible storage through AFS.\n * Uses decorator routing (@List, @Read, @Write, @Delete, @Meta, @Actions).\n *\n * @example\n * ```typescript\n * const s3 = new AFSS3({\n * bucket: \"my-bucket\",\n * prefix: \"data\",\n * region: \"us-east-1\",\n * });\n *\n * // Mount to AFS\n * afs.mount(s3);\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 AFSS3 extends AFSBaseProvider {\n override readonly name: string;\n override readonly description?: string;\n override readonly accessMode: AFSAccessMode;\n\n private options: Required<Pick<AFSS3Options, \"bucket\">> & AFSS3Options;\n private client: S3Client;\n private listCache?: LRUCache<AFSListResult>;\n private statCache?: LRUCache<AFSEntry>;\n\n constructor(options: AFSS3Options) {\n super();\n\n // Extract client before validation (not serializable)\n const { client, ...restOptions } = options;\n\n // Validate options (excluding client)\n const parsed = afss3OptionsSchema.parse(restOptions);\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 ?? `S3 bucket: ${parsed.bucket}`;\n this.accessMode = this.options.accessMode ?? \"readonly\";\n\n // Use provided client or create one\n this.client = client ?? createS3Client(this.options);\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 afss3OptionsSchema;\n }\n\n /**\n * Load from configuration file\n */\n static async load({ basePath, config }: AFSModuleLoadParams = {}): Promise<AFSS3> {\n const options = zodParse(afss3OptionsSchema, config, { prefix: basePath });\n return new AFSS3(options);\n }\n\n // ========== Helper Methods ==========\n\n /**\n * Build the full S3 key from a path\n */\n private buildS3Key(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 an S3 object\n */\n private generateId(key: string): string {\n const cleanKey = key.replace(/^\\/+/, \"\");\n return `s3://${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 S3 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 opts = ctx.options as { limit?: number; maxChildren?: number } | undefined;\n const maxChildren = opts?.limit ?? opts?.maxChildren ?? 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 const normalizedPath = ctx.params.path\n ? ctx.params.path.startsWith(\"/\")\n ? ctx.params.path\n : `/${ctx.params.path}`\n : \"/\";\n throw mapS3Error(error, normalizedPath);\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 let continuationToken: string | undefined;\n\n do {\n const command = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: prefix,\n Delimiter: \"/\",\n MaxKeys: Math.min(maxChildren - childEntries.length, 1000),\n ContinuationToken: continuationToken,\n });\n\n const response = await this.client.send(command);\n\n // Add directories from CommonPrefixes\n if (response.CommonPrefixes) {\n for (const commonPrefix of response.CommonPrefixes) {\n if (!commonPrefix.Prefix) continue;\n\n // Extract directory name\n const dirName = commonPrefix.Prefix.slice(prefix.length).replace(/\\/$/, \"\");\n if (!dirName) continue;\n\n const normalizedPath = joinURL(\"/\", basePath, dirName);\n\n childEntries.push({\n id: this.generateId(commonPrefix.Prefix),\n path: normalizedPath,\n meta: {\n childrenCount: -1,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n commonPrefix.Prefix,\n true,\n ),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n // Add files from Contents\n if (response.Contents) {\n for (const object of response.Contents) {\n if (!object.Key) continue;\n\n // Skip the prefix itself (if it's a \"directory marker\")\n if (object.Key === prefix) continue;\n\n // Extract file name\n const fileName = object.Key.slice(prefix.length);\n if (!fileName || fileName.includes(\"/\")) continue;\n\n const normalizedPath = joinURL(\"/\", basePath, fileName);\n\n childEntries.push({\n id: this.generateId(object.Key),\n path: normalizedPath,\n updatedAt: object.LastModified,\n meta: {\n size: object.Size,\n lastModified: object.LastModified?.toISOString(),\n etag: object.ETag?.replace(/\"/g, \"\"),\n storageClass: object.StorageClass,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n object.Key,\n false,\n ),\n },\n });\n\n if (childEntries.length >= maxChildren) break;\n }\n }\n\n continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;\n } while (continuationToken && childEntries.length < maxChildren);\n\n const selfPath = basePath ? joinURL(\"/\", 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 or directory marker\n const key = this.options.prefix ? `${this.options.prefix}/${basePath}` : basePath;\n\n try {\n const headCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n await this.client.send(headCommand);\n\n // Path exists as an object (file) — files have no children\n return { data: [] };\n } catch (error: any) {\n // Also check for directory marker\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n try {\n const dirMarkerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n await this.client.send(dirMarkerCommand);\n // Directory marker exists but is empty\n } catch {\n // Neither file nor directory marker exists\n throw new AFSNotFoundError(selfPath);\n }\n } else {\n throw error;\n }\n }\n }\n\n // Return children only (no self-entry per AFS convention)\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 const command = new ListObjectVersionsCommand({\n Bucket: this.options.bucket,\n Prefix: key,\n MaxKeys: 1000,\n });\n\n const response = await this.client.send(command);\n const entries: AFSEntry[] = [];\n\n if (response.Versions) {\n for (const version of response.Versions) {\n // Only include exact key matches\n if (version.Key === key && version.VersionId) {\n const versionPath = joinURL(\"/\", normalizedPath, \"@versions\", version.VersionId);\n entries.push({\n id: `${this.generateId(key)}:${version.VersionId}`,\n path: versionPath,\n updatedAt: version.LastModified,\n meta: {\n versionId: version.VersionId,\n isLatest: version.IsLatest ?? false,\n lastModified: version.LastModified?.toISOString(),\n size: version.Size ?? 0,\n etag: version.ETag?.replace(/\"/g, \"\"),\n },\n });\n }\n }\n }\n\n return { data: entries };\n } catch (error) {\n throw mapS3Error(error, `/${ctx.params.path}/@versions`);\n }\n }\n\n // ========== Read Operations ==========\n\n @Read(\"/:path*\")\n async readHandler(ctx: RouteContext<{ path: string }>): Promise<AFSEntry> {\n const normalizedOutputPath = ctx.path.startsWith(\"/\") ? ctx.path : `/${ctx.path}`;\n\n try {\n const key = this.buildS3Key(ctx.params.path);\n\n // Handle root path - return bucket metadata with childrenCount\n if (!key) {\n // Count children in root\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n const listResponse = await this.client.send(listCommand);\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n\n return {\n id: this.generateId(\"/\"),\n path: \"/\",\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, \"\", true),\n },\n };\n }\n\n // Try to get the object directly (file case)\n try {\n const command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const response = await this.client.send(command);\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || response.ContentType === \"application/x-directory\") {\n // This is a directory marker - return directory info\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content: \"\",\n updatedAt: response.LastModified,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true),\n },\n };\n }\n\n // Read body as string\n let content: string;\n if (response.Body) {\n const bytes = await response.Body.transformToByteArray();\n content = new TextDecoder().decode(bytes);\n } else {\n content = \"\";\n }\n\n return {\n id: this.generateId(key),\n path: normalizedOutputPath,\n content,\n updatedAt: response.LastModified,\n meta: {\n size: response.ContentLength,\n mimeType: response.ContentType,\n contentType: response.ContentType,\n contentLength: response.ContentLength,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n contentRange: response.ContentRange,\n },\n };\n } catch (error: any) {\n // If 404, check if it's a directory prefix\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n // Check if there are objects with this prefix (it's a directory)\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: `${key}/`,\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n\n const listResponse = await this.client.send(listCommand);\n const hasChildren =\n (listResponse.Contents && listResponse.Contents.length > 0) ||\n (listResponse.CommonPrefixes && listResponse.CommonPrefixes.length > 0);\n\n if (hasChildren) {\n // It's a directory - return directory info\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n try {\n const markerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n await this.client.send(markerCommand);\n // Directory marker exists\n return {\n id: this.generateId(`${key}/`),\n path: normalizedOutputPath,\n content: \"\",\n meta: {\n kind: \"afs:node\",\n childrenCount: 0,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n } catch {\n // Neither file nor directory exists\n throw new AFSNotFoundError(normalizedOutputPath);\n }\n }\n throw error;\n }\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapS3Error(error, normalizedOutputPath);\n }\n }\n\n // ========== Version Read ==========\n\n @Read(\"/:path*/@versions/:versionId\")\n async readVersionHandler(\n ctx: RouteContext<{ path: string; versionId: 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\n const command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n VersionId: ctx.params.versionId,\n });\n\n const response = await this.client.send(command);\n\n const content = response.Body ? await response.Body.transformToString() : \"\";\n\n return {\n id: `${this.generateId(key)}:${ctx.params.versionId}`,\n path: ctx.path,\n content,\n updatedAt: response.LastModified,\n meta: {\n size: response.ContentLength,\n contentType: response.ContentType,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n versionId: response.VersionId,\n },\n };\n } catch (error) {\n throw mapS3Error(error, `/${ctx.params.path}/@versions/${ctx.params.versionId}`);\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 // Root metadata - count children\n if (!key) {\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : \"\",\n Delimiter: \"/\",\n MaxKeys: 1000,\n });\n const listResponse = await this.client.send(listCommand);\n const childrenCount =\n (listResponse.CommonPrefixes?.length ?? 0) + (listResponse.Contents?.length ?? 0);\n\n return {\n id: this.generateId(\"/\"),\n path: \"/.meta\",\n meta: {\n kind: \"afs:node\",\n childrenCount,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, \"\", true),\n },\n };\n }\n\n // Check cache first\n if (this.statCache) {\n const cacheKey = createCacheKey(this.options.bucket, 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 try {\n const command = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const response = await this.client.send(command);\n\n // Check if this is a directory marker\n if (key.endsWith(\"/\") || response.ContentType === \"application/x-directory\") {\n return {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: response.LastModified,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, true),\n },\n };\n }\n\n const result = {\n id: this.generateId(key),\n path: ctx.path,\n updatedAt: response.LastModified,\n meta: {\n kind: \"afs:document\",\n size: response.ContentLength,\n contentType: response.ContentType,\n lastModified: response.LastModified?.toISOString(),\n etag: response.ETag?.replace(/\"/g, \"\"),\n storageClass: response.StorageClass,\n platformRef: generatePlatformRef(this.options.bucket, this.options.region, key, false),\n },\n };\n\n // Cache the result\n if (this.statCache) {\n const cacheKey = createCacheKey(this.options.bucket, this.options.prefix ?? \"\", path);\n this.statCache.set(cacheKey, result);\n }\n\n return result;\n } catch (error: any) {\n // If 404, check if it's a directory prefix\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n // Check if there are objects with this prefix\n const listCommand = new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: `${key}/`,\n MaxKeys: 1,\n });\n\n const listResponse = await this.client.send(listCommand);\n\n if (listResponse.Contents && listResponse.Contents.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(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n }\n\n // Also check for directory marker (key ending with /)\n try {\n const markerCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: `${key}/`,\n });\n const markerResponse = await this.client.send(markerCommand);\n\n return {\n id: this.generateId(`${key}/`),\n path: ctx.path,\n updatedAt: markerResponse.LastModified,\n meta: {\n kind: \"afs:node\",\n childrenCount: -1,\n platformRef: generatePlatformRef(\n this.options.bucket,\n this.options.region,\n key,\n true,\n ),\n },\n };\n } catch {\n // Neither file nor directory exists - ensure path has leading slash\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n throw new AFSNotFoundError(normalizedPath);\n }\n }\n\n throw error;\n }\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n // Ensure path has leading slash for mapS3Error\n const normalizedPath = (ctx.params.path ?? \"\").startsWith(\"/\")\n ? (ctx.params.path ?? \"/\")\n : `/${ctx.params.path ?? \"\"}`;\n throw mapS3Error(error, normalizedPath);\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.buildS3Key(ctx.params.path);\n\n // Prepare content\n let body: Buffer;\n if (typeof payload.content === \"string\") {\n body = Buffer.from(payload.content, \"utf-8\");\n } else if (Buffer.isBuffer(payload.content)) {\n body = payload.content;\n } else if (payload.content !== undefined) {\n // JSON content\n body = Buffer.from(JSON.stringify(payload.content), \"utf-8\");\n } else {\n body = Buffer.from(\"\");\n }\n\n // Determine content type from metadata or default\n const contentType =\n (payload.meta?.mimeType as string) ??\n (payload.meta?.contentType as string) ??\n \"application/octet-stream\";\n\n let etag: string | undefined;\n let versionId: string | undefined;\n\n // Use multipart upload for large files\n if (shouldUseMultipart(body.length)) {\n const result = await multipartUpload(this.client, this.options.bucket, key, body, {\n contentType,\n });\n etag = result.etag;\n versionId = result.versionId;\n } else {\n const command = new PutObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n });\n\n const response = await this.client.send(command);\n etag = response.ETag?.replace(/\"/g, \"\");\n versionId = response.VersionId;\n }\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 meta: {\n size: body.length,\n etag,\n versionId,\n ...payload.meta,\n },\n },\n };\n } catch (error) {\n const normalizedPath = ctx.params.path.startsWith(\"/\")\n ? ctx.params.path\n : `/${ctx.params.path}`;\n throw mapS3Error(error, normalizedPath);\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.buildS3Key(ctx.params.path);\n\n // Check if the object exists first (S3 DeleteObject is idempotent and doesn't error on missing objects)\n try {\n const headCommand = new HeadObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n await this.client.send(headCommand);\n } catch (error: any) {\n if (error?.name === \"NotFound\" || error?.$metadata?.httpStatusCode === 404) {\n throw new AFSNotFoundError(`/${ctx.params.path}`);\n }\n throw error;\n }\n\n const command = new DeleteObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n await this.client.send(command);\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 } catch (error) {\n if (error instanceof AFSNotFoundError) {\n throw error;\n }\n throw mapS3Error(error, `/${ctx.params.path}`);\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: \"select\",\n path: joinURL(ctx.path, \".actions\", \"select\"),\n summary: \"Query with S3 Select\",\n meta: {\n kind: \"afs:executable\",\n kinds: [\"afs:executable\", \"afs:node\"],\n inputSchema: {\n type: \"object\",\n properties: {\n expression: { type: \"string\", description: \"SQL expression\" },\n inputFormat: { type: \"string\", description: \"CSV | JSON | Parquet\" },\n outputFormat: { type: \"string\", description: \"CSV | JSON\" },\n },\n required: [\"expression\"],\n },\n },\n },\n {\n id: \"presign-download\",\n path: joinURL(ctx.path, \".actions\", \"presign-download\"),\n summary: \"Generate 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, \".actions\", \"presign-upload\"),\n summary: \"Generate 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 };\n }\n\n @Actions.Exec(\"/:path*\", \"select\")\n async selectActionHandler(\n ctx: RouteContext<{ path: string }>,\n args: Record<string, unknown>,\n ): Promise<AFSExecResult> {\n const expression = args.expression as string;\n if (!expression) {\n throw new AFSError(\"Missing required argument: expression\", \"AFS_INVALID_ARGUMENT\");\n }\n\n const normalizedPath = ctx.params.path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const key = this.options.prefix ? `${this.options.prefix}/${normalizedPath}` : normalizedPath;\n\n const result = await selectQuery(this.client, this.options.bucket, \"\", key, expression, {\n inputFormat: args.inputFormat as \"CSV\" | \"JSON\" | \"Parquet\" | undefined,\n outputFormat: args.outputFormat as \"CSV\" | \"JSON\" | undefined,\n });\n\n return {\n success: true,\n data: {\n records: result.records,\n stats: result.stats,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-download\")\n async presignDownloadActionHandler(\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\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 command = new GetObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n });\n\n const url = await getSignedUrl(this.client, command, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();\n\n return {\n success: true,\n data: {\n url,\n expiresAt,\n },\n };\n }\n\n @Actions.Exec(\"/:path*\", \"presign-upload\")\n async presignUploadActionHandler(\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\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 command = new PutObjectCommand({\n Bucket: this.options.bucket,\n Key: key,\n ContentType: (args.contentType as string) ?? \"application/octet-stream\",\n });\n\n const url = await getSignedUrl(this.client, command, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();\n\n return {\n success: true,\n data: {\n url,\n expiresAt,\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 response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: this.options.prefix ? `${this.options.prefix}/` : undefined,\n Delimiter: \"/\",\n MaxKeys: 1000,\n }),\n );\n\n const objectCount = response.Contents?.length ?? 0;\n const prefixCount = response.CommonPrefixes?.length ?? 0;\n\n const lines: string[] = [];\n lines.push(`# ${this.options.bucket}`);\n lines.push(\"\");\n lines.push(`- **Type**: S3 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.region) {\n lines.push(`- **Region**: ${this.options.region}`);\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.buildS3Key(normalizedPath);\n\n // Try as object first (HeadObject)\n try {\n const head = await this.client.send(\n new HeadObjectCommand({ Bucket: this.options.bucket, Key: key }),\n );\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**: ${head.ContentLength ?? 0} bytes`);\n if (head.ContentType) lines.push(`- **Content-Type**: ${head.ContentType}`);\n if (head.StorageClass) lines.push(`- **Storage Class**: ${head.StorageClass}`);\n if (head.LastModified) lines.push(`- **Last Modified**: ${head.LastModified.toISOString()}`);\n if (head.ETag) lines.push(`- **ETag**: ${head.ETag}`);\n\n return { format: \"markdown\", content: lines.join(\"\\n\") };\n } catch {\n // Not an object, try as prefix (directory)\n }\n\n // Try as prefix\n const prefix = key.endsWith(\"/\") ? key : `${key}/`;\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: prefix,\n Delimiter: \"/\",\n MaxKeys: 1000,\n }),\n );\n\n const objectCount = response.Contents?.length ?? 0;\n const prefixCount = response.CommonPrefixes?.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 results: AFSEntry[] = [];\n let continuationToken: string | undefined;\n\n // Paginate through all objects under the prefix\n do {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: prefix || undefined,\n ContinuationToken: continuationToken,\n MaxKeys: 1000,\n }),\n );\n\n for (const obj of response.Contents ?? []) {\n if (!obj.Key) continue;\n\n // Get relative name (strip prefix)\n const relativePath = prefix ? obj.Key.slice(prefix.length) : obj.Key;\n if (!relativePath) continue;\n\n // Match against glob pattern\n if (minimatch(relativePath, query)) {\n const displayPath = joinURL(\"/\", normalizedPath, relativePath);\n results.push({\n id: this.generateId(obj.Key),\n path: displayPath,\n meta: {\n size: obj.Size,\n lastModified: obj.LastModified?.toISOString(),\n etag: obj.ETag?.replace(/\"/g, \"\"),\n },\n });\n }\n }\n\n continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;\n } while (continuationToken);\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: \"s3\",\n description: `S3 bucket: ${this.options.bucket}`,\n tools: [],\n operations: this.getOperationsDeclaration(),\n actions: [\n {\n description: \"S3 object actions\",\n catalog: [\n {\n name: \"select\",\n description: \"Query object contents using S3 Select (CSV/JSON/Parquet)\",\n inputSchema: {\n type: \"object\",\n properties: {\n expression: { type: \"string\", description: \"SQL expression\" },\n inputFormat: { type: \"string\", description: \"CSV | JSON | Parquet\" },\n outputFormat: { type: \"string\", description: \"CSV | JSON\" },\n },\n required: [\"expression\"],\n },\n },\n {\n name: \"presign-download\",\n description: \"Generate a pre-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 pre-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 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: AFSS3 should implement AFSModuleClass\nconst _typeCheck: AFSModuleClass<AFSS3, AFSS3Options> = AFSS3;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/afs-s3",
3
- "version": "1.11.0-beta.6",
3
+ "version": "1.11.0-beta.7",
4
4
  "description": "AIGNE AFS module for AWS S3 and S3-compatible storage",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -35,9 +35,11 @@
35
35
  "dependencies": {
36
36
  "@aws-sdk/client-s3": "^3.821.0",
37
37
  "@aws-sdk/s3-request-presigner": "^3.975.0",
38
+ "minimatch": "^10.1.1",
38
39
  "tslib": "^2.8.1",
40
+ "ufo": "^1.6.3",
39
41
  "zod": "^3.25.67",
40
- "@aigne/afs": "^1.11.0-beta.6"
42
+ "@aigne/afs": "^1.11.0-beta.7"
41
43
  },
42
44
  "devDependencies": {
43
45
  "@types/bun": "^1.3.6",
@@ -45,6 +47,7 @@
45
47
  "rimraf": "^6.1.2",
46
48
  "tsdown": "0.20.0-beta.3",
47
49
  "typescript": "5.9.2",
50
+ "@aigne/afs-testing": "1.11.0-beta.7",
48
51
  "@aigne/scripts": "0.0.0",
49
52
  "@aigne/typescript-config": "0.0.0"
50
53
  },