@aigne/afs-json 1.11.0-beta.9 → 1.12.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
@@ -304,3 +304,35 @@ Search for values within the JSON/YAML structure.
304
304
  - **Kubernetes Config**: Navigate and query YAML configuration files
305
305
  - **CI/CD Pipelines**: Access and modify YAML pipeline configurations
306
306
 
307
+
308
+ ## 选 provider 的判据:file-as-blob vs file-as-tree
309
+
310
+ - **file-as-blob**(用 `AFSFS`):内容是不透明字节流——README.md、二进制文件、用户上传文件等
311
+ - **file-as-tree**(用 `AFSJSON` / `AFSToml` / `AFSYaml`):内容是结构化数据,需要 sub-path 读写
312
+ 单个字段。settings、config、provider 描述符等。
313
+
314
+ 判断口诀:**写一个字段时另一个字段会变吗?** 会 = 选 blob;不会 = 选 tree。
315
+ Wrapper-format settings 永远是 tree。
316
+
317
+ ## Structured settings MUST mount via AFSJSON sub-path overlay
318
+
319
+ 任何 wrapper-format settings 文件(`{ label, description, value, ... }` 结构)**必须**通过 AFSJSON
320
+ overlay mount 在自身路径上,让 bind 指向内层 sub-path(例如 `/<file>.json/value`),而不是直接写整个
321
+ wrapper 文件。理由:
322
+
323
+ - 直接写 wrapper 文件会丢失 sibling 字段(label / description)。这是 G0 回归 bug。
324
+ - AFSJSON overlay 让每个 `{label, description, value}` 字段成为可读写的 primitive sub-path。
325
+ 写 `/value` 只更新该字段,框架负责把整个 wrapper 重新序列化回磁盘——其他字段字节级保留。
326
+ - `buildFlatSubMounts` 已自动检测 wrapper 结构并挂 overlay;新写代码 **不要**绕过它直接写
327
+ wrapper 文件路径。
328
+
329
+ 参考:`runtimes/node/src/program/structured-settings-overlay.ts`、回归 sentinel
330
+ `providers/runtime/ui/test/persistence-sentinel.test.ts`、约定全文
331
+ `providers/runtime/ui/docs/settings-persistence.md`。
332
+
333
+ ## Nested mount 用 `allowOverlay: true`
334
+
335
+ 在已经被一个 provider 覆盖的子路径下叠 overlay 时(典型场景:FS 已 mount `/instance` 后,要在
336
+ `/instance/.../foo.json` 上叠 AFSJSON),mount 调用必须显式传 `{ allowOverlay: true }`,否则
337
+ 框架抛 `Mount conflict` 拒绝注册。验证要点:list 不重复、read 路由到 overlay、unmount
338
+ 后回退到底层 provider。参考 `providers/core/json/test/overlay.test.ts`。
package/dist/index.cjs CHANGED
@@ -1,23 +1,36 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
  const require_decorate = require('./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs');
3
- let node_fs = require("node:fs");
4
- let node_fs_promises = require("node:fs/promises");
5
- let node_path = require("node:path");
3
+ let node_crypto = require("node:crypto");
6
4
  let _aigne_afs = require("@aigne/afs");
7
5
  let _aigne_afs_provider = require("@aigne/afs/provider");
8
6
  let _aigne_afs_utils_zod = require("@aigne/afs/utils/zod");
7
+ let _aigne_afs_provider_utils = require("@aigne/afs-provider-utils");
8
+ let js_yaml = require("js-yaml");
9
9
  let ufo = require("ufo");
10
- let yaml = require("yaml");
11
10
  let zod = require("zod");
12
11
 
13
12
  //#region src/index.ts
14
13
  const LIST_MAX_LIMIT = 1e3;
14
+ const JSON_AS_FORMATS = [
15
+ "content",
16
+ "compact",
17
+ "tree"
18
+ ];
15
19
  /** Hidden key for storing AFS metadata (mirrors FS provider's .afs directory) */
16
20
  const AFS_KEY = ".afs";
17
21
  /** Subkey for storing metadata (mirrors FS provider's meta.yaml file) */
18
22
  const META_KEY = "meta";
19
23
  /** Subkey for storing child node metadata (mirrors FS provider's .nodes directory) */
20
24
  const NODES_KEY = ".nodes";
