ocean-brain 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/client/dist/assets/{Calendar-DI0PhQTq.js → Calendar-BU9XgX2N.js} +1 -1
- package/server/client/dist/assets/Callout-C6tVCxOT.js +1 -0
- package/server/client/dist/assets/{Graph-Dhuog4tc.js → Graph-Dond2hGK.js} +1 -1
- package/server/client/dist/assets/{Image-DlJ4MDo3.js → Image-DxWhlIDG.js} +1 -1
- package/server/client/dist/assets/{Image.es-COwqkQuh.js → Image.es-BjGK7y6Y.js} +1 -1
- package/server/client/dist/assets/Note-Cr6GtXUM.js +21 -0
- package/server/client/dist/assets/{Plus.es-KeV96R_e.js → Plus.es-EcmtS_uw.js} +1 -1
- package/server/client/dist/assets/{Reminders-D7MCTNxb.js → Reminders-BlLLT_x0.js} +1 -1
- package/server/client/dist/assets/{Search-B-fwTr5y.js → Search-CrZ0ap8x.js} +1 -1
- package/server/client/dist/assets/{SurfaceCard-CHjcCuqZ.js → SurfaceCard-CO0oVxeL.js} +1 -1
- package/server/client/dist/assets/{Tag-BbV2z-Db.js → Tag-vws5AfNW.js} +1 -1
- package/server/client/dist/assets/{TagNotes-2HKF09Jm.js → TagNotes-Fmf1Kgim.js} +1 -1
- package/server/client/dist/assets/{Trash.es-BqRTZPrI.js → Trash.es-DmwfSy3l.js} +1 -1
- package/server/client/dist/assets/{ViewNotes-TfM3Uzbp.js → ViewNotes-BALN7IfH.js} +1 -1
- package/server/client/dist/assets/{Views-CUwtnfa8.js → Views-Ccc_Ml5U.js} +1 -1
- package/server/client/dist/assets/{image.api-CMdQbCyN.js → image.api-DeF9ck7g.js} +1 -1
- package/server/client/dist/assets/{index-zhxZjg7V.js → index-BSVE2cGd.js} +1 -1
- package/server/client/dist/assets/{index-D2gRB1ig.css → index-BZUGWdOU.css} +1 -1
- package/server/client/dist/assets/{index-B4l2UvuC.js → index-BvKBTjQq.js} +51 -37
- package/server/client/dist/assets/{manage-image-DdXb5Bkj.js → manage-image-IoTXaMBP.js} +1 -1
- package/server/client/dist/assets/{manage-image-detail-CrrXCQIX.js → manage-image-detail-BRTXwOsh.js} +1 -1
- package/server/client/dist/assets/{mcp-DqGLZv90.js → mcp-Bh743HBz.js} +1 -1
- package/server/client/dist/assets/note-core-Z6kqulGB.js +180 -0
- package/server/client/dist/assets/note-vendor-i1M2FSI2.js +54 -0
- package/server/client/dist/assets/{placeholder-BXLtvdtN.js → placeholder-XQqo1Fvr.js} +3 -3
- package/server/client/dist/assets/{trash-3eUAjFRh.js → trash-hiJIg6-c.js} +1 -1
- package/server/client/dist/assets/{useReminderMutate-v1EXqM90.js → useReminderMutate-D21PurIS.js} +1 -1
- package/server/client/dist/assets/{view-dashboard-T6gmohr-.js → view-dashboard-BuaZ4MvI.js} +1 -1
- package/server/client/dist/index.html +4 -4
- package/server/dist/features/note/graphql/note.field.resolver.js +14 -1
- package/server/dist/features/note/graphql/note.field.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js +73 -36
- package/server/dist/features/note/graphql/note.mutation.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.query.resolver.js +74 -76
- package/server/dist/features/note/graphql/note.query.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.resolver.js +3 -2
- package/server/dist/features/note/graphql/note.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.type-defs.js +4 -1
- package/server/dist/features/note/graphql/note.type-defs.js.map +1 -1
- package/server/dist/features/note/services/cleanup.js +4 -3
- package/server/dist/features/note/services/cleanup.js.map +1 -1
- package/server/dist/features/note/services/content-blocks.js +113 -0
- package/server/dist/features/note/services/content-blocks.js.map +1 -0
- package/server/dist/features/note/services/search.js +10 -29
- package/server/dist/features/note/services/search.js.map +1 -1
- package/server/dist/features/note/services/snapshot.js +172 -12
- package/server/dist/features/note/services/snapshot.js.map +1 -1
- package/server/dist/features/note/services/write-conflict.js +87 -0
- package/server/dist/features/note/services/write-conflict.js.map +1 -0
- package/server/dist/features/note/services/write.js +107 -0
- package/server/dist/features/note/services/write.js.map +1 -0
- package/server/dist/modules/blocknote-tree.js +41 -0
- package/server/dist/modules/blocknote-tree.js.map +1 -0
- package/server/dist/modules/error-handler.js +7 -4
- package/server/dist/modules/error-handler.js.map +1 -1
- package/server/dist/modules/recovery-retention.js +2 -2
- package/server/dist/modules/recovery-retention.js.map +1 -1
- package/server/client/dist/assets/Callout-BAqbXI0L.js +0 -1
- package/server/client/dist/assets/Note-ChUWU3Su.js +0 -21
- package/server/client/dist/assets/note-core-GgFcksAF.js +0 -178
- package/server/client/dist/assets/note-vendor-B0t5F04o.js +0 -54
- package/server/dist/features/note/graphql/note.graphql.shared.js +0 -23
- package/server/dist/features/note/graphql/note.graphql.shared.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{j as e}from"./note-vendor-
|
|
1
|
+
import{j as e}from"./note-vendor-i1M2FSI2.js";import{p as Y,h as E,w as J,q as v,aS as V,u as ee,ag as M,P as ae,T as s,ak as te,aT as j,aU as N,B as C,a7 as se,M as m,l as w,I as b,r as le,ab as ne,a0 as re,i as $,j as ce,aE as ie,W as oe}from"./index-BvKBTjQq.js";import{a}from"./graph-vendor-CUxe67Lr.js";import{S as D}from"./SurfaceCard-CO0oVxeL.js";import{n as de}from"./Plus.es-EcmtS_uw.js";import"./note-core-Z6kqulGB.js";const me=new Map([["bold",a.createElement(a.Fragment,null,a.createElement("path",{d:"M216.49,168.49a12,12,0,0,1-17,0L128,97,56.49,168.49a12,12,0,0,1-17-17l80-80a12,12,0,0,1,17,0l80,80A12,12,0,0,1,216.49,168.49Z"}))],["duotone",a.createElement(a.Fragment,null,a.createElement("path",{d:"M208,160H48l80-80Z",opacity:"0.2"}),a.createElement("path",{d:"M213.66,154.34l-80-80a8,8,0,0,0-11.32,0l-80,80A8,8,0,0,0,48,168H208a8,8,0,0,0,5.66-13.66ZM67.31,152,128,91.31,188.69,152Z"}))],["fill",a.createElement(a.Fragment,null,a.createElement("path",{d:"M215.39,163.06A8,8,0,0,1,208,168H48a8,8,0,0,1-5.66-13.66l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,215.39,163.06Z"}))],["light",a.createElement(a.Fragment,null,a.createElement("path",{d:"M212.24,164.24a6,6,0,0,1-8.48,0L128,88.49,52.24,164.24a6,6,0,0,1-8.48-8.48l80-80a6,6,0,0,1,8.48,0l80,80A6,6,0,0,1,212.24,164.24Z"}))],["regular",a.createElement(a.Fragment,null,a.createElement("path",{d:"M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z"}))],["thin",a.createElement(a.Fragment,null,a.createElement("path",{d:"M210.83,162.83a4,4,0,0,1-5.66,0L128,85.66,50.83,162.83a4,4,0,0,1-5.66-5.66l80-80a4,4,0,0,1,5.66,0l80,80A4,4,0,0,1,210.83,162.83Z"}))]]),k=a.forwardRef((l,c)=>a.createElement(Y,{ref:c,...l,weights:me}));k.displayName="CaretUpIcon";const he=k,O=["id","name","template","replacement","createdAt","updatedAt"],ue=new Set(O),pe=`query FetchPlaceholders(
|
|
2
2
|
$searchFilter: SearchFilterInput,
|
|
3
3
|
$pagination: PaginationInput
|
|
4
4
|
) {
|
|
@@ -10,7 +10,7 @@ import{j as e}from"./note-vendor-B0t5F04o.js";import{p as Y,h as E,w as J,q as v
|
|
|
10
10
|
placeholders {
|
|
11
11
|
__PLACEHOLDER_FIELDS__ }
|
|
12
12
|
}
|
|
13
|
-
}`,xe=l=>{const c=l&&l.length>0?l.filter(r=>ue.has(r)):
|
|
13
|
+
}`,xe=l=>{const c=l&&l.length>0?l.filter(r=>ue.has(r)):O;return Array.from(new Set(c)).map(r=>" "+r).join(`
|
|
14
14
|
`)+`
|
|
15
15
|
`},ge=async({limit:l=25,offset:c=0,query:h="",fields:r}={})=>{const g=xe(r),i=pe.replace("__PLACEHOLDER_FIELDS__",g);return E(i,{searchFilter:{query:h},pagination:{limit:l,offset:c}})},ye=l=>E(`mutation CreatePlaceholder(
|
|
16
16
|
$name: String!,
|
|
@@ -31,4 +31,4 @@ __PLACEHOLDER_FIELDS__ }
|
|
|
31
31
|
}
|
|
32
32
|
}`,l),fe=l=>E(`mutation DeletePlaceholder($id: ID!) {
|
|
33
33
|
deletePlaceholder(id: $id)
|
|
34
|
-
}`,{id:l}),I=ce(ie),Pe=()=>{const l=oe(),c=J(),h=I.useNavigate(),{page:r}=I.useSearch(),[g,i]=a.useState(!1),[y,H]=a.useState(!0),[n,u]=a.useState({name:"",template:"",replacement:""}),o=25,T=v.placeholders.list({limit:o,offset:(r-1)*o}),[P]=a.useState(V),{data:d,isLoading:p}=ee({queryKey:T,queryFn:async()=>{const t=await ge({offset:(r-1)*o,limit:o});if(t.type==="error")throw t;return t.allPlaceholders}}),F=M({mutationFn:ye,onSuccess:async()=>{l("Placeholder added successfully"),i(!1),u({name:"",template:"",replacement:""}),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),q=M({mutationFn:fe,onSuccess:async()=>{l("Placeholder deleted successfully"),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),B=P.length,x=d?.totalCount??0,Z=d?.placeholders??[],f="font-medium text-fg-tertiary",L="leading-[1.2]",
|
|
34
|
+
}`,{id:l}),I=ce(ie),Pe=()=>{const l=oe(),c=J(),h=I.useNavigate(),{page:r}=I.useSearch(),[g,i]=a.useState(!1),[y,H]=a.useState(!0),[n,u]=a.useState({name:"",template:"",replacement:""}),o=25,T=v.placeholders.list({limit:o,offset:(r-1)*o}),[P]=a.useState(V),{data:d,isLoading:p}=ee({queryKey:T,queryFn:async()=>{const t=await ge({offset:(r-1)*o,limit:o});if(t.type==="error")throw t;return t.allPlaceholders}}),F=M({mutationFn:ye,onSuccess:async()=>{l("Placeholder added successfully"),i(!1),u({name:"",template:"",replacement:""}),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),q=M({mutationFn:fe,onSuccess:async()=>{l("Placeholder deleted successfully"),await c.invalidateQueries({queryKey:v.placeholders.listAll(),exact:!1})}}),B=P.length,x=d?.totalCount??0,Z=d?.placeholders??[],f="font-medium text-fg-tertiary",L="leading-[1.2]",S="break-all leading-[1.2]",A="leading-[1.2]",_="flex flex-col gap-px",R="space-y-px",Q="focus-ring-soft inline-flex items-center gap-2 rounded-[12px] border border-transparent px-2.5 py-1.5 text-fg-tertiary outline-none transition-colors hover:bg-hover-subtle hover:text-fg-default",U="relative",K="absolute right-2 top-2",G="focus-ring-soft surface-base group flex flex-col items-center justify-center gap-0.5 px-4 py-4 text-center text-fg-secondary outline-none transition-colors hover:bg-hover-subtle hover:text-fg-default",X="inline-flex h-[34px] w-[34px] items-center justify-center rounded-full border border-border-subtle bg-surface text-current transition-colors group-hover:border-border-secondary/70",z=x>0?`Placeholders (${x})`:void 0;return e.jsxs(ae,{title:"Placeholders",variant:"default",heading:p?e.jsx($,{width:244,height:24,className:"rounded-full"}):z,description:p?e.jsx($,{width:260,height:16,className:"rounded-full"}):"Create tokens that get replaced with values when you clone a note",children:[e.jsxs("div",{className:"flex flex-col gap-5",children:[e.jsxs("section",{className:"space-y-3 border-b border-border-subtle/80 pb-5",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(s,{as:"p",variant:"body",weight:"medium",children:"System placeholders"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"Built-in tokens for date and time that are always available when cloning notes"})]}),e.jsxs("button",{type:"button",onClick:()=>H(!y),className:Q,children:[e.jsxs(s,{as:"span",variant:"meta",weight:"medium",tone:"secondary",children:[B," items"]}),y?e.jsx(he,{className:"h-4 w-4"}):e.jsx(te,{className:"h-4 w-4"})]})]}),y&&e.jsx("div",{className:"grid-auto-cards grid gap-5",children:P.map(t=>e.jsx(D,{children:e.jsxs("div",{className:_,children:[e.jsx(s,{as:"div",variant:"body",weight:"medium",className:L,children:t.name}),e.jsxs("div",{className:R,children:[e.jsxs(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:S,children:[j,t.template,N]}),e.jsxs(s,{as:"div",variant:"label",weight:"medium",tone:"tertiary",className:A,children:["Current value: ",t.replacement]})]})]})},t.name))})]}),e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(s,{as:"p",variant:"body",weight:"medium",children:"Custom placeholders"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"Custom tokens you define once and reuse whenever you clone a note"})]}),!p&&e.jsx(s,{as:"span",variant:"meta",weight:"medium",tone:"secondary",children:x===1?"1 item":`${x} items`})]}),!p&&e.jsxs("div",{className:"grid-auto-cards grid gap-5",children:[Z.map(t=>e.jsxs(D,{className:U,children:[e.jsx(C,{variant:"ghost",size:"icon-sm",className:K,onClick:()=>q.mutate(t.id.toString()),children:e.jsx(se,{className:"h-4 w-4"})}),e.jsxs("div",{className:_,children:[e.jsx(s,{as:"div",variant:"body",weight:"medium",className:`min-w-0 pr-8 ${L}`,children:t.name}),e.jsxs("div",{className:R,children:[e.jsxs(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:S,children:[j,t.template,N]}),e.jsxs(s,{as:"div",variant:"label",weight:"medium",tone:"tertiary",className:A,children:["Replaces with: ",t.replacement]})]})]})]},t.id)),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:G,children:[e.jsx("span",{className:X,children:e.jsx(de,{className:"h-4.5 w-4.5"})}),e.jsx(s,{as:"div",variant:"meta",weight:"medium",tone:"secondary",className:"text-current",children:"New custom"})]})]})]})]}),e.jsxs(m,{isOpen:g,onClose:()=>i(!1),variant:"form",children:[e.jsx(m.Header,{title:"Add Placeholder",onClose:()=>i(!1)}),e.jsxs(m.Body,{children:[e.jsx(m.Description,{className:"mb-4",children:"Create a token that swaps in a value when you clone a note"}),e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"name",className:f,children:"Name"}),e.jsx(b,{id:"name",type:"text",value:n.name,onChange:t=>u({...n,name:t.target.value}),placeholder:"Description of this placeholder"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"A readable label so you can find this placeholder later"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"template",className:f,children:"Placeholder"}),e.jsx(b,{id:"template",type:"text",value:n.template,onChange:t=>u({...n,template:t.target.value}),placeholder:"note_app"}),e.jsxs(s,{as:"p",variant:"meta",tone:"secondary",children:["Use it in a note as ",j,"your_placeholder",N]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(w,{htmlFor:"replacement",className:f,children:"Replacement"}),e.jsx(b,{id:"replacement",type:"text",value:n.replacement,onChange:t=>u({...n,replacement:t.target.value}),placeholder:"Ocean Brain"}),e.jsx(s,{as:"p",variant:"meta",tone:"secondary",children:"This value is inserted when the placeholder is replaced during cloning"})]})]})]}),e.jsx(m.Footer,{children:e.jsxs(le,{children:[e.jsx(C,{variant:"ghost",onClick:()=>i(!1),children:"Cancel"}),e.jsx(C,{variant:"primary",isLoading:F.isPending,onClick:()=>{if(!n.name||!n.template||!n.replacement){l("All fields are required");return}F.mutate({name:n.name,template:n.template,replacement:n.replacement})},children:"Add"})]})})]}),e.jsx(ne,{fallback:null,children:d?.totalCount&&o<d.totalCount&&e.jsx(re,{page:r,last:Math.ceil(d.totalCount/o),onChange:t=>{h({search:W=>({...W,page:t})})}})})]})};export{Pe as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./note-vendor-
|
|
1
|
+
import{j as e}from"./note-vendor-i1M2FSI2.js";import{$ as M,j as F,av as L,w as Q,v as I,a5 as K,aI as _,q as i,ag as E,aJ as B,N as O,aK as W,P as N,F as $,T as d,aw as H,B as j,a0 as Y,aL as z,i as l,W as G,d as U,aj as V,u as J,aM as Z,M as y}from"./index-BvKBTjQq.js";import{a as k}from"./graph-vendor-CUxe67Lr.js";import{S as C}from"./SurfaceCard-CO0oVxeL.js";import"./note-core-Z6kqulGB.js";const m=25,v=F(L),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=>U(a).format("YYYY-MM-DD HH:mm"),X=({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 Z(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=Q(),c=G(),r=I(),[f,h]=k.useState(null),[x,b]=k.useState(null),{data:u}=K({queryKey:i.notes.trash({limit:m,offset:(a-1)*m}),queryFn:async()=>{const t=await _({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 V(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($,{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(H,{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(Y,{page:a,last:Math.ceil(u.totalCount/m),onChange:t=>{o({search:n=>({...n,page:t})})}})]}),e.jsx(X,{note:A,onClose:()=>h(null)}),e.jsx(z,{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};
|
package/server/client/dist/assets/{useReminderMutate-v1EXqM90.js → useReminderMutate-D21PurIS.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{j as y}from"./note-vendor-
|
|
1
|
+
import{j as y}from"./note-vendor-i1M2FSI2.js";import{h as u,a5 as f,q as n,w as R,W as $}from"./index-BvKBTjQq.js";import{a as l}from"./graph-vendor-CUxe67Lr.js";const g=(e={})=>({limit:e.limit??10,offset:e.offset??0}),D=async(e,t)=>u(`
|
|
2
2
|
query FetchNoteReminders($noteId: ID!, $pagination: PaginationInput) {
|
|
3
3
|
noteReminders(noteId: $noteId, pagination: $pagination) {
|
|
4
4
|
totalCount
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" href="/icon.png" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Ocean Brain</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-BvKBTjQq.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/graph-vendor-CUxe67Lr.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/note-core-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/note-vendor-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/note-core-Z6kqulGB.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/note-vendor-i1M2FSI2.js">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BZUGWdOU.css">
|
|
14
14
|
</head>
|
|
15
15
|
|
|
16
16
|
<body>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderNoteSnapshotContentAsMarkdown } from "../../../features/note/services/snapshot.js";
|
|
1
2
|
import models from "../../../models.js";
|
|
2
3
|
const noteFieldResolvers = {
|
|
3
4
|
tags: async (note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),
|
|
@@ -6,7 +7,19 @@ const noteFieldResolvers = {
|
|
|
6
7
|
return blocksToMarkdown(note.content);
|
|
7
8
|
}
|
|
8
9
|
};
|
|
10
|
+
const noteSnapshotFieldResolvers = {
|
|
11
|
+
contentAsMarkdown: async (snapshot) => {
|
|
12
|
+
if (typeof snapshot.contentAsMarkdown === "string") {
|
|
13
|
+
return snapshot.contentAsMarkdown;
|
|
14
|
+
}
|
|
15
|
+
if (typeof snapshot.payload === "string") {
|
|
16
|
+
return renderNoteSnapshotContentAsMarkdown(snapshot.payload);
|
|
17
|
+
}
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
9
21
|
export {
|
|
10
|
-
noteFieldResolvers
|
|
22
|
+
noteFieldResolvers,
|
|
23
|
+
noteSnapshotFieldResolvers
|
|
11
24
|
};
|
|
12
25
|
//# sourceMappingURL=note.field.resolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/graphql/note.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport type { Note } from '~/models.js';\nimport models from '~/models.js';\n\ntype NoteFieldResolvers = NonNullable<IResolvers['Note']>;\n\nexport const noteFieldResolvers: NoteFieldResolvers = {\n tags: async (note: Note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),\n contentAsMarkdown: async (note: Note) => {\n const { blocksToMarkdown } = await import('~/modules/blocknote.js');\n return blocksToMarkdown(note.content);\n },\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/note/graphql/note.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\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 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,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,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,12 +1,15 @@
|
|
|
1
|
+
import { GraphQLError } from "graphql";
|
|
2
|
+
import { extractBlocksByType, parseNoteContent } from "../../../features/note/services/content-blocks.js";
|
|
1
3
|
import { buildNoteSearchProjection } from "../../../features/note/services/search.js";
|
|
2
|
-
import {
|
|
3
|
-
captureNoteBaseline,
|
|
4
|
-
createSnapshotMetaFromUserAgent,
|
|
5
|
-
restoreNoteSnapshot
|
|
6
|
-
} from "../../../features/note/services/snapshot.js";
|
|
4
|
+
import { createSnapshotMetaFromUserAgent, restoreNoteSnapshot } from "../../../features/note/services/snapshot.js";
|
|
7
5
|
import { purgeTrashedNoteById, restoreTrashedNoteById, trashNoteById } from "../../../features/note/services/trash.js";
|
|
6
|
+
import { updateNoteWithVersionGuard } from "../../../features/note/services/write.js";
|
|
7
|
+
import {
|
|
8
|
+
isInvalidNoteVersionError,
|
|
9
|
+
isMissingNoteVersionError,
|
|
10
|
+
isNoteVersionConflictError
|
|
11
|
+
} from "../../../features/note/services/write-conflict.js";
|
|
8
12
|
import models from "../../../models.js";
|
|
9
|
-
import { extractBlocksByType } from "./note.graphql.shared.js";
|
|
10
13
|
const PLACEHOLDER_PREFIX = "{%";
|
|
11
14
|
const PLACEHOLDER_SUFFIX = "%}";
|
|
12
15
|
const replacePlaceholders = async (content) => {
|
|
@@ -31,10 +34,26 @@ const getRequestUserAgent = (req) => {
|
|
|
31
34
|
const userAgentHeader = req?.headers["user-agent"];
|
|
32
35
|
return Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;
|
|
33
36
|
};
|
|
37
|
+
const toTagConnections = (blocks) => {
|
|
38
|
+
return blocks.map((block) => block.props?.id).filter((id) => id !== void 0 && id !== null && String(id).trim() !== "").map((id) => Number(id)).filter(Number.isFinite).map((id) => ({ id }));
|
|
39
|
+
};
|
|
40
|
+
const parseRequiredNoteContent = (content) => {
|
|
41
|
+
const parsedContent = parseNoteContent(content);
|
|
42
|
+
if (!parsedContent) {
|
|
43
|
+
throw new GraphQLError("Note content must be valid BlockNote JSON.", {
|
|
44
|
+
extensions: {
|
|
45
|
+
code: "INVALID_NOTE_CONTENT"
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return parsedContent;
|
|
50
|
+
};
|
|
34
51
|
const noteMutationResolvers = {
|
|
35
52
|
createNote: async (_, { note }) => {
|
|
36
53
|
const replacedTitle = await replacePlaceholders(note.title);
|
|
37
54
|
const replacedContent = await replacePlaceholders(note.content);
|
|
55
|
+
const parsedContent = replacedContent ? parseRequiredNoteContent(replacedContent) : null;
|
|
56
|
+
const blocks = parsedContent ? extractBlocksByType("tag", parsedContent) : [];
|
|
38
57
|
const createdNote = await models.note.create({
|
|
39
58
|
data: {
|
|
40
59
|
title: replacedTitle,
|
|
@@ -49,46 +68,64 @@ const noteMutationResolvers = {
|
|
|
49
68
|
if (!replacedContent) {
|
|
50
69
|
return createdNote;
|
|
51
70
|
}
|
|
52
|
-
const blocks = extractBlocksByType("tag", JSON.parse(replacedContent));
|
|
53
71
|
return models.note.update({
|
|
54
72
|
where: { id: createdNote.id },
|
|
55
|
-
data: { tags: { set: blocks
|
|
73
|
+
data: { tags: { set: toTagConnections(blocks) } }
|
|
56
74
|
});
|
|
57
75
|
},
|
|
58
76
|
updateNote: async (_, {
|
|
59
77
|
id,
|
|
60
78
|
note,
|
|
61
|
-
editSessionId
|
|
79
|
+
editSessionId,
|
|
80
|
+
expectedUpdatedAt,
|
|
81
|
+
force
|
|
62
82
|
}, context) => {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
const parsedContent = note.content ? parseRequiredNoteContent(note.content) : null;
|
|
84
|
+
const blocks = parsedContent ? extractBlocksByType("tag", parsedContent) : [];
|
|
85
|
+
try {
|
|
86
|
+
const updatedNote = await updateNoteWithVersionGuard({
|
|
87
|
+
id: Number(id),
|
|
88
|
+
data: {
|
|
89
|
+
...note.title !== void 0 ? { title: note.title } : {},
|
|
90
|
+
...note.content !== void 0 ? { content: note.content } : {},
|
|
91
|
+
...note.layout !== void 0 ? { layout: note.layout } : {},
|
|
92
|
+
...note.content !== void 0 ? { tagIds: toTagConnections(blocks).map((tag) => tag.id) } : {}
|
|
93
|
+
},
|
|
94
|
+
...editSessionId ? { editSessionId } : {},
|
|
95
|
+
...expectedUpdatedAt ? { expectedUpdatedAt } : {},
|
|
96
|
+
...force ? { force: true } : {},
|
|
97
|
+
snapshotMeta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req))
|
|
98
|
+
});
|
|
99
|
+
if (!updatedNote) {
|
|
100
|
+
throw "NOT FOUND";
|
|
68
101
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const nextTitle = note.title ?? existingNote.title;
|
|
80
|
-
const nextContent = note.content ?? existingNote.content;
|
|
81
|
-
return models.note.update({
|
|
82
|
-
where: { id: Number(id) },
|
|
83
|
-
data: {
|
|
84
|
-
...note,
|
|
85
|
-
...buildNoteSearchProjection({
|
|
86
|
-
title: nextTitle,
|
|
87
|
-
content: nextContent
|
|
88
|
-
}),
|
|
89
|
-
...note.content ? { tags: { set: blocks.map((block) => ({ id: Number(block.props.id) })) } } : {}
|
|
102
|
+
return updatedNote;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (isNoteVersionConflictError(error)) {
|
|
105
|
+
throw new GraphQLError(error.message, {
|
|
106
|
+
extensions: {
|
|
107
|
+
code: error.code,
|
|
108
|
+
currentUpdatedAt: error.currentUpdatedAt,
|
|
109
|
+
expectedUpdatedAt: error.expectedUpdatedAt
|
|
110
|
+
}
|
|
111
|
+
});
|
|
90
112
|
}
|
|
91
|
-
|
|
113
|
+
if (isInvalidNoteVersionError(error)) {
|
|
114
|
+
throw new GraphQLError(error.message, {
|
|
115
|
+
extensions: {
|
|
116
|
+
code: error.code
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (isMissingNoteVersionError(error)) {
|
|
121
|
+
throw new GraphQLError(error.message, {
|
|
122
|
+
extensions: {
|
|
123
|
+
code: error.code
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
92
129
|
},
|
|
93
130
|
deleteNote: async (_, { id }) => {
|
|
94
131
|
const trashedNote = await trashNoteById(Number(id));
|
|
@@ -1 +1 @@
|
|
|
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 { buildNoteSearchProjection } from '~/features/note/services/search.js';\nimport {\n captureNoteBaseline,\n createSnapshotMetaFromUserAgent,\n restoreNoteSnapshot,\n} from '~/features/note/services/snapshot.js';\nimport { purgeTrashedNoteById, restoreTrashedNoteById, trashNoteById } from '~/features/note/services/trash.js';\nimport models from '~/models.js';\nimport type { NoteInput } from '~/types/index.js';\nimport { extractBlocksByType } from './note.graphql.shared.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\ntype NoteMutationResolvers = NonNullable<IResolvers['Mutation']>;\n\nexport const noteMutationResolvers: NoteMutationResolvers = {\n createNote: async (_, { note }: { note: NoteInput }) => {\n const replacedTitle = await replacePlaceholders(note.title);\n const replacedContent = await replacePlaceholders(note.content);\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 const blocks = extractBlocksByType<{ id: string }>('tag', JSON.parse(replacedContent));\n\n return models.note.update({\n where: { id: createdNote.id },\n data: { tags: { set: blocks.map((block) => ({ id: Number(block.props.id) })) } },\n });\n },\n updateNote: async (\n _,\n {\n id,\n note,\n editSessionId,\n }: {\n id: number;\n note: NoteInput;\n editSessionId?: string;\n },\n context: {\n req?: Request;\n },\n ) => {\n const existingNote = await models.note.findUnique({\n where: { id: Number(id) },\n select: {\n title: true,\n content: true,\n },\n });\n\n if (!existingNote) {\n throw 'NOT FOUND';\n }\n\n const blocks = note.content ? extractBlocksByType<{ id: string }>('tag', JSON.parse(note.content)) : [];\n\n await captureNoteBaseline({\n noteId: Number(id),\n ...(editSessionId ? { editSessionId } : {}),\n meta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n const nextTitle = note.title ?? existingNote.title;\n const nextContent = note.content ?? existingNote.content;\n\n return models.note.update({\n where: { id: Number(id) },\n data: {\n ...note,\n ...buildNoteSearchProjection({\n title: nextTitle,\n content: nextContent,\n }),\n ...(note.content ? { tags: { set: blocks.map((block) => ({ id: Number(block.props.id) })) } } : {}),\n },\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 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
|
+
{"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 { 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 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,10 +1,18 @@
|
|
|
1
1
|
import { getNoteCleanupPreview, listNoteCleanupCandidates } from "../../../features/note/services/cleanup.js";
|
|
2
2
|
import {
|
|
3
|
+
buildNoteGraph,
|
|
4
|
+
contentReferencesNote,
|
|
5
|
+
extractReferenceBlocksFromContent,
|
|
6
|
+
normalizeReferenceId,
|
|
7
|
+
syncReferenceTitlesInContent
|
|
8
|
+
} from "../../../features/note/services/content-blocks.js";
|
|
9
|
+
import {
|
|
10
|
+
buildNoteSearchProjection,
|
|
3
11
|
filterNotesBySearchQuery,
|
|
4
12
|
NOTE_SEARCH_TEXT_SCHEMA_VERSION,
|
|
5
13
|
parseNoteSearchQuery
|
|
6
14
|
} from "../../../features/note/services/search.js";
|
|
7
|
-
import { listNoteSnapshots } from "../../../features/note/services/snapshot.js";
|
|
15
|
+
import { getNoteSnapshot, listNoteSnapshots } from "../../../features/note/services/snapshot.js";
|
|
8
16
|
import {
|
|
9
17
|
buildNoteTagNamesWhere,
|
|
10
18
|
normalizeNoteTagNames
|
|
@@ -12,7 +20,6 @@ import {
|
|
|
12
20
|
import { getTrashedNoteById, listTrashedNotes } from "../../../features/note/services/trash.js";
|
|
13
21
|
import models from "../../../models.js";
|
|
14
22
|
import { runDataMaintenanceInBackground } from "../../../modules/data-maintenance.js";
|
|
15
|
-
import { extractBlocksByType } from "./note.graphql.shared.js";
|
|
16
23
|
const buildAllNotesOrderBy = (searchFilter) => {
|
|
17
24
|
const sortBy = searchFilter.sortBy || "updatedAt";
|
|
18
25
|
const sortOrder = searchFilter.sortOrder || "desc";
|
|
@@ -88,6 +95,9 @@ const mergeSortedNotesForSearch = (left, right, searchFilter) => {
|
|
|
88
95
|
}
|
|
89
96
|
return merged;
|
|
90
97
|
};
|
|
98
|
+
const isRecordNotFoundError = (error) => {
|
|
99
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "P2025";
|
|
100
|
+
};
|
|
91
101
|
const createAllNotesQueryResolver = (deps = {
|
|
92
102
|
countNotes: ({ where }) => models.note.count({ where }),
|
|
93
103
|
findNotes: ({ orderBy, where, take, skip }) => models.note.findMany({
|
|
@@ -174,6 +184,33 @@ const createAllNotesQueryResolver = (deps = {
|
|
|
174
184
|
};
|
|
175
185
|
};
|
|
176
186
|
};
|
|
187
|
+
const createBackReferencesQueryResolver = (deps = {
|
|
188
|
+
findCandidateNotes: (noteId) => models.note.findMany({
|
|
189
|
+
orderBy: [{ pinned: "desc" }, { updatedAt: "desc" }],
|
|
190
|
+
where: {
|
|
191
|
+
NOT: { id: noteId }
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
}) => {
|
|
195
|
+
return async (_, { id }) => {
|
|
196
|
+
const notes = await deps.findCandidateNotes(Number(id));
|
|
197
|
+
return notes.filter((note) => contentReferencesNote(note.content, id));
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
const createNoteGraphQueryResolver = (deps = {
|
|
201
|
+
findNotes: () => models.note.findMany({
|
|
202
|
+
select: {
|
|
203
|
+
id: true,
|
|
204
|
+
title: true,
|
|
205
|
+
content: true
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
}) => {
|
|
209
|
+
return async () => {
|
|
210
|
+
const notes = await deps.findNotes();
|
|
211
|
+
return buildNoteGraph(notes);
|
|
212
|
+
};
|
|
213
|
+
};
|
|
177
214
|
const noteQueryResolvers = {
|
|
178
215
|
allNotes: createAllNotesQueryResolver(),
|
|
179
216
|
notesInDateRange: async (_, {
|
|
@@ -248,12 +285,7 @@ const noteQueryResolvers = {
|
|
|
248
285
|
orderBy: { updatedAt: "desc" },
|
|
249
286
|
where: { content: { contains: src } }
|
|
250
287
|
}),
|
|
251
|
-
backReferences:
|
|
252
|
-
return models.note.findMany({
|
|
253
|
-
orderBy: [{ pinned: "desc" }, { updatedAt: "desc" }],
|
|
254
|
-
where: { content: { contains: `reference","props":{"id":"${id}"` } }
|
|
255
|
-
});
|
|
256
|
-
},
|
|
288
|
+
backReferences: createBackReferencesQueryResolver(),
|
|
257
289
|
note: async (_, { id }) => {
|
|
258
290
|
const note = await models.note.findUnique({ where: { id: Number(id) } });
|
|
259
291
|
if (!note) {
|
|
@@ -262,33 +294,42 @@ const noteQueryResolvers = {
|
|
|
262
294
|
if (!note.content) {
|
|
263
295
|
return note;
|
|
264
296
|
}
|
|
265
|
-
const blocks =
|
|
297
|
+
const blocks = extractReferenceBlocksFromContent(note.content);
|
|
266
298
|
if (blocks.length === 0) {
|
|
267
299
|
return note;
|
|
268
300
|
}
|
|
269
|
-
const referenceIds =
|
|
301
|
+
const referenceIds = Array.from(
|
|
302
|
+
new Set(
|
|
303
|
+
blocks.map((block) => normalizeReferenceId(block.props?.id)).filter((referenceId) => referenceId !== null).map(Number).filter(Number.isFinite)
|
|
304
|
+
)
|
|
305
|
+
);
|
|
270
306
|
const references = await models.note.findMany({ where: { id: { in: referenceIds } } });
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
`reference","props":{"id":"${referenceNote.id}","title":"${referenceNote.title}"`
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
return content;
|
|
280
|
-
}, note.content);
|
|
281
|
-
if (newContent === note.content) {
|
|
307
|
+
const titlesById = new Map(
|
|
308
|
+
references.map((referenceNote) => [String(referenceNote.id), referenceNote.title])
|
|
309
|
+
);
|
|
310
|
+
const newContent = syncReferenceTitlesInContent(note.content, titlesById);
|
|
311
|
+
if (!newContent || newContent === note.content) {
|
|
282
312
|
return note;
|
|
283
313
|
}
|
|
284
314
|
try {
|
|
285
|
-
JSON.parse(newContent);
|
|
286
315
|
return await models.note.update({
|
|
287
|
-
where: {
|
|
288
|
-
|
|
316
|
+
where: {
|
|
317
|
+
id: note.id,
|
|
318
|
+
updatedAt: note.updatedAt
|
|
319
|
+
},
|
|
320
|
+
data: {
|
|
321
|
+
content: newContent,
|
|
322
|
+
...buildNoteSearchProjection({
|
|
323
|
+
title: note.title,
|
|
324
|
+
content: newContent
|
|
325
|
+
})
|
|
326
|
+
}
|
|
289
327
|
});
|
|
290
|
-
} catch {
|
|
291
|
-
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (isRecordNotFoundError(error)) {
|
|
330
|
+
return note;
|
|
331
|
+
}
|
|
332
|
+
throw error;
|
|
292
333
|
}
|
|
293
334
|
},
|
|
294
335
|
noteCleanupCandidates: async (_, {
|
|
@@ -310,10 +351,13 @@ const noteQueryResolvers = {
|
|
|
310
351
|
},
|
|
311
352
|
noteSnapshots: async (_, {
|
|
312
353
|
id,
|
|
313
|
-
limit =
|
|
354
|
+
limit = 10
|
|
314
355
|
}) => {
|
|
315
356
|
return listNoteSnapshots(Number(id), Number(limit));
|
|
316
357
|
},
|
|
358
|
+
noteSnapshot: async (_, { id }) => {
|
|
359
|
+
return getNoteSnapshot(Number(id));
|
|
360
|
+
},
|
|
317
361
|
trashedNote: async (_, { id }) => {
|
|
318
362
|
return getTrashedNoteById(Number(id));
|
|
319
363
|
},
|
|
@@ -328,58 +372,12 @@ const noteQueryResolvers = {
|
|
|
328
372
|
offset: Number(pagination.offset)
|
|
329
373
|
});
|
|
330
374
|
},
|
|
331
|
-
noteGraph:
|
|
332
|
-
const notes = await models.note.findMany({
|
|
333
|
-
select: {
|
|
334
|
-
id: true,
|
|
335
|
-
title: true,
|
|
336
|
-
content: true
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
const nodes = [];
|
|
340
|
-
const links = [];
|
|
341
|
-
const connectionCount = {};
|
|
342
|
-
const linkSet = /* @__PURE__ */ new Set();
|
|
343
|
-
for (const note of notes) {
|
|
344
|
-
if (!note.content) {
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
try {
|
|
348
|
-
const blocks = extractBlocksByType("reference", JSON.parse(note.content));
|
|
349
|
-
for (const block of blocks) {
|
|
350
|
-
const targetId = block.props.id;
|
|
351
|
-
if (targetId && String(note.id) !== targetId) {
|
|
352
|
-
const linkKey = `${note.id}-${targetId}`;
|
|
353
|
-
const reverseLinkKey = `${targetId}-${note.id}`;
|
|
354
|
-
if (!linkSet.has(linkKey) && !linkSet.has(reverseLinkKey)) {
|
|
355
|
-
linkSet.add(linkKey);
|
|
356
|
-
links.push({
|
|
357
|
-
source: String(note.id),
|
|
358
|
-
target: targetId
|
|
359
|
-
});
|
|
360
|
-
connectionCount[String(note.id)] = (connectionCount[String(note.id)] || 0) + 1;
|
|
361
|
-
connectionCount[targetId] = (connectionCount[targetId] || 0) + 1;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
} catch {
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
for (const note of notes) {
|
|
369
|
-
nodes.push({
|
|
370
|
-
id: String(note.id),
|
|
371
|
-
title: note.title || "Untitled",
|
|
372
|
-
connections: connectionCount[String(note.id)] || 0
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
return {
|
|
376
|
-
nodes,
|
|
377
|
-
links
|
|
378
|
-
};
|
|
379
|
-
}
|
|
375
|
+
noteGraph: createNoteGraphQueryResolver()
|
|
380
376
|
};
|
|
381
377
|
export {
|
|
382
378
|
createAllNotesQueryResolver,
|
|
379
|
+
createBackReferencesQueryResolver,
|
|
380
|
+
createNoteGraphQueryResolver,
|
|
383
381
|
noteQueryResolvers
|
|
384
382
|
};
|
|
385
383
|
//# sourceMappingURL=note.query.resolver.js.map
|