@mongoosejs/studio 0.5.13 → 0.5.14

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 (50) hide show
  1. package/AGENTS.md +7 -0
  2. package/CLAUDE.md +7 -0
  3. package/README.md +3 -0
  4. package/backend/actions/ChatMessage/executeScript.js +7 -5
  5. package/backend/actions/ChatThread/createChatMessage.js +4 -2
  6. package/backend/actions/ChatThread/getChatThread.js +5 -2
  7. package/backend/actions/ChatThread/listChatThreads.js +3 -1
  8. package/backend/actions/ChatThread/shareChatThread.js +5 -2
  9. package/backend/actions/ChatThread/streamChatMessage.js +4 -2
  10. package/backend/actions/ChatThread/toggleAgentMode.js +5 -2
  11. package/backend/actions/Dashboard/deleteDashboard.js +7 -3
  12. package/backend/actions/Dashboard/getDashboard.js +21 -10
  13. package/backend/actions/Dashboard/getDashboards.js +3 -1
  14. package/backend/actions/Dashboard/updateDashboard.js +3 -2
  15. package/backend/actions/Model/addField.js +3 -2
  16. package/backend/actions/Model/analyzeSchema.js +6 -4
  17. package/backend/actions/Model/deleteDocument.js +3 -3
  18. package/backend/actions/Model/deleteDocuments.js +3 -1
  19. package/backend/actions/Model/executeDocumentScript.js +25 -29
  20. package/backend/actions/Model/exportQueryResults.js +3 -1
  21. package/backend/actions/Model/getCollectionInfo.js +5 -3
  22. package/backend/actions/Model/getDocument.js +3 -2
  23. package/backend/actions/Model/getDocuments.js +15 -5
  24. package/backend/actions/Model/getDocumentsStream.js +28 -18
  25. package/backend/actions/Model/getEstimatedDocumentCounts.js +5 -2
  26. package/backend/actions/Model/updateDocument.js +4 -2
  27. package/backend/actions/Model/updateDocuments.js +4 -2
  28. package/backend/actions/Model/validateDocument.js +5 -2
  29. package/backend/actions/Task/cancelTask.js +6 -3
  30. package/backend/actions/Task/getTaskOverview.js +3 -2
  31. package/backend/actions/Task/getTasks.js +3 -2
  32. package/backend/actions/Task/getTasksOverTime.js +3 -2
  33. package/backend/actions/Task/rescheduleTask.js +6 -3
  34. package/backend/actions/Task/runTask.js +6 -3
  35. package/backend/helpers/omitNullish.js +5 -0
  36. package/backend/index.js +4 -4
  37. package/backend/sandbox/createSandbox.js +2 -2
  38. package/backend/sandbox/createScriptDb.js +42 -19
  39. package/frontend/public/app.js +319 -96
  40. package/frontend/public/tw.css +68 -0
  41. package/frontend/src/api.js +5 -1
  42. package/frontend/src/models/models.html +29 -8
  43. package/frontend/src/models/models.js +30 -2
  44. package/frontend/src/tooltip/tooltip.html +28 -0
  45. package/frontend/src/tooltip/tooltip.js +94 -0
  46. package/index.d.ts +1 -1
  47. package/local.js +2 -1
  48. package/nest.d.ts +1 -0
  49. package/package.json +1 -1
  50. package/vercel.d.ts +2 -0
@@ -664,6 +664,10 @@ video {
664
664
  bottom: 0px;
665
665
  }
666
666
 
667
+ .-bottom-1 {
668
+ bottom: -0.25rem;
669
+ }
670
+
667
671
  .-left-1 {
668
672
  left: -0.25rem;
669
673
  }
@@ -672,6 +676,10 @@ video {
672
676
  left: -0.75rem;
673
677
  }
674
678
 
679
+ .-top-1 {
680
+ top: -0.25rem;
681
+ }
682
+
675
683
  .bottom-0 {
676
684
  bottom: 0px;
677
685
  }
@@ -792,6 +800,10 @@ video {
792
800
  z-index: 10001;
793
801
  }
794
802
 
803
+ .z-\[1000\] {
804
+ z-index: 1000;
805
+ }
806
+
795
807
  .z-\[100\] {
796
808
  z-index: 100;
797
809
  }