25
+ function assertLocalPathWithinCwd(resolvedPath, cwd, rawPath) {
26
+ const { path } = (0, _aigne_afs.getPlatform)();
27
+ const root = path.resolve(cwd);
28
+ const candidate = path.resolve(resolvedPath);
29
+ const rootPrefix = root.endsWith("/") || root.endsWith("\\") ? root : `${root}/`;
30
+ const windowsRootPrefix = root.endsWith("\\") ? root : `${root}\\`;
31
+ if (candidate !== root && !candidate.startsWith(rootPrefix) && !candidate.startsWith(windowsRootPrefix)) throw new Error(`Local path escapes configured base directory: ${rawPath}`);
32
+ return candidate;
33
+ }
21
34
  const afsJSONOptionsSchema = (0, _aigne_afs_utils_zod.camelize)(zod.z.object({
22
35
  name: (0, _aigne_afs_utils_zod.optionalize)(zod.z.string()),
23
36
  jsonPath: zod.z.string().describe("The path to the JSON/YAML file to mount"),
@@ -38,7 +51,7 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
38
51
  static manifest() {
39
52
  return {
40
53
  name: "json",
41
- description: "Mount a JSON or YAML file as a virtual filesystem",
54
+ description: "JSON or YAML file — navigate and edit structured data as a tree.\n- Objects and arrays become directories, primitives become leaf nodes\n- Read/write individual values, search across structure\n- Path structure: `/{key}/{nested-key}` (arrays indexed by position)",
42
55
  uriTemplate: "json://{localPath+}",
43
56
  category: "structured-data",
44
57
  schema: zod.z.object({ localPath: zod.z.string() }),
@@ -46,12 +59,56 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
46
59
  "json",
47
60
  "yaml",
48
61
  "structured-data"
49
- ]
62
+ ],
63
+ capabilityTags: [
64
+ "read-write",
65
+ "crud",
66
+ "search",
67
+ "auth:none",
68
+ "local"
69
+ ],
70
+ security: {
71
+ riskLevel: "sandboxed",
72
+ resourceAccess: []
73
+ },
74
+ capabilities: { filesystem: {
75
+ read: true,
76
+ write: true
77
+ } }
78
+ };
79
+ }
80
+ static treeSchema() {
81
+ return {
82
+ operations: [
83
+ "list",
84
+ "read",
85
+ "write",
86
+ "delete",
87
+ "search",
88
+ "stat",
89
+ "explain"
90
+ ],
91
+ tree: {
92
+ "/": { kind: "json:root" },
93
+ "/{key}": { kind: "json:value" }
94
+ },
95
+ auth: { type: "none" },
96
+ bestFor: [
97
+ "JSON/YAML navigation",
98
+ "structured data editing",
99
+ "config files"
100
+ ],
101
+ notFor: ["binary files", "large datasets"]
50
102
  };
51
103
  }
52
104
  static async load({ basePath, config } = {}) {
105
+ const raw = config;
106
+ const normalized = raw?.localPath && !raw.jsonPath ? {
107
+ ...raw,
108
+ jsonPath: raw.localPath
109
+ } : raw;
53
110
  return new AFSJSON({
54
- ...await AFSJSON.schema().parseAsync(config),
111
+ ...await AFSJSON.schema().parseAsync(normalized),
55
112
  cwd: basePath
56
113
  });
57
114
  }
@@ -68,22 +125,17 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
68
125
  this.options = options;
69
126
  if (options.localPath && !options.jsonPath) options.jsonPath = options.localPath;
70
127
  (0, _aigne_afs_utils_zod.zodParse)(afsJSONOptionsSchema, options);
71
- let jsonPath;
72
- jsonPath = options.jsonPath.replaceAll("${CWD}", process.cwd());
73
- if (jsonPath.startsWith("~/")) jsonPath = (0, node_path.join)(process.env.HOME || "", jsonPath.slice(2));
74
- if (!(0, node_path.isAbsolute)(jsonPath)) jsonPath = (0, node_path.join)(options.cwd || process.cwd(), jsonPath);
75
- if (!(0, node_fs.existsSync)(jsonPath)) {
76
- (0, node_fs.mkdirSync)((0, node_path.dirname)(jsonPath), { recursive: true });
77
- (0, node_fs.writeFileSync)(jsonPath, "{}", "utf8");
78
- }
79
- const ext = (0, node_path.extname)(jsonPath).toLowerCase();
128
+ let jsonPath = (0, _aigne_afs_provider_utils.resolveLocalPath)(options.jsonPath, { cwd: options.cwd });
129
+ if (options.cwd) jsonPath = assertLocalPathWithinCwd(jsonPath, options.cwd, options.jsonPath);
130
+ const platform = (0, _aigne_afs.getPlatform)();
131
+ const ext = platform.path.extname(jsonPath).toLowerCase();
80
132
  this.fileFormat = ext === ".yaml" || ext === ".yml" ? "yaml" : "json";
81
133
  const extensions = [
82
134
  ".json",
83
135
  ".yaml",
84
136
  ".yml"
85
137
  ];
86
- let name = (0, node_path.basename)(jsonPath);
138
+ let name = platform.path.basename(jsonPath);
87
139
  for (const e of extensions) if (name.endsWith(e)) {
88
140
  name = name.slice(0, -e.length);
89
141
  break;
@@ -94,6 +146,12 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
94
146
  this.accessMode = options.accessMode ?? (options.agentSkills ? "readonly" : "readwrite");
95
147
  this.resolvedJsonPath = jsonPath;
96
148
  }
149
+ async supportedAs(path) {
150
+ await this.ensureLoaded();
151
+ const normalizedPath = this.normalizePath(path);
152
+ const segments = this.getPathSegments(normalizedPath);
153
+ return this.getValueAtPath(this.jsonData, segments) === void 0 ? [] : [...JSON_AS_FORMATS];
154
+ }
97
155
  /**
98
156
  * Read metadata for a JSON node via /.meta or /path/.meta
99
157
  * Returns stored metadata merged with computed type information
@@ -145,7 +203,17 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
145
203
  if (!this.isDirectoryValue(value)) return { data: [] };
146
204
  const entries = [];
147
205
  const rootChildren = this.getChildren(value);
148
- const queue = (rootChildren.length > maxChildren ? rootChildren.slice(0, maxChildren) : rootChildren).map((child) => ({
206
+ const rootChildrenToProcess = rootChildren.length > maxChildren ? rootChildren.slice(0, maxChildren) : rootChildren;
207
+ const orderBy = ctx.options?.orderBy;
208
+ if (maxDepth === 1 && (0, _aigne_afs.orderByRequested)(orderBy)) {
209
+ const { entries: ordered, applied } = (0, _aigne_afs.orderEntries)(rootChildrenToProcess.map((child) => this.valueToAFSEntry(normalizedPath === "/" ? `/${child.key}` : `${normalizedPath}/${child.key}`, child.value)), orderBy);
210
+ const data = ordered.slice(0, limit);
211
+ return applied ? { data } : {
212
+ data,
213
+ meta: { orderByIgnored: true }
214
+ };
215
+ }
216
+ const queue = rootChildrenToProcess.map((child) => ({
149
217
  path: normalizedPath === "/" ? `/${child.key}` : `${normalizedPath}/${child.key}`,
150
218
  value: child.value,
151
219
  depth: 1
@@ -170,14 +238,37 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
170
238
  }
171
239
  }
172
240
  }
241
+ if ((0, _aigne_afs.orderByRequested)(orderBy)) return {
242
+ data: entries,
243
+ meta: { orderByIgnored: true }
244
+ };
173
245
  return { data: entries };
174
246
  }
247
+ /**
248
+ * AFSJSON handles `handleDepth` itself, so it doesn't get the base.ts
249
+ * orderBy fallback — it implements ordering natively in listHandler and
250
+ * declares the sub-capability here (consumed by both base.list's
251
+ * no-silent-drop check and the /.meta/.capabilities manifest).
252
+ */
253
+ getOperationsDeclaration() {
254
+ const ops = super.getOperationsDeclaration();
255
+ if (typeof ops.list === "boolean" ? ops.list : ops.list?.supported === true) ops.list = {
256
+ supported: true,
257
+ features: { orderBy: true }
258
+ };
259
+ return ops;
260
+ }
175
261
  async readHandler(ctx) {
176
262
  await this.ensureLoaded();
177
263
  const normalizedPath = ctx.params.path ? `/${ctx.params.path}` : "/";
178
264
  const segments = this.getPathSegments(normalizedPath);
179
265
  const value = this.getValueAtPath(this.jsonData, segments);
180
266
  if (value === void 0) throw new _aigne_afs.AFSNotFoundError(normalizedPath);
267
+ const as = ctx.options?.as;
268
+ if (as) {
269
+ if (!this.isJsonAsFormat(as)) throw new _aigne_afs.AFSNotFoundError((0, ufo.joinURL)(normalizedPath, ".as", as));
270
+ return this.buildJsonAsEntry(normalizedPath, value, as);
271
+ }
181
272
  return this.valueToAFSEntry(normalizedPath, value);
182
273
  }
183
274
  /**
@@ -193,6 +284,15 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
193
284
  await this.ensureLoaded();
194
285
  const normalizedPath = ctx.params.path ? `/${ctx.params.path}` : "/";
195
286
  const segments = this.getPathSegments(normalizedPath);
287
+ const ifMatch = ctx.options?.ifMatch;
288
+ if (ifMatch !== void 0) {
289
+ const currentValue = this.getValueAtPath(this.jsonData, segments);
290
+ const currentVersion = this.computeVersion(currentValue);
291
+ if (ifMatch !== currentVersion) throw new _aigne_afs.AFSConflictError(normalizedPath, {
292
+ cid: currentVersion,
293
+ mtime: this.fileStats.mtime ? this.fileStats.mtime.getTime() : null
294
+ });
295
+ }
196
296
  if (payload.content !== void 0) this.setValueAtPath(this.jsonData, segments, payload.content);
197
297
  if (payload.meta !== void 0 && typeof payload.meta === "object") {
198
298
  const finalMeta = {
@@ -215,7 +315,8 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
215
315
  updatedAt: this.fileStats.mtime,
216
316
  meta: {
217
317
  ...storedMeta,
218
- childrenCount: isDir ? children.length : void 0
318
+ childrenCount: isDir ? children.length : void 0,
319
+ version: this.computeVersion(newValue)
219
320
  },
220
321
  userId: payload.userId,
221
322
  sessionId: payload.sessionId,
@@ -291,6 +392,7 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
291
392
  const children = isDir ? this.getChildren(value) : [];
292
393
  const meta = { ...this.loadMeta(normalizedPath) };
293
394
  if (isDir) meta.childrenCount = children.length;
395
+ meta.version = this.computeVersion(value);
294
396
  return { data: {
295
397
  id: segments.length > 0 ? segments[segments.length - 1] : "/",
296
398
  path: normalizedPath,
@@ -299,6 +401,16 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
299
401
  meta
300
402
  } };
301
403
  }
404
+ /**
405
+ * Compute an opaque version string for a JSON value at a given path.
406
+ * Used as the ifMatch token for optimistic-concurrency writes. SHA-256 over
407
+ * the canonical JSON serialization keeps the token stable across mounts of
408
+ * the same content. Clients treat this as opaque — see AFSWriteOptions.ifMatch.
409
+ */
410
+ computeVersion(value) {
411
+ const canonical = value === void 0 ? "\0undefined" : JSON.stringify(value) ?? "null";
412
+ return (0, node_crypto.createHash)("sha256").update(canonical).digest("hex");
413
+ }
302
414
  async readCapabilitiesHandler(_ctx) {
303
415
  await this.ensureLoaded();
304
416
  const operations = [
@@ -309,6 +421,11 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
309
421
  "search"
310
422
  ];
311
423
  if (this.accessMode === "readwrite") operations.push("write", "delete", "rename");
424
+ const baseOps = this.getOperationsDeclaration();
425
+ if (typeof baseOps.write === "boolean" ? baseOps.write : baseOps.write?.supported === true) baseOps.write = {
426
+ supported: true,
427
+ features: { ifMatch: true }
428
+ };
312
429
  return {
313
430
  id: "/.meta/.capabilities",
314
431
  path: "/.meta/.capabilities",
@@ -318,7 +435,7 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
318
435
  description: this.description || `JSON/YAML virtual filesystem (${this.fileFormat} format)`,
319
436
  tools: [],
320
437
  actions: [],
321
- operations: this.getOperationsDeclaration()
438
+ operations: baseOps
322
439
  },
323
440
  meta: {
324
441
  kind: "afs:capabilities",
@@ -492,16 +609,20 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
492
609
  */
493
610
  async ensureLoaded() {
494
611
  if (this.jsonData !== null) return;
612
+ const platform = (0, _aigne_afs.getPlatform)();
495
613
  try {
496
- const stats = await (0, node_fs_promises.stat)(this.resolvedJsonPath);
614
+ const stats = await platform.fs.stat(this.resolvedJsonPath);
497
615
  this.fileStats = {
498
- birthtime: stats.birthtime,
499
- mtime: stats.mtime
616
+ birthtime: stats.birthtime ? new Date(stats.birthtime) : void 0,
617
+ mtime: stats.mtime ? new Date(stats.mtime) : void 0
500
618
  };
501
- this.jsonData = (0, yaml.parse)(await (0, node_fs_promises.readFile)(this.resolvedJsonPath, "utf8"));
502
- } catch (error) {
503
- if (error.code === "ENOENT") this.jsonData = {};
504
- else throw error;
619
+ this.jsonData = (0, js_yaml.load)(await platform.fs.readTextFile(this.resolvedJsonPath));
620
+ } catch {
621
+ try {
622
+ await platform.fs.mkdir(platform.path.dirname(this.resolvedJsonPath), { recursive: true });
623
+ await platform.fs.writeFile(this.resolvedJsonPath, "{}");
624
+ } catch {}
625
+ this.jsonData = {};
505
626
  }
506
627
  }
507
628
  /**
@@ -509,22 +630,34 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
509
630
  */
510
631
  async saveToFile() {
511
632
  let content;
512
- if (this.fileFormat === "yaml") content = (0, yaml.stringify)(this.jsonData);
633
+ if (this.fileFormat === "yaml") content = (0, js_yaml.dump)(this.jsonData);
513
634
  else content = JSON.stringify(this.jsonData, null, 2);
514
- await (0, node_fs_promises.writeFile)(this.resolvedJsonPath, content, "utf8");
515
- const stats = await (0, node_fs_promises.stat)(this.resolvedJsonPath);
635
+ const platform = (0, _aigne_afs.getPlatform)();
636
+ await platform.fs.writeFile(this.resolvedJsonPath, content);
637
+ const stats = await platform.fs.stat(this.resolvedJsonPath);
516
638
  this.fileStats = {
517
- birthtime: this.fileStats.birthtime || stats.birthtime,
518
- mtime: stats.mtime
639
+ birthtime: this.fileStats.birthtime,
640
+ mtime: stats.mtime ? new Date(stats.mtime) : void 0
519
641
  };
520
642
  }
521
643
  /**
522
644
  * Get path segments from normalized path
523
645
  */
646
+ /**
647
+ * Dangerous property names that must never be used as object keys.
648
+ * Prevents prototype pollution attacks (e.g., writing to /__proto__/polluted).
649
+ */
650
+ static DANGEROUS_KEYS = new Set([
651
+ "__proto__",
652
+ "constructor",
653
+ "prototype"
654
+ ]);
524
655
  getPathSegments(path) {
525
656
  const normalized = this.normalizePath(path);
526
657
  if (normalized === "/") return [];
527
- return normalized.slice(1).split("/");
658
+ const segments = normalized.slice(1).split("/");
659
+ for (const segment of segments) if (AFSJSON.DANGEROUS_KEYS.has(segment)) throw new Error(`Path segment "${segment}" is not allowed (prototype pollution guard)`);
660
+ return segments;
528
661
  }
529
662
  /**
530
663
  * Navigate to a value in the JSON structure using path segments
@@ -609,6 +742,9 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
609
742
  if (typeof value === "object" && value !== null) return true;
610
743
  return false;
611
744
  }
745
+ isJsonAsFormat(value) {
746
+ return typeof value === "string" && JSON_AS_FORMATS.includes(value);
747
+ }
612
748
  /**
613
749
  * Get children of a directory value (filters out .afs meta key)
614
750
  */
@@ -623,6 +759,48 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
623
759
  }));
