@aigne/afs 1.11.0-beta → 1.11.0-beta.10

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 (164) hide show
  1. package/LICENSE.md +17 -84
  2. package/README.md +4 -13
  3. package/dist/_virtual/rolldown_runtime.mjs +7 -0
  4. package/dist/afs.cjs +1330 -0
  5. package/dist/afs.d.cts +275 -0
  6. package/dist/afs.d.cts.map +1 -0
  7. package/dist/afs.d.mts +275 -0
  8. package/dist/afs.d.mts.map +1 -0
  9. package/dist/afs.mjs +1331 -0
  10. package/dist/afs.mjs.map +1 -0
  11. package/dist/capabilities/index.d.mts +2 -0
  12. package/dist/capabilities/types.d.cts +100 -0
  13. package/dist/capabilities/types.d.cts.map +1 -0
  14. package/dist/capabilities/types.d.mts +100 -0
  15. package/dist/capabilities/types.d.mts.map +1 -0
  16. package/dist/capabilities/world-mapping.cjs +20 -0
  17. package/dist/capabilities/world-mapping.d.cts +139 -0
  18. package/dist/capabilities/world-mapping.d.cts.map +1 -0
  19. package/dist/capabilities/world-mapping.d.mts +139 -0
  20. package/dist/capabilities/world-mapping.d.mts.map +1 -0
  21. package/dist/capabilities/world-mapping.mjs +20 -0
  22. package/dist/capabilities/world-mapping.mjs.map +1 -0
  23. package/dist/error.cjs +63 -0
  24. package/dist/error.d.cts +39 -0
  25. package/dist/error.d.cts.map +1 -0
  26. package/dist/error.d.mts +39 -0
  27. package/dist/error.d.mts.map +1 -0
  28. package/dist/error.mjs +59 -0
  29. package/dist/error.mjs.map +1 -0
  30. package/dist/index.cjs +72 -345
  31. package/dist/index.d.cts +18 -300
  32. package/dist/index.d.mts +20 -300
  33. package/dist/index.mjs +16 -342
  34. package/dist/loader/index.cjs +110 -0
  35. package/dist/loader/index.d.cts +48 -0
  36. package/dist/loader/index.d.cts.map +1 -0
  37. package/dist/loader/index.d.mts +48 -0
  38. package/dist/loader/index.d.mts.map +1 -0
  39. package/dist/loader/index.mjs +110 -0
  40. package/dist/loader/index.mjs.map +1 -0
  41. package/dist/meta/index.cjs +4 -0
  42. package/dist/meta/index.mjs +6 -0
  43. package/dist/meta/kind.cjs +161 -0
  44. package/dist/meta/kind.d.cts +134 -0
  45. package/dist/meta/kind.d.cts.map +1 -0
  46. package/dist/meta/kind.d.mts +134 -0
  47. package/dist/meta/kind.d.mts.map +1 -0
  48. package/dist/meta/kind.mjs +157 -0
  49. package/dist/meta/kind.mjs.map +1 -0
  50. package/dist/meta/path.cjs +116 -0
  51. package/dist/meta/path.d.cts +43 -0
  52. package/dist/meta/path.d.cts.map +1 -0
  53. package/dist/meta/path.d.mts +43 -0
  54. package/dist/meta/path.d.mts.map +1 -0
  55. package/dist/meta/path.mjs +112 -0
  56. package/dist/meta/path.mjs.map +1 -0
  57. package/dist/meta/type.d.cts +96 -0
  58. package/dist/meta/type.d.cts.map +1 -0
  59. package/dist/meta/type.d.mts +96 -0
  60. package/dist/meta/type.d.mts.map +1 -0
  61. package/dist/meta/validation.cjs +77 -0
  62. package/dist/meta/validation.d.cts +19 -0
  63. package/dist/meta/validation.d.cts.map +1 -0
  64. package/dist/meta/validation.d.mts +19 -0
  65. package/dist/meta/validation.d.mts.map +1 -0
  66. package/dist/meta/validation.mjs +77 -0
  67. package/dist/meta/validation.mjs.map +1 -0
  68. package/dist/meta/well-known-kinds.cjs +228 -0
  69. package/dist/meta/well-known-kinds.d.cts +52 -0
  70. package/dist/meta/well-known-kinds.d.cts.map +1 -0
  71. package/dist/meta/well-known-kinds.d.mts +52 -0
  72. package/dist/meta/well-known-kinds.d.mts.map +1 -0
  73. package/dist/meta/well-known-kinds.mjs +219 -0
  74. package/dist/meta/well-known-kinds.mjs.map +1 -0
  75. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.cts +141 -0
  76. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.cts.map +1 -0
  77. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.mts +141 -0
  78. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.mts.map +1 -0
  79. package/dist/path.cjs +255 -0
  80. package/dist/path.d.cts +93 -0
  81. package/dist/path.d.cts.map +1 -0
  82. package/dist/path.d.mts +93 -0
  83. package/dist/path.d.mts.map +1 -0
  84. package/dist/path.mjs +249 -0
  85. package/dist/path.mjs.map +1 -0
  86. package/dist/provider/base.cjs +425 -0
  87. package/dist/provider/base.d.cts +175 -0
  88. package/dist/provider/base.d.cts.map +1 -0
  89. package/dist/provider/base.d.mts +175 -0
  90. package/dist/provider/base.d.mts.map +1 -0
  91. package/dist/provider/base.mjs +426 -0
  92. package/dist/provider/base.mjs.map +1 -0
  93. package/dist/provider/decorators.cjs +268 -0
  94. package/dist/provider/decorators.d.cts +244 -0
  95. package/dist/provider/decorators.d.cts.map +1 -0
  96. package/dist/provider/decorators.d.mts +244 -0
  97. package/dist/provider/decorators.d.mts.map +1 -0
  98. package/dist/provider/decorators.mjs +256 -0
  99. package/dist/provider/decorators.mjs.map +1 -0
  100. package/dist/provider/index.cjs +19 -0
  101. package/dist/provider/index.d.cts +5 -0
  102. package/dist/provider/index.d.mts +5 -0
  103. package/dist/provider/index.mjs +5 -0
  104. package/dist/provider/router.cjs +185 -0
  105. package/dist/provider/router.d.cts +50 -0
  106. package/dist/provider/router.d.cts.map +1 -0
  107. package/dist/provider/router.d.mts +50 -0
  108. package/dist/provider/router.d.mts.map +1 -0
  109. package/dist/provider/router.mjs +185 -0
  110. package/dist/provider/router.mjs.map +1 -0
  111. package/dist/provider/types.d.cts +113 -0
  112. package/dist/provider/types.d.cts.map +1 -0
  113. package/dist/provider/types.d.mts +113 -0
  114. package/dist/provider/types.d.mts.map +1 -0
  115. package/dist/registry.cjs +358 -0
  116. package/dist/registry.d.cts +96 -0
  117. package/dist/registry.d.cts.map +1 -0
  118. package/dist/registry.d.mts +96 -0
  119. package/dist/registry.d.mts.map +1 -0
  120. package/dist/registry.mjs +360 -0
  121. package/dist/registry.mjs.map +1 -0
  122. package/dist/type.cjs +34 -0
  123. package/dist/type.d.cts +420 -0
  124. package/dist/type.d.cts.map +1 -0
  125. package/dist/type.d.mts +420 -0
  126. package/dist/type.d.mts.map +1 -0
  127. package/dist/type.mjs +33 -0
  128. package/dist/type.mjs.map +1 -0
  129. package/dist/utils/camelize.d.cts.map +1 -1
  130. package/dist/utils/camelize.d.mts.map +1 -1
  131. package/dist/utils/schema.cjs +129 -0
  132. package/dist/utils/schema.d.cts +65 -0
  133. package/dist/utils/schema.d.cts.map +1 -0
  134. package/dist/utils/schema.d.mts +65 -0
  135. package/dist/utils/schema.d.mts.map +1 -0
  136. package/dist/utils/schema.mjs +124 -0
  137. package/dist/utils/schema.mjs.map +1 -0
  138. package/dist/utils/type-utils.d.cts.map +1 -1
  139. package/dist/utils/type-utils.d.mts.map +1 -1
  140. package/dist/utils/uri-template.cjs +123 -0
  141. package/dist/utils/uri-template.d.cts +48 -0
  142. package/dist/utils/uri-template.d.cts.map +1 -0
  143. package/dist/utils/uri-template.d.mts +48 -0
  144. package/dist/utils/uri-template.d.mts.map +1 -0
  145. package/dist/utils/uri-template.mjs +120 -0
  146. package/dist/utils/uri-template.mjs.map +1 -0
  147. package/dist/utils/uri.cjs +49 -0
  148. package/dist/utils/uri.d.cts +34 -0
  149. package/dist/utils/uri.d.cts.map +1 -0
  150. package/dist/utils/uri.d.mts +34 -0
  151. package/dist/utils/uri.d.mts.map +1 -0
  152. package/dist/utils/uri.mjs +49 -0
  153. package/dist/utils/uri.mjs.map +1 -0
  154. package/dist/utils/zod.cjs +6 -8
  155. package/dist/utils/zod.d.cts +2 -2
  156. package/dist/utils/zod.d.cts.map +1 -1
  157. package/dist/utils/zod.d.mts +2 -2
  158. package/dist/utils/zod.d.mts.map +1 -1
  159. package/dist/utils/zod.mjs +6 -8
  160. package/dist/utils/zod.mjs.map +1 -1
  161. package/package.json +27 -4
  162. package/dist/index.d.cts.map +0 -1
  163. package/dist/index.d.mts.map +0 -1
  164. package/dist/index.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,343 +1,17 @@
