sourcey 3.3.10 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +19 -3
  2. package/dist/client/scroll-tracker.js +8 -1
  3. package/dist/client/search.js +26 -7
  4. package/dist/components/layout/Head.d.ts.map +1 -1
  5. package/dist/components/layout/Head.js +1 -1
  6. package/dist/components/layout/Header.d.ts.map +1 -1
  7. package/dist/components/layout/Header.js +2 -2
  8. package/dist/components/layout/Page.d.ts.map +1 -1
  9. package/dist/components/layout/Page.js +6 -5
  10. package/dist/components/layout/Sidebar.d.ts.map +1 -1
  11. package/dist/components/layout/Sidebar.js +11 -2
  12. package/dist/components/layout/TableOfContents.d.ts.map +1 -1
  13. package/dist/components/layout/TableOfContents.js +3 -2
  14. package/dist/components/mcp/AnnotationBadges.d.ts +15 -0
  15. package/dist/components/mcp/AnnotationBadges.d.ts.map +1 -0
  16. package/dist/components/mcp/AnnotationBadges.js +10 -0
  17. package/dist/components/mcp/McpConnection.d.ts +10 -0
  18. package/dist/components/mcp/McpConnection.d.ts.map +1 -0
  19. package/dist/components/mcp/McpConnection.js +32 -0
  20. package/dist/components/mcp/McpEndpointBar.d.ts +8 -0
  21. package/dist/components/mcp/McpEndpointBar.d.ts.map +1 -0
  22. package/dist/components/mcp/McpEndpointBar.js +16 -0
  23. package/dist/components/mcp/McpReturns.d.ts +18 -0
  24. package/dist/components/mcp/McpReturns.d.ts.map +1 -0
  25. package/dist/components/mcp/McpReturns.js +33 -0
  26. package/dist/components/openapi/Operation.d.ts.map +1 -1
  27. package/dist/components/openapi/Operation.js +5 -1
  28. package/dist/config.d.ts +11 -0
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +12 -3
  31. package/dist/core/markdown-loader.d.ts.map +1 -1
  32. package/dist/core/markdown-loader.js +7 -2
  33. package/dist/core/mcp-normalizer.d.ts +11 -0
  34. package/dist/core/mcp-normalizer.d.ts.map +1 -0
  35. package/dist/core/mcp-normalizer.js +382 -0
  36. package/dist/core/search-indexer.d.ts +3 -1
  37. package/dist/core/search-indexer.d.ts.map +1 -1
  38. package/dist/core/search-indexer.js +7 -3
  39. package/dist/core/types.d.ts +26 -2
  40. package/dist/core/types.d.ts.map +1 -1
  41. package/dist/dev-server.d.ts.map +1 -1
  42. package/dist/dev-server.js +47 -9
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +22 -10
  45. package/dist/renderer/html-builder.d.ts +2 -0
  46. package/dist/renderer/html-builder.d.ts.map +1 -1
  47. package/dist/renderer/html-builder.js +15 -0
  48. package/dist/renderer/llms.d.ts +6 -0
  49. package/dist/renderer/llms.d.ts.map +1 -0
  50. package/dist/renderer/llms.js +247 -0
  51. package/dist/themes/default/main.css +3 -0
  52. package/dist/themes/default/sourcey.css +101 -23
  53. package/dist/utils/icons.d.ts +4 -0
  54. package/dist/utils/icons.d.ts.map +1 -1
  55. package/dist/utils/icons.js +6 -0
  56. package/dist/utils/markdown.d.ts.map +1 -1
  57. package/dist/utils/markdown.js +97 -1
  58. package/package.json +6 -2
@@ -7,6 +7,38 @@
7
7
  * utilities in generated HTML.
8
8
  */
9
9
 
