@aigne/afs-http 1.11.0-beta.3

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.
Files changed (56) hide show
  1. package/LICENSE.md +26 -0
  2. package/README.md +318 -0
  3. package/dist/adapters/express.cjs +74 -0
  4. package/dist/adapters/express.d.cts +56 -0
  5. package/dist/adapters/express.d.cts.map +1 -0
  6. package/dist/adapters/express.d.mts +56 -0
  7. package/dist/adapters/express.d.mts.map +1 -0
  8. package/dist/adapters/express.mjs +74 -0
  9. package/dist/adapters/express.mjs.map +1 -0
  10. package/dist/adapters/koa.cjs +73 -0
  11. package/dist/adapters/koa.d.cts +56 -0
  12. package/dist/adapters/koa.d.cts.map +1 -0
  13. package/dist/adapters/koa.d.mts +56 -0
  14. package/dist/adapters/koa.d.mts.map +1 -0
  15. package/dist/adapters/koa.mjs +73 -0
  16. package/dist/adapters/koa.mjs.map +1 -0
  17. package/dist/client.cjs +143 -0
  18. package/dist/client.d.cts +70 -0
  19. package/dist/client.d.cts.map +1 -0
  20. package/dist/client.d.mts +70 -0
  21. package/dist/client.d.mts.map +1 -0
  22. package/dist/client.mjs +144 -0
  23. package/dist/client.mjs.map +1 -0
  24. package/dist/errors.cjs +105 -0
  25. package/dist/errors.d.cts +63 -0
  26. package/dist/errors.d.cts.map +1 -0
  27. package/dist/errors.d.mts +63 -0
  28. package/dist/errors.d.mts.map +1 -0
  29. package/dist/errors.mjs +98 -0
  30. package/dist/errors.mjs.map +1 -0
  31. package/dist/handler.cjs +126 -0
  32. package/dist/handler.d.cts +43 -0
  33. package/dist/handler.d.cts.map +1 -0
  34. package/dist/handler.d.mts +43 -0
  35. package/dist/handler.d.mts.map +1 -0
  36. package/dist/handler.mjs +127 -0
  37. package/dist/handler.mjs.map +1 -0
  38. package/dist/index.cjs +33 -0
  39. package/dist/index.d.cts +8 -0
  40. package/dist/index.d.mts +8 -0
  41. package/dist/index.mjs +9 -0
  42. package/dist/protocol.cjs +68 -0
  43. package/dist/protocol.d.cts +119 -0
  44. package/dist/protocol.d.cts.map +1 -0
  45. package/dist/protocol.d.mts +119 -0
  46. package/dist/protocol.d.mts.map +1 -0
  47. package/dist/protocol.mjs +64 -0
  48. package/dist/protocol.mjs.map +1 -0
  49. package/dist/retry.cjs +111 -0
  50. package/dist/retry.d.cts +57 -0
  51. package/dist/retry.d.cts.map +1 -0
  52. package/dist/retry.d.mts +57 -0
  53. package/dist/retry.d.mts.map +1 -0
  54. package/dist/retry.mjs +105 -0
  55. package/dist/retry.mjs.map +1 -0
  56. package/package.json +55 -0