1
- import { v7 } from "@aigne/uuid";
2
- import { Emitter } from "strict-event-emitter";
3
- import { joinURL } from "ufo";
4
- import { z } from "zod";
1
+ import { AFSError, AFSMountError, AFSNotFoundError, AFSReadonlyError, AFSValidationError } from "./error.mjs";
2
+ import { AFSPathError, isCanonicalPath, normalizePath, parseCanonicalPath, toCanonicalPath, validateModuleName, validatePath } from "./path.mjs";
3
+ import { AFS } from "./afs.mjs";
4
+ import { isWorldMappingCapable } from "./capabilities/world-mapping.mjs";
5
+ import { combineValidationResults, validateNodes } from "./meta/validation.mjs";
6
+ import { WELL_KNOWN_KINDS, WELL_KNOWN_KINDS_MAP, afsDocument, afsExecutable, afsImage, afsLink, afsNode, commonMetaSchema, getWellKnownKind, isWellKnownKind } from "./meta/well-known-kinds.mjs";
7
+ import { KindError, createKindResolver, defaultKindResolver, defineKind, getInheritanceChain, resolveKindSchema } from "./meta/kind.mjs";
8
+ import { KINDS_SEGMENT, META_SEGMENT, getNodePathFromMetaPath, isKindsPath, isMetaPath, parseMetaPath } from "./meta/path.mjs";
9
+ import "./meta/index.mjs";
10
+ import { Actions, Delete, Exec, Explain, List, Meta, Read, Rename, Search, Stat, Write, clearRoutes, getRoutes } from "./provider/decorators.mjs";
11
+ import { ProviderRouter } from "./provider/router.mjs";
12
+ import { AFSBaseProvider } from "./provider/base.mjs";
13
+ import "./provider/index.mjs";
14
+ import { ProviderRegistry } from "./registry.mjs";
15
+ import { accessModeSchema, actionSummarySchema, afsEntrySchema } from "./type.mjs";
5
16
 