10
+ /* ── Page Description (display font) ─────────────────────────────── */
11
+
12
+ @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap');
13
+
14
+ #sourcey .page-description {
15
+ font-family: 'Playfair Display', Georgia, serif;
16
+ font-size: 1.375rem;
17
+ line-height: 1.4;
18
+ font-style: italic;
19
+ color: rgb(var(--color-gray-600));
20
+ }
21
+ .dark #sourcey .page-description {
22
+ color: rgb(var(--color-gray-400));
23
+ }
24
+
25
+ /* ── Page Top Gradient ────────────────────────────────────────────── */
26
+
27
+ #sourcey #page::before {
28
+ content: "";
29
+ position: fixed;
30
+ top: 0;
31
+ left: 0;
32
+ right: 0;
33
+ height: 400px;
34
+ background: linear-gradient(to bottom, rgb(var(--color-primary) / 0.03), transparent);
35
+ pointer-events: none;
36
+ z-index: -1;
37
+ }
38
+ .dark #sourcey #page::before {
39
+ background: none;
40
+ }
41
+
10
42
  /* ── Scroll Offset (clears fixed navbar on anchor clicks) ──────────── */
11
43
 
12
44
  [data-traverse-target],
@@ -55,6 +87,43 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
55
87
  -webkit-overflow-scrolling: touch;
56
88
  }
57
89
 
90
+ /* ── Prose Video (::video directive) ──────────────────────────────── */
91
+
92
+ #sourcey .prose-video {
93
+ position: relative;
94
+ width: 100%;
95
+ padding-bottom: 56.25%; /* 16:9 */
96
+ margin: 1.5rem 0;
97
+ border-radius: var(--radius);
98
+ overflow: hidden;
99
+ }
100
+ #sourcey .prose-video iframe,
101
+ #sourcey .prose-video video {
102
+ position: absolute;
103
+ top: 0;
104
+ left: 0;
105
+ width: 100%;
106
+ height: 100%;
107
+ }
108
+
109
+ /* ── Prose Iframe (::iframe directive) ────────────────────────────── */
110
+
111
+ #sourcey .prose-iframe {
112
+ width: 100%;
113
+ margin: 1.5rem 0;
114
+ border-radius: var(--radius);
115
+ overflow: hidden;
116
+ border: 1px solid rgb(var(--color-stone-950) / 0.1);
117
+ }
118
+ .dark #sourcey .prose-iframe {
119
+ border-color: rgb(255 255 255 / 0.1);
120
+ }
121
+ #sourcey .prose-iframe iframe {
122
+ width: 100%;
123
+ height: 100%;
124
+ display: block;
125
+ }
126
+
58
127
  /* ── Prose Code Block (fenced code in markdown pages) ─────────────── */
59
128
 
60
129
  #sourcey .prose-code-block {
@@ -159,39 +228,49 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
159
228
  text-align: left;
160
229
  overflow-wrap: break-word;
161
230
  hyphens: auto;
162
- border-left: 2px solid transparent;
231
+ border-radius: 0.375rem;
163
232
  width: 100%;
164
233
  color: rgb(var(--color-gray-700));
165
- transition: color 0.15s, border-color 0.15s;
234
+ transition: color 0.15s, background-color 0.15s;
166
235
  }
167
236
  .dark #sourcey .nav-link {
168
237
  color: rgb(var(--color-gray-400));
169
238
  }
170
239
  #sourcey .nav-link:hover {
171
240
  color: rgb(var(--color-gray-900));
172
- border-left-color: rgb(var(--color-gray-300));
241
+ background: rgb(var(--color-gray-100) / 0.6);
173
242
  }
174
243
  .dark #sourcey .nav-link:hover {
175
244
  color: rgb(var(--color-gray-300));
176
- border-left-color: rgb(var(--color-gray-600));
245
+ background: rgb(var(--color-gray-800) / 0.4);
177
246
  }
178
247
  #sourcey .nav-link.active {
179
248
  color: rgb(var(--color-primary));
180
- border-left-color: rgb(var(--color-primary));
249
+ background: rgb(var(--color-primary) / 0.08);
181
250
  font-weight: 500;
182
251
  }
183
252
  .dark #sourcey .nav-link.active {
184
253
  color: rgb(var(--color-primary-light));
185
- border-left-color: rgb(var(--color-primary-light));
254
+ background: rgb(var(--color-primary-light) / 0.08);
186
255
  }
187
256
 
