@aigne/afs-http 1.11.0-beta.4 → 1.11.0-beta.5

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/README.md CHANGED
@@ -135,6 +135,18 @@ interface AFSHttpClientOptions {
135
135
  /** Optional description */
136
136
  description?: string;
137
137
 
138
+ /** Access mode: "readonly" or "readwrite" (default: "readwrite") */
139
+ accessMode?: AFSAccessMode;
140
+
141
+ /** Authorization token - sent as Bearer token in Authorization header */
142
+ token?: string;
143
+
144
+ /** Request timeout in milliseconds (default: 30000) */
145
+ timeout?: number;
146
+
147
+ /** Maximum request body size in bytes (default: 10MB) */
148
+ maxBodySize?: number;
149
+
138
150
  /** Retry configuration */
139
151
  retry?: {
140
152
  maxAttempts?: number; // Default: 3
@@ -142,12 +154,6 @@ interface AFSHttpClientOptions {
142
154
  maxDelay?: number; // Default: 10000ms
143
155
  multiplier?: number; // Default: 2
144
156
  };
145
-
146
- /** Request timeout in milliseconds (default: 30000) */
147
- timeout?: number;
148
-
149
- /** Custom fetch implementation */
150
- fetch?: typeof fetch;
151
157
  }
152
158
  ```
153
159
 
@@ -260,22 +266,16 @@ const client = new AFSHttpClient({
260
266
 
261
267
  ## Advanced Usage
262
268
 
263
- ### Custom Authentication
269
+ ### Authentication
264
270
 
265
- Add authentication headers to the client:
271
+ Use the built-in `token` option for Bearer token authentication:
266
272
 
267
273
  ```typescript
268
- class AuthenticatedHttpClient extends AFSHttpClient {
269
- async fetch(url: string, options: RequestInit): Promise<Response> {
270
- return super.fetch(url, {
271
- ...options,
272
- headers: {
273
- ...options.headers,
274
- "Authorization": `Bearer ${process.env.API_TOKEN}`,
275
- },
276
- });
277
- }
278
- }
274
+ const client = new AFSHttpClient({
275
+ url: "http://localhost:3000/afs",
276
+ name: "remote",
277
+ token: process.env.AFS_TOKEN, // Sent as "Authorization: Bearer <token>"
278
+ });
279
279
  ```
280
280
 
281
281
  ### Custom Error Handling
package/dist/client.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ const require_protocol = require('./protocol.cjs');
1
2
  const require_errors = require('./errors.cjs');
2
3
  const require_retry = require('./retry.cjs');
3
4
 
@@ -38,6 +39,7 @@ var AFSHttpClient = class {
38
39
  description;
39
40
  accessMode;
40
41
  url;
42
+ token;
41
43
  timeout;
42
44
  maxBodySize;
43
45
  retryOptions;
@@ -48,6 +50,7 @@ var AFSHttpClient = class {
48
50
  this.name = options.name;
49
51
  this.description = options.description;
50
52
  this.accessMode = options.accessMode ?? DEFAULT_CLIENT_OPTIONS.accessMode;
53
+ this.token = options.token;
51
54
  this.timeout = options.timeout ?? DEFAULT_CLIENT_OPTIONS.timeout;
52
55
  this.maxBodySize = options.maxBodySize ?? DEFAULT_CLIENT_OPTIONS.maxBodySize;
53
56
  this.retryOptions = {
@@ -64,11 +67,13 @@ var AFSHttpClient = class {
64
67
  params
65
68
  });
66
69
  if (body.length > this.maxBodySize) throw new require_errors.AFSRuntimeError(`Request body too large: ${body.length} bytes exceeds limit of ${this.maxBodySize} bytes`);
70
+ const headers = { "Content-Type": "application/json" };
71
+ if (this.token) headers.Authorization = `Bearer ${this.token}`;
67
72
  let response;
68
73
  try {
69
74
  response = await require_retry.fetchWithRetry(this.url, {
70
75
  method: "POST",
71
- headers: { "Content-Type": "application/json" },
76
+ headers,
72
77
  body,
73
78
  signal: AbortSignal.timeout(this.timeout)
74
79
  }, this.retryOptions);
@@ -83,10 +88,13 @@ var AFSHttpClient = class {
83
88
  }
84
89
  if (!result.success) {
85
90
  const error = result.error;
86
- if (error) throw new require_errors.AFSRuntimeError(error.message || "Unknown error", {
87
- code: error.code,
88
- details: error.details
89
- });
91
+ if (error) {
92
+ if (error.code === require_protocol.AFSErrorCode.UNAUTHORIZED) throw new require_errors.AFSUnauthorizedError(error.message, error.details);
93
+ throw new require_errors.AFSRuntimeError(error.message || "Unknown error", {
94
+ code: error.code,
95
+ details: error.details
96
+ });
97
+ }
90
98
  throw new require_errors.AFSRuntimeError("Request failed without error details");
91
99
  }
92
100
  return result.data;
package/dist/client.d.cts CHANGED
@@ -14,6 +14,8 @@ interface AFSHttpClientOptions {
14
14
  description?: string;
15
15
  /** Access mode (default: "readwrite") */
16
16
  accessMode?: AFSAccessMode;
17
+ /** Authorization token (optional) */
18
+ token?: string;
17
19
  /** Request timeout in milliseconds (default: 30000) */
18
20
  timeout?: number;
19
21
  /** Maximum request body size in bytes (default: 10MB) */
@@ -49,6 +51,7 @@ declare class AFSHttpClient implements AFSModule {
49
51
  readonly description?: string;
50
52
  readonly accessMode: AFSAccessMode;
51
53
  private readonly url;
54
+ private readonly token?;
52
55
  private readonly timeout;
53
56
  private readonly maxBodySize;
54
57
  private readonly retryOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;AA0BA;AAiDA;UAjDiB,oBAAA;EAAA;EAAA,GAAA;EAAA;EAAA,IAAA;EAAA;EAAA,WAAA;EAAA;EAAA,UAAA,GAQF,aAAA;EAAA;EAAA,OAAA;EAAA;EAAA,WAAA;EAAA;EAAA,KAAA,GAML,YAAA;AAAA;AAAA;AAmCV;;;;;;;;;;;;;;;;;;;;;;AAnCU,cAmCG,aAAA,YAAyB,SAAA;EAAA,SAAA,IAAA;EAAA,SAAA,WAAA;EAAA,SAAA,UAAA,EAGf,aAAA;EAAA,iBAAA,GAAA;EAAA,iBAAA,OAAA;EAAA,iBAAA,WAAA;EAAA,iBAAA,YAAA;EAAA,YAAA,OAAA,EAOA,oBAAA;EAAA;;;EAAA,QAAA,GAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GAoFc,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GAIzB,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,MAAA,IAAA,UAAA,OAAA,EAMjD,oBAAA,EAAA,OAAA,GACC,eAAA,GACT,OAAA,CAAQ,cAAA;EAAA,OAAA,IAAA,UAAA,OAAA,GAI0B,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,OAAA,OAAA,UAAA,OAAA,UAAA,OAAA,GAOpD,gBAAA,GACT,OAAA,CAAQ,eAAA;EAAA,OAAA,IAAA,UAAA,KAAA,UAAA,OAAA,GAIyC,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,KAAA,IAAA,UAAA,IAAA,EAMvE,MAAA,mBAAA,OAAA,EACG,cAAA,GACR,OAAA,CAAQ,aAAA;AAAA"}
1
+ {"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;AA+BA;AAmDA;UAnDiB,oBAAA;EAAA;EAAA,GAAA;EAAA;EAAA,IAAA;EAAA;EAAA,WAAA;EAAA;EAAA,UAAA,GAQF,aAAA;EAAA;EAAA,KAAA;EAAA;EAAA,OAAA;EAAA;EAAA,WAAA;EAAA;EAAA,KAAA,GAQL,YAAA;AAAA;AAAA;AAmCV;;;;;;;;;;;;;;;;;;;;;;AAnCU,cAmCG,aAAA,YAAyB,SAAA;EAAA,SAAA,IAAA;EAAA,SAAA,WAAA;EAAA,SAAA,UAAA,EAGf,aAAA;EAAA,iBAAA,GAAA;EAAA,iBAAA,KAAA;EAAA,iBAAA,OAAA;EAAA,iBAAA,WAAA;EAAA,iBAAA,YAAA;EAAA,YAAA,OAAA,EAQA,oBAAA;EAAA;;;EAAA,QAAA,GAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GA+Fc,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GAIzB,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,MAAA,IAAA,UAAA,OAAA,EAMjD,oBAAA,EAAA,OAAA,GACC,eAAA,GACT,OAAA,CAAQ,cAAA;EAAA,OAAA,IAAA,UAAA,OAAA,GAI0B,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,OAAA,OAAA,UAAA,OAAA,UAAA,OAAA,GAOpD,gBAAA,GACT,OAAA,CAAQ,eAAA;EAAA,OAAA,IAAA,UAAA,KAAA,UAAA,OAAA,GAIyC,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,KAAA,IAAA,UAAA,IAAA,EAMvE,MAAA,mBAAA,OAAA,EACG,cAAA,GACR,OAAA,CAAQ,aAAA;AAAA"}
package/dist/client.d.mts CHANGED
@@ -14,6 +14,8 @@ interface AFSHttpClientOptions {
14
14
  description?: string;
15
15
  /** Access mode (default: "readwrite") */
16
16
  accessMode?: AFSAccessMode;
17
+ /** Authorization token (optional) */
18
+ token?: string;
17
19
  /** Request timeout in milliseconds (default: 30000) */
18
20
  timeout?: number;
19
21
  /** Maximum request body size in bytes (default: 10MB) */
@@ -49,6 +51,7 @@ declare class AFSHttpClient implements AFSModule {
49
51
  readonly description?: string;
50
52
  readonly accessMode: AFSAccessMode;
51
53
  private readonly url;
54
+ private readonly token?;
52
55
  private readonly timeout;
53
56
  private readonly maxBodySize;
54
57
  private readonly retryOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;AA0BA;AAiDA;UAjDiB,oBAAA;EAAA;EAAA,GAAA;EAAA;EAAA,IAAA;EAAA;EAAA,WAAA;EAAA;EAAA,UAAA,GAQF,aAAA;EAAA;EAAA,OAAA;EAAA;EAAA,WAAA;EAAA;EAAA,KAAA,GAML,YAAA;AAAA;AAAA;AAmCV;;;;;;;;;;;;;;;;;;;;;;AAnCU,cAmCG,aAAA,YAAyB,SAAA;EAAA,SAAA,IAAA;EAAA,SAAA,WAAA;EAAA,SAAA,UAAA,EAGf,aAAA;EAAA,iBAAA,GAAA;EAAA,iBAAA,OAAA;EAAA,iBAAA,WAAA;EAAA,iBAAA,YAAA;EAAA,YAAA,OAAA,EAOA,oBAAA;EAAA;;;EAAA,QAAA,GAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GAoFc,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GAIzB,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,MAAA,IAAA,UAAA,OAAA,EAMjD,oBAAA,EAAA,OAAA,GACC,eAAA,GACT,OAAA,CAAQ,cAAA;EAAA,OAAA,IAAA,UAAA,OAAA,GAI0B,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,OAAA,OAAA,UAAA,OAAA,UAAA,OAAA,GAOpD,gBAAA,GACT,OAAA,CAAQ,eAAA;EAAA,OAAA,IAAA,UAAA,KAAA,UAAA,OAAA,GAIyC,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,KAAA,IAAA,UAAA,IAAA,EAMvE,MAAA,mBAAA,OAAA,EACG,cAAA,GACR,OAAA,CAAQ,aAAA;AAAA"}
1
+ {"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;AA+BA;AAmDA;UAnDiB,oBAAA;EAAA;EAAA,GAAA;EAAA;EAAA,IAAA;EAAA;EAAA,WAAA;EAAA;EAAA,UAAA,GAQF,aAAA;EAAA;EAAA,KAAA;EAAA;EAAA,OAAA;EAAA;EAAA,WAAA;EAAA;EAAA,KAAA,GAQL,YAAA;AAAA;AAAA;AAmCV;;;;;;;;;;;;;;;;;;;;;;AAnCU,cAmCG,aAAA,YAAyB,SAAA;EAAA,SAAA,IAAA;EAAA,SAAA,WAAA;EAAA,SAAA,UAAA,EAGf,aAAA;EAAA,iBAAA,GAAA;EAAA,iBAAA,KAAA;EAAA,iBAAA,OAAA;EAAA,iBAAA,WAAA;EAAA,iBAAA,YAAA;EAAA,YAAA,OAAA,EAQA,oBAAA;EAAA;;;EAAA,QAAA,GAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GA+Fc,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,KAAA,IAAA,UAAA,OAAA,GAIzB,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAAA,MAAA,IAAA,UAAA,OAAA,EAMjD,oBAAA,EAAA,OAAA,GACC,eAAA,GACT,OAAA,CAAQ,cAAA;EAAA,OAAA,IAAA,UAAA,OAAA,GAI0B,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,OAAA,OAAA,UAAA,OAAA,UAAA,OAAA,GAOpD,gBAAA,GACT,OAAA,CAAQ,eAAA;EAAA,OAAA,IAAA,UAAA,KAAA,UAAA,OAAA,GAIyC,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAAA,KAAA,IAAA,UAAA,IAAA,EAMvE,MAAA,mBAAA,OAAA,EACG,cAAA,GACR,OAAA,CAAQ,aAAA;AAAA"}
package/dist/client.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { AFSNetworkError, AFSRuntimeError } from "./errors.mjs";
1
+ import { AFSErrorCode } from "./protocol.mjs";
2
+ import { AFSNetworkError, AFSRuntimeError, AFSUnauthorizedError } from "./errors.mjs";
2
3
  import { DEFAULT_RETRY_OPTIONS, fetchWithRetry } from "./retry.mjs";
3
4
 
4
5
  //#region src/client.ts
@@ -38,6 +39,7 @@ var AFSHttpClient = class {
38
39
  description;
39
40
  accessMode;
40
41
  url;
42
+ token;
41
43
  timeout;
42
44
  maxBodySize;
43
45
  retryOptions;
@@ -48,6 +50,7 @@ var AFSHttpClient = class {
48
50
  this.name = options.name;
49
51
  this.description = options.description;
50
52
  this.accessMode = options.accessMode ?? DEFAULT_CLIENT_OPTIONS.accessMode;
53
+ this.token = options.token;
51
54
  this.timeout = options.timeout ?? DEFAULT_CLIENT_OPTIONS.timeout;
52
55
  this.maxBodySize = options.maxBodySize ?? DEFAULT_CLIENT_OPTIONS.maxBodySize;
53
56
  this.retryOptions = {
@@ -64,11 +67,13 @@ var AFSHttpClient = class {
64
67
  params
65
68
  });
66
69
  if (body.length > this.maxBodySize) throw new AFSRuntimeError(`Request body too large: ${body.length} bytes exceeds limit of ${this.maxBodySize} bytes`);
70
+ const headers = { "Content-Type": "application/json" };
71
+ if (this.token) headers.Authorization = `Bearer ${this.token}`;
67
72
  let response;
68
73
  try {
69
74
  response = await fetchWithRetry(this.url, {
70
75
  method: "POST",
71
- headers: { "Content-Type": "application/json" },
76
+ headers,
72
77
  body,
73
78
  signal: AbortSignal.timeout(this.timeout)
74
79
  }, this.retryOptions);
@@ -83,10 +88,13 @@ var AFSHttpClient = class {
83
88
  }
84
89
  if (!result.success) {
85
90
  const error = result.error;
86
- if (error) throw new AFSRuntimeError(error.message || "Unknown error", {
87
- code: error.code,
88
- details: error.details
89
- });
91
+ if (error) {
92
+ if (error.code === AFSErrorCode.UNAUTHORIZED) throw new AFSUnauthorizedError(error.message, error.details);
93
+ throw new AFSRuntimeError(error.message || "Unknown error", {
94
+ code: error.code,
95
+ details: error.details
96
+ });
97
+ }
90
98
  throw new AFSRuntimeError("Request failed without error details");
91
99
  }
92
100
  return result.data;
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type {\n AFSAccessMode,\n AFSDeleteOptions,\n AFSDeleteResult,\n AFSExecOptions,\n AFSExecResult,\n AFSListOptions,\n AFSListResult,\n AFSModule,\n AFSReadOptions,\n AFSReadResult,\n AFSRenameOptions,\n AFSRenameResult,\n AFSSearchOptions,\n AFSSearchResult,\n AFSWriteEntryPayload,\n AFSWriteOptions,\n AFSWriteResult,\n} from \"@aigne/afs\";\nimport { AFSNetworkError, AFSRuntimeError } from \"./errors.js\";\nimport type { AFSRpcMethod, AFSRpcResponse, AFSRpcResults } from \"./protocol.js\";\nimport { DEFAULT_RETRY_OPTIONS, fetchWithRetry, type RetryOptions } from \"./retry.js\";\n\n/**\n * Options for AFSHttpClient\n */\nexport interface AFSHttpClientOptions {\n /** Server URL (required) */\n url: string;\n /** Module name for mounting to AFS (required) */\n name: string;\n /** Module description (optional) */\n description?: string;\n /** Access mode (default: \"readwrite\") */\n accessMode?: AFSAccessMode;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n /** Maximum request body size in bytes (default: 10MB) */\n maxBodySize?: number;\n /** Retry configuration */\n retry?: RetryOptions;\n}\n\n/**\n * Default client options\n */\nconst DEFAULT_CLIENT_OPTIONS = {\n timeout: 30000,\n maxBodySize: 10 * 1024 * 1024, // 10MB\n accessMode: \"readwrite\" as AFSAccessMode,\n};\n\n/**\n * AFS HTTP Client\n *\n * Implements the AFSModule interface, forwarding all operations to a remote\n * AFS server via HTTP RPC calls.\n *\n * @example\n * ```typescript\n * import { AFS } from \"@aigne/afs\";\n * import { AFSHttpClient } from \"@aigne/afs-http\";\n *\n * const afs = new AFS();\n * afs.mount(\n * new AFSHttpClient({\n * url: \"https://remote-afs.example.com/afs/rpc\",\n * name: \"remote\",\n * })\n * );\n *\n * // Use like any local provider\n * const result = await afs.list(\"/modules/remote/some-path\");\n * ```\n */\nexport class AFSHttpClient implements AFSModule {\n readonly name: string;\n readonly description?: string;\n readonly accessMode: AFSAccessMode;\n\n private readonly url: string;\n private readonly timeout: number;\n private readonly maxBodySize: number;\n private readonly retryOptions: RetryOptions;\n\n constructor(options: AFSHttpClientOptions) {\n if (!options.url) {\n throw new Error(\"AFSHttpClient requires a url option\");\n }\n if (!options.name) {\n throw new Error(\"AFSHttpClient requires a name option\");\n }\n\n this.url = options.url.endsWith(\"/rpc\") ? options.url : `${options.url}/rpc`;\n this.name = options.name;\n this.description = options.description;\n this.accessMode = options.accessMode ?? DEFAULT_CLIENT_OPTIONS.accessMode;\n this.timeout = options.timeout ?? DEFAULT_CLIENT_OPTIONS.timeout;\n this.maxBodySize = options.maxBodySize ?? DEFAULT_CLIENT_OPTIONS.maxBodySize;\n this.retryOptions = {\n ...DEFAULT_RETRY_OPTIONS,\n ...options.retry,\n };\n }\n\n /**\n * Make an RPC call to the remote server\n */\n private async rpc<M extends AFSRpcMethod>(\n method: M,\n params: Record<string, unknown>,\n ): Promise<AFSRpcResults[M]> {\n const body = JSON.stringify({ method, params });\n\n // Check body size before sending\n if (body.length > this.maxBodySize) {\n throw new AFSRuntimeError(\n `Request body too large: ${body.length} bytes exceeds limit of ${this.maxBodySize} bytes`,\n );\n }\n\n let response: Response;\n try {\n response = await fetchWithRetry(\n this.url,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body,\n signal: AbortSignal.timeout(this.timeout),\n },\n this.retryOptions,\n );\n } catch (error) {\n throw new AFSNetworkError(\n `Failed to connect to ${this.url}: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined,\n );\n }\n\n // Parse response\n let result: AFSRpcResponse<AFSRpcResults[M]>;\n try {\n result = await response.json();\n } catch {\n throw new AFSRuntimeError(\n `Invalid response from server: expected JSON, got ${response.headers.get(\"content-type\")}`,\n );\n }\n\n // Check for RPC-level errors\n if (!result.success) {\n const error = result.error;\n if (error) {\n // Re-throw with the appropriate error type based on code\n const errorMessage = error.message || \"Unknown error\";\n throw new AFSRuntimeError(errorMessage, {\n code: error.code,\n details: error.details,\n });\n }\n throw new AFSRuntimeError(\"Request failed without error details\");\n }\n\n return result.data as AFSRpcResults[M];\n }\n\n async list(path: string, options?: AFSListOptions): Promise<AFSListResult> {\n return this.rpc(\"list\", { path, options });\n }\n\n async read(path: string, options?: AFSReadOptions): Promise<AFSReadResult> {\n return this.rpc(\"read\", { path, options });\n }\n\n async write(\n path: string,\n content: AFSWriteEntryPayload,\n options?: AFSWriteOptions,\n ): Promise<AFSWriteResult> {\n return this.rpc(\"write\", { path, content, options });\n }\n\n async delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult> {\n return this.rpc(\"delete\", { path, options });\n }\n\n async rename(\n oldPath: string,\n newPath: string,\n options?: AFSRenameOptions,\n ): Promise<AFSRenameResult> {\n return this.rpc(\"rename\", { oldPath, newPath, options });\n }\n\n async search(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult> {\n return this.rpc(\"search\", { path, query, options });\n }\n\n async exec(\n path: string,\n args: Record<string, unknown>,\n options: AFSExecOptions,\n ): Promise<AFSExecResult> {\n return this.rpc(\"exec\", { path, args, options });\n }\n}\n"],"mappings":";;;;;;;AA8CA,MAAM,yBAAyB;CAC7B,SAAS;CACT,aAAa,KAAK,OAAO;CACzB,YAAY;CACb;;;;;;;;;;;;;;;;;;;;;;;;AAyBD,IAAa,gBAAb,MAAgD;CAC9C,AAAS;CACT,AAAS;CACT,AAAS;CAET,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAA+B;AACzC,MAAI,CAAC,QAAQ,IACX,OAAM,IAAI,MAAM,sCAAsC;AAExD,MAAI,CAAC,QAAQ,KACX,OAAM,IAAI,MAAM,uCAAuC;AAGzD,OAAK,MAAM,QAAQ,IAAI,SAAS,OAAO,GAAG,QAAQ,MAAM,GAAG,QAAQ,IAAI;AACvE,OAAK,OAAO,QAAQ;AACpB,OAAK,cAAc,QAAQ;AAC3B,OAAK,aAAa,QAAQ,cAAc,uBAAuB;AAC/D,OAAK,UAAU,QAAQ,WAAW,uBAAuB;AACzD,OAAK,cAAc,QAAQ,eAAe,uBAAuB;AACjE,OAAK,eAAe;GAClB,GAAG;GACH,GAAG,QAAQ;GACZ;;;;;CAMH,MAAc,IACZ,QACA,QAC2B;EAC3B,MAAM,OAAO,KAAK,UAAU;GAAE;GAAQ;GAAQ,CAAC;AAG/C,MAAI,KAAK,SAAS,KAAK,YACrB,OAAM,IAAI,gBACR,2BAA2B,KAAK,OAAO,0BAA0B,KAAK,YAAY,QACnF;EAGH,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,eACf,KAAK,KACL;IACE,QAAQ;IACR,SAAS,EACP,gBAAgB,oBACjB;IACD;IACA,QAAQ,YAAY,QAAQ,KAAK,QAAQ;IAC1C,EACD,KAAK,aACN;WACM,OAAO;AACd,SAAM,IAAI,gBACR,wBAAwB,KAAK,IAAI,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC3F,iBAAiB,QAAQ,QAAQ,OAClC;;EAIH,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,SAAS,MAAM;UACxB;AACN,SAAM,IAAI,gBACR,oDAAoD,SAAS,QAAQ,IAAI,eAAe,GACzF;;AAIH,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,QAAQ,OAAO;AACrB,OAAI,MAGF,OAAM,IAAI,gBADW,MAAM,WAAW,iBACE;IACtC,MAAM,MAAM;IACZ,SAAS,MAAM;IAChB,CAAC;AAEJ,SAAM,IAAI,gBAAgB,uCAAuC;;AAGnE,SAAO,OAAO;;CAGhB,MAAM,KAAK,MAAc,SAAkD;AACzE,SAAO,KAAK,IAAI,QAAQ;GAAE;GAAM;GAAS,CAAC;;CAG5C,MAAM,KAAK,MAAc,SAAkD;AACzE,SAAO,KAAK,IAAI,QAAQ;GAAE;GAAM;GAAS,CAAC;;CAG5C,MAAM,MACJ,MACA,SACA,SACyB;AACzB,SAAO,KAAK,IAAI,SAAS;GAAE;GAAM;GAAS;GAAS,CAAC;;CAGtD,MAAM,OAAO,MAAc,SAAsD;AAC/E,SAAO,KAAK,IAAI,UAAU;GAAE;GAAM;GAAS,CAAC;;CAG9C,MAAM,OACJ,SACA,SACA,SAC0B;AAC1B,SAAO,KAAK,IAAI,UAAU;GAAE;GAAS;GAAS;GAAS,CAAC;;CAG1D,MAAM,OAAO,MAAc,OAAe,SAAsD;AAC9F,SAAO,KAAK,IAAI,UAAU;GAAE;GAAM;GAAO;GAAS,CAAC;;CAGrD,MAAM,KACJ,MACA,MACA,SACwB;AACxB,SAAO,KAAK,IAAI,QAAQ;GAAE;GAAM;GAAM;GAAS,CAAC"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type {\n AFSAccessMode,\n AFSDeleteOptions,\n AFSDeleteResult,\n AFSExecOptions,\n AFSExecResult,\n AFSListOptions,\n AFSListResult,\n AFSModule,\n AFSReadOptions,\n AFSReadResult,\n AFSRenameOptions,\n AFSRenameResult,\n AFSSearchOptions,\n AFSSearchResult,\n AFSWriteEntryPayload,\n AFSWriteOptions,\n AFSWriteResult,\n} from \"@aigne/afs\";\nimport { AFSNetworkError, AFSRuntimeError, AFSUnauthorizedError } from \"./errors.js\";\nimport {\n AFSErrorCode,\n type AFSRpcMethod,\n type AFSRpcResponse,\n type AFSRpcResults,\n} from \"./protocol.js\";\nimport { DEFAULT_RETRY_OPTIONS, fetchWithRetry, type RetryOptions } from \"./retry.js\";\n\n/**\n * Options for AFSHttpClient\n */\nexport interface AFSHttpClientOptions {\n /** Server URL (required) */\n url: string;\n /** Module name for mounting to AFS (required) */\n name: string;\n /** Module description (optional) */\n description?: string;\n /** Access mode (default: \"readwrite\") */\n accessMode?: AFSAccessMode;\n /** Authorization token (optional) */\n token?: string;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n /** Maximum request body size in bytes (default: 10MB) */\n maxBodySize?: number;\n /** Retry configuration */\n retry?: RetryOptions;\n}\n\n/**\n * Default client options\n */\nconst DEFAULT_CLIENT_OPTIONS = {\n timeout: 30000,\n maxBodySize: 10 * 1024 * 1024, // 10MB\n accessMode: \"readwrite\" as AFSAccessMode,\n};\n\n/**\n * AFS HTTP Client\n *\n * Implements the AFSModule interface, forwarding all operations to a remote\n * AFS server via HTTP RPC calls.\n *\n * @example\n * ```typescript\n * import { AFS } from \"@aigne/afs\";\n * import { AFSHttpClient } from \"@aigne/afs-http\";\n *\n * const afs = new AFS();\n * afs.mount(\n * new AFSHttpClient({\n * url: \"https://remote-afs.example.com/afs/rpc\",\n * name: \"remote\",\n * })\n * );\n *\n * // Use like any local provider\n * const result = await afs.list(\"/modules/remote/some-path\");\n * ```\n */\nexport class AFSHttpClient implements AFSModule {\n readonly name: string;\n readonly description?: string;\n readonly accessMode: AFSAccessMode;\n\n private readonly url: string;\n private readonly token?: string;\n private readonly timeout: number;\n private readonly maxBodySize: number;\n private readonly retryOptions: RetryOptions;\n\n constructor(options: AFSHttpClientOptions) {\n if (!options.url) {\n throw new Error(\"AFSHttpClient requires a url option\");\n }\n if (!options.name) {\n throw new Error(\"AFSHttpClient requires a name option\");\n }\n\n this.url = options.url.endsWith(\"/rpc\") ? options.url : `${options.url}/rpc`;\n this.name = options.name;\n this.description = options.description;\n this.accessMode = options.accessMode ?? DEFAULT_CLIENT_OPTIONS.accessMode;\n this.token = options.token;\n this.timeout = options.timeout ?? DEFAULT_CLIENT_OPTIONS.timeout;\n this.maxBodySize = options.maxBodySize ?? DEFAULT_CLIENT_OPTIONS.maxBodySize;\n this.retryOptions = {\n ...DEFAULT_RETRY_OPTIONS,\n ...options.retry,\n };\n }\n\n /**\n * Make an RPC call to the remote server\n */\n private async rpc<M extends AFSRpcMethod>(\n method: M,\n params: Record<string, unknown>,\n ): Promise<AFSRpcResults[M]> {\n const body = JSON.stringify({ method, params });\n\n // Check body size before sending\n if (body.length > this.maxBodySize) {\n throw new AFSRuntimeError(\n `Request body too large: ${body.length} bytes exceeds limit of ${this.maxBodySize} bytes`,\n );\n }\n\n // Build headers\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (this.token) {\n headers.Authorization = `Bearer ${this.token}`;\n }\n\n let response: Response;\n try {\n response = await fetchWithRetry(\n this.url,\n {\n method: \"POST\",\n headers,\n body,\n signal: AbortSignal.timeout(this.timeout),\n },\n this.retryOptions,\n );\n } catch (error) {\n throw new AFSNetworkError(\n `Failed to connect to ${this.url}: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined,\n );\n }\n\n // Parse response\n let result: AFSRpcResponse<AFSRpcResults[M]>;\n try {\n result = await response.json();\n } catch {\n throw new AFSRuntimeError(\n `Invalid response from server: expected JSON, got ${response.headers.get(\"content-type\")}`,\n );\n }\n\n // Check for RPC-level errors\n if (!result.success) {\n const error = result.error;\n if (error) {\n // Handle unauthorized error specifically\n if (error.code === AFSErrorCode.UNAUTHORIZED) {\n throw new AFSUnauthorizedError(error.message, error.details);\n }\n // Re-throw with the appropriate error type based on code\n const errorMessage = error.message || \"Unknown error\";\n throw new AFSRuntimeError(errorMessage, {\n code: error.code,\n details: error.details,\n });\n }\n throw new AFSRuntimeError(\"Request failed without error details\");\n }\n\n return result.data as AFSRpcResults[M];\n }\n\n async list(path: string, options?: AFSListOptions): Promise<AFSListResult> {\n return this.rpc(\"list\", { path, options });\n }\n\n async read(path: string, options?: AFSReadOptions): Promise<AFSReadResult> {\n return this.rpc(\"read\", { path, options });\n }\n\n async write(\n path: string,\n content: AFSWriteEntryPayload,\n options?: AFSWriteOptions,\n ): Promise<AFSWriteResult> {\n return this.rpc(\"write\", { path, content, options });\n }\n\n async delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult> {\n return this.rpc(\"delete\", { path, options });\n }\n\n async rename(\n oldPath: string,\n newPath: string,\n options?: AFSRenameOptions,\n ): Promise<AFSRenameResult> {\n return this.rpc(\"rename\", { oldPath, newPath, options });\n }\n\n async search(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult> {\n return this.rpc(\"search\", { path, query, options });\n }\n\n async exec(\n path: string,\n args: Record<string, unknown>,\n options: AFSExecOptions,\n ): Promise<AFSExecResult> {\n return this.rpc(\"exec\", { path, args, options });\n }\n}\n"],"mappings":";;;;;;;;AAqDA,MAAM,yBAAyB;CAC7B,SAAS;CACT,aAAa,KAAK,OAAO;CACzB,YAAY;CACb;;;;;;;;;;;;;;;;;;;;;;;;AAyBD,IAAa,gBAAb,MAAgD;CAC9C,AAAS;CACT,AAAS;CACT,AAAS;CAET,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAA+B;AACzC,MAAI,CAAC,QAAQ,IACX,OAAM,IAAI,MAAM,sCAAsC;AAExD,MAAI,CAAC,QAAQ,KACX,OAAM,IAAI,MAAM,uCAAuC;AAGzD,OAAK,MAAM,QAAQ,IAAI,SAAS,OAAO,GAAG,QAAQ,MAAM,GAAG,QAAQ,IAAI;AACvE,OAAK,OAAO,QAAQ;AACpB,OAAK,cAAc,QAAQ;AAC3B,OAAK,aAAa,QAAQ,cAAc,uBAAuB;AAC/D,OAAK,QAAQ,QAAQ;AACrB,OAAK,UAAU,QAAQ,WAAW,uBAAuB;AACzD,OAAK,cAAc,QAAQ,eAAe,uBAAuB;AACjE,OAAK,eAAe;GAClB,GAAG;GACH,GAAG,QAAQ;GACZ;;;;;CAMH,MAAc,IACZ,QACA,QAC2B;EAC3B,MAAM,OAAO,KAAK,UAAU;GAAE;GAAQ;GAAQ,CAAC;AAG/C,MAAI,KAAK,SAAS,KAAK,YACrB,OAAM,IAAI,gBACR,2BAA2B,KAAK,OAAO,0BAA0B,KAAK,YAAY,QACnF;EAIH,MAAM,UAAkC,EACtC,gBAAgB,oBACjB;AACD,MAAI,KAAK,MACP,SAAQ,gBAAgB,UAAU,KAAK;EAGzC,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,eACf,KAAK,KACL;IACE,QAAQ;IACR;IACA;IACA,QAAQ,YAAY,QAAQ,KAAK,QAAQ;IAC1C,EACD,KAAK,aACN;WACM,OAAO;AACd,SAAM,IAAI,gBACR,wBAAwB,KAAK,IAAI,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC3F,iBAAiB,QAAQ,QAAQ,OAClC;;EAIH,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,SAAS,MAAM;UACxB;AACN,SAAM,IAAI,gBACR,oDAAoD,SAAS,QAAQ,IAAI,eAAe,GACzF;;AAIH,MAAI,CAAC,OAAO,SAAS;GACnB,MAAM,QAAQ,OAAO;AACrB,OAAI,OAAO;AAET,QAAI,MAAM,SAAS,aAAa,aAC9B,OAAM,IAAI,qBAAqB,MAAM,SAAS,MAAM,QAAQ;AAI9D,UAAM,IAAI,gBADW,MAAM,WAAW,iBACE;KACtC,MAAM,MAAM;KACZ,SAAS,MAAM;KAChB,CAAC;;AAEJ,SAAM,IAAI,gBAAgB,uCAAuC;;AAGnE,SAAO,OAAO;;CAGhB,MAAM,KAAK,MAAc,SAAkD;AACzE,SAAO,KAAK,IAAI,QAAQ;GAAE;GAAM;GAAS,CAAC;;CAG5C,MAAM,KAAK,MAAc,SAAkD;AACzE,SAAO,KAAK,IAAI,QAAQ;GAAE;GAAM;GAAS,CAAC;;CAG5C,MAAM,MACJ,MACA,SACA,SACyB;AACzB,SAAO,KAAK,IAAI,SAAS;GAAE;GAAM;GAAS;GAAS,CAAC;;CAGtD,MAAM,OAAO,MAAc,SAAsD;AAC/E,SAAO,KAAK,IAAI,UAAU;GAAE;GAAM;GAAS,CAAC;;CAG9C,MAAM,OACJ,SACA,SACA,SAC0B;AAC1B,SAAO,KAAK,IAAI,UAAU;GAAE;GAAS;GAAS;GAAS,CAAC;;CAG1D,MAAM,OAAO,MAAc,OAAe,SAAsD;AAC9F,SAAO,KAAK,IAAI,UAAU;GAAE;GAAM;GAAO;GAAS,CAAC;;CAGrD,MAAM,KACJ,MACA,MACA,SACwB;AACxB,SAAO,KAAK,IAAI,QAAQ;GAAE;GAAM;GAAM;GAAS,CAAC"}
package/dist/errors.cjs CHANGED
@@ -72,6 +72,15 @@ var AFSPayloadTooLargeError = class extends AFSHttpError {
72
72
  }
