@aigne/afs-index 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/LICENSE.md +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/backend/d1-index-backend.cjs +656 -0
- package/dist/backend/d1-index-backend.d.cts +149 -0
- package/dist/backend/d1-index-backend.d.cts.map +1 -0
- package/dist/backend/d1-index-backend.d.mts +149 -0
- package/dist/backend/d1-index-backend.d.mts.map +1 -0
- package/dist/backend/d1-index-backend.mjs +657 -0
- package/dist/backend/d1-index-backend.mjs.map +1 -0
- package/dist/backend/d1-vector-backend.cjs +83 -0
- package/dist/backend/d1-vector-backend.d.cts +21 -0
- package/dist/backend/d1-vector-backend.d.cts.map +1 -0
- package/dist/backend/d1-vector-backend.d.mts +21 -0
- package/dist/backend/d1-vector-backend.d.mts.map +1 -0
- package/dist/backend/d1-vector-backend.mjs +84 -0
- package/dist/backend/d1-vector-backend.mjs.map +1 -0
- package/dist/backend/memory-backend.cjs +304 -0
- package/dist/backend/memory-backend.d.cts +66 -0
- package/dist/backend/memory-backend.d.cts.map +1 -0
- package/dist/backend/memory-backend.d.mts +66 -0
- package/dist/backend/memory-backend.d.mts.map +1 -0
- package/dist/backend/memory-backend.mjs +305 -0
- package/dist/backend/memory-backend.mjs.map +1 -0
- package/dist/backend/prefix-range.cjs +25 -0
- package/dist/backend/prefix-range.mjs +25 -0
- package/dist/backend/prefix-range.mjs.map +1 -0
- package/dist/backend/schema.cjs +187 -0
- package/dist/backend/schema.mjs +188 -0
- package/dist/backend/schema.mjs.map +1 -0
- package/dist/backend/sqlite-backend.cjs +889 -0
- package/dist/backend/sqlite-backend.d.cts +96 -0
- package/dist/backend/sqlite-backend.d.cts.map +1 -0
- package/dist/backend/sqlite-backend.d.mts +96 -0
- package/dist/backend/sqlite-backend.d.mts.map +1 -0
- package/dist/backend/sqlite-backend.mjs +890 -0
- package/dist/backend/sqlite-backend.mjs.map +1 -0
- package/dist/backend/types.d.cts +209 -0
- package/dist/backend/types.d.cts.map +1 -0
- package/dist/backend/types.d.mts +209 -0
- package/dist/backend/types.d.mts.map +1 -0
- package/dist/backend/upstash-vector-backend.cjs +98 -0
- package/dist/backend/upstash-vector-backend.d.cts +21 -0
- package/dist/backend/upstash-vector-backend.d.cts.map +1 -0
- package/dist/backend/upstash-vector-backend.d.mts +21 -0
- package/dist/backend/upstash-vector-backend.d.mts.map +1 -0
- package/dist/backend/upstash-vector-backend.mjs +98 -0
- package/dist/backend/upstash-vector-backend.mjs.map +1 -0
- package/dist/backend/vector-backend.d.cts +29 -0
- package/dist/backend/vector-backend.d.cts.map +1 -0
- package/dist/backend/vector-backend.d.mts +29 -0
- package/dist/backend/vector-backend.d.mts.map +1 -0
- package/dist/dirty-queue.d.cts +41 -0
- package/dist/dirty-queue.d.cts.map +1 -0
- package/dist/dirty-queue.d.mts +41 -0
- package/dist/dirty-queue.d.mts.map +1 -0
- package/dist/embed.cjs +45 -0
- package/dist/embed.d.cts +20 -0
- package/dist/embed.d.cts.map +1 -0
- package/dist/embed.d.mts +20 -0
- package/dist/embed.d.mts.map +1 -0
- package/dist/embed.mjs +46 -0
- package/dist/embed.mjs.map +1 -0
- package/dist/extraction/defaults.cjs +242 -0
- package/dist/extraction/defaults.d.cts +14 -0
- package/dist/extraction/defaults.d.cts.map +1 -0
- package/dist/extraction/defaults.d.mts +14 -0
- package/dist/extraction/defaults.d.mts.map +1 -0
- package/dist/extraction/defaults.mjs +242 -0
- package/dist/extraction/defaults.mjs.map +1 -0
- package/dist/extraction/field-map.cjs +162 -0
- package/dist/extraction/field-map.d.cts +70 -0
- package/dist/extraction/field-map.d.cts.map +1 -0
- package/dist/extraction/field-map.d.mts +70 -0
- package/dist/extraction/field-map.d.mts.map +1 -0
- package/dist/extraction/field-map.mjs +157 -0
- package/dist/extraction/field-map.mjs.map +1 -0
- package/dist/extraction/manager.cjs +74 -0
- package/dist/extraction/manager.d.cts +47 -0
- package/dist/extraction/manager.d.cts.map +1 -0
- package/dist/extraction/manager.d.mts +47 -0
- package/dist/extraction/manager.d.mts.map +1 -0
- package/dist/extraction/manager.mjs +74 -0
- package/dist/extraction/manager.mjs.map +1 -0
- package/dist/extraction/path-anchors.cjs +86 -0
- package/dist/extraction/path-anchors.d.cts +72 -0
- package/dist/extraction/path-anchors.d.cts.map +1 -0
- package/dist/extraction/path-anchors.d.mts +72 -0
- package/dist/extraction/path-anchors.d.mts.map +1 -0
- package/dist/extraction/path-anchors.mjs +86 -0
- package/dist/extraction/path-anchors.mjs.map +1 -0
- package/dist/follower-scheduler.cjs +92 -0
- package/dist/follower-scheduler.d.cts +58 -0
- package/dist/follower-scheduler.d.cts.map +1 -0
- package/dist/follower-scheduler.d.mts +58 -0
- package/dist/follower-scheduler.d.mts.map +1 -0
- package/dist/follower-scheduler.mjs +92 -0
- package/dist/follower-scheduler.mjs.map +1 -0
- package/dist/follower.cjs +577 -0
- package/dist/follower.d.cts +241 -0
- package/dist/follower.d.cts.map +1 -0
- package/dist/follower.d.mts +241 -0
- package/dist/follower.d.mts.map +1 -0
- package/dist/follower.mjs +571 -0
- package/dist/follower.mjs.map +1 -0
- package/dist/index-provider.cjs +1166 -0
- package/dist/index-provider.d.cts +175 -0
- package/dist/index-provider.d.cts.map +1 -0
- package/dist/index-provider.d.mts +175 -0
- package/dist/index-provider.d.mts.map +1 -0
- package/dist/index-provider.mjs +1167 -0
- package/dist/index-provider.mjs.map +1 -0
- package/dist/index.cjs +53 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.mjs +20 -0
- package/dist/keys/exact.cjs +20 -0
- package/dist/keys/exact.mjs +20 -0
- package/dist/keys/exact.mjs.map +1 -0
- package/dist/keys/location.cjs +13 -0
- package/dist/keys/location.mjs +13 -0
- package/dist/keys/location.mjs.map +1 -0
- package/dist/keys/normalized.cjs +62 -0
- package/dist/keys/normalized.mjs +63 -0
- package/dist/keys/normalized.mjs.map +1 -0
- package/dist/keys/person.cjs +15 -0
- package/dist/keys/person.mjs +15 -0
- package/dist/keys/person.mjs.map +1 -0
- package/dist/keys/registry.cjs +45 -0
- package/dist/keys/registry.d.cts +15 -0
- package/dist/keys/registry.d.cts.map +1 -0
- package/dist/keys/registry.d.mts +15 -0
- package/dist/keys/registry.d.mts.map +1 -0
- package/dist/keys/registry.mjs +46 -0
- package/dist/keys/registry.mjs.map +1 -0
- package/dist/keys/time.cjs +128 -0
- package/dist/keys/time.mjs +128 -0
- package/dist/keys/time.mjs.map +1 -0
- package/dist/keys/topic.cjs +23 -0
- package/dist/keys/topic.mjs +23 -0
- package/dist/keys/topic.mjs.map +1 -0
- package/dist/manifest-index.cjs +29 -0
- package/dist/manifest-index.d.cts +24 -0
- package/dist/manifest-index.d.cts.map +1 -0
- package/dist/manifest-index.d.mts +24 -0
- package/dist/manifest-index.d.mts.map +1 -0
- package/dist/manifest-index.mjs +28 -0
- package/dist/manifest-index.mjs.map +1 -0
- package/dist/offline-rebuild.cjs +48 -0
- package/dist/offline-rebuild.d.cts +57 -0
- package/dist/offline-rebuild.d.cts.map +1 -0
- package/dist/offline-rebuild.d.mts +57 -0
- package/dist/offline-rebuild.d.mts.map +1 -0
- package/dist/offline-rebuild.mjs +49 -0
- package/dist/offline-rebuild.mjs.map +1 -0
- package/dist/query/engine.cjs +433 -0
- package/dist/query/engine.d.cts +34 -0
- package/dist/query/engine.d.cts.map +1 -0
- package/dist/query/engine.d.mts +34 -0
- package/dist/query/engine.d.mts.map +1 -0
- package/dist/query/engine.mjs +433 -0
- package/dist/query/engine.mjs.map +1 -0
- package/dist/query/limits.cjs +12 -0
- package/dist/query/limits.mjs +12 -0
- package/dist/query/limits.mjs.map +1 -0
- package/dist/text/cjk.cjs +30 -0
- package/dist/text/cjk.d.cts +24 -0
- package/dist/text/cjk.d.cts.map +1 -0
- package/dist/text/cjk.d.mts +24 -0
- package/dist/text/cjk.d.mts.map +1 -0
- package/dist/text/cjk.mjs +30 -0
- package/dist/text/cjk.mjs.map +1 -0
- package/dist/text/fts.cjs +244 -0
- package/dist/text/fts.mjs +245 -0
- package/dist/text/fts.mjs.map +1 -0
- package/dist/text/host.cjs +39 -0
- package/dist/text/host.mjs +38 -0
- package/dist/text/host.mjs.map +1 -0
- package/dist/types.cjs +44 -0
- package/dist/types.d.cts +393 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +393 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +42 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,1166 @@
|
|
|
1
|
+
const require_time = require('./keys/time.cjs');
|
|
2
|
+
const require_registry = require('./keys/registry.cjs');
|
|
3
|
+
const require_sqlite_backend = require('./backend/sqlite-backend.cjs');
|
|
4
|
+
const require_embed = require('./embed.cjs');
|
|
5
|
+
const require_defaults = require('./extraction/defaults.cjs');
|
|
6
|
+
const require_manager = require('./extraction/manager.cjs');
|
|
7
|
+
const require_normalized = require('./keys/normalized.cjs');
|
|
8
|
+
const require_engine = require('./query/engine.cjs');
|
|
9
|
+
const require_types = require('./types.cjs');
|
|
10
|
+
const require_decorate = require('./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs');
|
|
11
|
+
let _aigne_afs = require("@aigne/afs");
|
|
12
|
+
let ufo = require("ufo");
|
|
13
|
+
let _aigne_afs_provider = require("@aigne/afs/provider");
|
|
14
|
+
let zod_v4 = require("zod/v4");
|
|
15
|
+
|
|
16
|
+
//#region src/index-provider.ts
|
|
17
|
+
const log = (0, _aigne_afs.makeNsLog)("provider:index");
|
|
18
|
+
const IndexArgsSchema = zod_v4.z.object({
|
|
19
|
+
entryPath: zod_v4.z.string(),
|
|
20
|
+
domain: zod_v4.z.string().optional(),
|
|
21
|
+
anchors: zod_v4.z.array(zod_v4.z.object({
|
|
22
|
+
type: zod_v4.z.string(),
|
|
23
|
+
confidence: zod_v4.z.number()
|
|
24
|
+
}).passthrough()).optional(),
|
|
25
|
+
summary: zod_v4.z.string().optional(),
|
|
26
|
+
embeddingSummary: zod_v4.z.string().optional(),
|
|
27
|
+
embedding: zod_v4.z.array(zod_v4.z.number()).optional(),
|
|
28
|
+
sourcePath: zod_v4.z.string().optional(),
|
|
29
|
+
sourceHash: zod_v4.z.string().optional(),
|
|
30
|
+
extractorId: zod_v4.z.string().optional(),
|
|
31
|
+
meta: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional()
|
|
32
|
+
});
|
|
33
|
+
const QueryArgsSchema = zod_v4.z.object({
|
|
34
|
+
mode: zod_v4.z.enum(require_types.SEARCH_MODES).optional(),
|
|
35
|
+
ftsMode: zod_v4.z.enum(["and", "or"]).optional(),
|
|
36
|
+
anchors: zod_v4.z.array(zod_v4.z.object({
|
|
37
|
+
type: zod_v4.z.string(),
|
|
38
|
+
values: zod_v4.z.array(zod_v4.z.string())
|
|
39
|
+
})).optional(),
|
|
40
|
+
text: zod_v4.z.string().optional(),
|
|
41
|
+
embeddingText: zod_v4.z.string().optional(),
|
|
42
|
+
embedding: zod_v4.z.array(zod_v4.z.number()).optional(),
|
|
43
|
+
rewriter: zod_v4.z.enum(["none", "multi-query"]).optional(),
|
|
44
|
+
opts: zod_v4.z.object({
|
|
45
|
+
scope: zod_v4.z.string().optional(),
|
|
46
|
+
pathPrefix: zod_v4.z.string().optional(),
|
|
47
|
+
limit: zod_v4.z.number().optional(),
|
|
48
|
+
cursor: zod_v4.z.string().optional(),
|
|
49
|
+
minResults: zod_v4.z.number().optional(),
|
|
50
|
+
minConfidence: zod_v4.z.number().optional(),
|
|
51
|
+
metaFilter: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional(),
|
|
52
|
+
embeddingFloor: zod_v4.z.number().optional(),
|
|
53
|
+
anchorFilter: zod_v4.z.boolean().optional(),
|
|
54
|
+
explain: zod_v4.z.boolean().optional()
|
|
55
|
+
}).optional()
|
|
56
|
+
});
|
|
57
|
+
const ReindexArgsSchema = zod_v4.z.object({
|
|
58
|
+
scope: zod_v4.z.string().optional(),
|
|
59
|
+
extractorId: zod_v4.z.string().optional(),
|
|
60
|
+
domain: zod_v4.z.string().optional()
|
|
61
|
+
});
|
|
62
|
+
const RegisterDomainArgsSchema = zod_v4.z.object({
|
|
63
|
+
name: zod_v4.z.string(),
|
|
64
|
+
types: zod_v4.z.array(zod_v4.z.object({ name: zod_v4.z.string() }).passthrough()).optional()
|
|
65
|
+
});
|
|
66
|
+
const GetArgsSchema = zod_v4.z.object({
|
|
67
|
+
entryPath: zod_v4.z.string().optional(),
|
|
68
|
+
entryPaths: zod_v4.z.array(zod_v4.z.string()).optional()
|
|
69
|
+
});
|
|
70
|
+
const CleanupArgsSchema = zod_v4.z.object({
|
|
71
|
+
state: zod_v4.z.enum(["stale", "removed"]).optional().default("stale"),
|
|
72
|
+
domain: zod_v4.z.string().optional(),
|
|
73
|
+
scope: zod_v4.z.string().optional()
|
|
74
|
+
});
|
|
75
|
+
const VerifyArgsSchema = zod_v4.z.object({
|
|
76
|
+
domain: zod_v4.z.string().optional(),
|
|
77
|
+
scope: zod_v4.z.string().optional()
|
|
78
|
+
});
|
|
79
|
+
const DiscoverArgsSchema = zod_v4.z.object({
|
|
80
|
+
providerPath: zod_v4.z.string(),
|
|
81
|
+
domain: zod_v4.z.string().optional()
|
|
82
|
+
});
|
|
83
|
+
const afsIndexOptionsSchema = zod_v4.z.object({
|
|
84
|
+
dbPath: zod_v4.z.string().optional(),
|
|
85
|
+
defaultDomain: zod_v4.z.string().optional()
|
|
86
|
+
});
|
|
87
|
+
var AFSIndex = class extends _aigne_afs_provider.AFSBaseProvider {
|
|
88
|
+
name;
|
|
89
|
+
description;
|
|
90
|
+
accessMode = "readwrite";
|
|
91
|
+
options;
|
|
92
|
+
keyRegistry = new require_registry.KeyGeneratorRegistry();
|
|
93
|
+
backend;
|
|
94
|
+
queryEngine;
|
|
95
|
+
domains = /* @__PURE__ */ new Map();
|
|
96
|
+
extractors = new require_manager.ExtractorManager();
|
|
97
|
+
unsubscribes = [];
|
|
98
|
+
afsRoot;
|
|
99
|
+
initialized = false;
|
|
100
|
+
follower;
|
|
101
|
+
catchUpInflight;
|
|
102
|
+
static schema() {
|
|
103
|
+
return afsIndexOptionsSchema;
|
|
104
|
+
}
|
|
105
|
+
static manifest() {
|
|
106
|
+
return {
|
|
107
|
+
name: "index",
|
|
108
|
+
description: "Full-text search and anchor-based indexing for AFS entries.\n- FTS5 full-text search on summaries\n- Structured anchor indexing (time, person, topic, location)\n- Combined query modes: anchor, fts, semantic\n- Actions: index, query, reindex, cleanup",
|
|
109
|
+
uriTemplate: "index://{dbPath+}",
|
|
110
|
+
category: "composite",
|
|
111
|
+
schema: zod_v4.z.object({
|
|
112
|
+
dbPath: zod_v4.z.string(),
|
|
113
|
+
defaultDomain: zod_v4.z.string().optional()
|
|
114
|
+
}),
|
|
115
|
+
tags: [
|
|
116
|
+
"index",
|
|
117
|
+
"search",
|
|
118
|
+
"fts5",
|
|
119
|
+
"sqlite",
|
|
120
|
+
"anchor"
|
|
121
|
+
],
|
|
122
|
+
capabilityTags: [
|
|
123
|
+
"read-write",
|
|
124
|
+
"search",
|
|
125
|
+
"auth:none",
|
|
126
|
+
"local"
|
|
127
|
+
],
|
|
128
|
+
security: {
|
|
129
|
+
riskLevel: "local",
|
|
130
|
+
resourceAccess: ["local-filesystem"],
|
|
131
|
+
dataSensitivity: [],
|
|
132
|
+
notes: ["Stores SQLite index database on local filesystem"]
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
constructor(options = {}) {
|
|
137
|
+
super();
|
|
138
|
+
this.options = options;
|
|
139
|
+
this.name = options.name ?? "index";
|
|
140
|
+
this.description = options.description ?? "AFS Index Provider — unified structured indexing";
|
|
141
|
+
if (options.timeKeyGranularity?.length) {
|
|
142
|
+
const include = new Set(options.timeKeyGranularity);
|
|
143
|
+
this.keyRegistry.register("time", (anchor) => require_time.generateTimeKeys(anchor, include));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async ensureInitialized() {
|
|
147
|
+
if (this.initialized) return;
|
|
148
|
+
this.backend = this.options.backend ?? new require_sqlite_backend.SQLiteIndexBackend(this.options.dbPath ?? ":memory:", this.keyRegistry, { ftsTokenizer: this.options.ftsTokenizer });
|
|
149
|
+
this.backend.setKeyRegistry?.(this.keyRegistry);
|
|
150
|
+
await this.backend.initialize();
|
|
151
|
+
this.queryEngine = new require_engine.QueryEngine(this.backend);
|
|
152
|
+
this.initialized = true;
|
|
153
|
+
}
|
|
154
|
+
onMount(root, _mountPath) {
|
|
155
|
+
this.afsRoot = root;
|
|
156
|
+
const unsubWrite = root.subscribe?.({ type: "afs:write" }, (event) => {
|
|
157
|
+
this.handleWriteEvent(event.path);
|
|
158
|
+
});
|
|
159
|
+
if (unsubWrite) this.unsubscribes.push(unsubWrite);
|
|
160
|
+
const unsubDelete = root.subscribe?.({ type: "afs:delete" }, (event) => {
|
|
161
|
+
this.handleDeleteEvent(event.path);
|
|
162
|
+
});
|
|
163
|
+
if (unsubDelete) this.unsubscribes.push(unsubDelete);
|
|
164
|
+
const unsubMount = root.subscribe?.({ type: "afs:mount" }, (event) => {
|
|
165
|
+
if (event.source === this.name) return;
|
|
166
|
+
this.handleMountEvent(event.path);
|
|
167
|
+
});
|
|
168
|
+
if (unsubMount) this.unsubscribes.push(unsubMount);
|
|
169
|
+
}
|
|
170
|
+
async handleMountEvent(providerPath) {
|
|
171
|
+
try {
|
|
172
|
+
if (!this.afsRoot) return;
|
|
173
|
+
const domainName = providerPath.split("/").filter(Boolean).pop() ?? "unknown";
|
|
174
|
+
const result = await this.discoverProvider(providerPath, domainName);
|
|
175
|
+
if (result.anchorTypes.length === 0 && !result.config && !result.extractor) this.domains.delete(domainName);
|
|
176
|
+
} catch {}
|
|
177
|
+
}
|
|
178
|
+
async handleDeleteEvent(path) {
|
|
179
|
+
try {
|
|
180
|
+
await this.ensureInitialized();
|
|
181
|
+
if (!await this.backend.getEntry(path)) return;
|
|
182
|
+
await this.backend.removeAnchors(path);
|
|
183
|
+
await this.backend.removeSummary(path);
|
|
184
|
+
if (this.backend.removeEmbedding) await this.backend.removeEmbedding(path);
|
|
185
|
+
await this.backend.removeEntry(path);
|
|
186
|
+
} catch {}
|
|
187
|
+
}
|
|
188
|
+
async handleWriteEvent(path) {
|
|
189
|
+
try {
|
|
190
|
+
if (!this.afsRoot) return;
|
|
191
|
+
await this.ensureInitialized();
|
|
192
|
+
let domain;
|
|
193
|
+
let result;
|
|
194
|
+
const extracted = this.extractors.hasRegistrations ? await this.extractors.extract(path, this.afsRoot) : null;
|
|
195
|
+
if (extracted) {
|
|
196
|
+
domain = extracted.domain;
|
|
197
|
+
result = extracted.result;
|
|
198
|
+
} else {
|
|
199
|
+
const readResult = await this.afsRoot.read?.(path);
|
|
200
|
+
if (!readResult?.data?.meta) return;
|
|
201
|
+
const entry = readResult.data;
|
|
202
|
+
const defaultResult = require_defaults.extractFromMeta(path, entry.meta);
|
|
203
|
+
if (!defaultResult) return;
|
|
204
|
+
domain = this.options.defaultDomain ?? "default";
|
|
205
|
+
result = defaultResult;
|
|
206
|
+
}
|
|
207
|
+
if (!this.shouldIndex(path, domain)) return;
|
|
208
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
209
|
+
await this.backend.writeEntry({
|
|
210
|
+
entryPath: path,
|
|
211
|
+
sourcePath: path,
|
|
212
|
+
domain,
|
|
213
|
+
state: "active",
|
|
214
|
+
indexedAt: now,
|
|
215
|
+
updatedAt: now,
|
|
216
|
+
anchorCount: result.anchors.length
|
|
217
|
+
});
|
|
218
|
+
if (result.anchors.length > 0) await this.backend.writeAnchors(path, result.anchors);
|
|
219
|
+
if (result.summary) await this.backend.writeSummary(path, result.summary);
|
|
220
|
+
if (this.backend.writeEmbedding) {
|
|
221
|
+
const embedding = result.embedding ?? await this.autoEmbed(result.summary);
|
|
222
|
+
if (embedding) await this.backend.writeEmbedding(path, embedding);
|
|
223
|
+
}
|
|
224
|
+
} catch {}
|
|
225
|
+
}
|
|
226
|
+
async close() {
|
|
227
|
+
for (const unsub of this.unsubscribes) unsub();
|
|
228
|
+
this.unsubscribes.length = 0;
|
|
229
|
+
this.extractors.clear();
|
|
230
|
+
if (this.initialized) {
|
|
231
|
+
await this.backend.close();
|
|
232
|
+
this.initialized = false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async readRoot(ctx) {
|
|
236
|
+
return this.buildEntry(ctx.path, {
|
|
237
|
+
content: this.description,
|
|
238
|
+
meta: {
|
|
239
|
+
kind: "afs:directory",
|
|
240
|
+
childrenCount: 3
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async listRoot(ctx) {
|
|
245
|
+
return { data: [
|
|
246
|
+
this.buildEntry((0, ufo.joinURL)(ctx.path, "domains"), { meta: {
|
|
247
|
+
kind: "afs:directory",
|
|
248
|
+
childrenCount: this.domains.size
|
|
249
|
+
} }),
|
|
250
|
+
this.buildEntry((0, ufo.joinURL)(ctx.path, "stats"), { meta: {
|
|
251
|
+
kind: "afs:directory",
|
|
252
|
+
childrenCount: 3
|
|
253
|
+
} }),
|
|
254
|
+
this.buildEntry((0, ufo.joinURL)(ctx.path, "config"), { meta: {
|
|
255
|
+
kind: "afs:directory",
|
|
256
|
+
childrenCount: 3
|
|
257
|
+
} })
|
|
258
|
+
] };
|
|
259
|
+
}
|
|
260
|
+
async listActions(ctx) {
|
|
261
|
+
const actions = [
|
|
262
|
+
"index",
|
|
263
|
+
"query",
|
|
264
|
+
"get",
|
|
265
|
+
"reindex",
|
|
266
|
+
"cleanup",
|
|
267
|
+
"verify",
|
|
268
|
+
"register-domain",
|
|
269
|
+
"discover"
|
|
270
|
+
];
|
|
271
|
+
const READ_ACTIONS = new Set([
|
|
272
|
+
"query",
|
|
273
|
+
"get",
|
|
274
|
+
"discover",
|
|
275
|
+
"verify"
|
|
276
|
+
]);
|
|
277
|
+
return { data: actions.map((name) => this.buildEntry((0, ufo.joinURL)(ctx.path, name), {
|
|
278
|
+
id: name,
|
|
279
|
+
meta: {
|
|
280
|
+
kind: "afs:executable",
|
|
281
|
+
effect: READ_ACTIONS.has(name) ? "read" : "write"
|
|
282
|
+
}
|
|
283
|
+
})) };
|
|
284
|
+
}
|
|
285
|
+
readDomains(ctx) {
|
|
286
|
+
return this.buildEntry(ctx.path, { meta: {
|
|
287
|
+
kind: "afs:directory",
|
|
288
|
+
childrenCount: this.domains.size
|
|
289
|
+
} });
|
|
290
|
+
}
|
|
291
|
+
readStatsDir(ctx) {
|
|
292
|
+
return this.buildEntry(ctx.path, { meta: {
|
|
293
|
+
kind: "afs:directory",
|
|
294
|
+
childrenCount: 3
|
|
295
|
+
} });
|
|
296
|
+
}
|
|
297
|
+
readConfigDir(ctx) {
|
|
298
|
+
return this.buildEntry(ctx.path, { meta: {
|
|
299
|
+
kind: "afs:directory",
|
|
300
|
+
childrenCount: 3
|
|
301
|
+
} });
|
|
302
|
+
}
|
|
303
|
+
async listDomains(ctx) {
|
|
304
|
+
return { data: Array.from(this.domains.values()).map((d) => this.buildEntry((0, ufo.joinURL)(ctx.path, d.name), {
|
|
305
|
+
id: d.name,
|
|
306
|
+
content: {
|
|
307
|
+
types: d.types.map((t) => t.name),
|
|
308
|
+
indexable: d.config?.indexable
|
|
309
|
+
}
|
|
310
|
+
})) };
|
|
311
|
+
}
|
|
312
|
+
async listStatsDir(ctx) {
|
|
313
|
+
return { data: [
|
|
314
|
+
"total-entries",
|
|
315
|
+
"active-entries",
|
|
316
|
+
"stale-entries"
|
|
317
|
+
].map((key) => this.buildEntry((0, ufo.joinURL)(ctx.path, key), {
|
|
318
|
+
id: key,
|
|
319
|
+
meta: {
|
|
320
|
+
kind: "afs:value",
|
|
321
|
+
childrenCount: 0
|
|
322
|
+
}
|
|
323
|
+
})) };
|
|
324
|
+
}
|
|
325
|
+
listConfig(ctx) {
|
|
326
|
+
return { data: [
|
|
327
|
+
"llm-provider",
|
|
328
|
+
"embedding-enabled",
|
|
329
|
+
"default-domain"
|
|
330
|
+
].map((key) => this.buildEntry((0, ufo.joinURL)(ctx.path, key), {
|
|
331
|
+
id: key,
|
|
332
|
+
meta: {
|
|
333
|
+
kind: "afs:value",
|
|
334
|
+
childrenCount: 0
|
|
335
|
+
}
|
|
336
|
+
})) };
|
|
337
|
+
}
|
|
338
|
+
listStatsKey() {
|
|
339
|
+
return { data: [] };
|
|
340
|
+
}
|
|
341
|
+
listConfigKey() {
|
|
342
|
+
return { data: [] };
|
|
343
|
+
}
|
|
344
|
+
async listDomainName(ctx) {
|
|
345
|
+
await this.ensureInitialized();
|
|
346
|
+
return { data: (await this.backend.listEntries({ domain: ctx.params.name })).map((e) => this.buildEntry((0, ufo.joinURL)(ctx.path, encodeURIComponent(e.entryPath)), {
|
|
347
|
+
id: e.entryPath,
|
|
348
|
+
content: {
|
|
349
|
+
entryPath: e.entryPath,
|
|
350
|
+
sourcePath: e.sourcePath,
|
|
351
|
+
state: e.state,
|
|
352
|
+
anchorCount: e.anchorCount,
|
|
353
|
+
indexedAt: e.indexedAt,
|
|
354
|
+
updatedAt: e.updatedAt
|
|
355
|
+
},
|
|
356
|
+
meta: {
|
|
357
|
+
kind: "index:entry",
|
|
358
|
+
domain: e.domain,
|
|
359
|
+
state: e.state,
|
|
360
|
+
anchorCount: e.anchorCount,
|
|
361
|
+
childrenCount: 0,
|
|
362
|
+
...e.meta ?? {}
|
|
363
|
+
},
|
|
364
|
+
createdAt: new Date(e.indexedAt),
|
|
365
|
+
updatedAt: new Date(e.updatedAt)
|
|
366
|
+
})) };
|
|
367
|
+
}
|
|
368
|
+
readRootMeta(ctx) {
|
|
369
|
+
const meta = {
|
|
370
|
+
kind: "afs:directory",
|
|
371
|
+
childrenCount: 3
|
|
372
|
+
};
|
|
373
|
+
return this.buildEntry(ctx.path, {
|
|
374
|
+
content: meta,
|
|
375
|
+
meta
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
readDomainsMeta(ctx) {
|
|
379
|
+
const meta = {
|
|
380
|
+
kind: "afs:directory",
|
|
381
|
+
childrenCount: this.domains.size
|
|
382
|
+
};
|
|
383
|
+
return this.buildEntry(ctx.path, {
|
|
384
|
+
content: meta,
|
|
385
|
+
meta
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
readStatsMeta(ctx) {
|
|
389
|
+
const meta = {
|
|
390
|
+
kind: "afs:directory",
|
|
391
|
+
childrenCount: 3
|
|
392
|
+
};
|
|
393
|
+
return this.buildEntry(ctx.path, {
|
|
394
|
+
content: meta,
|
|
395
|
+
meta
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
readConfigMeta(ctx) {
|
|
399
|
+
const meta = {
|
|
400
|
+
kind: "afs:directory",
|
|
401
|
+
childrenCount: 3
|
|
402
|
+
};
|
|
403
|
+
return this.buildEntry(ctx.path, {
|
|
404
|
+
content: meta,
|
|
405
|
+
meta
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
readStatsKeyMeta(ctx) {
|
|
409
|
+
const meta = {
|
|
410
|
+
kind: "afs:value",
|
|
411
|
+
childrenCount: 0
|
|
412
|
+
};
|
|
413
|
+
return this.buildEntry(ctx.path, {
|
|
414
|
+
content: meta,
|
|
415
|
+
meta
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
readConfigKeyMeta(ctx) {
|
|
419
|
+
const meta = {
|
|
420
|
+
kind: "afs:value",
|
|
421
|
+
childrenCount: 0
|
|
422
|
+
};
|
|
423
|
+
return this.buildEntry(ctx.path, {
|
|
424
|
+
content: meta,
|
|
425
|
+
meta
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
async readDomainNameMeta(ctx) {
|
|
429
|
+
await this.ensureInitialized();
|
|
430
|
+
const meta = {
|
|
431
|
+
kind: "afs:directory",
|
|
432
|
+
childrenCount: await this.backend.countEntries({ domain: ctx.params.name })
|
|
433
|
+
};
|
|
434
|
+
return this.buildEntry(ctx.path, {
|
|
435
|
+
content: meta,
|
|
436
|
+
meta
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
readCapabilities(ctx) {
|
|
440
|
+
return this.buildEntry(ctx.path, { content: { operations: this.getOperationsDeclaration() } });
|
|
441
|
+
}
|
|
442
|
+
explainRoot(_ctx) {
|
|
443
|
+
return {
|
|
444
|
+
content: `AFS Index Provider — unified structured indexing.\n\nCombines anchor indexing, full-text search, and optional embedding search. Domains: ${this.domains.size} registered.`,
|
|
445
|
+
format: "text"
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
explainPath(ctx) {
|
|
449
|
+
return {
|
|
450
|
+
content: `Index provider path: /${ctx.params.path}`,
|
|
451
|
+
format: "text"
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
async searchHandler(_ctx, query, options) {
|
|
455
|
+
await this.ensureInitialized();
|
|
456
|
+
try {
|
|
457
|
+
return { data: (await this.queryEngine.query({
|
|
458
|
+
mode: "fts",
|
|
459
|
+
text: query,
|
|
460
|
+
opts: { limit: options?.limit }
|
|
461
|
+
})).map((r) => this.buildEntry(r.entryPath, { content: {
|
|
462
|
+
matchType: r.matchType,
|
|
463
|
+
score: r.score
|
|
464
|
+
} })) };
|
|
465
|
+
} catch {
|
|
466
|
+
return { data: [] };
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async readConfig(ctx) {
|
|
470
|
+
switch (ctx.params.key) {
|
|
471
|
+
case "llm-provider": return this.buildEntry(ctx.path, { content: { value: this.options.llmProvider ?? null } });
|
|
472
|
+
case "embedding-enabled": return this.buildEntry(ctx.path, { content: { value: this.options.embeddingEnabled ?? false } });
|
|
473
|
+
case "default-domain": return this.buildEntry(ctx.path, { content: { value: this.options.defaultDomain ?? "default" } });
|
|
474
|
+
default: return;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
async readStats(ctx) {
|
|
478
|
+
await this.ensureInitialized();
|
|
479
|
+
switch (ctx.params.key) {
|
|
480
|
+
case "total-entries": {
|
|
481
|
+
const count = await this.backend.countEntries({});
|
|
482
|
+
return this.buildEntry(ctx.path, { content: { value: count } });
|
|
483
|
+
}
|
|
484
|
+
case "active-entries": {
|
|
485
|
+
const count = await this.backend.countEntries({ state: "active" });
|
|
486
|
+
return this.buildEntry(ctx.path, { content: { value: count } });
|
|
487
|
+
}
|
|
488
|
+
case "stale-entries": {
|
|
489
|
+
const count = await this.backend.countEntries({ state: "stale" });
|
|
490
|
+
return this.buildEntry(ctx.path, { content: { value: count } });
|
|
491
|
+
}
|
|
492
|
+
default: return;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async execIndex(_ctx, args) {
|
|
496
|
+
const parsed = IndexArgsSchema.safeParse(args);
|
|
497
|
+
if (!parsed.success) return {
|
|
498
|
+
success: false,
|
|
499
|
+
error: {
|
|
500
|
+
code: "VALIDATION_ERROR",
|
|
501
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, IndexArgsSchema)
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
await this.ensureInitialized();
|
|
505
|
+
const { entryPath, anchors: rawAnchors, summary, embeddingSummary, embedding: explicitEmbedding, sourcePath, sourceHash, extractorId, meta } = parsed.data;
|
|
506
|
+
const domain = parsed.data.domain ?? this.options.defaultDomain ?? "default";
|
|
507
|
+
const anchors = rawAnchors ?? [];
|
|
508
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
509
|
+
const entry = {
|
|
510
|
+
entryPath,
|
|
511
|
+
sourcePath,
|
|
512
|
+
sourceHash,
|
|
513
|
+
domain,
|
|
514
|
+
state: "active",
|
|
515
|
+
extractorId,
|
|
516
|
+
indexedAt: now,
|
|
517
|
+
updatedAt: now,
|
|
518
|
+
anchorCount: anchors.length,
|
|
519
|
+
meta
|
|
520
|
+
};
|
|
521
|
+
await this.backend.writeEntry(entry);
|
|
522
|
+
if (anchors.length > 0) await this.backend.writeAnchors(entryPath, anchors);
|
|
523
|
+
if (summary) await this.backend.writeSummary(entryPath, summary);
|
|
524
|
+
if (this.backend.writeEmbedding) {
|
|
525
|
+
const embedding = explicitEmbedding ?? await this.autoEmbed(embeddingSummary ?? summary);
|
|
526
|
+
if (embedding) await this.backend.writeEmbedding(entryPath, embedding);
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
success: true,
|
|
530
|
+
data: {
|
|
531
|
+
entryPath,
|
|
532
|
+
anchorCount: anchors.length
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
async execQuery(_ctx, args) {
|
|
537
|
+
const misplaced = [
|
|
538
|
+
"limit",
|
|
539
|
+
"cursor",
|
|
540
|
+
"anchorFilter",
|
|
541
|
+
"explain",
|
|
542
|
+
"scope",
|
|
543
|
+
"pathPrefix",
|
|
544
|
+
"minConfidence",
|
|
545
|
+
"metaFilter",
|
|
546
|
+
"embeddingFloor",
|
|
547
|
+
"minResults"
|
|
548
|
+
].filter((k) => k in args);
|
|
549
|
+
if (misplaced.length > 0) return {
|
|
550
|
+
success: false,
|
|
551
|
+
error: {
|
|
552
|
+
code: "VALIDATION_ERROR",
|
|
553
|
+
message: `Parameter(s) ${misplaced.map((k) => `"${k}"`).join(", ")} must be nested inside "opts" (e.g. { opts: { ${misplaced[0]}: ... } })`
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
const parsed = QueryArgsSchema.safeParse(args);
|
|
557
|
+
if (!parsed.success) return {
|
|
558
|
+
success: false,
|
|
559
|
+
error: {
|
|
560
|
+
code: "VALIDATION_ERROR",
|
|
561
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, QueryArgsSchema)
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
await this.ensureInitialized();
|
|
565
|
+
if (this.follower && !parsed.data.opts?.cursor) {
|
|
566
|
+
const { task, started } = this.startCatchUpOnce();
|
|
567
|
+
const schedule = this.options.scheduleBackground;
|
|
568
|
+
if (schedule) {
|
|
569
|
+
if (started) schedule(task);
|
|
570
|
+
} else await task;
|
|
571
|
+
}
|
|
572
|
+
const { anchors, text, embeddingText, opts } = parsed.data;
|
|
573
|
+
const mode = parsed.data.mode ?? this.options.defaultSearchMode ?? require_types.DEFAULT_SEARCH_MODE;
|
|
574
|
+
const ftsMode = parsed.data.ftsMode;
|
|
575
|
+
const rewriter = parsed.data.rewriter ?? "none";
|
|
576
|
+
if (rewriter !== "none" && opts?.cursor) return {
|
|
577
|
+
success: false,
|
|
578
|
+
error: {
|
|
579
|
+
code: "VALIDATION_ERROR",
|
|
580
|
+
message: "Query cursor is not supported with rewriter mode"
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
if (rewriter === "multi-query" && !parsed.data.embedding && (text || embeddingText) && this.resolveChatProvider()) {
|
|
584
|
+
const original = embeddingText ?? text;
|
|
585
|
+
return {
|
|
586
|
+
success: true,
|
|
587
|
+
data: { results: await this.queryWithMultiQuery({
|
|
588
|
+
original,
|
|
589
|
+
ftsText: text,
|
|
590
|
+
mode,
|
|
591
|
+
anchors,
|
|
592
|
+
opts
|
|
593
|
+
}) }
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
const embedding = parsed.data.embedding ?? (mode === "semantic" ? await this.autoEmbed(embeddingText ?? text, "query") : null);
|
|
597
|
+
let page;
|
|
598
|
+
try {
|
|
599
|
+
page = await this.queryEngine.queryPage({
|
|
600
|
+
mode,
|
|
601
|
+
ftsMode,
|
|
602
|
+
anchors,
|
|
603
|
+
text,
|
|
604
|
+
embedding: embedding ?? void 0,
|
|
605
|
+
opts: this.options.embeddingFloor != null && opts?.embeddingFloor == null ? {
|
|
606
|
+
...opts,
|
|
607
|
+
embeddingFloor: this.options.embeddingFloor
|
|
608
|
+
} : opts,
|
|
609
|
+
boundDomains: this.options.boundDomains
|
|
610
|
+
});
|
|
611
|
+
} catch (err) {
|
|
612
|
+
if (err instanceof Error && err.message === "Invalid or stale query cursor") return {
|
|
613
|
+
success: false,
|
|
614
|
+
error: {
|
|
615
|
+
code: "VALIDATION_ERROR",
|
|
616
|
+
message: err.message
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
throw err;
|
|
620
|
+
}
|
|
621
|
+
const { results } = page;
|
|
622
|
+
if (opts?.explain) {
|
|
623
|
+
const explanation = await this.buildExplanation({
|
|
624
|
+
mode,
|
|
625
|
+
text,
|
|
626
|
+
embeddingText,
|
|
627
|
+
anchors,
|
|
628
|
+
opts,
|
|
629
|
+
embeddingUsed: embedding !== null && embedding !== void 0,
|
|
630
|
+
results
|
|
631
|
+
});
|
|
632
|
+
log.debug("query explain", {
|
|
633
|
+
...explanation,
|
|
634
|
+
results: results.map((r) => ({
|
|
635
|
+
path: r.entryPath,
|
|
636
|
+
matchType: r.matchType,
|
|
637
|
+
score: r.score,
|
|
638
|
+
explanation: r.explanation
|
|
639
|
+
}))
|
|
640
|
+
});
|
|
641
|
+
return {
|
|
642
|
+
success: true,
|
|
643
|
+
data: {
|
|
644
|
+
results,
|
|
645
|
+
explanation,
|
|
646
|
+
...page.meta ? { meta: page.meta } : {}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
return {
|
|
651
|
+
success: true,
|
|
652
|
+
data: {
|
|
653
|
+
results,
|
|
654
|
+
...page.meta ? { meta: page.meta } : {}
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Build a "why did the query return what it returned" diagnostic.
|
|
660
|
+
*
|
|
661
|
+
* Cheap probes only: scope cardinality (countEntries), result-type
|
|
662
|
+
* breakdown (already in the result list), echo of the parsed inputs.
|
|
663
|
+
* Does not re-run the query — purely additive per planning doc §P4.
|
|
664
|
+
*/
|
|
665
|
+
async buildExplanation(args) {
|
|
666
|
+
const { mode, text, embeddingText, anchors, opts, embeddingUsed, results } = args;
|
|
667
|
+
const scopeFilter = opts?.scope;
|
|
668
|
+
const domain = scopeFilter ? scopeFilter.replace(/^\/+/, "") : void 0;
|
|
669
|
+
let totalIndexedInScope;
|
|
670
|
+
try {
|
|
671
|
+
totalIndexedInScope = await this.backend.countEntries({
|
|
672
|
+
...domain ? { domain } : {},
|
|
673
|
+
...scopeFilter ? { scope: scopeFilter } : {},
|
|
674
|
+
state: "active"
|
|
675
|
+
});
|
|
676
|
+
} catch {
|
|
677
|
+
totalIndexedInScope = void 0;
|
|
678
|
+
}
|
|
679
|
+
const matchTypeBreakdown = {};
|
|
680
|
+
for (const r of results) matchTypeBreakdown[r.matchType] = (matchTypeBreakdown[r.matchType] ?? 0) + 1;
|
|
681
|
+
let whyEmpty;
|
|
682
|
+
if (results.length === 0) if (totalIndexedInScope === 0) whyEmpty = `No entries indexed in scope "${scopeFilter ?? "(no scope)"}" — likely a domain mismatch or missing ingestion.`;
|
|
683
|
+
else if (mode === "fts" && !text) whyEmpty = "FTS mode requested but `text` was empty.";
|
|
684
|
+
else if (mode === "anchor" && (!anchors || anchors.length === 0)) whyEmpty = "Anchor mode requested but `anchors` was empty.";
|
|
685
|
+
else if (mode === "semantic" && !embeddingUsed) whyEmpty = "Semantic mode requested but no embedding was generated (no `text`/`embeddingText` and no auto-embed available).";
|
|
686
|
+
else whyEmpty = `Index has ${totalIndexedInScope ?? "?"} entries in scope but none matched query.`;
|
|
687
|
+
return {
|
|
688
|
+
mode,
|
|
689
|
+
ftsQuery: text,
|
|
690
|
+
embeddingText: embeddingText ?? text,
|
|
691
|
+
embeddingUsed,
|
|
692
|
+
anchors,
|
|
693
|
+
scope: scopeFilter,
|
|
694
|
+
metaFilter: opts?.metaFilter,
|
|
695
|
+
totalIndexedInScope,
|
|
696
|
+
resultCount: results.length,
|
|
697
|
+
matchTypeBreakdown,
|
|
698
|
+
whyEmpty
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
async execGet(_ctx, args) {
|
|
702
|
+
const parsed = GetArgsSchema.safeParse(args);
|
|
703
|
+
if (!parsed.success) return {
|
|
704
|
+
success: false,
|
|
705
|
+
error: {
|
|
706
|
+
code: "VALIDATION_ERROR",
|
|
707
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, GetArgsSchema)
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
await this.ensureInitialized();
|
|
711
|
+
const paths = parsed.data.entryPaths ?? (parsed.data.entryPath ? [parsed.data.entryPath] : []);
|
|
712
|
+
if (paths.length === 0) return {
|
|
713
|
+
success: false,
|
|
714
|
+
error: {
|
|
715
|
+
code: "VALIDATION_ERROR",
|
|
716
|
+
message: "entryPath or entryPaths required"
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
const bound = this.options.boundDomains;
|
|
720
|
+
const inBounds = (entry) => !entry || !bound || bound.length === 0 || bound.includes(entry.domain);
|
|
721
|
+
const entries = [];
|
|
722
|
+
for (const p of paths) {
|
|
723
|
+
const entry = await this.backend.getEntry(p);
|
|
724
|
+
if (!inBounds(entry)) {
|
|
725
|
+
entries.push({
|
|
726
|
+
entryPath: p,
|
|
727
|
+
entry: null,
|
|
728
|
+
summary: null
|
|
729
|
+
});
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const summary = this.backend.getSummary ? await this.backend.getSummary(p) : null;
|
|
733
|
+
entries.push({
|
|
734
|
+
entryPath: p,
|
|
735
|
+
entry,
|
|
736
|
+
summary
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
success: true,
|
|
741
|
+
data: { entries }
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
async execReindex(_ctx, args) {
|
|
745
|
+
const parsed = ReindexArgsSchema.safeParse(args);
|
|
746
|
+
if (!parsed.success) return {
|
|
747
|
+
success: false,
|
|
748
|
+
error: {
|
|
749
|
+
code: "VALIDATION_ERROR",
|
|
750
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, ReindexArgsSchema)
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
await this.ensureInitialized();
|
|
754
|
+
const { scope, extractorId, domain } = parsed.data;
|
|
755
|
+
return {
|
|
756
|
+
success: true,
|
|
757
|
+
data: { staleCount: await this.backend.markStale({
|
|
758
|
+
scope,
|
|
759
|
+
extractorId,
|
|
760
|
+
domain,
|
|
761
|
+
boundDomains: this.options.boundDomains
|
|
762
|
+
}) }
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
async execCleanup(_ctx, args) {
|
|
766
|
+
const parsed = CleanupArgsSchema.safeParse(args);
|
|
767
|
+
if (!parsed.success) return {
|
|
768
|
+
success: false,
|
|
769
|
+
error: {
|
|
770
|
+
code: "VALIDATION_ERROR",
|
|
771
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, CleanupArgsSchema)
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
await this.ensureInitialized();
|
|
775
|
+
const { state, domain, scope } = parsed.data;
|
|
776
|
+
const paths = (await this.backend.listEntries({
|
|
777
|
+
state,
|
|
778
|
+
domain,
|
|
779
|
+
scope,
|
|
780
|
+
boundDomains: this.options.boundDomains
|
|
781
|
+
})).map((e) => e.entryPath);
|
|
782
|
+
let purgedCount;
|
|
783
|
+
if (this.backend.purgeEntries) purgedCount = await this.backend.purgeEntries(paths);
|
|
784
|
+
else {
|
|
785
|
+
purgedCount = 0;
|
|
786
|
+
for (const path of paths) {
|
|
787
|
+
await this.backend.removeAnchors(path);
|
|
788
|
+
await this.backend.removeSummary(path);
|
|
789
|
+
if (this.backend.removeEmbedding) await this.backend.removeEmbedding(path);
|
|
790
|
+
await this.backend.removeEntry(path);
|
|
791
|
+
purgedCount++;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return {
|
|
795
|
+
success: true,
|
|
796
|
+
data: { purgedCount }
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* `verify` — index self-stats for drift detection.
|
|
801
|
+
*
|
|
802
|
+
* Returns the index's own view of what it holds: total entries, breakdown
|
|
803
|
+
* by domain and state, and how many entries have zero anchors (a common
|
|
804
|
+
* symptom of a half-finished or corrupted index). A caller compares these
|
|
805
|
+
* against the source-of-truth provider (e.g. memory's `health.totalEntries`)
|
|
806
|
+
* to decide whether to trigger a rebuild — without guessing from empty
|
|
807
|
+
* query results.
|
|
808
|
+
*/
|
|
809
|
+
async execVerify(_ctx, args) {
|
|
810
|
+
const parsed = VerifyArgsSchema.safeParse(args);
|
|
811
|
+
if (!parsed.success) return {
|
|
812
|
+
success: false,
|
|
813
|
+
error: {
|
|
814
|
+
code: "VALIDATION_ERROR",
|
|
815
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, VerifyArgsSchema)
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
await this.ensureInitialized();
|
|
819
|
+
const { domain, scope } = parsed.data;
|
|
820
|
+
const entries = await this.backend.listEntries({
|
|
821
|
+
...domain ? { domain } : {},
|
|
822
|
+
...scope ? { scope } : {},
|
|
823
|
+
boundDomains: this.options.boundDomains
|
|
824
|
+
});
|
|
825
|
+
const byDomain = {};
|
|
826
|
+
const byState = {};
|
|
827
|
+
let anchorlessCount = 0;
|
|
828
|
+
for (const e of entries) {
|
|
829
|
+
byDomain[e.domain] = (byDomain[e.domain] ?? 0) + 1;
|
|
830
|
+
byState[e.state] = (byState[e.state] ?? 0) + 1;
|
|
831
|
+
if (!e.anchorCount) anchorlessCount++;
|
|
832
|
+
}
|
|
833
|
+
return {
|
|
834
|
+
success: true,
|
|
835
|
+
data: {
|
|
836
|
+
totalEntries: entries.length,
|
|
837
|
+
byDomain,
|
|
838
|
+
byState,
|
|
839
|
+
anchorlessCount,
|
|
840
|
+
...domain ? { domain } : {},
|
|
841
|
+
...scope ? { scope } : {}
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Register one or more domains from a manifest projection, wiring each
|
|
847
|
+
* declared anchor type into the key registry (normalizedValue by default).
|
|
848
|
+
*
|
|
849
|
+
* This is the non-exec entry point for session-view wiring: called at index
|
|
850
|
+
* handle creation time so domains survive Worker restarts (the in-memory
|
|
851
|
+
* handles are rebuilt from the manifest on every cold start).
|
|
852
|
+
*/
|
|
853
|
+
registerDomains(domains) {
|
|
854
|
+
for (const domain of domains) {
|
|
855
|
+
this.domains.set(domain.name, {
|
|
856
|
+
name: domain.name,
|
|
857
|
+
...domain.scope ? { scope: domain.scope } : {},
|
|
858
|
+
types: domain.anchorTypes,
|
|
859
|
+
...domain.config ? { config: domain.config } : {}
|
|
860
|
+
});
|
|
861
|
+
this.registerAnchorTypeKeyGenerators(domain.anchorTypes);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Attach a changelog follower so execQuery triggers a lightweight catch-up
|
|
866
|
+
* before returning results. Called once at index handle creation time by the
|
|
867
|
+
* CF session-view (cf-session-view.ts) after building the follower.
|
|
868
|
+
*/
|
|
869
|
+
setFollower(follower) {
|
|
870
|
+
this.follower = follower;
|
|
871
|
+
follower.setEmbedder((text) => this.autoEmbed(text, "passage"));
|
|
872
|
+
}
|
|
873
|
+
startCatchUpOnce() {
|
|
874
|
+
if (this.catchUpInflight) return {
|
|
875
|
+
task: this.catchUpInflight,
|
|
876
|
+
started: false
|
|
877
|
+
};
|
|
878
|
+
const follower = this.follower;
|
|
879
|
+
if (!follower) return {
|
|
880
|
+
task: Promise.resolve(),
|
|
881
|
+
started: false
|
|
882
|
+
};
|
|
883
|
+
let task;
|
|
884
|
+
task = follower.runCatchUp().catch(() => {}).finally(() => {
|
|
885
|
+
if (this.catchUpInflight === task) this.catchUpInflight = void 0;
|
|
886
|
+
});
|
|
887
|
+
this.catchUpInflight = task;
|
|
888
|
+
return {
|
|
889
|
+
task,
|
|
890
|
+
started: true
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Wire declared anchor types into the shared key registry. Types with an
|
|
895
|
+
* explicit function keyGenerator use it directly; types without one get a
|
|
896
|
+
* normalizedValue generator (lowercase+trim) instead of the exact fallback
|
|
897
|
+
* (JSON.stringify, field-order/case-sensitive).
|
|
898
|
+
*
|
|
899
|
+
* Built-in types (time/location/person/topic — registered in
|
|
900
|
+
* `KeyGeneratorRegistry`'s constructor) are never overwritten here, even if
|
|
901
|
+
* a manifest or `.index/anchor-types/` declaration reuses one of those
|
|
902
|
+
* names: the built-in's multi-dimensional key generator always wins (issue
|
|
903
|
+
* #934 P0 — see `KeyGeneratorRegistry.isBuiltin`). Shared by
|
|
904
|
+
* `registerDomains` (manifest wiring) and `discoverProvider` (`.index/`
|
|
905
|
+
* convention wiring) — previously duplicated inline in both.
|
|
906
|
+
*/
|
|
907
|
+
registerAnchorTypeKeyGenerators(anchorTypes) {
|
|
908
|
+
for (const anchorType of anchorTypes) {
|
|
909
|
+
if (this.keyRegistry.isBuiltin(anchorType.name)) continue;
|
|
910
|
+
if (typeof anchorType.keyGenerator === "function") this.keyRegistry.register(anchorType.name, anchorType.keyGenerator);
|
|
911
|
+
else this.keyRegistry.register(anchorType.name, require_normalized.createNormalizedValueGenerator());
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async execRegisterDomain(_ctx, args) {
|
|
915
|
+
const parsed = RegisterDomainArgsSchema.safeParse(args);
|
|
916
|
+
if (!parsed.success) return {
|
|
917
|
+
success: false,
|
|
918
|
+
error: {
|
|
919
|
+
code: "VALIDATION_ERROR",
|
|
920
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, RegisterDomainArgsSchema)
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
const name = parsed.data.name;
|
|
924
|
+
const types = parsed.data.types ?? [];
|
|
925
|
+
this.domains.set(name, {
|
|
926
|
+
name,
|
|
927
|
+
types
|
|
928
|
+
});
|
|
929
|
+
return {
|
|
930
|
+
success: true,
|
|
931
|
+
data: {
|
|
932
|
+
domain: name,
|
|
933
|
+
typeCount: types.length
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
async execDiscover(_ctx, args) {
|
|
938
|
+
const parsed = DiscoverArgsSchema.safeParse(args);
|
|
939
|
+
if (!parsed.success) return {
|
|
940
|
+
success: false,
|
|
941
|
+
error: {
|
|
942
|
+
code: "VALIDATION_ERROR",
|
|
943
|
+
message: (0, _aigne_afs.explainZodError)(parsed.error, DiscoverArgsSchema)
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
if (!this.afsRoot) return {
|
|
947
|
+
success: false,
|
|
948
|
+
error: {
|
|
949
|
+
code: "NOT_MOUNTED",
|
|
950
|
+
message: "Provider not mounted (no AFS root)"
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
const { providerPath } = parsed.data;
|
|
954
|
+
const domainName = parsed.data.domain ?? providerPath.split("/").filter(Boolean).pop() ?? "unknown";
|
|
955
|
+
const result = await this.discoverProvider(providerPath, domainName);
|
|
956
|
+
return {
|
|
957
|
+
success: true,
|
|
958
|
+
data: {
|
|
959
|
+
domain: result.domainName,
|
|
960
|
+
anchorTypes: result.anchorTypes.map((t) => t.name),
|
|
961
|
+
config: result.config,
|
|
962
|
+
extractor: result.extractor
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
async discoverProvider(providerPath, domainName) {
|
|
967
|
+
const root = this.afsRoot;
|
|
968
|
+
let config;
|
|
969
|
+
try {
|
|
970
|
+
const content = (await root.read((0, ufo.joinURL)(providerPath, ".index", "config"))).data?.content;
|
|
971
|
+
if (content && typeof content === "object") config = content;
|
|
972
|
+
} catch {}
|
|
973
|
+
const anchorTypes = [];
|
|
974
|
+
try {
|
|
975
|
+
const typesPath = (0, ufo.joinURL)(providerPath, ".index", "anchor-types");
|
|
976
|
+
const typesList = await root.list(typesPath);
|
|
977
|
+
for (const entry of typesList.data) if (entry.id) try {
|
|
978
|
+
const content = (await root.read((0, ufo.joinURL)(typesPath, entry.id))).data?.content;
|
|
979
|
+
if (content && typeof content === "object" && "name" in content) anchorTypes.push(content);
|
|
980
|
+
} catch {}
|
|
981
|
+
} catch {}
|
|
982
|
+
let extractor;
|
|
983
|
+
try {
|
|
984
|
+
const content = (await root.read((0, ufo.joinURL)(providerPath, ".index", "extractor"))).data?.content;
|
|
985
|
+
if (content && typeof content === "object" && "path" in content) extractor = content;
|
|
986
|
+
} catch {}
|
|
987
|
+
this.domains.set(domainName, {
|
|
988
|
+
name: domainName,
|
|
989
|
+
scope: providerPath,
|
|
990
|
+
types: anchorTypes,
|
|
991
|
+
config,
|
|
992
|
+
extractor
|
|
993
|
+
});
|
|
994
|
+
this.registerAnchorTypeKeyGenerators(anchorTypes);
|
|
995
|
+
if (extractor) this.extractors.register(domainName, {
|
|
996
|
+
id: `${domainName}-extractor`,
|
|
997
|
+
scope: providerPath,
|
|
998
|
+
mode: extractor.mode,
|
|
999
|
+
extractor: extractor.path
|
|
1000
|
+
});
|
|
1001
|
+
return {
|
|
1002
|
+
domainName,
|
|
1003
|
+
anchorTypes,
|
|
1004
|
+
config,
|
|
1005
|
+
extractor
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Check if a path should be indexed based on .index/config rules.
|
|
1010
|
+
* Finds the domain whose scope covers the path and applies its config.
|
|
1011
|
+
* If no matching domain config exists, allows indexing by default.
|
|
1012
|
+
*/
|
|
1013
|
+
shouldIndex(path, _domain) {
|
|
1014
|
+
let config;
|
|
1015
|
+
for (const domainDef of this.domains.values()) if (domainDef.scope && path.startsWith(domainDef.scope) && domainDef.config) {
|
|
1016
|
+
config = domainDef.config;
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
if (!config) return true;
|
|
1020
|
+
const { indexable, skip } = config;
|
|
1021
|
+
if (skip?.some((pattern) => matchGlob(path, pattern))) return false;
|
|
1022
|
+
if (indexable && indexable.length > 0) return indexable.some((pattern) => matchGlob(path, pattern));
|
|
1023
|
+
return true;
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Auto-generate embedding via LLM provider if configured and enabled.
|
|
1027
|
+
* Returns null if LLM is not available or embedding is disabled.
|
|
1028
|
+
*
|
|
1029
|
+
* @param type - "passage" for write-time (document), "query" for query-time.
|
|
1030
|
+
* Models like E5 require different prefixes for query vs passage.
|
|
1031
|
+
*/
|
|
1032
|
+
async autoEmbed(text, type = "passage") {
|
|
1033
|
+
return require_embed.createEmbedder({
|
|
1034
|
+
embeddingEnabled: this.options.embeddingEnabled,
|
|
1035
|
+
llmProvider: this.options.llmProvider,
|
|
1036
|
+
embedRoot: this.options.embeddingAfsRoot ?? this.afsRoot
|
|
1037
|
+
})(text, type);
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Resolve the chat-action path used by query rewriters. Prefers an
|
|
1041
|
+
* explicit `chatProvider` config; falls back to `<llmProvider>/.actions/chat`
|
|
1042
|
+
* because the same AigneHub default endpoint hosts both embedding
|
|
1043
|
+
* and chat actions in practice.
|
|
1044
|
+
*/
|
|
1045
|
+
resolveChatProvider() {
|
|
1046
|
+
if (this.options.chatProvider) return this.options.chatProvider;
|
|
1047
|
+
if (this.options.llmProvider) return (0, ufo.joinURL)(this.options.llmProvider, ".actions", "chat");
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Generate paraphrases of `question` via the chat provider. Returns
|
|
1052
|
+
* the paraphrases plus the original question (the original is kept
|
|
1053
|
+
* as a safety net in case the rewrites drift). Returns just `[question]`
|
|
1054
|
+
* if the chat call fails — degrades gracefully to baseline.
|
|
1055
|
+
*/
|
|
1056
|
+
async generateParaphrases(question) {
|
|
1057
|
+
const chatPath = this.resolveChatProvider();
|
|
1058
|
+
const chatRoot = this.options.embeddingAfsRoot ?? this.afsRoot;
|
|
1059
|
+
if (!chatPath || !chatRoot) return [question];
|
|
1060
|
+
try {
|
|
1061
|
+
const result = await chatRoot.exec(chatPath, { messages: [{
|
|
1062
|
+
role: "system",
|
|
1063
|
+
content: "Rewrite a search query as 3 different paraphrases that mean the same thing but use different vocabulary. Each paraphrase should be a single line. Output exactly 3 lines, no numbering, no preamble. Keep them concrete and search-ready."
|
|
1064
|
+
}, {
|
|
1065
|
+
role: "user",
|
|
1066
|
+
content: question
|
|
1067
|
+
}] }, {});
|
|
1068
|
+
if (!result.success || !result.data) return [question];
|
|
1069
|
+
const data = result.data;
|
|
1070
|
+
if (!data.text) return [question];
|
|
1071
|
+
return [question, ...data.text.split("\n").map((l) => l.replace(/^\s*[-*0-9.)\]]+\s*/, "").trim()).filter((l) => l.length > 0).slice(0, 3)];
|
|
1072
|
+
} catch {
|
|
1073
|
+
return [question];
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Multi-query retrieval: generate N paraphrases, embed each, run a
|
|
1078
|
+
* separate semantic query per variant, and RRF-fuse the result lists.
|
|
1079
|
+
* The `index-diverse-bench` shows 2.6× MRR lift on paraphrase queries
|
|
1080
|
+
* with this strategy. Each variant pulls top-(2×limit) so the fusion
|
|
1081
|
+
* has enough candidates to differentiate; the final list is trimmed
|
|
1082
|
+
* to the caller's requested limit.
|
|
1083
|
+
*/
|
|
1084
|
+
async queryWithMultiQuery(input) {
|
|
1085
|
+
const { original, ftsText, mode, anchors, opts } = input;
|
|
1086
|
+
const variants = await this.generateParaphrases(original);
|
|
1087
|
+
const requestedLimit = opts?.limit ?? 10;
|
|
1088
|
+
const fanoutLimit = Math.max(requestedLimit * 2, 10);
|
|
1089
|
+
const lists = [];
|
|
1090
|
+
for (const variant of variants) {
|
|
1091
|
+
const embedding = await this.autoEmbed(variant, "query");
|
|
1092
|
+
const variantResults = await this.queryEngine.query({
|
|
1093
|
+
mode,
|
|
1094
|
+
anchors,
|
|
1095
|
+
text: variant === original ? ftsText : variant,
|
|
1096
|
+
embedding: embedding ?? void 0,
|
|
1097
|
+
opts: {
|
|
1098
|
+
...opts,
|
|
1099
|
+
limit: fanoutLimit
|
|
1100
|
+
},
|
|
1101
|
+
boundDomains: this.options.boundDomains
|
|
1102
|
+
});
|
|
1103
|
+
lists.push({ entries: variantResults });
|
|
1104
|
+
}
|
|
1105
|
+
const k = 60;
|
|
1106
|
+
const scores = /* @__PURE__ */ new Map();
|
|
1107
|
+
for (const list of lists) for (let i = 0; i < list.entries.length; i++) {
|
|
1108
|
+
const entry = list.entries[i];
|
|
1109
|
+
const contribution = 1 / (k + i + 1);
|
|
1110
|
+
const existing = scores.get(entry.entryPath);
|
|
1111
|
+
if (existing) existing.score += contribution;
|
|
1112
|
+
else scores.set(entry.entryPath, {
|
|
1113
|
+
entry,
|
|
1114
|
+
score: contribution
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
return [...scores.values()].sort((a, b) => b.score - a.score).slice(0, requestedLimit).map(({ entry, score }) => ({
|
|
1118
|
+
...entry,
|
|
1119
|
+
score
|
|
1120
|
+
}));
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/")], AFSIndex.prototype, "readRoot", null);
|
|
1124
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/")], AFSIndex.prototype, "listRoot", null);
|
|
1125
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/")], AFSIndex.prototype, "listActions", null);
|
|
1126
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/domains")], AFSIndex.prototype, "readDomains", null);
|
|
1127
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/stats")], AFSIndex.prototype, "readStatsDir", null);
|
|
1128
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/config")], AFSIndex.prototype, "readConfigDir", null);
|
|
1129
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/domains")], AFSIndex.prototype, "listDomains", null);
|
|
1130
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/stats")], AFSIndex.prototype, "listStatsDir", null);
|
|
1131
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/config")], AFSIndex.prototype, "listConfig", null);
|
|
1132
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/stats/:key")], AFSIndex.prototype, "listStatsKey", null);
|
|
1133
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/config/:key")], AFSIndex.prototype, "listConfigKey", null);
|
|
1134
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/domains/:name")], AFSIndex.prototype, "listDomainName", null);
|
|
1135
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/")], AFSIndex.prototype, "readRootMeta", null);
|
|
1136
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/domains")], AFSIndex.prototype, "readDomainsMeta", null);
|
|
1137
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/stats")], AFSIndex.prototype, "readStatsMeta", null);
|
|
1138
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/config")], AFSIndex.prototype, "readConfigMeta", null);
|
|
1139
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/stats/:key")], AFSIndex.prototype, "readStatsKeyMeta", null);
|
|
1140
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/config/:key")], AFSIndex.prototype, "readConfigKeyMeta", null);
|
|
1141
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/domains/:name")], AFSIndex.prototype, "readDomainNameMeta", null);
|
|
1142
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/.meta/.capabilities")], AFSIndex.prototype, "readCapabilities", null);
|
|
1143
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/")], AFSIndex.prototype, "explainRoot", null);
|
|
1144
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/:path*")], AFSIndex.prototype, "explainPath", null);
|
|
1145
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Search)("/:path*")], AFSIndex.prototype, "searchHandler", null);
|
|
1146
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/config/:key")], AFSIndex.prototype, "readConfig", null);
|
|
1147
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/stats/:key")], AFSIndex.prototype, "readStats", null);
|
|
1148
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "index")], AFSIndex.prototype, "execIndex", null);
|
|
1149
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "query")], AFSIndex.prototype, "execQuery", null);
|
|
1150
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "get")], AFSIndex.prototype, "execGet", null);
|
|
1151
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "reindex")], AFSIndex.prototype, "execReindex", null);
|
|
1152
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "cleanup")], AFSIndex.prototype, "execCleanup", null);
|
|
1153
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "verify")], AFSIndex.prototype, "execVerify", null);
|
|
1154
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "register-domain")], AFSIndex.prototype, "execRegisterDomain", null);
|
|
1155
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "discover")], AFSIndex.prototype, "execDiscover", null);
|
|
1156
|
+
/**
|
|
1157
|
+
* Simple glob matching — supports `*` (single segment) and `**` (multi-segment).
|
|
1158
|
+
* Also matches bare extensions like `*.md` against any path ending with `.md`.
|
|
1159
|
+
*/
|
|
1160
|
+
function matchGlob(path, pattern) {
|
|
1161
|
+
const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "⧫").replace(/\*/g, "[^/]*").replace(/⧫/g, ".*").replace(/\?/g, "[^/]");
|
|
1162
|
+
return (/* @__PURE__ */ new RegExp(`(^|/)${regexStr}$`)).test(path);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
//#endregion
|
|
1166
|
+
exports.AFSIndex = AFSIndex;
|