@@ -1085,6 +1097,10 @@ video {
1085
1097
  height: 4rem;
1086
1098
  }
1087
1099
 
1100
+ .h-2 {
1101
+ height: 0.5rem;
1102
+ }
1103
+
1088
1104
  .h-20 {
1089
1105
  height: 5rem;
1090
1106
  }
@@ -1265,6 +1281,10 @@ video {
1265
1281
  width: 4rem;
1266
1282
  }
1267
1283
 
1284
+ .w-2 {
1285
+ width: 0.5rem;
1286
+ }
1287
+
1268
1288
  .w-20 {
1269
1289
  width: 5rem;
1270
1290
  }
@@ -1365,6 +1385,11 @@ video {
1365
1385
  width: 100%;
1366
1386
  }
1367
1387
 
1388
+ .w-max {
1389
+ width: -moz-max-content;
1390
+ width: max-content;
1391
+ }
1392
+
1368
1393
  .min-w-0 {
1369
1394
  min-width: 0px;
1370
1395
  }
@@ -1441,6 +1466,10 @@ video {
1441
1466
  max-width: calc(100vw - 4rem);
1442
1467
  }
1443
1468
 
1469
+ .max-w-\[min\(20rem\2c calc\(100vw-1rem\)\)\] {
1470
+ max-width: min(20rem,calc(100vw - 1rem));
1471
+ }
1472
+
1444
1473
  .max-w-full {
1445
1474
  max-width: 100%;
1446
1475
  }
@@ -1555,6 +1584,11 @@ video {
1555
1584
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1556
1585
  }
1557
1586
 