73
73
  };
74
74
  /**
75
+ * Unauthorized error (missing or invalid token)
76
+ */
77
+ var AFSUnauthorizedError = class extends AFSHttpError {
78
+ constructor(message = "Unauthorized", details) {
79
+ super(message, require_protocol.AFSErrorCode.UNAUTHORIZED, 401, details);
80
+ this.name = "AFSUnauthorizedError";
81
+ }
82
+ };
83
+ /**
75
84
  * Network error (for client-side use)
76
85
  */
77
86
  var AFSNetworkError = class extends Error {
@@ -102,4 +111,5 @@ exports.AFSNotFoundError = AFSNotFoundError;
102
111
  exports.AFSPayloadTooLargeError = AFSPayloadTooLargeError;
103
112
  exports.AFSPermissionError = AFSPermissionError;
104
113
  exports.AFSRuntimeError = AFSRuntimeError;
114
+ exports.AFSUnauthorizedError = AFSUnauthorizedError;
105
115
  exports.mapErrorToCode = mapErrorToCode;
package/dist/errors.d.cts CHANGED
@@ -46,6 +46,12 @@ declare class AFSInvalidRequestError extends AFSHttpError {
46
46
  declare class AFSPayloadTooLargeError extends AFSHttpError {
47
47
  constructor(maxSize: number, actualSize?: number);
48
48
  }
49
+ /**
50
+ * Unauthorized error (missing or invalid token)
51
+ */
52
+ declare class AFSUnauthorizedError extends AFSHttpError {
53
+ constructor(message?: string, details?: unknown);
54
+ }
49
55
  /**
50
56
  * Network error (for client-side use)
51
57
  */
@@ -59,5 +65,5 @@ declare class AFSNetworkError extends Error {
59
65
  */
60
66
  declare function mapErrorToCode(error: Error): AFSErrorCode;
61
67
  //#endregion
62
- export { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, mapErrorToCode };
68
+ export { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, AFSUnauthorizedError, mapErrorToCode };
63
69
  //# sourceMappingURL=errors.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.cts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;AAKA;;cAAa,YAAA,SAAqB,KAAA;EAAA,SAAA,IAAA,EAGR,YAAA;EAAA,SAAA,UAAA;EAAA,SAAA,OAAA;EAAA,YAAA,OAAA,UAAA,IAAA,EAAA,YAAA,EAAA,UAAA,WAAA,OAAA;AAAA;AAAA;;;AAAA,cAYb,gBAAA,SAAyB,YAAA;EAAA,YAAA,IAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,kBAAA,SAA2B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUxC;AAUA;AApBwC,cAU3B,gBAAA,SAAyB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,eAAA,SAAwB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUrC;AAUA;AApBqC,cAUxB,sBAAA,SAA+B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAU5C;AAaA;AAvB4C,cAU/B,uBAAA,SAAgC,YAAA;EAAA,YAAA,OAAA,UAAA,UAAA;AAAA;AAAA;AAa7C;;AAb6C,cAahC,eAAA,SAAwB,KAAA;EAAA,SAAA,KAAA,GAGT,KAAA;EAAA,SAAA,SAAA;EAAA,YAAA,OAAA,UAAA,KAAA,GAAA,KAAA,cAAA,SAAA;AAAA;AAAA;;;AAAA,iBAWZ,cAAA,CAAA,KAAA,EAAsB,KAAA,GAAQ,YAAA"}
1
+ {"version":3,"file":"errors.d.cts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;AAKA;;cAAa,YAAA,SAAqB,KAAA;EAAA,SAAA,IAAA,EAGR,YAAA;EAAA,SAAA,UAAA;EAAA,SAAA,OAAA;EAAA,YAAA,OAAA,UAAA,IAAA,EAAA,YAAA,EAAA,UAAA,WAAA,OAAA;AAAA;AAAA;;;AAAA,cAYb,gBAAA,SAAyB,YAAA;EAAA,YAAA,IAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,kBAAA,SAA2B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUxC;AAUA;AApBwC,cAU3B,gBAAA,SAAyB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,eAAA,SAAwB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUrC;AAUA;AApBqC,cAUxB,sBAAA,SAA+B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAU5C;AAaA;AAvB4C,cAU/B,uBAAA,SAAgC,YAAA;EAAA,YAAA,OAAA,UAAA,UAAA;AAAA;AAAA;AAa7C;AAUA;AAvB6C,cAahC,oBAAA,SAA6B,YAAA;EAAA,YAAA,OAAA,WAAA,OAAA;AAAA;AAAA;AAU1C;;AAV0C,cAU7B,eAAA,SAAwB,KAAA;EAAA,SAAA,KAAA,GAGT,KAAA;EAAA,SAAA,SAAA;EAAA,YAAA,OAAA,UAAA,KAAA,GAAA,KAAA,cAAA,SAAA;AAAA;AAAA;;;AAAA,iBAWZ,cAAA,CAAA,KAAA,EAAsB,KAAA,GAAQ,YAAA"}
package/dist/errors.d.mts CHANGED
@@ -46,6 +46,12 @@ declare class AFSInvalidRequestError extends AFSHttpError {
46
46
  declare class AFSPayloadTooLargeError extends AFSHttpError {
47
47
  constructor(maxSize: number, actualSize?: number);
48
48
  }
49
+ /**
50
+ * Unauthorized error (missing or invalid token)
51
+ */
52
+ declare class AFSUnauthorizedError extends AFSHttpError {
53
+ constructor(message?: string, details?: unknown);
54
+ }
49
55
  /**
50
56
  * Network error (for client-side use)
51
57
  */
@@ -59,5 +65,5 @@ declare class AFSNetworkError extends Error {
59
65
  */
60
66
  declare function mapErrorToCode(error: Error): AFSErrorCode;
61
67
  //#endregion
62
- export { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, mapErrorToCode };
68
+ export { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, AFSUnauthorizedError, mapErrorToCode };
63
69
  //# sourceMappingURL=errors.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;AAKA;;cAAa,YAAA,SAAqB,KAAA;EAAA,SAAA,IAAA,EAGR,YAAA;EAAA,SAAA,UAAA;EAAA,SAAA,OAAA;EAAA,YAAA,OAAA,UAAA,IAAA,EAAA,YAAA,EAAA,UAAA,WAAA,OAAA;AAAA;AAAA;;;AAAA,cAYb,gBAAA,SAAyB,YAAA;EAAA,YAAA,IAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,kBAAA,SAA2B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUxC;AAUA;AApBwC,cAU3B,gBAAA,SAAyB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,eAAA,SAAwB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUrC;AAUA;AApBqC,cAUxB,sBAAA,SAA+B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAU5C;AAaA;AAvB4C,cAU/B,uBAAA,SAAgC,YAAA;EAAA,YAAA,OAAA,UAAA,UAAA;AAAA;AAAA;AAa7C;;AAb6C,cAahC,eAAA,SAAwB,KAAA;EAAA,SAAA,KAAA,GAGT,KAAA;EAAA,SAAA,SAAA;EAAA,YAAA,OAAA,UAAA,KAAA,GAAA,KAAA,cAAA,SAAA;AAAA;AAAA;;;AAAA,iBAWZ,cAAA,CAAA,KAAA,EAAsB,KAAA,GAAQ,YAAA"}
1
+ {"version":3,"file":"errors.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;AAKA;;cAAa,YAAA,SAAqB,KAAA;EAAA,SAAA,IAAA,EAGR,YAAA;EAAA,SAAA,UAAA;EAAA,SAAA,OAAA;EAAA,YAAA,OAAA,UAAA,IAAA,EAAA,YAAA,EAAA,UAAA,WAAA,OAAA;AAAA;AAAA;;;AAAA,cAYb,gBAAA,SAAyB,YAAA;EAAA,YAAA,IAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,kBAAA,SAA2B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUxC;AAUA;AApBwC,cAU3B,gBAAA,SAAyB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUtC;AAUA;AApBsC,cAUzB,eAAA,SAAwB,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAUrC;AAUA;AApBqC,cAUxB,sBAAA,SAA+B,YAAA;EAAA,YAAA,OAAA,UAAA,OAAA;AAAA;AAAA;AAU5C;AAaA;AAvB4C,cAU/B,uBAAA,SAAgC,YAAA;EAAA,YAAA,OAAA,UAAA,UAAA;AAAA;AAAA;AAa7C;AAUA;AAvB6C,cAahC,oBAAA,SAA6B,YAAA;EAAA,YAAA,OAAA,WAAA,OAAA;AAAA;AAAA;AAU1C;;AAV0C,cAU7B,eAAA,SAAwB,KAAA;EAAA,SAAA,KAAA,GAGT,KAAA;EAAA,SAAA,SAAA;EAAA,YAAA,OAAA,UAAA,KAAA,GAAA,KAAA,cAAA,SAAA;AAAA;AAAA;;;AAAA,iBAWZ,cAAA,CAAA,KAAA,EAAsB,KAAA,GAAQ,YAAA"}
package/dist/errors.mjs CHANGED
@@ -72,6 +72,15 @@ var AFSPayloadTooLargeError = class extends AFSHttpError {
72
72
  }
73
73
  };
74
74
  /**
75
+ * Unauthorized error (missing or invalid token)
76
+ */
77
+ var AFSUnauthorizedError = class extends AFSHttpError {
78
+ constructor(message = "Unauthorized", details) {
79
+ super(message, AFSErrorCode.UNAUTHORIZED, 401, details);
80
+ this.name = "AFSUnauthorizedError";
81
+ }
82
+ };
83
+ /**
75
84
  * Network error (for client-side use)
76
85
  */
77
86
  var AFSNetworkError = class extends Error {
@@ -94,5 +103,5 @@ function mapErrorToCode(error) {
94
103
  }
95
104
 
96
105
  //#endregion
97
- export { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, mapErrorToCode };
106
+ export { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, AFSUnauthorizedError, mapErrorToCode };
98
107
  //# sourceMappingURL=errors.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { AFSErrorCode } from \"./protocol.js\";\n\n/**\n * Base class for AFS HTTP errors\n */\nexport class AFSHttpError extends Error {\n constructor(\n message: string,\n public readonly code: AFSErrorCode,\n public readonly httpStatus: number = 200,\n public readonly details?: unknown,\n ) {\n super(message);\n this.name = \"AFSHttpError\";\n }\n}\n\n/**\n * Path not found error\n */\nexport class AFSNotFoundError extends AFSHttpError {\n constructor(path: string, details?: unknown) {\n super(`Path not found: ${path}`, AFSErrorCode.NOT_FOUND, 200, details);\n this.name = \"AFSNotFoundError\";\n }\n}\n\n/**\n * Permission denied error (including readonly mode)\n */\nexport class AFSPermissionError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.PERMISSION_DENIED, 200, details);\n this.name = \"AFSPermissionError\";\n }\n}\n\n/**\n * Conflict error (concurrent modification)\n */\nexport class AFSConflictError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.CONFLICT, 200, details);\n this.name = \"AFSConflictError\";\n }\n}\n\n/**\n * Runtime error\n */\nexport class AFSRuntimeError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.RUNTIME_ERROR, 200, details);\n this.name = \"AFSRuntimeError\";\n }\n}\n\n/**\n * Invalid request error (bad JSON, invalid method, etc.)\n */\nexport class AFSInvalidRequestError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.RUNTIME_ERROR, 400, details);\n this.name = \"AFSInvalidRequestError\";\n }\n}\n\n/**\n * Payload too large error\n */\nexport class AFSPayloadTooLargeError extends AFSHttpError {\n constructor(maxSize: number, actualSize?: number) {\n const message = actualSize\n ? `Payload too large: ${actualSize} bytes exceeds limit of ${maxSize} bytes`\n : `Payload too large: exceeds limit of ${maxSize} bytes`;\n super(message, AFSErrorCode.RUNTIME_ERROR, 413, { maxSize, actualSize });\n this.name = \"AFSPayloadTooLargeError\";\n }\n}\n\n/**\n * Network error (for client-side use)\n */\nexport class AFSNetworkError extends Error {\n constructor(\n message: string,\n public readonly cause?: Error,\n public readonly retryable: boolean = true,\n ) {\n super(message);\n this.name = \"AFSNetworkError\";\n }\n}\n\n/**\n * Map common error messages to appropriate AFS error codes\n */\nexport function mapErrorToCode(error: Error): AFSErrorCode {\n const message = error.message.toLowerCase();\n\n if (\n message.includes(\"not found\") ||\n message.includes(\"enoent\") ||\n message.includes(\"does not exist\")\n ) {\n return AFSErrorCode.NOT_FOUND;\n }\n\n if (\n message.includes(\"permission\") ||\n message.includes(\"readonly\") ||\n message.includes(\"read-only\") ||\n message.includes(\"access denied\") ||\n message.includes(\"eacces\")\n ) {\n return AFSErrorCode.PERMISSION_DENIED;\n }\n\n if (\n message.includes(\"conflict\") ||\n message.includes(\"already exists\") ||\n message.includes(\"eexist\")\n ) {\n return AFSErrorCode.CONFLICT;\n }\n\n return AFSErrorCode.RUNTIME_ERROR;\n}\n"],"mappings":";;;;;;AAKA,IAAa,eAAb,cAAkC,MAAM;CACtC,YACE,SACA,AAAgB,MAChB,AAAgB,aAAqB,KACrC,AAAgB,SAChB;AACA,QAAM,QAAQ;EAJE;EACA;EACA;AAGhB,OAAK,OAAO;;;;;;AAOhB,IAAa,mBAAb,cAAsC,aAAa;CACjD,YAAY,MAAc,SAAmB;AAC3C,QAAM,mBAAmB,QAAQ,aAAa,WAAW,KAAK,QAAQ;AACtE,OAAK,OAAO;;;;;;AAOhB,IAAa,qBAAb,cAAwC,aAAa;CACnD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,mBAAmB,KAAK,QAAQ;AAC5D,OAAK,OAAO;;;;;;AAOhB,IAAa,mBAAb,cAAsC,aAAa;CACjD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,UAAU,KAAK,QAAQ;AACnD,OAAK,OAAO;;;;;;AAOhB,IAAa,kBAAb,cAAqC,aAAa;CAChD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,eAAe,KAAK,QAAQ;AACxD,OAAK,OAAO;;;;;;AAOhB,IAAa,yBAAb,cAA4C,aAAa;CACvD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,eAAe,KAAK,QAAQ;AACxD,OAAK,OAAO;;;;;;AAOhB,IAAa,0BAAb,cAA6C,aAAa;CACxD,YAAY,SAAiB,YAAqB;EAChD,MAAM,UAAU,aACZ,sBAAsB,WAAW,0BAA0B,QAAQ,UACnE,uCAAuC,QAAQ;AACnD,QAAM,SAAS,aAAa,eAAe,KAAK;GAAE;GAAS;GAAY,CAAC;AACxE,OAAK,OAAO;;;;;;AAOhB,IAAa,kBAAb,cAAqC,MAAM;CACzC,YACE,SACA,AAAgB,OAChB,AAAgB,YAAqB,MACrC;AACA,QAAM,QAAQ;EAHE;EACA;AAGhB,OAAK,OAAO;;;;;;AAOhB,SAAgB,eAAe,OAA4B;CACzD,MAAM,UAAU,MAAM,QAAQ,aAAa;AAE3C,KACE,QAAQ,SAAS,YAAY,IAC7B,QAAQ,SAAS,SAAS,IAC1B,QAAQ,SAAS,iBAAiB,CAElC,QAAO,aAAa;AAGtB,KACE,QAAQ,SAAS,aAAa,IAC9B,QAAQ,SAAS,WAAW,IAC5B,QAAQ,SAAS,YAAY,IAC7B,QAAQ,SAAS,gBAAgB,IACjC,QAAQ,SAAS,SAAS,CAE1B,QAAO,aAAa;AAGtB,KACE,QAAQ,SAAS,WAAW,IAC5B,QAAQ,SAAS,iBAAiB,IAClC,QAAQ,SAAS,SAAS,CAE1B,QAAO,aAAa;AAGtB,QAAO,aAAa"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { AFSErrorCode } from \"./protocol.js\";\n\n/**\n * Base class for AFS HTTP errors\n */\nexport class AFSHttpError extends Error {\n constructor(\n message: string,\n public readonly code: AFSErrorCode,\n public readonly httpStatus: number = 200,\n public readonly details?: unknown,\n ) {\n super(message);\n this.name = \"AFSHttpError\";\n }\n}\n\n/**\n * Path not found error\n */\nexport class AFSNotFoundError extends AFSHttpError {\n constructor(path: string, details?: unknown) {\n super(`Path not found: ${path}`, AFSErrorCode.NOT_FOUND, 200, details);\n this.name = \"AFSNotFoundError\";\n }\n}\n\n/**\n * Permission denied error (including readonly mode)\n */\nexport class AFSPermissionError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.PERMISSION_DENIED, 200, details);\n this.name = \"AFSPermissionError\";\n }\n}\n\n/**\n * Conflict error (concurrent modification)\n */\nexport class AFSConflictError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.CONFLICT, 200, details);\n this.name = \"AFSConflictError\";\n }\n}\n\n/**\n * Runtime error\n */\nexport class AFSRuntimeError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.RUNTIME_ERROR, 200, details);\n this.name = \"AFSRuntimeError\";\n }\n}\n\n/**\n * Invalid request error (bad JSON, invalid method, etc.)\n */\nexport class AFSInvalidRequestError extends AFSHttpError {\n constructor(message: string, details?: unknown) {\n super(message, AFSErrorCode.RUNTIME_ERROR, 400, details);\n this.name = \"AFSInvalidRequestError\";\n }\n}\n\n/**\n * Payload too large error\n */\nexport class AFSPayloadTooLargeError extends AFSHttpError {\n constructor(maxSize: number, actualSize?: number) {\n const message = actualSize\n ? `Payload too large: ${actualSize} bytes exceeds limit of ${maxSize} bytes`\n : `Payload too large: exceeds limit of ${maxSize} bytes`;\n super(message, AFSErrorCode.RUNTIME_ERROR, 413, { maxSize, actualSize });\n this.name = \"AFSPayloadTooLargeError\";\n }\n}\n\n/**\n * Unauthorized error (missing or invalid token)\n */\nexport class AFSUnauthorizedError extends AFSHttpError {\n constructor(message = \"Unauthorized\", details?: unknown) {\n super(message, AFSErrorCode.UNAUTHORIZED, 401, details);\n this.name = \"AFSUnauthorizedError\";\n }\n}\n\n/**\n * Network error (for client-side use)\n */\nexport class AFSNetworkError extends Error {\n constructor(\n message: string,\n public readonly cause?: Error,\n public readonly retryable: boolean = true,\n ) {\n super(message);\n this.name = \"AFSNetworkError\";\n }\n}\n\n/**\n * Map common error messages to appropriate AFS error codes\n */\nexport function mapErrorToCode(error: Error): AFSErrorCode {\n const message = error.message.toLowerCase();\n\n if (\n message.includes(\"not found\") ||\n message.includes(\"enoent\") ||\n message.includes(\"does not exist\")\n ) {\n return AFSErrorCode.NOT_FOUND;\n }\n\n if (\n message.includes(\"permission\") ||\n message.includes(\"readonly\") ||\n message.includes(\"read-only\") ||\n message.includes(\"access denied\") ||\n message.includes(\"eacces\")\n ) {\n return AFSErrorCode.PERMISSION_DENIED;\n }\n\n if (\n message.includes(\"conflict\") ||\n message.includes(\"already exists\") ||\n message.includes(\"eexist\")\n ) {\n return AFSErrorCode.CONFLICT;\n }\n\n return AFSErrorCode.RUNTIME_ERROR;\n}\n"],"mappings":";;;;;;AAKA,IAAa,eAAb,cAAkC,MAAM;CACtC,YACE,SACA,AAAgB,MAChB,AAAgB,aAAqB,KACrC,AAAgB,SAChB;AACA,QAAM,QAAQ;EAJE;EACA;EACA;AAGhB,OAAK,OAAO;;;;;;AAOhB,IAAa,mBAAb,cAAsC,aAAa;CACjD,YAAY,MAAc,SAAmB;AAC3C,QAAM,mBAAmB,QAAQ,aAAa,WAAW,KAAK,QAAQ;AACtE,OAAK,OAAO;;;;;;AAOhB,IAAa,qBAAb,cAAwC,aAAa;CACnD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,mBAAmB,KAAK,QAAQ;AAC5D,OAAK,OAAO;;;;;;AAOhB,IAAa,mBAAb,cAAsC,aAAa;CACjD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,UAAU,KAAK,QAAQ;AACnD,OAAK,OAAO;;;;;;AAOhB,IAAa,kBAAb,cAAqC,aAAa;CAChD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,eAAe,KAAK,QAAQ;AACxD,OAAK,OAAO;;;;;;AAOhB,IAAa,yBAAb,cAA4C,aAAa;CACvD,YAAY,SAAiB,SAAmB;AAC9C,QAAM,SAAS,aAAa,eAAe,KAAK,QAAQ;AACxD,OAAK,OAAO;;;;;;AAOhB,IAAa,0BAAb,cAA6C,aAAa;CACxD,YAAY,SAAiB,YAAqB;EAChD,MAAM,UAAU,aACZ,sBAAsB,WAAW,0BAA0B,QAAQ,UACnE,uCAAuC,QAAQ;AACnD,QAAM,SAAS,aAAa,eAAe,KAAK;GAAE;GAAS;GAAY,CAAC;AACxE,OAAK,OAAO;;;;;;AAOhB,IAAa,uBAAb,cAA0C,aAAa;CACrD,YAAY,UAAU,gBAAgB,SAAmB;AACvD,QAAM,SAAS,aAAa,cAAc,KAAK,QAAQ;AACvD,OAAK,OAAO;;;;;;AAOhB,IAAa,kBAAb,cAAqC,MAAM;CACzC,YACE,SACA,AAAgB,OAChB,AAAgB,YAAqB,MACrC;AACA,QAAM,QAAQ;EAHE;EACA;AAGhB,OAAK,OAAO;;;;;;AAOhB,SAAgB,eAAe,OAA4B;CACzD,MAAM,UAAU,MAAM,QAAQ,aAAa;AAE3C,KACE,QAAQ,SAAS,YAAY,IAC7B,QAAQ,SAAS,SAAS,IAC1B,QAAQ,SAAS,iBAAiB,CAElC,QAAO,aAAa;AAGtB,KACE,QAAQ,SAAS,aAAa,IAC9B,QAAQ,SAAS,WAAW,IAC5B,QAAQ,SAAS,YAAY,IAC7B,QAAQ,SAAS,gBAAgB,IACjC,QAAQ,SAAS,SAAS,CAE1B,QAAO,aAAa;AAGtB,KACE,QAAQ,SAAS,WAAW,IAC5B,QAAQ,SAAS,iBAAiB,IAClC,QAAQ,SAAS,SAAS,CAE1B,QAAO,aAAa;AAGtB,QAAO,aAAa"}
package/dist/handler.cjs CHANGED
@@ -35,10 +35,13 @@ const DEFAULT_OPTIONS = {
35
35
  * ```
36
36
  */
37
37
  function createAFSHttpHandler(options) {
38
- const { module, maxBodySize = DEFAULT_OPTIONS.maxBodySize } = options;
38
+ const { module, maxBodySize = DEFAULT_OPTIONS.maxBodySize, token: tokenConfig } = options;
39
39
  return async (request) => {
40
40
  try {
41
41
  if (request.method !== "POST") return createJsonResponse(require_protocol.createErrorResponse(require_protocol.AFSErrorCode.RUNTIME_ERROR, `Method not allowed: ${request.method}`), 405);
42
+ if (tokenConfig) {
43
+ if (!validateToken(request, tokenConfig).valid) return createJsonResponse(require_protocol.createErrorResponse(require_protocol.AFSErrorCode.UNAUTHORIZED, "Unauthorized"), 401);
44
+ }
42
45
  if (!request.headers.get("content-type")?.includes("application/json")) return createJsonResponse(require_protocol.createErrorResponse(require_protocol.AFSErrorCode.RUNTIME_ERROR, "Content-Type must be application/json"), 400);
43
46
  const contentLength = request.headers.get("content-length");
44
47
  if (contentLength && Number.parseInt(contentLength, 10) > maxBodySize) {
@@ -113,6 +116,28 @@ async function executeRpcMethod(module, method, params) {
113
116
  }
114
117
  }
115
118
  /**
119
+ * Extract Bearer token from Authorization header
120
+ */
121
+ function extractBearerToken(request) {
122
+ const authHeader = request.headers.get("authorization");
123
+ if (!authHeader) return null;
124
+ if (!authHeader.toLowerCase().startsWith("bearer ")) return null;
125
+ return authHeader.slice(7).trim();
126
+ }
127
+ /**
128
+ * Validate token against configuration
129
+ */
130
+ function validateToken(request, tokenConfig) {
131
+ const token = extractBearerToken(request);
132
+ if (!token) return { valid: false };
133
+ if (typeof tokenConfig === "string") return { valid: token === tokenConfig };
134
+ try {
135
+ return { valid: tokenConfig(token) };
136
+ } catch {
137
+ return { valid: false };
138
+ }
139
+ }
140
+ /**
116
141
  * Create a JSON Response with proper headers
117
142
  */
118
143
  function createJsonResponse(body, status) {
@@ -1,6 +1,10 @@
1
1
  import { AFSModule } from "@aigne/afs";
2
2
 
3
3
  //#region src/handler.d.ts
4
+ /**
5
+ * Token validator function type
6
+ */
7
+ type TokenValidator = (token: string) => boolean;
4
8
  /**
5
9
  * Options for creating an AFS HTTP handler
6
10
  */
@@ -11,6 +15,13 @@ interface AFSHttpHandlerOptions {
11
15
  maxBodySize?: number;
12
16
  /** Request timeout in milliseconds (default: 30000) */
13
17
  timeout?: number;
18
+ /**
19
+ * Token validation configuration (optional)
20
+ * - Not configured: No authorization required (backward compatible)
21
+ * - String: Static token, requests must carry matching Bearer token
22
+ * - Function: Custom validation logic, returns true if valid
23
+ */
24
+ token?: string | TokenValidator;
14
25
  }
15
26
  /**
16
27
  * Create an AFS HTTP handler function
@@ -39,5 +50,5 @@ interface AFSHttpHandlerOptions {
39
50
  */
40
51
  declare function createAFSHttpHandler(options: AFSHttpHandlerOptions): (request: Request) => Promise<Response>;
41
52
  //#endregion
42
- export { AFSHttpHandlerOptions, createAFSHttpHandler };
53
+ export { AFSHttpHandlerOptions, TokenValidator, createAFSHttpHandler };
43
54
  //# sourceMappingURL=handler.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.cts","names":[],"sources":["../src/handler.ts"],"mappings":";;;;AAoBA;AA0CA;UA1CiB,qBAAA;EAAA;EAAA,MAAA,EAEP,SAAA;EAAA;EAAA,WAAA;EAAA;EAAA,OAAA;AAAA;AAAA;AAwCV;;;;;;;;;;;;;;;;;;;;;;;;AAxCU,iBAwCM,oBAAA,CAAA,OAAA,EACL,qBAAA,IAAA,OAAA,EACE,OAAA,KAAY,OAAA,CAAQ,QAAA"}
1
+ {"version":3,"file":"handler.d.cts","names":[],"sources":["../src/handler.ts"],"mappings":";;;;AAoBA;AAKA;KALY,cAAA,IAAA,KAAA;AAAA;AAKZ;AAiDA;AAtDY,UAKK,qBAAA;EAAA;EAAA,MAAA,EAEP,SAAA;EAAA;EAAA,WAAA;EAAA;EAAA,OAAA;EAAA;AA+CV;;;;;EA/CU,KAAA,YAWS,cAAA;AAAA;AAAA;AAoCnB;;;;;;;;;;;;;;;;;;;;;;;;AApCmB,iBAoCH,oBAAA,CAAA,OAAA,EACL,qBAAA,IAAA,OAAA,EACE,OAAA,KAAY,OAAA,CAAQ,QAAA"}
@@ -1,6 +1,10 @@
1
1
  import { AFSModule } from "@aigne/afs";
2
2
 
3
3
  //#region src/handler.d.ts
4
+ /**
5
+ * Token validator function type
6
+ */
7
+ type TokenValidator = (token: string) => boolean;
4
8
  /**
5
9
  * Options for creating an AFS HTTP handler
6
10
  */
@@ -11,6 +15,13 @@ interface AFSHttpHandlerOptions {
11
15
  maxBodySize?: number;
12
16
  /** Request timeout in milliseconds (default: 30000) */
13
17
  timeout?: number;
18
+ /**
19
+ * Token validation configuration (optional)
20
+ * - Not configured: No authorization required (backward compatible)
21
+ * - String: Static token, requests must carry matching Bearer token
22
+ * - Function: Custom validation logic, returns true if valid
23
+ */
24
+ token?: string | TokenValidator;
14
25
  }
15
26
  /**
16
27
  * Create an AFS HTTP handler function
@@ -39,5 +50,5 @@ interface AFSHttpHandlerOptions {
39
50
  */
40
51
  declare function createAFSHttpHandler(options: AFSHttpHandlerOptions): (request: Request) => Promise<Response>;
41
52
  //#endregion
42
- export { AFSHttpHandlerOptions, createAFSHttpHandler };
53
+ export { AFSHttpHandlerOptions, TokenValidator, createAFSHttpHandler };
43
54
  //# sourceMappingURL=handler.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.mts","names":[],"sources":["../src/handler.ts"],"mappings":";;;;AAoBA;AA0CA;UA1CiB,qBAAA;EAAA;EAAA,MAAA,EAEP,SAAA;EAAA;EAAA,WAAA;EAAA;EAAA,OAAA;AAAA;AAAA;AAwCV;;;;;;;;;;;;;;;;;;;;;;;;AAxCU,iBAwCM,oBAAA,CAAA,OAAA,EACL,qBAAA,IAAA,OAAA,EACE,OAAA,KAAY,OAAA,CAAQ,QAAA"}
1
+ {"version":3,"file":"handler.d.mts","names":[],"sources":["../src/handler.ts"],"mappings":";;;;AAoBA;AAKA;KALY,cAAA,IAAA,KAAA;AAAA;AAKZ;AAiDA;AAtDY,UAKK,qBAAA;EAAA;EAAA,MAAA,EAEP,SAAA;EAAA;EAAA,WAAA;EAAA;EAAA,OAAA;EAAA;AA+CV;;;;;EA/CU,KAAA,YAWS,cAAA;AAAA;AAAA;AAoCnB;;;;;;;;;;;;;;;;;;;;;;;;AApCmB,iBAoCH,oBAAA,CAAA,OAAA,EACL,qBAAA,IAAA,OAAA,EACE,OAAA,KAAY,OAAA,CAAQ,QAAA"}
package/dist/handler.mjs CHANGED
@@ -35,10 +35,13 @@ const DEFAULT_OPTIONS = {
35
35
  * ```
36
36
  */
37
37
  function createAFSHttpHandler(options) {
38
- const { module, maxBodySize = DEFAULT_OPTIONS.maxBodySize } = options;
38
+ const { module, maxBodySize = DEFAULT_OPTIONS.maxBodySize, token: tokenConfig } = options;
39
39
  return async (request) => {
40
40
  try {
41
41
  if (request.method !== "POST") return createJsonResponse(createErrorResponse(AFSErrorCode.RUNTIME_ERROR, `Method not allowed: ${request.method}`), 405);
42
+ if (tokenConfig) {
43
+ if (!validateToken(request, tokenConfig).valid) return createJsonResponse(createErrorResponse(AFSErrorCode.UNAUTHORIZED, "Unauthorized"), 401);
44
+ }
42
45
  if (!request.headers.get("content-type")?.includes("application/json")) return createJsonResponse(createErrorResponse(AFSErrorCode.RUNTIME_ERROR, "Content-Type must be application/json"), 400);
43
46
  const contentLength = request.headers.get("content-length");
44
47
  if (contentLength && Number.parseInt(contentLength, 10) > maxBodySize) {
@@ -113,6 +116,28 @@ async function executeRpcMethod(module, method, params) {
113
116
  }
114
117
  }
115
118
  /**
119
+ * Extract Bearer token from Authorization header
120
+ */
121
+ function extractBearerToken(request) {
122
+ const authHeader = request.headers.get("authorization");
123
+ if (!authHeader) return null;
124
+ if (!authHeader.toLowerCase().startsWith("bearer ")) return null;
125
+ return authHeader.slice(7).trim();
126
+ }
127
+ /**
128
+ * Validate token against configuration
129
+ */
130
+ function validateToken(request, tokenConfig) {
131
+ const token = extractBearerToken(request);
132
+ if (!token) return { valid: false };
133
+ if (typeof tokenConfig === "string") return { valid: token === tokenConfig };
134
+ try {
135
+ return { valid: tokenConfig(token) };
136
+ } catch {
137
+ return { valid: false };
138
+ }
139
+ }
140
+ /**
116
141
  * Create a JSON Response with proper headers
117
142
  */
118
143
  function createJsonResponse(body, status) {
@@ -1 +1 @@
1
- {"version":3,"file":"handler.mjs","names":[],"sources":["../src/handler.ts"],"sourcesContent":["import type { AFSModule } from \"@aigne/afs\";\nimport {\n AFSHttpError,\n AFSInvalidRequestError,\n AFSPayloadTooLargeError,\n mapErrorToCode,\n} from \"./errors.js\";\nimport {\n AFSErrorCode,\n type AFSRpcMethod,\n type AFSRpcRequest,\n type AFSRpcResponse,\n createErrorResponse,\n createSuccessResponse,\n isValidRpcMethod,\n} from \"./protocol.js\";\n\n/**\n * Options for creating an AFS HTTP handler\n */\nexport interface AFSHttpHandlerOptions {\n /** The AFS module to expose */\n module: AFSModule;\n /** Maximum request body size in bytes (default: 10MB) */\n maxBodySize?: number;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n}\n\n/**\n * Default handler options\n */\nconst DEFAULT_OPTIONS = {\n maxBodySize: 10 * 1024 * 1024, // 10MB\n timeout: 30000, // 30 seconds\n};\n\n/**\n * Create an AFS HTTP handler function\n *\n * This function creates a pure handler that accepts a Web Standard Request\n * and returns a Web Standard Response. It can be used with any HTTP framework\n * that supports these standards (Hono, Bun, Deno, etc.) or adapted for\n * frameworks like Express and Koa.\n *\n * @param options - Handler options\n * @returns A handler function that processes AFS RPC requests\n *\n * @example\n * ```typescript\n * // With Hono\n * const handler = createAFSHttpHandler({ module: provider });\n * app.post(\"/afs/rpc\", (c) => handler(c.req.raw));\n *\n * // With Bun\n * Bun.serve({\n * fetch(req) {\n * if (url.pathname === \"/afs/rpc\") return handler(req);\n * }\n * });\n * ```\n */\nexport function createAFSHttpHandler(\n options: AFSHttpHandlerOptions,\n): (request: Request) => Promise<Response> {\n const { module, maxBodySize = DEFAULT_OPTIONS.maxBodySize } = options;\n\n return async (request: Request): Promise<Response> => {\n try {\n // Validate HTTP method\n if (request.method !== \"POST\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, `Method not allowed: ${request.method}`),\n 405,\n );\n }\n\n // Check Content-Type\n const contentType = request.headers.get(\"content-type\");\n if (!contentType?.includes(\"application/json\")) {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Content-Type must be application/json\"),\n 400,\n );\n }\n\n // Check Content-Length if available\n const contentLength = request.headers.get(\"content-length\");\n if (contentLength && Number.parseInt(contentLength, 10) > maxBodySize) {\n const error = new AFSPayloadTooLargeError(maxBodySize, Number.parseInt(contentLength, 10));\n return createJsonResponse(\n createErrorResponse(error.code, error.message, error.details),\n error.httpStatus,\n );\n }\n\n // Parse request body\n let body: AFSRpcRequest;\n try {\n const text = await request.text();\n if (text.length > maxBodySize) {\n const error = new AFSPayloadTooLargeError(maxBodySize, text.length);\n return createJsonResponse(\n createErrorResponse(error.code, error.message, error.details),\n error.httpStatus,\n );\n }\n body = JSON.parse(text);\n } catch {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Invalid JSON body\"),\n 400,\n );\n }\n\n // Validate request structure\n if (!body || typeof body !== \"object\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Request body must be an object\"),\n 400,\n );\n }\n\n const { method, params } = body;\n\n // Validate method\n if (!method || typeof method !== \"string\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Missing or invalid method\"),\n 400,\n );\n }\n\n if (!isValidRpcMethod(method)) {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, `Unknown method: ${method}`),\n 400,\n );\n }\n\n // Validate params\n if (!params || typeof params !== \"object\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Missing or invalid params\"),\n 400,\n );\n }\n\n // Execute the RPC method\n const result = await executeRpcMethod(module, method, params);\n return createJsonResponse(createSuccessResponse(result), 200);\n } catch (error) {\n // Handle known AFS errors\n if (error instanceof AFSHttpError) {\n return createJsonResponse(\n createErrorResponse(error.code, error.message, error.details),\n error.httpStatus,\n );\n }\n\n // Handle unknown errors\n const message = error instanceof Error ? error.message : String(error);\n const code = error instanceof Error ? mapErrorToCode(error) : AFSErrorCode.RUNTIME_ERROR;\n return createJsonResponse(createErrorResponse(code, message), 200);\n }\n };\n}\n\n/**\n * Execute an RPC method on the module\n */\nasync function executeRpcMethod(\n module: AFSModule,\n method: AFSRpcMethod,\n params: Record<string, unknown>,\n): Promise<unknown> {\n switch (method) {\n case \"list\": {\n if (!module.list) {\n throw new AFSInvalidRequestError(\"Module does not support list operation\");\n }\n const { path, options } = params as { path: string; options?: unknown };\n return await module.list(path, options as any);\n }\n\n case \"read\": {\n if (!module.read) {\n throw new AFSInvalidRequestError(\"Module does not support read operation\");\n }\n const { path, options } = params as { path: string; options?: unknown };\n return await module.read(path, options as any);\n }\n\n case \"write\": {\n if (!module.write) {\n throw new AFSInvalidRequestError(\"Module does not support write operation\");\n }\n const { path, content, options } = params as {\n path: string;\n content: unknown;\n options?: unknown;\n };\n return await module.write(path, content as any, options as any);\n }\n\n case \"delete\": {\n if (!module.delete) {\n throw new AFSInvalidRequestError(\"Module does not support delete operation\");\n }\n const { path, options } = params as { path: string; options?: unknown };\n return await module.delete(path, options as any);\n }\n\n case \"rename\": {\n if (!module.rename) {\n throw new AFSInvalidRequestError(\"Module does not support rename operation\");\n }\n const { oldPath, newPath, options } = params as {\n oldPath: string;\n newPath: string;\n options?: unknown;\n };\n return await module.rename(oldPath, newPath, options as any);\n }\n\n case \"search\": {\n if (!module.search) {\n throw new AFSInvalidRequestError(\"Module does not support search operation\");\n }\n const { path, query, options } = params as {\n path: string;\n query: string;\n options?: unknown;\n };\n return await module.search(path, query, options as any);\n }\n\n case \"exec\": {\n if (!module.exec) {\n throw new AFSInvalidRequestError(\"Module does not support exec operation\");\n }\n const { path, args, options } = params as {\n path: string;\n args: Record<string, unknown>;\n options?: unknown;\n };\n return await module.exec(path, args, options as any);\n }\n\n default: {\n // TypeScript exhaustiveness check\n const _exhaustive: never = method;\n throw new AFSInvalidRequestError(`Unknown method: ${_exhaustive}`);\n }\n }\n}\n\n/**\n * Create a JSON Response with proper headers\n */\nfunction createJsonResponse(body: AFSRpcResponse, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"handler.mjs","names":[],"sources":["../src/handler.ts"],"sourcesContent":["import type { AFSModule } from \"@aigne/afs\";\nimport {\n AFSHttpError,\n AFSInvalidRequestError,\n AFSPayloadTooLargeError,\n mapErrorToCode,\n} from \"./errors.js\";\nimport {\n AFSErrorCode,\n type AFSRpcMethod,\n type AFSRpcRequest,\n type AFSRpcResponse,\n createErrorResponse,\n createSuccessResponse,\n isValidRpcMethod,\n} from \"./protocol.js\";\n\n/**\n * Token validator function type\n */\nexport type TokenValidator = (token: string) => boolean;\n\n/**\n * Options for creating an AFS HTTP handler\n */\nexport interface AFSHttpHandlerOptions {\n /** The AFS module to expose */\n module: AFSModule;\n /** Maximum request body size in bytes (default: 10MB) */\n maxBodySize?: number;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n /**\n * Token validation configuration (optional)\n * - Not configured: No authorization required (backward compatible)\n * - String: Static token, requests must carry matching Bearer token\n * - Function: Custom validation logic, returns true if valid\n */\n token?: string | TokenValidator;\n}\n\n/**\n * Default handler options\n */\nconst DEFAULT_OPTIONS = {\n maxBodySize: 10 * 1024 * 1024, // 10MB\n timeout: 30000, // 30 seconds\n};\n\n/**\n * Create an AFS HTTP handler function\n *\n * This function creates a pure handler that accepts a Web Standard Request\n * and returns a Web Standard Response. It can be used with any HTTP framework\n * that supports these standards (Hono, Bun, Deno, etc.) or adapted for\n * frameworks like Express and Koa.\n *\n * @param options - Handler options\n * @returns A handler function that processes AFS RPC requests\n *\n * @example\n * ```typescript\n * // With Hono\n * const handler = createAFSHttpHandler({ module: provider });\n * app.post(\"/afs/rpc\", (c) => handler(c.req.raw));\n *\n * // With Bun\n * Bun.serve({\n * fetch(req) {\n * if (url.pathname === \"/afs/rpc\") return handler(req);\n * }\n * });\n * ```\n */\nexport function createAFSHttpHandler(\n options: AFSHttpHandlerOptions,\n): (request: Request) => Promise<Response> {\n const { module, maxBodySize = DEFAULT_OPTIONS.maxBodySize, token: tokenConfig } = options;\n\n return async (request: Request): Promise<Response> => {\n try {\n // Validate HTTP method\n if (request.method !== \"POST\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, `Method not allowed: ${request.method}`),\n 405,\n );\n }\n\n // Validate token if configured\n if (tokenConfig) {\n const authResult = validateToken(request, tokenConfig);\n if (!authResult.valid) {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.UNAUTHORIZED, \"Unauthorized\"),\n 401,\n );\n }\n }\n\n // Check Content-Type\n const contentType = request.headers.get(\"content-type\");\n if (!contentType?.includes(\"application/json\")) {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Content-Type must be application/json\"),\n 400,\n );\n }\n\n // Check Content-Length if available\n const contentLength = request.headers.get(\"content-length\");\n if (contentLength && Number.parseInt(contentLength, 10) > maxBodySize) {\n const error = new AFSPayloadTooLargeError(maxBodySize, Number.parseInt(contentLength, 10));\n return createJsonResponse(\n createErrorResponse(error.code, error.message, error.details),\n error.httpStatus,\n );\n }\n\n // Parse request body\n let body: AFSRpcRequest;\n try {\n const text = await request.text();\n if (text.length > maxBodySize) {\n const error = new AFSPayloadTooLargeError(maxBodySize, text.length);\n return createJsonResponse(\n createErrorResponse(error.code, error.message, error.details),\n error.httpStatus,\n );\n }\n body = JSON.parse(text);\n } catch {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Invalid JSON body\"),\n 400,\n );\n }\n\n // Validate request structure\n if (!body || typeof body !== \"object\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Request body must be an object\"),\n 400,\n );\n }\n\n const { method, params } = body;\n\n // Validate method\n if (!method || typeof method !== \"string\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Missing or invalid method\"),\n 400,\n );\n }\n\n if (!isValidRpcMethod(method)) {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, `Unknown method: ${method}`),\n 400,\n );\n }\n\n // Validate params\n if (!params || typeof params !== \"object\") {\n return createJsonResponse(\n createErrorResponse(AFSErrorCode.RUNTIME_ERROR, \"Missing or invalid params\"),\n 400,\n );\n }\n\n // Execute the RPC method\n const result = await executeRpcMethod(module, method, params);\n return createJsonResponse(createSuccessResponse(result), 200);\n } catch (error) {\n // Handle known AFS errors\n if (error instanceof AFSHttpError) {\n return createJsonResponse(\n createErrorResponse(error.code, error.message, error.details),\n error.httpStatus,\n );\n }\n\n // Handle unknown errors\n const message = error instanceof Error ? error.message : String(error);\n const code = error instanceof Error ? mapErrorToCode(error) : AFSErrorCode.RUNTIME_ERROR;\n return createJsonResponse(createErrorResponse(code, message), 200);\n }\n };\n}\n\n/**\n * Execute an RPC method on the module\n */\nasync function executeRpcMethod(\n module: AFSModule,\n method: AFSRpcMethod,\n params: Record<string, unknown>,\n): Promise<unknown> {\n switch (method) {\n case \"list\": {\n if (!module.list) {\n throw new AFSInvalidRequestError(\"Module does not support list operation\");\n }\n const { path, options } = params as { path: string; options?: unknown };\n return await module.list(path, options as any);\n }\n\n case \"read\": {\n if (!module.read) {\n throw new AFSInvalidRequestError(\"Module does not support read operation\");\n }\n const { path, options } = params as { path: string; options?: unknown };\n return await module.read(path, options as any);\n }\n\n case \"write\": {\n if (!module.write) {\n throw new AFSInvalidRequestError(\"Module does not support write operation\");\n }\n const { path, content, options } = params as {\n path: string;\n content: unknown;\n options?: unknown;\n };\n return await module.write(path, content as any, options as any);\n }\n\n case \"delete\": {\n if (!module.delete) {\n throw new AFSInvalidRequestError(\"Module does not support delete operation\");\n }\n const { path, options } = params as { path: string; options?: unknown };\n return await module.delete(path, options as any);\n }\n\n case \"rename\": {\n if (!module.rename) {\n throw new AFSInvalidRequestError(\"Module does not support rename operation\");\n }\n const { oldPath, newPath, options } = params as {\n oldPath: string;\n newPath: string;\n options?: unknown;\n };\n return await module.rename(oldPath, newPath, options as any);\n }\n\n case \"search\": {\n if (!module.search) {\n throw new AFSInvalidRequestError(\"Module does not support search operation\");\n }\n const { path, query, options } = params as {\n path: string;\n query: string;\n options?: unknown;\n };\n return await module.search(path, query, options as any);\n }\n\n case \"exec\": {\n if (!module.exec) {\n throw new AFSInvalidRequestError(\"Module does not support exec operation\");\n }\n const { path, args, options } = params as {\n path: string;\n args: Record<string, unknown>;\n options?: unknown;\n };\n return await module.exec(path, args, options as any);\n }\n\n default: {\n // TypeScript exhaustiveness check\n const _exhaustive: never = method;\n throw new AFSInvalidRequestError(`Unknown method: ${_exhaustive}`);\n }\n }\n}\n\n/**\n * Extract Bearer token from Authorization header\n */\nfunction extractBearerToken(request: Request): string | null {\n const authHeader = request.headers.get(\"authorization\");\n if (!authHeader) {\n return null;\n }\n\n // Case-insensitive check for \"Bearer \" prefix\n const lowerHeader = authHeader.toLowerCase();\n if (!lowerHeader.startsWith(\"bearer \")) {\n return null;\n }\n\n // Extract and trim the token\n return authHeader.slice(7).trim();\n}\n\n/**\n * Validate token against configuration\n */\nfunction validateToken(request: Request, tokenConfig: string | TokenValidator): { valid: boolean } {\n const token = extractBearerToken(request);\n\n // No token provided\n if (!token) {\n return { valid: false };\n }\n\n // Static token comparison\n if (typeof tokenConfig === \"string\") {\n return { valid: token === tokenConfig };\n }\n\n // Custom validator function\n try {\n return { valid: tokenConfig(token) };\n } catch {\n // Validator threw an exception, treat as invalid\n return { valid: false };\n }\n}\n\n/**\n * Create a JSON Response with proper headers\n */\nfunction createJsonResponse(body: AFSRpcResponse, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -16,6 +16,7 @@ exports.AFSNotFoundError = require_errors.AFSNotFoundError;
16
16
  exports.AFSPayloadTooLargeError = require_errors.AFSPayloadTooLargeError;
17
17
  exports.AFSPermissionError = require_errors.AFSPermissionError;
18
18
  exports.AFSRuntimeError = require_errors.AFSRuntimeError;
19
+ exports.AFSUnauthorizedError = require_errors.AFSUnauthorizedError;
19
20
  exports.DEFAULT_RETRY_OPTIONS = require_retry.DEFAULT_RETRY_OPTIONS;
20
21
  exports.VALID_RPC_METHODS = require_protocol.VALID_RPC_METHODS;
21
22
  exports.calculateDelay = require_retry.calculateDelay;
package/dist/index.d.cts CHANGED
@@ -3,6 +3,6 @@ import { koaAdapter } from "./adapters/koa.cjs";
3
3
  import { DEFAULT_RETRY_OPTIONS, RetryOptions, calculateDelay, fetchWithRetry, isRetryableError, isRetryableStatus, sleep, withRetry } from "./retry.cjs";
4
4
  import { AFSHttpClient, AFSHttpClientOptions } from "./client.cjs";
5
5
  import { AFSErrorCode, AFSRpcError, AFSRpcMethod, AFSRpcParams, AFSRpcRequest, AFSRpcResponse, AFSRpcResults, VALID_RPC_METHODS, createErrorResponse, createSuccessResponse, isValidRpcMethod } from "./protocol.cjs";
6
- import { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, mapErrorToCode } from "./errors.cjs";
7
- import { AFSHttpHandlerOptions, createAFSHttpHandler } from "./handler.cjs";
8
- export { AFSConflictError, AFSErrorCode, AFSHttpClient, type AFSHttpClientOptions, AFSHttpError, type AFSHttpHandlerOptions, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, type AFSRpcError, type AFSRpcMethod, type AFSRpcParams, type AFSRpcRequest, type AFSRpcResponse, type AFSRpcResults, AFSRuntimeError, DEFAULT_RETRY_OPTIONS, type RetryOptions, VALID_RPC_METHODS, calculateDelay, createAFSHttpHandler, createErrorResponse, createSuccessResponse, expressAdapter, fetchWithRetry, isRetryableError, isRetryableStatus, isValidRpcMethod, koaAdapter, mapErrorToCode, sleep, withRetry };
6
+ import { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, AFSUnauthorizedError, mapErrorToCode } from "./errors.cjs";
7
+ import { AFSHttpHandlerOptions, TokenValidator, createAFSHttpHandler } from "./handler.cjs";
8
+ export { AFSConflictError, AFSErrorCode, AFSHttpClient, type AFSHttpClientOptions, AFSHttpError, type AFSHttpHandlerOptions, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, type AFSRpcError, type AFSRpcMethod, type AFSRpcParams, type AFSRpcRequest, type AFSRpcResponse, type AFSRpcResults, AFSRuntimeError, AFSUnauthorizedError, DEFAULT_RETRY_OPTIONS, type RetryOptions, type TokenValidator, VALID_RPC_METHODS, calculateDelay, createAFSHttpHandler, createErrorResponse, createSuccessResponse, expressAdapter, fetchWithRetry, isRetryableError, isRetryableStatus, isValidRpcMethod, koaAdapter, mapErrorToCode, sleep, withRetry };
package/dist/index.d.mts CHANGED
@@ -3,6 +3,6 @@ import { koaAdapter } from "./adapters/koa.mjs";
3
3
  import { DEFAULT_RETRY_OPTIONS, RetryOptions, calculateDelay, fetchWithRetry, isRetryableError, isRetryableStatus, sleep, withRetry } from "./retry.mjs";
4
4
  import { AFSHttpClient, AFSHttpClientOptions } from "./client.mjs";
5
5
  import { AFSErrorCode, AFSRpcError, AFSRpcMethod, AFSRpcParams, AFSRpcRequest, AFSRpcResponse, AFSRpcResults, VALID_RPC_METHODS, createErrorResponse, createSuccessResponse, isValidRpcMethod } from "./protocol.mjs";
6
- import { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, mapErrorToCode } from "./errors.mjs";
7
- import { AFSHttpHandlerOptions, createAFSHttpHandler } from "./handler.mjs";
8
- export { AFSConflictError, AFSErrorCode, AFSHttpClient, type AFSHttpClientOptions, AFSHttpError, type AFSHttpHandlerOptions, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, type AFSRpcError, type AFSRpcMethod, type AFSRpcParams, type AFSRpcRequest, type AFSRpcResponse, type AFSRpcResults, AFSRuntimeError, DEFAULT_RETRY_OPTIONS, type RetryOptions, VALID_RPC_METHODS, calculateDelay, createAFSHttpHandler, createErrorResponse, createSuccessResponse, expressAdapter, fetchWithRetry, isRetryableError, isRetryableStatus, isValidRpcMethod, koaAdapter, mapErrorToCode, sleep, withRetry };
6
+ import { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, AFSUnauthorizedError, mapErrorToCode } from "./errors.mjs";
7
+ import { AFSHttpHandlerOptions, TokenValidator, createAFSHttpHandler } from "./handler.mjs";
8
+ export { AFSConflictError, AFSErrorCode, AFSHttpClient, type AFSHttpClientOptions, AFSHttpError, type AFSHttpHandlerOptions, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, type AFSRpcError, type AFSRpcMethod, type AFSRpcParams, type AFSRpcRequest, type AFSRpcResponse, type AFSRpcResults, AFSRuntimeError, AFSUnauthorizedError, DEFAULT_RETRY_OPTIONS, type RetryOptions, type TokenValidator, VALID_RPC_METHODS, calculateDelay, createAFSHttpHandler, createErrorResponse, createSuccessResponse, expressAdapter, fetchWithRetry, isRetryableError, isRetryableStatus, isValidRpcMethod, koaAdapter, mapErrorToCode, sleep, withRetry };
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { expressAdapter } from "./adapters/express.mjs";
2
2
  import { koaAdapter } from "./adapters/koa.mjs";
3
3
  import { AFSErrorCode, VALID_RPC_METHODS, createErrorResponse, createSuccessResponse, isValidRpcMethod } from "./protocol.mjs";
4
- import { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, mapErrorToCode } from "./errors.mjs";
4
+ import { AFSConflictError, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, AFSUnauthorizedError, mapErrorToCode } from "./errors.mjs";
5
5
  import { DEFAULT_RETRY_OPTIONS, calculateDelay, fetchWithRetry, isRetryableError, isRetryableStatus, sleep, withRetry } from "./retry.mjs";
6
6
  import { AFSHttpClient } from "./client.mjs";
7
7
  import { createAFSHttpHandler } from "./handler.mjs";
8
8
 
9
- export { AFSConflictError, AFSErrorCode, AFSHttpClient, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, DEFAULT_RETRY_OPTIONS, VALID_RPC_METHODS, calculateDelay, createAFSHttpHandler, createErrorResponse, createSuccessResponse, expressAdapter, fetchWithRetry, isRetryableError, isRetryableStatus, isValidRpcMethod, koaAdapter, mapErrorToCode, sleep, withRetry };
9
+ export { AFSConflictError, AFSErrorCode, AFSHttpClient, AFSHttpError, AFSInvalidRequestError, AFSNetworkError, AFSNotFoundError, AFSPayloadTooLargeError, AFSPermissionError, AFSRuntimeError, AFSUnauthorizedError, DEFAULT_RETRY_OPTIONS, VALID_RPC_METHODS, calculateDelay, createAFSHttpHandler, createErrorResponse, createSuccessResponse, expressAdapter, fetchWithRetry, isRetryableError, isRetryableStatus, isValidRpcMethod, koaAdapter, mapErrorToCode, sleep, withRetry };
package/dist/protocol.cjs CHANGED
@@ -17,6 +17,8 @@ let AFSErrorCode = /* @__PURE__ */ function(AFSErrorCode) {
17
17
  AFSErrorCode[AFSErrorCode["PARTIAL"] = 4] = "PARTIAL";
18
18
  /** Runtime error */
19
19
  AFSErrorCode[AFSErrorCode["RUNTIME_ERROR"] = 5] = "RUNTIME_ERROR";
20
+ /** Unauthorized access */
21
+ AFSErrorCode[AFSErrorCode["UNAUTHORIZED"] = 6] = "UNAUTHORIZED";
20
22
  return AFSErrorCode;
21
23
  }({});
22
24
  /**
@@ -24,7 +24,7 @@ interface AFSRpcResponse<T = unknown> {
24
24
  * RPC error object
25
25
  */
26
26
  interface AFSRpcError {
27
- /** CLI error code (0-5), compatible with AFS CLI */
27
+ /** CLI error code (0-6), compatible with AFS CLI */
28
28
  code: AFSErrorCode;
29
29
  /** Human-readable error message */
30
30
  message: string;
@@ -48,6 +48,8 @@ declare enum AFSErrorCode {
48
48
  PARTIAL = 4,
49
49
  /** Runtime error */
50
50
  RUNTIME_ERROR = 5,
51
+ /** Unauthorized access */
52
+ UNAUTHORIZED = 6,
51
53
  }
52
54
  /**
53
55
  * Parameter types for each RPC method
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.cts","names":[],"sources":["../src/protocol.ts"],"mappings":";;;;AAqBA;AAKA;KALY,YAAA;AAAA;AAKZ;;AALY,UAKK,aAAA,WAAwB,YAAA,GAAe,YAAA;EAAA,MAAA,EAC9C,CAAA;EAAA,MAAA,EACA,YAAA,CAAa,CAAA;AAAA;AAAA;;AAMvB;AANuB,UAMN,cAAA;EAAA,OAAA;EAAA,IAAA,GAER,CAAA;EAAA,KAAA,GACC,WAAA;AAAA;AAAA;AAMV;AAaA;AAnBU,UAMO,WAAA;EAAA;EAAA,IAAA,EAET,YAAA;EAAA;EAAA,OAAA;EAAA;EAAA,OAAA;AAAA;AAAA;AAWR;AAkBA;;AA7BQ,aAWI,YAAA;EAAA;EAAA,EAAA;EAAA;EAAA,SAAA;EAAA;EAAA,iBAAA;EAAA;EAAA,QAAA;EAAA;EAAA,OAAA;EAAA;EAAA,aAAA;AAAA;AAAA;AAkBZ;;AAlBY,UAkBK,YAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAGH,cAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAIA,cAAA;EAAA;EAAA,KAAA;IAAA,IAAA;IAAA,OAAA,EAID,oBAAA;IAAA,OAAA,GACC,eAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,OAAA,GAIA,gBAAA;EAAA;EAAA,MAAA;IAAA,OAAA;IAAA,OAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,KAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,IAAA,EAIJ,MAAA;IAAA,OAAA,GACI,cAAA;EAAA;AAAA;AAAA;;AAOd;AAPc,UAOG,aAAA;EAAA,IAAA,EACT,aAAA;EAAA,IAAA,EACA,aAAA;EAAA,KAAA,EACC,cAAA;EAAA,MAAA,EACC,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,IAAA,EACF,aAAA;AAAA;AAAA;;AAMR;AANQ,cAMK,iBAAA,EAAmB,GAAA,CAAI,YAAA;AAAA;AAapC;AAOA;AApBoC,iBAapB,gBAAA,CAAA,MAAA,WAAA,MAAA,IAA4C,YAAA;AAAA;AAO5D;;AAP4D,iBAO5C,qBAAA,GAAA,CAAA,IAAA,EAA+B,CAAA,GAAI,cAAA,CAAe,CAAA;AAAA;;;AAAA,iBAUlD,mBAAA,CAAA,IAAA,EACR,YAAA,EAAA,OAAA,UAAA,OAAA,aAGL,cAAA"}
1
+ {"version":3,"file":"protocol.d.cts","names":[],"sources":["../src/protocol.ts"],"mappings":";;;;AAqBA;AAKA;KALY,YAAA;AAAA;AAKZ;;AALY,UAKK,aAAA,WAAwB,YAAA,GAAe,YAAA;EAAA,MAAA,EAC9C,CAAA;EAAA,MAAA,EACA,YAAA,CAAa,CAAA;AAAA;AAAA;;AAMvB;AANuB,UAMN,cAAA;EAAA,OAAA;EAAA,IAAA,GAER,CAAA;EAAA,KAAA,GACC,WAAA;AAAA;AAAA;AAMV;AAaA;AAnBU,UAMO,WAAA;EAAA;EAAA,IAAA,EAET,YAAA;EAAA;EAAA,OAAA;EAAA;EAAA,OAAA;AAAA;AAAA;AAWR;AAoBA;;AA/BQ,aAWI,YAAA;EAAA;EAAA,EAAA;EAAA;EAAA,SAAA;EAAA;EAAA,iBAAA;EAAA;EAAA,QAAA;EAAA;EAAA,OAAA;EAAA;EAAA,aAAA;EAAA;EAAA,YAAA;AAAA;AAAA;AAoBZ;;AApBY,UAoBK,YAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAGH,cAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAIA,cAAA;EAAA;EAAA,KAAA;IAAA,IAAA;IAAA,OAAA,EAID,oBAAA;IAAA,OAAA,GACC,eAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,OAAA,GAIA,gBAAA;EAAA;EAAA,MAAA;IAAA,OAAA;IAAA,OAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,KAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,IAAA,EAIJ,MAAA;IAAA,OAAA,GACI,cAAA;EAAA;AAAA;AAAA;;AAOd;AAPc,UAOG,aAAA;EAAA,IAAA,EACT,aAAA;EAAA,IAAA,EACA,aAAA;EAAA,KAAA,EACC,cAAA;EAAA,MAAA,EACC,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,IAAA,EACF,aAAA;AAAA;AAAA;;AAMR;AANQ,cAMK,iBAAA,EAAmB,GAAA,CAAI,YAAA;AAAA;AAapC;AAOA;AApBoC,iBAapB,gBAAA,CAAA,MAAA,WAAA,MAAA,IAA4C,YAAA;AAAA;AAO5D;;AAP4D,iBAO5C,qBAAA,GAAA,CAAA,IAAA,EAA+B,CAAA,GAAI,cAAA,CAAe,CAAA;AAAA;;;AAAA,iBAUlD,mBAAA,CAAA,IAAA,EACR,YAAA,EAAA,OAAA,UAAA,OAAA,aAGL,cAAA"}
@@ -24,7 +24,7 @@ interface AFSRpcResponse<T = unknown> {
24
24
  * RPC error object
25
25
  */
26
26
  interface AFSRpcError {
27
- /** CLI error code (0-5), compatible with AFS CLI */
27
+ /** CLI error code (0-6), compatible with AFS CLI */
28
28
  code: AFSErrorCode;
29
29
  /** Human-readable error message */
30
30
  message: string;
@@ -48,6 +48,8 @@ declare enum AFSErrorCode {
48
48
  PARTIAL = 4,
49
49
  /** Runtime error */
50
50
  RUNTIME_ERROR = 5,
51
+ /** Unauthorized access */
52
+ UNAUTHORIZED = 6,
51
53
  }
52
54
  /**
53
55
  * Parameter types for each RPC method
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.mts","names":[],"sources":["../src/protocol.ts"],"mappings":";;;;AAqBA;AAKA;KALY,YAAA;AAAA;AAKZ;;AALY,UAKK,aAAA,WAAwB,YAAA,GAAe,YAAA;EAAA,MAAA,EAC9C,CAAA;EAAA,MAAA,EACA,YAAA,CAAa,CAAA;AAAA;AAAA;;AAMvB;AANuB,UAMN,cAAA;EAAA,OAAA;EAAA,IAAA,GAER,CAAA;EAAA,KAAA,GACC,WAAA;AAAA;AAAA;AAMV;AAaA;AAnBU,UAMO,WAAA;EAAA;EAAA,IAAA,EAET,YAAA;EAAA;EAAA,OAAA;EAAA;EAAA,OAAA;AAAA;AAAA;AAWR;AAkBA;;AA7BQ,aAWI,YAAA;EAAA;EAAA,EAAA;EAAA;EAAA,SAAA;EAAA;EAAA,iBAAA;EAAA;EAAA,QAAA;EAAA;EAAA,OAAA;EAAA;EAAA,aAAA;AAAA;AAAA;AAkBZ;;AAlBY,UAkBK,YAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAGH,cAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAIA,cAAA;EAAA;EAAA,KAAA;IAAA,IAAA;IAAA,OAAA,EAID,oBAAA;IAAA,OAAA,GACC,eAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,OAAA,GAIA,gBAAA;EAAA;EAAA,MAAA;IAAA,OAAA;IAAA,OAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,KAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,IAAA,EAIJ,MAAA;IAAA,OAAA,GACI,cAAA;EAAA;AAAA;AAAA;;AAOd;AAPc,UAOG,aAAA;EAAA,IAAA,EACT,aAAA;EAAA,IAAA,EACA,aAAA;EAAA,KAAA,EACC,cAAA;EAAA,MAAA,EACC,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,IAAA,EACF,aAAA;AAAA;AAAA;;AAMR;AANQ,cAMK,iBAAA,EAAmB,GAAA,CAAI,YAAA;AAAA;AAapC;AAOA;AApBoC,iBAapB,gBAAA,CAAA,MAAA,WAAA,MAAA,IAA4C,YAAA;AAAA;AAO5D;;AAP4D,iBAO5C,qBAAA,GAAA,CAAA,IAAA,EAA+B,CAAA,GAAI,cAAA,CAAe,CAAA;AAAA;;;AAAA,iBAUlD,mBAAA,CAAA,IAAA,EACR,YAAA,EAAA,OAAA,UAAA,OAAA,aAGL,cAAA"}
1
+ {"version":3,"file":"protocol.d.mts","names":[],"sources":["../src/protocol.ts"],"mappings":";;;;AAqBA;AAKA;KALY,YAAA;AAAA;AAKZ;;AALY,UAKK,aAAA,WAAwB,YAAA,GAAe,YAAA;EAAA,MAAA,EAC9C,CAAA;EAAA,MAAA,EACA,YAAA,CAAa,CAAA;AAAA;AAAA;;AAMvB;AANuB,UAMN,cAAA;EAAA,OAAA;EAAA,IAAA,GAER,CAAA;EAAA,KAAA,GACC,WAAA;AAAA;AAAA;AAMV;AAaA;AAnBU,UAMO,WAAA;EAAA;EAAA,IAAA,EAET,YAAA;EAAA;EAAA,OAAA;EAAA;EAAA,OAAA;AAAA;AAAA;AAWR;AAoBA;;AA/BQ,aAWI,YAAA;EAAA;EAAA,EAAA;EAAA;EAAA,SAAA;EAAA;EAAA,iBAAA;EAAA;EAAA,QAAA;EAAA;EAAA,OAAA;EAAA;EAAA,aAAA;EAAA;EAAA,YAAA;AAAA;AAAA;AAoBZ;;AApBY,UAoBK,YAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAGH,cAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,OAAA,GAIA,cAAA;EAAA;EAAA,KAAA;IAAA,IAAA;IAAA,OAAA,EAID,oBAAA;IAAA,OAAA,GACC,eAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,OAAA,GAIA,gBAAA;EAAA;EAAA,MAAA;IAAA,OAAA;IAAA,OAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,MAAA;IAAA,IAAA;IAAA,KAAA;IAAA,OAAA,GAKA,gBAAA;EAAA;EAAA,IAAA;IAAA,IAAA;IAAA,IAAA,EAIJ,MAAA;IAAA,OAAA,GACI,cAAA;EAAA;AAAA;AAAA;;AAOd;AAPc,UAOG,aAAA;EAAA,IAAA,EACT,aAAA;EAAA,IAAA,EACA,aAAA;EAAA,KAAA,EACC,cAAA;EAAA,MAAA,EACC,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,MAAA,EACA,eAAA;EAAA,IAAA,EACF,aAAA;AAAA;AAAA;;AAMR;AANQ,cAMK,iBAAA,EAAmB,GAAA,CAAI,YAAA;AAAA;AAapC;AAOA;AApBoC,iBAapB,gBAAA,CAAA,MAAA,WAAA,MAAA,IAA4C,YAAA;AAAA;AAO5D;;AAP4D,iBAO5C,qBAAA,GAAA,CAAA,IAAA,EAA+B,CAAA,GAAI,cAAA,CAAe,CAAA;AAAA;;;AAAA,iBAUlD,mBAAA,CAAA,IAAA,EACR,YAAA,EAAA,OAAA,UAAA,OAAA,aAGL,cAAA"}
package/dist/protocol.mjs CHANGED
@@ -16,6 +16,8 @@ let AFSErrorCode = /* @__PURE__ */ function(AFSErrorCode) {
16
16
  AFSErrorCode[AFSErrorCode["PARTIAL"] = 4] = "PARTIAL";
17
17
  /** Runtime error */
18
18
  AFSErrorCode[AFSErrorCode["RUNTIME_ERROR"] = 5] = "RUNTIME_ERROR";
19
+ /** Unauthorized access */
20
+ AFSErrorCode[AFSErrorCode["UNAUTHORIZED"] = 6] = "UNAUTHORIZED";
19
21
  return AFSErrorCode;
20
22
  }({});
21
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.mjs","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["import type {\n AFSDeleteOptions,\n AFSDeleteResult,\n AFSExecOptions,\n AFSExecResult,\n AFSListOptions,\n AFSListResult,\n AFSReadOptions,\n AFSReadResult,\n AFSRenameOptions,\n AFSRenameResult,\n AFSSearchOptions,\n AFSSearchResult,\n AFSWriteEntryPayload,\n AFSWriteOptions,\n AFSWriteResult,\n} from \"@aigne/afs\";\n\n/**\n * AFS HTTP RPC method names\n */\nexport type AFSRpcMethod = \"list\" | \"read\" | \"write\" | \"delete\" | \"rename\" | \"search\" | \"exec\";\n\n/**\n * RPC request body\n */\nexport interface AFSRpcRequest<M extends AFSRpcMethod = AFSRpcMethod> {\n method: M;\n params: AFSRpcParams[M];\n}\n\n/**\n * RPC response body\n */\nexport interface AFSRpcResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: AFSRpcError;\n}\n\n/**\n * RPC error object\n */\nexport interface AFSRpcError {\n /** CLI error code (0-5), compatible with AFS CLI */\n code: AFSErrorCode;\n /** Human-readable error message */\n message: string;\n /** Additional error details */\n details?: unknown;\n}\n\n/**\n * CLI-compatible error codes\n * @see https://github.com/arcblock/afs/blob/main/intent/specs/afs-cli-spec.md\n */\nexport enum AFSErrorCode {\n /** Operation successful */\n OK = 0,\n /** Path not found */\n NOT_FOUND = 1,\n /** Permission denied (including readonly mode) */\n PERMISSION_DENIED = 2,\n /** Concurrent modification conflict */\n CONFLICT = 3,\n /** Partial success */\n PARTIAL = 4,\n /** Runtime error */\n RUNTIME_ERROR = 5,\n}\n\n/**\n * Parameter types for each RPC method\n */\nexport interface AFSRpcParams {\n list: {\n path: string;\n options?: AFSListOptions;\n };\n read: {\n path: string;\n options?: AFSReadOptions;\n };\n write: {\n path: string;\n content: AFSWriteEntryPayload;\n options?: AFSWriteOptions;\n };\n delete: {\n path: string;\n options?: AFSDeleteOptions;\n };\n rename: {\n oldPath: string;\n newPath: string;\n options?: AFSRenameOptions;\n };\n search: {\n path: string;\n query: string;\n options?: AFSSearchOptions;\n };\n exec: {\n path: string;\n args: Record<string, unknown>;\n options?: AFSExecOptions;\n };\n}\n\n/**\n * Result types for each RPC method\n */\nexport interface AFSRpcResults {\n list: AFSListResult;\n read: AFSReadResult;\n write: AFSWriteResult;\n delete: AFSDeleteResult;\n rename: AFSRenameResult;\n search: AFSSearchResult;\n exec: AFSExecResult;\n}\n\n/**\n * Valid RPC methods set for validation\n */\nexport const VALID_RPC_METHODS: Set<AFSRpcMethod> = new Set([\n \"list\",\n \"read\",\n \"write\",\n \"delete\",\n \"rename\",\n \"search\",\n \"exec\",\n]);\n\n/**\n * Check if a string is a valid RPC method\n */\nexport function isValidRpcMethod(method: string): method is AFSRpcMethod {\n return VALID_RPC_METHODS.has(method as AFSRpcMethod);\n}\n\n/**\n * Create a success response\n */\nexport function createSuccessResponse<T>(data: T): AFSRpcResponse<T> {\n return {\n success: true,\n data,\n };\n}\n\n/**\n * Create an error response\n */\nexport function createErrorResponse(\n code: AFSErrorCode,\n message: string,\n details?: unknown,\n): AFSRpcResponse<never> {\n return {\n success: false,\n error: {\n code,\n message,\n details,\n },\n };\n}\n"],"mappings":";;;;;AAwDA,IAAY,sDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;;;;;AAyDF,MAAa,oBAAuC,IAAI,IAAI;CAC1D;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,SAAgB,iBAAiB,QAAwC;AACvE,QAAO,kBAAkB,IAAI,OAAuB;;;;;AAMtD,SAAgB,sBAAyB,MAA4B;AACnE,QAAO;EACL,SAAS;EACT;EACD;;;;;AAMH,SAAgB,oBACd,MACA,SACA,SACuB;AACvB,QAAO;EACL,SAAS;EACT,OAAO;GACL;GACA;GACA;GACD;EACF"}
1
+ {"version":3,"file":"protocol.mjs","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["import type {\n AFSDeleteOptions,\n AFSDeleteResult,\n AFSExecOptions,\n AFSExecResult,\n AFSListOptions,\n AFSListResult,\n AFSReadOptions,\n AFSReadResult,\n AFSRenameOptions,\n AFSRenameResult,\n AFSSearchOptions,\n AFSSearchResult,\n AFSWriteEntryPayload,\n AFSWriteOptions,\n AFSWriteResult,\n} from \"@aigne/afs\";\n\n/**\n * AFS HTTP RPC method names\n */\nexport type AFSRpcMethod = \"list\" | \"read\" | \"write\" | \"delete\" | \"rename\" | \"search\" | \"exec\";\n\n/**\n * RPC request body\n */\nexport interface AFSRpcRequest<M extends AFSRpcMethod = AFSRpcMethod> {\n method: M;\n params: AFSRpcParams[M];\n}\n\n/**\n * RPC response body\n */\nexport interface AFSRpcResponse<T = unknown> {\n success: boolean;\n data?: T;\n error?: AFSRpcError;\n}\n\n/**\n * RPC error object\n */\nexport interface AFSRpcError {\n /** CLI error code (0-6), compatible with AFS CLI */\n code: AFSErrorCode;\n /** Human-readable error message */\n message: string;\n /** Additional error details */\n details?: unknown;\n}\n\n/**\n * CLI-compatible error codes\n * @see https://github.com/arcblock/afs/blob/main/intent/specs/afs-cli-spec.md\n */\nexport enum AFSErrorCode {\n /** Operation successful */\n OK = 0,\n /** Path not found */\n NOT_FOUND = 1,\n /** Permission denied (including readonly mode) */\n PERMISSION_DENIED = 2,\n /** Concurrent modification conflict */\n CONFLICT = 3,\n /** Partial success */\n PARTIAL = 4,\n /** Runtime error */\n RUNTIME_ERROR = 5,\n /** Unauthorized access */\n UNAUTHORIZED = 6,\n}\n\n/**\n * Parameter types for each RPC method\n */\nexport interface AFSRpcParams {\n list: {\n path: string;\n options?: AFSListOptions;\n };\n read: {\n path: string;\n options?: AFSReadOptions;\n };\n write: {\n path: string;\n content: AFSWriteEntryPayload;\n options?: AFSWriteOptions;\n };\n delete: {\n path: string;\n options?: AFSDeleteOptions;\n };\n rename: {\n oldPath: string;\n newPath: string;\n options?: AFSRenameOptions;\n };\n search: {\n path: string;\n query: string;\n options?: AFSSearchOptions;\n };\n exec: {\n path: string;\n args: Record<string, unknown>;\n options?: AFSExecOptions;\n };\n}\n\n/**\n * Result types for each RPC method\n */\nexport interface AFSRpcResults {\n list: AFSListResult;\n read: AFSReadResult;\n write: AFSWriteResult;\n delete: AFSDeleteResult;\n rename: AFSRenameResult;\n search: AFSSearchResult;\n exec: AFSExecResult;\n}\n\n/**\n * Valid RPC methods set for validation\n */\nexport const VALID_RPC_METHODS: Set<AFSRpcMethod> = new Set([\n \"list\",\n \"read\",\n \"write\",\n \"delete\",\n \"rename\",\n \"search\",\n \"exec\",\n]);\n\n/**\n * Check if a string is a valid RPC method\n */\nexport function isValidRpcMethod(method: string): method is AFSRpcMethod {\n return VALID_RPC_METHODS.has(method as AFSRpcMethod);\n}\n\n/**\n * Create a success response\n */\nexport function createSuccessResponse<T>(data: T): AFSRpcResponse<T> {\n return {\n success: true,\n data,\n };\n}\n\n/**\n * Create an error response\n */\nexport function createErrorResponse(\n code: AFSErrorCode,\n message: string,\n details?: unknown,\n): AFSRpcResponse<never> {\n return {\n success: false,\n error: {\n code,\n message,\n details,\n },\n };\n}\n"],"mappings":";;;;;AAwDA,IAAY,sDAAL;;AAEL;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;AAEA;;;;;;AAyDF,MAAa,oBAAuC,IAAI,IAAI;CAC1D;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,SAAgB,iBAAiB,QAAwC;AACvE,QAAO,kBAAkB,IAAI,OAAuB;;;;;AAMtD,SAAgB,sBAAyB,MAA4B;AACnE,QAAO;EACL,SAAS;EACT;EACD;;;;;AAMH,SAAgB,oBACd,MACA,SACA,SACuB;AACvB,QAAO;EACL,SAAS;EACT,OAAO;GACL;GACA;GACA;GACD;EACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/afs-http",
3
- "version": "1.11.0-beta.4",
3
+ "version": "1.11.0-beta.5",
4
4
  "description": "AIGNE AFS HTTP transport provider for remote AFS access",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -34,7 +34,7 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "zod": "^3.25.67",
37
- "@aigne/afs": "^1.11.0-beta.4"
37
+ "@aigne/afs": "^1.11.0-beta.5"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/bun": "^1.3.6",