@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
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
CLI scripts for s-private data operations.
|
|
4
4
|
|
|
5
|
+
The npm artifact bundles the required internal workspace code and generated Prisma client. Consumers install only this CLI package; internal s-private packages are not separate npm dependencies. Command names remain unchanged.
|
|
6
|
+
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
```sh
|
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
createWebpThumbnail,
|
|
4
|
+
readImageMetadata
|
|
5
|
+
} from "./chunk-KKZP7ZF3.js";
|
|
6
|
+
import {
|
|
7
|
+
createMinioClient,
|
|
8
|
+
createStorageService
|
|
9
|
+
} from "./chunk-F4J6JBR4.js";
|
|
10
|
+
import {
|
|
11
|
+
CreatedAt,
|
|
12
|
+
ExportedStatus,
|
|
13
|
+
Id,
|
|
14
|
+
LastUpdatedStatus,
|
|
15
|
+
UnexportedStatus,
|
|
16
|
+
UserId,
|
|
17
|
+
createPrismaClient,
|
|
18
|
+
import_generated,
|
|
19
|
+
makeId,
|
|
20
|
+
withTransactionRetry
|
|
21
|
+
} from "./chunk-IWNZQXDG.js";
|
|
22
|
+
import {
|
|
23
|
+
createPushoverService
|
|
24
|
+
} from "./chunk-7PVU2Z32.js";
|
|
25
|
+
|
|
26
|
+
// ../core/shared-kernel/entities/file-entity.ts
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
var Path = z.string().min(1).max(512, { message: "tooLong" }).brand();
|
|
29
|
+
var ContentType = z.enum(["image/jpeg", "image/png", "image/webp", "jpeg", "png", "webp"]).brand();
|
|
30
|
+
var FileSize = z.number().int().nonnegative().max(10 * 1024 * 1024).brand();
|
|
31
|
+
|
|
32
|
+
// ../core/shared-kernel/services/entity-factory.ts
|
|
33
|
+
import { ZodError } from "zod";
|
|
34
|
+
|
|
35
|
+
// ../core/shared-kernel/services/url-validation.ts
|
|
36
|
+
var isValidHttpUrl = (url) => {
|
|
37
|
+
if (!url) return true;
|
|
38
|
+
try {
|
|
39
|
+
const urlObject = new URL(url);
|
|
40
|
+
return urlObject.protocol === "http:" || urlObject.protocol === "https:";
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ../core/books/entities/book-entity.ts
|
|
47
|
+
import { z as z2 } from "zod";
|
|
48
|
+
var ISBN = z2.string({ message: "required" }).min(1, { message: "required" }).transform((v) => v.replaceAll("-", "")).pipe(
|
|
49
|
+
z2.string().regex(/^\d{13}$/u, { message: "invalidFormat" }).brand()
|
|
50
|
+
);
|
|
51
|
+
var makeISBN = (v) => ISBN.parse(v);
|
|
52
|
+
var BookTitle = z2.string({ message: "required" }).min(1, { message: "required" }).max(512, { message: "tooLong" }).brand();
|
|
53
|
+
var GoogleSubtitle = z2.string().max(512, { message: "tooLong" }).nullable().brand();
|
|
54
|
+
var GoogleAuthors = z2.array(z2.string()).nullable().brand();
|
|
55
|
+
var GoogleDescription = z2.string().nullable().brand();
|
|
56
|
+
var GoogleImgSrc = z2.string().max(1024, { message: "tooLong" }).refine(isValidHttpUrl, { message: "invalidFormat" }).nullable().brand();
|
|
57
|
+
var GoogleHref = z2.string().max(1024, { message: "tooLong" }).refine(isValidHttpUrl, { message: "invalidFormat" }).nullable().brand();
|
|
58
|
+
var Rating = z2.number({ message: "required" }).int({ message: "invalidFormat" }).min(1, { message: "outOfRange" }).max(5, { message: "outOfRange" }).brand();
|
|
59
|
+
var Tags = z2.array(
|
|
60
|
+
z2.string().min(1, { message: "required" }).max(64, { message: "tooLong" })
|
|
61
|
+
).brand();
|
|
62
|
+
var BookMarkdown = z2.string().nullable().brand();
|
|
63
|
+
var Base = z2.object({
|
|
64
|
+
id: Id,
|
|
65
|
+
userId: UserId,
|
|
66
|
+
isbn: ISBN,
|
|
67
|
+
title: BookTitle,
|
|
68
|
+
rating: Rating,
|
|
69
|
+
tags: Tags.optional(),
|
|
70
|
+
googleSubTitle: GoogleSubtitle.optional(),
|
|
71
|
+
googleAuthors: GoogleAuthors.optional(),
|
|
72
|
+
googleDescription: GoogleDescription.optional(),
|
|
73
|
+
googleImgSrc: GoogleImgSrc.optional(),
|
|
74
|
+
googleHref: GoogleHref.optional(),
|
|
75
|
+
imagePath: Path.optional(),
|
|
76
|
+
markdown: BookMarkdown.optional(),
|
|
77
|
+
createdAt: CreatedAt
|
|
78
|
+
});
|
|
79
|
+
var UnexportedBook = Base.extend({ status: UnexportedStatus });
|
|
80
|
+
var LastUpdatedBook = Base.extend({ status: LastUpdatedStatus });
|
|
81
|
+
var ExportedBook = Base.extend(ExportedStatus.shape);
|
|
82
|
+
|
|
83
|
+
// ../infrastructure/dist/content/sharp-image-processor.js
|
|
84
|
+
var THUMBNAIL_WIDTH = 192;
|
|
85
|
+
var THUMBNAIL_HEIGHT = 192;
|
|
86
|
+
async function createThumbnail(bytes, width = THUMBNAIL_WIDTH, height = THUMBNAIL_HEIGHT) {
|
|
87
|
+
return await createWebpThumbnail(bytes, { width, height });
|
|
88
|
+
}
|
|
89
|
+
async function getMetadata(bytes) {
|
|
90
|
+
return await readImageMetadata(bytes);
|
|
91
|
+
}
|
|
92
|
+
var sharpImageProcessor = {
|
|
93
|
+
createThumbnail,
|
|
94
|
+
getMetadata
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// ../application/dist/content/models.js
|
|
98
|
+
import { z as z3 } from "zod";
|
|
99
|
+
var contentDomainSchema = z3.enum([
|
|
100
|
+
"articles",
|
|
101
|
+
"notes",
|
|
102
|
+
"images",
|
|
103
|
+
"books"
|
|
104
|
+
]);
|
|
105
|
+
var syncDomainSchema = z3.enum([
|
|
106
|
+
"articles",
|
|
107
|
+
"notes",
|
|
108
|
+
"images",
|
|
109
|
+
"books",
|
|
110
|
+
"categories"
|
|
111
|
+
]);
|
|
112
|
+
var statusSchema = z3.enum(["UNEXPORTED", "LAST_UPDATED", "EXPORTED"]);
|
|
113
|
+
var idSchema = z3.string().min(1).max(512);
|
|
114
|
+
var contentBase = {
|
|
115
|
+
id: idSchema,
|
|
116
|
+
status: statusSchema,
|
|
117
|
+
createdAt: z3.date(),
|
|
118
|
+
updatedAt: z3.date(),
|
|
119
|
+
exportedAt: z3.date().nullable()
|
|
120
|
+
};
|
|
121
|
+
var articleSchema = z3.object({
|
|
122
|
+
...contentBase,
|
|
123
|
+
title: z3.string(),
|
|
124
|
+
url: z3.url(),
|
|
125
|
+
quote: z3.string().nullable(),
|
|
126
|
+
categoryId: idSchema,
|
|
127
|
+
categoryName: z3.string(),
|
|
128
|
+
ogImageUrl: z3.string().nullable().optional(),
|
|
129
|
+
ogTitle: z3.string().nullable().optional(),
|
|
130
|
+
ogDescription: z3.string().nullable().optional()
|
|
131
|
+
});
|
|
132
|
+
var noteSchema = z3.object({
|
|
133
|
+
...contentBase,
|
|
134
|
+
title: z3.string(),
|
|
135
|
+
markdown: z3.string()
|
|
136
|
+
});
|
|
137
|
+
var imageSchema = z3.object({
|
|
138
|
+
...contentBase,
|
|
139
|
+
path: z3.string(),
|
|
140
|
+
contentType: z3.enum(["image/jpeg", "image/png", "image/webp"]),
|
|
141
|
+
fileSize: z3.number().int().nonnegative().nullable(),
|
|
142
|
+
width: z3.number().int().positive().nullable(),
|
|
143
|
+
height: z3.number().int().positive().nullable()
|
|
144
|
+
});
|
|
145
|
+
var bookSchema = z3.object({
|
|
146
|
+
...contentBase,
|
|
147
|
+
isbn: z3.string(),
|
|
148
|
+
title: z3.string(),
|
|
149
|
+
markdown: z3.string().nullable(),
|
|
150
|
+
imagePath: z3.string().nullable(),
|
|
151
|
+
googleSubTitle: z3.string().nullable().optional(),
|
|
152
|
+
googleAuthors: z3.array(z3.string()).optional(),
|
|
153
|
+
googleDescription: z3.string().nullable().optional(),
|
|
154
|
+
googleImgSrc: z3.string().nullable().optional(),
|
|
155
|
+
googleHref: z3.string().nullable().optional(),
|
|
156
|
+
rating: z3.number().int(),
|
|
157
|
+
tags: z3.array(z3.string())
|
|
158
|
+
});
|
|
159
|
+
var categorySchema = z3.object({ id: idSchema, name: z3.string() });
|
|
160
|
+
|
|
161
|
+
// ../application/dist/batch/import-models.js
|
|
162
|
+
var importSchemas = {
|
|
163
|
+
articles: articleSchema.pick({
|
|
164
|
+
title: true,
|
|
165
|
+
url: true,
|
|
166
|
+
quote: true,
|
|
167
|
+
categoryName: true,
|
|
168
|
+
ogImageUrl: true,
|
|
169
|
+
ogTitle: true,
|
|
170
|
+
ogDescription: true
|
|
171
|
+
}),
|
|
172
|
+
notes: noteSchema.pick({ title: true, markdown: true }),
|
|
173
|
+
images: imageSchema.pick({
|
|
174
|
+
path: true,
|
|
175
|
+
contentType: true,
|
|
176
|
+
fileSize: true,
|
|
177
|
+
width: true,
|
|
178
|
+
height: true
|
|
179
|
+
}),
|
|
180
|
+
books: bookSchema.pick({
|
|
181
|
+
isbn: true,
|
|
182
|
+
title: true,
|
|
183
|
+
markdown: true,
|
|
184
|
+
imagePath: true,
|
|
185
|
+
googleSubTitle: true,
|
|
186
|
+
googleAuthors: true,
|
|
187
|
+
googleDescription: true,
|
|
188
|
+
googleImgSrc: true,
|
|
189
|
+
googleHref: true,
|
|
190
|
+
rating: true,
|
|
191
|
+
tags: true
|
|
192
|
+
})
|
|
193
|
+
};
|
|
194
|
+
function importKey(values) {
|
|
195
|
+
if ("url" in values)
|
|
196
|
+
return values.url;
|
|
197
|
+
if ("isbn" in values)
|
|
198
|
+
return values.isbn;
|
|
199
|
+
if ("path" in values)
|
|
200
|
+
return values.path;
|
|
201
|
+
return values.title;
|
|
202
|
+
}
|
|
203
|
+
function importChange(domain, id, values) {
|
|
204
|
+
switch (domain) {
|
|
205
|
+
case "articles":
|
|
206
|
+
return { domain, id, values: importSchemas.articles.parse(values) };
|
|
207
|
+
case "notes":
|
|
208
|
+
return { domain, id, values: importSchemas.notes.parse(values) };
|
|
209
|
+
case "images":
|
|
210
|
+
return { domain, id, values: importSchemas.images.parse(values) };
|
|
211
|
+
case "books":
|
|
212
|
+
return { domain, id, values: importSchemas.books.parse(values) };
|
|
213
|
+
}
|
|
214
|
+
throw new Error("Unsupported content domain");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ../core/articles/entities/article-entity.ts
|
|
218
|
+
import { z as z4 } from "zod";
|
|
219
|
+
var CategoryName = z4.string({ message: "required" }).trim().min(1, { message: "required" }).max(16, { message: "tooLong" }).brand();
|
|
220
|
+
var ArticleTitle = z4.string({ message: "required" }).min(1, { message: "required" }).max(128, { message: "tooLong" }).brand();
|
|
221
|
+
var Quote = z4.string().max(512, { message: "tooLong" }).nullable().optional().brand();
|
|
222
|
+
var Url = z4.url({ message: "invalidFormat" }).min(1, { message: "required" }).max(1024, { message: "tooLong" }).refine(isValidHttpUrl, { message: "invalidFormat" }).brand();
|
|
223
|
+
var OgTitle = z4.string().max(512, { message: "tooLong" }).nullable().optional().brand();
|
|
224
|
+
var OgDescription = z4.string().max(1024, { message: "tooLong" }).nullable().optional().brand();
|
|
225
|
+
var OgImageUrl = z4.string().max(1024, { message: "tooLong" }).refine(isValidHttpUrl, { message: "invalidFormat" }).nullable().optional().brand();
|
|
226
|
+
var Base2 = z4.object({
|
|
227
|
+
id: Id,
|
|
228
|
+
userId: UserId,
|
|
229
|
+
categoryId: Id,
|
|
230
|
+
title: ArticleTitle,
|
|
231
|
+
quote: Quote,
|
|
232
|
+
url: Url,
|
|
233
|
+
createdAt: CreatedAt,
|
|
234
|
+
ogTitle: OgTitle,
|
|
235
|
+
ogDescription: OgDescription,
|
|
236
|
+
ogImageUrl: OgImageUrl
|
|
237
|
+
});
|
|
238
|
+
var UnexportedArticle = Base2.extend({ status: UnexportedStatus });
|
|
239
|
+
var LastUpdatedArticle = Base2.extend({ status: LastUpdatedStatus });
|
|
240
|
+
var ExportedArticle = Base2.extend(ExportedStatus.shape);
|
|
241
|
+
|
|
242
|
+
// ../core/notes/entities/note-entity.ts
|
|
243
|
+
import { z as z5 } from "zod";
|
|
244
|
+
var NoteTitle = z5.string({ message: "required" }).min(1, { message: "required" }).max(64, { message: "tooLong" }).brand();
|
|
245
|
+
var Markdown = z5.string({ message: "required" }).min(1, { message: "required" }).brand();
|
|
246
|
+
var Base3 = z5.object({
|
|
247
|
+
id: Id,
|
|
248
|
+
userId: UserId,
|
|
249
|
+
title: NoteTitle,
|
|
250
|
+
markdown: Markdown,
|
|
251
|
+
createdAt: CreatedAt
|
|
252
|
+
});
|
|
253
|
+
var UnexportedNote = Base3.extend({ status: UnexportedStatus });
|
|
254
|
+
var LastUpdatedNote = Base3.extend({ status: LastUpdatedStatus });
|
|
255
|
+
var ExportedNote = Base3.extend(ExportedStatus.shape);
|
|
256
|
+
|
|
257
|
+
// ../application/dist/batch/validate-import.js
|
|
258
|
+
function validateImport(change) {
|
|
259
|
+
switch (change.domain) {
|
|
260
|
+
case "articles":
|
|
261
|
+
ArticleTitle.parse(change.values.title);
|
|
262
|
+
Url.parse(change.values.url);
|
|
263
|
+
CategoryName.parse(change.values.categoryName);
|
|
264
|
+
Quote.parse(change.values.quote);
|
|
265
|
+
OgTitle.parse(change.values.ogTitle);
|
|
266
|
+
OgDescription.parse(change.values.ogDescription);
|
|
267
|
+
OgImageUrl.parse(change.values.ogImageUrl);
|
|
268
|
+
break;
|
|
269
|
+
case "notes":
|
|
270
|
+
NoteTitle.parse(change.values.title);
|
|
271
|
+
Markdown.parse(change.values.markdown);
|
|
272
|
+
break;
|
|
273
|
+
case "books":
|
|
274
|
+
ISBN.parse(change.values.isbn);
|
|
275
|
+
BookTitle.parse(change.values.title);
|
|
276
|
+
Rating.parse(change.values.rating);
|
|
277
|
+
Tags.parse(change.values.tags);
|
|
278
|
+
break;
|
|
279
|
+
case "images":
|
|
280
|
+
Path.parse(change.values.path);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ../application/dist/error.js
|
|
286
|
+
var ApplicationError = class extends Error {
|
|
287
|
+
code;
|
|
288
|
+
constructor(code) {
|
|
289
|
+
super(code);
|
|
290
|
+
this.code = code;
|
|
291
|
+
this.name = "ApplicationError";
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// ../application/dist/batch/synchronize-content.js
|
|
296
|
+
function preserveCover(change, item, previous) {
|
|
297
|
+
if (item.preserveImagePath && change.domain === "books" && previous && "imagePath" in previous.values)
|
|
298
|
+
return {
|
|
299
|
+
...change,
|
|
300
|
+
values: { ...change.values, imagePath: previous.values.imagePath }
|
|
301
|
+
};
|
|
302
|
+
return change;
|
|
303
|
+
}
|
|
304
|
+
async function saveItem(owner, change, item, creating, deps) {
|
|
305
|
+
if (item.media && (change.domain === "images" || change.domain === "books")) {
|
|
306
|
+
if (!deps.media)
|
|
307
|
+
throw new ApplicationError("UNAVAILABLE");
|
|
308
|
+
await deps.media.put(change.domain, item.media.path, item.media.bytes);
|
|
309
|
+
}
|
|
310
|
+
await deps.repository.save(owner, change, creating, deps.clock.now());
|
|
311
|
+
}
|
|
312
|
+
async function prune(domain, owner, records, present, deps, options) {
|
|
313
|
+
let deletedCount = 0;
|
|
314
|
+
for (const record of records) {
|
|
315
|
+
if (record.status !== "EXPORTED" || present.has(record.key))
|
|
316
|
+
continue;
|
|
317
|
+
const count = options.dryRun ? 1 : await deps.repository.deleteExported(domain, owner, record.id);
|
|
318
|
+
if (count && !options.dryRun && domain === "images" && deps.media && !await deps.repository.isMediaReferenced(record.key))
|
|
319
|
+
await deps.media.removeImage(record.key);
|
|
320
|
+
deletedCount += count;
|
|
321
|
+
if (count)
|
|
322
|
+
options.onProgress?.({ action: "deleted", key: record.key });
|
|
323
|
+
}
|
|
324
|
+
return deletedCount;
|
|
325
|
+
}
|
|
326
|
+
async function synchronizeContent(domain, owner, source, deps, options) {
|
|
327
|
+
const records = await deps.repository.list(domain, owner);
|
|
328
|
+
const existing = new Map(records.map((record) => [record.key, record]));
|
|
329
|
+
const present = /* @__PURE__ */ new Set();
|
|
330
|
+
const summary = {
|
|
331
|
+
insertedCount: 0,
|
|
332
|
+
updatedCount: 0,
|
|
333
|
+
skippedCount: 0,
|
|
334
|
+
deletedCount: 0
|
|
335
|
+
};
|
|
336
|
+
for await (const item of source) {
|
|
337
|
+
let change = importChange(domain, "", item.values);
|
|
338
|
+
validateImport(change);
|
|
339
|
+
const key = importKey(change.values);
|
|
340
|
+
present.add(key);
|
|
341
|
+
const previous = existing.get(key);
|
|
342
|
+
change = preserveCover(change, item, previous);
|
|
343
|
+
if (previous && (domain === "images" || JSON.stringify(previous.values) === JSON.stringify(change.values))) {
|
|
344
|
+
summary.skippedCount++;
|
|
345
|
+
options.onProgress?.({ action: "skipped", key });
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
change = { ...change, id: previous?.id ?? makeId() };
|
|
349
|
+
if (!options.dryRun)
|
|
350
|
+
await saveItem(owner, change, item, !previous, deps);
|
|
351
|
+
existing.set(key, {
|
|
352
|
+
id: change.id,
|
|
353
|
+
key,
|
|
354
|
+
status: previous?.status ?? "EXPORTED",
|
|
355
|
+
values: change.values
|
|
356
|
+
});
|
|
357
|
+
if (previous)
|
|
358
|
+
summary.updatedCount++;
|
|
359
|
+
else
|
|
360
|
+
summary.insertedCount++;
|
|
361
|
+
options.onProgress?.({ action: previous ? "updated" : "inserted", key });
|
|
362
|
+
}
|
|
363
|
+
summary.deletedCount = await prune(domain, owner, records, present, deps, options);
|
|
364
|
+
return summary;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ../infrastructure/dist/persistence-errors.js
|
|
368
|
+
async function withPersistenceErrors(work, duplicate = "DUPLICATE") {
|
|
369
|
+
try {
|
|
370
|
+
return await work();
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (error instanceof import_generated.Prisma.PrismaClientKnownRequestError) {
|
|
373
|
+
if (error.code === "P2002")
|
|
374
|
+
throw new ApplicationError(duplicate);
|
|
375
|
+
if (error.code === "P2025")
|
|
376
|
+
throw new ApplicationError("NOT_FOUND");
|
|
377
|
+
}
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ../infrastructure/dist/batch/content-import-repository.js
|
|
383
|
+
import { z as z6 } from "zod";
|
|
384
|
+
async function categoryId(tx, owner, name, now) {
|
|
385
|
+
const existing = await tx.category.findUnique({
|
|
386
|
+
where: { name_userId: { name, userId: owner } },
|
|
387
|
+
select: { id: true }
|
|
388
|
+
});
|
|
389
|
+
if (existing)
|
|
390
|
+
return existing.id;
|
|
391
|
+
const created = await tx.category.create({
|
|
392
|
+
data: { id: makeId(), name, userId: owner, createdAt: now },
|
|
393
|
+
select: { id: true }
|
|
394
|
+
});
|
|
395
|
+
return created.id;
|
|
396
|
+
}
|
|
397
|
+
function createContentImportRepository(prisma) {
|
|
398
|
+
return {
|
|
399
|
+
async list(domain, owner) {
|
|
400
|
+
let rows;
|
|
401
|
+
switch (domain) {
|
|
402
|
+
case "articles":
|
|
403
|
+
rows = (await prisma.article.findMany({
|
|
404
|
+
where: { userId: owner },
|
|
405
|
+
include: { Category: { select: { name: true } } }
|
|
406
|
+
})).map(({ Category, ...row }) => Object.assign(row, { categoryName: Category.name }));
|
|
407
|
+
break;
|
|
408
|
+
case "notes":
|
|
409
|
+
rows = await prisma.note.findMany({ where: { userId: owner } });
|
|
410
|
+
break;
|
|
411
|
+
case "books":
|
|
412
|
+
rows = await prisma.book.findMany({ where: { userId: owner } });
|
|
413
|
+
break;
|
|
414
|
+
case "images":
|
|
415
|
+
rows = await prisma.image.findMany({ where: { userId: owner } });
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
return rows.map((row) => {
|
|
419
|
+
const { id, status } = z6.object({ id: z6.string(), status: statusSchema }).parse(row);
|
|
420
|
+
const change = importChange(domain, id, row);
|
|
421
|
+
return {
|
|
422
|
+
id,
|
|
423
|
+
status,
|
|
424
|
+
key: importKey(change.values),
|
|
425
|
+
values: change.values
|
|
426
|
+
};
|
|
427
|
+
});
|
|
428
|
+
},
|
|
429
|
+
async save(owner, change, creating, now) {
|
|
430
|
+
await withPersistenceErrors(() => withTransactionRetry(() => prisma.$transaction(async (tx) => {
|
|
431
|
+
const where = { id: change.id, userId: owner };
|
|
432
|
+
const base = {
|
|
433
|
+
id: change.id,
|
|
434
|
+
userId: owner,
|
|
435
|
+
status: "EXPORTED",
|
|
436
|
+
createdAt: now,
|
|
437
|
+
exportedAt: now
|
|
438
|
+
};
|
|
439
|
+
switch (change.domain) {
|
|
440
|
+
case "articles": {
|
|
441
|
+
const { categoryName, ...values } = change.values;
|
|
442
|
+
const category = await categoryId(tx, owner, categoryName, now);
|
|
443
|
+
const data = { ...values, categoryId: category };
|
|
444
|
+
if (creating)
|
|
445
|
+
await tx.article.create({ data: { ...base, ...data } });
|
|
446
|
+
else
|
|
447
|
+
await tx.article.update({ where, data });
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case "notes":
|
|
451
|
+
if (creating)
|
|
452
|
+
await tx.note.create({ data: { ...base, ...change.values } });
|
|
453
|
+
else
|
|
454
|
+
await tx.note.update({ where, data: change.values });
|
|
455
|
+
break;
|
|
456
|
+
case "books":
|
|
457
|
+
if (creating)
|
|
458
|
+
await tx.book.create({ data: { ...base, ...change.values } });
|
|
459
|
+
else
|
|
460
|
+
await tx.book.update({ where, data: change.values });
|
|
461
|
+
break;
|
|
462
|
+
case "images":
|
|
463
|
+
if (creating)
|
|
464
|
+
await tx.image.create({
|
|
465
|
+
data: { ...base, ...change.values }
|
|
466
|
+
});
|
|
467
|
+
else
|
|
468
|
+
await tx.image.update({ where, data: change.values });
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
})));
|
|
472
|
+
},
|
|
473
|
+
async deleteExported(domain, owner, id) {
|
|
474
|
+
const where = { id, userId: owner, status: "EXPORTED" };
|
|
475
|
+
switch (domain) {
|
|
476
|
+
case "articles":
|
|
477
|
+
return (await prisma.article.deleteMany({ where })).count;
|
|
478
|
+
case "notes":
|
|
479
|
+
return (await prisma.note.deleteMany({ where })).count;
|
|
480
|
+
case "books":
|
|
481
|
+
return (await prisma.book.deleteMany({ where })).count;
|
|
482
|
+
case "images":
|
|
483
|
+
return (await prisma.image.deleteMany({ where })).count;
|
|
484
|
+
}
|
|
485
|
+
throw new Error("Unsupported content domain");
|
|
486
|
+
},
|
|
487
|
+
async isMediaReferenced(path) {
|
|
488
|
+
return await prisma.image.count({ where: { path } }) > 0;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ../infrastructure/dist/batch/import-media-store.js
|
|
494
|
+
function createImportMediaStore(storage, processor) {
|
|
495
|
+
return {
|
|
496
|
+
async put(domain, path, bytes) {
|
|
497
|
+
const target = storage[domain];
|
|
498
|
+
try {
|
|
499
|
+
await target.getImageOrThrow(path, false);
|
|
500
|
+
} catch {
|
|
501
|
+
await target.uploadImage(path, bytes, false);
|
|
502
|
+
}
|
|
503
|
+
await target.uploadImage(path, await processor.createThumbnail(bytes, 192, 192), true);
|
|
504
|
+
},
|
|
505
|
+
async removeImage(path) {
|
|
506
|
+
await storage.images.deleteImage(path, false);
|
|
507
|
+
await storage.images.deleteImage(path, true);
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ../infrastructure/dist/shared/books-storage-service.js
|
|
513
|
+
var ORIGINAL_BOOK_IMAGE_PATH = "books/original";
|
|
514
|
+
var THUMBNAIL_BOOK_IMAGE_PATH = "books/thumbnail";
|
|
515
|
+
function createBooksStorageService(minioClient, bucketName) {
|
|
516
|
+
return createStorageService(minioClient, bucketName, {
|
|
517
|
+
originalPrefix: ORIGINAL_BOOK_IMAGE_PATH,
|
|
518
|
+
thumbnailPrefix: THUMBNAIL_BOOK_IMAGE_PATH
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// ../infrastructure/dist/shared/minio-storage-service.js
|
|
523
|
+
function createImagesStorageService(minioClient, bucketName) {
|
|
524
|
+
return createStorageService(minioClient, bucketName, {
|
|
525
|
+
originalPrefix: "images/original",
|
|
526
|
+
thumbnailPrefix: "images/thumbnail"
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ../infrastructure/dist/system-clock.js
|
|
531
|
+
var systemClock = { now: () => /* @__PURE__ */ new Date() };
|
|
532
|
+
|
|
533
|
+
// src/lib/run-content-import.ts
|
|
534
|
+
async function runContentImport(domain, source) {
|
|
535
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
536
|
+
const owner = process.env.USERNAME_TO_EXPORT;
|
|
537
|
+
if (!databaseUrl || !owner)
|
|
538
|
+
throw new Error("DATABASE_URL and USERNAME_TO_EXPORT are required.");
|
|
539
|
+
const dryRun = process.argv.includes("--dry-run");
|
|
540
|
+
const prisma = createPrismaClient(databaseUrl);
|
|
541
|
+
const notification = createPushoverService({
|
|
542
|
+
url: process.env.PUSHOVER_URL ?? "",
|
|
543
|
+
userKey: process.env.PUSHOVER_USER_KEY ?? "",
|
|
544
|
+
appToken: process.env.PUSHOVER_APP_TOKEN ?? ""
|
|
545
|
+
});
|
|
546
|
+
try {
|
|
547
|
+
const mediaDomain = domain === "images" || domain === "books";
|
|
548
|
+
const bucket = process.env.MINIO_BUCKET_NAME;
|
|
549
|
+
if (mediaDomain && !dryRun && !bucket)
|
|
550
|
+
throw new Error("MINIO_BUCKET_NAME is required.");
|
|
551
|
+
const client = mediaDomain && !dryRun ? createMinioClient() : void 0;
|
|
552
|
+
const media = client && bucket ? createImportMediaStore(
|
|
553
|
+
{
|
|
554
|
+
images: createImagesStorageService(client, bucket),
|
|
555
|
+
books: createBooksStorageService(client, bucket)
|
|
556
|
+
},
|
|
557
|
+
sharpImageProcessor
|
|
558
|
+
) : void 0;
|
|
559
|
+
const summary = await synchronizeContent(
|
|
560
|
+
domain,
|
|
561
|
+
owner,
|
|
562
|
+
source(process.env.S_CONTENTS_PATH ?? process.cwd()),
|
|
563
|
+
{
|
|
564
|
+
repository: createContentImportRepository(prisma),
|
|
565
|
+
clock: systemClock,
|
|
566
|
+
media
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
dryRun,
|
|
570
|
+
onProgress: (event) => console.log(
|
|
571
|
+
`${dryRun ? "[dry-run] " : ""}${event.action}: ${event.key}`
|
|
572
|
+
)
|
|
573
|
+
}
|
|
574
|
+
);
|
|
575
|
+
console.log(summary);
|
|
576
|
+
await notification.notifyInfo(`ingest-${domain} completed`, { caller: `ingest-${domain}` }).catch((error) => console.error("\u901A\u77E5\u5931\u6557:", error));
|
|
577
|
+
} catch (error) {
|
|
578
|
+
console.error(error);
|
|
579
|
+
process.exitCode = 1;
|
|
580
|
+
await notification.notifyError(`ingest-${domain} failed`, { caller: `ingest-${domain}` }).catch((notifyError) => console.error("\u901A\u77E5\u5931\u6557:", notifyError));
|
|
581
|
+
} finally {
|
|
582
|
+
await prisma.$disconnect();
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
export {
|
|
587
|
+
makeISBN,
|
|
588
|
+
sharpImageProcessor,
|
|
589
|
+
runContentImport
|
|
590
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// ../notification/dist/errors.js
|
|
4
|
+
var NotificationError = class extends Error {
|
|
5
|
+
constructor(message = "notificationSend") {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "NotificationError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// ../notification/dist/pushover.js
|
|
12
|
+
var PushoverService = class {
|
|
13
|
+
config;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Sends a message to the Pushover API.
|
|
19
|
+
*
|
|
20
|
+
* @param message - The message to send
|
|
21
|
+
* @param priority - The priority level (-1 = low, 0 = normal, 1 = high)
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
async sendMessage(message, priority = 0) {
|
|
26
|
+
try {
|
|
27
|
+
const body = new URLSearchParams({
|
|
28
|
+
token: this.config.appToken,
|
|
29
|
+
user: this.config.userKey,
|
|
30
|
+
message,
|
|
31
|
+
priority: String(priority)
|
|
32
|
+
});
|
|
33
|
+
const result = await fetch(this.config.url, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
36
|
+
body
|
|
37
|
+
});
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
throw new NotificationError();
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof NotificationError) {
|
|
43
|
+
console.error(`[NotificationService] Failed to send notification: ${error.message}`);
|
|
44
|
+
} else {
|
|
45
|
+
console.error("[NotificationService] Unknown error occurred while sending notification:", error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async notifyError(message, context) {
|
|
50
|
+
const enrichedMessage = `[ERROR] ${context.caller}: ${message}`;
|
|
51
|
+
await this.sendMessage(enrichedMessage, 1);
|
|
52
|
+
}
|
|
53
|
+
async notifyWarning(message, context) {
|
|
54
|
+
const enrichedMessage = `[WARN] ${context.caller}: ${message}`;
|
|
55
|
+
await this.sendMessage(enrichedMessage, 0);
|
|
56
|
+
}
|
|
57
|
+
async notifyInfo(message, context) {
|
|
58
|
+
const enrichedMessage = `[INFO] ${context.caller}: ${message}`;
|
|
59
|
+
await this.sendMessage(enrichedMessage, -1);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function createPushoverService(config) {
|
|
63
|
+
return new PushoverService(config);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ../notification/dist/web-push.js
|
|
67
|
+
import webPush from "web-push";
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
createPushoverService
|
|
71
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/lib/glob-paths.ts
|
|
4
|
+
import { glob } from "node:fs/promises";
|
|
5
|
+
async function globPaths(pattern, options = {}) {
|
|
6
|
+
const paths = [];
|
|
7
|
+
for await (const path of glob(pattern, options)) {
|
|
8
|
+
paths.push(path);
|
|
9
|
+
}
|
|
10
|
+
return paths.toSorted((left, right) => left.localeCompare(right, "en"));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
globPaths
|
|
15
|
+
};
|