ocean-brain 0.1.6 → 0.2.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/index.js
CHANGED
|
@@ -9,6 +9,17 @@ import { fileURLToPath } from "url";
|
|
|
9
9
|
import { Command } from "commander";
|
|
10
10
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
var serverRoot = path.resolve(__dirname, "..", "server");
|
|
12
|
+
function findBin(name, fromDir) {
|
|
13
|
+
let dir = path.resolve(fromDir, "..");
|
|
14
|
+
while (true) {
|
|
15
|
+
const bin = path.resolve(dir, "node_modules", ".bin", name);
|
|
16
|
+
if (fs.existsSync(bin)) return bin;
|
|
17
|
+
const parent = path.dirname(dir);
|
|
18
|
+
if (parent === dir) break;
|
|
19
|
+
dir = parent;
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Could not find "${name}" binary. Make sure it is installed.`);
|
|
22
|
+
}
|
|
12
23
|
var pkg = JSON.parse(
|
|
13
24
|
fs.readFileSync(path.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
14
25
|
);
|
|
@@ -28,7 +39,7 @@ program.command("serve", { isDefault: true }).description("Start the Ocean Brain
|
|
|
28
39
|
process.env.PORT = process.env.PORT || opts.port;
|
|
29
40
|
process.env.HOST = process.env.HOST || opts.host;
|
|
30
41
|
const schemaPath = path.resolve(serverRoot, "prisma/schema.prisma");
|
|
31
|
-
const prisma =
|
|
42
|
+
const prisma = findBin("prisma", __dirname);
|
|
32
43
|
execSync(`"${prisma}" generate --schema="${schemaPath}"`, {
|
|
33
44
|
stdio: "inherit",
|
|
34
45
|
env: { ...process.env }
|
|
@@ -39,4 +50,8 @@ program.command("serve", { isDefault: true }).description("Start the Ocean Brain
|
|
|
39
50
|
});
|
|
40
51
|
await import(path.resolve(serverRoot, "dist/main.js"));
|
|
41
52
|
});
|
|
53
|
+
program.command("mcp").description("Start MCP server for AI integration").option("-s, --server <url>", "Ocean Brain server URL", "http://localhost:6683").action(async (opts) => {
|
|
54
|
+
const { startMcpServer } = await import("./mcp.js");
|
|
55
|
+
await startMcpServer(opts.server);
|
|
56
|
+
});
|
|
42
57
|
program.parse();
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/mcp.ts
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
var pkg = JSON.parse(
|
|
12
|
+
fs.readFileSync(path.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
13
|
+
);
|
|
14
|
+
async function graphql(serverUrl, query, variables) {
|
|
15
|
+
const response = await fetch(`${serverUrl}/graphql`, {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: { "Content-Type": "application/json" },
|
|
18
|
+
body: JSON.stringify({ query, variables })
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`GraphQL request failed: ${response.status} ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const result = await response.json();
|
|
24
|
+
if (result.errors?.length) {
|
|
25
|
+
throw new Error(`GraphQL error: ${result.errors[0].message}`);
|
|
26
|
+
}
|
|
27
|
+
return result.data;
|
|
28
|
+
}
|
|
29
|
+
async function startMcpServer(serverUrl) {
|
|
30
|
+
const server = new McpServer({
|
|
31
|
+
name: "ocean-brain",
|
|
32
|
+
version: pkg.version
|
|
33
|
+
});
|
|
34
|
+
server.tool(
|
|
35
|
+
"search_notes",
|
|
36
|
+
"Search notes by keyword. Returns a list of matching notes with title and tags. Use read_note to get full content of a specific note.",
|
|
37
|
+
{
|
|
38
|
+
query: z.string().describe("Search keyword"),
|
|
39
|
+
limit: z.number().optional().default(10).describe("Max results (default: 10)")
|
|
40
|
+
},
|
|
41
|
+
async ({ query, limit }) => {
|
|
42
|
+
const data = await graphql(serverUrl, `
|
|
43
|
+
query ($searchFilter: SearchFilterInput, $pagination: PaginationInput) {
|
|
44
|
+
allNotes(searchFilter: $searchFilter, pagination: $pagination) {
|
|
45
|
+
totalCount
|
|
46
|
+
notes {
|
|
47
|
+
id
|
|
48
|
+
title
|
|
49
|
+
updatedAt
|
|
50
|
+
tags { id name }
|
|
51
|
+
contentAsMarkdown
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`, {
|
|
56
|
+
searchFilter: { query, sortBy: "updatedAt", sortOrder: "desc" },
|
|
57
|
+
pagination: { limit, offset: 0 }
|
|
58
|
+
});
|
|
59
|
+
const result = data?.allNotes;
|
|
60
|
+
const notes = result.notes.map((note) => ({
|
|
61
|
+
id: note.id,
|
|
62
|
+
title: note.title,
|
|
63
|
+
updatedAt: note.updatedAt,
|
|
64
|
+
tags: note.tags.map((t) => t.name),
|
|
65
|
+
preview: note.contentAsMarkdown.slice(0, 100)
|
|
66
|
+
}));
|
|
67
|
+
return {
|
|
68
|
+
content: [{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: JSON.stringify({ totalCount: result.totalCount, notes }, null, 2)
|
|
71
|
+
}]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
server.tool(
|
|
76
|
+
"read_note",
|
|
77
|
+
"Read content of a note by ID. Returns truncated by default (1000 chars). Set maxLength to 0 for full content only when necessary.",
|
|
78
|
+
{
|
|
79
|
+
id: z.string().describe("Note ID"),
|
|
80
|
+
maxLength: z.number().optional().default(1e3).describe("Max content length in characters. 0 for full content. (default: 1000)")
|
|
81
|
+
},
|
|
82
|
+
async ({ id, maxLength }) => {
|
|
83
|
+
const data = await graphql(serverUrl, `
|
|
84
|
+
query ($id: ID!) {
|
|
85
|
+
note(id: $id) {
|
|
86
|
+
id
|
|
87
|
+
title
|
|
88
|
+
contentAsMarkdown
|
|
89
|
+
createdAt
|
|
90
|
+
updatedAt
|
|
91
|
+
tags { id name }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
`, { id });
|
|
95
|
+
const note = data?.note;
|
|
96
|
+
let markdown = note.contentAsMarkdown;
|
|
97
|
+
const totalLength = markdown.length;
|
|
98
|
+
let truncated = false;
|
|
99
|
+
if (maxLength > 0 && markdown.length > maxLength) {
|
|
100
|
+
markdown = markdown.slice(0, maxLength);
|
|
101
|
+
truncated = true;
|
|
102
|
+
}
|
|
103
|
+
const output = [
|
|
104
|
+
`# ${note.title}`,
|
|
105
|
+
"",
|
|
106
|
+
`Tags: ${note.tags.map((t) => t.name).join(", ") || "(none)"}`,
|
|
107
|
+
`Created: ${note.createdAt}`,
|
|
108
|
+
`Updated: ${note.updatedAt}`,
|
|
109
|
+
...truncated ? [`Content: ${totalLength} chars (showing first ${maxLength})`] : [],
|
|
110
|
+
"",
|
|
111
|
+
markdown,
|
|
112
|
+
...truncated ? ["\n... (truncated, use maxLength: 0 to read full content)"] : []
|
|
113
|
+
].join("\n");
|
|
114
|
+
return {
|
|
115
|
+
content: [{
|
|
116
|
+
type: "text",
|
|
117
|
+
text: output
|
|
118
|
+
}]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
server.tool(
|
|
123
|
+
"list_tags",
|
|
124
|
+
"List all tags with their note counts.",
|
|
125
|
+
{},
|
|
126
|
+
async () => {
|
|
127
|
+
const data = await graphql(serverUrl, `
|
|
128
|
+
query ($searchFilter: SearchFilterInput, $pagination: PaginationInput) {
|
|
129
|
+
allTags(searchFilter: $searchFilter, pagination: $pagination) {
|
|
130
|
+
totalCount
|
|
131
|
+
tags {
|
|
132
|
+
id
|
|
133
|
+
name
|
|
134
|
+
referenceCount
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
`, {
|
|
139
|
+
searchFilter: { query: "" },
|
|
140
|
+
pagination: { limit: 100, offset: 0 }
|
|
141
|
+
});
|
|
142
|
+
const result = data?.allTags;
|
|
143
|
+
return {
|
|
144
|
+
content: [{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: JSON.stringify(result, null, 2)
|
|
147
|
+
}]
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
server.tool(
|
|
152
|
+
"list_recent_notes",
|
|
153
|
+
"List recently updated notes. Returns titles and tags only. Use read_note to get content of a specific note.",
|
|
154
|
+
{
|
|
155
|
+
limit: z.number().optional().default(10).describe("Max results (default: 10)")
|
|
156
|
+
},
|
|
157
|
+
async ({ limit }) => {
|
|
158
|
+
const data = await graphql(serverUrl, `
|
|
159
|
+
query ($searchFilter: SearchFilterInput, $pagination: PaginationInput) {
|
|
160
|
+
allNotes(searchFilter: $searchFilter, pagination: $pagination) {
|
|
161
|
+
totalCount
|
|
162
|
+
notes {
|
|
163
|
+
id
|
|
164
|
+
title
|
|
165
|
+
updatedAt
|
|
166
|
+
tags { id name }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
`, {
|
|
171
|
+
searchFilter: { query: "", sortBy: "updatedAt", sortOrder: "desc" },
|
|
172
|
+
pagination: { limit, offset: 0 }
|
|
173
|
+
});
|
|
174
|
+
const result = data?.allNotes;
|
|
175
|
+
const notes = result.notes.map((note) => ({
|
|
176
|
+
id: note.id,
|
|
177
|
+
title: note.title,
|
|
178
|
+
updatedAt: note.updatedAt,
|
|
179
|
+
tags: note.tags.map((t) => t.name)
|
|
180
|
+
}));
|
|
181
|
+
return {
|
|
182
|
+
content: [{
|
|
183
|
+
type: "text",
|
|
184
|
+
text: JSON.stringify({ totalCount: result.totalCount, notes }, null, 2)
|
|
185
|
+
}]
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
const transport = new StdioServerTransport();
|
|
190
|
+
await server.connect(transport);
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
startMcpServer
|
|
194
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ocean-brain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ocean-brain": "./dist/index.js"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@graphql-tools/schema": "^10.0.31",
|
|
17
17
|
"@graphql-tools/utils": "^11.0.0",
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
18
19
|
"@prisma/client": "^6.19.2",
|
|
19
20
|
"commander": "^14.0.3",
|
|
20
21
|
"express": "^5.2.1",
|
|
@@ -23,7 +24,8 @@
|
|
|
23
24
|
"graphql-http": "^1.22.4",
|
|
24
25
|
"prisma": "^6.19.2",
|
|
25
26
|
"sqlite3": "^5.1.7",
|
|
26
|
-
"winston": "^3.19.0"
|
|
27
|
+
"winston": "^3.19.0",
|
|
28
|
+
"zod": "^3.25.0"
|
|
27
29
|
},
|
|
28
30
|
"scripts": {
|
|
29
31
|
"dev": "tsx src/index.ts",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ServerBlockNoteEditor } from "@blocknote/server-util";
|
|
2
|
+
function preprocessCustomInlineContent(blocks) {
|
|
3
|
+
return blocks.map((block) => ({
|
|
4
|
+
...block,
|
|
5
|
+
content: block.content?.map((inline) => {
|
|
6
|
+
if (inline.type === "reference") {
|
|
7
|
+
return {
|
|
8
|
+
type: "text",
|
|
9
|
+
text: `[[${inline.props?.title || inline.props?.id || ""}]]`,
|
|
10
|
+
styles: {}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (inline.type === "tag") {
|
|
14
|
+
const tag = inline.props?.tag?.replace(/^@/, "") || "";
|
|
15
|
+
return {
|
|
16
|
+
type: "text",
|
|
17
|
+
text: `#${tag}`,
|
|
18
|
+
styles: {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return inline;
|
|
22
|
+
}),
|
|
23
|
+
children: block.children?.length ? preprocessCustomInlineContent(block.children) : []
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
let editorInstance = null;
|
|
27
|
+
function getEditor() {
|
|
28
|
+
if (!editorInstance) {
|
|
29
|
+
editorInstance = ServerBlockNoteEditor.create();
|
|
30
|
+
}
|
|
31
|
+
return editorInstance;
|
|
32
|
+
}
|
|
33
|
+
async function blocksToMarkdown(contentJson) {
|
|
34
|
+
try {
|
|
35
|
+
const blocks = JSON.parse(contentJson);
|
|
36
|
+
const processed = preprocessCustomInlineContent(blocks);
|
|
37
|
+
const editor = getEditor();
|
|
38
|
+
return await editor.blocksToMarkdownLossy(processed);
|
|
39
|
+
} catch {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
blocksToMarkdown
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=blocknote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/modules/blocknote.ts"],"sourcesContent":["import { ServerBlockNoteEditor } from '@blocknote/server-util';\n\ninterface BlockNote {\n id?: string;\n type: string;\n props?: Record<string, unknown>;\n content?: BlockNote[];\n children?: BlockNote[];\n text?: string;\n styles?: Record<string, unknown>;\n}\n\nfunction preprocessCustomInlineContent(blocks: BlockNote[]): BlockNote[] {\n return blocks.map((block) => ({\n ...block,\n content: 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)?.replace(/^@/, '') || '';\n return {\n type: 'text',\n text: `#${tag}`,\n styles: {}\n };\n }\n return inline;\n }),\n children: block.children?.length\n ? preprocessCustomInlineContent(block.children)\n : []\n }));\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 processed = preprocessCustomInlineContent(blocks);\n const editor = getEditor();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await editor.blocksToMarkdownLossy(processed as any);\n } catch {\n return '';\n }\n}\n"],"mappings":"AAAA,SAAS,6BAA6B;AAYtC,SAAS,8BAA8B,QAAkC;AACrE,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC1B,GAAG;AAAA,IACH,SAAS,MAAM,SAAS,IAAI,CAAC,WAAW;AACpC,UAAI,OAAO,SAAS,aAAa;AAC7B,eAAO;AAAA,UACH,MAAM;AAAA,UACN,MAAM,KAAK,OAAO,OAAO,SAAS,OAAO,OAAO,MAAM,EAAE;AAAA,UACxD,QAAQ,CAAC;AAAA,QACb;AAAA,MACJ;AACA,UAAI,OAAO,SAAS,OAAO;AACvB,cAAM,MAAO,OAAO,OAAO,KAAgB,QAAQ,MAAM,EAAE,KAAK;AAChE,eAAO;AAAA,UACH,MAAM;AAAA,UACN,MAAM,IAAI,GAAG;AAAA,UACb,QAAQ,CAAC;AAAA,QACb;AAAA,MACJ;AACA,aAAO;AAAA,IACX,CAAC;AAAA,IACD,UAAU,MAAM,UAAU,SACpB,8BAA8B,MAAM,QAAQ,IAC5C,CAAC;AAAA,EACX,EAAE;AACN;AAEA,IAAI,iBAA+C;AAEnD,SAAS,YAAmC;AACxC,MAAI,CAAC,gBAAgB;AACjB,qBAAiB,sBAAsB,OAAO;AAAA,EAClD;AACA,SAAO;AACX;AAEA,eAAsB,iBAAiB,aAAsC;AACzE,MAAI;AACA,UAAM,SAAS,KAAK,MAAM,WAAW;AACrC,UAAM,YAAY,8BAA8B,MAAM;AACtD,UAAM,SAAS,UAAU;AAEzB,WAAO,MAAM,OAAO,sBAAsB,SAAgB;AAAA,EAC9D,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;","names":[]}
|
|
@@ -46,6 +46,7 @@ const noteType = gql`
|
|
|
46
46
|
id: ID!
|
|
47
47
|
title: String!
|
|
48
48
|
content: String!
|
|
49
|
+
contentAsMarkdown: String!
|
|
49
50
|
createdAt: String!
|
|
50
51
|
updatedAt: String!
|
|
51
52
|
pinned: Boolean!
|
|
@@ -388,7 +389,13 @@ const noteResolvers = {
|
|
|
388
389
|
return await Promise.all(updatePromises);
|
|
389
390
|
}
|
|
390
391
|
},
|
|
391
|
-
Note: {
|
|
392
|
+
Note: {
|
|
393
|
+
tags: async (note) => await models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),
|
|
394
|
+
contentAsMarkdown: async (note) => {
|
|
395
|
+
const { blocksToMarkdown } = await import("../../modules/blocknote.js");
|
|
396
|
+
return blocksToMarkdown(note.content);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
392
399
|
};
|
|
393
400
|
export {
|
|
394
401
|
noteMutation,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/schema/note/index.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport models from '~/models.js';\nimport { gql } from '~/modules/graphql.js';\n\nimport type { Note, Prisma } from '~/models.js';\nimport type { Pagination, SearchFilter, NoteInput } from '~/types/index.js';\n\nexport const noteType = gql`\n input PaginationInput {\n limit: Int!\n offset: Int!\n }\n\n input SearchFilterInput {\n query: String!\n sortBy: String\n sortOrder: String\n pinnedFirst: Boolean\n }\n\n input DateRangeInput {\n start: String!\n end: String!\n }\n\n enum NoteLayout {\n narrow\n wide\n full\n }\n\n input NoteInput {\n title: String\n content: String\n layout: NoteLayout\n }\n\n input NoteOrderInput {\n id: ID!\n order: Int!\n }\n\n type Tag {\n id: ID!\n name: String!\n createdAt: String!\n updatedAt: String!\n }\n\n type Note {\n id: ID!\n title: String!\n content: String!\n createdAt: String!\n updatedAt: String!\n pinned: Boolean!\n order: Int!\n layout: NoteLayout!\n tags: [Tag!]!\n }\n\n type Notes {\n totalCount: Int!\n notes: [Note!]!\n }\n`;\n\nexport const noteQuery = gql`\n type GraphNode {\n id: ID!\n title: String!\n connections: Int!\n }\n\n type GraphLink {\n source: ID!\n target: ID!\n }\n\n type NoteGraph {\n nodes: [GraphNode!]!\n links: [GraphLink!]!\n }\n\n type Query {\n allNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!\n tagNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!\n notesInDateRange(dateRange: DateRangeInput): [Note!]!\n pinnedNotes: [Note!]!\n imageNotes(src: String!): [Note!]!\n backReferences(id: ID!): [Note]!\n note(id: ID!): Note!\n noteGraph: NoteGraph!\n }\n`;\n\nexport const noteMutation = gql`\n type Mutation {\n createNote(note: NoteInput!): Note!\n updateNote(id: ID!, note: NoteInput!): Note!\n deleteNote(id: ID!): Boolean!\n pinNote(id: ID!, pinned: Boolean!): Note!\n reorderNotes(notes: [NoteOrderInput!]!): [Note!]!\n }\n`;\n\nexport const noteTypeDefs = `\n ${noteType}\n ${noteQuery}\n ${noteMutation}\n`;\n\ninterface BlockNote<T = unknown> {\n id: string;\n type: string;\n props: T;\n content?: BlockNote<T>[];\n children?: BlockNote<T>[];\n}\n\nconst extractBlocksByType = <T>(type: string, dataArray: BlockNote[]): BlockNote<T>[] => {\n let result: BlockNote[] = [];\n\n for (const data of dataArray) {\n if (data.type === type) {\n result.push(data);\n }\n\n if (data.children && data.children.length > 0) {\n result = result.concat(extractBlocksByType(type, data.children));\n }\n\n if (data.content && data.content.length > 0) {\n for (const contentItem of data.content) {\n if (contentItem.type === type) {\n result.push(contentItem);\n }\n }\n }\n }\n\n return result as unknown as BlockNote<T>[];\n};\n\nexport const noteResolvers: IResolvers = {\n Query: {\n allNotes: async (_, {\n searchFilter,\n pagination\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n }) => {\n const queryItems = searchFilter.query.split(' ');\n const included = queryItems\n .filter((item: string) => !item.startsWith('-'))\n .map((word: string) => `%${word}%`);\n const excluded = queryItems\n .filter((item: string) => item.startsWith('-'))\n .map((item: string) => item.slice(1))\n .map((word: string) => `%${word}%`);\n\n const where: Prisma.NoteWhereInput = {\n AND: [\n ...included.map((keyword: string) => ({\n OR: [\n { title: { contains: keyword } },\n { content: { contains: keyword } }\n ]\n })),\n ...excluded.map((keyword: string) => ({\n NOT: {\n OR: [\n { title: { contains: keyword } },\n { content: { contains: keyword } }\n ]\n }\n }))\n ]\n };\n\n const sortBy = searchFilter.sortBy || 'updatedAt';\n const sortOrder = searchFilter.sortOrder || 'desc';\n const pinnedFirst = searchFilter.pinnedFirst || false;\n\n const orderBy: Prisma.NoteOrderByWithRelationInput[] = [];\n\n if (pinnedFirst) {\n orderBy.push({ pinned: 'desc' });\n }\n\n if (sortBy === 'createdAt') {\n orderBy.push({ createdAt: sortOrder as 'asc' | 'desc' });\n } else {\n orderBy.push({ updatedAt: sortOrder as 'asc' | 'desc' });\n }\n\n const $notes = models.note.findMany({\n orderBy,\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset)\n });\n return {\n totalCount: models.note.count({ where }),\n notes: $notes\n };\n },\n notesInDateRange: async (_, { dateRange }: {\n dateRange: {\n start: string;\n end: string;\n };\n }) => {\n const where: Prisma.NoteWhereInput = {\n OR: [\n {\n updatedAt: {\n gte: new Date(dateRange.start),\n lte: new Date(dateRange.end)\n }\n },\n {\n createdAt: {\n gte: new Date(dateRange.start),\n lte: new Date(dateRange.end)\n }\n }\n ]\n };\n\n const $notes = await models.note.findMany({\n orderBy: { createdAt: 'asc' },\n where\n });\n\n return $notes;\n },\n tagNotes: async (_, {\n searchFilter,\n pagination\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n }) => {\n const where: Prisma.NoteWhereInput = { tags: { some: { id: Number(searchFilter.query) } } };\n\n const $notes = models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset)\n });\n return {\n totalCount: models.note.count({ where }),\n notes: $notes\n };\n },\n pinnedNotes: async () => models.note.findMany({\n orderBy: [\n { order: 'asc' },\n { updatedAt: 'desc' }\n ],\n where: { pinned: true }\n }),\n imageNotes: async (_, { src }) => models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where: { content: { contains: src } }\n }),\n backReferences: async (_, { id }: Note) => {\n return models.note.findMany({\n orderBy: [\n { pinned: 'desc' },\n { updatedAt: 'desc' }\n ],\n where: { content: { contains: `reference\",\"props\":{\"id\":\"${id}\"` } }\n });\n },\n note: async (_, { id }: Note) => {\n const $note = await models.note.findUnique({ where: { id: Number(id) } });\n if (!$note) {\n throw 'NOT FOUND';\n }\n if ($note.content) {\n const blocks = extractBlocksByType<{\n id: string;\n title: string;\n }>('reference', JSON.parse($note.content));\n if (blocks.length > 0) {\n const referenceIds = blocks.map(block => Number(block.props.id));\n const $references = await models.note.findMany({ where: { id: { in: referenceIds } } });\n const newContent = $references.reduce<string>((acc: string, $reference: Note) => {\n const reference = blocks.find(block => Number(block.props.id) === $reference.id);\n if (reference && reference.props.title !== $reference.title) {\n return acc.replace(\n `reference\",\"props\":{\"id\":\"${reference.props.id}\",\"title\":\"${reference.props.title}\"`,\n `reference\",\"props\":{\"id\":\"${$reference.id}\",\"title\":\"${$reference.title}\"`\n );\n }\n return acc;\n }, $note.content);\n if (newContent !== $note.content) {\n try {\n JSON.parse(newContent);\n return await models.note.update({\n where: { id: $note.id },\n data: { content: newContent }\n });\n } catch (e) {\n console.error(e);\n }\n }\n }\n }\n return $note;\n },\n noteGraph: async () => {\n const $notes = await models.note.findMany({\n select: {\n id: true,\n title: true,\n content: true\n }\n });\n\n const nodes: Array<{ id: string; title: string; connections: number }> = [];\n const links: Array<{ source: string; target: string }> = [];\n const connectionCount: Record<string, number> = {};\n const linkSet = new Set<string>();\n\n // Extract all references from each note\n for (const $note of $notes) {\n if ($note.content) {\n try {\n const blocks = extractBlocksByType<{ id: string }>('reference', JSON.parse($note.content));\n for (const block of blocks) {\n const targetId = block.props.id;\n // Avoid self-references and duplicate links\n if (targetId && String($note.id) !== targetId) {\n const linkKey = `${$note.id}-${targetId}`;\n const reverseLinkKey = `${targetId}-${$note.id}`;\n if (!linkSet.has(linkKey) && !linkSet.has(reverseLinkKey)) {\n linkSet.add(linkKey);\n links.push({\n source: String($note.id),\n target: targetId\n });\n // Count connections for both nodes\n connectionCount[String($note.id)] = (connectionCount[String($note.id)] || 0) + 1;\n connectionCount[targetId] = (connectionCount[targetId] || 0) + 1;\n }\n }\n }\n } catch {\n // Skip notes with invalid JSON content\n }\n }\n }\n\n // Build nodes array with connection counts\n for (const $note of $notes) {\n nodes.push({\n id: String($note.id),\n title: $note.title || 'Untitled',\n connections: connectionCount[String($note.id)] || 0\n });\n }\n\n return {\n nodes,\n links\n };\n }\n },\n Mutation: {\n createNote: async (_, { note }: { note: NoteInput }) => {\n const PLACEHOLDER_PREFIX = '{%';\n const PLACEHOLDER_SUFFIX = '%}';\n\n const replacePlaceholder = async (content: string) => {\n const placeholders = content.matchAll(new RegExp(`${PLACEHOLDER_PREFIX}([^}]+)${PLACEHOLDER_SUFFIX}`, 'g'));\n const $placeholders = await models.placeholder.findMany({\n select: {\n template: true,\n replacement: true\n },\n where: { template: { in: Array.from(new Set(Array.from(placeholders, p => p[1]))) } }\n });\n\n for (const $placeholder of $placeholders) {\n content = content.replace(new RegExp(`${PLACEHOLDER_PREFIX}${$placeholder.template}${PLACEHOLDER_SUFFIX}`, 'g'), $placeholder.replacement);\n }\n return content;\n };\n\n const replacedTitle = await replacePlaceholder(note.title);\n const replacedContent = await replacePlaceholder(note.content);\n\n const $note = await models.note.create({\n data: {\n title: replacedTitle,\n content: replacedContent,\n ...(note.layout && { layout: note.layout })\n }\n });\n if (note.content) {\n const blocks = extractBlocksByType<{ id: string }>(\n 'tag',\n JSON.parse(note.content)\n );\n\n return await models.note.update({\n where: { id: $note.id },\n data: { tags: { set: blocks.map(block => ({ id: Number(block.props.id) })) } }\n });\n }\n\n return $note;\n },\n updateNote: async (_, { id, note }: { id: number; note: NoteInput }) => {\n let blocks: BlockNote<{ id: string }>[] = [];\n\n if (note.content) {\n blocks = extractBlocksByType<{ id: string }>(\n 'tag',\n JSON.parse(note.content)\n );\n }\n\n const $note = await models.note.update({\n where: { id: Number(id) },\n data: {\n ...note,\n ...(note.content ? { tags: { set: blocks.map(block => ({ id: Number(block.props.id) })) } } : {})\n }\n });\n return $note;\n },\n deleteNote: async (_, { id }: Note) => {\n await models.tag.deleteMany({ where: { notes: { none: {} } } });\n await models.note.delete({ where: { id: Number(id) } });\n return true;\n },\n pinNote: (_, { id, pinned }: Note) => models.note.update({\n where: { id: Number(id) },\n data: { pinned: Boolean(pinned) }\n }),\n reorderNotes: async (_, { notes }: { notes: Array<{ id: string; order: number }> }) => {\n const updatePromises = notes.map(({ id, order }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { order }\n })\n );\n return await Promise.all(updatePromises);\n }\n },\n Note: { tags: async (note: Note) => await models.tag.findMany({ where: { notes: { some: { id: note.id } } } }) }\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/schema/note/index.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport models from '~/models.js';\nimport { gql } from '~/modules/graphql.js';\n\nimport type { Note, Prisma } from '~/models.js';\nimport type { Pagination, SearchFilter, NoteInput } from '~/types/index.js';\n\nexport const noteType = gql`\n input PaginationInput {\n limit: Int!\n offset: Int!\n }\n\n input SearchFilterInput {\n query: String!\n sortBy: String\n sortOrder: String\n pinnedFirst: Boolean\n }\n\n input DateRangeInput {\n start: String!\n end: String!\n }\n\n enum NoteLayout {\n narrow\n wide\n full\n }\n\n input NoteInput {\n title: String\n content: String\n layout: NoteLayout\n }\n\n input NoteOrderInput {\n id: ID!\n order: Int!\n }\n\n type Tag {\n id: ID!\n name: String!\n createdAt: String!\n updatedAt: String!\n }\n\n type Note {\n id: ID!\n title: String!\n content: String!\n contentAsMarkdown: String!\n createdAt: String!\n updatedAt: String!\n pinned: Boolean!\n order: Int!\n layout: NoteLayout!\n tags: [Tag!]!\n }\n\n type Notes {\n totalCount: Int!\n notes: [Note!]!\n }\n`;\n\nexport const noteQuery = gql`\n type GraphNode {\n id: ID!\n title: String!\n connections: Int!\n }\n\n type GraphLink {\n source: ID!\n target: ID!\n }\n\n type NoteGraph {\n nodes: [GraphNode!]!\n links: [GraphLink!]!\n }\n\n type Query {\n allNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!\n tagNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!\n notesInDateRange(dateRange: DateRangeInput): [Note!]!\n pinnedNotes: [Note!]!\n imageNotes(src: String!): [Note!]!\n backReferences(id: ID!): [Note]!\n note(id: ID!): Note!\n noteGraph: NoteGraph!\n }\n`;\n\nexport const noteMutation = gql`\n type Mutation {\n createNote(note: NoteInput!): Note!\n updateNote(id: ID!, note: NoteInput!): Note!\n deleteNote(id: ID!): Boolean!\n pinNote(id: ID!, pinned: Boolean!): Note!\n reorderNotes(notes: [NoteOrderInput!]!): [Note!]!\n }\n`;\n\nexport const noteTypeDefs = `\n ${noteType}\n ${noteQuery}\n ${noteMutation}\n`;\n\ninterface BlockNote<T = unknown> {\n id: string;\n type: string;\n props: T;\n content?: BlockNote<T>[];\n children?: BlockNote<T>[];\n}\n\nconst extractBlocksByType = <T>(type: string, dataArray: BlockNote[]): BlockNote<T>[] => {\n let result: BlockNote[] = [];\n\n for (const data of dataArray) {\n if (data.type === type) {\n result.push(data);\n }\n\n if (data.children && data.children.length > 0) {\n result = result.concat(extractBlocksByType(type, data.children));\n }\n\n if (data.content && data.content.length > 0) {\n for (const contentItem of data.content) {\n if (contentItem.type === type) {\n result.push(contentItem);\n }\n }\n }\n }\n\n return result as unknown as BlockNote<T>[];\n};\n\nexport const noteResolvers: IResolvers = {\n Query: {\n allNotes: async (_, {\n searchFilter,\n pagination\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n }) => {\n const queryItems = searchFilter.query.split(' ');\n const included = queryItems\n .filter((item: string) => !item.startsWith('-'))\n .map((word: string) => `%${word}%`);\n const excluded = queryItems\n .filter((item: string) => item.startsWith('-'))\n .map((item: string) => item.slice(1))\n .map((word: string) => `%${word}%`);\n\n const where: Prisma.NoteWhereInput = {\n AND: [\n ...included.map((keyword: string) => ({\n OR: [\n { title: { contains: keyword } },\n { content: { contains: keyword } }\n ]\n })),\n ...excluded.map((keyword: string) => ({\n NOT: {\n OR: [\n { title: { contains: keyword } },\n { content: { contains: keyword } }\n ]\n }\n }))\n ]\n };\n\n const sortBy = searchFilter.sortBy || 'updatedAt';\n const sortOrder = searchFilter.sortOrder || 'desc';\n const pinnedFirst = searchFilter.pinnedFirst || false;\n\n const orderBy: Prisma.NoteOrderByWithRelationInput[] = [];\n\n if (pinnedFirst) {\n orderBy.push({ pinned: 'desc' });\n }\n\n if (sortBy === 'createdAt') {\n orderBy.push({ createdAt: sortOrder as 'asc' | 'desc' });\n } else {\n orderBy.push({ updatedAt: sortOrder as 'asc' | 'desc' });\n }\n\n const $notes = models.note.findMany({\n orderBy,\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset)\n });\n return {\n totalCount: models.note.count({ where }),\n notes: $notes\n };\n },\n notesInDateRange: async (_, { dateRange }: {\n dateRange: {\n start: string;\n end: string;\n };\n }) => {\n const where: Prisma.NoteWhereInput = {\n OR: [\n {\n updatedAt: {\n gte: new Date(dateRange.start),\n lte: new Date(dateRange.end)\n }\n },\n {\n createdAt: {\n gte: new Date(dateRange.start),\n lte: new Date(dateRange.end)\n }\n }\n ]\n };\n\n const $notes = await models.note.findMany({\n orderBy: { createdAt: 'asc' },\n where\n });\n\n return $notes;\n },\n tagNotes: async (_, {\n searchFilter,\n pagination\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n }) => {\n const where: Prisma.NoteWhereInput = { tags: { some: { id: Number(searchFilter.query) } } };\n\n const $notes = models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset)\n });\n return {\n totalCount: models.note.count({ where }),\n notes: $notes\n };\n },\n pinnedNotes: async () => models.note.findMany({\n orderBy: [\n { order: 'asc' },\n { updatedAt: 'desc' }\n ],\n where: { pinned: true }\n }),\n imageNotes: async (_, { src }) => models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where: { content: { contains: src } }\n }),\n backReferences: async (_, { id }: Note) => {\n return models.note.findMany({\n orderBy: [\n { pinned: 'desc' },\n { updatedAt: 'desc' }\n ],\n where: { content: { contains: `reference\",\"props\":{\"id\":\"${id}\"` } }\n });\n },\n note: async (_, { id }: Note) => {\n const $note = await models.note.findUnique({ where: { id: Number(id) } });\n if (!$note) {\n throw 'NOT FOUND';\n }\n if ($note.content) {\n const blocks = extractBlocksByType<{\n id: string;\n title: string;\n }>('reference', JSON.parse($note.content));\n if (blocks.length > 0) {\n const referenceIds = blocks.map(block => Number(block.props.id));\n const $references = await models.note.findMany({ where: { id: { in: referenceIds } } });\n const newContent = $references.reduce<string>((acc: string, $reference: Note) => {\n const reference = blocks.find(block => Number(block.props.id) === $reference.id);\n if (reference && reference.props.title !== $reference.title) {\n return acc.replace(\n `reference\",\"props\":{\"id\":\"${reference.props.id}\",\"title\":\"${reference.props.title}\"`,\n `reference\",\"props\":{\"id\":\"${$reference.id}\",\"title\":\"${$reference.title}\"`\n );\n }\n return acc;\n }, $note.content);\n if (newContent !== $note.content) {\n try {\n JSON.parse(newContent);\n return await models.note.update({\n where: { id: $note.id },\n data: { content: newContent }\n });\n } catch (e) {\n console.error(e);\n }\n }\n }\n }\n return $note;\n },\n noteGraph: async () => {\n const $notes = await models.note.findMany({\n select: {\n id: true,\n title: true,\n content: true\n }\n });\n\n const nodes: Array<{ id: string; title: string; connections: number }> = [];\n const links: Array<{ source: string; target: string }> = [];\n const connectionCount: Record<string, number> = {};\n const linkSet = new Set<string>();\n\n // Extract all references from each note\n for (const $note of $notes) {\n if ($note.content) {\n try {\n const blocks = extractBlocksByType<{ id: string }>('reference', JSON.parse($note.content));\n for (const block of blocks) {\n const targetId = block.props.id;\n // Avoid self-references and duplicate links\n if (targetId && String($note.id) !== targetId) {\n const linkKey = `${$note.id}-${targetId}`;\n const reverseLinkKey = `${targetId}-${$note.id}`;\n if (!linkSet.has(linkKey) && !linkSet.has(reverseLinkKey)) {\n linkSet.add(linkKey);\n links.push({\n source: String($note.id),\n target: targetId\n });\n // Count connections for both nodes\n connectionCount[String($note.id)] = (connectionCount[String($note.id)] || 0) + 1;\n connectionCount[targetId] = (connectionCount[targetId] || 0) + 1;\n }\n }\n }\n } catch {\n // Skip notes with invalid JSON content\n }\n }\n }\n\n // Build nodes array with connection counts\n for (const $note of $notes) {\n nodes.push({\n id: String($note.id),\n title: $note.title || 'Untitled',\n connections: connectionCount[String($note.id)] || 0\n });\n }\n\n return {\n nodes,\n links\n };\n }\n },\n Mutation: {\n createNote: async (_, { note }: { note: NoteInput }) => {\n const PLACEHOLDER_PREFIX = '{%';\n const PLACEHOLDER_SUFFIX = '%}';\n\n const replacePlaceholder = async (content: string) => {\n const placeholders = content.matchAll(new RegExp(`${PLACEHOLDER_PREFIX}([^}]+)${PLACEHOLDER_SUFFIX}`, 'g'));\n const $placeholders = await models.placeholder.findMany({\n select: {\n template: true,\n replacement: true\n },\n where: { template: { in: Array.from(new Set(Array.from(placeholders, p => p[1]))) } }\n });\n\n for (const $placeholder of $placeholders) {\n content = content.replace(new RegExp(`${PLACEHOLDER_PREFIX}${$placeholder.template}${PLACEHOLDER_SUFFIX}`, 'g'), $placeholder.replacement);\n }\n return content;\n };\n\n const replacedTitle = await replacePlaceholder(note.title);\n const replacedContent = await replacePlaceholder(note.content);\n\n const $note = await models.note.create({\n data: {\n title: replacedTitle,\n content: replacedContent,\n ...(note.layout && { layout: note.layout })\n }\n });\n if (note.content) {\n const blocks = extractBlocksByType<{ id: string }>(\n 'tag',\n JSON.parse(note.content)\n );\n\n return await models.note.update({\n where: { id: $note.id },\n data: { tags: { set: blocks.map(block => ({ id: Number(block.props.id) })) } }\n });\n }\n\n return $note;\n },\n updateNote: async (_, { id, note }: { id: number; note: NoteInput }) => {\n let blocks: BlockNote<{ id: string }>[] = [];\n\n if (note.content) {\n blocks = extractBlocksByType<{ id: string }>(\n 'tag',\n JSON.parse(note.content)\n );\n }\n\n const $note = await models.note.update({\n where: { id: Number(id) },\n data: {\n ...note,\n ...(note.content ? { tags: { set: blocks.map(block => ({ id: Number(block.props.id) })) } } : {})\n }\n });\n return $note;\n },\n deleteNote: async (_, { id }: Note) => {\n await models.tag.deleteMany({ where: { notes: { none: {} } } });\n await models.note.delete({ where: { id: Number(id) } });\n return true;\n },\n pinNote: (_, { id, pinned }: Note) => models.note.update({\n where: { id: Number(id) },\n data: { pinned: Boolean(pinned) }\n }),\n reorderNotes: async (_, { notes }: { notes: Array<{ id: string; order: number }> }) => {\n const updatePromises = notes.map(({ id, order }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { order }\n })\n );\n return await Promise.all(updatePromises);\n }\n },\n Note: {\n tags: async (note: Note) => await models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),\n contentAsMarkdown: async (note: Note) => {\n const { blocksToMarkdown } = await import('~/modules/blocknote.js');\n return blocksToMarkdown(note.content);\n }\n }\n};\n"],"mappings":"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","names":[]}
|