@@ -0,0 +1,56 @@
1
+ import { Readable } from "node:stream";
2
+
3
+ //#region src/adapters/koa.d.ts
4
+ /**
5
+ * Koa-compatible context interface
6
+ */
7
+ interface KoaContext {
8
+ method: string;
9
+ protocol: string;
10
+ host: string;
11
+ originalUrl: string;
12
+ request: {
13
+ body?: unknown;
14
+ headers: Record<string, string | string[] | undefined>;
15
+ };
16
+ req: Readable;
17
+ status: number;
18
+ body: string;
19
+ set(name: string, value: string): void;
20
+ }
21
+ /**
22
+ * Koa-compatible next function
23
+ */
24
+ type KoaNext = () => Promise<void>;
25
+ /**
26
+ * Koa-compatible middleware
27
+ */
28
+ type KoaMiddleware = (ctx: KoaContext, next: KoaNext) => Promise<void>;
29
+ /**
30
+ * Koa adapter for AFS HTTP handler
31
+ *
32
+ * This adapter converts between Koa context and Web Standard Request/Response
33
+ * objects, handling both scenarios where body parsing middleware is configured
34
+ * (like koa-bodyparser) and where it isn't.
35
+ *
36
+ * @param handler - The AFS HTTP handler function
37
+ * @returns A Koa-compatible middleware
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * import Koa from "koa";
42
+ * import Router from "@koa/router";
43
+ * import { createAFSHttpHandler, koaAdapter } from "@aigne/afs-http";
44
+ *
45
+ * const handler = createAFSHttpHandler({ module: provider });
46
+ * const app = new Koa();
47
+ * const router = new Router();
48
+ *
49
+ * router.post("/afs/rpc", koaAdapter(handler));
50
+ * app.use(router.routes());
51
+ * ```
52
+ */
53
+ declare function koaAdapter(handler: (request: Request) => Promise<Response>): KoaMiddleware;
54
+ //#endregion
55
+ export { koaAdapter };
56
+ //# sourceMappingURL=koa.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"koa.d.cts","names":[],"sources":["../../src/adapters/koa.ts"],"mappings":";;;;AAA4C;AAc7B;UATL,UAAA;EAAA,MAAA;EAAA,QAAA;EAAA,IAAA;EAAA,WAAA;EAAA,OAAA;IAAA,IAAA;IAAA,OAAA,EAOG,MAAA;EAAA;EAAA,GAAA,EAEN,QAAA;EAAA,MAAA;EAAA,IAAA;EAAA,GAAA,CAAA,IAAA,UAAA,KAAA;AAAA;AAAA;AAAQ;AASa;AATrB,KASF,OAAA,SAAgB,OAAA;AAAA;AAAO;;AAAP,KAKhB,aAAA,IAAA,GAAA,EAAsB,UAAA,EAAA,IAAA,EAAkB,OAAA,KAAY,OAAA;AAAA;;AAwDzD;;;;;;;;;;;;;;;;;;;;;;AAxDyD,iBAwDzC,UAAA,CAAA,OAAA,GAAA,OAAA,EAA8B,OAAA,KAAY,OAAA,CAAQ,QAAA,IAAY,aAAA"}
@@ -0,0 +1,56 @@
1
+ import { Readable } from "node:stream";
2
+
3
+ //#region src/adapters/koa.d.ts
4
+ /**
5
+ * Koa-compatible context interface
6
+ */
7
+ interface KoaContext {
8
+ method: string;
9
+ protocol: string;
10
+ host: string;
11
+ originalUrl: string;
12
+ request: {
13
+ body?: unknown;
14
+ headers: Record<string, string | string[] | undefined>;
15
+ };
16
+ req: Readable;
17
+ status: number;
18
+ body: string;
19
+ set(name: string, value: string): void;
20
+ }
21
+ /**
22
+ * Koa-compatible next function
23
+ */
24
+ type KoaNext = () => Promise<void>;
25
+ /**
26
+ * Koa-compatible middleware
27
+ */
28
+ type KoaMiddleware = (ctx: KoaContext, next: KoaNext) => Promise<void>;
29
+ /**
30
+ * Koa adapter for AFS HTTP handler
31
+ *
32
+ * This adapter converts between Koa context and Web Standard Request/Response
33
+ * objects, handling both scenarios where body parsing middleware is configured
34
+ * (like koa-bodyparser) and where it isn't.
35
+ *
36
+ * @param handler - The AFS HTTP handler function
37
+ * @returns A Koa-compatible middleware
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * import Koa from "koa";
42
+ * import Router from "@koa/router";
43
+ * import { createAFSHttpHandler, koaAdapter } from "@aigne/afs-http";
44
+ *
45
+ * const handler = createAFSHttpHandler({ module: provider });
46
+ * const app = new Koa();
47
+ * const router = new Router();
48
+ *
49
+ * router.post("/afs/rpc", koaAdapter(handler));
50
+ * app.use(router.routes());
51
+ * ```
52
+ */
53
+ declare function koaAdapter(handler: (request: Request) => Promise<Response>): KoaMiddleware;
54
+ //#endregion
55
+ export { koaAdapter };
56
+ //# sourceMappingURL=koa.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"koa.d.mts","names":[],"sources":["../../src/adapters/koa.ts"],"mappings":";;;;AAA4C;AAc7B;UATL,UAAA;EAAA,MAAA;EAAA,QAAA;EAAA,IAAA;EAAA,WAAA;EAAA,OAAA;IAAA,IAAA;IAAA,OAAA,EAOG,MAAA;EAAA;EAAA,GAAA,EAEN,QAAA;EAAA,MAAA;EAAA,IAAA;EAAA,GAAA,CAAA,IAAA,UAAA,KAAA;AAAA;AAAA;AAAQ;AASa;AATrB,KASF,OAAA,SAAgB,OAAA;AAAA;AAAO;;AAAP,KAKhB,aAAA,IAAA,GAAA,EAAsB,UAAA,EAAA,IAAA,EAAkB,OAAA,KAAY,OAAA;AAAA;;AAwDzD;;;;;;;;;;;;;;;;;;;;;;AAxDyD,iBAwDzC,UAAA,CAAA,OAAA,GAAA,OAAA,EAA8B,OAAA,KAAY,OAAA,CAAQ,QAAA,IAAY,aAAA"}
@@ -0,0 +1,73 @@
1
+ //#region src/adapters/koa.ts
2
+ /**
3
+ * Collect stream data into a string
4
+ */
5
+ async function streamToString(stream) {
6
+ const chunks = [];
7
+ return new Promise((resolve, reject) => {
8
+ stream.on("data", (chunk) => chunks.push(chunk));
9
+ stream.on("error", reject);
10
+ stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
11
+ });
12
+ }
13
+ /**
14
+ * Convert Koa headers to Headers object
15
+ */
16
+ function convertHeaders(headers) {
17
+ const result = new Headers();
18
+ for (const [key, value] of Object.entries(headers)) {
19
+ if (value === void 0) continue;
20
+ if (Array.isArray(value)) for (const v of value) result.append(key, v);
21
+ else result.set(key, value);
22
+ }
23
+ return result;
24
+ }
25
+ /**
26
+ * Koa adapter for AFS HTTP handler
27
+ *
28
+ * This adapter converts between Koa context and Web Standard Request/Response
29
+ * objects, handling both scenarios where body parsing middleware is configured
30
+ * (like koa-bodyparser) and where it isn't.
31
+ *
32
+ * @param handler - The AFS HTTP handler function
33
+ * @returns A Koa-compatible middleware
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * import Koa from "koa";
38
+ * import Router from "@koa/router";
39
+ * import { createAFSHttpHandler, koaAdapter } from "@aigne/afs-http";
40
+ *
41
+ * const handler = createAFSHttpHandler({ module: provider });
42
+ * const app = new Koa();
43
+ * const router = new Router();
44
+ *
45
+ * router.post("/afs/rpc", koaAdapter(handler));
46
+ * app.use(router.routes());
47
+ * ```
48
+ */
49
+ function koaAdapter(handler) {
50
+ return async (ctx, _next) => {
51
+ const url = `${ctx.protocol}://${ctx.host}${ctx.originalUrl}`;
52
+ let body;
53
+ if (ctx.method === "POST" || ctx.method === "PUT" || ctx.method === "PATCH") {
54
+ const requestBody = ctx.request.body;
55
+ if (requestBody !== void 0 && requestBody !== null && typeof requestBody === "object" && Object.keys(requestBody).length > 0) body = JSON.stringify(requestBody);
56
+ else body = await streamToString(ctx.req);
57
+ }
58
+ const response = await handler(new Request(url, {
59
+ method: ctx.method,
60
+ headers: convertHeaders(ctx.request.headers),
61
+ body
62
+ }));
63
+ ctx.status = response.status;
64
+ response.headers.forEach((value, key) => {
65
+ ctx.set(key, value);
66
+ });
67
+ ctx.body = await response.text();
68
+ };
69
+ }
70
+
71
+ //#endregion
72
+ export { koaAdapter };
73
+ //# sourceMappingURL=koa.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"koa.mjs","names":[],"sources":["../../src/adapters/koa.ts"],"sourcesContent":["import type { Readable } from \"node:stream\";\n\n/**\n * Koa-compatible context interface\n */\ninterface KoaContext {\n method: string;\n protocol: string;\n host: string;\n originalUrl: string;\n request: {\n body?: unknown;\n headers: Record<string, string | string[] | undefined>;\n };\n req: Readable;\n status: number;\n body: string;\n set(name: string, value: string): void;\n}\n\n/**\n * Koa-compatible next function\n */\ntype KoaNext = () => Promise<void>;\n\n/**\n * Koa-compatible middleware\n */\ntype KoaMiddleware = (ctx: KoaContext, next: KoaNext) => Promise<void>;\n\n/**\n * Collect stream data into a string\n */\nasync function streamToString(stream: Readable): Promise<string> {\n const chunks: Buffer[] = [];\n return new Promise((resolve, reject) => {\n stream.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n stream.on(\"error\", reject);\n stream.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf8\")));\n });\n}\n\n/**\n * Convert Koa headers to Headers object\n */\nfunction convertHeaders(headers: Record<string, string | string[] | undefined>): Headers {\n const result = new Headers();\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const v of value) {\n result.append(key, v);\n }\n } else {\n result.set(key, value);\n }\n }\n return result;\n}\n\n/**\n * Koa adapter for AFS HTTP handler\n *\n * This adapter converts between Koa context and Web Standard Request/Response\n * objects, handling both scenarios where body parsing middleware is configured\n * (like koa-bodyparser) and where it isn't.\n *\n * @param handler - The AFS HTTP handler function\n * @returns A Koa-compatible middleware\n *\n * @example\n * ```typescript\n * import Koa from \"koa\";\n * import Router from \"@koa/router\";\n * import { createAFSHttpHandler, koaAdapter } from \"@aigne/afs-http\";\n *\n * const handler = createAFSHttpHandler({ module: provider });\n * const app = new Koa();\n * const router = new Router();\n *\n * router.post(\"/afs/rpc\", koaAdapter(handler));\n * app.use(router.routes());\n * ```\n */\nexport function koaAdapter(handler: (request: Request) => Promise<Response>): KoaMiddleware {\n return async (ctx, _next) => {\n // Build URL from context\n const url = `${ctx.protocol}://${ctx.host}${ctx.originalUrl}`;\n\n // Get body - handle both parsed and unparsed scenarios\n let body: string | undefined;\n\n if (ctx.method === \"POST\" || ctx.method === \"PUT\" || ctx.method === \"PATCH\") {\n const requestBody = ctx.request.body;\n if (\n requestBody !== undefined &&\n requestBody !== null &&\n typeof requestBody === \"object\" &&\n Object.keys(requestBody).length > 0\n ) {\n // Scenario 1: Body already parsed by middleware (e.g., koa-bodyparser)\n body = JSON.stringify(requestBody);\n } else {\n // Scenario 2: Body not parsed, read from stream\n body = await streamToString(ctx.req);\n }\n }\n\n // Create Web Standard Request\n const request = new Request(url, {\n method: ctx.method,\n headers: convertHeaders(ctx.request.headers),\n body,\n });\n\n // Call the handler\n const response = await handler(request);\n\n // Write response back to Koa context\n ctx.status = response.status;\n\n // Copy headers\n response.headers.forEach((value, key) => {\n ctx.set(key, value);\n });\n\n // Set body\n ctx.body = await response.text();\n };\n}\n"],"mappings":";;;;AAiCA,eAAe,eAAe,QAAmC;CAC/D,MAAM,SAAmB,EAAE;AAC3B,QAAO,IAAI,SAAS,SAAS,WAAW;AACtC,SAAO,GAAG,SAAS,UAAkB,OAAO,KAAK,MAAM,CAAC;AACxD,SAAO,GAAG,SAAS,OAAO;AAC1B,SAAO,GAAG,aAAa,QAAQ,OAAO,OAAO,OAAO,CAAC,SAAS,OAAO,CAAC,CAAC;GACvE;;;;;AAMJ,SAAS,eAAe,SAAiE;CACvF,MAAM,SAAS,IAAI,SAAS;AAC5B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,MAAI,UAAU,OAAW;AACzB,MAAI,MAAM,QAAQ,MAAM,CACtB,MAAK,MAAM,KAAK,MACd,QAAO,OAAO,KAAK,EAAE;MAGvB,QAAO,IAAI,KAAK,MAAM;;AAG1B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BT,SAAgB,WAAW,SAAiE;AAC1F,QAAO,OAAO,KAAK,UAAU;EAE3B,MAAM,MAAM,GAAG,IAAI,SAAS,KAAK,IAAI,OAAO,IAAI;EAGhD,IAAI;AAEJ,MAAI,IAAI,WAAW,UAAU,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS;GAC3E,MAAM,cAAc,IAAI,QAAQ;AAChC,OACE,gBAAgB,UAChB,gBAAgB,QAChB,OAAO,gBAAgB,YACvB,OAAO,KAAK,YAAY,CAAC,SAAS,EAGlC,QAAO,KAAK,UAAU,YAAY;OAGlC,QAAO,MAAM,eAAe,IAAI,IAAI;;EAYxC,MAAM,WAAW,MAAM,QAPP,IAAI,QAAQ,KAAK;GAC/B,QAAQ,IAAI;GACZ,SAAS,eAAe,IAAI,QAAQ,QAAQ;GAC5C;GACD,CAAC,CAGqC;AAGvC,MAAI,SAAS,SAAS;AAGtB,WAAS,QAAQ,SAAS,OAAO,QAAQ;AACvC,OAAI,IAAI,KAAK,MAAM;IACnB;AAGF,MAAI,OAAO,MAAM,SAAS,MAAM"}
@@ -0,0 +1,143 @@
1
+ const require_errors = require('./errors.cjs');
2
+ const require_retry = require('./retry.cjs');
3
+
4
+ //#region src/client.ts
5
+ /**
6
+ * Default client options
7
+ */
8
+ const DEFAULT_CLIENT_OPTIONS = {
9
+ timeout: 3e4,
10
+ maxBodySize: 10 * 1024 * 1024,
11
+ accessMode: "readwrite"
12
+ };
13
+ /**
14
+ * AFS HTTP Client
15
+ *
16
+ * Implements the AFSModule interface, forwarding all operations to a remote
17
+ * AFS server via HTTP RPC calls.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { AFS } from "@aigne/afs";
22
+ * import { AFSHttpClient } from "@aigne/afs-http";
23
+ *
24
+ * const afs = new AFS();
25
+ * afs.mount(
26
+ * new AFSHttpClient({
27
+ * url: "https://remote-afs.example.com/afs/rpc",
28
+ * name: "remote",
29
+ * })
30
+ * );
31
+ *
32
+ * // Use like any local provider
33
+ * const result = await afs.list("/modules/remote/some-path");
34
+ * ```
35
+ */
36
+ var AFSHttpClient = class {
37
+ name;
38
+ description;
39
+ accessMode;
40
+ url;
41
+ timeout;
42
+ maxBodySize;
43
+ retryOptions;
44
+ constructor(options) {
45
+ if (!options.url) throw new Error("AFSHttpClient requires a url option");
46
+ if (!options.name) throw new Error("AFSHttpClient requires a name option");
47
+ this.url = options.url.endsWith("/rpc") ? options.url : `${options.url}/rpc`;
48
+ this.name = options.name;
49
+ this.description = options.description;
50
+ this.accessMode = options.accessMode ?? DEFAULT_CLIENT_OPTIONS.accessMode;
51
+ this.timeout = options.timeout ?? DEFAULT_CLIENT_OPTIONS.timeout;
52
+ this.maxBodySize = options.maxBodySize ?? DEFAULT_CLIENT_OPTIONS.maxBodySize;
53
+ this.retryOptions = {
54
+ ...require_retry.DEFAULT_RETRY_OPTIONS,
55
+ ...options.retry
56
+ };
57
+ }
58
+ /**
59
+ * Make an RPC call to the remote server
60
+ */
61
+ async rpc(method, params) {
62
+ const body = JSON.stringify({
63
+ method,
64
+ params
65
+ });
66
+ if (body.length > this.maxBodySize) throw new require_errors.AFSRuntimeError(`Request body too large: ${body.length} bytes exceeds limit of ${this.maxBodySize} bytes`);
67
+ let response;
68
+ try {
69
+ response = await require_retry.fetchWithRetry(this.url, {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body,
73
+ signal: AbortSignal.timeout(this.timeout)
74
+ }, this.retryOptions);
75
+ } catch (error) {
76
+ throw new require_errors.AFSNetworkError(`Failed to connect to ${this.url}: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
77
+ }
78
+ let result;
79
+ try {
80
+ result = await response.json();
81
+ } catch {
82
+ throw new require_errors.AFSRuntimeError(`Invalid response from server: expected JSON, got ${response.headers.get("content-type")}`);
83
+ }
84
+ if (!result.success) {
85
+ 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
+ });
90
+ throw new require_errors.AFSRuntimeError("Request failed without error details");
91
+ }
92
+ return result.data;
93
+ }
94
+ async list(path, options) {
95
+ return this.rpc("list", {
96
+ path,
97
+ options
98
+ });
99
+ }
100
+ async read(path, options) {
101
+ return this.rpc("read", {
102
+ path,
103
+ options
104
+ });
105
+ }
106
+ async write(path, content, options) {
107
+ return this.rpc("write", {
108
+ path,
109
+ content,
110
+ options
111
+ });
112
+ }
113
+ async delete(path, options) {
114
+ return this.rpc("delete", {
115
+ path,
116
+ options
117
+ });
118
+ }
119
+ async rename(oldPath, newPath, options) {
120
+ return this.rpc("rename", {
121
+ oldPath,
122
+ newPath,
123
+ options
124
+ });
125
+ }
126
+ async search(path, query, options) {
127
+ return this.rpc("search", {
128
+ path,
129
+ query,
130
+ options
131
+ });
132
+ }
133
+ async exec(path, args, options) {
134
+ return this.rpc("exec", {
135
+ path,
136
+ args,
137
+ options
138
+ });
139
+ }
140
+ };
141
+
142
+ //#endregion
143
+ exports.AFSHttpClient = AFSHttpClient;
@@ -0,0 +1,70 @@
1
+ import { RetryOptions } from "./retry.cjs";
2
+ import { AFSAccessMode, AFSDeleteOptions, AFSDeleteResult, AFSExecOptions, AFSExecResult, AFSListOptions, AFSListResult, AFSModule, AFSReadOptions, AFSReadResult, AFSRenameOptions, AFSRenameResult, AFSSearchOptions, AFSSearchResult, AFSWriteEntryPayload, AFSWriteOptions, AFSWriteResult } from "@aigne/afs";
3
+
4
+ //#region src/client.d.ts
5
+ /**
6
+ * Options for AFSHttpClient
7
+ */
8
+ interface AFSHttpClientOptions {
9
+ /** Server URL (required) */
10
+ url: string;
11
+ /** Module name for mounting to AFS (required) */
12
+ name: string;
13
+ /** Module description (optional) */
14
+ description?: string;
15
+ /** Access mode (default: "readwrite") */
16
+ accessMode?: AFSAccessMode;
17
+ /** Request timeout in milliseconds (default: 30000) */
18
+ timeout?: number;
19
+ /** Maximum request body size in bytes (default: 10MB) */
20
+ maxBodySize?: number;
21
+ /** Retry configuration */
22
+ retry?: RetryOptions;
23
+ }
24
+ /**
25
+ * AFS HTTP Client
26
+ *
27
+ * Implements the AFSModule interface, forwarding all operations to a remote
28
+ * AFS server via HTTP RPC calls.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { AFS } from "@aigne/afs";
33
+ * import { AFSHttpClient } from "@aigne/afs-http";
34
+ *
35
+ * const afs = new AFS();
36
+ * afs.mount(
37
+ * new AFSHttpClient({
38
+ * url: "https://remote-afs.example.com/afs/rpc",
39
+ * name: "remote",
40
+ * })
41
+ * );
42
+ *
43
+ * // Use like any local provider
44
+ * const result = await afs.list("/modules/remote/some-path");
45
+ * ```
46
+ */
47
+ declare class AFSHttpClient implements AFSModule {
48
+ readonly name: string;
49
+ readonly description?: string;
50
+ readonly accessMode: AFSAccessMode;
51
+ private readonly url;
52
+ private readonly timeout;
53
+ private readonly maxBodySize;
54
+ private readonly retryOptions;
55
+ constructor(options: AFSHttpClientOptions);
56
+ /**
57
+ * Make an RPC call to the remote server
58
+ */
59
+ private rpc;
60
+ list(path: string, options?: AFSListOptions): Promise<AFSListResult>;
61
+ read(path: string, options?: AFSReadOptions): Promise<AFSReadResult>;
62
+ write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
63
+ delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
64
+ rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
65
+ search(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
66
+ exec(path: string, args: Record<string, unknown>, options: AFSExecOptions): Promise<AFSExecResult>;
67
+ }
68
+ //#endregion
69
+ export { AFSHttpClient, AFSHttpClientOptions };
70
+ //# sourceMappingURL=client.d.cts.map
@@ -0,0 +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"}
@@ -0,0 +1,70 @@
1
+ import { RetryOptions } from "./retry.mjs";
2
+ import { AFSAccessMode, AFSDeleteOptions, AFSDeleteResult, AFSExecOptions, AFSExecResult, AFSListOptions, AFSListResult, AFSModule, AFSReadOptions, AFSReadResult, AFSRenameOptions, AFSRenameResult, AFSSearchOptions, AFSSearchResult, AFSWriteEntryPayload, AFSWriteOptions, AFSWriteResult } from "@aigne/afs";
3
+
4
+ //#region src/client.d.ts
5
+ /**
6
+ * Options for AFSHttpClient
7
+ */
8
+ interface AFSHttpClientOptions {
9
+ /** Server URL (required) */
10
+ url: string;
11
+ /** Module name for mounting to AFS (required) */
12
+ name: string;
13
+ /** Module description (optional) */
14
+ description?: string;
15
+ /** Access mode (default: "readwrite") */
16
+ accessMode?: AFSAccessMode;
17
+ /** Request timeout in milliseconds (default: 30000) */
18
+ timeout?: number;
19
+ /** Maximum request body size in bytes (default: 10MB) */
20
+ maxBodySize?: number;
21
+ /** Retry configuration */
22
+ retry?: RetryOptions;
23
+ }
24
+ /**
25
+ * AFS HTTP Client
26
+ *
27
+ * Implements the AFSModule interface, forwarding all operations to a remote
28
+ * AFS server via HTTP RPC calls.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { AFS } from "@aigne/afs";
33
+ * import { AFSHttpClient } from "@aigne/afs-http";
34
+ *
35
+ * const afs = new AFS();
36
+ * afs.mount(
37
+ * new AFSHttpClient({
38
+ * url: "https://remote-afs.example.com/afs/rpc",
39
+ * name: "remote",
40
+ * })
41
+ * );
42
+ *
43
+ * // Use like any local provider
44
+ * const result = await afs.list("/modules/remote/some-path");
45
+ * ```
46
+ */
47
+ declare class AFSHttpClient implements AFSModule {
48
+ readonly name: string;
49
+ readonly description?: string;
50
+ readonly accessMode: AFSAccessMode;
51
+ private readonly url;
52
+ private readonly timeout;
53
+ private readonly maxBodySize;
54
+ private readonly retryOptions;
55
+ constructor(options: AFSHttpClientOptions);
56
+ /**
57
+ * Make an RPC call to the remote server
58
+ */
59
+ private rpc;
60
+ list(path: string, options?: AFSListOptions): Promise<AFSListResult>;
61
+ read(path: string, options?: AFSReadOptions): Promise<AFSReadResult>;
62
+ write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
63
+ delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
64
+ rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
65
+ search(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
66
+ exec(path: string, args: Record<string, unknown>, options: AFSExecOptions): Promise<AFSExecResult>;
67
+ }
68
+ //#endregion
69
+ export { AFSHttpClient, AFSHttpClientOptions };
70
+ //# sourceMappingURL=client.d.mts.map
@@ -0,0 +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"}
@@ -0,0 +1,144 @@
1
+ import { AFSNetworkError, AFSRuntimeError } from "./errors.mjs";
2
+ import { DEFAULT_RETRY_OPTIONS, fetchWithRetry } from "./retry.mjs";
3
+
4
+ //#region src/client.ts
5
+ /**
6
+ * Default client options
7
+ */
8
+ const DEFAULT_CLIENT_OPTIONS = {
9
+ timeout: 3e4,
10
+ maxBodySize: 10 * 1024 * 1024,
11
+ accessMode: "readwrite"
12
+ };
13
+ /**
14
+ * AFS HTTP Client
15
+ *
16
+ * Implements the AFSModule interface, forwarding all operations to a remote
17
+ * AFS server via HTTP RPC calls.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { AFS } from "@aigne/afs";
22
+ * import { AFSHttpClient } from "@aigne/afs-http";
23
+ *
24
+ * const afs = new AFS();
25
+ * afs.mount(
26
+ * new AFSHttpClient({
27
+ * url: "https://remote-afs.example.com/afs/rpc",
28
+ * name: "remote",
29
+ * })
30
+ * );
31
+ *
32
+ * // Use like any local provider
33
+ * const result = await afs.list("/modules/remote/some-path");
34
+ * ```
35
+ */
36
+ var AFSHttpClient = class {
37
+ name;
38
+ description;
39
+ accessMode;
40
+ url;
41
+ timeout;
42
+ maxBodySize;
43
+ retryOptions;
44
+ constructor(options) {
45
+ if (!options.url) throw new Error("AFSHttpClient requires a url option");
46
+ if (!options.name) throw new Error("AFSHttpClient requires a name option");
47
+ this.url = options.url.endsWith("/rpc") ? options.url : `${options.url}/rpc`;
48
+ this.name = options.name;
49
+ this.description = options.description;
50
+ this.accessMode = options.accessMode ?? DEFAULT_CLIENT_OPTIONS.accessMode;
51
+ this.timeout = options.timeout ?? DEFAULT_CLIENT_OPTIONS.timeout;
52
+ this.maxBodySize = options.maxBodySize ?? DEFAULT_CLIENT_OPTIONS.maxBodySize;
53
+ this.retryOptions = {
54
+ ...DEFAULT_RETRY_OPTIONS,
55
+ ...options.retry
56
+ };
57
+ }
58
+ /**
59
+ * Make an RPC call to the remote server
60
+ */
61
+ async rpc(method, params) {
62
+ const body = JSON.stringify({
63
+ method,
64
+ params
65
+ });
66
+ if (body.length > this.maxBodySize) throw new AFSRuntimeError(`Request body too large: ${body.length} bytes exceeds limit of ${this.maxBodySize} bytes`);
67
+ let response;
68
+ try {
69
+ response = await fetchWithRetry(this.url, {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body,
73
+ signal: AbortSignal.timeout(this.timeout)
74
+ }, this.retryOptions);
75
+ } catch (error) {
76
+ throw new AFSNetworkError(`Failed to connect to ${this.url}: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : void 0);
77
+ }
78
+ let result;
79
+ try {
80
+ result = await response.json();
81
+ } catch {
82
+ throw new AFSRuntimeError(`Invalid response from server: expected JSON, got ${response.headers.get("content-type")}`);
83
+ }
84
+ if (!result.success) {
85
+ const error = result.error;
86
+ if (error) throw new AFSRuntimeError(error.message || "Unknown error", {
87
+ code: error.code,
88
+ details: error.details
89
+ });
90
+ throw new AFSRuntimeError("Request failed without error details");
91
+ }
92
+ return result.data;
93
+ }
94
+ async list(path, options) {
95
+ return this.rpc("list", {
96
+ path,
97
+ options
98
+ });
99
+ }
100
+ async read(path, options) {
101
+ return this.rpc("read", {
102
+ path,
103
+ options
104
+ });
105
+ }
106
+ async write(path, content, options) {
107
+ return this.rpc("write", {
108
+ path,
109
+ content,
110
+ options
111
+ });
112
+ }
113
+ async delete(path, options) {
114
+ return this.rpc("delete", {
115
+ path,
116
+ options
117
+ });
118
+ }
119
+ async rename(oldPath, newPath, options) {
120
+ return this.rpc("rename", {
121
+ oldPath,
122
+ newPath,
123
+ options
124
+ });
125
+ }
126
+ async search(path, query, options) {
127
+ return this.rpc("search", {
128
+ path,
129
+ query,
130
+ options
131
+ });
132
+ }
133
+ async exec(path, args, options) {
134
+ return this.rpc("exec", {
135
+ path,
136
+ args,
137
+ options
138
+ });
139
+ }
140
+ };
141
+
142
+ //#endregion
143
+ export { AFSHttpClient };
144
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +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"}