188
257
  /* ── TOC Active State ─────────────────────────────────────────────── */
189
258
 
259
+ #sourcey #toc .toc-item {
260
+ border-left: 2px solid rgb(var(--color-gray-200));
261
+ padding-left: 0.75rem;
262
+ transition: color 0.15s, border-color 0.15s;
263
+ }
264
+ .dark #sourcey #toc .toc-item {
265
+ border-left-color: rgb(var(--color-gray-700));
266
+ }
190
267
  #sourcey #toc .toc-item.active {
191
268
  color: rgb(var(--color-primary));
269
+ border-left-color: rgb(var(--color-primary));
192
270
  }
193
271
  .dark #sourcey #toc .toc-item.active {
194
272
  color: rgb(var(--color-primary-light));
273
+ border-left-color: rgb(var(--color-primary-light));
195
274
  }
196
275
 
197
276
  /* ── Code Block Panels (language dropdown + response tabs) ─────────── */
@@ -480,7 +559,7 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
480
559
 
481
560
  #sourcey .steps {
482
561
  margin-left: 0.875rem;
483
- margin-top: 2.5rem;
562
+ margin-top: 1rem;
484
563
  margin-bottom: 1.5rem;
485
564
  }
486
565
 
@@ -1025,11 +1104,10 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
1025
1104
  color: rgb(var(--color-primary-light));
1026
1105
  }
1027
1106
 
1028
- /* Drawer nav items — larger touch targets, no border indicator */
1107
+ /* Drawer nav items — larger touch targets */
1029
1108
  #sourcey .mobile-nav-dialog .nav-link,
1030
1109
  #sourcey .mobile-nav-dialog .nav-link:hover,
1031
1110
  #sourcey .mobile-nav-dialog .nav-link.active {
1032
- border-left: none;
1033
1111
  padding-top: 0.5rem;
1034
1112
  padding-bottom: 0.5rem;
1035
1113
  }
@@ -1080,30 +1158,25 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
1080
1158
  position: fixed;
1081
1159
  inset: 0;
1082
1160
  z-index: 500;
1083
- background: rgb(var(--color-overlay) / 0.4);
1084
- backdrop-filter: blur(4px);
1085
- -webkit-backdrop-filter: blur(4px);
1086
- align-items: flex-start;
1087
- justify-content: center;
1088
- padding-top: 15vh;
1161
+ background: transparent;
1089
1162
  }
1090
1163
 
1091
1164
  #sourcey #search-dialog.open {
1092
- display: flex;
1165
+ display: block;
1093
1166
  }
1094
1167
 
1095
1168
  #sourcey .search-dialog-inner {
1096
- width: 100%;
1097
- max-width: 540px;
1169
+ position: absolute;
1098
1170
  background: rgb(var(--color-background-light));
1099
1171
  border-radius: var(--radius);
1100
- box-shadow: 0 16px 48px rgb(var(--color-overlay) / 0.2);
1172
+ border: 1px solid rgb(var(--color-gray-200) / 0.7);
1173
+ box-shadow: 0 8px 32px rgb(var(--color-overlay) / 0.12);
1101
1174
  overflow: hidden;
1102
- margin: 0 1rem;
1103
1175
  }
1104
1176
  .dark #sourcey .search-dialog-inner {
1105
1177
  background: rgb(var(--color-gray-900));
1106
- box-shadow: 0 16px 48px rgb(var(--color-overlay) / 0.5);
1178
+ border-color: rgb(var(--color-gray-700) / 0.5);
1179
+ box-shadow: 0 8px 32px rgb(var(--color-overlay) / 0.4);
1107
1180
  }
1108
1181
 
1109
1182
  #sourcey .search-input-row {
@@ -1173,7 +1246,8 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
1173
1246
  gap: 0.25rem;
1174
1247
  }
1175
1248
 
1176
- #sourcey .search-footer kbd {
1249
+ #sourcey .search-footer kbd,
1250
+ #sourcey #search-open kbd {
1177
1251
  display: inline-flex;
1178
1252
  align-items: center;
1179
1253
  justify-content: center;
