@s-hirano-ist/s-scripts 1.26.7 → 4.0.0
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 +2 -0
- package/dist/chunk-5OXUZW2Q.js +590 -0
- package/dist/chunk-7PVU2Z32.js +71 -0
- package/dist/chunk-AJMTRCPJ.js +15 -0
- package/dist/chunk-AZF2R2BF.js +85 -0
- package/dist/chunk-F4J6JBR4.js +115 -0
- package/dist/chunk-HDYQV2Y2.js +58 -0
- package/dist/chunk-HIK7SX34.js +85 -0
- package/dist/chunk-IWNZQXDG.js +7660 -0
- package/dist/chunk-J4VLH4VA.js +267 -0
- package/dist/chunk-JZOR5GA2.js +42 -0
- package/dist/chunk-KKZP7ZF3.js +77 -0
- package/dist/chunk-N6TZF755.js +102 -0
- package/dist/chunk-S5WJ5VXW.js +85 -0
- package/dist/chunk-UXGLXHQ6.js +40 -0
- package/dist/cleanup-minio-images.js +115 -92
- package/dist/enrich-books.js +87 -78
- package/dist/fetch-articles.js +101 -90
- package/dist/fetch-books.js +147 -128
- package/dist/fetch-images.js +88 -72
- package/dist/fetch-notes.js +66 -54
- package/dist/find-duplicate-json-articles.js +100 -97
- package/dist/fix-book-frontmatter.js +111 -99
- package/dist/fix-image-formats.js +81 -65
- package/dist/ingest-articles.js +30 -249
- package/dist/ingest-books.js +61 -318
- package/dist/ingest-images.js +33 -184
- package/dist/ingest-notes.js +41 -171
- package/dist/rag/delete-collection.js +32 -22
- package/dist/rag/ingest.js +371 -92
- package/dist/rag/search.js +80 -76
- package/dist/reset-articles.js +57 -60
- package/dist/reset-books.js +57 -59
- package/dist/reset-images.js +57 -59
- package/dist/reset-notes.js +57 -59
- package/dist/revert-articles.js +53 -55
- package/dist/revert-books.js +53 -53
- package/dist/revert-images.js +53 -53
- package/dist/revert-notes.js +53 -53
- package/dist/s-content-format.js +103 -13
- package/dist/update-json-articles.js +101 -97
- package/dist/update-raw-articles.js +130 -130
- package/dist/validate-json-articles.js +129 -130
- package/package.json +14 -51
- package/dist/cleanup-minio-images.d.ts +0 -3
- package/dist/cleanup-minio-images.d.ts.map +0 -1
- package/dist/cleanup-minio-images.js.map +0 -1
- package/dist/enrich-books.d.ts +0 -3
- package/dist/enrich-books.d.ts.map +0 -1
- package/dist/enrich-books.js.map +0 -1
- package/dist/fetch-articles.d.ts +0 -3
- package/dist/fetch-articles.d.ts.map +0 -1
- package/dist/fetch-articles.js.map +0 -1
- package/dist/fetch-books.d.ts +0 -3
- package/dist/fetch-books.d.ts.map +0 -1
- package/dist/fetch-books.js.map +0 -1
- package/dist/fetch-images.d.ts +0 -3
- package/dist/fetch-images.d.ts.map +0 -1
- package/dist/fetch-images.js.map +0 -1
- package/dist/fetch-notes.d.ts +0 -3
- package/dist/fetch-notes.d.ts.map +0 -1
- package/dist/fetch-notes.js.map +0 -1
- package/dist/find-duplicate-json-articles.d.ts +0 -3
- package/dist/find-duplicate-json-articles.d.ts.map +0 -1
- package/dist/find-duplicate-json-articles.js.map +0 -1
- package/dist/fix-book-frontmatter.d.ts +0 -3
- package/dist/fix-book-frontmatter.d.ts.map +0 -1
- package/dist/fix-book-frontmatter.js.map +0 -1
- package/dist/fix-image-formats.d.ts +0 -3
- package/dist/fix-image-formats.d.ts.map +0 -1
- package/dist/fix-image-formats.js.map +0 -1
- package/dist/infrastructures/articles-command-repository.d.ts +0 -57
- package/dist/infrastructures/articles-command-repository.d.ts.map +0 -1
- package/dist/infrastructures/articles-command-repository.js +0 -68
- package/dist/infrastructures/articles-command-repository.js.map +0 -1
- package/dist/infrastructures/books-command-repository.d.ts +0 -34
- package/dist/infrastructures/books-command-repository.d.ts.map +0 -1
- package/dist/infrastructures/books-command-repository.js +0 -53
- package/dist/infrastructures/books-command-repository.js.map +0 -1
- package/dist/infrastructures/cache-invalidation-client.d.ts +0 -9
- package/dist/infrastructures/cache-invalidation-client.d.ts.map +0 -1
- package/dist/infrastructures/cache-invalidation-client.js +0 -21
- package/dist/infrastructures/cache-invalidation-client.js.map +0 -1
- package/dist/infrastructures/cache-invalidation-client.test.d.ts +0 -2
- package/dist/infrastructures/cache-invalidation-client.test.d.ts.map +0 -1
- package/dist/infrastructures/cache-invalidation-client.test.js +0 -25
- package/dist/infrastructures/cache-invalidation-client.test.js.map +0 -1
- package/dist/infrastructures/images-command-repository.d.ts +0 -34
- package/dist/infrastructures/images-command-repository.d.ts.map +0 -1
- package/dist/infrastructures/images-command-repository.js +0 -53
- package/dist/infrastructures/images-command-repository.js.map +0 -1
- package/dist/infrastructures/notes-command-repository.d.ts +0 -34
- package/dist/infrastructures/notes-command-repository.d.ts.map +0 -1
- package/dist/infrastructures/notes-command-repository.js +0 -53
- package/dist/infrastructures/notes-command-repository.js.map +0 -1
- package/dist/ingest-articles.d.ts +0 -3
- package/dist/ingest-articles.d.ts.map +0 -1
- package/dist/ingest-articles.js.map +0 -1
- package/dist/ingest-books.d.ts +0 -3
- package/dist/ingest-books.d.ts.map +0 -1
- package/dist/ingest-books.js.map +0 -1
- package/dist/ingest-images.d.ts +0 -3
- package/dist/ingest-images.d.ts.map +0 -1
- package/dist/ingest-images.js.map +0 -1
- package/dist/ingest-notes.d.ts +0 -3
- package/dist/ingest-notes.d.ts.map +0 -1
- package/dist/ingest-notes.js.map +0 -1
- package/dist/lib/glob-paths.d.ts +0 -6
- package/dist/lib/glob-paths.d.ts.map +0 -1
- package/dist/lib/glob-paths.js +0 -9
- package/dist/lib/glob-paths.js.map +0 -1
- package/dist/lib/glob-paths.test.d.ts +0 -2
- package/dist/lib/glob-paths.test.d.ts.map +0 -1
- package/dist/lib/glob-paths.test.js +0 -30
- package/dist/lib/glob-paths.test.js.map +0 -1
- package/dist/lib/html-charset.d.ts +0 -4
- package/dist/lib/html-charset.d.ts.map +0 -1
- package/dist/lib/html-charset.js +0 -33
- package/dist/lib/html-charset.js.map +0 -1
- package/dist/lib/html-charset.test.d.ts +0 -2
- package/dist/lib/html-charset.test.d.ts.map +0 -1
- package/dist/lib/html-charset.test.js +0 -27
- package/dist/lib/html-charset.test.js.map +0 -1
- package/dist/rag/delete-collection.d.ts +0 -3
- package/dist/rag/delete-collection.d.ts.map +0 -1
- package/dist/rag/delete-collection.js.map +0 -1
- package/dist/rag/ingest-config.d.ts +0 -8
- package/dist/rag/ingest-config.d.ts.map +0 -1
- package/dist/rag/ingest-config.js +0 -8
- package/dist/rag/ingest-config.js.map +0 -1
- package/dist/rag/ingest.d.ts +0 -3
- package/dist/rag/ingest.d.ts.map +0 -1
- package/dist/rag/ingest.js.map +0 -1
- package/dist/rag/search.d.ts +0 -3
- package/dist/rag/search.d.ts.map +0 -1
- package/dist/rag/search.js.map +0 -1
- package/dist/reset-articles.d.ts +0 -3
- package/dist/reset-articles.d.ts.map +0 -1
- package/dist/reset-articles.js.map +0 -1
- package/dist/reset-books.d.ts +0 -3
- package/dist/reset-books.d.ts.map +0 -1
- package/dist/reset-books.js.map +0 -1
- package/dist/reset-images.d.ts +0 -3
- package/dist/reset-images.d.ts.map +0 -1
- package/dist/reset-images.js.map +0 -1
- package/dist/reset-notes.d.ts +0 -3
- package/dist/reset-notes.d.ts.map +0 -1
- package/dist/reset-notes.js.map +0 -1
- package/dist/revert-articles.d.ts +0 -3
- package/dist/revert-articles.d.ts.map +0 -1
- package/dist/revert-articles.js.map +0 -1
- package/dist/revert-books.d.ts +0 -3
- package/dist/revert-books.d.ts.map +0 -1
- package/dist/revert-books.js.map +0 -1
- package/dist/revert-images.d.ts +0 -3
- package/dist/revert-images.d.ts.map +0 -1
- package/dist/revert-images.js.map +0 -1
- package/dist/revert-notes.d.ts +0 -3
- package/dist/revert-notes.d.ts.map +0 -1
- package/dist/revert-notes.js.map +0 -1
- package/dist/s-content-format-runner.d.ts +0 -10
- package/dist/s-content-format-runner.d.ts.map +0 -1
- package/dist/s-content-format-runner.js +0 -76
- package/dist/s-content-format-runner.js.map +0 -1
- package/dist/s-content-format.d.ts +0 -3
- package/dist/s-content-format.d.ts.map +0 -1
- package/dist/s-content-format.js.map +0 -1
- package/dist/s-content-format.test.d.ts +0 -2
- package/dist/s-content-format.test.d.ts.map +0 -1
- package/dist/s-content-format.test.js +0 -99
- package/dist/s-content-format.test.js.map +0 -1
- package/dist/update-json-articles.d.ts +0 -3
- package/dist/update-json-articles.d.ts.map +0 -1
- package/dist/update-json-articles.js.map +0 -1
- package/dist/update-raw-articles.d.ts +0 -3
- package/dist/update-raw-articles.d.ts.map +0 -1
- package/dist/update-raw-articles.js.map +0 -1
- package/dist/validate-json-articles.d.ts +0 -3
- package/dist/validate-json-articles.d.ts.map +0 -1
- package/dist/validate-json-articles.js.map +0 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// ../search/dist/qdrant-client.js
|
|
4
|
+
import { QdrantClient } from "@qdrant/js-client-rest";
|
|
5
|
+
|
|
6
|
+
// ../search/dist/config.js
|
|
7
|
+
function requireEnv(name) {
|
|
8
|
+
const value = process.env[name];
|
|
9
|
+
if (!value)
|
|
10
|
+
throw new Error(`${name} environment variable is required`);
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function envWithDefault(name, defaultValue) {
|
|
14
|
+
return process.env[name] || defaultValue;
|
|
15
|
+
}
|
|
16
|
+
var RAG_CONFIG = {
|
|
17
|
+
qdrant: {
|
|
18
|
+
collectionName: requireEnv("QDRANT_COLLECTION_NAME"),
|
|
19
|
+
vectorSize: Number(envWithDefault("EMBEDDING_VECTOR_SIZE", "384")),
|
|
20
|
+
distance: "Cosine"
|
|
21
|
+
},
|
|
22
|
+
embedding: {
|
|
23
|
+
// Deployed services and ingestion jobs always use Qdrant Cloud Inference.
|
|
24
|
+
// Keep TEI only for explicitly enabled, non-production local development.
|
|
25
|
+
url: process.env.LOCAL_DEV_MODE === "true" && process.env.VERCEL !== "1" && process.env.NODE_ENV !== "production" ? process.env.EMBEDDING_URL : void 0,
|
|
26
|
+
model: envWithDefault("EMBEDDING_MODEL", "intfloat/multilingual-e5-small")
|
|
27
|
+
},
|
|
28
|
+
chunking: { maxChunkLength: 2e3, headingLevels: [2, 3] }
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// ../search/dist/qdrant-client.js
|
|
32
|
+
var client = null;
|
|
33
|
+
async function embedLocally(texts) {
|
|
34
|
+
const { url } = RAG_CONFIG.embedding;
|
|
35
|
+
if (!url)
|
|
36
|
+
throw new Error("EMBEDDING_URL environment variable is required");
|
|
37
|
+
const response = await fetch(`${url.replace(/\/$/u, "")}/embed`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: { "content-type": "application/json" },
|
|
40
|
+
body: JSON.stringify({ inputs: texts, normalize: true, truncate: true })
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`Embedding request failed (${response.status}): ${await response.text()}`);
|
|
44
|
+
}
|
|
45
|
+
const embeddings = await response.json();
|
|
46
|
+
if (!Array.isArray(embeddings) || embeddings.length !== texts.length || embeddings.some((embedding) => !Array.isArray(embedding) || embedding.some((value) => typeof value !== "number"))) {
|
|
47
|
+
throw new TypeError("Embedding API returned an invalid response");
|
|
48
|
+
}
|
|
49
|
+
return embeddings;
|
|
50
|
+
}
|
|
51
|
+
function getQdrantClient(config) {
|
|
52
|
+
if (!client) {
|
|
53
|
+
const url = config?.url ?? process.env.QDRANT_URL;
|
|
54
|
+
const apiKey = config?.apiKey ?? process.env.QDRANT_API_KEY;
|
|
55
|
+
if (!url) {
|
|
56
|
+
throw new Error("QDRANT_URL environment variable is required");
|
|
57
|
+
}
|
|
58
|
+
client = new QdrantClient({
|
|
59
|
+
url,
|
|
60
|
+
apiKey
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return client;
|
|
64
|
+
}
|
|
65
|
+
async function deleteCollection(collectionName) {
|
|
66
|
+
const qdrant = getQdrantClient();
|
|
67
|
+
await qdrant.deleteCollection(collectionName);
|
|
68
|
+
}
|
|
69
|
+
async function ensureCollection() {
|
|
70
|
+
const qdrant = getQdrantClient();
|
|
71
|
+
const { collectionName, vectorSize, distance } = RAG_CONFIG.qdrant;
|
|
72
|
+
const collections = await qdrant.getCollections();
|
|
73
|
+
const exists = collections.collections.some((c) => c.name === collectionName);
|
|
74
|
+
if (exists) {
|
|
75
|
+
console.log(`Collection ${collectionName} already exists.`);
|
|
76
|
+
} else {
|
|
77
|
+
console.log(`Creating collection: ${collectionName}`);
|
|
78
|
+
await qdrant.createCollection(collectionName, {
|
|
79
|
+
vectors: {
|
|
80
|
+
size: vectorSize,
|
|
81
|
+
distance
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
console.log(`Collection ${collectionName} created successfully.`);
|
|
85
|
+
}
|
|
86
|
+
await qdrant.createPayloadIndex(collectionName, {
|
|
87
|
+
field_name: "type",
|
|
88
|
+
field_schema: "keyword",
|
|
89
|
+
wait: true
|
|
90
|
+
});
|
|
91
|
+
await qdrant.createPayloadIndex(collectionName, {
|
|
92
|
+
field_name: "top_heading",
|
|
93
|
+
field_schema: "keyword",
|
|
94
|
+
wait: true
|
|
95
|
+
});
|
|
96
|
+
await qdrant.createPayloadIndex(collectionName, {
|
|
97
|
+
field_name: "content_type",
|
|
98
|
+
field_schema: "keyword",
|
|
99
|
+
wait: true
|
|
100
|
+
});
|
|
101
|
+
console.log("Payload indexes ensured for: type, top_heading, content_type");
|
|
102
|
+
}
|
|
103
|
+
async function upsertPoints(points) {
|
|
104
|
+
const qdrant = getQdrantClient();
|
|
105
|
+
const { collectionName } = RAG_CONFIG.qdrant;
|
|
106
|
+
const localEmbeddings = RAG_CONFIG.embedding.url ? await embedLocally(points.map((point) => point.text)) : void 0;
|
|
107
|
+
const qdrantPoints = points.map((p, index) => ({
|
|
108
|
+
id: hashToUint(p.id),
|
|
109
|
+
vector: localEmbeddings?.[index] ?? {
|
|
110
|
+
text: p.text,
|
|
111
|
+
model: RAG_CONFIG.embedding.model
|
|
112
|
+
},
|
|
113
|
+
payload: p.payload
|
|
114
|
+
}));
|
|
115
|
+
await qdrant.upsert(collectionName, {
|
|
116
|
+
wait: true,
|
|
117
|
+
points: qdrantPoints
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function getExistingHashes(chunkIds) {
|
|
121
|
+
const qdrant = getQdrantClient();
|
|
122
|
+
const { collectionName } = RAG_CONFIG.qdrant;
|
|
123
|
+
const hashMap = /* @__PURE__ */ new Map();
|
|
124
|
+
if (chunkIds.length === 0)
|
|
125
|
+
return hashMap;
|
|
126
|
+
const numericIds = chunkIds.map((id) => hashToUint(id));
|
|
127
|
+
try {
|
|
128
|
+
const result = await qdrant.retrieve(collectionName, {
|
|
129
|
+
ids: numericIds,
|
|
130
|
+
with_payload: ["chunk_id", "content_hash"]
|
|
131
|
+
});
|
|
132
|
+
for (const point of result) {
|
|
133
|
+
const payload = point.payload;
|
|
134
|
+
if (payload?.chunk_id && payload?.content_hash) {
|
|
135
|
+
hashMap.set(payload.chunk_id, payload.content_hash);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
return hashMap;
|
|
141
|
+
}
|
|
142
|
+
async function search(queryText, options = {}) {
|
|
143
|
+
const qdrant = getQdrantClient();
|
|
144
|
+
const { collectionName } = RAG_CONFIG.qdrant;
|
|
145
|
+
const { topK = 10, filter } = options;
|
|
146
|
+
const filterConditions = [];
|
|
147
|
+
if (filter?.type) {
|
|
148
|
+
filterConditions.push({
|
|
149
|
+
key: "type",
|
|
150
|
+
match: { value: filter.type }
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (filter?.top_heading) {
|
|
154
|
+
filterConditions.push({
|
|
155
|
+
key: "top_heading",
|
|
156
|
+
match: { value: filter.top_heading }
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (filter?.content_type) {
|
|
160
|
+
if (Array.isArray(filter.content_type)) {
|
|
161
|
+
filterConditions.push({
|
|
162
|
+
key: "content_type",
|
|
163
|
+
match: { any: filter.content_type }
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
filterConditions.push({
|
|
167
|
+
key: "content_type",
|
|
168
|
+
match: { value: filter.content_type }
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const query = RAG_CONFIG.embedding.url ? (await embedLocally([queryText]))[0] : { text: queryText, model: RAG_CONFIG.embedding.model };
|
|
173
|
+
const result = await qdrant.query(collectionName, {
|
|
174
|
+
query,
|
|
175
|
+
limit: topK,
|
|
176
|
+
with_payload: true,
|
|
177
|
+
filter: filterConditions.length > 0 ? { must: filterConditions } : void 0
|
|
178
|
+
});
|
|
179
|
+
return result.points.map((r) => {
|
|
180
|
+
const payload = r.payload;
|
|
181
|
+
return {
|
|
182
|
+
score: r.score,
|
|
183
|
+
text: payload.text,
|
|
184
|
+
title: payload.title,
|
|
185
|
+
url: payload.url,
|
|
186
|
+
heading_path: payload.heading_path,
|
|
187
|
+
type: payload.type,
|
|
188
|
+
content_type: payload.content_type,
|
|
189
|
+
doc_id: payload.doc_id
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function listAllChunkIds() {
|
|
194
|
+
const qdrant = getQdrantClient();
|
|
195
|
+
const { collectionName } = RAG_CONFIG.qdrant;
|
|
196
|
+
const chunkIds = [];
|
|
197
|
+
const pageSize = 256;
|
|
198
|
+
let nextOffset;
|
|
199
|
+
try {
|
|
200
|
+
do {
|
|
201
|
+
const result = await qdrant.scroll(collectionName, {
|
|
202
|
+
limit: pageSize,
|
|
203
|
+
offset: typeof nextOffset === "string" || typeof nextOffset === "number" ? nextOffset : void 0,
|
|
204
|
+
with_payload: ["chunk_id"],
|
|
205
|
+
with_vector: false
|
|
206
|
+
});
|
|
207
|
+
for (const point of result.points) {
|
|
208
|
+
const payload = point.payload;
|
|
209
|
+
if (payload?.chunk_id) {
|
|
210
|
+
chunkIds.push(payload.chunk_id);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
nextOffset = result.next_page_offset;
|
|
214
|
+
} while (nextOffset !== null && nextOffset !== void 0);
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
return chunkIds;
|
|
218
|
+
}
|
|
219
|
+
async function deletePointsByChunkIds(chunkIds) {
|
|
220
|
+
if (chunkIds.length === 0)
|
|
221
|
+
return;
|
|
222
|
+
const qdrant = getQdrantClient();
|
|
223
|
+
const { collectionName } = RAG_CONFIG.qdrant;
|
|
224
|
+
const numericIds = chunkIds.map((id) => hashToUint(id));
|
|
225
|
+
await qdrant.delete(collectionName, {
|
|
226
|
+
wait: true,
|
|
227
|
+
points: numericIds
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
async function getCollectionStats() {
|
|
231
|
+
const qdrant = getQdrantClient();
|
|
232
|
+
const { collectionName } = RAG_CONFIG.qdrant;
|
|
233
|
+
try {
|
|
234
|
+
const info = await qdrant.getCollection(collectionName);
|
|
235
|
+
return {
|
|
236
|
+
pointsCount: info.points_count ?? 0,
|
|
237
|
+
status: info.status
|
|
238
|
+
};
|
|
239
|
+
} catch {
|
|
240
|
+
return {
|
|
241
|
+
pointsCount: 0,
|
|
242
|
+
status: "not_found"
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function hashToUint(str) {
|
|
247
|
+
let hash = 0;
|
|
248
|
+
for (let i = 0; i < str.length; i++) {
|
|
249
|
+
const char = str.codePointAt(i) ?? 0;
|
|
250
|
+
hash = (hash << 5) - hash + char;
|
|
251
|
+
hash = hash & hash;
|
|
252
|
+
}
|
|
253
|
+
return Math.abs(hash);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export {
|
|
257
|
+
RAG_CONFIG,
|
|
258
|
+
getQdrantClient,
|
|
259
|
+
deleteCollection,
|
|
260
|
+
ensureCollection,
|
|
261
|
+
upsertPoints,
|
|
262
|
+
getExistingHashes,
|
|
263
|
+
search,
|
|
264
|
+
listAllChunkIds,
|
|
265
|
+
deletePointsByChunkIds,
|
|
266
|
+
getCollectionStats
|
|
267
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
try {
|
|
16
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
throw mod = 0, e;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
__require,
|
|
40
|
+
__commonJS,
|
|
41
|
+
__toESM
|
|
42
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// ../image-processing/dist/node.js
|
|
4
|
+
import sharp from "sharp";
|
|
5
|
+
|
|
6
|
+
// ../image-processing/dist/shared.js
|
|
7
|
+
var WEBP_HEADER_OFFSET = 8;
|
|
8
|
+
var RIFF_HEADER_LENGTH = 12;
|
|
9
|
+
function startsWithBytes(bytes, signature) {
|
|
10
|
+
return bytes.length >= signature.length && signature.every((byte, index) => bytes[index] === byte);
|
|
11
|
+
}
|
|
12
|
+
function startsWithAscii(bytes, value) {
|
|
13
|
+
if (bytes.length < value.length) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
for (let index = 0; index < value.length; index++) {
|
|
17
|
+
if (bytes[index] !== value.codePointAt(index)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
function detectSupportedImageFormat(bytes) {
|
|
24
|
+
if (startsWithBytes(bytes, [255, 216, 255])) {
|
|
25
|
+
return "jpeg";
|
|
26
|
+
}
|
|
27
|
+
if (startsWithBytes(bytes, [137, 80, 78, 71, 13, 10, 26, 10])) {
|
|
28
|
+
return "png";
|
|
29
|
+
}
|
|
30
|
+
if (bytes.length >= RIFF_HEADER_LENGTH && startsWithAscii(bytes, "RIFF") && bytes[WEBP_HEADER_OFFSET] === "W".codePointAt(0) && bytes[WEBP_HEADER_OFFSET + 1] === "E".codePointAt(0) && bytes[WEBP_HEADER_OFFSET + 2] === "B".codePointAt(0) && bytes[WEBP_HEADER_OFFSET + 3] === "P".codePointAt(0)) {
|
|
31
|
+
return "webp";
|
|
32
|
+
}
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ../image-processing/dist/node.js
|
|
37
|
+
var DEFAULT_WEBP_QUALITY = 80;
|
|
38
|
+
function assertSupportedImage(bytes) {
|
|
39
|
+
const format = detectSupportedImageFormat(bytes);
|
|
40
|
+
if (format === void 0) {
|
|
41
|
+
throw new Error("Unsupported image signature.");
|
|
42
|
+
}
|
|
43
|
+
return format;
|
|
44
|
+
}
|
|
45
|
+
async function readImageMetadata(bytes) {
|
|
46
|
+
const format = assertSupportedImage(bytes);
|
|
47
|
+
const metadata = await sharp(bytes).metadata();
|
|
48
|
+
const dimensions = metadata.autoOrient;
|
|
49
|
+
if (!dimensions.width || !dimensions.height) {
|
|
50
|
+
throw new Error("Image dimensions could not be determined.");
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
format,
|
|
54
|
+
height: dimensions.height,
|
|
55
|
+
width: dimensions.width
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function createWebpThumbnail(bytes, options) {
|
|
59
|
+
assertSupportedImage(bytes);
|
|
60
|
+
const output = await sharp(bytes).autoOrient().resize(options.width, options.height, {
|
|
61
|
+
fit: "cover",
|
|
62
|
+
kernel: sharp.kernel.lanczos3,
|
|
63
|
+
position: "centre"
|
|
64
|
+
}).webp({ quality: DEFAULT_WEBP_QUALITY }).toBuffer();
|
|
65
|
+
return new Uint8Array(output);
|
|
66
|
+
}
|
|
67
|
+
async function convertToWebp(bytes, options) {
|
|
68
|
+
assertSupportedImage(bytes);
|
|
69
|
+
const output = await sharp(bytes).autoOrient().webp({ quality: options?.quality ?? DEFAULT_WEBP_QUALITY }).toBuffer();
|
|
70
|
+
return new Uint8Array(output);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
readImageMetadata,
|
|
75
|
+
createWebpThumbnail,
|
|
76
|
+
convertToWebp
|
|
77
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
makeExportedAt,
|
|
4
|
+
withTransactionRetry
|
|
5
|
+
} from "./chunk-IWNZQXDG.js";
|
|
6
|
+
|
|
7
|
+
// ../application/dist/batch/articles-batch-service.js
|
|
8
|
+
var ArticlesBatchApplicationService = class {
|
|
9
|
+
commandRepository;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new ArticlesBatchApplicationService instance.
|
|
12
|
+
*
|
|
13
|
+
* @param commandRepository - The command repository for batch updates
|
|
14
|
+
*/
|
|
15
|
+
constructor(commandRepository) {
|
|
16
|
+
this.commandRepository = commandRepository;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resets articles for a new batch export.
|
|
20
|
+
*
|
|
21
|
+
* @param userId - The user ID for tenant isolation
|
|
22
|
+
* @returns The result containing finalized and marked counts
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* Delegates to repository's resetStatus which performs two operations
|
|
26
|
+
* atomically within a single transaction:
|
|
27
|
+
* 1. Finalize: LAST_UPDATED -> EXPORTED (complete previous batch)
|
|
28
|
+
* 2. Mark: UNEXPORTED -> LAST_UPDATED (prepare current batch)
|
|
29
|
+
*/
|
|
30
|
+
async resetArticles(userId) {
|
|
31
|
+
return this.commandRepository.resetStatus(userId, makeExportedAt());
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Reverts articles from LAST_UPDATED back to UNEXPORTED.
|
|
35
|
+
*
|
|
36
|
+
* @param userId - The user ID for tenant isolation
|
|
37
|
+
* @returns The result containing the count of reverted articles
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* Used to undo a batch marking if something went wrong.
|
|
41
|
+
* Only affects articles in LAST_UPDATED status.
|
|
42
|
+
*/
|
|
43
|
+
async revertArticles(userId) {
|
|
44
|
+
return this.commandRepository.bulkUpdateStatus({
|
|
45
|
+
userId,
|
|
46
|
+
fromStatus: "LAST_UPDATED",
|
|
47
|
+
toStatus: "UNEXPORTED"
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// ../infrastructure/dist/batch/articles-command-repository.js
|
|
53
|
+
function createArticlesCommandRepository(prisma) {
|
|
54
|
+
return {
|
|
55
|
+
async bulkUpdateStatus(params) {
|
|
56
|
+
const { userId, fromStatus, toStatus, exportedAt } = params;
|
|
57
|
+
const result = await prisma.article.updateMany({
|
|
58
|
+
where: {
|
|
59
|
+
userId,
|
|
60
|
+
status: fromStatus
|
|
61
|
+
},
|
|
62
|
+
data: {
|
|
63
|
+
status: toStatus,
|
|
64
|
+
...exportedAt && { exportedAt }
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return { count: result.count };
|
|
68
|
+
},
|
|
69
|
+
async resetStatus(userId, exportedAt) {
|
|
70
|
+
const [finalized, marked] = await withTransactionRetry(() => prisma.$transaction([
|
|
71
|
+
prisma.article.updateMany({
|
|
72
|
+
where: {
|
|
73
|
+
userId,
|
|
74
|
+
status: "LAST_UPDATED"
|
|
75
|
+
},
|
|
76
|
+
data: {
|
|
77
|
+
status: "EXPORTED",
|
|
78
|
+
exportedAt
|
|
79
|
+
}
|
|
80
|
+
}),
|
|
81
|
+
prisma.article.updateMany({
|
|
82
|
+
where: {
|
|
83
|
+
userId,
|
|
84
|
+
status: "UNEXPORTED"
|
|
85
|
+
},
|
|
86
|
+
data: {
|
|
87
|
+
status: "LAST_UPDATED"
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
]));
|
|
91
|
+
return {
|
|
92
|
+
finalized: { count: finalized.count },
|
|
93
|
+
marked: { count: marked.count }
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
ArticlesBatchApplicationService,
|
|
101
|
+
createArticlesCommandRepository
|
|
102
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
makeExportedAt,
|
|
4
|
+
withTransactionRetry
|
|
5
|
+
} from "./chunk-IWNZQXDG.js";
|
|
6
|
+
|
|
7
|
+
// ../application/dist/batch/notes-batch-service.js
|
|
8
|
+
var NotesBatchApplicationService = class {
|
|
9
|
+
commandRepository;
|
|
10
|
+
constructor(commandRepository) {
|
|
11
|
+
this.commandRepository = commandRepository;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resets notes for a new batch export.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* Delegates to repository's resetStatus which performs two operations
|
|
18
|
+
* atomically within a single transaction.
|
|
19
|
+
*/
|
|
20
|
+
async resetNotes(userId) {
|
|
21
|
+
return this.commandRepository.resetStatus(userId, makeExportedAt());
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reverts notes from LAST_UPDATED back to UNEXPORTED.
|
|
25
|
+
*/
|
|
26
|
+
async revertNotes(userId) {
|
|
27
|
+
return this.commandRepository.bulkUpdateStatus({
|
|
28
|
+
userId,
|
|
29
|
+
fromStatus: "LAST_UPDATED",
|
|
30
|
+
toStatus: "UNEXPORTED"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ../infrastructure/dist/batch/notes-command-repository.js
|
|
36
|
+
function createNotesCommandRepository(prisma) {
|
|
37
|
+
return {
|
|
38
|
+
async bulkUpdateStatus(params) {
|
|
39
|
+
const { userId, fromStatus, toStatus, exportedAt } = params;
|
|
40
|
+
const result = await prisma.note.updateMany({
|
|
41
|
+
where: {
|
|
42
|
+
userId,
|
|
43
|
+
status: fromStatus
|
|
44
|
+
},
|
|
45
|
+
data: {
|
|
46
|
+
status: toStatus,
|
|
47
|
+
...exportedAt && { exportedAt }
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return { count: result.count };
|
|
51
|
+
},
|
|
52
|
+
async resetStatus(userId, exportedAt) {
|
|
53
|
+
const [finalized, marked] = await withTransactionRetry(() => prisma.$transaction([
|
|
54
|
+
prisma.note.updateMany({
|
|
55
|
+
where: {
|
|
56
|
+
userId,
|
|
57
|
+
status: "LAST_UPDATED"
|
|
58
|
+
},
|
|
59
|
+
data: {
|
|
60
|
+
status: "EXPORTED",
|
|
61
|
+
exportedAt
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
prisma.note.updateMany({
|
|
65
|
+
where: {
|
|
66
|
+
userId,
|
|
67
|
+
status: "UNEXPORTED"
|
|
68
|
+
},
|
|
69
|
+
data: {
|
|
70
|
+
status: "LAST_UPDATED"
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
]));
|
|
74
|
+
return {
|
|
75
|
+
finalized: { count: finalized.count },
|
|
76
|
+
marked: { count: marked.count }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
NotesBatchApplicationService,
|
|
84
|
+
createNotesCommandRepository
|
|
85
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/lib/html-charset.ts
|
|
4
|
+
var CHARSET_ALIASES = {
|
|
5
|
+
eucjp: "euc-jp",
|
|
6
|
+
sjis: "shift_jis",
|
|
7
|
+
shiftjis: "shift_jis",
|
|
8
|
+
utf8: "utf-8",
|
|
9
|
+
xeucjp: "euc-jp",
|
|
10
|
+
xsjis: "shift_jis"
|
|
11
|
+
};
|
|
12
|
+
function normalizeCharset(charset) {
|
|
13
|
+
const trimmed = charset.trim().replaceAll(/^['"]|['"]$/gu, "");
|
|
14
|
+
const normalized = trimmed.toLowerCase().replaceAll(/[^a-z0-9]/gu, "");
|
|
15
|
+
return CHARSET_ALIASES[normalized] ?? trimmed.toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
function detectHtmlCharset(headers, bytes) {
|
|
18
|
+
const contentType = headers.get("content-type") ?? "";
|
|
19
|
+
const headerMatch = /charset\s*=\s*["']?([^"'\s;]+)/iu.exec(contentType);
|
|
20
|
+
if (headerMatch) return normalizeCharset(headerMatch[1]);
|
|
21
|
+
const preview = new TextDecoder("windows-1252").decode(
|
|
22
|
+
bytes.subarray(0, Math.min(4096, bytes.length))
|
|
23
|
+
);
|
|
24
|
+
const metaCharset = /<meta\s{1,200}charset=["']?([^"'\s>]+)/iu.exec(preview);
|
|
25
|
+
if (metaCharset) return normalizeCharset(metaCharset[1]);
|
|
26
|
+
const metaHttpEquiv = /<meta[^>]{1,500}http-equiv=["']?Content-Type["']?[^>]{1,500}content=["'][^"']{0,500}charset=([^"'\s;]+)/iu.exec(
|
|
27
|
+
preview
|
|
28
|
+
);
|
|
29
|
+
if (metaHttpEquiv) return normalizeCharset(metaHttpEquiv[1]);
|
|
30
|
+
return "utf-8";
|
|
31
|
+
}
|
|
32
|
+
function decodeHtml(headers, bytes) {
|
|
33
|
+
return new TextDecoder(detectHtmlCharset(headers, bytes), {
|
|
34
|
+
fatal: false
|
|
35
|
+
}).decode(bytes);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
decodeHtml
|
|
40
|
+
};
|