@mongoosejs/studio 0.0.97 → 0.0.99

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.
@@ -16,49 +16,13 @@ const CreateChatMessageParams = new Archetype({
16
16
  $type: String
17
17
  },
18
18
  authorization: {
19
- $type: 'string',
20
- $required: true
19
+ $type: 'string'
21
20
  },
22
21
  roles: {
23
22
  $type: ['string']
24
23
  }
25
24
  }).compile('CreateChatMessageParams');
26
25
 
27
- const systemPrompt = `
28
- You are a data querying assistant who writes scripts for users accessing MongoDB data using Node.js and Mongoose.
29
-
30
- Keep scripts concise. Avoid unnecessary comments, error handling, and temporary variables.
31
-
32
- Do not write any imports or require() statements, that will cause the script to break.
33
-
34
- If the user approves the script, the script will run in the Node.js server and then send the response via JSON to the client. Be aware that the result of the query will be serialized to JSON before being displayed to the user.
35
-
36
- Assume the user has pre-defined schemas and models. Do not define any new schemas or models for the user.
37
-
38
- Use async/await where possible. Assume top-level await is allowed.
39
-
40
- Think carefully about the user's input and identify the models referred to by the user's query.
41
-
42
- Format output as Markdown, including code fences for any scripts the user requested.
43
-
44
- Add a brief text description of what the script does.
45
-
46
- If the user's query is best answered with a chart, return a Chart.js 4 configuration as \`return { $chart: chartJSConfig };\`. Disable ChartJS animation by default unless user asks for it. Set responsive: true, maintainAspectRatio: false options unless the user explicitly asks.
47
-
48
- Example output:
49
-
50
- The following script counts the number of users which are not deleted.
51
-
52
- \`\`\`javascript
53
- const users = await db.model('User').find({ isDeleted: false });
54
- return { numUsers: users.length };
55
- \`\`\`
56
-
57
- -----------
58
-
59
- Here is a description of the user's models. Assume these are the only models available in the system unless explicitly instructed otherwise by the user.
60
- `.trim();
61
-
62
26
  module.exports = ({ db, studioConnection, options }) => async function createChatMessage(params) {
63
27
  const { chatThreadId, userId, content, script, authorization, roles } = new CreateChatMessageParams(params);
64
28
  const ChatThread = studioConnection.model('__Studio_ChatThread');
@@ -83,20 +47,13 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
83
47
  llmMessages.push({ role: 'user', content });
84
48
 
85
49
  if (chatThread.title == null) {
86
- getChatCompletion([
87
- { role: 'system', content: 'Summarize the following chat thread in 6 words or less, as a helpful thread title' },
88
- ...llmMessages
89
- ], authorization).then(res => {
50
+ summarizeChatThread(llmMessages).then(res => {
90
51
  const title = res.response;
91
52
  chatThread.title = title;
92
53
  return chatThread.save();
93
54
  }).catch(() => {});
94
55
  }
95
56
 
96
- llmMessages.unshift({
97
- role: 'system',
98
- content: systemPrompt + getModelDescriptions(db)
99
- });
100
57
  if (options.context) {
101
58
  llmMessages.unshift({
102
59
  role: 'system',
@@ -113,7 +70,7 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
113
70
  script,
114
71
  executionResult: null
115
72
  }),
116
- getChatCompletion(llmMessages, authorization).then(res => {
73
+ createChatMessageCore(llmMessages, getModelDescriptions(db), authorization).then(res => {
117
74
  const content = res.response;
118
75
  return ChatMessage.create({
119
76
  chatThreadId,
@@ -126,16 +83,44 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
126
83
  return { chatMessages, chatThread };
127
84
  };
128
85
 
129
- async function getChatCompletion(messages, authorization) {
130
- const response = await fetch('https://mongoose-js.netlify.app/.netlify/functions/createChatMessage', {
86
+ async function summarizeChatThread(messages, authorization) {
87
+ const headers = { 'Content-Type': 'application/json' };
88
+ if (authorization) {
89
+ headers.Authorization = authorization;
90
+ }
91
+ const response = await fetch('https://mongoose-js.netlify.app/.netlify/functions/summarizeChatThread', {
131
92
  method: 'POST',
132
- headers: {
133
- Authorization: authorization,
134
- 'Content-Type': 'application/json'
135
- },
93
+ headers,
136
94
  body: JSON.stringify({
137
95
  messages
138
96
  })
97
+ }).then(response => {
98
+ if (response.status < 200 || response.status >= 400) {
99
+ return response.json().then(data => {
100
+ throw new Error(`Mongoose Studio chat thread summarization error: ${data.message}`);
101
+ });
102
+ }
103
+ return response;
104
+ });
105
+
106
+ return await response.json().then(res => {
107
+ console.log('Response', res);
108
+ return res;
109
+ });
110
+ }
111
+
112
+ async function createChatMessageCore(messages, modelDescriptions, authorization) {
113
+ const headers = { 'Content-Type': 'application/json' };
114
+ if (authorization) {
115
+ headers.Authorization = authorization;
116
+ }
117
+ const response = await fetch('https://mongoose-js.netlify.app/.netlify/functions/createChatMessage', {
118
+ method: 'POST',
119
+ headers,
120
+ body: JSON.stringify({
121
+ messages,
122
+ modelDescriptions
123
+ })
139
124
  }).then(response => {
140
125
  if (response.status < 200 || response.status >= 400) {
141
126
  return response.json().then(data => {
@@ -9,7 +9,7 @@ const DeleteDocumentsParams = new Archetype({
9
9
  $required: true
10
10
  },
11
11
  documentIds: {
12
- $type: 'string',
12
+ $type: ['string'],
13
13
  $required: true
14
14
  },
15
15
  roles: {
@@ -30,9 +30,7 @@ module.exports = ({ db }) => async function DeleteDocuments(params) {
30
30
 
31
31
  await Model.
32
32
  deleteMany({ _id: { $in: documentIds } }).
33
- setOptions({ sanitizeFilter: true }).
34
33
  orFail();
35
34
 
36
-
37
35
  return { };
38
36
  };
@@ -14,6 +14,7 @@ const actionsToRequiredRoles = {
14
14
  'Model.createDocument': ['owner', 'admin', 'member'],
15
15
  'Model.updateDocument': ['owner', 'admin', 'member'],
16
16
  'Model.deleteDocument': ['owner', 'admin', 'member'],
17
+ 'Model.deleteDocuments': ['owner', 'admin', 'member'],
17
18
  'Model.dropIndex': ['owner', 'admin'],
18
19
  'Model.exportQueryResults': ['owner', 'admin', 'member', 'readonly'],
19
20
  'Model.getDocument': ['owner', 'admin', 'member', 'readonly'],
@@ -1771,6 +1771,9 @@ if (typeof process === 'undefined') {
1771
1771
  __webpack_require__.g.process = { env: {} }; // To make `util` package work
1772
1772
  }
1773
1773
 
1774
+ const { version } = __webpack_require__(/*! ../../package.json */ "./package.json");
1775
+ console.log(`Mongoose Studio Version ${version}`);
1776
+
1774
1777
  const api = __webpack_require__(/*! ./api */ "./frontend/src/api.js");
1775
1778
  const mothership = __webpack_require__(/*! ./mothership */ "./frontend/src/mothership.js");
1776
1779
  const { routes } = __webpack_require__(/*! ./routes */ "./frontend/src/routes.js");
@@ -4189,7 +4192,7 @@ module.exports = ".document {\n max-width: 1200px;\n margin-left: auto;\n mar
4189
4192
  /***/ ((module) => {
4190
4193
 
4191
4194
  "use strict";
4192
- module.exports = "<div class=\"document\">\n <div class=\"document-menu\">\n <div class=\"left\">\n <button\n @click=\"$router.push('/model/' + this.model)\"\n class=\"rounded-md bg-gray-400 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &lsaquo; Back\n </button>\n </div>\n\n <div class=\"right\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &times; Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n <button\n @click=\"shouldShowDeleteModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/delete.svg\" class=\"inline\" /> Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-pink-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/duplicate.svg\" class=\"inline\" /> Clone\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">&times;</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">&times;</div>\n <confirm-delete @close=\"shouldShowConfirmModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">&times;</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n </div>\n</div>\n";
4195
+ module.exports = "<div class=\"document\">\n <div class=\"document-menu\">\n <div class=\"left\">\n <button\n @click=\"$router.push('/model/' + this.model)\"\n class=\"rounded-md bg-gray-400 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &lsaquo; Back\n </button>\n </div>\n\n <div class=\"right\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &times; Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n <button\n @click=\"shouldShowDeleteModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/delete.svg\" class=\"inline\" /> Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-pink-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/duplicate.svg\" class=\"inline\" /> Clone\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">&times;</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteModal = false;\">&times;</div>\n <confirm-delete @close=\"shouldShowDeleteModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">&times;</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n </div>\n</div>\n";
4193
4196
 
4194
4197
  /***/ }),
4195
4198
 
@@ -14562,6 +14565,17 @@ var bson = /*#__PURE__*/Object.freeze({
14562
14565
  //# sourceMappingURL=bson.mjs.map
14563
14566
 
14564
14567
 
14568
+ /***/ }),
14569
+
14570
+ /***/ "./package.json":
14571
+ /*!**********************!*\
14572
+ !*** ./package.json ***!
14573
+ \**********************/
14574
+ /***/ ((module) => {
14575
+
14576
+ "use strict";
14577
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.99","description":"A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.","homepage":"https://studio.mongoosejs.io/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"dependencies":{"archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"0.0.26","marked":"15.0.12","node-inspect-extracted":"3.x","tailwindcss":"3.4.0","vanillatoasts":"^1.6.0","vue":"3.x","webpack":"5.x"},"peerDependencies":{"bson":"^5.5.1 || 6.x","express":"4.x","mongoose":"7.x || 8.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongoose":"8.x"},"scripts":{"lint":"eslint .","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js"}}');
14578
+
14565
14579
  /***/ })
14566
14580
 
14567
14581
  /******/ });
@@ -894,18 +894,6 @@ video {
894
894
  height: 1px;
895
895
  }
896
896
 
897
- .h-10 {
898
- height: 2.5rem;
899
- }
900
-
901
- .h-11 {
902
- height: 2.75rem;
903
- }
904
-
905
- .max-h-\[30vh\] {
906
- max-height: 30vh;
907
- }
908
-
909
897
  .max-h-\[50vh\] {
910
898
  max-height: 50vh;
911
899
  }
@@ -1146,12 +1134,6 @@ video {
1146
1134
  margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
1147
1135
  }
1148
1136
 
1149
- .space-x-3 > :not([hidden]) ~ :not([hidden]) {
1150
- --tw-space-x-reverse: 0;
1151
- margin-right: calc(0.75rem * var(--tw-space-x-reverse));
1152
- margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
1153
- }
1154
-
1155
1137
  .space-y-1 > :not([hidden]) ~ :not([hidden]) {
1156
1138
  --tw-space-y-reverse: 0;
1157
1139
  margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
@@ -2346,10 +2328,6 @@ video {
2346
2328
  }
2347
2329
 
2348
2330
  @media (min-width: 768px) {
2349
- .md\:relative {
2350
- position: relative;
2351
- }
2352
-
2353
2331
  .md\:block {
2354
2332
  display: block;
2355
2333
  }
@@ -2358,18 +2336,6 @@ video {
2358
2336
  display: none;
2359
2337
  }
2360
2338
 
2361
- .md\:w-64 {
2362
- width: 16rem;
2363
- }
2364
-
2365
- .md\:max-w-\[calc\(100vw-16rem\)\] {
2366
- max-width: calc(100vw - 16rem);
2367
- }
2368
-
2369
- .md\:max-w-\[calc\(100vw-20rem\)\] {
2370
- max-width: calc(100vw - 20rem);
2371
- }
2372
-
2373
2339
  .md\:p-3 {
2374
2340
  padding: 0.75rem;
2375
2341
  }
@@ -67,8 +67,8 @@
67
67
  </modal>
68
68
  <modal v-if="shouldShowDeleteModal">
69
69
  <template v-slot:body>
70
- <div class="modal-exit" @click="shouldShowConfirmModal = false;">&times;</div>
71
- <confirm-delete @close="shouldShowConfirmModal = false;" @remove="remove" :value="document"></confirm-delete>
70
+ <div class="modal-exit" @click="shouldShowDeleteModal = false;">&times;</div>
71
+ <confirm-delete @close="shouldShowDeleteModal = false;" @remove="remove" :value="document"></confirm-delete>
72
72
  </template>
73
73
  </modal>
74
74
  <modal v-if="shouldShowCloneModal">
@@ -4,6 +4,9 @@ if (typeof process === 'undefined') {
4
4
  global.process = { env: {} }; // To make `util` package work
5
5
  }
6
6
 
7
+ const { version } = require('../../package.json');
8
+ console.log(`Mongoose Studio Version ${version}`);
9
+
7
10
  const api = require('./api');
8
11
  const mothership = require('./mothership');
9
12
  const { routes } = require('./routes');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.0.97",
3
+ "version": "0.0.99",
4
4
  "description": "A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.",
5
5
  "homepage": "https://studio.mongoosejs.io/",
6
6
  "repository": {
@@ -35,6 +35,7 @@
35
35
  "scripts": {
36
36
  "lint": "eslint .",
37
37
  "tailwind": "tailwindcss -o ./frontend/public/tw.css",
38
- "tailwind:watch": "tailwindcss -o ./frontend/public/tw.css --watch"
38
+ "tailwind:watch": "tailwindcss -o ./frontend/public/tw.css --watch",
39
+ "test": "mocha test/*.test.js"
39
40
  }
40
41
  }
@@ -1,18 +0,0 @@
1
- name: Lint
2
- on:
3
- pull_request:
4
- push:
5
- jobs:
6
- lint:
7
- runs-on: ubuntu-latest
8
- steps:
9
- - uses: actions/checkout@v3
10
- - name: Setting up the node version
11
- uses: actions/setup-node@v3
12
- with:
13
- node-version: 20.19.0
14
- - name: setup project
15
- run: npm i
16
- - name: run lint
17
- run: |
18
- npm run lint