folderblog 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -48
- package/dist/chunk-2TZSVPNP.cjs +148 -0
- package/dist/chunk-3RG5ZIWI.js +8 -0
- package/dist/chunk-6TFXNIO6.cjs +495 -0
- package/dist/chunk-B43UAOPC.js +475 -0
- package/dist/chunk-D26H5722.js +132 -0
- package/dist/chunk-E7PYGJA7.cjs +39 -0
- package/dist/chunk-J3Y3HEBF.cjs +1858 -0
- package/dist/chunk-K76XLEC7.js +76 -0
- package/dist/chunk-LPPBVXJ7.js +1786 -0
- package/dist/chunk-OBGZSXTJ.cjs +10 -0
- package/dist/chunk-Q6EXKX6K.js +17 -0
- package/dist/chunk-Q6EYTOTM.cjs +78 -0
- package/dist/chunk-UCXXH2MP.cjs +20 -0
- package/dist/chunk-XQD3UUL5.js +34 -0
- package/dist/cli/bin.cjs +25 -0
- package/dist/cli/bin.d.cts +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +23 -0
- package/dist/cli/index.cjs +22 -0
- package/dist/cli/index.d.cts +39 -0
- package/dist/cli/index.d.ts +39 -0
- package/dist/cli/index.js +15 -0
- package/dist/config-ADPY6IQS.d.cts +473 -0
- package/dist/config-Dctsdeo6.d.ts +473 -0
- package/dist/index.cjs +458 -1
- package/dist/index.d.cts +78 -9
- package/dist/index.d.ts +78 -9
- package/dist/index.js +100 -1
- package/dist/local/index.cjs +785 -0
- package/dist/local/index.d.cts +268 -0
- package/dist/local/index.d.ts +268 -0
- package/dist/local/index.js +772 -0
- package/dist/output-0P0br3Jc.d.cts +452 -0
- package/dist/output-0P0br3Jc.d.ts +452 -0
- package/dist/plugins/embed-cloudflare-ai.cjs +166 -0
- package/dist/plugins/embed-cloudflare-ai.d.cts +73 -0
- package/dist/plugins/embed-cloudflare-ai.d.ts +73 -0
- package/dist/plugins/embed-cloudflare-ai.js +156 -0
- package/dist/plugins/embed-transformers.cjs +121 -0
- package/dist/plugins/embed-transformers.d.cts +55 -0
- package/dist/plugins/embed-transformers.d.ts +55 -0
- package/dist/plugins/embed-transformers.js +113 -0
- package/dist/plugins/similarity.cjs +19 -0
- package/dist/plugins/similarity.d.cts +41 -0
- package/dist/plugins/similarity.d.ts +41 -0
- package/dist/plugins/similarity.js +2 -0
- package/dist/processor/index.cjs +349 -0
- package/dist/processor/index.d.cts +495 -0
- package/dist/processor/index.d.ts +495 -0
- package/dist/processor/index.js +4 -0
- package/dist/processor/plugins.cjs +63 -0
- package/dist/processor/plugins.d.cts +176 -0
- package/dist/processor/plugins.d.ts +176 -0
- package/dist/processor/plugins.js +2 -0
- package/dist/processor/types.cjs +67 -0
- package/dist/processor/types.d.cts +48 -0
- package/dist/processor/types.d.ts +48 -0
- package/dist/processor/types.js +2 -0
- package/dist/seo/index.cjs +289 -0
- package/dist/seo/index.d.cts +95 -0
- package/dist/seo/index.d.ts +95 -0
- package/dist/seo/index.js +274 -0
- package/dist/server/index.cjs +33 -0
- package/dist/server/index.d.cts +56 -0
- package/dist/server/index.d.ts +56 -0
- package/dist/server/index.js +31 -0
- package/package.json +98 -11
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs/promises');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
9
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
10
|
+
|
|
11
|
+
// ../processor/src/plugins/manager.ts
|
|
12
|
+
var PluginManager = class {
|
|
13
|
+
plugins = /* @__PURE__ */ new Map();
|
|
14
|
+
config;
|
|
15
|
+
outputDir;
|
|
16
|
+
issues;
|
|
17
|
+
log;
|
|
18
|
+
initialized = false;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config.config;
|
|
21
|
+
this.outputDir = config.outputDir;
|
|
22
|
+
this.issues = config.issues;
|
|
23
|
+
this.log = config.log ?? defaultLogger;
|
|
24
|
+
}
|
|
25
|
+
// --------------------------------------------------------------------------
|
|
26
|
+
// Initialization
|
|
27
|
+
// --------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Initialize all plugins in dependency order
|
|
30
|
+
*/
|
|
31
|
+
async initialize() {
|
|
32
|
+
if (this.initialized) {
|
|
33
|
+
this.log("Plugin manager already initialized", "warn");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const pluginConfigs = this.config.plugins ?? {};
|
|
37
|
+
const allPlugins = Object.values(pluginConfigs).filter(
|
|
38
|
+
(p) => p != null
|
|
39
|
+
);
|
|
40
|
+
if (allPlugins.length === 0) {
|
|
41
|
+
this.log("No plugins configured", "debug");
|
|
42
|
+
this.initialized = true;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.log(`Initializing ${allPlugins.length} plugin(s)...`, "info");
|
|
46
|
+
const sorted = topologicalSort(allPlugins);
|
|
47
|
+
const context = this.createContext();
|
|
48
|
+
for (const plugin of sorted) {
|
|
49
|
+
await this.initializePlugin(plugin, context);
|
|
50
|
+
}
|
|
51
|
+
this.initialized = true;
|
|
52
|
+
this.log(`All plugins initialized successfully`, "info");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Initialize a single plugin
|
|
56
|
+
*/
|
|
57
|
+
async initializePlugin(plugin, context) {
|
|
58
|
+
const { name, requires } = plugin;
|
|
59
|
+
this.log(`Initializing plugin: ${name}`, "debug");
|
|
60
|
+
for (const dep of requires ?? []) {
|
|
61
|
+
if (!this.plugins.has(dep)) {
|
|
62
|
+
const error = `Plugin "${name}" requires "${dep}" which is not configured`;
|
|
63
|
+
this.issues.addPluginError({
|
|
64
|
+
pluginName: name,
|
|
65
|
+
operation: "initialize",
|
|
66
|
+
errorMessage: error
|
|
67
|
+
});
|
|
68
|
+
throw new Error(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await plugin.initialize(context);
|
|
73
|
+
this.plugins.set(name, plugin);
|
|
74
|
+
this.log(`Plugin "${name}" initialized`, "debug");
|
|
75
|
+
} catch (error) {
|
|
76
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
77
|
+
this.issues.addPluginError({
|
|
78
|
+
pluginName: name,
|
|
79
|
+
operation: "initialize",
|
|
80
|
+
errorMessage
|
|
81
|
+
});
|
|
82
|
+
throw new Error(`Failed to initialize plugin "${name}": ${errorMessage}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create plugin context
|
|
87
|
+
*/
|
|
88
|
+
createContext() {
|
|
89
|
+
return {
|
|
90
|
+
outputDir: this.outputDir,
|
|
91
|
+
issues: this.issues,
|
|
92
|
+
log: this.log,
|
|
93
|
+
config: this.config,
|
|
94
|
+
getPlugin: (name) => this.getPlugin(name)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// --------------------------------------------------------------------------
|
|
98
|
+
// Plugin Access
|
|
99
|
+
// --------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Get a plugin by name
|
|
102
|
+
*/
|
|
103
|
+
getPlugin(name) {
|
|
104
|
+
return this.plugins.get(name);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get a typed plugin by its config key
|
|
108
|
+
*/
|
|
109
|
+
getPluginByKey(key) {
|
|
110
|
+
return this.plugins.get(key);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if a plugin is available
|
|
114
|
+
*/
|
|
115
|
+
hasPlugin(name) {
|
|
116
|
+
return this.plugins.has(name);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get all initialized plugin names
|
|
120
|
+
*/
|
|
121
|
+
getPluginNames() {
|
|
122
|
+
return [...this.plugins.keys()];
|
|
123
|
+
}
|
|
124
|
+
// --------------------------------------------------------------------------
|
|
125
|
+
// Cleanup
|
|
126
|
+
// --------------------------------------------------------------------------
|
|
127
|
+
/**
|
|
128
|
+
* Dispose all plugins
|
|
129
|
+
*/
|
|
130
|
+
async dispose() {
|
|
131
|
+
const plugins = [...this.plugins.values()].reverse();
|
|
132
|
+
for (const plugin of plugins) {
|
|
133
|
+
if (plugin.dispose) {
|
|
134
|
+
try {
|
|
135
|
+
await plugin.dispose();
|
|
136
|
+
this.log(`Plugin "${plugin.name}" disposed`, "debug");
|
|
137
|
+
} catch (error) {
|
|
138
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
139
|
+
this.issues.addPluginError({
|
|
140
|
+
pluginName: plugin.name,
|
|
141
|
+
operation: "dispose",
|
|
142
|
+
errorMessage
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
this.plugins.clear();
|
|
148
|
+
this.initialized = false;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var topologicalSort = (plugins) => {
|
|
152
|
+
const pluginMap = /* @__PURE__ */ new Map();
|
|
153
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
154
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
155
|
+
for (const plugin of plugins) {
|
|
156
|
+
pluginMap.set(plugin.name, plugin);
|
|
157
|
+
adjacency.set(plugin.name, []);
|
|
158
|
+
inDegree.set(plugin.name, 0);
|
|
159
|
+
}
|
|
160
|
+
for (const plugin of plugins) {
|
|
161
|
+
for (const dep of plugin.requires ?? []) {
|
|
162
|
+
if (adjacency.has(dep)) {
|
|
163
|
+
adjacency.get(dep).push(plugin.name);
|
|
164
|
+
inDegree.set(plugin.name, (inDegree.get(plugin.name) ?? 0) + 1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const queue = [];
|
|
169
|
+
const result = [];
|
|
170
|
+
for (const [name, degree] of inDegree) {
|
|
171
|
+
if (degree === 0) {
|
|
172
|
+
queue.push(name);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
while (queue.length > 0) {
|
|
176
|
+
const name = queue.shift();
|
|
177
|
+
const plugin = pluginMap.get(name);
|
|
178
|
+
if (plugin) {
|
|
179
|
+
result.push(plugin);
|
|
180
|
+
}
|
|
181
|
+
for (const neighbor of adjacency.get(name) ?? []) {
|
|
182
|
+
const newDegree = (inDegree.get(neighbor) ?? 0) - 1;
|
|
183
|
+
inDegree.set(neighbor, newDegree);
|
|
184
|
+
if (newDegree === 0) {
|
|
185
|
+
queue.push(neighbor);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (result.length !== plugins.length) {
|
|
190
|
+
const unresolved = plugins.filter((p) => !result.includes(p)).map((p) => p.name);
|
|
191
|
+
throw new Error(
|
|
192
|
+
`Circular plugin dependency detected involving: ${unresolved.join(", ")}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
};
|
|
197
|
+
var defaultLogger = (message, level = "info") => {
|
|
198
|
+
const prefix = `[processor-core]`;
|
|
199
|
+
switch (level) {
|
|
200
|
+
case "error":
|
|
201
|
+
console.error(`${prefix} ERROR: ${message}`);
|
|
202
|
+
break;
|
|
203
|
+
case "warn":
|
|
204
|
+
console.warn(`${prefix} WARN: ${message}`);
|
|
205
|
+
break;
|
|
206
|
+
case "debug":
|
|
207
|
+
if (process.env.NODE_ENV === "development" || process.env.DEBUG) {
|
|
208
|
+
console.log(`${prefix} DEBUG: ${message}`);
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
default:
|
|
212
|
+
console.log(`${prefix} ${message}`);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
var createPluginManager = async (config) => {
|
|
216
|
+
const manager = new PluginManager(config);
|
|
217
|
+
await manager.initialize();
|
|
218
|
+
return manager;
|
|
219
|
+
};
|
|
220
|
+
var CopyOnlyImageProcessor = class {
|
|
221
|
+
name = "imageProcessor";
|
|
222
|
+
ready = false;
|
|
223
|
+
context = null;
|
|
224
|
+
async initialize(context) {
|
|
225
|
+
this.context = context;
|
|
226
|
+
this.ready = true;
|
|
227
|
+
context.log("CopyOnlyImageProcessor initialized (no optimization)", "debug");
|
|
228
|
+
}
|
|
229
|
+
isReady() {
|
|
230
|
+
return this.ready;
|
|
231
|
+
}
|
|
232
|
+
canProcess(filePath) {
|
|
233
|
+
const ext = path__default.default.extname(filePath).toLowerCase();
|
|
234
|
+
return [".jpg", ".jpeg", ".png", ".gif", ".webp", ".avif", ".svg"].includes(ext);
|
|
235
|
+
}
|
|
236
|
+
async getMetadata(filePath) {
|
|
237
|
+
const ext = path__default.default.extname(filePath).toLowerCase().slice(1);
|
|
238
|
+
return {
|
|
239
|
+
width: 0,
|
|
240
|
+
height: 0,
|
|
241
|
+
format: ext || "unknown"
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
async process(inputPath, outputPath, _options) {
|
|
245
|
+
await this.copy(inputPath, outputPath);
|
|
246
|
+
const stats = await fs__default.default.stat(outputPath);
|
|
247
|
+
const ext = path__default.default.extname(inputPath).toLowerCase().slice(1);
|
|
248
|
+
return {
|
|
249
|
+
outputPath,
|
|
250
|
+
width: 0,
|
|
251
|
+
height: 0,
|
|
252
|
+
format: ext || "unknown",
|
|
253
|
+
size: stats.size
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async copy(inputPath, outputPath) {
|
|
257
|
+
await fs__default.default.mkdir(path__default.default.dirname(outputPath), { recursive: true });
|
|
258
|
+
await fs__default.default.copyFile(inputPath, outputPath);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
var PassthroughMermaidRenderer = class {
|
|
262
|
+
name = "mermaidRenderer";
|
|
263
|
+
ready = false;
|
|
264
|
+
async initialize(context) {
|
|
265
|
+
this.ready = true;
|
|
266
|
+
context.log("PassthroughMermaidRenderer initialized (client-side rendering)", "debug");
|
|
267
|
+
}
|
|
268
|
+
isReady() {
|
|
269
|
+
return this.ready;
|
|
270
|
+
}
|
|
271
|
+
async isAvailable() {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
async render(code, _options) {
|
|
275
|
+
return {
|
|
276
|
+
output: code,
|
|
277
|
+
strategy: "pre-mermaid"
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
var NoOpTextEmbedder = class {
|
|
282
|
+
name = "textEmbedder";
|
|
283
|
+
model = "none";
|
|
284
|
+
dimensions = 0;
|
|
285
|
+
ready = false;
|
|
286
|
+
async initialize(context) {
|
|
287
|
+
this.ready = true;
|
|
288
|
+
context.log("NoOpTextEmbedder initialized (embeddings disabled)", "debug");
|
|
289
|
+
}
|
|
290
|
+
isReady() {
|
|
291
|
+
return this.ready;
|
|
292
|
+
}
|
|
293
|
+
async embed(_text) {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
async batchEmbed(texts) {
|
|
297
|
+
return texts.map(() => []);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
var NoOpImageEmbedder = class {
|
|
301
|
+
name = "imageEmbedder";
|
|
302
|
+
model = "none";
|
|
303
|
+
dimensions = 0;
|
|
304
|
+
ready = false;
|
|
305
|
+
async initialize(context) {
|
|
306
|
+
this.ready = true;
|
|
307
|
+
context.log("NoOpImageEmbedder initialized (image embeddings disabled)", "debug");
|
|
308
|
+
}
|
|
309
|
+
isReady() {
|
|
310
|
+
return this.ready;
|
|
311
|
+
}
|
|
312
|
+
async embedFile(_filePath) {
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
async embedBuffer(_buffer, _mimeType) {
|
|
316
|
+
return [];
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
var NoOpSimilarity = class {
|
|
320
|
+
name = "similarity";
|
|
321
|
+
requires = ["textEmbedder"];
|
|
322
|
+
ready = false;
|
|
323
|
+
async initialize(context) {
|
|
324
|
+
this.ready = true;
|
|
325
|
+
context.log("NoOpSimilarity initialized (similarity disabled)", "debug");
|
|
326
|
+
}
|
|
327
|
+
isReady() {
|
|
328
|
+
return this.ready;
|
|
329
|
+
}
|
|
330
|
+
computeSimilarity(_a, _b) {
|
|
331
|
+
return 0;
|
|
332
|
+
}
|
|
333
|
+
async generateSimilarityMap(posts) {
|
|
334
|
+
return {
|
|
335
|
+
pairwiseScores: /* @__PURE__ */ new Map(),
|
|
336
|
+
similarPosts: /* @__PURE__ */ new Map(),
|
|
337
|
+
metadata: {
|
|
338
|
+
computedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
339
|
+
postCount: posts.length,
|
|
340
|
+
pairCount: 0
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
var NoOpDatabase = class {
|
|
346
|
+
name = "database";
|
|
347
|
+
ready = false;
|
|
348
|
+
async initialize(context) {
|
|
349
|
+
this.ready = true;
|
|
350
|
+
context.log("NoOpDatabase initialized (database generation disabled)", "debug");
|
|
351
|
+
}
|
|
352
|
+
isReady() {
|
|
353
|
+
return this.ready;
|
|
354
|
+
}
|
|
355
|
+
async build(_input) {
|
|
356
|
+
return {
|
|
357
|
+
databasePath: "",
|
|
358
|
+
tables: [],
|
|
359
|
+
rowCounts: {},
|
|
360
|
+
hasVectorSearch: false
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
var createDefaultPlugins = () => ({
|
|
365
|
+
imageProcessor: new CopyOnlyImageProcessor(),
|
|
366
|
+
mermaidRenderer: new PassthroughMermaidRenderer()
|
|
367
|
+
});
|
|
368
|
+
var createAllNoOpPlugins = () => ({
|
|
369
|
+
imageProcessor: new CopyOnlyImageProcessor(),
|
|
370
|
+
mermaidRenderer: new PassthroughMermaidRenderer(),
|
|
371
|
+
textEmbedder: new NoOpTextEmbedder(),
|
|
372
|
+
imageEmbedder: new NoOpImageEmbedder(),
|
|
373
|
+
similarity: new NoOpSimilarity(),
|
|
374
|
+
database: new NoOpDatabase()
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// ../processor/src/plugins/similarity.ts
|
|
378
|
+
var CosineSimilarityPlugin = class {
|
|
379
|
+
name = "similarity";
|
|
380
|
+
requires = ["textEmbedder"];
|
|
381
|
+
ready = false;
|
|
382
|
+
context = null;
|
|
383
|
+
topN;
|
|
384
|
+
threshold;
|
|
385
|
+
constructor(options = {}) {
|
|
386
|
+
this.topN = options.topN ?? 5;
|
|
387
|
+
this.threshold = options.threshold ?? 0;
|
|
388
|
+
}
|
|
389
|
+
async initialize(context) {
|
|
390
|
+
this.context = context;
|
|
391
|
+
this.ready = true;
|
|
392
|
+
context.log(
|
|
393
|
+
`CosineSimilarityPlugin initialized (topN=${this.topN}, threshold=${this.threshold})`,
|
|
394
|
+
"info"
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
isReady() {
|
|
398
|
+
return this.ready;
|
|
399
|
+
}
|
|
400
|
+
async dispose() {
|
|
401
|
+
this.ready = false;
|
|
402
|
+
}
|
|
403
|
+
computeSimilarity(a, b) {
|
|
404
|
+
return cosineSimilarity(a, b);
|
|
405
|
+
}
|
|
406
|
+
async generateSimilarityMap(posts) {
|
|
407
|
+
const postsWithEmbeddings = posts.filter(
|
|
408
|
+
(p) => p.embedding && p.embedding.length > 0
|
|
409
|
+
);
|
|
410
|
+
if (postsWithEmbeddings.length < 2) {
|
|
411
|
+
this.context?.log(
|
|
412
|
+
`Skipping similarity: only ${postsWithEmbeddings.length} posts with embeddings`,
|
|
413
|
+
"debug"
|
|
414
|
+
);
|
|
415
|
+
return {
|
|
416
|
+
pairwiseScores: /* @__PURE__ */ new Map(),
|
|
417
|
+
similarPosts: /* @__PURE__ */ new Map(),
|
|
418
|
+
metadata: {
|
|
419
|
+
computedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
420
|
+
postCount: postsWithEmbeddings.length,
|
|
421
|
+
pairCount: 0
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const pairwiseScores = /* @__PURE__ */ new Map();
|
|
426
|
+
const similarPosts = /* @__PURE__ */ new Map();
|
|
427
|
+
let pairCount = 0;
|
|
428
|
+
for (const post of postsWithEmbeddings) {
|
|
429
|
+
const similarities = [];
|
|
430
|
+
for (const other of postsWithEmbeddings) {
|
|
431
|
+
if (other.hash === post.hash) continue;
|
|
432
|
+
const pairKey = post.hash < other.hash ? `${post.hash}-${other.hash}` : `${other.hash}-${post.hash}`;
|
|
433
|
+
let score;
|
|
434
|
+
if (pairwiseScores.has(pairKey)) {
|
|
435
|
+
score = pairwiseScores.get(pairKey);
|
|
436
|
+
} else {
|
|
437
|
+
score = cosineSimilarity(post.embedding, other.embedding);
|
|
438
|
+
pairwiseScores.set(pairKey, score);
|
|
439
|
+
pairCount++;
|
|
440
|
+
}
|
|
441
|
+
if (score >= this.threshold) {
|
|
442
|
+
similarities.push({ hash: other.hash, score });
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
similarities.sort((a, b) => b.score - a.score);
|
|
446
|
+
const topHashes = similarities.slice(0, this.topN).map((s) => s.hash);
|
|
447
|
+
similarPosts.set(post.hash, topHashes);
|
|
448
|
+
}
|
|
449
|
+
this.context?.log(
|
|
450
|
+
`Computed similarity: ${pairCount} pairs for ${postsWithEmbeddings.length} posts`,
|
|
451
|
+
"info"
|
|
452
|
+
);
|
|
453
|
+
return {
|
|
454
|
+
pairwiseScores,
|
|
455
|
+
similarPosts,
|
|
456
|
+
metadata: {
|
|
457
|
+
computedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
458
|
+
postCount: postsWithEmbeddings.length,
|
|
459
|
+
pairCount
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
function cosineSimilarity(a, b) {
|
|
465
|
+
let dot = 0;
|
|
466
|
+
let normA = 0;
|
|
467
|
+
let normB = 0;
|
|
468
|
+
for (let i = 0; i < a.length; i++) {
|
|
469
|
+
const aVal = a[i] ?? 0;
|
|
470
|
+
const bVal = b[i] ?? 0;
|
|
471
|
+
dot += aVal * bVal;
|
|
472
|
+
normA += aVal * aVal;
|
|
473
|
+
normB += bVal * bVal;
|
|
474
|
+
}
|
|
475
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
476
|
+
return magnitude === 0 ? 0 : dot / magnitude;
|
|
477
|
+
}
|
|
478
|
+
var createSimilarityPlugin = (options) => {
|
|
479
|
+
return new CosineSimilarityPlugin(options);
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
exports.CopyOnlyImageProcessor = CopyOnlyImageProcessor;
|
|
483
|
+
exports.CosineSimilarityPlugin = CosineSimilarityPlugin;
|
|
484
|
+
exports.NoOpDatabase = NoOpDatabase;
|
|
485
|
+
exports.NoOpImageEmbedder = NoOpImageEmbedder;
|
|
486
|
+
exports.NoOpSimilarity = NoOpSimilarity;
|
|
487
|
+
exports.NoOpTextEmbedder = NoOpTextEmbedder;
|
|
488
|
+
exports.PassthroughMermaidRenderer = PassthroughMermaidRenderer;
|
|
489
|
+
exports.PluginManager = PluginManager;
|
|
490
|
+
exports.cosineSimilarity = cosineSimilarity;
|
|
491
|
+
exports.createAllNoOpPlugins = createAllNoOpPlugins;
|
|
492
|
+
exports.createDefaultPlugins = createDefaultPlugins;
|
|
493
|
+
exports.createPluginManager = createPluginManager;
|
|
494
|
+
exports.createSimilarityPlugin = createSimilarityPlugin;
|
|
495
|
+
exports.topologicalSort = topologicalSort;
|