@cyberismo/backend 0.0.22 → 0.0.23

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.
Files changed (57) hide show
  1. package/dist/app.js +3 -1
  2. package/dist/app.js.map +1 -1
  3. package/dist/domain/cards/index.js +107 -12
  4. package/dist/domain/cards/index.js.map +1 -1
  5. package/dist/domain/cards/lib.js +1 -4
  6. package/dist/domain/cards/lib.js.map +1 -1
  7. package/dist/domain/cards/presence.d.ts +50 -0
  8. package/dist/domain/cards/presence.js +93 -0
  9. package/dist/domain/cards/presence.js.map +1 -0
  10. package/dist/domain/cards/schema.d.ts +47 -0
  11. package/dist/domain/cards/schema.js +37 -0
  12. package/dist/domain/cards/schema.js.map +1 -0
  13. package/dist/domain/cards/service.d.ts +5 -2
  14. package/dist/domain/cards/service.js +21 -4
  15. package/dist/domain/cards/service.js.map +1 -1
  16. package/dist/domain/connectors/index.d.ts +15 -0
  17. package/dist/domain/connectors/index.js +37 -0
  18. package/dist/domain/connectors/index.js.map +1 -0
  19. package/dist/domain/connectors/service.d.ts +23 -0
  20. package/dist/domain/connectors/service.js +46 -0
  21. package/dist/domain/connectors/service.js.map +1 -0
  22. package/dist/domain/project/index.js +12 -1
  23. package/dist/domain/project/index.js.map +1 -1
  24. package/dist/domain/project/schema.d.ts +3 -0
  25. package/dist/domain/project/schema.js +8 -0
  26. package/dist/domain/project/schema.js.map +1 -1
  27. package/dist/domain/project/service.d.ts +3 -1
  28. package/dist/domain/project/service.js +6 -0
  29. package/dist/domain/project/service.js.map +1 -1
  30. package/dist/domain/tree/service.js +0 -1
  31. package/dist/domain/tree/service.js.map +1 -1
  32. package/dist/export.js +1 -2
  33. package/dist/export.js.map +1 -1
  34. package/dist/index.js +7 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/public/THIRD-PARTY.txt +1196 -615
  37. package/dist/public/assets/index-Cdn_jRWy.js +720 -0
  38. package/dist/public/assets/index-ypsafPwV.css +1 -0
  39. package/dist/public/images/broken_link.svg +7 -0
  40. package/dist/public/index.html +2 -2
  41. package/package.json +5 -5
  42. package/src/app.ts +3 -1
  43. package/src/domain/cards/index.ts +198 -48
  44. package/src/domain/cards/lib.ts +2 -5
  45. package/src/domain/cards/presence.ts +124 -0
  46. package/src/domain/cards/schema.ts +41 -0
  47. package/src/domain/cards/service.ts +65 -4
  48. package/src/domain/connectors/index.ts +39 -0
  49. package/src/domain/connectors/service.ts +67 -0
  50. package/src/domain/project/index.ts +23 -1
  51. package/src/domain/project/schema.ts +9 -0
  52. package/src/domain/project/service.ts +17 -1
  53. package/src/domain/tree/service.ts +0 -1
  54. package/src/export.ts +1 -2
  55. package/src/index.ts +8 -4
  56. package/dist/public/assets/index-B_lh6qtv.css +0 -1
  57. package/dist/public/assets/index-CEol8Bfi.js +0 -164949