6
- //#region src/error.ts
7
- /**
8
- * Base error class for all AFS errors.
9
- */
10
- var AFSError = class extends Error {
11
- code;
12
- constructor(message, code) {
13
- super(message);
14
- this.name = "AFSError";
15
- this.code = code;
16
- }
17
- };
18
- /**
19
- * Error thrown when attempting write operations on a readonly AFS or module.
20
- */
21
- var AFSReadonlyError = class extends AFSError {
22
- constructor(message) {
23
- super(message, "AFS_READONLY");
24
- this.name = "AFSReadonlyError";
25
- }
26
- };
27
-
28
- //#endregion
29
- //#region src/type.ts
30
- /**
31
- * Zod schema for access mode validation.
32
- * Can be reused across modules that support access mode configuration.
33
- */
34
- const accessModeSchema = z.enum(["readonly", "readwrite"]).describe("Access mode for this module").optional();
35
- const afsEntrySchema = z.object({
36
- id: z.string(),
37
- createdAt: z.date().optional(),
38
- updatedAt: z.date().optional(),
39
- path: z.string(),
40
- userId: z.string().nullable().optional(),
41
- sessionId: z.string().nullable().optional(),
42
- summary: z.string().nullable().optional(),
43
- description: z.string().nullable().optional(),
44
- metadata: z.record(z.any()).nullable().optional(),
45
- linkTo: z.string().nullable().optional(),
46
- content: z.any().optional()
47
- });
48
-
49
- //#endregion
50
- //#region src/afs.ts
51
- const DEFAULT_MAX_DEPTH = 1;
52
- const MODULES_ROOT_DIR = "/modules";
53
- var AFS = class extends Emitter {
54
- name = "AFSRoot";
55
- constructor(options = {}) {
56
- super();
57
- this.options = options;
58
- for (const module of options?.modules ?? []) this.mount(module);
59
- }
60
- modules = /* @__PURE__ */ new Map();
61
- /**
62
- * Check if write operations are allowed for the given module.
63
- * Throws AFSReadonlyError if not allowed.
64
- */
65
- checkWritePermission(module, operation, path) {
66
- if (module.accessMode !== "readwrite") throw new AFSReadonlyError(`Module '${module.name}' is readonly, cannot perform ${operation} to ${path}`);
67
- }
68
- mount(module) {
69
- if (module.name.includes("/")) throw new Error(`Invalid module name: ${module.name}. Module name must not contain '/'`);
70
- const path = joinURL(MODULES_ROOT_DIR, module.name);
71
- if (this.modules.has(path)) throw new Error(`Module already mounted at path: ${path}`);
72
- this.modules.set(path, module);
73
- module.onMount?.(this);
74
- return this;
75
- }
76
- async listModules() {
77
- return Array.from(this.modules.entries()).map(([path, module]) => ({
78
- path,
79
- name: module.name,
80
- description: module.description,
81
- module
82
- }));
83
- }
84
- async list(path, options = {}) {
85
- let preset;
86
- if (options.preset) {
87
- preset = this.options?.context?.list?.presets?.[options.preset];
88
- if (!preset) throw new Error(`Preset not found: ${options.preset}`);
89
- }
90
- return await this.processWithPreset(path, void 0, preset, {
91
- ...options,
92
- defaultSelect: () => this._list(path, options)
93
- });
94
- }
95
- async _list(path, options = {}) {
96
- const results = [];
97
- if (path === "/" && this.modules.size > 0) {
98
- const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
99
- results.push({
100
- id: "modules",
101
- path: MODULES_ROOT_DIR,
102
- summary: "All mounted modules"
103
- });
104
- if (maxDepth === 1) return { data: results };
105
- const childrenResult = await this._list(MODULES_ROOT_DIR, {
106
- ...options,
107
- maxDepth: maxDepth - 1
108
- });
109
- results.push(...childrenResult.data);
110
- return { data: results };
111
- }
112
- const matches = this.findModules(path, options);
113
- for (const matched of matches) {
114
- if (matched.maxDepth === 0) {
115
- const moduleEntry = {
116
- id: matched.module.name,
117
- path: matched.modulePath,
118
- summary: matched.module.description
119
- };
120
- results.push(moduleEntry);
121
- continue;
122
- }
123
- if (!matched.module.list) continue;
124
- try {
125
- const { data } = await matched.module.list(matched.subpath, {
126
- ...options,
127
- maxDepth: matched.maxDepth
128
- });
129
- const children = data.map((entry) => ({
130
- ...entry,
131
- path: joinURL(matched.modulePath, entry.path)
132
- }));
133
- results.push(...children);
134
- } catch (error) {
135
- throw new Error(`Error listing from module at ${matched.modulePath}: ${error.message}`);
136
- }
137
- }
138
- return { data: results };
139
- }
140
- async read(path, _options) {
141
- const modules = this.findModules(path, { exactMatch: true });
142
- for (const { module, modulePath, subpath } of modules) {
143
- const res = await module.read?.(subpath);
144
- if (res?.data) return {
145
- ...res,
146
- data: {
147
- ...res.data,
148
- path: joinURL(modulePath, res.data.path)
149
- }
150
- };
151
- }
152
- return {
153
- data: void 0,
154
- message: "File not found"
155
- };
156
- }
157
- async write(path, content, options) {
158
- const module = this.findModules(path, { exactMatch: true })[0];
159
- if (!module?.module.write) throw new Error(`No module found for path: ${path}`);
160
- this.checkWritePermission(module.module, "write", path);
161
- const res = await module.module.write(module.subpath, content, options);
162
- return {
163
- ...res,
164
- data: {
165
- ...res.data,
166
- path: joinURL(module.modulePath, res.data.path)
167
- }
168
- };
169
- }
170
- async delete(path, options) {
171
- const module = this.findModules(path, { exactMatch: true })[0];
172
- if (!module?.module.delete) throw new Error(`No module found for path: ${path}`);
173
- this.checkWritePermission(module.module, "delete", path);
174
- return await module.module.delete(module.subpath, options);
175
- }
176
- async rename(oldPath, newPath, options) {
177
- const oldModule = this.findModules(oldPath, { exactMatch: true })[0];
178
- const newModule = this.findModules(newPath, { exactMatch: true })[0];
179
- if (!oldModule || !newModule || oldModule.modulePath !== newModule.modulePath) throw new Error(`Cannot rename across different modules. Both paths must be in the same module.`);
180
- if (!oldModule.module.rename) throw new Error(`Module does not support rename operation: ${oldModule.modulePath}`);
181
- this.checkWritePermission(oldModule.module, "rename", oldPath);
182
- return await oldModule.module.rename(oldModule.subpath, newModule.subpath, options);
183
- }
184
- async search(path, query, options = {}) {
185
- let preset;
186
- if (options.preset) {
187
- preset = this.options?.context?.search?.presets?.[options.preset];
188
- if (!preset) throw new Error(`Preset not found: ${options.preset}`);
189
- }
190
- return await this.processWithPreset(path, query, preset, {
191
- ...options,
192
- defaultSelect: () => this._search(path, query, options)
193
- });
194
- }
195
- async processWithPreset(path, query, preset, options) {
196
- const select = options.select || preset?.select;
197
- const per = options.per || preset?.per;
198
- const dedupe = options.dedupe || preset?.dedupe;
199
- const format = options.format || preset?.format;
200
- const entries = select ? (await this._select(path, query, select, options)).data : (await options.defaultSelect()).data;
201
- const mapped = per ? await Promise.all(entries.map((data) => per.invoke({ data }, options).then((res) => res.data))) : entries;
202
- const deduped = dedupe ? await dedupe.invoke({ data: mapped }, options).then((res) => res.data) : mapped;
203
- let formatted = deduped;
204
- if (format === "simple-list" || format === "tree") {
205
- const valid = z.array(afsEntrySchema).safeParse(deduped);
206
- if (!valid.data) throw new Error("Tree format requires entries to be AFSEntry objects");
207
- if (format === "tree") formatted = this.buildTreeView(valid.data);
208
- else if (format === "simple-list") formatted = this.buildSimpleListView(valid.data);
209
- } else if (typeof format === "object" && typeof format.invoke === "function") formatted = await format.invoke({ data: deduped }, options).then((res) => res.data);
210
- return { data: formatted };
211
- }
212
- async _select(path, query, select, options) {
213
- const { data } = await select.invoke({
214
- path,
215
- query
216
- }, options);
217
- return { data: (await Promise.all(data.map((p) => this.read(p).then((res) => res.data)))).filter((i) => !!i) };
218
- }
219
- async _search(path, query, options) {
220
- const results = [];
221
- const messages = [];
222
- for (const { module, modulePath, subpath } of this.findModules(path)) {
223
- if (!module.search) continue;
224
- try {
225
- const { data, message } = await module.search(subpath, query, options);
226
- results.push(...data.map((entry) => ({
227
- ...entry,
228
- path: joinURL(modulePath, entry.path)
229
- })));
230
- if (message) messages.push(message);
231
- } catch (error) {
232
- throw new Error(`Error searching in module at ${modulePath}: ${error.message}`);
233
- }
234
- }
235
- return {
236
- data: results,
237
- message: messages.join("; ")
238
- };
239
- }
240
- findModules(path, options) {
241
- const maxDepth = Math.max(options?.maxDepth ?? DEFAULT_MAX_DEPTH, 1);
242
- const matched = [];
243
- for (const [modulePath, module] of this.modules) {
244
- const pathSegments = path.split("/").filter(Boolean);
245
- const modulePathSegments = modulePath.split("/").filter(Boolean);
246
- let newMaxDepth;
247
- let subpath;
248
- let remainedModulePath;
249
- if (!options?.exactMatch && modulePath.startsWith(path)) {
250
- newMaxDepth = Math.max(0, maxDepth - (modulePathSegments.length - pathSegments.length));
251
- subpath = "/";
252
- remainedModulePath = joinURL("/", ...modulePathSegments.slice(pathSegments.length).slice(0, maxDepth));
253
- } else if (path.startsWith(modulePath)) {
254
- newMaxDepth = maxDepth;
255
- subpath = joinURL("/", ...pathSegments.slice(modulePathSegments.length));
256
- remainedModulePath = "/";
257
- } else continue;
258
- if (newMaxDepth < 0) continue;
259
- matched.push({
260
- module,
261
- modulePath,
262
- maxDepth: newMaxDepth,
263
- subpath,
264
- remainedModulePath
265
- });
266
- }
267
- return matched;
268
- }
269
- async exec(path, args, options) {
270
- const module = this.findModules(path)[0];
271
- if (!module?.module.exec) throw new Error(`No module found for path: ${path}`);
272
- return await module.module.exec(module.subpath, args, options);
273
- }
274
- buildSimpleListView(entries) {
275
- return entries.map((entry) => `${entry.path}${this.buildMetadataSuffix(entry)}`);
276
- }
277
- buildTreeView(entries) {
278
- const tree = {};
279
- const entryMap = /* @__PURE__ */ new Map();
280
- for (const entry of entries) {
281
- entryMap.set(entry.path, entry);
282
- const parts = entry.path.split("/").filter(Boolean);
283
- let current = tree;
284
- for (const part of parts) {
285
- if (!current[part]) current[part] = {};
286
- current = current[part];
287
- }
288
- }
289
- const renderTree = (node, prefix = "", currentPath = "") => {
290
- let result = "";
291
- const keys = Object.keys(node);
292
- keys.forEach((key, index) => {
293
- const isLast = index === keys.length - 1;
294
- const fullPath = currentPath ? `${currentPath}/${key}` : `/${key}`;
295
- const entry = entryMap.get(fullPath);
296
- result += `${prefix}${isLast ? "└── " : "├── "}${key}${entry ? this.buildMetadataSuffix(entry) : ""}`;
297
- result += `\n`;
298
- result += renderTree(node[key], `${prefix}${isLast ? " " : "│ "}`, fullPath);
299
- });
300
- return result;
301
- };
302
- return renderTree(tree);
303
- }
304
- buildMetadataSuffix(entry) {
305
- const metadataParts = [];
306
- const childrenCount = entry?.metadata?.childrenCount;
307
- if (typeof childrenCount === "number") metadataParts.push(`${childrenCount} items`);
308
- if (entry?.metadata?.childrenTruncated) metadataParts.push("truncated");
309
- if (entry?.metadata?.gitignored) metadataParts.push("gitignored");
310
- if (entry?.metadata?.execute) metadataParts.push("executable");
311
- return metadataParts.length > 0 ? ` [${metadataParts.join(", ")}]` : "";
312
- }
313
- physicalPath;
314
- async initializePhysicalPath() {
315
- this.physicalPath ??= (async () => {
316
- const path = await import("node:path");
317
- const os = await import("node:os");
318
- const fs = await import("node:fs/promises");
319
- const rootDir = path.join(os.tmpdir(), v7());
320
- await fs.mkdir(rootDir, { recursive: true });
321
- for (const [modulePath, module] of this.modules) {
322
- const physicalModulePath = path.join(rootDir, modulePath);
323
- await fs.mkdir(path.dirname(physicalModulePath), { recursive: true });
324
- await module.symlinkToPhysical?.(physicalModulePath);
325
- }
326
- return rootDir;
327
- })();
328
- return this.physicalPath;
329
- }
330
- async cleanupPhysicalPath() {
331
- if (this.physicalPath) {
332
- await (await import("node:fs/promises")).rm(await this.physicalPath, {
333
- recursive: true,
334
- force: true
335
- });
336
- this.physicalPath = void 0;
337
- }
338
- }
339
- };
340
-
341
- //#endregion
342
- export { AFS, AFSError, AFSReadonlyError, accessModeSchema, afsEntrySchema };
343
- //# sourceMappingURL=index.mjs.map
17
+ export { AFS, AFSBaseProvider, AFSError, AFSMountError, AFSNotFoundError, AFSPathError, AFSReadonlyError, AFSValidationError, Actions, Delete, Exec, Explain, KINDS_SEGMENT, KindError, List, META_SEGMENT, Meta, ProviderRegistry, ProviderRouter, Read, Rename, Search, Stat, WELL_KNOWN_KINDS, WELL_KNOWN_KINDS_MAP, Write, accessModeSchema, actionSummarySchema, afsDocument, afsEntrySchema, afsExecutable, afsImage, afsLink, afsNode, clearRoutes, combineValidationResults, commonMetaSchema, createKindResolver, defaultKindResolver, defineKind, getInheritanceChain, getNodePathFromMetaPath, getRoutes, getWellKnownKind, isCanonicalPath, isKindsPath, isMetaPath, isWellKnownKind, isWorldMappingCapable, normalizePath, parseCanonicalPath, parseMetaPath, resolveKindSchema, toCanonicalPath, validateModuleName, validateNodes, validatePath };
@@ -0,0 +1,110 @@
1
+ let node_fs = require("node:fs");
2
+ let node_path = require("node:path");
3
+
4
+ //#region src/loader/index.ts
5
+ /**
6
+ * AFS Provider Dynamic Loader
7
+ *
8
+ * Provides utilities for dynamically loading AFS providers at runtime.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { loadProvider, getProviderSchema } from "@aigne/afs/loader";
13
+ *
14
+ * // Load a provider with configuration
15
+ * const fs = await loadProvider("@aigne/afs-fs", {
16
+ * config: { localPath: "./data" },
17
+ * basePath: "/path/to/config/dir"
18
+ * });
19
+ *
20
+ * // Get provider schema for validation
21
+ * const schema = await getProviderSchema("@aigne/afs-fs");
22
+ * ```
23
+ */
24
+ /**
25
+ * Resolve a package specifier to an importable path.
26
+ * If the specifier is a directory path (e.g. from npm install),
27
+ * read its package.json to find the ESM entry point.
28
+ * We must use the ESM (.mjs) entry, not CJS (.cjs), because
29
+ * importing CJS from ESM wraps module.exports in an extra default layer
30
+ * which breaks the expected export shape.
31
+ */
32
+ function resolveImportPath(specifier) {
33
+ if (!specifier.startsWith("/")) return specifier;
34
+ const pkgJsonPath = (0, node_path.join)(specifier, "package.json");
35
+ if (!(0, node_fs.existsSync)(pkgJsonPath)) return specifier;
36
+ try {
37
+ const pkg = JSON.parse((0, node_fs.readFileSync)(pkgJsonPath, "utf-8"));
38
+ const esmEntry = typeof pkg.exports === "object" && pkg.exports["."]?.import || pkg.module;
39
+ if (esmEntry) return (0, node_path.join)(specifier, esmEntry);
40
+ if (pkg.main) return (0, node_path.join)(specifier, pkg.main);
41
+ } catch {}
42
+ return specifier;
43
+ }
44
+ /**
45
+ * Dynamically load an AFS Provider from a package.
46
+ *
47
+ * @param packageName - Package name or subpath (e.g., "@aigne/afs-fs" or "@aigne/afs-cloud/s3")
48
+ * @param options - Load options (optional)
49
+ * @returns Provider instance
50
+ *
51
+ * @throws Error if package doesn't exist or doesn't export a valid AFS Provider
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const fs = await loadProvider("@aigne/afs-fs", {
56
+ * config: { localPath: "./data" },
57
+ * basePath: "/path/to/config/dir"
58
+ * });
59
+ * ```
60
+ */
61
+ async function loadProvider(packageName, options) {
62
+ const importPath = resolveImportPath(packageName);
63
+ let module;
64
+ try {
65
+ module = await import(importPath);
66
+ } catch (error) {
67
+ throw new Error(`Failed to import package "${packageName}": ${error instanceof Error ? error.message : String(error)}`);
68
+ }
69
+ const ProviderClass = module.default;
70
+ if (!ProviderClass || typeof ProviderClass.load !== "function") throw new Error(`Package "${packageName}" does not export a valid AFS Provider. Expected default export with static load() method.`);
71
+ return ProviderClass.load(options ?? {});
72
+ }
73
+ /**
74
+ * Get the configuration schema for an AFS Provider without creating an instance.
75
+ *
76
+ * Useful for:
77
+ * - Configuration validation
78
+ * - Documentation generation
79
+ * - IDE integration
80
+ *
81
+ * @param packageName - Package name or subpath
82
+ * @returns Zod schema for the provider's configuration
83
+ *
84
+ * @throws Error if package doesn't export schema() method
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const schema = await getProviderSchema("@aigne/afs-fs");
89
+ * const result = schema.safeParse(config);
90
+ * if (!result.success) {
91
+ * console.error(result.error);
92
+ * }
93
+ * ```
94
+ */
95
+ async function getProviderSchema(packageName) {
96
+ const importPath = resolveImportPath(packageName);
97
+ let module;
98
+ try {
99
+ module = await import(importPath);
100
+ } catch (error) {
101
+ throw new Error(`Failed to import package "${packageName}": ${error instanceof Error ? error.message : String(error)}`);
102
+ }
103
+ const ProviderClass = module.default;
104
+ if (!ProviderClass || typeof ProviderClass.schema !== "function") throw new Error(`Package "${packageName}" does not export schema() method.`);
105
+ return ProviderClass.schema();
106
+ }
107
+
108
+ //#endregion
109
+ exports.getProviderSchema = getProviderSchema;
110
+ exports.loadProvider = loadProvider;
@@ -0,0 +1,48 @@
1
+ import { AFSModule, AFSModuleLoadParams } from "../type.cjs";
2
+ import { ZodType } from "zod";
3
+
4
+ //#region src/loader/index.d.ts
5
+ /**
6
+ * Dynamically load an AFS Provider from a package.
7
+ *
8
+ * @param packageName - Package name or subpath (e.g., "@aigne/afs-fs" or "@aigne/afs-cloud/s3")
9
+ * @param options - Load options (optional)
10
+ * @returns Provider instance
11
+ *
12
+ * @throws Error if package doesn't exist or doesn't export a valid AFS Provider
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const fs = await loadProvider("@aigne/afs-fs", {
17
+ * config: { localPath: "./data" },
18
+ * basePath: "/path/to/config/dir"
19
+ * });
20
+ * ```
21
+ */
22
+ declare function loadProvider<T extends AFSModule = AFSModule>(packageName: string, options?: AFSModuleLoadParams): Promise<T>;
23
+ /**
24
+ * Get the configuration schema for an AFS Provider without creating an instance.
25
+ *
26
+ * Useful for:
27
+ * - Configuration validation
28
+ * - Documentation generation
29
+ * - IDE integration
30
+ *
31
+ * @param packageName - Package name or subpath
32
+ * @returns Zod schema for the provider's configuration
33
+ *
34
+ * @throws Error if package doesn't export schema() method
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const schema = await getProviderSchema("@aigne/afs-fs");
39
+ * const result = schema.safeParse(config);
40
+ * if (!result.success) {
41
+ * console.error(result.error);
42
+ * }
43
+ * ```
44
+ */
45
+ declare function getProviderSchema(packageName: string): Promise<ZodType<unknown>>;
46
+ //#endregion
47
+ export { type AFSModuleLoadParams, getProviderSchema, loadProvider };
48
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/loader/index.ts"],"mappings":";;;;;;AA6HA;;;;;;;;;;;;;;;iBArDsB,YAAA,WAAuB,SAAA,GAAY,SAAA,CAAA,CACvD,WAAA,UACA,OAAA,GAAU,mBAAA,GACT,OAAA,CAAQ,CAAA;;;;;;;;;;;;;;;;;;;;;;;iBAkDW,iBAAA,CAAkB,WAAA,WAAsB,OAAA,CAAQ,OAAA"}
@@ -0,0 +1,48 @@
1
+ import { AFSModule, AFSModuleLoadParams } from "../type.mjs";
2
+ import { ZodType } from "zod";
3
+
4
+ //#region src/loader/index.d.ts
5
+ /**
6
+ * Dynamically load an AFS Provider from a package.
7
+ *
8
+ * @param packageName - Package name or subpath (e.g., "@aigne/afs-fs" or "@aigne/afs-cloud/s3")
9
+ * @param options - Load options (optional)
10
+ * @returns Provider instance
11
+ *
12
+ * @throws Error if package doesn't exist or doesn't export a valid AFS Provider
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const fs = await loadProvider("@aigne/afs-fs", {
17
+ * config: { localPath: "./data" },
18
+ * basePath: "/path/to/config/dir"
19
+ * });
20
+ * ```
21
+ */
22
+ declare function loadProvider<T extends AFSModule = AFSModule>(packageName: string, options?: AFSModuleLoadParams): Promise<T>;
23
+ /**
24
+ * Get the configuration schema for an AFS Provider without creating an instance.
25
+ *
26
+ * Useful for:
27
+ * - Configuration validation
28
+ * - Documentation generation
29
+ * - IDE integration
30
+ *
31
+ * @param packageName - Package name or subpath
32
+ * @returns Zod schema for the provider's configuration
33
+ *
34
+ * @throws Error if package doesn't export schema() method
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const schema = await getProviderSchema("@aigne/afs-fs");
39
+ * const result = schema.safeParse(config);
40
+ * if (!result.success) {
41
+ * console.error(result.error);
42
+ * }
43
+ * ```
44
+ */
45
+ declare function getProviderSchema(packageName: string): Promise<ZodType<unknown>>;
46
+ //#endregion
47
+ export { type AFSModuleLoadParams, getProviderSchema, loadProvider };
48
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/loader/index.ts"],"mappings":";;;;;;AA6HA;;;;;;;;;;;;;;;iBArDsB,YAAA,WAAuB,SAAA,GAAY,SAAA,CAAA,CACvD,WAAA,UACA,OAAA,GAAU,mBAAA,GACT,OAAA,CAAQ,CAAA;;;;;;;;;;;;;;;;;;;;;;;iBAkDW,iBAAA,CAAkB,WAAA,WAAsB,OAAA,CAAQ,OAAA"}
@@ -0,0 +1,110 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ //#region src/loader/index.ts
5
+ /**
6
+ * AFS Provider Dynamic Loader
7
+ *
8
+ * Provides utilities for dynamically loading AFS providers at runtime.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { loadProvider, getProviderSchema } from "@aigne/afs/loader";
13
+ *
14
+ * // Load a provider with configuration
15
+ * const fs = await loadProvider("@aigne/afs-fs", {
16
+ * config: { localPath: "./data" },
17
+ * basePath: "/path/to/config/dir"
18
+ * });
19
+ *
20
+ * // Get provider schema for validation
21
+ * const schema = await getProviderSchema("@aigne/afs-fs");
22
+ * ```
23
+ */
24
+ /**
25
+ * Resolve a package specifier to an importable path.
26
+ * If the specifier is a directory path (e.g. from npm install),
27
+ * read its package.json to find the ESM entry point.
28
+ * We must use the ESM (.mjs) entry, not CJS (.cjs), because
29
+ * importing CJS from ESM wraps module.exports in an extra default layer
30
+ * which breaks the expected export shape.
31
+ */
32
+ function resolveImportPath(specifier) {
33
+ if (!specifier.startsWith("/")) return specifier;
34
+ const pkgJsonPath = join(specifier, "package.json");
35
+ if (!existsSync(pkgJsonPath)) return specifier;
36
+ try {
37
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
38
+ const esmEntry = typeof pkg.exports === "object" && pkg.exports["."]?.import || pkg.module;
39
+ if (esmEntry) return join(specifier, esmEntry);
40
+ if (pkg.main) return join(specifier, pkg.main);
41
+ } catch {}
42
+ return specifier;
43
+ }
44
+ /**
45
+ * Dynamically load an AFS Provider from a package.
46
+ *
47
+ * @param packageName - Package name or subpath (e.g., "@aigne/afs-fs" or "@aigne/afs-cloud/s3")
48
+ * @param options - Load options (optional)
49
+ * @returns Provider instance
50
+ *
51
+ * @throws Error if package doesn't exist or doesn't export a valid AFS Provider
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const fs = await loadProvider("@aigne/afs-fs", {
56
+ * config: { localPath: "./data" },
57
+ * basePath: "/path/to/config/dir"
58
+ * });
59
+ * ```
60
+ */
61
+ async function loadProvider(packageName, options) {
62
+ const importPath = resolveImportPath(packageName);
63
+ let module;
64
+ try {
65
+ module = await import(importPath);
66
+ } catch (error) {
67
+ throw new Error(`Failed to import package "${packageName}": ${error instanceof Error ? error.message : String(error)}`);
68
+ }
69
+ const ProviderClass = module.default;
70
+ if (!ProviderClass || typeof ProviderClass.load !== "function") throw new Error(`Package "${packageName}" does not export a valid AFS Provider. Expected default export with static load() method.`);
71
+ return ProviderClass.load(options ?? {});
72
+ }
73
+ /**
74
+ * Get the configuration schema for an AFS Provider without creating an instance.
75
+ *
76
+ * Useful for:
77
+ * - Configuration validation
78
+ * - Documentation generation
79
+ * - IDE integration
80
+ *
81
+ * @param packageName - Package name or subpath
82
+ * @returns Zod schema for the provider's configuration
83
+ *
84
+ * @throws Error if package doesn't export schema() method
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const schema = await getProviderSchema("@aigne/afs-fs");
89
+ * const result = schema.safeParse(config);
90
+ * if (!result.success) {
91
+ * console.error(result.error);
92
+ * }
93
+ * ```
94
+ */
95
+ async function getProviderSchema(packageName) {
96
+ const importPath = resolveImportPath(packageName);
97
+ let module;
98
+ try {
99
+ module = await import(importPath);
100
+ } catch (error) {
101
+ throw new Error(`Failed to import package "${packageName}": ${error instanceof Error ? error.message : String(error)}`);
102
+ }
103
+ const ProviderClass = module.default;
104
+ if (!ProviderClass || typeof ProviderClass.schema !== "function") throw new Error(`Package "${packageName}" does not export schema() method.`);
105
+ return ProviderClass.schema();
106
+ }
107
+
108
+ //#endregion
109
+ export { getProviderSchema, loadProvider };
110
+ //# sourceMappingURL=index.mjs.map