ocean-brain 0.3.3 → 0.4.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/dist/mcp.js +151 -37
- package/package.json +5 -5
- package/scripts/postinstall-prisma.mjs +3 -2
- package/server/client/dist/assets/Calendar-CSXEuWmQ.js +25 -0
- package/server/client/dist/assets/Callout-Ap1OcxWN.js +1 -0
- package/server/client/dist/assets/Graph-C0Ax9qjI.js +1 -0
- package/server/client/dist/assets/{Image-ZxF459sC.js → Image-CSbcG-5a.js} +1 -1
- package/server/client/dist/assets/{Image.es-CFTbON7k.js → Image.es-Fbe7aDDo.js} +1 -1
- package/server/client/dist/assets/{ModalActionRow-Hg6MY61u.js → ModalActionRow-DjI5b4bl.js} +1 -1
- package/server/client/dist/assets/Note-BY6WgYLq.js +1 -0
- package/server/client/dist/assets/Note-W4f6H9K0.css +1 -0
- package/server/client/dist/assets/Reminders-vRksvSQE.js +1 -0
- package/server/client/dist/assets/Search-Cq4tuiJO.js +1 -0
- package/server/client/dist/assets/{SurfaceCard-BV6y4bCo.js → SurfaceCard-Br6TjX5W.js} +1 -1
- package/server/client/dist/assets/Tag-ARDLA-Pb.js +1 -0
- package/server/client/dist/assets/TagNotes-CDGVrCJU.js +1 -0
- package/server/client/dist/assets/{Trash.es-CPtRLE0y.js → Trash.es-BfCEiL3-.js} +1 -1
- package/server/client/dist/assets/ViewNotes-ZBOQ1adH.js +1 -0
- package/server/client/dist/assets/Views-DQHPfdwM.js +1 -0
- package/server/client/dist/assets/{image.api-Dsrx9S-G.js → image.api-DGXlQ_Ca.js} +1 -1
- package/server/client/dist/assets/index-B28TO4FC.js +1 -0
- package/server/client/dist/assets/{index-CD2bFEqK.css → index-Bm9l_L20.css} +1 -1
- package/server/client/dist/assets/index-Bsx4P7EA.js +210 -0
- package/server/client/dist/assets/manage-image-Dg297A_h.js +1 -0
- package/server/client/dist/assets/manage-image-detail-hk6jWyiB.js +1 -0
- package/server/client/dist/assets/mcp-CSjmBjFR.js +1 -0
- package/server/client/dist/assets/{note-core-BCgMq5QA.js → note-core-GgFcksAF.js} +1 -1
- package/server/client/dist/assets/{note-vendor-BofYbzmZ.js → note-vendor-5N5mRkIT.js} +3 -3
- package/server/client/dist/assets/{placeholder-MNsco0AT.js → placeholder-Bmiowo28.js} +4 -4
- package/server/client/dist/assets/trash-CM7SbVtW.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-yJak8FzP.js → useReminderMutate-DsIvVpCQ.js} +2 -2
- package/server/client/dist/assets/view-dashboard-CP8nhfxQ.js +90 -0
- package/server/client/dist/index.html +4 -4
- package/server/dist/app.js +8 -84
- package/server/dist/app.js.map +1 -1
- package/server/dist/features/auth/http/api.js +43 -0
- package/server/dist/features/auth/http/api.js.map +1 -0
- package/server/dist/features/auth/http/login-page.js +28 -0
- package/server/dist/features/auth/http/login-page.js.map +1 -0
- package/server/dist/features/auth/http/pages.js +50 -0
- package/server/dist/features/auth/http/pages.js.map +1 -0
- package/server/dist/features/auth/service.js +94 -0
- package/server/dist/features/auth/service.js.map +1 -0
- package/server/dist/features/cache/graphql/cache.field.resolver.js +5 -0
- package/server/dist/features/cache/graphql/cache.field.resolver.js.map +1 -0
- package/server/dist/features/cache/graphql/cache.mutation.resolver.js +49 -0
- package/server/dist/features/cache/graphql/cache.mutation.resolver.js.map +1 -0
- package/server/dist/features/cache/graphql/cache.query.resolver.js +21 -0
- package/server/dist/features/cache/graphql/cache.query.resolver.js.map +1 -0
- package/server/dist/features/cache/graphql/cache.resolver.js +12 -0
- package/server/dist/features/cache/graphql/cache.resolver.js.map +1 -0
- package/server/dist/features/cache/graphql/cache.type-defs.js +32 -0
- package/server/dist/features/cache/graphql/cache.type-defs.js.map +1 -0
- package/server/dist/features/cache/graphql/index.js +21 -0
- package/server/dist/features/cache/graphql/index.js.map +1 -0
- package/server/dist/features/image/graphql/image.field.resolver.js +14 -0
- package/server/dist/features/image/graphql/image.field.resolver.js.map +1 -0
- package/server/dist/features/image/graphql/image.mutation.resolver.js +21 -0
- package/server/dist/features/image/graphql/image.mutation.resolver.js.map +1 -0
- package/server/dist/features/image/graphql/image.query.resolver.js +40 -0
- package/server/dist/features/image/graphql/image.query.resolver.js.map +1 -0
- package/server/dist/features/image/graphql/image.resolver.js +12 -0
- package/server/dist/features/image/graphql/image.resolver.js.map +1 -0
- package/server/dist/features/image/graphql/image.type-defs.js +31 -0
- package/server/dist/features/image/graphql/image.type-defs.js.map +1 -0
- package/server/dist/features/image/graphql/index.js +16 -0
- package/server/dist/features/image/graphql/index.js.map +1 -0
- package/server/dist/features/image/http/upload.js +48 -0
- package/server/dist/features/image/http/upload.js.map +1 -0
- package/server/dist/features/image/services/delete.js +52 -0
- package/server/dist/features/image/services/delete.js.map +1 -0
- package/server/dist/{modules/remote-image.js → features/image/services/remote-fetch.js} +1 -1
- package/server/dist/features/image/services/remote-fetch.js.map +1 -0
- package/server/dist/{modules/image-upload.js → features/image/services/upload.js} +4 -8
- package/server/dist/features/image/services/upload.js.map +1 -0
- package/server/dist/{views/mcp-admin.js → features/mcp-admin/http/handlers.js} +3 -3
- package/server/dist/features/mcp-admin/http/handlers.js.map +1 -0
- package/server/dist/{modules/mcp-admin.js → features/mcp-admin/service.js} +3 -3
- package/server/dist/features/mcp-admin/service.js.map +1 -0
- package/server/dist/features/note/graphql/index.js +8 -0
- package/server/dist/features/note/graphql/index.js.map +1 -0
- package/server/dist/features/note/graphql/note.field.resolver.js +12 -0
- package/server/dist/features/note/graphql/note.field.resolver.js.map +1 -0
- package/server/dist/features/note/graphql/note.graphql.shared.js +23 -0
- package/server/dist/features/note/graphql/note.graphql.shared.js.map +1 -0
- package/server/dist/features/note/graphql/note.mutation.resolver.js +140 -0
- package/server/dist/features/note/graphql/note.mutation.resolver.js.map +1 -0
- package/server/dist/features/note/graphql/note.query.resolver.js +382 -0
- package/server/dist/features/note/graphql/note.query.resolver.js.map +1 -0
- package/server/dist/features/note/graphql/note.resolver.js +14 -0
- package/server/dist/features/note/graphql/note.resolver.js.map +1 -0
- package/server/dist/features/note/graphql/note.type-defs.js +184 -0
- package/server/dist/features/note/graphql/note.type-defs.js.map +1 -0
- package/server/dist/{views/note.js → features/note/http/mcp.js} +27 -15
- package/server/dist/features/note/http/mcp.js.map +1 -0
- package/server/dist/{modules/note-authoring.js → features/note/services/authoring.js} +25 -7
- package/server/dist/features/note/services/authoring.js.map +1 -0
- package/server/dist/{modules/note-cleanup.js → features/note/services/cleanup.js} +14 -23
- package/server/dist/features/note/services/cleanup.js.map +1 -0
- package/server/dist/features/note/services/search-backfill.js +84 -0
- package/server/dist/features/note/services/search-backfill.js.map +1 -0
- package/server/dist/features/note/services/search.js +197 -0
- package/server/dist/features/note/services/search.js.map +1 -0
- package/server/dist/{modules/note-snapshot.js → features/note/services/snapshot.js} +51 -49
- package/server/dist/features/note/services/snapshot.js.map +1 -0
- package/server/dist/features/note/services/tag-filter.js +18 -0
- package/server/dist/features/note/services/tag-filter.js.map +1 -0
- package/server/dist/{modules/note-trash.js → features/note/services/trash.js} +28 -23
- package/server/dist/features/note/services/trash.js.map +1 -0
- package/server/dist/features/placeholder/graphql/index.js +27 -0
- package/server/dist/features/placeholder/graphql/index.js.map +1 -0
- package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js +5 -0
- package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js.map +1 -0
- package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js +72 -0
- package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js.map +1 -0
- package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js +36 -0
- package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js.map +1 -0
- package/server/dist/features/placeholder/graphql/placeholder.resolver.js +12 -0
- package/server/dist/features/placeholder/graphql/placeholder.resolver.js.map +1 -0
- package/server/dist/features/placeholder/graphql/placeholder.type-defs.js +50 -0
- package/server/dist/features/placeholder/graphql/placeholder.type-defs.js.map +1 -0
- package/server/dist/features/reminder/graphql/index.js +27 -0
- package/server/dist/features/reminder/graphql/index.js.map +1 -0
- package/server/dist/features/reminder/graphql/reminder.field.resolver.js +10 -0
- package/server/dist/features/reminder/graphql/reminder.field.resolver.js.map +1 -0
- package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js +59 -0
- package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js.map +1 -0
- package/server/dist/features/reminder/graphql/reminder.query.resolver.js +79 -0
- package/server/dist/features/reminder/graphql/reminder.query.resolver.js.map +1 -0
- package/server/dist/features/reminder/graphql/reminder.resolver.js +12 -0
- package/server/dist/features/reminder/graphql/reminder.resolver.js.map +1 -0
- package/server/dist/features/reminder/graphql/reminder.type-defs.js +51 -0
- package/server/dist/features/reminder/graphql/reminder.type-defs.js.map +1 -0
- package/server/dist/features/tag/graphql/index.js +14 -0
- package/server/dist/features/tag/graphql/index.js.map +1 -0
- package/server/dist/features/tag/graphql/tag.field.resolver.js +10 -0
- package/server/dist/features/tag/graphql/tag.field.resolver.js.map +1 -0
- package/server/dist/features/tag/graphql/tag.mutation.resolver.js +15 -0
- package/server/dist/features/tag/graphql/tag.mutation.resolver.js.map +1 -0
- package/server/dist/features/tag/graphql/tag.query.resolver.js +26 -0
- package/server/dist/features/tag/graphql/tag.query.resolver.js.map +1 -0
- package/server/dist/features/tag/graphql/tag.resolver.js +12 -0
- package/server/dist/features/tag/graphql/tag.resolver.js.map +1 -0
- package/server/dist/features/tag/graphql/tag.type-defs.js +46 -0
- package/server/dist/features/tag/graphql/tag.type-defs.js.map +1 -0
- package/server/dist/{views/tag.js → features/tag/http/mcp.js} +3 -3
- package/server/dist/features/tag/http/mcp.js.map +1 -0
- package/server/dist/{modules/tag-organization.js → features/tag/services/organization.js} +38 -8
- package/server/dist/features/tag/services/organization.js.map +1 -0
- package/server/dist/features/view/graphql/index.js +11 -0
- package/server/dist/features/view/graphql/index.js.map +1 -0
- package/server/dist/features/view/graphql/view.mutation.resolver.js +53 -0
- package/server/dist/features/view/graphql/view.mutation.resolver.js.map +1 -0
- package/server/dist/features/view/graphql/view.query.resolver.js +29 -0
- package/server/dist/features/view/graphql/view.query.resolver.js.map +1 -0
- package/server/dist/features/view/graphql/view.resolver.js +10 -0
- package/server/dist/features/view/graphql/view.resolver.js.map +1 -0
- package/server/dist/features/view/graphql/view.type-defs.js +63 -0
- package/server/dist/features/view/graphql/view.type-defs.js.map +1 -0
- package/server/dist/features/view/services/workspace.js +432 -0
- package/server/dist/features/view/services/workspace.js.map +1 -0
- package/server/dist/main.js +14 -0
- package/server/dist/main.js.map +1 -1
- package/server/dist/modules/auth-guard.js +10 -9
- package/server/dist/modules/auth-guard.js.map +1 -1
- package/server/dist/modules/auth-mode.js +1 -3
- package/server/dist/modules/auth-mode.js.map +1 -1
- package/server/dist/modules/blocknote.js +96 -33
- package/server/dist/modules/blocknote.js.map +1 -1
- package/server/dist/modules/data-maintenance.js +75 -0
- package/server/dist/modules/data-maintenance.js.map +1 -0
- package/server/dist/modules/error-handler.js.map +1 -1
- package/server/dist/modules/logger.js +5 -23
- package/server/dist/modules/logger.js.map +1 -1
- package/server/dist/modules/mcp-auth.js +4 -5
- package/server/dist/modules/mcp-auth.js.map +1 -1
- package/server/dist/modules/mcp-token.js.map +1 -1
- package/server/dist/modules/prisma-runtime.js +2 -7
- package/server/dist/modules/prisma-runtime.js.map +1 -1
- package/server/dist/modules/recovery-retention.js +1 -1
- package/server/dist/modules/recovery-retention.js.map +1 -1
- package/server/dist/modules/server-events-handler.js +34 -0
- package/server/dist/modules/server-events-handler.js.map +1 -0
- package/server/dist/modules/server-events.js +32 -0
- package/server/dist/modules/server-events.js.map +1 -0
- package/server/dist/modules/use-async.js.map +1 -1
- package/server/dist/paths.js.map +1 -1
- package/server/dist/routes/api.js +34 -0
- package/server/dist/routes/api.js.map +1 -0
- package/server/dist/routes/auth-pages.js +11 -0
- package/server/dist/routes/auth-pages.js.map +1 -0
- package/server/dist/routes/client.js +27 -0
- package/server/dist/routes/client.js.map +1 -0
- package/server/dist/routes/graphql.js +33 -0
- package/server/dist/routes/graphql.js.map +1 -0
- package/server/dist/routes/index.js +11 -0
- package/server/dist/routes/index.js.map +1 -0
- package/server/dist/routes/mcp.js +30 -0
- package/server/dist/routes/mcp.js.map +1 -0
- package/server/dist/schema/index.js +11 -8
- package/server/dist/schema/index.js.map +1 -1
- package/server/dist/types/index.js.map +1 -1
- package/server/prisma/migrations/20260413143000_0014_tag_name_unique/migration.sql +32 -0
- package/server/prisma/migrations/20260414143000_0015_note_searchable_text/migration.sql +5 -0
- package/server/prisma/migrations/20260416090000_0016_view_workspace/migration.sql +50 -0
- package/server/prisma/schema.prisma +66 -12
- package/server/client/dist/assets/Calendar-DyXzs5M3.js +0 -25
- package/server/client/dist/assets/Graph-C8K4PJOn.js +0 -1
- package/server/client/dist/assets/Note-BbgZvrC3.js +0 -1
- package/server/client/dist/assets/Note-PEevDKBh.css +0 -1
- package/server/client/dist/assets/Reminders-jFhljND4.js +0 -1
- package/server/client/dist/assets/Search-X6QIR_m2.js +0 -1
- package/server/client/dist/assets/Tag-Ba2Dw4gH.js +0 -1
- package/server/client/dist/assets/TagNotes-BRJv6sDI.js +0 -1
- package/server/client/dist/assets/index-BglnLSet.js +0 -205
- package/server/client/dist/assets/index-DpqKngJz.js +0 -1
- package/server/client/dist/assets/manage-image-C6Y_0M5H.js +0 -1
- package/server/client/dist/assets/manage-image-detail-Dv3xofpe.js +0 -1
- package/server/client/dist/assets/mcp-BijQkpfY.js +0 -1
- package/server/client/dist/assets/trash-CiFJm_PK.js +0 -1
- package/server/dist/modules/auth.js +0 -25
- package/server/dist/modules/auth.js.map +0 -1
- package/server/dist/modules/image-upload.js.map +0 -1
- package/server/dist/modules/mcp-admin.js.map +0 -1
- package/server/dist/modules/note-authoring.js.map +0 -1
- package/server/dist/modules/note-cleanup.js.map +0 -1
- package/server/dist/modules/note-snapshot.js.map +0 -1
- package/server/dist/modules/note-trash.js.map +0 -1
- package/server/dist/modules/remote-image.js.map +0 -1
- package/server/dist/modules/tag-organization.js.map +0 -1
- package/server/dist/schema/cache/index.js +0 -58
- package/server/dist/schema/cache/index.js.map +0 -1
- package/server/dist/schema/image/index.js +0 -88
- package/server/dist/schema/image/index.js.map +0 -1
- package/server/dist/schema/note/index.js +0 -551
- package/server/dist/schema/note/index.js.map +0 -1
- package/server/dist/schema/placeholder/index.js +0 -105
- package/server/dist/schema/placeholder/index.js.map +0 -1
- package/server/dist/schema/reminder/index.js +0 -161
- package/server/dist/schema/reminder/index.js.map +0 -1
- package/server/dist/schema/tag/index.js +0 -81
- package/server/dist/schema/tag/index.js.map +0 -1
- package/server/dist/urls.js +0 -9
- package/server/dist/urls.js.map +0 -1
- package/server/dist/views/auth.js +0 -305
- package/server/dist/views/auth.js.map +0 -1
- package/server/dist/views/image.js +0 -44
- package/server/dist/views/image.js.map +0 -1
- package/server/dist/views/index.js +0 -6
- package/server/dist/views/index.js.map +0 -1
- package/server/dist/views/mcp-admin.js.map +0 -1
- package/server/dist/views/note.js.map +0 -1
- package/server/dist/views/tag.js.map +0 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { ServerBlockNoteEditor } from "@blocknote/server-util";
|
|
2
|
+
import { ensureTagByName } from "../features/tag/services/organization.js";
|
|
2
3
|
import models from "../models.js";
|
|
3
|
-
|
|
4
|
-
const UNSUPPORTED_MARKDOWN_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
5
|
-
"tableOfContents"
|
|
6
|
-
]);
|
|
4
|
+
const UNSUPPORTED_MARKDOWN_BLOCK_TYPES = /* @__PURE__ */ new Set(["tableOfContents"]);
|
|
7
5
|
const TAG_PLACEHOLDER_PREFIX = "OCEAN_BRAIN_TAG_";
|
|
8
6
|
const TAG_PLACEHOLDER_SUFFIX = "_TOKEN";
|
|
9
7
|
const defaultMarkdownImportDeps = {
|
|
@@ -33,10 +31,87 @@ function stripUnsupportedMarkdownBlocks(blocks) {
|
|
|
33
31
|
function createTagPlaceholder(index) {
|
|
34
32
|
return `${TAG_PLACEHOLDER_PREFIX}${index}${TAG_PLACEHOLDER_SUFFIX}`;
|
|
35
33
|
}
|
|
36
|
-
function
|
|
34
|
+
function isTableContent(content) {
|
|
35
|
+
return !Array.isArray(content) && content?.type === "tableContent";
|
|
36
|
+
}
|
|
37
|
+
function mapBlockContent(content, mapInline) {
|
|
38
|
+
if (Array.isArray(content)) {
|
|
39
|
+
return content.map(mapInline);
|
|
40
|
+
}
|
|
41
|
+
if (!isTableContent(content)) {
|
|
42
|
+
return content;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
...content,
|
|
46
|
+
rows: content.rows?.map((row) => ({
|
|
47
|
+
...row,
|
|
48
|
+
cells: row.cells?.map((cell) => ({
|
|
49
|
+
...cell,
|
|
50
|
+
content: cell.content?.map(mapInline)
|
|
51
|
+
}))
|
|
52
|
+
}))
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async function mapBlockContentAsync(content, mapInline) {
|
|
56
|
+
if (Array.isArray(content)) {
|
|
57
|
+
return (await Promise.all(content.map(mapInline))).flat();
|
|
58
|
+
}
|
|
59
|
+
if (!isTableContent(content)) {
|
|
60
|
+
return content;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...content,
|
|
64
|
+
rows: await Promise.all(
|
|
65
|
+
(content.rows ?? []).map(async (row) => ({
|
|
66
|
+
...row,
|
|
67
|
+
cells: await Promise.all(
|
|
68
|
+
(row.cells ?? []).map(async (cell) => ({
|
|
69
|
+
...cell,
|
|
70
|
+
content: cell.content ? (await Promise.all(cell.content.map(mapInline))).flat() : cell.content
|
|
71
|
+
}))
|
|
72
|
+
)
|
|
73
|
+
}))
|
|
74
|
+
)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function visitBlockContent(content, visitInline) {
|
|
78
|
+
if (Array.isArray(content)) {
|
|
79
|
+
for (const inline of content) {
|
|
80
|
+
visitInline(inline);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!isTableContent(content)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const row of content.rows ?? []) {
|
|
88
|
+
for (const cell of row.cells ?? []) {
|
|
89
|
+
for (const inline of cell.content ?? []) {
|
|
90
|
+
visitInline(inline);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function mapBlocks(blocks, mapContent) {
|
|
37
96
|
return blocks.map((block) => ({
|
|
38
97
|
...block,
|
|
39
|
-
content:
|
|
98
|
+
content: mapContent(block.content),
|
|
99
|
+
children: block.children?.length ? mapBlocks(block.children, mapContent) : []
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
async function mapBlocksAsync(blocks, mapContent) {
|
|
103
|
+
return Promise.all(
|
|
104
|
+
blocks.map(async (block) => ({
|
|
105
|
+
...block,
|
|
106
|
+
content: await mapContent(block.content),
|
|
107
|
+
children: block.children?.length ? await mapBlocksAsync(block.children, mapContent) : []
|
|
108
|
+
}))
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function preprocessCustomInlineContent(blocks, placeholderToTag, nextPlaceholderIndex) {
|
|
112
|
+
return mapBlocks(
|
|
113
|
+
blocks,
|
|
114
|
+
(content) => mapBlockContent(content, (inline) => {
|
|
40
115
|
if (inline.type === "reference") {
|
|
41
116
|
return {
|
|
42
117
|
type: "text",
|
|
@@ -55,9 +130,8 @@ function preprocessCustomInlineContent(blocks, placeholderToTag, nextPlaceholder
|
|
|
55
130
|
};
|
|
56
131
|
}
|
|
57
132
|
return inline;
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
}));
|
|
133
|
+
})
|
|
134
|
+
);
|
|
61
135
|
}
|
|
62
136
|
function restoreTagPlaceholdersInMarkdown(markdown, placeholderToTag) {
|
|
63
137
|
let restoredMarkdown = markdown;
|
|
@@ -91,9 +165,9 @@ function findTagPlaceholderAtCursor(text, cursor, placeholderToTag) {
|
|
|
91
165
|
return null;
|
|
92
166
|
}
|
|
93
167
|
function restoreRemainingTagPlaceholders(blocks, placeholderToTag) {
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
168
|
+
return mapBlocks(
|
|
169
|
+
blocks,
|
|
170
|
+
(content) => mapBlockContent(content, (inline) => {
|
|
97
171
|
if (inline.type !== "text" || typeof inline.text !== "string") {
|
|
98
172
|
return inline;
|
|
99
173
|
}
|
|
@@ -105,9 +179,8 @@ function restoreRemainingTagPlaceholders(blocks, placeholderToTag) {
|
|
|
105
179
|
...inline,
|
|
106
180
|
text
|
|
107
181
|
};
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
}));
|
|
182
|
+
})
|
|
183
|
+
);
|
|
111
184
|
}
|
|
112
185
|
function createTextInline(text, styles = {}) {
|
|
113
186
|
return {
|
|
@@ -199,23 +272,17 @@ async function restoreCustomInlineContent(blocks, deps, placeholderToTag) {
|
|
|
199
272
|
}
|
|
200
273
|
return restored;
|
|
201
274
|
};
|
|
202
|
-
return
|
|
203
|
-
blocks.map(async (block) => ({
|
|
204
|
-
...block,
|
|
205
|
-
content: block.content ? (await Promise.all(block.content.map((inline) => restoreTextInline(inline)))).flat() : void 0,
|
|
206
|
-
children: block.children?.length ? await restoreCustomInlineContent(block.children, deps, placeholderToTag) : []
|
|
207
|
-
}))
|
|
208
|
-
);
|
|
275
|
+
return mapBlocksAsync(blocks, (content) => mapBlockContentAsync(content, restoreTextInline));
|
|
209
276
|
}
|
|
210
277
|
function collectTagIds(blocks) {
|
|
211
278
|
const tagIds = /* @__PURE__ */ new Set();
|
|
212
279
|
const visit = (items) => {
|
|
213
280
|
for (const block of items) {
|
|
214
|
-
|
|
281
|
+
visitBlockContent(block.content, (inline) => {
|
|
215
282
|
if (inline.type === "tag" && typeof inline.props?.id === "string") {
|
|
216
283
|
tagIds.add(inline.props.id);
|
|
217
284
|
}
|
|
218
|
-
}
|
|
285
|
+
});
|
|
219
286
|
if (block.children?.length) {
|
|
220
287
|
visit(block.children);
|
|
221
288
|
}
|
|
@@ -236,13 +303,11 @@ async function blocksToMarkdown(contentJson) {
|
|
|
236
303
|
const blocks = JSON.parse(contentJson);
|
|
237
304
|
const supportedBlocks = stripUnsupportedMarkdownBlocks(blocks);
|
|
238
305
|
const placeholderToTag = /* @__PURE__ */ new Map();
|
|
239
|
-
const processed = preprocessCustomInlineContent(
|
|
240
|
-
supportedBlocks,
|
|
241
|
-
placeholderToTag,
|
|
242
|
-
{ value: 0 }
|
|
243
|
-
);
|
|
306
|
+
const processed = preprocessCustomInlineContent(supportedBlocks, placeholderToTag, { value: 0 });
|
|
244
307
|
const editor = getEditor();
|
|
245
|
-
const markdown = await editor.blocksToMarkdownLossy(
|
|
308
|
+
const markdown = await editor.blocksToMarkdownLossy(
|
|
309
|
+
processed
|
|
310
|
+
);
|
|
246
311
|
return restoreTagPlaceholdersInMarkdown(markdown, placeholderToTag);
|
|
247
312
|
} catch {
|
|
248
313
|
return "";
|
|
@@ -257,9 +322,7 @@ async function markdownToBlocksJson(markdown, deps = defaultMarkdownImportDeps)
|
|
|
257
322
|
deps,
|
|
258
323
|
preprocessedMarkdown.placeholderToTag
|
|
259
324
|
);
|
|
260
|
-
return JSON.stringify(
|
|
261
|
-
restoreRemainingTagPlaceholders(restoredBlocks, preprocessedMarkdown.placeholderToTag)
|
|
262
|
-
);
|
|
325
|
+
return JSON.stringify(restoreRemainingTagPlaceholders(restoredBlocks, preprocessedMarkdown.placeholderToTag));
|
|
263
326
|
}
|
|
264
327
|
function extractTagIdsFromContentJson(contentJson) {
|
|
265
328
|
const blocks = JSON.parse(contentJson);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/blocknote.ts"],"sourcesContent":["import { ServerBlockNoteEditor } from '@blocknote/server-util';\n\nimport models from '~/models.js';\nimport { ensureTagByName } from './tag-organization.js';\n\ninterface BlockNote {\n id?: string;\n type: string;\n props?: Record<string, unknown>;\n content?: BlockNoteInline[];\n children?: BlockNote[];\n text?: string;\n styles?: Record<string, unknown>;\n}\n\ninterface BlockNoteInline {\n type: string;\n props?: Record<string, unknown>;\n text?: string;\n styles?: Record<string, unknown>;\n}\n\ninterface MarkdownImportDeps {\n ensureTag: (name: string) => Promise<\n | {\n id: string;\n name: string;\n }\n | {\n tag: {\n id: string;\n name: string;\n };\n }\n >;\n findNotesByTitle: (title: string) => Promise<Array<{\n id: string;\n title: string;\n }>>;\n}\n\nconst UNSUPPORTED_MARKDOWN_BLOCK_TYPES = new Set([\n 'tableOfContents'\n]);\nconst TAG_PLACEHOLDER_PREFIX = 'OCEAN_BRAIN_TAG_';\nconst TAG_PLACEHOLDER_SUFFIX = '_TOKEN';\n\nconst defaultMarkdownImportDeps: MarkdownImportDeps = {\n ensureTag: async (name) => ensureTagByName(name),\n findNotesByTitle: async (title) => {\n const notes = await models.note.findMany({\n select: {\n id: true,\n title: true\n },\n where: { title },\n orderBy: { createdAt: 'asc' },\n take: 2\n });\n\n return notes.map((note) => ({\n id: String(note.id),\n title: note.title\n }));\n }\n};\n\nfunction stripUnsupportedMarkdownBlocks(blocks: BlockNote[]): BlockNote[] {\n return blocks\n .filter((block) => !UNSUPPORTED_MARKDOWN_BLOCK_TYPES.has(block.type))\n .map((block) => ({\n ...block,\n children: block.children?.length\n ? stripUnsupportedMarkdownBlocks(block.children)\n : []\n }));\n}\n\nfunction createTagPlaceholder(index: number) {\n return `${TAG_PLACEHOLDER_PREFIX}${index}${TAG_PLACEHOLDER_SUFFIX}`;\n}\n\nfunction preprocessCustomInlineContent(\n blocks: BlockNote[],\n placeholderToTag: Map<string, string>,\n nextPlaceholderIndex: { value: number }\n): BlockNote[] {\n return blocks.map((block) => ({\n ...block,\n content: Array.isArray(block.content)\n ? block.content.map((inline) => {\n if (inline.type === 'reference') {\n return {\n type: 'text',\n text: `[[${inline.props?.title || inline.props?.id || ''}]]`,\n styles: {}\n };\n }\n if (inline.type === 'tag') {\n const tag = (inline.props?.tag as string) || '';\n const placeholder = createTagPlaceholder(nextPlaceholderIndex.value++);\n placeholderToTag.set(placeholder, tag);\n return {\n type: 'text',\n text: placeholder,\n styles: {}\n };\n }\n return inline;\n })\n : block.content,\n children: block.children?.length\n ? preprocessCustomInlineContent(block.children, placeholderToTag, nextPlaceholderIndex)\n : []\n }));\n}\n\nfunction restoreTagPlaceholdersInMarkdown(markdown: string, placeholderToTag: Map<string, string>) {\n let restoredMarkdown = markdown;\n\n for (const [placeholder, tag] of placeholderToTag.entries()) {\n restoredMarkdown = restoredMarkdown.split(placeholder).join(`[${tag}]`);\n }\n\n return restoredMarkdown;\n}\n\nfunction preprocessMarkdownExplicitTags(markdown: string) {\n const placeholderToTag = new Map<string, string>();\n let nextPlaceholderIndex = 0;\n const preprocessedMarkdown = markdown.replace(/\\[([@#][^\\s[\\]]+)\\]/g, (_match, tagToken: string) => {\n const placeholder = createTagPlaceholder(nextPlaceholderIndex++);\n placeholderToTag.set(placeholder, tagToken);\n return placeholder;\n });\n\n return {\n markdown: preprocessedMarkdown,\n placeholderToTag\n };\n}\n\nfunction findTagPlaceholderAtCursor(\n text: string,\n cursor: number,\n placeholderToTag: Map<string, string>\n) {\n for (const [placeholder, tagToken] of placeholderToTag.entries()) {\n if (text.startsWith(placeholder, cursor)) {\n return {\n placeholder,\n tagToken\n };\n }\n }\n\n return null;\n}\n\nfunction restoreRemainingTagPlaceholders(\n blocks: BlockNote[],\n placeholderToTag: Map<string, string>\n): BlockNote[] {\n return blocks.map((block) => ({\n ...block,\n content: Array.isArray(block.content)\n ? block.content.map((inline) => {\n if (inline.type !== 'text' || typeof inline.text !== 'string') {\n return inline;\n }\n\n let text = inline.text;\n\n for (const [placeholder, tagToken] of placeholderToTag.entries()) {\n text = text.split(placeholder).join(`[${tagToken}]`);\n }\n\n return {\n ...inline,\n text\n };\n })\n : block.content,\n children: block.children?.length\n ? restoreRemainingTagPlaceholders(block.children, placeholderToTag)\n : []\n }));\n}\n\nfunction createTextInline(text: string, styles: Record<string, unknown> = {}): BlockNoteInline {\n return {\n type: 'text',\n text,\n styles\n };\n}\n\nfunction appendInline(target: BlockNoteInline[], inline: BlockNoteInline) {\n const previous = target.at(-1);\n\n if (\n previous?.type === 'text' &&\n inline.type === 'text' &&\n previous.text !== undefined &&\n inline.text !== undefined &&\n JSON.stringify(previous.styles ?? {}) === JSON.stringify(inline.styles ?? {})\n ) {\n previous.text += inline.text;\n return;\n }\n\n target.push(inline);\n}\n\nasync function restoreCustomInlineContent(\n blocks: BlockNote[],\n deps: MarkdownImportDeps,\n placeholderToTag: Map<string, string>\n): Promise<BlockNote[]> {\n const tagCache = new Map<string, Promise<{ id: string; tag: string }>>();\n const noteCache = new Map<string, Promise<Array<{ id: string; title: string }>>>();\n\n const getTag = (token: string) => {\n const normalizedToken = token.startsWith('#')\n ? `@${token.slice(1)}`\n : token;\n let existing = tagCache.get(normalizedToken);\n\n if (!existing) {\n existing = deps.ensureTag(normalizedToken.slice(1)).then((result) => ({\n id: 'tag' in result ? result.tag.id : result.id,\n tag: 'tag' in result ? result.tag.name : result.name\n }));\n tagCache.set(normalizedToken, existing);\n }\n\n return existing;\n };\n\n const getNotesByTitle = (title: string) => {\n let existing = noteCache.get(title);\n\n if (!existing) {\n existing = deps.findNotesByTitle(title);\n noteCache.set(title, existing);\n }\n\n return existing;\n };\n\n const restoreTextInline = async (inline: BlockNoteInline): Promise<BlockNoteInline[]> => {\n if (\n inline.type !== 'text' ||\n typeof inline.text !== 'string' ||\n !inline.text ||\n inline.styles?.code === true\n ) {\n return [inline];\n }\n\n const restored: BlockNoteInline[] = [];\n const styles = inline.styles ?? {};\n let cursor = 0;\n\n while (cursor < inline.text.length) {\n if (inline.text.startsWith('[[', cursor)) {\n const closeIndex = inline.text.indexOf(']]', cursor + 2);\n\n if (closeIndex !== -1) {\n const title = inline.text.slice(cursor + 2, closeIndex);\n const matchingNotes = await getNotesByTitle(title);\n\n if (matchingNotes.length === 1) {\n appendInline(restored, {\n type: 'reference',\n props: {\n id: matchingNotes[0].id,\n title: matchingNotes[0].title\n }\n });\n cursor = closeIndex + 2;\n continue;\n }\n }\n }\n\n const tagPlaceholderMatch = findTagPlaceholderAtCursor(inline.text, cursor, placeholderToTag);\n\n if (tagPlaceholderMatch) {\n const tag = await getTag(tagPlaceholderMatch.tagToken);\n appendInline(restored, {\n type: 'tag',\n props: tag\n });\n cursor += tagPlaceholderMatch.placeholder.length;\n continue;\n }\n\n let nextCursor = inline.text.length;\n\n const nextReferenceCursor = inline.text.indexOf('[[', cursor + 1);\n\n if (nextReferenceCursor !== -1) {\n nextCursor = Math.min(nextCursor, nextReferenceCursor);\n }\n\n for (const placeholder of placeholderToTag.keys()) {\n const nextPlaceholderCursor = inline.text.indexOf(placeholder, cursor + 1);\n\n if (nextPlaceholderCursor !== -1) {\n nextCursor = Math.min(nextCursor, nextPlaceholderCursor);\n }\n }\n\n appendInline(restored, createTextInline(inline.text.slice(cursor, nextCursor), styles));\n cursor = nextCursor;\n }\n\n return restored;\n };\n\n return Promise.all(\n blocks.map(async (block) => ({\n ...block,\n content: block.content\n ? (await Promise.all(block.content.map((inline) => restoreTextInline(inline)))).flat()\n : undefined,\n children: block.children?.length\n ? await restoreCustomInlineContent(block.children, deps, placeholderToTag)\n : []\n }))\n );\n}\n\nfunction collectTagIds(blocks: BlockNote[]): string[] {\n const tagIds = new Set<string>();\n\n const visit = (items: BlockNote[]) => {\n for (const block of items) {\n for (const inline of block.content ?? []) {\n if (inline.type === 'tag' && typeof inline.props?.id === 'string') {\n tagIds.add(inline.props.id);\n }\n }\n\n if (block.children?.length) {\n visit(block.children);\n }\n }\n };\n\n visit(blocks);\n return [...tagIds];\n}\n\nlet editorInstance: ServerBlockNoteEditor | null = null;\n\nfunction getEditor(): ServerBlockNoteEditor {\n if (!editorInstance) {\n editorInstance = ServerBlockNoteEditor.create();\n }\n return editorInstance;\n}\n\nexport async function blocksToMarkdown(contentJson: string): Promise<string> {\n try {\n const blocks = JSON.parse(contentJson);\n const supportedBlocks = stripUnsupportedMarkdownBlocks(blocks);\n const placeholderToTag = new Map<string, string>();\n const processed = preprocessCustomInlineContent(\n supportedBlocks,\n placeholderToTag,\n { value: 0 }\n );\n const editor = getEditor();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const markdown = await editor.blocksToMarkdownLossy(processed as any);\n return restoreTagPlaceholdersInMarkdown(markdown, placeholderToTag);\n } catch {\n return '';\n }\n}\n\nexport async function markdownToBlocksJson(\n markdown: string,\n deps: MarkdownImportDeps = defaultMarkdownImportDeps\n): Promise<string> {\n const editor = getEditor();\n const preprocessedMarkdown = preprocessMarkdownExplicitTags(markdown);\n const blocks = await editor.tryParseMarkdownToBlocks(preprocessedMarkdown.markdown);\n const restoredBlocks = await restoreCustomInlineContent(\n blocks as BlockNote[],\n deps,\n preprocessedMarkdown.placeholderToTag\n );\n return JSON.stringify(\n restoreRemainingTagPlaceholders(restoredBlocks, preprocessedMarkdown.placeholderToTag)\n );\n}\n\nexport function extractTagIdsFromContentJson(contentJson: string): string[] {\n const blocks = JSON.parse(contentJson) as BlockNote[];\n return collectTagIds(blocks);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/modules/blocknote.ts"],"sourcesContent":["import { ServerBlockNoteEditor } from '@blocknote/server-util';\nimport { ensureTagByName } from '~/features/tag/services/organization.js';\nimport models from '~/models.js';\n\ninterface BlockNote {\n id?: string;\n type: string;\n props?: Record<string, unknown>;\n content?: BlockNoteContent;\n children?: BlockNote[];\n text?: string;\n styles?: Record<string, unknown>;\n}\n\ninterface BlockNoteInline {\n type: string;\n props?: Record<string, unknown>;\n text?: string;\n styles?: Record<string, unknown>;\n}\n\ninterface BlockNoteTableCell {\n type: 'tableCell';\n props?: Record<string, unknown>;\n content?: BlockNoteInline[];\n}\n\ninterface BlockNoteTableRow {\n cells?: BlockNoteTableCell[];\n}\n\ninterface BlockNoteTableContent {\n type: 'tableContent';\n columnWidths?: Array<number | null>;\n headerRows?: number;\n rows?: BlockNoteTableRow[];\n}\n\ntype BlockNoteContent = BlockNoteInline[] | BlockNoteTableContent;\n\ninterface MarkdownImportDeps {\n ensureTag: (name: string) => Promise<\n | {\n id: string;\n name: string;\n }\n | {\n tag: {\n id: string;\n name: string;\n };\n }\n >;\n findNotesByTitle: (title: string) => Promise<\n Array<{\n id: string;\n title: string;\n }>\n >;\n}\n\nconst UNSUPPORTED_MARKDOWN_BLOCK_TYPES = new Set(['tableOfContents']);\nconst TAG_PLACEHOLDER_PREFIX = 'OCEAN_BRAIN_TAG_';\nconst TAG_PLACEHOLDER_SUFFIX = '_TOKEN';\n\nconst defaultMarkdownImportDeps: MarkdownImportDeps = {\n ensureTag: async (name) => ensureTagByName(name),\n findNotesByTitle: async (title) => {\n const notes = await models.note.findMany({\n select: {\n id: true,\n title: true,\n },\n where: { title },\n orderBy: { createdAt: 'asc' },\n take: 2,\n });\n\n return notes.map((note) => ({\n id: String(note.id),\n title: note.title,\n }));\n },\n};\n\nfunction stripUnsupportedMarkdownBlocks(blocks: BlockNote[]): BlockNote[] {\n return blocks\n .filter((block) => !UNSUPPORTED_MARKDOWN_BLOCK_TYPES.has(block.type))\n .map((block) => ({\n ...block,\n children: block.children?.length ? stripUnsupportedMarkdownBlocks(block.children) : [],\n }));\n}\n\nfunction createTagPlaceholder(index: number) {\n return `${TAG_PLACEHOLDER_PREFIX}${index}${TAG_PLACEHOLDER_SUFFIX}`;\n}\n\nfunction isTableContent(content: BlockNoteContent | undefined): content is BlockNoteTableContent {\n return !Array.isArray(content) && content?.type === 'tableContent';\n}\n\nfunction mapBlockContent(\n content: BlockNoteContent | undefined,\n mapInline: (inline: BlockNoteInline) => BlockNoteInline,\n): BlockNoteContent | undefined {\n if (Array.isArray(content)) {\n return content.map(mapInline);\n }\n\n if (!isTableContent(content)) {\n return content;\n }\n\n return {\n ...content,\n rows: content.rows?.map((row) => ({\n ...row,\n cells: row.cells?.map((cell) => ({\n ...cell,\n content: cell.content?.map(mapInline),\n })),\n })),\n };\n}\n\nasync function mapBlockContentAsync(\n content: BlockNoteContent | undefined,\n mapInline: (inline: BlockNoteInline) => Promise<BlockNoteInline[]>,\n): Promise<BlockNoteContent | undefined> {\n if (Array.isArray(content)) {\n return (await Promise.all(content.map(mapInline))).flat();\n }\n\n if (!isTableContent(content)) {\n return content;\n }\n\n return {\n ...content,\n rows: await Promise.all(\n (content.rows ?? []).map(async (row) => ({\n ...row,\n cells: await Promise.all(\n (row.cells ?? []).map(async (cell) => ({\n ...cell,\n content: cell.content ? (await Promise.all(cell.content.map(mapInline))).flat() : cell.content,\n })),\n ),\n })),\n ),\n };\n}\n\nfunction visitBlockContent(content: BlockNoteContent | undefined, visitInline: (inline: BlockNoteInline) => void) {\n if (Array.isArray(content)) {\n for (const inline of content) {\n visitInline(inline);\n }\n return;\n }\n\n if (!isTableContent(content)) {\n return;\n }\n\n for (const row of content.rows ?? []) {\n for (const cell of row.cells ?? []) {\n for (const inline of cell.content ?? []) {\n visitInline(inline);\n }\n }\n }\n}\n\nfunction mapBlocks(\n blocks: BlockNote[],\n mapContent: (content: BlockNoteContent | undefined) => BlockNoteContent | undefined,\n): BlockNote[] {\n return blocks.map((block) => ({\n ...block,\n content: mapContent(block.content),\n children: block.children?.length ? mapBlocks(block.children, mapContent) : [],\n }));\n}\n\nasync function mapBlocksAsync(\n blocks: BlockNote[],\n mapContent: (content: BlockNoteContent | undefined) => Promise<BlockNoteContent | undefined>,\n): Promise<BlockNote[]> {\n return Promise.all(\n blocks.map(async (block) => ({\n ...block,\n content: await mapContent(block.content),\n children: block.children?.length ? await mapBlocksAsync(block.children, mapContent) : [],\n })),\n );\n}\n\nfunction preprocessCustomInlineContent(\n blocks: BlockNote[],\n placeholderToTag: Map<string, string>,\n nextPlaceholderIndex: { value: number },\n): BlockNote[] {\n return mapBlocks(blocks, (content) =>\n mapBlockContent(content, (inline) => {\n if (inline.type === 'reference') {\n return {\n type: 'text',\n text: `[[${inline.props?.title || inline.props?.id || ''}]]`,\n styles: {},\n };\n }\n if (inline.type === 'tag') {\n const tag = (inline.props?.tag as string) || '';\n const placeholder = createTagPlaceholder(nextPlaceholderIndex.value++);\n placeholderToTag.set(placeholder, tag);\n return {\n type: 'text',\n text: placeholder,\n styles: {},\n };\n }\n return inline;\n }),\n );\n}\n\nfunction restoreTagPlaceholdersInMarkdown(markdown: string, placeholderToTag: Map<string, string>) {\n let restoredMarkdown = markdown;\n\n for (const [placeholder, tag] of placeholderToTag.entries()) {\n restoredMarkdown = restoredMarkdown.split(placeholder).join(`[${tag}]`);\n }\n\n return restoredMarkdown;\n}\n\nfunction preprocessMarkdownExplicitTags(markdown: string) {\n const placeholderToTag = new Map<string, string>();\n let nextPlaceholderIndex = 0;\n const preprocessedMarkdown = markdown.replace(/\\[([@#][^\\s[\\]]+)\\]/g, (_match, tagToken: string) => {\n const placeholder = createTagPlaceholder(nextPlaceholderIndex++);\n placeholderToTag.set(placeholder, tagToken);\n return placeholder;\n });\n\n return {\n markdown: preprocessedMarkdown,\n placeholderToTag,\n };\n}\n\nfunction findTagPlaceholderAtCursor(text: string, cursor: number, placeholderToTag: Map<string, string>) {\n for (const [placeholder, tagToken] of placeholderToTag.entries()) {\n if (text.startsWith(placeholder, cursor)) {\n return {\n placeholder,\n tagToken,\n };\n }\n }\n\n return null;\n}\n\nfunction restoreRemainingTagPlaceholders(blocks: BlockNote[], placeholderToTag: Map<string, string>): BlockNote[] {\n return mapBlocks(blocks, (content) =>\n mapBlockContent(content, (inline) => {\n if (inline.type !== 'text' || typeof inline.text !== 'string') {\n return inline;\n }\n\n let text = inline.text;\n\n for (const [placeholder, tagToken] of placeholderToTag.entries()) {\n text = text.split(placeholder).join(`[${tagToken}]`);\n }\n\n return {\n ...inline,\n text,\n };\n }),\n );\n}\n\nfunction createTextInline(text: string, styles: Record<string, unknown> = {}): BlockNoteInline {\n return {\n type: 'text',\n text,\n styles,\n };\n}\n\nfunction appendInline(target: BlockNoteInline[], inline: BlockNoteInline) {\n const previous = target.at(-1);\n\n if (\n previous?.type === 'text' &&\n inline.type === 'text' &&\n previous.text !== undefined &&\n inline.text !== undefined &&\n JSON.stringify(previous.styles ?? {}) === JSON.stringify(inline.styles ?? {})\n ) {\n previous.text += inline.text;\n return;\n }\n\n target.push(inline);\n}\n\nasync function restoreCustomInlineContent(\n blocks: BlockNote[],\n deps: MarkdownImportDeps,\n placeholderToTag: Map<string, string>,\n): Promise<BlockNote[]> {\n const tagCache = new Map<string, Promise<{ id: string; tag: string }>>();\n const noteCache = new Map<string, Promise<Array<{ id: string; title: string }>>>();\n\n const getTag = (token: string) => {\n const normalizedToken = token.startsWith('#') ? `@${token.slice(1)}` : token;\n let existing = tagCache.get(normalizedToken);\n\n if (!existing) {\n existing = deps.ensureTag(normalizedToken.slice(1)).then((result) => ({\n id: 'tag' in result ? result.tag.id : result.id,\n tag: 'tag' in result ? result.tag.name : result.name,\n }));\n tagCache.set(normalizedToken, existing);\n }\n\n return existing;\n };\n\n const getNotesByTitle = (title: string) => {\n let existing = noteCache.get(title);\n\n if (!existing) {\n existing = deps.findNotesByTitle(title);\n noteCache.set(title, existing);\n }\n\n return existing;\n };\n\n const restoreTextInline = async (inline: BlockNoteInline): Promise<BlockNoteInline[]> => {\n if (inline.type !== 'text' || typeof inline.text !== 'string' || !inline.text || inline.styles?.code === true) {\n return [inline];\n }\n\n const restored: BlockNoteInline[] = [];\n const styles = inline.styles ?? {};\n let cursor = 0;\n\n while (cursor < inline.text.length) {\n if (inline.text.startsWith('[[', cursor)) {\n const closeIndex = inline.text.indexOf(']]', cursor + 2);\n\n if (closeIndex !== -1) {\n const title = inline.text.slice(cursor + 2, closeIndex);\n const matchingNotes = await getNotesByTitle(title);\n\n if (matchingNotes.length === 1) {\n appendInline(restored, {\n type: 'reference',\n props: {\n id: matchingNotes[0].id,\n title: matchingNotes[0].title,\n },\n });\n cursor = closeIndex + 2;\n continue;\n }\n }\n }\n\n const tagPlaceholderMatch = findTagPlaceholderAtCursor(inline.text, cursor, placeholderToTag);\n\n if (tagPlaceholderMatch) {\n const tag = await getTag(tagPlaceholderMatch.tagToken);\n appendInline(restored, {\n type: 'tag',\n props: tag,\n });\n cursor += tagPlaceholderMatch.placeholder.length;\n continue;\n }\n\n let nextCursor = inline.text.length;\n\n const nextReferenceCursor = inline.text.indexOf('[[', cursor + 1);\n\n if (nextReferenceCursor !== -1) {\n nextCursor = Math.min(nextCursor, nextReferenceCursor);\n }\n\n for (const placeholder of placeholderToTag.keys()) {\n const nextPlaceholderCursor = inline.text.indexOf(placeholder, cursor + 1);\n\n if (nextPlaceholderCursor !== -1) {\n nextCursor = Math.min(nextCursor, nextPlaceholderCursor);\n }\n }\n\n appendInline(restored, createTextInline(inline.text.slice(cursor, nextCursor), styles));\n cursor = nextCursor;\n }\n\n return restored;\n };\n\n return mapBlocksAsync(blocks, (content) => mapBlockContentAsync(content, restoreTextInline));\n}\n\nfunction collectTagIds(blocks: BlockNote[]): string[] {\n const tagIds = new Set<string>();\n\n const visit = (items: BlockNote[]) => {\n for (const block of items) {\n visitBlockContent(block.content, (inline) => {\n if (inline.type === 'tag' && typeof inline.props?.id === 'string') {\n tagIds.add(inline.props.id);\n }\n });\n\n if (block.children?.length) {\n visit(block.children);\n }\n }\n };\n\n visit(blocks);\n return [...tagIds];\n}\n\nlet editorInstance: ServerBlockNoteEditor | null = null;\n\nfunction getEditor(): ServerBlockNoteEditor {\n if (!editorInstance) {\n editorInstance = ServerBlockNoteEditor.create();\n }\n return editorInstance;\n}\n\nexport async function blocksToMarkdown(contentJson: string): Promise<string> {\n try {\n const blocks = JSON.parse(contentJson);\n const supportedBlocks = stripUnsupportedMarkdownBlocks(blocks);\n const placeholderToTag = new Map<string, string>();\n const processed = preprocessCustomInlineContent(supportedBlocks, placeholderToTag, { value: 0 });\n const editor = getEditor();\n const markdown = await editor.blocksToMarkdownLossy(\n processed as Parameters<typeof editor.blocksToMarkdownLossy>[0],\n );\n return restoreTagPlaceholdersInMarkdown(markdown, placeholderToTag);\n } catch {\n return '';\n }\n}\n\nexport async function markdownToBlocksJson(\n markdown: string,\n deps: MarkdownImportDeps = defaultMarkdownImportDeps,\n): Promise<string> {\n const editor = getEditor();\n const preprocessedMarkdown = preprocessMarkdownExplicitTags(markdown);\n const blocks = await editor.tryParseMarkdownToBlocks(preprocessedMarkdown.markdown);\n const restoredBlocks = await restoreCustomInlineContent(\n blocks as BlockNote[],\n deps,\n preprocessedMarkdown.placeholderToTag,\n );\n return JSON.stringify(restoreRemainingTagPlaceholders(restoredBlocks, preprocessedMarkdown.placeholderToTag));\n}\n\nexport function extractTagIdsFromContentJson(contentJson: string): string[] {\n const blocks = JSON.parse(contentJson) as BlockNote[];\n return collectTagIds(blocks);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { backfillStaleNoteSearchText } from "../features/note/services/search-backfill.js";
|
|
2
|
+
let activeDataMaintenanceTimer = null;
|
|
3
|
+
const DEFAULT_DATA_MAINTENANCE_INTERVAL_MS = 15 * 60 * 1e3;
|
|
4
|
+
const createDataMaintenanceService = (jobs) => {
|
|
5
|
+
let activeDataMaintenancePromise = null;
|
|
6
|
+
const runNow = async (limit) => {
|
|
7
|
+
const results = [];
|
|
8
|
+
for (const job of jobs) {
|
|
9
|
+
const processedCount = await job.run(limit);
|
|
10
|
+
if (processedCount > 0) {
|
|
11
|
+
results.push({
|
|
12
|
+
key: job.key,
|
|
13
|
+
processedCount
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return results;
|
|
18
|
+
};
|
|
19
|
+
const runInBackground = (limit) => {
|
|
20
|
+
if (activeDataMaintenancePromise) {
|
|
21
|
+
return activeDataMaintenancePromise;
|
|
22
|
+
}
|
|
23
|
+
activeDataMaintenancePromise = runNow(limit).finally(() => {
|
|
24
|
+
activeDataMaintenancePromise = null;
|
|
25
|
+
});
|
|
26
|
+
return activeDataMaintenancePromise;
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
runNow,
|
|
30
|
+
runInBackground
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const defaultDataMaintenanceService = createDataMaintenanceService([
|
|
34
|
+
{
|
|
35
|
+
key: "note-search-projection",
|
|
36
|
+
run: backfillStaleNoteSearchText
|
|
37
|
+
}
|
|
38
|
+
]);
|
|
39
|
+
const runDataMaintenance = async (limit) => {
|
|
40
|
+
return defaultDataMaintenanceService.runNow(limit);
|
|
41
|
+
};
|
|
42
|
+
const runDataMaintenanceInBackground = (limit) => {
|
|
43
|
+
return defaultDataMaintenanceService.runInBackground(limit);
|
|
44
|
+
};
|
|
45
|
+
const startDataMaintenanceScheduler = (options = {}) => {
|
|
46
|
+
if (activeDataMaintenanceTimer) {
|
|
47
|
+
return () => void 0;
|
|
48
|
+
}
|
|
49
|
+
const intervalMs = options.intervalMs ?? DEFAULT_DATA_MAINTENANCE_INTERVAL_MS;
|
|
50
|
+
const tick = () => {
|
|
51
|
+
const runInBackground = options.runInBackground ?? runDataMaintenanceInBackground;
|
|
52
|
+
void runInBackground().then((results) => {
|
|
53
|
+
options.onResults?.(results);
|
|
54
|
+
}).catch((error) => {
|
|
55
|
+
options.onError?.(error);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
tick();
|
|
59
|
+
activeDataMaintenanceTimer = setInterval(tick, intervalMs);
|
|
60
|
+
activeDataMaintenanceTimer.unref?.();
|
|
61
|
+
return () => {
|
|
62
|
+
if (!activeDataMaintenanceTimer) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
clearInterval(activeDataMaintenanceTimer);
|
|
66
|
+
activeDataMaintenanceTimer = null;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export {
|
|
70
|
+
createDataMaintenanceService,
|
|
71
|
+
runDataMaintenance,
|
|
72
|
+
runDataMaintenanceInBackground,
|
|
73
|
+
startDataMaintenanceScheduler
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=data-maintenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/modules/data-maintenance.ts"],"sourcesContent":["import { backfillStaleNoteSearchText } from '~/features/note/services/search-backfill.js';\n\nexport interface DataMaintenanceJobResult {\n key: string;\n processedCount: number;\n}\n\ninterface DataMaintenanceSchedulerOptions {\n intervalMs?: number;\n onError?: (error: unknown) => void;\n onResults?: (results: DataMaintenanceJobResult[]) => void;\n runInBackground?: (limit?: number) => Promise<DataMaintenanceJobResult[]>;\n}\n\ninterface DataMaintenanceJob {\n key: string;\n run: (limit?: number) => Promise<number>;\n}\n\ninterface DataMaintenanceService {\n runNow: (limit?: number) => Promise<DataMaintenanceJobResult[]>;\n runInBackground: (limit?: number) => Promise<DataMaintenanceJobResult[]>;\n}\n\nlet activeDataMaintenanceTimer: ReturnType<typeof setInterval> | null = null;\nconst DEFAULT_DATA_MAINTENANCE_INTERVAL_MS = 15 * 60 * 1000;\n\nexport const createDataMaintenanceService = (jobs: DataMaintenanceJob[]): DataMaintenanceService => {\n let activeDataMaintenancePromise: Promise<DataMaintenanceJobResult[]> | null = null;\n\n const runNow = async (limit?: number) => {\n const results: DataMaintenanceJobResult[] = [];\n\n for (const job of jobs) {\n const processedCount = await job.run(limit);\n\n if (processedCount > 0) {\n results.push({\n key: job.key,\n processedCount,\n });\n }\n }\n\n return results;\n };\n\n const runInBackground = (limit?: number) => {\n if (activeDataMaintenancePromise) {\n return activeDataMaintenancePromise;\n }\n\n activeDataMaintenancePromise = runNow(limit).finally(() => {\n activeDataMaintenancePromise = null;\n });\n\n return activeDataMaintenancePromise;\n };\n\n return {\n runNow,\n runInBackground,\n };\n};\n\nconst defaultDataMaintenanceService = createDataMaintenanceService([\n {\n key: 'note-search-projection',\n run: backfillStaleNoteSearchText,\n },\n]);\n\nexport const runDataMaintenance = async (limit?: number) => {\n return defaultDataMaintenanceService.runNow(limit);\n};\n\nexport const runDataMaintenanceInBackground = (limit?: number) => {\n return defaultDataMaintenanceService.runInBackground(limit);\n};\n\nexport const startDataMaintenanceScheduler = (options: DataMaintenanceSchedulerOptions = {}) => {\n if (activeDataMaintenanceTimer) {\n return () => undefined;\n }\n\n const intervalMs = options.intervalMs ?? DEFAULT_DATA_MAINTENANCE_INTERVAL_MS;\n\n const tick = () => {\n const runInBackground = options.runInBackground ?? runDataMaintenanceInBackground;\n\n void runInBackground()\n .then((results) => {\n options.onResults?.(results);\n })\n .catch((error) => {\n options.onError?.(error);\n });\n };\n\n tick();\n\n activeDataMaintenanceTimer = setInterval(tick, intervalMs);\n activeDataMaintenanceTimer.unref?.();\n\n return () => {\n if (!activeDataMaintenanceTimer) {\n return;\n }\n\n clearInterval(activeDataMaintenanceTimer);\n activeDataMaintenanceTimer = null;\n };\n};\n"],"mappings":"AAAA,SAAS,mCAAmC;AAwB5C,IAAI,6BAAoE;AACxE,MAAM,uCAAuC,KAAK,KAAK;AAEhD,MAAM,+BAA+B,CAAC,SAAuD;AAChG,MAAI,+BAA2E;AAE/E,QAAM,SAAS,OAAO,UAAmB;AACrC,UAAM,UAAsC,CAAC;AAE7C,eAAW,OAAO,MAAM;AACpB,YAAM,iBAAiB,MAAM,IAAI,IAAI,KAAK;AAE1C,UAAI,iBAAiB,GAAG;AACpB,gBAAQ,KAAK;AAAA,UACT,KAAK,IAAI;AAAA,UACT;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAEA,QAAM,kBAAkB,CAAC,UAAmB;AACxC,QAAI,8BAA8B;AAC9B,aAAO;AAAA,IACX;AAEA,mCAA+B,OAAO,KAAK,EAAE,QAAQ,MAAM;AACvD,qCAA+B;AAAA,IACnC,CAAC;AAED,WAAO;AAAA,EACX;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,EACJ;AACJ;AAEA,MAAM,gCAAgC,6BAA6B;AAAA,EAC/D;AAAA,IACI,KAAK;AAAA,IACL,KAAK;AAAA,EACT;AACJ,CAAC;AAEM,MAAM,qBAAqB,OAAO,UAAmB;AACxD,SAAO,8BAA8B,OAAO,KAAK;AACrD;AAEO,MAAM,iCAAiC,CAAC,UAAmB;AAC9D,SAAO,8BAA8B,gBAAgB,KAAK;AAC9D;AAEO,MAAM,gCAAgC,CAAC,UAA2C,CAAC,MAAM;AAC5F,MAAI,4BAA4B;AAC5B,WAAO,MAAM;AAAA,EACjB;AAEA,QAAM,aAAa,QAAQ,cAAc;AAEzC,QAAM,OAAO,MAAM;AACf,UAAM,kBAAkB,QAAQ,mBAAmB;AAEnD,SAAK,gBAAgB,EAChB,KAAK,CAAC,YAAY;AACf,cAAQ,YAAY,OAAO;AAAA,IAC/B,CAAC,EACA,MAAM,CAAC,UAAU;AACd,cAAQ,UAAU,KAAK;AAAA,IAC3B,CAAC;AAAA,EACT;AAEA,OAAK;AAEL,+BAA6B,YAAY,MAAM,UAAU;AACzD,6BAA2B,QAAQ;AAEnC,SAAO,MAAM;AACT,QAAI,CAAC,4BAA4B;AAC7B;AAAA,IACJ;AAEA,kBAAc,0BAA0B;AACxC,iCAA6B;AAAA,EACjC;AACJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/error-handler.ts"],"sourcesContent":["import type { ErrorRequestHandler } from 'express';\n\nexport class AppError extends Error {\n code: string;\n status: number;\n\n constructor(status: number, code: string, message: string) {\n super(message);\n this.name = 'AppError';\n this.status = status;\n this.code = code;\n }\n}\n\nexport const createAppError = (status: number, code: string, message: string) => {\n return new AppError(status, code, message);\n};\n\nexport const createErrorHandler = (): ErrorRequestHandler => {\n return (error, _req, res, next) => {\n if (res.headersSent) {\n next(error);\n return;\n }\n\n if (error instanceof AppError) {\n res.status(error.status).json({\n
|
|
1
|
+
{"version":3,"sources":["../../src/modules/error-handler.ts"],"sourcesContent":["import type { ErrorRequestHandler } from 'express';\n\nexport class AppError extends Error {\n code: string;\n status: number;\n\n constructor(status: number, code: string, message: string) {\n super(message);\n this.name = 'AppError';\n this.status = status;\n this.code = code;\n }\n}\n\nexport const createAppError = (status: number, code: string, message: string) => {\n return new AppError(status, code, message);\n};\n\nexport const createErrorHandler = (): ErrorRequestHandler => {\n return (error, _req, res, next) => {\n if (res.headersSent) {\n next(error);\n return;\n }\n\n if (error instanceof AppError) {\n res.status(error.status)\n .json({\n code: error.code,\n message: error.message,\n })\n .end();\n return;\n }\n\n const message = error instanceof Error ? error.stack || error.message : String(error);\n process.stderr.write(`[error] ${message}\\n`);\n\n res.status(500)\n .json({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Internal Server Error',\n })\n .end();\n };\n};\n"],"mappings":"AAEO,MAAM,iBAAiB,MAAM;AAAA,EAChC;AAAA,EACA;AAAA,EAEA,YAAY,QAAgB,MAAc,SAAiB;AACvD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EAChB;AACJ;AAEO,MAAM,iBAAiB,CAAC,QAAgB,MAAc,YAAoB;AAC7E,SAAO,IAAI,SAAS,QAAQ,MAAM,OAAO;AAC7C;AAEO,MAAM,qBAAqB,MAA2B;AACzD,SAAO,CAAC,OAAO,MAAM,KAAK,SAAS;AAC/B,QAAI,IAAI,aAAa;AACjB,WAAK,KAAK;AACV;AAAA,IACJ;AAEA,QAAI,iBAAiB,UAAU;AAC3B,UAAI,OAAO,MAAM,MAAM,EAClB,KAAK;AAAA,QACF,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,MACnB,CAAC,EACA,IAAI;AACT;AAAA,IACJ;AAEA,UAAM,UAAU,iBAAiB,QAAQ,MAAM,SAAS,MAAM,UAAU,OAAO,KAAK;AACpF,YAAQ,OAAO,MAAM,WAAW,OAAO;AAAA,CAAI;AAE3C,QAAI,OAAO,GAAG,EACT,KAAK;AAAA,MACF,MAAM;AAAA,MACN,SAAS;AAAA,IACb,CAAC,EACA,IAAI;AAAA,EACb;AACJ;","names":[]}
|
|
@@ -1,36 +1,18 @@
|
|
|
1
1
|
import expressWinston from "express-winston";
|
|
2
2
|
import winston from "winston";
|
|
3
3
|
const loggerOptions = {
|
|
4
|
-
transports: [
|
|
5
|
-
new winston.transports.Console()
|
|
6
|
-
],
|
|
4
|
+
transports: [new winston.transports.Console()],
|
|
7
5
|
meta: true,
|
|
8
|
-
requestWhitelist: [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"originalUrl",
|
|
12
|
-
"url",
|
|
13
|
-
"query",
|
|
14
|
-
"params",
|
|
15
|
-
"ip",
|
|
16
|
-
"headers"
|
|
17
|
-
],
|
|
18
|
-
responseWhitelist: [
|
|
19
|
-
"statusCode"
|
|
20
|
-
],
|
|
21
|
-
headerBlacklist: [
|
|
22
|
-
"authorization",
|
|
23
|
-
"cookie",
|
|
24
|
-
"set-cookie",
|
|
25
|
-
"x-api-key"
|
|
26
|
-
],
|
|
6
|
+
requestWhitelist: ["httpVersion", "method", "originalUrl", "url", "query", "params", "ip", "headers"],
|
|
7
|
+
responseWhitelist: ["statusCode"],
|
|
8
|
+
headerBlacklist: ["authorization", "cookie", "set-cookie", "x-api-key"],
|
|
27
9
|
requestFilter: (req, propName) => {
|
|
28
10
|
if (propName === "headers") {
|
|
29
11
|
const h = req.headers || {};
|
|
30
12
|
return {
|
|
31
13
|
"user-agent": h["user-agent"],
|
|
32
14
|
"content-type": h["content-type"],
|
|
33
|
-
|
|
15
|
+
accept: h["accept"],
|
|
34
16
|
"x-forwarded-for": h["x-forwarded-for"],
|
|
35
17
|
"x-real-ip": h["x-real-ip"]
|
|
36
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/logger.ts"],"sourcesContent":["import type { LoggerOptions } from 'express-winston';\nimport expressWinston from 'express-winston';\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/modules/logger.ts"],"sourcesContent":["import type { LoggerOptions } from 'express-winston';\nimport expressWinston from 'express-winston';\nimport type { TransformableInfo } from 'logform';\nimport winston from 'winston';\n\nexport const loggerOptions: LoggerOptions = {\n transports: [new winston.transports.Console()],\n meta: true,\n requestWhitelist: ['httpVersion', 'method', 'originalUrl', 'url', 'query', 'params', 'ip', 'headers'],\n responseWhitelist: ['statusCode'],\n headerBlacklist: ['authorization', 'cookie', 'set-cookie', 'x-api-key'],\n requestFilter: (\n req: { headers?: Record<string, string | string[] | undefined> } & Record<string, unknown>,\n propName: string,\n ): unknown => {\n if (propName === 'headers') {\n const h = req.headers || {};\n return {\n 'user-agent': h['user-agent'],\n 'content-type': h['content-type'],\n accept: h['accept'],\n 'x-forwarded-for': h['x-forwarded-for'],\n 'x-real-ip': h['x-real-ip'],\n };\n }\n return (req as Record<string, unknown>)[propName];\n },\n responseFilter: (res: Record<string, unknown>, propName: string): unknown => {\n return res[propName];\n },\n format: winston.format.combine(\n winston.format.colorize({ all: true }),\n winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),\n winston.format.printf((info: TransformableInfo) => {\n const meta = (info as unknown as { meta?: Record<string, unknown> }).meta || {};\n const req = (meta.req as Record<string, unknown>) || {};\n const res = (meta.res as Record<string, unknown>) || {};\n const rt = (meta as Record<string, unknown>).responseTime as number | undefined;\n const method = req.method as string | undefined;\n const url = (req.originalUrl as string | undefined) || (req.url as string | undefined);\n const status = res.statusCode as number | undefined;\n const headers = (req.headers as Record<string, string> | undefined) || {};\n const ua = headers['user-agent'];\n const fwd = headers['x-forwarded-for'];\n const fwdFirst = typeof fwd === 'string' ? fwd.split(',')[0].trim() : undefined;\n const realIp = fwdFirst || headers['x-real-ip'];\n const socketIp =\n (req as unknown as { ip?: string; socket?: { remoteAddress?: string } }).ip ||\n (req as unknown as { socket?: { remoteAddress?: string } }).socket?.remoteAddress;\n const clientIp = fwdFirst || realIp || socketIp || '';\n return `${info.timestamp as string} ${info.level as string}: [${status ?? ''}] ${method || ''} ${url || ''} ${rt ?? ''}ms ip=\"${clientIp}\" ua=\"${ua || ''}\"`;\n }),\n ),\n colorize: true,\n expressFormat: false,\n};\n\nexport default expressWinston.logger(loggerOptions);\n"],"mappings":"AACA,OAAO,oBAAoB;AAE3B,OAAO,aAAa;AAEb,MAAM,gBAA+B;AAAA,EACxC,YAAY,CAAC,IAAI,QAAQ,WAAW,QAAQ,CAAC;AAAA,EAC7C,MAAM;AAAA,EACN,kBAAkB,CAAC,eAAe,UAAU,eAAe,OAAO,SAAS,UAAU,MAAM,SAAS;AAAA,EACpG,mBAAmB,CAAC,YAAY;AAAA,EAChC,iBAAiB,CAAC,iBAAiB,UAAU,cAAc,WAAW;AAAA,EACtE,eAAe,CACX,KACA,aACU;AACV,QAAI,aAAa,WAAW;AACxB,YAAM,IAAI,IAAI,WAAW,CAAC;AAC1B,aAAO;AAAA,QACH,cAAc,EAAE,YAAY;AAAA,QAC5B,gBAAgB,EAAE,cAAc;AAAA,QAChC,QAAQ,EAAE,QAAQ;AAAA,QAClB,mBAAmB,EAAE,iBAAiB;AAAA,QACtC,aAAa,EAAE,WAAW;AAAA,MAC9B;AAAA,IACJ;AACA,WAAQ,IAAgC,QAAQ;AAAA,EACpD;AAAA,EACA,gBAAgB,CAAC,KAA8B,aAA8B;AACzE,WAAO,IAAI,QAAQ;AAAA,EACvB;AAAA,EACA,QAAQ,QAAQ,OAAO;AAAA,IACnB,QAAQ,OAAO,SAAS,EAAE,KAAK,KAAK,CAAC;AAAA,IACrC,QAAQ,OAAO,UAAU,EAAE,QAAQ,sBAAsB,CAAC;AAAA,IAC1D,QAAQ,OAAO,OAAO,CAAC,SAA4B;AAC/C,YAAM,OAAQ,KAAuD,QAAQ,CAAC;AAC9E,YAAM,MAAO,KAAK,OAAmC,CAAC;AACtD,YAAM,MAAO,KAAK,OAAmC,CAAC;AACtD,YAAM,KAAM,KAAiC;AAC7C,YAAM,SAAS,IAAI;AACnB,YAAM,MAAO,IAAI,eAAuC,IAAI;AAC5D,YAAM,SAAS,IAAI;AACnB,YAAM,UAAW,IAAI,WAAkD,CAAC;AACxE,YAAM,KAAK,QAAQ,YAAY;AAC/B,YAAM,MAAM,QAAQ,iBAAiB;AACrC,YAAM,WAAW,OAAO,QAAQ,WAAW,IAAI,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK,IAAI;AACtE,YAAM,SAAS,YAAY,QAAQ,WAAW;AAC9C,YAAM,WACD,IAAwE,MACxE,IAA2D,QAAQ;AACxE,YAAM,WAAW,YAAY,UAAU,YAAY;AACnD,aAAO,GAAG,KAAK,SAAmB,IAAI,KAAK,KAAe,MAAM,UAAU,EAAE,KAAK,UAAU,EAAE,IAAI,OAAO,EAAE,IAAI,MAAM,EAAE,UAAU,QAAQ,SAAS,MAAM,EAAE;AAAA,IAC7J,CAAC;AAAA,EACL;AAAA,EACA,UAAU;AAAA,EACV,eAAe;AACnB;AAEA,IAAO,iBAAQ,eAAe,OAAO,aAAa;","names":[]}
|
|
@@ -53,13 +53,12 @@ const createReadOnlyMcpValidationRule = () => {
|
|
|
53
53
|
if (node.operation === "query") {
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
context.reportError(
|
|
57
|
-
"MCP endpoint is read-only",
|
|
58
|
-
{
|
|
56
|
+
context.reportError(
|
|
57
|
+
new GraphQLError("MCP endpoint is read-only", {
|
|
59
58
|
nodes: [node],
|
|
60
59
|
extensions: { code: "FORBIDDEN" }
|
|
61
|
-
}
|
|
62
|
-
)
|
|
60
|
+
})
|
|
61
|
+
);
|
|
63
62
|
}
|
|
64
63
|
};
|
|
65
64
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/mcp-auth.ts"],"sourcesContent":["import type { NextFunction, Request,
|
|
1
|
+
{"version":3,"sources":["../../src/modules/mcp-auth.ts"],"sourcesContent":["import type { NextFunction, Request, RequestHandler, Response } from 'express';\nimport type { ValidationRule } from 'graphql';\nimport { GraphQLError } from 'graphql';\n\nimport type { AuthConfig } from './auth-mode.js';\n\nexport interface McpTokenValidationResult {\n ok: boolean;\n reason?: 'not_configured' | 'forbidden';\n}\n\nexport interface McpAdminAuthPort {\n getStatus: () => Promise<{ enabled: boolean }>;\n validatePresentedToken: (token: string) => Promise<McpTokenValidationResult>;\n}\n\nconst JSON_HEADERS = { 'Content-Type': 'application/json' };\n\nconst readBearerToken = (authorizationHeader?: string) => {\n if (!authorizationHeader?.startsWith('Bearer ')) {\n return undefined;\n }\n\n return authorizationHeader.slice('Bearer '.length).trim() || undefined;\n};\n\nexport const createMcpAuthMiddleware = (_authConfig: AuthConfig, mcpAdminAuth: McpAdminAuthPort): RequestHandler => {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n const status = await mcpAdminAuth.getStatus();\n if (!status.enabled) {\n res.status(403)\n .set(JSON_HEADERS)\n .json({\n code: 'MCP_DISABLED',\n message: 'MCP access is disabled by admin.',\n })\n .end();\n return;\n }\n\n const bearerToken = readBearerToken(req.headers.authorization);\n\n if (!bearerToken) {\n res.status(401)\n .set(JSON_HEADERS)\n .json({\n code: 'UNAUTHORIZED',\n message: 'A valid MCP bearer token is required.',\n })\n .end();\n return;\n }\n\n const validation = await mcpAdminAuth.validatePresentedToken(bearerToken);\n\n if (!validation.ok && validation.reason === 'not_configured') {\n res.status(503)\n .set(JSON_HEADERS)\n .json({\n code: 'MCP_AUTH_NOT_CONFIGURED',\n message: 'MCP bearer auth is not configured.',\n })\n .end();\n return;\n }\n\n if (!validation.ok) {\n res.status(403)\n .set(JSON_HEADERS)\n .json({\n code: 'FORBIDDEN',\n message: 'Invalid MCP bearer token.',\n })\n .end();\n return;\n }\n\n next();\n } catch (error) {\n next(error);\n }\n };\n};\n\nexport const createReadOnlyMcpValidationRule = (): ValidationRule => {\n return (context) => {\n return {\n OperationDefinition(node) {\n if (node.operation === 'query') {\n return;\n }\n\n context.reportError(\n new GraphQLError('MCP endpoint is read-only', {\n nodes: [node],\n extensions: { code: 'FORBIDDEN' },\n }),\n );\n },\n };\n };\n};\n"],"mappings":"AAEA,SAAS,oBAAoB;AAc7B,MAAM,eAAe,EAAE,gBAAgB,mBAAmB;AAE1D,MAAM,kBAAkB,CAAC,wBAAiC;AACtD,MAAI,CAAC,qBAAqB,WAAW,SAAS,GAAG;AAC7C,WAAO;AAAA,EACX;AAEA,SAAO,oBAAoB,MAAM,UAAU,MAAM,EAAE,KAAK,KAAK;AACjE;AAEO,MAAM,0BAA0B,CAAC,aAAyB,iBAAmD;AAChH,SAAO,OAAO,KAAc,KAAe,SAAuB;AAC9D,QAAI;AACA,YAAM,SAAS,MAAM,aAAa,UAAU;AAC5C,UAAI,CAAC,OAAO,SAAS;AACjB,YAAI,OAAO,GAAG,EACT,IAAI,YAAY,EAChB,KAAK;AAAA,UACF,MAAM;AAAA,UACN,SAAS;AAAA,QACb,CAAC,EACA,IAAI;AACT;AAAA,MACJ;AAEA,YAAM,cAAc,gBAAgB,IAAI,QAAQ,aAAa;AAE7D,UAAI,CAAC,aAAa;AACd,YAAI,OAAO,GAAG,EACT,IAAI,YAAY,EAChB,KAAK;AAAA,UACF,MAAM;AAAA,UACN,SAAS;AAAA,QACb,CAAC,EACA,IAAI;AACT;AAAA,MACJ;AAEA,YAAM,aAAa,MAAM,aAAa,uBAAuB,WAAW;AAExE,UAAI,CAAC,WAAW,MAAM,WAAW,WAAW,kBAAkB;AAC1D,YAAI,OAAO,GAAG,EACT,IAAI,YAAY,EAChB,KAAK;AAAA,UACF,MAAM;AAAA,UACN,SAAS;AAAA,QACb,CAAC,EACA,IAAI;AACT;AAAA,MACJ;AAEA,UAAI,CAAC,WAAW,IAAI;AAChB,YAAI,OAAO,GAAG,EACT,IAAI,YAAY,EAChB,KAAK;AAAA,UACF,MAAM;AAAA,UACN,SAAS;AAAA,QACb,CAAC,EACA,IAAI;AACT;AAAA,MACJ;AAEA,WAAK;AAAA,IACT,SAAS,OAAO;AACZ,WAAK,KAAK;AAAA,IACd;AAAA,EACJ;AACJ;AAEO,MAAM,kCAAkC,MAAsB;AACjE,SAAO,CAAC,YAAY;AAChB,WAAO;AAAA,MACH,oBAAoB,MAAM;AACtB,YAAI,KAAK,cAAc,SAAS;AAC5B;AAAA,QACJ;AAEA,gBAAQ;AAAA,UACJ,IAAI,aAAa,6BAA6B;AAAA,YAC1C,OAAO,CAAC,IAAI;AAAA,YACZ,YAAY,EAAE,MAAM,YAAY;AAAA,UACpC,CAAC;AAAA,QACL;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/mcp-token.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst TOKEN_BYTE_LENGTH = 32;\nconst HASH_ALGORITHM = 'sha256';\n\nexport interface IssuedMcpToken {\n plaintext: string;\n hash: string;\n}\n\nconst toTokenHash = (token: string) => {\n return crypto
|
|
1
|
+
{"version":3,"sources":["../../src/modules/mcp-token.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst TOKEN_BYTE_LENGTH = 32;\nconst HASH_ALGORITHM = 'sha256';\n\nexport interface IssuedMcpToken {\n plaintext: string;\n hash: string;\n}\n\nconst toTokenHash = (token: string) => {\n return crypto.createHash(HASH_ALGORITHM).update(token, 'utf8').digest('hex');\n};\n\nexport const issueMcpToken = (): IssuedMcpToken => {\n const plaintext = crypto.randomBytes(TOKEN_BYTE_LENGTH).toString('base64url');\n const hash = toTokenHash(plaintext);\n\n return {\n plaintext,\n hash,\n };\n};\n\nexport const verifyMcpToken = (storedHash: string, presentedToken: string) => {\n const presentedHash = toTokenHash(presentedToken);\n const storedBuffer = Buffer.from(storedHash, 'utf8');\n const presentedBuffer = Buffer.from(presentedHash, 'utf8');\n\n if (storedBuffer.length !== presentedBuffer.length) {\n return false;\n }\n\n return crypto.timingSafeEqual(storedBuffer, presentedBuffer);\n};\n"],"mappings":"AAAA,OAAO,YAAY;AAEnB,MAAM,oBAAoB;AAC1B,MAAM,iBAAiB;AAOvB,MAAM,cAAc,CAAC,UAAkB;AACnC,SAAO,OAAO,WAAW,cAAc,EAAE,OAAO,OAAO,MAAM,EAAE,OAAO,KAAK;AAC/E;AAEO,MAAM,gBAAgB,MAAsB;AAC/C,QAAM,YAAY,OAAO,YAAY,iBAAiB,EAAE,SAAS,WAAW;AAC5E,QAAM,OAAO,YAAY,SAAS;AAElC,SAAO;AAAA,IACH;AAAA,IACA;AAAA,EACJ;AACJ;AAEO,MAAM,iBAAiB,CAAC,YAAoB,mBAA2B;AAC1E,QAAM,gBAAgB,YAAY,cAAc;AAChD,QAAM,eAAe,OAAO,KAAK,YAAY,MAAM;AACnD,QAAM,kBAAkB,OAAO,KAAK,eAAe,MAAM;AAEzD,MAAI,aAAa,WAAW,gBAAgB,QAAQ;AAChD,WAAO;AAAA,EACX;AAEA,SAAO,OAAO,gBAAgB,cAAc,eAAe;AAC/D;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "child_process";
|
|
2
|
-
import path from "path";
|
|
3
2
|
import { createRequire } from "module";
|
|
3
|
+
import path from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
const resolveServerRoot = () => {
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -17,12 +17,7 @@ const createPrismaRuntime = (deps) => ({
|
|
|
17
17
|
runMigrateDeploy: () => {
|
|
18
18
|
deps.execFileSync(
|
|
19
19
|
process.execPath,
|
|
20
|
-
[
|
|
21
|
-
deps.resolvePrismaCliEntry(),
|
|
22
|
-
"migrate",
|
|
23
|
-
"deploy",
|
|
24
|
-
`--schema=${resolvePrismaSchemaPath()}`
|
|
25
|
-
],
|
|
20
|
+
[deps.resolvePrismaCliEntry(), "migrate", "deploy", `--schema=${resolvePrismaSchemaPath()}`],
|
|
26
21
|
{
|
|
27
22
|
stdio: "inherit",
|
|
28
23
|
env: { ...process.env }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/prisma-runtime.ts"],"sourcesContent":["import { execFileSync } from 'child_process';\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/modules/prisma-runtime.ts"],"sourcesContent":["import { execFileSync } from 'child_process';\nimport { createRequire } from 'module';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\ninterface PrismaRuntimeDeps {\n execFileSync: (\n file: string,\n args: string[],\n options: {\n env: NodeJS.ProcessEnv;\n stdio: 'inherit';\n },\n ) => void;\n resolvePrismaCliEntry: () => string;\n}\n\nconst resolveServerRoot = () => {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return process.env.OCEAN_BRAIN_PACKAGE_ROOT || path.resolve(__dirname, '../..');\n};\n\nexport const resolvePrismaSchemaPath = () => {\n return path.resolve(resolveServerRoot(), 'prisma/schema.prisma');\n};\n\nexport const resolvePrismaCliEntry = () => {\n const require = createRequire(import.meta.url);\n return require.resolve('prisma/build/index.js');\n};\n\nexport const createPrismaRuntime = (deps: PrismaRuntimeDeps) => ({\n runMigrateDeploy: () => {\n deps.execFileSync(\n process.execPath,\n [deps.resolvePrismaCliEntry(), 'migrate', 'deploy', `--schema=${resolvePrismaSchemaPath()}`],\n {\n stdio: 'inherit',\n env: { ...process.env },\n },\n );\n },\n});\n\nconst defaultPrismaRuntime = createPrismaRuntime({\n execFileSync,\n resolvePrismaCliEntry,\n});\n\nexport const runPrismaMigrateDeploy = () => {\n defaultPrismaRuntime.runMigrateDeploy();\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAc9B,MAAM,oBAAoB,MAAM;AAC5B,QAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,SAAO,QAAQ,IAAI,4BAA4B,KAAK,QAAQ,WAAW,OAAO;AAClF;AAEO,MAAM,0BAA0B,MAAM;AACzC,SAAO,KAAK,QAAQ,kBAAkB,GAAG,sBAAsB;AACnE;AAEO,MAAM,wBAAwB,MAAM;AACvC,QAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,SAAOA,SAAQ,QAAQ,uBAAuB;AAClD;AAEO,MAAM,sBAAsB,CAAC,UAA6B;AAAA,EAC7D,kBAAkB,MAAM;AACpB,SAAK;AAAA,MACD,QAAQ;AAAA,MACR,CAAC,KAAK,sBAAsB,GAAG,WAAW,UAAU,YAAY,wBAAwB,CAAC,EAAE;AAAA,MAC3F;AAAA,QACI,OAAO;AAAA,QACP,KAAK,EAAE,GAAG,QAAQ,IAAI;AAAA,MAC1B;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,MAAM,uBAAuB,oBAAoB;AAAA,EAC7C;AAAA,EACA;AACJ,CAAC;AAEM,MAAM,yBAAyB,MAAM;AACxC,uBAAqB,iBAAiB;AAC1C;","names":["require"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const NOTE_SNAPSHOT_RETENTION_DAYS = 14;
|
|
2
|
-
const NOTE_SNAPSHOT_MAX_PER_NOTE =
|
|
2
|
+
const NOTE_SNAPSHOT_MAX_PER_NOTE = 5;
|
|
3
3
|
const NOTE_TRASH_RETENTION_DAYS = 30;
|
|
4
4
|
const RECOVERY_RETENTION_BATCH_LIMIT = 100;
|
|
5
5
|
const SNAPSHOT_RETENTION_DAYS = NOTE_SNAPSHOT_RETENTION_DAYS;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/recovery-retention.ts"],"sourcesContent":["export const NOTE_SNAPSHOT_RETENTION_DAYS = 14;\nexport const NOTE_SNAPSHOT_MAX_PER_NOTE =
|
|
1
|
+
{"version":3,"sources":["../../src/modules/recovery-retention.ts"],"sourcesContent":["export const NOTE_SNAPSHOT_RETENTION_DAYS = 14;\nexport const NOTE_SNAPSHOT_MAX_PER_NOTE = 5;\nexport const NOTE_TRASH_RETENTION_DAYS = 30;\nexport const RECOVERY_RETENTION_BATCH_LIMIT = 100;\n\nexport const SNAPSHOT_RETENTION_DAYS = NOTE_SNAPSHOT_RETENTION_DAYS;\nexport const SNAPSHOT_MAX_PER_NOTE = NOTE_SNAPSHOT_MAX_PER_NOTE;\nexport const TRASH_RETENTION_DAYS = NOTE_TRASH_RETENTION_DAYS;\nexport const RECOVERY_CLEANUP_BATCH_LIMIT = RECOVERY_RETENTION_BATCH_LIMIT;\n\nconst DAY_IN_MS = 24 * 60 * 60 * 1000;\n\nexport const resolveRetentionCutoff = (retentionDays: number, now = new Date()) => {\n return new Date(now.getTime() - retentionDays * DAY_IN_MS);\n};\n\nexport const createRetentionCutoff = resolveRetentionCutoff;\n"],"mappings":"AAAO,MAAM,+BAA+B;AACrC,MAAM,6BAA6B;AACnC,MAAM,4BAA4B;AAClC,MAAM,iCAAiC;AAEvC,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;AAC9B,MAAM,uBAAuB;AAC7B,MAAM,+BAA+B;AAE5C,MAAM,YAAY,KAAK,KAAK,KAAK;AAE1B,MAAM,yBAAyB,CAAC,eAAuB,MAAM,oBAAI,KAAK,MAAM;AAC/E,SAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,gBAAgB,SAAS;AAC7D;AAEO,MAAM,wBAAwB;","names":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { serializeServerEvent, subscribeServerEvents } from "./server-events.js";
|
|
2
|
+
const KEEP_ALIVE_INTERVAL_MS = 3e4;
|
|
3
|
+
const createServerEventsHandler = () => {
|
|
4
|
+
return async (req, res) => {
|
|
5
|
+
res.status(200);
|
|
6
|
+
res.setHeader("Content-Type", "text/event-stream; charset=utf-8");
|
|
7
|
+
res.setHeader("Cache-Control", "no-cache, no-transform");
|
|
8
|
+
res.setHeader("Connection", "keep-alive");
|
|
9
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
10
|
+
res.flushHeaders?.();
|
|
11
|
+
res.write(": connected\n\n");
|
|
12
|
+
const unsubscribe = subscribeServerEvents((event) => {
|
|
13
|
+
res.write(serializeServerEvent(event));
|
|
14
|
+
});
|
|
15
|
+
const keepAliveTimer = setInterval(() => {
|
|
16
|
+
res.write(": keepalive\n\n");
|
|
17
|
+
}, KEEP_ALIVE_INTERVAL_MS);
|
|
18
|
+
let cleanedUp = false;
|
|
19
|
+
const cleanup = () => {
|
|
20
|
+
if (cleanedUp) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
cleanedUp = true;
|
|
24
|
+
clearInterval(keepAliveTimer);
|
|
25
|
+
unsubscribe();
|
|
26
|
+
};
|
|
27
|
+
req.on("close", cleanup);
|
|
28
|
+
res.on("close", cleanup);
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export {
|
|
32
|
+
createServerEventsHandler
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=server-events-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/modules/server-events-handler.ts"],"sourcesContent":["import type { Controller } from '~/types/index.js';\nimport { serializeServerEvent, subscribeServerEvents } from './server-events.js';\n\nconst KEEP_ALIVE_INTERVAL_MS = 30_000;\n\nexport const createServerEventsHandler = (): Controller => {\n return async (req, res) => {\n res.status(200);\n res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n res.setHeader('Cache-Control', 'no-cache, no-transform');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders?.();\n res.write(': connected\\n\\n');\n\n const unsubscribe = subscribeServerEvents((event) => {\n res.write(serializeServerEvent(event));\n });\n\n const keepAliveTimer = setInterval(() => {\n res.write(': keepalive\\n\\n');\n }, KEEP_ALIVE_INTERVAL_MS);\n\n let cleanedUp = false;\n\n const cleanup = () => {\n if (cleanedUp) {\n return;\n }\n\n cleanedUp = true;\n clearInterval(keepAliveTimer);\n unsubscribe();\n };\n\n req.on('close', cleanup);\n res.on('close', cleanup);\n };\n};\n"],"mappings":"AACA,SAAS,sBAAsB,6BAA6B;AAE5D,MAAM,yBAAyB;AAExB,MAAM,4BAA4B,MAAkB;AACvD,SAAO,OAAO,KAAK,QAAQ;AACvB,QAAI,OAAO,GAAG;AACd,QAAI,UAAU,gBAAgB,kCAAkC;AAChE,QAAI,UAAU,iBAAiB,wBAAwB;AACvD,QAAI,UAAU,cAAc,YAAY;AACxC,QAAI,UAAU,qBAAqB,IAAI;AACvC,QAAI,eAAe;AACnB,QAAI,MAAM,iBAAiB;AAE3B,UAAM,cAAc,sBAAsB,CAAC,UAAU;AACjD,UAAI,MAAM,qBAAqB,KAAK,CAAC;AAAA,IACzC,CAAC;AAED,UAAM,iBAAiB,YAAY,MAAM;AACrC,UAAI,MAAM,iBAAiB;AAAA,IAC/B,GAAG,sBAAsB;AAEzB,QAAI,YAAY;AAEhB,UAAM,UAAU,MAAM;AAClB,UAAI,WAAW;AACX;AAAA,MACJ;AAEA,kBAAY;AACZ,oBAAc,cAAc;AAC5B,kBAAY;AAAA,IAChB;AAEA,QAAI,GAAG,SAAS,OAAO;AACvB,QAAI,GAAG,SAAS,OAAO;AAAA,EAC3B;AACJ;","names":[]}
|