@@ -0,0 +1 @@
1
+ @media (width<=800px){.breadcrumbs{display:none}}[role=treeitem][aria-selected=false]:hover .treenode{background-color:var(--joy-palette-background-level1,#dedede)}[role=treeitem][aria-selected=true]{outline:none}.resizeHandle{width:2px}.resizeHandle:hover,.resizeHandle:active{background-color:var(--joy-palette-primary-500,#0b6bcb)}.cyberismo-svg-wrapper svg{max-height:100vh}.doc .MuiButton-root{margin-top:12px}.doc table.tableblock{border-collapse:collapse}.doc{color:var(--joy-palette-text-primary,#333);font-size:inherit;-webkit-hyphens:auto;hyphens:auto;margin:0;padding:0;line-height:1.6}@media screen and (width>=1024px){.doc{flex:auto;min-width:0;margin:0;font-size:.94444rem}}.doc h1,.doc h2,.doc h3,.doc h4,.doc h5,.doc h6{color:var(--joy-palette-text-primary,#191919);-webkit-hyphens:none;hyphens:none;margin:1rem 0 0;font-weight:400;line-height:1.3}.doc>h1.page:first-child{margin:1.5rem 0;font-size:2rem}@media screen and (width>=769px){.doc>h1.page:first-child{margin-top:2.5rem}}.doc>h1.page:first-child+aside.toc.embedded{margin-top:-.5rem}.doc>h2#name+.sectionbody{margin-top:1rem}#preamble+.sect1,.doc .sect1+.sect1{margin-top:2rem}.doc h1.sect0{background:var(--joy-palette-background-surface,#f0f0f0);margin:1.5rem -1rem 0;padding:.5rem 1rem;font-size:1.8em}.doc h2:not(.discrete){border-bottom:1px solid var(--joy-palette-divider,#e1e1e1);margin-left:-1rem;margin-right:-1rem;padding:.4rem 1rem .1rem}.doc h3:not(.discrete),.doc h4:not(.discrete){font-weight:600}.doc h1 .anchor,.doc h2 .anchor,.doc h3 .anchor,.doc h4 .anchor,.doc h5 .anchor,.doc h6 .anchor{visibility:hidden;width:1.75ex;margin-left:-1.5ex;padding-top:.05em;font-size:.8em;font-weight:400;text-decoration:none;position:absolute}.doc h1 .anchor:before,.doc h2 .anchor:before,.doc h3 .anchor:before,.doc h4 .anchor:before,.doc h5 .anchor:before,.doc h6 .anchor:before{content:"§"}.doc h1:hover .anchor,.doc h2:hover .anchor,.doc h3:hover .anchor,.doc h4:hover .anchor,.doc h5:hover .anchor,.doc h6:hover .anchor{visibility:visible}.doc dl,.doc p{margin:0}.doc a{color:var(--joy-palette-primary-600,#1565c0)}.doc a:hover{color:var(--joy-palette-primary-700,#104d92)}.doc a.bare{-webkit-hyphens:none;hyphens:none}.doc a.unresolved{color:var(--joy-palette-danger-500,#d32f2f)}.doc i.fa{-webkit-hyphens:none;hyphens:none;font-style:normal}.doc .colist>table code,.doc p code,.doc thead code{color:var(--joy-palette-text-primary,#222);background:var(--joy-palette-background-level1,#fafafa);border-radius:.25em;padding:.125em .25em;font-size:.95em}.doc code,.doc pre{-webkit-hyphens:none;hyphens:none}.doc pre{margin:0;font-size:.88889rem;line-height:1.5}.doc blockquote{margin:0}.doc .paragraph.lead>p{font-size:1rem}.doc .right{float:right}.doc .left{float:left}.doc .float-gap.right{margin:0 1rem 1rem 0}.doc .float-gap.left{margin:0 0 1rem 1rem}.doc .float-group:after{content:"";clear:both;display:table}.doc .text-left{text-align:left}.doc .text-center{text-align:center}.doc .text-right{text-align:right}.doc .text-justify{text-align:justify}.doc .stretch{width:100%}.doc .big{font-size:larger}.doc .small{font-size:smaller}.doc .underline{text-decoration:underline}.doc .line-through{text-decoration:line-through}.doc .dlist,.doc .exampleblock,.doc .hdlist,.doc .imageblock,.doc .listingblock,.doc .literalblock,.doc .olist,.doc .paragraph,.doc .partintro,.doc .quoteblock,.doc .sidebarblock,.doc .tabs,.doc .ulist,.doc .verseblock,.doc .videoblock,.doc details,.doc hr{margin:1rem 0 0}.doc .tablecontainer,.doc .tablecontainer+*,.doc :not(.tablecontainer)>table.tableblock,.doc :not(.tablecontainer)>table.tableblock+*,.doc>table.tableblock,.doc>table.tableblock+*{margin-top:1.5rem}.doc table.tableblock{font-size:.83333rem}.doc p.tableblock+p.tableblock{margin-top:.5rem}.doc table.tableblock pre{font-size:inherit}.doc td.tableblock>.content{word-wrap:anywhere}.doc td.tableblock>.content>:first-child{margin-top:0}.doc table.tableblock td{padding:.5rem}.doc table.tableblock th{background:var(--joy-palette-background-level1,#fbfcfe);padding:.5rem}.doc table.tableblock,.doc table.tableblock>*>tr>*{border:0 solid var(--joy-palette-divider,#e1e1e1)}.doc table.grid-all>*>tr>*{border-width:1px}.doc table.grid-cols>*>tr>*{border-width:0 1px}.doc table.grid-rows>*>tr>*{border-width:1px 0}.doc table.grid-all>thead th,.doc table.grid-rows>thead th{border-bottom-width:2.5px}.doc table.frame-all{border-width:1px}.doc table.frame-ends{border-width:1px 0}.doc table.frame-sides{border-width:0 1px}.doc table.frame-none>colgroup+*>:first-child>*,.doc table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}.doc table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}.doc table.frame-ends>*>tr>:first-child,.doc table.frame-none>*>tr>:first-child{border-left-width:0}.doc table.frame-ends>*>tr>:last-child,.doc table.frame-none>*>tr>:last-child{border-right-width:0}.doc table.stripes-all>tbody>tr,.doc table.stripes-even>tbody>tr:nth-of-type(2n),.doc table.stripes-hover>tbody>tr:hover,.doc table.stripes-odd>tbody>tr:nth-of-type(odd){background:var(--joy-palette-background-level1,#fafafa)}.doc table.tableblock>tfoot{background:var(--joy-palette-background-surface,#f0f0f0)}.doc .halign-left{text-align:left}.doc .halign-right{text-align:right}.doc .halign-center{text-align:center}.doc .valign-top{vertical-align:top}.doc .valign-bottom{vertical-align:bottom}.doc .valign-middle{vertical-align:middle}.doc .admonitionblock{margin:1.4rem 0 0}.doc .admonitionblock p,.doc .admonitionblock td.content{font-size:.88889rem}.doc .admonitionblock td.content>.title+*,.doc .admonitionblock td.content>:not(.title):first-child{margin-top:0}.doc .admonitionblock td.content pre{font-size:.83333rem}.doc .admonitionblock>table{table-layout:fixed;width:100%;position:relative}.doc .admonitionblock td.content{background:var(--joy-palette-background-level1,#fafafa);word-wrap:anywhere;width:100%;padding:1rem 1rem .75rem}.doc .admonitionblock td.icon{padding:0;font-size:.83333rem;line-height:1;position:absolute;top:0;left:0;transform:translate(-.5rem,-50%)}.doc .admonitionblock td.icon i{filter:none;height:1.25rem;vertical-align:initial;border-radius:.45rem;align-items:center;width:fit-content;padding:0 .5rem;display:inline-flex}.doc .admonitionblock td.icon i:after{content:attr(title);text-transform:uppercase;font-style:normal;font-weight:600}.doc .admonitionblock td.icon i.icon-caution{color:#fff;background-color:#a0439c}.doc .admonitionblock td.icon i.icon-important{color:#fff;background-color:#d32f2f}.doc .admonitionblock td.icon i.icon-note{color:#fff;background-color:#217ee7}.doc .admonitionblock td.icon i.icon-tip{color:#fff;background-color:#41af46}.doc .admonitionblock td.icon i.icon-warning{color:#fff;background-color:#e18114}.doc .imageblock,.doc .videoblock{flex-direction:column;align-items:center;display:flex}.doc .imageblock .content{text-align:center;align-self:stretch}.doc .imageblock.text-left,.doc .videoblock.text-left{align-items:flex-start}.doc .imageblock.text-left .content{text-align:left}.doc .imageblock.text-right,.doc .videoblock.text-right{align-items:flex-end}.doc .imageblock.text-right .content{text-align:right}.doc .image>img,.doc .image>object,.doc .image>svg,.doc .imageblock img,.doc .imageblock object,.doc .imageblock svg{vertical-align:middle;max-width:100%;max-height:75vh;display:inline-block}.doc .image:not(.left):not(.right)>img{margin-top:-.2em}.doc .videoblock iframe,.doc .videoblock video{vertical-align:middle;max-width:100%}#preamble .abstract blockquote{background:var(--joy-palette-background-surface,#f0f0f0);border-left:5px solid var(--joy-palette-divider,#e1e1e1);color:var(--joy-palette-text-secondary,#4a4a4a);padding:.75em 1em;font-size:.88889rem}.doc .quoteblock,.doc .verseblock{background:var(--joy-palette-background-level1,#fafafa);border-left:5px solid var(--joy-palette-neutral-500,#5d5d5d);color:var(--joy-palette-text-secondary,#5d5d5d)}.doc .quoteblock{padding:.25rem 2rem 1.25rem}.doc .quoteblock .attribution{color:var(--joy-palette-text-tertiary,#8e8e8e);margin-top:.75rem;font-size:.83333rem}.doc .quoteblock blockquote{margin-top:1rem}.doc .quoteblock .paragraph{font-style:italic}.doc .quoteblock cite{padding-left:1em}.doc .verseblock{padding:1rem 2rem;font-size:1.15em}.doc .verseblock pre{font-family:inherit;font-size:inherit}.doc ol,.doc ul{margin:0;padding:0 0 0 2rem}.doc ol.none,.doc ol.unnumbered,.doc ol.unstyled,.doc ul.checklist,.doc ul.no-bullet,.doc ul.none,.doc ul.unstyled{list-style-type:none}.doc ol.unnumbered,.doc ul.no-bullet{padding-left:1.25rem}.doc ol.unstyled,.doc ul.unstyled{padding-left:0}.doc ul.circle{list-style-type:circle}.doc ul.disc{list-style-type:disc}.doc ul.square{list-style-type:square}.doc ul.circle ul:not([class]),.doc ul.disc ul:not([class]),.doc ul.square ul:not([class]){list-style:inherit}.doc ol.arabic{list-style-type:decimal}.doc ol.decimal{list-style-type:decimal-leading-zero}.doc ol.loweralpha{list-style-type:lower-alpha}.doc ol.upperalpha{list-style-type:upper-alpha}.doc ol.lowerroman{list-style-type:lower-roman}.doc ol.upperroman{list-style-type:upper-roman}.doc ol.lowergreek{list-style-type:lower-greek}.doc ul.checklist{padding-left:1.75rem}.doc .dlist .dlist,.doc .dlist .olist,.doc .dlist .ulist,.doc .olist .dlist,.doc .olist .olist,.doc .olist .ulist,.doc .olist li+li,.doc .ulist .dlist,.doc .ulist .olist,.doc .ulist .ulist,.doc .ulist li+li{margin-top:.5rem}.doc .admonitionblock .listingblock,.doc .olist .listingblock,.doc .ulist .listingblock{padding:0}.doc .admonitionblock .title,.doc .exampleblock .title,.doc .imageblock .title,.doc .listingblock .title,.doc .literalblock .title,.doc .openblock .title,.doc .videoblock .title,.doc table.tableblock caption{color:var(--joy-palette-text-secondary,#5d5d5d);-webkit-hyphens:none;hyphens:none;letter-spacing:.01em;padding-bottom:.075rem;font-size:.88889rem;font-style:italic;font-weight:600}.doc table.tableblock caption{text-align:left}.doc .olist .title,.doc .ulist .title{margin-bottom:.25rem;font-style:italic;font-weight:600}.doc .imageblock .title,.doc .videoblock .title{margin-top:.5rem;padding-bottom:0}.doc details{margin-left:1rem}.doc details>summary{margin-bottom:.5rem;line-height:1.6;display:block;position:relative}.doc details>summary::-webkit-details-marker{display:none}.doc details>summary:before{content:"";border:.3em solid #0000;border-left:.5em solid;border-right-width:0;position:absolute;top:.5em;left:-1rem;transform:translate(15%)}.doc details[open]>summary:before{border-width:.5rem .3rem 0;border-color:currentColor #0000 #0000;transform:translateY(15%)}.doc details>summary:after{content:"";width:1rem;height:1em;position:absolute;top:.3em;left:-1rem}.doc details.result{margin-top:.25rem}.doc details.result>summary{color:var(--joy-palette-text-secondary,#5d5d5d);margin-bottom:0;font-style:italic}.doc details.result>.content{margin-left:-1rem}.doc .exampleblock>.content,.doc details.result>.content{background:var(--joy-palette-background-body,#fff);border:.25rem solid var(--joy-palette-neutral-500,#5d5d5d);border-radius:.5rem;padding:.75rem}.doc .exampleblock>.content:after,.doc details.result>.content:after{content:"";clear:both;display:table}.doc .exampleblock>.content>:first-child,.doc details>.content>:first-child{margin-top:0}.doc .sidebarblock{background:var(--joy-palette-neutral-softBg,#e1e1e1);border-radius:.75rem;padding:.75rem 1.5rem}.doc .sidebarblock>.content>.title{text-align:center;margin-bottom:.5rem;font-size:1.25rem;font-weight:600;line-height:1.3}.doc .sidebarblock>.content>.title+*,.doc .sidebarblock>.content>:not(.title):first-child{margin-top:0}.doc .listingblock.wrap pre,.doc table.tableblock pre{white-space:pre-wrap}.doc .listingblock pre:not(.highlight),.doc .literalblock pre,.doc pre.highlight>code{background:var(--joy-palette-background-level1,#fafafa);-webkit-box-shadow:inset 0 0 1.75px var(--joy-palette-divider,#e1e1e1);box-shadow:inset 0 0 1.75px var(--joy-palette-divider,#e1e1e1);padding:.875em;display:block;overflow-x:auto}.doc .listingblock>.content{position:relative}.doc .source-toolbox{visibility:hidden;color:gray;-webkit-user-select:none;user-select:none;white-space:nowrap;z-index:1;font-family:Roboto,sans-serif;font-size:.72222rem;line-height:1;display:flex;position:absolute;top:.25rem;right:.5rem}.doc .listingblock:hover .source-toolbox{visibility:visible}.doc .source-toolbox .source-lang{text-transform:uppercase;letter-spacing:.075em}.doc .source-toolbox>:not(:last-child):after{content:"|";letter-spacing:0;padding:0 1ch}.doc .source-toolbox .copy-button{color:inherit;font-size:inherit;line-height:inherit;background:0 0;border:none;outline:none;flex-direction:column;align-items:center;width:1em;height:1em;padding:0;display:flex}.doc .source-toolbox .copy-icon{width:inherit;height:inherit;flex:none}.doc .source-toolbox img.copy-icon{filter:invert(50.2%)}.doc .source-toolbox svg.copy-icon{fill:currentColor}.doc .source-toolbox .copy-toast{background-color:var(--joy-palette-neutral-800,#333);color:var(--joy-palette-common-white,#fff);cursor:auto;opacity:0;border-radius:.25em;flex:none;justify-content:center;margin-top:1em;padding:.5em;transition:opacity .5s .5s;display:inline-flex;position:relative}.doc .source-toolbox .copy-toast:after{content:"";border:.55em solid #0000;border-left-color:var(--joy-palette-neutral-800,#333);transform-origin:0;width:1em;height:1em;position:absolute;top:0;transform:rotate(-90deg)translate(50%)translateY(50%)}.doc .source-toolbox .copy-button.clicked .copy-toast{opacity:1;transition:none}.doc .language-console .hljs-meta{-webkit-user-select:none;user-select:none}.doc .dlist dt{font-style:italic}.doc .dlist dd{margin:0 0 0 1.5rem}.doc .dlist dd+dt,.doc .dlist dd>p:first-child{margin-top:.5rem}.doc td.hdlist1,.doc td.hdlist2{vertical-align:top;padding:.5rem 0 0}.doc tr:first-child>.hdlist1,.doc tr:first-child>.hdlist2{padding-top:0}.doc td.hdlist1{padding-right:.25rem;font-weight:600}.doc td.hdlist2{padding-left:.25rem}.doc .colist{margin:.25rem 0 -.25rem;font-size:.88889rem}.doc .colist>table>tbody>tr>:first-child,.doc .colist>table>tr>:first-child{vertical-align:top;padding:.25em .5rem 0}.doc .colist>table>tbody>tr>:last-child,.doc .colist>table>tr>:last-child{padding:.25rem 0}.doc .conum[data-value]{text-align:center;letter-spacing:-.25ex;text-indent:-.25ex;border:1px solid;border-radius:100%;width:1.25em;height:1.25em;font-family:Roboto,sans-serif;font-size:.75rem;font-style:normal;line-height:1.2;display:inline-block}.doc .conum[data-value]:after{content:attr(data-value)}.doc .conum[data-value]+b{display:none}.doc hr{border:solid var(--joy-palette-divider,#e1e1e1);border-width:2px 0 0;height:0}.doc b.button{white-space:nowrap}.doc b.button:before{content:"[";padding-right:.25em}.doc b.button:after{content:"]";padding-left:.25em}.doc kbd{background:var(--joy-palette-background-level1,#fafafa);border:1px solid var(--joy-palette-neutral-400,#c1c1c1);-webkit-box-shadow:0 1px 0 var(--joy-palette-neutral-400,#c1c1c1), 0 0 0 .1em var(--joy-palette-background-body,#fff) inset;box-shadow:0 1px 0 var(--joy-palette-neutral-400,#c1c1c1), inset 0 0 0 .1em var(--joy-palette-background-body,#fff);vertical-align:text-bottom;white-space:nowrap;border-radius:.25em;padding:.25em .5em;font-size:.66667rem;display:inline-block}.doc .keyseq,.doc kbd{line-height:1}.doc .keyseq{font-size:.88889rem}.doc .keyseq kbd{margin:0 .125em}.doc .keyseq kbd:first-child{margin-left:0}.doc .keyseq kbd:last-child{margin-right:0}.doc .menuseq,.doc .path{-webkit-hyphens:none;hyphens:none}.doc .menuseq i.caret:before{content:"›";font-size:1.1em;font-weight:600;line-height:.90909}.doc :not(pre).nowrap{white-space:nowrap}.doc .nobreak{-webkit-hyphens:none;hyphens:none;word-wrap:normal}.doc :not(pre).pre-wrap{white-space:pre-wrap}#footnotes{margin:2rem -.5rem 0;font-size:.85em;line-height:1.5}.doc td.tableblock>.content #footnotes{margin:2rem 0 0}#footnotes hr{border-top-width:1px;width:20%;margin-top:0}#footnotes .footnote{margin:.5em 0 0 1em}#footnotes .footnote+.footnote{margin-top:.25em}#footnotes .footnote>a:first-of-type{text-align:right;width:1.5em;margin-left:-2em;display:inline-block}.toc-menu{color:var(--joy-palette-text-secondary,#5d5d5d)}.toc.sidebar .toc-menu{margin-right:.75rem;position:sticky;top:6rem}.toc .toc-menu h3{color:var(--joy-palette-text-primary,#333);margin:0 -.5px;padding-bottom:.25rem;font-size:.88889rem;font-weight:600;line-height:1.3}.toc.sidebar .toc-menu h3{flex-direction:column;justify-content:flex-end;height:2.5rem;display:flex}.toc .toc-menu ul{margin:0;padding:0;font-size:.83333rem;line-height:1.2;list-style:none}.toc.sidebar .toc-menu ul{-ms-scroll-chaining:none;overscroll-behavior:none;max-height:calc(100vh - 8.5rem);overflow-y:auto}@supports (scrollbar-width:none){.toc.sidebar .toc-menu ul{scrollbar-width:none}}.toc .toc-menu ul::-webkit-scrollbar{width:0;height:0}@media screen and (width>=1024px){.toc .toc-menu h3{font-size:.83333rem}.toc .toc-menu ul{font-size:.75rem}}.toc .toc-menu li{margin:0}.toc .toc-menu li[data-level="2"] a{padding-left:1.25rem}.toc .toc-menu li[data-level="3"] a{padding-left:2rem}.toc .toc-menu a{color:inherit;border-left:2px solid var(--joy-palette-divider,#e1e1e1);padding:.25rem 0 .25rem .5rem;text-decoration:none;display:inline-block}.sidebar.toc .toc-menu a{outline:none;display:block}.toc .toc-menu a:hover{color:var(--joy-palette-primary-600,#1565c0)}.toc .toc-menu a.is-active{border-left-color:var(--joy-palette-primary-500,#2a7ee4);color:var(--joy-palette-primary-500,#2a7ee4)}.sidebar.toc .toc-menu a:focus{background:var(--joy-palette-background-level1,#fafafa)}.toc .toc-menu .is-hidden-toc{display:none!important}.contentSidebar{min-width:160px}.doc .videoblock .content{width:100%;max-width:100%;position:relative}.doc .videoblock .content:before{content:"";padding-top:56.25%;display:block}.doc .videoblock .content>iframe,.doc .videoblock .content>video{background:#000;border:0;display:block;position:absolute;inset:0;width:100%!important;height:100%!important}
@@ -0,0 +1,7 @@
1
+ <svg width="721" height="283" viewBox="0 0 721 283" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M163 140.5C163 176.122 191.878 205 227.5 205L255.5 205L226.5 235L227.5 235C175.309 235 133 192.691 133 140.5C133 88.3091 175.309 46 227.5 46L427.5 46L397.5 76L227.5 76C191.878 76 163 104.878 163 140.5Z" fill="black"/>
3
+ <path d="M566.5 140.5C566.5 104.878 537.622 76 502 76L474 76L503 46L502 46C554.191 46 596.5 88.3091 596.5 140.5C596.5 192.691 554.191 235 502 235L365 235L392 205L502 205C537.622 205 566.5 176.122 566.5 140.5Z" fill="black"/>
4
+ <path d="M366.012 98.4991L224.882 266.38L340.776 171.162L348.984 212.415L504.678 0.000272155L398.529 107.516L366.012 98.4991Z" fill="#FF4400"/>
5
+ <rect x="456" y="123" width="265" height="35" rx="17.5" fill="black"/>
6
+ <rect y="123" width="265" height="35" rx="17.5" fill="black"/>
7
+ </svg>
@@ -11,8 +11,8 @@
11
11
  name="msapplication-TileImage"
12
12
  content="/cropped-favicon-270x270.png"
13
13
  />
14
- <script type="module" crossorigin src="/assets/index-CEol8Bfi.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-B_lh6qtv.css">
14
+ <script type="module" crossorigin src="/assets/index-Cdn_jRWy.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-ypsafPwV.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyberismo/backend",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Express backend for Cyberismo",
5
5
  "main": "dist/index.js",
6
6
  "keywords": [],
@@ -15,16 +15,16 @@
15
15
  "dependencies": {
16
16
  "@asciidoctor/core": "^3.0.4",
17
17
  "@modelcontextprotocol/sdk": "^1.27.1",
18
- "@hono/node-server": "^1.19.2",
18
+ "@hono/node-server": "^1.19.10",
19
19
  "@hono/zod-validator": "^0.7.6",
20
20
  "@types/mime-types": "^3.0.1",
21
21
  "dotenv": "^17.3.1",
22
- "hono": "^4.10.7",
22
+ "hono": "^4.12.7",
23
23
  "jose": "^6.1.3",
24
24
  "mime-types": "^3.0.2",
25
25
  "zod": "^4.3.6",
26
- "@cyberismo/data-handler": "0.0.22",
27
- "@cyberismo/mcp": "0.0.21"
26
+ "@cyberismo/mcp": "0.0.23",
27
+ "@cyberismo/data-handler": "0.0.23"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@cyberismo/app": "0.0.2"
package/src/app.ts CHANGED
@@ -17,6 +17,7 @@ import { attachCommandManager } from './middleware/commandManager.js';
17
17
  import calculationsRouter from './domain/calculations/index.js';
18
18
  import cardsRouter from './domain/cards/index.js';
19
19
  import cardTypesRouter from './domain/cardTypes/index.js';
20
+ import connectorsRouter from './domain/connectors/index.js';
20
21
  import fieldTypesRouter from './domain/fieldTypes/index.js';
21
22
  import graphModelsRouter from './domain/graphModels/index.js';
22
23
  import graphViewsRouter from './domain/graphViews/index.js';
@@ -70,6 +71,7 @@ export function createApp(
70
71
  app.route('/api/calculations', calculationsRouter);
71
72
  app.route('/api/cards', cardsRouter);
72
73
  app.route('/api/cardTypes', cardTypesRouter);
74
+ app.route('/api/connectors', connectorsRouter);
73
75
  app.route('/api/fieldTypes', fieldTypesRouter);
74
76
  app.route('/api/graphModels', graphModelsRouter);
75
77
  app.route('/api/graphViews', graphViewsRouter);
@@ -96,7 +98,7 @@ export function createApp(
96
98
  // serve index.html for all other routes
97
99
  app.notFound(async (c) => {
98
100
  if (c.req.path.startsWith('/api')) {
99
- return c.text('Not Found', 400);
101
+ return c.text('Not Found', 404);
100
102
  }
101
103
  const file = await readFile(
102
104
  path.join(import.meta.dirname, 'public', 'index.html'),
@@ -13,12 +13,20 @@
13
13
 
14
14
  import { type Context, Hono } from 'hono';
15
15
  import type { ContentfulStatusCode } from 'hono/utils/http-status';
16
+ import { streamSSE } from 'hono/streaming';
16
17
  import { getCardDetails } from './lib.js';
17
18
  import * as cardService from './service.js';
18
19
  import { isSSGContext, ssgParams } from 'hono/ssg';
19
20
  import type { AppContext } from '../../types.js';
20
21
  import { UserRole } from '../../types.js';
22
+ import { presenceStore } from './presence.js';
21
23
  import { requireRole } from '../../middleware/auth.js';
24
+ import { zValidator } from '../../middleware/zvalidator.js';
25
+ import {
26
+ createLinkSchema,
27
+ removeLinkSchema,
28
+ updateLinkSchema,
29
+ } from './schema.js';
22
30
 
23
31
  const router = new Hono();
24
32
 
@@ -514,33 +522,36 @@ router.post('/:key/parse', requireRole(UserRole.Reader), async (c) => {
514
522
  * 500:
515
523
  * description: Server error
516
524
  */
517
- router.post('/:key/links', requireRole(UserRole.Editor), async (c) => {
518
- const commands = c.get('commands');
519
- const key = c.req.param('key');
520
- const { toCard, linkType, description } = await c.req.json();
521
-
522
- if (!toCard || !linkType) {
523
- return c.json({ error: 'toCard and linkType are required' }, 400);
524
- }
525
+ router.post(
526
+ '/:key/links',
527
+ requireRole(UserRole.Editor),
528
+ zValidator('json', createLinkSchema),
529
+ async (c) => {
530
+ const commands = c.get('commands');
531
+ const key = c.req.param('key');
532
+ const { toCard, linkType, description, direction } = c.req.valid('json');
525
533
 
526
- try {
527
- const result = await cardService.createLink(
528
- commands,
529
- key,
530
- toCard,
531
- linkType,
532
- description,
533
- );
534
- return c.json(result);
535
- } catch (error) {
536
- return c.json(
537
- {
538
- error: error instanceof Error ? error.message : 'Failed to create link',
539
- },
540
- 500,
541
- );
542
- }
543
- });
534
+ try {
535
+ const result = await cardService.createLink(
536
+ commands,
537
+ key,
538
+ toCard,
539
+ linkType,
540
+ direction,
541
+ description,
542
+ );
543
+ return c.json(result);
544
+ } catch (error) {
545
+ return c.json(
546
+ {
547
+ error:
548
+ error instanceof Error ? error.message : 'Failed to create link',
549
+ },
550
+ 500,
551
+ );
552
+ }
553
+ },
554
+ );
544
555
 
545
556
  /**
546
557
  * @swagger
@@ -573,33 +584,122 @@ router.post('/:key/links', requireRole(UserRole.Editor), async (c) => {
573
584
  * 500:
574
585
  * description: Server error
575
586
  */
576
- router.delete('/:key/links', requireRole(UserRole.Editor), async (c) => {
577
- const commands = c.get('commands');
578
- const key = c.req.param('key');
579
- const { toCard, linkType, description } = await c.req.json();
587
+ router.delete(
588
+ '/:key/links',
589
+ requireRole(UserRole.Editor),
590
+ zValidator('json', removeLinkSchema),
591
+ async (c) => {
592
+ const commands = c.get('commands');
593
+ const key = c.req.param('key');
594
+ const { toCard, linkType, description, direction } = c.req.valid('json');
580
595
 
581
- if (!toCard || !linkType) {
582
- return c.json({ error: 'toCard and linkType are required' }, 400);
583
- }
596
+ try {
597
+ const result = await cardService.removeLink(
598
+ commands,
599
+ key,
600
+ toCard,
601
+ linkType,
602
+ direction,
603
+ description,
604
+ );
605
+ return c.json(result);
606
+ } catch (error) {
607
+ return c.json(
608
+ {
609
+ error:
610
+ error instanceof Error ? error.message : 'Failed to remove link',
611
+ },
612
+ 500,
613
+ );
614
+ }
615
+ },
616
+ );
584
617
 
585
- try {
586
- const result = await cardService.removeLink(
587
- commands,
588
- key,
618
+ /**
619
+ * @swagger
620
+ * /api/cards/{key}/links:
621
+ * put:
622
+ * summary: Update a link between cards
623
+ * parameters:
624
+ * - name: key
625
+ * in: path
626
+ * required: true
627
+ * schema:
628
+ * type: string
629
+ * requestBody:
630
+ * content:
631
+ * application/json:
632
+ * schema:
633
+ * type: object
634
+ * properties:
635
+ * toCard:
636
+ * type: string
637
+ * linkType:
638
+ * type: string
639
+ * description:
640
+ * type: string
641
+ * direction:
642
+ * type: string
643
+ * previousToCard:
644
+ * type: string
645
+ * previousLinkType:
646
+ * type: string
647
+ * previousDirection:
648
+ * type: string
649
+ * previousDescription:
650
+ * type: string
651
+ * responses:
652
+ * 200:
653
+ * description: Link updated successfully
654
+ * 400:
655
+ * description: Invalid request
656
+ * 500:
657
+ * description: Server error
658
+ */
659
+ router.put(
660
+ '/:key/links',
661
+ requireRole(UserRole.Editor),
662
+ zValidator('json', updateLinkSchema),
663
+ async (c) => {
664
+ const commands = c.get('commands');
665
+ const key = c.req.param('key');
666
+
667
+ const {
589
668
  toCard,
590
669
  linkType,
591
670
  description,
592
- );
593
- return c.json(result);
594
- } catch (error) {
595
- return c.json(
596
- {
597
- error: error instanceof Error ? error.message : 'Failed to remove link',
598
- },
599
- 500,
600
- );
601
- }
602
- });
671
+ direction,
672
+ previousToCard,
673
+ previousLinkType,
674
+ previousDirection,
675
+ previousDescription,
676
+ } = c.req.valid('json');
677
+
678
+ try {
679
+ const result = await cardService.updateLink(
680
+ commands,
681
+ key,
682
+ toCard,
683
+ linkType,
684
+ direction,
685
+ previousToCard,
686
+ previousLinkType,
687
+ previousDirection,
688
+ description,
689
+ previousDescription,
690
+ );
691
+ return c.json(result);
692
+ } catch (error) {
693
+ return c.json(
694
+ {
695
+ error:
696
+ error instanceof Error ? error.message : 'Failed to update link',
697
+ },
698
+ 500,
699
+ );
700
+ }
701
+ },
702
+ );
603
703
 
604
704
  /**
605
705
  * @swagger
@@ -670,5 +770,55 @@ router.get(
670
770
  }
671
771
  },
672
772
  );
773
+ /**
774
+ * @swagger
775
+ * /api/cards/{key}/presence:
776
+ * get:
777
+ * summary: SSE stream of users currently viewing or editing this card
778
+ * parameters:
779
+ * - name: key
780
+ * in: path
781
+ * required: true
782
+ * description: Card key (string)
783
+ * - name: mode
784
+ * in: query
785
+ * required: false
786
+ * schema:
787
+ * type: string
788
+ * enum: [viewing, editing]
789
+ * description: Whether the user is viewing or editing (default: viewing)
790
+ * responses:
791
+ * 200:
792
+ * description: SSE stream with presence events
793
+ */
794
+ router.get('/:key/presence', requireRole(UserRole.Reader), (c) => {
795
+ const key = c.req.param('key');
796
+ const mode = c.req.query('mode') === 'editing' ? 'editing' : 'viewing';
797
+ const user = c.get('user');
673
798
 
799
+ if (!key) {
800
+ return c.text('No card key', 400);
801
+ }
802
+
803
+ return streamSSE(c, async (stream) => {
804
+ const connId = presenceStore.add(
805
+ key,
806
+ user,
807
+ mode,
808
+ (data) => void stream.writeSSE(data),
809
+ );
810
+
811
+ let aborted = false;
812
+ stream.onAbort(() => {
813
+ aborted = true;
814
+ presenceStore.remove(key, connId);
815
+ });
816
+
817
+ // Keep connection alive with periodic heartbeat
818
+ while (!aborted) {
819
+ await stream.write(': hb\n\n');
820
+ await stream.sleep(30000);
821
+ }
822
+ });
823
+ });
674
824
  export default router;
@@ -42,11 +42,6 @@ export async function getCardDetails(
42
42
  return { status: 400, message: `Card ${key} not found from project` };
43
43
  }
44
44
 
45
- // always parse for now if not in export mode
46
- if (!staticMode && !raw) {
47
- await commands.calculateCmd.generate();
48
- }
49
-
50
45
  let asciidocContent = '';
51
46
  try {
52
47
  asciidocContent = await evaluateMacros(
@@ -110,6 +105,7 @@ export async function getCardDetails(
110
105
  cardTypeDisplayName: cardDetailsResponse.metadata.cardType,
111
106
  workflowState: '',
112
107
  lastUpdated: cardDetailsResponse.metadata.lastUpdated,
108
+ createdAt: cardDetailsResponse.metadata.createdAt,
113
109
  fields,
114
110
  labels: cardDetailsResponse.metadata?.labels || [],
115
111
  links: [],
@@ -137,6 +133,7 @@ export async function getCardDetails(
137
133
  : await commands.calculateCmd.runQuery('card', 'localApp', {
138
134
  cardKey: key,
139
135
  });
136
+
140
137
  if (card.length !== 1) {
141
138
  throw new Error('Query failed. Check card-query syntax');
142
139
  }
@@ -0,0 +1,124 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2026
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import type { UserInfo } from '../../types.js';
15
+ import type { SSEMessage } from 'hono/streaming';
16
+
17
+ export interface PresenceEntry {
18
+ userId: string;
19
+ userName: string;
20
+ mode: 'viewing' | 'editing';
21
+ }
22
+
23
+ interface Connection {
24
+ user: UserInfo;
25
+ mode: 'viewing' | 'editing';
26
+ send: (message: SSEMessage) => void;
27
+ }
28
+
29
+ /**
30
+ * In-memory presence tracker for card editing/viewing.
31
+ * Tracks which users are currently looking at or editing each card,
32
+ * and notifies all connected clients via SSE when presence changes.
33
+ */
34
+ class PresenceStore {
35
+ // cardKey -> connectionId -> Connection
36
+ private connections = new Map<string, Map<string, Connection>>();
37
+ private nextId = 0;
38
+
39
+ /**
40
+ * Add a user connection for a card. Returns a connection ID for later removal.
41
+ */
42
+ add(
43
+ cardKey: string,
44
+ user: UserInfo,
45
+ mode: 'viewing' | 'editing',
46
+ send: (message: SSEMessage) => void,
47
+ ): string {
48
+ const connId = String(this.nextId++);
49
+
50
+ if (!this.connections.has(cardKey)) {
51
+ this.connections.set(cardKey, new Map());
52
+ }
53
+
54
+ this.connections.get(cardKey)!.set(connId, { user, mode, send });
55
+ this.broadcast(cardKey);
56
+
57
+ return connId;
58
+ }
59
+
60
+ /**
61
+ * Remove a connection and broadcast updated presence.
62
+ */
63
+ remove(cardKey: string, connId: string): void {
64
+ const cardConns = this.connections.get(cardKey);
65
+ if (!cardConns) return;
66
+
67
+ cardConns.delete(connId);
68
+
69
+ if (cardConns.size === 0) {
70
+ this.connections.delete(cardKey);
71
+ } else {
72
+ this.broadcast(cardKey);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Get current presence list for a card.
78
+ */
79
+ getPresence(cardKey: string): PresenceEntry[] {
80
+ const cardConns = this.connections.get(cardKey);
81
+ if (!cardConns) return [];
82
+
83
+ // Deduplicate by userId — if a user has multiple connections,
84
+ // prefer the 'editing' mode
85
+ const byUser = new Map<string, PresenceEntry>();
86
+ for (const conn of cardConns.values()) {
87
+ const existing = byUser.get(conn.user.id);
88
+ if (!existing || conn.mode === 'editing') {
89
+ byUser.set(conn.user.id, {
90
+ userId: conn.user.id,
91
+ userName: conn.user.name,
92
+ mode: conn.mode,
93
+ });
94
+ }
95
+ }
96
+
97
+ return Array.from(byUser.values());
98
+ }
99
+
100
+ /**
101
+ * Broadcast current presence to all connected clients for a card.
102
+ */
103
+ private broadcast(cardKey: string): void {
104
+ const cardConns = this.connections.get(cardKey);
105
+ if (!cardConns) return;
106
+
107
+ const presence = this.getPresence(cardKey);
108
+ const data = JSON.stringify({ editors: presence });
109
+ const message: SSEMessage = { event: 'presence', data };
110
+
111
+ for (const conn of cardConns.values()) {
112
+ conn.send(message);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Remove all connections. Intended for test cleanup.
118
+ */
119
+ removeAll(): void {
120
+ this.connections.clear();
121
+ }
122
+ }
123
+
124
+ export const presenceStore = new PresenceStore();
@@ -0,0 +1,41 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2026
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import { z } from 'zod';
15
+
16
+ const linkDirection = z.enum(['outbound', 'inbound']);
17
+
18
+ export const createLinkSchema = z.object({
19
+ toCard: z.string(),
20
+ linkType: z.string(),
21
+ direction: linkDirection.default('outbound'),
22
+ description: z.string().optional(),
23
+ });
24
+
25
+ export const removeLinkSchema = z.object({
26
+ toCard: z.string(),
27
+ linkType: z.string(),
28
+ direction: linkDirection.default('outbound'),
29
+ description: z.string().optional(),
30
+ });
31
+
32
+ export const updateLinkSchema = z.object({
33
+ toCard: z.string(),
34
+ linkType: z.string(),
35
+ direction: linkDirection,
36
+ description: z.string().optional(),
37
+ previousToCard: z.string(),
38
+ previousLinkType: z.string(),
39
+ previousDirection: linkDirection,
40
+ previousDescription: z.string().optional(),
41
+ });