624
760
  return [];
625
761
  }
762
+ compactJsonValue(value) {
763
+ if (!this.isDirectoryValue(value)) return value;
764
+ if (Array.isArray(value)) return {
765
+ type: "array",
766
+ length: value.length,
767
+ sample: value.slice(0, 3).map((item) => this.compactJsonValue(item))
768
+ };
769
+ const entries = Object.entries(value).filter(([key]) => !this.isMetaKey(key));
770
+ const preview = {};
771
+ for (const [key, child] of entries) {
772
+ if (Object.keys(preview).length >= 6) break;
773
+ if (!this.isDirectoryValue(child)) preview[key] = child;
774
+ }
775
+ return {
776
+ type: "object",
777
+ keys: entries.map(([key]) => key).slice(0, 12),
778
+ ...Object.keys(preview).length > 0 ? { preview } : {}
779
+ };
780
+ }
781
+ stripMetaFromValue(value) {
782
+ if (!this.isDirectoryValue(value)) return value;
783
+ if (Array.isArray(value)) return value.map((item) => this.stripMetaFromValue(item));
784
+ const result = {};
785
+ for (const [key, child] of Object.entries(value)) if (!this.isMetaKey(key)) result[key] = this.stripMetaFromValue(child);
786
+ return result;
787
+ }
788
+ buildJsonAsEntry(path, value, as) {
789
+ let content;
790
+ if (as === "content") content = value;
791
+ else if (as === "tree") content = this.stripMetaFromValue(value);
792
+ else content = this.compactJsonValue(value);
793
+ return {
794
+ id: `${path}.as:${as}`,
795
+ path: (0, ufo.joinURL)(path, ".as", as),
796
+ content,
797
+ meta: {
798
+ kind: `json:${as}`,
799
+ representation: as
800
+ },
801
+ actions: []
802
+ };
803
+ }
626
804
  /**
627
805
  * Convert a JSON value to an AFSEntry
628
806
  */
