@mongoosejs/studio 0.5.12 → 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.
- package/AGENTS.md +7 -0
- package/CLAUDE.md +7 -0
- package/README.md +3 -0
- package/backend/actions/ChatMessage/executeScript.js +7 -5
- package/backend/actions/ChatThread/createChatMessage.js +4 -2
- package/backend/actions/ChatThread/getChatThread.js +5 -2
- package/backend/actions/ChatThread/listChatThreads.js +3 -1
- package/backend/actions/ChatThread/shareChatThread.js +5 -2
- package/backend/actions/ChatThread/streamChatMessage.js +4 -2
- package/backend/actions/ChatThread/toggleAgentMode.js +5 -2
- package/backend/actions/Dashboard/deleteDashboard.js +7 -3
- package/backend/actions/Dashboard/getDashboard.js +21 -10
- package/backend/actions/Dashboard/getDashboards.js +3 -1
- package/backend/actions/Dashboard/updateDashboard.js +3 -2
- package/backend/actions/Model/addField.js +3 -2
- package/backend/actions/Model/analyzeSchema.js +6 -4
- package/backend/actions/Model/deleteDocument.js +3 -3
- package/backend/actions/Model/deleteDocuments.js +3 -1
- package/backend/actions/Model/executeDocumentScript.js +25 -29
- package/backend/actions/Model/exportQueryResults.js +3 -1
- package/backend/actions/Model/getCollectionInfo.js +5 -3
- package/backend/actions/Model/getDocument.js +3 -2
- package/backend/actions/Model/getDocuments.js +15 -5
- package/backend/actions/Model/getDocumentsStream.js +28 -18
- package/backend/actions/Model/getEstimatedDocumentCounts.js +5 -2
- package/backend/actions/Model/updateDocument.js +4 -2
- package/backend/actions/Model/updateDocuments.js +4 -2
- package/backend/actions/Model/validateDocument.js +5 -2
- package/backend/actions/Task/cancelTask.js +6 -3
- package/backend/actions/Task/getTaskOverview.js +3 -2
- package/backend/actions/Task/getTasks.js +3 -2
- package/backend/actions/Task/getTasksOverTime.js +3 -2
- package/backend/actions/Task/rescheduleTask.js +6 -3
- package/backend/actions/Task/runTask.js +6 -3
- package/backend/helpers/omitNullish.js +5 -0
- package/backend/index.js +4 -4
- package/backend/sandbox/createSandbox.js +2 -2
- package/backend/sandbox/createScriptDb.js +42 -19
- package/frontend/public/app.js +3210 -2706
- package/frontend/public/tw.css +68 -0
- package/frontend/src/api.js +5 -1
- package/frontend/src/chat/chat-message/chat-message.js +5 -1
- package/frontend/src/detail-default/detail-default.html +0 -10
- package/frontend/src/detail-default/detail-default.js +29 -1061
- package/frontend/src/document-details/document-property/document-property.html +0 -11
- package/frontend/src/document-details/document-property/document-property.js +6 -14
- package/frontend/src/edit-geojson/edit-geojson.html +17 -0
- package/frontend/src/edit-geojson/edit-geojson.js +1104 -0
- package/frontend/src/models/models.html +29 -8
- package/frontend/src/models/models.js +30 -2
- package/frontend/src/tooltip/tooltip.html +28 -0
- package/frontend/src/tooltip/tooltip.js +94 -0
- package/index.d.ts +1 -1
- package/local.js +3 -2
- package/nest.d.ts +1 -0
- package/package.json +1 -1
- package/seed/connect.js +4 -0
- package/seed/index.js +16 -0
- package/vercel.d.ts +48 -0
- package/vercel.js +3 -0
|
@@ -110,7 +110,18 @@
|
|
|
110
110
|
>
|
|
111
111
|
</document-search>
|
|
112
112
|
<div>
|
|
113
|
-
<
|
|
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
|
|
457
|
-
|
|
458
|
-
|
|
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
|
|
487
|
-
|
|
488
|
-
|
|
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
|
|
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
|
@@ -28,9 +28,10 @@ async function run() {
|
|
|
28
28
|
__build: true,
|
|
29
29
|
__watch: process.env.WATCH,
|
|
30
30
|
_mothershipUrl: 'http://localhost:7777/.netlify/functions',
|
|
31
|
-
apiKey: 'TACO',
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.5.
|
|
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/seed/connect.js
CHANGED
|
@@ -10,6 +10,10 @@ module.exports = async function connect() {
|
|
|
10
10
|
mongoose.model('User', new mongoose.Schema({
|
|
11
11
|
name: String,
|
|
12
12
|
email: String,
|
|
13
|
+
location: new mongoose.Schema({
|
|
14
|
+
type: { type: String },
|
|
15
|
+
coordinates: [Number]
|
|
16
|
+
}),
|
|
13
17
|
role: String,
|
|
14
18
|
plan: String,
|
|
15
19
|
status: String,
|
package/seed/index.js
CHANGED
|
@@ -20,6 +20,10 @@ async function run() {
|
|
|
20
20
|
{
|
|
21
21
|
name: 'Ada Lovelace',
|
|
22
22
|
email: 'ada@example.com',
|
|
23
|
+
location: {
|
|
24
|
+
type: 'Point',
|
|
25
|
+
coordinates: [-0.1276, 51.5072]
|
|
26
|
+
},
|
|
23
27
|
role: 'admin',
|
|
24
28
|
plan: 'enterprise',
|
|
25
29
|
status: 'active',
|
|
@@ -36,6 +40,10 @@ async function run() {
|
|
|
36
40
|
{
|
|
37
41
|
name: 'Grace Hopper',
|
|
38
42
|
email: 'grace@example.com',
|
|
43
|
+
location: {
|
|
44
|
+
type: 'Point',
|
|
45
|
+
coordinates: [-74.006, 40.7128]
|
|
46
|
+
},
|
|
39
47
|
role: 'analyst',
|
|
40
48
|
plan: 'pro',
|
|
41
49
|
status: 'active',
|
|
@@ -52,6 +60,10 @@ async function run() {
|
|
|
52
60
|
{
|
|
53
61
|
name: 'Linus Torvalds',
|
|
54
62
|
email: 'linus@example.com',
|
|
63
|
+
location: {
|
|
64
|
+
type: 'Point',
|
|
65
|
+
coordinates: [24.9384, 60.1699]
|
|
66
|
+
},
|
|
55
67
|
role: 'editor',
|
|
56
68
|
plan: 'starter',
|
|
57
69
|
status: 'invited',
|
|
@@ -68,6 +80,10 @@ async function run() {
|
|
|
68
80
|
{
|
|
69
81
|
name: 'Margaret Hamilton',
|
|
70
82
|
email: 'margaret@example.com',
|
|
83
|
+
location: {
|
|
84
|
+
type: 'Point',
|
|
85
|
+
coordinates: [-71.1097, 42.3736]
|
|
86
|
+
},
|
|
71
87
|
role: 'viewer',
|
|
72
88
|
plan: 'pro',
|
|
73
89
|
status: 'inactive',
|
package/vercel.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
declare module '@mongoosejs/studio/vercel' {
|
|
2
|
+
import { Connection, Mongoose } from 'mongoose';
|
|
3
|
+
|
|
4
|
+
export interface VercelAPIRouteHandlerOptions {
|
|
5
|
+
/** Mongoose Studio Pro API key. When set, requests must be authorized. */
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
/** Separate connection for Studio's own collections (dashboards, chat threads). */
|
|
8
|
+
studioConnection?: Connection;
|
|
9
|
+
/** Watch the connection for changes and stream them to the Studio frontend. */
|
|
10
|
+
changeStream?: boolean;
|
|
11
|
+
/** Maximum execution time in milliseconds for MongoDB operations. */
|
|
12
|
+
maxTimeMS?: number;
|
|
13
|
+
/** Chat model name, e.g. 'gpt-4o-mini' */
|
|
14
|
+
model?: string;
|
|
15
|
+
openAIAPIKey?: string;
|
|
16
|
+
anthropicAPIKey?: string;
|
|
17
|
+
googleGeminiAPIKey?: string;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Minimal request shape: satisfied by Next.js API routes (NextApiRequest),
|
|
23
|
+
* Express, and other Node server frameworks with query/body parsing.
|
|
24
|
+
*/
|
|
25
|
+
export interface VercelAPIRouteHandlerRequest {
|
|
26
|
+
query?: Record<string, any>;
|
|
27
|
+
body?: any;
|
|
28
|
+
params?: Record<string, any>;
|
|
29
|
+
headers: { authorization?: string | null; [key: string]: any };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Minimal response shape: satisfied by NextApiResponse and Express responses. */
|
|
33
|
+
export interface VercelAPIRouteHandlerResponse {
|
|
34
|
+
status(code: number): any;
|
|
35
|
+
json(body: any): any;
|
|
36
|
+
send(body: any): any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates a Mongoose Studio API handler for Next.js Pages Router API routes
|
|
41
|
+
* (`pages/api/studio.js`) and other Vercel-style serverless functions.
|
|
42
|
+
* Pass `null` to use the default Mongoose global.
|
|
43
|
+
*/
|
|
44
|
+
export function vercelAPIRouteHandler(
|
|
45
|
+
conn?: Connection | Mongoose | null,
|
|
46
|
+
options?: VercelAPIRouteHandlerOptions
|
|
47
|
+
): (req: VercelAPIRouteHandlerRequest, res: VercelAPIRouteHandlerResponse) => Promise<any>;
|
|
48
|
+
}
|
package/vercel.js
ADDED