@@ -1188,7 +1262,8 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
1188
1262
  background: rgb(var(--color-gray-50));
1189
1263
  color: rgb(var(--color-gray-500));
1190
1264
  }
1191
- .dark #sourcey .search-footer kbd {
1265
+ .dark #sourcey .search-footer kbd,
1266
+ .dark #sourcey #search-open kbd {
1192
1267
  border-color: rgb(var(--color-gray-700));
1193
1268
  background: rgb(var(--color-gray-800));
1194
1269
  color: rgb(var(--color-gray-400));
@@ -1249,6 +1324,9 @@ h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
1249
1324
  #sourcey .search-result-method.method-put { background: var(--method-put); }
1250
1325
  #sourcey .search-result-method.method-delete { background: var(--method-delete); }
1251
1326
  #sourcey .search-result-method.method-patch { background: var(--method-patch); }
1327
+ #sourcey .search-result-method.method-tool { background: var(--method-tool); }
1328
+ #sourcey .search-result-method.method-resource { background: var(--method-resource); }
1329
+ #sourcey .search-result-method.method-prompt { background: var(--method-prompt); }
1252
1330
 
1253
1331
  #sourcey .search-result-path {
1254
1332
  font-family: var(--font-mono);
@@ -6,6 +6,10 @@
6
6
  * Each value is the inner path content (no wrapping <svg>).
7
7
  * Render with: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">${path}</svg>`
8
8
  */
9
+ /**
10
+ * Return raw inner SVG content for a named icon, or undefined.
11
+ */
12
+ export declare function iconPath(name: string): string | undefined;
9
13
  /**
10
14
  * Render a Heroicon as an inline SVG string.
11
15
  * Returns empty string if the icon name is not found.
@@ -1 +1 @@
1
- {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/utils/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgFH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C"}
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/utils/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgFH;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C"}
@@ -46,6 +46,12 @@ const icons = {
46
46
  info: '<path stroke-linecap="round" stroke-linejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"/>',
47
47
  warning: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"/>',
48
48
  };
49
+ /**
50
+ * Return raw inner SVG content for a named icon, or undefined.
51
+ */
52
+ export function iconPath(name) {
53
+ return icons[name];
54
+ }
49
55
  /**
50
56
  * Render a Heroicon as an inline SVG string.
51
57
  * Returns empty string if the icon name is not found.
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CASnE;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AA8BD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAe5D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/utils/markdown.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CASnE;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAgJD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAe5D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D"}
@@ -16,8 +16,104 @@ export function renderCodeBlock(text, lang) {
16
16
  <div class="prose-code-content">${shiki}</div>
17
17
  </div>`;
18
18
  }
19
+ /* ── Shared directive helpers ─────────────────────────────────────── */
20
+ function parseDirectiveAttrs(raw) {
21
+ const attrs = {};
22
+ for (const [, k, v] of raw.matchAll(/(\w+)="([^"]*)"/g))
23
+ attrs[k] = v;
24
+ return attrs;
25
+ }
26
+ function escAttr(s) {
27
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
28
+ }
29
+ function requireHttps(url) {
30
+ try {
31
+ const u = new URL(url);
32
+ if (u.protocol !== "https:" && u.protocol !== "http:")
33
+ return null;
34
+ return u.href;
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ function parseVideoUrl(url) {
41
+ // YouTube
42
+ let m = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
43
+ if (m)
44
+ return { src: `https://www.youtube-nocookie.com/embed/${m[1]}`, type: "iframe" };
45
+ // Vimeo
46
+ m = url.match(/vimeo\.com\/(\d+)/);
47
+ if (m)
48
+ return { src: `https://player.vimeo.com/video/${m[1]}`, type: "iframe" };
49
+ // Raw video
50
+ const ext = url.split(".").pop()?.toLowerCase();
51
+ const mime = ext === "webm" ? "video/webm" : "video/mp4";
52
+ return { src: url, type: "video", mime };
53
+ }
54
+ const videoExtension = {
55
+ extensions: [
56
+ {
57
+ name: "video",
58
+ level: "block",
59
+ start(src) {
60
+ return src.match(/::video\[/)?.index;
61
+ },
62
+ tokenizer(src) {
63
+ const match = src.match(/^::video\[([^\]]+)\](?:\{([^}]*)\})?/);
64
+ if (!match)
65
+ return undefined;
66
+ const attrs = parseDirectiveAttrs(match[2] ?? "");
67
+ return { type: "video", raw: match[0], url: match[1], title: attrs.title ?? "" };
68
+ },
69
+ renderer(token) {
70
+ const { url, title } = token;
71
+ const parsed = parseVideoUrl(url);
72
+ const safeTitle = escAttr(title);
73
+ if (parsed.type === "iframe") {
74
+ return `<div class="prose-video not-prose">
75
+ <iframe src="${parsed.src}" title="${safeTitle}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe>
76
+ </div>\n`;
77
+ }
78
+ return `<div class="prose-video not-prose">
79
+ <video controls preload="metadata" title="${safeTitle}">
80
+ <source src="${parsed.src}" type="${parsed.mime}" />
81
+ </video>
82
+ </div>\n`;
83
+ },
84
+ },
85
+ ],
86
+ };
87
+ const iframeExtension = {
88
+ extensions: [
89
+ {
90
+ name: "iframe",
91
+ level: "block",
92
+ start(src) {
93
+ return src.match(/::iframe\[/)?.index;
94
+ },
95
+ tokenizer(src) {
96
+ const match = src.match(/^::iframe\[([^\]]+)\](?:\{([^}]*)\})?/);
97
+ if (!match)
98
+ return undefined;
99
+ const attrs = parseDirectiveAttrs(match[2] ?? "");
100
+ const height = parseInt(attrs.height ?? "", 10);
101
+ return { type: "iframe", raw: match[0], url: match[1], title: attrs.title ?? "", height: Number.isFinite(height) ? height : 400 };
102
+ },
103
+ renderer(token) {
104
+ const { url, title, height } = token;
105
+ const safeUrl = requireHttps(url);
106
+ if (!safeUrl)
107
+ return `<p>[iframe: invalid URL]</p>\n`;
108
+ return `<div class="prose-iframe not-prose" style="height:${height}px">
109
+ <iframe src="${escAttr(safeUrl)}" title="${escAttr(title)}" frameborder="0" loading="lazy" allowfullscreen></iframe>
110
+ </div>\n`;
111
+ },
112
+ },
113
+ ],
114
+ };
19
115
  /** Singleton Marked instance — code blocks get Shiki + prose wrapper, headings get IDs. */