1587
+ .rotate-45 {
1588
+ --tw-rotate: 45deg;
1589
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1590
+ }
1591
+
1558
1592
  .rotate-90 {
1559
1593
  --tw-rotate: 90deg;
1560
1594
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@@ -1608,6 +1642,10 @@ video {
1608
1642
  resize: vertical;
1609
1643
  }
1610
1644
 
1645
+ .resize {
1646
+ resize: both;
1647
+ }
1648
+
1611
1649
  .snap-y {
1612
1650
  scroll-snap-type: y var(--tw-scroll-snap-strictness);
1613
1651
  }
@@ -1861,6 +1899,10 @@ video {
1861
1899
  text-overflow: ellipsis;
1862
1900
  }
1863
1901
 
1902
+ .whitespace-normal {
1903
+ white-space: normal;
1904
+ }
1905
+
1864
1906
  .whitespace-nowrap {
1865
1907
  white-space: nowrap;
1866
1908
  }
@@ -2184,6 +2226,11 @@ video {
2184
2226
  background-color: rgb(55 65 81 / var(--tw-bg-opacity));
2185
2227
  }
2186
2228
 
2229
+ .bg-gray-900 {
2230
+ --tw-bg-opacity: 1;
2231
+ background-color: rgb(17 24 39 / var(--tw-bg-opacity));
2232
+ }
2233
+
2187
2234
  .bg-green-100 {
2188
2235
  --tw-bg-opacity: 1;
2189
2236
  background-color: rgb(220 252 231 / var(--tw-bg-opacity));
@@ -2747,6 +2794,10 @@ video {
2747
2794
  font-weight: 500;
2748
2795
  }
2749
2796
 
2797
+ .font-normal {
2798
+ font-weight: 400;
2799
+ }
2800
+
2750
2801
  .font-semibold {
2751
2802
  font-weight: 600;
2752
2803
  }
@@ -2763,6 +2814,10 @@ video {
2763
2814
  font-style: italic;
2764
2815
  }
2765
2816
 
2817
+ .leading-4 {
2818
+ line-height: 1rem;
2819
+ }
2820
+
2766
2821
  .leading-6 {
2767
2822
  line-height: 1.5rem;
2768
2823
  }
@@ -3216,6 +3271,11 @@ video {
3216
3271
  --tw-ring-color: rgb(176 32 43 / var(--tw-ring-opacity));
3217
3272
  }
3218
3273
 
3274
+ .ring-red-200 {
3275
+ --tw-ring-opacity: 1;
3276
+ --tw-ring-color: rgb(254 202 202 / var(--tw-ring-opacity));
3277
+ }
3278
+
3219
3279
  .ring-red-400 {
3220
3280
  --tw-ring-opacity: 1;
3221
3281
  --tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity));
@@ -3837,6 +3897,10 @@ video {
3837
3897
  --tw-ring-offset-width: 2px;
3838
3898
  }
3839
3899
 
3900
+ .focus-visible\:rounded-md:focus-visible {
3901
+ border-radius: 0.375rem;
3902
+ }
3903
+
3840
3904
  .focus-visible\:opacity-100:focus-visible {
3841
3905
  opacity: 1;
3842
3906
  }
@@ -3853,6 +3917,10 @@ video {
3853
3917
  outline-offset: 2px;
3854
3918
  }
3855
3919
 
3920
+ .focus-visible\:outline-blue-600:focus-visible {
3921
+ outline-color: #2563eb;
3922
+ }
3923
+
3856
3924
  .focus-visible\:outline-edge-strong:focus-visible {
3857
3925
  outline-color: var(--color-edge-strong);
3858
3926
  }
@@ -229,7 +229,11 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
229
229
  getDocumentsStream: async function* getDocumentsStream(params) {
230
230
  const data = await client.post('', { action: 'Model.getDocuments', ...params }).then(res => res.data);
231
231
  yield { schemaPaths: data.schemaPaths, suggestedFields: data.suggestedFields };
232
- yield { numDocs: data.numDocs };
232
+ if (data.numDocsError) {
233
+ yield { numDocsError: data.numDocsError };
234
+ } else {
235
+ yield { numDocs: data.numDocs };
236
+ }
233
237
  for (const doc of data.docs) {
234
238
  yield { document: doc };
235
239
  }
@@ -110,7 +110,18 @@
110
110
  >
111
111
  </document-search>
112
112
  <div>
113
- <span v-if="numDocuments == null">Loading ...</span>
113
+ <tooltip
114
+ v-if="numDocumentsError"
115
+ :text="'Error counting documents: ' + numDocumentsError"
116
+ >
117
+ <span
118
+ class="inline-flex rounded-md bg-red-50 px-2 py-2 text-sm text-red-700 ring-1 ring-inset ring-red-200"
119
+ role="alert"
120
+ >
121
+ Count Error
122
+ </span>
123
+ </tooltip>
124
+ <span v-else-if="numDocuments == null">Loading...</span>
114
125
  <span v-else-if="typeof numDocuments === 'number'">{{documents.length}}/{{numDocuments === 1 ? numDocuments + ' document' : numDocuments + ' documents'}}</span>
115
126
  </div>
116
127
  <button
@@ -453,11 +464,16 @@
453
464
  </tbody>
454
465
  </table>
455
466
  </div>
456
- <div v-if="outputType === 'table' && (loadingMore || (status === 'loading' && documents.length > 0))" class="flex items-center justify-center gap-2 py-3 text-sm text-content-tertiary border-t border-edge bg-surface">
457
- <img src="images/loader.gif" alt="" class="h-5 w-5">
458
- <span>Loading documents…</span>
467
+ <div
468
+ v-if="outputType === 'table' && (documentsError || loadingMore || (status === 'loading' && documents.length > 0))"
469
+ class="flex items-center justify-center gap-2 py-3 text-sm border-t"
470
+ :class="documentsError ? 'border-red-200 bg-red-50 text-red-700' : 'border-edge bg-surface text-content-tertiary'"
471
+ :role="documentsError ? 'alert' : null"
472
+ >
473
+ <img v-if="!documentsError" src="images/loader.gif" alt="" class="h-5 w-5">
474
+ <span>{{documentsError || 'Loading documents…'}}</span>
459
475
  </div>
460
- <p v-if="outputType === 'table' && documents.length === 0 && status === 'loaded'" class="mt-2 text-sm text-content-tertiary px-1">
476
+ <p v-if="outputType === 'table' && documents.length === 0 && status === 'loaded' && !documentsError" class="mt-2 text-sm text-content-tertiary px-1">
461
477
  No documents to show. Use Projection in the menu to choose columns.
462
478
  </p>
463
479
  </div>
@@ -483,9 +499,14 @@
483
499
  <list-json :value="filterDocument(document)" :references="referenceMap">
484
500
  </list-json>
485
501
  </div>
486
- <div v-if="outputType === 'json' && (loadingMore || (status === 'loading' && documents.length > 0))" class="flex items-center justify-center gap-2 py-3 text-sm text-content-tertiary">
487
- <img src="images/loader.gif" alt="" class="h-5 w-5">
488
- <span>Loading documents…</span>
502
+ <div
503
+ v-if="outputType === 'json' && (documentsError || loadingMore || (status === 'loading' && documents.length > 0))"
504
+ class="flex items-center justify-center gap-2 py-3 text-sm"
505
+ :class="documentsError ? 'rounded-md border border-red-200 bg-red-50 text-red-700' : 'text-content-tertiary'"
506
+ :role="documentsError ? 'alert' : null"
507
+ >
508
+ <img v-if="!documentsError" src="images/loader.gif" alt="" class="h-5 w-5">
509
+ <span>{{documentsError || 'Loading documents…'}}</span>
489
510
  </div>
490
511
  </div>
491
512
  <div v-else-if="outputType === 'map'" class="flex flex-col h-full">
@@ -151,6 +151,8 @@ module.exports = app => app.component('models', {
151
151
  filteredPaths: [],
152
152
  selectedPaths: [],
153
153
  numDocuments: null,
154
+ numDocumentsError: null,
155
+ documentsError: null,
154
156
  mongoDBIndexes: [],
155
157
  schemaIndexes: [],
156
158
  status: 'loading',
@@ -838,7 +840,8 @@ module.exports = app => app.component('models', {
838
840
  return filteredDoc;
839
841
  },
840
842
  async checkIfScrolledToBottom() {
841
- if (this.status === 'loading' || this.loadedAllDocs) {
843
+ // Stop infinite scroll if the last load failed, otherwise we would retry the same failing request forever.
844
+ if (this.status === 'loading' || this.loadedAllDocs || this.documentsError) {
842
845
  return;
843
846
  }
844
847
  // Infinite scroll only applies to table/json views.
@@ -864,11 +867,20 @@ module.exports = app => app.component('models', {
864
867
  try {
865
868
  const skip = this.documents.length;
866
869
  const params = this.buildDocumentFetchParams({ skip });
867
- const { docs } = await api.Model.getDocuments(params);
870
+ const { docs, numDocs, numDocsError } = await api.Model.getDocuments(params);
871
+ if (numDocsError) {
872
+ this.numDocumentsError = numDocsError;
873
+ } else if (numDocs !== undefined) {
874
+ this.numDocuments = numDocs;
875
+ this.numDocumentsError = null;
876
+ }
868
877
  if (docs.length < limit) {
869
878
  this.loadedAllDocs = true;
870
879
  }
871
880
  this.documents.push(...docs);
881
+ this.documentsError = null;
882
+ } catch (err) {
883
+ this.documentsError = err?.message;
872
884
  } finally {
873
885
  this.loadingMore = false;
874
886
  this.status = 'loaded';
@@ -1081,6 +1093,8 @@ module.exports = app => app.component('models', {
1081
1093
  // Clear previous data
1082
1094
  this.documents = [];
1083
1095
  this.numDocuments = null;
1096
+ this.numDocumentsError = null;
1097
+ this.documentsError = null;
1084
1098
  this.loadedAllDocs = false;
1085
1099
  this.lastSelectedIndex = null;
1086
1100
 
@@ -1130,6 +1144,10 @@ module.exports = app => app.component('models', {
1130
1144
  }
1131
1145
  if (event.numDocs !== undefined) {
1132
1146
  this.numDocuments = event.numDocs;
1147
+ this.numDocumentsError = null;
1148
+ }
1149
+ if (event.numDocsError) {
1150
+ this.numDocumentsError = event.numDocsError;
1133
1151
  }
1134
1152
  if (event.document) {
1135
1153
  this.documents.push(event.document);
@@ -1143,6 +1161,9 @@ module.exports = app => app.component('models', {
1143
1161
  if (docsCount < limit) {
1144
1162
  this.loadedAllDocs = true;
1145
1163
  }
1164
+ this.documentsError = null;
1165
+ } catch (err) {
1166
+ this.documentsError = err?.message;
1146
1167
  } finally {
1147
1168
  this.status = 'loaded';
1148
1169
  }
@@ -1169,8 +1190,12 @@ module.exports = app => app.component('models', {
1169
1190
  for await (const event of api.Model.getDocumentsStream(params)) {
1170
1191
  if (event.numDocs !== undefined && !numDocsReceived) {
1171
1192
  this.numDocuments = event.numDocs;
1193
+ this.numDocumentsError = null;
1172
1194
  numDocsReceived = true;
1173
1195
  }
1196
+ if (event.numDocsError) {
1197
+ this.numDocumentsError = event.numDocsError;
1198
+ }
1174
1199
  if (event.document) {
1175
1200
  this.documents.push(event.document);
1176
1201
  docsCount++;
@@ -1183,6 +1208,9 @@ module.exports = app => app.component('models', {
1183
1208
  if (docsCount < limit) {
1184
1209
  this.loadedAllDocs = true;
1185
1210
  }
1211
+ this.documentsError = null;
1212
+ } catch (err) {
1213
+ this.documentsError = err?.message;
1186
1214
  } finally {
1187
1215
  this.loadingMore = false;
1188
1216
  this.status = 'loaded';
@@ -0,0 +1,28 @@
1
+ <span
2
+ ref="trigger"
3
+ class="inline-flex outline-none focus-visible:rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
4
+ tabindex="0"
5
+ :aria-describedby="isOpen ? tooltipId : null"
6
+ @mouseenter="show"
7
+ @mouseleave="hideUnlessPinned"
8
+ @focusin="show"
9
+ @focusout="handleFocusOut"
10
+ @click="toggle"
11
+ @keydown.esc.stop="hide"
12
+ >
13
+ <slot></slot>
14
+ <span
15
+ v-show="isOpen"
16
+ ref="tooltip"
17
+ :id="tooltipId"
18
+ class="pointer-events-none fixed z-[1000] w-max max-w-[min(20rem,calc(100vw-1rem))] whitespace-normal rounded-md bg-gray-900 px-2.5 py-1.5 text-xs font-normal leading-4 text-white shadow-lg"
19
+ :style="tooltipStyle"
20
+ role="tooltip"
21
+ >
22
+ {{ text }}
23
+ <span
24
+ class="absolute left-1/2 h-2 w-2 -translate-x-1/2 rotate-45 bg-gray-900"
25
+ :class="placement === 'bottom' ? '-top-1' : '-bottom-1'"
26
+ ></span>
27
+ </span>
28
+ </span>
@@ -0,0 +1,94 @@
1
+ 'use strict';
2
+
3
+ const template = require('./tooltip.html');
4
+
5
+ let nextTooltipId = 0;
6
+
7
+ module.exports = app => app.component('tooltip', {
8
+ props: {
9
+ text: {
10
+ type: String,
11
+ required: true
12
+ }
13
+ },
14
+ template,
15
+ data() {
16
+ return {
17
+ isOpen: false,
18
+ isPinned: false,
19
+ placement: 'bottom',
20
+ tooltipId: `tooltip-${++nextTooltipId}`,
21
+ tooltipStyle: {
22
+ visibility: 'hidden'
23
+ }
24
+ };
25
+ },
26
+ mounted() {
27
+ document.addEventListener('click', this.handleOutsideClick);
28
+ window.addEventListener('resize', this.updatePosition);
29
+ window.addEventListener('scroll', this.updatePosition, true);
30
+ },
31
+ beforeUnmount() {
32
+ document.removeEventListener('click', this.handleOutsideClick);
33
+ window.removeEventListener('resize', this.updatePosition);
34
+ window.removeEventListener('scroll', this.updatePosition, true);
35
+ },
36
+ methods: {
37
+ show() {
38
+ this.isOpen = true;
39
+ this.tooltipStyle = { visibility: 'hidden' };
40
+ this.$nextTick(this.updatePosition);
41
+ },
42
+ hide() {
43
+ this.isOpen = false;
44
+ this.isPinned = false;
45
+ },
46
+ hideUnlessPinned() {
47
+ if (!this.isPinned) {
48
+ this.isOpen = false;
49
+ }
50
+ },
51
+ toggle() {
52
+ if (this.isPinned) {
53
+ this.hide();
54
+ return;
55
+ }
56
+
57
+ this.isPinned = true;
58
+ this.show();
59
+ },
60
+ handleFocusOut(event) {
61
+ if (!this.$el.contains(event.relatedTarget)) {
62
+ this.hideUnlessPinned();
63
+ }
64
+ },
65
+ handleOutsideClick(event) {
66
+ if (!this.$el.contains(event.target)) {
67
+ this.hide();
68
+ }
69
+ },
70
+ updatePosition() {
71
+ if (!this.isOpen || !this.$refs.trigger || !this.$refs.tooltip) {
72
+ return;
73
+ }
74
+
75
+ const gap = 8;
76
+ const viewportPadding = 8;
77
+ const triggerRect = this.$refs.trigger.getBoundingClientRect();
78
+ const tooltipRect = this.$refs.tooltip.getBoundingClientRect();
79
+ const fitsBelow = triggerRect.bottom + gap + tooltipRect.height <= window.innerHeight - viewportPadding;
80
+ const top = fitsBelow ?
81
+ triggerRect.bottom + gap :
82
+ Math.max(viewportPadding, triggerRect.top - gap - tooltipRect.height);
83
+ const centeredLeft = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
84
+ const maxLeft = Math.max(viewportPadding, window.innerWidth - tooltipRect.width - viewportPadding);
85
+ const left = Math.min(Math.max(viewportPadding, centeredLeft), maxLeft);
86
+
87
+ this.placement = fitsBelow ? 'bottom' : 'top';
88
+ this.tooltipStyle = {
89
+ left: `${Math.round(left)}px`,
90
+ top: `${Math.round(top)}px`
91
+ };
92
+ }
93
+ }
94
+ });
package/index.d.ts CHANGED
@@ -5,7 +5,7 @@ declare module '@mongoosejs/studio' {
5
5
  const express: (
6
6
  path?: string,
7
7
  connOrMongoose?: Connection | Mongoose,
8
- options?: { apiKey?: string; bindIp?: string | string[] | null }
8
+ options?: { apiKey?: string; bindIp?: string | string[] | null; maxTimeMS?: number }
9
9
  ) => Promise<RequestHandler>;
10
10
 
11
11
  const studio: {
package/local.js CHANGED
@@ -30,7 +30,8 @@ async function run() {
30
30
  _mothershipUrl: 'http://localhost:7777/.netlify/functions',
31
31
  // apiKey: 'TACO',
32
32
  openAIAPIKey: process.env.OPENAI_API_KEY,
33
- googleGeminiAPIKey: process.env.GEMINI_API_KEY
33
+ googleGeminiAPIKey: process.env.GEMINI_API_KEY,
34
+ maxTimeMS: 1000
34
35
  })
35
36
  );
36
37
 
package/nest.d.ts CHANGED
@@ -9,6 +9,7 @@ declare module '@mongoosejs/studio/nest' {
9
9
  connectionToken?: string | symbol | Function;
10
10
  apiKey?: string;
11
11
  bindIp?: string | string[] | null;
12
+ maxTimeMS?: number;
12
13
  [key: string]: any;
13
14
  }
14
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.5.13",
3
+ "version": "0.5.14",
4
4
  "description": "AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.",
5
5
  "homepage": "https://mongoosestudio.app/",
6
6
  "repository": {
package/vercel.d.ts CHANGED
@@ -8,6 +8,8 @@ declare module '@mongoosejs/studio/vercel' {
8
8
  studioConnection?: Connection;
9
9
  /** Watch the connection for changes and stream them to the Studio frontend. */
10
10
  changeStream?: boolean;
11
+ /** Maximum execution time in milliseconds for MongoDB operations. */
12
+ maxTimeMS?: number;
11
13
  /** Chat model name, e.g. 'gpt-4o-mini' */
12
14
  model?: string;
13
15
  openAIAPIKey?: string;