@@ -634,7 +812,8 @@ var AFSJSON = class AFSJSON extends _aigne_afs_provider.AFSBaseProvider {
634
812
  content: isDir ? void 0 : value,
635
813
  meta: {
636
814
  kind,
637
- childrenCount: isDir ? children.length : void 0
815
+ childrenCount: isDir ? children.length : void 0,
816
+ version: this.computeVersion(value)
638
817
  },
639
818
  createdAt: this.fileStats.birthtime,
640
819
  updatedAt: this.fileStats.mtime
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AFSAccessMode, AFSEntry, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, ProviderManifest } from "@aigne/afs";
1
+ import { AFSAccessMode, AFSEntry, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, OperationsDeclaration, ProviderManifest, ProviderTreeSchema } from "@aigne/afs";
2
2
  import { AFSBaseProvider, RouteContext } from "@aigne/afs/provider";
3
3
  import { z } from "zod";
4
4
 
@@ -47,6 +47,7 @@ declare class AFSJSON extends AFSBaseProvider {
47
47
  agentSkills: boolean | undefined;
48
48
  }, unknown>>;
49
49
  static manifest(): ProviderManifest;
50
+ static treeSchema(): ProviderTreeSchema;
50
51
  static load({
51
52
  basePath,
52
53
  config
@@ -64,6 +65,7 @@ declare class AFSJSON extends AFSBaseProvider {
64
65
  localPath?: string;
65
66
  uri?: string;
66
67
  });
68
+ supportedAs(path: string): Promise<string[]>;
67
69
  /**
68
70
  * Read metadata for a JSON node via /.meta or /path/.meta
69
71
  * Returns stored metadata merged with computed type information
@@ -77,6 +79,13 @@ declare class AFSJSON extends AFSBaseProvider {
77
79
  }>): Promise<AFSListResult & {
78
80
  noExpand?: string[];
79
81
  }>;
82
+ /**
83
+ * AFSJSON handles `handleDepth` itself, so it doesn't get the base.ts
84
+ * orderBy fallback — it implements ordering natively in listHandler and
85
+ * declares the sub-capability here (consumed by both base.list's
86
+ * no-silent-drop check and the /.meta/.capabilities manifest).
87
+ */
88
+ protected getOperationsDeclaration(): OperationsDeclaration;
80
89
  readHandler(ctx: RouteContext<{
81
90
  path?: string;
82
91
  }>): Promise<AFSEntry | undefined>;
@@ -113,6 +122,13 @@ declare class AFSJSON extends AFSBaseProvider {
113
122
  statHandler(ctx: RouteContext<{
114
123
  path?: string;
115
124
  }>): Promise<AFSStatResult>;
125
+ /**
126
+ * Compute an opaque version string for a JSON value at a given path.
127
+ * Used as the ifMatch token for optimistic-concurrency writes. SHA-256 over
128
+ * the canonical JSON serialization keeps the token stable across mounts of
129
+ * the same content. Clients treat this as opaque — see AFSWriteOptions.ifMatch.
130
+ */
131
+ private computeVersion;
116
132
  readCapabilitiesHandler(_ctx: RouteContext): Promise<AFSEntry | undefined>;
117
133
  explainHandler(ctx: RouteContext<{
118
134
  path?: string;
@@ -153,6 +169,11 @@ declare class AFSJSON extends AFSBaseProvider {
153
169
  /**
154
170
  * Get path segments from normalized path
155
171
  */
172
+ /**
173
+ * Dangerous property names that must never be used as object keys.
174
+ * Prevents prototype pollution attacks (e.g., writing to /__proto__/polluted).
175
+ */
176
+ private static readonly DANGEROUS_KEYS;
156
177
  private getPathSegments;
157
178
  /**
158
179
  * Navigate to a value in the JSON structure using path segments
@@ -170,10 +191,14 @@ declare class AFSJSON extends AFSBaseProvider {
170
191
  * Check if a value is a "directory" (object or array with children)
171
192
  */
172
193
  private isDirectoryValue;
194
+ private isJsonAsFormat;
173
195
  /**
174
196
  * Get children of a directory value (filters out .afs meta key)
175
197
  */
176
198
  private getChildren;
199
+ private compactJsonValue;
200
+ private stripMetaFromValue;
201
+ private buildJsonAsEntry;
177
202
  /**
178
203
  * Convert a JSON value to an AFSEntry
179
204
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;UAkDiB,cAAA;EACf,IAAA;EACA,QAAA;EACA,WAAA;;;;;;;EAOA,UAAA,GAAa,aAAA;EAMb;;;AAuBF;;EAvBE,WAAA;AAAA;;;;;;;cAuBW,OAAA,SAAgB,eAAA;EAkCR,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAAA,OAjC1E,MAAA,CAAA,GAAM,CAAA,CAAA,OAAA;;;;;;;;;;;;;SAIN,QAAA,CAAA,GAAY,gBAAA;EAAA,OAWN,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAAwB,OAAA,CAAA,OAAA;EAAA,SAKvD,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,SACZ,WAAA;EAAA,QAED,QAAA;EAAA,QACA,SAAA;EAAA,QAIA,UAAA;EAAA,QACA,gBAAA;cAEW,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAlCtD;;;;;EA4GrB,eAAA,CAAgB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;EAmE/D,WAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KACnB,OAAA,CAAQ,aAAA;IAAkB,QAAA;EAAA;EAiFvB,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;;;;;;;;;;EAwB3D,YAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,EAAS,oBAAA,GACR,OAAA;IAAU,IAAA,EAAM,QAAA;EAAA;EAgDb,aAAA,CAAc,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA;IAAU,OAAA;EAAA;EA0B/D,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,WACC,OAAA;IAAU,OAAA;EAAA;EAiCP,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,KAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,IAAA,EAAM,QAAA;IAAY,OAAA;EAAA;EAuDzB,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EAmC3D,uBAAA,CAAwB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EA0BrD,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,gBAAA;EAvZ9C;;;EAAA,QAmgBd,YAAA;EA/bD;;;EAAA,QA8cC,SAAA;EA7cG;;;;;;;EAAA,QAwdH,QAAA;EA/WF;;;;;;;EAAA,QA+aE,QAAA;EA5aW;;;;EAAA,QAseL,YAAA;EAtb6C;;;EAAA,QAid7C,UAAA;EAtbQ;;;EAAA,QA6cd,eAAA;EA3cK;;;EAAA,QAodL,cAAA;EAlbN;;;EAAA,QA0cM,cAAA;EAvcL;;;EAAA,QAigBK,iBAAA;EA1cF;;;EAAA,QAqfE,gBAAA;EArfiD;;;EAAA,QA8fjD,WAAA;EA3dsB;;;EAAA,QA0etB,eAAA;AAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;UA2EiB,cAAA;EACf,IAAA;EACA,QAAA;EACA,WAAA;;;;;;;EAOA,UAAA,GAAa,aAAA;EAMb;;;AAuBF;;EAvBE,WAAA;AAAA;;;;;;;cAuBW,OAAA,SAAgB,eAAA;EA0DR,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAAA,OAzD1E,MAAA,CAAA,GAAM,CAAA,CAAA,OAAA;;;;;;;;;;;;;SAIN,QAAA,CAAA,GAAY,gBAAA;EAAA,OAoBZ,UAAA,CAAA,GAAc,kBAAA;EAAA,OAaR,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAAwB,OAAA,CAAA,OAAA;EAAA,SAOvD,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,SACZ,WAAA;EAAA,QAED,QAAA;EAAA,QACA,SAAA;EAAA,QAIA,UAAA;EAAA,QACA,gBAAA;cAEW,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAwC3E,WAAA,CAAY,IAAA,WAAe,OAAA;EAyhB2B;;;;;EAvftD,eAAA,CAAgB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;EAmE/D,WAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KACnB,OAAA,CAAQ,aAAA;IAAkB,QAAA;EAAA;;;;;;;YA8GnB,wBAAA,CAAA,GAA4B,qBAAA;EAUhC,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;;;;;;;;;;EA8B3D,YAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,EAAS,oBAAA,GACR,OAAA;IAAU,IAAA,EAAM,QAAA;EAAA;EAiEb,aAAA,CAAc,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA;IAAU,OAAA;EAAA;EA0B/D,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,WACC,OAAA;IAAU,OAAA;EAAA;EAiCP,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,KAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,IAAA,EAAM,QAAA;IAAY,OAAA;EAAA;EAuDzB,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EAxZ3D;;;;;;EAAA,QAkcE,cAAA;EAQF,uBAAA,CAAwB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EA6CrD,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,gBAAA;EAlbzD;;;EAAA,QA8hBH,YAAA;EAtaF;;;EAAA,QAqbE,SAAA;EArbiD;;;;;;;EAAA,QAgcjD,QAAA;EA/ZL;;;;;;;EAAA,QA+dK,QAAA;EA9Z6D;;;;EAAA,QAwdvD,YAAA;EA5bZ;;;EAAA,QA4dY,UAAA;EAzbP;;;EACL;;;;EAAA,wBAodsB,cAAA;EAAA,QAEhB,eAAA;EApduB;;;EAAA,QAsevB,cAAA;EA/aU;;;EAAA,QAucV,cAAA;EArZF;;;EAAA,QA+cE,iBAAA;EA/cmD;;;EAAA,QA0fnD,gBAAA;EAAA,QAMA,cAAA;EAndoD;;;EAAA,QA0dpD,WAAA;EAAA,QAYA,gBAAA;EAAA,QAwBA,kBAAA;EAAA,QAUA,gBAAA;EAxOM;;;EAAA,QA4PN,eAAA;AAAA"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AFSAccessMode, AFSEntry, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, ProviderManifest } from "@aigne/afs";
1
+ import { AFSAccessMode, AFSEntry, AFSExplainResult, AFSListResult, AFSModuleLoadParams, AFSSearchOptions, AFSStatResult, AFSWriteEntryPayload, OperationsDeclaration, ProviderManifest, ProviderTreeSchema } from "@aigne/afs";
2
2
  import { AFSBaseProvider, RouteContext } from "@aigne/afs/provider";
3
3
  import { z } from "zod";
4
4
 
@@ -47,6 +47,7 @@ declare class AFSJSON extends AFSBaseProvider {
47
47
  agentSkills: boolean | undefined;
48
48
  }, unknown>>;
49
49
  static manifest(): ProviderManifest;
50
+ static treeSchema(): ProviderTreeSchema;
50
51
  static load({
51
52
  basePath,
52
53
  config
@@ -64,6 +65,7 @@ declare class AFSJSON extends AFSBaseProvider {
64
65
  localPath?: string;
65
66
  uri?: string;
66
67
  });
68
+ supportedAs(path: string): Promise<string[]>;
67
69
  /**
68
70
  * Read metadata for a JSON node via /.meta or /path/.meta
69
71
  * Returns stored metadata merged with computed type information
@@ -77,6 +79,13 @@ declare class AFSJSON extends AFSBaseProvider {
77
79
  }>): Promise<AFSListResult & {
78
80
  noExpand?: string[];
79
81
  }>;
82
+ /**
83
+ * AFSJSON handles `handleDepth` itself, so it doesn't get the base.ts
84
+ * orderBy fallback — it implements ordering natively in listHandler and
85
+ * declares the sub-capability here (consumed by both base.list's
86
+ * no-silent-drop check and the /.meta/.capabilities manifest).
87
+ */
88
+ protected getOperationsDeclaration(): OperationsDeclaration;
80
89
  readHandler(ctx: RouteContext<{
81
90
  path?: string;
82
91
  }>): Promise<AFSEntry | undefined>;
@@ -113,6 +122,13 @@ declare class AFSJSON extends AFSBaseProvider {
113
122
  statHandler(ctx: RouteContext<{
114
123
  path?: string;
115
124
  }>): Promise<AFSStatResult>;
125
+ /**
126
+ * Compute an opaque version string for a JSON value at a given path.
127
+ * Used as the ifMatch token for optimistic-concurrency writes. SHA-256 over
128
+ * the canonical JSON serialization keeps the token stable across mounts of
129
+ * the same content. Clients treat this as opaque — see AFSWriteOptions.ifMatch.
130
+ */
131
+ private computeVersion;
116
132
  readCapabilitiesHandler(_ctx: RouteContext): Promise<AFSEntry | undefined>;
117
133
  explainHandler(ctx: RouteContext<{
118
134
  path?: string;
@@ -153,6 +169,11 @@ declare class AFSJSON extends AFSBaseProvider {
153
169
  /**
154
170
  * Get path segments from normalized path
155
171
  */
172
+ /**
173
+ * Dangerous property names that must never be used as object keys.
174
+ * Prevents prototype pollution attacks (e.g., writing to /__proto__/polluted).
175
+ */
176
+ private static readonly DANGEROUS_KEYS;
156
177
  private getPathSegments;
157
178
  /**
158
179
  * Navigate to a value in the JSON structure using path segments
@@ -170,10 +191,14 @@ declare class AFSJSON extends AFSBaseProvider {
170
191
  * Check if a value is a "directory" (object or array with children)
171
192
  */
172
193
  private isDirectoryValue;
194
+ private isJsonAsFormat;
173
195
  /**
174
196
  * Get children of a directory value (filters out .afs meta key)
175
197
  */
176
198
  private getChildren;
199
+ private compactJsonValue;
200
+ private stripMetaFromValue;
201
+ private buildJsonAsEntry;
177
202
  /**
178
203
  * Convert a JSON value to an AFSEntry
179
204
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;UAkDiB,cAAA;EACf,IAAA;EACA,QAAA;EACA,WAAA;;;;;;;EAOA,UAAA,GAAa,aAAA;EAMb;;;AAuBF;;EAvBE,WAAA;AAAA;;;;;;;cAuBW,OAAA,SAAgB,eAAA;EAkCR,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAAA,OAjC1E,MAAA,CAAA,GAAM,CAAA,CAAA,OAAA;;;;;;;;;;;;;SAIN,QAAA,CAAA,GAAY,gBAAA;EAAA,OAWN,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAAwB,OAAA,CAAA,OAAA;EAAA,SAKvD,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,SACZ,WAAA;EAAA,QAED,QAAA;EAAA,QACA,SAAA;EAAA,QAIA,UAAA;EAAA,QACA,gBAAA;cAEW,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAlCtD;;;;;EA4GrB,eAAA,CAAgB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;EAmE/D,WAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KACnB,OAAA,CAAQ,aAAA;IAAkB,QAAA;EAAA;EAiFvB,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;;;;;;;;;;EAwB3D,YAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,EAAS,oBAAA,GACR,OAAA;IAAU,IAAA,EAAM,QAAA;EAAA;EAgDb,aAAA,CAAc,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA;IAAU,OAAA;EAAA;EA0B/D,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,WACC,OAAA;IAAU,OAAA;EAAA;EAiCP,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,KAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,IAAA,EAAM,QAAA;IAAY,OAAA;EAAA;EAuDzB,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EAmC3D,uBAAA,CAAwB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EA0BrD,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,gBAAA;EAvZ9C;;;EAAA,QAmgBd,YAAA;EA/bD;;;EAAA,QA8cC,SAAA;EA7cG;;;;;;;EAAA,QAwdH,QAAA;EA/WF;;;;;;;EAAA,QA+aE,QAAA;EA5aW;;;;EAAA,QAseL,YAAA;EAtb6C;;;EAAA,QAid7C,UAAA;EAtbQ;;;EAAA,QA6cd,eAAA;EA3cK;;;EAAA,QAodL,cAAA;EAlbN;;;EAAA,QA0cM,cAAA;EAvcL;;;EAAA,QAigBK,iBAAA;EA1cF;;;EAAA,QAqfE,gBAAA;EArfiD;;;EAAA,QA8fjD,WAAA;EA3dsB;;;EAAA,QA0etB,eAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;UA2EiB,cAAA;EACf,IAAA;EACA,QAAA;EACA,WAAA;;;;;;;EAOA,UAAA,GAAa,aAAA;EAMb;;;AAuBF;;EAvBE,WAAA;AAAA;;;;;;;cAuBW,OAAA,SAAgB,eAAA;EA0DR,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAAA,OAzD1E,MAAA,CAAA,GAAM,CAAA,CAAA,OAAA;;;;;;;;;;;;;SAIN,QAAA,CAAA,GAAY,gBAAA;EAAA,OAoBZ,UAAA,CAAA,GAAc,kBAAA;EAAA,OAaR,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAAwB,OAAA,CAAA,OAAA;EAAA,SAOvD,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,SACZ,WAAA;EAAA,QAED,QAAA;EAAA,QACA,SAAA;EAAA,QAIA,UAAA;EAAA,QACA,gBAAA;cAEW,OAAA,EAAS,cAAA;IAAmB,GAAA;IAAc,SAAA;IAAoB,GAAA;EAAA;EAwC3E,WAAA,CAAY,IAAA,WAAe,OAAA;EAyhB2B;;;;;EAvftD,eAAA,CAAgB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;EAmE/D,WAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KACnB,OAAA,CAAQ,aAAA;IAAkB,QAAA;EAAA;;;;;;;YA8GnB,wBAAA,CAAA,GAA4B,qBAAA;EAUhC,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,QAAA;;;;;;;;;;EA8B3D,YAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,EAAS,oBAAA,GACR,OAAA;IAAU,IAAA,EAAM,QAAA;EAAA;EAiEb,aAAA,CAAc,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA;IAAU,OAAA;EAAA;EA0B/D,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,WACC,OAAA;IAAU,OAAA;EAAA;EAiCP,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,KAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,IAAA,EAAM,QAAA;IAAY,OAAA;EAAA;EAuDzB,WAAA,CAAY,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,aAAA;EAxZ3D;;;;;;EAAA,QAkcE,cAAA;EAQF,uBAAA,CAAwB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EA6CrD,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAmB,OAAA,CAAQ,gBAAA;EAlbzD;;;EAAA,QA8hBH,YAAA;EAtaF;;;EAAA,QAqbE,SAAA;EArbiD;;;;;;;EAAA,QAgcjD,QAAA;EA/ZL;;;;;;;EAAA,QA+dK,QAAA;EA9Z6D;;;;EAAA,QAwdvD,YAAA;EA5bZ;;;EAAA,QA4dY,UAAA;EAzbP;;;EACL;;;;EAAA,wBAodsB,cAAA;EAAA,QAEhB,eAAA;EApduB;;;EAAA,QAsevB,cAAA;EA/aU;;;EAAA,QAucV,cAAA;EArZF;;;EAAA,QA+cE,iBAAA;EA/cmD;;;EAAA,QA0fnD,gBAAA;EAAA,QAMA,cAAA;EAndoD;;;EAAA,QA0dpD,WAAA;EAAA,QAYA,gBAAA;EAAA,QAwBA,kBAAA;EAAA,QAUA,gBAAA;EAxOM;;;EAAA,QA4PN,eAAA;AAAA"}