20
- const marked = new Marked({
116
+ const marked = new Marked(videoExtension, iframeExtension, {
21
117
  renderer: {
22
118
  code({ text, lang }) {
23
119
  return renderCodeBlock(text, lang);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sourcey",
3
- "version": "3.3.10",
3
+ "version": "3.4.1",
4
4
  "description": "Open source documentation platform. API references, guides, static output.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "scripts": {
23
23
  "build": "tsc && cp -r src/themes dist/ && cp src/client/*.js dist/client/",
24
- "dev": "tsc --watch",
24
+ "watch": "npm run build && tsc --watch --preserveWatchOutput",
25
25
  "test": "vitest run",
26
26
  "test:watch": "vitest",
27
27
  "lint": "eslint src/ test/",
@@ -39,6 +39,7 @@
39
39
  "jiti": "^2.6.1",
40
40
  "js-yaml": "^4.1.0",
41
41
  "marked": "^15.0.0",
42
+ "mcp-parser": "^0.2.0",
42
43
  "moxygen": "^2.1.1",
43
44
  "preact": "^10.28.4",
44
45
  "preact-render-to-string": "^6.6.6",
@@ -69,6 +70,9 @@
69
70
  "developer-documentation",
70
71
  "doxygen"
71
72
  ],
73
+ "funding": {
74
+ "url": "https://sourcey.com"
75
+ },
72
76
  "author": "Kam Low <oss@0state.com> (https://sourcey.com)",
73
77
  "license": "AGPL-3.0-only",
74
78
  "repository": {