ocean-brain 0.7.5 → 0.8.1
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 +3 -3
- package/dist/mcp.js +180 -736
- package/package.json +6 -2
- package/server/client/dist/assets/ArrowRight.es-CbA8ey6a.js +1 -0
- package/server/client/dist/assets/{Calendar-Bc2zJueR.js → Calendar-CgWgr1BV.js} +2 -2
- package/server/client/dist/assets/Callout-DKruIT0f.js +1 -0
- package/server/client/dist/assets/Copy.es-DFbgpaNJ.js +1 -0
- package/server/client/dist/assets/Graph-kCltGRlY.js +1 -0
- package/server/client/dist/assets/{Image-DxWhlIDG.js → Image-7W0DZwMo.js} +1 -1
- package/server/client/dist/assets/Image.es-5pXLx9TU.js +1 -0
- package/server/client/dist/assets/Note-BW6Np_5g.js +20 -0
- package/server/client/dist/assets/Plus.es-B0sg64VJ.js +1 -0
- package/server/client/dist/assets/Reminders-C10fHQcl.js +1 -0
- package/server/client/dist/assets/Search-tf2nV4v0.js +1 -0
- package/server/client/dist/assets/{SurfaceCard-CkAVMLPs.js → SurfaceCard-pydtvHfu.js} +1 -1
- package/server/client/dist/assets/Tag-uCoYbISy.js +1 -0
- package/server/client/dist/assets/TagNotes-CqOcQiRw.js +1 -0
- package/server/client/dist/assets/Trash.es-w32Goxfa.js +1 -0
- package/server/client/dist/assets/ViewNotes-slPXXoOY.js +1 -0
- package/server/client/dist/assets/ViewSectionDialog-brTrsF6G.js +102 -0
- package/server/client/dist/assets/Views-B_wsb-XJ.js +1 -0
- package/server/client/dist/assets/{graph-vendor-CUxe67Lr.js → graph-vendor-BAF95C0g.js} +2 -2
- package/server/client/dist/assets/image.api-CYsjXiMr.js +17 -0
- package/server/client/dist/assets/index-BR05aSXr.js +342 -0
- package/server/client/dist/assets/{index-D3BfroIT.js → index-D83QbOcw.js} +1 -1
- package/server/client/dist/assets/{index-C5BGzDgt.css → index-DPI6YoDt.css} +1 -1
- package/server/client/dist/assets/manage-image-BYIhWu56.js +1 -0
- package/server/client/dist/assets/{manage-image-detail-C8Q7K7I3.js → manage-image-detail-6KgmQvbN.js} +1 -1
- package/server/client/dist/assets/mcp-Dd98N9Hq.js +4 -0
- package/server/client/dist/assets/mcp-admin-adapter-DOgBsYpl.js +1 -0
- package/server/client/dist/assets/note-core-CMGUaW1v.js +176 -0
- package/server/client/dist/assets/note-vendor-Hoz9Q4-4.js +54 -0
- package/server/client/dist/assets/{placeholder-Cr_nI8UI.js → placeholder-siHGg9aK.js} +4 -4
- package/server/client/dist/assets/{properties-CvYWJWUM.js → properties-CyVRKgcT.js} +1 -1
- package/server/client/dist/assets/tag.api-CwmC8crB.js +21 -0
- package/server/client/dist/assets/trash-wojQBK9Z.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-CDmIEwsw.js → useReminderMutate-DuVKxpBh.js} +1 -1
- package/server/client/dist/index.html +5 -5
- package/server/dist/app.js +0 -1
- package/server/dist/features/auth/http/api.js +0 -1
- package/server/dist/features/auth/http/login-page.js +272 -5
- package/server/dist/features/auth/http/pages.js +20 -3
- package/server/dist/features/auth/service.js +3 -2
- package/server/dist/features/cache/graphql/cache.field.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.mutation.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.query.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.type-defs.js +0 -1
- package/server/dist/features/cache/graphql/index.js +0 -1
- package/server/dist/features/image/graphql/image.field.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.mutation.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.query.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.type-defs.js +0 -1
- package/server/dist/features/image/graphql/index.js +0 -1
- package/server/dist/features/image/http/upload.js +34 -24
- package/server/dist/features/image/services/delete.js +0 -1
- package/server/dist/features/image/services/upload.js +0 -1
- package/server/dist/features/image/services/validation.js +110 -0
- package/server/dist/features/mcp-admin/http/handlers.js +11 -4
- package/server/dist/features/mcp-admin/service.js +0 -1
- package/server/dist/features/note/graphql/index.js +0 -1
- package/server/dist/features/note/graphql/note.field.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.query.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.type-defs.js +0 -1
- package/server/dist/features/note/http/mcp.js +8 -44
- package/server/dist/features/note/services/authoring.js +0 -1
- package/server/dist/features/note/services/cleanup.js +2 -3
- package/server/dist/features/note/services/content-blocks.js +0 -1
- package/server/dist/features/note/services/markdown-intent-write.js +11 -44
- package/server/dist/features/note/services/markdown-patch.js +4 -124
- package/server/dist/features/note/services/properties.js +0 -1
- package/server/dist/features/note/services/search-backfill.js +0 -1
- package/server/dist/features/note/services/search.js +0 -1
- package/server/dist/features/note/services/snapshot.js +0 -1
- package/server/dist/features/note/services/tag-filter.js +0 -1
- package/server/dist/features/note/services/trash.js +0 -1
- package/server/dist/features/note/services/write-conflict.js +0 -1
- package/server/dist/features/note/services/write.js +13 -14
- package/server/dist/features/placeholder/graphql/index.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.type-defs.js +0 -1
- package/server/dist/features/reminder/graphql/index.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.field.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.query.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.type-defs.js +0 -1
- package/server/dist/features/tag/graphql/index.js +0 -1
- package/server/dist/features/tag/graphql/tag.field.resolver.js +0 -1
- package/server/dist/features/tag/graphql/tag.mutation.resolver.js +0 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js +16 -2
- package/server/dist/features/tag/graphql/tag.resolver.js +0 -1
- package/server/dist/features/tag/graphql/tag.type-defs.js +2 -1
- package/server/dist/features/tag/http/mcp.js +0 -1
- package/server/dist/features/tag/services/organization.js +0 -1
- package/server/dist/features/view/graphql/index.js +0 -1
- package/server/dist/features/view/graphql/view.mutation.resolver.js +0 -1
- package/server/dist/features/view/graphql/view.query.resolver.js +0 -1
- package/server/dist/features/view/graphql/view.resolver.js +0 -1
- package/server/dist/features/view/graphql/view.type-defs.js +0 -1
- package/server/dist/features/view/services/workspace.js +0 -1
- package/server/dist/main.js +0 -1
- package/server/dist/models.js +0 -1
- package/server/dist/modules/app-version.js +104 -0
- package/server/dist/modules/auth-guard.js +0 -1
- package/server/dist/modules/auth-mode.js +0 -1
- package/server/dist/modules/auth-redirect.js +0 -1
- package/server/dist/modules/blocknote-markdown-editor.js +53 -0
- package/server/dist/modules/blocknote-tree.js +0 -1
- package/server/dist/modules/blocknote.js +10 -4
- package/server/dist/modules/data-maintenance.js +0 -1
- package/server/dist/modules/error-handler.js +0 -1
- package/server/dist/modules/graphql.js +0 -1
- package/server/dist/modules/logger.js +0 -1
- package/server/dist/modules/mcp-auth.js +62 -2
- package/server/dist/modules/mcp-token.js +0 -1
- package/server/dist/modules/prisma-runtime.js +0 -1
- package/server/dist/modules/rate-limit.js +0 -1
- package/server/dist/modules/recovery-retention.js +0 -1
- package/server/dist/modules/server-events-handler.js +0 -1
- package/server/dist/modules/server-events.js +0 -1
- package/server/dist/modules/session-store.js +0 -1
- package/server/dist/modules/use-async.js +0 -1
- package/server/dist/paths.js +1 -1
- package/server/dist/routes/api.js +2 -9
- package/server/dist/routes/auth-pages.js +0 -1
- package/server/dist/routes/client.js +51 -2
- package/server/dist/routes/graphql.js +0 -1
- package/server/dist/routes/index.js +0 -1
- package/server/dist/routes/mcp.js +0 -1
- package/server/dist/schema/index.js +0 -1
- package/server/dist/start.js +0 -1
- package/server/dist/types/index.js +0 -1
- package/server/dist/types/input.js +0 -1
- package/server/client/dist/assets/Callout-B4kA_Cvr.js +0 -1
- package/server/client/dist/assets/Graph-uq5jfJgt.js +0 -1
- package/server/client/dist/assets/Image.es-BpAOJjJT.js +0 -1
- package/server/client/dist/assets/Note-Bo8ZYRP-.js +0 -22
- package/server/client/dist/assets/Plus.es-Dc_js8AC.js +0 -1
- package/server/client/dist/assets/Reminders-lj7Equld.js +0 -1
- package/server/client/dist/assets/Search-1wcRO5--.js +0 -1
- package/server/client/dist/assets/Tag-CeygwFfv.js +0 -1
- package/server/client/dist/assets/TagNotes-B8DqItxw.js +0 -1
- package/server/client/dist/assets/Trash.es-OuQVb_SX.js +0 -1
- package/server/client/dist/assets/ViewNotes-BH7PbdOD.js +0 -1
- package/server/client/dist/assets/ViewSectionTableRenderer-BJwhd5be.js +0 -102
- package/server/client/dist/assets/Views-eYFtsdFj.js +0 -1
- package/server/client/dist/assets/image.api-C0eYGCeH.js +0 -17
- package/server/client/dist/assets/index-jIfkRdY_.js +0 -361
- package/server/client/dist/assets/manage-image-C0YwkLIP.js +0 -1
- package/server/client/dist/assets/mcp-DHHhTLC8.js +0 -1
- package/server/client/dist/assets/note-core-Z6kqulGB.js +0 -180
- package/server/client/dist/assets/note-vendor-i1M2FSI2.js +0 -54
- package/server/client/dist/assets/trash-DA27aW7L.js +0 -1
- package/server/client/dist/fonts/Pretendard-Bold.woff +0 -0
- package/server/client/dist/fonts/Pretendard-Regular.woff +0 -0
- package/server/dist/app.js.map +0 -1
- package/server/dist/features/auth/http/api.js.map +0 -1
- package/server/dist/features/auth/http/login-page.js.map +0 -1
- package/server/dist/features/auth/http/pages.js.map +0 -1
- package/server/dist/features/auth/service.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.field.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.mutation.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.query.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.type-defs.js.map +0 -1
- package/server/dist/features/cache/graphql/index.js.map +0 -1
- package/server/dist/features/image/graphql/image.field.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.mutation.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.query.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.type-defs.js.map +0 -1
- package/server/dist/features/image/graphql/index.js.map +0 -1
- package/server/dist/features/image/http/upload.js.map +0 -1
- package/server/dist/features/image/services/delete.js.map +0 -1
- package/server/dist/features/image/services/remote-fetch.js +0 -176
- package/server/dist/features/image/services/remote-fetch.js.map +0 -1
- package/server/dist/features/image/services/upload.js.map +0 -1
- package/server/dist/features/mcp-admin/http/handlers.js.map +0 -1
- package/server/dist/features/mcp-admin/service.js.map +0 -1
- package/server/dist/features/note/graphql/index.js.map +0 -1
- package/server/dist/features/note/graphql/note.field.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.query.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.type-defs.js.map +0 -1
- package/server/dist/features/note/http/mcp.js.map +0 -1
- package/server/dist/features/note/services/authoring.js.map +0 -1
- package/server/dist/features/note/services/cleanup.js.map +0 -1
- package/server/dist/features/note/services/content-blocks.js.map +0 -1
- package/server/dist/features/note/services/markdown-intent-write.js.map +0 -1
- package/server/dist/features/note/services/markdown-patch.js.map +0 -1
- package/server/dist/features/note/services/properties.js.map +0 -1
- package/server/dist/features/note/services/search-backfill.js.map +0 -1
- package/server/dist/features/note/services/search.js.map +0 -1
- package/server/dist/features/note/services/snapshot.js.map +0 -1
- package/server/dist/features/note/services/tag-filter.js.map +0 -1
- package/server/dist/features/note/services/trash.js.map +0 -1
- package/server/dist/features/note/services/write-conflict.js.map +0 -1
- package/server/dist/features/note/services/write.js.map +0 -1
- package/server/dist/features/placeholder/graphql/index.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.type-defs.js.map +0 -1
- package/server/dist/features/reminder/graphql/index.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.field.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.query.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.type-defs.js.map +0 -1
- package/server/dist/features/tag/graphql/index.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.field.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.mutation.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.type-defs.js.map +0 -1
- package/server/dist/features/tag/http/mcp.js.map +0 -1
- package/server/dist/features/tag/services/organization.js.map +0 -1
- package/server/dist/features/view/graphql/index.js.map +0 -1
- package/server/dist/features/view/graphql/view.mutation.resolver.js.map +0 -1
- package/server/dist/features/view/graphql/view.query.resolver.js.map +0 -1
- package/server/dist/features/view/graphql/view.resolver.js.map +0 -1
- package/server/dist/features/view/graphql/view.type-defs.js.map +0 -1
- package/server/dist/features/view/services/workspace.js.map +0 -1
- package/server/dist/main.js.map +0 -1
- package/server/dist/models.js.map +0 -1
- package/server/dist/modules/auth-guard.js.map +0 -1
- package/server/dist/modules/auth-mode.js.map +0 -1
- package/server/dist/modules/auth-redirect.js.map +0 -1
- package/server/dist/modules/blocknote-tree.js.map +0 -1
- package/server/dist/modules/blocknote.js.map +0 -1
- package/server/dist/modules/data-maintenance.js.map +0 -1
- package/server/dist/modules/error-handler.js.map +0 -1
- package/server/dist/modules/graphql.js.map +0 -1
- package/server/dist/modules/logger.js.map +0 -1
- package/server/dist/modules/mcp-auth.js.map +0 -1
- package/server/dist/modules/mcp-token.js.map +0 -1
- package/server/dist/modules/prisma-runtime.js.map +0 -1
- package/server/dist/modules/rate-limit.js.map +0 -1
- package/server/dist/modules/recovery-retention.js.map +0 -1
- package/server/dist/modules/server-events-handler.js.map +0 -1
- package/server/dist/modules/server-events.js.map +0 -1
- package/server/dist/modules/session-store.js.map +0 -1
- package/server/dist/modules/use-async.js.map +0 -1
- package/server/dist/paths.js.map +0 -1
- package/server/dist/routes/api.js.map +0 -1
- package/server/dist/routes/auth-pages.js.map +0 -1
- package/server/dist/routes/client.js.map +0 -1
- package/server/dist/routes/graphql.js.map +0 -1
- package/server/dist/routes/index.js.map +0 -1
- package/server/dist/routes/mcp.js.map +0 -1
- package/server/dist/schema/index.js.map +0 -1
- package/server/dist/start.js.map +0 -1
- package/server/dist/types/index.js.map +0 -1
- package/server/dist/types/input.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as e}from"./note-vendor-i1M2FSI2.js";import{a0 as M,j as Q,aw as F,k as L,y as _,a6 as I,aT as K,q as i,ah as E,aU as B,N as O,aV as W,P as N,J as H,T as d,ax as U,B as j,a1 as V,aW as Y,i as l,m as $,d as z,al as G,u as J,aX as X,M as y}from"./index-jIfkRdY_.js";import{a as k}from"./graph-vendor-CUxe67Lr.js";import{S as C}from"./SurfaceCard-CkAVMLPs.js";import"./note-core-Z6kqulGB.js";const m=25,v=Q(F),S=30,R=`Deleted notes stay here for ${S} days before permanent removal`,g={title:"Permanently delete note?",description:"This note is referenced by the notes below. If you permanently delete it, those links will stay broken. Other notes will not be edited automatically.",confirmLabel:"Delete now",confirmVariant:"danger"},P=a=>z(a).format("YYYY-MM-DD HH:mm"),Z=({note:a,onClose:o})=>{const s=a?.id??"",c=a?.title||"Untitled note",r=J({queryKey:i.notes.trashDetail(s),enabled:!!a,queryFn:async()=>{const h=await X(s);if(h.type==="error")throw h;if(!h.trashedNote)throw new Error("Trashed note not found");return h.trashedNote}}),f=r.data?.contentAsMarkdown.trim()??"";return e.jsxs(y,{isOpen:!!a,onClose:o,variant:"inspect",children:[e.jsx(y.Header,{title:c,onClose:o}),e.jsx(y.Body,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[a&&e.jsxs(d,{as:"p",variant:"meta",tone:"secondary",children:["Deleted ",P(a.deletedAt)]}),r.isLoading&&e.jsx(d,{as:"p",variant:"meta",tone:"secondary",children:"Loading deleted note content..."}),r.isError&&e.jsx(d,{as:"p",variant:"meta",tone:"error",children:"Failed to load deleted note content."}),!r.isLoading&&!r.isError&&f&&e.jsx("pre",{className:"max-h-[60vh] overflow-auto whitespace-pre-wrap break-words rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3 text-sm leading-6 text-fg-secondary",children:f}),!r.isLoading&&!r.isError&&!f&&e.jsx(d,{as:"p",variant:"meta",tone:"secondary",className:"rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3",children:"No readable content was found for this deleted note."})]})}),e.jsx(y.Footer,{children:e.jsx(j,{variant:"ghost",size:"sm",onClick:o,children:"Close"})})]})},ee=()=>e.jsx(N,{title:"Trash",variant:"default",heading:e.jsx(l,{width:118,height:24,className:"rounded-full"}),description:e.jsx(l,{width:352,height:16,className:"rounded-full"}),children:e.jsx("div",{className:"grid gap-3",children:Array.from({length:3},(a,o)=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1 space-y-2",children:[e.jsx(l,{width:"40%",height:18,className:"rounded-full"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(l,{width:168,height:14,className:"rounded-full"}),e.jsx(l,{width:176,height:14,className:"rounded-full"})]}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(l,{width:56,height:24,className:"rounded-full"}),e.jsx(l,{width:72,height:24,className:"rounded-full"})]})]}),e.jsx(l,{width:92,height:32,className:"rounded-[12px]"})]})},o))})}),te=()=>{const{page:a}=v.useSearch(),o=v.useNavigate(),s=L(),c=$(),r=_(),[f,h]=k.useState(null),[x,b]=k.useState(null),{data:u}=I({queryKey:i.notes.trash({limit:m,offset:(a-1)*m}),queryFn:async()=>{const t=await K({limit:m,offset:(a-1)*m});if(t.type==="error")throw t;return t.trashedNotes}}),w=E({mutationFn:B,onSuccess:async t=>{if(t.type==="error")throw t;await Promise.all([s.invalidateQueries({queryKey:i.notes.all(),exact:!1}),s.invalidateQueries({queryKey:i.tags.all(),exact:!1}),s.invalidateQueries({queryKey:i.reminders.all(),exact:!1}),s.invalidateQueries({queryKey:i.images.all(),exact:!1}),s.invalidateQueries({queryKey:i.calendar.all(),exact:!1})]),c("The note has been restored."),o({to:O,params:{id:t.restoreTrashedNote.id}})},onError:()=>{c("Failed to restore the note.")}}),p=E({mutationFn:W,onSuccess:async t=>{if(t.type==="error")throw t;await s.invalidateQueries({queryKey:i.notes.trashAll(),exact:!1}),c("The note has been permanently deleted.")},onError:()=>{c("Failed to permanently delete the note.")}}),D=()=>{if(!x)return;const{id:t}=x;b(null),p.mutate(t)},q=async t=>{const n=await G(t);if(n.type==="error"){c("Failed to check linked notes before permanently deleting this note.");return}if(s.setQueryData(i.notes.backReferences(t),n.backReferences),n.backReferences.length>0){b({id:t,backReferences:n.backReferences});return}await r("Permanently delete this note? This cannot be undone.")&&p.mutate(t)},T=u.totalCount>0?`Trash (${u.totalCount})`:void 0,A=u.notes.find(t=>t.id===f)??null;return u.notes.length===0?e.jsx(N,{title:"Trash",variant:"default",heading:T,description:R,children:e.jsx(H,{title:"Trash is empty",description:`Delete a note and you can restore it here for ${S} days before permanent removal`})}):e.jsxs(N,{title:"Trash",variant:"default",heading:T,description:R,children:[e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("div",{className:"grid gap-3",children:u.notes.map(t=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex min-w-0 items-start gap-1.5",children:[e.jsx(d,{as:"h2",variant:"body",weight:"medium",className:"min-w-0 flex-1 break-words leading-[1.25]",children:t.title||"Untitled note"}),t.pinned&&e.jsx("span",{title:"Pinned",className:"inline-flex h-5 w-5 shrink-0 items-center justify-center text-fg-tertiary",children:e.jsx(U,{className:"h-3.5 w-3.5"})})]}),e.jsx("div",{className:"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1",children:e.jsxs(d,{as:"div",variant:"meta",weight:"medium",tone:"secondary",children:["Deleted ",P(t.deletedAt)]})}),t.tagNames.length>0&&e.jsx("div",{className:"mt-2 flex flex-wrap gap-1.5",children:t.tagNames.map(n=>e.jsx(d,{as:"span",variant:"label",weight:"medium",tone:"tertiary",className:"rounded-full border border-border-subtle bg-hover-subtle px-2 py-0.5",children:n},`${t.id}-${n}`))}),t.contentPreview&&e.jsx("div",{className:"mt-3 rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-3 py-2",children:e.jsx(d,{as:"p",variant:"meta",tone:"secondary",className:"line-clamp-3 whitespace-pre-wrap break-words",children:t.contentPreview})})]}),e.jsxs("div",{className:"flex shrink-0 flex-wrap gap-2 sm:justify-end",children:[e.jsx(j,{variant:"subtle",size:"sm",onClick:()=>h(t.id),children:"View content"}),e.jsx(j,{variant:"subtle",size:"sm",isLoading:w.isPending&&w.variables===t.id,onClick:()=>w.mutate(t.id),children:"Restore"}),e.jsx(j,{variant:"soft-danger",size:"sm",isLoading:p.isPending&&p.variables===t.id,onClick:()=>q(t.id),children:"Delete now"})]})]})},t.id))}),u.totalCount>m&&e.jsx(V,{page:a,last:Math.ceil(u.totalCount/m),onChange:t=>{o({search:n=>({...n,page:t})})}})]}),e.jsx(Z,{note:A,onClose:()=>h(null)}),e.jsx(Y,{isOpen:!!x,title:g.title,description:g.description,references:x?.backReferences??[],confirmLabel:g.confirmLabel,confirmVariant:g.confirmVariant,onClose:()=>b(null),onConfirm:D})]})},oe=()=>{const{page:a}=v.useSearch();return e.jsx(M,{fallback:e.jsx(ee,{}),errorTitle:"Failed to load trash",errorDescription:"Retry loading deleted notes",resetKeys:[a],children:e.jsx(te,{})})};export{oe as default};
|
|
Binary file
|
|
Binary file
|
package/server/dist/app.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/app.ts"],"sourcesContent":["import express from 'express';\nimport { createMcpAdminService, type McpAdminService } from './features/mcp-admin/service.js';\nimport { purgeExpiredNoteSnapshots } from './features/note/services/snapshot.js';\nimport { purgeExpiredTrashedNotes } from './features/note/services/trash.js';\nimport { createSessionMiddleware } from './modules/auth-guard.js';\nimport type { AuthConfig } from './modules/auth-mode.js';\nimport { createErrorHandler } from './modules/error-handler.js';\nimport logger from './modules/logger.js';\nimport { createApiRouter, createAuthPagesRouter, createClientRouter, createGraphqlRouter } from './routes/index.js';\n\nexport const createApp = (authConfig: AuthConfig) => {\n const mcpAdminService = createMcpAdminService();\n return createAppWithMcpAuth(authConfig, mcpAdminService);\n};\n\nexport const createAppWithMcpAuth = (authConfig: AuthConfig, mcpAdminService: McpAdminService) => {\n const app = express();\n app.locals.authConfig = authConfig;\n\n void Promise.all([purgeExpiredNoteSnapshots(), purgeExpiredTrashedNotes()]).catch((error) => {\n const message = error instanceof Error ? error.message : 'Unknown recovery cleanup error';\n process.stderr.write(`[recovery] Startup cleanup failed: ${message}\\n`);\n });\n\n app.use(logger)\n .use(createSessionMiddleware(authConfig))\n .use(express.urlencoded({ extended: false }))\n .use(express.json({ limit: '50mb' }))\n .use('/api', createApiRouter(authConfig, mcpAdminService))\n .use(createAuthPagesRouter(authConfig))\n .use('/graphql', createGraphqlRouter(authConfig, mcpAdminService))\n .use(createClientRouter(authConfig))\n .use(createErrorHandler());\n\n return app;\n};\n\nexport default createApp;\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,6BAAmD;AAC5D,SAAS,iCAAiC;AAC1C,SAAS,gCAAgC;AACzC,SAAS,+BAA+B;AAExC,SAAS,0BAA0B;AACnC,OAAO,YAAY;AACnB,SAAS,iBAAiB,uBAAuB,oBAAoB,2BAA2B;AAEzF,MAAM,YAAY,CAAC,eAA2B;AACjD,QAAM,kBAAkB,sBAAsB;AAC9C,SAAO,qBAAqB,YAAY,eAAe;AAC3D;AAEO,MAAM,uBAAuB,CAAC,YAAwB,oBAAqC;AAC9F,QAAM,MAAM,QAAQ;AACpB,MAAI,OAAO,aAAa;AAExB,OAAK,QAAQ,IAAI,CAAC,0BAA0B,GAAG,yBAAyB,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU;AACzF,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,YAAQ,OAAO,MAAM,sCAAsC,OAAO;AAAA,CAAI;AAAA,EAC1E,CAAC;AAED,MAAI,IAAI,MAAM,EACT,IAAI,wBAAwB,UAAU,CAAC,EACvC,IAAI,QAAQ,WAAW,EAAE,UAAU,MAAM,CAAC,CAAC,EAC3C,IAAI,QAAQ,KAAK,EAAE,OAAO,OAAO,CAAC,CAAC,EACnC,IAAI,QAAQ,gBAAgB,YAAY,eAAe,CAAC,EACxD,IAAI,sBAAsB,UAAU,CAAC,EACrC,IAAI,YAAY,oBAAoB,YAAY,eAAe,CAAC,EAChE,IAAI,mBAAmB,UAAU,CAAC,EAClC,IAAI,mBAAmB,CAAC;AAE7B,SAAO;AACX;AAEA,IAAO,cAAQ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/auth/http/api.ts"],"sourcesContent":["import { buildAuthSessionResponse } from '@baejino/auth';\nimport type { AuthConfig } from '~/modules/auth-mode.js';\nimport { createAppError } from '~/modules/error-handler.js';\nimport type { Controller } from '~/types/index.js';\nimport {\n assertPasswordLoginAvailable,\n buildSessionResponse,\n compareSharedSecret,\n destroySession,\n refreshCsrfToken,\n regenerateSession,\n setSessionStatusHeaders,\n} from '../service.js';\n\nexport const createLoginHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n const expectedPassword = assertPasswordLoginAvailable(authConfig);\n const password = typeof req.body?.password === 'string' ? req.body.password : '';\n\n if (!password || !compareSharedSecret(expectedPassword, password)) {\n throw createAppError(401, 'UNAUTHORIZED', 'Invalid password');\n }\n\n await regenerateSession(req);\n req.session.authenticated = true;\n refreshCsrfToken(req);\n\n res.status(200).json(buildSessionResponse(authConfig, req)).end();\n };\n};\n\nexport const createLogoutHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n if (authConfig.mode === 'password') {\n await destroySession(req);\n }\n\n res.status(200).json(buildAuthSessionResponse(authConfig, false)).end();\n };\n};\n\nexport const createSessionStatusHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n setSessionStatusHeaders(res);\n res.status(200).json(buildSessionResponse(authConfig, req)).end();\n };\n};\n"],"mappings":"AAAA,SAAS,gCAAgC;AAEzC,SAAS,sBAAsB;AAE/B;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AAEA,MAAM,qBAAqB,CAAC,eAAuC;AACtE,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,mBAAmB,6BAA6B,UAAU;AAChE,UAAM,WAAW,OAAO,IAAI,MAAM,aAAa,WAAW,IAAI,KAAK,WAAW;AAE9E,QAAI,CAAC,YAAY,CAAC,oBAAoB,kBAAkB,QAAQ,GAAG;AAC/D,YAAM,eAAe,KAAK,gBAAgB,kBAAkB;AAAA,IAChE;AAEA,UAAM,kBAAkB,GAAG;AAC3B,QAAI,QAAQ,gBAAgB;AAC5B,qBAAiB,GAAG;AAEpB,QAAI,OAAO,GAAG,EAAE,KAAK,qBAAqB,YAAY,GAAG,CAAC,EAAE,IAAI;AAAA,EACpE;AACJ;AAEO,MAAM,sBAAsB,CAAC,eAAuC;AACvE,SAAO,OAAO,KAAK,QAAQ;AACvB,QAAI,WAAW,SAAS,YAAY;AAChC,YAAM,eAAe,GAAG;AAAA,IAC5B;AAEA,QAAI,OAAO,GAAG,EAAE,KAAK,yBAAyB,YAAY,KAAK,CAAC,EAAE,IAAI;AAAA,EAC1E;AACJ;AAEO,MAAM,6BAA6B,CAAC,eAAuC;AAC9E,SAAO,OAAO,KAAK,QAAQ;AACvB,4BAAwB,GAAG;AAC3B,QAAI,OAAO,GAAG,EAAE,KAAK,qBAAqB,YAAY,GAAG,CAAC,EAAE,IAAI;AAAA,EACpE;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/auth/http/login-page.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\n\ndeclare const __LOGIN_PAGE_TEMPLATE__: string | undefined;\n\nconst LOGIN_ERROR_TOKEN = '<!-- OCEAN_BRAIN_LOGIN_ERROR -->';\nconst NEXT_PATH_TOKEN = 'OCEAN_BRAIN_NEXT_PATH';\n\nconst readLoginPageTemplate = () => {\n if (typeof __LOGIN_PAGE_TEMPLATE__ === 'string') {\n return __LOGIN_PAGE_TEMPLATE__;\n }\n\n return readFileSync(new URL('./login-page.html', import.meta.url), 'utf8');\n};\n\nconst LOGIN_PAGE_TEMPLATE = readLoginPageTemplate();\n\nconst escapeHtml = (value: string) =>\n value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n\ninterface LoginPageParams {\n nextPath: string;\n errorMessage?: string;\n csrfToken?: string;\n}\n\nconst renderLoginError = (errorMessage?: string) => {\n if (!errorMessage) {\n return '';\n }\n\n return `<div class=\"error\" role=\"alert\">${escapeHtml(errorMessage)}</div>`;\n};\n\nconst renderCsrfInput = (csrfToken?: string) => {\n if (!csrfToken) {\n return '';\n }\n\n return `<input type=\"hidden\" name=\"_csrf\" value=\"${escapeHtml(csrfToken)}\" />`;\n};\n\nconst renderLoginTemplate = (values: { errorBlock: string; nextPath: string; csrfInput: string }) => {\n return LOGIN_PAGE_TEMPLATE.replace(LOGIN_ERROR_TOKEN, values.errorBlock)\n .replace(NEXT_PATH_TOKEN, values.nextPath)\n .replace('<!-- OCEAN_BRAIN_CSRF_INPUT -->', values.csrfInput);\n};\n\nexport const renderLoginPage = ({ nextPath, errorMessage, csrfToken }: LoginPageParams) =>\n renderLoginTemplate({\n errorBlock: renderLoginError(errorMessage),\n nextPath: escapeHtml(nextPath),\n csrfInput: renderCsrfInput(csrfToken),\n });\n"],"mappings":"AAAA,SAAS,oBAAoB;AAI7B,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;AAExB,MAAM,wBAAwB,MAAM;AAChC,MAAI,MAA6C;AAC7C,WAAO;AAAA,EACX;AAEA,SAAO,aAAa,IAAI,IAAI,qBAAqB,YAAY,GAAG,GAAG,MAAM;AAC7E;AAEA,MAAM,sBAAsB,sBAAsB;AAElD,MAAM,aAAa,CAAC,UAChB,MACK,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,OAAO;AAQhC,MAAM,mBAAmB,CAAC,iBAA0B;AAChD,MAAI,CAAC,cAAc;AACf,WAAO;AAAA,EACX;AAEA,SAAO,mCAAmC,WAAW,YAAY,CAAC;AACtE;AAEA,MAAM,kBAAkB,CAAC,cAAuB;AAC5C,MAAI,CAAC,WAAW;AACZ,WAAO;AAAA,EACX;AAEA,SAAO,4CAA4C,WAAW,SAAS,CAAC;AAC5E;AAEA,MAAM,sBAAsB,CAAC,WAAwE;AACjG,SAAO,oBAAoB,QAAQ,mBAAmB,OAAO,UAAU,EAClE,QAAQ,iBAAiB,OAAO,QAAQ,EACxC,QAAQ,mCAAmC,OAAO,SAAS;AACpE;AAEO,MAAM,kBAAkB,CAAC,EAAE,UAAU,cAAc,UAAU,MAChE,oBAAoB;AAAA,EAChB,YAAY,iBAAiB,YAAY;AAAA,EACzC,UAAU,WAAW,QAAQ;AAAA,EAC7B,WAAW,gBAAgB,SAAS;AACxC,CAAC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/auth/http/pages.ts"],"sourcesContent":["import type { AuthConfig } from '~/modules/auth-mode.js';\nimport type { Controller } from '~/types/index.js';\nimport {\n compareSharedSecret,\n destroySession,\n refreshCsrfToken,\n regenerateSession,\n sanitizeRedirectPath,\n} from '../service.js';\nimport { renderLoginPage } from './login-page.js';\n\nexport const createLoginPageHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n if (authConfig.mode !== 'password' || req.session?.authenticated) {\n res.redirect(303, sanitizeRedirectPath(req.query.next));\n return;\n }\n\n const nextPath = sanitizeRedirectPath(req.query.next);\n res.status(200)\n .type('html')\n .send(renderLoginPage({ nextPath, csrfToken: res.locals._csrf }))\n .end();\n };\n};\n\nexport const createLoginPageSubmitHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n const nextPath = sanitizeRedirectPath(req.body?.next);\n\n if (authConfig.mode !== 'password' || !authConfig.password) {\n res.redirect(303, nextPath);\n return;\n }\n\n const password = typeof req.body?.password === 'string' ? req.body.password : '';\n\n if (!password || !compareSharedSecret(authConfig.password, password)) {\n res.status(401)\n .type('html')\n .send(\n renderLoginPage({\n nextPath,\n errorMessage: 'Invalid password',\n csrfToken: res.locals._csrf,\n }),\n )\n .end();\n return;\n }\n\n await regenerateSession(req);\n req.session.authenticated = true;\n refreshCsrfToken(req);\n\n res.redirect(303, nextPath);\n };\n};\n\nexport const createLogoutPageHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n if (authConfig.mode === 'password') {\n await destroySession(req);\n res.redirect(303, '/login');\n return;\n }\n\n res.redirect(303, '/');\n };\n};\n"],"mappings":"AAEA;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,uBAAuB;AAEzB,MAAM,yBAAyB,CAAC,eAAuC;AAC1E,SAAO,OAAO,KAAK,QAAQ;AACvB,QAAI,WAAW,SAAS,cAAc,IAAI,SAAS,eAAe;AAC9D,UAAI,SAAS,KAAK,qBAAqB,IAAI,MAAM,IAAI,CAAC;AACtD;AAAA,IACJ;AAEA,UAAM,WAAW,qBAAqB,IAAI,MAAM,IAAI;AACpD,QAAI,OAAO,GAAG,EACT,KAAK,MAAM,EACX,KAAK,gBAAgB,EAAE,UAAU,WAAW,IAAI,OAAO,MAAM,CAAC,CAAC,EAC/D,IAAI;AAAA,EACb;AACJ;AAEO,MAAM,+BAA+B,CAAC,eAAuC;AAChF,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,WAAW,qBAAqB,IAAI,MAAM,IAAI;AAEpD,QAAI,WAAW,SAAS,cAAc,CAAC,WAAW,UAAU;AACxD,UAAI,SAAS,KAAK,QAAQ;AAC1B;AAAA,IACJ;AAEA,UAAM,WAAW,OAAO,IAAI,MAAM,aAAa,WAAW,IAAI,KAAK,WAAW;AAE9E,QAAI,CAAC,YAAY,CAAC,oBAAoB,WAAW,UAAU,QAAQ,GAAG;AAClE,UAAI,OAAO,GAAG,EACT,KAAK,MAAM,EACX;AAAA,QACG,gBAAgB;AAAA,UACZ;AAAA,UACA,cAAc;AAAA,UACd,WAAW,IAAI,OAAO;AAAA,QAC1B,CAAC;AAAA,MACL,EACC,IAAI;AACT;AAAA,IACJ;AAEA,UAAM,kBAAkB,GAAG;AAC3B,QAAI,QAAQ,gBAAgB;AAC5B,qBAAiB,GAAG;AAEpB,QAAI,SAAS,KAAK,QAAQ;AAAA,EAC9B;AACJ;AAEO,MAAM,0BAA0B,CAAC,eAAuC;AAC3E,SAAO,OAAO,KAAK,QAAQ;AACvB,QAAI,WAAW,SAAS,YAAY;AAChC,YAAM,eAAe,GAAG;AACxB,UAAI,SAAS,KAAK,QAAQ;AAC1B;AAAA,IACJ;AAEA,QAAI,SAAS,KAAK,GAAG;AAAA,EACzB;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/auth/service.ts"],"sourcesContent":["import { buildAuthSessionResponse } from '@baejino/auth';\nimport { compareSharedSecret as compareCommonSharedSecret } from '@baejino/auth/crypto';\nimport crypto from 'crypto';\nimport type { Request, Response } from 'express';\nimport type { AuthConfig } from '~/modules/auth-mode.js';\nimport { sanitizeRedirectPath } from '~/modules/auth-redirect.js';\nimport { createAppError } from '~/modules/error-handler.js';\n\nexport const AUTH_SESSION_GENERATION_HEADER = 'X-Ocean-Brain-Session-Generation';\nconst AUTH_SESSION_GENERATION = crypto.randomUUID();\n\nexport const createPasswordHash = async (password: string) => {\n const salt = crypto.randomBytes(16).toString('hex');\n const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return `${salt}:${hash}`;\n};\n\nexport const comparePassword = async (password: string, storedHash: string) => {\n const [salt, hash] = storedHash.split(':');\n const newHash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return hash === newHash;\n};\n\nexport const compareSharedSecret = compareCommonSharedSecret;\n\nexport const buildSessionResponse = (authConfig: AuthConfig, req: Request) =>\n buildAuthSessionResponse(authConfig, Boolean(req.session?.authenticated));\n\nexport const setSessionStatusHeaders = (res: Response) => {\n res.set(AUTH_SESSION_GENERATION_HEADER, AUTH_SESSION_GENERATION);\n res.set('Cache-Control', 'no-store');\n};\n\nexport const refreshCsrfToken = (req: Request) => {\n const requestWithCsrf = req as Request & { csrfToken?: () => string };\n requestWithCsrf.csrfToken?.();\n};\n\nexport const assertPasswordLoginAvailable = (authConfig: AuthConfig) => {\n if (authConfig.mode !== 'password') {\n throw createAppError(409, 'AUTH_OPEN_MODE', 'Login is unavailable while auth mode is open.');\n }\n\n return authConfig.password;\n};\n\nexport const regenerateSession = async (req: Request) => {\n await new Promise<void>((resolve, reject) => {\n req.session.regenerate((error) => {\n if (error) {\n reject(error);\n return;\n }\n\n resolve();\n });\n });\n};\n\nexport const destroySession = async (req: Request) => {\n if (!req.session) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n req.session.destroy((error) => {\n if (error) {\n reject(error);\n return;\n }\n\n resolve();\n });\n });\n};\n\nexport { sanitizeRedirectPath };\n"],"mappings":"AAAA,SAAS,gCAAgC;AACzC,SAAS,uBAAuB,iCAAiC;AACjE,OAAO,YAAY;AAGnB,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAExB,MAAM,iCAAiC;AAC9C,MAAM,0BAA0B,OAAO,WAAW;AAE3C,MAAM,qBAAqB,OAAO,aAAqB;AAC1D,QAAM,OAAO,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAClD,QAAM,OAAO,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACjF,SAAO,GAAG,IAAI,IAAI,IAAI;AAC1B;AAEO,MAAM,kBAAkB,OAAO,UAAkB,eAAuB;AAC3E,QAAM,CAAC,MAAM,IAAI,IAAI,WAAW,MAAM,GAAG;AACzC,QAAM,UAAU,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACpF,SAAO,SAAS;AACpB;AAEO,MAAM,sBAAsB;AAE5B,MAAM,uBAAuB,CAAC,YAAwB,QACzD,yBAAyB,YAAY,QAAQ,IAAI,SAAS,aAAa,CAAC;AAErE,MAAM,0BAA0B,CAAC,QAAkB;AACtD,MAAI,IAAI,gCAAgC,uBAAuB;AAC/D,MAAI,IAAI,iBAAiB,UAAU;AACvC;AAEO,MAAM,mBAAmB,CAAC,QAAiB;AAC9C,QAAM,kBAAkB;AACxB,kBAAgB,YAAY;AAChC;AAEO,MAAM,+BAA+B,CAAC,eAA2B;AACpE,MAAI,WAAW,SAAS,YAAY;AAChC,UAAM,eAAe,KAAK,kBAAkB,+CAA+C;AAAA,EAC/F;AAEA,SAAO,WAAW;AACtB;AAEO,MAAM,oBAAoB,OAAO,QAAiB;AACrD,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AACzC,QAAI,QAAQ,WAAW,CAAC,UAAU;AAC9B,UAAI,OAAO;AACP,eAAO,KAAK;AACZ;AAAA,MACJ;AAEA,cAAQ;AAAA,IACZ,CAAC;AAAA,EACL,CAAC;AACL;AAEO,MAAM,iBAAiB,OAAO,QAAiB;AAClD,MAAI,CAAC,IAAI,SAAS;AACd;AAAA,EACJ;AAEA,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AACzC,QAAI,QAAQ,QAAQ,CAAC,UAAU;AAC3B,UAAI,OAAO;AACP,eAAO,KAAK;AACZ;AAAA,MACJ;AAEA,cAAQ;AAAA,IACZ,CAAC;AAAA,EACL,CAAC;AACL;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/cache/graphql/cache.field.resolver.ts"],"sourcesContent":["export const cacheFieldResolvers = {};\n"],"mappings":"AAAO,MAAM,sBAAsB,CAAC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/cache/graphql/cache.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport models, { type Cache } from '~/models.js';\n\ntype CacheRecord = Pick<Cache, 'id' | 'key' | 'value'>;\ntype CacheMutationInput = Pick<Cache, 'key' | 'value'>;\n\ntype CacheMutationService = {\n findCacheByKey: (key: string) => Promise<CacheRecord | null>;\n createCache: (input: CacheMutationInput) => Promise<CacheRecord>;\n updateCache: (input: CacheMutationInput) => Promise<CacheRecord>;\n deleteCache: (key: string) => Promise<void>;\n};\n\nconst createCacheMutationService = (): CacheMutationService => ({\n findCacheByKey: (key) => models.cache.findFirst({ where: { key } }),\n createCache: ({ key, value }) =>\n models.cache.create({\n data: {\n key,\n value,\n },\n }),\n updateCache: ({ key, value }) =>\n models.cache.update({\n where: { key },\n data: { value },\n }),\n deleteCache: async (key) => {\n await models.cache.delete({ where: { key } });\n },\n});\n\nexport const createSetCacheMutationResolver = (service: CacheMutationService = createCacheMutationService()) => {\n return async (_: unknown, { key, value }: CacheMutationInput) => {\n const decodedValue = decodeURIComponent(value);\n const existingCache = await service.findCacheByKey(key);\n\n if (existingCache) {\n return service.updateCache({\n key,\n value: decodedValue,\n });\n }\n\n return service.createCache({\n key,\n value: decodedValue,\n });\n };\n};\n\nexport const createDeleteCacheMutationResolver = (service: CacheMutationService = createCacheMutationService()) => {\n return async (_: unknown, { key }: Pick<Cache, 'key'>) => {\n await service.deleteCache(key);\n return true;\n };\n};\n\nexport const cacheMutationResolvers: NonNullable<IResolvers['Mutation']> = {\n setCache: createSetCacheMutationResolver(),\n deleteCache: createDeleteCacheMutationResolver(),\n};\n"],"mappings":"AAEA,OAAO,YAA4B;AAYnC,MAAM,6BAA6B,OAA6B;AAAA,EAC5D,gBAAgB,CAAC,QAAQ,OAAO,MAAM,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA,EAClE,aAAa,CAAC,EAAE,KAAK,MAAM,MACvB,OAAO,MAAM,OAAO;AAAA,IAChB,MAAM;AAAA,MACF;AAAA,MACA;AAAA,IACJ;AAAA,EACJ,CAAC;AAAA,EACL,aAAa,CAAC,EAAE,KAAK,MAAM,MACvB,OAAO,MAAM,OAAO;AAAA,IAChB,OAAO,EAAE,IAAI;AAAA,IACb,MAAM,EAAE,MAAM;AAAA,EAClB,CAAC;AAAA,EACL,aAAa,OAAO,QAAQ;AACxB,UAAM,OAAO,MAAM,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA,EAChD;AACJ;AAEO,MAAM,iCAAiC,CAAC,UAAgC,2BAA2B,MAAM;AAC5G,SAAO,OAAO,GAAY,EAAE,KAAK,MAAM,MAA0B;AAC7D,UAAM,eAAe,mBAAmB,KAAK;AAC7C,UAAM,gBAAgB,MAAM,QAAQ,eAAe,GAAG;AAEtD,QAAI,eAAe;AACf,aAAO,QAAQ,YAAY;AAAA,QACvB;AAAA,QACA,OAAO;AAAA,MACX,CAAC;AAAA,IACL;AAEA,WAAO,QAAQ,YAAY;AAAA,MACvB;AAAA,MACA,OAAO;AAAA,IACX,CAAC;AAAA,EACL;AACJ;AAEO,MAAM,oCAAoC,CAAC,UAAgC,2BAA2B,MAAM;AAC/G,SAAO,OAAO,GAAY,EAAE,IAAI,MAA0B;AACtD,UAAM,QAAQ,YAAY,GAAG;AAC7B,WAAO;AAAA,EACX;AACJ;AAEO,MAAM,yBAA8D;AAAA,EACvE,UAAU,+BAA+B;AAAA,EACzC,aAAa,kCAAkC;AACnD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/cache/graphql/cache.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport models, { type Cache } from '~/models.js';\n\ntype CacheRecord = Pick<Cache, 'id' | 'key' | 'value'>;\n\ntype CacheQueryService = {\n findCaches: () => Promise<CacheRecord[]>;\n findCacheByKey: (key: string) => Promise<CacheRecord | null>;\n};\n\nconst createCacheQueryService = (): CacheQueryService => ({\n findCaches: () => models.cache.findMany(),\n findCacheByKey: (key) => models.cache.findUnique({ where: { key } }),\n});\n\nexport const createAllCachesQueryResolver = (service: CacheQueryService = createCacheQueryService()) => {\n return async () => service.findCaches();\n};\n\nexport const createCacheQueryResolver = (service: CacheQueryService = createCacheQueryService()) => {\n return async (_: unknown, { key }: { key: string }) => service.findCacheByKey(key);\n};\n\nexport const cacheQueryResolvers: NonNullable<IResolvers['Query']> = {\n allCaches: createAllCachesQueryResolver(),\n cache: createCacheQueryResolver(),\n};\n"],"mappings":"AAEA,OAAO,YAA4B;AASnC,MAAM,0BAA0B,OAA0B;AAAA,EACtD,YAAY,MAAM,OAAO,MAAM,SAAS;AAAA,EACxC,gBAAgB,CAAC,QAAQ,OAAO,MAAM,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE;AAEO,MAAM,+BAA+B,CAAC,UAA6B,wBAAwB,MAAM;AACpG,SAAO,YAAY,QAAQ,WAAW;AAC1C;AAEO,MAAM,2BAA2B,CAAC,UAA6B,wBAAwB,MAAM;AAChG,SAAO,OAAO,GAAY,EAAE,IAAI,MAAuB,QAAQ,eAAe,GAAG;AACrF;AAEO,MAAM,sBAAwD;AAAA,EACjE,WAAW,6BAA6B;AAAA,EACxC,OAAO,yBAAyB;AACpC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/cache/graphql/cache.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport { cacheFieldResolvers } from './cache.field.resolver.js';\nimport { cacheMutationResolvers } from './cache.mutation.resolver.js';\nimport { cacheQueryResolvers } from './cache.query.resolver.js';\n\nexport const cacheResolvers: IResolvers = {\n Cache: cacheFieldResolvers,\n Query: cacheQueryResolvers,\n Mutation: cacheMutationResolvers,\n};\n"],"mappings":"AAEA,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AAE7B,MAAM,iBAA6B;AAAA,EACtC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AACd;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/cache/graphql/cache.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const cacheType = gql`\n type Cache {\n id: ID!\n key: String!\n value: String!\n }\n`;\n\nexport const cacheQuery = gql`\n type Query {\n allCaches: [Cache!]!\n cache(key: String!): Cache!\n }\n`;\n\nexport const cacheMutation = gql`\n type Mutation {\n setCache(key: String, value: String): Cache!\n deleteCache(key: String!): Boolean!\n }\n`;\n\nexport const cacheTypeDefs = `\n ${cacheType}\n ${cacheQuery}\n ${cacheMutation}\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQlB,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAOnB,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOtB,MAAM,gBAAgB;AAAA,MACvB,SAAS;AAAA,MACT,UAAU;AAAA,MACV,aAAa;AAAA;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/cache/graphql/index.ts"],"sourcesContent":["export { cacheFieldResolvers } from './cache.field.resolver.js';\nexport {\n cacheMutationResolvers,\n createDeleteCacheMutationResolver,\n createSetCacheMutationResolver,\n} from './cache.mutation.resolver.js';\nexport { cacheQueryResolvers, createAllCachesQueryResolver, createCacheQueryResolver } from './cache.query.resolver.js';\nexport { cacheResolvers } from './cache.resolver.js';\nexport { cacheTypeDefs } from './cache.type-defs.js';\n"],"mappings":"AAAA,SAAS,2BAA2B;AACpC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,qBAAqB,8BAA8B,gCAAgC;AAC5F,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/graphql/image.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport models, { type Image } from '~/models.js';\n\ntype ImageFieldResolvers = NonNullable<IResolvers['Image']>;\n\ntype CountImageReferences = (url: string) => Promise<number>;\n\nexport const createImageReferenceCountFieldResolver = (\n countReferences: CountImageReferences = async (url) => models.note.count({ where: { content: { contains: url } } }),\n) => {\n return async (image: Image) => {\n return countReferences(image.url);\n };\n};\n\nexport const imageFieldResolvers: ImageFieldResolvers = {\n referenceCount: createImageReferenceCountFieldResolver(),\n};\n"],"mappings":"AACA,OAAO,YAA4B;AAM5B,MAAM,yCAAyC,CAClD,kBAAwC,OAAO,QAAQ,OAAO,KAAK,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,IAAI,EAAE,EAAE,CAAC,MACjH;AACD,SAAO,OAAO,UAAiB;AAC3B,WAAO,gBAAgB,MAAM,GAAG;AAAA,EACpC;AACJ;AAEO,MAAM,sBAA2C;AAAA,EACpD,gBAAgB,uCAAuC;AAC3D;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/graphql/image.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { deleteImageById } from '../services/delete.js';\n\ntype ImageMutationResolvers = NonNullable<IResolvers['Mutation']>;\n\ntype DeleteImage = (id: number) => Promise<boolean>;\n\nexport const createDeleteImageMutationResolver = (deleteImage: DeleteImage = deleteImageById) => {\n return async (_: unknown, { id }: { id: string | number }) => {\n try {\n return await deleteImage(Number(id));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[image] Delete failed: ${message}\\n`);\n return false;\n }\n };\n};\n\nexport const imageMutationResolvers: ImageMutationResolvers = {\n deleteImage: createDeleteImageMutationResolver(),\n};\n"],"mappings":"AACA,SAAS,uBAAuB;AAMzB,MAAM,oCAAoC,CAAC,cAA2B,oBAAoB;AAC7F,SAAO,OAAO,GAAY,EAAE,GAAG,MAA+B;AAC1D,QAAI;AACA,aAAO,MAAM,YAAY,OAAO,EAAE,CAAC;AAAA,IACvC,SAAS,OAAO;AACZ,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,cAAQ,OAAO,MAAM,0BAA0B,OAAO;AAAA,CAAI;AAC1D,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAEO,MAAM,yBAAiD;AAAA,EAC1D,aAAa,kCAAkC;AACnD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/graphql/image.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport models from '~/models.js';\nimport type { Pagination } from '~/types/index.js';\n\ntype ImageQueryResolvers = NonNullable<IResolvers['Query']>;\n\ninterface ImageQueryDeps {\n countImages: () => Promise<number>;\n findImageById: (id: number) => Promise<unknown>;\n findImages: (input: { orderBy: { createdAt: 'desc' }; skip: number; take: number }) => Promise<unknown[]>;\n}\n\nexport const createAllImagesQueryResolver = (\n deps: ImageQueryDeps = {\n countImages: async () => models.image.count(),\n findImageById: async (id) => models.image.findFirst({ where: { id } }),\n findImages: async (input) => models.image.findMany(input),\n },\n) => {\n return async (\n _: unknown,\n {\n pagination,\n }: {\n pagination: Pagination;\n },\n ) => {\n const [totalCount, images] = await Promise.all([\n deps.countImages(),\n deps.findImages({\n skip: pagination.offset,\n take: pagination.limit,\n orderBy: { createdAt: 'desc' },\n }),\n ]);\n\n return {\n totalCount,\n images,\n };\n };\n};\n\nexport const createImageQueryResolver = (\n deps: Pick<ImageQueryDeps, 'findImageById'> = {\n findImageById: async (id) => models.image.findFirst({ where: { id } }),\n },\n) => {\n return async (_: unknown, { id }: { id: string | number }) => {\n return deps.findImageById(Number(id));\n };\n};\n\nexport const imageQueryResolvers: ImageQueryResolvers = {\n allImages: createAllImagesQueryResolver(),\n image: createImageQueryResolver(),\n};\n"],"mappings":"AACA,OAAO,YAAY;AAWZ,MAAM,+BAA+B,CACxC,OAAuB;AAAA,EACnB,aAAa,YAAY,OAAO,MAAM,MAAM;AAAA,EAC5C,eAAe,OAAO,OAAO,OAAO,MAAM,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAAA,EACrE,YAAY,OAAO,UAAU,OAAO,MAAM,SAAS,KAAK;AAC5D,MACC;AACD,SAAO,OACH,GACA;AAAA,IACI;AAAA,EACJ,MAGC;AACD,UAAM,CAAC,YAAY,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3C,KAAK,YAAY;AAAA,MACjB,KAAK,WAAW;AAAA,QACZ,MAAM,WAAW;AAAA,QACjB,MAAM,WAAW;AAAA,QACjB,SAAS,EAAE,WAAW,OAAO;AAAA,MACjC,CAAC;AAAA,IACL,CAAC;AAED,WAAO;AAAA,MACH;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AACJ;AAEO,MAAM,2BAA2B,CACpC,OAA8C;AAAA,EAC1C,eAAe,OAAO,OAAO,OAAO,MAAM,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AACzE,MACC;AACD,SAAO,OAAO,GAAY,EAAE,GAAG,MAA+B;AAC1D,WAAO,KAAK,cAAc,OAAO,EAAE,CAAC;AAAA,EACxC;AACJ;AAEO,MAAM,sBAA2C;AAAA,EACpD,WAAW,6BAA6B;AAAA,EACxC,OAAO,yBAAyB;AACpC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/graphql/image.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { imageFieldResolvers } from './image.field.resolver.js';\nimport { imageMutationResolvers } from './image.mutation.resolver.js';\nimport { imageQueryResolvers } from './image.query.resolver.js';\n\nexport const imageResolvers: IResolvers = {\n Query: imageQueryResolvers,\n Mutation: imageMutationResolvers,\n Image: imageFieldResolvers,\n};\n"],"mappings":"AACA,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AAE7B,MAAM,iBAA6B;AAAA,EACtC,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AACX;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/graphql/image.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const imageTypeDefs = gql`\n input PaginationInput {\n limit: Int!\n offset: Int!\n }\n\n type Image {\n id: ID!\n url: String!\n referenceCount: Int!\n }\n\n type Images {\n totalCount: Int!\n images: [Image!]!\n }\n\n type Query {\n allImages(pagination: PaginationInput): Images!\n image(id: ID!): Image!\n }\n\n type Mutation {\n deleteImage(id: ID!): Boolean!\n }\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/graphql/index.ts"],"sourcesContent":["export { imageFieldResolvers } from './image.field.resolver.js';\nexport { createDeleteImageMutationResolver, imageMutationResolvers } from './image.mutation.resolver.js';\nexport { createAllImagesQueryResolver, createImageQueryResolver, imageQueryResolvers } from './image.query.resolver.js';\nexport { imageResolvers } from './image.resolver.js';\nexport { imageTypeDefs } from './image.type-defs.js';\n"],"mappings":"AAAA,SAAS,2BAA2B;AACpC,SAAS,mCAAmC,8BAA8B;AAC1E,SAAS,8BAA8B,0BAA0B,2BAA2B;AAC5F,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/http/upload.ts"],"sourcesContent":["import { createAppError } from '~/modules/error-handler.js';\nimport type { Controller } from '~/types/index.js';\nimport { fetchRemoteImage, type RemoteImage, RemoteImageFetchError } from '../services/remote-fetch.js';\nimport { type PersistedImageSummary, type PersistImageInput, persistUploadedImage } from '../services/upload.js';\n\ntype PersistImage = (input: PersistImageInput) => Promise<PersistedImageSummary>;\ntype FetchImage = (src: string) => Promise<RemoteImage>;\n\nconst createUploadResponse = (image: PersistedImageSummary) => {\n return {\n id: image.id,\n url: image.url,\n };\n};\n\nexport const createUploadImageHandler = (persistImage: PersistImage = persistUploadedImage): Controller => {\n return async (req, res) => {\n const { image } = req.body ?? {};\n\n if (typeof image !== 'string' || !image.match(/data:image\\/\\s*(\\w+);base64,/)) {\n throw createAppError(400, 'INVALID_IMAGE_UPLOAD', 'No image uploaded');\n }\n\n const [info, data] = image.split(',');\n const buffer = Buffer.from(data, 'base64');\n const extension = info.split(';')[0]?.split('/')[1] ?? '';\n const uploadedImage = await persistImage({\n buffer,\n extension,\n });\n\n res.status(200).json(createUploadResponse(uploadedImage)).end();\n };\n};\n\nexport const createUploadImageFromSrcHandler = (\n fetchImage: FetchImage = fetchRemoteImage,\n persistImage: PersistImage = persistUploadedImage,\n): Controller => {\n return async (req, res) => {\n const src = String(req.body?.src ?? '');\n\n try {\n const remoteImage = await fetchImage(src);\n const uploadedImage = await persistImage({\n buffer: remoteImage.buffer,\n extension: remoteImage.extension,\n });\n\n res.status(200).json(createUploadResponse(uploadedImage)).end();\n } catch (error) {\n if (error instanceof RemoteImageFetchError) {\n throw createAppError(error.status, error.code, error.message);\n }\n\n throw error;\n }\n };\n};\n"],"mappings":"AAAA,SAAS,sBAAsB;AAE/B,SAAS,kBAAoC,6BAA6B;AAC1E,SAA6D,4BAA4B;AAKzF,MAAM,uBAAuB,CAAC,UAAiC;AAC3D,SAAO;AAAA,IACH,IAAI,MAAM;AAAA,IACV,KAAK,MAAM;AAAA,EACf;AACJ;AAEO,MAAM,2BAA2B,CAAC,eAA6B,yBAAqC;AACvG,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,EAAE,MAAM,IAAI,IAAI,QAAQ,CAAC;AAE/B,QAAI,OAAO,UAAU,YAAY,CAAC,MAAM,MAAM,8BAA8B,GAAG;AAC3E,YAAM,eAAe,KAAK,wBAAwB,mBAAmB;AAAA,IACzE;AAEA,UAAM,CAAC,MAAM,IAAI,IAAI,MAAM,MAAM,GAAG;AACpC,UAAM,SAAS,OAAO,KAAK,MAAM,QAAQ;AACzC,UAAM,YAAY,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK;AACvD,UAAM,gBAAgB,MAAM,aAAa;AAAA,MACrC;AAAA,MACA;AAAA,IACJ,CAAC;AAED,QAAI,OAAO,GAAG,EAAE,KAAK,qBAAqB,aAAa,CAAC,EAAE,IAAI;AAAA,EAClE;AACJ;AAEO,MAAM,kCAAkC,CAC3C,aAAyB,kBACzB,eAA6B,yBAChB;AACb,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,MAAM,OAAO,IAAI,MAAM,OAAO,EAAE;AAEtC,QAAI;AACA,YAAM,cAAc,MAAM,WAAW,GAAG;AACxC,YAAM,gBAAgB,MAAM,aAAa;AAAA,QACrC,QAAQ,YAAY;AAAA,QACpB,WAAW,YAAY;AAAA,MAC3B,CAAC;AAED,UAAI,OAAO,GAAG,EAAE,KAAK,qBAAqB,aAAa,CAAC,EAAE,IAAI;AAAA,IAClE,SAAS,OAAO;AACZ,UAAI,iBAAiB,uBAAuB;AACxC,cAAM,eAAe,MAAM,QAAQ,MAAM,MAAM,MAAM,OAAO;AAAA,MAChE;AAEA,YAAM;AAAA,IACV;AAAA,EACJ;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/services/delete.ts"],"sourcesContent":["import fs from 'fs';\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport models from '~/models.js';\nimport { paths } from '~/paths.js';\n\ninterface DeleteImageRecord {\n id: number;\n url: string;\n}\n\ninterface ImageDeleteDeps {\n deleteImageRecord: (id: number) => Promise<void>;\n fileExists: (filePath: string) => boolean;\n findImageById: (id: number) => Promise<DeleteImageRecord | null>;\n removeFile: (filePath: string) => Promise<void>;\n resolveImagePath: (url: string) => string;\n}\n\nexport const resolveStoredImagePath = (url: string) => {\n return path.resolve(paths.imageDir, url.replace('/assets/images/', ''));\n};\n\nexport const createImageDeleteService = (deps: ImageDeleteDeps) => {\n return {\n deleteImageById: async (id: number) => {\n const image = await deps.findImageById(id);\n\n if (!image) {\n return false;\n }\n\n const imagePath = deps.resolveImagePath(image.url);\n\n if (deps.fileExists(imagePath)) {\n await deps.removeFile(imagePath);\n }\n\n await deps.deleteImageRecord(id);\n\n return true;\n },\n };\n};\n\nconst defaultImageDeleteService = createImageDeleteService({\n deleteImageRecord: async (id) => {\n await models.image.delete({ where: { id } });\n },\n fileExists: (filePath) => fs.existsSync(filePath),\n findImageById: async (id) => {\n return models.image.findFirst({\n where: { id },\n select: {\n id: true,\n url: true,\n },\n });\n },\n removeFile: async (filePath) => {\n await fsPromises.unlink(filePath);\n },\n resolveImagePath: resolveStoredImagePath,\n});\n\nexport const deleteImageById = async (id: number) => {\n return defaultImageDeleteService.deleteImageById(id);\n};\n"],"mappings":"AAAA,OAAO,QAAQ;AACf,OAAO,gBAAgB;AACvB,OAAO,UAAU;AAEjB,OAAO,YAAY;AACnB,SAAS,aAAa;AAef,MAAM,yBAAyB,CAAC,QAAgB;AACnD,SAAO,KAAK,QAAQ,MAAM,UAAU,IAAI,QAAQ,mBAAmB,EAAE,CAAC;AAC1E;AAEO,MAAM,2BAA2B,CAAC,SAA0B;AAC/D,SAAO;AAAA,IACH,iBAAiB,OAAO,OAAe;AACnC,YAAM,QAAQ,MAAM,KAAK,cAAc,EAAE;AAEzC,UAAI,CAAC,OAAO;AACR,eAAO;AAAA,MACX;AAEA,YAAM,YAAY,KAAK,iBAAiB,MAAM,GAAG;AAEjD,UAAI,KAAK,WAAW,SAAS,GAAG;AAC5B,cAAM,KAAK,WAAW,SAAS;AAAA,MACnC;AAEA,YAAM,KAAK,kBAAkB,EAAE;AAE/B,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAEA,MAAM,4BAA4B,yBAAyB;AAAA,EACvD,mBAAmB,OAAO,OAAO;AAC7B,UAAM,OAAO,MAAM,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAAA,EAC/C;AAAA,EACA,YAAY,CAAC,aAAa,GAAG,WAAW,QAAQ;AAAA,EAChD,eAAe,OAAO,OAAO;AACzB,WAAO,OAAO,MAAM,UAAU;AAAA,MAC1B,OAAO,EAAE,GAAG;AAAA,MACZ,QAAQ;AAAA,QACJ,IAAI;AAAA,QACJ,KAAK;AAAA,MACT;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EACA,YAAY,OAAO,aAAa;AAC5B,UAAM,WAAW,OAAO,QAAQ;AAAA,EACpC;AAAA,EACA,kBAAkB;AACtB,CAAC;AAEM,MAAM,kBAAkB,OAAO,OAAe;AACjD,SAAO,0BAA0B,gBAAgB,EAAE;AACvD;","names":[]}
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { lookup as dnsLookup } from "node:dns/promises";
|
|
2
|
-
import { isIP } from "node:net";
|
|
3
|
-
class RemoteImageFetchError extends Error {
|
|
4
|
-
code;
|
|
5
|
-
status;
|
|
6
|
-
constructor(code, status, message) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = "RemoteImageFetchError";
|
|
9
|
-
this.code = code;
|
|
10
|
-
this.status = status;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
const DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
|
|
14
|
-
const DEFAULT_TIMEOUT_MS = 5e3;
|
|
15
|
-
const SUPPORTED_IMAGE_TYPES = /* @__PURE__ */ new Map([
|
|
16
|
-
["image/png", "png"],
|
|
17
|
-
["image/jpeg", "jpg"],
|
|
18
|
-
["image/gif", "gif"],
|
|
19
|
-
["image/webp", "webp"],
|
|
20
|
-
["image/bmp", "bmp"],
|
|
21
|
-
["image/avif", "avif"]
|
|
22
|
-
]);
|
|
23
|
-
const createRemoteImageFetchError = (code, status, message) => {
|
|
24
|
-
return new RemoteImageFetchError(code, status, message);
|
|
25
|
-
};
|
|
26
|
-
const normalizeIp = (address) => {
|
|
27
|
-
return address.toLowerCase().replace(/^::ffff:/, "");
|
|
28
|
-
};
|
|
29
|
-
const isBlockedIpv4 = (address) => {
|
|
30
|
-
const octets = address.split(".").map((segment) => Number(segment));
|
|
31
|
-
if (octets.length !== 4 || octets.some((segment) => Number.isNaN(segment))) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
if (octets[0] === 127 || octets[0] === 10) {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
if (octets[0] === 192 && octets[1] === 168) {
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
return octets[0] === 169 && octets[1] === 254;
|
|
44
|
-
};
|
|
45
|
-
const isBlockedIpv6 = (address) => {
|
|
46
|
-
if (address === "::1") {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
const firstSegment = address.split(":")[0];
|
|
50
|
-
if (!firstSegment) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
const firstValue = Number.parseInt(firstSegment, 16);
|
|
54
|
-
if (Number.isNaN(firstValue)) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
return (firstValue & 65024) === 64512 || (firstValue & 65472) === 65152;
|
|
58
|
-
};
|
|
59
|
-
const isBlockedAddress = (address) => {
|
|
60
|
-
const normalizedAddress = normalizeIp(address);
|
|
61
|
-
const family = isIP(normalizedAddress);
|
|
62
|
-
if (family === 4) {
|
|
63
|
-
return isBlockedIpv4(normalizedAddress);
|
|
64
|
-
}
|
|
65
|
-
if (family === 6) {
|
|
66
|
-
return isBlockedIpv6(normalizedAddress);
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
};
|
|
70
|
-
const assertSafeRemoteUrl = async (src, lookupHostname) => {
|
|
71
|
-
let remoteUrl;
|
|
72
|
-
try {
|
|
73
|
-
remoteUrl = new URL(src);
|
|
74
|
-
} catch {
|
|
75
|
-
throw createRemoteImageFetchError("INVALID_REMOTE_URL", 400, "Remote image URL is invalid.");
|
|
76
|
-
}
|
|
77
|
-
if (!["http:", "https:"].includes(remoteUrl.protocol)) {
|
|
78
|
-
throw createRemoteImageFetchError("INVALID_REMOTE_URL", 400, "Remote image URL must use http or https.");
|
|
79
|
-
}
|
|
80
|
-
if (!remoteUrl.hostname || remoteUrl.username || remoteUrl.password) {
|
|
81
|
-
throw createRemoteImageFetchError("INVALID_REMOTE_URL", 400, "Remote image URL is invalid.");
|
|
82
|
-
}
|
|
83
|
-
if (remoteUrl.port && !(remoteUrl.protocol === "http:" && remoteUrl.port === "80" || remoteUrl.protocol === "https:" && remoteUrl.port === "443")) {
|
|
84
|
-
throw createRemoteImageFetchError("REMOTE_URL_BLOCKED", 403, "Remote image host is not allowed.");
|
|
85
|
-
}
|
|
86
|
-
if (remoteUrl.hostname.toLowerCase() === "localhost") {
|
|
87
|
-
throw createRemoteImageFetchError("REMOTE_URL_BLOCKED", 403, "Remote image host is not allowed.");
|
|
88
|
-
}
|
|
89
|
-
if (isIP(remoteUrl.hostname)) {
|
|
90
|
-
if (isBlockedAddress(remoteUrl.hostname)) {
|
|
91
|
-
throw createRemoteImageFetchError("REMOTE_URL_BLOCKED", 403, "Remote image host is not allowed.");
|
|
92
|
-
}
|
|
93
|
-
return remoteUrl;
|
|
94
|
-
}
|
|
95
|
-
let addresses;
|
|
96
|
-
try {
|
|
97
|
-
addresses = await lookupHostname(remoteUrl.hostname);
|
|
98
|
-
} catch {
|
|
99
|
-
throw createRemoteImageFetchError("REMOTE_FETCH_FAILED", 502, "Remote image could not be fetched.");
|
|
100
|
-
}
|
|
101
|
-
if (addresses.length === 0 || addresses.some(({ address }) => isBlockedAddress(address))) {
|
|
102
|
-
throw createRemoteImageFetchError("REMOTE_URL_BLOCKED", 403, "Remote image host is not allowed.");
|
|
103
|
-
}
|
|
104
|
-
return remoteUrl;
|
|
105
|
-
};
|
|
106
|
-
const readImageBody = async (response, maxBytes) => {
|
|
107
|
-
if (!response.body) {
|
|
108
|
-
throw createRemoteImageFetchError("REMOTE_FETCH_FAILED", 502, "Remote image response body was empty.");
|
|
109
|
-
}
|
|
110
|
-
const reader = response.body.getReader();
|
|
111
|
-
const chunks = [];
|
|
112
|
-
let totalBytes = 0;
|
|
113
|
-
while (true) {
|
|
114
|
-
const { done, value } = await reader.read();
|
|
115
|
-
if (done) {
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
totalBytes += value.byteLength;
|
|
119
|
-
if (totalBytes > maxBytes) {
|
|
120
|
-
await reader.cancel();
|
|
121
|
-
throw createRemoteImageFetchError("REMOTE_IMAGE_TOO_LARGE", 413, "Remote image exceeded the size limit.");
|
|
122
|
-
}
|
|
123
|
-
chunks.push(Buffer.from(value));
|
|
124
|
-
}
|
|
125
|
-
return Buffer.concat(chunks);
|
|
126
|
-
};
|
|
127
|
-
const fetchRemoteImage = async (src, options = {}) => {
|
|
128
|
-
const fetchImpl = options.fetchImpl ?? fetch;
|
|
129
|
-
const lookupHostname = options.lookupHostname ?? ((hostname) => {
|
|
130
|
-
return dnsLookup(hostname, {
|
|
131
|
-
all: true,
|
|
132
|
-
verbatim: true
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
136
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
137
|
-
const remoteUrl = await assertSafeRemoteUrl(src, lookupHostname);
|
|
138
|
-
let response;
|
|
139
|
-
try {
|
|
140
|
-
response = await fetchImpl(remoteUrl, {
|
|
141
|
-
method: "GET",
|
|
142
|
-
redirect: "error",
|
|
143
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
144
|
-
});
|
|
145
|
-
} catch (error) {
|
|
146
|
-
if (error instanceof RemoteImageFetchError) {
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
149
|
-
if (error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError")) {
|
|
150
|
-
throw createRemoteImageFetchError("REMOTE_FETCH_TIMEOUT", 504, "Remote image fetch timed out.");
|
|
151
|
-
}
|
|
152
|
-
throw createRemoteImageFetchError("REMOTE_FETCH_FAILED", 502, "Remote image could not be fetched.");
|
|
153
|
-
}
|
|
154
|
-
if (!response.ok) {
|
|
155
|
-
throw createRemoteImageFetchError("REMOTE_FETCH_FAILED", 502, "Remote image could not be fetched.");
|
|
156
|
-
}
|
|
157
|
-
const contentTypeHeader = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
158
|
-
const extension = SUPPORTED_IMAGE_TYPES.get(contentTypeHeader);
|
|
159
|
-
if (!extension) {
|
|
160
|
-
throw createRemoteImageFetchError(
|
|
161
|
-
"REMOTE_IMAGE_UNSUPPORTED_CONTENT_TYPE",
|
|
162
|
-
415,
|
|
163
|
-
"Remote image content type is not supported."
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
buffer: await readImageBody(response, maxBytes),
|
|
168
|
-
contentType: contentTypeHeader,
|
|
169
|
-
extension
|
|
170
|
-
};
|
|
171
|
-
};
|
|
172
|
-
export {
|
|
173
|
-
RemoteImageFetchError,
|
|
174
|
-
fetchRemoteImage
|
|
175
|
-
};
|
|
176
|
-
//# sourceMappingURL=remote-fetch.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/services/remote-fetch.ts"],"sourcesContent":["import { lookup as dnsLookup } from 'node:dns/promises';\nimport { isIP } from 'node:net';\n\nexport interface RemoteImage {\n buffer: Buffer;\n contentType: string;\n extension: string;\n}\n\nexport interface RemoteImageFetchOptions {\n fetchImpl?: typeof fetch;\n lookupHostname?: (hostname: string) => Promise<Array<{ address: string }>>;\n maxBytes?: number;\n timeoutMs?: number;\n}\n\nexport class RemoteImageFetchError extends Error {\n public readonly code: string;\n public readonly status: number;\n\n constructor(code: string, status: number, message: string) {\n super(message);\n this.name = 'RemoteImageFetchError';\n this.code = code;\n this.status = status;\n }\n}\n\nconst DEFAULT_MAX_BYTES = 10 * 1024 * 1024;\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst SUPPORTED_IMAGE_TYPES = new Map([\n ['image/png', 'png'],\n ['image/jpeg', 'jpg'],\n ['image/gif', 'gif'],\n ['image/webp', 'webp'],\n ['image/bmp', 'bmp'],\n ['image/avif', 'avif'],\n]);\n\nconst createRemoteImageFetchError = (code: string, status: number, message: string) => {\n return new RemoteImageFetchError(code, status, message);\n};\n\nconst normalizeIp = (address: string) => {\n return address.toLowerCase().replace(/^::ffff:/, '');\n};\n\nconst isBlockedIpv4 = (address: string) => {\n const octets = address.split('.').map((segment) => Number(segment));\n\n if (octets.length !== 4 || octets.some((segment) => Number.isNaN(segment))) {\n return false;\n }\n\n if (octets[0] === 127 || octets[0] === 10) {\n return true;\n }\n\n if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {\n return true;\n }\n\n if (octets[0] === 192 && octets[1] === 168) {\n return true;\n }\n\n return octets[0] === 169 && octets[1] === 254;\n};\n\nconst isBlockedIpv6 = (address: string) => {\n if (address === '::1') {\n return true;\n }\n\n const firstSegment = address.split(':')[0];\n\n if (!firstSegment) {\n return false;\n }\n\n const firstValue = Number.parseInt(firstSegment, 16);\n\n if (Number.isNaN(firstValue)) {\n return false;\n }\n\n return (firstValue & 0xfe00) === 0xfc00 || (firstValue & 0xffc0) === 0xfe80;\n};\n\nconst isBlockedAddress = (address: string) => {\n const normalizedAddress = normalizeIp(address);\n const family = isIP(normalizedAddress);\n\n if (family === 4) {\n return isBlockedIpv4(normalizedAddress);\n }\n\n if (family === 6) {\n return isBlockedIpv6(normalizedAddress);\n }\n\n return false;\n};\n\nconst assertSafeRemoteUrl = async (\n src: string,\n lookupHostname: NonNullable<RemoteImageFetchOptions['lookupHostname']>,\n) => {\n let remoteUrl: URL;\n\n try {\n remoteUrl = new URL(src);\n } catch {\n throw createRemoteImageFetchError('INVALID_REMOTE_URL', 400, 'Remote image URL is invalid.');\n }\n\n if (!['http:', 'https:'].includes(remoteUrl.protocol)) {\n throw createRemoteImageFetchError('INVALID_REMOTE_URL', 400, 'Remote image URL must use http or https.');\n }\n\n if (!remoteUrl.hostname || remoteUrl.username || remoteUrl.password) {\n throw createRemoteImageFetchError('INVALID_REMOTE_URL', 400, 'Remote image URL is invalid.');\n }\n\n if (\n remoteUrl.port &&\n !(\n (remoteUrl.protocol === 'http:' && remoteUrl.port === '80') ||\n (remoteUrl.protocol === 'https:' && remoteUrl.port === '443')\n )\n ) {\n throw createRemoteImageFetchError('REMOTE_URL_BLOCKED', 403, 'Remote image host is not allowed.');\n }\n\n if (remoteUrl.hostname.toLowerCase() === 'localhost') {\n throw createRemoteImageFetchError('REMOTE_URL_BLOCKED', 403, 'Remote image host is not allowed.');\n }\n\n if (isIP(remoteUrl.hostname)) {\n if (isBlockedAddress(remoteUrl.hostname)) {\n throw createRemoteImageFetchError('REMOTE_URL_BLOCKED', 403, 'Remote image host is not allowed.');\n }\n\n return remoteUrl;\n }\n\n let addresses: Array<{ address: string }>;\n\n try {\n addresses = await lookupHostname(remoteUrl.hostname);\n } catch {\n throw createRemoteImageFetchError('REMOTE_FETCH_FAILED', 502, 'Remote image could not be fetched.');\n }\n\n if (addresses.length === 0 || addresses.some(({ address }) => isBlockedAddress(address))) {\n throw createRemoteImageFetchError('REMOTE_URL_BLOCKED', 403, 'Remote image host is not allowed.');\n }\n\n return remoteUrl;\n};\n\nconst readImageBody = async (response: Response, maxBytes: number) => {\n if (!response.body) {\n throw createRemoteImageFetchError('REMOTE_FETCH_FAILED', 502, 'Remote image response body was empty.');\n }\n\n const reader = response.body.getReader();\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw createRemoteImageFetchError('REMOTE_IMAGE_TOO_LARGE', 413, 'Remote image exceeded the size limit.');\n }\n\n chunks.push(Buffer.from(value));\n }\n\n return Buffer.concat(chunks);\n};\n\nexport const fetchRemoteImage = async (src: string, options: RemoteImageFetchOptions = {}): Promise<RemoteImage> => {\n const fetchImpl = options.fetchImpl ?? fetch;\n const lookupHostname =\n options.lookupHostname ??\n ((hostname: string) => {\n return dnsLookup(hostname, {\n all: true,\n verbatim: true,\n });\n });\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const remoteUrl = await assertSafeRemoteUrl(src, lookupHostname);\n\n let response: Response;\n\n try {\n response = await fetchImpl(remoteUrl, {\n method: 'GET',\n redirect: 'error',\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (error) {\n if (error instanceof RemoteImageFetchError) {\n throw error;\n }\n\n if (error instanceof Error && (error.name === 'TimeoutError' || error.name === 'AbortError')) {\n throw createRemoteImageFetchError('REMOTE_FETCH_TIMEOUT', 504, 'Remote image fetch timed out.');\n }\n\n throw createRemoteImageFetchError('REMOTE_FETCH_FAILED', 502, 'Remote image could not be fetched.');\n }\n\n if (!response.ok) {\n throw createRemoteImageFetchError('REMOTE_FETCH_FAILED', 502, 'Remote image could not be fetched.');\n }\n\n const contentTypeHeader = response.headers.get('content-type')?.split(';')[0]?.trim().toLowerCase() ?? '';\n const extension = SUPPORTED_IMAGE_TYPES.get(contentTypeHeader);\n\n if (!extension) {\n throw createRemoteImageFetchError(\n 'REMOTE_IMAGE_UNSUPPORTED_CONTENT_TYPE',\n 415,\n 'Remote image content type is not supported.',\n );\n }\n\n return {\n buffer: await readImageBody(response, maxBytes),\n contentType: contentTypeHeader,\n extension,\n };\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/image/services/upload.ts"],"sourcesContent":["import crypto from 'crypto';\nimport fs from 'fs/promises';\nimport path from 'path';\n\nimport models from '~/models.js';\nimport { paths } from '~/paths.js';\n\ninterface ImageRecord {\n id: number;\n url: string;\n}\n\ninterface ImageUploadDeps {\n createImage: (input: { hash: string; url: string }) => Promise<ImageRecord>;\n ensureDir: (dirPath: string) => Promise<void>;\n findImageByHash: (hash: string) => Promise<ImageRecord | null>;\n removeFile: (filePath: string) => Promise<void>;\n writeFile: (filePath: string, buffer: Buffer) => Promise<void>;\n}\n\nexport interface PersistImageInput {\n buffer: Buffer;\n extension: string;\n}\n\nexport interface PersistedImageSummary {\n id: number;\n url: string;\n}\n\nexport const hashImageBuffer = (buffer: Buffer) => {\n return crypto.createHash('sha512').update(buffer).digest('hex');\n};\n\nconst buildImageTarget = (hash: string, extension: string, now = new Date()) => {\n const normalizedExtension = extension.trim().toLowerCase().replace(/^\\./, '');\n const currentPath = [now.getFullYear().toString(), (now.getMonth() + 1).toString(), now.getDate().toString()];\n const targetDir = path.resolve(paths.imageDir, ...currentPath);\n const fileName = `${hash}.${normalizedExtension}`;\n\n return {\n targetDir,\n absolutePath: path.resolve(targetDir, fileName),\n url: `/assets/images/${currentPath.join('/')}/${fileName}`,\n };\n};\n\nconst isImageHashUniqueConflict = (error: unknown) => {\n if (typeof error !== 'object' || error === null || !('code' in error)) {\n return false;\n }\n\n if (error.code !== 'P2002') {\n return false;\n }\n\n if (!('meta' in error) || typeof error.meta !== 'object' || error.meta === null) {\n return true;\n }\n\n const rawTarget = 'target' in error.meta ? error.meta.target : undefined;\n const targets = Array.isArray(rawTarget)\n ? rawTarget.filter((value): value is string => typeof value === 'string')\n : typeof rawTarget === 'string'\n ? [rawTarget]\n : [];\n\n return targets.length === 0 || targets.includes('hash');\n};\n\nconst removeFileIfPresent = async (deps: Pick<ImageUploadDeps, 'removeFile'>, filePath: string) => {\n try {\n await deps.removeFile(filePath);\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT') {\n return;\n }\n\n throw error;\n }\n};\n\nexport const createImageUploadService = (deps: ImageUploadDeps) => {\n return {\n persistImage: async (input: PersistImageInput): Promise<PersistedImageSummary> => {\n const hash = hashImageBuffer(input.buffer);\n const existingImage = await deps.findImageByHash(hash);\n\n if (existingImage) {\n return existingImage;\n }\n\n const target = buildImageTarget(hash, input.extension);\n\n await deps.ensureDir(target.targetDir);\n await deps.writeFile(target.absolutePath, input.buffer);\n\n try {\n return await deps.createImage({\n hash,\n url: target.url,\n });\n } catch (error) {\n await removeFileIfPresent(deps, target.absolutePath);\n\n if (isImageHashUniqueConflict(error)) {\n const conflictedImage = await deps.findImageByHash(hash);\n\n if (conflictedImage) {\n return conflictedImage;\n }\n }\n\n throw error;\n }\n },\n };\n};\n\nconst defaultImageUploadService = createImageUploadService({\n createImage: async (input) => {\n return models.image.create({\n data: input,\n select: {\n id: true,\n url: true,\n },\n });\n },\n ensureDir: async (dirPath) => {\n await fs.mkdir(dirPath, { recursive: true });\n },\n findImageByHash: async (hash) => {\n return models.image.findFirst({\n where: { hash },\n select: {\n id: true,\n url: true,\n },\n });\n },\n removeFile: async (filePath) => {\n await fs.unlink(filePath);\n },\n writeFile: async (filePath, buffer) => {\n await fs.writeFile(filePath, buffer);\n },\n});\n\nexport const persistUploadedImage = async (input: PersistImageInput) => {\n return defaultImageUploadService.persistImage(input);\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/mcp-admin/http/handlers.ts"],"sourcesContent":["import { createAppError } from '~/modules/error-handler.js';\nimport type { Controller } from '~/types/index.js';\nimport { createMcpAdminService, type McpAdminService } from '../service.js';\n\ntype McpAdminControllerService = Pick<\n McpAdminService,\n 'getStatus' | 'setEnabled' | 'rotateToken' | 'revokeActiveToken'\n>;\n\nexport const createMcpAdminStatusHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (_req, res) => {\n const status = await service.getStatus();\n res.status(200).json(status).end();\n };\n};\n\nexport const createMcpAdminSetEnabledHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (req, res) => {\n const enabled = req.body?.enabled;\n if (typeof enabled !== 'boolean') {\n throw createAppError(400, 'INVALID_MCP_ENABLED', 'enabled must be a boolean.');\n }\n\n await service.setEnabled(enabled);\n const status = await service.getStatus();\n res.status(200).json(status).end();\n };\n};\n\nexport const createMcpAdminRotateTokenHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (_req, res) => {\n const result = await service.rotateToken();\n res.status(200)\n .json({\n token: result.token,\n message: 'Save this token now. It is shown only once.',\n })\n .end();\n };\n};\n\nexport const createMcpAdminRevokeTokenHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (_req, res) => {\n await service.revokeActiveToken();\n const status = await service.getStatus();\n res.status(200).json(status).end();\n };\n};\n"],"mappings":"AAAA,SAAS,sBAAsB;AAE/B,SAAS,6BAAmD;AAOrD,MAAM,8BAA8B,CACvC,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,MAAM,QAAQ;AACxB,UAAM,SAAS,MAAM,QAAQ,UAAU;AACvC,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,IAAI;AAAA,EACrC;AACJ;AAEO,MAAM,kCAAkC,CAC3C,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,UAAU,IAAI,MAAM;AAC1B,QAAI,OAAO,YAAY,WAAW;AAC9B,YAAM,eAAe,KAAK,uBAAuB,4BAA4B;AAAA,IACjF;AAEA,UAAM,QAAQ,WAAW,OAAO;AAChC,UAAM,SAAS,MAAM,QAAQ,UAAU;AACvC,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,IAAI;AAAA,EACrC;AACJ;AAEO,MAAM,mCAAmC,CAC5C,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,MAAM,QAAQ;AACxB,UAAM,SAAS,MAAM,QAAQ,YAAY;AACzC,QAAI,OAAO,GAAG,EACT,KAAK;AAAA,MACF,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,IACb,CAAC,EACA,IAAI;AAAA,EACb;AACJ;AAEO,MAAM,mCAAmC,CAC5C,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,MAAM,QAAQ;AACxB,UAAM,QAAQ,kBAAkB;AAChC,UAAM,SAAS,MAAM,QAAQ,UAAU;AACvC,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,IAAI;AAAA,EACrC;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/features/mcp-admin/service.ts"],"sourcesContent":["import models from '~/models.js';\nimport { issueMcpToken, verifyMcpToken } from '~/modules/mcp-token.js';\n\nconst MCP_ENABLED_CACHE_KEY = 'MCP_ENABLED';\n\nexport interface McpTokenSummary {\n id: string;\n createdAt: string;\n lastUsedAt: string | null;\n}\n\nexport interface McpAdminStatus {\n enabled: boolean;\n hasActiveToken: boolean;\n token: McpTokenSummary | null;\n}\n\nexport interface McpTokenValidationResult {\n ok: boolean;\n reason?: 'not_configured' | 'forbidden';\n}\n\nexport interface McpAdminService {\n getStatus: () => Promise<McpAdminStatus>;\n setEnabled: (enabled: boolean) => Promise<void>;\n rotateToken: () => Promise<{ token: string }>;\n revokeActiveToken: () => Promise<void>;\n validatePresentedToken: (token: string) => Promise<McpTokenValidationResult>;\n}\n\ninterface McpAdminDb {\n cache: {\n findUnique: (args: { where: { key: string } }) => Promise<{ value: string } | null>;\n upsert: (args: {\n where: { key: string };\n update: { value: string };\n create: { key: string; value: string };\n }) => Promise<unknown>;\n };\n mcpToken: {\n findFirst: (args: { where: { revokedAt: Date | null }; orderBy: { createdAt: 'desc' | 'asc' } }) => Promise<{\n id: number;\n tokenHash: string;\n createdAt: Date;\n lastUsedAt: Date | null;\n revokedAt: Date | null;\n } | null>;\n updateMany: (args: {\n where: { revokedAt: Date | null };\n data: { revokedAt: Date };\n }) => Promise<{ count: number }>;\n create: (args: { data: { tokenHash: string } }) => Promise<unknown>;\n update: (args: { where: { id: number }; data: { lastUsedAt: Date } }) => Promise<unknown>;\n };\n $transaction: <T>(callback: (tx: McpAdminDb) => Promise<T>) => Promise<T>;\n}\n\nconst toStatus = (\n enabled: boolean,\n activeToken: {\n id: number;\n createdAt: Date;\n lastUsedAt: Date | null;\n } | null,\n): McpAdminStatus => {\n if (!activeToken) {\n return {\n enabled,\n hasActiveToken: false,\n token: null,\n };\n }\n\n return {\n enabled,\n hasActiveToken: true,\n token: {\n id: String(activeToken.id),\n createdAt: activeToken.createdAt.toISOString(),\n lastUsedAt: activeToken.lastUsedAt ? activeToken.lastUsedAt.toISOString() : null,\n },\n };\n};\n\nexport const createMcpAdminService = (db: McpAdminDb = models as unknown as McpAdminDb): McpAdminService => {\n return {\n async getStatus() {\n const enabledCache = await db.cache.findUnique({ where: { key: MCP_ENABLED_CACHE_KEY } });\n const activeToken = await db.mcpToken.findFirst({\n where: { revokedAt: null },\n orderBy: { createdAt: 'desc' },\n });\n\n return toStatus(enabledCache?.value === 'true', activeToken);\n },\n async setEnabled(enabled) {\n await db.cache.upsert({\n where: { key: MCP_ENABLED_CACHE_KEY },\n update: { value: String(enabled) },\n create: {\n key: MCP_ENABLED_CACHE_KEY,\n value: String(enabled),\n },\n });\n },\n async rotateToken() {\n const now = new Date();\n const issued = issueMcpToken();\n\n await db.$transaction(async (transaction) => {\n await transaction.mcpToken.updateMany({\n where: { revokedAt: null },\n data: { revokedAt: now },\n });\n await transaction.mcpToken.create({ data: { tokenHash: issued.hash } });\n });\n\n return { token: issued.plaintext };\n },\n async revokeActiveToken() {\n await db.mcpToken.updateMany({\n where: { revokedAt: null },\n data: { revokedAt: new Date() },\n });\n },\n async validatePresentedToken(token) {\n const activeToken = await db.mcpToken.findFirst({\n where: { revokedAt: null },\n orderBy: { createdAt: 'desc' },\n });\n\n if (!activeToken) {\n return {\n ok: false,\n reason: 'not_configured',\n };\n }\n\n if (!verifyMcpToken(activeToken.tokenHash, token)) {\n return {\n ok: false,\n reason: 'forbidden',\n };\n }\n\n await db.mcpToken.update({\n where: { id: activeToken.id },\n data: { lastUsedAt: new Date() },\n });\n\n return { ok: true };\n },\n };\n};\n"],"mappings":"AAAA,OAAO,YAAY;AACnB,SAAS,eAAe,sBAAsB;AAE9C,MAAM,wBAAwB;AAsD9B,MAAM,WAAW,CACb,SACA,gBAKiB;AACjB,MAAI,CAAC,aAAa;AACd,WAAO;AAAA,MACH;AAAA,MACA,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AAAA,IACH;AAAA,IACA,gBAAgB;AAAA,IAChB,OAAO;AAAA,MACH,IAAI,OAAO,YAAY,EAAE;AAAA,MACzB,WAAW,YAAY,UAAU,YAAY;AAAA,MAC7C,YAAY,YAAY,aAAa,YAAY,WAAW,YAAY,IAAI;AAAA,IAChF;AAAA,EACJ;AACJ;AAEO,MAAM,wBAAwB,CAAC,KAAiB,WAAqD;AACxG,SAAO;AAAA,IACH,MAAM,YAAY;AACd,YAAM,eAAe,MAAM,GAAG,MAAM,WAAW,EAAE,OAAO,EAAE,KAAK,sBAAsB,EAAE,CAAC;AACxF,YAAM,cAAc,MAAM,GAAG,SAAS,UAAU;AAAA,QAC5C,OAAO,EAAE,WAAW,KAAK;AAAA,QACzB,SAAS,EAAE,WAAW,OAAO;AAAA,MACjC,CAAC;AAED,aAAO,SAAS,cAAc,UAAU,QAAQ,WAAW;AAAA,IAC/D;AAAA,IACA,MAAM,WAAW,SAAS;AACtB,YAAM,GAAG,MAAM,OAAO;AAAA,QAClB,OAAO,EAAE,KAAK,sBAAsB;AAAA,QACpC,QAAQ,EAAE,OAAO,OAAO,OAAO,EAAE;AAAA,QACjC,QAAQ;AAAA,UACJ,KAAK;AAAA,UACL,OAAO,OAAO,OAAO;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,IACA,MAAM,cAAc;AAChB,YAAM,MAAM,oBAAI,KAAK;AACrB,YAAM,SAAS,cAAc;AAE7B,YAAM,GAAG,aAAa,OAAO,gBAAgB;AACzC,cAAM,YAAY,SAAS,WAAW;AAAA,UAClC,OAAO,EAAE,WAAW,KAAK;AAAA,UACzB,MAAM,EAAE,WAAW,IAAI;AAAA,QAC3B,CAAC;AACD,cAAM,YAAY,SAAS,OAAO,EAAE,MAAM,EAAE,WAAW,OAAO,KAAK,EAAE,CAAC;AAAA,MAC1E,CAAC;AAED,aAAO,EAAE,OAAO,OAAO,UAAU;AAAA,IACrC;AAAA,IACA,MAAM,oBAAoB;AACtB,YAAM,GAAG,SAAS,WAAW;AAAA,QACzB,OAAO,EAAE,WAAW,KAAK;AAAA,QACzB,MAAM,EAAE,WAAW,oBAAI,KAAK,EAAE;AAAA,MAClC,CAAC;AAAA,IACL;AAAA,IACA,MAAM,uBAAuB,OAAO;AAChC,YAAM,cAAc,MAAM,GAAG,SAAS,UAAU;AAAA,QAC5C,OAAO,EAAE,WAAW,KAAK;AAAA,QACzB,SAAS,EAAE,WAAW,OAAO;AAAA,MACjC,CAAC;AAED,UAAI,CAAC,aAAa;AACd,eAAO;AAAA,UACH,IAAI;AAAA,UACJ,QAAQ;AAAA,QACZ;AAAA,MACJ;AAEA,UAAI,CAAC,eAAe,YAAY,WAAW,KAAK,GAAG;AAC/C,eAAO;AAAA,UACH,IAAI;AAAA,UACJ,QAAQ;AAAA,QACZ;AAAA,MACJ;AAEA,YAAM,GAAG,SAAS,OAAO;AAAA,QACrB,OAAO,EAAE,IAAI,YAAY,GAAG;AAAA,QAC5B,MAAM,EAAE,YAAY,oBAAI,KAAK,EAAE;AAAA,MACnC,CAAC;AAED,aAAO,EAAE,IAAI,KAAK;AAAA,IACtB;AAAA,EACJ;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/graphql/index.ts"],"sourcesContent":["export { createAllNotesQueryResolver, noteResolvers } from './note.resolver.js';\nexport { noteTypeDefs } from './note.type-defs.js';\n"],"mappings":"AAAA,SAAS,6BAA6B,qBAAqB;AAC3D,SAAS,oBAAoB;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/graphql/note.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { listNoteProperties } from '~/features/note/services/properties.js';\nimport { renderNoteSnapshotContentAsMarkdown } from '~/features/note/services/snapshot.js';\nimport type { Note } from '~/models.js';\nimport models from '~/models.js';\n\ntype NoteFieldResolvers = NonNullable<IResolvers['Note']>;\ntype NoteSnapshotFieldResolvers = NonNullable<IResolvers['NoteSnapshot']>;\n\ninterface NoteSnapshotSource {\n contentAsMarkdown?: string;\n payload?: string;\n}\n\nexport const noteFieldResolvers: NoteFieldResolvers = {\n tags: async (note: Note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),\n properties: async (note: Note) => listNoteProperties(note.id),\n contentAsMarkdown: async (note: Note) => {\n const { blocksToMarkdown } = await import('~/modules/blocknote.js');\n return blocksToMarkdown(note.content);\n },\n};\n\nexport const noteSnapshotFieldResolvers: NoteSnapshotFieldResolvers = {\n contentAsMarkdown: async (snapshot: NoteSnapshotSource) => {\n if (typeof snapshot.contentAsMarkdown === 'string') {\n return snapshot.contentAsMarkdown;\n }\n\n if (typeof snapshot.payload === 'string') {\n return renderNoteSnapshotContentAsMarkdown(snapshot.payload);\n }\n\n return '';\n },\n};\n"],"mappings":"AACA,SAAS,0BAA0B;AACnC,SAAS,2CAA2C;AAEpD,OAAO,YAAY;AAUZ,MAAM,qBAAyC;AAAA,EAClD,MAAM,OAAO,SAAe,OAAO,IAAI,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC;AAAA,EAC/F,YAAY,OAAO,SAAe,mBAAmB,KAAK,EAAE;AAAA,EAC5D,mBAAmB,OAAO,SAAe;AACrC,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAwB;AAClE,WAAO,iBAAiB,KAAK,OAAO;AAAA,EACxC;AACJ;AAEO,MAAM,6BAAyD;AAAA,EAClE,mBAAmB,OAAO,aAAiC;AACvD,QAAI,OAAO,SAAS,sBAAsB,UAAU;AAChD,aAAO,SAAS;AAAA,IACpB;AAEA,QAAI,OAAO,SAAS,YAAY,UAAU;AACtC,aAAO,oCAAoC,SAAS,OAAO;AAAA,IAC/D;AAEA,WAAO;AAAA,EACX;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/graphql/note.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport type { Request } from 'express';\nimport { GraphQLError } from 'graphql';\nimport { extractBlocksByType, parseNoteContent } from '~/features/note/services/content-blocks.js';\nimport {\n createNotePropertyDefinition,\n deleteNotePropertyDefinition,\n InvalidNotePropertyInputError,\n type NotePropertiesPatchInput,\n type NotePropertyDefinitionInput,\n type NotePropertyDefinitionUpdateInput,\n NotePropertyDeleteConfirmationRequiredError,\n updateNotePropertiesWithVersionGuard,\n updateNotePropertyDefinition,\n} from '~/features/note/services/properties.js';\nimport { buildNoteSearchProjection } from '~/features/note/services/search.js';\nimport { createSnapshotMetaFromUserAgent, restoreNoteSnapshot } from '~/features/note/services/snapshot.js';\nimport { purgeTrashedNoteById, restoreTrashedNoteById, trashNoteById } from '~/features/note/services/trash.js';\nimport { updateNoteWithVersionGuard } from '~/features/note/services/write.js';\nimport {\n isInvalidNoteVersionError,\n isMissingNoteVersionError,\n isNoteVersionConflictError,\n} from '~/features/note/services/write-conflict.js';\nimport models from '~/models.js';\nimport type { NoteInput } from '~/types/index.js';\n\nconst PLACEHOLDER_PREFIX = '{%';\nconst PLACEHOLDER_SUFFIX = '%}';\n\nconst replacePlaceholders = async (content: string) => {\n const placeholders = content.matchAll(new RegExp(`${PLACEHOLDER_PREFIX}([^}]+)${PLACEHOLDER_SUFFIX}`, 'g'));\n const resolvedPlaceholders = await models.placeholder.findMany({\n select: {\n template: true,\n replacement: true,\n },\n where: { template: { in: Array.from(new Set(Array.from(placeholders, (match) => match[1]))) } },\n });\n\n let replacedContent = content;\n\n for (const placeholder of resolvedPlaceholders) {\n replacedContent = replacedContent.replace(\n new RegExp(`${PLACEHOLDER_PREFIX}${placeholder.template}${PLACEHOLDER_SUFFIX}`, 'g'),\n placeholder.replacement,\n );\n }\n\n return replacedContent;\n};\n\nconst getRequestUserAgent = (req?: Request) => {\n const userAgentHeader = req?.headers['user-agent'];\n return Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;\n};\n\nconst toTagConnections = (blocks: Array<{ props?: { id?: string | number } }>) => {\n return blocks\n .map((block) => block.props?.id)\n .filter((id) => id !== undefined && id !== null && String(id).trim() !== '')\n .map((id) => Number(id))\n .filter(Number.isFinite)\n .map((id) => ({ id }));\n};\n\nconst parseRequiredNoteContent = (content: string) => {\n const parsedContent = parseNoteContent(content);\n\n if (!parsedContent) {\n throw new GraphQLError('Note content must be valid BlockNote JSON.', {\n extensions: {\n code: 'INVALID_NOTE_CONTENT',\n },\n });\n }\n\n return parsedContent;\n};\n\ntype NoteMutationResolvers = NonNullable<IResolvers['Mutation']>;\ntype CreateNoteInput = Required<Pick<NoteInput, 'title' | 'content'>> & Pick<NoteInput, 'layout'>;\n\nexport const noteMutationResolvers: NoteMutationResolvers = {\n createNote: async (_, { note }: { note: CreateNoteInput }) => {\n const replacedTitle = await replacePlaceholders(note.title);\n const replacedContent = await replacePlaceholders(note.content);\n const parsedContent = replacedContent ? parseRequiredNoteContent(replacedContent) : null;\n const blocks = parsedContent ? extractBlocksByType<{ id: string }>('tag', parsedContent) : [];\n const createdNote = await models.note.create({\n data: {\n title: replacedTitle,\n content: replacedContent,\n ...buildNoteSearchProjection({\n title: replacedTitle,\n content: replacedContent,\n }),\n ...(note.layout && { layout: note.layout }),\n },\n });\n\n if (!replacedContent) {\n return createdNote;\n }\n\n return models.note.update({\n where: { id: createdNote.id },\n data: { tags: { set: toTagConnections(blocks) } },\n });\n },\n updateNote: async (\n _,\n {\n id,\n note,\n editSessionId,\n expectedUpdatedAt,\n force,\n }: {\n id: number;\n note: NoteInput;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n force?: boolean;\n },\n context: {\n req?: Request;\n },\n ) => {\n const parsedContent = note.content ? parseRequiredNoteContent(note.content) : null;\n const blocks = parsedContent ? extractBlocksByType<{ id: string }>('tag', parsedContent) : [];\n try {\n const updatedNote = await updateNoteWithVersionGuard({\n id: Number(id),\n data: {\n ...(note.title !== undefined ? { title: note.title } : {}),\n ...(note.content !== undefined ? { content: note.content } : {}),\n ...(note.layout !== undefined ? { layout: note.layout } : {}),\n ...(note.content !== undefined ? { tagIds: toTagConnections(blocks).map((tag) => tag.id) } : {}),\n },\n ...(editSessionId ? { editSessionId } : {}),\n ...(expectedUpdatedAt ? { expectedUpdatedAt } : {}),\n ...(force ? { force: true } : {}),\n snapshotMeta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n if (!updatedNote) {\n throw 'NOT FOUND';\n }\n\n return updatedNote;\n } catch (error) {\n if (isNoteVersionConflictError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n currentUpdatedAt: error.currentUpdatedAt,\n expectedUpdatedAt: error.expectedUpdatedAt,\n },\n });\n }\n\n if (isInvalidNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n if (isMissingNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n throw error;\n }\n },\n deleteNote: async (_, { id }: { id: string }) => {\n const trashedNote = await trashNoteById(Number(id));\n\n if (!trashedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n restoreNoteSnapshot: async (\n _,\n { id }: { id: string },\n context: {\n req?: Request;\n },\n ) => {\n const note = await restoreNoteSnapshot(Number(id), {\n meta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n restoreTrashedNote: async (_, { id }: { id: string }) => {\n const note = await restoreTrashedNoteById(Number(id));\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n purgeTrashedNote: async (_, { id }: { id: string }) => {\n const purgedNote = await purgeTrashedNoteById(Number(id));\n\n if (!purgedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n createNotePropertyKey: async (_, { input }: { input: NotePropertyDefinitionInput }) => {\n try {\n return await createNotePropertyDefinition(input);\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n throw error;\n }\n },\n updateNotePropertyKey: async (_, { key, input }: { key: string; input: NotePropertyDefinitionUpdateInput }) => {\n try {\n const result = await updateNotePropertyDefinition({ key, input });\n\n if (!result) {\n throw 'NOT FOUND';\n }\n\n return result;\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n throw error;\n }\n },\n deleteNotePropertyKey: async (_, { key, confirmImpact }: { key: string; confirmImpact?: boolean }) => {\n try {\n const result = await deleteNotePropertyDefinition({ key, confirmImpact });\n\n if (!result) {\n throw 'NOT FOUND';\n }\n\n return result;\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n if (error instanceof NotePropertyDeleteConfirmationRequiredError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n affectedNoteCount: error.affectedNoteCount,\n },\n });\n }\n\n throw error;\n }\n },\n updateNoteProperties: async (\n _,\n {\n id,\n patch,\n editSessionId,\n expectedUpdatedAt,\n force,\n }: {\n id: string;\n patch: NotePropertiesPatchInput;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n force?: boolean;\n },\n ) => {\n try {\n const note = await updateNotePropertiesWithVersionGuard({\n id: Number(id),\n patch,\n ...(editSessionId ? { editSessionId } : {}),\n ...(expectedUpdatedAt ? { expectedUpdatedAt } : {}),\n ...(force ? { force: true } : {}),\n });\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n if (isNoteVersionConflictError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n currentUpdatedAt: error.currentUpdatedAt,\n expectedUpdatedAt: error.expectedUpdatedAt,\n },\n });\n }\n\n if (isInvalidNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n if (isMissingNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n throw error;\n }\n },\n pinNote: (_, { id, pinned }: { id: string; pinned: boolean }) =>\n 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\n return Promise.all(updatePromises);\n },\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/graphql/note.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { getNoteCleanupPreview, listNoteCleanupCandidates } from '~/features/note/services/cleanup.js';\nimport {\n buildNoteGraph,\n contentReferencesNote,\n extractReferenceBlocksFromContent,\n normalizeReferenceId,\n syncReferenceTitlesInContent,\n} from '~/features/note/services/content-blocks.js';\nimport { listNotePropertyKeys } from '~/features/note/services/properties.js';\nimport {\n buildNoteSearchProjection,\n filterNotesBySearchQuery,\n NOTE_SEARCH_TEXT_SCHEMA_VERSION,\n parseNoteSearchQuery,\n} from '~/features/note/services/search.js';\nimport { diffNoteSnapshot, getNoteSnapshot, listNoteSnapshots } from '~/features/note/services/snapshot.js';\nimport {\n buildNoteTagNamesWhere,\n type NoteTagMatchMode,\n normalizeNoteTagNames,\n} from '~/features/note/services/tag-filter.js';\nimport { getTrashedNoteById, listTrashedNotes } from '~/features/note/services/trash.js';\nimport type { Note, Prisma } from '~/models.js';\nimport models from '~/models.js';\nimport { runDataMaintenanceInBackground } from '~/modules/data-maintenance.js';\nimport type { Pagination, SearchFilter } from '~/types/index.js';\n\ninterface AllNotesResolverDeps {\n countNotes: (args: { where?: Prisma.NoteWhereInput }) => Promise<number>;\n findNotes: (args: {\n orderBy: Prisma.NoteOrderByWithRelationInput[];\n where?: Prisma.NoteWhereInput;\n take?: number;\n skip?: number;\n }) => Promise<Note[]>;\n triggerSearchBackfill: () => void;\n}\n\nconst MAX_SQLITE_INT_ID = 2_147_483_647;\n\nconst isValidNoteIdString = (id: string) => {\n if (!/^[1-9]\\d*$/.test(id)) {\n return false;\n }\n\n const numericId = Number(id);\n\n return Number.isSafeInteger(numericId) && numericId <= MAX_SQLITE_INT_ID;\n};\n\ninterface NoteResolverDeps {\n findNote: (id: number) => Promise<Note | null>;\n findReferenceNotes: (ids: number[]) => Promise<Note[]>;\n updateNoteContent: (input: {\n id: number;\n updatedAt: Date;\n content: string;\n searchableText: string;\n searchableTextVersion: number;\n }) => Promise<Note>;\n isRecordNotFoundError: (error: unknown) => boolean;\n}\n\nconst buildAllNotesOrderBy = (searchFilter: SearchFilter) => {\n const sortBy = searchFilter.sortBy || 'updatedAt';\n const sortOrder = searchFilter.sortOrder || 'desc';\n const pinnedFirst = searchFilter.pinnedFirst || false;\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 return orderBy;\n};\n\nconst buildAllNotesSearchWhere = (searchFilter: SearchFilter) => {\n const parsedQuery = parseNoteSearchQuery(searchFilter.query);\n\n if (!parsedQuery.hasFilters) {\n return undefined;\n }\n\n return {\n AND: [\n { searchableTextVersion: NOTE_SEARCH_TEXT_SCHEMA_VERSION },\n ...parsedQuery.included.map((keyword) => ({ searchableText: { contains: keyword } })),\n ...parsedQuery.excluded.map((keyword) => ({ NOT: { searchableText: { contains: keyword } } })),\n ],\n } satisfies Prisma.NoteWhereInput;\n};\n\nconst buildAllNotesStaleCandidateWhere = (searchFilter: SearchFilter) => {\n const parsedQuery = parseNoteSearchQuery(searchFilter.query);\n\n return {\n AND: [\n { searchableTextVersion: { not: NOTE_SEARCH_TEXT_SCHEMA_VERSION } },\n ...parsedQuery.included.map((keyword) => ({\n OR: [{ title: { contains: keyword } }, { content: { contains: keyword } }],\n })),\n ],\n } satisfies Prisma.NoteWhereInput;\n};\n\nconst compareNotesForSearch = (left: Note, right: Note, searchFilter: SearchFilter) => {\n if (searchFilter.pinnedFirst && left.pinned !== right.pinned) {\n return left.pinned ? -1 : 1;\n }\n\n const sortBy = searchFilter.sortBy || 'updatedAt';\n const sortOrder = searchFilter.sortOrder || 'desc';\n const leftValue = sortBy === 'createdAt' ? left.createdAt.getTime() : left.updatedAt.getTime();\n const rightValue = sortBy === 'createdAt' ? right.createdAt.getTime() : right.updatedAt.getTime();\n\n if (leftValue === rightValue) {\n return 0;\n }\n\n return sortOrder === 'asc' ? leftValue - rightValue : rightValue - leftValue;\n};\n\nconst mergeSortedNotesForSearch = (left: Note[], right: Note[], searchFilter: SearchFilter) => {\n const merged: Note[] = [];\n let leftIndex = 0;\n let rightIndex = 0;\n\n while (leftIndex < left.length && rightIndex < right.length) {\n if (compareNotesForSearch(left[leftIndex], right[rightIndex], searchFilter) <= 0) {\n merged.push(left[leftIndex]);\n leftIndex += 1;\n } else {\n merged.push(right[rightIndex]);\n rightIndex += 1;\n }\n }\n\n while (leftIndex < left.length) {\n merged.push(left[leftIndex]);\n leftIndex += 1;\n }\n\n while (rightIndex < right.length) {\n merged.push(right[rightIndex]);\n rightIndex += 1;\n }\n\n return merged;\n};\n\nconst isRecordNotFoundError = (error: unknown) => {\n return (\n typeof error === 'object' && error !== null && 'code' in error && (error as { code?: unknown }).code === 'P2025'\n );\n};\n\nexport const createAllNotesQueryResolver = (\n deps: AllNotesResolverDeps = {\n countNotes: ({ where }) => models.note.count({ where }),\n findNotes: ({ orderBy, where, take, skip }) =>\n models.note.findMany({\n orderBy,\n where,\n take,\n skip,\n }),\n triggerSearchBackfill: () => {\n void runDataMaintenanceInBackground();\n },\n },\n) => {\n return async (\n _: unknown,\n {\n searchFilter,\n pagination,\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n },\n ) => {\n const orderBy = buildAllNotesOrderBy(searchFilter);\n const limit = Number(pagination.limit);\n const offset = Number(pagination.offset);\n const where = buildAllNotesSearchWhere(searchFilter);\n\n if (!where) {\n const [notes, totalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n take: limit,\n skip: offset,\n }),\n deps.countNotes({}),\n ]);\n\n return {\n totalCount,\n notes,\n };\n }\n\n const staleCandidateNotes = await deps.findNotes({\n orderBy,\n where: buildAllNotesStaleCandidateWhere(searchFilter),\n });\n\n if (staleCandidateNotes.length === 0) {\n const [notes, totalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n where,\n take: limit,\n skip: offset,\n }),\n deps.countNotes({ where }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n }\n\n deps.triggerSearchBackfill();\n\n const parsedQuery = parseNoteSearchQuery(searchFilter.query);\n const staleNotes = filterNotesBySearchQuery(staleCandidateNotes, parsedQuery);\n\n if (staleNotes.length === 0) {\n const [notes, totalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n where,\n take: limit,\n skip: offset,\n }),\n deps.countNotes({ where }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n }\n\n const [freshNotesPrefix, freshTotalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n where,\n take: offset + limit,\n }),\n deps.countNotes({ where }),\n ]);\n\n const mergedNotes = mergeSortedNotesForSearch(freshNotesPrefix, staleNotes, searchFilter);\n\n return {\n totalCount: freshTotalCount + staleNotes.length,\n notes: mergedNotes.slice(offset, offset + limit),\n };\n };\n};\n\ntype NoteQueryResolvers = NonNullable<IResolvers['Query']>;\n\ninterface BackReferencesQueryResolverDeps {\n findCandidateNotes: (noteId: number) => Promise<Note[]>;\n}\n\nexport const createBackReferencesQueryResolver = (\n deps: BackReferencesQueryResolverDeps = {\n findCandidateNotes: (noteId) =>\n models.note.findMany({\n orderBy: [{ pinned: 'desc' }, { updatedAt: 'desc' }],\n where: {\n NOT: { id: noteId },\n },\n }),\n },\n) => {\n return async (_: unknown, { id }: { id: string }) => {\n const notes = await deps.findCandidateNotes(Number(id));\n\n return notes.filter((note) => contentReferencesNote(note.content, id));\n };\n};\n\ninterface NoteGraphQueryResolverDeps {\n findNotes: () => Promise<Array<Pick<Note, 'id' | 'title' | 'content'>>>;\n}\n\nexport const createNoteGraphQueryResolver = (\n deps: NoteGraphQueryResolverDeps = {\n findNotes: () =>\n models.note.findMany({\n select: {\n id: true,\n title: true,\n content: true,\n },\n }),\n },\n) => {\n return async () => {\n const notes = await deps.findNotes();\n\n return buildNoteGraph(notes);\n };\n};\n\nexport const createNoteQueryResolver = (\n deps: NoteResolverDeps = {\n findNote: (id) => models.note.findUnique({ where: { id } }),\n findReferenceNotes: (ids) => models.note.findMany({ where: { id: { in: ids } } }),\n updateNoteContent: ({ id, updatedAt, content, searchableText, searchableTextVersion }) =>\n models.note.update({\n where: {\n id,\n updatedAt,\n },\n data: {\n content,\n searchableText,\n searchableTextVersion,\n },\n }),\n isRecordNotFoundError,\n },\n) => {\n return async (_: unknown, { id }: { id: string }) => {\n const note = await deps.findNote(Number(id));\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n if (!note.content) {\n return note;\n }\n\n const blocks = extractReferenceBlocksFromContent(note.content);\n\n if (blocks.length === 0) {\n return note;\n }\n\n const referenceIds = Array.from(\n new Set(\n blocks\n .map((block) => normalizeReferenceId(block.props?.id))\n .filter(\n (referenceId): referenceId is string =>\n referenceId !== null && isValidNoteIdString(referenceId),\n )\n .map(Number),\n ),\n );\n const references = await deps.findReferenceNotes(referenceIds);\n const titlesById = new Map(\n references.map((referenceNote: Note) => [String(referenceNote.id), referenceNote.title]),\n );\n const newContent = syncReferenceTitlesInContent(note.content, titlesById);\n\n if (!newContent || newContent === note.content) {\n return note;\n }\n\n try {\n return await deps.updateNoteContent({\n id: note.id,\n updatedAt: note.updatedAt,\n content: newContent,\n ...buildNoteSearchProjection({\n title: note.title,\n content: newContent,\n }),\n });\n } catch (error) {\n if (deps.isRecordNotFoundError(error)) {\n return note;\n }\n\n throw error;\n }\n };\n};\n\nexport const noteQueryResolvers: NoteQueryResolvers = {\n allNotes: createAllNotesQueryResolver(),\n notesInDateRange: async (\n _,\n {\n dateRange,\n }: {\n dateRange: {\n start: string;\n end: string;\n };\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 return models.note.findMany({\n orderBy: { createdAt: 'asc' },\n where,\n });\n },\n tagNotes: async (\n _,\n {\n searchFilter,\n pagination,\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n },\n ) => {\n const where: Prisma.NoteWhereInput = { tags: { some: { id: Number(searchFilter.query) } } };\n const notes = models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset),\n });\n\n return {\n totalCount: models.note.count({ where }),\n notes,\n };\n },\n notesByTagNames: async (\n _,\n {\n tagNames,\n mode,\n pagination,\n }: {\n tagNames: string[];\n mode: NoteTagMatchMode;\n pagination: Pagination;\n },\n ) => {\n const normalizedTagNames = normalizeNoteTagNames(tagNames);\n\n if (normalizedTagNames.length === 0) {\n return {\n totalCount: 0,\n notes: [],\n };\n }\n\n const where = buildNoteTagNamesWhere(normalizedTagNames, mode);\n const notes = models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset),\n });\n\n return {\n totalCount: models.note.count({ where }),\n notes,\n };\n },\n pinnedNotes: async () =>\n models.note.findMany({\n orderBy: [{ order: 'asc' }, { updatedAt: 'desc' }],\n where: { pinned: true },\n }),\n imageNotes: async (_, { src }: { src: string }) =>\n models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where: { content: { contains: src } },\n }),\n backReferences: createBackReferencesQueryResolver(),\n note: createNoteQueryResolver(),\n noteCleanupCandidates: async (\n _,\n {\n query,\n pagination = {\n limit: 20,\n offset: 0,\n },\n }: {\n query?: string;\n pagination: Pagination;\n },\n ) => {\n const result = await listNoteCleanupCandidates({\n keywords: query\n ? query\n .split(/[,\\s]+/)\n .map((keyword) => keyword.trim())\n .filter(Boolean)\n : undefined,\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n\n return result.notes;\n },\n noteCleanupPreview: async (_, { id }: { id: string }) => {\n return getNoteCleanupPreview(Number(id));\n },\n noteSnapshots: async (\n _,\n {\n id,\n limit = 10,\n }: {\n id: string;\n limit?: number;\n },\n ) => {\n return listNoteSnapshots(Number(id), Number(limit));\n },\n noteSnapshot: async (_, { id }: { id: string }) => {\n return getNoteSnapshot(Number(id));\n },\n noteSnapshotDiff: async (\n _,\n {\n id,\n compareToSnapshotId,\n target,\n contextLines,\n }: {\n id: string;\n compareToSnapshotId?: string;\n target?: 'NEXT' | 'PREVIOUS' | 'CURRENT';\n contextLines?: number | null;\n },\n ) => {\n const normalizedContextLines = contextLines ?? 3;\n\n if (normalizedContextLines < 0 || normalizedContextLines > 20) {\n throw new Error('INVALID_SNAPSHOT_DIFF_CONTEXT_LINES');\n }\n\n return diffNoteSnapshot(Number(id), {\n ...(compareToSnapshotId !== undefined ? { compareToSnapshotId: Number(compareToSnapshotId) } : {}),\n ...(target !== undefined ? { target: target.toLowerCase() as 'next' | 'previous' | 'current' } : {}),\n contextLines: normalizedContextLines,\n });\n },\n notePropertyKeys: async (\n _,\n {\n query,\n pagination = {\n limit: 50,\n offset: 0,\n },\n }: {\n query?: string;\n pagination: Pagination;\n },\n ) => {\n return listNotePropertyKeys({\n query,\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n },\n trashedNote: async (_, { id }: { id: string }) => {\n return getTrashedNoteById(Number(id));\n },\n trashedNotes: async (\n _,\n {\n pagination = {\n limit: 25,\n offset: 0,\n },\n }: {\n pagination: Pagination;\n },\n ) => {\n return listTrashedNotes({\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n },\n noteGraph: createNoteGraphQueryResolver(),\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/graphql/note.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { noteFieldResolvers, noteSnapshotFieldResolvers } from './note.field.resolver.js';\nimport { noteMutationResolvers } from './note.mutation.resolver.js';\nimport { createAllNotesQueryResolver, noteQueryResolvers } from './note.query.resolver.js';\n\nexport { createAllNotesQueryResolver } from './note.query.resolver.js';\n\nexport const noteResolvers: IResolvers = {\n Query: noteQueryResolvers,\n Mutation: noteMutationResolvers,\n Note: noteFieldResolvers,\n NoteSnapshot: noteSnapshotFieldResolvers,\n};\n"],"mappings":"AACA,SAAS,oBAAoB,kCAAkC;AAC/D,SAAS,6BAA6B;AACtC,SAAsC,0BAA0B;AAEhE,SAAS,+BAAAA,oCAAmC;AAErC,MAAM,gBAA4B;AAAA,EACrC,OAAO;AAAA,EACP,UAAU;AAAA,EACV,MAAM;AAAA,EACN,cAAc;AAClB;","names":["createAllNotesQueryResolver"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/graphql/note.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.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 enum TagMatchMode {\n and\n or\n }\n\n enum NotePropertyValueType {\n text\n url\n number\n date\n boolean\n select\n }\n\n input NoteInput {\n title: String\n content: String\n layout: NoteLayout\n }\n\n input NotePropertySetInput {\n key: String!\n name: String\n value: String!\n valueType: NotePropertyValueType!\n }\n\n input NotePropertyOptionInput {\n label: String!\n value: String\n color: String\n order: Int\n }\n\n input NotePropertyOptionUpdateInput {\n id: ID\n label: String!\n value: String\n color: String\n order: Int\n }\n\n input NotePropertyDefinitionInput {\n key: String!\n name: String\n valueType: NotePropertyValueType!\n options: [NotePropertyOptionInput!]\n }\n\n input NotePropertyDefinitionUpdateInput {\n name: String\n options: [NotePropertyOptionUpdateInput!]\n }\n\n input NotePropertiesPatchInput {\n set: [NotePropertySetInput!]\n deleteKeys: [String!]\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 properties: [NoteProperty!]!\n }\n\n type NoteProperty {\n key: String!\n name: String!\n value: String!\n valueType: NotePropertyValueType!\n createdAt: String!\n option: NotePropertyOption\n updatedAt: String!\n }\n\n type NotePropertyOption {\n id: ID!\n label: String!\n value: String!\n color: String\n order: Int!\n }\n\n type NotePropertyKey {\n key: String!\n name: String!\n valueType: NotePropertyValueType!\n noteCount: Int!\n options: [NotePropertyOption!]!\n updatedAt: String!\n }\n\n type NotePropertyKeys {\n totalCount: Int!\n keys: [NotePropertyKey!]!\n }\n\n type NotePropertyDeleteResult {\n key: String!\n name: String!\n valueType: NotePropertyValueType!\n affectedNoteCount: Int!\n deleted: Boolean!\n }\n\n type Notes {\n totalCount: Int!\n notes: [Note!]!\n }\n\n type NoteCleanupBackReference {\n id: ID!\n title: String!\n }\n\n type NoteCleanupCandidate {\n id: ID!\n title: String!\n updatedAt: String!\n pinned: Boolean!\n tagNames: [String!]!\n reminderCount: Int!\n backReferenceCount: Int!\n matchedTerms: [String!]!\n reasons: [String!]!\n requiresForce: Boolean!\n forceReasons: [String!]!\n }\n\n type NoteCleanupPreview {\n id: ID!\n title: String!\n updatedAt: String!\n pinned: Boolean!\n tagNames: [String!]!\n reminderCount: Int!\n backReferences: [NoteCleanupBackReference!]!\n orphanedTagNames: [String!]!\n requiresForce: Boolean!\n forceReasons: [String!]!\n }\n\n type NoteSnapshotMeta {\n entrypoint: String\n label: String\n }\n\n type NoteSnapshot {\n id: ID!\n title: String!\n contentPreview: String!\n contentAsMarkdown: String!\n createdAt: String!\n meta: NoteSnapshotMeta!\n }\n\n enum NoteSnapshotDiffTarget {\n NEXT\n PREVIOUS\n CURRENT\n }\n\n type NoteSnapshotDiffEndpoint {\n kind: String!\n id: ID!\n title: String!\n createdAt: String\n updatedAt: String\n meta: NoteSnapshotMeta\n }\n\n type NoteSnapshotDiffDetail {\n markdown: String!\n changedLineCount: Int!\n changedCharCount: Int!\n beforeMarkdownSha256: String!\n afterMarkdownSha256: String!\n }\n\n type NoteSnapshotDiff {\n noteId: ID!\n mode: String!\n before: NoteSnapshotDiffEndpoint!\n after: NoteSnapshotDiffEndpoint!\n diff: NoteSnapshotDiffDetail!\n }\n\n type DeletedNote {\n id: ID!\n title: String!\n createdAt: String!\n updatedAt: String!\n deletedAt: String!\n contentPreview: String!\n contentAsMarkdown: String\n pinned: Boolean!\n order: Int!\n layout: NoteLayout!\n tagNames: [String!]!\n }\n\n type DeletedNotes {\n totalCount: Int!\n notes: [DeletedNote!]!\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 notesByTagNames(tagNames: [String!]!, mode: TagMatchMode!, 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 noteCleanupCandidates(query: String, pagination: PaginationInput): [NoteCleanupCandidate!]!\n noteCleanupPreview(id: ID!): NoteCleanupPreview\n noteSnapshots(id: ID!, limit: Int): [NoteSnapshot!]!\n noteSnapshot(id: ID!): NoteSnapshot\n noteSnapshotDiff(id: ID!, compareToSnapshotId: ID, target: NoteSnapshotDiffTarget, contextLines: Int): NoteSnapshotDiff\n notePropertyKeys(query: String, pagination: PaginationInput): NotePropertyKeys!\n trashedNote(id: ID!): DeletedNote\n trashedNotes(pagination: PaginationInput): DeletedNotes!\n noteGraph: NoteGraph!\n }\n`;\n\nexport const noteMutation = gql`\n type Mutation {\n createNote(note: NoteInput!): Note!\n updateNote(id: ID!, note: NoteInput!, editSessionId: String, expectedUpdatedAt: String, force: Boolean): Note!\n deleteNote(id: ID!): Boolean!\n restoreNoteSnapshot(id: ID!): Note!\n restoreTrashedNote(id: ID!): Note!\n purgeTrashedNote(id: ID!): Boolean!\n createNotePropertyKey(input: NotePropertyDefinitionInput!): NotePropertyKey!\n updateNotePropertyKey(key: String!, input: NotePropertyDefinitionUpdateInput!): NotePropertyKey!\n deleteNotePropertyKey(key: String!, confirmImpact: Boolean): NotePropertyDeleteResult!\n updateNoteProperties(\n id: ID!\n patch: NotePropertiesPatchInput!\n editSessionId: String\n expectedUpdatedAt: String!\n force: Boolean\n ): Note!\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"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2PjB,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsClB,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBrB,MAAM,eAAe;AAAA,MACtB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA;","names":[]}
|